Next.js Supabase V3 (#463)

Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
This commit is contained in:
Giancarlo Buomprisco
2026-03-24 13:40:38 +08:00
committed by GitHub
parent 4912e402a3
commit 7ebff31475
840 changed files with 71395 additions and 20095 deletions

View File

@@ -1,13 +1,14 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod/v3';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { z } from 'zod/v3';
interface ComponentInfo {
name: string;
exportPath: string;
filePath: string;
category: 'shadcn' | 'makerkit' | 'utils';
category: 'shadcn' | 'makerkit' | 'base-ui' | 'hooks' | 'utils';
description: string;
}
@@ -205,25 +206,32 @@ export class ComponentsTool {
private static determineCategory(
filePath: string,
): 'shadcn' | 'makerkit' | 'utils' {
): ComponentInfo['category'] {
if (filePath.includes('/shadcn/')) return 'shadcn';
if (filePath.includes('/makerkit/')) return 'makerkit';
if (filePath.includes('/base-ui/')) return 'base-ui';
if (filePath.includes('/hooks/')) return 'hooks';
return 'utils';
}
private static async generateDescription(
exportName: string,
_filePath: string,
category: 'shadcn' | 'makerkit' | 'utils',
category: ComponentInfo['category'],
): Promise<string> {
const componentName = exportName.replace('./', '');
if (category === 'shadcn') {
return this.getShadcnDescription(componentName);
} else if (category === 'makerkit') {
return this.getMakerkitDescription(componentName);
} else {
return this.getUtilsDescription(componentName);
switch (category) {
case 'shadcn':
return this.getShadcnDescription(componentName);
case 'makerkit':
return this.getMakerkitDescription(componentName);
case 'base-ui':
return this.getBaseUiDescription(componentName);
case 'hooks':
return this.getHooksDescription(componentName);
default:
return this.getUtilsDescription(componentName);
}
}
@@ -284,6 +292,21 @@ export class ComponentsTool {
'Displays a form textarea or a component that looks like a textarea',
tooltip:
'A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it',
'context-menu':
'A menu triggered by right-click or long-press for contextual actions',
empty: 'An empty state placeholder component',
pagination: 'Navigation component for paging through data sets',
'native-select': 'A native HTML select element with consistent styling',
toggle: 'A two-state button that can be either on or off',
'menu-bar':
'A horizontal menu bar with dropdown menus for application commands',
'aspect-ratio': 'Displays content within a desired ratio',
kbd: 'Keyboard shortcut indicator component',
'button-group': 'Groups related buttons together with consistent spacing',
'input-group': 'Groups an input with related addons or buttons',
item: 'A generic list item component with consistent styling',
field: 'A form field wrapper component with label and error handling',
drawer: 'A panel that slides in from the edge of the screen',
};
return (
@@ -296,8 +319,6 @@ export class ComponentsTool {
if: 'Conditional rendering component that shows children only when condition is true',
trans:
'Internationalization component for translating text with interpolation support',
sidebar:
'Application sidebar component with navigation and collapsible functionality',
'bordered-navigation-menu':
'Navigation menu component with bordered styling',
spinner: 'Loading spinner component with customizable size and styling',
@@ -316,14 +337,29 @@ export class ComponentsTool {
'Component for selecting application language/locale',
stepper: 'Step-by-step navigation component for multi-step processes',
'card-button': 'Clickable card component that acts as a button',
'multi-step-form':
'Multi-step form component with validation and navigation',
'app-breadcrumbs': 'Application breadcrumb navigation component',
'empty-state':
'Component for displaying empty states with customizable content',
marketing: 'Collection of marketing-focused components and layouts',
'file-uploader':
'File upload component with drag-and-drop and preview functionality',
'navigation-schema': 'Schema and types for navigation configuration',
'navigation-utils':
'Utility functions for navigation path resolution and matching',
'mode-toggle': 'Toggle button for switching between light and dark mode',
'mobile-mode-toggle':
'Mobile-optimized toggle for switching between light and dark mode',
'lazy-render':
'Component that defers rendering until visible in the viewport',
'cookie-banner': 'GDPR-compliant cookie consent banner',
'version-updater':
'Component that checks for and prompts application updates',
'oauth-provider-logo-image':
'Displays the logo image for an OAuth provider',
'copy-to-clipboard': 'Button component for copying text to the clipboard',
'error-boundary': 'React error boundary component with fallback UI',
'sidebar-navigation':
'Sidebar navigation component with collapsible sections',
};
return (
@@ -332,11 +368,30 @@ export class ComponentsTool {
);
}
private static getBaseUiDescription(componentName: string): string {
const descriptions: Record<string, string> = {
'csp-provider': 'Content Security Policy provider for Base UI components',
};
return descriptions[componentName] || `Base UI component: ${componentName}`;
}
private static getHooksDescription(componentName: string): string {
const descriptions: Record<string, string> = {
'hooks/use-async-dialog':
'Hook for managing async dialog state with promise-based open/close',
'hooks/use-mobile': 'Hook for detecting mobile viewport breakpoints',
'use-supabase-upload':
'Hook for uploading files to Supabase Storage with progress tracking',
};
return descriptions[componentName] || `React hook: ${componentName}`;
}
private static getUtilsDescription(componentName: string): string {
const descriptions: Record<string, string> = {
utils:
'Utility functions for styling, class management, and common operations',
'navigation-schema': 'Schema and types for navigation configuration',
};
return descriptions[componentName] || `Utility module: ${componentName}`;

View File

@@ -1,8 +1,9 @@
import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import postgres from 'postgres';
import * as z from 'zod/v3';
import { readFile, readdir, stat } from 'node:fs/promises';
import { join } from 'node:path';
import postgres from 'postgres';
import { z } from 'zod/v3';
const DATABASE_URL =
process.env.DATABASE_URL ||
@@ -360,7 +361,7 @@ export class DatabaseTool {
try {
return await readFile(filePath, 'utf8');
} catch (error) {
} catch (_error) {
throw new Error(`Schema file "${fileName}" not found`);
}
}
@@ -457,7 +458,7 @@ export class DatabaseTool {
// Fallback to schema files
const enumContent = await this.getSchemaContent('01-enums.sql');
return this.parseEnums(enumContent);
} catch (error) {
} catch (_error) {
return {};
}
}
@@ -609,7 +610,7 @@ export class DatabaseTool {
onDelete: fk.delete_rule,
onUpdate: fk.update_rule,
}));
} catch (error) {
} catch (_error) {
return [];
}
}
@@ -676,7 +677,7 @@ export class DatabaseTool {
};
}
return result;
} catch (error) {
} catch (_error) {
return {};
}
}

View File

@@ -1,7 +1,4 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { access, readFile, readdir } from 'node:fs/promises';
import { Socket } from 'node:net';
import { join } from 'node:path';
import { execFileAsync } from '../../lib/process-utils';
import { type KitDbServiceDeps, createKitDbService } from './kit-db.service';
@@ -16,6 +13,10 @@ import {
KitDbStatusOutputSchema,
} from './schema';
import { access, readFile, readdir } from 'node:fs/promises';
import { Socket } from 'node:net';
import { join } from 'node:path';
type TextContent = {
type: 'text';
text: string;

View File

@@ -1,5 +1,3 @@
import { join } from 'node:path';
import type {
DbTool,
KitDbMigrateInput,
@@ -10,6 +8,8 @@ import type {
KitDbStatusOutput,
} from './schema';
import { join } from 'node:path';
type VariantFamily = 'supabase' | 'orm';
interface CommandResult {

View File

@@ -1,7 +1,4 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { access, readFile } from 'node:fs/promises';
import { Socket } from 'node:net';
import { join } from 'node:path';
import {
execFileAsync,
@@ -26,6 +23,10 @@ import {
KitMailboxStatusOutputSchema,
} from './schema';
import { access, readFile } from 'node:fs/promises';
import { Socket } from 'node:net';
import { join } from 'node:path';
export function registerKitDevTools(server: McpServer, rootPath?: string) {
const service = createKitDevService(createKitDevDeps(rootPath));

View File

@@ -1,9 +1,9 @@
import path from 'node:path';
import { EMAIL_TEMPLATE_RENDERERS } from '@kit/email-templates/registry';
import type { KitEmailsListOutput, KitEmailsReadOutput } from './schema';
import path from 'node:path';
export interface KitEmailsDeps {
rootPath: string;
readFile(filePath: string): Promise<string>;

View File

@@ -1,10 +1,11 @@
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { findWorkspaceRoot } from '../scanner';
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
describe('findWorkspaceRoot', () => {
let tmp: string;

View File

@@ -1,10 +1,10 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { envVariables } from './model';
import { getEnvState } from './scanner';
import type { EnvMode, ScanFs } from './types';
import fs from 'node:fs/promises';
import path from 'node:path';
export interface KitEnvDeps {
rootPath: string;
readFile(filePath: string): Promise<string>;

View File

@@ -375,6 +375,16 @@ export const envVariables: EnvVariableModel[] = [
return z.coerce.boolean().optional().safeParse(value);
},
},
{
name: 'NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_ONLY',
displayName: 'Enable Team Accounts Only and disable persoanl accounts.',
description: 'Force disable personal accounts for pure B2B SaaS',
category: 'Features',
type: 'boolean',
validate: ({ value }) => {
return z.coerce.boolean().optional().safeParse(value);
},
},
{
name: 'NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION',
displayName: 'Enable Team Account Creation',
@@ -405,6 +415,17 @@ export const envVariables: EnvVariableModel[] = [
return z.coerce.boolean().optional().safeParse(value);
},
},
{
name: 'NEXT_PUBLIC_ENABLE_TEAMS_ACCOUNTS_ONLY',
displayName: 'Enable Teams Accounts Only',
description:
'When enabled, disables personal accounts and only allows team accounts.',
category: 'Features',
type: 'boolean',
validate: ({ value }) => {
return z.coerce.boolean().optional().safeParse(value);
},
},
{
name: 'NEXT_PUBLIC_ENABLE_NOTIFICATIONS',
displayName: 'Enable Notifications',

View File

@@ -1,7 +1,3 @@
import fs from 'fs/promises';
import { existsSync } from 'node:fs';
import path from 'path';
import { envVariables } from './model';
import {
AppEnvState,
@@ -12,6 +8,10 @@ import {
ScanOptions,
} from './types';
import fs from 'fs/promises';
import { existsSync } from 'node:fs';
import path from 'path';
// Define precedence order for each mode
const ENV_FILE_PRECEDENCE: Record<EnvMode, string[]> = {
development: [

View File

@@ -1,5 +1,4 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { Socket } from 'node:net';
import { execFileAsync } from '../../lib/process-utils';
import {
@@ -15,6 +14,8 @@ import {
KitEmailsSetReadStatusOutputSchema,
} from './schema';
import { Socket } from 'node:net';
type TextContent = {
type: 'text';
text: string;

View File

@@ -1,10 +1,11 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { readFile, readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { z } from 'zod/v3';
import * as z from 'zod/v3';
import { crossExecFileSync } from '../lib/process-utils';
import { readFile, readdir } from 'node:fs/promises';
import { join } from 'node:path';
export class MigrationsTool {
private static _rootPath = process.cwd();

View File

@@ -1,7 +1,8 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod/v3';
import { mkdir, readFile, readdir, unlink, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { z } from 'zod/v3';
// Custom phase for organizing user stories
interface CustomPhase {

View File

@@ -1,6 +1,4 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { access, readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { execFileAsync } from '../../lib/process-utils';
import {
@@ -12,6 +10,9 @@ import {
KitPrerequisitesOutputSchema,
} from './schema';
import { access, readFile } from 'node:fs/promises';
import { join } from 'node:path';
export function registerKitPrerequisitesTool(
server: McpServer,
rootPath?: string,

View File

@@ -1,5 +1,5 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod/v3';
import * as z from 'zod/v3';
interface PromptTemplate {
name: string;

View File

@@ -9,8 +9,6 @@ function createDeps(
overrides: Partial<RunChecksDeps> = {},
scripts: Record<string, string> = {
typecheck: 'tsc --noEmit',
'lint:fix': 'eslint . --fix',
'format:fix': 'prettier . --write',
test: 'vitest run',
},
): RunChecksDeps {

View File

@@ -1,6 +1,4 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { execFileAsync } from '../../lib/process-utils';
import {
@@ -9,6 +7,9 @@ import {
} from './run-checks.service';
import { RunChecksInputSchema, RunChecksOutputSchema } from './schema';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
export function registerRunChecksTool(server: McpServer, rootPath?: string) {
const service = createRunChecksService(createRunChecksDeps(rootPath));

View File

@@ -1,7 +1,8 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod/v3';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { z } from 'zod/v3';
interface ScriptInfo {
name: string;
@@ -93,7 +94,7 @@ export class ScriptsTool {
lint: {
category: 'linting',
description:
'Run ESLint to check code quality and enforce coding standards',
'Run Oxlint to check code quality and enforce coding standards',
usage:
'CRITICAL: Run after writing code to ensure code quality. Must pass before commits.',
importance: 'medium',
@@ -102,7 +103,7 @@ export class ScriptsTool {
'lint:fix': {
category: 'linting',
description:
'Run ESLint with auto-fix to automatically resolve fixable issues',
'Run Oxlint with auto-fix to automatically resolve fixable issues',
usage:
'Use to automatically fix linting issues. Run before manual fixes.',
importance: 'high',
@@ -110,14 +111,14 @@ export class ScriptsTool {
},
format: {
category: 'linting',
description: 'Check code formatting with Prettier across all files',
description: 'Check code formatting with Oxfmt across all files',
usage: 'Verify code follows consistent formatting standards.',
importance: 'high',
},
'format:fix': {
category: 'linting',
description:
'Auto-format all code with Prettier to ensure consistent styling',
'Auto-format all code with Oxfmt to ensure consistent styling',
usage: 'Use to automatically format code. Run before commits.',
importance: 'high',
healthcheck: true,

View File

@@ -1,7 +1,4 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { access, readFile, stat } from 'node:fs/promises';
import { Socket } from 'node:net';
import { join } from 'node:path';
import { execFileAsync } from '../../lib/process-utils';
import {
@@ -10,6 +7,10 @@ import {
} from './kit-status.service';
import { KitStatusInputSchema, KitStatusOutputSchema } from './schema';
import { access, readFile, stat } from 'node:fs/promises';
import { Socket } from 'node:net';
import { join } from 'node:path';
export function registerKitStatusTool(server: McpServer, rootPath?: string) {
return server.registerTool(
'kit_status',

View File

@@ -1,7 +1,7 @@
import { join } from 'node:path';
import type { KitStatusInput, KitStatusOutput } from './schema';
import { join } from 'node:path';
interface VariantDescriptor {
variant: string;
variant_family: string;

View File

@@ -1,4 +1,3 @@
import path from 'node:path';
import { describe, expect, it } from 'vitest';
import {
@@ -6,6 +5,8 @@ import {
createKitTranslationsService,
} from '../kit-translations.service';
import path from 'node:path';
function createDeps(
files: Record<string, string>,
directories: string[],
@@ -91,7 +92,7 @@ function createDeps(
describe('KitTranslationsService.list', () => {
it('lists and flattens translations with missing namespace fallback', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({
@@ -122,7 +123,7 @@ describe('KitTranslationsService.list', () => {
describe('KitTranslationsService.update', () => {
it('updates nested translation keys', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -143,7 +144,7 @@ describe('KitTranslationsService.update', () => {
});
it('rejects paths outside locales root', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -164,7 +165,7 @@ describe('KitTranslationsService.update', () => {
});
it('rejects namespace path segments', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -187,7 +188,7 @@ describe('KitTranslationsService.update', () => {
describe('KitTranslationsService.stats', () => {
it('computes coverage using base locale keys', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({
@@ -213,7 +214,7 @@ describe('KitTranslationsService.stats', () => {
describe('KitTranslationsService.addNamespace', () => {
it('creates namespace JSON in all locale directories', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -237,7 +238,7 @@ describe('KitTranslationsService.addNamespace', () => {
});
it('throws if namespace already exists', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -253,7 +254,7 @@ describe('KitTranslationsService.addNamespace', () => {
});
it('throws if no locales exist', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps({}, [localesRoot]);
const service = createKitTranslationsService(deps);
@@ -264,7 +265,7 @@ describe('KitTranslationsService.addNamespace', () => {
});
it('rejects path traversal in namespace', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -286,7 +287,7 @@ describe('KitTranslationsService.addNamespace', () => {
describe('KitTranslationsService.addLocale', () => {
it('creates locale directory with namespace files', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({ hello: 'Hello' }),
@@ -310,7 +311,7 @@ describe('KitTranslationsService.addLocale', () => {
});
it('throws if locale already exists', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -326,7 +327,7 @@ describe('KitTranslationsService.addLocale', () => {
});
it('works when no namespaces exist yet', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps({}, [localesRoot]);
const service = createKitTranslationsService(deps);
@@ -337,7 +338,7 @@ describe('KitTranslationsService.addLocale', () => {
});
it('rejects path traversal in locale', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps({}, [localesRoot]);
const service = createKitTranslationsService(deps);
@@ -354,7 +355,7 @@ describe('KitTranslationsService.addLocale', () => {
describe('KitTranslationsService.removeNamespace', () => {
it('deletes namespace files from all locales', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -377,7 +378,7 @@ describe('KitTranslationsService.removeNamespace', () => {
});
it('throws if namespace does not exist', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -393,7 +394,7 @@ describe('KitTranslationsService.removeNamespace', () => {
});
it('rejects path traversal', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps({}, [localesRoot]);
const service = createKitTranslationsService(deps);
@@ -406,7 +407,7 @@ describe('KitTranslationsService.removeNamespace', () => {
describe('KitTranslationsService.removeLocale', () => {
it('deletes entire locale directory', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -426,7 +427,7 @@ describe('KitTranslationsService.removeLocale', () => {
});
it('throws if locale does not exist', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps({}, [localesRoot]);
const service = createKitTranslationsService(deps);
@@ -437,7 +438,7 @@ describe('KitTranslationsService.removeLocale', () => {
});
it('throws when trying to delete base locale', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps(
{
[`${localesRoot}/en/common.json`]: JSON.stringify({}),
@@ -454,7 +455,7 @@ describe('KitTranslationsService.removeLocale', () => {
});
it('rejects path traversal', async () => {
const localesRoot = '/repo/apps/web/public/locales';
const localesRoot = '/repo/apps/web/i18n/messages';
const deps = createDeps({}, [localesRoot]);
const service = createKitTranslationsService(deps);

View File

@@ -1,5 +1,3 @@
import path from 'node:path';
import type {
KitTranslationsAddLocaleInput,
KitTranslationsAddLocaleSuccess,
@@ -15,6 +13,8 @@ import type {
KitTranslationsUpdateSuccess,
} from './schema';
import path from 'node:path';
export interface KitTranslationsDeps {
rootPath: string;
readFile(filePath: string): Promise<string>;
@@ -408,7 +408,7 @@ export class KitTranslationsService {
}
private getLocalesRoot() {
return path.resolve(this.deps.rootPath, 'apps', 'web', 'public', 'locales');
return path.resolve(this.deps.rootPath, 'apps', 'web', 'i18n', 'messages');
}
}