Architecture Overview¶
Status: Audit v0.1 (2026-05-09). Every claim cites a source.
1. At a glance¶
| Property | Value | Source |
|---|---|---|
| Framework | Nuxt 2.18.1 (Vue 2.7.16) | package.json:67 (nuxt), :84 (vue) |
| Rendering mode | SPA — ssr: false |
nuxt.config.js:2 |
| Module system | CommonJS-leaning ESM, Babel-compiled | nuxt.config.js:325-330 (Babel config); no "type": "module" in package.json |
| Language | JavaScript only | No tsconfig.json or *.ts files outside node_modules |
| Bundler | Webpack 4 (bundled with Nuxt 2) | package.json:113 (webpack: ^4.46.0) |
| Type checking | None | No TS, no vue-tsc, no Flow |
| Package manager | Yarn 1 (classic) | yarn.lock header: yarn lockfile v1 |
| Node version target | 20.x (assumed) | Dockerfile:4 (node:20.18.3-slim); no engines field in package.json |
| State management | Vuex 3 (non-strict) | package.json:104; store/index.js:1 (export const strict = false) |
| Routing | Nuxt 2 file-based routing | pages/ directory; nuxt.config.js:227-243 |
| HTTP client | @nuxtjs/axios |
nuxt.config.js:250; package.json:? (transitive via @nuxtjs/auth) |
| Auth | @nuxtjs/auth v4 (local strategy) |
package.json:24; nuxt.config.js:285-311 |
The web client is a single-page application — the framework is being used for routing, code-splitting, and dev tooling, but no server-side rendering occurs at runtime. This is consequential for SEO, observability, and how environment variables are exposed (see seo-and-metadata.md, security.md).
For the breadth of the product surface this SPA serves, see ../19-feature-inventory.md — the feature → page → component → backend-endpoint map. Headline: 14 major surface areas (auth + onboarding, account/team, content planner, editor in 6 variants, content creation incl. AI, library/templates/media, brand kit + AI brand assets, social-account management, dashboards in ~10 sub-sections, billing, network features, chat, settings, public/marketing). This breadth is the core product value; it is also the multiplier on every operational-maturity gap (see enterprise-readiness.md).
A second, independent React 18 / MobX-State-Tree application lives at polotno-editor/. It is built with Parcel into polotno-bundle.{js,css} at the repo root, then imported by editor pages in the main Nuxt app as a regular ES module. See ./build-and-deploy.md and the existing project doc ../06-polotno-integration.md for the integration pattern.
2. Source structure¶
Top-level directories under repo root:
api/ Nuxt server middleware (Express handlers — NOT the backend API)
assets/ CSS/SCSS/images processed by webpack
components/ Vue components (auto-imported by Nuxt; 161 .vue files)
constants/ Provider/plan/path constants
data/ Sitemap data
helpers/ Pure utility functions (helper.js, circleNetworkHelper.js, mixins/, models/)
layouts/ Page layouts (default, error, fourOhOne, onBoard, someliNetwork, specialOffer)
middleware/ Nuxt router middleware (axios.js, redirect.js, appendUrl.js, etc.)
mixins/ Vue mixins (only onboardingNavigation.js)
pages/ File-based routes (84 .vue files)
plugins/ Nuxt plugins (19 files; 14 wired in nuxt.config.js)
services/ Service-layer modules (only circleNetworkService.js)
static/ Static assets served verbatim
store/ Vuex modules (root + 7 feature modules + api.js + onboarding.js + modules/sitemap.js)
polotno-editor/ Independent React 18 + MobX-State-Tree app (separate package.json, separate build)
Data sources for the directory listing: find . -maxdepth 1 -type d; per-folder file counts via find <dir> -type f | wc -l.
The structure is organized by Nuxt convention (pages/, components/, layouts/, middleware/, plugins/, store/) rather than by feature. There are no feature folders (e.g., no features/billing/ or domains/posts/); related pages, components, and store modules are co-located only by Nuxt naming convention, not by domain.
3. Code volume¶
| Measurement | Value | Source |
|---|---|---|
| Total Vue + JS LOC (excluding node_modules, .nuxt, dist, polotno-editor) | 159,530 lines | find pages components layouts plugins helpers store middleware mixins constants api -type f \( -name '*.vue' -o -name '*.js' \) \| xargs wc -l |
Vue (.vue) LOC alone |
154,779 lines | Same command, -name '*.vue' only |
| Number of pages | 84 | find pages -type f -name '*.vue' \| wc -l |
| Number of components | 161 | find components -type f \| wc -l |
| Number of layouts | 6 | ls layouts/ |
| Number of plugins (files) | 19 | ls plugins/ |
Number of plugins wired in nuxt.config.js |
14 | nuxt.config.js:179-225 (hotjar2 is commented out and excluded) |
3.1 Largest files (top 5 of each)¶
Pages (find pages -name '*.vue' -exec wc -l {} \; | sort -rn | head -5):
| Lines | File |
|---|---|
| 10,795 | pages/userSetting/index.vue |
| 9,781 | pages/_accid/userSetting/index.vue |
| 5,506 | pages/_accid/my_library/index.vue |
| 4,957 | pages/_accid/contentplanner/index.vue |
| 4,069 | pages/contentplanner/index.vue |
Components (find components -name '*.vue' -exec wc -l {} \; | sort -rn | head -5):
| Lines | File |
|---|---|
| 3,678 | components/editPostModal.vue |
| 3,328 | components/Cards/ContentCard.vue |
| 2,512 | components/Chatbot.vue |
| 2,311 | components/ImageManager.vue |
| 2,200 | components/createContentModal.vue |
Finding [WC-ARCH-1] (Severity: High): Multiple files exceed 4,000 lines and one (pages/userSetting/index.vue) is 10,795 lines. Files of this size are well past any reasonable maintainability threshold; refactoring or extracting child components is a high-value Phase 0/1 effort. The total of files >2,000 lines is 9.
Finding [WC-ARCH-2] (Severity: High): pages/userSetting/index.vue (10,795 lines, legacy non-_accid route) and pages/_accid/userSetting/index.vue (9,781 lines, current account-scoped route) are near-duplicate files. The same pattern repeats for contentplanner, my_library, billing, topics, and Organisationprofile. This is a massive code-duplication finding: every user-facing change touches both copies. Estimated duplicated LOC: ~30,000+. Cleanup is a Phase 1 effort.
4. Code style and quality enforcement¶
| Tool | Wired? | Source |
|---|---|---|
| Prettier | Yes, via yarn format (manual) |
package.json:12 |
| Prettier on save in editor | Disabled by .vscode/settings.json |
.vscode/settings.json (sets formatOnSave: false) |
| ESLint | Configured but not wired to a script | package.json:128-141 (eslintConfig); no lint script |
| ESLint config severity | Lowest | extends: ["plugin:vue/essential", "eslint:recommended"], rules: {} |
| eslint-plugin-jsx-a11y | Not installed | Absent from package.json |
| TypeScript | Not used | No TS files, no tsconfig.json |
.editorconfig |
Yes | .editorconfig (2-space, LF, UTF-8) |
.prettierrc |
Yes | .prettierrc (single quotes, no semis, no trailing comma, 2-space) |
| Pre-commit hooks | None functionally | .git/hooks/pre-commit exists from yorkie but package.json has no pre-commit hook script, so it no-ops |
| CI lint gate | None | .github/workflows/dev_app.yml runs yarn build, no lint step |
| Type-check gate | None (no TS) | — |
Finding [WC-ARCH-3] (Severity: Medium): Quality enforcement is minimal. ESLint exists in config but is not run anywhere — no script, no pre-commit hook, no CI gate. Effective code-quality enforcement is manual peer review only. The CMMI rating for "Architecture & Modularity" pillar will reflect this.
5. Dead code and inconsistencies¶
| Item | Source |
|---|---|
pages/posteditor/_id/index.vue, pages/cuseditor/_id/index.vue, pages/editor/_id/, pages/carouseleditor1/_id/, pages/carouseleditor/_id/, pages/carouselcpeditor/_id/ are legacy non-_accid routes; the live app uses _accid-prefixed equivalents. The appendUrl middleware silently rewrites unprefixed URLs to inject accountId. |
middleware/appendUrl.js:6-36; existing doc ../04-routing.md |
api/index.js contains ~80 lines of commented-out Twitter/Passport scaffolding around 18 lines of live code |
api/index.js:1-105 |
plugins/fullstory.js contains a complete FullStory init script with hardcoded org ID o-1C5EVB-na1 but is not registered in nuxt.config.js → plugins |
plugins/fullstory.js; nuxt.config.js:179-225 |
plugins/hotjar.js, plugins/hotjar2.js exist but are not registered |
Same |
plugins/sales_iq.js, plugins/snowfall.js exist; snowfall is registered, sales_iq is not |
Same |
middleware/head.js contains substantial role-based access control logic but is not wired in nuxt.config.js or any per-page middleware |
Existing doc ../03-auth-and-sessions.md; verified via grep -rn "['\"]head['\"]" pages nuxt.config.js \| grep -v "<head>\|head:" |
middleware/socialLogin.js is not referenced anywhere |
Same approach |
Both yarn.lock (11,423 lines) and package-lock.json (22,271 lines) exist at the repo root — npm has been run alongside yarn at some point |
wc -l yarn.lock package-lock.json |
node_modules.backup/ directories exist at root and inside polotno-editor/ |
ls -la \| grep node_modules |
.env~ (vim swap-style backup) exists at the repo root and is not in .gitignore |
ls .env*; grep "\.env~" .gitignore (no match) |
'admin' layout is referenced by one page but layouts/admin.vue does not exist |
Existing doc ../04-routing.md |
Finding [WC-ARCH-4] (Severity: Medium): The repo carries substantial dead/unwired code — multiple unwired plugins, an unwired-but-implemented head middleware, ~80 lines of commented-out Express code in api/index.js, a near-complete legacy route tree under pages/<route>/_id/ that the rewriter middleware short-circuits, and dual lockfiles. Cleanup is a Phase 0 / Phase 1 hygiene task.
Finding [WC-ARCH-5] (Severity: Low/Medium): package-lock.json and yarn.lock coexist. This creates dependency drift if both are honored. Convention in this repo is yarn (per Jenkinsfile, 01-local-setup.md). Action: delete package-lock.json, add it to .gitignore. Confirm with team that no tool depends on it.
6. Build process¶
yarn build runs nuxt build, which produces:
.nuxt/ Nuxt build cache (62 MB)
dist/ Static-site output if `yarn generate` is run
(currently 78 MB; contains a snapshot from `nuxt generate`)
The actual production runtime is yarn start → nuxt start which serves the SPA from .nuxt/dist/. The dist/ directory is the artifact of a prior nuxt generate run; it is not what the production container serves.
The bundle is split across 254 JS chunks in dist/_nuxt/ totaling 21.8 MB raw. Top 5 chunks by size:
| Raw bytes | Gzipped | File | Likely contents |
|---|---|---|---|
| 4,986,869 | 1,368,730 | db8106d.js |
Polotno-related (konva, react-dom) |
| 2,088,345 | 498,388 | 50ea2d1.js |
konva, moment |
| 1,437,102 | 290,458 | cc9063d.js |
filepond |
| 923,358 | 210,595 | 4e81c68.js |
filepond, @fullcalendar/* |
| 830,007 | 401,030 | 9a92ef4.js |
(unknown — investigate in performance.md) |
See ./performance.md for full bundle analysis.
7. Embedded React app¶
The polotno-editor/ sub-project is an entirely separate React 18 application built with Parcel:
| Property | Value | Source |
|---|---|---|
| Framework | React 18 + MobX 6 + MobX-State-Tree 6 | polotno-editor/package.json |
| Build tool | Parcel 2 | Same |
| Output | ../polotno-bundle.js (216 KB / 221,172 bytes), ../polotno-bundle.css (~9.5 KB / 9,697 bytes) |
polotno-editor/package.json:6 ("main": "../polotno-bundle.js"); stat -c%s polotno-bundle.* |
| License key | Hardcoded in polotno-editor/index.js:14 (createStore({ key: 'FXZvloSJvAe09-bdR9iC' })) |
polotno-editor/index.js:14 |
| Persistence | localStorage keyed by pathname + hash, debounced 2s |
polotno-editor/index.js:23-31 |
| Dependencies | 26 production, 2 dev | jq '.dependencies \| length' polotno-editor/package.json |
This is a second component for audit purposes but is treated here as a sub-component of the web client. Its security posture (large React bundle, AI provider keys baked at build time) is covered in ./security.md and ./performance.md.
8. Architectural concerns (summary)¶
The detailed maturity rating is in enterprise-readiness.md. Headlines:
- Stack age. Vue 2 reached EOL on 2023-12-31 (per Vue.js core team announcement). Nuxt 2 reached EOL on 2024-06-30. Both are running unsupported in 2026. This is the single most consequential finding — it caps the upper bound on every other improvement and creates ongoing security exposure (see
security.md). - No type safety. 159K lines of JS with no TypeScript and no runtime validation library (no Zod, Yup, etc., observed in deps). Refactor risk is high.
- Massive single-file complexity. Five files >4K lines, two files >9K lines.
- Zero test coverage. No project-owned test files (the 164
*.test.*matches are all insidenode_modules/of dependencies). No CI test gate. See./testing.md. - Dual lockfiles. Drift risk; convention is yarn but npm has been run.
- No type/lint CI gates. ESLint configured but never executed; no pre-commit hooks.
These are flagged as findings in enterprise-readiness.md and roadmapped per WEB_CLIENT_AUDIT_GUIDE.md §6.3.