From d904c04c2d9c6b7bfe41174ed202c6ae309175a7 Mon Sep 17 00:00:00 2001 From: defiQUG Date: Mon, 9 Feb 2026 21:51:55 -0800 Subject: [PATCH] Initial commit: add .gitignore and README --- .changeset/config.json | 12 ++ .gitignore | 49 ++++++++ .npmrc | 10 ++ README.md | 142 +++++++++++++++++++++++ package.json | 28 +++++ packages/api-client/package.json | 29 +++++ packages/api-client/src/client.ts | 32 +++++ packages/api-client/src/index.ts | 9 ++ packages/api-client/src/interceptors.ts | 56 +++++++++ packages/api-client/src/types.ts | 21 ++++ packages/api-client/tsconfig.json | 20 ++++ packages/blockchain/package.json | 29 +++++ packages/blockchain/src/contracts.ts | 30 +++++ packages/blockchain/src/ethers.ts | 51 ++++++++ packages/blockchain/src/index.ts | 10 ++ packages/blockchain/src/transactions.ts | 45 +++++++ packages/blockchain/src/types.ts | 25 ++++ packages/blockchain/tsconfig.json | 20 ++++ packages/shared-auth/package.json | 31 +++++ packages/shared-auth/src/index.ts | 10 ++ packages/shared-auth/src/jwt.ts | 55 +++++++++ packages/shared-auth/src/password.ts | 25 ++++ packages/shared-auth/src/permissions.ts | 63 ++++++++++ packages/shared-auth/src/types.ts | 23 ++++ packages/shared-auth/tsconfig.json | 20 ++++ packages/shared-config/package.json | 29 +++++ packages/shared-config/src/env.ts | 55 +++++++++ packages/shared-config/src/index.ts | 9 ++ packages/shared-config/src/schemas.ts | 37 ++++++ packages/shared-config/src/validation.ts | 27 +++++ packages/shared-config/tsconfig.json | 20 ++++ packages/shared-types/package.json | 25 ++++ packages/shared-types/src/api.ts | 29 +++++ packages/shared-types/src/config.ts | 33 ++++++ packages/shared-types/src/database.ts | 20 ++++ packages/shared-types/src/index.ts | 54 +++++++++ packages/shared-types/tsconfig.json | 20 ++++ packages/shared-utils/package.json | 30 +++++ packages/shared-utils/src/date.ts | 35 ++++++ packages/shared-utils/src/index.ts | 10 ++ packages/shared-utils/src/string.ts | 58 +++++++++ packages/shared-utils/src/uuid.ts | 20 ++++ packages/shared-utils/src/validation.ts | 49 ++++++++ packages/shared-utils/tsconfig.json | 20 ++++ packages/validation/package.json | 28 +++++ packages/validation/src/index.ts | 8 ++ packages/validation/src/schemas.ts | 37 ++++++ packages/validation/src/validators.ts | 27 +++++ packages/validation/tsconfig.json | 20 ++++ pnpm-workspace.yaml | 5 + scripts/publish.sh | 39 +++++++ turbo.json | 24 ++++ 52 files changed, 1613 insertions(+) create mode 100644 .changeset/config.json create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 README.md create mode 100644 package.json create mode 100644 packages/api-client/package.json create mode 100644 packages/api-client/src/client.ts create mode 100644 packages/api-client/src/index.ts create mode 100644 packages/api-client/src/interceptors.ts create mode 100644 packages/api-client/src/types.ts create mode 100644 packages/api-client/tsconfig.json create mode 100644 packages/blockchain/package.json create mode 100644 packages/blockchain/src/contracts.ts create mode 100644 packages/blockchain/src/ethers.ts create mode 100644 packages/blockchain/src/index.ts create mode 100644 packages/blockchain/src/transactions.ts create mode 100644 packages/blockchain/src/types.ts create mode 100644 packages/blockchain/tsconfig.json create mode 100644 packages/shared-auth/package.json create mode 100644 packages/shared-auth/src/index.ts create mode 100644 packages/shared-auth/src/jwt.ts create mode 100644 packages/shared-auth/src/password.ts create mode 100644 packages/shared-auth/src/permissions.ts create mode 100644 packages/shared-auth/src/types.ts create mode 100644 packages/shared-auth/tsconfig.json create mode 100644 packages/shared-config/package.json create mode 100644 packages/shared-config/src/env.ts create mode 100644 packages/shared-config/src/index.ts create mode 100644 packages/shared-config/src/schemas.ts create mode 100644 packages/shared-config/src/validation.ts create mode 100644 packages/shared-config/tsconfig.json create mode 100644 packages/shared-types/package.json create mode 100644 packages/shared-types/src/api.ts create mode 100644 packages/shared-types/src/config.ts create mode 100644 packages/shared-types/src/database.ts create mode 100644 packages/shared-types/src/index.ts create mode 100644 packages/shared-types/tsconfig.json create mode 100644 packages/shared-utils/package.json create mode 100644 packages/shared-utils/src/date.ts create mode 100644 packages/shared-utils/src/index.ts create mode 100644 packages/shared-utils/src/string.ts create mode 100644 packages/shared-utils/src/uuid.ts create mode 100644 packages/shared-utils/src/validation.ts create mode 100644 packages/shared-utils/tsconfig.json create mode 100644 packages/validation/package.json create mode 100644 packages/validation/src/index.ts create mode 100644 packages/validation/src/schemas.ts create mode 100644 packages/validation/src/validators.ts create mode 100644 packages/validation/tsconfig.json create mode 100644 pnpm-workspace.yaml create mode 100755 scripts/publish.sh create mode 100644 turbo.json diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..83078e9 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c64e16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Dependencies +node_modules/ +.pnpm-store/ +vendor/ + +# Package manager lock files (optional: uncomment to ignore) +# package-lock.json +# yarn.lock + +# Environment and secrets +.env +.env.local +.env.*.local +*.env.backup +.env.backup.* + +# Logs and temp +*.log +logs/ +*.tmp +*.temp +*.tmp.* + +# OS +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Build / output +dist/ +build/ +.next/ +out/ +*.pyc +__pycache__/ +.eggs/ +*.egg-info/ +.coverage +htmlcov/ + +# Optional +.reports/ +reports/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..7864230 --- /dev/null +++ b/.npmrc @@ -0,0 +1,10 @@ +# Private npm registry configuration +# Uncomment and configure when registry is set up + +# @workspace:registry=http://verdaccio:4873/ +# //verdaccio:4873/:_authToken=${NPM_TOKEN} + +# Or for GitHub Packages: +# @workspace:registry=https://npm.pkg.github.com +# //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} + diff --git a/README.md b/README.md new file mode 100644 index 0000000..c14001e --- /dev/null +++ b/README.md @@ -0,0 +1,142 @@ +# Workspace Shared Packages + +**Status**: 🚧 **In Development** +**Purpose**: Shared packages and libraries for workspace projects +**Last Updated**: 2025-01-27 + +--- + +## Overview + +This monorepo contains shared packages and libraries used across multiple projects in the workspace. It enables code reuse, consistent patterns, and simplified dependency management. + +--- + +## Packages + +### @workspace/shared-types +**Status**: 🚧 Planned +**Purpose**: Common TypeScript types and interfaces +**Usage**: Used across dbis_core, the_order, Sankofa, and others + +### @workspace/shared-utils +**Status**: 🚧 Planned +**Purpose**: Common utility functions +**Usage**: Used in 20+ projects + +### @workspace/shared-config +**Status**: 🚧 Planned +**Purpose**: Shared configuration schemas and validation +**Usage**: All projects with configuration + +### @workspace/shared-constants +**Status**: 🚧 Planned +**Purpose**: Shared constants and enums +**Usage**: DBIS projects, DeFi projects + +### @workspace/api-client +**Status**: 🚧 Planned +**Purpose**: Common API client utilities +**Usage**: Frontend projects, API consumers + +### @workspace/validation +**Status**: 🚧 Planned +**Purpose**: Zod schemas and validators +**Usage**: Multiple backend services + +### @workspace/blockchain +**Status**: 🚧 Planned +**Purpose**: Blockchain utilities and helpers +**Usage**: Blockchain projects + +--- + +## Getting Started + +### Prerequisites +- Node.js >= 18.0.0 +- pnpm >= 8.0.0 + +### Installation + +```bash +# Install dependencies +pnpm install + +# Build all packages +pnpm build + +# Run tests +pnpm test + +# Lint all packages +pnpm lint +``` + +### Using Packages + +In your project's `package.json`: + +```json +{ + "dependencies": { + "@workspace/shared-types": "workspace:*", + "@workspace/shared-utils": "workspace:*" + } +} +``` + +--- + +## Development + +### Creating a New Package + +1. Create package directory: `packages/package-name/` +2. Add `package.json` with proper name (`@workspace/package-name`) +3. Implement package code +4. Add to workspace configuration +5. Build and test + +### Publishing + +Packages are published to private npm registry (Verdaccio or GitHub Packages). + +```bash +# Build package +cd packages/package-name +pnpm build + +# Publish +pnpm publish --registry= +``` + +--- + +## Structure + +``` +workspace-shared/ +├── packages/ # Shared packages +│ ├── shared-types/ +│ ├── shared-utils/ +│ ├── shared-config/ +│ └── ... +├── apps/ # Shared applications (if any) +├── tools/ # Development tools +├── package.json # Root package.json +├── pnpm-workspace.yaml +└── turbo.json # Turborepo configuration +``` + +--- + +## Related Documents + +- [Dependency Consolidation Plan](../docs/DEPENDENCY_CONSOLIDATION_PLAN.md) +- [Integration & Streamlining Plan](../INTEGRATION_STREAMLINING_PLAN.md) + +--- + +**Last Updated**: 2025-01-27 + diff --git a/package.json b/package.json new file mode 100644 index 0000000..132eb55 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "workspace-shared", + "version": "1.0.0", + "private": true, + "description": "Shared packages and libraries for workspace projects", + "scripts": { + "build": "turbo run build", + "test": "turbo run test", + "lint": "turbo run lint", + "type-check": "turbo run type-check", + "clean": "turbo run clean" + }, + "devDependencies": { + "turbo": "^2.0.0", + "typescript": "^5.5.4", + "@types/node": "^20.11.0", + "prettier": "^3.3.3", + "eslint": "^9.17.0", + "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/eslint-plugin": "^7.18.0" + }, + "engines": { + "node": ">=18.0.0", + "pnpm": ">=8.0.0" + }, + "packageManager": "pnpm@8.15.0" +} + diff --git a/packages/api-client/package.json b/packages/api-client/package.json new file mode 100644 index 0000000..1407663 --- /dev/null +++ b/packages/api-client/package.json @@ -0,0 +1,29 @@ +{ + "name": "@workspace/api-client", + "version": "1.0.0", + "description": "Common API client utilities", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "lint": "eslint src", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "axios": "^1.6.5", + "@workspace/shared-types": "workspace:*" + }, + "devDependencies": { + "typescript": "^5.5.4", + "vitest": "^1.2.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "restricted" + } +} + diff --git a/packages/api-client/src/client.ts b/packages/api-client/src/client.ts new file mode 100644 index 0000000..8bc9312 --- /dev/null +++ b/packages/api-client/src/client.ts @@ -0,0 +1,32 @@ +/** + * API client factory + */ + +import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; +import { setupInterceptors } from './interceptors'; + +export interface ApiClientConfig extends AxiosRequestConfig { + baseURL: string; + timeout?: number; + retries?: number; +} + +/** + * Create configured API client + */ +export function createApiClient(config: ApiClientConfig): AxiosInstance { + const client = axios.create({ + baseURL: config.baseURL, + timeout: config.timeout || 30000, + headers: { + 'Content-Type': 'application/json', + ...config.headers, + }, + }); + + // Setup interceptors + setupInterceptors(client, config.retries || 3); + + return client; +} + diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts new file mode 100644 index 0000000..13e9eac --- /dev/null +++ b/packages/api-client/src/index.ts @@ -0,0 +1,9 @@ +/** + * @workspace/api-client + * Common API client utilities + */ + +export * from './client'; +export * from './interceptors'; +export * from './types'; + diff --git a/packages/api-client/src/interceptors.ts b/packages/api-client/src/interceptors.ts new file mode 100644 index 0000000..bee7cec --- /dev/null +++ b/packages/api-client/src/interceptors.ts @@ -0,0 +1,56 @@ +/** + * Axios interceptors + */ + +import { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios'; + +/** + * Setup request and response interceptors + */ +export function setupInterceptors(client: AxiosInstance, maxRetries: number = 3): void { + // Request interceptor + client.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + // Add auth token if available + const token = process.env.API_TOKEN; + if (token && config.headers) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } + ); + + // Response interceptor with retry logic + client.interceptors.response.use( + (response) => response, + async (error: AxiosError) => { + const config = error.config as InternalAxiosRequestConfig & { __retryCount?: number }; + + if (!config) { + return Promise.reject(error); + } + + config.__retryCount = config.__retryCount || 0; + + // Retry on network errors or 5xx errors + if ( + (error.code === 'ECONNABORTED' || (error.response?.status && error.response.status >= 500)) && + config.__retryCount < maxRetries + ) { + config.__retryCount += 1; + + // Exponential backoff + const delay = Math.pow(2, config.__retryCount) * 1000; + await new Promise((resolve) => setTimeout(resolve, delay)); + + return client(config); + } + + return Promise.reject(error); + } + ); +} + diff --git a/packages/api-client/src/types.ts b/packages/api-client/src/types.ts new file mode 100644 index 0000000..6542d75 --- /dev/null +++ b/packages/api-client/src/types.ts @@ -0,0 +1,21 @@ +/** + * API client types + */ + +import { AxiosRequestConfig, AxiosResponse } from 'axios'; + +export interface ApiRequestConfig extends AxiosRequestConfig { + retries?: number; +} + +export interface ApiResponse extends AxiosResponse { + data: T; +} + +export interface ApiError { + message: string; + code?: string; + status?: number; + details?: unknown; +} + diff --git a/packages/api-client/tsconfig.json b/packages/api-client/tsconfig.json new file mode 100644 index 0000000..0da5816 --- /dev/null +++ b/packages/api-client/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} + diff --git a/packages/blockchain/package.json b/packages/blockchain/package.json new file mode 100644 index 0000000..3b3d15e --- /dev/null +++ b/packages/blockchain/package.json @@ -0,0 +1,29 @@ +{ + "name": "@workspace/blockchain", + "version": "1.0.0", + "description": "Blockchain utilities and helpers", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "lint": "eslint src", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "ethers": "^6.9.0", + "@workspace/shared-types": "workspace:*" + }, + "devDependencies": { + "typescript": "^5.5.4", + "vitest": "^1.2.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "restricted" + } +} + diff --git a/packages/blockchain/src/contracts.ts b/packages/blockchain/src/contracts.ts new file mode 100644 index 0000000..978b4d5 --- /dev/null +++ b/packages/blockchain/src/contracts.ts @@ -0,0 +1,30 @@ +/** + * Contract utilities + */ + +import { ethers } from 'ethers'; + +/** + * Get contract instance + */ +export function getContract( + address: string, + abi: ethers.InterfaceAbi, + signerOrProvider: ethers.Signer | ethers.Provider +): ethers.Contract { + return new ethers.Contract(address, abi, signerOrProvider); +} + +/** + * Deploy contract + */ +export async function deployContract( + signer: ethers.Signer, + contractFactory: ethers.ContractFactory, + ...args: unknown[] +): Promise { + const contract = await contractFactory.deploy(...args); + await contract.waitForDeployment(); + return contract; +} + diff --git a/packages/blockchain/src/ethers.ts b/packages/blockchain/src/ethers.ts new file mode 100644 index 0000000..aa4da94 --- /dev/null +++ b/packages/blockchain/src/ethers.ts @@ -0,0 +1,51 @@ +/** + * Ethers.js utilities + */ + +import { ethers } from 'ethers'; + +/** + * Create provider from RPC URL + */ +export function createProvider(rpcUrl: string): ethers.JsonRpcProvider { + return new ethers.JsonRpcProvider(rpcUrl); +} + +/** + * Create wallet from private key + */ +export function createWallet( + privateKey: string, + provider?: ethers.Provider +): ethers.Wallet { + return new ethers.Wallet(privateKey, provider); +} + +/** + * Format ether value + */ +export function formatEther(value: bigint): string { + return ethers.formatEther(value); +} + +/** + * Parse ether value + */ +export function parseEther(value: string): bigint { + return ethers.parseEther(value); +} + +/** + * Format units + */ +export function formatUnits(value: bigint, decimals: number = 18): string { + return ethers.formatUnits(value, decimals); +} + +/** + * Parse units + */ +export function parseUnits(value: string, decimals: number = 18): bigint { + return ethers.parseUnits(value, decimals); +} + diff --git a/packages/blockchain/src/index.ts b/packages/blockchain/src/index.ts new file mode 100644 index 0000000..0036e99 --- /dev/null +++ b/packages/blockchain/src/index.ts @@ -0,0 +1,10 @@ +/** + * @workspace/blockchain + * Blockchain utilities and helpers + */ + +export * from './ethers'; +export * from './transactions'; +export * from './contracts'; +export * from './types'; + diff --git a/packages/blockchain/src/transactions.ts b/packages/blockchain/src/transactions.ts new file mode 100644 index 0000000..75af124 --- /dev/null +++ b/packages/blockchain/src/transactions.ts @@ -0,0 +1,45 @@ +/** + * Transaction utilities + */ + +import { ethers } from 'ethers'; + +export interface TransactionOptions { + gasLimit?: bigint; + gasPrice?: bigint; + maxFeePerGas?: bigint; + maxPriorityFeePerGas?: bigint; + value?: bigint; +} + +/** + * Wait for transaction confirmation + */ +export async function waitForTransaction( + provider: ethers.Provider, + txHash: string, + confirmations: number = 1 +): Promise { + return provider.waitForTransaction(txHash, confirmations); +} + +/** + * Get transaction receipt + */ +export async function getTransactionReceipt( + provider: ethers.Provider, + txHash: string +): Promise { + return provider.getTransactionReceipt(txHash); +} + +/** + * Estimate gas for transaction + */ +export async function estimateGas( + provider: ethers.Provider, + transaction: ethers.TransactionRequest +): Promise { + return provider.estimateGas(transaction); +} + diff --git a/packages/blockchain/src/types.ts b/packages/blockchain/src/types.ts new file mode 100644 index 0000000..0b2539c --- /dev/null +++ b/packages/blockchain/src/types.ts @@ -0,0 +1,25 @@ +/** + * Blockchain types + */ + +export interface NetworkConfig { + name: string; + chainId: number; + rpcUrl: string; + explorerUrl?: string; +} + +export interface TokenInfo { + address: string; + symbol: string; + name: string; + decimals: number; +} + +export interface TransactionStatus { + hash: string; + status: 'pending' | 'confirmed' | 'failed'; + blockNumber?: number; + confirmations: number; +} + diff --git a/packages/blockchain/tsconfig.json b/packages/blockchain/tsconfig.json new file mode 100644 index 0000000..0da5816 --- /dev/null +++ b/packages/blockchain/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} + diff --git a/packages/shared-auth/package.json b/packages/shared-auth/package.json new file mode 100644 index 0000000..fa3d87f --- /dev/null +++ b/packages/shared-auth/package.json @@ -0,0 +1,31 @@ +{ + "name": "@workspace/shared-auth", + "version": "1.0.0", + "description": "Shared authentication and authorization utilities", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "lint": "eslint src", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "jsonwebtoken": "^9.0.2", + "bcryptjs": "^2.4.3" + }, + "devDependencies": { + "typescript": "^5.5.4", + "@types/jsonwebtoken": "^9.0.4", + "@types/bcryptjs": "^2.4.6", + "vitest": "^1.2.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "restricted" + } +} + diff --git a/packages/shared-auth/src/index.ts b/packages/shared-auth/src/index.ts new file mode 100644 index 0000000..3921100 --- /dev/null +++ b/packages/shared-auth/src/index.ts @@ -0,0 +1,10 @@ +/** + * @workspace/shared-auth + * Shared authentication and authorization utilities + */ + +export * from './jwt'; +export * from './password'; +export * from './permissions'; +export * from './types'; + diff --git a/packages/shared-auth/src/jwt.ts b/packages/shared-auth/src/jwt.ts new file mode 100644 index 0000000..08e8bf0 --- /dev/null +++ b/packages/shared-auth/src/jwt.ts @@ -0,0 +1,55 @@ +/** + * JWT token utilities + */ + +import jwt from 'jsonwebtoken'; + +export interface TokenPayload { + userId: string; + email: string; + roles?: string[]; + [key: string]: unknown; +} + +export interface TokenOptions { + expiresIn?: string | number; + issuer?: string; + audience?: string; +} + +/** + * Generate JWT token + */ +export function generateToken( + payload: TokenPayload, + secret: string, + options?: TokenOptions +): string { + return jwt.sign(payload, secret, { + expiresIn: options?.expiresIn || '24h', + issuer: options?.issuer, + audience: options?.audience, + }); +} + +/** + * Verify JWT token + */ +export function verifyToken( + token: string, + secret: string +): T { + return jwt.verify(token, secret) as T; +} + +/** + * Decode JWT token (without verification) + */ +export function decodeToken(token: string): T | null { + try { + return jwt.decode(token) as T; + } catch { + return null; + } +} + diff --git a/packages/shared-auth/src/password.ts b/packages/shared-auth/src/password.ts new file mode 100644 index 0000000..c83c098 --- /dev/null +++ b/packages/shared-auth/src/password.ts @@ -0,0 +1,25 @@ +/** + * Password hashing utilities + */ + +import bcrypt from 'bcryptjs'; + +const SALT_ROUNDS = 10; + +/** + * Hash password + */ +export async function hashPassword(password: string): Promise { + return bcrypt.hash(password, SALT_ROUNDS); +} + +/** + * Verify password + */ +export async function verifyPassword( + password: string, + hash: string +): Promise { + return bcrypt.compare(password, hash); +} + diff --git a/packages/shared-auth/src/permissions.ts b/packages/shared-auth/src/permissions.ts new file mode 100644 index 0000000..f01d6ab --- /dev/null +++ b/packages/shared-auth/src/permissions.ts @@ -0,0 +1,63 @@ +/** + * Permission checking utilities + */ + +export type Permission = string; +export type Role = string; + +export interface UserPermissions { + roles: Role[]; + permissions: Permission[]; +} + +/** + * Check if user has role + */ +export function hasRole(user: UserPermissions, role: Role): boolean { + return user.roles.includes(role); +} + +/** + * Check if user has permission + */ +export function hasPermission( + user: UserPermissions, + permission: Permission +): boolean { + return user.permissions.includes(permission); +} + +/** + * Check if user has any of the required roles + */ +export function hasAnyRole(user: UserPermissions, roles: Role[]): boolean { + return roles.some((role) => hasRole(user, role)); +} + +/** + * Check if user has all required roles + */ +export function hasAllRoles(user: UserPermissions, roles: Role[]): boolean { + return roles.every((role) => hasRole(user, role)); +} + +/** + * Check if user has any of the required permissions + */ +export function hasAnyPermission( + user: UserPermissions, + permissions: Permission[] +): boolean { + return permissions.some((permission) => hasPermission(user, permission)); +} + +/** + * Check if user has all required permissions + */ +export function hasAllPermissions( + user: UserPermissions, + permissions: Permission[] +): boolean { + return permissions.every((permission) => hasPermission(user, permission)); +} + diff --git a/packages/shared-auth/src/types.ts b/packages/shared-auth/src/types.ts new file mode 100644 index 0000000..a8a2418 --- /dev/null +++ b/packages/shared-auth/src/types.ts @@ -0,0 +1,23 @@ +/** + * Authentication and authorization types + */ + +export interface User { + id: string; + email: string; + roles: string[]; + permissions: string[]; +} + +export interface AuthContext { + user: User | null; + isAuthenticated: boolean; +} + +export interface Session { + userId: string; + token: string; + expiresAt: Date; + refreshToken?: string; +} + diff --git a/packages/shared-auth/tsconfig.json b/packages/shared-auth/tsconfig.json new file mode 100644 index 0000000..0da5816 --- /dev/null +++ b/packages/shared-auth/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} + diff --git a/packages/shared-config/package.json b/packages/shared-config/package.json new file mode 100644 index 0000000..c5304ff --- /dev/null +++ b/packages/shared-config/package.json @@ -0,0 +1,29 @@ +{ + "name": "@workspace/shared-config", + "version": "1.0.0", + "description": "Shared configuration schemas and validation", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "lint": "eslint src", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "zod": "^3.23.8", + "dotenv": "^16.4.1" + }, + "devDependencies": { + "typescript": "^5.5.4", + "vitest": "^1.2.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "restricted" + } +} + diff --git a/packages/shared-config/src/env.ts b/packages/shared-config/src/env.ts new file mode 100644 index 0000000..87c46b2 --- /dev/null +++ b/packages/shared-config/src/env.ts @@ -0,0 +1,55 @@ +/** + * Environment variable utilities + */ + +import { config } from 'dotenv'; +import { z } from 'zod'; + +/** + * Load environment variables + */ +export function loadEnv(envFile?: string): void { + config({ path: envFile }); +} + +/** + * Get environment variable with default + */ +export function getEnv(key: string, defaultValue?: string): string { + const value = process.env[key]; + if (value === undefined) { + if (defaultValue !== undefined) { + return defaultValue; + } + throw new Error(`Environment variable ${key} is not set`); + } + return value; +} + +/** + * Get environment variable as number + */ +export function getEnvNumber(key: string, defaultValue?: number): number { + const value = getEnv(key, defaultValue?.toString()); + const num = Number(value); + if (isNaN(num)) { + throw new Error(`Environment variable ${key} is not a valid number`); + } + return num; +} + +/** + * Get environment variable as boolean + */ +export function getEnvBoolean(key: string, defaultValue?: boolean): boolean { + const value = getEnv(key, defaultValue?.toString()); + return value.toLowerCase() === 'true' || value === '1'; +} + +/** + * Validate environment variables against schema + */ +export function validateEnv(schema: z.ZodSchema): T { + return schema.parse(process.env); +} + diff --git a/packages/shared-config/src/index.ts b/packages/shared-config/src/index.ts new file mode 100644 index 0000000..e971801 --- /dev/null +++ b/packages/shared-config/src/index.ts @@ -0,0 +1,9 @@ +/** + * @workspace/shared-config + * Shared configuration schemas and validation + */ + +export * from './env'; +export * from './schemas'; +export * from './validation'; + diff --git a/packages/shared-config/src/schemas.ts b/packages/shared-config/src/schemas.ts new file mode 100644 index 0000000..73ecba8 --- /dev/null +++ b/packages/shared-config/src/schemas.ts @@ -0,0 +1,37 @@ +/** + * Configuration schemas + */ + +import { z } from 'zod'; + +/** + * Database configuration schema + */ +export const databaseConfigSchema = z.object({ + DB_HOST: z.string(), + DB_PORT: z.string().transform(Number), + DB_NAME: z.string(), + DB_USER: z.string(), + DB_PASSWORD: z.string(), + DB_SSL: z.string().optional().transform((val) => val === 'true'), +}); + +/** + * Server configuration schema + */ +export const serverConfigSchema = z.object({ + PORT: z.string().transform(Number).default('3000'), + NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'), + HOST: z.string().default('localhost'), +}); + +/** + * JWT configuration schema + */ +export const jwtConfigSchema = z.object({ + JWT_SECRET: z.string(), + JWT_EXPIRES_IN: z.string().default('24h'), + JWT_ISSUER: z.string().optional(), + JWT_AUDIENCE: z.string().optional(), +}); + diff --git a/packages/shared-config/src/validation.ts b/packages/shared-config/src/validation.ts new file mode 100644 index 0000000..da7a504 --- /dev/null +++ b/packages/shared-config/src/validation.ts @@ -0,0 +1,27 @@ +/** + * Configuration validation utilities + */ + +import { z } from 'zod'; + +/** + * Validate configuration object + */ +export function validateConfig(schema: z.ZodSchema, config: unknown): T { + return schema.parse(config); +} + +/** + * Validate configuration with error handling + */ +export function validateConfigSafe( + schema: z.ZodSchema, + config: unknown +): { success: true; data: T } | { success: false; error: z.ZodError } { + const result = schema.safeParse(config); + if (result.success) { + return { success: true, data: result.data }; + } + return { success: false, error: result.error }; +} + diff --git a/packages/shared-config/tsconfig.json b/packages/shared-config/tsconfig.json new file mode 100644 index 0000000..0da5816 --- /dev/null +++ b/packages/shared-config/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} + diff --git a/packages/shared-types/package.json b/packages/shared-types/package.json new file mode 100644 index 0000000..b4143b9 --- /dev/null +++ b/packages/shared-types/package.json @@ -0,0 +1,25 @@ +{ + "name": "@workspace/shared-types", + "version": "1.0.0", + "description": "Common TypeScript types and interfaces", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "lint": "eslint src", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "devDependencies": { + "typescript": "^5.5.4", + "vitest": "^1.2.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "restricted" + } +} + diff --git a/packages/shared-types/src/api.ts b/packages/shared-types/src/api.ts new file mode 100644 index 0000000..30b23da --- /dev/null +++ b/packages/shared-types/src/api.ts @@ -0,0 +1,29 @@ +/** + * API-related types + */ + +export interface PaginationParams { + page: number; + limit: number; + offset?: number; +} + +export interface PaginatedResponse { + data: T[]; + pagination: { + page: number; + limit: number; + total: number; + totalPages: number; + }; +} + +export interface SortParams { + field: string; + order: 'asc' | 'desc'; +} + +export interface FilterParams { + [key: string]: unknown; +} + diff --git a/packages/shared-types/src/config.ts b/packages/shared-types/src/config.ts new file mode 100644 index 0000000..08c8bef --- /dev/null +++ b/packages/shared-types/src/config.ts @@ -0,0 +1,33 @@ +/** + * Configuration-related types + */ + +export interface DatabaseConfig { + host: string; + port: number; + database: string; + username: string; + password: string; + ssl?: boolean; + pool?: { + min?: number; + max?: number; + }; +} + +export interface ServerConfig { + port: number; + host: string; + env: 'development' | 'staging' | 'production'; + cors?: { + origin: string | string[]; + credentials?: boolean; + }; +} + +export interface LoggingConfig { + level: 'debug' | 'info' | 'warn' | 'error'; + format: 'json' | 'text'; + destination?: string; +} + diff --git a/packages/shared-types/src/database.ts b/packages/shared-types/src/database.ts new file mode 100644 index 0000000..8972ef9 --- /dev/null +++ b/packages/shared-types/src/database.ts @@ -0,0 +1,20 @@ +/** + * Database-related types + */ + +export interface BaseEntity { + id: string; + createdAt: Date; + updatedAt: Date; + deletedAt?: Date | null; +} + +export interface SoftDeletable { + deletedAt: Date | null; +} + +export interface Timestamped { + createdAt: Date; + updatedAt: Date; +} + diff --git a/packages/shared-types/src/index.ts b/packages/shared-types/src/index.ts new file mode 100644 index 0000000..ea7f62a --- /dev/null +++ b/packages/shared-types/src/index.ts @@ -0,0 +1,54 @@ +/** + * @workspace/shared-types + * Common TypeScript types and interfaces + */ + +// API Response Types +export interface ApiResponse { + data: T; + status: number; + message?: string; +} + +export interface ApiError { + code: string; + message: string; + details?: unknown; +} + +// Database Types +export interface BaseEntity { + id: string; + createdAt: Date; + updatedAt: Date; +} + +// Configuration Types +export interface DatabaseConfig { + host: string; + port: number; + database: string; + username: string; + password: string; +} + +export interface ServerConfig { + port: number; + host: string; + env: 'development' | 'staging' | 'production'; +} + +// Common Utility Types +export type Nullable = T | null; +export type Optional = T | undefined; +export type Maybe = T | null | undefined; + +// Status Types +export type Status = 'active' | 'inactive' | 'pending' | 'archived'; +export type Priority = 'low' | 'medium' | 'high' | 'critical'; + +// Export all types +export * from './api'; +export * from './database'; +export * from './config'; + diff --git a/packages/shared-types/tsconfig.json b/packages/shared-types/tsconfig.json new file mode 100644 index 0000000..0da5816 --- /dev/null +++ b/packages/shared-types/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} + diff --git a/packages/shared-utils/package.json b/packages/shared-utils/package.json new file mode 100644 index 0000000..fd8ef82 --- /dev/null +++ b/packages/shared-utils/package.json @@ -0,0 +1,30 @@ +{ + "name": "@workspace/shared-utils", + "version": "1.0.0", + "description": "Shared utility functions", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "lint": "eslint src", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "date-fns": "^3.3.1", + "uuid": "^9.0.1" + }, + "devDependencies": { + "typescript": "^5.5.4", + "@types/uuid": "^9.0.7", + "vitest": "^1.2.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "restricted" + } +} + diff --git a/packages/shared-utils/src/date.ts b/packages/shared-utils/src/date.ts new file mode 100644 index 0000000..d4986a5 --- /dev/null +++ b/packages/shared-utils/src/date.ts @@ -0,0 +1,35 @@ +/** + * Date utility functions + */ + +import { format, parseISO, isValid } from 'date-fns'; + +/** + * Format date to ISO string + */ +export function formatDate(date: Date | string, formatStr: string = 'yyyy-MM-dd'): string { + const dateObj = typeof date === 'string' ? parseISO(date) : date; + if (!isValid(dateObj)) { + throw new Error('Invalid date'); + } + return format(dateObj, formatStr); +} + +/** + * Format date to ISO 8601 + */ +export function formatISO(date: Date | string): string { + const dateObj = typeof date === 'string' ? parseISO(date) : date; + if (!isValid(dateObj)) { + throw new Error('Invalid date'); + } + return dateObj.toISOString(); +} + +/** + * Check if date is valid + */ +export function isValidDate(date: unknown): date is Date { + return date instanceof Date && isValid(date); +} + diff --git a/packages/shared-utils/src/index.ts b/packages/shared-utils/src/index.ts new file mode 100644 index 0000000..034301e --- /dev/null +++ b/packages/shared-utils/src/index.ts @@ -0,0 +1,10 @@ +/** + * @workspace/shared-utils + * Shared utility functions + */ + +export * from './date'; +export * from './string'; +export * from './validation'; +export * from './uuid'; + diff --git a/packages/shared-utils/src/string.ts b/packages/shared-utils/src/string.ts new file mode 100644 index 0000000..58255f0 --- /dev/null +++ b/packages/shared-utils/src/string.ts @@ -0,0 +1,58 @@ +/** + * String utility functions + */ + +/** + * Capitalize first letter + */ +export function capitalize(str: string): string { + if (!str) return str; + return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); +} + +/** + * Convert string to camelCase + */ +export function toCamelCase(str: string): string { + return str + .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => { + return index === 0 ? word.toLowerCase() : word.toUpperCase(); + }) + .replace(/\s+/g, ''); +} + +/** + * Convert string to kebab-case + */ +export function toKebabCase(str: string): string { + return str + .replace(/([a-z])([A-Z])/g, '$1-$2') + .replace(/[\s_]+/g, '-') + .toLowerCase(); +} + +/** + * Convert string to snake_case + */ +export function toSnakeCase(str: string): string { + return str + .replace(/([a-z])([A-Z])/g, '$1_$2') + .replace(/[\s-]+/g, '_') + .toLowerCase(); +} + +/** + * Truncate string with ellipsis + */ +export function truncate(str: string, length: number, suffix: string = '...'): string { + if (str.length <= length) return str; + return str.slice(0, length - suffix.length) + suffix; +} + +/** + * Remove whitespace + */ +export function removeWhitespace(str: string): string { + return str.replace(/\s+/g, ''); +} + diff --git a/packages/shared-utils/src/uuid.ts b/packages/shared-utils/src/uuid.ts new file mode 100644 index 0000000..894d188 --- /dev/null +++ b/packages/shared-utils/src/uuid.ts @@ -0,0 +1,20 @@ +/** + * UUID utility functions + */ + +import { v4 as uuidv4, validate as validateUUID } from 'uuid'; + +/** + * Generate UUID v4 + */ +export function generateUUID(): string { + return uuidv4(); +} + +/** + * Validate UUID + */ +export function isValidUUID(uuid: string): boolean { + return validateUUID(uuid); +} + diff --git a/packages/shared-utils/src/validation.ts b/packages/shared-utils/src/validation.ts new file mode 100644 index 0000000..e0a4c11 --- /dev/null +++ b/packages/shared-utils/src/validation.ts @@ -0,0 +1,49 @@ +/** + * Validation utility functions + */ + +/** + * Check if value is empty + */ +export function isEmpty(value: unknown): boolean { + if (value === null || value === undefined) return true; + if (typeof value === 'string') return value.trim().length === 0; + if (Array.isArray(value)) return value.length === 0; + if (typeof value === 'object') return Object.keys(value).length === 0; + return false; +} + +/** + * Check if value is not empty + */ +export function isNotEmpty(value: unknown): boolean { + return !isEmpty(value); +} + +/** + * Check if email is valid + */ +export function isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +} + +/** + * Check if URL is valid + */ +export function isValidUrl(url: string): boolean { + try { + new URL(url); + return true; + } catch { + return false; + } +} + +/** + * Check if string is numeric + */ +export function isNumeric(str: string): boolean { + return !isNaN(Number(str)) && !isNaN(parseFloat(str)); +} + diff --git a/packages/shared-utils/tsconfig.json b/packages/shared-utils/tsconfig.json new file mode 100644 index 0000000..0da5816 --- /dev/null +++ b/packages/shared-utils/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} + diff --git a/packages/validation/package.json b/packages/validation/package.json new file mode 100644 index 0000000..06760a1 --- /dev/null +++ b/packages/validation/package.json @@ -0,0 +1,28 @@ +{ + "name": "@workspace/validation", + "version": "1.0.0", + "description": "Zod schemas and validators", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "lint": "eslint src", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "zod": "^3.23.8" + }, + "devDependencies": { + "typescript": "^5.5.4", + "vitest": "^1.2.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "restricted" + } +} + diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts new file mode 100644 index 0000000..d9e66d1 --- /dev/null +++ b/packages/validation/src/index.ts @@ -0,0 +1,8 @@ +/** + * @workspace/validation + * Zod schemas and validators + */ + +export * from './schemas'; +export * from './validators'; + diff --git a/packages/validation/src/schemas.ts b/packages/validation/src/schemas.ts new file mode 100644 index 0000000..289d014 --- /dev/null +++ b/packages/validation/src/schemas.ts @@ -0,0 +1,37 @@ +/** + * Common validation schemas + */ + +import { z } from 'zod'; + +/** + * Email validation schema + */ +export const emailSchema = z.string().email('Invalid email address'); + +/** + * UUID validation schema + */ +export const uuidSchema = z.string().uuid('Invalid UUID'); + +/** + * URL validation schema + */ +export const urlSchema = z.string().url('Invalid URL'); + +/** + * Pagination schema + */ +export const paginationSchema = z.object({ + page: z.number().int().positive().default(1), + limit: z.number().int().positive().max(100).default(10), +}); + +/** + * Sort schema + */ +export const sortSchema = z.object({ + field: z.string(), + order: z.enum(['asc', 'desc']).default('asc'), +}); + diff --git a/packages/validation/src/validators.ts b/packages/validation/src/validators.ts new file mode 100644 index 0000000..b7a03f1 --- /dev/null +++ b/packages/validation/src/validators.ts @@ -0,0 +1,27 @@ +/** + * Validation utilities + */ + +import { z } from 'zod'; + +/** + * Validate data against schema + */ +export function validate(schema: z.ZodSchema, data: unknown): T { + return schema.parse(data); +} + +/** + * Safe validate (returns result instead of throwing) + */ +export function validateSafe( + schema: z.ZodSchema, + data: unknown +): { success: true; data: T } | { success: false; error: z.ZodError } { + const result = schema.safeParse(data); + if (result.success) { + return { success: true, data: result.data }; + } + return { success: false, error: result.error }; +} + diff --git a/packages/validation/tsconfig.json b/packages/validation/tsconfig.json new file mode 100644 index 0000000..0da5816 --- /dev/null +++ b/packages/validation/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} + diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..4f472c5 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - 'packages/*' + - 'apps/*' + - 'tools/*' + diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100755 index 0000000..4d8c424 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Publish shared packages to private npm registry + +set -e + +REGISTRY="${NPM_REGISTRY:-http://localhost:4873}" +SCOPE="@workspace" + +echo "📦 Publishing shared packages to registry: $REGISTRY" + +# Check if registry is accessible +if ! curl -s "$REGISTRY" > /dev/null; then + echo "❌ Registry not accessible at $REGISTRY" + echo " Please ensure registry is running or set NPM_REGISTRY environment variable" + exit 1 +fi + +# Publish each package +for package in packages/*/; do + if [ -f "$package/package.json" ]; then + package_name=$(basename "$package") + echo "📤 Publishing $package_name..." + + cd "$package" + + # Build package + pnpm build + + # Publish + npm publish --registry="$REGISTRY" --access restricted || { + echo "⚠️ Failed to publish $package_name (may already be published)" + } + + cd - > /dev/null + fi +done + +echo "✅ Publishing complete!" + diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..a6c624d --- /dev/null +++ b/turbo.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", ".next/**", "build/**"] + }, + "test": { + "dependsOn": ["build"], + "outputs": ["coverage/**"] + }, + "lint": { + "outputs": [] + }, + "type-check": { + "dependsOn": ["^build"], + "outputs": [] + }, + "clean": { + "cache": false + } + } +} +