streetui 1.6.0 → 1.6.1

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.
@@ -11,6 +11,13 @@ interface DOMAdapter {
11
11
  createTextNode(data: string): Text;
12
12
  createComment(data: string): Comment;
13
13
  createFragment(): DocumentFragment;
14
+ /**
15
+ * Optional, server-only: create a verbatim pre-serialized HTML node used by
16
+ * the v1.7 static SSR plan. The browser adapter does not implement it; the
17
+ * renderer only calls it when a static SSR plan is active (i.e. during SSR),
18
+ * so client builds never reach this path and it stays tree-shakeable.
19
+ */
20
+ createRawHTML?(html: string): Node;
14
21
  appendChild(parent: Node, child: Node): void;
15
22
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
16
23
  removeChild(parent: Node, child: Node): void;
@@ -62,6 +69,13 @@ declare class ServerDOMAdapter implements DOMAdapter {
62
69
  createTextNode(data: string): Text;
63
70
  createComment(data: string): Comment;
64
71
  createFragment(): DocumentFragment;
72
+ /**
73
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
74
+ * Server-only: the browser adapter does not implement this, and the renderer
75
+ * fast path only invokes it when a static SSR plan is present (SSR). The
76
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
77
+ */
78
+ createRawHTML(html: string): Node;
65
79
  appendChild(parent: Node, child: Node): void;
66
80
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
67
81
  removeChild(parent: Node, child: Node): void;
@@ -152,6 +166,18 @@ interface RenderToStringOptions {
152
166
  * `ServerDOMAdapter` per call so concurrent renders never share state.
153
167
  */
154
168
  readonly domAdapter?: ServerDOMAdapter;
169
+ /**
170
+ * @internal — testing/benchmark knob for the v1.7 static SSR plan.
171
+ *
172
+ * `undefined` (default): use the per-app cached plan (build once, reuse).
173
+ * `null`: disable the plan entirely — the exact v1.6 runtime mount path, used
174
+ * by the byte-identity gate and A/B benchmark as the "legacy" baseline.
175
+ * a map: use this explicit plan.
176
+ *
177
+ * Not part of the supported public API; output is byte-identical regardless
178
+ * of this value (§8).
179
+ */
180
+ readonly staticPlan?: ReadonlyMap<string, string> | null;
155
181
  }
156
182
  /**
157
183
  * Render a compiled StreetUI application to an HTML string.
@@ -11,6 +11,13 @@ interface DOMAdapter {
11
11
  createTextNode(data: string): Text;
12
12
  createComment(data: string): Comment;
13
13
  createFragment(): DocumentFragment;
14
+ /**
15
+ * Optional, server-only: create a verbatim pre-serialized HTML node used by
16
+ * the v1.7 static SSR plan. The browser adapter does not implement it; the
17
+ * renderer only calls it when a static SSR plan is active (i.e. during SSR),
18
+ * so client builds never reach this path and it stays tree-shakeable.
19
+ */
20
+ createRawHTML?(html: string): Node;
14
21
  appendChild(parent: Node, child: Node): void;
15
22
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
16
23
  removeChild(parent: Node, child: Node): void;
@@ -62,6 +69,13 @@ declare class ServerDOMAdapter implements DOMAdapter {
62
69
  createTextNode(data: string): Text;
63
70
  createComment(data: string): Comment;
64
71
  createFragment(): DocumentFragment;
72
+ /**
73
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
74
+ * Server-only: the browser adapter does not implement this, and the renderer
75
+ * fast path only invokes it when a static SSR plan is present (SSR). The
76
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
77
+ */
78
+ createRawHTML(html: string): Node;
65
79
  appendChild(parent: Node, child: Node): void;
66
80
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
67
81
  removeChild(parent: Node, child: Node): void;
@@ -152,6 +166,18 @@ interface RenderToStringOptions {
152
166
  * `ServerDOMAdapter` per call so concurrent renders never share state.
153
167
  */
154
168
  readonly domAdapter?: ServerDOMAdapter;
169
+ /**
170
+ * @internal — testing/benchmark knob for the v1.7 static SSR plan.
171
+ *
172
+ * `undefined` (default): use the per-app cached plan (build once, reuse).
173
+ * `null`: disable the plan entirely — the exact v1.6 runtime mount path, used
174
+ * by the byte-identity gate and A/B benchmark as the "legacy" baseline.
175
+ * a map: use this explicit plan.
176
+ *
177
+ * Not part of the supported public API; output is byte-identical regardless
178
+ * of this value (§8).
179
+ */
180
+ readonly staticPlan?: ReadonlyMap<string, string> | null;
155
181
  }
156
182
  /**
157
183
  * Render a compiled StreetUI application to an HTML string.
package/dist/server.cjs CHANGED
@@ -30,13 +30,14 @@ __export(server_exports, {
30
30
  module.exports = __toCommonJS(server_exports);
31
31
 
32
32
  // ../renderer/src/render-context.ts
33
- function createRenderContext(dom, graph, container, hydrationDiagnostics) {
33
+ function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
34
34
  return {
35
35
  dom,
36
36
  graph,
37
37
  instances: /* @__PURE__ */ new Map(),
38
38
  container,
39
- ...hydrationDiagnostics !== void 0 ? { hydrationDiagnostics } : {}
39
+ ...hydrationDiagnostics !== void 0 ? { hydrationDiagnostics } : {},
40
+ ...staticHTML !== void 0 ? { staticHTML } : {}
40
41
  };
41
42
  }
42
43
 
@@ -481,6 +482,17 @@ function mountGraph(ctx) {
481
482
  }
482
483
  function mountNode(ctx, graphNode, parentDom) {
483
484
  const { dom, graph } = ctx;
485
+ const staticHTML = ctx.staticHTML;
486
+ if (staticHTML !== void 0 && dom.createRawHTML !== void 0) {
487
+ const precomputed = staticHTML.get(graphNode.id);
488
+ if (precomputed !== void 0) {
489
+ const raw = dom.createRawHTML(precomputed);
490
+ dom.appendChild(parentDom, raw);
491
+ const instance2 = new NodeInstance(graphNode, raw);
492
+ ctx.instances.set(graphNode.id, instance2);
493
+ return instance2;
494
+ }
495
+ }
484
496
  if (graphNode.type === "application") {
485
497
  const instance2 = new NodeInstance(graphNode, parentDom);
486
498
  ctx.instances.set(graphNode.id, instance2);
@@ -787,6 +799,14 @@ var ServerFragment = class {
787
799
  parent = null;
788
800
  children = [];
789
801
  };
802
+ var ServerRawHTML = class {
803
+ kind = "raw";
804
+ parent = null;
805
+ html;
806
+ constructor(html) {
807
+ this.html = html;
808
+ }
809
+ };
790
810
  var ServerElement = class {
791
811
  kind = "element";
792
812
  parent = null;
@@ -937,6 +957,8 @@ function serializeServerNode(node) {
937
957
  return `<!--${node.data}-->`;
938
958
  case "fragment":
939
959
  return serializeChildren(node);
960
+ case "raw":
961
+ return node.html;
940
962
  case "element": {
941
963
  const el = node;
942
964
  const tag = el.tagName;
@@ -976,6 +998,15 @@ var ServerDOMAdapter = class {
976
998
  createFragment() {
977
999
  return new ServerFragment();
978
1000
  }
1001
+ /**
1002
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
1003
+ * Server-only: the browser adapter does not implement this, and the renderer
1004
+ * fast path only invokes it when a static SSR plan is present (SSR). The
1005
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
1006
+ */
1007
+ createRawHTML(html) {
1008
+ return new ServerRawHTML(html);
1009
+ }
979
1010
  appendChild(parent, child) {
980
1011
  const p = asParent(parent);
981
1012
  const c = asServer(child);
@@ -1163,11 +1194,114 @@ function readState(dom, root) {
1163
1194
  }
1164
1195
  }
1165
1196
 
1197
+ // ../compiler/dist/diagnostics.js
1198
+ var TEXT_PROP_KEYS = /* @__PURE__ */ new Set(["text", "label", "value"]);
1199
+ function analyzeGraph(graph) {
1200
+ const nodes = /* @__PURE__ */ new Map();
1201
+ const summary = {
1202
+ totalNodes: 0,
1203
+ staticNodes: 0,
1204
+ staticSubtrees: 0,
1205
+ dynamicTextNodes: 0,
1206
+ dynamicAttrNodes: 0,
1207
+ eventNodes: 0,
1208
+ lists: 0,
1209
+ conditionals: 0
1210
+ };
1211
+ const visit = (node) => {
1212
+ let allChildrenStatic = true;
1213
+ for (const child of node.children) {
1214
+ const childSubtreeStatic = visit(child);
1215
+ if (!childSubtreeStatic) allChildrenStatic = false;
1216
+ }
1217
+ let hasDynamicText = false;
1218
+ let hasDynamicAttr = false;
1219
+ for (const ref of node.stateRefs) {
1220
+ if (TEXT_PROP_KEYS.has(ref.propKey)) hasDynamicText = true;
1221
+ else hasDynamicAttr = true;
1222
+ }
1223
+ const hasEvents = node.events.length > 0;
1224
+ const isList = node.type === "reactive-list";
1225
+ const isConditional = node.type === "conditional";
1226
+ const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional;
1227
+ const isStaticSubtree = isStatic && allChildrenStatic;
1228
+ nodes.set(node.id, {
1229
+ isStatic,
1230
+ isStaticSubtree,
1231
+ hasDynamicText,
1232
+ hasDynamicAttr,
1233
+ hasEvents,
1234
+ isList,
1235
+ isConditional
1236
+ });
1237
+ summary.totalNodes += 1;
1238
+ if (isStatic) summary.staticNodes += 1;
1239
+ if (isStaticSubtree) summary.staticSubtrees += 1;
1240
+ if (hasDynamicText) summary.dynamicTextNodes += 1;
1241
+ if (hasDynamicAttr) summary.dynamicAttrNodes += 1;
1242
+ if (hasEvents) summary.eventNodes += 1;
1243
+ if (isList) summary.lists += 1;
1244
+ if (isConditional) summary.conditionals += 1;
1245
+ return isStaticSubtree;
1246
+ };
1247
+ visit(graph.root);
1248
+ return { nodes, summary };
1249
+ }
1250
+
1251
+ // ../renderer/src/static-ssr-plan.ts
1252
+ function collectMaximalStaticRoots(graph) {
1253
+ const analysis = analyzeGraph(graph);
1254
+ const roots = [];
1255
+ const walk = (node) => {
1256
+ if (node.type !== "application") {
1257
+ const a = analysis.nodes.get(node.id);
1258
+ if (a !== void 0 && a.isStaticSubtree) {
1259
+ roots.push(node);
1260
+ return;
1261
+ }
1262
+ }
1263
+ for (const child of node.children) walk(child);
1264
+ };
1265
+ walk(graph.root);
1266
+ return roots;
1267
+ }
1268
+ function serializeStaticSubtree(dom, graph, root) {
1269
+ const container = dom.createElement("div");
1270
+ const ctx = createRenderContext(dom, graph, container);
1271
+ const instance = mountNode(ctx, root, container);
1272
+ const html = dom.serializeInner(container);
1273
+ instance.dispose();
1274
+ ctx.instances.clear();
1275
+ return html;
1276
+ }
1277
+ function buildStaticSSRPlan(compiled) {
1278
+ const graph = compiled.graph;
1279
+ const roots = collectMaximalStaticRoots(graph);
1280
+ const plan = /* @__PURE__ */ new Map();
1281
+ if (roots.length === 0) return plan;
1282
+ const dom = new ServerDOMAdapter();
1283
+ for (const root of roots) {
1284
+ plan.set(root.id, serializeStaticSubtree(dom, graph, root));
1285
+ }
1286
+ return plan;
1287
+ }
1288
+ var PLAN_CACHE = /* @__PURE__ */ new WeakMap();
1289
+ function getStaticSSRPlan(compiled) {
1290
+ let plan = PLAN_CACHE.get(compiled);
1291
+ if (plan === void 0) {
1292
+ plan = buildStaticSSRPlan(compiled);
1293
+ PLAN_CACHE.set(compiled, plan);
1294
+ }
1295
+ return plan;
1296
+ }
1297
+
1166
1298
  // ../renderer/src/ssr.ts
1167
1299
  function renderToString(compiled, options = {}) {
1168
1300
  const dom = options.domAdapter ?? new ServerDOMAdapter();
1301
+ const plan = options.staticPlan === null ? void 0 : options.staticPlan ?? getStaticSSRPlan(compiled);
1302
+ const staticHTML = plan !== void 0 && plan.size > 0 ? plan : void 0;
1169
1303
  const container = dom.createElement("div");
1170
- const ctx = createRenderContext(dom, compiled.graph, container);
1304
+ const ctx = createRenderContext(dom, compiled.graph, container, void 0, staticHTML);
1171
1305
  const rootInstance = mountGraph(ctx);
1172
1306
  const html = dom.serializeInner(container);
1173
1307
  rootInstance.dispose();