@gyral/core 0.3.0 → 0.3.1

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 (381) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/NOTICE +19 -0
  3. package/README.md +30 -5
  4. package/dist/call.d.ts +32 -0
  5. package/dist/call.js +63 -0
  6. package/dist/call.js.map +1 -0
  7. package/dist/children.d.ts +25 -3
  8. package/dist/children.js +18 -1
  9. package/dist/children.js.map +1 -1
  10. package/dist/cli/gyral-types.d.ts +2 -0
  11. package/dist/cli/gyral-types.js +23 -0
  12. package/dist/cli/gyral-types.js.map +1 -0
  13. package/dist/command.d.ts +54 -12
  14. package/dist/command.js +25 -11
  15. package/dist/command.js.map +1 -1
  16. package/dist/compiler/ast.d.ts +2 -0
  17. package/dist/compiler/ast.js +5 -1
  18. package/dist/compiler/ast.js.map +1 -1
  19. package/dist/compiler/compile.d.ts +5 -2
  20. package/dist/compiler/compile.js +36 -17
  21. package/dist/compiler/compile.js.map +1 -1
  22. package/dist/compiler/component-entries.d.ts +7 -0
  23. package/dist/compiler/component-entries.js +42 -0
  24. package/dist/compiler/component-entries.js.map +1 -0
  25. package/dist/compiler/components.d.ts +4 -0
  26. package/dist/compiler/components.js +182 -0
  27. package/dist/compiler/components.js.map +1 -0
  28. package/dist/compiler/facts.d.ts +31 -0
  29. package/dist/compiler/facts.js +285 -0
  30. package/dist/compiler/facts.js.map +1 -0
  31. package/dist/compiler/features.d.ts +34 -0
  32. package/dist/compiler/features.js +158 -0
  33. package/dist/compiler/features.js.map +1 -0
  34. package/dist/compiler/hooks.d.ts +4 -0
  35. package/dist/compiler/hooks.js +144 -89
  36. package/dist/compiler/hooks.js.map +1 -1
  37. package/dist/compiler/loader-source.d.ts +14 -0
  38. package/dist/compiler/loader-source.js +86 -0
  39. package/dist/compiler/loader-source.js.map +1 -0
  40. package/dist/compiler/locate.d.ts +14 -0
  41. package/dist/compiler/locate.js +78 -0
  42. package/dist/compiler/locate.js.map +1 -0
  43. package/dist/compiler/parse5-check.d.ts +1 -1
  44. package/dist/compiler/parse5-check.js +8 -3
  45. package/dist/compiler/parse5-check.js.map +1 -1
  46. package/dist/compiler/prop-schemas.d.ts +4 -0
  47. package/dist/compiler/prop-schemas.js +108 -0
  48. package/dist/compiler/prop-schemas.js.map +1 -0
  49. package/dist/compiler/scan.d.ts +13 -3
  50. package/dist/compiler/scan.js +21 -13
  51. package/dist/compiler/scan.js.map +1 -1
  52. package/dist/compiler/scope.d.ts +8 -0
  53. package/dist/compiler/scope.js +116 -0
  54. package/dist/compiler/scope.js.map +1 -0
  55. package/dist/compiler/tag-map.d.ts +41 -0
  56. package/dist/compiler/tag-map.js +126 -0
  57. package/dist/compiler/tag-map.js.map +1 -0
  58. package/dist/components-ready.d.ts +7 -0
  59. package/dist/components-ready.js +19 -0
  60. package/dist/components-ready.js.map +1 -0
  61. package/dist/define.d.ts +13 -4
  62. package/dist/define.js +8 -12
  63. package/dist/define.js.map +1 -1
  64. package/dist/devtools-events.d.ts +8 -0
  65. package/dist/devtools-events.js.map +1 -1
  66. package/dist/devtools-off.d.ts +1 -0
  67. package/dist/devtools-off.js +1 -0
  68. package/dist/devtools-off.js.map +1 -1
  69. package/dist/devtools.d.ts +3 -0
  70. package/dist/devtools.js +14 -0
  71. package/dist/devtools.js.map +1 -1
  72. package/dist/drivers-scope.js.map +1 -1
  73. package/dist/element-internals.d.ts +1 -0
  74. package/dist/element-internals.js +11 -0
  75. package/dist/element-internals.js.map +1 -0
  76. package/dist/element-types.d.ts +6 -2
  77. package/dist/element-types.js.map +1 -1
  78. package/dist/element.js +80 -27
  79. package/dist/element.js.map +1 -1
  80. package/dist/error-view.d.ts +10 -0
  81. package/dist/error-view.js +30 -0
  82. package/dist/error-view.js.map +1 -0
  83. package/dist/errors.d.ts +21 -0
  84. package/dist/errors.js +47 -0
  85. package/dist/errors.js.map +1 -0
  86. package/dist/errors.json +289 -0
  87. package/dist/eslint/imports.d.ts +2 -0
  88. package/dist/eslint/imports.js +2 -2
  89. package/dist/eslint/imports.js.map +1 -1
  90. package/dist/eslint/index.d.ts +2 -1
  91. package/dist/eslint/index.js +11 -3
  92. package/dist/eslint/index.js.map +1 -1
  93. package/dist/eslint/template-rule.d.ts +1 -0
  94. package/dist/eslint/template-rule.js +15 -5
  95. package/dist/eslint/template-rule.js.map +1 -1
  96. package/dist/eslint/unused-intent-rule.d.ts +2 -0
  97. package/dist/eslint/unused-intent-rule.js +186 -0
  98. package/dist/eslint/unused-intent-rule.js.map +1 -0
  99. package/dist/features.d.ts +2 -2
  100. package/dist/features.js.map +1 -1
  101. package/dist/focus.d.ts +6 -0
  102. package/dist/focus.js +32 -2
  103. package/dist/focus.js.map +1 -1
  104. package/dist/form-associated.d.ts +46 -0
  105. package/dist/form-associated.js +77 -0
  106. package/dist/form-associated.js.map +1 -0
  107. package/dist/forms.d.ts +42 -30
  108. package/dist/forms.js +85 -41
  109. package/dist/forms.js.map +1 -1
  110. package/dist/frame-lane.d.ts +11 -0
  111. package/dist/frame-lane.js +28 -0
  112. package/dist/frame-lane.js.map +1 -0
  113. package/dist/head.d.ts +61 -0
  114. package/dist/head.js +85 -0
  115. package/dist/head.js.map +1 -0
  116. package/dist/host-model.d.ts +34 -5
  117. package/dist/host-model.js +146 -23
  118. package/dist/host-model.js.map +1 -1
  119. package/dist/hydration-client.js +2 -2
  120. package/dist/hydration-client.js.map +1 -1
  121. package/dist/hydration-loader.d.ts +13 -0
  122. package/dist/hydration-loader.js +58 -0
  123. package/dist/hydration-loader.js.map +1 -0
  124. package/dist/hydration-off.d.ts +8 -0
  125. package/dist/hydration-off.js +25 -0
  126. package/dist/hydration-off.js.map +1 -0
  127. package/dist/hydration.d.ts +0 -12
  128. package/dist/hydration.js +3 -52
  129. package/dist/hydration.js.map +1 -1
  130. package/dist/index.d.ts +20 -24
  131. package/dist/index.js +10 -19
  132. package/dist/index.js.map +1 -1
  133. package/dist/indexeddb-types.d.ts +97 -0
  134. package/dist/indexeddb-types.js +2 -0
  135. package/dist/indexeddb-types.js.map +1 -0
  136. package/dist/indexeddb.d.ts +5 -0
  137. package/dist/indexeddb.js +118 -0
  138. package/dist/indexeddb.js.map +1 -0
  139. package/dist/intent.d.ts +41 -19
  140. package/dist/intent.js +144 -106
  141. package/dist/intent.js.map +1 -1
  142. package/dist/internal/indexeddb-open.d.ts +8 -0
  143. package/dist/internal/indexeddb-open.js +96 -0
  144. package/dist/internal/indexeddb-open.js.map +1 -0
  145. package/dist/internal/indexeddb-transaction.d.ts +4 -0
  146. package/dist/internal/indexeddb-transaction.js +134 -0
  147. package/dist/internal/indexeddb-transaction.js.map +1 -0
  148. package/dist/internal/interpreter.d.ts +12 -4
  149. package/dist/internal/interpreter.js +36 -48
  150. package/dist/internal/interpreter.js.map +1 -1
  151. package/dist/internal/token.d.ts +1 -0
  152. package/dist/internal/token.js +7 -0
  153. package/dist/internal/token.js.map +1 -0
  154. package/dist/internal.d.ts +20 -0
  155. package/dist/internal.js +22 -0
  156. package/dist/internal.js.map +1 -0
  157. package/dist/invoker-fallback-used.d.ts +4 -0
  158. package/dist/invoker-fallback-used.js +10 -0
  159. package/dist/invoker-fallback-used.js.map +1 -0
  160. package/dist/invoker-fallback.d.ts +4 -0
  161. package/dist/invoker-fallback.js +15 -0
  162. package/dist/invoker-fallback.js.map +1 -0
  163. package/dist/light-dom.d.ts +1 -1
  164. package/dist/light-dom.js.map +1 -1
  165. package/dist/prop.d.ts +30 -7
  166. package/dist/prop.js +19 -0
  167. package/dist/prop.js.map +1 -1
  168. package/dist/props.d.ts +8 -3
  169. package/dist/props.js +22 -15
  170. package/dist/props.js.map +1 -1
  171. package/dist/retry.d.ts +15 -0
  172. package/dist/retry.js +48 -0
  173. package/dist/retry.js.map +1 -0
  174. package/dist/scheduler.d.ts +7 -3
  175. package/dist/scheduler.js +38 -56
  176. package/dist/scheduler.js.map +1 -1
  177. package/dist/script-json.d.ts +2 -0
  178. package/dist/script-json.js +13 -0
  179. package/dist/script-json.js.map +1 -0
  180. package/dist/server-component.d.ts +1 -1
  181. package/dist/server-component.js +45 -14
  182. package/dist/server-component.js.map +1 -1
  183. package/dist/server.d.ts +8 -2
  184. package/dist/server.js +11 -2
  185. package/dist/server.js.map +1 -1
  186. package/dist/settled.d.ts +4 -3
  187. package/dist/settled.js +48 -26
  188. package/dist/settled.js.map +1 -1
  189. package/dist/spec-features-used.d.ts +21 -0
  190. package/dist/spec-features-used.js +50 -0
  191. package/dist/spec-features-used.js.map +1 -0
  192. package/dist/spec-features.d.ts +28 -0
  193. package/dist/spec-features.js +42 -0
  194. package/dist/spec-features.js.map +1 -0
  195. package/dist/states.d.ts +3 -5
  196. package/dist/states.js +22 -8
  197. package/dist/states.js.map +1 -1
  198. package/dist/storage.d.ts +69 -0
  199. package/dist/storage.js +158 -0
  200. package/dist/storage.js.map +1 -0
  201. package/dist/store-binding.js +2 -2
  202. package/dist/store-binding.js.map +1 -1
  203. package/dist/store-scope.d.ts +2 -4
  204. package/dist/store-scope.js +6 -16
  205. package/dist/store-scope.js.map +1 -1
  206. package/dist/store.js +36 -8
  207. package/dist/store.js.map +1 -1
  208. package/dist/subscription.d.ts +35 -0
  209. package/dist/subscription.js +74 -0
  210. package/dist/subscription.js.map +1 -0
  211. package/dist/subscriptions.d.ts +26 -0
  212. package/dist/subscriptions.js +98 -0
  213. package/dist/subscriptions.js.map +1 -0
  214. package/dist/transitions.d.ts +4 -5
  215. package/dist/transitions.js +10 -4
  216. package/dist/transitions.js.map +1 -1
  217. package/dist/types.d.ts +124 -27
  218. package/dist/types.js.map +1 -1
  219. package/dist/use-dialogs.d.ts +2 -0
  220. package/dist/use-dialogs.js +9 -0
  221. package/dist/use-dialogs.js.map +1 -0
  222. package/dist/use-disposal.d.ts +2 -0
  223. package/dist/use-disposal.js +9 -0
  224. package/dist/use-disposal.js.map +1 -0
  225. package/dist/use-error-view.d.ts +2 -0
  226. package/dist/use-error-view.js +9 -0
  227. package/dist/use-error-view.js.map +1 -0
  228. package/dist/use-errored.d.ts +2 -0
  229. package/dist/use-errored.js +9 -0
  230. package/dist/use-errored.js.map +1 -0
  231. package/dist/use-form-associated.d.ts +2 -0
  232. package/dist/use-form-associated.js +7 -0
  233. package/dist/use-form-associated.js.map +1 -0
  234. package/dist/use-frame-lane.d.ts +2 -0
  235. package/dist/use-frame-lane.js +10 -0
  236. package/dist/use-frame-lane.js.map +1 -0
  237. package/dist/use-invokers.d.ts +2 -0
  238. package/dist/use-invokers.js +10 -0
  239. package/dist/use-invokers.js.map +1 -0
  240. package/dist/use-states.d.ts +2 -0
  241. package/dist/use-states.js +10 -0
  242. package/dist/use-states.js.map +1 -0
  243. package/dist/use-subscriptions.d.ts +2 -0
  244. package/dist/use-subscriptions.js +10 -0
  245. package/dist/use-subscriptions.js.map +1 -0
  246. package/dist/use-transitions.d.ts +2 -0
  247. package/dist/use-transitions.js +10 -0
  248. package/dist/use-transitions.js.map +1 -0
  249. package/dist/view/attributes.d.ts +5 -0
  250. package/dist/view/attributes.js +5 -0
  251. package/dist/view/attributes.js.map +1 -1
  252. package/dist/view/compiled.d.ts +1 -1
  253. package/dist/view/compiled.js +1 -1
  254. package/dist/view/compiled.js.map +1 -1
  255. package/dist/view/index.d.ts +9 -4
  256. package/dist/view/index.js +9 -3
  257. package/dist/view/index.js.map +1 -1
  258. package/dist/view/loc.d.ts +9 -0
  259. package/dist/view/loc.js +50 -0
  260. package/dist/view/loc.js.map +1 -0
  261. package/dist/view/message.d.ts +17 -0
  262. package/dist/view/message.js +40 -0
  263. package/dist/view/message.js.map +1 -0
  264. package/dist/view/messages.d.ts +43 -0
  265. package/dist/view/messages.js +84 -0
  266. package/dist/view/messages.js.map +1 -0
  267. package/dist/view/normalize/check.d.ts +5 -2
  268. package/dist/view/normalize/check.js +6 -3
  269. package/dist/view/normalize/check.js.map +1 -1
  270. package/dist/view/normalize/emit.d.ts +2 -0
  271. package/dist/view/normalize/emit.js +40 -11
  272. package/dist/view/normalize/emit.js.map +1 -1
  273. package/dist/view/normalize/errors.js +1 -1
  274. package/dist/view/normalize/errors.js.map +1 -1
  275. package/dist/view/normalize/id.d.ts +5 -2
  276. package/dist/view/normalize/id.js +9 -2
  277. package/dist/view/normalize/id.js.map +1 -1
  278. package/dist/view/normalize/normalize.d.ts +7 -4
  279. package/dist/view/normalize/normalize.js +15 -9
  280. package/dist/view/normalize/normalize.js.map +1 -1
  281. package/dist/view/normalize/rules.d.ts +9 -0
  282. package/dist/view/normalize/rules.js +35 -5
  283. package/dist/view/normalize/rules.js.map +1 -1
  284. package/dist/view/normalize/svg.d.ts +14 -0
  285. package/dist/view/normalize/svg.js +40 -0
  286. package/dist/view/normalize/svg.js.map +1 -0
  287. package/dist/view/normalize/tokens.d.ts +5 -1
  288. package/dist/view/normalize/tokens.js.map +1 -1
  289. package/dist/view/normalize/tree.d.ts +8 -2
  290. package/dist/view/normalize/tree.js +58 -13
  291. package/dist/view/normalize/tree.js.map +1 -1
  292. package/dist/view/normalize/types.d.ts +24 -4
  293. package/dist/view/normalize/types.js +4 -1
  294. package/dist/view/normalize/types.js.map +1 -1
  295. package/dist/view/normalize/whitespace.js +4 -3
  296. package/dist/view/normalize/whitespace.js.map +1 -1
  297. package/dist/view/prepare-stub.js +2 -2
  298. package/dist/view/prepare-stub.js.map +1 -1
  299. package/dist/view/prepare.d.ts +5 -2
  300. package/dist/view/prepare.js +9 -3
  301. package/dist/view/prepare.js.map +1 -1
  302. package/dist/view/registry.d.ts +15 -0
  303. package/dist/view/registry.js +10 -2
  304. package/dist/view/registry.js.map +1 -1
  305. package/dist/view/render/adopt-attr.js +7 -1
  306. package/dist/view/render/adopt-attr.js.map +1 -1
  307. package/dist/view/render/adopt.js +21 -4
  308. package/dist/view/render/adopt.js.map +1 -1
  309. package/dist/view/render/attr-parts.d.ts +12 -1
  310. package/dist/view/render/attr-parts.js +38 -5
  311. package/dist/view/render/attr-parts.js.map +1 -1
  312. package/dist/view/render/child-part.js +5 -2
  313. package/dist/view/render/child-part.js.map +1 -1
  314. package/dist/view/render/dispose.d.ts +3 -0
  315. package/dist/view/render/dispose.js +76 -0
  316. package/dist/view/render/dispose.js.map +1 -0
  317. package/dist/view/render/hook-part.d.ts +3 -1
  318. package/dist/view/render/hook-part.js +21 -5
  319. package/dist/view/render/hook-part.js.map +1 -1
  320. package/dist/view/render/hook-run.d.ts +2 -0
  321. package/dist/view/render/hook-run.js +72 -0
  322. package/dist/view/render/hook-run.js.map +1 -0
  323. package/dist/view/render/hooks.d.ts +73 -5
  324. package/dist/view/render/hooks.js +79 -17
  325. package/dist/view/render/hooks.js.map +1 -1
  326. package/dist/view/render/hydrate.js +2 -2
  327. package/dist/view/render/hydrate.js.map +1 -1
  328. package/dist/view/render/keyed.d.ts +6 -0
  329. package/dist/view/render/keyed.js +15 -0
  330. package/dist/view/render/keyed.js.map +1 -0
  331. package/dist/view/render/mismatch.js +5 -3
  332. package/dist/view/render/mismatch.js.map +1 -1
  333. package/dist/view/render/modal.d.ts +3 -0
  334. package/dist/view/render/modal.js +39 -0
  335. package/dist/view/render/modal.js.map +1 -0
  336. package/dist/view/render/raw.d.ts +4 -1
  337. package/dist/view/render/raw.js +6 -2
  338. package/dist/view/render/raw.js.map +1 -1
  339. package/dist/view/render/render.d.ts +4 -1
  340. package/dist/view/render/render.js +17 -12
  341. package/dist/view/render/render.js.map +1 -1
  342. package/dist/view/render/toggles.d.ts +6 -0
  343. package/dist/view/render/toggles.js +6 -0
  344. package/dist/view/render/toggles.js.map +1 -0
  345. package/dist/view/render/warn.d.ts +14 -0
  346. package/dist/view/render/warn.js +45 -0
  347. package/dist/view/render/warn.js.map +1 -1
  348. package/dist/view/server/component.d.ts +6 -3
  349. package/dist/view/server/component.js +29 -7
  350. package/dist/view/server/component.js.map +1 -1
  351. package/dist/view/server/index.d.ts +20 -0
  352. package/dist/view/server/index.js +12 -4
  353. package/dist/view/server/index.js.map +1 -1
  354. package/dist/view/server/tags.d.ts +13 -2
  355. package/dist/view/server/tags.js +13 -2
  356. package/dist/view/server/tags.js.map +1 -1
  357. package/dist/view/server/values.d.ts +2 -0
  358. package/dist/view/server/values.js +8 -0
  359. package/dist/view/server/values.js.map +1 -1
  360. package/dist/view/server/writer.d.ts +9 -4
  361. package/dist/view/server/writer.js +33 -18
  362. package/dist/view/server/writer.js.map +1 -1
  363. package/dist/view/template-element.d.ts +7 -0
  364. package/dist/view/template-element.js +25 -5
  365. package/dist/view/template-element.js.map +1 -1
  366. package/dist/view/template.d.ts +10 -2
  367. package/dist/view/template.js +62 -8
  368. package/dist/view/template.js.map +1 -1
  369. package/dist/view/trusted-html.d.ts +6 -0
  370. package/dist/view/trusted-html.js +34 -0
  371. package/dist/view/trusted-html.js.map +1 -0
  372. package/dist/vite.d.ts +38 -31
  373. package/dist/vite.js +198 -31
  374. package/dist/vite.js.map +1 -1
  375. package/package.json +34 -3
  376. package/dist/hooks/labelled-by.d.ts +0 -13
  377. package/dist/hooks/labelled-by.js +0 -51
  378. package/dist/hooks/labelled-by.js.map +0 -1
  379. package/dist/random.d.ts +0 -13
  380. package/dist/random.js +0 -19
  381. package/dist/random.js.map +0 -1
package/dist/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared state: stores as \"props from the side\" (docs/design-docs/0013-shared-state.md).\n// A store is MVI without a view: init + pure update, commands run by its own interpreter.\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { DEVTOOLS_ENABLED, devCommands, devStore } from '#devtools';\nimport {\n splitNext,\n type AnyDriver,\n type Command,\n type DriverOverrides,\n type Next,\n} from './command.js';\nimport { features, type LocalHost } from './features.js';\nimport { makeInterpreter, type Interpreter } from './internal/interpreter.js';\nimport { bindStores } from './store-binding.js';\nimport type { IntentRejected, Tagged } from './types.js';\n\n/** No DOM means a server render: store commands never run there (ADR 0012). */\nconst onServer = typeof document === 'undefined';\n\ntype Variant<M extends Tagged, K extends M['_tag']> = Extract<M, { readonly _tag: K }>;\n\n/** One pure reducer per message tag, like a component's `update` but without context. */\nexport type StoreUpdate<S, M extends Tagged> = {\n readonly [K in M['_tag']]: (state: S, msg: Variant<M, K>) => Next<S, M>;\n};\n\nexport interface StoreSpec<S, M extends Tagged> {\n /** Initial state (and optional commands, e.g. load a saved cart). */\n readonly init: () => Next<S, M>;\n readonly update: StoreUpdate<S, M>;\n /** Driver substitutions by name for this store's commands. */\n readonly drivers?: DriverOverrides;\n /**\n * Validates the server's page seed before the client uses it (any synchronous Standard\n * Schema). An invalid seed is reported with console.error and the store starts from `init`.\n */\n readonly schema?: StandardSchemaV1<unknown, S>;\n}\n\n/** The result of checking a seed against a store's schema. */\nexport type SeedCheck<S> =\n | { readonly ok: true; readonly state: S }\n | { readonly ok: false; readonly issues: readonly string[] };\n\n/** What `ctx.read()` accepts: anything that names a store and carries its state type. */\nexport interface StoreRef<S> {\n readonly name: string;\n /** Type-only marker for the state type. Never set at runtime. */\n readonly stateType?: (state: S) => S;\n}\n\n/** A live store: one per page on the client, one per request on the server, one per test. */\nexport interface StoreInstance<S, M extends Tagged> {\n readonly store: Store<S, M>;\n readonly state: S;\n /** Feeds a message through the store's `update` and notifies subscribers on change. */\n send(msg: M): void;\n /** Called after every state change. Returns an unsubscribe function. */\n subscribe(listener: (state: S, prev: S) => void): () => void;\n /** Per-instance driver substitutions (test fakes). Checked before the spec's. */\n drivers: DriverOverrides;\n /** Stops running commands and drops subscribers. */\n dispose(): void;\n /** Set by the registry that holds the instance (store-to-store `send`). */\n bindScope(resolve: StoreResolver): void;\n}\n\nexport interface Store<S, M extends Tagged> extends StoreRef<S> {\n readonly spec: StoreSpec<S, M>;\n /**\n * A fresh, independent instance. `initial` skips `init`'s state (a server seed, a test\n * fixture); `init`'s commands still run on the client.\n */\n instance(initial?: S): StoreInstance<S, M>;\n /** Checks a seed with `spec.schema` (ok as-is without one). */\n checkSeed(seed: unknown): SeedCheck<S>;\n /** Type-only marker for the message type. Never set at runtime. */\n readonly messageType?: (msg: M) => M;\n}\n\n/** Any store, with its types erased (for declarations and registries). */\nexport interface AnyStore {\n readonly name: string;\n instance(initial?: never): AnyStoreInstance;\n checkSeed(seed: unknown): SeedCheck<unknown>;\n}\n\n/** Any store instance, with its types erased. */\nexport interface AnyStoreInstance {\n readonly store: AnyStore;\n readonly state: unknown;\n send(msg: never): void;\n subscribe(listener: (state: unknown, prev: unknown) => void): () => void;\n dispose(): void;\n /**\n * Called by the registry that holds this instance, so the store's `send(other, msg)`\n * commands reach `other`'s instance in the same scope (gyral-czi.20).\n */\n bindScope?(resolve: StoreResolver): void;\n}\n\n/** Finds a store's instance in a scope (a registry). */\nexport type StoreResolver = (store: AnyStore) => AnyStoreInstance;\n\n/** Store instances substituted by store name (tests, islands). */\nexport type StoreOverrides = Readonly<Record<string, AnyStoreInstance>>;\n\n/** Defines a store. `name` keys it in registries, seeds and overrides, so keep it unique. */\nexport function defineStore<S, M extends Tagged>(name: string, spec: StoreSpec<S, M>): Store<S, M> {\n features.stores = bindStores; // components can use stores now (features.ts)\n const store: Store<S, M> = {\n name,\n spec,\n instance: (initial?: S) => createInstance(store, initial),\n checkSeed: (seed) => checkSeed(spec.schema, seed),\n };\n return store;\n}\n\nconst issuePath = (issue: StandardSchemaV1.Issue): string =>\n (issue.path ?? []).map((s) => String(typeof s === 'object' ? s.key : s)).join('.');\n\nfunction checkSeed<S>(\n schema: StandardSchemaV1<unknown, S> | undefined,\n seed: unknown,\n): SeedCheck<S> {\n // Sound without a schema: the seed is this store's state, serialized by the server.\n if (schema === undefined) return { ok: true, state: seed as S };\n const result = schema['~standard'].validate(seed);\n if (result instanceof Promise) {\n return { ok: false, issues: ['the store schema is async; seeds need a synchronous schema'] };\n }\n if (result.issues === undefined) return { ok: true, state: result.value };\n return {\n ok: false,\n issues: result.issues.map((i) => `${issuePath(i) || '(root)'}: ${i.message}`),\n };\n}\n\nfunction createInstance<S, M extends Tagged>(\n store: Store<S, M>,\n initial: S | undefined,\n): StoreInstance<S, M> {\n // Sound: send() only calls the reducer whose key equals msg._tag.\n const reducers = store.spec.update as unknown as Readonly<\n Record<string, ((state: S, msg: M) => Next<S, M>) | undefined>\n >;\n const listeners = new Set<(state: S, prev: S) => void>();\n let interpreter: Interpreter<M | IntentRejected> | undefined;\n let disposed = false;\n\n const [initState, initCommands] = splitNext(store.spec.init());\n let state = initial === undefined ? initState : initial;\n\n const resolve = (driver: AnyDriver): AnyDriver =>\n instance.drivers[driver.name] ?? store.spec.drivers?.[driver.name] ?? driver;\n\n let scope: StoreResolver | undefined;\n\n // Store-to-store writes: delivered synchronously to the other store's instance in scope.\n const deliver = ({ store: target, msg }: StoreSendInput): void => {\n if (scope === undefined) {\n console.warn(\n `store \"${store.name}\" sends to \"${target.name}\" but is not held by a store scope ` +\n '(a registry, <gyral-stores> or the page default), so the message is dropped.',\n );\n return;\n }\n (scope(target) as { send(msg: unknown): void }).send(msg);\n };\n\n const run = (commands: ReadonlyArray<Command<M | IntentRejected>>): void => {\n if (onServer || disposed || commands.length === 0) return; // never on the server (ADR 0012)\n for (const cmd of commands) {\n if (cmd.driver === STORE_SEND) {\n deliver(cmd.input as StoreSendInput);\n continue;\n }\n interpreter ??= makeInterpreter<M | IntentRejected>(\n resolve,\n (msg) => {\n // Stores have no IntentRejected reducer; only their own messages apply.\n if (msg._tag !== 'IntentRejected') instance.send(msg as M);\n },\n DEVTOOLS_ENABLED ? devCommands(() => `store:${store.name}`) : undefined,\n );\n interpreter.run(cmd);\n }\n };\n\n const instance: StoreInstance<S, M> = {\n store,\n get state() {\n return state;\n },\n drivers: {},\n send(msg) {\n const reducer = reducers[msg._tag];\n if (reducer === undefined) {\n console.warn(`store \"${store.name}\" has no update for message \"${msg._tag}\".`);\n return;\n }\n const prev = state;\n const [next, commands] = splitNext(reducer(prev, msg));\n state = next;\n if (DEVTOOLS_ENABLED) devStore(store.name, msg, prev, next);\n if (!Object.is(next, prev)) for (const listener of [...listeners]) listener(next, prev);\n run(commands);\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n dispose() {\n disposed = true;\n interpreter?.dispose();\n listeners.clear();\n },\n bindScope(resolver) {\n scope = resolver;\n },\n };\n\n // A seeded instance (hydration) starts init's commands after the hydrating renders flushed,\n // so a fast driver can't change state under them (same reason as ADR 0012 for components).\n if (initial === undefined) run(initCommands);\n else if (initCommands.length > 0)\n setTimeout(() => {\n run(initCommands);\n }, 0);\n return instance;\n}\n\n/** Marker driver for `send()`: `define()` delivers these to the resolved store instance. */\nexport const STORE_SEND = {\n name: '@gyral/store-send',\n run: () => undefined,\n /** A component's send: delivered to its instance of the store (features.ts). */\n local: (host: LocalHost, input: unknown) => {\n host.stores().send(input as StoreSendInput);\n },\n} as const;\n\n/** The input of a `send()` command. */\nexport interface StoreSendInput {\n readonly store: AnyStore;\n readonly msg: Tagged;\n}\n\n/** A command that sends `msg` to the component's instance of `store`. Writes are commands. */\n// NoInfer: the store decides the message type, so a literal (`{ _tag: 'Track', … }`) is checked\n// against it instead of widening `_tag` to string (store-to-store sends have no other context).\nexport function send<S, M extends Tagged>(store: Store<S, M>, msg: NoInfer<M>): Command<never> {\n const input: StoreSendInput = { store, msg };\n return { driver: STORE_SEND, input, onSuccess: () => undefined };\n}\n\n/** Framework message: a store this component reads changed (ADR 0013). Optional reducer. */\nexport interface StoreChanged {\n readonly _tag: 'StoreChanged';\n /** The store's name. Narrow with `changed(store, msg)`. */\n readonly store: string;\n readonly state: unknown;\n readonly prev: unknown;\n}\n\n/** Narrows a `StoreChanged` message to one store's typed state, or `undefined`. */\nexport function changed<S>(\n store: StoreRef<S>,\n msg: StoreChanged,\n): { readonly state: S; readonly prev: S } | undefined {\n // Sound: the message was built from this store's instance (matched by unique name).\n return msg.store === store.name ? { state: msg.state as S, prev: msg.prev as S } : undefined;\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared state: stores as \"props from the side\" (docs/design-docs/0013-shared-state.md).\n// A store is MVI without a view: init + pure update, commands run by its own interpreter.\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { DEVTOOLS_ENABLED, devCommands, devStore } from '#devtools';\nimport {\n splitNext,\n type AnyDriver,\n type Command,\n type DriverOverrides,\n type Next,\n} from './command.js';\nimport { fail } from './errors.js';\nimport { features, type LocalHost } from './features.js';\nimport { makeInterpreter, type Interpreter } from './internal/interpreter.js';\nimport { noteActivity } from './scheduler.js';\nimport { bindStores } from './store-binding.js';\nimport type { IntentRejected, Tagged } from './types.js';\nimport { message } from './view/index.js';\n\n/** No DOM means a server render: store commands never run there (ADR 0012). */\nconst onServer = typeof document === 'undefined';\n\ntype Variant<M extends Tagged, K extends M['_tag']> = Extract<M, { readonly _tag: K }>;\n\n/** One pure reducer per message tag, like a component's `update` but without context. */\nexport type StoreUpdate<S, M extends Tagged> = {\n readonly [K in M['_tag']]: (state: S, msg: Variant<M, K>) => Next<S, M>;\n};\n\nexport interface StoreSpec<S, M extends Tagged> {\n /** Initial state (and optional commands, e.g. load a saved cart). */\n readonly init: () => Next<S, M>;\n readonly update: StoreUpdate<S, M>;\n /** Driver substitutions by name for this store's commands. */\n readonly drivers?: DriverOverrides;\n /**\n * Validates the server's page seed before the client uses it (any synchronous Standard\n * Schema). An invalid seed is reported with console.error and the store starts from `init`.\n */\n readonly schema?: StandardSchemaV1<unknown, S>;\n}\n\n/** The result of checking a seed against a store's schema. */\nexport type SeedCheck<S> =\n | { readonly ok: true; readonly state: S }\n | { readonly ok: false; readonly issues: readonly string[] };\n\n/** What `ctx.read()` accepts: anything that names a store and carries its state type. */\nexport interface StoreRef<S> {\n readonly name: string;\n /** Type-only marker for the state type. Never set at runtime. */\n readonly stateType?: (state: S) => S;\n}\n\n/** A live store: one per page on the client, one per request on the server, one per test. */\nexport interface StoreInstance<S, M extends Tagged> {\n readonly store: Store<S, M>;\n readonly state: S;\n /** Feeds a message through the store's `update` and notifies subscribers on change. */\n send(msg: M): void;\n /** Called after every state change. Returns an unsubscribe function. */\n subscribe(listener: (state: S, prev: S) => void): () => void;\n /** Per-instance driver substitutions (test fakes). Checked before the spec's. */\n drivers: DriverOverrides;\n /** Stops running commands and drops subscribers. */\n dispose(): void;\n /** Set by the registry that holds the instance (store-to-store `send`). */\n bindScope(resolve: StoreResolver): void;\n}\n\nexport interface Store<S, M extends Tagged> extends StoreRef<S> {\n readonly spec: StoreSpec<S, M>;\n /**\n * A fresh, independent instance. `initial` skips `init`'s state (a server seed, a test\n * fixture); `init`'s commands still run on the client.\n */\n instance(initial?: S): StoreInstance<S, M>;\n /** Checks a seed with `spec.schema` (ok as-is without one). */\n checkSeed(seed: unknown): SeedCheck<S>;\n /** Type-only marker for the message type. Never set at runtime. */\n readonly messageType?: (msg: M) => M;\n}\n\n/** Any store, with its types erased (for declarations and registries). */\nexport interface AnyStore {\n readonly name: string;\n instance(initial?: never): AnyStoreInstance;\n checkSeed(seed: unknown): SeedCheck<unknown>;\n}\n\n/** Any store instance, with its types erased. */\nexport interface AnyStoreInstance {\n readonly store: AnyStore;\n readonly state: unknown;\n send(msg: never): void;\n subscribe(listener: (state: unknown, prev: unknown) => void): () => void;\n dispose(): void;\n /**\n * Called by the registry that holds this instance, so the store's `send(other, msg)`\n * commands reach `other`'s instance in the same scope (gyral-czi.20).\n */\n bindScope?(resolve: StoreResolver): void;\n}\n\n/** Finds a store's instance in a scope (a registry). */\nexport type StoreResolver = (store: AnyStore) => AnyStoreInstance;\n\n/** Store instances substituted by store name (tests, islands). */\nexport type StoreOverrides = Readonly<Record<string, AnyStoreInstance>>;\n\n/** Defines a store. `name` keys it in registries, seeds and overrides, so keep it unique. */\nexport function defineStore<S, M extends Tagged>(name: string, spec: StoreSpec<S, M>): Store<S, M> {\n features.stores = bindStores; // components can use stores now (features.ts)\n const store: Store<S, M> = {\n name,\n spec,\n instance: (initial?: S) => createInstance(store, initial),\n checkSeed: (seed) => checkSeed(spec.schema, seed),\n };\n return store;\n}\n\nconst issuePath = (issue: StandardSchemaV1.Issue): string =>\n (issue.path ?? []).map((s) => String(typeof s === 'object' ? s.key : s)).join('.');\n\nfunction checkSeed<S>(\n schema: StandardSchemaV1<unknown, S> | undefined,\n seed: unknown,\n): SeedCheck<S> {\n // Sound without a schema: the seed is this store's state, serialized by the server.\n if (schema === undefined) return { ok: true, state: seed as S };\n const result = schema['~standard'].validate(seed);\n if (result instanceof Promise) {\n return { ok: false, issues: ['the store schema is async; seeds need a synchronous schema'] };\n }\n if (result.issues === undefined) return { ok: true, state: result.value };\n return {\n ok: false,\n issues: result.issues.map((i) => `${issuePath(i) || '(root)'}: ${i.message}`),\n };\n}\n\nfunction createInstance<S, M extends Tagged>(\n store: Store<S, M>,\n initial: S | undefined,\n): StoreInstance<S, M> {\n // Sound: send() only calls the reducer whose key equals msg._tag.\n const reducers = store.spec.update as unknown as Readonly<\n Record<string, ((state: S, msg: M) => Next<S, M>) | undefined>\n >;\n const listeners = new Set<(state: S, prev: S) => void>();\n let interpreter: Interpreter<M | IntentRejected> | undefined;\n let disposed = false;\n\n const [initState, initCommands] = splitNext(store.spec.init());\n let state = initial === undefined ? initState : initial;\n\n const resolve = (driver: AnyDriver): AnyDriver =>\n instance.drivers[driver.name] ?? store.spec.drivers?.[driver.name] ?? driver;\n\n let scope: StoreResolver | undefined;\n\n // Store-to-store writes: delivered synchronously to the other store's instance in scope.\n const deliver = ({ store: target, msg }: StoreSendInput): void => {\n if (scope === undefined) {\n console.warn(message(54, store.name, target.name));\n return;\n }\n (scope(target) as { send(msg: unknown): void }).send(msg);\n };\n\n const run = (commands: ReadonlyArray<Command<M | IntentRejected>>): void => {\n if (onServer || disposed || commands.length === 0) return; // never on the server (ADR 0012)\n for (const cmd of commands) {\n if (cmd.driver === STORE_SEND) {\n deliver(cmd.input as StoreSendInput);\n continue;\n }\n interpreter ??= makeInterpreter<M | IntentRejected>(\n resolve,\n (msg) => {\n // Stores have no IntentRejected reducer; only their own messages apply.\n if (msg._tag !== 'IntentRejected') instance.send(msg as M);\n },\n DEVTOOLS_ENABLED ? devCommands(() => `store:${store.name}`) : undefined,\n (cause, code, driver) => {\n const owner = `store \"${store.name}\"`;\n const text =\n code === 40\n ? message(40, owner, driver)\n : code === 41\n ? message(41, owner, driver)\n : message(43, owner, driver);\n fail(cause, 'command', text, { msg: driver });\n },\n );\n interpreter.run(cmd);\n }\n };\n\n const instance: StoreInstance<S, M> = {\n store,\n get state() {\n return state;\n },\n drivers: {},\n send(msg) {\n noteActivity(); // settled() keeps waiting while messages arrive (04)\n const reducer = reducers[msg._tag];\n if (reducer === undefined) {\n console.warn(message(55, store.name, msg._tag));\n return;\n }\n const prev = state;\n let result: Next<S, M>;\n try {\n result = reducer(prev, msg);\n } catch (cause) {\n // A store reducer that throws changes nothing (ADR 0024).\n fail(cause, 'store', message(76, store.name, msg._tag), { msg: msg._tag });\n return;\n }\n const [next, commands] = splitNext(result);\n state = next;\n if (DEVTOOLS_ENABLED) devStore(store.name, msg, prev, next);\n if (!Object.is(next, prev)) {\n // Each subscriber on its own: one that throws doesn't keep the others stale (ADR 0024).\n for (const listener of [...listeners]) {\n try {\n listener(next, prev);\n } catch (cause) {\n fail(cause, 'store', message(77, store.name), { msg: msg._tag });\n }\n }\n }\n run(commands);\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n dispose() {\n disposed = true;\n interpreter?.dispose();\n listeners.clear();\n },\n bindScope(resolver) {\n scope = resolver;\n },\n };\n\n // A seeded instance (hydration) starts init's commands after the hydrating renders flushed,\n // so a fast driver can't change state under them (same reason as ADR 0012 for components).\n if (initial === undefined) run(initCommands);\n else if (initCommands.length > 0)\n setTimeout(() => {\n run(initCommands);\n }, 0);\n return instance;\n}\n\n/** Marker driver for `send()`: `define()` delivers these to the resolved store instance. */\nexport const STORE_SEND = {\n name: '@gyral/store-send',\n run: () => undefined,\n /** A component's send: delivered to its instance of the store (features.ts). */\n local: (host: LocalHost, input: unknown) => {\n host.stores().send(input as StoreSendInput);\n },\n} as const;\n\n/** The input of a `send()` command. */\nexport interface StoreSendInput {\n readonly store: AnyStore;\n readonly msg: Tagged;\n}\n\n/** A command that sends `msg` to the component's instance of `store`. Writes are commands. */\n// NoInfer: the store decides the message type, so a literal (`{ _tag: 'Track', … }`) is checked\n// against it instead of widening `_tag` to string (store-to-store sends have no other context).\nexport function send<S, M extends Tagged>(store: Store<S, M>, msg: NoInfer<M>): Command<never> {\n const input: StoreSendInput = { store, msg };\n return { driver: STORE_SEND, input, onSuccess: () => undefined };\n}\n\n/** Framework message: a store this component reads changed (ADR 0013). Optional reducer. */\nexport interface StoreChanged {\n readonly _tag: 'StoreChanged';\n /** The store's name. Narrow with `changed(store, msg)`. */\n readonly store: string;\n readonly state: unknown;\n readonly prev: unknown;\n}\n\n/** Narrows a `StoreChanged` message to one store's typed state, or `undefined`. */\nexport function changed<S>(\n store: StoreRef<S>,\n msg: StoreChanged,\n): { readonly state: S; readonly prev: S } | undefined {\n // Sound: the message was built from this store's instance (matched by unique name).\n return msg.store === store.name ? { state: msg.state as S, prev: msg.prev as S } : undefined;\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import type { Concurrency, Driver } from './command.js';
2
+ /** How to stop listening: a function, or an object with `unsubscribe()` (RxJS, XState). */
3
+ export type Unsubscribe = (() => void) | {
4
+ readonly unsubscribe: () => void;
5
+ };
6
+ export interface SubscriptionContext<I> {
7
+ /** The command's input (a room code, a URL, a store key). */
8
+ readonly input: I;
9
+ /** Aborts when the command is switched away or its component disconnects. */
10
+ readonly signal: AbortSignal;
11
+ /**
12
+ * Ends the subscription with an error: the source is released and the command's
13
+ * `onFailure` gets it. Wrap the driver in `retry()` to subscribe again instead.
14
+ */
15
+ readonly fail: (error: unknown) => void;
16
+ }
17
+ /** Driver options a subscription may set. `concurrency` defaults to `'switch'`. */
18
+ export interface SubscriptionOptions<E> {
19
+ readonly concurrency?: Concurrency;
20
+ readonly toError?: (cause: unknown) => E;
21
+ }
22
+ /**
23
+ * A streaming driver over a source Gyral doesn't own. `subscribe` starts listening, calls
24
+ * `emit` with each value (it may emit the current value at once) and returns how to stop
25
+ * (a function, or an object with `unsubscribe()`).
26
+ * The subscription lasts while the command runs; aborting it (lane `switch`, disconnect)
27
+ * releases the source, and emits after that are ignored. Substitute it by `name` in tests.
28
+ *
29
+ * const cart = subscription<CartState>('cart', (emit) => {
30
+ * emit(reduxStore.getState());
31
+ * return reduxStore.subscribe(() => emit(reduxStore.getState()));
32
+ * });
33
+ * // init: [state, [command(cart, undefined, { onSuccess: (c) => ({ _tag: 'Cart', c }) })]]
34
+ */
35
+ export declare function subscription<O, I = undefined, E = unknown>(name: string, subscribe: (emit: (value: O) => void, ctx: SubscriptionContext<I>) => Unsubscribe, options?: SubscriptionOptions<E>): Driver<I, O, E>;
@@ -0,0 +1,74 @@
1
+ import { fail } from './errors.js';
2
+ import { message } from './view/index.js';
3
+ const release = (name, stop) => {
4
+ if (stop === undefined)
5
+ return;
6
+ try {
7
+ if (typeof stop === 'function')
8
+ stop();
9
+ else
10
+ stop.unsubscribe();
11
+ }
12
+ catch (error) {
13
+ fail(error, 'subscribe', message(42, name), { msg: name });
14
+ }
15
+ };
16
+ const asError = (reason) => reason instanceof Error ? reason : new DOMException('Aborted', 'AbortError');
17
+ /**
18
+ * A streaming driver over a source Gyral doesn't own. `subscribe` starts listening, calls
19
+ * `emit` with each value (it may emit the current value at once) and returns how to stop
20
+ * (a function, or an object with `unsubscribe()`).
21
+ * The subscription lasts while the command runs; aborting it (lane `switch`, disconnect)
22
+ * releases the source, and emits after that are ignored. Substitute it by `name` in tests.
23
+ *
24
+ * const cart = subscription<CartState>('cart', (emit) => {
25
+ * emit(reduxStore.getState());
26
+ * return reduxStore.subscribe(() => emit(reduxStore.getState()));
27
+ * });
28
+ * // init: [state, [command(cart, undefined, { onSuccess: (c) => ({ _tag: 'Cart', c }) })]]
29
+ */
30
+ export function subscription(name, subscribe, options = {}) {
31
+ return {
32
+ name,
33
+ concurrency: 'switch',
34
+ ...options,
35
+ run: (input, { signal, emit }) => new Promise((_resolve, reject) => {
36
+ // `ended` may flip inside subscribe (fail() or an abort), so it lives in an object.
37
+ const sub = { ended: false };
38
+ // Deliberately not wrapped in an Error: the driver's toError (or onFailure) sees the
39
+ // source's own error (a WebSocket's error event, a store's rejection).
40
+ const end = (error) => {
41
+ if (sub.ended)
42
+ return;
43
+ sub.ended = true;
44
+ signal.removeEventListener('abort', onAbort);
45
+ release(name, sub.stop);
46
+ // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
47
+ reject(error);
48
+ };
49
+ const onAbort = () => {
50
+ end(asError(signal.reason));
51
+ };
52
+ if (signal.aborted) {
53
+ onAbort();
54
+ return;
55
+ }
56
+ signal.addEventListener('abort', onAbort, { once: true });
57
+ try {
58
+ const stop = subscribe((value) => {
59
+ if (!sub.ended)
60
+ emit(value);
61
+ }, { input, signal, fail: end });
62
+ // fail() or an abort while subscribing ended it before `stop` was known.
63
+ if (sub.ended)
64
+ release(name, stop);
65
+ else
66
+ sub.stop = stop;
67
+ }
68
+ catch (error) {
69
+ end(error);
70
+ }
71
+ }),
72
+ };
73
+ }
74
+ //# sourceMappingURL=subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.js","sourceRoot":"","sources":["../src/subscription.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAuB1C,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,IAA6B,EAAQ,EAAE;IACpE,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,IAAI,CAAC;QACH,IAAI,OAAO,IAAI,KAAK,UAAU;YAAE,IAAI,EAAE,CAAC;;YAClC,IAAI,CAAC,WAAW,EAAE,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,MAAe,EAAS,EAAE,CACzC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAE/E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,SAAiF,EACjF,UAAkC,EAAE;IAEpC,OAAO;QACL,IAAI;QACJ,WAAW,EAAE,QAAQ;QACrB,GAAG,OAAO;QACV,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAC/B,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;YACtC,oFAAoF;YACpF,MAAM,GAAG,GAA2C,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YACrE,qFAAqF;YACrF,uEAAuE;YACvE,MAAM,GAAG,GAAG,CAAC,KAAc,EAAQ,EAAE;gBACnC,IAAI,GAAG,CAAC,KAAK;oBAAE,OAAO;gBACtB,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC;gBACjB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC7C,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBACxB,2EAA2E;gBAC3E,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC;YACF,MAAM,OAAO,GAAG,GAAS,EAAE;gBACzB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YAC9B,CAAC,CAAC;YACF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,SAAS,CACpB,CAAC,KAAK,EAAE,EAAE;oBACR,IAAI,CAAC,GAAG,CAAC,KAAK;wBAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC9B,CAAC,EACD,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAC7B,CAAC;gBACF,yEAAyE;gBACzE,IAAI,GAAG,CAAC,KAAK;oBAAE,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;;oBAC9B,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,KAAK,CAAC,CAAC;YACb,CAAC;QACH,CAAC,CAAC;KACL,CAAC;AACJ,CAAC","sourcesContent":["// Streaming drivers over sources Gyral doesn't own (ADR 0006 \"Outside sources\"): TC39 signals,\n// Redux-style stores, XState actors, WebSocket feeds. `subscription()` turns \"subscribe and\n// return how to stop\" into a driver: values become messages through the command's\n// `onSuccess`, and the source is released when the command is switched away or its component\n// disconnects. A standalone module: apps that don't import it don't bundle it.\nimport type { Concurrency, Driver } from './command.js';\nimport { fail } from './errors.js';\nimport { message } from './view/index.js';\n\n/** How to stop listening: a function, or an object with `unsubscribe()` (RxJS, XState). */\nexport type Unsubscribe = (() => void) | { readonly unsubscribe: () => void };\n\nexport interface SubscriptionContext<I> {\n /** The command's input (a room code, a URL, a store key). */\n readonly input: I;\n /** Aborts when the command is switched away or its component disconnects. */\n readonly signal: AbortSignal;\n /**\n * Ends the subscription with an error: the source is released and the command's\n * `onFailure` gets it. Wrap the driver in `retry()` to subscribe again instead.\n */\n readonly fail: (error: unknown) => void;\n}\n\n/** Driver options a subscription may set. `concurrency` defaults to `'switch'`. */\nexport interface SubscriptionOptions<E> {\n readonly concurrency?: Concurrency;\n readonly toError?: (cause: unknown) => E;\n}\n\nconst release = (name: string, stop: Unsubscribe | undefined): void => {\n if (stop === undefined) return;\n try {\n if (typeof stop === 'function') stop();\n else stop.unsubscribe();\n } catch (error) {\n fail(error, 'subscribe', message(42, name), { msg: name });\n }\n};\n\nconst asError = (reason: unknown): Error =>\n reason instanceof Error ? reason : new DOMException('Aborted', 'AbortError');\n\n/**\n * A streaming driver over a source Gyral doesn't own. `subscribe` starts listening, calls\n * `emit` with each value (it may emit the current value at once) and returns how to stop\n * (a function, or an object with `unsubscribe()`).\n * The subscription lasts while the command runs; aborting it (lane `switch`, disconnect)\n * releases the source, and emits after that are ignored. Substitute it by `name` in tests.\n *\n * const cart = subscription<CartState>('cart', (emit) => {\n * emit(reduxStore.getState());\n * return reduxStore.subscribe(() => emit(reduxStore.getState()));\n * });\n * // init: [state, [command(cart, undefined, { onSuccess: (c) => ({ _tag: 'Cart', c }) })]]\n */\nexport function subscription<O, I = undefined, E = unknown>(\n name: string,\n subscribe: (emit: (value: O) => void, ctx: SubscriptionContext<I>) => Unsubscribe,\n options: SubscriptionOptions<E> = {},\n): Driver<I, O, E> {\n return {\n name,\n concurrency: 'switch',\n ...options,\n run: (input, { signal, emit }) =>\n new Promise<never>((_resolve, reject) => {\n // `ended` may flip inside subscribe (fail() or an abort), so it lives in an object.\n const sub: { ended: boolean; stop?: Unsubscribe } = { ended: false };\n // Deliberately not wrapped in an Error: the driver's toError (or onFailure) sees the\n // source's own error (a WebSocket's error event, a store's rejection).\n const end = (error: unknown): void => {\n if (sub.ended) return;\n sub.ended = true;\n signal.removeEventListener('abort', onAbort);\n release(name, sub.stop);\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n reject(error);\n };\n const onAbort = (): void => {\n end(asError(signal.reason));\n };\n if (signal.aborted) {\n onAbort();\n return;\n }\n signal.addEventListener('abort', onAbort, { once: true });\n try {\n const stop = subscribe(\n (value) => {\n if (!sub.ended) emit(value);\n },\n { input, signal, fail: end },\n );\n // fail() or an abort while subscribing ended it before `stop` was known.\n if (sub.ended) release(name, stop);\n else sub.stop = stop;\n } catch (error) {\n end(error);\n }\n }),\n };\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { Command } from './command.js';
2
+ /** What syncing needs from the host's model (host-model.ts). */
3
+ export interface SubscriptionHost<M> {
4
+ readonly tag: string;
5
+ state(): unknown;
6
+ ctx(): {
7
+ readonly props: unknown;
8
+ };
9
+ /** Runs commands as a reducer's would. */
10
+ run(commands: readonly Command<M>[]): void;
11
+ /** Aborts the running task of `lane`. */
12
+ cancel(lane: string): void;
13
+ fail(cause: unknown, phase: 'update', text: string, msg: string): unknown;
14
+ }
15
+ /** One host's subscriptions. */
16
+ export interface Subscriptions {
17
+ /** Evaluates `subscriptions` and makes the running set equal to its result. */
18
+ readonly update: () => void;
19
+ /** Forgets the running set (the host's commands were stopped). */
20
+ readonly reset: () => void;
21
+ }
22
+ /**
23
+ * `derive` is the spec field. A throw is reported as an update failure (ADR 0024) and leaves
24
+ * the running set as it was.
25
+ */
26
+ export declare function subscriptionSet<M>(host: SubscriptionHost<M>, derive: (state: never, props: never) => readonly Command<M>[]): Subscriptions;
@@ -0,0 +1,98 @@
1
+ import { DEV, message } from './view/index.js';
2
+ /**
3
+ * An input's identity for the diff: its data as JSON text. Functions inside it count as equal
4
+ * (an inline selector, `watch(store, (s) => s.x)`, is a new function after every message, and
5
+ * comparing it would restart the lane each time), repeated objects and cycles are cut, and
6
+ * BigInts are written out. So a changed key or changed data restarts a lane; a changed function
7
+ * alone doesn't: to restart on a new selector, change the key (gyral-1zd.29).
8
+ */
9
+ const fingerprint = (input) => {
10
+ const seen = new WeakSet();
11
+ // Wrapped in an array, so even `undefined` has a text (`[null]`).
12
+ return JSON.stringify([input], (_key, value) => {
13
+ if (typeof value === 'function')
14
+ return '[function]';
15
+ if (typeof value === 'bigint')
16
+ return `${value.toString()}n`;
17
+ if (typeof value === 'object' && value !== null) {
18
+ if (seen.has(value))
19
+ return '[seen]';
20
+ seen.add(value);
21
+ }
22
+ return value;
23
+ });
24
+ };
25
+ /**
26
+ * `derive` is the spec field. A throw is reported as an update failure (ADR 0024) and leaves
27
+ * the running set as it was.
28
+ */
29
+ export function subscriptionSet(host, derive) {
30
+ const tag = host.tag;
31
+ const running = new Map();
32
+ // A command can emit while it starts (a timer that fires at once), so its message's update
33
+ // asks to sync again in the middle of a sync: it runs once this one is done.
34
+ let syncing = false;
35
+ let again = false;
36
+ const askedAgain = () => again; // read through a call: sync() may set it
37
+ const sync = () => {
38
+ let commands;
39
+ try {
40
+ // Sound: the host passes its own state and props to its own spec's field.
41
+ commands = derive(host.state(), host.ctx().props);
42
+ }
43
+ catch (cause) {
44
+ host.fail(cause, 'update', message(73, tag, 'subscriptions'), 'subscriptions');
45
+ return;
46
+ }
47
+ const seen = new Set();
48
+ for (const cmd of commands) {
49
+ const lane = cmd.key ?? cmd.driver.name;
50
+ if (seen.has(lane)) {
51
+ if (DEV)
52
+ console.warn(message(79, tag, lane));
53
+ continue;
54
+ }
55
+ seen.add(lane);
56
+ const print = fingerprint(cmd.input);
57
+ if (running.has(lane)) {
58
+ if (Object.is(running.get(lane), print))
59
+ continue;
60
+ host.cancel(lane);
61
+ }
62
+ running.set(lane, print);
63
+ host.run([cmd]);
64
+ }
65
+ for (const lane of running.keys()) {
66
+ if (seen.has(lane))
67
+ continue;
68
+ host.cancel(lane);
69
+ running.delete(lane);
70
+ }
71
+ };
72
+ return {
73
+ update() {
74
+ if (syncing) {
75
+ again = true;
76
+ return;
77
+ }
78
+ syncing = true;
79
+ try {
80
+ // A subscription whose input depends on what it emits would restart forever.
81
+ for (let rounds = 0; rounds < 100; rounds++) {
82
+ again = false;
83
+ sync();
84
+ if (!askedAgain())
85
+ return;
86
+ }
87
+ host.fail(new Error(message(80, tag)), 'update', message(80, tag), 'subscriptions');
88
+ }
89
+ finally {
90
+ syncing = false;
91
+ }
92
+ },
93
+ reset() {
94
+ running.clear();
95
+ },
96
+ };
97
+ }
98
+ //# sourceMappingURL=subscriptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../src/subscriptions.ts"],"names":[],"mappings":"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","sourcesContent":["// Declarative subscriptions (ADR 0025, view/05-element.md \"Subscriptions\"): `spec.subscriptions`\n// says which long-lived commands should be running for the current state and props, and this\n// module keeps the running set equal to it. Reached through `#spec-features` (registered by\n// the build when a module names the field), so apps that don't use it don't bundle it.\n//\n// The rules: once the host has rendered, and after every update and props change while it is\n// connected, `subscriptions(state, props)` is evaluated and synced. Commands are matched by\n// lane (`key`, else the driver's name). A lane that is new starts; a lane that is gone is\n// aborted; a lane whose input changed (compared as JSON text, functions ignored) is aborted\n// and started again; a lane with the same input keeps running. A subscription that ends by\n// itself stays ended until its lane or input changes. When the host's commands stop (removal,\n// ADR 0006) the set is forgotten, and the next connect starts it again from the current state.\nimport type { Command } from './command.js';\nimport { DEV, message } from './view/index.js';\n\n/** What syncing needs from the host's model (host-model.ts). */\nexport interface SubscriptionHost<M> {\n readonly tag: string;\n state(): unknown;\n ctx(): { readonly props: unknown };\n /** Runs commands as a reducer's would. */\n run(commands: readonly Command<M>[]): void;\n /** Aborts the running task of `lane`. */\n cancel(lane: string): void;\n fail(cause: unknown, phase: 'update', text: string, msg: string): unknown;\n}\n\n/** One host's subscriptions. */\nexport interface Subscriptions {\n /** Evaluates `subscriptions` and makes the running set equal to its result. */\n readonly update: () => void;\n /** Forgets the running set (the host's commands were stopped). */\n readonly reset: () => void;\n}\n\n/**\n * An input's identity for the diff: its data as JSON text. Functions inside it count as equal\n * (an inline selector, `watch(store, (s) => s.x)`, is a new function after every message, and\n * comparing it would restart the lane each time), repeated objects and cycles are cut, and\n * BigInts are written out. So a changed key or changed data restarts a lane; a changed function\n * alone doesn't: to restart on a new selector, change the key (gyral-1zd.29).\n */\nconst fingerprint = (input: unknown): string => {\n const seen = new WeakSet<object>();\n // Wrapped in an array, so even `undefined` has a text (`[null]`).\n return JSON.stringify([input], (_key, value: unknown) => {\n if (typeof value === 'function') return '[function]';\n if (typeof value === 'bigint') return `${value.toString()}n`;\n if (typeof value === 'object' && value !== null) {\n if (seen.has(value)) return '[seen]';\n seen.add(value);\n }\n return value;\n });\n};\n\n/**\n * `derive` is the spec field. A throw is reported as an update failure (ADR 0024) and leaves\n * the running set as it was.\n */\nexport function subscriptionSet<M>(\n host: SubscriptionHost<M>,\n derive: (state: never, props: never) => readonly Command<M>[],\n): Subscriptions {\n const tag = host.tag;\n const running = new Map<string, unknown>();\n // A command can emit while it starts (a timer that fires at once), so its message's update\n // asks to sync again in the middle of a sync: it runs once this one is done.\n let syncing = false;\n let again = false;\n const askedAgain = (): boolean => again; // read through a call: sync() may set it\n const sync = (): void => {\n let commands: readonly Command<M>[];\n try {\n // Sound: the host passes its own state and props to its own spec's field.\n commands = derive(host.state() as never, host.ctx().props as never);\n } catch (cause) {\n host.fail(cause, 'update', message(73, tag, 'subscriptions'), 'subscriptions');\n return;\n }\n const seen = new Set<string>();\n for (const cmd of commands) {\n const lane = cmd.key ?? cmd.driver.name;\n if (seen.has(lane)) {\n if (DEV) console.warn(message(79, tag, lane));\n continue;\n }\n seen.add(lane);\n const print = fingerprint(cmd.input);\n if (running.has(lane)) {\n if (Object.is(running.get(lane), print)) continue;\n host.cancel(lane);\n }\n running.set(lane, print);\n host.run([cmd]);\n }\n for (const lane of running.keys()) {\n if (seen.has(lane)) continue;\n host.cancel(lane);\n running.delete(lane);\n }\n };\n return {\n update() {\n if (syncing) {\n again = true;\n return;\n }\n syncing = true;\n try {\n // A subscription whose input depends on what it emits would restart forever.\n for (let rounds = 0; rounds < 100; rounds++) {\n again = false;\n sync();\n if (!askedAgain()) return;\n }\n host.fail(new Error(message(80, tag)), 'update', message(80, tag), 'subscriptions');\n } finally {\n syncing = false;\n }\n },\n reset() {\n running.clear();\n },\n };\n}\n"]}
@@ -1,8 +1,7 @@
1
- /** True when a view transition may run now (API present, motion not reduced). */
2
- export declare const canTransition: () => boolean;
3
1
  /**
4
- * Runs `update` (a whole flush) as a view transition's update callback. A transition skipped
2
+ * Runs `update` (a whole flush) as a view transition's update callback and returns true, or
3
+ * returns false when no transition may run now (the caller then runs it). A transition skipped
5
4
  * by a newer one still runs its update (per spec); its rejected promises are expected and
6
- * swallowed. Call only after `canTransition()`.
5
+ * swallowed.
7
6
  */
8
- export declare function startTransition(update: () => void): void;
7
+ export declare function startTransition(update: () => void): boolean;
@@ -1,17 +1,23 @@
1
1
  // View Transitions for the scheduler (view/04-scheduler.md "View transitions", ADR 0001
2
2
  // addendum). An ADR 0003 enhancement: without the API, or with reduced motion requested, the
3
- // flush runs as normal.
3
+ // flush runs as normal. Reached through core's `#spec-features` (spec-features.ts): compiled
4
+ // builds bundle it only when a module names `viewTransition` (compiler/features.ts,
5
+ // gyral-c5d.12).
4
6
  const ignore = () => undefined;
5
7
  /** True when a view transition may run now (API present, motion not reduced). */
6
- export const canTransition = () => 'startViewTransition' in document && !matchMedia('(prefers-reduced-motion: reduce)').matches;
8
+ const canTransition = () => 'startViewTransition' in document && !matchMedia('(prefers-reduced-motion: reduce)').matches;
7
9
  /**
8
- * Runs `update` (a whole flush) as a view transition's update callback. A transition skipped
10
+ * Runs `update` (a whole flush) as a view transition's update callback and returns true, or
11
+ * returns false when no transition may run now (the caller then runs it). A transition skipped
9
12
  * by a newer one still runs its update (per spec); its rejected promises are expected and
10
- * swallowed. Call only after `canTransition()`.
13
+ * swallowed.
11
14
  */
12
15
  export function startTransition(update) {
16
+ if (!canTransition())
17
+ return false;
13
18
  const transition = document.startViewTransition(update);
14
19
  transition.ready.catch(ignore);
15
20
  transition.finished.catch(ignore);
21
+ return true;
16
22
  }
17
23
  //# sourceMappingURL=transitions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transitions.js","sourceRoot":"","sources":["../src/transitions.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,6FAA6F;AAC7F,wBAAwB;AAWxB,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC,SAAS,CAAC;AAErC,iFAAiF;AACjF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAY,EAAE,CACzC,qBAAqB,IAAI,QAAQ,IAAI,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO,CAAC;AAE/F;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAkB;IAChD,MAAM,UAAU,GAAI,QAA8C,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC/F,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC","sourcesContent":["// View Transitions for the scheduler (view/04-scheduler.md \"View transitions\", ADR 0001\n// addendum). An ADR 0003 enhancement: without the API, or with reduced motion requested, the\n// flush runs as normal.\n\ninterface ViewTransitionLike {\n readonly ready: Promise<unknown>;\n readonly finished: Promise<unknown>;\n}\n\ninterface ViewTransitionDocument {\n startViewTransition(update: () => void): ViewTransitionLike;\n}\n\nconst ignore = (): void => undefined;\n\n/** True when a view transition may run now (API present, motion not reduced). */\nexport const canTransition = (): boolean =>\n 'startViewTransition' in document && !matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n/**\n * Runs `update` (a whole flush) as a view transition's update callback. A transition skipped\n * by a newer one still runs its update (per spec); its rejected promises are expected and\n * swallowed. Call only after `canTransition()`.\n */\nexport function startTransition(update: () => void): void {\n const transition = (document as Document & ViewTransitionDocument).startViewTransition(update);\n transition.ready.catch(ignore);\n transition.finished.catch(ignore);\n}\n"]}
1
+ {"version":3,"file":"transitions.js","sourceRoot":"","sources":["../src/transitions.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,6FAA6F;AAC7F,6FAA6F;AAC7F,oFAAoF;AACpF,iBAAiB;AAWjB,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC,SAAS,CAAC;AAErC,iFAAiF;AACjF,MAAM,aAAa,GAAG,GAAY,EAAE,CAClC,qBAAqB,IAAI,QAAQ,IAAI,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO,CAAC;AAE/F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,MAAkB;IAChD,IAAI,CAAC,aAAa,EAAE;QAAE,OAAO,KAAK,CAAC;IACnC,MAAM,UAAU,GAAI,QAA8C,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC/F,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["// View Transitions for the scheduler (view/04-scheduler.md \"View transitions\", ADR 0001\n// addendum). An ADR 0003 enhancement: without the API, or with reduced motion requested, the\n// flush runs as normal. Reached through core's `#spec-features` (spec-features.ts): compiled\n// builds bundle it only when a module names `viewTransition` (compiler/features.ts,\n// gyral-c5d.12).\n\ninterface ViewTransitionLike {\n readonly ready: Promise<unknown>;\n readonly finished: Promise<unknown>;\n}\n\ninterface ViewTransitionDocument {\n startViewTransition(update: () => void): ViewTransitionLike;\n}\n\nconst ignore = (): void => undefined;\n\n/** True when a view transition may run now (API present, motion not reduced). */\nconst canTransition = (): boolean =>\n 'startViewTransition' in document && !matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n/**\n * Runs `update` (a whole flush) as a view transition's update callback and returns true, or\n * returns false when no transition may run now (the caller then runs it). A transition skipped\n * by a newer one still runs its update (per spec); its rejected promises are expected and\n * swallowed.\n */\nexport function startTransition(update: () => void): boolean {\n if (!canTransition()) return false;\n const transition = (document as Document & ViewTransitionDocument).startViewTransition(update);\n transition.ready.catch(ignore);\n transition.finished.catch(ignore);\n return true;\n}\n"]}