@nisli/core 0.56.0 → 0.57.1

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 (114) hide show
  1. package/README.md +70 -9
  2. package/dist/bundler/component.js +409 -0
  3. package/dist/bundler/component.js.map +7 -0
  4. package/dist/bundler/context.js +27 -0
  5. package/dist/bundler/context.js.map +7 -0
  6. package/dist/bundler/diagnostics.js +55 -0
  7. package/dist/bundler/diagnostics.js.map +7 -0
  8. package/dist/bundler/disposable.js +9 -0
  9. package/dist/bundler/disposable.js.map +7 -0
  10. package/dist/bundler/element-context.js +47 -0
  11. package/dist/bundler/element-context.js.map +7 -0
  12. package/dist/bundler/emitter.js +70 -0
  13. package/dist/bundler/emitter.js.map +7 -0
  14. package/dist/bundler/hmr/registry.js +72 -0
  15. package/dist/bundler/hmr/registry.js.map +7 -0
  16. package/dist/bundler/hmr/transform.js +30 -0
  17. package/dist/bundler/hmr/transform.js.map +7 -0
  18. package/dist/bundler/index.js +103 -0
  19. package/dist/bundler/index.js.map +7 -0
  20. package/dist/bundler/injector.js +75 -0
  21. package/dist/bundler/injector.js.map +7 -0
  22. package/dist/bundler/lifecycle.js +56 -0
  23. package/dist/bundler/lifecycle.js.map +7 -0
  24. package/dist/bundler/projection.js +92 -0
  25. package/dist/bundler/projection.js.map +7 -0
  26. package/dist/bundler/query.js +302 -0
  27. package/dist/bundler/query.js.map +7 -0
  28. package/dist/bundler/ref.js +16 -0
  29. package/dist/bundler/ref.js.map +7 -0
  30. package/dist/bundler/resource.js +99 -0
  31. package/dist/bundler/resource.js.map +7 -0
  32. package/dist/bundler/settle.js +43 -0
  33. package/dist/bundler/settle.js.map +7 -0
  34. package/dist/bundler/signal.js +403 -0
  35. package/dist/bundler/signal.js.map +7 -0
  36. package/dist/bundler/template-audit.js +162 -0
  37. package/dist/bundler/template-audit.js.map +7 -0
  38. package/dist/bundler/template.js +922 -0
  39. package/dist/bundler/template.js.map +7 -0
  40. package/dist/bundler/view-transition.js +20 -0
  41. package/dist/bundler/view-transition.js.map +7 -0
  42. package/dist/bundler/vite-hmr/plugin.js +32 -0
  43. package/dist/bundler/vite-hmr/plugin.js.map +7 -0
  44. package/dist/bundler/vite-hmr/runtime.js +8 -0
  45. package/dist/bundler/vite-hmr/runtime.js.map +7 -0
  46. package/dist/index.js +2682 -47
  47. package/dist/index.js.map +7 -1
  48. package/dist/index.production.js +2 -0
  49. package/dist/index.production.js.map +6 -0
  50. package/dist/{diagnostics.d.ts → types/diagnostics.d.ts} +8 -15
  51. package/dist/{template-audit.d.ts → types/template-audit.d.ts} +4 -5
  52. package/dist/{template.d.ts → types/template.d.ts} +2 -1
  53. package/package.json +15 -10
  54. package/dist/component.js +0 -633
  55. package/dist/component.js.map +0 -1
  56. package/dist/context.js +0 -55
  57. package/dist/context.js.map +0 -1
  58. package/dist/diagnostics.js +0 -118
  59. package/dist/diagnostics.js.map +0 -1
  60. package/dist/disposable.js +0 -8
  61. package/dist/disposable.js.map +0 -1
  62. package/dist/element-context.js +0 -108
  63. package/dist/element-context.js.map +0 -1
  64. package/dist/emitter.js +0 -91
  65. package/dist/emitter.js.map +0 -1
  66. package/dist/hmr/registry.js +0 -136
  67. package/dist/hmr/registry.js.map +0 -1
  68. package/dist/hmr/transform.js +0 -52
  69. package/dist/hmr/transform.js.map +0 -1
  70. package/dist/injector.js +0 -118
  71. package/dist/injector.js.map +0 -1
  72. package/dist/lifecycle.js +0 -117
  73. package/dist/lifecycle.js.map +0 -1
  74. package/dist/projection.js +0 -179
  75. package/dist/projection.js.map +0 -1
  76. package/dist/query.js +0 -429
  77. package/dist/query.js.map +0 -1
  78. package/dist/ref.js +0 -43
  79. package/dist/ref.js.map +0 -1
  80. package/dist/resource.js +0 -129
  81. package/dist/resource.js.map +0 -1
  82. package/dist/settle.js +0 -106
  83. package/dist/settle.js.map +0 -1
  84. package/dist/signal.js +0 -645
  85. package/dist/signal.js.map +0 -1
  86. package/dist/template-audit.js +0 -246
  87. package/dist/template-audit.js.map +0 -1
  88. package/dist/template.js +0 -1244
  89. package/dist/template.js.map +0 -1
  90. package/dist/view-transition.js +0 -98
  91. package/dist/view-transition.js.map +0 -1
  92. package/dist/vite-hmr/plugin.js +0 -62
  93. package/dist/vite-hmr/plugin.js.map +0 -1
  94. package/dist/vite-hmr/runtime.js +0 -17
  95. package/dist/vite-hmr/runtime.js.map +0 -1
  96. /package/dist/{component.d.ts → types/component.d.ts} +0 -0
  97. /package/dist/{context.d.ts → types/context.d.ts} +0 -0
  98. /package/dist/{disposable.d.ts → types/disposable.d.ts} +0 -0
  99. /package/dist/{element-context.d.ts → types/element-context.d.ts} +0 -0
  100. /package/dist/{emitter.d.ts → types/emitter.d.ts} +0 -0
  101. /package/dist/{hmr → types/hmr}/registry.d.ts +0 -0
  102. /package/dist/{hmr → types/hmr}/transform.d.ts +0 -0
  103. /package/dist/{index.d.ts → types/index.d.ts} +0 -0
  104. /package/dist/{injector.d.ts → types/injector.d.ts} +0 -0
  105. /package/dist/{lifecycle.d.ts → types/lifecycle.d.ts} +0 -0
  106. /package/dist/{projection.d.ts → types/projection.d.ts} +0 -0
  107. /package/dist/{query.d.ts → types/query.d.ts} +0 -0
  108. /package/dist/{ref.d.ts → types/ref.d.ts} +0 -0
  109. /package/dist/{resource.d.ts → types/resource.d.ts} +0 -0
  110. /package/dist/{settle.d.ts → types/settle.d.ts} +0 -0
  111. /package/dist/{signal.d.ts → types/signal.d.ts} +0 -0
  112. /package/dist/{view-transition.d.ts → types/view-transition.d.ts} +0 -0
  113. /package/dist/{vite-hmr → types/vite-hmr}/plugin.d.ts +0 -0
  114. /package/dist/{vite-hmr → types/vite-hmr}/runtime.d.ts +0 -0
package/dist/context.js DELETED
@@ -1,55 +0,0 @@
1
- /**
2
- * context.ts — Setup context for pure function DI
3
- *
4
- * The setup context lets pure functions (inject(), listen(), effect())
5
- * access the component they belong to without needing `this`.
6
- *
7
- * The context exists ONLY during component initialization (setup()).
8
- * It is NOT a runtime thing — it's a synchronous initialization-time concept.
9
- *
10
- * Same pattern as Angular's inject(), Solid's createSignal(), Vue's setup().
11
- */
12
- // ── Context state ───────────────────────────────────────────────────
13
- let currentComponent = null;
14
- // ── Public API ──────────────────────────────────────────────────────
15
- /**
16
- * Run a function with a component as the active context.
17
- * Pure functions called during `fn` can access the component via
18
- * `getCurrentComponent()`.
19
- *
20
- * Context is strictly synchronous — it does NOT survive across
21
- * await boundaries or microtasks.
22
- */
23
- export function runWithContext(component, fn) {
24
- const prev = currentComponent;
25
- currentComponent = component;
26
- try {
27
- fn();
28
- }
29
- finally {
30
- currentComponent = prev;
31
- }
32
- }
33
- /**
34
- * Get the currently active component host.
35
- * Throws if called outside a setup() context.
36
- *
37
- * This is framework-internal — component authors use inject(), effect(),
38
- * etc., which call this internally.
39
- */
40
- export function getCurrentComponent() {
41
- if (!currentComponent) {
42
- throw new Error('getCurrentComponent() called outside setup(). ' +
43
- 'inject(), effect(), and other context-dependent functions ' +
44
- 'can only be called during component initialization.');
45
- }
46
- return currentComponent;
47
- }
48
- /**
49
- * Check if there is an active setup context.
50
- * Returns true during component setup, false otherwise.
51
- */
52
- export function hasContext() {
53
- return currentComponent !== null;
54
- }
55
- //# sourceMappingURL=context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,uEAAuE;AAEvE,IAAI,gBAAgB,GAAyB,IAAI,CAAC;AAElD,uEAAuE;AAEvE;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,SAAwB,EAAE,EAAc;IACrE,MAAM,IAAI,GAAG,gBAAgB,CAAC;IAC9B,gBAAgB,GAAG,SAAS,CAAC;IAC7B,IAAI,CAAC;QACH,EAAE,EAAE,CAAC;IACP,CAAC;YAAS,CAAC;QACT,gBAAgB,GAAG,IAAI,CAAC;IAC1B,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB;IACjC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,gDAAgD;YAChD,4DAA4D;YAC5D,qDAAqD,CACtD,CAAC;IACJ,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,gBAAgB,KAAK,IAAI,CAAC;AACnC,CAAC"}
@@ -1,118 +0,0 @@
1
- /**
2
- * diagnostics.ts — coded diagnostics leaf + the dev-mode gate (ADR 0030.2, Wave 1)
3
- *
4
- * The shared foundation every core layer reports through. Two rules keep it a
5
- * LEAF (ADR 0030.2 §7): zero imports from other core modules (so signal.ts,
6
- * template.ts, component.ts, injector.ts can all use it without cycles), and
7
- * codes + a minimal payload emitter only — no docs prose.
8
- *
9
- * Contract:
10
- * - Every diagnostic carries a stable machine code `N<range><nn>` and renders
11
- * as ONE console.error line: `[nisli CODE] message` (+ structured detail as
12
- * the second console argument when given). Thrown coded errors reuse the
13
- * same `[nisli CODE]` prefix via {@link formatDiag} so `grep '\[nisli '`
14
- * finds every framework-reported failure, logged or thrown.
15
- * - `diag()` itself is NOT dev-gated: gating is the CALL SITE's policy. Some
16
- * diagnostics are production facts (T6 containment N401/N402 — parity with
17
- * the pre-T6 unconditional console.error); others are dev-only attribution
18
- * (N201/N202/N501) and their callers guard with {@link isDev}.
19
- *
20
- * The dev gate (a named Wave-1 deliverable — core had NO dev/prod mechanism):
21
- * a runtime probe, layered for the three environments core must serve without
22
- * a bundler assumption (ADR 0019):
23
- * 1. `import.meta.env.DEV` / `.PROD` when an injector (Vite serve/build,
24
- * vitest) provides them — statically replaced in Vite builds, so a prod
25
- * build resolves prod even in the browser where `process` is absent.
26
- * 2. `globalThis.process?.env?.NODE_ENV` — Node scripts, test runners, SSG.
27
- * 3. Neither present (raw buildless ESM in a browser): DEV — loud by
28
- * default, matching the framework's no-build posture; `setDevMode(false)`
29
- * is the explicit opt-out for buildless production pages.
30
- * The probe runs once at module load; `setDevMode()` overrides it (tests,
31
- * bundler-less prod), `setDevMode(null)` restores the probed default.
32
- */
33
- // ── Code registry ───────────────────────────────────────────────────
34
- //
35
- // The minimal central code table (0030.1 B2, landed as part of T6 per §8).
36
- // `Nxyz` = concrete code; `Nxxx` range keys document ownership so parallel
37
- // waves allocate without collision. Ranges owned by other Wave-1 worktrees
38
- // (template, reactivity, async) are seeded here as placeholders only — their
39
- // concrete codes land with their waves.
40
- export const codes = {
41
- // Range placeholders — owner module in parentheses.
42
- N1xx: 'template: parse/static-audit diagnostics (template.ts — template wave)',
43
- N2xx: 'define/props: registration and prop-contract diagnostics (component.ts)',
44
- N3xx: 'reactivity: scheduler/purity diagnostics (signal.ts — scheduler wave)',
45
- N4xx: 'lifecycle: setup/mount containment (component.ts, lifecycle.ts)',
46
- N5xx: 'dependency injection (injector.ts)',
47
- N6xx: 'async: query/resource/settle diagnostics (query.ts, resource.ts — async wave)',
48
- // Assigned codes.
49
- N201: 'duplicate component definition — tag already defined; first definition stays live',
50
- N202: 'unknown-prop echo — prop written after mount but never read by setup()',
51
- N401: 'setup failed — scope contained, host stamped data-nisli-error="N401"',
52
- N402: 'onMount failed — scope contained, host stamped data-nisli-error="N402"',
53
- N501: 'provide() after the token was already instantiated — resetInjector() first',
54
- N101: 'template audit — dash-tag never defined (misspelled or unimported component?)',
55
- N102: 'template audit — unknown @event name',
56
- N103: 'template audit — unknown event modifier',
57
- N104: 'template audit — undeclared attribute on a component with declared attrs',
58
- N105: 'template — remounting a live bound TemplateResult (shared-binding corruption)',
59
- N106: 'template — html:inner requires a branded value: raw(), sanitized(), or TrustedHTML',
60
- N107: 'template — html:inner sanitized() has no sink: no native Element.setHTML() and no setSanitizerFallback() hook',
61
- N301: 'reactivity — effect exceeded the consecutive re-schedule cap (loop); disposed',
62
- N302: 'reactivity — effect wrote a signal in its own dependency set (attribution)',
63
- N303: 'reactivity — flush()/tick() iteration cap reached before quiescence',
64
- N310: 'reactivity — effect callback returned a Promise; use resource() for async',
65
- N602: 'query — invalid key element (flat primitives only; null is the optional sentinel)',
66
- N603: 'settle() — no quiescence within the iteration cap; returning anyway',
67
- // N601 (mixed QueryClients) was retired before release: N501 makes that
68
- // state unrepresentable at its cause. The number is not reused.
69
- };
70
- // ── Dev gate ────────────────────────────────────────────────────────
71
- function probeDevMode() {
72
- // 1) import.meta.env injectors (Vite, vitest). Read the OBJECT (not the
73
- // dotted key) so Vite's define replacement works through the optional
74
- // access, and a missing env is just `undefined` in Node/raw ESM.
75
- const env = import.meta.env;
76
- if (env && typeof env.DEV === 'boolean')
77
- return env.DEV;
78
- if (env && typeof env.PROD === 'boolean')
79
- return !env.PROD;
80
- // 2) Node / test runners / bundlers that define process.env.NODE_ENV.
81
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
82
- if (typeof nodeEnv === 'string')
83
- return nodeEnv !== 'production';
84
- // 3) No signal at all (raw buildless ESM): loud by default.
85
- return true;
86
- }
87
- const probed = probeDevMode();
88
- let override = null;
89
- /** Whether dev-only diagnostics are active. See the module doc for the probe. */
90
- export function isDev() {
91
- return override ?? probed;
92
- }
93
- /**
94
- * Override the probed dev mode (`true`/`false`), or restore the probed
95
- * default (`null`). For tests and for buildless pages deployed to production.
96
- */
97
- export function setDevMode(on) {
98
- override = on;
99
- }
100
- // ── Emission ────────────────────────────────────────────────────────
101
- /** The shared `[nisli CODE] message` line, for both logged and thrown paths. */
102
- export function formatDiag(code, message) {
103
- return `[nisli ${code}] ${message}`;
104
- }
105
- /**
106
- * Emit one coded diagnostic: a single console.error line
107
- * `[nisli CODE] message`, with `detail` attached as structured payload.
108
- * NOT dev-gated here — the caller owns the policy (see module doc).
109
- */
110
- export function diag(code, message, detail) {
111
- if (detail === undefined) {
112
- console.error(formatDiag(code, message));
113
- }
114
- else {
115
- console.error(formatDiag(code, message), detail);
116
- }
117
- }
118
- //# sourceMappingURL=diagnostics.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,uEAAuE;AACvE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,wCAAwC;AACxC,MAAM,CAAC,MAAM,KAAK,GAA2B;IAC3C,oDAAoD;IACpD,IAAI,EAAE,wEAAwE;IAC9E,IAAI,EAAE,yEAAyE;IAC/E,IAAI,EAAE,uEAAuE;IAC7E,IAAI,EAAE,iEAAiE;IACvE,IAAI,EAAE,oCAAoC;IAC1C,IAAI,EAAE,+EAA+E;IACrF,kBAAkB;IAClB,IAAI,EAAE,mFAAmF;IACzF,IAAI,EAAE,wEAAwE;IAC9E,IAAI,EAAE,sEAAsE;IAC5E,IAAI,EAAE,wEAAwE;IAC9E,IAAI,EAAE,4EAA4E;IAClF,IAAI,EAAE,+EAA+E;IACrF,IAAI,EAAE,sCAAsC;IAC5C,IAAI,EAAE,yCAAyC;IAC/C,IAAI,EAAE,0EAA0E;IAChF,IAAI,EAAE,+EAA+E;IACrF,IAAI,EAAE,oFAAoF;IAC1F,IAAI,EAAE,+GAA+G;IACrH,IAAI,EAAE,+EAA+E;IACrF,IAAI,EAAE,4EAA4E;IAClF,IAAI,EAAE,qEAAqE;IAC3E,IAAI,EAAE,2EAA2E;IACjF,IAAI,EAAE,mFAAmF;IACzF,IAAI,EAAE,qEAAqE;IAC3E,wEAAwE;IACxE,gEAAgE;CACjE,CAAC;AAEF,uEAAuE;AAEvE,SAAS,YAAY;IACnB,wEAAwE;IACxE,sEAAsE;IACtE,iEAAiE;IACjE,MAAM,GAAG,GAAI,MAAM,CAAC,IAElB,CAAC,GAAG,CAAC;IACP,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC;IACxD,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IAC3D,sEAAsE;IACtE,MAAM,OAAO,GAAI,UAEf,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC;IAC1B,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,KAAK,YAAY,CAAC;IACjE,4DAA4D;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;AAC9B,IAAI,QAAQ,GAAmB,IAAI,CAAC;AAEpC,iFAAiF;AACjF,MAAM,UAAU,KAAK;IACnB,OAAO,QAAQ,IAAI,MAAM,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,EAAkB;IAC3C,QAAQ,GAAG,EAAE,CAAC;AAChB,CAAC;AAED,uEAAuE;AAEvE,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,OAAe;IACtD,OAAO,UAAU,IAAI,KAAK,OAAO,EAAE,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,OAAe,EAAE,MAAgC;IAClF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -1,8 +0,0 @@
1
- const DISPOSE = Symbol.dispose;
2
- /** Attach a guarded Symbol.dispose alias without polyfilling the platform. */
3
- export function disposable(value, dispose) {
4
- if (DISPOSE)
5
- value[DISPOSE] = dispose;
6
- return value;
7
- }
8
- //# sourceMappingURL=disposable.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"disposable.js","sourceRoot":"","sources":["../src/disposable.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,GAAG,MAAM,CAAC,OAA4C,CAAC;AAEpE,8EAA8E;AAC9E,MAAM,UAAU,UAAU,CACxB,KAAQ,EACR,OAAmB;IAEnB,IAAI,OAAO;QAAG,KAAoC,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACtE,OAAO,KAA+B,CAAC;AACzC,CAAC"}
@@ -1,108 +0,0 @@
1
- /**
2
- * element-context.ts — subtree-scoped context (provide / inject).
3
- *
4
- * The DOM-native equivalent of React/Radix context: a provider publishes a
5
- * typed value on its host element, and descendant components `inject()` it by
6
- * walking up the element tree. It replaces the userland `host.__uiX = state` +
7
- * `host.closest('ui-x')` convention (stringly-typed, invisible to TypeScript,
8
- * re-implemented per family) with an identity-keyed, end-to-end-typed primitive.
9
- *
10
- * ```ts
11
- * interface TabsState { value: ReadonlySignal<string>; setValue(v: string): void }
12
- * const TabsContext = createContext<TabsState>('Tabs');
13
- *
14
- * // provider setup:
15
- * TabsContext.provide(host, state);
16
- *
17
- * // descendant setup:
18
- * const tabs = TabsContext.inject(); // throws (error boundary) if absent
19
- * const maybe = TabsContext.inject.optional(); // TabsState | undefined
20
- * ```
21
- *
22
- * Design (see ADR 0025 §2):
23
- *
24
- * - **Typed, no string keys.** Each `createContext` mints a private `Symbol`
25
- * used as the storage key; consumers reference the context object, never a
26
- * string. Two contexts never collide even with the same `name` (name is for
27
- * diagnostics only).
28
- *
29
- * - **Capture at setup, not at lookup.** `inject()` resolves during component
30
- * setup, while the injecting host is still a DOM descendant of its provider.
31
- * It returns the value *once*; because provider values carry signals,
32
- * reactivity flows through them with no further tree-walks. This is what makes
33
- * it **portal-safe**: after a subtree is reparented to `<body>` (dialog, menu,
34
- * …) the injected value is already held, so the component keeps resolving its
35
- * ORIGINAL provider — a later walk from the portaled host would find nothing.
36
- * `inject()` therefore requires an active setup context when no host is passed.
37
- *
38
- * - **Zero cost for non-users.** Providing sets one symbol property on the host;
39
- * injecting is a `parentElement` walk. Components that use neither pay nothing,
40
- * and there is no global registry to maintain.
41
- */
42
- import { getCurrentComponent, hasContext } from './context.js';
43
- // ── Public API ──────────────────────────────────────────────────────
44
- /**
45
- * Create a subtree-scoped context. `name` is used only in diagnostics; the
46
- * context's identity (a private symbol) is what keys storage and lookup.
47
- *
48
- * Pass `{ providerTag }` to name the provider element so a missing-provider
49
- * `inject()` produces an ACTIONABLE, element-language error —
50
- * `<injecting-tag> must be used inside <provider-tag>.` — instead of naming the
51
- * private context object. Set it whenever consumers are DOM elements (the whole
52
- * registry): it's the difference between a message a plain-HTML user can act on
53
- * and one they can't.
54
- */
55
- export function createContext(name, options = {}) {
56
- const { providerTag } = options;
57
- // Private per-context storage key. Not derived from `name`, so same-named
58
- // contexts stay distinct, and it never appears in userland.
59
- const KEY = Symbol(`nisli.context:${name}`);
60
- const store = (el) => el;
61
- const provide = (host, value) => {
62
- store(host)[KEY] = value;
63
- };
64
- const peek = (host) => KEY in host ? store(host)[KEY] : undefined;
65
- // Walk up the element tree (inclusive of `start`) to the nearest provider.
66
- // `parentElement` transparently crosses `display: contents` hosts.
67
- const find = (start) => {
68
- let el = start;
69
- while (el) {
70
- if (KEY in el)
71
- return store(el)[KEY];
72
- el = el.parentElement;
73
- }
74
- return undefined;
75
- };
76
- const startHost = (host, caller) => {
77
- if (host)
78
- return host;
79
- if (!hasContext()) {
80
- throw new Error(`${caller} for context "${name}" was called without a host and outside component ` +
81
- `setup. Call it during setup (so the provider is resolved before any portal move), ` +
82
- `or pass an explicit host.`);
83
- }
84
- return getCurrentComponent().element;
85
- };
86
- const inject = ((host) => {
87
- const start = startHost(host, 'inject()');
88
- const found = find(start);
89
- if (found === undefined) {
90
- const injectingTag = start.tagName.toLowerCase();
91
- // With a declared provider tag, speak element-language the consumer can
92
- // act on; otherwise fall back to the generic (provider named by API).
93
- throw new Error(providerTag
94
- ? `Context "${name}": <${injectingTag}> must be used inside <${providerTag}>.`
95
- : `Context "${name}" not found: <${injectingTag}> must be rendered ` +
96
- `inside a provider that calls ${name}Context.provide(host, …).`);
97
- }
98
- return found;
99
- });
100
- inject.optional = (host) => {
101
- // Outside setup with no host: there is nothing to resolve from — undefined.
102
- if (!host && !hasContext())
103
- return undefined;
104
- return find(startHost(host, 'inject.optional()'));
105
- };
106
- return { name, provide, inject, peek };
107
- }
108
- //# sourceMappingURL=element-context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"element-context.js","sourceRoot":"","sources":["../src/element-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAgC/D,uEAAuE;AAEvE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,UAAoC,EAAE;IAEtC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAChC,0EAA0E;IAC1E,4DAA4D;IAC5D,MAAM,GAAG,GAAG,MAAM,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,KAAK,GAAG,CAAC,EAAW,EAA2B,EAAE,CACrD,EAAwC,CAAC;IAE3C,MAAM,OAAO,GAAG,CAAC,IAAiB,EAAE,KAAQ,EAAQ,EAAE;QACpD,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC3B,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,IAAiB,EAAiB,EAAE,CAChD,GAAG,IAAI,IAAI,CAAC,CAAC,CAAE,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpD,2EAA2E;IAC3E,mEAAmE;IACnE,MAAM,IAAI,GAAG,CAAC,KAAkB,EAAiB,EAAE;QACjD,IAAI,EAAE,GAAmB,KAAK,CAAC;QAC/B,OAAO,EAAE,EAAE,CAAC;YACV,IAAI,GAAG,IAAI,EAAE;gBAAE,OAAO,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAM,CAAC;YAC1C,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC;QACxB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,IAA6B,EAAE,MAAc,EAAsB,EAAE;QACtF,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACtB,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,GAAG,MAAM,iBAAiB,IAAI,oDAAoD;gBAChF,oFAAoF;gBACpF,2BAA2B,CAC9B,CAAC;QACJ,CAAC;QACD,OAAO,mBAAmB,EAAE,CAAC,OAAO,CAAC;IACvC,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,CAAC,IAAkB,EAAK,EAAE;QACxC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,UAAU,CAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACjD,wEAAwE;YACxE,sEAAsE;YACtE,MAAM,IAAI,KAAK,CACb,WAAW;gBACT,CAAC,CAAC,YAAY,IAAI,OAAO,YAAY,0BAA0B,WAAW,IAAI;gBAC9E,CAAC,CAAC,YAAY,IAAI,iBAAiB,YAAY,qBAAqB;oBAChE,gCAAgC,IAAI,2BAA2B,CACtE,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAc,CAAC;IAEhB,MAAM,CAAC,QAAQ,GAAG,CAAC,IAAkB,EAAiB,EAAE;QACtD,4EAA4E;QAC5E,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE;YAAE,OAAO,SAAS,CAAC;QAC7C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,mBAAmB,CAAE,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACzC,CAAC"}
package/dist/emitter.js DELETED
@@ -1,91 +0,0 @@
1
- /**
2
- * emitter.ts — Typed pub/sub base class for services.
3
- *
4
- * Replaces `document.dispatchEvent(new CustomEvent(...))` with typed,
5
- * scoped event emitters. Services extend Emitter<T> where T defines
6
- * the event contract as a record of event names → payload types.
7
- *
8
- * ```ts
9
- * class NavigationEvents extends Emitter<{
10
- * select: { id: string };
11
- * filter: { filter: string };
12
- * }> {}
13
- *
14
- * const nav = inject(NavigationEvents);
15
- * nav.emit('select', { id: 'TASK-1' }); // typed
16
- * nav.on('select', ({ id }) => console.log(id)); // typed
17
- * const selectedId = nav.toSignal('select', e => e.id, null);
18
- * ```
19
- */
20
- import { signal } from './signal.js';
21
- import { hasContext, getCurrentComponent } from './context.js';
22
- import { disposable } from './disposable.js';
23
- // ── Emitter base class ──────────────────────────────────────────────
24
- export class Emitter {
25
- listeners = new Map();
26
- /**
27
- * Emit a typed event. All subscribers for this event name are called
28
- * synchronously. If a subscriber throws, other subscribers still fire.
29
- */
30
- emit(event, payload) {
31
- const set = this.listeners.get(event);
32
- if (!set)
33
- return;
34
- // Iterate a copy to allow unsubscribe during callback
35
- for (const fn of [...set]) {
36
- try {
37
- fn(payload);
38
- }
39
- catch (err) {
40
- console.error(`Emitter: subscriber for '${String(event)}' threw:`, err);
41
- }
42
- }
43
- }
44
- /**
45
- * Subscribe to a typed event. Returns an unsubscribe function.
46
- *
47
- * If called during component setup, the subscription is automatically
48
- * disposed on disconnectedCallback.
49
- */
50
- on(event, fn) {
51
- let set = this.listeners.get(event);
52
- if (!set) {
53
- set = new Set();
54
- this.listeners.set(event, set);
55
- }
56
- set.add(fn);
57
- const unsubscribe = () => {
58
- set.delete(fn);
59
- if (set.size === 0) {
60
- this.listeners.delete(event);
61
- }
62
- };
63
- // Auto-dispose if inside component setup context
64
- if (hasContext()) {
65
- getCurrentComponent().addDisposer(unsubscribe);
66
- }
67
- return disposable(unsubscribe, unsubscribe);
68
- }
69
- /**
70
- * Bridge an event into the signal system.
71
- * Returns a signal that updates whenever the event fires.
72
- *
73
- * @param event - Event name to subscribe to
74
- * @param selector - Transform the payload into the signal value
75
- * @param initial - Initial signal value before any event fires
76
- */
77
- toSignal(event, selector, initial) {
78
- const s = signal(initial);
79
- this.on(event, (payload) => {
80
- s.value = selector(payload);
81
- });
82
- return s;
83
- }
84
- /**
85
- * Remove all subscribers. Useful for testing cleanup.
86
- */
87
- clear() {
88
- this.listeners.clear();
89
- }
90
- }
91
- //# sourceMappingURL=emitter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAe,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAiB,MAAM,iBAAiB,CAAC;AAO5D,uEAAuE;AAEvE,MAAM,OAAO,OAAO;IACV,SAAS,GAAG,IAAI,GAAG,EAAoC,CAAC;IAEhE;;;OAGG;IACH,IAAI,CAAyB,KAAQ,EAAE,OAAkB;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,sDAAsD;QACtD,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,EAAE,CAAC,OAAO,CAAC,CAAC;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,EAAE,CAAyB,KAAQ,EAAE,EAAuB;QAC1D,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEZ,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,GAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAChB,IAAI,GAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC;QAEF,iDAAiD;QACjD,IAAI,UAAU,EAAE,EAAE,CAAC;YACjB,mBAAmB,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,UAAU,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CACN,KAAQ,EACR,QAAmC,EACnC,OAAU;QAEV,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE;YACzB,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,CAAC;IACX,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF"}
@@ -1,136 +0,0 @@
1
- /**
2
- * hmr/registry.ts — Transport-agnostic HMR core for Nisli (ADR 0021, ADR 0110).
3
- *
4
- * The tag→setup registry, the stable indirection thunk, and the in-place
5
- * re-mount through the custom-element lifecycle. This is the part that is
6
- * IDENTICAL regardless of how change notifications arrive — change
7
- * (`../esbuild-hmr/*`) or Vite `import.meta.hot` (`../vite-hmr/*`) both drive
8
- * the same registry. Keeping it here means one implementation, one set of
9
- * correctness guarantees (ADR 0008.1 disposal), and no duplication.
10
- *
11
- * DEV-ONLY: reachable only via the dev subpaths, never from the `@nisli/core`
12
- * `.` entry, so it contributes zero production bytes.
13
- */
14
- // ── Tag → setup registry (Ruling 2) ─────────────────────────────────
15
- const registry = new Map();
16
- /** Tags whose setup changed since the last drain — pending a re-mount. */
17
- const pendingRemounts = new Set();
18
- let remountScheduled = false;
19
- /**
20
- * When true, `__register` accumulates pending tags but does NOT auto-schedule a
21
- * microtask drain — the caller owns the drain so it can wait for a full module
22
- * re-eval to register ALL changed tags first.
23
- * The Vite path leaves this false and drains explicitly from its accept hook.
24
- */
25
- let suppressAutoDrain = false;
26
- /**
27
- * Begin a caller-owned drain window: suppress auto-scheduling AND cancel any
28
- * pending microtask drain, so the caller can re-evaluate a whole bundle and
29
- * register ALL changed tags before a single explicit `drainRemounts()`. Used by
30
- * the Vite path drains per-module.
31
- */
32
- export function beginManualDrain() {
33
- suppressAutoDrain = true;
34
- remountScheduled = false;
35
- }
36
- /** End a caller-owned drain window (re-enable auto-scheduling). */
37
- export function endManualDrain() {
38
- suppressAutoDrain = false;
39
- }
40
- /**
41
- * Register (or update) the setup for a tag and return a STABLE indirection
42
- * thunk that always reads the *current* setup from the registry.
43
- *
44
- * First registration for a tag (initial load) schedules no re-mount. A
45
- * subsequent registration with a *different* setup reference (a rebuild
46
- * re-evaluating the module) queues the tag for re-mount.
47
- *
48
- * The returned thunk is what `component()` hands to the real
49
- * `FrameworkComponent`; because it reads `registry.get(tag)` lazily, the live
50
- * element picks up new setup with no class swap or tag re-definition.
51
- */
52
- export function __register(tag, setup) {
53
- const prev = registry.get(tag);
54
- registry.set(tag, setup);
55
- if (prev !== undefined && prev !== setup) {
56
- pendingRemounts.add(tag);
57
- if (!suppressAutoDrain)
58
- scheduleRemount();
59
- }
60
- const thunk = (props, host) => {
61
- const current = registry.get(tag);
62
- if (!current)
63
- throw new Error(`[nisli-hmr] no setup registered for <${tag}>`);
64
- return current(props, host);
65
- };
66
- // N201 exemption marker (ADR 0030.2 §3/§8): component() throws a coded dev
67
- // error on duplicate `define`, EXCEPT when the incoming setup is a registry
68
- // thunk — an HMR re-evaluation of the same module re-calling component() is
69
- // the swap mechanism working, not a second definer. The marker rides the
70
- // thunk itself so component.ts needs no import from this dev-only module.
71
- thunk.__nisliHmr = true;
72
- return thunk;
73
- }
74
- /** Number of tags awaiting a re-mount (for orchestration/tests). */
75
- export function pendingRemountCount() {
76
- return pendingRemounts.size;
77
- }
78
- /** Drain and re-mount every pending tag. Returns the tags that were remounted. */
79
- export function drainRemounts() {
80
- const tags = [...pendingRemounts];
81
- pendingRemounts.clear();
82
- remountScheduled = false;
83
- for (const tag of tags)
84
- remount(tag);
85
- return tags;
86
- }
87
- function scheduleRemount() {
88
- if (remountScheduled)
89
- return;
90
- remountScheduled = true;
91
- // Defer so a full module re-eval can register *all* changed tags first.
92
- queueMicrotask(() => {
93
- if (remountScheduled)
94
- drainRemounts();
95
- });
96
- }
97
- // ── Re-mount through the existing lifecycle (Ruling 3) ──────────────
98
- /**
99
- * Re-mount every live element of `tag` in place, on the SAME instance:
100
- *
101
- * disconnectedCallback() — resets `_mounted`, disposes effects + template
102
- * (the ADR 0008.1 disposal contract)
103
- * replaceChildren() — clears old DOM, firing nested custom elements'
104
- * disconnectedCallback so their disposers run too
105
- * connectedCallback() — re-runs the NEW setup (via the registry thunk)
106
- * inside the same `untrack(...)` mount path
107
- *
108
- * Props survive because `_propsProxy` is constructor-owned (component.ts), and
109
- * the element node identity is preserved — siblings, scroll, route, and global
110
- * stores are untouched.
111
- */
112
- export function remount(tag) {
113
- const elements = document.querySelectorAll(tag);
114
- elements.forEach((node) => {
115
- const el = node;
116
- if (el._remount) {
117
- el._remount();
118
- return;
119
- }
120
- // Dispose first: must run BEFORE clearing DOM or effects/subscriptions
121
- // leak across reloads (ADR 0008.1 hazard).
122
- el.disconnectedCallback?.();
123
- // Clear old DOM — removal fires nested children's disconnectedCallback.
124
- el.replaceChildren();
125
- // Re-run setup + re-mount on the same instance.
126
- el.connectedCallback?.();
127
- });
128
- }
129
- /** Test-only: clear the registry and pending state between cases. */
130
- export function __resetRegistry() {
131
- registry.clear();
132
- pendingRemounts.clear();
133
- remountScheduled = false;
134
- suppressAutoDrain = false;
135
- }
136
- //# sourceMappingURL=registry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hmr/registry.ts"],"names":[],"mappings":"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"}
@@ -1,52 +0,0 @@
1
- /**
2
- * hmr/transform.ts — Shared source rewrite for Nisli HMR (ADR 0021, ADR 0110).
3
- *
4
- * Wraps the `component` a module imports from `@nisli/core` with the registry
5
- * indirection so a rebuilt module's `component()` calls re-register their setup
6
- * and trigger an in-place re-mount. Transport-agnostic and pure — used by both
7
- * the Vite `transform` hook.
8
- */
9
- function escapeRegExp(s) {
10
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11
- }
12
- /**
13
- * Rewrite a module's source so the `component` it imports from `coreSpecifier`
14
- * is wrapped by the registry indirection from `runtimeSpecifier`.
15
- *
16
- * Strategy: alias the real import, then shadow `component` with a wrapper that
17
- * registers the setup and passes a STABLE thunk to the real `component`. Cost:
18
- * one Map write + one thunk allocation per `component()` call.
19
- *
20
- * Returns the original source unchanged if it does not import `component` from
21
- * the core specifier (nothing to wrap) — the caller uses identity to decide
22
- * whether the module is HMR-relevant.
23
- */
24
- export function transformSource(code, coreSpecifier, runtimeSpecifier) {
25
- const spec = escapeRegExp(coreSpecifier);
26
- // Match a named import of `component` (alone or among others) from core.
27
- const importRe = new RegExp(`import\\s*\\{([^}]*)\\}\\s*from\\s*['"]${spec}['"]`, 'g');
28
- let importsComponent = false;
29
- const rewritten = code.replace(importRe, (_full, names) => {
30
- const parts = names.split(',').map((s) => s.trim()).filter(Boolean);
31
- const next = parts.map((p) => {
32
- // Handle `component` and `component as X` — we only wrap the bare name.
33
- if (p === 'component') {
34
- importsComponent = true;
35
- return 'component as __nisliRealComponent';
36
- }
37
- return p;
38
- });
39
- return `import { ${next.join(', ')} } from '${coreSpecifier}'`;
40
- });
41
- if (!importsComponent)
42
- return code;
43
- // Prepend the registry-import + wrapper. `__register` returns the STABLE
44
- // thunk the real component captures; the wrapper preserves the factory's
45
- // return value and signature.
46
- const preamble = `import { __register as __nisliRegister } from '${runtimeSpecifier}';\n` +
47
- `function component(tag, setup, opts) {\n` +
48
- ` return __nisliRealComponent(tag, __nisliRegister(tag, setup), opts);\n` +
49
- `}\n`;
50
- return preamble + rewritten;
51
- }
52
- //# sourceMappingURL=transform.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/hmr/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,aAAqB,EAAE,gBAAwB;IAC3F,MAAM,IAAI,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IACzC,yEAAyE;IACzE,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,0CAA0C,IAAI,MAAM,EAAE,GAAG,CAAC,CAAC;IAEvF,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,EAAE;QACxE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC3B,wEAAwE;YACxE,IAAI,CAAC,KAAK,WAAW,EAAE,CAAC;gBACtB,gBAAgB,GAAG,IAAI,CAAC;gBACxB,OAAO,mCAAmC,CAAC;YAC7C,CAAC;YACD,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;QACH,OAAO,YAAY,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,aAAa,GAAG,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAEnC,yEAAyE;IACzE,yEAAyE;IACzE,8BAA8B;IAC9B,MAAM,QAAQ,GACZ,kDAAkD,gBAAgB,MAAM;QACxE,0CAA0C;QAC1C,0EAA0E;QAC1E,KAAK,CAAC;IAER,OAAO,QAAQ,GAAG,SAAS,CAAC;AAC9B,CAAC"}