@bquery/bquery 1.2.0 → 1.4.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 (309) hide show
  1. package/README.md +127 -27
  2. package/dist/batch-x7b2eZST.js +13 -0
  3. package/dist/batch-x7b2eZST.js.map +1 -0
  4. package/dist/component/component.d.ts +69 -0
  5. package/dist/component/component.d.ts.map +1 -0
  6. package/dist/component/html.d.ts +35 -0
  7. package/dist/component/html.d.ts.map +1 -0
  8. package/dist/component/index.d.ts +3 -126
  9. package/dist/component/index.d.ts.map +1 -1
  10. package/dist/component/props.d.ts +18 -0
  11. package/dist/component/props.d.ts.map +1 -0
  12. package/dist/component/types.d.ts +77 -0
  13. package/dist/component/types.d.ts.map +1 -0
  14. package/dist/component.es.mjs +90 -59
  15. package/dist/component.es.mjs.map +1 -1
  16. package/dist/core/collection.d.ts +55 -3
  17. package/dist/core/collection.d.ts.map +1 -1
  18. package/dist/core/dom.d.ts +6 -0
  19. package/dist/core/dom.d.ts.map +1 -0
  20. package/dist/core/element.d.ts +31 -4
  21. package/dist/core/element.d.ts.map +1 -1
  22. package/dist/core/index.d.ts +2 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/utils/array.d.ts +74 -0
  25. package/dist/core/utils/array.d.ts.map +1 -0
  26. package/dist/core/utils/function.d.ts +87 -0
  27. package/dist/core/utils/function.d.ts.map +1 -0
  28. package/dist/core/utils/index.d.ts +70 -0
  29. package/dist/core/utils/index.d.ts.map +1 -0
  30. package/dist/core/utils/misc.d.ts +63 -0
  31. package/dist/core/utils/misc.d.ts.map +1 -0
  32. package/dist/core/utils/number.d.ts +65 -0
  33. package/dist/core/utils/number.d.ts.map +1 -0
  34. package/dist/core/utils/object.d.ts +133 -0
  35. package/dist/core/utils/object.d.ts.map +1 -0
  36. package/dist/core/utils/string.d.ts +80 -0
  37. package/dist/core/utils/string.d.ts.map +1 -0
  38. package/dist/core/utils/type-guards.d.ts +79 -0
  39. package/dist/core/utils/type-guards.d.ts.map +1 -0
  40. package/dist/core-BhpuvPhy.js +170 -0
  41. package/dist/core-BhpuvPhy.js.map +1 -0
  42. package/dist/core.es.mjs +495 -489
  43. package/dist/core.es.mjs.map +1 -1
  44. package/dist/full.d.ts +2 -2
  45. package/dist/full.d.ts.map +1 -1
  46. package/dist/full.es.mjs +87 -64
  47. package/dist/full.es.mjs.map +1 -1
  48. package/dist/full.iife.js +2 -2
  49. package/dist/full.iife.js.map +1 -1
  50. package/dist/full.umd.js +2 -2
  51. package/dist/full.umd.js.map +1 -1
  52. package/dist/index.es.mjs +138 -68
  53. package/dist/index.es.mjs.map +1 -1
  54. package/dist/motion/animate.d.ts +25 -0
  55. package/dist/motion/animate.d.ts.map +1 -0
  56. package/dist/motion/easing.d.ts +30 -0
  57. package/dist/motion/easing.d.ts.map +1 -0
  58. package/dist/motion/flip.d.ts +55 -0
  59. package/dist/motion/flip.d.ts.map +1 -0
  60. package/dist/motion/index.d.ts +11 -138
  61. package/dist/motion/index.d.ts.map +1 -1
  62. package/dist/motion/keyframes.d.ts +21 -0
  63. package/dist/motion/keyframes.d.ts.map +1 -0
  64. package/dist/motion/reduced-motion.d.ts +12 -0
  65. package/dist/motion/reduced-motion.d.ts.map +1 -0
  66. package/dist/motion/scroll.d.ts +15 -0
  67. package/dist/motion/scroll.d.ts.map +1 -0
  68. package/dist/motion/spring.d.ts +42 -0
  69. package/dist/motion/spring.d.ts.map +1 -0
  70. package/dist/motion/stagger.d.ts +22 -0
  71. package/dist/motion/stagger.d.ts.map +1 -0
  72. package/dist/motion/timeline.d.ts +21 -0
  73. package/dist/motion/timeline.d.ts.map +1 -0
  74. package/dist/motion/transition.d.ts +22 -0
  75. package/dist/motion/transition.d.ts.map +1 -0
  76. package/dist/motion/types.d.ts +182 -0
  77. package/dist/motion/types.d.ts.map +1 -0
  78. package/dist/motion.es.mjs +320 -61
  79. package/dist/motion.es.mjs.map +1 -1
  80. package/dist/persisted-DHoi3uEs.js +278 -0
  81. package/dist/persisted-DHoi3uEs.js.map +1 -0
  82. package/dist/platform/storage.d.ts.map +1 -1
  83. package/dist/platform.es.mjs +12 -7
  84. package/dist/platform.es.mjs.map +1 -1
  85. package/dist/reactive/batch.d.ts +13 -0
  86. package/dist/reactive/batch.d.ts.map +1 -0
  87. package/dist/reactive/computed.d.ts +50 -0
  88. package/dist/reactive/computed.d.ts.map +1 -0
  89. package/dist/reactive/core.d.ts +72 -0
  90. package/dist/reactive/core.d.ts.map +1 -0
  91. package/dist/reactive/effect.d.ts +15 -0
  92. package/dist/reactive/effect.d.ts.map +1 -0
  93. package/dist/reactive/index.d.ts +2 -2
  94. package/dist/reactive/index.d.ts.map +1 -1
  95. package/dist/reactive/internals.d.ts +42 -0
  96. package/dist/reactive/internals.d.ts.map +1 -0
  97. package/dist/reactive/linked.d.ts +36 -0
  98. package/dist/reactive/linked.d.ts.map +1 -0
  99. package/dist/reactive/persisted.d.ts +14 -0
  100. package/dist/reactive/persisted.d.ts.map +1 -0
  101. package/dist/reactive/readonly.d.ts +26 -0
  102. package/dist/reactive/readonly.d.ts.map +1 -0
  103. package/dist/reactive/signal.d.ts +13 -312
  104. package/dist/reactive/signal.d.ts.map +1 -1
  105. package/dist/reactive/type-guards.d.ts +20 -0
  106. package/dist/reactive/type-guards.d.ts.map +1 -0
  107. package/dist/reactive/untrack.d.ts +29 -0
  108. package/dist/reactive/untrack.d.ts.map +1 -0
  109. package/dist/reactive/watch.d.ts +42 -0
  110. package/dist/reactive/watch.d.ts.map +1 -0
  111. package/dist/reactive.es.mjs +30 -163
  112. package/dist/reactive.es.mjs.map +1 -1
  113. package/dist/router/index.d.ts +6 -252
  114. package/dist/router/index.d.ts.map +1 -1
  115. package/dist/router/links.d.ts +44 -0
  116. package/dist/router/links.d.ts.map +1 -0
  117. package/dist/router/match.d.ts +20 -0
  118. package/dist/router/match.d.ts.map +1 -0
  119. package/dist/router/navigation.d.ts +45 -0
  120. package/dist/router/navigation.d.ts.map +1 -0
  121. package/dist/router/query.d.ts +16 -0
  122. package/dist/router/query.d.ts.map +1 -0
  123. package/dist/router/router.d.ts +34 -0
  124. package/dist/router/router.d.ts.map +1 -0
  125. package/dist/router/state.d.ts +27 -0
  126. package/dist/router/state.d.ts.map +1 -0
  127. package/dist/router/types.d.ts +88 -0
  128. package/dist/router/types.d.ts.map +1 -0
  129. package/dist/router/utils.d.ts +65 -0
  130. package/dist/router/utils.d.ts.map +1 -0
  131. package/dist/router.es.mjs +168 -132
  132. package/dist/router.es.mjs.map +1 -1
  133. package/dist/sanitize-Cxvxa-DX.js +283 -0
  134. package/dist/sanitize-Cxvxa-DX.js.map +1 -0
  135. package/dist/security/constants.d.ts +42 -0
  136. package/dist/security/constants.d.ts.map +1 -0
  137. package/dist/security/csp.d.ts +24 -0
  138. package/dist/security/csp.d.ts.map +1 -0
  139. package/dist/security/index.d.ts +4 -2
  140. package/dist/security/index.d.ts.map +1 -1
  141. package/dist/security/sanitize-core.d.ts +13 -0
  142. package/dist/security/sanitize-core.d.ts.map +1 -0
  143. package/dist/security/sanitize.d.ts +5 -57
  144. package/dist/security/sanitize.d.ts.map +1 -1
  145. package/dist/security/trusted-types.d.ts +25 -0
  146. package/dist/security/trusted-types.d.ts.map +1 -0
  147. package/dist/security/types.d.ts +36 -0
  148. package/dist/security/types.d.ts.map +1 -0
  149. package/dist/security.es.mjs +50 -277
  150. package/dist/security.es.mjs.map +1 -1
  151. package/dist/store/create-store.d.ts +15 -0
  152. package/dist/store/create-store.d.ts.map +1 -0
  153. package/dist/store/define-store.d.ts +28 -0
  154. package/dist/store/define-store.d.ts.map +1 -0
  155. package/dist/store/devtools.d.ts +22 -0
  156. package/dist/store/devtools.d.ts.map +1 -0
  157. package/dist/store/index.d.ts +10 -286
  158. package/dist/store/index.d.ts.map +1 -1
  159. package/dist/store/mapping.d.ts +28 -0
  160. package/dist/store/mapping.d.ts.map +1 -0
  161. package/dist/store/persisted.d.ts +13 -0
  162. package/dist/store/persisted.d.ts.map +1 -0
  163. package/dist/store/plugins.d.ts +13 -0
  164. package/dist/store/plugins.d.ts.map +1 -0
  165. package/dist/store/registry.d.ts +28 -0
  166. package/dist/store/registry.d.ts.map +1 -0
  167. package/dist/store/types.d.ts +71 -0
  168. package/dist/store/types.d.ts.map +1 -0
  169. package/dist/store/utils.d.ts +28 -0
  170. package/dist/store/utils.d.ts.map +1 -0
  171. package/dist/store/watch.d.ts +23 -0
  172. package/dist/store/watch.d.ts.map +1 -0
  173. package/dist/store.es.mjs +22 -224
  174. package/dist/store.es.mjs.map +1 -1
  175. package/dist/type-guards-BdKlYYlS.js +32 -0
  176. package/dist/type-guards-BdKlYYlS.js.map +1 -0
  177. package/dist/untrack-DNnnqdlR.js +6 -0
  178. package/dist/untrack-DNnnqdlR.js.map +1 -0
  179. package/dist/view/directives/bind.d.ts +7 -0
  180. package/dist/view/directives/bind.d.ts.map +1 -0
  181. package/dist/view/directives/class.d.ts +8 -0
  182. package/dist/view/directives/class.d.ts.map +1 -0
  183. package/dist/view/directives/for.d.ts +23 -0
  184. package/dist/view/directives/for.d.ts.map +1 -0
  185. package/dist/view/directives/html.d.ts +7 -0
  186. package/dist/view/directives/html.d.ts.map +1 -0
  187. package/dist/view/directives/if.d.ts +7 -0
  188. package/dist/view/directives/if.d.ts.map +1 -0
  189. package/dist/view/directives/index.d.ts +12 -0
  190. package/dist/view/directives/index.d.ts.map +1 -0
  191. package/dist/view/directives/model.d.ts +7 -0
  192. package/dist/view/directives/model.d.ts.map +1 -0
  193. package/dist/view/directives/on.d.ts +7 -0
  194. package/dist/view/directives/on.d.ts.map +1 -0
  195. package/dist/view/directives/ref.d.ts +7 -0
  196. package/dist/view/directives/ref.d.ts.map +1 -0
  197. package/dist/view/directives/show.d.ts +7 -0
  198. package/dist/view/directives/show.d.ts.map +1 -0
  199. package/dist/view/directives/style.d.ts +7 -0
  200. package/dist/view/directives/style.d.ts.map +1 -0
  201. package/dist/view/directives/text.d.ts +7 -0
  202. package/dist/view/directives/text.d.ts.map +1 -0
  203. package/dist/view/evaluate.d.ts +43 -0
  204. package/dist/view/evaluate.d.ts.map +1 -0
  205. package/dist/view/index.d.ts +3 -93
  206. package/dist/view/index.d.ts.map +1 -1
  207. package/dist/view/mount.d.ts +69 -0
  208. package/dist/view/mount.d.ts.map +1 -0
  209. package/dist/view/process.d.ts +26 -0
  210. package/dist/view/process.d.ts.map +1 -0
  211. package/dist/view/types.d.ts +36 -0
  212. package/dist/view/types.d.ts.map +1 -0
  213. package/dist/view.es.mjs +358 -251
  214. package/dist/view.es.mjs.map +1 -1
  215. package/dist/watch-DXXv3iAI.js +58 -0
  216. package/dist/watch-DXXv3iAI.js.map +1 -0
  217. package/package.json +14 -14
  218. package/src/component/component.ts +289 -0
  219. package/src/component/html.ts +53 -0
  220. package/src/component/index.ts +40 -414
  221. package/src/component/props.ts +116 -0
  222. package/src/component/types.ts +85 -0
  223. package/src/core/collection.ts +181 -7
  224. package/src/core/dom.ts +38 -0
  225. package/src/core/element.ts +59 -25
  226. package/src/core/index.ts +48 -4
  227. package/src/core/utils/array.ts +102 -0
  228. package/src/core/utils/function.ts +151 -0
  229. package/src/core/utils/index.ts +83 -0
  230. package/src/core/utils/misc.ts +82 -0
  231. package/src/core/utils/number.ts +78 -0
  232. package/src/core/utils/object.ts +206 -0
  233. package/src/core/utils/string.ts +112 -0
  234. package/src/core/utils/type-guards.ts +112 -0
  235. package/src/full.ts +187 -150
  236. package/src/index.ts +36 -36
  237. package/src/motion/animate.ts +113 -0
  238. package/src/motion/easing.ts +40 -0
  239. package/src/motion/flip.ts +176 -0
  240. package/src/motion/index.ts +41 -358
  241. package/src/motion/keyframes.ts +46 -0
  242. package/src/motion/reduced-motion.ts +17 -0
  243. package/src/motion/scroll.ts +57 -0
  244. package/src/motion/spring.ts +150 -0
  245. package/src/motion/stagger.ts +43 -0
  246. package/src/motion/timeline.ts +246 -0
  247. package/src/motion/transition.ts +51 -0
  248. package/src/motion/types.ts +198 -0
  249. package/src/platform/storage.ts +215 -208
  250. package/src/reactive/batch.ts +22 -0
  251. package/src/reactive/computed.ts +92 -0
  252. package/src/reactive/core.ts +114 -0
  253. package/src/reactive/effect.ts +54 -0
  254. package/src/reactive/index.ts +23 -22
  255. package/src/reactive/internals.ts +122 -0
  256. package/src/reactive/linked.ts +56 -0
  257. package/src/reactive/persisted.ts +74 -0
  258. package/src/reactive/readonly.ts +35 -0
  259. package/src/reactive/signal.ts +20 -520
  260. package/src/reactive/type-guards.ts +22 -0
  261. package/src/reactive/untrack.ts +31 -0
  262. package/src/reactive/watch.ts +73 -0
  263. package/src/router/index.ts +41 -718
  264. package/src/router/links.ts +130 -0
  265. package/src/router/match.ts +106 -0
  266. package/src/router/navigation.ts +71 -0
  267. package/src/router/query.ts +35 -0
  268. package/src/router/router.ts +211 -0
  269. package/src/router/state.ts +46 -0
  270. package/src/router/types.ts +93 -0
  271. package/src/router/utils.ts +116 -0
  272. package/src/security/constants.ts +209 -0
  273. package/src/security/csp.ts +77 -0
  274. package/src/security/index.ts +4 -12
  275. package/src/security/sanitize-core.ts +364 -0
  276. package/src/security/sanitize.ts +66 -625
  277. package/src/security/trusted-types.ts +69 -0
  278. package/src/security/types.ts +40 -0
  279. package/src/store/create-store.ts +329 -0
  280. package/src/store/define-store.ts +48 -0
  281. package/src/store/devtools.ts +45 -0
  282. package/src/store/index.ts +22 -848
  283. package/src/store/mapping.ts +73 -0
  284. package/src/store/persisted.ts +61 -0
  285. package/src/store/plugins.ts +32 -0
  286. package/src/store/registry.ts +51 -0
  287. package/src/store/types.ts +94 -0
  288. package/src/store/utils.ts +141 -0
  289. package/src/store/watch.ts +52 -0
  290. package/src/view/directives/bind.ts +23 -0
  291. package/src/view/directives/class.ts +70 -0
  292. package/src/view/directives/for.ts +275 -0
  293. package/src/view/directives/html.ts +19 -0
  294. package/src/view/directives/if.ts +30 -0
  295. package/src/view/directives/index.ts +11 -0
  296. package/src/view/directives/model.ts +56 -0
  297. package/src/view/directives/on.ts +41 -0
  298. package/src/view/directives/ref.ts +41 -0
  299. package/src/view/directives/show.ts +26 -0
  300. package/src/view/directives/style.ts +47 -0
  301. package/src/view/directives/text.ts +15 -0
  302. package/src/view/evaluate.ts +290 -0
  303. package/src/view/index.ts +112 -1041
  304. package/src/view/mount.ts +200 -0
  305. package/src/view/process.ts +92 -0
  306. package/src/view/types.ts +44 -0
  307. package/dist/core/utils.d.ts +0 -313
  308. package/dist/core/utils.d.ts.map +0 -1
  309. package/src/core/utils.ts +0 -444
@@ -0,0 +1,290 @@
1
+ import { isComputed, isSignal, type Signal } from '../reactive/index';
2
+ import type { BindingContext } from './types';
3
+
4
+ /** Maximum number of cached expression functions before LRU eviction */
5
+ const MAX_CACHE_SIZE = 500;
6
+
7
+ /** Compiled function type for expression evaluation */
8
+ type CompiledFn = (ctx: BindingContext) => unknown;
9
+
10
+ /**
11
+ * Simple LRU cache for compiled expression functions.
12
+ * Uses Map's insertion order to track recency - accessed items are re-inserted.
13
+ * @internal
14
+ */
15
+ class LRUCache {
16
+ private cache = new Map<string, CompiledFn>();
17
+ private maxSize: number;
18
+
19
+ constructor(maxSize: number) {
20
+ this.maxSize = maxSize;
21
+ }
22
+
23
+ get(key: string): CompiledFn | undefined {
24
+ const value = this.cache.get(key);
25
+ if (value !== undefined) {
26
+ // Move to end (most recently used) by re-inserting
27
+ this.cache.delete(key);
28
+ this.cache.set(key, value);
29
+ }
30
+ return value;
31
+ }
32
+
33
+ set(key: string, value: CompiledFn): void {
34
+ // Delete first if exists to update insertion order
35
+ if (this.cache.has(key)) {
36
+ this.cache.delete(key);
37
+ } else if (this.cache.size >= this.maxSize) {
38
+ // Evict oldest (first) entry
39
+ const oldest = this.cache.keys().next().value;
40
+ if (oldest !== undefined) {
41
+ this.cache.delete(oldest);
42
+ }
43
+ }
44
+ this.cache.set(key, value);
45
+ }
46
+
47
+ clear(): void {
48
+ this.cache.clear();
49
+ }
50
+
51
+ get size(): number {
52
+ return this.cache.size;
53
+ }
54
+ }
55
+
56
+ /** LRU cache for compiled evaluate functions, keyed by expression string */
57
+ const evaluateCache = new LRUCache(MAX_CACHE_SIZE);
58
+
59
+ /** LRU cache for compiled evaluateRaw functions, keyed by expression string */
60
+ const evaluateRawCache = new LRUCache(MAX_CACHE_SIZE);
61
+
62
+ /**
63
+ * Clears all cached compiled expression functions.
64
+ * Call this when unmounting views or to free memory after heavy template usage.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import { clearExpressionCache } from 'bquery/view';
69
+ *
70
+ * // After destroying a view or when cleaning up
71
+ * clearExpressionCache();
72
+ * ```
73
+ */
74
+ export const clearExpressionCache = (): void => {
75
+ evaluateCache.clear();
76
+ evaluateRawCache.clear();
77
+ };
78
+
79
+ /**
80
+ * Creates a proxy that lazily unwraps signals/computed only when accessed.
81
+ * This avoids subscribing to signals that aren't referenced in the expression.
82
+ * @internal
83
+ */
84
+ const createLazyContext = (context: BindingContext): BindingContext =>
85
+ new Proxy(context, {
86
+ get(target, prop: string | symbol) {
87
+ // Only handle string keys for BindingContext indexing
88
+ if (typeof prop !== 'string') {
89
+ return Reflect.get(target, prop);
90
+ }
91
+ const value = target[prop];
92
+ // Auto-unwrap signals/computed only when actually accessed
93
+ if (isSignal(value) || isComputed(value)) {
94
+ return (value as Signal<unknown>).value;
95
+ }
96
+ return value;
97
+ },
98
+ has(target, prop: string | symbol) {
99
+ // Required for `with` statement to resolve identifiers correctly
100
+ if (typeof prop !== 'string') {
101
+ return Reflect.has(target, prop);
102
+ }
103
+ return prop in target;
104
+ },
105
+ });
106
+
107
+ /**
108
+ * Evaluates an expression in the given context using `new Function()`.
109
+ *
110
+ * Signals and computed values in the context are lazily unwrapped only when
111
+ * accessed by the expression, avoiding unnecessary subscriptions to unused values.
112
+ *
113
+ * @security **WARNING:** This function uses dynamic code execution via `new Function()`.
114
+ * - NEVER pass expressions derived from user input or untrusted sources
115
+ * - Expressions should only come from developer-controlled templates
116
+ * - Malicious expressions can access and exfiltrate context data
117
+ * - Consider this equivalent to `eval()` in terms of security implications
118
+ *
119
+ * @internal
120
+ */
121
+ export const evaluate = <T = unknown>(expression: string, context: BindingContext): T => {
122
+ try {
123
+ // Create a proxy that lazily unwraps signals/computed on access
124
+ const lazyContext = createLazyContext(context);
125
+
126
+ // Use cached function or compile and cache a new one
127
+ let fn = evaluateCache.get(expression);
128
+ if (!fn) {
129
+ // Use `with` to enable direct property access from proxy scope.
130
+ // Note: `new Function()` runs in non-strict mode, so `with` is allowed.
131
+ fn = new Function('$ctx', `with($ctx) { return (${expression}); }`) as (
132
+ ctx: BindingContext
133
+ ) => unknown;
134
+ evaluateCache.set(expression, fn);
135
+ }
136
+ return fn(lazyContext) as T;
137
+ } catch (error) {
138
+ console.error(`bQuery view: Error evaluating "${expression}"`, error);
139
+ return undefined as T;
140
+ }
141
+ };
142
+
143
+ /**
144
+ * Evaluates an expression and returns the raw value (for signal access).
145
+ *
146
+ * @security **WARNING:** Uses dynamic code execution. See {@link evaluate} for security notes.
147
+ * @internal
148
+ */
149
+ export const evaluateRaw = <T = unknown>(expression: string, context: BindingContext): T => {
150
+ try {
151
+ // Use cached function or compile and cache a new one
152
+ let fn = evaluateRawCache.get(expression);
153
+ if (!fn) {
154
+ // Use `with` to enable direct property access from context scope.
155
+ // Unlike `evaluate`, we don't use a lazy proxy - values are accessed directly.
156
+ fn = new Function('$ctx', `with($ctx) { return (${expression}); }`) as (
157
+ ctx: BindingContext
158
+ ) => unknown;
159
+ evaluateRawCache.set(expression, fn);
160
+ }
161
+ return fn(context) as T;
162
+ } catch (error) {
163
+ console.error(`bQuery view: Error evaluating "${expression}"`, error);
164
+ return undefined as T;
165
+ }
166
+ };
167
+
168
+ /**
169
+ * Parses object expression like "{ active: isActive, disabled: !enabled }".
170
+ * Handles nested structures like function calls, arrays, and template literals.
171
+ * @internal
172
+ */
173
+ export const parseObjectExpression = (expression: string): Record<string, string> => {
174
+ const result: Record<string, string> = {};
175
+
176
+ // Remove outer braces and trim
177
+ const inner = expression
178
+ .trim()
179
+ .replace(/^\{|\}$/g, '')
180
+ .trim();
181
+ if (!inner) return result;
182
+
183
+ // Split by comma at depth 0, respecting strings and nesting
184
+ const parts: string[] = [];
185
+ let current = '';
186
+ let depth = 0;
187
+ let inString: string | null = null;
188
+
189
+ for (let i = 0; i < inner.length; i++) {
190
+ const char = inner[i];
191
+
192
+ // Handle string literals: count consecutive backslashes before a quote
193
+ // to correctly distinguish escaped quotes from end-of-string
194
+ if (char === '"' || char === "'" || char === '`') {
195
+ let backslashCount = 0;
196
+ let j = i - 1;
197
+ while (j >= 0 && inner[j] === '\\') {
198
+ backslashCount++;
199
+ j--;
200
+ }
201
+ // Quote is escaped only if preceded by an odd number of backslashes
202
+ if (backslashCount % 2 === 0) {
203
+ if (inString === null) {
204
+ inString = char;
205
+ } else if (inString === char) {
206
+ inString = null;
207
+ }
208
+ }
209
+ current += char;
210
+ continue;
211
+ }
212
+
213
+ // Skip if inside string
214
+ if (inString !== null) {
215
+ current += char;
216
+ continue;
217
+ }
218
+
219
+ // Track nesting depth for parentheses, brackets, and braces
220
+ if (char === '(' || char === '[' || char === '{') {
221
+ depth++;
222
+ current += char;
223
+ } else if (char === ')' || char === ']' || char === '}') {
224
+ depth--;
225
+ current += char;
226
+ } else if (char === ',' && depth === 0) {
227
+ // Top-level comma - split point
228
+ parts.push(current.trim());
229
+ current = '';
230
+ } else {
231
+ current += char;
232
+ }
233
+ }
234
+
235
+ // Add the last part
236
+ if (current.trim()) {
237
+ parts.push(current.trim());
238
+ }
239
+
240
+ // Parse each part to extract key and value
241
+ for (const part of parts) {
242
+ // Find the first colon at depth 0 (to handle ternary operators in values)
243
+ let colonIndex = -1;
244
+ let partDepth = 0;
245
+ let partInString: string | null = null;
246
+
247
+ for (let i = 0; i < part.length; i++) {
248
+ const char = part[i];
249
+
250
+ if (char === '"' || char === "'" || char === '`') {
251
+ let backslashCount = 0;
252
+ let j = i - 1;
253
+ while (j >= 0 && part[j] === '\\') {
254
+ backslashCount++;
255
+ j--;
256
+ }
257
+ if (backslashCount % 2 === 0) {
258
+ if (partInString === null) {
259
+ partInString = char;
260
+ } else if (partInString === char) {
261
+ partInString = null;
262
+ }
263
+ }
264
+ continue;
265
+ }
266
+
267
+ if (partInString !== null) continue;
268
+
269
+ if (char === '(' || char === '[' || char === '{') {
270
+ partDepth++;
271
+ } else if (char === ')' || char === ']' || char === '}') {
272
+ partDepth--;
273
+ } else if (char === ':' && partDepth === 0) {
274
+ colonIndex = i;
275
+ break;
276
+ }
277
+ }
278
+
279
+ if (colonIndex > -1) {
280
+ const key = part
281
+ .slice(0, colonIndex)
282
+ .trim()
283
+ .replace(/^['"]|['"]$/g, '');
284
+ const value = part.slice(colonIndex + 1).trim();
285
+ result[key] = value;
286
+ }
287
+ }
288
+
289
+ return result;
290
+ };