sygnal 2.6.0 → 2.6.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.cjs.js CHANGED
@@ -351,6 +351,16 @@ var hasSymbols$2 = function hasNativeSymbols() {
351
351
  return hasSymbolSham();
352
352
  };
353
353
 
354
+ var test = {
355
+ foo: {}
356
+ };
357
+
358
+ var $Object = Object;
359
+
360
+ var hasProto$1 = function hasProto() {
361
+ return { __proto__: test }.foo === test.foo && !({ __proto__: null } instanceof $Object);
362
+ };
363
+
354
364
  /* eslint no-invalid-this: 1 */
355
365
 
356
366
  var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
@@ -453,18 +463,23 @@ var ThrowTypeError = $gOPD
453
463
  : throwTypeError;
454
464
 
455
465
  var hasSymbols$1 = hasSymbols$2();
466
+ var hasProto = hasProto$1();
456
467
 
457
- var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto
468
+ var getProto = Object.getPrototypeOf || (
469
+ hasProto
470
+ ? function (x) { return x.__proto__; } // eslint-disable-line no-proto
471
+ : null
472
+ );
458
473
 
459
474
  var needsEval = {};
460
475
 
461
- var TypedArray = typeof Uint8Array === 'undefined' ? undefined$1 : getProto(Uint8Array);
476
+ var TypedArray = typeof Uint8Array === 'undefined' || !getProto ? undefined$1 : getProto(Uint8Array);
462
477
 
463
478
  var INTRINSICS = {
464
479
  '%AggregateError%': typeof AggregateError === 'undefined' ? undefined$1 : AggregateError,
465
480
  '%Array%': Array,
466
481
  '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer,
467
- '%ArrayIteratorPrototype%': hasSymbols$1 ? getProto([][Symbol.iterator]()) : undefined$1,
482
+ '%ArrayIteratorPrototype%': hasSymbols$1 && getProto ? getProto([][Symbol.iterator]()) : undefined$1,
468
483
  '%AsyncFromSyncIteratorPrototype%': undefined$1,
469
484
  '%AsyncFunction%': needsEval,
470
485
  '%AsyncGenerator%': needsEval,
@@ -472,6 +487,8 @@ var INTRINSICS = {
472
487
  '%AsyncIteratorPrototype%': needsEval,
473
488
  '%Atomics%': typeof Atomics === 'undefined' ? undefined$1 : Atomics,
474
489
  '%BigInt%': typeof BigInt === 'undefined' ? undefined$1 : BigInt,
490
+ '%BigInt64Array%': typeof BigInt64Array === 'undefined' ? undefined$1 : BigInt64Array,
491
+ '%BigUint64Array%': typeof BigUint64Array === 'undefined' ? undefined$1 : BigUint64Array,
475
492
  '%Boolean%': Boolean,
476
493
  '%DataView%': typeof DataView === 'undefined' ? undefined$1 : DataView,
477
494
  '%Date%': Date,
@@ -492,10 +509,10 @@ var INTRINSICS = {
492
509
  '%Int32Array%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array,
493
510
  '%isFinite%': isFinite,
494
511
  '%isNaN%': isNaN,
495
- '%IteratorPrototype%': hasSymbols$1 ? getProto(getProto([][Symbol.iterator]())) : undefined$1,
512
+ '%IteratorPrototype%': hasSymbols$1 && getProto ? getProto(getProto([][Symbol.iterator]())) : undefined$1,
496
513
  '%JSON%': typeof JSON === 'object' ? JSON : undefined$1,
497
514
  '%Map%': typeof Map === 'undefined' ? undefined$1 : Map,
498
- '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols$1 ? undefined$1 : getProto(new Map()[Symbol.iterator]()),
515
+ '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols$1 || !getProto ? undefined$1 : getProto(new Map()[Symbol.iterator]()),
499
516
  '%Math%': Math,
500
517
  '%Number%': Number,
501
518
  '%Object%': Object,
@@ -508,10 +525,10 @@ var INTRINSICS = {
508
525
  '%Reflect%': typeof Reflect === 'undefined' ? undefined$1 : Reflect,
509
526
  '%RegExp%': RegExp,
510
527
  '%Set%': typeof Set === 'undefined' ? undefined$1 : Set,
511
- '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols$1 ? undefined$1 : getProto(new Set()[Symbol.iterator]()),
528
+ '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols$1 || !getProto ? undefined$1 : getProto(new Set()[Symbol.iterator]()),
512
529
  '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer,
513
530
  '%String%': String,
514
- '%StringIteratorPrototype%': hasSymbols$1 ? getProto(''[Symbol.iterator]()) : undefined$1,
531
+ '%StringIteratorPrototype%': hasSymbols$1 && getProto ? getProto(''[Symbol.iterator]()) : undefined$1,
515
532
  '%Symbol%': hasSymbols$1 ? Symbol : undefined$1,
516
533
  '%SyntaxError%': $SyntaxError,
517
534
  '%ThrowTypeError%': ThrowTypeError,
@@ -527,6 +544,16 @@ var INTRINSICS = {
527
544
  '%WeakSet%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet
528
545
  };
529
546
 
547
+ if (getProto) {
548
+ try {
549
+ null.error; // eslint-disable-line no-unused-expressions
550
+ } catch (e) {
551
+ // https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229
552
+ var errorProto = getProto(getProto(e));
553
+ INTRINSICS['%Error.prototype%'] = errorProto;
554
+ }
555
+ }
556
+
530
557
  var doEval = function doEval(name) {
531
558
  var value;
532
559
  if (name === '%AsyncFunction%') {
@@ -542,7 +569,7 @@ var doEval = function doEval(name) {
542
569
  }
543
570
  } else if (name === '%AsyncIteratorPrototype%') {
544
571
  var gen = doEval('%AsyncGenerator%');
545
- if (gen) {
572
+ if (gen && getProto) {
546
573
  value = getProto(gen.prototype);
547
574
  }
548
575
  }
@@ -612,6 +639,7 @@ var $concat = bind.call(Function.call, Array.prototype.concat);
612
639
  var $spliceApply = bind.call(Function.apply, Array.prototype.splice);
613
640
  var $replace = bind.call(Function.call, String.prototype.replace);
614
641
  var $strSlice = bind.call(Function.call, String.prototype.slice);
642
+ var $exec = bind.call(Function.call, RegExp.prototype.exec);
615
643
 
616
644
  /* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */
617
645
  var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g;
@@ -667,6 +695,9 @@ var getIntrinsic = function GetIntrinsic(name, allowMissing) {
667
695
  throw new $TypeError('"allowMissing" argument must be a boolean');
668
696
  }
669
697
 
698
+ if ($exec(/^%?[^%]*%?$/, name) === null) {
699
+ throw new $SyntaxError('`%` may not be present anywhere but at the beginning and end of the intrinsic name');
700
+ }
670
701
  var parts = stringToPath(name);
671
702
  var intrinsicBaseName = parts.length > 0 ? parts[0] : '';
672
703
 
@@ -787,8 +818,14 @@ var hasPropertyDescriptors = hasPropertyDescriptors_1();
787
818
  var supportsDescriptors = origDefineProperty && hasPropertyDescriptors;
788
819
 
789
820
  var defineProperty = function (object, name, value, predicate) {
790
- if (name in object && (!isFunction(predicate) || !predicate())) {
791
- return;
821
+ if (name in object) {
822
+ if (predicate === true) {
823
+ if (object[name] === value) {
824
+ return;
825
+ }
826
+ } else if (!isFunction(predicate) || !predicate()) {
827
+ return;
828
+ }
792
829
  }
793
830
  if (supportsDescriptors) {
794
831
  origDefineProperty(object, name, {
@@ -3663,7 +3700,7 @@ class Component {
3663
3700
  }
3664
3701
 
3665
3702
  addCalculated(state) {
3666
- if (!this.calculated || typeof state !== 'object') return state
3703
+ if (!this.calculated || typeof state !== 'object' || state instanceof Array) return state
3667
3704
  if (typeof this.calculated !== 'object') throw new Error(`'calculated' parameter must be an object mapping calculated state field named to functions`)
3668
3705
  const entries = Object.entries(this.calculated);
3669
3706
  const calculated = entries.reduce((acc, [field, fn]) => {
@@ -3678,11 +3715,13 @@ class Component {
3678
3715
  return { ...state, ...calculated }
3679
3716
  }
3680
3717
 
3681
- cleanupCalculated(state) {
3682
- if (this.storeCalculatedInState) return this.addCalculated(state)
3683
- if (!this.calculated || !state || typeof state !== 'object') return state
3718
+ cleanupCalculated(incomingState) {
3719
+ if (!incomingState || typeof incomingState !== 'object' || incomingState instanceof Array) return incomingState
3720
+ const state = this.storeCalculatedInState ? this.addCalculated(incomingState) : incomingState;
3721
+ const { __props, __children, ...sanitized } = state;
3722
+ const copy = { ...sanitized };
3723
+ if (!this.calculated) return copy
3684
3724
  const keys = Object.keys(this.calculated);
3685
- const copy = { ...state };
3686
3725
  keys.forEach(key => {
3687
3726
  if (this.initialState && typeof this.initialState[key] !== 'undefined') {
3688
3727
  copy[key] = this.initialState[key];
@@ -3704,11 +3743,11 @@ class Component {
3704
3743
  renderParams[this.stateSourceName] = stateStream;
3705
3744
 
3706
3745
  if (this.sources.props$) {
3707
- renderParams.props = this.sources.props$;
3746
+ renderParams.__props = this.sources.props$;
3708
3747
  }
3709
3748
 
3710
3749
  if (this.sources.children$) {
3711
- renderParams.children = this.sources.children$;
3750
+ renderParams.__children = this.sources.children$;
3712
3751
  }
3713
3752
 
3714
3753
  const names = [];
@@ -3774,17 +3813,9 @@ class Component {
3774
3813
  instantiator = this.instantiateCustomComponent.bind(this);
3775
3814
  }
3776
3815
 
3777
- const { sink$, preventStateUpdates } = instantiator(el, props$, children$);
3778
-
3779
- if (preventStateUpdates) {
3780
- const originalStateSink = sink$[this.stateSourceName];
3781
- sink$[this.stateSourceName] = originalStateSink.filter(state => {
3782
- console.warn('State update attempt from component with inderect link to state: Components with state set through HTML properties/attributes cannot update application state directly');
3783
- return false
3784
- });
3785
- }
3816
+ const sink$ = instantiator(el, props$, children$);
3786
3817
 
3787
- sink$[this.DOMSourceName] = this.makeCoordinatedSubComponentDomSink(sink$[this.DOMSourceName]);
3818
+ sink$[this.DOMSourceName] = sink$[this.DOMSourceName] ? this.makeCoordinatedSubComponentDomSink(sink$[this.DOMSourceName]) : xs__default["default"].never();
3788
3819
 
3789
3820
  acc[id] = { sink$, props$, children$ };
3790
3821
 
@@ -3827,115 +3858,160 @@ class Component {
3827
3858
  const data = el.data;
3828
3859
  const props = data.props || {};
3829
3860
  el.children || [];
3830
- let stateSource = new state.StateSource(this.sources[this.stateSourceName].stream.startWith(this.currentState));
3831
-
3832
- let preventStateUpdates = false;
3833
- let sink$, field, lense;
3834
-
3835
- const stateGetter = state => {
3836
- const arr = state[field];
3837
- if (typeof arr === 'undefined') return
3838
- if (!Array.isArray(arr)) {
3839
- const label = typeof props.of === 'string' ? props.of : 'components';
3840
- console.warn(`Collection of ${ label } does not have a valid array in the 'for' property: expects either an array or a string of the name of an array property on the state`);
3841
- return []
3861
+
3862
+ const combined$ = xs__default["default"].combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
3863
+ .map(([state, __props, __children]) => {
3864
+ return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children } : { value: state, __props, __children }
3865
+ });
3866
+
3867
+ const stateSource = new state.StateSource(combined$);
3868
+ const stateField = props.from;
3869
+ let lense;
3870
+
3871
+ const factory = typeof props.of === 'function' ? props.of : this.components[props.of];
3872
+
3873
+ const sanitizeItems = item => {
3874
+ if (typeof item === 'object') {
3875
+ const { __props, __children, ...sanitized } = item;
3876
+ return sanitized
3877
+ } else {
3878
+ return item
3879
+ }
3880
+ };
3881
+
3882
+ const fieldLense = {
3883
+ get: state => {
3884
+ const { __props, __children } = state;
3885
+ if (!Array.isArray(state[stateField])) return []
3886
+ return state[stateField].map(item => {
3887
+ return typeof item === 'object' ? { ...item, __props, __children } : { value: item, __props, __children }
3888
+ })
3889
+ },
3890
+ set: (oldState, newState) => {
3891
+ if (this.calculated && stateField in this.calculated) {
3892
+ console.warn(`Collection sub-component of ${ this.name } attempted to update state on a calculated field '${ stateField }': Update ignored`);
3893
+ return oldState
3894
+ }
3895
+ return { ...oldState, [stateField]: newState.map(sanitizeItems) }
3842
3896
  }
3843
- return arr
3844
3897
  };
3845
3898
 
3846
- if (typeof props.for === 'undefined') {
3899
+ if (stateField === undefined) {
3847
3900
  lense = {
3848
3901
  get: state => {
3849
- if (!Array.isArray(state)) {
3850
- console.warn(`Collection sub-component of ${ this.name } has no 'for' attribute and the parent state is not an array: Provide a 'for' attribute with either an array or the name of a state property containing an array`);
3851
- return []
3852
- }
3902
+ if (!(state instanceof Array) && state.value && state.value instanceof Array) return state.value
3853
3903
  return state
3854
3904
  },
3855
- set: (oldState, newState) => newState
3856
- };
3857
- } else if (typeof props.for === 'string') {
3858
- field = props.for;
3859
- lense = {
3860
- get: stateGetter,
3861
- set: (state, arr) => {
3862
- if (this.calculated && field in this.calculated) {
3863
- console.warn(`Collection sub-component of ${ this.name } attempted to update state on a calculated field '${ field }': Update ignored`);
3864
- return state
3865
- }
3866
- return { ...state, [field]: arr }
3905
+ set: (oldState, newState) => {
3906
+ console.log('COLL SET', newState);
3907
+ return newState
3867
3908
  }
3868
3909
  };
3910
+ } else if (typeof stateField === 'string') {
3911
+ if (typeof this.currentState === 'object') {
3912
+ if(!(stateField in this.currentState)) {
3913
+ console.error(`Collection component in ${ this.name } is attempting to use non-existent state property '${ stateField }': To fix this error, specify a valid array property on the state. Attempting to use parent component state.`);
3914
+ lense = undefined;
3915
+ } else if (!Array.isArray(this.currentState[stateField])) {
3916
+ console.warn(`State property '${ stateField }' in collection comopnent of ${ this.name } is not an array: No components will be instantiated in the collection.`);
3917
+ lense = fieldLense;
3918
+ } else {
3919
+ lense = fieldLense;
3920
+ }
3921
+ } else {
3922
+ if (!Array.isArray(this.currentState[stateField])) {
3923
+ console.warn(`State property '${ stateField }' in collection comopnent of ${ this.name } is not an array: No components will be instantiated in the collection.`);
3924
+ lense = fieldLense;
3925
+ } else {
3926
+ lense = fieldLense;
3927
+ }
3928
+ }
3929
+ } else if (typeof stateField === 'object') {
3930
+ if (typeof stateField.get !== 'function') {
3931
+ console.error(`Collection component in ${ this.name } has an invalid 'from' field: Expecting 'undefined', a string indicating an array property in the state, or an object with 'get' and 'set' functions for retrieving and setting child state from the current state. Attempting to use parent component state.`);
3932
+ lense = undefined;
3933
+ } else {
3934
+ lense = { get: stateField.get, set: stateField.set };
3935
+ }
3869
3936
  } else {
3870
- field = 'for';
3871
- stateSource = new state.StateSource(props$.remember());
3872
- lense = {
3873
- get: stateGetter,
3874
- set: (state, arr) => state
3875
- };
3876
- preventStateUpdates = true;
3937
+ console.error(`Collection component in ${ this.name } has an invalid 'from' field: Expecting 'undefined', a string indicating an array property in the state, or an object with 'get' and 'set' functions for retrieving and setting child state from the current state. Attempting to use parent component state.`);
3938
+ lense = undefined;
3877
3939
  }
3878
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
3879
- const factory = typeof props.of === 'function' ? props.of : this.components[props.of];
3880
- sink$ = collection(factory, lense, { container: null })(sources);
3940
+
3941
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
3942
+ const sink$ = collection(factory, lense, { container: null })(sources);
3881
3943
  if (typeof sink$ !== 'object') {
3882
3944
  throw new Error('Invalid sinks returned from component factory of collection element')
3883
3945
  }
3884
- return { sink$, preventStateUpdates }
3946
+ return sink$
3885
3947
  }
3886
3948
 
3887
3949
  instantiateSwitchable(el, props$, children$) {
3888
3950
  const data = el.data;
3889
3951
  const props = data.props || {};
3890
3952
  el.children || [];
3891
- let stateSource = new state.StateSource(this.sources[this.stateSourceName].stream.startWith(this.currentState));
3892
3953
 
3893
- const stateField = props.state;
3894
- let preventStateUpdates = false;
3895
- let isolateSwitchable = false;
3896
- let sink$, lense;
3954
+ const combined$ = xs__default["default"].combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
3955
+ .map(([state, __props, __children]) => {
3956
+ return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children } : { value: state, __props, __children }
3957
+ });
3897
3958
 
3898
- if (typeof stateField === 'string') {
3899
- isolateSwitchable = true;
3900
- lense = {
3901
- get: state => {
3902
- return state[stateField]
3903
- },
3904
- set: (oldState, newState) => {
3905
- if (this.calculated && stateField in this.calculated) {
3906
- console.warn(`Switchable sub-component of ${ this.name } attempted to update state on a calculated field '${ stateField }': Update ignored`);
3907
- return oldState
3908
- }
3909
- return { ...oldState, [stateField]: newState }
3959
+ const stateSource = new state.StateSource(combined$);
3960
+ const stateField = props.state;
3961
+ let lense;
3962
+
3963
+ const fieldLense = {
3964
+ get: state => {
3965
+ const { __props, __children } = state;
3966
+ return (typeof state[stateField] === 'object' && !(state[stateField] instanceof Array)) ? { ...state[stateField], __props, __children } : { value: state[stateField], __props, __children }
3967
+ },
3968
+ set: (oldState, newState) => {
3969
+ if (this.calculated && stateField in this.calculated) {
3970
+ console.warn(`Switchable sub-component of ${ this.name } attempted to update state on a calculated field '${ stateField }': Update ignored`);
3971
+ return oldState
3910
3972
  }
3911
- };
3912
- preventStateUpdates = false;
3913
- } else if (typeof stateField === 'undefined') {
3914
- isolateSwitchable = true;
3915
- lense = {
3916
- get: state => state,
3917
- set: (oldState, newState) => newState
3918
- };
3919
- preventStateUpdates = false;
3973
+ console.log('SWITCH SET', newState);
3974
+ if (typeof newState !== 'object' || newState instanceof Array) return { ...oldState, [stateField]: newState }
3975
+ const { __props, __children, ...sanitized } = newState;
3976
+ return { ...oldState, [stateField]: sanitized }
3977
+ }
3978
+ };
3979
+
3980
+ const baseLense = {
3981
+ get: state => state,
3982
+ set: (oldState, newState) => {
3983
+ if (typeof newState !== 'object' || newState instanceof Array) return newState
3984
+ const { __props, __children, ...sanitized } = newState;
3985
+ return sanitized
3986
+ }
3987
+ };
3988
+
3989
+ if (typeof stateField === 'undefined') {
3990
+ lense = baseLense;
3991
+ } else if (typeof stateField === 'string') {
3992
+ lense = fieldLense;
3920
3993
  } else if (typeof stateField === 'object') {
3921
- stateSource = new state.StateSource(props$.map(props => props.state));
3994
+ if (typeof stateField.get !== 'function') {
3995
+ console.error(`Switchable component in ${ this.name } has an invalid 'state' field: Expecting 'undefined', a string indicating an array property in the state, or an object with 'get' and 'set' functions for retrieving and setting sub-component state from the current state. Attempting to use parent component state.`);
3996
+ lense = baseLense;
3997
+ } else {
3998
+ lense = { get: stateField.get, set: stateField.set };
3999
+ }
3922
4000
  } else {
3923
- throw new Error(`Invalid state provided to collection sub-component of ${ this.name }: Expecting string, object, or none, but found ${ typeof stateField }`)
4001
+ console.error(`Invalid state provided to switchable sub-component of ${ this.name }: Expecting string, object, or undefined, but found ${ typeof stateField }. Attempting to use parent component state.`);
4002
+ lense = baseLense;
3924
4003
  }
3925
4004
 
3926
- const switchableComponents = data.props.of;
4005
+ const switchableComponents = props.of;
3927
4006
  const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
3928
- if (isolateSwitchable) {
3929
- sink$ = isolate__default["default"](switchable(switchableComponents, props$.map(props => props.current)), { [this.stateSourceName]: lense })(sources);
3930
- } else {
3931
- sink$ = switchable(switchableComponents, props$.map(props => props.current))(sources);
3932
- }
4007
+
4008
+ const sink$ = isolate__default["default"](switchable(switchableComponents, props$.map(props => props.current)), { [this.stateSourceName]: lense })(sources);
3933
4009
 
3934
4010
  if (typeof sink$ !== 'object') {
3935
4011
  throw new Error('Invalid sinks returned from component factory of switchable element')
3936
4012
  }
3937
4013
 
3938
- return { sink$, preventStateUpdates }
4014
+ return sink$
3939
4015
  }
3940
4016
 
3941
4017
  instantiateCustomComponent(el, props$, children$) {
@@ -3943,43 +4019,71 @@ class Component {
3943
4019
  const data = el.data;
3944
4020
  const props = data.props || {};
3945
4021
  el.children || [];
3946
- let stateSource = new state.StateSource(this.sources[this.stateSourceName].stream.startWith(this.currentState));
4022
+
4023
+ const combined$ = xs__default["default"].combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
4024
+ .map(([state, __props, __children]) => {
4025
+ return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children } : { value: state, __props, __children }
4026
+ });
4027
+
4028
+ const stateSource = new state.StateSource(combined$);
4029
+ const stateField = props.state;
3947
4030
 
3948
4031
  const factory = componentName === 'sygnal-factory' ? props.sygnalFactory : (this.components[componentName] || props.sygnalFactory);
3949
- if (!factory && !isCollection && !isSwitchable) {
4032
+ if (!factory) {
3950
4033
  if (componentName === 'sygnal-factory') throw new Error(`Component not found on element with Capitalized selector and nameless function: JSX transpilation replaces selectors starting with upper case letters with functions in-scope with the same name, Sygnal cannot see the name of the resulting component.`)
3951
4034
  throw new Error(`Component not found: ${ componentName }`)
3952
4035
  }
3953
4036
 
3954
- let preventStateUpdates = false;
3955
- let sink$;
4037
+ let lense;
3956
4038
 
3957
- const { state: stateProp, sygnalFactory, id, ...sanitizedProps } = props;
3958
- if (typeof stateProp === 'undefined' && (typeof sanitizedProps !== 'object' || Object.keys(sanitizedProps).length === 0)) {
3959
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$: xs__default["default"].never().startWith(null), children$ };
3960
- sink$ = factory(sources);
3961
- preventStateUpdates = false;
4039
+ const fieldLense = {
4040
+ get: state => {
4041
+ const { __props, __children } = state;
4042
+ return typeof state[stateField] === 'object' ? { ...state[stateField], __props, __children } : { value: state[stateField], __props, __children }
4043
+ },
4044
+ set: (oldState, newState) => {
4045
+ if (this.calculated && stateField in this.calculated) {
4046
+ console.warn(`Sub-component of ${ this.name } attempted to update state on a calculated field '${ stateField }': Update ignored`);
4047
+ return oldState
4048
+ }
4049
+ return { ...oldState, [stateField]: newState }
4050
+ }
4051
+ };
4052
+
4053
+ const baseLense = {
4054
+ get: state => state,
4055
+ set: (oldState, newState) => {
4056
+ if (typeof newState !== 'object' || newState instanceof Array) return newState
4057
+ const { __props, __children, ...sanitized } = newState;
4058
+ return sanitized
4059
+ }
4060
+ };
4061
+
4062
+ if (typeof stateField === 'undefined') {
4063
+ lense = baseLense;
4064
+ } else if (typeof stateField === 'string') {
4065
+ lense = fieldLense;
4066
+ } else if (typeof stateField === 'object') {
4067
+ if (typeof stateField.get !== 'function') {
4068
+ console.error(`Sub-component in ${ this.name } has an invalid 'state' field: Expecting 'undefined', a string indicating an array property in the state, or an object with 'get' and 'set' functions for retrieving and setting sub-component state from the current state. Attempting to use parent component state.`);
4069
+ lense = baseLense;
4070
+ } else {
4071
+ lense = { get: stateField.get, set: stateField.set };
4072
+ }
3962
4073
  } else {
3963
- const lense = (props) => {
3964
- const state = props.state;
3965
- if (typeof state === 'undefined') return props
3966
- if (typeof state !== 'object') return state
3967
-
3968
- const copy = { ...props };
3969
- delete copy.state;
3970
- return { ...copy, ...state }
3971
- };
3972
- stateSource = new state.StateSource(props$.map(lense));
3973
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
3974
- sink$ = factory(sources);
4074
+ console.error(`Invalid state provided to sub-component of ${ this.name }: Expecting string, object, or undefined, but found ${ typeof stateField }. Attempting to use parent component state.`);
4075
+ lense = baseLense;
3975
4076
  }
3976
4077
 
4078
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
4079
+ const sink$ = isolate__default["default"](factory, { [this.stateSourceName]: lense })(sources);
4080
+
3977
4081
  if (typeof sink$ !== 'object') {
3978
4082
  const name = componentName === 'sygnal-factory' ? 'custom element' : componentName;
3979
4083
  throw new Error('Invalid sinks returned from component factory:', name)
3980
4084
  }
3981
4085
 
3982
- return { sink$, preventStateUpdates }
4086
+ return sink$
3983
4087
  }
3984
4088
 
3985
4089
  renderVdom(componentInstances$) {
@@ -4068,7 +4172,7 @@ function getComponents(currentElement, componentNames, depth=0, index=0) {
4068
4172
  const isSwitchable = sel && sel.toLowerCase() === 'switchable';
4069
4173
  const isComponent = sel && (['collection', 'switchable', 'sygnal-factory', ...componentNames].includes(currentElement.sel)) || typeof currentElement.data?.props?.sygnalFactory === 'function';
4070
4174
  const props = (currentElement.data && currentElement.data.props) || {};
4071
- const attrs = (currentElement.data && currentElement.data.attrs) || {};
4175
+ (currentElement.data && currentElement.data.attrs) || {};
4072
4176
  const children = currentElement.children || [];
4073
4177
 
4074
4178
  let found = {};
@@ -4079,11 +4183,9 @@ function getComponents(currentElement, componentNames, depth=0, index=0) {
4079
4183
  if (!props.of) throw new Error(`Collection element missing required 'component' property`)
4080
4184
  if (typeof props.of !== 'string' && typeof props.of !== 'function') throw new Error(`Invalid 'component' property of collection element: found ${ typeof props.of } requires string or component factory function`)
4081
4185
  if (typeof props.of !== 'function' && !componentNames.includes(props.of)) throw new Error(`Specified component for collection not found: ${ props.of }`)
4082
- if (typeof attrs.for !== 'undefined' && !(typeof attrs.for === 'string' || Array.isArray(attrs.for))) console.warn(`No valid array found in the 'value' property of collection ${ typeof props.of === 'string' ? props.of : 'function component' }: no collection components will be created`);
4186
+ if (typeof props.from !== 'undefined' && !(typeof props.from === 'string' || Array.isArray(props.from))) console.warn(`No valid array found in the 'value' property of collection ${ typeof props.of === 'string' ? props.of : 'function component' }: no collection components will be created`);
4083
4187
  currentElement.data.isCollection = true;
4084
4188
  currentElement.data.props ||= {};
4085
- currentElement.data.props.for = attrs.for;
4086
- currentElement.data.attrs = undefined;
4087
4189
  } else if (isSwitchable) {
4088
4190
  if (!props.of) throw new Error(`Switchable element missing required 'of' property`)
4089
4191
  if (typeof props.of !== 'object') throw new Error(`Invalid 'components' property of switchable element: found ${ typeof props.of } requires object mapping names to component factories`)