@nisli/core 0.54.1 → 0.55.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.
Files changed (47) hide show
  1. package/README.md +93 -3
  2. package/dist/component.js +146 -5
  3. package/dist/component.js.map +1 -1
  4. package/dist/diagnostics.d.ts +48 -0
  5. package/dist/diagnostics.js +118 -0
  6. package/dist/diagnostics.js.map +1 -0
  7. package/dist/disposable.d.ts +14 -0
  8. package/dist/disposable.js +8 -0
  9. package/dist/disposable.js.map +1 -0
  10. package/dist/emitter.d.ts +2 -1
  11. package/dist/emitter.js +2 -1
  12. package/dist/emitter.js.map +1 -1
  13. package/dist/hmr/registry.js +8 -1
  14. package/dist/hmr/registry.js.map +1 -1
  15. package/dist/index.d.ts +5 -3
  16. package/dist/index.js +6 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/injector.d.ts +9 -2
  19. package/dist/injector.js +18 -0
  20. package/dist/injector.js.map +1 -1
  21. package/dist/lifecycle.d.ts +9 -0
  22. package/dist/lifecycle.js +17 -7
  23. package/dist/lifecycle.js.map +1 -1
  24. package/dist/projection.js +24 -5
  25. package/dist/projection.js.map +1 -1
  26. package/dist/query.d.ts +134 -55
  27. package/dist/query.js +379 -188
  28. package/dist/query.js.map +1 -1
  29. package/dist/resource.d.ts +2 -1
  30. package/dist/resource.js +21 -4
  31. package/dist/resource.js.map +1 -1
  32. package/dist/settle.d.ts +61 -0
  33. package/dist/settle.js +106 -0
  34. package/dist/settle.js.map +1 -0
  35. package/dist/signal.d.ts +42 -7
  36. package/dist/signal.js +248 -68
  37. package/dist/signal.js.map +1 -1
  38. package/dist/template-audit.d.ts +64 -0
  39. package/dist/template-audit.js +246 -0
  40. package/dist/template-audit.js.map +1 -0
  41. package/dist/template.d.ts +70 -1
  42. package/dist/template.js +322 -65
  43. package/dist/template.js.map +1 -1
  44. package/dist/view-transition.d.ts +70 -0
  45. package/dist/view-transition.js +98 -0
  46. package/dist/view-transition.js.map +1 -0
  47. package/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ /**
2
+ * view-transition.ts — opt-in View Transitions over the synchronous flush.
3
+ *
4
+ * `viewTransition()` wraps a state update so the browser animates between the
5
+ * before and after frames. It is a thin, opt-in wrapper: nothing here runs
6
+ * unless you call it, and no scheduler behaviour changes.
7
+ *
8
+ * ```ts
9
+ * viewTransition(() => { items.value = sorted; }, { types: ['reorder'] });
10
+ * ```
11
+ *
12
+ * ## Why `flush()` is the whole trick
13
+ *
14
+ * `document.startViewTransition()` captures the OLD frame, runs the update
15
+ * callback, then captures the NEW frame. Nisli coalesces signal writes onto a
16
+ * microtask (ADR 0015), so a bare `items.value = sorted` inside that callback
17
+ * would mutate the DOM *after* the capture window — the browser would animate
18
+ * a frame to itself. Calling `flush()` inside the callback drains the effect
19
+ * cascade synchronously, so the DOM mutation the browser snapshots is nisli's
20
+ * own synchronous flush. `flush()` keeps its documented contract exactly
21
+ * ("effects executed before the next line", ADR 0015); this is a consumer of
22
+ * it, not a change to it. N303's cascade cap still applies inside the
23
+ * callback — a runaway cascade degrades to a skipped transition, not a hang.
24
+ *
25
+ * ## Progressive enhancement
26
+ *
27
+ * Where `document.startViewTransition` is missing (no DOM at all, or an engine
28
+ * without the API) the update still applies — synchronously, flushed, and
29
+ * unanimated — and the return is `null`. Where the API exists but the
30
+ * object/`types` form does not (Chrome 111–124), the plain callback form is
31
+ * used: the transition still runs, `:active-view-transition-type()` CSS simply
32
+ * never matches. There is no polyfill and no UA sniffing.
33
+ *
34
+ * ## Companion CSS (author-side, core ships no stylesheet)
35
+ *
36
+ * The root crossfade is the UA default and needs no CSS; tune it, and cut
37
+ * motion for users who ask, with:
38
+ *
39
+ * ```css
40
+ * ::view-transition-old(root),
41
+ * ::view-transition-new(root) { animation-duration: 200ms; }
42
+ *
43
+ * @media (prefers-reduced-motion: reduce) {
44
+ * ::view-transition-group(*),
45
+ * ::view-transition-old(*),
46
+ * ::view-transition-new(*) { animation: none !important; }
47
+ * }
48
+ * ```
49
+ *
50
+ * Reduced motion is answered in CSS, not JS: `viewTransition()` is still
51
+ * called, so the swap stays atomic and typed styles stay active — only the
52
+ * motion is neutralised. `skipTransition()` on the returned handle is the hard
53
+ * opt-out.
54
+ */
55
+ export interface ViewTransitionOptions {
56
+ /** Transition types, surfaced to `:active-view-transition-type()` CSS. */
57
+ types?: string[];
58
+ }
59
+ /**
60
+ * Run a state update inside a view transition.
61
+ *
62
+ * `update` must be synchronous: everything it does happens inside the
63
+ * browser's capture window, where the page is frozen. Keep async work
64
+ * (loaders, fetches) outside and wrap only the commit.
65
+ *
66
+ * @returns the live `ViewTransition` handle (`finished`, `ready`,
67
+ * `skipTransition()`), or `null` when the platform has no support and the
68
+ * update was applied directly.
69
+ */
70
+ export declare function viewTransition(update: () => void, options?: ViewTransitionOptions): ViewTransition | null;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * view-transition.ts — opt-in View Transitions over the synchronous flush.
3
+ *
4
+ * `viewTransition()` wraps a state update so the browser animates between the
5
+ * before and after frames. It is a thin, opt-in wrapper: nothing here runs
6
+ * unless you call it, and no scheduler behaviour changes.
7
+ *
8
+ * ```ts
9
+ * viewTransition(() => { items.value = sorted; }, { types: ['reorder'] });
10
+ * ```
11
+ *
12
+ * ## Why `flush()` is the whole trick
13
+ *
14
+ * `document.startViewTransition()` captures the OLD frame, runs the update
15
+ * callback, then captures the NEW frame. Nisli coalesces signal writes onto a
16
+ * microtask (ADR 0015), so a bare `items.value = sorted` inside that callback
17
+ * would mutate the DOM *after* the capture window — the browser would animate
18
+ * a frame to itself. Calling `flush()` inside the callback drains the effect
19
+ * cascade synchronously, so the DOM mutation the browser snapshots is nisli's
20
+ * own synchronous flush. `flush()` keeps its documented contract exactly
21
+ * ("effects executed before the next line", ADR 0015); this is a consumer of
22
+ * it, not a change to it. N303's cascade cap still applies inside the
23
+ * callback — a runaway cascade degrades to a skipped transition, not a hang.
24
+ *
25
+ * ## Progressive enhancement
26
+ *
27
+ * Where `document.startViewTransition` is missing (no DOM at all, or an engine
28
+ * without the API) the update still applies — synchronously, flushed, and
29
+ * unanimated — and the return is `null`. Where the API exists but the
30
+ * object/`types` form does not (Chrome 111–124), the plain callback form is
31
+ * used: the transition still runs, `:active-view-transition-type()` CSS simply
32
+ * never matches. There is no polyfill and no UA sniffing.
33
+ *
34
+ * ## Companion CSS (author-side, core ships no stylesheet)
35
+ *
36
+ * The root crossfade is the UA default and needs no CSS; tune it, and cut
37
+ * motion for users who ask, with:
38
+ *
39
+ * ```css
40
+ * ::view-transition-old(root),
41
+ * ::view-transition-new(root) { animation-duration: 200ms; }
42
+ *
43
+ * @media (prefers-reduced-motion: reduce) {
44
+ * ::view-transition-group(*),
45
+ * ::view-transition-old(*),
46
+ * ::view-transition-new(*) { animation: none !important; }
47
+ * }
48
+ * ```
49
+ *
50
+ * Reduced motion is answered in CSS, not JS: `viewTransition()` is still
51
+ * called, so the swap stays atomic and typed styles stay active — only the
52
+ * motion is neutralised. `skipTransition()` on the returned handle is the hard
53
+ * opt-out.
54
+ */
55
+ import { flush } from './signal.js';
56
+ // ── Feature detection ───────────────────────────────────────────────
57
+ /**
58
+ * Does this engine accept `startViewTransition({ update, types })`?
59
+ *
60
+ * The object form and the `ViewTransition.types` accessor shipped together,
61
+ * so the accessor is the probe. Engines predating it take a bare callback —
62
+ * passing them an object would throw. A false negative costs only type-scoped
63
+ * CSS, never the transition.
64
+ */
65
+ function supportsTransitionTypes() {
66
+ return typeof ViewTransition !== 'undefined'
67
+ && 'types' in ViewTransition.prototype;
68
+ }
69
+ // ── Public API ──────────────────────────────────────────────────────
70
+ /**
71
+ * Run a state update inside a view transition.
72
+ *
73
+ * `update` must be synchronous: everything it does happens inside the
74
+ * browser's capture window, where the page is frozen. Keep async work
75
+ * (loaders, fetches) outside and wrap only the commit.
76
+ *
77
+ * @returns the live `ViewTransition` handle (`finished`, `ready`,
78
+ * `skipTransition()`), or `null` when the platform has no support and the
79
+ * update was applied directly.
80
+ */
81
+ export function viewTransition(update, options = {}) {
82
+ // The commit the browser snapshots: apply the update, then settle the
83
+ // reactive cascade now rather than on the next microtask.
84
+ const commit = () => {
85
+ update();
86
+ flush();
87
+ };
88
+ if (typeof document === 'undefined'
89
+ || typeof document.startViewTransition !== 'function') {
90
+ commit();
91
+ return null;
92
+ }
93
+ const types = options.types;
94
+ return types !== undefined && types.length > 0 && supportsTransitionTypes()
95
+ ? document.startViewTransition({ update: commit, types })
96
+ : document.startViewTransition(commit);
97
+ }
98
+ //# sourceMappingURL=view-transition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-transition.js","sourceRoot":"","sources":["../src/view-transition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AASpC,uEAAuE;AAEvE;;;;;;;GAOG;AACH,SAAS,uBAAuB;IAC9B,OAAO,OAAO,cAAc,KAAK,WAAW;WACvC,OAAO,IAAI,cAAc,CAAC,SAAS,CAAC;AAC3C,CAAC;AAED,uEAAuE;AAEvE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAkB,EAClB,UAAiC,EAAE;IAEnC,sEAAsE;IACtE,0DAA0D;IAC1D,MAAM,MAAM,GAAG,GAAS,EAAE;QACxB,MAAM,EAAE,CAAC;QACT,KAAK,EAAE,CAAC;IACV,CAAC,CAAC;IAEF,IAAI,OAAO,QAAQ,KAAK,WAAW;WAC9B,OAAO,QAAQ,CAAC,mBAAmB,KAAK,UAAU,EAAE,CAAC;QACxD,MAAM,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC5B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,uBAAuB,EAAE;QACzE,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACzD,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nisli/core",
3
- "version": "0.54.1",
3
+ "version": "0.55.0",
4
4
  "description": "A reactive web component framework. Signals, templates, dependency injection — no build step, no virtual DOM, no dependencies.",
5
5
  "type": "module",
6
6
  "license": "MIT",