sygnal 6.6.0 → 7.0.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/CHANGELOG.md +132 -0
- package/README.md +2 -0
- package/dist/astro/index.cjs.js +32 -9
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +32 -9
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs.map +1 -1
- package/dist/devtools.cjs.js.map +1 -1
- package/dist/devtools.esm.js.map +1 -1
- package/dist/diagnostics.cjs.js +87 -4
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +86 -5
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/guide/accessibility.md +2 -0
- package/dist/guide/ai-chat.md +6 -6
- package/dist/guide/behaviors.md +1 -1
- package/dist/guide/element-commands.md +4 -2
- package/dist/guide/forms-reference.md +89 -3
- package/dist/guide/forms.md +8 -8
- package/dist/guide/http.md +77 -7
- package/dist/guide/migrating-to-7.md +314 -0
- package/dist/guide/recipes/ai-summarize.md +2 -1
- package/dist/guide/recipes/i18n.md +1 -1
- package/dist/guide/ui/tabs.md +5 -4
- package/dist/guide/ui/tooltip.md +24 -0
- package/dist/index.cjs.js +772 -318
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +198 -31
- package/dist/index.esm.js +772 -318
- package/dist/index.esm.js.map +1 -1
- package/dist/sygnal.min.js +1 -1
- package/dist/sygnal.min.js.map +1 -1
- package/dist/ui.cjs.js +96 -11
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.esm.js +96 -12
- package/dist/ui.esm.js.map +1 -1
- package/dist/vike/config/package.json +1 -1
- package/dist/vite/plugin.cjs.js +19 -9
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +19 -9
- package/dist/vite/plugin.mjs.map +1 -1
- package/llms.txt +17 -16
- package/package.json +1 -1
- package/src/astro/index.d.ts +8 -0
- package/src/astro/index.ts +18 -0
- package/src/core/actions.ts +28 -21
- package/src/core/define.ts +2 -0
- package/src/core/hooks.ts +2 -2
- package/src/core/hosts/collection.ts +4 -3
- package/src/core/instance.ts +60 -16
- package/src/core/runtime.ts +5 -1
- package/src/extra/ai/chat/behavior.ts +3 -3
- package/src/extra/ai/commandBar.ts +3 -3
- package/src/extra/ai/compose/behavior.ts +3 -3
- package/src/extra/ai/transports/shared.ts +8 -2
- package/src/extra/behaviors.ts +21 -0
- package/src/extra/diagnostics/checks/fetch.ts +21 -1
- package/src/extra/diagnostics/checks/forms.ts +4 -3
- package/src/extra/diagnostics/checks/index.ts +1 -0
- package/src/extra/diagnostics/checks/next.ts +3 -0
- package/src/extra/diagnostics/checks/public.d.ts +12 -0
- package/src/extra/diagnostics/checks/timingAudit.ts +47 -0
- package/src/extra/diagnostics/codes.ts +13 -0
- package/src/extra/fetchDriver.ts +112 -7
- package/src/extra/fetchEventSource.ts +113 -0
- package/src/extra/form.ts +68 -26
- package/src/extra/formHelpers.ts +3 -1
- package/src/extra/lens.ts +3 -2
- package/src/extra/queryCache.ts +4 -3
- package/src/extra/socketDriver.ts +10 -4
- package/src/extra/testing.ts +68 -11
- package/src/index.d.ts +197 -30
- package/src/index.ts +1 -0
- package/src/shared.ts +3 -0
- package/src/ui/dialog.ts +3 -2
- package/src/ui/tooltips.ts +92 -0
- package/src/ui.d.ts +35 -0
- package/src/ui.ts +1 -0
- package/src/vite/graph.ts +11 -3
- package/src/vite/plugin.d.ts +7 -0
- package/src/vite/plugin.ts +10 -5
package/dist/diagnostics.esm.js
CHANGED
|
@@ -281,6 +281,8 @@ const CODE_TITLES = {
|
|
|
281
281
|
SYG634: 'latest: true with a computed key',
|
|
282
282
|
// PLAN-3 5-5 (D88)
|
|
283
283
|
SYG635: 'Caching asked for without a queryCache',
|
|
284
|
+
// PLAN-11 U-1: progress requests (dev entry, fetch.ts)
|
|
285
|
+
SYG636: "progress that can't be reported as asked",
|
|
284
286
|
// PLAN-4 GS-2: element commands (reported by the dev entry, checks/elementCommands.ts)
|
|
285
287
|
SYG640: 'Element command target not found',
|
|
286
288
|
SYG641: 'Unknown element command',
|
|
@@ -340,6 +342,9 @@ const CODE_TITLES = {
|
|
|
340
342
|
// PLAN-9 X-2: static only (sygnal-check rules/syg253-catalog.js)
|
|
341
343
|
SYG253: 'Misspelled catalog declaration key',
|
|
342
344
|
SYG254: 'Catalog descriptions or options overlap',
|
|
345
|
+
SYG256: 'Output guard rejects the updated state',
|
|
346
|
+
SYG257: 'Output reads a field its action changed (7.0 timing audit)',
|
|
347
|
+
SYG454: 'State binding changed without a new component identity',
|
|
343
348
|
// PLAN-9 H: chat client tools and ask (src/extra/ai/chat/behavior.ts, compose/ask.ts, compose/render.ts)
|
|
344
349
|
SYG160: 'Result part rendered outside a card',
|
|
345
350
|
SYG684: 'Client tool shadows an agent tool',
|
|
@@ -450,6 +455,8 @@ const DEV_CODE_SEVERITY = {
|
|
|
450
455
|
SYG634: 'info',
|
|
451
456
|
// PLAN-3 5-5 (fetch.ts)
|
|
452
457
|
SYG635: 'warn',
|
|
458
|
+
// PLAN-11 U-1 (fetch.ts)
|
|
459
|
+
SYG636: 'warn',
|
|
453
460
|
// PLAN-4 GS-2 (checks/elementCommands.ts, through the core bridge; renderComponent too)
|
|
454
461
|
SYG640: 'warn',
|
|
455
462
|
SYG641: 'error',
|
|
@@ -566,6 +573,12 @@ const DEV_CODE_SEVERITY = {
|
|
|
566
573
|
// PLAN-9 X-2: static only (sygnal-check); listed so the entry knows their titles
|
|
567
574
|
SYG253: 'warn',
|
|
568
575
|
SYG254: 'info',
|
|
576
|
+
// PLAN-12: static only; proven after-state output guard contradiction.
|
|
577
|
+
SYG256: 'warn',
|
|
578
|
+
// 7.0: the opt-in timing audit (checks/next.ts; configureTimingAudit)
|
|
579
|
+
SYG257: 'info',
|
|
580
|
+
// PLAN-12: static only for legacy (6.x) binding contracts. Sygnal 7 remounts known targets.
|
|
581
|
+
SYG454: 'warn',
|
|
569
582
|
};
|
|
570
583
|
function getCodeInfo(code) {
|
|
571
584
|
const severity = CODE_SEVERITY[code];
|
|
@@ -2191,14 +2204,15 @@ const formsCheck = {
|
|
|
2191
2204
|
});
|
|
2192
2205
|
const model = Object.keys(view.model || {}).map(a => a.split('|')[0].trim()), own = ownActions(b);
|
|
2193
2206
|
const clash = own.includes(o.submit);
|
|
2194
|
-
|
|
2207
|
+
// PLAN-11 F-3: with `request`, the form sends the submit itself and `submit` is optional
|
|
2208
|
+
if (o.request ? o.submit !== undefined && (typeof o.submit != 'string' || !model.includes(o.submit) || clash) : typeof o.submit != 'string' || !model.includes(o.submit) || clash) {
|
|
2195
2209
|
devReport('SYG234', {
|
|
2196
2210
|
component,
|
|
2197
|
-
message: typeof o.submit != 'string' ? `${name}'s form '${key}' has no submit action, so a valid submit dispatches nothing`
|
|
2211
|
+
message: typeof o.submit != 'string' ? `${name}'s form '${key}' has no submit action and no request, so a valid submit dispatches nothing`
|
|
2198
2212
|
: clash ? `${name}'s form '${key}' names submit: '${o.submit}', one of the form's own actions; a valid submit dispatches the form's '${key}.${o.submit}'${model.includes(o.submit) ? `, never ${name}'s '${o.submit}' entry` : ''}`
|
|
2199
2213
|
: `${name}'s form '${key}' dispatches '${o.submit}' on a valid submit, but ${name} has no model entry '${o.submit}'`,
|
|
2200
2214
|
fix: clash ? SUBMIT_FIX(key, o.submit, own)
|
|
2201
|
-
: `
|
|
2215
|
+
: `Give the form a request, form(schema, { values, request: { url: '/api/signup' } }) (it sends the valid submit itself), or name a host action and add it to the model: submit: 'SIGN_UP', model: { SIGN_UP: { HTTP: (state, values) => ({ url, json: values, ok: '${key}.DONE', error: '${key}.ERRORS' }) } }`,
|
|
2202
2216
|
data: { key, submit: o.submit },
|
|
2203
2217
|
});
|
|
2204
2218
|
}
|
|
@@ -2623,6 +2637,13 @@ const routerCheck = {
|
|
|
2623
2637
|
const isStandardSchema = (schema) => !!schema && (typeof schema == 'object' || typeof schema == 'function') && typeof schema['~standard']?.validate == 'function';
|
|
2624
2638
|
|
|
2625
2639
|
const IDEMPOTENT = /^(GET|HEAD)$/i;
|
|
2640
|
+
// PLAN-11 U-1: fetch() init options a progress request (XMLHttpRequest) can't honour
|
|
2641
|
+
const XHR_IGNORES = ['keepalive', 'priority', 'cache', 'redirect', 'integrity', 'mode', 'referrer', 'referrerPolicy', 'window', 'duplex'];
|
|
2642
|
+
const reportProgress = (component, sink, url, why, fix) => {
|
|
2643
|
+
if (!once(`SYG636:${nameOf(component)}:${url}:${why}`))
|
|
2644
|
+
return;
|
|
2645
|
+
devReport('SYG636', { component, message: `${sink} ${url ? url + ' ' : ''}has progress, but ${why}`, fix, data: { sink, url } });
|
|
2646
|
+
};
|
|
2626
2647
|
/** instance → reply key lanes it has sent, and ok name → the other key it was sent under */
|
|
2627
2648
|
const lanes = new WeakMap();
|
|
2628
2649
|
const fetchSink = (component, name) => component?.sources?.[name]?.__sygnalStatic === 'resources';
|
|
@@ -2646,7 +2667,7 @@ function checkOne(component, sink, req, where) {
|
|
|
2646
2667
|
return;
|
|
2647
2668
|
const name = nameOf(component);
|
|
2648
2669
|
const url = typeof req.url === 'string' ? req.url : '';
|
|
2649
|
-
const method = String(req.method || (req.json !== undefined || req.body !== undefined ? 'POST' : 'GET')).toUpperCase();
|
|
2670
|
+
const method = String(req.method || (req.json !== undefined || req.body !== undefined || req.multipart !== undefined ? 'POST' : 'GET')).toUpperCase();
|
|
2650
2671
|
if ((req.cache === true || req.staleTime != null) && !IDEMPOTENT.test(method) && once(`SYG630:${name}:${method}:${url}`)) {
|
|
2651
2672
|
devReport('SYG630', {
|
|
2652
2673
|
component,
|
|
@@ -2657,6 +2678,17 @@ function checkOne(component, sink, req, where) {
|
|
|
2657
2678
|
}
|
|
2658
2679
|
if (req.cache === true || req.staleTime != null)
|
|
2659
2680
|
reportNoCache(component, sink, req.cache === true ? 'cache: true' : 'staleTime', url);
|
|
2681
|
+
if (req.progress) {
|
|
2682
|
+
if (where.includes('.resources.'))
|
|
2683
|
+
reportProgress(component, sink, url, 'resources report no progress (it is ignored)', 'Send the download as a request with progress and reply actions instead of a resource, or remove progress');
|
|
2684
|
+
else if (req.batch)
|
|
2685
|
+
reportProgress(component, sink, url, 'a batch request reports no progress (it is ignored)', 'Send the parts as separate requests with progress, or remove progress');
|
|
2686
|
+
else {
|
|
2687
|
+
const bad = XHR_IGNORES.filter(k => req.init && req.init[k] !== undefined);
|
|
2688
|
+
if (bad.length)
|
|
2689
|
+
reportProgress(component, sink, url, `it goes over XMLHttpRequest, which ignores init.${bad.join(', init.')}`, `Remove ${bad.join(', ')} from init for this request, or drop progress if the request needs ${bad.length > 1 ? 'them' : 'it'}`);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2660
2692
|
if ('validate' in req && !isStandardSchema(req.validate) && once(`SYG631:${name}:${url}`)) {
|
|
2661
2693
|
devReport('SYG631', {
|
|
2662
2694
|
component,
|
|
@@ -3757,6 +3789,52 @@ function installInspect() {
|
|
|
3757
3789
|
};
|
|
3758
3790
|
}
|
|
3759
3791
|
|
|
3792
|
+
/**
|
|
3793
|
+
* The 7.0 timing audit (SYG257, info): since 7.0 a model entry's STATE runs first and every other
|
|
3794
|
+
* sink gets the state it committed (6.x gave them the state from before the action). For code
|
|
3795
|
+
* written for 6.x, this reports each output sink that reads a top-level state field its action's
|
|
3796
|
+
* STATE changed: it gets the new value now. Fix: read `props.previousState.<field>` where the old
|
|
3797
|
+
* value is meant; a read meant to see the new value needs nothing. Opt-in, because in 7.0-native
|
|
3798
|
+
* code reading the new value is the intent. Turn it on with any of:
|
|
3799
|
+
* - configureTimingAudit(true) (this entry)
|
|
3800
|
+
* - renderComponent(C, { timingAudit: true }) (tests)
|
|
3801
|
+
* - sygnal({ diagnostics: { timingAudit: true } }) (sygnal/vite, dev)
|
|
3802
|
+
* - globalThis.__SYGNAL_TIMING_AUDIT__ = true
|
|
3803
|
+
* The flag lives on the core bridge (shared by every copy of this entry and renderComponent).
|
|
3804
|
+
* Mechanism: the dev hook layer's wrapHandler (checks/next.ts) wraps each non-STATE handler and
|
|
3805
|
+
* gives it the state through a Proxy that notes reads of changed top-level fields. Top-level
|
|
3806
|
+
* fields only (a nested change shows as its top-level field changing); once per component name,
|
|
3807
|
+
* action, sink and field. Nothing in production builds.
|
|
3808
|
+
*/
|
|
3809
|
+
/** Turn the timing audit on (true), off (false), or back to the default (globalThis.__SYGNAL_TIMING_AUDIT__). */
|
|
3810
|
+
function configureTimingAudit(on) {
|
|
3811
|
+
const core = bridge();
|
|
3812
|
+
if (core)
|
|
3813
|
+
core.timingAudit = on;
|
|
3814
|
+
}
|
|
3815
|
+
/** Whether the timing audit is on. */
|
|
3816
|
+
function isTimingAuditEnabled() {
|
|
3817
|
+
const flag = bridge()?.timingAudit;
|
|
3818
|
+
return flag === undefined ? globalThis.__SYGNAL_TIMING_AUDIT__ === true : flag === true;
|
|
3819
|
+
}
|
|
3820
|
+
/** handler `h` of `f`'s action `type`, sink `sink`, audited (f: the checks' instance facade) */
|
|
3821
|
+
const audited = (f, type, sink, h) => (s, d, nx, p, ...r) => {
|
|
3822
|
+
const was = p?.previousState;
|
|
3823
|
+
if (!isTimingAuditEnabled() || !was || was === s || !s || typeof s != 'object' || typeof was != 'object' || Array.isArray(s))
|
|
3824
|
+
return h(s, d, nx, p, ...r);
|
|
3825
|
+
const view = new Proxy(s, { get(t, k, rcv) {
|
|
3826
|
+
if (typeof k == 'string' && k in was && was[k] !== t[k] && once(`SYG257:${f.name}:${type}:${sink}:${k}`))
|
|
3827
|
+
devReport('SYG257', {
|
|
3828
|
+
component: f,
|
|
3829
|
+
message: `${f.name}'s '${type}' ${sink} read state.${k}, which the same action's STATE changed: since 7.0 it gets the new value (6.x gave it the old one)`,
|
|
3830
|
+
fix: `If it meant the value from before the action, read props.previousState.${k} (the 4th argument); if it meant the new value, nothing to change`,
|
|
3831
|
+
data: { action: type, sink, field: k },
|
|
3832
|
+
});
|
|
3833
|
+
return Reflect.get(t, k, rcv);
|
|
3834
|
+
} });
|
|
3835
|
+
return h(view, d, nx, p, ...r);
|
|
3836
|
+
};
|
|
3837
|
+
|
|
3760
3838
|
/**
|
|
3761
3839
|
* PLAN-4.6 R4: the dev entry's checks on the next component core (src/core/).
|
|
3762
3840
|
*
|
|
@@ -4269,6 +4347,9 @@ function nextHooks(_api) {
|
|
|
4269
4347
|
onAction(iv, a) { log.onAction(iv, a); },
|
|
4270
4348
|
wrapHandler(iv, type, sink, fn) {
|
|
4271
4349
|
let h = log.wrapHandler(iv, type, sink, fn) || fn;
|
|
4350
|
+
// 7.0 timing audit (SYG257, opt-in; checks/timingAudit.ts): checked per call, so turning it on later counts
|
|
4351
|
+
if (on() && sink != 'STATE' && typeof h == 'function')
|
|
4352
|
+
h = audited(fac(iv), type, sink, h);
|
|
4272
4353
|
// SYG222: the STATE reducers, wrapped (today the check rewrote the instance's model)
|
|
4273
4354
|
if (on() && sink == 'STATE' && typeof fn == 'function')
|
|
4274
4355
|
h = watchMutation(fac(iv), type, h);
|
|
@@ -4483,5 +4564,5 @@ function resetChecks() {
|
|
|
4483
4564
|
}
|
|
4484
4565
|
installChecks();
|
|
4485
4566
|
|
|
4486
|
-
export { RXJS_HINTS, checkEventBus, checks, configureChecks, configureStrict, getCodeInfo, inspect, installChecks, isStrictEnabled, listCodes, resetChecks };
|
|
4567
|
+
export { RXJS_HINTS, checkEventBus, checks, configureChecks, configureStrict, configureTimingAudit, getCodeInfo, inspect, installChecks, isStrictEnabled, isTimingAuditEnabled, listCodes, resetChecks };
|
|
4487
4568
|
//# sourceMappingURL=diagnostics.esm.js.map
|