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,557 @@
1
+ import { CSS_STYLE_PROPERTIES } from './cssStyleProperties.js';
2
+ // The one description of how a typed attribute builder's DOM property relates
3
+ // to the HTML attribute it is named after.
4
+ //
5
+ // Two mechanisms read this. A server render turns the property into attribute
6
+ // text a parser will read back, and a client render assigns the property to a
7
+ // live element. They agree only where the property is a reflecting IDL
8
+ // attribute of that element, so the same tables decide what the serializer
9
+ // emits and what the builders refuse to build. Keeping one copy is what stops
10
+ // the two from drifting: a property added to the serializer alone would be
11
+ // served as markup no client render reproduces.
12
+ /** DOM properties whose attribute is present or absent rather than valued.
13
+ *
14
+ * @internal */
15
+ export const BOOLEAN_PROPERTIES = new Set([
16
+ 'autofocus',
17
+ 'autoplay',
18
+ 'checked',
19
+ 'controls',
20
+ 'disabled',
21
+ 'formNoValidate',
22
+ 'hidden',
23
+ 'inert',
24
+ 'isMap',
25
+ 'loop',
26
+ 'multiple',
27
+ 'muted',
28
+ 'noValidate',
29
+ 'open',
30
+ 'playsInline',
31
+ 'readOnly',
32
+ 'required',
33
+ 'reversed',
34
+ 'selected',
35
+ ]);
36
+ /** DOM properties whose name differs from the attribute they reflect.
37
+ *
38
+ * @internal */
39
+ export const RENAMED_PROPERTY_ATTRIBUTES = {
40
+ colSpan: 'colspan',
41
+ dateTime: 'datetime',
42
+ formAction: 'formaction',
43
+ formEnctype: 'formenctype',
44
+ formMethod: 'formmethod',
45
+ formNoValidate: 'formnovalidate',
46
+ formTarget: 'formtarget',
47
+ htmlFor: 'for',
48
+ isMap: 'ismap',
49
+ maxLength: 'maxlength',
50
+ minLength: 'minlength',
51
+ noValidate: 'novalidate',
52
+ playsInline: 'playsinline',
53
+ readOnly: 'readonly',
54
+ rowSpan: 'rowspan',
55
+ tabIndex: 'tabindex',
56
+ };
57
+ /** DOM properties whose value is written as the same-named attribute verbatim.
58
+ *
59
+ * @internal */
60
+ export const PASSTHROUGH_PROPERTIES = new Set([
61
+ 'accept',
62
+ 'action',
63
+ 'alt',
64
+ 'autocomplete',
65
+ 'cite',
66
+ 'cols',
67
+ 'dir',
68
+ 'download',
69
+ 'enctype',
70
+ 'high',
71
+ 'href',
72
+ 'id',
73
+ 'label',
74
+ 'lang',
75
+ 'low',
76
+ 'max',
77
+ 'method',
78
+ 'min',
79
+ 'name',
80
+ 'optimum',
81
+ 'pattern',
82
+ 'placeholder',
83
+ 'poster',
84
+ 'preload',
85
+ 'rel',
86
+ 'rows',
87
+ 'size',
88
+ 'span',
89
+ 'src',
90
+ 'start',
91
+ 'step',
92
+ 'target',
93
+ 'title',
94
+ 'type',
95
+ 'value',
96
+ 'wrap',
97
+ ]);
98
+ /** The property names that are universal HTML global attributes.
99
+ *
100
+ * @internal */
101
+ export const GLOBAL_ATTRIBUTE_PROPERTIES = new Set([
102
+ 'autofocus',
103
+ 'id',
104
+ 'title',
105
+ 'lang',
106
+ 'dir',
107
+ 'tabIndex',
108
+ 'hidden',
109
+ 'inert',
110
+ 'draggable',
111
+ ]);
112
+ // The properties an SVG or MathML element carries as reflecting IDL attributes,
113
+ // so that assigning one on the client leaves the element in the state parsing
114
+ // the served attribute produces.
115
+ //
116
+ // Everything else is an HTML interface member the foreign element does not
117
+ // have. Assigning `title` or `href` to an SVG element sets an expando or, since
118
+ // bundled code is strict, throws on a readonly `SVGAnimatedString`, while the
119
+ // serializer would have written an attribute; the two never agree. Measured in
120
+ // Chromium across the whole table above on `svg:a`, `svg:rect`, and
121
+ // `math:mtext`: these three reflect on all of them and nothing else reflects on
122
+ // any of them.
123
+ /** @internal */
124
+ export const FOREIGN_REPRESENTABLE_PROPERTIES = new Set([
125
+ 'autofocus',
126
+ 'id',
127
+ 'tabIndex',
128
+ ]);
129
+ /** The HTML attribute a typed builder's DOM property reflects, or `undefined`
130
+ * when the property is not one the tables above describe.
131
+ *
132
+ * @internal */
133
+ export const reflectedAttributeName = (propertyName) => {
134
+ const renamed = RENAMED_PROPERTY_ATTRIBUTES[propertyName];
135
+ if (renamed !== undefined) {
136
+ return renamed;
137
+ }
138
+ if (BOOLEAN_PROPERTIES.has(propertyName) ||
139
+ PASSTHROUGH_PROPERTIES.has(propertyName) ||
140
+ propertyName === 'draggable') {
141
+ return propertyName;
142
+ }
143
+ return undefined;
144
+ };
145
+ // HTML attribute names are ASCII case-insensitive: a parser lowercases them,
146
+ // and `setAttribute` on an HTML element does the same. `h.Attribute('MULTIPLE',
147
+ // '')` and `h.Attribute('multiple', '')` therefore name one attribute, and any
148
+ // analysis that compares names verbatim reads the first as an unrelated one.
149
+ // Foreign content is left alone, where `viewBox` and `preserveAspectRatio` are
150
+ // case-sensitive names of their own.
151
+ const ASCII_UPPER = /[A-Z]/g;
152
+ /** @internal */
153
+ export const toHtmlAttributeName = (name) => name.replace(ASCII_UPPER, letter => letter.toLowerCase());
154
+ const SVG_ATTRIBUTE_ADJUSTMENTS = {
155
+ attributename: 'attributeName',
156
+ attributetype: 'attributeType',
157
+ basefrequency: 'baseFrequency',
158
+ baseprofile: 'baseProfile',
159
+ calcmode: 'calcMode',
160
+ clippathunits: 'clipPathUnits',
161
+ diffuseconstant: 'diffuseConstant',
162
+ edgemode: 'edgeMode',
163
+ filterunits: 'filterUnits',
164
+ glyphref: 'glyphRef',
165
+ gradienttransform: 'gradientTransform',
166
+ gradientunits: 'gradientUnits',
167
+ kernelmatrix: 'kernelMatrix',
168
+ kernelunitlength: 'kernelUnitLength',
169
+ keypoints: 'keyPoints',
170
+ keysplines: 'keySplines',
171
+ keytimes: 'keyTimes',
172
+ lengthadjust: 'lengthAdjust',
173
+ limitingconeangle: 'limitingConeAngle',
174
+ markerheight: 'markerHeight',
175
+ markerunits: 'markerUnits',
176
+ markerwidth: 'markerWidth',
177
+ maskcontentunits: 'maskContentUnits',
178
+ maskunits: 'maskUnits',
179
+ numoctaves: 'numOctaves',
180
+ pathlength: 'pathLength',
181
+ patterncontentunits: 'patternContentUnits',
182
+ patterntransform: 'patternTransform',
183
+ patternunits: 'patternUnits',
184
+ pointsatx: 'pointsAtX',
185
+ pointsaty: 'pointsAtY',
186
+ pointsatz: 'pointsAtZ',
187
+ preservealpha: 'preserveAlpha',
188
+ preserveaspectratio: 'preserveAspectRatio',
189
+ primitiveunits: 'primitiveUnits',
190
+ refx: 'refX',
191
+ refy: 'refY',
192
+ repeatcount: 'repeatCount',
193
+ repeatdur: 'repeatDur',
194
+ requiredextensions: 'requiredExtensions',
195
+ requiredfeatures: 'requiredFeatures',
196
+ specularconstant: 'specularConstant',
197
+ specularexponent: 'specularExponent',
198
+ spreadmethod: 'spreadMethod',
199
+ startoffset: 'startOffset',
200
+ stddeviation: 'stdDeviation',
201
+ stitchtiles: 'stitchTiles',
202
+ surfacescale: 'surfaceScale',
203
+ systemlanguage: 'systemLanguage',
204
+ tablevalues: 'tableValues',
205
+ targetx: 'targetX',
206
+ targety: 'targetY',
207
+ textlength: 'textLength',
208
+ viewbox: 'viewBox',
209
+ viewtarget: 'viewTarget',
210
+ xchannelselector: 'xChannelSelector',
211
+ ychannelselector: 'yChannelSelector',
212
+ zoomandpan: 'zoomAndPan',
213
+ };
214
+ export const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
215
+ export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
216
+ export const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
217
+ /** The attribute name produced by parsing HTML source in the given namespace.
218
+ *
219
+ * @internal
220
+ */
221
+ export const parsedAttributeName = (namespace, name) => {
222
+ const lowerName = toHtmlAttributeName(name);
223
+ if (namespace === SVG_NAMESPACE) {
224
+ return SVG_ATTRIBUTE_ADJUSTMENTS[lowerName] ?? lowerName;
225
+ }
226
+ if (namespace === MATHML_NAMESPACE && lowerName === 'definitionurl') {
227
+ return 'definitionURL';
228
+ }
229
+ return lowerName;
230
+ };
231
+ const HTML_PROPERTY_ELEMENTS = {
232
+ accept: new Set(['input']),
233
+ action: new Set(['form']),
234
+ alt: new Set(['area', 'img', 'input']),
235
+ autocomplete: new Set(['form', 'input', 'select', 'textarea']),
236
+ autoplay: new Set(['audio', 'video']),
237
+ checked: new Set(['input']),
238
+ cite: new Set(['blockquote', 'del', 'ins', 'q']),
239
+ cols: new Set(['textarea']),
240
+ colSpan: new Set(['td', 'th']),
241
+ controls: new Set(['audio', 'video']),
242
+ dateTime: new Set(['del', 'ins', 'time']),
243
+ disabled: new Set([
244
+ 'button',
245
+ 'fieldset',
246
+ 'input',
247
+ 'link',
248
+ 'optgroup',
249
+ 'option',
250
+ 'select',
251
+ 'textarea',
252
+ ]),
253
+ download: new Set(['a', 'area']),
254
+ enctype: new Set(['form']),
255
+ formAction: new Set(['button', 'input']),
256
+ formEnctype: new Set(['button', 'input']),
257
+ formMethod: new Set(['button', 'input']),
258
+ formNoValidate: new Set(['button', 'input']),
259
+ formTarget: new Set(['button', 'input']),
260
+ high: new Set(['meter']),
261
+ href: new Set(['a', 'area', 'base', 'link']),
262
+ htmlFor: new Set(['label', 'output', 'script']),
263
+ isMap: new Set(['img']),
264
+ label: new Set(['optgroup', 'option', 'track']),
265
+ loop: new Set(['audio', 'video']),
266
+ low: new Set(['meter']),
267
+ max: new Set(['input', 'meter', 'progress']),
268
+ maxLength: new Set(['input', 'textarea']),
269
+ method: new Set(['form']),
270
+ min: new Set(['input', 'meter']),
271
+ minLength: new Set(['input', 'textarea']),
272
+ multiple: new Set(['input', 'select']),
273
+ muted: new Set(['audio', 'video']),
274
+ name: new Set([
275
+ 'a',
276
+ 'button',
277
+ 'details',
278
+ 'embed',
279
+ 'fieldset',
280
+ 'form',
281
+ 'iframe',
282
+ 'img',
283
+ 'input',
284
+ 'map',
285
+ 'meta',
286
+ 'object',
287
+ 'output',
288
+ 'param',
289
+ 'select',
290
+ 'slot',
291
+ 'textarea',
292
+ ]),
293
+ noValidate: new Set(['form']),
294
+ open: new Set(['details', 'dialog']),
295
+ optimum: new Set(['meter']),
296
+ pattern: new Set(['input']),
297
+ placeholder: new Set(['input', 'textarea']),
298
+ playsInline: new Set(['video']),
299
+ poster: new Set(['video']),
300
+ preload: new Set(['audio', 'video']),
301
+ readOnly: new Set(['input', 'textarea']),
302
+ rel: new Set(['a', 'area', 'form', 'link']),
303
+ required: new Set(['input', 'select', 'textarea']),
304
+ reversed: new Set(['ol']),
305
+ rows: new Set(['textarea']),
306
+ rowSpan: new Set(['td', 'th']),
307
+ selected: new Set(['option']),
308
+ size: new Set(['hr', 'input', 'select']),
309
+ span: new Set(['col', 'colgroup']),
310
+ src: new Set([
311
+ 'audio',
312
+ 'embed',
313
+ 'iframe',
314
+ 'img',
315
+ 'input',
316
+ 'script',
317
+ 'source',
318
+ 'track',
319
+ 'video',
320
+ ]),
321
+ start: new Set(['ol']),
322
+ step: new Set(['input']),
323
+ target: new Set(['a', 'area', 'base', 'form', 'link']),
324
+ type: new Set([
325
+ 'a',
326
+ 'button',
327
+ 'embed',
328
+ 'input',
329
+ 'li',
330
+ 'link',
331
+ 'object',
332
+ 'ol',
333
+ 'param',
334
+ 'script',
335
+ 'source',
336
+ 'style',
337
+ 'ul',
338
+ ]),
339
+ value: new Set([
340
+ 'button',
341
+ 'data',
342
+ 'input',
343
+ 'li',
344
+ 'meter',
345
+ 'option',
346
+ 'output',
347
+ 'param',
348
+ 'progress',
349
+ 'select',
350
+ 'textarea',
351
+ ]),
352
+ wrap: new Set(['textarea']),
353
+ };
354
+ /** Whether assigning a typed property can be represented by parsed HTML for
355
+ * the target element.
356
+ *
357
+ * @internal
358
+ */
359
+ export const isHtmlPropertyRepresentable = (tagName, propertyName) => GLOBAL_ATTRIBUTE_PROPERTIES.has(propertyName) ||
360
+ (Object.hasOwn(HTML_PROPERTY_ELEMENTS, propertyName) &&
361
+ HTML_PROPERTY_ELEMENTS[propertyName]?.has(tagName.toLowerCase()) === true);
362
+ const CANONICAL_NUMBER_PROPERTIES = {
363
+ li: new Set(['value']),
364
+ meter: new Set(['high', 'low', 'max', 'min', 'optimum', 'value']),
365
+ progress: new Set(['max', 'value']),
366
+ };
367
+ /** The attribute spelling produced by assigning a typed property on a fresh
368
+ * element.
369
+ *
370
+ * @internal
371
+ */
372
+ export const serializedHtmlPropertyValue = (tagName, propertyName, value) => {
373
+ const lowerTagName = tagName.toLowerCase();
374
+ if (lowerTagName === 'textarea' && propertyName === 'cols' && value === 0) {
375
+ return '20';
376
+ }
377
+ if (lowerTagName === 'textarea' && propertyName === 'rows' && value === 0) {
378
+ return '2';
379
+ }
380
+ if (CANONICAL_NUMBER_PROPERTIES[lowerTagName]?.has(propertyName) === true) {
381
+ return String(Number(value));
382
+ }
383
+ return String(value);
384
+ };
385
+ const STYLE_LIFECYCLE_KEYS = new Set([
386
+ 'delayed',
387
+ 'destroy',
388
+ 'remove',
389
+ ]);
390
+ const CUSTOM_STYLE_PROPERTY_NAME = /^--(?:[A-Za-z0-9_-]|[^\u0000-\u007f])+$/;
391
+ const STYLE_PROPERTY_NAME = /^-?[A-Za-z_][A-Za-z0-9_-]*$/;
392
+ const canonicalStylePropertyName = (name) => name.startsWith('Webkit') ? `webkit${name.slice('Webkit'.length)}` : name;
393
+ const cssDeclarationName = (name) => {
394
+ if (name.startsWith('--')) {
395
+ return name;
396
+ }
397
+ if (name === 'cssFloat') {
398
+ return 'float';
399
+ }
400
+ const canonicalName = canonicalStylePropertyName(name);
401
+ const kebabName = canonicalName
402
+ .replace(ASCII_UPPER, letter => `-${letter}`)
403
+ .toLowerCase();
404
+ if (kebabName.startsWith('webkit-') || kebabName.startsWith('ms-')) {
405
+ return `-${kebabName}`;
406
+ }
407
+ return kebabName;
408
+ };
409
+ const CSS_DECLARATION_PROPERTIES = new Set(Array.from(CSS_STYLE_PROPERTIES, cssDeclarationName));
410
+ const hasUnsafeStyleSyntax = (value) => {
411
+ let quote;
412
+ let parentheses = 0;
413
+ let isComment = false;
414
+ for (let index = 0; index < value.length; index += 1) {
415
+ const character = value.charAt(index);
416
+ const next = value.charAt(index + 1);
417
+ if (isComment) {
418
+ if (character === '*' && next === '/') {
419
+ isComment = false;
420
+ index += 1;
421
+ }
422
+ continue;
423
+ }
424
+ if (character === '\\') {
425
+ if (next === '') {
426
+ return true;
427
+ }
428
+ index += 1;
429
+ continue;
430
+ }
431
+ if (quote !== undefined) {
432
+ if (character === quote) {
433
+ quote = undefined;
434
+ }
435
+ continue;
436
+ }
437
+ if (character === '/' && next === '*') {
438
+ isComment = true;
439
+ index += 1;
440
+ }
441
+ else if (character === '"' || character === "'") {
442
+ quote = character;
443
+ }
444
+ else if (character === '(') {
445
+ parentheses += 1;
446
+ }
447
+ else if (character === ')') {
448
+ if (parentheses === 0) {
449
+ return true;
450
+ }
451
+ parentheses -= 1;
452
+ }
453
+ else if (parentheses === 0 && (character === ';' || character === '!')) {
454
+ return true;
455
+ }
456
+ }
457
+ return quote !== undefined || parentheses !== 0 || isComment;
458
+ };
459
+ /** Refuses style entries whose property assignment and serialized declaration
460
+ * can describe different CSS.
461
+ *
462
+ * @internal
463
+ */
464
+ export const assertStyleIsRepresentable = (style) => {
465
+ const declarationOwners = new Map();
466
+ for (const name of Object.keys(style)) {
467
+ if (STYLE_LIFECYCLE_KEYS.has(name)) {
468
+ throw new Error(`[foldkit] h.Style property ${JSON.stringify(name)} is a Snabbdom ` +
469
+ 'lifecycle control rather than a CSS property. Foldkit h.Style ' +
470
+ 'accepts only declarations applied while the element exists.');
471
+ }
472
+ if (name === 'cssText' ||
473
+ (name.startsWith('--')
474
+ ? !CUSTOM_STYLE_PROPERTY_NAME.test(name)
475
+ : !STYLE_PROPERTY_NAME.test(name))) {
476
+ throw new Error(`[foldkit] h.Style property ${JSON.stringify(name)} cannot be ` +
477
+ 'represented as one inline CSS declaration. Use a CSS property ' +
478
+ 'name without declaration delimiters.');
479
+ }
480
+ const canonicalName = canonicalStylePropertyName(name);
481
+ if (!name.startsWith('--') &&
482
+ !CSS_STYLE_PROPERTIES.has(canonicalName) &&
483
+ !CSS_DECLARATION_PROPERTIES.has(name)) {
484
+ throw new Error(`[foldkit] h.Style property ${JSON.stringify(name)} is not a ` +
485
+ 'CSSStyleDeclaration property supported by Foldkit. Use a known ' +
486
+ 'camel-case or declaration name, or a custom property beginning --.');
487
+ }
488
+ const declarationName = cssDeclarationName(name);
489
+ const existingOwner = declarationOwners.get(declarationName);
490
+ if (existingOwner !== undefined) {
491
+ throw new Error(`[foldkit] h.Style properties ${JSON.stringify(existingOwner)} and ` +
492
+ `${JSON.stringify(name)} both name the ${JSON.stringify(declarationName)} ` +
493
+ 'CSS declaration. Keep one spelling so the server, fresh client, ' +
494
+ 'and hydration have one owner for it.');
495
+ }
496
+ declarationOwners.set(declarationName, name);
497
+ const value = style[name];
498
+ if (typeof value !== 'string') {
499
+ throw new Error(`[foldkit] h.Style value for ${JSON.stringify(name)} must be a ` +
500
+ 'string. Browser CSSOM assignment coerces other values while server ' +
501
+ 'serialization cannot preserve the same authored declaration.');
502
+ }
503
+ if (hasUnsafeStyleSyntax(value)) {
504
+ throw new Error(`[foldkit] h.Style value for ${JSON.stringify(name)} contains a ` +
505
+ 'unsafe CSS declaration syntax. Assigning one DOM style ' +
506
+ 'property rejects that value, while serializing it into a style ' +
507
+ 'attribute could activate additional or !important declarations. ' +
508
+ 'Keep the value to one declaration.');
509
+ }
510
+ }
511
+ };
512
+ /** The CSS declaration name corresponding to a CSSStyleDeclaration property.
513
+ *
514
+ * @internal
515
+ */
516
+ export const serializedStylePropertyName = (name) => {
517
+ return cssDeclarationName(name);
518
+ };
519
+ /** A style object keyed by the declaration names shared by CSSOM and HTML.
520
+ *
521
+ * @internal
522
+ */
523
+ export const normalizedStyleProperties = (style) => {
524
+ assertStyleIsRepresentable(style);
525
+ return Object.fromEntries(Object.entries(style).map(([name, value]) => [
526
+ cssDeclarationName(name),
527
+ String(value),
528
+ ]));
529
+ };
530
+ /** Inline style source whose parsed declarations match fresh CSSOM property
531
+ * assignment, excluding Snabbdom lifecycle controls.
532
+ *
533
+ * @internal
534
+ */
535
+ export const serializedStyleAttribute = (style) => {
536
+ const declarations = Object.entries(style).flatMap(([name, value]) => STYLE_LIFECYCLE_KEYS.has(name) || typeof value !== 'string' || value === ''
537
+ ? []
538
+ : [`${cssDeclarationName(name)}: ${value}`]);
539
+ const serialized = declarations.join('; ');
540
+ return serialized === '' ? undefined : serialized;
541
+ };
542
+ /** The value of an HTML attribute written under any ASCII casing, or
543
+ * `undefined` when no spelling of it is present.
544
+ *
545
+ * @internal */
546
+ export const htmlAttributeValue = (attributes, name) => {
547
+ if (attributes === undefined) {
548
+ return undefined;
549
+ }
550
+ let value;
551
+ for (const written of Object.keys(attributes)) {
552
+ if (toHtmlAttributeName(written) === name) {
553
+ value = attributes[written];
554
+ }
555
+ }
556
+ return value;
557
+ };
@@ -1,2 +1,3 @@
1
1
  export * as Machine from './machine/index.js';
2
+ export * as Server from './server/index.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/experimental/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/experimental/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA"}
@@ -1 +1,2 @@
1
1
  export * as Machine from './machine/index.js';
2
+ export * as Server from './server/index.js';
@@ -63,6 +63,8 @@ type GuardValueOf<GuardResult> = [GuardResult] extends [boolean] ? void : Option
63
63
  * from the table, and Messages absent from a state's `on` record, are
64
64
  * ignored: {@link Machine.step} reports them as `Ignored` rather than
65
65
  * transitioning.
66
+ *
67
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
66
68
  */
67
69
  export type TransitionTable<State extends Tagged, Message extends Tagged, R = never> = Readonly<{
68
70
  [SourceTag in TagOf<State>]?: Readonly<{
@@ -83,6 +85,8 @@ export type TransitionTable<State extends Tagged, Message extends Tagged, R = ne
83
85
  *
84
86
  * Only meaningful inside a {@link TransitionTable}: the source and trigger
85
87
  * types flow in from the table position contextually.
88
+ *
89
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
86
90
  */
87
91
  export declare const to: <State extends Tagged, Message extends Tagged, SourceState extends State, TriggerMessage extends Message, const TargetTag extends TagOf<State>, R = never>(target: TargetTag, build: (input: NoInfer<EdgeInput<SourceState, TriggerMessage>>) => NoInfer<Variant<State, TargetTag>>, commands?: (input: NoInfer<EdgeInput<SourceState, TriggerMessage>>) => ReadonlyArray<Command<NoInfer<Message>, never, R>>) => Edge<State, Message, SourceState, TriggerMessage, void, R>;
88
92
  /**
@@ -95,12 +99,16 @@ export declare const to: <State extends Tagged, Message extends Tagged, SourceSt
95
99
  *
96
100
  * The state and Message parameters are `NoInfer` so they resolve from the
97
101
  * guard list's table position alone.
102
+ *
103
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
98
104
  */
99
105
  export declare const when: <State extends Tagged, Message extends Tagged, SourceState extends State, TriggerMessage extends Message, GuardResult extends Option.Option<unknown> | boolean, const TargetTag extends TagOf<State>, R = never>(guard: (state: NoInfer<SourceState>, message: NoInfer<TriggerMessage>) => GuardResult, target: TargetTag, build: (input: NoInfer<EdgeInput<SourceState, TriggerMessage, GuardValueOf<GuardResult>>>) => NoInfer<Variant<State, TargetTag>>, commands?: (input: NoInfer<EdgeInput<SourceState, TriggerMessage, GuardValueOf<GuardResult>>>) => ReadonlyArray<Command<NoInfer<Message>, never, R>>) => When<State, Message, SourceState, TriggerMessage, GuardValueOf<GuardResult>, R>;
100
106
  /**
101
107
  * The unconditional fallback at the end of a guard list. Its edge may carry
102
108
  * Commands whose Effects need services, threading their requirements into the
103
109
  * Machine's `R` the same way {@link to} and {@link when} do.
110
+ *
111
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
104
112
  */
105
113
  export declare const otherwise: <State extends Tagged, Message extends Tagged, SourceState extends State, TriggerMessage extends Message, R = never>(edge: Edge<State, Message, SourceState, TriggerMessage, void, R>) => Otherwise<State, Message, SourceState, TriggerMessage, R>;
106
114
  /** A step that matched an Edge: the next state plus any transition-time Commands. */
@@ -119,7 +127,10 @@ export type Ignored<State extends Tagged, Message extends Tagged> = Readonly<{
119
127
  messageTag: TagOf<Message>;
120
128
  state: State;
121
129
  }>;
122
- /** The observable outcome of one step: `Transitioned` or `Ignored`. */
130
+ /** The observable outcome of one step: `Transitioned` or `Ignored`.
131
+ *
132
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
133
+ */
123
134
  export type TransitionResult<State extends Tagged, Message extends Tagged, R = never> = Transitioned<State, Message, R> | Ignored<State, Message>;
124
135
  /** Which guard construct an Edge sits under, with its position in the guard list. */
125
136
  export type EdgeGuard = Readonly<{
@@ -145,7 +156,10 @@ export type DeadTransition<State extends Tagged, Message extends Tagged> = Reado
145
156
  edge: EdgeSummary<State, Message>;
146
157
  reason: DeadTransitionReason;
147
158
  }>;
148
- /** A compiled state Machine: a pure transition function plus static analysis over the Edge set. */
159
+ /** A compiled state Machine: a pure transition function plus static analysis over the Edge set.
160
+ *
161
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
162
+ */
149
163
  export type Machine<State extends Tagged, Message extends Tagged, R = never> = Readonly<{
150
164
  initial: State;
151
165
  stateTags: ReadonlyArray<TagOf<State>>;
@@ -171,7 +185,10 @@ export type MachineSchemas<State extends Tagged, Message extends Tagged> = Reado
171
185
  Type: Message;
172
186
  }>;
173
187
  }>;
174
- /** The Machine definition: the initial state and the transition table. */
188
+ /** The Machine definition: the initial state and the transition table.
189
+ *
190
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
191
+ */
175
192
  export type MachineDefinition<State extends Tagged, Message extends Tagged, R = never> = Readonly<{
176
193
  initial: State;
177
194
  states: TransitionTable<State, Message, R>;
@@ -216,6 +233,8 @@ export type MachineDefinition<State extends Tagged, Message extends Tagged, R =
216
233
  * })
217
234
  * // machine.transition(...) returns Commands typed with UploadsClient in R.
218
235
  * ```
236
+ *
237
+ * @experimental Ships from `foldkit/experimental/machine`; expect breaking changes while the API settles.
219
238
  */
220
239
  export declare const define: <State extends Tagged, Message extends Tagged>(schemas: MachineSchemas<State, Message>) => <R = never>(definition: MachineDefinition<State, Message, R>) => Machine<State, Message, R>;
221
240
  export {};