evg_observable 3.0.1 → 3.1.1

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/.claudeignore ADDED
@@ -0,0 +1,9 @@
1
+ .env
2
+ .env.*
3
+ *.pem
4
+ *.key
5
+ credentials.json
6
+ service-account*.json
7
+ **/secrets/**
8
+ .npmrc
9
+ .pypirc
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
1
  [![Socket Badge](https://socket.dev/api/badge/npm/package/evg_observable)](https://socket.dev/npm/package/evg_observable)
2
+ <a href="https://www.npmjs.com/package/evg_observable"><img alt="npm version" src="https://img.shields.io/npm/v/evg_observable.svg?style=flat-square"></a>
3
+ <a href="https://bundlephobia.com/result?p=evg_observable"><img alt="Bundle size" src="https://badgen.net/bundlephobia/min/evg_observable"></a>
2
4
  <h1 align=center style="color: saddlebrown">
3
5
  EVG Observable
4
6
  </h1>
@@ -20,6 +22,13 @@ EVG Observable - is a light library for simple use.
20
22
  - [pipe().once()](#pipeonce)
21
23
  - [pipe().unsubscribeBy()](#pipeunsubscribebycondition)
22
24
  - [pipe().and()](#pipeandcondition)
25
+ - [pipe().throttle()](#pipethrottlems)
26
+ - [pipe().debounce()](#pipedebouncems)
27
+ - [pipe().distinctUntilChanged()](#pipedistinctuntilchangedcomparator)
28
+ - [pipe().tap()](#pipetapfn)
29
+ - [pipe().take()](#pipetaken)
30
+ - [pipe().skip()](#pipeskipn)
31
+ - [pipe().scan()](#pipescankfn-seed)
23
32
  - [pipe().toJson()](#pipetojson)
24
33
  - [pipe().fromJson()](#pipefromjsonk)
25
34
  - [pipe().in()](#pipeink-v)
@@ -41,14 +50,26 @@ EVG Observable - is a light library for simple use.
41
50
 
42
51
  | Metric | EVG Observable | RxJS |
43
52
  |--------|----------------|------|
44
- | **Bundle size** | **7.2 kB** | 88 kB |
45
- | **Size advantage** | **12.2x smaller** | - |
46
- | **Operations** | ~40 | 100+ |
47
- | **Performance** | **2-7x faster** | baseline |
53
+ | **Bundle size** | **9.4 kB** | 63.6 kB |
54
+ | **Size advantage** | **6.8x smaller** | - |
55
+ | **Operations** | ~43 | 100+ |
56
+ | **Performance** | **2-6x faster** | baseline |
48
57
 
49
58
  ### Performance Comparison (Bundle vs Bundle)
50
59
 
51
- Benchmarked with minified bundles on Node.js v22.17.1 (v3.0.0 API, averaged over 3 clean runs):
60
+ Benchmarked with minified bundles on Node.js v20.18.3 (v3.1.0 API):
61
+
62
+ | Test | EVG Observable | RxJS | Advantage |
63
+ |------|----------------|------|-----------|
64
+ | Emit 100 values | 2,955K ops/sec | 508K ops/sec | **5.8x faster** |
65
+ | Filter + transform | 615K ops/sec | 237K ops/sec | **2.6x faster** |
66
+ | 10 subscribers | 21,881K ops/sec | 7,556K ops/sec | **2.9x faster** |
67
+ | 100 subscribers | 2,284K ops/sec | 907K ops/sec | **2.5x faster** |
68
+ | 1000 subscribers | 181K ops/sec | 81K ops/sec | **2.2x faster** |
69
+ | Large payload | 1,455K ops/sec | 370K ops/sec | **3.9x faster** |
70
+
71
+ <details>
72
+ <summary>Previous results (v3.0.0 API, Node.js v22.17.1, averaged over 3 clean runs)</summary>
52
73
 
53
74
  | Test | EVG Observable | RxJS | Advantage |
54
75
  |------|----------------|------|-----------|
@@ -60,20 +81,6 @@ Benchmarked with minified bundles on Node.js v22.17.1 (v3.0.0 API, averaged over
60
81
  | Batch emission - of(100) | 906K ops/sec | 176K ops/sec | **5.1x faster** |
61
82
  | 5 chained filters | 19K ops/sec | 9K ops/sec | **2.1x faster** |
62
83
  | Large payload | 879K ops/sec | 184K ops/sec | **4.8x faster** |
63
-
64
- <details>
65
- <summary>Previous results (v2.x API, measured in different conditions)</summary>
66
-
67
- | Test | EVG Observable | RxJS | Advantage |
68
- |------|----------------|------|-----------|
69
- | Emit 100 values | 1,548K ops/sec | 240K ops/sec | **6.4x faster** |
70
- | Filter + transform | 353K ops/sec | 164K ops/sec | **2.1x faster** |
71
- | 10 subscribers | 9,078K ops/sec | 2,900K ops/sec | **3.1x faster** |
72
- | 100 subscribers | 1,245K ops/sec | 336K ops/sec | **3.7x faster** |
73
- | 1000 subscribers | 122K ops/sec | 33K ops/sec | **3.7x faster** |
74
- | Large payload | 865K ops/sec | 199K ops/sec | **4.3x faster** |
75
-
76
- **Note**: v3.0.0 performance is equal or better than v2.x (emit: +7%, 10 subs: +10%). The API redesign with more flexible pipe system maintains excellent performance while providing enhanced functionality.
77
84
  </details>
78
85
 
79
86
  ### EVG Observable Advantages
@@ -90,7 +97,7 @@ Benchmarked with minified bundles on Node.js v22.17.1 (v3.0.0 API, averaged over
90
97
 
91
98
  ### When to use RxJS instead
92
99
 
93
- RxJS is better when you need specialized operators like `debounceTime`, `throttleTime`, `switchMap`, `mergeMap`, `combineLatest`, `withLatestFrom`, or schedulers for async control.
100
+ RxJS is better when you need specialized operators like `switchMap`, `mergeMap`, `combineLatest`, `withLatestFrom`, or schedulers for async control.
94
101
 
95
102
  **For 80% of reactive programming tasks, EVG Observable provides sufficient functionality with significant performance and size benefits.**
96
103
 
@@ -103,7 +110,7 @@ Comparison with lightweight libraries in the same weight category (observable-fn
103
110
  | Metric | EVG Observable | observable-fns |
104
111
  |--------|----------------|----------------|
105
112
  | **Weekly downloads** | Growing | 67K |
106
- | **Bundle size (minified)** | 7.2 kB | 10.8 kB |
113
+ | **Bundle size (minified)** | 9.4 kB | 9.9 kB |
107
114
  | **Implementation** | Original architecture | zen-observable re-implementation |
108
115
  | **Dependencies** | 0 | 0 |
109
116
  | **Architecture** | True hot observables | Cold observables (zen-observable API) |
@@ -334,6 +341,269 @@ observable$.next({message: "some message3", isNeedUnsubscribe: true});
334
341
 
335
342
  Observable will send a value to the listener only if condition returns "true". There is no automatic unsubscription.
336
343
 
344
+ ### pipe().throttle(ms)
345
+
346
+ Throttle emissions using leading-edge strategy. The first value passes immediately; subsequent values within the cooldown interval are silently dropped.
347
+
348
+ ```ts
349
+ import {Observable} from "evg_observable";
350
+
351
+ const observable$ = new Observable<string>('');
352
+ const received: string[] = [];
353
+
354
+ observable$
355
+ .pipe()
356
+ .throttle(300) // Only allow one value per 300ms
357
+ .subscribe((value: string) => received.push(value));
358
+
359
+ observable$.next("first"); // passes immediately
360
+ observable$.next("second"); // dropped (within 300ms)
361
+ observable$.next("third"); // dropped (within 300ms)
362
+ // After 300ms...
363
+ observable$.next("fourth"); // passes (interval expired)
364
+ ```
365
+
366
+ Throttle can be combined with other pipe operators:
367
+
368
+ ```ts
369
+ observable$
370
+ .pipe()
371
+ .and(str => str.length > 1) // filter short strings
372
+ .throttle(500) // throttle remaining values
373
+ .map<number>(str => str.length) // transform to length
374
+ .subscribe(listener);
375
+ ```
376
+
377
+ ### pipe().debounce(ms)
378
+
379
+ Debounce emissions using trailing-edge strategy. Each new value resets the timer. The value is emitted after `ms` milliseconds of silence (no new values).
380
+
381
+ ```ts
382
+ import {Observable} from "evg_observable";
383
+
384
+ const search$ = new Observable<string>('');
385
+ const results: string[] = [];
386
+
387
+ search$
388
+ .pipe()
389
+ .debounce(300) // Wait 300ms of silence before emitting
390
+ .subscribe((value: string) => results.push(value));
391
+
392
+ search$.next("h"); // timer starts
393
+ search$.next("he"); // timer resets
394
+ search$.next("hello"); // timer resets
395
+ // After 300ms of silence → "hello" is emitted
396
+ ```
397
+
398
+ Debounce can be combined with other pipe operators:
399
+
400
+ ```ts
401
+ observable$
402
+ .pipe()
403
+ .and(str => str.length > 2) // filter short strings first
404
+ .debounce(200) // debounce remaining values
405
+ .map<number>(str => str.length) // transform to length
406
+ .subscribe(listener);
407
+ ```
408
+
409
+ ### pipe().distinctUntilChanged(comparator?)
410
+
411
+ Suppresses consecutive duplicate values. A value is emitted only when it differs from the previously emitted value. The first value always passes through.
412
+
413
+ ```ts
414
+ import {Observable} from "evg_observable";
415
+
416
+ const observable$ = new Observable<number>(0);
417
+ const results: number[] = [];
418
+
419
+ observable$
420
+ .pipe()
421
+ .distinctUntilChanged()
422
+ .subscribe((value: number) => results.push(value));
423
+
424
+ observable$.next(1); // emitted (first value)
425
+ observable$.next(1); // suppressed (same as previous)
426
+ observable$.next(2); // emitted (different)
427
+ observable$.next(2); // suppressed (same as previous)
428
+ observable$.next(1); // emitted (different from 2)
429
+ // results: [1, 2, 1]
430
+ ```
431
+
432
+ With custom comparator for objects:
433
+
434
+ ```ts
435
+ const users$ = new Observable<{id: number; name: string}>({id: 0, name: ''});
436
+
437
+ users$
438
+ .pipe()
439
+ .distinctUntilChanged((prev, curr) => prev.id === curr.id)
440
+ .subscribe(user => console.log(user.name));
441
+
442
+ users$.next({id: 1, name: 'Alice'}); // emitted
443
+ users$.next({id: 1, name: 'Alice Updated'}); // suppressed (same id)
444
+ users$.next({id: 2, name: 'Bob'}); // emitted
445
+ ```
446
+
447
+ ### pipe().tap(fn)
448
+
449
+ Executes a side-effect function on the current value without modifying it. The value passes through unchanged to the next operator. Useful for logging, debugging, or triggering external actions mid-pipeline.
450
+
451
+ ```ts
452
+ import {Observable} from "evg_observable";
453
+
454
+ const observable$ = new Observable<number>(0);
455
+
456
+ observable$
457
+ .pipe()
458
+ .tap(value => console.log('before filter:', value))
459
+ .and(value => value > 0)
460
+ .tap(value => console.log('after filter:', value))
461
+ .map<string>(value => `Result: ${value}`)
462
+ .subscribe(result => console.log(result));
463
+
464
+ observable$.next(5);
465
+ // before filter: 5
466
+ // after filter: 5
467
+ // Result: 5
468
+
469
+ observable$.next(-1);
470
+ // before filter: -1
471
+ // (filtered out by .and(), tap after filter is not called)
472
+ ```
473
+
474
+ #### Global debug flag pattern
475
+
476
+ Use a debug flag to enable/disable all taps at once — zero runtime cost when disabled:
477
+
478
+ ```ts
479
+ const DEBUG = true; // set to false in production
480
+
481
+ const observable$ = new Observable<number>(0);
482
+
483
+ observable$
484
+ .pipe()
485
+ .tap(value => DEBUG && console.log('[filter-in]:', value))
486
+ .and(value => value > 0)
487
+ .tap(value => DEBUG && console.log('[filter-out]:', value))
488
+ .map<number>(value => value * 2)
489
+ .tap(value => DEBUG && console.log('[mapped]:', value))
490
+ .subscribe(listener);
491
+
492
+ observable$.next(5);
493
+ // DEBUG=true: [filter-in]: 5 → [filter-out]: 5 → [mapped]: 10
494
+ // DEBUG=false: (nothing logged, V8 optimizes `false && ...` to no-op)
495
+ ```
496
+
497
+ You can also use named taps for clarity in complex pipelines:
498
+
499
+ ```ts
500
+ const tap_log = (name: string) => (value: any) =>
501
+ DEBUG && console.log(`[${name}]:`, value);
502
+
503
+ observable$
504
+ .pipe()
505
+ .tap(tap_log('raw'))
506
+ .and(value => value > 0)
507
+ .tap(tap_log('filtered'))
508
+ .distinctUntilChanged()
509
+ .tap(tap_log('unique'))
510
+ .subscribe(listener);
511
+ ```
512
+
513
+ ### pipe().take(n)
514
+
515
+ Passes the first N values through the pipe, then automatically unsubscribes. Generalization of `once()` — `once()` is equivalent to `take(1)`.
516
+
517
+ ```ts
518
+ import {Observable} from "evg_observable";
519
+
520
+ const observable$ = new Observable<string>('');
521
+ const received: string[] = [];
522
+
523
+ observable$
524
+ .pipe()
525
+ .take(3)
526
+ .subscribe((value: string) => received.push(value));
527
+
528
+ observable$.next("a"); // received: ["a"]
529
+ observable$.next("b"); // received: ["a", "b"]
530
+ observable$.next("c"); // received: ["a", "b", "c"] — auto-unsubscribed
531
+ observable$.next("d"); // not received
532
+ ```
533
+
534
+ Combine with filters and transforms:
535
+
536
+ ```ts
537
+ observable$
538
+ .pipe()
539
+ .and(str => str.length > 2) // filter short strings
540
+ .take(2) // take first 2 that pass
541
+ .subscribe(listener);
542
+ ```
543
+
544
+ ### pipe().skip(n)
545
+
546
+ Ignores the first N values in the pipe, then passes all subsequent values through. Mirror of `take(n)`.
547
+
548
+ ```ts
549
+ import {Observable} from "evg_observable";
550
+
551
+ const observable$ = new Observable<string>('');
552
+ const received: string[] = [];
553
+
554
+ observable$
555
+ .pipe()
556
+ .skip(2)
557
+ .subscribe((value: string) => received.push(value));
558
+
559
+ observable$.next("a"); // skipped
560
+ observable$.next("b"); // skipped
561
+ observable$.next("c"); // received: ["c"]
562
+ observable$.next("d"); // received: ["c", "d"]
563
+ ```
564
+
565
+ Combine skip + take for window slicing:
566
+
567
+ ```ts
568
+ observable$
569
+ .pipe()
570
+ .skip(2) // skip first 2
571
+ .take(3) // take next 3, then unsubscribe
572
+ .subscribe(listener);
573
+ ```
574
+
575
+ ### pipe().scan&lt;K&gt;(fn, seed)
576
+
577
+ Accumulator operator — each value passes through a reducer function, the accumulated result is emitted. Like `Array.reduce()` for streams.
578
+
579
+ ```ts
580
+ import {Observable} from "evg_observable";
581
+
582
+ const observable$ = new Observable<number>(0);
583
+ const sums: number[] = [];
584
+
585
+ observable$
586
+ .pipe()
587
+ .scan<number>((acc, val) => acc + val, 0)
588
+ .subscribe((sum: number) => sums.push(sum));
589
+
590
+ observable$.next(1); // sums: [1]
591
+ observable$.next(2); // sums: [1, 3]
592
+ observable$.next(3); // sums: [1, 3, 6]
593
+ ```
594
+
595
+ Type-changing scan (string → number):
596
+
597
+ ```ts
598
+ const observable$ = new Observable<string>('');
599
+
600
+ observable$
601
+ .pipe()
602
+ .scan<number>((acc, str) => acc + str.length, 0)
603
+ .and(total => total > 5)
604
+ .subscribe(total => console.log('Total chars:', total));
605
+ ```
606
+
337
607
  ### pipe().toJson()
338
608
 
339
609
  To convert the observable's data to JSON format, you can use the serialize method. This method turns the observer's
@@ -805,7 +1075,14 @@ observable$.next(-1); // Listener 1 throws, listener 2 still receives
805
1075
  | `.choice()` | SwitchCase object | transitions the pipe into switch-case mode. In this mode, only the first condition that returns a positive result is triggered, and all others are ignored. This allows you to handle multiple cases more conveniently. |
806
1076
  | `.or(*condition)` | PipeCase object | Adds a condition to the chain of cases. The entire chain operates on the principle of "OR". This is different from other pipe methods which, when chained, operate on the principle of "AND". |
807
1077
  | `.anyOf(*conditions)` | PipeCase object | This method allows you to add a group of conditions for filtering cases data in the pipeline chain. |
1078
+ | `.throttle(ms: number)` | pipe object | Throttles emissions using leading-edge strategy. First value passes immediately, subsequent values within `ms` interval are dropped. |
1079
+ | `.debounce(ms: number)` | pipe object | Debounces emissions using trailing-edge strategy. Each new value resets the timer. Emits after `ms` milliseconds of silence. |
1080
+ | `.distinctUntilChanged(comparator?)` | pipe object | Suppresses consecutive duplicate values. Optional `comparator(prev, curr) => boolean` for custom equality. Defaults to `===`. |
1081
+ | `.tap(fn: ICallback<T>)` | pipe object | Executes a side-effect function without modifying the value. The value passes through unchanged. |
1082
+ | `.take(n: number)` | pipe object | Passes the first N values, then auto-unsubscribes. Generalization of `once()`. |
1083
+ | `.skip(n: number)` | pipe object | Ignores the first N values, then passes all subsequent values through. Mirror of `take()`. |
808
1084
  | `.map<K>(transform: ICallback<T>)` | Observable instance with new data type | This method allows transforming payload data in the pipe chain by applying user callback function. `transform` should be a function that takes the current data and returns transformed data of possibly another type. |
1085
+ | `.scan<K>(fn, seed)` | pipe object with new type K | Accumulator — each value passes through reducer `fn(acc, val) => newAcc`, emits accumulated result. Like `Array.reduce()` for streams. |
809
1086
  | `.toJson()` | pipe object | Converts the observers data into a JSON string. |
810
1087
  | `.fromJson<K>()` | pipe object | Converts a JSON string into an object of type K. |
811
1088
  | `.of<K, V>(transform?: ICallback<K>)`| pipe object | Iterates over array elements. For each element, emits it to subscribers. Optional transform function processes each element before emission. |
package/package.json CHANGED
@@ -1,26 +1,43 @@
1
1
  {
2
2
  "name": "evg_observable",
3
- "version": "3.0.1",
4
- "description": "Alternative fast and light library version - observable.",
5
- "main": "src/outLib/index.js",
6
- "types": "src/outLib/index.d.ts",
3
+ "version": "3.1.1",
4
+ "description": "Lightweight reactive Observable library (zero dependencies) — 2-7x faster than RxJS, 1.5-3x faster than observable-fns. Pipe operators: throttle, debounce, distinctUntilChanged, map, tap. Original hot-observable architecture, not a fork or wrapper.",
5
+ "main": "./src/outLib/index.js",
6
+ "module": "./src/outLib-esm/index.mjs",
7
+ "types": "./src/outLib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./src/outLib/index.d.ts",
12
+ "default": "./src/outLib-esm/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./src/outLib/index.d.ts",
16
+ "default": "./src/outLib/index.js"
17
+ }
18
+ }
19
+ },
7
20
  "directories": {
8
21
  "test": "test"
9
22
  },
10
23
  "scripts": {
11
24
  "test": "nyc ./node_modules/.bin/_mocha 'test/**/*.test.ts'",
12
25
  "remove": "rm -rf ./src/Libraries; rm -rf ./test; rm .mocharc.json; rm .nyrc.json; rm register.js; rm tsconfig.json",
13
- "build": "tsc --declaration; npm run remove",
26
+ "build": "tsc --declaration && npm run build:esm && npm run remove",
27
+ "build:esm": "esbuild src/Libraries/Observables/index.ts --bundle --format=esm --minify --outfile=src/outLib-esm/index.mjs",
14
28
  "benchmark": "ts-node benchmarks/benchmark.ts",
15
29
  "benchmark:comparison": "ts-node benchmarks/benchmark-comparison.ts",
16
30
  "benchmark:competitors": "ts-node benchmarks/benchmark-competitors.ts",
17
31
  "benchmark:browser": "ts-node benchmarks/benchmark-browser-bundle.ts",
32
+ "benchmark:esm": "ts-node benchmarks/benchmark-esm-bundle.ts",
18
33
  "benchmark:bundles": "ts-node benchmarks/benchmark-bundles.ts",
19
34
  "benchmark:patterns": "ts-node benchmarks/benchmark-subscription-patterns.ts",
20
35
  "benchmark:patterns-edge": "ts-node benchmarks/benchmark-patterns-edge-cases.ts",
21
36
  "benchmark:patterns-clean": "ts-node benchmarks/benchmark-patterns-clean.ts",
22
37
  "benchmark:patterns-final": "ts-node benchmarks/benchmark-patterns-fixed.ts",
23
- "benchmark:patterns-vs-competitors": "ts-node benchmarks/benchmark-patterns-vs-competitors.ts"
38
+ "benchmark:patterns-vs-competitors": "ts-node benchmarks/benchmark-patterns-vs-competitors.ts",
39
+ "bundle": "esbuild src/browser-entry.ts --bundle --minify --format=iife --outfile=repo/evg_observable.js",
40
+ "bundle:watch": "esbuild src/browser-entry.ts --bundle --minify --format=iife --outfile=repo/evg_observable.js --watch"
24
41
  },
25
42
  "repository": {
26
43
  "type": "git",
@@ -38,6 +55,7 @@
38
55
  "@types/chai": "^4.3.4",
39
56
  "benchmark": "^2.1.4",
40
57
  "chai": "^4.3.7",
58
+ "esbuild": "^0.27.4",
41
59
  "microtime": "^3.1.1",
42
60
  "mocha": "^11.7.5",
43
61
  "nyc": "^17.1.0",
@@ -47,17 +65,23 @@
47
65
  "typescript": "^5.4.5"
48
66
  },
49
67
  "keywords": [
50
- "EVG Observable",
51
- "evg_observable",
52
- "Observable",
53
- "Ordered observable",
54
- "Light observable",
55
- "Collector",
56
- "Type script",
57
- "TypeScript",
58
- "TS",
59
- "Java script",
60
- "JavaScript",
61
- "JS"
68
+ "observable",
69
+ "reactive",
70
+ "event-emitter",
71
+ "rxjs-alternative",
72
+ "lightweight",
73
+ "pub-sub",
74
+ "subscribe",
75
+ "pipe",
76
+ "filter",
77
+ "map",
78
+ "throttle",
79
+ "debounce",
80
+ "distinctUntilChanged",
81
+ "tap",
82
+ "state-management",
83
+ "event-stream",
84
+ "typescript",
85
+ "zero-dependencies"
62
86
  ]
63
87
  }
@@ -1 +1 @@
1
- (()=>{"use strict";function s(s,r){return s.order>r.order?1:s.order<r.order?-1:0}function r(s,r){return s.order>r.order?-1:s.order<r.order?1:0}function e(s,r){const e=s.indexOf(r);return-1!==e&&(s.splice(e,1),!0)}function i(s){return"next"in s?r=>s.next(r):s}class t{pipe;counter;constructor(s){this.pipe=s,this.counter=s.chain.length?s.chain.length:0}or(s){this.counter++;const r=this.counter,e=this.pipe.chain;return e.push(i=>{i.isAvailable=!0,s(i.payload)&&(i.isBreak=!0),r!==e.length||i.isBreak||(i.isAvailable=!1)}),this}anyOf(s){if(!Array.isArray(s))return this;for(let r=0;r<s.length;r++)this.or(s[r]);return this}}class n{chain=[];flow={isBreak:!1,isUnsubscribe:!1,isAvailable:!1,payload:null};push(s){return this.chain.push(s),this}once(){return this.push(s=>{this.listener(s.payload),s.isUnsubscribe=!0})}unsubscribeBy(s){return this.push(r=>{r.isAvailable=!0,s(r.payload)&&(r.isUnsubscribe=!0)})}and(s){return this.push(r=>s(r.payload)&&(r.isAvailable=!0))}allOf(s){if(!Array.isArray(s))return this;for(let r=0;r<s.length;r++)this.and(s[r]);return this}choice(){return new h(this)}map(s){return this.push(r=>{r.payload=s(r.payload),r.isAvailable=!0})}toJson(){return this.push(s=>{s.payload=JSON.stringify(s.payload),s.isAvailable=!0})}fromJson(){return this.push(s=>{s.payload=JSON.parse(s.payload),s.isAvailable=!0})}group(){return this}processChain(s){const r=this.chain,e=this.flow,i=r.length;for(let s=0;s<i;s++){if(e.isUnsubscribe=!1,e.isAvailable=!1,r[s](e),e.isUnsubscribe)return this.unsubscribe();if(!e.isAvailable)return;if(e.isBreak)break}return s(e.payload)}}class h extends t{subscribe(s,r){return this.pipe.subscribe(s,r)}group(){return this.pipe}}class l extends n{observer;listener;errorHandler=(s,r)=>{console.log(`(Unit of SubscribeObject).send(${s}) ERROR:`,r)};_order=0;paused=!1;piped=!1;listeners;errorHandlers;constructor(s,r){super(),this.observer=s,this.piped=!!r}subscribe(s,r){return this.listener=function(s){if(Array.isArray(s)){const r=[];for(let e=0;e<s.length;e++)r.push(i(s[e]));return s=>{for(let e=0;e<r.length;e++)r[e](s)}}return i(s)}(s),r&&(this.errorHandler=r),this}add(s,r){if(this.listeners||(this.listeners=[],this.errorHandlers=[]),Array.isArray(s))for(let e=0;e<s.length;e++){this.listeners.push(s[e]);const i=r&&Array.isArray(r)?r[e]??this.errorHandler:r||this.errorHandler;this.errorHandlers.push(i)}else{this.listeners.push(s);const e=r&&!Array.isArray(r)?r:this.errorHandler;this.errorHandlers.push(e)}return this}unsubscribe(){this.observer&&(this.observer.unSubscribe(this),this.observer=null,this.listener=null,this.chain.length=0)}send(s){const r=this.listener,e=this.listeners&&this.listeners.length>0;if(r||e){if(this.observer&&!this.paused)if(this.piped)try{if(this.flow.payload=s,this.flow.isBreak=!1,r)this.processChain(r);else{const s=this.chain,r=this.flow,e=s.length;r.isAvailable=0===e;for(let i=0;i<e;i++){if(r.isUnsubscribe=!1,r.isAvailable=!1,s[i](r),r.isUnsubscribe)return void this.unsubscribe();if(!r.isAvailable)return;if(r.isBreak)break}}if(e){const s=this.flow.payload;for(let r=0;r<this.listeners.length;r++)try{this.listeners[r](s)}catch(e){this.errorHandlers[r](s,e)}}}catch(r){this.errorHandler(s,r)}else{if(r)try{r(s)}catch(r){this.errorHandler(s,r)}if(e)for(let r=0;r<this.listeners.length;r++)try{this.listeners[r](s)}catch(e){this.errorHandlers[r](s,e)}}}else this.unsubscribe()}resume(){this.paused=!1}pause(){this.paused=!0}get order(){return this._order}set order(s){this._order=s}}class a{chain=[];flow={isBreak:!1,isAvailable:!1,payload:null};response={isOK:!1,payload:void 0};errHandler;get isEmpty(){return!this.chain.length}push(s){return this.chain.push(s),this}and(s){return this.push(r=>s(r.payload)&&(r.isAvailable=!0))}allOf(s){if(!Array.isArray(s))return this;for(let r=0;r<s.length;r++)this.and(s[r]);return this}choice(){return new o(this)}processChain(s){const r=this.chain,e=this.flow,i=this.response;i.isOK=!1,i.payload=void 0,e.payload=s,e.isBreak=!1;try{const s=r.length;for(let t=0;t<s;t++){if(e.isAvailable=!1,r[t](e),!e.isAvailable)return i;if(e.isBreak)break}}catch(s){return this.errHandler?this.errHandler(s,"Filter.processChain ERROR:"):console.log("Filter.processChain ERROR:",s),i}return i.isOK=!0,i.payload=e.payload,i}addErrorHandler(s){this.errHandler=s}}class o extends t{}class u{subs=[];enabled=!0;killed=!1;process=!1;trash=[];filters=new a;_value;constructor(s){this._value=s}addFilter(s){return s&&this.filters.addErrorHandler(s),this.filters}disable(){this.enabled=!1}enable(){this.enabled=!0}get isEnable(){return this.enabled}next(s){if(this.killed)return;if(!this.enabled)return;if(!this.subs.length)return;if(!this.filters.isEmpty&&!this.filters.processChain(s).isOK)return;this.process=!0,this._value=s;const r=this.subs,e=r.length;for(let i=0;i<e;i++)r[i].send(s);this.process=!1,this.trash.length&&this.clearTrash()}of(s){if(!this.killed&&this.enabled)for(let r=0;r<s.length;r++)this.next(s[r])}in(s){if(!this.killed&&this.enabled)for(const r in s)Object.hasOwn(s,r)&&this.next([r,s[r]])}clearTrash(){const s=this.trash.length;for(let r=0;r<s;r++)this.unSubscribe(this.trash[r]);this.trash.length=0}unSubscribe(s){this.killed||(this.process&&s?this.trash.push(s):this.subs&&e(this.subs,s))}destroy(){if(!this.killed){if(this.killed=!0,!this.process)return this._value=null,void(this.subs.length=0);Promise.resolve().then(()=>{this._value=null,this.subs.length=0})}}unsubscribeAll(){if(!this.killed){if(this.process){const s=this.subs;for(let r=0;r<s.length;r++)this.trash.push(s[r]);return}this.subs.length=0}}getValue(){if(!this.killed)return this._value}size(){return this.killed?0:this.subs.length}subscribe(s,r){if(this.killed)return;if(!this.isListener(s))return;const e=new l(this,!1);return this.addObserver(e,s,r),e}addObserver(s,r,e){s.subscribe(r,e),this.subs.push(s)}isListener(s){return!this.killed&&!!s}pipe(){if(this.killed)return;const s=new l(this,!0);return this.subs.push(s),s}get isDestroyed(){return this.killed}}class d extends l{constructor(s,r){super(s,r)}get order(){return this._order}set order(s){!this.observer||this.observer&&this.observer.isDestroyed?this._order=void 0:(this._order=s,this.observer.sortByOrder())}subscribe(s,r){return super.subscribe(s,r),this}once(){return super.once()}}const c=window;c.Observable=u,c.Collector=class{arr=[];killed=!1;collect(...s){this.killed||this.arr.push(...s)}unsubscribe(s){this.killed||(s?.unsubscribe(),e(this.arr,s))}unsubscribeAll(){if(this.killed)return;const s=this.arr;for(let r=0;r<s.length;r++)s[r].unsubscribe();s.length=0}size(){return this.killed?0:this.arr.length}destroy(){this.unsubscribeAll(),this.arr.length=0,this.arr=0,this.killed=!0}get isDestroyed(){return this.killed}},c.OrderedObservable=class extends u{sortDirection=s;ascendingSort(){return this.sortDirection=s,this.sortByOrder()}descendingSort(){return this.sortDirection=r,this.sortByOrder()}sortByOrder(){return!this.killed&&(this.subs.sort(this.sortDirection),!0)}subscribe(s,r){if(!this.isListener(s))return;const e=new d(this,!1);return this.addObserver(e,s,r),e}pipe(){if(this.killed)return;const s=new d(this,!0);return this.subs.push(s),s}unSubscribe(s){this.killed||(this.process&&s?this.trash.push(s):this.subs&&e(this.subs,s))}}})();
1
+ "use strict";(()=>{function y(t,e){return t.order>e.order?1:t.order<e.order?-1:0}function v(t,e){return t.order>e.order?-1:t.order<e.order?1:0}function o(t,e){let r=t.indexOf(e);return r===-1?!1:(t.splice(r,1),!0)}function O(t){if(Array.isArray(t)){let e=t.length,r=new Array(e);for(let i=0;i<e;i++)r[i]=k(t[i]);return i=>{for(let s=0;s<e;s++)r[s](i)}}return k(t)}function k(t){return"next"in t?e=>t.next(e):t}var c=class{pipe;counter;constructor(e){this.pipe=e,this.counter=e.chain.length?e.chain.length:0}or(e){this.counter++;let r=this.counter,i=this.pipe.chain;return i.push(s=>{s.isAvailable=!0,e(s.payload)&&(s.isBreak=!0),r===i.length&&!s.isBreak&&(s.isAvailable=!1)}),this}anyOf(e){if(!Array.isArray(e))return this;for(let r=0;r<e.length;r++)this.or(e[r]);return this}};var I=class{chain=[];flow={isBreak:!1,isUnsubscribe:!1,isAvailable:!1,debounceMs:0,debounceTimer:0,debounceValue:void 0,debounceIndex:0,payload:null};push(e){return this.chain.push(e),this}once(){return this.push(e=>{this.listener(e.payload),e.isUnsubscribe=!0})}take(e){e<0&&(e=0);let r=0;return this.push(i=>{if(r>=e){i.isUnsubscribe=!0;return}r++,this.listener(i.payload),r>=e&&(i.isUnsubscribe=!0)})}skip(e){e<0&&(e=0);let r=0;return this.push(i=>{if(r<e){r++;return}i.isAvailable=!0})}unsubscribeBy(e){return this.push(r=>{r.isAvailable=!0,e(r.payload)&&(r.isUnsubscribe=!0)})}and(e){return this.push(r=>e(r.payload)&&(r.isAvailable=!0))}allOf(e){if(!Array.isArray(e))return this;for(let r=0;r<e.length;r++)this.and(e[r]);return this}choice(){return new T(this)}map(e){return this.push(r=>{r.payload=e(r.payload),r.isAvailable=!0})}scan(e,r){let i=r;return this.push(s=>{i=e(i,s.payload),s.payload=i,s.isAvailable=!0})}tap(e){return this.push(r=>{e(r.payload),r.isAvailable=!0})}throttle(e){let r=0;return this.push(i=>{let s=Date.now();s-r>=e&&(r=s,i.isAvailable=!0)})}debounce(e){return this.push(r=>{r.isAvailable=!0,r.debounceMs=e})}distinctUntilChanged(e){let r=!1,i;return this.push(s=>{let n=s.payload;r&&(e?e(i,n):i===n)||(r=!0,i=n,s.isAvailable=!0)})}toJson(){return this.push(e=>{e.payload=JSON.stringify(e.payload),e.isAvailable=!0})}fromJson(){return this.push(e=>{e.payload=JSON.parse(e.payload),e.isAvailable=!0})}group(){return this}processChain(e){let r=this.chain,i=this.flow,s=r.length;for(let n=0;n<s;n++){if(i.isUnsubscribe=!1,i.isAvailable=!1,i.debounceMs=0,r[n](i),i.isUnsubscribe)return this.unsubscribe();if(i.debounceMs>0){i.debounceValue=i.payload,i.debounceIndex=n+1;let l=()=>{i.debounceTimer=0,i.payload=i.debounceValue,i.isBreak=!1;for(let a=i.debounceIndex;a<s;a++){if(i.isUnsubscribe=!1,i.isAvailable=!1,i.debounceMs=0,r[a](i),i.isUnsubscribe)return this.unsubscribe();if(i.debounceMs>0){i.debounceValue=i.payload,i.debounceIndex=a+1,clearTimeout(i.debounceTimer),i.debounceTimer=setTimeout(l,i.debounceMs);return}if(!i.isAvailable)return;if(i.isBreak)break}e&&e(i.payload)};clearTimeout(i.debounceTimer),i.debounceTimer=setTimeout(l,i.debounceMs);return}if(!i.isAvailable)return;if(i.isBreak)break}i.isAvailable=!0,e&&e(i.payload)}},T=class extends c{subscribe(e,r){return this.pipe.subscribe(e,r)}group(){return this.pipe}};var u=class extends I{observer;listener;errorHandler=(e,r)=>{console.log(`(Unit of SubscribeObject).send(${e}) ERROR:`,r)};_order=0;paused=!1;piped=!1;listeners;errorHandlers;constructor(e,r){super(),this.observer=e,this.piped=!!r}subscribe(e,r){return this.listener=O(e),r&&(this.errorHandler=r),this}add(e,r){if(this.listeners||(this.listeners=[],this.errorHandlers=[]),Array.isArray(e))for(let i=0;i<e.length;i++){this.listeners.push(e[i]);let s=r&&Array.isArray(r)?r[i]??this.errorHandler:r||this.errorHandler;this.errorHandlers.push(s)}else{this.listeners.push(e);let i=r&&!Array.isArray(r)?r:this.errorHandler;this.errorHandlers.push(i)}return this}unsubscribe(){this.observer&&(clearTimeout(this.flow.debounceTimer),this.observer.unSubscribe(this),this.observer=null,this.listener=null,this.chain.length=0)}send(e){let r=this.listener,i=this.listeners&&this.listeners.length>0;if(!r&&!i){this.unsubscribe();return}if(!(!this.observer||this.paused)){if(!this.piped){if(r)try{r(e)}catch(s){this.errorHandler(e,s)}return}try{if(this.flow.payload=e,this.flow.isBreak=!1,i){let s=this.listeners,n=this.errorHandlers;this.processChain(l=>{r&&r(l);for(let a=0;a<s.length;a++)try{s[a](l)}catch(C){n[a](l,C)}})}else this.processChain(r)}catch(s){this.errorHandler(e,s)}}}resume(){this.paused=!1}pause(){this.paused=!0}get order(){return this._order}set order(e){this._order=e}};var f=class{chain=[];flow={isBreak:!1,isAvailable:!1,payload:null};response={isOK:!1,payload:void 0};errHandler;get isEmpty(){return!this.chain.length}push(e){return this.chain.push(e),this}and(e){return this.push(r=>e(r.payload)&&(r.isAvailable=!0))}allOf(e){if(!Array.isArray(e))return this;for(let r=0;r<e.length;r++)this.and(e[r]);return this}choice(){return new m(this)}processChain(e){let r=this.chain,i=this.flow,s=this.response;s.isOK=!1,s.payload=void 0,i.payload=e,i.isBreak=!1;try{let n=r.length;for(let l=0;l<n;l++){if(i.isAvailable=!1,r[l](i),!i.isAvailable)return s;if(i.isBreak)break}}catch(n){return this.errHandler?this.errHandler(n,"Filter.processChain ERROR:"):console.log("Filter.processChain ERROR:",n),s}return s.isOK=!0,s.payload=i.payload,s}addErrorHandler(e){this.errHandler=e}},m=class extends c{};var b=class{subs=[];enabled=!0;killed=!1;process=!1;trash=[];filters=new f;_value;constructor(e){this._value=e}addFilter(e){return e&&this.filters.addErrorHandler(e),this.filters}disable(){this.enabled=!1}enable(){this.enabled=!0}get isEnable(){return this.enabled}next(e){if(this.killed||!this.enabled||!this.subs.length||!this.filters.isEmpty&&!this.filters.processChain(e).isOK)return;this.process=!0,this._value=e;let r=this.subs,i=r.length;for(let s=0;s<i;s++)r[s].send(e);this.process=!1,this.trash.length&&this.clearTrash()}of(e){if(!this.killed&&this.enabled)for(let r=0;r<e.length;r++)this.next(e[r])}in(e){if(!this.killed&&this.enabled)for(let r in e)Object.hasOwn(e,r)&&this.next([r,e[r]])}clearTrash(){let e=this.trash.length;for(let r=0;r<e;r++)this.unSubscribe(this.trash[r]);this.trash.length=0}unSubscribe(e){if(!this.killed){if(this.process&&e){this.trash.push(e);return}this.subs&&o(this.subs,e)}}destroy(){if(!this.killed){if(this.killed=!0,!this.process){this.clearDebounceTimers(),this._value=null,this.subs.length=0;return}Promise.resolve().then(()=>{this.clearDebounceTimers(),this._value=null,this.subs.length=0})}}unsubscribeAll(){if(!this.killed){if(this.process){this.clearDebounceTimers();let e=this.subs;for(let r=0;r<e.length;r++)this.trash.push(e[r]);return}this.clearDebounceTimers(),this.subs.length=0}}clearDebounceTimers(){let e=this.subs;for(let r=0;r<e.length;r++)clearTimeout(e[r].flow.debounceTimer)}getValue(){if(!this.killed)return this._value}size(){return this.killed?0:this.subs.length}subscribe(e,r){if(this.killed||!this.isListener(e))return;let i=new u(this,!1);return this.addObserver(i,e,r),i}addObserver(e,r,i){e.subscribe(r,i),this.subs.push(e)}isListener(e){return this.killed?!1:!!e}pipe(){if(this.killed)return;let e=new u(this,!0);return this.subs.push(e),e}get isDestroyed(){return this.killed}};var d=class extends u{constructor(e,r){super(e,r)}get order(){return this._order}set order(e){if(!this.observer||this.observer&&this.observer.isDestroyed){this._order=void 0;return}this._order=e,this.observer.sortByOrder()}subscribe(e,r){return super.subscribe(e,r),this}once(){return super.once()}take(e){return super.take(e)}skip(e){return super.skip(e)}scan(e,r){return super.scan(e,r)}};var h=class extends b{sortDirection=y;ascendingSort(){return this.sortDirection=y,this.sortByOrder()}descendingSort(){return this.sortDirection=v,this.sortByOrder()}sortByOrder(){return this.killed?!1:(this.subs.sort(this.sortDirection),!0)}subscribe(e,r){if(!this.isListener(e))return;let i=new d(this,!1);return this.addObserver(i,e,r),i}pipe(){if(this.killed)return;let e=new d(this,!0);return this.subs.push(e),e}unSubscribe(e){if(!this.killed){if(this.process&&e){this.trash.push(e);return}this.subs&&o(this.subs,e)}}};var p=class{arr=[];killed=!1;collect(...e){this.killed||this.arr.push(...e)}unsubscribe(e){this.killed||(e?.unsubscribe(),o(this.arr,e))}unsubscribeAll(){if(this.killed)return;let e=this.arr;for(let r=0;r<e.length;r++)e[r].unsubscribe();e.length=0}size(){return this.killed?0:this.arr.length}destroy(){this.unsubscribeAll(),this.arr.length=0,this.arr=0,this.killed=!0}get isDestroyed(){return this.killed}};var S=window;S.Observable=b;S.OrderedObservable=h;S.Collector=p;})();
@@ -0,0 +1 @@
1
+ (()=>{"use strict";function s(s,r){return s.order>r.order?1:s.order<r.order?-1:0}function r(s,r){return s.order>r.order?-1:s.order<r.order?1:0}function e(s,r){const e=s.indexOf(r);return-1!==e&&(s.splice(e,1),!0)}function i(s){return"next"in s?r=>s.next(r):s}class t{pipe;counter;constructor(s){this.pipe=s,this.counter=s.chain.length?s.chain.length:0}or(s){this.counter++;const r=this.counter,e=this.pipe.chain;return e.push(i=>{i.isAvailable=!0,s(i.payload)&&(i.isBreak=!0),r!==e.length||i.isBreak||(i.isAvailable=!1)}),this}anyOf(s){if(!Array.isArray(s))return this;for(let r=0;r<s.length;r++)this.or(s[r]);return this}}class n{chain=[];flow={isBreak:!1,isUnsubscribe:!1,isAvailable:!1,payload:null};push(s){return this.chain.push(s),this}once(){return this.push(s=>{this.listener(s.payload),s.isUnsubscribe=!0})}unsubscribeBy(s){return this.push(r=>{r.isAvailable=!0,s(r.payload)&&(r.isUnsubscribe=!0)})}and(s){return this.push(r=>s(r.payload)&&(r.isAvailable=!0))}allOf(s){if(!Array.isArray(s))return this;for(let r=0;r<s.length;r++)this.and(s[r]);return this}choice(){return new h(this)}map(s){return this.push(r=>{r.payload=s(r.payload),r.isAvailable=!0})}toJson(){return this.push(s=>{s.payload=JSON.stringify(s.payload),s.isAvailable=!0})}fromJson(){return this.push(s=>{s.payload=JSON.parse(s.payload),s.isAvailable=!0})}group(){return this}processChain(s){const r=this.chain,e=this.flow,i=r.length;for(let s=0;s<i;s++){if(e.isUnsubscribe=!1,e.isAvailable=!1,r[s](e),e.isUnsubscribe)return this.unsubscribe();if(!e.isAvailable)return;if(e.isBreak)break}return s(e.payload)}}class h extends t{subscribe(s,r){return this.pipe.subscribe(s,r)}group(){return this.pipe}}class l extends n{observer;listener;errorHandler=(s,r)=>{console.log(`(Unit of SubscribeObject).send(${s}) ERROR:`,r)};_order=0;paused=!1;piped=!1;listeners;errorHandlers;constructor(s,r){super(),this.observer=s,this.piped=!!r}subscribe(s,r){return this.listener=function(s){if(Array.isArray(s)){const r=[];for(let e=0;e<s.length;e++)r.push(i(s[e]));return s=>{for(let e=0;e<r.length;e++)r[e](s)}}return i(s)}(s),r&&(this.errorHandler=r),this}add(s,r){if(this.listeners||(this.listeners=[],this.errorHandlers=[]),Array.isArray(s))for(let e=0;e<s.length;e++){this.listeners.push(s[e]);const i=r&&Array.isArray(r)?r[e]??this.errorHandler:r||this.errorHandler;this.errorHandlers.push(i)}else{this.listeners.push(s);const e=r&&!Array.isArray(r)?r:this.errorHandler;this.errorHandlers.push(e)}return this}unsubscribe(){this.observer&&(this.observer.unSubscribe(this),this.observer=null,this.listener=null,this.chain.length=0)}send(s){const r=this.listener,e=this.listeners&&this.listeners.length>0;if(r||e){if(this.observer&&!this.paused)if(this.piped)try{if(this.flow.payload=s,this.flow.isBreak=!1,r)this.processChain(r);else{const s=this.chain,r=this.flow,e=s.length;r.isAvailable=0===e;for(let i=0;i<e;i++){if(r.isUnsubscribe=!1,r.isAvailable=!1,s[i](r),r.isUnsubscribe)return void this.unsubscribe();if(!r.isAvailable)return;if(r.isBreak)break}}if(e){const s=this.flow.payload;for(let r=0;r<this.listeners.length;r++)try{this.listeners[r](s)}catch(e){this.errorHandlers[r](s,e)}}}catch(r){this.errorHandler(s,r)}else{if(r)try{r(s)}catch(r){this.errorHandler(s,r)}if(e)for(let r=0;r<this.listeners.length;r++)try{this.listeners[r](s)}catch(e){this.errorHandlers[r](s,e)}}}else this.unsubscribe()}resume(){this.paused=!1}pause(){this.paused=!0}get order(){return this._order}set order(s){this._order=s}}class a{chain=[];flow={isBreak:!1,isAvailable:!1,payload:null};response={isOK:!1,payload:void 0};errHandler;get isEmpty(){return!this.chain.length}push(s){return this.chain.push(s),this}and(s){return this.push(r=>s(r.payload)&&(r.isAvailable=!0))}allOf(s){if(!Array.isArray(s))return this;for(let r=0;r<s.length;r++)this.and(s[r]);return this}choice(){return new o(this)}processChain(s){const r=this.chain,e=this.flow,i=this.response;i.isOK=!1,i.payload=void 0,e.payload=s,e.isBreak=!1;try{const s=r.length;for(let t=0;t<s;t++){if(e.isAvailable=!1,r[t](e),!e.isAvailable)return i;if(e.isBreak)break}}catch(s){return this.errHandler?this.errHandler(s,"Filter.processChain ERROR:"):console.log("Filter.processChain ERROR:",s),i}return i.isOK=!0,i.payload=e.payload,i}addErrorHandler(s){this.errHandler=s}}class o extends t{}class u{subs=[];enabled=!0;killed=!1;process=!1;trash=[];filters=new a;_value;constructor(s){this._value=s}addFilter(s){return s&&this.filters.addErrorHandler(s),this.filters}disable(){this.enabled=!1}enable(){this.enabled=!0}get isEnable(){return this.enabled}next(s){if(this.killed)return;if(!this.enabled)return;if(!this.subs.length)return;if(!this.filters.isEmpty&&!this.filters.processChain(s).isOK)return;this.process=!0,this._value=s;const r=this.subs,e=r.length;for(let i=0;i<e;i++)r[i].send(s);this.process=!1,this.trash.length&&this.clearTrash()}of(s){if(!this.killed&&this.enabled)for(let r=0;r<s.length;r++)this.next(s[r])}in(s){if(!this.killed&&this.enabled)for(const r in s)Object.hasOwn(s,r)&&this.next([r,s[r]])}clearTrash(){const s=this.trash.length;for(let r=0;r<s;r++)this.unSubscribe(this.trash[r]);this.trash.length=0}unSubscribe(s){this.killed||(this.process&&s?this.trash.push(s):this.subs&&e(this.subs,s))}destroy(){if(!this.killed){if(this.killed=!0,!this.process)return this._value=null,void(this.subs.length=0);Promise.resolve().then(()=>{this._value=null,this.subs.length=0})}}unsubscribeAll(){if(!this.killed){if(this.process){const s=this.subs;for(let r=0;r<s.length;r++)this.trash.push(s[r]);return}this.subs.length=0}}getValue(){if(!this.killed)return this._value}size(){return this.killed?0:this.subs.length}subscribe(s,r){if(this.killed)return;if(!this.isListener(s))return;const e=new l(this,!1);return this.addObserver(e,s,r),e}addObserver(s,r,e){s.subscribe(r,e),this.subs.push(s)}isListener(s){return!this.killed&&!!s}pipe(){if(this.killed)return;const s=new l(this,!0);return this.subs.push(s),s}get isDestroyed(){return this.killed}}class d extends l{constructor(s,r){super(s,r)}get order(){return this._order}set order(s){!this.observer||this.observer&&this.observer.isDestroyed?this._order=void 0:(this._order=s,this.observer.sortByOrder())}subscribe(s,r){return super.subscribe(s,r),this}once(){return super.once()}}const c=window;c.Observable=u,c.Collector=class{arr=[];killed=!1;collect(...s){this.killed||this.arr.push(...s)}unsubscribe(s){this.killed||(s?.unsubscribe(),e(this.arr,s))}unsubscribeAll(){if(this.killed)return;const s=this.arr;for(let r=0;r<s.length;r++)s[r].unsubscribe();s.length=0}size(){return this.killed?0:this.arr.length}destroy(){this.unsubscribeAll(),this.arr.length=0,this.arr=0,this.killed=!0}get isDestroyed(){return this.killed}},c.OrderedObservable=class extends u{sortDirection=s;ascendingSort(){return this.sortDirection=s,this.sortByOrder()}descendingSort(){return this.sortDirection=r,this.sortByOrder()}sortByOrder(){return!this.killed&&(this.subs.sort(this.sortDirection),!0)}subscribe(s,r){if(!this.isListener(s))return;const e=new d(this,!1);return this.addObserver(e,s,r),e}pipe(){if(this.killed)return;const s=new d(this,!0);return this.subs.push(s),s}unSubscribe(s){this.killed||(this.process&&s?this.trash.push(s):this.subs&&e(this.subs,s))}}})();
@@ -0,0 +1,69 @@
1
+ # Automation Scripts
2
+
3
+ Scripts for automated code review and PR preparation using Claude Code in headless mode.
4
+
5
+ ## Prerequisites
6
+
7
+ - Claude Code CLI installed (`npm install -g @anthropic-ai/claude-code`)
8
+ - Authenticated Claude session (API key or OAuth)
9
+ - Git repository with at least one base branch (`dev`, `stage`, `main`, or `master`)
10
+
11
+ ## Scripts
12
+
13
+ ### claude-review-files.sh
14
+
15
+ Code review of current branch changes against a base branch.
16
+
17
+ **What it checks:**
18
+ - Correctness and edge cases
19
+ - TypeScript type safety
20
+ - Naming conventions (PascalCase, camelCase, `I` prefix, `$` suffix)
21
+ - Code style (2-space indent, JSDoc)
22
+ - Resource cleanup (destroy/unsubscribe)
23
+ - Regression risk
24
+
25
+ **When to use:** After finishing implementation, before creating a PR. Helps catch problems early.
26
+
27
+ ```bash
28
+ ./scripts/claude-review-files.sh # auto-detect base branch
29
+ ./scripts/claude-review-files.sh dev # review against dev
30
+ ```
31
+
32
+ ---
33
+
34
+ ### claude-pr-prep.sh
35
+
36
+ Runs tests and generates a PR description from commit history and test results.
37
+
38
+ **What it does:**
39
+ 1. Runs `npm test` and captures results
40
+ 2. Collects commit log between current and base branch
41
+ 3. Generates a structured PR description (Summary, Changes, Test plan)
42
+
43
+ **When to use:** When the branch is ready for PR and you need a description.
44
+
45
+ ```bash
46
+ ./scripts/claude-pr-prep.sh # auto-detect base branch
47
+ ./scripts/claude-pr-prep.sh dev # prepare PR against dev
48
+ ```
49
+
50
+ ---
51
+
52
+ ### claude-full-check.sh
53
+
54
+ Full pre-PR pipeline: runs review first, then PR preparation.
55
+
56
+ **What it does:**
57
+ 1. Runs `claude-review-files.sh` — review code for issues
58
+ 2. Runs `claude-pr-prep.sh` — run tests and generate PR description
59
+
60
+ **When to use:** Complete quality check before creating a PR. One command instead of two.
61
+
62
+ ```bash
63
+ ./scripts/claude-full-check.sh # auto-detect base branch
64
+ ./scripts/claude-full-check.sh dev # full check against dev
65
+ ```
66
+
67
+ ## Base Branch Detection
68
+
69
+ All scripts auto-detect the base branch in this order: `dev` → `stage` → `main` → `master`. The first existing branch is used. You can override by passing the branch name as an argument.
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ # scripts/claude-full-check.sh
3
+ # Full pre-PR pipeline: review code, then prepare PR description.
4
+ # Runs claude-review-files.sh first, then claude-pr-prep.sh.
5
+ #
6
+ # Usage:
7
+ # ./scripts/claude-full-check.sh # auto-detect base branch
8
+ # ./scripts/claude-full-check.sh dev # explicit base branch
9
+
10
+ set -e
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
13
+ BASE="${1:-}"
14
+
15
+ echo "========================================="
16
+ echo " Step 1/2: Code Review"
17
+ echo "========================================="
18
+ echo ""
19
+
20
+ "$SCRIPT_DIR/claude-review-files.sh" $BASE
21
+
22
+ echo ""
23
+ echo "========================================="
24
+ echo " Step 2/2: PR Preparation"
25
+ echo "========================================="
26
+ echo ""
27
+
28
+ "$SCRIPT_DIR/claude-pr-prep.sh" $BASE
29
+
30
+ echo ""
31
+ echo "========================================="
32
+ echo " Done. Review the output above."
33
+ echo "========================================="