tungsten 0.3.1 → 0.3.3

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 (291) hide show
  1. package/dist/.build-complete +1 -0
  2. package/dist/Components.d.ts +2 -1
  3. package/dist/Components.d.ts.map +1 -1
  4. package/dist/Components.js +10 -1
  5. package/dist/DomRegion.js +1 -1
  6. package/dist/Framework.d.ts +2 -0
  7. package/dist/Framework.d.ts.map +1 -1
  8. package/dist/Framework.js +68 -57
  9. package/dist/FrameworkConfig.d.ts +6 -0
  10. package/dist/FrameworkConfig.d.ts.map +1 -1
  11. package/dist/FunctionComponentStandIn.d.ts +2 -0
  12. package/dist/FunctionComponentStandIn.d.ts.map +1 -1
  13. package/dist/FunctionComponentStandIn.js +10 -1
  14. package/dist/Instance/Instance.d.ts.map +1 -1
  15. package/dist/Instance/Instance.js +18 -1
  16. package/dist/Instance/InstanceConfig.d.ts +1 -0
  17. package/dist/Instance/InstanceConfig.d.ts.map +1 -1
  18. package/dist/PopMessage.type.d.ts +6 -0
  19. package/dist/PopMessage.type.d.ts.map +1 -0
  20. package/dist/PopMessage.type.js +1 -0
  21. package/dist/Servers/DevServer.d.ts +4 -0
  22. package/dist/Servers/DevServer.d.ts.map +1 -1
  23. package/dist/Servers/DevServer.js +116 -13
  24. package/dist/Servers/MatchedRouteInfo.d.ts +1 -1
  25. package/dist/Servers/MatchedRouteInfo.d.ts.map +1 -1
  26. package/dist/Servers/ProdServer.d.ts +1 -0
  27. package/dist/Servers/ProdServer.d.ts.map +1 -1
  28. package/dist/Servers/ProdServer.js +79 -5
  29. package/dist/Servers/devErrorPageAutoRetry.d.ts +4 -0
  30. package/dist/Servers/devErrorPageAutoRetry.d.ts.map +1 -0
  31. package/dist/Servers/devErrorPageAutoRetry.js +26 -0
  32. package/dist/Servers/hono/DevServerHono.js +25 -29
  33. package/dist/Servers/hono/ProdServerHono.js +49 -142
  34. package/dist/Servers/hono/TungstenServerHono.d.ts +1 -1
  35. package/dist/Servers/hono/TungstenServerHono.d.ts.map +1 -1
  36. package/dist/Servers/hono/TungstenServerHono.js +12 -29
  37. package/dist/Servers/routing.d.ts.map +1 -1
  38. package/dist/Servers/routing.js +10 -6
  39. package/dist/Servers/tungstenCorePrivateEnvSchema.d.ts +0 -3
  40. package/dist/Servers/tungstenCorePrivateEnvSchema.d.ts.map +1 -1
  41. package/dist/Servers/tungstenCorePrivateEnvSchema.js +3 -3
  42. package/dist/Servers/tungstenCorePublicEnvSchema.d.ts +8 -0
  43. package/dist/Servers/tungstenCorePublicEnvSchema.d.ts.map +1 -0
  44. package/dist/Servers/tungstenCorePublicEnvSchema.js +14 -0
  45. package/dist/Store/InternalStoreSchema.d.ts +2 -0
  46. package/dist/Store/InternalStoreSchema.d.ts.map +1 -1
  47. package/dist/Store/InternalStoreSchema.js +1 -0
  48. package/dist/api/Request.d.ts +1 -1
  49. package/dist/api/Request.d.ts.map +1 -1
  50. package/dist/api/Response.d.ts +1 -1
  51. package/dist/api/Response.d.ts.map +1 -1
  52. package/dist/api/getExport.d.ts.map +1 -1
  53. package/dist/api/getExport.js +2 -1
  54. package/dist/babelPlugins/BabelAddDevCacheBust.d.ts +11 -0
  55. package/dist/babelPlugins/BabelAddDevCacheBust.d.ts.map +1 -0
  56. package/dist/babelPlugins/BabelAddDevCacheBust.js +68 -0
  57. package/dist/babelPlugins/BabelTypes.d.ts +2 -2
  58. package/dist/babelPlugins/BabelTypes.d.ts.map +1 -1
  59. package/dist/babelPlugins/BabelTypes.js +0 -1
  60. package/dist/bin/commands/build.d.ts.map +1 -1
  61. package/dist/bin/commands/build.js +3 -0
  62. package/dist/bin/commands/dev.d.ts.map +1 -1
  63. package/dist/bin/commands/dev.js +16 -3
  64. package/dist/bin/commands/devmon.d.ts +2 -0
  65. package/dist/bin/commands/devmon.d.ts.map +1 -0
  66. package/dist/bin/commands/devmon.js +66 -0
  67. package/dist/bin/index.d.ts.map +1 -1
  68. package/dist/bin/index.js +6 -0
  69. package/dist/booleanAttributeValue.d.ts +21 -0
  70. package/dist/booleanAttributeValue.d.ts.map +1 -0
  71. package/dist/booleanAttributeValue.js +23 -0
  72. package/dist/build/compileJavascript.js +1 -1
  73. package/dist/build/compileUserApp.d.ts +2 -1
  74. package/dist/build/compileUserApp.d.ts.map +1 -1
  75. package/dist/build/compileUserApp.js +10 -9
  76. package/dist/build/createInstanceConfigFile.js +1 -1
  77. package/dist/build/makeBabelUserDevConfig.d.ts.map +1 -1
  78. package/dist/build/makeBabelUserDevConfig.js +6 -1
  79. package/dist/compat/types.d.ts +1 -1
  80. package/dist/compat/types.d.ts.map +1 -1
  81. package/dist/components/PopMessages.d.ts +12 -0
  82. package/dist/components/PopMessages.d.ts.map +1 -0
  83. package/dist/components/PopMessages.js +45 -0
  84. package/dist/defaultConfig.d.ts +3 -0
  85. package/dist/defaultConfig.d.ts.map +1 -1
  86. package/dist/defaultConfig.js +4 -1
  87. package/dist/env/Interfaces.d.ts +2 -1
  88. package/dist/env/Interfaces.d.ts.map +1 -1
  89. package/dist/env/getEnvSchemas.d.ts +1 -1
  90. package/dist/env/getEnvSchemas.d.ts.map +1 -1
  91. package/dist/env/getEnvSchemas.js +2 -1
  92. package/dist/env/publicEnv.d.ts.map +1 -1
  93. package/dist/env/publicEnv.js +4 -1
  94. package/dist/frameworkConfigSchema.d.ts +4 -0
  95. package/dist/frameworkConfigSchema.d.ts.map +1 -1
  96. package/dist/frameworkConfigSchema.js +2 -1
  97. package/dist/getComponent.d.ts.map +1 -1
  98. package/dist/getComponent.js +2 -1
  99. package/dist/getRoutes.d.ts.map +1 -1
  100. package/dist/getRoutes.js +2 -1
  101. package/dist/hmr/applyHmrModule.d.ts +11 -0
  102. package/dist/hmr/applyHmrModule.d.ts.map +1 -0
  103. package/dist/hmr/applyHmrModule.js +78 -0
  104. package/dist/hmr/componentIdentity.d.ts +9 -0
  105. package/dist/hmr/componentIdentity.d.ts.map +1 -0
  106. package/dist/hmr/componentIdentity.js +17 -0
  107. package/dist/hmr/instanceRegistry.d.ts +4 -0
  108. package/dist/hmr/instanceRegistry.d.ts.map +1 -0
  109. package/dist/hmr/instanceRegistry.js +6 -0
  110. package/dist/hmr/liveReloadHub.d.ts +10 -0
  111. package/dist/hmr/liveReloadHub.d.ts.map +1 -0
  112. package/dist/{dev → hmr}/liveReloadHub.js +8 -2
  113. package/dist/hmr/refreshStylesheets.d.ts +2 -0
  114. package/dist/hmr/refreshStylesheets.d.ts.map +1 -0
  115. package/dist/hmr/refreshStylesheets.js +26 -0
  116. package/dist/hooks/index.d.ts +1 -0
  117. package/dist/hooks/index.d.ts.map +1 -1
  118. package/dist/hooks/index.js +1 -0
  119. package/dist/hooks/useInstance.d.ts +3 -0
  120. package/dist/hooks/useInstance.d.ts.map +1 -0
  121. package/dist/hooks/useInstance.js +4 -0
  122. package/dist/hydrate/hydrate.d.ts +10 -0
  123. package/dist/hydrate/hydrate.d.ts.map +1 -1
  124. package/dist/hydrate/hydrate.js +81 -19
  125. package/dist/hydrate/reconcileHydratedAttributes.d.ts.map +1 -1
  126. package/dist/hydrate/reconcileHydratedAttributes.js +14 -5
  127. package/dist/hydrate/renderHydrationMismatchBanner.d.ts +7 -0
  128. package/dist/hydrate/renderHydrationMismatchBanner.d.ts.map +1 -0
  129. package/dist/hydrate/renderHydrationMismatchBanner.js +69 -0
  130. package/dist/importDevModuleWithRetry.d.ts +3 -0
  131. package/dist/importDevModuleWithRetry.d.ts.map +1 -0
  132. package/dist/importDevModuleWithRetry.js +46 -0
  133. package/dist/indexInner.d.ts +8 -0
  134. package/dist/indexInner.d.ts.map +1 -1
  135. package/dist/indexInner.js +7 -1
  136. package/dist/makeInstanceConfigFromFrameworkConfig.d.ts.map +1 -1
  137. package/dist/makeInstanceConfigFromFrameworkConfig.js +2 -1
  138. package/dist/pop.d.ts +10 -0
  139. package/dist/pop.d.ts.map +1 -0
  140. package/dist/pop.js +49 -0
  141. package/dist/rawHtml/filterContentNode.d.ts +20 -0
  142. package/dist/rawHtml/filterContentNode.d.ts.map +1 -0
  143. package/dist/rawHtml/filterContentNode.js +96 -0
  144. package/dist/rawHtml/parseHtml.d.ts +14 -0
  145. package/dist/rawHtml/parseHtml.d.ts.map +1 -1
  146. package/dist/rawHtml/parseHtml.js +4 -4
  147. package/dist/rawHtml/sanitizeHtml.d.ts +17 -0
  148. package/dist/rawHtml/sanitizeHtml.d.ts.map +1 -0
  149. package/dist/rawHtml/sanitizeHtml.js +19 -0
  150. package/dist/render/HeadData.d.ts +2 -0
  151. package/dist/render/HeadData.d.ts.map +1 -1
  152. package/dist/render/contentNodeToHtml.d.ts +18 -0
  153. package/dist/render/contentNodeToHtml.d.ts.map +1 -0
  154. package/dist/render/contentNodeToHtml.js +66 -0
  155. package/dist/render/contentToHtml.d.ts.map +1 -1
  156. package/dist/render/contentToHtml.js +3 -1
  157. package/dist/render/processHead.d.ts.map +1 -1
  158. package/dist/render/processHead.js +53 -6
  159. package/dist/render/renderingTreeToHtml.d.ts.map +1 -1
  160. package/dist/render/renderingTreeToHtml.js +8 -1
  161. package/dist/routing/ErrorRouteType.d.ts +6 -0
  162. package/dist/routing/ErrorRouteType.d.ts.map +1 -0
  163. package/dist/routing/ErrorRouteType.js +1 -0
  164. package/dist/routing/RouteTypes.d.ts +2 -1
  165. package/dist/routing/RouteTypes.d.ts.map +1 -1
  166. package/dist/routing/Routes.d.ts +5 -0
  167. package/dist/routing/Routes.d.ts.map +1 -1
  168. package/dist/routing/TungstenErrorInfo.d.ts +6 -0
  169. package/dist/routing/TungstenErrorInfo.d.ts.map +1 -0
  170. package/dist/routing/TungstenErrorInfo.js +6 -0
  171. package/dist/style/styleContentSxy.js +21 -0
  172. package/dist/testing/hydrateTestApp.d.ts +38 -0
  173. package/dist/testing/hydrateTestApp.d.ts.map +1 -0
  174. package/dist/testing/hydrateTestApp.js +242 -0
  175. package/dist/testing/index.d.ts +4 -3
  176. package/dist/testing/index.d.ts.map +1 -1
  177. package/dist/testing/index.js +16 -2
  178. package/dist/timeProfiler.d.ts +6 -0
  179. package/dist/timeProfiler.d.ts.map +1 -0
  180. package/dist/timeProfiler.js +31 -0
  181. package/dist/update/integrateUpdateVdom.d.ts +16 -1
  182. package/dist/update/integrateUpdateVdom.d.ts.map +1 -1
  183. package/dist/update/integrateUpdateVdom.js +116 -7
  184. package/dist/warnOnFrameworkChange.d.ts +2 -0
  185. package/dist/warnOnFrameworkChange.d.ts.map +1 -0
  186. package/dist/warnOnFrameworkChange.js +27 -0
  187. package/package.json +18 -16
  188. package/dist/Instance/gotoRedirect.sxyt.d.ts +0 -2
  189. package/dist/Instance/gotoRedirect.sxyt.d.ts.map +0 -1
  190. package/dist/Instance/gotoRedirect.sxyt.js +0 -148
  191. package/dist/Instance/runTrackedWork.sxyt.d.ts +0 -2
  192. package/dist/Instance/runTrackedWork.sxyt.d.ts.map +0 -1
  193. package/dist/Instance/runTrackedWork.sxyt.js +0 -35
  194. package/dist/_client/cjsHelpers.sxyt.d.ts +0 -2
  195. package/dist/_client/cjsHelpers.sxyt.d.ts.map +0 -1
  196. package/dist/_client/cjsHelpers.sxyt.js +0 -45
  197. package/dist/auth/passwordHashing.sxyt.d.ts +0 -2
  198. package/dist/auth/passwordHashing.sxyt.d.ts.map +0 -1
  199. package/dist/auth/passwordHashing.sxyt.js +0 -46
  200. package/dist/auth/ref/checkPasswordResetToken.sxyt.d.ts +0 -2
  201. package/dist/auth/ref/checkPasswordResetToken.sxyt.d.ts.map +0 -1
  202. package/dist/auth/ref/checkPasswordResetToken.sxyt.js +0 -40
  203. package/dist/auth/ref/createPasswordResetToken.sxyt.d.ts +0 -2
  204. package/dist/auth/ref/createPasswordResetToken.sxyt.d.ts.map +0 -1
  205. package/dist/auth/ref/createPasswordResetToken.sxyt.js +0 -10
  206. package/dist/babelPlugins/fixImportsInSassErrorMessage.sxyt.d.ts +0 -2
  207. package/dist/babelPlugins/fixImportsInSassErrorMessage.sxyt.d.ts.map +0 -1
  208. package/dist/babelPlugins/fixImportsInSassErrorMessage.sxyt.js +0 -108
  209. package/dist/babelPlugins/transformPackageImports/resolveBareImport.sxyt.d.ts +0 -2
  210. package/dist/babelPlugins/transformPackageImports/resolveBareImport.sxyt.d.ts.map +0 -1
  211. package/dist/babelPlugins/transformPackageImports/resolveBareImport.sxyt.js +0 -104
  212. package/dist/bin/commands/compile.sxyt.d.ts +0 -2
  213. package/dist/bin/commands/compile.sxyt.d.ts.map +0 -1
  214. package/dist/bin/commands/compile.sxyt.js +0 -255
  215. package/dist/build/modifySassImports.sxyt.d.ts +0 -2
  216. package/dist/build/modifySassImports.sxyt.d.ts.map +0 -1
  217. package/dist/build/modifySassImports.sxyt.js +0 -55
  218. package/dist/build/sassImportParser.sxyt.d.ts +0 -2
  219. package/dist/build/sassImportParser.sxyt.d.ts.map +0 -1
  220. package/dist/build/sassImportParser.sxyt.js +0 -110
  221. package/dist/build/scssImportParserChatgpt.sxyt.d.ts +0 -2
  222. package/dist/build/scssImportParserChatgpt.sxyt.d.ts.map +0 -1
  223. package/dist/build/scssImportParserChatgpt.sxyt.js +0 -120
  224. package/dist/clone.sxyt.d.ts +0 -2
  225. package/dist/clone.sxyt.d.ts.map +0 -1
  226. package/dist/clone.sxyt.js +0 -24
  227. package/dist/componentProxyHandlers.sxyt.d.ts +0 -2
  228. package/dist/componentProxyHandlers.sxyt.d.ts.map +0 -1
  229. package/dist/componentProxyHandlers.sxyt.js +0 -130
  230. package/dist/components/RawHtml.sxyt.d.ts +0 -2
  231. package/dist/components/RawHtml.sxyt.d.ts.map +0 -1
  232. package/dist/components/RawHtml.sxyt.js +0 -104
  233. package/dist/components/bindable/BindableSelect.sxyt.d.ts +0 -2
  234. package/dist/components/bindable/BindableSelect.sxyt.d.ts.map +0 -1
  235. package/dist/components/bindable/BindableSelect.sxyt.js +0 -186
  236. package/dist/dev/liveReloadHub.d.ts +0 -6
  237. package/dist/dev/liveReloadHub.d.ts.map +0 -1
  238. package/dist/env/injectEnv.sxyt.d.ts +0 -2
  239. package/dist/env/injectEnv.sxyt.d.ts.map +0 -1
  240. package/dist/env/injectEnv.sxyt.js +0 -20
  241. package/dist/env/parseEnv.sxyt.d.ts +0 -2
  242. package/dist/env/parseEnv.sxyt.d.ts.map +0 -1
  243. package/dist/env/parseEnv.sxyt.js +0 -11
  244. package/dist/findProjectDir.sxyt.d.ts +0 -2
  245. package/dist/findProjectDir.sxyt.d.ts.map +0 -1
  246. package/dist/findProjectDir.sxyt.js +0 -82
  247. package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts +0 -2
  248. package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts.map +0 -1
  249. package/dist/hydrate/reconcileHydratedAttributes.sxyt.js +0 -173
  250. package/dist/innerHtmlFromProps.sxyt.d.ts +0 -2
  251. package/dist/innerHtmlFromProps.sxyt.d.ts.map +0 -1
  252. package/dist/innerHtmlFromProps.sxyt.js +0 -99
  253. package/dist/postcssPlugins/parseSelectors.sxyt.d.ts +0 -2
  254. package/dist/postcssPlugins/parseSelectors.sxyt.d.ts.map +0 -1
  255. package/dist/postcssPlugins/parseSelectors.sxyt.js +0 -62
  256. package/dist/postcssPlugins/parseSelectorsRich.sxyt.d.ts +0 -2
  257. package/dist/postcssPlugins/parseSelectorsRich.sxyt.d.ts.map +0 -1
  258. package/dist/postcssPlugins/parseSelectorsRich.sxyt.js +0 -155
  259. package/dist/rawHtml/parseHtml.sxyt.d.ts +0 -2
  260. package/dist/rawHtml/parseHtml.sxyt.d.ts.map +0 -1
  261. package/dist/rawHtml/parseHtml.sxyt.js +0 -239
  262. package/dist/render/getFunctionBodyAsString.sxyt.d.ts +0 -2
  263. package/dist/render/getFunctionBodyAsString.sxyt.d.ts.map +0 -1
  264. package/dist/render/getFunctionBodyAsString.sxyt.js +0 -37
  265. package/dist/render/processProps.sxyt.d.ts +0 -2
  266. package/dist/render/processProps.sxyt.d.ts.map +0 -1
  267. package/dist/render/processProps.sxyt.js +0 -90
  268. package/dist/routing/compareRequestToRegex.sxyt.d.ts +0 -2
  269. package/dist/routing/compareRequestToRegex.sxyt.d.ts.map +0 -1
  270. package/dist/routing/compareRequestToRegex.sxyt.js +0 -65
  271. package/dist/routing/getPathPriority.sxyt.d.ts +0 -2
  272. package/dist/routing/getPathPriority.sxyt.d.ts.map +0 -1
  273. package/dist/routing/getPathPriority.sxyt.js +0 -24
  274. package/dist/routing/matchPath.sxyt.d.ts +0 -2
  275. package/dist/routing/matchPath.sxyt.d.ts.map +0 -1
  276. package/dist/routing/matchPath.sxyt.js +0 -41
  277. package/dist/routing/patternToRegex.sxyt.d.ts +0 -2
  278. package/dist/routing/patternToRegex.sxyt.d.ts.map +0 -1
  279. package/dist/routing/patternToRegex.sxyt.js +0 -69
  280. package/dist/routing/splitRequest.sxyt.d.ts +0 -2
  281. package/dist/routing/splitRequest.sxyt.d.ts.map +0 -1
  282. package/dist/routing/splitRequest.sxyt.js +0 -47
  283. package/dist/style/jssToCss.sxyt.d.ts +0 -2
  284. package/dist/style/jssToCss.sxyt.d.ts.map +0 -1
  285. package/dist/style/jssToCss.sxyt.js +0 -17
  286. package/dist/testing/createTestApp.sxyt.d.ts +0 -2
  287. package/dist/testing/createTestApp.sxyt.d.ts.map +0 -1
  288. package/dist/testing/createTestApp.sxyt.js +0 -470
  289. package/dist/testing/ensureTestEnvLoaded.sxyt.d.ts +0 -2
  290. package/dist/testing/ensureTestEnvLoaded.sxyt.d.ts.map +0 -1
  291. package/dist/testing/ensureTestEnvLoaded.sxyt.js +0 -20
@@ -0,0 +1,69 @@
1
+ // Read by the CSS rule below via attr() - not by Tungsten. Exported so an
2
+ // application that wants different styling can target the same selector.
3
+ export const hydrationMismatchAttribute = 'data-tungsten-hydration-mismatches';
4
+ const styleId = 'tungsten-hydration-mismatches-style';
5
+ // Reports hydration mismatches in the page, not only the console, without
6
+ // adding a real DOM node to the hydrated tree.
7
+ //
8
+ // A real element - even one appended after hydration finishes and never
9
+ // touched again by this code - is not actually safe here. Every container
10
+ // Tungsten re-renders gets a fresh `new DomRegion(containerDomNode)` built
11
+ // from whatever is *actually* in its childNodes at that moment, and every
12
+ // position Tungsten writes to next is a raw index into that same list
13
+ // (DomPointer.get/next/prev, and several inline `childNodes[domPointer.index]`
14
+ // reads in integrateUpdateVdom.ts/hydrate.tsx). An extra, untracked child
15
+ // shifts every index after it, so the next update touching that container
16
+ // - body, in a typical app - would compare and write to the wrong nodes.
17
+ // There is no flag on an element that makes the walker skip it; it would
18
+ // have to be built into DomPointer/DomRegion and every direct childNodes
19
+ // access alongside them, which is a core-framework change, not a banner.
20
+ //
21
+ // Attributes are a different story: reconcileHydratedAttributes only runs
22
+ // once, during hydration, before this ever runs, and every later update
23
+ // (updateAttributes) diffs against domNode.__tungsten_oldAttributes - its
24
+ // own private record of what it last set - never the DOM's live attribute
25
+ // list. A plain setAttribute() here is in neither its old props nor its new
26
+ // props, so it is never visited, and never removed.
27
+ //
28
+ // So: set an attribute on <html> (outside Tungsten's own bookkeeping, so
29
+ // permanently untouched by it) and let a `content: attr(...)` pseudo-element
30
+ // display it. A pseudo-element is not a real node - never in .childNodes,
31
+ // never visited by DomPointer or DomRegion - so this carries zero index risk
32
+ // for the app's own reconciliation, at the cost of one small, deliberately
33
+ // hacky wrinkle: a single <style> tag inserted into <head> once.
34
+ //
35
+ // head is not structurally exempt from diffing in general - it currently
36
+ // just never gets diffed node-by-node during hydration (hydrateNodeSingle's
37
+ // 'head' case skips it outright, a known hack, not a guarantee), and nothing
38
+ // in a typical app changes head's vdom children after mount, so the general
39
+ // update path never exercises it either. That could change. What head does
40
+ // already have is a marker for exactly this situation - data--t-persist,
41
+ // used by processHead.ts for framework-injected head content (the entry
42
+ // script, the component CSS link) that isn't part of the app's own vdom and
43
+ // must survive hydration - so this style tag carries it too, rather than
44
+ // riding on the blanket skip alone.
45
+ export function renderHydrationMismatchBanner(mismatches, doc = document) {
46
+ if (mismatches.length === 0) return;
47
+ if (doc.getElementById(styleId) === null) {
48
+ const style = doc.createElement('style');
49
+ style.id = styleId;
50
+ style.setAttribute('data--t-persist', 'true');
51
+ style.textContent = `
52
+ html[${hydrationMismatchAttribute}]::before {
53
+ content: attr(${hydrationMismatchAttribute});
54
+ display: block;
55
+ background: #7a1f1f;
56
+ color: #fff;
57
+ font: 13px/1.5 monospace;
58
+ padding: 8px 12px;
59
+ max-height: 33vh;
60
+ overflow: auto;
61
+ white-space: pre-wrap;
62
+ }
63
+ `;
64
+ doc.head.appendChild(style);
65
+ }
66
+ const heading = `Tungsten: ${mismatches.length} hydration mismatch${mismatches.length === 1 ? '' : 'es'} (dev only)`;
67
+ const lines = mismatches.map(mismatch => `<${mismatch.tagName}> ${mismatch.name}: ` + `rendered ${JSON.stringify(mismatch.expected)}, server sent ${JSON.stringify(mismatch.actual)}`);
68
+ doc.documentElement.setAttribute(hydrationMismatchAttribute, [heading, ...lines].join('\n'));
69
+ }
@@ -0,0 +1,3 @@
1
+ export declare function isTransientDevModuleError(e: unknown): boolean;
2
+ export declare function importDevModuleWithRetry(url: string, attempts?: number, delayMs?: number): Promise<any>;
3
+ //# sourceMappingURL=importDevModuleWithRetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importDevModuleWithRetry.d.ts","sourceRoot":"","sources":["../src/importDevModuleWithRetry.ts"],"names":[],"mappings":"AAuBA,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAU7D;AAMD,wBAAsB,wBAAwB,CAC1C,GAAG,EAAE,MAAM,EACX,QAAQ,SAAK,EACb,OAAO,SAAM,GACd,OAAO,CAAC,GAAG,CAAC,CAUd"}
@@ -0,0 +1,46 @@
1
+ // importDevModuleWithRetry
2
+ // exports importDevModuleWithRetry (function)
3
+ //
4
+ // Dev-mode dynamic import wrapper. tsc's own --watch mode emits a multi-file
5
+ // edit (e.g. a rename spanning two files) one recompiled file at a time, so
6
+ // there's a real window where one file has landed and the other hasn't yet.
7
+ // A dev-server request for a component in that window hits a genuine (but
8
+ // self-resolving within a few hundred ms) module-shape error - without this,
9
+ // that's a scary SSR error page for what is, from the developer's
10
+ // perspective, a completely normal edit. Retrying a few times with a fresh
11
+ // cache-busting query per attempt rides out the window; a real, permanent
12
+ // error still surfaces (just slightly slower) once attempts are exhausted.
13
+ // A two-file edit's compile cycle runs tsc's incremental emit followed by a
14
+ // separate downstream babel recompile, sequentially - observed live to take
15
+ // several seconds under load, so the default budget below is generous rather
16
+ // than tuned to a happy-path guess. There's no precise "compile cycle
17
+ // finished" signal plumbed through to here to await instead; this is a
18
+ // bounded heuristic, not a proper synchronization fix.
19
+ // Exported so other dev-only tools (the error page's auto-retry script - see
20
+ // devErrorPageAutoRetry.ts) can recognize the same transient shape without
21
+ // duplicating the heuristic.
22
+ export function isTransientDevModuleError(e) {
23
+ if (!(e instanceof Error)) return false;
24
+ if (e.code === 'ERR_MODULE_NOT_FOUND') return true;
25
+ if (/does not provide an export named/.test(e.message)) return true;
26
+ // A within-file version of the same race: a multi-step edit can transiently save a file
27
+ // whose import line was updated but whose JSX/body still references the old local name -
28
+ // that's a plain runtime ReferenceError, not an ESM linking error, but self-resolves the same
29
+ // way once the file's own recompile catches up. Kept narrow (a bare identifier only) so it
30
+ // doesn't swallow other ReferenceError shapes that are more likely to be genuine bugs.
31
+ return e instanceof ReferenceError && /^\w+ is not defined$/.test(e.message);
32
+ }
33
+ function delay(ms) {
34
+ return new Promise(resolve => setTimeout(resolve, ms));
35
+ }
36
+ export async function importDevModuleWithRetry(url, attempts = 30, delayMs = 250) {
37
+ for (let attempt = 0;; attempt++) {
38
+ try {
39
+ const attemptUrl = attempt === 0 ? url : `${url}${url.includes('?') ? '&' : '?'}retryAttempt=${attempt}`;
40
+ return await import(attemptUrl); // eslint-disable-line @typescript-eslint/no-unsafe-return
41
+ } catch (e) {
42
+ if (attempt >= attempts - 1 || !isTransientDevModuleError(e)) throw e;
43
+ await delay(delayMs);
44
+ }
45
+ }
46
+ }
@@ -16,13 +16,18 @@ export * from './components/bindable/BindableTextarea.jsx';
16
16
  export * from './components/GoToComponent.jsx';
17
17
  export * from './components/Lazy.js';
18
18
  export * from './components/Link.jsx';
19
+ export * from './components/PopMessages.jsx';
19
20
  export * from './components/RawHtml.jsx';
20
21
  export * from './components/RouteRedirectComponent.jsx';
21
22
  export { parseHtml, type ParseHtmlOptions } from './rawHtml/parseHtml.js';
22
23
  export { safeHtmlAllowList } from './rawHtml/safeHtmlAllowList.js';
23
24
  export type { HtmlAllowList } from './rawHtml/HtmlAllowList.js';
25
+ export { sanitizeHtml, type SanitizeHtmlOptions } from './rawHtml/sanitizeHtml.js';
26
+ export { filterContentNode } from './rawHtml/filterContentNode.js';
27
+ export { contentNodeToHtml } from './render/contentNodeToHtml.js';
24
28
  export * from './components/WebRedirectComponent.jsx';
25
29
  export * from './nbsp.js';
30
+ export { pop, dismissPop } from './pop.js';
26
31
  export * from './browserStorage.js';
27
32
  export { createElement } from './createElement.js';
28
33
  export * from './createRef.js';
@@ -64,12 +69,15 @@ export * from './ContentNode.js';
64
69
  export type { GenericObject } from './GenericObject.type.js';
65
70
  export type { Instance } from './Instance/Instance.js';
66
71
  export type { Key } from './Key.js';
72
+ export type { PopMessage } from './PopMessage.type.js';
67
73
  export type { ReqRes, ReqResOrNull } from './ReqRes.js';
68
74
  export type { Params } from './routing/Params.js';
69
75
  export type { ParamsFromPath } from './routing/ParamsFromPath.js';
70
76
  export type { RouteType, Route } from './routing/RouteType.js';
71
77
  export { type RouteKey, type Path, type Path as RoutePath, StringIsPath, RouteKeyIsPath } from './routing/RouteTypes.js';
72
78
  export type { Routes, CustomRoutes } from './routing/Routes.js';
79
+ export type { ErrorRouteType } from './routing/ErrorRouteType.js';
80
+ export { type TungstenErrorInfo, makeTungstenErrorInfo } from './routing/TungstenErrorInfo.js';
73
81
  export type { RouteParams } from './routing/RouteParams.type.js';
74
82
  export type { RouteData } from './routing/RouteData.js';
75
83
  export type { RouteDataFunction } from './routing/RouteDataFunction.type.js';
@@ -1 +1 @@
1
- {"version":3,"file":"indexInner.d.ts","sourceRoot":"","sources":["../src/indexInner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indexInner.d.ts","sourceRoot":"","sources":["../src/indexInner.ts"],"names":[],"mappings":"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"}
@@ -21,12 +21,17 @@ export * from "./components/bindable/BindableTextarea.js";
21
21
  export * from "./components/GoToComponent.js";
22
22
  export * from "./components/Lazy.js";
23
23
  export * from "./components/Link.js";
24
+ export * from "./components/PopMessages.js";
24
25
  export * from "./components/RawHtml.js";
25
26
  export * from "./components/RouteRedirectComponent.js";
26
27
  export { parseHtml } from "./rawHtml/parseHtml.js";
27
28
  export { safeHtmlAllowList } from "./rawHtml/safeHtmlAllowList.js";
29
+ export { sanitizeHtml } from "./rawHtml/sanitizeHtml.js";
30
+ export { filterContentNode } from "./rawHtml/filterContentNode.js";
31
+ export { contentNodeToHtml } from "./render/contentNodeToHtml.js";
28
32
  export * from "./components/WebRedirectComponent.js";
29
33
  export * from "./nbsp.js";
34
+ export { pop, dismissPop } from "./pop.js";
30
35
  export * from "./browserStorage.js";
31
36
  export { createElement } from "./createElement.js";
32
37
  export * from "./createRef.js";
@@ -66,4 +71,5 @@ export * from "./components/Stylesheet.js";
66
71
  export { TungstenError } from "./TungstenError.js";
67
72
  export * from "./__deproxy.js";
68
73
  export * from "./ContentNode.js"; // types and type guards
69
- export { StringIsPath, RouteKeyIsPath } from "./routing/RouteTypes.js";
74
+ export { StringIsPath, RouteKeyIsPath } from "./routing/RouteTypes.js";
75
+ export { makeTungstenErrorInfo } from "./routing/TungstenErrorInfo.js";
@@ -1 +1 @@
1
- {"version":3,"file":"makeInstanceConfigFromFrameworkConfig.d.ts","sourceRoot":"","sources":["../src/makeInstanceConfigFromFrameworkConfig.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAEnE,wBAAgB,qCAAqC,CACjD,eAAe,EAAE,uBAAuB,EAAE,GAAG,GAAE,OAAe,GAC/D,cAAc,CAUhB"}
1
+ {"version":3,"file":"makeInstanceConfigFromFrameworkConfig.d.ts","sourceRoot":"","sources":["../src/makeInstanceConfigFromFrameworkConfig.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAEnE,wBAAgB,qCAAqC,CACjD,eAAe,EAAE,uBAAuB,EAAE,GAAG,GAAE,OAAe,GAC/D,cAAc,CAWhB"}
@@ -5,7 +5,8 @@ export function makeInstanceConfigFromFrameworkConfig(frameworkConfig, dev = fal
5
5
  useRootComponent: frameworkConfig.useRootComponent,
6
6
  clientSideNav: frameworkConfig.clientSideNav,
7
7
  siteBasePath: dev ? '' : frameworkConfig.siteBasePath,
8
- staticSite: frameworkConfig.staticSite
8
+ staticSite: frameworkConfig.staticSite,
9
+ csr: frameworkConfig.csr
9
10
  };
10
11
  if ('sourceDir' in frameworkConfig) c.sourceDir = frameworkConfig.sourceDir;
11
12
  return c;
package/dist/pop.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { Instance } from './Instance/Instance.js';
2
+ export declare function registerPopInstance(newInstance: Instance): void;
3
+ export declare function dismissPop(id: number): void;
4
+ export declare function pop(text: string): void;
5
+ type PopFunction = typeof pop;
6
+ declare global {
7
+ var pop: PopFunction;
8
+ }
9
+ export {};
10
+ //# sourceMappingURL=pop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pop.d.ts","sourceRoot":"","sources":["../src/pop.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAStD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,QAAQ,GAAG,IAAI,CAE/D;AAQD,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAO3C;AAID,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAatC;AAMD,KAAK,WAAW,GAAG,OAAO,GAAG,CAAA;AAE7B,OAAO,CAAC,MAAM,CAAC;IAEX,IAAI,GAAG,EAAE,WAAW,CAAA;CACvB"}
package/dist/pop.js ADDED
@@ -0,0 +1,49 @@
1
+ import { isServer } from "./isServer.js";
2
+ import { log, warn } from "./appOutput.js";
3
+ // Set once by PopMessages.clientInit, from this.__instance - one Instance per app, shared
4
+ // across the whole client lifetime, so no per-component proxy/subscription is needed here.
5
+ let instance = null;
6
+ export function registerPopInstance(newInstance) {
7
+ instance = newInstance;
8
+ }
9
+ let nextId = 0;
10
+ const AUTO_DISMISS_MS = 3000;
11
+ const LEAVE_MS = 200; // match .pop-message.leaving's transition duration in PopMessages.tsx
12
+ // Shared by pop()'s auto-dismiss and PopMessages' click-to-dismiss, so both leave the same way.
13
+ export function dismissPop(id) {
14
+ if (instance === null) return;
15
+ const {
16
+ popMessages
17
+ } = instance.internalStore;
18
+ popMessages.set(popMessages.get().map(message => message.id === id ? {
19
+ ...message,
20
+ leaving: true
21
+ } : message));
22
+ setTimeout(() => {
23
+ popMessages.set(popMessages.get().filter(message => message.id !== id));
24
+ }, LEAVE_MS);
25
+ }
26
+ // Shows a dismissible banner via <PopMessages/> (an app mounts one in its Root). Also available
27
+ // as global.pop / window.pop for ad-hoc use outside component code, on server and client alike.
28
+ export function pop(text) {
29
+ if (isServer) {
30
+ log('pop: ' + text);
31
+ return;
32
+ }
33
+ if (instance === null) {
34
+ warn('pop() called before the app finished mounting; message dropped:', text);
35
+ return;
36
+ }
37
+ const message = {
38
+ id: nextId++,
39
+ text
40
+ };
41
+ const {
42
+ popMessages
43
+ } = instance.internalStore;
44
+ popMessages.set([...popMessages.get(), message]);
45
+ setTimeout(() => dismissPop(message.id), AUTO_DISMISS_MS);
46
+ }
47
+ // Assigned at module load in both environments, so a pre-mount browser call hits pop()'s
48
+ // no-bridge branch instead of throwing.
49
+ globalThis.pop = pop;
@@ -0,0 +1,20 @@
1
+ import type { ContentNode } from '../ContentNode.js';
2
+ import type { HtmlAllowList } from './HtmlAllowList.js';
3
+ /**
4
+ * Applies an allow list to a content tree that already exists, rather than
5
+ * one being built - the same decisions `parseHtml(html, { allowList })` makes
6
+ * while parsing, for a tree that did not come from parsing HTML at all (one
7
+ * rebuilt from storage, for instance) and so never had the chance to.
8
+ *
9
+ * A disallowed element is unwrapped, keeping its children in its place, so
10
+ * filtering a tag never costs the reader the words inside it - the same rule
11
+ * `parseHtml` applies. An element holding program text rather than prose
12
+ * (`<script>`, and anything on the same footing as `<script>` even under a
13
+ * permissive allow list) is dropped whole instead. `on*` props, `ref`, and
14
+ * `dangerouslySetInnerHTML` are always stripped - the tree-shape equivalent
15
+ * of what `parseHtml` refuses to produce in the first place - and a `href`/
16
+ * `src`/... value using an executable scheme (`javascript:`, `vbscript:`) or,
17
+ * on a navigating attribute, `data:` is dropped the same way.
18
+ */
19
+ export declare function filterContentNode(node: ContentNode, allowList: HtmlAllowList): ContentNode;
20
+ //# sourceMappingURL=filterContentNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterContentNode.d.ts","sourceRoot":"","sources":["../../src/rawHtml/filterContentNode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,mBAAmB,CAAA;AAI/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAyFvD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,GAAG,WAAW,CAE1F"}
@@ -0,0 +1,96 @@
1
+ import { ContentNodeIsComponent } from "../ContentNode.js";
2
+ import { $props, $type } from "../contentNodeSymbols.js";
3
+ import { StringIsHtmlEvent } from "../htmlEvents.js";
4
+ import { blockedUrl, makeAllowCheck, removalFor, urlAttributes } from "./parseHtml.js";
5
+ // The NodeProps equivalent of parseHtml's class->className, style->css
6
+ // rename: the allow list is written in terms of the HTML attribute name, but
7
+ // a content tree already carries Tungsten's prop names for these two.
8
+ const attributeCheckName = {
9
+ className: 'class',
10
+ css: 'style'
11
+ };
12
+ function filterProps(tag, props, allow) {
13
+ const filtered = {};
14
+ for (const key in props) {
15
+ if (key === 'children') continue;
16
+ const value = props[key];
17
+ // Tungsten reads these as event listeners, not attributes - same rule
18
+ // parseHtml applies to on* HTML attributes, expressed for prop names.
19
+ if (StringIsHtmlEvent(key)) continue;
20
+ // Raw markup that bypasses tree filtering entirely: parseHtml never
21
+ // produces this prop, so a tree in its shape should not carry it either.
22
+ if (key === 'dangerouslySetInnerHTML') continue;
23
+ if (key === 'ref') continue;
24
+ const checkName = (attributeCheckName[key] ?? key).toLowerCase();
25
+ if (!allow.attribute(tag, checkName)) continue;
26
+ if (checkName in urlAttributes && typeof value === 'string' && blockedUrl(checkName, value)) continue;
27
+ filtered[key] = value;
28
+ }
29
+ return filtered;
30
+ }
31
+ // Matches toContentNode() in parseHtml.ts: zero children omits the prop,
32
+ // one is the value directly (not wrapped in an array), two or more is an
33
+ // array. Without this, an element that loses every child to filtering ends
34
+ // up with an empty-array `children` instead of no `children` prop at all -
35
+ // a shape parseHtml itself would never produce.
36
+ function normalizeChildren(children) {
37
+ if (!Array.isArray(children)) return children;
38
+ if (children.length === 0) return null;
39
+ if (children.length === 1) return children[0] ?? null;
40
+ return children;
41
+ }
42
+ function filter(node, allow) {
43
+ if (node === undefined || node === null || node === true || node === false) return node;
44
+ if (typeof node === 'string' || typeof node === 'number') return node;
45
+ if (Array.isArray(node)) {
46
+ const out = [];
47
+ for (const child of node) {
48
+ const filteredChild = filter(child, allow);
49
+ // Non-rendered values, whether the child already was one or
50
+ // filtering made it one (an unwrapped or dropped element that
51
+ // left nothing behind), contribute nothing to the array - never
52
+ // a stray null placeholder in a dropped element's place.
53
+ if (filteredChild === undefined || filteredChild === null || filteredChild === true || filteredChild === false) continue;
54
+ if (Array.isArray(filteredChild)) out.push(...filteredChild);else out.push(filteredChild);
55
+ }
56
+ return out;
57
+ }
58
+ // Not authored markup - parseHtml never produces a component node, so a
59
+ // tree in its shape should not carry one either, but this is defensive
60
+ // rather than reachable: nothing that round-trips through storage (JSON)
61
+ // can carry a class or function reference.
62
+ if (ContentNodeIsComponent(node)) return node;
63
+ const tag = node[$type];
64
+ const removal = removalFor(tag, true, allow);
65
+ if (removal === 'drop') return null;
66
+ const children = normalizeChildren(filter(node[$props].children ?? null, allow));
67
+ if (removal === 'unwrap') return children ?? [];
68
+ return {
69
+ [$type]: tag,
70
+ [$props]: {
71
+ ...filterProps(tag, node[$props], allow),
72
+ ...(children === null ? {} : {
73
+ children
74
+ })
75
+ }
76
+ };
77
+ }
78
+ /**
79
+ * Applies an allow list to a content tree that already exists, rather than
80
+ * one being built - the same decisions `parseHtml(html, { allowList })` makes
81
+ * while parsing, for a tree that did not come from parsing HTML at all (one
82
+ * rebuilt from storage, for instance) and so never had the chance to.
83
+ *
84
+ * A disallowed element is unwrapped, keeping its children in its place, so
85
+ * filtering a tag never costs the reader the words inside it - the same rule
86
+ * `parseHtml` applies. An element holding program text rather than prose
87
+ * (`<script>`, and anything on the same footing as `<script>` even under a
88
+ * permissive allow list) is dropped whole instead. `on*` props, `ref`, and
89
+ * `dangerouslySetInnerHTML` are always stripped - the tree-shape equivalent
90
+ * of what `parseHtml` refuses to produce in the first place - and a `href`/
91
+ * `src`/... value using an executable scheme (`javascript:`, `vbscript:`) or,
92
+ * on a navigating attribute, `data:` is dropped the same way.
93
+ */
94
+ export function filterContentNode(node, allowList) {
95
+ return filter(node, makeAllowCheck(allowList));
96
+ }
@@ -18,6 +18,20 @@ export type ParseHtmlOptions = {
18
18
  */
19
19
  allowList?: HtmlAllowList | undefined;
20
20
  };
21
+ export type AllowCheck = {
22
+ tag: (name: string) => boolean;
23
+ attribute: (tag: string, name: string) => boolean;
24
+ };
25
+ export declare function makeAllowCheck(allowList: HtmlAllowList | undefined): AllowCheck;
26
+ export declare const urlAttributes: Record<string, true>;
27
+ export declare function blockedUrl(attributeName: string, value: string): boolean;
28
+ /**
29
+ * Whether an element survives, and if not, how it goes.
30
+ *
31
+ * Elements holding program text rather than prose are dropped whole — there is
32
+ * no reader's content inside them to preserve. Everything else is unwrapped.
33
+ */
34
+ export declare function removalFor(name: string, sanitize: boolean, allow: AllowCheck): 'drop' | 'unwrap' | undefined;
21
35
  /**
22
36
  * Parses raw HTML into a Tungsten content tree.
23
37
  *
@@ -1 +1 @@
1
- {"version":3,"file":"parseHtml.d.ts","sourceRoot":"","sources":["../../src/rawHtml/parseHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA8B,MAAM,mBAAmB,CAAA;AAKhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,MAAM,MAAM,gBAAgB,GAAG;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CACxC,CAAA;AAsID;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,WAAW,CAqGnF"}
1
+ {"version":3,"file":"parseHtml.d.ts","sourceRoot":"","sources":["../../src/rawHtml/parseHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA8B,MAAM,mBAAmB,CAAA;AAKhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,MAAM,MAAM,gBAAgB,GAAG;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CACxC,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACrB,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IAC9B,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CACpD,CAAA;AAID,wBAAgB,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,GAAG,UAAU,CAe/E;AAiBD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAE9C,CAAA;AA2BD,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAOxE;AAgCD;;;;;GAKG;AACH,wBAAgB,UAAU,CACtB,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,GACnD,MAAM,GAAG,QAAQ,GAAG,SAAS,CAK/B;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,WAAW,CAqGnF"}
@@ -6,7 +6,7 @@ const allowEverything = {
6
6
  tag: () => true,
7
7
  attribute: () => true
8
8
  };
9
- function makeAllowCheck(allowList) {
9
+ export function makeAllowCheck(allowList) {
10
10
  if (allowList === undefined) return allowEverything;
11
11
  const tags = new Set(allowList.tags);
12
12
  const attributes = new Set(allowList.attributes);
@@ -21,7 +21,7 @@ function makeAllowCheck(allowList) {
21
21
  }
22
22
  // REGEX: on followed by at least one letter, e.g. onclick, onerror
23
23
  const eventAttribute = /^on[a-z]/i;
24
- const urlAttributes = {
24
+ export const urlAttributes = {
25
25
  href: true,
26
26
  src: true,
27
27
  action: true,
@@ -56,7 +56,7 @@ function urlScheme(url) {
56
56
  }
57
57
  return '';
58
58
  }
59
- function blockedUrl(attributeName, value) {
59
+ export function blockedUrl(attributeName, value) {
60
60
  const scheme = urlScheme(value);
61
61
  if (scheme in executableSchemes) return true;
62
62
  // data: URLs can carry a whole HTML document, so they are barred from
@@ -87,7 +87,7 @@ function attributesToProps(tag, attributes, sanitize, allow) {
87
87
  * Elements holding program text rather than prose are dropped whole — there is
88
88
  * no reader's content inside them to preserve. Everything else is unwrapped.
89
89
  */
90
- function removalFor(name, sanitize, allow) {
90
+ export function removalFor(name, sanitize, allow) {
91
91
  if (sanitize && name === 'script') return 'drop';
92
92
  if (allow.tag(name)) return undefined;
93
93
  return name in htmlRawTextTags ? 'drop' : 'unwrap';
@@ -0,0 +1,17 @@
1
+ import type { HtmlAllowList } from './HtmlAllowList.js';
2
+ export type SanitizeHtmlOptions = {
3
+ /** Defaults to `safeHtmlAllowList`. Pass your own list to allow more or less. */
4
+ allowList?: HtmlAllowList;
5
+ };
6
+ /**
7
+ * Cleans untrusted HTML and returns it as a string, synchronously.
8
+ *
9
+ * `parseHtml()` already does the cleaning - this only adds the return trip,
10
+ * for a caller that needs the result as a string rather than a content tree:
11
+ * a paste handler cleaning a clipboard payload before it is inserted, or an
12
+ * editor cleaning its content before it is stored. Both happen at a rate
13
+ * `renderingNodeToHtml` (the async, component-aware server renderer) is the
14
+ * wrong shape for.
15
+ */
16
+ export declare function sanitizeHtml(html: string, options?: SanitizeHtmlOptions): string;
17
+ //# sourceMappingURL=sanitizeHtml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../../src/rawHtml/sanitizeHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIvD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,iFAAiF;IACjF,SAAS,CAAC,EAAE,aAAa,CAAA;CAC5B,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAGpF"}
@@ -0,0 +1,19 @@
1
+ import { contentNodeToHtml } from "../render/contentNodeToHtml.js";
2
+ import { parseHtml } from "./parseHtml.js";
3
+ import { safeHtmlAllowList } from "./safeHtmlAllowList.js";
4
+ /**
5
+ * Cleans untrusted HTML and returns it as a string, synchronously.
6
+ *
7
+ * `parseHtml()` already does the cleaning - this only adds the return trip,
8
+ * for a caller that needs the result as a string rather than a content tree:
9
+ * a paste handler cleaning a clipboard payload before it is inserted, or an
10
+ * editor cleaning its content before it is stored. Both happen at a rate
11
+ * `renderingNodeToHtml` (the async, component-aware server renderer) is the
12
+ * wrong shape for.
13
+ */
14
+ export function sanitizeHtml(html, options = {}) {
15
+ const allowList = options.allowList ?? safeHtmlAllowList;
16
+ return contentNodeToHtml(parseHtml(html, {
17
+ allowList
18
+ }));
19
+ }
@@ -11,5 +11,7 @@ export type HeadData = {
11
11
  dev: boolean;
12
12
  tungstenPublicEnv: TungstenEnvOrUndefined;
13
13
  siteBasePath: SiteBasePath;
14
+ hmr: boolean | 'reload';
15
+ csr: boolean;
14
16
  };
15
17
  //# sourceMappingURL=HeadData.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HeadData.d.ts","sourceRoot":"","sources":["../../src/render/HeadData.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAE9D,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,MAAM,EAAE,CAAA;AAE5C,MAAM,MAAM,QAAQ,GAAG;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,mBAAmB,EAAE,aAAa,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,OAAO,CAAC;IACb,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C,YAAY,EAAE,YAAY,CAAA;CAC7B,CAAA"}
1
+ {"version":3,"file":"HeadData.d.ts","sourceRoot":"","sources":["../../src/render/HeadData.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAE9D,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,MAAM,EAAE,CAAA;AAE5C,MAAM,MAAM,QAAQ,GAAG;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,mBAAmB,EAAE,aAAa,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,OAAO,CAAC;IACb,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,OAAO,GAAG,QAAQ,CAAA;IACvB,GAAG,EAAE,OAAO,CAAA;CACf,CAAA"}
@@ -0,0 +1,18 @@
1
+ import type { ContentNode } from '../ContentNode.js';
2
+ /**
3
+ * Serialises a content tree to HTML, synchronously.
4
+ *
5
+ * `renderingNodeToHtml` (the server's real renderer) covers the general case -
6
+ * components, async data, `<head>` - and is async and internal because of it.
7
+ * This covers the subset that never needs any of that: plain HTML elements,
8
+ * text and arrays, the exact shape `parseHtml()` produces. Given a component
9
+ * node it throws rather than silently rendering nothing, since a caller
10
+ * reaching for the synchronous path has nothing that could resolve one.
11
+ *
12
+ * Attribute values are escaped, unlike `renderingNodeToHtml`'s - that renderer
13
+ * trusts values from application-authored JSX; this one exists specifically to
14
+ * round-trip untrusted markup back out as a string, so a value carrying `"`
15
+ * must not be able to break out of its attribute.
16
+ */
17
+ export declare function contentNodeToHtml(node: ContentNode): string;
18
+ //# sourceMappingURL=contentNodeToHtml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentNodeToHtml.d.ts","sourceRoot":"","sources":["../../src/render/contentNodeToHtml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAapD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA+C3D"}
@@ -0,0 +1,66 @@
1
+ import { ContentNodeIsComponent } from "../ContentNode.js";
2
+ import { booleanAttributeValue } from "../booleanAttributeValue.js";
3
+ import { $props, $type } from "../contentNodeSymbols.js";
4
+ import { htmlEntities } from "../htmlEntities.js";
5
+ import { innerHtmlFromProps } from "../innerHtmlFromProps.js";
6
+ import { throwError } from "../throwError.js";
7
+ import { validChildren } from "../validChildren.js";
8
+ import { htmlMonoTags } from "./htmlMonoTags.js";
9
+ import { htmlSelfClosableTags } from "./htmlSelfClosableTags.js";
10
+ import { htmlVoidTags } from "./htmlVoidTags.js";
11
+ import { processProps } from "./processProps.js";
12
+ /**
13
+ * Serialises a content tree to HTML, synchronously.
14
+ *
15
+ * `renderingNodeToHtml` (the server's real renderer) covers the general case -
16
+ * components, async data, `<head>` - and is async and internal because of it.
17
+ * This covers the subset that never needs any of that: plain HTML elements,
18
+ * text and arrays, the exact shape `parseHtml()` produces. Given a component
19
+ * node it throws rather than silently rendering nothing, since a caller
20
+ * reaching for the synchronous path has nothing that could resolve one.
21
+ *
22
+ * Attribute values are escaped, unlike `renderingNodeToHtml`'s - that renderer
23
+ * trusts values from application-authored JSX; this one exists specifically to
24
+ * round-trip untrusted markup back out as a string, so a value carrying `"`
25
+ * must not be able to break out of its attribute.
26
+ */
27
+ export function contentNodeToHtml(node) {
28
+ if (node === undefined || node === null || node === false || node === true) return '';
29
+ if (Array.isArray(node)) return node.map(contentNodeToHtml).join('');
30
+ if (typeof node === 'string' || typeof node === 'number') return htmlEntities(node.toString());
31
+ if (ContentNodeIsComponent(node)) {
32
+ throwError('contentNodeToHtml', `component nodes are not supported - encountered one of type ` + `${String(typeof node[$type] === 'string' ? node[$type] : node[$type].name)}.`);
33
+ }
34
+ const type = node[$type];
35
+ const {
36
+ children,
37
+ ...restProps
38
+ } = node[$props];
39
+ const attribProps = processProps(restProps);
40
+ let attributes = '';
41
+ for (const key in attribProps) {
42
+ const value = attribProps[key];
43
+ if (typeof value === 'boolean') {
44
+ const attrValue = booleanAttributeValue(key, value);
45
+ if (attrValue !== null) attributes += attrValue === '' ? ` ${key}` : ` ${key}="${attrValue}"`;
46
+ } else if (value !== undefined && value !== null) {
47
+ attributes += ` ${key}="${htmlEntities(String(value))}"`;
48
+ }
49
+ }
50
+ const innerHtml = innerHtmlFromProps(type, node[$props], validChildren(children));
51
+ if (innerHtml !== null) {
52
+ if (type in htmlVoidTags) {
53
+ throwError('contentNodeToHtml', `<${type}> is an HTML void type (cannot have children), but the node has dangerouslySetInnerHTML.`);
54
+ }
55
+ return `<${type}${attributes}>${innerHtml}</${type}>`;
56
+ }
57
+ if (validChildren(children)) {
58
+ if (type in htmlVoidTags) {
59
+ throwError('contentNodeToHtml', `<${type}> is an HTML void type (cannot have children), but the node has children.`);
60
+ }
61
+ return `<${type}${attributes}>${contentNodeToHtml(children)}</${type}>`;
62
+ }
63
+ if (type in htmlMonoTags) return `<${type}${attributes}>`;
64
+ if (type in htmlSelfClosableTags) return `<${type}${attributes} />`;
65
+ return `<${type}${attributes}></${type}>`;
66
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"contentToHtml.d.ts","sourceRoot":"","sources":["../../src/render/contentToHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAMvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAI9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAGrE,wBAAsB,aAAa,CAC/B,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EACpE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EACxC,MAAM,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EACnE,iBAAiB,GAAE,sBAAkC,EAAE,GAAG,UAAQ,GACnE,OAAO,CAAC,MAAM,CAAC,CAoDjB"}
1
+ {"version":3,"file":"contentToHtml.d.ts","sourceRoot":"","sources":["../../src/render/contentToHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAMvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAI9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAGrE,wBAAsB,aAAa,CAC/B,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EACpE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EACxC,MAAM,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EACnE,iBAAiB,GAAE,sBAAkC,EAAE,GAAG,UAAQ,GACnE,OAAO,CAAC,MAAM,CAAC,CAsDjB"}