signalium 1.1.0 → 1.2.0

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.
@@ -1,12 +1,12 @@
1
1
 
2
- > signalium@1.1.0 build
2
+ > signalium@1.2.0 build
3
3
  > npm run build:esm && npm run build:cjs
4
4
 
5
5
 
6
- > signalium@1.1.0 build:esm
6
+ > signalium@1.2.0 build:esm
7
7
  > tsc
8
8
 
9
9
 
10
- > signalium@1.1.0 build:cjs
10
+ > signalium@1.2.0 build:cjs
11
11
  > tsc --module commonjs --outDir dist/cjs --moduleResolution node
12
12
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # signalium
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a56cc6f: Add runReactiveSafe for running reactive functions safely in React apps
8
+
9
+ ## 1.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - dd4a7f9: Fix root context inheritance
14
+
3
15
  ## 1.1.0
4
16
 
5
17
  ### Minor Changes
@@ -2,4 +2,5 @@ export { ContextProvider } from './provider.js';
2
2
  export { useScope } from './context.js';
3
3
  export { setupReact } from './setup.js';
4
4
  export { useStateSignal } from './state.js';
5
+ export { runReactiveSafe } from './rendering.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useStateSignal = exports.setupReact = exports.useScope = exports.ContextProvider = void 0;
3
+ exports.runReactiveSafe = exports.useStateSignal = exports.setupReact = exports.useScope = exports.ContextProvider = void 0;
4
4
  var provider_js_1 = require("./provider.js");
5
5
  Object.defineProperty(exports, "ContextProvider", { enumerable: true, get: function () { return provider_js_1.ContextProvider; } });
6
6
  var context_js_1 = require("./context.js");
@@ -9,4 +9,6 @@ var setup_js_1 = require("./setup.js");
9
9
  Object.defineProperty(exports, "setupReact", { enumerable: true, get: function () { return setup_js_1.setupReact; } });
10
10
  var state_js_1 = require("./state.js");
11
11
  Object.defineProperty(exports, "useStateSignal", { enumerable: true, get: function () { return state_js_1.useStateSignal; } });
12
+ var rendering_js_1 = require("./rendering.js");
13
+ Object.defineProperty(exports, "runReactiveSafe", { enumerable: true, get: function () { return rendering_js_1.runReactiveSafe; } });
12
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":";;;AAAA,6CAAgD;AAAvC,8GAAA,eAAe,OAAA;AACxB,2CAAwC;AAA/B,sGAAA,QAAQ,OAAA;AACjB,uCAAwC;AAA/B,sGAAA,UAAU,OAAA;AACnB,uCAA4C;AAAnC,0GAAA,cAAc,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":";;;AAAA,6CAAgD;AAAvC,8GAAA,eAAe,OAAA;AACxB,2CAAwC;AAA/B,sGAAA,QAAQ,OAAA;AACjB,uCAAwC;AAA/B,sGAAA,UAAU,OAAA;AACnB,uCAA4C;AAAnC,0GAAA,cAAc,OAAA;AAEvB,+CAAiD;AAAxC,+GAAA,eAAe,OAAA"}
@@ -1,8 +1,7 @@
1
1
  import { ContextPair } from '../internals/contexts.js';
2
- export declare function ContextProvider<C extends unknown[]>({ children, contexts, inherit, root, }: {
2
+ export declare function ContextProvider<C extends unknown[]>({ children, contexts, inherit, }: {
3
3
  children: React.ReactNode;
4
- contexts: [...ContextPair<C>];
4
+ contexts?: [...ContextPair<C>] | [];
5
5
  inherit?: boolean;
6
- root?: boolean;
7
6
  }): import("react/jsx-runtime").JSX.Element;
8
7
  //# sourceMappingURL=provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAe,WAAW,EAAe,MAAM,0BAA0B,CAAC;AAEjF,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,EACnD,QAAQ,EACR,QAAQ,EACR,OAAc,EACd,IAAY,GACb,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,2CAKA"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAe,WAAW,EAA2B,MAAM,0BAA0B,CAAC;AAE7F,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,EACnD,QAAQ,EACR,QAAa,EACb,OAAc,GACf,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,2CAKA"}
@@ -5,8 +5,8 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const context_js_1 = require("./context.js");
7
7
  const contexts_js_1 = require("../internals/contexts.js");
8
- function ContextProvider({ children, contexts, inherit = true, root = false, }) {
9
- const parentScope = (0, react_1.useContext)(context_js_1.ScopeContext);
8
+ function ContextProvider({ children, contexts = [], inherit = true, }) {
9
+ const parentScope = (0, react_1.useContext)(context_js_1.ScopeContext) ?? contexts_js_1.ROOT_SCOPE;
10
10
  const scope = new contexts_js_1.SignalScope(contexts, inherit ? parentScope : undefined);
11
11
  return (0, jsx_runtime_1.jsx)(context_js_1.ScopeContext.Provider, { value: scope, children: children });
12
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":";;AAIA,0CAeC;;AAnBD,iCAAmC;AACnC,6CAA4C;AAC5C,0DAAiF;AAEjF,SAAgB,eAAe,CAAsB,EACnD,QAAQ,EACR,QAAQ,EACR,OAAO,GAAG,IAAI,EACd,IAAI,GAAG,KAAK,GAMb;IACC,MAAM,WAAW,GAAG,IAAA,kBAAU,EAAC,yBAAY,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,yBAAW,CAAC,QAA6C,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEhH,OAAO,uBAAC,yBAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAyB,CAAC;AACjF,CAAC"}
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":";;AAIA,0CAaC;;AAjBD,iCAAmC;AACnC,6CAA4C;AAC5C,0DAA6F;AAE7F,SAAgB,eAAe,CAAsB,EACnD,QAAQ,EACR,QAAQ,GAAG,EAAE,EACb,OAAO,GAAG,IAAI,GAKf;IACC,MAAM,WAAW,GAAG,IAAA,kBAAU,EAAC,yBAAY,CAAC,IAAI,wBAAU,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAI,yBAAW,CAAC,QAA6C,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEhH,OAAO,uBAAC,yBAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAyB,CAAC;AACjF,CAAC"}
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Reactive functions can be called anywhere, but React Hooks cannot. When calling reactive functions
3
+ * in code that _may or may not_ be used while rendering, we need to use this function to wrap the
4
+ * call. This will ensure that we will not be in a rendering context when the reactive function is called.
5
+ */
6
+ export declare const runReactiveSafe: <T, Args extends unknown[]>(fn: (...args: Args) => T, ...args: Args) => T;
1
7
  export declare function isRendering(): boolean;
2
8
  //# sourceMappingURL=rendering.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":"AAcA,wBAAgB,WAAW,YAU1B"}
1
+ {"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,IAAI,SAAS,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,WAAW,IAAI,KAAG,CAQpG,CAAC;AAEF,wBAAgB,WAAW,YAW1B"}
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runReactiveSafe = void 0;
6
7
  exports.isRendering = isRendering;
7
8
  const react_1 = __importDefault(require("react"));
8
9
  // This is a private React internal that we need to access to check if we are rendering.
@@ -14,9 +15,26 @@ const REACT_INTERNALS = react_1.default.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WIL
14
15
  react_1.default.__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
15
16
  const IS_REACT_18 = !!REACT_INTERNALS.ReactCurrentDispatcher;
16
17
  const ReactCurrentDispatcher = REACT_INTERNALS.ReactCurrentDispatcher || REACT_INTERNALS;
18
+ let RENDERING_SAFE_MODE_COUNT = 0;
19
+ /**
20
+ * Reactive functions can be called anywhere, but React Hooks cannot. When calling reactive functions
21
+ * in code that _may or may not_ be used while rendering, we need to use this function to wrap the
22
+ * call. This will ensure that we will not be in a rendering context when the reactive function is called.
23
+ */
24
+ const runReactiveSafe = (fn, ...args) => {
25
+ RENDERING_SAFE_MODE_COUNT++;
26
+ try {
27
+ return fn(...args);
28
+ }
29
+ finally {
30
+ RENDERING_SAFE_MODE_COUNT--;
31
+ }
32
+ };
33
+ exports.runReactiveSafe = runReactiveSafe;
17
34
  function isRendering() {
18
35
  const dispatcher = IS_REACT_18 ? ReactCurrentDispatcher.current : ReactCurrentDispatcher.H;
19
- return (!!dispatcher &&
36
+ return (RENDERING_SAFE_MODE_COUNT === 0 &&
37
+ !!dispatcher &&
20
38
  // dispatcher can be in a state where it's defined, but all hooks are invalid to call.
21
39
  // Only way we can tell is that if they are invalid, they will all be equal to each other
22
40
  // (e.g. because it's the function that throws an error)
@@ -1 +1 @@
1
- {"version":3,"file":"rendering.js","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":";;;;;AAcA,kCAUC;AAxBD,kDAA0B;AAE1B,wFAAwF;AACxF,oFAAoF;AACpF,mFAAmF;AACnF,0DAA0D;AAC1D,MAAM,eAAe,GAClB,eAAa,CAAC,kDAAkD;IAChE,eAAa,CAAC,+DAA+D;IAC7E,eAAa,CAAC,+DAA+D,CAAC;AAEjF,MAAM,WAAW,GAAG,CAAC,CAAC,eAAe,CAAC,sBAAsB,CAAC;AAC7D,MAAM,sBAAsB,GAAG,eAAe,CAAC,sBAAsB,IAAI,eAAe,CAAC;AAEzF,SAAgB,WAAW;IACzB,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAE3F,OAAO,CACL,CAAC,CAAC,UAAU;QACZ,sFAAsF;QACtF,yFAAyF;QACzF,wDAAwD;QACxD,UAAU,CAAC,QAAQ,KAAK,UAAU,CAAC,SAAS,CAC7C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"rendering.js","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":";;;;;;AA+BA,kCAWC;AA1CD,kDAA0B;AAE1B,wFAAwF;AACxF,oFAAoF;AACpF,mFAAmF;AACnF,0DAA0D;AAC1D,MAAM,eAAe,GAClB,eAAa,CAAC,kDAAkD;IAChE,eAAa,CAAC,+DAA+D;IAC7E,eAAa,CAAC,+DAA+D,CAAC;AAEjF,MAAM,WAAW,GAAG,CAAC,CAAC,eAAe,CAAC,sBAAsB,CAAC;AAC7D,MAAM,sBAAsB,GAAG,eAAe,CAAC,sBAAsB,IAAI,eAAe,CAAC;AAEzF,IAAI,yBAAyB,GAAG,CAAC,CAAC;AAElC;;;;GAIG;AACI,MAAM,eAAe,GAAG,CAA4B,EAAwB,EAAE,GAAG,IAAU,EAAK,EAAE;IACvG,yBAAyB,EAAE,CAAC;IAE5B,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,yBAAyB,EAAE,CAAC;IAC9B,CAAC;AACH,CAAC,CAAC;AARW,QAAA,eAAe,mBAQ1B;AAEF,SAAgB,WAAW;IACzB,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAE3F,OAAO,CACL,yBAAyB,KAAK,CAAC;QAC/B,CAAC,CAAC,UAAU;QACZ,sFAAsF;QACtF,yFAAyF;QACzF,wDAAwD;QACxD,UAAU,CAAC,QAAQ,KAAK,UAAU,CAAC,SAAS,CAC7C,CAAC;AACJ,CAAC"}
@@ -2,4 +2,5 @@ export { ContextProvider } from './provider.js';
2
2
  export { useScope } from './context.js';
3
3
  export { setupReact } from './setup.js';
4
4
  export { useStateSignal } from './state.js';
5
+ export { runReactiveSafe } from './rendering.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -2,4 +2,5 @@ export { ContextProvider } from './provider.js';
2
2
  export { useScope } from './context.js';
3
3
  export { setupReact } from './setup.js';
4
4
  export { useStateSignal } from './state.js';
5
+ export { runReactiveSafe } from './rendering.js';
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,8 +1,7 @@
1
1
  import { ContextPair } from '../internals/contexts.js';
2
- export declare function ContextProvider<C extends unknown[]>({ children, contexts, inherit, root, }: {
2
+ export declare function ContextProvider<C extends unknown[]>({ children, contexts, inherit, }: {
3
3
  children: React.ReactNode;
4
- contexts: [...ContextPair<C>];
4
+ contexts?: [...ContextPair<C>] | [];
5
5
  inherit?: boolean;
6
- root?: boolean;
7
6
  }): import("react/jsx-runtime").JSX.Element;
8
7
  //# sourceMappingURL=provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAe,WAAW,EAAe,MAAM,0BAA0B,CAAC;AAEjF,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,EACnD,QAAQ,EACR,QAAQ,EACR,OAAc,EACd,IAAY,GACb,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,2CAKA"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAe,WAAW,EAA2B,MAAM,0BAA0B,CAAC;AAE7F,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,EACnD,QAAQ,EACR,QAAa,EACb,OAAc,GACf,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,2CAKA"}
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useContext } from 'react';
3
3
  import { ScopeContext } from './context.js';
4
- import { SignalScope } from '../internals/contexts.js';
5
- export function ContextProvider({ children, contexts, inherit = true, root = false, }) {
6
- const parentScope = useContext(ScopeContext);
4
+ import { ROOT_SCOPE, SignalScope } from '../internals/contexts.js';
5
+ export function ContextProvider({ children, contexts = [], inherit = true, }) {
6
+ const parentScope = useContext(ScopeContext) ?? ROOT_SCOPE;
7
7
  const scope = new SignalScope(contexts, inherit ? parentScope : undefined);
8
8
  return _jsx(ScopeContext.Provider, { value: scope, children: children });
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAA4B,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEjF,MAAM,UAAU,eAAe,CAAsB,EACnD,QAAQ,EACR,QAAQ,EACR,OAAO,GAAG,IAAI,EACd,IAAI,GAAG,KAAK,GAMb;IACC,MAAM,WAAW,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,QAA6C,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEhH,OAAO,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAyB,CAAC;AACjF,CAAC"}
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/react/provider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAA4B,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE7F,MAAM,UAAU,eAAe,CAAsB,EACnD,QAAQ,EACR,QAAQ,GAAG,EAAE,EACb,OAAO,GAAG,IAAI,GAKf;IACC,MAAM,WAAW,GAAG,UAAU,CAAC,YAAY,CAAC,IAAI,UAAU,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,QAA6C,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEhH,OAAO,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAyB,CAAC;AACjF,CAAC"}
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Reactive functions can be called anywhere, but React Hooks cannot. When calling reactive functions
3
+ * in code that _may or may not_ be used while rendering, we need to use this function to wrap the
4
+ * call. This will ensure that we will not be in a rendering context when the reactive function is called.
5
+ */
6
+ export declare const runReactiveSafe: <T, Args extends unknown[]>(fn: (...args: Args) => T, ...args: Args) => T;
1
7
  export declare function isRendering(): boolean;
2
8
  //# sourceMappingURL=rendering.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":"AAcA,wBAAgB,WAAW,YAU1B"}
1
+ {"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,IAAI,SAAS,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,WAAW,IAAI,KAAG,CAQpG,CAAC;AAEF,wBAAgB,WAAW,YAW1B"}
@@ -8,9 +8,25 @@ const REACT_INTERNALS = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
8
8
  React.__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
9
9
  const IS_REACT_18 = !!REACT_INTERNALS.ReactCurrentDispatcher;
10
10
  const ReactCurrentDispatcher = REACT_INTERNALS.ReactCurrentDispatcher || REACT_INTERNALS;
11
+ let RENDERING_SAFE_MODE_COUNT = 0;
12
+ /**
13
+ * Reactive functions can be called anywhere, but React Hooks cannot. When calling reactive functions
14
+ * in code that _may or may not_ be used while rendering, we need to use this function to wrap the
15
+ * call. This will ensure that we will not be in a rendering context when the reactive function is called.
16
+ */
17
+ export const runReactiveSafe = (fn, ...args) => {
18
+ RENDERING_SAFE_MODE_COUNT++;
19
+ try {
20
+ return fn(...args);
21
+ }
22
+ finally {
23
+ RENDERING_SAFE_MODE_COUNT--;
24
+ }
25
+ };
11
26
  export function isRendering() {
12
27
  const dispatcher = IS_REACT_18 ? ReactCurrentDispatcher.current : ReactCurrentDispatcher.H;
13
- return (!!dispatcher &&
28
+ return (RENDERING_SAFE_MODE_COUNT === 0 &&
29
+ !!dispatcher &&
14
30
  // dispatcher can be in a state where it's defined, but all hooks are invalid to call.
15
31
  // Only way we can tell is that if they are invalid, they will all be equal to each other
16
32
  // (e.g. because it's the function that throws an error)
@@ -1 +1 @@
1
- {"version":3,"file":"rendering.js","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,wFAAwF;AACxF,oFAAoF;AACpF,mFAAmF;AACnF,0DAA0D;AAC1D,MAAM,eAAe,GAClB,KAAa,CAAC,kDAAkD;IAChE,KAAa,CAAC,+DAA+D;IAC7E,KAAa,CAAC,+DAA+D,CAAC;AAEjF,MAAM,WAAW,GAAG,CAAC,CAAC,eAAe,CAAC,sBAAsB,CAAC;AAC7D,MAAM,sBAAsB,GAAG,eAAe,CAAC,sBAAsB,IAAI,eAAe,CAAC;AAEzF,MAAM,UAAU,WAAW;IACzB,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAE3F,OAAO,CACL,CAAC,CAAC,UAAU;QACZ,sFAAsF;QACtF,yFAAyF;QACzF,wDAAwD;QACxD,UAAU,CAAC,QAAQ,KAAK,UAAU,CAAC,SAAS,CAC7C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"rendering.js","sourceRoot":"","sources":["../../../src/react/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,wFAAwF;AACxF,oFAAoF;AACpF,mFAAmF;AACnF,0DAA0D;AAC1D,MAAM,eAAe,GAClB,KAAa,CAAC,kDAAkD;IAChE,KAAa,CAAC,+DAA+D;IAC7E,KAAa,CAAC,+DAA+D,CAAC;AAEjF,MAAM,WAAW,GAAG,CAAC,CAAC,eAAe,CAAC,sBAAsB,CAAC;AAC7D,MAAM,sBAAsB,GAAG,eAAe,CAAC,sBAAsB,IAAI,eAAe,CAAC;AAEzF,IAAI,yBAAyB,GAAG,CAAC,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAA4B,EAAwB,EAAE,GAAG,IAAU,EAAK,EAAE;IACvG,yBAAyB,EAAE,CAAC;IAE5B,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,yBAAyB,EAAE,CAAC;IAC9B,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,UAAU,WAAW;IACzB,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAE3F,OAAO,CACL,yBAAyB,KAAK,CAAC;QAC/B,CAAC,CAAC,UAAU;QACZ,sFAAsF;QACtF,yFAAyF;QACzF,wDAAwD;QACxD,UAAU,CAAC,QAAQ,KAAK,UAAU,CAAC,SAAS,CAC7C,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalium",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "repository": "https://github.com/pzuraq/signalium",
6
6
  "description": "Chain-reactivity at critical mass",
@@ -65,6 +65,54 @@ describe('contexts', () => {
65
65
  expect(derived()).toBe('Hi, Everyone');
66
66
  });
67
67
 
68
+ test('withContexts inherits from root scope', () => {
69
+ const defaultValue1 = state('default1');
70
+ const defaultValue2 = state('default2');
71
+ const ctx1 = createContext(defaultValue1);
72
+ const ctx2 = createContext(defaultValue2);
73
+ const rootOverride1 = state('root1');
74
+ const rootOverride2 = state('root2');
75
+
76
+ // Set root contexts
77
+ setRootContexts([
78
+ [ctx1, rootOverride1],
79
+ [ctx2, rootOverride2],
80
+ ]);
81
+
82
+ // Create a reactive function that uses both contexts
83
+ const derived = reactive(() => `${useContext(ctx1).get()}-${useContext(ctx2).get()}`);
84
+
85
+ // Should inherit from root scope when no local overrides
86
+ const result1 = withContexts([], () => derived());
87
+ expect(result1).toBe('root1-root2');
88
+
89
+ // Should inherit from root scope for unoverridden contexts
90
+ const localOverride1 = state('local1');
91
+ const result2 = withContexts([[ctx1, localOverride1]], () => derived());
92
+ expect(result2).toBe('local1-root2');
93
+
94
+ // Should use local overrides when provided
95
+ const localOverride2 = state('local2');
96
+ const result3 = withContexts(
97
+ [
98
+ [ctx1, localOverride1],
99
+ [ctx2, localOverride2],
100
+ ],
101
+ () => derived(),
102
+ );
103
+ expect(result3).toBe('local1-local2');
104
+
105
+ // Changes to root contexts should be reflected in inherited contexts
106
+ rootOverride1.set('updated-root1');
107
+ rootOverride2.set('updated-root2');
108
+
109
+ const result4 = withContexts([], () => derived());
110
+ expect(result4).toBe('updated-root1-updated-root2');
111
+
112
+ const result5 = withContexts([[ctx1, localOverride1]], () => derived());
113
+ expect(result5).toBe('local1-updated-root2');
114
+ });
115
+
68
116
  test('async computed maintains context ownership across await boundaries', async () => {
69
117
  const ctx = createContext('default');
70
118
 
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from 'vitest';
2
2
  import { render } from 'vitest-browser-react';
3
- import { state, reactive, createContext, useContext } from 'signalium';
3
+ import { state, reactive, createContext, useContext, setRootContexts } from '../../index.js';
4
4
  import { ContextProvider, setupReact, useScope } from '../index.js';
5
5
  import React, { useState } from 'react';
6
6
 
@@ -49,6 +49,69 @@ describe('React > contexts', () => {
49
49
  await expect.element(getByText('Hey, World')).toBeInTheDocument();
50
50
  });
51
51
 
52
+ test('provider inherits from root scope', async () => {
53
+ const defaultValue1 = state('default1');
54
+ const defaultValue2 = state('default2');
55
+ const ctx1 = createContext(defaultValue1);
56
+ const ctx2 = createContext(defaultValue2);
57
+ const rootOverride1 = state('root1');
58
+ const rootOverride2 = state('root2');
59
+
60
+ // Set root contexts
61
+ setRootContexts([
62
+ [ctx1, rootOverride1],
63
+ [ctx2, rootOverride2],
64
+ ]);
65
+
66
+ // Component that uses both contexts
67
+ function Component({ testId }: { testId: string }): React.ReactNode {
68
+ const value1 = useContext(ctx1);
69
+ const value2 = useContext(ctx2);
70
+ const derived = reactive(() => `${value1.get()}-${value2.get()}`);
71
+ return <div data-testid={testId}>{derived()}</div>;
72
+ }
73
+ const localOverride1 = state('local1');
74
+ const localOverride2 = state('local2');
75
+
76
+ // Should inherit from root scope when no local overrides
77
+ const { getByTestId } = render(
78
+ <>
79
+ <ContextProvider contexts={[]}>
80
+ <Component testId="result" />
81
+ </ContextProvider>
82
+ <ContextProvider contexts={[[ctx1, localOverride1]]}>
83
+ <Component testId="result2" />
84
+ </ContextProvider>
85
+ <ContextProvider
86
+ contexts={[
87
+ [ctx1, localOverride1],
88
+ [ctx2, localOverride2],
89
+ ]}
90
+ >
91
+ <Component testId="result3" />
92
+ </ContextProvider>
93
+ </>,
94
+ );
95
+
96
+ await expect.element(getByTestId('result')).toHaveTextContent('root1-root2');
97
+
98
+ // Should inherit from root scope for unoverridden contexts
99
+ await expect.element(getByTestId('result2')).toHaveTextContent('local1-root2');
100
+
101
+ // Should use local overrides when provided
102
+ await expect.element(getByTestId('result3')).toHaveTextContent('local1-local2');
103
+
104
+ // Changes to root contexts should be reflected in inherited contexts
105
+ rootOverride1.set('updated-root1');
106
+ rootOverride2.set('updated-root2');
107
+
108
+ await expect.element(getByTestId('result')).toHaveTextContent('updated-root1-updated-root2');
109
+
110
+ // Local overrides should remain unaffected by root context changes
111
+ await expect.element(getByTestId('result2')).toHaveTextContent('local1-updated-root2');
112
+ await expect.element(getByTestId('result3')).toHaveTextContent('local1-local2');
113
+ });
114
+
52
115
  test('useContext works inside computed value passed via context provider', async () => {
53
116
  const value = state('Hello');
54
117
  const override = state('Hey');
@@ -2,3 +2,5 @@ export { ContextProvider } from './provider.js';
2
2
  export { useScope } from './context.js';
3
3
  export { setupReact } from './setup.js';
4
4
  export { useStateSignal } from './state.js';
5
+
6
+ export { runReactiveSafe } from './rendering.js';
@@ -1,19 +1,17 @@
1
1
  import { useContext } from 'react';
2
2
  import { ScopeContext } from './context.js';
3
- import { ContextImpl, ContextPair, SignalScope } from '../internals/contexts.js';
3
+ import { ContextImpl, ContextPair, ROOT_SCOPE, SignalScope } from '../internals/contexts.js';
4
4
 
5
5
  export function ContextProvider<C extends unknown[]>({
6
6
  children,
7
- contexts,
7
+ contexts = [],
8
8
  inherit = true,
9
- root = false,
10
9
  }: {
11
10
  children: React.ReactNode;
12
- contexts: [...ContextPair<C>];
11
+ contexts?: [...ContextPair<C>] | [];
13
12
  inherit?: boolean;
14
- root?: boolean;
15
13
  }) {
16
- const parentScope = useContext(ScopeContext);
14
+ const parentScope = useContext(ScopeContext) ?? ROOT_SCOPE;
17
15
  const scope = new SignalScope(contexts as [ContextImpl<unknown>, unknown][], inherit ? parentScope : undefined);
18
16
 
19
17
  return <ScopeContext.Provider value={scope}>{children}</ScopeContext.Provider>;
@@ -12,10 +12,28 @@ const REACT_INTERNALS =
12
12
  const IS_REACT_18 = !!REACT_INTERNALS.ReactCurrentDispatcher;
13
13
  const ReactCurrentDispatcher = REACT_INTERNALS.ReactCurrentDispatcher || REACT_INTERNALS;
14
14
 
15
+ let RENDERING_SAFE_MODE_COUNT = 0;
16
+
17
+ /**
18
+ * Reactive functions can be called anywhere, but React Hooks cannot. When calling reactive functions
19
+ * in code that _may or may not_ be used while rendering, we need to use this function to wrap the
20
+ * call. This will ensure that we will not be in a rendering context when the reactive function is called.
21
+ */
22
+ export const runReactiveSafe = <T, Args extends unknown[]>(fn: (...args: Args) => T, ...args: Args): T => {
23
+ RENDERING_SAFE_MODE_COUNT++;
24
+
25
+ try {
26
+ return fn(...args);
27
+ } finally {
28
+ RENDERING_SAFE_MODE_COUNT--;
29
+ }
30
+ };
31
+
15
32
  export function isRendering() {
16
33
  const dispatcher = IS_REACT_18 ? ReactCurrentDispatcher.current : ReactCurrentDispatcher.H;
17
34
 
18
35
  return (
36
+ RENDERING_SAFE_MODE_COUNT === 0 &&
19
37
  !!dispatcher &&
20
38
  // dispatcher can be in a state where it's defined, but all hooks are invalid to call.
21
39
  // Only way we can tell is that if they are invalid, they will all be equal to each other