@tanstack/start-client-core 1.169.4 → 1.170.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/esm/client/hydrateStart.d.ts +13 -1
  2. package/dist/esm/client/hydrateStart.js +20 -1
  3. package/dist/esm/client/hydrateStart.js.map +1 -1
  4. package/dist/esm/client/index.js +2 -2
  5. package/dist/esm/hydration/condition.d.ts +5 -0
  6. package/dist/esm/hydration/condition.js +16 -0
  7. package/dist/esm/hydration/condition.js.map +1 -0
  8. package/dist/esm/hydration/constants.d.ts +3 -0
  9. package/dist/esm/hydration/constants.js +8 -0
  10. package/dist/esm/hydration/constants.js.map +1 -0
  11. package/dist/esm/hydration/idle.d.ts +7 -0
  12. package/dist/esm/hydration/idle.js +22 -0
  13. package/dist/esm/hydration/idle.js.map +1 -0
  14. package/dist/esm/hydration/interaction.d.ts +8 -0
  15. package/dist/esm/hydration/interaction.js +221 -0
  16. package/dist/esm/hydration/interaction.js.map +1 -0
  17. package/dist/esm/hydration/load.d.ts +4 -0
  18. package/dist/esm/hydration/load.js +16 -0
  19. package/dist/esm/hydration/load.js.map +1 -0
  20. package/dist/esm/hydration/media.d.ts +4 -0
  21. package/dist/esm/hydration/media.js +23 -0
  22. package/dist/esm/hydration/media.js.map +1 -0
  23. package/dist/esm/hydration/never.d.ts +4 -0
  24. package/dist/esm/hydration/never.js +13 -0
  25. package/dist/esm/hydration/never.js.map +1 -0
  26. package/dist/esm/hydration/renderer.d.ts +5 -0
  27. package/dist/esm/hydration/renderer.js +9 -0
  28. package/dist/esm/hydration/renderer.js.map +1 -0
  29. package/dist/esm/hydration/runtime.d.ts +23 -0
  30. package/dist/esm/hydration/runtime.js +129 -0
  31. package/dist/esm/hydration/runtime.js.map +1 -0
  32. package/dist/esm/hydration/types.d.ts +38 -0
  33. package/dist/esm/hydration/visible.d.ts +8 -0
  34. package/dist/esm/hydration/visible.js +67 -0
  35. package/dist/esm/hydration/visible.js.map +1 -0
  36. package/dist/esm/hydration.d.ts +18 -0
  37. package/dist/esm/hydration.js +16 -0
  38. package/dist/esm/hydration.js.map +1 -0
  39. package/package.json +24 -3
  40. package/src/client/hydrateStart.ts +48 -2
  41. package/src/hydration/condition.ts +20 -0
  42. package/src/hydration/constants.ts +4 -0
  43. package/src/hydration/idle.ts +35 -0
  44. package/src/hydration/interaction.ts +342 -0
  45. package/src/hydration/load.ts +16 -0
  46. package/src/hydration/media.ts +25 -0
  47. package/src/hydration/never.ts +13 -0
  48. package/src/hydration/renderer.ts +21 -0
  49. package/src/hydration/runtime.ts +191 -0
  50. package/src/hydration/types.ts +90 -0
  51. package/src/hydration/visible.ts +90 -0
  52. package/src/hydration.ts +50 -0
@@ -0,0 +1,129 @@
1
+ import { hydrateIdAttribute, hydrateWhenAttribute } from "./constants.js";
2
+ //#region src/hydration/runtime.ts
3
+ var hydrateIdSelector = `[${hydrateIdAttribute}]`;
4
+ var gateRegistry = /* @__PURE__ */ new Map();
5
+ var resolvedGateIds = /* @__PURE__ */ new Set();
6
+ var fallbackHtmlByGateId = /* @__PURE__ */ new Map();
7
+ function createResolvedGate(id, when) {
8
+ return {
9
+ id,
10
+ when,
11
+ promise: Promise.resolve(),
12
+ resolve: () => {},
13
+ resolved: true,
14
+ consumers: 0,
15
+ resolveListeners: /* @__PURE__ */ new Set()
16
+ };
17
+ }
18
+ function getOrCreateGate(id, when) {
19
+ const existing = gateRegistry.get(id);
20
+ if (existing?.when === when) {
21
+ existing.consumers++;
22
+ return existing;
23
+ }
24
+ let resolvePromise;
25
+ const gate = {
26
+ id,
27
+ promise: new Promise((resolve) => {
28
+ resolvePromise = resolve;
29
+ }),
30
+ resolved: false,
31
+ consumers: 1,
32
+ when,
33
+ resolveListeners: /* @__PURE__ */ new Set(),
34
+ resolve: () => {
35
+ if (gate.resolved) return;
36
+ gate.resolved = true;
37
+ resolvePromise();
38
+ gate.resolveListeners.forEach((listener) => listener());
39
+ gate.resolveListeners.clear();
40
+ }
41
+ };
42
+ gateRegistry.set(id, gate);
43
+ if (when !== "never" && resolvedGateIds.has(id)) {
44
+ resolvedGateIds.delete(id);
45
+ gate.resolve();
46
+ }
47
+ return gate;
48
+ }
49
+ function releaseGate(gate) {
50
+ resolvedGateIds.delete(gate.id);
51
+ gate.consumers--;
52
+ if (gate.consumers > 0) return;
53
+ if (gateRegistry.get(gate.id) === gate) {
54
+ gateRegistry.delete(gate.id);
55
+ fallbackHtmlByGateId.delete(gate.id);
56
+ gate.resolveListeners.clear();
57
+ }
58
+ }
59
+ function onGateResolve(gate, listener) {
60
+ if (gate.resolved) {
61
+ listener();
62
+ return () => {};
63
+ }
64
+ gate.resolveListeners.add(listener);
65
+ return () => {
66
+ gate.resolveListeners.delete(listener);
67
+ };
68
+ }
69
+ function runHydrationStrategyCleanup(cleanup) {
70
+ if (typeof cleanup === "function") return cleanup;
71
+ }
72
+ function waitForHydrationPrefetchStrategy(strategy, options) {
73
+ if (options.signal.aborted) return Promise.resolve("abort");
74
+ return new Promise((resolve) => {
75
+ const state = { disposed: false };
76
+ const cleanupStrategyRef = { current: void 0 };
77
+ let cleanupHydrate = () => {};
78
+ const finish = (reason) => {
79
+ if (state.disposed) return;
80
+ state.disposed = true;
81
+ options.signal.removeEventListener("abort", onAbort);
82
+ cleanupHydrate();
83
+ runHydrationStrategyCleanup(cleanupStrategyRef.current)?.();
84
+ resolve(reason);
85
+ };
86
+ const onAbort = () => finish("abort");
87
+ options.signal.addEventListener("abort", onAbort, { once: true });
88
+ cleanupHydrate = options.onHydrate(() => finish("hydrate"));
89
+ const cleanupStrategy = strategy._s?.({
90
+ element: options.element,
91
+ prefetch: () => finish("prefetch")
92
+ });
93
+ cleanupStrategyRef.current = cleanupStrategy;
94
+ if (state.disposed) runHydrationStrategyCleanup(cleanupStrategy)?.();
95
+ });
96
+ }
97
+ function getMarkerGate(marker) {
98
+ const id = marker.getAttribute(hydrateIdAttribute);
99
+ return id ? gateRegistry.get(id) : void 0;
100
+ }
101
+ function resolveHydrationMarker(marker) {
102
+ const id = marker.getAttribute(hydrateIdAttribute);
103
+ const when = marker.getAttribute(hydrateWhenAttribute);
104
+ if (!id || !when || when === "never") return;
105
+ const gate = gateRegistry.get(id);
106
+ if (gate) {
107
+ if (gate.when !== "never") gate.resolve();
108
+ return;
109
+ }
110
+ resolvedGateIds.add(id);
111
+ }
112
+ function clearResolvedGateIdsInMarker(marker) {
113
+ const ownId = marker.getAttribute(hydrateIdAttribute);
114
+ if (ownId) resolvedGateIds.delete(ownId);
115
+ marker.querySelectorAll(hydrateIdSelector).forEach((childMarker) => {
116
+ const childId = childMarker.getAttribute(hydrateIdAttribute);
117
+ if (childId) resolvedGateIds.delete(childId);
118
+ });
119
+ }
120
+ function saveFallbackHtml(id, element) {
121
+ if (!fallbackHtmlByGateId.has(id)) fallbackHtmlByGateId.set(id, element.innerHTML);
122
+ }
123
+ function getFallbackHtml(id) {
124
+ return fallbackHtmlByGateId.get(id);
125
+ }
126
+ //#endregion
127
+ export { clearResolvedGateIdsInMarker, createResolvedGate, getFallbackHtml, getMarkerGate, getOrCreateGate, onGateResolve, releaseGate, resolveHydrationMarker, runHydrationStrategyCleanup, saveFallbackHtml, waitForHydrationPrefetchStrategy };
128
+
129
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","names":[],"sources":["../../../src/hydration/runtime.ts"],"sourcesContent":["import { hydrateIdAttribute, hydrateWhenAttribute } from './constants'\nimport type {\n HydrationPrefetchStrategy,\n HydrationPrefetchWaitReason,\n HydrationRuntimeGate,\n HydrationWhen,\n} from './types'\n\nconst hydrateIdSelector = `[${hydrateIdAttribute}]`\n\nexport type HydrationGateRecord = HydrationRuntimeGate & {\n id: string\n when: HydrationWhen\n promise: Promise<void>\n consumers: number\n resolveListeners: Set<() => void>\n}\n\nconst gateRegistry = /* @__PURE__ */ new Map<string, HydrationGateRecord>()\nconst resolvedGateIds = /* @__PURE__ */ new Set<string>()\nconst fallbackHtmlByGateId = /* @__PURE__ */ new Map<string, string>()\n\nexport function createResolvedGate(\n id: string,\n when: HydrationWhen,\n): HydrationGateRecord {\n return {\n id,\n when,\n promise: Promise.resolve(),\n resolve: () => {},\n resolved: true,\n consumers: 0,\n resolveListeners: new Set<() => void>(),\n }\n}\n\nexport function getOrCreateGate(\n id: string,\n when: HydrationWhen,\n): HydrationGateRecord {\n const existing = gateRegistry.get(id)\n if (existing?.when === when) {\n existing.consumers++\n return existing\n }\n\n let resolvePromise!: () => void\n const promise = new Promise<void>((resolve) => {\n resolvePromise = resolve\n })\n\n const gate: HydrationGateRecord = {\n id,\n promise,\n resolved: false,\n consumers: 1,\n when,\n resolveListeners: new Set(),\n resolve: () => {\n if (gate.resolved) return\n gate.resolved = true\n resolvePromise()\n gate.resolveListeners.forEach((listener) => listener())\n gate.resolveListeners.clear()\n },\n }\n\n gateRegistry.set(id, gate)\n if (when !== 'never' && resolvedGateIds.has(id)) {\n resolvedGateIds.delete(id)\n gate.resolve()\n }\n return gate\n}\n\nexport function releaseGate(gate: HydrationGateRecord) {\n resolvedGateIds.delete(gate.id)\n gate.consumers--\n if (gate.consumers > 0) return\n if (gateRegistry.get(gate.id) === gate) {\n gateRegistry.delete(gate.id)\n fallbackHtmlByGateId.delete(gate.id)\n gate.resolveListeners.clear()\n }\n}\n\nexport function onGateResolve(gate: HydrationGateRecord, listener: () => void) {\n if (gate.resolved) {\n listener()\n return () => {}\n }\n\n gate.resolveListeners.add(listener)\n return () => {\n gate.resolveListeners.delete(listener)\n }\n}\n\nexport function runHydrationStrategyCleanup(cleanup: void | (() => void)) {\n if (typeof cleanup === 'function') return cleanup\n return undefined\n}\n\nexport function waitForHydrationPrefetchStrategy(\n strategy: HydrationPrefetchStrategy,\n options: {\n element: Element | null\n signal: AbortSignal\n onHydrate: (listener: () => void) => () => void\n },\n): Promise<HydrationPrefetchWaitReason> {\n if (options.signal.aborted) {\n return Promise.resolve('abort')\n }\n\n return new Promise((resolve) => {\n const state = { disposed: false }\n const cleanupStrategyRef: { current: void | (() => void) } = {\n current: undefined,\n }\n let cleanupHydrate = () => {}\n\n const finish = (reason: HydrationPrefetchWaitReason) => {\n if (state.disposed) return\n state.disposed = true\n options.signal.removeEventListener('abort', onAbort)\n cleanupHydrate()\n runHydrationStrategyCleanup(cleanupStrategyRef.current)?.()\n resolve(reason)\n }\n\n const onAbort = () => finish('abort')\n\n options.signal.addEventListener('abort', onAbort, { once: true })\n cleanupHydrate = options.onHydrate(() => finish('hydrate'))\n const cleanupStrategy = strategy._s?.({\n element: options.element,\n prefetch: () => finish('prefetch'),\n })\n cleanupStrategyRef.current = cleanupStrategy\n if (state.disposed) {\n runHydrationStrategyCleanup(cleanupStrategy)?.()\n }\n })\n}\n\nexport function getMarkerGate(marker: Element) {\n const id = marker.getAttribute(hydrateIdAttribute)\n return id ? gateRegistry.get(id) : undefined\n}\n\nexport function resolveHydrationMarker(marker: Element) {\n const id = marker.getAttribute(hydrateIdAttribute)\n const when = marker.getAttribute(hydrateWhenAttribute)\n if (!id || !when || when === 'never') {\n return\n }\n\n const gate = gateRegistry.get(id)\n if (gate) {\n if (gate.when !== 'never') gate.resolve()\n return\n }\n\n resolvedGateIds.add(id)\n}\n\nexport function clearResolvedGateIdsInMarker(marker: Element) {\n const ownId = marker.getAttribute(hydrateIdAttribute)\n if (ownId) {\n resolvedGateIds.delete(ownId)\n }\n\n marker.querySelectorAll(hydrateIdSelector).forEach((childMarker) => {\n const childId = childMarker.getAttribute(hydrateIdAttribute)\n if (childId) {\n resolvedGateIds.delete(childId)\n }\n })\n}\n\nexport function saveFallbackHtml(id: string, element: Element) {\n if (!fallbackHtmlByGateId.has(id)) {\n fallbackHtmlByGateId.set(id, element.innerHTML)\n }\n}\n\nexport function getFallbackHtml(id: string) {\n return fallbackHtmlByGateId.get(id)\n}\n"],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export type HydrationWhen = 'load' | 'idle' | 'visible' | 'media' | 'interaction' | 'condition' | 'never' | 'dynamic';
2
+ export type HydrationInteractionEvent = 'auxclick' | 'click' | 'contextmenu' | 'dblclick' | 'focusin' | 'keydown' | 'keyup' | 'mousedown' | 'mouseenter' | 'mouseover' | 'mouseup' | 'pointerdown' | 'pointerenter' | 'pointerover' | 'pointerup';
3
+ export type HydrationInteractionEvents = HydrationInteractionEvent | ReadonlyArray<HydrationInteractionEvent>;
4
+ export type HydrationMarkerAttributes = Record<string, string | undefined>;
5
+ export type HydrationRuntimeGate = {
6
+ id?: string;
7
+ when?: HydrationWhen;
8
+ resolved: boolean;
9
+ resolve: () => void;
10
+ };
11
+ export type HydrationRuntimeContext = {
12
+ element: Element | null;
13
+ gate?: HydrationRuntimeGate;
14
+ prefetch?: () => void;
15
+ delegated?: boolean;
16
+ };
17
+ export type HydrationStrategyTypes<TWhen extends HydrationWhen = HydrationWhen, TCanPrefetch extends boolean = boolean> = {
18
+ when: TWhen;
19
+ canPrefetch: TCanPrefetch;
20
+ };
21
+ export type HydrationStrategy<TWhen extends HydrationWhen = HydrationWhen, TCanPrefetch extends boolean = boolean> = {
22
+ _t?: TWhen;
23
+ readonly '~types'?: HydrationStrategyTypes<TWhen, TCanPrefetch>;
24
+ _d?: () => boolean;
25
+ _s?: (context: HydrationRuntimeContext) => void | (() => void);
26
+ _o?: (id: string) => void;
27
+ _a?: () => HydrationMarkerAttributes | undefined;
28
+ };
29
+ export type HydrationPrefetchWhen = Exclude<HydrationWhen, 'condition' | 'never' | 'dynamic'>;
30
+ export type HydrationPrefetchStrategy<TWhen extends HydrationPrefetchWhen = HydrationPrefetchWhen> = HydrationStrategy<TWhen, true>;
31
+ export type HydrationPrefetchWaitReason = 'prefetch' | 'hydrate' | 'abort';
32
+ export type HydrationPrefetchContext = {
33
+ element: Element | null;
34
+ signal: AbortSignal;
35
+ preload: () => Promise<void>;
36
+ waitFor: (strategy: HydrationPrefetchStrategy) => Promise<HydrationPrefetchWaitReason>;
37
+ };
38
+ export type HydrationPrefetchFunction = (context: HydrationPrefetchContext) => void | Promise<void>;
@@ -0,0 +1,8 @@
1
+ import { HydrationPrefetchStrategy } from './types.js';
2
+ declare const visibleType = "visible";
3
+ export type VisibleHydrationOptions = {
4
+ rootMargin?: string;
5
+ threshold?: number | Array<number>;
6
+ };
7
+ export declare function visible(options?: VisibleHydrationOptions): HydrationPrefetchStrategy<typeof visibleType>;
8
+ export {};
@@ -0,0 +1,67 @@
1
+ //#region src/hydration/visible.ts
2
+ var visibleType = "visible";
3
+ var observerRegistry = /* @__PURE__ */ new Map();
4
+ function cleanupVisibleObserverEntry(observerEntry) {
5
+ if (observerEntry.elements.size > 0) return;
6
+ observerEntry.observer.disconnect();
7
+ observerRegistry.delete(observerEntry.key);
8
+ }
9
+ /* @__NO_SIDE_EFFECTS__ */
10
+ function visible(options = {}) {
11
+ const rootMargin = options.rootMargin ?? "600px";
12
+ const threshold = options.threshold ?? 0;
13
+ return {
14
+ _t: visibleType,
15
+ _s: ({ element, gate, prefetch }) => {
16
+ const callback = prefetch ?? gate.resolve;
17
+ if (!element) {
18
+ callback();
19
+ return;
20
+ }
21
+ const key = `${rootMargin}|${Array.isArray(threshold) ? threshold.join(",") : String(threshold)}`;
22
+ let observerEntry = observerRegistry.get(key);
23
+ if (!observerEntry) {
24
+ const entry = {
25
+ key,
26
+ elements: /* @__PURE__ */ new Map(),
27
+ observer: new IntersectionObserver((entries) => {
28
+ for (const intersectingEntry of entries) {
29
+ if (!intersectingEntry.isIntersecting) continue;
30
+ const callbacks = entry.elements.get(intersectingEntry.target);
31
+ if (!callbacks) continue;
32
+ callbacks.forEach((callback) => callback());
33
+ entry.elements.delete(intersectingEntry.target);
34
+ entry.observer.unobserve(intersectingEntry.target);
35
+ cleanupVisibleObserverEntry(entry);
36
+ }
37
+ }, {
38
+ rootMargin,
39
+ threshold
40
+ })
41
+ };
42
+ observerRegistry.set(key, entry);
43
+ observerEntry = entry;
44
+ }
45
+ let callbacks = observerEntry.elements.get(element);
46
+ if (!callbacks) {
47
+ callbacks = /* @__PURE__ */ new Set();
48
+ observerEntry.elements.set(element, callbacks);
49
+ observerEntry.observer.observe(element);
50
+ }
51
+ callbacks.add(callback);
52
+ return () => {
53
+ const currentCallbacks = observerEntry.elements.get(element);
54
+ currentCallbacks?.delete(callback);
55
+ if (currentCallbacks?.size === 0) {
56
+ observerEntry.elements.delete(element);
57
+ observerEntry.observer.unobserve(element);
58
+ }
59
+ cleanupVisibleObserverEntry(observerEntry);
60
+ };
61
+ }
62
+ };
63
+ }
64
+ //#endregion
65
+ export { visible };
66
+
67
+ //# sourceMappingURL=visible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visible.js","names":[],"sources":["../../../src/hydration/visible.ts"],"sourcesContent":["import type { HydrationPrefetchStrategy } from './types'\n\nconst visibleType = 'visible'\n\nexport type VisibleHydrationOptions = {\n rootMargin?: string\n threshold?: number | Array<number>\n}\n\ntype VisibleObserverEntry = {\n key: string\n observer: IntersectionObserver\n elements: Map<Element, Set<() => void>>\n}\n\nconst observerRegistry = /* @__PURE__ */ new Map<string, VisibleObserverEntry>()\n\nfunction cleanupVisibleObserverEntry(observerEntry: VisibleObserverEntry) {\n if (observerEntry.elements.size > 0) return\n observerEntry.observer.disconnect()\n observerRegistry.delete(observerEntry.key)\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function visible(\n options: VisibleHydrationOptions = {},\n): HydrationPrefetchStrategy<typeof visibleType> {\n const rootMargin = options.rootMargin ?? '600px'\n const threshold = options.threshold ?? 0\n\n return {\n _t: visibleType,\n _s: ({ element, gate, prefetch }) => {\n const callback = prefetch ?? gate!.resolve\n\n if (!element) {\n callback()\n return\n }\n\n const key = `${rootMargin}|${\n Array.isArray(threshold) ? threshold.join(',') : String(threshold)\n }`\n let observerEntry = observerRegistry.get(key)\n\n if (!observerEntry) {\n const entry: VisibleObserverEntry = {\n key,\n elements: new Map<Element, Set<() => void>>(),\n observer: new IntersectionObserver(\n (entries) => {\n for (const intersectingEntry of entries) {\n if (!intersectingEntry.isIntersecting) continue\n\n const callbacks = entry.elements.get(intersectingEntry.target)\n if (!callbacks) continue\n\n callbacks.forEach((callback) => callback())\n entry.elements.delete(intersectingEntry.target)\n entry.observer.unobserve(intersectingEntry.target)\n cleanupVisibleObserverEntry(entry)\n }\n },\n { rootMargin, threshold },\n ),\n }\n observerRegistry.set(key, entry)\n observerEntry = entry\n }\n\n let callbacks = observerEntry.elements.get(element)\n if (!callbacks) {\n callbacks = new Set()\n observerEntry.elements.set(element, callbacks)\n observerEntry.observer.observe(element)\n }\n callbacks.add(callback)\n\n return () => {\n const currentCallbacks = observerEntry.elements.get(element)\n currentCallbacks?.delete(callback)\n if (currentCallbacks?.size === 0) {\n observerEntry.elements.delete(element)\n observerEntry.observer.unobserve(element)\n }\n cleanupVisibleObserverEntry(observerEntry)\n }\n },\n }\n}\n"],"mappings":";AAEA,IAAM,cAAc;AAapB,IAAM,mCAAmC,IAAI,KAAmC;AAEhF,SAAS,4BAA4B,eAAqC;AACxE,KAAI,cAAc,SAAS,OAAO,EAAG;AACrC,eAAc,SAAS,YAAY;AACnC,kBAAiB,OAAO,cAAc,IAAI;;;AAI5C,SAAgB,QACd,UAAmC,EAAE,EACU;CAC/C,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,YAAY,QAAQ,aAAa;AAEvC,QAAO;EACL,IAAI;EACJ,KAAK,EAAE,SAAS,MAAM,eAAe;GACnC,MAAM,WAAW,YAAY,KAAM;AAEnC,OAAI,CAAC,SAAS;AACZ,cAAU;AACV;;GAGF,MAAM,MAAM,GAAG,WAAW,GACxB,MAAM,QAAQ,UAAU,GAAG,UAAU,KAAK,IAAI,GAAG,OAAO,UAAU;GAEpE,IAAI,gBAAgB,iBAAiB,IAAI,IAAI;AAE7C,OAAI,CAAC,eAAe;IAClB,MAAM,QAA8B;KAClC;KACA,0BAAU,IAAI,KAA+B;KAC7C,UAAU,IAAI,sBACX,YAAY;AACX,WAAK,MAAM,qBAAqB,SAAS;AACvC,WAAI,CAAC,kBAAkB,eAAgB;OAEvC,MAAM,YAAY,MAAM,SAAS,IAAI,kBAAkB,OAAO;AAC9D,WAAI,CAAC,UAAW;AAEhB,iBAAU,SAAS,aAAa,UAAU,CAAC;AAC3C,aAAM,SAAS,OAAO,kBAAkB,OAAO;AAC/C,aAAM,SAAS,UAAU,kBAAkB,OAAO;AAClD,mCAA4B,MAAM;;QAGtC;MAAE;MAAY;MAAW,CAC1B;KACF;AACD,qBAAiB,IAAI,KAAK,MAAM;AAChC,oBAAgB;;GAGlB,IAAI,YAAY,cAAc,SAAS,IAAI,QAAQ;AACnD,OAAI,CAAC,WAAW;AACd,gCAAY,IAAI,KAAK;AACrB,kBAAc,SAAS,IAAI,SAAS,UAAU;AAC9C,kBAAc,SAAS,QAAQ,QAAQ;;AAEzC,aAAU,IAAI,SAAS;AAEvB,gBAAa;IACX,MAAM,mBAAmB,cAAc,SAAS,IAAI,QAAQ;AAC5D,sBAAkB,OAAO,SAAS;AAClC,QAAI,kBAAkB,SAAS,GAAG;AAChC,mBAAc,SAAS,OAAO,QAAQ;AACtC,mBAAc,SAAS,UAAU,QAAQ;;AAE3C,gCAA4B,cAAc;;;EAG/C"}
@@ -0,0 +1,18 @@
1
+ export { condition } from './hydration/condition.js';
2
+ export type { HydrationCondition } from './hydration/condition.js';
3
+ export { hydrateIdAttribute, hydrateInteractionEventsAttribute, hydrateWhenAttribute, } from './hydration/constants.js';
4
+ export declare const hydrateIdSelector = "[data-ts-hydrate-id]";
5
+ export { idle } from './hydration/idle.js';
6
+ export type { IdleHydrationOptions } from './hydration/idle.js';
7
+ export { interaction } from './hydration/interaction.js';
8
+ export { load } from './hydration/load.js';
9
+ export { media } from './hydration/media.js';
10
+ export { never } from './hydration/never.js';
11
+ export { clearResolvedGateIdsInMarker, createResolvedGate, getFallbackHtml, getMarkerGate, getOrCreateGate, onGateResolve, releaseGate, resolveHydrationMarker, runHydrationStrategyCleanup, saveFallbackHtml, waitForHydrationPrefetchStrategy, } from './hydration/runtime.js';
12
+ export { withHydrationRenderer } from './hydration/renderer.js';
13
+ export { visible } from './hydration/visible.js';
14
+ export { listenForDelegatedHydrationIntent } from './hydration/interaction.js';
15
+ export type { VisibleHydrationOptions } from './hydration/visible.js';
16
+ export type { HydrationGateRecord } from './hydration/runtime.js';
17
+ export type { HydrationStrategyWithRenderer } from './hydration/renderer.js';
18
+ export type { HydrationInteractionEvent, HydrationInteractionEvents, HydrationMarkerAttributes, HydrationPrefetchContext, HydrationPrefetchFunction, HydrationPrefetchWhen, HydrationPrefetchStrategy, HydrationPrefetchWaitReason, HydrationRuntimeContext, HydrationRuntimeGate, HydrationStrategy, HydrationStrategyTypes, HydrationWhen, } from './hydration/types.js';
@@ -0,0 +1,16 @@
1
+ import { hydrateIdAttribute, hydrateInteractionEventsAttribute, hydrateWhenAttribute } from "./hydration/constants.js";
2
+ import { condition } from "./hydration/condition.js";
3
+ import { idle } from "./hydration/idle.js";
4
+ import { clearResolvedGateIdsInMarker, createResolvedGate, getFallbackHtml, getMarkerGate, getOrCreateGate, onGateResolve, releaseGate, resolveHydrationMarker, runHydrationStrategyCleanup, saveFallbackHtml, waitForHydrationPrefetchStrategy } from "./hydration/runtime.js";
5
+ import { interaction, listenForDelegatedHydrationIntent } from "./hydration/interaction.js";
6
+ import { load } from "./hydration/load.js";
7
+ import { media } from "./hydration/media.js";
8
+ import { never } from "./hydration/never.js";
9
+ import { withHydrationRenderer } from "./hydration/renderer.js";
10
+ import { visible } from "./hydration/visible.js";
11
+ //#region src/hydration.ts
12
+ var hydrateIdSelector = `[${hydrateIdAttribute}]`;
13
+ //#endregion
14
+ export { clearResolvedGateIdsInMarker, condition, createResolvedGate, getFallbackHtml, getMarkerGate, getOrCreateGate, hydrateIdAttribute, hydrateIdSelector, hydrateInteractionEventsAttribute, hydrateWhenAttribute, idle, interaction, listenForDelegatedHydrationIntent, load, media, never, onGateResolve, releaseGate, resolveHydrationMarker, runHydrationStrategyCleanup, saveFallbackHtml, visible, waitForHydrationPrefetchStrategy, withHydrationRenderer };
15
+
16
+ //# sourceMappingURL=hydration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydration.js","names":[],"sources":["../../src/hydration.ts"],"sourcesContent":["import { hydrateIdAttribute } from './hydration/constants'\n\nexport { condition } from './hydration/condition'\nexport type { HydrationCondition } from './hydration/condition'\nexport {\n hydrateIdAttribute,\n hydrateInteractionEventsAttribute,\n hydrateWhenAttribute,\n} from './hydration/constants'\nexport const hydrateIdSelector = `[${hydrateIdAttribute}]`\nexport { idle } from './hydration/idle'\nexport type { IdleHydrationOptions } from './hydration/idle'\nexport { interaction } from './hydration/interaction'\nexport { load } from './hydration/load'\nexport { media } from './hydration/media'\nexport { never } from './hydration/never'\nexport {\n clearResolvedGateIdsInMarker,\n createResolvedGate,\n getFallbackHtml,\n getMarkerGate,\n getOrCreateGate,\n onGateResolve,\n releaseGate,\n resolveHydrationMarker,\n runHydrationStrategyCleanup,\n saveFallbackHtml,\n waitForHydrationPrefetchStrategy,\n} from './hydration/runtime'\nexport { withHydrationRenderer } from './hydration/renderer'\nexport { visible } from './hydration/visible'\nexport { listenForDelegatedHydrationIntent } from './hydration/interaction'\nexport type { VisibleHydrationOptions } from './hydration/visible'\nexport type { HydrationGateRecord } from './hydration/runtime'\nexport type { HydrationStrategyWithRenderer } from './hydration/renderer'\nexport type {\n HydrationInteractionEvent,\n HydrationInteractionEvents,\n HydrationMarkerAttributes,\n HydrationPrefetchContext,\n HydrationPrefetchFunction,\n HydrationPrefetchWhen,\n HydrationPrefetchStrategy,\n HydrationPrefetchWaitReason,\n HydrationRuntimeContext,\n HydrationRuntimeGate,\n HydrationStrategy,\n HydrationStrategyTypes,\n HydrationWhen,\n} from './hydration/types'\n"],"mappings":";;;;;;;;;;;AASA,IAAa,oBAAoB,IAAI,mBAAmB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-client-core",
3
- "version": "1.169.4",
3
+ "version": "1.170.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -44,16 +44,37 @@
44
44
  "default": "./dist/esm/client-rpc/index.js"
45
45
  }
46
46
  },
47
+ "./hydration": {
48
+ "import": {
49
+ "types": "./dist/esm/hydration.d.ts",
50
+ "default": "./dist/esm/hydration.js"
51
+ }
52
+ },
53
+ "./hydration/constants": {
54
+ "import": {
55
+ "types": "./dist/esm/hydration/constants.d.ts",
56
+ "default": "./dist/esm/hydration/constants.js"
57
+ }
58
+ },
59
+ "./hydration/runtime": {
60
+ "import": {
61
+ "types": "./dist/esm/hydration/runtime.d.ts",
62
+ "default": "./dist/esm/hydration/runtime.js"
63
+ }
64
+ },
47
65
  "./package.json": "./package.json"
48
66
  },
49
67
  "imports": {
50
68
  "#tanstack-start-entry": {
69
+ "types": "./src/start-entry.d.ts",
51
70
  "default": "./dist/esm/fake-entries/start.js"
52
71
  },
53
72
  "#tanstack-router-entry": {
73
+ "types": "./src/start-entry.d.ts",
54
74
  "default": "./dist/esm/fake-entries/router.js"
55
75
  },
56
76
  "#tanstack-start-plugin-adapters": {
77
+ "types": "./src/start-entry.d.ts",
57
78
  "default": "./dist/esm/fake-entries/plugin-adapters.js"
58
79
  }
59
80
  },
@@ -70,9 +91,9 @@
70
91
  },
71
92
  "dependencies": {
72
93
  "seroval": "^1.5.4",
73
- "@tanstack/router-core": "1.171.2",
94
+ "@tanstack/router-core": "1.171.3",
74
95
  "@tanstack/start-fn-stubs": "1.162.0",
75
- "@tanstack/start-storage-context": "1.167.4"
96
+ "@tanstack/start-storage-context": "1.167.5"
76
97
  },
77
98
  "devDependencies": {
78
99
  "vite": "*",
@@ -1,5 +1,4 @@
1
1
  import { hydrate } from '@tanstack/router-core/ssr/client'
2
-
3
2
  import { startInstance } from '#tanstack-start-entry'
4
3
  import {
5
4
  hasPluginAdapters,
@@ -10,7 +9,19 @@ import { ServerFunctionSerializationAdapter } from './ServerFunctionSerializatio
10
9
  import type { AnyRouter, AnySerializationAdapter } from '@tanstack/router-core'
11
10
  import type { AnyStartInstanceOptions } from '../createStart'
12
11
 
13
- export async function hydrateStart(): Promise<AnyRouter> {
12
+ type HotContext = {
13
+ data?: Record<string, unknown>
14
+ dispose?: (cb: (data: Record<string, unknown>) => void) => void
15
+ }
16
+
17
+ declare global {
18
+ interface ImportMeta {
19
+ hot?: HotContext
20
+ webpackHot?: HotContext
21
+ }
22
+ }
23
+
24
+ async function hydrateStart(): Promise<AnyRouter> {
14
25
  const router = await getRouter()
15
26
 
16
27
  let serializationAdapters: Array<AnySerializationAdapter>
@@ -47,3 +58,38 @@ export async function hydrateStart(): Promise<AnyRouter> {
47
58
 
48
59
  return router
49
60
  }
61
+
62
+ function hydrateStartWithHmr(): Promise<AnyRouter> {
63
+ const hot = import.meta.hot ?? import.meta.webpackHot
64
+
65
+ if (!hot) {
66
+ return hydrateStart()
67
+ }
68
+
69
+ const key = 'tss-hydrate-start-promise'
70
+ const hotData = (hot.data ??= {})
71
+ let hydrationPromise = hotData[key] as Promise<AnyRouter> | undefined
72
+
73
+ if (!hydrationPromise) {
74
+ hydrationPromise = hydrateStart().catch((error) => {
75
+ if (hotData[key] === hydrationPromise) {
76
+ hotData[key] = undefined
77
+ }
78
+
79
+ throw error
80
+ })
81
+
82
+ hotData[key] = hydrationPromise
83
+ }
84
+
85
+ hot.dispose?.((data) => {
86
+ data[key] = hotData[key]
87
+ })
88
+
89
+ return hydrationPromise
90
+ }
91
+
92
+ const exportedHydrateStart =
93
+ process.env.NODE_ENV !== 'production' ? hydrateStartWithHmr : hydrateStart
94
+
95
+ export { exportedHydrateStart as hydrateStart }
@@ -0,0 +1,20 @@
1
+ import type { HydrationStrategy } from './types'
2
+
3
+ const conditionType = 'condition'
4
+
5
+ export type HydrationCondition = boolean | (() => boolean)
6
+
7
+ /* @__NO_SIDE_EFFECTS__ */
8
+ export function condition(
9
+ condition: HydrationCondition,
10
+ ): HydrationStrategy<typeof conditionType, false> {
11
+ return {
12
+ _t: conditionType,
13
+ _d: () => !(typeof condition === 'function' ? condition() : condition),
14
+ _s: ({ gate }) => {
15
+ if (typeof condition === 'function' ? condition() : condition) {
16
+ gate!.resolve()
17
+ }
18
+ },
19
+ }
20
+ }
@@ -0,0 +1,4 @@
1
+ export const hydrateIdAttribute = 'data-ts-hydrate-id'
2
+ export const hydrateWhenAttribute = 'data-ts-hydrate-when'
3
+ export const hydrateInteractionEventsAttribute =
4
+ 'data-ts-hydrate-interaction-events'
@@ -0,0 +1,35 @@
1
+ import type { HydrationPrefetchStrategy } from './types'
2
+
3
+ const idleType = 'idle'
4
+
5
+ export type IdleHydrationOptions = {
6
+ timeout?: number
7
+ }
8
+
9
+ export function idle(
10
+ options: IdleHydrationOptions = {},
11
+ ): HydrationPrefetchStrategy<typeof idleType> {
12
+ const timeout = options.timeout ?? 2000
13
+
14
+ return {
15
+ _t: idleType,
16
+ _s: ({ gate, prefetch }) => {
17
+ const schedule = globalThis as unknown as {
18
+ requestIdleCallback?: (
19
+ callback: IdleRequestCallback,
20
+ options?: IdleRequestOptions,
21
+ ) => number
22
+ cancelIdleCallback?: (handle: number) => void
23
+ }
24
+ const callback = prefetch ?? gate!.resolve
25
+
26
+ if (schedule.requestIdleCallback) {
27
+ const handle = schedule.requestIdleCallback(callback, { timeout })
28
+ return () => schedule.cancelIdleCallback?.(handle)
29
+ }
30
+
31
+ const timeoutId = globalThis.setTimeout(callback, timeout)
32
+ return () => globalThis.clearTimeout(timeoutId)
33
+ },
34
+ }
35
+ }