@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.
- package/README.md +790 -337
- package/dist/cjs/SwcAppEngine.js.map +2 -2
- package/dist/cjs/decorators/addEventListener.js +1925 -22
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/applyAttribute.js +41 -47
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +14 -11
- package/dist/cjs/decorators/applyClass.js.map +2 -2
- package/dist/cjs/decorators/applyNode.js +19 -21
- package/dist/cjs/decorators/applyNode.js.map +2 -2
- package/dist/cjs/decorators/applyProperty.js +24 -48
- package/dist/cjs/decorators/applyProperty.js.map +2 -2
- package/dist/cjs/decorators/applySlot.js +9 -8
- package/dist/cjs/decorators/applySlot.js.map +2 -2
- package/dist/cjs/decorators/applyStyle.js +14 -11
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/around.js +124 -0
- package/dist/cjs/decorators/around.js.map +7 -0
- package/dist/cjs/decorators/changedAttribute.js +61 -24
- package/dist/cjs/decorators/changedAttribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +240 -196
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +14 -12
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/eventMedia.js +112 -0
- package/dist/cjs/decorators/eventMedia.js.map +7 -0
- package/dist/cjs/decorators/fetch.js +278 -0
- package/dist/cjs/decorators/fetch.js.map +7 -0
- package/dist/cjs/decorators/index.js +8 -2
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/intersectionObserver.js +76 -65
- package/dist/cjs/decorators/intersectionObserver.js.map +2 -2
- package/dist/cjs/decorators/lifecycles.js +11 -11
- package/dist/cjs/decorators/lifecycles.js.map +2 -2
- package/dist/cjs/decorators/mutationObserver.js +97 -90
- package/dist/cjs/decorators/mutationObserver.js.map +3 -3
- package/dist/cjs/decorators/parameter.js +156 -0
- package/dist/cjs/decorators/parameter.js.map +7 -0
- package/dist/cjs/decorators/publishSwcAppMessage.js +2 -2
- package/dist/cjs/decorators/publishSwcAppMessage.js.map +2 -2
- package/dist/cjs/decorators/query.js +11 -5
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/requestAnimationFrame.js +115 -0
- package/dist/cjs/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/cjs/decorators/resizeObserver.js +84 -69
- package/dist/cjs/decorators/resizeObserver.js.map +3 -3
- package/dist/cjs/decorators/setInterval.js +65 -22
- package/dist/cjs/decorators/setInterval.js.map +2 -2
- package/dist/cjs/decorators/setTimeout.js +68 -22
- package/dist/cjs/decorators/setTimeout.js.map +2 -2
- package/dist/cjs/decorators/state.js +13 -10
- package/dist/cjs/decorators/state.js.map +2 -2
- package/dist/cjs/decorators/{subscribeSwcAppMessageWhileConnected.js → subscribeSwcAppMessage.js} +20 -21
- package/dist/cjs/decorators/subscribeSwcAppMessage.js.map +7 -0
- package/dist/cjs/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +23 -19
- package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/cjs/decorators/swcAppRoute.js +149 -0
- package/dist/cjs/decorators/swcAppRoute.js.map +7 -0
- package/dist/cjs/elements/SwcApp.js +70 -0
- package/dist/cjs/elements/SwcApp.js.map +2 -2
- package/dist/cjs/elements/SwcAppMixin.js +256 -67
- package/dist/cjs/elements/SwcAppMixin.js.map +3 -3
- package/dist/cjs/types/index.js +5 -1
- package/dist/cjs/types/index.js.map +2 -2
- package/dist/cjs/types/window.d.js.map +1 -1
- package/dist/cjs/utils/Utils.js +36 -15
- package/dist/cjs/utils/Utils.js.map +2 -2
- package/dist/esm/SwcAppEngine.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +1925 -22
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/applyAttribute.js +41 -47
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +14 -11
- package/dist/esm/decorators/applyClass.js.map +2 -2
- package/dist/esm/decorators/applyNode.js +20 -22
- package/dist/esm/decorators/applyNode.js.map +2 -2
- package/dist/esm/decorators/applyProperty.js +24 -48
- package/dist/esm/decorators/applyProperty.js.map +2 -2
- package/dist/esm/decorators/applySlot.js +9 -8
- package/dist/esm/decorators/applySlot.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +14 -11
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/around.js +105 -0
- package/dist/esm/decorators/around.js.map +7 -0
- package/dist/esm/decorators/changedAttribute.js +61 -24
- package/dist/esm/decorators/changedAttribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +238 -194
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/emitCustomEvent.js +15 -13
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/eventMedia.js +94 -0
- package/dist/esm/decorators/eventMedia.js.map +7 -0
- package/dist/esm/decorators/fetch.js +259 -0
- package/dist/esm/decorators/fetch.js.map +7 -0
- package/dist/esm/decorators/index.js +8 -2
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/intersectionObserver.js +76 -65
- package/dist/esm/decorators/intersectionObserver.js.map +2 -2
- package/dist/esm/decorators/lifecycles.js +11 -11
- package/dist/esm/decorators/lifecycles.js.map +2 -2
- package/dist/esm/decorators/mutationObserver.js +97 -90
- package/dist/esm/decorators/mutationObserver.js.map +3 -3
- package/dist/esm/decorators/parameter.js +137 -0
- package/dist/esm/decorators/parameter.js.map +7 -0
- package/dist/esm/decorators/publishSwcAppMessage.js +2 -2
- package/dist/esm/decorators/publishSwcAppMessage.js.map +2 -2
- package/dist/esm/decorators/query.js +11 -5
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/requestAnimationFrame.js +96 -0
- package/dist/esm/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/esm/decorators/resizeObserver.js +84 -69
- package/dist/esm/decorators/resizeObserver.js.map +2 -2
- package/dist/esm/decorators/setInterval.js +65 -23
- package/dist/esm/decorators/setInterval.js.map +2 -2
- package/dist/esm/decorators/setTimeout.js +68 -23
- package/dist/esm/decorators/setTimeout.js.map +2 -2
- package/dist/esm/decorators/state.js +13 -10
- package/dist/esm/decorators/state.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppMessage.js +53 -0
- package/dist/esm/decorators/subscribeSwcAppMessage.js.map +7 -0
- package/dist/esm/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +20 -16
- package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/esm/decorators/swcAppRoute.js +130 -0
- package/dist/esm/decorators/swcAppRoute.js.map +7 -0
- package/dist/esm/elements/SwcApp.js +70 -0
- package/dist/esm/elements/SwcApp.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +258 -69
- package/dist/esm/elements/SwcAppMixin.js.map +3 -3
- package/dist/esm/types/index.js +5 -1
- package/dist/esm/types/index.js.map +2 -2
- package/dist/esm/utils/Utils.js +36 -15
- package/dist/esm/utils/Utils.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +4395 -1266
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/SwcAppEngine.d.ts +1 -0
- package/dist/types/SwcAppEngine.d.ts.map +1 -1
- package/dist/types/decorators/addEventListener.d.ts +936 -11
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +27 -37
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +2 -2
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +16 -16
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +28 -44
- package/dist/types/decorators/applyProperty.d.ts.map +1 -1
- package/dist/types/decorators/applySlot.d.ts.map +1 -1
- package/dist/types/decorators/applyStyle.d.ts +2 -2
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/around.d.ts +87 -0
- package/dist/types/decorators/around.d.ts.map +1 -0
- package/dist/types/decorators/changedAttribute.d.ts +22 -4
- package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts +0 -1
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/eventMedia.d.ts +48 -0
- package/dist/types/decorators/eventMedia.d.ts.map +1 -0
- package/dist/types/decorators/fetch.d.ts +139 -0
- package/dist/types/decorators/fetch.d.ts.map +1 -0
- package/dist/types/decorators/index.d.ts +8 -2
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/intersectionObserver.d.ts +32 -10
- package/dist/types/decorators/intersectionObserver.d.ts.map +1 -1
- package/dist/types/decorators/lifecycles.d.ts +8 -7
- package/dist/types/decorators/lifecycles.d.ts.map +1 -1
- package/dist/types/decorators/mutationObserver.d.ts +34 -12
- package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
- package/dist/types/decorators/parameter.d.ts +113 -0
- package/dist/types/decorators/parameter.d.ts.map +1 -0
- package/dist/types/decorators/publishSwcAppMessage.d.ts +0 -1
- package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +7 -7
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/requestAnimationFrame.d.ts +47 -0
- package/dist/types/decorators/requestAnimationFrame.d.ts.map +1 -0
- package/dist/types/decorators/resizeObserver.d.ts +34 -12
- package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
- package/dist/types/decorators/setInterval.d.ts +27 -5
- package/dist/types/decorators/setInterval.d.ts.map +1 -1
- package/dist/types/decorators/setTimeout.d.ts +27 -5
- package/dist/types/decorators/setTimeout.d.ts.map +1 -1
- package/dist/types/decorators/state.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts +44 -0
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts.map +1 -0
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +60 -0
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +1 -0
- package/dist/types/decorators/swcAppRoute.d.ts +94 -0
- package/dist/types/decorators/swcAppRoute.d.ts.map +1 -0
- package/dist/types/elements/SwcApp.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts +423 -388
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +43 -4
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +15 -5
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +4395 -1266
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +8 -6
- package/src/SwcAppEngine.ts +1 -0
- package/src/decorators/addEventListener.ts +1086 -28
- package/src/decorators/applyAttribute.ts +82 -134
- package/src/decorators/applyClass.ts +14 -10
- package/src/decorators/applyNode.ts +39 -37
- package/src/decorators/applyProperty.ts +72 -100
- package/src/decorators/applySlot.ts +9 -8
- package/src/decorators/applyStyle.ts +14 -10
- package/src/decorators/around.ts +196 -0
- package/src/decorators/changedAttribute.ts +65 -26
- package/src/decorators/elementDefine.ts +298 -220
- package/src/decorators/emitCustomEvent.ts +17 -13
- package/src/decorators/eventMedia.ts +119 -0
- package/src/decorators/fetch.ts +454 -0
- package/src/decorators/index.ts +8 -2
- package/src/decorators/intersectionObserver.ts +108 -78
- package/src/decorators/lifecycles.ts +24 -22
- package/src/decorators/mutationObserver.ts +140 -104
- package/src/decorators/parameter.ts +212 -0
- package/src/decorators/publishSwcAppMessage.ts +3 -4
- package/src/decorators/query.ts +22 -14
- package/src/decorators/requestAnimationFrame.ts +149 -0
- package/src/decorators/resizeObserver.ts +119 -82
- package/src/decorators/setInterval.ts +90 -23
- package/src/decorators/setTimeout.ts +93 -23
- package/src/decorators/state.ts +19 -13
- package/src/decorators/subscribeSwcAppMessage.ts +106 -0
- package/src/decorators/subscribeSwcAppRouteChange.ts +131 -0
- package/src/decorators/swcAppRoute.ts +231 -0
- package/src/elements/SwcApp.ts +40 -10
- package/src/elements/SwcAppMixin.ts +329 -85
- package/src/types/index.ts +38 -5
- package/src/types/window.d.ts +2 -18
- package/src/utils/Utils.ts +50 -18
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
- package/dist/cjs/examples/RxJSOperatorsExample.js +0 -285
- package/dist/cjs/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +0 -54
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
- package/dist/esm/examples/RxJSOperatorsExample.js +0 -266
- package/dist/esm/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +0 -23
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +0 -1
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +0 -31
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +0 -1
- package/dist/types/examples/RxJSOperatorsExample.d.ts +0 -94
- package/dist/types/examples/RxJSOperatorsExample.d.ts.map +0 -1
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +0 -91
- package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +0 -107
- package/src/examples/RxJSOperatorsExample.ts +0 -279
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
+
import { ensureInit } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { buildSwcParameterArgs } from './parameter';
|
|
2
5
|
import { ElementDefineLifeCycler, HelperHostSet } from '../types';
|
|
3
6
|
|
|
4
7
|
export const SET_INTERVAL_METADATA_KEY = Symbol.for('simple-web-component:set-interval');
|
|
5
8
|
|
|
9
|
+
export type SetIntervalType = 'onConnected' | 'returnValue';
|
|
10
|
+
|
|
6
11
|
export interface SetIntervalOptions {
|
|
7
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* 'onConnected' (connect 시 자동으로 시작, disconnect 시 자동 정리) |
|
|
14
|
+
* 'returnValue' (기본값 - 자동 시작 없음. 개발자가 이 메서드를 직접 호출해야 시작되고,
|
|
15
|
+
* 그 호출의 리턴값이 실제로 반복 실행될 함수가 된다 - 아래 설명 참고).
|
|
16
|
+
*/
|
|
17
|
+
type?: SetIntervalType;
|
|
18
|
+
/** 매 tick마다 데코레이트된 메서드에 넘길 인자를 계산. type:'onConnected'에서만 의미가 있음 (없으면 인자 없이 호출). */
|
|
8
19
|
parameter?: (set: HelperHostSet) => any[];
|
|
9
20
|
/** 타이머 등록 직후 1회 호출 (디버깅/로깅용). id는 실제 setInterval 반환값 - cleanup은 자동으로 처리되므로 직접 clear할 필요 없음. */
|
|
10
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>;
|
|
11
28
|
/**
|
|
12
|
-
* 매 tick마다 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
|
|
13
|
-
*
|
|
29
|
+
* type:'onConnected' - 매 tick마다 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
|
|
30
|
+
* type:'returnValue' - 리턴값이 함수면 그 자체를, 객체면 이 키의 값이 함수일 때 그 값을 "실제로 반복 실행될 함수"로 사용한다.
|
|
31
|
+
* 안 주면 SET_INTERVAL_METADATA_KEY로 폴백한다 - applyAttribute.ts/applyNode.ts의 valueKey와 동일한 컨벤션
|
|
32
|
+
* (리턴 객체에서 이 데코레이터 몫만 뽑아 쓰고, 다른 데코레이터와 스택할 때만 커스텀 키로 구분하면 된다).
|
|
14
33
|
*/
|
|
15
34
|
valueKey?: symbol | string;
|
|
16
35
|
}
|
|
@@ -21,60 +40,108 @@ export interface SetIntervalMetadata {
|
|
|
21
40
|
options: SetIntervalOptions;
|
|
22
41
|
}
|
|
23
42
|
|
|
43
|
+
const getActiveEntries = (inst: any): number[] => (inst.__swc_intervalIds ??= []);
|
|
44
|
+
|
|
24
45
|
/**
|
|
25
|
-
* connect 시 자동으로 setInterval을 걸고, disconnect 시 자동으로 clearInterval 한다.
|
|
46
|
+
* type:'onConnected' - connect 시 자동으로 setInterval을 걸고, disconnect 시 자동으로 clearInterval 한다.
|
|
47
|
+
* type:'returnValue'(기본값) - 메서드를 감싸지 않는 대신, 호출될 때마다(개발자가 직접 호출) 원본 로직을
|
|
48
|
+
* 그대로 실행하고 리턴값(또는 valueKey로 뽑은 값)이 함수면 그 함수를 새 setInterval의 반복 콜백으로 등록한다.
|
|
49
|
+
* 호출할 때마다 별도의 새 타이머가 생기며, disconnect 시 전부 정리된다. 원본 리턴값은 그대로 통과시키므로
|
|
50
|
+
* 다른 데코레이터와 같은 메서드에 스택해도 순서에 상관없이 안전하다.
|
|
26
51
|
*/
|
|
27
52
|
export function setInterval(interval: number, options: SetIntervalOptions = {}): MethodDecorator {
|
|
28
|
-
return (target: Object, propertyKey: string | symbol) => {
|
|
53
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
29
54
|
const constructor = target.constructor;
|
|
55
|
+
const normalizedOptions: SetIntervalOptions = { ...options, type: options.type ?? 'returnValue' };
|
|
56
|
+
|
|
30
57
|
let metaList = ReflectUtils.getOwnMetadata(SET_INTERVAL_METADATA_KEY, constructor) as SetIntervalMetadata[];
|
|
31
58
|
if (!metaList) {
|
|
32
59
|
metaList = [];
|
|
33
60
|
ReflectUtils.defineMetadata(SET_INTERVAL_METADATA_KEY, metaList, constructor);
|
|
34
61
|
}
|
|
35
|
-
metaList.push({ propertyKey, interval, options });
|
|
62
|
+
metaList.push({ propertyKey, interval, options: normalizedOptions });
|
|
63
|
+
|
|
64
|
+
if (normalizedOptions.type === 'onConnected' || !descriptor) {
|
|
65
|
+
return; // LifeCycler가 connect/disconnect를 전담 - wrapping 불필요
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const original = descriptor.value;
|
|
69
|
+
descriptor.value = function (this: any, ...args: any[]) {
|
|
70
|
+
const res = original.apply(this, args);
|
|
71
|
+
// valueKey를 안 주면 이 데코레이터 전용 METADATA_KEY로 폴백 (applyAttribute.ts/applyNode.ts와 동일한 컨벤션)
|
|
72
|
+
const vk = normalizedOptions.valueKey ?? SET_INTERVAL_METADATA_KEY;
|
|
73
|
+
const tickFn: ((id: number) => void) | undefined =
|
|
74
|
+
typeof res === 'function' ? res : res && typeof res === 'object' && typeof res[vk] === 'function' ? res[vk] : undefined;
|
|
75
|
+
|
|
76
|
+
if (tickFn) {
|
|
77
|
+
ensureInit(this);
|
|
78
|
+
const win = SwcUtils.resolveWindow(this);
|
|
79
|
+
const id = win.setInterval(() => {
|
|
80
|
+
try {
|
|
81
|
+
tickFn(id);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.error('[SWC] setInterval tick error:', e);
|
|
84
|
+
}
|
|
85
|
+
}, interval);
|
|
86
|
+
normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(this), id);
|
|
87
|
+
getActiveEntries(this).push(id);
|
|
88
|
+
}
|
|
89
|
+
return res;
|
|
90
|
+
};
|
|
91
|
+
return descriptor;
|
|
36
92
|
};
|
|
37
93
|
}
|
|
38
94
|
|
|
39
95
|
export const findAllSetIntervalMetadata = (target: any): SetIntervalMetadata[] => {
|
|
40
|
-
const constructor = target
|
|
96
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
41
97
|
return (ReflectUtils.findAllMetadata<SetIntervalMetadata[]>(SET_INTERVAL_METADATA_KEY, constructor) || []).flat();
|
|
42
98
|
};
|
|
43
99
|
|
|
44
100
|
export class SetIntervalLifeCycler implements ElementDefineLifeCycler {
|
|
45
|
-
private readonly activeMap = new WeakMap<any, number[]>();
|
|
46
|
-
|
|
47
101
|
onConnected(helperHostSet: HelperHostSet): void {
|
|
48
102
|
const inst = helperHostSet.$this;
|
|
49
|
-
|
|
103
|
+
inst.__swc_intervalIds = []; // 재연결 대비 리셋
|
|
50
104
|
for (const meta of findAllSetIntervalMetadata(inst)) {
|
|
105
|
+
if (meta.options.type !== 'onConnected') continue; // returnValue 타입은 수동 호출을 기다림
|
|
51
106
|
const id = helperHostSet.$w.setInterval(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
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 });
|
|
58
127
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
128
|
+
if (error) console.error('[SWC] setInterval tick error:', error);
|
|
129
|
+
})().catch(e => console.error('[SWC] setInterval tick error:', e));
|
|
62
130
|
}, meta.interval);
|
|
63
131
|
meta.options.created?.(helperHostSet, id);
|
|
64
|
-
|
|
132
|
+
getActiveEntries(inst).push(id);
|
|
65
133
|
}
|
|
66
|
-
this.activeMap.set(inst, ids);
|
|
67
134
|
}
|
|
68
135
|
|
|
69
136
|
onDisconnected(helperHostSet: HelperHostSet): void {
|
|
70
137
|
const inst = helperHostSet.$this;
|
|
71
|
-
for (const id of
|
|
138
|
+
for (const id of inst.__swc_intervalIds ?? []) {
|
|
72
139
|
try {
|
|
73
140
|
helperHostSet.$w.clearInterval(id);
|
|
74
141
|
} catch (e) {
|
|
75
142
|
console.error('[SWC] clearInterval error:', e);
|
|
76
143
|
}
|
|
77
144
|
}
|
|
78
|
-
|
|
145
|
+
inst.__swc_intervalIds = [];
|
|
79
146
|
}
|
|
80
147
|
}
|
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
+
import { ensureInit } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { buildSwcParameterArgs } from './parameter';
|
|
2
5
|
import { ElementDefineLifeCycler, HelperHostSet } from '../types';
|
|
3
6
|
|
|
4
7
|
export const SET_TIMEOUT_METADATA_KEY = Symbol.for('simple-web-component:set-timeout');
|
|
5
8
|
|
|
9
|
+
export type SetTimeoutType = 'onConnected' | 'returnValue';
|
|
10
|
+
|
|
6
11
|
export interface SetTimeoutOptions {
|
|
7
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* 'onConnected' (connect 시 자동으로 예약, disconnect 시 자동 정리) |
|
|
14
|
+
* 'returnValue' (기본값 - 자동 예약 없음. 개발자가 이 메서드를 직접 호출해야 예약되고,
|
|
15
|
+
* 그 호출의 리턴값이 실제로 실행될 함수가 된다 - 아래 설명 참고).
|
|
16
|
+
*/
|
|
17
|
+
type?: SetTimeoutType;
|
|
18
|
+
/** 실행 시 데코레이트된 메서드에 넘길 인자를 계산. type:'onConnected'에서만 의미가 있음 (없으면 인자 없이 호출). */
|
|
8
19
|
parameter?: (set: HelperHostSet) => any[];
|
|
9
20
|
/** 타이머 등록 직후 1회 호출 (디버깅/로깅용). id는 실제 setTimeout 반환값 - cleanup은 자동으로 처리되므로 직접 clear할 필요 없음. */
|
|
10
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>;
|
|
11
28
|
/**
|
|
12
|
-
* fire 시 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
|
|
13
|
-
*
|
|
29
|
+
* type:'onConnected' - fire 시 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
|
|
30
|
+
* type:'returnValue' - 리턴값이 함수면 그 자체를, 객체면 이 키의 값이 함수일 때 그 값을 "실제로 실행될 함수"로 사용한다.
|
|
31
|
+
* 안 주면 SET_TIMEOUT_METADATA_KEY로 폴백한다 - applyAttribute.ts/applyNode.ts의 valueKey와 동일한 컨벤션
|
|
32
|
+
* (리턴 객체에서 이 데코레이터 몫만 뽑아 쓰고, 다른 데코레이터와 스택할 때만 커스텀 키로 구분하면 된다).
|
|
14
33
|
*/
|
|
15
34
|
valueKey?: symbol | string;
|
|
16
35
|
}
|
|
@@ -21,60 +40,111 @@ export interface SetTimeoutMetadata {
|
|
|
21
40
|
options: SetTimeoutOptions;
|
|
22
41
|
}
|
|
23
42
|
|
|
43
|
+
const getActiveEntries = (inst: any): number[] => (inst.__swc_timeoutIds ??= []);
|
|
44
|
+
|
|
24
45
|
/**
|
|
25
|
-
* connect 시 자동으로 setTimeout을 걸고, disconnect 시 (아직 실행 전이라면) 자동으로 clearTimeout 한다.
|
|
46
|
+
* type:'onConnected' - connect 시 자동으로 setTimeout을 걸고, disconnect 시 (아직 실행 전이라면) 자동으로 clearTimeout 한다.
|
|
47
|
+
* type:'returnValue'(기본값) - 메서드를 감싸지 않는 대신, 호출될 때마다(개발자가 직접 호출) 원본 로직을
|
|
48
|
+
* 그대로 실행하고 리턴값(또는 valueKey로 뽑은 값)이 함수면 그 함수를 새 setTimeout으로 예약한다.
|
|
49
|
+
* 호출할 때마다 별도의 새 타이머가 생기며, disconnect 시 (아직 실행 전인 것들은) 전부 정리된다.
|
|
50
|
+
* 원본 리턴값은 그대로 통과시키므로 다른 데코레이터와 같은 메서드에 스택해도 순서에 상관없이 안전하다.
|
|
26
51
|
*/
|
|
27
52
|
export function setTimeout(delay: number, options: SetTimeoutOptions = {}): MethodDecorator {
|
|
28
|
-
return (target: Object, propertyKey: string | symbol) => {
|
|
53
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
29
54
|
const constructor = target.constructor;
|
|
55
|
+
const normalizedOptions: SetTimeoutOptions = { ...options, type: options.type ?? 'returnValue' };
|
|
56
|
+
|
|
30
57
|
let metaList = ReflectUtils.getOwnMetadata(SET_TIMEOUT_METADATA_KEY, constructor) as SetTimeoutMetadata[];
|
|
31
58
|
if (!metaList) {
|
|
32
59
|
metaList = [];
|
|
33
60
|
ReflectUtils.defineMetadata(SET_TIMEOUT_METADATA_KEY, metaList, constructor);
|
|
34
61
|
}
|
|
35
|
-
metaList.push({ propertyKey, delay, options });
|
|
62
|
+
metaList.push({ propertyKey, delay, options: normalizedOptions });
|
|
63
|
+
|
|
64
|
+
if (normalizedOptions.type === 'onConnected' || !descriptor) {
|
|
65
|
+
return; // LifeCycler가 connect/disconnect를 전담 - wrapping 불필요
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const original = descriptor.value;
|
|
69
|
+
descriptor.value = function (this: any, ...args: any[]) {
|
|
70
|
+
const res = original.apply(this, args);
|
|
71
|
+
// valueKey를 안 주면 이 데코레이터 전용 METADATA_KEY로 폴백 (applyAttribute.ts/applyNode.ts와 동일한 컨벤션)
|
|
72
|
+
const vk = normalizedOptions.valueKey ?? SET_TIMEOUT_METADATA_KEY;
|
|
73
|
+
const tickFn: ((id: number) => void) | undefined =
|
|
74
|
+
typeof res === 'function' ? res : res && typeof res === 'object' && typeof res[vk] === 'function' ? res[vk] : undefined;
|
|
75
|
+
|
|
76
|
+
if (tickFn) {
|
|
77
|
+
ensureInit(this);
|
|
78
|
+
const win = SwcUtils.resolveWindow(this);
|
|
79
|
+
const id = win.setTimeout(() => {
|
|
80
|
+
const entries = getActiveEntries(this);
|
|
81
|
+
const idx = entries.indexOf(id);
|
|
82
|
+
if (idx >= 0) entries.splice(idx, 1);
|
|
83
|
+
try {
|
|
84
|
+
tickFn(id);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error('[SWC] setTimeout fire error:', e);
|
|
87
|
+
}
|
|
88
|
+
}, delay);
|
|
89
|
+
normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(this), id);
|
|
90
|
+
getActiveEntries(this).push(id);
|
|
91
|
+
}
|
|
92
|
+
return res;
|
|
93
|
+
};
|
|
94
|
+
return descriptor;
|
|
36
95
|
};
|
|
37
96
|
}
|
|
38
97
|
|
|
39
98
|
export const findAllSetTimeoutMetadata = (target: any): SetTimeoutMetadata[] => {
|
|
40
|
-
const constructor = target
|
|
99
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
41
100
|
return (ReflectUtils.findAllMetadata<SetTimeoutMetadata[]>(SET_TIMEOUT_METADATA_KEY, constructor) || []).flat();
|
|
42
101
|
};
|
|
43
102
|
|
|
44
103
|
export class SetTimeoutLifeCycler implements ElementDefineLifeCycler {
|
|
45
|
-
private readonly activeMap = new WeakMap<any, number[]>();
|
|
46
|
-
|
|
47
104
|
onConnected(helperHostSet: HelperHostSet): void {
|
|
48
105
|
const inst = helperHostSet.$this;
|
|
49
|
-
|
|
106
|
+
inst.__swc_timeoutIds = []; // 재연결 대비 리셋
|
|
50
107
|
for (const meta of findAllSetTimeoutMetadata(inst)) {
|
|
108
|
+
if (meta.options.type !== 'onConnected') continue; // returnValue 타입은 수동 호출을 기다림
|
|
51
109
|
const id = helperHostSet.$w.setTimeout(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
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 });
|
|
58
130
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
131
|
+
if (error) console.error('[SWC] setTimeout fire error:', error);
|
|
132
|
+
})().catch(e => console.error('[SWC] setTimeout fire error:', e));
|
|
62
133
|
}, meta.delay);
|
|
63
134
|
meta.options.created?.(helperHostSet, id);
|
|
64
|
-
|
|
135
|
+
getActiveEntries(inst).push(id);
|
|
65
136
|
}
|
|
66
|
-
this.activeMap.set(inst, ids);
|
|
67
137
|
}
|
|
68
138
|
|
|
69
139
|
onDisconnected(helperHostSet: HelperHostSet): void {
|
|
70
140
|
const inst = helperHostSet.$this;
|
|
71
|
-
for (const id of
|
|
141
|
+
for (const id of inst.__swc_timeoutIds ?? []) {
|
|
72
142
|
try {
|
|
73
143
|
helperHostSet.$w.clearTimeout(id);
|
|
74
144
|
} catch (e) {
|
|
75
145
|
console.error('[SWC] clearTimeout error:', e);
|
|
76
146
|
}
|
|
77
147
|
}
|
|
78
|
-
|
|
148
|
+
inst.__swc_timeoutIds = [];
|
|
79
149
|
}
|
|
80
150
|
}
|
package/src/decorators/state.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
28
|
-
//
|
|
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
|
-
|
|
32
|
-
const config = getElementConfig(this)
|
|
30
|
+
this[storageKey] = nv;
|
|
33
31
|
const ea = new ElementApply(this, {id: this._swcId});
|
|
34
|
-
const helperHostSet = SwcUtils.getHelperAndHostSet(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
82
|
-
return ReflectUtils.
|
|
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
|
+
}
|