sygnal 5.2.0 → 5.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/README.md +21 -2
- package/dist/astro/client.cjs.js +271 -14
- package/dist/astro/client.mjs +271 -14
- package/dist/astro/server.cjs.js +18 -0
- package/dist/astro/server.mjs +18 -0
- package/dist/index.cjs.js +543 -17
- package/dist/index.d.ts +63 -0
- package/dist/index.esm.js +538 -18
- package/dist/sygnal.min.js +1 -1
- package/dist/vike/+config.cjs.js +5 -1
- package/dist/vike/+config.js +5 -1
- package/dist/vike/ClientOnly.cjs.js +34 -0
- package/dist/vike/ClientOnly.mjs +32 -0
- package/dist/vike/onRenderClient.cjs.js +292 -35
- package/dist/vike/onRenderClient.mjs +292 -35
- package/dist/vike/onRenderHtml.cjs.js +71 -34
- package/dist/vike/onRenderHtml.mjs +71 -34
- package/dist/vite/plugin.cjs.js +6 -4
- package/dist/vite/plugin.mjs +6 -4
- package/package.json +5 -1
- package/src/component.ts +63 -6
- package/src/extra/command.ts +13 -2
- package/src/extra/devtools.ts +221 -7
- package/src/extra/dragDriver.ts +59 -5
- package/src/extra/eventDriver.ts +6 -2
- package/src/extra/pwa.ts +179 -0
- package/src/extra/reducers.ts +64 -0
- package/src/extra/ssr.ts +19 -0
- package/src/index.d.ts +63 -0
- package/src/index.ts +2 -0
- package/src/vike/+config.ts +5 -1
- package/src/vike/ClientOnly.ts +10 -0
- package/src/vike/onRenderClient.ts +319 -36
- package/src/vike/onRenderHtml.ts +77 -33
- package/src/vike/types.ts +2 -0
- package/src/vite/plugin.ts +6 -4
package/dist/astro/client.mjs
CHANGED
|
@@ -5027,7 +5027,12 @@ function makeDOMDriver(container, options = {}) {
|
|
|
5027
5027
|
function eventBusDriver(out$) {
|
|
5028
5028
|
const events = new EventTarget();
|
|
5029
5029
|
out$.subscribe({
|
|
5030
|
-
next: (event) =>
|
|
5030
|
+
next: (event) => {
|
|
5031
|
+
events.dispatchEvent(new CustomEvent('data', { detail: event }));
|
|
5032
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
5033
|
+
window.__SYGNAL_DEVTOOLS__.onBusEvent(event);
|
|
5034
|
+
}
|
|
5035
|
+
},
|
|
5031
5036
|
error: (err) => console.error('[EVENTS driver] Error in sink stream:', err),
|
|
5032
5037
|
});
|
|
5033
5038
|
return {
|
|
@@ -5530,7 +5535,7 @@ function component(opts) {
|
|
|
5530
5535
|
return returnFunction;
|
|
5531
5536
|
}
|
|
5532
5537
|
class Component {
|
|
5533
|
-
constructor({ name = 'NO NAME', sources, intent, model, hmrActions, context, response, view, peers = {}, components = {}, initialState, calculated, storeCalculatedInState = true, DOMSourceName = 'DOM', stateSourceName = 'STATE', requestSourceName = 'HTTP', onError, debug = false }) {
|
|
5538
|
+
constructor({ name = 'NO NAME', sources, intent, model, hmrActions, context, response, view, peers = {}, components = {}, initialState, calculated, storeCalculatedInState = true, DOMSourceName = 'DOM', stateSourceName = 'STATE', requestSourceName = 'HTTP', isolatedState = false, onError, debug = false }) {
|
|
5534
5539
|
if (!sources || !isObj(sources))
|
|
5535
5540
|
throw new Error(`[${name}] Missing or invalid sources`);
|
|
5536
5541
|
this._componentNumber = COMPONENT_COUNT++;
|
|
@@ -5552,6 +5557,7 @@ class Component {
|
|
|
5552
5557
|
this.requestSourceName = requestSourceName;
|
|
5553
5558
|
this.sourceNames = Object.keys(sources);
|
|
5554
5559
|
this.onError = onError;
|
|
5560
|
+
this.isolatedState = isolatedState;
|
|
5555
5561
|
this._debug = debug;
|
|
5556
5562
|
// Warn if calculated fields shadow base state keys
|
|
5557
5563
|
if (this.calculated && this.initialState
|
|
@@ -5680,6 +5686,9 @@ class Component {
|
|
|
5680
5686
|
this.sources.props$ = props$.map((val) => {
|
|
5681
5687
|
const { sygnalFactory, sygnalOptions, ...sanitizedProps } = val;
|
|
5682
5688
|
this.currentProps = sanitizedProps;
|
|
5689
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
5690
|
+
window.__SYGNAL_DEVTOOLS__.onPropsChanged(this._componentNumber, this.name, sanitizedProps);
|
|
5691
|
+
}
|
|
5683
5692
|
return val;
|
|
5684
5693
|
});
|
|
5685
5694
|
}
|
|
@@ -5751,6 +5760,9 @@ class Component {
|
|
|
5751
5760
|
}
|
|
5752
5761
|
}
|
|
5753
5762
|
dispose() {
|
|
5763
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
5764
|
+
window.__SYGNAL_DEVTOOLS__.onComponentDisposed(this._componentNumber, this.name);
|
|
5765
|
+
}
|
|
5754
5766
|
// Fire the DISPOSE built-in action so model handlers can run cleanup logic
|
|
5755
5767
|
const hasDispose = this.model && (this.model[DISPOSE_ACTION] || Object.keys(this.model).some(k => k.includes('|') && k.split('|')[0].trim() === DISPOSE_ACTION));
|
|
5756
5768
|
if (hasDispose && this.action$ && typeof this.action$.shamefullySendNext === 'function') {
|
|
@@ -5951,7 +5963,7 @@ class Component {
|
|
|
5951
5963
|
const hmrState = ENVIRONMENT?.__SYGNAL_HMR_STATE;
|
|
5952
5964
|
const effectiveInitialState = (typeof hmrState !== 'undefined') ? hmrState : this.initialState;
|
|
5953
5965
|
const initial = { type: INITIALIZE_ACTION, data: effectiveInitialState };
|
|
5954
|
-
if (this.isSubComponent && this.initialState) {
|
|
5966
|
+
if (this.isSubComponent && this.initialState && !this.isolatedState) {
|
|
5955
5967
|
console.warn(`[${this.name}] Initial state provided to sub-component. This will overwrite any state provided by the parent component.`);
|
|
5956
5968
|
}
|
|
5957
5969
|
const hasInitialState = (typeof effectiveInitialState !== 'undefined');
|
|
@@ -6132,6 +6144,7 @@ class Component {
|
|
|
6132
6144
|
.map((vdom) => processLazy(vdom, this))
|
|
6133
6145
|
.map(processPortals)
|
|
6134
6146
|
.map(processTransitions)
|
|
6147
|
+
.map(processClientOnly)
|
|
6135
6148
|
.compose(this.instantiateSubComponents.bind(this))
|
|
6136
6149
|
.filter((val) => val !== undefined)
|
|
6137
6150
|
.compose(this.renderVdom.bind(this));
|
|
@@ -6147,6 +6160,12 @@ class Component {
|
|
|
6147
6160
|
else {
|
|
6148
6161
|
acc[name] = xs.merge((this.model$[name] || xs.never()), subComponentSink$, ...(this.peers$[name] || []));
|
|
6149
6162
|
}
|
|
6163
|
+
// Stamp EVENTS sink emissions with emitter component info for devtools
|
|
6164
|
+
if (name === 'EVENTS' && acc[name]) {
|
|
6165
|
+
const _componentNumber = this._componentNumber;
|
|
6166
|
+
const _name = this.name;
|
|
6167
|
+
acc[name] = acc[name].map((ev) => ({ ...ev, __emitterId: _componentNumber, __emitterName: _name }));
|
|
6168
|
+
}
|
|
6150
6169
|
return acc;
|
|
6151
6170
|
}, {});
|
|
6152
6171
|
this.sinks[this.DOMSourceName] = this.vdom$;
|
|
@@ -6669,6 +6688,13 @@ class Component {
|
|
|
6669
6688
|
if (!isObj(sink$)) {
|
|
6670
6689
|
throw new Error(`[${this.name}] Invalid sinks returned from component factory of collection element`);
|
|
6671
6690
|
}
|
|
6691
|
+
// Notify devtools of collection mount
|
|
6692
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
6693
|
+
const itemName = typeof collectionOf === 'function'
|
|
6694
|
+
? (collectionOf.componentName || collectionOf.label || collectionOf.name || 'anonymous')
|
|
6695
|
+
: String(collectionOf);
|
|
6696
|
+
window.__SYGNAL_DEVTOOLS__.onCollectionMounted(this._componentNumber, this.name, itemName, typeof stateField === 'string' ? stateField : null);
|
|
6697
|
+
}
|
|
6672
6698
|
return sink$;
|
|
6673
6699
|
}
|
|
6674
6700
|
instantiateSwitchable(el, props$, children$) {
|
|
@@ -6807,6 +6833,7 @@ class Component {
|
|
|
6807
6833
|
for (const key of Object.keys(props)) {
|
|
6808
6834
|
const val = props[key];
|
|
6809
6835
|
if (val && val.__sygnalCommand) {
|
|
6836
|
+
val._targetComponentName = componentName;
|
|
6810
6837
|
sources.commands$ = makeCommandSource(val);
|
|
6811
6838
|
break;
|
|
6812
6839
|
}
|
|
@@ -6849,10 +6876,15 @@ class Component {
|
|
|
6849
6876
|
const wasReady = this._childReadyState[id];
|
|
6850
6877
|
this._childReadyState[id] = !!ready;
|
|
6851
6878
|
// When READY state changes, trigger a re-render
|
|
6852
|
-
if (wasReady !== !!ready
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6879
|
+
if (wasReady !== !!ready) {
|
|
6880
|
+
if (this._readyChangedListener) {
|
|
6881
|
+
setTimeout(() => {
|
|
6882
|
+
this._readyChangedListener?.next(null);
|
|
6883
|
+
}, 0);
|
|
6884
|
+
}
|
|
6885
|
+
if (typeof window !== 'undefined' && window.__SYGNAL_DEVTOOLS__?.connected) {
|
|
6886
|
+
window.__SYGNAL_DEVTOOLS__.onReadyChanged(this._componentNumber, this.name, id, !!ready);
|
|
6887
|
+
}
|
|
6856
6888
|
}
|
|
6857
6889
|
},
|
|
6858
6890
|
error: () => { },
|
|
@@ -7109,6 +7141,31 @@ function processTransitions(vnode) {
|
|
|
7109
7141
|
}
|
|
7110
7142
|
return vnode;
|
|
7111
7143
|
}
|
|
7144
|
+
function processClientOnly(vnode) {
|
|
7145
|
+
if (!vnode || !vnode.sel)
|
|
7146
|
+
return vnode;
|
|
7147
|
+
if (vnode.sel === 'clientonly') {
|
|
7148
|
+
// On the client, unwrap to children (render them normally)
|
|
7149
|
+
const children = vnode.children || [];
|
|
7150
|
+
if (children.length === 0)
|
|
7151
|
+
return { sel: 'div', data: {}, children: [] };
|
|
7152
|
+
if (children.length === 1)
|
|
7153
|
+
return processClientOnly(children[0]);
|
|
7154
|
+
// Multiple children: wrap in a div
|
|
7155
|
+
return {
|
|
7156
|
+
sel: 'div',
|
|
7157
|
+
data: {},
|
|
7158
|
+
children: children.map(processClientOnly),
|
|
7159
|
+
text: undefined,
|
|
7160
|
+
elm: undefined,
|
|
7161
|
+
key: undefined,
|
|
7162
|
+
};
|
|
7163
|
+
}
|
|
7164
|
+
if (vnode.children && vnode.children.length > 0) {
|
|
7165
|
+
vnode.children = vnode.children.map(processClientOnly);
|
|
7166
|
+
}
|
|
7167
|
+
return vnode;
|
|
7168
|
+
}
|
|
7112
7169
|
function applyTransitionHooks(vnode, name, duration) {
|
|
7113
7170
|
const existingInsert = vnode.data?.hook?.insert;
|
|
7114
7171
|
const existingRemove = vnode.data?.hook?.remove;
|
|
@@ -7463,6 +7520,12 @@ class SygnalDevTools {
|
|
|
7463
7520
|
case 'TIME_TRAVEL':
|
|
7464
7521
|
this._timeTravel(msg.payload);
|
|
7465
7522
|
break;
|
|
7523
|
+
case 'SNAPSHOT':
|
|
7524
|
+
this._takeSnapshot();
|
|
7525
|
+
break;
|
|
7526
|
+
case 'RESTORE_SNAPSHOT':
|
|
7527
|
+
this._restoreSnapshot(msg.payload);
|
|
7528
|
+
break;
|
|
7466
7529
|
case 'GET_STATE':
|
|
7467
7530
|
this._sendComponentState(msg.payload.componentId);
|
|
7468
7531
|
break;
|
|
@@ -7482,6 +7545,7 @@ class SygnalDevTools {
|
|
|
7482
7545
|
children: [],
|
|
7483
7546
|
debug: instance._debug,
|
|
7484
7547
|
createdAt: Date.now(),
|
|
7548
|
+
mviGraph: this._extractMviGraph(instance),
|
|
7485
7549
|
_instanceRef: new WeakRef(instance),
|
|
7486
7550
|
};
|
|
7487
7551
|
this._components.set(componentNumber, meta);
|
|
@@ -7506,7 +7570,6 @@ class SygnalDevTools {
|
|
|
7506
7570
|
componentId: componentNumber,
|
|
7507
7571
|
componentName: name,
|
|
7508
7572
|
state: entry.state,
|
|
7509
|
-
historyIndex: this._stateHistory.length - 1,
|
|
7510
7573
|
});
|
|
7511
7574
|
}
|
|
7512
7575
|
onActionDispatched(componentNumber, name, actionType, data) {
|
|
@@ -7543,6 +7606,76 @@ class SygnalDevTools {
|
|
|
7543
7606
|
componentId: componentNumber,
|
|
7544
7607
|
componentName: name,
|
|
7545
7608
|
context: this._safeClone(context),
|
|
7609
|
+
contextTrace: this._buildContextTrace(componentNumber, context),
|
|
7610
|
+
});
|
|
7611
|
+
}
|
|
7612
|
+
onPropsChanged(componentNumber, name, props) {
|
|
7613
|
+
if (!this.connected)
|
|
7614
|
+
return;
|
|
7615
|
+
this._post('PROPS_CHANGED', {
|
|
7616
|
+
componentId: componentNumber,
|
|
7617
|
+
componentName: name,
|
|
7618
|
+
props: this._safeClone(props),
|
|
7619
|
+
});
|
|
7620
|
+
}
|
|
7621
|
+
onBusEvent(event) {
|
|
7622
|
+
if (!this.connected)
|
|
7623
|
+
return;
|
|
7624
|
+
this._post('BUS_EVENT', {
|
|
7625
|
+
type: event.type,
|
|
7626
|
+
data: this._safeClone(event.data),
|
|
7627
|
+
componentId: event.__emitterId ?? null,
|
|
7628
|
+
componentName: event.__emitterName ?? null,
|
|
7629
|
+
timestamp: Date.now(),
|
|
7630
|
+
});
|
|
7631
|
+
}
|
|
7632
|
+
onCommandSent(type, data, targetComponentId, targetComponentName) {
|
|
7633
|
+
if (!this.connected)
|
|
7634
|
+
return;
|
|
7635
|
+
this._post('COMMAND_SENT', {
|
|
7636
|
+
type,
|
|
7637
|
+
data: this._safeClone(data),
|
|
7638
|
+
targetComponentName: targetComponentName ?? null,
|
|
7639
|
+
timestamp: Date.now(),
|
|
7640
|
+
});
|
|
7641
|
+
}
|
|
7642
|
+
onReadyChanged(parentId, parentName, childKey, ready) {
|
|
7643
|
+
if (!this.connected)
|
|
7644
|
+
return;
|
|
7645
|
+
this._post('READY_CHANGED', {
|
|
7646
|
+
parentId,
|
|
7647
|
+
parentName,
|
|
7648
|
+
childKey,
|
|
7649
|
+
ready,
|
|
7650
|
+
timestamp: Date.now(),
|
|
7651
|
+
});
|
|
7652
|
+
}
|
|
7653
|
+
onCollectionMounted(parentId, parentName, itemComponentName, stateField) {
|
|
7654
|
+
const meta = this._components.get(parentId);
|
|
7655
|
+
if (meta) {
|
|
7656
|
+
meta.collection = { itemComponent: itemComponentName, stateField };
|
|
7657
|
+
}
|
|
7658
|
+
if (!this.connected)
|
|
7659
|
+
return;
|
|
7660
|
+
this._post('COLLECTION_MOUNTED', {
|
|
7661
|
+
parentId,
|
|
7662
|
+
parentName,
|
|
7663
|
+
itemComponent: itemComponentName,
|
|
7664
|
+
stateField,
|
|
7665
|
+
});
|
|
7666
|
+
}
|
|
7667
|
+
onComponentDisposed(componentNumber, name) {
|
|
7668
|
+
const meta = this._components.get(componentNumber);
|
|
7669
|
+
if (meta) {
|
|
7670
|
+
meta.disposed = true;
|
|
7671
|
+
meta.disposedAt = Date.now();
|
|
7672
|
+
}
|
|
7673
|
+
if (!this.connected)
|
|
7674
|
+
return;
|
|
7675
|
+
this._post('COMPONENT_DISPOSED', {
|
|
7676
|
+
componentId: componentNumber,
|
|
7677
|
+
componentName: name,
|
|
7678
|
+
timestamp: Date.now(),
|
|
7546
7679
|
});
|
|
7547
7680
|
}
|
|
7548
7681
|
onDebugLog(componentNumber, message) {
|
|
@@ -7571,20 +7704,79 @@ class SygnalDevTools {
|
|
|
7571
7704
|
}
|
|
7572
7705
|
}
|
|
7573
7706
|
}
|
|
7574
|
-
_timeTravel({
|
|
7575
|
-
|
|
7576
|
-
|
|
7707
|
+
_timeTravel({ componentId, componentName, state }) {
|
|
7708
|
+
if (componentId == null || !state) {
|
|
7709
|
+
console.warn('[Sygnal DevTools] _timeTravel: missing componentId or state', { componentId, hasState: !!state });
|
|
7577
7710
|
return;
|
|
7711
|
+
}
|
|
7578
7712
|
if (typeof window === 'undefined')
|
|
7579
7713
|
return;
|
|
7714
|
+
const newState = this._safeClone(state);
|
|
7715
|
+
// Try per-component time-travel via the component's STATE sink (reducer stream)
|
|
7716
|
+
const meta = this._components.get(componentId);
|
|
7717
|
+
if (meta) {
|
|
7718
|
+
const instance = meta._instanceRef?.deref();
|
|
7719
|
+
if (!instance) {
|
|
7720
|
+
console.warn(`[Sygnal DevTools] _timeTravel: WeakRef for component #${componentId} (${componentName}) has been GC'd`);
|
|
7721
|
+
}
|
|
7722
|
+
else {
|
|
7723
|
+
// sinks[stateSourceName] is the reducer stream — push a reducer that replaces state
|
|
7724
|
+
const stateSinkName = instance.stateSourceName || 'STATE';
|
|
7725
|
+
const stateSink = instance.sinks?.[stateSinkName];
|
|
7726
|
+
if (stateSink?.shamefullySendNext) {
|
|
7727
|
+
stateSink.shamefullySendNext(() => ({ ...newState }));
|
|
7728
|
+
this._post('TIME_TRAVEL_APPLIED', {
|
|
7729
|
+
componentId,
|
|
7730
|
+
componentName,
|
|
7731
|
+
state: newState,
|
|
7732
|
+
});
|
|
7733
|
+
return;
|
|
7734
|
+
}
|
|
7735
|
+
console.warn(`[Sygnal DevTools] _timeTravel: component #${componentId} (${componentName}) has no STATE sink with shamefullySendNext. sinkName=${stateSinkName}, hasSinks=${!!instance.sinks}, sinkKeys=${instance.sinks ? Object.keys(instance.sinks).join(',') : 'none'}`);
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
else {
|
|
7739
|
+
console.warn(`[Sygnal DevTools] _timeTravel: no meta for componentId ${componentId}`);
|
|
7740
|
+
}
|
|
7741
|
+
// Fall back to root STATE sink for root-level components
|
|
7580
7742
|
const app = window.__SYGNAL_DEVTOOLS_APP__;
|
|
7581
7743
|
if (app?.sinks?.STATE?.shamefullySendNext) {
|
|
7582
|
-
app.sinks.STATE.shamefullySendNext(() => ({ ...
|
|
7744
|
+
app.sinks.STATE.shamefullySendNext(() => ({ ...newState }));
|
|
7583
7745
|
this._post('TIME_TRAVEL_APPLIED', {
|
|
7584
|
-
|
|
7585
|
-
|
|
7746
|
+
componentId,
|
|
7747
|
+
componentName,
|
|
7748
|
+
state: newState,
|
|
7586
7749
|
});
|
|
7587
7750
|
}
|
|
7751
|
+
else {
|
|
7752
|
+
console.warn(`[Sygnal DevTools] _timeTravel: no fallback root STATE sink available`);
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
_takeSnapshot() {
|
|
7756
|
+
const entries = [];
|
|
7757
|
+
for (const [id, meta] of this._components) {
|
|
7758
|
+
if (meta.disposed)
|
|
7759
|
+
continue;
|
|
7760
|
+
const instance = meta._instanceRef?.deref();
|
|
7761
|
+
if (instance?.currentState != null) {
|
|
7762
|
+
entries.push({
|
|
7763
|
+
componentId: id,
|
|
7764
|
+
componentName: meta.name,
|
|
7765
|
+
state: this._safeClone(instance.currentState),
|
|
7766
|
+
});
|
|
7767
|
+
}
|
|
7768
|
+
}
|
|
7769
|
+
this._post('SNAPSHOT_TAKEN', {
|
|
7770
|
+
entries,
|
|
7771
|
+
timestamp: Date.now(),
|
|
7772
|
+
});
|
|
7773
|
+
}
|
|
7774
|
+
_restoreSnapshot(snapshot) {
|
|
7775
|
+
if (!snapshot?.entries)
|
|
7776
|
+
return;
|
|
7777
|
+
for (const entry of snapshot.entries) {
|
|
7778
|
+
this._timeTravel(entry);
|
|
7779
|
+
}
|
|
7588
7780
|
}
|
|
7589
7781
|
_sendComponentState(componentId) {
|
|
7590
7782
|
const meta = this._components.get(componentId);
|
|
@@ -7595,11 +7787,38 @@ class SygnalDevTools {
|
|
|
7595
7787
|
componentId,
|
|
7596
7788
|
state: this._safeClone(instance.currentState),
|
|
7597
7789
|
context: this._safeClone(instance.currentContext),
|
|
7790
|
+
contextTrace: this._buildContextTrace(componentId, instance.currentContext),
|
|
7598
7791
|
props: this._safeClone(instance.currentProps),
|
|
7599
7792
|
});
|
|
7600
7793
|
}
|
|
7601
7794
|
}
|
|
7602
7795
|
}
|
|
7796
|
+
_buildContextTrace(componentId, context) {
|
|
7797
|
+
if (!context || typeof context !== 'object')
|
|
7798
|
+
return [];
|
|
7799
|
+
const trace = [];
|
|
7800
|
+
const fields = Object.keys(context);
|
|
7801
|
+
for (const field of fields) {
|
|
7802
|
+
// Walk up parent chain to find which component provides this field
|
|
7803
|
+
let currentId = componentId;
|
|
7804
|
+
let found = false;
|
|
7805
|
+
while (currentId != null) {
|
|
7806
|
+
const meta = this._components.get(currentId);
|
|
7807
|
+
if (!meta)
|
|
7808
|
+
break;
|
|
7809
|
+
if (meta.mviGraph?.contextProvides?.includes(field)) {
|
|
7810
|
+
trace.push({ field, providerId: meta.id, providerName: meta.name });
|
|
7811
|
+
found = true;
|
|
7812
|
+
break;
|
|
7813
|
+
}
|
|
7814
|
+
currentId = meta.parentId;
|
|
7815
|
+
}
|
|
7816
|
+
if (!found) {
|
|
7817
|
+
trace.push({ field, providerId: -1, providerName: 'unknown' });
|
|
7818
|
+
}
|
|
7819
|
+
}
|
|
7820
|
+
return trace;
|
|
7821
|
+
}
|
|
7603
7822
|
_sendFullTree() {
|
|
7604
7823
|
const tree = [];
|
|
7605
7824
|
for (const [, meta] of this._components) {
|
|
@@ -7634,6 +7853,44 @@ class SygnalDevTools {
|
|
|
7634
7853
|
return '[unserializable]';
|
|
7635
7854
|
}
|
|
7636
7855
|
}
|
|
7856
|
+
_extractMviGraph(instance) {
|
|
7857
|
+
if (!instance.model)
|
|
7858
|
+
return null;
|
|
7859
|
+
try {
|
|
7860
|
+
const sources = instance.sourceNames ? [...instance.sourceNames] : [];
|
|
7861
|
+
const actions = [];
|
|
7862
|
+
const model = instance.model;
|
|
7863
|
+
for (const key of Object.keys(model)) {
|
|
7864
|
+
let actionName = key;
|
|
7865
|
+
let entry = model[key];
|
|
7866
|
+
// Handle shorthand 'ACTION | SINK'
|
|
7867
|
+
if (key.includes('|')) {
|
|
7868
|
+
const parts = key.split('|').map((s) => s.trim());
|
|
7869
|
+
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
7870
|
+
actionName = parts[0];
|
|
7871
|
+
actions.push({ name: actionName, sinks: [parts[1]] });
|
|
7872
|
+
continue;
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7875
|
+
// Function → targets STATE
|
|
7876
|
+
if (typeof entry === 'function') {
|
|
7877
|
+
actions.push({ name: actionName, sinks: [instance.stateSourceName || 'STATE'] });
|
|
7878
|
+
continue;
|
|
7879
|
+
}
|
|
7880
|
+
// Object → keys are sink names
|
|
7881
|
+
if (entry && typeof entry === 'object') {
|
|
7882
|
+
actions.push({ name: actionName, sinks: Object.keys(entry) });
|
|
7883
|
+
continue;
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
const contextProvides = instance.context && typeof instance.context === 'object'
|
|
7887
|
+
? Object.keys(instance.context) : [];
|
|
7888
|
+
return { sources, actions, contextProvides };
|
|
7889
|
+
}
|
|
7890
|
+
catch (e) {
|
|
7891
|
+
return null;
|
|
7892
|
+
}
|
|
7893
|
+
}
|
|
7637
7894
|
_serializeMeta(meta) {
|
|
7638
7895
|
const { _instanceRef, ...rest } = meta;
|
|
7639
7896
|
return rest;
|
package/dist/astro/server.cjs.js
CHANGED
|
@@ -172,6 +172,24 @@ function processSSRTree(vnode, context, parentState) {
|
|
|
172
172
|
key: undefined,
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
|
+
// ClientOnly: render fallback during SSR, skip children (they need a browser)
|
|
176
|
+
if (sel === 'clientonly') {
|
|
177
|
+
const props = vnode.data?.props || {};
|
|
178
|
+
const fallback = props.fallback;
|
|
179
|
+
if (fallback) {
|
|
180
|
+
// fallback can be a VNode or a string
|
|
181
|
+
return processSSRTree(fallback, context, parentState);
|
|
182
|
+
}
|
|
183
|
+
// No fallback — render an empty placeholder div
|
|
184
|
+
return {
|
|
185
|
+
sel: 'div',
|
|
186
|
+
data: { attrs: { 'data-sygnal-clientonly': '' } },
|
|
187
|
+
children: [],
|
|
188
|
+
text: undefined,
|
|
189
|
+
elm: undefined,
|
|
190
|
+
key: undefined,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
175
193
|
// Slot: unwrap to children
|
|
176
194
|
if (sel === 'slot') {
|
|
177
195
|
const children = vnode.children || [];
|
package/dist/astro/server.mjs
CHANGED
|
@@ -168,6 +168,24 @@ function processSSRTree(vnode, context, parentState) {
|
|
|
168
168
|
key: undefined,
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
|
+
// ClientOnly: render fallback during SSR, skip children (they need a browser)
|
|
172
|
+
if (sel === 'clientonly') {
|
|
173
|
+
const props = vnode.data?.props || {};
|
|
174
|
+
const fallback = props.fallback;
|
|
175
|
+
if (fallback) {
|
|
176
|
+
// fallback can be a VNode or a string
|
|
177
|
+
return processSSRTree(fallback, context, parentState);
|
|
178
|
+
}
|
|
179
|
+
// No fallback — render an empty placeholder div
|
|
180
|
+
return {
|
|
181
|
+
sel: 'div',
|
|
182
|
+
data: { attrs: { 'data-sygnal-clientonly': '' } },
|
|
183
|
+
children: [],
|
|
184
|
+
text: undefined,
|
|
185
|
+
elm: undefined,
|
|
186
|
+
key: undefined,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
171
189
|
// Slot: unwrap to children
|
|
172
190
|
if (sel === 'slot') {
|
|
173
191
|
const children = vnode.children || [];
|