* clear

* feat: Add everything

* fix: Commenht
This commit is contained in:
Prad Nukala
2025-10-03 14:45:52 -04:00
committed by GitHub
parent 43b4a11c06
commit 13e6c3e84d
1935 changed files with 655061 additions and 40058 deletions
+16
View File
@@ -0,0 +1,16 @@
[tool.commitizen]
name = "cz_customize"
tag_format = "pkg-com/v$version"
ignored_tag_formats = ["*/v${version}", "v${version}"]
version_scheme = "semver"
version_provider = "npm"
update_changelog_on_bump = true
major_version_zero = true
pre_bump_hooks = ["bash ../../scripts/hook-bump-pre.sh"]
post_bump_hooks = ["pnpm --filter '@sonr.io/com' publish --no-git-checks"]
[tool.commitizen.customize]
bump_pattern = "^(feat|fix|refactor|perf|BREAKING CHANGE)"
bump_map = { "BREAKING CHANGE" = "MAJOR", "feat" = "MINOR", "fix" = "PATCH", "refactor" = "PATCH", "perf" = "PATCH" }
default_bump = "PATCH"
changelog_pattern = "^(feat|fix|refactor|docs|build)\\(pkg-com\\)(!)?:"
+3
View File
@@ -0,0 +1,3 @@
## pkg-com/v0.1.12 (2025-09-28)
## pkg-com/v0.1.11 (2025-09-27)
+70
View File
@@ -0,0 +1,70 @@
{
"name": "@sonr.io/com",
"version": "0.1.12",
"description": "Common types, utilities, and constants for Sonr ecosystem",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./types": {
"types": "./dist/types/index.d.ts",
"import": "./dist/types/index.js",
"require": "./dist/types/index.cjs"
},
"./utils": {
"types": "./dist/utils/index.d.ts",
"import": "./dist/utils/index.js",
"require": "./dist/utils/index.cjs"
},
"./constants": {
"types": "./dist/constants/index.d.ts",
"import": "./dist/constants/index.js",
"require": "./dist/constants/index.cjs"
}
},
"files": [
"dist",
"src"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"clean": "rm -rf dist",
"test": "echo 'No tests defined for @sonr.io/com'",
"typecheck": "tsc --noEmit",
"lint": "biome check .",
"format": "biome format --write .",
"release": "cz --no-raise 6,21 bump --yes --increment PATCH"
},
"dependencies": {
"zod": "^3.24.1"
},
"devDependencies": {
"@biomejs/biome": "^1.8.0",
"@types/node": "^22.5.5",
"tsup": "^8.0.0",
"typescript": "^5.7.2"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/sonr-io/sonr.git",
"directory": "packages/com"
},
"license": "MIT",
"keywords": [
"sonr",
"common",
"types",
"utilities",
"constants"
]
}
+199
View File
@@ -0,0 +1,199 @@
/**
* @sonr.io/com - Constants
* Shared constants and configurations
*/
/**
* Status constants
*/
export const STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive',
PENDING: 'pending',
ERROR: 'error',
WARNING: 'warning',
SUCCESS: 'success',
} as const;
/**
* HTTP methods
*/
export const HTTP_METHOD = {
GET: 'GET',
POST: 'POST',
PUT: 'PUT',
DELETE: 'DELETE',
PATCH: 'PATCH',
} as const;
/**
* Time intervals in milliseconds
*/
export const TIME = {
SECOND: 1000,
MINUTE: 60 * 1000,
HOUR: 60 * 60 * 1000,
DAY: 24 * 60 * 60 * 1000,
WEEK: 7 * 24 * 60 * 60 * 1000,
} as const;
/**
* Default pagination settings
*/
export const PAGINATION = {
DEFAULT_PAGE: 1,
DEFAULT_PAGE_SIZE: 20,
MAX_PAGE_SIZE: 100,
} as const;
/**
* API error codes
*/
export const ERROR_CODE = {
NETWORK_ERROR: 'NETWORK_ERROR',
TIMEOUT: 'TIMEOUT',
UNAUTHORIZED: 'UNAUTHORIZED',
FORBIDDEN: 'FORBIDDEN',
NOT_FOUND: 'NOT_FOUND',
SERVER_ERROR: 'SERVER_ERROR',
VALIDATION_ERROR: 'VALIDATION_ERROR',
RATE_LIMIT: 'RATE_LIMIT',
} as const;
/**
* User roles
*/
export const USER_ROLE = {
OWNER: 'owner',
ADMIN: 'admin',
DEVELOPER: 'developer',
VIEWER: 'viewer',
} as const;
/**
* Permission effects
*/
export const PERMISSION_EFFECT = {
ALLOW: 'allow',
DENY: 'deny',
} as const;
/**
* Domain verification methods
*/
export const VERIFICATION_METHOD = {
DNS: 'dns',
HTTP: 'http',
} as const;
/**
* Chart types
*/
export const CHART_TYPE = {
LINE: 'line',
AREA: 'area',
BAR: 'bar',
PIE: 'pie',
DONUT: 'donut',
RADAR: 'radar',
} as const;
/**
* Theme options
*/
export const THEME = {
LIGHT: 'light',
DARK: 'dark',
SYSTEM: 'system',
} as const;
/**
* Notification frequency
*/
export const NOTIFICATION_FREQUENCY = {
REALTIME: 'realtime',
DAILY: 'daily',
WEEKLY: 'weekly',
NEVER: 'never',
} as const;
/**
* Performance status thresholds
*/
export const PERFORMANCE_THRESHOLD = {
GOOD: 'good',
WARNING: 'warning',
CRITICAL: 'critical',
} as const;
/**
* DNS record types
*/
export const DNS_RECORD_TYPE = {
TXT: 'TXT',
CNAME: 'CNAME',
A: 'A',
AAAA: 'AAAA',
} as const;
/**
* Service categories
*/
export const SERVICE_CATEGORY = {
API: 'api',
WEBAPP: 'webapp',
MOBILE: 'mobile',
IOT: 'iot',
BLOCKCHAIN: 'blockchain',
OTHER: 'other',
} as const;
/**
* Permission categories
*/
export const PERMISSION_CATEGORY = {
BASIC: 'basic',
STANDARD: 'standard',
ADVANCED: 'advanced',
CUSTOM: 'custom',
} as const;
/**
* Time range presets
*/
export const TIME_PRESET = {
TODAY: 'today',
YESTERDAY: 'yesterday',
LAST_7_DAYS: 'last7days',
LAST_30_DAYS: 'last30days',
THIS_MONTH: 'thisMonth',
LAST_MONTH: 'lastMonth',
CUSTOM: 'custom',
} as const;
/**
* Regular expressions for validation
*/
export const REGEX = {
EMAIL: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
URL: /^https?:\/\/.+/,
DOMAIN: /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i,
API_KEY: /^sk_(test|live)_[a-zA-Z0-9]{24,}$/,
DID: /^did:[a-z0-9]+:[a-zA-Z0-9:.-]+$/,
USERNAME: /^[a-zA-Z0-9_-]+$/,
PHONE: /^\+?[1-9]\d{1,14}$/,
} as const;
/**
* Default chart colors
*/
export const CHART_COLORS = [
'#3b82f6', // blue
'#10b981', // emerald
'#f59e0b', // amber
'#ef4444', // red
'#8b5cf6', // violet
'#ec4899', // pink
'#14b8a6', // teal
'#f97316', // orange
] as const;
+79
View File
@@ -0,0 +1,79 @@
/**
* @sonr.io/com
* Shared types, utilities, and constants for the Sonr ecosystem
*/
// Export all types
export * from './types';
// Export all utilities
export * from './utils';
// Export all constants
export * from './constants';
// Re-export commonly used items at top level for convenience
export type {
// Core types
ID,
Timestamp,
Status,
Pagination,
Filter,
// Service types
Service,
ServiceMetrics,
ServiceRequest,
// Domain types
Domain,
DomainStatus,
DNSRecord,
// Permission types
Permission,
UCANToken,
UCANCapability,
// User types
User,
UserRole,
UserPreferences,
// API types
ApiResponse,
ApiError,
// Analytics types
TimeRange,
DataPoint,
Metric,
} from './types';
export {
// Date utilities
formatDate,
formatTime,
formatDateTime,
getRelativeTime,
// Format utilities
formatNumber,
formatCurrency,
formatPercentage,
formatBytes,
abbreviateNumber,
// Validation utilities
isValidEmail,
isValidUrl,
isValidDomain,
schemas,
// Array utilities
groupBy,
sortBy,
filterBy,
unique,
} from './utils';
export {
// Constants
STATUS,
HTTP_METHOD,
ERROR_CODE,
USER_ROLE,
THEME,
} from './constants';
+91
View File
@@ -0,0 +1,91 @@
/**
* Analytics & Metrics Types
* Types for charts, metrics, and data visualization
*/
import type { Timestamp } from './common';
/**
* Time range for analytics
*/
export interface TimeRange {
start: Date;
end: Date;
preset?:
| 'today'
| 'yesterday'
| 'last7days'
| 'last30days'
| 'thisMonth'
| 'lastMonth'
| 'custom';
}
/**
* Analytics data point
*/
export interface DataPoint {
timestamp: Timestamp;
value: number;
label?: string;
metadata?: Record<string, any>;
}
/**
* Chart configuration
*/
export interface ChartConfig {
type: 'line' | 'area' | 'bar' | 'pie' | 'donut' | 'radar';
colors?: string[];
showLegend?: boolean;
showTooltip?: boolean;
showGrid?: boolean;
animated?: boolean;
stacked?: boolean;
}
/**
* Metric with trend
*/
export interface Metric {
label: string;
value: number | string;
change?: number;
changeType?: 'increase' | 'decrease' | 'neutral';
unit?: string;
icon?: string;
}
/**
* Performance metric
*/
export interface PerformanceMetric {
name: string;
value: number;
unit: 'ms' | 's' | 'min' | '%' | 'rpm' | 'rps';
status: 'good' | 'warning' | 'critical';
threshold?: {
warning: number;
critical: number;
};
}
/**
* Activity data for charts
*/
export interface ActivityData {
date: string;
value: number;
category?: string;
}
/**
* Request pattern data
*/
export interface RequestPatternData {
endpoint: string;
method: string;
count: number;
avgDuration: number;
errorRate: number;
}
+69
View File
@@ -0,0 +1,69 @@
/**
* API Types
* Types for API requests, responses, and errors
*/
import type { Timestamp } from './common';
/**
* API response wrapper
*/
export interface ApiResponse<T> {
data?: T;
error?: ApiError;
status: number;
timestamp: Timestamp;
}
/**
* API error
*/
export interface ApiError {
code: string;
message: string;
details?: Record<string, any>;
stack?: string;
}
/**
* API request configuration
*/
export interface ApiRequestConfig {
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
headers?: Record<string, string>;
params?: Record<string, any>;
body?: any;
timeout?: number;
retry?: {
attempts: number;
delay: number;
backoff?: 'linear' | 'exponential';
};
}
/**
* Paginated API response
*/
export interface PaginatedResponse<T> {
items: T[];
pagination: {
page: number;
pageSize: number;
total: number;
totalPages: number;
};
}
/**
* API endpoint metadata
*/
export interface ApiEndpoint {
path: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
description?: string;
authenticated: boolean;
rateLimit?: {
requests: number;
window: number;
};
}
+97
View File
@@ -0,0 +1,97 @@
/**
* Core Common Types
* Fundamental types used across the Sonr ecosystem
*/
/**
* Common ID type for entities
*/
export type ID = string;
/**
* Timestamp in ISO 8601 format
*/
export type Timestamp = string;
/**
* Status types used across components
*/
export type Status = 'active' | 'inactive' | 'pending' | 'error' | 'warning' | 'success';
/**
* Sort direction for tables and lists
*/
export type SortDirection = 'asc' | 'desc';
/**
* Common pagination props
*/
export interface Pagination {
page: number;
pageSize: number;
total: number;
totalPages: number;
}
/**
* Common filter props
*/
export interface Filter {
field: string;
operator: 'equals' | 'contains' | 'startsWith' | 'endsWith' | 'gt' | 'lt' | 'gte' | 'lte';
value: string | number | boolean;
}
/**
* Navigation item
*/
export interface NavigationItem {
id: ID;
label: string;
href: string;
icon?: string;
badge?: string | number;
disabled?: boolean;
external?: boolean;
children?: NavigationItem[];
}
/**
* Breadcrumb item
*/
export interface BreadcrumbItem {
label: string;
href?: string;
current?: boolean;
}
/**
* Notification
*/
export interface Notification {
id: ID;
type: 'info' | 'success' | 'warning' | 'error';
title: string;
message?: string;
timestamp: Timestamp;
read?: boolean;
action?: {
label: string;
href?: string;
onClick?: () => void;
};
}
/**
* Activity event
*/
export interface ActivityEvent {
id: ID;
type: string;
actor: string;
action: string;
target?: string;
timestamp: Timestamp;
details?: Record<string, any>;
icon?: string;
}
+69
View File
@@ -0,0 +1,69 @@
/**
* Domain & DNS Types
* Types for domain verification and DNS management
*/
import type { ID, Timestamp } from './common';
/**
* Domain verification status
*/
export type DomainStatus = 'unverified' | 'pending' | 'verified' | 'failed' | 'expired';
/**
* Domain entity
*/
export interface Domain {
id: ID;
domain: string;
status: DomainStatus;
owner: string;
verificationMethod: 'dns' | 'http';
verificationRecord?: DNSRecord;
verifiedAt?: Timestamp;
expiresAt?: Timestamp;
services?: ID[];
createdAt: Timestamp;
updatedAt: Timestamp;
}
/**
* DNS record for domain verification
*/
export interface DNSRecord {
type: 'TXT' | 'CNAME' | 'A' | 'AAAA';
name: string;
value: string;
ttl?: number;
}
/**
* Domain verification request
*/
export interface DomainVerificationRequest {
domain: string;
method: 'dns' | 'http';
autoVerify?: boolean;
}
/**
* Verification step
*/
export interface VerificationStep {
id: string;
label: string;
description?: string;
status: 'pending' | 'in-progress' | 'completed' | 'error';
errorMessage?: string;
}
/**
* Verification check result
*/
export interface VerificationCheck {
type: 'dns' | 'http';
success: boolean;
message: string;
details?: Record<string, any>;
timestamp: Timestamp;
}
+75
View File
@@ -0,0 +1,75 @@
/**
* @sonr.io/com - Type Definitions
* Central export for all shared types
*/
// Core common types
export * from './common';
// Service types
export * from './service';
// Domain & DNS types
export * from './domain';
// Permission & auth types
export * from './permission';
// User types
export * from './user';
// Analytics types
export * from './analytics';
// API types
export * from './api';
// Re-export commonly used types for convenience
export type {
ID,
Timestamp,
Status,
SortDirection,
Pagination,
Filter,
} from './common';
export type {
Service,
ServiceMetrics,
ServiceEndpoint,
ServiceRequest,
} from './service';
export type {
Domain,
DomainStatus,
DNSRecord,
DomainVerificationRequest,
} from './domain';
export type {
Permission,
UCANToken,
UCANCapability,
AuditLogEntry,
} from './permission';
export type {
User,
UserRole,
UserPreferences,
} from './user';
export type {
TimeRange,
DataPoint,
Metric,
PerformanceMetric,
} from './analytics';
export type {
ApiResponse,
ApiError,
ApiRequestConfig,
} from './api';
+88
View File
@@ -0,0 +1,88 @@
/**
* Auth & Permission Types
* Types for authentication, authorization, and UCAN tokens
*/
import type { ID, Timestamp } from './common';
/**
* Permission entity
*/
export interface Permission {
id: ID;
resource: string;
action: string;
effect: 'allow' | 'deny';
conditions?: PermissionCondition[];
description?: string;
}
/**
* Permission condition
*/
export interface PermissionCondition {
field: string;
operator: 'equals' | 'notEquals' | 'contains' | 'in' | 'notIn';
value: any;
}
/**
* Permission template for quick setup
*/
export interface PermissionTemplate {
id: ID;
name: string;
description: string;
permissions: Permission[];
category: 'basic' | 'standard' | 'advanced' | 'custom';
}
/**
* UCAN token capability
*/
export interface UCANCapability {
with: string;
can: string;
nb?: Record<string, any>;
}
/**
* UCAN token structure
*/
export interface UCANToken {
iss: string;
aud: string;
exp?: number;
nbf?: number;
nnc?: string;
att: UCANCapability[];
prf?: string[];
fct?: Record<string, any>;
}
/**
* Audit log entry
*/
export interface AuditLogEntry {
id: ID;
timestamp: Timestamp;
actor: string;
action: string;
resource: string;
result: 'success' | 'failure';
details?: Record<string, any>;
ip?: string;
userAgent?: string;
}
/**
* Permission request data
*/
export interface PermissionRequestData {
requester: string;
resource: string;
actions: string[];
reason?: string;
duration?: number;
conditions?: PermissionCondition[];
}
+74
View File
@@ -0,0 +1,74 @@
/**
* Service-related Types
* Types for Sonr Services (x/svc module)
*/
import type { ID, Status, Timestamp } from './common';
import type { Permission } from './permission';
/**
* Service entity
*/
export interface Service {
id: ID;
name: string;
description?: string;
domain: string;
status: 'active' | 'inactive' | 'pending';
apiKey: string;
createdAt: Timestamp;
updatedAt: Timestamp;
permissions?: Permission[];
metrics?: ServiceMetrics;
owner: string;
tags?: string[];
endpoints?: ServiceEndpoint[];
lastActive?: Timestamp;
domainVerificationStatus?: 'verified' | 'pending' | 'failed' | 'unverified';
}
/**
* Service endpoint configuration
*/
export interface ServiceEndpoint {
url: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
description?: string;
authenticated: boolean;
rateLimit?: number;
}
/**
* Service metrics
*/
export interface ServiceMetrics {
requests: number;
errors: number;
latencyP50: number;
latencyP95: number;
latencyP99: number;
uptime: number;
lastActivity?: Timestamp;
}
/**
* Service creation/update request
*/
export interface ServiceRequest {
name: string;
description?: string;
domain?: string;
permissions?: string[];
tags?: string[];
endpoints?: ServiceEndpoint[];
}
/**
* Service status
*/
export type ServiceStatus = 'active' | 'inactive' | 'suspended' | 'pending';
/**
* Service category
*/
export type ServiceCategory = 'api' | 'webapp' | 'mobile' | 'iot' | 'blockchain' | 'other';
+47
View File
@@ -0,0 +1,47 @@
/**
* User Types
* Types for user profiles, roles, and preferences
*/
import type { ID, Timestamp } from './common';
/**
* User profile
*/
export interface User {
id: ID;
did: string;
username?: string;
email?: string;
name?: string;
avatar?: string;
role?: UserRole;
createdAt: Timestamp;
lastLogin?: Timestamp;
preferences?: UserPreferences;
}
/**
* User role
*/
export type UserRole = 'owner' | 'admin' | 'developer' | 'viewer';
/**
* User preferences
*/
export interface UserPreferences {
theme?: 'light' | 'dark' | 'system';
language?: string;
timezone?: string;
notifications?: NotificationPreferences;
}
/**
* Notification preferences
*/
export interface NotificationPreferences {
email?: boolean;
push?: boolean;
inApp?: boolean;
frequency?: 'realtime' | 'daily' | 'weekly' | 'never';
}
+167
View File
@@ -0,0 +1,167 @@
/**
* Array & Object Utilities
* Functions for manipulating arrays and objects
*/
import type { Filter, SortDirection } from '../types';
/**
* Group an array of objects by a key
*/
export function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {
return array.reduce(
(result, item) => {
const group = String(item[key]);
if (!result[group]) result[group] = [];
result[group].push(item);
return result;
},
{} as Record<string, T[]>
);
}
/**
* Sort an array of objects by a key
*/
export function sortBy<T>(array: T[], key: keyof T, direction: SortDirection = 'asc'): T[] {
return [...array].sort((a, b) => {
const aVal = a[key];
const bVal = b[key];
if (aVal < bVal) return direction === 'asc' ? -1 : 1;
if (aVal > bVal) return direction === 'asc' ? 1 : -1;
return 0;
});
}
/**
* Filter an array of objects by multiple filters
*/
export function filterBy<T>(array: T[], filters: Filter[]): T[] {
return array.filter((item) => {
return filters.every((filter) => {
const value = (item as any)[filter.field];
const filterValue = filter.value;
switch (filter.operator) {
case 'equals':
return value === filterValue;
case 'contains':
return String(value).toLowerCase().includes(String(filterValue).toLowerCase());
case 'startsWith':
return String(value).toLowerCase().startsWith(String(filterValue).toLowerCase());
case 'endsWith':
return String(value).toLowerCase().endsWith(String(filterValue).toLowerCase());
case 'gt':
return value > filterValue;
case 'lt':
return value < filterValue;
case 'gte':
return value >= filterValue;
case 'lte':
return value <= filterValue;
default:
return true;
}
});
});
}
/**
* Deep clone an object
*/
export function deepClone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
/**
* Merge objects deeply
*/
export function deepMerge<T extends Record<string, any>>(...objects: Partial<T>[]): T {
const result = {} as any;
for (const obj of objects) {
for (const key in obj) {
const val = obj[key];
if (val !== null && typeof val === 'object' && !Array.isArray(val)) {
result[key] = deepMerge(result[key] || {}, val);
} else {
result[key] = val;
}
}
}
return result;
}
/**
* Remove duplicates from array
*/
export function unique<T>(array: T[]): T[] {
return [...new Set(array)];
}
/**
* Remove duplicates from array of objects by key
*/
export function uniqueBy<T>(array: T[], key: keyof T): T[] {
const seen = new Set();
return array.filter((item) => {
const value = item[key];
if (seen.has(value)) {
return false;
}
seen.add(value);
return true;
});
}
/**
* Chunk array into smaller arrays
*/
export function chunk<T>(array: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
/**
* Flatten nested array
*/
export function flatten<T>(array: (T | T[])[]): T[] {
return array.reduce<T[]>((flat, item) => {
return flat.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
/**
* Pick specific keys from object
*/
export function pick<T extends Record<string, any>, K extends keyof T>(
obj: T,
keys: K[]
): Pick<T, K> {
const result = {} as Pick<T, K>;
keys.forEach((key) => {
if (key in obj) {
result[key] = obj[key];
}
});
return result;
}
/**
* Omit specific keys from object
*/
export function omit<T extends Record<string, any>, K extends keyof T>(
obj: T,
keys: K[]
): Omit<T, K> {
const result = { ...obj };
keys.forEach((key) => {
delete result[key];
});
return result as Omit<T, K>;
}
+175
View File
@@ -0,0 +1,175 @@
/**
* Chart & Analytics Utilities
* Functions for data visualization and analytics
*/
import type { DataPoint } from '../types';
/**
* Generate chart colors
*/
export function generateChartColors(count: number): string[] {
const baseColors = [
'#3b82f6', // blue
'#10b981', // emerald
'#f59e0b', // amber
'#ef4444', // red
'#8b5cf6', // violet
'#ec4899', // pink
'#14b8a6', // teal
'#f97316', // orange
];
const colors: string[] = [];
for (let i = 0; i < count; i++) {
colors.push(baseColors[i % baseColors.length]);
}
return colors;
}
/**
* Calculate percentage change
*/
export function calculatePercentageChange(oldValue: number, newValue: number): number {
if (oldValue === 0) return newValue > 0 ? 100 : 0;
return ((newValue - oldValue) / Math.abs(oldValue)) * 100;
}
/**
* Aggregate data points by interval
*/
export function aggregateDataPoints(
points: DataPoint[],
interval: 'hour' | 'day' | 'week' | 'month'
): DataPoint[] {
const grouped = new Map<string, DataPoint[]>();
points.forEach((point) => {
const date = new Date(point.timestamp);
let key: string;
switch (interval) {
case 'hour':
key = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getHours()}`;
break;
case 'day':
key = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
break;
case 'week': {
const weekNumber = Math.floor(date.getDate() / 7);
key = `${date.getFullYear()}-${date.getMonth()}-W${weekNumber}`;
break;
}
case 'month':
key = `${date.getFullYear()}-${date.getMonth()}`;
break;
}
if (!grouped.has(key)) {
grouped.set(key, []);
}
grouped.get(key)?.push(point);
});
const aggregated: DataPoint[] = [];
grouped.forEach((group, key) => {
const sum = group.reduce((acc, point) => acc + point.value, 0);
const avg = sum / group.length;
aggregated.push({
timestamp: group[0].timestamp,
value: avg,
label: key,
metadata: {
count: group.length,
sum,
avg,
min: Math.min(...group.map((p) => p.value)),
max: Math.max(...group.map((p) => p.value)),
},
});
});
return aggregated.sort(
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
);
}
/**
* Calculate statistics for data points
*/
export function calculateStatistics(values: number[]): {
min: number;
max: number;
mean: number;
median: number;
sum: number;
count: number;
stdDev: number;
} {
const sorted = [...values].sort((a, b) => a - b);
const sum = values.reduce((acc, val) => acc + val, 0);
const mean = sum / values.length;
const median =
values.length % 2 === 0
? (sorted[values.length / 2 - 1] + sorted[values.length / 2]) / 2
: sorted[Math.floor(values.length / 2)];
const variance = values.reduce((acc, val) => acc + (val - mean) ** 2, 0) / values.length;
const stdDev = Math.sqrt(variance);
return {
min: Math.min(...values),
max: Math.max(...values),
mean,
median,
sum,
count: values.length,
stdDev,
};
}
/**
* Generate trend line data
*/
export function generateTrendLine(points: DataPoint[]): DataPoint[] {
if (points.length < 2) return points;
// Simple linear regression
const n = points.length;
const sumX = points.reduce((acc, _, i) => acc + i, 0);
const sumY = points.reduce((acc, p) => acc + p.value, 0);
const sumXY = points.reduce((acc, p, i) => acc + i * p.value, 0);
const sumX2 = points.reduce((acc, _, i) => acc + i * i, 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
const intercept = (sumY - slope * sumX) / n;
return points.map((point, i) => ({
...point,
value: slope * i + intercept,
metadata: {
...point.metadata,
isTrendLine: true,
},
}));
}
/**
* Calculate moving average
*/
export function calculateMovingAverage(values: number[], window: number): number[] {
if (window > values.length) return values;
const result: number[] = [];
for (let i = 0; i < values.length; i++) {
const start = Math.max(0, i - window + 1);
const windowValues = values.slice(start, i + 1);
const avg = windowValues.reduce((acc, val) => acc + val, 0) / windowValues.length;
result.push(avg);
}
return result;
}
+153
View File
@@ -0,0 +1,153 @@
/**
* Date & Time Utilities
* Functions for date/time formatting and manipulation
*/
import type { TimeRange, Timestamp } from '../types';
/**
* Format a timestamp to a human-readable date string
*/
export function formatDate(timestamp: Timestamp, options?: Intl.DateTimeFormatOptions): string {
const date = new Date(timestamp);
return date.toLocaleDateString(
undefined,
options || {
year: 'numeric',
month: 'short',
day: 'numeric',
}
);
}
/**
* Format a timestamp to a human-readable time string
*/
export function formatTime(timestamp: Timestamp, options?: Intl.DateTimeFormatOptions): string {
const date = new Date(timestamp);
return date.toLocaleTimeString(
undefined,
options || {
hour: '2-digit',
minute: '2-digit',
}
);
}
/**
* Format a timestamp to a human-readable date and time string
*/
export function formatDateTime(timestamp: Timestamp): string {
return `${formatDate(timestamp)} ${formatTime(timestamp)}`;
}
/**
* Get a relative time string (e.g., "2 hours ago")
*/
export function getRelativeTime(timestamp: Timestamp): string {
const now = Date.now();
const then = new Date(timestamp).getTime();
const diff = now - then;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const weeks = Math.floor(days / 7);
const months = Math.floor(days / 30);
const years = Math.floor(days / 365);
if (seconds < 60) return `${seconds} second${seconds !== 1 ? 's' : ''} ago`;
if (minutes < 60) return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
if (hours < 24) return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
if (days < 7) return `${days} day${days !== 1 ? 's' : ''} ago`;
if (weeks < 4) return `${weeks} week${weeks !== 1 ? 's' : ''} ago`;
if (months < 12) return `${months} month${months !== 1 ? 's' : ''} ago`;
return `${years} year${years !== 1 ? 's' : ''} ago`;
}
/**
* Get date range from preset
*/
export function getDateRangeFromPreset(preset: TimeRange['preset']): TimeRange {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
switch (preset) {
case 'today':
return {
start: today,
end: now,
preset,
};
case 'yesterday': {
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
return {
start: yesterday,
end: today,
preset,
};
}
case 'last7days': {
const weekAgo = new Date(today);
weekAgo.setDate(weekAgo.getDate() - 7);
return {
start: weekAgo,
end: now,
preset,
};
}
case 'last30days': {
const monthAgo = new Date(today);
monthAgo.setDate(monthAgo.getDate() - 30);
return {
start: monthAgo,
end: now,
preset,
};
}
case 'thisMonth': {
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
return {
start: startOfMonth,
end: now,
preset,
};
}
case 'lastMonth': {
const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0);
return {
start: startOfLastMonth,
end: endOfLastMonth,
preset,
};
}
default:
return {
start: today,
end: now,
preset: 'today',
};
}
}
/**
* Format duration in milliseconds to human-readable string
*/
export function formatDuration(ms: number): string {
if (ms < 1000) return `${ms}ms`;
const seconds = Math.floor(ms / 1000);
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h ${minutes % 60}m`;
const days = Math.floor(hours / 24);
return `${days}d ${hours % 24}h`;
}
+162
View File
@@ -0,0 +1,162 @@
/**
* Formatting Utilities
* Functions for number and string formatting
*/
import type { Metric, Status } from '../types';
/**
* Format a number with commas
*/
export function formatNumber(value: number, decimals = 0): string {
return value.toLocaleString(undefined, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
});
}
/**
* Format a number as currency
*/
export function formatCurrency(value: number, currency = 'USD'): string {
return value.toLocaleString(undefined, {
style: 'currency',
currency,
});
}
/**
* Format a number as a percentage
*/
export function formatPercentage(value: number, decimals = 1): string {
return `${(value * 100).toFixed(decimals)}%`;
}
/**
* Format bytes to human-readable size
*/
export function formatBytes(bytes: number, decimals = 2): string {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${Number.parseFloat((bytes / k ** i).toFixed(decimals))} ${sizes[i]}`;
}
/**
* Abbreviate large numbers (e.g., 1.2K, 3.4M)
*/
export function abbreviateNumber(value: number): string {
if (value < 1000) return value.toString();
const suffixes = ['', 'K', 'M', 'B', 'T'];
const suffixNum = Math.floor(`${value}`.length / 3);
const shortValue = Number.parseFloat(
(suffixNum !== 0 ? value / 1000 ** suffixNum : value).toPrecision(2)
);
if (shortValue % 1 !== 0) {
return shortValue.toFixed(1) + suffixes[suffixNum];
}
return shortValue + suffixes[suffixNum];
}
/**
* Truncate a string to a maximum length
*/
export function truncate(str: string, maxLength: number, suffix = '...'): string {
if (str.length <= maxLength) return str;
return str.slice(0, maxLength - suffix.length) + suffix;
}
/**
* Convert a string to title case
*/
export function toTitleCase(str: string): string {
return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
}
/**
* Convert a string to slug format
*/
export function toSlug(str: string): string {
return str
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');
}
/**
* Generate a random ID
*/
export function generateId(prefix = ''): string {
const random = Math.random().toString(36).substr(2, 9);
const timestamp = Date.now().toString(36);
return prefix ? `${prefix}_${timestamp}_${random}` : `${timestamp}_${random}`;
}
/**
* Extract initials from a name
*/
export function getInitials(name: string): string {
return name
.split(' ')
.map((word) => word[0])
.join('')
.toUpperCase()
.slice(0, 2);
}
/**
* Get status color/variant
*/
export function getStatusColor(status: Status): string {
const colors: Record<Status, string> = {
active: 'green',
inactive: 'gray',
pending: 'yellow',
error: 'red',
warning: 'orange',
success: 'green',
};
return colors[status] || 'gray';
}
/**
* Get status icon
*/
export function getStatusIcon(status: Status): string {
const icons: Record<Status, string> = {
active: 'check-circle',
inactive: 'x-circle',
pending: 'clock',
error: 'alert-circle',
warning: 'alert-triangle',
success: 'check-circle',
};
return icons[status] || 'circle';
}
/**
* Format metric with trend
*/
export function formatMetricWithTrend(metric: Metric): string {
let result = String(metric.value);
if (metric.unit) {
result += ` ${metric.unit}`;
}
if (metric.change !== undefined) {
const symbol = metric.change > 0 ? '↑' : metric.change < 0 ? '↓' : '→';
const changeStr = Math.abs(metric.change).toFixed(1);
result += ` ${symbol} ${changeStr}%`;
}
return result;
}
+79
View File
@@ -0,0 +1,79 @@
/**
* @sonr.io/com - Utility Functions
* Central export for all utility functions
*/
// Date & time utilities
export * from './date';
// Formatting utilities
export * from './format';
// Validation utilities
export * from './validation';
// Array & object utilities
export * from './array';
// Chart & analytics utilities
export * from './chart';
// Export utility collections for convenience
export {
formatDate,
formatTime,
formatDateTime,
getRelativeTime,
getDateRangeFromPreset,
formatDuration,
} from './date';
export {
formatNumber,
formatCurrency,
formatPercentage,
formatBytes,
abbreviateNumber,
truncate,
toTitleCase,
toSlug,
generateId,
getInitials,
getStatusColor,
getStatusIcon,
formatMetricWithTrend,
} from './format';
export {
isValidEmail,
isValidUrl,
isValidDomain,
isValidApiKey,
isValidDID,
isValidPhoneNumber,
schemas,
createValidator,
} from './validation';
export {
groupBy,
sortBy,
filterBy,
deepClone,
deepMerge,
unique,
uniqueBy,
chunk,
flatten,
pick,
omit,
} from './array';
export {
generateChartColors,
calculatePercentageChange,
aggregateDataPoints,
calculateStatistics,
generateTrendLine,
calculateMovingAverage,
} from './chart';
+184
View File
@@ -0,0 +1,184 @@
/**
* Validation Utilities
* Functions for validating common formats and patterns
*/
import { z } from 'zod';
/**
* Validate email address
*/
export function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
/**
* Validate URL
*/
export function isValidUrl(url: string): boolean {
try {
new URL(url);
return true;
} catch {
return false;
}
}
/**
* Validate domain
*/
export function isValidDomain(domain: string): boolean {
const domainRegex =
/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
return domainRegex.test(domain);
}
/**
* Validate API key format
*/
export function isValidApiKey(key: string): boolean {
const apiKeyRegex = /^sk_(test|live)_[a-zA-Z0-9]{24,}$/;
return apiKeyRegex.test(key);
}
/**
* Validate DID format
*/
export function isValidDID(did: string): boolean {
// W3C DID format: did:method:method-specific-id
const didRegex = /^did:[a-z0-9]+:[a-zA-Z0-9:.-]+$/;
return didRegex.test(did);
}
/**
* Validate phone number
*/
export function isValidPhoneNumber(phone: string): boolean {
// Basic international format
const phoneRegex = /^\+?[1-9]\d{1,14}$/;
return phoneRegex.test(phone.replace(/[\s-()]/g, ''));
}
// ============================================================================
// Zod Validation Schemas
// ============================================================================
/**
* Common validation schemas
*/
export const schemas = {
/**
* Email schema
*/
email: z.string().email('Invalid email address'),
/**
* URL schema
*/
url: z.string().url('Invalid URL'),
/**
* Domain schema
*/
domain: z.string().refine(isValidDomain, 'Invalid domain'),
/**
* API key schema
*/
apiKey: z.string().refine(isValidApiKey, 'Invalid API key format'),
/**
* DID schema
*/
did: z.string().refine(isValidDID, 'Invalid DID format'),
/**
* Phone number schema
*/
phoneNumber: z.string().refine(isValidPhoneNumber, 'Invalid phone number'),
/**
* Service registration schema
*/
serviceRegistration: z.object({
name: z.string().min(3, 'Name must be at least 3 characters').max(50),
description: z.string().optional(),
domain: z.string().refine(isValidDomain, 'Invalid domain').optional(),
permissions: z.array(z.string()).optional(),
tags: z.array(z.string()).max(10, 'Maximum 10 tags allowed').optional(),
}),
/**
* Domain verification schema
*/
domainVerification: z.object({
domain: z.string().refine(isValidDomain, 'Invalid domain'),
method: z.enum(['dns', 'http']),
autoVerify: z.boolean().optional(),
}),
/**
* User profile schema
*/
userProfile: z.object({
username: z
.string()
.min(3)
.max(30)
.regex(/^[a-zA-Z0-9_-]+$/, 'Username can only contain letters, numbers, - and _'),
email: z.string().email().optional(),
name: z.string().max(100).optional(),
avatar: z.string().url().optional(),
}),
/**
* Pagination schema
*/
pagination: z.object({
page: z.number().int().positive(),
pageSize: z.number().int().positive().max(100),
}),
/**
* Time range schema
*/
timeRange: z.object({
start: z.date(),
end: z.date(),
preset: z
.enum(['today', 'yesterday', 'last7days', 'last30days', 'thisMonth', 'lastMonth', 'custom'])
.optional(),
}),
/**
* Permission request schema
*/
permissionRequest: z.object({
resource: z.string().min(1),
action: z.string().min(1),
effect: z.enum(['allow', 'deny']),
conditions: z
.array(
z.object({
field: z.string(),
operator: z.enum(['equals', 'notEquals', 'contains', 'in', 'notIn']),
value: z.any(),
})
)
.optional(),
}),
};
/**
* Create a validator function from a Zod schema
*/
export function createValidator<T>(schema: z.ZodSchema<T>) {
return (data: unknown): { success: boolean; data?: T; errors?: z.ZodError } => {
const result = schema.safeParse(data);
if (result.success) {
return { success: true, data: result.data };
}
return { success: false, errors: result.error };
};
}
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022"],
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"types": ["node"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["dist", "node_modules", "**/*.test.ts", "**/*.spec.ts"]
}
+18
View File
@@ -0,0 +1,18 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: {
index: 'src/index.ts',
'types/index': 'src/types/index.ts',
'utils/index': 'src/utils/index.ts',
'constants/index': 'src/constants/index.ts',
},
format: ['esm', 'cjs'],
dts: true,
clean: true,
splitting: false,
sourcemap: true,
minify: false,
treeshake: true,
external: ['zod'],
});