rask-ui 0.4.1 → 0.4.2

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/batch.d.ts CHANGED
@@ -3,5 +3,4 @@ export type QueuedCallback = (() => void) & {
3
3
  };
4
4
  export declare function queue(cb: QueuedCallback): void;
5
5
  export declare function syncBatch(cb: () => void): void;
6
- export declare function installEventBatching(target?: EventTarget): void;
7
6
  //# sourceMappingURL=batch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAwClE,wBAAgB,KAAK,CAAC,EAAE,EAAE,cAAc,QAYvC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAavC;AAED,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,WAAsB,QA4BlE"}
1
+ {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAgElE,wBAAgB,KAAK,CAAC,EAAE,EAAE,cAAc,QAevC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAgBvC"}
package/dist/batch.js CHANGED
@@ -1,30 +1,11 @@
1
- const INTERACTIVE_EVENTS = [
2
- // DISCRETE
3
- "beforeinput",
4
- "input",
5
- "change",
6
- "compositionend",
7
- "keydown",
8
- "keyup",
9
- "click",
10
- "contextmenu",
11
- "submit",
12
- "reset",
13
- // GESTURE START
14
- "pointerdown",
15
- "mousedown",
16
- "touchstart",
17
- // GESTURE END
18
- "pointerup",
19
- "mouseup",
20
- "touchend",
21
- "touchcancel",
22
- ];
23
1
  const asyncQueue = [];
24
2
  const syncQueue = [];
25
3
  let inInteractive = 0;
26
4
  let asyncScheduled = false;
27
5
  let inSyncBatch = 0;
6
+ // New: guards against re-entrant flushing
7
+ let inAsyncFlush = false;
8
+ let inSyncFlush = false;
28
9
  function scheduleAsyncFlush() {
29
10
  if (asyncScheduled)
30
11
  return;
@@ -32,29 +13,53 @@ function scheduleAsyncFlush() {
32
13
  queueMicrotask(flushAsyncQueue);
33
14
  }
34
15
  function flushAsyncQueue() {
35
- asyncScheduled = false;
36
- if (!asyncQueue.length)
16
+ if (inAsyncFlush)
37
17
  return;
38
- for (let i = 0; i < asyncQueue.length; i++) {
39
- const cb = asyncQueue[i];
40
- asyncQueue[i] = undefined;
41
- cb();
42
- cb.__queued = false;
18
+ inAsyncFlush = true;
19
+ asyncScheduled = false;
20
+ try {
21
+ if (!asyncQueue.length)
22
+ return;
23
+ // Note: we intentionally DO NOT snapshot.
24
+ // If callbacks queue more async work, it gets picked up
25
+ // in this same loop because length grows.
26
+ for (let i = 0; i < asyncQueue.length; i++) {
27
+ const cb = asyncQueue[i];
28
+ asyncQueue[i] = undefined;
29
+ cb();
30
+ cb.__queued = false;
31
+ }
32
+ asyncQueue.length = 0;
33
+ }
34
+ finally {
35
+ inAsyncFlush = false;
43
36
  }
44
- asyncQueue.length = 0;
45
37
  }
46
38
  function flushSyncQueue() {
47
- if (!syncQueue.length)
39
+ if (inSyncFlush)
48
40
  return;
49
- for (let i = 0; i < syncQueue.length; i++) {
50
- const cb = syncQueue[i];
51
- syncQueue[i] = undefined;
52
- cb();
53
- cb.__queued = false;
41
+ inSyncFlush = true;
42
+ try {
43
+ if (!syncQueue.length)
44
+ return;
45
+ // Same pattern as async: no snapshot, just iterate.
46
+ // New callbacks queued via syncBatch inside this flush
47
+ // will be pushed to syncQueue and picked up by this loop.
48
+ for (let i = 0; i < syncQueue.length; i++) {
49
+ const cb = syncQueue[i];
50
+ syncQueue[i] = undefined;
51
+ cb();
52
+ cb.__queued = false;
53
+ }
54
+ syncQueue.length = 0;
55
+ }
56
+ finally {
57
+ inSyncFlush = false;
54
58
  }
55
- syncQueue.length = 0;
56
59
  }
57
60
  export function queue(cb) {
61
+ // Optional: uncomment this if you want deduping:
62
+ // if (cb.__queued) return;
58
63
  cb.__queued = true;
59
64
  if (inSyncBatch) {
60
65
  syncQueue.push(cb);
@@ -69,39 +74,16 @@ export function syncBatch(cb) {
69
74
  inSyncBatch++;
70
75
  try {
71
76
  cb();
72
- // Only flush on successful completion
73
- inSyncBatch--;
74
- if (!inSyncBatch) {
75
- flushSyncQueue();
76
- }
77
77
  }
78
78
  catch (e) {
79
79
  inSyncBatch--;
80
- throw e; // Re-throw without flushing
80
+ throw e; // no flush on error
81
+ }
82
+ inSyncBatch--;
83
+ if (!inSyncBatch) {
84
+ // Only the outermost syncBatch triggers a flush.
85
+ // If this happens *inside* an ongoing flushSyncQueue,
86
+ // inSyncFlush will be true and flushSyncQueue will no-op.
87
+ flushSyncQueue();
81
88
  }
82
- }
83
- export function installEventBatching(target = document) {
84
- const captureOptions = {
85
- capture: true,
86
- passive: true,
87
- };
88
- const bubbleOptions = { passive: true };
89
- const onCapture = () => {
90
- inInteractive++;
91
- scheduleAsyncFlush(); // backup in case of stopPropagation
92
- };
93
- const onBubble = () => {
94
- if (--inInteractive === 0 && asyncQueue.length) {
95
- // Flush inline once outermost interactive event finishes
96
- flushAsyncQueue();
97
- }
98
- };
99
- INTERACTIVE_EVENTS.forEach((type) => {
100
- target.addEventListener(type, onCapture, captureOptions);
101
- });
102
- queueMicrotask(() => {
103
- INTERACTIVE_EVENTS.forEach((type) => {
104
- target.addEventListener(type, onBubble, bubbleOptions);
105
- });
106
- });
107
89
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { render as infernoRender } from "inferno";
1
+ export { render } from "./render";
2
2
  export { onCleanup, onMount } from "./component";
3
3
  export { createContext } from "./createContext";
4
4
  export { createState } from "./createState";
@@ -10,5 +10,5 @@ export { createRef } from "inferno";
10
10
  export { createView } from "./createView";
11
11
  export { createEffect } from "./createEffect";
12
12
  export { createComputed } from "./createComputed";
13
- export declare function render(...params: Parameters<typeof infernoRender>): void;
13
+ export { syncBatch } from "./batch";
14
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,SAAS,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,wBAAgB,MAAM,CAAC,GAAG,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,QAMjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
- import { render as infernoRender } from "inferno";
2
- import { installEventBatching } from "./batch";
1
+ export { render } from "./render";
3
2
  export { onCleanup, onMount } from "./component";
4
3
  export { createContext } from "./createContext";
5
4
  export { createState } from "./createState";
@@ -11,10 +10,4 @@ export { createRef } from "inferno";
11
10
  export { createView } from "./createView";
12
11
  export { createEffect } from "./createEffect";
13
12
  export { createComputed } from "./createComputed";
14
- export function render(...params) {
15
- if (!params[1]) {
16
- throw new Error("You need a target container");
17
- }
18
- installEventBatching(params[1]);
19
- return infernoRender(...params);
20
- }
13
+ export { syncBatch } from "./batch";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Temporarily patches document.addEventListener during render to capture
3
+ * and wrap Inferno's delegated event listeners with syncBatch
4
+ */
5
+ export declare function patchInfernoEventHandling(renderFn: () => void): void;
6
+ //# sourceMappingURL=patchInferno.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patchInferno.d.ts","sourceRoot":"","sources":["../src/patchInferno.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,IAAI,QA0D7D"}
@@ -0,0 +1,53 @@
1
+ import { syncBatch } from "./batch";
2
+ /**
3
+ * Temporarily patches document.addEventListener during render to capture
4
+ * and wrap Inferno's delegated event listeners with syncBatch
5
+ */
6
+ export function patchInfernoEventHandling(renderFn) {
7
+ const originalAddEventListener = document.addEventListener.bind(document);
8
+ const patchedEvents = new Set();
9
+ // Inferno's delegated events
10
+ const INFERNO_EVENTS = [
11
+ "click",
12
+ "dblclick",
13
+ "focusin",
14
+ "focusout",
15
+ "keydown",
16
+ "keypress",
17
+ "keyup",
18
+ "mousedown",
19
+ "mousemove",
20
+ "mouseup",
21
+ "touchend",
22
+ "touchmove",
23
+ "touchstart",
24
+ "change",
25
+ "input",
26
+ "submit",
27
+ ];
28
+ // Temporarily replace addEventListener
29
+ document.addEventListener = function (type, listener, options) {
30
+ // Only wrap Inferno's delegated event listeners
31
+ if (INFERNO_EVENTS.includes(type) &&
32
+ typeof listener === "function" &&
33
+ !patchedEvents.has(type)) {
34
+ patchedEvents.add(type);
35
+ const wrappedListener = function (event) {
36
+ syncBatch(() => {
37
+ listener.call(this, event);
38
+ });
39
+ };
40
+ return originalAddEventListener(type, wrappedListener, options);
41
+ }
42
+ // @ts-ignore
43
+ return originalAddEventListener(type, listener, options);
44
+ };
45
+ try {
46
+ // Call render - Inferno will synchronously attach its listeners
47
+ renderFn();
48
+ }
49
+ finally {
50
+ // Restore original addEventListener
51
+ document.addEventListener = originalAddEventListener;
52
+ }
53
+ }
@@ -0,0 +1,8 @@
1
+ import { render as infernoRender } from "inferno";
2
+ /**
3
+ * Renders a component with automatic event batching.
4
+ * Temporarily patches document.addEventListener to wrap
5
+ * Inferno's delegated event listeners with syncBatch.
6
+ */
7
+ export declare function render(...params: Parameters<typeof infernoRender>): void;
8
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,SAAS,CAAC;AAGlD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,GAAG,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,QAkEjE"}
package/dist/render.js ADDED
@@ -0,0 +1,62 @@
1
+ import { render as infernoRender } from "inferno";
2
+ import { syncBatch } from "./batch";
3
+ /**
4
+ * Renders a component with automatic event batching.
5
+ * Temporarily patches document.addEventListener to wrap
6
+ * Inferno's delegated event listeners with syncBatch.
7
+ */
8
+ export function render(...params) {
9
+ if (!params[1]) {
10
+ throw new Error("You need a target container");
11
+ }
12
+ /**
13
+ * Temporarily patches document.addEventListener during render to capture
14
+ * and wrap Inferno's delegated event listeners with syncBatch
15
+ */
16
+ const originalAddEventListener = document.addEventListener.bind(document);
17
+ const patchedEvents = new Set();
18
+ // Inferno's delegated events
19
+ const INFERNO_EVENTS = [
20
+ "click",
21
+ "dblclick",
22
+ "focusin",
23
+ "focusout",
24
+ "keydown",
25
+ "keypress",
26
+ "keyup",
27
+ "mousedown",
28
+ "mousemove",
29
+ "mouseup",
30
+ "touchend",
31
+ "touchmove",
32
+ "touchstart",
33
+ "change",
34
+ "input",
35
+ "submit",
36
+ ];
37
+ // Temporarily replace addEventListener
38
+ document.addEventListener = function (type, listener, options) {
39
+ // Only wrap Inferno's delegated event listeners
40
+ if (INFERNO_EVENTS.includes(type) &&
41
+ typeof listener === "function" &&
42
+ !patchedEvents.has(type)) {
43
+ patchedEvents.add(type);
44
+ const wrappedListener = function (event) {
45
+ syncBatch(() => {
46
+ listener.call(this, event);
47
+ });
48
+ };
49
+ return originalAddEventListener(type, wrappedListener, options);
50
+ }
51
+ // @ts-ignore
52
+ return originalAddEventListener(type, listener, options);
53
+ };
54
+ try {
55
+ // Call render - Inferno will synchronously attach its listeners
56
+ return infernoRender(...params);
57
+ }
58
+ finally {
59
+ // Restore original addEventListener
60
+ document.addEventListener = originalAddEventListener;
61
+ }
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rask-ui",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",