@symbiotejs/symbiote 3.0.0-next.3 → 3.0.2

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/AI_REFERENCE.md CHANGED
@@ -509,43 +509,67 @@ Usage:
509
509
 
510
510
  ## Server-Side Rendering (SSR)
511
511
 
512
- Import `core/ssr.js` to render components to HTML strings on the server. Requires `linkedom` (optional peer dependency).
512
+ Import `node/SSR.js` to render components to HTML strings on the server. Requires `linkedom` (optional peer dependency).
513
+
514
+ ### Basic usage — `processHtml`
513
515
 
514
516
  ```js
515
- import { initSSR, renderToString, destroySSR } from '@symbiotejs/symbiote/core/ssr.js';
517
+ import { SSR } from '@symbiotejs/symbiote/node/SSR.js';
518
+
519
+ await SSR.init(); // patches globals with linkedom env
520
+ await import('./my-component.js'); // component reg() works normally
521
+
522
+ let html = await SSR.processHtml('<div><my-component></my-component></div>');
523
+ // => '<div><my-component><style>...</style><template shadowrootmode="open">...</template>content</my-component></div>'
524
+
525
+ SSR.destroy(); // cleanup globals
526
+ ```
516
527
 
517
- await initSSR(); // patches globals with linkedom env
528
+ `processHtml` takes any HTML string, renders all Symbiote components found within, and returns the processed HTML. If `SSR.init()` was already called, it reuses the existing environment; otherwise it auto-initializes (and auto-destroys after).
518
529
 
519
- import './my-component.js'; // component reg() works normally
530
+ ### Advanced `renderToString` / `renderToStream`
520
531
 
521
- let html = renderToString('my-component', { title: 'Hello' });
532
+ ```js
533
+ import { SSR } from '@symbiotejs/symbiote/node/SSR.js';
534
+
535
+ await SSR.init();
536
+ await import('./my-component.js');
537
+
538
+ let html = SSR.renderToString('my-component', { title: 'Hello' });
522
539
  // => '<my-component title="Hello"><h1>Hello</h1></my-component>'
523
540
 
524
- destroySSR(); // cleanup globals
541
+ SSR.destroy();
525
542
  ```
526
543
 
527
544
  ### API
528
545
 
529
- | Function | Description |
530
- |----------|-------------|
531
- | `initSSR()` | `async` — creates linkedom document, polyfills CSSStyleSheet/NodeFilter/MutationObserver, patches globals |
532
- | `renderToString(tagName, attrs?)` | Creates element, triggers `connectedCallback`, serializes to HTML string. Shadow DOM DSD with inlined `<style>` |
533
- | `renderToStream(tagName, attrs?)` | Async generator yields HTML chunks as it walks the DOM tree. Same output as `renderToString`, but streamed for lower TTFB and memory |
534
- | `destroySSR()` | Removes global patches, cleans up document |
546
+ | Method | Description |
547
+ |--------|-------------|
548
+ | `SSR.init()` | `async` — creates linkedom document, polyfills CSSStyleSheet/NodeFilter/MutationObserver/adoptedStyleSheets, patches globals |
549
+ | `SSR.processHtml(html)` | `async` parses HTML string, renders all custom elements, returns processed HTML. Auto-inits if needed |
550
+ | `SSR.renderToString(tagName, attrs?)` | Creates element, triggers `connectedCallback`, serializes to HTML string |
551
+ | `SSR.renderToStream(tagName, attrs?)` | Async generator yields HTML chunks. Same output as `renderToString`, but streamed for lower TTFB |
552
+ | `SSR.destroy()` | Removes global patches, cleans up document |
553
+
554
+ ### Styles in SSR output
555
+
556
+ - **rootStyles** → `<style>` tag as first child of the component (light DOM, deduplicated per constructor)
557
+ - **shadowStyles** → `<style>` inside the Declarative Shadow DOM `<template>`
558
+ - Both are supported simultaneously on the same component
535
559
 
536
560
  ### Streaming usage
537
561
 
538
562
  ```js
539
563
  import http from 'node:http';
540
- import { initSSR, renderToStream } from '@symbiotejs/symbiote/core/ssr.js';
564
+ import { SSR } from '@symbiotejs/symbiote/node/SSR.js';
541
565
 
542
- await initSSR();
566
+ await SSR.init();
543
567
  import './my-app.js';
544
568
 
545
569
  http.createServer(async (req, res) => {
546
570
  res.writeHead(200, { 'Content-Type': 'text/html' });
547
571
  res.write('<!DOCTYPE html><html><body>');
548
- for await (let chunk of renderToStream('my-app')) {
572
+ for await (let chunk of SSR.renderToStream('my-app')) {
549
573
  res.write(chunk);
550
574
  }
551
575
  res.end('</body></html>');
@@ -554,19 +578,22 @@ http.createServer(async (req, res) => {
554
578
 
555
579
  ### Shadow DOM output
556
580
 
557
- Shadow components produce Declarative Shadow DOM markup with styles inlined:
581
+ Shadow components produce Declarative Shadow DOM markup with styles inlined. Light DOM content is preserved alongside the DSD template:
558
582
  ```html
559
583
  <my-shadow>
584
+ <style>my-shadow { display: block; }</style>
560
585
  <template shadowrootmode="open">
561
- <style>:host { display: block; }</style>
586
+ <style>:host { color: red; }</style>
562
587
  <h1>Content</h1>
588
+ <slot></slot>
563
589
  </template>
590
+ Light DOM content here
564
591
  </my-shadow>
565
592
  ```
566
593
 
567
594
  ### SSR context detection
568
595
 
569
- `initSSR()` sets `globalThis.__SYMBIOTE_SSR = true`. This is separate from the instance `ssrMode` flag:
596
+ `SSR.init()` sets `globalThis.__SYMBIOTE_SSR = true`. This is separate from the instance `ssrMode` flag:
570
597
 
571
598
  | Flag | Scope | Purpose |
572
599
  |------|-------|-------|
@@ -575,7 +602,7 @@ Shadow components produce Declarative Shadow DOM markup with styles inlined:
575
602
 
576
603
  ### Hydration flow
577
604
 
578
- 1. **Server**: `renderToString()` produces HTML with `bind=` / `itemize=` attributes preserved
605
+ 1. **Server**: `SSR.processHtml()` / `SSR.renderToString()` produces HTML with `bind=` / `itemize=` attributes preserved
579
606
  2. **Client**: component with `ssrMode = true` skips template injection, attaches bindings to pre-rendered DOM
580
607
  3. State mutations on client update DOM reactively
581
608
 
package/CHANGELOG.md CHANGED
@@ -1,43 +1,36 @@
1
1
  # Changelog
2
2
 
3
- ## 3.0.0-rc.1
3
+ ## 3.0.0
4
4
 
5
5
  ### ⚠️ Breaking Changes
6
6
 
7
- - **`tplProcessors` renamed to `templateProcessors`.**
8
- The instance property and its API now use the full name. The `addTemplateProcessor()` method has been removed — use native `Set` methods directly:
7
+ - **`tplProcessors` `templateProcessors`.**
8
+ The `addTemplateProcessor()` method is removed — use native `Set` methods:
9
9
  ```js
10
- // Before (2.x):
10
+ // 2.x:
11
11
  this.addTemplateProcessor(myProcessor);
12
-
13
- // After (3.x):
12
+ // 3.x:
14
13
  this.templateProcessors.add(myProcessor);
15
14
  ```
16
15
 
17
- - **`AppRouter.applyRoute()` renamed to `AppRouter.navigate()`.**
16
+ - **`AppRouter.applyRoute()` `AppRouter.navigate()`.**
18
17
 
19
18
  - **`AppRouter` removed from main entry point.**
20
- Now imported directly from its module:
21
19
  ```js
22
- // Before (2.x):
20
+ // 2.x:
23
21
  import { AppRouter } from '@symbiotejs/symbiote';
24
-
25
- // After (3.x):
22
+ // 3.x:
26
23
  import { AppRouter } from '@symbiotejs/symbiote/core/AppRouter.js';
27
24
  ```
28
25
 
29
- - **`#disconnectTimeout` renamed to `#destroyTimeout`.**
30
- Internal field renamed for clarity. No public API impact.
31
-
32
26
  - **Computed properties: cross-context requires explicit deps.**
33
- Computed properties that depend on external named contexts no longer auto-detect dependencies via global scan. Use the new object syntax:
27
+ Local computeds (same-context) keep working unchanged. Cross-context now uses object syntax:
34
28
  ```js
35
- // Before (2.x) — implicit, worked via global scan:
29
+ // 2.x — implicit via global scan:
36
30
  init$ = {
37
31
  '+total': () => this.$['APP/score'] + this.$.local,
38
32
  };
39
-
40
- // After (3.x) — explicit deps required:
33
+ // 3.x — explicit deps:
41
34
  init$ = {
42
35
  '+total': {
43
36
  deps: ['APP/score'],
@@ -45,57 +38,73 @@
45
38
  },
46
39
  };
47
40
  ```
48
- Local computed properties (depending only on same-context props) continue to work with function syntax unchanged.
41
+
42
+ - **Shared context (`*prop`) simplified.**
43
+ Removed `ctxOwner` / `ctx-owner`. First-registered value always wins. Dev-mode warnings when `*prop` used without `ctx` attribute.
49
44
 
50
45
  ### Performance
51
46
 
52
47
  - **Computed properties: per-instance dependency tracking.**
53
- Replaced global `#processComputed` scan with per-instance local dependency auto-tracking. `read()` now records which local props a computed function accesses, and only affected computeds are recalculated. Benchmarked up to **676x faster** for local computeds, **14x** for sparse scenarios.
48
+ Replaced global scan with per-instance auto-tracking. Up to **676× faster** for local computeds, **14×** for sparse scenarios.
54
49
 
55
- - **Microtask batching for computed recalculation.**
56
- Replaced `setTimeout`-based debounce with `queueMicrotask`, providing predictable async batching and eliminating per-computed timer overhead.
50
+ - **Microtask batching.**
51
+ All computed recalculation and internal scheduling uses `queueMicrotask` instead of `setTimeout`.
57
52
 
58
- ### Added
53
+ - **`#parseProp` fast path.**
54
+ `charCodeAt` checks skip full string parsing for common local properties — the most frequent case.
59
55
 
60
- - **`Symbiote.devMode` flag.**
61
- Enables verbose development warnings (unresolved template bindings, etc.). Default: `false`.
56
+ - **`$` proxy inlined fast path.**
57
+ Both `get` and `set` traps bypass `#parseProp` entirely for local props.
62
58
 
63
- - **Enhanced warning messages.**
64
- All warnings now use `[Symbiote]` prefix with component tag names, context UIDs, available contexts, and fix suggestions.
59
+ - **`PubSub.pub()` direct value pass.**
60
+ Eliminates redundant `read()` on every state update.
65
61
 
66
- - **`this` in template detection.**
67
- `html` tagged template now fires `console.error` when `${this.…}` is used in template interpolation (templates are context-free).
62
+ - **Dev warnings gated by `PubSub.devMode`.**
63
+ Type-mismatch checks have zero overhead in production.
68
64
 
69
- - **`reg()` returns the class itself.**
70
- Enables patterns like `export default MyComponent.reg('my-component')`.
65
+ - **`txtNodesProcessor` early exit.**
66
+ Skips text-node scanning when template contains no `{{` tokens.
71
67
 
72
- - **Declarative Shadow DOM hydration (`ssrMode`).**
73
- `ssrMode = true` (client-only) hydrates both light DOM and existing Declarative Shadow DOM (`<template shadowrootmode="open">`). Template injection is skipped; bindings attach to pre-rendered content. Shadow styles applied via `adoptedStyleSheets`. Bypassed on the server (`__SYMBIOTE_SSR`).
68
+ - **`localCtx` direct construction.**
69
+ Uses `new PubSub({})` instead of `PubSub.registerCtx({})`, bypassing global store for component-scoped state.
74
70
 
75
- - **Server-side rendering (`core/ssr.js`).**
76
- New SSR module: `initSSR()` creates a linkedom-backed DOM environment with polyfills (CSSStyleSheet, NodeFilter, MutationObserver) and sets `globalThis.__SYMBIOTE_SSR`. `renderToString(tagName, attrs?)` renders components to HTML with Declarative Shadow DOM and inlined `<style>`. `renderToStream(tagName, attrs?)` async generator yields HTML chunks for lower TTFB and memory usage. Binding attributes (`bind`, `ref`, `itemize`) are preserved in output for client-side hydration. `linkedom` is an optional peer dependency.
71
+ - **`hasOwnProperty` → `in` operator** across `PubSub` internals.
77
72
 
78
- - **Exit animation hook (`animateOut`).**
79
- New `animateOut(el)` utility and `Symbiote.animateOut` static method. Sets `[leaving]` attribute, waits for CSS `transitionend`, then removes element. Integrated into both itemize processors items with CSS transitions animate out automatically. Enter animations use native CSS `@starting-style`.
80
-
81
- ### Changed
73
+ - **`itemizeProcessor-keyed.js` optional optimized itemize processor.**
74
+ Drop-in replacement with reference-equality fast paths and key-based reconciliation. Up to **3× faster** for appends, **2×** for in-place updates, **32×** for no-ops:
75
+ ```js
76
+ import { itemizeProcessor } from '@symbiotejs/symbiote/core/itemizeProcessor-keyed.js';
77
+ import { itemizeProcessor as defaultProcessor } from '@symbiotejs/symbiote/core/itemizeProcessor.js';
82
78
 
83
- - **Shared context (`*prop`) simplified.**
84
- Removed `ctxOwner` property and `ctx-owner` attribute. First-registered value always wins (via `add()` without rewrite). Dev-mode warnings: (1) when `*prop` is used without `ctx` attribute or `--ctx` CSS variable, (2) when a later component tries to set a different initial value for the same shared prop.
79
+ class BigList extends Symbiote {
80
+ constructor() {
81
+ super();
82
+ this.templateProcessors.delete(defaultProcessor);
83
+ this.templateProcessors = new Set([itemizeProcessor, ...this.templateProcessors]);
84
+ }
85
+ }
86
+ ```
85
87
 
86
- - **Trusted Types support.**
87
- Template `innerHTML` writes now use a `'symbiote'` Trusted Types policy when the API is available. Compatible with `require-trusted-types-for 'script'` CSP headers. Zero overhead when Trusted Types not enabled.
88
+ ### Added
88
89
 
89
- ### Fixed
90
+ - **Server-side rendering (`node/SSR.js`).**
91
+ `SSR` class with static methods for server-side rendering. `SSR.processHtml(html)` renders any HTML string with embedded components. `SSR.renderToString(tagName, attrs?)` renders a single component. `SSR.renderToStream(tagName, attrs?)` async generator yields HTML chunks. Declarative Shadow DOM with inlined styles. rootStyles emitted as `<style>` tags in light DOM. Light DOM content preserved. Binding attributes preserved for client hydration. `linkedom` is an optional peer dependency.
92
+ ```js
93
+ import { SSR } from '@symbiotejs/symbiote/node/SSR.js';
94
+ await SSR.init();
95
+ await import('./my-app.js');
96
+ let html = await SSR.processHtml('<my-app>content</my-app>');
97
+ SSR.destroy();
98
+ ```
90
99
 
91
- - **`css()` tagged template trailing `undefined`.**
92
- `props[idx]` appended `"undefined"` when no interpolations exist. Fixed with `?? ''`.
100
+ - **Declarative Shadow DOM hydration (`ssrMode`).**
101
+ `ssrMode = true` hydrates pre-rendered content (light DOM + `<template shadowrootmode>`). Template injection skipped; bindings attach to existing DOM. Shadow styles applied via `adoptedStyleSheets`.
93
102
 
94
- - **`new DocumentFragment()` SSR compatibility.**
95
- Replaced with `document.createDocumentFragment()` in both `itemizeProcessor.js` and `itemizeProcessor-keyed.js` for linkedom compatibility.
103
+ - **Exit animation hook (`animateOut`).**
104
+ Sets `[leaving]` attribute, waits for CSS `transitionend`, removes element. Integrated into itemize processors — items with transitions animate out automatically. Enter animations use `@starting-style`.
96
105
 
97
106
  - **`AppRouter`: path-based routing.**
98
- Routes with a `pattern` key auto-switch to path-based URLs. Supports `:param` extraction:
107
+ Routes with `pattern` key use path-based URLs with `:param` extraction:
99
108
  ```js
100
109
  AppRouter.initRoutingCtx('R', {
101
110
  home: { pattern: '/', title: 'Home', default: true },
@@ -103,54 +112,47 @@
103
112
  });
104
113
  // /users/42 → { route: 'user', options: { id: '42' } }
105
114
  ```
106
- Routes without `pattern` keep working as query-string (full backward compatibility).
115
+ Query-string routes remain fully backward compatible.
107
116
 
108
- - **`AppRouter.beforeRoute(fn)` — route guards.**
109
- Register middleware that runs before navigation. Return `false` to cancel, a route string to redirect:
117
+ - **Route guards — `AppRouter.beforeRoute(fn)`.**
118
+ Return `false` to cancel, a route string to redirect:
110
119
  ```js
111
120
  let unsub = AppRouter.beforeRoute((to, from) => {
112
121
  if (!isAuth && to.route === 'settings') return 'login';
113
122
  });
114
123
  ```
115
124
 
116
- - **Lazy loaded route components.**
117
- Add `load` to route descriptors for dynamic imports. Loaded once, cached automatically:
125
+ - **Lazy loaded routes.**
126
+ `load` in route descriptors for dynamic imports, cached automatically:
118
127
  ```js
119
128
  { pattern: '/settings', load: () => import('./pages/settings.js') }
120
129
  ```
121
130
 
122
- - **AI_REFERENCE.md** — comprehensive AI context file for code assistants, covering full API surface, template syntax, state management, lifecycle, styling, routing, itemize, and common mistakes.
123
-
124
131
  - **Event handler method fallback.**
125
- `on*` bindings now fall back to class methods when no matching `init$` property is found:
132
+ `on*` bindings fall back to class methods when no `init$` property found:
126
133
  ```js
127
- // Works without init$ entry — class method is used as fallback:
128
134
  onSubmit() { console.log('submitted'); }
129
135
  ```
130
136
 
137
+ - **`Symbiote.devMode` flag.**
138
+ Enables verbose warnings (unresolved bindings, tag names, available contexts). Also wires `PubSub.devMode`.
139
+
140
+ - **`reg()` returns the class itself.**
141
+ Enables `export default MyComponent.reg('my-component')`.
142
+
131
143
  - **`destructionDelay` instance property.**
132
- Configurable delay (default `100`ms) before component destruction in `disconnectedCallback`. Override per-component to control cleanup timing:
133
- ```js
134
- class MyComponent extends Symbiote {
135
- destructionDelay = 0; // instant cleanup
136
- }
137
- ```
144
+ Configurable delay (default `100`ms) before cleanup in `disconnectedCallback`.
138
145
 
139
- - **`itemizeProcessor-keyed.js` optional optimized itemize processor.**
140
- Drop-in replacement with reference-equality fast paths and key-based reconciliation. Up to **3x faster** for appends, **2x** for in-place updates, **32x** for no-ops. Opt-in per component:
141
- ```js
142
- import { itemizeProcessor } from '@symbiotejs/symbiote/core/itemizeProcessor-keyed.js';
143
- import { itemizeProcessor as defaultProcessor } from '@symbiotejs/symbiote/core/itemizeProcessor.js';
146
+ - **Trusted Types support.**
147
+ Template writes use `'symbiote'` Trusted Types policy when available. Zero overhead otherwise.
144
148
 
145
- class BigList extends Symbiote {
146
- constructor() {
147
- super();
148
- this.templateProcessors.delete(defaultProcessor);
149
- this.templateProcessors = new Set([itemizeProcessor, ...this.templateProcessors]);
150
- }
151
- }
152
- ```
149
+ - **`this` in template detection.**
150
+ `html` fires `console.error` when `${this.…}` used in template (templates are context-free).
151
+
152
+ - **AI_REFERENCE.md** — comprehensive context file for code assistants.
153
153
 
154
- ### Internal
154
+ ### Fixed
155
155
 
156
- - Replaced `setTimeout` with `queueMicrotask` in prop binding race avoidance and async accessor handlers.
156
+ - `css()` trailing `undefined` when no interpolations exist.
157
+ - `new DocumentFragment()` → `document.createDocumentFragment()` for linkedom compatibility.
158
+ - `txtNodesProcessor` null check for `fr.textContent` in SSR environments.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021-present symbiotejs.org. All rights reserved.
3
+ Copyright (c) 2021-present rnd-pro.com. All rights reserved.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -11,7 +11,7 @@ Symbiote.js gives you the convenience of a modern framework while staying close
11
11
 
12
12
  ## What's new in 3.x
13
13
 
14
- - **Server-Side Rendering** — render components to HTML on the server with `renderToString()` or stream chunks with `renderToStream()`. Client-side hydration via `ssrMode` attaches bindings to existing DOM without re-rendering.
14
+ - **Server-Side Rendering** — render components to HTML with `SSR.processHtml()` or stream chunks with `SSR.renderToStream()`. Client-side hydration via `ssrMode` attaches bindings to existing DOM without re-rendering.
15
15
  - **Computed properties** — reactive derived state with microtask batching.
16
16
  - **Path-based router** — optional `AppRouter` module with `:param` extraction, route guards, and lazy loading.
17
17
  - **Exit animations** — `animateOut(el)` for CSS-driven exit transitions, integrated into itemize API.
@@ -60,16 +60,16 @@ import Symbiote, { html, css } from '@symbiotejs/symbiote';
60
60
 
61
61
  ## SSR — simpler than you'd expect
62
62
 
63
- Symbiote's SSR doesn't need a virtual DOM, a reconciler, or framework-specific server packages. It's three functions:
63
+ Symbiote's SSR doesn't need a virtual DOM, a reconciler, or framework-specific server packages. It's one class:
64
64
 
65
65
  ```js
66
- import { initSSR, renderToString, destroySSR } from '@symbiotejs/symbiote/core/ssr.js';
66
+ import { SSR } from '@symbiotejs/symbiote/node/SSR.js';
67
67
 
68
- await initSSR(); // patches globals with linkedom
69
- await import('./my-app.js'); // your components register normally
68
+ await SSR.init(); // patches globals with linkedom
69
+ await import('./my-app.js'); // your components register normally
70
70
 
71
- let html = renderToString('my-app'); // full HTML string
72
- destroySSR(); // cleanup
71
+ let html = await SSR.processHtml('<my-app>slot content</my-app>');
72
+ SSR.destroy(); // cleanup
73
73
  ```
74
74
 
75
75
  On the client, components with `ssrMode = true` skip template injection and attach bindings to the existing DOM. State mutations work immediately — no hydration step, no reconciliation, no diffing.
@@ -79,12 +79,15 @@ On the client, components with `ssrMode = true` skip template injection and atta
79
79
  For large pages, stream HTML chunks instead of building a string:
80
80
 
81
81
  ```js
82
- import { renderToStream } from '@symbiotejs/symbiote/core/ssr.js';
82
+ import { SSR } from '@symbiotejs/symbiote/node/SSR.js';
83
+
84
+ await SSR.init();
85
+ await import('./my-app.js');
83
86
 
84
87
  http.createServer(async (req, res) => {
85
88
  res.writeHead(200, { 'Content-Type': 'text/html' });
86
89
  res.write('<!DOCTYPE html><html><body>');
87
- for await (let chunk of renderToStream('my-app')) {
90
+ for await (let chunk of SSR.renderToStream('my-app')) {
88
91
  res.write(chunk);
89
92
  }
90
93
  res.end('</body></html>');
@@ -97,7 +100,7 @@ http.createServer(async (req, res) => {
97
100
  |--|----------------|---------------------|--------------------------|
98
101
  | **Architecture** | Binding-based. Client attaches to existing DOM | Virtual DOM. Client re-renders and diffs against server HTML | Template-based with comment markers |
99
102
  | **Hydration** | `ssrMode = true` — one flag, no diffing | `hydrateRoot()` — must produce identical output or errors | Requires `ssr-client` + hydrate support module loaded before Lit |
100
- | **Packages** | 1 module (`core/ssr.js`) + `linkedom` peer dep | Next.js framework (full buy-in) | 3 packages: `ssr`, `ssr-client`, `ssr-dom-shim` |
103
+ | **Packages** | 1 module (`node/SSR.js`) + `linkedom` peer dep | Next.js framework (full buy-in) | 3 packages: `ssr`, `ssr-client`, `ssr-dom-shim` |
101
104
  | **Streaming** | `renderToStream()` async generator | `renderToPipeableStream()` | Iterable `RenderResult` |
102
105
  | **Mismatch handling** | Not needed — bindings attach to whatever DOM exists | Hard errors or visual glitches if server/client output differs | N/A |
103
106
  | **Component code** | Same code, no changes | Server Components vs Client Components split | Same code, but load-order constraints |
@@ -1,5 +1,6 @@
1
1
  import { DICT } from './dictionary.js';
2
2
  import { animateOut } from './animateOut.js';
3
+ import { ownElements } from './ownElements.js';
3
4
 
4
5
  /**
5
6
  * Optimized itemize template processor.
@@ -29,7 +30,7 @@ import { animateOut } from './animateOut.js';
29
30
  * @param {T} fnCtx
30
31
  */
31
32
  export function itemizeProcessor(fr, fnCtx) {
32
- [...fr.querySelectorAll(`[${DICT.LIST_ATTR}]`)].filter((el) => {
33
+ ownElements(fr, `[${DICT.LIST_ATTR}]`).filter((el) => {
33
34
  return !el.matches(`[${DICT.LIST_ATTR}] [${DICT.LIST_ATTR}]`);
34
35
  }).forEach((el) => {
35
36
  let itemTag = el.getAttribute(DICT.LIST_ITEM_TAG_ATTR);
@@ -1,5 +1,6 @@
1
1
  import { DICT } from './dictionary.js';
2
2
  import { animateOut } from './animateOut.js';
3
+ import { ownElements } from './ownElements.js';
3
4
 
4
5
  /**
5
6
  * @template {import('./Symbiote.js').Symbiote} T
@@ -7,7 +8,7 @@ import { animateOut } from './animateOut.js';
7
8
  * @param {T} fnCtx
8
9
  */
9
10
  export function itemizeProcessor(fr, fnCtx) {
10
- [...fr.querySelectorAll(`[${DICT.LIST_ATTR}]`)].filter((el) => {
11
+ ownElements(fr, `[${DICT.LIST_ATTR}]`).filter((el) => {
11
12
  return !el.matches(`[${DICT.LIST_ATTR}] [${DICT.LIST_ATTR}]`);
12
13
  }).forEach((el) => {
13
14
  let itemTag = el.getAttribute(DICT.LIST_ITEM_TAG_ATTR);
@@ -0,0 +1,38 @@
1
+ /**
2
+ * querySelectorAll scoped to own elements only.
3
+ * During ssrMode hydration, `fr` is the component itself (an HTMLElement).
4
+ * querySelectorAll matches ALL descendants, including those inside child components.
5
+ * This helper excludes elements whose closest custom-element ancestor is not `fr`.
6
+ * @param {Element | DocumentFragment} fr
7
+ * @param {string} selector
8
+ * @returns {Element[]}
9
+ */
10
+ export function ownElements(fr, selector) {
11
+ let all = [...fr.querySelectorAll(selector)];
12
+ if (!(fr instanceof HTMLElement) || !fr.localName?.includes('-')) {
13
+ return all;
14
+ }
15
+ return all.filter((el) => {
16
+ let parent = el.parentElement;
17
+ while (parent && parent !== fr) {
18
+ if (parent.localName?.includes('-')) return false;
19
+ parent = parent.parentElement;
20
+ }
21
+ return true;
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Check if a node belongs to `root` and not to a nested custom element.
27
+ * @param {Node} node
28
+ * @param {Element} root
29
+ * @returns {boolean}
30
+ */
31
+ export function isOwnNode(node, root) {
32
+ let parent = node.parentElement;
33
+ while (parent && parent !== root) {
34
+ if (parent.localName?.includes('-')) return false;
35
+ parent = parent.parentElement;
36
+ }
37
+ return true;
38
+ }
@@ -1,16 +1,19 @@
1
1
  import { DICT } from './dictionary.js';
2
2
  import { setNestedProp } from '../utils/setNestedProp.js';
3
+ import { ownElements, isOwnNode } from './ownElements.js';
3
4
 
4
5
  // Should go first among other processors:
5
6
  import { itemizeProcessor } from './itemizeProcessor.js';
6
7
 
8
+
9
+
7
10
  /**
8
11
  * @template {import('./Symbiote.js').Symbiote} T
9
12
  * @param {DocumentFragment} fr
10
13
  * @param {T} fnCtx
11
14
  */
12
15
  function refProcessor(fr, fnCtx) {
13
- [...fr.querySelectorAll(`[${DICT.EL_REF_ATTR}]`)].forEach((/** @type {HTMLElement} */ el) => {
16
+ ownElements(fr, `[${DICT.EL_REF_ATTR}]`).forEach((/** @type {HTMLElement} */ el) => {
14
17
  let refName = el.getAttribute(DICT.EL_REF_ATTR);
15
18
  fnCtx.ref[refName] = el;
16
19
  if (!globalThis.__SYMBIOTE_SSR) {
@@ -25,7 +28,7 @@ function refProcessor(fr, fnCtx) {
25
28
  * @param {T} fnCtx
26
29
  */
27
30
  function domBindProcessor(fr, fnCtx) {
28
- [...fr.querySelectorAll(`[${DICT.BIND_ATTR}]`)].forEach((el) => {
31
+ ownElements(fr, `[${DICT.BIND_ATTR}]`).forEach((el) => {
29
32
  let subStr = el.getAttribute(DICT.BIND_ATTR);
30
33
  let keyValArr = subStr.split(';');
31
34
  keyValArr.forEach((keyValStr) => {
@@ -104,10 +107,12 @@ function domBindProcessor(fr, fnCtx) {
104
107
  }
105
108
 
106
109
  function getTextNodesWithTokens(el) {
110
+ let isCustomEl = el instanceof HTMLElement && el.localName?.includes('-');
107
111
  let node;
108
112
  let result = [];
109
113
  let walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, {
110
114
  acceptNode: (txt) => {
115
+ if (isCustomEl && !isOwnNode(txt, el)) return NodeFilter.FILTER_REJECT;
111
116
  return !txt.parentElement?.hasAttribute(DICT.TEXT_NODE_SKIP_ATTR)
112
117
  && txt.textContent.includes(DICT.TEXT_NODE_OPEN_TOKEN)
113
118
  && txt.textContent.includes(DICT.TEXT_NODE_CLOSE_TOKEN)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@symbiotejs/symbiote",
4
- "version": "3.0.0-next.3",
4
+ "version": "3.0.2",
5
5
  "description": "Symbiote.js - zero-dependency close-to-platform frontend library to build super-powered web components",
6
6
  "author": "team@rnd-pro.com",
7
7
  "license": "MIT",
@@ -9,10 +9,10 @@
9
9
  "prepare": "git config core.hooksPath .git-hooks",
10
10
  "types": "rm -rf types && tsc -p dts.cfg.json && node scripts/clean-dts.js",
11
11
  "prepublishOnly": "npm test",
12
- "publish": "npm run types && node scripts/update-exports.js && npm publish --tag next",
12
+ "pub": "npm run types && node scripts/update-exports.js && npm publish --tag beta",
13
13
  "postinstall": "node scripts/postinstall.js",
14
- "test": "node --test test/*.test.js && npx playwright test",
15
- "test:unit": "node --test test/*.test.js",
14
+ "test": "node --test test/node/*.test.js && npx playwright test",
15
+ "test:unit": "node --test test/node/*.test.js",
16
16
  "test:browser": "npx playwright test"
17
17
  },
18
18
  "files": [
@@ -68,14 +68,14 @@
68
68
  "types": "./types/core/itemizeProcessor.d.ts",
69
69
  "default": "./core/itemizeProcessor.js"
70
70
  },
71
+ "./core/ownElements.js": {
72
+ "types": "./types/core/ownElements.d.ts",
73
+ "default": "./core/ownElements.js"
74
+ },
71
75
  "./core/slotProcessor.js": {
72
76
  "types": "./types/core/slotProcessor.d.ts",
73
77
  "default": "./core/slotProcessor.js"
74
78
  },
75
- "./core/ssr.js": {
76
- "types": "./types/core/ssr.d.ts",
77
- "default": "./core/ssr.js"
78
- },
79
79
  "./core/tpl-processors.js": {
80
80
  "types": "./types/core/tpl-processors.d.ts",
81
81
  "default": "./core/tpl-processors.js"
@@ -122,7 +122,11 @@
122
122
  },
123
123
  "keywords": [
124
124
  "web components",
125
+ "SSR",
126
+ "server side rendering for web components",
125
127
  "ui library",
128
+ "data flow",
129
+ "design system",
126
130
  "symbiote.js",
127
131
  "symbiote",
128
132
  "widget",
@@ -1 +1 @@
1
- {"version":3,"file":"itemizeProcessor-keyed.d.ts","sourceRoot":"","sources":["../../core/itemizeProcessor-keyed.js"],"names":[],"mappings":"AA8BA,iCAJgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC,QA6MX"}
1
+ {"version":3,"file":"itemizeProcessor-keyed.d.ts","sourceRoot":"","sources":["../../core/itemizeProcessor-keyed.js"],"names":[],"mappings":"AA+BA,iCAJgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC,QA6MX"}
@@ -1 +1 @@
1
- {"version":3,"file":"itemizeProcessor.d.ts","sourceRoot":"","sources":["../../core/itemizeProcessor.js"],"names":[],"mappings":"AAQA,iCAJgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC,QAoFX"}
1
+ {"version":3,"file":"itemizeProcessor.d.ts","sourceRoot":"","sources":["../../core/itemizeProcessor.js"],"names":[],"mappings":"AASA,iCAJgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC,QAoFX"}
@@ -0,0 +1,3 @@
1
+ export function ownElements(fr: Element | DocumentFragment, selector: string): Element[];
2
+ export function isOwnNode(node: Node, root: Element): boolean;
3
+ //# sourceMappingURL=ownElements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownElements.d.ts","sourceRoot":"","sources":["../../core/ownElements.js"],"names":[],"mappings":"AASA,gCAJW,OAAO,GAAG,gBAAgB,YAC1B,MAAM,GACJ,OAAO,EAAE,CAerB;AAQD,gCAJW,IAAI,QACJ,OAAO,GACL,OAAO,CASnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"tpl-processors.d.ts","sourceRoot":"","sources":["../../core/tpl-processors.js"],"names":[],"mappings":"0BA2HgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC"}
1
+ {"version":3,"file":"tpl-processors.d.ts","sourceRoot":"","sources":["../../core/tpl-processors.js"],"names":[],"mappings":"0BAgIgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC"}
@@ -0,0 +1,16 @@
1
+ export class SSR {
2
+ static init(): Promise<{
3
+ document: any;
4
+ window: any;
5
+ }>;
6
+ static destroy(): void;
7
+ static processHtml(html: string): Promise<string>;
8
+ static renderToString(tagName: string, attrs?: {
9
+ [x: string]: string;
10
+ }): string;
11
+ static renderToStream(tagName: string, attrs?: {
12
+ [x: string]: string;
13
+ }): AsyncGenerator<string>;
14
+ }
15
+ export default SSR;
16
+ //# sourceMappingURL=SSR.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SSR.d.ts","sourceRoot":"","sources":["../../node/SSR.js"],"names":[],"mappings":"AAuPA;IAEE,8BAAmB;IACnB,8BAAmB;IAMnB;;;OAkEC;IAMD,uBAeC;IAeD,yBATW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAwB3B;IAUD,+BAJW,MAAM;;QAEJ,MAAM,CAclB;IAUD,+BAJW,MAAM;;QAEJ,cAAc,CAAC,MAAM,CAAC,CAalC;CACF"}
package/core/README.md DELETED
@@ -1,31 +0,0 @@
1
- ## Folder contents
2
-
3
- ### index.js
4
- All-in-one exports.
5
-
6
- ### Symbiote.js
7
- Base component class. Major utility for the web-component creation, template data binding and data management.
8
-
9
- ### html.js
10
- Template literal tag-function, that transforms interpolated binding descriptions into resulting html.
11
-
12
- ### css.js
13
- Template literal tag-function, that creates the CSSStyleSheet instance.
14
-
15
- ### PubSub.js
16
- Implements data layer for the local component context and the top level context both. The state management approach is based on simple well known pub/sub pattern.
17
-
18
- ### AppRouter.js
19
- SPA routing utility. Based on browser-native History API.
20
-
21
- ### tpl-rpcessors.js
22
- Template processing functions. Implements basic template processing flow.
23
-
24
- ### itemizeProcessor.js
25
- Dynamic list items rendering implementation.
26
-
27
- ### dictionary.js
28
- Dictionary for the set of the basic keys.
29
-
30
- ### slotProcessor.js
31
- Light DOM support for the template `slot`s. This processor is optional since 2.x. and excluded from default template processing pipeline.
package/core/ssr.js DELETED
@@ -1,313 +0,0 @@
1
- /**
2
- * @module ssr
3
- * Server-side rendering for Symbiote.js components.
4
- * Requires `linkedom` as a peer dependency.
5
- *
6
- * Usage:
7
- * ```js
8
- * import { initSSR, renderToString } from '@symbiotejs/symbiote/core/ssr.js';
9
- * initSSR();
10
- * import './my-component.js';
11
- * let html = renderToString('my-component', { title: 'Hello' });
12
- * ```
13
- */
14
-
15
- let ssrDocument = null;
16
- let ssrWindow = null;
17
-
18
- /**
19
- * Initialize the SSR environment using linkedom.
20
- * Must be called before importing any Symbiote components.
21
- */
22
- export async function initSSR() {
23
- // @ts-ignore
24
- let { parseHTML } = /** @type {any} */ (await import('linkedom'));
25
- let { document, window, HTMLElement, customElements, DocumentFragment, NodeFilter, MutationObserver } = parseHTML('<!DOCTYPE html><html><head></head><body></body></html>');
26
-
27
- ssrDocument = document;
28
- ssrWindow = window;
29
-
30
- // Polyfill CSSStyleSheet for linkedom:
31
- if (!window.CSSStyleSheet || !('replaceSync' in (window.CSSStyleSheet?.prototype || {}))) {
32
- class SSRStyleSheet {
33
- #cssText = '';
34
- replaceSync(text) {
35
- this.#cssText = text;
36
- }
37
- replace(text) {
38
- this.#cssText = text;
39
- return Promise.resolve(this);
40
- }
41
- get cssText() {
42
- return this.#cssText;
43
- }
44
- get cssRules() {
45
- return [];
46
- }
47
- }
48
- // @ts-ignore — SSR polyfill doesn't need full CSSStyleSheet interface
49
- window.CSSStyleSheet = SSRStyleSheet;
50
- // @ts-ignore
51
- globalThis.CSSStyleSheet = SSRStyleSheet;
52
- }
53
-
54
- // Polyfill NodeFilter (linkedom may not expose it):
55
- let nodeFilter = NodeFilter || {
56
- SHOW_ALL: 0xFFFFFFFF,
57
- SHOW_ELEMENT: 0x1,
58
- SHOW_TEXT: 0x4,
59
- SHOW_COMMENT: 0x80,
60
- FILTER_ACCEPT: 1,
61
- FILTER_REJECT: 2,
62
- FILTER_SKIP: 3,
63
- };
64
-
65
- // Polyfill MutationObserver (not needed for SSR one-shot render):
66
- let mutationObserver = MutationObserver || class {
67
- observe() {}
68
- disconnect() {}
69
- takeRecords() { return []; }
70
- };
71
-
72
- // Patch globals:
73
- globalThis.__SYMBIOTE_SSR = true;
74
- globalThis.document = document;
75
- globalThis.window = window;
76
- globalThis.HTMLElement = HTMLElement;
77
- globalThis.customElements = customElements;
78
- globalThis.DocumentFragment = DocumentFragment;
79
- globalThis.NodeFilter = nodeFilter;
80
- globalThis.MutationObserver = mutationObserver;
81
-
82
- return { document, window };
83
- }
84
-
85
- /**
86
- * Extract CSS text from a stylesheet (works with both CSSStyleSheet and SSR polyfill).
87
- * @param {CSSStyleSheet | {cssText: string}} sheet
88
- * @returns {string}
89
- */
90
- function extractCSS(sheet) {
91
- if ('cssText' in sheet && typeof sheet.cssText === 'string') {
92
- return sheet.cssText;
93
- }
94
- // Standard CSSStyleSheet — extract from cssRules:
95
- let text = '';
96
- try {
97
- // @ts-ignore — cssRules may not exist on SSR polyfill
98
- for (let rule of sheet.cssRules) {
99
- text += rule.cssText + '\n';
100
- }
101
- } catch {
102
- // Security restrictions on some stylesheets
103
- }
104
- return text;
105
- }
106
-
107
- /**
108
- * Render a Symbiote component to an HTML string.
109
- * @param {string} tagName - Custom element tag name
110
- * @param {Object<string, string>} [attrs] - Attributes to set on the element
111
- * @returns {string} HTML string with Declarative Shadow DOM if applicable
112
- */
113
- export function renderToString(tagName, attrs = {}) {
114
- if (!ssrDocument) {
115
- throw new Error('[Symbiote SSR] Call initSSR() before renderToString()');
116
- }
117
-
118
- let el = ssrDocument.createElement(tagName);
119
-
120
- for (let [key, val] of Object.entries(attrs)) {
121
- el.setAttribute(key, String(val));
122
- }
123
-
124
- // Trigger component lifecycle:
125
- ssrDocument.body.appendChild(el);
126
-
127
- // Build output HTML:
128
- let html = serializeElement(el);
129
-
130
- // Cleanup:
131
- el.remove();
132
-
133
- return html;
134
- }
135
-
136
- /**
137
- * Serialize a custom element to HTML with DSD support.
138
- * @param {HTMLElement} el
139
- * @returns {string}
140
- */
141
- function serializeElement(el) {
142
- let tagName = el.localName;
143
- let attrsStr = '';
144
- if (el.attributes) {
145
- for (let attr of el.attributes) {
146
- attrsStr += ` ${attr.name}="${attr.value}"`;
147
- }
148
- }
149
-
150
- let innerContent = '';
151
-
152
- // Declarative Shadow DOM:
153
- if (el.shadowRoot) {
154
- let shadowHTML = '';
155
-
156
- // Inline shadow styles:
157
- let ctor = /** @type {any} */ (el).constructor;
158
- if (ctor.shadowStyleSheets) {
159
- for (let sheet of ctor.shadowStyleSheets) {
160
- let cssText = extractCSS(sheet);
161
- if (cssText) {
162
- shadowHTML += `<style>${cssText}</style>`;
163
- }
164
- }
165
- }
166
-
167
- shadowHTML += el.shadowRoot.innerHTML;
168
- innerContent += `<template shadowrootmode="open">${shadowHTML}</template>`;
169
- }
170
-
171
- // Light DOM content:
172
- if (!el.shadowRoot) {
173
- innerContent += el.innerHTML;
174
- }
175
-
176
- return `<${tagName}${attrsStr}>${innerContent}</${tagName}>`;
177
- }
178
-
179
- /**
180
- * Render a Symbiote component to a stream of HTML chunks.
181
- * @param {string} tagName - Custom element tag name
182
- * @param {Object<string, string>} [attrs] - Attributes to set on the element
183
- * @returns {AsyncGenerator<string>}
184
- */
185
- export async function* renderToStream(tagName, attrs = {}) {
186
- if (!ssrDocument) {
187
- throw new Error('[Symbiote SSR] Call initSSR() before renderToStream()');
188
- }
189
-
190
- let el = ssrDocument.createElement(tagName);
191
-
192
- for (let [key, val] of Object.entries(attrs)) {
193
- el.setAttribute(key, String(val));
194
- }
195
-
196
- // Trigger component lifecycle:
197
- ssrDocument.body.appendChild(el);
198
-
199
- // Stream the element:
200
- yield* streamElement(el);
201
-
202
- // Cleanup:
203
- el.remove();
204
- }
205
-
206
- /**
207
- * Stream-serialize an element, yielding chunks.
208
- * Custom element children are recursed into for granular streaming.
209
- * @param {HTMLElement} el
210
- * @returns {AsyncGenerator<string>}
211
- */
212
- async function* streamElement(el) {
213
- let tagName = el.localName;
214
- let attrsStr = '';
215
- if (el.attributes) {
216
- for (let attr of el.attributes) {
217
- attrsStr += ` ${attr.name}="${attr.value}"`;
218
- }
219
- }
220
-
221
- yield `<${tagName}${attrsStr}>`;
222
-
223
- // Declarative Shadow DOM:
224
- if (el.shadowRoot) {
225
- yield '<template shadowrootmode="open">';
226
-
227
- let ctor = /** @type {any} */ (el).constructor;
228
- if (ctor.shadowStyleSheets) {
229
- for (let sheet of ctor.shadowStyleSheets) {
230
- let cssText = extractCSS(sheet);
231
- if (cssText) {
232
- yield `<style>${cssText}</style>`;
233
- }
234
- }
235
- }
236
-
237
- // Stream shadow DOM children:
238
- for (let child of el.shadowRoot.childNodes) {
239
- yield* streamNode(child);
240
- }
241
-
242
- yield '</template>';
243
- } else {
244
- // Stream light DOM children:
245
- for (let child of el.childNodes) {
246
- yield* streamNode(child);
247
- }
248
- }
249
-
250
- yield `</${tagName}>`;
251
- }
252
-
253
- /**
254
- * Stream-serialize a DOM node.
255
- * @param {Node} node
256
- * @returns {AsyncGenerator<string>}
257
- */
258
- async function* streamNode(node) {
259
- // Custom element — recurse for granular streaming:
260
- if (node.nodeType === 1 && /** @type {Element} */ (node).localName?.includes('-')) {
261
- yield* streamElement(/** @type {HTMLElement} */ (node));
262
- return;
263
- }
264
- // Regular element with children:
265
- if (node.nodeType === 1) {
266
- let el = /** @type {HTMLElement} */ (node);
267
- let attrsStr = '';
268
- if (el.attributes) {
269
- for (let attr of el.attributes) {
270
- attrsStr += ` ${attr.name}="${attr.value}"`;
271
- }
272
- }
273
- if (el.childNodes.length) {
274
- yield `<${el.localName}${attrsStr}>`;
275
- for (let child of el.childNodes) {
276
- yield* streamNode(child);
277
- }
278
- yield `</${el.localName}>`;
279
- } else {
280
- yield `<${el.localName}${attrsStr}>${el.innerHTML}</${el.localName}>`;
281
- }
282
- return;
283
- }
284
- // Text node:
285
- if (node.nodeType === 3) {
286
- yield node.textContent || '';
287
- return;
288
- }
289
- // Comment node:
290
- if (node.nodeType === 8) {
291
- yield `<!--${node.textContent}-->`;
292
- }
293
- }
294
-
295
- /**
296
- * Clean up the SSR environment.
297
- */
298
- export function destroySSR() {
299
- if (ssrDocument) {
300
- ssrDocument.body.innerHTML = '';
301
- }
302
- delete globalThis.__SYMBIOTE_SSR;
303
- delete globalThis.document;
304
- delete globalThis.window;
305
- delete globalThis.HTMLElement;
306
- delete globalThis.customElements;
307
- delete globalThis.DocumentFragment;
308
- delete globalThis.NodeFilter;
309
- delete globalThis.MutationObserver;
310
- delete globalThis.CSSStyleSheet;
311
- ssrDocument = null;
312
- ssrWindow = null;
313
- }
@@ -1,12 +0,0 @@
1
- export function initSSR(): Promise<{
2
- document: any;
3
- window: any;
4
- }>;
5
- export function renderToString(tagName: string, attrs?: {
6
- [x: string]: string;
7
- }): string;
8
- export function renderToStream(tagName: string, attrs?: {
9
- [x: string]: string;
10
- }): AsyncGenerator<string>;
11
- export function destroySSR(): void;
12
- //# sourceMappingURL=ssr.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../../core/ssr.js"],"names":[],"mappings":"AAqBA;;;GA6DC;AA8BD,wCAJW,MAAM;;IAEJ,MAAM,CAuBlB;AAmDD,wCAJW,MAAM;;IAEJ,cAAc,CAAC,MAAM,CAAC,CAqBlC;AA8FD,mCAeC"}