effect-start 0.18.0 → 0.20.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 (203) hide show
  1. package/README.md +3 -3
  2. package/dist/Development.d.ts +8 -3
  3. package/dist/Development.js +14 -7
  4. package/dist/Effectify.d.ts +212 -0
  5. package/dist/Effectify.js +19 -0
  6. package/dist/FilePathPattern.d.ts +29 -0
  7. package/dist/FilePathPattern.js +86 -0
  8. package/dist/FileRouter.d.ts +39 -41
  9. package/dist/FileRouter.js +104 -158
  10. package/dist/FileRouterCodegen.d.ts +7 -8
  11. package/dist/FileRouterCodegen.js +97 -66
  12. package/dist/PlatformError.d.ts +46 -0
  13. package/dist/PlatformError.js +43 -0
  14. package/dist/PlatformRuntime.d.ts +27 -0
  15. package/dist/PlatformRuntime.js +51 -0
  16. package/dist/Route.d.ts +6 -2
  17. package/dist/Route.js +22 -0
  18. package/dist/RouteBody.d.ts +1 -1
  19. package/dist/RouteHttp.d.ts +1 -1
  20. package/dist/RouteHttp.js +12 -19
  21. package/dist/RouteMount.d.ts +2 -1
  22. package/dist/Start.d.ts +33 -6
  23. package/dist/Start.js +31 -13
  24. package/dist/Unique.d.ts +50 -0
  25. package/dist/Unique.js +187 -0
  26. package/dist/bun/BunHttpServer.js +5 -6
  27. package/dist/bun/BunPlatformHttpServer.d.ts +10 -0
  28. package/dist/bun/BunPlatformHttpServer.js +53 -0
  29. package/dist/bun/BunRoute.d.ts +4 -6
  30. package/dist/bun/BunRoute.js +10 -18
  31. package/dist/bun/BunRuntime.d.ts +2 -1
  32. package/dist/bun/BunRuntime.js +10 -5
  33. package/dist/bun/BunServer.d.ts +33 -0
  34. package/dist/bun/BunServer.js +133 -0
  35. package/dist/bun/BunServerRequest.d.ts +60 -0
  36. package/dist/bun/BunServerRequest.js +252 -0
  37. package/dist/bun/index.d.ts +1 -1
  38. package/dist/bun/index.js +1 -1
  39. package/dist/datastar/actions/fetch.d.ts +30 -0
  40. package/dist/datastar/actions/fetch.js +411 -0
  41. package/dist/datastar/actions/peek.d.ts +1 -0
  42. package/dist/datastar/actions/peek.js +14 -0
  43. package/dist/datastar/actions/setAll.d.ts +1 -0
  44. package/dist/datastar/actions/setAll.js +13 -0
  45. package/dist/datastar/actions/toggleAll.d.ts +1 -0
  46. package/dist/datastar/actions/toggleAll.js +13 -0
  47. package/dist/datastar/attributes/attr.d.ts +1 -0
  48. package/dist/datastar/attributes/attr.js +49 -0
  49. package/dist/datastar/attributes/bind.d.ts +1 -0
  50. package/dist/datastar/attributes/bind.js +183 -0
  51. package/dist/datastar/attributes/class.d.ts +1 -0
  52. package/dist/datastar/attributes/class.js +50 -0
  53. package/dist/datastar/attributes/computed.d.ts +1 -0
  54. package/dist/datastar/attributes/computed.js +27 -0
  55. package/dist/datastar/attributes/effect.d.ts +1 -0
  56. package/dist/datastar/attributes/effect.js +10 -0
  57. package/dist/datastar/attributes/indicator.d.ts +1 -0
  58. package/dist/datastar/attributes/indicator.js +32 -0
  59. package/dist/datastar/attributes/init.d.ts +1 -0
  60. package/dist/datastar/attributes/init.js +27 -0
  61. package/dist/datastar/attributes/jsonSignals.d.ts +1 -0
  62. package/dist/datastar/attributes/jsonSignals.js +31 -0
  63. package/dist/datastar/attributes/on.d.ts +1 -0
  64. package/dist/datastar/attributes/on.js +59 -0
  65. package/dist/datastar/attributes/onIntersect.d.ts +1 -0
  66. package/dist/datastar/attributes/onIntersect.js +54 -0
  67. package/dist/datastar/attributes/onInterval.d.ts +1 -0
  68. package/dist/datastar/attributes/onInterval.js +31 -0
  69. package/dist/datastar/attributes/onSignalPatch.d.ts +1 -0
  70. package/dist/datastar/attributes/onSignalPatch.js +44 -0
  71. package/dist/datastar/attributes/ref.d.ts +1 -0
  72. package/dist/datastar/attributes/ref.js +11 -0
  73. package/dist/datastar/attributes/show.d.ts +1 -0
  74. package/dist/datastar/attributes/show.js +32 -0
  75. package/dist/datastar/attributes/signals.d.ts +1 -0
  76. package/dist/datastar/attributes/signals.js +18 -0
  77. package/dist/datastar/attributes/style.d.ts +1 -0
  78. package/dist/datastar/attributes/style.js +56 -0
  79. package/dist/datastar/attributes/text.d.ts +1 -0
  80. package/dist/datastar/attributes/text.js +27 -0
  81. package/dist/datastar/engine.d.ts +156 -0
  82. package/dist/datastar/engine.js +971 -0
  83. package/dist/datastar/index.d.ts +24 -0
  84. package/dist/datastar/index.js +24 -0
  85. package/dist/datastar/load.d.ts +24 -0
  86. package/dist/datastar/load.js +24 -0
  87. package/dist/datastar/utils.d.ts +51 -0
  88. package/dist/datastar/utils.js +205 -0
  89. package/dist/datastar/watchers/patchElements.d.ts +1 -0
  90. package/dist/datastar/watchers/patchElements.js +420 -0
  91. package/dist/datastar/watchers/patchSignals.d.ts +1 -0
  92. package/dist/datastar/watchers/patchSignals.js +15 -0
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.js +1 -0
  95. package/dist/node/Effectify.d.ts +209 -0
  96. package/dist/node/Effectify.js +19 -0
  97. package/dist/node/FileSystem.d.ts +3 -5
  98. package/dist/node/FileSystem.js +42 -62
  99. package/dist/node/NodeFileSystem.d.ts +7 -0
  100. package/dist/node/NodeFileSystem.js +420 -0
  101. package/dist/node/NodeUtils.d.ts +2 -0
  102. package/dist/node/NodeUtils.js +20 -0
  103. package/dist/node/PlatformError.d.ts +46 -0
  104. package/dist/node/PlatformError.js +43 -0
  105. package/dist/testing/TestLogger.js +1 -1
  106. package/dist/x/tailwind/plugin.js +1 -1
  107. package/package.json +18 -7
  108. package/src/Development.ts +36 -40
  109. package/src/Effectify.ts +269 -0
  110. package/src/FilePathPattern.ts +115 -0
  111. package/src/FileRouter.ts +178 -255
  112. package/src/FileRouterCodegen.ts +135 -92
  113. package/src/PlatformError.ts +117 -0
  114. package/src/PlatformRuntime.ts +108 -0
  115. package/src/Route.ts +31 -2
  116. package/src/RouteBody.ts +1 -1
  117. package/src/RouteHttp.ts +15 -29
  118. package/src/RouteMount.ts +1 -1
  119. package/src/Start.ts +61 -27
  120. package/src/Unique.ts +232 -0
  121. package/src/bun/BunPlatformHttpServer.ts +88 -0
  122. package/src/bun/BunRoute.ts +14 -24
  123. package/src/bun/BunRuntime.ts +21 -5
  124. package/src/bun/BunServer.ts +228 -0
  125. package/src/bun/index.ts +1 -1
  126. package/src/datastar/README.md +18 -0
  127. package/src/datastar/actions/fetch.ts +609 -0
  128. package/src/datastar/actions/peek.ts +17 -0
  129. package/src/datastar/actions/setAll.ts +20 -0
  130. package/src/datastar/actions/toggleAll.ts +20 -0
  131. package/src/datastar/attributes/attr.ts +50 -0
  132. package/src/datastar/attributes/bind.ts +220 -0
  133. package/src/datastar/attributes/class.ts +57 -0
  134. package/src/datastar/attributes/computed.ts +33 -0
  135. package/src/datastar/attributes/effect.ts +11 -0
  136. package/src/datastar/attributes/indicator.ts +39 -0
  137. package/src/datastar/attributes/init.ts +35 -0
  138. package/src/datastar/attributes/jsonSignals.ts +38 -0
  139. package/src/datastar/attributes/on.ts +71 -0
  140. package/src/datastar/attributes/onIntersect.ts +65 -0
  141. package/src/datastar/attributes/onInterval.ts +39 -0
  142. package/src/datastar/attributes/onSignalPatch.ts +63 -0
  143. package/src/datastar/attributes/ref.ts +12 -0
  144. package/src/datastar/attributes/show.ts +33 -0
  145. package/src/datastar/attributes/signals.ts +22 -0
  146. package/src/datastar/attributes/style.ts +63 -0
  147. package/src/datastar/attributes/text.ts +30 -0
  148. package/src/datastar/engine.ts +1341 -0
  149. package/src/datastar/index.ts +25 -0
  150. package/src/datastar/utils.ts +286 -0
  151. package/src/datastar/watchers/patchElements.ts +554 -0
  152. package/src/datastar/watchers/patchSignals.ts +15 -0
  153. package/src/index.ts +1 -0
  154. package/src/node/{FileSystem.ts → NodeFileSystem.ts} +59 -97
  155. package/src/node/{Utils.ts → NodeUtils.ts} +2 -0
  156. package/src/testing/TestLogger.ts +1 -1
  157. package/src/x/tailwind/plugin.ts +1 -1
  158. package/dist/Random.d.ts +0 -5
  159. package/dist/Random.js +0 -49
  160. package/src/Commander.test.ts +0 -1639
  161. package/src/ContentNegotiation.test.ts +0 -603
  162. package/src/Development.test.ts +0 -119
  163. package/src/Entity.test.ts +0 -592
  164. package/src/FileRouterCodegen.todo.ts +0 -1133
  165. package/src/FileRouterPattern.test.ts +0 -147
  166. package/src/FileRouterPattern.ts +0 -59
  167. package/src/FileRouter_files.test.ts +0 -64
  168. package/src/FileRouter_path.test.ts +0 -145
  169. package/src/FileRouter_tree.test.ts +0 -132
  170. package/src/Http.test.ts +0 -319
  171. package/src/HttpAppExtra.test.ts +0 -103
  172. package/src/HttpUtils.test.ts +0 -85
  173. package/src/PathPattern.test.ts +0 -648
  174. package/src/Random.ts +0 -59
  175. package/src/RouteBody.test.ts +0 -232
  176. package/src/RouteHook.test.ts +0 -40
  177. package/src/RouteHttp.test.ts +0 -2909
  178. package/src/RouteMount.test.ts +0 -481
  179. package/src/RouteSchema.test.ts +0 -427
  180. package/src/RouteSse.test.ts +0 -249
  181. package/src/RouteTree.test.ts +0 -494
  182. package/src/RouteTrie.test.ts +0 -322
  183. package/src/RouterPattern.test.ts +0 -676
  184. package/src/RouterPattern.ts +0 -416
  185. package/src/StartApp.ts +0 -47
  186. package/src/Values.test.ts +0 -263
  187. package/src/bun/BunBundle.test.ts +0 -268
  188. package/src/bun/BunBundle_imports.test.ts +0 -48
  189. package/src/bun/BunHttpServer.test.ts +0 -251
  190. package/src/bun/BunHttpServer.ts +0 -306
  191. package/src/bun/BunImportTrackerPlugin.test.ts +0 -77
  192. package/src/bun/BunRoute.test.ts +0 -162
  193. package/src/bundler/BundleHttp.test.ts +0 -132
  194. package/src/effect/HttpRouter.test.ts +0 -548
  195. package/src/experimental/EncryptedCookies.test.ts +0 -488
  196. package/src/hyper/HyperHtml.test.ts +0 -209
  197. package/src/hyper/HyperRoute.test.tsx +0 -197
  198. package/src/middlewares/BasicAuthMiddleware.test.ts +0 -84
  199. package/src/testing/TestHttpClient.test.ts +0 -83
  200. package/src/testing/TestLogger.test.ts +0 -51
  201. package/src/x/datastar/Datastar.test.ts +0 -266
  202. package/src/x/tailwind/TailwindPlugin.test.ts +0 -333
  203. /package/src/bun/{BunHttpServer_web.ts → BunServerRequest.ts} +0 -0
@@ -0,0 +1,1341 @@
1
+ import {
2
+ aliasify,
3
+ hasOwn,
4
+ isHTMLOrSVG,
5
+ isPojo,
6
+ pathToObj,
7
+ snake,
8
+ } from "./utils.ts"
9
+
10
+ /*********
11
+ * consts.ts
12
+ *********/
13
+ const lol = /🖕JS_DS🚀/.source
14
+ export const DSP = lol.slice(0, 5)
15
+ export const DSS = lol.slice(4)
16
+ export const DATASTAR_FETCH_EVENT = "datastar-fetch"
17
+ export const DATASTAR_SIGNAL_PATCH_EVENT = "datastar-signal-patch"
18
+
19
+ /*********
20
+ * types.ts
21
+ *********/
22
+ export type JSONPatch = Record<string, any> & { length?: never }
23
+ export type Paths = [string, any][]
24
+
25
+ export type DatastarFetchEvent = {
26
+ type: string
27
+ el: HTMLOrSVG
28
+ argsRaw: Record<string, string>
29
+ }
30
+
31
+ export type CustomEventMap = {
32
+ [DATASTAR_SIGNAL_PATCH_EVENT]: CustomEvent<JSONPatch>
33
+ }
34
+ export type WatcherFn<K extends keyof CustomEventMap> = (
35
+ this: Document,
36
+ ev: CustomEventMap[K],
37
+ ) => void
38
+
39
+ export type ErrorFn = (name: string, ctx?: Record<string, any>) => void
40
+
41
+ export type ActionContext = {
42
+ el: HTMLOrSVG
43
+ evt?: Event
44
+ error: ErrorFn
45
+ cleanups: Map<string, () => void>
46
+ }
47
+
48
+ export type RequirementType = "allowed" | "must" | "denied" | "exclusive"
49
+
50
+ export type Requirement =
51
+ | RequirementType
52
+ | {
53
+ key: Exclude<RequirementType, "exclusive">
54
+ value?: Exclude<RequirementType, "exclusive">
55
+ }
56
+ | {
57
+ key?: Exclude<RequirementType, "exclusive">
58
+ value: Exclude<RequirementType, "exclusive">
59
+ }
60
+
61
+ type Rx<B extends boolean> = (...args: any[]) => B extends true ? unknown : void
62
+
63
+ type ReqField<R, K extends "key" | "value", Return> = R extends
64
+ | "must"
65
+ | { [P in K]: "must" } ? Return
66
+ : R extends "denied" | { [P in K]: "denied" } ? undefined
67
+ : R extends
68
+ | "allowed"
69
+ | { [P in K]: "allowed" }
70
+ | (K extends keyof R ? never : R) ? Return | undefined
71
+ : never
72
+
73
+ type ReqFields<R extends Requirement, B extends boolean> = R extends "exclusive"
74
+ ?
75
+ | { key: string; value: undefined; rx: undefined }
76
+ | { key: undefined; value: string; rx: Rx<B> }
77
+ : {
78
+ key: ReqField<R, "key", string>
79
+ value: ReqField<R, "value", string>
80
+ rx: ReqField<R, "value", Rx<B>>
81
+ }
82
+
83
+ export type AttributeContext<
84
+ R extends Requirement = Requirement,
85
+ RxReturn extends boolean = boolean,
86
+ > = {
87
+ el: HTMLOrSVG
88
+ mods: Modifiers
89
+ rawKey: string
90
+ evt?: Event
91
+ error: ErrorFn
92
+ loadedPluginNames: {
93
+ actions: Readonly<Set<string>>
94
+ attributes: Readonly<Set<string>>
95
+ }
96
+ } & ReqFields<R, RxReturn>
97
+
98
+ export type AttributePlugin<
99
+ R extends Requirement = Requirement,
100
+ RxReturn extends boolean = boolean,
101
+ > = {
102
+ name: string
103
+ apply: (ctx: AttributeContext<R, RxReturn>) => void | (() => void)
104
+ requirement?: R
105
+ returnsValue?: RxReturn
106
+ argNames?: string[]
107
+ }
108
+
109
+ export type WatcherContext = {
110
+ error: ErrorFn
111
+ }
112
+
113
+ export type WatcherPlugin = {
114
+ name: string
115
+ apply: (ctx: WatcherContext, args: Record<string, string | undefined>) => void
116
+ }
117
+
118
+ export type ActionPlugins = Record<string, ActionPlugin>
119
+
120
+ export type ActionPlugin<T = any> = {
121
+ name: string
122
+ apply: (ctx: ActionContext, ...args: any[]) => T
123
+ }
124
+
125
+ export type MergePatchArgs = {
126
+ ifMissing?: boolean
127
+ }
128
+
129
+ export type HTMLOrSVG = HTMLElement | SVGElement | MathMLElement
130
+ export type Modifiers = Map<string, Set<string>>
131
+
132
+ export type EventCallbackHandler = (...args: any[]) => void
133
+
134
+ export type SignalFilter = RegExp
135
+ export type SignalFilterOptions = {
136
+ include?: RegExp | string
137
+ exclude?: RegExp | string
138
+ }
139
+
140
+ export type Signal<T> = {
141
+ (): T
142
+ (value: T): boolean
143
+ }
144
+
145
+ export type Computed<T> = () => T
146
+
147
+ export type Effect = () => void
148
+
149
+ /*********
150
+ * signals.ts
151
+ *********/
152
+ interface ReactiveNode {
153
+ deps_?: Link
154
+ depsTail_?: Link
155
+ subs_?: Link
156
+ subsTail_?: Link
157
+ flags_: ReactiveFlags
158
+ }
159
+
160
+ interface Link {
161
+ version_: number
162
+ dep_: ReactiveNode
163
+ sub_: ReactiveNode
164
+ prevSub_?: Link
165
+ nextSub_?: Link
166
+ prevDep_?: Link
167
+ nextDep_?: Link
168
+ }
169
+
170
+ interface Stack<T> {
171
+ value_: T
172
+ prev_?: Stack<T>
173
+ }
174
+
175
+ const ReactiveFlags = {
176
+ None: 0,
177
+ Mutable: 1 << 0,
178
+ Watching: 1 << 1,
179
+ RecursedCheck: 1 << 2,
180
+ Recursed: 1 << 3,
181
+ Dirty: 1 << 4,
182
+ Pending: 1 << 5,
183
+ } as const
184
+ type ReactiveFlags = typeof ReactiveFlags[keyof typeof ReactiveFlags]
185
+ type ReactiveFlags_None = typeof ReactiveFlags.None
186
+ type ReactiveFlags_Mutable = typeof ReactiveFlags.Mutable
187
+ type ReactiveFlags_Watching = typeof ReactiveFlags.Watching
188
+ type ReactiveFlags_RecursedCheck = typeof ReactiveFlags.RecursedCheck
189
+ type ReactiveFlags_Recursed = typeof ReactiveFlags.Recursed
190
+ type ReactiveFlags_Dirty = typeof ReactiveFlags.Dirty
191
+ type ReactiveFlags_Pending = typeof ReactiveFlags.Pending
192
+
193
+ const EffectFlags = {
194
+ Queued: 1 << 6,
195
+ } as const
196
+ type EffectFlags_Queued = typeof EffectFlags.Queued
197
+
198
+ interface AlienEffect extends ReactiveNode {
199
+ fn_(): void
200
+ }
201
+
202
+ interface AlienComputed<T = unknown> extends ReactiveNode {
203
+ value_?: T
204
+ getter(previousValue?: T): T
205
+ }
206
+
207
+ interface AlienSignal<T = unknown> extends ReactiveNode {
208
+ previousValue: T
209
+ value_: T
210
+ }
211
+
212
+ const currentPatch: Paths = []
213
+ const queuedEffects: (AlienEffect | undefined)[] = []
214
+ let batchDepth = 0
215
+ let notifyIndex = 0
216
+ let queuedEffectsLength = 0
217
+ let prevSub: ReactiveNode | undefined
218
+ let activeSub: ReactiveNode | undefined
219
+ let version = 0
220
+
221
+ export const beginBatch = (): void => {
222
+ batchDepth++
223
+ }
224
+
225
+ export const endBatch = (): void => {
226
+ if (!--batchDepth) {
227
+ flush()
228
+ dispatch()
229
+ }
230
+ }
231
+
232
+ export const startPeeking = (sub?: ReactiveNode): void => {
233
+ prevSub = activeSub
234
+ activeSub = sub
235
+ }
236
+
237
+ export const stopPeeking = (): void => {
238
+ activeSub = prevSub
239
+ prevSub = undefined
240
+ }
241
+
242
+ export const signal = <T>(initialValue?: T): Signal<T> => {
243
+ return signalOper.bind(0, {
244
+ previousValue: initialValue,
245
+ value_: initialValue,
246
+ flags_: 1 satisfies ReactiveFlags_Mutable,
247
+ }) as Signal<T>
248
+ }
249
+
250
+ const computedSymbol = Symbol("computed")
251
+ export const computed = <T>(getter: (previousValue?: T) => T): Computed<T> => {
252
+ const c = computedOper.bind(0, {
253
+ flags_: 17 as ReactiveFlags_Mutable | ReactiveFlags_Dirty,
254
+ getter,
255
+ }) as Computed<T>
256
+ // @ts-ignore
257
+ c[computedSymbol] = 1
258
+ return c
259
+ }
260
+
261
+ export const effect = (fn: () => void): Effect => {
262
+ const e: AlienEffect = {
263
+ fn_: fn,
264
+ flags_: 2 satisfies ReactiveFlags_Watching,
265
+ }
266
+ if (activeSub) {
267
+ link(e, activeSub)
268
+ }
269
+ startPeeking(e)
270
+ beginBatch()
271
+ try {
272
+ e.fn_()
273
+ } finally {
274
+ endBatch()
275
+ stopPeeking()
276
+ }
277
+ return effectOper.bind(0, e)
278
+ }
279
+
280
+ const flush = () => {
281
+ while (notifyIndex < queuedEffectsLength) {
282
+ const effect = queuedEffects[notifyIndex]!
283
+ queuedEffects[notifyIndex++] = undefined
284
+ run(effect, effect.flags_ &= ~EffectFlags.Queued)
285
+ }
286
+ notifyIndex = 0
287
+ queuedEffectsLength = 0
288
+ }
289
+
290
+ const update = (signal: AlienSignal | AlienComputed): boolean => {
291
+ if ("getter" in signal) {
292
+ return updateComputed(signal)
293
+ }
294
+ return updateSignal(signal, signal.value_)
295
+ }
296
+
297
+ const updateComputed = (c: AlienComputed): boolean => {
298
+ startPeeking(c)
299
+ startTracking(c)
300
+ try {
301
+ const oldValue = c.value_
302
+ return oldValue !== (c.value_ = c.getter(oldValue))
303
+ } finally {
304
+ stopPeeking()
305
+ endTracking(c)
306
+ }
307
+ }
308
+
309
+ const updateSignal = <T>(s: AlienSignal<T>, value: T): boolean => {
310
+ s.flags_ = 1 satisfies ReactiveFlags_Mutable
311
+ return s.previousValue !== (s.previousValue = value)
312
+ }
313
+
314
+ const notify = (e: AlienEffect): void => {
315
+ const flags = e.flags_
316
+ if (!(flags & EffectFlags.Queued)) {
317
+ e.flags_ = flags | EffectFlags.Queued
318
+ const subs = e.subs_
319
+ if (subs) {
320
+ notify(subs.sub_ as AlienEffect)
321
+ } else {
322
+ queuedEffects[queuedEffectsLength++] = e
323
+ }
324
+ }
325
+ }
326
+
327
+ const run = (e: AlienEffect, flags: ReactiveFlags): void => {
328
+ if (
329
+ flags & (16 satisfies ReactiveFlags_Dirty)
330
+ || (flags & (32 satisfies ReactiveFlags_Pending) && checkDirty(e.deps_!, e))
331
+ ) {
332
+ startPeeking(e)
333
+ startTracking(e)
334
+ beginBatch()
335
+ try {
336
+ e.fn_()
337
+ } finally {
338
+ endBatch()
339
+ stopPeeking()
340
+ endTracking(e)
341
+ }
342
+ return
343
+ }
344
+ if (flags & (32 satisfies ReactiveFlags_Pending)) {
345
+ e.flags_ = flags & ~(32 satisfies ReactiveFlags_Pending)
346
+ }
347
+ let link = e.deps_
348
+ while (link) {
349
+ const dep = link.dep_
350
+ const depFlags = dep.flags_
351
+ if (depFlags & EffectFlags.Queued) {
352
+ run(dep as AlienEffect, dep.flags_ = depFlags & ~EffectFlags.Queued)
353
+ }
354
+ link = link.nextDep_
355
+ }
356
+ }
357
+
358
+ const signalOper = <T>(s: AlienSignal<T>, ...value: [T]): T | boolean => {
359
+ if (value.length) {
360
+ if (s.value_ !== (s.value_ = value[0])) {
361
+ s.flags_ = 17 as ReactiveFlags_Mutable | ReactiveFlags_Dirty
362
+ const subs = s.subs_
363
+ if (subs) {
364
+ propagate(subs)
365
+ if (!batchDepth) {
366
+ flush()
367
+ }
368
+ }
369
+ return true
370
+ }
371
+ return false
372
+ }
373
+ const currentValue = s.value_
374
+ if (s.flags_ & (16 satisfies ReactiveFlags_Dirty)) {
375
+ if (updateSignal(s, currentValue)) {
376
+ const subs_ = s.subs_
377
+ if (subs_) {
378
+ shallowPropagate(subs_)
379
+ }
380
+ }
381
+ }
382
+ if (activeSub) {
383
+ link(s, activeSub)
384
+ }
385
+ return currentValue
386
+ }
387
+
388
+ const computedOper = <T>(c: AlienComputed<T>): T => {
389
+ const flags = c.flags_
390
+ if (
391
+ flags & (16 satisfies ReactiveFlags_Dirty)
392
+ || (flags & (32 satisfies ReactiveFlags_Pending) && checkDirty(c.deps_!, c))
393
+ ) {
394
+ if (updateComputed(c)) {
395
+ const subs = c.subs_
396
+ if (subs) {
397
+ shallowPropagate(subs)
398
+ }
399
+ }
400
+ } else if (flags & (32 satisfies ReactiveFlags_Pending)) {
401
+ c.flags_ = flags & ~(32 satisfies ReactiveFlags_Pending)
402
+ }
403
+ if (activeSub) {
404
+ link(c, activeSub)
405
+ }
406
+ return c.value_!
407
+ }
408
+
409
+ const effectOper = (e: AlienEffect): void => {
410
+ let dep = e.deps_
411
+ while (dep) {
412
+ dep = unlink(dep, e)
413
+ }
414
+ const sub = e.subs_
415
+ if (sub) {
416
+ unlink(sub)
417
+ }
418
+ e.flags_ = 0 satisfies ReactiveFlags_None
419
+ }
420
+
421
+ const link = (dep: ReactiveNode, sub: ReactiveNode): void => {
422
+ const prevDep = sub.depsTail_
423
+ if (prevDep && prevDep.dep_ === dep) {
424
+ return
425
+ }
426
+ const nextDep = prevDep ? prevDep.nextDep_ : sub.deps_
427
+ if (nextDep && nextDep.dep_ === dep) {
428
+ nextDep.version_ = version
429
+ sub.depsTail_ = nextDep
430
+ return
431
+ }
432
+ const prevSub = dep.subsTail_
433
+ if (prevSub && prevSub.version_ === version && prevSub.sub_ === sub) {
434
+ return
435
+ }
436
+ const newLink = (sub.depsTail_ =
437
+ dep.subsTail_ =
438
+ {
439
+ version_: version,
440
+ dep_: dep,
441
+ sub_: sub,
442
+ prevDep_: prevDep,
443
+ nextDep_: nextDep,
444
+ prevSub_: prevSub,
445
+ })
446
+ if (nextDep) {
447
+ nextDep.prevDep_ = newLink
448
+ }
449
+ if (prevDep) {
450
+ prevDep.nextDep_ = newLink
451
+ } else {
452
+ sub.deps_ = newLink
453
+ }
454
+ if (prevSub) {
455
+ prevSub.nextSub_ = newLink
456
+ } else {
457
+ dep.subs_ = newLink
458
+ }
459
+ }
460
+
461
+ const unlink = (link: Link, sub = link.sub_): Link | undefined => {
462
+ const dep_ = link.dep_
463
+ const prevDep_ = link.prevDep_
464
+ const nextDep_ = link.nextDep_
465
+ const nextSub_ = link.nextSub_
466
+ const prevSub_ = link.prevSub_
467
+ if (nextDep_) {
468
+ nextDep_.prevDep_ = prevDep_
469
+ } else {
470
+ sub.depsTail_ = prevDep_
471
+ }
472
+ if (prevDep_) {
473
+ prevDep_.nextDep_ = nextDep_
474
+ } else {
475
+ sub.deps_ = nextDep_
476
+ }
477
+ if (nextSub_) {
478
+ nextSub_.prevSub_ = prevSub_
479
+ } else {
480
+ dep_.subsTail_ = prevSub_
481
+ }
482
+ if (prevSub_) {
483
+ prevSub_.nextSub_ = nextSub_
484
+ } else if (!(dep_.subs_ = nextSub_)) {
485
+ if ("getter" in dep_) {
486
+ let toRemove = dep_.deps_
487
+ if (toRemove) {
488
+ dep_.flags_ = 17 as ReactiveFlags_Mutable | ReactiveFlags_Dirty
489
+ do {
490
+ toRemove = unlink(toRemove, dep_)
491
+ } while (toRemove)
492
+ }
493
+ } else if (!("previousValue" in dep_)) {
494
+ effectOper(dep_ as AlienEffect)
495
+ }
496
+ }
497
+ return nextDep_
498
+ }
499
+
500
+ const propagate = (link: Link): void => {
501
+ let next = link.nextSub_
502
+ let stack: Stack<Link | undefined> | undefined
503
+
504
+ top: while (true) {
505
+ const sub = link.sub_
506
+
507
+ let flags = sub.flags_
508
+
509
+ if (
510
+ !(
511
+ flags
512
+ & (60 as
513
+ | ReactiveFlags_RecursedCheck
514
+ | ReactiveFlags_Recursed
515
+ | ReactiveFlags_Dirty
516
+ | ReactiveFlags_Pending)
517
+ )
518
+ ) {
519
+ sub.flags_ = flags | (32 satisfies ReactiveFlags_Pending)
520
+ } else if (
521
+ !(flags & (12 as ReactiveFlags_RecursedCheck | ReactiveFlags_Recursed))
522
+ ) {
523
+ flags = 0 satisfies ReactiveFlags_None
524
+ } else if (!(flags & (4 satisfies ReactiveFlags_RecursedCheck))) {
525
+ sub.flags_ = (flags & ~(8 satisfies ReactiveFlags_Recursed))
526
+ | (32 satisfies ReactiveFlags_Pending)
527
+ } else if (
528
+ !(flags & (48 as ReactiveFlags_Dirty | ReactiveFlags_Pending))
529
+ && isValidLink(link, sub)
530
+ ) {
531
+ sub.flags_ = flags
532
+ | (40 as ReactiveFlags_Recursed | ReactiveFlags_Pending)
533
+ flags &= 1 satisfies ReactiveFlags_Mutable
534
+ } else {
535
+ flags = 0 satisfies ReactiveFlags_None
536
+ }
537
+
538
+ if (flags & (2 satisfies ReactiveFlags_Watching)) {
539
+ notify(sub as AlienEffect)
540
+ }
541
+
542
+ if (flags & (1 satisfies ReactiveFlags_Mutable)) {
543
+ const subSubs = sub.subs_
544
+ if (subSubs) {
545
+ const nextSub = (link = subSubs).nextSub_
546
+ if (nextSub) {
547
+ stack = { value_: next, prev_: stack }
548
+ next = nextSub
549
+ }
550
+ continue
551
+ }
552
+ }
553
+
554
+ if ((link = next!)) {
555
+ next = link.nextSub_
556
+ continue
557
+ }
558
+
559
+ while (stack) {
560
+ link = stack.value_!
561
+ stack = stack.prev_
562
+ if (link) {
563
+ next = link.nextSub_
564
+ continue top
565
+ }
566
+ }
567
+
568
+ break
569
+ }
570
+ }
571
+
572
+ const startTracking = (sub: ReactiveNode): void => {
573
+ version++
574
+ sub.depsTail_ = undefined
575
+ sub.flags_ = (sub.flags_
576
+ & ~(56 as
577
+ | ReactiveFlags_Recursed
578
+ | ReactiveFlags_Dirty
579
+ | ReactiveFlags_Pending))
580
+ | (4 satisfies ReactiveFlags_RecursedCheck)
581
+ }
582
+
583
+ const endTracking = (sub: ReactiveNode): void => {
584
+ const depsTail_ = sub.depsTail_
585
+ let toRemove = depsTail_ ? depsTail_.nextDep_ : sub.deps_
586
+ while (toRemove) {
587
+ toRemove = unlink(toRemove, sub)
588
+ }
589
+ sub.flags_ &= ~(4 satisfies ReactiveFlags_RecursedCheck)
590
+ }
591
+
592
+ const checkDirty = (link: Link, sub: ReactiveNode): boolean => {
593
+ let stack: Stack<Link> | undefined
594
+ let checkDepth = 0
595
+ let dirty = false
596
+
597
+ top: while (true) {
598
+ const dep = link.dep_
599
+ const flags = dep.flags_
600
+
601
+ if (sub.flags_ & (16 satisfies ReactiveFlags_Dirty)) {
602
+ dirty = true
603
+ } else if (
604
+ (flags & (17 as ReactiveFlags_Mutable | ReactiveFlags_Dirty))
605
+ === (17 as ReactiveFlags_Mutable | ReactiveFlags_Dirty)
606
+ ) {
607
+ if (update(dep as AlienSignal | AlienComputed)) {
608
+ const subs = dep.subs_!
609
+ if (subs.nextSub_) {
610
+ shallowPropagate(subs)
611
+ }
612
+ dirty = true
613
+ }
614
+ } else if (
615
+ (flags & (33 as ReactiveFlags_Mutable | ReactiveFlags_Pending))
616
+ === (33 as ReactiveFlags_Mutable | ReactiveFlags_Pending)
617
+ ) {
618
+ if (link.nextSub_ || link.prevSub_) {
619
+ stack = { value_: link, prev_: stack }
620
+ }
621
+ link = dep.deps_!
622
+ sub = dep
623
+ ++checkDepth
624
+ continue
625
+ }
626
+
627
+ if (!dirty) {
628
+ const nextDep = link.nextDep_
629
+ if (nextDep) {
630
+ link = nextDep
631
+ continue
632
+ }
633
+ }
634
+
635
+ while (checkDepth--) {
636
+ const firstSub = sub.subs_!
637
+ const hasMultipleSubs = firstSub.nextSub_
638
+ if (hasMultipleSubs) {
639
+ link = stack!.value_
640
+ stack = stack!.prev_
641
+ } else {
642
+ link = firstSub
643
+ }
644
+ if (dirty) {
645
+ if (update(sub as AlienSignal | AlienComputed)) {
646
+ if (hasMultipleSubs) {
647
+ shallowPropagate(firstSub)
648
+ }
649
+ sub = link.sub_
650
+ continue
651
+ }
652
+ dirty = false
653
+ } else {
654
+ sub.flags_ &= ~(32 satisfies ReactiveFlags_Pending)
655
+ }
656
+ sub = link.sub_
657
+ if (link.nextDep_) {
658
+ link = link.nextDep_
659
+ continue top
660
+ }
661
+ }
662
+
663
+ return dirty
664
+ }
665
+ }
666
+
667
+ const shallowPropagate = (link: Link): void => {
668
+ do {
669
+ const sub = link.sub_
670
+ const flags = sub.flags_
671
+ if (
672
+ (flags & (48 as ReactiveFlags_Pending | ReactiveFlags_Dirty))
673
+ === (32 satisfies ReactiveFlags_Pending)
674
+ ) {
675
+ sub.flags_ = flags | (16 satisfies ReactiveFlags_Dirty)
676
+ if (flags & (2 satisfies ReactiveFlags_Watching)) {
677
+ notify(sub as AlienEffect)
678
+ }
679
+ }
680
+ } while ((link = link.nextSub_!))
681
+ }
682
+
683
+ const isValidLink = (checkLink: Link, sub: ReactiveNode): boolean => {
684
+ let link = sub.depsTail_
685
+ while (link) {
686
+ if (link === checkLink) {
687
+ return true
688
+ }
689
+ link = link.prevDep_
690
+ }
691
+ return false
692
+ }
693
+
694
+ export const getPath = <T = any>(path: string): T | undefined => {
695
+ let result = root
696
+ const split = path.split(".")
697
+ for (const path of split) {
698
+ if (result == null || !hasOwn(result, path)) {
699
+ return
700
+ }
701
+ result = result[path]
702
+ }
703
+ return result as T
704
+ }
705
+
706
+ const deep = (value: any, prefix = ""): any => {
707
+ const isArr = Array.isArray(value)
708
+ if (isArr || isPojo(value)) {
709
+ const deepObj = (isArr ? [] : {}) as Record<string, Signal<any>>
710
+ for (const key in value) {
711
+ deepObj[key] = signal(
712
+ deep((value as Record<string, Signal<any>>)[key], `${prefix + key}.`),
713
+ )
714
+ }
715
+ const keys = signal(0)
716
+ return new Proxy(deepObj, {
717
+ get(_, prop: string) {
718
+ if (!(prop === "toJSON" && !hasOwn(deepObj, prop))) {
719
+ if (isArr && prop in Array.prototype) {
720
+ keys()
721
+ return deepObj[prop]
722
+ }
723
+ if (typeof prop === "symbol") {
724
+ return deepObj[prop]
725
+ }
726
+ if (!hasOwn(deepObj, prop) || deepObj[prop]() == null) {
727
+ deepObj[prop] = signal("")
728
+ dispatch(prefix + prop, "")
729
+ keys(keys() + 1)
730
+ }
731
+ return deepObj[prop]()
732
+ }
733
+ },
734
+ set(_, prop: string, newValue) {
735
+ const path = prefix + prop
736
+ if (isArr && prop === "length") {
737
+ const diff = (deepObj[prop] as unknown as number) - newValue
738
+ deepObj[prop] = newValue
739
+ if (diff > 0) {
740
+ const patch: Record<string, any> = {}
741
+ for (let i = newValue; i < deepObj[prop]; i++) {
742
+ patch[i] = null
743
+ }
744
+ dispatch(prefix.slice(0, -1), patch)
745
+ keys(keys() + 1)
746
+ }
747
+ } else if (hasOwn(deepObj, prop)) {
748
+ if (newValue == null) {
749
+ delete deepObj[prop]
750
+ } else if (hasOwn(newValue, computedSymbol)) {
751
+ deepObj[prop] = newValue
752
+ dispatch(path, "")
753
+ } else {
754
+ const currentValue = deepObj[prop]()
755
+ const pathStr = `${path}.`
756
+ if (isPojo(currentValue) && isPojo(newValue)) {
757
+ for (const key in currentValue) {
758
+ if (!hasOwn(newValue, key)) {
759
+ delete currentValue[key]
760
+ dispatch(pathStr + key, null)
761
+ }
762
+ }
763
+ for (const key in newValue) {
764
+ const nextVal = newValue[key]
765
+ if (currentValue[key] !== nextVal) {
766
+ currentValue[key] = nextVal
767
+ }
768
+ }
769
+ } else if (deepObj[prop](deep(newValue, pathStr))) {
770
+ dispatch(path, newValue)
771
+ }
772
+ }
773
+ } else if (newValue != null) {
774
+ if (hasOwn(newValue, computedSymbol)) {
775
+ deepObj[prop] = newValue
776
+ dispatch(path, "")
777
+ } else {
778
+ deepObj[prop] = signal(deep(newValue, `${path}.`))
779
+ dispatch(path, newValue)
780
+ }
781
+ keys(keys() + 1)
782
+ }
783
+
784
+ return true
785
+ },
786
+ deleteProperty(_, prop: string) {
787
+ delete deepObj[prop]
788
+ keys(keys() + 1)
789
+ return true
790
+ },
791
+ ownKeys() {
792
+ keys()
793
+ return Reflect.ownKeys(deepObj)
794
+ },
795
+ has(_, prop) {
796
+ keys()
797
+ return prop in deepObj
798
+ },
799
+ })
800
+ }
801
+ return value
802
+ }
803
+
804
+ const dispatch = (path?: string, value?: any) => {
805
+ if (path !== undefined && value !== undefined) {
806
+ currentPatch.push([path, value])
807
+ }
808
+ if (!batchDepth && currentPatch.length) {
809
+ const detail = pathToObj(currentPatch)
810
+ currentPatch.length = 0
811
+ document.dispatchEvent(
812
+ new CustomEvent<JSONPatch>(DATASTAR_SIGNAL_PATCH_EVENT, {
813
+ detail,
814
+ }),
815
+ )
816
+ }
817
+ }
818
+
819
+ export const mergePatch = (
820
+ patch: JSONPatch,
821
+ { ifMissing }: MergePatchArgs = {},
822
+ ): void => {
823
+ beginBatch()
824
+ for (const key in patch) {
825
+ if (patch[key] == null) {
826
+ if (!ifMissing) {
827
+ delete root[key]
828
+ }
829
+ } else {
830
+ mergeInner(patch[key], key, root, "", ifMissing)
831
+ }
832
+ }
833
+ endBatch()
834
+ }
835
+
836
+ export const mergePaths = (paths: Paths, options?: MergePatchArgs): void =>
837
+ mergePatch(pathToObj(paths), options)
838
+
839
+ const mergeInner = (
840
+ patch: any,
841
+ target: string,
842
+ targetParent: Record<string, any>,
843
+ prefix: string,
844
+ ifMissing: boolean | undefined,
845
+ ): void => {
846
+ if (isPojo(patch)) {
847
+ if (
848
+ !(
849
+ hasOwn(targetParent, target)
850
+ && (isPojo(targetParent[target]) || Array.isArray(targetParent[target]))
851
+ )
852
+ ) {
853
+ targetParent[target] = {}
854
+ }
855
+
856
+ for (const key in patch) {
857
+ if (patch[key] == null) {
858
+ if (!ifMissing) {
859
+ delete targetParent[target][key]
860
+ }
861
+ } else {
862
+ mergeInner(
863
+ patch[key],
864
+ key,
865
+ targetParent[target],
866
+ `${prefix + target}.`,
867
+ ifMissing,
868
+ )
869
+ }
870
+ }
871
+ } else if (!(ifMissing && hasOwn(targetParent, target))) {
872
+ targetParent[target] = patch
873
+ }
874
+ }
875
+
876
+ const toRegExp = (val: string | RegExp): RegExp =>
877
+ typeof val === "string" ? RegExp(val.replace(/^\/|\/$/g, "")) : val
878
+
879
+ export const filtered = (
880
+ { include = /.*/, exclude = /(?!)/ }: SignalFilterOptions = {},
881
+ obj: JSONPatch = root,
882
+ ): Record<string, any> => {
883
+ const includeRe = toRegExp(include)
884
+ const excludeRe = toRegExp(exclude)
885
+ const paths: Paths = []
886
+ const stack: [any, string][] = [[obj, ""]]
887
+
888
+ while (stack.length) {
889
+ const [node, prefix] = stack.pop()!
890
+
891
+ for (const key in node) {
892
+ const path = prefix + key
893
+ if (isPojo(node[key])) {
894
+ stack.push([node[key], `${path}.`])
895
+ } else if (includeRe.test(path) && !excludeRe.test(path)) {
896
+ paths.push([path, getPath(path)])
897
+ }
898
+ }
899
+ }
900
+
901
+ return pathToObj(paths)
902
+ }
903
+
904
+ export const root: Record<string, any> = deep({})
905
+
906
+ /*********
907
+ * engine.ts (plugin system)
908
+ *********/
909
+ const url = "https://data-star.dev/errors"
910
+
911
+ const error = (
912
+ ctx: Record<string, any>,
913
+ reason: string,
914
+ metadata: Record<string, any> = {},
915
+ ) => {
916
+ Object.assign(metadata, ctx)
917
+ const e = new Error()
918
+ const r = snake(reason)
919
+ const q = new URLSearchParams({
920
+ metadata: JSON.stringify(metadata),
921
+ })
922
+ .toString()
923
+ const c = JSON.stringify(metadata, null, 2)
924
+ e.message = `${reason}\nMore info: ${url}/${r}?${q}\nContext: ${c}`
925
+ return e
926
+ }
927
+
928
+ const actionPlugins: Map<string, ActionPlugin> = new Map()
929
+ const attributePlugins: Map<string, AttributePlugin> = new Map()
930
+ const watcherPlugins: Map<string, WatcherPlugin> = new Map()
931
+
932
+ export const actions: Record<
933
+ string,
934
+ (ctx: ActionContext, ...args: any[]) => any
935
+ > = new Proxy(
936
+ {},
937
+ {
938
+ get: (_, prop: string) => actionPlugins.get(prop)?.apply,
939
+ has: (_, prop: string) => actionPlugins.has(prop),
940
+ ownKeys: () => Reflect.ownKeys(actionPlugins),
941
+ set: () => false,
942
+ deleteProperty: () => false,
943
+ },
944
+ )
945
+
946
+ const removals = new Map<HTMLOrSVG, Map<string, Map<string, () => void>>>()
947
+
948
+ const queuedAttributes: AttributePlugin[] = []
949
+ const queuedAttributeNames = new Set<string>()
950
+ const observedRoots = new WeakSet<Node>()
951
+ export const attribute = <R extends Requirement, B extends boolean>(
952
+ plugin: AttributePlugin<R, B>,
953
+ ): void => {
954
+ queuedAttributes.push(plugin as unknown as AttributePlugin)
955
+
956
+ if (queuedAttributes.length === 1) {
957
+ setTimeout(() => {
958
+ for (const attribute of queuedAttributes) {
959
+ queuedAttributeNames.add(attribute.name)
960
+ attributePlugins.set(attribute.name, attribute)
961
+ }
962
+ queuedAttributes.length = 0
963
+ apply()
964
+ queuedAttributeNames.clear()
965
+ })
966
+ }
967
+ }
968
+
969
+ export const action = <T>(plugin: ActionPlugin<T>): void => {
970
+ actionPlugins.set(plugin.name, plugin)
971
+ }
972
+
973
+ document.addEventListener(
974
+ DATASTAR_FETCH_EVENT,
975
+ ((
976
+ evt: CustomEvent<DatastarFetchEvent>,
977
+ ) => {
978
+ const plugin = watcherPlugins.get(evt.detail.type)
979
+ if (plugin) {
980
+ plugin.apply(
981
+ {
982
+ error: error.bind(0, {
983
+ plugin: { type: "watcher", name: plugin.name },
984
+ element: {
985
+ id: (evt.target as Element).id,
986
+ tag: (evt.target as Element).tagName,
987
+ },
988
+ }),
989
+ },
990
+ evt.detail.argsRaw,
991
+ )
992
+ }
993
+ }) as EventListener,
994
+ )
995
+
996
+ export const watcher = (plugin: WatcherPlugin): void => {
997
+ watcherPlugins.set(plugin.name, plugin)
998
+ }
999
+
1000
+ const cleanupEls = (els: Iterable<HTMLOrSVG>): void => {
1001
+ for (const el of els) {
1002
+ const elCleanups = removals.get(el)
1003
+ if (elCleanups && removals.delete(el)) {
1004
+ for (const attrCleanups of elCleanups.values()) {
1005
+ for (const cleanup of attrCleanups.values()) {
1006
+ cleanup()
1007
+ }
1008
+ }
1009
+ }
1010
+ }
1011
+ }
1012
+
1013
+ const aliasedIgnore = aliasify("ignore")
1014
+ const aliasedIgnoreAttr = `[${aliasedIgnore}]`
1015
+ const shouldIgnore = (el: HTMLOrSVG) =>
1016
+ el.hasAttribute(`${aliasedIgnore}__self`) || !!el.closest(aliasedIgnoreAttr)
1017
+
1018
+ const applyEls = (els: Iterable<HTMLOrSVG>, onlyNew?: boolean): void => {
1019
+ for (const el of els) {
1020
+ if (!shouldIgnore(el)) {
1021
+ for (const key in el.dataset) {
1022
+ applyAttributePlugin(
1023
+ el,
1024
+ key.replace(/[A-Z]/g, "-$&").toLowerCase(),
1025
+ el.dataset[key]!,
1026
+ onlyNew,
1027
+ )
1028
+ }
1029
+ }
1030
+ }
1031
+ }
1032
+
1033
+ const observe = (mutations: MutationRecord[]) => {
1034
+ for (
1035
+ const {
1036
+ target,
1037
+ type,
1038
+ attributeName,
1039
+ addedNodes,
1040
+ removedNodes,
1041
+ } of mutations
1042
+ ) {
1043
+ if (type === "childList") {
1044
+ for (const node of removedNodes) {
1045
+ if (isHTMLOrSVG(node)) {
1046
+ cleanupEls([node])
1047
+ cleanupEls(node.querySelectorAll<HTMLOrSVG>("*"))
1048
+ }
1049
+ }
1050
+
1051
+ for (const node of addedNodes) {
1052
+ if (isHTMLOrSVG(node)) {
1053
+ applyEls([node])
1054
+ applyEls(node.querySelectorAll<HTMLOrSVG>("*"))
1055
+ }
1056
+ }
1057
+ } else if (
1058
+ type === "attributes"
1059
+ && attributeName!.startsWith("data-")
1060
+ && isHTMLOrSVG(target)
1061
+ && !shouldIgnore(target)
1062
+ ) {
1063
+ const key = attributeName!.slice(5)
1064
+ const value = target.getAttribute(attributeName!)
1065
+ if (value === null) {
1066
+ const elCleanups = removals.get(target)
1067
+ if (elCleanups) {
1068
+ const attrCleanups = elCleanups.get(key)
1069
+ if (attrCleanups) {
1070
+ for (const cleanup of attrCleanups.values()) {
1071
+ cleanup()
1072
+ }
1073
+ elCleanups.delete(key)
1074
+ }
1075
+ }
1076
+ } else {
1077
+ applyAttributePlugin(target, key, value)
1078
+ }
1079
+ }
1080
+ }
1081
+ }
1082
+
1083
+ const mutationObserver = new MutationObserver(observe)
1084
+
1085
+ export const parseAttributeKey = (
1086
+ rawKey: string,
1087
+ ): {
1088
+ pluginName: string
1089
+ key: string | undefined
1090
+ mods: Modifiers
1091
+ } => {
1092
+ const [namePart, ...rawModifiers] = rawKey.split("__")
1093
+ const [pluginName, key] = namePart.split(/:(.+)/)
1094
+ const mods: Modifiers = new Map()
1095
+
1096
+ for (const rawMod of rawModifiers) {
1097
+ const [label, ...mod] = rawMod.split(".")
1098
+ mods.set(label, new Set(mod))
1099
+ }
1100
+
1101
+ return { pluginName, key, mods }
1102
+ }
1103
+
1104
+ export const isDocumentObserverActive = () =>
1105
+ observedRoots.has(document.documentElement)
1106
+
1107
+ export const apply = (
1108
+ root: HTMLOrSVG | ShadowRoot = document.documentElement,
1109
+ observeRoot = true,
1110
+ ): void => {
1111
+ if (isHTMLOrSVG(root)) {
1112
+ applyEls([root], true)
1113
+ }
1114
+ applyEls(root.querySelectorAll<HTMLOrSVG>("*"), true)
1115
+
1116
+ if (observeRoot) {
1117
+ mutationObserver.observe(root, {
1118
+ subtree: true,
1119
+ childList: true,
1120
+ attributes: true,
1121
+ })
1122
+ observedRoots.add(root)
1123
+ }
1124
+ }
1125
+
1126
+ const applyAttributePlugin = (
1127
+ el: HTMLOrSVG,
1128
+ attrKey: string,
1129
+ value: string,
1130
+ onlyNew?: boolean,
1131
+ ): void => {
1132
+ const rawKey = attrKey
1133
+ const { pluginName, key, mods } = parseAttributeKey(rawKey)
1134
+ const plugin = attributePlugins.get(pluginName)
1135
+ if ((!onlyNew || queuedAttributeNames.has(pluginName)) && plugin) {
1136
+ const ctx = {
1137
+ el,
1138
+ rawKey,
1139
+ mods,
1140
+ error: error.bind(0, {
1141
+ plugin: { type: "attribute", name: plugin.name },
1142
+ element: { id: el.id, tag: el.tagName },
1143
+ expression: { rawKey, key, value },
1144
+ }),
1145
+ key,
1146
+ value,
1147
+ loadedPluginNames: {
1148
+ actions: new Set(actionPlugins.keys()),
1149
+ attributes: new Set(attributePlugins.keys()),
1150
+ },
1151
+ rx: undefined,
1152
+ } as AttributeContext
1153
+
1154
+ const keyReq = (plugin.requirement
1155
+ && (typeof plugin.requirement === "string"
1156
+ ? plugin.requirement
1157
+ : plugin.requirement.key))
1158
+ || "allowed"
1159
+ const valueReq = (plugin.requirement
1160
+ && (typeof plugin.requirement === "string"
1161
+ ? plugin.requirement
1162
+ : plugin.requirement.value))
1163
+ || "allowed"
1164
+
1165
+ const keyProvided = key !== undefined && key !== null && key !== ""
1166
+ const valueProvided = value !== undefined && value !== null && value !== ""
1167
+
1168
+ if (keyProvided) {
1169
+ if (keyReq === "denied") {
1170
+ throw ctx.error("KeyNotAllowed")
1171
+ }
1172
+ } else if (keyReq === "must") {
1173
+ throw ctx.error("KeyRequired")
1174
+ }
1175
+
1176
+ if (valueProvided) {
1177
+ if (valueReq === "denied") {
1178
+ throw ctx.error("ValueNotAllowed")
1179
+ }
1180
+ } else if (valueReq === "must") {
1181
+ throw ctx.error("ValueRequired")
1182
+ }
1183
+
1184
+ if (keyReq === "exclusive" || valueReq === "exclusive") {
1185
+ if (keyProvided && valueProvided) {
1186
+ throw ctx.error("KeyAndValueProvided")
1187
+ }
1188
+ if (!keyProvided && !valueProvided) {
1189
+ throw ctx.error("KeyOrValueRequired")
1190
+ }
1191
+ }
1192
+
1193
+ const cleanups = new Map<string, () => void>()
1194
+ if (valueProvided) {
1195
+ let cachedRx: GenRxFn
1196
+ ctx.rx = (...args: any[]) => {
1197
+ if (!cachedRx) {
1198
+ cachedRx = genRx(value, {
1199
+ returnsValue: plugin.returnsValue,
1200
+ argNames: plugin.argNames,
1201
+ cleanups,
1202
+ })
1203
+ }
1204
+ return cachedRx(el, ...args)
1205
+ }
1206
+ }
1207
+
1208
+ const cleanup = plugin.apply(ctx)
1209
+ if (cleanup) {
1210
+ cleanups.set("attribute", cleanup)
1211
+ }
1212
+
1213
+ let elCleanups = removals.get(el)
1214
+ if (elCleanups) {
1215
+ const attrCleanups = elCleanups.get(rawKey)
1216
+ if (attrCleanups) {
1217
+ for (const oldCleanup of attrCleanups.values()) {
1218
+ oldCleanup()
1219
+ }
1220
+ }
1221
+ } else {
1222
+ elCleanups = new Map()
1223
+ removals.set(el, elCleanups)
1224
+ }
1225
+ elCleanups.set(rawKey, cleanups)
1226
+ }
1227
+ }
1228
+
1229
+ type GenRxOptions = {
1230
+ returnsValue?: boolean
1231
+ argNames?: string[]
1232
+ cleanups?: Map<string, () => void>
1233
+ }
1234
+
1235
+ type GenRxFn = <T>(el: HTMLOrSVG, ...args: any[]) => T
1236
+
1237
+ const genRx = (
1238
+ value: string,
1239
+ {
1240
+ returnsValue = false,
1241
+ argNames = [],
1242
+ cleanups = new Map(),
1243
+ }: GenRxOptions = {},
1244
+ ): GenRxFn => {
1245
+ let expr = ""
1246
+ if (returnsValue) {
1247
+ const statementRe =
1248
+ /(\/(\\\/|[^/])*\/|"(\\"|[^"])*"|'(\\'|[^'])*'|`(\\`|[^`])*`|\(\s*((function)\s*\(\s*\)|(\(\s*\))\s*=>)\s*(?:\{[\s\S]*?\}|[^;){]*)\s*\)\s*\(\s*\)|[^;])+/gm
1249
+ const statements = value.trim().match(statementRe)
1250
+ if (statements) {
1251
+ const lastIdx = statements.length - 1
1252
+ const last = statements[lastIdx].trim()
1253
+ if (!last.startsWith("return")) {
1254
+ statements[lastIdx] = `return (${last});`
1255
+ }
1256
+ expr = statements.join(";\n")
1257
+ }
1258
+ } else {
1259
+ expr = value.trim()
1260
+ }
1261
+
1262
+ const escaped = new Map<string, string>()
1263
+ const escapeRe = RegExp(`(?:${DSP})(.*?)(?:${DSS})`, "gm")
1264
+ let counter = 0
1265
+ for (const match of expr.matchAll(escapeRe)) {
1266
+ const k = match[1]
1267
+ const v = `__escaped${counter++}`
1268
+ escaped.set(v, k)
1269
+ expr = expr.replace(DSP + k + DSS, v)
1270
+ }
1271
+
1272
+ expr = expr
1273
+ .replace(/\$\['([a-zA-Z_$\d][\w$]*)'\]/g, "$$$1")
1274
+ .replace(/\$([a-zA-Z_\d]\w*(?:[.-]\w+)*)/g, (_, signalName) =>
1275
+ signalName
1276
+ .split(".")
1277
+ .reduce((acc: string, part: string) => `${acc}['${part}']`, "$"))
1278
+
1279
+ expr = expr.replaceAll(/@([A-Za-z_$][\w$]*)\(/g, "__action(\"$1\",evt,")
1280
+
1281
+ for (const [k, v] of escaped) {
1282
+ expr = expr.replace(k, v)
1283
+ }
1284
+
1285
+ try {
1286
+ const fn = Function("el", "$", "__action", "evt", ...argNames, expr)
1287
+ return (el: HTMLOrSVG, ...args: any[]) => {
1288
+ const action = (name: string, evt: Event | undefined, ...args: any[]) => {
1289
+ const err = error.bind(0, {
1290
+ plugin: { type: "action", name },
1291
+ element: { id: el.id, tag: el.tagName },
1292
+ expression: {
1293
+ fnContent: expr,
1294
+ value,
1295
+ },
1296
+ })
1297
+ const fn = actions[name]
1298
+ if (fn) {
1299
+ return fn(
1300
+ {
1301
+ el,
1302
+ evt,
1303
+ error: err,
1304
+ cleanups,
1305
+ },
1306
+ ...args,
1307
+ )
1308
+ }
1309
+ throw err("UndefinedAction")
1310
+ }
1311
+ try {
1312
+ return fn(el, root, action, undefined, ...args)
1313
+ } catch (e: any) {
1314
+ console.error(e)
1315
+ throw error(
1316
+ {
1317
+ element: { id: el.id, tag: el.tagName },
1318
+ expression: {
1319
+ fnContent: expr,
1320
+ value,
1321
+ },
1322
+ error: e.message,
1323
+ },
1324
+ "ExecuteExpression",
1325
+ )
1326
+ }
1327
+ }
1328
+ } catch (e: any) {
1329
+ console.error(e)
1330
+ throw error(
1331
+ {
1332
+ expression: {
1333
+ fnContent: expr,
1334
+ value,
1335
+ },
1336
+ error: e.message,
1337
+ },
1338
+ "GenerateExpression",
1339
+ )
1340
+ }
1341
+ }