@ogygia/core 0.0.0 → 0.11.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 (548) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -1
  3. package/ambient.d.ts +194 -0
  4. package/dist/ClientBindingStub.svelte +6 -0
  5. package/dist/ClientBindingStub.svelte.d.ts +26 -0
  6. package/dist/KeepHost.svelte +19 -0
  7. package/dist/KeepHost.svelte.d.ts +10 -0
  8. package/dist/LakeBoundary.svelte +16 -0
  9. package/dist/LakeBoundary.svelte.d.ts +11 -0
  10. package/dist/LateIsland.svelte +22 -0
  11. package/dist/LateIsland.svelte.d.ts +15 -0
  12. package/dist/LateIslandAwait.svelte +17 -0
  13. package/dist/LateIslandAwait.svelte.d.ts +15 -0
  14. package/dist/LiveHost.svelte +19 -0
  15. package/dist/LiveHost.svelte.d.ts +10 -0
  16. package/dist/NestedProvider.svelte +13 -0
  17. package/dist/NestedProvider.svelte.d.ts +8 -0
  18. package/dist/OgygiaBoundary.svelte +5 -0
  19. package/dist/OgygiaBoundary.svelte.d.ts +17 -0
  20. package/dist/PromiseRegion.svelte +40 -0
  21. package/dist/PromiseRegion.svelte.d.ts +15 -0
  22. package/dist/PromiseRegionAwait.svelte +27 -0
  23. package/dist/PromiseRegionAwait.svelte.d.ts +15 -0
  24. package/dist/Provide.svelte +27 -0
  25. package/dist/Provide.svelte.d.ts +8 -0
  26. package/dist/RawHtml.svelte +9 -0
  27. package/dist/RawHtml.svelte.d.ts +6 -0
  28. package/dist/Region.svelte +1173 -0
  29. package/dist/Region.svelte.d.ts +68 -0
  30. package/dist/SlotBoundary.svelte +13 -0
  31. package/dist/SlotBoundary.svelte.d.ts +7 -0
  32. package/dist/app.d.ts +4 -0
  33. package/dist/app.js +3 -0
  34. package/dist/boundary.d.ts +35 -0
  35. package/dist/boundary.js +127 -0
  36. package/dist/build-cache.d.ts +13 -0
  37. package/dist/build-cache.js +77 -0
  38. package/dist/compiler/blank-styles.d.ts +1 -0
  39. package/dist/compiler/blank-styles.js +63 -0
  40. package/dist/compiler/bom.d.ts +2 -0
  41. package/dist/compiler/bom.js +15 -0
  42. package/dist/compiler/browser-full.d.ts +8 -0
  43. package/dist/compiler/browser-full.js +8 -0
  44. package/dist/compiler/browser.d.ts +11 -0
  45. package/dist/compiler/browser.js +11 -0
  46. package/dist/compiler/content/transportables.d.ts +26 -0
  47. package/dist/compiler/content/transportables.js +219 -0
  48. package/dist/compiler/ctx.d.ts +180 -0
  49. package/dist/compiler/ctx.js +183 -0
  50. package/dist/compiler/debarrel/barrel.d.ts +55 -0
  51. package/dist/compiler/debarrel/barrel.js +248 -0
  52. package/dist/compiler/debarrel/options.d.ts +67 -0
  53. package/dist/compiler/debarrel/options.js +31 -0
  54. package/dist/compiler/debarrel/parse.d.ts +65 -0
  55. package/dist/compiler/debarrel/parse.js +256 -0
  56. package/dist/compiler/debarrel/plugin.d.ts +31 -0
  57. package/dist/compiler/debarrel/plugin.js +190 -0
  58. package/dist/compiler/debarrel/rewrite.d.ts +27 -0
  59. package/dist/compiler/debarrel/rewrite.js +118 -0
  60. package/dist/compiler/debarrel/svelte.d.ts +13 -0
  61. package/dist/compiler/debarrel/svelte.js +56 -0
  62. package/dist/compiler/dev/css-scope.d.ts +16 -0
  63. package/dist/compiler/dev/css-scope.js +38 -0
  64. package/dist/compiler/dev/dev-hmr.d.ts +25 -0
  65. package/dist/compiler/dev/dev-hmr.js +29 -0
  66. package/dist/compiler/dev/hmr.d.ts +34 -0
  67. package/dist/compiler/dev/hmr.js +57 -0
  68. package/dist/compiler/dev/region-bytes.d.ts +34 -0
  69. package/dist/compiler/dev/region-bytes.js +126 -0
  70. package/dist/compiler/driver.d.ts +280 -0
  71. package/dist/compiler/driver.js +1352 -0
  72. package/dist/compiler/flags.d.ts +16 -0
  73. package/dist/compiler/flags.js +101 -0
  74. package/dist/compiler/fouc-css.d.ts +97 -0
  75. package/dist/compiler/fouc-css.js +335 -0
  76. package/dist/compiler/free-vars.d.ts +20 -0
  77. package/dist/compiler/free-vars.js +327 -0
  78. package/dist/compiler/host.d.ts +65 -0
  79. package/dist/compiler/host.js +51 -0
  80. package/dist/compiler/ids.d.ts +84 -0
  81. package/dist/compiler/ids.js +86 -0
  82. package/dist/compiler/index.d.ts +11 -0
  83. package/dist/compiler/index.js +11 -0
  84. package/dist/compiler/kit.d.ts +129 -0
  85. package/dist/compiler/kit.js +563 -0
  86. package/dist/compiler/link/build-output.d.ts +44 -0
  87. package/dist/compiler/link/build-output.js +147 -0
  88. package/dist/compiler/link/caps.d.ts +48 -0
  89. package/dist/compiler/link/caps.js +64 -0
  90. package/dist/compiler/link/context-detect.d.ts +23 -0
  91. package/dist/compiler/link/context-detect.js +137 -0
  92. package/dist/compiler/link/emit-gate.d.ts +11 -0
  93. package/dist/compiler/link/emit-gate.js +40 -0
  94. package/dist/compiler/link/entry-shim.d.ts +4 -0
  95. package/dist/compiler/link/entry-shim.js +29 -0
  96. package/dist/compiler/link/island-deps.d.ts +142 -0
  97. package/dist/compiler/link/island-deps.js +525 -0
  98. package/dist/compiler/link/manifest.d.ts +5 -0
  99. package/dist/compiler/link/manifest.js +9 -0
  100. package/dist/compiler/link/page-keys.d.ts +64 -0
  101. package/dist/compiler/link/page-keys.js +683 -0
  102. package/dist/compiler/link/registry-stub.d.ts +41 -0
  103. package/dist/compiler/link/registry-stub.js +212 -0
  104. package/dist/compiler/link/router-config.d.ts +11 -0
  105. package/dist/compiler/link/router-config.js +15 -0
  106. package/dist/compiler/link/router-css.d.ts +22 -0
  107. package/dist/compiler/link/router-css.js +142 -0
  108. package/dist/compiler/link/runtime-entry.d.ts +71 -0
  109. package/dist/compiler/link/runtime-entry.js +132 -0
  110. package/dist/compiler/link/server-manifest.d.ts +12 -0
  111. package/dist/compiler/link/server-manifest.js +45 -0
  112. package/dist/compiler/link/speculation.d.ts +17 -0
  113. package/dist/compiler/link/speculation.js +36 -0
  114. package/dist/compiler/link/transport.d.ts +31 -0
  115. package/dist/compiler/link/transport.js +67 -0
  116. package/dist/compiler/macros/bake.d.ts +20 -0
  117. package/dist/compiler/macros/bake.js +317 -0
  118. package/dist/compiler/macros/browser.d.ts +11 -0
  119. package/dist/compiler/macros/browser.js +69 -0
  120. package/dist/compiler/macros/dedent.d.ts +12 -0
  121. package/dist/compiler/macros/dedent.js +36 -0
  122. package/dist/compiler/macros/dollar.d.ts +15 -0
  123. package/dist/compiler/macros/dollar.js +387 -0
  124. package/dist/compiler/macros/pipeline.d.ts +31 -0
  125. package/dist/compiler/macros/pipeline.js +106 -0
  126. package/dist/compiler/macros/source.d.ts +6 -0
  127. package/dist/compiler/macros/source.js +127 -0
  128. package/dist/compiler/macros/store.d.ts +17 -0
  129. package/dist/compiler/macros/store.js +244 -0
  130. package/dist/compiler/macros/wire.d.ts +14 -0
  131. package/dist/compiler/macros/wire.js +160 -0
  132. package/dist/compiler/ownership-stamps.d.ts +6 -0
  133. package/dist/compiler/ownership-stamps.js +144 -0
  134. package/dist/compiler/parse/oxc.d.ts +34 -0
  135. package/dist/compiler/parse/oxc.js +59 -0
  136. package/dist/compiler/parse/scan.d.ts +43 -0
  137. package/dist/compiler/parse/scan.js +232 -0
  138. package/dist/compiler/program.d.ts +140 -0
  139. package/dist/compiler/program.js +242 -0
  140. package/dist/compiler/region/emit.d.ts +169 -0
  141. package/dist/compiler/region/emit.js +286 -0
  142. package/dist/compiler/region/identity.d.ts +28 -0
  143. package/dist/compiler/region/identity.js +75 -0
  144. package/dist/compiler/region/ir.d.ts +65 -0
  145. package/dist/compiler/region/ir.js +0 -0
  146. package/dist/compiler/region/transform.d.ts +178 -0
  147. package/dist/compiler/region/transform.js +1933 -0
  148. package/dist/compiler/script-request.d.ts +1 -0
  149. package/dist/compiler/script-request.js +43 -0
  150. package/dist/context-bridge.d.ts +65 -0
  151. package/dist/context-bridge.js +217 -0
  152. package/dist/context-registry.d.ts +40 -0
  153. package/dist/context-registry.js +15 -0
  154. package/dist/context.d.ts +72 -0
  155. package/dist/context.js +158 -0
  156. package/dist/current-region.d.ts +14 -0
  157. package/dist/current-region.js +36 -0
  158. package/dist/document.d.ts +41 -0
  159. package/dist/document.js +125 -0
  160. package/dist/entry-hydrate.d.ts +5 -0
  161. package/dist/entry-hydrate.js +42 -0
  162. package/dist/escape.d.ts +18 -0
  163. package/dist/escape.js +37 -0
  164. package/dist/events/bus.d.ts +33 -0
  165. package/dist/events/bus.js +153 -0
  166. package/dist/events/index.d.ts +4 -0
  167. package/dist/events/index.js +4 -0
  168. package/dist/events/schema.d.ts +389 -0
  169. package/dist/events/schema.js +23 -0
  170. package/dist/events/server-registry.d.ts +7 -0
  171. package/dist/events/server-registry.js +13 -0
  172. package/dist/events/sinks.d.ts +64 -0
  173. package/dist/events/sinks.js +85 -0
  174. package/dist/fn-transport.d.ts +33 -0
  175. package/dist/fn-transport.js +140 -0
  176. package/dist/frame.d.ts +20 -0
  177. package/dist/frame.js +19 -0
  178. package/dist/holes-record.d.ts +6 -0
  179. package/dist/holes-record.js +8 -0
  180. package/dist/hooks.d.ts +43 -0
  181. package/dist/hooks.js +1191 -0
  182. package/dist/hydration-info.d.ts +1 -0
  183. package/dist/hydration-info.js +31 -0
  184. package/dist/index.d.ts +21 -0
  185. package/dist/index.js +17 -0
  186. package/dist/insights/attr-rules.d.ts +15 -0
  187. package/dist/insights/attr-rules.js +27 -0
  188. package/dist/insights/cpu.d.ts +82 -0
  189. package/dist/insights/cpu.js +177 -0
  190. package/dist/insights/fp-drift.d.ts +28 -0
  191. package/dist/insights/fp-drift.js +148 -0
  192. package/dist/insights/hole-segments.d.ts +26 -0
  193. package/dist/insights/hole-segments.js +50 -0
  194. package/dist/insights/html-diff.d.ts +25 -0
  195. package/dist/insights/html-diff.js +157 -0
  196. package/dist/insights/hydration-hazards.d.ts +36 -0
  197. package/dist/insights/hydration-hazards.js +136 -0
  198. package/dist/insights/hydration-status.d.ts +2 -0
  199. package/dist/insights/hydration-status.js +0 -0
  200. package/dist/insights/index.d.ts +14 -0
  201. package/dist/insights/index.js +12 -0
  202. package/dist/insights/page-assets.d.ts +211 -0
  203. package/dist/insights/page-assets.js +608 -0
  204. package/dist/insights/page-insights.d.ts +749 -0
  205. package/dist/insights/page-insights.js +2157 -0
  206. package/dist/insights/session-data.d.ts +108 -0
  207. package/dist/insights/session-data.js +0 -0
  208. package/dist/insights/session-insights.d.ts +121 -0
  209. package/dist/insights/session-insights.js +548 -0
  210. package/dist/insights/switch.d.ts +53 -0
  211. package/dist/insights/switch.js +91 -0
  212. package/dist/insights/testing-version.d.ts +19 -0
  213. package/dist/insights/testing-version.js +21 -0
  214. package/dist/insights/third-party.d.ts +75 -0
  215. package/dist/insights/third-party.js +160 -0
  216. package/dist/internal-instruments.d.ts +13 -0
  217. package/dist/internal-instruments.js +12 -0
  218. package/dist/internal-reconcile.d.ts +3 -0
  219. package/dist/internal-reconcile.js +3 -0
  220. package/dist/internal-register.d.ts +7 -0
  221. package/dist/internal-register.js +7 -0
  222. package/dist/internal-server.d.ts +7 -0
  223. package/dist/internal-server.js +7 -0
  224. package/dist/internal.d.ts +13 -0
  225. package/dist/internal.js +13 -0
  226. package/dist/island-graph.d.ts +36 -0
  227. package/dist/island-graph.js +89 -0
  228. package/dist/keep-fallback-marker.d.ts +7 -0
  229. package/dist/keep-fallback-marker.js +9 -0
  230. package/dist/keep-fallback.d.ts +13 -0
  231. package/dist/keep-fallback.js +39 -0
  232. package/dist/kit-paths.d.ts +2 -0
  233. package/dist/kit-paths.js +39 -0
  234. package/dist/late-region-registry.d.ts +40 -0
  235. package/dist/late-region-registry.js +36 -0
  236. package/dist/live-transport.d.ts +57 -0
  237. package/dist/live-transport.js +145 -0
  238. package/dist/markup.d.ts +4 -0
  239. package/dist/markup.js +4 -0
  240. package/dist/own-package.d.ts +15 -0
  241. package/dist/own-package.js +20 -0
  242. package/dist/page-defer.d.ts +33 -0
  243. package/dist/page-defer.js +35 -0
  244. package/dist/page-facts-script.d.ts +7 -0
  245. package/dist/page-facts-script.js +9 -0
  246. package/dist/page-seed-registry.d.ts +72 -0
  247. package/dist/page-seed-registry.js +31 -0
  248. package/dist/plugin/capability.d.ts +14 -0
  249. package/dist/plugin/capability.js +44 -0
  250. package/dist/plugin/client.d.ts +39 -0
  251. package/dist/plugin/client.js +6 -0
  252. package/dist/plugin/css.d.ts +3 -0
  253. package/dist/plugin/css.js +54 -0
  254. package/dist/plugin/facts.d.ts +42 -0
  255. package/dist/plugin/facts.js +37 -0
  256. package/dist/plugin/hooks.d.ts +33 -0
  257. package/dist/plugin/hooks.js +38 -0
  258. package/dist/plugin/index.d.ts +127 -0
  259. package/dist/plugin/index.js +26 -0
  260. package/dist/plugin/routes.d.ts +32 -0
  261. package/dist/plugin/routes.js +35 -0
  262. package/dist/plugin/server.d.ts +27 -0
  263. package/dist/plugin/server.js +92 -0
  264. package/dist/plugin/standard-schema.d.ts +27 -0
  265. package/dist/plugin/standard-schema.js +0 -0
  266. package/dist/plugin/testing.d.ts +13 -0
  267. package/dist/plugin/testing.js +54 -0
  268. package/dist/plugin/utils.d.ts +13 -0
  269. package/dist/plugin/utils.js +19 -0
  270. package/dist/portable-form.d.ts +22 -0
  271. package/dist/portable-form.js +52 -0
  272. package/dist/preference.d.ts +35 -0
  273. package/dist/preference.js +84 -0
  274. package/dist/preload.d.ts +1 -0
  275. package/dist/preload.js +21 -0
  276. package/dist/ref-watch.d.ts +31 -0
  277. package/dist/ref-watch.js +66 -0
  278. package/dist/ref.d.ts +124 -0
  279. package/dist/ref.js +272 -0
  280. package/dist/region-brand.d.ts +8 -0
  281. package/dist/region-brand.js +10 -0
  282. package/dist/region-core.d.ts +2 -0
  283. package/dist/region-core.js +1 -0
  284. package/dist/region-snippet.d.ts +73 -0
  285. package/dist/region-snippet.js +351 -0
  286. package/dist/region.d.ts +127 -0
  287. package/dist/region.js +162 -0
  288. package/dist/request-event.d.ts +1 -0
  289. package/dist/request-event.js +37 -0
  290. package/dist/router-css.d.ts +39 -0
  291. package/dist/router-css.js +66 -0
  292. package/dist/runtime/app-transport.d.ts +2 -0
  293. package/dist/runtime/app-transport.js +13 -0
  294. package/dist/runtime/beacon.d.ts +271 -0
  295. package/dist/runtime/beacon.js +2190 -0
  296. package/dist/runtime/boot-link.d.ts +1 -0
  297. package/dist/runtime/boot-link.js +44 -0
  298. package/dist/runtime/client-hooks.d.ts +5 -0
  299. package/dist/runtime/client-hooks.js +22 -0
  300. package/dist/runtime/client-plugins.d.ts +8 -0
  301. package/dist/runtime/client-plugins.js +34 -0
  302. package/dist/runtime/concurrency.d.ts +12 -0
  303. package/dist/runtime/concurrency.js +29 -0
  304. package/dist/runtime/connected.d.ts +12 -0
  305. package/dist/runtime/connected.js +16 -0
  306. package/dist/runtime/context.d.ts +1 -0
  307. package/dist/runtime/context.js +19 -0
  308. package/dist/runtime/core.d.ts +15 -0
  309. package/dist/runtime/core.js +1173 -0
  310. package/dist/runtime/drift-watch.d.ts +9 -0
  311. package/dist/runtime/drift-watch.js +60 -0
  312. package/dist/runtime/entry-locations.d.ts +33 -0
  313. package/dist/runtime/entry-locations.js +119 -0
  314. package/dist/runtime/foreign-props.d.ts +8 -0
  315. package/dist/runtime/foreign-props.js +14 -0
  316. package/dist/runtime/form-continuity.d.ts +10 -0
  317. package/dist/runtime/form-continuity.js +107 -0
  318. package/dist/runtime/frame-nav.d.ts +36 -0
  319. package/dist/runtime/frame-nav.js +225 -0
  320. package/dist/runtime/frame-store.d.ts +62 -0
  321. package/dist/runtime/frame-store.js +214 -0
  322. package/dist/runtime/frames.d.ts +1 -0
  323. package/dist/runtime/frames.js +25 -0
  324. package/dist/runtime/full.d.ts +4 -0
  325. package/dist/runtime/full.js +32 -0
  326. package/dist/runtime/hash.d.ts +41 -0
  327. package/dist/runtime/hash.js +79 -0
  328. package/dist/runtime/hole-facts.d.ts +8 -0
  329. package/dist/runtime/hole-facts.js +57 -0
  330. package/dist/runtime/host-classes.d.ts +24 -0
  331. package/dist/runtime/host-classes.js +43 -0
  332. package/dist/runtime/hydrate-core.d.ts +101 -0
  333. package/dist/runtime/hydrate-core.js +605 -0
  334. package/dist/runtime/index.d.ts +2 -0
  335. package/dist/runtime/index.js +1 -0
  336. package/dist/runtime/interaction-replay.d.ts +11 -0
  337. package/dist/runtime/interaction-replay.js +70 -0
  338. package/dist/runtime/interaction.d.ts +33 -0
  339. package/dist/runtime/interaction.js +217 -0
  340. package/dist/runtime/island-graph-preload.d.ts +13 -0
  341. package/dist/runtime/island-graph-preload.js +99 -0
  342. package/dist/runtime/kit-boot.d.ts +29 -0
  343. package/dist/runtime/kit-boot.js +60 -0
  344. package/dist/runtime/kit-page-thread.svelte.d.ts +1 -0
  345. package/dist/runtime/kit-page-thread.svelte.js +116 -0
  346. package/dist/runtime/lake-anchors.d.ts +13 -0
  347. package/dist/runtime/lake-anchors.js +19 -0
  348. package/dist/runtime/lakes.d.ts +15 -0
  349. package/dist/runtime/lakes.js +212 -0
  350. package/dist/runtime/live.d.ts +3 -0
  351. package/dist/runtime/live.js +17 -0
  352. package/dist/runtime/load-scheduler.d.ts +53 -0
  353. package/dist/runtime/load-scheduler.js +317 -0
  354. package/dist/runtime/morph.d.ts +42 -0
  355. package/dist/runtime/morph.js +671 -0
  356. package/dist/runtime/nav-handle.d.ts +25 -0
  357. package/dist/runtime/nav-handle.js +71 -0
  358. package/dist/runtime/nav-hooks.d.ts +2 -0
  359. package/dist/runtime/nav-hooks.js +23 -0
  360. package/dist/runtime/nearest-attr.d.ts +4 -0
  361. package/dist/runtime/nearest-attr.js +16 -0
  362. package/dist/runtime/observe.d.ts +27 -0
  363. package/dist/runtime/observe.js +63 -0
  364. package/dist/runtime/own-cost.d.ts +9 -0
  365. package/dist/runtime/own-cost.js +35 -0
  366. package/dist/runtime/ownership.d.ts +101 -0
  367. package/dist/runtime/ownership.js +115 -0
  368. package/dist/runtime/page-cache.d.ts +23 -0
  369. package/dist/runtime/page-cache.js +74 -0
  370. package/dist/runtime/page-defer.d.ts +14 -0
  371. package/dist/runtime/page-defer.js +103 -0
  372. package/dist/runtime/parse-html.d.ts +40 -0
  373. package/dist/runtime/parse-html.js +81 -0
  374. package/dist/runtime/reconcile.d.ts +41 -0
  375. package/dist/runtime/reconcile.js +194 -0
  376. package/dist/runtime/region-attrs.d.ts +95 -0
  377. package/dist/runtime/region-attrs.js +153 -0
  378. package/dist/runtime/region-endpoint-url.d.ts +76 -0
  379. package/dist/runtime/region-endpoint-url.js +169 -0
  380. package/dist/runtime/region-ids.d.ts +8 -0
  381. package/dist/runtime/region-ids.js +33 -0
  382. package/dist/runtime/remote-seeds.d.ts +2 -0
  383. package/dist/runtime/remote-seeds.js +21 -0
  384. package/dist/runtime/render-blocking.d.ts +2 -0
  385. package/dist/runtime/render-blocking.js +30 -0
  386. package/dist/runtime/restore.d.ts +34 -0
  387. package/dist/runtime/restore.js +390 -0
  388. package/dist/runtime/router-nav.d.ts +63 -0
  389. package/dist/runtime/router-nav.js +740 -0
  390. package/dist/runtime/router.d.ts +201 -0
  391. package/dist/runtime/router.js +593 -0
  392. package/dist/runtime/schedule.d.ts +49 -0
  393. package/dist/runtime/schedule.js +256 -0
  394. package/dist/runtime/seeds.d.ts +50 -0
  395. package/dist/runtime/seeds.js +208 -0
  396. package/dist/runtime/session.d.ts +52 -0
  397. package/dist/runtime/session.js +130 -0
  398. package/dist/runtime/sidecar.d.ts +3 -0
  399. package/dist/runtime/sidecar.js +56 -0
  400. package/dist/runtime/slots.d.ts +183 -0
  401. package/dist/runtime/slots.js +44 -0
  402. package/dist/runtime/spa-cacheable.d.ts +7 -0
  403. package/dist/runtime/spa-cacheable.js +18 -0
  404. package/dist/runtime/speculate-hint.d.ts +1 -0
  405. package/dist/runtime/speculate-hint.js +35 -0
  406. package/dist/runtime/svelte-warnings.d.ts +28 -0
  407. package/dist/runtime/svelte-warnings.js +71 -0
  408. package/dist/runtime/wire-classes.d.ts +3 -0
  409. package/dist/runtime/wire-classes.js +69 -0
  410. package/dist/runtime/wire-format.d.ts +7 -0
  411. package/dist/runtime/wire-format.js +22 -0
  412. package/dist/script.d.ts +134 -0
  413. package/dist/script.js +184 -0
  414. package/dist/seed-refs.d.ts +105 -0
  415. package/dist/seed-refs.js +487 -0
  416. package/dist/server/capability-lifetime.d.ts +28 -0
  417. package/dist/server/capability-lifetime.js +31 -0
  418. package/dist/server/dead-kit-resolves.d.ts +1 -0
  419. package/dist/server/dead-kit-resolves.js +39 -0
  420. package/dist/server/document-assembly.d.ts +30 -0
  421. package/dist/server/document-assembly.js +26 -0
  422. package/dist/server/document-tail.d.ts +113 -0
  423. package/dist/server/document-tail.js +208 -0
  424. package/dist/server/endpoint.d.ts +51 -0
  425. package/dist/server/endpoint.js +90 -0
  426. package/dist/server/entry-location.d.ts +8 -0
  427. package/dist/server/entry-location.js +29 -0
  428. package/dist/server/fingerprint.d.ts +1 -0
  429. package/dist/server/fingerprint.js +25 -0
  430. package/dist/server/head-presence.d.ts +45 -0
  431. package/dist/server/head-presence.js +156 -0
  432. package/dist/server/hmac.d.ts +38 -0
  433. package/dist/server/hmac.js +89 -0
  434. package/dist/server/hole-graph.d.ts +7 -0
  435. package/dist/server/hole-graph.js +71 -0
  436. package/dist/server/hole-urls.d.ts +3 -0
  437. package/dist/server/hole-urls.js +76 -0
  438. package/dist/server/html-scan.d.ts +19 -0
  439. package/dist/server/html-scan.js +60 -0
  440. package/dist/server/kit-context.d.ts +78 -0
  441. package/dist/server/kit-context.js +110 -0
  442. package/dist/server/late-wire.d.ts +22 -0
  443. package/dist/server/late-wire.js +28 -0
  444. package/dist/server/page-facts.d.ts +25 -0
  445. package/dist/server/page-facts.js +138 -0
  446. package/dist/server/page-seed.d.ts +33 -0
  447. package/dist/server/page-seed.js +60 -0
  448. package/dist/server/page-stream.d.ts +55 -0
  449. package/dist/server/page-stream.js +121 -0
  450. package/dist/server/payload.d.ts +6 -0
  451. package/dist/server/payload.js +22 -0
  452. package/dist/server/personal-response.d.ts +6 -0
  453. package/dist/server/personal-response.js +13 -0
  454. package/dist/server/props-wire.d.ts +56 -0
  455. package/dist/server/props-wire.js +178 -0
  456. package/dist/server/rate-limit.d.ts +22 -0
  457. package/dist/server/rate-limit.js +63 -0
  458. package/dist/server/region-css.d.ts +5 -0
  459. package/dist/server/region-css.js +30 -0
  460. package/dist/server/region-endpoint.d.ts +35 -0
  461. package/dist/server/region-endpoint.js +142 -0
  462. package/dist/server/region-props.d.ts +6 -0
  463. package/dist/server/region-props.js +44 -0
  464. package/dist/server/remote-seed-gate.d.ts +50 -0
  465. package/dist/server/remote-seed-gate.js +66 -0
  466. package/dist/server/render-cache.d.ts +26 -0
  467. package/dist/server/render-cache.js +51 -0
  468. package/dist/server/render-page.d.ts +51 -0
  469. package/dist/server/render-page.js +176 -0
  470. package/dist/server/render-region-html.d.ts +2 -0
  471. package/dist/server/render-region-html.js +19 -0
  472. package/dist/server/request-stats.d.ts +174 -0
  473. package/dist/server/request-stats.js +54 -0
  474. package/dist/server/reversible.d.ts +83 -0
  475. package/dist/server/reversible.js +994 -0
  476. package/dist/server/seed-shape.d.ts +22 -0
  477. package/dist/server/seed-shape.js +24 -0
  478. package/dist/server/shared-cache.d.ts +35 -0
  479. package/dist/server/shared-cache.js +104 -0
  480. package/dist/server/signing-key.d.ts +11 -0
  481. package/dist/server/signing-key.js +47 -0
  482. package/dist/server/sized-lru.d.ts +20 -0
  483. package/dist/server/sized-lru.js +68 -0
  484. package/dist/server/split-regions.d.ts +81 -0
  485. package/dist/server/split-regions.js +368 -0
  486. package/dist/server/stream-regions.d.ts +5 -0
  487. package/dist/server/stream-regions.js +36 -0
  488. package/dist/shared-state.d.ts +7 -0
  489. package/dist/shared-state.js +152 -0
  490. package/dist/shims/app-environment.d.ts +4 -0
  491. package/dist/shims/app-environment.js +6 -0
  492. package/dist/shims/app-navigation.d.ts +33 -0
  493. package/dist/shims/app-navigation.js +119 -0
  494. package/dist/shims/app-paths.d.ts +6 -0
  495. package/dist/shims/app-paths.js +18 -0
  496. package/dist/shims/app-state.svelte.d.ts +23 -0
  497. package/dist/shims/app-state.svelte.js +50 -0
  498. package/dist/shims/app-stores.d.ts +21 -0
  499. package/dist/shims/app-stores.js +54 -0
  500. package/dist/shims/kit-bridge.d.ts +29 -0
  501. package/dist/shims/kit-bridge.js +7 -0
  502. package/dist/shims/kit-remote/client-stub.d.ts +27 -0
  503. package/dist/shims/kit-remote/client-stub.js +43 -0
  504. package/dist/shims/kit-remote/paths-internal-stub.d.ts +3 -0
  505. package/dist/shims/kit-remote/paths-internal-stub.js +5 -0
  506. package/dist/shims/kit-remote/remote-cache.d.ts +49 -0
  507. package/dist/shims/kit-remote/remote-cache.js +73 -0
  508. package/dist/shims/kit-remote/state-stub.d.ts +8 -0
  509. package/dist/shims/kit-remote/state-stub.js +8 -0
  510. package/dist/shims/page-store.svelte.d.ts +39 -0
  511. package/dist/shims/page-store.svelte.js +77 -0
  512. package/dist/source.d.ts +29 -0
  513. package/dist/source.js +87 -0
  514. package/dist/store-transport.d.ts +86 -0
  515. package/dist/store-transport.js +229 -0
  516. package/dist/tool-fetches.d.ts +5 -0
  517. package/dist/tool-fetches.js +41 -0
  518. package/dist/transport.d.ts +48 -0
  519. package/dist/transport.js +181 -0
  520. package/dist/unscoped-css.d.ts +26 -0
  521. package/dist/unscoped-css.js +53 -0
  522. package/dist/vite/dev-maps.d.ts +33 -0
  523. package/dist/vite/dev-maps.js +140 -0
  524. package/dist/vite/index.d.ts +19 -0
  525. package/dist/vite/index.js +820 -0
  526. package/dist/vite/kit-dirs.d.ts +7 -0
  527. package/dist/vite/kit-dirs.js +67 -0
  528. package/dist/vite/late-island.d.ts +25 -0
  529. package/dist/vite/late-island.js +33 -0
  530. package/dist/vite/module-map.d.ts +8 -0
  531. package/dist/vite/module-map.js +144 -0
  532. package/dist/vite/options.d.ts +208 -0
  533. package/dist/vite/options.js +74 -0
  534. package/dist/vite/package-files.d.ts +3 -0
  535. package/dist/vite/package-files.js +84 -0
  536. package/dist/vite/paths.d.ts +45 -0
  537. package/dist/vite/paths.js +112 -0
  538. package/dist/vite/plugin-api.d.ts +166 -0
  539. package/dist/vite/plugin-api.js +212 -0
  540. package/dist/vite/profiler-maps.d.ts +23 -0
  541. package/dist/vite/profiler-maps.js +178 -0
  542. package/dist/vite/server-sourcemaps.d.ts +8 -0
  543. package/dist/vite/server-sourcemaps.js +80 -0
  544. package/dist/vite/sourcemaps.d.ts +18 -0
  545. package/dist/vite/sourcemaps.js +80 -0
  546. package/dist/vite/style-preprocess.d.ts +2 -0
  547. package/dist/vite/style-preprocess.js +96 -0
  548. package/package.json +180 -6
@@ -0,0 +1,1933 @@
1
+ import { createHash } from "../host.js";
2
+ import { foucCssVirtualId } from "../fouc-css.js";
3
+ import { collectCaptureInfo } from "../free-vars.js";
4
+ import { parse_module } from "../parse/oxc.js";
5
+ import { regionId, regionIdentity, strategyKey } from "./identity.js";
6
+ import { regionBindingVirtualId } from "../ids.js";
7
+ import { island_entry_source, lake_wrapper_source, make_region_binding, make_wake_island, server_wrapper_source } from "./emit.js";
8
+ import { blank_styles } from "../blank-styles.js";
9
+ import { parse } from "svelte/compiler";
10
+ import MagicString from "magic-string";
11
+ // src/compiler/region/transform.ts
12
+ /** A host the parser still cannot read keeps its region imports as plain imports (no island):
13
+ * said once per file, never skipped in silence. The Svelte compiler reports the error itself. */
14
+ const warned_unparsed = /* @__PURE__ */ new Set();
15
+ function warn_unparsed(id, err) {
16
+ if (warned_unparsed.has(id)) return;
17
+ warned_unparsed.add(id);
18
+ const e = err;
19
+ console.warn(`[ogygia] regions: skipped ${id}: ${e.code ?? (e.message ?? "parse error").split("\n")[0].slice(0, 80)}${e.start?.line ? ` at ${e.start.line}:${(e.start.column ?? 0) + 1}` : ""} — its region imports stay plain imports`);
20
+ }
21
+ const REGEXP_META = /[.*+?^${}()|[\]\\]/g;
22
+ const PATH_SEP = /[/\\]/;
23
+ const DURATION = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/i;
24
+ const JS_EXT = /\.js$/;
25
+ /** A generated module id (`virtual:ogygia/…`, optionally `\0`-resolved) — it has no directory. */
26
+ const VIRTUAL_HOST_RE = /^\0?virtual:/;
27
+ const WRAP_QUOTES = /^['"]|['"]$/g;
28
+ /** One `key: 'value'` pair of an import-attributes clause (shared `g` regex — reset `lastIndex`
29
+ * before each scan). */
30
+ const IMPORT_ATTR_PAIR_G = /([A-Za-z_$][\w$]*|'[^']*'|"[^"]*")\s*:\s*('[^']*'|"[^"]*")/g;
31
+ /** `import X from '…' with { … }` — the one held-region import form in a `.ts`/`.js` module
32
+ * (shared `g` regex — reset `lastIndex` before each scan). */
33
+ const HELD_IMPORT_G = /import\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])([^'"]+)\2\s+with\s*\{([^}]*)\}\s*;?/g;
34
+ const ISLAND_DIR = ".ogygia";
35
+ /** True for a GENERATED island glue id (virtual island entry / wrapper / region binding, or the legacy
36
+ * on-disk `.ogygia/` shape) — the ids that are ogygia's own emit, not authored app/component source. */
37
+ function is_island_path(id) {
38
+ const bare = id.split("?")[0];
39
+ return bare.startsWith("virtual:ogygia/island/") && (bare.endsWith(".js") || bare.endsWith(".svelte")) || bare.startsWith("virtual:ogygia/wrapper/") && bare.endsWith(".svelte") || bare.startsWith("virtual:ogygia/region/") && bare.endsWith(".js") || bare.startsWith("virtual:ogygia/lazy/") && bare.endsWith(".js") || bare.includes("/.ogygia/") && bare.endsWith(".svelte");
40
+ }
41
+ /**
42
+ * Default import-attribute keys — the two-dial grammar. Internal role names stay `hydrate`/`defer`
43
+ * (the wire format + runtime keep those anchors); the user-facing ATTRIBUTE names are the values.
44
+ * Override via `ogygia({ importKeys })`.
45
+ * - `render` attribute → the MODE: `static` (inline HTML) | `deferred` (a hole, fetched) | `live`
46
+ * (a hole that revalidates). Defaults to `static`.
47
+ * - `fetch` attribute → a `deferred` hole's TIMING, when its HTML arrives (default `load`):
48
+ * `with { render: 'deferred', fetch: 'visible' }`.
49
+ * - `wake` attribute → when an island's JS runs (`none`: a lake). Never a hole's timing.
50
+ */
51
+ const DEFAULT_IMPORT_KEYS = {
52
+ wake: "wake",
53
+ fetch: "fetch",
54
+ render: "render",
55
+ preset: "preset",
56
+ region: "region"
57
+ };
58
+ /** The three delivery modes the `render` attribute accepts. */
59
+ const RENDER_MODES = /* @__PURE__ */ new Set([
60
+ "static",
61
+ "deferred",
62
+ "live"
63
+ ]);
64
+ const JS_IDENT = /^[A-Za-z_$][\w$]*$/;
65
+ /** Store constructor callees marking a host declaration as store-like (best-effort signal). */
66
+ const STORE_CTORS = /* @__PURE__ */ new Set([
67
+ "writable",
68
+ "readable",
69
+ "derived",
70
+ "toStore",
71
+ "fromStore"
72
+ ]);
73
+ /** `$name` subscription reads anywhere in a file (heuristic store signal; runes filtered out). */
74
+ const STORE_SUB_RE = /\$([A-Za-z_][\w$]*)/g;
75
+ const RUNE_NAMES = /* @__PURE__ */ new Set([
76
+ "state",
77
+ "derived",
78
+ "effect",
79
+ "props",
80
+ "bindable",
81
+ "inspect",
82
+ "host"
83
+ ]);
84
+ /** 1-based `line:col` for an offset — trace addresses in store-crossing warnings (cold path). */
85
+ function line_col(src, offset) {
86
+ let line = 1;
87
+ let last = -1;
88
+ for (let i = 0; i < offset; i++) if (src.charCodeAt(i) === 10) {
89
+ line++;
90
+ last = i;
91
+ }
92
+ return `${line}:${offset - last}`;
93
+ }
94
+ /** Once per key for the process — the ssr AND client passes both transform the same host. */
95
+ const store_warned = /* @__PURE__ */ new Set();
96
+ function warn_once(key, msg) {
97
+ if (store_warned.has(key)) return;
98
+ store_warned.add(key);
99
+ console.warn(msg);
100
+ }
101
+ const KNOWN_STRATEGIES = /* @__PURE__ */ new Set([
102
+ "load",
103
+ "idle",
104
+ "visible"
105
+ ]);
106
+ const HYDRATE_STRATEGIES = /* @__PURE__ */ new Set([...KNOWN_STRATEGIES, "interaction"]);
107
+ const DEFER_STRATEGIES = HYDRATE_STRATEGIES;
108
+ /** Inline attribute keys accepted after normalization (canonical internal names). */
109
+ const ATTR_SCHEMA = /* @__PURE__ */ new Set([
110
+ "hydrate",
111
+ "defer",
112
+ "margin",
113
+ "keep",
114
+ "stitch",
115
+ "prefetch"
116
+ ]);
117
+ /** A deferred hole's `prefetch` schedule: the warm happens on one of these (never `interaction`,
118
+ * which is the swap — `wake`'s job — and never `none`, which is no schedule). */
119
+ const PREFETCH_STRATEGIES = KNOWN_STRATEGIES;
120
+ /** AST fragment child-key names walked when descending the template. */
121
+ const CHILD_KEYS = [
122
+ "consequent",
123
+ "alternate",
124
+ "body",
125
+ "fallback",
126
+ "pending",
127
+ "then",
128
+ "catch",
129
+ "fragment"
130
+ ];
131
+ /**
132
+ * Merge partial `importKeys` with {@link DEFAULT_IMPORT_KEYS}.
133
+ * Rejects empty strings, non-identifiers, and colliding role names.
134
+ *
135
+ * @param overrides - Optional overrides for one or more roles.
136
+ * @returns Fully resolved key map used by the transform.
137
+ * @throws If a value is not a JS identifier or two roles share the same name.
138
+ */
139
+ const NORMALIZED_CACHE = /* @__PURE__ */ new WeakMap();
140
+ let DEFAULT_NORMALIZED = null;
141
+ function normalize_import_keys(overrides) {
142
+ if (overrides == null) return DEFAULT_NORMALIZED ??= normalize_import_keys_uncached(null);
143
+ const hit = NORMALIZED_CACHE.get(overrides);
144
+ if (hit) return hit;
145
+ const result = normalize_import_keys_uncached(overrides);
146
+ NORMALIZED_CACHE.set(overrides, result);
147
+ return result;
148
+ }
149
+ function normalize_import_keys_uncached(overrides) {
150
+ const wake = (overrides?.wake ?? DEFAULT_IMPORT_KEYS.wake).trim();
151
+ const fetch = (overrides?.fetch ?? DEFAULT_IMPORT_KEYS.fetch).trim();
152
+ const render = (overrides?.render ?? DEFAULT_IMPORT_KEYS.render).trim();
153
+ const preset = (overrides?.preset ?? DEFAULT_IMPORT_KEYS.preset).trim();
154
+ const region = (overrides?.region ?? DEFAULT_IMPORT_KEYS.region).trim();
155
+ for (const [role, name] of [
156
+ ["wake", wake],
157
+ ["fetch", fetch],
158
+ ["render", render],
159
+ ["preset", preset],
160
+ ["region", region]
161
+ ]) if (!name || !JS_IDENT.test(name)) throw new Error(`[ogygia] importKeys.${role} must be a non-empty JS identifier (got ${JSON.stringify(overrides?.[role])}).`);
162
+ if ((/* @__PURE__ */ new Set([
163
+ wake,
164
+ fetch,
165
+ render,
166
+ preset,
167
+ region
168
+ ])).size !== 5) throw new Error("[ogygia] importKeys.wake, importKeys.fetch, importKeys.render, importKeys.preset, and importKeys.region must be distinct.");
169
+ return {
170
+ wake,
171
+ fetch,
172
+ render,
173
+ preset,
174
+ region
175
+ };
176
+ }
177
+ /**
178
+ * Cheap source-scan regex matching any of the configured import-attribute key names.
179
+ * Used to skip AST work on hosts that cannot contain region imports.
180
+ *
181
+ * @param import_keys - Resolved key map from {@link normalize_import_keys}.
182
+ */
183
+ /** Memoized hint regex per resolved key map — `import_keys` is build-invariant, so the RegExp is
184
+ * compiled once instead of on every `transformHost` call (per module, per leg). */
185
+ const HINT_CACHE = /* @__PURE__ */ new WeakMap();
186
+ function import_keys_hint(import_keys) {
187
+ let re = HINT_CACHE.get(import_keys);
188
+ if (re) return re;
189
+ const esc = (s) => s.replace(REGEXP_META, "\\$&");
190
+ re = new RegExp(`${esc(import_keys.wake)}|${esc(import_keys.fetch)}|${esc(import_keys.render)}|${esc(import_keys.preset)}|${esc(import_keys.region)}`);
191
+ HINT_CACHE.set(import_keys, re);
192
+ return re;
193
+ }
194
+ /** Deterministic short id for a region (stable across dev + build).
195
+ * When `salt` is set (production `OGYGIA_SECRET`), ids are not offline-computable (P1-ID).
196
+ * Paths are always posix so SSR/client builds agree across OS path separators. */
197
+ function islandId(relHostPath, index, salt = "") {
198
+ const rel = String(relHostPath).split(PATH_SEP).join("/");
199
+ const msg = salt ? `${salt}\0${rel}::${index}` : `${rel}::${index}`;
200
+ return createHash("md5").update(msg).digest("hex").slice(0, 12);
201
+ }
202
+ /**
203
+ * Deterministic client chunk path for a hydrate island (mirrors `og-runtime.<hash>.js`).
204
+ * SSR bakes this into `<ogygia-region entry>` so the sticky runtime can `import(entry)` with no
205
+ * app-wide regions map — Kit builds server before client, so content-hashed Vite names can't hand off.
206
+ */
207
+ function islandChunkFileName(iid, appDir = "_app") {
208
+ return `${appDir}/immutable/og-region.${iid}.js`;
209
+ }
210
+ /** App-internal URL for {@link islandChunkFileName}: `/<appDir>/immutable/…` (leading slash, appDir,
211
+ * NO base). It is what SSR bakes onto `<ogygia-region entry>`; `Region.svelte` runs it through Kit's
212
+ * `asset()` at render time, which is the sole authority on `base` / the assets CDN / relative paths —
213
+ * so a base is never baked here (that would double-apply it). NOT the on-disk output filename. */
214
+ function islandPublicUrl(iid, appDir = "_app") {
215
+ return "/" + islandChunkFileName(iid, appDir);
216
+ }
217
+ /** Portable wrapper module id — SSR / csr=true host binding (Island/ServerIsland/Lake shell). */
218
+ function wrapperVirtualId(iid) {
219
+ return `virtual:ogygia/wrapper/${iid}.svelte`;
220
+ }
221
+ const AS_REGION_ARG_G = /import\.meta\.og\.asRegion\(\s*([A-Za-z_$][\w$]*)/g;
222
+ /**
223
+ * The local bindings a file hands to `import.meta.og.asRegion(X)`. An island's identity is keyed on
224
+ * the import that binds `X` (`'<specifier>#<export>'`), and the prescan derives it from the raw
225
+ * source on disk — so no pass that runs before the transform may rewrite that import (the debarrel
226
+ * pass leaves declarations binding these names alone, as it does marked imports). A textual scan on
227
+ * purpose: it runs before any parse, on every transformed file.
228
+ */
229
+ function asRegionLocals(code) {
230
+ const out = /* @__PURE__ */ new Set();
231
+ if (!code.includes("asRegion(")) return out;
232
+ AS_REGION_ARG_G.lastIndex = 0;
233
+ let m;
234
+ while (m = AS_REGION_ARG_G.exec(code)) out.add(m[1]);
235
+ return out;
236
+ }
237
+ /**
238
+ * csr=false CLIENT host binding target. Kit still emits those page nodes; pointing marked
239
+ * imports here (instead of wrappers) keeps N island wrappers/entries out of the page graph so
240
+ * `emitFile` owns hydrate modules and Rolldown does not thin-facade them. Hydration uses
241
+ * `import(entry)` only — this stub is never loaded for island JS.
242
+ *
243
+ * Beside the stub the transform emits `import 'virtual:ogygia/fouc-css/<entry>'` so Kit still
244
+ * links stylesheets (FOUC) via CSS-only side effects. Importing the full `.svelte` JS module
245
+ * dual-owns it with the emitFile island entry and Rolldown thin-facades `og-region.*`.
246
+ * Omitting CSS entirely orphans rules (scoped class hashes, no stylesheet).
247
+ */
248
+ const CLIENT_BINDING_STUB = "virtual:ogygia/client-binding-stub";
249
+ /** True if `val` looks like a CSS media query (must contain a balanced-ish `(…)`). */
250
+ function is_media_query(val) {
251
+ const open = val.indexOf("(");
252
+ return open !== -1 && val.indexOf(")", open) !== -1;
253
+ }
254
+ /**
255
+ * Is `node` the member expression `import.meta.og.<prop>` (the macro namespace head)? Used to spot an
256
+ * `import.meta.og.asRegion(…)` call — the macro alternative to a `with { wake }` region import.
257
+ */
258
+ function is_import_meta_og(node, prop) {
259
+ return node?.type === "MemberExpression" && node.property?.type === "Identifier" && node.property.name === prop && node.object?.type === "MemberExpression" && node.object.property?.type === "Identifier" && node.object.property.name === "og" && node.object.object?.type === "MetaProperty" && node.object.object.meta?.name === "import" && node.object.object.property?.name === "meta";
260
+ }
261
+ /**
262
+ * Resolve a local name back to its import binding — the module specifier plus the EXPORT NAME it
263
+ * imports (`'default'` for `import X`, the imported name for `import { X }` / `import { A as X }`).
264
+ * Feeds asRegion: the island entry re-imports the component by this export name (barrel-friendly), and
265
+ * region identity keys on `source#exportName`. Namespace imports (`import * as X`) return `{ namespace }`.
266
+ */
267
+ function import_binding_of(node, localName) {
268
+ for (const spec of node.specifiers ?? []) {
269
+ if (spec.local?.name !== localName) continue;
270
+ if (spec.type === "ImportDefaultSpecifier") return {
271
+ source: node.source.value,
272
+ exportName: "default"
273
+ };
274
+ if (spec.type === "ImportSpecifier") {
275
+ const imported = spec.imported;
276
+ const name = imported?.type === "Literal" ? String(imported.value) : imported?.name;
277
+ return {
278
+ source: node.source.value,
279
+ exportName: name
280
+ };
281
+ }
282
+ if (spec.type === "ImportNamespaceSpecifier") return { namespace: true };
283
+ }
284
+ return null;
285
+ }
286
+ /**
287
+ * Parse an `asRegion` options object in a `.ts`/`.js` file into a `.ts` region marker — `{ wake }` or
288
+ * `{ region }`, the same option surface a `with { … }` import attribute has there. `fail(msg)` throws
289
+ * the caller's contextual error.
290
+ */
291
+ function parse_ts_as_region_options(arg, fail, regionKey, wakeKey, renderKey, fetchKey = "fetch") {
292
+ if (!arg || arg.type !== "ObjectExpression") throw fail(`needs an options object — e.g. \`import.meta.og.asRegion(Comp, { wake: 'load' })\`.`);
293
+ let region;
294
+ let wake;
295
+ let render;
296
+ let fetch;
297
+ for (const p of arg.properties ?? []) {
298
+ if (p.type !== "Property" || p.computed) throw fail("options must be a plain object of string-valued keys.");
299
+ const key = p.key?.name ?? p.key?.value;
300
+ if (p.value?.type !== "Literal" || typeof p.value.value !== "string") throw fail(`option \`${key}\` must be a string literal.`);
301
+ const val = String(p.value.value);
302
+ if (key === regionKey || key === "region") region = val;
303
+ else if (key === wakeKey || key === "wake") wake = val;
304
+ else if (key === fetchKey || key === "fetch") fetch = val;
305
+ else if (key === renderKey || key === "render") render = val;
306
+ else throw fail(`unknown option \`${key}\` — a .ts region takes \`wake: '…'\`, \`region: 'raw'\`, or \`render: 'deferred'\` (with \`fetch\` as its timing).`);
307
+ }
308
+ if (render != null || fetch != null) {
309
+ if (region != null) throw fail("`render` and `region` are different markers — use one.");
310
+ return {
311
+ render,
312
+ wake,
313
+ fetch
314
+ };
315
+ }
316
+ if (region != null && wake != null) throw fail("takes exactly one of `wake` or `region`.");
317
+ if (region != null) return { region };
318
+ if (wake != null) return { wake };
319
+ throw fail("needs a `wake`, `region`, or `render` option.");
320
+ }
321
+ /**
322
+ * The `with { region: … }` marker has ONE value — `'raw'` (an adjective: "a raw/held region"). It
323
+ * carries NO schedule: the wake timing is set at the `region()` call (`region(C, props, { wake })`)
324
+ * or, in a block tree, per node. This is the only surviving import-attribute marker for a component
325
+ * a registry hands to `region()` where the transform can't see the call site.
326
+ *
327
+ * @param raw the marker value
328
+ * @param where host label for error messages
329
+ * @param regionKey configured `region` attribute name (for the message)
330
+ */
331
+ function normalize_region_value(raw, where, regionKey = "region") {
332
+ if ((raw ?? "").trim() === "raw") return;
333
+ throw new Error(`[ogygia] ${where}: \`${regionKey}\` only takes the value 'raw' (a held region with no baked schedule). To bake a schedule use a \`wake:\` mark, or set it at the \`region()\` call.`);
334
+ }
335
+ /**
336
+ * Validate a `wake:` schedule value on a held import (a `.ts` registry / remote). Accepts the wake
337
+ * strategies (`load` / `idle` / `visible` / `interaction`) or a media query — the same vocabulary as
338
+ * a placed island's `wake:`. Returns the value unchanged (it becomes the baked `__hydrate`).
339
+ */
340
+ function normalize_hydrate_value(raw, where, wakeKey = "wake") {
341
+ const v = (raw ?? "").trim();
342
+ if (HYDRATE_STRATEGIES.has(v) || is_media_query(v)) return v;
343
+ throw new Error(`[ogygia] ${where}: unknown \`${wakeKey}\` strategy '${v}'. Use 'load' | 'idle' | 'visible' | 'interaction' | a media query.`);
344
+ }
345
+ /** Reconstruct an import declaration without its `with { ... }` attributes clause. */
346
+ function clean_import_text(source, node) {
347
+ return source.slice(node.start, node.source.end) + ";";
348
+ }
349
+ /**
350
+ * Region keys (`hydrate` / `defer` / `preset` under configured names) on a dynamic
351
+ * `import(spec, { with: { … } })` / `{ assert: { … } }` options object.
352
+ * Returns `[]` when the call has no options or no claimed region keys.
353
+ *
354
+ * @param node
355
+ * @param import_keys
356
+ * @returns
357
+ */
358
+ function region_keys_on_dynamic_import(node, import_keys) {
359
+ const opts = node.options;
360
+ if (!opts || opts.type !== "ObjectExpression") return [];
361
+ const claimed = /* @__PURE__ */ new Set([
362
+ import_keys.wake,
363
+ import_keys.fetch,
364
+ import_keys.render,
365
+ import_keys.preset
366
+ ]);
367
+ const found = [];
368
+ for (const prop of opts.properties) {
369
+ if (prop.type !== "Property" || prop.computed) continue;
370
+ const bag_key = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" ? String(prop.key.value) : null;
371
+ if (bag_key !== "with" && bag_key !== "assert") continue;
372
+ if (prop.value.type !== "ObjectExpression") continue;
373
+ for (const atr of prop.value.properties) {
374
+ if (atr.type !== "Property" || atr.computed) continue;
375
+ const ak = atr.key.type === "Identifier" ? atr.key.name : atr.key.type === "Literal" ? String(atr.key.value) : null;
376
+ if (ak && claimed.has(ak) && !found.includes(ak)) found.push(ak);
377
+ }
378
+ }
379
+ return found;
380
+ }
381
+ /**
382
+ * Walk a script Program body for `ImportExpression` nodes carrying ogygia region keys.
383
+ * Dynamic `import()` cannot author islands — SSR shells need a static marked import binding.
384
+ *
385
+ * @param body
386
+ * @param import_keys
387
+ * @param fail
388
+ */
389
+ function reject_dynamic_region_imports(body, import_keys, fail) {
390
+ const walk = (node) => {
391
+ if (!node || typeof node !== "object") return;
392
+ if (node.type === "ImportExpression") {
393
+ const keys = region_keys_on_dynamic_import(node, import_keys);
394
+ if (keys.length) fail(keys);
395
+ }
396
+ for (const k of Object.keys(node)) {
397
+ if (k === "start" || k === "end" || k === "loc") continue;
398
+ const v = node[k];
399
+ if (Array.isArray(v)) for (const c of v) walk(c);
400
+ else if (v && typeof v === "object" && typeof v.type === "string") walk(v);
401
+ }
402
+ };
403
+ for (const n of body ?? []) walk(n);
404
+ }
405
+ function script_lang_attr(scriptNode) {
406
+ if (!scriptNode) return "";
407
+ for (const attr of scriptNode.attributes ?? []) if (attr.name === "lang" && Array.isArray(attr.value) && attr.value[0]?.data) return ` lang="${attr.value[0].data}"`;
408
+ return "";
409
+ }
410
+ const REMOUNT_SHORTHANDS = /* @__PURE__ */ new Set([
411
+ "cache",
412
+ "empty",
413
+ "swr"
414
+ ]);
415
+ const REMOUNT_ON_EXPIRE = /* @__PURE__ */ new Set(["empty", "fetch"]);
416
+ /** Schedule keywords shared by `hydrate`, `defer` and `remount.revalidate`. */
417
+ const SCHEDULE_KEYWORDS = /* @__PURE__ */ new Set([
418
+ "load",
419
+ "idle",
420
+ "visible"
421
+ ]);
422
+ /**
423
+ * Parse `remount.maxAge` — number (ms) or duration string (`30s` / `5m` / `1h` / `500ms`).
424
+ * @returns milliseconds
425
+ */
426
+ function parse_max_age(raw, err, names) {
427
+ if (raw == null) return void 0;
428
+ if (typeof raw === "number") {
429
+ if (!Number.isFinite(raw) || raw < 0) throw err(names, `\`remount.maxAge\` must be a non-negative number (ms), got ${raw}.`);
430
+ return Math.floor(raw);
431
+ }
432
+ if (typeof raw === "string") {
433
+ const m = raw.trim().match(DURATION);
434
+ if (!m) throw err(names, `unknown remount.maxAge '${raw}'. Use a number (ms) or a duration like '30s' | '5m' | '1h'.`);
435
+ const n = Number(m[1]);
436
+ const unit = (m[2] || "ms").toLowerCase();
437
+ return Math.floor(n * (unit === "ms" ? 1 : unit === "s" ? 1e3 : unit === "m" ? 6e4 : 36e5));
438
+ }
439
+ throw err(names, `\`remount.maxAge\` must be a number (ms) or duration string.`);
440
+ }
441
+ /**
442
+ * Parse a deferred hole's `maxAge` → response cache max-age in **seconds**. Unlike `remount.maxAge`
443
+ * (client staleness, ms), this is an HTTP header value, so a bare number is SECONDS; duration units
444
+ * (`'30s'` / `'5m'` / `'1h'`, or `'500ms'` rounded down) are converted to seconds. `0` = no-store.
445
+ * @returns seconds
446
+ */
447
+ function parse_cache_ttl_sec(raw, err, names) {
448
+ if (raw == null) return void 0;
449
+ if (typeof raw === "number") {
450
+ if (!Number.isFinite(raw) || raw < 0) throw err(names, `\`maxAge\` must be a non-negative number of seconds, got ${raw}.`);
451
+ return Math.floor(raw);
452
+ }
453
+ if (typeof raw === "string") {
454
+ const m = raw.trim().match(DURATION);
455
+ if (!m) throw err(names, `unknown \`maxAge\` '${raw}'. Use seconds (a number) or a duration like '30s' | '5m' | '1h'.`);
456
+ const n = Number(m[1]);
457
+ const unit = (m[2] || "s").toLowerCase();
458
+ const secs = unit === "ms" ? n / 1e3 : unit === "s" ? n : unit === "m" ? n * 60 : n * 3600;
459
+ return Math.floor(secs);
460
+ }
461
+ throw err(names, `\`maxAge\` must be a number of seconds or a duration string.`);
462
+ }
463
+ /**
464
+ * Normalize preset `remount`.
465
+ *
466
+ * Shorthands: `'cache'` | `'empty'` | `'swr'` (`swr` ≡ `{ revalidate: 'load' }`).
467
+ * Object: `{ revalidate?: false | schedule, maxAge?, onExpire?: 'empty' | 'fetch' }`.
468
+ *
469
+ * @returns
470
+ */
471
+ function parse_remount(raw, err, names) {
472
+ if (raw == null) return void 0;
473
+ if (typeof raw === "string") {
474
+ if (!REMOUNT_SHORTHANDS.has(raw)) throw err(names, `unknown remount '${raw}'. Use 'cache' | 'empty' | 'swr'.`);
475
+ if (raw === "swr") return {
476
+ policy: "swr",
477
+ when: "load"
478
+ };
479
+ return { policy: raw };
480
+ }
481
+ if (typeof raw !== "object" || raw === null) throw err(names, `\`remount\` must be 'cache' | 'empty' | 'swr' or \`{ revalidate?, maxAge?, onExpire? }\`.`);
482
+ if ("strategy" in raw || "when" in raw) throw err(names, "`remount` object uses `revalidate` (false | 'load' | 'idle' | 'visible' | media), not `strategy`/`when`. Examples: { revalidate: false, maxAge: '5m' } or { revalidate: 'idle' }. Shorthands: 'cache' | 'empty' | 'swr'.");
483
+ for (const k of Object.keys(raw)) if (k !== "revalidate" && k !== "maxAge" && k !== "onExpire") throw err(names, `unknown remount key '${k}'. Use revalidate, maxAge, onExpire.`);
484
+ const ro = raw;
485
+ if (ro.revalidate == null && ro.maxAge == null && ro.onExpire == null) throw err(names, `\`remount\` object needs revalidate, maxAge, and/or onExpire — or use the 'cache' | 'empty' | 'swr' shorthand.`);
486
+ const out = { policy: "cache" };
487
+ if (ro.revalidate === false || ro.revalidate == null) out.policy = "cache";
488
+ else if (ro.revalidate === true) throw err(names, `\`remount.revalidate: true\` is invalid — use 'load' (or 'idle' | 'visible' | a media query).`);
489
+ else {
490
+ const rev = String(ro.revalidate);
491
+ if (!SCHEDULE_KEYWORDS.has(rev) && !is_media_query(rev)) throw err(names, `unknown remount.revalidate '${rev}'. Use false | 'load' | 'idle' | 'visible' | a media query.`);
492
+ out.policy = "swr";
493
+ out.when = rev;
494
+ }
495
+ if (ro.onExpire != null) {
496
+ const oe = String(ro.onExpire);
497
+ if (!REMOUNT_ON_EXPIRE.has(oe)) throw err(names, `unknown remount.onExpire '${oe}'. Use 'empty' | 'fetch'.`);
498
+ if (out.policy === "cache" && oe === "fetch") throw err(names, "`onExpire: 'fetch'` requires `revalidate` (a schedule). Pure cache expires to empty — use { revalidate: 'load', maxAge, onExpire: 'fetch' } for SWR past maxAge.");
499
+ out.onExpire = oe;
500
+ }
501
+ if (ro.maxAge != null) out.maxAgeMs = parse_max_age(ro.maxAge, err, names);
502
+ return out;
503
+ }
504
+ /**
505
+ * Svelte 5 event attributes (`onclick`, …) that cannot cross devalue for remount:swr.
506
+ * Deliberately NOT `/^on[a-z]+$/` — that falsely rejects data props like `online={x}`.
507
+ */
508
+ const SWR_EVENT_ATTR = /* @__PURE__ */ new Set([
509
+ "onclick",
510
+ "ondblclick",
511
+ "oncontextmenu",
512
+ "onauxclick",
513
+ "onpointerdown",
514
+ "onpointerup",
515
+ "onpointermove",
516
+ "onpointerenter",
517
+ "onpointerleave",
518
+ "onpointercancel",
519
+ "onpointerover",
520
+ "onpointerout",
521
+ "onmousedown",
522
+ "onmouseup",
523
+ "onmousemove",
524
+ "onmouseenter",
525
+ "onmouseleave",
526
+ "onmouseover",
527
+ "onmouseout",
528
+ "onkeydown",
529
+ "onkeyup",
530
+ "onkeypress",
531
+ "onfocus",
532
+ "onblur",
533
+ "onfocusin",
534
+ "onfocusout",
535
+ "oninput",
536
+ "onbeforeinput",
537
+ "onchange",
538
+ "onsubmit",
539
+ "onreset",
540
+ "onscroll",
541
+ "onwheel",
542
+ "ontouchstart",
543
+ "ontouchend",
544
+ "ontouchmove",
545
+ "ontouchcancel",
546
+ "ondrag",
547
+ "ondragstart",
548
+ "ondragend",
549
+ "ondragenter",
550
+ "ondragleave",
551
+ "ondragover",
552
+ "ondrop",
553
+ "oncopy",
554
+ "oncut",
555
+ "onpaste",
556
+ "onanimationstart",
557
+ "onanimationend",
558
+ "onanimationiteration",
559
+ "ontransitionend",
560
+ "ontransitionrun",
561
+ "ontransitionstart",
562
+ "ontransitioncancel",
563
+ "onload",
564
+ "onerror",
565
+ "ontoggle",
566
+ "onselect",
567
+ "oninvalid",
568
+ "onformdata"
569
+ ]);
570
+ /** A template `<Component>` node, which the parser gives its name and offsets. */
571
+ function is_component_node(n) {
572
+ return n.type === "Component";
573
+ }
574
+ /**
575
+ * Everything a `render: 'live'` region needs must survive the wire: the endpoint re-renders the
576
+ * component from devalue'd PROPS alone. Children (snippets) and `bind:` targets cannot cross, and
577
+ * silently losing them on revalidate would be worse than refusing the build.
578
+ */
579
+ function assert_swr_lake_crossable(node, err) {
580
+ if ((node.fragment?.nodes ?? []).filter((n) => !(n.type === "Text" && !String(n.data ?? "").trim())).length) throw err(node.name, `\`render: 'live'\` region <${node.name}> cannot have children — the revalidate endpoint re-renders it from serialized props only, so snippets cannot cross. Move the content inside the component, or make it a static lake with \`wake: 'none'\`.`);
581
+ for (const attr of node.attributes ?? []) {
582
+ if (attr.type === "SpreadAttribute") continue;
583
+ if (attr.type !== "Attribute") throw err(node.name, `\`render: 'live'\` region <${node.name}> cannot use \`${attr.type === "BindDirective" ? "bind:" + attr.name : attr.name || attr.type}\` — only plain attributes and spreads can be serialized for the revalidate endpoint.`);
584
+ const name = attr.name;
585
+ if (typeof name === "string" && SWR_EVENT_ATTR.has(name)) throw err(node.name, `\`render: 'live'\` region <${node.name}> cannot use \`${name}\` — event/callback attributes cannot be serialized for the revalidate endpoint. Pass serializable data props instead.`);
586
+ const val = attr.value;
587
+ const chunk_list = Array.isArray(val) ? val : val != null && val !== true ? [val] : [];
588
+ for (const chunk of chunk_list) {
589
+ const t = chunk?.expression?.type;
590
+ if (t === "ArrowFunctionExpression" || t === "FunctionExpression") throw err(node.name, `\`render: 'live'\` region <${node.name}> cannot use a function value for \`${name}\` — functions cannot be serialized for the revalidate endpoint.`);
591
+ }
592
+ }
593
+ }
594
+ /**
595
+ * Resolve a marked import specifier to the component path baked into generated modules.
596
+ *
597
+ * `$lib` + relative specifiers resolve to ABSOLUTE file paths (build machinery — emitFile prescan,
598
+ * HMR invalidation, fouc-css — keys on them). Anything else (a PACKAGE specifier like
599
+ * `'ogygia/content/tab-group'`, or a Vite alias) is kept VERBATIM: every generated virtual module
600
+ * (island entry, wrapper, child synth, region binding) re-emits the original specifier and Vite's
601
+ * resolver handles it — exports map, workspace link, or alias — so both a node_modules install and
602
+ * a monorepo workspace link work without this transform ever touching the filesystem. A marked
603
+ * specifier Vite cannot resolve fails loudly at resolve time (see the plugin's resolveId hook).
604
+ */
605
+ function resolve_component_path(spec, host_id, ctx) {
606
+ if (typeof spec !== "string" || !spec.trim()) return null;
607
+ if (spec.startsWith(".") && VIRTUAL_HOST_RE.test(host_id)) throw new Error(`[ogygia] ${host_id}: cannot resolve the relative import '${spec}' from a generated module — it has no directory. Its real origin must be threaded (HostCtx.originOf); this is an ogygia bug, please report it.`);
608
+ if (spec === "$lib" || spec.startsWith("$lib/")) return ctx.pathModule.join(ctx.libDir, spec === "$lib" ? "" : spec.slice(5));
609
+ if (spec.startsWith(".")) return ctx.pathModule.resolve(ctx.pathModule.dirname(host_id), spec);
610
+ return spec;
611
+ }
612
+ /**
613
+ * @typedef {Object} TransformResult
614
+ * @property code rewritten host source
615
+ * @property map source map
616
+ * @property islands
617
+ */
618
+ /**
619
+ * @param source
620
+ * @param id absolute host path
621
+ * @param ctx
622
+ * @param ctx.root project root (abs)
623
+ * @param ctx.libDir abs path for `$lib`
624
+ * @param ctx.readFile sync file reader for filename-strategy lookup
625
+ * @param ctx.virtualPathFor virtual module id for an island
626
+ * @returns
627
+ */
628
+ /**
629
+ * csr=true route host: strip its OWN island directives to plain imports (Kit compiles + hydrates them
630
+ * inline) and degrade any `asRegion` to the plain component. No context marker is emitted — the
631
+ * inline-vs-island choice is now a single runtime fact (`documentIsCsrTrue`, context.ts), read the same
632
+ * on both legs, which replaced the old per-host `CSR_TRUE_KEY` marker + `csr=false` reset cascade.
633
+ * @param source @param id @param has_island_hint @param import_keys
634
+ * @returns
635
+ */
636
+ function transform_csr_true_host(source, id, has_island_hint, import_keys) {
637
+ let ast;
638
+ try {
639
+ ast = parse(blank_styles(source), {
640
+ modern: true,
641
+ filename: id
642
+ });
643
+ } catch (err) {
644
+ warn_unparsed(id, err);
645
+ return null;
646
+ }
647
+ const ms = new MagicString(source);
648
+ if (has_island_hint) {
649
+ const region_keys = /* @__PURE__ */ new Set([
650
+ import_keys.wake,
651
+ import_keys.fetch,
652
+ import_keys.render,
653
+ import_keys.preset,
654
+ import_keys.region
655
+ ]);
656
+ for (const node of ast.instance?.content?.body ?? []) {
657
+ if (node.type !== "ImportDeclaration") continue;
658
+ if (!(node.attributes ?? []).filter((a) => a.type === "ImportAttribute").some((a) => region_keys.has(a.key.name ?? a.key.value))) continue;
659
+ ms.overwrite(node.start, node.end, clean_import_text(source, node));
660
+ }
661
+ }
662
+ for (const node of ast.instance?.content?.body ?? []) {
663
+ if (node.type !== "VariableDeclaration") continue;
664
+ for (const decl of node.declarations) {
665
+ const call = decl.init;
666
+ if (call?.type === "CallExpression" && is_import_meta_og(call.callee, "asRegion")) {
667
+ const comp = call.arguments?.[0];
668
+ if (comp?.type === "Identifier") ms.overwrite(call.start, call.end, comp.name);
669
+ }
670
+ }
671
+ }
672
+ return {
673
+ code: ms.toString(),
674
+ map: ms.generateMap({
675
+ hires: true,
676
+ source: id,
677
+ includeContent: true
678
+ }),
679
+ islands: []
680
+ };
681
+ }
682
+ function transformHost(source, id, ctx) {
683
+ return new FileCompilation(source, id, ctx).run();
684
+ }
685
+ /**
686
+ * `FileCompilation` — the per-file compilation unit for a `.svelte` host, and the biggest win of the
687
+ * rewrite. The ephemeral state the fused pass threaded through ~30 nested closures (the AST, the marks
688
+ * map, the free-var sets, …) lives here as `#fields`; the phases are methods over shared `this`:
689
+ * `#analyze()` reads the AST into the fields (the {@link FileIR} shape), `#lower()` rewrites from them.
690
+ * A fresh instance per file, discarded after — it organizes EPHEMERAL state, never cross-file state
691
+ * (that is the Program's job), so it stays pure per file.
692
+ */
693
+ var FileCompilation = class {
694
+ #source;
695
+ #id;
696
+ #ctx;
697
+ #ast;
698
+ #instance_body;
699
+ #module_body;
700
+ #lang;
701
+ #rel_host;
702
+ #import_keys;
703
+ #imports;
704
+ #marked_components;
705
+ #imports_to_strip;
706
+ #as_regions;
707
+ #as_region_nodes;
708
+ #synthetic_export;
709
+ #has_island_children;
710
+ /** Hydrate bindings PLACED as a static `<Tag>` in this host — the complement (a marked hydrate
711
+ * binding with NO static placement) is a dynamically-used island: a registry value, an each/
712
+ * `{@const}`/`svelte:component` use, a prop hand-off. Its call sites are compile-invisible, so
713
+ * children CAN cross at runtime and the app must carry the wire revivers (see program.register —
714
+ * a field regression: a registry-placed carousel's slot pointers hydrated against a runtime
715
+ * without the hub → "Unknown type OgygiaRef", every island dead). */
716
+ #static_placed;
717
+ #has_island_hint;
718
+ constructor(source, id, ctx) {
719
+ this.#source = source;
720
+ this.#id = id;
721
+ this.#ctx = ctx;
722
+ this.#import_keys = normalize_import_keys(ctx.importKeys);
723
+ }
724
+ /** Compile the host: analyze ▸ (early-out) ▸ lower. */
725
+ run() {
726
+ const early = this.#analyze();
727
+ if (early) return early.done;
728
+ return this.#lower();
729
+ }
730
+ /** Walk an AST subtree for an Identifier / Component reference to `local`. Pure over the AST. */
731
+ #ast_refs_local(root, local) {
732
+ let found = false;
733
+ const walk = (node) => {
734
+ if (found || !node || typeof node !== "object") return;
735
+ if (node.type === "Identifier" && node.name === local) {
736
+ found = true;
737
+ return;
738
+ }
739
+ if (node.type === "Component") {
740
+ const n = node.name || "";
741
+ if (n === local || n.startsWith(local + ".")) {
742
+ found = true;
743
+ return;
744
+ }
745
+ }
746
+ for (const k of Object.keys(node)) {
747
+ if (k === "start" || k === "end" || k === "loc") continue;
748
+ const v = node[k];
749
+ if (Array.isArray(v)) for (const c of v) walk(c);
750
+ else if (v && typeof v === "object" && typeof v.type === "string") walk(v);
751
+ }
752
+ };
753
+ if (Array.isArray(root)) for (const n of root) walk(n);
754
+ else walk(root);
755
+ return found;
756
+ }
757
+ /** Is a marked import's `local` used anywhere but its own import (else it is dead, strip it)? */
758
+ #marked_import_referenced(local) {
759
+ for (const n of this.#instance_body) {
760
+ if (n.type === "ImportDeclaration") continue;
761
+ if (this.#ast_refs_local(n, local)) return true;
762
+ }
763
+ for (const n of this.#module_body) {
764
+ if (n.type === "ImportDeclaration") continue;
765
+ if (this.#ast_refs_local(n, local)) return true;
766
+ }
767
+ return this.#ast_refs_local(this.#ast.fragment?.nodes ?? [], local);
768
+ }
769
+ #resolve_region_mark(inline, fail) {
770
+ const import_keys = this.#import_keys;
771
+ const ctx = this.#ctx;
772
+ const rel_host = this.#rel_host;
773
+ const REGION_KEYS = [
774
+ import_keys.wake,
775
+ import_keys.fetch,
776
+ import_keys.render,
777
+ import_keys.preset,
778
+ import_keys.region
779
+ ];
780
+ const err_shim = (_names, msg) => fail(msg);
781
+ if (inline.has("partial") && !REGION_KEYS.includes("partial")) throw fail(`the \`partial\` import attribute was retired. Mark a held-across-a-boundary component with \`${import_keys.region}: 'raw'\` (schedule set at the \`region()\` call), or bake a schedule with a \`${import_keys.wake}:\` mark.`);
782
+ if (!REGION_KEYS.some((k) => inline.has(k))) return null;
783
+ if (inline.has(import_keys.region)) {
784
+ if (inline.size > 1) throw fail(`\`${import_keys.region}\` must be the only import attribute — it only marks a held region ('raw'); set the wake schedule at the \`region()\` call.`);
785
+ normalize_region_value(inline.get(import_keys.region), rel_host, import_keys.region);
786
+ return {
787
+ strategy: "held",
788
+ options: {}
789
+ };
790
+ }
791
+ const attrs = /* @__PURE__ */ new Map();
792
+ let remount_opt;
793
+ let from_preset = null;
794
+ let render_mode;
795
+ let wake_val;
796
+ let fetch_val;
797
+ const live_opts = {};
798
+ if (inline.has(import_keys.preset)) {
799
+ if (inline.size > 1) throw fail(`\`${import_keys.preset}\` must be the only import attribute — put its options (margin, maxAge, …) in the preset definition (ogygia({ presets })).`);
800
+ from_preset = inline.get(import_keys.preset);
801
+ const preset = ctx.presets && ctx.presets[from_preset];
802
+ if (!preset) {
803
+ const avail = Object.keys(ctx.presets || {});
804
+ throw fail(`unknown ${import_keys.preset} '${from_preset}'. Available: ${avail.length ? avail.join(", ") : "(none)"}.`);
805
+ }
806
+ for (const [k, v] of Object.entries(preset)) {
807
+ if (v == null) continue;
808
+ if (k === import_keys.render) render_mode = String(v);
809
+ else if (k === import_keys.wake) wake_val = String(v);
810
+ else if (k === import_keys.fetch) fetch_val = String(v);
811
+ else if (k === "maxAge" || k === "onExpire" || k === "revalidate") live_opts[k] = v;
812
+ else if (k === "margin" || k === "keep" || k === "prefetch") attrs.set(k, String(v));
813
+ else if (k === "stitch") {
814
+ if (v !== true && v !== "serve" && v !== "edge") throw fail(`preset '${from_preset}': \`stitch\` takes \`true\` / 'serve' (the hole fills at ORIGIN on every serve) or 'edge' (an ESI include the CDN fills — the shell stays edge-cached).`);
815
+ attrs.set("stitch", v === "edge" ? "edge" : "serve");
816
+ } else throw fail(`unknown key \`${k}\` in preset '${from_preset}'. Use \`${import_keys.wake}\`, \`${import_keys.fetch}\`, \`${import_keys.render}\`, \`margin\`, \`maxAge\`, \`onExpire\`, \`revalidate\`, \`stitch\`, \`prefetch\`.`);
817
+ }
818
+ } else {
819
+ for (const k of inline.keys()) if (k !== import_keys.wake && k !== import_keys.fetch && k !== import_keys.render && k !== "keep" && k !== "stitch" && k !== "prefetch") throw fail(`\`${k}\` is not allowed inline. Use \`${import_keys.wake}\`, \`${import_keys.fetch}\`, \`${import_keys.render}\`, \`keep\`, \`stitch\`, \`prefetch\`, or a named \`${import_keys.preset}\` — options like \`margin\` / \`maxAge\` belong in plugin config (ogygia({ presets })).`);
820
+ if (inline.has(import_keys.render)) render_mode = inline.get(import_keys.render);
821
+ if (inline.has(import_keys.wake)) wake_val = inline.get(import_keys.wake);
822
+ if (inline.has(import_keys.fetch)) fetch_val = inline.get(import_keys.fetch);
823
+ if (inline.has("keep")) attrs.set("keep", inline.get("keep"));
824
+ if (inline.has("prefetch")) attrs.set("prefetch", inline.get("prefetch"));
825
+ if (inline.has("stitch")) {
826
+ const mode = inline.get("stitch");
827
+ if (mode !== "serve" && mode !== "edge") throw fail(`\`stitch: '${mode}'\` — the inline values are \`stitch: 'serve'\` (the hole fills at ORIGIN on every serve) or \`stitch: 'edge'\` (an ESI include the CDN fills — the shell stays edge-cached).`);
828
+ attrs.set("stitch", mode);
829
+ }
830
+ }
831
+ if (render_mode != null && !RENDER_MODES.has(render_mode)) throw fail(`unknown ${import_keys.render} '${render_mode}'. Use 'static' (inline HTML) | 'deferred' (a hole, fetched) | 'live' (a hole that revalidates).`);
832
+ const said = from_preset ? `preset '${from_preset}'` : "this import";
833
+ if (render_mode === "deferred" && wake_val != null) throw fail(`${said} times a \`${import_keys.render}: 'deferred'\` hole with \`${import_keys.wake}: '${wake_val}'\` — that spelling is gone. Write \`${import_keys.render}: 'deferred', ${import_keys.fetch}: '${wake_val}'\` instead: \`${import_keys.fetch}\` says when a hole's HTML arrives. (\`${import_keys.wake}\` is when an island's JS runs; a hole ships no JS.)`);
834
+ if (fetch_val != null && render_mode !== "deferred") throw fail(`${said} sets \`${import_keys.fetch}: '${fetch_val}'\` without \`${import_keys.render}: 'deferred'\` — \`${import_keys.fetch}\` is a hole's timing. Write \`${import_keys.render}: 'deferred', ${import_keys.fetch}: '${fetch_val}'\` for a hole` + (render_mode == null ? `, or \`${import_keys.wake}: '${fetch_val}'\` for an island.` : "."));
835
+ if (render_mode === "deferred") attrs.set("defer", fetch_val ?? "load");
836
+ else if (render_mode === "live") {
837
+ attrs.set("hydrate", "none");
838
+ remount_opt = parse_remount({
839
+ revalidate: wake_val ?? live_opts.revalidate ?? "load",
840
+ ...live_opts.maxAge != null ? { maxAge: live_opts.maxAge } : {},
841
+ ...live_opts.onExpire != null ? { onExpire: live_opts.onExpire } : {}
842
+ }, err_shim, "");
843
+ } else if (wake_val != null) attrs.set("hydrate", wake_val);
844
+ if (attrs.has("stitch") && !attrs.has("defer")) throw fail(`\`stitch\` is only valid with \`${import_keys.render}: 'deferred'\` — it says WHERE a hole fills on artifact serves; there is no hole without 'deferred'.`);
845
+ if (attrs.has("prefetch") && !attrs.has("defer")) throw fail(`\`prefetch\` is only valid with \`${import_keys.render}: 'deferred'\` — it warms a hole's HTML ahead of its \`${import_keys.fetch}\` timing; an island's JS is preloaded by the runtime when it wakes, not by \`prefetch\`.`);
846
+ if (from_preset && !attrs.has("hydrate") && !attrs.has("defer")) throw fail(`${import_keys.preset} '${from_preset}' must set \`${import_keys.render}\` or \`${import_keys.wake}\` — a margin-only (or empty) preset is a no-op.`);
847
+ for (const k of attrs.keys()) if (!ATTR_SCHEMA.has(k)) throw fail(from_preset ? `unknown key \`${k}\` in preset '${from_preset}'.` : `unknown import attribute \`${k}\`.`);
848
+ if (remount_opt && attrs.get("hydrate") !== "none") throw fail(`\`remount\` is only valid with \`${import_keys.wake}: 'none'\`.`);
849
+ if (attrs.has("defer")) {
850
+ const dval = attrs.get("defer");
851
+ let when;
852
+ if (DEFER_STRATEGIES.has(dval)) when = dval;
853
+ else if (is_media_query(dval)) when = dval;
854
+ else throw fail(`\`${import_keys.render}: 'deferred'\` fetches on its \`${import_keys.fetch}\` timing, but '${dval}' is not one. Use \`${import_keys.fetch}: 'load' | 'idle' | 'visible' | 'interaction'\` or a media query (not 'none' — a hole must fetch).`);
855
+ const options = { when };
856
+ if (attrs.has("prefetch")) {
857
+ const p = attrs.get("prefetch");
858
+ if (p === "interaction" || !(PREFETCH_STRATEGIES.has(p) || is_media_query(p))) throw fail(`\`prefetch: '${p}'\` — a hole prefetches its HTML on 'load' | 'idle' | 'visible' | a media query. Never 'interaction': that is the SWAP, \`${import_keys.fetch}\`'s job; prefetch only warms the bytes ahead of it.`);
859
+ if (p === when) throw fail(`\`prefetch: '${p}'\` is this hole's own \`${import_keys.fetch}\` schedule — it adds nothing. Prefetch EARLIER than the swap ('load' | 'idle' | 'visible' for an 'interaction' hole), or drop it.`);
860
+ options.prefetch = p;
861
+ }
862
+ if (when === "visible" || options.prefetch === "visible") options.margin = attrs.get("margin") ?? ctx.visibleMargin ?? void 0;
863
+ if (live_opts.maxAge != null) {
864
+ const ttl = parse_cache_ttl_sec(live_opts.maxAge, err_shim, "");
865
+ if (ttl != null && ttl > 0) options.cacheTtlSec = ttl;
866
+ }
867
+ if (attrs.has("stitch")) options.stitch = attrs.get("stitch");
868
+ return {
869
+ strategy: "server",
870
+ options
871
+ };
872
+ }
873
+ if (attrs.has("hydrate")) {
874
+ const val = attrs.get("hydrate");
875
+ if (val === "none") {
876
+ const lake_opts = {};
877
+ if (remount_opt) {
878
+ lake_opts.remount = remount_opt.policy;
879
+ if (remount_opt.when) lake_opts.when = remount_opt.when;
880
+ if (remount_opt.maxAgeMs != null) lake_opts.maxAgeMs = remount_opt.maxAgeMs;
881
+ if (remount_opt.onExpire) lake_opts.onExpire = remount_opt.onExpire;
882
+ if (remount_opt.policy === "swr" && remount_opt.when === "visible") lake_opts.margin = attrs.get("margin") ?? ctx.visibleMargin ?? void 0;
883
+ }
884
+ return {
885
+ strategy: "lake",
886
+ options: lake_opts
887
+ };
888
+ }
889
+ if (val === "false") throw fail(`\`${import_keys.wake}: 'false'\` is not valid — use \`${import_keys.wake}: 'none'\` for a lake (a frozen region inside a hydrated island). See DESIGN.md.`);
890
+ let strategy;
891
+ if (HYDRATE_STRATEGIES.has(val)) strategy = val;
892
+ else if (is_media_query(val)) strategy = val;
893
+ else throw fail(`unknown ${import_keys.wake} strategy '${val}'. Use 'load' | 'idle' | 'visible' | 'interaction' | a media query.`);
894
+ const options = {};
895
+ if (strategy === "visible") options.margin = attrs.get("margin") ?? ctx.visibleMargin ?? void 0;
896
+ if (attrs.has("keep")) {
897
+ const p = String(attrs.get("keep")).trim();
898
+ if (!p) throw fail(`\`keep\` needs a non-empty name (e.g. keep: 'player').`);
899
+ options.keep = p;
900
+ }
901
+ return {
902
+ strategy,
903
+ options
904
+ };
905
+ }
906
+ return null;
907
+ }
908
+ /** Depth-first: the first `import.meta.og.asRegion(…)` CallExpression in a subtree (a STRAY use). */
909
+ #find_stray_as_region(node) {
910
+ if (!node || typeof node !== "object") return null;
911
+ if (Array.isArray(node)) {
912
+ for (const n of node) {
913
+ const hit = this.#find_stray_as_region(n);
914
+ if (hit) return hit;
915
+ }
916
+ return null;
917
+ }
918
+ if (node.type === "CallExpression" && is_import_meta_og(node.callee, "asRegion")) return node;
919
+ for (const k in node) {
920
+ if (k === "type" || k === "start" || k === "end" || k === "loc" || k === "parent") continue;
921
+ const v = node[k];
922
+ if (v && typeof v === "object") {
923
+ const hit = this.#find_stray_as_region(v);
924
+ if (hit) return hit;
925
+ }
926
+ }
927
+ return null;
928
+ }
929
+ /** Non-fallback children on a hydrate/defer call site cannot cross devalue — reject. */
930
+ #assert_portable_children(node, local, is_server) {
931
+ const kids = (node.fragment?.nodes ?? []).filter((n) => !(n.type === "Text" && !String(n.data ?? "").trim()));
932
+ if (kids.length === 0) return;
933
+ if (is_server && kids.length === 1 && kids[0].type === "SnippetBlock" && kids[0].expression?.name === "ogygiaFallback") return;
934
+ throw new Error(`[ogygia] ${this.#rel_host}: <${local}> has host children/snippets that cannot cross the island boundary. Under portable bindings, pass serializable props and put UI inside the island component` + (is_server ? ` (only the reserved \`{#snippet ogygiaFallback()}\` may appear at the call site).` : `.`));
935
+ }
936
+ /** Walk the markup: enforce region-usage rules + surface whether a hydrate island has real
937
+ * children (the app then needs the wire slot revivers). Mutates #has_island_children. */
938
+ #visit_usages(nodes) {
939
+ for (const node of nodes ?? []) {
940
+ if (node.type === "Component") {
941
+ const name = node.name || "";
942
+ if (name.includes(".")) {
943
+ const root = name.split(".")[0];
944
+ if (this.#marked_components.has(root) && this.#marked_components.get(root).strategy !== "lake") throw new Error(`[ogygia] ${this.#rel_host}: dotted tag \`<${name}>\` is not supported for region import '${root}'. Import the leaf component with \`with { hydrate|defer }\` instead.`);
945
+ } else if (this.#marked_components.has(name)) {
946
+ this.#static_placed.add(name);
947
+ const mark = this.#marked_components.get(name);
948
+ if (mark.strategy === "lake") {
949
+ if (mark.options?.remount === "swr") assert_swr_lake_crossable(node, (specifiers, msg) => this.#err(specifiers, msg));
950
+ } else if (mark.strategy === "server" || mark.strategy === "held") this.#assert_portable_children(node, name, mark.strategy === "server");
951
+ else if ((node.fragment?.nodes ?? []).filter((n) => !(n.type === "Text" && !String(n.data ?? "").trim())).length) this.#has_island_children = true;
952
+ }
953
+ }
954
+ for (const k of CHILD_KEYS) if (node[k]?.nodes) this.#visit_usages(node[k].nodes);
955
+ }
956
+ }
957
+ /** Root-relative posix path — island ids must not drift across Windows/POSIX build legs. */
958
+ #posix_rel(abs) {
959
+ return this.#ctx.pathModule.relative(this.#ctx.root, abs).split(PATH_SEP).join("/");
960
+ }
961
+ /**
962
+ * Region-identity path for a component: filesystem components key on their root-relative posix
963
+ * path; a package specifier IS its own identity (already stable + posix, and identical across
964
+ * hosts — so two hosts marking the same package import share one region id). A file under a
965
+ * DECLARED `ogygia.files` package keys as `<pkg-name>/<rel>` instead of root-relative: pnpm
966
+ * store paths carry version+peer hashes, so a root-relative key would change per install and
967
+ * prod HTML would stop matching its chunks.
968
+ */
969
+ #component_identity(p) {
970
+ if (!this.#ctx.pathModule.isAbsolute(p)) return p;
971
+ return (typeof this.#ctx.pkg_identity === "function" ? this.#ctx.pkg_identity(p) : null) ?? this.#posix_rel(p);
972
+ }
973
+ /** `[ogygia] host: import { … } — msg` — the region-import error formatter. */
974
+ #err(specifiers, msg) {
975
+ return /* @__PURE__ */ new Error(`[ogygia] ${this.#rel_host}: import { ${specifiers} } — ${msg}`);
976
+ }
977
+ /** `[ogygia] host: import.meta.og.asRegion (local) — msg` — the asRegion error formatter. */
978
+ #as_err(local, msg) {
979
+ return /* @__PURE__ */ new Error(`[ogygia] ${this.#rel_host}: import.meta.og.asRegion (${local}) — ${msg}`);
980
+ }
981
+ /** Read asRegion's options object into the same key→value map an import attribute produces, so the
982
+ * SHARED parser gives asRegion EXACTLY the import-attribute option surface. Object-only and
983
+ * string-valued — the same shape as a `with { … }` clause. */
984
+ #as_region_inline(arg, local) {
985
+ if (!arg || arg.type !== "ObjectExpression") throw this.#as_err(local, `needs an options object — \`import.meta.og.asRegion(Comp, { wake: 'load' })\`, the same shape as \`with { … }\`.`);
986
+ const inline = /* @__PURE__ */ new Map();
987
+ for (const p of arg.properties ?? []) {
988
+ if (p.type !== "Property" || p.computed) throw this.#as_err(local, "options must be a plain object of string-valued keys.");
989
+ const key = p.key?.name ?? p.key?.value;
990
+ if (p.value?.type !== "Literal" || typeof p.value.value !== "string") throw this.#as_err(local, `option \`${key}\` must be a string literal (region options are string-valued, exactly like an import attribute).`);
991
+ inline.set(String(key), String(p.value.value));
992
+ }
993
+ return inline;
994
+ }
995
+ /**
996
+ * Analyze — read the host's AST into the compilation's fields: its imports, region marks (import
997
+ * attributes + `asRegion` call sites), the usage findings, and the csr tri-state. No MagicString,
998
+ * no rewriting — a pure "what does this file declare" pass. Returns `{ done }` for an early exit (a
999
+ * csr=true route host, the cheap island-hint bailout, a snippet-less file with no islands), or
1000
+ * `null` once the fields are populated and `#lower()` should run.
1001
+ */
1002
+ #analyze() {
1003
+ const source = this.#source;
1004
+ const id = this.#id;
1005
+ const ctx = this.#ctx;
1006
+ const import_keys = this.#import_keys;
1007
+ const has_island_hint = import_keys_hint(import_keys).test(source);
1008
+ if (ctx.routeCsr === true) return { done: has_island_hint || source.includes("asRegion") ? transform_csr_true_host(source, id, has_island_hint, import_keys) : null };
1009
+ if (!has_island_hint && !source.includes("{#snippet") && !source.includes("asRegion")) return { done: null };
1010
+ let ast;
1011
+ try {
1012
+ ast = parse(blank_styles(source), {
1013
+ modern: true,
1014
+ filename: id
1015
+ });
1016
+ } catch (err) {
1017
+ warn_unparsed(id, err);
1018
+ return { done: null };
1019
+ }
1020
+ const instance_body = ast.instance?.content?.body ?? [];
1021
+ const module_body = ast.module?.content?.body ?? [];
1022
+ const lang = script_lang_attr(ast.instance) || script_lang_attr(ast.module);
1023
+ const path = ctx.pathModule;
1024
+ const rel_host = this.#rel_host = path.relative(ctx.root, id).split(PATH_SEP).join("/");
1025
+ reject_dynamic_region_imports(instance_body, import_keys, (keys) => {
1026
+ throw new Error(`[ogygia] ${rel_host}: dynamic import() with { with: { ${keys.join(", ")} } } is not supported. Mark islands with a static \`import X from '…' with { ${import_keys.wake}: '…' }\` (or \`${import_keys.render}\` / \`${import_keys.preset}\`) — the binding becomes a portable island component. For a click-loaded chunk that is not an island, use plain \`await import('./Comp.svelte')\` (no region attributes) inside a host island. To delay a real island until click, gate a static region import with \`{#if}\`.`);
1027
+ });
1028
+ reject_dynamic_region_imports(module_body, import_keys, (keys) => {
1029
+ throw new Error(`[ogygia] ${rel_host}: dynamic import() with { with: { ${keys.join(", ")} } } is not supported. Mark islands with a static \`import X from '…' with { ${import_keys.wake}: '…' }\` (or \`${import_keys.render}\` / \`${import_keys.preset}\`) — the binding becomes a portable island component. For a click-loaded chunk that is not an island, use plain \`await import('./Comp.svelte')\` (no region attributes) inside a host island. To delay a real island until click, gate a static region import with \`{#if}\`.`);
1030
+ });
1031
+ /** localName -> { node, cleaned } */
1032
+ const imports = /* @__PURE__ */ new Map();
1033
+ /** localName -> { strategy, options } */
1034
+ const marked_components = this.#marked_components = /* @__PURE__ */ new Map();
1035
+ const imports_to_strip = /* @__PURE__ */ new Set();
1036
+ for (const node of instance_body) {
1037
+ if (node.type !== "ImportDeclaration") continue;
1038
+ const cleaned = clean_import_text(source, node);
1039
+ for (const spec of node.specifiers) imports.set(spec.local.name, {
1040
+ node,
1041
+ cleaned
1042
+ });
1043
+ const attr_list = (node.attributes ?? []).filter((a) => a.type === "ImportAttribute");
1044
+ if (attr_list.length === 0) continue;
1045
+ const inline = /* @__PURE__ */ new Map();
1046
+ for (const a of attr_list) inline.set(a.key.name ?? a.key.value, String(a.value.value));
1047
+ const names = node.specifiers.map((sp) => sp.local.name).join(", ");
1048
+ const mark = this.#resolve_region_mark(inline, (m) => this.#err(names, m));
1049
+ if (!mark) continue;
1050
+ for (const spec of node.specifiers) marked_components.set(spec.local.name, mark);
1051
+ }
1052
+ const synthetic_export = /* @__PURE__ */ new Map();
1053
+ const as_regions = [];
1054
+ const as_region_nodes = /* @__PURE__ */ new Set();
1055
+ for (const node of instance_body) {
1056
+ if (node.type !== "VariableDeclaration") continue;
1057
+ if (node.declarations.filter((d) => d.init?.type === "CallExpression" && is_import_meta_og(d.init.callee, "asRegion")).length === 0) continue;
1058
+ if (node.kind !== "const") throw this.#as_err(node.declarations[0]?.id?.name ?? "?", `asRegion must be bound with \`const\` (not \`${node.kind}\`) — it compiles to an import binding.`);
1059
+ if (node.declarations.length !== 1) throw this.#as_err(node.declarations[0]?.id?.name ?? "?", "declare one asRegion per `const X = import.meta.og.asRegion(…)` statement.");
1060
+ const decl = node.declarations[0];
1061
+ if (decl.id?.type !== "Identifier") throw this.#as_err("?", "must bind to a plain `const Name = …`.");
1062
+ const local = decl.id.name;
1063
+ const call_args = decl.init.arguments ?? [];
1064
+ const comp_arg = call_args[0];
1065
+ if (!comp_arg || comp_arg.type !== "Identifier") throw this.#as_err(local, "the first argument must be a component you imported (a bare identifier).");
1066
+ if (marked_components.has(comp_arg.name)) throw this.#as_err(local, `'${comp_arg.name}' is already marked an island with an import attribute (\`with { … }\`). Use the import attribute OR asRegion, not both.`);
1067
+ const info = imports.get(comp_arg.name);
1068
+ if (!info) throw this.#as_err(local, `'${comp_arg.name}' is not an imported component — import it first (e.g. \`import { ${comp_arg.name} } from './…'\`).`);
1069
+ const binding = import_binding_of(info.node, comp_arg.name);
1070
+ if (!binding || "namespace" in binding) throw this.#as_err(local, `'${comp_arg.name}' must be a default or named import, not a namespace import.`);
1071
+ const mark = this.#resolve_region_mark(this.#as_region_inline(call_args[1], local), (m) => this.#as_err(local, m));
1072
+ if (!mark) throw this.#as_err(local, "needs a `wake`, `render`, `region`, or `preset` option.");
1073
+ imports.set(local, {
1074
+ node: {
1075
+ start: node.start,
1076
+ end: node.end,
1077
+ type: "ImportDeclaration",
1078
+ source: { value: binding.source },
1079
+ specifiers: [{
1080
+ type: "ImportDefaultSpecifier",
1081
+ local: { name: local }
1082
+ }],
1083
+ attributes: []
1084
+ },
1085
+ cleaned: ""
1086
+ });
1087
+ marked_components.set(local, mark);
1088
+ synthetic_export.set(local, binding.exportName);
1089
+ as_regions.push({
1090
+ local,
1091
+ compLocal: comp_arg.name,
1092
+ node
1093
+ });
1094
+ as_region_nodes.add(node);
1095
+ }
1096
+ for (const place of [
1097
+ ...instance_body.filter((n) => !as_region_nodes.has(n)),
1098
+ ...module_body,
1099
+ ...ast.fragment?.nodes ?? []
1100
+ ]) {
1101
+ const stray = this.#find_stray_as_region(place);
1102
+ if (stray) {
1103
+ const arg = stray.arguments?.[0];
1104
+ throw this.#as_err(arg?.type === "Identifier" ? arg.name : "?", "must be a top-level `const Name = import.meta.og.asRegion(Comp, timing)` in the instance <script> — it compiles to a hoisted import, so it can never sit inside a loop, function, block, larger expression, `<script module>`, or markup.");
1105
+ }
1106
+ }
1107
+ if (marked_components.size === 0 && as_regions.length === 0 && !source.includes("{#snippet")) return { done: null };
1108
+ /** Non-fallback children on a hydrate/defer call site cannot cross devalue — reject. */
1109
+ this.#has_island_children = false;
1110
+ this.#static_placed = /* @__PURE__ */ new Set();
1111
+ this.#visit_usages(ast.fragment?.nodes ?? []);
1112
+ this.#ast = ast;
1113
+ this.#instance_body = instance_body;
1114
+ this.#module_body = module_body;
1115
+ this.#lang = lang;
1116
+ this.#rel_host = rel_host;
1117
+ this.#import_keys = import_keys;
1118
+ this.#imports = imports;
1119
+ this.#imports_to_strip = imports_to_strip;
1120
+ this.#as_regions = as_regions;
1121
+ this.#as_region_nodes = as_region_nodes;
1122
+ this.#synthetic_export = synthetic_export;
1123
+ this.#has_island_hint = has_island_hint;
1124
+ return null;
1125
+ }
1126
+ /**
1127
+ * Lower — the compilation's fields → rewritten source + `IslandDescriptor[]`. Consumes ONLY the
1128
+ * analyze pass's fields (it never re-reads the file): creates the MagicString, rewrites each marked
1129
+ * import to its wrapper / binding / stub, mints the island records, and brands portable snippets.
1130
+ * `err` / `ast_refs_local` / `marked_import_referenced` are rebuilt here from those fields — pure
1131
+ * over the AST + host path.
1132
+ */
1133
+ #lower() {
1134
+ const source = this.#source;
1135
+ const id = this.#id;
1136
+ const ctx = this.#ctx;
1137
+ const ast = this.#ast;
1138
+ const instance_body = this.#instance_body;
1139
+ const module_body = this.#module_body;
1140
+ const lang = this.#lang;
1141
+ const rel_host = this.#rel_host;
1142
+ const import_keys = this.#import_keys;
1143
+ const imports = this.#imports;
1144
+ const marked_components = this.#marked_components;
1145
+ const imports_to_strip = this.#imports_to_strip;
1146
+ const as_regions = this.#as_regions;
1147
+ const as_region_nodes = this.#as_region_nodes;
1148
+ const synthetic_export = this.#synthetic_export;
1149
+ const has_island_children = this.#has_island_children;
1150
+ const has_island_hint = this.#has_island_hint;
1151
+ const path = ctx.pathModule;
1152
+ const s = new MagicString(source);
1153
+ const origin = typeof ctx.originOf === "function" ? ctx.originOf(id) || id : id;
1154
+ const islands_by_id = /* @__PURE__ */ new Map();
1155
+ const salt = ctx.idSalt || "";
1156
+ const wrapperPathFor = typeof ctx.wrapperPathFor === "function" ? ctx.wrapperPathFor : (_host, iid) => wrapperVirtualId(iid);
1157
+ const link_virtual = ctx.linkVirtualIsland !== false;
1158
+ const binding_stub = typeof ctx.clientBindingStub === "string" && ctx.clientBindingStub ? ctx.clientBindingStub : CLIENT_BINDING_STUB;
1159
+ const rewritten_import_nodes = /* @__PURE__ */ new Set();
1160
+ /** Entry module specs already emitted as side-effect CSS imports on this host. */
1161
+ const fouc_css_specs = /* @__PURE__ */ new Set();
1162
+ /**
1163
+ * Kit links CSS only from the *client* page graph, so a marked component whose scoped CSS is
1164
+ * NOT in that graph must side-effect-import `virtual:ogygia/fouc-css/<entry>` (the CSS graph
1165
+ * only — not the component JS) to ship its stylesheet without dual-owning the module emitFile
1166
+ * registers as `og-region.*`. When the CSS *is* absent from the client graph depends on the
1167
+ * binding, not on `link_virtual` alone:
1168
+ * - island STUB leg (`!link_virtual`), DEV only: no component in the graph → needs the link
1169
+ * (Vite serves component CSS as modules; the graph is the only way it reaches the page).
1170
+ * In a BUILD the link is dropped: Kit would link the CSS of every marked import the host
1171
+ * declares, rendered or not, while Region.svelte already links exactly the islands that
1172
+ * render (`island_css_html`, from the chunk closure `islandCss()` records) — the render
1173
+ * pass decides, not the import list. See link/registry-stub.ts for the same rule applied
1174
+ * to a `.ts` registry the host imports.
1175
+ * - island real-wrapper leg (`link_virtual`, a csr=true-capable page/layout): the wrapper
1176
+ * pulls the hydrate entry, which carries the CSS → no link needed.
1177
+ * - LAKE (`always_link_css`): its inner is client-stubbed to render-nothing on EVERY client
1178
+ * leg — including the real-wrapper leg a csr=true-capable layout takes — so its CSS never
1179
+ * reaches the client graph on its own and it always needs the link on a client build.
1180
+ * `ctx.ssr` guards the server build (it renders the real component and collects CSS directly).
1181
+ */
1182
+ const binding_rewrite = (local, bindingPath, componentPathAbs, always_link_css = false) => {
1183
+ let text = `import ${local} from ${JSON.stringify(bindingPath)};`;
1184
+ if (!ctx.ssr && (always_link_css || !link_virtual && ctx.dev) && typeof componentPathAbs === "string" && componentPathAbs && path.isAbsolute(componentPathAbs) && !fouc_css_specs.has(componentPathAbs)) {
1185
+ fouc_css_specs.add(componentPathAbs);
1186
+ const rel = this.#posix_rel(componentPathAbs);
1187
+ text += `\nimport ${JSON.stringify(foucCssVirtualId(rel))};`;
1188
+ }
1189
+ return text;
1190
+ };
1191
+ const host_declared = /* @__PURE__ */ new Set();
1192
+ const collect_pattern_names = (pat) => {
1193
+ if (!pat) return;
1194
+ if (pat.type === "Identifier") host_declared.add(pat.name);
1195
+ else if (pat.type === "ObjectPattern") for (const p of pat.properties ?? []) collect_pattern_names(p.value ?? p.argument);
1196
+ else if (pat.type === "ArrayPattern") for (const e of pat.elements ?? []) collect_pattern_names(e);
1197
+ else if (pat.type === "RestElement") collect_pattern_names(pat.argument);
1198
+ else if (pat.type === "AssignmentPattern") collect_pattern_names(pat.left);
1199
+ };
1200
+ const declare = (node) => {
1201
+ if (node?.type === "VariableDeclaration") for (const d of node.declarations) collect_pattern_names(d.id);
1202
+ else if (node?.type === "FunctionDeclaration" || node?.type === "ClassDeclaration") {
1203
+ if (node.id?.name) host_declared.add(node.id.name);
1204
+ } else if (node?.type === "ExportNamedDeclaration") declare(node.declaration);
1205
+ else if (node?.type === "LabeledStatement" && node.label?.name === "$") {
1206
+ const e = node.body?.type === "ExpressionStatement" ? node.body.expression : null;
1207
+ if (e?.type === "AssignmentExpression") collect_pattern_names(e.left);
1208
+ }
1209
+ };
1210
+ for (const node of instance_body) declare(node);
1211
+ for (const node of module_body) declare(node);
1212
+ const module_imports = /* @__PURE__ */ new Map();
1213
+ for (const node of module_body) {
1214
+ if (node.type !== "ImportDeclaration") continue;
1215
+ const cleaned = clean_import_text(source, node);
1216
+ for (const spec of node.specifiers) module_imports.set(spec.local.name, cleaned);
1217
+ }
1218
+ const store_like = /* @__PURE__ */ new Set();
1219
+ for (const node of instance_body) {
1220
+ if (node.type !== "VariableDeclaration") continue;
1221
+ for (const d of node.declarations) {
1222
+ const callee = d.init?.type === "CallExpression" ? d.init.callee : null;
1223
+ const cname = callee?.type === "Identifier" ? callee.name : null;
1224
+ if (cname && STORE_CTORS.has(cname) && d.id?.type === "Identifier") store_like.add(d.id.name);
1225
+ }
1226
+ }
1227
+ STORE_SUB_RE.lastIndex = 0;
1228
+ for (let m; m = STORE_SUB_RE.exec(source);) {
1229
+ if (source.charCodeAt(m.index - 1) === 36) continue;
1230
+ if (!RUNE_NAMES.has(m[1])) store_like.add(m[1]);
1231
+ }
1232
+ for (const [local, mark] of marked_components) {
1233
+ const info = imports.get(local);
1234
+ if (!info) continue;
1235
+ if (!this.#marked_import_referenced(local)) {
1236
+ if (!rewritten_import_nodes.has(info.node)) imports_to_strip.add(info.node);
1237
+ continue;
1238
+ }
1239
+ const entry_spec = info.node.source?.value;
1240
+ const componentPath = resolve_component_path(entry_spec, origin, ctx);
1241
+ if (!componentPath) throw new Error(`[ogygia] ${rel_host}: region import '${local}' needs a module specifier ($lib/…, relative, or a package specifier like 'pkg/component').`);
1242
+ const exportName = synthetic_export.get(local);
1243
+ const comp_rel = this.#component_identity(componentPath);
1244
+ const id_base = exportName && exportName !== "default" ? `${comp_rel}#${exportName}` : comp_rel;
1245
+ const identity = regionIdentity(id_base, mark);
1246
+ const iid = regionId(identity, salt);
1247
+ const entryPath = ctx.virtualPathFor(id, iid);
1248
+ const wrapPath = wrapperPathFor(id, iid);
1249
+ const bindingPath = link_virtual ? wrapPath : binding_stub;
1250
+ if (mark.strategy === "lake") {
1251
+ if (!islands_by_id.has(iid)) {
1252
+ const swr = (mark.options?.remount || "cache") === "swr";
1253
+ islands_by_id.set(iid, {
1254
+ id: iid,
1255
+ virtualPath: swr ? entryPath : void 0,
1256
+ source: swr ? island_entry_source(componentPath, iid, exportName) : void 0,
1257
+ wrapperPath: wrapPath,
1258
+ wrapperSource: lake_wrapper_source(iid, componentPath, mark.options, exportName, lang),
1259
+ hostPath: origin,
1260
+ componentPath,
1261
+ server: swr,
1262
+ kind: "lake",
1263
+ lakes: ["OgygiaLakeInner"],
1264
+ identity
1265
+ });
1266
+ }
1267
+ if (!rewritten_import_nodes.has(info.node)) {
1268
+ s.overwrite(info.node.start, info.node.end, binding_rewrite(local, bindingPath, componentPath, true));
1269
+ rewritten_import_nodes.add(info.node);
1270
+ }
1271
+ continue;
1272
+ }
1273
+ if (mark.strategy === "held") {
1274
+ if (!islands_by_id.has(iid)) islands_by_id.set(iid, make_region_binding({
1275
+ iid,
1276
+ componentPath,
1277
+ entryPath,
1278
+ hostPath: origin,
1279
+ moduleUrl: ctx.dev ? ctx.devUrlFor(entryPath) : islandPublicUrl(iid, ctx.appDir),
1280
+ exportName,
1281
+ identity
1282
+ }));
1283
+ if (!rewritten_import_nodes.has(info.node)) {
1284
+ const specs = info.node.specifiers ?? [];
1285
+ if (specs.length !== 1 || specs[0].type !== "ImportDefaultSpecifier") throw this.#err(local, `held-region imports must be a default import (\`import X from '…' with { ${import_keys.region}: 'raw' } \`).`);
1286
+ s.overwrite(info.node.start, info.node.end, `import ${local} from ${JSON.stringify(regionBindingVirtualId(iid))};`);
1287
+ rewritten_import_nodes.add(info.node);
1288
+ }
1289
+ continue;
1290
+ }
1291
+ const is_server = mark.strategy === "server";
1292
+ const deferred_hydrate = is_server && !!mark.options?.hydrate;
1293
+ const wants_attach = !is_server;
1294
+ if (!islands_by_id.has(iid)) islands_by_id.set(iid, is_server ? {
1295
+ id: iid,
1296
+ virtualPath: entryPath,
1297
+ wrapperPath: wrapPath,
1298
+ wrapperSource: server_wrapper_source(iid, componentPath, entryPath, mark.options, exportName, ctx.dev ? ctx.devUrlFor(entryPath) : islandPublicUrl(iid, ctx.appDir), lang),
1299
+ wrapperClientSource: server_wrapper_source(iid, componentPath, entryPath, mark.options, exportName, ctx.dev ? ctx.devUrlFor(entryPath) : islandPublicUrl(iid, ctx.appDir), lang, false),
1300
+ source: island_entry_source(componentPath, iid, exportName),
1301
+ hostPath: origin,
1302
+ componentPath,
1303
+ server: true,
1304
+ kind: deferred_hydrate ? "hydrate" : "defer",
1305
+ lakes: [],
1306
+ identity,
1307
+ strategy: mark.strategy,
1308
+ fetchWhen: mark.options?.when,
1309
+ wakeAfter: deferred_hydrate ? mark.options?.hydrate : void 0,
1310
+ keep: mark.options?.keep
1311
+ } : make_wake_island({
1312
+ iid,
1313
+ componentPath,
1314
+ entryPath,
1315
+ wrapperPath: wrapPath,
1316
+ moduleUrl: ctx.dev ? ctx.devUrlFor(entryPath) : islandPublicUrl(iid, ctx.appDir),
1317
+ strategy: mark.strategy,
1318
+ options: mark.options,
1319
+ exportName,
1320
+ hostPath: origin,
1321
+ identity,
1322
+ lang
1323
+ }));
1324
+ if (!rewritten_import_nodes.has(info.node)) {
1325
+ const specs = info.node.specifiers ?? [];
1326
+ if (specs.length !== 1 || specs[0].type !== "ImportDefaultSpecifier") throw this.#err(local, `region imports must be a default import (\`import X from '…' with { … }\`).`);
1327
+ const rewrite_path = wants_attach && link_virtual ? regionBindingVirtualId(iid) : bindingPath;
1328
+ s.overwrite(info.node.start, info.node.end, binding_rewrite(local, rewrite_path, componentPath));
1329
+ rewritten_import_nodes.add(info.node);
1330
+ }
1331
+ }
1332
+ const as_region_locals = new Set(as_regions.map((r) => r.compLocal));
1333
+ if (as_region_locals.size) {
1334
+ const referenced_outside = (local) => {
1335
+ for (const n of instance_body) {
1336
+ if (n.type === "ImportDeclaration" || as_region_nodes.has(n)) continue;
1337
+ if (this.#ast_refs_local(n, local)) return true;
1338
+ }
1339
+ for (const n of module_body) {
1340
+ if (n.type === "ImportDeclaration") continue;
1341
+ if (this.#ast_refs_local(n, local)) return true;
1342
+ }
1343
+ return this.#ast_refs_local(ast.fragment?.nodes ?? [], local);
1344
+ };
1345
+ const seen = /* @__PURE__ */ new Set();
1346
+ for (const compLocal of as_region_locals) {
1347
+ const info = imports.get(compLocal);
1348
+ if (!info || seen.has(info.node)) continue;
1349
+ seen.add(info.node);
1350
+ if ((info.node.specifiers ?? []).every((sp) => as_region_locals.has(sp.local.name) && !referenced_outside(sp.local.name)) && !rewritten_import_nodes.has(info.node)) imports_to_strip.add(info.node);
1351
+ }
1352
+ }
1353
+ for (const node of imports_to_strip) {
1354
+ if (rewritten_import_nodes.has(node)) continue;
1355
+ s.remove(node.start, node.end);
1356
+ }
1357
+ const OG_PORTABLE = "__og_portable";
1358
+ const portable_candidates = [];
1359
+ const portable_allowed = !id.includes("node_modules");
1360
+ const walk_portable = (nodes) => {
1361
+ for (const node of nodes ?? []) {
1362
+ if (is_component_node(node)) {
1363
+ const base_name = String(node.name || "").split(".")[0];
1364
+ const mark = marked_components.get(base_name);
1365
+ const island_site = !!mark && ![
1366
+ "lake",
1367
+ "server",
1368
+ "held"
1369
+ ].includes(mark.strategy);
1370
+ const from_generated = String(imports.get(base_name)?.node?.source?.value ?? "").startsWith("virtual:ogygia/");
1371
+ for (const child of node.fragment?.nodes ?? []) {
1372
+ if (child.type !== "SnippetBlock" || !child.expression?.name) continue;
1373
+ const zero_arg = (child.parameters?.length ?? 0) === 0;
1374
+ if (island_site || !mark && zero_arg && !from_generated) portable_candidates.push({
1375
+ comp: node,
1376
+ snip: child
1377
+ });
1378
+ }
1379
+ }
1380
+ for (const k of CHILD_KEYS) if (node[k]?.nodes) walk_portable(node[k].nodes);
1381
+ }
1382
+ };
1383
+ if (portable_allowed) walk_portable(ast.fragment?.nodes ?? []);
1384
+ const outer_candidates = portable_candidates.filter((c) => !portable_candidates.some((o) => o !== c && c.snip.start >= o.snip.start && c.snip.end <= o.snip.end));
1385
+ const block_bound = /* @__PURE__ */ new Set();
1386
+ const bind_pattern = (n) => {
1387
+ if (!n || typeof n !== "object") return;
1388
+ if (Array.isArray(n)) return n.forEach(bind_pattern);
1389
+ if (n.type === "Identifier" && n.name) block_bound.add(n.name);
1390
+ for (const k in n) {
1391
+ if (k === "type" || k === "start" || k === "end" || k === "loc") continue;
1392
+ const v = n[k];
1393
+ if (v && typeof v === "object") bind_pattern(v);
1394
+ }
1395
+ };
1396
+ const walk_bound = (nodes, root) => {
1397
+ for (const node of nodes ?? []) {
1398
+ if (node.type === "EachBlock") {
1399
+ bind_pattern(node.context);
1400
+ if (node.index) block_bound.add(node.index);
1401
+ } else if (node.type === "AwaitBlock") {
1402
+ bind_pattern(node.value);
1403
+ bind_pattern(node.error);
1404
+ } else if (node.type === "ConstTag") bind_pattern(node.declaration?.declarations?.map((d) => d.id));
1405
+ else if (node.type === "SnippetBlock") {
1406
+ bind_pattern(node.parameters);
1407
+ if (!root && node.expression?.name) block_bound.add(node.expression.name);
1408
+ }
1409
+ for (const a of node.attributes ?? []) {
1410
+ if (a.type !== "LetDirective") continue;
1411
+ if (a.expression) bind_pattern(a.expression);
1412
+ else if (a.name) block_bound.add(a.name);
1413
+ }
1414
+ for (const k of CHILD_KEYS) if (node[k]?.nodes) walk_bound(node[k].nodes, false);
1415
+ }
1416
+ };
1417
+ walk_bound(ast.fragment?.nodes ?? [], true);
1418
+ const root_snippets = /* @__PURE__ */ new Map();
1419
+ for (const n of ast.fragment?.nodes ?? []) if (n.type === "SnippetBlock" && n.expression?.name) root_snippets.set(n.expression.name, n);
1420
+ let portable_emitted = false;
1421
+ const portable_imports = [];
1422
+ const portable_seen = /* @__PURE__ */ new Set();
1423
+ let native_seq = 0;
1424
+ const host_style_src = ast.css ? source.slice(ast.css.start, ast.css.end) : "";
1425
+ for (const { comp, snip } of outer_candidates) {
1426
+ const name = snip.expression.name;
1427
+ const body = snip.body?.nodes ?? [];
1428
+ if (!body.length) continue;
1429
+ const snip_params = snip.parameters ?? [];
1430
+ const params_src = snip_params.length ? source.slice(snip_params[0].start, snip_params[snip_params.length - 1].end) : "";
1431
+ const param_names = /* @__PURE__ */ new Set();
1432
+ const collect_param_names = (n) => {
1433
+ if (!n || typeof n !== "object") return;
1434
+ if (Array.isArray(n)) return n.forEach(collect_param_names);
1435
+ if (n.type === "Identifier" && n.name) param_names.add(n.name);
1436
+ for (const k in n) {
1437
+ if (k === "type" || k === "start" || k === "end") continue;
1438
+ const v = n[k];
1439
+ if (v && typeof v === "object") collect_param_names(v);
1440
+ }
1441
+ };
1442
+ collect_param_names(snip_params);
1443
+ const { free, mutated, stores } = collectCaptureInfo(body);
1444
+ if ([...free].some((nm) => block_bound.has(nm) && !param_names.has(nm))) continue;
1445
+ if ([...mutated].some((m) => !param_names.has(m))) continue;
1446
+ const carried = [];
1447
+ const pending = [...free].filter((nm) => root_snippets.has(nm) && !param_names.has(nm));
1448
+ const carried_names = /* @__PURE__ */ new Set();
1449
+ let carry_blocked = false;
1450
+ while (pending.length && !carry_blocked) {
1451
+ const nm = pending.pop();
1452
+ if (carried_names.has(nm)) continue;
1453
+ carried_names.add(nm);
1454
+ const node = root_snippets.get(nm);
1455
+ const info = collectCaptureInfo([node]);
1456
+ if (info.mutated.size || info.stores.size) carry_blocked = true;
1457
+ carried.push(node);
1458
+ for (const f of info.free) if (root_snippets.has(f)) pending.push(f);
1459
+ else free.add(f);
1460
+ }
1461
+ if (carry_blocked) continue;
1462
+ for (const nm of carried_names) free.delete(nm);
1463
+ const store_captures = [];
1464
+ const store_rewrites = [];
1465
+ for (const [nm, sites] of stores) {
1466
+ if (nm.startsWith("$$")) throw new Error(`[ogygia] ${rel_host}: {#snippet ${name}} crosses an island boundary but reads \`${nm}\` — the host's $$-props cannot cross. Read the value into a plain host variable outside the snippet and use that instead.`);
1467
+ const base = nm.slice(1);
1468
+ const prop = `__og_sub_${base}`;
1469
+ store_captures.push({
1470
+ prop,
1471
+ expr: nm
1472
+ });
1473
+ for (const p of sites) store_rewrites.push({
1474
+ start: p.start,
1475
+ end: p.end,
1476
+ prop
1477
+ });
1478
+ const trace = sites.map((p) => `${rel_host}:${line_col(source, p.start)}`).join(", ");
1479
+ if (host_declared.has(base) || imports.has(base)) warn_once(`${id}\0${name}\0${nm}\0frozen`, `[ogygia] ${rel_host}: {#snippet ${name}} crosses an island boundary and reads \`${nm}\` (at ${trace}) — captured as a FROZEN snapshot of the store's value at render time; later store updates never reach the island. To make the snapshot explicit (and silence this), read it at host scope outside the snippet — e.g. \`const ${base}_value = ${nm};\` — and use that.`);
1480
+ else warn_once(`${id}\0${name}\0${nm}\0unresolved`, `[ogygia] ${rel_host}: {#snippet ${name}} reads \`${nm}\` (at ${trace}), but \`${base}\` is not a host declaration or import, so ogygia cannot verify it is a store. Its value is captured at the host anyway — if the build now errors in ${rel_host}, start there.`);
1481
+ }
1482
+ const cleaned_imports = [];
1483
+ const seen_imports = /* @__PURE__ */ new Set();
1484
+ const captures = [];
1485
+ for (const nm of free) {
1486
+ if (nm.startsWith("$")) continue;
1487
+ if (param_names.has(nm)) continue;
1488
+ if (imports.has(nm)) {
1489
+ const info = imports.get(nm);
1490
+ const text = marked_components.has(nm) ? source.slice(info.node.start, info.node.end).trim() : info.cleaned.trim();
1491
+ if (!seen_imports.has(text)) {
1492
+ seen_imports.add(text);
1493
+ cleaned_imports.push(text);
1494
+ }
1495
+ } else if (host_declared.has(nm)) {
1496
+ if (store_like.has(nm)) warn_once(`${id}\0${name}\0${nm}\0object`, `[ogygia] ${rel_host}: {#snippet ${name}} crosses an island boundary and captures the store \`${nm}\` as an OBJECT — a host-declared store cannot cross (its functions don't serialize). Use its value (\`$${nm}\`) inside the snippet, or share live state with a wired class instead.`);
1497
+ captures.push(nm);
1498
+ } else if (module_imports.has(nm)) {
1499
+ const text = module_imports.get(nm).trim();
1500
+ if (!seen_imports.has(text)) {
1501
+ seen_imports.add(text);
1502
+ cleaned_imports.push(text);
1503
+ }
1504
+ }
1505
+ }
1506
+ const body_start = body[0].start;
1507
+ const body_end = body[body.length - 1].end;
1508
+ let markup;
1509
+ if (store_rewrites.length) {
1510
+ store_rewrites.sort((a, b) => a.start - b.start);
1511
+ let out = "";
1512
+ let cursor = body_start;
1513
+ for (const r of store_rewrites) {
1514
+ out += source.slice(cursor, r.start) + r.prop;
1515
+ cursor = r.end;
1516
+ }
1517
+ markup = out + source.slice(cursor, body_end);
1518
+ } else markup = source.slice(body_start, body_end);
1519
+ const carried_src = carried.map((n) => source.slice(n.start, n.end)).join("\n");
1520
+ const hash = createHash("md5").update(`${markup}\0${captures.join(",")}\0${cleaned_imports.join("\n")}\0${params_src}\0${carried_src}`).digest("hex").slice(0, 12);
1521
+ const identity = regionIdentity(`${this.#component_identity(this.#id)}\0psnip:${hash}`, { strategy: "hydrate" });
1522
+ const iid = regionId(identity, salt);
1523
+ const entryPath = ctx.virtualPathFor(id, iid).replace(JS_EXT, ".svelte");
1524
+ if (!islands_by_id.has(iid)) {
1525
+ const store_props = store_captures.map((c) => c.prop);
1526
+ const prop_names = snip_params.length ? [
1527
+ "__ogArgs = []",
1528
+ ...captures,
1529
+ ...store_props
1530
+ ] : [...captures, ...store_props];
1531
+ const wrapped = body.some((n) => n.type === "ConstTag");
1532
+ const template = (carried_src ? carried_src + "\n" : "") + (wrapped ? `{#snippet __og_body()}\n${markup}\n{/snippet}\n{@render __og_body()}` : markup);
1533
+ const synth = `<script${lang}>\n\timport 'virtual:ogygia/transportables-eager';\n` + cleaned_imports.map((imp) => `\t${imp}\n`).join("") + (prop_names.length ? `\t// svelte-ignore state_referenced_locally\n\tlet { ${prop_names.join(", ")} } = $props();\n` : "") + (snip_params.length ? `\t// svelte-ignore state_referenced_locally\n\tconst [${params_src}] = __ogArgs;\n` : "") + `<\/script>\n` + template + "\n" + (host_style_src ? host_style_src + "\n" : "");
1534
+ islands_by_id.set(iid, {
1535
+ id: iid,
1536
+ virtualPath: entryPath,
1537
+ wrapperPath: wrapperPathFor(id, iid),
1538
+ wrapperSource: "",
1539
+ source: synth,
1540
+ hostPath: origin,
1541
+ componentPath: entryPath,
1542
+ server: false,
1543
+ kind: "hydrate",
1544
+ lakes: [],
1545
+ identity,
1546
+ strategy: "hydrate",
1547
+ portable: true
1548
+ });
1549
+ }
1550
+ const url = ctx.dev ? ctx.devUrlFor(entryPath) : islandPublicUrl(iid, ctx.appDir);
1551
+ const cap_entries = [...captures, ...store_captures.map((c) => `${c.prop}: ${c.expr}`)];
1552
+ const cap_obj = cap_entries.length ? `{ ${cap_entries.join(", ")} }` : "{}";
1553
+ const entry_ref = ctx.ssr ? `__OgPS_${iid}` : "null";
1554
+ if (!portable_seen.has(iid)) {
1555
+ portable_seen.add(iid);
1556
+ if (ctx.ssr) portable_imports.push(`import __OgPS_${iid} from ${JSON.stringify(entryPath)};`);
1557
+ }
1558
+ const native_name = `__og_native_${name}_${native_seq++}`;
1559
+ s.move(snip.start, snip.end, source.length);
1560
+ s.overwrite(snip.expression.start, snip.expression.end, native_name);
1561
+ const insert_at = comp.start + 1 + String(comp.name).length;
1562
+ s.appendLeft(insert_at, ` ${name}={${OG_PORTABLE}(${native_name}, ${entry_ref}, ${cap_obj}, ${JSON.stringify(url)})}`);
1563
+ portable_emitted = true;
1564
+ }
1565
+ {
1566
+ let head = "";
1567
+ if (portable_emitted) head += `import { og_portable as ${OG_PORTABLE} } from 'ogygia/internal';\n` + portable_imports.join("\n") + "\n";
1568
+ if (head) {
1569
+ if (ast.instance) s.appendLeft(ast.instance.content.start, `\n${head}`);
1570
+ else s.prepend(`<script${lang}>\n${head}<\/script>\n`);
1571
+ }
1572
+ }
1573
+ if (!has_island_hint && !portable_emitted && islands_by_id.size === 0) return null;
1574
+ const has_dynamic_island_use = [...marked_components].some(([name, m]) => m.strategy !== "server" && m.strategy !== "held" && m.strategy !== "lake" && !this.#static_placed.has(name));
1575
+ return {
1576
+ code: s.toString(),
1577
+ map: s.generateMap({
1578
+ hires: true,
1579
+ source: id,
1580
+ includeContent: true
1581
+ }),
1582
+ islands: [...islands_by_id.values()],
1583
+ hasWireCrossing: has_island_children || has_dynamic_island_use || portable_emitted
1584
+ };
1585
+ }
1586
+ };
1587
+ /** Parse a flat import-attributes clause body (`a: 'x', b: "y"`) into a key→value map. */
1588
+ /**
1589
+ * Parse the flat body of an import-attributes clause (`with { a: 'x', b: "y" }`) into a map.
1590
+ * Values must be string literals (the only form ogygia marks use).
1591
+ * @param raw the text between the `with {` and `}`
1592
+ * @returns
1593
+ */
1594
+ function parse_import_attrs(raw) {
1595
+ const attrs = /* @__PURE__ */ new Map();
1596
+ IMPORT_ATTR_PAIR_G.lastIndex = 0;
1597
+ let m;
1598
+ while (m = IMPORT_ATTR_PAIR_G.exec(raw)) {
1599
+ const key = m[1].replace(WRAP_QUOTES, "");
1600
+ const val = m[2].slice(1, -1);
1601
+ attrs.set(key, val);
1602
+ }
1603
+ return attrs;
1604
+ }
1605
+ /**
1606
+ * Rewrite held-region imports in a `.ts` / `.js` module (a load or remote function, where held
1607
+ * regions are minted and handed to `region()`). Svelte's `parse()` can't read these files, so this is
1608
+ * a targeted regex pass over the (only supported) default-import form. A `.ts` module has no template,
1609
+ * so every marked import here is HELD → a descriptor. Two markers:
1610
+ * - `region: 'raw'` → bakes no schedule (set at the `region()` call).
1611
+ * - `wake: 'x'` → bakes that schedule (the `region()` call can still override).
1612
+ * Each registers a server-manifest entry + client chunk and is rewritten to import the leg-split
1613
+ * descriptor module. Returns the same `{code, map, islands}` shape, or `null` when nothing matched.
1614
+ *
1615
+ * @param source
1616
+ * @param id absolute module path
1617
+ * @param ctx same context fields as {@link transformHost} (root, libDir, dev, devUrlFor,
1618
+ * virtualPathFor, importKeys, idSalt, pathModule)
1619
+ * @returns
1620
+ */
1621
+ /** Byte ranges of every template/string literal AND comment in a `.ts`/`.js` source, or `null` if it
1622
+ * doesn't parse. Used to tell a REAL held-region import from one that is only TEXT inside a code
1623
+ * sample — whether that sample sits in a template/string literal or a JSDoc `@example` comment. */
1624
+ function ts_literal_ranges(source, id) {
1625
+ const { program, ok, comments } = parse_module(source, id);
1626
+ if (!ok || !program) return null;
1627
+ const ranges = [];
1628
+ const walk = (n) => {
1629
+ if (!n || typeof n !== "object") return;
1630
+ if (Array.isArray(n)) return void n.forEach(walk);
1631
+ const node = n;
1632
+ const t = node.type;
1633
+ const is_str = (t === "Literal" || t === "StringLiteral") && typeof node.value === "string";
1634
+ if ((t === "TemplateLiteral" || is_str) && typeof node.start === "number" && typeof node.end === "number") ranges.push([node.start, node.end]);
1635
+ for (const k in node) {
1636
+ if (k === "type" || k === "start" || k === "end") continue;
1637
+ const v = node[k];
1638
+ if (v && typeof v === "object") walk(v);
1639
+ }
1640
+ };
1641
+ walk(program);
1642
+ for (const c of comments) if (typeof c.start === "number" && typeof c.end === "number") ranges.push([c.start, c.end]);
1643
+ return ranges;
1644
+ }
1645
+ /** Fallback for {@link ts_literal_ranges} when the file doesn't parse: is `pos` preceded by an odd
1646
+ * number of unescaped backticks (i.e. inside an unterminated template literal)? */
1647
+ function odd_unescaped_backticks_before(source, pos) {
1648
+ let backticks = 0;
1649
+ for (let i = 0; i < pos; i++) if (source[i] === "`" && source[i - 1] !== "\\") backticks++;
1650
+ return backticks % 2 === 1;
1651
+ }
1652
+ function transformTsRegions(source, id, ctx) {
1653
+ return new TsRegionCompilation(source, id, ctx).run();
1654
+ }
1655
+ /**
1656
+ * `TsRegionCompilation` — the per-file compilation unit for a `.ts`/`.js` region module (a held
1657
+ * registry / remote). A `.ts` module has no template, so every mark mints a HELD binding (mountable +
1658
+ * crossable) through the SAME emitters the `.svelte` host uses. Ephemeral state lives on `this`;
1659
+ * `run()` is the single pass — scan `with { … }` imports + `asRegion` call sites and rewrite each to
1660
+ * its binding import. A fresh instance per file, like {@link FileCompilation}.
1661
+ */
1662
+ var TsRegionCompilation = class {
1663
+ #source;
1664
+ #id;
1665
+ #ctx;
1666
+ #import_keys;
1667
+ #regionKey;
1668
+ #wakeKey;
1669
+ #path;
1670
+ #root;
1671
+ #salt;
1672
+ #rel_host;
1673
+ constructor(source, id, ctx) {
1674
+ this.#source = source;
1675
+ this.#id = id;
1676
+ this.#ctx = ctx;
1677
+ this.#import_keys = normalize_import_keys(ctx.importKeys);
1678
+ this.#regionKey = this.#import_keys.region;
1679
+ this.#wakeKey = this.#import_keys.wake;
1680
+ this.#path = ctx.pathModule;
1681
+ this.#root = ctx.root;
1682
+ this.#salt = ctx.idSalt || "";
1683
+ this.#rel_host = this.#path.relative(this.#root, id).split(PATH_SEP).join("/");
1684
+ }
1685
+ /** Root-relative posix path (see FileCompilation#posix_rel). */
1686
+ #posix_rel(abs) {
1687
+ return this.#path.relative(this.#root, abs).split(PATH_SEP).join("/");
1688
+ }
1689
+ /** `[ogygia] host: import.meta.og.asRegion (local) — msg`. */
1690
+ #as_err(local, msg) {
1691
+ return /* @__PURE__ */ new Error(`[ogygia] ${this.#rel_host}: import.meta.og.asRegion (${local}) — ${msg}`);
1692
+ }
1693
+ /**
1694
+ * Same policy as transformHost's resolve_component_path: $lib/relative → absolute file path,
1695
+ * anything else (package specifier / alias) is kept verbatim and re-emitted for Vite to resolve.
1696
+ */
1697
+ #resolve_spec(spec) {
1698
+ const ctx = this.#ctx;
1699
+ const path = this.#path;
1700
+ if (typeof spec !== "string" || !spec.trim()) return null;
1701
+ if (spec === "$lib" || spec.startsWith("$lib/")) return path.join(ctx.libDir, spec === "$lib" ? "" : spec.slice(5));
1702
+ if (spec.startsWith(".")) return path.resolve(path.dirname(this.#id), spec);
1703
+ return spec;
1704
+ }
1705
+ /** Region-identity path — filesystem components key on their posix path; a package spec is its
1706
+ * own. Declared-package files key `<pkg-name>/<rel>` (install-independent — see
1707
+ * FileCompilation#component_identity); duck-typed like wrapperPathFor, host ctxs may lack it. */
1708
+ #component_identity(p) {
1709
+ if (!this.#path.isAbsolute(p)) return p;
1710
+ const ctx = this.#ctx;
1711
+ return (typeof ctx.pkg_identity === "function" ? ctx.pkg_identity(p) : null) ?? this.#posix_rel(p);
1712
+ }
1713
+ #wrapper_path_for(wid) {
1714
+ const ctx = this.#ctx;
1715
+ return typeof ctx.wrapperPathFor === "function" ? ctx.wrapperPathFor(this.#id, wid) : wrapperVirtualId(wid);
1716
+ }
1717
+ #emit_ts_region(componentPath, marker, exportName) {
1718
+ const comp_rel = this.#component_identity(componentPath);
1719
+ const id_base = exportName && exportName !== "default" ? `${comp_rel}#${exportName}` : comp_rel;
1720
+ const fetchKey = this.#import_keys.fetch;
1721
+ const renderKey = this.#import_keys.render;
1722
+ if (marker.fetch != null && marker.render == null) throw new Error(`[ogygia] ${this.#rel_host}: \`${fetchKey}: '${marker.fetch}'\` without \`${renderKey}: 'deferred'\` — \`${fetchKey}\` is a hole's timing. Write \`${renderKey}: 'deferred', ${fetchKey}: '${marker.fetch}'\` for a hole, or \`${this.#wakeKey}: '${marker.fetch}'\` for an island.`);
1723
+ if (marker.render != null) {
1724
+ if (marker.render !== "deferred") throw new Error(marker.render === "live" ? `[ogygia] ${this.#rel_host}: \`${renderKey}: 'live'\` is not supported on a .ts import — use it in a .svelte file.` : `[ogygia] ${this.#rel_host}: unknown \`${renderKey}\` '${marker.render}'. Use 'deferred'.`);
1725
+ if (marker.wake != null) throw new Error(`[ogygia] ${this.#rel_host}: a \`${renderKey}: 'deferred'\` hole timed with \`${this.#wakeKey}: '${marker.wake}'\` — that spelling is gone. Write \`${renderKey}: 'deferred', ${fetchKey}: '${marker.wake}'\` instead: \`${fetchKey}\` says when a hole's HTML arrives. (\`${this.#wakeKey}\` is when an island's JS runs; a hole ships no JS.)`);
1726
+ const dval = marker.fetch ?? "load";
1727
+ let when;
1728
+ if (DEFER_STRATEGIES.has(dval)) when = dval;
1729
+ else if (is_media_query(dval)) when = dval;
1730
+ else throw new Error(`[ogygia] ${this.#rel_host}: \`${fetchKey}: '${dval}'\` is not a timing. Use \`${fetchKey}: 'load' | 'idle' | 'visible' | 'interaction'\` or a media query (a hole must fetch — not 'none').`);
1731
+ const options = { when };
1732
+ if (when === "visible" && this.#ctx.visibleMargin != null) options.margin = this.#ctx.visibleMargin;
1733
+ const identity = regionIdentity(id_base, {
1734
+ strategy: "server",
1735
+ options
1736
+ });
1737
+ const iid = regionId(identity, this.#salt);
1738
+ const entryPath = this.#ctx.virtualPathFor(this.#id, iid);
1739
+ const wrapPath = this.#wrapper_path_for(iid);
1740
+ return {
1741
+ iid,
1742
+ importFrom: wrapPath,
1743
+ record: {
1744
+ id: iid,
1745
+ virtualPath: entryPath,
1746
+ wrapperPath: wrapPath,
1747
+ wrapperSource: server_wrapper_source(iid, componentPath, entryPath, options, exportName, this.#ctx.dev ? this.#ctx.devUrlFor(entryPath) : islandPublicUrl(iid, this.#ctx.appDir), ""),
1748
+ wrapperClientSource: server_wrapper_source(iid, componentPath, entryPath, options, exportName, this.#ctx.dev ? this.#ctx.devUrlFor(entryPath) : islandPublicUrl(iid, this.#ctx.appDir), "", false),
1749
+ source: island_entry_source(componentPath, iid, exportName),
1750
+ hostPath: this.#id,
1751
+ componentPath,
1752
+ server: true,
1753
+ kind: "defer",
1754
+ lakes: [],
1755
+ identity,
1756
+ strategy: "server",
1757
+ fetchWhen: options.when
1758
+ }
1759
+ };
1760
+ }
1761
+ if (marker.region != null) {
1762
+ normalize_region_value(marker.region, this.#rel_host, this.#regionKey);
1763
+ const identity = regionIdentity(id_base, {
1764
+ strategy: "held",
1765
+ options: {}
1766
+ });
1767
+ const iid = regionId(identity, this.#salt);
1768
+ const entryPath = this.#ctx.virtualPathFor(this.#id, iid);
1769
+ return {
1770
+ iid,
1771
+ importFrom: regionBindingVirtualId(iid),
1772
+ record: make_region_binding({
1773
+ iid,
1774
+ componentPath,
1775
+ entryPath,
1776
+ hostPath: this.#id,
1777
+ moduleUrl: this.#ctx.dev ? this.#ctx.devUrlFor(entryPath) : islandPublicUrl(iid, this.#ctx.appDir),
1778
+ exportName,
1779
+ identity
1780
+ })
1781
+ };
1782
+ }
1783
+ const strategy = normalize_hydrate_value(marker.wake, this.#rel_host, this.#wakeKey);
1784
+ const hydrateMargin = strategy === "visible" && this.#ctx.visibleMargin != null ? this.#ctx.visibleMargin : void 0;
1785
+ const identity = regionIdentity(id_base, {
1786
+ strategy: "held",
1787
+ options: hydrateMargin ? {
1788
+ hydrate: strategy,
1789
+ hydrateMargin
1790
+ } : { hydrate: strategy }
1791
+ });
1792
+ const iid = regionId(identity, this.#salt);
1793
+ const entryPath = this.#ctx.virtualPathFor(this.#id, iid);
1794
+ return {
1795
+ iid,
1796
+ importFrom: regionBindingVirtualId(iid),
1797
+ record: make_wake_island({
1798
+ iid,
1799
+ componentPath,
1800
+ entryPath,
1801
+ wrapperPath: this.#wrapper_path_for(iid),
1802
+ moduleUrl: this.#ctx.dev ? this.#ctx.devUrlFor(entryPath) : islandPublicUrl(iid, this.#ctx.appDir),
1803
+ strategy,
1804
+ options: hydrateMargin ? { margin: hydrateMargin } : {},
1805
+ hostPath: this.#id,
1806
+ identity,
1807
+ lang: "",
1808
+ held: true,
1809
+ exportName
1810
+ })
1811
+ };
1812
+ }
1813
+ run() {
1814
+ const source = this.#source;
1815
+ const id = this.#id;
1816
+ const import_keys = this.#import_keys;
1817
+ const regionKey = this.#regionKey;
1818
+ const wakeKey = this.#wakeKey;
1819
+ const renderKey = import_keys.render;
1820
+ const fetchKey = import_keys.fetch;
1821
+ const has_as_region = source.includes("asRegion");
1822
+ if (!has_as_region && (!source.includes(regionKey) && !source.includes(wakeKey) && !source.includes(fetchKey) && !source.includes(renderKey) || !source.includes("with"))) return null;
1823
+ const rel_host = this.#rel_host;
1824
+ const s = new MagicString(source);
1825
+ const islands_by_id = /* @__PURE__ */ new Map();
1826
+ let matched = false;
1827
+ const literal_ranges = ts_literal_ranges(source, id);
1828
+ const inside_literal = (pos) => literal_ranges ? literal_ranges.some(([a, b]) => pos >= a && pos < b) : odd_unescaped_backticks_before(source, pos);
1829
+ HELD_IMPORT_G.lastIndex = 0;
1830
+ let m;
1831
+ while (m = HELD_IMPORT_G.exec(source)) {
1832
+ const [full, local, , spec, attrsRaw] = m;
1833
+ const attrs = parse_import_attrs(attrsRaw);
1834
+ const has_render = attrs.has(renderKey);
1835
+ const has_region = attrs.has(regionKey);
1836
+ const has_wake = attrs.has(wakeKey);
1837
+ const has_fetch = attrs.has(fetchKey);
1838
+ if (!has_render && !has_region && !has_wake && !has_fetch) continue;
1839
+ if (inside_literal(m.index)) continue;
1840
+ if (has_region && attrs.size > 1 || has_render && [...attrs.keys()].some((k) => k !== renderKey && k !== wakeKey && k !== fetchKey) || !has_render && has_wake && attrs.size > 1) throw new Error(`[ogygia] ${rel_host}: a held-region import on '${local}' takes ONE marker — \`${regionKey}: 'raw'\`, \`${wakeKey}: '…'\`, or \`${renderKey}: 'deferred'\` (with \`${fetchKey}\` as its timing).`);
1841
+ const componentPath = this.#resolve_spec(spec);
1842
+ if (!componentPath) throw new Error(`[ogygia] ${rel_host}: held-region import '${local}' needs a module specifier ($lib/…, relative, or a package specifier like 'pkg/component').`);
1843
+ const marker = has_render ? {
1844
+ render: attrs.get(renderKey),
1845
+ wake: attrs.get(wakeKey),
1846
+ fetch: attrs.get(fetchKey)
1847
+ } : has_region ? { region: attrs.get(regionKey) } : has_fetch ? { fetch: attrs.get(fetchKey) } : { wake: attrs.get(wakeKey) };
1848
+ const { iid, record, importFrom } = this.#emit_ts_region(componentPath, marker);
1849
+ if (!islands_by_id.has(iid)) islands_by_id.set(iid, record);
1850
+ s.overwrite(m.index, m.index + full.length, `import ${local} from ${JSON.stringify(importFrom)};`);
1851
+ matched = true;
1852
+ }
1853
+ if (has_as_region) {
1854
+ const { program: as_program } = parse_module(source, id);
1855
+ if (as_program) {
1856
+ const body = as_program.body ?? [];
1857
+ const import_of = /* @__PURE__ */ new Map();
1858
+ for (const node of body) {
1859
+ if (node.type !== "ImportDeclaration") continue;
1860
+ for (const spec of node.specifiers ?? []) if (spec.local?.name) import_of.set(spec.local.name, node);
1861
+ }
1862
+ const legal_nodes = /* @__PURE__ */ new Set();
1863
+ for (const node of body) {
1864
+ if (node.type !== "VariableDeclaration") continue;
1865
+ if (node.declarations.some((d) => d.init?.type === "CallExpression" && is_import_meta_og(d.init.callee, "asRegion"))) legal_nodes.add(node);
1866
+ }
1867
+ const find_stray = (n) => {
1868
+ if (!n || typeof n !== "object") return null;
1869
+ if (Array.isArray(n)) {
1870
+ for (const x of n) {
1871
+ const hit = find_stray(x);
1872
+ if (hit) return hit;
1873
+ }
1874
+ return null;
1875
+ }
1876
+ if (n.type === "CallExpression" && is_import_meta_og(n.callee, "asRegion")) return n;
1877
+ for (const k in n) {
1878
+ if (k === "type" || k === "start" || k === "end" || k === "loc" || k === "parent") continue;
1879
+ const v = n[k];
1880
+ if (v && typeof v === "object") {
1881
+ const hit = find_stray(v);
1882
+ if (hit) return hit;
1883
+ }
1884
+ }
1885
+ return null;
1886
+ };
1887
+ for (const node of body) {
1888
+ if (legal_nodes.has(node)) continue;
1889
+ const stray = find_stray(node);
1890
+ if (stray) {
1891
+ const a = stray.arguments?.[0];
1892
+ throw this.#as_err(a?.type === "Identifier" ? a.name : "?", "must be a top-level `const Name = import.meta.og.asRegion(Comp, options)` — it compiles to a hoisted import, so it can never sit in a loop, function, block, or larger expression.");
1893
+ }
1894
+ }
1895
+ for (const node of body) {
1896
+ if (!legal_nodes.has(node)) continue;
1897
+ if (node.kind !== "const") throw this.#as_err(node.declarations[0]?.id?.name ?? "?", `must be bound with \`const\` (not \`${node.kind}\`).`);
1898
+ if (node.declarations.length !== 1) throw this.#as_err(node.declarations[0]?.id?.name ?? "?", "declare one asRegion per `const` statement.");
1899
+ const decl = node.declarations[0];
1900
+ if (decl.id?.type !== "Identifier") throw this.#as_err("?", "must bind to a plain `const Name = …`.");
1901
+ const local = decl.id.name;
1902
+ const call_args = decl.init.arguments ?? [];
1903
+ const comp_arg = call_args[0];
1904
+ if (!comp_arg || comp_arg.type !== "Identifier") throw this.#as_err(local, "the first argument must be a component you imported (a bare identifier).");
1905
+ const imp = import_of.get(comp_arg.name);
1906
+ if (!imp) throw this.#as_err(local, `'${comp_arg.name}' is not an imported component — import it first (e.g. \`import { ${comp_arg.name} } from './…'\`).`);
1907
+ const binding = import_binding_of(imp, comp_arg.name);
1908
+ if (!binding || "namespace" in binding) throw this.#as_err(local, `'${comp_arg.name}' must be a default or named import, not a namespace import.`);
1909
+ const componentPath = this.#resolve_spec(binding.source);
1910
+ if (!componentPath) throw this.#as_err(local, `could not resolve '${binding.source}'.`);
1911
+ const marker = parse_ts_as_region_options(call_args[1], (msg) => this.#as_err(local, msg), regionKey, wakeKey, renderKey, fetchKey);
1912
+ const { iid, record, importFrom } = this.#emit_ts_region(componentPath, marker, binding.exportName);
1913
+ if (!islands_by_id.has(iid)) islands_by_id.set(iid, record);
1914
+ s.overwrite(node.start, node.end, `import ${local} from ${JSON.stringify(importFrom)};`);
1915
+ matched = true;
1916
+ }
1917
+ }
1918
+ }
1919
+ if (!matched) return null;
1920
+ const islands = [...islands_by_id.values()];
1921
+ return {
1922
+ code: s.toString(),
1923
+ map: s.generateMap({
1924
+ hires: true,
1925
+ source: id,
1926
+ includeContent: true
1927
+ }),
1928
+ islands,
1929
+ hasWireCrossing: islands.some((i) => typeof i.strategy === "string" && i.strategy !== "server" && i.strategy !== "none")
1930
+ };
1931
+ }
1932
+ };
1933
+ export { CLIENT_BINDING_STUB, DEFAULT_IMPORT_KEYS, ISLAND_DIR, asRegionLocals, foucCssVirtualId, import_keys_hint, is_island_path, islandChunkFileName, islandId, islandPublicUrl, normalize_hydrate_value, normalize_import_keys, normalize_region_value, regionBindingVirtualId, regionId, regionIdentity, strategyKey, transformHost, transformTsRegions, wrapperVirtualId };