foldkit 0.145.0 → 0.147.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/customElement/index.d.ts.map +1 -1
- package/dist/customElement/index.js +23 -0
- package/dist/experimental/index.d.ts +1 -0
- package/dist/experimental/index.d.ts.map +1 -1
- package/dist/experimental/index.js +1 -0
- package/dist/experimental/machine/machine.d.ts +22 -3
- package/dist/experimental/machine/machine.d.ts.map +1 -1
- package/dist/experimental/machine/machine.js +8 -0
- package/dist/experimental/server/entry.d.ts +73 -0
- package/dist/experimental/server/entry.d.ts.map +1 -0
- package/dist/experimental/server/entry.js +41 -0
- package/dist/experimental/server/host.d.ts +42 -0
- package/dist/experimental/server/host.d.ts.map +1 -0
- package/dist/experimental/server/host.js +169 -0
- package/dist/experimental/server/index.d.ts +5 -0
- package/dist/experimental/server/index.d.ts.map +1 -0
- package/dist/experimental/server/index.js +4 -0
- package/dist/experimental/server/public.d.ts +3 -0
- package/dist/experimental/server/public.d.ts.map +1 -0
- package/dist/experimental/server/public.js +1 -0
- package/dist/experimental/server/serialize.d.ts +33 -0
- package/dist/experimental/server/serialize.d.ts.map +1 -0
- package/dist/experimental/server/serialize.js +563 -0
- package/dist/experimental/server/server.d.ts +201 -0
- package/dist/experimental/server/server.d.ts.map +1 -0
- package/dist/experimental/server/server.js +423 -0
- package/dist/experimental/server/template.d.ts +45 -0
- package/dist/experimental/server/template.d.ts.map +1 -0
- package/dist/experimental/server/template.js +182 -0
- package/dist/html/index.d.ts +5 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +38 -4
- package/dist/hydrate.d.ts +4 -0
- package/dist/hydrate.d.ts.map +1 -0
- package/dist/hydrate.js +522 -0
- package/dist/hydrationMarker.d.ts +10 -0
- package/dist/hydrationMarker.d.ts.map +1 -0
- package/dist/hydrationMarker.js +9 -0
- package/dist/mount/index.d.ts +1 -1
- package/dist/runtime/public.d.ts +2 -2
- package/dist/runtime/public.d.ts.map +1 -1
- package/dist/runtime/public.js +1 -1
- package/dist/runtime/runtime.d.ts +75 -36
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +209 -71
- package/dist/snabbdom/h.d.ts +1 -0
- package/dist/snabbdom/h.d.ts.map +1 -1
- package/dist/snabbdom/h.js +85 -4
- package/dist/snabbdom/tovnode.d.ts.map +1 -1
- package/dist/snabbdom/tovnode.js +5 -1
- package/dist/tagName.d.ts +6 -0
- package/dist/tagName.d.ts.map +1 -0
- package/dist/tagName.js +11 -0
- package/dist/vdom.d.ts.map +1 -1
- package/dist/vdom.js +25 -1
- package/package.json +11 -4
package/dist/html/index.js
CHANGED
|
@@ -58,6 +58,16 @@ const keyboardModifiers = (event) => ({
|
|
|
58
58
|
/** Text direction for the document root, applied to `dir` on the `<html>`
|
|
59
59
|
* element. `Auto` defers to the browser's first-strong-character heuristic. */
|
|
60
60
|
export const TextDirection = S.Literals(['Ltr', 'Rtl', 'Auto']);
|
|
61
|
+
const textDirectionAttributes = {
|
|
62
|
+
Ltr: 'ltr',
|
|
63
|
+
Rtl: 'rtl',
|
|
64
|
+
Auto: 'auto',
|
|
65
|
+
};
|
|
66
|
+
/** Maps a {@link TextDirection} to the lowercase value written to the `dir`
|
|
67
|
+
* attribute on the `<html>` element. Shared by the client runtime, which sets
|
|
68
|
+
* it after each render, and server rendering, which stamps it into the served
|
|
69
|
+
* shell so the direction is correct on first paint. */
|
|
70
|
+
export const textDirectionToAttribute = (direction) => textDirectionAttributes[direction];
|
|
61
71
|
const onMountStates = new WeakMap();
|
|
62
72
|
// NOTE: snabbdom `destroy` hooks fire during the patch that removes an element,
|
|
63
73
|
// and the hook on the OLD (being-removed) VNode was built in a prior live
|
|
@@ -197,6 +207,30 @@ const classObjectFor = (value) => {
|
|
|
197
207
|
classObjectCache.set(value, classObject);
|
|
198
208
|
return classObject;
|
|
199
209
|
};
|
|
210
|
+
// NOTE: navigation and resource URL attributes (href, src, action,
|
|
211
|
+
// formaction) execute script when their scheme is `javascript:` or
|
|
212
|
+
// `vbscript:`, so an untrusted value bound to them is an XSS sink. Browsers
|
|
213
|
+
// ignore ASCII control characters embedded in a scheme (`java\tscript:`
|
|
214
|
+
// still runs), so those are stripped before the scheme is read. A dangerous
|
|
215
|
+
// scheme neutralizes to an empty value; every other URL, including relative
|
|
216
|
+
// paths, http(s), mailto, tel, and data URLs, passes through unchanged.
|
|
217
|
+
const DANGEROUS_URL_SCHEMES = new Set([
|
|
218
|
+
'javascript',
|
|
219
|
+
'vbscript',
|
|
220
|
+
]);
|
|
221
|
+
const URL_CONTROL_CHARACTERS = /[\u0000-\u001F\u007F-\u009F]/g;
|
|
222
|
+
const URL_SCHEME_PATTERN = /^\s*([a-zA-Z][a-zA-Z0-9+.-]*)\s*:/;
|
|
223
|
+
const sanitizeUrl = (value) => {
|
|
224
|
+
const match = URL_SCHEME_PATTERN.exec(value.replace(URL_CONTROL_CHARACTERS, ''));
|
|
225
|
+
if (match !== null) {
|
|
226
|
+
const scheme = match[1];
|
|
227
|
+
if (scheme !== undefined &&
|
|
228
|
+
DANGEROUS_URL_SCHEMES.has(scheme.toLowerCase())) {
|
|
229
|
+
return '';
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return value;
|
|
233
|
+
};
|
|
200
234
|
const attributeHandlers = {
|
|
201
235
|
Key: ({ value }, ctx) => setData(ctx, 'key', value),
|
|
202
236
|
Class: ({ value }, ctx) => setModuleData(ctx, 'class', classObjectFor(value), VNodeDataMask.Class),
|
|
@@ -511,17 +545,17 @@ const attributeHandlers = {
|
|
|
511
545
|
Min: ({ value }, ctx) => setDataProp(ctx, 'min', value),
|
|
512
546
|
Step: ({ value }, ctx) => setDataProp(ctx, 'step', value),
|
|
513
547
|
For: ({ value }, ctx) => setDataProp(ctx, 'htmlFor', value),
|
|
514
|
-
Href: ({ value }, ctx) => setDataProp(ctx, 'href', value),
|
|
515
|
-
Src: ({ value }, ctx) => setDataProp(ctx, 'src', value),
|
|
548
|
+
Href: ({ value }, ctx) => setDataProp(ctx, 'href', sanitizeUrl(value)),
|
|
549
|
+
Src: ({ value }, ctx) => setDataProp(ctx, 'src', sanitizeUrl(value)),
|
|
516
550
|
Alt: ({ value }, ctx) => setDataProp(ctx, 'alt', value),
|
|
517
551
|
Target: ({ value }, ctx) => setDataProp(ctx, 'target', value),
|
|
518
552
|
Rel: ({ value }, ctx) => setDataProp(ctx, 'rel', value),
|
|
519
553
|
Download: ({ value }, ctx) => setDataProp(ctx, 'download', value),
|
|
520
|
-
Action: ({ value }, ctx) => setDataProp(ctx, 'action', value),
|
|
554
|
+
Action: ({ value }, ctx) => setDataProp(ctx, 'action', sanitizeUrl(value)),
|
|
521
555
|
Method: ({ value }, ctx) => setDataProp(ctx, 'method', value),
|
|
522
556
|
Enctype: ({ value }, ctx) => setDataProp(ctx, 'enctype', value),
|
|
523
557
|
Novalidate: ({ value }, ctx) => setDataProp(ctx, 'noValidate', value),
|
|
524
|
-
Formaction: ({ value }, ctx) => setDataProp(ctx, 'formAction', value),
|
|
558
|
+
Formaction: ({ value }, ctx) => setDataProp(ctx, 'formAction', sanitizeUrl(value)),
|
|
525
559
|
Formmethod: ({ value }, ctx) => setDataProp(ctx, 'formMethod', value),
|
|
526
560
|
Formnovalidate: ({ value }, ctx) => setDataProp(ctx, 'formNoValidate', value),
|
|
527
561
|
Formtarget: ({ value }, ctx) => setDataProp(ctx, 'formTarget', value),
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { VNode } from './snabbdom/index.js';
|
|
2
|
+
export declare const __elementSignature: (element: Element, vnode: VNode) => string;
|
|
3
|
+
export declare const __hydrateVNode: (hydrationRoot: Element, nextVNode: VNode | null, seen?: Set<object>) => VNode;
|
|
4
|
+
//# sourceMappingURL=hydrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../src/hydrate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAsJhD,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,EAAE,OAAO,KAAK,KAAG,MA6BnE,CAAA;AAsbD,eAAO,MAAM,cAAc,GACzB,eAAe,OAAO,EACtB,WAAW,KAAK,GAAG,IAAI,EACvB,OAAO,GAAG,CAAC,MAAM,CAAC,KACjB,KAwCF,CAAA"}
|
package/dist/hydrate.js
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
import { Option } from 'effect';
|
|
2
|
+
import { h, toVNode } from './snabbdom/index.js';
|
|
3
|
+
import { tagNameFromSelector } from './tagName.js';
|
|
4
|
+
import { dedupeSharedVNodes, patch } from './vdom.js';
|
|
5
|
+
// NOTE: the differ only knows the page through the `elm` pointers on its
|
|
6
|
+
// vnodes; `patch` never queries the document. On a server-rendered page the
|
|
7
|
+
// browser has already built real DOM by parsing the HTML the server sent
|
|
8
|
+
// (the "server DOM" below), but snabbdom did not create those nodes, so no
|
|
9
|
+
// vnode anywhere points at them and the differ cannot see them. Hydration's
|
|
10
|
+
// whole job is to make that DOM visible to the differ, then let one
|
|
11
|
+
// ordinary patch attach behavior to it.
|
|
12
|
+
//
|
|
13
|
+
// The mechanism has three steps. First, walk the first render's vnode tree
|
|
14
|
+
// and the server DOM together, position by position. Second, wherever the
|
|
15
|
+
// two agree, record the existing DOM node: those records form a second
|
|
16
|
+
// vnode tree, a clone of the first render's tree whose `elm` fields point
|
|
17
|
+
// at the server DOM nodes. The clone copies exactly what `sameVnode`
|
|
18
|
+
// compares (`sel`, `key`, `identity`, `data.is`), so `patchVnode` reuses
|
|
19
|
+
// every adopted element, and deliberately nothing else, so every module
|
|
20
|
+
// update hook re-asserts the new tree's attrs, props, classes, styles, and
|
|
21
|
+
// listeners onto the adopted elements. Third, run `patch(clone, newTree)`:
|
|
22
|
+
// to the differ this is a completely ordinary update from a tree that
|
|
23
|
+
// happens to point at existing nodes, so the vendored differ stays
|
|
24
|
+
// untouched and never learns the nodes came from a server.
|
|
25
|
+
//
|
|
26
|
+
// Where the DOM disagrees with the vnode tree, the walk clears the nearest
|
|
27
|
+
// parent's children and hands `patch` an empty child list, so the subtree is
|
|
28
|
+
// rebuilt through `createElm`, which is exactly the pre-hydration replace
|
|
29
|
+
// behavior scoped to the mismatching subtree. Trailing vnode children with
|
|
30
|
+
// no DOM counterpart are simply absent from the clone; `updateChildren`
|
|
31
|
+
// appends them.
|
|
32
|
+
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
33
|
+
const HYDRATION_STAMP_ATTRIBUTE = 'data-foldkit-app';
|
|
34
|
+
// The style module keys regular properties in camelCase (`backgroundColor`)
|
|
35
|
+
// and custom properties as written (`--accent`), so a property read from the
|
|
36
|
+
// DOM in kebab case is converted to the module's key form before seeding.
|
|
37
|
+
const styleModuleKey = (property) => property.startsWith('--')
|
|
38
|
+
? property
|
|
39
|
+
: property.replace(/-([a-z])/g, (_match, character) => character.toUpperCase());
|
|
40
|
+
const classListOf = (element) => {
|
|
41
|
+
const classes = {};
|
|
42
|
+
for (const className of Array.from(element.classList)) {
|
|
43
|
+
classes[className] = true;
|
|
44
|
+
}
|
|
45
|
+
return classes;
|
|
46
|
+
};
|
|
47
|
+
const inlineStyleOf = (element) => {
|
|
48
|
+
const style = {};
|
|
49
|
+
if (element instanceof HTMLElement || element instanceof SVGElement) {
|
|
50
|
+
const inlineStyle = element.style;
|
|
51
|
+
for (let index = 0; index < inlineStyle.length; index += 1) {
|
|
52
|
+
const property = inlineStyle.item(index);
|
|
53
|
+
style[styleModuleKey(property)] = inlineStyle.getPropertyValue(property);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return style;
|
|
57
|
+
};
|
|
58
|
+
// NOTE: for a custom element, seed only the class tokens the vnode declares so
|
|
59
|
+
// the class module leaves component-added tokens in place; the view does not
|
|
60
|
+
// reassert them, so the full class list would reconcile them away as stale.
|
|
61
|
+
// Reading each token from the element keeps an agreeing render a no-op. A normal
|
|
62
|
+
// element seeds its whole class list, so stale tokens reconcile away.
|
|
63
|
+
const seedClasses = (element, vnode, classOwnedByModule, isCustomElement) => {
|
|
64
|
+
if (!classOwnedByModule) {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
if (!isCustomElement) {
|
|
68
|
+
return classListOf(element);
|
|
69
|
+
}
|
|
70
|
+
const declared = {};
|
|
71
|
+
for (const token of Object.keys(vnode.data?.class ?? {})) {
|
|
72
|
+
declared[token] = element.classList.contains(token);
|
|
73
|
+
}
|
|
74
|
+
return declared;
|
|
75
|
+
};
|
|
76
|
+
// NOTE: the style counterpart to seedClasses. For a custom element, seed only
|
|
77
|
+
// the style properties the vnode declares so the style module leaves
|
|
78
|
+
// component-added properties in place; a normal element seeds its whole inline
|
|
79
|
+
// style so stale properties reconcile away.
|
|
80
|
+
const seedStyle = (element, vnode, styleOwnedByModule, isCustomElement) => {
|
|
81
|
+
if (!styleOwnedByModule) {
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
if (!isCustomElement) {
|
|
85
|
+
return inlineStyleOf(element);
|
|
86
|
+
}
|
|
87
|
+
const current = inlineStyleOf(element);
|
|
88
|
+
const declared = {};
|
|
89
|
+
for (const [key, value] of Object.entries(vnode.data?.style ?? {})) {
|
|
90
|
+
if (typeof value === 'string') {
|
|
91
|
+
declared[key] = current[key] ?? '';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return declared;
|
|
95
|
+
};
|
|
96
|
+
// Properties the serializer emits as attributes but the client sets as DOM
|
|
97
|
+
// properties that do not reflect back to the attribute, so a correct hydration
|
|
98
|
+
// drops the server attribute. When a vnode owns one of these through
|
|
99
|
+
// `data.props`, the signature compares the live property value instead of the
|
|
100
|
+
// attribute, so that expected drop is not read as a disagreement. A view that
|
|
101
|
+
// instead sets the same name as a raw attribute keeps it in the attribute set.
|
|
102
|
+
const NON_REFLECTING_PROPERTIES = new Set([
|
|
103
|
+
'value',
|
|
104
|
+
'checked',
|
|
105
|
+
'selected',
|
|
106
|
+
'muted',
|
|
107
|
+
]);
|
|
108
|
+
const propertyManagedNames = (vnode) => {
|
|
109
|
+
const props = vnode.data?.props;
|
|
110
|
+
if (props === undefined) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return Array.from(NON_REFLECTING_PROPERTIES).filter(name => name in props);
|
|
114
|
+
};
|
|
115
|
+
const byName = ([leftName], [rightName]) => leftName.localeCompare(rightName);
|
|
116
|
+
// A structured, order-independent snapshot of the state an element and its
|
|
117
|
+
// vnode share: the DOM attributes, the non-reflecting properties the vnode
|
|
118
|
+
// owns (read from the element, not the attribute), the class set, and inline
|
|
119
|
+
// style. Comparing the snapshot taken during adoption (the server DOM) with
|
|
120
|
+
// the element's state after the client patch flags any attribute, property,
|
|
121
|
+
// class, or style the two disagree on. Both sides pass through the same DOM
|
|
122
|
+
// APIs, so spelling differences never register, and the snapshot is JSON so no
|
|
123
|
+
// value can collide with a delimiter. Values feed the comparison, never a log.
|
|
124
|
+
export const __elementSignature = (element, vnode) => {
|
|
125
|
+
const propertyNames = propertyManagedNames(vnode);
|
|
126
|
+
const attributes = [];
|
|
127
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
128
|
+
const name = attribute.name;
|
|
129
|
+
if (name === HYDRATION_STAMP_ATTRIBUTE ||
|
|
130
|
+
name === 'class' ||
|
|
131
|
+
name === 'style' ||
|
|
132
|
+
propertyNames.includes(name)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
attributes.push([name, attribute.value]);
|
|
136
|
+
}
|
|
137
|
+
attributes.sort(byName);
|
|
138
|
+
const properties = [];
|
|
139
|
+
for (const name of propertyNames) {
|
|
140
|
+
properties.push([name, String(Reflect.get(element, name))]);
|
|
141
|
+
}
|
|
142
|
+
properties.sort(byName);
|
|
143
|
+
const styles = Object.entries(inlineStyleOf(element)).sort(byName);
|
|
144
|
+
const classes = Array.from(element.classList).sort();
|
|
145
|
+
return JSON.stringify({ attributes, properties, classes, styles });
|
|
146
|
+
};
|
|
147
|
+
// NOTE: reconcile stale server DOM against the client's first render by
|
|
148
|
+
// seeding the adopted clone with the element's current attributes, classes,
|
|
149
|
+
// and inline styles. Server DOM state is all view-produced, so the diff
|
|
150
|
+
// modules remove any value the client tree does not reassert, converging a
|
|
151
|
+
// nondeterministic render instead of leaving stale, behavior-affecting state
|
|
152
|
+
// (a stale href, a stale class) on the page. class and inline style are
|
|
153
|
+
// seeded into their own module only when the client view owns them solely
|
|
154
|
+
// through that module. When the view also sets `class` or `style` through a
|
|
155
|
+
// raw attribute, or does not use the module at all, the whole attribute rides
|
|
156
|
+
// in attrs and the module (if present) re-asserts its tokens on top, so no
|
|
157
|
+
// value is written by one module and then removed as stale state by another.
|
|
158
|
+
// The hydration stamp is never seeded, so it is never removed.
|
|
159
|
+
const seedAdoptedState = (element, vnode, clone, status, isCustomElement) => {
|
|
160
|
+
const classOwnedByModule = vnode.data?.class !== undefined &&
|
|
161
|
+
vnode.data?.attrs?.['class'] === undefined;
|
|
162
|
+
const styleOwnedByModule = vnode.data?.style !== undefined &&
|
|
163
|
+
vnode.data?.attrs?.['style'] === undefined;
|
|
164
|
+
// NOTE: a custom element that upgraded before hydration adds attributes of its
|
|
165
|
+
// own in connectedCallback. Seeding only the attributes the vnode declares
|
|
166
|
+
// leaves those component-owned attributes in place, while a vnode-declared
|
|
167
|
+
// attribute still reconciles.
|
|
168
|
+
const declaredAttributes = isCustomElement
|
|
169
|
+
? new Set(Object.keys(vnode.data?.attrs ?? {}).map(name => name.toLowerCase()))
|
|
170
|
+
: undefined;
|
|
171
|
+
const attrs = {};
|
|
172
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
173
|
+
const name = attribute.name;
|
|
174
|
+
if (name === HYDRATION_STAMP_ATTRIBUTE) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (name === 'class' && classOwnedByModule) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (name === 'style' && styleOwnedByModule) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (declaredAttributes !== undefined &&
|
|
184
|
+
!declaredAttributes.has(name.toLowerCase())) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
attrs[name] = attribute.value;
|
|
188
|
+
}
|
|
189
|
+
const classes = seedClasses(element, vnode, classOwnedByModule, isCustomElement);
|
|
190
|
+
const style = seedStyle(element, vnode, styleOwnedByModule, isCustomElement);
|
|
191
|
+
clone.data = {
|
|
192
|
+
...clone.data,
|
|
193
|
+
...(Object.keys(attrs).length > 0 ? { attrs } : {}),
|
|
194
|
+
...(Object.keys(classes).length > 0 ? { class: classes } : {}),
|
|
195
|
+
...(Object.keys(style).length > 0 ? { style } : {}),
|
|
196
|
+
};
|
|
197
|
+
// In development, record the server DOM signature so the post-patch pass can
|
|
198
|
+
// report an attribute-only mismatch the structural walk cannot see. Gated on
|
|
199
|
+
// the dev flag so a production hydrate does no extra work.
|
|
200
|
+
if (import.meta.hot) {
|
|
201
|
+
status.adoptedSignatures.set(element, {
|
|
202
|
+
vnode,
|
|
203
|
+
server: __elementSignature(element, vnode),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const detectMismatch = (status) => {
|
|
208
|
+
status.isMismatchDetected = true;
|
|
209
|
+
};
|
|
210
|
+
const reportMismatch = (status) => {
|
|
211
|
+
if (import.meta.hot && status.isMismatchDetected) {
|
|
212
|
+
console.warn('[foldkit] The server DOM did not match the first client view during ' +
|
|
213
|
+
'hydration. Foldkit reconciled the mismatching subtree. Ensure Flags, ' +
|
|
214
|
+
'init, and view produce deterministic initial markup.');
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const isText = (node) => node.nodeType === Node.TEXT_NODE;
|
|
218
|
+
const isComment = (node) => node.nodeType === Node.COMMENT_NODE;
|
|
219
|
+
const isElement = (node) => node.nodeType === Node.ELEMENT_NODE;
|
|
220
|
+
const hasOnlyTextContent = (element) => {
|
|
221
|
+
const firstChild = element.firstChild;
|
|
222
|
+
return (firstChild === null ||
|
|
223
|
+
(firstChild.nextSibling === null && isText(firstChild)));
|
|
224
|
+
};
|
|
225
|
+
const matchesTag = (element, selector) => element.tagName.toLowerCase() === tagNameFromSelector(selector).toLowerCase();
|
|
226
|
+
// The namespace a vnode expects is carried in `data.ns` for foreign content
|
|
227
|
+
// (SVG, MathML) and is otherwise HTML. An element whose namespace disagrees
|
|
228
|
+
// (an HTML element parsed inside an SVG integration point, say) must be
|
|
229
|
+
// rebuilt rather than adopted, since the two are not interchangeable.
|
|
230
|
+
const namespaceOf = (vnode) => typeof vnode.data?.ns === 'string' ? vnode.data.ns : HTML_NAMESPACE;
|
|
231
|
+
const matchesNamespace = (element, vnode) => (element.namespaceURI ?? HTML_NAMESPACE) === namespaceOf(vnode);
|
|
232
|
+
const cloneOf = (vnode, elm) => {
|
|
233
|
+
const clone = {
|
|
234
|
+
sel: vnode.sel,
|
|
235
|
+
data: vnode.data?.is === undefined ? {} : { is: vnode.data.is },
|
|
236
|
+
children: undefined,
|
|
237
|
+
elm,
|
|
238
|
+
text: undefined,
|
|
239
|
+
key: vnode.key,
|
|
240
|
+
};
|
|
241
|
+
if (vnode.identity !== undefined) {
|
|
242
|
+
clone.identity = vnode.identity;
|
|
243
|
+
}
|
|
244
|
+
return clone;
|
|
245
|
+
};
|
|
246
|
+
const asVNode = (child) => typeof child === 'string'
|
|
247
|
+
? {
|
|
248
|
+
sel: undefined,
|
|
249
|
+
data: undefined,
|
|
250
|
+
children: undefined,
|
|
251
|
+
elm: undefined,
|
|
252
|
+
text: child,
|
|
253
|
+
key: undefined,
|
|
254
|
+
}
|
|
255
|
+
: child;
|
|
256
|
+
const clearChildren = (element) => {
|
|
257
|
+
element.textContent = '';
|
|
258
|
+
};
|
|
259
|
+
const adoptText = (element, domChild, text) => {
|
|
260
|
+
if (domChild !== null && isText(domChild)) {
|
|
261
|
+
const domText = domChild.data;
|
|
262
|
+
if (domText === text) {
|
|
263
|
+
return Option.some({
|
|
264
|
+
adoptedNode: domChild,
|
|
265
|
+
nextDomChild: domChild.nextSibling,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (domText.startsWith(text)) {
|
|
269
|
+
domChild.splitText(text.length);
|
|
270
|
+
return Option.some({
|
|
271
|
+
adoptedNode: domChild,
|
|
272
|
+
nextDomChild: domChild.nextSibling,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return Option.none();
|
|
276
|
+
}
|
|
277
|
+
if (text === '') {
|
|
278
|
+
const emptyTextNode = element.ownerDocument.createTextNode('');
|
|
279
|
+
element.insertBefore(emptyTextNode, domChild);
|
|
280
|
+
return Option.some({ adoptedNode: emptyTextNode, nextDomChild: domChild });
|
|
281
|
+
}
|
|
282
|
+
return Option.none();
|
|
283
|
+
};
|
|
284
|
+
const adoptElement = (element, vnode, adopted, status) => {
|
|
285
|
+
const clone = cloneOf(vnode, element);
|
|
286
|
+
adopted.add(element);
|
|
287
|
+
// NOTE: an autonomous custom element (an HTML-namespace element whose name
|
|
288
|
+
// carries a hyphen) that upgraded before hydration adds attributes, classes,
|
|
289
|
+
// styles, and light DOM of its own in connectedCallback. The attributes, class
|
|
290
|
+
// tokens, and style properties the vnode does not declare are always preserved
|
|
291
|
+
// (here and in seedAdoptedState). Light DOM ownership follows the vnode: a
|
|
292
|
+
// childless vnode leaves the component's light DOM untouched, while a vnode
|
|
293
|
+
// that declares children owns the light DOM and reconciles it like any
|
|
294
|
+
// element. The two cannot share: once both write same-tag nodes a positional
|
|
295
|
+
// walk cannot tell a component node from a view node, so declared children
|
|
296
|
+
// take full ownership rather than interleave. The test is the name shape, not
|
|
297
|
+
// `customElements.get`: whether the element has upgraded is timing-dependent
|
|
298
|
+
// at hydration (its definition may register after the server DOM parses), so a
|
|
299
|
+
// name test is deterministic. A hyphenated element that never upgrades is
|
|
300
|
+
// treated the same way, which is safe: with no component light DOM, a
|
|
301
|
+
// childless vnode leaves an empty element and a vnode with children reconciles
|
|
302
|
+
// normally.
|
|
303
|
+
const isCustomElement = (element.namespaceURI === null ||
|
|
304
|
+
element.namespaceURI === HTML_NAMESPACE) &&
|
|
305
|
+
element.localName.includes('-');
|
|
306
|
+
seedAdoptedState(element, vnode, clone, status, isCustomElement);
|
|
307
|
+
// NOTE: a controlled textarea serializes its value as text content, which
|
|
308
|
+
// sets the element's defaultValue. A fresh boot sets only the value
|
|
309
|
+
// property and leaves defaultValue empty, so the server text is cleared
|
|
310
|
+
// before the props module applies the value; otherwise the adopted
|
|
311
|
+
// textarea's defaultValue and form.reset would differ from a fresh boot.
|
|
312
|
+
// An uncontrolled textarea, whose content is its default, is left alone.
|
|
313
|
+
if (element.tagName === 'TEXTAREA' &&
|
|
314
|
+
vnode.data?.props?.['value'] !== undefined) {
|
|
315
|
+
clearChildren(element);
|
|
316
|
+
clone.children = [];
|
|
317
|
+
return clone;
|
|
318
|
+
}
|
|
319
|
+
const authoredInnerHtml = vnode.data?.props?.['innerHTML'];
|
|
320
|
+
if (authoredInnerHtml !== undefined) {
|
|
321
|
+
// NOTE: the browser normalizes markup as it parses (entity forms, tag
|
|
322
|
+
// case, attribute order), so the served innerHTML string rarely equals
|
|
323
|
+
// the authored one byte for byte. Parsing the authored string through a
|
|
324
|
+
// probe element of the same tag compares the two in normalized form;
|
|
325
|
+
// when they agree the clone carries the authored string, the props
|
|
326
|
+
// module sees no change, and the adopted subtree survives. The probe is
|
|
327
|
+
// created in the element's own namespace: foreign content such as SVG
|
|
328
|
+
// parses with case-preserved names (pathLength, viewBox) that an
|
|
329
|
+
// HTML-context parse would lowercase, false-mismatching every camelCase
|
|
330
|
+
// attribute.
|
|
331
|
+
if (typeof authoredInnerHtml === 'string') {
|
|
332
|
+
const probe = element.namespaceURI === null || element.namespaceURI === HTML_NAMESPACE
|
|
333
|
+
? element.ownerDocument.createElement(element.tagName)
|
|
334
|
+
: element.ownerDocument.createElementNS(element.namespaceURI, element.tagName);
|
|
335
|
+
probe.innerHTML = authoredInnerHtml;
|
|
336
|
+
const isEquivalentMarkup = probe.innerHTML === element.innerHTML;
|
|
337
|
+
if (!isEquivalentMarkup) {
|
|
338
|
+
detectMismatch(status);
|
|
339
|
+
}
|
|
340
|
+
clone.data = {
|
|
341
|
+
...clone.data,
|
|
342
|
+
props: {
|
|
343
|
+
innerHTML: isEquivalentMarkup ? authoredInnerHtml : element.innerHTML,
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
clone.data = { ...clone.data, props: { innerHTML: element.innerHTML } };
|
|
349
|
+
}
|
|
350
|
+
clone.children = [];
|
|
351
|
+
return clone;
|
|
352
|
+
}
|
|
353
|
+
const vnodeChildren = vnode.children;
|
|
354
|
+
// NOTE: no children (undefined) and an empty child list both mean the view
|
|
355
|
+
// declares no children, so they share the childless path. This is also where
|
|
356
|
+
// a custom element's ownership splits: a childless vnode has no view child to
|
|
357
|
+
// adopt, so the component's light DOM is left untouched, while a vnode with
|
|
358
|
+
// real children (the branch below) owns the light DOM and reconciles it.
|
|
359
|
+
if (vnodeChildren === undefined || vnodeChildren.length === 0) {
|
|
360
|
+
// NOTE: only adopt the text shortcut when the element already holds a
|
|
361
|
+
// single text node. `textContent` flattens across element children, so
|
|
362
|
+
// copying it for an element that carries stray markup would compare equal
|
|
363
|
+
// to the vnode text and leave that markup in place. Leaving `clone.text`
|
|
364
|
+
// undefined makes `patchVnode` overwrite the element's content with the
|
|
365
|
+
// vnode text instead, rebuilding the mismatching shape.
|
|
366
|
+
if (vnode.text !== undefined) {
|
|
367
|
+
if (hasOnlyTextContent(element)) {
|
|
368
|
+
clone.text = element.textContent ?? '';
|
|
369
|
+
if (clone.text !== vnode.text) {
|
|
370
|
+
detectMismatch(status);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
detectMismatch(status);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else if (!isCustomElement && element.firstChild !== null) {
|
|
378
|
+
// NOTE: a childless vnode (no children or an empty child list, and no
|
|
379
|
+
// text) owns an empty element. Server DOM left under it, an older build's
|
|
380
|
+
// content behind a cache race, would otherwise survive every future
|
|
381
|
+
// render, since patchVnode has nothing to diff it against. Clear it so the
|
|
382
|
+
// empty client tree wins, matching the mismatch branches below. A custom
|
|
383
|
+
// element is exempt: its light DOM is component-owned, not stale server
|
|
384
|
+
// state.
|
|
385
|
+
detectMismatch(status);
|
|
386
|
+
clearChildren(element);
|
|
387
|
+
clone.children = [];
|
|
388
|
+
}
|
|
389
|
+
return clone;
|
|
390
|
+
}
|
|
391
|
+
const cloneChildren = [];
|
|
392
|
+
let domChild = element.firstChild;
|
|
393
|
+
for (const rawChild of vnodeChildren) {
|
|
394
|
+
const child = asVNode(rawChild);
|
|
395
|
+
if (child.sel === undefined || child.sel === '') {
|
|
396
|
+
const childText = child.text ?? '';
|
|
397
|
+
const maybeAdoption = adoptText(element, domChild, childText);
|
|
398
|
+
if (Option.isNone(maybeAdoption)) {
|
|
399
|
+
detectMismatch(status);
|
|
400
|
+
if (domChild === null) {
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
clearChildren(element);
|
|
404
|
+
clone.children = [];
|
|
405
|
+
return clone;
|
|
406
|
+
}
|
|
407
|
+
const adoption = maybeAdoption.value;
|
|
408
|
+
const textClone = cloneOf(child, adoption.adoptedNode);
|
|
409
|
+
textClone.text = childText;
|
|
410
|
+
cloneChildren.push(textClone);
|
|
411
|
+
domChild = adoption.nextDomChild;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (domChild === null) {
|
|
415
|
+
detectMismatch(status);
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
if (child.sel === '!') {
|
|
419
|
+
if (!isComment(domChild)) {
|
|
420
|
+
detectMismatch(status);
|
|
421
|
+
clearChildren(element);
|
|
422
|
+
clone.children = [];
|
|
423
|
+
return clone;
|
|
424
|
+
}
|
|
425
|
+
const commentClone = cloneOf(child, domChild);
|
|
426
|
+
commentClone.text = domChild.data;
|
|
427
|
+
cloneChildren.push(commentClone);
|
|
428
|
+
domChild = domChild.nextSibling;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (!isElement(domChild) ||
|
|
432
|
+
!matchesTag(domChild, child.sel) ||
|
|
433
|
+
!matchesNamespace(domChild, child)) {
|
|
434
|
+
detectMismatch(status);
|
|
435
|
+
clearChildren(element);
|
|
436
|
+
clone.children = [];
|
|
437
|
+
return clone;
|
|
438
|
+
}
|
|
439
|
+
cloneChildren.push(adoptElement(domChild, child, adopted, status));
|
|
440
|
+
domChild = domChild.nextSibling;
|
|
441
|
+
}
|
|
442
|
+
while (domChild !== null) {
|
|
443
|
+
detectMismatch(status);
|
|
444
|
+
const nextDomChild = domChild.nextSibling;
|
|
445
|
+
element.removeChild(domChild);
|
|
446
|
+
domChild = nextDomChild;
|
|
447
|
+
}
|
|
448
|
+
clone.children = cloneChildren;
|
|
449
|
+
return clone;
|
|
450
|
+
};
|
|
451
|
+
const fireAdoptedInsertHooks = (vnode, adopted) => {
|
|
452
|
+
const children = vnode.children;
|
|
453
|
+
if (children !== undefined) {
|
|
454
|
+
for (const child of children) {
|
|
455
|
+
if (typeof child !== 'string') {
|
|
456
|
+
fireAdoptedInsertHooks(child, adopted);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
const insertHook = vnode.data?.hook?.insert;
|
|
461
|
+
if (insertHook !== undefined &&
|
|
462
|
+
vnode.elm !== undefined &&
|
|
463
|
+
adopted.has(vnode.elm)) {
|
|
464
|
+
insertHook(vnode);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
/** Hydrates a server-rendered root element against the first render's vnode
|
|
468
|
+
* tree. Matching DOM nodes are adopted in place, so pre-rendered content is
|
|
469
|
+
* never torn down on boot: module hooks attach listeners and re-assert
|
|
470
|
+
* attrs and props onto the existing elements, and `insert` hooks (Mounts)
|
|
471
|
+
* fire for adopted nodes in the same children-first order the differ uses
|
|
472
|
+
* for created ones. A mismatching subtree falls back to a rebuild through
|
|
473
|
+
* `createElm` at the nearest parent, and a root-level mismatch falls back
|
|
474
|
+
* to the pre-hydration replace boot. Development builds warn when
|
|
475
|
+
* reconciliation is required. Returns the patched vnode to store as the
|
|
476
|
+
* runtime's current tree. */
|
|
477
|
+
// Replace the hydration root with a fresh render of the vnode. snabbdom's
|
|
478
|
+
// sameVnode compares tag but not namespace, so patching the root directly
|
|
479
|
+
// would reuse a same-tag element even across a namespace change. Patching
|
|
480
|
+
// against a comment placed where the root was is never sameVnode with a new
|
|
481
|
+
// element, so the differ builds a fresh node in the correct namespace and
|
|
482
|
+
// swaps it in.
|
|
483
|
+
const replaceHydrationRoot = (hydrationRoot, vnode) => {
|
|
484
|
+
const parent = hydrationRoot.parentNode;
|
|
485
|
+
if (parent === null) {
|
|
486
|
+
return patch(toVNode(hydrationRoot), vnode);
|
|
487
|
+
}
|
|
488
|
+
const placeholder = hydrationRoot.ownerDocument.createComment('');
|
|
489
|
+
parent.replaceChild(placeholder, hydrationRoot);
|
|
490
|
+
return patch(toVNode(placeholder), vnode);
|
|
491
|
+
};
|
|
492
|
+
export const __hydrateVNode = (hydrationRoot, nextVNode, seen) => {
|
|
493
|
+
const dedupedVNode = nextVNode !== null ? dedupeSharedVNodes(nextVNode, seen) : h('!');
|
|
494
|
+
const status = {
|
|
495
|
+
isMismatchDetected: false,
|
|
496
|
+
adoptedSignatures: new Map(),
|
|
497
|
+
};
|
|
498
|
+
if (dedupedVNode.sel === undefined ||
|
|
499
|
+
dedupedVNode.sel === '' ||
|
|
500
|
+
dedupedVNode.sel === '!' ||
|
|
501
|
+
!matchesTag(hydrationRoot, dedupedVNode.sel) ||
|
|
502
|
+
!matchesNamespace(hydrationRoot, dedupedVNode)) {
|
|
503
|
+
detectMismatch(status);
|
|
504
|
+
const patchedVNode = replaceHydrationRoot(hydrationRoot, dedupedVNode);
|
|
505
|
+
reportMismatch(status);
|
|
506
|
+
return patchedVNode;
|
|
507
|
+
}
|
|
508
|
+
const adopted = new Set();
|
|
509
|
+
const adoptedClone = adoptElement(hydrationRoot, dedupedVNode, adopted, status);
|
|
510
|
+
const patchedVNode = patch(adoptedClone, dedupedVNode);
|
|
511
|
+
if (import.meta.hot && !status.isMismatchDetected) {
|
|
512
|
+
for (const [element, { vnode, server }] of status.adoptedSignatures) {
|
|
513
|
+
if (__elementSignature(element, vnode) !== server) {
|
|
514
|
+
detectMismatch(status);
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
fireAdoptedInsertHooks(patchedVNode, adopted);
|
|
520
|
+
reportMismatch(status);
|
|
521
|
+
return patchedVNode;
|
|
522
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Attribute stamped on the server-rendered application root. Its presence
|
|
2
|
+
* tells a booting runtime to hydrate instead of rendering fresh, and its
|
|
3
|
+
* value is the runtime id used for HMR model preservation. */
|
|
4
|
+
export declare const FOLDKIT_APP_ATTRIBUTE = "data-foldkit-app";
|
|
5
|
+
/** Attribute on the JSON script tag carrying the Schema-encoded flags the
|
|
6
|
+
* server rendered with. A hydrating runtime decodes this payload instead of
|
|
7
|
+
* running the client `flags` Effect, so both sides call `init` with the
|
|
8
|
+
* same value. */
|
|
9
|
+
export declare const FOLDKIT_FLAGS_ATTRIBUTE = "data-foldkit-flags";
|
|
10
|
+
//# sourceMappingURL=hydrationMarker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrationMarker.d.ts","sourceRoot":"","sources":["../src/hydrationMarker.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAC/D,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AAEvD;;;kBAGkB;AAClB,eAAO,MAAM,uBAAuB,uBAAuB,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Attribute stamped on the server-rendered application root. Its presence
|
|
2
|
+
* tells a booting runtime to hydrate instead of rendering fresh, and its
|
|
3
|
+
* value is the runtime id used for HMR model preservation. */
|
|
4
|
+
export const FOLDKIT_APP_ATTRIBUTE = 'data-foldkit-app';
|
|
5
|
+
/** Attribute on the JSON script tag carrying the Schema-encoded flags the
|
|
6
|
+
* server rendered with. A hydrating runtime decodes this payload instead of
|
|
7
|
+
* running the client `flags` Effect, so both sides call `init` with the
|
|
8
|
+
* same value. */
|
|
9
|
+
export const FOLDKIT_FLAGS_ATTRIBUTE = 'data-foldkit-flags';
|
package/dist/mount/index.d.ts
CHANGED
|
@@ -133,7 +133,7 @@ export type MountDefinition<Name extends string = string, ResultMessage = any> =
|
|
|
133
133
|
* )(({ buttonId, anchor }) => element =>
|
|
134
134
|
* Effect.gen(function* () {
|
|
135
135
|
* yield* Effect.acquireRelease(
|
|
136
|
-
* Effect.sync(() => anchorSetup({ buttonId, anchor })
|
|
136
|
+
* Effect.sync(() => anchorSetup(element, { buttonId, anchor })),
|
|
137
137
|
* cleanup => Effect.sync(cleanup),
|
|
138
138
|
* )
|
|
139
139
|
* return CompletedAnchorPopover()
|
package/dist/runtime/public.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { SlowPhase, defaultSlowCallback, embed, makeApplication, makeElement, run, } from './runtime.js';
|
|
2
|
-
export type { RoutingConfig, CrashConfig, CrashContext, ElementCrashConfig, RoutingApplicationConfigWithFlags, RoutingApplicationConfig, ApplicationConfigWithFlags, ApplicationConfig, ElementConfigWithFlags, ElementConfig, ApplicationInit, RoutingApplicationInit, ElementInit, EmbedHandle, InboundPortHandle, InboundPortHandles, OutboundPortHandle, OutboundPortHandles, PortHandles, MakeRuntimeReturn, Visibility, SlowConfig, SlowContext, SlowPatchContext, SlowSubscriptionDependenciesContext, SlowThresholdOverrides, SlowUpdateContext, SlowViewContext, DevToolsConfig, DevToolsMode, DevToolsModeConfig, DevToolsPosition, } from './runtime.js';
|
|
1
|
+
export { SlowPhase, defaultSlowCallback, embed, hydrate, makeApplication, makeElement, run, } from './runtime.js';
|
|
2
|
+
export type { RoutingConfig, CrashConfig, CrashContext, ElementCrashConfig, RoutingApplicationConfigWithFlags, RoutingApplicationConfig, ApplicationConfigWithFlags, ApplicationConfig, ElementConfigWithFlags, ElementConfig, ApplicationInit, RoutingApplicationInit, ElementInit, EmbedHandle, InboundPortHandle, InboundPortHandles, OutboundPortHandle, OutboundPortHandles, PortHandles, MakeRuntimeReturn, RunOptions, Visibility, SlowConfig, SlowContext, SlowPatchContext, SlowSubscriptionDependenciesContext, SlowThresholdOverrides, SlowUpdateContext, SlowViewContext, DevToolsConfig, DevToolsMode, DevToolsModeConfig, DevToolsPosition, } from './runtime.js';
|
|
3
3
|
export type { ViewTransitionConfig, ViewTransitionContext, ViewTransitionDecision, } from './viewTransition.js';
|
|
4
4
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/runtime/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,KAAK,EACL,eAAe,EACf,WAAW,EACX,GAAG,GACJ,MAAM,cAAc,CAAA;AAErB,YAAY,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iCAAiC,EACjC,wBAAwB,EACxB,0BAA0B,EAC1B,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,mCAAmC,EACnC,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAA;AAErB,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA"}
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/runtime/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,KAAK,EACL,OAAO,EACP,eAAe,EACf,WAAW,EACX,GAAG,GACJ,MAAM,cAAc,CAAA;AAErB,YAAY,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iCAAiC,EACjC,wBAAwB,EACxB,0BAA0B,EAC1B,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,mCAAmC,EACnC,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAA;AAErB,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA"}
|
package/dist/runtime/public.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { SlowPhase, defaultSlowCallback, embed, makeApplication, makeElement, run, } from './runtime.js';
|
|
1
|
+
export { SlowPhase, defaultSlowCallback, embed, hydrate, makeApplication, makeElement, run, } from './runtime.js';
|