perf: split frontend routes from initial bundle (AEG-X-002)
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# AEG-X-002 — Route Code-Splitting Refactoring
|
||||
|
||||
- **WBS / Requirement:** AEG-X-002 / REQ-PLAT-TOOL
|
||||
- **Classification:** behavior-preserving frontend build refactoring
|
||||
- **Selected on:** 2026-08-08
|
||||
|
||||
## Source / Assumption / Unknown / Decision Required
|
||||
|
||||
- **Source:** `frontend/src/app/router.ts` and the co-located tracked `router.js` initially statically import every route component; the actual 2026-08-08 production build reports a 501.14 kB gzip initial JavaScript chunk. A first TypeScript-only refactoring did not change build output, demonstrating that the Vite resolver uses the co-located JavaScript router entry in this repository.
|
||||
- **Assumption:** Vue Router's supported lazy route-component function preserves every route path, metadata, guard behavior, and component API while moving feature code out of the initial chunk.
|
||||
- **Unknown:** an approved application performance budget is not present. Chunk reduction is an observation, not a performance-gate pass.
|
||||
- **Decision Required:** none for this refactoring. Budget approval, visual evidence, and large-list interaction evidence remain separate work.
|
||||
|
||||
## Change
|
||||
|
||||
- Replace route component static imports with lazy `import()` factories in both active co-located router entries.
|
||||
|
||||
## Acceptance Evidence
|
||||
|
||||
- All existing route paths and metadata remain unchanged.
|
||||
- `pnpm test` passed: 26 files / 58 tests. Preserved output: `evidence/AEG-X-002/frontend-regression-route-split_20260808.log`.
|
||||
- `pnpm build` passed. Preserved output: `evidence/AEG-X-002/frontend-build-route-split_20260808.log`.
|
||||
- The initial gzip JavaScript chunk changed from 501.14 kB to 423.76 kB and page chunks are emitted separately. Vite still warns because its default raw-size threshold is exceeded; the threshold was not changed and this is not an approved performance-gate pass.
|
||||
|
||||
No API, provider, policy, database, capability, or route authorization behavior changes.
|
||||
@@ -1,6 +1,6 @@
|
||||
WBS_ID,Sprint,Slice_ID,Task,Status,Completion_Date,Evidence_Link,Owner,Notes
|
||||
AEG-X-001,S0,Cross,Version Coverage Matrix 고도화,COMPLETED,2026-08-04,docs/contracts/platform/VERSION_COVERAGE_MATRIX.md,PM/Architect,"✅ Version matrix: v10/v12/v12.1 compatibility (Retained/Improved/Superseded 100%), Supersession registry, Breaking change assessment, Migration roadmap"
|
||||
AEG-X-002,S0,Cross,global.json 고도화,COMPLETED,2026-08-08,".gitea/workflows/ci.yml (dotnet/pnpm restore/build/test); docs/CURRENT/AEG-X-002_TYPECHECK_EMIT_REMEDIATION.md; evidence/AEG-X-002/frontend-build-noemit_20260808.log",DevOps,"2026-08-08: behavior-preserving frontend toolchain regression remediated. `pnpm build` now executes `vue-tsc --noEmit && vite build`; actual build passed and before/after hashes of previously dirtied tracked *.vue.js files were unchanged. Full frontend regression: 26 files / 57 tests passed before this command-only change. Build retains a 501.14 kB gzip chunk warning; no performance pass is claimed."
|
||||
AEG-X-002,S0,Cross,global.json 고도화,COMPLETED,2026-08-08,".gitea/workflows/ci.yml (dotnet/pnpm restore/build/test); docs/CURRENT/AEG-X-002_TYPECHECK_EMIT_REMEDIATION.md; docs/CURRENT/AEG-X-002_ROUTE_CODE_SPLITTING.md; evidence/AEG-X-002/frontend-build-noemit_20260808.log; evidence/AEG-X-002/frontend-regression-route-split_20260808.log; evidence/AEG-X-002/frontend-build-route-split_20260808.log",DevOps,"2026-08-08: behavior-preserving frontend toolchain corrections completed. `pnpm build` uses `vue-tsc --noEmit && vite build`; actual no-emit build passed. Router static imports were replaced in both active co-located router entries after the first TS-only change proved Vite resolves the JS entry. Full regression: 26 files / 58 tests passed. Initial gzip JS reduced 501.14 kB→423.76 kB with per-route chunks. Vite raw-size warning remains; no approved performance-gate pass is claimed."
|
||||
AEG-X-003,S0,Cross,Architecture tests 고도화,COMPLETED,2026-08-04,tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs (6 tests PASSING),Architect/QA,"✅ Architecture rules enforced: (1) No prohibited patterns, (2) Domain isolation from infrastructure, (3) SQL validation (no SELECT *, schema-qualified), (4) Endpoint authorization (Roles/Policies), (5) No placeholder files, (6) No duplicate aggregate IDs. All 6 tests PASS."
|
||||
AEG-X-004,S0,Cross,DbUp 복구 rehearsal 고도화,COMPLETED,2026-08-06,"docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md; docs/CURRENT/AEG-X-004_STATUS_CONTRACT_SLICE.md; db/migrations/0032_shadow_run_queued_status_contract.sql; tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs; tests/KArtSell.Integration.Tests/DbUpRecoveryTests.cs; evidence/AEG-X-004/0032-isolated-migration.trx",DBA/BE,"✅ Queued status contract applied as append-only 0032; isolated kartsell_migration_test rehearsal targeted 1/1 and recovery 6/6 passed. kartselldb_test was not reset. Production migration/DBA approval and Phase 1 requeue remain unclaimed. ⚠️ 2026-08-07 regression: 12 DbUpMigrationTests (Migration0008/0009/0010/0032) now fail locally with Postgres 42501 'must be owner of database kartsell_migration_test' — the kartsell DB user no longer owns/can DROP+CREATE that database on this environment. Code-side (fix/dapper-underscore-mapping-and-build branch) is unaffected; this needs a DBA grant (ALTER DATABASE kartsell_migration_test OWNER TO kartsell, or equivalent) before the fresh/upgrade/re-run rehearsal can be re-verified."
|
||||
AEG-X-005,S0,Cross,Security auth 고도화,COMPLETED,2026-08-04,"docs/decisions/ADR-SEC-001.md + tests/KArtSell.Integration.Tests/SecurityAuthenticationTests.cs (6 tests)",Security/BE,"✅ ADR-SEC-001 produced (OIDC/JWT/DevelopmentHeader tiers), SecurityAuthenticationTests.cs (6 tests): endpoint authorization, DevelopmentHeader mode check, secret logging prevention, secret hardcoding check, AI prompt PII, auth config validation. Acceptance_Evidence verified: '비개발 무인증 접근 0, secret/log/prompt 노출 0'"
|
||||
|
||||
|
@@ -0,0 +1,41 @@
|
||||
|
||||
> kartsell-frontend@0.1.0 build D:\JobRoomz\KArtSell.Aegis\frontend
|
||||
> vue-tsc --noEmit && vite build
|
||||
|
||||
[36mvite v8.2.0 [32mbuilding client environment for production...[36m[39m
|
||||
[2K
|
||||
transforming...✓ 539 modules transformed.
|
||||
rendering chunks...
|
||||
computing gzip size...
|
||||
dist/index.html 0.48 kB │ gzip: 0.31 kB
|
||||
dist/assets/QueryStateBoundary-Cm4hqHqN.css 0.08 kB │ gzip: 0.10 kB
|
||||
dist/assets/components-F4IuDNEJ.css 0.16 kB │ gzip: 0.13 kB
|
||||
dist/assets/UiStandardPage-Atul8cRq.css 0.99 kB │ gzip: 0.37 kB
|
||||
dist/assets/WbsWorkspacePage-Cm2tRI08.css 2.00 kB │ gzip: 0.62 kB
|
||||
dist/assets/IngestionStatus-BxFe4JZn.css 2.29 kB │ gzip: 0.69 kB
|
||||
dist/assets/RebalanceForm-CVHOjC1p.css 2.73 kB │ gzip: 0.76 kB
|
||||
dist/assets/MarketDataIngestion-DHfK7rqp.css 3.40 kB │ gzip: 0.89 kB
|
||||
dist/assets/RiskDashboard-BYX4FW-a.css 5.55 kB │ gzip: 1.35 kB
|
||||
dist/assets/index-B95bfm6H.css 10.87 kB │ gzip: 2.66 kB
|
||||
dist/assets/KsInlineMessage-Dlf3N6cz.js 0.81 kB │ gzip: 0.45 kB
|
||||
dist/assets/DataQualityPage-D-KoGLI8.js 1.41 kB │ gzip: 0.86 kB
|
||||
dist/assets/QueryStateBoundary-Ce9Y6DVD.js 2.51 kB │ gzip: 1.22 kB
|
||||
dist/assets/WbsWorkspacePage-Bm9s-J8M.js 2.95 kB │ gzip: 1.56 kB
|
||||
dist/assets/components-C61hxtTB.js 3.19 kB │ gzip: 1.16 kB
|
||||
dist/assets/IngestionStatus-c1TR6aqO.js 3.26 kB │ gzip: 1.32 kB
|
||||
dist/assets/RebalanceForm-C49XLG4q.js 3.50 kB │ gzip: 1.46 kB
|
||||
dist/assets/MarketDataIngestion-Dbre_wda.js 6.06 kB │ gzip: 2.44 kB
|
||||
dist/assets/RiskDashboard-DibfOweQ.js 6.72 kB │ gzip: 2.44 kB
|
||||
dist/assets/UiStandardPage-CIoUHY2t.js 8.63 kB │ gzip: 3.82 kB
|
||||
dist/assets/SellDecisionPage-BnBdDU-n.js 8.90 kB │ gzip: 3.70 kB
|
||||
dist/assets/ModelOperationsPage-BtkopGzg.js 11.46 kB │ gzip: 4.55 kB
|
||||
dist/assets/_plugin-vue_export-helper-CAeQu6x4.js 65.35 kB │ gzip: 25.65 kB
|
||||
dist/assets/schemas-D2HQgFg1.js 114.29 kB │ gzip: 35.34 kB
|
||||
dist/assets/index-CQ315XnJ.js 1,604.35 kB │ gzip: 423.76 kB
|
||||
|
||||
[32m✓ built in 1.36s[39m
|
||||
[plugin builtin:vite-reporter]
|
||||
(!) Some chunks are larger than 500 kB after minification. Consider:
|
||||
- Using dynamic import() to code-split the application
|
||||
- Use build.rolldownOptions.output.codeSplitting to improve chunking: https://rolldown.rs/reference/OutputOptions.codeSplitting
|
||||
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
> kartsell-frontend@0.1.0 test D:\JobRoomz\KArtSell.Aegis\frontend
|
||||
> vitest run
|
||||
|
||||
|
||||
RUN v4.1.10 D:/JobRoomz/KArtSell.Aegis/frontend
|
||||
|
||||
|
||||
Test Files 26 passed (26)
|
||||
Tests 58 passed (58)
|
||||
Start at 16:19:53
|
||||
Duration 32.53s (transform 5.54s, setup 0ms, import 33.62s, tests 1.41s, environment 319.66s)
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import SellDecisionPage from '../features/sell-decision/pages/SellDecisionPage.vue';
|
||||
import DataQualityPage from '../features/data-quality/pages/DataQualityPage.vue';
|
||||
import ModelOperationsPage from '../features/model-operations/pages/ModelOperationsPage.vue';
|
||||
import UiStandardPage from '../features/ui-standard/pages/UiStandardPage.vue';
|
||||
import RiskDashboard from '../features/portfolio/pages/RiskDashboard.vue';
|
||||
import RebalanceForm from '../features/portfolio/pages/RebalanceForm.vue';
|
||||
import MarketDataIngestion from '../features/marketData/pages/MarketDataIngestion.vue';
|
||||
import IngestionStatus from '../features/marketData/pages/IngestionStatus.vue';
|
||||
export const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{ path: '/', redirect: '/research/sell-decision' },
|
||||
{ path: '/research/sell-decision', component: SellDecisionPage, meta: { screenId: 'SCR-002', templateId: 'T02' } },
|
||||
{ path: '/ops/data-quality', component: DataQualityPage, meta: { screenId: 'SCR-013', templateId: 'T08' } },
|
||||
{ path: '/ops/model-operations', component: ModelOperationsPage, meta: { screenId: 'SCR-015', templateId: 'T10' } },
|
||||
{ path: '/ops/market-data-ingestion', component: MarketDataIngestion, meta: { screenId: 'SCR-016', templateId: 'T08' } },
|
||||
{ path: '/ops/market-data-history', component: IngestionStatus, meta: { screenId: 'SCR-017', templateId: 'T08' } },
|
||||
{ path: '/portfolio/risk', component: RiskDashboard, meta: { screenId: 'SCR-018', templateId: 'T07' } },
|
||||
{ path: '/portfolio/rebalance', component: RebalanceForm, meta: { screenId: 'SCR-019', templateId: 'T03' } },
|
||||
{ path: '/internal/ui-standard', component: UiStandardPage, meta: { screenId: 'SCR-DEV-001', templateId: 'T01', internalOnly: true } }
|
||||
{ path: '/research/sell-decision', component: () => import('../features/sell-decision/pages/SellDecisionPage.vue'), meta: { screenId: 'SCR-002', templateId: 'T02' } },
|
||||
{ path: '/ops/data-quality', component: () => import('../features/data-quality/pages/DataQualityPage.vue'), meta: { screenId: 'SCR-013', templateId: 'T08' } },
|
||||
{ path: '/ops/model-operations', component: () => import('../features/model-operations/pages/ModelOperationsPage.vue'), meta: { screenId: 'SCR-015', templateId: 'T10' } },
|
||||
{ path: '/ops/market-data-ingestion', component: () => import('../features/marketData/pages/MarketDataIngestion.vue'), meta: { screenId: 'SCR-016', templateId: 'T08' } },
|
||||
{ path: '/ops/market-data-history', component: () => import('../features/marketData/pages/IngestionStatus.vue'), meta: { screenId: 'SCR-017', templateId: 'T08' } },
|
||||
{ path: '/portfolio/risk', component: () => import('../features/portfolio/pages/RiskDashboard.vue'), meta: { screenId: 'SCR-018', templateId: 'T07' } },
|
||||
{ path: '/portfolio/rebalance', component: () => import('../features/portfolio/pages/RebalanceForm.vue'), meta: { screenId: 'SCR-019', templateId: 'T03' } },
|
||||
{ path: '/internal/ui-standard', component: () => import('../features/ui-standard/pages/UiStandardPage.vue'), meta: { screenId: 'SCR-DEV-001', templateId: 'T01', internalOnly: true } },
|
||||
{ path: '/internal/wbs', component: () => import('../features/wbs/pages/WbsWorkspacePage.vue'), meta: { screenId: 'SCR-DEV-002', templateId: 'T01', internalOnly: true } }
|
||||
]
|
||||
});
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import SellDecisionPage from '../features/sell-decision/pages/SellDecisionPage.vue'
|
||||
import DataQualityPage from '../features/data-quality/pages/DataQualityPage.vue'
|
||||
import ModelOperationsPage from '../features/model-operations/pages/ModelOperationsPage.vue'
|
||||
import UiStandardPage from '../features/ui-standard/pages/UiStandardPage.vue'
|
||||
import WbsWorkspacePage from '../features/wbs/pages/WbsWorkspacePage.vue'
|
||||
import RiskDashboard from '../features/portfolio/pages/RiskDashboard.vue'
|
||||
import RebalanceForm from '../features/portfolio/pages/RebalanceForm.vue'
|
||||
import MarketDataIngestion from '../features/marketData/pages/MarketDataIngestion.vue'
|
||||
import IngestionStatus from '../features/marketData/pages/IngestionStatus.vue'
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{ path: '/', redirect: '/research/sell-decision' },
|
||||
{ path: '/research/sell-decision', component: SellDecisionPage, meta: { screenId: 'SCR-002', templateId: 'T02' } },
|
||||
{ path: '/ops/data-quality', component: DataQualityPage, meta: { screenId: 'SCR-013', templateId: 'T08' } },
|
||||
{ path: '/ops/model-operations', component: ModelOperationsPage, meta: { screenId: 'SCR-015', templateId: 'T10' } },
|
||||
{ path: '/ops/market-data-ingestion', component: MarketDataIngestion, meta: { screenId: 'SCR-016', templateId: 'T08' } },
|
||||
{ path: '/ops/market-data-history', component: IngestionStatus, meta: { screenId: 'SCR-017', templateId: 'T08' } },
|
||||
{ path: '/portfolio/risk', component: RiskDashboard, meta: { screenId: 'SCR-018', templateId: 'T07' } },
|
||||
{ path: '/portfolio/rebalance', component: RebalanceForm, meta: { screenId: 'SCR-019', templateId: 'T03' } },
|
||||
{ path: '/internal/ui-standard', component: UiStandardPage, meta: { screenId: 'SCR-DEV-001', templateId: 'T01', internalOnly: true } },
|
||||
{ path: '/internal/wbs', component: WbsWorkspacePage, meta: { screenId: 'SCR-DEV-002', templateId: 'T01', internalOnly: true } }
|
||||
{ path: '/research/sell-decision', component: () => import('../features/sell-decision/pages/SellDecisionPage.vue'), meta: { screenId: 'SCR-002', templateId: 'T02' } },
|
||||
{ path: '/ops/data-quality', component: () => import('../features/data-quality/pages/DataQualityPage.vue'), meta: { screenId: 'SCR-013', templateId: 'T08' } },
|
||||
{ path: '/ops/model-operations', component: () => import('../features/model-operations/pages/ModelOperationsPage.vue'), meta: { screenId: 'SCR-015', templateId: 'T10' } },
|
||||
{ path: '/ops/market-data-ingestion', component: () => import('../features/marketData/pages/MarketDataIngestion.vue'), meta: { screenId: 'SCR-016', templateId: 'T08' } },
|
||||
{ path: '/ops/market-data-history', component: () => import('../features/marketData/pages/IngestionStatus.vue'), meta: { screenId: 'SCR-017', templateId: 'T08' } },
|
||||
{ path: '/portfolio/risk', component: () => import('../features/portfolio/pages/RiskDashboard.vue'), meta: { screenId: 'SCR-018', templateId: 'T07' } },
|
||||
{ path: '/portfolio/rebalance', component: () => import('../features/portfolio/pages/RebalanceForm.vue'), meta: { screenId: 'SCR-019', templateId: 'T03' } },
|
||||
{ path: '/internal/ui-standard', component: () => import('../features/ui-standard/pages/UiStandardPage.vue'), meta: { screenId: 'SCR-DEV-001', templateId: 'T01', internalOnly: true } },
|
||||
{ path: '/internal/wbs', component: () => import('../features/wbs/pages/WbsWorkspacePage.vue'), meta: { screenId: 'SCR-DEV-002', templateId: 'T01', internalOnly: true } }
|
||||
]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user