sygnal 4.2.0 → 4.3.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.
- package/dist/astro/client.cjs.js +547 -82
- package/dist/astro/client.mjs +547 -82
- package/dist/index.cjs.js +569 -94
- package/dist/index.esm.js +569 -95
- package/dist/jsx-dev-runtime.cjs.js +49 -12
- package/dist/jsx-dev-runtime.esm.js +49 -12
- package/dist/jsx-runtime.cjs.js +49 -12
- package/dist/jsx-runtime.esm.js +49 -12
- package/dist/jsx.cjs.js +49 -12
- package/dist/jsx.esm.js +49 -12
- package/dist/sygnal.min.js +1 -1
- package/package.json +6 -3
- package/src/collection.js +5 -2
- package/src/component.js +278 -79
- package/src/extra/devtools.js +249 -0
- package/src/extra/driverFactories.js +12 -12
- package/src/extra/eventDriver.js +2 -1
- package/src/extra/logDriver.js +3 -0
- package/src/extra/processDrag.js +6 -0
- package/src/extra/processForm.js +3 -0
- package/src/extra/run.js +12 -0
- package/src/index.d.ts +8 -4
- package/src/index.js +1 -0
- package/src/pragma/index.js +21 -9
- package/src/pragma/is.js +27 -2
- package/src/switchable.js +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -40,7 +40,12 @@ function _mergeNamespaces(n, m) {
|
|
|
40
40
|
|
|
41
41
|
var xstreamNamespace__namespace = /*#__PURE__*/_interopNamespaceDefault(xstreamNamespace);
|
|
42
42
|
|
|
43
|
+
let COLLECTION_COUNT = 0;
|
|
44
|
+
|
|
43
45
|
function collection(component, stateLense, opts={}) {
|
|
46
|
+
if (typeof component !== 'function') {
|
|
47
|
+
throw new Error('collection: first argument (component) must be a function')
|
|
48
|
+
}
|
|
44
49
|
const {
|
|
45
50
|
combineList = ['DOM'],
|
|
46
51
|
globalList = ['EVENTS'],
|
|
@@ -51,7 +56,7 @@ function collection(component, stateLense, opts={}) {
|
|
|
51
56
|
} = opts;
|
|
52
57
|
|
|
53
58
|
return (sources) => {
|
|
54
|
-
const key =
|
|
59
|
+
const key = `sygnal-collection-${COLLECTION_COUNT++}`;
|
|
55
60
|
const collectionOpts = {
|
|
56
61
|
item: component,
|
|
57
62
|
itemKey: (state, ind) => typeof state.id !== 'undefined' ? state.id : ind,
|
|
@@ -2868,7 +2873,7 @@ function switchable(factories, name$, initial, opts={}) {
|
|
|
2868
2873
|
const mapFunction = (nameType === 'function' && name$) || (state => state[name$]);
|
|
2869
2874
|
return sources => {
|
|
2870
2875
|
const state$ = sources && ((typeof stateSourceName === 'string' && sources[stateSourceName]) || sources.STATE || sources.state).stream;
|
|
2871
|
-
if (!state$ instanceof Stream$1) throw new Error(`Could not find the state source: ${
|
|
2876
|
+
if (!(state$ instanceof Stream$1)) throw new Error(`Could not find the state source: ${stateSourceName}`)
|
|
2872
2877
|
const _name$ = state$
|
|
2873
2878
|
.map(mapFunction)
|
|
2874
2879
|
.filter(name => typeof name === 'string')
|
|
@@ -3282,13 +3287,27 @@ function wrapDOMSource(domSource) {
|
|
|
3282
3287
|
}
|
|
3283
3288
|
|
|
3284
3289
|
|
|
3285
|
-
const ABORT = '
|
|
3290
|
+
const ABORT = Symbol('ABORT');
|
|
3291
|
+
|
|
3292
|
+
|
|
3293
|
+
function normalizeCalculatedEntry(field, entry) {
|
|
3294
|
+
if (typeof entry === 'function') {
|
|
3295
|
+
return { fn: entry, deps: null }
|
|
3296
|
+
}
|
|
3297
|
+
if (Array.isArray(entry) && entry.length === 2
|
|
3298
|
+
&& Array.isArray(entry[0]) && typeof entry[1] === 'function') {
|
|
3299
|
+
return { fn: entry[1], deps: entry[0] }
|
|
3300
|
+
}
|
|
3301
|
+
throw new Error(
|
|
3302
|
+
`Invalid calculated field '${field}': expected a function or [depsArray, function]`
|
|
3303
|
+
)
|
|
3304
|
+
}
|
|
3286
3305
|
|
|
3287
3306
|
function component (opts) {
|
|
3288
3307
|
const { name, sources, isolateOpts, stateSourceName='STATE' } = opts;
|
|
3289
3308
|
|
|
3290
3309
|
if (sources && !isObj(sources)) {
|
|
3291
|
-
throw new Error(
|
|
3310
|
+
throw new Error(`[${name}] Sources must be a Cycle.js sources object`)
|
|
3292
3311
|
}
|
|
3293
3312
|
|
|
3294
3313
|
let fixedIsolateOpts;
|
|
@@ -3368,7 +3387,9 @@ class Component {
|
|
|
3368
3387
|
// sinks
|
|
3369
3388
|
|
|
3370
3389
|
constructor({ name='NO NAME', sources, intent, model, hmrActions, context, response, view, peers={}, components={}, initialState, calculated, storeCalculatedInState=true, DOMSourceName='DOM', stateSourceName='STATE', requestSourceName='HTTP', debug=false }) {
|
|
3371
|
-
if (!sources || !isObj(sources)) throw new Error(
|
|
3390
|
+
if (!sources || !isObj(sources)) throw new Error(`[${name}] Missing or invalid sources`)
|
|
3391
|
+
|
|
3392
|
+
this._componentNumber = COMPONENT_COUNT++;
|
|
3372
3393
|
|
|
3373
3394
|
this.name = name;
|
|
3374
3395
|
this.sources = sources;
|
|
@@ -3389,6 +3410,123 @@ class Component {
|
|
|
3389
3410
|
this.sourceNames = Object.keys(sources);
|
|
3390
3411
|
this._debug = debug;
|
|
3391
3412
|
|
|
3413
|
+
// Warn if calculated fields shadow base state keys
|
|
3414
|
+
if (this.calculated && this.initialState
|
|
3415
|
+
&& isObj(this.calculated) && isObj(this.initialState)) {
|
|
3416
|
+
for (const key of Object.keys(this.calculated)) {
|
|
3417
|
+
if (key in this.initialState) {
|
|
3418
|
+
console.warn(
|
|
3419
|
+
`[${name}] Calculated field '${key}' shadows a key in initialState. ` +
|
|
3420
|
+
`The initialState value will be overwritten on every state update.`
|
|
3421
|
+
);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
// Normalize calculated entries, build dependency graph, topological sort
|
|
3427
|
+
if (this.calculated && isObj(this.calculated)) {
|
|
3428
|
+
const calcEntries = Object.entries(this.calculated);
|
|
3429
|
+
|
|
3430
|
+
// Normalize all entries to { fn, deps } shape
|
|
3431
|
+
this._calculatedNormalized = {};
|
|
3432
|
+
for (const [field, entry] of calcEntries) {
|
|
3433
|
+
this._calculatedNormalized[field] = normalizeCalculatedEntry(field, entry);
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
this._calculatedFieldNames = new Set(Object.keys(this._calculatedNormalized));
|
|
3437
|
+
|
|
3438
|
+
// Warn on deps referencing nonexistent keys
|
|
3439
|
+
for (const [field, { deps }] of Object.entries(this._calculatedNormalized)) {
|
|
3440
|
+
if (deps !== null) {
|
|
3441
|
+
for (const dep of deps) {
|
|
3442
|
+
if (!this._calculatedFieldNames.has(dep)
|
|
3443
|
+
&& this.initialState && !(dep in this.initialState)) {
|
|
3444
|
+
console.warn(
|
|
3445
|
+
`[${name}] Calculated field '${field}' declares dependency '${dep}' ` +
|
|
3446
|
+
`which is not in initialState or calculated fields`
|
|
3447
|
+
);
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
// Build adjacency: for each field, which other calculated fields must run first?
|
|
3454
|
+
const calcDeps = {};
|
|
3455
|
+
for (const [field, { deps }] of Object.entries(this._calculatedNormalized)) {
|
|
3456
|
+
if (deps === null) {
|
|
3457
|
+
calcDeps[field] = [];
|
|
3458
|
+
} else {
|
|
3459
|
+
calcDeps[field] = deps.filter(d => this._calculatedFieldNames.has(d));
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
// Kahn's algorithm for topological sort
|
|
3464
|
+
const inDegree = {};
|
|
3465
|
+
const reverseGraph = {};
|
|
3466
|
+
for (const field of this._calculatedFieldNames) {
|
|
3467
|
+
inDegree[field] = 0;
|
|
3468
|
+
reverseGraph[field] = [];
|
|
3469
|
+
}
|
|
3470
|
+
for (const [field, depList] of Object.entries(calcDeps)) {
|
|
3471
|
+
inDegree[field] = depList.length;
|
|
3472
|
+
for (const dep of depList) {
|
|
3473
|
+
reverseGraph[dep].push(field);
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3477
|
+
const queue = [];
|
|
3478
|
+
for (const [field, degree] of Object.entries(inDegree)) {
|
|
3479
|
+
if (degree === 0) queue.push(field);
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
const sorted = [];
|
|
3483
|
+
while (queue.length > 0) {
|
|
3484
|
+
const current = queue.shift();
|
|
3485
|
+
sorted.push(current);
|
|
3486
|
+
for (const dependent of reverseGraph[current]) {
|
|
3487
|
+
inDegree[dependent]--;
|
|
3488
|
+
if (inDegree[dependent] === 0) queue.push(dependent);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
if (sorted.length !== this._calculatedFieldNames.size) {
|
|
3493
|
+
// Cycle detected — build error message with cycle path
|
|
3494
|
+
const inCycle = [...this._calculatedFieldNames].filter(f => !sorted.includes(f));
|
|
3495
|
+
const visited = new Set();
|
|
3496
|
+
const path = [];
|
|
3497
|
+
const traceCycle = (node) => {
|
|
3498
|
+
if (visited.has(node)) { path.push(node); return true }
|
|
3499
|
+
visited.add(node);
|
|
3500
|
+
path.push(node);
|
|
3501
|
+
for (const dep of calcDeps[node]) {
|
|
3502
|
+
if (inCycle.includes(dep) && traceCycle(dep)) return true
|
|
3503
|
+
}
|
|
3504
|
+
path.pop();
|
|
3505
|
+
visited.delete(node);
|
|
3506
|
+
return false
|
|
3507
|
+
};
|
|
3508
|
+
traceCycle(inCycle[0]);
|
|
3509
|
+
const start = path[path.length - 1];
|
|
3510
|
+
const cycle = path.slice(path.indexOf(start));
|
|
3511
|
+
throw new Error(`Circular calculated dependency: ${cycle.join(' \u2192 ')}`)
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3514
|
+
this._calculatedOrder = sorted.map(f => [f, this._calculatedNormalized[f]]);
|
|
3515
|
+
|
|
3516
|
+
// Initialize per-field memoization caches for fields with declared deps
|
|
3517
|
+
this._calculatedFieldCache = {};
|
|
3518
|
+
for (const [field, { deps }] of this._calculatedOrder) {
|
|
3519
|
+
if (deps !== null) {
|
|
3520
|
+
this._calculatedFieldCache[field] = { lastDepValues: undefined, lastResult: undefined };
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
} else {
|
|
3524
|
+
this._calculatedOrder = null;
|
|
3525
|
+
this._calculatedNormalized = null;
|
|
3526
|
+
this._calculatedFieldNames = null;
|
|
3527
|
+
this._calculatedFieldCache = null;
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3392
3530
|
this.isSubComponent = this.sourceNames.includes('props$');
|
|
3393
3531
|
|
|
3394
3532
|
const state$ = sources[stateSourceName] && sources[stateSourceName].stream;
|
|
@@ -3397,6 +3535,9 @@ class Component {
|
|
|
3397
3535
|
this.currentState = initialState || {};
|
|
3398
3536
|
this.sources[stateSourceName] = new state.StateSource(state$.map(val => {
|
|
3399
3537
|
this.currentState = val;
|
|
3538
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
3539
|
+
window.__SYGNAL_DEVTOOLS__.onStateChanged(this._componentNumber, this.name, val);
|
|
3540
|
+
}
|
|
3400
3541
|
return val
|
|
3401
3542
|
}));
|
|
3402
3543
|
}
|
|
@@ -3432,10 +3573,8 @@ class Component {
|
|
|
3432
3573
|
};
|
|
3433
3574
|
}
|
|
3434
3575
|
|
|
3435
|
-
const componentNumber = COMPONENT_COUNT++;
|
|
3436
|
-
|
|
3437
3576
|
this.addCalculated = this.createMemoizedAddCalculated();
|
|
3438
|
-
this.log = makeLog(`${
|
|
3577
|
+
this.log = makeLog(`${this._componentNumber} | ${name}`);
|
|
3439
3578
|
|
|
3440
3579
|
this.initChildSources$();
|
|
3441
3580
|
this.initIntent$();
|
|
@@ -3450,9 +3589,20 @@ class Component {
|
|
|
3450
3589
|
this.initVdom$();
|
|
3451
3590
|
this.initSinks();
|
|
3452
3591
|
|
|
3453
|
-
this.sinks.__index =
|
|
3592
|
+
this.sinks.__index = this._componentNumber;
|
|
3454
3593
|
|
|
3455
3594
|
this.log(`Instantiated`, true);
|
|
3595
|
+
|
|
3596
|
+
// Hook 1: Register with DevTools
|
|
3597
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__) {
|
|
3598
|
+
window.__SYGNAL_DEVTOOLS__.onComponentCreated(this._componentNumber, name, this);
|
|
3599
|
+
|
|
3600
|
+
// Hook 1b: Register parent-child relationship
|
|
3601
|
+
const parentNum = sources?.__parentComponentNumber;
|
|
3602
|
+
if (typeof parentNum === 'number') {
|
|
3603
|
+
window.__SYGNAL_DEVTOOLS__.onSubComponentRegistered(parentNum, this._componentNumber);
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3456
3606
|
}
|
|
3457
3607
|
|
|
3458
3608
|
get debug() {
|
|
@@ -3464,13 +3614,13 @@ class Component {
|
|
|
3464
3614
|
return
|
|
3465
3615
|
}
|
|
3466
3616
|
if (typeof this.intent != 'function') {
|
|
3467
|
-
throw new Error(
|
|
3617
|
+
throw new Error(`[${this.name}] Intent must be a function`)
|
|
3468
3618
|
}
|
|
3469
3619
|
|
|
3470
3620
|
this.intent$ = this.intent(this.sources);
|
|
3471
3621
|
|
|
3472
3622
|
if (!(this.intent$ instanceof Stream$1) && (!isObj(this.intent$))) {
|
|
3473
|
-
throw new Error(
|
|
3623
|
+
throw new Error(`[${this.name}] Intent must return either an action$ stream or map of event streams`)
|
|
3474
3624
|
}
|
|
3475
3625
|
}
|
|
3476
3626
|
|
|
@@ -3483,10 +3633,10 @@ class Component {
|
|
|
3483
3633
|
this.hmrActions = [this.hmrActions];
|
|
3484
3634
|
}
|
|
3485
3635
|
if (!Array.isArray(this.hmrActions)) {
|
|
3486
|
-
throw new Error(`[${
|
|
3636
|
+
throw new Error(`[${this.name}] hmrActions must be the name of an action or an array of names of actions to run when a component is hot-reloaded`)
|
|
3487
3637
|
}
|
|
3488
3638
|
if (this.hmrActions.some(action => typeof action !== 'string')) {
|
|
3489
|
-
throw new Error(`[${
|
|
3639
|
+
throw new Error(`[${this.name}] hmrActions must be the name of an action or an array of names of actions to run when a component is hot-reloaded`)
|
|
3490
3640
|
}
|
|
3491
3641
|
this.hmrAction$ = xs$1.fromArray(this.hmrActions.map(action => ({ type: action })));
|
|
3492
3642
|
}
|
|
@@ -3525,7 +3675,15 @@ class Component {
|
|
|
3525
3675
|
const hydrate$ = initialApiData.map(data => ({ type: HYDRATE_ACTION, data }));
|
|
3526
3676
|
|
|
3527
3677
|
this.action$ = xs$1.merge(wrapped$, hydrate$)
|
|
3528
|
-
.compose(this.log(({ type }) => `<${
|
|
3678
|
+
.compose(this.log(({ type }) => `<${type}> Action triggered`))
|
|
3679
|
+
.map(action => {
|
|
3680
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
3681
|
+
window.__SYGNAL_DEVTOOLS__.onActionDispatched(
|
|
3682
|
+
this._componentNumber, this.name, action.type, action.data
|
|
3683
|
+
);
|
|
3684
|
+
}
|
|
3685
|
+
return action
|
|
3686
|
+
});
|
|
3529
3687
|
}
|
|
3530
3688
|
|
|
3531
3689
|
initState() {
|
|
@@ -3537,7 +3695,7 @@ class Component {
|
|
|
3537
3695
|
} else if (isObj(this.model[INITIALIZE_ACTION])) {
|
|
3538
3696
|
Object.keys(this.model[INITIALIZE_ACTION]).forEach(name => {
|
|
3539
3697
|
if (name !== this.stateSourceName) {
|
|
3540
|
-
console.warn(`${
|
|
3698
|
+
console.warn(`${INITIALIZE_ACTION} can only be used with the ${this.stateSourceName} source... disregarding ${name}`);
|
|
3541
3699
|
delete this.model[INITIALIZE_ACTION][name];
|
|
3542
3700
|
}
|
|
3543
3701
|
});
|
|
@@ -3572,7 +3730,7 @@ class Component {
|
|
|
3572
3730
|
} else if (valueType === 'function') {
|
|
3573
3731
|
_value = value(state);
|
|
3574
3732
|
} else {
|
|
3575
|
-
console.error(`[${
|
|
3733
|
+
console.error(`[${this.name}] Invalid context entry '${name}': must be the name of a state property or a function returning a value to use`);
|
|
3576
3734
|
return acc
|
|
3577
3735
|
}
|
|
3578
3736
|
acc[name] = _value;
|
|
@@ -3580,11 +3738,14 @@ class Component {
|
|
|
3580
3738
|
}, {});
|
|
3581
3739
|
const newContext = { ..._parent, ...values };
|
|
3582
3740
|
this.currentContext = newContext;
|
|
3741
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
3742
|
+
window.__SYGNAL_DEVTOOLS__.onContextChanged(this._componentNumber, this.name, newContext);
|
|
3743
|
+
}
|
|
3583
3744
|
return newContext
|
|
3584
3745
|
})
|
|
3585
3746
|
.compose(dropRepeats(objIsEqual))
|
|
3586
3747
|
.startWith({});
|
|
3587
|
-
this.context$.subscribe({ next: _ => _ });
|
|
3748
|
+
this.context$.subscribe({ next: _ => _, error: err => console.error(`[${this.name}] Error in context stream:`, err) });
|
|
3588
3749
|
}
|
|
3589
3750
|
|
|
3590
3751
|
initModel$() {
|
|
@@ -3600,7 +3761,7 @@ class Component {
|
|
|
3600
3761
|
const effectiveInitialState = (typeof hmrState !== 'undefined') ? hmrState : this.initialState;
|
|
3601
3762
|
const initial = { type: INITIALIZE_ACTION, data: effectiveInitialState };
|
|
3602
3763
|
if (this.isSubComponent && this.initialState) {
|
|
3603
|
-
console.warn(`[${
|
|
3764
|
+
console.warn(`[${this.name}] Initial state provided to sub-component. This will overwrite any state provided by the parent component.`);
|
|
3604
3765
|
}
|
|
3605
3766
|
const hasInitialState = (typeof effectiveInitialState !== 'undefined');
|
|
3606
3767
|
const shouldInjectInitialState = hasInitialState && (ENVIRONMENT?.__SYGNAL_HMR_UPDATING !== true || typeof hmrState !== 'undefined');
|
|
@@ -3621,7 +3782,7 @@ class Component {
|
|
|
3621
3782
|
}
|
|
3622
3783
|
|
|
3623
3784
|
if (!isObj(sinks)) {
|
|
3624
|
-
throw new Error(`Entry for each action must be an object: ${
|
|
3785
|
+
throw new Error(`[${this.name}] Entry for each action must be an object: ${action}`)
|
|
3625
3786
|
}
|
|
3626
3787
|
|
|
3627
3788
|
const sinkEntries = Object.entries(sinks);
|
|
@@ -3638,12 +3799,12 @@ class Component {
|
|
|
3638
3799
|
const wrapped$ = on$
|
|
3639
3800
|
.compose(this.log(data => {
|
|
3640
3801
|
if (isStateSink) {
|
|
3641
|
-
return `<${
|
|
3802
|
+
return `<${action}> State reducer added`
|
|
3642
3803
|
} else if (isParentSink) {
|
|
3643
|
-
return `<${
|
|
3804
|
+
return `<${action}> Data sent to parent component: ${JSON.stringify(data.value).replaceAll('"', '')}`
|
|
3644
3805
|
} else {
|
|
3645
3806
|
const extra = data && (data.type || data.command || data.name || data.key || (Array.isArray(data) && 'Array') || data);
|
|
3646
|
-
return `<${
|
|
3807
|
+
return `<${action}> Data sent to [${sink}]: ${JSON.stringify(extra).replaceAll('"', '')}`
|
|
3647
3808
|
}
|
|
3648
3809
|
}));
|
|
3649
3810
|
|
|
@@ -3721,7 +3882,7 @@ class Component {
|
|
|
3721
3882
|
|
|
3722
3883
|
}
|
|
3723
3884
|
});
|
|
3724
|
-
subComponentSink$.subscribe({ next: _ => _ });
|
|
3885
|
+
subComponentSink$.subscribe({ next: _ => _, error: err => console.error(`[${this.name}] Error in sub-component sink stream:`, err) });
|
|
3725
3886
|
this.subComponentSink$ = subComponentSink$.filter(sinks => Object.keys(sinks).length > 0);
|
|
3726
3887
|
}
|
|
3727
3888
|
|
|
@@ -3784,13 +3945,13 @@ class Component {
|
|
|
3784
3945
|
if (typeof reducer === 'function') {
|
|
3785
3946
|
returnStream$ = filtered$.map(action => {
|
|
3786
3947
|
const next = (type, data, delay=10) => {
|
|
3787
|
-
if (typeof delay !== 'number') throw new Error(`[${
|
|
3948
|
+
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.`)
|
|
3788
3949
|
// put the "next" action request at the end of the event loop so the "current" action completes first
|
|
3789
3950
|
setTimeout(() => {
|
|
3790
3951
|
// push the "next" action request into the action$ stream
|
|
3791
3952
|
rootAction$.shamefullySendNext({ type, data });
|
|
3792
3953
|
}, delay);
|
|
3793
|
-
this.log(`<${
|
|
3954
|
+
this.log(`<${name}> Triggered a next() action: <${type}> ${delay}ms delay`, true);
|
|
3794
3955
|
};
|
|
3795
3956
|
|
|
3796
3957
|
const props = { ...this.currentProps, children: this.currentChildren, context: this.currentContext };
|
|
@@ -3802,7 +3963,7 @@ class Component {
|
|
|
3802
3963
|
const enhancedState = this.addCalculated(_state);
|
|
3803
3964
|
props.state = enhancedState;
|
|
3804
3965
|
const newState = reducer(enhancedState, data, next, props);
|
|
3805
|
-
if (newState
|
|
3966
|
+
if (newState === ABORT) return _state
|
|
3806
3967
|
return this.cleanupCalculated(newState)
|
|
3807
3968
|
}
|
|
3808
3969
|
} else {
|
|
@@ -3811,13 +3972,13 @@ class Component {
|
|
|
3811
3972
|
const reduced = reducer(enhancedState, data, next, props);
|
|
3812
3973
|
const type = typeof reduced;
|
|
3813
3974
|
if (isObj(reduced) || ['string', 'number', 'boolean', 'function'].includes(type)) return reduced
|
|
3814
|
-
if (type
|
|
3815
|
-
console.warn(`'undefined' value sent to ${
|
|
3975
|
+
if (type === 'undefined') {
|
|
3976
|
+
console.warn(`[${this.name}] 'undefined' value sent to ${name}`);
|
|
3816
3977
|
return reduced
|
|
3817
3978
|
}
|
|
3818
|
-
throw new Error(`Invalid reducer type for ${
|
|
3979
|
+
throw new Error(`[${this.name}] Invalid reducer type for action '${name}': ${type}`)
|
|
3819
3980
|
}
|
|
3820
|
-
}).filter(result => result
|
|
3981
|
+
}).filter(result => result !== ABORT);
|
|
3821
3982
|
} else if (reducer === undefined || reducer === true) {
|
|
3822
3983
|
returnStream$ = filtered$.map(({data}) => data);
|
|
3823
3984
|
} else {
|
|
@@ -3838,7 +3999,7 @@ class Component {
|
|
|
3838
3999
|
if (state === lastState) {
|
|
3839
4000
|
return lastResult
|
|
3840
4001
|
}
|
|
3841
|
-
if (!isObj(this.calculated)) throw new Error(`'calculated' parameter must be an object mapping calculated state field
|
|
4002
|
+
if (!isObj(this.calculated)) throw new Error(`[${this.name}] 'calculated' parameter must be an object mapping calculated state field names to functions`)
|
|
3842
4003
|
|
|
3843
4004
|
const calculated = this.getCalculatedValues(state);
|
|
3844
4005
|
if (!calculated) {
|
|
@@ -3857,19 +4018,55 @@ class Component {
|
|
|
3857
4018
|
}
|
|
3858
4019
|
|
|
3859
4020
|
getCalculatedValues(state) {
|
|
3860
|
-
|
|
3861
|
-
if (entries.length === 0) {
|
|
4021
|
+
if (!this._calculatedOrder || this._calculatedOrder.length === 0) {
|
|
3862
4022
|
return
|
|
3863
4023
|
}
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
4024
|
+
|
|
4025
|
+
const mergedState = { ...state };
|
|
4026
|
+
const computedSoFar = {};
|
|
4027
|
+
|
|
4028
|
+
for (const [field, { fn, deps }] of this._calculatedOrder) {
|
|
4029
|
+
if (deps !== null && this._calculatedFieldCache) {
|
|
4030
|
+
const cache = this._calculatedFieldCache[field];
|
|
4031
|
+
const currentDepValues = deps.map(d => mergedState[d]);
|
|
4032
|
+
|
|
4033
|
+
if (cache.lastDepValues !== undefined) {
|
|
4034
|
+
let unchanged = true;
|
|
4035
|
+
for (let i = 0; i < currentDepValues.length; i++) {
|
|
4036
|
+
if (currentDepValues[i] !== cache.lastDepValues[i]) {
|
|
4037
|
+
unchanged = false;
|
|
4038
|
+
break
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
if (unchanged) {
|
|
4042
|
+
computedSoFar[field] = cache.lastResult;
|
|
4043
|
+
mergedState[field] = cache.lastResult;
|
|
4044
|
+
continue
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
|
|
4048
|
+
try {
|
|
4049
|
+
const result = fn(mergedState);
|
|
4050
|
+
cache.lastDepValues = currentDepValues;
|
|
4051
|
+
cache.lastResult = result;
|
|
4052
|
+
computedSoFar[field] = result;
|
|
4053
|
+
mergedState[field] = result;
|
|
4054
|
+
} catch (e) {
|
|
4055
|
+
console.warn(`Calculated field '${field}' threw an error during calculation: ${e.message}`);
|
|
4056
|
+
}
|
|
4057
|
+
} else {
|
|
4058
|
+
// No deps declared — always recompute
|
|
4059
|
+
try {
|
|
4060
|
+
const result = fn(mergedState);
|
|
4061
|
+
computedSoFar[field] = result;
|
|
4062
|
+
mergedState[field] = result;
|
|
4063
|
+
} catch (e) {
|
|
4064
|
+
console.warn(`Calculated field '${field}' threw an error during calculation: ${e.message}`);
|
|
4065
|
+
}
|
|
3870
4066
|
}
|
|
3871
|
-
|
|
3872
|
-
|
|
4067
|
+
}
|
|
4068
|
+
|
|
4069
|
+
return computedSoFar
|
|
3873
4070
|
}
|
|
3874
4071
|
|
|
3875
4072
|
cleanupCalculated(incomingState) {
|
|
@@ -4017,7 +4214,7 @@ class Component {
|
|
|
4017
4214
|
this.newChildSources(childSources);
|
|
4018
4215
|
|
|
4019
4216
|
|
|
4020
|
-
if (newInstanceCount > 0) this.log(`New sub components instantiated: ${
|
|
4217
|
+
if (newInstanceCount > 0) this.log(`New sub components instantiated: ${newInstanceCount}`, true);
|
|
4021
4218
|
|
|
4022
4219
|
return newComponents
|
|
4023
4220
|
}, {})
|
|
@@ -4083,7 +4280,7 @@ class Component {
|
|
|
4083
4280
|
} else if (this.components[collectionOf]) {
|
|
4084
4281
|
factory = this.components[collectionOf];
|
|
4085
4282
|
} else {
|
|
4086
|
-
throw new Error(`[${this.name}] Invalid 'of'
|
|
4283
|
+
throw new Error(`[${this.name}] Invalid 'of' property in collection: ${collectionOf}`)
|
|
4087
4284
|
}
|
|
4088
4285
|
|
|
4089
4286
|
const fieldLense = {
|
|
@@ -4091,7 +4288,7 @@ class Component {
|
|
|
4091
4288
|
if (!Array.isArray(state[stateField])) return []
|
|
4092
4289
|
const items = state[stateField];
|
|
4093
4290
|
const filtered = typeof arrayOperators.filter === 'function' ? items.filter(arrayOperators.filter) : items;
|
|
4094
|
-
const sorted = typeof arrayOperators.sort ? filtered.sort(arrayOperators.sort) : filtered;
|
|
4291
|
+
const sorted = typeof arrayOperators.sort === 'function' ? filtered.sort(arrayOperators.sort) : filtered;
|
|
4095
4292
|
const mapped = sorted.map((item, index) => {
|
|
4096
4293
|
return (isObj(item)) ? { ...item, [idField]: item[idField] || index } : { value: item, [idField]: index }
|
|
4097
4294
|
});
|
|
@@ -4100,7 +4297,7 @@ class Component {
|
|
|
4100
4297
|
},
|
|
4101
4298
|
set: (oldState, newState) => {
|
|
4102
4299
|
if (this.calculated && stateField in this.calculated) {
|
|
4103
|
-
console.warn(`Collection sub-component of ${
|
|
4300
|
+
console.warn(`Collection sub-component of ${this.name} attempted to update state on a calculated field '${stateField}': Update ignored`);
|
|
4104
4301
|
return oldState
|
|
4105
4302
|
}
|
|
4106
4303
|
const updated = [];
|
|
@@ -4129,17 +4326,17 @@ class Component {
|
|
|
4129
4326
|
} else if (typeof stateField === 'string') {
|
|
4130
4327
|
if (isObj(this.currentState)) {
|
|
4131
4328
|
if(!(this.currentState && stateField in this.currentState) && !(this.calculated && stateField in this.calculated)) {
|
|
4132
|
-
console.error(`Collection component in ${
|
|
4329
|
+
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.`);
|
|
4133
4330
|
lense = undefined;
|
|
4134
4331
|
} else if (!Array.isArray(this.currentState[stateField])) {
|
|
4135
|
-
console.warn(`State property '${
|
|
4332
|
+
console.warn(`[${this.name}] State property '${stateField}' in collection component is not an array: No components will be instantiated in the collection.`);
|
|
4136
4333
|
lense = fieldLense;
|
|
4137
4334
|
} else {
|
|
4138
4335
|
lense = fieldLense;
|
|
4139
4336
|
}
|
|
4140
4337
|
} else {
|
|
4141
4338
|
if (!Array.isArray(this.currentState[stateField])) {
|
|
4142
|
-
console.warn(`State property '${
|
|
4339
|
+
console.warn(`[${this.name}] State property '${stateField}' in collection component is not an array: No components will be instantiated in the collection.`);
|
|
4143
4340
|
lense = fieldLense;
|
|
4144
4341
|
} else {
|
|
4145
4342
|
lense = fieldLense;
|
|
@@ -4147,14 +4344,14 @@ class Component {
|
|
|
4147
4344
|
}
|
|
4148
4345
|
} else if (isObj(stateField)) {
|
|
4149
4346
|
if (typeof stateField.get !== 'function') {
|
|
4150
|
-
console.error(`Collection component in ${
|
|
4347
|
+
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.`);
|
|
4151
4348
|
lense = undefined;
|
|
4152
4349
|
} else {
|
|
4153
4350
|
lense = {
|
|
4154
4351
|
get: (state) => {
|
|
4155
4352
|
const newState = stateField.get(state);
|
|
4156
4353
|
if (!Array.isArray(newState)) {
|
|
4157
|
-
console.warn(`State getter function in collection component of ${
|
|
4354
|
+
console.warn(`State getter function in collection component of ${this.name} did not return an array: No components will be instantiated in the collection. Returned value:`, newState);
|
|
4158
4355
|
return []
|
|
4159
4356
|
}
|
|
4160
4357
|
return newState
|
|
@@ -4163,14 +4360,14 @@ class Component {
|
|
|
4163
4360
|
};
|
|
4164
4361
|
}
|
|
4165
4362
|
} else {
|
|
4166
|
-
console.error(`Collection component in ${
|
|
4363
|
+
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.`);
|
|
4167
4364
|
lense = undefined;
|
|
4168
4365
|
}
|
|
4169
4366
|
|
|
4170
|
-
const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$, PARENT: null };
|
|
4367
|
+
const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$, PARENT: null, __parentComponentNumber: this._componentNumber };
|
|
4171
4368
|
const sink$ = collection(factory, lense, { container: null })(sources);
|
|
4172
4369
|
if (!isObj(sink$)) {
|
|
4173
|
-
throw new Error(
|
|
4370
|
+
throw new Error(`[${this.name}] Invalid sinks returned from component factory of collection element`)
|
|
4174
4371
|
}
|
|
4175
4372
|
return sink$
|
|
4176
4373
|
}
|
|
@@ -4192,7 +4389,7 @@ class Component {
|
|
|
4192
4389
|
get: state => state[stateField],
|
|
4193
4390
|
set: (oldState, newState) => {
|
|
4194
4391
|
if (this.calculated && stateField in this.calculated) {
|
|
4195
|
-
console.warn(`Switchable sub-component of ${
|
|
4392
|
+
console.warn(`Switchable sub-component of ${this.name} attempted to update state on a calculated field '${stateField}': Update ignored`);
|
|
4196
4393
|
return oldState
|
|
4197
4394
|
}
|
|
4198
4395
|
if (!isObj(newState) || Array.isArray(newState)) return { ...oldState, [stateField]: newState }
|
|
@@ -4211,13 +4408,13 @@ class Component {
|
|
|
4211
4408
|
lense = fieldLense;
|
|
4212
4409
|
} else if (isObj(stateField)) {
|
|
4213
4410
|
if (typeof stateField.get !== 'function') {
|
|
4214
|
-
console.error(`Switchable component in ${
|
|
4411
|
+
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.`);
|
|
4215
4412
|
lense = baseLense;
|
|
4216
4413
|
} else {
|
|
4217
4414
|
lense = { get: stateField.get, set: stateField.set };
|
|
4218
4415
|
}
|
|
4219
4416
|
} else {
|
|
4220
|
-
console.error(`Invalid state provided to switchable sub-component of ${
|
|
4417
|
+
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.`);
|
|
4221
4418
|
lense = baseLense;
|
|
4222
4419
|
}
|
|
4223
4420
|
|
|
@@ -4233,12 +4430,12 @@ class Component {
|
|
|
4233
4430
|
switchableComponents[key] = component(options);
|
|
4234
4431
|
}
|
|
4235
4432
|
});
|
|
4236
|
-
const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context
|
|
4433
|
+
const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$, __parentComponentNumber: this._componentNumber };
|
|
4237
4434
|
|
|
4238
4435
|
const sink$ = isolate(switchable(switchableComponents, props$.map(props => props.current)), { [this.stateSourceName]: lense })(sources);
|
|
4239
4436
|
|
|
4240
4437
|
if (!isObj(sink$)) {
|
|
4241
|
-
throw new Error(
|
|
4438
|
+
throw new Error(`[${this.name}] Invalid sinks returned from component factory of switchable element`)
|
|
4242
4439
|
}
|
|
4243
4440
|
|
|
4244
4441
|
return sink$
|
|
@@ -4264,7 +4461,7 @@ class Component {
|
|
|
4264
4461
|
const factory = componentName === 'sygnal-factory' ? props.sygnalFactory : (this.components[componentName] || props.sygnalFactory);
|
|
4265
4462
|
if (!factory) {
|
|
4266
4463
|
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.`)
|
|
4267
|
-
throw new Error(`Component not found: ${
|
|
4464
|
+
throw new Error(`Component not found: ${componentName}`)
|
|
4268
4465
|
}
|
|
4269
4466
|
|
|
4270
4467
|
let lense;
|
|
@@ -4273,7 +4470,7 @@ class Component {
|
|
|
4273
4470
|
get: state => state[stateField],
|
|
4274
4471
|
set: (oldState, newState) => {
|
|
4275
4472
|
if (this.calculated && stateField in this.calculated) {
|
|
4276
|
-
console.warn(`Sub-component of ${
|
|
4473
|
+
console.warn(`Sub-component of ${this.name} attempted to update state on a calculated field '${stateField}': Update ignored`);
|
|
4277
4474
|
return oldState
|
|
4278
4475
|
}
|
|
4279
4476
|
return { ...oldState, [stateField]: newState }
|
|
@@ -4291,17 +4488,17 @@ class Component {
|
|
|
4291
4488
|
lense = fieldLense;
|
|
4292
4489
|
} else if (isObj(stateField)) {
|
|
4293
4490
|
if (typeof stateField.get !== 'function') {
|
|
4294
|
-
console.error(`Sub-component in ${
|
|
4491
|
+
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.`);
|
|
4295
4492
|
lense = baseLense;
|
|
4296
4493
|
} else {
|
|
4297
4494
|
lense = { get: stateField.get, set: stateField.set };
|
|
4298
4495
|
}
|
|
4299
4496
|
} else {
|
|
4300
|
-
console.error(`Invalid state provided to sub-component of ${
|
|
4497
|
+
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.`);
|
|
4301
4498
|
lense = baseLense;
|
|
4302
4499
|
}
|
|
4303
4500
|
|
|
4304
|
-
const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context
|
|
4501
|
+
const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$, __parentComponentNumber: this._componentNumber };
|
|
4305
4502
|
const sink$ = isolate(factory, { [this.stateSourceName]: lense })(sources);
|
|
4306
4503
|
|
|
4307
4504
|
if (!isObj(sink$)) {
|
|
@@ -4376,14 +4573,22 @@ class Component {
|
|
|
4376
4573
|
const fixedMsg = (typeof msg === 'function') ? msg : _ => msg;
|
|
4377
4574
|
if (immediate) {
|
|
4378
4575
|
if (this.debug) {
|
|
4379
|
-
|
|
4576
|
+
const text = `[${context}] ${fixedMsg(msg)}`;
|
|
4577
|
+
console.log(text);
|
|
4578
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
4579
|
+
window.__SYGNAL_DEVTOOLS__.onDebugLog(this._componentNumber, text);
|
|
4580
|
+
}
|
|
4380
4581
|
}
|
|
4381
4582
|
return
|
|
4382
4583
|
} else {
|
|
4383
4584
|
return stream => {
|
|
4384
4585
|
return stream.debug(msg => {
|
|
4385
4586
|
if (this.debug) {
|
|
4386
|
-
|
|
4587
|
+
const text = `[${context}] ${fixedMsg(msg)}`;
|
|
4588
|
+
console.log(text);
|
|
4589
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
4590
|
+
window.__SYGNAL_DEVTOOLS__.onDebugLog(this._componentNumber, text);
|
|
4591
|
+
}
|
|
4387
4592
|
}
|
|
4388
4593
|
})
|
|
4389
4594
|
}
|
|
@@ -4393,11 +4598,11 @@ class Component {
|
|
|
4393
4598
|
|
|
4394
4599
|
|
|
4395
4600
|
|
|
4396
|
-
function getComponents(currentElement, componentNames,
|
|
4601
|
+
function getComponents(currentElement, componentNames, path='r', parentId) {
|
|
4397
4602
|
if (!currentElement) return {}
|
|
4398
4603
|
|
|
4399
4604
|
if (currentElement.data?.componentsProcessed) return {}
|
|
4400
|
-
if (
|
|
4605
|
+
if (path === 'r') currentElement.data.componentsProcessed = true;
|
|
4401
4606
|
|
|
4402
4607
|
const sel = currentElement.sel;
|
|
4403
4608
|
const isCollection = sel && sel.toLowerCase() === 'collection';
|
|
@@ -4411,11 +4616,11 @@ function getComponents(currentElement, componentNames, depth=0, index=0, parentI
|
|
|
4411
4616
|
|
|
4412
4617
|
let id = parentId;
|
|
4413
4618
|
if (isComponent) {
|
|
4414
|
-
id = getComponentIdFromElement(currentElement,
|
|
4619
|
+
id = getComponentIdFromElement(currentElement, path, parentId);
|
|
4415
4620
|
if (isCollection) {
|
|
4416
4621
|
if (!props.of) throw new Error(`Collection element missing required 'component' property`)
|
|
4417
4622
|
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`)
|
|
4418
|
-
if (typeof props.of !== 'function' && !componentNames.includes(props.of)) throw new Error(`Specified component for collection not found: ${
|
|
4623
|
+
if (typeof props.of !== 'function' && !componentNames.includes(props.of)) throw new Error(`Specified component for collection not found: ${props.of}`)
|
|
4419
4624
|
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);
|
|
4420
4625
|
currentElement.data.isCollection = true;
|
|
4421
4626
|
currentElement.data.props ||= {};
|
|
@@ -4426,7 +4631,7 @@ function getComponents(currentElement, componentNames, depth=0, index=0, parentI
|
|
|
4426
4631
|
if (!switchableComponents.every(comp => typeof comp === 'function')) throw new Error(`One or more components provided to switchable element is not a valid component factory`)
|
|
4427
4632
|
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`)
|
|
4428
4633
|
const switchableComponentNames = Object.keys(props.of);
|
|
4429
|
-
if (!switchableComponentNames.includes(props.current)) throw new Error(`Component '${
|
|
4634
|
+
if (!switchableComponentNames.includes(props.current)) throw new Error(`Component '${props.current}' not found in switchable element`)
|
|
4430
4635
|
currentElement.data.isSwitchable = true;
|
|
4431
4636
|
} else ;
|
|
4432
4637
|
if (typeof props.key === 'undefined') currentElement.data.props.key = id;
|
|
@@ -4434,7 +4639,7 @@ function getComponents(currentElement, componentNames, depth=0, index=0, parentI
|
|
|
4434
4639
|
}
|
|
4435
4640
|
|
|
4436
4641
|
if (children.length > 0) {
|
|
4437
|
-
children.map((child, i) => getComponents(child, componentNames,
|
|
4642
|
+
children.map((child, i) => getComponents(child, componentNames, `${path}.${i}`, id))
|
|
4438
4643
|
.forEach((child) => {
|
|
4439
4644
|
Object.entries(child).forEach(([id, el]) => found[id] = el);
|
|
4440
4645
|
});
|
|
@@ -4443,10 +4648,10 @@ function getComponents(currentElement, componentNames, depth=0, index=0, parentI
|
|
|
4443
4648
|
return found
|
|
4444
4649
|
}
|
|
4445
4650
|
|
|
4446
|
-
function injectComponents(currentElement, components, componentNames,
|
|
4651
|
+
function injectComponents(currentElement, components, componentNames, path='r', parentId) {
|
|
4447
4652
|
if (!currentElement) return
|
|
4448
4653
|
if (currentElement.data?.componentsInjected) return currentElement
|
|
4449
|
-
if (
|
|
4654
|
+
if (path === 'r' && currentElement.data) currentElement.data.componentsInjected = true;
|
|
4450
4655
|
|
|
4451
4656
|
|
|
4452
4657
|
const sel = currentElement.sel || 'NO SELECTOR';
|
|
@@ -4458,7 +4663,7 @@ function injectComponents(currentElement, components, componentNames, depth=0, i
|
|
|
4458
4663
|
|
|
4459
4664
|
let id = parentId;
|
|
4460
4665
|
if (isComponent) {
|
|
4461
|
-
id = getComponentIdFromElement(currentElement,
|
|
4666
|
+
id = getComponentIdFromElement(currentElement, path, parentId);
|
|
4462
4667
|
const component = components[id];
|
|
4463
4668
|
if (isCollection) {
|
|
4464
4669
|
currentElement.sel = 'div';
|
|
@@ -4470,21 +4675,20 @@ function injectComponents(currentElement, components, componentNames, depth=0, i
|
|
|
4470
4675
|
return component
|
|
4471
4676
|
}
|
|
4472
4677
|
} else if (children.length > 0) {
|
|
4473
|
-
currentElement.children = children.map((child, i) => injectComponents(child, components, componentNames,
|
|
4678
|
+
currentElement.children = children.map((child, i) => injectComponents(child, components, componentNames, `${path}.${i}`, id)).flat();
|
|
4474
4679
|
return currentElement
|
|
4475
4680
|
} else {
|
|
4476
4681
|
return currentElement
|
|
4477
4682
|
}
|
|
4478
4683
|
}
|
|
4479
4684
|
|
|
4480
|
-
function getComponentIdFromElement(el,
|
|
4685
|
+
function getComponentIdFromElement(el, path, parentId) {
|
|
4481
4686
|
const sel = el.sel;
|
|
4482
4687
|
const name = typeof sel === 'string' ? sel : 'functionComponent';
|
|
4483
|
-
const uid = `${depth}:${index}`;
|
|
4484
4688
|
const props = el.data?.props || {};
|
|
4485
|
-
const id = (props.id && JSON.stringify(props.id).replaceAll('"', '')) ||
|
|
4486
|
-
const parentString = parentId ? `${
|
|
4487
|
-
const fullId = `${
|
|
4689
|
+
const id = (props.id && JSON.stringify(props.id).replaceAll('"', '')) || path;
|
|
4690
|
+
const parentString = parentId ? `${parentId}|` : '';
|
|
4691
|
+
const fullId = `${parentString}${name}::${id}`;
|
|
4488
4692
|
return fullId
|
|
4489
4693
|
}
|
|
4490
4694
|
|
|
@@ -4628,7 +4832,7 @@ function sortFunctionFromProp(sortProp) {
|
|
|
4628
4832
|
} else if (isObj(sortProp)) {
|
|
4629
4833
|
return __sortFunctionFromObj(sortProp)
|
|
4630
4834
|
} else {
|
|
4631
|
-
console.error('Invalid sort option (ignoring):',
|
|
4835
|
+
console.error('Invalid sort option (ignoring):', sortProp);
|
|
4632
4836
|
return undefined
|
|
4633
4837
|
}
|
|
4634
4838
|
}
|
|
@@ -4645,11 +4849,11 @@ function driverFromAsync(promiseReturningFunction, opts = {}) {
|
|
|
4645
4849
|
const functionName = promiseReturningFunction.name || '[anonymous function]';
|
|
4646
4850
|
const functionArgsType = typeof functionArgs;
|
|
4647
4851
|
if (functionArgsType !== 'string' && functionArgsType !== 'function' && !(Array.isArray(functionArgs) && functionArgs.every((arg) => typeof arg === 'string'))) {
|
|
4648
|
-
throw new Error(`The 'args' option for driverFromAsync(${
|
|
4852
|
+
throw new Error(`The 'args' option for driverFromAsync(${functionName}) must be a string, array of strings, or a function. Received ${functionArgsType}`)
|
|
4649
4853
|
}
|
|
4650
4854
|
|
|
4651
4855
|
if (typeof selectorProperty !== 'string') {
|
|
4652
|
-
throw new Error(`The 'selector' option for driverFromAsync(${
|
|
4856
|
+
throw new Error(`The 'selector' option for driverFromAsync(${functionName}) must be a string. Received ${typeof selectorProperty}`)
|
|
4653
4857
|
}
|
|
4654
4858
|
|
|
4655
4859
|
return (fromApp$) => {
|
|
@@ -4678,7 +4882,7 @@ function driverFromAsync(promiseReturningFunction, opts = {}) {
|
|
|
4678
4882
|
argArr = functionArgs.map((arg) => preProcessed[arg]);
|
|
4679
4883
|
}
|
|
4680
4884
|
}
|
|
4681
|
-
const errMsg = `Error in driver created using driverFromAsync(${
|
|
4885
|
+
const errMsg = `Error in driver created using driverFromAsync(${functionName})`;
|
|
4682
4886
|
promiseReturningFunction(...argArr)
|
|
4683
4887
|
.then((innerVal) => {
|
|
4684
4888
|
const constructReply = (rawVal) => {
|
|
@@ -4688,7 +4892,7 @@ function driverFromAsync(promiseReturningFunction, opts = {}) {
|
|
|
4688
4892
|
if (typeof outgoing === 'object' && outgoing !== null) {
|
|
4689
4893
|
outgoing[selectorProperty] = incoming[selectorProperty];
|
|
4690
4894
|
} else {
|
|
4691
|
-
console.warn(`The 'return' option for driverFromAsync(${
|
|
4895
|
+
console.warn(`The 'return' option for driverFromAsync(${functionName}) was not set, but the promise returned an non-object. The result will be returned as-is, but the '${selectorProperty}' property will not be set, so will not be filtered by the 'select' method of the driver.`);
|
|
4692
4896
|
}
|
|
4693
4897
|
} else if (typeof returnProperty === 'string') {
|
|
4694
4898
|
outgoing = {
|
|
@@ -4696,7 +4900,7 @@ function driverFromAsync(promiseReturningFunction, opts = {}) {
|
|
|
4696
4900
|
[selectorProperty]: incoming[selectorProperty]
|
|
4697
4901
|
};
|
|
4698
4902
|
} else {
|
|
4699
|
-
throw new Error(`The 'return' option for driverFromAsync(${
|
|
4903
|
+
throw new Error(`The 'return' option for driverFromAsync(${functionName}) must be a string. Received ${typeof returnProperty}`)
|
|
4700
4904
|
}
|
|
4701
4905
|
return outgoing
|
|
4702
4906
|
};
|
|
@@ -4712,12 +4916,12 @@ function driverFromAsync(promiseReturningFunction, opts = {}) {
|
|
|
4712
4916
|
.then((innerProcessedOutgoing) => {
|
|
4713
4917
|
sendFn(constructReply(innerProcessedOutgoing));
|
|
4714
4918
|
})
|
|
4715
|
-
.catch((err) => console.error(`${
|
|
4919
|
+
.catch((err) => console.error(`${errMsg}: ${err}`));
|
|
4716
4920
|
} else {
|
|
4717
|
-
sendFn(constructReply(
|
|
4921
|
+
sendFn(constructReply(processedOutgoing));
|
|
4718
4922
|
}
|
|
4719
4923
|
})
|
|
4720
|
-
.catch((err) => console.error(`${
|
|
4924
|
+
.catch((err) => console.error(`${errMsg}: ${err}`));
|
|
4721
4925
|
} else {
|
|
4722
4926
|
const processedOutgoing = postFunction(innerVal, incoming);
|
|
4723
4927
|
if (typeof processedOutgoing.then === 'function') {
|
|
@@ -4725,19 +4929,19 @@ function driverFromAsync(promiseReturningFunction, opts = {}) {
|
|
|
4725
4929
|
.then((innerProcessedOutgoing) => {
|
|
4726
4930
|
sendFn(constructReply(innerProcessedOutgoing));
|
|
4727
4931
|
})
|
|
4728
|
-
.catch((err) => console.error(`${
|
|
4932
|
+
.catch((err) => console.error(`${errMsg}: ${err}`));
|
|
4729
4933
|
} else {
|
|
4730
4934
|
sendFn(constructReply(processedOutgoing));
|
|
4731
4935
|
}
|
|
4732
4936
|
}
|
|
4733
4937
|
})
|
|
4734
|
-
.catch((err) => console.error(`${
|
|
4938
|
+
.catch((err) => console.error(`${errMsg}: ${err}`));
|
|
4735
4939
|
},
|
|
4736
4940
|
error: (err) => {
|
|
4737
|
-
console.error(`Error
|
|
4941
|
+
console.error(`Error received from sink stream in driver created using driverFromAsync(${functionName}):`, err);
|
|
4738
4942
|
},
|
|
4739
4943
|
complete: () => {
|
|
4740
|
-
console.warn(`Unexpected completion of sink stream to driver created using driverFromAsync(${
|
|
4944
|
+
console.warn(`Unexpected completion of sink stream to driver created using driverFromAsync(${functionName})`);
|
|
4741
4945
|
}
|
|
4742
4946
|
});
|
|
4743
4947
|
|
|
@@ -4753,6 +4957,9 @@ function driverFromAsync(promiseReturningFunction, opts = {}) {
|
|
|
4753
4957
|
}
|
|
4754
4958
|
|
|
4755
4959
|
function processForm(form, options={}) {
|
|
4960
|
+
if (!form || typeof form.events !== 'function') {
|
|
4961
|
+
throw new Error('processForm: first argument must have an .events() method (e.g. DOM.select(...))')
|
|
4962
|
+
}
|
|
4756
4963
|
let { events = ['input', 'submit'], preventDefault = true } = options;
|
|
4757
4964
|
if (typeof events === 'string') events = [events];
|
|
4758
4965
|
|
|
@@ -4780,6 +4987,12 @@ function processForm(form, options={}) {
|
|
|
4780
4987
|
}
|
|
4781
4988
|
|
|
4782
4989
|
function processDrag({ draggable, dropZone } = {}, options = {}) {
|
|
4990
|
+
if (draggable && typeof draggable.events !== 'function') {
|
|
4991
|
+
throw new Error('processDrag: draggable must have an .events() method (e.g. DOM.select(...))')
|
|
4992
|
+
}
|
|
4993
|
+
if (dropZone && typeof dropZone.events !== 'function') {
|
|
4994
|
+
throw new Error('processDrag: dropZone must have an .events() method (e.g. DOM.select(...))')
|
|
4995
|
+
}
|
|
4783
4996
|
const { effectAllowed = 'move' } = options;
|
|
4784
4997
|
|
|
4785
4998
|
const dragStart$ = draggable
|
|
@@ -5008,7 +5221,8 @@ function eventBusDriver(out$) {
|
|
|
5008
5221
|
const events = new EventTarget();
|
|
5009
5222
|
|
|
5010
5223
|
out$.subscribe({
|
|
5011
|
-
next: event => events.dispatchEvent(new CustomEvent('data', { detail: event }))
|
|
5224
|
+
next: event => events.dispatchEvent(new CustomEvent('data', { detail: event })),
|
|
5225
|
+
error: err => console.error('[EVENTS driver] Error in sink stream:', err)
|
|
5012
5226
|
});
|
|
5013
5227
|
|
|
5014
5228
|
return {
|
|
@@ -5036,11 +5250,266 @@ function logDriver(out$) {
|
|
|
5036
5250
|
out$.addListener({
|
|
5037
5251
|
next: (val) => {
|
|
5038
5252
|
console.log(val);
|
|
5253
|
+
},
|
|
5254
|
+
error: (err) => {
|
|
5255
|
+
console.error('[LOG driver] Error in sink stream:', err);
|
|
5039
5256
|
}
|
|
5040
5257
|
});
|
|
5041
5258
|
}
|
|
5042
5259
|
|
|
5260
|
+
const DEVTOOLS_SOURCE = '__SYGNAL_DEVTOOLS_PAGE__';
|
|
5261
|
+
const EXTENSION_SOURCE = '__SYGNAL_DEVTOOLS_EXTENSION__';
|
|
5262
|
+
const DEFAULT_MAX_HISTORY = 200;
|
|
5263
|
+
|
|
5264
|
+
class SygnalDevTools {
|
|
5265
|
+
constructor() {
|
|
5266
|
+
this._connected = false;
|
|
5267
|
+
this._components = new Map();
|
|
5268
|
+
this._stateHistory = [];
|
|
5269
|
+
this._maxHistory = DEFAULT_MAX_HISTORY;
|
|
5270
|
+
}
|
|
5271
|
+
|
|
5272
|
+
get connected() {
|
|
5273
|
+
return this._connected && typeof window !== 'undefined'
|
|
5274
|
+
}
|
|
5275
|
+
|
|
5276
|
+
// ─── Initialization ─────────────────────────────────────────────────────────
|
|
5277
|
+
|
|
5278
|
+
init() {
|
|
5279
|
+
if (typeof window === 'undefined') return
|
|
5280
|
+
|
|
5281
|
+
window.__SYGNAL_DEVTOOLS__ = this;
|
|
5282
|
+
|
|
5283
|
+
window.addEventListener('message', (event) => {
|
|
5284
|
+
if (event.source !== window) return
|
|
5285
|
+
if (event.data?.source === EXTENSION_SOURCE) {
|
|
5286
|
+
this._handleExtensionMessage(event.data);
|
|
5287
|
+
}
|
|
5288
|
+
});
|
|
5289
|
+
}
|
|
5290
|
+
|
|
5291
|
+
_handleExtensionMessage(msg) {
|
|
5292
|
+
switch (msg.type) {
|
|
5293
|
+
case 'CONNECT':
|
|
5294
|
+
this._connected = true;
|
|
5295
|
+
if (msg.payload?.maxHistory) this._maxHistory = msg.payload.maxHistory;
|
|
5296
|
+
this._sendFullTree();
|
|
5297
|
+
break
|
|
5298
|
+
case 'DISCONNECT':
|
|
5299
|
+
this._connected = false;
|
|
5300
|
+
break
|
|
5301
|
+
case 'SET_DEBUG':
|
|
5302
|
+
this._setDebug(msg.payload);
|
|
5303
|
+
break
|
|
5304
|
+
case 'TIME_TRAVEL':
|
|
5305
|
+
this._timeTravel(msg.payload);
|
|
5306
|
+
break
|
|
5307
|
+
case 'GET_STATE':
|
|
5308
|
+
this._sendComponentState(msg.payload.componentId);
|
|
5309
|
+
break
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
|
|
5313
|
+
// ─── Hooks (called from component.js) ────────────────────────────────────────
|
|
5314
|
+
|
|
5315
|
+
onComponentCreated(componentNumber, name, instance) {
|
|
5316
|
+
const meta = {
|
|
5317
|
+
id: componentNumber,
|
|
5318
|
+
name: name,
|
|
5319
|
+
isSubComponent: instance.isSubComponent,
|
|
5320
|
+
hasModel: !!instance.model,
|
|
5321
|
+
hasIntent: !!instance.intent,
|
|
5322
|
+
hasContext: !!instance.context,
|
|
5323
|
+
hasCalculated: !!instance.calculated,
|
|
5324
|
+
components: Object.keys(instance.components || {}),
|
|
5325
|
+
parentId: null,
|
|
5326
|
+
children: [],
|
|
5327
|
+
debug: instance._debug,
|
|
5328
|
+
createdAt: Date.now(),
|
|
5329
|
+
_instanceRef: new WeakRef(instance),
|
|
5330
|
+
};
|
|
5331
|
+
this._components.set(componentNumber, meta);
|
|
5332
|
+
|
|
5333
|
+
if (!this.connected) return
|
|
5334
|
+
this._post('COMPONENT_CREATED', this._serializeMeta(meta));
|
|
5335
|
+
}
|
|
5336
|
+
|
|
5337
|
+
onStateChanged(componentNumber, name, state) {
|
|
5338
|
+
if (!this.connected) return
|
|
5339
|
+
|
|
5340
|
+
const entry = {
|
|
5341
|
+
componentId: componentNumber,
|
|
5342
|
+
componentName: name,
|
|
5343
|
+
timestamp: Date.now(),
|
|
5344
|
+
state: this._safeClone(state),
|
|
5345
|
+
};
|
|
5346
|
+
|
|
5347
|
+
this._stateHistory.push(entry);
|
|
5348
|
+
if (this._stateHistory.length > this._maxHistory) {
|
|
5349
|
+
this._stateHistory.shift();
|
|
5350
|
+
}
|
|
5351
|
+
|
|
5352
|
+
this._post('STATE_CHANGED', {
|
|
5353
|
+
componentId: componentNumber,
|
|
5354
|
+
componentName: name,
|
|
5355
|
+
state: entry.state,
|
|
5356
|
+
historyIndex: this._stateHistory.length - 1,
|
|
5357
|
+
});
|
|
5358
|
+
}
|
|
5359
|
+
|
|
5360
|
+
onActionDispatched(componentNumber, name, actionType, data) {
|
|
5361
|
+
if (!this.connected) return
|
|
5362
|
+
this._post('ACTION_DISPATCHED', {
|
|
5363
|
+
componentId: componentNumber,
|
|
5364
|
+
componentName: name,
|
|
5365
|
+
actionType: actionType,
|
|
5366
|
+
data: this._safeClone(data),
|
|
5367
|
+
timestamp: Date.now(),
|
|
5368
|
+
});
|
|
5369
|
+
}
|
|
5370
|
+
|
|
5371
|
+
onSubComponentRegistered(parentNumber, childNumber) {
|
|
5372
|
+
const parent = this._components.get(parentNumber);
|
|
5373
|
+
const child = this._components.get(childNumber);
|
|
5374
|
+
if (parent && child) {
|
|
5375
|
+
child.parentId = parentNumber;
|
|
5376
|
+
if (!parent.children.includes(childNumber)) {
|
|
5377
|
+
parent.children.push(childNumber);
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
|
|
5381
|
+
if (!this.connected) return
|
|
5382
|
+
this._post('TREE_UPDATED', {
|
|
5383
|
+
parentId: parentNumber,
|
|
5384
|
+
childId: childNumber,
|
|
5385
|
+
});
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5388
|
+
onContextChanged(componentNumber, name, context) {
|
|
5389
|
+
if (!this.connected) return
|
|
5390
|
+
this._post('CONTEXT_CHANGED', {
|
|
5391
|
+
componentId: componentNumber,
|
|
5392
|
+
componentName: name,
|
|
5393
|
+
context: this._safeClone(context),
|
|
5394
|
+
});
|
|
5395
|
+
}
|
|
5396
|
+
|
|
5397
|
+
onDebugLog(componentNumber, message) {
|
|
5398
|
+
if (!this.connected) return
|
|
5399
|
+
this._post('DEBUG_LOG', {
|
|
5400
|
+
componentId: componentNumber,
|
|
5401
|
+
message: message,
|
|
5402
|
+
timestamp: Date.now(),
|
|
5403
|
+
});
|
|
5404
|
+
}
|
|
5405
|
+
|
|
5406
|
+
// ─── Commands (from extension to page) ───────────────────────────────────────
|
|
5407
|
+
|
|
5408
|
+
_setDebug({ componentId, enabled }) {
|
|
5409
|
+
if (typeof componentId === 'undefined' || componentId === null) {
|
|
5410
|
+
if (typeof window !== 'undefined') window.SYGNAL_DEBUG = enabled ? 'true' : false;
|
|
5411
|
+
this._post('DEBUG_TOGGLED', { global: true, enabled });
|
|
5412
|
+
return
|
|
5413
|
+
}
|
|
5414
|
+
|
|
5415
|
+
const meta = this._components.get(componentId);
|
|
5416
|
+
if (meta && meta._instanceRef) {
|
|
5417
|
+
const instance = meta._instanceRef.deref();
|
|
5418
|
+
if (instance) {
|
|
5419
|
+
instance._debug = enabled;
|
|
5420
|
+
meta.debug = enabled;
|
|
5421
|
+
this._post('DEBUG_TOGGLED', { componentId, enabled });
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
|
|
5426
|
+
_timeTravel({ historyIndex }) {
|
|
5427
|
+
const entry = this._stateHistory[historyIndex];
|
|
5428
|
+
if (!entry) return
|
|
5429
|
+
|
|
5430
|
+
const app = typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS_APP__;
|
|
5431
|
+
if (app?.sinks?.STATE?.shamefullySendNext) {
|
|
5432
|
+
app.sinks.STATE.shamefullySendNext(() => ({ ...entry.state }));
|
|
5433
|
+
this._post('TIME_TRAVEL_APPLIED', {
|
|
5434
|
+
historyIndex,
|
|
5435
|
+
state: entry.state,
|
|
5436
|
+
});
|
|
5437
|
+
}
|
|
5438
|
+
}
|
|
5439
|
+
|
|
5440
|
+
_sendComponentState(componentId) {
|
|
5441
|
+
const meta = this._components.get(componentId);
|
|
5442
|
+
if (meta && meta._instanceRef) {
|
|
5443
|
+
const instance = meta._instanceRef.deref();
|
|
5444
|
+
if (instance) {
|
|
5445
|
+
this._post('COMPONENT_STATE', {
|
|
5446
|
+
componentId,
|
|
5447
|
+
state: this._safeClone(instance.currentState),
|
|
5448
|
+
context: this._safeClone(instance.currentContext),
|
|
5449
|
+
props: this._safeClone(instance.currentProps),
|
|
5450
|
+
});
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
|
|
5455
|
+
_sendFullTree() {
|
|
5456
|
+
const tree = [];
|
|
5457
|
+
for (const [id, meta] of this._components) {
|
|
5458
|
+
const instance = meta._instanceRef?.deref();
|
|
5459
|
+
tree.push({
|
|
5460
|
+
...this._serializeMeta(meta),
|
|
5461
|
+
state: instance ? this._safeClone(instance.currentState) : null,
|
|
5462
|
+
context: instance ? this._safeClone(instance.currentContext) : null,
|
|
5463
|
+
});
|
|
5464
|
+
}
|
|
5465
|
+
this._post('FULL_TREE', {
|
|
5466
|
+
components: tree,
|
|
5467
|
+
history: this._stateHistory,
|
|
5468
|
+
});
|
|
5469
|
+
}
|
|
5470
|
+
|
|
5471
|
+
// ─── Transport ───────────────────────────────────────────────────────────────
|
|
5472
|
+
|
|
5473
|
+
_post(type, payload) {
|
|
5474
|
+
if (typeof window === 'undefined') return
|
|
5475
|
+
window.postMessage({
|
|
5476
|
+
source: DEVTOOLS_SOURCE,
|
|
5477
|
+
type,
|
|
5478
|
+
payload,
|
|
5479
|
+
}, '*');
|
|
5480
|
+
}
|
|
5481
|
+
|
|
5482
|
+
_safeClone(obj) {
|
|
5483
|
+
if (obj === undefined || obj === null) return obj
|
|
5484
|
+
try {
|
|
5485
|
+
return JSON.parse(JSON.stringify(obj))
|
|
5486
|
+
} catch (e) {
|
|
5487
|
+
return '[unserializable]'
|
|
5488
|
+
}
|
|
5489
|
+
}
|
|
5490
|
+
|
|
5491
|
+
_serializeMeta(meta) {
|
|
5492
|
+
const { _instanceRef, ...rest } = meta;
|
|
5493
|
+
return rest
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
|
|
5497
|
+
// ─── Singleton ────────────────────────────────────────────────────────────────
|
|
5498
|
+
|
|
5499
|
+
let instance = null;
|
|
5500
|
+
|
|
5501
|
+
function getDevTools() {
|
|
5502
|
+
if (!instance) instance = new SygnalDevTools();
|
|
5503
|
+
return instance
|
|
5504
|
+
}
|
|
5505
|
+
|
|
5043
5506
|
function run(app, drivers={}, options={}) {
|
|
5507
|
+
// Initialize DevTools instrumentation bridge early (before component creation)
|
|
5508
|
+
if (typeof window !== 'undefined') {
|
|
5509
|
+
const dt = getDevTools();
|
|
5510
|
+
dt.init();
|
|
5511
|
+
}
|
|
5512
|
+
|
|
5044
5513
|
const { mountPoint='#root', fragments=true, useDefaultDrivers=true } = options;
|
|
5045
5514
|
if (!app.isSygnalComponent) {
|
|
5046
5515
|
const name = app.name || app.componentName || app.label || "FUNCTIONAL_COMPONENT";
|
|
@@ -5090,6 +5559,11 @@ function run(app, drivers={}, options={}) {
|
|
|
5090
5559
|
|
|
5091
5560
|
const exposed = { sources, sinks, dispose };
|
|
5092
5561
|
|
|
5562
|
+
// Store app reference for time-travel
|
|
5563
|
+
if (typeof window !== 'undefined') {
|
|
5564
|
+
window.__SYGNAL_DEVTOOLS_APP__ = exposed;
|
|
5565
|
+
}
|
|
5566
|
+
|
|
5093
5567
|
const swapToComponent = (newComponent, state) => {
|
|
5094
5568
|
const persistedState = (typeof window !== 'undefined') ? window.__SYGNAL_HMR_PERSISTED_STATE : undefined;
|
|
5095
5569
|
const fallbackState = typeof persistedState !== 'undefined' ? persistedState : app.initialState;
|
|
@@ -5604,6 +6078,7 @@ exports.driverFromAsync = driverFromAsync;
|
|
|
5604
6078
|
exports.dropRepeats = _default$5;
|
|
5605
6079
|
exports.enableHMR = enableHMR;
|
|
5606
6080
|
exports.exactState = exactState;
|
|
6081
|
+
exports.getDevTools = getDevTools;
|
|
5607
6082
|
exports.makeDragDriver = makeDragDriver;
|
|
5608
6083
|
exports.processDrag = processDrag;
|
|
5609
6084
|
exports.processForm = processForm;
|