sygnal 2.9.2 → 2.9.4

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
@@ -3158,7 +3158,7 @@ const ABORT = '~#~#~ABORT~#~#~';
3158
3158
  function component (opts) {
3159
3159
  const { name, sources, isolateOpts, stateSourceName='STATE' } = opts;
3160
3160
 
3161
- if (sources && typeof sources !== 'object') {
3161
+ if (sources && !isObj(sources)) {
3162
3162
  throw new Error('Sources must be a Cycle.js sources object:', name)
3163
3163
  }
3164
3164
 
@@ -3176,7 +3176,7 @@ function component (opts) {
3176
3176
  const currySources = typeof sources === 'undefined';
3177
3177
  let returnFunction;
3178
3178
 
3179
- if (typeof fixedIsolateOpts == 'object') {
3179
+ if (isObj(fixedIsolateOpts)) {
3180
3180
  const wrapped = (sources) => {
3181
3181
  const fixedOpts = { ...opts, sources };
3182
3182
  return (new Component(fixedOpts)).sinks
@@ -3240,7 +3240,7 @@ class Component {
3240
3240
  // sinks
3241
3241
 
3242
3242
  constructor({ name='NO NAME', sources, intent, request, model, context, response, view, children={}, components={}, initialState, calculated, storeCalculatedInState=true, DOMSourceName='DOM', stateSourceName='STATE', requestSourceName='HTTP', debug=false }) {
3243
- if (!sources || typeof sources != 'object') throw new Error('Missing or invalid sources')
3243
+ if (!sources || !isObj(sources)) throw new Error('Missing or invalid sources')
3244
3244
 
3245
3245
  this.name = name;
3246
3246
  this.sources = sources;
@@ -3320,7 +3320,7 @@ class Component {
3320
3320
 
3321
3321
  this.intent$ = this.intent(this.sources);
3322
3322
 
3323
- if (!(this.intent$ instanceof xs$1.Stream) && (typeof this.intent$ != 'object')) {
3323
+ if (!(this.intent$ instanceof xs$1.Stream) && (!isObj(this.intent$))) {
3324
3324
  throw new Error('Intent must return either an action$ stream or map of event streams')
3325
3325
  }
3326
3326
  }
@@ -3364,7 +3364,7 @@ class Component {
3364
3364
  initResponse$() {
3365
3365
  if (typeof this.request == 'undefined') {
3366
3366
  return
3367
- } else if (typeof this.request != 'object') {
3367
+ } else if (!isObj(this.request)) {
3368
3368
  throw new Error('The request parameter must be an object')
3369
3369
  }
3370
3370
 
@@ -3469,29 +3469,16 @@ class Component {
3469
3469
  this.context$ = xs$1.of({});
3470
3470
  return
3471
3471
  }
3472
- const repeatChecker = (a, b) => {
3473
- if (a === b) return true
3474
- if (typeof a !== 'object' || typeof b !== 'object') {
3475
- return a === b
3476
- }
3477
- const entriesA = Object.entries(a);
3478
- const entriesB = Object.entries(b);
3479
- if (entriesA.length === 0 && entriesB.length === 0) return true
3480
- if (entriesA.length !== entriesB.length) return false
3481
- return entriesA.every(([name, value]) => {
3482
- return b[name] === value
3483
- })
3484
- };
3485
3472
 
3486
- const state$ = this.sources[this.stateSourceName]?.stream.startWith({}).compose(_default$5(repeatChecker)) || xs$1.never();
3487
- const parentContext$ = this.sources.__parentContext$.startWith({}).compose(_default$5(repeatChecker)) || xs$1.of({});
3488
- if (this.context && typeof this.context !== 'object') {
3473
+ const state$ = this.sources[this.stateSourceName]?.stream.startWith({}).compose(_default$5(objIsEqual)) || xs$1.never();
3474
+ const parentContext$ = this.sources.__parentContext$.startWith({}).compose(_default$5(objIsEqual)) || xs$1.of({});
3475
+ if (this.context && !isObj(this.context)) {
3489
3476
  console.error(`[${this.name}] Context must be an object mapping names to values of functions: ignoring provided ${ typeof this.context }`);
3490
3477
  }
3491
3478
  this.context$ = xs$1.combine(state$, parentContext$)
3492
3479
  .map(([_, parent]) => {
3493
- const _parent = typeof parent === 'object' ? parent : {};
3494
- const context = typeof this.context === 'object' ? this.context : {};
3480
+ const _parent = isObj(parent) ? parent : {};
3481
+ const context = isObj(this.context) ? this.context : {};
3495
3482
  const state = this.currentState;
3496
3483
  const values = Object.entries(context).reduce((acc, current) => {
3497
3484
  const [name, value] = current;
@@ -3514,7 +3501,7 @@ class Component {
3514
3501
  this.currentContext = newContext;
3515
3502
  return newContext
3516
3503
  })
3517
- .compose(_default$5(repeatChecker))
3504
+ .compose(_default$5(objIsEqual))
3518
3505
  .startWith({});
3519
3506
  this.context$.subscribe({ next: _ => _ });
3520
3507
  }
@@ -3548,7 +3535,7 @@ class Component {
3548
3535
  sinks = { [this.stateSourceName]: sinks };
3549
3536
  }
3550
3537
 
3551
- if (typeof sinks !== 'object') {
3538
+ if (!isObj(sinks)) {
3552
3539
  throw new Error(`Entry for each action must be an object: ${ this.name } ${ action }`)
3553
3540
  }
3554
3541
 
@@ -3614,7 +3601,7 @@ class Component {
3614
3601
  };
3615
3602
 
3616
3603
  const out = this.response(selectable);
3617
- if (typeof out != 'object') {
3604
+ if (!isObj(out)) {
3618
3605
  throw new Error('The response function must return an object')
3619
3606
  }
3620
3607
 
@@ -3726,7 +3713,7 @@ class Component {
3726
3713
  const next = (type, data, delay=10) => {
3727
3714
  if (typeof delay !== 'number') throw new Error(`[${ this.name } ] Invalid delay value provided to next() function in model action '${ name }'. Must be a number in ms.`)
3728
3715
  const _reqId = action._reqId || (action.req && action.req.id);
3729
- const _data = _reqId ? (typeof data == 'object' ? { ...data, _reqId, _action: name } : { data, _reqId, _action: name }) : data;
3716
+ const _data = _reqId ? (isObj(data) ? { ...data, _reqId, _action: name } : { data, _reqId, _action: name }) : data;
3730
3717
  // put the "next" action request at the end of the event loop so the "current" action completes first
3731
3718
  setTimeout(() => {
3732
3719
  // push the "next" action request into the action$ stream
@@ -3751,7 +3738,7 @@ class Component {
3751
3738
  const type = typeof reduced;
3752
3739
  const _reqId = action._reqId || (action.req && action.req.id);
3753
3740
  if (['string', 'number', 'boolean', 'function'].includes(type)) return reduced
3754
- if (type == 'object') return { ...reduced, _reqId, _action: name }
3741
+ if (isObj(reduced)) return { ...reduced, _reqId, _action: name }
3755
3742
  if (type == 'undefined') {
3756
3743
  console.warn(`'undefined' value sent to ${ name }`);
3757
3744
  return reduced
@@ -3775,11 +3762,11 @@ class Component {
3775
3762
  let lastResult;
3776
3763
 
3777
3764
  return function(state) {
3778
- if (!this.calculated || typeof state !== 'object' || state instanceof Array) return state
3765
+ if (!this.calculated || !isObj(state) || Array.isArray(state)) return state
3779
3766
  if (state === lastState) {
3780
3767
  return lastResult
3781
3768
  }
3782
- if (typeof this.calculated !== 'object') throw new Error(`'calculated' parameter must be an object mapping calculated state field named to functions`)
3769
+ if (!isObj(this.calculated)) throw new Error(`'calculated' parameter must be an object mapping calculated state field named to functions`)
3783
3770
  const entries = Object.entries(this.calculated);
3784
3771
  if (entries.length === 0) {
3785
3772
  lastState = state;
@@ -3806,7 +3793,7 @@ class Component {
3806
3793
  }
3807
3794
 
3808
3795
  cleanupCalculated(incomingState) {
3809
- if (!incomingState || typeof incomingState !== 'object' || incomingState instanceof Array) return incomingState
3796
+ if (!incomingState || !isObj(incomingState) || Array.isArray(incomingState)) return incomingState
3810
3797
  const state = this.storeCalculatedInState ? this.addCalculated(incomingState) : incomingState;
3811
3798
  const { __props, __children, __context, ...sanitized } = state;
3812
3799
  const copy = { ...sanitized };
@@ -3967,20 +3954,42 @@ class Component {
3967
3954
  const data = el.data;
3968
3955
  const props = data.props || {};
3969
3956
  el.children || [];
3957
+ let filter = typeof props.filter === 'function' ? props.filter : _ => true;
3970
3958
 
3971
3959
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
3972
3960
  .map(([state, __props, __children]) => {
3973
- return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
3961
+ if (__props.filter && typeof __props.filter === 'function') {
3962
+ filter = __props.filter;
3963
+ }
3964
+ return isObj(state) ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
3974
3965
  });
3975
3966
 
3976
- const stateSource = new state.StateSource(combined$);
3977
- const stateField = props.from;
3967
+ const stateSource = new state.StateSource(combined$);
3968
+ const stateField = props.from;
3969
+ const collectionOf = props.of;
3970
+ const idField = props.idfield || 'id';
3971
+
3978
3972
  let lense;
3973
+ let factory;
3979
3974
 
3980
- const factory = typeof props.of === 'function' ? props.of : this.components[props.of];
3975
+ if (typeof collectionOf === 'function') {
3976
+ if (collectionOf.isSygnalComponent) {
3977
+ factory = collectionOf;
3978
+ } else {
3979
+ const name = (typeof collectionOf.name === 'string') ? collectionOf.name : 'FUNCTION_COMPONENT';
3980
+ const view = collectionOf;
3981
+ const { model, intent, context, children, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = collectionOf;
3982
+ const options = { name, view, model, intent, context, children, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
3983
+ factory = component(options);
3984
+ }
3985
+ } else if (this.components[collectionOf]) {
3986
+ factory = this.components[collectionOf];
3987
+ } else {
3988
+ throw new Error(`[${this.name}] Invalid 'of' propery in collection: ${ collectionOf }`)
3989
+ }
3981
3990
 
3982
3991
  const sanitizeItems = item => {
3983
- if (typeof item === 'object') {
3992
+ if (isObj(item)) {
3984
3993
  const { __props, __children, __context, ...sanitized } = item;
3985
3994
  return sanitized
3986
3995
  } else {
@@ -3992,8 +4001,8 @@ class Component {
3992
4001
  get: state => {
3993
4002
  const { __props, __children } = state;
3994
4003
  if (!Array.isArray(state[stateField])) return []
3995
- return state[stateField].map(item => {
3996
- return typeof item === 'object' ? { ...item, __props, __children, __context: this.currentContext } : { value: item, __props, __children, __context: this.currentContext }
4004
+ return state[stateField].filter(filter).map((item, index) => {
4005
+ return (isObj(item)) ? { ...item, [idField]: item[idField] || index, __props, __children, __context: this.currentContext } : { value: item, [idField]: index, __props, __children, __context: this.currentContext }
3997
4006
  })
3998
4007
  },
3999
4008
  set: (oldState, newState) => {
@@ -4001,6 +4010,15 @@ class Component {
4001
4010
  console.warn(`Collection sub-component of ${ this.name } attempted to update state on a calculated field '${ stateField }': Update ignored`);
4002
4011
  return oldState
4003
4012
  }
4013
+ const updated = [];
4014
+ for (const oldItem of oldState[stateField].map((item, index) => ({ __primitive: true, value: item, [idField]: index }))) {
4015
+ if (!filter(oldItem)) {
4016
+ updated.push(oldItem.__primitive ? oldItem.value : oldItem);
4017
+ } else {
4018
+ const newItem = newState.find(newItem => newItem.id === oldItem.id);
4019
+ if (typeof newItem !== 'undefined') updated.push(oldItem.__primitive ? newItem.value : newItem);
4020
+ }
4021
+ }
4004
4022
  return { ...oldState, [stateField]: newState.map(sanitizeItems) }
4005
4023
  }
4006
4024
  };
@@ -4016,7 +4034,7 @@ class Component {
4016
4034
  }
4017
4035
  };
4018
4036
  } else if (typeof stateField === 'string') {
4019
- if (typeof this.currentState === 'object') {
4037
+ if (isObj(this.currentState)) {
4020
4038
  if(!(this.currentState && stateField in this.currentState) && !(this.calculated && stateField in this.calculated)) {
4021
4039
  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.`);
4022
4040
  lense = undefined;
@@ -4034,7 +4052,7 @@ class Component {
4034
4052
  lense = fieldLense;
4035
4053
  }
4036
4054
  }
4037
- } else if (typeof stateField === 'object') {
4055
+ } else if (isObj(stateField)) {
4038
4056
  if (typeof stateField.get !== 'function') {
4039
4057
  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.`);
4040
4058
  lense = undefined;
@@ -4058,7 +4076,7 @@ class Component {
4058
4076
 
4059
4077
  const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4060
4078
  const sink$ = collection(factory, lense, { container: null })(sources);
4061
- if (typeof sink$ !== 'object') {
4079
+ if (!isObj(sink$)) {
4062
4080
  throw new Error('Invalid sinks returned from component factory of collection element')
4063
4081
  }
4064
4082
  return sink$
@@ -4071,7 +4089,7 @@ class Component {
4071
4089
 
4072
4090
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
4073
4091
  .map(([state, __props, __children]) => {
4074
- return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
4092
+ return isObj(state) ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
4075
4093
  });
4076
4094
 
4077
4095
  const stateSource = new state.StateSource(combined$);
@@ -4081,14 +4099,14 @@ class Component {
4081
4099
  const fieldLense = {
4082
4100
  get: state => {
4083
4101
  const { __props, __children } = state;
4084
- return (typeof state[stateField] === 'object' && !(state[stateField] instanceof Array)) ? { ...state[stateField], __props, __children, __context: this.currentContext } : { value: state[stateField], __props, __children, __context: this.currentContext }
4102
+ return (isObj(state[stateField])) ? { ...state[stateField], __props, __children, __context: this.currentContext } : { value: state[stateField], __props, __children, __context: this.currentContext }
4085
4103
  },
4086
4104
  set: (oldState, newState) => {
4087
4105
  if (this.calculated && stateField in this.calculated) {
4088
4106
  console.warn(`Switchable sub-component of ${ this.name } attempted to update state on a calculated field '${ stateField }': Update ignored`);
4089
4107
  return oldState
4090
4108
  }
4091
- if (typeof newState !== 'object' || newState instanceof Array) return { ...oldState, [stateField]: newState }
4109
+ if (!isObj(newState) || Array.isArray(newState)) return { ...oldState, [stateField]: newState }
4092
4110
  const { __props, __children, __context, ...sanitized } = newState;
4093
4111
  return { ...oldState, [stateField]: sanitized }
4094
4112
  }
@@ -4097,7 +4115,7 @@ class Component {
4097
4115
  const baseLense = {
4098
4116
  get: state => state,
4099
4117
  set: (oldState, newState) => {
4100
- if (typeof newState !== 'object' || newState instanceof Array) return newState
4118
+ if (!isObj(newState) || Array.isArray(newState)) return newState
4101
4119
  const { __props, __children, __context, ...sanitized } = newState;
4102
4120
  return sanitized
4103
4121
  }
@@ -4107,7 +4125,7 @@ class Component {
4107
4125
  lense = baseLense;
4108
4126
  } else if (typeof stateField === 'string') {
4109
4127
  lense = fieldLense;
4110
- } else if (typeof stateField === 'object') {
4128
+ } else if (isObj(stateField)) {
4111
4129
  if (typeof stateField.get !== 'function') {
4112
4130
  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.`);
4113
4131
  lense = baseLense;
@@ -4120,11 +4138,22 @@ class Component {
4120
4138
  }
4121
4139
 
4122
4140
  const switchableComponents = props.of;
4141
+ const keys = Object.keys(switchableComponents);
4142
+ keys.forEach(key => {
4143
+ const current = switchableComponents[key];
4144
+ if (!current.isSygnalComponent) {
4145
+ const name = (typeof current.name === 'string') ? current.name : 'FUNCTION_COMPONENT';
4146
+ const view = current;
4147
+ const { model, intent, context, children, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = current;
4148
+ const options = { name, view, model, intent, context, children, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
4149
+ switchableComponents[key] = component(options);
4150
+ }
4151
+ });
4123
4152
  const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4124
4153
 
4125
4154
  const sink$ = isolate(switchable(switchableComponents, props$.map(props => props.current)), { [this.stateSourceName]: lense })(sources);
4126
4155
 
4127
- if (typeof sink$ !== 'object') {
4156
+ if (!isObj(sink$)) {
4128
4157
  throw new Error('Invalid sinks returned from component factory of switchable element')
4129
4158
  }
4130
4159
 
@@ -4139,13 +4168,13 @@ class Component {
4139
4168
 
4140
4169
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
4141
4170
  .map(([state, __props, __children]) => {
4142
- return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
4171
+ return isObj(state) ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
4143
4172
  });
4144
4173
 
4145
4174
  const stateSource = new state.StateSource(combined$);
4146
4175
  const stateField = props.state;
4147
4176
 
4148
- if (typeof props.sygnalFactory !== 'function' && typeof props.sygnalOptions === 'object') {
4177
+ if (typeof props.sygnalFactory !== 'function' && isObj(props.sygnalOptions)) {
4149
4178
  props.sygnalFactory = component(props.sygnalOptions);
4150
4179
  }
4151
4180
 
@@ -4160,7 +4189,7 @@ class Component {
4160
4189
  const fieldLense = {
4161
4190
  get: state => {
4162
4191
  const { __props, __children } = state;
4163
- return typeof state[stateField] === 'object' ? { ...state[stateField], __props, __children, __context: this.currentContext } : { value: state[stateField], __props, __children, __context: this.currentContext }
4192
+ return isObj(state[stateField]) ? { ...state[stateField], __props, __children, __context: this.currentContext } : { value: state[stateField], __props, __children, __context: this.currentContext }
4164
4193
  },
4165
4194
  set: (oldState, newState) => {
4166
4195
  if (this.calculated && stateField in this.calculated) {
@@ -4174,7 +4203,7 @@ class Component {
4174
4203
  const baseLense = {
4175
4204
  get: state => state,
4176
4205
  set: (oldState, newState) => {
4177
- if (typeof newState !== 'object' || newState instanceof Array) return newState
4206
+ if (!isObj(newState) || Array.isArray(newState)) return newState
4178
4207
  const { __props, __children, __context, ...sanitized } = newState;
4179
4208
  return sanitized
4180
4209
  }
@@ -4184,7 +4213,7 @@ class Component {
4184
4213
  lense = baseLense;
4185
4214
  } else if (typeof stateField === 'string') {
4186
4215
  lense = fieldLense;
4187
- } else if (typeof stateField === 'object') {
4216
+ } else if (isObj(stateField)) {
4188
4217
  if (typeof stateField.get !== 'function') {
4189
4218
  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.`);
4190
4219
  lense = baseLense;
@@ -4199,7 +4228,7 @@ class Component {
4199
4228
  const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4200
4229
  const sink$ = isolate(factory, { [this.stateSourceName]: lense })(sources);
4201
4230
 
4202
- if (typeof sink$ !== 'object') {
4231
+ if (!isObj(sink$)) {
4203
4232
  const name = componentName === 'sygnal-factory' ? 'custom element' : componentName;
4204
4233
  throw new Error('Invalid sinks returned from component factory:', name)
4205
4234
  }
@@ -4297,7 +4326,7 @@ function getComponents(currentElement, componentNames, depth=0, index=0, parentI
4297
4326
  const sel = currentElement.sel;
4298
4327
  const isCollection = sel && sel.toLowerCase() === 'collection';
4299
4328
  const isSwitchable = sel && sel.toLowerCase() === 'switchable';
4300
- const isComponent = sel && (['collection', 'switchable', 'sygnal-factory', ...componentNames].includes(sel)) || typeof currentElement.data?.props?.sygnalFactory === 'function' || typeof currentElement.data?.props?.sygnalOptions === 'object';
4329
+ const isComponent = sel && (['collection', 'switchable', 'sygnal-factory', ...componentNames].includes(sel)) || typeof currentElement.data?.props?.sygnalFactory === 'function' || isObj(currentElement.data?.props?.sygnalOptions);
4301
4330
  const props = (currentElement.data && currentElement.data.props) || {};
4302
4331
  (currentElement.data && currentElement.data.attrs) || {};
4303
4332
  const children = currentElement.children || [];
@@ -4308,15 +4337,15 @@ function getComponents(currentElement, componentNames, depth=0, index=0, parentI
4308
4337
  if (isComponent) {
4309
4338
  id = getComponentIdFromElement(currentElement, depth, index, parentId);
4310
4339
  if (isCollection) {
4311
- if (!props.of) throw new Error(`Collection element missing required 'component' property`)
4340
+ if (!props.of) throw new Error(`Collection element missing required 'component' property`)
4312
4341
  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`)
4313
4342
  if (typeof props.of !== 'function' && !componentNames.includes(props.of)) throw new Error(`Specified component for collection not found: ${ props.of }`)
4314
4343
  if (typeof props.from !== 'undefined' && !(typeof props.from === 'string' || Array.isArray(props.from) || typeof props.from.get === 'function')) console.warn(`No valid array found for collection ${ typeof props.of === 'string' ? props.of : 'function component' }: no collection components will be created`, props.from);
4315
4344
  currentElement.data.isCollection = true;
4316
4345
  currentElement.data.props ||= {};
4317
4346
  } else if (isSwitchable) {
4318
- if (!props.of) throw new Error(`Switchable element missing required 'of' property`)
4319
- if (typeof props.of !== 'object') throw new Error(`Invalid 'of' property of switchable element: found ${ typeof props.of } requires object mapping names to component factories`)
4347
+ if (!props.of) throw new Error(`Switchable element missing required 'of' property`)
4348
+ if (!isObj(props.of)) throw new Error(`Invalid 'of' property of switchable element: found ${ typeof props.of } requires object mapping names to component factories`)
4320
4349
  const switchableComponents = Object.values(props.of);
4321
4350
  if (!switchableComponents.every(comp => typeof comp === 'function')) throw new Error(`One or more components provided to switchable element is not a valid component factory`)
4322
4351
  if (!props.current || (typeof props.current !== 'string' && typeof props.current !== 'function')) throw new Error(`Missing or invalid 'current' property for switchable element: found '${ typeof props.current }' requires string or function`)
@@ -4345,7 +4374,7 @@ function injectComponents(currentElement, components, componentNames, depth=0, i
4345
4374
 
4346
4375
 
4347
4376
  const sel = currentElement.sel || 'NO SELECTOR';
4348
- const isComponent = ['collection', 'switchable', 'sygnal-factory', ...componentNames].includes(sel) || typeof currentElement.data?.props?.sygnalFactory === 'function' || typeof currentElement.data?.props?.sygnalOptions === 'object';
4377
+ const isComponent = ['collection', 'switchable', 'sygnal-factory', ...componentNames].includes(sel) || typeof currentElement.data?.props?.sygnalFactory === 'function' || isObj(currentElement.data?.props?.sygnalOptions);
4349
4378
  const isCollection = currentElement?.data?.isCollection;
4350
4379
  const isSwitchable = currentElement?.data?.isSwitchable;
4351
4380
  (currentElement.data && currentElement.data.props) || {};
@@ -4443,11 +4472,15 @@ function objIsEqual(objA, objB, maxDepth = 5, depth = 0) {
4443
4472
  }
4444
4473
 
4445
4474
  function sanitizeObject(obj) {
4446
- if (typeof obj !== 'object' || obj === null) return obj
4475
+ if (!isObj(obj)) return obj
4447
4476
  const {state, of, from, _reqId, _action, __props, __children, __context, ...sanitized} = obj;
4448
4477
  return sanitized
4449
4478
  }
4450
4479
 
4480
+ function isObj(obj) {
4481
+ return typeof obj === 'object' && obj !== null && !Array.isArray(obj)
4482
+ }
4483
+
4451
4484
  function driverFromAsync(promiseReturningFunction, opts = {}) {
4452
4485
  const {
4453
4486
  selector: selectorProperty = 'category',