From 00a3e65651295e3288b385b7825598fca7b50141 Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 7 Jan 2026 14:14:37 +0200 Subject: [PATCH 1/6] refactor core/ to structured by resource --- AGENTS.md | 78 +++++++++++-------- src/cli/commands/auth/login.ts | 9 ++- src/cli/commands/auth/logout.ts | 2 +- src/cli/commands/auth/whoami.ts | 2 +- src/cli/commands/project/show-project.ts | 2 +- src/cli/utils/runCommand.ts | 2 +- src/core/api/auth/index.ts | 3 - src/core/api/index.ts | 1 - src/core/{api/auth/client.ts => auth/api.ts} | 2 +- src/core/{config/auth.ts => auth/config.ts} | 8 +- src/core/auth/index.ts | 3 + src/core/{api => }/auth/schema.ts | 11 +++ src/core/{schemas/config.ts => config/app.ts} | 0 src/core/config/index.ts | 6 +- src/core/config/project.ts | 31 +++++--- .../{config/entities.ts => entity/config.ts} | 7 +- src/core/entity/index.ts | 3 + .../{schemas/entity.ts => entity/schema.ts} | 1 + src/core/{errors => }/errors.ts | 0 src/core/errors/index.ts | 1 - .../functions.ts => function/config.ts} | 8 +- src/core/function/index.ts | 3 + .../function.ts => function/schema.ts} | 1 + src/core/index.ts | 7 +- src/core/schemas/auth.ts | 9 --- src/core/schemas/index.ts | 5 -- src/core/schemas/project.ts | 16 ---- tsconfig.json | 3 - vitest.config.ts | 6 ++ 29 files changed, 125 insertions(+), 105 deletions(-) delete mode 100644 src/core/api/auth/index.ts delete mode 100644 src/core/api/index.ts rename src/core/{api/auth/client.ts => auth/api.ts} (97%) rename src/core/{config/auth.ts => auth/config.ts} (87%) create mode 100644 src/core/auth/index.ts rename src/core/{api => }/auth/schema.ts (70%) rename src/core/{schemas/config.ts => config/app.ts} (100%) rename src/core/{config/entities.ts => entity/config.ts} (82%) create mode 100644 src/core/entity/index.ts rename src/core/{schemas/entity.ts => entity/schema.ts} (99%) rename src/core/{errors => }/errors.ts (100%) delete mode 100644 src/core/errors/index.ts rename src/core/{config/functions.ts => function/config.ts} (78%) create mode 100644 src/core/function/index.ts rename src/core/{schemas/function.ts => function/schema.ts} (99%) delete mode 100644 src/core/schemas/auth.ts delete mode 100644 src/core/schemas/index.ts delete mode 100644 src/core/schemas/project.ts diff --git a/AGENTS.md b/AGENTS.md index 3ff54f16..96de1af1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ The Base44 CLI is a TypeScript-based command-line tool built with: ### Project Structure - **Package**: `base44` - Single package published to npm -- **Core Module**: `src/core/` - Shared utilities, API clients, schemas, config +- **Core Module**: `src/core/` - Resource modules, utilities, errors, and config - **CLI Module**: `src/cli/` - CLI commands and entry point ## Key Technologies & Patterns @@ -60,11 +60,13 @@ The Base44 CLI is a TypeScript-based command-line tool built with: cli/ ├── src/ │ ├── core/ # Core module (shared code) -│ │ ├── api/ # API client code -│ │ ├── config/ # Configuration management +│ │ ├── auth/ # Auth resource (api, schema, config) +│ │ ├── entity/ # Entity resource (schema, config) +│ │ ├── function/ # Function resource (schema, config) +│ │ ├── config/ # Project/app configuration │ │ ├── errors/ # Custom error classes -│ │ ├── schemas/ # Zod schemas │ │ ├── utils/ # Utility functions +│ │ ├── consts.ts # Shared constants │ │ └── index.ts # Core module exports │ └── cli/ # CLI module (main CLI) │ ├── commands/ # Command implementations (grouped by feature) @@ -77,19 +79,24 @@ cli/ └── tsconfig.json # TypeScript configuration ``` +#### Resource Module Structure + +Each resource (auth, entity, function) has its own folder containing: +- `schema.ts` - Zod schemas and TypeScript types +- `api.ts` - API client functions (if applicable) +- `config.ts` - Configuration/file reading functions +- `index.ts` - Barrel exports + #### Path Aliases -The project uses TypeScript path aliases for cleaner imports. These are defined in `tsconfig.json` and resolved at build time using `tsc-alias`: +The project uses a single TypeScript path alias for cleaner imports. Defined in `tsconfig.json` and resolved at build time using `tsc-alias`: -- `@api/*` → `./src/core/api/*` -- `@schemas/*` → `./src/core/schemas/*` -- `@config/*` → `./src/core/config/*` - `@core/*` → `./src/core/*` **Example usage:** ```typescript -import { writeAuth } from "@config/auth.js"; -import { generateDeviceCode } from "@api/auth/index.js"; +import { writeAuth, generateDeviceCode } from "@core/auth/index.js"; +import { readProjectConfig } from "@core/config/project.js"; import { AuthApiError } from "@core/errors/index.js"; ``` @@ -98,8 +105,7 @@ import { AuthApiError } from "@core/errors/index.js"; import { Command } from "commander"; import { log } from "@clack/prompts"; import { runCommand, runTask } from "../../utils/index.js"; -import { someConfig } from "@config/some.js"; -import { someApiCall } from "@api/some/index.js"; +import { someApiCall, someConfig } from "@core/resource/index.js"; async function commandFunction(): Promise { const result = await runTask( @@ -126,7 +132,7 @@ export const commandName = new Command("command-name") **Important**: - All commands must use `runCommand()` wrapper for consistent Base44 branding - Use `runTask()` for async operations that need spinner feedback -- Use path aliases for imports from core module +- Use `@core/*` path alias for imports from core module - Use relative imports for CLI-specific utilities #### CLI Utilities @@ -178,8 +184,8 @@ export type User = z.infer; ## Important Rules 1. **Use npm** for all package management - never yarn -2. **Project structure** - Core module (`src/core/`) contains shared code, CLI module (`src/cli/`) contains commands -3. **Path aliases** - Use `@api/*`, `@config/*`, `@schemas/*`, `@core/*` for imports from core module +2. **Project structure** - Core module (`src/core/`) contains resource modules and shared code, CLI module (`src/cli/`) contains commands +3. **Path aliases** - Use `@core/*` for imports from core module (e.g., `@core/auth/index.js`, `@core/entity/index.js`) 4. **CLI utilities** - Use relative imports for CLI-specific utilities (`../../utils/index.js`) 5. **Zod validation is required** for all external data 6. **@clack/prompts for all user interaction** - no raw `readline` or `inquirer` @@ -192,6 +198,7 @@ export type User = z.infer; 13. **Task wrapper** - Use `runTask()` for async operations that need spinner feedback 14. **ES Modules** - Package uses `"type": "module"` - always use `.js` extensions in import statements 15. **Shared utilities** - Use cross-platform file utilities and config management from `src/core/` +16. **Resource-based organization** - Each resource (auth, entity, function) has its own folder with schema, api, and config files ## Common Patterns @@ -199,27 +206,34 @@ export type User = z.infer; 1. Create command file in `src/cli/commands//` directory 2. Import and register in main CLI entry point (`src/cli/index.ts`) 3. Use Commander.js Command class -4. Add Zod validation for inputs (schemas in `src/core/schemas/`) +4. Import from appropriate resource module (`@core/auth/index.js`, `@core/entity/index.js`, etc.) 5. Use @clack/prompts for user interaction -6. Use path aliases for imports from core module (`@api/*`, `@config/*`, etc.) -7. Use relative imports for CLI utilities (`../../utils/index.js`) -8. Wrap command function with `runCommand()` utility -9. Use `runTask()` for async operations with spinners +6. Use relative imports for CLI utilities (`../../utils/index.js`) +7. Wrap command function with `runCommand()` utility +8. Use `runTask()` for async operations with spinners + +### Adding a New Resource +1. Create a new folder in `src/core//` +2. Add `schema.ts` with Zod schemas and inferred types +3. Add `api.ts` for API client functions (if applicable) +4. Add `config.ts` for file/config reading functions +5. Add `index.ts` barrel file exporting all public APIs +6. Export from `src/core/index.ts` ### API Integration -1. Define Zod schema in `src/core/schemas/` directory -2. Create API client function in `src/core/api/` directory -3. Export from `src/core/api/index.ts` -4. Import in CLI commands using path alias (`@api/*`) +1. Define Zod schema in the resource's `schema.ts` file +2. Create API client function in the resource's `api.ts` file +3. Export from the resource's `index.ts` +4. Import in CLI commands using `@core//index.js` 5. Validate response with Zod schema 6. Handle errors gracefully 7. Use `runTask()` for loading states ### Configuration Management -1. Define Zod schema in `src/core/schemas/` directory -2. Create config management functions in `src/core/config/` directory -3. Export from `src/core/config/index.ts` -4. Import in CLI commands using path alias (`@config/*`) +1. Define Zod schema in `src/core/config/` or the relevant resource folder +2. Create config management functions alongside the schema +3. Export from the module's `index.ts` +4. Import in CLI commands using `@core/config/` or `@core//` 5. Read config file 6. Validate with Zod schema 7. Provide type-safe access via inferred types @@ -242,7 +256,7 @@ export type User = z.infer; - **Main plan**: `cli/plan.md` - Full implementation plan - **This file**: `cli/AGENTS.md` - AI agent guidelines -- **Core module**: `cli/src/core/` - Shared utilities, API, schemas, config +- **Core module**: `cli/src/core/` - Resource modules (auth, entity, function), config, utils, errors - **CLI module**: `cli/src/cli/` - CLI commands and entry point ## Questions to Ask @@ -257,13 +271,15 @@ If uncertain about implementation: ## Notes from Development - **Project structure**: Single package with core and cli modules +- **Resource-based architecture**: Each resource (auth, entity, function) has its own folder with schema, api, and config - CLI uses TypeScript with strict type checking - All commands must be registered in main CLI entry point (`src/cli/index.ts`) - Build process compiles TypeScript to JavaScript in `dist/` folder and resolves path aliases - Commands should be testable independently -- Shared code (API, schemas, config, utils, errors) goes in `src/core/` +- Resource modules (auth, entity, function) go in `src/core//` +- Shared config (project, app) goes in `src/core/config/` - CLI-specific code (commands, runCommand, runTask) goes in `src/cli/` -- Use path aliases (`@api/*`, `@config/*`, `@schemas/*`, `@core/*`) for imports from core +- Use `@core/*` path alias for imports from core module - Use relative imports for CLI-specific utilities - Error handling should be user-friendly with clear messages - Use @clack/prompts for all user-facing interactions (no console.log for prompts) diff --git a/src/cli/commands/auth/login.ts b/src/cli/commands/auth/login.ts index bf49dff1..6367977b 100644 --- a/src/cli/commands/auth/login.ts +++ b/src/cli/commands/auth/login.ts @@ -1,9 +1,12 @@ import { Command } from "commander"; import { log } from "@clack/prompts"; import pWaitFor from "p-wait-for"; -import { writeAuth } from "@config/auth.js"; -import { generateDeviceCode, getTokenFromDeviceCode } from "@api/auth/index.js"; -import type { DeviceCodeResponse, TokenResponse } from "@api/auth/index.js"; +import { + writeAuth, + generateDeviceCode, + getTokenFromDeviceCode, +} from "@core/auth/index.js"; +import type { DeviceCodeResponse, TokenResponse } from "@core/auth/index.js"; import { runCommand, runTask } from "../../utils/index.js"; async function generateAndDisplayDeviceCode(): Promise { diff --git a/src/cli/commands/auth/logout.ts b/src/cli/commands/auth/logout.ts index d6252ea2..97fb1c0e 100644 --- a/src/cli/commands/auth/logout.ts +++ b/src/cli/commands/auth/logout.ts @@ -1,6 +1,6 @@ import { Command } from "commander"; import { log } from "@clack/prompts"; -import { deleteAuth } from "@config/auth.js"; +import { deleteAuth } from "@core/auth/index.js"; import { runCommand } from "../../utils/index.js"; async function logout(): Promise { diff --git a/src/cli/commands/auth/whoami.ts b/src/cli/commands/auth/whoami.ts index 69e148d2..e8c221c8 100644 --- a/src/cli/commands/auth/whoami.ts +++ b/src/cli/commands/auth/whoami.ts @@ -1,6 +1,6 @@ import { Command } from "commander"; import { log } from "@clack/prompts"; -import { readAuth } from "@config/auth.js"; +import { readAuth } from "@core/auth/index.js"; import { runCommand } from "../../utils/index.js"; async function whoami(): Promise { diff --git a/src/cli/commands/project/show-project.ts b/src/cli/commands/project/show-project.ts index 8117a960..8f82a311 100644 --- a/src/cli/commands/project/show-project.ts +++ b/src/cli/commands/project/show-project.ts @@ -1,6 +1,6 @@ import { Command } from "commander"; import { log } from "@clack/prompts"; -import { readProjectConfig } from "../../../core/config/project.js"; +import { readProjectConfig } from "@core/config/project.js"; import { runCommand, runTask } from "../../utils/index.js"; async function showProject(): Promise { diff --git a/src/cli/utils/runCommand.ts b/src/cli/utils/runCommand.ts index c7d12aa0..55ad065d 100644 --- a/src/cli/utils/runCommand.ts +++ b/src/cli/utils/runCommand.ts @@ -1,6 +1,6 @@ import { intro, log } from "@clack/prompts"; import chalk from "chalk"; -import { AuthApiError, AuthValidationError } from "@core/errors/index.js"; +import { AuthApiError, AuthValidationError } from "@core/errors.js"; const base44Color = chalk.bgHex("#E86B3C"); diff --git a/src/core/api/auth/index.ts b/src/core/api/auth/index.ts deleted file mode 100644 index abb9bb05..00000000 --- a/src/core/api/auth/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./client.js"; -export * from "./schema.js"; -export { AuthApiError, AuthValidationError } from "@core/errors/index.js"; diff --git a/src/core/api/index.ts b/src/core/api/index.ts deleted file mode 100644 index b424c88d..00000000 --- a/src/core/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./auth/index.js"; diff --git a/src/core/api/auth/client.ts b/src/core/auth/api.ts similarity index 97% rename from src/core/api/auth/client.ts rename to src/core/auth/api.ts index 9bde639f..112e486a 100644 --- a/src/core/api/auth/client.ts +++ b/src/core/auth/api.ts @@ -1,4 +1,4 @@ -import { AuthApiError, AuthValidationError } from "@core/errors/index.js"; +import { AuthApiError, AuthValidationError } from "@core/errors.js"; import { DeviceCodeResponseSchema, TokenResponseSchema } from "./schema.js"; import type { DeviceCodeResponse, TokenResponse } from "./schema.js"; diff --git a/src/core/config/auth.ts b/src/core/auth/config.ts similarity index 87% rename from src/core/config/auth.ts rename to src/core/auth/config.ts index ddd9503d..358b1ef1 100644 --- a/src/core/config/auth.ts +++ b/src/core/auth/config.ts @@ -1,7 +1,7 @@ -import { AuthDataSchema } from "../schemas/auth.js"; -import type { AuthData } from "../schemas/auth.js"; -import { getAuthFilePath } from "../consts.js"; -import { readJsonFile, writeJsonFile, deleteFile } from "../utils/fs.js"; +import { getAuthFilePath } from "@core/consts.js"; +import { readJsonFile, writeJsonFile, deleteFile } from "@core/utils/fs.js"; +import { AuthDataSchema } from "./schema.js"; +import type { AuthData } from "./schema.js"; export async function readAuth(): Promise { try { diff --git a/src/core/auth/index.ts b/src/core/auth/index.ts new file mode 100644 index 00000000..a00baf2d --- /dev/null +++ b/src/core/auth/index.ts @@ -0,0 +1,3 @@ +export * from "./schema.js"; +export * from "./api.js"; +export * from "./config.js"; diff --git a/src/core/api/auth/schema.ts b/src/core/auth/schema.ts similarity index 70% rename from src/core/api/auth/schema.ts rename to src/core/auth/schema.ts index 03651af3..9ee0f12c 100644 --- a/src/core/api/auth/schema.ts +++ b/src/core/auth/schema.ts @@ -1,5 +1,15 @@ import { z } from "zod"; +// Auth data schema (stored locally) +export const AuthDataSchema = z.object({ + token: z.string().min(1, "Token cannot be empty"), + email: z.email(), + name: z.string().min(1, "Name cannot be empty"), +}); + +export type AuthData = z.infer; + +// API response schemas export const DeviceCodeResponseSchema = z.object({ deviceCode: z.string().min(1, "Device code cannot be empty"), userCode: z.string().min(1, "User code cannot be empty"), @@ -16,3 +26,4 @@ export const TokenResponseSchema = z.object({ }); export type TokenResponse = z.infer; + diff --git a/src/core/schemas/config.ts b/src/core/config/app.ts similarity index 100% rename from src/core/schemas/config.ts rename to src/core/config/app.ts diff --git a/src/core/config/index.ts b/src/core/config/index.ts index c7afb0f4..fb6b65d8 100644 --- a/src/core/config/index.ts +++ b/src/core/config/index.ts @@ -1,5 +1,3 @@ -export * from "../consts.js"; -export * from "./auth.js"; +export * from "@core/consts.js"; export * from "./project.js"; -export * from "./entities.js"; -export * from "./functions.js"; +export * from "./app.js"; diff --git a/src/core/config/project.ts b/src/core/config/project.ts index 7f73d41e..6a1c5136 100644 --- a/src/core/config/project.ts +++ b/src/core/config/project.ts @@ -1,13 +1,26 @@ import { join, dirname } from "node:path"; +import { z } from "zod"; import { globby } from "globby"; -import type { ProjectWithPaths } from "../schemas/project.js"; -import { ProjectConfigSchema } from "../schemas/project.js"; -import type { Entity } from "../schemas/entity.js"; -import type { FunctionConfig } from "../schemas/function.js"; -import { getProjectConfigPatterns, PROJECT_SUBDIR } from "../consts.js"; -import { readJsonFile } from "../utils/fs.js"; -import { readAllEntities } from "./entities.js"; -import { readAllFunctions } from "./functions.js"; +import { getProjectConfigPatterns, PROJECT_SUBDIR } from "@core/consts.js"; +import { readJsonFile } from "@core/utils/fs.js"; +import { readAllEntities } from "@core/entity/index.js"; +import type { Entity } from "@core/entity/index.js"; +import { readAllFunctions } from "@core/function/index.js"; +import type { FunctionConfig } from "@core/function/index.js"; + +// Project config schema +export const ProjectConfigSchema = z.looseObject({ + name: z.string().min(1, "Project name cannot be empty"), + entitySrc: z.string().default("./entities"), + functionSrc: z.string().default("./functions"), +}); + +export type ProjectConfig = z.infer; + +export interface ProjectWithPaths extends ProjectConfig { + root: string; + configPath: string; +} export interface ProjectRoot { root: string; @@ -20,7 +33,6 @@ export interface ProjectData { functions: FunctionConfig[]; } -// Finds config file in a directory using globby, respecting priority order. async function findConfigInDir(dir: string): Promise { const files = await globby(getProjectConfigPatterns(), { cwd: dir, @@ -29,7 +41,6 @@ async function findConfigInDir(dir: string): Promise { return files[0] ?? null; } -// Walks up the directory tree to locate a Base44 project config file. export async function findProjectRoot( startPath?: string ): Promise { diff --git a/src/core/config/entities.ts b/src/core/entity/config.ts similarity index 82% rename from src/core/config/entities.ts rename to src/core/entity/config.ts index 6319b02c..e9e38837 100644 --- a/src/core/config/entities.ts +++ b/src/core/entity/config.ts @@ -1,7 +1,7 @@ import { globby } from "globby"; -import { EntitySchema } from "../schemas/entity.js"; -import type { Entity } from "../schemas/entity.js"; -import { readJsonFile, pathExists } from "../utils/fs.js"; +import { readJsonFile, pathExists } from "@core/utils/fs.js"; +import { EntitySchema } from "./schema.js"; +import type { Entity } from "./schema.js"; async function readEntityFile(entityPath: string): Promise { const parsed = await readJsonFile(entityPath); @@ -34,3 +34,4 @@ export async function readAllEntities(entitiesDir: string): Promise { return entities; } + diff --git a/src/core/entity/index.ts b/src/core/entity/index.ts new file mode 100644 index 00000000..bbe1e94e --- /dev/null +++ b/src/core/entity/index.ts @@ -0,0 +1,3 @@ +export * from "./schema.js"; +export * from "./config.js"; + diff --git a/src/core/schemas/entity.ts b/src/core/entity/schema.ts similarity index 99% rename from src/core/schemas/entity.ts rename to src/core/entity/schema.ts index 75a8e3f3..d615c11a 100644 --- a/src/core/schemas/entity.ts +++ b/src/core/entity/schema.ts @@ -33,3 +33,4 @@ export const EntitySchema = z.object({ export type EntityProperty = z.infer; export type EntityPolicies = z.infer; export type Entity = z.infer; + diff --git a/src/core/errors/errors.ts b/src/core/errors.ts similarity index 100% rename from src/core/errors/errors.ts rename to src/core/errors.ts diff --git a/src/core/errors/index.ts b/src/core/errors/index.ts deleted file mode 100644 index 3cec8d9c..00000000 --- a/src/core/errors/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./errors.js"; diff --git a/src/core/config/functions.ts b/src/core/function/config.ts similarity index 78% rename from src/core/config/functions.ts rename to src/core/function/config.ts index 4cecf067..a3171d4f 100644 --- a/src/core/config/functions.ts +++ b/src/core/function/config.ts @@ -1,8 +1,8 @@ import { globby } from "globby"; -import { FunctionConfigSchema } from "../schemas/function.js"; -import type { FunctionConfig } from "../schemas/function.js"; -import { FUNCTION_CONFIG_FILE } from "../consts.js"; -import { readJsonFile, pathExists } from "../utils/fs.js"; +import { FUNCTION_CONFIG_FILE } from "@core/consts.js"; +import { readJsonFile, pathExists } from "@core/utils/fs.js"; +import { FunctionConfigSchema } from "./schema.js"; +import type { FunctionConfig } from "./schema.js"; export async function readFunctionConfig( configPath: string diff --git a/src/core/function/index.ts b/src/core/function/index.ts new file mode 100644 index 00000000..bbe1e94e --- /dev/null +++ b/src/core/function/index.ts @@ -0,0 +1,3 @@ +export * from "./schema.js"; +export * from "./config.js"; + diff --git a/src/core/schemas/function.ts b/src/core/function/schema.ts similarity index 99% rename from src/core/schemas/function.ts rename to src/core/function/schema.ts index 02b94436..ef3bca0c 100644 --- a/src/core/schemas/function.ts +++ b/src/core/function/schema.ts @@ -44,3 +44,4 @@ export type ScheduleTrigger = z.infer; export type EventTrigger = z.infer; export type Trigger = z.infer; export type FunctionConfig = z.infer; + diff --git a/src/core/index.ts b/src/core/index.ts index c179c52f..f0da0433 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,5 +1,6 @@ -export * from "./schemas/index.js"; +export * from "./auth/index.js"; +export * from "./entity/index.js"; +export * from "./function/index.js"; export * from "./config/index.js"; export * from "./utils/index.js"; -export * from "./api/index.js"; -export * from "./errors/index.js"; +export * from "./errors.js"; diff --git a/src/core/schemas/auth.ts b/src/core/schemas/auth.ts deleted file mode 100644 index a3cede98..00000000 --- a/src/core/schemas/auth.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { z } from "zod"; - -export const AuthDataSchema = z.object({ - token: z.string().min(1, "Token cannot be empty"), - email: z.email(), - name: z.string().min(1, "Name cannot be empty"), -}); - -export type AuthData = z.infer; diff --git a/src/core/schemas/index.ts b/src/core/schemas/index.ts deleted file mode 100644 index 6d3cf81b..00000000 --- a/src/core/schemas/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./auth.js"; -export * from "./project.js"; -export * from "./config.js"; -export * from "./entity.js"; -export * from "./function.js"; diff --git a/src/core/schemas/project.ts b/src/core/schemas/project.ts deleted file mode 100644 index 76dfa95c..00000000 --- a/src/core/schemas/project.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from "zod"; - -export const ProjectConfigSchema = z.looseObject({ - name: z.string().min(1, "Project name cannot be empty"), - entitySrc: z.string().default("./entities"), - functionSrc: z.string().default("./functions"), -}); - -export type ProjectConfig = z.infer; - -export interface ProjectWithPaths extends ProjectConfig { - root: string; - configPath: string; -} - - diff --git a/tsconfig.json b/tsconfig.json index ded1bb3f..e8cb1a5f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,9 +17,6 @@ "types": ["node"], "baseUrl": ".", "paths": { - "@api/*": ["./src/core/api/*"], - "@schemas/*": ["./src/core/schemas/*"], - "@config/*": ["./src/core/config/*"], "@core/*": ["./src/core/*"] } }, diff --git a/vitest.config.ts b/vitest.config.ts index e232b3c7..1f2cb3d8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from "vitest/config"; +import { resolve } from "path"; export default defineConfig({ test: { @@ -7,4 +8,9 @@ export default defineConfig({ include: ["tests/**/*.test.ts"], testTimeout: 10000, }, + resolve: { + alias: { + "@core": resolve(__dirname, "./src/core"), + }, + }, }); From 19794a8576b6d7318a050465e0fb9226e14beebe Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 7 Jan 2026 14:27:07 +0200 Subject: [PATCH 2/6] remove usage of @core from inside core/ --- src/core/auth/api.ts | 2 +- src/core/auth/config.ts | 4 ++-- src/core/auth/schema.ts | 1 - src/core/config/app.ts | 1 - src/core/config/index.ts | 1 - src/core/config/project.ts | 12 ++++++------ src/core/entity/config.ts | 3 +-- src/core/entity/index.ts | 1 - src/core/entity/schema.ts | 1 - src/core/errors.ts | 10 +++------- src/core/function/config.ts | 4 ++-- src/core/function/index.ts | 1 - src/core/function/schema.ts | 1 - 13 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/core/auth/api.ts b/src/core/auth/api.ts index 112e486a..ab585814 100644 --- a/src/core/auth/api.ts +++ b/src/core/auth/api.ts @@ -1,4 +1,4 @@ -import { AuthApiError, AuthValidationError } from "@core/errors.js"; +import { AuthApiError, AuthValidationError } from "../errors.js"; import { DeviceCodeResponseSchema, TokenResponseSchema } from "./schema.js"; import type { DeviceCodeResponse, TokenResponse } from "./schema.js"; diff --git a/src/core/auth/config.ts b/src/core/auth/config.ts index 358b1ef1..594fa0b4 100644 --- a/src/core/auth/config.ts +++ b/src/core/auth/config.ts @@ -1,5 +1,5 @@ -import { getAuthFilePath } from "@core/consts.js"; -import { readJsonFile, writeJsonFile, deleteFile } from "@core/utils/fs.js"; +import { getAuthFilePath } from "../consts.js"; +import { readJsonFile, writeJsonFile, deleteFile } from "../utils/fs.js"; import { AuthDataSchema } from "./schema.js"; import type { AuthData } from "./schema.js"; diff --git a/src/core/auth/schema.ts b/src/core/auth/schema.ts index 9ee0f12c..e689569d 100644 --- a/src/core/auth/schema.ts +++ b/src/core/auth/schema.ts @@ -26,4 +26,3 @@ export const TokenResponseSchema = z.object({ }); export type TokenResponse = z.infer; - diff --git a/src/core/config/app.ts b/src/core/config/app.ts index cfdbd8dc..ff47302b 100644 --- a/src/core/config/app.ts +++ b/src/core/config/app.ts @@ -16,4 +16,3 @@ export const AppConfigSchema = z.object({ export type SiteConfig = z.infer; export type AppConfig = z.infer; - diff --git a/src/core/config/index.ts b/src/core/config/index.ts index fb6b65d8..bc70aa42 100644 --- a/src/core/config/index.ts +++ b/src/core/config/index.ts @@ -1,3 +1,2 @@ -export * from "@core/consts.js"; export * from "./project.js"; export * from "./app.js"; diff --git a/src/core/config/project.ts b/src/core/config/project.ts index 6a1c5136..975b6e44 100644 --- a/src/core/config/project.ts +++ b/src/core/config/project.ts @@ -1,12 +1,12 @@ import { join, dirname } from "node:path"; import { z } from "zod"; import { globby } from "globby"; -import { getProjectConfigPatterns, PROJECT_SUBDIR } from "@core/consts.js"; -import { readJsonFile } from "@core/utils/fs.js"; -import { readAllEntities } from "@core/entity/index.js"; -import type { Entity } from "@core/entity/index.js"; -import { readAllFunctions } from "@core/function/index.js"; -import type { FunctionConfig } from "@core/function/index.js"; +import { getProjectConfigPatterns, PROJECT_SUBDIR } from "../consts.js"; +import { readJsonFile } from "../utils/fs.js"; +import { readAllEntities } from "../entity/index.js"; +import type { Entity } from "../entity/index.js"; +import { readAllFunctions } from "../function/index.js"; +import type { FunctionConfig } from "../function/index.js"; // Project config schema export const ProjectConfigSchema = z.looseObject({ diff --git a/src/core/entity/config.ts b/src/core/entity/config.ts index e9e38837..9f124b95 100644 --- a/src/core/entity/config.ts +++ b/src/core/entity/config.ts @@ -1,5 +1,5 @@ import { globby } from "globby"; -import { readJsonFile, pathExists } from "@core/utils/fs.js"; +import { readJsonFile, pathExists } from "../utils/fs.js"; import { EntitySchema } from "./schema.js"; import type { Entity } from "./schema.js"; @@ -34,4 +34,3 @@ export async function readAllEntities(entitiesDir: string): Promise { return entities; } - diff --git a/src/core/entity/index.ts b/src/core/entity/index.ts index bbe1e94e..e3c17ba0 100644 --- a/src/core/entity/index.ts +++ b/src/core/entity/index.ts @@ -1,3 +1,2 @@ export * from "./schema.js"; export * from "./config.js"; - diff --git a/src/core/entity/schema.ts b/src/core/entity/schema.ts index d615c11a..75a8e3f3 100644 --- a/src/core/entity/schema.ts +++ b/src/core/entity/schema.ts @@ -33,4 +33,3 @@ export const EntitySchema = z.object({ export type EntityProperty = z.infer; export type EntityPolicies = z.infer; export type Entity = z.infer; - diff --git a/src/core/errors.ts b/src/core/errors.ts index 20d05eda..15812e38 100644 --- a/src/core/errors.ts +++ b/src/core/errors.ts @@ -1,10 +1,7 @@ export class AuthApiError extends Error { - constructor( - message: string, - public readonly cause?: unknown - ) { + constructor(message: string, public readonly cause?: unknown) { super(message); - this.name = 'AuthApiError'; + this.name = "AuthApiError"; } } @@ -14,7 +11,6 @@ export class AuthValidationError extends Error { public readonly issues: Array<{ message: string; path: string[] }> ) { super(message); - this.name = 'AuthValidationError'; + this.name = "AuthValidationError"; } } - diff --git a/src/core/function/config.ts b/src/core/function/config.ts index a3171d4f..f6693977 100644 --- a/src/core/function/config.ts +++ b/src/core/function/config.ts @@ -1,6 +1,6 @@ import { globby } from "globby"; -import { FUNCTION_CONFIG_FILE } from "@core/consts.js"; -import { readJsonFile, pathExists } from "@core/utils/fs.js"; +import { FUNCTION_CONFIG_FILE } from "../consts.js"; +import { readJsonFile, pathExists } from "../utils/fs.js"; import { FunctionConfigSchema } from "./schema.js"; import type { FunctionConfig } from "./schema.js"; diff --git a/src/core/function/index.ts b/src/core/function/index.ts index bbe1e94e..e3c17ba0 100644 --- a/src/core/function/index.ts +++ b/src/core/function/index.ts @@ -1,3 +1,2 @@ export * from "./schema.js"; export * from "./config.js"; - diff --git a/src/core/function/schema.ts b/src/core/function/schema.ts index ef3bca0c..02b94436 100644 --- a/src/core/function/schema.ts +++ b/src/core/function/schema.ts @@ -44,4 +44,3 @@ export type ScheduleTrigger = z.infer; export type EventTrigger = z.infer; export type Trigger = z.infer; export type FunctionConfig = z.infer; - From cf6ebd2ac0345afc5c74aa01004fc01ffadef803 Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 7 Jan 2026 14:53:28 +0200 Subject: [PATCH 3/6] some more changes --- AGENTS.md | 334 +++++------------- src/core/config/index.ts | 1 + src/core/config/project.ts | 16 +- src/core/config/resource.ts | 5 + src/core/index.ts | 3 +- src/core/{ => resources}/entity/config.ts | 3 +- .../{function => resources/entity}/index.ts | 2 + src/core/resources/entity/resource.ts | 9 + src/core/{ => resources}/entity/schema.ts | 1 + src/core/{ => resources}/function/config.ts | 5 +- .../{entity => resources/function}/index.ts | 2 + src/core/resources/function/resource.ts | 9 + src/core/{ => resources}/function/schema.ts | 1 + src/core/resources/index.ts | 3 + 14 files changed, 131 insertions(+), 263 deletions(-) create mode 100644 src/core/config/resource.ts rename src/core/{ => resources}/entity/config.ts (93%) rename src/core/{function => resources/entity}/index.ts (64%) create mode 100644 src/core/resources/entity/resource.ts rename src/core/{ => resources}/entity/schema.ts (99%) rename src/core/{ => resources}/function/config.ts (89%) rename src/core/{entity => resources/function}/index.ts (64%) create mode 100644 src/core/resources/function/resource.ts rename src/core/{ => resources}/function/schema.ts (99%) create mode 100644 src/core/resources/index.ts diff --git a/AGENTS.md b/AGENTS.md index 96de1af1..e72aa193 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,8 @@ This document provides essential context and guidelines for AI agents working on the Base44 CLI project. +**Important**: Keep this file updated when making significant architectural changes. + ## Project Overview The Base44 CLI is a TypeScript-based command-line tool built with: @@ -12,279 +14,113 @@ The Base44 CLI is a TypeScript-based command-line tool built with: ### Project Structure - **Package**: `base44` - Single package published to npm -- **Core Module**: `src/core/` - Resource modules, utilities, errors, and config +- **Core Module**: `src/core/` - Resources, utilities, errors, and config - **CLI Module**: `src/cli/` - CLI commands and entry point -## Key Technologies & Patterns - -### CLI Framework -- Use **Commander.js** for all command definitions -- CLI name is **`base44`** -- Commands follow the pattern: `base44 [subcommand] [options]` - -### User Interaction -- Always use **@clack/prompts** for interactive prompts -- Use `@clack/prompts` for: - - User input collection - - Progress indicators - - Spinners for async operations (via `runTask` utility) - - Confirmation dialogs - - Selection menus - -### Schema Validation -- **Zod is mandatory** for all validation: - - API response validation - - Configuration file validation - - User input validation - - File schema validation -- Create Zod schemas before implementing features -- Use Zod-inferred types for TypeScript type safety -- Always validate external data before processing - -### Code Style & Structure +## Folder Structure -#### Code Comments -- **Minimal commenting approach**: Only add comments for: - - Complex algorithms or non-obvious logic - - Unclear design decisions that need explanation - - Workarounds or non-standard patterns -- **Avoid commenting**: - - Self-explanatory code - - Simple function signatures (TypeScript types provide documentation) - - Obvious operations (e.g., "// Read the file" when the function is `readFile`) -- **JSDoc comments**: Only use for public APIs that need documentation for external consumers -- Let the code speak for itself - prefer clear naming over comments - -#### Project Folder Structure ``` cli/ ├── src/ -│ ├── core/ # Core module (shared code) -│ │ ├── auth/ # Auth resource (api, schema, config) -│ │ ├── entity/ # Entity resource (schema, config) -│ │ ├── function/ # Function resource (schema, config) -│ │ ├── config/ # Project/app configuration -│ │ ├── errors/ # Custom error classes -│ │ ├── utils/ # Utility functions -│ │ ├── consts.ts # Shared constants -│ │ └── index.ts # Core module exports -│ └── cli/ # CLI module (main CLI) -│ ├── commands/ # Command implementations (grouped by feature) -│ │ ├── auth/ # Authentication commands (login, logout, whoami) -│ │ └── project/ # Project commands (show-project) -│ ├── utils/ # CLI-specific utilities (runCommand, runTask) -│ └── index.ts # Main CLI entry point (with shebang) -├── dist/ # Build output -├── package.json # Package configuration -└── tsconfig.json # TypeScript configuration +│ ├── core/ +│ │ ├── auth/ # Auth (user authentication, not a project resource) +│ │ │ ├── api.ts +│ │ │ ├── schema.ts +│ │ │ ├── config.ts +│ │ │ └── index.ts +│ │ ├── resources/ # Project resources (entity, function, etc.) +│ │ │ ├── entity/ +│ │ │ │ ├── schema.ts +│ │ │ │ ├── config.ts +│ │ │ │ ├── resource.ts +│ │ │ │ └── index.ts +│ │ │ ├── function/ +│ │ │ │ ├── schema.ts +│ │ │ │ ├── config.ts +│ │ │ │ ├── resource.ts +│ │ │ │ └── index.ts +│ │ │ └── index.ts +│ │ ├── config/ # Project/app configuration +│ │ │ ├── resource.ts # Resource interface +│ │ │ ├── project.ts # Project loading logic +│ │ │ ├── app.ts +│ │ │ └── index.ts +│ │ ├── utils/ +│ │ ├── consts.ts +│ │ ├── errors.ts +│ │ └── index.ts +│ └── cli/ +│ ├── commands/ +│ │ ├── auth/ +│ │ └── project/ +│ ├── utils/ +│ └── index.ts +├── dist/ +├── package.json +└── tsconfig.json ``` -#### Resource Module Structure - -Each resource (auth, entity, function) has its own folder containing: -- `schema.ts` - Zod schemas and TypeScript types -- `api.ts` - API client functions (if applicable) -- `config.ts` - Configuration/file reading functions -- `index.ts` - Barrel exports - -#### Path Aliases +## Resource Pattern -The project uses a single TypeScript path alias for cleaner imports. Defined in `tsconfig.json` and resolved at build time using `tsc-alias`: - -- `@core/*` → `./src/core/*` +Resources are project-specific collections (entities, functions) that can be loaded from the filesystem. -**Example usage:** +### Resource Interface (`config/resource.ts`) ```typescript -import { writeAuth, generateDeviceCode } from "@core/auth/index.js"; -import { readProjectConfig } from "@core/config/project.js"; -import { AuthApiError } from "@core/errors/index.js"; +export interface Resource { + name: string; + readAll(dir: string): Promise; +} ``` -#### Command Implementation Pattern +### Resource Implementation (`resources//resource.ts`) ```typescript -import { Command } from "commander"; -import { log } from "@clack/prompts"; -import { runCommand, runTask } from "../../utils/index.js"; -import { someApiCall, someConfig } from "@core/resource/index.js"; - -async function commandFunction(): Promise { - const result = await runTask( - "Loading data...", - async () => { - return await someApiCall(); - }, - { - successMessage: "Data loaded", - errorMessage: "Failed to load data", - } - ); - - log.info(`Result: ${result}`); -} - -export const commandName = new Command("command-name") - .description("Command description") - .action(async () => { - await runCommand(commandFunction); - }); +export const entityResource: Resource = { + name: "entity", + readAll: readAllEntities, +}; ``` -**Important**: -- All commands must use `runCommand()` wrapper for consistent Base44 branding -- Use `runTask()` for async operations that need spinner feedback -- Use `@core/*` path alias for imports from core module -- Use relative imports for CLI-specific utilities - -#### CLI Utilities +### Adding a New Resource +1. Create folder in `src/core/resources//` +2. Add `schema.ts` with Zod schemas +3. Add `config.ts` with file reading logic +4. Add `resource.ts` implementing `Resource` +5. Add `index.ts` barrel exports +6. Register in `config/project.ts` resources list +7. Add typed field to `ProjectData` interface -**`runCommand(commandFn)`** - Wraps command execution with: -- Base44 intro banner -- Consistent error handling for `AuthApiError`, `AuthValidationError`, and generic errors -- Process exit on error +## Path Aliases -**`runTask(message, operation, options)`** - Wraps async operations with: -- Automatic spinner management -- Success/error message customization -- Returns the operation result +Single alias defined in `tsconfig.json`: +- `@core/*` → `./src/core/*` -#### Schema Definition Pattern ```typescript -import { z } from "zod"; - -export const UserSchema = z.object({ - id: z.string(), - email: z.string().email(), - name: z.string(), -}); - -export type User = z.infer; +import { readProjectConfig } from "@core/config/project.js"; +import { entityResource } from "@core/resources/entity/index.js"; ``` -## Development Workflow - -### Package Manager -- **Use npm** for all package management operations -- Install dependencies: `npm install` -- Add dependencies: `npm install ` -- Add dev dependencies: `npm install -D ` - -### Build Process -- **Build**: Use `npm run build` to compile TypeScript to JavaScript (runs `tsc && tsc-alias`) -- **Development**: Use `npm run dev` for development mode (uses `tsx` to run TypeScript directly) -- Always build before testing -- **ES Modules**: Package uses `"type": "module"` - use `.js` extensions in imports -- **CLI Entry Point**: Main entry point (`src/cli/index.ts`) includes shebang for direct execution -- **Output**: Compiled JavaScript output goes to `dist/` directory -- **Path Alias Resolution**: `tsc-alias` resolves path aliases in compiled output - -### Command Testing -- Test commands by running the compiled CLI or using development mode -- Verify help text: `base44 --help` - ## Important Rules -1. **Use npm** for all package management - never yarn -2. **Project structure** - Core module (`src/core/`) contains resource modules and shared code, CLI module (`src/cli/`) contains commands -3. **Path aliases** - Use `@core/*` for imports from core module (e.g., `@core/auth/index.js`, `@core/entity/index.js`) -4. **CLI utilities** - Use relative imports for CLI-specific utilities (`../../utils/index.js`) -5. **Zod validation is required** for all external data -6. **@clack/prompts for all user interaction** - no raw `readline` or `inquirer` -7. **TypeScript strict mode** - maintain type safety -8. **Commander.js for commands** - follow the established pattern -9. **TypeScript compiler for builds** - use `tsc && tsc-alias` for production builds, `tsx` for development -10. **Test commands** after implementation to ensure they're registered -11. **Cross-platform support** - The CLI must work on both Windows and Unix-like systems. Always use `path.join()`, `path.dirname()`, and other `path` module utilities for path operations. Never use string concatenation or hardcoded path separators. -12. **Command wrapper** - All commands must use `runCommand()` utility for consistent Base44 branding -13. **Task wrapper** - Use `runTask()` for async operations that need spinner feedback -14. **ES Modules** - Package uses `"type": "module"` - always use `.js` extensions in import statements -15. **Shared utilities** - Use cross-platform file utilities and config management from `src/core/` -16. **Resource-based organization** - Each resource (auth, entity, function) has its own folder with schema, api, and config files - -## Common Patterns - -### Adding a New Command -1. Create command file in `src/cli/commands//` directory -2. Import and register in main CLI entry point (`src/cli/index.ts`) -3. Use Commander.js Command class -4. Import from appropriate resource module (`@core/auth/index.js`, `@core/entity/index.js`, etc.) -5. Use @clack/prompts for user interaction -6. Use relative imports for CLI utilities (`../../utils/index.js`) -7. Wrap command function with `runCommand()` utility -8. Use `runTask()` for async operations with spinners - -### Adding a New Resource -1. Create a new folder in `src/core//` -2. Add `schema.ts` with Zod schemas and inferred types -3. Add `api.ts` for API client functions (if applicable) -4. Add `config.ts` for file/config reading functions -5. Add `index.ts` barrel file exporting all public APIs -6. Export from `src/core/index.ts` - -### API Integration -1. Define Zod schema in the resource's `schema.ts` file -2. Create API client function in the resource's `api.ts` file -3. Export from the resource's `index.ts` -4. Import in CLI commands using `@core//index.js` -5. Validate response with Zod schema -6. Handle errors gracefully -7. Use `runTask()` for loading states - -### Configuration Management -1. Define Zod schema in `src/core/config/` or the relevant resource folder -2. Create config management functions alongside the schema -3. Export from the module's `index.ts` -4. Import in CLI commands using `@core/config/` or `@core//` -5. Read config file -6. Validate with Zod schema -7. Provide type-safe access via inferred types - -## Dependencies Reference - -### Core (Required) -- `commander` - CLI framework -- `@clack/prompts` - User prompts and UI components -- `chalk` - Terminal colors -- `zod` - Schema validation -- `p-wait-for` - Polling utility for async operations - -### Development -- `typescript` - Language -- `tsx` - TypeScript execution for development mode -- `tsc-alias` - Path alias resolution for compiled output +1. **npm only** - Never use yarn +2. **Zod validation** - Required for all external data +3. **@clack/prompts** - For all user interaction +4. **ES Modules** - Use `.js` extensions in imports +5. **Cross-platform** - Use `path` module utilities, never hardcode separators +6. **Command wrapper** - All commands use `runCommand()` utility +7. **Task wrapper** - Use `runTask()` for async operations with spinners +8. **Keep AGENTS.md updated** - Update this file when architecture changes + +## Development + +```bash +npm run build # tsc && tsc-alias +npm run dev # tsx for development +npm test # vitest +``` ## File Locations -- **Main plan**: `cli/plan.md` - Full implementation plan -- **This file**: `cli/AGENTS.md` - AI agent guidelines -- **Core module**: `cli/src/core/` - Resource modules (auth, entity, function), config, utils, errors -- **CLI module**: `cli/src/cli/` - CLI commands and entry point - -## Questions to Ask - -If uncertain about implementation: -1. Check `plan.md` for feature requirements -2. Verify command name matches `base44 ` pattern -3. Ensure Zod validation is included -4. Confirm @clack/prompts is used for user interaction -5. Check if feature is in current phase scope - -## Notes from Development - -- **Project structure**: Single package with core and cli modules -- **Resource-based architecture**: Each resource (auth, entity, function) has its own folder with schema, api, and config -- CLI uses TypeScript with strict type checking -- All commands must be registered in main CLI entry point (`src/cli/index.ts`) -- Build process compiles TypeScript to JavaScript in `dist/` folder and resolves path aliases -- Commands should be testable independently -- Resource modules (auth, entity, function) go in `src/core//` -- Shared config (project, app) goes in `src/core/config/` -- CLI-specific code (commands, runCommand, runTask) goes in `src/cli/` -- Use `@core/*` path alias for imports from core module -- Use relative imports for CLI-specific utilities -- Error handling should be user-friendly with clear messages -- Use @clack/prompts for all user-facing interactions (no console.log for prompts) -- All commands use `runCommand()` utility for consistent branding -- Use `runTask()` for async operations with spinner feedback -- Package uses ES modules - imports must use `.js` extensions -- Use cross-platform file utilities from `src/core/utils/` for file operations -- All data validation uses Zod schemas with type inference +- `cli/plan.md` - Implementation plan +- `cli/AGENTS.md` - This file +- `cli/src/core/` - Core module +- `cli/src/cli/` - CLI commands diff --git a/src/core/config/index.ts b/src/core/config/index.ts index bc70aa42..1332f29b 100644 --- a/src/core/config/index.ts +++ b/src/core/config/index.ts @@ -1,2 +1,3 @@ +export * from "./resource.js"; export * from "./project.js"; export * from "./app.js"; diff --git a/src/core/config/project.ts b/src/core/config/project.ts index 975b6e44..81dc37a7 100644 --- a/src/core/config/project.ts +++ b/src/core/config/project.ts @@ -3,12 +3,12 @@ import { z } from "zod"; import { globby } from "globby"; import { getProjectConfigPatterns, PROJECT_SUBDIR } from "../consts.js"; import { readJsonFile } from "../utils/fs.js"; -import { readAllEntities } from "../entity/index.js"; -import type { Entity } from "../entity/index.js"; -import { readAllFunctions } from "../function/index.js"; -import type { FunctionConfig } from "../function/index.js"; +import { entityResource, type Entity } from "../resources/entity/index.js"; +import { + functionResource, + type FunctionConfig, +} from "../resources/function/index.js"; -// Project config schema export const ProjectConfigSchema = z.looseObject({ name: z.string().min(1, "Project name cannot be empty"), entitySrc: z.string().default("./entities"), @@ -87,12 +87,10 @@ export async function readProjectConfig( const project = result.data; const configDir = dirname(configPath); - const entitiesPath = join(configDir, project.entitySrc); - const functionsPath = join(configDir, project.functionSrc); const [entities, functions] = await Promise.all([ - readAllEntities(entitiesPath), - readAllFunctions(functionsPath), + entityResource.readAll(join(configDir, project.entitySrc)), + functionResource.readAll(join(configDir, project.functionSrc)), ]); return { diff --git a/src/core/config/resource.ts b/src/core/config/resource.ts new file mode 100644 index 00000000..71685e68 --- /dev/null +++ b/src/core/config/resource.ts @@ -0,0 +1,5 @@ +export interface Resource { + name: string; + readAll(dir: string): Promise; +} + diff --git a/src/core/index.ts b/src/core/index.ts index f0da0433..b5f005df 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,6 +1,5 @@ export * from "./auth/index.js"; -export * from "./entity/index.js"; -export * from "./function/index.js"; +export * from "./resources/index.js"; export * from "./config/index.js"; export * from "./utils/index.js"; export * from "./errors.js"; diff --git a/src/core/entity/config.ts b/src/core/resources/entity/config.ts similarity index 93% rename from src/core/entity/config.ts rename to src/core/resources/entity/config.ts index 9f124b95..37a8fff5 100644 --- a/src/core/entity/config.ts +++ b/src/core/resources/entity/config.ts @@ -1,5 +1,5 @@ import { globby } from "globby"; -import { readJsonFile, pathExists } from "../utils/fs.js"; +import { readJsonFile, pathExists } from "../../utils/fs.js"; import { EntitySchema } from "./schema.js"; import type { Entity } from "./schema.js"; @@ -34,3 +34,4 @@ export async function readAllEntities(entitiesDir: string): Promise { return entities; } + diff --git a/src/core/function/index.ts b/src/core/resources/entity/index.ts similarity index 64% rename from src/core/function/index.ts rename to src/core/resources/entity/index.ts index e3c17ba0..744f74af 100644 --- a/src/core/function/index.ts +++ b/src/core/resources/entity/index.ts @@ -1,2 +1,4 @@ export * from "./schema.js"; export * from "./config.js"; +export * from "./resource.js"; + diff --git a/src/core/resources/entity/resource.ts b/src/core/resources/entity/resource.ts new file mode 100644 index 00000000..973e48fd --- /dev/null +++ b/src/core/resources/entity/resource.ts @@ -0,0 +1,9 @@ +import type { Resource } from "@core/config/resource.js"; +import type { Entity } from "./schema.js"; +import { readAllEntities } from "./config.js"; + +export const entityResource: Resource = { + name: "entity", + readAll: readAllEntities, +}; + diff --git a/src/core/entity/schema.ts b/src/core/resources/entity/schema.ts similarity index 99% rename from src/core/entity/schema.ts rename to src/core/resources/entity/schema.ts index 75a8e3f3..d615c11a 100644 --- a/src/core/entity/schema.ts +++ b/src/core/resources/entity/schema.ts @@ -33,3 +33,4 @@ export const EntitySchema = z.object({ export type EntityProperty = z.infer; export type EntityPolicies = z.infer; export type Entity = z.infer; + diff --git a/src/core/function/config.ts b/src/core/resources/function/config.ts similarity index 89% rename from src/core/function/config.ts rename to src/core/resources/function/config.ts index f6693977..243576e7 100644 --- a/src/core/function/config.ts +++ b/src/core/resources/function/config.ts @@ -1,6 +1,6 @@ import { globby } from "globby"; -import { FUNCTION_CONFIG_FILE } from "../consts.js"; -import { readJsonFile, pathExists } from "../utils/fs.js"; +import { FUNCTION_CONFIG_FILE } from "../../consts.js"; +import { readJsonFile, pathExists } from "../../utils/fs.js"; import { FunctionConfigSchema } from "./schema.js"; import type { FunctionConfig } from "./schema.js"; @@ -39,3 +39,4 @@ export async function readAllFunctions( return functionConfigs; } + diff --git a/src/core/entity/index.ts b/src/core/resources/function/index.ts similarity index 64% rename from src/core/entity/index.ts rename to src/core/resources/function/index.ts index e3c17ba0..744f74af 100644 --- a/src/core/entity/index.ts +++ b/src/core/resources/function/index.ts @@ -1,2 +1,4 @@ export * from "./schema.js"; export * from "./config.js"; +export * from "./resource.js"; + diff --git a/src/core/resources/function/resource.ts b/src/core/resources/function/resource.ts new file mode 100644 index 00000000..7123f491 --- /dev/null +++ b/src/core/resources/function/resource.ts @@ -0,0 +1,9 @@ +import type { Resource } from "@core/config/resource.js"; +import type { FunctionConfig } from "./schema.js"; +import { readAllFunctions } from "./config.js"; + +export const functionResource: Resource = { + name: "function", + readAll: readAllFunctions, +}; + diff --git a/src/core/function/schema.ts b/src/core/resources/function/schema.ts similarity index 99% rename from src/core/function/schema.ts rename to src/core/resources/function/schema.ts index 02b94436..ef3bca0c 100644 --- a/src/core/function/schema.ts +++ b/src/core/resources/function/schema.ts @@ -44,3 +44,4 @@ export type ScheduleTrigger = z.infer; export type EventTrigger = z.infer; export type Trigger = z.infer; export type FunctionConfig = z.infer; + diff --git a/src/core/resources/index.ts b/src/core/resources/index.ts new file mode 100644 index 00000000..f0e58de4 --- /dev/null +++ b/src/core/resources/index.ts @@ -0,0 +1,3 @@ +export * from "./entity/index.js"; +export * from "./function/index.js"; + From f7b42461d096373a10b71b29f9963d330e669c53 Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 7 Jan 2026 14:55:07 +0200 Subject: [PATCH 4/6] lint issues --- src/core/config/index.ts | 2 +- src/core/config/project.ts | 9 ++++----- src/core/config/resource.ts | 4 +--- src/core/resources/entity/resource.ts | 2 -- src/core/resources/function/resource.ts | 2 -- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/core/config/index.ts b/src/core/config/index.ts index 1332f29b..efda2993 100644 --- a/src/core/config/index.ts +++ b/src/core/config/index.ts @@ -1,3 +1,3 @@ -export * from "./resource.js"; +export type * from "./resource.js"; export * from "./project.js"; export * from "./app.js"; diff --git a/src/core/config/project.ts b/src/core/config/project.ts index 81dc37a7..5e8ef247 100644 --- a/src/core/config/project.ts +++ b/src/core/config/project.ts @@ -3,11 +3,10 @@ import { z } from "zod"; import { globby } from "globby"; import { getProjectConfigPatterns, PROJECT_SUBDIR } from "../consts.js"; import { readJsonFile } from "../utils/fs.js"; -import { entityResource, type Entity } from "../resources/entity/index.js"; -import { - functionResource, - type FunctionConfig, -} from "../resources/function/index.js"; +import { entityResource } from "../resources/entity/index.js"; +import type { Entity } from "../resources/entity/index.js"; +import { functionResource } from "../resources/function/index.js"; +import type { FunctionConfig } from "../resources/function/index.js"; export const ProjectConfigSchema = z.looseObject({ name: z.string().min(1, "Project name cannot be empty"), diff --git a/src/core/config/resource.ts b/src/core/config/resource.ts index 71685e68..da19f94b 100644 --- a/src/core/config/resource.ts +++ b/src/core/config/resource.ts @@ -1,5 +1,3 @@ export interface Resource { - name: string; - readAll(dir: string): Promise; + readAll: (dir: string) => Promise; } - diff --git a/src/core/resources/entity/resource.ts b/src/core/resources/entity/resource.ts index 973e48fd..0323a437 100644 --- a/src/core/resources/entity/resource.ts +++ b/src/core/resources/entity/resource.ts @@ -3,7 +3,5 @@ import type { Entity } from "./schema.js"; import { readAllEntities } from "./config.js"; export const entityResource: Resource = { - name: "entity", readAll: readAllEntities, }; - diff --git a/src/core/resources/function/resource.ts b/src/core/resources/function/resource.ts index 7123f491..9382f230 100644 --- a/src/core/resources/function/resource.ts +++ b/src/core/resources/function/resource.ts @@ -3,7 +3,5 @@ import type { FunctionConfig } from "./schema.js"; import { readAllFunctions } from "./config.js"; export const functionResource: Resource = { - name: "function", readAll: readAllFunctions, }; - From 720ff85d29a183e76cff386a232e4ff6f5f5a1fa Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 7 Jan 2026 15:05:50 +0200 Subject: [PATCH 5/6] small eslint rule --- eslint.config.mjs | 8 +++++ package-lock.json | 48 ++++++++++++++++++++++++++++++ package.json | 1 + src/core/resources/entity/index.ts | 1 - src/core/resources/index.ts | 1 - 5 files changed, 57 insertions(+), 2 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 47e408a8..9d143128 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,6 +1,7 @@ import tseslint from "typescript-eslint"; import importPlugin from "eslint-plugin-import"; import unicornPlugin from "eslint-plugin-unicorn"; +import stylistic from "@stylistic/eslint-plugin"; export default tseslint.config( { @@ -21,6 +22,7 @@ export default tseslint.config( "@typescript-eslint": tseslint.plugin, import: importPlugin, unicorn: unicornPlugin, + "@stylistic": stylistic, }, rules: { // @@ -109,6 +111,12 @@ export default tseslint.config( "unicorn/no-lonely-if": "error", "unicorn/no-typeof-undefined": "error", "unicorn/prefer-array-some": "error", + + // + // Stylistic + // + + "@stylistic/no-trailing-spaces": "error", }, } ); diff --git a/package-lock.json b/package-lock.json index 67c7586c..ab6a1eb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "base44": "dist/cli/index.js" }, "devDependencies": { + "@stylistic/eslint-plugin": "^5.6.1", "@types/node": "^22.10.5", "@typescript-eslint/eslint-plugin": "^8.51.0", "@typescript-eslint/parser": "^8.51.0", @@ -1090,6 +1091,53 @@ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.6.1.tgz", + "integrity": "sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.0", + "@typescript-eslint/types": "^8.47.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", diff --git a/package.json b/package.json index df918457..7b8c2452 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "zod": "^4.3.5" }, "devDependencies": { + "@stylistic/eslint-plugin": "^5.6.1", "@types/node": "^22.10.5", "@typescript-eslint/eslint-plugin": "^8.51.0", "@typescript-eslint/parser": "^8.51.0", diff --git a/src/core/resources/entity/index.ts b/src/core/resources/entity/index.ts index 744f74af..c061bbba 100644 --- a/src/core/resources/entity/index.ts +++ b/src/core/resources/entity/index.ts @@ -1,4 +1,3 @@ export * from "./schema.js"; export * from "./config.js"; export * from "./resource.js"; - diff --git a/src/core/resources/index.ts b/src/core/resources/index.ts index f0e58de4..7a55108f 100644 --- a/src/core/resources/index.ts +++ b/src/core/resources/index.ts @@ -1,3 +1,2 @@ export * from "./entity/index.js"; export * from "./function/index.js"; - From a5aa793f01dc6b5ccf8e5559401c74da48bb4e9b Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 7 Jan 2026 15:14:39 +0200 Subject: [PATCH 6/6] no name for resource --- AGENTS.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e72aa193..4c932d93 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,7 +67,6 @@ Resources are project-specific collections (entities, functions) that can be loa ### Resource Interface (`config/resource.ts`) ```typescript export interface Resource { - name: string; readAll(dir: string): Promise; } ``` @@ -75,7 +74,6 @@ export interface Resource { ### Resource Implementation (`resources//resource.ts`) ```typescript export const entityResource: Resource = { - name: "entity", readAll: readAllEntities, }; ```