@qwik.dev/core 2.0.0-beta.32 → 2.0.0-beta.35

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/core.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.32-dev+0e29f8a
3
+ * @qwik.dev/core 2.0.0-beta.35-dev+4603135
4
4
  * Copyright QwikDev. All Rights Reserved.
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://github.com/QwikDev/qwik/blob/main/LICENSE
@@ -9,13 +9,14 @@ import { isDev, isServer, isBrowser as isBrowser$1 } from '@qwik.dev/core/build'
9
9
  export { isBrowser, isDev, isServer } from '@qwik.dev/core/build';
10
10
  import { p } from '@qwik.dev/core/preloader';
11
11
 
12
- // same as isDev but separate so we can test
13
- const g = globalThis;
14
- const qDev = g.qDev !== false;
15
- const qInspector = g.qInspector === true;
16
- const qDynamicPlatform = g.qDynamicPlatform !== false;
17
- const qTest = g.qTest === true;
18
- const qRuntimeQrl = g.qRuntimeQrl === true;
12
+ // Direct `globalThis.X` accesses (no alias) so Terser's `global_defs` engages
13
+ // and folds these to literal booleans in production builds, allowing
14
+ // `qTest ? testBranch : prodBranch` shims to tree-shake.
15
+ const qDev = globalThis.qDev !== false;
16
+ const qInspector = globalThis.qInspector === true;
17
+ const qDynamicPlatform = globalThis.qDynamicPlatform !== false;
18
+ const qTest = globalThis.qTest === true;
19
+ const qRuntimeQrl = globalThis.qRuntimeQrl === true;
19
20
  const seal = (obj) => {
20
21
  if (qDev) {
21
22
  Object.seal(obj);
@@ -189,9 +190,9 @@ const isPrimitiveOrNullUndefined = (v) => {
189
190
  return (typeof v !== 'object' && typeof v !== 'function') || v === null || v === undefined;
190
191
  };
191
192
 
192
- const baseUrl = 'https://qwikdev-build-v2.qwik-8nx.pages.dev/docs/errors/#Q';
193
+ const baseUrl = 'https://qwikdev-build-v2.qwik-8nx.pages.dev/docs/errors/#q';
193
194
  const codeToText = (code, ...parts) => {
194
- if (qDev) {
195
+ if (isDev) {
195
196
  // Keep one error, one line to make it easier to search for the error message.
196
197
  // Keep in sync with packages/docs/src/routes/docs/errors/index.mdx
197
198
  const MAP = [
@@ -443,6 +444,7 @@ const ELEMENT_KEY = 'q:key';
443
444
  const ELEMENT_PROPS = 'q:props';
444
445
  const ELEMENT_SEQ = 'q:seq';
445
446
  const ELEMENT_SEQ_IDX = 'q:seqIdx';
447
+ const Q_PREFIX = 'q:';
446
448
  const ITERATION_ITEM_SINGLE = 'q:p'; // Single iteration parameter (not an array)
447
449
  const ITERATION_ITEM_MULTI = 'q:ps'; // Multiple iteration parameters (array)
448
450
  /** Non serializable markers - always begins with `:` character */
@@ -450,6 +452,8 @@ const NON_SERIALIZABLE_MARKER_PREFIX = ':';
450
452
  const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
451
453
  const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx';
452
454
  const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags';
455
+ const QCursorBoundary = Q_PREFIX + 'cursorBoundary';
456
+ const QNearestCursorBoundary = NON_SERIALIZABLE_MARKER_PREFIX + 'nearestCursorBoundary';
453
457
  const Q_PROPS_SEPARATOR = ':';
454
458
  const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
455
459
  const qwikInspectorAttr = 'data-qwik-inspector';
@@ -793,6 +797,30 @@ const styleKey = (qStyles, index) => {
793
797
  return `${hashCode(qStyles.$hash$)}-${index}`;
794
798
  };
795
799
 
800
+ /**
801
+ * Creates a function that schedules `fn` to run as a microtask. Microtasks run before browser
802
+ * paint, preventing flickering.
803
+ */
804
+ const createMicroTask = (fn) => {
805
+ return () => queueMicrotask(fn);
806
+ };
807
+ /**
808
+ * Creates a function that schedules `fn` to run as a macrotask. Macrotasks yield to the browser,
809
+ * allowing paint and user input. Used for time-slicing to avoid blocking the main thread.
810
+ */
811
+ const createMacroTask = (fn) => {
812
+ let macroTask;
813
+ if (typeof MessageChannel !== 'undefined') {
814
+ const channel = new MessageChannel();
815
+ channel.port1.onmessage = () => fn();
816
+ macroTask = () => channel.port2.postMessage(null);
817
+ }
818
+ else {
819
+ macroTask = () => setTimeout(fn);
820
+ }
821
+ return macroTask;
822
+ };
823
+
796
824
  // keep this import from core/build so the cjs build works
797
825
  const createPlatform = () => {
798
826
  return {
@@ -893,33 +921,13 @@ const isServerPlatform = () => {
893
921
  return false;
894
922
  };
895
923
 
896
- /**
897
- * Creates a function that schedules `fn` to run as a microtask. Microtasks run before browser
898
- * paint, preventing flickering.
899
- */
900
- const createMicroTask = (fn) => {
901
- return () => queueMicrotask(fn);
902
- };
903
- /**
904
- * Creates a function that schedules `fn` to run as a macrotask. Macrotasks yield to the browser,
905
- * allowing paint and user input. Used for time-slicing to avoid blocking the main thread.
906
- */
907
- const createMacroTask = (fn) => {
908
- let macroTask;
909
- if (typeof MessageChannel !== 'undefined') {
910
- const channel = new MessageChannel();
911
- channel.port1.onmessage = () => fn();
912
- macroTask = () => channel.port2.postMessage(null);
913
- }
914
- else {
915
- macroTask = () => setTimeout(fn);
916
- }
917
- return macroTask;
918
- };
919
-
920
- const isBrowser = import.meta.env.TEST ? !isServerPlatform() : !isServer;
924
+ const hasDocument = typeof document !== 'undefined';
925
+ const isBrowser = (qTest ? !isServerPlatform() : !isServer) && hasDocument;
921
926
  // Browser-specific setup
922
927
  const doc = isBrowser ? document : undefined;
928
+ const config = {
929
+ $maxIdlePreloads$: 25,
930
+ };
923
931
  // Determine which rel attribute to use based on browser support
924
932
  const rel = isBrowser && doc.createElement('link').relList?.supports?.('modulepreload')
925
933
  ? 'modulePreload'
@@ -935,7 +943,6 @@ const BundleImportState_Alias = 3;
935
943
  const BundleImportState_Loaded = 4;
936
944
 
937
945
  let base;
938
- import.meta.env.TEST ? !isServerPlatform() : !isServer;
939
946
  const makeBundle = (name, deps) => {
940
947
  return {
941
948
  $name$: name,
@@ -965,7 +972,6 @@ const nextAdjustmentMacroTask = createMacroTask(processPendingAdjustments);
965
972
  let isTriggerScheduled = false;
966
973
  let isAdjustmentScheduled = false;
967
974
  let isProcessingAdjustments = false;
968
- const shouldYieldInBrowser = import.meta.env.TEST ? !isServerPlatform() : isBrowser$1;
969
975
  const adjustmentStack = [];
970
976
  /**
971
977
  * This is called when a bundle is queued, or finished loading.
@@ -987,10 +993,8 @@ function trigger() {
987
993
  const bundle = queue[0];
988
994
  const inverseProbability = bundle.$inverseProbability$;
989
995
  const probability = 1 - inverseProbability;
990
- const allowedPreloads = // While the graph is not available, we limit to 5 preloads
991
- 5;
992
- // When we're 99% sure, everything needs to be queued
993
- if (probability >= 0.99 || preloadCount < allowedPreloads) {
996
+ // We want to preload all the transitive static (1) and dynamic (0.99) dependencies, throttled by the user defined maxIdlePreloads.
997
+ if (probability >= 0.99 || preloadCount < config.$maxIdlePreloads$) {
994
998
  queue.shift();
995
999
  preloadOne(bundle);
996
1000
  if (performance.now() >= deadline) {
@@ -1007,13 +1011,12 @@ function trigger() {
1007
1011
  nextTriggerMacroTask();
1008
1012
  }
1009
1013
  }
1010
- const enqueueAdjustment = (bundle, inverseProbability, context, seen) => {
1014
+ const enqueueAdjustment = (bundle, inverseProbability, seen) => {
1011
1015
  // Keep existing work on the stack hot and append new roots behind it.
1012
1016
  adjustmentStack.unshift({
1013
1017
  $bundle$: bundle,
1014
1018
  $inverseProbability$: inverseProbability,
1015
1019
  $seen$: seen,
1016
- $context$: context,
1017
1020
  });
1018
1021
  };
1019
1022
  const processAdjustmentFrame = () => {
@@ -1033,9 +1036,8 @@ const processAdjustmentFrame = () => {
1033
1036
  }
1034
1037
  const probability = 1 - bundle.$inverseProbability$;
1035
1038
  let newInverseProbability;
1036
- if (probability === 1 || (probability >= 0.99 && frame.$context$.$depsCount$ < 100)) {
1037
- frame.$context$.$depsCount$++;
1038
- // we're loaded at max probability, so elevate dynamic imports to 99% sure
1039
+ if (probability === 1 || probability >= 0.99) {
1040
+ // bundle is requested at max probability, so elevate all its transitive static and dynamic deps to 99% sure
1039
1041
  newInverseProbability = Math.min(0.01, 1 - dep.$importProbability$);
1040
1042
  }
1041
1043
  else {
@@ -1051,7 +1053,6 @@ const processAdjustmentFrame = () => {
1051
1053
  $bundle$: depBundle,
1052
1054
  $inverseProbability$: newInverseProbability,
1053
1055
  $seen$: frame.$seen$,
1054
- $context$: frame.$context$,
1055
1056
  });
1056
1057
  return true;
1057
1058
  }
@@ -1083,12 +1084,12 @@ function processPendingAdjustments() {
1083
1084
  }
1084
1085
  isAdjustmentScheduled = false;
1085
1086
  isProcessingAdjustments = true;
1086
- const deadline = shouldYieldInBrowser ? performance.now() + yieldInterval : 0;
1087
+ const deadline = isBrowser ? performance.now() + yieldInterval : 0;
1087
1088
  let processed = false;
1088
1089
  while (adjustmentStack.length) {
1089
1090
  processed = true;
1090
1091
  const checkDeadline = processAdjustmentFrame();
1091
- if (shouldYieldInBrowser && checkDeadline && performance.now() >= deadline) {
1092
+ if (isBrowser && checkDeadline && performance.now() >= deadline) {
1092
1093
  if (!isAdjustmentScheduled) {
1093
1094
  isAdjustmentScheduled = true;
1094
1095
  nextAdjustmentMacroTask();
@@ -1097,7 +1098,7 @@ function processPendingAdjustments() {
1097
1098
  }
1098
1099
  }
1099
1100
  isProcessingAdjustments = false;
1100
- if (processed && shouldYieldInBrowser) {
1101
+ if (processed && isBrowser) {
1101
1102
  nextTriggerMacroTask();
1102
1103
  }
1103
1104
  }
@@ -1128,64 +1129,35 @@ const preloadOne = (bundle) => {
1128
1129
  };
1129
1130
  doc.head.appendChild(link);
1130
1131
  };
1131
- /**
1132
- * Adjust the probability of a bundle based on the probability of its dependent bundles, and queue
1133
- * it if it's likely enough to be preloaded.
1134
- *
1135
- * Note that if the probability is 100%, we treat the dynamic imports as 99% sure, and both will be
1136
- * preloaded without limit.
1137
- *
1138
- * We also limit "organic" probability to 98% so they don't get unlimited preloads.
1139
- */
1140
- const adjustProbabilities = (bundle, newInverseProbability, seen) => {
1141
- enqueueAdjustment(bundle, newInverseProbability, { $depsCount$: 0 }, seen);
1142
- if (shouldYieldInBrowser) {
1143
- nextAdjustmentMacroTask();
1144
- }
1145
- else {
1146
- processPendingAdjustments();
1147
- }
1148
- };
1149
- const handleBundle = (name, inverseProbability, context) => {
1132
+ const handleBundle = (name, inverseProbability) => {
1150
1133
  const bundle = getBundle(name);
1151
- if (bundle && bundle.$inverseProbability$ > inverseProbability) {
1152
- if (context) {
1153
- enqueueAdjustment(bundle, inverseProbability, context);
1154
- }
1155
- else {
1156
- adjustProbabilities(bundle, inverseProbability);
1157
- }
1134
+ if (bundle) {
1135
+ enqueueAdjustment(bundle, inverseProbability);
1158
1136
  }
1159
1137
  };
1160
- const preload = (name, probability) => {
1161
- if (!name?.length) {
1138
+ const preload = (item, probability) => {
1139
+ if (!item?.length) {
1162
1140
  return;
1163
1141
  }
1164
- let inverseProbability = 1 - probability ;
1165
- const context = { $depsCount$: 0 };
1166
- if (Array.isArray(name)) {
1142
+ const inverseProbability = 1 - probability ;
1143
+ if (Array.isArray(item)) {
1167
1144
  // We must process in reverse order to ensure first bundles are handled first
1168
- for (let i = name.length - 1; i >= 0; i--) {
1169
- const item = name[i];
1170
- if (typeof item === 'number') {
1171
- inverseProbability = 1 - item / 10;
1172
- }
1173
- else {
1174
- handleBundle(item, inverseProbability, context);
1175
- }
1145
+ for (let i = item.length - 1; i >= 0; i--) {
1146
+ const bundle = item[i];
1147
+ handleBundle(bundle, inverseProbability);
1176
1148
  }
1177
1149
  }
1178
1150
  else {
1179
- handleBundle(name, inverseProbability, context);
1151
+ handleBundle(item, inverseProbability);
1180
1152
  }
1181
- if (shouldYieldInBrowser) {
1153
+ if (isBrowser) {
1182
1154
  nextAdjustmentMacroTask();
1183
1155
  }
1184
1156
  else {
1185
1157
  processPendingAdjustments();
1186
1158
  }
1187
1159
  };
1188
- if (import.meta.env.TEST ? !isServerPlatform() : isBrowser$1) {
1160
+ if (isBrowser) {
1189
1161
  // Get early hints from qwikloader
1190
1162
  document.addEventListener('qsymbol', (ev) => {
1191
1163
  const { symbol, href } = ev.detail;
@@ -1219,7 +1191,7 @@ const COMMA = ',';
1219
1191
  *
1220
1192
  * @public
1221
1193
  */
1222
- const version = "2.0.0-beta.32-dev+0e29f8a";
1194
+ const version = "2.0.0-beta.35-dev+4603135";
1223
1195
 
1224
1196
  const isNode = (value) => {
1225
1197
  return value && typeof value.nodeType === 'number';
@@ -1675,6 +1647,17 @@ function mergeCursors(container, newCursorData, oldCursor) {
1675
1647
  newCursorData.journal = oldJournal;
1676
1648
  }
1677
1649
  }
1650
+ // merge cursor boundaries
1651
+ const oldBoundaries = oldCursorData.boundaries;
1652
+ if (__EXPERIMENTAL__.suspense && oldBoundaries && oldBoundaries.length > 0) {
1653
+ const newBoundaries = (newCursorData.boundaries ||= []);
1654
+ for (let i = 0; i < oldBoundaries.length; i++) {
1655
+ const boundary = oldBoundaries[i];
1656
+ if (!newBoundaries.includes(boundary)) {
1657
+ newBoundaries.push(boundary);
1658
+ }
1659
+ }
1660
+ }
1678
1661
  }
1679
1662
  /**
1680
1663
  * Gets the cursor data from a vNode.
@@ -1905,7 +1888,7 @@ class SignalImpl {
1905
1888
  // to unsubscribe from. So we need to store the reference from the effect back
1906
1889
  // to this signal.
1907
1890
  ensureContainsBackRef(effectSubscriber, this);
1908
- (import.meta.env.TEST ? !isDomContainer(this.$container$) : isServer) &&
1891
+ (qTest ? !isDomContainer(this.$container$) : isServer) &&
1909
1892
  addQrlToSerializationCtx(effectSubscriber, this.$container$);
1910
1893
  }
1911
1894
  return val;
@@ -2131,8 +2114,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2131
2114
  get untrackedValue() {
2132
2115
  this.$computeIfNeeded$();
2133
2116
  if (this.$current$?.$promise$) {
2134
- if (this.$untrackedValue$ === NEEDS_COMPUTATION ||
2135
- (import.meta.env.TEST ? isServerPlatform() : isServer)) {
2117
+ if (this.$untrackedValue$ === NEEDS_COMPUTATION || (qTest ? isServerPlatform() : isServer)) {
2136
2118
  throw this.$current$?.$promise$;
2137
2119
  }
2138
2120
  return this.$untrackedValue$;
@@ -2142,7 +2124,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2142
2124
  }
2143
2125
  // For clientOnly signals without initial value during SSR, throw if trying to read value
2144
2126
  // During SSR, clientOnly signals are skipped, so there's no computed value available
2145
- if ((import.meta.env.TEST ? isServerPlatform() : isServer) &&
2127
+ if ((qTest ? isServerPlatform() : isServer) &&
2146
2128
  this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */ &&
2147
2129
  this.$untrackedValue$ === NEEDS_COMPUTATION) {
2148
2130
  throw qError(35 /* QError.asyncClientOnlyValueDuringSSR */);
@@ -2216,7 +2198,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2216
2198
  // reading `.loading` means someone is interested in the result, so we should trigger the computation. The alternative is eager computation or imperative calls to invalidate; this seems nicer.
2217
2199
  this.$computeIfNeeded$();
2218
2200
  // During SSR there's no such thing as loading state, we must render complete results
2219
- if ((import.meta.env.TEST ? isServerPlatform() : isServer) && this.$current$?.$promise$) {
2201
+ if ((qTest ? isServerPlatform() : isServer) && this.$current$?.$promise$) {
2220
2202
  throw this.$current$?.$promise$;
2221
2203
  }
2222
2204
  return this.$untrackedLoading$;
@@ -2317,7 +2299,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2317
2299
  if (!(this.$flags$ & 32 /* AsyncSignalFlags.EAGER_CLEANUP */) || this.$hasSubscribers$()) {
2318
2300
  return;
2319
2301
  }
2320
- if (!(import.meta.env.TEST ? !isServerPlatform() : isBrowser$1)) {
2302
+ if (!(qTest ? !isServerPlatform() : isBrowser$1)) {
2321
2303
  return;
2322
2304
  }
2323
2305
  setTimeout(() => {
@@ -2340,8 +2322,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2340
2322
  return;
2341
2323
  }
2342
2324
  // Skip computation on SSR for clientOnly signals
2343
- if ((import.meta.env.TEST ? isServerPlatform() : isServer) &&
2344
- this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
2325
+ if ((qTest ? isServerPlatform() : isServer) && this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
2345
2326
  // We must pretend to load, and register as a listener for the captures
2346
2327
  this.$untrackedLoading$ = true;
2347
2328
  this.$container$?.serializationCtx.$eagerResume$.add(this);
@@ -2373,9 +2354,11 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2373
2354
  }
2374
2355
  async $runComputation$(running) {
2375
2356
  const isCurrent = () => running === this.$current$;
2376
- this.untrackedLoading = true;
2377
2357
  let fn = this.$computeQrl$.resolved;
2378
2358
  if (!fn) {
2359
+ // QRL resolution is async — we have to publish loading=true before awaiting so
2360
+ // subscribers know the value isn't ready yet.
2361
+ this.untrackedLoading = true;
2379
2362
  fn = await this.$computeQrl$.resolve();
2380
2363
  if (running.$abortController$?.signal.aborted) {
2381
2364
  running.$promise$ = null;
@@ -2390,9 +2373,20 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2390
2373
  running.$abortController$?.abort(error);
2391
2374
  }, this.$timeoutMs$);
2392
2375
  }
2393
- // Try to stay sync if possible
2376
+ // Try to stay sync if possible. Only publish loading=true to subscribers when
2377
+ // the compute is actually asynchronous — a synchronous resolve (e.g. pre-loaded
2378
+ // values injected via _injectAsyncSignalValue) should never transition through a
2379
+ // visible loading state, which on SSR would fire the loading-effect subscribers
2380
+ // (tasks) while the value is still "loading" from their perspective.
2394
2381
  const valuePromise = retryOnPromise(fn.bind(null, running));
2395
- const value = isPromise(valuePromise) ? await valuePromise : valuePromise;
2382
+ let value;
2383
+ if (isPromise(valuePromise)) {
2384
+ this.untrackedLoading = true;
2385
+ value = await valuePromise;
2386
+ }
2387
+ else {
2388
+ value = valuePromise;
2389
+ }
2396
2390
  running.$promise$ = null;
2397
2391
  if (running.$canWrite$) {
2398
2392
  const jobs = this.$jobs$;
@@ -2476,7 +2470,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2476
2470
  }
2477
2471
  }
2478
2472
  $scheduleNextPoll$() {
2479
- if ((import.meta.env.TEST ? isServerPlatform() : isServer) || !this.$expires$) {
2473
+ if ((qTest ? isServerPlatform() : isServer) || !this.$expires$) {
2480
2474
  return;
2481
2475
  }
2482
2476
  this.$clearNextPoll$();
@@ -3619,7 +3613,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
3619
3613
  const inlineComponent = componentQRL;
3620
3614
  componentFn = () => invokeApply(iCtx, inlineComponent, [props || EMPTY_OBJ]);
3621
3615
  }
3622
- const isSsr = import.meta.env.TEST ? isServerPlatform() : isServer;
3616
+ const isSsr = qTest ? isServerPlatform() : isServer;
3623
3617
  const executeComponentWithPromiseExceptionRetry = (retryCount = 0) => safeCall(() => {
3624
3618
  if (!isInlineComponent) {
3625
3619
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
@@ -4004,6 +3998,214 @@ const createSetTextOperation = (target, text) => new SetTextOperation(target, te
4004
3998
  const createInsertOrMoveOperation = (target, parent, beforeTarget) => new InsertOrMoveOperation(target, parent, beforeTarget);
4005
3999
  const createSetAttributeOperation = (target, attrName, attrValue, scopedStyleIdPrefix = null, isSvg = false) => new SetAttributeOperation(target, attrName, attrValue, scopedStyleIdPrefix, isSvg);
4006
4000
 
4001
+ /**
4002
+ * @internal
4003
+ * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
4004
+ */
4005
+ const useSequentialScope = () => {
4006
+ const iCtx = useInvokeContext();
4007
+ const hostElement = iCtx.$hostElement$;
4008
+ const host = hostElement;
4009
+ let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
4010
+ if (seq === null) {
4011
+ seq = [];
4012
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
4013
+ }
4014
+ let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
4015
+ if (seqIdx === null) {
4016
+ seqIdx = 0;
4017
+ }
4018
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
4019
+ while (seq.length <= seqIdx) {
4020
+ seq.push(undefined);
4021
+ }
4022
+ const set = (value) => {
4023
+ if (qDev) {
4024
+ verifySerializable(value);
4025
+ }
4026
+ return (seq[seqIdx] = value);
4027
+ };
4028
+ return {
4029
+ val: seq[seqIdx],
4030
+ set,
4031
+ i: seqIdx,
4032
+ iCtx,
4033
+ };
4034
+ };
4035
+
4036
+ const getSignal = (initialState) => {
4037
+ const value = isFunction(initialState) && !isQwikComponent(initialState)
4038
+ ? invoke(undefined, initialState)
4039
+ : initialState;
4040
+ return createSignal(value);
4041
+ };
4042
+ // <docs markdown="../readme.md#useSignal">
4043
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
4044
+ // (edit ../readme.md#useSignal instead and run `pnpm docs.sync`)
4045
+ /**
4046
+ * Creates an object with a single reactive `.value` property, that Qwik can track across
4047
+ * serializations.
4048
+ *
4049
+ * Use it to create state for your application. The object has a getter and setter to track reads
4050
+ * and writes of the `.value` property. When the value changes, any functions that read from it will
4051
+ * re-run.
4052
+ *
4053
+ * Prefer `useSignal` over `useStore` when possible, as it is more efficient.
4054
+ *
4055
+ * ### Example
4056
+ *
4057
+ * ```tsx
4058
+ * const Signals = component$(() => {
4059
+ * const counter = useSignal(1);
4060
+ * const text = useSignal('changeme');
4061
+ * const toggle = useSignal(false);
4062
+ *
4063
+ * // useSignal() can also accept a function to calculate the initial value
4064
+ * const state = useSignal(() => {
4065
+ * return expensiveInitialValue();
4066
+ * });
4067
+ *
4068
+ * return (
4069
+ * <div>
4070
+ * <button onClick$={() => counter.value++}>Counter: {counter.value}</button>
4071
+ * {
4072
+ * // pass signal values as the value, the optimizer will make it pass the signal
4073
+ * }
4074
+ * <Child state={state.value} />
4075
+ * {
4076
+ * // signals can be bound to inputs. A property named `bind:x` implies that the property
4077
+ * is a signal
4078
+ * }
4079
+ * <input type="text" bind:value={text} />
4080
+ * <input type="checkbox" bind:checked={toggle} />
4081
+ * </div>
4082
+ * );
4083
+ * });
4084
+ * ```
4085
+ *
4086
+ * @public
4087
+ */
4088
+ // </docs>
4089
+ const useSignal = (initialState) => {
4090
+ return useConstant((getSignal), initialState);
4091
+ };
4092
+ /**
4093
+ * Stores a value which is retained for the lifetime of the component. Subsequent calls to
4094
+ * `useConstant` will always return the first value given.
4095
+ *
4096
+ * If the value is a function, the function is invoked once to calculate the actual value. You can
4097
+ * then also pass arguments to call the function with, so that you don't need to create a new
4098
+ * function on every render.
4099
+ *
4100
+ * @example
4101
+ *
4102
+ * ```tsx
4103
+ * const fixedRandomValue = useConstant(() => Math.random);
4104
+ * const otherFixedRandomValue = useConstant(Math.random);
4105
+ *
4106
+ * const getConfig = (env: string) => { ... }
4107
+ * const config = useConstant(getConfig, environment);
4108
+ * ```
4109
+ *
4110
+ * @public
4111
+ */
4112
+ const useConstant = (value, ...args) => {
4113
+ const { val, set } = useSequentialScope();
4114
+ if (val != null) {
4115
+ return val;
4116
+ }
4117
+ // We don't want to create a subscription since we only run this once
4118
+ // Note: We are not using `invoke` here because we don't want to clear the context
4119
+ value = isFunction(value) && !isQwikComponent(value) ? untrack(value, ...args) : value;
4120
+ return set(value);
4121
+ };
4122
+
4123
+ const createCursorBoundary = () => {
4124
+ return {
4125
+ pending: createSignal(0),
4126
+ version: createSignal(0),
4127
+ };
4128
+ };
4129
+ /** @internal */
4130
+ const useCursorBoundary = () => {
4131
+ if (!__EXPERIMENTAL__.suspense) {
4132
+ throw new Error('useCursorBoundary is experimental and must be enabled with `experimental: ["suspense"]` in the `qwikVite` plugin.');
4133
+ }
4134
+ return useConstant(createCursorBoundary);
4135
+ };
4136
+ function addCursorBoundary(cursorData, vNode) {
4137
+ if (!__EXPERIMENTAL__.suspense) {
4138
+ return;
4139
+ }
4140
+ const boundary = getNearestCursorBoundary(cursorData.container, vNode);
4141
+ if (!boundary) {
4142
+ return;
4143
+ }
4144
+ const boundaries = (cursorData.boundaries ||= []);
4145
+ if (!boundaries.includes(boundary)) {
4146
+ boundaries.push(boundary);
4147
+ boundary.pending.value++;
4148
+ }
4149
+ }
4150
+ function resolveCursorBoundaries(cursorData) {
4151
+ if (!__EXPERIMENTAL__.suspense) {
4152
+ return;
4153
+ }
4154
+ const boundaries = cursorData.boundaries;
4155
+ if (!boundaries) {
4156
+ return;
4157
+ }
4158
+ cursorData.boundaries = null;
4159
+ for (let i = 0; i < boundaries.length; i++) {
4160
+ const boundary = boundaries[i];
4161
+ boundary.pending.value = Math.max(0, boundary.pending.value - 1);
4162
+ boundary.version.value++;
4163
+ }
4164
+ }
4165
+ function getOwnCursorBoundary(container, vNode) {
4166
+ if (!__EXPERIMENTAL__.suspense) {
4167
+ return null;
4168
+ }
4169
+ return container.getHostProp(vNode, QCursorBoundary);
4170
+ }
4171
+ function getNearestCursorBoundaryProp(vNode) {
4172
+ if (!__EXPERIMENTAL__.suspense) {
4173
+ return null;
4174
+ }
4175
+ return (vnode_getProp(vNode, QNearestCursorBoundary, null) ||
4176
+ null);
4177
+ }
4178
+ function clearNearestCursorBoundary(vNode) {
4179
+ if (!__EXPERIMENTAL__.suspense) {
4180
+ return;
4181
+ }
4182
+ vnode_setProp(vNode, QNearestCursorBoundary, null);
4183
+ }
4184
+ function getNearestCursorBoundary(container, vNode) {
4185
+ if (!__EXPERIMENTAL__.suspense) {
4186
+ return null;
4187
+ }
4188
+ return getNearestCursorBoundaryProp(vNode) || getOwnCursorBoundary(container, vNode);
4189
+ }
4190
+ function setNearestCursorBoundary(vNode, boundary) {
4191
+ __EXPERIMENTAL__.suspense && vnode_setProp(vNode, QNearestCursorBoundary, boundary);
4192
+ }
4193
+ /** Updates the nearest cursor boundary cache on a vnode and any already-dirty descendants. */
4194
+ function updateDirtySubtreeCursorBoundary(container, vNode, boundary) {
4195
+ if (!__EXPERIMENTAL__.suspense) {
4196
+ return;
4197
+ }
4198
+ setNearestCursorBoundary(vNode, boundary);
4199
+ const dirtyChildren = vNode.dirtyChildren;
4200
+ if (!dirtyChildren || dirtyChildren.length === 0) {
4201
+ return;
4202
+ }
4203
+ for (let i = 0; i < dirtyChildren.length; i++) {
4204
+ const child = dirtyChildren[i];
4205
+ updateDirtySubtreeCursorBoundary(container, child, getOwnCursorBoundary(container, child) || boundary);
4206
+ }
4207
+ }
4208
+
4007
4209
  const cleanupDestroyable = (destroyable) => {
4008
4210
  if (destroyable.$destroy$) {
4009
4211
  try {
@@ -4124,7 +4326,7 @@ function peekNextSiblingWithinBoundary(diffContext, vCurrent) {
4124
4326
  const _hasOwnProperty = Object.prototype.hasOwnProperty;
4125
4327
  /** Helper to set an attribute on a vnode. Extracted to module scope to avoid closure allocation. */
4126
4328
  function setAttribute(journal, vnode, key, value, scopedStyleIdPrefix, originalValue) {
4127
- import.meta.env.TEST &&
4329
+ qTest &&
4128
4330
  scopedStyleIdPrefix &&
4129
4331
  vnode_setProp(vnode, debugStyleScopeIdPrefixAttr, scopedStyleIdPrefix);
4130
4332
  vnode_setProp(vnode, key, originalValue);
@@ -4528,8 +4730,10 @@ function expectProjection(diffContext) {
4528
4730
  }
4529
4731
  }
4530
4732
  function expectSlot(diffContext) {
4733
+ const jsxNode = diffContext.$jsxValue$;
4531
4734
  const vHost = vnode_getProjectionParentComponent(diffContext.$vParent$);
4532
4735
  const slotNameKey = getSlotNameKey(diffContext, vHost);
4736
+ const cursorBoundary = directGetPropsProxyProp(jsxNode, QCursorBoundary) || null;
4533
4737
  const vProjectedNode = vHost
4534
4738
  ? vnode_getProp(vHost, slotNameKey,
4535
4739
  // for slots this id is vnode ref id
@@ -4540,6 +4744,8 @@ function expectSlot(diffContext) {
4540
4744
  if (vProjectedNode == null) {
4541
4745
  diffContext.$vNewNode$ = vnode_newVirtual();
4542
4746
  vnode_setProp(diffContext.$vNewNode$, QSlot, slotNameKey);
4747
+ vnode_setProp(diffContext.$vNewNode$, QCursorBoundary, cursorBoundary);
4748
+ updateDirtySubtreeCursorBoundary(diffContext.$container$, diffContext.$vNewNode$, cursorBoundary);
4543
4749
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.$vNewNode$);
4544
4750
  isDev &&
4545
4751
  vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "P" /* VirtualType.Projection */); // Nothing to project, so render content of the slot.
@@ -4552,6 +4758,8 @@ function expectSlot(diffContext) {
4552
4758
  const oldParent = vProjectedNode.parent;
4553
4759
  diffContext.$vNewNode$ = vProjectedNode;
4554
4760
  vnode_setProp(diffContext.$vNewNode$, QSlot, slotNameKey);
4761
+ vnode_setProp(diffContext.$vNewNode$, QCursorBoundary, cursorBoundary);
4762
+ updateDirtySubtreeCursorBoundary(diffContext.$container$, diffContext.$vNewNode$, cursorBoundary);
4555
4763
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.$vNewNode$);
4556
4764
  isDev &&
4557
4765
  vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "P" /* VirtualType.Projection */);
@@ -4787,7 +4995,7 @@ function registerEventHandlers(key, value, element, vnode, diffContext) {
4787
4995
  function createElementWithNamespace(diffContext, elementName) {
4788
4996
  const domParentVNode = vnode_getDomParentVNode(diffContext.$vParent$, true);
4789
4997
  const namespaceData = getNewElementNamespaceData(domParentVNode, elementName);
4790
- const currentDocument = import.meta.env.TEST ? diffContext.$container$.document : document;
4998
+ const currentDocument = qTest ? diffContext.$container$.document : document;
4791
4999
  const element = namespaceData.elementNamespaceFlag === 0 /* VNodeFlags.NS_html */
4792
5000
  ? currentDocument.createElement(elementName)
4793
5001
  : currentDocument.createElementNS(namespaceData.elementNamespace, elementName);
@@ -4912,7 +5120,7 @@ const patchProperty = (diffContext, vnode, key, value, currentFile) => {
4912
5120
  setAttribute(diffContext.$journal$, vnode, key, value, diffContext.$scopedStyleIdPrefix$, originalValue);
4913
5121
  };
4914
5122
  function registerQwikLoaderEvent(diffContext, eventName) {
4915
- const qWindow = import.meta.env.TEST
5123
+ const qWindow = qTest
4916
5124
  ? diffContext.$container$.document.defaultView
4917
5125
  : window;
4918
5126
  if (qWindow) {
@@ -5226,7 +5434,7 @@ function expectText(diffContext, text) {
5226
5434
  return;
5227
5435
  }
5228
5436
  }
5229
- vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newText((import.meta.env.TEST ? diffContext.$container$.document : document).createTextNode(text), text)), getCurrentInsertBefore(diffContext));
5437
+ vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newText((qTest ? diffContext.$container$.document : document).createTextNode(text), text)), getCurrentInsertBefore(diffContext));
5230
5438
  }
5231
5439
  /**
5232
5440
  * Retrieve the key from the VNode.
@@ -6294,13 +6502,18 @@ function scheduleYield() {
6294
6502
  *
6295
6503
  * @param options - Walk options (time budget, etc.)
6296
6504
  */
6297
- function processCursorQueue(options = {
6298
- timeBudget: 1000 / 60, // 60fps
6299
- }) {
6505
+ function processCursorQueue() {
6300
6506
  isNextTickScheduled = false;
6507
+ const startTime = performance.now();
6508
+ const yieldTime = startTime + 15; // 16 ms = 60 FPS, use 15 to yield slightly before next frame
6301
6509
  let cursor = null;
6302
6510
  while ((cursor = getHighestPriorityCursor())) {
6303
- walkCursor(cursor, options);
6511
+ if (walkCursor(cursor, yieldTime)) {
6512
+ // Cursor overran time budget, yield to browser
6513
+ // Note that each tick we process at least one thing
6514
+ scheduleYield();
6515
+ return;
6516
+ }
6304
6517
  }
6305
6518
  }
6306
6519
  /**
@@ -6319,13 +6532,12 @@ function processCursorQueue(options = {
6319
6532
  * Note that there is only one walker for all containers in the app with the same Qwik version.
6320
6533
  *
6321
6534
  * @param cursor - The cursor to walk
6322
- * @param options - Walk options (time budget, etc.)
6323
- * @returns Walk result indicating completion status
6535
+ * @param until - Time budget (timestamp to yield by)
6536
+ * @returns `true` if the walk was paused due to time budget (do not process more cursors in this
6537
+ * tick)
6324
6538
  */
6325
- function walkCursor(cursor, options) {
6326
- const { timeBudget } = options;
6327
- const isRunningOnServer = import.meta.env.TEST ? isServerPlatform() : isServer;
6328
- const startTime = performance.now();
6539
+ function walkCursor(cursor, until) {
6540
+ const isRunningOnServer = qTest ? isServerPlatform() : isServer;
6329
6541
  const cursorData = getCursorData(cursor);
6330
6542
  // Check if cursor is blocked by a promise
6331
6543
  const blockingPromise = cursorData.promise;
@@ -6349,7 +6561,8 @@ function walkCursor(cursor, options) {
6349
6561
  // Skip if the vNode is not dirty
6350
6562
  if (!(currentVNode.dirty & 511 /* ChoreBits.DIRTY_MASK */)) {
6351
6563
  // Move to next node
6352
- setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor));
6564
+ __EXPERIMENTAL__.suspense && clearNearestCursorBoundary(currentVNode);
6565
+ setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor, container));
6353
6566
  continue;
6354
6567
  }
6355
6568
  // Skip if the vNode is deleted
@@ -6367,7 +6580,7 @@ function walkCursor(cursor, options) {
6367
6580
  }
6368
6581
  // Clear dirty bits and move to next node
6369
6582
  currentVNode.dirty &= -512 /* ChoreBits.DIRTY_MASK */;
6370
- setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor));
6583
+ setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor, container));
6371
6584
  continue;
6372
6585
  }
6373
6586
  let result;
@@ -6410,6 +6623,7 @@ function walkCursor(cursor, options) {
6410
6623
  }
6411
6624
  // Handle blocking promise
6412
6625
  if (result && isPromise(result)) {
6626
+ addCursorBoundary(cursorData, currentVNode);
6413
6627
  // Store promise on cursor and pause
6414
6628
  cursorData.promise = result;
6415
6629
  pauseCursor(cursor, container);
@@ -6426,13 +6640,8 @@ function walkCursor(cursor, options) {
6426
6640
  return;
6427
6641
  }
6428
6642
  // Check time budget (only for DOM, not SSR)
6429
- if (isBrowser$1) {
6430
- const elapsed = performance.now() - startTime;
6431
- if (elapsed >= timeBudget) {
6432
- // Schedule continuation as macrotask to actually yield to browser
6433
- scheduleYield();
6434
- return;
6435
- }
6643
+ if (performance.now() >= until) {
6644
+ return true;
6436
6645
  }
6437
6646
  }
6438
6647
  isDev &&
@@ -6445,6 +6654,7 @@ function finishWalk(container, cursor, cursorData, isServer) {
6445
6654
  if (!isServer) {
6446
6655
  executeFlushPhase(cursor, container);
6447
6656
  }
6657
+ resolveCursorBoundaries(cursorData);
6448
6658
  if (cursorData.extraPromises) {
6449
6659
  Promise.all(cursorData.extraPromises).then(() => {
6450
6660
  resolveCursor(container);
@@ -6465,30 +6675,14 @@ function tryDescendDirtyChildren(container, cursorData, currentVNode, cursor) {
6465
6675
  const dirtyChildren = currentVNode.dirtyChildren;
6466
6676
  if (!dirtyChildren || dirtyChildren.length === 0) {
6467
6677
  currentVNode.dirty &= -33 /* ChoreBits.CHILDREN */;
6678
+ clearNearestCursorBoundary(currentVNode);
6468
6679
  return null;
6469
6680
  }
6470
6681
  partitionDirtyChildren(dirtyChildren, currentVNode);
6471
- // Scan dirtyChildren directly instead of going through getNextVNode.
6472
- // getNextVNode follows the child's parent/slotParent pointer, which for Projection nodes
6473
- // points to the DOM insertion location rather than currentVNode — that would scan the
6474
- // wrong dirtyChildren array and potentially cause infinite loops.
6475
- // const len = dirtyChildren.length;
6476
- // for (let i = 0; i < len; i++) {
6477
- // const child = dirtyChildren[i];
6478
- // if (child.dirty & ChoreBits.DIRTY_MASK) {
6479
- // currentVNode.nextDirtyChildIndex = (i + 1) % len;
6480
- // setCursorPosition(container, cursorData, child);
6481
- // return child;
6482
- // }
6483
- // }
6484
- // // No dirty child found — clean up
6485
- // currentVNode.dirty &= ~ChoreBits.CHILDREN;
6486
- // currentVNode.dirtyChildren = null;
6487
6682
  currentVNode.nextDirtyChildIndex = 0;
6488
- const next = getNextVNode(dirtyChildren[0], cursor);
6683
+ const next = getNextVNode(dirtyChildren[0], cursor, container);
6489
6684
  setCursorPosition(container, cursorData, next);
6490
6685
  return next;
6491
- // return null;
6492
6686
  }
6493
6687
  /**
6494
6688
  * Partitions dirtyChildren array so non-projections come first, projections last. Uses in-place
@@ -6510,7 +6704,7 @@ function partitionDirtyChildren(dirtyChildren, parent) {
6510
6704
  }
6511
6705
  }
6512
6706
  /** @returns Next vNode to process, or null if traversal is complete */
6513
- function getNextVNode(vNode, cursor) {
6707
+ function getNextVNode(vNode, cursor, container) {
6514
6708
  if (vNode === cursor) {
6515
6709
  if (cursor.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6516
6710
  return cursor;
@@ -6538,6 +6732,13 @@ function getNextVNode(vNode, cursor) {
6538
6732
  while (count-- > 0) {
6539
6733
  const nextVNode = dirtyChildren[index];
6540
6734
  if (nextVNode.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6735
+ if (container && splitCursorBoundary(container, nextVNode)) {
6736
+ index++;
6737
+ if (index === len) {
6738
+ index = 0;
6739
+ }
6740
+ continue;
6741
+ }
6541
6742
  parent.nextDirtyChildIndex = (index + 1) % len;
6542
6743
  return nextVNode;
6543
6744
  }
@@ -6550,7 +6751,22 @@ function getNextVNode(vNode, cursor) {
6550
6751
  parent.dirty &= -33 /* ChoreBits.CHILDREN */;
6551
6752
  parent.dirtyChildren = null;
6552
6753
  parent.nextDirtyChildIndex = 0;
6553
- return getNextVNode(parent, cursor);
6754
+ __EXPERIMENTAL__.suspense && clearNearestCursorBoundary(parent);
6755
+ return getNextVNode(parent, cursor, container);
6756
+ }
6757
+ function splitCursorBoundary(container, vNode) {
6758
+ if (!__EXPERIMENTAL__.suspense) {
6759
+ return false;
6760
+ }
6761
+ if (!vNode.props ||
6762
+ !(QCursorBoundary in vNode.props) ||
6763
+ !container.getHostProp(vNode, QCursorBoundary)) {
6764
+ return false;
6765
+ }
6766
+ if (!isCursor(vNode)) {
6767
+ addCursor(container, vNode, 0);
6768
+ }
6769
+ return true;
6554
6770
  }
6555
6771
 
6556
6772
  /**
@@ -6570,6 +6786,7 @@ function addCursor(container, root, priority) {
6570
6786
  position: root,
6571
6787
  priority: priority,
6572
6788
  promise: null,
6789
+ boundaries: null,
6573
6790
  };
6574
6791
  setCursorData(root, cursorData);
6575
6792
  const cursor = root;
@@ -6613,7 +6830,7 @@ function _executeSsrChores(container, ssrNode) {
6613
6830
  if (ssrNode.dirty & 16 /* ChoreBits.COMPUTE */) {
6614
6831
  executeCompute(ssrNode, container);
6615
6832
  }
6616
- if (ssrNode.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6833
+ if (isDev && ssrNode.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6617
6834
  // We are running on the server.
6618
6835
  // On server we can't schedule task for a different host!
6619
6836
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -6736,14 +6953,23 @@ function propagatePath(target) {
6736
6953
  * Propagates dirty bits from vNode up to the specified cursorRoot. Used during diff when we know
6737
6954
  * the cursor root to merge with. Also updates cursor position if we pass through any cursors.
6738
6955
  */
6739
- function propagateToCursorRoot(vNode, cursorRoot) {
6956
+ function propagateToCursorRoot(container, vNode, cursorRoot) {
6740
6957
  reusablePath.push(vNode);
6958
+ let cursorBoundary = getOwnCursorBoundary(container, vNode);
6741
6959
  let current = vNode.slotParent || vNode.parent;
6742
6960
  while (current) {
6743
6961
  const isDirty = current.dirty & 511 /* ChoreBits.DIRTY_MASK */;
6744
6962
  const currentIsCursor = isCursor(current);
6963
+ if (__EXPERIMENTAL__.suspense) {
6964
+ cursorBoundary ||=
6965
+ getOwnCursorBoundary(container, current) ||
6966
+ (isDirty ? getNearestCursorBoundary(container, current) : null);
6967
+ }
6745
6968
  // Stop when we reach the cursor root or a dirty ancestor
6746
6969
  if (current === cursorRoot || isDirty) {
6970
+ // Known cursor root / dirty ancestor case: cache the boundary discovered while walking
6971
+ // before attaching this dirty vnode to the existing scheduled subtree.
6972
+ setNearestCursorBoundary(vNode, cursorBoundary);
6747
6973
  propagatePath(current);
6748
6974
  // Update cursor position if current is a cursor
6749
6975
  if (currentIsCursor) {
@@ -6772,12 +6998,23 @@ function propagateToCursorRoot(vNode, cursorRoot) {
6772
6998
  * Finds a blocking cursor or dirty ancestor and propagates dirty bits to it. Returns true if found
6773
6999
  * and attached, false if a new cursor should be created.
6774
7000
  */
6775
- function findAndPropagateToBlockingCursor(vNode) {
7001
+ function findAndPropagateToBlockingCursor(container, vNode) {
6776
7002
  reusablePath.push(vNode);
7003
+ let cursorBoundary = __EXPERIMENTAL__.suspense
7004
+ ? getOwnCursorBoundary(container, vNode)
7005
+ : null;
6777
7006
  let current = vNode.slotParent || vNode.parent;
6778
7007
  while (current) {
6779
7008
  const currentIsCursor = isCursor(current);
7009
+ if (__EXPERIMENTAL__.suspense) {
7010
+ cursorBoundary ||=
7011
+ getOwnCursorBoundary(container, current) ||
7012
+ (currentIsCursor ? getNearestCursorBoundary(container, current) : null);
7013
+ }
6780
7014
  if (currentIsCursor) {
7015
+ // Existing cursor case: attach this dirty vnode to the blocking cursor found above it and
7016
+ // remember that cursor's nearest boundary for async/suspense bookkeeping.
7017
+ setNearestCursorBoundary(vNode, cursorBoundary);
6781
7018
  propagatePath(current);
6782
7019
  reusablePath.length = 0;
6783
7020
  return true;
@@ -6785,11 +7022,14 @@ function findAndPropagateToBlockingCursor(vNode) {
6785
7022
  reusablePath.push(current);
6786
7023
  current = current.slotParent || current.parent;
6787
7024
  }
7025
+ // New cursor case: no blocking cursor was found above this vnode, so cache the nearest boundary
7026
+ // before the caller creates a cursor rooted at this vnode.
7027
+ setNearestCursorBoundary(vNode, cursorBoundary);
6788
7028
  reusablePath.length = 0;
6789
7029
  return false;
6790
7030
  }
6791
7031
  function isSsrNodeGuard(_vNode) {
6792
- return import.meta.env.TEST ? isServerPlatform() : isServer;
7032
+ return qTest ? isServerPlatform() : isServer;
6793
7033
  }
6794
7034
  /**
6795
7035
  * Marks a vNode as dirty and propagates dirty bits up the tree.
@@ -6820,11 +7060,14 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
6820
7060
  const parent = vNode.slotParent || vNode.parent;
6821
7061
  // If cursorRoot is provided, propagate up to it
6822
7062
  if (cursorRoot && isRealDirty && parent && !parent.dirty) {
6823
- propagateToCursorRoot(vNode, cursorRoot);
7063
+ propagateToCursorRoot(container, vNode, cursorRoot);
6824
7064
  return;
6825
7065
  }
6826
7066
  // We must attach to a cursor subtree if it exists
6827
7067
  if (parent && parent.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
7068
+ // Dirty parent case: this vnode joins an already scheduled subtree, so inherit the parent's
7069
+ // nearest boundary unless this vnode owns a boundary itself.
7070
+ setNearestCursorBoundary(vNode, getOwnCursorBoundary(container, vNode) || getNearestCursorBoundary(container, parent));
6828
7071
  if (isRealDirty) {
6829
7072
  parent.dirty |= 32 /* ChoreBits.CHILDREN */;
6830
7073
  }
@@ -6856,11 +7099,16 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
6856
7099
  else if (!isCursor(vNode)) {
6857
7100
  // Check if there's an existing cursor that is blocking (executing a render-blocking task)
6858
7101
  // If so, merge with it instead of creating a new cursor (single-pass find + propagate)
6859
- if (!findAndPropagateToBlockingCursor(vNode)) {
7102
+ if (!findAndPropagateToBlockingCursor(container, vNode)) {
6860
7103
  // No blocking cursor found, create a new one
6861
7104
  addCursor(container, vNode, 0);
6862
7105
  }
6863
7106
  }
7107
+ else {
7108
+ // Existing cursor-root case: the vnode is already the scheduled cursor, so only its own
7109
+ // boundary can be authoritative here.
7110
+ setNearestCursorBoundary(vNode, getOwnCursorBoundary(container, vNode));
7111
+ }
6864
7112
  }
6865
7113
  function addVNodeOperation(journal, operation) {
6866
7114
  journal.push(operation);
@@ -7049,6 +7297,7 @@ const vnode_newText = (textNode, textContent) => {
7049
7297
  isDev && assertFalse(vnode_isVirtualVNode(vnode), 'Incorrect format of TextVNode.');
7050
7298
  return vnode;
7051
7299
  };
7300
+ /** @internal */
7052
7301
  const vnode_newVirtual = () => {
7053
7302
  const vnode = new VirtualVNode(null, 2 /* VNodeFlags.Virtual */ | (-1 << 12 /* VNodeFlagsIndex.shift */), // Flags
7054
7303
  null, null, null, null, null, null);
@@ -7061,6 +7310,7 @@ const vnode_newVirtual = () => {
7061
7310
  const vnode_isVNode = (vNode) => {
7062
7311
  return vNode instanceof VNode;
7063
7312
  };
7313
+ /** @internal */
7064
7314
  const vnode_isElementVNode = (vNode) => {
7065
7315
  return (vNode.flags & 1 /* VNodeFlags.Element */) === 1 /* VNodeFlags.Element */;
7066
7316
  };
@@ -7119,6 +7369,7 @@ const vnode_getNodeTypeName = (vNode) => {
7119
7369
  }
7120
7370
  return '<unknown>';
7121
7371
  };
7372
+ /** @internal */
7122
7373
  const vnode_getProp = (vNode, key, getObject) => {
7123
7374
  if (vnode_isElementVNode(vNode) || vnode_isVirtualVNode(vNode)) {
7124
7375
  const value = vNode.props?.[key] ?? null;
@@ -7131,6 +7382,7 @@ const vnode_getProp = (vNode, key, getObject) => {
7131
7382
  }
7132
7383
  return null;
7133
7384
  };
7385
+ /** @internal */
7134
7386
  const vnode_setProp = (vNode, key, value) => {
7135
7387
  if (value == null && vNode.props) {
7136
7388
  delete vNode.props[key];
@@ -7142,7 +7394,7 @@ const vnode_setProp = (vNode, key, value) => {
7142
7394
  };
7143
7395
  const vnode_setAttr = (journal, vNode, key, value, scopedStyleIdPrefix = null) => {
7144
7396
  if (vnode_isElementVNode(vNode)) {
7145
- import.meta.env.TEST &&
7397
+ qTest &&
7146
7398
  scopedStyleIdPrefix &&
7147
7399
  vnode_setProp(vNode, debugStyleScopeIdPrefixAttr, scopedStyleIdPrefix);
7148
7400
  vnode_setProp(vNode, key, value);
@@ -7522,6 +7774,7 @@ const vnode_getChildWithIdx = (vNode, childIdx) => {
7522
7774
  return child;
7523
7775
  };
7524
7776
  const vNodeStack = [];
7777
+ /** @internal */
7525
7778
  const vnode_getVNodeForChildNode = (vNode, childElement) => {
7526
7779
  ensureElementVNode(vNode);
7527
7780
  let child = vnode_getFirstChild(vNode);
@@ -7835,6 +8088,7 @@ const vnode_inflateProjectionTrailingText = (journal, projection) => {
7835
8088
  vnode_ensureTextInflated(journal, last);
7836
8089
  }
7837
8090
  };
8091
+ /** @internal */
7838
8092
  const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
7839
8093
  if (vnode_isElementOrTextVNode(newChild)) {
7840
8094
  vnode_insertElementBefore(journal, parent, newChild, insertBefore);
@@ -7858,6 +8112,7 @@ const vnode_getDomParentVNode = (vnode, includeProjection) => {
7858
8112
  }
7859
8113
  return vnode;
7860
8114
  };
8115
+ /** @internal */
7861
8116
  const vnode_remove = (journal, vParent, vToRemove, removeDOM) => {
7862
8117
  isDev && assertEqual(vParent, vToRemove.parent, 'Parent mismatch.');
7863
8118
  if (vnode_isTextVNode(vToRemove)) {
@@ -7926,6 +8181,7 @@ const vnode_truncate = (journal, vParent, vDelete, removeDOM = true) => {
7926
8181
  vParent.lastChild = vPrevious;
7927
8182
  };
7928
8183
  //////////////////////////////////////////////////////////////////////////////////////////////////////
8184
+ /** @internal */
7929
8185
  const vnode_getElementName = (vnode) => {
7930
8186
  const elementVNode = ensureElementVNode(vnode);
7931
8187
  let elementName = elementVNode.elementName;
@@ -8727,41 +8983,6 @@ function clearStoreOrProps(producer, effect) {
8727
8983
  }
8728
8984
  }
8729
8985
 
8730
- /**
8731
- * @internal
8732
- * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
8733
- */
8734
- const useSequentialScope = () => {
8735
- const iCtx = useInvokeContext();
8736
- const hostElement = iCtx.$hostElement$;
8737
- const host = hostElement;
8738
- let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
8739
- if (seq === null) {
8740
- seq = [];
8741
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
8742
- }
8743
- let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
8744
- if (seqIdx === null) {
8745
- seqIdx = 0;
8746
- }
8747
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
8748
- while (seq.length <= seqIdx) {
8749
- seq.push(undefined);
8750
- }
8751
- const set = (value) => {
8752
- if (qDev) {
8753
- verifySerializable(value);
8754
- }
8755
- return (seq[seqIdx] = value);
8756
- };
8757
- return {
8758
- val: seq[seqIdx],
8759
- set,
8760
- i: seqIdx,
8761
- iCtx,
8762
- };
8763
- };
8764
-
8765
8986
  /** @internal */
8766
8987
  const useTaskQrl = (qrl, opts) => {
8767
8988
  const { val, set, iCtx, i } = useSequentialScope();
@@ -8907,7 +9128,7 @@ const addQrlToSerializationCtx = (effectSubscriber, container) => {
8907
9128
  }
8908
9129
  };
8909
9130
  const scheduleEffects = (container, signal, effects) => {
8910
- const isBrowser = import.meta.env.TEST ? !isServerPlatform() : !isServer;
9131
+ const isRunningOnBrowser = qTest ? !isServerPlatform() : isBrowser$1;
8911
9132
  if (effects) {
8912
9133
  const scheduleEffect = (effectSubscription) => {
8913
9134
  const consumer = effectSubscription.consumer;
@@ -8924,7 +9145,7 @@ const scheduleEffects = (container, signal, effects) => {
8924
9145
  markVNodeDirty(container, consumer, 4 /* ChoreBits.COMPONENT */);
8925
9146
  }
8926
9147
  else if (property === "." /* EffectProperty.VNODE */) {
8927
- if (isBrowser) {
9148
+ if (isRunningOnBrowser) {
8928
9149
  setNodeDiffPayload(consumer, signal);
8929
9150
  markVNodeDirty(container, consumer, 2 /* ChoreBits.NODE_DIFF */);
8930
9151
  }
@@ -8938,7 +9159,7 @@ const scheduleEffects = (container, signal, effects) => {
8938
9159
  scopedStyleIdPrefix: data.$scopedStyleIdPrefix$,
8939
9160
  value: signal,
8940
9161
  };
8941
- if (isBrowser) {
9162
+ if (isRunningOnBrowser) {
8942
9163
  setNodePropData(consumer, property, payload);
8943
9164
  }
8944
9165
  else {
@@ -9294,7 +9515,7 @@ function addStoreEffect(target, prop, store, effectSubscription) {
9294
9515
  // to this signal.
9295
9516
  ensureContainsBackRef(effectSubscription, target);
9296
9517
  // TODO is this needed with the preloader?
9297
- (import.meta.env.TEST ? !isDomContainer(store.$container$) : isServer) &&
9518
+ (qTest ? !isDomContainer(store.$container$) : isServer) &&
9298
9519
  addQrlToSerializationCtx(effectSubscription, store.$container$);
9299
9520
  }
9300
9521
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
@@ -10398,7 +10619,9 @@ DomRefConstructor, symbolToChunkResolver, setProp, storeProxyMap, writer) => {
10398
10619
  if (!writer) {
10399
10620
  const buffer = [];
10400
10621
  writer = {
10401
- write: (text) => buffer.push(text),
10622
+ write: (text) => {
10623
+ buffer.push(text);
10624
+ },
10402
10625
  toString: () => buffer.join(''),
10403
10626
  };
10404
10627
  }
@@ -10933,19 +11156,29 @@ const _noopQrl = (symbolName, lexicalScopeCapture) => {
10933
11156
  /** @internal */
10934
11157
  const _noopQrlDEV = (symbolName, opts, lexicalScopeCapture) => {
10935
11158
  const newQrl = _noopQrl(symbolName, lexicalScopeCapture);
11159
+ qDev && newQrl.$setDev$(opts);
11160
+ return newQrl;
11161
+ };
11162
+ /** @internal */
11163
+ const _qrlWithChunk = (chunk, importer, symbol, lexicalScopeCapture) => {
11164
+ return createQRL(chunk, symbol, null, importer, lexicalScopeCapture);
11165
+ };
11166
+ /** @internal */
11167
+ const _qrlWithChunkDEV = (chunk, importer, symbol, opts, lexicalScopeCapture) => {
11168
+ const newQrl = _qrlWithChunk(chunk, importer, symbol, lexicalScopeCapture);
10936
11169
  newQrl.$setDev$(opts);
10937
11170
  return newQrl;
10938
11171
  };
10939
11172
  /** @internal */
10940
11173
  const qrlDEV = (chunkOrFn, symbol, opts, lexicalScopeCapture) => {
10941
11174
  const newQrl = qrl(chunkOrFn, symbol, lexicalScopeCapture, 1);
10942
- newQrl.$setDev$(opts);
11175
+ qDev && newQrl.$setDev$(opts);
10943
11176
  return newQrl;
10944
11177
  };
10945
11178
  /** @internal */
10946
11179
  const inlinedQrlDEV = (symbol, symbolName, opts, lexicalScopeCapture) => {
10947
11180
  const qrl = inlinedQrl(symbol, symbolName, lexicalScopeCapture);
10948
- qrl.$setDev$(opts);
11181
+ qDev && qrl.$setDev$(opts);
10949
11182
  return qrl;
10950
11183
  };
10951
11184
  /**
@@ -10962,6 +11195,12 @@ const _regSymbol = (symbol, hash) => {
10962
11195
  return symbol;
10963
11196
  };
10964
11197
 
11198
+ /** @internal */
11199
+ const getAsyncLocalStorage = () => {
11200
+ const process = globalThis.process;
11201
+ return process?.getBuiltinModule?.('node:async_hooks')?.AsyncLocalStorage;
11202
+ };
11203
+
10965
11204
  /** @file Shared types */
10966
11205
  /** @internal */
10967
11206
  function isStringifiable(value) {
@@ -11051,23 +11290,31 @@ async function _walkJSX(ssr, value, options) {
11051
11290
  const enqueue = (value) => stack.push(value);
11052
11291
  const drain = async () => {
11053
11292
  while (stack.length) {
11054
- const value = stack.pop();
11055
- // Reference equality first (no prototype walk), then typeof
11056
- if (value === MaybeAsyncSignal) {
11057
- const trackFn = stack.pop();
11058
- await retryOnPromise(() => stack.push(trackFn()));
11059
- continue;
11060
- }
11061
- if (typeof value === 'function') {
11062
- if (value === Promise) {
11063
- stack.push(await stack.pop());
11293
+ try {
11294
+ const value = stack.pop();
11295
+ // Reference equality first (no prototype walk), then typeof
11296
+ if (value === MaybeAsyncSignal) {
11297
+ const trackFn = stack.pop();
11298
+ await retryOnPromise(() => stack.push(trackFn()));
11299
+ continue;
11064
11300
  }
11065
- else {
11066
- await value.apply(ssr);
11301
+ if (typeof value === 'function') {
11302
+ if (value === Promise) {
11303
+ stack.push(await stack.pop());
11304
+ }
11305
+ else {
11306
+ await value.apply(ssr);
11307
+ }
11308
+ continue;
11309
+ }
11310
+ processJSXNode(ssr, enqueue, value, options);
11311
+ }
11312
+ finally {
11313
+ const pendingFlush = ssr.streamHandler.waitForPendingFlush();
11314
+ if (isPromise(pendingFlush)) {
11315
+ await pendingFlush;
11067
11316
  }
11068
- continue;
11069
11317
  }
11070
- processJSXNode(ssr, enqueue, value, options);
11071
11318
  }
11072
11319
  };
11073
11320
  await drain();
@@ -11115,7 +11362,7 @@ function processJSXNode(ssr, enqueue, value, options) {
11115
11362
  currentStyleScoped: options.currentStyleScoped,
11116
11363
  parentComponentFrame: options.parentComponentFrame,
11117
11364
  });
11118
- ssr.streamHandler.flush();
11365
+ await ssr.streamHandler.flush();
11119
11366
  }
11120
11367
  });
11121
11368
  }
@@ -11169,9 +11416,11 @@ function processJSXNode(ssr, enqueue, value, options) {
11169
11416
  const componentFrame = options.parentComponentFrame;
11170
11417
  if (componentFrame) {
11171
11418
  const compId = componentFrame.componentNode.id || '';
11172
- const projectionAttrs = isDev
11173
- ? { [DEBUG_TYPE]: "P" /* VirtualType.Projection */ }
11174
- : {};
11419
+ const projectionAttrs = isDev ? { [DEBUG_TYPE]: "P" /* VirtualType.Projection */ } : {};
11420
+ const cursorBoundary = directGetPropsProxyProp(jsx, QCursorBoundary);
11421
+ if (cursorBoundary) {
11422
+ projectionAttrs[QCursorBoundary] = cursorBoundary;
11423
+ }
11175
11424
  projectionAttrs[QSlotParent] = compId;
11176
11425
  ssr.openProjection(projectionAttrs);
11177
11426
  const host = componentFrame.componentNode;
@@ -11212,7 +11461,7 @@ function processJSXNode(ssr, enqueue, value, options) {
11212
11461
  currentStyleScoped: options.currentStyleScoped,
11213
11462
  parentComponentFrame: options.parentComponentFrame,
11214
11463
  });
11215
- ssr.streamHandler.flush();
11464
+ await ssr.streamHandler.flush();
11216
11465
  },
11217
11466
  });
11218
11467
  }
@@ -12091,7 +12340,7 @@ function inflateWrappedSignalValue(signal) {
12091
12340
  }
12092
12341
  }
12093
12342
  function restoreEffectBackRefForConsumer(effect) {
12094
- const isServerSide = import.meta.env.TEST ? isServerPlatform() : isServer;
12343
+ const isServerSide = qTest ? isServerPlatform() : isServer;
12095
12344
  const consumerBackRef = effect.consumer;
12096
12345
  if (isServerSide && !consumerBackRef) {
12097
12346
  // on browser, we don't serialize for example VNodes, so then on server side we don't have consumer
@@ -12239,6 +12488,7 @@ function getObjectById(id, stateData) {
12239
12488
  isDev && assertTrue(id < stateData.length, `Invalid reference ${id} >= ${stateData.length}`);
12240
12489
  return stateData[id];
12241
12490
  }
12491
+ /** @internal */
12242
12492
  function _createDeserializeContainer(stateData) {
12243
12493
  // eslint-disable-next-line prefer-const
12244
12494
  let state;
@@ -12281,6 +12531,14 @@ const _verifySerializable = (value, seen, ctx, preMessage) => {
12281
12531
  if (canSerialize(unwrapped)) {
12282
12532
  return value;
12283
12533
  }
12534
+ // Framework-internal branded values (e.g. route loaders/actions, validators)
12535
+ // are callables or objects that stamp __brand / __brand__ to opt out of the
12536
+ // serializer walking their internals. Honor that for both objects and
12537
+ // functions — loader/action refs are functions with __brand = 'server_loader'
12538
+ // / 'server_action' and should not be rejected as unserializable.
12539
+ if (unwrapped.__brand || unwrapped.__brand__) {
12540
+ return value;
12541
+ }
12284
12542
  const typeObj = typeof unwrapped;
12285
12543
  switch (typeObj) {
12286
12544
  case 'object':
@@ -12310,10 +12568,6 @@ const _verifySerializable = (value, seen, ctx, preMessage) => {
12310
12568
  if (unwrapped instanceof VNode) {
12311
12569
  return value;
12312
12570
  }
12313
- // We have .__brand and .__brand__
12314
- if (unwrapped.__brand || unwrapped.__brand__) {
12315
- return value;
12316
- }
12317
12571
  if (isSerializableObject(unwrapped)) {
12318
12572
  for (const [key, item] of Object.entries(unwrapped)) {
12319
12573
  _verifySerializable(item, seen, ctx + '.' + key);
@@ -13137,6 +13391,7 @@ class DomContainer extends _SharedContainer {
13137
13391
  case OnRenderProp:
13138
13392
  case QCtxAttr:
13139
13393
  case QBackRefs:
13394
+ case QCursorBoundary:
13140
13395
  getObjectById = this.$getObjectById$;
13141
13396
  break;
13142
13397
  case ELEMENT_SEQ_IDX:
@@ -13217,13 +13472,10 @@ class DomContainer extends _SharedContainer {
13217
13472
  let _locale = undefined;
13218
13473
  let localAsyncStore;
13219
13474
  if (isServer) {
13220
- import('node:async_hooks')
13221
- .then((module) => {
13222
- localAsyncStore = new module.AsyncLocalStorage();
13223
- })
13224
- .catch(() => {
13225
- // ignore if AsyncLocalStorage is not available
13226
- });
13475
+ const AsyncLocalStorage = getAsyncLocalStorage();
13476
+ if (AsyncLocalStorage) {
13477
+ localAsyncStore = new AsyncLocalStorage();
13478
+ }
13227
13479
  }
13228
13480
  /**
13229
13481
  * Retrieve the current locale.
@@ -14157,6 +14409,25 @@ const component$ = (onMount) => {
14157
14409
  /** @public */
14158
14410
  const event$ = implicit$FirstArg(eventQrl);
14159
14411
 
14412
+ /**
14413
+ * Returns the client build manifest, which includes the mappings from symbols to bundles, the
14414
+ * bundlegraph etc.
14415
+ *
14416
+ * @public
14417
+ */
14418
+ const getClientManifest = () => {
14419
+ // Keep this first because the magic-string first replaces the `!...` version and it can't replace after that.
14420
+ const manifest = globalThis.__QWIK_MANIFEST__;
14421
+ /**
14422
+ * Keep as-is, this is replaced verbatim with `false` by the qwikVite plugin, so this function
14423
+ * only throws if the build was not done correctly + no manifest was provided on globalThis.
14424
+ */
14425
+ if (!globalThis.__QWIK_MANIFEST__) {
14426
+ throw new Error(`Client manifest is not available. It should have been automatically injected during the build process. Make sure that @qwik.dev/core is internal to the build.`);
14427
+ }
14428
+ return manifest;
14429
+ };
14430
+
14160
14431
  /**
14161
14432
  * Render JSX.
14162
14433
  *
@@ -14727,7 +14998,7 @@ const useStylesScopedQrl = (styles) => {
14727
14998
  */
14728
14999
  // </docs>
14729
15000
  const useStylesScoped$ = /*#__PURE__*/ implicit$FirstArg(useStylesScopedQrl);
14730
- const liveUpdate = isDev && ((import.meta.hot && typeof document !== 'undefined') || import.meta.env.TEST);
15001
+ const liveUpdate = isDev && ((import.meta.hot && typeof document !== 'undefined') || qTest);
14731
15002
  const _useStyles = (styleQrl, transform, scoped) => {
14732
15003
  assertQrl(styleQrl);
14733
15004
  // eslint-disable-next-line prefer-const
@@ -14779,93 +15050,6 @@ const _useStyles = (styleQrl, transform, scoped) => {
14779
15050
  return styleId;
14780
15051
  };
14781
15052
 
14782
- const getSignal = (initialState) => {
14783
- const value = isFunction(initialState) && !isQwikComponent(initialState)
14784
- ? invoke(undefined, initialState)
14785
- : initialState;
14786
- return createSignal(value);
14787
- };
14788
- // <docs markdown="../readme.md#useSignal">
14789
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
14790
- // (edit ../readme.md#useSignal instead and run `pnpm docs.sync`)
14791
- /**
14792
- * Creates an object with a single reactive `.value` property, that Qwik can track across
14793
- * serializations.
14794
- *
14795
- * Use it to create state for your application. The object has a getter and setter to track reads
14796
- * and writes of the `.value` property. When the value changes, any functions that read from it will
14797
- * re-run.
14798
- *
14799
- * Prefer `useSignal` over `useStore` when possible, as it is more efficient.
14800
- *
14801
- * ### Example
14802
- *
14803
- * ```tsx
14804
- * const Signals = component$(() => {
14805
- * const counter = useSignal(1);
14806
- * const text = useSignal('changeme');
14807
- * const toggle = useSignal(false);
14808
- *
14809
- * // useSignal() can also accept a function to calculate the initial value
14810
- * const state = useSignal(() => {
14811
- * return expensiveInitialValue();
14812
- * });
14813
- *
14814
- * return (
14815
- * <div>
14816
- * <button onClick$={() => counter.value++}>Counter: {counter.value}</button>
14817
- * {
14818
- * // pass signal values as the value, the optimizer will make it pass the signal
14819
- * }
14820
- * <Child state={state.value} />
14821
- * {
14822
- * // signals can be bound to inputs. A property named `bind:x` implies that the property
14823
- * is a signal
14824
- * }
14825
- * <input type="text" bind:value={text} />
14826
- * <input type="checkbox" bind:checked={toggle} />
14827
- * </div>
14828
- * );
14829
- * });
14830
- * ```
14831
- *
14832
- * @public
14833
- */
14834
- // </docs>
14835
- const useSignal = (initialState) => {
14836
- return useConstant((getSignal), initialState);
14837
- };
14838
- /**
14839
- * Stores a value which is retained for the lifetime of the component. Subsequent calls to
14840
- * `useConstant` will always return the first value given.
14841
- *
14842
- * If the value is a function, the function is invoked once to calculate the actual value. You can
14843
- * then also pass arguments to call the function with, so that you don't need to create a new
14844
- * function on every render.
14845
- *
14846
- * @example
14847
- *
14848
- * ```tsx
14849
- * const fixedRandomValue = useConstant(() => Math.random);
14850
- * const otherFixedRandomValue = useConstant(Math.random);
14851
- *
14852
- * const getConfig = (env: string) => { ... }
14853
- * const config = useConstant(getConfig, environment);
14854
- * ```
14855
- *
14856
- * @public
14857
- */
14858
- const useConstant = (value, ...args) => {
14859
- const { val, set } = useSequentialScope();
14860
- if (val != null) {
14861
- return val;
14862
- }
14863
- // We don't want to create a subscription since we only run this once
14864
- // Note: We are not using `invoke` here because we don't want to clear the context
14865
- value = isFunction(value) && !isQwikComponent(value) ? untrack(value, ...args) : value;
14866
- return set(value);
14867
- };
14868
-
14869
15053
  const creator$2 = (qrl, options) => {
14870
15054
  qrl.resolve();
14871
15055
  return createComputedSignal(qrl, options);
@@ -15167,7 +15351,7 @@ const eachCmpTask = async ({ track }) => {
15167
15351
  const host = context.$hostElement$;
15168
15352
  const container = context.$container$;
15169
15353
  markVNodeDirty(container, host, 128 /* ChoreBits.RECONCILE */);
15170
- const isSsr = import.meta.env.TEST ? isServerPlatform() : isServer;
15354
+ const isSsr = qTest ? isServerPlatform() : isServer;
15171
15355
  if (isSsr) {
15172
15356
  await container.$renderPromise$;
15173
15357
  }
@@ -15184,6 +15368,173 @@ const eachCmp = (props) => {
15184
15368
  const Each = /*#__PURE__*/ componentQrl(
15185
15369
  /*#__PURE__*/ inlinedQrl(eachCmp, '_eaC'));
15186
15370
 
15371
+ const RevealContext = /*#__PURE__*/ createContextId('qk-reveal');
15372
+ const createRevealContext = (props) => {
15373
+ return {
15374
+ order: props.order ?? 'parallel',
15375
+ collapsed: props.collapsed === true,
15376
+ items: [],
15377
+ version: createSignal(0),
15378
+ };
15379
+ };
15380
+ /** @internal */
15381
+ const revealCanReveal = () => {
15382
+ const registration = _captures[0];
15383
+ if (registration === null) {
15384
+ return true;
15385
+ }
15386
+ const reveal = registration.reveal;
15387
+ const current = registration.item;
15388
+ const items = reveal.items;
15389
+ // `version` is monotonic; the branch keeps the subscription read from being dropped by minifiers.
15390
+ if (reveal.version.value < 0) {
15391
+ return false;
15392
+ }
15393
+ switch (reveal.order) {
15394
+ case 'together':
15395
+ for (let i = 0; i < items.length; i++) {
15396
+ if (items[i].boundary.pending.untrackedValue > 0) {
15397
+ return false;
15398
+ }
15399
+ }
15400
+ return true;
15401
+ case 'sequential':
15402
+ for (let i = 0; i < items.length; i++) {
15403
+ const item = items[i];
15404
+ if (item === current) {
15405
+ return true;
15406
+ }
15407
+ if (item.boundary.pending.untrackedValue > 0) {
15408
+ return false;
15409
+ }
15410
+ }
15411
+ return true;
15412
+ case 'reverse':
15413
+ for (let i = items.length - 1; i >= 0; i--) {
15414
+ const item = items[i];
15415
+ if (item === current) {
15416
+ return true;
15417
+ }
15418
+ if (item.boundary.pending.untrackedValue > 0) {
15419
+ return false;
15420
+ }
15421
+ }
15422
+ return true;
15423
+ default:
15424
+ return true;
15425
+ }
15426
+ };
15427
+ /** @internal */
15428
+ const revealCleanupTask = ({ cleanup }) => {
15429
+ const registration = _captures[0];
15430
+ cleanup(() => {
15431
+ // Keep the SSR registry intact so `reveal.items` serializes for resume.
15432
+ if (qTest ? isServerPlatform() : !isBrowser$1) {
15433
+ return;
15434
+ }
15435
+ const items = registration.reveal.items;
15436
+ const index = items.indexOf(registration.item);
15437
+ if (index !== -1) {
15438
+ items.splice(index, 1);
15439
+ registration.reveal.version.value++;
15440
+ }
15441
+ });
15442
+ };
15443
+ const useRevealBoundary = (boundary) => {
15444
+ const reveal = useContext(RevealContext, null);
15445
+ const registration = useConstant(() => {
15446
+ if (reveal === null) {
15447
+ return null;
15448
+ }
15449
+ const item = { boundary };
15450
+ reveal.items.push(item);
15451
+ return { reveal, item };
15452
+ });
15453
+ if (registration !== null) {
15454
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(revealCleanupTask, '_reT', [registration]), {
15455
+ deferUpdates: false,
15456
+ });
15457
+ }
15458
+ return registration;
15459
+ };
15460
+ /** @internal */
15461
+ const revealCmp = (props) => {
15462
+ if (!__EXPERIMENTAL__.suspense) {
15463
+ throw new Error('Reveal is experimental and must be enabled with `experimental: ["suspense"]` in the `qwikVite` plugin.');
15464
+ }
15465
+ const reveal = useConstant(createRevealContext, props);
15466
+ useContextProvider(RevealContext, reveal);
15467
+ return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 0, 'u7_0');
15468
+ };
15469
+ /** @public @experimental */
15470
+ const Reveal = /*#__PURE__*/ componentQrl(
15471
+ /*#__PURE__*/ inlinedQrl(revealCmp, '_reC'));
15472
+
15473
+ const _hf0 = (p0, p1, p2, p3) => ({
15474
+ display: p1.value === 'fallback' &&
15475
+ p0.fallback != null &&
15476
+ p0.fallback !== false &&
15477
+ (p2 === null || p2.value || !p3.reveal.collapsed)
15478
+ ? 'contents'
15479
+ : 'none',
15480
+ });
15481
+ const _hf0_str = '{display:p1.value==="fallback"&&p0.fallback!=null&&p0.fallback!==false&&(p2===null||p2.value||!p3.reveal.collapsed)?"contents":"none"}';
15482
+ const _hf1 = (p0, p1, p2) => ({
15483
+ display: (p2 === null || p2.value) && (p1.value === 'content' || p0.showStale) ? 'contents' : 'none',
15484
+ });
15485
+ const _hf1_str = '{display:(p2===null||p2.value)&&(p1.value==="content"||p0.showStale)?"contents":"none"}';
15486
+ /** @internal */
15487
+ const suspenseTask = ({ track, cleanup }) => {
15488
+ const cursorBoundary = _captures[0], props = _captures[1], state = _captures[2], revealRegistration = _captures[3];
15489
+ const pendingCount = track(cursorBoundary.pending);
15490
+ const isBrowserEnv = qTest ? !isServerPlatform() : isBrowser$1;
15491
+ if (revealRegistration !== null && isBrowserEnv) {
15492
+ revealRegistration.reveal.version.value++;
15493
+ }
15494
+ if (!isBrowserEnv || pendingCount === 0) {
15495
+ state.value = 'content';
15496
+ return;
15497
+ }
15498
+ const delayTimer = setTimeout(() => {
15499
+ if (cursorBoundary.pending.value > 0) {
15500
+ state.value = 'fallback';
15501
+ }
15502
+ }, props.delay ?? 0);
15503
+ cleanup(() => clearTimeout(delayTimer));
15504
+ };
15505
+ /** @internal */
15506
+ const suspenseCmp = (props) => {
15507
+ if (!__EXPERIMENTAL__.suspense) {
15508
+ throw new Error('Suspense is experimental and must be enabled with `experimental: ["suspense"]` in the `qwikVite` plugin.');
15509
+ }
15510
+ const state = useSignal('content');
15511
+ const cursorBoundary = useCursorBoundary();
15512
+ const revealRegistration = useRevealBoundary(cursorBoundary);
15513
+ const canReveal = useComputedQrl(
15514
+ /*#__PURE__*/ inlinedQrl(revealCanReveal, '_reR', [revealRegistration]));
15515
+ useTaskQrl(
15516
+ /*#__PURE__*/ inlinedQrl(suspenseTask, '_suT', [
15517
+ cursorBoundary,
15518
+ props,
15519
+ state,
15520
+ revealRegistration,
15521
+ ]));
15522
+ return /*#__PURE__*/ _jsxSorted(Fragment, null, null, [
15523
+ /*#__PURE__*/ _jsxSorted('div', {
15524
+ style: _fnSignal(_hf0, [props, state, canReveal, revealRegistration], _hf0_str),
15525
+ }, null, _wrapProp(props, 'fallback'), 1, null),
15526
+ /*#__PURE__*/ _jsxSorted('div', null, {
15527
+ style: _fnSignal(_hf1, [props, state, canReveal], _hf1_str),
15528
+ },
15529
+ /*#__PURE__*/ _jsxSorted(Slot, {
15530
+ [QCursorBoundary]: cursorBoundary,
15531
+ }, null, null, 3, 'u6_0'), 1, null),
15532
+ ], 1, 'u6_1');
15533
+ };
15534
+ /** @public @experimental */
15535
+ const Suspense = /*#__PURE__*/ componentQrl(
15536
+ /*#__PURE__*/ inlinedQrl(suspenseCmp, '_suC'));
15537
+
15187
15538
  // keep this import from core/build so the cjs build works
15188
15539
  /**
15189
15540
  * @deprecated This is no longer needed as the preloading happens automatically in qrl-class.ts.
@@ -15192,7 +15543,7 @@ const Each = /*#__PURE__*/ componentQrl(
15192
15543
  * @alpha
15193
15544
  */
15194
15545
  const PrefetchServiceWorker = (opts) => {
15195
- const isTest = import.meta.env.TEST;
15546
+ const isTest = qTest;
15196
15547
  if (isDev && !isTest) {
15197
15548
  const props = {
15198
15549
  dangerouslySetInnerHTML: '<!-- PrefetchServiceWorker is disabled in dev mode. -->',
@@ -15201,7 +15552,7 @@ const PrefetchServiceWorker = (opts) => {
15201
15552
  }
15202
15553
  // if an MFE app has a custom BASE_URL then this will be the correct value
15203
15554
  // if you're not using MFE from another codebase then you want to override this value to your custom setup
15204
- const baseUrl = import.meta.env.BASE_URL || '/';
15555
+ const baseUrl = import.meta.env?.BASE_URL || '/';
15205
15556
  const resolvedOpts = {
15206
15557
  path: 'qwik-prefetch-service-worker.js',
15207
15558
  ...opts,
@@ -15279,5 +15630,5 @@ if (import.meta.hot) {
15279
15630
  });
15280
15631
  }
15281
15632
 
15282
- export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, createQRL as _createQRL, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, _hmr, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getFirstChild as _vnode_getFirstChild, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
15633
+ export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, Reveal, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, Suspense, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, _createDeserializeContainer, createQRL as _createQRL, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, _hmr, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _qrlWithChunk, _qrlWithChunkDEV, revealCmp as _reC, revealCanReveal as _reR, revealCleanupTask as _reT, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, suspenseCmp as _suC, suspenseTask as _suT, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getElementName as _vnode_getElementName, vnode_getFirstChild as _vnode_getFirstChild, vnode_getProp as _vnode_getProp, vnode_getVNodeForChildNode as _vnode_getVNodeForChildNode, vnode_insertBefore as _vnode_insertBefore, vnode_isElementVNode as _vnode_isElementVNode, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_newVirtual as _vnode_newVirtual, vnode_remove as _vnode_remove, vnode_setProp as _vnode_setProp, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getClientManifest, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
15283
15634
  //# sourceMappingURL=core.mjs.map