@typed/template 0.1.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 (285) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/Directive.js +76 -0
  4. package/dist/cjs/Directive.js.map +1 -0
  5. package/dist/cjs/ElementRef.js +83 -0
  6. package/dist/cjs/ElementRef.js.map +1 -0
  7. package/dist/cjs/ElementSource.js +244 -0
  8. package/dist/cjs/ElementSource.js.map +1 -0
  9. package/dist/cjs/Entry.js +6 -0
  10. package/dist/cjs/Entry.js.map +1 -0
  11. package/dist/cjs/EventHandler.js +65 -0
  12. package/dist/cjs/EventHandler.js.map +1 -0
  13. package/dist/cjs/Html.js +169 -0
  14. package/dist/cjs/Html.js.map +1 -0
  15. package/dist/cjs/HtmlChunk.js +257 -0
  16. package/dist/cjs/HtmlChunk.js.map +1 -0
  17. package/dist/cjs/Hydrate.js +49 -0
  18. package/dist/cjs/Hydrate.js.map +1 -0
  19. package/dist/cjs/Many.js +45 -0
  20. package/dist/cjs/Many.js.map +1 -0
  21. package/dist/cjs/Meta.js +37 -0
  22. package/dist/cjs/Meta.js.map +1 -0
  23. package/dist/cjs/Parser.js +331 -0
  24. package/dist/cjs/Parser.js.map +1 -0
  25. package/dist/cjs/Part.js +6 -0
  26. package/dist/cjs/Part.js.map +1 -0
  27. package/dist/cjs/Placeholder.js +38 -0
  28. package/dist/cjs/Placeholder.js.map +1 -0
  29. package/dist/cjs/Platform.js +64 -0
  30. package/dist/cjs/Platform.js.map +1 -0
  31. package/dist/cjs/Render.js +39 -0
  32. package/dist/cjs/Render.js.map +1 -0
  33. package/dist/cjs/RenderContext.js +130 -0
  34. package/dist/cjs/RenderContext.js.map +1 -0
  35. package/dist/cjs/RenderEvent.js +44 -0
  36. package/dist/cjs/RenderEvent.js.map +1 -0
  37. package/dist/cjs/RenderTemplate.js +41 -0
  38. package/dist/cjs/RenderTemplate.js.map +1 -0
  39. package/dist/cjs/Renderable.js +6 -0
  40. package/dist/cjs/Renderable.js.map +1 -0
  41. package/dist/cjs/Template.js +266 -0
  42. package/dist/cjs/Template.js.map +1 -0
  43. package/dist/cjs/TemplateInstance.js +51 -0
  44. package/dist/cjs/TemplateInstance.js.map +1 -0
  45. package/dist/cjs/Test.js +90 -0
  46. package/dist/cjs/Test.js.map +1 -0
  47. package/dist/cjs/Token.js +270 -0
  48. package/dist/cjs/Token.js.map +1 -0
  49. package/dist/cjs/Tokenizer.js +18 -0
  50. package/dist/cjs/Tokenizer.js.map +1 -0
  51. package/dist/cjs/Vitest.js +44 -0
  52. package/dist/cjs/Vitest.js.map +1 -0
  53. package/dist/cjs/index.js +147 -0
  54. package/dist/cjs/index.js.map +1 -0
  55. package/dist/cjs/internal/HydrateContext.js +13 -0
  56. package/dist/cjs/internal/HydrateContext.js.map +1 -0
  57. package/dist/cjs/internal/browser.js +109 -0
  58. package/dist/cjs/internal/browser.js.map +1 -0
  59. package/dist/cjs/internal/chunks.js +54 -0
  60. package/dist/cjs/internal/chunks.js.map +1 -0
  61. package/dist/cjs/internal/errors.js +23 -0
  62. package/dist/cjs/internal/errors.js.map +1 -0
  63. package/dist/cjs/internal/hydrate.js +197 -0
  64. package/dist/cjs/internal/hydrate.js.map +1 -0
  65. package/dist/cjs/internal/indexRefCounter.js +32 -0
  66. package/dist/cjs/internal/indexRefCounter.js.map +1 -0
  67. package/dist/cjs/internal/module-augmentation.js +6 -0
  68. package/dist/cjs/internal/module-augmentation.js.map +1 -0
  69. package/dist/cjs/internal/parser.js +492 -0
  70. package/dist/cjs/internal/parser.js.map +1 -0
  71. package/dist/cjs/internal/parts.js +350 -0
  72. package/dist/cjs/internal/parts.js.map +1 -0
  73. package/dist/cjs/internal/readAttribute.js +34 -0
  74. package/dist/cjs/internal/readAttribute.js.map +1 -0
  75. package/dist/cjs/internal/render.js +332 -0
  76. package/dist/cjs/internal/render.js.map +1 -0
  77. package/dist/cjs/internal/server.js +219 -0
  78. package/dist/cjs/internal/server.js.map +1 -0
  79. package/dist/cjs/internal/tokenizer.js +264 -0
  80. package/dist/cjs/internal/tokenizer.js.map +1 -0
  81. package/dist/cjs/internal/utils.js +68 -0
  82. package/dist/cjs/internal/utils.js.map +1 -0
  83. package/dist/dts/Directive.d.ts +70 -0
  84. package/dist/dts/Directive.d.ts.map +1 -0
  85. package/dist/dts/ElementRef.d.ts +40 -0
  86. package/dist/dts/ElementRef.d.ts.map +1 -0
  87. package/dist/dts/ElementSource.d.ts +72 -0
  88. package/dist/dts/ElementSource.d.ts.map +1 -0
  89. package/dist/dts/Entry.d.ts +26 -0
  90. package/dist/dts/Entry.d.ts.map +1 -0
  91. package/dist/dts/EventHandler.d.ts +61 -0
  92. package/dist/dts/EventHandler.d.ts.map +1 -0
  93. package/dist/dts/Html.d.ts +17 -0
  94. package/dist/dts/Html.d.ts.map +1 -0
  95. package/dist/dts/HtmlChunk.d.ts +56 -0
  96. package/dist/dts/HtmlChunk.d.ts.map +1 -0
  97. package/dist/dts/Hydrate.d.ts +20 -0
  98. package/dist/dts/Hydrate.d.ts.map +1 -0
  99. package/dist/dts/Many.d.ts +32 -0
  100. package/dist/dts/Many.d.ts.map +1 -0
  101. package/dist/dts/Meta.d.ts +24 -0
  102. package/dist/dts/Meta.d.ts.map +1 -0
  103. package/dist/dts/Parser.d.ts +16 -0
  104. package/dist/dts/Parser.d.ts.map +1 -0
  105. package/dist/dts/Part.d.ts +147 -0
  106. package/dist/dts/Part.d.ts.map +1 -0
  107. package/dist/dts/Placeholder.d.ts +51 -0
  108. package/dist/dts/Placeholder.d.ts.map +1 -0
  109. package/dist/dts/Platform.d.ts +23 -0
  110. package/dist/dts/Platform.d.ts.map +1 -0
  111. package/dist/dts/Render.d.ts +23 -0
  112. package/dist/dts/Render.d.ts.map +1 -0
  113. package/dist/dts/RenderContext.d.ts +88 -0
  114. package/dist/dts/RenderContext.d.ts.map +1 -0
  115. package/dist/dts/RenderEvent.d.ts +37 -0
  116. package/dist/dts/RenderEvent.d.ts.map +1 -0
  117. package/dist/dts/RenderTemplate.d.ts +38 -0
  118. package/dist/dts/RenderTemplate.d.ts.map +1 -0
  119. package/dist/dts/Renderable.d.ts +28 -0
  120. package/dist/dts/Renderable.d.ts.map +1 -0
  121. package/dist/dts/Template.d.ts +218 -0
  122. package/dist/dts/Template.d.ts.map +1 -0
  123. package/dist/dts/TemplateInstance.d.ts +32 -0
  124. package/dist/dts/TemplateInstance.d.ts.map +1 -0
  125. package/dist/dts/Test.d.ts +58 -0
  126. package/dist/dts/Test.d.ts.map +1 -0
  127. package/dist/dts/Token.d.ts +202 -0
  128. package/dist/dts/Token.d.ts.map +1 -0
  129. package/dist/dts/Tokenizer.d.ts +6 -0
  130. package/dist/dts/Tokenizer.d.ts.map +1 -0
  131. package/dist/dts/Vitest.d.ts +28 -0
  132. package/dist/dts/Vitest.d.ts.map +1 -0
  133. package/dist/dts/index.d.ts +65 -0
  134. package/dist/dts/index.d.ts.map +1 -0
  135. package/dist/dts/internal/HydrateContext.d.ts +2 -0
  136. package/dist/dts/internal/HydrateContext.d.ts.map +1 -0
  137. package/dist/dts/internal/browser.d.ts +8 -0
  138. package/dist/dts/internal/browser.d.ts.map +1 -0
  139. package/dist/dts/internal/chunks.d.ts +22 -0
  140. package/dist/dts/internal/chunks.d.ts.map +1 -0
  141. package/dist/dts/internal/errors.d.ts +9 -0
  142. package/dist/dts/internal/errors.d.ts.map +1 -0
  143. package/dist/dts/internal/hydrate.d.ts +37 -0
  144. package/dist/dts/internal/hydrate.d.ts.map +1 -0
  145. package/dist/dts/internal/indexRefCounter.d.ts +6 -0
  146. package/dist/dts/internal/indexRefCounter.d.ts.map +1 -0
  147. package/dist/dts/internal/module-augmentation.d.ts +36 -0
  148. package/dist/dts/internal/module-augmentation.d.ts.map +1 -0
  149. package/dist/dts/internal/parser.d.ts +12 -0
  150. package/dist/dts/internal/parser.d.ts.map +1 -0
  151. package/dist/dts/internal/parts.d.ts +304 -0
  152. package/dist/dts/internal/parts.d.ts.map +1 -0
  153. package/dist/dts/internal/readAttribute.d.ts +9 -0
  154. package/dist/dts/internal/readAttribute.d.ts.map +1 -0
  155. package/dist/dts/internal/render.d.ts +30 -0
  156. package/dist/dts/internal/render.d.ts.map +1 -0
  157. package/dist/dts/internal/server.d.ts +31 -0
  158. package/dist/dts/internal/server.d.ts.map +1 -0
  159. package/dist/dts/internal/tokenizer.d.ts +3 -0
  160. package/dist/dts/internal/tokenizer.d.ts.map +1 -0
  161. package/dist/dts/internal/utils.d.ts +15 -0
  162. package/dist/dts/internal/utils.d.ts.map +1 -0
  163. package/dist/esm/Directive.js +64 -0
  164. package/dist/esm/Directive.js.map +1 -0
  165. package/dist/esm/ElementRef.js +72 -0
  166. package/dist/esm/ElementRef.js.map +1 -0
  167. package/dist/esm/ElementSource.js +237 -0
  168. package/dist/esm/ElementSource.js.map +1 -0
  169. package/dist/esm/Entry.js +2 -0
  170. package/dist/esm/Entry.js.map +1 -0
  171. package/dist/esm/EventHandler.js +52 -0
  172. package/dist/esm/EventHandler.js.map +1 -0
  173. package/dist/esm/Html.js +167 -0
  174. package/dist/esm/Html.js.map +1 -0
  175. package/dist/esm/HtmlChunk.js +274 -0
  176. package/dist/esm/HtmlChunk.js.map +1 -0
  177. package/dist/esm/Hydrate.js +37 -0
  178. package/dist/esm/Hydrate.js.map +1 -0
  179. package/dist/esm/Many.js +33 -0
  180. package/dist/esm/Many.js.map +1 -0
  181. package/dist/esm/Meta.js +29 -0
  182. package/dist/esm/Meta.js.map +1 -0
  183. package/dist/esm/Parser.js +342 -0
  184. package/dist/esm/Parser.js.map +1 -0
  185. package/dist/esm/Part.js +5 -0
  186. package/dist/esm/Part.js.map +1 -0
  187. package/dist/esm/Placeholder.js +30 -0
  188. package/dist/esm/Placeholder.js.map +1 -0
  189. package/dist/esm/Platform.js +41 -0
  190. package/dist/esm/Platform.js.map +1 -0
  191. package/dist/esm/Render.js +27 -0
  192. package/dist/esm/Render.js.map +1 -0
  193. package/dist/esm/RenderContext.js +113 -0
  194. package/dist/esm/RenderContext.js.map +1 -0
  195. package/dist/esm/RenderEvent.js +36 -0
  196. package/dist/esm/RenderEvent.js.map +1 -0
  197. package/dist/esm/RenderTemplate.js +26 -0
  198. package/dist/esm/RenderTemplate.js.map +1 -0
  199. package/dist/esm/Renderable.js +2 -0
  200. package/dist/esm/Renderable.js.map +1 -0
  201. package/dist/esm/Template.js +239 -0
  202. package/dist/esm/Template.js.map +1 -0
  203. package/dist/esm/TemplateInstance.js +43 -0
  204. package/dist/esm/TemplateInstance.js.map +1 -0
  205. package/dist/esm/Test.js +68 -0
  206. package/dist/esm/Test.js.map +1 -0
  207. package/dist/esm/Token.js +264 -0
  208. package/dist/esm/Token.js.map +1 -0
  209. package/dist/esm/Tokenizer.js +9 -0
  210. package/dist/esm/Tokenizer.js.map +1 -0
  211. package/dist/esm/Vitest.js +29 -0
  212. package/dist/esm/Vitest.js.map +1 -0
  213. package/dist/esm/index.js +65 -0
  214. package/dist/esm/index.js.map +1 -0
  215. package/dist/esm/internal/HydrateContext.js +7 -0
  216. package/dist/esm/internal/HydrateContext.js.map +1 -0
  217. package/dist/esm/internal/browser.js +102 -0
  218. package/dist/esm/internal/browser.js.map +1 -0
  219. package/dist/esm/internal/chunks.js +47 -0
  220. package/dist/esm/internal/chunks.js.map +1 -0
  221. package/dist/esm/internal/errors.js +15 -0
  222. package/dist/esm/internal/errors.js.map +1 -0
  223. package/dist/esm/internal/hydrate.js +165 -0
  224. package/dist/esm/internal/hydrate.js.map +1 -0
  225. package/dist/esm/internal/indexRefCounter.js +24 -0
  226. package/dist/esm/internal/indexRefCounter.js.map +1 -0
  227. package/dist/esm/internal/module-augmentation.js +2 -0
  228. package/dist/esm/internal/module-augmentation.js.map +1 -0
  229. package/dist/esm/internal/parser.js +493 -0
  230. package/dist/esm/internal/parser.js.map +1 -0
  231. package/dist/esm/internal/parts.js +291 -0
  232. package/dist/esm/internal/parts.js.map +1 -0
  233. package/dist/esm/internal/readAttribute.js +24 -0
  234. package/dist/esm/internal/readAttribute.js.map +1 -0
  235. package/dist/esm/internal/render.js +329 -0
  236. package/dist/esm/internal/render.js.map +1 -0
  237. package/dist/esm/internal/server.js +174 -0
  238. package/dist/esm/internal/server.js.map +1 -0
  239. package/dist/esm/internal/tokenizer.js +296 -0
  240. package/dist/esm/internal/tokenizer.js.map +1 -0
  241. package/dist/esm/internal/utils.js +52 -0
  242. package/dist/esm/internal/utils.js.map +1 -0
  243. package/dist/esm/package.json +4 -0
  244. package/package.json +242 -0
  245. package/src/Directive.ts +114 -0
  246. package/src/ElementRef.ts +123 -0
  247. package/src/ElementSource.ts +417 -0
  248. package/src/Entry.ts +28 -0
  249. package/src/EventHandler.ts +104 -0
  250. package/src/Html.ts +258 -0
  251. package/src/HtmlChunk.ts +346 -0
  252. package/src/Hydrate.ts +53 -0
  253. package/src/Many.ts +128 -0
  254. package/src/Meta.ts +32 -0
  255. package/src/Parser.ts +457 -0
  256. package/src/Part.ts +186 -0
  257. package/src/Placeholder.ts +70 -0
  258. package/src/Platform.ts +71 -0
  259. package/src/Render.ts +45 -0
  260. package/src/RenderContext.ts +221 -0
  261. package/src/RenderEvent.ts +67 -0
  262. package/src/RenderTemplate.ts +88 -0
  263. package/src/Renderable.ts +34 -0
  264. package/src/Template.ts +284 -0
  265. package/src/TemplateInstance.ts +83 -0
  266. package/src/Test.ts +151 -0
  267. package/src/Token.ts +269 -0
  268. package/src/Tokenizer.ts +10 -0
  269. package/src/Vitest.ts +61 -0
  270. package/src/index.ts +66 -0
  271. package/src/internal/HydrateContext.ts +23 -0
  272. package/src/internal/browser.ts +132 -0
  273. package/src/internal/chunks.ts +73 -0
  274. package/src/internal/errors.ts +11 -0
  275. package/src/internal/external.d.ts +11 -0
  276. package/src/internal/hydrate.ts +262 -0
  277. package/src/internal/indexRefCounter.ts +33 -0
  278. package/src/internal/module-augmentation.ts +48 -0
  279. package/src/internal/parser.ts +637 -0
  280. package/src/internal/parts.ts +527 -0
  281. package/src/internal/readAttribute.ts +28 -0
  282. package/src/internal/render.ts +529 -0
  283. package/src/internal/server.ts +293 -0
  284. package/src/internal/tokenizer.ts +338 -0
  285. package/src/internal/utils.ts +73 -0
package/src/Part.ts ADDED
@@ -0,0 +1,186 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import type { Cause } from "effect/Cause"
6
+ import type { Effect } from "effect/Effect"
7
+ import type { Scope } from "effect/Scope"
8
+ import type { ElementSource } from "./ElementSource"
9
+ import type { EventHandler } from "./EventHandler"
10
+
11
+ /**
12
+ * @since 1.0.0
13
+ */
14
+ export type Part =
15
+ | AttributePart
16
+ | BooleanPart
17
+ | ClassNamePart
18
+ | CommentPart
19
+ | DataPart
20
+ | EventPart
21
+ | NodePart
22
+ | PropertyPart
23
+ | RefPart
24
+ | TextPart
25
+
26
+ /**
27
+ * @since 1.0.0
28
+ */
29
+ export interface AttributePart {
30
+ readonly _tag: "attribute"
31
+ readonly name: string
32
+ readonly value: string | null | undefined
33
+ readonly index: number
34
+
35
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
36
+ }
37
+
38
+ /**
39
+ * @since 1.0.0
40
+ */
41
+ export interface BooleanPart {
42
+ readonly _tag: "boolean"
43
+ readonly name: string
44
+ readonly value: boolean | null | undefined
45
+ readonly index: number
46
+
47
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
48
+ }
49
+
50
+ /**
51
+ * @since 1.0.0
52
+ */
53
+ export interface ClassNamePart {
54
+ readonly _tag: "className"
55
+ readonly value: ReadonlyArray<string> | null | undefined
56
+ readonly index: number
57
+
58
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
59
+ }
60
+
61
+ /**
62
+ * @since 1.0.0
63
+ */
64
+ export interface DataPart {
65
+ readonly _tag: "data"
66
+ readonly value: Readonly<Record<string, string | undefined>> | null | undefined
67
+ readonly index: number
68
+
69
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
70
+ }
71
+
72
+ /**
73
+ * @since 1.0.0
74
+ */
75
+ export interface EventPart {
76
+ readonly _tag: "event"
77
+ readonly name: string
78
+ readonly value: EventHandler<unknown, never> | null | undefined
79
+ readonly index: number
80
+ readonly onCause: (cause: Cause<unknown>) => Effect<never, never, unknown>
81
+
82
+ readonly update: <R = never>(value: EventHandler<R, never> | null | undefined) => Effect<R | Scope, never, void>
83
+ }
84
+
85
+ /**
86
+ * @since 1.0.0
87
+ */
88
+ export interface PropertyPart {
89
+ readonly _tag: "property"
90
+ readonly name: string
91
+ readonly value: unknown
92
+ readonly index: number
93
+
94
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
95
+ }
96
+
97
+ /**
98
+ * @since 1.0.0
99
+ */
100
+ export interface RefPart<T extends HTMLElement | SVGElement = HTMLElement | SVGElement> {
101
+ readonly _tag: "ref"
102
+ readonly value: ElementSource<T>
103
+ readonly index: number
104
+ }
105
+
106
+ /**
107
+ * @since 1.0.0
108
+ */
109
+ export interface CommentPart {
110
+ readonly _tag: "comment"
111
+ readonly value: string | null | undefined
112
+ readonly index: number
113
+
114
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
115
+ }
116
+
117
+ /**
118
+ * @since 1.0.0
119
+ */
120
+ export interface TextPart {
121
+ readonly _tag: "text"
122
+ readonly value: string | null | undefined
123
+ readonly index: number
124
+
125
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
126
+ }
127
+
128
+ /**
129
+ * @since 1.0.0
130
+ */
131
+ export interface NodePart {
132
+ readonly _tag: "node"
133
+ readonly value: unknown
134
+ readonly index: number
135
+
136
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
137
+ }
138
+
139
+ /**
140
+ * @since 1.0.0
141
+ */
142
+ export type SparsePart = SparseAttributePart | SparseClassNamePart | SparseCommentPart
143
+
144
+ /**
145
+ * @since 1.0.0
146
+ */
147
+ export interface SparseAttributePart {
148
+ readonly _tag: "sparse/attribute"
149
+ readonly name: string
150
+ readonly parts: ReadonlyArray<AttributePart | StaticText>
151
+
152
+ readonly update: (value: ReadonlyArray<string>) => Effect<Scope, never, void>
153
+ }
154
+
155
+ /**
156
+ * @since 1.0.0
157
+ */
158
+ export interface SparseClassNamePart {
159
+ readonly _tag: "sparse/className"
160
+ readonly parts: ReadonlyArray<ClassNamePart | StaticText>
161
+
162
+ readonly update: (value: ReadonlyArray<string>) => Effect<Scope, never, void>
163
+ }
164
+
165
+ /**
166
+ * @since 1.0.0
167
+ */
168
+ export interface SparseCommentPart {
169
+ readonly _tag: "sparse/comment"
170
+ readonly parts: ReadonlyArray<CommentPart | StaticText>
171
+
172
+ readonly update: (value: ReadonlyArray<string>) => Effect<Scope, never, void>
173
+ }
174
+
175
+ /**
176
+ * @since 1.0.0
177
+ */
178
+ export interface StaticText {
179
+ readonly _tag: "static/text"
180
+ readonly value: string
181
+ }
182
+
183
+ /**
184
+ * @since 1.0.0
185
+ */
186
+ export type Parts = ReadonlyArray<Part | SparsePart>
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import "./internal/module-augmentation"
6
+ import type { Fx } from "@typed/fx/Fx"
7
+ import { isFx } from "@typed/fx/Fx"
8
+ import * as RefSubject from "@typed/fx/RefSubject"
9
+ import type { Scope } from "effect"
10
+ import { Effect } from "effect"
11
+
12
+ /**
13
+ * @since 1.0.0
14
+ */
15
+ export const PlaceholderTypeId = Symbol.for("./Placholder")
16
+ /**
17
+ * @since 1.0.0
18
+ */
19
+ export type PlaceholderTypeId = typeof PlaceholderTypeId
20
+
21
+ /**
22
+ * @since 1.0.0
23
+ */
24
+ export interface Placeholder<out R = never, out E = never, out A = unknown> {
25
+ readonly [PlaceholderTypeId]: {
26
+ readonly _R: (_: never) => R
27
+ readonly _E: (_: never) => E
28
+ readonly _A: (_: never) => A
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @since 1.0.0
34
+ */
35
+ export namespace Placeholder {
36
+ /**
37
+ * @since 1.0.0
38
+ */
39
+ export type Any<A = any> =
40
+ | Placeholder<any, any, A>
41
+ | Placeholder<any, never, A>
42
+ | Placeholder<never, never, A>
43
+ | Placeholder<never, any, A>
44
+
45
+ /**
46
+ * @since 1.0.0
47
+ */
48
+ export type Context<T> = [T] extends [never] ? never : T extends Placeholder<infer R, infer _E, infer _A> ? R : never
49
+ /**
50
+ * @since 1.0.0
51
+ */
52
+ export type Error<T> = [T] extends [never] ? never : T extends Placeholder<infer _R, infer E, infer _A> ? E : never
53
+ /**
54
+ * @since 1.0.0
55
+ */
56
+ export type Success<T> = [T] extends [never] ? never : T extends Placeholder<infer _R, infer _E, infer A> ? A : never
57
+
58
+ /**
59
+ * @since 1.0.0
60
+ */
61
+ export function asRef<R = never, E = never, A = never>(
62
+ placeholder: Placeholder<R, E, A>
63
+ ): Effect.Effect<R | Scope.Scope, never, RefSubject.RefSubject<never, E, A>> {
64
+ if (isFx<R, E, A>(placeholder) || Effect.isEffect(placeholder)) {
65
+ return RefSubject.make(placeholder as Fx<R, E, A>)
66
+ } else {
67
+ return RefSubject.of(placeholder as A)
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import * as Headers from "@effect/platform/Http/Headers"
6
+ import type { ServerRequest } from "@effect/platform/Http/ServerRequest"
7
+ import * as HttpServer from "@effect/platform/HttpServer"
8
+ import type * as Fx from "@typed/fx/Fx"
9
+ import { toStream } from "@typed/fx/Stream"
10
+ import { Effect, Option, Stream } from "effect"
11
+ import { renderToHtml } from "./Html"
12
+ import type * as RenderContext from "./RenderContext"
13
+ import type { RenderEvent } from "./RenderEvent"
14
+ import type { RenderTemplate } from "./RenderTemplate"
15
+
16
+ const HTML_CONTENT_TYPE = "text/html"
17
+ const CAMEL_CASE_CONTENT_TYPE = { contentType: HTML_CONTENT_TYPE }
18
+ const HYPHENATED_CONTENT_TYPE = { "content-type": HTML_CONTENT_TYPE }
19
+
20
+ /**
21
+ * @since 1.0.0
22
+ */
23
+ export function htmlResponse<R, E>(
24
+ fx: Fx.Fx<R, E, RenderEvent>,
25
+ options?: HttpServer.response.Options
26
+ ): Effect.Effect<RenderContext.RenderContext | Exclude<R, RenderTemplate>, E, HttpServer.response.ServerResponse> {
27
+ return Effect.contextWithEffect((ctx) =>
28
+ HttpServer.response.stream(
29
+ Stream.provideContext(Stream.encodeText(toStream(renderToHtml(fx))), ctx),
30
+ {
31
+ ...CAMEL_CASE_CONTENT_TYPE,
32
+ ...options,
33
+ headers: { ...HYPHENATED_CONTENT_TYPE, ...options?.headers }
34
+ }
35
+ )
36
+ )
37
+ }
38
+
39
+ /**
40
+ * @since 1.0.0
41
+ */
42
+ export function htmlResponseString(
43
+ html: string,
44
+ options?: HttpServer.response.Options
45
+ ): HttpServer.response.ServerResponse {
46
+ return HttpServer.response.raw(
47
+ html,
48
+ {
49
+ ...CAMEL_CASE_CONTENT_TYPE,
50
+ ...options,
51
+ headers: { ...HYPHENATED_CONTENT_TYPE, ...options?.headers }
52
+ }
53
+ )
54
+ }
55
+
56
+ /**
57
+ * @since 1.0.0
58
+ */
59
+ export function getUrlFromServerRequest(request: ServerRequest): URL {
60
+ const { headers } = request
61
+ const host = Headers.get(headers, "x-forwarded-host").pipe(
62
+ Option.orElse(() => Headers.get(headers, "host")),
63
+ Option.getOrElse(() => "localhost")
64
+ )
65
+ const protocol = Headers.get(headers, "x-forwarded-proto").pipe(
66
+ Option.orElse(() => Headers.get(headers, "protocol")),
67
+ Option.getOrElse(() => "http")
68
+ )
69
+
70
+ return new URL(request.url, `${protocol}://${host}`)
71
+ }
package/src/Render.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import * as Context from "@typed/context"
6
+ import { Document } from "@typed/dom/Document"
7
+ import { RootElement } from "@typed/dom/RootElement"
8
+ import * as Fx from "@typed/fx/Fx"
9
+ import { type Rendered } from "@typed/wire"
10
+ import * as Effect from "effect/Effect"
11
+ import { attachRoot, renderTemplate } from "./internal/render"
12
+ import { RenderContext } from "./RenderContext"
13
+ import { type RenderEvent } from "./RenderEvent"
14
+ import { RenderTemplate } from "./RenderTemplate"
15
+
16
+ /**
17
+ * @since 1.0.0
18
+ */
19
+ export type ToRendered<T extends RenderEvent | null> = T extends null ? Rendered | null : Rendered
20
+
21
+ /**
22
+ * @since 1.0.0
23
+ */
24
+ export function render<R, E, T extends RenderEvent | null>(
25
+ rendered: Fx.Fx<R, E, T>
26
+ ): Fx.Fx<Exclude<R, RenderTemplate> | Document | RenderContext | RootElement, E, ToRendered<T>> {
27
+ return Fx.fromFxEffect(Effect.contextWith((context) => {
28
+ const [document, ctx, { rootElement }] = Context.getMany(context, Document, RenderContext, RootElement)
29
+
30
+ return Fx.provideService(
31
+ Fx.mapEffect(rendered, (what) => attachRoot(ctx.renderCache, rootElement, what)),
32
+ RenderTemplate,
33
+ renderTemplate(document, ctx)
34
+ )
35
+ }))
36
+ }
37
+
38
+ /**
39
+ * @since 1.0.0
40
+ */
41
+ export function renderLayer<R, E, T extends RenderEvent | null>(
42
+ rendered: Fx.Fx<R, E, T>
43
+ ) {
44
+ return Fx.drainLayer(Fx.switchMapCause(render(rendered), Effect.logError))
45
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * The context in which templates are rendered within
3
+ * @since 1.0.0
4
+ */
5
+
6
+ import * as Context from "@typed/context"
7
+ import { type DomServices, domServices, type DomServicesElementParams } from "@typed/dom/DomServices"
8
+ import { GlobalThis } from "@typed/dom/GlobalThis"
9
+ import { Window } from "@typed/dom/Window"
10
+ import type { Environment } from "@typed/environment"
11
+ import { CurrentEnvironment } from "@typed/environment"
12
+ import * as Idle from "@typed/fx/Idle"
13
+ import type { Rendered } from "@typed/wire"
14
+ import { Effect, Layer, Option } from "effect"
15
+ import * as Scope from "effect/Scope"
16
+ import type { Entry } from "./Entry"
17
+ import type { Part, SparsePart } from "./Part"
18
+
19
+ /**
20
+ * The context in which templates are rendered within
21
+ * @since 1.0.0
22
+ */
23
+ export interface RenderContext {
24
+ /**
25
+ * The current environment we are rendering within
26
+ */
27
+ readonly environment: Environment
28
+
29
+ /**
30
+ * Cache for root Node's being rendered into.
31
+ */
32
+ readonly renderCache: WeakMap<object, Rendered | null>
33
+
34
+ /**
35
+ * Cache for individual templates.
36
+ */
37
+ readonly templateCache: WeakMap<TemplateStringsArray, Entry>
38
+
39
+ /**
40
+ * Queue for work to be batched
41
+ */
42
+ readonly queue: RenderQueue
43
+ }
44
+
45
+ /**
46
+ * The context in which templates are rendered within
47
+ * @since 1.0.0
48
+ */
49
+ export const RenderContext: Context.Tagged<RenderContext, RenderContext> = Context.Tagged<RenderContext>(
50
+ "./RenderContext"
51
+ )
52
+
53
+ /**
54
+ * @since 1.0.0
55
+ */
56
+ export interface RenderQueue {
57
+ readonly add: (part: Part | SparsePart, task: () => void) => Effect.Effect<Scope.Scope, never, void>
58
+ }
59
+
60
+ /**
61
+ * @since 1.0.0
62
+ */
63
+ export type RenderContextOptions = IdleRequestOptions & {
64
+ readonly environment: Environment
65
+ readonly scope: Scope.Scope
66
+ }
67
+
68
+ /**
69
+ * @since 1.0.0
70
+ */
71
+ export function make({ ...options }: Omit<RenderContextOptions, "scope">, skipRenderScheduling?: boolean) {
72
+ return Effect.scopeWith((scope) => Effect.succeed(unsafeMake({ ...options, scope }, skipRenderScheduling)))
73
+ }
74
+
75
+ /**
76
+ * @since 1.0.0
77
+ */
78
+ export function unsafeMake({
79
+ environment,
80
+ scope,
81
+ ...options
82
+ }: RenderContextOptions, skipRenderScheduling?: boolean): RenderContext {
83
+ return {
84
+ environment,
85
+ renderCache: new WeakMap(),
86
+ templateCache: new WeakMap(),
87
+ queue: new RenderQueueImpl(scope, options, skipRenderScheduling ?? false)
88
+ }
89
+ }
90
+
91
+ /**
92
+ * @since 1.0.0
93
+ */
94
+ export function getRenderCache<T>(renderCache: RenderContext["renderCache"], key: object): Option.Option<T> {
95
+ return renderCache.has(key) ? Option.some(renderCache.get(key) as T) : Option.none()
96
+ }
97
+
98
+ /**
99
+ * @since 1.0.0
100
+ */
101
+ export function getTemplateCache(
102
+ templateCache: RenderContext["templateCache"],
103
+ key: TemplateStringsArray
104
+ ): Option.Option<Entry> {
105
+ return Option.fromNullable(templateCache.get(key))
106
+ }
107
+
108
+ const buildWithCurrentEnvironment = (environment: Environment, skipRenderScheduling?: boolean) =>
109
+ Layer.mergeAll(
110
+ RenderContext.scoped(make({ environment }, skipRenderScheduling)),
111
+ CurrentEnvironment.layer(environment)
112
+ )
113
+
114
+ /**
115
+ * @since 1.0.0
116
+ */
117
+ export const browser: (
118
+ window: Window & GlobalThis,
119
+ options?: DomServicesElementParams & { readonly skipRenderScheduling?: boolean }
120
+ ) => Layer.Layer<never, never, RenderContext | CurrentEnvironment | DomServices> = (window, options) =>
121
+ Layer.provideMerge(
122
+ Layer.mergeAll(Window.layer(window), GlobalThis.layer(window)),
123
+ Layer.mergeAll(
124
+ buildWithCurrentEnvironment(
125
+ "browser",
126
+ options?.skipRenderScheduling
127
+ ),
128
+ domServices(options)
129
+ )
130
+ )
131
+
132
+ /**
133
+ * @since 1.0.0
134
+ */
135
+ export const server: Layer.Layer<never, never, RenderContext | CurrentEnvironment> = buildWithCurrentEnvironment(
136
+ "server"
137
+ )
138
+
139
+ const static_: Layer.Layer<never, never, RenderContext | CurrentEnvironment> = buildWithCurrentEnvironment("static")
140
+
141
+ export {
142
+ /**
143
+ * @since 1.0.0
144
+ */
145
+ static_ as static
146
+ }
147
+
148
+ class RenderQueueImpl implements RenderQueue {
149
+ queue = new Map<Part | SparsePart, () => void>()
150
+ scheduled = false
151
+
152
+ constructor(
153
+ readonly scope: Scope.Scope,
154
+ readonly options?: IdleRequestOptions,
155
+ readonly skipRenderScheduling: boolean = false
156
+ ) {
157
+ this.add.bind(this)
158
+ }
159
+
160
+ add(part: Part | SparsePart, task: () => void) {
161
+ if (this.skipRenderScheduling) return Effect.sync(task)
162
+
163
+ return Effect.suspend(() => {
164
+ this.queue.set(part, task)
165
+
166
+ return Effect.zipRight(
167
+ Effect.addFinalizer(() =>
168
+ Effect.sync(() => {
169
+ const currentTask = this.queue.get(part)
170
+
171
+ // If the current task is still the same we'll delete it from the queue
172
+ if (currentTask === task) {
173
+ this.queue.delete(part)
174
+ }
175
+ })
176
+ ),
177
+ this.scheduleNextRun
178
+ )
179
+ })
180
+ }
181
+
182
+ scheduleNextRun = Effect.suspend(() => {
183
+ if (this.queue.size === 0 || this.scheduled) return Effect.unit
184
+
185
+ this.scheduled = true
186
+
187
+ return this.run.pipe(
188
+ Scope.extend(this.scope),
189
+ Effect.forkIn(this.scope)
190
+ )
191
+ })
192
+
193
+ run: Effect.Effect<Scope.Scope, never, void> = Effect.suspend(() =>
194
+ Effect.flatMap(
195
+ Idle.whenIdle(this.options),
196
+ (deadline) =>
197
+ Effect.suspend(() => {
198
+ const iterator = this.queue.entries()
199
+
200
+ while (Idle.shouldContinue(deadline)) {
201
+ const result = iterator.next()
202
+
203
+ if (result.done) break
204
+ else {
205
+ const [part, task] = result.value
206
+ this.queue.delete(part)
207
+ task()
208
+ }
209
+ }
210
+
211
+ if (this.queue.size > 0) {
212
+ return this.run
213
+ }
214
+
215
+ this.scheduled = false
216
+
217
+ return Effect.unit
218
+ })
219
+ )
220
+ )
221
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import type { Rendered } from "@typed/wire"
6
+
7
+ /**
8
+ * @since 1.0.0
9
+ */
10
+ export type RenderEvent = DomRenderEvent | HtmlRenderEvent
11
+
12
+ /**
13
+ * @since 1.0.0
14
+ */
15
+ export type DomRenderEvent = {
16
+ readonly _tag: "dom"
17
+ readonly rendered: Rendered
18
+ readonly valueOf: () => Rendered
19
+ }
20
+
21
+ /**
22
+ * @since 1.0.0
23
+ */
24
+ export function DomRenderEvent(rendered: Rendered): DomRenderEvent {
25
+ return {
26
+ _tag: "dom",
27
+ rendered,
28
+ valueOf: () => rendered
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @since 1.0.0
34
+ */
35
+ export type HtmlRenderEvent = {
36
+ readonly _tag: "html"
37
+ readonly html: string
38
+ readonly valueOf: () => string
39
+ }
40
+
41
+ /**
42
+ * @since 1.0.0
43
+ */
44
+ export function HtmlRenderEvent(html: string): HtmlRenderEvent {
45
+ return {
46
+ _tag: "html",
47
+ html,
48
+ valueOf: () => html
49
+ }
50
+ }
51
+
52
+ /**
53
+ * @since 1.0.0
54
+ */
55
+ export function isRenderEvent(value: unknown): value is RenderEvent {
56
+ return isTaggedObject(value) && (value._tag === "html" || value._tag === "dom")
57
+ }
58
+
59
+ function isTaggedObject(
60
+ value: unknown
61
+ ): value is Record<string, unknown> & { readonly _tag: unknown } {
62
+ return isObject(value) && "_tag" in value
63
+ }
64
+
65
+ function isObject(value: unknown): value is Record<string, unknown> {
66
+ return !!value && typeof value === "object"
67
+ }