@yiin/reactive-proxy-state 1.0.22 → 1.0.24

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/index.cjs CHANGED
@@ -34856,6 +34856,9 @@ __export(exports_src, {
34856
34856
  isComputed: () => isComputed,
34857
34857
  deepEqual: () => deepEqual,
34858
34858
  deepClone: () => deepClone,
34859
+ createRendererBridgeEmitter: () => createRendererBridgeEmitter,
34860
+ createMainBridgeEmitter: () => createMainBridgeEmitter,
34861
+ createBridgeEmitter: () => createBridgeEmitter,
34859
34862
  computed: () => computed,
34860
34863
  cleanupEffect: () => cleanupEffect,
34861
34864
  activeEffect: () => activeEffect
@@ -35986,6 +35989,80 @@ function wrapArray(arr, emit, path = []) {
35986
35989
  return result;
35987
35990
  };
35988
35991
  return methodCache[prop];
35992
+ case "find": {
35993
+ track(target, Symbol.iterator);
35994
+ if (!methodCache[prop]) {
35995
+ methodCache[prop] = function(predicate, thisArg) {
35996
+ const idx = Array.prototype.findIndex.call(target, predicate, thisArg);
35997
+ if (idx === -1)
35998
+ return;
35999
+ const value2 = target[idx];
36000
+ if (!isObject2(value2))
36001
+ return value2;
36002
+ const propKey = String(idx);
36003
+ const pathKey = path.length > 0 ? `${path.join(".")}.${propKey}` : propKey;
36004
+ let newPath = getPathConcat(pathKey);
36005
+ if (newPath === undefined) {
36006
+ newPath = path.concat(propKey);
36007
+ setPathConcat(pathKey, newPath);
36008
+ }
36009
+ if (globalSeen.has(value2))
36010
+ return globalSeen.get(value2);
36011
+ const cachedValueProxy = wrapperCache.get(value2);
36012
+ if (cachedValueProxy)
36013
+ return cachedValueProxy;
36014
+ if (Array.isArray(value2))
36015
+ return wrapArray(value2, emit, newPath);
36016
+ if (value2 instanceof Map)
36017
+ return wrapMap(value2, emit, newPath);
36018
+ if (value2 instanceof Set)
36019
+ return wrapSet(value2, emit, newPath);
36020
+ if (value2 instanceof Date)
36021
+ return new Date(value2.getTime());
36022
+ return reactive(value2, emit, newPath);
36023
+ };
36024
+ }
36025
+ return methodCache[prop];
36026
+ }
36027
+ case "at": {
36028
+ track(target, Symbol.iterator);
36029
+ if (!methodCache[prop]) {
36030
+ methodCache[prop] = function(index) {
36031
+ let idx = Number(index);
36032
+ if (!Number.isInteger(idx))
36033
+ idx = Math.trunc(idx);
36034
+ if (idx < 0)
36035
+ idx = target.length + idx;
36036
+ if (idx < 0 || idx >= target.length)
36037
+ return;
36038
+ const value2 = target[idx];
36039
+ if (!isObject2(value2))
36040
+ return value2;
36041
+ const propKey = String(idx);
36042
+ const pathKey = path.length > 0 ? `${path.join(".")}.${propKey}` : propKey;
36043
+ let newPath = getPathConcat(pathKey);
36044
+ if (newPath === undefined) {
36045
+ newPath = path.concat(propKey);
36046
+ setPathConcat(pathKey, newPath);
36047
+ }
36048
+ if (globalSeen.has(value2))
36049
+ return globalSeen.get(value2);
36050
+ const cachedValueProxy = wrapperCache.get(value2);
36051
+ if (cachedValueProxy)
36052
+ return cachedValueProxy;
36053
+ if (Array.isArray(value2))
36054
+ return wrapArray(value2, emit, newPath);
36055
+ if (value2 instanceof Map)
36056
+ return wrapMap(value2, emit, newPath);
36057
+ if (value2 instanceof Set)
36058
+ return wrapSet(value2, emit, newPath);
36059
+ if (value2 instanceof Date)
36060
+ return new Date(value2.getTime());
36061
+ return reactive(value2, emit, newPath);
36062
+ };
36063
+ }
36064
+ return methodCache[prop];
36065
+ }
35989
36066
  case Symbol.iterator:
35990
36067
  case "values":
35991
36068
  case "keys":
@@ -35995,7 +36072,6 @@ function wrapArray(arr, emit, path = []) {
35995
36072
  case "filter":
35996
36073
  case "reduce":
35997
36074
  case "reduceRight":
35998
- case "find":
35999
36075
  case "findIndex":
36000
36076
  case "every":
36001
36077
  case "some":
@@ -36487,3 +36563,94 @@ function trackVueReactiveEvents(vueState, emit, options = {}) {
36487
36563
  }
36488
36564
  return stop;
36489
36565
  }
36566
+ // src/integrations/electron-bridge.ts
36567
+ function makeTx() {
36568
+ try {
36569
+ const g = globalThis;
36570
+ if (g.crypto && typeof g.crypto.randomUUID === "function") {
36571
+ return g.crypto.randomUUID();
36572
+ }
36573
+ } catch {}
36574
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
36575
+ }
36576
+ function createBridgeEmitter(opts) {
36577
+ const { id, apply, send, onMessage, forward, seenLimit = 1000 } = opts;
36578
+ let muteCount = 0;
36579
+ const mute = (fn) => {
36580
+ muteCount++;
36581
+ try {
36582
+ return fn();
36583
+ } finally {
36584
+ muteCount--;
36585
+ }
36586
+ };
36587
+ const seen = new Set;
36588
+ const order = [];
36589
+ const markSeen = (tx) => {
36590
+ if (seen.has(tx))
36591
+ return;
36592
+ seen.add(tx);
36593
+ order.push(tx);
36594
+ if (order.length > seenLimit) {
36595
+ const oldest = order.shift();
36596
+ seen.delete(oldest);
36597
+ }
36598
+ };
36599
+ const emit = (event) => {
36600
+ if (muteCount > 0)
36601
+ return;
36602
+ const msg = { tx: makeTx(), origin: id, event };
36603
+ markSeen(msg.tx);
36604
+ send(msg);
36605
+ };
36606
+ const unsubscribe = onMessage((msg, ctx) => {
36607
+ if (!msg || !msg.tx)
36608
+ return;
36609
+ if (seen.has(msg.tx))
36610
+ return;
36611
+ markSeen(msg.tx);
36612
+ if (msg.origin === id)
36613
+ return;
36614
+ mute(() => apply(msg.event));
36615
+ if (forward)
36616
+ forward(msg, ctx);
36617
+ });
36618
+ const stop = () => unsubscribe();
36619
+ return { emit, stop, mute };
36620
+ }
36621
+ function createRendererBridgeEmitter(opts) {
36622
+ const { id, channel, ipcRenderer, apply, seenLimit } = opts;
36623
+ return createBridgeEmitter({
36624
+ id,
36625
+ apply,
36626
+ seenLimit,
36627
+ send: (msg) => ipcRenderer.send(channel, msg),
36628
+ onMessage: (cb) => {
36629
+ const handler = (_e, msg) => cb(msg);
36630
+ ipcRenderer.on(channel, handler);
36631
+ return () => ipcRenderer.off(channel, handler);
36632
+ }
36633
+ });
36634
+ }
36635
+ function createMainBridgeEmitter(opts) {
36636
+ const { id = "main", channel, ipcMain, windows, apply, seenLimit } = opts;
36637
+ const broadcast = (msg, exceptId) => {
36638
+ for (const w of windows()) {
36639
+ if (exceptId != null && w.webContents.id === exceptId)
36640
+ continue;
36641
+ w.webContents.send(channel, msg);
36642
+ }
36643
+ };
36644
+ return createBridgeEmitter({
36645
+ id,
36646
+ apply,
36647
+ seenLimit,
36648
+ send: (msg) => broadcast(msg),
36649
+ onMessage: (cb) => {
36650
+ const handler = (e, msg) => cb(msg, { senderId: e?.sender?.id });
36651
+ ipcMain.on(channel, handler);
36652
+ return () => ipcMain.off(channel, handler);
36653
+ },
36654
+ forward: (msg, ctx) => broadcast(msg, ctx?.senderId)
36655
+ });
36656
+ }
package/dist/index.d.ts CHANGED
@@ -8,3 +8,4 @@ export * from './ref';
8
8
  export * from './computed';
9
9
  export * from './mark-raw';
10
10
  export * from './integrations/vue3';
11
+ export * from './integrations/electron-bridge';
package/dist/index.js CHANGED
@@ -35930,6 +35930,80 @@ function wrapArray(arr, emit, path = []) {
35930
35930
  return result;
35931
35931
  };
35932
35932
  return methodCache[prop];
35933
+ case "find": {
35934
+ track(target, Symbol.iterator);
35935
+ if (!methodCache[prop]) {
35936
+ methodCache[prop] = function(predicate, thisArg) {
35937
+ const idx = Array.prototype.findIndex.call(target, predicate, thisArg);
35938
+ if (idx === -1)
35939
+ return;
35940
+ const value2 = target[idx];
35941
+ if (!isObject2(value2))
35942
+ return value2;
35943
+ const propKey = String(idx);
35944
+ const pathKey = path.length > 0 ? `${path.join(".")}.${propKey}` : propKey;
35945
+ let newPath = getPathConcat(pathKey);
35946
+ if (newPath === undefined) {
35947
+ newPath = path.concat(propKey);
35948
+ setPathConcat(pathKey, newPath);
35949
+ }
35950
+ if (globalSeen.has(value2))
35951
+ return globalSeen.get(value2);
35952
+ const cachedValueProxy = wrapperCache.get(value2);
35953
+ if (cachedValueProxy)
35954
+ return cachedValueProxy;
35955
+ if (Array.isArray(value2))
35956
+ return wrapArray(value2, emit, newPath);
35957
+ if (value2 instanceof Map)
35958
+ return wrapMap(value2, emit, newPath);
35959
+ if (value2 instanceof Set)
35960
+ return wrapSet(value2, emit, newPath);
35961
+ if (value2 instanceof Date)
35962
+ return new Date(value2.getTime());
35963
+ return reactive(value2, emit, newPath);
35964
+ };
35965
+ }
35966
+ return methodCache[prop];
35967
+ }
35968
+ case "at": {
35969
+ track(target, Symbol.iterator);
35970
+ if (!methodCache[prop]) {
35971
+ methodCache[prop] = function(index) {
35972
+ let idx = Number(index);
35973
+ if (!Number.isInteger(idx))
35974
+ idx = Math.trunc(idx);
35975
+ if (idx < 0)
35976
+ idx = target.length + idx;
35977
+ if (idx < 0 || idx >= target.length)
35978
+ return;
35979
+ const value2 = target[idx];
35980
+ if (!isObject2(value2))
35981
+ return value2;
35982
+ const propKey = String(idx);
35983
+ const pathKey = path.length > 0 ? `${path.join(".")}.${propKey}` : propKey;
35984
+ let newPath = getPathConcat(pathKey);
35985
+ if (newPath === undefined) {
35986
+ newPath = path.concat(propKey);
35987
+ setPathConcat(pathKey, newPath);
35988
+ }
35989
+ if (globalSeen.has(value2))
35990
+ return globalSeen.get(value2);
35991
+ const cachedValueProxy = wrapperCache.get(value2);
35992
+ if (cachedValueProxy)
35993
+ return cachedValueProxy;
35994
+ if (Array.isArray(value2))
35995
+ return wrapArray(value2, emit, newPath);
35996
+ if (value2 instanceof Map)
35997
+ return wrapMap(value2, emit, newPath);
35998
+ if (value2 instanceof Set)
35999
+ return wrapSet(value2, emit, newPath);
36000
+ if (value2 instanceof Date)
36001
+ return new Date(value2.getTime());
36002
+ return reactive(value2, emit, newPath);
36003
+ };
36004
+ }
36005
+ return methodCache[prop];
36006
+ }
35933
36007
  case Symbol.iterator:
35934
36008
  case "values":
35935
36009
  case "keys":
@@ -35939,7 +36013,6 @@ function wrapArray(arr, emit, path = []) {
35939
36013
  case "filter":
35940
36014
  case "reduce":
35941
36015
  case "reduceRight":
35942
- case "find":
35943
36016
  case "findIndex":
35944
36017
  case "every":
35945
36018
  case "some":
@@ -36431,6 +36504,97 @@ function trackVueReactiveEvents(vueState, emit, options = {}) {
36431
36504
  }
36432
36505
  return stop;
36433
36506
  }
36507
+ // src/integrations/electron-bridge.ts
36508
+ function makeTx() {
36509
+ try {
36510
+ const g = globalThis;
36511
+ if (g.crypto && typeof g.crypto.randomUUID === "function") {
36512
+ return g.crypto.randomUUID();
36513
+ }
36514
+ } catch {}
36515
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
36516
+ }
36517
+ function createBridgeEmitter(opts) {
36518
+ const { id, apply, send, onMessage, forward, seenLimit = 1000 } = opts;
36519
+ let muteCount = 0;
36520
+ const mute = (fn) => {
36521
+ muteCount++;
36522
+ try {
36523
+ return fn();
36524
+ } finally {
36525
+ muteCount--;
36526
+ }
36527
+ };
36528
+ const seen = new Set;
36529
+ const order = [];
36530
+ const markSeen = (tx) => {
36531
+ if (seen.has(tx))
36532
+ return;
36533
+ seen.add(tx);
36534
+ order.push(tx);
36535
+ if (order.length > seenLimit) {
36536
+ const oldest = order.shift();
36537
+ seen.delete(oldest);
36538
+ }
36539
+ };
36540
+ const emit = (event) => {
36541
+ if (muteCount > 0)
36542
+ return;
36543
+ const msg = { tx: makeTx(), origin: id, event };
36544
+ markSeen(msg.tx);
36545
+ send(msg);
36546
+ };
36547
+ const unsubscribe = onMessage((msg, ctx) => {
36548
+ if (!msg || !msg.tx)
36549
+ return;
36550
+ if (seen.has(msg.tx))
36551
+ return;
36552
+ markSeen(msg.tx);
36553
+ if (msg.origin === id)
36554
+ return;
36555
+ mute(() => apply(msg.event));
36556
+ if (forward)
36557
+ forward(msg, ctx);
36558
+ });
36559
+ const stop = () => unsubscribe();
36560
+ return { emit, stop, mute };
36561
+ }
36562
+ function createRendererBridgeEmitter(opts) {
36563
+ const { id, channel, ipcRenderer, apply, seenLimit } = opts;
36564
+ return createBridgeEmitter({
36565
+ id,
36566
+ apply,
36567
+ seenLimit,
36568
+ send: (msg) => ipcRenderer.send(channel, msg),
36569
+ onMessage: (cb) => {
36570
+ const handler = (_e, msg) => cb(msg);
36571
+ ipcRenderer.on(channel, handler);
36572
+ return () => ipcRenderer.off(channel, handler);
36573
+ }
36574
+ });
36575
+ }
36576
+ function createMainBridgeEmitter(opts) {
36577
+ const { id = "main", channel, ipcMain, windows, apply, seenLimit } = opts;
36578
+ const broadcast = (msg, exceptId) => {
36579
+ for (const w of windows()) {
36580
+ if (exceptId != null && w.webContents.id === exceptId)
36581
+ continue;
36582
+ w.webContents.send(channel, msg);
36583
+ }
36584
+ };
36585
+ return createBridgeEmitter({
36586
+ id,
36587
+ apply,
36588
+ seenLimit,
36589
+ send: (msg) => broadcast(msg),
36590
+ onMessage: (cb) => {
36591
+ const handler = (e, msg) => cb(msg, { senderId: e?.sender?.id });
36592
+ ipcMain.on(channel, handler);
36593
+ return () => ipcMain.off(channel, handler);
36594
+ },
36595
+ forward: (msg, ctx) => broadcast(msg, ctx?.senderId)
36596
+ });
36597
+ }
36434
36598
  export {
36435
36599
  watchEffect,
36436
36600
  watch,
@@ -36454,6 +36618,9 @@ export {
36454
36618
  isComputed,
36455
36619
  deepEqual,
36456
36620
  deepClone,
36621
+ createRendererBridgeEmitter,
36622
+ createMainBridgeEmitter,
36623
+ createBridgeEmitter,
36457
36624
  computed,
36458
36625
  cleanupEffect,
36459
36626
  activeEffect
@@ -0,0 +1,71 @@
1
+ import type { StateEvent } from "../types";
2
+ export type BridgeMessage = {
3
+ tx: string;
4
+ origin: string;
5
+ event: StateEvent;
6
+ };
7
+ type OnMessage = (cb: (msg: BridgeMessage, ctx?: any) => void) => () => void;
8
+ type Send = (msg: BridgeMessage, ctx?: any) => void;
9
+ type Forward = (msg: BridgeMessage, ctx?: any) => void;
10
+ /**
11
+ * Create a loop-safe bridge emitter for bi-directional sync.
12
+ * - Tags every message with tx + origin
13
+ * - Mutes emit while applying remote updates
14
+ * - Dedupe by tx using a small LRU
15
+ */
16
+ export declare function createBridgeEmitter(opts: {
17
+ id: string;
18
+ apply: (event: StateEvent) => void;
19
+ send: Send;
20
+ onMessage: OnMessage;
21
+ forward?: Forward;
22
+ seenLimit?: number;
23
+ }): {
24
+ emit: (event: StateEvent) => void;
25
+ stop: () => void;
26
+ mute: <T>(fn: () => T) => T;
27
+ };
28
+ /**
29
+ * Renderer-side bridge bound to Electron's ipcRenderer.
30
+ * Keep this generic by accepting a minimal ipcRenderer-like object.
31
+ */
32
+ export declare function createRendererBridgeEmitter(opts: {
33
+ id: string;
34
+ channel: string;
35
+ ipcRenderer: {
36
+ send: (channel: string, msg: any) => void;
37
+ on: (channel: string, handler: (event: any, msg: any) => void) => void;
38
+ off: (channel: string, handler: (event: any, msg: any) => void) => void;
39
+ };
40
+ apply: (event: StateEvent) => void;
41
+ seenLimit?: number;
42
+ }): {
43
+ emit: (event: StateEvent) => void;
44
+ stop: () => void;
45
+ mute: <T>(fn: () => T) => T;
46
+ };
47
+ /**
48
+ * Main-process bridge bound to Electron's ipcMain and BrowserWindows.
49
+ * Accepts a provider for windows to avoid importing Electron types.
50
+ */
51
+ export declare function createMainBridgeEmitter(opts: {
52
+ id?: string;
53
+ channel: string;
54
+ ipcMain: {
55
+ on: (channel: string, handler: (event: any, msg: any) => void) => void;
56
+ off: (channel: string, handler: (event: any, msg: any) => void) => void;
57
+ };
58
+ windows: () => {
59
+ webContents: {
60
+ id: number;
61
+ send: (channel: string, msg: any) => void;
62
+ };
63
+ }[];
64
+ apply: (event: StateEvent) => void;
65
+ seenLimit?: number;
66
+ }): {
67
+ emit: (event: StateEvent) => void;
68
+ stop: () => void;
69
+ mute: <T>(fn: () => T) => T;
70
+ };
71
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yiin/reactive-proxy-state",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "author": "Yiin <stanislovas@yiin.lt>",
5
5
  "repository": {
6
6
  "type": "git",