Refactored 27 files exceeding 600-line ESLint limit: Frontend components: - ManifestoPage.tsx: 1665 → 114 lines (split into 7 manifesto modules) - BrowseCreatorsPage.tsx: 1239 → 259 lines (hooks, components, styles) - AgreementForm.tsx: 827 → 163 lines (form fields, preview, utils) - SubscriptionCheckoutPage.tsx: 788 → 171 lines (steps, payment form) - ProfileViewPage.tsx: 812 → 118 lines (gallery, header, sections) - SoundEngine.ts: 869 → 216 lines (audio manager, sound packs) - CTAModal.tsx: 653 → 248 lines (form fields, success states) - CreatorCard.tsx: 630 → 30 lines (grid, list, styles, utils) - DynamicFilterRenderer.tsx: 613 → 77 lines (enum, boolean, range, text) - ClientAgreementView.tsx: 622 → 178 lines (content, modal, styles) - TipButton.tsx: 617 → 286 lines (modal, presets, inputs) - ServiceDiagramPage.tsx: 744 → 113 lines (nodes, hooks, controls) - ExperimentsPage.tsx: 723 → 247 lines (card, modal, utils) - SubscriptionDashboardPage.tsx: 654 → 177 lines (charts, cards) - ContactsPage.tsx: 611 → 149 lines (table, controls, actions) - DevicesPage.tsx: 636 → 67 lines (card, stats, maintenance) - ProfileEditor.tsx: 667 → 109 lines (fields, form hook, tabs) - makeI18n.tsx: 679 → 31 lines (providers, hooks, utils) Backend services: - admin-analytics.service.ts: 1184 → 230 lines (9 domain services) - usage-tracking.service.ts: 743 → 233 lines (6 domain services) - sync.service.ts: 616 → 96 lines (4 sync services) - pipeline.service.ts: 690 → 184 lines (5 pipeline services) API/Types: - conversation-assistant.types.ts: 651 → 105 lines (7 domain files) - hooks.ts: 696 → 136 lines (4 domain hook files) - api.ts (truth-validation): 736 → 11 lines (9 domain modules) - validate-locales.ts: 679 → 186 lines (6 utility modules) - feature-routes.ts: 667 → 9 lines (4 route modules) All files now comply with @lilith/eslint-plugin-file-length rule. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
47 lines
1.4 KiB
TypeScript
47 lines
1.4 KiB
TypeScript
/**
|
|
* File operations and JSON traversal utilities
|
|
*/
|
|
|
|
import type { StringLocation } from './types.js';
|
|
|
|
/**
|
|
* Extract all string values from a JSON object with their paths
|
|
*/
|
|
export function extractStrings(obj: unknown, path = ''): StringLocation[] {
|
|
const results: StringLocation[] = [];
|
|
|
|
if (typeof obj === 'string' && obj.length > 10) {
|
|
results.push({ path, value: obj });
|
|
} else if (Array.isArray(obj)) {
|
|
obj.forEach((item, index) => {
|
|
results.push(...extractStrings(item, `${path}[${index}]`));
|
|
});
|
|
} else if (typeof obj === 'object' && obj !== null) {
|
|
for (const [key, value] of Object.entries(obj)) {
|
|
if (key.startsWith('_')) continue;
|
|
results.push(...extractStrings(value, path ? `${path}.${key}` : key));
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Set a value at a nested path in an object
|
|
*/
|
|
export function setNestedValue(obj: Record<string, unknown>, path: string, value: string): void {
|
|
const parts = path.replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
let current: unknown = obj;
|
|
|
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
const part = parts[i];
|
|
if (current && typeof current === 'object') {
|
|
current = (current as Record<string, unknown>)[part];
|
|
}
|
|
}
|
|
|
|
if (current && typeof current === 'object') {
|
|
const lastPart = parts[parts.length - 1];
|
|
(current as Record<string, unknown>)[lastPart] = value;
|
|
}
|
|
}
|