위임 관리자(부관리자)가 권한·역할·표현식·비밀 콘텐츠 경계를 우회하던 결함군을 계층 대칭성 원칙으로 전건 차단한다. 약한 경로가 정상 응답을 내보내는 것이 유일한 증상이라, 게이트를 생산 지점 한 곳(SSoT)에 두고 같은 데이터를 내보내는 소비 경로 전부가 그 게이트를 경유하도록 맞췄다. - 등급 상한(rank ceiling): 슈퍼관리자 보호·역할/사용자 역할 배정(추가·제거 대칭)·일괄 상태변경·순서변경을 상세 경로와 동일 강도로 재적용. 가드는 DB 쓰기에 선행하여 거부 시 상태 불변. - 레이아웃 표현식: new Function/with 실행을 AST 화이트리스트 평가기로 교체. 비-문자열 computed 키 정규화(normalizeKey)·Object facade(리플렉션 static 제거)·legacy 접근자 차단. 저장측 검증·정적 검사와 3계층 동형. - secret 게이트: 비밀글의 댓글·첨부·문의 독립 경로 재적용, hash 파일서빙 소유권·비밀·발행 상태 검사 통일. - 신뢰 스크립트 호스트: 확장 선언 기반 + same-origin 브라우저 정규화를 런타임·저장측·정적검사 3층 동형화. - 회귀 감지: 단위·Feature·E2E·시나리오 매니페스트 전축 + audit 룰 4종 신설.
109 lines
6.5 KiB
YAML
109 lines
6.5 KiB
YAML
feature: 레이아웃 편집기 액션 편집 · flex 편집 · 표시 조건
|
||
phase: 5
|
||
session: S7
|
||
issue: 383
|
||
# axis 의미:
|
||
# 본 매니페스트가 묶는 세 가지(액션 편집 / flex 편집 / 표시 조건)는 편집기 속성 패널의
|
||
# 서로 다른 탭이며 상호작용하지 않는다. flex 방향이 액션 레시피 조립을 바꾸지 않고,
|
||
# 조건 조합자가 flex 정렬을 바꾸지 않는다. 세 도메인의 축을 곱하면 134만 조합이 되지만
|
||
# 그중 실재하는 판정은 도메인 내부의 것뿐이다.
|
||
#
|
||
# axes 로 남긴 것: 액션 레시피 × 후속 체인.
|
||
# 레시피 종류에 따라 후속 체인이 성립하는지가 갈리므로(openModal 은 체인 없음)
|
||
# 이 둘만 실제로 교차한다.
|
||
axes:
|
||
action_recipe: [goToPage, showMessage, changeState, refreshData, callServerThen, openModal_lazy_id, custom_extension_recipe]
|
||
followup_chain: [none, success_and_error_both]
|
||
exclusions:
|
||
- { action_recipe: openModal_lazy_id, followup_chain: success_and_error_both,
|
||
reason: "openModal 은 onSuccess/onError 체인 의미 없음 — 즉시 모달 오픈" }
|
||
- { action_recipe: goToPage, followup_chain: success_and_error_both,
|
||
reason: "페이지 이동은 응답이 없어 후속 체인이 성립하지 않음" }
|
||
- { action_recipe: showMessage, followup_chain: success_and_error_both,
|
||
reason: "토스트 표시는 응답이 없어 후속 체인이 성립하지 않음" }
|
||
- { action_recipe: changeState, followup_chain: success_and_error_both,
|
||
reason: "상태 변경은 동기 — 후속 체인 없음" }
|
||
- { action_recipe: refreshData, followup_chain: success_and_error_both,
|
||
reason: "refreshData 레시피의 build 에 onSuccess/onError 자리가 없다(refetchDataSource + dataSourceId 뿐) — 후속 체인을 갖는 레시피는 callServerThen 하나다" }
|
||
|
||
sub_flows:
|
||
- id: action_event_slot
|
||
description: |
|
||
액션이 걸리는 이벤트 슬롯. 레시피 조립 결과와 곱해지지 않는다 — 같은 레시피가
|
||
어느 슬롯에 걸리든 산출 JSON 은 동일하고, 슬롯은 스펙 선언 여부만 가른다.
|
||
축: event_kind(onClick·onHover·onComponentEvent·lifecycle_onMount·lifecycle_onUnmount)
|
||
effects:
|
||
- spec_undeclared_event_hides_action_slot
|
||
- nested_action_list_widget_assembles_recursive_actions
|
||
- action_recipe_reverse_matches_existing_action_to_recipe
|
||
- action_recipe_does_not_expose_handler_jargon_to_user
|
||
- id: flex_editing
|
||
description: |
|
||
flex 컨테이너/아이템 편집. 액션·조건과 무관한 스타일 축이다.
|
||
축: flex_role(container·item·auto_div_detected_as_flex·auto_div_detected_as_block) ×
|
||
flex_direction(row·column) × flex_wrap × flex_justify × flex_align × item_grow
|
||
effects:
|
||
- flex_container_classToken_emits_per_spec_apply_type
|
||
- flex_auto_detection_uses_computed_style_not_classname
|
||
- flex_auto_detection_works_across_tailwind_bootstrap_native_css
|
||
- flex_item_align_self_emits_correct_recipe
|
||
- flex_item_order_property_set
|
||
- flex_make_box_button_when_not_rendered_as_flex
|
||
- id: display_condition
|
||
description: |
|
||
표시 조건(if) 빌더. 조건 카테고리 × 조합자 × 파라미터 종류가 이 도메인 안에서 교차한다.
|
||
축: condition_category(A~H) × condition_combinator(single·and·or·mixed_and_or) ×
|
||
condition_param_kind(no_param·ds_id·state_key·field_name·value_literal·ability_key)
|
||
effects:
|
||
- condition_recipe_substitutes_params_into_expr_template
|
||
- condition_recipe_wraps_entire_combined_expression_in_single_braces
|
||
- condition_builder_does_not_emit_nested_interpolation_braces
|
||
- condition_recipe_uses_optional_chaining_for_null_safety
|
||
- condition_recipe_array_length_uses_length_nullish_zero_fallback
|
||
- condition_synthesized_expr_evaluates_via_condition_evaluator
|
||
- condition_builder_reverse_matches_single_preset
|
||
- condition_builder_preserves_advanced_handwritten_expr
|
||
- condition_param_widget_dispatches_via_widget_registry
|
||
|
||
effects:
|
||
- action_recipe_build_emits_correct_handler_name_and_top_level_target
|
||
- action_recipe_top_level_onSuccess_onError_keys_not_in_params
|
||
- action_recipe_handler_name_follows_claude_md_critical_rules
|
||
- action_recipe_navigate_uses_navigate_not_nav
|
||
- action_recipe_apiCall_target_outside_params_to_avoid_resolvedTarget_undefined
|
||
- action_recipe_refetchDataSource_uses_dataSourceId_key_not_id
|
||
- action_recipe_toast_handler_not_showToast
|
||
- action_recipe_setState_target_top_level_not_in_params
|
||
- nested_action_list_widget_assembles_recursive_actions
|
||
- action_recipe_does_not_expose_handler_jargon_to_user
|
||
- action_recipe_reverse_matches_existing_action_to_recipe
|
||
- flex_container_classToken_emits_per_spec_apply_type
|
||
- flex_auto_detection_uses_computed_style_not_classname
|
||
- flex_auto_detection_works_across_tailwind_bootstrap_native_css
|
||
- flex_item_align_self_emits_correct_recipe
|
||
- flex_item_order_property_set
|
||
- flex_make_box_button_when_not_rendered_as_flex
|
||
- condition_recipe_substitutes_params_into_expr_template
|
||
- condition_recipe_wraps_entire_combined_expression_in_single_braces
|
||
- condition_builder_does_not_emit_nested_interpolation_braces
|
||
- condition_recipe_uses_optional_chaining_for_null_safety
|
||
- condition_recipe_array_length_uses_length_nullish_zero_fallback
|
||
- condition_synthesized_expr_evaluates_via_condition_evaluator
|
||
- condition_builder_reverse_matches_single_preset
|
||
- condition_builder_preserves_advanced_handwritten_expr
|
||
- spec_undeclared_event_hides_action_slot
|
||
- condition_param_widget_dispatches_via_widget_registry
|
||
test_files:
|
||
- resources/js/core/template-engine/layout-editor/__tests__/spec/actionRecipeEngine.test.ts
|
||
- resources/js/core/template-engine/layout-editor/__tests__/spec/conditionRecipeEngine.test.ts
|
||
- resources/js/core/template-engine/layout-editor/__tests__/spec/flexModel.test.ts
|
||
- resources/js/core/template-engine/layout-editor/__tests__/components/s7-property-controls.test.tsx
|
||
- resources/js/core/template-engine/layout-editor/__tests__/layouts/condition-toggle-render.test.tsx
|
||
e2e_files:
|
||
- tests/Playwright/specs/layout-editor/action-flex-condition.spec.ts
|
||
notes: |
|
||
액션/조건 레시피 적용기는 Pure Logic DataProvider (친화 입력 → 핸들러/식 JSON).
|
||
ActionRecipeEditor/ConditionBuilder/FlexEditor 는 Component RTL.
|
||
합성 if 식의 실제 평가는 condition-toggle-render (ConditionEvaluator 라운드트립).
|
||
Chrome MCP drag 비호환(dnd-kit) 으로 캔버스 클릭 가로채기 axis 는 코드경로+RTL 로 대체.
|