@solidjs/web 2.0.0-beta.23 → 2.0.0-beta.24

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/dist/server.cjs CHANGED
@@ -313,7 +313,8 @@ function renderToStream(code, options = {}) {
313
313
  }
314
314
  tasks += task + ";";
315
315
  if (!timer && firstFlushed) {
316
- timer = setTimeout(writeTasks);
316
+ timer = true;
317
+ queue(() => queue(writeTasks));
317
318
  }
318
319
  };
319
320
  const onDone = () => {
@@ -396,7 +397,6 @@ function renderToStream(code, options = {}) {
396
397
  buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks}</script>`);
397
398
  tasks = "";
398
399
  }
399
- timer && clearTimeout(timer);
400
400
  timer = null;
401
401
  };
402
402
  let context;
@@ -648,6 +648,26 @@ function renderToStream(code, options = {}) {
648
648
  });
649
649
  shellCompleted = true;
650
650
  }
651
+ const MIN_DRAIN_TURNS = 8;
652
+ let lastBlockingSize = -1;
653
+ let lastRegistrySize = -1;
654
+ let drainTurn = 0;
655
+ const scheduleFlush = fn => {
656
+ const attempt = () => {
657
+ if (registry.size !== lastRegistrySize || drainTurn++ < MIN_DRAIN_TURNS) {
658
+ if (registry.size !== lastRegistrySize) drainTurn = 0;
659
+ lastRegistrySize = registry.size;
660
+ queue(attempt);
661
+ return;
662
+ }
663
+ fn();
664
+ };
665
+ const progressed = blockingPromises.size !== lastBlockingSize;
666
+ lastBlockingSize = blockingPromises.size;
667
+ lastRegistrySize = -1;
668
+ drainTurn = 0;
669
+ progressed ? queue(attempt) : setTimeout(attempt);
670
+ };
651
671
  return {
652
672
  then(fn) {
653
673
  function complete() {
@@ -663,7 +683,7 @@ function renderToStream(code, options = {}) {
663
683
  } else onCompleteAll = complete;
664
684
  function flush() {
665
685
  allSettled(blockingPromises).then(() => {
666
- setTimeout(() => {
686
+ scheduleFlush(() => {
667
687
  if (!resolveRootHoles()) return flush();
668
688
  queue(flushEnd);
669
689
  });
@@ -674,7 +694,7 @@ function renderToStream(code, options = {}) {
674
694
  pipe(w) {
675
695
  function flush() {
676
696
  allSettled(blockingPromises).then(() => {
677
- setTimeout(() => {
697
+ scheduleFlush(() => {
678
698
  doShell();
679
699
  if (!shellCompleted) return flush();
680
700
  buffer = writable = w;
@@ -694,7 +714,7 @@ function renderToStream(code, options = {}) {
694
714
  const p = new Promise(r => resolve = r);
695
715
  function flush() {
696
716
  allSettled(blockingPromises).then(() => {
697
- setTimeout(() => {
717
+ scheduleFlush(() => {
698
718
  doShell();
699
719
  if (!shellCompleted) return flush();
700
720
  const encoder = new TextEncoder();
@@ -997,14 +1017,12 @@ function escape(s, attr) {
997
1017
  }
998
1018
  return s;
999
1019
  }
1000
- const delimCode = attr ? 34 : 60;
1001
- const len = s.length;
1002
- for (let i = 0; i < len; i++) {
1003
- const c = s.charCodeAt(i);
1004
- if (c === 38 || c === delimCode) return escapeSlow(s, attr, i);
1005
- }
1006
- return s;
1020
+ const i = s.search(attr ? ESCAPE_ATTR : ESCAPE_CONTENT);
1021
+ if (i < 0) return s;
1022
+ return escapeSlow(s, attr, i);
1007
1023
  }
1024
+ const ESCAPE_CONTENT = /[&<]/;
1025
+ const ESCAPE_ATTR = /[&"]/;
1008
1026
  function escapeSlow(s, attr, start) {
1009
1027
  const delim = attr ? '"' : "<";
1010
1028
  const delimCode = attr ? 34 : 60;
package/dist/server.js CHANGED
@@ -312,7 +312,8 @@ function renderToStream(code, options = {}) {
312
312
  }
313
313
  tasks += task + ";";
314
314
  if (!timer && firstFlushed) {
315
- timer = setTimeout(writeTasks);
315
+ timer = true;
316
+ queue(() => queue(writeTasks));
316
317
  }
317
318
  };
318
319
  const onDone = () => {
@@ -395,7 +396,6 @@ function renderToStream(code, options = {}) {
395
396
  buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks}</script>`);
396
397
  tasks = "";
397
398
  }
398
- timer && clearTimeout(timer);
399
399
  timer = null;
400
400
  };
401
401
  let context;
@@ -647,6 +647,26 @@ function renderToStream(code, options = {}) {
647
647
  });
648
648
  shellCompleted = true;
649
649
  }
650
+ const MIN_DRAIN_TURNS = 8;
651
+ let lastBlockingSize = -1;
652
+ let lastRegistrySize = -1;
653
+ let drainTurn = 0;
654
+ const scheduleFlush = fn => {
655
+ const attempt = () => {
656
+ if (registry.size !== lastRegistrySize || drainTurn++ < MIN_DRAIN_TURNS) {
657
+ if (registry.size !== lastRegistrySize) drainTurn = 0;
658
+ lastRegistrySize = registry.size;
659
+ queue(attempt);
660
+ return;
661
+ }
662
+ fn();
663
+ };
664
+ const progressed = blockingPromises.size !== lastBlockingSize;
665
+ lastBlockingSize = blockingPromises.size;
666
+ lastRegistrySize = -1;
667
+ drainTurn = 0;
668
+ progressed ? queue(attempt) : setTimeout(attempt);
669
+ };
650
670
  return {
651
671
  then(fn) {
652
672
  function complete() {
@@ -662,7 +682,7 @@ function renderToStream(code, options = {}) {
662
682
  } else onCompleteAll = complete;
663
683
  function flush() {
664
684
  allSettled(blockingPromises).then(() => {
665
- setTimeout(() => {
685
+ scheduleFlush(() => {
666
686
  if (!resolveRootHoles()) return flush();
667
687
  queue(flushEnd);
668
688
  });
@@ -673,7 +693,7 @@ function renderToStream(code, options = {}) {
673
693
  pipe(w) {
674
694
  function flush() {
675
695
  allSettled(blockingPromises).then(() => {
676
- setTimeout(() => {
696
+ scheduleFlush(() => {
677
697
  doShell();
678
698
  if (!shellCompleted) return flush();
679
699
  buffer = writable = w;
@@ -693,7 +713,7 @@ function renderToStream(code, options = {}) {
693
713
  const p = new Promise(r => resolve = r);
694
714
  function flush() {
695
715
  allSettled(blockingPromises).then(() => {
696
- setTimeout(() => {
716
+ scheduleFlush(() => {
697
717
  doShell();
698
718
  if (!shellCompleted) return flush();
699
719
  const encoder = new TextEncoder();
@@ -996,14 +1016,12 @@ function escape(s, attr) {
996
1016
  }
997
1017
  return s;
998
1018
  }
999
- const delimCode = attr ? 34 : 60;
1000
- const len = s.length;
1001
- for (let i = 0; i < len; i++) {
1002
- const c = s.charCodeAt(i);
1003
- if (c === 38 || c === delimCode) return escapeSlow(s, attr, i);
1004
- }
1005
- return s;
1019
+ const i = s.search(attr ? ESCAPE_ATTR : ESCAPE_CONTENT);
1020
+ if (i < 0) return s;
1021
+ return escapeSlow(s, attr, i);
1006
1022
  }
1023
+ const ESCAPE_CONTENT = /[&<]/;
1024
+ const ESCAPE_ATTR = /[&"]/;
1007
1025
  function escapeSlow(s, attr, start) {
1008
1026
  const delim = attr ? '"' : "<";
1009
1027
  const delimCode = attr ? 34 : 60;
@@ -126,7 +126,20 @@ function createFrameHost(options = {}) {
126
126
  const buffered = pending.get(id);
127
127
  if (buffered) {
128
128
  pending.delete(id);
129
- for (const chunk of buffered) deliver(frame, chunk);
129
+ const records = {};
130
+ let version;
131
+ for (const chunk of buffered) {
132
+ if (chunk.type === "data") {
133
+ options.applyData && options.applyData(chunk);
134
+ continue;
135
+ }
136
+ version = chunk.version;
137
+ Object.assign(records, chunkToRecords(chunk));
138
+ }
139
+ if (version !== undefined) frame.apply({
140
+ version,
141
+ r: records
142
+ });
130
143
  }
131
144
  },
132
145
  unregister(id, frame) {
@@ -346,11 +359,11 @@ class FrameImpl {
346
359
  this.#runSlotCleanups(occurrence);
347
360
  }
348
361
  if (!this.#mountedSlots.has(occurrence)) {
349
- const nodes = this.#invokeSlot(occurrence, callback, record, start);
362
+ const nodes = this.#invokeSlot(occurrence, callback, record, start, this.#options.adopt);
350
363
  if (nodes) this.#replaceRange(occurrence, start, nodes);
351
364
  this.#slotNodes.set(occurrence, nodes);
352
365
  this.#mountedSlots.add(occurrence);
353
- if (!record) this.#discoverRegions(occurrence, start);
366
+ this.#discoverRegions(occurrence, start);
354
367
  this.#bindRegions(occurrence);
355
368
  } else if (record !== this.#slotArgs.get(occurrence)) {
356
369
  if (this.#refArgsUnchanged(occurrence, record)) {
@@ -367,11 +380,12 @@ class FrameImpl {
367
380
  if (!found.has(occurrence)) this.#unmountSlot(occurrence);
368
381
  }
369
382
  }
370
- #invokeSlot(occurrence, callback, record, start) {
383
+ #invokeSlot(occurrence, callback, record, start, adopted) {
371
384
  const cleanups = this.#slotCleanups.get(occurrence) ?? [];
372
385
  const ctx = {
373
386
  frame: this.#options.claimScope ?? this.#options.id,
374
387
  key: occurrence,
388
+ adopted: !!adopted,
375
389
  onCleanup: fn => cleanups.push(fn),
376
390
  existing: start ? rangeInterior(start, slotEnd(occurrence)) : []
377
391
  };
@@ -514,11 +528,22 @@ class FrameImpl {
514
528
  }
515
529
  #refArgsUnchanged(occurrence, record) {
516
530
  const old = this.#slotArgs.get(occurrence);
517
- if (!old || old.kind !== "slot" || !record || record.kind !== "slot") return false;
518
- const a = old.args || {};
531
+ if (!record || record.kind !== "slot") return false;
532
+ if (old && old.kind !== "slot") return false;
533
+ const a = old && old.args || {};
519
534
  const b = record.args || {};
520
535
  const ka = Object.keys(a);
521
- if (ka.length !== Object.keys(b).length) return false;
536
+ const kb = Object.keys(b);
537
+ if (kb.length < ka.length) return false;
538
+ if (kb.length !== ka.length) {
539
+ const regions = this.#slotRegions.get(occurrence);
540
+ for (const key of kb) {
541
+ if (key in a) continue;
542
+ const vb = b[key];
543
+ if (!vb || typeof vb !== "object" || typeof vb.$frame !== "string") return false;
544
+ if (!regions || !regions.has(vb.$frame)) return false;
545
+ }
546
+ }
522
547
  const cache = this.#slotResolvedRefs.get(occurrence);
523
548
  for (const key of ka) {
524
549
  const va = a[key];
@@ -1322,7 +1347,7 @@ function slotsFor(props) {
1322
1347
  const v = props[prop];
1323
1348
  return settle(normalizeSlotContent(typeof v === "function" ? v(slotProps) : v));
1324
1349
  };
1325
- if (ctx && ctx.frame && ctx.existing && ctx.existing.length) {
1350
+ if (ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length) {
1326
1351
  return claimRender(`sc-${ctx.frame}-${ctx.key}-`, ctx.existing, render);
1327
1352
  }
1328
1353
  return render();
@@ -124,7 +124,20 @@ function createFrameHost(options = {}) {
124
124
  const buffered = pending.get(id);
125
125
  if (buffered) {
126
126
  pending.delete(id);
127
- for (const chunk of buffered) deliver(frame, chunk);
127
+ const records = {};
128
+ let version;
129
+ for (const chunk of buffered) {
130
+ if (chunk.type === "data") {
131
+ options.applyData && options.applyData(chunk);
132
+ continue;
133
+ }
134
+ version = chunk.version;
135
+ Object.assign(records, chunkToRecords(chunk));
136
+ }
137
+ if (version !== undefined) frame.apply({
138
+ version,
139
+ r: records
140
+ });
128
141
  }
129
142
  },
130
143
  unregister(id, frame) {
@@ -344,11 +357,11 @@ class FrameImpl {
344
357
  this.#runSlotCleanups(occurrence);
345
358
  }
346
359
  if (!this.#mountedSlots.has(occurrence)) {
347
- const nodes = this.#invokeSlot(occurrence, callback, record, start);
360
+ const nodes = this.#invokeSlot(occurrence, callback, record, start, this.#options.adopt);
348
361
  if (nodes) this.#replaceRange(occurrence, start, nodes);
349
362
  this.#slotNodes.set(occurrence, nodes);
350
363
  this.#mountedSlots.add(occurrence);
351
- if (!record) this.#discoverRegions(occurrence, start);
364
+ this.#discoverRegions(occurrence, start);
352
365
  this.#bindRegions(occurrence);
353
366
  } else if (record !== this.#slotArgs.get(occurrence)) {
354
367
  if (this.#refArgsUnchanged(occurrence, record)) {
@@ -365,11 +378,12 @@ class FrameImpl {
365
378
  if (!found.has(occurrence)) this.#unmountSlot(occurrence);
366
379
  }
367
380
  }
368
- #invokeSlot(occurrence, callback, record, start) {
381
+ #invokeSlot(occurrence, callback, record, start, adopted) {
369
382
  const cleanups = this.#slotCleanups.get(occurrence) ?? [];
370
383
  const ctx = {
371
384
  frame: this.#options.claimScope ?? this.#options.id,
372
385
  key: occurrence,
386
+ adopted: !!adopted,
373
387
  onCleanup: fn => cleanups.push(fn),
374
388
  existing: start ? rangeInterior(start, slotEnd(occurrence)) : []
375
389
  };
@@ -512,11 +526,22 @@ class FrameImpl {
512
526
  }
513
527
  #refArgsUnchanged(occurrence, record) {
514
528
  const old = this.#slotArgs.get(occurrence);
515
- if (!old || old.kind !== "slot" || !record || record.kind !== "slot") return false;
516
- const a = old.args || {};
529
+ if (!record || record.kind !== "slot") return false;
530
+ if (old && old.kind !== "slot") return false;
531
+ const a = old && old.args || {};
517
532
  const b = record.args || {};
518
533
  const ka = Object.keys(a);
519
- if (ka.length !== Object.keys(b).length) return false;
534
+ const kb = Object.keys(b);
535
+ if (kb.length < ka.length) return false;
536
+ if (kb.length !== ka.length) {
537
+ const regions = this.#slotRegions.get(occurrence);
538
+ for (const key of kb) {
539
+ if (key in a) continue;
540
+ const vb = b[key];
541
+ if (!vb || typeof vb !== "object" || typeof vb.$frame !== "string") return false;
542
+ if (!regions || !regions.has(vb.$frame)) return false;
543
+ }
544
+ }
520
545
  const cache = this.#slotResolvedRefs.get(occurrence);
521
546
  for (const key of ka) {
522
547
  const va = a[key];
@@ -1320,7 +1345,7 @@ function slotsFor(props) {
1320
1345
  const v = props[prop];
1321
1346
  return settle(normalizeSlotContent(typeof v === "function" ? v(slotProps) : v));
1322
1347
  };
1323
- if (ctx && ctx.frame && ctx.existing && ctx.existing.length) {
1348
+ if (ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length) {
1324
1349
  return claimRender(`sc-${ctx.frame}-${ctx.key}-`, ctx.existing, render);
1325
1350
  }
1326
1351
  return render();
@@ -266,7 +266,8 @@ function renderToStream(code, options = {}) {
266
266
  }
267
267
  tasks += task + ";";
268
268
  if (!timer && firstFlushed) {
269
- timer = setTimeout(writeTasks);
269
+ timer = true;
270
+ queue(() => queue(writeTasks));
270
271
  }
271
272
  };
272
273
  const onDone = () => {
@@ -349,7 +350,6 @@ function renderToStream(code, options = {}) {
349
350
  buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks}</script>`);
350
351
  tasks = "";
351
352
  }
352
- timer && clearTimeout(timer);
353
353
  timer = null;
354
354
  };
355
355
  let context;
@@ -601,6 +601,26 @@ function renderToStream(code, options = {}) {
601
601
  });
602
602
  shellCompleted = true;
603
603
  }
604
+ const MIN_DRAIN_TURNS = 8;
605
+ let lastBlockingSize = -1;
606
+ let lastRegistrySize = -1;
607
+ let drainTurn = 0;
608
+ const scheduleFlush = fn => {
609
+ const attempt = () => {
610
+ if (registry.size !== lastRegistrySize || drainTurn++ < MIN_DRAIN_TURNS) {
611
+ if (registry.size !== lastRegistrySize) drainTurn = 0;
612
+ lastRegistrySize = registry.size;
613
+ queue(attempt);
614
+ return;
615
+ }
616
+ fn();
617
+ };
618
+ const progressed = blockingPromises.size !== lastBlockingSize;
619
+ lastBlockingSize = blockingPromises.size;
620
+ lastRegistrySize = -1;
621
+ drainTurn = 0;
622
+ progressed ? queue(attempt) : setTimeout(attempt);
623
+ };
604
624
  return {
605
625
  then(fn) {
606
626
  function complete() {
@@ -616,7 +636,7 @@ function renderToStream(code, options = {}) {
616
636
  } else onCompleteAll = complete;
617
637
  function flush() {
618
638
  allSettled(blockingPromises).then(() => {
619
- setTimeout(() => {
639
+ scheduleFlush(() => {
620
640
  if (!resolveRootHoles()) return flush();
621
641
  queue(flushEnd);
622
642
  });
@@ -627,7 +647,7 @@ function renderToStream(code, options = {}) {
627
647
  pipe(w) {
628
648
  function flush() {
629
649
  allSettled(blockingPromises).then(() => {
630
- setTimeout(() => {
650
+ scheduleFlush(() => {
631
651
  doShell();
632
652
  if (!shellCompleted) return flush();
633
653
  buffer = writable = w;
@@ -647,7 +667,7 @@ function renderToStream(code, options = {}) {
647
667
  const p = new Promise(r => resolve = r);
648
668
  function flush() {
649
669
  allSettled(blockingPromises).then(() => {
650
- setTimeout(() => {
670
+ scheduleFlush(() => {
651
671
  doShell();
652
672
  if (!shellCompleted) return flush();
653
673
  const encoder = new TextEncoder();
@@ -861,14 +881,12 @@ function escape(s, attr) {
861
881
  }
862
882
  return s;
863
883
  }
864
- const delimCode = attr ? 34 : 60;
865
- const len = s.length;
866
- for (let i = 0; i < len; i++) {
867
- const c = s.charCodeAt(i);
868
- if (c === 38 || c === delimCode) return escapeSlow(s, attr, i);
869
- }
870
- return s;
884
+ const i = s.search(attr ? ESCAPE_ATTR : ESCAPE_CONTENT);
885
+ if (i < 0) return s;
886
+ return escapeSlow(s, attr, i);
871
887
  }
888
+ const ESCAPE_CONTENT = /[&<]/;
889
+ const ESCAPE_ATTR = /[&"]/;
872
890
  function escapeSlow(s, attr, start) {
873
891
  const delim = attr ? '"' : "<";
874
892
  const delimCode = attr ? 34 : 60;
@@ -1516,7 +1534,7 @@ function renderedHtmlOf(out) {
1516
1534
  try {
1517
1535
  const res = solidJs.sharedConfig.context.resolve(out);
1518
1536
  if (res && res.t && (!res.h || !res.h.length)) {
1519
- return res.t[0].replace(/<!--[^>]*-->/g, "");
1537
+ return res.t[0].replace(/<!--[^>]*-->/g, "").replace(/ _hk=("[^"]*"|[^\s>]+)/g, "");
1520
1538
  }
1521
1539
  } catch (e) {}
1522
1540
  return null;
@@ -264,7 +264,8 @@ function renderToStream(code, options = {}) {
264
264
  }
265
265
  tasks += task + ";";
266
266
  if (!timer && firstFlushed) {
267
- timer = setTimeout(writeTasks);
267
+ timer = true;
268
+ queue(() => queue(writeTasks));
268
269
  }
269
270
  };
270
271
  const onDone = () => {
@@ -347,7 +348,6 @@ function renderToStream(code, options = {}) {
347
348
  buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks}</script>`);
348
349
  tasks = "";
349
350
  }
350
- timer && clearTimeout(timer);
351
351
  timer = null;
352
352
  };
353
353
  let context;
@@ -599,6 +599,26 @@ function renderToStream(code, options = {}) {
599
599
  });
600
600
  shellCompleted = true;
601
601
  }
602
+ const MIN_DRAIN_TURNS = 8;
603
+ let lastBlockingSize = -1;
604
+ let lastRegistrySize = -1;
605
+ let drainTurn = 0;
606
+ const scheduleFlush = fn => {
607
+ const attempt = () => {
608
+ if (registry.size !== lastRegistrySize || drainTurn++ < MIN_DRAIN_TURNS) {
609
+ if (registry.size !== lastRegistrySize) drainTurn = 0;
610
+ lastRegistrySize = registry.size;
611
+ queue(attempt);
612
+ return;
613
+ }
614
+ fn();
615
+ };
616
+ const progressed = blockingPromises.size !== lastBlockingSize;
617
+ lastBlockingSize = blockingPromises.size;
618
+ lastRegistrySize = -1;
619
+ drainTurn = 0;
620
+ progressed ? queue(attempt) : setTimeout(attempt);
621
+ };
602
622
  return {
603
623
  then(fn) {
604
624
  function complete() {
@@ -614,7 +634,7 @@ function renderToStream(code, options = {}) {
614
634
  } else onCompleteAll = complete;
615
635
  function flush() {
616
636
  allSettled(blockingPromises).then(() => {
617
- setTimeout(() => {
637
+ scheduleFlush(() => {
618
638
  if (!resolveRootHoles()) return flush();
619
639
  queue(flushEnd);
620
640
  });
@@ -625,7 +645,7 @@ function renderToStream(code, options = {}) {
625
645
  pipe(w) {
626
646
  function flush() {
627
647
  allSettled(blockingPromises).then(() => {
628
- setTimeout(() => {
648
+ scheduleFlush(() => {
629
649
  doShell();
630
650
  if (!shellCompleted) return flush();
631
651
  buffer = writable = w;
@@ -645,7 +665,7 @@ function renderToStream(code, options = {}) {
645
665
  const p = new Promise(r => resolve = r);
646
666
  function flush() {
647
667
  allSettled(blockingPromises).then(() => {
648
- setTimeout(() => {
668
+ scheduleFlush(() => {
649
669
  doShell();
650
670
  if (!shellCompleted) return flush();
651
671
  const encoder = new TextEncoder();
@@ -859,14 +879,12 @@ function escape(s, attr) {
859
879
  }
860
880
  return s;
861
881
  }
862
- const delimCode = attr ? 34 : 60;
863
- const len = s.length;
864
- for (let i = 0; i < len; i++) {
865
- const c = s.charCodeAt(i);
866
- if (c === 38 || c === delimCode) return escapeSlow(s, attr, i);
867
- }
868
- return s;
882
+ const i = s.search(attr ? ESCAPE_ATTR : ESCAPE_CONTENT);
883
+ if (i < 0) return s;
884
+ return escapeSlow(s, attr, i);
869
885
  }
886
+ const ESCAPE_CONTENT = /[&<]/;
887
+ const ESCAPE_ATTR = /[&"]/;
870
888
  function escapeSlow(s, attr, start) {
871
889
  const delim = attr ? '"' : "<";
872
890
  const delimCode = attr ? 34 : 60;
@@ -1514,7 +1532,7 @@ function renderedHtmlOf(out) {
1514
1532
  try {
1515
1533
  const res = sharedConfig.context.resolve(out);
1516
1534
  if (res && res.t && (!res.h || !res.h.length)) {
1517
- return res.t[0].replace(/<!--[^>]*-->/g, "");
1535
+ return res.t[0].replace(/<!--[^>]*-->/g, "").replace(/ _hk=("[^"]*"|[^\s>]+)/g, "");
1518
1536
  }
1519
1537
  } catch (e) {}
1520
1538
  return null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@solidjs/web",
3
3
  "description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
4
- "version": "2.0.0-beta.23",
4
+ "version": "2.0.0-beta.24",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
7
7
  "homepage": "https://solidjs.com",
@@ -296,10 +296,10 @@
296
296
  "seroval-plugins": "~1.5.4"
297
297
  },
298
298
  "peerDependencies": {
299
- "solid-js": "^2.0.0-beta.23"
299
+ "solid-js": "^2.0.0-beta.24"
300
300
  },
301
301
  "devDependencies": {
302
- "solid-js": "2.0.0-beta.23"
302
+ "solid-js": "2.0.0-beta.24"
303
303
  },
304
304
  "scripts": {
305
305
  "build": "npm-run-all -nl build:clean types:copy-jsx build:js",
@@ -332,18 +332,21 @@ async function decodeResponse(response, codecOptions) {
332
332
  const config = {
333
333
  provideEvent: undefined,
334
334
  collectFlightData: undefined,
335
+ transformResult: undefined,
335
336
  transformDirectResult: undefined,
336
337
  endpoint: "/_server"
337
338
  };
338
339
  function configureServerFunctionsServer({
339
340
  provideEvent,
340
341
  collectFlightData,
342
+ transformResult,
341
343
  transformDirectResult,
342
344
  endpoint,
343
345
  codec
344
346
  } = {}) {
345
347
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
346
348
  if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
349
+ if (transformResult !== undefined) config.transformResult = transformResult;
347
350
  if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
348
351
  if (endpoint !== undefined) config.endpoint = endpoint;
349
352
  if (codec !== undefined) configureServerFunctionsCodec(codec);
@@ -526,6 +529,7 @@ async function handleServerFunctionRequest(request, options = {}) {
526
529
  };
527
530
  const provide = options.provideEvent || provideEvent;
528
531
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
532
+ const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
529
533
  const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
530
534
  const parsed = await parseArguments(request, url, instance, codec);
531
535
  const headers = new Headers();
@@ -536,8 +540,8 @@ async function handleServerFunctionRequest(request, options = {}) {
536
540
  };
537
541
  return serverFunction(...parsed);
538
542
  });
539
- if (options.transformResult) {
540
- result = await options.transformResult(event, result, {
543
+ if (transformResult) {
544
+ result = await transformResult(event, result, {
541
545
  instance,
542
546
  request
543
547
  });
@@ -592,8 +596,8 @@ async function handleServerFunctionRequest(request, options = {}) {
592
596
  return encodeResult(result, headers, status, codec);
593
597
  } catch (x) {
594
598
  if (x instanceof Response || isResponseEnvelope(x)) {
595
- if (options.transformResult) {
596
- x = await options.transformResult(event, x, {
599
+ if (transformResult) {
600
+ x = await transformResult(event, x, {
597
601
  instance,
598
602
  request,
599
603
  thrown: true
@@ -330,18 +330,21 @@ async function decodeResponse(response, codecOptions) {
330
330
  const config = {
331
331
  provideEvent: undefined,
332
332
  collectFlightData: undefined,
333
+ transformResult: undefined,
333
334
  transformDirectResult: undefined,
334
335
  endpoint: "/_server"
335
336
  };
336
337
  function configureServerFunctionsServer({
337
338
  provideEvent,
338
339
  collectFlightData,
340
+ transformResult,
339
341
  transformDirectResult,
340
342
  endpoint,
341
343
  codec
342
344
  } = {}) {
343
345
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
344
346
  if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
347
+ if (transformResult !== undefined) config.transformResult = transformResult;
345
348
  if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
346
349
  if (endpoint !== undefined) config.endpoint = endpoint;
347
350
  if (codec !== undefined) configureServerFunctionsCodec(codec);
@@ -524,6 +527,7 @@ async function handleServerFunctionRequest(request, options = {}) {
524
527
  };
525
528
  const provide = options.provideEvent || provideEvent;
526
529
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
530
+ const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
527
531
  const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
528
532
  const parsed = await parseArguments(request, url, instance, codec);
529
533
  const headers = new Headers();
@@ -534,8 +538,8 @@ async function handleServerFunctionRequest(request, options = {}) {
534
538
  };
535
539
  return serverFunction(...parsed);
536
540
  });
537
- if (options.transformResult) {
538
- result = await options.transformResult(event, result, {
541
+ if (transformResult) {
542
+ result = await transformResult(event, result, {
539
543
  instance,
540
544
  request
541
545
  });
@@ -590,8 +594,8 @@ async function handleServerFunctionRequest(request, options = {}) {
590
594
  return encodeResult(result, headers, status, codec);
591
595
  } catch (x) {
592
596
  if (x instanceof Response || isResponseEnvelope(x)) {
593
- if (options.transformResult) {
594
- x = await options.transformResult(event, x, {
597
+ if (transformResult) {
598
+ x = await transformResult(event, x, {
595
599
  instance,
596
600
  request,
597
601
  thrown: true
@@ -72,6 +72,14 @@ export interface FrameWrite {
72
72
 
73
73
  /** Context passed to a slot callback. */
74
74
  export interface SlotContext {
75
+ /**
76
+ * True only for the hydration-attach invocation of an adopted
77
+ * document-SSR range — the one call a consumer may answer with a claim
78
+ * (`existing` IS the server-rendered output for these args). Unset on
79
+ * stream-driven re-calls: those must render for real, or content the
80
+ * re-call displaced (e.g. `{$frame}` region ranges) is dropped.
81
+ */
82
+ adopted?: boolean;
75
83
  /**
76
84
  * Register cleanup for when this occurrence's range is removed from the
77
85
  * server content, or the owning frame is disposed.
@@ -203,3 +211,12 @@ export function createFrameInsertable(options: FrameOptions): {
203
211
  readonly frame: Frame | null;
204
212
  dispose(): void;
205
213
  };
214
+
215
+ /**
216
+ * Binds a frame to an EXISTING marker range — the document-SSR adoption
217
+ * path: the page already holds the server-rendered boundary between
218
+ * `frame:<id>:start`/`:end` comments; the frame constructed over it treats
219
+ * that content as its own (first stream morphs rather than materializes)
220
+ * and slots sync immediately, claiming their server-rendered DOM.
221
+ */
222
+ export function adoptFrameRange(start: Comment, end: Comment, options?: FrameOptions): Frame;
@@ -96,10 +96,11 @@ export interface ServerComponentHandlerOptions<C = unknown> {
96
96
  * key a WeakMap of boundaries (dying with their call sites); ownerless calls
97
97
  * fall back to one boundary per function id.
98
98
  */
99
- export function createServerComponentHandler<C>(
100
- options: ServerComponentHandlerOptions<C>
101
- ): {
99
+ export function createServerComponentHandler<C>(options: ServerComponentHandlerOptions<C>): {
102
100
  capture?(info: { id: string; meta: unknown }): unknown;
103
101
  intercept?(info: { id: string; meta: unknown; args: unknown[] }): C | undefined;
104
- handle(response: Response, ctx: { id: string; meta: unknown; args: unknown[]; context: unknown }): C | undefined;
102
+ handle(
103
+ response: Response,
104
+ ctx: { id: string; meta: unknown; args: unknown[]; context: unknown }
105
+ ): C | undefined;
105
106
  };
@@ -102,6 +102,23 @@ export interface ServerFunctionsServerConfig {
102
102
  * router); per-handler `collectFlightData` options override it.
103
103
  */
104
104
  collectFlightData?: CollectFlightDataHook;
105
+ /**
106
+ * Server-wide default for the handler's `transformResult` (same contract
107
+ * — see `HandleServerFunctionRequestOptions`); a per-request option
108
+ * overrides it. Registering it here makes result policies (e.g. frames'
109
+ * `frameTransformResult`) work through generic dispatchers that call
110
+ * `handleServerFunctionRequest(request)` with no options.
111
+ */
112
+ transformResult?(
113
+ event: ServerFunctionEvent,
114
+ result: unknown,
115
+ context: { instance: string | null; request: Request; thrown?: boolean }
116
+ ): unknown | ResponseEnvelope | Promise<unknown | ResponseEnvelope>;
117
+ /**
118
+ * The in-process mirror of `transformResult` for direct (same-server)
119
+ * calls during document SSR — e.g. frames' `frameTransformDirectResult`.
120
+ */
121
+ transformDirectResult?(value: unknown, options: { id: string }): unknown;
105
122
  /**
106
123
  * Endpoint the HTTP handler is mounted on, used for the `url` of SSR'd
107
124
  * references (e.g. form actions) — must match the client configuration.
@@ -369,3 +369,20 @@ export function decodeResponse<T = unknown>(
369
369
  response: Response,
370
370
  codecOptions?: JSONCodecOptions
371
371
  ): Promise<T | undefined>;
372
+
373
+ /**
374
+ * Frame one payload for the server-function wire: a `;0x<len32>;` length
375
+ * prefix followed by the utf-8 data. Both transports (server-function
376
+ * responses and frame streams) share this framing.
377
+ */
378
+ export function createChunk(data: string): Uint8Array;
379
+
380
+ /**
381
+ * Incremental decoder for `createChunk` framing over a byte stream: `next()`
382
+ * yields one complete payload string per call (async-iterator result shape),
383
+ * buffering partial frames internally until their length prefix is satisfied.
384
+ */
385
+ export class ChunkReader {
386
+ constructor(stream: ReadableStream<Uint8Array>);
387
+ next(): Promise<{ done: boolean; value: string | undefined }>;
388
+ }
@@ -72,6 +72,14 @@ export interface FrameWrite {
72
72
 
73
73
  /** Context passed to a slot callback. */
74
74
  export interface SlotContext {
75
+ /**
76
+ * True only for the hydration-attach invocation of an adopted
77
+ * document-SSR range — the one call a consumer may answer with a claim
78
+ * (`existing` IS the server-rendered output for these args). Unset on
79
+ * stream-driven re-calls: those must render for real, or content the
80
+ * re-call displaced (e.g. `{$frame}` region ranges) is dropped.
81
+ */
82
+ adopted?: boolean;
75
83
  /**
76
84
  * Register cleanup for when this occurrence's range is removed from the
77
85
  * server content, or the owning frame is disposed.
@@ -203,3 +211,12 @@ export function createFrameInsertable(options: FrameOptions): {
203
211
  readonly frame: Frame | null;
204
212
  dispose(): void;
205
213
  };
214
+
215
+ /**
216
+ * Binds a frame to an EXISTING marker range — the document-SSR adoption
217
+ * path: the page already holds the server-rendered boundary between
218
+ * `frame:<id>:start`/`:end` comments; the frame constructed over it treats
219
+ * that content as its own (first stream morphs rather than materializes)
220
+ * and slots sync immediately, claiming their server-rendered DOM.
221
+ */
222
+ export function adoptFrameRange(start: Comment, end: Comment, options?: FrameOptions): Frame;
@@ -96,10 +96,11 @@ export interface ServerComponentHandlerOptions<C = unknown> {
96
96
  * key a WeakMap of boundaries (dying with their call sites); ownerless calls
97
97
  * fall back to one boundary per function id.
98
98
  */
99
- export function createServerComponentHandler<C>(
100
- options: ServerComponentHandlerOptions<C>
101
- ): {
99
+ export function createServerComponentHandler<C>(options: ServerComponentHandlerOptions<C>): {
102
100
  capture?(info: { id: string; meta: unknown }): unknown;
103
101
  intercept?(info: { id: string; meta: unknown; args: unknown[] }): C | undefined;
104
- handle(response: Response, ctx: { id: string; meta: unknown; args: unknown[]; context: unknown }): C | undefined;
102
+ handle(
103
+ response: Response,
104
+ ctx: { id: string; meta: unknown; args: unknown[]; context: unknown }
105
+ ): C | undefined;
105
106
  };
@@ -102,6 +102,23 @@ export interface ServerFunctionsServerConfig {
102
102
  * router); per-handler `collectFlightData` options override it.
103
103
  */
104
104
  collectFlightData?: CollectFlightDataHook;
105
+ /**
106
+ * Server-wide default for the handler's `transformResult` (same contract
107
+ * — see `HandleServerFunctionRequestOptions`); a per-request option
108
+ * overrides it. Registering it here makes result policies (e.g. frames'
109
+ * `frameTransformResult`) work through generic dispatchers that call
110
+ * `handleServerFunctionRequest(request)` with no options.
111
+ */
112
+ transformResult?(
113
+ event: ServerFunctionEvent,
114
+ result: unknown,
115
+ context: { instance: string | null; request: Request; thrown?: boolean }
116
+ ): unknown | ResponseEnvelope | Promise<unknown | ResponseEnvelope>;
117
+ /**
118
+ * The in-process mirror of `transformResult` for direct (same-server)
119
+ * calls during document SSR — e.g. frames' `frameTransformDirectResult`.
120
+ */
121
+ transformDirectResult?(value: unknown, options: { id: string }): unknown;
105
122
  /**
106
123
  * Endpoint the HTTP handler is mounted on, used for the `url` of SSR'd
107
124
  * references (e.g. form actions) — must match the client configuration.
@@ -369,3 +369,20 @@ export function decodeResponse<T = unknown>(
369
369
  response: Response,
370
370
  codecOptions?: JSONCodecOptions
371
371
  ): Promise<T | undefined>;
372
+
373
+ /**
374
+ * Frame one payload for the server-function wire: a `;0x<len32>;` length
375
+ * prefix followed by the utf-8 data. Both transports (server-function
376
+ * responses and frame streams) share this framing.
377
+ */
378
+ export function createChunk(data: string): Uint8Array;
379
+
380
+ /**
381
+ * Incremental decoder for `createChunk` framing over a byte stream: `next()`
382
+ * yields one complete payload string per call (async-iterator result shape),
383
+ * buffering partial frames internally until their length prefix is satisfied.
384
+ */
385
+ export class ChunkReader {
386
+ constructor(stream: ReadableStream<Uint8Array>);
387
+ next(): Promise<{ done: boolean; value: string | undefined }>;
388
+ }