foldkit 0.146.0 → 0.148.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +5 -2
  2. package/dist/buildToken.d.ts +3 -0
  3. package/dist/buildToken.d.ts.map +1 -0
  4. package/dist/buildToken.js +21 -0
  5. package/dist/controlledDomState.d.ts +25 -0
  6. package/dist/controlledDomState.d.ts.map +1 -0
  7. package/dist/controlledDomState.js +240 -0
  8. package/dist/cssStyleProperties.d.ts +6 -0
  9. package/dist/cssStyleProperties.d.ts.map +1 -0
  10. package/dist/cssStyleProperties.js +91 -0
  11. package/dist/customElement/index.d.ts.map +1 -1
  12. package/dist/customElement/index.js +23 -0
  13. package/dist/domReflection.d.ts +73 -0
  14. package/dist/domReflection.d.ts.map +1 -0
  15. package/dist/domReflection.js +557 -0
  16. package/dist/experimental/index.d.ts +1 -0
  17. package/dist/experimental/index.d.ts.map +1 -1
  18. package/dist/experimental/index.js +1 -0
  19. package/dist/experimental/machine/machine.d.ts +22 -3
  20. package/dist/experimental/machine/machine.d.ts.map +1 -1
  21. package/dist/experimental/machine/machine.js +8 -0
  22. package/dist/experimental/server/entry.d.ts +73 -0
  23. package/dist/experimental/server/entry.d.ts.map +1 -0
  24. package/dist/experimental/server/entry.js +41 -0
  25. package/dist/experimental/server/host.d.ts +136 -0
  26. package/dist/experimental/server/host.d.ts.map +1 -0
  27. package/dist/experimental/server/host.js +359 -0
  28. package/dist/experimental/server/index.d.ts +5 -0
  29. package/dist/experimental/server/index.d.ts.map +1 -0
  30. package/dist/experimental/server/index.js +4 -0
  31. package/dist/experimental/server/public.d.ts +3 -0
  32. package/dist/experimental/server/public.d.ts.map +1 -0
  33. package/dist/experimental/server/public.js +1 -0
  34. package/dist/experimental/server/serialize.d.ts +43 -0
  35. package/dist/experimental/server/serialize.d.ts.map +1 -0
  36. package/dist/experimental/server/serialize.js +786 -0
  37. package/dist/experimental/server/server.d.ts +242 -0
  38. package/dist/experimental/server/server.d.ts.map +1 -0
  39. package/dist/experimental/server/server.js +946 -0
  40. package/dist/experimental/server/template.d.ts +53 -0
  41. package/dist/experimental/server/template.d.ts.map +1 -0
  42. package/dist/experimental/server/template.js +617 -0
  43. package/dist/html/index.d.ts +5 -0
  44. package/dist/html/index.d.ts.map +1 -1
  45. package/dist/html/index.js +474 -33
  46. package/dist/hydrate.d.ts +4 -0
  47. package/dist/hydrate.d.ts.map +1 -0
  48. package/dist/hydrate.js +850 -0
  49. package/dist/hydrationMarker.d.ts +10 -0
  50. package/dist/hydrationMarker.d.ts.map +1 -0
  51. package/dist/hydrationMarker.js +9 -0
  52. package/dist/hydrationMarkers.d.ts +15 -0
  53. package/dist/hydrationMarkers.d.ts.map +1 -0
  54. package/dist/hydrationMarkers.js +70 -0
  55. package/dist/nativeInnerHtml.d.ts +13 -0
  56. package/dist/nativeInnerHtml.d.ts.map +1 -0
  57. package/dist/nativeInnerHtml.js +30 -0
  58. package/dist/propertyProvenance.d.ts +33 -0
  59. package/dist/propertyProvenance.d.ts.map +1 -0
  60. package/dist/propertyProvenance.js +78 -0
  61. package/dist/propsModule.d.ts.map +1 -1
  62. package/dist/propsModule.js +149 -15
  63. package/dist/runtime/public.d.ts +2 -2
  64. package/dist/runtime/public.d.ts.map +1 -1
  65. package/dist/runtime/public.js +1 -1
  66. package/dist/runtime/runtime.d.ts +99 -36
  67. package/dist/runtime/runtime.d.ts.map +1 -1
  68. package/dist/runtime/runtime.js +412 -71
  69. package/dist/snabbdom/attributes.d.ts.map +1 -1
  70. package/dist/snabbdom/attributes.js +65 -37
  71. package/dist/snabbdom/h.d.ts +1 -0
  72. package/dist/snabbdom/h.d.ts.map +1 -1
  73. package/dist/snabbdom/h.js +85 -4
  74. package/dist/snabbdom/style.d.ts.map +1 -1
  75. package/dist/snabbdom/style.js +53 -34
  76. package/dist/snabbdom/tovnode.d.ts.map +1 -1
  77. package/dist/snabbdom/tovnode.js +5 -1
  78. package/dist/tagName.d.ts +6 -0
  79. package/dist/tagName.d.ts.map +1 -0
  80. package/dist/tagName.js +11 -0
  81. package/dist/test/apps/attributes.d.ts +1 -0
  82. package/dist/test/apps/attributes.d.ts.map +1 -1
  83. package/dist/test/apps/attributes.js +8 -1
  84. package/dist/test/apps/login.js +1 -1
  85. package/dist/test/matchers.d.ts.map +1 -1
  86. package/dist/test/matchers.js +2 -1
  87. package/dist/test/scene.d.ts.map +1 -1
  88. package/dist/test/scene.js +2 -1
  89. package/dist/vdom.d.ts.map +1 -1
  90. package/dist/vdom.js +25 -1
  91. package/package.json +9 -2
@@ -0,0 +1,359 @@
1
+ // NOTE: request-classification primitives shared by the dev host (the Vite
2
+ // plugin), the reference server, and the scaffold, so development predicts
3
+ // production. `acceptsHtml` parses the `Accept` header the way a browser
4
+ // negotiates content, and `resolvesToIndexHtml` normalizes a request path the
5
+ // way a static file server does, both of which are easy to get subtly wrong
6
+ // when duplicated per host.
7
+ const HTML_RANGE_SPECIFICITY = {
8
+ 'text/html': 3,
9
+ 'text/*': 2,
10
+ '*/*': 1,
11
+ };
12
+ const qualityOf = (parameters) => {
13
+ for (const parameter of parameters) {
14
+ const separator = parameter.indexOf('=');
15
+ if (separator === -1) {
16
+ continue;
17
+ }
18
+ const name = parameter.slice(0, separator).trim().toLowerCase();
19
+ if (name !== 'q') {
20
+ continue;
21
+ }
22
+ const value = Number(parameter.slice(separator + 1).trim());
23
+ return Number.isNaN(value) ? 1 : value;
24
+ }
25
+ return 1;
26
+ };
27
+ // NOTE: split on a delimiter only where it is not inside a double-quoted string,
28
+ // so a quoted media-type parameter (`text/html;profile="a,b"`) is not torn apart
29
+ // at the comma or semicolon inside its value. A backslash inside a quoted string
30
+ // escapes the next character, so an escaped quote (`"a\",b"`) does not end the
31
+ // string and its delimiter stays quoted.
32
+ const splitOutsideQuotes = (value, delimiter) => {
33
+ const parts = [];
34
+ let current = '';
35
+ let isInQuotes = false;
36
+ let isEscaped = false;
37
+ for (const character of value) {
38
+ if (isEscaped) {
39
+ current += character;
40
+ isEscaped = false;
41
+ }
42
+ else if (isInQuotes && character === '\\') {
43
+ current += character;
44
+ isEscaped = true;
45
+ }
46
+ else if (character === '"') {
47
+ isInQuotes = !isInQuotes;
48
+ current += character;
49
+ }
50
+ else if (character === delimiter && !isInQuotes) {
51
+ parts.push(current);
52
+ current = '';
53
+ }
54
+ else {
55
+ current += character;
56
+ }
57
+ }
58
+ parts.push(current);
59
+ return parts;
60
+ };
61
+ /**
62
+ * Decides whether a client accepts an HTML response, given its `Accept`
63
+ * header. An absent or empty header accepts anything. Otherwise the most
64
+ * specific media range that covers `text/html` (`text/html`, then `text/*`,
65
+ * then `*​/*`) decides, using its `q` value, so `text/html;q=0` is refused
66
+ * even alongside `*​/*`.
67
+ *
68
+ * A page host renders the application for a request that accepts HTML and
69
+ * serves a non-page response otherwise, so this is the negotiation a host runs
70
+ * before falling through to a render.
71
+ *
72
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
73
+ */
74
+ export const acceptsHtml = (acceptHeader) => {
75
+ if (acceptHeader === undefined || acceptHeader.trim() === '') {
76
+ return true;
77
+ }
78
+ let bestSpecificity = 0;
79
+ let bestQuality = 0;
80
+ for (const range of splitOutsideQuotes(acceptHeader, ',')) {
81
+ const parts = splitOutsideQuotes(range, ';');
82
+ const mediaType = parts[0]?.trim().toLowerCase() ?? '';
83
+ const specificity = HTML_RANGE_SPECIFICITY[mediaType];
84
+ if (specificity === undefined) {
85
+ continue;
86
+ }
87
+ if (specificity > bestSpecificity) {
88
+ bestSpecificity = specificity;
89
+ bestQuality = qualityOf(parts.slice(1));
90
+ }
91
+ }
92
+ return bestSpecificity > 0 && bestQuality > 0;
93
+ };
94
+ /**
95
+ * Merges a field name into an existing `Vary` header value, parsing it as a
96
+ * comma-separated, case-insensitive list of field names. `Vary: *` already
97
+ * varies on everything and is returned unchanged, a token already present (in
98
+ * any case, and distinct from a longer name that merely starts with it, such as
99
+ * `Accept-Language` beside `Accept`) is not duplicated, and otherwise the field
100
+ * name is appended.
101
+ *
102
+ * A host whose response depends on a request header must declare that header in
103
+ * `Vary` so a shared cache does not serve one client's representation to
104
+ * another. The dev host, reference server, and scaffold merge through this one
105
+ * helper rather than assigning `Vary`, which would drop the fields already
106
+ * there.
107
+ *
108
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
109
+ */
110
+ export const varyWith = (existing, fieldName) => {
111
+ const tokens = existing === undefined
112
+ ? []
113
+ : existing
114
+ .split(',')
115
+ .map(token => token.trim())
116
+ .filter(token => token !== '');
117
+ const lowered = tokens.map(token => token.toLowerCase());
118
+ if (lowered.includes('*')) {
119
+ return '*';
120
+ }
121
+ if (lowered.includes(fieldName.toLowerCase())) {
122
+ return tokens.join(', ');
123
+ }
124
+ return [...tokens, fieldName].join(', ');
125
+ };
126
+ /**
127
+ * Merges the `Accept` field name into an existing `Vary` header value.
128
+ *
129
+ * A page host that negotiates HTML on the `Accept` header must declare that in
130
+ * `Vary` so a shared cache does not serve one representation in place of the
131
+ * other.
132
+ *
133
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
134
+ */
135
+ export const varyWithAccept = (existing) => varyWith(existing, 'Accept');
136
+ /**
137
+ * Resolves a request target against the origin the host serves, returning the
138
+ * absolute URL to hand the server entry, or `undefined` when the target names
139
+ * a different origin.
140
+ *
141
+ * A request target is not a URL. HTTP allows origin-form (`/page?q=1`) and
142
+ * absolute-form (`http://host/page`), and a client can send a network-path
143
+ * reference (`//elsewhere.example/page`) that resolves against no scheme at
144
+ * all. Resolving one of those against the host origin silently adopts the
145
+ * origin the client wrote, so an entry that derives redirects, canonical URLs,
146
+ * cookie domains, or tenant selection from `Request.url` would take them from
147
+ * the request rather than from the deployment. Only a target that resolves to
148
+ * the host's own origin is accepted; a host behind a proxy that must serve a
149
+ * different public origin passes that origin in explicitly.
150
+ *
151
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
152
+ */
153
+ export const resolveRequestUrl = (requestTarget, origin) => {
154
+ let base;
155
+ try {
156
+ base = new URL(origin);
157
+ }
158
+ catch {
159
+ return undefined;
160
+ }
161
+ let resolved;
162
+ try {
163
+ resolved = new URL(requestTarget, base);
164
+ }
165
+ catch {
166
+ return undefined;
167
+ }
168
+ // Credentials in the target are refused rather than passed along. `origin`
169
+ // ignores them, so `http://user:pass@host/page` reads as same-origin here and
170
+ // then makes `new Request(url)` throw, turning a malformed request into a 500
171
+ // instead of the 400 it is.
172
+ if (resolved.username !== '' || resolved.password !== '') {
173
+ return undefined;
174
+ }
175
+ return resolved.origin === base.origin ? resolved.href : undefined;
176
+ };
177
+ const normalizePath = (path) => {
178
+ const segments = [];
179
+ for (const segment of path.split('/')) {
180
+ if (segment === '' || segment === '.') {
181
+ continue;
182
+ }
183
+ if (segment === '..') {
184
+ segments.pop();
185
+ continue;
186
+ }
187
+ segments.push(segment);
188
+ }
189
+ return segments.join('/');
190
+ };
191
+ // The `Sec-Fetch-Dest` values a browser sends for a subresource it fetches for
192
+ // an already-loaded page, as opposed to a navigation (`document`, `iframe`,
193
+ // `frame`) or a scripted fetch whose destination is `empty`. A request carrying
194
+ // one of these is never a page request, whatever it says it accepts.
195
+ const SUBRESOURCE_FETCH_DESTINATIONS = new Set([
196
+ 'audio',
197
+ 'audioworklet',
198
+ 'embed',
199
+ 'font',
200
+ 'image',
201
+ 'manifest',
202
+ 'object',
203
+ 'paintworklet',
204
+ 'script',
205
+ 'serviceworker',
206
+ 'sharedworker',
207
+ 'style',
208
+ 'track',
209
+ 'video',
210
+ 'worker',
211
+ 'xslt',
212
+ ]);
213
+ const ASSET_EXTENSIONS = new Set([
214
+ 'avif',
215
+ 'bmp',
216
+ 'cjs',
217
+ 'css',
218
+ 'csv',
219
+ 'eot',
220
+ 'gif',
221
+ 'gz',
222
+ 'ico',
223
+ 'jpeg',
224
+ 'jpg',
225
+ 'js',
226
+ 'json',
227
+ 'map',
228
+ 'mjs',
229
+ 'mp3',
230
+ 'mp4',
231
+ 'ogg',
232
+ 'otf',
233
+ 'pdf',
234
+ 'png',
235
+ 'svg',
236
+ 'ttf',
237
+ 'txt',
238
+ 'wasm',
239
+ 'wav',
240
+ 'webm',
241
+ 'webmanifest',
242
+ 'webp',
243
+ 'woff',
244
+ 'woff2',
245
+ 'xml',
246
+ 'zip',
247
+ ]);
248
+ /**
249
+ * Classifies a request that no static file answered, from its path and, when
250
+ * the client sent one, its `Sec-Fetch-Dest`.
251
+ *
252
+ * A host answers a static miss by content negotiation, and a browser requests
253
+ * scripts, stylesheets, and images with `Accept: *​/*`, which accepts HTML. A
254
+ * request for a hashed asset that is no longer deployed would therefore be
255
+ * answered with the application shell at 200, so a stale bundle reads as a
256
+ * blank page rather than the 404 it is.
257
+ *
258
+ * The path is read first, so a request the URL alone settles is never made to
259
+ * depend on a header the client may or may not send.
260
+ *
261
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
262
+ */
263
+ export const classifyRequest = (requestUrl, fetchDestination) => {
264
+ let pathname;
265
+ try {
266
+ pathname = new URL(requestUrl, 'http://localhost').pathname;
267
+ }
268
+ catch {
269
+ pathname = '';
270
+ }
271
+ // Read the path a static file server would resolve, not the raw one. A file
272
+ // server percent-decodes before it looks for a file, so `/assets/a%2Ejs` and
273
+ // `/assets/a.js` name the same asset; classifying the raw form reads the
274
+ // first as an ordinary page and answers a missing bundle with the shell.
275
+ try {
276
+ pathname = decodeURIComponent(pathname);
277
+ }
278
+ catch {
279
+ // An undecodable path names no file, so leave it as written.
280
+ }
281
+ const lastSegment = pathname.slice(pathname.lastIndexOf('/') + 1);
282
+ const separator = lastSegment.lastIndexOf('.');
283
+ if (separator > 0 &&
284
+ ASSET_EXTENSIONS.has(lastSegment.slice(separator + 1).toLowerCase())) {
285
+ return 'PathAsset';
286
+ }
287
+ if (fetchDestination !== undefined &&
288
+ SUBRESOURCE_FETCH_DESTINATIONS.has(fetchDestination.trim().toLowerCase())) {
289
+ return 'DestinationAsset';
290
+ }
291
+ return 'Page';
292
+ };
293
+ /**
294
+ * Decides whether a request path resolves to the `index.html` template, the
295
+ * way a static file server resolves it: percent-decoded, backslashes and
296
+ * repeated separators collapsed, and dot segments resolved. A host renders the
297
+ * application for such a request rather than serving the raw, unfilled
298
+ * template, so `/`, `/index.html`, `/%2findex.html`, and `/foo/../index.html`
299
+ * all resolve to it. An undecodable or null-byte path resolves to nothing.
300
+ *
301
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
302
+ */
303
+ export const resolvesToIndexHtml = (requestUrl) => {
304
+ let pathname;
305
+ try {
306
+ pathname = new URL(requestUrl, 'http://localhost').pathname;
307
+ }
308
+ catch {
309
+ return false;
310
+ }
311
+ let decoded;
312
+ try {
313
+ decoded = decodeURIComponent(pathname);
314
+ }
315
+ catch {
316
+ return false;
317
+ }
318
+ if (decoded.includes('\0')) {
319
+ return false;
320
+ }
321
+ const normalized = normalizePath(decoded.replace(/\\/g, '/'));
322
+ return normalized === '' || normalized.toLowerCase() === 'index.html';
323
+ };
324
+ /**
325
+ * The answer a host gives for the methods it cannot hand to a server entry.
326
+ *
327
+ * The WHATWG `Request` constructor rejects `CONNECT`, `TRACE`, and `TRACK`, so
328
+ * an entry can never be handed one: forwarding it turns a malformed request
329
+ * into a 500. A host refuses them itself, with `405` and an `Allow` header
330
+ * naming what it does forward.
331
+ *
332
+ * On Node only `TRACE` reaches this rule. The HTTP parser rejects `TRACK` with
333
+ * a 400 before any handler runs, and `CONNECT` arrives on its own event rather
334
+ * than as an ordinary request. Both are named anyway, so a host on another
335
+ * runtime refuses them rather than handing one to `new Request`.
336
+ *
337
+ * Every other method reaches the entry, `OPTIONS` included. A preflight is a
338
+ * question about a resource, so the application answers it: the entry can allow
339
+ * one origin for one route and refuse it for another, which no host-level
340
+ * policy could express. The Vite dev host applies Vite's CORS option only to
341
+ * Vite-owned modules and assets. An application preflight therefore reaches
342
+ * the entry rather than middleware a deployed host has no counterpart for.
343
+ *
344
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
345
+ */
346
+ export const HOST_METHOD_ANSWERS = {
347
+ refusedStatus: 405,
348
+ allow: 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS',
349
+ };
350
+ /**
351
+ * Whether a host refuses this method itself instead of calling the server
352
+ * entry. See {@link HOST_METHOD_ANSWERS}.
353
+ *
354
+ * @experimental Ships from `foldkit/experimental/server`; expect breaking changes while the API settles.
355
+ */
356
+ export const isHostSettledMethod = (method) => {
357
+ const normalized = method.toUpperCase();
358
+ return (normalized === 'CONNECT' || normalized === 'TRACE' || normalized === 'TRACK');
359
+ };
@@ -0,0 +1,5 @@
1
+ export * from './entry.js';
2
+ export * from './host.js';
3
+ export * from './server.js';
4
+ export * from './template.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/experimental/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './entry.js';
2
+ export * from './host.js';
3
+ export * from './server.js';
4
+ export * from './template.js';
@@ -0,0 +1,3 @@
1
+ export { FOLDKIT_APP_ATTRIBUTE, FOLDKIT_FLAGS_ATTRIBUTE, FlagsEncodeError, HOST_METHOD_ANSWERS, InvalidHydrationRoot, InvalidRuntimeId, InvalidUrl, MissingBuildId, Rendered, Responded, SerializationError, acceptsHtml, classifyRequest, injectIntoTemplate, isHostSettledMethod, renderToString, resolveRequestUrl, resolvesToIndexHtml, toResponse, varyWith, varyWithAccept, } from './index.js';
2
+ export type { InjectIntoTemplateOptions, RequestClassification, ResponseOptions, RenderedApplication, HydratableRenderOptions, StaticRenderOptions, RenderOptions, RenderUrlOptions, RenderFlagsOptions, RenderUrlFlagsOptions, ApplicationConfig, ApplicationConfigWithFlags, EntryModule, EntryResult, RenderError, RoutingApplicationConfig, RoutingApplicationConfigWithFlags, } from './index.js';
3
+ //# sourceMappingURL=public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/experimental/server/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,QAAQ,EACR,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,EACV,QAAQ,EACR,cAAc,GACf,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,0BAA0B,EAC1B,WAAW,EACX,WAAW,EACX,WAAW,EACX,wBAAwB,EACxB,iCAAiC,GAClC,MAAM,YAAY,CAAA"}
@@ -0,0 +1 @@
1
+ export { FOLDKIT_APP_ATTRIBUTE, FOLDKIT_FLAGS_ATTRIBUTE, FlagsEncodeError, HOST_METHOD_ANSWERS, InvalidHydrationRoot, InvalidRuntimeId, InvalidUrl, MissingBuildId, Rendered, Responded, SerializationError, acceptsHtml, classifyRequest, injectIntoTemplate, isHostSettledMethod, renderToString, resolveRequestUrl, resolvesToIndexHtml, toResponse, varyWith, varyWithAccept, } from './index.js';
@@ -0,0 +1,43 @@
1
+ import type { VNode } from '../../snabbdom/vnode.js';
2
+ /** Extra attributes appended to the serialized root element, used by
3
+ * `renderToString` to stamp the hydration marker without threading
4
+ * server-only concerns through view code. Root attributes are applied after
5
+ * the vnode's own data, so the stamp always wins over a same-named
6
+ * attribute in the view.
7
+ *
8
+ * `emitHydrationMarkers` turns on the key and identity markers hydration
9
+ * verifies adoption against. They are part of the hydration handoff, so a
10
+ * render nobody will hydrate carries none of them.
11
+ *
12
+ * @internal Not part of the `foldkit/experimental/server` surface; `renderToString` is the public entry to serialization.
13
+ */
14
+ export type SerializeOptions = Readonly<{
15
+ rootAttributes?: Readonly<Record<string, string>>;
16
+ emitHydrationMarkers?: boolean;
17
+ }>;
18
+ /** Escapes a string for use as HTML text content.
19
+ *
20
+ * @internal Shared with the template injector; not part of the `foldkit/experimental/server` surface.
21
+ */
22
+ export declare const escapeText: (value: string) => string;
23
+ /** Escapes a string for use inside a double-quoted HTML attribute value.
24
+ *
25
+ * @internal Shared with the template injector; not part of the `foldkit/experimental/server` surface.
26
+ */
27
+ export declare const escapeAttributeValue: (value: string) => string;
28
+ export declare const controlledValueContent: (properties: Readonly<Record<string, unknown>> | undefined) => string | undefined;
29
+ /** Serializes a view-produced vnode tree to an HTML string. Event handlers and
30
+ * hooks are behavior, not markup, and are skipped; attrs, class, dataset,
31
+ * prop-backed attributes, and inline style are emitted in that order. A `null`
32
+ * tree serializes to an empty comment, mirroring how the runtime patches
33
+ * `null` as a comment node.
34
+ *
35
+ * A hydratable render also stamps a digest of each vnode's key and identity, so
36
+ * hydration can tell one logical entity from another; the raw key and the
37
+ * compiler's source identity never appear in the markup. A render that is not
38
+ * hydratable emits neither.
39
+ *
40
+ * @internal Not part of the `foldkit/experimental/server` surface; `renderToString` is the public entry to serialization.
41
+ */
42
+ export declare const serializeHtml: (root: VNode | null, options?: SerializeOptions) => string;
43
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../../src/experimental/server/serialize.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAGpD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAC,CAAA;AAsNF;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAM1C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,MAMpD,CAAA;AA0MD,eAAO,MAAM,sBAAsB,GACjC,YAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,KACxD,MAAM,GAAG,SAMX,CAAA;AAugBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,KAAK,GAAG,IAAI,EAClB,UAAU,gBAAgB,KACzB,MAUF,CAAA"}