@rplx/core 0.2.0 → 0.2.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/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +50 -22
- package/dist/index.mjs +50 -22
- package/package.json +15 -6
package/dist/index.d.mts
CHANGED
|
@@ -79,6 +79,12 @@ interface StoreConfig<State, Cofx = {}> {
|
|
|
79
79
|
enabled?: boolean;
|
|
80
80
|
debounceTime?: number;
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Optional scheduler for batching state updates
|
|
84
|
+
* Defaults to requestAnimationFrame
|
|
85
|
+
* @internal - For testing purposes only
|
|
86
|
+
*/
|
|
87
|
+
__scheduler?: (callback: FrameRequestCallback) => number;
|
|
82
88
|
}
|
|
83
89
|
interface QueuedEvent {
|
|
84
90
|
eventKey: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -79,6 +79,12 @@ interface StoreConfig<State, Cofx = {}> {
|
|
|
79
79
|
enabled?: boolean;
|
|
80
80
|
debounceTime?: number;
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Optional scheduler for batching state updates
|
|
84
|
+
* Defaults to requestAnimationFrame
|
|
85
|
+
* @internal - For testing purposes only
|
|
86
|
+
*/
|
|
87
|
+
__scheduler?: (callback: FrameRequestCallback) => number;
|
|
82
88
|
}
|
|
83
89
|
interface QueuedEvent {
|
|
84
90
|
eventKey: string;
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,8 @@ function createRegistrar(options) {
|
|
|
77
77
|
} else {
|
|
78
78
|
console.warn(`re-frame: can't clear ${kind} handler for ${id}. Handler not found.`);
|
|
79
79
|
}
|
|
80
|
+
} else {
|
|
81
|
+
console.warn(`re-frame: can't clear ${kind} handler for ${id}. Handler not found.`);
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
}
|
|
@@ -84,13 +86,13 @@ function createRegistrar(options) {
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
// src/modules/state.ts
|
|
87
|
-
function createStateManager(initialState, onStateChange, onStateChangeForSubscriptions) {
|
|
89
|
+
function createStateManager(initialState, onStateChange, onStateChangeForSubscriptions, scheduler = requestAnimationFrame) {
|
|
88
90
|
let state = initialState;
|
|
89
91
|
const stateChanges = [];
|
|
90
92
|
let rafId = null;
|
|
91
93
|
const scheduleNotification = () => {
|
|
92
94
|
if (rafId !== null) return;
|
|
93
|
-
rafId =
|
|
95
|
+
rafId = scheduler(() => {
|
|
94
96
|
rafId = null;
|
|
95
97
|
if (stateChanges.length > 0) {
|
|
96
98
|
const latestState = stateChanges[stateChanges.length - 1];
|
|
@@ -375,21 +377,21 @@ function createEffectExecutor(deps) {
|
|
|
375
377
|
registerEffect("db", (newState) => {
|
|
376
378
|
stateManager.setState(newState);
|
|
377
379
|
});
|
|
378
|
-
registerEffect("dispatch",
|
|
380
|
+
registerEffect("dispatch", (config) => {
|
|
379
381
|
if (!config || typeof config.event !== "string") {
|
|
380
382
|
console.error("re-frame: ignoring bad :dispatch value. Expected {event: string, payload: any}, but got:", config);
|
|
381
383
|
return;
|
|
382
384
|
}
|
|
383
|
-
|
|
385
|
+
dispatch(config.event, config.payload);
|
|
384
386
|
});
|
|
385
|
-
registerEffect("dispatch-n",
|
|
387
|
+
registerEffect("dispatch-n", (configs) => {
|
|
386
388
|
if (!Array.isArray(configs)) {
|
|
387
389
|
console.error("re-frame: ignoring bad :dispatch-n value. Expected an array, but got:", configs);
|
|
388
390
|
return;
|
|
389
391
|
}
|
|
390
392
|
for (const config of configs) {
|
|
391
393
|
if (config && config.event) {
|
|
392
|
-
|
|
394
|
+
dispatch(config.event, config.payload);
|
|
393
395
|
}
|
|
394
396
|
}
|
|
395
397
|
});
|
|
@@ -676,33 +678,54 @@ function createRouter(deps) {
|
|
|
676
678
|
const { eventManager } = deps;
|
|
677
679
|
const eventQueue = [];
|
|
678
680
|
let isProcessing = false;
|
|
681
|
+
let processingPromise = null;
|
|
679
682
|
async function processEvent(eventKey, payload) {
|
|
680
683
|
await eventManager.handleEvent(eventKey, payload);
|
|
681
684
|
}
|
|
682
685
|
async function processQueue() {
|
|
683
686
|
if (isProcessing) return;
|
|
684
687
|
isProcessing = true;
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
688
|
+
const promise = (async () => {
|
|
689
|
+
try {
|
|
690
|
+
while (eventQueue.length > 0) {
|
|
691
|
+
const event = eventQueue.shift();
|
|
692
|
+
try {
|
|
693
|
+
await processEvent(event.eventKey, event.payload);
|
|
694
|
+
event.resolve();
|
|
695
|
+
} catch (error) {
|
|
696
|
+
event.reject(error instanceof Error ? error : new Error(String(error)));
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
} finally {
|
|
700
|
+
isProcessing = false;
|
|
701
|
+
processingPromise = null;
|
|
689
702
|
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
|
|
703
|
+
})();
|
|
704
|
+
processingPromise = promise;
|
|
705
|
+
await promise;
|
|
693
706
|
}
|
|
694
707
|
function dispatch(eventKey, payload) {
|
|
695
708
|
return new Promise((resolve, reject) => {
|
|
696
|
-
eventQueue.push({
|
|
709
|
+
eventQueue.push({
|
|
710
|
+
eventKey,
|
|
711
|
+
payload,
|
|
712
|
+
resolve,
|
|
713
|
+
reject
|
|
714
|
+
});
|
|
697
715
|
if (!isProcessing) {
|
|
698
|
-
processQueue().
|
|
699
|
-
|
|
700
|
-
|
|
716
|
+
processQueue().catch((error) => {
|
|
717
|
+
console.error("Unexpected error in processQueue:", error);
|
|
718
|
+
});
|
|
701
719
|
}
|
|
702
720
|
});
|
|
703
721
|
}
|
|
704
722
|
async function flush() {
|
|
705
|
-
|
|
723
|
+
if (processingPromise) {
|
|
724
|
+
await processingPromise;
|
|
725
|
+
}
|
|
726
|
+
if (eventQueue.length > 0) {
|
|
727
|
+
await processQueue();
|
|
728
|
+
}
|
|
706
729
|
}
|
|
707
730
|
return {
|
|
708
731
|
dispatch,
|
|
@@ -898,7 +921,9 @@ function createStore(config) {
|
|
|
898
921
|
if (subscriptionManagerRef) {
|
|
899
922
|
subscriptionManagerRef.notifyListeners(state);
|
|
900
923
|
}
|
|
901
|
-
}
|
|
924
|
+
},
|
|
925
|
+
config.__scheduler ?? requestAnimationFrame
|
|
926
|
+
// Use scheduler if provided, otherwise default to RAF
|
|
902
927
|
);
|
|
903
928
|
const subscriptionManager = createSubscriptionManager({
|
|
904
929
|
stateManager,
|
|
@@ -1075,7 +1100,8 @@ function debug() {
|
|
|
1075
1100
|
return context;
|
|
1076
1101
|
},
|
|
1077
1102
|
after: (context) => {
|
|
1078
|
-
|
|
1103
|
+
const newState = context.effects.db !== void 0 ? context.effects.db : context.coeffects.db;
|
|
1104
|
+
console.log("New State:", newState);
|
|
1079
1105
|
console.log("Effects:", context.effects);
|
|
1080
1106
|
console.groupEnd();
|
|
1081
1107
|
return context;
|
|
@@ -1086,7 +1112,8 @@ function after(fn) {
|
|
|
1086
1112
|
return {
|
|
1087
1113
|
id: "after",
|
|
1088
1114
|
after: (context) => {
|
|
1089
|
-
|
|
1115
|
+
const newState = context.effects.db !== void 0 ? context.effects.db : context.coeffects.db;
|
|
1116
|
+
fn(newState, context.effects);
|
|
1090
1117
|
return context;
|
|
1091
1118
|
}
|
|
1092
1119
|
};
|
|
@@ -1109,7 +1136,8 @@ function validate(schema) {
|
|
|
1109
1136
|
return {
|
|
1110
1137
|
id: "validate",
|
|
1111
1138
|
after: (context) => {
|
|
1112
|
-
const
|
|
1139
|
+
const stateToValidate = context.effects.db !== void 0 ? context.effects.db : context.coeffects.db;
|
|
1140
|
+
const result = schema(stateToValidate);
|
|
1113
1141
|
if (result !== true) {
|
|
1114
1142
|
console.error("State validation failed:", result);
|
|
1115
1143
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -42,6 +42,8 @@ function createRegistrar(options) {
|
|
|
42
42
|
} else {
|
|
43
43
|
console.warn(`re-frame: can't clear ${kind} handler for ${id}. Handler not found.`);
|
|
44
44
|
}
|
|
45
|
+
} else {
|
|
46
|
+
console.warn(`re-frame: can't clear ${kind} handler for ${id}. Handler not found.`);
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -49,13 +51,13 @@ function createRegistrar(options) {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
// src/modules/state.ts
|
|
52
|
-
function createStateManager(initialState, onStateChange, onStateChangeForSubscriptions) {
|
|
54
|
+
function createStateManager(initialState, onStateChange, onStateChangeForSubscriptions, scheduler = requestAnimationFrame) {
|
|
53
55
|
let state = initialState;
|
|
54
56
|
const stateChanges = [];
|
|
55
57
|
let rafId = null;
|
|
56
58
|
const scheduleNotification = () => {
|
|
57
59
|
if (rafId !== null) return;
|
|
58
|
-
rafId =
|
|
60
|
+
rafId = scheduler(() => {
|
|
59
61
|
rafId = null;
|
|
60
62
|
if (stateChanges.length > 0) {
|
|
61
63
|
const latestState = stateChanges[stateChanges.length - 1];
|
|
@@ -340,21 +342,21 @@ function createEffectExecutor(deps) {
|
|
|
340
342
|
registerEffect("db", (newState) => {
|
|
341
343
|
stateManager.setState(newState);
|
|
342
344
|
});
|
|
343
|
-
registerEffect("dispatch",
|
|
345
|
+
registerEffect("dispatch", (config) => {
|
|
344
346
|
if (!config || typeof config.event !== "string") {
|
|
345
347
|
console.error("re-frame: ignoring bad :dispatch value. Expected {event: string, payload: any}, but got:", config);
|
|
346
348
|
return;
|
|
347
349
|
}
|
|
348
|
-
|
|
350
|
+
dispatch(config.event, config.payload);
|
|
349
351
|
});
|
|
350
|
-
registerEffect("dispatch-n",
|
|
352
|
+
registerEffect("dispatch-n", (configs) => {
|
|
351
353
|
if (!Array.isArray(configs)) {
|
|
352
354
|
console.error("re-frame: ignoring bad :dispatch-n value. Expected an array, but got:", configs);
|
|
353
355
|
return;
|
|
354
356
|
}
|
|
355
357
|
for (const config of configs) {
|
|
356
358
|
if (config && config.event) {
|
|
357
|
-
|
|
359
|
+
dispatch(config.event, config.payload);
|
|
358
360
|
}
|
|
359
361
|
}
|
|
360
362
|
});
|
|
@@ -641,33 +643,54 @@ function createRouter(deps) {
|
|
|
641
643
|
const { eventManager } = deps;
|
|
642
644
|
const eventQueue = [];
|
|
643
645
|
let isProcessing = false;
|
|
646
|
+
let processingPromise = null;
|
|
644
647
|
async function processEvent(eventKey, payload) {
|
|
645
648
|
await eventManager.handleEvent(eventKey, payload);
|
|
646
649
|
}
|
|
647
650
|
async function processQueue() {
|
|
648
651
|
if (isProcessing) return;
|
|
649
652
|
isProcessing = true;
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
653
|
+
const promise = (async () => {
|
|
654
|
+
try {
|
|
655
|
+
while (eventQueue.length > 0) {
|
|
656
|
+
const event = eventQueue.shift();
|
|
657
|
+
try {
|
|
658
|
+
await processEvent(event.eventKey, event.payload);
|
|
659
|
+
event.resolve();
|
|
660
|
+
} catch (error) {
|
|
661
|
+
event.reject(error instanceof Error ? error : new Error(String(error)));
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} finally {
|
|
665
|
+
isProcessing = false;
|
|
666
|
+
processingPromise = null;
|
|
654
667
|
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
|
|
668
|
+
})();
|
|
669
|
+
processingPromise = promise;
|
|
670
|
+
await promise;
|
|
658
671
|
}
|
|
659
672
|
function dispatch(eventKey, payload) {
|
|
660
673
|
return new Promise((resolve, reject) => {
|
|
661
|
-
eventQueue.push({
|
|
674
|
+
eventQueue.push({
|
|
675
|
+
eventKey,
|
|
676
|
+
payload,
|
|
677
|
+
resolve,
|
|
678
|
+
reject
|
|
679
|
+
});
|
|
662
680
|
if (!isProcessing) {
|
|
663
|
-
processQueue().
|
|
664
|
-
|
|
665
|
-
|
|
681
|
+
processQueue().catch((error) => {
|
|
682
|
+
console.error("Unexpected error in processQueue:", error);
|
|
683
|
+
});
|
|
666
684
|
}
|
|
667
685
|
});
|
|
668
686
|
}
|
|
669
687
|
async function flush() {
|
|
670
|
-
|
|
688
|
+
if (processingPromise) {
|
|
689
|
+
await processingPromise;
|
|
690
|
+
}
|
|
691
|
+
if (eventQueue.length > 0) {
|
|
692
|
+
await processQueue();
|
|
693
|
+
}
|
|
671
694
|
}
|
|
672
695
|
return {
|
|
673
696
|
dispatch,
|
|
@@ -863,7 +886,9 @@ function createStore(config) {
|
|
|
863
886
|
if (subscriptionManagerRef) {
|
|
864
887
|
subscriptionManagerRef.notifyListeners(state);
|
|
865
888
|
}
|
|
866
|
-
}
|
|
889
|
+
},
|
|
890
|
+
config.__scheduler ?? requestAnimationFrame
|
|
891
|
+
// Use scheduler if provided, otherwise default to RAF
|
|
867
892
|
);
|
|
868
893
|
const subscriptionManager = createSubscriptionManager({
|
|
869
894
|
stateManager,
|
|
@@ -1040,7 +1065,8 @@ function debug() {
|
|
|
1040
1065
|
return context;
|
|
1041
1066
|
},
|
|
1042
1067
|
after: (context) => {
|
|
1043
|
-
|
|
1068
|
+
const newState = context.effects.db !== void 0 ? context.effects.db : context.coeffects.db;
|
|
1069
|
+
console.log("New State:", newState);
|
|
1044
1070
|
console.log("Effects:", context.effects);
|
|
1045
1071
|
console.groupEnd();
|
|
1046
1072
|
return context;
|
|
@@ -1051,7 +1077,8 @@ function after(fn) {
|
|
|
1051
1077
|
return {
|
|
1052
1078
|
id: "after",
|
|
1053
1079
|
after: (context) => {
|
|
1054
|
-
|
|
1080
|
+
const newState = context.effects.db !== void 0 ? context.effects.db : context.coeffects.db;
|
|
1081
|
+
fn(newState, context.effects);
|
|
1055
1082
|
return context;
|
|
1056
1083
|
}
|
|
1057
1084
|
};
|
|
@@ -1074,7 +1101,8 @@ function validate(schema) {
|
|
|
1074
1101
|
return {
|
|
1075
1102
|
id: "validate",
|
|
1076
1103
|
after: (context) => {
|
|
1077
|
-
const
|
|
1104
|
+
const stateToValidate = context.effects.db !== void 0 ? context.effects.db : context.coeffects.db;
|
|
1105
|
+
const result = schema(stateToValidate);
|
|
1078
1106
|
if (result !== true) {
|
|
1079
1107
|
console.error("State validation failed:", result);
|
|
1080
1108
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rplx/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A re-frame inspired state management library for TypeScript",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -19,9 +19,18 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
21
21
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
22
|
-
"clean": "rm -rf dist"
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"test": "jest",
|
|
24
|
+
"test:watch": "jest --watch",
|
|
25
|
+
"test:coverage": "jest --coverage",
|
|
26
|
+
"test:types": "jest __tests__/type-tests"
|
|
23
27
|
},
|
|
24
28
|
"devDependencies": {
|
|
29
|
+
"@jest/test-sequencer": "^30.2.0",
|
|
30
|
+
"@types/jest": "^30.0.0",
|
|
31
|
+
"expect-type": "^1.3.0",
|
|
32
|
+
"jest": "^30.2.0",
|
|
33
|
+
"ts-jest": "^29.4.6",
|
|
25
34
|
"tsup": "^8.0.0",
|
|
26
35
|
"typescript": "^5.2.2"
|
|
27
36
|
},
|
|
@@ -34,13 +43,13 @@
|
|
|
34
43
|
"license": "MIT",
|
|
35
44
|
"repository": {
|
|
36
45
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/anonymeye/
|
|
38
|
-
"directory": "packages/
|
|
46
|
+
"url": "https://github.com/anonymeye/ripplex.git",
|
|
47
|
+
"directory": "packages/ripplex"
|
|
39
48
|
},
|
|
40
49
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/anonymeye/
|
|
50
|
+
"url": "https://github.com/anonymeye/ripplex/issues"
|
|
42
51
|
},
|
|
43
|
-
"homepage": "https://github.com/anonymeye/
|
|
52
|
+
"homepage": "https://github.com/anonymeye/ripplex#readme",
|
|
44
53
|
"publishConfig": {
|
|
45
54
|
"access": "public"
|
|
46
55
|
}
|