haori 0.13.0 → 0.13.2

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 (307) hide show
  1. package/README.ja.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/haori.cjs.js +10 -10
  4. package/dist/haori.es.js +191 -165
  5. package/dist/haori.iife.js +10 -10
  6. package/dist/index.d.ts +1 -1
  7. package/package.json +11 -3
  8. package/dist/haori.cjs.js.map +0 -1
  9. package/dist/haori.es.js.map +0 -1
  10. package/dist/haori.iife.js.map +0 -1
  11. package/dist/package.json +0 -70
  12. package/dist/src/builtins.d.ts +0 -100
  13. package/dist/src/builtins.d.ts.map +0 -1
  14. package/dist/src/builtins.js +0 -194
  15. package/dist/src/builtins.js.map +0 -1
  16. package/dist/src/core.d.ts +0 -505
  17. package/dist/src/core.d.ts.map +0 -1
  18. package/dist/src/core.js +0 -1817
  19. package/dist/src/core.js.map +0 -1
  20. package/dist/src/dev.d.ts +0 -35
  21. package/dist/src/dev.d.ts.map +0 -1
  22. package/dist/src/dev.js +0 -44
  23. package/dist/src/dev.js.map +0 -1
  24. package/dist/src/env.d.ts +0 -43
  25. package/dist/src/env.d.ts.map +0 -1
  26. package/dist/src/env.js +0 -108
  27. package/dist/src/env.js.map +0 -1
  28. package/dist/src/event.d.ts +0 -171
  29. package/dist/src/event.d.ts.map +0 -1
  30. package/dist/src/event.js +0 -235
  31. package/dist/src/event.js.map +0 -1
  32. package/dist/src/event_dispatcher.d.ts +0 -66
  33. package/dist/src/event_dispatcher.d.ts.map +0 -1
  34. package/dist/src/event_dispatcher.js +0 -167
  35. package/dist/src/event_dispatcher.js.map +0 -1
  36. package/dist/src/expression.d.ts +0 -260
  37. package/dist/src/expression.d.ts.map +0 -1
  38. package/dist/src/expression.js +0 -975
  39. package/dist/src/expression.js.map +0 -1
  40. package/dist/src/form.d.ts +0 -148
  41. package/dist/src/form.d.ts.map +0 -1
  42. package/dist/src/form.js +0 -428
  43. package/dist/src/form.js.map +0 -1
  44. package/dist/src/fragment.d.ts +0 -621
  45. package/dist/src/fragment.d.ts.map +0 -1
  46. package/dist/src/fragment.js +0 -1963
  47. package/dist/src/fragment.js.map +0 -1
  48. package/dist/src/haori.d.ts +0 -130
  49. package/dist/src/haori.d.ts.map +0 -1
  50. package/dist/src/haori.js +0 -215
  51. package/dist/src/haori.js.map +0 -1
  52. package/dist/src/import.d.ts +0 -19
  53. package/dist/src/import.d.ts.map +0 -1
  54. package/dist/src/import.js +0 -64
  55. package/dist/src/import.js.map +0 -1
  56. package/dist/src/index.d.ts +0 -27
  57. package/dist/src/index.d.ts.map +0 -1
  58. package/dist/src/index.js +0 -30
  59. package/dist/src/index.js.map +0 -1
  60. package/dist/src/intersect.d.ts +0 -28
  61. package/dist/src/intersect.d.ts.map +0 -1
  62. package/dist/src/intersect.js +0 -201
  63. package/dist/src/intersect.js.map +0 -1
  64. package/dist/src/log.d.ts +0 -32
  65. package/dist/src/log.d.ts.map +0 -1
  66. package/dist/src/log.js +0 -43
  67. package/dist/src/log.js.map +0 -1
  68. package/dist/src/observer.d.ts +0 -24
  69. package/dist/src/observer.d.ts.map +0 -1
  70. package/dist/src/observer.js +0 -131
  71. package/dist/src/observer.js.map +0 -1
  72. package/dist/src/procedure.d.ts +0 -447
  73. package/dist/src/procedure.d.ts.map +0 -1
  74. package/dist/src/procedure.js +0 -1921
  75. package/dist/src/procedure.js.map +0 -1
  76. package/dist/src/queue.d.ts +0 -35
  77. package/dist/src/queue.d.ts.map +0 -1
  78. package/dist/src/queue.js +0 -202
  79. package/dist/src/queue.js.map +0 -1
  80. package/dist/src/url.d.ts +0 -14
  81. package/dist/src/url.d.ts.map +0 -1
  82. package/dist/src/url.js +0 -22
  83. package/dist/src/url.js.map +0 -1
  84. package/dist/tests/bind-arg-own-binding.test.d.ts +0 -2
  85. package/dist/tests/bind-arg-own-binding.test.d.ts.map +0 -1
  86. package/dist/tests/bind-arg-own-binding.test.js +0 -63
  87. package/dist/tests/bind-arg-own-binding.test.js.map +0 -1
  88. package/dist/tests/bindcomplete-and-scope.test.d.ts +0 -2
  89. package/dist/tests/bindcomplete-and-scope.test.d.ts.map +0 -1
  90. package/dist/tests/bindcomplete-and-scope.test.js +0 -115
  91. package/dist/tests/bindcomplete-and-scope.test.js.map +0 -1
  92. package/dist/tests/builtins.test.d.ts +0 -2
  93. package/dist/tests/builtins.test.d.ts.map +0 -1
  94. package/dist/tests/builtins.test.js +0 -152
  95. package/dist/tests/builtins.test.js.map +0 -1
  96. package/dist/tests/click-attributes.test.d.ts +0 -2
  97. package/dist/tests/click-attributes.test.d.ts.map +0 -1
  98. package/dist/tests/click-attributes.test.js +0 -113
  99. package/dist/tests/click-attributes.test.js.map +0 -1
  100. package/dist/tests/click-no-disabled.test.d.ts +0 -2
  101. package/dist/tests/click-no-disabled.test.d.ts.map +0 -1
  102. package/dist/tests/click-no-disabled.test.js +0 -67
  103. package/dist/tests/click-no-disabled.test.js.map +0 -1
  104. package/dist/tests/core.test.d.ts +0 -5
  105. package/dist/tests/core.test.d.ts.map +0 -1
  106. package/dist/tests/core.test.js +0 -1056
  107. package/dist/tests/core.test.js.map +0 -1
  108. package/dist/tests/data-bind-arg-reeval.test.d.ts +0 -2
  109. package/dist/tests/data-bind-arg-reeval.test.d.ts.map +0 -1
  110. package/dist/tests/data-bind-arg-reeval.test.js +0 -119
  111. package/dist/tests/data-bind-arg-reeval.test.js.map +0 -1
  112. package/dist/tests/data-bind-merge.test.d.ts +0 -2
  113. package/dist/tests/data-bind-merge.test.d.ts.map +0 -1
  114. package/dist/tests/data-bind-merge.test.js +0 -86
  115. package/dist/tests/data-bind-merge.test.js.map +0 -1
  116. package/dist/tests/data-click-defer.test.d.ts +0 -2
  117. package/dist/tests/data-click-defer.test.d.ts.map +0 -1
  118. package/dist/tests/data-click-defer.test.js +0 -57
  119. package/dist/tests/data-click-defer.test.js.map +0 -1
  120. package/dist/tests/data-click-run.test.d.ts +0 -5
  121. package/dist/tests/data-click-run.test.d.ts.map +0 -1
  122. package/dist/tests/data-click-run.test.js +0 -109
  123. package/dist/tests/data-click-run.test.js.map +0 -1
  124. package/dist/tests/data-derive.test.d.ts +0 -2
  125. package/dist/tests/data-derive.test.d.ts.map +0 -1
  126. package/dist/tests/data-derive.test.js +0 -999
  127. package/dist/tests/data-derive.test.js.map +0 -1
  128. package/dist/tests/data-each-browserlike.test.d.ts +0 -2
  129. package/dist/tests/data-each-browserlike.test.d.ts.map +0 -1
  130. package/dist/tests/data-each-browserlike.test.js +0 -48
  131. package/dist/tests/data-each-browserlike.test.js.map +0 -1
  132. package/dist/tests/data-each-fragment-debug.test.d.ts +0 -2
  133. package/dist/tests/data-each-fragment-debug.test.d.ts.map +0 -1
  134. package/dist/tests/data-each-fragment-debug.test.js +0 -119
  135. package/dist/tests/data-each-fragment-debug.test.js.map +0 -1
  136. package/dist/tests/data-each-table.test.d.ts +0 -2
  137. package/dist/tests/data-each-table.test.d.ts.map +0 -1
  138. package/dist/tests/data-each-table.test.js +0 -131
  139. package/dist/tests/data-each-table.test.js.map +0 -1
  140. package/dist/tests/data-each-template-recovery.test.d.ts +0 -2
  141. package/dist/tests/data-each-template-recovery.test.d.ts.map +0 -1
  142. package/dist/tests/data-each-template-recovery.test.js +0 -87
  143. package/dist/tests/data-each-template-recovery.test.js.map +0 -1
  144. package/dist/tests/data-event-prevent.test.d.ts +0 -2
  145. package/dist/tests/data-event-prevent.test.d.ts.map +0 -1
  146. package/dist/tests/data-event-prevent.test.js +0 -76
  147. package/dist/tests/data-event-prevent.test.js.map +0 -1
  148. package/dist/tests/data-fetch-form-input-binding.test.d.ts +0 -2
  149. package/dist/tests/data-fetch-form-input-binding.test.d.ts.map +0 -1
  150. package/dist/tests/data-fetch-form-input-binding.test.js +0 -202
  151. package/dist/tests/data-fetch-form-input-binding.test.js.map +0 -1
  152. package/dist/tests/data-fetch-tbody-dom.test.d.ts +0 -2
  153. package/dist/tests/data-fetch-tbody-dom.test.d.ts.map +0 -1
  154. package/dist/tests/data-fetch-tbody-dom.test.js +0 -82
  155. package/dist/tests/data-fetch-tbody-dom.test.js.map +0 -1
  156. package/dist/tests/data-form-container.test.d.ts +0 -2
  157. package/dist/tests/data-form-container.test.d.ts.map +0 -1
  158. package/dist/tests/data-form-container.test.js +0 -88
  159. package/dist/tests/data-form-container.test.js.map +0 -1
  160. package/dist/tests/data-if-dev-diagnostics.test.d.ts +0 -2
  161. package/dist/tests/data-if-dev-diagnostics.test.d.ts.map +0 -1
  162. package/dist/tests/data-if-dev-diagnostics.test.js +0 -55
  163. package/dist/tests/data-if-dev-diagnostics.test.js.map +0 -1
  164. package/dist/tests/data-if-falsy.test.d.ts +0 -2
  165. package/dist/tests/data-if-falsy.test.d.ts.map +0 -1
  166. package/dist/tests/data-if-falsy.test.js +0 -73
  167. package/dist/tests/data-if-falsy.test.js.map +0 -1
  168. package/dist/tests/data-load-on-show.test.d.ts +0 -2
  169. package/dist/tests/data-load-on-show.test.d.ts.map +0 -1
  170. package/dist/tests/data-load-on-show.test.js +0 -98
  171. package/dist/tests/data-load-on-show.test.js.map +0 -1
  172. package/dist/tests/derive-each-render-wait.test.d.ts +0 -2
  173. package/dist/tests/derive-each-render-wait.test.d.ts.map +0 -1
  174. package/dist/tests/derive-each-render-wait.test.js +0 -90
  175. package/dist/tests/derive-each-render-wait.test.js.map +0 -1
  176. package/dist/tests/dev.test.d.ts +0 -2
  177. package/dist/tests/dev.test.d.ts.map +0 -1
  178. package/dist/tests/dev.test.js +0 -51
  179. package/dist/tests/dev.test.js.map +0 -1
  180. package/dist/tests/each-update-event.test.d.ts +0 -2
  181. package/dist/tests/each-update-event.test.d.ts.map +0 -1
  182. package/dist/tests/each-update-event.test.js +0 -83
  183. package/dist/tests/each-update-event.test.js.map +0 -1
  184. package/dist/tests/each_arg.test.d.ts +0 -2
  185. package/dist/tests/each_arg.test.d.ts.map +0 -1
  186. package/dist/tests/each_arg.test.js +0 -41
  187. package/dist/tests/each_arg.test.js.map +0 -1
  188. package/dist/tests/env.test.d.ts +0 -2
  189. package/dist/tests/env.test.d.ts.map +0 -1
  190. package/dist/tests/env.test.js +0 -124
  191. package/dist/tests/env.test.js.map +0 -1
  192. package/dist/tests/evaluation-profile.test.d.ts +0 -2
  193. package/dist/tests/evaluation-profile.test.d.ts.map +0 -1
  194. package/dist/tests/evaluation-profile.test.js +0 -92
  195. package/dist/tests/evaluation-profile.test.js.map +0 -1
  196. package/dist/tests/event.test.d.ts +0 -2
  197. package/dist/tests/event.test.d.ts.map +0 -1
  198. package/dist/tests/event.test.js +0 -300
  199. package/dist/tests/event.test.js.map +0 -1
  200. package/dist/tests/expression-disallowed-keyword-hint.test.d.ts +0 -2
  201. package/dist/tests/expression-disallowed-keyword-hint.test.d.ts.map +0 -1
  202. package/dist/tests/expression-disallowed-keyword-hint.test.js +0 -45
  203. package/dist/tests/expression-disallowed-keyword-hint.test.js.map +0 -1
  204. package/dist/tests/expression-forbidden-hint.test.d.ts +0 -2
  205. package/dist/tests/expression-forbidden-hint.test.d.ts.map +0 -1
  206. package/dist/tests/expression-forbidden-hint.test.js +0 -37
  207. package/dist/tests/expression-forbidden-hint.test.js.map +0 -1
  208. package/dist/tests/expression.test.d.ts +0 -2
  209. package/dist/tests/expression.test.d.ts.map +0 -1
  210. package/dist/tests/expression.test.js +0 -482
  211. package/dist/tests/expression.test.js.map +0 -1
  212. package/dist/tests/fetch-and-procedure-scenarios.test.d.ts +0 -2
  213. package/dist/tests/fetch-and-procedure-scenarios.test.d.ts.map +0 -1
  214. package/dist/tests/fetch-and-procedure-scenarios.test.js +0 -195
  215. package/dist/tests/fetch-and-procedure-scenarios.test.js.map +0 -1
  216. package/dist/tests/form-number-type.test.d.ts +0 -2
  217. package/dist/tests/form-number-type.test.d.ts.map +0 -1
  218. package/dist/tests/form-number-type.test.js +0 -83
  219. package/dist/tests/form-number-type.test.js.map +0 -1
  220. package/dist/tests/form.test.d.ts +0 -2
  221. package/dist/tests/form.test.d.ts.map +0 -1
  222. package/dist/tests/form.test.js +0 -636
  223. package/dist/tests/form.test.js.map +0 -1
  224. package/dist/tests/fragment.test.d.ts +0 -2
  225. package/dist/tests/fragment.test.d.ts.map +0 -1
  226. package/dist/tests/fragment.test.js +0 -284
  227. package/dist/tests/fragment.test.js.map +0 -1
  228. package/dist/tests/haori.test.d.ts +0 -2
  229. package/dist/tests/haori.test.d.ts.map +0 -1
  230. package/dist/tests/haori.test.js +0 -149
  231. package/dist/tests/haori.test.js.map +0 -1
  232. package/dist/tests/helpers/async.d.ts +0 -20
  233. package/dist/tests/helpers/async.d.ts.map +0 -1
  234. package/dist/tests/helpers/async.js +0 -35
  235. package/dist/tests/helpers/async.js.map +0 -1
  236. package/dist/tests/import.test.d.ts +0 -2
  237. package/dist/tests/import.test.d.ts.map +0 -1
  238. package/dist/tests/import.test.js +0 -148
  239. package/dist/tests/import.test.js.map +0 -1
  240. package/dist/tests/intersect.test.d.ts +0 -2
  241. package/dist/tests/intersect.test.d.ts.map +0 -1
  242. package/dist/tests/intersect.test.js +0 -173
  243. package/dist/tests/intersect.test.js.map +0 -1
  244. package/dist/tests/log.test.d.ts +0 -2
  245. package/dist/tests/log.test.d.ts.map +0 -1
  246. package/dist/tests/log.test.js +0 -58
  247. package/dist/tests/log.test.js.map +0 -1
  248. package/dist/tests/observer.test.d.ts +0 -2
  249. package/dist/tests/observer.test.d.ts.map +0 -1
  250. package/dist/tests/observer.test.js +0 -64
  251. package/dist/tests/observer.test.js.map +0 -1
  252. package/dist/tests/placeholder-resolution.test.d.ts +0 -2
  253. package/dist/tests/placeholder-resolution.test.d.ts.map +0 -1
  254. package/dist/tests/placeholder-resolution.test.js +0 -463
  255. package/dist/tests/placeholder-resolution.test.js.map +0 -1
  256. package/dist/tests/procedure-action-operations.test.d.ts +0 -2
  257. package/dist/tests/procedure-action-operations.test.d.ts.map +0 -1
  258. package/dist/tests/procedure-action-operations.test.js +0 -1140
  259. package/dist/tests/procedure-action-operations.test.js.map +0 -1
  260. package/dist/tests/procedure-bind-append.test.d.ts +0 -2
  261. package/dist/tests/procedure-bind-append.test.d.ts.map +0 -1
  262. package/dist/tests/procedure-bind-append.test.js +0 -80
  263. package/dist/tests/procedure-bind-append.test.js.map +0 -1
  264. package/dist/tests/procedure-fetch-options.test.d.ts +0 -2
  265. package/dist/tests/procedure-fetch-options.test.d.ts.map +0 -1
  266. package/dist/tests/procedure-fetch-options.test.js +0 -255
  267. package/dist/tests/procedure-fetch-options.test.js.map +0 -1
  268. package/dist/tests/procedure.test.d.ts +0 -2
  269. package/dist/tests/procedure.test.d.ts.map +0 -1
  270. package/dist/tests/procedure.test.js +0 -137
  271. package/dist/tests/procedure.test.js.map +0 -1
  272. package/dist/tests/procedure_events.test.d.ts +0 -7
  273. package/dist/tests/procedure_events.test.d.ts.map +0 -1
  274. package/dist/tests/procedure_events.test.js +0 -261
  275. package/dist/tests/procedure_events.test.js.map +0 -1
  276. package/dist/tests/render-wait.test.d.ts +0 -2
  277. package/dist/tests/render-wait.test.d.ts.map +0 -1
  278. package/dist/tests/render-wait.test.js +0 -68
  279. package/dist/tests/render-wait.test.js.map +0 -1
  280. package/dist/tests/reset_each.test.d.ts +0 -2
  281. package/dist/tests/reset_each.test.d.ts.map +0 -1
  282. package/dist/tests/reset_each.test.js +0 -215
  283. package/dist/tests/reset_each.test.js.map +0 -1
  284. package/dist/tests/row-move.test.d.ts +0 -2
  285. package/dist/tests/row-move.test.d.ts.map +0 -1
  286. package/dist/tests/row-move.test.js +0 -220
  287. package/dist/tests/row-move.test.js.map +0 -1
  288. package/dist/tests/row-operations.test.d.ts +0 -2
  289. package/dist/tests/row-operations.test.d.ts.map +0 -1
  290. package/dist/tests/row-operations.test.js +0 -259
  291. package/dist/tests/row-operations.test.js.map +0 -1
  292. package/dist/tests/scheduled-evaluate-wait.test.d.ts +0 -2
  293. package/dist/tests/scheduled-evaluate-wait.test.d.ts.map +0 -1
  294. package/dist/tests/scheduled-evaluate-wait.test.js +0 -68
  295. package/dist/tests/scheduled-evaluate-wait.test.js.map +0 -1
  296. package/dist/tests/url.test.d.ts +0 -2
  297. package/dist/tests/url.test.d.ts.map +0 -1
  298. package/dist/tests/url.test.js +0 -150
  299. package/dist/tests/url.test.js.map +0 -1
  300. package/dist/vite.config.d.ts +0 -3
  301. package/dist/vite.config.d.ts.map +0 -1
  302. package/dist/vite.config.js +0 -28
  303. package/dist/vite.config.js.map +0 -1
  304. package/dist/vitest.config.d.ts +0 -3
  305. package/dist/vitest.config.d.ts.map +0 -1
  306. package/dist/vitest.config.js +0 -19
  307. package/dist/vitest.config.js.map +0 -1
@@ -1,975 +0,0 @@
1
- /**
2
- * @fileoverview 式評価エンジン
3
- *
4
- * 式評価システムです。
5
- * XSS攻撃やコードインジェクションを防ぐためのセキュリティ機能と、
6
- * パフォーマンス向上のためのキャッシュ機能を提供します。
7
- */
8
- import Log from './log';
9
- import Builtins from './builtins';
10
- class Expression {
11
- /**
12
- * 明示バインド内に持ち込まれてはならない危険値を返します。
13
- *
14
- * @returns 危険値の配列
15
- */
16
- static getForbiddenBindingValues() {
17
- const scope = globalThis;
18
- const candidates = [
19
- scope,
20
- scope.window,
21
- scope.document,
22
- scope.navigator,
23
- scope.history,
24
- scope.localStorage,
25
- scope.sessionStorage,
26
- scope.fetch,
27
- scope.Function,
28
- scope.setTimeout,
29
- scope.setInterval,
30
- scope.requestAnimationFrame,
31
- scope.alert,
32
- scope.confirm,
33
- scope.prompt,
34
- ];
35
- if (scope.window?.location) {
36
- candidates.push(scope.window.location);
37
- }
38
- return candidates.filter(value => value !== undefined && value !== null);
39
- }
40
- /**
41
- * 現在の評価サイクルで利用する危険値集合を返します。
42
- *
43
- * @returns 危険値の集合
44
- */
45
- static getForbiddenBindingValueSet() {
46
- return new Set(this.getForbiddenBindingValues());
47
- }
48
- /**
49
- * 危険値チェック用の短命キャッシュを次の microtask で破棄します。
50
- */
51
- static scheduleForbiddenBindingValueCacheReset() {
52
- if (this.forbiddenBindingValueCacheResetScheduled) {
53
- return;
54
- }
55
- this.forbiddenBindingValueCacheResetScheduled = true;
56
- queueMicrotask(() => {
57
- this.forbiddenBindingValueCache = new WeakMap();
58
- this.forbiddenBindingValueCacheResetScheduled = false;
59
- });
60
- }
61
- /**
62
- * 現在のバインド識別子に含まれない禁止グローバルを遮断するコードを生成します。
63
- *
64
- * @param bindKeys 現在の式で利用するバインド識別子一覧
65
- * @returns 評価前に挿入する初期化コード
66
- */
67
- static buildAssignments(bindKeys) {
68
- const bindKeySet = new Set(bindKeys);
69
- return this.FORBIDDEN_NAMES.filter(name => !bindKeySet.has(name))
70
- .map(name => `const ${name} = undefined`)
71
- .join(';\n');
72
- }
73
- /**
74
- * 式の中で「使用できない(ブロックされた)グローバル識別子」を参照しているものを
75
- * 検出します。`Object` などは評価時に `undefined` へ遮断されるため、`Object.assign`
76
- * のように使うと原因の分かりにくい `TypeError` になります。エラー時のヒント表示に
77
- * 用います。プロパティアクセス(`foo.Object`)は対象外です。
78
- *
79
- * @param expression 評価対象の式
80
- * @returns 式が参照しているブロック済み識別子の一覧
81
- */
82
- static detectForbiddenIdentifiers(expression) {
83
- return this.FORBIDDEN_NAMES.filter(name => {
84
- // 直前が単語構成文字・`$`・`.` でなく、直後が単語構成文字・`$` でない
85
- // 独立した識別子としての出現のみを検出する。
86
- const pattern = new RegExp(`(^|[^\\w$.])${name}(?![\\w$])`);
87
- return pattern.test(expression);
88
- });
89
- }
90
- /**
91
- * 式を評価します。
92
- *
93
- * @param expression 評価する式文字列
94
- * @param bindedValue バインドされた値のオブジェクト
95
- */
96
- static evaluate(expression, bindedValues = {}) {
97
- return this.evaluateDetailed(expression, bindedValues).value;
98
- }
99
- /**
100
- * 式を評価し、未解決参照の有無を含む詳細結果を返します。
101
- *
102
- * @param expression 評価する式文字列
103
- * @param bindedValues バインドされた値のオブジェクト
104
- * @returns 評価結果と未解決参照の有無
105
- */
106
- static evaluateDetailed(expression, bindedValues = {}) {
107
- this.scheduleForbiddenBindingValueCacheReset();
108
- if (expression.trim() === '') {
109
- Log.warn('[Haori]', expression, 'Expression is empty');
110
- return { value: null, unresolvedReference: false };
111
- }
112
- if (this.containsDangerousPatterns(expression)) {
113
- const keywords = this.detectDisallowedKeywords(expression);
114
- if (keywords.length > 0) {
115
- // function 宣言・return・各種ステートメント系キーワードは式構文として
116
- // 使用できない。よくある原因は function(){return ...} の使用なので、
117
- // アロー関数への置き換えを促す具体的なヒントを併記する。
118
- const hint = keywords.some(k => k === 'function' || k === 'return')
119
- ? ' Statement keywords are not allowed in expressions;' +
120
- ' use an arrow function such as `x => ({key: value})`' +
121
- ' instead of `function(x){ return {key: value}; }`.'
122
- : ' These are statement keywords and cannot be used in expressions.';
123
- Log.warn('[Haori]', expression, 'Expression uses disallowed keyword(s): ' +
124
- keywords.join(', ') +
125
- '.' +
126
- hint);
127
- }
128
- else {
129
- Log.warn('[Haori]', expression, 'Expression contains dangerous patterns');
130
- }
131
- return { value: null, unresolvedReference: false };
132
- }
133
- if (this.containsForbiddenKeys(bindedValues)) {
134
- Log.warn('[Haori]', bindedValues, 'Binded values contain forbidden keys');
135
- return { value: null, unresolvedReference: false };
136
- }
137
- const forbiddenBindingValues = this.getForbiddenBindingValueSet();
138
- if (this.containsForbiddenBindingValues(bindedValues, new WeakSet(), forbiddenBindingValues)) {
139
- Log.warn('[Haori]', bindedValues, 'Binded values contain forbidden values');
140
- return { value: null, unresolvedReference: false };
141
- }
142
- // 式が予約名前空間 `haori` を参照している場合のみ組み込みヘルパーを注入する。
143
- // 参照していない式に無駄な引数・Proxy ラップを増やさないための最適化。
144
- const referencesBuiltins = this.BUILTIN_REFERENCE_PATTERN.test(expression);
145
- if (referencesBuiltins && this.BUILTIN_NAMESPACE in bindedValues) {
146
- Log.warn('[Haori]', `Binding key "${this.BUILTIN_NAMESPACE}" is reserved for built-in` +
147
- ' helpers; the bound value is ignored in expressions.');
148
- }
149
- const runtimeBindings = referencesBuiltins
150
- ? { ...bindedValues, [this.BUILTIN_NAMESPACE]: this.BUILTIN_HELPERS }
151
- : { ...bindedValues };
152
- const allowMissingIdentifierRecovery = this.canAttemptMissingIdentifierRecovery(expression);
153
- for (let recoveryCount = 0; recoveryCount <= this.MAX_IDENTIFIER_RECOVERY_COUNT; recoveryCount += 1) {
154
- const setup = this.prepareEvaluator(expression, runtimeBindings);
155
- if (setup.compileFailed || setup.evaluator === null) {
156
- return { value: null, unresolvedReference: false };
157
- }
158
- try {
159
- const argValues = [];
160
- const wrappedValues = this.wrapBoundValues(runtimeBindings);
161
- setup.bindKeys.forEach((key) => {
162
- argValues.push(wrappedValues[key]);
163
- });
164
- return {
165
- value: this.withBlockedPropertyAccess(() => setup.evaluator(...argValues)),
166
- unresolvedReference: false,
167
- };
168
- }
169
- catch (error) {
170
- if (allowMissingIdentifierRecovery && error instanceof ReferenceError) {
171
- const missingIdentifier = this.extractMissingIdentifier(error);
172
- if (missingIdentifier !== null &&
173
- this.canRecoverMissingIdentifier(missingIdentifier, runtimeBindings)) {
174
- runtimeBindings[missingIdentifier] = undefined;
175
- continue;
176
- }
177
- }
178
- // 式が使用できない(ブロックされた)識別子を参照している場合は、
179
- // 原因が分かりにくい TypeError になりがちなため明示的なヒントを出す。
180
- const blocked = this.detectForbiddenIdentifiers(expression);
181
- if (blocked.length > 0) {
182
- Log.warn('[Haori]', 'Expression references blocked identifier(s): ' +
183
- blocked.join(', ') +
184
- '. These are blocked in expressions and evaluate to' +
185
- ' undefined (often the cause of this error).' +
186
- ' Use spread {...a, ...b} instead of Object.assign.', expression);
187
- }
188
- Log.error('[Haori]', 'Expression evaluation error:', expression, error);
189
- if (error instanceof ReferenceError) {
190
- // ReferenceError(未定義変数)はundefinedを返す
191
- return { value: undefined, unresolvedReference: true };
192
- }
193
- return { value: null, unresolvedReference: false };
194
- }
195
- }
196
- Log.error('[Haori]', 'Failed to recover missing identifiers:', expression, runtimeBindings);
197
- return { value: undefined, unresolvedReference: true };
198
- }
199
- /**
200
- * 現在のバインド集合で evaluator を取得または生成します。
201
- *
202
- * @param expression 評価する式
203
- * @param bindedValues バインドされた値のオブジェクト
204
- * @returns evaluator 準備結果
205
- */
206
- static prepareEvaluator(expression, bindedValues) {
207
- const bindKeys = Object.keys(bindedValues)
208
- .filter(key => !this.FORBIDDEN_BINDING_NAMES.has(key))
209
- .sort();
210
- const cacheKey = `${expression}:${bindKeys.join(',')}`;
211
- let evaluator = this.EXPRESSION_CACHE.get(cacheKey) || null;
212
- if (evaluator !== null) {
213
- return {
214
- bindKeys,
215
- evaluator,
216
- compileFailed: false,
217
- };
218
- }
219
- const assignments = this.buildAssignments(bindKeys);
220
- const body = assignments
221
- ? '"use strict";\n' + `${assignments};\nreturn (${expression});`
222
- : '"use strict";\n' + `return (${expression});`;
223
- try {
224
- evaluator = new Function(...bindKeys, body);
225
- this.EXPRESSION_CACHE.set(cacheKey, evaluator);
226
- return {
227
- bindKeys,
228
- evaluator,
229
- compileFailed: false,
230
- };
231
- }
232
- catch (error) {
233
- Log.error('[Haori]', 'Failed to compile expression:', expression, error);
234
- return {
235
- bindKeys,
236
- evaluator: null,
237
- compileFailed: true,
238
- };
239
- }
240
- }
241
- /**
242
- * ReferenceError から未宣言識別子名を抽出します。
243
- *
244
- * @param error 発生した ReferenceError
245
- * @returns 識別子名。抽出できない場合は null
246
- */
247
- static extractMissingIdentifier(error) {
248
- const message = String(error.message || '');
249
- const match = message.match(/^([A-Za-z_$][A-Za-z0-9_$]*) is not defined$/);
250
- return match?.[1] || null;
251
- }
252
- /**
253
- * 未宣言識別子を undefined バインドとして補完可能かを返します。
254
- *
255
- * @param identifier 識別子名
256
- * @param bindedValues 現在のバインド値
257
- * @returns 補完可能なら true
258
- */
259
- static canRecoverMissingIdentifier(identifier, bindedValues) {
260
- if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) {
261
- return false;
262
- }
263
- return (bindedValues[identifier] === undefined &&
264
- !(identifier in bindedValues));
265
- }
266
- /**
267
- * 未宣言識別子の補完を試みてよい式かを返します。
268
- *
269
- * @param expression 評価する式
270
- * @returns 補完を試みてよい場合は true
271
- */
272
- static canAttemptMissingIdentifierRecovery(expression) {
273
- return (expression.includes('?.') ||
274
- expression.includes('??') ||
275
- expression.includes('||') ||
276
- expression.includes('&&'));
277
- }
278
- /**
279
- * 式にevalや危険な構文が含まれているかチェックします。
280
- *
281
- * @param expression チェック対象の式文字列
282
- * @return 危険なパターンが含まれている場合はtrue
283
- */
284
- static containsDangerousPatterns(expression) {
285
- if (!this.hasAllowedSyntax(expression)) {
286
- return true;
287
- }
288
- const dangerousPatterns = [
289
- /\beval\s*\(/, // eval(...)
290
- /\barguments\s*\[/, // arguments[...]
291
- /\barguments\s*\./, // arguments.xxx
292
- ];
293
- return dangerousPatterns.some(pattern => pattern.test(expression));
294
- }
295
- /**
296
- * 式の中で使用されている「使用できないキーワード」を検出します。
297
- * 文字列リテラルやプロパティ名は対象外とするため、トークナイザを用いて
298
- * 識別子トークンのみを判定します。エラー時のヒント表示に使用します。
299
- *
300
- * @param expression 検査対象の式
301
- * @returns 式に現れた使用できないキーワードの一覧(重複なし、出現順)
302
- */
303
- static detectDisallowedKeywords(expression) {
304
- // トークナイザは `;` など式構文外の文字で null を返すため、ここでは独立した
305
- // 識別子としての出現を正規表現で検出する。直前が単語構成文字・`$`・`.`(プロパティ
306
- // アクセス)でなく、直後が単語構成文字・`$` でないものだけを対象にする。
307
- const found = [];
308
- this.DISALLOWED_KEYWORDS.forEach(keyword => {
309
- const pattern = new RegExp(`(^|[^\\w$.])${keyword}(?![\\w$])`);
310
- if (pattern.test(expression)) {
311
- found.push(keyword);
312
- }
313
- });
314
- return found;
315
- }
316
- /**
317
- * 許可する式構文かどうかを検証します。
318
- *
319
- * @param expression 検証対象の式
320
- * @returns 許可する構文であればtrue
321
- */
322
- static hasAllowedSyntax(expression) {
323
- const tokens = this.tokenizeExpression(expression);
324
- if (tokens === null || tokens.length === 0) {
325
- return false;
326
- }
327
- const groups = [];
328
- let previous = null;
329
- for (let index = 0; index < tokens.length; index++) {
330
- const token = tokens[index];
331
- const next = tokens[index + 1] || null;
332
- const activeGroup = groups[groups.length - 1] || null;
333
- const beforePrevious = tokens[index - 2] || null;
334
- const thirdPrevious = tokens[index - 3] || null;
335
- if (this.startsObjectKey(activeGroup, previous, beforePrevious, thirdPrevious)) {
336
- if (token.value === '[') {
337
- return false;
338
- }
339
- if (token.type === 'identifier' &&
340
- this.FORBIDDEN_PROPERTY_NAMES.has(token.value)) {
341
- return false;
342
- }
343
- if (token.type === 'string' &&
344
- this.FORBIDDEN_PROPERTY_NAMES.has(this.decodeStringLiteral(token.value))) {
345
- return false;
346
- }
347
- }
348
- if (token.type === 'identifier') {
349
- if (this.DISALLOWED_KEYWORDS.has(token.value)) {
350
- return false;
351
- }
352
- if (this.STRICT_FORBIDDEN_NAMES.includes(token.value)) {
353
- return false;
354
- }
355
- if ((previous?.value === '.' || previous?.value === '?.') &&
356
- this.FORBIDDEN_PROPERTY_NAMES.has(token.value)) {
357
- return false;
358
- }
359
- }
360
- if (activeGroup === 'member' && token.value !== ']') {
361
- if (token.type === 'string' &&
362
- this.FORBIDDEN_PROPERTY_NAMES.has(this.decodeStringLiteral(token.value))) {
363
- return false;
364
- }
365
- }
366
- if (token.value === '.' && next?.type !== 'identifier') {
367
- return false;
368
- }
369
- if (token.value === '?.' &&
370
- next?.type !== 'identifier' &&
371
- next?.value !== '[' &&
372
- next?.value !== '(') {
373
- return false;
374
- }
375
- switch (token.value) {
376
- case '(':
377
- groups.push('paren');
378
- break;
379
- case ')': {
380
- const group = groups.pop();
381
- if (group !== 'paren') {
382
- return false;
383
- }
384
- break;
385
- }
386
- case '[': {
387
- const group = this.startsMemberAccess(previous)
388
- ? 'member'
389
- : 'array';
390
- groups.push(group);
391
- break;
392
- }
393
- case '{':
394
- groups.push('object');
395
- break;
396
- case ']': {
397
- const group = groups.pop();
398
- if (group === undefined) {
399
- return false;
400
- }
401
- break;
402
- }
403
- case '}': {
404
- const group = groups.pop();
405
- if (group !== 'object') {
406
- return false;
407
- }
408
- break;
409
- }
410
- }
411
- previous = token;
412
- }
413
- return groups.length === 0;
414
- }
415
- /**
416
- * 式をトークン列に分解します。
417
- *
418
- * @param expression 評価前に検証する式
419
- * @returns 分解結果。未対応構文を含む場合はnull
420
- */
421
- static tokenizeExpression(expression) {
422
- const tokens = [];
423
- const operators = [
424
- '===',
425
- '!==',
426
- '...',
427
- '?.',
428
- '&&',
429
- '||',
430
- '>=',
431
- '<=',
432
- '==',
433
- '!=',
434
- '=>',
435
- ];
436
- const singleCharacters = new Set([
437
- '(',
438
- ')',
439
- '{',
440
- '}',
441
- '[',
442
- ']',
443
- '.',
444
- ',',
445
- '?',
446
- ':',
447
- '+',
448
- '-',
449
- '*',
450
- '/',
451
- '%',
452
- '!',
453
- '>',
454
- '<',
455
- ]);
456
- let index = 0;
457
- while (index < expression.length) {
458
- const current = expression[index];
459
- if (/\s/.test(current)) {
460
- index += 1;
461
- continue;
462
- }
463
- if (current === '/' &&
464
- (expression[index + 1] === '/' || expression[index + 1] === '*')) {
465
- return null;
466
- }
467
- if (current === '"' || current === '\'') {
468
- const stringToken = this.readStringToken(expression, index);
469
- if (stringToken === null) {
470
- return null;
471
- }
472
- tokens.push(stringToken.token);
473
- index = stringToken.nextIndex;
474
- continue;
475
- }
476
- const operator = operators.find(item => expression.startsWith(item, index));
477
- if (operator) {
478
- tokens.push({ type: 'operator', value: operator, position: index });
479
- index += operator.length;
480
- continue;
481
- }
482
- if (/[0-9]/.test(current)) {
483
- const numberToken = this.readNumberToken(expression, index);
484
- tokens.push(numberToken.token);
485
- index = numberToken.nextIndex;
486
- continue;
487
- }
488
- if (/[A-Za-z_$]/.test(current)) {
489
- const identifierToken = this.readIdentifierToken(expression, index);
490
- tokens.push(identifierToken.token);
491
- index = identifierToken.nextIndex;
492
- continue;
493
- }
494
- if (singleCharacters.has(current)) {
495
- tokens.push({ type: 'operator', value: current, position: index });
496
- index += 1;
497
- continue;
498
- }
499
- return null;
500
- }
501
- return tokens;
502
- }
503
- /**
504
- * 文字列リテラルを読み取ります。
505
- *
506
- * @param expression 式全体
507
- * @param start 開始位置
508
- * @returns トークンと次の位置
509
- */
510
- static readStringToken(expression, start) {
511
- const quote = expression[start];
512
- let index = start + 1;
513
- while (index < expression.length) {
514
- const current = expression[index];
515
- if (current === '\\') {
516
- index += 2;
517
- continue;
518
- }
519
- if (current === quote) {
520
- return {
521
- token: {
522
- type: 'string',
523
- value: expression.slice(start, index + 1),
524
- position: start,
525
- },
526
- nextIndex: index + 1,
527
- };
528
- }
529
- index += 1;
530
- }
531
- return null;
532
- }
533
- /**
534
- * 数値リテラルを読み取ります。
535
- *
536
- * @param expression 式全体
537
- * @param start 開始位置
538
- * @returns トークンと次の位置
539
- */
540
- static readNumberToken(expression, start) {
541
- let index = start;
542
- while (index < expression.length && /[0-9_]/.test(expression[index])) {
543
- index += 1;
544
- }
545
- if (expression[index] === '.') {
546
- index += 1;
547
- while (index < expression.length && /[0-9_]/.test(expression[index])) {
548
- index += 1;
549
- }
550
- }
551
- return {
552
- token: {
553
- type: 'number',
554
- value: expression.slice(start, index),
555
- position: start,
556
- },
557
- nextIndex: index,
558
- };
559
- }
560
- /**
561
- * 識別子を読み取ります。
562
- *
563
- * @param expression 式全体
564
- * @param start 開始位置
565
- * @returns トークンと次の位置
566
- */
567
- static readIdentifierToken(expression, start) {
568
- let index = start;
569
- while (index < expression.length &&
570
- /[A-Za-z0-9_$]/.test(expression[index])) {
571
- index += 1;
572
- }
573
- return {
574
- token: {
575
- type: 'identifier',
576
- value: expression.slice(start, index),
577
- position: start,
578
- },
579
- nextIndex: index,
580
- };
581
- }
582
- /**
583
- * 角括弧がメンバーアクセスかどうかを判定します。
584
- *
585
- * @param previous 直前のトークン
586
- * @returns メンバーアクセスであればtrue
587
- */
588
- static startsMemberAccess(previous) {
589
- if (previous === null) {
590
- return false;
591
- }
592
- if (previous.type === 'identifier' || previous.type === 'number') {
593
- return true;
594
- }
595
- return (previous.value === ')' ||
596
- previous.value === ']' ||
597
- previous.value === '?.');
598
- }
599
- /**
600
- * object literal 内で次のトークンがキー位置かどうかを判定します。
601
- *
602
- * @param activeGroup 現在のグループ種別
603
- * @param previous 直前のトークン
604
- * @returns object literal のキー位置であれば true
605
- */
606
- static startsObjectKey(activeGroup, previous, beforePrevious, thirdPrevious) {
607
- if (activeGroup !== 'object') {
608
- return false;
609
- }
610
- if (previous?.value === '{' || previous?.value === ',') {
611
- return true;
612
- }
613
- if (previous?.type === 'identifier' &&
614
- this.OBJECT_PROPERTY_MODIFIERS.has(previous.value) &&
615
- (beforePrevious?.value === '{' || beforePrevious?.value === ',')) {
616
- return true;
617
- }
618
- if (previous?.value !== '*') {
619
- return false;
620
- }
621
- if (beforePrevious?.value === '{' || beforePrevious?.value === ',') {
622
- return true;
623
- }
624
- return (beforePrevious?.type === 'identifier' &&
625
- beforePrevious.value === 'async' &&
626
- (thirdPrevious?.value === '{' || thirdPrevious?.value === ','));
627
- }
628
- /**
629
- * 文字列リテラルをプレーン文字列へ変換します。
630
- *
631
- * @param literal 文字列リテラル
632
- * @returns デコード後の文字列
633
- */
634
- static decodeStringLiteral(literal) {
635
- return literal
636
- .slice(1, -1)
637
- .replace(/\\u\{([0-9a-fA-F]+)\}/g, (_, code) => String.fromCodePoint(parseInt(code, 16)))
638
- .replace(/\\u([0-9a-fA-F]{4})/g, (_, code) => String.fromCharCode(parseInt(code, 16)))
639
- .replace(/\\x([0-9a-fA-F]{2})/g, (_, code) => String.fromCharCode(parseInt(code, 16)))
640
- .replace(/\\(["'\\bfnrtv0])/g, (_, escaped) => {
641
- switch (escaped) {
642
- case 'b':
643
- return '\b';
644
- case 'f':
645
- return '\f';
646
- case 'n':
647
- return '\n';
648
- case 'r':
649
- return '\r';
650
- case 't':
651
- return '\t';
652
- case 'v':
653
- return '\v';
654
- case '0':
655
- return '\0';
656
- default:
657
- return escaped;
658
- }
659
- });
660
- }
661
- /**
662
- * バインド値を安全なProxyでラップします。
663
- *
664
- * @param bindedValues バインド値
665
- * @returns ラップ済みのバインド値
666
- */
667
- static wrapBoundValues(bindedValues) {
668
- const cache = new WeakMap();
669
- const wrappedValues = {};
670
- Object.entries(bindedValues).forEach(([key, value]) => {
671
- wrappedValues[key] = this.wrapBoundValue(value, cache);
672
- });
673
- return wrappedValues;
674
- }
675
- /**
676
- * 危険なプロパティアクセスを防ぐために値を再帰的にラップします。
677
- *
678
- * @param value ラップ対象の値
679
- * @param cache 既存Proxyのキャッシュ
680
- * @returns ラップ済みの値
681
- */
682
- static wrapBoundValue(value, cache) {
683
- if (!this.shouldWrapValue(value)) {
684
- return value;
685
- }
686
- const target = value;
687
- const cachedValue = cache.get(target);
688
- if (cachedValue !== undefined) {
689
- return cachedValue;
690
- }
691
- const proxy = new Proxy(target, {
692
- get: (currentTarget, property, receiver) => {
693
- if (typeof property === 'string' &&
694
- this.FORBIDDEN_PROPERTY_NAMES.has(property)) {
695
- return undefined;
696
- }
697
- const result = Reflect.get(currentTarget, property, receiver);
698
- if (typeof property === 'symbol') {
699
- return result;
700
- }
701
- return this.wrapBoundValue(result, cache);
702
- },
703
- has: (currentTarget, property) => {
704
- if (typeof property === 'string' &&
705
- this.FORBIDDEN_PROPERTY_NAMES.has(property)) {
706
- return false;
707
- }
708
- return Reflect.has(currentTarget, property);
709
- },
710
- getOwnPropertyDescriptor: (currentTarget, property) => {
711
- if (typeof property === 'string' &&
712
- this.FORBIDDEN_PROPERTY_NAMES.has(property)) {
713
- return undefined;
714
- }
715
- return Reflect.getOwnPropertyDescriptor(currentTarget, property);
716
- },
717
- apply: (currentTarget, thisArg, argArray) => {
718
- const result = Reflect.apply(currentTarget, thisArg, argArray);
719
- if (this.isIteratorLike(result)) {
720
- return result;
721
- }
722
- return this.wrapBoundValue(result, cache);
723
- },
724
- construct: (currentTarget, argArray, newTarget) => {
725
- return this.wrapBoundValue(Reflect.construct(currentTarget, argArray, newTarget), cache);
726
- },
727
- });
728
- cache.set(target, proxy);
729
- return proxy;
730
- }
731
- /**
732
- * Proxy ラップ対象の値かどうかを判定します。
733
- *
734
- * @param value 判定対象
735
- * @returns ラップ対象であればtrue
736
- */
737
- static shouldWrapValue(value) {
738
- if (typeof value === 'function') {
739
- return true;
740
- }
741
- if (value === null || typeof value !== 'object') {
742
- return false;
743
- }
744
- if (Array.isArray(value)) {
745
- return true;
746
- }
747
- const prototype = Object.getPrototypeOf(value);
748
- return prototype === Object.prototype || prototype === null;
749
- }
750
- /**
751
- * 評価中のみ prototype 系プロパティへの生アクセスを抑止します。
752
- *
753
- * @param callback 実行する処理
754
- * @returns 処理結果
755
- */
756
- static withBlockedPropertyAccess(callback) {
757
- const blockedDescriptors = [
758
- { target: Object.prototype, property: 'constructor' },
759
- { target: Function.prototype, property: 'constructor' },
760
- { target: Object.prototype, property: '__proto__' },
761
- ];
762
- const originals = blockedDescriptors
763
- .map(item => ({
764
- ...item,
765
- descriptor: Object.getOwnPropertyDescriptor(item.target, item.property),
766
- }))
767
- .filter(item => item.descriptor?.configurable === true);
768
- originals.forEach(({ target, property }) => {
769
- Object.defineProperty(target, property, {
770
- configurable: true,
771
- enumerable: false,
772
- get: () => undefined,
773
- set: () => undefined,
774
- });
775
- });
776
- try {
777
- return callback();
778
- }
779
- finally {
780
- originals.forEach(({ target, property, descriptor }) => {
781
- if (descriptor !== undefined) {
782
- Object.defineProperty(target, property, descriptor);
783
- }
784
- });
785
- }
786
- }
787
- /**
788
- * イテレータ互換オブジェクトかどうかを判定します。
789
- *
790
- * @param value 判定対象
791
- * @returns イテレータ互換であればtrue
792
- */
793
- static isIteratorLike(value) {
794
- if (value === null || typeof value !== 'object') {
795
- return false;
796
- }
797
- return typeof value.next === 'function';
798
- }
799
- /**
800
- * トップレベルのバインド識別子に拒否対象名が含まれていないかを判定します。
801
- * ネストしたオブジェクトのプロパティ名は識別子として評価されないため、ここでは拒否しません。
802
- *
803
- * @param obj チェック対象のオブジェクト
804
- * @return 禁止識別子が含まれていればtrue
805
- */
806
- static containsForbiddenKeys(obj) {
807
- if (!obj || typeof obj !== 'object') {
808
- return false;
809
- }
810
- for (const key of Object.keys(obj)) {
811
- if (this.FORBIDDEN_BINDING_NAMES.has(key)) {
812
- return true;
813
- }
814
- }
815
- return false;
816
- }
817
- /**
818
- * バインド値に危険なホストオブジェクトやグローバル関数が含まれていないかを再帰的に判定します。
819
- *
820
- * @param obj チェック対象の値
821
- * @param seen 循環参照検出用の訪問済み集合
822
- * @return 危険値が含まれていればtrue
823
- */
824
- static containsForbiddenBindingValues(obj, seen = new WeakSet(), forbiddenBindingValues = this.getForbiddenBindingValueSet()) {
825
- if (!obj || typeof obj !== 'object') {
826
- return false;
827
- }
828
- const cached = this.forbiddenBindingValueCache.get(obj);
829
- if (cached !== undefined) {
830
- return cached;
831
- }
832
- if (seen.has(obj)) {
833
- return false;
834
- }
835
- seen.add(obj);
836
- if (forbiddenBindingValues.has(obj)) {
837
- this.forbiddenBindingValueCache.set(obj, true);
838
- return true;
839
- }
840
- for (const value of Object.values(obj)) {
841
- if (typeof value === 'function') {
842
- if (forbiddenBindingValues.has(value)) {
843
- this.forbiddenBindingValueCache.set(obj, true);
844
- return true;
845
- }
846
- continue;
847
- }
848
- if (this.containsForbiddenBindingValues(value, seen, forbiddenBindingValues)) {
849
- this.forbiddenBindingValueCache.set(obj, true);
850
- return true;
851
- }
852
- }
853
- this.forbiddenBindingValueCache.set(obj, false);
854
- return false;
855
- }
856
- }
857
- /** 未宣言識別子の自動補完を試みる最大回数 */
858
- Expression.MAX_IDENTIFIER_RECOVERY_COUNT = 8;
859
- /** 組み込みヘルパーを公開する予約名前空間 */
860
- Expression.BUILTIN_NAMESPACE = 'haori';
861
- /**
862
- * 式スコープへ注入する組み込みヘルパー。公開 API の凍結オブジェクトをそのまま
863
- * 注入すると、評価時の Proxy ラップが「read-only プロパティに別値を返せない」と
864
- * いう Proxy 不変条件に違反するため、非凍結の浅いコピーを用いる。
865
- */
866
- Expression.BUILTIN_HELPERS = {
867
- ...Builtins,
868
- };
869
- /**
870
- * 式が予約名前空間 `haori` を独立した識別子として参照しているか判定する正規表現。
871
- * `foo.haori` のようなプロパティアクセスは対象外とする。
872
- */
873
- Expression.BUILTIN_REFERENCE_PATTERN = /(^|[^\w$.])haori(?![\w$])/;
874
- /** 危険値チェック結果の短命キャッシュ */
875
- Expression.forbiddenBindingValueCache = new WeakMap();
876
- /** 危険値チェックキャッシュのクリア予約済みフラグ */
877
- Expression.forbiddenBindingValueCacheResetScheduled = false;
878
- /** Haoriで禁止すべき識別子一覧(eval と arguments は strict モードで無効化) */
879
- Expression.FORBIDDEN_NAMES = [
880
- // グローバルオブジェクト
881
- 'window',
882
- 'self',
883
- 'globalThis',
884
- 'frames',
885
- 'parent',
886
- 'top',
887
- // 危険な関数/オブジェクト
888
- 'Function',
889
- 'setTimeout',
890
- 'setInterval',
891
- 'requestAnimationFrame',
892
- 'alert',
893
- 'confirm',
894
- 'prompt',
895
- 'fetch',
896
- 'XMLHttpRequest',
897
- 'Reflect',
898
- // 脱出経路・プロトタイプ
899
- 'constructor',
900
- '__proto__',
901
- 'prototype',
902
- 'Object',
903
- // その他
904
- 'document',
905
- 'location',
906
- 'navigator',
907
- 'localStorage',
908
- 'sessionStorage',
909
- 'IndexedDB',
910
- 'history',
911
- ];
912
- /** strict モードで禁止される識別子 */
913
- Expression.STRICT_FORBIDDEN_NAMES = ['eval', 'arguments'];
914
- /** 明示バインド時のみ利用を許可する衝突名 */
915
- Expression.REBINDABLE_FORBIDDEN_NAMES = new Set(['location']);
916
- /** バインド識別子としては拒否する名前 */
917
- Expression.FORBIDDEN_BINDING_NAMES = new Set([
918
- ...Expression.FORBIDDEN_NAMES.filter(name => !Expression.REBINDABLE_FORBIDDEN_NAMES.has(name)),
919
- 'constructor',
920
- '__proto__',
921
- 'prototype',
922
- ...Expression.STRICT_FORBIDDEN_NAMES,
923
- ]);
924
- /** プロパティアクセスで拒否する名前 */
925
- Expression.FORBIDDEN_PROPERTY_NAMES = new Set([
926
- 'constructor',
927
- '__proto__',
928
- 'prototype',
929
- ]);
930
- /** object literal のプロパティ定義で前置修飾子として扱う識別子 */
931
- Expression.OBJECT_PROPERTY_MODIFIERS = new Set([
932
- 'get',
933
- 'set',
934
- 'async',
935
- ]);
936
- /** 式構文として許可しない予約語 */
937
- Expression.DISALLOWED_KEYWORDS = new Set([
938
- 'await',
939
- 'break',
940
- 'case',
941
- 'catch',
942
- 'class',
943
- 'const',
944
- 'continue',
945
- 'debugger',
946
- 'default',
947
- 'delete',
948
- 'do',
949
- 'else',
950
- 'export',
951
- 'finally',
952
- 'for',
953
- 'function',
954
- 'if',
955
- 'import',
956
- 'in',
957
- 'instanceof',
958
- 'let',
959
- 'new',
960
- 'return',
961
- 'switch',
962
- 'this',
963
- 'throw',
964
- 'try',
965
- 'typeof',
966
- 'var',
967
- 'void',
968
- 'while',
969
- 'with',
970
- 'yield',
971
- ]);
972
- /** 式 → 評価関数のグローバルキャッシュ */
973
- Expression.EXPRESSION_CACHE = new Map();
974
- export default Expression;
975
- //# sourceMappingURL=expression.js.map