octane 0.1.45 → 0.1.46

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/README.md CHANGED
@@ -25,6 +25,11 @@ Vite builds can fold proven immutable CSS-module class strings into templates.
25
25
  See the [CSS-module constants guide](https://github.com/octanejs/octane/blob/main/docs/compiler-css-module-constants.md)
26
26
  for the provider contract and stylesheet-loading guarantees.
27
27
 
28
+ Custom native integrations can opt into the experimental
29
+ [Valdi writer compiler](https://github.com/octanejs/octane/blob/main/docs/valdi-compiler.md).
30
+ It requires an application-provided adapter; Octane does not bundle a Valdi
31
+ runtime or native build integration.
32
+
28
33
  Direct Node or Bun server scripts can preload `octane/compiler/register` to
29
34
  compile imported Octane components without going through Vite. See the
30
35
  [SSR guide](https://github.com/octanejs/octane/blob/main/docs/ssr.md#run-an-ssg-script-directly).
@@ -12041,6 +12041,14 @@ function reconcileDeoptNode(prev, value, ownerBlock, ns) {
12041
12041
  return null;
12042
12042
  }
12043
12043
  function reconcileDeoptChildren(el, children, ownerBlock) {
12044
+ const type = typeof children;
12045
+ if (type === "string" && children !== "" || type === "number" || type === "bigint") {
12046
+ const first = getFirstChild(el);
12047
+ if (first !== null && first.nodeType === 3 && getNextSibling(first) === null && first.$$deoptKey === 0 && first.$$portalEnd == null) {
12048
+ updateTextValue(first, String(children));
12049
+ return;
12050
+ }
12051
+ }
12044
12052
  const childNs = deoptChildNamespace(el);
12045
12053
  const next = [];
12046
12054
  const nextKeys = [];
@@ -12165,7 +12173,7 @@ function deoptItemBody(item, scope) {
12165
12173
  const block = scope.block;
12166
12174
  const hydration = activeHydration();
12167
12175
  const existingChild = scope.slots[0];
12168
- const needsBlocks = descNeedsBlocks(item) || isHostDescriptor(item) && existingChild?.__kind === "childSlot" && existingChild.currentComp === hostElementBody;
12176
+ const needsBlocks = isHostDescriptor(item) && existingChild?.__kind === "childSlot" && existingChild.currentComp === hostElementBody || descNeedsBlocks(item);
12169
12177
  const sm = block.startMarker;
12170
12178
  if (sm !== null && sm === block.endMarker && sm.nodeType !== 8 && sm.parentNode !== null && (needsBlocks || !isHostDescriptor(item))) {
12171
12179
  const p = sm.parentNode;
@@ -12981,7 +12989,7 @@ function childSlot(parentScope, slotKey, domParent, value, anchor, ownEnd, ownsH
12981
12989
  if (iterable !== null) value = iterable;
12982
12990
  const preparedList = compiledMapBody !== void 0 ? { items: value, keys: null } : prepareDeoptList(value, false, includeKeyedSingle);
12983
12991
  let state = parentScope.slots[slotKey];
12984
- const pureHost = preparedList === null && isHostDescriptor(value) && !descNeedsBlocks(value) && state?.currentComp !== hostElementBody;
12992
+ const pureHost = preparedList === null && isHostDescriptor(value) && state?.currentComp !== hostElementBody && !descNeedsBlocks(value);
12985
12993
  let rootShapeChanged = false;
12986
12994
  if (state !== void 0 && ROOT_RENDER_TRANSACTION !== null) {
12987
12995
  const component = pureHost || preparedList !== null ? null : isHostDescriptor(value) ? hostElementBody : valueComponent;
@@ -1932,21 +1932,9 @@ function captureComponentReplayState(scope, frame) {
1932
1932
  streamNextId: stream?.nextId ?? 0,
1933
1933
  streamActiveTryKeys: stream?.activeTryKeys.slice() ?? [],
1934
1934
  streamActiveOwnerKeys: stream?.activeOwnerKeys.slice() ?? [],
1935
- streamPassBoundaryKeys: stream?.activePassBoundaryKeys === null || stream?.activePassBoundaryKeys === void 0 ? null : new Set(stream.activePassBoundaryKeys),
1935
+ streamPassBoundaryCount: stream?.activePassBoundaryKeys?.size ?? 0,
1936
1936
  asyncScope: ASYNC_SCOPE,
1937
- streamBoundaries: stream === null ? null : Array.from(stream.boundaries, ([key, entry]) => ({
1938
- key,
1939
- entry,
1940
- id: entry.id,
1941
- order: entry.order,
1942
- state: entry.state,
1943
- html: entry.html,
1944
- seeds: entry.seeds.slice(),
1945
- pendingIdOffset: entry.pendingIdOffset,
1946
- ancestors: entry.ancestors.slice(),
1947
- owners: entry.owners.slice(),
1948
- namespace: entry.namespace
1949
- })),
1937
+ streamReplayCheckpoint: stream?.replay?.length ?? 0,
1950
1938
  frameDeferred: frame?.deferred ?? false,
1951
1939
  frameNextChild: frame?.nextChild ?? 0,
1952
1940
  frameScopedChildren: frame?.scopedChildren === null || frame?.scopedChildren === void 0 ? null : new Map(frame.scopedChildren),
@@ -1996,30 +1984,19 @@ function rewindComponentReplayState(snapshot, scope, frame) {
1996
1984
  VT_SSR_STACK.push(entry.candidate);
1997
1985
  }
1998
1986
  const stream = snapshot.stream;
1999
- if (stream !== null && snapshot.streamBoundaries !== null) {
1987
+ if (stream !== null) {
2000
1988
  stream.nextId = snapshot.streamNextId;
2001
- if (stream.activePassBoundaryKeys !== null && snapshot.streamPassBoundaryKeys !== null) {
2002
- stream.activePassBoundaryKeys.clear();
2003
- for (const key of snapshot.streamPassBoundaryKeys) stream.activePassBoundaryKeys.add(key);
1989
+ if (stream.activePassBoundaryKeys !== null) {
1990
+ let index = 0;
1991
+ for (const key of stream.activePassBoundaryKeys) {
1992
+ if (index++ >= snapshot.streamPassBoundaryCount) stream.activePassBoundaryKeys.delete(key);
1993
+ }
2004
1994
  }
2005
1995
  stream.activeTryKeys.length = 0;
2006
1996
  stream.activeTryKeys.push(...snapshot.streamActiveTryKeys);
2007
1997
  stream.activeOwnerKeys.length = 0;
2008
1998
  stream.activeOwnerKeys.push(...snapshot.streamActiveOwnerKeys);
2009
- stream.boundaries.clear();
2010
- for (const saved of snapshot.streamBoundaries) {
2011
- const entry = saved.entry;
2012
- entry.id = saved.id;
2013
- entry.order = saved.order;
2014
- entry.state = saved.state;
2015
- entry.html = saved.html;
2016
- entry.seeds = saved.seeds.slice();
2017
- entry.pendingIdOffset = saved.pendingIdOffset;
2018
- entry.ancestors = saved.ancestors.slice();
2019
- entry.owners = saved.owners.slice();
2020
- entry.namespace = saved.namespace;
2021
- stream.boundaries.set(saved.key, entry);
2022
- }
1999
+ rewindStreamBoundaryReplay(stream, snapshot.streamReplayCheckpoint);
2023
2000
  }
2024
2001
  scope.$$ctxValues = snapshot.context;
2025
2002
  if (frame !== null) {
@@ -3920,6 +3897,40 @@ function renderToStaticMarkup(entryComponent, props, options) {
3920
3897
  const html = spliceHead(pass.body, pass.head);
3921
3898
  return { html: pass.vtCandidates ? vtSsrStrip(html) : html, css: pass.css };
3922
3899
  }
3900
+ function recordStreamBoundaryMutation(stream, key) {
3901
+ if (stream.replay === null) return;
3902
+ const boundary = stream.boundaries.get(key);
3903
+ stream.replay.push({
3904
+ key,
3905
+ boundary,
3906
+ value: boundary === void 0 ? void 0 : { ...boundary }
3907
+ });
3908
+ }
3909
+ function rewindStreamBoundaryReplay(stream, checkpoint) {
3910
+ const replay = stream.replay;
3911
+ if (replay === null) return;
3912
+ let restoredDeletion = false;
3913
+ while (replay.length > checkpoint) {
3914
+ const saved = replay.pop();
3915
+ if (saved.boundary === void 0) {
3916
+ stream.boundaries.delete(saved.key);
3917
+ } else {
3918
+ const boundary = saved.boundary;
3919
+ const value = saved.value;
3920
+ Object.assign(boundary, value);
3921
+ boundary.error = value.error;
3922
+ boundary.errorReported = value.errorReported;
3923
+ boundary.errorFlushed = value.errorFlushed;
3924
+ if (!stream.boundaries.has(saved.key)) restoredDeletion = true;
3925
+ stream.boundaries.set(saved.key, boundary);
3926
+ }
3927
+ }
3928
+ if (restoredDeletion) {
3929
+ const ordered = [...stream.boundaries].sort((a, b) => a[1].order - b[1].order);
3930
+ stream.boundaries.clear();
3931
+ for (const [key, boundary] of ordered) stream.boundaries.set(key, boundary);
3932
+ }
3933
+ }
3923
3934
  let STREAM_REALM_SALT = null;
3924
3935
  function streamRealmSalt() {
3925
3936
  if (STREAM_REALM_SALT !== null) return STREAM_REALM_SALT;
@@ -3948,6 +3959,7 @@ function pruneUnrepresentedStreamDescendants(stream, ownerKey, ownerHtml) {
3948
3959
  }
3949
3960
  if (nearestOwner !== ownerKey) continue;
3950
3961
  if (ownerHtml.includes(import_constants.STREAM_BOUNDARY_ATTR + '="' + child.id + '"')) continue;
3962
+ recordStreamBoundaryMutation(stream, childKey);
3951
3963
  stream.boundaries.delete(childKey);
3952
3964
  removed = true;
3953
3965
  }
@@ -3987,7 +3999,10 @@ function ssrTry(scope, siteKey, tryFn, pendFn, catchFn, namespace = FRAME?.names
3987
3999
  ancestorKeys = stream.activeTryKeys.slice();
3988
4000
  ownerKeys = stream.activeOwnerKeys.slice();
3989
4001
  entry = stream.boundaries.get(key);
3990
- if (entry !== void 0) entry.namespace = namespace;
4002
+ if (entry !== void 0) {
4003
+ recordStreamBoundaryMutation(stream, key);
4004
+ entry.namespace = namespace;
4005
+ }
3991
4006
  if (entry !== void 0 && entry.state === "pending") {
3992
4007
  entry.ancestors = ancestorKeys;
3993
4008
  entry.owners = ownerKeys;
@@ -4183,6 +4198,7 @@ function ssrTry(scope, siteKey, tryFn, pendFn, catchFn, namespace = FRAME?.names
4183
4198
  ancestors: ancestorKeys,
4184
4199
  owners: ownerKeys
4185
4200
  };
4201
+ recordStreamBoundaryMutation(stream, key);
4186
4202
  stream.boundaries.set(key, entry);
4187
4203
  enterBoundaryIds(pendingIdOffset);
4188
4204
  } else {
@@ -4235,6 +4251,7 @@ function ssrTry(scope, siteKey, tryFn, pendFn, catchFn, namespace = FRAME?.names
4235
4251
  ancestors: ancestorKeys,
4236
4252
  owners: ownerKeys
4237
4253
  };
4254
+ recordStreamBoundaryMutation(stream, key);
4238
4255
  stream.boundaries.set(key, entry);
4239
4256
  enterBoundaryIds(pendingIdOffset);
4240
4257
  } else if (entry.state === "pending") {
@@ -4337,12 +4354,15 @@ async function runStream(component, props, options, sink) {
4337
4354
  token: createStreamToken(),
4338
4355
  activePassBoundaryKeys: null,
4339
4356
  activeTryKeys: [],
4340
- activeOwnerKeys: []
4357
+ activeOwnerKeys: [],
4358
+ replay: null
4341
4359
  };
4342
4360
  const renderFullPass = () => {
4343
4361
  const boundaryKeys = /* @__PURE__ */ new Set();
4344
4362
  const previousBoundaryKeys = stream.activePassBoundaryKeys;
4363
+ const previousReplay = stream.replay;
4345
4364
  stream.activePassBoundaryKeys = boundaryKeys;
4365
+ stream.replay = [];
4346
4366
  try {
4347
4367
  return {
4348
4368
  pass: withStream(
@@ -4353,6 +4373,7 @@ async function runStream(component, props, options, sink) {
4353
4373
  };
4354
4374
  } finally {
4355
4375
  stream.activePassBoundaryKeys = previousBoundaryKeys;
4376
+ stream.replay = previousReplay;
4356
4377
  }
4357
4378
  };
4358
4379
  const injection = options?.injection;
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  version: () => version
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const version = "0.1.45";
24
+ const version = "0.1.46";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  version
@@ -1082,7 +1082,7 @@ class OctaneBundlerCompiler {
1082
1082
  if (!hostOwned) this._assertClientOnlySourceSupported(file, filename, renderer, collected);
1083
1083
  if (
1084
1084
  plainHelperSource &&
1085
- renderer.target === 'universal' &&
1085
+ (renderer.target === 'universal' || renderer.target === 'valdi') &&
1086
1086
  renderer.validation !== undefined &&
1087
1087
  this._isProjectOwnedSource(file) &&
1088
1088
  !this.exclude.some((path) => file.includes(path)) &&
@@ -1622,6 +1622,12 @@ function importSourceRanges(sources) {
1622
1622
  export function validateRendererModuleSource(source, filename, renderer) {
1623
1623
  if (renderer?.validation === undefined) return;
1624
1624
  const ast = parseModule(source, filename);
1625
+ validateRendererAst(ast, filename, renderer);
1626
+ }
1627
+
1628
+ /** Reuse renderer restrictions without reparsing an already adopted AST. */
1629
+ export function validateRendererAst(ast, filename, renderer) {
1630
+ if (renderer?.validation === undefined) return;
1625
1631
  validateRendererSource(ast, { filename, renderer });
1626
1632
  }
1627
1633