@dooboostore/simple-web-component 1.0.50 → 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 (241) hide show
  1. package/README.md +735 -354
  2. package/dist/cjs/SwcAppEngine.js.map +2 -2
  3. package/dist/cjs/decorators/addEventListener.js +64 -146
  4. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  5. package/dist/cjs/decorators/applyAttribute.js +36 -48
  6. package/dist/cjs/decorators/applyAttribute.js.map +2 -2
  7. package/dist/cjs/decorators/applyClass.js +9 -12
  8. package/dist/cjs/decorators/applyClass.js.map +2 -2
  9. package/dist/cjs/decorators/applyNode.js +14 -22
  10. package/dist/cjs/decorators/applyNode.js.map +2 -2
  11. package/dist/cjs/decorators/applyProperty.js +12 -42
  12. package/dist/cjs/decorators/applyProperty.js.map +2 -2
  13. package/dist/cjs/decorators/applySlot.js +3 -3
  14. package/dist/cjs/decorators/applySlot.js.map +2 -2
  15. package/dist/cjs/decorators/applyStyle.js +9 -12
  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 +40 -12
  20. package/dist/cjs/decorators/changedAttribute.js.map +2 -2
  21. package/dist/cjs/decorators/elementDefine.js +224 -195
  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 +36 -8
  26. package/dist/cjs/decorators/eventMedia.js.map +2 -2
  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 +5 -2
  30. package/dist/cjs/decorators/index.js.map +2 -2
  31. package/dist/cjs/decorators/intersectionObserver.js +33 -13
  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 +55 -39
  36. package/dist/cjs/decorators/mutationObserver.js.map +3 -3
  37. package/dist/cjs/decorators/parameter.js +92 -9
  38. package/dist/cjs/decorators/parameter.js.map +2 -2
  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 +7 -16
  44. package/dist/cjs/decorators/requestAnimationFrame.js.map +2 -2
  45. package/dist/cjs/decorators/resizeObserver.js +41 -17
  46. package/dist/cjs/decorators/resizeObserver.js.map +3 -3
  47. package/dist/cjs/decorators/setInterval.js +36 -25
  48. package/dist/cjs/decorators/setInterval.js.map +2 -2
  49. package/dist/cjs/decorators/setTimeout.js +36 -25
  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 -83
  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/utils/Utils.js +36 -15
  66. package/dist/cjs/utils/Utils.js.map +2 -2
  67. package/dist/esm/SwcAppEngine.js.map +2 -2
  68. package/dist/esm/decorators/addEventListener.js +64 -146
  69. package/dist/esm/decorators/addEventListener.js.map +2 -2
  70. package/dist/esm/decorators/applyAttribute.js +36 -48
  71. package/dist/esm/decorators/applyAttribute.js.map +2 -2
  72. package/dist/esm/decorators/applyClass.js +9 -12
  73. package/dist/esm/decorators/applyClass.js.map +2 -2
  74. package/dist/esm/decorators/applyNode.js +15 -23
  75. package/dist/esm/decorators/applyNode.js.map +2 -2
  76. package/dist/esm/decorators/applyProperty.js +12 -42
  77. package/dist/esm/decorators/applyProperty.js.map +2 -2
  78. package/dist/esm/decorators/applySlot.js +3 -3
  79. package/dist/esm/decorators/applySlot.js.map +2 -2
  80. package/dist/esm/decorators/applyStyle.js +9 -12
  81. package/dist/esm/decorators/applyStyle.js.map +2 -2
  82. package/dist/esm/decorators/around.js +105 -0
  83. package/dist/esm/decorators/around.js.map +7 -0
  84. package/dist/esm/decorators/changedAttribute.js +40 -12
  85. package/dist/esm/decorators/changedAttribute.js.map +2 -2
  86. package/dist/esm/decorators/elementDefine.js +222 -193
  87. package/dist/esm/decorators/elementDefine.js.map +3 -3
  88. package/dist/esm/decorators/emitCustomEvent.js +15 -13
  89. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  90. package/dist/esm/decorators/eventMedia.js +36 -8
  91. package/dist/esm/decorators/eventMedia.js.map +2 -2
  92. package/dist/esm/decorators/fetch.js +259 -0
  93. package/dist/esm/decorators/fetch.js.map +7 -0
  94. package/dist/esm/decorators/index.js +5 -2
  95. package/dist/esm/decorators/index.js.map +2 -2
  96. package/dist/esm/decorators/intersectionObserver.js +33 -13
  97. package/dist/esm/decorators/intersectionObserver.js.map +2 -2
  98. package/dist/esm/decorators/lifecycles.js +11 -11
  99. package/dist/esm/decorators/lifecycles.js.map +2 -2
  100. package/dist/esm/decorators/mutationObserver.js +55 -39
  101. package/dist/esm/decorators/mutationObserver.js.map +3 -3
  102. package/dist/esm/decorators/parameter.js +92 -9
  103. package/dist/esm/decorators/parameter.js.map +2 -2
  104. package/dist/esm/decorators/publishSwcAppMessage.js +2 -2
  105. package/dist/esm/decorators/publishSwcAppMessage.js.map +2 -2
  106. package/dist/esm/decorators/query.js +11 -5
  107. package/dist/esm/decorators/query.js.map +2 -2
  108. package/dist/esm/decorators/requestAnimationFrame.js +8 -17
  109. package/dist/esm/decorators/requestAnimationFrame.js.map +2 -2
  110. package/dist/esm/decorators/resizeObserver.js +41 -17
  111. package/dist/esm/decorators/resizeObserver.js.map +2 -2
  112. package/dist/esm/decorators/setInterval.js +37 -26
  113. package/dist/esm/decorators/setInterval.js.map +2 -2
  114. package/dist/esm/decorators/setTimeout.js +37 -26
  115. package/dist/esm/decorators/setTimeout.js.map +2 -2
  116. package/dist/esm/decorators/state.js +13 -10
  117. package/dist/esm/decorators/state.js.map +2 -2
  118. package/dist/esm/decorators/subscribeSwcAppMessage.js +53 -0
  119. package/dist/esm/decorators/subscribeSwcAppMessage.js.map +7 -0
  120. package/dist/esm/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +20 -16
  121. package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +7 -0
  122. package/dist/esm/decorators/swcAppRoute.js +130 -0
  123. package/dist/esm/decorators/swcAppRoute.js.map +7 -0
  124. package/dist/esm/elements/SwcApp.js +70 -0
  125. package/dist/esm/elements/SwcApp.js.map +2 -2
  126. package/dist/esm/elements/SwcAppMixin.js +258 -85
  127. package/dist/esm/elements/SwcAppMixin.js.map +3 -3
  128. package/dist/esm/types/index.js +5 -1
  129. package/dist/esm/types/index.js.map +2 -2
  130. package/dist/esm/utils/Utils.js +36 -15
  131. package/dist/esm/utils/Utils.js.map +2 -2
  132. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +1886 -999
  133. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  134. package/dist/types/SwcAppEngine.d.ts +1 -0
  135. package/dist/types/SwcAppEngine.d.ts.map +1 -1
  136. package/dist/types/decorators/addEventListener.d.ts +923 -960
  137. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  138. package/dist/types/decorators/applyAttribute.d.ts +26 -38
  139. package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
  140. package/dist/types/decorators/applyClass.d.ts +1 -3
  141. package/dist/types/decorators/applyClass.d.ts.map +1 -1
  142. package/dist/types/decorators/applyNode.d.ts +15 -17
  143. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  144. package/dist/types/decorators/applyProperty.d.ts +25 -45
  145. package/dist/types/decorators/applyProperty.d.ts.map +1 -1
  146. package/dist/types/decorators/applyStyle.d.ts +1 -3
  147. package/dist/types/decorators/applyStyle.d.ts.map +1 -1
  148. package/dist/types/decorators/around.d.ts +87 -0
  149. package/dist/types/decorators/around.d.ts.map +1 -0
  150. package/dist/types/decorators/changedAttribute.d.ts +21 -1
  151. package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
  152. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  153. package/dist/types/decorators/emitCustomEvent.d.ts +0 -1
  154. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  155. package/dist/types/decorators/eventMedia.d.ts +24 -1
  156. package/dist/types/decorators/eventMedia.d.ts.map +1 -1
  157. package/dist/types/decorators/fetch.d.ts +139 -0
  158. package/dist/types/decorators/fetch.d.ts.map +1 -0
  159. package/dist/types/decorators/index.d.ts +5 -2
  160. package/dist/types/decorators/index.d.ts.map +1 -1
  161. package/dist/types/decorators/intersectionObserver.d.ts +24 -11
  162. package/dist/types/decorators/intersectionObserver.d.ts.map +1 -1
  163. package/dist/types/decorators/lifecycles.d.ts +8 -7
  164. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  165. package/dist/types/decorators/mutationObserver.d.ts +26 -13
  166. package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
  167. package/dist/types/decorators/parameter.d.ts +84 -6
  168. package/dist/types/decorators/parameter.d.ts.map +1 -1
  169. package/dist/types/decorators/publishSwcAppMessage.d.ts +0 -1
  170. package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -1
  171. package/dist/types/decorators/query.d.ts +7 -7
  172. package/dist/types/decorators/query.d.ts.map +1 -1
  173. package/dist/types/decorators/requestAnimationFrame.d.ts +1 -1
  174. package/dist/types/decorators/requestAnimationFrame.d.ts.map +1 -1
  175. package/dist/types/decorators/resizeObserver.d.ts +26 -13
  176. package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
  177. package/dist/types/decorators/setInterval.d.ts +10 -0
  178. package/dist/types/decorators/setInterval.d.ts.map +1 -1
  179. package/dist/types/decorators/setTimeout.d.ts +10 -0
  180. package/dist/types/decorators/setTimeout.d.ts.map +1 -1
  181. package/dist/types/decorators/state.d.ts.map +1 -1
  182. package/dist/types/decorators/subscribeSwcAppMessage.d.ts +44 -0
  183. package/dist/types/decorators/subscribeSwcAppMessage.d.ts.map +1 -0
  184. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +60 -0
  185. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +1 -0
  186. package/dist/types/decorators/swcAppRoute.d.ts +94 -0
  187. package/dist/types/decorators/swcAppRoute.d.ts.map +1 -0
  188. package/dist/types/elements/SwcApp.d.ts.map +1 -1
  189. package/dist/types/elements/SwcAppMixin.d.ts +423 -388
  190. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  191. package/dist/types/types/index.d.ts +43 -4
  192. package/dist/types/types/index.d.ts.map +1 -1
  193. package/dist/types/utils/Utils.d.ts +15 -5
  194. package/dist/types/utils/Utils.d.ts.map +1 -1
  195. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +1886 -999
  196. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  197. package/package.json +8 -6
  198. package/src/SwcAppEngine.ts +1 -0
  199. package/src/decorators/addEventListener.ts +102 -96
  200. package/src/decorators/applyAttribute.ts +76 -138
  201. package/src/decorators/applyClass.ts +8 -14
  202. package/src/decorators/applyNode.ts +33 -41
  203. package/src/decorators/applyProperty.ts +51 -99
  204. package/src/decorators/applySlot.ts +3 -3
  205. package/src/decorators/applyStyle.ts +8 -14
  206. package/src/decorators/around.ts +196 -0
  207. package/src/decorators/changedAttribute.ts +39 -15
  208. package/src/decorators/elementDefine.ts +270 -219
  209. package/src/decorators/emitCustomEvent.ts +17 -13
  210. package/src/decorators/eventMedia.ts +50 -9
  211. package/src/decorators/fetch.ts +454 -0
  212. package/src/decorators/index.ts +5 -2
  213. package/src/decorators/intersectionObserver.ts +38 -24
  214. package/src/decorators/lifecycles.ts +24 -22
  215. package/src/decorators/mutationObserver.ts +71 -52
  216. package/src/decorators/parameter.ts +145 -14
  217. package/src/decorators/publishSwcAppMessage.ts +3 -4
  218. package/src/decorators/query.ts +22 -14
  219. package/src/decorators/requestAnimationFrame.ts +10 -19
  220. package/src/decorators/resizeObserver.ts +50 -29
  221. package/src/decorators/setInterval.ts +37 -26
  222. package/src/decorators/setTimeout.ts +37 -26
  223. package/src/decorators/state.ts +19 -13
  224. package/src/decorators/subscribeSwcAppMessage.ts +106 -0
  225. package/src/decorators/subscribeSwcAppRouteChange.ts +131 -0
  226. package/src/decorators/swcAppRoute.ts +231 -0
  227. package/src/elements/SwcApp.ts +40 -10
  228. package/src/elements/SwcAppMixin.ts +330 -101
  229. package/src/types/index.ts +38 -5
  230. package/src/utils/Utils.ts +50 -18
  231. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
  232. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
  233. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +0 -54
  234. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
  235. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
  236. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +0 -23
  237. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +0 -1
  238. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +0 -31
  239. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +0 -1
  240. package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +0 -91
  241. package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +0 -107
@@ -1,6 +1,7 @@
1
1
  import { ReflectUtils } from '@dooboostore/core';
2
- import { ensureInit, getElementConfig } from './elementDefine';
2
+ import { ensureInit } from './elementDefine';
3
3
  import { SwcUtils } from '../utils/Utils';
4
+ import { buildSwcParameterArgs } from './parameter';
4
5
  import { ElementDefineLifeCycler, HelperHostSet } from '../types';
5
6
 
6
7
  export const SET_INTERVAL_METADATA_KEY = Symbol.for('simple-web-component:set-interval');
@@ -18,6 +19,12 @@ export interface SetIntervalOptions {
18
19
  parameter?: (set: HelperHostSet) => any[];
19
20
  /** 타이머 등록 직후 1회 호출 (디버깅/로깅용). id는 실제 setInterval 반환값 - cleanup은 자동으로 처리되므로 직접 clear할 필요 없음. */
20
21
  created?: (set: HelperHostSet, id: number) => void;
22
+ /** [type:'onConnected' 전용] 매 tick 실행 여부 게이트. Promise<boolean>도 가능. false면 이 tick 스킵. */
23
+ filter?: (set: HelperHostSet) => boolean | Promise<boolean>;
24
+ /** [type:'onConnected' 전용] tick 핸들러 직전 훅. await되고, 리턴값은 @setIntervalBeforeReturn 으로 주입된다. */
25
+ before?: (set: HelperHostSet) => any | Promise<any>;
26
+ /** [type:'onConnected' 전용] tick 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 인자/결과/에러. */
27
+ finally?: (set: HelperHostSet, ctx: { args: any[]; result?: any; error?: any }) => any | Promise<any>;
21
28
  /**
22
29
  * type:'onConnected' - 매 tick마다 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
23
30
  * type:'returnValue' - 리턴값이 함수면 그 자체를, 객체면 이 키의 값이 함수일 때 그 값을 "실제로 반복 실행될 함수"로 사용한다.
@@ -33,15 +40,7 @@ export interface SetIntervalMetadata {
33
40
  options: SetIntervalOptions;
34
41
  }
35
42
 
36
- const activeMap = new WeakMap<any, number[]>();
37
- const getActiveEntries = (inst: any): number[] => {
38
- let entries = activeMap.get(inst);
39
- if (!entries) {
40
- entries = [];
41
- activeMap.set(inst, entries);
42
- }
43
- return entries;
44
- };
43
+ const getActiveEntries = (inst: any): number[] => (inst.__swc_intervalIds ??= []);
45
44
 
46
45
  /**
47
46
  * type:'onConnected' - connect 시 자동으로 setInterval을 걸고, disconnect 시 자동으로 clearInterval 한다.
@@ -76,8 +75,7 @@ export function setInterval(interval: number, options: SetIntervalOptions = {}):
76
75
 
77
76
  if (tickFn) {
78
77
  ensureInit(this);
79
- const conf = getElementConfig(this);
80
- const win = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
78
+ const win = SwcUtils.resolveWindow(this);
81
79
  const id = win.setInterval(() => {
82
80
  try {
83
81
  tickFn(id);
@@ -85,7 +83,7 @@ export function setInterval(interval: number, options: SetIntervalOptions = {}):
85
83
  console.error('[SWC] setInterval tick error:', e);
86
84
  }
87
85
  }, interval);
88
- normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(win, this), id);
86
+ normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(this), id);
89
87
  getActiveEntries(this).push(id);
90
88
  }
91
89
  return res;
@@ -95,27 +93,40 @@ export function setInterval(interval: number, options: SetIntervalOptions = {}):
95
93
  }
96
94
 
97
95
  export const findAllSetIntervalMetadata = (target: any): SetIntervalMetadata[] => {
98
- const constructor = target instanceof Function ? target : target.constructor;
96
+ const constructor = typeof target === 'function' ? target : target.constructor;
99
97
  return (ReflectUtils.findAllMetadata<SetIntervalMetadata[]>(SET_INTERVAL_METADATA_KEY, constructor) || []).flat();
100
98
  };
101
99
 
102
100
  export class SetIntervalLifeCycler implements ElementDefineLifeCycler {
103
101
  onConnected(helperHostSet: HelperHostSet): void {
104
102
  const inst = helperHostSet.$this;
105
- activeMap.delete(inst); // 재연결 대비 리셋
103
+ inst.__swc_intervalIds = []; // 재연결 대비 리셋
106
104
  for (const meta of findAllSetIntervalMetadata(inst)) {
107
105
  if (meta.options.type !== 'onConnected') continue; // returnValue 타입은 수동 호출을 기다림
108
106
  const id = helperHostSet.$w.setInterval(() => {
109
- const args = meta.options.parameter?.(helperHostSet) ?? [];
110
- try {
111
- const res = inst[meta.propertyKey](...args);
112
- const vk = meta.options.valueKey ?? SET_INTERVAL_METADATA_KEY;
113
- if (res && typeof res === 'object' && typeof res[vk] === 'function') {
114
- res[vk](id);
107
+ void (async () => {
108
+ const helper = SwcUtils.getHelperAndHostSet(inst);
109
+ if (meta.options.filter && !(await meta.options.filter(helper))) return;
110
+ const paramArgs = meta.options.parameter?.(helperHostSet) ?? [];
111
+ const hostSet = SwcUtils.getHostSet(inst);
112
+ const helperSet = SwcUtils.getHelperSet(helperHostSet.$w);
113
+ const buildArgs = (beforeReturn: any) => buildSwcParameterArgs(inst, meta.propertyKey, {
114
+ hostSet, helperHostSet: helper, helperSet, setIntervalBeforeReturn: beforeReturn
115
+ }, [...paramArgs, beforeReturn]);
116
+ let args = buildArgs(undefined);
117
+ if (meta.options.before) { const br = await meta.options.before(helper); args = buildArgs(br); }
118
+ let result: any, error: any;
119
+ try {
120
+ result = await inst[meta.propertyKey](...args);
121
+ const vk = meta.options.valueKey ?? SET_INTERVAL_METADATA_KEY;
122
+ if (result && typeof result === 'object' && typeof result[vk] === 'function') result[vk](id);
123
+ } catch (e) {
124
+ error = e;
125
+ } finally {
126
+ if (meta.options.finally) await meta.options.finally(helper, { args, result, error });
115
127
  }
116
- } catch (e) {
117
- console.error('[SWC] setInterval tick error:', e);
118
- }
128
+ if (error) console.error('[SWC] setInterval tick error:', error);
129
+ })().catch(e => console.error('[SWC] setInterval tick error:', e));
119
130
  }, meta.interval);
120
131
  meta.options.created?.(helperHostSet, id);
121
132
  getActiveEntries(inst).push(id);
@@ -124,13 +135,13 @@ export class SetIntervalLifeCycler implements ElementDefineLifeCycler {
124
135
 
125
136
  onDisconnected(helperHostSet: HelperHostSet): void {
126
137
  const inst = helperHostSet.$this;
127
- for (const id of activeMap.get(inst) ?? []) {
138
+ for (const id of inst.__swc_intervalIds ?? []) {
128
139
  try {
129
140
  helperHostSet.$w.clearInterval(id);
130
141
  } catch (e) {
131
142
  console.error('[SWC] clearInterval error:', e);
132
143
  }
133
144
  }
134
- activeMap.delete(inst);
145
+ inst.__swc_intervalIds = [];
135
146
  }
136
147
  }
@@ -1,6 +1,7 @@
1
1
  import { ReflectUtils } from '@dooboostore/core';
2
- import { ensureInit, getElementConfig } from './elementDefine';
2
+ import { ensureInit } from './elementDefine';
3
3
  import { SwcUtils } from '../utils/Utils';
4
+ import { buildSwcParameterArgs } from './parameter';
4
5
  import { ElementDefineLifeCycler, HelperHostSet } from '../types';
5
6
 
6
7
  export const SET_TIMEOUT_METADATA_KEY = Symbol.for('simple-web-component:set-timeout');
@@ -18,6 +19,12 @@ export interface SetTimeoutOptions {
18
19
  parameter?: (set: HelperHostSet) => any[];
19
20
  /** 타이머 등록 직후 1회 호출 (디버깅/로깅용). id는 실제 setTimeout 반환값 - cleanup은 자동으로 처리되므로 직접 clear할 필요 없음. */
20
21
  created?: (set: HelperHostSet, id: number) => void;
22
+ /** [type:'onConnected' 전용] 발화 여부 게이트. Promise<boolean>도 가능. false면 스킵. */
23
+ filter?: (set: HelperHostSet) => boolean | Promise<boolean>;
24
+ /** [type:'onConnected' 전용] 핸들러 직전 훅. await되고, 리턴값은 @setTimeoutBeforeReturn 으로 주입된다. */
25
+ before?: (set: HelperHostSet) => any | Promise<any>;
26
+ /** [type:'onConnected' 전용] 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 인자/결과/에러. */
27
+ finally?: (set: HelperHostSet, ctx: { args: any[]; result?: any; error?: any }) => any | Promise<any>;
21
28
  /**
22
29
  * type:'onConnected' - fire 시 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
23
30
  * type:'returnValue' - 리턴값이 함수면 그 자체를, 객체면 이 키의 값이 함수일 때 그 값을 "실제로 실행될 함수"로 사용한다.
@@ -33,15 +40,7 @@ export interface SetTimeoutMetadata {
33
40
  options: SetTimeoutOptions;
34
41
  }
35
42
 
36
- const activeMap = new WeakMap<any, number[]>();
37
- const getActiveEntries = (inst: any): number[] => {
38
- let entries = activeMap.get(inst);
39
- if (!entries) {
40
- entries = [];
41
- activeMap.set(inst, entries);
42
- }
43
- return entries;
44
- };
43
+ const getActiveEntries = (inst: any): number[] => (inst.__swc_timeoutIds ??= []);
45
44
 
46
45
  /**
47
46
  * type:'onConnected' - connect 시 자동으로 setTimeout을 걸고, disconnect 시 (아직 실행 전이라면) 자동으로 clearTimeout 한다.
@@ -76,8 +75,7 @@ export function setTimeout(delay: number, options: SetTimeoutOptions = {}): Meth
76
75
 
77
76
  if (tickFn) {
78
77
  ensureInit(this);
79
- const conf = getElementConfig(this);
80
- const win = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
78
+ const win = SwcUtils.resolveWindow(this);
81
79
  const id = win.setTimeout(() => {
82
80
  const entries = getActiveEntries(this);
83
81
  const idx = entries.indexOf(id);
@@ -88,7 +86,7 @@ export function setTimeout(delay: number, options: SetTimeoutOptions = {}): Meth
88
86
  console.error('[SWC] setTimeout fire error:', e);
89
87
  }
90
88
  }, delay);
91
- normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(win, this), id);
89
+ normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(this), id);
92
90
  getActiveEntries(this).push(id);
93
91
  }
94
92
  return res;
@@ -98,27 +96,40 @@ export function setTimeout(delay: number, options: SetTimeoutOptions = {}): Meth
98
96
  }
99
97
 
100
98
  export const findAllSetTimeoutMetadata = (target: any): SetTimeoutMetadata[] => {
101
- const constructor = target instanceof Function ? target : target.constructor;
99
+ const constructor = typeof target === 'function' ? target : target.constructor;
102
100
  return (ReflectUtils.findAllMetadata<SetTimeoutMetadata[]>(SET_TIMEOUT_METADATA_KEY, constructor) || []).flat();
103
101
  };
104
102
 
105
103
  export class SetTimeoutLifeCycler implements ElementDefineLifeCycler {
106
104
  onConnected(helperHostSet: HelperHostSet): void {
107
105
  const inst = helperHostSet.$this;
108
- activeMap.delete(inst); // 재연결 대비 리셋
106
+ inst.__swc_timeoutIds = []; // 재연결 대비 리셋
109
107
  for (const meta of findAllSetTimeoutMetadata(inst)) {
110
108
  if (meta.options.type !== 'onConnected') continue; // returnValue 타입은 수동 호출을 기다림
111
109
  const id = helperHostSet.$w.setTimeout(() => {
112
- const args = meta.options.parameter?.(helperHostSet) ?? [];
113
- try {
114
- const res = inst[meta.propertyKey](...args);
115
- const vk = meta.options.valueKey ?? SET_TIMEOUT_METADATA_KEY;
116
- if (res && typeof res === 'object' && typeof res[vk] === 'function') {
117
- res[vk](id);
110
+ void (async () => {
111
+ const helper = SwcUtils.getHelperAndHostSet(inst);
112
+ if (meta.options.filter && !(await meta.options.filter(helper))) return;
113
+ const paramArgs = meta.options.parameter?.(helperHostSet) ?? [];
114
+ const hostSet = SwcUtils.getHostSet(inst);
115
+ const helperSet = SwcUtils.getHelperSet(helperHostSet.$w);
116
+ const buildArgs = (beforeReturn: any) => buildSwcParameterArgs(inst, meta.propertyKey, {
117
+ hostSet, helperHostSet: helper, helperSet, setTimeoutBeforeReturn: beforeReturn
118
+ }, [...paramArgs, beforeReturn]);
119
+ let args = buildArgs(undefined);
120
+ if (meta.options.before) { const br = await meta.options.before(helper); args = buildArgs(br); }
121
+ let result: any, error: any;
122
+ try {
123
+ result = await inst[meta.propertyKey](...args);
124
+ const vk = meta.options.valueKey ?? SET_TIMEOUT_METADATA_KEY;
125
+ if (result && typeof result === 'object' && typeof result[vk] === 'function') result[vk](id);
126
+ } catch (e) {
127
+ error = e;
128
+ } finally {
129
+ if (meta.options.finally) await meta.options.finally(helper, { args, result, error });
118
130
  }
119
- } catch (e) {
120
- console.error('[SWC] setTimeout fire error:', e);
121
- }
131
+ if (error) console.error('[SWC] setTimeout fire error:', error);
132
+ })().catch(e => console.error('[SWC] setTimeout fire error:', e));
122
133
  }, meta.delay);
123
134
  meta.options.created?.(helperHostSet, id);
124
135
  getActiveEntries(inst).push(id);
@@ -127,13 +138,13 @@ export class SetTimeoutLifeCycler implements ElementDefineLifeCycler {
127
138
 
128
139
  onDisconnected(helperHostSet: HelperHostSet): void {
129
140
  const inst = helperHostSet.$this;
130
- for (const id of activeMap.get(inst) ?? []) {
141
+ for (const id of inst.__swc_timeoutIds ?? []) {
131
142
  try {
132
143
  helperHostSet.$w.clearTimeout(id);
133
144
  } catch (e) {
134
145
  console.error('[SWC] clearTimeout error:', e);
135
146
  }
136
147
  }
137
- activeMap.delete(inst);
148
+ inst.__swc_timeoutIds = [];
138
149
  }
139
150
  }
@@ -1,7 +1,6 @@
1
1
  import {ReflectUtils} from '@dooboostore/core';
2
2
  import {ElementApply} from "@dooboostore/core-web";
3
3
  import {SwcUtils} from "../utils/Utils";
4
- import {getElementConfig} from "./elementDefine";
5
4
 
6
5
  export interface StateMetadata {
7
6
  propertyKey: string | symbol;
@@ -15,23 +14,22 @@ export const STATE_METADATA_KEY = Symbol.for('simple-web-component:state');
15
14
  export function state(target: Object, propertyKey: string | symbol): void;
16
15
  export function state(name: string): PropertyDecorator;
17
16
  /**
18
- * Supports both forms:
17
+ * Forms (빈 괄호 @state() 는 타입 에러):
19
18
  * - @state
20
- * - @state()
21
19
  * - @state('selector')
22
20
  */
23
21
  export function state(nameOrTarget?: string | Object, propertyKey?: string | symbol): any {
24
22
 
25
23
  const stateDefine = (targetObj: any, stateMetadata: StateMetadata) => {
26
24
 
27
- let storage = targetObj[stateMetadata.propertyKey];
28
- // console.log('ssssssssssssssss', storage, stateMetadata.propertyKey)
25
+ // around.ts와 동일: 클로저 변수에 두면 모든 인스턴스가 공유해버리므로
26
+ // 인스턴스 전용 Symbol 슬롯에 실제 값을 둔다.
27
+ const storageKey = Symbol(`state:${String(stateMetadata.propertyKey)}`);
29
28
  Object.defineProperty(targetObj, stateMetadata.propertyKey, {
30
29
  set(this: any, nv: string) {
31
- storage = nv;
32
- const config = getElementConfig(this)
30
+ this[storageKey] = nv;
33
31
  const ea = new ElementApply(this, {id: this._swcId});
34
- const helperHostSet = SwcUtils.getHelperAndHostSet(config.window, this);
32
+ const helperHostSet = SwcUtils.getHelperAndHostSet(this);
35
33
  const stateContext: any = {...helperHostSet};
36
34
  findAllStateMetadata(this).forEach(it => {
37
35
  stateContext[it.name] = this[it.propertyKey]
@@ -39,7 +37,15 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
39
37
  ea.apply({context:stateContext, targetVariableName: stateMetadata.name, bind: this});
40
38
  },
41
39
  get(this: any) {
42
- return storage;
40
+ // 안전망: 필드 이니셜라이저의 own property가 남아있다면(ensureInit이 안 돈
41
+ // 일반 클래스 등) 슬롯으로 회수한다. setter 경유는 안 함 — setter의 DOM
42
+ // 동기화는 초기화 전에 돌면 안 되므로 직접 슬롯에 둔다.
43
+ const own = Object.getOwnPropertyDescriptor(this, stateMetadata.propertyKey);
44
+ if (own && 'value' in own) {
45
+ delete this[stateMetadata.propertyKey];
46
+ this[storageKey] = own.value;
47
+ }
48
+ return this[storageKey];
43
49
  },
44
50
  enumerable: true,
45
51
  configurable: true
@@ -51,7 +57,7 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
51
57
  const targetObj = nameOrTarget as Object;
52
58
  const name = String(propertyKey);
53
59
  const constructor = (targetObj as any).constructor;
54
- let states = ReflectUtils.getMetadata<StateMetadata[]>(STATE_METADATA_KEY, constructor);
60
+ let states = ReflectUtils.getOwnMetadata(STATE_METADATA_KEY, constructor) as StateMetadata[] | undefined;
55
61
  if (!states) {
56
62
  states = [];
57
63
  ReflectUtils.defineMetadata(STATE_METADATA_KEY, states, constructor);
@@ -66,7 +72,7 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
66
72
  return (targetObj: Object, propertyKey: string | symbol): void => {
67
73
  const name = String(nameOrTarget);
68
74
  const constructor = targetObj.constructor;
69
- let states = ReflectUtils.getMetadata<StateMetadata[]>(STATE_METADATA_KEY, constructor);
75
+ let states = ReflectUtils.getOwnMetadata(STATE_METADATA_KEY, constructor) as StateMetadata[] | undefined;
70
76
  if (!states) {
71
77
  states = [];
72
78
  ReflectUtils.defineMetadata(STATE_METADATA_KEY, states, constructor);
@@ -78,6 +84,6 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
78
84
  }
79
85
 
80
86
  export const findAllStateMetadata = (target: any): StateMetadata[] => {
81
- const constructor = target instanceof Function ? target : target.constructor;
82
- return ReflectUtils.getMetadata(STATE_METADATA_KEY, constructor) ?? [];
87
+ const constructor = typeof target === 'function' ? target : target.constructor;
88
+ return ReflectUtils.findAllMetadata<any[]>(STATE_METADATA_KEY, constructor).flat();
83
89
  };
@@ -0,0 +1,106 @@
1
+ import { ReflectUtils } from '@dooboostore/core';
2
+ import { SwcAppMessage, SwcAppMessageSubject, SpecialSelector, SwcQueryOptions, HelperHostSet } from '../types';
3
+
4
+ export const SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY = Symbol.for('simple-web-component:subscribe-swc-app-message');
5
+
6
+ export interface SwcAppMessageOptions<Data = any, BeforeReturn = any, Result = any> {
7
+ /** 메시지 수신 시 핸들러 실행 여부 게이트. Promise<boolean>도 되어 async 판정 가능. false면 스킵. */
8
+ filter?: (message: SwcAppMessage<Data>, currentThis: any) => boolean | Promise<boolean>;
9
+ /** filter 통과 후 핸들러 직전 훅. await되고, 리턴값은 @appMessageBeforeReturn 으로 핸들러에 주입된다. */
10
+ before?: (message: SwcAppMessage<Data>, currentThis: any) => BeforeReturn | Promise<BeforeReturn>;
11
+ /** 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 핸들러 인자/결과/에러를 받는다. 에러는 삼키지 않고 전파. */
12
+ finally?: (message: SwcAppMessage<Data>, currentThis: any, ctx: { args: any[]; result?: Result; error?: any }) => any | Promise<any>;
13
+ /**
14
+ * 늦게 연결된 경우 과거 메시지 재생 방식. 이미 연결된 구독자에게는 항상 live로 쏨.
15
+ * - 미지정/'subject': live만. 지나간 건 못 받음 (기본값, 기존 동작)
16
+ * - 'behavior': 마지막 1개 재생
17
+ * - 'replay': 버퍼 전체 시간순 재생
18
+ */
19
+ subject?: SwcAppMessageSubject;
20
+ /**
21
+ * 재생 시점. 기본 'connected' (연결 즉시).
22
+ * - 'connectedDone': 엘리먼트 render 완료 후 (_connectedDone). @innerHtml 등
23
+ * DOM 타겟이 필요한 핸들러는 이걸로 (replay가 render보다 먼저 와서 증발하는 레이스 방지).
24
+ */
25
+ trigger?: 'connected' | 'connectedDone';
26
+ }
27
+
28
+ export interface SwcAppMessageSubscriberMetadata extends SwcAppMessageOptions {
29
+ propertyKey: string | symbol;
30
+ messageType?: string;
31
+ }
32
+
33
+ function createSubscribeSwcAppMessage(options?: SwcAppMessageOptions, messageType?: string): MethodDecorator {
34
+ return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
35
+ const constructor = target.constructor;
36
+ let list = ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY, constructor) as SwcAppMessageSubscriberMetadata[];
37
+ if (!list) {
38
+ list = [];
39
+ ReflectUtils.defineMetadata(SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY, list, constructor);
40
+ }
41
+ list.push({ propertyKey, messageType, filter: options?.filter, before: options?.before, finally: options?.finally, subject: options?.subject, trigger: options?.trigger });
42
+
43
+ return descriptor;
44
+ };
45
+ }
46
+
47
+ // 오버로드 시그니처 - 직접 사용 (괄호 없음)
48
+ export function subscribeSwcAppMessage(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
49
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음) - 메시지 타입만
50
+ export function subscribeSwcAppMessage(messageType: string): MethodDecorator;
51
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음) - 옵션만
52
+ export function subscribeSwcAppMessage<Data = any, BeforeReturn = any, Result = any>(options: SwcAppMessageOptions<NoInfer<Data>, NoInfer<BeforeReturn>, NoInfer<Result>>): MethodDecorator;
53
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음) - 메시지 타입 + 옵션
54
+ export function subscribeSwcAppMessage<Data = any, BeforeReturn = any, Result = any>(messageType: string, options: SwcAppMessageOptions<NoInfer<Data>, NoInfer<BeforeReturn>, NoInfer<Result>>): MethodDecorator;
55
+
56
+ export function subscribeSwcAppMessage(targetOrMessageTypeOrOptions?: any, optionsOrPropertyKey?: any, descriptor?: PropertyDescriptor): any {
57
+ // 직접 데코레이터로 사용된 경우 (괄호 없음): @subscribeSwcAppMessage
58
+ if (targetOrMessageTypeOrOptions && typeof targetOrMessageTypeOrOptions === 'object' &&
59
+ optionsOrPropertyKey && (typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol') &&
60
+ descriptor) {
61
+ const target = targetOrMessageTypeOrOptions;
62
+ const propertyKey = optionsOrPropertyKey;
63
+ return createSubscribeSwcAppMessage({})(target, propertyKey, descriptor);
64
+ }
65
+
66
+ // 함수로 호출된 경우 (괄호 있음)
67
+ let messageType: string | undefined;
68
+ let options: SwcAppMessageOptions = {};
69
+
70
+ if (typeof targetOrMessageTypeOrOptions === 'string') {
71
+ // 메시지 타입 전달: @subscribeSwcAppMessage('messageType') 또는 @subscribeSwcAppMessage('messageType', options)
72
+ messageType = targetOrMessageTypeOrOptions;
73
+ if (optionsOrPropertyKey && typeof optionsOrPropertyKey === 'object') {
74
+ options = optionsOrPropertyKey;
75
+ }
76
+ } else if (targetOrMessageTypeOrOptions && typeof targetOrMessageTypeOrOptions === 'object') {
77
+ // 옵션만 전달: @subscribeSwcAppMessage({ filter: ... })
78
+ options = targetOrMessageTypeOrOptions;
79
+ }
80
+
81
+ return createSubscribeSwcAppMessage(options, messageType);
82
+ }
83
+
84
+ export const receiveMessage = subscribeSwcAppMessage;
85
+ // Helper function to retrieve message subscribers metadata
86
+ export const getSubscribeSwcAppMessageMetadata = (target: any): SwcAppMessageSubscriberMetadata[] | undefined => {
87
+ const constructor = typeof target === 'function' ? target : target.constructor;
88
+ return ReflectUtils.findAllMetadata<any[]>(SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY, constructor).flat();
89
+ };
90
+
91
+ // ─────────────────────────────────────────────────────────────────────────────
92
+ // MessageSubscribeLifeCycler
93
+ // NOTE: 실제 구독 로직은 SwcAppMixin._connected 에서 처리된다.
94
+ // ─────────────────────────────────────────────────────────────────────────────
95
+ import { ElementDefineLifeCycler } from '../types';
96
+
97
+ export class MessageSubscribeLifeCycler implements ElementDefineLifeCycler {
98
+ onConnected(_helperHostSet: HelperHostSet): void {
99
+ // 실제 구독은 SwcAppMixin._connected 에서 처리
100
+ }
101
+
102
+ /** SwcAppMixin 이 구독 대상 메타를 꺼낼 때 사용 */
103
+ getMetadata(helperHostSet: HelperHostSet): SwcAppMessageSubscriberMetadata[] | undefined {
104
+ return getSubscribeSwcAppMessageMetadata(helperHostSet.$this);
105
+ }
106
+ }
@@ -0,0 +1,131 @@
1
+ import {ReflectUtils} from '@dooboostore/core';
2
+ import {Router} from '@dooboostore/core-web';
3
+ import {HelperHostSet} from '../types';
4
+
5
+ export const SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY = Symbol.for('simple-web-component:subscribe-swc-app-route-change');
6
+
7
+ export type RoutePathType = string | string[] | ((currentThis: any) => string | string[]);
8
+
9
+ export interface SwcAppRouteChangeSubscriberMetadata {
10
+ propertyKey: string | symbol;
11
+ options?: SwcAppRouteChangeOptions;
12
+ }
13
+
14
+ export interface SwcAppRouteChangeOptions<BeforeReturn = any, Result = any> {
15
+ path?: RoutePathType;
16
+ /** 라우트 변경 시 핸들러 실행 여부 게이트. Promise<boolean>도 되어 async 가드(인증 체크 등) 가능. false면 스킵. */
17
+ filter?: (router: Router, meta: { currentThis: any; helper: HelperHostSet }) => boolean | Promise<boolean>;
18
+ /** filter 통과 후 핸들러 직전 훅. await되고, 리턴값은 @routeChangeBeforeReturn 으로 핸들러에 주입된다. */
19
+ before?: (router: Router, meta: { currentThis: any; helper: HelperHostSet }) => BeforeReturn | Promise<BeforeReturn>;
20
+ /** 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 핸들러 인자/결과/에러를 받는다. 에러는 삼키지 않고 전파. */
21
+ finally?: (router: Router, meta: { currentThis: any; helper: HelperHostSet }, ctx: { args: any[]; result?: Result; error?: any }) => any | Promise<any>;
22
+ order?: number;
23
+ valueKey?: symbol | string;
24
+ /**
25
+ * 늦게 붙은 인스턴스에 마지막 라우트 재생 시점. 기본 'connected'.
26
+ * - 'connectedDone': render 완료 후 (_connectedDone). DOM 타겟 필요한 핸들러용.
27
+ * - live 라우트 변경(전체 방송) 때는 항상 실행. replay에만 적용.
28
+ */
29
+ trigger?: 'connected' | 'connectedDone';
30
+ /**
31
+ * 언제 실행할지. 기본 'match'(경로가 맞을 때마다 — 기존 동작).
32
+ * - 'enter': 안 맞다가 맞게 됐을 때 (처음 붙을 때의 재생 포함)
33
+ * - 'update': 계속 맞는데 경로나 query 가 바뀌었을 때
34
+ * - 'leave': 맞다가 안 맞게 됐을 때 (요소가 붙어 있을 때만 — 교체되는 페이지는 onDisconnected 가 담당)
35
+ * - 'beforeLeave': 이 경로를 떠나기 전. false(또는 Promise<false>)를 리턴하면 이동 취소 (router.go / 뒤로·앞으로 가기)
36
+ */
37
+ on?: 'match' | 'enter' | 'update' | 'leave' | 'beforeLeave';
38
+ }
39
+
40
+ function createSubscribeSwcAppRouteChange(options?: SwcAppRouteChangeOptions): MethodDecorator {
41
+ return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
42
+ const pathPattern = options?.path;
43
+ const filter = options?.filter;
44
+
45
+ const constructor = target.constructor;
46
+ let list = ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, constructor) as SwcAppRouteChangeSubscriberMetadata[];
47
+ if (!list) {
48
+ list = [];
49
+ ReflectUtils.defineMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, list, constructor);
50
+ }
51
+ list.push({ propertyKey, options });
52
+
53
+ return descriptor;
54
+ };
55
+ }
56
+
57
+ // 오버로드 시그니처 - 직접 사용 (괄호 없음)
58
+ export function subscribeSwcAppRouteChange(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
59
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음)
60
+ export function subscribeSwcAppRouteChange(pathPattern: RoutePathType): MethodDecorator;
61
+ export function subscribeSwcAppRouteChange<BeforeReturn = any, Result = any>(pathPattern: RoutePathType, config: Omit<SwcAppRouteChangeOptions<NoInfer<BeforeReturn>, NoInfer<Result>>, 'path'>): MethodDecorator;
62
+ export function subscribeSwcAppRouteChange<BeforeReturn = any, Result = any>(options: SwcAppRouteChangeOptions<NoInfer<BeforeReturn>, NoInfer<Result>>): MethodDecorator;
63
+
64
+ // 실제 구현 (이중 모드)
65
+ export function subscribeSwcAppRouteChange(targetOrOptions?: any, propertyKeyOrConfig?: any, descriptor?: PropertyDescriptor): any {
66
+ // 직접 데코레이터로 사용된 경우 (괄호 없음): @subscribeSwcAppRouteChange
67
+ // propertyKey가 string | symbol이고 descriptor가 있으면 직접 사용
68
+ if (propertyKeyOrConfig && (typeof propertyKeyOrConfig === 'string' || typeof propertyKeyOrConfig === 'symbol')) {
69
+ return createSubscribeSwcAppRouteChange({})(targetOrOptions, propertyKeyOrConfig, descriptor);
70
+ }
71
+
72
+ // 함수로 호출된 경우 (인자 있음): @subscribeSwcAppRouteChange('/path')
73
+ let options: SwcAppRouteChangeOptions = {};
74
+
75
+ if (Array.isArray(targetOrOptions)) {
76
+ // 배열: @subscribeSwcAppRouteChange(['/path1', '/path2'])
77
+ // 또는: @subscribeSwcAppRouteChange(['/path1', '/path2'], { order: 1 })
78
+ options.path = targetOrOptions;
79
+ if (propertyKeyOrConfig && typeof propertyKeyOrConfig === 'object') {
80
+ options = { ...options, ...propertyKeyOrConfig };
81
+ }
82
+ } else if (typeof targetOrOptions === 'string' || typeof targetOrOptions === 'function') {
83
+ // 문자열 또는 함수: @subscribeSwcAppRouteChange('/path')
84
+ // 또는: @subscribeSwcAppRouteChange('/path', { order: 1 })
85
+ options.path = targetOrOptions;
86
+ if (propertyKeyOrConfig && typeof propertyKeyOrConfig === 'object') {
87
+ options = { ...options, ...propertyKeyOrConfig };
88
+ }
89
+ } else if (targetOrOptions && typeof targetOrOptions === 'object') {
90
+ // 객체: @subscribeSwcAppRouteChange({ path: '/path', filter: ..., order: 1 })
91
+ options = targetOrOptions;
92
+ }
93
+
94
+ return createSubscribeSwcAppRouteChange(options);
95
+ }
96
+ export const changedRoute = subscribeSwcAppRouteChange;
97
+
98
+ // 늦게 붙은 인스턴스도 렌더 완료 후 마지막 라우트를 재생 — DOM 타겟 핸들러용.
99
+ // @subscribeSwcAppRouteChange(path, { trigger: 'connectedDone' }) 의 단축형.
100
+ export function subscribeSwcAppRouteChangeConnectedDone<BeforeReturn = any, Result = any>(pathPattern: RoutePathType, config?: Omit<SwcAppRouteChangeOptions<NoInfer<BeforeReturn>, NoInfer<Result>>, 'path' | 'trigger'>): MethodDecorator {
101
+ return subscribeSwcAppRouteChange(pathPattern, { ...config, trigger: 'connectedDone' });
102
+ }
103
+
104
+ // Helper function to retrieve route change subscribers metadata
105
+ export const getSubscribeSwcAppRouteChangeMetadata = (target: any): SwcAppRouteChangeSubscriberMetadata[] => {
106
+ const constructor = typeof target === 'function' ? target : target.constructor;
107
+ const results = ReflectUtils.findAllMetadata<any[]>(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, constructor).flat();
108
+ if (!results) return [];
109
+
110
+ // Sort by order (default 0 if not specified)
111
+ return [...results].sort((a, b) => (a.options?.order ?? 0) - (b.options?.order ?? 0));
112
+ };
113
+
114
+ // ─────────────────────────────────────────────────────────────────────────────
115
+ // RouteSubscribeLifeCycler
116
+ // NOTE: 실제 구독 로직은 SwcAppMixin._connected 에서 처리된다.
117
+ // 이 cycler 는 메타데이터 접근 진입점 역할만 하며,
118
+ // 향후 SwcAppMixin 에서 로직을 분리할 때 이 클래스로 이전한다.
119
+ // ─────────────────────────────────────────────────────────────────────────────
120
+ import { ElementDefineLifeCycler } from '../types';
121
+
122
+ export class RouteSubscribeLifeCycler implements ElementDefineLifeCycler {
123
+ onConnected(_helperHostSet: HelperHostSet): void {
124
+ // 실제 구독은 SwcAppMixin._connected 에서 처리
125
+ }
126
+
127
+ /** SwcAppMixin 이 구독 대상 메타를 꺼낼 때 사용 */
128
+ getMetadata(helperHostSet: HelperHostSet): SwcAppRouteChangeSubscriberMetadata[] {
129
+ return getSubscribeSwcAppRouteChangeMetadata(helperHostSet.$this);
130
+ }
131
+ }