@fictjs/runtime 0.0.3 → 0.0.5

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/slim.d.cts CHANGED
@@ -9,12 +9,22 @@ interface SignalAccessor<T> {
9
9
  * Computed accessor - function to get computed value
10
10
  */
11
11
  type ComputedAccessor<T> = () => T;
12
+ /**
13
+ * Effect scope disposer - function to dispose an effect scope
14
+ */
15
+ type EffectScopeDisposer = () => void;
12
16
  /**
13
17
  * Create a reactive signal
14
18
  * @param initialValue - The initial value
15
19
  * @returns A signal accessor function
16
20
  */
17
21
  declare function signal<T>(initialValue: T): SignalAccessor<T>;
22
+ /**
23
+ * Create a reactive effect scope
24
+ * @param fn - The scope function
25
+ * @returns An effect scope disposer function
26
+ */
27
+ declare function effectScope(fn: () => void): EffectScopeDisposer;
18
28
  declare const $state: <T>(value: T) => T;
19
29
  /**
20
30
  * Create a selector signal that efficiently updates only when the selected key matches.
@@ -60,9 +70,6 @@ type Effect = () => void | Cleanup;
60
70
  declare function createEffect(fn: Effect): () => void;
61
71
  declare function createRenderEffect(fn: Effect): () => void;
62
72
 
63
- declare function batch<T>(fn: () => T): T;
64
- declare function untrack<T>(fn: () => T): T;
65
-
66
73
  /**
67
74
  * Fict Reactive DOM Binding System
68
75
  *
@@ -76,6 +83,8 @@ declare function untrack<T>(fn: () => T): T;
76
83
  * - The compiler transforms JSX expressions to use these primitives
77
84
  */
78
85
 
86
+ /** A reactive value that can be either static or a getter function */
87
+ type MaybeReactive<T> = T | (() => T);
79
88
  /** Internal type for createElement function reference */
80
89
  type CreateElementFn = (node: FictNode) => Node;
81
90
  /** Handle returned by conditional/list bindings for cleanup */
@@ -212,6 +221,24 @@ type KeyFn<T> = (item: T, index: number) => string | number;
212
221
  */
213
222
  declare function createList<T>(items: () => T[], renderItem: (item: T, index: number) => FictNode, createElementFn: CreateElementFn, getKey?: KeyFn<T>): BindingHandle;
214
223
 
224
+ interface ReactiveScope {
225
+ run<T>(fn: () => T): T;
226
+ stop(): void;
227
+ }
228
+ /**
229
+ * Create an explicit reactive scope that can contain effects/memos and be stopped manually.
230
+ * The scope registers with the current root for cleanup.
231
+ */
232
+ declare function createScope(): ReactiveScope;
233
+ /**
234
+ * Run a block of reactive code inside a managed scope that follows a boolean flag.
235
+ * When the flag turns false, the scope is disposed and all contained effects/memos are cleaned up.
236
+ */
237
+ declare function runInScope(flag: MaybeReactive<boolean>, fn: () => void): void;
238
+
239
+ declare function batch<T>(fn: () => T): T;
240
+ declare function untrack<T>(fn: () => T): T;
241
+
215
242
  /**
216
243
  * Fict DOM Rendering System
217
244
  *
@@ -472,4 +499,4 @@ type PropGetter<T> = (() => T) & {
472
499
  */
473
500
  declare function useProp<T>(getter: () => T): PropGetter<T>;
474
501
 
475
- export { $state, Fragment, __fictPopContext, __fictProp, __fictPropsRest, __fictPushContext, __fictRender, __fictResetContext, __fictUseContext, __fictUseEffect, __fictUseMemo, __fictUseSignal, batch, bindAttribute, bindClass, bindEvent, bindProperty, bindRef, bindStyle, bindText, clearDelegatedEvents, createConditional, createEffect, createElement, createKeyedBlock, createKeyedList, createKeyedListContainer, createList, createMemo, createPropsProxy, createRenderEffect, createSelector, signal as createSignal, delegateEvents, destroyMarkerBlock, getFirstNodeAfter, insert, insertNodesBefore, mergeProps, moveMarkerBlock, onDestroy, __fictProp as prop, removeNodes, render, template, toNodeArray, untrack, useProp };
502
+ export { $state, Fragment, type ReactiveScope, __fictPopContext, __fictProp, __fictPropsRest, __fictPushContext, __fictRender, __fictResetContext, __fictUseContext, __fictUseEffect, __fictUseMemo, __fictUseSignal, batch, bindAttribute, bindClass, bindEvent, bindProperty, bindRef, bindStyle, bindText, clearDelegatedEvents, createConditional, createEffect, createElement, createKeyedBlock, createKeyedList, createKeyedListContainer, createList, createMemo, createPropsProxy, createRenderEffect, createScope, createSelector, signal as createSignal, delegateEvents, destroyMarkerBlock, effectScope, getFirstNodeAfter, insert, insertNodesBefore, mergeProps, moveMarkerBlock, onDestroy, __fictProp as prop, removeNodes, render, runInScope, template, toNodeArray, untrack, useProp };
package/dist/slim.d.ts CHANGED
@@ -9,12 +9,22 @@ interface SignalAccessor<T> {
9
9
  * Computed accessor - function to get computed value
10
10
  */
11
11
  type ComputedAccessor<T> = () => T;
12
+ /**
13
+ * Effect scope disposer - function to dispose an effect scope
14
+ */
15
+ type EffectScopeDisposer = () => void;
12
16
  /**
13
17
  * Create a reactive signal
14
18
  * @param initialValue - The initial value
15
19
  * @returns A signal accessor function
16
20
  */
17
21
  declare function signal<T>(initialValue: T): SignalAccessor<T>;
22
+ /**
23
+ * Create a reactive effect scope
24
+ * @param fn - The scope function
25
+ * @returns An effect scope disposer function
26
+ */
27
+ declare function effectScope(fn: () => void): EffectScopeDisposer;
18
28
  declare const $state: <T>(value: T) => T;
19
29
  /**
20
30
  * Create a selector signal that efficiently updates only when the selected key matches.
@@ -60,9 +70,6 @@ type Effect = () => void | Cleanup;
60
70
  declare function createEffect(fn: Effect): () => void;
61
71
  declare function createRenderEffect(fn: Effect): () => void;
62
72
 
63
- declare function batch<T>(fn: () => T): T;
64
- declare function untrack<T>(fn: () => T): T;
65
-
66
73
  /**
67
74
  * Fict Reactive DOM Binding System
68
75
  *
@@ -76,6 +83,8 @@ declare function untrack<T>(fn: () => T): T;
76
83
  * - The compiler transforms JSX expressions to use these primitives
77
84
  */
78
85
 
86
+ /** A reactive value that can be either static or a getter function */
87
+ type MaybeReactive<T> = T | (() => T);
79
88
  /** Internal type for createElement function reference */
80
89
  type CreateElementFn = (node: FictNode) => Node;
81
90
  /** Handle returned by conditional/list bindings for cleanup */
@@ -212,6 +221,24 @@ type KeyFn<T> = (item: T, index: number) => string | number;
212
221
  */
213
222
  declare function createList<T>(items: () => T[], renderItem: (item: T, index: number) => FictNode, createElementFn: CreateElementFn, getKey?: KeyFn<T>): BindingHandle;
214
223
 
224
+ interface ReactiveScope {
225
+ run<T>(fn: () => T): T;
226
+ stop(): void;
227
+ }
228
+ /**
229
+ * Create an explicit reactive scope that can contain effects/memos and be stopped manually.
230
+ * The scope registers with the current root for cleanup.
231
+ */
232
+ declare function createScope(): ReactiveScope;
233
+ /**
234
+ * Run a block of reactive code inside a managed scope that follows a boolean flag.
235
+ * When the flag turns false, the scope is disposed and all contained effects/memos are cleaned up.
236
+ */
237
+ declare function runInScope(flag: MaybeReactive<boolean>, fn: () => void): void;
238
+
239
+ declare function batch<T>(fn: () => T): T;
240
+ declare function untrack<T>(fn: () => T): T;
241
+
215
242
  /**
216
243
  * Fict DOM Rendering System
217
244
  *
@@ -472,4 +499,4 @@ type PropGetter<T> = (() => T) & {
472
499
  */
473
500
  declare function useProp<T>(getter: () => T): PropGetter<T>;
474
501
 
475
- export { $state, Fragment, __fictPopContext, __fictProp, __fictPropsRest, __fictPushContext, __fictRender, __fictResetContext, __fictUseContext, __fictUseEffect, __fictUseMemo, __fictUseSignal, batch, bindAttribute, bindClass, bindEvent, bindProperty, bindRef, bindStyle, bindText, clearDelegatedEvents, createConditional, createEffect, createElement, createKeyedBlock, createKeyedList, createKeyedListContainer, createList, createMemo, createPropsProxy, createRenderEffect, createSelector, signal as createSignal, delegateEvents, destroyMarkerBlock, getFirstNodeAfter, insert, insertNodesBefore, mergeProps, moveMarkerBlock, onDestroy, __fictProp as prop, removeNodes, render, template, toNodeArray, untrack, useProp };
502
+ export { $state, Fragment, type ReactiveScope, __fictPopContext, __fictProp, __fictPropsRest, __fictPushContext, __fictRender, __fictResetContext, __fictUseContext, __fictUseEffect, __fictUseMemo, __fictUseSignal, batch, bindAttribute, bindClass, bindEvent, bindProperty, bindRef, bindStyle, bindText, clearDelegatedEvents, createConditional, createEffect, createElement, createKeyedBlock, createKeyedList, createKeyedListContainer, createList, createMemo, createPropsProxy, createRenderEffect, createScope, createSelector, signal as createSignal, delegateEvents, destroyMarkerBlock, effectScope, getFirstNodeAfter, insert, insertNodesBefore, mergeProps, moveMarkerBlock, onDestroy, __fictProp as prop, removeNodes, render, runInScope, template, toNodeArray, untrack, useProp };
package/dist/slim.js CHANGED
@@ -99,6 +99,7 @@ function reportCycle(reason, detail = void 0) {
99
99
 
100
100
  // src/lifecycle.ts
101
101
  var currentRoot;
102
+ var currentEffectCleanups;
102
103
  var globalErrorHandlers = /* @__PURE__ */ new WeakMap();
103
104
  var globalSuspenseHandlers = /* @__PURE__ */ new WeakMap();
104
105
  function createRootContext(parent = currentRoot) {
@@ -128,6 +129,9 @@ function onDestroy(fn) {
128
129
  }
129
130
  runLifecycle(fn);
130
131
  }
132
+ function onCleanup(fn) {
133
+ registerEffectCleanup(fn);
134
+ }
131
135
  function flushOnMount(root) {
132
136
  const cbs = root.onMountCallbacks;
133
137
  if (!cbs || cbs.length === 0) return;
@@ -166,10 +170,35 @@ function destroyRoot(root) {
166
170
  globalSuspenseHandlers.delete(root);
167
171
  }
168
172
  }
173
+ function createRoot(fn) {
174
+ const root = createRootContext();
175
+ const prev = pushRoot(root);
176
+ let value;
177
+ try {
178
+ value = fn();
179
+ } finally {
180
+ popRoot(prev);
181
+ }
182
+ flushOnMount(root);
183
+ return {
184
+ dispose: () => destroyRoot(root),
185
+ value
186
+ };
187
+ }
169
188
  function withEffectCleanups(bucket, fn) {
189
+ const prev = currentEffectCleanups;
190
+ currentEffectCleanups = bucket;
170
191
  try {
171
192
  return fn();
172
193
  } finally {
194
+ currentEffectCleanups = prev;
195
+ }
196
+ }
197
+ function registerEffectCleanup(fn) {
198
+ if (currentEffectCleanups) {
199
+ currentEffectCleanups.push(fn);
200
+ } else {
201
+ registerRootCleanup(fn);
173
202
  }
174
203
  }
175
204
  function runCleanupList(list) {
@@ -718,6 +747,21 @@ function effect(fn) {
718
747
  function effectOper() {
719
748
  disposeNode(this);
720
749
  }
750
+ function effectScope(fn) {
751
+ const e = { deps: void 0, depsTail: void 0, subs: void 0, subsTail: void 0, flags: 0 };
752
+ const prevSub = activeSub;
753
+ if (prevSub !== void 0) link(e, prevSub, 0);
754
+ activeSub = e;
755
+ try {
756
+ fn();
757
+ } finally {
758
+ activeSub = prevSub;
759
+ }
760
+ return effectScopeOper.bind(e);
761
+ }
762
+ function effectScopeOper() {
763
+ disposeNode(this);
764
+ }
721
765
  function batch(fn) {
722
766
  ++batchDepth;
723
767
  try {
@@ -861,14 +905,6 @@ function createRenderEffect(fn) {
861
905
  return teardown;
862
906
  }
863
907
 
864
- // src/scheduler.ts
865
- function batch2(fn) {
866
- return batch(fn);
867
- }
868
- function untrack2(fn) {
869
- return untrack(fn);
870
- }
871
-
872
908
  // src/constants.ts
873
909
  var booleans = [
874
910
  "allowfullscreen",
@@ -1311,6 +1347,27 @@ function removeNodes(nodes) {
1311
1347
  function isReactive(value) {
1312
1348
  return typeof value === "function" && value.length === 0;
1313
1349
  }
1350
+ function callEventHandler(handler, event, node, data) {
1351
+ if (!handler) return;
1352
+ const context = node ?? event.currentTarget ?? void 0;
1353
+ const invoke = (fn) => {
1354
+ if (typeof fn === "function") {
1355
+ const result = data === void 0 ? fn.call(context, event) : fn.call(context, data, event);
1356
+ if (typeof result === "function" && result !== fn) {
1357
+ if (data === void 0) {
1358
+ result.call(context, event);
1359
+ } else {
1360
+ result.call(context, data, event);
1361
+ }
1362
+ } else if (result && typeof result.handleEvent === "function") {
1363
+ result.handleEvent.call(result, event);
1364
+ }
1365
+ } else if (fn && typeof fn.handleEvent === "function") {
1366
+ fn.handleEvent.call(fn, event);
1367
+ }
1368
+ };
1369
+ invoke(handler);
1370
+ }
1314
1371
  var PRIMITIVE_PROXY = Symbol("fict:primitive-proxy");
1315
1372
  var PRIMITIVE_PROXY_RAW_VALUE = Symbol("fict:primitive-proxy:raw-value");
1316
1373
  function createValueProxy(read) {
@@ -1797,14 +1854,10 @@ function globalEventHandler(e) {
1797
1854
  const hasData = rawData !== void 0;
1798
1855
  const resolvedNodeData = hasData ? resolveData(rawData) : void 0;
1799
1856
  if (typeof handler === "function") {
1800
- if (hasData) {
1801
- handler.call(node, resolvedNodeData, e);
1802
- } else {
1803
- handler.call(node, e);
1804
- }
1857
+ callEventHandler(handler, e, node, hasData ? resolvedNodeData : void 0);
1805
1858
  } else if (Array.isArray(handler)) {
1806
1859
  const tupleData = resolveData(handler[1]);
1807
- handler[0].call(node, tupleData, e);
1860
+ callEventHandler(handler[0], e, node, tupleData);
1808
1861
  }
1809
1862
  if (e.cancelBubble) return false;
1810
1863
  }
@@ -1852,23 +1905,15 @@ function bindEvent(el, eventName, handler, options2) {
1852
1905
  if (DelegatedEvents.has(eventName) && !options2) {
1853
1906
  const key = `$$${eventName}`;
1854
1907
  delegateEvents([eventName]);
1855
- const createWrapped = (resolve) => {
1856
- const wrapped2 = function(...args) {
1857
- try {
1858
- const fn = resolve();
1859
- if (typeof fn === "function") {
1860
- return fn.apply(this, args);
1861
- } else if (fn && typeof fn.handleEvent === "function") {
1862
- return fn.handleEvent.apply(fn, args);
1863
- }
1864
- } catch (err) {
1865
- handleError(err, { source: "event", eventName }, rootRef);
1866
- }
1867
- };
1868
- return wrapped2;
1869
- };
1870
1908
  const resolveHandler = isReactive(handler) ? handler : () => handler;
1871
- el[key] = createWrapped(resolveHandler);
1909
+ el[key] = function(...args) {
1910
+ try {
1911
+ const fn = resolveHandler();
1912
+ callEventHandler(fn, args[0], el);
1913
+ } catch (err) {
1914
+ handleError(err, { source: "event", eventName }, rootRef);
1915
+ }
1916
+ };
1872
1917
  return () => {
1873
1918
  el[key] = void 0;
1874
1919
  };
@@ -1877,13 +1922,7 @@ function bindEvent(el, eventName, handler, options2) {
1877
1922
  const wrapped = (event) => {
1878
1923
  try {
1879
1924
  const resolved = getHandler();
1880
- if (typeof resolved === "function") {
1881
- ;
1882
- resolved(event);
1883
- } else if (resolved && typeof resolved.handleEvent === "function") {
1884
- ;
1885
- resolved.handleEvent(event);
1886
- }
1925
+ callEventHandler(resolved, event, el);
1887
1926
  } catch (err) {
1888
1927
  if (handleError(err, { source: "event", eventName }, rootRef)) {
1889
1928
  return;
@@ -2342,6 +2381,46 @@ function bumpBlockVersion(block) {
2342
2381
  block.version(block.version() + 1);
2343
2382
  }
2344
2383
 
2384
+ // src/scope.ts
2385
+ function createScope() {
2386
+ let dispose = null;
2387
+ const stop = () => {
2388
+ if (dispose) {
2389
+ dispose();
2390
+ dispose = null;
2391
+ }
2392
+ };
2393
+ const run = (fn) => {
2394
+ stop();
2395
+ const { dispose: rootDispose, value } = createRoot(fn);
2396
+ dispose = rootDispose;
2397
+ return value;
2398
+ };
2399
+ registerRootCleanup(stop);
2400
+ return { run, stop };
2401
+ }
2402
+ function runInScope(flag, fn) {
2403
+ const scope = createScope();
2404
+ const evaluate = () => isReactive(flag) ? flag() : !!flag;
2405
+ createEffect(() => {
2406
+ const enabled = evaluate();
2407
+ if (enabled) {
2408
+ scope.run(fn);
2409
+ } else {
2410
+ scope.stop();
2411
+ }
2412
+ });
2413
+ onCleanup(scope.stop);
2414
+ }
2415
+
2416
+ // src/scheduler.ts
2417
+ function batch2(fn) {
2418
+ return batch(fn);
2419
+ }
2420
+ function untrack2(fn) {
2421
+ return untrack(fn);
2422
+ }
2423
+
2345
2424
  // src/hooks.ts
2346
2425
  var ctxStack = [];
2347
2426
  function __fictUseContext() {
@@ -3330,6 +3409,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
3330
3409
  };
3331
3410
  }
3332
3411
 
3333
- export { $state, Fragment, __fictPopContext, __fictProp, __fictPropsRest, __fictPushContext, __fictRender, __fictResetContext, __fictUseContext, __fictUseEffect, __fictUseMemo, __fictUseSignal, batch2 as batch, bindAttribute, bindClass, bindEvent, bindProperty, bindRef, bindStyle, bindText, clearDelegatedEvents, createConditional, createEffect, createElement, createKeyedBlock, createKeyedList, createKeyedListContainer, createList, createMemo, createPropsProxy, createRenderEffect, createSelector, signal as createSignal, delegateEvents, destroyMarkerBlock, getFirstNodeAfter, insert, insertNodesBefore, mergeProps, moveMarkerBlock, onDestroy, __fictProp as prop, removeNodes, render, template, toNodeArray, untrack2 as untrack, useProp };
3412
+ export { $state, Fragment, __fictPopContext, __fictProp, __fictPropsRest, __fictPushContext, __fictRender, __fictResetContext, __fictUseContext, __fictUseEffect, __fictUseMemo, __fictUseSignal, batch2 as batch, bindAttribute, bindClass, bindEvent, bindProperty, bindRef, bindStyle, bindText, clearDelegatedEvents, createConditional, createEffect, createElement, createKeyedBlock, createKeyedList, createKeyedListContainer, createList, createMemo, createPropsProxy, createRenderEffect, createScope, createSelector, signal as createSignal, delegateEvents, destroyMarkerBlock, effectScope, getFirstNodeAfter, insert, insertNodesBefore, mergeProps, moveMarkerBlock, onDestroy, __fictProp as prop, removeNodes, render, runInScope, template, toNodeArray, untrack2 as untrack, useProp };
3334
3413
  //# sourceMappingURL=slim.js.map
3335
3414
  //# sourceMappingURL=slim.js.map