rwsdk 0.1.16 → 0.1.17

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 (327) hide show
  1. package/dist/lib/$.d.mts +8 -0
  2. package/dist/lib/$.mjs +5 -0
  3. package/dist/lib/compileTsModule.d.mts +1 -0
  4. package/dist/lib/compileTsModule.mjs +27 -0
  5. package/dist/lib/constants.d.mts +4 -0
  6. package/dist/lib/constants.mjs +6 -0
  7. package/dist/lib/findWranglerConfig.d.mts +1 -0
  8. package/dist/lib/findWranglerConfig.mjs +12 -0
  9. package/dist/lib/getShortName.d.mts +1 -0
  10. package/dist/lib/getShortName.mjs +2 -0
  11. package/dist/lib/getSrcPaths.d.ts +15 -0
  12. package/dist/lib/getSrcPaths.js +80 -0
  13. package/dist/lib/hasPkgScript.d.mts +1 -0
  14. package/dist/lib/hasPkgScript.mjs +9 -0
  15. package/dist/lib/jsonUtils.d.mts +28 -0
  16. package/dist/lib/jsonUtils.mjs +167 -0
  17. package/dist/lib/setupEnvFiles.d.mts +4 -0
  18. package/dist/lib/setupEnvFiles.mjs +31 -0
  19. package/dist/lib/smokeTests/artifacts.d.mts +10 -0
  20. package/dist/lib/smokeTests/artifacts.mjs +164 -0
  21. package/dist/lib/smokeTests/browser.d.mts +48 -0
  22. package/dist/lib/smokeTests/browser.mjs +1041 -0
  23. package/dist/lib/smokeTests/cleanup.d.mts +5 -0
  24. package/dist/lib/smokeTests/cleanup.mjs +214 -0
  25. package/dist/lib/smokeTests/codeUpdates.d.mts +8 -0
  26. package/dist/lib/smokeTests/codeUpdates.mjs +229 -0
  27. package/dist/lib/smokeTests/constants.d.mts +5 -0
  28. package/dist/lib/smokeTests/constants.mjs +10 -0
  29. package/dist/lib/smokeTests/development.d.mts +11 -0
  30. package/dist/lib/smokeTests/development.mjs +209 -0
  31. package/dist/lib/smokeTests/environment.d.mts +14 -0
  32. package/dist/lib/smokeTests/environment.mjs +163 -0
  33. package/dist/lib/smokeTests/release.d.mts +61 -0
  34. package/dist/lib/smokeTests/release.mjs +526 -0
  35. package/dist/lib/smokeTests/reporting.d.mts +13 -0
  36. package/dist/lib/smokeTests/reporting.mjs +355 -0
  37. package/dist/lib/smokeTests/runSmokeTests.d.mts +5 -0
  38. package/dist/lib/smokeTests/runSmokeTests.mjs +144 -0
  39. package/dist/lib/smokeTests/state.d.mts +48 -0
  40. package/dist/lib/smokeTests/state.mjs +57 -0
  41. package/dist/lib/smokeTests/templates/SmokeTest.template.d.ts +1 -0
  42. package/dist/lib/smokeTests/templates/SmokeTest.template.js +81 -0
  43. package/dist/lib/smokeTests/templates/SmokeTestClient.template.d.ts +1 -0
  44. package/dist/lib/smokeTests/templates/SmokeTestClient.template.js +159 -0
  45. package/dist/lib/smokeTests/templates/smokeTestFunctions.template.d.ts +1 -0
  46. package/dist/lib/smokeTests/templates/smokeTestFunctions.template.js +19 -0
  47. package/dist/lib/smokeTests/types.d.mts +75 -0
  48. package/dist/lib/smokeTests/types.mjs +1 -0
  49. package/dist/lib/smokeTests/utils.d.mts +15 -0
  50. package/dist/lib/smokeTests/utils.mjs +147 -0
  51. package/dist/llms/index.d.ts +3 -0
  52. package/dist/llms/index.js +35 -0
  53. package/dist/llms/rules/interruptors.d.ts +1 -0
  54. package/dist/llms/rules/interruptors.js +243 -0
  55. package/dist/llms/rules/middleware.d.ts +1 -0
  56. package/dist/llms/rules/middleware.js +71 -0
  57. package/dist/llms/rules/react.d.ts +1 -0
  58. package/dist/llms/rules/react.js +106 -0
  59. package/dist/llms/rules/request-response.d.ts +1 -0
  60. package/dist/llms/rules/request-response.js +209 -0
  61. package/dist/runtime/client.d.ts +17 -0
  62. package/dist/runtime/client.js +74 -0
  63. package/dist/runtime/clientNavigation.d.ts +4 -0
  64. package/dist/runtime/clientNavigation.js +53 -0
  65. package/dist/runtime/clientNavigation.test.d.ts +1 -0
  66. package/dist/runtime/clientNavigation.test.js +55 -0
  67. package/dist/runtime/constants.d.ts +1 -0
  68. package/dist/runtime/constants.js +1 -0
  69. package/dist/runtime/entries/auth.d.ts +1 -0
  70. package/dist/runtime/entries/auth.js +1 -0
  71. package/dist/runtime/entries/client.d.ts +4 -0
  72. package/dist/runtime/entries/client.js +4 -0
  73. package/dist/runtime/entries/clientSSR.d.ts +1 -0
  74. package/dist/runtime/entries/clientSSR.js +1 -0
  75. package/dist/runtime/entries/navigation.d.ts +1 -0
  76. package/dist/runtime/entries/navigation.js +1 -0
  77. package/dist/runtime/entries/no-react-server.d.ts +0 -0
  78. package/dist/runtime/entries/no-react-server.js +2 -0
  79. package/dist/runtime/entries/react-server-only.d.ts +0 -0
  80. package/dist/runtime/entries/react-server-only.js +2 -0
  81. package/dist/runtime/entries/router.d.ts +2 -0
  82. package/dist/runtime/entries/router.js +2 -0
  83. package/dist/runtime/entries/ssr.d.ts +1 -0
  84. package/dist/runtime/entries/ssr.js +1 -0
  85. package/dist/runtime/entries/worker.d.ts +9 -0
  86. package/dist/runtime/entries/worker.js +9 -0
  87. package/dist/runtime/error.d.ts +6 -0
  88. package/dist/runtime/error.js +8 -0
  89. package/dist/runtime/imports/ClientOnly.d.ts +3 -0
  90. package/dist/runtime/imports/ClientOnly.js +8 -0
  91. package/dist/runtime/imports/client.d.ts +4 -0
  92. package/dist/runtime/imports/client.js +33 -0
  93. package/dist/runtime/imports/ssr.d.ts +5 -0
  94. package/dist/runtime/imports/ssr.js +20 -0
  95. package/dist/runtime/imports/worker.d.ts +5 -0
  96. package/dist/runtime/imports/worker.js +22 -0
  97. package/dist/runtime/lib/auth/index.d.ts +1 -0
  98. package/dist/runtime/lib/auth/index.js +1 -0
  99. package/dist/runtime/lib/auth/session.d.ts +50 -0
  100. package/dist/runtime/lib/auth/session.js +148 -0
  101. package/dist/runtime/lib/db/DOWorkerDialect.d.ts +29 -0
  102. package/dist/runtime/lib/db/DOWorkerDialect.js +66 -0
  103. package/dist/runtime/lib/db/SqliteDurableObject.d.ts +14 -0
  104. package/dist/runtime/lib/db/SqliteDurableObject.js +42 -0
  105. package/dist/runtime/lib/db/createDb.d.ts +2 -0
  106. package/dist/runtime/lib/db/createDb.js +33 -0
  107. package/dist/runtime/lib/db/index.d.ts +4 -0
  108. package/dist/runtime/lib/db/index.js +3 -0
  109. package/dist/runtime/lib/db/migrations.d.ts +23 -0
  110. package/dist/runtime/lib/db/migrations.js +34 -0
  111. package/dist/runtime/lib/db/typeInference/assert.d.ts +2 -0
  112. package/dist/runtime/lib/db/typeInference/assert.js +1 -0
  113. package/dist/runtime/lib/db/typeInference/builders/alterColumn.d.ts +27 -0
  114. package/dist/runtime/lib/db/typeInference/builders/alterColumn.js +1 -0
  115. package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +53 -0
  116. package/dist/runtime/lib/db/typeInference/builders/alterTable.js +1 -0
  117. package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +26 -0
  118. package/dist/runtime/lib/db/typeInference/builders/columnDefinition.js +1 -0
  119. package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +49 -0
  120. package/dist/runtime/lib/db/typeInference/builders/createTable.js +1 -0
  121. package/dist/runtime/lib/db/typeInference/builders/createView.d.ts +17 -0
  122. package/dist/runtime/lib/db/typeInference/builders/createView.js +1 -0
  123. package/dist/runtime/lib/db/typeInference/builders/dropTable.d.ts +11 -0
  124. package/dist/runtime/lib/db/typeInference/builders/dropTable.js +1 -0
  125. package/dist/runtime/lib/db/typeInference/builders/dropView.d.ts +12 -0
  126. package/dist/runtime/lib/db/typeInference/builders/dropView.js +1 -0
  127. package/dist/runtime/lib/db/typeInference/builders/schema.d.ts +24 -0
  128. package/dist/runtime/lib/db/typeInference/builders/schema.js +1 -0
  129. package/dist/runtime/lib/db/typeInference/database.d.ts +27 -0
  130. package/dist/runtime/lib/db/typeInference/database.js +1 -0
  131. package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.d.ts +1 -0
  132. package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.js +360 -0
  133. package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.d.ts +1 -0
  134. package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.js +33 -0
  135. package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.d.ts +1 -0
  136. package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.js +143 -0
  137. package/dist/runtime/lib/db/typeInference/typetests/print.d.ts +3 -0
  138. package/dist/runtime/lib/db/typeInference/typetests/print.js +1 -0
  139. package/dist/runtime/lib/db/typeInference/typetests/testUtils.d.ts +2 -0
  140. package/dist/runtime/lib/db/typeInference/typetests/testUtils.js +1 -0
  141. package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.d.ts +1 -0
  142. package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.js +17 -0
  143. package/dist/runtime/lib/db/typeInference/utils.d.ts +82 -0
  144. package/dist/runtime/lib/db/typeInference/utils.js +2 -0
  145. package/dist/runtime/lib/debug.d.ts +2 -0
  146. package/dist/runtime/lib/debug.js +36 -0
  147. package/dist/runtime/lib/links.d.ts +14 -0
  148. package/dist/runtime/lib/links.js +38 -0
  149. package/dist/runtime/lib/realtime/client.d.ts +7 -0
  150. package/dist/runtime/lib/realtime/client.js +166 -0
  151. package/dist/runtime/lib/realtime/constants.d.ts +1 -0
  152. package/dist/runtime/lib/realtime/constants.js +1 -0
  153. package/dist/runtime/lib/realtime/durableObject.d.ts +29 -0
  154. package/dist/runtime/lib/realtime/durableObject.js +187 -0
  155. package/dist/runtime/lib/realtime/renderRealtimeClients.d.ts +7 -0
  156. package/dist/runtime/lib/realtime/renderRealtimeClients.js +6 -0
  157. package/dist/runtime/lib/realtime/shared.d.ts +10 -0
  158. package/dist/runtime/lib/realtime/shared.js +10 -0
  159. package/dist/runtime/lib/realtime/validateUpgradeRequest.d.ts +6 -0
  160. package/dist/runtime/lib/realtime/validateUpgradeRequest.js +29 -0
  161. package/dist/runtime/lib/realtime/worker.d.ts +3 -0
  162. package/dist/runtime/lib/realtime/worker.js +16 -0
  163. package/dist/runtime/lib/router.d.ts +56 -0
  164. package/dist/runtime/lib/router.js +210 -0
  165. package/dist/runtime/lib/router.test.d.ts +1 -0
  166. package/dist/runtime/lib/router.test.js +58 -0
  167. package/dist/runtime/lib/streams/consumeEventStream.d.ts +4 -0
  168. package/dist/runtime/lib/streams/consumeEventStream.js +13 -0
  169. package/dist/runtime/lib/turnstile/TurnstileScript.d.ts +1 -0
  170. package/dist/runtime/lib/turnstile/TurnstileScript.js +2 -0
  171. package/dist/runtime/lib/turnstile/turnstile.d.ts +3 -0
  172. package/dist/runtime/lib/turnstile/turnstile.js +3 -0
  173. package/dist/runtime/lib/turnstile/useTurnstile.d.ts +4 -0
  174. package/dist/runtime/lib/turnstile/useTurnstile.js +23 -0
  175. package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +4 -0
  176. package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +15 -0
  177. package/dist/runtime/lib/utils.d.ts +1 -0
  178. package/dist/runtime/lib/utils.js +1 -0
  179. package/dist/runtime/register/client.d.ts +1 -0
  180. package/dist/runtime/register/client.js +5 -0
  181. package/dist/runtime/register/ssr.d.ts +3 -0
  182. package/dist/runtime/register/ssr.js +26 -0
  183. package/dist/runtime/register/worker.d.ts +4 -0
  184. package/dist/runtime/register/worker.js +42 -0
  185. package/dist/runtime/render/createClientManifest.d.ts +1 -0
  186. package/dist/runtime/render/createClientManifest.js +7 -0
  187. package/dist/runtime/render/createModuleMap.d.ts +1 -0
  188. package/dist/runtime/render/createModuleMap.js +13 -0
  189. package/dist/runtime/render/injectRSCPayload.d.ts +3 -0
  190. package/dist/runtime/render/injectRSCPayload.js +79 -0
  191. package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +9 -0
  192. package/dist/runtime/render/renderRscThenableToHtmlStream.js +49 -0
  193. package/dist/runtime/render/renderToRscStream.d.ts +5 -0
  194. package/dist/runtime/render/renderToRscStream.js +46 -0
  195. package/dist/runtime/render/renderToStream.d.ts +9 -0
  196. package/dist/runtime/render/renderToStream.js +27 -0
  197. package/dist/runtime/render/renderToString.d.ts +7 -0
  198. package/dist/runtime/render/renderToString.js +26 -0
  199. package/dist/runtime/render/transformRscToHtmlStream.d.ts +8 -0
  200. package/dist/runtime/render/transformRscToHtmlStream.js +19 -0
  201. package/dist/runtime/requestInfo/types.d.ts +11 -0
  202. package/dist/runtime/requestInfo/types.js +1 -0
  203. package/dist/runtime/requestInfo/worker.d.ts +5 -0
  204. package/dist/runtime/requestInfo/worker.js +33 -0
  205. package/dist/runtime/script.d.ts +5 -0
  206. package/dist/runtime/script.js +8 -0
  207. package/dist/runtime/ssrBridge.d.ts +2 -0
  208. package/dist/runtime/ssrBridge.js +11 -0
  209. package/dist/runtime/worker.d.ts +18 -0
  210. package/dist/runtime/worker.js +173 -0
  211. package/dist/scripts/__sdk.d.mts +1 -0
  212. package/dist/scripts/__sdk.mjs +14 -0
  213. package/dist/scripts/build-vendor-bundles.d.mts +1 -0
  214. package/dist/scripts/build-vendor-bundles.mjs +92 -0
  215. package/dist/scripts/debug-sync.d.mts +6 -0
  216. package/dist/scripts/debug-sync.mjs +224 -0
  217. package/dist/scripts/dev-init.d.mts +1 -0
  218. package/dist/scripts/dev-init.mjs +25 -0
  219. package/dist/scripts/ensure-deploy-env.d.mts +1 -0
  220. package/dist/scripts/ensure-deploy-env.mjs +271 -0
  221. package/dist/scripts/ensure-env.d.mts +1 -0
  222. package/dist/scripts/ensure-env.mjs +9 -0
  223. package/dist/scripts/migrate-new.d.mts +1 -0
  224. package/dist/scripts/migrate-new.mjs +51 -0
  225. package/dist/scripts/smoke-test.d.mts +1 -0
  226. package/dist/scripts/smoke-test.mjs +166 -0
  227. package/dist/scripts/worker-run.d.mts +1 -0
  228. package/dist/scripts/worker-run.mjs +82 -0
  229. package/dist/vite/aliasByEnvPlugin.d.mts +2 -0
  230. package/dist/vite/aliasByEnvPlugin.mjs +11 -0
  231. package/dist/vite/asyncSetupPlugin.d.mts +6 -0
  232. package/dist/vite/asyncSetupPlugin.mjs +23 -0
  233. package/dist/vite/checkIsUsingPrisma.d.mts +6 -0
  234. package/dist/vite/checkIsUsingPrisma.mjs +18 -0
  235. package/dist/vite/configPlugin.d.mts +9 -0
  236. package/dist/vite/configPlugin.mjs +169 -0
  237. package/dist/vite/copyPrismaWasmPlugin.d.mts +4 -0
  238. package/dist/vite/copyPrismaWasmPlugin.mjs +32 -0
  239. package/dist/vite/createDirectiveLookupPlugin.d.mts +21 -0
  240. package/dist/vite/createDirectiveLookupPlugin.mjs +231 -0
  241. package/dist/vite/customReactBuildPlugin.d.mts +4 -0
  242. package/dist/vite/customReactBuildPlugin.mjs +61 -0
  243. package/dist/vite/devServerTimingPlugin.d.mts +2 -0
  244. package/dist/vite/devServerTimingPlugin.mjs +24 -0
  245. package/dist/vite/directivesPlugin.d.mts +6 -0
  246. package/dist/vite/directivesPlugin.mjs +200 -0
  247. package/dist/vite/ensureAliasArray.d.mts +2 -0
  248. package/dist/vite/ensureAliasArray.mjs +17 -0
  249. package/dist/vite/findSpecifiers.d.mts +31 -0
  250. package/dist/vite/findSpecifiers.mjs +230 -0
  251. package/dist/vite/findSsrSpecifiers.d.mts +11 -0
  252. package/dist/vite/findSsrSpecifiers.mjs +67 -0
  253. package/dist/vite/hasDirective.d.mts +7 -0
  254. package/dist/vite/hasDirective.mjs +54 -0
  255. package/dist/vite/hasOwnCloudflareVitePlugin.d.mts +3 -0
  256. package/dist/vite/hasOwnCloudflareVitePlugin.mjs +14 -0
  257. package/dist/vite/index.d.mts +1 -0
  258. package/dist/vite/index.mjs +1 -0
  259. package/dist/vite/injectHmrPreambleJsxPlugin.d.mts +2 -0
  260. package/dist/vite/injectHmrPreambleJsxPlugin.mjs +22 -0
  261. package/dist/vite/injectVitePreamblePlugin.d.mts +4 -0
  262. package/dist/vite/injectVitePreamblePlugin.mjs +23 -0
  263. package/dist/vite/invalidateCacheIfPrismaClientChanged.d.mts +3 -0
  264. package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +27 -0
  265. package/dist/vite/invalidateModule.d.mts +6 -0
  266. package/dist/vite/invalidateModule.mjs +30 -0
  267. package/dist/vite/miniflareHMRPlugin.d.mts +10 -0
  268. package/dist/vite/miniflareHMRPlugin.mjs +209 -0
  269. package/dist/vite/miniflarePlugin.d.mts +9 -0
  270. package/dist/vite/miniflarePlugin.mjs +135 -0
  271. package/dist/vite/moveStaticAssetsPlugin.d.mts +4 -0
  272. package/dist/vite/moveStaticAssetsPlugin.mjs +12 -0
  273. package/dist/vite/normalizeModulePath.d.mts +1 -0
  274. package/dist/vite/normalizeModulePath.mjs +13 -0
  275. package/dist/vite/prismaPlugin.d.mts +4 -0
  276. package/dist/vite/prismaPlugin.mjs +43 -0
  277. package/dist/vite/reactConditionsResolverPlugin.d.mts +16 -0
  278. package/dist/vite/reactConditionsResolverPlugin.mjs +179 -0
  279. package/dist/vite/redwoodPlugin.d.mts +12 -0
  280. package/dist/vite/redwoodPlugin.mjs +105 -0
  281. package/dist/vite/requestUtils.d.mts +6 -0
  282. package/dist/vite/requestUtils.mjs +35 -0
  283. package/dist/vite/setupEnvFiles.d.mts +4 -0
  284. package/dist/vite/setupEnvFiles.mjs +31 -0
  285. package/dist/vite/ssrBridgePlugin.d.mts +7 -0
  286. package/dist/vite/ssrBridgePlugin.mjs +137 -0
  287. package/dist/vite/transformClientComponents.d.mts +12 -0
  288. package/dist/vite/transformClientComponents.mjs +116 -0
  289. package/dist/vite/transformClientComponents.test.d.mts +1 -0
  290. package/dist/vite/transformClientComponents.test.mjs +264 -0
  291. package/dist/vite/transformJsxScriptTagsPlugin.d.mts +8 -0
  292. package/dist/vite/transformJsxScriptTagsPlugin.mjs +315 -0
  293. package/dist/vite/transformJsxScriptTagsPlugin.test.d.mts +1 -0
  294. package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +334 -0
  295. package/dist/vite/transformServerFunctions.d.mts +16 -0
  296. package/dist/vite/transformServerFunctions.mjs +296 -0
  297. package/dist/vite/transformServerFunctions.test.d.mts +1 -0
  298. package/dist/vite/transformServerFunctions.test.mjs +124 -0
  299. package/dist/vite/useClientLookupPlugin.d.mts +5 -0
  300. package/dist/vite/useClientLookupPlugin.mjs +15 -0
  301. package/dist/vite/useClientPlugin.d.mts +8 -0
  302. package/dist/vite/useClientPlugin.mjs +295 -0
  303. package/dist/vite/useClientPlugin.test.d.mts +1 -0
  304. package/dist/vite/useClientPlugin.test.mjs +1204 -0
  305. package/dist/vite/useServerLookupPlugin.d.mts +5 -0
  306. package/dist/vite/useServerLookupPlugin.mjs +15 -0
  307. package/dist/vite/useServerPlugin.d.mts +1 -0
  308. package/dist/vite/useServerPlugin.mjs +1 -0
  309. package/dist/vite/virtualPlugin.d.mts +2 -0
  310. package/dist/vite/virtualPlugin.mjs +18 -0
  311. package/dist/vite/vitePreamblePlugin.d.mts +1 -0
  312. package/dist/vite/vitePreamblePlugin.mjs +11 -0
  313. package/dist/worker/__ssr_bridge.js +8947 -0
  314. package/dist/worker/__ssr_bridge.js.map +1 -0
  315. package/package.json +1 -1
  316. package/dist/vite/invalidateClientModule.d.mts +0 -2
  317. package/dist/vite/invalidateClientModule.mjs +0 -8
  318. package/dist/vite/invalidateModule copy.d.mts +0 -2
  319. package/dist/vite/invalidateModule copy.mjs +0 -14
  320. package/dist/vite/invalidateSSRModule.d.mts +0 -2
  321. package/dist/vite/invalidateSSRModule.mjs +0 -7
  322. package/dist/vite/mode.d.mts +0 -5
  323. package/dist/vite/mode.mjs +0 -25
  324. package/dist/vite/modePlugin.d.mts +0 -2
  325. package/dist/vite/modePlugin.mjs +0 -10
  326. /package/dist/vite/{isJsFile.d.ts → isJsFile.d.mts} +0 -0
  327. /package/dist/vite/{isJsFile.js → isJsFile.mjs} +0 -0
@@ -0,0 +1,1041 @@
1
+ import * as os from "os";
2
+ import { join } from "path";
3
+ import { pathExists } from "fs-extra";
4
+ import { log } from "./constants.mjs";
5
+ import { mkdirp } from "fs-extra";
6
+ import { install, resolveBuildId, computeExecutablePath, detectBrowserPlatform, Browser as PuppeteerBrowser, } from "@puppeteer/browsers";
7
+ import puppeteer from "puppeteer-core";
8
+ import { takeScreenshot } from "./artifacts.mjs";
9
+ import { RETRIES } from "./constants.mjs";
10
+ import { $ } from "../$.mjs";
11
+ import { fail } from "./utils.mjs";
12
+ import { reportSmokeTestResult } from "./reporting.mjs";
13
+ import { updateTestStatus } from "./state.mjs";
14
+ /**
15
+ * Launch a browser instance
16
+ */
17
+ export async function launchBrowser(browserPath, headless = true) {
18
+ // Get browser path if not provided
19
+ if (!browserPath) {
20
+ log("Getting browser executable path");
21
+ browserPath = await getBrowserPath();
22
+ }
23
+ console.log(`🚀 Launching browser from ${browserPath} (headless: ${headless})`);
24
+ log("Starting browser with puppeteer");
25
+ return await puppeteer.launch({
26
+ executablePath: browserPath,
27
+ headless,
28
+ args: ["--no-sandbox", "--disable-setuid-sandbox"],
29
+ });
30
+ }
31
+ /**
32
+ * Get the browser executable path
33
+ */
34
+ export async function getBrowserPath(testOptions) {
35
+ console.log("Finding Chrome executable...");
36
+ // First try using environment variable if set
37
+ if (process.env.CHROME_PATH) {
38
+ console.log(`Using Chrome from environment variable: ${process.env.CHROME_PATH}`);
39
+ return process.env.CHROME_PATH;
40
+ }
41
+ // Detect platform
42
+ log("Detecting platform");
43
+ const platform = detectBrowserPlatform();
44
+ if (!platform) {
45
+ log("ERROR: Failed to detect browser platform");
46
+ throw new Error("Failed to detect browser platform");
47
+ }
48
+ log("Detected platform: %s", platform);
49
+ // Define a consistent cache directory path in system temp folder
50
+ const rwCacheDir = join(os.tmpdir(), "redwoodjs-smoke-test-cache");
51
+ await mkdirp(rwCacheDir);
52
+ log("Using cache directory: %s", rwCacheDir);
53
+ // Determine browser type based on headless option
54
+ const browser = testOptions?.headless === false
55
+ ? PuppeteerBrowser.CHROME
56
+ : PuppeteerBrowser.CHROMEHEADLESSSHELL;
57
+ log(`Using browser type: ${browser}`);
58
+ // Resolve the buildId for the stable Chrome version - do this once
59
+ log("Resolving Chrome buildId for stable channel");
60
+ const buildId = await resolveBuildId(browser, platform, "stable");
61
+ log("Resolved buildId: %s", buildId);
62
+ // Create installation options - use them consistently
63
+ const installOptions = {
64
+ browser,
65
+ platform,
66
+ cacheDir: rwCacheDir,
67
+ buildId,
68
+ unpack: true,
69
+ };
70
+ try {
71
+ // Try to compute the path first (this will check if it's installed)
72
+ log("Attempting to find existing Chrome installation");
73
+ const path = computeExecutablePath(installOptions);
74
+ if (await pathExists(path)) {
75
+ console.log(`Found existing Chrome at: ${path}`);
76
+ return path;
77
+ }
78
+ else {
79
+ throw new Error("Chrome not found at: " + path);
80
+ }
81
+ }
82
+ catch (error) {
83
+ // If path computation fails, install Chrome
84
+ console.log("No Chrome installation found. Installing Chrome...");
85
+ // Add better error handling for the install step
86
+ try {
87
+ console.log("Downloading Chrome (this may take a few minutes)...");
88
+ await install(installOptions);
89
+ console.log("✅ Chrome installation completed successfully");
90
+ // Now compute the path for the installed browser
91
+ const path = computeExecutablePath(installOptions);
92
+ console.log(`Installed and using Chrome at: ${path}`);
93
+ return path;
94
+ }
95
+ catch (installError) {
96
+ // Provide more detailed error about the browser download failure
97
+ log("ERROR: Failed to download/install Chrome: %O", installError);
98
+ console.error(`❌ Failed to download/install Chrome browser.`);
99
+ console.error(`This is likely a network issue or the browser download URL is unavailable.`);
100
+ console.error(`Error details: ${installError instanceof Error ? installError.message : String(installError)}`);
101
+ // For debug builds, show the full error stack if available
102
+ if (installError instanceof Error && installError.stack) {
103
+ log("Error stack: %s", installError.stack);
104
+ }
105
+ console.log("\nPossible solutions:");
106
+ console.log("1. Check your internet connection");
107
+ console.log("2. Set CHROME_PATH environment variable to an existing Chrome installation");
108
+ console.log("3. Install Chrome manually and run the tests again");
109
+ throw new Error(`Failed to install Chrome browser: ${installError instanceof Error ? installError.message : String(installError)}`);
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * Check a URL by performing smoke tests and realtime upgrade
115
+ */
116
+ export async function checkUrl(url, artifactDir, browserPath, headless = true, bail = false, skipClient = false, environment = "Development", realtime = false, targetDir, skipHmr = false) {
117
+ console.log(`🔍 Testing URL: ${url}`);
118
+ log("Launching browser");
119
+ let browser;
120
+ try {
121
+ browser = await launchBrowser(browserPath, headless);
122
+ }
123
+ catch (error) {
124
+ // Use fail() directly for browser launch errors
125
+ await fail(error, 1, `${environment} - Browser Launch`);
126
+ return; // This will never be reached due to fail() exiting
127
+ }
128
+ // Track failures to report at the end
129
+ let hasFailures = false;
130
+ let initialTestError = null;
131
+ let realtimeTestError = null;
132
+ // Store timestamp values between test phases
133
+ const timestampState = {
134
+ initialServerValue: 23, // This is the default initial module-level value
135
+ clientUpdatedValue: null,
136
+ };
137
+ try {
138
+ log("Opening new page");
139
+ const page = await browser.newPage();
140
+ page.setDefaultNavigationTimeout(30000);
141
+ log("Set navigation timeout: %dms", 30000);
142
+ if (realtime) {
143
+ // If realtime flag is set, use the simplified flow that only does realtime testing
144
+ log("Using realtime-only flow (--realtime option enabled)");
145
+ console.log("⏩ Skipping initial smoke tests and upgrade step (--realtime option enabled)");
146
+ // Skip upgradeToRealtime and just run the realtime tests directly
147
+ try {
148
+ log("Performing realtime-only smoke test");
149
+ await checkUrlSmoke(page, url, true, bail, skipClient, environment, timestampState, targetDir, skipHmr);
150
+ // Take a screenshot of the realtime test
151
+ await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-passed`);
152
+ }
153
+ catch (error) {
154
+ hasFailures = true;
155
+ realtimeTestError =
156
+ error instanceof Error ? error : new Error(String(error));
157
+ log("Error during realtime-only test: %O", error);
158
+ console.error(`❌ Realtime test failed: ${realtimeTestError.message}`);
159
+ // Take a failure screenshot
160
+ await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-failed`).catch((e) => log("Failed to take error screenshot: %O", e));
161
+ // If bail is true, propagate the error
162
+ if (bail) {
163
+ throw error;
164
+ }
165
+ }
166
+ }
167
+ else {
168
+ // Normal flow with both initial and realtime tests
169
+ // Initial smoke test
170
+ log("Performing initial smoke test");
171
+ let initialTestStatus = "passed";
172
+ try {
173
+ await checkUrlSmoke(page, url, false, bail, skipClient, environment, timestampState, targetDir, skipHmr);
174
+ }
175
+ catch (error) {
176
+ hasFailures = true;
177
+ initialTestStatus = "failed";
178
+ initialTestError =
179
+ error instanceof Error ? error : new Error(String(error));
180
+ log("Error during initial smoke test: %O", error);
181
+ console.error(`❌ Initial smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
182
+ // If bail is true, stop the tests
183
+ if (bail) {
184
+ throw error;
185
+ }
186
+ console.log("Continuing with realtime upgrade test since --bail is not enabled...");
187
+ }
188
+ // Take a screenshot after initial test
189
+ await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-initial-${initialTestStatus}`);
190
+ // Upgrade to realtime and check again
191
+ log("Upgrading to realtime");
192
+ let realtimeTestStatus = "passed";
193
+ try {
194
+ await upgradeToRealtime(page, environment, bail);
195
+ log("Performing post-upgrade smoke test");
196
+ await checkUrlSmoke(page, url, true, bail, skipClient, environment, timestampState, targetDir, skipHmr);
197
+ }
198
+ catch (error) {
199
+ hasFailures = true;
200
+ realtimeTestStatus = "failed";
201
+ realtimeTestError =
202
+ error instanceof Error ? error : new Error(String(error));
203
+ log("Error during realtime smoke test: %O", error);
204
+ console.error(`❌ Realtime smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
205
+ // If bail is true, stop the tests
206
+ if (bail) {
207
+ throw error;
208
+ }
209
+ }
210
+ // Take a screenshot after realtime test
211
+ await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-${realtimeTestStatus}`);
212
+ }
213
+ // If there were failures, propagate them after taking screenshots
214
+ if (hasFailures) {
215
+ // Combine errors or just throw the one that happened
216
+ if (initialTestError && realtimeTestError) {
217
+ throw new Error(`Multiple test failures: Initial test: ${initialTestError.message}, Realtime test: ${realtimeTestError.message}`);
218
+ }
219
+ else if (initialTestError) {
220
+ throw initialTestError;
221
+ }
222
+ else if (realtimeTestError) {
223
+ throw realtimeTestError;
224
+ }
225
+ }
226
+ }
227
+ catch (error) {
228
+ log("Error during URL check: %O", error);
229
+ await browser
230
+ .close()
231
+ .catch((e) => log("Error closing browser: %O", e));
232
+ throw error;
233
+ }
234
+ finally {
235
+ log("Closing browser");
236
+ await browser
237
+ .close()
238
+ .catch((e) => log("Error closing browser: %O", e));
239
+ }
240
+ log("URL check completed successfully");
241
+ }
242
+ /**
243
+ * Check smoke test status for a specific URL
244
+ */
245
+ export async function checkUrlSmoke(page, url, isRealtime, bail = false, skipClient = false, environment = "Development", timestampState, targetDir, skipHmr = false) {
246
+ const phase = isRealtime ? "Post-upgrade" : "Initial";
247
+ console.log(`🔍 Testing ${phase} smoke tests at ${url}`);
248
+ // Parse the base URL and path to properly handle smoke test queries
249
+ const parsedUrl = new URL(url);
250
+ log("Parsed URL: %O", {
251
+ origin: parsedUrl.origin,
252
+ pathname: parsedUrl.pathname,
253
+ search: parsedUrl.search,
254
+ });
255
+ // Add __smoke_test query parameter, preserving any existing query parameters
256
+ if (parsedUrl.searchParams.has("__smoke_test")) {
257
+ console.log(`URL already has __smoke_test parameter: ${url}`);
258
+ }
259
+ else {
260
+ parsedUrl.searchParams.append("__smoke_test", "1");
261
+ log("Added __smoke_test parameter to URL");
262
+ }
263
+ // Navigate to smoke test page
264
+ const smokeUrl = parsedUrl.toString();
265
+ console.log(`🔍 Accessing smoke test page: ${smokeUrl}`);
266
+ await page.goto(smokeUrl, { waitUntil: "networkidle0" });
267
+ log("Page loaded successfully");
268
+ // Track failures to report at the end
269
+ let hasFailures = false;
270
+ let initialServerTestError = null;
271
+ let clientTestError = null;
272
+ let serverRenderCheckError = null;
273
+ let hmrTestError = null;
274
+ // Step 1: Run initial server-side smoke test to check the server state
275
+ log("Running initial server-side smoke test");
276
+ let initialServerResult;
277
+ try {
278
+ // For initial checks: use the fixed initial value (23)
279
+ // For realtime checks: if we've previously updated the value, use that instead of 23
280
+ // For HMR tests: if HMR happened, we'll be back to the initial value (23)
281
+ const expectedValue = isRealtime && timestampState.clientUpdatedValue !== null && skipHmr
282
+ ? timestampState.clientUpdatedValue
283
+ : timestampState.initialServerValue;
284
+ // Check that the server is returning the expected value
285
+ initialServerResult = await checkServerSmoke(page, phase, environment, bail, expectedValue, false);
286
+ log(`${phase} server-side check passed - module variable has expected value ${expectedValue}`);
287
+ // Store the current timestamp for potential future reference
288
+ if (initialServerResult && initialServerResult.timestamp) {
289
+ timestampState.initialServerValue = initialServerResult.timestamp;
290
+ }
291
+ }
292
+ catch (error) {
293
+ hasFailures = true;
294
+ initialServerTestError =
295
+ error instanceof Error ? error : new Error(String(error));
296
+ log("Error during initial server-side smoke test: %O", error);
297
+ console.error(`❌ ${phase} server-side smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
298
+ // If bail is true, stop the tests
299
+ if (bail) {
300
+ throw error;
301
+ }
302
+ console.log("Continuing with client-side smoke test since --bail is not enabled...");
303
+ }
304
+ // Skip client tests if requested
305
+ if (skipClient) {
306
+ log("Skipping client-side smoke test as requested");
307
+ console.log("⏩ Skipping client-side smoke test as requested");
308
+ // If we're running HMR tests and have a target directory
309
+ if (!skipHmr && targetDir) {
310
+ try {
311
+ // Run server HMR test if client tests are skipped
312
+ log("Running server HMR test");
313
+ await testServerComponentHmr(page, targetDir, phase, environment, bail);
314
+ }
315
+ catch (error) {
316
+ hasFailures = true;
317
+ hmrTestError =
318
+ error instanceof Error ? error : new Error(String(error));
319
+ log("Error during HMR test: %O", error);
320
+ if (bail) {
321
+ throw error;
322
+ }
323
+ }
324
+ }
325
+ return;
326
+ }
327
+ // Step 2: Run client-side smoke test to update the server timestamp
328
+ log("Running client-side smoke test");
329
+ let clientResult;
330
+ try {
331
+ clientResult = await checkClientSmoke(page, phase, environment, bail);
332
+ // Store the client-updated timestamp for further tests
333
+ if (clientResult && clientResult.clientTimestamp) {
334
+ timestampState.clientUpdatedValue = clientResult.clientTimestamp;
335
+ log(`Saved client timestamp ${clientResult.clientTimestamp} for verification`);
336
+ }
337
+ }
338
+ catch (error) {
339
+ hasFailures = true;
340
+ clientTestError = error instanceof Error ? error : new Error(String(error));
341
+ log("Error during client-side smoke test: %O", error);
342
+ console.error(`❌ Client-side smoke test failed: ${error instanceof Error ? error.message : String(error)}`);
343
+ // If bail is true, stop the tests
344
+ if (bail) {
345
+ throw error;
346
+ }
347
+ console.log("Continuing with server render check since --bail is not enabled...");
348
+ }
349
+ // Step 3: Check if the server has rendered with the updated timestamp (server render check)
350
+ if (clientResult && clientResult.clientTimestamp) {
351
+ log("Running server render check with client timestamp");
352
+ // Wait a moment for any server renders to complete
353
+ await new Promise((resolve) => setTimeout(resolve, 500));
354
+ try {
355
+ await checkServerSmoke(page, phase, environment, bail, clientResult.clientTimestamp, // Expected to match the client-set timestamp
356
+ true);
357
+ log("Server render check passed - server has updated with client timestamp");
358
+ }
359
+ catch (error) {
360
+ hasFailures = true;
361
+ serverRenderCheckError =
362
+ error instanceof Error ? error : new Error(String(error));
363
+ log("Error during server render check: %O", error);
364
+ console.error(`❌ Server render check failed: ${error instanceof Error ? error.message : String(error)}`);
365
+ // If bail is true, stop the tests
366
+ if (bail) {
367
+ throw error;
368
+ }
369
+ }
370
+ }
371
+ else {
372
+ log("Skipping server render check - no client timestamp available");
373
+ // Update test status for server render check as skipped
374
+ const env = environment === "Development" ? "dev" : "production";
375
+ const serverRenderKey = phase === "Initial" || !phase
376
+ ? "initialServerRenderCheck"
377
+ : "realtimeServerRenderCheck";
378
+ updateTestStatus(env, serverRenderKey, "SKIPPED");
379
+ }
380
+ // Step 4: Run HMR tests if target directory is provided and HMR tests are not skipped
381
+ if (!skipHmr && targetDir && environment === "Development") {
382
+ log(`Starting HMR tests for ${phase} phase`);
383
+ console.log(`\n🔄 Running HMR tests for ${phase} phase...`);
384
+ try {
385
+ // Test server component HMR
386
+ await testServerComponentHmr(page, targetDir, phase, environment, bail);
387
+ // Test client component HMR if client tests aren't skipped
388
+ if (!skipClient) {
389
+ await testClientComponentHmr(page, targetDir, phase, environment, bail);
390
+ }
391
+ }
392
+ catch (error) {
393
+ hasFailures = true;
394
+ hmrTestError = error instanceof Error ? error : new Error(String(error));
395
+ log("Error during HMR tests: %O", error);
396
+ console.error(`❌ HMR tests failed: ${hmrTestError.message}`);
397
+ // If bail is true, stop the tests
398
+ if (bail) {
399
+ throw error;
400
+ }
401
+ }
402
+ }
403
+ else {
404
+ log("Skipping HMR tests - targetDir not provided or skipHmr is true or not in Development environment");
405
+ if (skipHmr) {
406
+ console.log("⏩ Skipping HMR tests as requested");
407
+ }
408
+ else if (!targetDir) {
409
+ console.log("⏩ Skipping HMR tests - target directory not provided");
410
+ }
411
+ else if (environment !== "Development") {
412
+ console.log(`⏩ Skipping HMR tests - not applicable in ${environment} environment`);
413
+ }
414
+ // Update test status for HMR tests as skipped
415
+ const env = environment === "Development" ? "dev" : "production";
416
+ updateTestStatus(env, phase === "Initial" ? "initialServerHmr" : "realtimeServerHmr", "SKIPPED");
417
+ updateTestStatus(env, phase === "Initial" ? "initialClientHmr" : "realtimeClientHmr", "SKIPPED");
418
+ }
419
+ // If there were failures, propagate them
420
+ if (hasFailures) {
421
+ // Combine errors
422
+ const errors = [];
423
+ if (initialServerTestError) {
424
+ errors.push(`Initial server test: ${initialServerTestError.message}`);
425
+ }
426
+ if (clientTestError) {
427
+ errors.push(`Client test: ${clientTestError.message}`);
428
+ }
429
+ if (serverRenderCheckError) {
430
+ errors.push(`Server render check: ${serverRenderCheckError.message}`);
431
+ }
432
+ if (hmrTestError) {
433
+ errors.push(`HMR test: ${hmrTestError.message}`);
434
+ }
435
+ throw new Error(`Multiple test failures: ${errors.join(", ")}`);
436
+ }
437
+ log("URL smoke test completed successfully");
438
+ }
439
+ /**
440
+ * Check server-side smoke test status
441
+ */
442
+ export async function checkServerSmoke(page, phase = "", environment = "Development", bail = false, expectedTimestamp, isServerRenderCheck = false) {
443
+ const checkType = isServerRenderCheck
444
+ ? "Server Render Check"
445
+ : "Initial Check";
446
+ console.log(`🔍 Testing server-side smoke test ${phase ? `(${phase})` : ""} - ${checkType}`);
447
+ // Determine the environment and test key for state update
448
+ const env = environment === "Development" ? "dev" : "production";
449
+ // Determine the appropriate test key based on phase and whether this is a server render check
450
+ let testKey;
451
+ if (isServerRenderCheck) {
452
+ // This is a server render check - use the appropriate key based on phase
453
+ testKey =
454
+ phase === "Initial" || !phase
455
+ ? "initialServerRenderCheck"
456
+ : "realtimeServerRenderCheck";
457
+ }
458
+ else {
459
+ // Regular server-side check
460
+ testKey =
461
+ phase === "Initial" || !phase
462
+ ? "initialServerSide"
463
+ : "realtimeServerSide";
464
+ }
465
+ const result = await page.evaluate(async (expectedTimestamp) => {
466
+ try {
467
+ // Look for smoke test status indicator in the page
468
+ const smokeElement = document.querySelector('[data-testid="health-status"]');
469
+ if (!smokeElement) {
470
+ return {
471
+ status: "error",
472
+ verificationPassed: false,
473
+ error: "Smoke test status element not found in the page",
474
+ };
475
+ }
476
+ // Check if required attributes exist
477
+ const status = smokeElement.getAttribute("data-status");
478
+ if (status === null) {
479
+ return {
480
+ status: "error",
481
+ verificationPassed: false,
482
+ error: "data-status attribute is missing on health-status element",
483
+ };
484
+ }
485
+ const timestamp = parseInt(smokeElement.getAttribute("data-timestamp") || "0", 10);
486
+ const serverTimestamp = parseInt(smokeElement.getAttribute("data-server-timestamp") || "0", 10);
487
+ // If an expected timestamp is provided, verify it matches
488
+ let verificationPassed = true;
489
+ let verificationError;
490
+ if (expectedTimestamp) {
491
+ verificationPassed = timestamp === expectedTimestamp;
492
+ if (!verificationPassed) {
493
+ verificationError = `Server timestamp (${timestamp}) does not match expected (${expectedTimestamp})`;
494
+ }
495
+ }
496
+ return {
497
+ status: status || "error",
498
+ verificationPassed: status === "ok" && verificationPassed,
499
+ timestamp,
500
+ serverTimestamp,
501
+ error: verificationError ||
502
+ (status !== "ok" ? "Smoke test did not return ok status" : undefined),
503
+ };
504
+ }
505
+ catch (error) {
506
+ return {
507
+ status: "error",
508
+ verificationPassed: false,
509
+ error: error instanceof Error ? error.message : String(error),
510
+ };
511
+ }
512
+ }, expectedTimestamp);
513
+ log("Server-side smoke test result: %O", result);
514
+ // Update test status based on result
515
+ updateTestStatus(env, testKey, result.verificationPassed ? "PASSED" : "FAILED");
516
+ // Report the result (this no longer throws errors)
517
+ reportSmokeTestResult(result, isServerRenderCheck ? "Server Render Check" : "Server-side", phase, environment);
518
+ // Handle the error if verification failed
519
+ if (!result.verificationPassed) {
520
+ const errorMessage = `${environment} - ${phase ? `(${phase}) ` : ""}${isServerRenderCheck ? "Server Render Check" : "Server-side smoke test"} failed. Status: ${result.status}${result.error ? `. Error: ${result.error}` : ""}`;
521
+ if (bail) {
522
+ // If bail is true, call fail() which will exit the process
523
+ await fail(new Error(errorMessage), 1, `${environment} - ${isServerRenderCheck ? "Server Render Check" : "Server-side Smoke Test"} (${phase})`);
524
+ }
525
+ else {
526
+ // Otherwise throw an error that can be caught by the caller
527
+ throw new Error(errorMessage);
528
+ }
529
+ }
530
+ return result;
531
+ }
532
+ /**
533
+ * Check client-side smoke test if refresh button is available
534
+ */
535
+ export async function checkClientSmoke(page, phase = "", environment = "Development", bail = false) {
536
+ console.log(`🔍 Testing client-side smoke test ${phase ? `(${phase})` : ""}`);
537
+ // Determine the environment and test key for state update
538
+ const env = environment === "Development" ? "dev" : "production";
539
+ const testKey = phase === "Initial" || !phase ? "initialClientSide" : "realtimeClientSide";
540
+ // Check if refresh button exists
541
+ log("Checking for refresh button");
542
+ const refreshButtonExists = await page.evaluate(() => {
543
+ const button = document.querySelector('[data-testid="refresh-health"]');
544
+ return !!button;
545
+ });
546
+ if (!refreshButtonExists) {
547
+ log("No client-side refresh button found");
548
+ console.warn("⚠️ No client-side refresh button found - this is expected only if testing a non-smoke test page");
549
+ // Look for any other evidence that the page is working
550
+ log("Checking if page content is valid HTML");
551
+ const pageContent = await page.content();
552
+ if (!pageContent.includes("<!DOCTYPE html>")) {
553
+ log("ERROR: Page doesn't appear to be a valid HTML document");
554
+ throw new Error("Page doesn't appear to be a valid HTML document");
555
+ }
556
+ // Check if we're on a smoke test page - in which case missing the refresh button is a failure
557
+ const currentUrl = page.url();
558
+ log("Current URL: %s", currentUrl);
559
+ if (currentUrl.includes("__smoke_test")) {
560
+ log("ERROR: Smoke test page is missing the refresh-health button");
561
+ throw new Error("Smoke test page is missing the refresh-health button - this is a test failure");
562
+ }
563
+ console.log("ℹ️ Basic page structure verified, continuing without client-side smoke test");
564
+ return null;
565
+ }
566
+ log("Clicking refresh button to trigger client-side smoke test");
567
+ await page.click('[data-testid="refresh-health"]');
568
+ // Wait for client-side update to complete
569
+ log("Waiting for client-side test to complete");
570
+ try {
571
+ await page.waitForFunction(() => {
572
+ const clientIndicator = document.querySelector("#smoke-test-client-timestamp");
573
+ return (clientIndicator &&
574
+ +(clientIndicator.getAttribute("data-client-timestamp") ?? "0") > 0);
575
+ }, { timeout: 5000 });
576
+ log("Client-side test completed");
577
+ }
578
+ catch (error) {
579
+ log("ERROR: Timed out waiting for client-side smoke test to complete: %O", error);
580
+ throw new Error(`Timed out waiting for client-side smoke test to complete: ${error instanceof Error ? error.message : String(error)}`);
581
+ }
582
+ const result = await page.evaluate(async () => {
583
+ try {
584
+ const smokeElement = document.querySelector('[data-testid="health-status"]');
585
+ if (!smokeElement) {
586
+ return {
587
+ status: "error",
588
+ verificationPassed: false,
589
+ error: "Smoke test status element not found in the page",
590
+ };
591
+ }
592
+ // Check if required client attributes exist
593
+ const clientTimestampElement = document.querySelector("#smoke-test-client-timestamp");
594
+ if (!clientTimestampElement) {
595
+ return {
596
+ status: "error",
597
+ verificationPassed: false,
598
+ error: "Client timestamp element not found in the page",
599
+ };
600
+ }
601
+ // Check if required client attributes exist
602
+ const clientTimestampAttr = clientTimestampElement.getAttribute("data-client-timestamp");
603
+ if (clientTimestampAttr === null) {
604
+ return {
605
+ status: "error",
606
+ verificationPassed: false,
607
+ error: "data-client-timestamp attribute is missing",
608
+ };
609
+ }
610
+ const clientTimestamp = parseInt(clientTimestampAttr, 10);
611
+ // Check if status attribute exists
612
+ const clientStatus = clientTimestampElement.getAttribute("data-status");
613
+ if (clientStatus === null) {
614
+ return {
615
+ status: "error",
616
+ verificationPassed: false,
617
+ error: "data-status attribute is missing on client timestamp element",
618
+ };
619
+ }
620
+ // Check if verified attribute exists
621
+ if (clientTimestampElement.getAttribute("data-verified") === null) {
622
+ return {
623
+ status: "error",
624
+ verificationPassed: false,
625
+ error: "data-verified attribute is missing on client timestamp element",
626
+ };
627
+ }
628
+ // Get client verification result directly
629
+ const verificationPassed = clientTimestampElement.getAttribute("data-verified") === "true";
630
+ return {
631
+ status: clientStatus,
632
+ verificationPassed: clientStatus === "ok" && verificationPassed,
633
+ clientTimestamp,
634
+ error: clientStatus !== "ok"
635
+ ? "Client smoke test did not return ok status"
636
+ : undefined,
637
+ };
638
+ }
639
+ catch (error) {
640
+ return {
641
+ status: "error",
642
+ verificationPassed: false,
643
+ error: error instanceof Error ? error.message : String(error),
644
+ };
645
+ }
646
+ });
647
+ log("Client-side smoke test result: %O", result);
648
+ // Update test status based on result
649
+ updateTestStatus(env, testKey, result.verificationPassed ? "PASSED" : "FAILED");
650
+ // Report the result (this no longer throws errors)
651
+ reportSmokeTestResult(result, "Client-side", phase, environment);
652
+ // Handle the error if verification failed
653
+ if (!result.verificationPassed) {
654
+ const errorMessage = `${environment} - ${phase ? `(${phase}) ` : ""}Client-side smoke test failed. Status: ${result.status}${result.error ? `. Error: ${result.error}` : ""}`;
655
+ if (bail) {
656
+ // If bail is true, call fail() which will exit the process
657
+ await fail(new Error(errorMessage), 1, `${environment} - Client-side Smoke Test (${phase})`);
658
+ }
659
+ else {
660
+ // Otherwise throw an error that can be caught by the caller
661
+ throw new Error(errorMessage);
662
+ }
663
+ }
664
+ return result;
665
+ }
666
+ /**
667
+ * Upgrade to realtime mode
668
+ */
669
+ export async function upgradeToRealtime(page, environment = "Development", // Add environment parameter with default
670
+ bail = false) {
671
+ console.log("\n📡 Upgrading to realtime mode");
672
+ // Determine the environment for state update
673
+ const env = environment === "Development" ? "dev" : "production";
674
+ const upgradeResult = await page.evaluate(async () => {
675
+ try {
676
+ // Check if __rw API exists
677
+ if (typeof window.__rw !== "object" ||
678
+ typeof window.__rw.upgradeToRealtime !== "function") {
679
+ return {
680
+ success: false,
681
+ message: "The __rw API or upgradeToRealtime method is not available",
682
+ };
683
+ }
684
+ await window.__rw.upgradeToRealtime();
685
+ // If we get here, it succeeded
686
+ return { success: true };
687
+ }
688
+ catch (error) {
689
+ return {
690
+ success: false,
691
+ message: error instanceof Error ? error.message : String(error),
692
+ };
693
+ }
694
+ });
695
+ if (!upgradeResult.success) {
696
+ log("ERROR: Failed to upgrade to realtime mode: %s", upgradeResult.message);
697
+ const errorMessage = `Failed to upgrade to realtime mode: ${upgradeResult.message}`;
698
+ // Update test status to FAILED
699
+ updateTestStatus(env, "realtimeUpgrade", "FAILED");
700
+ if (bail) {
701
+ // If bail is true, call fail() which will exit the process
702
+ await fail(new Error(errorMessage), 1, `${environment} - Realtime Upgrade`);
703
+ return; // This will never be reached due to fail() exiting
704
+ }
705
+ else {
706
+ // Otherwise throw an error that can be caught by the caller
707
+ throw new Error(errorMessage);
708
+ }
709
+ }
710
+ // Update test status to PASSED
711
+ updateTestStatus(env, "realtimeUpgrade", "PASSED");
712
+ console.log("✅ Successfully upgraded to realtime mode");
713
+ }
714
+ /**
715
+ * DRY: checkServerUp now checks both root and custom path if needed
716
+ */
717
+ export async function checkServerUp(baseUrl, customPath = "/", retries = RETRIES, bail = false) {
718
+ // Always check root first, then custom path if different
719
+ const pathsToCheck = ["/"];
720
+ if (customPath !== "/" && customPath !== "") {
721
+ pathsToCheck.push(customPath);
722
+ }
723
+ for (const path of pathsToCheck) {
724
+ const url = baseUrl + (path.startsWith("/") ? path : "/" + path);
725
+ log("Checking if server is up at %s (max retries: %d)", url, retries);
726
+ let up = false;
727
+ for (let i = 0; i < retries; i++) {
728
+ try {
729
+ log("Attempt %d/%d to check server at %s", i + 1, retries, url);
730
+ console.log(`Checking if server is up at ${url} (attempt ${i + 1}/${retries})...`);
731
+ await $ `curl -s -o /dev/null -w "%{http_code}" ${url}`;
732
+ log("Server is up at %s", url);
733
+ console.log(`✅ Server is up at ${url}`);
734
+ up = true;
735
+ break;
736
+ }
737
+ catch (error) {
738
+ if (i === retries - 1) {
739
+ log("ERROR: Server at %s did not become available after %d attempts", url, retries);
740
+ const errorMessage = `Server at ${url} did not become available after ${retries} attempts`;
741
+ if (bail) {
742
+ // If bail is true, call fail() which will exit the process
743
+ await fail(new Error(errorMessage), 1, `Server Availability Check: ${url}`);
744
+ return false; // This will never be reached due to fail() exiting
745
+ }
746
+ else {
747
+ // Otherwise throw an error that can be caught by the caller
748
+ throw new Error(errorMessage);
749
+ }
750
+ }
751
+ log("Server not up yet, retrying in 2 seconds");
752
+ console.log(`Server not up yet, retrying in 2 seconds...`);
753
+ await new Promise((resolve) => setTimeout(() => resolve(), 2000));
754
+ }
755
+ }
756
+ if (!up)
757
+ return false;
758
+ }
759
+ return true;
760
+ }
761
+ /**
762
+ * Perform only the realtime upgrade and tests without doing initial checks
763
+ */
764
+ async function realtimeOnlyFlow(page, url, artifactDir, bail, skipClient, environment, targetDir, skipHmr = false) {
765
+ let hasFailures = false;
766
+ let realtimeError = null;
767
+ // Create timestamp state for the realtime-only flow
768
+ const timestampState = {
769
+ initialServerValue: 23, // This is the default initial module-level value
770
+ clientUpdatedValue: null,
771
+ };
772
+ try {
773
+ log("Performing realtime-only smoke test");
774
+ await checkUrlSmoke(page, url, true, bail, skipClient, environment, timestampState, targetDir, skipHmr);
775
+ // Take a screenshot of the realtime test
776
+ await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-passed`);
777
+ }
778
+ catch (error) {
779
+ hasFailures = true;
780
+ realtimeError = error instanceof Error ? error : new Error(String(error));
781
+ log("Error during realtime-only test: %O", error);
782
+ console.error(`❌ Realtime test failed: ${realtimeError.message}`);
783
+ // Take a failure screenshot
784
+ await takeScreenshot(page, url, artifactDir, `${environment.toLowerCase()}-realtime-failed`).catch((e) => log("Failed to take error screenshot: %O", e));
785
+ // If bail is true, propagate the error
786
+ if (bail) {
787
+ throw error;
788
+ }
789
+ }
790
+ return { hasFailures, error: realtimeError };
791
+ }
792
+ /**
793
+ * HMR test for server component
794
+ * Updates the server component and verifies that HMR applies the changes
795
+ */
796
+ export async function testServerComponentHmr(page, targetDir, phase = "", environment = "Development", bail = false) {
797
+ const testPhase = phase ? phase : "Initial";
798
+ // Skip HMR tests in production environments
799
+ if (environment !== "Development") {
800
+ console.log(`⏩ Skipping server HMR test in ${environment} environment`);
801
+ // Update test status to SKIPPED
802
+ const env = environment === "Development" ? "dev" : "production";
803
+ const testKey = testPhase === "Initial" || !testPhase
804
+ ? "initialServerHmr"
805
+ : "realtimeServerHmr";
806
+ updateTestStatus(env, testKey, "SKIPPED");
807
+ return false;
808
+ }
809
+ console.log(`🔄 Testing ${testPhase} Server Component HMR`);
810
+ // Determine the environment and test key for state update
811
+ const env = environment === "Development" ? "dev" : "production";
812
+ const testKey = testPhase === "Initial" || !testPhase
813
+ ? "initialServerHmr"
814
+ : "realtimeServerHmr";
815
+ try {
816
+ // First, verify the server HMR marker exists
817
+ log("Checking for server HMR marker");
818
+ const markerExists = await page.evaluate(() => {
819
+ const marker = document.querySelector('[data-testid="server-hmr-marker"]');
820
+ return !!marker;
821
+ });
822
+ if (!markerExists) {
823
+ log("Server HMR marker not found");
824
+ console.warn("⚠️ Server HMR marker not found in the page - skipping server HMR test");
825
+ updateTestStatus(env, testKey, "SKIPPED");
826
+ return false;
827
+ }
828
+ // Get the initial attributes before making changes
829
+ const initialAttributes = await page.evaluate(() => {
830
+ const marker = document.querySelector('[data-testid="server-hmr-marker"]');
831
+ if (!marker)
832
+ return null;
833
+ return {
834
+ text: marker.getAttribute("data-hmr-text"),
835
+ timestamp: marker.getAttribute("data-hmr-timestamp"),
836
+ content: marker.textContent,
837
+ };
838
+ });
839
+ log("Initial server HMR marker state: %O", initialAttributes);
840
+ // Find the SmokeTest.tsx file path
841
+ const smokePath = join(targetDir, "src", "app", "components", "__SmokeTest.tsx");
842
+ log("Looking for smoke test file at: %s", smokePath);
843
+ // Read the current file content
844
+ const fs = await import("fs/promises");
845
+ const fileContent = await fs.readFile(smokePath, "utf-8");
846
+ // Define the new content with updated HMR marker
847
+ const newTimestamp = Date.now();
848
+ const updatedContent = fileContent
849
+ .replace(/data-hmr-text="[^"]*"/g, `data-hmr-text="updated-${newTimestamp}"`)
850
+ .replace(/data-hmr-timestamp=\{[^}]*\}/g, `data-hmr-timestamp={${newTimestamp}}`)
851
+ .replace(/Server Component HMR: <span>[^<]*<\/span>/g, `Server Component HMR: <span>Updated Text ${newTimestamp}</span>`);
852
+ // Write the updated file
853
+ log("Writing updated server component content");
854
+ await fs.writeFile(smokePath, updatedContent, "utf-8");
855
+ // Wait for HMR to apply changes
856
+ console.log("Waiting for server HMR to update component...");
857
+ // Wait for the data-hmr-text attribute to change
858
+ log("Waiting for server HMR update to apply");
859
+ try {
860
+ await page.waitForFunction((timestamp) => {
861
+ const marker = document.querySelector('[data-testid="server-hmr-marker"]');
862
+ if (!marker)
863
+ return false;
864
+ const currentText = marker.getAttribute("data-hmr-text");
865
+ return (currentText &&
866
+ currentText.includes("updated-") &&
867
+ currentText.includes(timestamp));
868
+ }, { timeout: 10000 }, newTimestamp.toString());
869
+ log("Server HMR update detected");
870
+ }
871
+ catch (error) {
872
+ log("ERROR: Server HMR update not detected: %O", error);
873
+ updateTestStatus(env, testKey, "FAILED");
874
+ if (bail) {
875
+ await fail(new Error(`Server HMR test failed: Update not detected within timeout`), 1, `${environment} - Server HMR Test (${testPhase})`);
876
+ }
877
+ return false;
878
+ }
879
+ // Verify final state
880
+ const updatedAttributes = await page.evaluate(() => {
881
+ const marker = document.querySelector('[data-testid="server-hmr-marker"]');
882
+ if (!marker)
883
+ return null;
884
+ return {
885
+ text: marker.getAttribute("data-hmr-text"),
886
+ timestamp: marker.getAttribute("data-hmr-timestamp"),
887
+ content: marker.textContent,
888
+ };
889
+ });
890
+ log("Updated server HMR marker state: %O", updatedAttributes);
891
+ const hmrSuccess = updatedAttributes &&
892
+ updatedAttributes.text &&
893
+ updatedAttributes.text.includes("updated-");
894
+ updateTestStatus(env, testKey, hmrSuccess ? "PASSED" : "FAILED");
895
+ if (hmrSuccess) {
896
+ console.log("✅ Server component HMR test passed");
897
+ return true;
898
+ }
899
+ else {
900
+ console.error("❌ Server component HMR test failed: Content did not update properly");
901
+ if (bail) {
902
+ await fail(new Error(`Server HMR test failed: Content did not update properly`), 1, `${environment} - Server HMR Test (${testPhase})`);
903
+ }
904
+ return false;
905
+ }
906
+ }
907
+ catch (error) {
908
+ log("Error during server HMR test: %O", error);
909
+ console.error(`❌ Server HMR test failed: ${error instanceof Error ? error.message : String(error)}`);
910
+ updateTestStatus(env, testKey, "FAILED");
911
+ if (bail) {
912
+ await fail(error instanceof Error ? error : new Error(String(error)), 1, `${environment} - Server HMR Test (${testPhase})`);
913
+ }
914
+ return false;
915
+ }
916
+ }
917
+ /**
918
+ * HMR test for client component
919
+ * Updates the client component and verifies that HMR applies the changes
920
+ */
921
+ export async function testClientComponentHmr(page, targetDir, phase = "", environment = "Development", bail = false) {
922
+ const testPhase = phase ? phase : "Initial";
923
+ // Skip HMR tests in production environments
924
+ if (environment !== "Development") {
925
+ console.log(`⏩ Skipping client HMR test in ${environment} environment`);
926
+ // Update test status to SKIPPED
927
+ const env = environment === "Development" ? "dev" : "production";
928
+ const testKey = testPhase === "Initial" || !testPhase
929
+ ? "initialClientHmr"
930
+ : "realtimeClientHmr";
931
+ updateTestStatus(env, testKey, "SKIPPED");
932
+ return false;
933
+ }
934
+ console.log(`🔄 Testing ${testPhase} Client Component HMR`);
935
+ // Determine the environment and test key for state update
936
+ const env = environment === "Development" ? "dev" : "production";
937
+ const testKey = testPhase === "Initial" || !testPhase
938
+ ? "initialClientHmr"
939
+ : "realtimeClientHmr";
940
+ try {
941
+ // First, verify the client HMR marker exists
942
+ log("Checking for client HMR marker");
943
+ const markerExists = await page.evaluate(() => {
944
+ const marker = document.querySelector('[data-testid="client-hmr-marker"]');
945
+ return !!marker;
946
+ });
947
+ if (!markerExists) {
948
+ log("Client HMR marker not found");
949
+ console.warn("⚠️ Client HMR marker not found in the page - skipping client HMR test");
950
+ updateTestStatus(env, testKey, "SKIPPED");
951
+ return false;
952
+ }
953
+ // Get the initial attributes before making changes
954
+ const initialAttributes = await page.evaluate(() => {
955
+ const marker = document.querySelector('[data-testid="client-hmr-marker"]');
956
+ if (!marker)
957
+ return null;
958
+ return {
959
+ text: marker.getAttribute("data-hmr-text"),
960
+ timestamp: marker.getAttribute("data-hmr-timestamp"),
961
+ content: marker.textContent,
962
+ };
963
+ });
964
+ log("Initial client HMR marker state: %O", initialAttributes);
965
+ // Find the SmokeTestClient.tsx file path
966
+ const clientPath = join(targetDir, "src", "app", "components", "__SmokeTestClient.tsx");
967
+ log("Looking for client component file at: %s", clientPath);
968
+ // Read the current file content
969
+ const fs = await import("fs/promises");
970
+ const fileContent = await fs.readFile(clientPath, "utf-8");
971
+ // Define the new content with updated HMR marker
972
+ const newTimestamp = Date.now();
973
+ const updatedContent = fileContent
974
+ .replace(/data-hmr-text="[^"]*"/g, `data-hmr-text="updated-${newTimestamp}"`)
975
+ .replace(/data-hmr-timestamp=\{[^}]*\}/g, `data-hmr-timestamp={${newTimestamp}}`)
976
+ .replace(/Client Component HMR: <span>[^<]*<\/span>/g, `Client Component HMR: <span>Updated Text ${newTimestamp}</span>`);
977
+ // Write the updated file
978
+ log("Writing updated client component content");
979
+ await fs.writeFile(clientPath, updatedContent, "utf-8");
980
+ // Wait for HMR to apply changes
981
+ console.log("Waiting for client HMR to update component...");
982
+ // Wait for the data-hmr-text attribute to change
983
+ log("Waiting for client HMR update to apply");
984
+ try {
985
+ await page.waitForFunction((timestamp) => {
986
+ const marker = document.querySelector('[data-testid="client-hmr-marker"]');
987
+ if (!marker)
988
+ return false;
989
+ const currentText = marker.getAttribute("data-hmr-text");
990
+ return (currentText &&
991
+ currentText.includes("updated-") &&
992
+ currentText.includes(timestamp));
993
+ }, { timeout: 10000 }, newTimestamp.toString());
994
+ log("Client HMR update detected");
995
+ }
996
+ catch (error) {
997
+ log("ERROR: Client HMR update not detected: %O", error);
998
+ updateTestStatus(env, testKey, "FAILED");
999
+ if (bail) {
1000
+ await fail(new Error(`Client HMR test failed: Update not detected within timeout`), 1, `${environment} - Client HMR Test (${testPhase})`);
1001
+ }
1002
+ return false;
1003
+ }
1004
+ // Verify final state
1005
+ const updatedAttributes = await page.evaluate(() => {
1006
+ const marker = document.querySelector('[data-testid="client-hmr-marker"]');
1007
+ if (!marker)
1008
+ return null;
1009
+ return {
1010
+ text: marker.getAttribute("data-hmr-text"),
1011
+ timestamp: marker.getAttribute("data-hmr-timestamp"),
1012
+ content: marker.textContent,
1013
+ };
1014
+ });
1015
+ log("Updated client HMR marker state: %O", updatedAttributes);
1016
+ const hmrSuccess = updatedAttributes &&
1017
+ updatedAttributes.text &&
1018
+ updatedAttributes.text.includes("updated-");
1019
+ updateTestStatus(env, testKey, hmrSuccess ? "PASSED" : "FAILED");
1020
+ if (hmrSuccess) {
1021
+ console.log("✅ Client component HMR test passed");
1022
+ return true;
1023
+ }
1024
+ else {
1025
+ console.error("❌ Client component HMR test failed: Content did not update properly");
1026
+ if (bail) {
1027
+ await fail(new Error(`Client HMR test failed: Content did not update properly`), 1, `${environment} - Client HMR Test (${testPhase})`);
1028
+ }
1029
+ return false;
1030
+ }
1031
+ }
1032
+ catch (error) {
1033
+ log("Error during client HMR test: %O", error);
1034
+ console.error(`❌ Client HMR test failed: ${error instanceof Error ? error.message : String(error)}`);
1035
+ updateTestStatus(env, testKey, "FAILED");
1036
+ if (bail) {
1037
+ await fail(error instanceof Error ? error : new Error(String(error)), 1, `${environment} - Client HMR Test (${testPhase})`);
1038
+ }
1039
+ return false;
1040
+ }
1041
+ }