dolphin-client 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/store.d.ts CHANGED
@@ -1,29 +1,103 @@
1
+ /**
2
+ * Lazy, chainable data-transformation engine.
3
+ * All transforms are applied only when .get() is called.
4
+ * Inspired by map-ultimate.ts DataEngine concept, adapted for HTML-based Dolphin.
5
+ */
6
+ export declare class DataEngine<T extends Record<string, any> = any> {
7
+ private _src;
8
+ private _filtered;
9
+ private _filters;
10
+ private _sortFn;
11
+ private _version;
12
+ constructor(initialData?: T[]);
13
+ private _invalidate;
14
+ getVersion(): number;
15
+ /** Text search across fields (case-insensitive) */
16
+ search(term: string, fields?: (keyof T)[]): this;
17
+ /** Exact value filter on a field */
18
+ filter(field: keyof T, value: any): this;
19
+ /** Numeric range filter */
20
+ range(field: keyof T, min: number, max: number): this;
21
+ /** Sort by field ascending or descending */
22
+ sort(field: keyof T, asc?: boolean): this;
23
+ /** Clear all filters and sort */
24
+ clearFilters(): this;
25
+ /** Get filtered + sorted results (lazy, cached) */
26
+ get(): T[];
27
+ /** Paginate the filtered result */
28
+ page(pageNum?: number, size?: number): {
29
+ data: T[];
30
+ total: number;
31
+ page: number;
32
+ size: number;
33
+ pages: number;
34
+ hasNext: boolean;
35
+ hasPrev: boolean;
36
+ };
37
+ get length(): number;
38
+ get total(): number;
39
+ setSource(newData: T[]): this;
40
+ add(item: T): this;
41
+ push(...items: T[]): this;
42
+ updateById(id: any, updates: Partial<T>, key?: string): this;
43
+ removeById(id: any, key?: string): this;
44
+ remove(predicate: (item: T, index: number) => boolean): this;
45
+ /** Get raw source (unfiltered) */
46
+ getSource(): T[];
47
+ }
1
48
  export declare class DolphinStore {
2
49
  client: any;
3
50
  data: Map<string, any>;
4
51
  listeners: Set<any>;
5
52
  subscribed: Set<string>;
6
- /** @fix: Store unsubscribe functions so destroy() can clean up WS subscriptions (was: subscriptions never removed) */
53
+ /** @fix: Store unsubscribe functions so destroy() can clean up WS subscriptions */
7
54
  _unsubscribers: Map<string, () => void>;
8
- /** @param {DolphinClient} client */
55
+ /** @fix: Race condition guard — tracks in-flight fetches */
56
+ private _fetching;
57
+ /** Batch notification flag */
58
+ private _batchPending;
59
+ /** Per-collection DataEngine instances */
60
+ private _engines;
61
+ /** Per-item loading tracking: collectionName → Set of IDs being processed */
62
+ private _trackingIds;
9
63
  constructor(client: any);
10
64
  /** @private */
11
- _getCollection(name: any): any;
65
+ _getCollection(name: string): any;
66
+ /**
67
+ * @private — apply legacy where/orderBy + DataEngine filters.
68
+ * engine is optional: if not provided (e.g. tests set data manually),
69
+ * falls back to state._rawItems directly.
70
+ */
71
+ _applyTransform(state: any, engine?: DataEngine): void;
72
+ /**
73
+ * @private — Batch multiple rapid store updates into a single DOM notify.
74
+ * Uses queueMicrotask in production for batching.
75
+ * In test environments (Jest), calls notify synchronously so assertions work.
76
+ */
77
+ private _batchNotify;
78
+ /**
79
+ * @private — Fetch from API and subscribe to WebSocket sync.
80
+ * @fix Bug 2: _fetching guard prevents race condition / double-fetch.
81
+ */
82
+ private _fetchAndSync;
83
+ /** @private — Handle WebSocket real-time update for a collection */
84
+ _handleRemoteUpdate(collection: string, update: any): void;
12
85
  /** @private */
13
- _fetchAndSync(name: any): Promise<void>;
86
+ private _trackStart;
14
87
  /** @private */
15
- _applyTransform(state: any): void;
88
+ private _trackEnd;
16
89
  /** @private */
17
- _handleRemoteUpdate(collection: any, update: any): void;
18
- /** Subscribe for React useSyncExternalStore */
19
- subscribe(listener: any): () => boolean;
20
- /** @param {string} collection */
21
- getSnapshot(collection: any): any;
90
+ private _isTracking;
91
+ /** Subscribe for React useSyncExternalStore or external listeners */
92
+ subscribe(listener: () => void): () => boolean;
93
+ /** Get snapshot of a collection (for useSyncExternalStore) */
94
+ getSnapshot(collection: string): any;
22
95
  /** @private */
23
96
  _notify(): void;
24
97
  /**
25
98
  * Clean up all WebSocket subscriptions and listeners.
26
99
  * Call this when the store is no longer needed to prevent resource leaks.
100
+ * @fix: Properly unsubscribes because updateHandler is now captured correctly.
27
101
  */
28
102
  destroy(): void;
29
103
  }
package/dist/testing.d.ts CHANGED
@@ -7,11 +7,11 @@ export declare class DolphinTestUtils {
7
7
  static mockWebSocket(): {
8
8
  readyState: number;
9
9
  send: (data: string) => void;
10
- close: jest.Mock<any, any, any>;
11
- onopen: jest.Mock<any, any, any>;
12
- onmessage: jest.Mock<any, any, any>;
13
- onclose: jest.Mock<any, any, any>;
14
- onerror: jest.Mock<any, any, any>;
10
+ close: any;
11
+ onopen: any;
12
+ onmessage: any;
13
+ onclose: any;
14
+ onerror: any;
15
15
  sentMessages: string[];
16
16
  };
17
17
  static simulateClick(el: any): void;
package/fulltutorial.md CHANGED
@@ -503,6 +503,113 @@ Ternary operator (`condition ? true : false`) वा बहु-लाइन `if/
503
503
  <button data-store-click="app.darkMode = !app.darkMode">डार्क मोड अन/अफ</button>
504
504
  ```
505
505
 
506
+ ### १०.१.३. Declarative Store Initialization & Context Containers (`<dolphin-store>`) - स्टोर घोषणा, प्रारम्भिकरण र अटो-बाइन्डिङ
507
+
508
+ Dolphin Client v2.0 introduces the `<dolphin-store>` tag for declaring, seeding, and auto-wiring reactive stores directly in HTML without writing custom JavaScript scripts.
509
+
510
+ #### क) Seed-Only Mode (विशुद्ध डेटा सीडिङ)
511
+ If the `<dolphin-store>` tag does not contain child elements, it seeds the store and remains hidden (`display: none`). You can seed data via tag attributes or inline JSON content:
512
+
513
+ **Attribute-Based Seeding:**
514
+ ```html
515
+ <!-- Automatically seeds store "app" with boolean, numeric, null, and string types -->
516
+ <dolphin-store name="app" count="0" isAdding="false" user="null" theme="dark"></dolphin-store>
517
+ ```
518
+
519
+ **JSON-Based Seeding:**
520
+ ```html
521
+ <!-- Seed complex objects/arrays by putting JSON content inside the tag -->
522
+ <dolphin-store name="app">
523
+ {
524
+ "count": 10,
525
+ "user": { "name": "Shankar", "role": "admin" },
526
+ "loggedIn": true
527
+ }
528
+ </dolphin-store>
529
+ ```
530
+
531
+ #### ख) Context Container Dual-Mode (डबल-मोड - डेटा कन्टेनर)
532
+ If the `<dolphin-store>` tag contains child elements, it acts as a **reactive context container** and remains visible. Its children can read and write to the store dynamically using `data-store-*` directives:
533
+
534
+ ```html
535
+ <!-- Acts as the reactive container for 'store/profile' -->
536
+ <dolphin-store name="profile" username="Shankar" role="Admin">
537
+ <div class="card">
538
+ <h3 data-rt-text="username"></h3>
539
+ <span class="badge" data-rt-text="role"></span>
540
+ </div>
541
+ </dolphin-store>
542
+ ```
543
+
544
+ #### ग) Template Auto-Wiring (टेम्पलेट स्वतः-कनेक्सन)
545
+ By setting the `template` attribute on a `<dolphin-store>`, Dolphin will automatically generate a reactive binding `div` right after it, compile the specified template selector, and render it with the store's seeded state immediately on page load:
546
+
547
+ ```html
548
+ <!-- 1. The Svelte-style template -->
549
+ <template id="counter-ui">
550
+ <div class="counter-box">
551
+ <p>Count: {{count}}</p>
552
+ <button data-rt-click="app.count = app.count + 1">Increment</button>
553
+ </div>
554
+ </template>
555
+
556
+ <!-- 2. Auto-wire the store app to the template (No separate wrapper div required!) -->
557
+ <dolphin-store name="app" template="#counter-ui" count="5"></dolphin-store>
558
+ ```
559
+ Under the hood, this dynamically injects a binding container that subscribes to `store/app` and updates the UI in real-time.
560
+
561
+ ### १०.१.४. Database Store Collections & CRUD Actions in HTML (डेटाबेस स्टोर कलेक्सन र बिना जाभास्क्रिप्ट CRUD र फिल्टर्स)
562
+
563
+ Dolphin Client v2.0 ले डेटाबेस स्टोरका कलेक्सनहरूलाई (जुन स्वतः REST र WebSockets बाट सिंक हुन्छन्) सिधै HTML attributes भित्रैबाट चलाउन र व्यवस्थापन गर्न मिल्ने सुविधा प्रदान गर्दछ।
564
+
565
+ यदि कुनै `<dolphin-store>` वा dynamic database collection (जस्तै `products` वा `users`) दर्ता छ भने, तपाईँले HTML elements मा `data-store-click`, `data-store-input`, वा `data-store-change` प्रयोग गरेर सिधै ती कलेक्सनका Methods कल गर्न सक्नुहुन्छ।
566
+
567
+ कल गरेपछि Dolphin ले स्वतः DOM लाई reactive रूपमा re-render गराउँछ।
568
+
569
+ #### उपलब्ध मुख्य Collection Methods:
570
+ * **`collectionName.search(term, fields)`**: पाठ (text) खोज्नका लागि (fields array ऐच्छिक हो)।
571
+ * **`collectionName.filter(field, value)`**: कुनै निश्चित field को भ्यालुअनुसार फिल्टर गर्न।
572
+ * **`collectionName.range(field, min, max)`**: अंकहरूको सीमाअनुसार फिल्टर गर्न।
573
+ * **`collectionName.sort(field, asc)`**: कुनै फिल्डको आधारमा alphabetical वा numerical क्रममा मिलाउन (asc `true`/`false` हुन्छ)।
574
+ * **`collectionName.clearFilters()`**: सबै फिल्टर्स र सर्टिङ हटाउन।
575
+ * **`collectionName.deleteById(id)`**: आईडीको आधारमा आइटम हटाउन (DOM र database दुवैमा)।
576
+ * **`collectionName.updateById(id, updates)`**: आईडीको आधारमा आइटम अपडेट गर्न।
577
+ * **`collectionName.add(item)`**: नयाँ आइटम थप्न।
578
+
579
+ #### HTML मा प्रयोगको उदाहरण:
580
+ ```html
581
+ <!-- १. डेटाबेस स्टोर कलेक्सन दर्ता गर्ने -->
582
+ <dolphin-store name="products"></dolphin-store>
583
+
584
+ <!-- २. बिना जाभास्क्रिप्ट टेक्स्ट सर्च (input event मा चल्ने) -->
585
+ <input placeholder="Search products..." data-store-input="products.search(this.value)" />
586
+
587
+ <!-- ३. विना जाभास्क्रिप्ट ड्रपडाउन फिल्टर (change event मा चल्ने) -->
588
+ <select data-store-change="products.filter('category', this.value)">
589
+ <option value="">All Categories</option>
590
+ <option value="electronics">Electronics</option>
591
+ <option value="books">Books</option>
592
+ </select>
593
+
594
+ <!-- ४. मूल्यअनुसार सर्टिङ र फिल्टर क्लियर गर्ने बटनहरू -->
595
+ <button data-store-click="products.sort('price', true)">कम मूल्य पहिला</button>
596
+ <button data-store-click="products.sort('price', false)">उच्च मूल्य पहिला</button>
597
+ <button data-store-click="products.clearFilters()">फिल्टर हटाउनुहोस्</button>
598
+
599
+ <!-- ५. डाटाहरू रेन्डर गर्ने र डिलिट बटन राख्ने -->
600
+ <div data-rt-bind="store/products" data-rt-template="#product-card-template"></div>
601
+ <template id="product-card-template">
602
+ {#each items as item}
603
+ <div class="product-item">
604
+ <h4>{item.name}</h4>
605
+ <p>मूल्य: ${item.price}</p>
606
+ <!-- सिधै HTML बाट database item delete गर्ने (DOM स्वतः अपडेट हुन्छ!) -->
607
+ <button data-store-click="products.deleteById(item.id)">मेटाउनुहोस्</button>
608
+ </div>
609
+ {/each}
610
+ </template>
611
+ ```
612
+
506
613
  ### १०.२. Declarative Validations (`data-validate`)
507
614
  Apply robust validation rules to inputs and forms instantly. Tagged invalid inputs automatically receive `.invalid` classes and publish error text:
508
615
 
@@ -836,21 +943,68 @@ Dolphin Client मा भएको **DolphinStore** ले तपाईँला
836
943
  1. **स्वचालित HTTP Fetch**: `/devices` वा `/orders` मा `GET` रिक्वेस्ट पठाउँछ।
837
944
  2. **स्वचालित WebSocket Sync**: `db:sync/devices` च्यानलमा स्वतः सब्सक्राइब गर्छ र ब्याकइन्डमा कुनै आइटम `create`, `update`, वा `delete` हुँदा कलेक्सन डेटा रियल-टाइम अपडेट गर्छ।
838
945
 
839
- #### क) डेटा फिल्टरिङसर्टिङ (Filtering & Sorting)
840
- तपाईँले स्टोर कलेक्सनहरूमा `.where(filterFn)` `.orderBy(key, direction)` प्रयोग गरेर इन-मेमोरी फिल्टरसर्ट गर्न सक्नुहुन्छ:
946
+ #### क) chainable DataEngineFilters (डेटा फिल्टरिङ, सर्च र सर्टिङ)
947
+ Dolphin v2.0 ले नयाँ chainable `DataEngine` ल्याएको जसले इन-मेमोरी रुपमै धेरै छिटो प्रभावकारी फिल्टर, सर्च र सर्टिङ गर्दछ:
948
+
949
+ ```javascript
950
+ const products = dolphin.store.products;
951
+
952
+ // १. Chainable search/filter/range/sort
953
+ const results = products
954
+ .search('laptop', ['name', 'description']) // Case-insensitive free text search
955
+ .filter('inStock', true) // Exact match filter
956
+ .range('price', 500, 1500) // Numeric range
957
+ .sort('price', true); // Sorting (field, ascending=true)
958
+
959
+ console.log("फिल्टर गरिएका उत्पादनहरू:", results.items);
960
+
961
+ // २. Pagination (पेजिनेसन)
962
+ const pageData = products.page(1, 10); // Page 1, size 10
963
+ console.log("Page data:", pageData.data);
964
+ console.log("Total pages:", pageData.pages);
965
+ console.log("Has next page?", pageData.hasNext);
966
+
967
+ // ३. filters clear गर्ने र reset गर्ने
968
+ products.clearFilters();
969
+ ```
970
+
971
+ #### ख) Optimistic UI Updates with Rollback (अप्टिमिस्टिक अपडेटहरू)
972
+ यदि तपाईँ इन्टरनेट स्लो भएको बेला वा रियल-टाइम अनुभव दिनका लागि क्लाइन्टमा डेटा तुरुन्त अपडेट (म्युटेट) गर्न चाहनुहुन्छ भने `optimisticDelete` र `optimisticUpdate` चलाउन सक्नुहुन्छ। यसले UI मा तुरुन्तै परिवर्तन देखाउँछ र ब्याकइन्ड API फेल भएमा स्वतः पहिलेकै अवस्थामा फर्काइदिन्छ (rollback):
841
973
 
842
974
  ```javascript
843
- const orders = dolphin.store.orders;
975
+ // १. Optimistic Delete (मेटाउने र फेल भएमा रोलब्याक गर्ने)
976
+ await dolphin.store.products.optimisticDelete(105, () => {
977
+ return dolphin.api.delete('/products/105');
978
+ });
844
979
 
845
- // active total 50 भन्दा बढी भएका अर्डरहरू फिल्टर गरी मूल्यअनुसार सर्ट गर्ने
846
- const highValueOrders = orders
847
- .where(o => o.status === 'active' && o.total > 50)
848
- .orderBy('price', 'desc');
980
+ // २. Optimistic Update (अपडेट गर्ने फेल भएमा रोलब्याक गर्ने)
981
+ await dolphin.store.products.optimisticUpdate(105, { price: 999 }, () => {
982
+ return dolphin.api.put('/products/105', { price: 999 });
983
+ });
984
+ ```
985
+
986
+ #### ग) Per-Item Loading State Tracking (प्रति-आइटम लोडिङ ट्र्याकिङ)
987
+ डेटाबेसका कुनै विशेष आइटमहरूमा प्रोसेसिंग (loading) हुँदैछ कि छैन भनेर जाँच गर्न प्रति-आइटम ट्र्याकिङ चलाउन सकिन्छ (उदाहरणका लागि: एउटा मात्र कार्डमा लोडिङ स्पिनर देखाउन):
988
+
989
+ ```javascript
990
+ const products = dolphin.store.products;
991
+
992
+ // १. लोडिङ सुरु भएको जनाउन
993
+ products.trackStart(105);
994
+ console.log(products.isLoading(105)); // true
849
995
 
850
- console.log("Filtered orders:", highValueOrders.items);
996
+ // २. लोडिङ समाप्त भएको जनाउन
997
+ products.trackEnd(105);
998
+ console.log(products.isLoading(105)); // false
851
999
  ```
852
1000
 
853
- #### ) म्यानुअल सब्सक्राइब (Manual Subscription & Snapshot)
1001
+ #### ) Race Condition Guards, Batching & Resource Cleanup
1002
+ Dolphin v2.0 ले ब्याकइन्ड र मेमोरी व्यवस्थापनमा निम्न सुधारहरू गरेको छ:
1003
+ * **Race Condition Guard**: एउटै कलेक्सन बारम्बार र एकै पटक fetch हुन खोज्दा रेसिङ हुन नदिन `_fetching` Set गार्ड राखिएको छ।
1004
+ * **Batch Notification (`queueMicrotask`)**: धेरै छिटो र लगातार हुने स्टेट अपडेटहरूलाई एकै पटक ब्याच गरी DOM र ऐप्लिकेसनलाई single render cycle मा सूचित गर्दछ, जसले गर्दा UI ६०fps मा स्मूथ चल्छ।
1005
+ * **Memory Leak Fixes on Destroy**: `destroy()` कल गर्दा सबै एक्टिभ WebSocket च्यानलहरू र unsubscribe handlers लाई सफा गरिन्छ।
1006
+
1007
+ #### ङ) म्यानुअल सब्सक्राइब (Manual Subscription & Snapshot)
854
1008
  ```javascript
855
1009
  // १. स्टोरमा आउने जुनसुकै अपडेट सुन्न सब्सक्राइब गर्ने
856
1010
  const unsubscribe = dolphin.store.subscribe(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dolphin-client",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "HTML is back! Hookless, framework-agnostic real-time reactive DOM-binding client for Dolphin Server with WebSockets, WebRTC signaling, and offline REST API fallbacks.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",