@tanstack/redact 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/dist/core/index.d.ts +2 -0
  2. package/dist/core/index.js +4 -0
  3. package/dist/core/index.js.map +7 -0
  4. package/dist/core/internal.d.ts +76 -0
  5. package/dist/core/internal.js +59 -0
  6. package/dist/core/internal.js.map +7 -0
  7. package/dist/core/types.d.ts +21 -0
  8. package/dist/core/types.js +10 -0
  9. package/dist/core/types.js.map +7 -0
  10. package/dist/dom/_all.d.ts +17 -0
  11. package/dist/dom/_all.js +51 -0
  12. package/dist/dom/_all.js.map +7 -0
  13. package/dist/dom/client.d.ts +3 -0
  14. package/dist/dom/client.js +8 -0
  15. package/dist/dom/client.js.map +7 -0
  16. package/dist/dom/dispatcher.d.ts +20 -0
  17. package/dist/dom/dispatcher.js +267 -0
  18. package/dist/dom/dispatcher.js.map +7 -0
  19. package/dist/dom/dom.d.ts +10 -0
  20. package/dist/dom/dom.js +262 -0
  21. package/dist/dom/dom.js.map +7 -0
  22. package/dist/dom/event-replay.d.ts +13 -0
  23. package/dist/dom/event-replay.js +46 -0
  24. package/dist/dom/event-replay.js.map +7 -0
  25. package/dist/dom/features/class/full.d.ts +1 -0
  26. package/dist/dom/features/class/full.js +91 -0
  27. package/dist/dom/features/class/full.js.map +7 -0
  28. package/dist/dom/features/class/index.d.ts +1 -0
  29. package/dist/dom/features/class/index.js +3 -0
  30. package/dist/dom/features/class/index.js.map +7 -0
  31. package/dist/dom/features/class/stub.d.ts +1 -0
  32. package/dist/dom/features/class/stub.js +50 -0
  33. package/dist/dom/features/class/stub.js.map +7 -0
  34. package/dist/dom/features/context/full.d.ts +1 -0
  35. package/dist/dom/features/context/full.js +49 -0
  36. package/dist/dom/features/context/full.js.map +7 -0
  37. package/dist/dom/features/context/index.d.ts +1 -0
  38. package/dist/dom/features/context/index.js +3 -0
  39. package/dist/dom/features/context/index.js.map +7 -0
  40. package/dist/dom/features/context/stub.d.ts +1 -0
  41. package/dist/dom/features/context/stub.js +22 -0
  42. package/dist/dom/features/context/stub.js.map +7 -0
  43. package/dist/dom/features/forward-ref/full.d.ts +1 -0
  44. package/dist/dom/features/forward-ref/full.js +51 -0
  45. package/dist/dom/features/forward-ref/full.js.map +7 -0
  46. package/dist/dom/features/forward-ref/index.d.ts +1 -0
  47. package/dist/dom/features/forward-ref/index.js +3 -0
  48. package/dist/dom/features/forward-ref/index.js.map +7 -0
  49. package/dist/dom/features/forward-ref/stub.d.ts +1 -0
  50. package/dist/dom/features/forward-ref/stub.js +22 -0
  51. package/dist/dom/features/forward-ref/stub.js.map +7 -0
  52. package/dist/dom/features/hydration/full.d.ts +61 -0
  53. package/dist/dom/features/hydration/full.js +264 -0
  54. package/dist/dom/features/hydration/full.js.map +7 -0
  55. package/dist/dom/features/hydration/index.d.ts +1 -0
  56. package/dist/dom/features/hydration/index.js +3 -0
  57. package/dist/dom/features/hydration/index.js.map +7 -0
  58. package/dist/dom/features/hydration/stub.d.ts +28 -0
  59. package/dist/dom/features/hydration/stub.js +66 -0
  60. package/dist/dom/features/hydration/stub.js.map +7 -0
  61. package/dist/dom/features/index.d.ts +7 -0
  62. package/dist/dom/features/index.js +9 -0
  63. package/dist/dom/features/index.js.map +7 -0
  64. package/dist/dom/features/lazy/full.d.ts +1 -0
  65. package/dist/dom/features/lazy/full.js +78 -0
  66. package/dist/dom/features/lazy/full.js.map +7 -0
  67. package/dist/dom/features/lazy/index.d.ts +1 -0
  68. package/dist/dom/features/lazy/index.js +3 -0
  69. package/dist/dom/features/lazy/index.js.map +7 -0
  70. package/dist/dom/features/lazy/stub.d.ts +1 -0
  71. package/dist/dom/features/lazy/stub.js +36 -0
  72. package/dist/dom/features/lazy/stub.js.map +7 -0
  73. package/dist/dom/features/memo/full.d.ts +1 -0
  74. package/dist/dom/features/memo/full.js +59 -0
  75. package/dist/dom/features/memo/full.js.map +7 -0
  76. package/dist/dom/features/memo/index.d.ts +1 -0
  77. package/dist/dom/features/memo/index.js +3 -0
  78. package/dist/dom/features/memo/index.js.map +7 -0
  79. package/dist/dom/features/memo/stub.d.ts +1 -0
  80. package/dist/dom/features/memo/stub.js +36 -0
  81. package/dist/dom/features/memo/stub.js.map +7 -0
  82. package/dist/dom/features/portal/full.d.ts +1 -0
  83. package/dist/dom/features/portal/full.js +19 -0
  84. package/dist/dom/features/portal/full.js.map +7 -0
  85. package/dist/dom/features/portal/index.d.ts +1 -0
  86. package/dist/dom/features/portal/index.js +3 -0
  87. package/dist/dom/features/portal/index.js.map +7 -0
  88. package/dist/dom/features/portal/stub.d.ts +1 -0
  89. package/dist/dom/features/portal/stub.js +7 -0
  90. package/dist/dom/features/portal/stub.js.map +7 -0
  91. package/dist/dom/features/suspense/full.d.ts +1 -0
  92. package/dist/dom/features/suspense/full.js +146 -0
  93. package/dist/dom/features/suspense/full.js.map +7 -0
  94. package/dist/dom/features/suspense/index.d.ts +1 -0
  95. package/dist/dom/features/suspense/index.js +3 -0
  96. package/dist/dom/features/suspense/index.js.map +7 -0
  97. package/dist/dom/features/suspense/stub.d.ts +1 -0
  98. package/dist/dom/features/suspense/stub.js +6 -0
  99. package/dist/dom/features/suspense/stub.js.map +7 -0
  100. package/dist/dom/index.d.ts +25 -0
  101. package/dist/dom/index.js +45 -0
  102. package/dist/dom/index.js.map +7 -0
  103. package/dist/dom/portal.d.ts +2 -0
  104. package/dist/dom/portal.js +15 -0
  105. package/dist/dom/portal.js.map +7 -0
  106. package/dist/dom/reconcile.d.ts +40 -0
  107. package/dist/dom/reconcile.js +795 -0
  108. package/dist/dom/reconcile.js.map +7 -0
  109. package/dist/dom/root.d.ts +15 -0
  110. package/dist/dom/root.js +85 -0
  111. package/dist/dom/root.js.map +7 -0
  112. package/dist/dom/test-utils.d.ts +1 -0
  113. package/dist/dom/test-utils.js +10 -0
  114. package/dist/dom/test-utils.js.map +7 -0
  115. package/dist/react/children.d.ts +8 -0
  116. package/dist/react/children.js +75 -0
  117. package/dist/react/children.js.map +7 -0
  118. package/dist/react/class.d.ts +31 -0
  119. package/dist/react/class.js +40 -0
  120. package/dist/react/class.js.map +7 -0
  121. package/dist/react/context.d.ts +27 -0
  122. package/dist/react/context.js +31 -0
  123. package/dist/react/context.js.map +7 -0
  124. package/dist/react/element.d.ts +10 -0
  125. package/dist/react/element.js +64 -0
  126. package/dist/react/element.js.map +7 -0
  127. package/dist/react/hooks.d.ts +37 -0
  128. package/dist/react/hooks.js +146 -0
  129. package/dist/react/hooks.js.map +7 -0
  130. package/dist/react/index.d.ts +76 -0
  131. package/dist/react/index.js +187 -0
  132. package/dist/react/index.js.map +7 -0
  133. package/dist/react/jsx-runtime.d.ts +5 -0
  134. package/dist/react/jsx-runtime.js +21 -0
  135. package/dist/react/jsx-runtime.js.map +7 -0
  136. package/dist/react/memo.d.ts +29 -0
  137. package/dist/react/memo.js +47 -0
  138. package/dist/react/memo.js.map +7 -0
  139. package/dist/react/portal.d.ts +1 -0
  140. package/dist/react/portal.js +6 -0
  141. package/dist/react/portal.js.map +7 -0
  142. package/dist/react/shared-internals.d.ts +33 -0
  143. package/dist/react/shared-internals.js +29 -0
  144. package/dist/react/shared-internals.js.map +7 -0
  145. package/dist/react/suspense.d.ts +15 -0
  146. package/dist/react/suspense.js +16 -0
  147. package/dist/react/suspense.js.map +7 -0
  148. package/dist/scheduler/index.d.ts +44 -0
  149. package/dist/scheduler/index.js +109 -0
  150. package/dist/scheduler/index.js.map +7 -0
  151. package/dist/server/bootstrap-script.d.ts +22 -0
  152. package/dist/server/bootstrap-script.js +16 -0
  153. package/dist/server/bootstrap-script.js.map +7 -0
  154. package/dist/server/dispatcher.d.ts +37 -0
  155. package/dist/server/dispatcher.js +140 -0
  156. package/dist/server/dispatcher.js.map +7 -0
  157. package/dist/server/escape.d.ts +7 -0
  158. package/dist/server/escape.js +159 -0
  159. package/dist/server/escape.js.map +7 -0
  160. package/dist/server/index.d.ts +16 -0
  161. package/dist/server/index.js +26 -0
  162. package/dist/server/index.js.map +7 -0
  163. package/dist/server/renderToString.d.ts +7 -0
  164. package/dist/server/renderToString.js +29 -0
  165. package/dist/server/renderToString.js.map +7 -0
  166. package/dist/server/stream.d.ts +40 -0
  167. package/dist/server/stream.js +200 -0
  168. package/dist/server/stream.js.map +7 -0
  169. package/dist/server/walk.d.ts +33 -0
  170. package/dist/server/walk.js +298 -0
  171. package/dist/server/walk.js.map +7 -0
  172. package/dist/vite/index.d.ts +96 -0
  173. package/dist/vite/index.js +217 -0
  174. package/dist/vite/index.js.map +7 -0
  175. package/package.json +83 -0
  176. package/src/core/index.ts +2 -0
  177. package/src/core/internal.ts +107 -0
  178. package/src/core/types.ts +36 -0
  179. package/src/dom/_all.ts +54 -0
  180. package/src/dom/client.ts +4 -0
  181. package/src/dom/dispatcher.ts +318 -0
  182. package/src/dom/dom.ts +316 -0
  183. package/src/dom/event-replay.ts +55 -0
  184. package/src/dom/features/class/full.ts +104 -0
  185. package/src/dom/features/class/index.ts +1 -0
  186. package/src/dom/features/class/stub.ts +65 -0
  187. package/src/dom/features/context/full.ts +57 -0
  188. package/src/dom/features/context/index.ts +1 -0
  189. package/src/dom/features/context/stub.ts +32 -0
  190. package/src/dom/features/forward-ref/full.ts +54 -0
  191. package/src/dom/features/forward-ref/index.ts +1 -0
  192. package/src/dom/features/forward-ref/stub.ts +28 -0
  193. package/src/dom/features/hydration/full.ts +345 -0
  194. package/src/dom/features/hydration/index.ts +1 -0
  195. package/src/dom/features/hydration/stub.ts +72 -0
  196. package/src/dom/features/index.ts +11 -0
  197. package/src/dom/features/lazy/full.ts +98 -0
  198. package/src/dom/features/lazy/index.ts +1 -0
  199. package/src/dom/features/lazy/stub.ts +45 -0
  200. package/src/dom/features/memo/full.ts +74 -0
  201. package/src/dom/features/memo/index.ts +1 -0
  202. package/src/dom/features/memo/stub.ts +42 -0
  203. package/src/dom/features/portal/full.ts +22 -0
  204. package/src/dom/features/portal/index.ts +1 -0
  205. package/src/dom/features/portal/stub.ts +11 -0
  206. package/src/dom/features/suspense/full.ts +191 -0
  207. package/src/dom/features/suspense/index.ts +1 -0
  208. package/src/dom/features/suspense/stub.ts +12 -0
  209. package/src/dom/index.ts +33 -0
  210. package/src/dom/portal.ts +16 -0
  211. package/src/dom/reconcile.ts +1169 -0
  212. package/src/dom/root.ts +107 -0
  213. package/src/dom/test-utils.ts +6 -0
  214. package/src/react/children.ts +85 -0
  215. package/src/react/class.ts +56 -0
  216. package/src/react/context.ts +50 -0
  217. package/src/react/element.ts +80 -0
  218. package/src/react/hooks.ts +190 -0
  219. package/src/react/index.ts +132 -0
  220. package/src/react/jsx-runtime.ts +16 -0
  221. package/src/react/memo.ts +45 -0
  222. package/src/react/portal.ts +1 -0
  223. package/src/react/shared-internals.ts +79 -0
  224. package/src/react/suspense.ts +18 -0
  225. package/src/scheduler/index.ts +124 -0
  226. package/src/server/bootstrap-script.ts +52 -0
  227. package/src/server/dispatcher.ts +158 -0
  228. package/src/server/escape.ts +187 -0
  229. package/src/server/index.ts +26 -0
  230. package/src/server/renderToString.ts +33 -0
  231. package/src/server/stream.ts +302 -0
  232. package/src/server/walk.ts +448 -0
  233. package/src/vite/index.ts +401 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/vite/index.ts"],
4
+ "sourcesContent": ["import { existsSync, readFileSync, realpathSync } from 'node:fs'\nimport { dirname, resolve as resolvePath } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nexport type RedactPreset = 'nano' | 'full'\n\n/**\n * Opt-in feature set. Each flag toggles whether the feature's real\n * implementation ships (`true`) or is swapped with a stub module that\n * degrades gracefully (`false`). Missing keys fall back to the preset's\n * default. Adding a feature to this interface propagates to consumer\n * configs as autocompleted options.\n */\nexport interface RedactFeatures {\n /**\n * `createPortal`. When `false`, portal elements render in place as a\n * Fragment (the `container` prop is ignored). `renderPortal` and its\n * deps are stripped from the bundle.\n */\n portal?: boolean\n /**\n * `createContext` / `useContext` / `<Provider>` / `<Consumer>`. When\n * `false`, Providers render as Fragments (value never propagates),\n * Consumers invoke their function-children with the context's default\n * value, and `useContext` returns the default. Provider-walk logic and\n * `renderProvider` are stripped.\n */\n context?: boolean\n /**\n * `<Suspense>` boundaries + streaming hydration. When `false`, Suspense\n * elements render as Fragments (children mount inline, `fallback` is\n * ignored). Thrown thenables still schedule a re-render on settle, so\n * eventual consistency works \u2014 just no fallback UI during the pending\n * window. Boundary-handler stack and hydration integration are stripped.\n */\n suspense?: boolean\n /**\n * `React.memo`. When `false`, memoized components still render but without\n * the prop-equality gate \u2014 every parent rerender passes through.\n * `shallowEqual` and the force-rerender bypass are stripped.\n */\n memo?: boolean\n /**\n * `React.forwardRef`. When `false`, forwardRef components still render but\n * the ref prop isn't forwarded to the inner function. React 19+ treats\n * refs as normal props on function components anyway, so most apps can\n * drop this. The dispatcher save/restore machinery is stripped.\n */\n forwardRef?: boolean\n /**\n * `React.lazy`. When `false`, lazy elements still resolve if their payload\n * is already available synchronously (e.g. pre-awaited RSC Flight); async\n * resolution throws a clear error. The hydration-deferred-reveal path and\n * Suspense coordination are stripped.\n */\n lazy?: boolean\n /**\n * Class components (`extends Component`). When `false`, class components\n * still render but only honor the core contract: constructor + `render()`\n * + `setState`. Dropped: `contextType`, `getDerivedStateFromProps`,\n * `shouldComponentUpdate`, `componentDidMount`/`Update`/`WillUnmount`,\n * `getDerivedStateFromError`/`componentDidCatch` (error boundaries).\n */\n classComponents?: boolean\n /**\n * SSR hydration (`hydrateRoot`). When `false`, `hydrateRoot` throws\n * (use `createRoot` for SPAs). The HydrationCursor / DOM adoption /\n * streaming-boundary coordination / event-replay / scroll-guard\n * machinery is stripped \u2014 the biggest single chunk of reducible code.\n */\n hydration?: boolean\n}\n\ninterface ResolvedFeatures {\n portal: boolean\n context: boolean\n suspense: boolean\n memo: boolean\n forwardRef: boolean\n lazy: boolean\n classComponents: boolean\n hydration: boolean\n}\n\nconst PRESET_DEFAULTS: Record<RedactPreset, ResolvedFeatures> = {\n // Opt-in: everything off. Turn individual features on via `features`.\n nano: {\n portal: false, context: false, suspense: false, memo: false,\n forwardRef: false, lazy: false, classComponents: false, hydration: false,\n },\n // Opt-out: everything on (drop-in React parity). Turn features off via `features`.\n full: {\n portal: true, context: true, suspense: true, memo: true,\n forwardRef: true, lazy: true, classComponents: true, hydration: true,\n },\n}\n\nfunction resolveFeatures(\n preset: RedactPreset,\n overrides: RedactFeatures,\n): ResolvedFeatures {\n const p = PRESET_DEFAULTS[preset]\n return {\n portal: overrides.portal ?? p.portal,\n context: overrides.context ?? p.context,\n suspense: overrides.suspense ?? p.suspense,\n memo: overrides.memo ?? p.memo,\n forwardRef: overrides.forwardRef ?? p.forwardRef,\n lazy: overrides.lazy ?? p.lazy,\n classComponents: overrides.classComponents ?? p.classComponents,\n hydration: overrides.hydration ?? p.hydration,\n }\n}\n\nexport interface RedactOptions {\n /** Skip aliasing specific specifiers, e.g. if a consumer wants real React somewhere. */\n skip?: ReadonlyArray<string>\n /**\n * Override package resolution root. Defaults to the Vite config root. Useful\n * for monorepos where the plugin lives in a different workspace than the\n * consumer app.\n */\n resolveFrom?: string\n /**\n * Explicit package roots, bypassing node_modules lookup. Keys are package\n * names (e.g. `@tanstack/redact`), values are absolute paths to the package\n * directory. Handy for cross-workspace testing / bring-your-own-build setups.\n */\n packageRoots?: Record<string, string>\n /**\n * Starting point for feature selection. `'full'` (default) turns every\n * feature on \u2014 drop-in React parity, opt-out individual features via\n * `features`. `'nano'` turns everything off \u2014 opt in to what you need.\n */\n preset?: RedactPreset\n /**\n * Per-feature overrides merged on top of the preset's defaults. Enables\n * fine-grained \"preset minus X\" or \"preset plus Y\" configurations.\n */\n features?: RedactFeatures\n}\n\n// Alias map. ORDER MATTERS \u2014 Vite's alias matcher uses first-match against\n// prefix, so more-specific specifiers MUST come before less-specific ones.\n// Without this, `react-dom/server` would prefix-match `react-dom` first and\n// resolve to `@tanstack/redact/dom/server` (wrong) instead of\n// `@tanstack/redact/server`.\n//\n// `use-sync-external-store` aliases are here because its CJS-only React 17\n// compat shim does `var React = require('react')`. That survives Vite's\n// pre-bundling intact and explodes in Cloudflare Workers (no `require`).\n// Modern React has `useSyncExternalStore` built-in, and `@tanstack/redact`\n// additionally exports `useSyncExternalStoreWithSelector` so this alias\n// is safe everywhere.\nconst ALIASES: Record<string, string> = {\n // ---- most-specific first ----\n 'use-sync-external-store/shim/with-selector': '@tanstack/redact',\n 'use-sync-external-store/shim/with-selector.js': '@tanstack/redact',\n 'use-sync-external-store/with-selector': '@tanstack/redact',\n 'use-sync-external-store/with-selector.js': '@tanstack/redact',\n 'use-sync-external-store/shim': '@tanstack/redact',\n 'use-sync-external-store': '@tanstack/redact',\n\n // React drop-in shim targets. Subpaths first.\n 'react/jsx-runtime': '@tanstack/redact/jsx-runtime',\n 'react/jsx-dev-runtime': '@tanstack/redact/jsx-dev-runtime',\n 'react-dom/client': '@tanstack/redact/dom-client',\n 'react-dom/server': '@tanstack/redact/server',\n 'react-dom/test-utils': '@tanstack/redact/dom-test-utils',\n 'react-dom': '@tanstack/redact/dom',\n react: '@tanstack/redact',\n scheduler: '@tanstack/redact/scheduler',\n\n // Self-aliases so Vite resolves `@tanstack/redact/*` imports to the same\n // canonical file path no matter where they originate (worker bundle vs\n // deps_ssr pre-bundle vs source). Without these, Cloudflare's\n // `noExternal: true` worker config inlines one copy while Vite's\n // optimizeDeps pre-bundles another, ending up with two separate\n // ReactSharedInternals instances and a null dispatcher in user hooks.\n // Subpaths first here too.\n '@tanstack/redact/jsx-runtime': '@tanstack/redact/jsx-runtime',\n '@tanstack/redact/jsx-dev-runtime': '@tanstack/redact/jsx-dev-runtime',\n '@tanstack/redact/dom-client': '@tanstack/redact/dom-client',\n '@tanstack/redact/dom-test-utils': '@tanstack/redact/dom-test-utils',\n '@tanstack/redact/server': '@tanstack/redact/server',\n '@tanstack/redact/scheduler': '@tanstack/redact/scheduler',\n '@tanstack/redact/dom': '@tanstack/redact/dom',\n '@tanstack/redact': '@tanstack/redact',\n}\n\nfunction splitSpecifier(specifier: string): { pkg: string; sub: string } {\n if (specifier.startsWith('@')) {\n const slash1 = specifier.indexOf('/')\n const slash2 = specifier.indexOf('/', slash1 + 1)\n if (slash2 < 0) return { pkg: specifier, sub: '' }\n return { pkg: specifier.slice(0, slash2), sub: specifier.slice(slash2 + 1) }\n }\n const slash = specifier.indexOf('/')\n if (slash < 0) return { pkg: specifier, sub: '' }\n return { pkg: specifier.slice(0, slash), sub: specifier.slice(slash + 1) }\n}\n\nfunction findPackageDir(pkg: string, fromDir: string): string | null {\n let dir = fromDir\n while (true) {\n const candidate = resolvePath(dir, 'node_modules', pkg)\n if (existsSync(resolvePath(candidate, 'package.json'))) return candidate\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n}\n\nfunction resolveExport(packageDir: string, sub: string): string | null {\n const pkgJsonPath = resolvePath(packageDir, 'package.json')\n let pkg: any\n try {\n pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))\n } catch {\n return null\n }\n const key = sub ? './' + sub : '.'\n const exp = pkg.exports?.[key]\n // Prefer published `import` (dist/.js) over `source` \u2014 dist is a single\n // transformed bundle so Vite's dep optimizer doesn't thrash on dozens of\n // individual source files. The package keeps cross-subpath imports\n // external, so there's still only one runtime instance.\n const pick = (v: any): string | null => {\n if (typeof v === 'string') return v\n if (v && typeof v === 'object') {\n return pick(v.import ?? v.module ?? v.source ?? v.default ?? null)\n }\n return null\n }\n const target = pick(exp)\n if (target) return resolvePath(packageDir, target)\n if (!sub) {\n const main = pkg.module ?? pkg.main\n if (typeof main === 'string') return resolvePath(packageDir, main)\n }\n return null\n}\n\n// When installed from npm, `@tanstack/redact` is declared as a `dependency`\n// of consumer apps. Under pnpm's strict mode it ends up nested under the\n// plugin's own `.pnpm/@tanstack+redact@.../node_modules/` rather than\n// hoisted to the consumer's root, so a `findPackageDir` walk starting at\n// the Vite project root won't always find it. Search from the plugin's own\n// directory first (which walks into its nested node_modules), then fall\n// back to the consumer root for hoisted installs.\nconst pluginDir = dirname(fileURLToPath(import.meta.url))\n\nfunction resolveSpecifier(\n specifier: string,\n fromDir: string,\n packageRoots: Record<string, string>,\n): string | null {\n const { pkg, sub } = splitSpecifier(specifier)\n const packageDir =\n packageRoots[pkg] ??\n findPackageDir(pkg, pluginDir) ??\n findPackageDir(pkg, fromDir)\n if (!packageDir) return null\n const target = resolveExport(packageDir, sub)\n if (!target) return null\n // Canonicalize through pnpm symlinks. Under strict pnpm, the package may\n // live nested under `.pnpm/@tanstack+redact@.../node_modules/*`, but each\n // of those is itself a symlink to the flat `.pnpm/@tanstack+redact@.../`\n // entry. Vite's `fetchModule` (used by TanStack Start's server-fn\n // compiler) follows the realpath, so the id seen by the capture-transform\n // differs from the nested id we'd return. That leaves the compiler's\n // moduleCache keyed on the realpath while `getModuleInfo` looks up the\n // nested path \u2192 miss \u2192 \"could not load module info\". Returning the\n // canonical realpath here keeps the two sides in agreement.\n try {\n return realpathSync(target)\n } catch {\n return target\n }\n}\n\nexport function redact(options: RedactOptions = {}): any {\n const skip = new Set(options.skip ?? [])\n const entries = Object.entries(ALIASES).filter(([k]) => !skip.has(k))\n const features = resolveFeatures(options.preset ?? 'full', options.features ?? {})\n\n const resolvedMap: Record<string, string> = {}\n let done = false\n\n function resolveAll(root: string): void {\n if (done) return\n const fromDir = options.resolveFrom ?? root\n const packageRoots = options.packageRoots ?? {}\n for (const [from, to] of entries) {\n const resolved = resolveSpecifier(to, fromDir, packageRoots)\n if (resolved) resolvedMap[from] = resolved\n }\n done = true\n }\n\n return {\n name: 'redact',\n enforce: 'pre',\n\n config() {\n const excludeList = entries.map(([k]) => k)\n // Single package \u2014 only one name to dedupe / no-external.\n const noExt = ['@tanstack/redact']\n // Top-level resolve.alias so `react` \u2192 `@tanstack/redact` happens\n // BEFORE any plugin-based resolveId hook fires. The Cloudflare\n // vite-plugin's rolldown worker-runner pre-scans the worker entry's\n // exports and resolves bare specifiers via Vite's alias map directly\n // (not via plugin hooks), so without this it would resolve `react` to\n // the real npm package and try to `require()` it in a Worker (no CJS\n // support). Object form is required \u2014 array form is silently ignored\n // by rolldown's worker-runner.\n const aliasMap = Object.fromEntries(entries.filter(([from, to]) => from !== to))\n // Scope optimizeDeps to client + ssr environments ONLY. Do NOT set a\n // top-level optimizeDeps \u2014 in Vite 6+ that's effectively the client\n // env's default but also seeps into the rsc env's `'use client'`\n // analysis, causing flood warnings like \"inconsistently optimized\".\n // Dedupe `@tanstack/redact` so Vite resolves it to a single instance\n // even when multiple packages (e.g. @tanstack/react-router and user\n // code) drag it into different parts of the module graph.\n const dedupe = noExt\n return {\n resolve: {\n alias: aliasMap,\n dedupe,\n },\n environments: {\n client: {\n optimizeDeps: { exclude: excludeList },\n },\n ssr: {\n optimizeDeps: { exclude: excludeList },\n resolve: { noExternal: noExt },\n },\n },\n ssr: { noExternal: noExt },\n }\n },\n\n configResolved(config: any) {\n resolveAll(config.root)\n // With `packageRoots`, package sources live outside the consumer's Vite\n // project root, so the default server.fs.allow list blocks them. Append\n // to the resolved allow list rather than replacing via `config()`, so we\n // keep Vite's defaults (root + node_modules + client runtime).\n const fsAllow = Object.values(options.packageRoots ?? {})\n if (fsAllow.length && config.server?.fs?.allow) {\n for (const p of fsAllow) {\n if (!config.server.fs.allow.includes(p)) {\n config.server.fs.allow.push(p)\n }\n }\n }\n },\n\n async resolveId(this: any, id: string, importer?: string, opts?: any) {\n // Skip the RSC environment \u2014 it relies on real React internals via\n // @vitejs/plugin-rsc's vendored react-server-dom. Substituting our\n // shim there breaks Flight serialization. Client + SSR envs still swap.\n const envName = this?.environment?.name\n if (envName === 'rsc') return null\n\n // Feature-flag swap: when the reconciler's `features/index` module\n // imports a feature by relative path, redirect to that feature's stub\n // if the flag is off. The stub registers a graceful-degradation\n // matcher (e.g. Portal \u2192 Fragment) so user code keeps working.\n if (importer && /[\\\\/]features[\\\\/]index\\.[jt]sx?$/.test(importer)) {\n const m = id.match(/^\\.\\/([a-z-]+)$/)\n if (m) {\n const name = m[1] as keyof ResolvedFeatures\n if (name in features && !features[name]) {\n const r = await this.resolve(`./${name}/stub`, importer, {\n ...opts,\n skipSelf: true,\n })\n if (r) return r.id\n }\n }\n }\n\n // Hydration swap: hydration isn't self-registering, so it's imported\n // from reconcile.ts, root.ts, and the Suspense/Lazy feature modules.\n // Any specifier ending in `/hydration` that resolves to our feature\n // module gets redirected to the stub when the flag is off.\n if (!features.hydration && importer && /[\\\\/]hydration$/.test(id)) {\n const r = await this.resolve(id, importer, { ...opts, skipSelf: true })\n if (r && /features[\\\\/]hydration[\\\\/]index\\.(ts|js)$/.test(r.id)) {\n return r.id.replace(/index\\.(ts|js)$/, 'stub.$1')\n }\n }\n\n return resolvedMap[id] ?? null\n },\n }\n}\n\nexport default redact\n"],
5
+ "mappings": ";AAAA,SAAS,YAAY,cAAc,oBAAoB;AACvD,SAAS,SAAS,WAAW,mBAAmB;AAChD,SAAS,qBAAqB;AAkF9B,IAAM,kBAA0D;AAAA;AAAA,EAE9D,MAAM;AAAA,IACJ,QAAQ;AAAA,IAAO,SAAS;AAAA,IAAO,UAAU;AAAA,IAAO,MAAM;AAAA,IACtD,YAAY;AAAA,IAAO,MAAM;AAAA,IAAO,iBAAiB;AAAA,IAAO,WAAW;AAAA,EACrE;AAAA;AAAA,EAEA,MAAM;AAAA,IACJ,QAAQ;AAAA,IAAM,SAAS;AAAA,IAAM,UAAU;AAAA,IAAM,MAAM;AAAA,IACnD,YAAY;AAAA,IAAM,MAAM;AAAA,IAAM,iBAAiB;AAAA,IAAM,WAAW;AAAA,EAClE;AACF;AAEA,SAAS,gBACP,QACA,WACkB;AAClB,QAAM,IAAI,gBAAgB,MAAM;AAChC,SAAO;AAAA,IACL,QAAQ,UAAU,UAAU,EAAE;AAAA,IAC9B,SAAS,UAAU,WAAW,EAAE;AAAA,IAChC,UAAU,UAAU,YAAY,EAAE;AAAA,IAClC,MAAM,UAAU,QAAQ,EAAE;AAAA,IAC1B,YAAY,UAAU,cAAc,EAAE;AAAA,IACtC,MAAM,UAAU,QAAQ,EAAE;AAAA,IAC1B,iBAAiB,UAAU,mBAAmB,EAAE;AAAA,IAChD,WAAW,UAAU,aAAa,EAAE;AAAA,EACtC;AACF;AA0CA,IAAM,UAAkC;AAAA;AAAA,EAEtC,8CAA8C;AAAA,EAC9C,iDAAiD;AAAA,EACjD,yCAAyC;AAAA,EACzC,4CAA4C;AAAA,EAC5C,gCAAgC;AAAA,EAChC,2BAA2B;AAAA;AAAA,EAG3B,qBAAqB;AAAA,EACrB,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,aAAa;AAAA,EACb,OAAO;AAAA,EACP,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASX,gCAAgC;AAAA,EAChC,oCAAoC;AAAA,EACpC,+BAA+B;AAAA,EAC/B,mCAAmC;AAAA,EACnC,2BAA2B;AAAA,EAC3B,8BAA8B;AAAA,EAC9B,wBAAwB;AAAA,EACxB,oBAAoB;AACtB;AAEA,SAAS,eAAe,WAAiD;AACvE,MAAI,UAAU,WAAW,GAAG,GAAG;AAC7B,UAAM,SAAS,UAAU,QAAQ,GAAG;AACpC,UAAM,SAAS,UAAU,QAAQ,KAAK,SAAS,CAAC;AAChD,QAAI,SAAS,EAAG,QAAO,EAAE,KAAK,WAAW,KAAK,GAAG;AACjD,WAAO,EAAE,KAAK,UAAU,MAAM,GAAG,MAAM,GAAG,KAAK,UAAU,MAAM,SAAS,CAAC,EAAE;AAAA,EAC7E;AACA,QAAM,QAAQ,UAAU,QAAQ,GAAG;AACnC,MAAI,QAAQ,EAAG,QAAO,EAAE,KAAK,WAAW,KAAK,GAAG;AAChD,SAAO,EAAE,KAAK,UAAU,MAAM,GAAG,KAAK,GAAG,KAAK,UAAU,MAAM,QAAQ,CAAC,EAAE;AAC3E;AAEA,SAAS,eAAe,KAAa,SAAgC;AACnE,MAAI,MAAM;AACV,SAAO,MAAM;AACX,UAAM,YAAY,YAAY,KAAK,gBAAgB,GAAG;AACtD,QAAI,WAAW,YAAY,WAAW,cAAc,CAAC,EAAG,QAAO;AAC/D,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;AAEA,SAAS,cAAc,YAAoB,KAA4B;AACrE,QAAM,cAAc,YAAY,YAAY,cAAc;AAC1D,MAAI;AACJ,MAAI;AACF,UAAM,KAAK,MAAM,aAAa,aAAa,MAAM,CAAC;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,MAAM,MAAM,OAAO,MAAM;AAC/B,QAAM,MAAM,IAAI,UAAU,GAAG;AAK7B,QAAM,OAAO,CAAC,MAA0B;AACtC,QAAI,OAAO,MAAM,SAAU,QAAO;AAClC,QAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,aAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,IAAI;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK,GAAG;AACvB,MAAI,OAAQ,QAAO,YAAY,YAAY,MAAM;AACjD,MAAI,CAAC,KAAK;AACR,UAAM,OAAO,IAAI,UAAU,IAAI;AAC/B,QAAI,OAAO,SAAS,SAAU,QAAO,YAAY,YAAY,IAAI;AAAA,EACnE;AACA,SAAO;AACT;AASA,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,SAAS,iBACP,WACA,SACA,cACe;AACf,QAAM,EAAE,KAAK,IAAI,IAAI,eAAe,SAAS;AAC7C,QAAM,aACJ,aAAa,GAAG,KAChB,eAAe,KAAK,SAAS,KAC7B,eAAe,KAAK,OAAO;AAC7B,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,SAAS,cAAc,YAAY,GAAG;AAC5C,MAAI,CAAC,OAAQ,QAAO;AAUpB,MAAI;AACF,WAAO,aAAa,MAAM;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,OAAO,UAAyB,CAAC,GAAQ;AACvD,QAAM,OAAO,IAAI,IAAI,QAAQ,QAAQ,CAAC,CAAC;AACvC,QAAM,UAAU,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;AACpE,QAAM,WAAW,gBAAgB,QAAQ,UAAU,QAAQ,QAAQ,YAAY,CAAC,CAAC;AAEjF,QAAM,cAAsC,CAAC;AAC7C,MAAI,OAAO;AAEX,WAAS,WAAW,MAAoB;AACtC,QAAI,KAAM;AACV,UAAM,UAAU,QAAQ,eAAe;AACvC,UAAM,eAAe,QAAQ,gBAAgB,CAAC;AAC9C,eAAW,CAAC,MAAM,EAAE,KAAK,SAAS;AAChC,YAAM,WAAW,iBAAiB,IAAI,SAAS,YAAY;AAC3D,UAAI,SAAU,aAAY,IAAI,IAAI;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,SAAS;AACP,YAAM,cAAc,QAAQ,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAE1C,YAAM,QAAQ,CAAC,kBAAkB;AASjC,YAAM,WAAW,OAAO,YAAY,QAAQ,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,SAAS,EAAE,CAAC;AAQ/E,YAAM,SAAS;AACf,aAAO;AAAA,QACL,SAAS;AAAA,UACP,OAAO;AAAA,UACP;AAAA,QACF;AAAA,QACA,cAAc;AAAA,UACZ,QAAQ;AAAA,YACN,cAAc,EAAE,SAAS,YAAY;AAAA,UACvC;AAAA,UACA,KAAK;AAAA,YACH,cAAc,EAAE,SAAS,YAAY;AAAA,YACrC,SAAS,EAAE,YAAY,MAAM;AAAA,UAC/B;AAAA,QACF;AAAA,QACA,KAAK,EAAE,YAAY,MAAM;AAAA,MAC3B;AAAA,IACF;AAAA,IAEA,eAAe,QAAa;AAC1B,iBAAW,OAAO,IAAI;AAKtB,YAAM,UAAU,OAAO,OAAO,QAAQ,gBAAgB,CAAC,CAAC;AACxD,UAAI,QAAQ,UAAU,OAAO,QAAQ,IAAI,OAAO;AAC9C,mBAAW,KAAK,SAAS;AACvB,cAAI,CAAC,OAAO,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG;AACvC,mBAAO,OAAO,GAAG,MAAM,KAAK,CAAC;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,UAAqB,IAAY,UAAmB,MAAY;AAIpE,YAAM,UAAU,MAAM,aAAa;AACnC,UAAI,YAAY,MAAO,QAAO;AAM9B,UAAI,YAAY,oCAAoC,KAAK,QAAQ,GAAG;AAClE,cAAM,IAAI,GAAG,MAAM,iBAAiB;AACpC,YAAI,GAAG;AACL,gBAAM,OAAO,EAAE,CAAC;AAChB,cAAI,QAAQ,YAAY,CAAC,SAAS,IAAI,GAAG;AACvC,kBAAM,IAAI,MAAM,KAAK,QAAQ,KAAK,IAAI,SAAS,UAAU;AAAA,cACvD,GAAG;AAAA,cACH,UAAU;AAAA,YACZ,CAAC;AACD,gBAAI,EAAG,QAAO,EAAE;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAMA,UAAI,CAAC,SAAS,aAAa,YAAY,kBAAkB,KAAK,EAAE,GAAG;AACjE,cAAM,IAAI,MAAM,KAAK,QAAQ,IAAI,UAAU,EAAE,GAAG,MAAM,UAAU,KAAK,CAAC;AACtE,YAAI,KAAK,6CAA6C,KAAK,EAAE,EAAE,GAAG;AAChE,iBAAO,EAAE,GAAG,QAAQ,mBAAmB,SAAS;AAAA,QAClD;AAAA,MACF;AAEA,aAAO,YAAY,EAAE,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;AAEA,IAAO,eAAQ;",
6
+ "names": []
7
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@tanstack/redact",
3
+ "version": "0.0.1",
4
+ "description": "React, redacted. A minimal React-API-compatible drop-in replacement.",
5
+ "type": "module",
6
+ "main": "./dist/react/index.js",
7
+ "module": "./dist/react/index.js",
8
+ "types": "./dist/react/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/react/index.d.ts",
12
+ "import": "./dist/react/index.js"
13
+ },
14
+ "./jsx-runtime": {
15
+ "types": "./dist/react/jsx-runtime.d.ts",
16
+ "import": "./dist/react/jsx-runtime.js"
17
+ },
18
+ "./jsx-dev-runtime": {
19
+ "types": "./dist/react/jsx-runtime.d.ts",
20
+ "import": "./dist/react/jsx-runtime.js"
21
+ },
22
+ "./dom": {
23
+ "types": "./dist/dom/index.d.ts",
24
+ "import": "./dist/dom/index.js"
25
+ },
26
+ "./dom-client": {
27
+ "types": "./dist/dom/client.d.ts",
28
+ "import": "./dist/dom/client.js"
29
+ },
30
+ "./dom-test-utils": {
31
+ "types": "./dist/dom/test-utils.d.ts",
32
+ "import": "./dist/dom/test-utils.js"
33
+ },
34
+ "./server": {
35
+ "types": "./dist/server/index.d.ts",
36
+ "import": "./dist/server/index.js"
37
+ },
38
+ "./scheduler": {
39
+ "types": "./dist/scheduler/index.d.ts",
40
+ "import": "./dist/scheduler/index.js"
41
+ },
42
+ "./vite": {
43
+ "types": "./dist/vite/index.d.ts",
44
+ "import": "./dist/vite/index.js"
45
+ },
46
+ "./features/*": {
47
+ "types": "./dist/dom/features/*.d.ts",
48
+ "import": "./dist/dom/features/*.js"
49
+ },
50
+ "./_all": {
51
+ "types": "./dist/dom/_all.d.ts",
52
+ "import": "./dist/dom/_all.js"
53
+ }
54
+ },
55
+ "files": [
56
+ "dist",
57
+ "src"
58
+ ],
59
+ "sideEffects": [
60
+ "./src/dom/features/**",
61
+ "./dist/dom/features/**",
62
+ "./src/dom/index.ts",
63
+ "./src/dom/client.ts",
64
+ "./src/dom/_all.ts",
65
+ "./dist/dom/index.js",
66
+ "./dist/dom/client.js",
67
+ "./dist/dom/_all.js"
68
+ ],
69
+ "peerDependencies": {
70
+ "vite": ">=5"
71
+ },
72
+ "peerDependenciesMeta": {
73
+ "vite": {
74
+ "optional": true
75
+ }
76
+ },
77
+ "scripts": {
78
+ "build": "echo done-by-root-build"
79
+ },
80
+ "publishConfig": {
81
+ "access": "public"
82
+ }
83
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types'
2
+ export * from './internal'
@@ -0,0 +1,107 @@
1
+ import type { ReactElement, ReactNode, Ref } from './types'
2
+
3
+ export const enum FiberTag {
4
+ Host = 0,
5
+ Text = 1,
6
+ Function = 2,
7
+ Class = 3,
8
+ Fragment = 4,
9
+ Portal = 5,
10
+ Provider = 6,
11
+ Consumer = 7,
12
+ ForwardRef = 8,
13
+ Memo = 9,
14
+ Lazy = 10,
15
+ Suspense = 11,
16
+ Root = 12,
17
+ }
18
+
19
+ export const enum FiberFlag {
20
+ None = 0,
21
+ Placement = 1 << 0,
22
+ Update = 1 << 1,
23
+ Deletion = 1 << 2,
24
+ Ref = 1 << 3,
25
+ Effect = 1 << 4,
26
+ LayoutEffect = 1 << 5,
27
+ ContentReset = 1 << 6,
28
+ DidCapture = 1 << 7,
29
+ }
30
+
31
+ export interface Hook {
32
+ state: any
33
+ queue: any
34
+ deps: any
35
+ cleanup: any
36
+ next: Hook | null
37
+ }
38
+
39
+ export interface Effect {
40
+ tag: 'effect' | 'layout' | 'insertion'
41
+ create: () => any
42
+ destroy: (() => void) | void
43
+ deps: ReadonlyArray<unknown> | undefined
44
+ }
45
+
46
+ export interface Fiber {
47
+ tag: FiberTag
48
+ type: any
49
+ key: string | null
50
+ ref: Ref<any> | null
51
+ pendingProps: any
52
+ memoizedProps: any
53
+ memoizedState: any
54
+ stateNode: any
55
+ dom: Node | null
56
+ parent: Fiber | null
57
+ child: Fiber | null
58
+ sibling: Fiber | null
59
+ hooks: Hook | null
60
+ effects: Effect[] | null
61
+ layoutEffects: Effect[] | null
62
+ cleanups: Array<() => void> | null
63
+ flags: FiberFlag
64
+ dirty: boolean
65
+ unmounted: boolean
66
+ root: FiberRoot | null
67
+ }
68
+
69
+ export interface FiberRoot {
70
+ container: Element | DocumentFragment
71
+ current: Fiber
72
+ pending: Set<Fiber>
73
+ scheduled: boolean
74
+ onRecoverableError?: (err: unknown) => void
75
+ onCaughtError?: (err: unknown) => void
76
+ onUncaughtError?: (err: unknown) => void
77
+ identifierPrefix: string
78
+ hydrating: boolean
79
+ }
80
+
81
+ export function createFiber(tag: FiberTag, type: any, key: string | null): Fiber {
82
+ return {
83
+ tag,
84
+ type,
85
+ key,
86
+ ref: null,
87
+ pendingProps: null,
88
+ memoizedProps: null,
89
+ memoizedState: null,
90
+ stateNode: null,
91
+ dom: null,
92
+ parent: null,
93
+ child: null,
94
+ sibling: null,
95
+ hooks: null,
96
+ effects: null,
97
+ layoutEffects: null,
98
+ cleanups: null,
99
+ flags: FiberFlag.None,
100
+ dirty: false,
101
+ unmounted: false,
102
+ root: null,
103
+ }
104
+ }
105
+
106
+ export type ChildNode = ReactElement | string | number | boolean | null | undefined | ChildNode[]
107
+ export type { ReactElement, ReactNode }
@@ -0,0 +1,36 @@
1
+ // React 19+ uses the transitional element symbol. When our shim's elements
2
+ // are passed to react-dom (e.g. Start routing through real react-dom/server
3
+ // somewhere), they must carry the symbol that React's isValidElement checks.
4
+ export const REACT_ELEMENT_TYPE = Symbol.for('react.transitional.element')
5
+ export const REACT_LEGACY_ELEMENT_TYPE = Symbol.for('react.element')
6
+ export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment')
7
+
8
+ export type Key = string | number | null | undefined
9
+
10
+ export interface ReactElement<P = any, T = any> {
11
+ $$typeof: typeof REACT_ELEMENT_TYPE
12
+ type: T
13
+ key: string | null
14
+ ref: any
15
+ props: P
16
+ }
17
+
18
+ export type ReactNode =
19
+ | ReactElement
20
+ | string
21
+ | number
22
+ | boolean
23
+ | null
24
+ | undefined
25
+ | Iterable<ReactNode>
26
+
27
+ export type RefObject<T> = { current: T | null }
28
+ export type RefCallback<T> = (instance: T | null) => void | (() => void)
29
+ export type Ref<T> = RefObject<T> | RefCallback<T> | null
30
+
31
+ export type Dispatch<A> = (value: A) => void
32
+ export type SetStateAction<S> = S | ((prev: S) => S)
33
+
34
+ export type EffectCallback = () => void | (() => void)
35
+ export type DependencyList = ReadonlyArray<unknown>
36
+
@@ -0,0 +1,54 @@
1
+ // Unified entry exposing both top-level public API (createRoot, hydrateRoot,
2
+ // createPortal, …) and the registration primitives that custom features and
3
+ // custom bundler plugins need to hook into the reconciler. Reachable as
4
+ // `@tanstack/redact/_all` via the package's subpath exports. Single-instance
5
+ // state for things like the hydration CLAIMED WeakSet, scheduler queue,
6
+ // dispatcher H slot, etc. is preserved because every public entry funnels
7
+ // through this module.
8
+
9
+ // Side-effect import: registers all opt-in features (renderers, type matchers,
10
+ // element markers) with the reconciler. A vite plugin may alias individual
11
+ // feature modules to ./features/<name>/stub to strip them from the bundle.
12
+ import './features'
13
+
14
+ // Top-level public API (mirrors the surface of /, /dom, /dom-client).
15
+ export { flushSync, batchedUpdates as unstable_batchedUpdates } from './root'
16
+ export { createRoot, hydrateRoot } from './root'
17
+ export type { Root, RootOptions } from './root'
18
+ export { createPortal } from './portal'
19
+ export { act } from './test-utils'
20
+
21
+ // Resource hints — stubs
22
+ export function preconnect(_href: string, _opts?: any): void {}
23
+ export function prefetchDNS(_href: string): void {}
24
+ export function preload(_href: string, _opts?: any): void {}
25
+ export function preinit(_href: string, _opts?: any): void {}
26
+ export function preloadModule(_href: string, _opts?: any): void {}
27
+ export function preinitModule(_href: string, _opts?: any): void {}
28
+
29
+ export const version = '19.2.3'
30
+
31
+ // Registration primitives — needed by custom features
32
+ // (`registerRenderer`, `registerTypeMatcher`, `registerElementMarker`) and by
33
+ // anything that wants to override a cross-cutting concern like Suspense's
34
+ // `handleSuspended` or Context's `readContext` (`installCapability`). The
35
+ // built-in features in `./features/*/{full,stub}.ts` use these via relative
36
+ // imports; external authors reach them through this entry. Order-of-
37
+ // registration matters: later calls overwrite earlier ones.
38
+ export {
39
+ registerRenderer,
40
+ registerTypeMatcher,
41
+ registerElementMarker,
42
+ installCapability,
43
+ reconcileChildren,
44
+ childrenToArray,
45
+ } from './reconcile'
46
+ export type {
47
+ RenderFn,
48
+ TypeMatcher,
49
+ Capabilities,
50
+ } from './reconcile'
51
+
52
+ // Core types most custom features need.
53
+ export { FiberTag } from '../core'
54
+ export type { Fiber, FiberRoot, ReactNode, ReactElement } from '../core'
@@ -0,0 +1,4 @@
1
+ import './features'
2
+
3
+ export { createRoot, hydrateRoot } from './root'
4
+ export type { Root, RootOptions } from './root'
@@ -0,0 +1,318 @@
1
+ import type { Hook, Fiber, FiberRoot, Effect } from '../core'
2
+ import { ReactSharedInternals, REACT_CONTEXT_TYPE } from '../react'
3
+ import { scheduleUpdate, enqueueEffect, readContext } from './reconcile'
4
+
5
+ function getCurrentFiber(): Fiber {
6
+ const f = ReactSharedInternals.currentFiber
7
+ if (!f) throw new Error('Hook called outside a function component render.')
8
+ return f
9
+ }
10
+
11
+ function nextHook(): Hook {
12
+ const fiber = getCurrentFiber()
13
+ const idx = ReactSharedInternals.hookIndex++
14
+
15
+ let prev = ReactSharedInternals.currentHook
16
+
17
+ if (idx === 0) {
18
+ if (fiber.hooks) {
19
+ ReactSharedInternals.currentHook = fiber.hooks
20
+ return fiber.hooks
21
+ }
22
+ const h: Hook = { state: undefined, queue: undefined, deps: undefined, cleanup: undefined, next: null }
23
+ fiber.hooks = h
24
+ ReactSharedInternals.currentHook = h
25
+ return h
26
+ }
27
+
28
+ if (prev && prev.next) {
29
+ ReactSharedInternals.currentHook = prev.next
30
+ return prev.next
31
+ }
32
+
33
+ const h: Hook = { state: undefined, queue: undefined, deps: undefined, cleanup: undefined, next: null }
34
+ if (prev) prev.next = h
35
+ else fiber.hooks = h
36
+ ReactSharedInternals.currentHook = h
37
+ return h
38
+ }
39
+
40
+ function depsEqual(
41
+ a: ReadonlyArray<unknown> | undefined,
42
+ b: ReadonlyArray<unknown> | undefined,
43
+ ): boolean {
44
+ if (a === b) return true
45
+ if (!a || !b) return false
46
+ if (a.length !== b.length) return false
47
+ for (let i = 0; i < a.length; i++) {
48
+ if (!Object.is(a[i], b[i])) return false
49
+ }
50
+ return true
51
+ }
52
+
53
+ export function makeDispatcher() {
54
+ return {
55
+ useState<S>(initial: S | (() => S)) {
56
+ return this.useReducer<S, S | ((p: S) => S)>(
57
+ basicReducer as any,
58
+ typeof initial === 'function' ? (initial as () => S)() : initial,
59
+ )
60
+ },
61
+
62
+ useReducer<S, A>(reducer: (s: S, a: A) => S, initialArg: any, init?: (a: any) => S) {
63
+ const hook = nextHook()
64
+ const fiber = getCurrentFiber()
65
+ if (hook.queue === undefined) {
66
+ hook.state = init ? init(initialArg) : initialArg
67
+ const queue: any = { reducer }
68
+ const dispatch = (action: A) => {
69
+ const currentState = hook.state as S
70
+ const next = queue.reducer(currentState, action)
71
+ if (!Object.is(next, currentState)) {
72
+ hook.state = next
73
+ scheduleUpdate(fiber)
74
+ }
75
+ }
76
+ queue.dispatch = dispatch
77
+ hook.queue = queue
78
+ } else {
79
+ hook.queue.reducer = reducer
80
+ }
81
+ return [hook.state, hook.queue.dispatch] as [S, (a: A) => void]
82
+ },
83
+
84
+ useEffect(create: () => any, deps?: ReadonlyArray<unknown>) {
85
+ const hook = nextHook()
86
+ const fiber = getCurrentFiber()
87
+ const prevDeps = hook.deps
88
+ if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return
89
+ hook.deps = deps
90
+ const effect: Effect = {
91
+ tag: 'effect',
92
+ create: () => {
93
+ // Run the prior cleanup INSIDE the effect run, not during the
94
+ // dispatch/render phase. If render A → B → C all happen back-to-
95
+ // back before the passive microtask drains, dispatch-time cleanup
96
+ // only fires once (between A→B) and effects B + C both run fresh,
97
+ // leaving two side-effects (e.g. two plot SVGs) in the DOM. Doing
98
+ // it here, at effect-run time, means every new create first tears
99
+ // down whatever cleanup is currently live on the hook.
100
+ if (hook.cleanup) {
101
+ try { hook.cleanup() } catch {}
102
+ // The prior cleanup was also pushed onto fiber.cleanups; remove
103
+ // it so unmount doesn't double-call it.
104
+ if (fiber.cleanups) {
105
+ const i = fiber.cleanups.indexOf(hook.cleanup)
106
+ if (i >= 0) fiber.cleanups.splice(i, 1)
107
+ }
108
+ hook.cleanup = null
109
+ }
110
+ const c = create()
111
+ hook.cleanup = typeof c === 'function' ? c : null
112
+ return hook.cleanup
113
+ },
114
+ destroy: undefined,
115
+ deps,
116
+ }
117
+ enqueueEffect(fiber, effect)
118
+ },
119
+
120
+ useLayoutEffect(create: () => any, deps?: ReadonlyArray<unknown>) {
121
+ const hook = nextHook()
122
+ const fiber = getCurrentFiber()
123
+ const prevDeps = hook.deps
124
+ if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return
125
+ hook.deps = deps
126
+ const effect: Effect = {
127
+ tag: 'layout',
128
+ create: () => {
129
+ // Mirror useEffect: tear down the prior cleanup at run time so
130
+ // coalesced renders don't leak side-effects.
131
+ if (hook.cleanup) {
132
+ try { hook.cleanup() } catch {}
133
+ if (fiber.cleanups) {
134
+ const i = fiber.cleanups.indexOf(hook.cleanup)
135
+ if (i >= 0) fiber.cleanups.splice(i, 1)
136
+ }
137
+ hook.cleanup = null
138
+ }
139
+ const c = create()
140
+ hook.cleanup = typeof c === 'function' ? c : null
141
+ return hook.cleanup
142
+ },
143
+ destroy: undefined,
144
+ deps,
145
+ }
146
+ enqueueEffect(fiber, effect)
147
+ },
148
+
149
+ useInsertionEffect(create: () => any, deps?: ReadonlyArray<unknown>) {
150
+ return this.useLayoutEffect(create, deps)
151
+ },
152
+
153
+ useRef<T>(initial: T) {
154
+ const hook = nextHook()
155
+ if (hook.state === undefined) hook.state = { current: initial }
156
+ return hook.state as { current: T }
157
+ },
158
+
159
+ useMemo<T>(factory: () => T, deps?: ReadonlyArray<unknown>) {
160
+ const hook = nextHook()
161
+ if (hook.deps !== undefined && depsEqual(hook.deps, deps)) {
162
+ return hook.state as T
163
+ }
164
+ const value = factory()
165
+ hook.state = value
166
+ hook.deps = deps
167
+ return value
168
+ },
169
+
170
+ useCallback<T extends Function>(fn: T, deps?: ReadonlyArray<unknown>): T {
171
+ return this.useMemo(() => fn, deps) as T
172
+ },
173
+
174
+ useContext<T>(ctx: any): T {
175
+ const fiber = getCurrentFiber()
176
+ return readContext(fiber, ctx)
177
+ },
178
+
179
+ useImperativeHandle<T>(ref: any, factory: () => T, deps?: ReadonlyArray<unknown>) {
180
+ const hook = nextHook()
181
+ if (hook.deps !== undefined && depsEqual(hook.deps, deps)) return
182
+ hook.deps = deps
183
+ const value = factory()
184
+ if (ref) {
185
+ if (typeof ref === 'function') ref(value)
186
+ else ref.current = value
187
+ }
188
+ },
189
+
190
+ useDebugValue<T>(_value: T, _formatter?: (v: T) => any): void {
191
+ // noop
192
+ },
193
+
194
+ useId(): string {
195
+ const hook = nextHook()
196
+ if (hook.state === undefined) {
197
+ const fiber = getCurrentFiber()
198
+ const root = findRootFromFiber(fiber)
199
+ hook.state = (root?.identifierPrefix ?? ':r') + (idCounter++).toString(36)
200
+ }
201
+ return hook.state as string
202
+ },
203
+
204
+ useTransition(): [boolean, (fn: () => void) => void] {
205
+ return [false, (fn: () => void) => fn()]
206
+ },
207
+
208
+ useDeferredValue<T>(v: T): T {
209
+ return v
210
+ },
211
+
212
+ useSyncExternalStore<T>(
213
+ subscribe: (cb: () => void) => () => void,
214
+ getSnapshot: () => T,
215
+ getServerSnapshot?: () => T,
216
+ ): T {
217
+ const fiber = getCurrentFiber()
218
+ const hook = nextHook()
219
+
220
+ // During hydration, use the server snapshot (if provided) so the tree
221
+ // matches the SSR output. Components like TanStack Router's ClientOnly
222
+ // rely on this: they render `false` on server, `true` on client — and
223
+ // if we return `true` during hydration, client and server diverge and
224
+ // the tree mounts fresh next to the SSR fallback DOM.
225
+ const root = fiber.root ?? findRootFromFiber(fiber)
226
+ const isHydrating = Boolean(root?.hydrating)
227
+ const value =
228
+ isHydrating && getServerSnapshot ? getServerSnapshot() : getSnapshot()
229
+ hook.state = value
230
+
231
+ if (hook.cleanup == null) {
232
+ const forceUpdate = () => {
233
+ let next: T
234
+ try {
235
+ next = getSnapshot()
236
+ } catch {
237
+ scheduleUpdate(fiber)
238
+ return
239
+ }
240
+ if (!Object.is(hook.state, next)) {
241
+ hook.state = next
242
+ scheduleUpdate(fiber)
243
+ }
244
+ }
245
+ const unsubscribe = subscribe(forceUpdate)
246
+ hook.cleanup = unsubscribe
247
+ // Register with fiber so unmountFiber runs it. Without this, the store
248
+ // keeps holding forceUpdate and every store update schedules an already-
249
+ // unmounted fiber — its rerender walks stale .parent pointers and mounts
250
+ // zombie DOM into the old parent.
251
+ if (typeof unsubscribe === 'function') {
252
+ fiber.cleanups ||= []
253
+ fiber.cleanups.push(unsubscribe)
254
+ }
255
+
256
+ // If we served the server snapshot, run a post-hydration check so
257
+ // components like `useHydrated()` flip from false → true after the
258
+ // initial render commits. Queued late so hydration finishes first.
259
+ if (isHydrating && getServerSnapshot) {
260
+ queueMicrotask(() => queueMicrotask(forceUpdate))
261
+ }
262
+ }
263
+ return value
264
+ },
265
+
266
+ use<T>(resource: any): T {
267
+ if (resource == null) throw new Error('use() received null or undefined')
268
+ if (resource.$$typeof === REACT_CONTEXT_TYPE) {
269
+ return readContext(getCurrentFiber(), resource)
270
+ }
271
+ if (typeof resource.then === 'function') {
272
+ const thenable = resource
273
+ switch (thenable.status) {
274
+ case 'fulfilled':
275
+ return thenable.value
276
+ case 'rejected':
277
+ throw thenable.reason
278
+ default: {
279
+ if (thenable.status === undefined) {
280
+ thenable.status = 'pending'
281
+ thenable.then(
282
+ (v: any) => {
283
+ if (thenable.status === 'pending') {
284
+ thenable.status = 'fulfilled'
285
+ thenable.value = v
286
+ }
287
+ },
288
+ (e: any) => {
289
+ if (thenable.status === 'pending') {
290
+ thenable.status = 'rejected'
291
+ thenable.reason = e
292
+ }
293
+ },
294
+ )
295
+ }
296
+ throw thenable
297
+ }
298
+ }
299
+ }
300
+ throw new Error('use() expected a Promise or Context')
301
+ },
302
+ }
303
+ }
304
+
305
+ function basicReducer<S>(state: S, action: S | ((p: S) => S)): S {
306
+ return typeof action === 'function' ? (action as (p: S) => S)(state) : action
307
+ }
308
+
309
+ let idCounter = 0
310
+
311
+ function findRootFromFiber(fiber: Fiber): FiberRoot | null {
312
+ let f: Fiber | null = fiber
313
+ while (f) {
314
+ if (f.root) return f.root
315
+ f = f.parent
316
+ }
317
+ return null
318
+ }