Initial commit: add .gitignore and README

This commit is contained in:
defiQUG
2026-02-09 21:51:55 -08:00
commit d904c04c2d
52 changed files with 1613 additions and 0 deletions

12
.changeset/config.json Normal file
View File

@@ -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": []
}

49
.gitignore vendored Normal file
View File

@@ -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/

10
.npmrc Normal file
View File

@@ -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}

142
README.md Normal file
View File

@@ -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=<registry-url>
```
---
## 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

28
package.json Normal file
View File

@@ -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"
}

View File

@@ -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"
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,9 @@
/**
* @workspace/api-client
* Common API client utilities
*/
export * from './client';
export * from './interceptors';
export * from './types';

View File

@@ -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);
}
);
}

View File

@@ -0,0 +1,21 @@
/**
* API client types
*/
import { AxiosRequestConfig, AxiosResponse } from 'axios';
export interface ApiRequestConfig extends AxiosRequestConfig {
retries?: number;
}
export interface ApiResponse<T = unknown> extends AxiosResponse<T> {
data: T;
}
export interface ApiError {
message: string;
code?: string;
status?: number;
details?: unknown;
}

View File

@@ -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"]
}

View File

@@ -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"
}
}

View File

@@ -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<ethers.Contract> {
const contract = await contractFactory.deploy(...args);
await contract.waitForDeployment();
return contract;
}

View File

@@ -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);
}

View File

@@ -0,0 +1,10 @@
/**
* @workspace/blockchain
* Blockchain utilities and helpers
*/
export * from './ethers';
export * from './transactions';
export * from './contracts';
export * from './types';

View File

@@ -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<ethers.TransactionReceipt | null> {
return provider.waitForTransaction(txHash, confirmations);
}
/**
* Get transaction receipt
*/
export async function getTransactionReceipt(
provider: ethers.Provider,
txHash: string
): Promise<ethers.TransactionReceipt | null> {
return provider.getTransactionReceipt(txHash);
}
/**
* Estimate gas for transaction
*/
export async function estimateGas(
provider: ethers.Provider,
transaction: ethers.TransactionRequest
): Promise<bigint> {
return provider.estimateGas(transaction);
}

View File

@@ -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;
}

View File

@@ -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"]
}

View File

@@ -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"
}
}

View File

@@ -0,0 +1,10 @@
/**
* @workspace/shared-auth
* Shared authentication and authorization utilities
*/
export * from './jwt';
export * from './password';
export * from './permissions';
export * from './types';

View File

@@ -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<T extends TokenPayload>(
token: string,
secret: string
): T {
return jwt.verify(token, secret) as T;
}
/**
* Decode JWT token (without verification)
*/
export function decodeToken<T extends TokenPayload>(token: string): T | null {
try {
return jwt.decode(token) as T;
} catch {
return null;
}
}

View File

@@ -0,0 +1,25 @@
/**
* Password hashing utilities
*/
import bcrypt from 'bcryptjs';
const SALT_ROUNDS = 10;
/**
* Hash password
*/
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
/**
* Verify password
*/
export async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
return bcrypt.compare(password, hash);
}

View File

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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"]
}

View File

@@ -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"
}
}

View File

@@ -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<T>(schema: z.ZodSchema<T>): T {
return schema.parse(process.env);
}

View File

@@ -0,0 +1,9 @@
/**
* @workspace/shared-config
* Shared configuration schemas and validation
*/
export * from './env';
export * from './schemas';
export * from './validation';

View File

@@ -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(),
});

View File

@@ -0,0 +1,27 @@
/**
* Configuration validation utilities
*/
import { z } from 'zod';
/**
* Validate configuration object
*/
export function validateConfig<T>(schema: z.ZodSchema<T>, config: unknown): T {
return schema.parse(config);
}
/**
* Validate configuration with error handling
*/
export function validateConfigSafe<T>(
schema: z.ZodSchema<T>,
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 };
}

View File

@@ -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"]
}

View File

@@ -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"
}
}

View File

@@ -0,0 +1,29 @@
/**
* API-related types
*/
export interface PaginationParams {
page: number;
limit: number;
offset?: number;
}
export interface PaginatedResponse<T> {
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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,54 @@
/**
* @workspace/shared-types
* Common TypeScript types and interfaces
*/
// API Response Types
export interface ApiResponse<T> {
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> = T | null;
export type Optional<T> = T | undefined;
export type Maybe<T> = 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';

View File

@@ -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"]
}

View File

@@ -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"
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,10 @@
/**
* @workspace/shared-utils
* Shared utility functions
*/
export * from './date';
export * from './string';
export * from './validation';
export * from './uuid';

View File

@@ -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, '');
}

View File

@@ -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);
}

View File

@@ -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));
}

View File

@@ -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"]
}

View File

@@ -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"
}
}

View File

@@ -0,0 +1,8 @@
/**
* @workspace/validation
* Zod schemas and validators
*/
export * from './schemas';
export * from './validators';

View File

@@ -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'),
});

View File

@@ -0,0 +1,27 @@
/**
* Validation utilities
*/
import { z } from 'zod';
/**
* Validate data against schema
*/
export function validate<T>(schema: z.ZodSchema<T>, data: unknown): T {
return schema.parse(data);
}
/**
* Safe validate (returns result instead of throwing)
*/
export function validateSafe<T>(
schema: z.ZodSchema<T>,
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 };
}

View File

@@ -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"]
}

5
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,5 @@
packages:
- 'packages/*'
- 'apps/*'
- 'tools/*'

39
scripts/publish.sh Executable file
View File

@@ -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!"

24
turbo.json Normal file
View File

@@ -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
}
}
}