@microsoft/webui-framework 0.0.25 → 0.0.27

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 (53) hide show
  1. package/README.md +209 -53
  2. package/dist/component-asset/asset.d.ts +4 -2
  3. package/dist/component-asset/asset.js +19 -3
  4. package/dist/component-asset/generated-manifest.d.ts +1 -0
  5. package/dist/component-asset/generated-manifest.js +32 -0
  6. package/dist/component-asset/loader.d.ts +2 -1
  7. package/dist/component-asset/loader.js +98 -22
  8. package/dist/component-asset/manifest.d.ts +2 -1
  9. package/dist/component-asset.d.ts +1 -1
  10. package/dist/component-asset.js +12 -2
  11. package/dist/element/link-styles.d.ts +20 -0
  12. package/dist/element/link-styles.js +1042 -0
  13. package/dist/element/markers.d.ts +4 -0
  14. package/dist/element/markers.js +60 -5
  15. package/dist/element/styles.d.ts +39 -1
  16. package/dist/element/styles.js +623 -13
  17. package/dist/element/types.d.ts +5 -3
  18. package/dist/element.js +9 -1
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +1 -0
  21. package/dist/interaction-hydration.d.ts +8 -0
  22. package/dist/interaction-hydration.js +167 -0
  23. package/dist/static-host.js +43 -1
  24. package/dist/streaming-activation.d.ts +2 -2
  25. package/dist/streaming-activation.js +4 -2
  26. package/dist/streaming-bootstrap.d.ts +2 -2
  27. package/dist/streaming-bootstrap.js +7 -1
  28. package/dist/streaming-cleanup.d.ts +1 -0
  29. package/dist/streaming-cleanup.js +66 -14
  30. package/dist/streaming-coordinator.d.ts +3 -2
  31. package/dist/streaming-coordinator.js +107 -47
  32. package/dist/streaming-deferred.d.ts +14 -4
  33. package/dist/streaming-deferred.js +200 -87
  34. package/dist/streaming-dom.d.ts +12 -3
  35. package/dist/streaming-dom.js +41 -27
  36. package/dist/streaming-mode.d.ts +5 -0
  37. package/dist/streaming-mode.js +4 -0
  38. package/dist/streaming-protocol.d.ts +19 -3
  39. package/dist/streaming-protocol.js +4 -3
  40. package/dist/streaming-spans.d.ts +9 -0
  41. package/dist/streaming-spans.js +200 -0
  42. package/dist/template-content.d.ts +17 -0
  43. package/dist/template-content.js +106 -0
  44. package/dist/template-element.d.ts +17 -2
  45. package/dist/template-element.js +493 -177
  46. package/dist/template-events.d.ts +7 -0
  47. package/dist/template-events.js +7 -1
  48. package/dist/template-types.d.ts +1 -2
  49. package/dist/template.d.ts +7 -1
  50. package/dist/template.js +79 -16
  51. package/package.json +9 -3
  52. package/dist/component-asset/resources.d.ts +0 -4
  53. package/dist/component-asset/resources.js +0 -83
@@ -1,6 +1,13 @@
1
1
  import type { TemplateMeta } from './template.js';
2
2
  export declare const TEMPLATES_REGISTERED_EVENT = "webui:templates-registered";
3
+ export interface TemplateRegistrationDetail {
4
+ templates?: Record<string, TemplateMeta>;
5
+ componentStyles?: unknown;
6
+ waitUntil?: (promise: PromiseLike<unknown>) => void;
7
+ }
3
8
  export declare function dispatchTemplatesRegistered(templates: Record<string, TemplateMeta>): void;
4
9
  export declare function templateRegistrationDetail(event: Event): {
5
10
  templates?: Record<string, TemplateMeta>;
11
+ componentStyles?: unknown;
12
+ waitUntil?: (promise: PromiseLike<unknown>) => void;
6
13
  } | undefined;
@@ -18,6 +18,12 @@ export function templateRegistrationDetail(event) {
18
18
  templates: typeof templates === 'object' && templates !== null
19
19
  ? templates
20
20
  : undefined,
21
+ componentStyles: detail.componentStyles,
22
+ waitUntil: typeof detail.waitUntil === 'function'
23
+ ? detail.waitUntil
24
+ : undefined,
21
25
  };
22
- return payload.templates ? payload : undefined;
26
+ return payload.templates || payload.componentStyles !== undefined || payload.waitUntil
27
+ ? payload
28
+ : undefined;
23
29
  }
@@ -44,11 +44,10 @@ export interface TemplateBlockMeta {
44
44
  }
45
45
  export interface TemplateMeta extends TemplateBlockMeta {
46
46
  b?: TemplateBlockMeta[];
47
- sa?: string;
48
47
  re?: [string, string, CompiledEventArgs][];
49
48
  tr?: string[];
50
49
  ta?: string[];
51
50
  sd?: 1;
52
51
  th?: boolean | 1;
53
- wp?: 1 | 2;
52
+ wp?: 1 | 2 | 3 | 4;
54
53
  }
@@ -1,19 +1,25 @@
1
1
  export type { CompiledAttrGroupMeta, CompiledAttrMeta, CompiledAttrPart, CompiledCondition, CompiledConditionFn, CompiledConditionalMeta, CompiledEventArg, CompiledEventArgs, CompiledEventBindingMeta, CompiledEventGroupMeta, SerializedCompiledCondition, TemplateCondition, CompiledTextRunMeta, TemplateBlockMeta, TemplateMeta, TemplateNodeIndex, TemplateSlot, } from './template-types.js';
2
+ import { type ComponentStyles } from './element/styles.js';
2
3
  import type { CompiledConditionFn, TemplateMeta } from './template-types.js';
3
4
  declare global {
4
5
  interface WebUIRuntimeGlobal {
5
6
  state?: Record<string, unknown>;
6
7
  templates?: Record<string, TemplateMeta>;
7
8
  templateFns?: Record<string, CompiledConditionFn[]>;
9
+ componentAssetStyles?: Record<string, readonly string[]>;
8
10
  templateHostExclusions?: Set<string>;
11
+ componentStyles?: ComponentStyles;
9
12
  [key: string]: unknown;
10
13
  }
11
14
  interface Window {
12
15
  __webui?: WebUIRuntimeGlobal;
16
+ __webuiRegisterComponentStyles?: (value: unknown) => Promise<void> | undefined;
13
17
  }
14
18
  }
15
19
  export declare function getTemplate(name: string): TemplateMeta | undefined;
16
20
  export declare function getTemplateRegistry(): Record<string, TemplateMeta> | undefined;
17
- export declare function registerTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>): void;
21
+ export declare function registerTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>, componentStylesValue?: ComponentStyles | unknown): void;
18
22
  export declare function prepareAssetTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>): void;
19
23
  export declare function deferTemplateDefinition(tagName: string, ctor: Function, define: () => void): void;
24
+ export declare function releaseSSRBootstrapState(): void;
25
+ export declare function releaseNonRoutedSSRBootstrapState(): void;
package/dist/template.js CHANGED
@@ -1,25 +1,32 @@
1
1
  import { dispatchTemplatesRegistered, templateRegistrationDetail, TEMPLATES_REGISTERED_EVENT, } from './template-events.js';
2
+ import { prepareComponentStyles, registerComponentStyles, registerPreparedComponentStyles, requireComponentStyles, validateComponentStylesRegistration, } from './element/styles.js';
3
+ import { prepareComponentStyleLinks, prepareRegisteredLinkStyles, } from './element/link-styles.js';
2
4
  const WEBUI_DATA_ID = 'webui-data';
5
+ const HYDRATION_COMPLETE_EVENT = 'webui:hydration-complete';
6
+ const TEMPLATE_FN_COUNT = Symbol.for('microsoft.webui.templateFnCount');
3
7
  const normalizedTemplates = new WeakSet();
4
8
  const assetNormalizedTemplates = new WeakSet();
5
9
  let webuiDataLoaded = false;
10
+ function runtimeTemplateFnCount(registry) {
11
+ const count = registry[TEMPLATE_FN_COUNT];
12
+ if (typeof count === 'number')
13
+ return count;
14
+ const initialized = Object.keys(registry).length;
15
+ registry[TEMPLATE_FN_COUNT] = initialized;
16
+ return initialized;
17
+ }
6
18
  const pendingTemplateDefinitions = new Map();
7
- function acceptTemplateData(templates, templateFns) {
19
+ function acceptTemplateData(templates, templateFns, componentStyles) {
20
+ validateComponentStylesRegistration(componentStyles);
8
21
  prepareTemplateData(templates, templateFns);
22
+ if (componentStyles) {
23
+ registerPreparedComponentStyles(componentStyles);
24
+ }
9
25
  const w = window;
10
26
  if (!w.__webui)
11
27
  w.__webui = {};
12
28
  if (!w.__webui.templates)
13
29
  w.__webui.templates = {};
14
- if (templateFns) {
15
- if (!w.__webui.templateFns)
16
- w.__webui.templateFns = {};
17
- const fnNames = Object.keys(templateFns);
18
- for (let i = 0; i < fnNames.length; i++) {
19
- const tag = fnNames[i];
20
- w.__webui.templateFns[tag] = templateFns[tag];
21
- }
22
- }
23
30
  const names = Object.keys(templates);
24
31
  for (let i = 0; i < names.length; i++) {
25
32
  const tag = names[i];
@@ -35,11 +42,28 @@ function acceptTemplateData(templates, templateFns) {
35
42
  return names.length > 0;
36
43
  }
37
44
  if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
45
+ window.__webuiRegisterComponentStyles = (value) => {
46
+ const styles = requireComponentStyles(value);
47
+ validateComponentStylesRegistration(styles);
48
+ registerPreparedComponentStyles(styles);
49
+ return prepareComponentStyleLinks(styles);
50
+ };
51
+ if (window.__webui?.componentStyles) {
52
+ registerComponentStyles(window.__webui.componentStyles);
53
+ }
38
54
  window.addEventListener(TEMPLATES_REGISTERED_EVENT, (event) => {
39
- const templates = templateRegistrationDetail(event)?.templates;
40
- if (templates)
41
- acceptTemplateData(templates);
55
+ const detail = templateRegistrationDetail(event);
56
+ if (!detail?.templates)
57
+ return;
58
+ const styles = detail.componentStyles === undefined
59
+ ? undefined
60
+ : prepareComponentStyles(detail.componentStyles);
61
+ acceptTemplateData(detail.templates, undefined, styles);
62
+ const ready = prepareRegisteredLinkStyles(detail.templates);
63
+ if (ready)
64
+ detail.waitUntil?.(ready);
42
65
  });
66
+ window.addEventListener(HYDRATION_COMPLETE_EVENT, releaseNonRoutedSSRBootstrapState, { once: true });
43
67
  }
44
68
  export function getTemplate(name) {
45
69
  let meta = window.__webui?.templates?.[name];
@@ -55,8 +79,9 @@ export function getTemplateRegistry() {
55
79
  loadWebUIDataBlock();
56
80
  return window.__webui?.templates;
57
81
  }
58
- export function registerTemplateData(templates, templateFns) {
59
- if (acceptTemplateData(templates, templateFns)) {
82
+ export function registerTemplateData(templates, templateFns, componentStylesValue) {
83
+ const componentStyles = prepareComponentStyles(componentStylesValue);
84
+ if (acceptTemplateData(templates, templateFns, componentStyles)) {
60
85
  dispatchTemplatesRegistered(templates);
61
86
  }
62
87
  }
@@ -100,20 +125,58 @@ function loadWebUIDataBlock() {
100
125
  const text = el.textContent;
101
126
  if (text) {
102
127
  const templateFns = window.__webui?.templateFns;
128
+ const componentAssetStyles = window.__webui?.componentAssetStyles;
103
129
  const parsed = JSON.parse(text);
104
130
  if (templateFns)
105
131
  parsed.templateFns = templateFns;
132
+ if (componentAssetStyles)
133
+ parsed.componentAssetStyles = componentAssetStyles;
106
134
  window.__webui = parsed;
135
+ el.remove();
136
+ webuiDataLoaded = true;
137
+ if (parsed.componentStyles !== undefined) {
138
+ registerComponentStyles(parsed.componentStyles);
139
+ }
140
+ return;
107
141
  }
108
142
  el.remove();
109
143
  webuiDataLoaded = true;
110
144
  }
145
+ export function releaseSSRBootstrapState() {
146
+ const runtime = window.__webui;
147
+ if (runtime?.state !== undefined) {
148
+ delete runtime.state;
149
+ }
150
+ }
151
+ export function releaseNonRoutedSSRBootstrapState() {
152
+ const runtime = window.__webui;
153
+ if (runtime?.chain === undefined &&
154
+ runtime?.templateHostExclusions === undefined) {
155
+ releaseSSRBootstrapState();
156
+ }
157
+ }
111
158
  function normalizeTemplate(name, meta, suppliedFns) {
112
159
  if (normalizedTemplates.has(meta))
113
160
  return;
114
- const fns = suppliedFns ?? window.__webui?.templateFns?.[name] ?? [];
161
+ const runtimeFns = window.__webui?.templateFns;
162
+ const fns = suppliedFns ?? runtimeFns?.[name] ?? [];
115
163
  normalizeTemplateConditions(name, meta, fns);
116
164
  normalizedTemplates.add(meta);
165
+ if (suppliedFns === undefined && runtimeFns?.[name] === fns) {
166
+ let remaining = runtimeTemplateFnCount(runtimeFns);
167
+ delete runtimeFns[name];
168
+ remaining--;
169
+ runtimeFns[TEMPLATE_FN_COUNT] = remaining;
170
+ if (remaining === 0 && window.__webui) {
171
+ const liveCount = Object.keys(runtimeFns).length;
172
+ if (liveCount === 0) {
173
+ delete window.__webui.templateFns;
174
+ }
175
+ else {
176
+ runtimeFns[TEMPLATE_FN_COUNT] = liveCount;
177
+ }
178
+ }
179
+ }
117
180
  }
118
181
  function normalizeTemplateConditions(name, meta, fns) {
119
182
  const stack = [meta];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/webui-framework",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "type": "module",
5
5
  "description": "WebUI Framework Next — Preact-inspired lightweight Web Component runtime with SSR hydration and compiled-template path mapping.",
6
6
  "license": "MIT",
@@ -28,6 +28,12 @@
28
28
  "types": "./dist/lazy-hydration-entry.d.ts",
29
29
  "default": "./dist/lazy-hydration-entry.js"
30
30
  }
31
+ },
32
+ "./interaction-hydration.js": {
33
+ "import": {
34
+ "types": "./dist/interaction-hydration.d.ts",
35
+ "default": "./dist/interaction-hydration.js"
36
+ }
31
37
  }
32
38
  },
33
39
  "files": [
@@ -36,8 +42,8 @@
36
42
  "devDependencies": {
37
43
  "@playwright/test": "^1.61.1",
38
44
  "@types/node": "^26.1.0",
39
- "typescript": "^6.0.3",
40
- "@microsoft/webui-test-support": "0.0.25"
45
+ "typescript": "7.0.2",
46
+ "@microsoft/webui-test-support": "0.0.27"
41
47
  },
42
48
  "scripts": {
43
49
  "build": "tsc",
@@ -1,4 +0,0 @@
1
- export type PreparedAssetStyles = Record<string, string>[];
2
- export declare function prepareAssetStyles(templateStyles: readonly string[]): PreparedAssetStyles;
3
- export declare function readNonce(): string;
4
- export declare function registerAssetStyles(preparedStyles: readonly Record<string, string>[], nonce: string): void;
@@ -1,83 +0,0 @@
1
- const injectedAssetStyles = new Set();
2
- let assetStylesSeeded = false;
3
- export function prepareAssetStyles(templateStyles) {
4
- const prepared = new Array(templateStyles.length);
5
- for (let i = 0; i < templateStyles.length; i++) {
6
- prepared[i] = parseImportMap(templateStyles[i]);
7
- }
8
- return prepared;
9
- }
10
- export function readNonce() {
11
- const nonce = assetGlobal()?.nonce;
12
- if (nonce)
13
- return nonce;
14
- const meta = document.querySelector('meta[name="webui-nonce"]');
15
- return meta?.content ?? '';
16
- }
17
- export function registerAssetStyles(preparedStyles, nonce) {
18
- if (preparedStyles.length === 0)
19
- return;
20
- seedAssetStyleSet();
21
- for (let i = 0; i < preparedStyles.length; i++) {
22
- const imports = preparedStyles[i];
23
- const nextImports = {};
24
- let hasNewImport = false;
25
- const specifiers = Object.keys(imports);
26
- for (let j = 0; j < specifiers.length; j++) {
27
- const specifier = specifiers[j];
28
- if (injectedAssetStyles.has(specifier))
29
- continue;
30
- injectedAssetStyles.add(specifier);
31
- nextImports[specifier] = imports[specifier];
32
- hasNewImport = true;
33
- }
34
- if (!hasNewImport)
35
- continue;
36
- const script = document.createElement('script');
37
- script.type = 'importmap';
38
- if (nonce)
39
- script.nonce = nonce;
40
- script.textContent = JSON.stringify({ imports: nextImports });
41
- document.head.appendChild(script);
42
- }
43
- }
44
- function assetGlobal() {
45
- return window.__webui;
46
- }
47
- function seedAssetStyleSet() {
48
- if (assetStylesSeeded)
49
- return;
50
- assetStylesSeeded = true;
51
- const styles = assetGlobal()?.styles;
52
- if (!styles)
53
- return;
54
- for (let i = 0; i < styles.length; i++) {
55
- injectedAssetStyles.add(styles[i]);
56
- }
57
- }
58
- function parseImportMap(scriptMarkup) {
59
- const trimmed = scriptMarkup.trim();
60
- if (!trimmed.startsWith('<script type="importmap"')) {
61
- throw new Error('[WebUI] Component asset templateStyles entry must be a <script type="importmap"> tag.');
62
- }
63
- const openTagEnd = trimmed.indexOf('>');
64
- const closeTagStart = trimmed.lastIndexOf('</script>');
65
- if (openTagEnd < 0 || closeTagStart <= openTagEnd) {
66
- throw new Error('[WebUI] Component asset importmap tag is malformed.');
67
- }
68
- const parsed = JSON.parse(trimmed.substring(openTagEnd + 1, closeTagStart));
69
- if (!parsed.imports || typeof parsed.imports !== 'object') {
70
- throw new Error('[WebUI] Component asset importmap is missing an imports object.');
71
- }
72
- const imports = {};
73
- const specifiers = Object.keys(parsed.imports);
74
- for (let i = 0; i < specifiers.length; i++) {
75
- const specifier = specifiers[i];
76
- const uri = parsed.imports[specifier];
77
- if (typeof uri !== 'string' || !uri.startsWith('data:text/css,')) {
78
- throw new Error(`[WebUI] Component asset importmap entry "${specifier}" must be a data:text/css URI.`);
79
- }
80
- imports[specifier] = uri;
81
- }
82
- return imports;
83
- }