@dooboostore/simple-web-component 1.0.49 → 1.0.51

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 (251) hide show
  1. package/README.md +790 -337
  2. package/dist/cjs/SwcAppEngine.js.map +2 -2
  3. package/dist/cjs/decorators/addEventListener.js +1925 -22
  4. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  5. package/dist/cjs/decorators/applyAttribute.js +41 -47
  6. package/dist/cjs/decorators/applyAttribute.js.map +2 -2
  7. package/dist/cjs/decorators/applyClass.js +14 -11
  8. package/dist/cjs/decorators/applyClass.js.map +2 -2
  9. package/dist/cjs/decorators/applyNode.js +19 -21
  10. package/dist/cjs/decorators/applyNode.js.map +2 -2
  11. package/dist/cjs/decorators/applyProperty.js +24 -48
  12. package/dist/cjs/decorators/applyProperty.js.map +2 -2
  13. package/dist/cjs/decorators/applySlot.js +9 -8
  14. package/dist/cjs/decorators/applySlot.js.map +2 -2
  15. package/dist/cjs/decorators/applyStyle.js +14 -11
  16. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  17. package/dist/cjs/decorators/around.js +124 -0
  18. package/dist/cjs/decorators/around.js.map +7 -0
  19. package/dist/cjs/decorators/changedAttribute.js +61 -24
  20. package/dist/cjs/decorators/changedAttribute.js.map +2 -2
  21. package/dist/cjs/decorators/elementDefine.js +240 -196
  22. package/dist/cjs/decorators/elementDefine.js.map +3 -3
  23. package/dist/cjs/decorators/emitCustomEvent.js +14 -12
  24. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  25. package/dist/cjs/decorators/eventMedia.js +112 -0
  26. package/dist/cjs/decorators/eventMedia.js.map +7 -0
  27. package/dist/cjs/decorators/fetch.js +278 -0
  28. package/dist/cjs/decorators/fetch.js.map +7 -0
  29. package/dist/cjs/decorators/index.js +8 -2
  30. package/dist/cjs/decorators/index.js.map +2 -2
  31. package/dist/cjs/decorators/intersectionObserver.js +76 -65
  32. package/dist/cjs/decorators/intersectionObserver.js.map +2 -2
  33. package/dist/cjs/decorators/lifecycles.js +11 -11
  34. package/dist/cjs/decorators/lifecycles.js.map +2 -2
  35. package/dist/cjs/decorators/mutationObserver.js +97 -90
  36. package/dist/cjs/decorators/mutationObserver.js.map +3 -3
  37. package/dist/cjs/decorators/parameter.js +156 -0
  38. package/dist/cjs/decorators/parameter.js.map +7 -0
  39. package/dist/cjs/decorators/publishSwcAppMessage.js +2 -2
  40. package/dist/cjs/decorators/publishSwcAppMessage.js.map +2 -2
  41. package/dist/cjs/decorators/query.js +11 -5
  42. package/dist/cjs/decorators/query.js.map +2 -2
  43. package/dist/cjs/decorators/requestAnimationFrame.js +115 -0
  44. package/dist/cjs/decorators/requestAnimationFrame.js.map +7 -0
  45. package/dist/cjs/decorators/resizeObserver.js +84 -69
  46. package/dist/cjs/decorators/resizeObserver.js.map +3 -3
  47. package/dist/cjs/decorators/setInterval.js +65 -22
  48. package/dist/cjs/decorators/setInterval.js.map +2 -2
  49. package/dist/cjs/decorators/setTimeout.js +68 -22
  50. package/dist/cjs/decorators/setTimeout.js.map +2 -2
  51. package/dist/cjs/decorators/state.js +13 -10
  52. package/dist/cjs/decorators/state.js.map +2 -2
  53. package/dist/cjs/decorators/{subscribeSwcAppMessageWhileConnected.js → subscribeSwcAppMessage.js} +20 -21
  54. package/dist/cjs/decorators/subscribeSwcAppMessage.js.map +7 -0
  55. package/dist/cjs/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +23 -19
  56. package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +7 -0
  57. package/dist/cjs/decorators/swcAppRoute.js +149 -0
  58. package/dist/cjs/decorators/swcAppRoute.js.map +7 -0
  59. package/dist/cjs/elements/SwcApp.js +70 -0
  60. package/dist/cjs/elements/SwcApp.js.map +2 -2
  61. package/dist/cjs/elements/SwcAppMixin.js +256 -67
  62. package/dist/cjs/elements/SwcAppMixin.js.map +3 -3
  63. package/dist/cjs/types/index.js +5 -1
  64. package/dist/cjs/types/index.js.map +2 -2
  65. package/dist/cjs/types/window.d.js.map +1 -1
  66. package/dist/cjs/utils/Utils.js +36 -15
  67. package/dist/cjs/utils/Utils.js.map +2 -2
  68. package/dist/esm/SwcAppEngine.js.map +2 -2
  69. package/dist/esm/decorators/addEventListener.js +1925 -22
  70. package/dist/esm/decorators/addEventListener.js.map +2 -2
  71. package/dist/esm/decorators/applyAttribute.js +41 -47
  72. package/dist/esm/decorators/applyAttribute.js.map +2 -2
  73. package/dist/esm/decorators/applyClass.js +14 -11
  74. package/dist/esm/decorators/applyClass.js.map +2 -2
  75. package/dist/esm/decorators/applyNode.js +20 -22
  76. package/dist/esm/decorators/applyNode.js.map +2 -2
  77. package/dist/esm/decorators/applyProperty.js +24 -48
  78. package/dist/esm/decorators/applyProperty.js.map +2 -2
  79. package/dist/esm/decorators/applySlot.js +9 -8
  80. package/dist/esm/decorators/applySlot.js.map +2 -2
  81. package/dist/esm/decorators/applyStyle.js +14 -11
  82. package/dist/esm/decorators/applyStyle.js.map +2 -2
  83. package/dist/esm/decorators/around.js +105 -0
  84. package/dist/esm/decorators/around.js.map +7 -0
  85. package/dist/esm/decorators/changedAttribute.js +61 -24
  86. package/dist/esm/decorators/changedAttribute.js.map +2 -2
  87. package/dist/esm/decorators/elementDefine.js +238 -194
  88. package/dist/esm/decorators/elementDefine.js.map +3 -3
  89. package/dist/esm/decorators/emitCustomEvent.js +15 -13
  90. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  91. package/dist/esm/decorators/eventMedia.js +94 -0
  92. package/dist/esm/decorators/eventMedia.js.map +7 -0
  93. package/dist/esm/decorators/fetch.js +259 -0
  94. package/dist/esm/decorators/fetch.js.map +7 -0
  95. package/dist/esm/decorators/index.js +8 -2
  96. package/dist/esm/decorators/index.js.map +2 -2
  97. package/dist/esm/decorators/intersectionObserver.js +76 -65
  98. package/dist/esm/decorators/intersectionObserver.js.map +2 -2
  99. package/dist/esm/decorators/lifecycles.js +11 -11
  100. package/dist/esm/decorators/lifecycles.js.map +2 -2
  101. package/dist/esm/decorators/mutationObserver.js +97 -90
  102. package/dist/esm/decorators/mutationObserver.js.map +3 -3
  103. package/dist/esm/decorators/parameter.js +137 -0
  104. package/dist/esm/decorators/parameter.js.map +7 -0
  105. package/dist/esm/decorators/publishSwcAppMessage.js +2 -2
  106. package/dist/esm/decorators/publishSwcAppMessage.js.map +2 -2
  107. package/dist/esm/decorators/query.js +11 -5
  108. package/dist/esm/decorators/query.js.map +2 -2
  109. package/dist/esm/decorators/requestAnimationFrame.js +96 -0
  110. package/dist/esm/decorators/requestAnimationFrame.js.map +7 -0
  111. package/dist/esm/decorators/resizeObserver.js +84 -69
  112. package/dist/esm/decorators/resizeObserver.js.map +2 -2
  113. package/dist/esm/decorators/setInterval.js +65 -23
  114. package/dist/esm/decorators/setInterval.js.map +2 -2
  115. package/dist/esm/decorators/setTimeout.js +68 -23
  116. package/dist/esm/decorators/setTimeout.js.map +2 -2
  117. package/dist/esm/decorators/state.js +13 -10
  118. package/dist/esm/decorators/state.js.map +2 -2
  119. package/dist/esm/decorators/subscribeSwcAppMessage.js +53 -0
  120. package/dist/esm/decorators/subscribeSwcAppMessage.js.map +7 -0
  121. package/dist/esm/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +20 -16
  122. package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +7 -0
  123. package/dist/esm/decorators/swcAppRoute.js +130 -0
  124. package/dist/esm/decorators/swcAppRoute.js.map +7 -0
  125. package/dist/esm/elements/SwcApp.js +70 -0
  126. package/dist/esm/elements/SwcApp.js.map +2 -2
  127. package/dist/esm/elements/SwcAppMixin.js +258 -69
  128. package/dist/esm/elements/SwcAppMixin.js.map +3 -3
  129. package/dist/esm/types/index.js +5 -1
  130. package/dist/esm/types/index.js.map +2 -2
  131. package/dist/esm/utils/Utils.js +36 -15
  132. package/dist/esm/utils/Utils.js.map +2 -2
  133. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +4395 -1266
  134. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  135. package/dist/types/SwcAppEngine.d.ts +1 -0
  136. package/dist/types/SwcAppEngine.d.ts.map +1 -1
  137. package/dist/types/decorators/addEventListener.d.ts +936 -11
  138. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  139. package/dist/types/decorators/applyAttribute.d.ts +27 -37
  140. package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
  141. package/dist/types/decorators/applyClass.d.ts +2 -2
  142. package/dist/types/decorators/applyClass.d.ts.map +1 -1
  143. package/dist/types/decorators/applyNode.d.ts +16 -16
  144. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  145. package/dist/types/decorators/applyProperty.d.ts +28 -44
  146. package/dist/types/decorators/applyProperty.d.ts.map +1 -1
  147. package/dist/types/decorators/applySlot.d.ts.map +1 -1
  148. package/dist/types/decorators/applyStyle.d.ts +2 -2
  149. package/dist/types/decorators/applyStyle.d.ts.map +1 -1
  150. package/dist/types/decorators/around.d.ts +87 -0
  151. package/dist/types/decorators/around.d.ts.map +1 -0
  152. package/dist/types/decorators/changedAttribute.d.ts +22 -4
  153. package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
  154. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  155. package/dist/types/decorators/emitCustomEvent.d.ts +0 -1
  156. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  157. package/dist/types/decorators/eventMedia.d.ts +48 -0
  158. package/dist/types/decorators/eventMedia.d.ts.map +1 -0
  159. package/dist/types/decorators/fetch.d.ts +139 -0
  160. package/dist/types/decorators/fetch.d.ts.map +1 -0
  161. package/dist/types/decorators/index.d.ts +8 -2
  162. package/dist/types/decorators/index.d.ts.map +1 -1
  163. package/dist/types/decorators/intersectionObserver.d.ts +32 -10
  164. package/dist/types/decorators/intersectionObserver.d.ts.map +1 -1
  165. package/dist/types/decorators/lifecycles.d.ts +8 -7
  166. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  167. package/dist/types/decorators/mutationObserver.d.ts +34 -12
  168. package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
  169. package/dist/types/decorators/parameter.d.ts +113 -0
  170. package/dist/types/decorators/parameter.d.ts.map +1 -0
  171. package/dist/types/decorators/publishSwcAppMessage.d.ts +0 -1
  172. package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -1
  173. package/dist/types/decorators/query.d.ts +7 -7
  174. package/dist/types/decorators/query.d.ts.map +1 -1
  175. package/dist/types/decorators/requestAnimationFrame.d.ts +47 -0
  176. package/dist/types/decorators/requestAnimationFrame.d.ts.map +1 -0
  177. package/dist/types/decorators/resizeObserver.d.ts +34 -12
  178. package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
  179. package/dist/types/decorators/setInterval.d.ts +27 -5
  180. package/dist/types/decorators/setInterval.d.ts.map +1 -1
  181. package/dist/types/decorators/setTimeout.d.ts +27 -5
  182. package/dist/types/decorators/setTimeout.d.ts.map +1 -1
  183. package/dist/types/decorators/state.d.ts.map +1 -1
  184. package/dist/types/decorators/subscribeSwcAppMessage.d.ts +44 -0
  185. package/dist/types/decorators/subscribeSwcAppMessage.d.ts.map +1 -0
  186. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +60 -0
  187. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +1 -0
  188. package/dist/types/decorators/swcAppRoute.d.ts +94 -0
  189. package/dist/types/decorators/swcAppRoute.d.ts.map +1 -0
  190. package/dist/types/elements/SwcApp.d.ts.map +1 -1
  191. package/dist/types/elements/SwcAppMixin.d.ts +423 -388
  192. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  193. package/dist/types/types/index.d.ts +43 -4
  194. package/dist/types/types/index.d.ts.map +1 -1
  195. package/dist/types/utils/Utils.d.ts +15 -5
  196. package/dist/types/utils/Utils.d.ts.map +1 -1
  197. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +4395 -1266
  198. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  199. package/package.json +8 -6
  200. package/src/SwcAppEngine.ts +1 -0
  201. package/src/decorators/addEventListener.ts +1086 -28
  202. package/src/decorators/applyAttribute.ts +82 -134
  203. package/src/decorators/applyClass.ts +14 -10
  204. package/src/decorators/applyNode.ts +39 -37
  205. package/src/decorators/applyProperty.ts +72 -100
  206. package/src/decorators/applySlot.ts +9 -8
  207. package/src/decorators/applyStyle.ts +14 -10
  208. package/src/decorators/around.ts +196 -0
  209. package/src/decorators/changedAttribute.ts +65 -26
  210. package/src/decorators/elementDefine.ts +298 -220
  211. package/src/decorators/emitCustomEvent.ts +17 -13
  212. package/src/decorators/eventMedia.ts +119 -0
  213. package/src/decorators/fetch.ts +454 -0
  214. package/src/decorators/index.ts +8 -2
  215. package/src/decorators/intersectionObserver.ts +108 -78
  216. package/src/decorators/lifecycles.ts +24 -22
  217. package/src/decorators/mutationObserver.ts +140 -104
  218. package/src/decorators/parameter.ts +212 -0
  219. package/src/decorators/publishSwcAppMessage.ts +3 -4
  220. package/src/decorators/query.ts +22 -14
  221. package/src/decorators/requestAnimationFrame.ts +149 -0
  222. package/src/decorators/resizeObserver.ts +119 -82
  223. package/src/decorators/setInterval.ts +90 -23
  224. package/src/decorators/setTimeout.ts +93 -23
  225. package/src/decorators/state.ts +19 -13
  226. package/src/decorators/subscribeSwcAppMessage.ts +106 -0
  227. package/src/decorators/subscribeSwcAppRouteChange.ts +131 -0
  228. package/src/decorators/swcAppRoute.ts +231 -0
  229. package/src/elements/SwcApp.ts +40 -10
  230. package/src/elements/SwcAppMixin.ts +329 -85
  231. package/src/types/index.ts +38 -5
  232. package/src/types/window.d.ts +2 -18
  233. package/src/utils/Utils.ts +50 -18
  234. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
  235. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
  236. package/dist/cjs/examples/RxJSOperatorsExample.js +0 -285
  237. package/dist/cjs/examples/RxJSOperatorsExample.js.map +0 -7
  238. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +0 -54
  239. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
  240. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
  241. package/dist/esm/examples/RxJSOperatorsExample.js +0 -266
  242. package/dist/esm/examples/RxJSOperatorsExample.js.map +0 -7
  243. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +0 -23
  244. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +0 -1
  245. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +0 -31
  246. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +0 -1
  247. package/dist/types/examples/RxJSOperatorsExample.d.ts +0 -94
  248. package/dist/types/examples/RxJSOperatorsExample.d.ts.map +0 -1
  249. package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +0 -91
  250. package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +0 -107
  251. package/src/examples/RxJSOperatorsExample.ts +0 -279
@@ -11,15 +11,21 @@ import {getQueryMetadata, getQueryAllMetadata} from './query';
11
11
  import {SwcUtils} from '../utils/Utils';
12
12
  import {DOM_EVENT_NAMES, HTML_TAG_ENTRIES} from '../config/config';
13
13
  import {SituationTypeContainer, SituationTypeContainers} from '@dooboostore/simple-boot/decorators/inject/Inject';
14
- import {ElementDefineLifeCycler, HelperHostSet, HostSet, InjectSituationType, IntersectionObserverSet, MutationObserverSet, OnConnectedResult, ResizeObserverSet, ResizeObserverSetEntry, SwcRootType} from '../types';
14
+ import {FirstCheckMaker} from '@dooboostore/simple-boot';
15
+ import {buildSwcParameterArgs, getParameterMetadata} from './parameter';
16
+ import {ElementDefineLifeCycler, HelperHostSet, HostSet, InjectSituationType, IntersectionObserverSet, MutationObserverSet, MutationObserverSetEntry, ObserverScope, OnConnectedResult, ResizeObserverSet, ResizeObserverSetEntry, SwcRootType} from '../types';
15
17
  import {ConvertUtils, ElementApply} from '@dooboostore/core-web';
16
18
  import {isSSR} from "../elements/SwcAppMixin";
17
19
  import {findAllStateMetadata} from "./state";
20
+ import {findAllAroundMetadata} from "./around";
18
21
  import {findAllPropertyMetadata} from "./applyProperty";
19
- import {MessageSubscribeLifeCycler} from "./subscribeSwcAppMessageWhileConnected";
20
- import {RouteSubscribeLifeCycler} from "./subscribeSwcAppRouteChangeWhileConnected";
22
+ import {MessageSubscribeLifeCycler} from "./subscribeSwcAppMessage";
23
+ import {RouteSubscribeLifeCycler} from "./subscribeSwcAppRouteChange";
21
24
  import {SetIntervalLifeCycler} from "./setInterval";
22
25
  import {SetTimeoutLifeCycler} from "./setTimeout";
26
+ import {RequestAnimationFrameLifeCycler} from "./requestAnimationFrame";
27
+ import {EventMediaLifeCycler} from "./eventMedia";
28
+ import {FetchLifeCycler} from "./fetch";
23
29
 
24
30
  // --- Core Interfaces & Types ---
25
31
 
@@ -39,7 +45,7 @@ export interface ElementMetadata extends Omit<ElementConfig, 'window'> {
39
45
  }
40
46
 
41
47
  export const getElementConfig = (target: any): ElementMetadata | undefined => {
42
- const constructor = target instanceof Function ? target : target.constructor;
48
+ const constructor = typeof target === 'function' ? target : target.constructor;
43
49
  return ReflectUtils.getMetadata(ELEMENT_CONFIG_KEY, constructor);
44
50
  };
45
51
 
@@ -53,7 +59,7 @@ export const ensureInit = (inst: any) => { // HTMLElement
53
59
  // delete (inst as any)[meta.propertyKey];
54
60
  // });
55
61
 
56
- const target = inst instanceof Function ? inst : inst.constructor;
62
+ const target = typeof inst === 'function' ? inst : inst.constructor;
57
63
 
58
64
  // ╔════════════════════════════════════════════════════════════════════════════════╗
59
65
  // ║ decorator 필드의 own property 삭제 ║
@@ -112,6 +118,19 @@ export const ensureInit = (inst: any) => { // HTMLElement
112
118
  const propertyAllList = findAllPropertyMetadata(target).filter(it => it.type === 'property');
113
119
  if (propertyAllList) {
114
120
  propertyAllList.forEach(meta => {
121
+ // getter가 없으면(순수 필드: declare·하이드레이션 값 등) 지우지 않음.
122
+ // getter가 있을 때만 own 값을 지워 getter가 DOM을 새로 읽게 한다.
123
+ let proto = Object.getPrototypeOf(inst);
124
+ let hasGetter = false;
125
+ while (proto && proto !== Object.prototype) {
126
+ const d = Object.getOwnPropertyDescriptor(proto, meta.propertyKey);
127
+ if (d) {
128
+ hasGetter = typeof d.get === 'function';
129
+ break;
130
+ }
131
+ proto = Object.getPrototypeOf(proto);
132
+ }
133
+ if (!hasGetter) return;
115
134
  delete (inst as any)[meta.propertyKey];
116
135
  });
117
136
  }
@@ -119,9 +138,31 @@ export const ensureInit = (inst: any) => { // HTMLElement
119
138
  const stateAllList = findAllStateMetadata(target);
120
139
  if (stateAllList) {
121
140
  stateAllList.forEach(meta => {
122
- const initUserData = (inst as any)[meta.propertyKey];
123
- delete (inst as any)[meta.propertyKey];
124
- (inst as any)[meta.propertyKey] = initUserData;
141
+ // getter로 읽으면 안 됨 — own이 남아있을 때 getter가 이주까지 해버려
142
+ // setter 재대입과 합치면 DOM 동기화가 중복 실행됨. descriptor에서 raw를 꺼낸다.
143
+ // own이 없으면(이미 정리됨) 스킵.
144
+ const own = Object.getOwnPropertyDescriptor(inst, meta.propertyKey);
145
+ if (own && 'value' in own) {
146
+ const initUserData = own.value;
147
+ delete (inst as any)[meta.propertyKey];
148
+ (inst as any)[meta.propertyKey] = initUserData;
149
+ }
150
+ });
151
+ }
152
+
153
+ // @around 필드의 own property 정리 → setter 경유로 재대입 (초기값에 set() 적용).
154
+ // attribute/query처럼 plain delete하면 초기값이 날아간다 — 반드시 살려서 setter로 넣는다.
155
+ // getter로 읽으면 안 됨(before 변환값이 섞임) — descriptor에서 raw를 꺼낸다.
156
+ // own이 없으면(이미 정리됨) 스킵 — 그래야 중복 변환이 안 생긴다.
157
+ const aroundAllList = findAllAroundMetadata(target);
158
+ if (aroundAllList) {
159
+ aroundAllList.forEach(meta => {
160
+ const own = Object.getOwnPropertyDescriptor(inst, meta.propertyKey);
161
+ if (own && 'value' in own) {
162
+ const initUserData = own.value;
163
+ delete (inst as any)[meta.propertyKey];
164
+ (inst as any)[meta.propertyKey] = initUserData;
165
+ }
125
166
  });
126
167
  }
127
168
 
@@ -151,7 +192,7 @@ const handleGlobalSwcEvent = async (event: Event) => {
151
192
  if (!(node instanceof HTMLElement)) continue;
152
193
  const script = node.getAttribute?.(attrName);
153
194
  if (script && !getElementConfig(node)) {
154
- const host: any = SwcUtils.findNearestSwcHostIncludingSelf(node);
195
+ const host: any = SwcUtils.findNearestSwcAncestorHost(node);
155
196
  if (host && host.isConnected && typeof host.__swc_executeAttributeEvent === 'function') {
156
197
  await host.__swc_executeAttributeEvent(node, attrName, script, event);
157
198
  (event as any).__swc_handled = true;
@@ -209,8 +250,7 @@ const setupPrototype = (proto: any, win: Window) => {
209
250
  const script = this.getAttribute(attrName);
210
251
  if (script) {
211
252
  try {
212
- const conf = getElementConfig(this);
213
- const currentWin = (this as any)._resolveWindow(conf);
253
+ const currentWin = SwcUtils.resolveWindow(this);
214
254
  const helpers = SwcUtils.getHelperSet(currentWin);
215
255
  const args = {...hostSet, ...helpers, ...extraArgs, $el: this, $root: this.getRootNode()};
216
256
  FunctionUtils.execute({script, context: this, args});
@@ -220,22 +260,18 @@ const setupPrototype = (proto: any, win: Window) => {
220
260
  }
221
261
  };
222
262
 
223
- proto._resolveWindow = function (localConfig?: ElementConfig): Window {
224
- ensureInit(this);
225
- if (localConfig?.window) return localConfig.window;
226
- const ancestors = SwcUtils.findAllSwcHostsIncludingSelf(this as any);
227
- for (let i = ancestors.length - 1; i >= 0; i--) {
228
- const aConf = getElementConfig(ancestors[i]);
229
- if (aConf?.window) return aConf.window;
230
- }
231
- return getElementConfig(this).window ?? ((typeof window !== 'undefined' ? window : undefined) as Window);
232
- };
233
-
234
263
  proto._invokeLifecycleMethod = function (methodName: string | symbol, hostSet?: HostSet, extraArgs: any[] = []) {
235
264
  ensureInit(this);
236
265
  if (typeof (this as any)[methodName] !== 'function') return;
237
- const useHostSet = hostSet ?? SwcUtils.getHelperAndHostSet(win, this);
266
+ const useHostSet = hostSet ?? SwcUtils.getHelperAndHostSet(this);
238
267
  const app = useHostSet?.$appHost?.simpleApplication;
268
+
269
+ // @hostSet/@helperHostSet/@helperSet(parameter.ts)를 위한 값. lifecycle 메서드는
270
+ // event/matched 개념이 없으므로 이 세 가지만 채운다.
271
+ const helperSetValue = SwcUtils.getHelperSet(win);
272
+ const helperHostSetValue = {...helperSetValue, ...useHostSet, $this: this};
273
+ const kindValues = {hostSet: useHostSet, helperHostSet: helperHostSetValue, helperSet: helperSetValue};
274
+
239
275
  // console.log('---->hh',app, this, methodName);
240
276
  if (app) {
241
277
  const otherStorage = new Map<any, any>();
@@ -248,16 +284,33 @@ const setupPrototype = (proto: any, win: Window) => {
248
284
  }), new SituationTypeContainer({situationType: InjectSituationType.LAST_APP_HOST, data: useHostSet.$lastAppHost})]);
249
285
  otherStorage.set(SituationTypeContainers, situations);
250
286
 
287
+ // @hostSet/@helperHostSet/@helperSet가 붙은 파라미터는 @Inject/situationType 기반
288
+ // 해석보다 먼저 처리되고(firstCheckMaker), 그 외 파라미터는 기존처럼 @Inject/타입
289
+ // 기반으로 정상 해석된다 — 같은 메서드에서 두 방식을 섞어 써도 된다.
290
+ // swc 파라미터 데코레이터가 붙은 자리는 DI 로 넘기지 않는다. lifecycle 이 못 채우는 kind
291
+ // (예: @fetchSettled — @fetch 가 나중에 채움)는 null. undefined 를 돌려주면
292
+ // "처리 안 함"으로 보고 DI 가 파라미터 타입(Object 등)을 resolve 하려다 SimNoSuch 로 죽는다.
293
+ const firstCheckMaker: FirstCheckMaker = ({target, targetKey}, token, idx) => {
294
+ const saves = getParameterMetadata(target, targetKey!);
295
+ const found = saves.find(s => s.index === idx);
296
+ return found ? ((kindValues as any)[found.kind] ?? null) : undefined;
297
+ };
298
+
251
299
  return app.simstanceManager.executeBindParameterSim(
252
300
  {
253
301
  target: this,
254
302
  targetKey: methodName,
255
- inputParameters: extraArgs
303
+ inputParameters: extraArgs,
304
+ firstCheckMaker: [firstCheckMaker]
256
305
  },
257
306
  otherStorage
258
307
  );
259
308
  } else {
260
- return (this as any)[methodName](...extraArgs);
309
+ // SimpleApplication(DI 컨테이너)이 없는 standalone 사용 시에도 @hostSet/@helperHostSet/
310
+ // @helperSet만큼은 buildSwcParameterArgs로 주입해준다 — 해당 데코레이터가 하나도 없으면
311
+ // extraArgs를 그대로 반환하므로(기존 동작 그대로) 하위호환 깨지지 않는다.
312
+ const args = buildSwcParameterArgs(this, methodName, kindValues, extraArgs);
313
+ return (this as any)[methodName](...args);
261
314
  }
262
315
  };
263
316
 
@@ -279,8 +332,7 @@ const setupPrototype = (proto: any, win: Window) => {
279
332
 
280
333
  const handler = async (event: any) => {
281
334
  const hostSet = SwcUtils.getHostSet(el);
282
- const conf = getElementConfig(this);
283
- const currentWin = (this as any)._resolveWindow(conf);
335
+ const currentWin = SwcUtils.resolveWindow(this);
284
336
  const helpers = SwcUtils.getHelperSet(currentWin);
285
337
  const args = {
286
338
  event,
@@ -301,8 +353,7 @@ const setupPrototype = (proto: any, win: Window) => {
301
353
  ensureInit(this);
302
354
 
303
355
  const hostSet = SwcUtils.getHostSet(el);
304
- const conf = getElementConfig(this);
305
- const currentWin = (this as any)._resolveWindow(conf);
356
+ const currentWin = SwcUtils.resolveWindow(this);
306
357
  const currentHelpers = SwcUtils.getHelperSet(currentWin);
307
358
  const detail = (event as CustomEvent).detail;
308
359
  const args = {
@@ -319,6 +370,11 @@ const setupPrototype = (proto: any, win: Window) => {
319
370
  };
320
371
  };
321
372
 
373
+ // 부모 클래스도 elementDefine 됐으면 proto.xxxCallback 은 부모의 swc 래퍼다 — 그걸 원래 함수로 부르면 라이프사이클이 두 번 돈다.
374
+ // 래퍼에 기억해 둔 사용자 원래 함수(없으면 undefined)를 쓴다.
375
+ const SWC_ORIGINAL_CALLBACK = Symbol.for('simple-web-component:original-callback');
376
+ const userCallback = (fn: any) => (typeof fn === 'function' && SWC_ORIGINAL_CALLBACK in fn ? fn[SWC_ORIGINAL_CALLBACK] : fn);
377
+
322
378
  export const elementDefine =
323
379
  (name: string, config: Partial<ElementConfig> = {}): ClassDecorator =>
324
380
  (constructor: any) => {
@@ -357,34 +413,87 @@ export const elementDefine =
357
413
  const proto = constructor.prototype;
358
414
  setupPrototype(proto, win);
359
415
 
360
- // 값을 기대하지않는다.
361
- let helperHostSet: HelperHostSet | null = null;
416
+ // helperHostSet 은 인스턴스마다 다르다 — 클래스 단위 변수로 두면 다른 인스턴스의 connect 가 덮어써서
417
+ // disconnect 때 엉뚱한 인스턴스가 정리된다. connect 시점 값을 인스턴스에 저장해 disconnect/adopt 에서 쓴다.
418
+ const connectedHelperHostSet = (inst: any): HelperHostSet => inst.__swc_helperHostSet ?? SwcUtils.getHelperAndHostSet(inst);
362
419
  // ── cyclers: 데코레이터별 라이프사이클 위임 클래스 (elementDefine 시 1회 생성, 계속 재사용) ──
363
420
  const eventCycler = new EventListenerLifeCycler();
421
+ const resizeCycler = new ResizeObserverLifeCycler();
422
+ const intersectionCycler = new IntersectionObserverLifeCycler();
423
+ const mutationCycler = new MutationObserverLifeCycler();
364
424
  const cyclers: ElementDefineLifeCycler[] = [
365
425
  eventCycler,
366
426
  new ChangedAttributeLifeCycler(),
367
427
  new EmitCustomEventLifeCycler(),
368
- new ResizeObserverLifeCycler(),
369
- new IntersectionObserverLifeCycler(),
370
- new MutationObserverLifeCycler(),
428
+ resizeCycler,
429
+ intersectionCycler,
430
+ mutationCycler,
371
431
  new MessageSubscribeLifeCycler(),
372
432
  new RouteSubscribeLifeCycler(),
373
433
  new SetIntervalLifeCycler(),
374
434
  new SetTimeoutLifeCycler(),
435
+ new RequestAnimationFrameLifeCycler(),
436
+ new EventMediaLifeCycler(),
437
+ new FetchLifeCycler(),
375
438
  ];
376
439
 
377
440
  // observedAttributes 기여 cycler (define-time, constructor 기반)
378
441
  const cyclerObservedAttributes = cyclers.flatMap(c => c.getObservedAttributeNames?.(constructor) ?? []);
379
442
  const mergedObservedAttributes = [...new Set([...(metadata.observedAttributes ?? []), ...originalStaticObservedAttributes, ...cyclerObservedAttributes, ...attributeApplyNames, ...hostAttributes, ...swcLifecycleAttributes, ...swcOnEvents])];
380
443
 
381
- // 렌더 후 모든 시클러 onConnected 호출 → Set 수집 후 observer 생성
382
- const buildObservers = async (helperHostSet: HelperHostSet): Promise<void> => {
444
+ // ── observer 빌드 (connect 1회당 상태 1개) ──
445
+ // MutationObserver·ResizeObserver 는 인스턴스당 1개씩만 만들고 모든 데코레이터가 공유한다 (delegate 추적 포함).
446
+ // 'self'(렌더 전): 자기 자신 대상 mutation/resize 만 붙여 첫 렌더부터 본다.
447
+ // 'rest'(렌더 후): 나머지 전부를 같은 옵저버에 덧붙인다.
448
+ // 시클러가 scope 별로 자기 메타만 담은 콜백을 주고, 메타는 자기 대상 레코드/엔트리만 받으므로 따로 나눌 필요가 없다.
449
+ // IntersectionObserver 는 옵션 그룹별로 1개 ('rest' 전용).
450
+ type IntersectionDelegate = { target: string; delegateRoot?: SwcRootType; observe: (el: Element) => void; unobserve: (el: Element) => void };
451
+ interface ObserverState {
452
+ observers: Array<MutationObserver | ResizeObserver | IntersectionObserver>;
453
+ ro?: ResizeObserver;
454
+ roCallbacks: Set<ResizeObserverSetEntry['callback']>;
455
+ roObserved: WeakSet<Element>;
456
+ resizeDelegates: Array<ResizeObserverSetEntry & { target: string }>;
457
+ intersectionDelegates: IntersectionDelegate[];
458
+ mo?: MutationObserver;
459
+ moCallbacks: Set<MutationObserverSetEntry['callback']>;
460
+ moTargets: Map<Element | ShadowRoot, MutationObserverInit>;
461
+ }
462
+ const newObserverState = (): ObserverState => ({
463
+ observers: [], roCallbacks: new Set(), roObserved: new WeakSet(), resizeDelegates: [], intersectionDelegates: [],
464
+ moCallbacks: new Set(), moTargets: new Map(),
465
+ });
466
+
467
+ // 같은 target 이 여러 소스에서 지정되면 MutationObserverInit 을 합집합(최대 감지범위)으로 병합한다.
468
+ const mergeMutationInit = (existing: MutationObserverInit | undefined, next: MutationObserverInit): MutationObserverInit => {
469
+ if (!existing) return next;
470
+ const mergedFilter = existing.attributeFilter || next.attributeFilter
471
+ ? [...new Set([...(existing.attributeFilter ?? []), ...(next.attributeFilter ?? [])])]
472
+ : undefined;
473
+ return {
474
+ childList: existing.childList || next.childList,
475
+ attributes: existing.attributes || next.attributes,
476
+ characterData: existing.characterData || next.characterData,
477
+ subtree: existing.subtree || next.subtree,
478
+ attributeOldValue: existing.attributeOldValue || next.attributeOldValue,
479
+ characterDataOldValue: existing.characterDataOldValue || next.characterDataOldValue,
480
+ attributeFilter: mergedFilter,
481
+ };
482
+ };
483
+
484
+ // delegate 셀렉터에 맞는 요소(자신 또는 하위)를 찾아 observe/unobserve — MutationObserver 콜백에서 동적 추적용
485
+ const forDelegateMatches = (n: Node, selector: string, fn: (el: Element) => void) => {
486
+ if (n.nodeType !== 1) return;
487
+ const el = n as HTMLElement;
488
+ const isThis = selector === '$this' || selector === '';
489
+ if (isThis || el.matches?.(selector) || el.closest?.(selector)) fn(el);
490
+ else el.querySelectorAll(selector).forEach(fn);
491
+ };
492
+
493
+ const buildObservers = async (helperHostSet: HelperHostSet, scope: ObserverScope, st: ObserverState, alive: () => boolean): Promise<void> => {
383
494
  const inst = helperHostSet.$this;
384
- const observers: Array<MutationObserver | ResizeObserver | IntersectionObserver> = [];
385
- let resizeObserverSet: ResizeObserverSet = [];
386
- let mutationObserverSet: MutationObserverSet = [];
387
- let intersectionObserverSet: IntersectionObserverSet = [];
495
+ // 'self' 는 순수 수집기(observer 시클러)만 돌린다 — 리스너·타이머 등 부작용 시클러는 'rest' 에서 한 번만.
496
+ const activeCyclers = scope === 'self' ? [resizeCycler, mutationCycler] : cyclers;
388
497
 
389
498
  // delegate 추적 root 해석: shadow/light/auto/all → 실제 root 목록
390
499
  const resolveDelegateRoots = (delegateRoot: SwcRootType | undefined): (HTMLElement | ShadowRoot)[] => {
@@ -397,225 +506,143 @@ export const elementDefine =
397
506
  return roots;
398
507
  };
399
508
 
400
- // 1. 모든 시클러 onConnected → observer Set 수집 (여러 시클러가 반환한 Set 을 전부 누적)
401
- for (const c of cyclers) {
402
- const result = (await c.onConnected?.(helperHostSet, resizeObserverSet)) as OnConnectedResult | void;
509
+ // 1. 시클러 onConnected → observer Set 수집 (여러 시클러가 반환한 Set 을 전부 누적)
510
+ let resizeObserverSet: ResizeObserverSet = [];
511
+ let mutationObserverSet: MutationObserverSet = [];
512
+ let intersectionObserverSet: IntersectionObserverSet = [];
513
+ for (const c of activeCyclers) {
514
+ const result = (await c.onConnected?.(helperHostSet, resizeObserverSet, scope)) as OnConnectedResult | void;
515
+ // await 사이에 떼어졌으면(새 연결이 차례를 가져갔으면) 여기서 멈춘다 — 옵저버를 만들면 아무도 disconnect 하지 않아 샌다.
516
+ if (!alive()) return;
403
517
  if (!result) continue;
404
518
  if (result.resizeObserverSet?.length) resizeObserverSet = [...resizeObserverSet, ...result.resizeObserverSet];
405
519
  if (result.mutationObserverSet?.length) mutationObserverSet = [...mutationObserverSet, ...result.mutationObserverSet];
406
520
  if (result.intersectionObserverSet?.length) intersectionObserverSet = [...intersectionObserverSet, ...result.intersectionObserverSet];
407
521
  }
408
522
 
409
- // 2. 타입별로 observer 생성
410
- // ResizeObserver 먼저: mutation callback 이 observe/unobserve 를 주입받아야 함
411
- // observeTargets 중 delegate 항목은 셀렉터 문자열이라 초기 observe 하지 않는다 (동적 추적 전용).
412
- // 동적 observe/unobserve 는 중복 호출을 막기 위해 observe 대상 WeakSet 으로 추적한다.
413
- const resizeDelegates = (resizeObserverSet ?? []).filter(t => t.delegate && typeof t.target === 'string') as Array<ResizeObserverSetEntry & { target: string }>;
414
- let resizeObserve: ((el: Element, opts?: ResizeObserverOptions) => void) | undefined;
415
- let resizeUnobserve: ((el: Element) => void) | undefined;
416
- const resizeObserved = new WeakSet<Element>();
417
- if (resizeObserverSet?.length) {
418
- const roCallbacks = [...new Set(resizeObserverSet.map(e => e.callback))];
419
- const ro = new (win as any).ResizeObserver((entries, obs) => {
420
- for (const cb of roCallbacks) cb(entries, obs);
421
- });
523
+ // 2. ResizeObserver (1개). delegate 항목은 셀렉터 문자열이라 초기 observe 하지 않고 MutationObserver 로 동적 추적한다.
524
+ const resizeObserve = (el: Element, opts?: ResizeObserverOptions) => {
525
+ if (!st.ro || st.roObserved.has(el)) return;
526
+ st.ro.observe(el, opts);
527
+ st.roObserved.add(el);
528
+ };
529
+ const resizeUnobserve = (el: Element) => {
530
+ if (!st.ro || !st.roObserved.has(el)) return;
531
+ st.ro.unobserve(el);
532
+ st.roObserved.delete(el);
533
+ };
534
+ const newResizeDelegates: ObserverState['resizeDelegates'] = [];
535
+ if (resizeObserverSet.length) {
536
+ if (!st.ro) {
537
+ st.ro = new win.ResizeObserver((entries, obs) => { for (const cb of st.roCallbacks) cb(entries, obs); });
538
+ st.observers.push(st.ro);
539
+ }
422
540
  for (const e of resizeObserverSet) {
423
- if (e.delegate) continue;
541
+ st.roCallbacks.add(e.callback);
542
+ if (e.delegate) {
543
+ if (typeof e.target === 'string') newResizeDelegates.push(e as ResizeObserverSetEntry & { target: string });
544
+ continue;
545
+ }
424
546
  if (typeof e.target === 'string') {
425
547
  // 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
426
- const scope = resolveDelegateRoots(e.delegateRoot);
427
- for (const sr of scope) {
428
- sr.querySelectorAll(e.target).forEach(el => {
429
- if (!resizeObserved.has(el)) { ro.observe(el, e.options); resizeObserved.add(el); }
430
- });
431
- }
548
+ for (const r of resolveDelegateRoots(e.delegateRoot)) r.querySelectorAll(e.target).forEach(el => resizeObserve(el, e.options));
432
549
  continue;
433
550
  }
434
- ro.observe(e.target as Element, e.options);
435
- if (e.target instanceof Element) resizeObserved.add(e.target);
551
+ st.ro.observe(e.target as Element, e.options);
552
+ if (e.target instanceof Element) st.roObserved.add(e.target);
436
553
  }
437
- resizeObserve = (el, opts) => {
438
- if (resizeObserved.has(el)) return;
439
- ro.observe(el, opts);
440
- resizeObserved.add(el);
441
- };
442
- resizeUnobserve = (el) => {
443
- if (!resizeObserved.has(el)) return;
444
- ro.unobserve(el);
445
- resizeObserved.delete(el);
446
- };
447
- observers.push(ro);
554
+ st.resizeDelegates.push(...newResizeDelegates);
448
555
  }
449
556
 
450
- // IntersectionObserver: 옵션 그룹별로 1개씩 생성.
451
- // delegate 항목은 동적 추적용 셀렉터이므로 observe/unobserve 함수를 MutationObserver 콜백에 넘긴다.
452
- // 동적 observe/unobserve 중복 방지를 위해 그룹별 WeakSet 으로 추적한다.
453
- const intersectionDelegates: Array<{ target: string; delegateRoot?: SwcRootType; observe: (el: Element) => void; unobserve: (el: Element) => void }> = [];
557
+ // 3. IntersectionObserver: 옵션 그룹별로 1개. delegate 항목은 MutationObserver 로 동적 추적한다.
558
+ const newIntersectionDelegates: IntersectionDelegate[] = [];
454
559
  for (const group of intersectionObserverSet) {
455
560
  const ioCallbacks = [...new Set(group.observeTargets.map(e => e.callback))];
456
- const io = new (win as any).IntersectionObserver((entries, obs) => {
457
- for (const cb of ioCallbacks) cb(entries, obs);
458
- }, group.options);
561
+ const io = new win.IntersectionObserver((entries, obs) => { for (const cb of ioCallbacks) cb(entries, obs); }, group.options);
459
562
  const ioObserved = new WeakSet<Element>();
563
+ const observe = (el: Element) => { if (ioObserved.has(el)) return; io.observe(el); ioObserved.add(el); };
564
+ const unobserve = (el: Element) => { if (!ioObserved.has(el)) return; io.unobserve(el); ioObserved.delete(el); };
460
565
  for (const e of group.observeTargets) {
461
- if (e.delegate) {
462
- const target = e.target as string;
463
- const observe = (el: Element) => {
464
- if (ioObserved.has(el)) return;
465
- io.observe(el);
466
- ioObserved.add(el);
467
- };
468
- const unobserve = (el: Element) => {
469
- if (!ioObserved.has(el)) return;
470
- io.unobserve(el);
471
- ioObserved.delete(el);
472
- };
473
- intersectionDelegates.push({ target, delegateRoot: e.delegateRoot, observe, unobserve });
474
- continue;
475
- }
566
+ if (e.delegate) { newIntersectionDelegates.push({ target: e.target as string, delegateRoot: e.delegateRoot, observe, unobserve }); continue; }
476
567
  if (typeof e.target === 'string') {
477
- // 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
478
- const scope = resolveDelegateRoots(e.delegateRoot);
479
- for (const sr of scope) {
480
- sr.querySelectorAll(e.target).forEach(el => {
481
- if (!ioObserved.has(el)) { io.observe(el); ioObserved.add(el); }
482
- });
483
- }
568
+ for (const r of resolveDelegateRoots(e.delegateRoot)) r.querySelectorAll(e.target).forEach(observe);
484
569
  continue;
485
570
  }
486
- io.observe(e.target as Element);
487
- if (e.target instanceof Element) ioObserved.add(e.target);
571
+ observe(e.target as Element);
488
572
  }
489
- observers.push(io);
573
+ st.observers.push(io);
490
574
  }
491
-
492
- // resize/intersection delegate 동적 추적 — MutationObserver 콜백에서 추가/제거된 요소를 observe/unobserve 한다.
493
- const handleResizeDelegate = (n: Node, observe: boolean) => {
494
- if (!resizeObserve || !resizeUnobserve || n.nodeType !== 1) return;
495
- const el = n as HTMLElement;
496
- for (const d of resizeDelegates) {
497
- const isThis = d.target === '$this' || d.target === '';
498
- const matchesSel = (node: any) => {
499
- if (!node || node.nodeType !== 1) return false;
500
- if (isThis) return true;
501
- return (node as HTMLElement).matches?.(d.target) || !!(node as HTMLElement).closest?.(d.target);
502
- };
503
- if (matchesSel(el)) {
504
- observe ? resizeObserve(el, d.options)
505
- : resizeUnobserve(el);
506
- } else if (!isThis) {
507
- el.querySelectorAll(d.target).forEach(sub => {
508
- observe ? resizeObserve(sub as HTMLElement, d.options)
509
- : resizeUnobserve(sub as HTMLElement);
510
- });
511
- }
512
- }
513
- };
514
- const handleIntersectionDelegate = (n: Node, observe: boolean) => {
515
- if (n.nodeType !== 1) return;
516
- const el = n as HTMLElement;
517
- for (const d of intersectionDelegates) {
518
- const isThis = d.target === '$this' || d.target === '';
519
- const matchesSel = (node: any) => {
520
- if (!node || node.nodeType !== 1) return false;
521
- if (isThis) return true;
522
- return (node as HTMLElement).matches?.(d.target) || !!(node as HTMLElement).closest?.(d.target);
523
- };
524
- if (matchesSel(el)) {
525
- observe ? d.observe(el) : d.unobserve(el);
526
- } else if (!isThis) {
527
- el.querySelectorAll(d.target).forEach(sub => {
528
- observe ? d.observe(sub as HTMLElement) : d.unobserve(sub as HTMLElement);
529
- });
530
- }
575
+ st.intersectionDelegates.push(...newIntersectionDelegates);
576
+
577
+ // 4. MutationObserver (1개): mutation 콜백 + resize/intersection delegate 동적 추적을 함께 처리한다.
578
+ if (mutationObserverSet.length || newResizeDelegates.length || newIntersectionDelegates.length) {
579
+ if (!st.mo) {
580
+ st.mo = new win.MutationObserver((mutations, obs) => {
581
+ for (const mut of mutations) {
582
+ for (const [nodes, on] of [[mut.addedNodes, true], [mut.removedNodes, false]] as const) {
583
+ for (const n of Array.from(nodes)) {
584
+ for (const d of st.resizeDelegates) forDelegateMatches(n, d.target, el => on ? resizeObserve(el, d.options) : resizeUnobserve(el));
585
+ for (const d of st.intersectionDelegates) forDelegateMatches(n, d.target, el => on ? d.observe(el) : d.unobserve(el));
586
+ }
587
+ }
588
+ }
589
+ for (const cb of st.moCallbacks) cb(mutations, obs);
590
+ });
591
+ st.observers.push(st.mo);
531
592
  }
532
- };
533
-
534
- if (mutationObserverSet?.length || resizeDelegates.length > 0 || intersectionDelegates.length > 0) {
535
- // 하나의 MutationObserver 로 resize/intersection delegate 동적 추적 + mutation 콜백을 함께 처리
536
- const callbacks = [...new Set((mutationObserverSet ?? []).map(e => e.callback))];
537
- const mo = new (win as any).MutationObserver((mutations, obs) => {
538
- for (const mut of mutations) {
539
- Array.from(mut.addedNodes || []).forEach(n => { handleResizeDelegate(n as Node, true); handleIntersectionDelegate(n as Node, true); });
540
- Array.from(mut.removedNodes || []).forEach(n => { handleResizeDelegate(n as Node, false); handleIntersectionDelegate(n as Node, false); });
541
- }
542
- for (const cb of callbacks) cb(mutations, obs);
543
- });
544
-
545
- // observe 대상 전체(엔트리 + delegate 추적 root)를 하나로 모아 중복 observe 를 제거한다.
546
- // 같은 target 이 여러 소스에서 지정되면 MutationObserverInit 을 합집합(최대 감지범위)으로 병합한다.
547
- const moObserveTargets = new Map<Element | ShadowRoot, MutationObserverInit>();
548
- const mergeMutationInit = (existing: MutationObserverInit | undefined, next: MutationObserverInit): MutationObserverInit => {
549
- if (!existing) return next;
550
- const mergedFilter = existing.attributeFilter || next.attributeFilter
551
- ? [...new Set([...(existing.attributeFilter ?? []), ...(next.attributeFilter ?? [])])]
552
- : undefined;
553
- return {
554
- childList: existing.childList || next.childList,
555
- attributes: existing.attributes || next.attributes,
556
- characterData: existing.characterData || next.characterData,
557
- subtree: existing.subtree || next.subtree,
558
- attributeOldValue: existing.attributeOldValue || next.attributeOldValue,
559
- characterDataOldValue: existing.characterDataOldValue || next.characterDataOldValue,
560
- attributeFilter: mergedFilter,
561
- };
562
- };
563
593
  const addObserveTarget = (target: Element | ShadowRoot, options: MutationObserverInit) => {
564
- moObserveTargets.set(target, mergeMutationInit(moObserveTargets.get(target), options));
594
+ st.moTargets.set(target, mergeMutationInit(st.moTargets.get(target), options));
565
595
  };
566
-
567
- // 1) mutationObserverSet 엔트리
568
- for (const e of mutationObserverSet ?? []) {
596
+ for (const e of mutationObserverSet) {
597
+ st.moCallbacks.add(e.callback);
569
598
  if (e.delegate) continue;
570
599
  const init = e.options ?? { childList: true };
571
600
  if (typeof e.target === 'string') {
572
- // 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
573
- const scope = resolveDelegateRoots(e.delegateRoot);
574
- for (const sr of scope) {
575
- sr.querySelectorAll(e.target).forEach(el => addObserveTarget(el, init));
576
- }
601
+ for (const r of resolveDelegateRoots(e.delegateRoot)) r.querySelectorAll(e.target).forEach(el => addObserveTarget(el, init));
577
602
  continue;
578
603
  }
579
604
  addObserveTarget(e.target, init);
580
605
  }
581
-
582
- // 2) delegate 추적 root: shadow/light/all/auto 로 해석해 전부 observe
583
- for (const d of resizeDelegates) {
584
- for (const r of resolveDelegateRoots(d.delegateRoot)) addObserveTarget(r, { childList: true, subtree: true });
585
- }
586
- for (const d of intersectionDelegates) {
606
+ for (const d of [...newResizeDelegates, ...newIntersectionDelegates]) {
587
607
  for (const r of resolveDelegateRoots(d.delegateRoot)) addObserveTarget(r, { childList: true, subtree: true });
588
608
  }
589
-
590
- for (const [target, options] of moObserveTargets) {
591
- mo.observe(target, options);
592
- }
593
- observers.push(mo);
609
+ // 같은 노드를 다시 observe 하면 옵션만 교체된다 (등록·전달 중복 없음) → 'self' 분과 합쳐진 옵션으로 갱신
610
+ for (const [target, options] of st.moTargets) st.mo.observe(target, options);
594
611
  }
595
612
 
596
- inst.__swc_observers = observers;
613
+ inst.__swc_observers = st.observers;
597
614
  };
598
615
 
599
- const originalConnected = proto.connectedCallback;
616
+ const originalConnected = userCallback(proto.connectedCallback);
600
617
  proto.connectedCallback = async function () {
601
618
  ensureInit(this);
602
619
  // 재연결 시 누적 방지 — cycler 내부 리소스 초기화
603
- helperHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
620
+ const helperHostSet = SwcUtils.getHelperAndHostSet(this as any);
621
+ (this as any).__swc_helperHostSet = helperHostSet;
604
622
  const appHost = helperHostSet.$appHost;
605
623
  const useSsr = isSSR(this);
624
+ // 연결 처리는 await 를 거친다. 그 사이 떨어지거나 다시 붙으면(disconnect / 새 connect 가 차례 번호를 올리면) 이 연결은 멈춘다 —
625
+ // 안 그러면 떼어진 뒤에 리스너·타이머가 등록돼 새고, 다시 붙은 연결과 겹쳐 두 번씩 돈다.
626
+ // (isConnected 는 보지 않는다 — 직접 호출이나 SSR 처럼 붙어 있지 않아도 라이프사이클은 돌아야 한다)
627
+ const gen = ((this as any).__swc_connectGen = ((this as any).__swc_connectGen ?? 0) + 1);
628
+ const alive = () => (this as any).__swc_connectGen === gen;
629
+ const obsState = newObserverState();
606
630
  try {
607
631
  if (appHost && typeof (appHost as any)._connected === 'function') {
608
632
  await (appHost as any)._connected(this);
633
+ if (!alive()) return;
609
634
  } else if (appHost && typeof (appHost as any)) {
610
635
  (appHost as any)._connected_safari_and_standby ??= [];
611
636
  (appHost as any)._connected_safari_and_standby.push(this);
612
637
  }
613
638
 
614
639
  const conf = getElementConfig(this);
615
- const currentWin = (this as any)._resolveWindow(conf);
616
640
 
617
641
  // before-connected 라이프사이클 메서드 (@onConnectedBefore)
618
- for (const m of findAllOnConnectedBeforeMetadata(this)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
642
+ for (const m of findAllOnConnectedBeforeMetadata(this)) {
643
+ await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
644
+ if (!alive()) return;
645
+ }
619
646
  (this as any)._executeSwcScript('swc-on-before-connected', helperHostSet);
620
647
 
621
648
  // ── 렌더 (@onConnected / @onConnectedBody*) — elementDefine 책임 ──
@@ -626,6 +653,10 @@ export const elementDefine =
626
653
  this.attachShadow({ mode: mode as ShadowRootMode });
627
654
  }
628
655
 
656
+ // 자기 자신 대상 mutation/resize 는 렌더 전에 붙여 첫 렌더부터 본다 (shadow root 가 생긴 뒤라 $this 가 맞는 root 를 잡는다)
657
+ await buildObservers(helperHostSet, 'self', obsState, alive);
658
+ if (!alive()) return;
659
+
629
660
  const stateContext: any = { ...helperHostSet };
630
661
  findAllStateMetadata(this).forEach(it => {
631
662
  stateContext[it.name] = this[it.propertyKey];
@@ -635,8 +666,40 @@ export const elementDefine =
635
666
  const lightChildren: Node[] = [];
636
667
 
637
668
  if (targetConnectedList.length > 0) {
669
+ const toNodes = (value: any): Node[] => {
670
+ if (!value) return [];
671
+ if (typeof value === 'string') {
672
+ const htmlTemplateElement = doc.createElement('template');
673
+ htmlTemplateElement.innerHTML = value;
674
+ return Array.from(htmlTemplateElement.content.childNodes);
675
+ }
676
+ if (Array.isArray(value)) return value;
677
+ return [value];
678
+ };
679
+ // fallback: 비동기 렌더 대기 중에 먼저 보여준다. 결과는 모든 렌더 메서드가 끝난 뒤 한 번에 들어가므로
680
+ // 그때 같이 걷어낸다 (메서드별로 걷으면 다음 메서드를 기다리는 동안 빈 화면이 된다).
681
+ const shownFallbacks: Node[] = [];
682
+ const removeFallbacks = () => shownFallbacks.splice(0).forEach(n => (n as ChildNode).remove());
638
683
  for (const meta of targetConnectedList) {
684
+ const useShadowRoot = meta.options.useShadow || conf?.useShadow;
685
+ if (meta.options.fallback) {
686
+ const fb = typeof meta.options.fallback === 'function'
687
+ ? (meta.options.fallback as any)(helperHostSet)
688
+ : meta.options.fallback;
689
+ const fallbackNodes = toNodes(fb);
690
+ const parent = useShadowRoot ? this.shadowRoot : this;
691
+ if (fallbackNodes.length && parent) {
692
+ try {
693
+ const processed = SwcUtils.projectProcessHtml(this._swcId, fallbackNodes, doc);
694
+ parent.append(...processed);
695
+ shownFallbacks.push(...processed);
696
+ } catch (e) {
697
+ console.error('[ElementDefine] fallback setting error:', e);
698
+ }
699
+ }
700
+ }
639
701
  let res = await (this as any)._invokeLifecycleMethod(meta.propertyKey, helperHostSet);
702
+ if (!alive()) { removeFallbacks(); return; }
640
703
  if (typeof res === 'string') {
641
704
  const htmlTemplateElement = doc.createElement('template');
642
705
  htmlTemplateElement.innerHTML = res;
@@ -651,6 +714,7 @@ export const elementDefine =
651
714
  }
652
715
  }
653
716
  }
717
+ removeFallbacks();
654
718
  try {
655
719
  if (this.shadowRoot) {
656
720
  const applyShadowChildren = SwcUtils.projectProcessHtml(this._swcId, shadowChildren, doc);
@@ -677,22 +741,27 @@ export const elementDefine =
677
741
  globalDelegatedRoots.add(root);
678
742
  }
679
743
 
680
- // ── cycler 실행 (렌더 후): 모든 시클러 onConnected → observer Set 수집 → observer 생성 ──
681
- // helperHostSet 은 클로저 변수라 async 흐름에서 다른 인스턴스로 덮어써질 수 있다.
682
- // 반드시 현재 인스턴스(this)로 재계산한 값을 사용한다.
683
- await buildObservers(SwcUtils.getHelperAndHostSet(win, this as any));
744
+ // ── cycler 실행 (렌더 후): 나머지 시클러 onConnected → observer Set 수집 → 같은 옵저버에 덧붙임 ──
745
+ await buildObservers(helperHostSet, 'rest', obsState, alive);
746
+ if (!alive()) return;
684
747
 
685
748
  if (originalConnected) await originalConnected.apply(this);
749
+ if (!alive()) return;
686
750
 
687
751
  // after-connected 라이프사이클 메서드 (@onConnectedAfter)
688
- for (const m of findAllOnConnectedAfterMetadata(this)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
752
+ for (const m of findAllOnConnectedAfterMetadata(this)) {
753
+ await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
754
+ if (!alive()) return;
755
+ }
689
756
  (this as any)._executeSwcScript('swc-on-connected', helperHostSet);
690
757
  (this as any)._executeSwcScript('swc-on-after-connected', helperHostSet);
691
758
  (this as any).__swc_connected = true;
692
759
  } finally {
693
- for (const m of findAllLifecycleMetadata(this, ON_CONNECTED_COMPLETED_METADATA_KEY)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
760
+ // 멈춘 연결은 완료 훅을 부르지 않고, 앱 host 에는 중단만 알린다 (자식 연결 수만 맞추고 재생은 안 함)
761
+ const aborted = !alive();
762
+ if (!aborted) for (const m of findAllLifecycleMetadata(this, ON_CONNECTED_COMPLETED_METADATA_KEY)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
694
763
  if (appHost && typeof (appHost as any)._connectedDone === 'function') {
695
- await (appHost as any)._connectedDone(this);
764
+ await (appHost as any)._connectedDone(this, { aborted });
696
765
  }
697
766
  }
698
767
  };
@@ -700,16 +769,19 @@ export const elementDefine =
700
769
  /////////////////////////////////////////////////
701
770
  // disconnectedCallback
702
771
  ////////////////////////////////////////////////
703
- const originalDisconnected = proto.disconnectedCallback;
772
+ const originalDisconnected = userCallback(proto.disconnectedCallback);
704
773
  proto.disconnectedCallback = function () {
705
- const appHost = helperHostSet?.$appHost;
774
+ (this as any).__swc_connectGen = ((this as any).__swc_connectGen ?? 0) + 1; // 진행 중인 연결은 멈춘다
775
+ // 떨어진 뒤라 DOM 으로는 $appHost 를 다시 못 찾는다 → connect 때 저장한 이 인스턴스의 값
776
+ const helperHostSet = connectedHelperHostSet(this);
777
+ const appHost = helperHostSet.$appHost;
706
778
  if (appHost && typeof (appHost as any)._disconnected === 'function') {
707
779
  (appHost as any)._disconnected(this);
708
780
  }
709
781
 
710
782
  (this as any)._executeSwcScript('swc-on-before-disconnected', helperHostSet);
711
783
  for (const m of findAllLifecycleMetadata(this, ON_BEFORE_DISCONNECTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
712
- for (const c of cyclers) c.onDisconnected?.(helperHostSet!);
784
+ for (const c of cyclers) c.onDisconnected?.(helperHostSet);
713
785
 
714
786
  for (const o of (this as any).__swc_observers ?? []) {
715
787
  try { (o as any)?.disconnect?.(); } catch (e) { console.error('[SWC] observer disconnect error:', e); }
@@ -727,12 +799,12 @@ export const elementDefine =
727
799
  (this as any).__swc_connected = false;
728
800
  };
729
801
 
730
- const originalAdopted = proto.adoptedCallback;
802
+ const originalAdopted = userCallback(proto.adoptedCallback);
731
803
  proto.adoptedCallback = function () {
732
804
  const hostSet = SwcUtils.getHostSet(this as any);
733
805
  (this as any)._executeSwcScript('swc-on-before-adopted', hostSet);
734
806
  for (const m of findAllLifecycleMetadata(this, ON_BEFORE_ADOPTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
735
- for (const c of cyclers) c.onAdopted?.(helperHostSet!);
807
+ for (const c of cyclers) c.onAdopted?.(connectedHelperHostSet(this));
736
808
 
737
809
  if (originalAdopted) originalAdopted.apply(this);
738
810
 
@@ -741,13 +813,13 @@ export const elementDefine =
741
813
  (this as any)._executeSwcScript('swc-on-after-adopted', hostSet);
742
814
  };
743
815
 
744
- const originalAttributeChanged = proto.attributeChangedCallback;
816
+ const originalAttributeChanged = userCallback(proto.attributeChangedCallback);
745
817
  proto.attributeChangedCallback = function (name: string, old: string | null, newVal: string | null) {
746
818
  if (originalAttributeChanged) originalAttributeChanged.apply(this, [name, old, newVal]);
747
819
 
748
820
  // attributeChangedCallback 은 connected 이전에도 호출될 수 있으므로
749
821
  // 클로저의 helperHostSet(null 가능) 대신 이 시점에 새로 계산한다.
750
- const helperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
822
+ const helperAndHostSet = SwcUtils.getHelperAndHostSet(this as any);
751
823
 
752
824
  // Process expression directive before passing to handlers
753
825
  let processedVal: any = newVal;
@@ -755,8 +827,8 @@ export const elementDefine =
755
827
  const ae = new ActionExpression(newVal);
756
828
  const expr = ae.getFirstExpression('callReturn');
757
829
  if (expr) {
758
- const win = (this as any)._resolveWindow?.() || ((typeof window !== 'undefined' ? window : undefined) as any);
759
- const exprHelperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
830
+ const win = SwcUtils.resolveWindow(this);
831
+ const exprHelperAndHostSet = SwcUtils.getHelperAndHostSet(this as any);
760
832
  const script = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
761
833
  try {
762
834
  const result = FunctionUtils.executeReturn({
@@ -788,6 +860,12 @@ export const elementDefine =
788
860
  configurable: true
789
861
  });
790
862
 
863
+ // 이 클래스를 다시 상속해 elementDefine 하면 자식이 이 래퍼 대신 원래 사용자 함수를 부르도록 기억해 둔다
864
+ proto.connectedCallback[SWC_ORIGINAL_CALLBACK] = originalConnected;
865
+ proto.disconnectedCallback[SWC_ORIGINAL_CALLBACK] = originalDisconnected;
866
+ proto.adoptedCallback[SWC_ORIGINAL_CALLBACK] = originalAdopted;
867
+ proto.attributeChangedCallback[SWC_ORIGINAL_CALLBACK] = originalAttributeChanged;
868
+
791
869
  ReflectUtils.defineMetadata(ELEMENT_CONFIG_KEY, metadata, constructor);
792
870
  const registry = metadata.customElementRegistry || (win as any)?.customElements;
793
871
  if (registry && !registry.get(metadata.name)) {