tungsten 0.7.0 → 0.7.2

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 (687) hide show
  1. package/AGENTS.md +1246 -0
  2. package/dist/.build-complete +1 -1
  3. package/dist/FrameworkConfig.d.ts +2 -0
  4. package/dist/Instance/Instance.js +6 -33
  5. package/dist/Instance/InstanceConfig.d.ts +1 -0
  6. package/dist/Servers/DevServer.js +10 -1
  7. package/dist/Servers/ProdServer.d.ts +1 -0
  8. package/dist/Servers/ProdServer.js +18 -1
  9. package/dist/Servers/routing.js +9 -5
  10. package/dist/Servers/stubInstance.d.ts +8 -0
  11. package/dist/Servers/stubInstance.js +40 -0
  12. package/dist/build/createInstanceConfigFile.js +1 -1
  13. package/dist/componentHelpers.d.ts +2 -1
  14. package/dist/componentHelpers.js +3 -0
  15. package/dist/frameworkConfigSchema.d.ts +2 -0
  16. package/dist/frameworkConfigSchema.js +4 -1
  17. package/dist/hydrate/hydrate.js +31 -10
  18. package/dist/hydrate/stubRouting.d.ts +8 -0
  19. package/dist/hydrate/stubRouting.js +25 -0
  20. package/dist/makeFinalConfig.js +2 -0
  21. package/dist/makeInstanceConfigFromFrameworkConfig.js +1 -0
  22. package/dist/pipeline/PipelineRequestHandler.js +9 -1
  23. package/dist/pipeline/hydratePipeline.js +28 -9
  24. package/dist/pipeline/pipeline.d.ts +35 -33
  25. package/dist/pipeline/pipeline.js +10 -5
  26. package/dist/pipeline/pipelineDefinition.d.ts +20 -9
  27. package/dist/pipeline/runPipeline.d.ts +6 -8
  28. package/dist/pipeline/runPipeline.js +8 -8
  29. package/dist/pipeline/runSplitPipeline.d.ts +3 -3
  30. package/dist/pipeline/runSplitPipeline.js +12 -8
  31. package/dist/pipeline/splitPipeline.d.ts +8 -8
  32. package/dist/render/contentTreeToRenderingTree.js +4 -1
  33. package/dist/routing/MatchedRouteInfo.d.ts +2 -0
  34. package/dist/routing/MatchedRouteInfo.js +1 -1
  35. package/dist/routing/RouteContentFunction.d.ts +1 -1
  36. package/dist/routing/RouteTypes.d.ts +1 -1
  37. package/dist/routing/Routes.d.ts +9 -4
  38. package/dist/routing/builder.d.ts +2 -1
  39. package/dist/routing/builder.js +6 -0
  40. package/dist/routing/redirectOf.d.ts +11 -0
  41. package/dist/routing/redirectOf.js +32 -0
  42. package/dist/routing/routesArray.d.ts +2 -2
  43. package/dist/routing/routingBuilder.js +0 -1
  44. package/dist/testing/contentToString.d.ts +13 -0
  45. package/dist/testing/contentToString.js +105 -0
  46. package/dist/testing/display.d.ts +2 -0
  47. package/dist/testing/display.js +37 -0
  48. package/dist/testing/ensure.d.ts +52 -41
  49. package/dist/testing/ensure.js +204 -62
  50. package/dist/testing/host.d.ts +25 -25
  51. package/dist/testing/host.js +1 -3
  52. package/dist/testing/hydrateTestApp.js +4 -3
  53. package/dist/testing/index.d.ts +2 -3
  54. package/dist/testing/index.js +1 -2
  55. package/dist/testing/pipeline/createTestPacketApp.d.ts +30 -0
  56. package/dist/testing/pipeline/createTestPacketApp.js +45 -0
  57. package/dist/testing/pipeline/createTestPipelineApp.d.ts +70 -0
  58. package/dist/testing/pipeline/createTestPipelineApp.js +90 -0
  59. package/dist/testing/pipeline/hostPipeline.d.ts +25 -0
  60. package/dist/testing/pipeline/hostPipeline.js +18 -0
  61. package/dist/testing/{hydrateTestPipelineApp.d.ts → pipeline/hydrateTestPipelineApp.d.ts} +6 -6
  62. package/dist/testing/{hydrateTestPipelineApp.js → pipeline/hydrateTestPipelineApp.js} +22 -19
  63. package/dist/testing/pipeline/index.d.ts +5 -0
  64. package/dist/testing/pipeline/index.js +4 -0
  65. package/dist/testing/pipeline/pipelineTestApp.d.ts +34 -0
  66. package/dist/testing/pipeline/pipelineTestApp.js +100 -0
  67. package/dist/testing/pipeline/testPipelineRoute.d.ts +2 -0
  68. package/dist/testing/pipeline/testPipelineRoute.js +1 -0
  69. package/dist/testing/resolveContent.d.ts +2 -0
  70. package/dist/testing/resolveContent.js +9 -1
  71. package/package.json +10 -5
  72. package/vendor/css-select.js +3383 -0
  73. package/vendor/manifest.json +4 -3
  74. package/dist/AnyComponent.d.ts.map +0 -1
  75. package/dist/AnyComponentType.d.ts.map +0 -1
  76. package/dist/AppResponse.d.ts.map +0 -1
  77. package/dist/AppTypeExtraction.d.ts.map +0 -1
  78. package/dist/AssignedStyle.d.ts.map +0 -1
  79. package/dist/Brands.d.ts.map +0 -1
  80. package/dist/CjsTypes.d.ts.map +0 -1
  81. package/dist/ComponentInstance.d.ts.map +0 -1
  82. package/dist/ComponentRenderInfo.type.d.ts.map +0 -1
  83. package/dist/ComponentStyle.d.ts.map +0 -1
  84. package/dist/Components.d.ts.map +0 -1
  85. package/dist/ContentFunction.d.ts.map +0 -1
  86. package/dist/ContentNode.d.ts.map +0 -1
  87. package/dist/ContentNodeNew.d.ts.map +0 -1
  88. package/dist/CssNames.d.ts.map +0 -1
  89. package/dist/DomNodes.d.ts.map +0 -1
  90. package/dist/DomPointer.d.ts.map +0 -1
  91. package/dist/DomRegion.d.ts.map +0 -1
  92. package/dist/ExtendableHTMLElement.d.ts.map +0 -1
  93. package/dist/Framework.d.ts.map +0 -1
  94. package/dist/FrameworkConfig.d.ts.map +0 -1
  95. package/dist/FunctionComponent.d.ts.map +0 -1
  96. package/dist/FunctionComponentStandIn.d.ts.map +0 -1
  97. package/dist/GenericObject.type.d.ts.map +0 -1
  98. package/dist/HtmlElementAttributes.d.ts.map +0 -1
  99. package/dist/Instance/ClientRuntimeContext.d.ts.map +0 -1
  100. package/dist/Instance/Instance.d.ts.map +0 -1
  101. package/dist/Instance/InstanceConfig.d.ts.map +0 -1
  102. package/dist/IsDefined.d.ts.map +0 -1
  103. package/dist/IsIndexableObject.d.ts.map +0 -1
  104. package/dist/IsNumber.d.ts.map +0 -1
  105. package/dist/IsObject.d.ts.map +0 -1
  106. package/dist/Json.type.d.ts.map +0 -1
  107. package/dist/JsxContent.d.ts.map +0 -1
  108. package/dist/Key.d.ts.map +0 -1
  109. package/dist/KeyAttr.d.ts.map +0 -1
  110. package/dist/MapObject.d.ts.map +0 -1
  111. package/dist/Mode.d.ts.map +0 -1
  112. package/dist/Module.d.ts.map +0 -1
  113. package/dist/NoDeproxy.d.ts.map +0 -1
  114. package/dist/PopMessage.type.d.ts.map +0 -1
  115. package/dist/ProcessedStylesObject.d.ts.map +0 -1
  116. package/dist/RenderFunction.d.ts.map +0 -1
  117. package/dist/RenderedContentNode.d.ts.map +0 -1
  118. package/dist/RenderingNode.d.ts.map +0 -1
  119. package/dist/ReqRes.d.ts.map +0 -1
  120. package/dist/Sass.d.ts.map +0 -1
  121. package/dist/Script.d.ts.map +0 -1
  122. package/dist/ServerRpcFunction.type.d.ts.map +0 -1
  123. package/dist/Servers/DevServer.d.ts.map +0 -1
  124. package/dist/Servers/ProdServer.d.ts.map +0 -1
  125. package/dist/Servers/ServerMiddleware.d.ts.map +0 -1
  126. package/dist/Servers/StaticSiteServer.d.ts.map +0 -1
  127. package/dist/Servers/TungstenServer.d.ts.map +0 -1
  128. package/dist/Servers/cacheVersioned.d.ts.map +0 -1
  129. package/dist/Servers/devErrorPageAutoRetry.d.ts.map +0 -1
  130. package/dist/Servers/initStores.d.ts.map +0 -1
  131. package/dist/Servers/listenVerified.d.ts.map +0 -1
  132. package/dist/Servers/loadEnvsDev.d.ts.map +0 -1
  133. package/dist/Servers/portError.d.ts.map +0 -1
  134. package/dist/Servers/resetRegistrations.d.ts.map +0 -1
  135. package/dist/Servers/routing.d.ts.map +0 -1
  136. package/dist/Servers/serveStatic.d.ts.map +0 -1
  137. package/dist/Servers/tungstenCorePrivateEnvSchema.d.ts.map +0 -1
  138. package/dist/Servers/tungstenCorePublicEnvSchema.d.ts.map +0 -1
  139. package/dist/Store/BaseStoreSchema.d.ts.map +0 -1
  140. package/dist/Store/InternalStoreSchema.d.ts.map +0 -1
  141. package/dist/Store/ServerDataLoading.d.ts.map +0 -1
  142. package/dist/Store/Store.d.ts.map +0 -1
  143. package/dist/Store/StoreProxyInterface.d.ts.map +0 -1
  144. package/dist/Store/StoreSchemaClass.d.ts.map +0 -1
  145. package/dist/Store/StoreSubscriptions.types.d.ts.map +0 -1
  146. package/dist/Store/TypeSafeStore.d.ts.map +0 -1
  147. package/dist/Store/createStores.d.ts.map +0 -1
  148. package/dist/Store/exportStore.d.ts.map +0 -1
  149. package/dist/Store/getInternalStore.d.ts.map +0 -1
  150. package/dist/Store/getStore.d.ts.map +0 -1
  151. package/dist/Store/makeStore.d.ts.map +0 -1
  152. package/dist/Store/registerStore.d.ts.map +0 -1
  153. package/dist/Store/storeCreators.d.ts.map +0 -1
  154. package/dist/StoreBindVariable.d.ts.map +0 -1
  155. package/dist/StyleProcessor.d.ts.map +0 -1
  156. package/dist/TElement.d.ts.map +0 -1
  157. package/dist/TungstenApp.d.ts.map +0 -1
  158. package/dist/TungstenAppOld.d.ts.map +0 -1
  159. package/dist/TungstenElement.d.ts.map +0 -1
  160. package/dist/TungstenError.d.ts.map +0 -1
  161. package/dist/TungstenStyleObject.d.ts.map +0 -1
  162. package/dist/__deproxy.d.ts.map +0 -1
  163. package/dist/_client/RootComponent.d.ts.map +0 -1
  164. package/dist/_client/cjsHelpers.d.ts.map +0 -1
  165. package/dist/_client/client.d.ts.map +0 -1
  166. package/dist/_client/hmr.d.ts.map +0 -1
  167. package/dist/_client/liveReload.d.ts.map +0 -1
  168. package/dist/_client/liveReloadSource.d.ts.map +0 -1
  169. package/dist/_client/rootComponentRelFilePath.d.ts.map +0 -1
  170. package/dist/_client/routes.d.ts.map +0 -1
  171. package/dist/_testing/regexEquals.d.ts.map +0 -1
  172. package/dist/_testing/should.d.ts.map +0 -1
  173. package/dist/_testing/sxy-test-runner.types.d.ts.map +0 -1
  174. package/dist/alive.d.ts.map +0 -1
  175. package/dist/api/ApiEndpoint.d.ts.map +0 -1
  176. package/dist/api/ApiMethod.type.d.ts.map +0 -1
  177. package/dist/api/ApiRoute.d.ts.map +0 -1
  178. package/dist/api/ApiRoutes.d.ts.map +0 -1
  179. package/dist/api/Request.d.ts.map +0 -1
  180. package/dist/api/Response.d.ts.map +0 -1
  181. package/dist/api/getApiRoutes.d.ts.map +0 -1
  182. package/dist/api/getExport.d.ts.map +0 -1
  183. package/dist/api/getExportDev.d.ts.map +0 -1
  184. package/dist/appBuilder.d.ts.map +0 -1
  185. package/dist/appBuilder.types.test.d.ts +0 -110
  186. package/dist/appBuilder.types.test.d.ts.map +0 -1
  187. package/dist/appBuilder.types.test.js +0 -240
  188. package/dist/appOutput.d.ts.map +0 -1
  189. package/dist/assert.d.ts.map +0 -1
  190. package/dist/auth/checkPassword.d.ts.map +0 -1
  191. package/dist/auth/hashPassword.d.ts.map +0 -1
  192. package/dist/auth/index.d.ts.map +0 -1
  193. package/dist/babelPlugins/BabelAddDevCacheBust.d.ts.map +0 -1
  194. package/dist/babelPlugins/BabelExtractComponentStylesheet.d.ts.map +0 -1
  195. package/dist/babelPlugins/BabelFakeType.d.ts.map +0 -1
  196. package/dist/babelPlugins/BabelState.type.d.ts.map +0 -1
  197. package/dist/babelPlugins/BabelTransformBindDirect.d.ts.map +0 -1
  198. package/dist/babelPlugins/BabelTransformBindVar.d.ts.map +0 -1
  199. package/dist/babelPlugins/BabelTransformCjsToEsm.d.ts.map +0 -1
  200. package/dist/babelPlugins/BabelTransformCjsToFunction.d.ts.map +0 -1
  201. package/dist/babelPlugins/BabelTransformCjsToFunction2.d.ts.map +0 -1
  202. package/dist/babelPlugins/BabelTransformComponentState.d.ts.map +0 -1
  203. package/dist/babelPlugins/BabelTransformComponentStyles.d.ts.map +0 -1
  204. package/dist/babelPlugins/BabelTransformCssImports.d.ts.map +0 -1
  205. package/dist/babelPlugins/BabelTransformDependencyImportsOld.d.ts.map +0 -1
  206. package/dist/babelPlugins/BabelTransformDeproxyState.d.ts.map +0 -1
  207. package/dist/babelPlugins/BabelTransformDocInit.d.ts.map +0 -1
  208. package/dist/babelPlugins/BabelTransformJsxExtensionsToJs.d.ts.map +0 -1
  209. package/dist/babelPlugins/BabelTransformJsxSignals.d.ts.map +0 -1
  210. package/dist/babelPlugins/BabelTransformPackageImports.d.ts.map +0 -1
  211. package/dist/babelPlugins/BabelTransformReactive.d.ts.map +0 -1
  212. package/dist/babelPlugins/BabelTransformReactiveSvelteStyle.d.ts.map +0 -1
  213. package/dist/babelPlugins/BabelTransformRemoveBlocks.d.ts.map +0 -1
  214. package/dist/babelPlugins/BabelTransformRemoveDevBlocks.d.ts.map +0 -1
  215. package/dist/babelPlugins/BabelTransformRemoveServerBlocks.d.ts.map +0 -1
  216. package/dist/babelPlugins/BabelTransformRemoveServerCode.d.ts.map +0 -1
  217. package/dist/babelPlugins/BabelTransformSxyFrameworkImports.d.ts.map +0 -1
  218. package/dist/babelPlugins/BabelTransformSxyJsx.d.ts.map +0 -1
  219. package/dist/babelPlugins/BabelTransformUniqueComponents.d.ts.map +0 -1
  220. package/dist/babelPlugins/BabelTypes.d.ts.map +0 -1
  221. package/dist/babelPlugins/fixImportsInSassErrorMessage.d.ts.map +0 -1
  222. package/dist/babelPlugins/isBareImport.d.ts.map +0 -1
  223. package/dist/babelPlugins/test.d.ts.map +0 -1
  224. package/dist/babelPlugins/transformPackageImports/BabelTransformDependencyImportsCjs.d.ts.map +0 -1
  225. package/dist/babelPlugins/transformPackageImports/BabelTransformDependencyImportsEsm.d.ts.map +0 -1
  226. package/dist/babelPlugins/transformPackageImports/getWebLocationOfPackageFile.d.ts.map +0 -1
  227. package/dist/babelPlugins/transformPackageImports/resolveBareImport.d.ts.map +0 -1
  228. package/dist/bin/args.d.ts.map +0 -1
  229. package/dist/bin/commands/build.d.ts.map +0 -1
  230. package/dist/bin/commands/buildHelp.d.ts.map +0 -1
  231. package/dist/bin/commands/clean.d.ts.map +0 -1
  232. package/dist/bin/commands/compile.d.ts.map +0 -1
  233. package/dist/bin/commands/compileSimplePackage.d.ts.map +0 -1
  234. package/dist/bin/commands/dev.d.ts.map +0 -1
  235. package/dist/bin/commands/devmon.d.ts.map +0 -1
  236. package/dist/bin/commands/help.d.ts.map +0 -1
  237. package/dist/bin/commands/speed.d.ts.map +0 -1
  238. package/dist/bin/commands/start.d.ts.map +0 -1
  239. package/dist/bin/commands/static.d.ts.map +0 -1
  240. package/dist/bin/index.d.ts.map +0 -1
  241. package/dist/bin/moduleLoadAnalysis.d.ts.map +0 -1
  242. package/dist/bin/vendorHook.d.ts.map +0 -1
  243. package/dist/booleanAttributeValue.d.ts.map +0 -1
  244. package/dist/browserStorage.d.ts.map +0 -1
  245. package/dist/build/BabelConfig.d.ts.map +0 -1
  246. package/dist/build/CompilationInfo.d.ts.map +0 -1
  247. package/dist/build/CompilationInfoFailure.d.ts.map +0 -1
  248. package/dist/build/CompilationInfoSuccess.d.ts.map +0 -1
  249. package/dist/build/Event.type.d.ts.map +0 -1
  250. package/dist/build/ImportAliases.d.ts.map +0 -1
  251. package/dist/build/PackageList.d.ts.map +0 -1
  252. package/dist/build/PackagesCacheInfo.d.ts.map +0 -1
  253. package/dist/build/ProcessPackageResult.d.ts.map +0 -1
  254. package/dist/build/ProcessPackageResults.d.ts.map +0 -1
  255. package/dist/build/PromiseSettledResultIsPromiseRejectedResult.d.ts.map +0 -1
  256. package/dist/build/assetVersions.d.ts.map +0 -1
  257. package/dist/build/babel.user.common.config.d.ts.map +0 -1
  258. package/dist/build/bundle.d.ts.map +0 -1
  259. package/dist/build/bundleBabelOptions.d.ts.map +0 -1
  260. package/dist/build/clientModuleAliases.d.ts.map +0 -1
  261. package/dist/build/clientRoutesFile.d.ts.map +0 -1
  262. package/dist/build/combineComponentCss.d.ts.map +0 -1
  263. package/dist/build/compileJavascript.d.ts.map +0 -1
  264. package/dist/build/compileStyles.d.ts.map +0 -1
  265. package/dist/build/compileUserApp.d.ts.map +0 -1
  266. package/dist/build/componentStylesCache.d.ts.map +0 -1
  267. package/dist/build/copyAssets.d.ts.map +0 -1
  268. package/dist/build/copyPackageModulesToPackagesFolder.d.ts.map +0 -1
  269. package/dist/build/copyPublicFiles.d.ts.map +0 -1
  270. package/dist/build/createClientFiles.d.ts.map +0 -1
  271. package/dist/build/createInstanceConfigFile.d.ts.map +0 -1
  272. package/dist/build/createPackageVirtualFileSystem.d.ts.map +0 -1
  273. package/dist/build/createPackageVirtualFileSystems.d.ts.map +0 -1
  274. package/dist/build/customImportAliases.d.ts.map +0 -1
  275. package/dist/build/customPackageEntryPoints.d.ts.map +0 -1
  276. package/dist/build/devBundle.d.ts.map +0 -1
  277. package/dist/build/devBundle.rollup.config.d.ts.map +0 -1
  278. package/dist/build/findRequiredUserModules.d.ts.map +0 -1
  279. package/dist/build/frameworkDestLocation.d.ts.map +0 -1
  280. package/dist/build/frameworkStylesheets.d.ts.map +0 -1
  281. package/dist/build/getAllSxyClientModules.d.ts.map +0 -1
  282. package/dist/build/getPackageDirForFile.d.ts.map +0 -1
  283. package/dist/build/getSxyClientPackageAliases.d.ts.map +0 -1
  284. package/dist/build/getTempDirForAppCompilation.d.ts.map +0 -1
  285. package/dist/build/internalAliases.d.ts.map +0 -1
  286. package/dist/build/makeBabelRawConfig.d.ts.map +0 -1
  287. package/dist/build/makeBabelUserDevConfig.d.ts.map +0 -1
  288. package/dist/build/makeBabelUserProdConfig.d.ts.map +0 -1
  289. package/dist/build/makeEnvJavascriptContent.d.ts.map +0 -1
  290. package/dist/build/modifySassImports.d.ts.map +0 -1
  291. package/dist/build/packageIdentifier.d.ts.map +0 -1
  292. package/dist/build/processDataToLines.d.ts.map +0 -1
  293. package/dist/build/resolveSassScssImports.d.ts.map +0 -1
  294. package/dist/build/rollup.user.prod.config.d.ts.map +0 -1
  295. package/dist/build/rollupExtractComponentStylesheets.d.ts.map +0 -1
  296. package/dist/build/rollupProdConstants.d.ts.map +0 -1
  297. package/dist/build/sassImportParser.d.ts.map +0 -1
  298. package/dist/build/scssImportParserChatgpt.d.ts.map +0 -1
  299. package/dist/build/tallyBabel.d.ts.map +0 -1
  300. package/dist/cacheControl.d.ts.map +0 -1
  301. package/dist/calculateImportedStaticFileLocations.d.ts.map +0 -1
  302. package/dist/calledFrom.d.ts.map +0 -1
  303. package/dist/checkedAttributeToBoolean.d.ts.map +0 -1
  304. package/dist/checkedValueToBoolean.d.ts.map +0 -1
  305. package/dist/cliOutput.d.ts.map +0 -1
  306. package/dist/clone.d.ts.map +0 -1
  307. package/dist/compat/context.d.ts.map +0 -1
  308. package/dist/compat/forwardRef.d.ts.map +0 -1
  309. package/dist/compat/index.d.ts.map +0 -1
  310. package/dist/compat/propTypes.d.ts.map +0 -1
  311. package/dist/compat/toTungstenClassComponent.d.ts.map +0 -1
  312. package/dist/compat/toTungstenFunctionComponent.d.ts.map +0 -1
  313. package/dist/compat/types.d.ts.map +0 -1
  314. package/dist/componentClassName.d.ts.map +0 -1
  315. package/dist/componentHelpers.d.ts.map +0 -1
  316. package/dist/componentProxyHandlers.d.ts.map +0 -1
  317. package/dist/componentType.d.ts.map +0 -1
  318. package/dist/components/Await.d.ts.map +0 -1
  319. package/dist/components/Fragment.d.ts.map +0 -1
  320. package/dist/components/GoToComponent.d.ts.map +0 -1
  321. package/dist/components/Lazy.d.ts.map +0 -1
  322. package/dist/components/Link.d.ts.map +0 -1
  323. package/dist/components/NativeHtml.d.ts.map +0 -1
  324. package/dist/components/NoEscape.d.ts.map +0 -1
  325. package/dist/components/PopMessages.d.ts.map +0 -1
  326. package/dist/components/Protected.d.ts.map +0 -1
  327. package/dist/components/RawHtml.d.ts.map +0 -1
  328. package/dist/components/RouteRedirectComponent.d.ts.map +0 -1
  329. package/dist/components/Stylesheet.d.ts.map +0 -1
  330. package/dist/components/WebRedirectComponent.d.ts.map +0 -1
  331. package/dist/components/_GoToComponent.d.ts.map +0 -1
  332. package/dist/components/bindable/BindMode.d.ts.map +0 -1
  333. package/dist/components/bindable/BindVariable.d.ts.map +0 -1
  334. package/dist/components/bindable/BindableCheckbox.d.ts.map +0 -1
  335. package/dist/components/bindable/BindableInput.d.ts.map +0 -1
  336. package/dist/components/bindable/BindableInputNonString.d.ts.map +0 -1
  337. package/dist/components/bindable/BindableInputString.d.ts.map +0 -1
  338. package/dist/components/bindable/BindableInputTyped.d.ts.map +0 -1
  339. package/dist/components/bindable/BindableInputTypedLegacy.d.ts.map +0 -1
  340. package/dist/components/bindable/BindableRadio.d.ts.map +0 -1
  341. package/dist/components/bindable/BindableSelect.d.ts.map +0 -1
  342. package/dist/components/bindable/BindableSelectNaive.d.ts.map +0 -1
  343. package/dist/components/bindable/BindableTextarea.d.ts.map +0 -1
  344. package/dist/components/bindable/bindableCandidates.comparison.d.ts.map +0 -1
  345. package/dist/components/bindable/bindableHtmlElements.d.ts.map +0 -1
  346. package/dist/components/bindable/bindableProps.types.test.d.ts +0 -50
  347. package/dist/components/bindable/bindableProps.types.test.d.ts.map +0 -1
  348. package/dist/components/bindable/bindableProps.types.test.js +0 -252
  349. package/dist/components/library/RichTextEditor/RichTextEditor.d.ts.map +0 -1
  350. package/dist/components/library/RichTextEditor/StoredContentNode.d.ts.map +0 -1
  351. package/dist/components/library/RichTextEditor/activeFormats.d.ts.map +0 -1
  352. package/dist/components/library/RichTextEditor/contentTree.d.ts.map +0 -1
  353. package/dist/components/library/RichTextEditor/sanitizeAuthoredHtml.d.ts.map +0 -1
  354. package/dist/components/library/__StatefulMessage/MessageData.d.ts.map +0 -1
  355. package/dist/components/library/__StatefulMessage/StatefulMessage.d.ts.map +0 -1
  356. package/dist/components/library/index.d.ts.map +0 -1
  357. package/dist/components/makeTypedLink.d.ts.map +0 -1
  358. package/dist/concat.d.ts.map +0 -1
  359. package/dist/contentNodeSymbols.d.ts.map +0 -1
  360. package/dist/context/registerContext.d.ts.map +0 -1
  361. package/dist/copy.d.ts.map +0 -1
  362. package/dist/createElement.d.ts.map +0 -1
  363. package/dist/createRef.d.ts.map +0 -1
  364. package/dist/createRoutes.d.ts.map +0 -1
  365. package/dist/defaultConfig.d.ts.map +0 -1
  366. package/dist/deleteDirs.d.ts.map +0 -1
  367. package/dist/devBanner.d.ts.map +0 -1
  368. package/dist/docInit.d.ts.map +0 -1
  369. package/dist/domNodeChecks.d.ts.map +0 -1
  370. package/dist/env/Env.d.ts.map +0 -1
  371. package/dist/env/Interfaces.d.ts.map +0 -1
  372. package/dist/env/addToEnv.d.ts.map +0 -1
  373. package/dist/env/droppedCoreEnvKeys.d.ts.map +0 -1
  374. package/dist/env/funcs.d.ts.map +0 -1
  375. package/dist/env/getEnvSchemas.d.ts.map +0 -1
  376. package/dist/env/index.d.ts.map +0 -1
  377. package/dist/env/privateEnv.d.ts.map +0 -1
  378. package/dist/env/privateEnvOrThrow.d.ts.map +0 -1
  379. package/dist/env/publicEnv.d.ts.map +0 -1
  380. package/dist/env/publicEnvOrThrow.d.ts.map +0 -1
  381. package/dist/env/registerEnvSchemas.d.ts.map +0 -1
  382. package/dist/env/setPrivateEnvValidator.d.ts.map +0 -1
  383. package/dist/env/setPublicEnvValidator.d.ts.map +0 -1
  384. package/dist/env/test.d.ts.map +0 -1
  385. package/dist/env/types.d.ts.map +0 -1
  386. package/dist/env/windowGlobalEnvStatement.d.ts.map +0 -1
  387. package/dist/experiments.d.ts.map +0 -1
  388. package/dist/filePathFromUrl.d.ts.map +0 -1
  389. package/dist/fileVersions.d.ts.map +0 -1
  390. package/dist/findPackageDotJson.d.ts.map +0 -1
  391. package/dist/findProjectDir.d.ts.map +0 -1
  392. package/dist/findUserConfig.d.ts.map +0 -1
  393. package/dist/fixExtension.d.ts.map +0 -1
  394. package/dist/foreach.d.ts.map +0 -1
  395. package/dist/frameworkConfigSchema.d.ts.map +0 -1
  396. package/dist/getArrowFunctionBody.d.ts.map +0 -1
  397. package/dist/getComponent.d.ts.map +0 -1
  398. package/dist/getComponentDev.d.ts.map +0 -1
  399. package/dist/getConfig.d.ts.map +0 -1
  400. package/dist/getContentFromFunctionComponent.d.ts.map +0 -1
  401. package/dist/getFrameworkDir.d.ts.map +0 -1
  402. package/dist/getRoutes.d.ts.map +0 -1
  403. package/dist/getRoutesDevs.d.ts.map +0 -1
  404. package/dist/getStackTrace.d.ts.map +0 -1
  405. package/dist/getStyledContentFromFunctionComponent.d.ts.map +0 -1
  406. package/dist/getUserConfig.d.ts.map +0 -1
  407. package/dist/glob.d.ts.map +0 -1
  408. package/dist/globals.d.ts.map +0 -1
  409. package/dist/goto.d.ts.map +0 -1
  410. package/dist/hasDomain.d.ts.map +0 -1
  411. package/dist/hasValidKey.d.ts.map +0 -1
  412. package/dist/headChildren.d.ts.map +0 -1
  413. package/dist/helpers/combineClassNames.d.ts.map +0 -1
  414. package/dist/helpers/forwardingFetch.d.ts.map +0 -1
  415. package/dist/helpers/getFetchCompatibleHeaders.d.ts.map +0 -1
  416. package/dist/helpers/index.d.ts.map +0 -1
  417. package/dist/helpers/perRequest.d.ts.map +0 -1
  418. package/dist/hmr/applyHmrModule.d.ts.map +0 -1
  419. package/dist/hmr/componentIdentity.d.ts.map +0 -1
  420. package/dist/hmr/instanceRegistry.d.ts.map +0 -1
  421. package/dist/hmr/liveReloadHub.d.ts.map +0 -1
  422. package/dist/hmr/refreshStylesheets.d.ts.map +0 -1
  423. package/dist/hooks/clearCurrentComponent.d.ts.map +0 -1
  424. package/dist/hooks/getCurrentComponent.d.ts.map +0 -1
  425. package/dist/hooks/globalObj.d.ts.map +0 -1
  426. package/dist/hooks/globalType.d.ts.map +0 -1
  427. package/dist/hooks/index.d.ts.map +0 -1
  428. package/dist/hooks/setCurrentComponent.d.ts.map +0 -1
  429. package/dist/hooks/shallowCompare.d.ts.map +0 -1
  430. package/dist/hooks/useCallback.d.ts.map +0 -1
  431. package/dist/hooks/useContext.d.ts.map +0 -1
  432. package/dist/hooks/useEffect.d.ts.map +0 -1
  433. package/dist/hooks/useInstance.d.ts.map +0 -1
  434. package/dist/hooks/useMemo.d.ts.map +0 -1
  435. package/dist/hooks/useReducer.d.ts.map +0 -1
  436. package/dist/hooks/useRef.d.ts.map +0 -1
  437. package/dist/hooks/useState.d.ts.map +0 -1
  438. package/dist/htmlEntities.d.ts.map +0 -1
  439. package/dist/htmlEventHandlerKey.d.ts.map +0 -1
  440. package/dist/htmlEvents.d.ts.map +0 -1
  441. package/dist/hydrate/hydrate.d.ts.map +0 -1
  442. package/dist/hydrate/hydrateGlobalData.d.ts.map +0 -1
  443. package/dist/hydrate/reconcileHydratedAttributes.d.ts.map +0 -1
  444. package/dist/hydrate/renderHydrationMismatchBanner.d.ts.map +0 -1
  445. package/dist/importDevModuleWithRetry.d.ts.map +0 -1
  446. package/dist/importModule.d.ts.map +0 -1
  447. package/dist/index.d.ts.map +0 -1
  448. package/dist/indexInner.d.ts.map +0 -1
  449. package/dist/info.d.ts.map +0 -1
  450. package/dist/innerHtmlFromProps.d.ts.map +0 -1
  451. package/dist/isBrowser.d.ts.map +0 -1
  452. package/dist/isClient.d.ts.map +0 -1
  453. package/dist/isDev.d.ts.map +0 -1
  454. package/dist/isProd.d.ts.map +0 -1
  455. package/dist/isServer.d.ts.map +0 -1
  456. package/dist/iterationQueryString.d.ts.map +0 -1
  457. package/dist/json.d.ts.map +0 -1
  458. package/dist/jsx-runtime.d.ts.map +0 -1
  459. package/dist/jsxHelpers.d.ts.map +0 -1
  460. package/dist/jsxRuntime.d.ts.map +0 -1
  461. package/dist/jsxRuntimeSignals.d.ts.map +0 -1
  462. package/dist/loadPackageDotJsonFromDir.d.ts.map +0 -1
  463. package/dist/logAndReturn.d.ts.map +0 -1
  464. package/dist/makeFinalConfig.d.ts.map +0 -1
  465. package/dist/makeInstanceConfigFromFrameworkConfig.d.ts.map +0 -1
  466. package/dist/makeServerRpc.d.ts.map +0 -1
  467. package/dist/markOptionSelected.d.ts.map +0 -1
  468. package/dist/mergeConfigs.d.ts.map +0 -1
  469. package/dist/moduleReloader.d.ts.map +0 -1
  470. package/dist/mutatingFunctions.d.ts.map +0 -1
  471. package/dist/nbsp.d.ts.map +0 -1
  472. package/dist/objectMap.d.ts.map +0 -1
  473. package/dist/objectTypesToCopy.d.ts.map +0 -1
  474. package/dist/objectTypesToProxy.d.ts.map +0 -1
  475. package/dist/output.d.ts.map +0 -1
  476. package/dist/packageSettings.d.ts.map +0 -1
  477. package/dist/pipeLog.d.ts.map +0 -1
  478. package/dist/pipeline/PipelineRequestHandler.d.ts.map +0 -1
  479. package/dist/pipeline/contextStore.d.ts.map +0 -1
  480. package/dist/pipeline/hydratePipeline.d.ts.map +0 -1
  481. package/dist/pipeline/importRoutesModule.d.ts.map +0 -1
  482. package/dist/pipeline/networkedTransport.d.ts.map +0 -1
  483. package/dist/pipeline/pipeline.d.ts.map +0 -1
  484. package/dist/pipeline/pipeline.types.test.d.ts +0 -650
  485. package/dist/pipeline/pipeline.types.test.d.ts.map +0 -1
  486. package/dist/pipeline/pipeline.types.test.js +0 -342
  487. package/dist/pipeline/pipelineDefinition.d.ts.map +0 -1
  488. package/dist/pipeline/pipelineSplit.types.test.d.ts +0 -1971
  489. package/dist/pipeline/pipelineSplit.types.test.d.ts.map +0 -1
  490. package/dist/pipeline/pipelineSplit.types.test.js +0 -440
  491. package/dist/pipeline/runPipeline.d.ts.map +0 -1
  492. package/dist/pipeline/runSplitPipeline.d.ts.map +0 -1
  493. package/dist/pipeline/splitPipeline.d.ts.map +0 -1
  494. package/dist/pop.d.ts.map +0 -1
  495. package/dist/possibleEnvFiles.d.ts.map +0 -1
  496. package/dist/postcssPlugins/TransformCssNames.d.ts.map +0 -1
  497. package/dist/postcssPlugins/parseSelectors.d.ts.map +0 -1
  498. package/dist/postcssPlugins/parseSelectorsRich.d.ts.map +0 -1
  499. package/dist/postcssPlugins/scopeAnimationValue.d.ts.map +0 -1
  500. package/dist/postcssPlugins/splitCssValue.d.ts.map +0 -1
  501. package/dist/postcssPlugins/unwrapIgnoreMarker.d.ts.map +0 -1
  502. package/dist/promise.d.ts.map +0 -1
  503. package/dist/propsAreEqual.d.ts.map +0 -1
  504. package/dist/rawHtml/HtmlAllowList.d.ts.map +0 -1
  505. package/dist/rawHtml/decodeHtmlEntities.d.ts.map +0 -1
  506. package/dist/rawHtml/filterContentNode.d.ts.map +0 -1
  507. package/dist/rawHtml/htmlParsingTags.d.ts.map +0 -1
  508. package/dist/rawHtml/parseHtml.d.ts.map +0 -1
  509. package/dist/rawHtml/safeHtmlAllowList.d.ts.map +0 -1
  510. package/dist/rawHtml/sanitizeHtml.d.ts.map +0 -1
  511. package/dist/rawHtml/tokenizeHtml.d.ts.map +0 -1
  512. package/dist/reRouting/addRequireResolveHooks.d.ts.map +0 -1
  513. package/dist/reRouting/importLoader.d.ts.map +0 -1
  514. package/dist/ref.d.ts.map +0 -1
  515. package/dist/regexEscape.d.ts.map +0 -1
  516. package/dist/removeRecursive.d.ts.map +0 -1
  517. package/dist/render/HeadData.d.ts.map +0 -1
  518. package/dist/render/SxyFrameworkModule.d.ts.map +0 -1
  519. package/dist/render/SxyFrameworkModules.d.ts.map +0 -1
  520. package/dist/render/addImportMap.d.ts.map +0 -1
  521. package/dist/render/classComponentNodeToRenderingTree.d.ts.map +0 -1
  522. package/dist/render/componentNodeToRenderingTree.d.ts.map +0 -1
  523. package/dist/render/contentNodeToHtml.d.ts.map +0 -1
  524. package/dist/render/contentToHtml.d.ts.map +0 -1
  525. package/dist/render/contentToHtmlDev.d.ts.map +0 -1
  526. package/dist/render/contentTreeToRenderingTree.d.ts.map +0 -1
  527. package/dist/render/debug.d.ts.map +0 -1
  528. package/dist/render/functionComponentNodeToRenderingTree.d.ts.map +0 -1
  529. package/dist/render/getFunctionBodyAsString.d.ts.map +0 -1
  530. package/dist/render/htmlMonoTags.d.ts.map +0 -1
  531. package/dist/render/htmlNoSelfCloseTags.d.ts.map +0 -1
  532. package/dist/render/htmlSelfClosableTags.d.ts.map +0 -1
  533. package/dist/render/htmlSingleOnlyTags.d.ts.map +0 -1
  534. package/dist/render/htmlVoidTags.d.ts.map +0 -1
  535. package/dist/render/instantiateClassComponent.d.ts.map +0 -1
  536. package/dist/render/instantiateComponent.d.ts.map +0 -1
  537. package/dist/render/instantiateFunctionComponent.d.ts.map +0 -1
  538. package/dist/render/printComponentNameInSource.d.ts.map +0 -1
  539. package/dist/render/processBody.d.ts.map +0 -1
  540. package/dist/render/processHead.d.ts.map +0 -1
  541. package/dist/render/processHtmlNode.d.ts.map +0 -1
  542. package/dist/render/processProps.d.ts.map +0 -1
  543. package/dist/render/processRenderingNodeHtml.d.ts.map +0 -1
  544. package/dist/render/readDocInitScript.d.ts.map +0 -1
  545. package/dist/render/renderingTreeToHtml.d.ts.map +0 -1
  546. package/dist/render/rootToHtml.d.ts.map +0 -1
  547. package/dist/render/rootToHtmlDev.d.ts.map +0 -1
  548. package/dist/requests/axios/get.d.ts.map +0 -1
  549. package/dist/requests/axios/post.d.ts.map +0 -1
  550. package/dist/requests/fetch/FetchExtras.d.ts.map +0 -1
  551. package/dist/requests/fetch/_fetchPromise.d.ts.map +0 -1
  552. package/dist/requests/fetch/delete.d.ts.map +0 -1
  553. package/dist/requests/fetch/get.d.ts.map +0 -1
  554. package/dist/requests/fetch/mergeFetchInit.d.ts.map +0 -1
  555. package/dist/requests/fetch/paramsObjectToParamsString.d.ts.map +0 -1
  556. package/dist/requests/fetch/patch.d.ts.map +0 -1
  557. package/dist/requests/fetch/post.d.ts.map +0 -1
  558. package/dist/requests/fetch/put.d.ts.map +0 -1
  559. package/dist/requests/index.d.ts.map +0 -1
  560. package/dist/routesLocationParts.d.ts.map +0 -1
  561. package/dist/routing/AppRoutes.d.ts.map +0 -1
  562. package/dist/routing/Default404.d.ts.map +0 -1
  563. package/dist/routing/ErrorRouteType.d.ts.map +0 -1
  564. package/dist/routing/Group.d.ts.map +0 -1
  565. package/dist/routing/Match.type.d.ts.map +0 -1
  566. package/dist/routing/MatchedRoute.d.ts.map +0 -1
  567. package/dist/routing/MatchedRouteInfo.d.ts.map +0 -1
  568. package/dist/routing/Params.d.ts.map +0 -1
  569. package/dist/routing/ParamsFromPath.d.ts.map +0 -1
  570. package/dist/routing/RedirectCode.d.ts.map +0 -1
  571. package/dist/routing/Route.d.ts.map +0 -1
  572. package/dist/routing/RouteContent.d.ts.map +0 -1
  573. package/dist/routing/RouteContentFunction.d.ts.map +0 -1
  574. package/dist/routing/RouteData.d.ts.map +0 -1
  575. package/dist/routing/RouteDataFunction.type.d.ts.map +0 -1
  576. package/dist/routing/RouteDataReturnType.d.ts.map +0 -1
  577. package/dist/routing/RouteParams.type.d.ts.map +0 -1
  578. package/dist/routing/RoutePaths.d.ts.map +0 -1
  579. package/dist/routing/RouteProp.d.ts.map +0 -1
  580. package/dist/routing/RouteType.d.ts.map +0 -1
  581. package/dist/routing/RouteTypes.d.ts.map +0 -1
  582. package/dist/routing/Router.d.ts.map +0 -1
  583. package/dist/routing/Routes.d.ts.map +0 -1
  584. package/dist/routing/RoutesFunction.d.ts.map +0 -1
  585. package/dist/routing/RoutesWithSafeParams.d.ts.map +0 -1
  586. package/dist/routing/RoutingRedirect.d.ts.map +0 -1
  587. package/dist/routing/Serializable.d.ts.map +0 -1
  588. package/dist/routing/TungstenErrorInfo.d.ts.map +0 -1
  589. package/dist/routing/WebRedirect.d.ts.map +0 -1
  590. package/dist/routing/builder.d.ts.map +0 -1
  591. package/dist/routing/builder.trial.d.ts.map +0 -1
  592. package/dist/routing/compareRequestToRegex.d.ts.map +0 -1
  593. package/dist/routing/debug.d.ts.map +0 -1
  594. package/dist/routing/defineRoutes.d.ts.map +0 -1
  595. package/dist/routing/getPathPriority.d.ts.map +0 -1
  596. package/dist/routing/matchPath.d.ts.map +0 -1
  597. package/dist/routing/patternToRegex.d.ts.map +0 -1
  598. package/dist/routing/pyramidRouting.d.ts.map +0 -1
  599. package/dist/routing/registerGlobalData.d.ts.map +0 -1
  600. package/dist/routing/registerParts.types.test.d.ts +0 -2
  601. package/dist/routing/registerParts.types.test.d.ts.map +0 -1
  602. package/dist/routing/registerParts.types.test.js +0 -139
  603. package/dist/routing/registerRoutes.d.ts.map +0 -1
  604. package/dist/routing/registerRouting.d.ts.map +0 -1
  605. package/dist/routing/registerRouting.types.test.d.ts +0 -80
  606. package/dist/routing/registerRouting.types.test.d.ts.map +0 -1
  607. package/dist/routing/registerRouting.types.test.js +0 -120
  608. package/dist/routing/registrationTypes.d.ts.map +0 -1
  609. package/dist/routing/resolveAppRoutes.d.ts.map +0 -1
  610. package/dist/routing/routeSymbols.d.ts.map +0 -1
  611. package/dist/routing/routeUrl.d.ts.map +0 -1
  612. package/dist/routing/routesArray.d.ts.map +0 -1
  613. package/dist/routing/routesArray.types.test.d.ts +0 -2
  614. package/dist/routing/routesArray.types.test.d.ts.map +0 -1
  615. package/dist/routing/routesArray.types.test.js +0 -132
  616. package/dist/routing/routingBuilder.d.ts.map +0 -1
  617. package/dist/routing/routingBuilder.types.test.d.ts +0 -2
  618. package/dist/routing/routingBuilder.types.test.d.ts.map +0 -1
  619. package/dist/routing/routingBuilder.types.test.js +0 -171
  620. package/dist/routing/routingRegistry.d.ts.map +0 -1
  621. package/dist/routing/runContentFunction.d.ts.map +0 -1
  622. package/dist/routing/splitRequest.d.ts.map +0 -1
  623. package/dist/run.d.ts.map +0 -1
  624. package/dist/server.d.ts.map +0 -1
  625. package/dist/serverModuleLocation.d.ts.map +0 -1
  626. package/dist/serverRpc.d.ts.map +0 -1
  627. package/dist/serverRpcs.d.ts.map +0 -1
  628. package/dist/settings.d.ts.map +0 -1
  629. package/dist/signals/tracking.d.ts.map +0 -1
  630. package/dist/slash.d.ts.map +0 -1
  631. package/dist/spawnShellCommand.d.ts.map +0 -1
  632. package/dist/standardSchema.d.ts.map +0 -1
  633. package/dist/standardizeWebPath.d.ts.map +0 -1
  634. package/dist/startDevServer.d.ts.map +0 -1
  635. package/dist/stateTracking.d.ts.map +0 -1
  636. package/dist/staticGen/RouteDefinition.d.ts.map +0 -1
  637. package/dist/staticGen/getAppRoutes.d.ts.map +0 -1
  638. package/dist/staticGen/staticGen.d.ts.map +0 -1
  639. package/dist/staticGen/staticGenFunctions.d.ts.map +0 -1
  640. package/dist/style/jssToCss.d.ts.map +0 -1
  641. package/dist/style/styleContent.d.ts.map +0 -1
  642. package/dist/style/styleContentReact.d.ts.map +0 -1
  643. package/dist/style/styleContentReactClassOrClassname.d.ts.map +0 -1
  644. package/dist/style/styleContentSxy.d.ts.map +0 -1
  645. package/dist/style/styleContentSxyClassOrClassname.d.ts.map +0 -1
  646. package/dist/style/styleElementContent.d.ts.map +0 -1
  647. package/dist/style/unusedStyles.d.ts.map +0 -1
  648. package/dist/styleProcessors.d.ts.map +0 -1
  649. package/dist/symbols.d.ts.map +0 -1
  650. package/dist/sync/index.d.ts.map +0 -1
  651. package/dist/syncServer/index.d.cts.map +0 -1
  652. package/dist/syncServer/preload.d.ts.map +0 -1
  653. package/dist/testing/createTestApp.d.ts.map +0 -1
  654. package/dist/testing/createTestPipelineApp.d.ts +0 -25
  655. package/dist/testing/createTestPipelineApp.d.ts.map +0 -1
  656. package/dist/testing/createTestPipelineApp.js +0 -153
  657. package/dist/testing/ensure.d.ts.map +0 -1
  658. package/dist/testing/ensure.trial.d.ts.map +0 -1
  659. package/dist/testing/helpers/contentHtmlElements.d.ts.map +0 -1
  660. package/dist/testing/host.d.ts.map +0 -1
  661. package/dist/testing/hostPipeline.d.ts +0 -157
  662. package/dist/testing/hostPipeline.d.ts.map +0 -1
  663. package/dist/testing/hostPipeline.js +0 -341
  664. package/dist/testing/hydrateTestApp.d.ts.map +0 -1
  665. package/dist/testing/hydrateTestPipelineApp.d.ts.map +0 -1
  666. package/dist/testing/index.d.ts.map +0 -1
  667. package/dist/testing/installTestDocInit.d.ts.map +0 -1
  668. package/dist/testing/installTestStyleProcessors.d.ts.map +0 -1
  669. package/dist/testing/loadEnv.d.ts.map +0 -1
  670. package/dist/testing/normalize.d.ts.map +0 -1
  671. package/dist/testing/resolveContent.d.ts.map +0 -1
  672. package/dist/testing/testEnvIsRequested.d.ts.map +0 -1
  673. package/dist/testing/testingComponentPackage.d.ts.map +0 -1
  674. package/dist/throwError.d.ts.map +0 -1
  675. package/dist/timeProfiler.d.ts.map +0 -1
  676. package/dist/todo.d.ts.map +0 -1
  677. package/dist/update/UpdateResult.d.ts.map +0 -1
  678. package/dist/update/UpdateScheduler.d.ts.map +0 -1
  679. package/dist/update/guardedUpdate.d.ts.map +0 -1
  680. package/dist/update/integrateUpdateVdom.d.ts.map +0 -1
  681. package/dist/update/integrateUpdateVdomPrebuild.d.ts.map +0 -1
  682. package/dist/update/pendingMounts.d.ts.map +0 -1
  683. package/dist/validChildren.d.ts.map +0 -1
  684. package/dist/warnOnFrameworkChange.d.ts.map +0 -1
  685. package/dist/watch.d.ts.map +0 -1
  686. package/dist/when.d.ts.map +0 -1
  687. package/dist/zones/react.d.ts.map +0 -1
package/AGENTS.md ADDED
@@ -0,0 +1,1246 @@
1
+ # Tungsten — guide for coding agents
2
+
3
+ A working guide to building applications with [Tungsten](https://robertsandiford.co.uk/tungsten),
4
+ written for AI coding agents. It covers the parts of the framework you need to write correct code,
5
+ and the places where Tungsten differs from React/Next in ways that silently produce broken output.
6
+
7
+ Read the section for the work you are about to do. The **Things to avoid** list at the end is a
8
+ summary, not a substitute.
9
+
10
+ ## Authority
11
+
12
+ Prefer these sources in order:
13
+
14
+ 1. The installed source and TypeScript declarations — `node_modules/tungsten/dist/**/*.d.ts`, and
15
+ `src/` if you have the framework repo. This is definitive.
16
+ 2. Existing code in the application you are working on. Follow its established patterns rather
17
+ than introducing a second way of doing the same thing.
18
+ 3. This document.
19
+ 4. The [published documentation](https://robertsandiford.co.uk/tungsten). Good for concepts and
20
+ background, but it is a work in progress and some examples use older API names.
21
+
22
+ If this document and the installed declarations disagree, the declarations win — say so rather
23
+ than working around it.
24
+
25
+ ## What Tungsten is
26
+
27
+ A TypeScript, class-component frontend framework whose rendering and updating algorithm is
28
+ deliberately close to React's, wrapped in a scope closer to Next: it supplies routing, scoped
29
+ styling, a reactive store, SSR + hydration, environment handling, dev/prod servers, and a build
30
+ pipeline.
31
+
32
+ What it is **not**:
33
+
34
+ - Not React. Do not add React, React DOM, or hooks-based code to a Tungsten app. Some
35
+ function-component compatibility exists (`src/hooks/`, `src/compat/`) but it is deprioritized —
36
+ function-component stand-ins are not persistent. Class components are the model.
37
+ - Not file-system routed. Routes are defined in code, deliberately.
38
+ - Not a full-stack data framework. It has no server components and no opinion about your API or
39
+ database. tRPC and Prisma appear in the author's own applications by preference; they are not
40
+ part of Tungsten.
41
+
42
+ ## Related packages
43
+
44
+ Two sibling repositories, in `framework/` next to this work folder, ship with Tungsten and track
45
+ its conventions:
46
+
47
+ - **`create-tungsten-app`** — the project generator, with the starter templates under `templates/`.
48
+ A change to an app-facing protocol here (config, env, routing, the JSX runtime) usually has to
49
+ reach the templates too.
50
+ - **`eslint-config-tungsten-flat`** — the lint configuration the generated apps use.
51
+
52
+ Neither is touched from this repo; each has its own working tree and history.
53
+
54
+ ## Runtime model
55
+
56
+ On an initial request the server matches a route, runs that route's `data` function, renders the
57
+ root component and matched page to HTML, and sends it. The browser hydrates that markup.
58
+ Subsequent navigation through `<Link>` is client-side: the destination route's `data` function is
59
+ fetched first, then its content renders.
60
+
61
+ ```
62
+ request -> match route -> route data -> root + page render -> HTML -> hydrate
63
+ ```
64
+
65
+ Two consequences that cause real bugs:
66
+
67
+ - **The server render and the hydration render must produce identical output.** Anything
68
+ environment-dependent — `toLocaleString`, local timezone, `Math.random`, `Date.now` — will
69
+ differ between server and browser and corrupt hydration. Build such strings from fixed parts
70
+ (UTC getters, explicit locales), and coerce defensively: a `Date` in route data may arrive at
71
+ the client as an ISO string.
72
+ - **Clicks before hydration completes are lost.** This is the standard SSR caveat; do not add
73
+ application code to work around it.
74
+
75
+ ## Project layout and configuration
76
+
77
+ `tungsten.config.js` at the project root configures the framework. Defaults are in
78
+ `src/defaultConfig.ts`; the type is `FrameworkConfig` (usable via JSDoc in a `.js` config).
79
+
80
+ | Key | Meaning |
81
+ | --- | --- |
82
+ | `codeDir` | Folder of compiled application code Tungsten consumes, e.g. `'dist'`. |
83
+ | `sourceDir` | Source folder — needed to locate `.sass`/`.css` files if the TS build does not copy them. |
84
+ | `rootComponent` | Entry component, relative to `codeDir`, e.g. `'client/Root.jsx'`. |
85
+ | `routesLocation` | `[file, exportName]`, relative to `codeDir`. Defaults to `['routes.jsx', 'routes']`; set it only when the routes live elsewhere or under another name, e.g. `['client/routes.jsx', 'getRoutes']`. |
86
+ | `envSchemasLocation` | Module that registers the env schemas (see [Environment](#environment)). Defaults to `'env.js'`, e.g. `'common/env.js'`. |
87
+ | `clientDirs` / `clientPatterns` | Sources the browser pipeline compiles. Unset, everything. |
88
+ | `rawDirs` / `rawPatterns` | Sources the node pipeline compiles (types stripped, imports untouched) — server middleware and the like. Unset, nothing. |
89
+ | `ignoreDirs` / `ignorePatterns` | Sources neither pipeline compiles. No default. Each pipeline compiles its selection **and not** these, so an ignored folder cannot be selected back in. `ignoreDirs: ['server']` suits an app whose own build emits its server code; to have Tungsten compile the server, narrow `clientDirs` and select it in `rawDirs` instead. |
90
+ | `serverMiddleware` | `[file, exportName, mountPath]` — Express handlers mounted on Tungsten's own server, so a frontend and an API can share one process. Loaded from the raw pipeline's output when the file is in `rawDirs`, otherwise from `codeDir`. |
91
+ | `defaultCacheControl` | The `Cache-Control` of prod pages that set none themselves (see [Page caching](#page-caching)). Unset, pages are sent without one. |
92
+ | `serverSideRouting` | 0.7.1+. Unset is `true`. `false`: the server runs none of the app's code for a page. It does no routing and fetches no data, and sends the same stub for every URL (status 200): the root component's `<html>`, `<head>` and `<body>` with nothing in them. The client then fetches the global data (or the pipeline's all-routes data) beside its first routing, with the route data, and builds the head and body. A pipeline app runs, in order: `clientInit`/`init`, then the all-routes data beside routing, then `clientPost`/`post`, then `clientAfterRouting`. A route data redirect is followed on the client before anything is built. Turns `csr` on, and needs a root component. The root's own `content` still runs on the server, and registered stores are built there with an empty context, so neither may depend on the data. Static generation writes the stub for each listed route; routes found only by following links are not found. |
93
+
94
+ ### The two-stage build, and why imports end in `.jsx`
95
+
96
+ The application `tsconfig.json` uses `jsx: "preserve"` with an `outDir`, so **tsc emits `.jsx`
97
+ files**; Tungsten then Babel-processes those (its own JSX runtime plus transforms for component
98
+ state, bindings, styles, css imports, and package handling).
99
+
100
+ Under NodeNext resolution that means source files import the *emitted* extension:
101
+
102
+ ```ts
103
+ import { Home } from './pages/Home.jsx' // even though the file is Home.tsx
104
+ import { formatDate } from './lib/date.js' // even though the file is date.ts
105
+ ```
106
+
107
+ This is correct and intentional. Do not "fix" it to `.tsx`/`.ts` or extensionless.
108
+
109
+ Typical scripts: `dev` = `tsc --watch` alongside `tungsten dev`; `build` = `tsc && tungsten build`;
110
+ `start` = `tungsten start`.
111
+
112
+ ## Components
113
+
114
+ Base classes, exported from `tungsten` with both a long and a short name
115
+ (`src/Components.tsx`, `src/indexInner.ts`):
116
+
117
+ | Use | Class | Alias |
118
+ | --- | --- | --- |
119
+ | No local state | `TungstenSimpleComponent<Props, RouteData?>` | `SimpleComponent` |
120
+ | Local state | `TungstenStatefulComponent<Props, State, RouteData?>` | `StatefulComponent` |
121
+ | Optional state | `TungstenComponent<Props, State?, RouteData?>` | — |
122
+ | Store access, stateless | `TungstenBaseSimpleStoreComponent<Props, RouteData, Schema>` | `BaseSimpleStoreComponent` |
123
+ | Store access, stateful | `TungstenBaseStatefulStoreComponent<Props, State, RouteData, Schema>` | `BaseStatefulStoreComponent` |
124
+
125
+ Applications normally subclass the two store base classes once with their own schema and use those
126
+ wrappers everywhere (see **Store**). Pick the smallest class that fits: no state where none is
127
+ needed, no store where the component only needs props.
128
+
129
+ Everything is a class field, usually an arrow function:
130
+
131
+ ```tsx
132
+ import { StatefulComponent, BindableInput } from 'tungsten'
133
+
134
+ type Props = { initialName: string }
135
+ type State = { name: string }
136
+
137
+ export class Greeting extends StatefulComponent<Props, State> {
138
+ state: State = { name: this.props.initialName }
139
+
140
+ title = `Hello, ${this.props.initialName}` // sets document title
141
+
142
+ reset = () => { this.state.name = this.props.initialName }
143
+
144
+ content = () => (
145
+ <section>
146
+ <BindableInput name="name" bind={[this.state, 'name']} />
147
+ <p>{this.state.name}</p>
148
+ <button onClick={this.reset}>Reset</button>
149
+ </section>
150
+ )
151
+
152
+ style = Sass`
153
+ section
154
+ padding: 1rem
155
+ `
156
+ }
157
+ ```
158
+
159
+ - **`content` is the render method.** It is called as `content(props, state)` and **may be
160
+ async** — the framework awaits it. (`render` is accepted as a React-compat alternative; if both
161
+ exist, `content` wins.)
162
+ - Use arrow-function class fields for handlers so `this` is bound.
163
+ - `title` sets the document title and may be `(props, state) => string`. **Only set `title` on
164
+ page components** — the last call wins, so a nested component setting it will fight the page.
165
+ - `this.tungsten` holds framework helpers bound to the component (0.7.1+):
166
+ `this.tungsten.getRoutes()` gives the app's routes as this request resolved them (the function
167
+ form called for you), for rendering nav; `this.tungsten.fileUrl('/logo.png')` gives a site
168
+ file's URL with its cache-busting version (see **Styling**). `this.tungsten.instance` is this
169
+ component's instance — the same object as `this.__instance`, under a name that is part of the
170
+ API rather than a convention.
171
+
172
+ ### Lifecycle
173
+
174
+ Each hook has a React-style name and a short alias:
175
+
176
+ | Hook | Alias | Notes |
177
+ | --- | --- | --- |
178
+ | `init(props)` / `init(props, state)` | — | On construction, both server and client. May be async. A `TungstenSimpleComponent` is passed props only; the stateful bases pass state too. |
179
+ | `serverInit` / `clientInit` | — | Side-specific initialization. Same arguments as `init`. |
180
+ | `componentDidMount` | `_mounted` | |
181
+ | `componentDidHydrate` | `_hydrated` | Client only, after hydration attaches. |
182
+ | `componentDidUpdate` | `_updated` | |
183
+ | `componentDidRender` | `_rendered` | |
184
+ | `componentWillUnmount` | `_deleting` | |
185
+ | `componentDidUnmount` | `_deleted` | |
186
+
187
+ Put browser-only work (`window`, `location`, `localStorage`) in `clientInit` or a later client
188
+ hook — never unguarded in `init`, route code, or `content`.
189
+
190
+ Other instance members worth knowing: `this.props`, `this.routeParams`, `this.routeData`,
191
+ `this.globalData`, `this.triggerUpdate()` (manual re-render),
192
+ `this.pickProps(...)` / `this.pickState(...)`, and `this.using(disposable)` for something that
193
+ should be disposed on unmount. `this.setTimeout(fn, ms)` / `this.setInterval(fn, ms)` (with
194
+ `this.clearTimeout` / `this.clearInterval`) work like the window timers but are cleared when the
195
+ component is deleted; on the server they do nothing and return `undefined`.
196
+
197
+ ### State is mutable and proxied — this is the biggest difference from React
198
+
199
+ `this.state`, and any plain objects, arrays, `Map`s and `Set`s inside it, are wrapped in proxies
200
+ on the client (`src/componentProxyHandlers.ts`). Writes are detected and queue a re-render.
201
+
202
+ ```ts
203
+ this.state.count++
204
+ this.state.items.push(item)
205
+ this.state.form.email = value
206
+ delete this.state.items[index]
207
+ ```
208
+
209
+ All of that is correct Tungsten code. Rules that follow from it:
210
+
211
+ - **Do not use `setState`.** It exists only as a React-compat shim and is not needed.
212
+ - **Do not write immutable-update code** — no `{ ...this.state, count: n }` ceremony, no
213
+ reducers. Deep state structures are fine and are not discouraged.
214
+ - **State updates apply immediately; re-renders are batched.** You read your own writes at once,
215
+ but the DOM catches up on the next tick. An `await` is a batching boundary — a queued update can
216
+ run while you are suspended.
217
+ - **Never mutate state inside `content`** or anything it calls. That risks a torn render. Mutate
218
+ in init hooks and event handlers only.
219
+ - An object already owned by another component's state is **adopted by copy** when you store it,
220
+ so the two components' states stay independent. Do not rely on sharing a mutable object between
221
+ components as a communication channel — use the store, or props plus a callback.
222
+ - **An `async content` widens every one of these rules.** Nothing locks a component while it is
223
+ suspended: a state write starts a second, overlapping update, and a parent re-render replaces
224
+ `this.props` under the render in flight — so code before the first `await` and code after it can
225
+ see different props. Prefer a synchronous `content` with the loading done in a lifecycle hook.
226
+
227
+ #### Framework internals: the render baseline is stamped *before* the render
228
+
229
+ `markRendered(element)` records the tick a component last rendered at, and `propsAreEqual()` skips
230
+ a child whose props carry no write newer than it. Both call sites in `integrateUpdateVdom.ts` must
231
+ therefore stamp **before** building content, as `__initialContent` already does at mount. Stamping
232
+ afterwards buries any write that landed while an async content was suspended: its tick is then
233
+ `<=` the baseline, and the child is skipped **for good**, since nothing will stamp that object
234
+ again. Regression test: `src/update/asyncRenderBaseline.dom.sxyt.tsx`.
235
+
236
+ ## Routing and data loading
237
+
238
+ Routes are an object keyed by path, exported from the module named by `routesLocation` — by
239
+ default an export named `routes` from `routes.jsx` in `codeDir`, which is what
240
+ `create-tungsten-app` generates and needs no config:
241
+
242
+ ```tsx
243
+ export const routes = {
244
+ '/': { content: <Home /> },
245
+ '404': { content: <NotFound /> },
246
+ } as const satisfies CustomRoutes<Endpoint<any, any>>
247
+ ```
248
+
249
+ The export may instead be a function of the request, `(reqRes: ReqRes | null) => Routes`, when
250
+ building the routes needs it — below, to create an API client for that request. A different
251
+ export name must be set in `routesLocation`; a missing export throws an error naming the setting.
252
+
253
+ ```tsx
254
+ import { type Params, type Endpoint, type CustomRoutes } from 'tungsten'
255
+
256
+ // tungsten.config.js: routesLocation: ['routes.jsx', 'getRoutes']
257
+ export const getRoutes = (reqRes: ReqResOrNull) => {
258
+ const api = getApiClient(reqRes)
259
+
260
+ return {
261
+ '/': { content: <Home /> },
262
+
263
+ '/things/:id<1+>': {
264
+ data: ({ id }: Params<'id'>) => api.thing.get(Number(id)),
265
+ content: (data, params: Params<'id'>) => <ThingDetail thing={data} />,
266
+ } satisfies Endpoint<Params<'id'>, ThingResponse>,
267
+
268
+ '404': { content: <NotFound /> },
269
+ } as const satisfies CustomRoutes<Endpoint<any, any>>
270
+ }
271
+
272
+ export type AppRoutes = ReturnType<typeof getRoutes>
273
+ ```
274
+
275
+ - `content` is JSX, or `(data, params) => JSX` when the route has a `data` function.
276
+ - `data` is async and runs **before** the content renders — on the server during SSR, over the
277
+ network on client-side navigation. The component then receives resolved data, so there is no
278
+ loading flash on first paint. Route loaders are the right place for a page's initial read.
279
+ - Annotate each entry with `satisfies Endpoint<Params, Data>`. `Data` should be the actual response
280
+ type of the endpoint, not a locally duplicated shape. `satisfies` preserves the literal path key
281
+ while still checking the loader and the content callback.
282
+ - A **`'404'` key** supplies not-found content. Every application needs one. It answers with status
283
+ 404, or with its own **`code`** when it gives one (0.7.1+), e.g. `'404': { content: <Gone />,
284
+ code: 410 }`. An object typed with `CustomRoutes` accepts `code` from unreleased; in 0.7.1 only
285
+ `Routes` and the array form do.
286
+ - Route definitions may carry **arbitrary extra fields** (`name`, `nav`, `requireAuth`, …) by
287
+ extending `Endpoint<Params, Data>` — a navigation component can then read them off the routes
288
+ object.
289
+
290
+ ### The array form (0.7.1+)
291
+
292
+ Routes may instead be an array, each entry naming its path — anywhere the object is accepted, and
293
+ as a routes function's return:
294
+
295
+ ```tsx
296
+ import { route } from 'tungsten'
297
+
298
+ export const getRoutes = (reqRes: ReqResOrNull) => [
299
+ route({ path: '/', content: <Home /> }),
300
+ route({ path: '/things/:id<1+>', content: <Thing /> }),
301
+ route({ path: '404', content: <NotFound /> }),
302
+ ]
303
+ ```
304
+
305
+ - `route()` keeps the path literal, so `RoutesType` and `PathsType` read the array as they read
306
+ the object.
307
+ - A **path given twice** is a type error when the routes are registered through the app builder's
308
+ `.routes()`: the message names the path. It needs the array written out in the call (or in the
309
+ function it returns from); an array built elsewhere is checked when the routes are read instead,
310
+ which throws in dev. Outside dev, the last entry for a path wins, as a repeated object key does.
311
+ - The array cannot carry the object's `[$cacheControl]`; each entry's own `cacheControl` still
312
+ applies.
313
+ - **`routesWithList<Rule>()`** (0.7.1+) requires fields of every path route, leaving `'default'`,
314
+ `'404'` and `'error'` free of them. It returns the array as given, typed as a tuple:
315
+
316
+ ```tsx
317
+ export const routes = routesWithList<{ name: string }>()([
318
+ route({ path: '/', name: 'Home', content: <Home /> }),
319
+ route({ path: '404', content: <NotFound /> }),
320
+ ])
321
+ ```
322
+
323
+ `routesWith<Rule>()` takes the routes as separate arguments and returns them keyed by path; its
324
+ rule applies to every route, `'404'` included.
325
+
326
+ ### Path parameters
327
+
328
+ Express-like `:name` segments. Bare `:name` matches `[^/]+`; a trailing slash is always optional.
329
+ Two constraint syntaxes, both supported (`src/routing/patternToRegex.ts`):
330
+
331
+ - **Raw regex:** `:id(\d+)`.
332
+ - **Named types:** `:id<uint>`. Available: `digits`/`uint`/`unsigned-integer`;
333
+ `int`/`integer`/`sint`/`signed-integer`; `int-ge1`/`1+`; `num`/`number`; `alpha`;
334
+ `alpha-lower`; `alpha-num`/`alpha-numeric`; `alpha-lower-num`/`alpha-lower-numeric`.
335
+
336
+ Note `1+` means **an integer ≥ 1**, not "one or more digits". A route that does not match its
337
+ constraint falls through to the next candidate, so put literal paths before overlapping
338
+ parametric ones — `/things/new` must precede `/things/:id<1+>`.
339
+
340
+ Type params with `Params<'id'>` or `ParamsFromPath<'/things/:id'>`.
341
+
342
+ ### Navigation and redirects
343
+
344
+ - `<Link to="/things/1">` for internal navigation — it keeps routing client-side. Use a plain
345
+ `<a>` only for external URLs or paths deliberately served by something else. Since 0.6.2 a click
346
+ with a modifier key (ctrl, meta, shift, alt) or a non-primary button is left to the browser, so
347
+ open-in-new-tab works.
348
+ - **Typed links (0.7.1+):** `RoutePaths<typeof routes>` is the union of the app's paths,
349
+ parameters as template holes: `'/leads/:id<1+>'` gives `` `/leads/${number}` ``. The routes may be
350
+ an object or a routes function (every table it can return counts). Make a `Link` typed by it in
351
+ an app file:
352
+
353
+ ```ts
354
+ import { makeTypedLink } from 'tungsten'
355
+ import type { routes } from '../routes.jsx'
356
+
357
+ export const { Link } = makeTypedLink('Link')<typeof routes>()
358
+ ```
359
+
360
+ The string names the component for stylesheets (`Link` as a selector), and it is the only key
361
+ returned, so destructuring any other name is a type error. The type argument is the routes
362
+ (object or function), not `RoutePaths`: leaving it out, or passing anything with no paths, fails
363
+ at this call. Import the routes' type with
364
+ `import type`: a value import of the routes file from a component risks an import cycle. A plain
365
+ subclass does the same, its class name being the selector:
366
+ `export class Link extends _Link<RoutePaths<typeof routes>> {}`.
367
+ - `this.__instance.goto(path)` navigates from code; `<GoTo to="/path" />` does it when rendered.
368
+ Since 0.6.2, with `clientSideNav: false` it is a full page load, and during a server render it
369
+ answers the request with a redirect immediately (the first one wins if the render asks twice).
370
+ - Back/Forward keep the query (0.7.1+): a route's `data()` gets the query of the entry moved
371
+ to, and `internalStore.location` is the route path plus query after hydration as on the server.
372
+ Moving back from a same-page `#anchor` no longer re-runs the route or reloads.
373
+ - From a `data` function, **return** `new RoutingRedirect(path)` (in-app) or
374
+ `new WebRedirect(url)` (full browser redirect) instead of data. `RedirectCode` sets the status.
375
+ - As content, render `<RouteRedirectComponent to="/path" />`.
376
+ - `<MatchedRoute />` renders the active route; it belongs in the root component's `<body>`.
377
+
378
+ ### Page caching
379
+
380
+ The prod server sends each page with a `Cache-Control` chosen from named policies (0.7.1+):
381
+
382
+ | Value | Header | Use for |
383
+ | --- | --- | --- |
384
+ | `'no-store'` | `no-store` | pages that must never be kept |
385
+ | `'private-check'` | `private, no-cache` | per-user pages: only the user's browser keeps them, and checks back each time (a cheap 304 when unchanged) |
386
+ | `'public-check'` | `public, no-cache` | pages the same for everyone: CDNs may keep them too, still checking back each time |
387
+ | `{ privacy, maxAge, staleWhileRevalidate? }` | e.g. `public, max-age=300` | reused without checking for `maxAge` seconds; `privacy` is `'private'` or `'public'` |
388
+
389
+ `public` lets a CDN serve one user's copy to another, so never use it on a page that shows
390
+ per-user data. A `maxAge` page can be up to that many seconds out of date, including after a
391
+ deploy.
392
+
393
+ The most specific setting wins:
394
+
395
+ ```ts
396
+ import { $cacheControl } from 'tungsten'
397
+
398
+ export const routes = {
399
+ [$cacheControl]: 'private-check', // every route that sets none
400
+ '/docs': { content: <Docs />, cacheControl: 'public-check' },
401
+ '/feed': {
402
+ content: <Feed />,
403
+ cacheControl: ({ req }) => (req.query.live !== undefined ? 'no-store' : 'public-check'),
404
+ },
405
+ // ...
406
+ }
407
+ ```
408
+
409
+ A route's `cacheControl` (a value, or a function of
410
+ `{ req, res }`) beats the routes object's `[$cacheControl]`, which beats config
411
+ `defaultCacheControl`. With none of them set no header is
412
+ sent, and a `Cache-Control` set by `serverMiddleware` is never overwritten. The error page and
413
+ redirects are not affected. Static sites take their headers from their host.
414
+
415
+ ### The root component
416
+
417
+ The root component replaces `index.html` — it renders `<html>`, `<head>` and `<body>` itself.
418
+
419
+ **`<head>` content is not hydrated.** Event handlers placed there are never attached. The
420
+ framework inserts its own scripts and stylesheets into the head.
421
+
422
+ ## Store and global data
423
+
424
+ `registerStore(SchemaClass)` creates a typed reactive store. The schema is a **class**,
425
+ constructed per app instance with the request context, which is what makes it safe under SSR —
426
+ one store per request on the server, not a module-level singleton.
427
+
428
+ ```ts
429
+ // Store.ts
430
+ export class StoreSchema {
431
+ apiClient: ApiClient
432
+ authed: boolean = false
433
+ constructor(reqRes: ReqRes | null) { this.apiClient = getApiClient(reqRes) }
434
+ }
435
+ export const { getComponentStore } = registerStore(StoreSchema)
436
+
437
+ // StoreComponents.ts — subclass once, use everywhere
438
+ export abstract class SimpleStoreComponent<P, RD>
439
+ extends BaseSimpleStoreComponent<P, RD, StoreSchema> {
440
+ store = getComponentStore(this)
441
+ }
442
+ export abstract class StatefulStoreComponent<P, S, RD = unknown>
443
+ extends BaseStatefulStoreComponent<P, S, RD, StoreSchema> {
444
+ store = getComponentStore(this)
445
+ }
446
+ ```
447
+
448
+ Each store value exposes `.get()`, `.set(v)`, `.subscribe()`, `.unsubscribe()`, `.sync()`, and —
449
+ since 0.6.1 — `.notify(fn)` and `.stopNotify(fn)`.
450
+
451
+ **`sync()` = get + subscribe this component.** Use `sync()` when reading from `content` and the
452
+ component should re-render on change; use `get()` for a one-off read, such as pulling a client out
453
+ to make a call. Subscriptions are cleaned up when the component is deleted.
454
+
455
+ ```tsx
456
+ const client = this.store.apiClient.get() // just need the object
457
+ const authed = this.store.authed.sync() // re-render when this changes
458
+ this.store.authed.set(true)
459
+ ```
460
+
461
+ **`notify(fn)` runs a callback on every write, instead of re-rendering.** Use it for a side effect
462
+ a value should trigger — closing a menu, starting a timer, calling out — where `sync()` would be
463
+ wrong because nothing in the markup depends on the value. The callback receives the new value, and
464
+ `notify` returns the function so it can be kept for `stopNotify(fn)`.
465
+
466
+ ```tsx
467
+ init = () => {
468
+ this.store.authed.notify(authed => { if (!authed) this.closeMenu() })
469
+ }
470
+ ```
471
+
472
+ Callbacks registered from a component are released when that component is deleted, like
473
+ subscriptions — there is no need to call `stopNotify` in a teardown hook. `notify` runs on writes
474
+ only; it does not fire with the current value on registration.
475
+
476
+ The framework's own store is reached through `this.__instance.internalStore`. Its values are not
477
+ bound to a component, so pass it — from a store component, since that is the type they take:
478
+ `.sync(this)`, `.notify(fn, this)`. Two of them are read-only
479
+ (0.6.2+): `navigating` (a client navigation is in progress) and `navigationIndex` (counts
480
+ navigations) have no `.set()` — `goto()` writes them, and a test harness can't supply them.
481
+
482
+ ```tsx
483
+ content = () => this.__instance.internalStore.navigating.sync(this) ? <Spinner /> : null
484
+ ```
485
+
486
+ The store is for genuine cross-component state. Keep form and page state in `this.state`. Do not
487
+ push fetched page records into the store merely to avoid passing props — that is what route data
488
+ and props are for. The store may hold non-serializable services as well as data.
489
+
490
+ For request-scoped values available to every component, `registerGlobalData(async reqRes => ({…}))`
491
+ plus a declaration-merged `interface GlobalData` gives you `this.globalData`. The function runs
492
+ before any route renders: before SSR, with the request, and (0.7.1+) again before each
493
+ client-side navigation, with `reqRes` null, in parallel with the route's data. Its result replaces
494
+ the global data as the new route renders. The server's result is serialized into the page for
495
+ hydration, so it must be serializable.
496
+
497
+ **Context (0.7.1+).** `registerContext` supplies an object made fresh for each server request,
498
+ and once per page on the client (it survives client-side navigation):
499
+
500
+ ```ts
501
+ registerContext({ client: ClientContext, server: ServerContext }) // new ServerContext(reqRes), new ClientContext()
502
+ registerContext(Context) // one class: new Context(reqRes | null)
503
+ ```
504
+
505
+ Register it in code the routes file loads. The context is the routes function's second argument,
506
+ each store schema's fourth constructor argument (after `reqRes, globalData, matchedRouteInfo`), and
507
+ `this.__instance.context`. It is untyped (`unknown`) for now: annotate the parameter with your own
508
+ classes, e.g. `(reqRes, context: ClientContext | ServerContext) => routes`. Test harnesses take a
509
+ `context` option.
510
+
511
+ ## Styling
512
+
513
+ Component styles are a class field set to a tagged template. **`Css`, `Sass` and `Scss` are
514
+ ambient globals** — declared in `tungsten/types/index.d.ts`, so there is no import. (Less is not
515
+ implemented.)
516
+
517
+ ```tsx
518
+ style = Sass`
519
+ .panel
520
+ display: grid
521
+ gap: 0.6rem
522
+ `
523
+ ```
524
+
525
+ Sass `@use` and imports of `.sass`/`.scss` files work inside the tag. For genuinely global
526
+ styles, import a `.sass` file — the import yields an href string — and render it as
527
+ `<Stylesheet>{baseStyles}</Stylesheet>` in the root's `<head>`, which emits a `<link>` and lets
528
+ the browser cache it. An inline `css="…"` attribute is also available on elements.
529
+
530
+ In a production build (0.7.1+) URLs to the site's own files get a `?v=<content hash>` query, and
531
+ those URLs are served with a one-year `immutable` cache. A new deploy changes the hash, so browsers
532
+ fetch the new file. It covers the framework's bundle and component CSS, and every file in `public/`,
533
+ the compiled stylesheets and `assets/`. For these it is automatic: any element's `src`, `href`,
534
+ `poster` or `srcset` naming one of those files exactly is versioned when rendered, so
535
+ `<Stylesheet>{baseStyles}</Stylesheet>` and a hand-written `<img src="/logo.png" />` both get it.
536
+ A URL built in code (a `fetch`, an inline style) gets it through
537
+ `this.tungsten.fileUrl('/logo.png')`, which also adds the site base path. Not covered: URLs that
538
+ already carry a `?` or `#`, and `url(...)` inside CSS. Dev URLs carry no version.
539
+
540
+ ### How scoping works — and the mistake to avoid
541
+
542
+ Every selector in a component's `style` is rewritten to a class private to that component, and
543
+ that class is stamped onto the elements **the component itself renders**.
544
+ `dist/postcssPlugins/TransformCssNames.js` is the authority: a class selector `.panel` becomes
545
+ `Thing_panel`; a bare tag selector `div` becomes `Thing_#_div`.
546
+
547
+ Two things follow.
548
+
549
+ **First, tag selectors become class selectors, so a tag and a class have equal specificity** —
550
+ unlike ordinary CSS. Order and explicitness matter more than you expect.
551
+
552
+ **Second — the common error — a component cannot style markup rendered by a child component.**
553
+ This is deliberate encapsulation. React CSS Modules leak here; Tungsten does not.
554
+
555
+ ```tsx
556
+ <nav className="user-links">
557
+ <Link to={`/users/${user.id}`}>Profile</Link>
558
+ </nav>
559
+ ```
560
+
561
+ renders as `<nav class="user-links User_user-links"><a href="/users/3">Profile</a></nav>`. The
562
+ anchor belongs to `Link` and carries none of this component's classes, so:
563
+
564
+ ```sass
565
+ // ❌ compiles to a selector that can never match — the links stay unstyled
566
+ .user-links
567
+ a
568
+ font-weight: 600
569
+ ```
570
+
571
+ The rule is not an error, produces no warning, and simply does nothing. It is easy to write and
572
+ easy to miss in review, so check it deliberately whenever you style something that turns out to be
573
+ a component: `Link`, `BindableInput`, `BindableSelect`, `BindableTextarea`, and any component of
574
+ your own.
575
+
576
+ **The fix: name the component as the selector.** A capitalised name is transformed the same way a
577
+ tag is; Tungsten generates the scoped class and passes it to that component as `className`
578
+ automatically. There is no prop to write and no class name to invent.
579
+
580
+ ```sass
581
+ // ✅
582
+ .user-links
583
+ Link
584
+ font-weight: 600
585
+ ```
586
+
587
+ Passing a class explicitly — `<Link className="user-link">` — uses the same mechanism by hand.
588
+ Reserve it for cases the component selector cannot express: different styling for different
589
+ instances of the same component, or a child that also needs a global class.
590
+
591
+ Either way, **the child must accept a `className` prop and apply it**. Tungsten's own components
592
+ do. When you write a component, accept `className` and put it on your root element (or merge it
593
+ with `combineClassNames`) — that is what makes it styleable by a parent:
594
+
595
+ ```tsx
596
+ type Props = { className?: string, label: string }
597
+
598
+ export class Badge extends SimpleComponent<Props> {
599
+ content = ({ className, label }: Props) =>
600
+ <span className={combineClassNames(className, 'badge')}>{label}</span>
601
+ }
602
+ ```
603
+
604
+ Parent rules that land this way still beat the child's own: component styles are emitted with
605
+ 4× stacked class selectors (`.C.C.C.C`), so a parent overrides a child's internals. `!important`
606
+ is a last resort.
607
+
608
+ **Escape hatch:** `:i(<selector>)` — or its long form `:ignore(<selector>)` — pierces scoping:
609
+ `button :i(span)` reaches spans inside child components. Reserve it for third-party or legacy components that do not accept a `className`.
610
+ Reaching for it on a component you control means that component should have taken a `className`
611
+ instead.
612
+
613
+ ### Class attribute
614
+
615
+ Both `class` and `className` work in JSX, but **`className` is the canonical internal prop** —
616
+ scoped styling reads and rewrites it, and the renderer converts it to the HTML `class` attribute.
617
+ Prefer `className` consistently, for native elements and components alike, so behaviour does not
618
+ change when an element becomes a component or a prop is forwarded. Build dynamic lists with
619
+ `combineClassNames`.
620
+
621
+ ## Rendering a string of HTML
622
+
623
+ Use **`<ParseHtml>`** (the same component is also exported as `<RawHtml>`; prefer the `ParseHtml`
624
+ name, which says what it does). It parses the markup into an ordinary content tree, so the result
625
+ renders, hydrates and reconciles like anything else:
626
+
627
+ ```tsx
628
+ <div className="tip-text"><ParseHtml html={tip.text} safe /></div>
629
+ ```
630
+
631
+ | Prop | Effect |
632
+ | --- | --- |
633
+ | `html` | the markup. Anything unparseable is kept as text; it never throws |
634
+ | `safe` | applies `safeHtmlAllowList` — formatting, lists, tables, links, images |
635
+ | `allowList` | your own `HtmlAllowList`. Wins over `safe`. Disallowed elements are *unwrapped*, so no text is lost |
636
+ | `sanitize` | defaults to on: drops `<script>`, `javascript:`/`vbscript:` URLs, and `data:` URLs in navigation attributes. `on*` attributes are always dropped |
637
+
638
+ `parseHtml()`, `safeHtmlAllowList` and the `HtmlAllowList` type are exported for use outside a
639
+ component.
640
+
641
+ **The parsed elements are not part of any component's scoped CSS**, because no component rendered
642
+ them. Component styles reach them only through `:i()` / `:ignore()`:
643
+
644
+ ```sass
645
+ .tip-text
646
+ :i(p)
647
+ margin: 0 0 0.5rem
648
+ ```
649
+
650
+ **Choosing a host element matters.** Authored HTML is usually block level — paragraphs, tables —
651
+ and a `<p>` host cannot contain it: the parser closes the host at the first inner block tag,
652
+ leaving it empty. That breaks hydration outright. Use a `div`.
653
+
654
+ `dangerouslySetInnerHTML={{ __html }}` also works, and hands the markup to the browser as the
655
+ element's inner content. Tungsten never reconciles inside it, so reach for it only when the markup
656
+ must not be touched — `<ParseHtml>` is the default choice.
657
+
658
+ ### Markup that will be managed by hand: `<Protected>`
659
+
660
+ For an element whose contents a library or your own code will mutate directly (an editor, a chart,
661
+ a widget that owns its DOM), wrap the markup in **`<Protected>`**. It renders its children once and
662
+ Tungsten never updates them again: a parent re-render skips the subtree, so the reconciler does not
663
+ undo the manual changes. It does not stop the component's own updates, and it is a plain component,
664
+ so the initial markup still renders server-side and hydrates. Its `children` is a `ContentNode`, so
665
+ any JSX goes in without a cast — and markup you hold as a string goes in the same way through
666
+ `<ParseHtml>` or the `parseHtml()` function, which parse an HTML string into a content tree, the
667
+ same structure JSX compiles to:
668
+
669
+ ```tsx
670
+ <Protected><ParseHtml html={widgetMarkup} /></Protected>
671
+ ```
672
+
673
+ Prefer it over the older `<NativeHtml>`, whose children type is `ContentNodeHtml` and therefore
674
+ needs a cast on any JSX child. The name is not final.
675
+
676
+ **Avoid `__noEscape`.** It is an older switch that turns off escaping for an element's children.
677
+ It inherits down the entire subtree, so nested Tungsten text also renders unescaped; it only ever
678
+ worked server-side, so a client render escapes the markup into visible text; and it leaks into the
679
+ markup as a literal `__noescape` attribute.
680
+
681
+ ### Inline scripts and styles
682
+
683
+ The text children of `<script>` and `<style>` are emitted **verbatim** by the server renderer
684
+ (`renderingTreeToHtml.ts`; test `src/render/rawTextElements.sxyt.ts`). So inline code is just a
685
+ child string:
686
+
687
+ ```tsx
688
+ <script>{themeBoot}</script>
689
+ <style>{criticalCss}</style>
690
+ ```
691
+
692
+ The React idiom `<script dangerouslySetInnerHTML={{ __html: code }} />` exists because React
693
+ HTML-escapes script children; Tungsten does not, so the idiom is unnecessary — do not carry it
694
+ over. As with any framework, the string itself must not contain `</script>`.
695
+
696
+ A boot script that runs before hydration (theme, colour scheme) usually wants to set an attribute
697
+ on `<html>` or `<body>`. **Hydration reconciles attributes to what the component rendered and
698
+ removes any it did not** (`reconcileHydratedAttributes.ts`), so an attribute the script set on an
699
+ element the root component renders is stripped the moment that element hydrates — reported in dev
700
+ as a mismatch banner (`data-theme: rendered null, server sent …`), silently in prod. Deriving the
701
+ value on the client instead (`localStorage` in `clientInit`) does not help: the hydration render
702
+ then differs from the server's markup, which is the same mismatch from the other side, on every
703
+ load. The server has to know the value: **keep such a preference in a cookie**, read it
704
+ server-side (`reqRes` / global data) and render the attribute from the root component, so both
705
+ renders agree — no boot script, no flash, no mismatch. A value the server cannot know (`'auto'`,
706
+ i.e. `prefers-color-scheme`) should render no attribute at all, with CSS `@media
707
+ (prefers-color-scheme: dark)` as the fallback when `data-theme` is absent.
708
+
709
+ ## Form binding
710
+
711
+ The bindable components give two-way binding to any object property:
712
+
713
+ ```tsx
714
+ <BindableInput name="email" bind={[this.state.form, 'email']} />
715
+ <BindableSelect name="role" bind={[this.state.form, 'role']} options={roleOptions} />
716
+ <BindableCheckbox name="active" bindChecked={[this.state.form, 'active']} />
717
+ ```
718
+
719
+ The family is `BindableInput`, `BindableInputTyped`, `BindableCheckbox`, `BindableRadio`,
720
+ `BindableSelect`, `BindableTextarea`. `bind={[object, 'key']}` accepts state, a nested state
721
+ object, or a plain instance property. Binds take an optional `filter` to transform or validate
722
+ incoming values before they reach the property — the usual use is converting an input string to
723
+ the field's real type.
724
+
725
+ `BindableSelect` takes either an `options` array of `{ value, text }` structs or ordinary JSX
726
+ children (`<option>`, `<optgroup>`, conditionals). Prefer `options` when the values form a literal
727
+ union: `value` is how option values reach the type system, and with children they degrade to
728
+ `string`. As with a plain `<select>`, `placeholder` does nothing — supply a leading empty-valued
729
+ option instead. See `src/components/bindable/BindableSelect.md` before changing that component.
730
+
731
+ A common and reasonable application pattern is to hold form values as strings in state and convert
732
+ to the domain type on submit, since browser inputs produce strings.
733
+
734
+ ## Environment
735
+
736
+ Applications register Standard Schema schemas (zod, valibot, arktype…) from the module named by
737
+ `envSchemasLocation`: `registerPrivateEnvSchema(schema)` (server only) and
738
+ `registerPublicEnvSchema(schema)` (shipped to the client), both imported from `tungsten`; either
739
+ may be omitted. Tungsten imports that module for its side effect, loads `.env` files, validates
740
+ them at startup, and injects the public values into the client bundle. The old protocol of two
741
+ named exports, `privateEnvSchema` / `publicEnvSchema`, is no longer read and fails loudly.
742
+
743
+ Access is through **ambient globals**, no import: `tungstenPrivateEnv` and `tungstenPublicEnv`.
744
+ Type them by declaration-merging `TungstenPrivateEnv` / `TungstenPublicEnv` in `declare global`.
745
+
746
+ - Never expose a private value to client code, and never copy one into the public schema or file.
747
+ - Add every new variable to the appropriate schema and to the sample env file.
748
+ - Code that runs outside Tungsten startup (standalone scripts, some test setups) must bootstrap
749
+ the globals itself before importing modules that read them.
750
+
751
+ Tungsten reads four variables of its own, which your schemas must declare like any other:
752
+ `TUNGSTEN_SERVER_PORT` (private) and `FRONTEND_PROTO` / `FRONTEND_HOSTNAME` / `FRONTEND_PORT`
753
+ (public). Both ports are validated as numbers `>= 0`.
754
+
755
+ - `TUNGSTEN_SERVER_PORT` is the port the server binds; unset means 3000. **`0` asks the OS for a
756
+ free port** (0.6.0) and `tungsten dev` reports the one it took. `tungsten start`
757
+ rejects `0` — production needs a fixed address.
758
+ - The `FRONTEND_*` trio describes the address the **browser** uses, which is not the server's own
759
+ whenever a proxy sits in front. `FRONTEND_PORT` unset or `0` means "whatever port the server
760
+ bound", so a dev server on a random port still reports a usable URL. A default port is left out
761
+ of the URL: `https://site.example`, not `https://site.example:443`.
762
+ - **`FRONTEND_HOSTNAME` was `FRONTEND_HOST`** (0.6.0). Rename it in your env files
763
+ and schema — under the old name the public address silently falls back to `localhost`.
764
+
765
+ ## Testing
766
+
767
+ **Write the failing test before the fix, and watch it fail.** Then fix, and watch it pass. A test
768
+ never seen red proves nothing — it may assert something that was already true, or never reach the
769
+ code at all. If you have already written the fix, revert it, run the test, confirm it fails for
770
+ the reason you expect, then restore it.
771
+
772
+ Two things bite here in particular. The runner executes compiled JS, not your source — an app's
773
+ test compile tree (`.tungsten/test` below), this repo's `dist/` — and a watch build may have
774
+ rebuilt your change already: check that the red run really is testing the unfixed code. And put the test at the layer the bug lives at: `createTestApp()` renders on the client
775
+ only, so a server-render or hydration bug can sit underneath a fully green suite.
776
+
777
+ Test APIs live at `tungsten/testing`, not the main entry point. The test project supplies its own
778
+ DOM implementation (happy-dom, jsdom):
779
+
780
+ ```tsx
781
+ import { Window } from 'happy-dom'
782
+ import { createTestApp } from 'tungsten/testing'
783
+
784
+ const window = new Window()
785
+ const testApp = await createTestApp(RootComponent, {
786
+ document: window.document as unknown as Document,
787
+ props: { initialName: 'Robert' },
788
+ location: '/',
789
+ })
790
+
791
+ try {
792
+ const button = testApp.container.querySelector('button')
793
+ button!.dispatchEvent(new window.Event('click') as unknown as Event)
794
+ await testApp.resolved()
795
+ expect(testApp.container.textContent).toContain('Robert')
796
+ } finally {
797
+ await testApp.unmount()
798
+ }
799
+ ```
800
+
801
+ `createTestApp()` completes the initial client render before returning. `resolved()` then waits
802
+ for Tungsten updates, DOM reconciliation, lifecycle-triggered updates, and promises returned by
803
+ registered event handlers — but not unrelated timers or network requests. Casts are only needed
804
+ when the DOM package's types are not structurally compatible with the TypeScript DOM library.
805
+ An automatically created container is appended to the supplied document's body and removed by
806
+ `unmount()`; a caller-supplied container must be empty, and is emptied but kept. Both
807
+ `createTestApp()` and `hydrateTestApp()` are async-disposable (0.6.2+), so
808
+ `await using testApp = await createTestApp(...)` unmounts at the end of the scope instead of a
809
+ `try`/`finally`. Pass `routes` (an object, or a routes function — called with `null`, as on the
810
+ client) to let components `goto()` for real (0.6.2+); without it the app has no routes and a
811
+ navigation goes nowhere. Unlike an app's routes, these need no `'404'` or `'default'` route: a miss
812
+ lands on the built-in "404 page not found", which the test's own assertions will catch. Tests
813
+ containing Tungsten JSX must be compiled through `tungsten compile` so they use Tungsten's JSX runtime and
814
+ transforms.
815
+
816
+ `hydrateTestApp()` and `hydrateTestPipelineApp()` (0.7.1+) throw at once unless `isClient` is
817
+ true: run them in a DOM suite whose `setup` puts happy-dom's `window` on the global before the tests
818
+ are imported, since tungsten reads `isClient` when it loads.
819
+
820
+ An app built with `pipeline()` or `splitPipeline()` (0.7.1+) has its own pair, imported from
821
+ `tungsten/testing/pipeline` rather than `tungsten/testing`, taking the app (the routes module's `.routes()`
822
+ export, or a smaller set built off the same stage) first:
823
+ `createTestPipelineApp(app, Root, options)` runs the app's client stages and routes `location`
824
+ against its routes; `hydrateTestPipelineApp(app, Root, options)` runs the server stages (with a
825
+ stand-in request unless `reqRes` is given), renders, carries the all-routes and networked data as the
826
+ page does, then runs the client stages and hydrates. A component whose store or markup comes from
827
+ the pipeline's context needs these: `createTestApp()` and `hydrateTestApp()` run no pipeline, so its
828
+ context is empty.
829
+
830
+ Stores are created independently per test app; initialize one with `testStore()` before the root
831
+ component starts. Component tests that need the application's env globals opt in by calling
832
+ `loadEnv({ buildLocation })` once from the test runner `setup` hook, where `buildLocation` is the
833
+ test compile's output (`tungsten compile --output .tungsten/test` → `'.tungsten/test'`): the env
834
+ schemas module is imported from that compiled tree, not the dev build's. `loadEnv()` loads env once
835
+ per process and a missing/invalid env schema module is a hard error. `TUNGSTEN_TEST_ENV` (the
836
+ variable name is exported as `testEnvVariableName`) only chooses which files: set it to prefer
837
+ `.env.test*` over the base ones. The harness itself only waits for a load `setup` started
838
+ (`waitForEnvIfLoading`), so a package that never calls `loadEnv()` loads nothing.
839
+
840
+ ### `host()` — a component without a DOM
841
+
842
+ `createTestApp()` is the harness for anything that needs a document. When the question is only
843
+ "what does this component render, and what do its handlers do", `host()` is the smaller tool: it
844
+ constructs the component with a mock construct package, awaits `init`, runs the mount lifecycles,
845
+ and hands back a wrapper.
846
+
847
+ ```ts
848
+ import { host } from 'tungsten/testing'
849
+
850
+ const hosted = await host(DataTable, { head, rows: [], columns: 2, emptyText: 'Nothing found' })
851
+
852
+ hosted.content() // content(props) or content(props, state)
853
+ await hosted.updateProps({ ...props, rows }) // re-renders, runs the update lifecycles
854
+ await hosted.updateState(state => { state.open = true })
855
+ hosted.toggleRow(3) // the component's own methods, bound
856
+ ```
857
+
858
+ - **`content()` takes no arguments.** It calls the content function with what that component's
859
+ flavour takes — `(props)` for a `TungstenSimpleComponent`, `(props, state)` for the rest — so
860
+ tests stop writing `component.content(component.props, component.state)` and stop casting when a
861
+ component's signature differs.
862
+ - **`content()` gives the whole tree, not one component's output** (the default since
863
+ 0.7.1; before, `resolve: true` asked for it). Every component in the tree is constructed and
864
+ asked for its content, down to html and text, and `content()`, `updateProps()` and
865
+ `updateState()` all hand back that tree. Resolution is async, so they are promises whatever the
866
+ component's own `content` returns — await them — and the wrapper's type is
867
+ `HostedResolved<Component>`. **`resolve: false`** hands back what the hosted component itself
868
+ returned, its child components unexpanded (`one.component('UserCard')` is there, the html it
869
+ renders is not); that `content()` is not awaited — an async `content` returns a promise, a sync
870
+ one the node, and the type (`Hosted<Component>`) says which:
871
+
872
+ ```ts
873
+ const full = await host(Page, { id: 7 })
874
+ ensure(await full.content(), [
875
+ ensure.one.component('UserCard').with.one.prop('name').is('Bob'), // node and props kept
876
+ ensure.one.html('em'), ensure.one.text('Bob'), // what it rendered
877
+ ])
878
+ ```
879
+
880
+ A component node stays in the tree as written — its type, key and props — holding what it
881
+ rendered in place of the children it was passed, so a fact reads it by name, by prop, and by what
882
+ it put on the page. A component that ignores its children shows none of them. Child components
883
+ run their real `init` and mount lifecycles against the same store, internal store and recorded
884
+ navigations — so pass the store the *children* need, even where the hosted component has none.
885
+ With a partial store, a key a child reads that the options did not supply names itself, as it
886
+ does for the hosted component, except in the child's own `init`, which has run by then. A
887
+ component the app renders on the client alone (`render`) resolves to its placeholder, its content
888
+ never run.
889
+ - **`contentToString(tree)` shows a tree as JSX** (0.7.1+, from `tungsten/testing`), to read
890
+ what a component rendered: `console.log(contentToString(await hosted.content()))`. A resolved
891
+ component's output sits under it, marked `⤵`; the children it was passed show as
892
+ `children={...}`, in full with `{ children: true }`. One unresolved nests its children as written.
893
+ A tree that renders nothing shows what it was (`{null}`, `{false}`, `{[]}`); inside a tree such
894
+ values are left out, as the page leaves them. Text with spaces at an edge is quoted (`{'Save '}`).
895
+ Fragments are hidden, their text running on as the page and `ensure()` text facts join it;
896
+ `{ fragments: true }` shows them:
897
+
898
+ ```
899
+ <div id='page'>
900
+ <Middle name='Bob'> ⤵
901
+ <p>Hi, Bob.</p>
902
+ </Middle>
903
+ </div>
904
+ ```
905
+ - **Nothing re-renders on its own.** The construct package is marked `hosted`, so state is not
906
+ proxied on either side; `updateProps` and `updateState` are the renders. `updateState` takes
907
+ `(state) => newState | void`, so mutate it or return a replacement.
908
+ - **No starting state and no app state.** The component inits and renders as it really would.
909
+ The internal store gets `location` (the option, else `/`) and empty `popMessages`, and a matched
910
+ route only if the options give one. Reading a key nothing
911
+ supplied throws, naming the key. If the component needs a real app, it needs `createTestApp()`.
912
+ - **The side is the process's.** `host()` has no client/server option: `isClient`/`isServer` are
913
+ what the framework and your components both read, so a plain node run is the server
914
+ (`serverInit`). To test the client, install a window and DOM (happy-dom, jsdom) in the test
915
+ runner's setup, before anything imports tungsten.
916
+ - `hosted.title` and `hosted.navigations` record what the component tried to do to the document
917
+ title and the router; both are recorded rather than carried out. A lifecycle failure is thrown
918
+ rather than logged, so a test cannot pass over one.
919
+ - **Literal props are typed by the component** (0.5.4+, also `createTestApp()`), so
920
+ `{ type: 'success' }` or `bind: [form, 'value']` needs no `as const`. Before 0.5.4 they widen to
921
+ `string` and arrays and fail against the component's props — annotate them. For a *generic*
922
+ component a bare tuple still widens in any version: annotate it
923
+ (`const bind: TypedBindVariable<Form, 'value'> = [form, 'value']`).
924
+ - **The third argument is the options** (`HostOptions<Component>`), required for a store
925
+ component. A component without a store of its own takes the store forms too (0.7.1+) — it
926
+ may hold children that have one, which resolving constructs against the same instance —
927
+ but with the schema unchecked, there being no class of its own to check it against. A store
928
+ component is given its store one of four ways: `store` (a real `makeStore` store, its cells
929
+ keeping state, so one can be shared between
930
+ hosts), `storeData` (a value per key, each wrapped in a cell of its own), or the partial opt-ins
931
+ `partialStore` / `partialStoreData`, where a key left out throws, naming itself, when read.
932
+ Routing: `location` (a `/path`), `routes`, and either `matchedRoute: { key, params?, data? }`
933
+ stated by the test, or `routing: true` to have `host()` match `location` against `routes`
934
+ itself. The location defaults to `/` (0.7.1+; before, it had none). There is no default
935
+ route: a component that reads one the test did not give throws, naming it. An internal-store component takes those routing options, not a store.
936
+ `HostOptions`, `HostStoreOptions`, `makeStore` and `TrueStore` are all exported from
937
+ `tungsten/testing`, so a test file needs no second import for them. For a wrapper that fixes
938
+ `resolve`, `HostResolveOptions` / `HostDontResolveOptions` (per component) and
939
+ `HostStoreResolveOptions` / `HostStoreDontResolveOptions` (per store schema) are the same
940
+ options with `resolve` settled (0.7.1+), so no distributive `Omit` is needed.
941
+ - **A real store for tests** is `makeStore(schema, new Map())`. The second argument is the
942
+ subscribed-elements map the store's cells share; a fresh `Map` is right for a test. Cells are
943
+ branded, so a hand-rolled `{ get, set, … }` object is not a `TrueStore` and will not type-check:
944
+ make one with `makeStore`, or give `host()` values through `storeData`. Stub what the schema
945
+ holds before handing it over, an API client above all:
946
+
947
+ ```ts
948
+ const schema = new StoreSchema()
949
+ schema.apiClient = stubClient // a stub, not a real client
950
+ const store = makeStore(schema, new Map())
951
+ ```
952
+ - **`serverInit` runs under `host()`.** A plain node run is the server, so a store component whose
953
+ `serverInit` calls the API reaches whatever client its store holds. Stub the client through the
954
+ store, as above.
955
+ - **Class components only.** `host()` takes a component class. A function component is a
956
+ function: call it.
957
+ - **`hostJsx()` takes the element instead** (0.7.1+), for a test that reads as the page does:
958
+
959
+ ```ts
960
+ const hosted = await hostJsx(<DataTable head={head} rows={rows} columns={2} />)
961
+ ensure(await hosted.content(), [ensure.one.html('table')])
962
+ ```
963
+
964
+ It is `host()` underneath, with the same options and the same lifecycles, given the class and
965
+ props off the element. The cost is types: TypeScript types every JSX expression as `JSX.Element`,
966
+ so the element does not carry the component it names, and the wrapper comes back as the base
967
+ component — `props` and `state` are `any`, the component's own methods are not on it, and
968
+ `content()` is `ContentNode | Promise<ContentNode>`, so `await` it. Naming the component in the
969
+ call restores all of that: `await hostJsx<DataTable>(<DataTable … />)`. Reach for `hostJsx()`
970
+ when the test asserts the content, and `host()` when it drives the component through the
971
+ wrapper's api. An element that is not a class component — an html tag, a fragment, a function
972
+ component — throws, naming what it was given. Also on `host.jsx()` (0.7.1+).
973
+ - **`host.generic()` for a component class that is generic** (0.7.1+). It takes the class in a
974
+ call of its own, then the props:
975
+
976
+ ```ts
977
+ const hosted = await host.generic(BoundValue)({ bind: [form, 'value'] })
978
+ const own = await host.generic(BoundValue, { resolve: false })({ bind: [form, 'value'] })
979
+ ```
980
+
981
+ Plain `host()` cannot infer such a class's type arguments: passed as an argument, its own type
982
+ parameters are erased to their constraints before the props are looked at, so a tuple prop like
983
+ `bind: [form, 'value']` widens to an array and the class stops fitting — `Target requires 2
984
+ element(s) but source may have fewer`. Annotating the prop (`const bind: TypedBindVariable<Form,
985
+ 'value'> = [form, 'value']`) is the way round it with `host()`; `host.generic()` needs no
986
+ annotation and checks the props against the component the way JSX does. Options go on the first
987
+ call, not the second — that is what keeps the inference working.
988
+ - **A generic helper can wrap it without a cast.** Store components resolve to an
989
+ overload whose schema is its own type parameter, inferred from the class, so a project helper
990
+ such as `<C extends TungstenBaseComponent<any, any> & { store: ComponentStore<Schema> }>(Type,
991
+ props, store) => host(Type, props, { store })` type-checks while `C` is still generic. The
992
+ store options are checked as before, and a wrong schema or a store-less component is rejected
993
+ at the `host()` call inside the helper. `HostStoreOptions<Schema>` is the options type for a
994
+ schema, for a helper that builds on it, say making `store` optional with a mock default. It is
995
+ a union (the four store forms times the two routing forms), so `Omit` it through a conditional,
996
+ which distributes; a plain `Omit<HostStoreOptions<S>, 'store'>` merges the routing variants,
997
+ `routing` widens to `boolean`, and `host()` rejects the result:
998
+
999
+ ```ts
1000
+ type OptionalStore<V> = V extends unknown ? Omit<V, 'store'> & { store?: TrueStore<S> } : never
1001
+ type Options = OptionalStore<Extract<HostStoreOptions<S>, { store: unknown }>>
1002
+ ```
1003
+
1004
+ Before this, `host()`'s argument tuple was conditional on the component, which a still-generic
1005
+ component cannot satisfy, so such helpers cast.
1006
+
1007
+ ### `ensure()` — facts about a content tree
1008
+
1009
+ `ensure()` states what a content tree holds, with no DOM and no string of HTML in between
1010
+ (0.7.1+). It takes the tree and a list of facts: a passing call is its own assertion, and a
1011
+ failure throws `EnsureError`, naming the fact and what it saw, with the `actual` / `expected` /
1012
+ `showDiff` fields a runner reads for a diff and a stack that starts at the `ensure()` call.
1013
+
1014
+ ```ts
1015
+ import { ensure } from 'tungsten/testing'
1016
+
1017
+ ensure(hosted.content(), [
1018
+ ensure.one.html('form'),
1019
+ ensure.two.html('input').with.one.prop('name'),
1020
+ ensure.count(2).text('Bob'),
1021
+ ensure.no.html('video'),
1022
+ ])
1023
+ ```
1024
+
1025
+ **The model is sightings, one flat list, a kind each.** The tree is flattened in document order into
1026
+ sightings: text, html elements by tag, components by the name stylesheets match, fragments, an
1027
+ element's classes one at a time, its id, and every prop but `children`. A fact meets only sightings
1028
+ of its own kind, and its quantifier counts the ones that *match* — so
1029
+ `no.html('a').with.one.prop('href')` says "no `a` with an href", not "no `a`". Depth does not
1030
+ matter: the walk goes through html elements, fragments and a component's children as written, so a
1031
+ fact at the top finds an element at any level.
1032
+ Text is joined as the page shows it — `Hi, {name}.` is one sighting, a number is text, a fragment
1033
+ does not break a run, an element either side of one does.
1034
+
1035
+ **Two things the walk does not enter.** A prop other than `children`: an element in
1036
+ `rows={[<tr/>]}` is a `prop:rows` sighting and nothing more until a fact opts in with `.content()`.
1037
+ And a component's rendered output where the tree does not hold it — the tree is the one given, so
1038
+ in a tree as written a component is read by name and by prop, not by what it renders. `host()`'s
1039
+ `content()` resolves by default, holding both (with `{ resolve: false }`, the tree as written); the
1040
+ facts are the same either way.
1041
+
1042
+ **Quantifiers**: `no` / `none`, `one` (`a` and `an` read the same), `two`, `three`, `count(n)`,
1043
+ `some` (one or more), `all` (every sighting of the kind matches, and there is at least one — all
1044
+ of none proves nothing). Each is on `ensure`, as `ensure.one`; the bare forms, for facts written
1045
+ inside a scope, come from the deep import:
1046
+ `import { one, two, children } from 'tungsten/testing/ensure.js'`.
1047
+
1048
+ **Ends** — what a sighting has to be:
1049
+
1050
+ - `text('Bob')`, `text.contains('ob')`, `text.matches(/^B/)`, and `textCi` for the same three
1051
+ ignoring case. `g` and `y` flags are dropped, so one regex is safe across sightings.
1052
+ - `interruptedText('BobAlice')` — an element's whole text with the markup between the words
1053
+ ignored, as `textContent` reads it; `.contains` and `.matches` too.
1054
+ - `html('button')`, `component('Badge')`, `fragment()`.
1055
+ - `select('nav > a.active')` — the elements a CSS selector matches (0.7.1+), counted by any
1056
+ quantifier and scoped like `html()`: `all.select('.bar').with.one.prop('data-t')`. Names are
1057
+ case-sensitive, and a capitalised one is a component, by the name `component()` matches, and it
1058
+ is the parent of what it holds: `select('Link > a')`, not `select('nav > a')`. Attributes are
1059
+ the props: `class` reads `className`, `for` reads `htmlFor`, `true` is present, `false`, `null`
1060
+ and `undefined` absent; a component's `className` and `id` are no class or id. `:has()`,
1061
+ `:not()` and the rest work. A scope is a tree of its own, so inside `.with.children` a selector
1062
+ sees no ancestor outside it. `select` is not taken by `with`: a match's own tag, classes and
1063
+ attributes go in its selector (`select('nav.main:has(a)')`).
1064
+ - `className('card')`, `id('page')` — one class at a time; with no argument, that it has one.
1065
+ - `prop('href')` — the prop is there, whatever its value, `undefined` included. Then `.is(value)`
1066
+ (`===`, so a handler or ref matches by reference), `.contains(text)`, `.matches(regex)`,
1067
+ `.exact(value)` (deep equal), `.subset(shape)` (extra keys and array elements pass, each shape
1068
+ element matching some element in any order), `.duck(shape)` (extra keys pass, but an array is a
1069
+ tuple: same length, in place), and `.content()` (the value is content; given facts, its tree).
1070
+
1071
+ **Scopes tie facts to one element.** Two facts in the list are independent —
1072
+ `one.html('button').with.one.prop('disabled')` and
1073
+ `one.html('button').with.children.one.text('Save')` can be met by two different buttons. To say one
1074
+ button is both, put both facts in the same scope:
1075
+
1076
+ - `.with([…])`, or chained as `.with.one.prop('name')` — properties of the match itself: `prop`,
1077
+ `className`, `id`, `interruptedText`, and `children` facts. `html`, `component`, `select`,
1078
+ `fragment`, `text` and `textCi` are not properties, so `with` does not take them (a type error;
1079
+ 0.7.1+) — what the element holds is `.with.children`.
1080
+ `.with(props => boolean)` is the escape hatch for what facts cannot say, and the function's name
1081
+ is what the failure shows.
1082
+ - `.with.children([…])`, or `.with.children.one.text('Save')` — facts on the content it holds.
1083
+ - `children([…])` and `children.one.html('li')` — the same children facts, written for a `with`
1084
+ list, so they mix with the element's own facts and all of them apply to that one element:
1085
+
1086
+ ```ts
1087
+ ensure(tree, [
1088
+ ensure.one.html('button').with([
1089
+ one.prop('disabled').is(true),
1090
+ children.one.text('Save'),
1091
+ ]),
1092
+ ])
1093
+ ```
1094
+
1095
+ A failure states the fact as prose, with the fact as written on a `rule:` line, and lists the
1096
+ sightings of its kind with the reason each one missed, down through the scopes. A reason names
1097
+ every inner fact the sighting missed, then those it met (`met: …`), and the nearest misses are
1098
+ listed first, ahead of the cap of 10. Where stdout has colour, sightings are white, misses red and
1099
+ facts met green; `{ color: false }` turns it off (0.7.1+):
1100
+
1101
+ ```
1102
+ ensure: expected one <a> with one href="/y", saw 0
1103
+ rule: one.html('a').with.one.prop('href').is('/y')
1104
+ ·
1105
+ <a> seen:
1106
+ <a href='/x'> (one href="/y": saw 0)
1107
+ ```
1108
+
1109
+ `ensure(tree, facts, { readable: false })` states it only as the fact was written:
1110
+
1111
+ ```
1112
+ ensure: expected one.html('a').with.one.prop('href').is('/y'), saw 0
1113
+ html:a seen:
1114
+ <a href='/x'> (one.prop('href').is('/y'): saw 0)
1115
+ ```
1116
+
1117
+ ### `tungsten compile` — preparing code for tests
1118
+
1119
+ A one-shot dev compilation without a server. By default it compiles JavaScript (JSX and the Babel
1120
+ transforms), stylesheets, client support files, assets and public files into `.tungsten/dev`,
1121
+ cleaning each selected target's existing output first (`--no-clean` keeps it). `--output <root>`
1122
+ selects an independent output root with the same layout (`code`, `.client`, `.static`, `.assets`,
1123
+ `.public`, `.componentCss`, `.packages`); cleaning and the package cache are scoped to it. Limit
1124
+ the work with `--js`, `--css`, `--client`, `--assets`, `--public` (`--all` is the explicit
1125
+ default). `--watch` keeps the js, css, asset and public compilers running; client support files
1126
+ are generated once. For orchestration, `--ready-file <file>` writes a small JSON file only after
1127
+ the initial compilation succeeds (removed by a failed watch cycle, recreated by the next success),
1128
+ and `--quiet` drops per-file messages but keeps errors and cycle status:
1129
+
1130
+ ```sh
1131
+ tungsten compile --watch --output .tungsten/test --ready-file .tungsten/test.ready --quiet
1132
+ ```
1133
+
1134
+ Test files are left out of every compile unless `--tests` is passed: mark them in config with
1135
+ `testDirs` / `testPatterns` (the same two shapes as `ignoreDirs` / `ignorePatterns`, which drop
1136
+ files from every pipeline; `--ignore-dirs` / `--ignore-patterns` override those two from the
1137
+ command line). **On upgrade, add `--tests` to the test compile**, or it emits no test files and
1138
+ the runner finds nothing to run. Whether that fails CI is the runner's call: sxy-test-runner 2.4.12
1139
+ exits 3 for "no test files", earlier versions used other codes, and a runner that treats it as a
1140
+ pass goes green having run nothing, so check for the "no tests" case rather than assume. `--packages` also compiles imported packages into the build's `.packages` tree for
1141
+ the dev server to serve; it is off by default, and a use for it outside the dev server has not
1142
+ come up, since compiled code left to node's own resolution needs none of it. Which sources each
1143
+ pipeline compiles is set by `clientDirs` / `clientPatterns` (compiled for the browser with every
1144
+ plugin, into `.tungsten/<mode>/code`) and `rawDirs` / `rawPatterns` (compiled for node with types
1145
+ stripped and imports untouched, into `.tungsten/<mode>-raw`, for server middleware and the like).
1146
+ Each pipeline compiles **its selection and not the ignores**: client and not ignore, raw and not
1147
+ ignore. Unset, the client selection is everything and the raw selection is nothing; a file may be
1148
+ in both. Because the ignores win, ignoring server code also removes it from the raw pipeline: that
1149
+ is fine when the app's own build emits the server into `codeDir` (e.g. `ignoreDirs: ['server']`),
1150
+ but to have Tungsten compile it, narrow `clientDirs` and put it in `rawDirs` instead (e.g.
1151
+ `clientDirs: ['client', 'common'], rawDirs: ['server', 'common']`). Keep node-only modules out
1152
+ of the client set: the client pipeline does not tolerate imports of node built-ins when it
1153
+ compiles packages, which the dev server does. `--fresh` ignores the packages and styles caches.
1154
+ `sourceMaps: true` in `tungsten.config.js` appends an inline source map to each
1155
+ compiled app file in both pipelines, for `tungsten compile` and the dev server alike, chained
1156
+ through any map the source already references (e.g. tsc's `.js.map`), so debuggers (and node run
1157
+ with `--enable-source-maps`) point at the original source. Packages in `.packages` and the
1158
+ production bundle are not mapped. `tungsten compile --source-maps` / `--no-source-maps` override
1159
+ the config entry for one compile.
1160
+
1161
+ `skipPackageCompilation` in `tungsten.config.js` (one glob or an array, matched against the
1162
+ JavaScript files Tungsten receives, extensions untranslated) names files to transform and emit
1163
+ without traversing or copying their package dependencies; bare package imports in them are left as
1164
+ they are. Example: `'**/*.domTest.js'`.
1165
+
1166
+ ## Dev server behaviour
1167
+
1168
+ Things that look like application bugs but are not:
1169
+
1170
+ - **HMR is on by default.** A source edit hot-swaps the changed component in place, keeping its
1171
+ position, props and state, and swaps CSS without a reload; anything it cannot handle falls back
1172
+ to a full page reload. The `hmr` config option picks the branch: `true` (default), `'reload'` for
1173
+ a plain full reload on every change, `false` for no live reload at all. Design and limits are in
1174
+ `docs/hmr.md`.
1175
+ - **Server modules are re-imported per request.** The dev server busts Node's ESM cache with an
1176
+ incrementing query string so server changes are picked up without a restart. Two consequences:
1177
+ - **Module-scope singletons are not singletons.** A database client or connection pool created
1178
+ at module scope in your server code is rebuilt on nearly every request, and old pools are not
1179
+ released — under load this exhausts the pool. The framework-sanctioned fix is the
1180
+ **`._cached_` filename convention**: the loader (`dist/moduleReloader.js`) exempts any module
1181
+ matching `*._cached_.{js,ts,jsx,tsx,…}` **and its dependency graph** from reloading. Put the
1182
+ client construction in e.g. `server/db/client._cached_.ts` and re-export it.
1183
+ - **A flat per-request latency** before any endpoint code runs, which grows with the number of
1184
+ first-party server modules, because the entry module's whole import graph is re-imported. It
1185
+ is the same for a database-free endpoint and for a 404. Do not chase it in application code;
1186
+ it does not exist in a production build.
1187
+ - Tungsten's own env globals *do* reload per page request, but a plain static import inside a
1188
+ middleware module resolves to the cached instance — so app-level env read at module scope by
1189
+ your own middleware still needs a restart.
1190
+ - **Startup fails loudly if the port is not fully yours** (0.6.0). Both servers
1191
+ reject on a bind error, and after binding fetch their own boot id from `/__tungsten_boot_probe__`
1192
+ over `127.0.0.1` and `::1`; if another process answers on either, or accepts and never answers,
1193
+ `tungsten dev`/`start` exits non-zero naming the address and the reason, instead of printing
1194
+ "ready" while a stale server takes the traffic. Stop the other server and start again.
1195
+ - **`TUNGSTEN_SERVER_PORT=0` starts dev on a free port** (0.6.0). The ready line
1196
+ names the port taken, and the browser opens on it. Pairing a random port with a fixed
1197
+ `FRONTEND_PORT` warns at startup: a proxy cannot be pointed at a port that changes every boot.
1198
+ - **The origin-mismatch warning compares hosts, not whole origins** (0.6.0). It
1199
+ fires when a request carries an `Origin` matching your configured frontend but arrives at a
1200
+ different host — the case where cookies silently do not travel. The scheme is ignored on purpose:
1201
+ Tungsten always serves http, so behind a TLS-terminating proxy every request would otherwise look
1202
+ like a mismatch, which is what earlier versions reported. `suppressOriginMismatchWarning: true`
1203
+ turns it off; the production server repeats it at most once a day per host.
1204
+
1205
+ ## Everyday workflow
1206
+
1207
+ 1. Add or adjust the API endpoint and its tests.
1208
+ 2. Add the route and its `data` loader for the page's initial read.
1209
+ 3. Build the page as a typed class component; use the store wrapper only if it needs shared state.
1210
+ 4. Bind ordinary form fields; call the API from event handlers for mutations.
1211
+ 5. Handle every result status the endpoint can return, then refresh local or route data.
1212
+ 6. Type-check, then run the test suite.
1213
+
1214
+ ## Things to avoid
1215
+
1216
+ The principle behind most of these: **do not deviate between the server render and the client
1217
+ render.** In the normal flow the two are identical, and hydration depends on it. The only sanctioned
1218
+ deviations are updates after the first render, `DocInit()` mutations made before hydration, and
1219
+ client-only rendering — `csr: true` in config (the body ships empty and the client mounts fresh),
1220
+ `serverSideRouting: false` (the head's children too, and the client fetches the data), or a
1221
+ component's `static render = 'client'` / `static render = { location: 'client', placeholder }`
1222
+ (the server emits the placeholder, the client renders the component). Anything else that makes
1223
+ the first client render differ from the server's markup is a bug, not a technique.
1224
+
1225
+ - Do not use `setState`, immutable state updates, or React hooks. Mutate state directly.
1226
+ - Do not mutate state inside `content` or anything it calls.
1227
+ - Do not write a descendant selector for markup a **child component** renders — name the
1228
+ component as the selector instead. Check this every time you style what turns out to be a
1229
+ component.
1230
+ - Do not rely on tag-vs-class specificity in scoped styles; they are equal.
1231
+ - Do not fetch a page's initial data only in `clientInit` — it loses SSR and the first render.
1232
+ - Do not touch browser globals in `init`, route code, or `content` without a guard; use
1233
+ `clientInit`.
1234
+ - Do not produce environment-dependent output during render — it breaks hydration.
1235
+ - Do not bypass `<Link>` for a path that should stay client-side.
1236
+ - Do not put page-local state, or fetched records, in the global store to avoid passing props.
1237
+ - Do not set `title` on anything but a page component.
1238
+ - Do not create module-scope connections or caches in server code without the `._cached_`
1239
+ convention.
1240
+ - Do not put event handlers in `<head>` — it is not hydrated.
1241
+ - Do not use `dangerouslySetInnerHTML` for inline `<script>`/`<style>` code — their children are
1242
+ not escaped; write `<script>{code}</script>`. And do not expect an attribute a boot script sets on
1243
+ `<html>`/`<body>` to survive hydration unless the root component renders it too.
1244
+ - Do not "correct" `.jsx`/`.js` import extensions in `.tsx` source.
1245
+ - Do not treat the published documentation's names and examples as definitive over the installed
1246
+ declarations.