@nifrajs/web 5.5.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/dist/boundary.d.ts +67 -7
  3. package/dist/boundary.d.ts.map +1 -1
  4. package/dist/boundary.js +6 -4
  5. package/dist/boundary.js.map +1 -1
  6. package/dist/build-plan.d.ts +3 -0
  7. package/dist/build-plan.d.ts.map +1 -1
  8. package/dist/build-plan.js.map +1 -1
  9. package/dist/build-vite.d.ts.map +1 -1
  10. package/dist/build-vite.js +2 -0
  11. package/dist/build-vite.js.map +1 -1
  12. package/dist/build.d.ts +9 -2
  13. package/dist/build.d.ts.map +1 -1
  14. package/dist/build.js +9 -0
  15. package/dist/build.js.map +1 -1
  16. package/dist/client.d.ts +2 -1
  17. package/dist/client.d.ts.map +1 -1
  18. package/dist/client.js +140 -12
  19. package/dist/client.js.map +1 -1
  20. package/dist/csp.d.ts +8 -0
  21. package/dist/csp.d.ts.map +1 -1
  22. package/dist/csp.js +11 -2
  23. package/dist/csp.js.map +1 -1
  24. package/dist/deferred.d.ts +6 -4
  25. package/dist/deferred.d.ts.map +1 -1
  26. package/dist/deferred.js +10 -6
  27. package/dist/deferred.js.map +1 -1
  28. package/dist/dev-feed.d.ts.map +1 -1
  29. package/dist/dev-feed.js +11 -2
  30. package/dist/dev-feed.js.map +1 -1
  31. package/dist/dev-session.d.ts.map +1 -1
  32. package/dist/dev-session.js +13 -6
  33. package/dist/dev-session.js.map +1 -1
  34. package/dist/dev.d.ts +4 -1
  35. package/dist/dev.d.ts.map +1 -1
  36. package/dist/dev.js +12 -5
  37. package/dist/dev.js.map +1 -1
  38. package/dist/diagnostic-catalog.d.ts +4 -0
  39. package/dist/diagnostic-catalog.d.ts.map +1 -1
  40. package/dist/diagnostic-catalog.js +48 -0
  41. package/dist/diagnostic-catalog.js.map +1 -1
  42. package/dist/diagnostic.d.ts +1 -1
  43. package/dist/diagnostic.d.ts.map +1 -1
  44. package/dist/diagnostic.js +1 -1
  45. package/dist/diagnostic.js.map +1 -1
  46. package/dist/fn.d.ts +5 -3
  47. package/dist/fn.d.ts.map +1 -1
  48. package/dist/fn.js +20 -5
  49. package/dist/fn.js.map +1 -1
  50. package/dist/fs.d.ts.map +1 -1
  51. package/dist/fs.js +13 -3
  52. package/dist/fs.js.map +1 -1
  53. package/dist/identity.d.ts +22 -0
  54. package/dist/identity.d.ts.map +1 -0
  55. package/dist/identity.js +60 -0
  56. package/dist/identity.js.map +1 -0
  57. package/dist/index.d.ts +7 -6
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +4 -2
  60. package/dist/index.js.map +1 -1
  61. package/dist/internal/cdn/cloudflare.d.ts +2 -1
  62. package/dist/internal/cdn/cloudflare.d.ts.map +1 -1
  63. package/dist/internal/cdn/cloudflare.js +2 -1
  64. package/dist/internal/cdn/cloudflare.js.map +1 -1
  65. package/dist/internal/certified-components.d.ts +1 -1
  66. package/dist/internal/certified-components.js +1 -1
  67. package/dist/internal/codegen.d.ts +11 -10
  68. package/dist/internal/codegen.d.ts.map +1 -1
  69. package/dist/internal/codegen.js +65 -8
  70. package/dist/internal/codegen.js.map +1 -1
  71. package/dist/internal/dev-websocket.d.ts +73 -0
  72. package/dist/internal/dev-websocket.d.ts.map +1 -0
  73. package/dist/internal/dev-websocket.js +145 -0
  74. package/dist/internal/dev-websocket.js.map +1 -0
  75. package/dist/internal/hole-runtime.d.ts +66 -0
  76. package/dist/internal/hole-runtime.d.ts.map +1 -0
  77. package/dist/internal/hole-runtime.js +211 -0
  78. package/dist/internal/hole-runtime.js.map +1 -0
  79. package/dist/internal/identity-epoch.d.ts +50 -0
  80. package/dist/internal/identity-epoch.d.ts.map +1 -0
  81. package/dist/internal/identity-epoch.js +179 -0
  82. package/dist/internal/identity-epoch.js.map +1 -0
  83. package/dist/internal/invalidation-contract.d.ts +23 -0
  84. package/dist/internal/invalidation-contract.d.ts.map +1 -0
  85. package/dist/internal/invalidation-contract.js +11 -0
  86. package/dist/internal/invalidation-contract.js.map +1 -0
  87. package/dist/internal/invalidation.d.ts +62 -0
  88. package/dist/internal/invalidation.d.ts.map +1 -0
  89. package/dist/internal/invalidation.js +149 -0
  90. package/dist/internal/invalidation.js.map +1 -0
  91. package/dist/internal/island-bindings.d.ts +51 -0
  92. package/dist/internal/island-bindings.d.ts.map +1 -0
  93. package/dist/internal/island-bindings.js +147 -0
  94. package/dist/internal/island-bindings.js.map +1 -0
  95. package/dist/internal/island-list.d.ts +26 -0
  96. package/dist/internal/island-list.d.ts.map +1 -0
  97. package/dist/internal/island-list.js +78 -0
  98. package/dist/internal/island-list.js.map +1 -0
  99. package/dist/internal/island-mount.d.ts +4 -0
  100. package/dist/internal/island-mount.d.ts.map +1 -0
  101. package/dist/internal/island-mount.js +66 -0
  102. package/dist/internal/island-mount.js.map +1 -0
  103. package/dist/internal/island-owner.d.ts +5 -0
  104. package/dist/internal/island-owner.d.ts.map +1 -0
  105. package/dist/internal/island-owner.js +20 -0
  106. package/dist/internal/island-owner.js.map +1 -0
  107. package/dist/internal/island-resource.d.ts +30 -0
  108. package/dist/internal/island-resource.d.ts.map +1 -0
  109. package/dist/internal/island-resource.js +57 -0
  110. package/dist/internal/island-resource.js.map +1 -0
  111. package/dist/internal/island-signals.d.ts +51 -0
  112. package/dist/internal/island-signals.d.ts.map +1 -0
  113. package/dist/internal/island-signals.js +220 -0
  114. package/dist/internal/island-signals.js.map +1 -0
  115. package/dist/internal/keep-alive-table.d.ts +23 -0
  116. package/dist/internal/keep-alive-table.d.ts.map +1 -0
  117. package/dist/internal/keep-alive-table.js +53 -0
  118. package/dist/internal/keep-alive-table.js.map +1 -0
  119. package/dist/internal/live-contract.d.ts +96 -0
  120. package/dist/internal/live-contract.d.ts.map +1 -0
  121. package/dist/internal/live-contract.js +5 -0
  122. package/dist/internal/live-contract.js.map +1 -0
  123. package/dist/internal/live-queries.d.ts +22 -0
  124. package/dist/internal/live-queries.d.ts.map +1 -0
  125. package/dist/internal/live-queries.js +54 -0
  126. package/dist/internal/live-queries.js.map +1 -0
  127. package/dist/internal/page-events.d.ts +10 -0
  128. package/dist/internal/page-events.d.ts.map +1 -0
  129. package/dist/internal/page-events.js +26 -0
  130. package/dist/internal/page-events.js.map +1 -0
  131. package/dist/internal/page-execution.d.ts +14 -1
  132. package/dist/internal/page-execution.d.ts.map +1 -1
  133. package/dist/internal/page-execution.js +415 -31
  134. package/dist/internal/page-execution.js.map +1 -1
  135. package/dist/internal/ppr/admission.d.ts +83 -0
  136. package/dist/internal/ppr/admission.d.ts.map +1 -0
  137. package/dist/internal/ppr/admission.js +221 -0
  138. package/dist/internal/ppr/admission.js.map +1 -0
  139. package/dist/internal/ppr/canary.d.ts +24 -0
  140. package/dist/internal/ppr/canary.d.ts.map +1 -0
  141. package/dist/internal/ppr/canary.js +102 -0
  142. package/dist/internal/ppr/canary.js.map +1 -0
  143. package/dist/internal/ppr/compile-shell.d.ts +39 -0
  144. package/dist/internal/ppr/compile-shell.d.ts.map +1 -0
  145. package/dist/internal/ppr/compile-shell.js +298 -0
  146. package/dist/internal/ppr/compile-shell.js.map +1 -0
  147. package/dist/internal/ppr/contract.d.ts +177 -0
  148. package/dist/internal/ppr/contract.d.ts.map +1 -0
  149. package/dist/internal/ppr/contract.js +80 -0
  150. package/dist/internal/ppr/contract.js.map +1 -0
  151. package/dist/internal/ppr/frames.d.ts +71 -0
  152. package/dist/internal/ppr/frames.d.ts.map +1 -0
  153. package/dist/internal/ppr/frames.js +119 -0
  154. package/dist/internal/ppr/frames.js.map +1 -0
  155. package/dist/internal/ppr/integration.d.ts +19 -0
  156. package/dist/internal/ppr/integration.d.ts.map +1 -0
  157. package/dist/internal/ppr/integration.js +106 -0
  158. package/dist/internal/ppr/integration.js.map +1 -0
  159. package/dist/internal/ppr/options.d.ts +96 -0
  160. package/dist/internal/ppr/options.d.ts.map +1 -0
  161. package/dist/internal/ppr/options.js +125 -0
  162. package/dist/internal/ppr/options.js.map +1 -0
  163. package/dist/internal/ppr/route-rules.d.ts +12 -0
  164. package/dist/internal/ppr/route-rules.d.ts.map +1 -0
  165. package/dist/internal/ppr/route-rules.js +53 -0
  166. package/dist/internal/ppr/route-rules.js.map +1 -0
  167. package/dist/internal/ppr/runtime.d.ts +29 -0
  168. package/dist/internal/ppr/runtime.d.ts.map +1 -0
  169. package/dist/internal/ppr/runtime.js +32 -0
  170. package/dist/internal/ppr/runtime.js.map +1 -0
  171. package/dist/internal/ppr/server.d.ts +29 -0
  172. package/dist/internal/ppr/server.d.ts.map +1 -0
  173. package/dist/internal/ppr/server.js +1407 -0
  174. package/dist/internal/ppr/server.js.map +1 -0
  175. package/dist/internal/ppr/shell-store.d.ts +38 -0
  176. package/dist/internal/ppr/shell-store.d.ts.map +1 -0
  177. package/dist/internal/ppr/shell-store.js +90 -0
  178. package/dist/internal/ppr/shell-store.js.map +1 -0
  179. package/dist/internal/prerendered-pages.d.ts +1 -1
  180. package/dist/internal/prerendered-pages.d.ts.map +1 -1
  181. package/dist/internal/prerendered-pages.js +7 -4
  182. package/dist/internal/prerendered-pages.js.map +1 -1
  183. package/dist/internal/query-digest.d.ts +9 -0
  184. package/dist/internal/query-digest.d.ts.map +1 -0
  185. package/dist/internal/query-digest.js +23 -0
  186. package/dist/internal/query-digest.js.map +1 -0
  187. package/dist/internal/query-runtime.d.ts +6 -1
  188. package/dist/internal/query-runtime.d.ts.map +1 -1
  189. package/dist/internal/query-runtime.js +9 -0
  190. package/dist/internal/query-runtime.js.map +1 -1
  191. package/dist/internal/query-seam.d.ts +29 -0
  192. package/dist/internal/query-seam.d.ts.map +1 -0
  193. package/dist/internal/query-seam.js +23 -0
  194. package/dist/internal/query-seam.js.map +1 -0
  195. package/dist/internal/render-document.d.ts +28 -1
  196. package/dist/internal/render-document.d.ts.map +1 -1
  197. package/dist/internal/render-document.js +83 -18
  198. package/dist/internal/render-document.js.map +1 -1
  199. package/dist/internal/request-abort.d.ts +8 -0
  200. package/dist/internal/request-abort.d.ts.map +1 -0
  201. package/dist/internal/request-abort.js +22 -0
  202. package/dist/internal/request-abort.js.map +1 -0
  203. package/dist/internal/server-boundary.d.ts +2 -0
  204. package/dist/internal/server-boundary.d.ts.map +1 -1
  205. package/dist/internal/server-boundary.js +8 -1
  206. package/dist/internal/server-boundary.js.map +1 -1
  207. package/dist/internal/share-data.d.ts +10 -0
  208. package/dist/internal/share-data.d.ts.map +1 -0
  209. package/dist/internal/share-data.js +135 -0
  210. package/dist/internal/share-data.js.map +1 -0
  211. package/dist/internal/store-signature.d.ts +15 -0
  212. package/dist/internal/store-signature.d.ts.map +1 -0
  213. package/dist/internal/store-signature.js +114 -0
  214. package/dist/internal/store-signature.js.map +1 -0
  215. package/dist/internal/vite-plugin-compat.d.ts +2 -2
  216. package/dist/internal/vite-plugin-compat.js +2 -2
  217. package/dist/internal/web-app.d.ts +25 -0
  218. package/dist/internal/web-app.d.ts.map +1 -1
  219. package/dist/internal/web-app.js +78 -3
  220. package/dist/internal/web-app.js.map +1 -1
  221. package/dist/islands-swap.d.ts +41 -0
  222. package/dist/islands-swap.d.ts.map +1 -0
  223. package/dist/islands-swap.js +270 -0
  224. package/dist/islands-swap.js.map +1 -0
  225. package/dist/islands.d.ts +86 -24
  226. package/dist/islands.d.ts.map +1 -1
  227. package/dist/islands.js +138 -78
  228. package/dist/islands.js.map +1 -1
  229. package/dist/isr.d.ts +67 -13
  230. package/dist/isr.d.ts.map +1 -1
  231. package/dist/isr.js +247 -99
  232. package/dist/isr.js.map +1 -1
  233. package/dist/keep-alive.d.ts +147 -0
  234. package/dist/keep-alive.d.ts.map +1 -0
  235. package/dist/keep-alive.js +1069 -0
  236. package/dist/keep-alive.js.map +1 -0
  237. package/dist/live/bus.d.ts +56 -0
  238. package/dist/live/bus.d.ts.map +1 -0
  239. package/dist/live/bus.js +32 -0
  240. package/dist/live/bus.js.map +1 -0
  241. package/dist/live/core.d.ts +25 -0
  242. package/dist/live/core.d.ts.map +1 -0
  243. package/dist/live/core.js +29 -0
  244. package/dist/live/core.js.map +1 -0
  245. package/dist/live/history.d.ts +38 -0
  246. package/dist/live/history.d.ts.map +1 -0
  247. package/dist/live/history.js +104 -0
  248. package/dist/live/history.js.map +1 -0
  249. package/dist/live/hub-protocol.d.ts +46 -0
  250. package/dist/live/hub-protocol.d.ts.map +1 -0
  251. package/dist/live/hub-protocol.js +108 -0
  252. package/dist/live/hub-protocol.js.map +1 -0
  253. package/dist/live/hub.d.ts +181 -0
  254. package/dist/live/hub.d.ts.map +1 -0
  255. package/dist/live/hub.js +539 -0
  256. package/dist/live/hub.js.map +1 -0
  257. package/dist/live/keyring.d.ts +87 -0
  258. package/dist/live/keyring.d.ts.map +1 -0
  259. package/dist/live/keyring.js +260 -0
  260. package/dist/live/keyring.js.map +1 -0
  261. package/dist/live/read-capped.d.ts +6 -0
  262. package/dist/live/read-capped.d.ts.map +1 -0
  263. package/dist/live/read-capped.js +42 -0
  264. package/dist/live/read-capped.js.map +1 -0
  265. package/dist/live/revocations.d.ts +52 -0
  266. package/dist/live/revocations.d.ts.map +1 -0
  267. package/dist/live/revocations.js +116 -0
  268. package/dist/live/revocations.js.map +1 -0
  269. package/dist/live/settings.d.ts +14 -0
  270. package/dist/live/settings.d.ts.map +1 -0
  271. package/dist/live/settings.js +38 -0
  272. package/dist/live/settings.js.map +1 -0
  273. package/dist/live-client.d.ts +34 -0
  274. package/dist/live-client.d.ts.map +1 -0
  275. package/dist/live-client.js +840 -0
  276. package/dist/live-client.js.map +1 -0
  277. package/dist/live-hub.d.ts +130 -0
  278. package/dist/live-hub.d.ts.map +1 -0
  279. package/dist/live-hub.js +507 -0
  280. package/dist/live-hub.js.map +1 -0
  281. package/dist/live-redis.d.ts +48 -0
  282. package/dist/live-redis.d.ts.map +1 -0
  283. package/dist/live-redis.js +201 -0
  284. package/dist/live-redis.js.map +1 -0
  285. package/dist/live.d.ts +94 -0
  286. package/dist/live.d.ts.map +1 -0
  287. package/dist/live.js +869 -0
  288. package/dist/live.js.map +1 -0
  289. package/dist/manifest.d.ts +113 -2
  290. package/dist/manifest.d.ts.map +1 -1
  291. package/dist/manifest.js +109 -5
  292. package/dist/manifest.js.map +1 -1
  293. package/dist/navigation.d.ts +9 -3
  294. package/dist/navigation.d.ts.map +1 -1
  295. package/dist/navigation.js +13 -3
  296. package/dist/navigation.js.map +1 -1
  297. package/dist/plugins/vite-zone-guard.d.ts +6 -0
  298. package/dist/plugins/vite-zone-guard.d.ts.map +1 -1
  299. package/dist/plugins/vite-zone-guard.js +8 -2
  300. package/dist/plugins/vite-zone-guard.js.map +1 -1
  301. package/dist/ppr.d.ts +73 -0
  302. package/dist/ppr.d.ts.map +1 -0
  303. package/dist/ppr.js +76 -0
  304. package/dist/ppr.js.map +1 -0
  305. package/dist/query.d.ts +8 -13
  306. package/dist/query.d.ts.map +1 -1
  307. package/dist/query.js +112 -18
  308. package/dist/query.js.map +1 -1
  309. package/dist/render-seam.d.ts +30 -0
  310. package/dist/render-seam.d.ts.map +1 -1
  311. package/dist/render-seam.js +5 -0
  312. package/dist/render-seam.js.map +1 -1
  313. package/dist/route-types.d.ts.map +1 -1
  314. package/dist/route-types.js +2 -2
  315. package/dist/route-types.js.map +1 -1
  316. package/dist/router.d.ts +72 -4
  317. package/dist/router.d.ts.map +1 -1
  318. package/dist/router.js +374 -99
  319. package/dist/router.js.map +1 -1
  320. package/dist/server-components/build.d.ts.map +1 -1
  321. package/dist/server-components/build.js +3 -3
  322. package/dist/server-components/build.js.map +1 -1
  323. package/dist/server-components/client.d.ts +23 -1
  324. package/dist/server-components/client.d.ts.map +1 -1
  325. package/dist/server-components/client.js +92 -5
  326. package/dist/server-components/client.js.map +1 -1
  327. package/dist/server-components/hole.d.ts +8 -0
  328. package/dist/server-components/hole.d.ts.map +1 -0
  329. package/dist/server-components/hole.js +20 -0
  330. package/dist/server-components/hole.js.map +1 -0
  331. package/dist/server-components/html-policy.d.ts.map +1 -1
  332. package/dist/server-components/html-policy.js +62 -4
  333. package/dist/server-components/html-policy.js.map +1 -1
  334. package/dist/server-components/html-scan.d.ts +17 -0
  335. package/dist/server-components/html-scan.d.ts.map +1 -0
  336. package/dist/server-components/html-scan.js +180 -0
  337. package/dist/server-components/html-scan.js.map +1 -0
  338. package/dist/server-components/html.d.ts.map +1 -1
  339. package/dist/server-components/html.js +19 -4
  340. package/dist/server-components/html.js.map +1 -1
  341. package/dist/server-components/router-integration.d.ts +4 -1
  342. package/dist/server-components/router-integration.d.ts.map +1 -1
  343. package/dist/server-components/router-integration.js +23 -2
  344. package/dist/server-components/router-integration.js.map +1 -1
  345. package/dist/server-components/runtime.d.ts +6 -1
  346. package/dist/server-components/runtime.d.ts.map +1 -1
  347. package/dist/server-components/runtime.js +86 -28
  348. package/dist/server-components/runtime.js.map +1 -1
  349. package/dist/server-components/state.d.ts +3 -1
  350. package/dist/server-components/state.d.ts.map +1 -1
  351. package/dist/server-components/state.js +50 -38
  352. package/dist/server-components/state.js.map +1 -1
  353. package/dist/server-components/types.d.ts +9 -0
  354. package/dist/server-components/types.d.ts.map +1 -1
  355. package/dist/vite.d.ts +5 -1
  356. package/dist/vite.d.ts.map +1 -1
  357. package/dist/vite.js +58 -4
  358. package/dist/vite.js.map +1 -1
  359. package/package.json +48 -8
  360. package/src/boundary.ts +77 -5
  361. package/src/build-plan.ts +3 -0
  362. package/src/build-vite.ts +2 -0
  363. package/src/build.ts +19 -1
  364. package/src/client.ts +147 -10
  365. package/src/csp.ts +16 -2
  366. package/src/deferred.ts +11 -5
  367. package/src/dev-feed.ts +11 -2
  368. package/src/dev-session.ts +22 -11
  369. package/src/dev.ts +29 -8
  370. package/src/diagnostic-catalog.ts +53 -0
  371. package/src/diagnostic.ts +1 -0
  372. package/src/fn.ts +24 -7
  373. package/src/fs.ts +22 -8
  374. package/src/identity.ts +61 -0
  375. package/src/index.ts +19 -0
  376. package/src/internal/cdn/cloudflare.ts +2 -1
  377. package/src/internal/certified-components.ts +1 -1
  378. package/src/internal/codegen.ts +87 -8
  379. package/src/internal/dev-websocket.ts +230 -0
  380. package/src/internal/hole-runtime.ts +286 -0
  381. package/src/internal/identity-epoch.ts +235 -0
  382. package/src/internal/invalidation-contract.ts +35 -0
  383. package/src/internal/invalidation.ts +213 -0
  384. package/src/internal/island-bindings.ts +191 -0
  385. package/src/internal/island-list.ts +92 -0
  386. package/src/internal/island-mount.ts +68 -0
  387. package/src/internal/island-owner.ts +20 -0
  388. package/src/internal/island-resource.ts +76 -0
  389. package/src/internal/island-signals.ts +249 -0
  390. package/src/internal/keep-alive-table.ts +72 -0
  391. package/src/internal/live-contract.ts +106 -0
  392. package/src/internal/live-queries.ts +60 -0
  393. package/src/internal/page-events.ts +27 -0
  394. package/src/internal/page-execution.ts +579 -40
  395. package/src/internal/ppr/admission.ts +300 -0
  396. package/src/internal/ppr/canary.ts +122 -0
  397. package/src/internal/ppr/compile-shell.ts +379 -0
  398. package/src/internal/ppr/contract.ts +265 -0
  399. package/src/internal/ppr/frames.ts +170 -0
  400. package/src/internal/ppr/integration.ts +137 -0
  401. package/src/internal/ppr/options.ts +221 -0
  402. package/src/internal/ppr/route-rules.ts +77 -0
  403. package/src/internal/ppr/runtime.ts +36 -0
  404. package/src/internal/ppr/server.ts +1764 -0
  405. package/src/internal/ppr/shell-store.ts +139 -0
  406. package/src/internal/prerendered-pages.ts +7 -4
  407. package/src/internal/query-digest.ts +34 -0
  408. package/src/internal/query-runtime.ts +14 -1
  409. package/src/internal/query-seam.ts +48 -0
  410. package/src/internal/render-document.ts +149 -22
  411. package/src/internal/request-abort.ts +26 -0
  412. package/src/internal/server-boundary.ts +10 -1
  413. package/src/internal/share-data.ts +136 -0
  414. package/src/internal/store-signature.ts +149 -0
  415. package/src/internal/vite-plugin-compat.ts +2 -2
  416. package/src/internal/web-app.ts +121 -3
  417. package/src/islands-swap.ts +283 -0
  418. package/src/islands.ts +169 -72
  419. package/src/isr.ts +348 -106
  420. package/src/keep-alive.ts +1315 -0
  421. package/src/live/bus.ts +77 -0
  422. package/src/live/core.ts +52 -0
  423. package/src/live/history.ts +109 -0
  424. package/src/live/hub-protocol.ts +133 -0
  425. package/src/live/hub.ts +615 -0
  426. package/src/live/keyring.ts +379 -0
  427. package/src/live/read-capped.ts +36 -0
  428. package/src/live/revocations.ts +155 -0
  429. package/src/live/settings.ts +46 -0
  430. package/src/live-client.ts +973 -0
  431. package/src/live-hub.ts +658 -0
  432. package/src/live-redis.ts +259 -0
  433. package/src/live.ts +1096 -0
  434. package/src/manifest.ts +274 -6
  435. package/src/navigation.ts +14 -2
  436. package/src/plugins/vite-zone-guard.ts +15 -2
  437. package/src/ppr.ts +153 -0
  438. package/src/query.ts +147 -20
  439. package/src/render-seam.ts +35 -0
  440. package/src/route-types.ts +3 -1
  441. package/src/router.ts +494 -101
  442. package/src/server-components/build.ts +9 -3
  443. package/src/server-components/client.ts +89 -5
  444. package/src/server-components/hole.ts +23 -0
  445. package/src/server-components/html-policy.ts +63 -4
  446. package/src/server-components/html-scan.ts +159 -0
  447. package/src/server-components/html.ts +18 -4
  448. package/src/server-components/router-integration.ts +21 -2
  449. package/src/server-components/runtime.ts +112 -30
  450. package/src/server-components/state.ts +66 -44
  451. package/src/server-components/types.ts +16 -0
  452. package/src/vite.ts +75 -6
  453. package/dist/nano.d.ts +0 -125
  454. package/dist/nano.d.ts.map +0 -1
  455. package/dist/nano.js +0 -190
  456. package/dist/nano.js.map +0 -1
  457. package/src/nano.ts +0 -246
@@ -0,0 +1,230 @@
1
+ /**
2
+ * The app's WebSocket routes under the dev servers. Neither dev server is the app's own listener, so
3
+ * neither gets the upgrades `app.listen()` and the adapters wire: each hands the handshake to the
4
+ * app's `resolveWebSocketUpgrade` seam - the route's `upgrade()` guard and Origin check included - and
5
+ * wires an accepted socket with the runtime's own `attach`, so a dev socket behaves as a served one.
6
+ */
7
+ import type { WebSocketUpgradeOutcome } from "@nifrajs/core"
8
+
9
+ type Upgrade = Extract<WebSocketUpgradeOutcome, { readonly kind: "upgrade" }>
10
+
11
+ /** A nifra app's handshake seam; a plain `{ fetch }` handler has none and takes no upgrades. */
12
+ export interface UpgradableApp {
13
+ resolveWebSocketUpgrade?(
14
+ request: Request,
15
+ ): WebSocketUpgradeOutcome | Promise<WebSocketUpgradeOutcome>
16
+ }
17
+
18
+ export function isWebSocketUpgrade(request: Request): boolean {
19
+ return request.headers.get("upgrade")?.toLowerCase() === "websocket"
20
+ }
21
+
22
+ type Listener = (event: never) => void
23
+
24
+ /** A standard-socket view `attach` wires its listeners to; the dev server feeds it events. */
25
+ function socketView(native: {
26
+ send(data: string | ArrayBufferView | ArrayBuffer): unknown
27
+ close(code?: number, reason?: string): void
28
+ readonly readyState: number
29
+ }) {
30
+ const listeners = new Map<string, Listener[]>()
31
+ return {
32
+ view: {
33
+ send: (data: string | ArrayBufferView | ArrayBuffer): void => {
34
+ native.send(data)
35
+ },
36
+ close: (code?: number, reason?: string) => native.close(code, reason),
37
+ get readyState() {
38
+ return native.readyState
39
+ },
40
+ addEventListener(type: string, listener: Listener): void {
41
+ listeners.set(type, [...(listeners.get(type) ?? []), listener])
42
+ },
43
+ },
44
+ emit(type: string, event: unknown): void {
45
+ for (const listener of listeners.get(type) ?? []) Reflect.apply(listener, undefined, [event])
46
+ },
47
+ }
48
+ }
49
+
50
+ function attachView(view: ReturnType<typeof socketView>["view"], outcome: Upgrade): void {
51
+ outcome.attach(view, outcome.handler, outcome.data, {
52
+ openNow: true,
53
+ pubsub: outcome.pubsub,
54
+ maxPayloadBytes: outcome.maxPayloadBytes,
55
+ })
56
+ }
57
+
58
+ // --- Bun ----------------------------------------------------------------------------------------
59
+
60
+ /** What a dev socket carries from its handshake to `open`, and the view its events go to after. */
61
+ export interface BunDevSocketData {
62
+ readonly outcome: Upgrade
63
+ emit?: (type: string, event: unknown) => void
64
+ }
65
+
66
+ /** The part of Bun's `ServerWebSocket` used here. */
67
+ interface BunDevSocket {
68
+ send(data: string | ArrayBufferView | ArrayBuffer): unknown
69
+ close(code?: number, reason?: string): void
70
+ readonly readyState: number
71
+ readonly data: BunDevSocketData
72
+ }
73
+
74
+ export interface BunUpgradeServer {
75
+ upgrade(request: Request, options: { data: BunDevSocketData }): boolean
76
+ }
77
+
78
+ /** `Bun.serve`'s `websocket` option for a dev server whose upgrades go through {@link upgradeOnBun}. */
79
+ export const bunDevWebSocket = {
80
+ open(socket: BunDevSocket): void {
81
+ const { view, emit } = socketView(socket)
82
+ socket.data.emit = emit
83
+ attachView(view, socket.data.outcome)
84
+ },
85
+ message(socket: BunDevSocket, message: string | Uint8Array): void {
86
+ socket.data.emit?.("message", { data: message })
87
+ },
88
+ close(socket: BunDevSocket, code: number, reason: string): void {
89
+ socket.data.emit?.("close", { code, reason })
90
+ },
91
+ }
92
+
93
+ /**
94
+ * Run the handshake through the app. `undefined` when the socket was upgraded; a `Response` for a
95
+ * refusal; `"pass"` when no WebSocket route takes the path, for the dev server to answer as HTTP.
96
+ */
97
+ export async function upgradeOnBun(
98
+ app: UpgradableApp,
99
+ request: Request,
100
+ server: BunUpgradeServer,
101
+ ): Promise<Response | "pass" | undefined> {
102
+ if (app.resolveWebSocketUpgrade === undefined) return "pass"
103
+ const outcome = await app.resolveWebSocketUpgrade(request)
104
+ if (outcome.kind === "pass") return "pass"
105
+ if (outcome.kind === "reject") return outcome.response
106
+ return server.upgrade(request, { data: { outcome } })
107
+ ? undefined
108
+ : new Response("upgrade_required", { status: 426 })
109
+ }
110
+
111
+ // --- Node (the Vite dev server) -------------------------------------------------------------------
112
+
113
+ /** The part of a Node socket an upgrade writes to. */
114
+ export interface UpgradeSocket {
115
+ write(chunk: string): unknown
116
+ destroy(): void
117
+ on(event: "error", listener: () => void): unknown
118
+ }
119
+
120
+ /** The part of the `ws` package's `WebSocketServer` used here (no `@types/ws` dependency). */
121
+ export interface WsServer {
122
+ handleUpgrade(
123
+ request: unknown,
124
+ socket: UpgradeSocket,
125
+ head: unknown,
126
+ done: (ws: WsSocket) => void,
127
+ ): void
128
+ }
129
+
130
+ /** The part of a `ws` socket used here. Its own events, not its `addEventListener`: Bun's `ws`
131
+ * shim hands text frames to that as bytes. The shim also gives a close reason as a string where
132
+ * `ws` gives bytes. */
133
+ interface WsSocket {
134
+ send(data: string | ArrayBufferView | ArrayBuffer): void
135
+ close(code?: number, reason?: string): void
136
+ readonly readyState: number
137
+ on(event: "message", listener: (data: string | Uint8Array, isBinary: boolean) => void): void
138
+ on(event: "close", listener: (code: number, reason: string | Uint8Array) => void): void
139
+ on(event: "error", listener: (error: Error) => void): void
140
+ }
141
+
142
+ const TEXT = new TextDecoder()
143
+ const textOf = (data: string | Uint8Array): string =>
144
+ typeof data === "string" ? data : TEXT.decode(data)
145
+
146
+ /** A non-literal specifier: `ws` is an optional peer, typed structurally above. */
147
+ const WS_MODULE = "ws"
148
+
149
+ /** A noServer `ws` server from `specifier`; undefined when it cannot be loaded or has none. */
150
+ export async function wsServerFrom(specifier: string): Promise<WsServer | undefined> {
151
+ try {
152
+ const mod: {
153
+ WebSocketServer?: new (options: { noServer: true }) => WsServer
154
+ default?: { WebSocketServer?: new (options: { noServer: true }) => WsServer }
155
+ } = await import(specifier)
156
+ const Server = mod.WebSocketServer ?? mod.default?.WebSocketServer
157
+ return Server === undefined ? undefined : new Server({ noServer: true })
158
+ } catch {
159
+ return undefined
160
+ }
161
+ }
162
+
163
+ let wsServer: Promise<WsServer | undefined> | undefined
164
+
165
+ /** One server for every dev upgrade. */
166
+ const loadWsServer = (): Promise<WsServer | undefined> => {
167
+ wsServer ??= wsServerFrom(WS_MODULE)
168
+ return wsServer
169
+ }
170
+
171
+ /**
172
+ * Answer a Node `upgrade` event through the app. Resolves `false` when no WebSocket route takes the
173
+ * path, leaving the socket to the other listeners (Vite's HMR among them).
174
+ */
175
+ export async function upgradeOnNode(
176
+ app: UpgradableApp,
177
+ request: Request,
178
+ nodeRequest: unknown,
179
+ socket: UpgradeSocket,
180
+ head: unknown,
181
+ wsServerOf: () => Promise<WsServer | undefined> = loadWsServer,
182
+ ): Promise<boolean> {
183
+ if (app.resolveWebSocketUpgrade === undefined) return false
184
+ // A peer can reset the socket while the guard is awaited; with no listener that error ends the process.
185
+ socket.on("error", () => {})
186
+ let outcome: WebSocketUpgradeOutcome
187
+ try {
188
+ outcome = await app.resolveWebSocketUpgrade(request)
189
+ } catch {
190
+ await writeRefusal(socket, new Response("internal_error", { status: 500 }))
191
+ return true
192
+ }
193
+ if (outcome.kind === "pass") return false
194
+ if (outcome.kind === "reject") {
195
+ await writeRefusal(socket, outcome.response)
196
+ return true
197
+ }
198
+ const wss = await wsServerOf()
199
+ if (wss === undefined) {
200
+ await writeRefusal(
201
+ socket,
202
+ new Response("[nifra] install `ws` to serve app.ws() routes under the Vite dev server\n", {
203
+ status: 501,
204
+ }),
205
+ )
206
+ return true
207
+ }
208
+ const upgrade = outcome
209
+ wss.handleUpgrade(nodeRequest, socket, head, (ws) => {
210
+ const { view, emit } = socketView(ws)
211
+ ws.on("message", (data, isBinary) => emit("message", { data: isBinary ? data : textOf(data) }))
212
+ // Bun's `node:http` stops listening when a socket's close listener throws.
213
+ ws.on("close", (code, reason) => emit("close", { code, reason: textOf(reason) }))
214
+ ws.on("error", (error) => emit("error", error))
215
+ attachView(view, upgrade)
216
+ })
217
+ return true
218
+ }
219
+
220
+ /** A refusal written to the raw socket, then closed. */
221
+ async function writeRefusal(socket: UpgradeSocket, response: Response): Promise<void> {
222
+ const body = await response.text()
223
+ let head = `HTTP/1.1 ${response.status} ${response.statusText || "Refused"}\r\n`
224
+ response.headers.forEach((value, key) => {
225
+ if (key !== "content-length" && key !== "connection") head += `${key}: ${value}\r\n`
226
+ })
227
+ head += `content-length: ${new TextEncoder().encode(body).byteLength}\r\nconnection: close\r\n\r\n`
228
+ socket.write(head + body)
229
+ socket.destroy()
230
+ }
@@ -0,0 +1,286 @@
1
+ /**
2
+ * The browser side of a partially prerendered page's holes, shared by every adapter's `Hole`: the
3
+ * host's first fill from the document, and each refill a data request brings when the page on screen
4
+ * reloads. An adapter supplies only how an interactive hole's own root hydrates, updates and goes. A
5
+ * hole that shows a server component mounts the client islands inside it through the component
6
+ * client the app registered with `holeComponents` (`@nifrajs/web/server-components/client`).
7
+ */
8
+ import type { ComponentClientAdapter, ComponentClientRoot } from "../server-components/client.ts"
9
+ import type { ComponentRecord } from "../server-components/types.ts"
10
+ import { onPageRelease, PAGE_HIDE_EVENT, PAGE_SHOW_EVENT } from "./page-events.ts"
11
+
12
+ /** One hole's fill, as the PPR runtime keeps it. */
13
+ export interface HoleFill {
14
+ readonly props?: unknown
15
+ readonly error: boolean
16
+ /** The record of the server component the hole shows, without its HTML. */
17
+ readonly component?: unknown
18
+ /** That component's HTML. */
19
+ readonly html?: string
20
+ }
21
+
22
+ /** The shell's inline runtime (internal/ppr/runtime.ts). */
23
+ interface PprRuntime {
24
+ get(name: string): HoleFill | undefined
25
+ subscribe(name: string, callback: (fill: HoleFill) => void): () => void
26
+ put(name: string, frame: unknown, host: Element): void
27
+ /** Stop filling holes from the document, and cancel the ones still loading on the server. */
28
+ leave?(): void
29
+ }
30
+
31
+ /** The error slot a failed, timed-out or refused hole shows. Constant: no thrown message reaches it. */
32
+ export const HOLE_ERROR_HTML = '<p data-nifra-hole-error="">This section is unavailable.</p>'
33
+
34
+ const ERROR_FRAME = { v: 1, error: true, html: HOLE_ERROR_HTML } as const
35
+
36
+ /** The runtime the shell's inline script defines, when this document has one. */
37
+ const pprRuntime = (): PprRuntime | undefined => {
38
+ const runtime: unknown = Reflect.get(globalThis, "__nifraPpr")
39
+ return typeof runtime === "function" &&
40
+ "get" in runtime &&
41
+ "subscribe" in runtime &&
42
+ "put" in runtime
43
+ ? // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the shell's constant inline runtime defines exactly this shape.
44
+ (runtime as PprRuntime)
45
+ : undefined
46
+ }
47
+
48
+ /** How the islands inside a hole that shows a server component mount: the app's component client,
49
+ * and the protocol check the component's record passes first. */
50
+ export interface HoleComponents {
51
+ readonly client: ComponentClientAdapter
52
+ record(value: unknown): ComponentRecord
53
+ }
54
+
55
+ let components: HoleComponents | undefined
56
+
57
+ /** What `holeComponents` registers; `undefined` forgets it. */
58
+ export function setHoleComponents(next: HoleComponents | undefined): void {
59
+ components = next
60
+ }
61
+
62
+ /** An interactive hole's own root, as its adapter made it. */
63
+ export interface HoleRoot {
64
+ /** Render new props into the root, keeping its state; `false` when it has to be made again. */
65
+ update(props: unknown): boolean
66
+ /** `leaving`: the page is unmounting the host, so the adapter may defer the work. A promise means
67
+ * the host is not free until it settles. */
68
+ dispose(leaving: boolean): void | Promise<void>
69
+ /** The kept page holding the host is hidden (`true`) or shown again: a root whose framework can
70
+ * pause what it runs keeps its state either way. */
71
+ hide?(hidden: boolean): void
72
+ }
73
+
74
+ export interface HoleHost {
75
+ /** Apply a data request's fill once it arrives, unless a newer one is given first. Without the
76
+ * shell's runtime, only a server component's fill or the error slot can be put. A first fill the
77
+ * host was rendered with (`initialHtml`) hydrates its islands over that HTML. */
78
+ refill(fill: unknown): void
79
+ dispose(): void
80
+ /**
81
+ * For an adapter whose hidden page cleans its effects up (React's `Activity`): while a kept page
82
+ * hides the host, keep it, root and all, for the page's return, and dispose it only if the page is
83
+ * dropped. `false` when nothing hides the host, and disposing is the adapter's.
84
+ */
85
+ park(): boolean
86
+ /** The parked host is the adapter's again. */
87
+ unpark(): void
88
+ }
89
+
90
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
91
+ typeof value === "object" && value !== null
92
+
93
+ /** A fill the server deferred: the NDJSON stream settles its promise. */
94
+ const settledFill = (fill: unknown): Promise<unknown> =>
95
+ isRecord(fill) && fill.__nifra_deferred === true && fill.promise instanceof Promise
96
+ ? fill.promise
97
+ : Promise.resolve(fill)
98
+
99
+ /**
100
+ * What a host renders with: a blocking render's settled fill puts the server's HTML in it, which a
101
+ * crawler reads with no script; any other state leaves it empty. A host reads it once, so no later
102
+ * render changes what the runtime put there.
103
+ */
104
+ export const initialHtml = (fill: unknown): string =>
105
+ isRecord(fill) && fill.__nifra_deferred !== true && typeof fill.html === "string" ? fill.html : ""
106
+
107
+ /** The record of the server component a fill shows, its HTML put back, not yet checked. */
108
+ const recordOf = (fill: Record<string, unknown>): Record<string, unknown> | undefined =>
109
+ isRecord(fill.component) && typeof fill.html === "string"
110
+ ? { ...fill.component, html: fill.html }
111
+ : undefined
112
+
113
+ /** The error slot, built without HTML parsing, for a document with no runtime to put it. */
114
+ const errorSlot = (host: Element): void => {
115
+ const slot = host.ownerDocument.createElement("p")
116
+ slot.setAttribute("data-nifra-hole-error", "")
117
+ slot.textContent = "This section is unavailable."
118
+ host.replaceChildren(slot)
119
+ host.setAttribute("data-nifra-hole-error", "")
120
+ }
121
+
122
+ /**
123
+ * The islands of the server component in `host`: hydrated over the server's HTML, or with `fresh`,
124
+ * put into the host from the record. `failed` runs when they cannot mount, as when the record fails
125
+ * the protocol check.
126
+ */
127
+ function componentRoot(
128
+ host: Element,
129
+ unchecked: Record<string, unknown>,
130
+ fresh: boolean,
131
+ failed: () => void,
132
+ ): HoleRoot {
133
+ const registered = components
134
+ let current = true
135
+ const valid = (): boolean => current
136
+ const mounted: Promise<ComponentClientRoot | undefined> = (async () => {
137
+ if (registered === undefined) {
138
+ throw new Error(
139
+ "[nifra/web] ppr: a hole shows a server component, but no holeComponents client",
140
+ )
141
+ }
142
+ const { client } = registered
143
+ const record = registered.record(unchecked)
144
+ const mount = fresh ? client.create : client.hydrate
145
+ if (mount === undefined) throw new Error("[nifra/web] ppr: the component client cannot create")
146
+ const prepared = await client.prepare(record, valid, host, { records: [record] })
147
+ if (!current) return undefined
148
+ const root = await mount.call(client, host, record, prepared, valid)
149
+ if (!current) {
150
+ await root.dispose()
151
+ return undefined
152
+ }
153
+ host.setAttribute("aria-busy", "false")
154
+ return root
155
+ })().catch((error: unknown) => {
156
+ if (current) {
157
+ console.error("[nifra/web] ppr: a server component hole's islands did not mount", error)
158
+ failed()
159
+ }
160
+ return undefined
161
+ })
162
+ return {
163
+ update: () => false,
164
+ dispose() {
165
+ current = false
166
+ return mounted.then((root) => root?.dispose())
167
+ },
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Drive the host of hole `name`. `hydrate` makes an interactive hole's root over the server HTML in
173
+ * `host`; a hole without one only ever shows server HTML, or a server component's.
174
+ */
175
+ export function holeHost(
176
+ name: string,
177
+ host: Element,
178
+ hydrate: ((host: Element, props: unknown) => HoleRoot) | undefined,
179
+ ): HoleHost {
180
+ const runtime = pprRuntime()
181
+ let root: HoleRoot | undefined
182
+ let disposed = false
183
+ let latest = 0
184
+ // The kept page hiding the host, and what disposes the host if that page is dropped.
185
+ let hiddenBy: Element | undefined
186
+ let release: (() => void) | undefined
187
+ const put = (frame: Record<string, unknown>): void => {
188
+ if (runtime !== undefined) runtime.put(name, frame, host)
189
+ else errorSlot(host)
190
+ host.setAttribute("aria-busy", "false")
191
+ }
192
+ const showError = (): void => {
193
+ root = undefined
194
+ put(ERROR_FRAME)
195
+ }
196
+ const start = (fill: HoleFill): void => {
197
+ if (disposed || root !== undefined || fill.error) return
198
+ const record = recordOf({ component: fill.component, html: fill.html })
199
+ if (record !== undefined) root = componentRoot(host, record, false, showError)
200
+ else if (hydrate !== undefined) root = hydrate(host, fill.props)
201
+ }
202
+ const now = runtime?.get(name)
203
+ const unsubscribe =
204
+ now === undefined && (hydrate !== undefined || components !== undefined)
205
+ ? runtime?.subscribe(name, start)
206
+ : undefined
207
+ if (now !== undefined) start(now)
208
+ // A `keepAlive` page hidden behind another has been left: the document's holes stop filling and
209
+ // the ones still loading are cancelled, and a refill already on its way is dropped. The page's
210
+ // reload when it is shown again refills the host.
211
+ const hidden = (event: Event): void => {
212
+ if (!(event.target instanceof Element) || !event.target.contains(host)) return
213
+ hiddenBy = event.target
214
+ latest++
215
+ runtime?.leave?.()
216
+ root?.hide?.(true)
217
+ }
218
+ const shown = (event: Event): void => {
219
+ if (event.target !== hiddenBy) return
220
+ hiddenBy = undefined
221
+ root?.hide?.(false)
222
+ }
223
+ host.ownerDocument.addEventListener(PAGE_HIDE_EVENT, hidden)
224
+ host.ownerDocument.addEventListener(PAGE_SHOW_EVENT, shown)
225
+ const apply = (frame: unknown, mine: number, inPlace: boolean): void => {
226
+ if (!isRecord(frame)) return
227
+ const failed = frame.error === true
228
+ const record = failed ? undefined : recordOf(frame)
229
+ if (!failed && record === undefined && root?.update(frame.props) === true) {
230
+ host.setAttribute("aria-busy", "false")
231
+ return
232
+ }
233
+ // An error shows the error slot, never content the action that reloaded it may have changed.
234
+ const gone = root?.dispose(false)
235
+ root = undefined
236
+ const fill = (): void => {
237
+ if (disposed || mine !== latest) return
238
+ host.removeAttribute("data-nifra-hole-error")
239
+ if (record !== undefined) {
240
+ host.setAttribute("data-nifra-filled", "")
241
+ root = componentRoot(host, record, !inPlace, showError)
242
+ return
243
+ }
244
+ put(frame)
245
+ if (!failed && hydrate !== undefined) root = hydrate(host, frame.props)
246
+ }
247
+ if (gone instanceof Promise) gone.then(fill, fill)
248
+ else fill()
249
+ }
250
+ const dispose = (): void => {
251
+ disposed = true
252
+ release?.()
253
+ release = undefined
254
+ host.ownerDocument.removeEventListener(PAGE_HIDE_EVENT, hidden)
255
+ host.ownerDocument.removeEventListener(PAGE_SHOW_EVENT, shown)
256
+ unsubscribe?.()
257
+ Promise.resolve(root?.dispose(true)).catch(() => {})
258
+ root = undefined
259
+ }
260
+ return {
261
+ refill(fill) {
262
+ // A blocking render's settled fill is the server HTML the host already shows.
263
+ const inPlace = latest === 0 && root === undefined && initialHtml(fill) !== ""
264
+ const mine = ++latest
265
+ host.setAttribute("aria-busy", "true")
266
+ settledFill(fill).then(
267
+ (frame) => {
268
+ if (!disposed && mine === latest) apply(frame, mine, inPlace)
269
+ },
270
+ () => {
271
+ if (!disposed && mine === latest) apply(ERROR_FRAME, mine, false)
272
+ },
273
+ )
274
+ },
275
+ dispose,
276
+ park() {
277
+ if (disposed || hiddenBy === undefined) return false
278
+ release ??= onPageRelease(hiddenBy, dispose)
279
+ return true
280
+ },
281
+ unpark() {
282
+ release?.()
283
+ release = undefined
284
+ },
285
+ }
286
+ }