@spfn/core 0.2.0-beta.4 → 0.2.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -754,12 +754,33 @@ declare class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection> {
754
754
  * @internal
755
755
  */
756
756
  private validateSchemas;
757
+ /**
758
+ * SKIP_ENV_VALIDATION 환경변수 확인
759
+ */
760
+ private shouldSkipValidation;
757
761
  /**
758
762
  * 실제 접근 시점에 환경변수 값 가져오기 및 검증
759
763
  *
760
764
  * @internal
761
765
  */
762
766
  private getAndValidate;
767
+ /**
768
+ * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)
769
+ *
770
+ * CLI에서 사용하기 위한 메서드로, 모든 required 환경변수를 강제 검증합니다.
771
+ *
772
+ * @returns 검증 결과 (errors, warnings)
773
+ */
774
+ validateAll(): {
775
+ errors: Array<{
776
+ key: string;
777
+ message: string;
778
+ }>;
779
+ warnings: Array<{
780
+ key: string;
781
+ message: string;
782
+ }>;
783
+ };
763
784
  /**
764
785
  * 환경변수 검증 및 타입 안전한 env 객체 반환
765
786
  *
@@ -779,6 +800,20 @@ declare class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection> {
779
800
  */
780
801
  validate(): InferEnvType<T>;
781
802
  }
803
+ /**
804
+ * 환경변수 검증 결과
805
+ */
806
+ interface EnvValidationResult {
807
+ valid: boolean;
808
+ errors: Array<{
809
+ key: string;
810
+ message: string;
811
+ }>;
812
+ warnings: Array<{
813
+ key: string;
814
+ message: string;
815
+ }>;
816
+ }
782
817
  /**
783
818
  * 레지스트리 생성 헬퍼
784
819
  *
@@ -793,6 +828,24 @@ declare class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection> {
793
828
  * ```
794
829
  */
795
830
  declare function createEnvRegistry<T extends EnvSchemaCollection>(schemas: T): EnvRegistry<T>;
831
+ /**
832
+ * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)
833
+ *
834
+ * CLI `spfn env validate` 명령어에서 사용
835
+ *
836
+ * @param registries - 검증할 레지스트리 배열
837
+ * @returns 검증 결과
838
+ *
839
+ * @example
840
+ * ```typescript
841
+ * const result = validateAllEnv([coreRegistry, authRegistry]);
842
+ * if (!result.valid) {
843
+ * console.error('Missing env vars:', result.errors);
844
+ * process.exit(1);
845
+ * }
846
+ * ```
847
+ */
848
+ declare function validateAllEnv(registries: EnvRegistry<any>[]): EnvValidationResult;
796
849
 
797
850
  /**
798
851
  * Environment Types
@@ -802,4 +855,4 @@ declare function createEnvRegistry<T extends EnvSchemaCollection>(schemas: T): E
802
855
  */
803
856
  type NodeEnv = 'local' | 'development' | 'staging' | 'production' | 'test';
804
857
 
805
- export { EnvRegistry, type EnvSchemaCollection, type EnvVarSchema, type InferEnvType, type NodeEnv, type Parser, chain, createArrayParser, createEnumParser, createEnvRegistry, createJsonParser, createNumberParser, createPasswordParser, createSecureSecretParser, createStringParser, createUrlParser, defineEnvSchema, envBoolean, envEnum, envJson, envNumber, envString, envUrl, isClientAccessible, isNextjsAccessible, isServerOnly, isSpfnServerOnly, optional, parseArray, parseBoolean, parseDecimal, parseEnum, parseInteger, parseJson, parseNumber, parsePostgresUrl, parseRedisUrl, parseString, parseUrl, withFallback };
858
+ export { EnvRegistry, type EnvSchemaCollection, type EnvValidationResult, type EnvVarSchema, type InferEnvType, type NodeEnv, type Parser, chain, createArrayParser, createEnumParser, createEnvRegistry, createJsonParser, createNumberParser, createPasswordParser, createSecureSecretParser, createStringParser, createUrlParser, defineEnvSchema, envBoolean, envEnum, envJson, envNumber, envString, envUrl, isClientAccessible, isNextjsAccessible, isServerOnly, isSpfnServerOnly, optional, parseArray, parseBoolean, parseDecimal, parseEnum, parseInteger, parseJson, parseNumber, parsePostgresUrl, parseRedisUrl, parseString, parseUrl, validateAllEnv, withFallback };
package/dist/env/index.js CHANGED
@@ -431,6 +431,13 @@ var EnvRegistry = class {
431
431
  }
432
432
  this.hasValidated = true;
433
433
  }
434
+ /**
435
+ * SKIP_ENV_VALIDATION 환경변수 확인
436
+ */
437
+ shouldSkipValidation() {
438
+ const skip = process.env.SKIP_ENV_VALIDATION;
439
+ return skip === "true" || skip === "1";
440
+ }
434
441
  /**
435
442
  * 실제 접근 시점에 환경변수 값 가져오기 및 검증
436
443
  *
@@ -442,7 +449,7 @@ var EnvRegistry = class {
442
449
  return void 0;
443
450
  }
444
451
  const value = this.getRawValue(key, schema.fallbackKeys);
445
- if (schema.required && !value) {
452
+ if (schema.required && !value && !this.shouldSkipValidation()) {
446
453
  const fallbackHint = schema.fallbackKeys ? ` (or ${schema.fallbackKeys.join(", ")})` : "";
447
454
  const errorMsg = `${key}${fallbackHint} is required but not set. ${schema.description || ""}`;
448
455
  envLogger.error(`Environment validation failed:
@@ -467,6 +474,52 @@ var EnvRegistry = class {
467
474
  throw new Error("Environment validation failed");
468
475
  }
469
476
  }
477
+ /**
478
+ * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)
479
+ *
480
+ * CLI에서 사용하기 위한 메서드로, 모든 required 환경변수를 강제 검증합니다.
481
+ *
482
+ * @returns 검증 결과 (errors, warnings)
483
+ */
484
+ validateAll() {
485
+ const errors = [];
486
+ const warnings = [];
487
+ for (const [key, schema] of this.schemas) {
488
+ if (isClientAccessible(key) && schema.sensitive) {
489
+ warnings.push({
490
+ key,
491
+ message: `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*).`
492
+ });
493
+ }
494
+ const value = this.getRawValue(key, schema.fallbackKeys);
495
+ if (schema.required && !value) {
496
+ const fallbackHint = schema.fallbackKeys ? ` (or ${schema.fallbackKeys.join(", ")})` : "";
497
+ errors.push({
498
+ key,
499
+ message: `${key}${fallbackHint} is required but not set. ${schema.description || ""}`
500
+ });
501
+ continue;
502
+ }
503
+ if (value && schema.minLength !== void 0 && value.length < schema.minLength) {
504
+ errors.push({
505
+ key,
506
+ message: `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`
507
+ });
508
+ continue;
509
+ }
510
+ if (value && schema.validator) {
511
+ try {
512
+ schema.validator(value);
513
+ } catch (error) {
514
+ errors.push({
515
+ key,
516
+ message: `${key} validation failed: ${error instanceof Error ? error.message : String(error)}`
517
+ });
518
+ }
519
+ }
520
+ }
521
+ return { errors, warnings };
522
+ }
470
523
  /**
471
524
  * 환경변수 검증 및 타입 안전한 env 객체 반환
472
525
  *
@@ -512,7 +565,21 @@ var EnvRegistry = class {
512
565
  function createEnvRegistry(schemas) {
513
566
  return new EnvRegistry(schemas);
514
567
  }
568
+ function validateAllEnv(registries) {
569
+ const errors = [];
570
+ const warnings = [];
571
+ for (const registry of registries) {
572
+ const result = registry.validateAll();
573
+ errors.push(...result.errors);
574
+ warnings.push(...result.warnings);
575
+ }
576
+ return {
577
+ valid: errors.length === 0,
578
+ errors,
579
+ warnings
580
+ };
581
+ }
515
582
 
516
- export { EnvRegistry, chain, createArrayParser, createEnumParser, createEnvRegistry, createJsonParser, createNumberParser, createPasswordParser, createSecureSecretParser, createStringParser, createUrlParser, defineEnvSchema, envBoolean, envEnum, envJson, envNumber, envString, envUrl, isClientAccessible, isNextjsAccessible, isServerOnly, isSpfnServerOnly, optional, parseArray, parseBoolean, parseDecimal, parseEnum, parseInteger, parseJson, parseNumber, parsePostgresUrl, parseRedisUrl, parseString, parseUrl, withFallback };
583
+ export { EnvRegistry, chain, createArrayParser, createEnumParser, createEnvRegistry, createJsonParser, createNumberParser, createPasswordParser, createSecureSecretParser, createStringParser, createUrlParser, defineEnvSchema, envBoolean, envEnum, envJson, envNumber, envString, envUrl, isClientAccessible, isNextjsAccessible, isServerOnly, isSpfnServerOnly, optional, parseArray, parseBoolean, parseDecimal, parseEnum, parseInteger, parseJson, parseNumber, parsePostgresUrl, parseRedisUrl, parseString, parseUrl, validateAllEnv, withFallback };
517
584
  //# sourceMappingURL=index.js.map
518
585
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/env/validator.ts","../../src/env/schema.ts","../../src/env/registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment Variable Management - Parsers\n *\n * Parser functions that transform and validate environment variable strings.\n * All parsers follow the pattern: (value: string) => T or throw Error\n */\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\n/**\n * Parser function that transforms and validates a string value\n * @throws Error if validation fails\n */\nexport type Parser<T> = (value: string) => T;\n\n// ============================================================================\n// String Parsers\n// ============================================================================\n\n/**\n * Parse a non-empty string\n *\n * @param value - Value to parse\n * @returns Trimmed string\n * @throws Error if string is empty after trimming\n *\n * @example\n * ```typescript\n * const name = getEnvVar('APP_NAME', {\n * validator: parseString,\n * });\n * ```\n */\nexport function parseString(value: string): string\n{\n const trimmed = value.trim();\n\n if (trimmed.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n return trimmed;\n}\n\n/**\n * Create a string parser with validation rules\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: createStringParser({\n * minLength: 32,\n * pattern: /^[A-Za-z0-9_-]+$/,\n * }),\n * });\n * ```\n */\nexport function createStringParser(\n options: {\n minLength?: number;\n maxLength?: number;\n pattern?: RegExp;\n trim?: boolean;\n } = {}\n): Parser<string>\n{\n return (value: string): string =>\n {\n const { minLength, maxLength, pattern, trim = true } = options;\n\n let result = trim ? value.trim() : value;\n\n // Empty check\n if (result.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n // Length validation\n if (minLength !== undefined && result.length < minLength)\n {\n throw new Error(`Must be at least ${minLength} characters long (current: ${result.length})`);\n }\n\n if (maxLength !== undefined && result.length > maxLength)\n {\n throw new Error(`Must be at most ${maxLength} characters long (current: ${result.length})`);\n }\n\n // Pattern validation\n if (pattern && !pattern.test(result))\n {\n throw new Error(`Must match pattern ${pattern}`);\n }\n\n return result;\n };\n}\n\n// ============================================================================\n// Boolean Parser\n// ============================================================================\n\n/**\n * Parse a boolean environment variable\n *\n * Accepts: 'true', '1', 'yes' (case-insensitive) → true\n * 'false', '0', 'no' (case-insensitive) → false\n *\n * @param value - Value to parse\n * @returns Boolean value\n * @throws Error if value is not a valid boolean string\n *\n * @example\n * ```typescript\n * const debug = getEnvVar('DEBUG', {\n * default: 'false',\n * validator: parseBoolean,\n * });\n * ```\n */\nexport function parseBoolean(value: string): boolean\n{\n const normalized = value.toLowerCase().trim();\n\n if (['true', '1', 'yes'].includes(normalized))\n {\n return true;\n }\n\n if (['false', '0', 'no'].includes(normalized))\n {\n return false;\n }\n\n throw new Error(\n `Must be a boolean value (true/false, 1/0, yes/no), got: ${value}`\n );\n}\n\n// ============================================================================\n// Number Parsers\n// ============================================================================\n\n/**\n * Parse and validate number\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Parsed number\n * @throws Error if invalid number or constraint violation\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: (val) => parseNumber(val, { min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function parseNumber(\n value: string,\n options: { min?: number; max?: number; integer?: boolean } = {}\n): number\n{\n const { min, max, integer = false } = options;\n\n // Reject empty strings\n if (value.trim() === '')\n {\n throw new Error('Value cannot be empty');\n }\n\n const num = Number(value);\n\n if (isNaN(num))\n {\n throw new Error(`Must be a valid number, got: ${value}`);\n }\n\n if (integer && !Number.isInteger(num))\n {\n throw new Error(`Must be an integer, got: ${value}`);\n }\n\n if (min !== undefined && num < min)\n {\n throw new Error(`Must be at least ${min}, got: ${num}`);\n }\n\n if (max !== undefined && num > max)\n {\n throw new Error(`Must be at most ${max}, got: ${num}`);\n }\n\n return num;\n}\n\n/**\n * Create a number parser with specific constraints\n *\n * @param options - Validation constraints\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: createNumberParser({ min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function createNumberParser(\n options: { min?: number; max?: number; integer?: boolean } = {}\n): Parser<number>\n{\n return (value: string) => parseNumber(value, options);\n}\n\n/**\n * Parse integer with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed integer\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const retries = getEnvVar('MAX_RETRIES', {\n * default: '3',\n * validator: (val) => parseInteger(val, { min: 1, max: 10 }),\n * });\n * ```\n */\nexport function parseInteger(\n value: string,\n options: { min?: number; max?: number } = {}\n): number\n{\n return parseNumber(value, { ...options, integer: true });\n}\n\n/**\n * Parse float/decimal number with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed decimal number\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const ratio = getEnvVar('CACHE_RATIO', {\n * default: '0.75',\n * validator: (val) => parseDecimal(val, { min: 0, max: 1 }),\n * });\n * ```\n */\nexport function parseDecimal(\n value: string,\n options: { min?: number; max?: number } = {}\n): number\n{\n return parseNumber(value, { ...options, integer: false });\n}\n\n// ============================================================================\n// URL Parsers\n// ============================================================================\n\n/**\n * Parse and validate URL\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Validated URL string\n * @throws Error if invalid URL or protocol mismatch\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: (val) => parseUrl(val, { protocol: 'https' }),\n * });\n * ```\n */\nexport function parseUrl(\n value: string,\n options: { protocol?: 'http' | 'https' | 'any' } = {}\n): string\n{\n const { protocol = 'any' } = options;\n\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error(`Invalid URL: ${value}`);\n }\n throw error;\n }\n\n // Validate protocol\n if (protocol === 'http' && url.protocol !== 'http:')\n {\n throw new Error(`URL must use HTTP protocol, got ${url.protocol}`);\n }\n\n if (protocol === 'https' && url.protocol !== 'https:')\n {\n throw new Error(`URL must use HTTPS protocol, got ${url.protocol}`);\n }\n\n return value;\n}\n\n/**\n * Create a URL parser with specific protocol requirement\n *\n * @param protocol - Required protocol ('http', 'https', or 'any')\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: createUrlParser('https'),\n * });\n * ```\n */\nexport function createUrlParser(protocol: 'http' | 'https' | 'any' = 'any'): Parser<string>\n{\n return (value: string) => parseUrl(value, { protocol });\n}\n\n/**\n * Parse PostgreSQL connection string\n *\n * @param value - Value to parse\n * @returns Validated PostgreSQL URL string\n * @throws Error if invalid PostgreSQL URL\n *\n * @example\n * ```typescript\n * const dbUrl = getEnvVar('DATABASE_URL', {\n * required: true,\n * validator: parsePostgresUrl,\n * });\n * ```\n */\nexport function parsePostgresUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error(`Invalid PostgreSQL URL: ${value}`);\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'postgres:' && url.protocol !== 'postgresql:')\n {\n throw new Error(\n `Must be a PostgreSQL URL (postgres:// or postgresql://), got ${url.protocol}`\n );\n }\n\n return value;\n}\n\n/**\n * Parse Redis connection string\n *\n * @param value - Value to parse\n * @returns Validated Redis URL string\n * @throws Error if invalid Redis URL\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * required: true,\n * validator: parseRedisUrl,\n * });\n * ```\n */\nexport function parseRedisUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error(`Invalid Redis URL: ${value}`);\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'redis:' && url.protocol !== 'rediss:')\n {\n throw new Error(\n `Must be a Redis URL (redis:// or rediss://), got ${url.protocol}`\n );\n }\n\n return value;\n}\n\n// ============================================================================\n// Enum Parser\n// ============================================================================\n\n/**\n * Parse and validate enum value\n *\n * @param value - Value to parse\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Validated enum value\n * @throws Error if value not in allowed list\n *\n * @example\n * ```typescript\n * const env = getEnvVar('NODE_ENV', {\n * validator: (val) => parseEnum(val, ['development', 'production', 'test']),\n * });\n * ```\n */\nexport function parseEnum(\n value: string,\n allowed: string[],\n caseInsensitive = false\n): string\n{\n if (caseInsensitive)\n {\n const normalizedValue = value.toLowerCase();\n const normalizedAllowed = allowed.map((v) => v.toLowerCase());\n const index = normalizedAllowed.indexOf(normalizedValue);\n\n if (index === -1)\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}], got: ${value}`\n );\n }\n\n return allowed[index]; // Return original case from allowed list\n }\n\n if (!allowed.includes(value))\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}], got: ${value}`\n );\n }\n\n return value;\n}\n\n/**\n * Create an enum parser with specific allowed values\n *\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const logLevel = getEnvVar('LOG_LEVEL', {\n * default: 'info',\n * validator: createEnumParser(['debug', 'info', 'warn', 'error']),\n * });\n * ```\n */\nexport function createEnumParser(allowed: string[], caseInsensitive = false): Parser<string>\n{\n return (value: string) => parseEnum(value, allowed, caseInsensitive);\n}\n\n// ============================================================================\n// JSON Parser\n// ============================================================================\n\n/**\n * Parse JSON string\n *\n * @param value - JSON string to parse\n * @returns Parsed JSON value\n * @throws Error if invalid JSON\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: parseJson,\n * });\n * ```\n */\nexport function parseJson<T = any>(value: string): T\n{\n try\n {\n return JSON.parse(value) as T;\n }\n catch (error)\n {\n throw new Error(\n `Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n}\n\n/**\n * Create a typed JSON parser\n *\n * @returns Parser function\n *\n * @example\n * ```typescript\n * interface Config {\n * host: string;\n * port: number;\n * }\n *\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: createJsonParser<Config>(),\n * });\n * ```\n */\nexport function createJsonParser<T>(): Parser<T>\n{\n return (value: string) => parseJson<T>(value);\n}\n\n// ============================================================================\n// Array Parser\n// ============================================================================\n\n/**\n * Parse comma-separated values into array\n *\n * @param value - Comma-separated string\n * @param options - Parser options\n * @returns Array of strings\n *\n * @example\n * ```typescript\n * const hosts = getEnvVar('ALLOWED_HOSTS', {\n * validator: parseArray,\n * });\n * // \"localhost,example.com,api.example.com\" → ['localhost', 'example.com', 'api.example.com']\n * ```\n */\nexport function parseArray(\n value: string,\n options: {\n separator?: string;\n trim?: boolean;\n filter?: (item: string) => boolean;\n } = {}\n): string[]\n{\n const { separator = ',', trim = true, filter } = options;\n\n if (value.trim() === '')\n {\n return [];\n }\n\n let items = value.split(separator);\n\n if (trim)\n {\n items = items.map((item) => item.trim());\n }\n\n if (filter)\n {\n items = items.filter(filter);\n }\n\n return items;\n}\n\n/**\n * Create an array parser with item parser\n *\n * @param itemParser - Parser to apply to each array item\n * @param options - Array parsing options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * // Parse comma-separated ports\n * const ports = getEnvVar('PORTS', {\n * validator: createArrayParser(\n * createNumberParser({ min: 1, max: 65535, integer: true })\n * ),\n * });\n * // \"3000,4000,5000\" → [3000, 4000, 5000]\n * ```\n */\nexport function createArrayParser<T>(\n itemParser: Parser<T>,\n options: { separator?: string } = {}\n): Parser<T[]>\n{\n return (value: string): T[] =>\n {\n const items = parseArray(value, options);\n\n return items.map((item, index) =>\n {\n try\n {\n return itemParser(item);\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid item at index ${index}: ${message}`);\n }\n });\n };\n}\n\n// ============================================================================\n// Secure Secret Parser\n// ============================================================================\n\n/**\n * Calculate Shannon entropy of a string\n * Returns entropy in bits per character\n *\n * @param str - String to calculate entropy for\n * @returns Entropy value (0 to ~6.6 bits for printable ASCII)\n *\n * @example\n * ```typescript\n * const entropy = calculateEntropy('my-secret-key');\n * // Higher entropy = more random\n * // - Random lowercase: ~4.7 bits/char\n * // - Random alphanumeric: ~5.2 bits/char\n * // - Random printable ASCII: ~6.6 bits/char\n * // - \"aaaaaaa...\": ~0 bits/char\n * ```\n */\nfunction calculateEntropy(str: string): number\n{\n const len = str.length;\n const frequencies = new Map<string, number>();\n\n // Count character frequencies\n for (const char of str)\n {\n frequencies.set(char, (frequencies.get(char) || 0) + 1);\n }\n\n // Calculate Shannon entropy\n let entropy = 0;\n for (const count of frequencies.values())\n {\n const probability = count / len;\n entropy -= probability * Math.log2(probability);\n }\n\n return entropy;\n}\n\n/**\n * Create a secure secret parser with entropy validation\n *\n * Validates cryptographic secrets for sufficient length, character diversity, and randomness.\n * Uses Shannon entropy to measure randomness quality.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const sessionSecret = getEnvVar('SESSION_SECRET', {\n * validator: createSecureSecretParser({\n * minLength: 32, // Minimum 256-bit\n * minUniqueChars: 16, // Character diversity\n * minEntropy: 3.5, // Shannon entropy (bits/char)\n * }),\n * });\n * ```\n */\nexport function createSecureSecretParser(\n options: {\n minLength?: number;\n minUniqueChars?: number;\n minEntropy?: number;\n } = {}\n): Parser<string>\n{\n const {\n minLength = 32,\n minUniqueChars = 16,\n minEntropy = 3.5,\n } = options;\n\n return (value: string): string =>\n {\n const length = value.length;\n const uniqueChars = new Set(value).size;\n const entropy = calculateEntropy(value);\n\n // Check length (minimum for cryptographic strength)\n if (length < minLength)\n {\n throw new Error(\n `Secret too short: ${length} characters (minimum: ${minLength})`\n );\n }\n\n // Check unique character diversity\n if (uniqueChars < minUniqueChars)\n {\n throw new Error(\n `Secret has low diversity: ${uniqueChars} unique characters (minimum: ${minUniqueChars})`\n );\n }\n\n // Check Shannon entropy (randomness quality)\n // Reference values:\n // - Random lowercase: ~4.7 bits/char\n // - Random alphanumeric: ~5.2 bits/char\n // - Random printable ASCII: ~6.6 bits/char\n // - \"aaaaaaa...\": ~0 bits/char\n // - \"abcabcabc...\": ~1.58 bits/char\n if (entropy < minEntropy)\n {\n throw new Error(\n `Secret has low entropy: ${entropy.toFixed(2)} bits/char (minimum: ${minEntropy}). Use a more random secret.`\n );\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Password Parser\n// ============================================================================\n\n/**\n * Create a password strength parser\n *\n * Validates password strength based on configurable requirements.\n * Useful for enforcing password policies in environment variables or user input.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const adminPassword = getEnvVar('ADMIN_PASSWORD', {\n * validator: createPasswordParser({\n * minLength: 12,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireNumber: true,\n * requireSpecial: true,\n * }),\n * });\n * ```\n */\nexport function createPasswordParser(\n options: {\n minLength?: number;\n requireUppercase?: boolean;\n requireLowercase?: boolean;\n requireNumber?: boolean;\n requireSpecial?: boolean;\n } = {}\n): Parser<string>\n{\n const {\n minLength = 8,\n requireUppercase = true,\n requireLowercase = true,\n requireNumber = true,\n requireSpecial = true,\n } = options;\n\n return (value: string): string =>\n {\n const errors: string[] = [];\n\n // Length check\n if (value.length < minLength)\n {\n errors.push(`Must be at least ${minLength} characters`);\n }\n\n // Uppercase check\n if (requireUppercase && !/[A-Z]/.test(value))\n {\n errors.push('Must contain at least one uppercase letter');\n }\n\n // Lowercase check\n if (requireLowercase && !/[a-z]/.test(value))\n {\n errors.push('Must contain at least one lowercase letter');\n }\n\n // Number check\n if (requireNumber && !/[0-9]/.test(value))\n {\n errors.push('Must contain at least one number');\n }\n\n // Special character check\n if (requireSpecial && !/[^A-Za-z0-9]/.test(value))\n {\n errors.push('Must contain at least one special character');\n }\n\n if (errors.length > 0)\n {\n throw new Error(`Password validation failed: ${errors.join(', ')}`);\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Parser Composition\n// ============================================================================\n\n/**\n * Chain multiple parsers sequentially\n *\n * Each parser receives the output of the previous parser.\n * Useful for multi-step validation/transformation.\n *\n * @param parsers - Array of parser functions\n * @returns Combined parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: chain(\n * parseString,\n * createStringParser({ minLength: 32, pattern: /^[A-Za-z0-9_-]+$/ }),\n * ),\n * });\n * ```\n */\nexport function chain<T>(...parsers: Array<Parser<T>>): Parser<T>\n{\n return (value: string): T =>\n {\n let result = value as any;\n\n for (const parser of parsers)\n {\n result = parser(result);\n }\n\n return result;\n };\n}\n\n/**\n * Apply parser with fallback value\n *\n * If parser throws, returns fallback instead.\n * Useful for optional environment variables with complex parsing.\n *\n * @param parser - Parser to attempt\n * @param fallback - Fallback value if parsing fails\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: withFallback(parseJson, { host: 'localhost', port: 3000 }),\n * });\n * ```\n */\nexport function withFallback<T>(parser: Parser<T>, fallback: T): Parser<T>\n{\n return (value: string): T =>\n {\n try\n {\n return parser(value);\n }\n catch\n {\n return fallback;\n }\n };\n}\n\n/**\n * Make parser optional\n *\n * Returns undefined for empty strings instead of throwing.\n *\n * @param parser - Parser to make optional\n * @returns Parser function that returns T | undefined\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * validator: optional(parseRedisUrl),\n * });\n * // Empty string → undefined\n * // Valid URL → parsed URL\n * // Invalid URL → throws\n * ```\n */\nexport function optional<T>(parser: Parser<T>): Parser<T | undefined>\n{\n return (value: string): T | undefined =>\n {\n if (value.trim() === '')\n {\n return undefined;\n }\n\n return parser(value);\n };\n}","/**\n * Environment Variable Schema Definition System\n *\n * 환경변수에 메타데이터를 정의하여 중앙 관리, 문서화, 검증을 지원합니다.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envUrl({\n * description: 'Database connection',\n * required: true,\n * validator: parsePostgresUrl,\n * sensitive: true,\n * })\n * });\n * ```\n *\n * @module env/schema\n */\n\nimport { parseBoolean, parseNumber, parseJson } from './validator';\n\n/**\n * 환경변수 스키마 정의\n */\nexport interface EnvVarSchema<T = string>\n{\n /** 환경변수 키 */\n key: string;\n\n /** 설명 (목적, 사용처) */\n description: string;\n\n /** 타입 */\n type: 'string' | 'number' | 'boolean' | 'url' | 'enum' | 'json';\n\n /** 필수 여부 */\n required?: boolean;\n\n /** 기본값 */\n default?: T;\n\n /** 검증/변환 함수 */\n validator?: (value: string) => T;\n\n // === 검증 옵션 ===\n\n /** Fallback 환경변수 키들 (backward compatibility) */\n fallbackKeys?: string[];\n\n /** 최소 길이 (문자열 타입) */\n minLength?: number;\n\n // === 메타데이터 ===\n\n /** 민감정보 여부 (로깅 시 마스킹) */\n sensitive?: boolean;\n\n /** 예시 값들 (타입과 일치해야 함) */\n examples?: T[];\n\n // === 파일 분리 ===\n\n /**\n * Next.js 프로세스에서 사용 여부\n *\n * - true: .env.local에 존재해야 함 (Next.js 서버 컴포넌트에서 접근 가능)\n * - false: .env.server.local에만 존재해야 함 (SPFN 서버에서만 접근)\n *\n * @default NEXT_PUBLIC_* 이면 true, 아니면 false\n */\n nextjs?: boolean;\n}\n\n/**\n * 스키마 컬렉션 타입\n */\nexport type EnvSchemaCollection = Record<string, EnvVarSchema<any>>;\n\n/**\n * Helper type: Check if field has default value\n */\ntype HasDefault<T> = T extends { default: any } ? true : false;\n\n/**\n * Helper type: Check if field is explicitly required\n */\ntype IsRequired<T> = T extends { required: true } ? true : false;\n\n/**\n * Helper type: Check if field should be required (has default OR required: true)\n */\ntype ShouldBeRequired<T> = HasDefault<T> extends true ? true : IsRequired<T>;\n\n/**\n * 스키마로부터 타입 추출\n *\n * required: true 또는 default가 있는 필드 → 필수\n * required: false 또는 미지정 → optional (| undefined)\n */\nexport type InferEnvType<T extends EnvSchemaCollection> = {\n // Required fields (required: true OR has default)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? K : never]:\n T[K] extends EnvVarSchema<infer U> ? U : string;\n} & {\n // Optional fields (required: false OR not specified)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? never : K]?:\n T[K] extends EnvVarSchema<infer U> ? U | undefined : string | undefined;\n};\n\n/**\n * 스키마 정의 헬퍼 (타입 추론 지원)\n *\n * Automatically fills in the `key` property from object keys.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n * // Automatically adds key: 'DATABASE_URL'\n * ```\n */\nexport function defineEnvSchema<T extends Record<string, any>>(\n schema: T\n): { [K in keyof T]: T[K] & { key: K } }\n{\n const result: any = {};\n\n for (const key in schema)\n {\n result[key] = {\n ...schema[key],\n key,\n };\n }\n\n return result;\n}\n\n/**\n * 문자열 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * API_KEY: {\n * ...envString({\n * description: 'API authentication key',\n * required: true,\n * sensitive: true,\n * }),\n * key: 'API_KEY',\n * }\n * };\n * ```\n */\nexport function envString<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T\n): T & { type: 'string' }\n{\n return {\n ...options,\n type: 'string',\n };\n}\n\n/**\n * 숫자 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * PORT: {\n * ...envNumber({\n * description: 'Server port',\n * default: 3000,\n * validator: createNumberParser({ min: 1, max: 65535 }),\n * }),\n * key: 'PORT',\n * }\n * };\n * ```\n */\nexport function envNumber<T extends Omit<EnvVarSchema<number>, 'key' | 'type'>>(\n options: T\n): T & { type: 'number'; validator: (value: string) => number }\n{\n return {\n ...options,\n type: 'number',\n validator: options.validator || parseNumber,\n };\n}\n\n/**\n * Boolean 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DEBUG: {\n * ...envBoolean({\n * description: 'Enable debug mode',\n * default: false,\n * }),\n * key: 'DEBUG',\n * }\n * };\n * ```\n */\nexport function envBoolean<T extends Omit<EnvVarSchema<boolean>, 'key' | 'type'>>(\n options: T\n): T & { type: 'boolean'; validator: (value: string) => boolean }\n{\n return {\n ...options,\n type: 'boolean',\n validator: options.validator || parseBoolean,\n };\n}\n\n/**\n * URL 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DATABASE_URL: {\n * ...envUrl({\n * description: 'Database connection URL',\n * required: true,\n * validator: parsePostgresUrl,\n * }),\n * key: 'DATABASE_URL',\n * }\n * };\n * ```\n */\nexport function envUrl<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T\n): T & { type: 'url' }\n{\n return {\n ...options,\n type: 'url',\n };\n}\n\n/**\n * Enum 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * LOG_LEVEL: {\n * ...envEnum(['debug', 'info', 'warn', 'error'] as const, {\n * description: 'Logging level',\n * default: 'info',\n * }),\n * key: 'LOG_LEVEL',\n * }\n * };\n * ```\n */\nexport function envEnum<\n T extends string,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>\n>(\n allowed: readonly T[],\n options: O\n): O & { type: 'enum'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'enum',\n validator: (val: string): T =>\n {\n if (!allowed.includes(val as T))\n {\n throw new Error(`Must be one of: ${allowed.join(', ')}, got: ${val}`);\n }\n\n return val as T;\n },\n };\n}\n\n/**\n * JSON 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * CONFIG_JSON: {\n * ...envJson<{ host: string; port: number }>({\n * description: 'JSON configuration object',\n * required: true,\n * }),\n * key: 'CONFIG_JSON',\n * }\n * };\n * ```\n */\nexport function envJson<\n T = any,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'> = Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>\n>(\n options: O\n): O & { type: 'json'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'json',\n validator: (val: string) => parseJson<T>(val),\n };\n}\n\n/**\n * 환경변수가 클라이언트에서 접근 가능한지 확인\n * (NEXT_PUBLIC_ 접두사로 판단)\n *\n * @param key - 환경변수 키\n * @returns 클라이언트에서 접근 가능하면 true\n *\n * @example\n * ```typescript\n * isClientAccessible('NEXT_PUBLIC_API_URL'); // true\n * isClientAccessible('DATABASE_URL'); // false\n * ```\n */\nexport function isClientAccessible(key: string): boolean\n{\n return key.startsWith('NEXT_PUBLIC_');\n}\n\n/**\n * 환경변수가 서버 전용인지 확인\n * (NEXT_PUBLIC_ 접두사가 없으면 서버 전용)\n *\n * @param key - 환경변수 키\n * @returns 서버 전용이면 true\n *\n * @example\n * ```typescript\n * isServerOnly('DATABASE_URL'); // true\n * isServerOnly('NEXT_PUBLIC_API_URL'); // false\n * ```\n */\nexport function isServerOnly(key: string): boolean\n{\n return !isClientAccessible(key);\n}\n\n/**\n * 스키마의 nextjs 옵션 값 결정\n *\n * 명시적으로 지정되지 않은 경우:\n * - NEXT_PUBLIC_* → true\n * - 그 외 → false\n *\n * @param schema - 환경변수 스키마\n * @returns Next.js 프로세스에서 사용 가능 여부\n */\nexport function isNextjsAccessible(schema: EnvVarSchema): boolean\n{\n if (schema.nextjs !== undefined)\n {\n return schema.nextjs;\n }\n\n return isClientAccessible(schema.key);\n}\n\n/**\n * 스키마가 SPFN 서버 전용인지 확인\n *\n * @param schema - 환경변수 스키마\n * @returns SPFN 서버에서만 사용되면 true\n */\nexport function isSpfnServerOnly(schema: EnvVarSchema): boolean\n{\n return !isNextjsAccessible(schema);\n}","/**\n * Environment Variable Registry\n *\n * 환경변수 스키마를 등록하고 타입 안전하게 접근할 수 있는 레지스트리\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 검증 + env 반환\n * console.log(env.DATABASE_URL);\n * ```\n *\n * @module env/registry\n */\nimport type { EnvVarSchema, EnvSchemaCollection, InferEnvType } from './schema';\nimport { isClientAccessible } from './schema';\nimport { logger } from '@spfn/core/logger';\n\nconst envLogger = logger.child('@spfn/core:env-registry')\n\n\n/**\n * 환경변수 레지스트리\n *\n * 스키마 기반 환경변수 관리 및 검증\n */\nexport class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection>\n{\n private schemas = new Map<string, EnvVarSchema>();\n private hasValidated = false;\n\n constructor(schemas?: T)\n {\n if (schemas)\n {\n this.registerMultiple(schemas);\n }\n }\n\n /**\n * 스키마 등록\n */\n register(schema: EnvVarSchema): void\n {\n this.schemas.set(schema.key, schema);\n }\n\n /**\n * 여러 스키마 등록\n */\n registerMultiple(schemas: EnvSchemaCollection): void\n {\n for (const [key, schema] of Object.entries(schemas))\n {\n this.register({ ...schema, key });\n }\n }\n\n /**\n * 검증 상태 리셋 (테스트용)\n */\n reset(): void\n {\n this.hasValidated = false;\n }\n\n /**\n * 환경변수 원시값 가져오기 (fallback 지원)\n */\n private getRawValue(key: string, fallbackKeys?: string[]): string | undefined\n {\n let value = process.env[key];\n\n if (!value && fallbackKeys)\n {\n for (const fallbackKey of fallbackKeys)\n {\n value = process.env[fallbackKey];\n if (value)\n {\n break;\n }\n }\n }\n\n return value;\n }\n\n /**\n * 값에 validator 적용\n */\n private applyValidator<U>(\n value: string,\n schema: EnvVarSchema<U>\n ): U\n {\n if (schema.validator)\n {\n return schema.validator(value) as U;\n }\n\n return value as U;\n }\n\n /**\n * 스키마 검증 수행 (값 읽기 없이)\n *\n * @internal\n */\n private validateSchemas(): void\n {\n // Skip if already validated\n if (this.hasValidated)\n {\n return;\n }\n\n const warnings: string[] = [];\n\n // 클라이언트 변수 중 민감정보 경고\n for (const [key, schema] of this.schemas)\n {\n if (isClientAccessible(key) && schema.sensitive)\n {\n warnings.push(\n `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*). Remove NEXT_PUBLIC_ prefix or unmark as sensitive.`\n );\n }\n }\n\n // Log warnings\n if (warnings.length > 0)\n {\n envLogger.warn('Environment validation warnings:');\n warnings.forEach(w => envLogger.warn(` - ${w}`));\n }\n\n this.hasValidated = true;\n }\n\n /**\n * 실제 접근 시점에 환경변수 값 가져오기 및 검증\n *\n * @internal\n */\n private getAndValidate(key: string): any\n {\n const schema = this.schemas.get(key);\n if (!schema)\n {\n return undefined;\n }\n\n // Get raw value using common helper\n const value = this.getRawValue(key, schema.fallbackKeys);\n\n // Check if required\n if (schema.required && !value)\n {\n const fallbackHint = schema.fallbackKeys\n ? ` (or ${schema.fallbackKeys.join(', ')})`\n : '';\n\n const errorMsg = `${key}${fallbackHint} is required but not set. ${schema.description || ''}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // If no value and not required, use default\n if (!value)\n {\n return schema.default;\n }\n\n // Check minLength\n if (schema.minLength !== undefined && value.length < schema.minLength)\n {\n const errorMsg = `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // Apply validator\n try\n {\n return this.applyValidator(value, schema);\n }\n catch (error)\n {\n const errorMsg = `${key} validation failed: ${error instanceof Error ? error.message : String(error)}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n }\n\n /**\n * 환경변수 검증 및 타입 안전한 env 객체 반환\n *\n * Proxy 기반으로 구현되어 실제 환경변수 접근 시점에 값을 읽고 검증합니다.\n * 이를 통해 dotenv 로딩 타이밍과 무관하게 최신 환경변수 값을 가져올 수 있습니다.\n *\n * @returns 검증된 환경변수 객체 (Proxy)\n * @throws {Error} 필수 변수 누락 또는 검증 실패 시\n *\n * @example\n * ```typescript\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 스키마만 검증\n * // ... dotenv 로딩 ...\n * console.log(env.DATABASE_URL); // 이 시점에 실제 값 읽기\n * ```\n */\n validate(): InferEnvType<T>\n {\n // Perform schema-level validation (without reading values)\n this.validateSchemas();\n\n // Return Proxy that lazily reads and validates on access\n return new Proxy({} as InferEnvType<T>, {\n get: (_target, prop: string) =>\n {\n return this.getAndValidate(prop);\n },\n\n ownKeys: () =>\n {\n return Array.from(this.schemas.keys());\n },\n\n getOwnPropertyDescriptor: (_target, prop: string) =>\n {\n if (this.schemas.has(prop))\n {\n return {\n enumerable: true,\n configurable: true,\n get: () => this.getAndValidate(prop)\n };\n }\n\n return undefined;\n },\n\n has: (_target, prop: string) =>\n {\n return this.schemas.has(prop);\n }\n });\n }\n}\n\n/**\n * 레지스트리 생성 헬퍼\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate();\n * ```\n */\nexport function createEnvRegistry<T extends EnvSchemaCollection>(\n schemas: T\n): EnvRegistry<T>\n{\n return new EnvRegistry(schemas);\n}"]}
1
+ {"version":3,"sources":["../../src/env/validator.ts","../../src/env/schema.ts","../../src/env/registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment Variable Management - Parsers\n *\n * Parser functions that transform and validate environment variable strings.\n * All parsers follow the pattern: (value: string) => T or throw Error\n */\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\n/**\n * Parser function that transforms and validates a string value\n * @throws Error if validation fails\n */\nexport type Parser<T> = (value: string) => T;\n\n// ============================================================================\n// String Parsers\n// ============================================================================\n\n/**\n * Parse a non-empty string\n *\n * @param value - Value to parse\n * @returns Trimmed string\n * @throws Error if string is empty after trimming\n *\n * @example\n * ```typescript\n * const name = getEnvVar('APP_NAME', {\n * validator: parseString,\n * });\n * ```\n */\nexport function parseString(value: string): string\n{\n const trimmed = value.trim();\n\n if (trimmed.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n return trimmed;\n}\n\n/**\n * Create a string parser with validation rules\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: createStringParser({\n * minLength: 32,\n * pattern: /^[A-Za-z0-9_-]+$/,\n * }),\n * });\n * ```\n */\nexport function createStringParser(\n options: {\n minLength?: number;\n maxLength?: number;\n pattern?: RegExp;\n trim?: boolean;\n } = {}\n): Parser<string>\n{\n return (value: string): string =>\n {\n const { minLength, maxLength, pattern, trim = true } = options;\n\n let result = trim ? value.trim() : value;\n\n // Empty check\n if (result.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n // Length validation\n if (minLength !== undefined && result.length < minLength)\n {\n throw new Error(`Must be at least ${minLength} characters long (current: ${result.length})`);\n }\n\n if (maxLength !== undefined && result.length > maxLength)\n {\n throw new Error(`Must be at most ${maxLength} characters long (current: ${result.length})`);\n }\n\n // Pattern validation\n if (pattern && !pattern.test(result))\n {\n throw new Error(`Must match pattern ${pattern}`);\n }\n\n return result;\n };\n}\n\n// ============================================================================\n// Boolean Parser\n// ============================================================================\n\n/**\n * Parse a boolean environment variable\n *\n * Accepts: 'true', '1', 'yes' (case-insensitive) → true\n * 'false', '0', 'no' (case-insensitive) → false\n *\n * @param value - Value to parse\n * @returns Boolean value\n * @throws Error if value is not a valid boolean string\n *\n * @example\n * ```typescript\n * const debug = getEnvVar('DEBUG', {\n * default: 'false',\n * validator: parseBoolean,\n * });\n * ```\n */\nexport function parseBoolean(value: string): boolean\n{\n const normalized = value.toLowerCase().trim();\n\n if (['true', '1', 'yes'].includes(normalized))\n {\n return true;\n }\n\n if (['false', '0', 'no'].includes(normalized))\n {\n return false;\n }\n\n throw new Error(\n `Must be a boolean value (true/false, 1/0, yes/no), got: ${value}`\n );\n}\n\n// ============================================================================\n// Number Parsers\n// ============================================================================\n\n/**\n * Parse and validate number\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Parsed number\n * @throws Error if invalid number or constraint violation\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: (val) => parseNumber(val, { min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function parseNumber(\n value: string,\n options: { min?: number; max?: number; integer?: boolean } = {}\n): number\n{\n const { min, max, integer = false } = options;\n\n // Reject empty strings\n if (value.trim() === '')\n {\n throw new Error('Value cannot be empty');\n }\n\n const num = Number(value);\n\n if (isNaN(num))\n {\n throw new Error(`Must be a valid number, got: ${value}`);\n }\n\n if (integer && !Number.isInteger(num))\n {\n throw new Error(`Must be an integer, got: ${value}`);\n }\n\n if (min !== undefined && num < min)\n {\n throw new Error(`Must be at least ${min}, got: ${num}`);\n }\n\n if (max !== undefined && num > max)\n {\n throw new Error(`Must be at most ${max}, got: ${num}`);\n }\n\n return num;\n}\n\n/**\n * Create a number parser with specific constraints\n *\n * @param options - Validation constraints\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: createNumberParser({ min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function createNumberParser(\n options: { min?: number; max?: number; integer?: boolean } = {}\n): Parser<number>\n{\n return (value: string) => parseNumber(value, options);\n}\n\n/**\n * Parse integer with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed integer\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const retries = getEnvVar('MAX_RETRIES', {\n * default: '3',\n * validator: (val) => parseInteger(val, { min: 1, max: 10 }),\n * });\n * ```\n */\nexport function parseInteger(\n value: string,\n options: { min?: number; max?: number } = {}\n): number\n{\n return parseNumber(value, { ...options, integer: true });\n}\n\n/**\n * Parse float/decimal number with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed decimal number\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const ratio = getEnvVar('CACHE_RATIO', {\n * default: '0.75',\n * validator: (val) => parseDecimal(val, { min: 0, max: 1 }),\n * });\n * ```\n */\nexport function parseDecimal(\n value: string,\n options: { min?: number; max?: number } = {}\n): number\n{\n return parseNumber(value, { ...options, integer: false });\n}\n\n// ============================================================================\n// URL Parsers\n// ============================================================================\n\n/**\n * Parse and validate URL\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Validated URL string\n * @throws Error if invalid URL or protocol mismatch\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: (val) => parseUrl(val, { protocol: 'https' }),\n * });\n * ```\n */\nexport function parseUrl(\n value: string,\n options: { protocol?: 'http' | 'https' | 'any' } = {}\n): string\n{\n const { protocol = 'any' } = options;\n\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error(`Invalid URL: ${value}`);\n }\n throw error;\n }\n\n // Validate protocol\n if (protocol === 'http' && url.protocol !== 'http:')\n {\n throw new Error(`URL must use HTTP protocol, got ${url.protocol}`);\n }\n\n if (protocol === 'https' && url.protocol !== 'https:')\n {\n throw new Error(`URL must use HTTPS protocol, got ${url.protocol}`);\n }\n\n return value;\n}\n\n/**\n * Create a URL parser with specific protocol requirement\n *\n * @param protocol - Required protocol ('http', 'https', or 'any')\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: createUrlParser('https'),\n * });\n * ```\n */\nexport function createUrlParser(protocol: 'http' | 'https' | 'any' = 'any'): Parser<string>\n{\n return (value: string) => parseUrl(value, { protocol });\n}\n\n/**\n * Parse PostgreSQL connection string\n *\n * @param value - Value to parse\n * @returns Validated PostgreSQL URL string\n * @throws Error if invalid PostgreSQL URL\n *\n * @example\n * ```typescript\n * const dbUrl = getEnvVar('DATABASE_URL', {\n * required: true,\n * validator: parsePostgresUrl,\n * });\n * ```\n */\nexport function parsePostgresUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error(`Invalid PostgreSQL URL: ${value}`);\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'postgres:' && url.protocol !== 'postgresql:')\n {\n throw new Error(\n `Must be a PostgreSQL URL (postgres:// or postgresql://), got ${url.protocol}`\n );\n }\n\n return value;\n}\n\n/**\n * Parse Redis connection string\n *\n * @param value - Value to parse\n * @returns Validated Redis URL string\n * @throws Error if invalid Redis URL\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * required: true,\n * validator: parseRedisUrl,\n * });\n * ```\n */\nexport function parseRedisUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error(`Invalid Redis URL: ${value}`);\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'redis:' && url.protocol !== 'rediss:')\n {\n throw new Error(\n `Must be a Redis URL (redis:// or rediss://), got ${url.protocol}`\n );\n }\n\n return value;\n}\n\n// ============================================================================\n// Enum Parser\n// ============================================================================\n\n/**\n * Parse and validate enum value\n *\n * @param value - Value to parse\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Validated enum value\n * @throws Error if value not in allowed list\n *\n * @example\n * ```typescript\n * const env = getEnvVar('NODE_ENV', {\n * validator: (val) => parseEnum(val, ['development', 'production', 'test']),\n * });\n * ```\n */\nexport function parseEnum(\n value: string,\n allowed: string[],\n caseInsensitive = false\n): string\n{\n if (caseInsensitive)\n {\n const normalizedValue = value.toLowerCase();\n const normalizedAllowed = allowed.map((v) => v.toLowerCase());\n const index = normalizedAllowed.indexOf(normalizedValue);\n\n if (index === -1)\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}], got: ${value}`\n );\n }\n\n return allowed[index]; // Return original case from allowed list\n }\n\n if (!allowed.includes(value))\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}], got: ${value}`\n );\n }\n\n return value;\n}\n\n/**\n * Create an enum parser with specific allowed values\n *\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const logLevel = getEnvVar('LOG_LEVEL', {\n * default: 'info',\n * validator: createEnumParser(['debug', 'info', 'warn', 'error']),\n * });\n * ```\n */\nexport function createEnumParser(allowed: string[], caseInsensitive = false): Parser<string>\n{\n return (value: string) => parseEnum(value, allowed, caseInsensitive);\n}\n\n// ============================================================================\n// JSON Parser\n// ============================================================================\n\n/**\n * Parse JSON string\n *\n * @param value - JSON string to parse\n * @returns Parsed JSON value\n * @throws Error if invalid JSON\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: parseJson,\n * });\n * ```\n */\nexport function parseJson<T = any>(value: string): T\n{\n try\n {\n return JSON.parse(value) as T;\n }\n catch (error)\n {\n throw new Error(\n `Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n}\n\n/**\n * Create a typed JSON parser\n *\n * @returns Parser function\n *\n * @example\n * ```typescript\n * interface Config {\n * host: string;\n * port: number;\n * }\n *\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: createJsonParser<Config>(),\n * });\n * ```\n */\nexport function createJsonParser<T>(): Parser<T>\n{\n return (value: string) => parseJson<T>(value);\n}\n\n// ============================================================================\n// Array Parser\n// ============================================================================\n\n/**\n * Parse comma-separated values into array\n *\n * @param value - Comma-separated string\n * @param options - Parser options\n * @returns Array of strings\n *\n * @example\n * ```typescript\n * const hosts = getEnvVar('ALLOWED_HOSTS', {\n * validator: parseArray,\n * });\n * // \"localhost,example.com,api.example.com\" → ['localhost', 'example.com', 'api.example.com']\n * ```\n */\nexport function parseArray(\n value: string,\n options: {\n separator?: string;\n trim?: boolean;\n filter?: (item: string) => boolean;\n } = {}\n): string[]\n{\n const { separator = ',', trim = true, filter } = options;\n\n if (value.trim() === '')\n {\n return [];\n }\n\n let items = value.split(separator);\n\n if (trim)\n {\n items = items.map((item) => item.trim());\n }\n\n if (filter)\n {\n items = items.filter(filter);\n }\n\n return items;\n}\n\n/**\n * Create an array parser with item parser\n *\n * @param itemParser - Parser to apply to each array item\n * @param options - Array parsing options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * // Parse comma-separated ports\n * const ports = getEnvVar('PORTS', {\n * validator: createArrayParser(\n * createNumberParser({ min: 1, max: 65535, integer: true })\n * ),\n * });\n * // \"3000,4000,5000\" → [3000, 4000, 5000]\n * ```\n */\nexport function createArrayParser<T>(\n itemParser: Parser<T>,\n options: { separator?: string } = {}\n): Parser<T[]>\n{\n return (value: string): T[] =>\n {\n const items = parseArray(value, options);\n\n return items.map((item, index) =>\n {\n try\n {\n return itemParser(item);\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid item at index ${index}: ${message}`);\n }\n });\n };\n}\n\n// ============================================================================\n// Secure Secret Parser\n// ============================================================================\n\n/**\n * Calculate Shannon entropy of a string\n * Returns entropy in bits per character\n *\n * @param str - String to calculate entropy for\n * @returns Entropy value (0 to ~6.6 bits for printable ASCII)\n *\n * @example\n * ```typescript\n * const entropy = calculateEntropy('my-secret-key');\n * // Higher entropy = more random\n * // - Random lowercase: ~4.7 bits/char\n * // - Random alphanumeric: ~5.2 bits/char\n * // - Random printable ASCII: ~6.6 bits/char\n * // - \"aaaaaaa...\": ~0 bits/char\n * ```\n */\nfunction calculateEntropy(str: string): number\n{\n const len = str.length;\n const frequencies = new Map<string, number>();\n\n // Count character frequencies\n for (const char of str)\n {\n frequencies.set(char, (frequencies.get(char) || 0) + 1);\n }\n\n // Calculate Shannon entropy\n let entropy = 0;\n for (const count of frequencies.values())\n {\n const probability = count / len;\n entropy -= probability * Math.log2(probability);\n }\n\n return entropy;\n}\n\n/**\n * Create a secure secret parser with entropy validation\n *\n * Validates cryptographic secrets for sufficient length, character diversity, and randomness.\n * Uses Shannon entropy to measure randomness quality.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const sessionSecret = getEnvVar('SESSION_SECRET', {\n * validator: createSecureSecretParser({\n * minLength: 32, // Minimum 256-bit\n * minUniqueChars: 16, // Character diversity\n * minEntropy: 3.5, // Shannon entropy (bits/char)\n * }),\n * });\n * ```\n */\nexport function createSecureSecretParser(\n options: {\n minLength?: number;\n minUniqueChars?: number;\n minEntropy?: number;\n } = {}\n): Parser<string>\n{\n const {\n minLength = 32,\n minUniqueChars = 16,\n minEntropy = 3.5,\n } = options;\n\n return (value: string): string =>\n {\n const length = value.length;\n const uniqueChars = new Set(value).size;\n const entropy = calculateEntropy(value);\n\n // Check length (minimum for cryptographic strength)\n if (length < minLength)\n {\n throw new Error(\n `Secret too short: ${length} characters (minimum: ${minLength})`\n );\n }\n\n // Check unique character diversity\n if (uniqueChars < minUniqueChars)\n {\n throw new Error(\n `Secret has low diversity: ${uniqueChars} unique characters (minimum: ${minUniqueChars})`\n );\n }\n\n // Check Shannon entropy (randomness quality)\n // Reference values:\n // - Random lowercase: ~4.7 bits/char\n // - Random alphanumeric: ~5.2 bits/char\n // - Random printable ASCII: ~6.6 bits/char\n // - \"aaaaaaa...\": ~0 bits/char\n // - \"abcabcabc...\": ~1.58 bits/char\n if (entropy < minEntropy)\n {\n throw new Error(\n `Secret has low entropy: ${entropy.toFixed(2)} bits/char (minimum: ${minEntropy}). Use a more random secret.`\n );\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Password Parser\n// ============================================================================\n\n/**\n * Create a password strength parser\n *\n * Validates password strength based on configurable requirements.\n * Useful for enforcing password policies in environment variables or user input.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const adminPassword = getEnvVar('ADMIN_PASSWORD', {\n * validator: createPasswordParser({\n * minLength: 12,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireNumber: true,\n * requireSpecial: true,\n * }),\n * });\n * ```\n */\nexport function createPasswordParser(\n options: {\n minLength?: number;\n requireUppercase?: boolean;\n requireLowercase?: boolean;\n requireNumber?: boolean;\n requireSpecial?: boolean;\n } = {}\n): Parser<string>\n{\n const {\n minLength = 8,\n requireUppercase = true,\n requireLowercase = true,\n requireNumber = true,\n requireSpecial = true,\n } = options;\n\n return (value: string): string =>\n {\n const errors: string[] = [];\n\n // Length check\n if (value.length < minLength)\n {\n errors.push(`Must be at least ${minLength} characters`);\n }\n\n // Uppercase check\n if (requireUppercase && !/[A-Z]/.test(value))\n {\n errors.push('Must contain at least one uppercase letter');\n }\n\n // Lowercase check\n if (requireLowercase && !/[a-z]/.test(value))\n {\n errors.push('Must contain at least one lowercase letter');\n }\n\n // Number check\n if (requireNumber && !/[0-9]/.test(value))\n {\n errors.push('Must contain at least one number');\n }\n\n // Special character check\n if (requireSpecial && !/[^A-Za-z0-9]/.test(value))\n {\n errors.push('Must contain at least one special character');\n }\n\n if (errors.length > 0)\n {\n throw new Error(`Password validation failed: ${errors.join(', ')}`);\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Parser Composition\n// ============================================================================\n\n/**\n * Chain multiple parsers sequentially\n *\n * Each parser receives the output of the previous parser.\n * Useful for multi-step validation/transformation.\n *\n * @param parsers - Array of parser functions\n * @returns Combined parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: chain(\n * parseString,\n * createStringParser({ minLength: 32, pattern: /^[A-Za-z0-9_-]+$/ }),\n * ),\n * });\n * ```\n */\nexport function chain<T>(...parsers: Array<Parser<T>>): Parser<T>\n{\n return (value: string): T =>\n {\n let result = value as any;\n\n for (const parser of parsers)\n {\n result = parser(result);\n }\n\n return result;\n };\n}\n\n/**\n * Apply parser with fallback value\n *\n * If parser throws, returns fallback instead.\n * Useful for optional environment variables with complex parsing.\n *\n * @param parser - Parser to attempt\n * @param fallback - Fallback value if parsing fails\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: withFallback(parseJson, { host: 'localhost', port: 3000 }),\n * });\n * ```\n */\nexport function withFallback<T>(parser: Parser<T>, fallback: T): Parser<T>\n{\n return (value: string): T =>\n {\n try\n {\n return parser(value);\n }\n catch\n {\n return fallback;\n }\n };\n}\n\n/**\n * Make parser optional\n *\n * Returns undefined for empty strings instead of throwing.\n *\n * @param parser - Parser to make optional\n * @returns Parser function that returns T | undefined\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * validator: optional(parseRedisUrl),\n * });\n * // Empty string → undefined\n * // Valid URL → parsed URL\n * // Invalid URL → throws\n * ```\n */\nexport function optional<T>(parser: Parser<T>): Parser<T | undefined>\n{\n return (value: string): T | undefined =>\n {\n if (value.trim() === '')\n {\n return undefined;\n }\n\n return parser(value);\n };\n}","/**\n * Environment Variable Schema Definition System\n *\n * 환경변수에 메타데이터를 정의하여 중앙 관리, 문서화, 검증을 지원합니다.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envUrl({\n * description: 'Database connection',\n * required: true,\n * validator: parsePostgresUrl,\n * sensitive: true,\n * })\n * });\n * ```\n *\n * @module env/schema\n */\n\nimport { parseBoolean, parseNumber, parseJson } from './validator';\n\n/**\n * 환경변수 스키마 정의\n */\nexport interface EnvVarSchema<T = string>\n{\n /** 환경변수 키 */\n key: string;\n\n /** 설명 (목적, 사용처) */\n description: string;\n\n /** 타입 */\n type: 'string' | 'number' | 'boolean' | 'url' | 'enum' | 'json';\n\n /** 필수 여부 */\n required?: boolean;\n\n /** 기본값 */\n default?: T;\n\n /** 검증/변환 함수 */\n validator?: (value: string) => T;\n\n // === 검증 옵션 ===\n\n /** Fallback 환경변수 키들 (backward compatibility) */\n fallbackKeys?: string[];\n\n /** 최소 길이 (문자열 타입) */\n minLength?: number;\n\n // === 메타데이터 ===\n\n /** 민감정보 여부 (로깅 시 마스킹) */\n sensitive?: boolean;\n\n /** 예시 값들 (타입과 일치해야 함) */\n examples?: T[];\n\n // === 파일 분리 ===\n\n /**\n * Next.js 프로세스에서 사용 여부\n *\n * - true: .env.local에 존재해야 함 (Next.js 서버 컴포넌트에서 접근 가능)\n * - false: .env.server.local에만 존재해야 함 (SPFN 서버에서만 접근)\n *\n * @default NEXT_PUBLIC_* 이면 true, 아니면 false\n */\n nextjs?: boolean;\n}\n\n/**\n * 스키마 컬렉션 타입\n */\nexport type EnvSchemaCollection = Record<string, EnvVarSchema<any>>;\n\n/**\n * Helper type: Check if field has default value\n */\ntype HasDefault<T> = T extends { default: any } ? true : false;\n\n/**\n * Helper type: Check if field is explicitly required\n */\ntype IsRequired<T> = T extends { required: true } ? true : false;\n\n/**\n * Helper type: Check if field should be required (has default OR required: true)\n */\ntype ShouldBeRequired<T> = HasDefault<T> extends true ? true : IsRequired<T>;\n\n/**\n * 스키마로부터 타입 추출\n *\n * required: true 또는 default가 있는 필드 → 필수\n * required: false 또는 미지정 → optional (| undefined)\n */\nexport type InferEnvType<T extends EnvSchemaCollection> = {\n // Required fields (required: true OR has default)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? K : never]:\n T[K] extends EnvVarSchema<infer U> ? U : string;\n} & {\n // Optional fields (required: false OR not specified)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? never : K]?:\n T[K] extends EnvVarSchema<infer U> ? U | undefined : string | undefined;\n};\n\n/**\n * 스키마 정의 헬퍼 (타입 추론 지원)\n *\n * Automatically fills in the `key` property from object keys.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n * // Automatically adds key: 'DATABASE_URL'\n * ```\n */\nexport function defineEnvSchema<T extends Record<string, any>>(\n schema: T\n): { [K in keyof T]: T[K] & { key: K } }\n{\n const result: any = {};\n\n for (const key in schema)\n {\n result[key] = {\n ...schema[key],\n key,\n };\n }\n\n return result;\n}\n\n/**\n * 문자열 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * API_KEY: {\n * ...envString({\n * description: 'API authentication key',\n * required: true,\n * sensitive: true,\n * }),\n * key: 'API_KEY',\n * }\n * };\n * ```\n */\nexport function envString<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T\n): T & { type: 'string' }\n{\n return {\n ...options,\n type: 'string',\n };\n}\n\n/**\n * 숫자 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * PORT: {\n * ...envNumber({\n * description: 'Server port',\n * default: 3000,\n * validator: createNumberParser({ min: 1, max: 65535 }),\n * }),\n * key: 'PORT',\n * }\n * };\n * ```\n */\nexport function envNumber<T extends Omit<EnvVarSchema<number>, 'key' | 'type'>>(\n options: T\n): T & { type: 'number'; validator: (value: string) => number }\n{\n return {\n ...options,\n type: 'number',\n validator: options.validator || parseNumber,\n };\n}\n\n/**\n * Boolean 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DEBUG: {\n * ...envBoolean({\n * description: 'Enable debug mode',\n * default: false,\n * }),\n * key: 'DEBUG',\n * }\n * };\n * ```\n */\nexport function envBoolean<T extends Omit<EnvVarSchema<boolean>, 'key' | 'type'>>(\n options: T\n): T & { type: 'boolean'; validator: (value: string) => boolean }\n{\n return {\n ...options,\n type: 'boolean',\n validator: options.validator || parseBoolean,\n };\n}\n\n/**\n * URL 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DATABASE_URL: {\n * ...envUrl({\n * description: 'Database connection URL',\n * required: true,\n * validator: parsePostgresUrl,\n * }),\n * key: 'DATABASE_URL',\n * }\n * };\n * ```\n */\nexport function envUrl<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T\n): T & { type: 'url' }\n{\n return {\n ...options,\n type: 'url',\n };\n}\n\n/**\n * Enum 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * LOG_LEVEL: {\n * ...envEnum(['debug', 'info', 'warn', 'error'] as const, {\n * description: 'Logging level',\n * default: 'info',\n * }),\n * key: 'LOG_LEVEL',\n * }\n * };\n * ```\n */\nexport function envEnum<\n T extends string,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>\n>(\n allowed: readonly T[],\n options: O\n): O & { type: 'enum'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'enum',\n validator: (val: string): T =>\n {\n if (!allowed.includes(val as T))\n {\n throw new Error(`Must be one of: ${allowed.join(', ')}, got: ${val}`);\n }\n\n return val as T;\n },\n };\n}\n\n/**\n * JSON 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * CONFIG_JSON: {\n * ...envJson<{ host: string; port: number }>({\n * description: 'JSON configuration object',\n * required: true,\n * }),\n * key: 'CONFIG_JSON',\n * }\n * };\n * ```\n */\nexport function envJson<\n T = any,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'> = Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>\n>(\n options: O\n): O & { type: 'json'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'json',\n validator: (val: string) => parseJson<T>(val),\n };\n}\n\n/**\n * 환경변수가 클라이언트에서 접근 가능한지 확인\n * (NEXT_PUBLIC_ 접두사로 판단)\n *\n * @param key - 환경변수 키\n * @returns 클라이언트에서 접근 가능하면 true\n *\n * @example\n * ```typescript\n * isClientAccessible('NEXT_PUBLIC_API_URL'); // true\n * isClientAccessible('DATABASE_URL'); // false\n * ```\n */\nexport function isClientAccessible(key: string): boolean\n{\n return key.startsWith('NEXT_PUBLIC_');\n}\n\n/**\n * 환경변수가 서버 전용인지 확인\n * (NEXT_PUBLIC_ 접두사가 없으면 서버 전용)\n *\n * @param key - 환경변수 키\n * @returns 서버 전용이면 true\n *\n * @example\n * ```typescript\n * isServerOnly('DATABASE_URL'); // true\n * isServerOnly('NEXT_PUBLIC_API_URL'); // false\n * ```\n */\nexport function isServerOnly(key: string): boolean\n{\n return !isClientAccessible(key);\n}\n\n/**\n * 스키마의 nextjs 옵션 값 결정\n *\n * 명시적으로 지정되지 않은 경우:\n * - NEXT_PUBLIC_* → true\n * - 그 외 → false\n *\n * @param schema - 환경변수 스키마\n * @returns Next.js 프로세스에서 사용 가능 여부\n */\nexport function isNextjsAccessible(schema: EnvVarSchema): boolean\n{\n if (schema.nextjs !== undefined)\n {\n return schema.nextjs;\n }\n\n return isClientAccessible(schema.key);\n}\n\n/**\n * 스키마가 SPFN 서버 전용인지 확인\n *\n * @param schema - 환경변수 스키마\n * @returns SPFN 서버에서만 사용되면 true\n */\nexport function isSpfnServerOnly(schema: EnvVarSchema): boolean\n{\n return !isNextjsAccessible(schema);\n}","/**\n * Environment Variable Registry\n *\n * 환경변수 스키마를 등록하고 타입 안전하게 접근할 수 있는 레지스트리\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 검증 + env 반환\n * console.log(env.DATABASE_URL);\n * ```\n *\n * @module env/registry\n */\nimport type { EnvVarSchema, EnvSchemaCollection, InferEnvType } from './schema';\nimport { isClientAccessible } from './schema';\nimport { logger } from '@spfn/core/logger';\n\nconst envLogger = logger.child('@spfn/core:env-registry')\n\n\n/**\n * 환경변수 레지스트리\n *\n * 스키마 기반 환경변수 관리 및 검증\n */\nexport class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection>\n{\n private schemas = new Map<string, EnvVarSchema>();\n private hasValidated = false;\n\n constructor(schemas?: T)\n {\n if (schemas)\n {\n this.registerMultiple(schemas);\n }\n }\n\n /**\n * 스키마 등록\n */\n register(schema: EnvVarSchema): void\n {\n this.schemas.set(schema.key, schema);\n }\n\n /**\n * 여러 스키마 등록\n */\n registerMultiple(schemas: EnvSchemaCollection): void\n {\n for (const [key, schema] of Object.entries(schemas))\n {\n this.register({ ...schema, key });\n }\n }\n\n /**\n * 검증 상태 리셋 (테스트용)\n */\n reset(): void\n {\n this.hasValidated = false;\n }\n\n /**\n * 환경변수 원시값 가져오기 (fallback 지원)\n */\n private getRawValue(key: string, fallbackKeys?: string[]): string | undefined\n {\n let value = process.env[key];\n\n if (!value && fallbackKeys)\n {\n for (const fallbackKey of fallbackKeys)\n {\n value = process.env[fallbackKey];\n if (value)\n {\n break;\n }\n }\n }\n\n return value;\n }\n\n /**\n * 값에 validator 적용\n */\n private applyValidator<U>(\n value: string,\n schema: EnvVarSchema<U>\n ): U\n {\n if (schema.validator)\n {\n return schema.validator(value) as U;\n }\n\n return value as U;\n }\n\n /**\n * 스키마 검증 수행 (값 읽기 없이)\n *\n * @internal\n */\n private validateSchemas(): void\n {\n // Skip if already validated\n if (this.hasValidated)\n {\n return;\n }\n\n const warnings: string[] = [];\n\n // 클라이언트 변수 중 민감정보 경고\n for (const [key, schema] of this.schemas)\n {\n if (isClientAccessible(key) && schema.sensitive)\n {\n warnings.push(\n `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*). Remove NEXT_PUBLIC_ prefix or unmark as sensitive.`\n );\n }\n }\n\n // Log warnings\n if (warnings.length > 0)\n {\n envLogger.warn('Environment validation warnings:');\n warnings.forEach(w => envLogger.warn(` - ${w}`));\n }\n\n this.hasValidated = true;\n }\n\n /**\n * SKIP_ENV_VALIDATION 환경변수 확인\n */\n private shouldSkipValidation(): boolean\n {\n const skip = process.env.SKIP_ENV_VALIDATION;\n return skip === 'true' || skip === '1';\n }\n\n /**\n * 실제 접근 시점에 환경변수 값 가져오기 및 검증\n *\n * @internal\n */\n private getAndValidate(key: string): any\n {\n const schema = this.schemas.get(key);\n if (!schema)\n {\n return undefined;\n }\n\n // Get raw value using common helper\n const value = this.getRawValue(key, schema.fallbackKeys);\n\n // Check if required (skip if SKIP_ENV_VALIDATION is set)\n if (schema.required && !value && !this.shouldSkipValidation())\n {\n const fallbackHint = schema.fallbackKeys\n ? ` (or ${schema.fallbackKeys.join(', ')})`\n : '';\n\n const errorMsg = `${key}${fallbackHint} is required but not set. ${schema.description || ''}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // If no value and not required, use default\n if (!value)\n {\n return schema.default;\n }\n\n // Check minLength\n if (schema.minLength !== undefined && value.length < schema.minLength)\n {\n const errorMsg = `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // Apply validator\n try\n {\n return this.applyValidator(value, schema);\n }\n catch (error)\n {\n const errorMsg = `${key} validation failed: ${error instanceof Error ? error.message : String(error)}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n }\n\n /**\n * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)\n *\n * CLI에서 사용하기 위한 메서드로, 모든 required 환경변수를 강제 검증합니다.\n *\n * @returns 검증 결과 (errors, warnings)\n */\n validateAll(): { errors: Array<{ key: string; message: string }>; warnings: Array<{ key: string; message: string }> }\n {\n const errors: Array<{ key: string; message: string }> = [];\n const warnings: Array<{ key: string; message: string }> = [];\n\n for (const [key, schema] of this.schemas)\n {\n // 클라이언트 변수 중 민감정보 경고\n if (isClientAccessible(key) && schema.sensitive)\n {\n warnings.push({\n key,\n message: `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*).`,\n });\n }\n\n const value = this.getRawValue(key, schema.fallbackKeys);\n\n // Check required\n if (schema.required && !value)\n {\n const fallbackHint = schema.fallbackKeys\n ? ` (or ${schema.fallbackKeys.join(', ')})`\n : '';\n errors.push({\n key,\n message: `${key}${fallbackHint} is required but not set. ${schema.description || ''}`,\n });\n continue;\n }\n\n // Check minLength\n if (value && schema.minLength !== undefined && value.length < schema.minLength)\n {\n errors.push({\n key,\n message: `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`,\n });\n continue;\n }\n\n // Check validator\n if (value && schema.validator)\n {\n try\n {\n schema.validator(value);\n }\n catch (error)\n {\n errors.push({\n key,\n message: `${key} validation failed: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n }\n\n return { errors, warnings };\n }\n\n /**\n * 환경변수 검증 및 타입 안전한 env 객체 반환\n *\n * Proxy 기반으로 구현되어 실제 환경변수 접근 시점에 값을 읽고 검증합니다.\n * 이를 통해 dotenv 로딩 타이밍과 무관하게 최신 환경변수 값을 가져올 수 있습니다.\n *\n * @returns 검증된 환경변수 객체 (Proxy)\n * @throws {Error} 필수 변수 누락 또는 검증 실패 시\n *\n * @example\n * ```typescript\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 스키마만 검증\n * // ... dotenv 로딩 ...\n * console.log(env.DATABASE_URL); // 이 시점에 실제 값 읽기\n * ```\n */\n validate(): InferEnvType<T>\n {\n // Perform schema-level validation (without reading values)\n this.validateSchemas();\n\n // Return Proxy that lazily reads and validates on access\n return new Proxy({} as InferEnvType<T>, {\n get: (_target, prop: string) =>\n {\n return this.getAndValidate(prop);\n },\n\n ownKeys: () =>\n {\n return Array.from(this.schemas.keys());\n },\n\n getOwnPropertyDescriptor: (_target, prop: string) =>\n {\n if (this.schemas.has(prop))\n {\n return {\n enumerable: true,\n configurable: true,\n get: () => this.getAndValidate(prop)\n };\n }\n\n return undefined;\n },\n\n has: (_target, prop: string) =>\n {\n return this.schemas.has(prop);\n }\n });\n }\n}\n\n/**\n * 환경변수 검증 결과\n */\nexport interface EnvValidationResult\n{\n valid: boolean;\n errors: Array<{ key: string; message: string }>;\n warnings: Array<{ key: string; message: string }>;\n}\n\n/**\n * 레지스트리 생성 헬퍼\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate();\n * ```\n */\nexport function createEnvRegistry<T extends EnvSchemaCollection>(\n schemas: T\n): EnvRegistry<T>\n{\n return new EnvRegistry(schemas);\n}\n\n/**\n * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)\n *\n * CLI `spfn env validate` 명령어에서 사용\n *\n * @param registries - 검증할 레지스트리 배열\n * @returns 검증 결과\n *\n * @example\n * ```typescript\n * const result = validateAllEnv([coreRegistry, authRegistry]);\n * if (!result.valid) {\n * console.error('Missing env vars:', result.errors);\n * process.exit(1);\n * }\n * ```\n */\nexport function validateAllEnv(\n registries: EnvRegistry<any>[]\n): EnvValidationResult\n{\n const errors: Array<{ key: string; message: string }> = [];\n const warnings: Array<{ key: string; message: string }> = [];\n\n for (const registry of registries)\n {\n const result = registry.validateAll();\n errors.push(...result.errors);\n warnings.push(...result.warnings);\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}"]}
@@ -1,7 +1,7 @@
1
1
  import { logger } from '@spfn/core/logger';
2
+ import { FormatRegistry, Type } from '@sinclair/typebox';
2
3
  import { Value } from '@sinclair/typebox/value';
3
4
  import { ValidationError } from '@spfn/core/errors';
4
- import { Type } from '@sinclair/typebox';
5
5
 
6
6
  // src/route/route-builder.ts
7
7
  var RouteBuilder = class _RouteBuilder {
@@ -260,6 +260,26 @@ function createRouterInstance(routes, packageRouters = [], globalMiddlewares = [
260
260
  function defineRouter(routes) {
261
261
  return createRouterInstance(routes);
262
262
  }
263
+ FormatRegistry.Set(
264
+ "email",
265
+ (value) => typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
266
+ );
267
+ FormatRegistry.Set(
268
+ "uri",
269
+ (value) => typeof value === "string" && /^https?:\/\/.+/.test(value)
270
+ );
271
+ FormatRegistry.Set(
272
+ "uuid",
273
+ (value) => typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)
274
+ );
275
+ FormatRegistry.Set(
276
+ "date",
277
+ (value) => typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)
278
+ );
279
+ FormatRegistry.Set(
280
+ "date-time",
281
+ (value) => typeof value === "string" && !isNaN(Date.parse(value))
282
+ );
263
283
  function validateField(schema, rawValue, fieldName) {
264
284
  if (!schema) {
265
285
  return {};
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/route/route-builder.ts","../../src/route/router.ts","../../src/route/validation.ts","../../src/route/register-routes.ts","../../src/route/define-middleware.ts","../../src/route/helpers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Route Builder\n *\n * Provides tRPC-style chainable API for route definition\n */\n\nimport type { MiddlewareHandler } from 'hono';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { HttpMethod } from './types';\n\n/**\n * Route handler function\n */\nexport type RouteHandlerFn<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown\n> = (c: RouteBuilderContext<TInput, TInterceptor>) => Response | Promise<Response> | TResponse | Promise<TResponse>;\n\n/**\n * Route definition result\n *\n * Contains all information needed for type inference and registration\n */\nexport type RouteDef<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown\n> = {\n method?: HttpMethod;\n path?: string;\n input?: TInput;\n interceptor?: TInterceptor;\n middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares?: string[] | '*';\n handler: RouteHandlerFn<TInput, TInterceptor, TResponse>;\n\n // Type inference helpers\n _input: TInput;\n _interceptor: TInterceptor;\n _response: TResponse;\n};\n\n/**\n * Route builder with chainable API (tRPC-style)\n */\nexport class RouteBuilder<\n TInput extends RouteInput = {},\n TInterceptor extends RouteInput = {},\n TResponse = never\n>\n{\n public _method?: HttpMethod;\n public _path?: string;\n public _input?: TInput;\n public _interceptor?: TInterceptor;\n public _middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n public _skipMiddlewares?: string[] | '*';\n\n /**\n * Create a new RouteBuilder with copied properties and optional overrides\n */\n private clone<\n TNewInput extends RouteInput = TInput,\n TNewInterceptor extends RouteInput = TInterceptor\n >(\n overrides?: Partial<{\n input: TNewInput;\n interceptor: TNewInterceptor;\n middlewares: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares: string[] | '*';\n }>\n ): RouteBuilder<TNewInput, TNewInterceptor, TResponse>\n {\n const builder = new RouteBuilder<TNewInput, TNewInterceptor, TResponse>();\n builder._method = this._method;\n builder._path = this._path;\n builder._input = (overrides?.input ?? this._input) as TNewInput | undefined;\n builder._interceptor = (overrides?.interceptor ?? this._interceptor) as TNewInterceptor | undefined;\n builder._middlewares = overrides?.middlewares ?? this._middlewares;\n builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;\n return builder;\n }\n\n /**\n * Define input schemas\n *\n * @example\n * ```ts\n * route.get('/users/:id')\n * .input({\n * params: Type.Object({ id: Type.String() }),\n * query: Type.Object({ page: Type.Number() }),\n * headers: Type.Object({ authorization: Type.String() })\n * })\n * .handler(async (c) => {\n * const { params, query, headers } = await c.data();\n * // params = { id: string }\n * // query = { page: number }\n * // headers = { authorization: string }\n * })\n * ```\n */\n input<TNewInput extends RouteInput>(input: TNewInput): RouteBuilder<TNewInput, TInterceptor, TResponse>\n {\n return this.clone({ input });\n }\n\n /**\n * Define fields injected by interceptors\n *\n * These fields are:\n * - Available in the handler (merged with input)\n * - Excluded from client types (codegen uses only input)\n * - Not validated by route input schema (injected by middleware)\n *\n * Use this when middleware/interceptors add fields to the request\n * before it reaches the handler.\n *\n * @example\n * ```ts\n * // Auth interceptor injects crypto key fields\n * route.post('/_auth/login')\n * .input({\n * body: Type.Object({\n * email: Type.String(),\n * password: Type.String()\n * })\n * })\n * .interceptor({\n * body: Type.Object({\n * publicKey: Type.String(),\n * keyId: Type.String(),\n * fingerprint: Type.String()\n * })\n * })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * // body type: { email, password, publicKey, keyId, fingerprint }\n * // Client only sees: { email, password }\n * return loginService(body);\n * });\n * ```\n */\n interceptor<TNewInterceptor extends RouteInput>(\n interceptor: TNewInterceptor\n ): RouteBuilder<TInput, TNewInterceptor, TResponse>\n {\n return this.clone({ interceptor });\n }\n\n /**\n * Add middlewares to the route\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n middleware(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ middlewares });\n }\n\n /**\n * Add middlewares to the route (alias for `.middleware()`)\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.middleware(middlewares);\n }\n\n /**\n * Skip server-level named middlewares\n *\n * Useful for public endpoints that should bypass auth or rate limiting\n *\n * @param middlewareNames - Array of middleware names to skip, or '*' to skip all\n *\n * @example\n * ```ts\n * // Skip specific middlewares\n * route.get('/health')\n * .skip(['auth', 'rateLimit'])\n * .handler(async (c) => c.json({ status: 'ok' }));\n *\n * // Skip only auth (still apply rate limiting)\n * route.get('/public-data')\n * .skip(['auth'])\n * .handler(async (c) => { ... });\n *\n * // Skip all middlewares\n * route.get('/public-health')\n * .skip('*')\n * .handler(async (c) => c.json({ status: 'ok' }));\n * ```\n */\n skip(middlewareNames: string[] | '*'): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ skipMiddlewares: middlewareNames });\n }\n\n /**\n * Define handler function\n *\n * Response type is automatically inferred from the return value.\n * Use helper methods like `c.created()`, `c.paginated()` for proper type inference.\n *\n * @example\n * ```ts\n * // Direct return - type inferred from data\n * route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await getUser(params.id); // Type: User\n * })\n *\n * // Using c.created() - returns data with 201 status, type preserved\n * route.post('/users')\n * .input({ body: Type.Object({ name: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await createUser(body)); // Type: User\n * })\n *\n * // Using c.paginated() - returns PaginatedResult<T>\n * route.get('/users')\n * .handler(async (c) => {\n * const users = await getUsers();\n * return c.paginated(users, 1, 20, 100); // Type: PaginatedResult<User>\n * })\n *\n * // Using c.noContent() - returns void\n * route.delete('/users/:id')\n * .handler(async (c) => {\n * await deleteUser(params.id);\n * return c.noContent(); // Type: void\n * })\n *\n * // Using c.json() - returns Response (type inference lost)\n * // Use only when you need custom status codes not covered by helpers\n * route.get('/custom')\n * .handler(async (c) => {\n * return c.json({ data }, 418); // Type: Response\n * })\n * ```\n */\n handler<THandlerResponse>(\n fn: RouteHandlerFn<TInput, TInterceptor, THandlerResponse>\n ): RouteDef<TInput, TInterceptor, THandlerResponse>\n {\n return {\n method: this._method,\n path: this._path,\n input: this._input,\n interceptor: this._interceptor,\n middlewares: this._middlewares,\n skipMiddlewares: this._skipMiddlewares,\n handler: fn,\n _input: {} as TInput,\n _interceptor: {} as TInterceptor,\n _response: {} as THandlerResponse,\n };\n }\n}\n\n/**\n * Create a route definition with HTTP method shortcuts\n */\nfunction createMethodRoute(method: HttpMethod): (path: string) => RouteBuilder\n{\n return (path: string) =>\n {\n const builder = new RouteBuilder();\n builder._method = method;\n builder._path = path;\n return builder;\n };\n}\n\n/**\n * Route builder entry point\n *\n * @example\n * ```ts\n * // GET request\n * export const getUser = route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await db.user.findUnique({ where: { id: params.id } });\n * });\n *\n * // POST request\n * export const createUser = route.post('/users')\n * .input({ body: Type.Object({ name: Type.String(), email: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await db.user.create({ data: body }));\n * });\n * ```\n */\nexport const route = {\n get: createMethodRoute('GET'),\n post: createMethodRoute('POST'),\n put: createMethodRoute('PUT'),\n patch: createMethodRoute('PATCH'),\n delete: createMethodRoute('DELETE'),\n};","/**\n * Router Definition\n *\n * Provides router composition and middleware management\n */\n\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteDef } from './route-builder';\n\n/**\n * Router definition - holds all routes\n */\nexport interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>> {\n routes: TRoutes;\n _routes: TRoutes;\n _packageRouters: Router<any>[];\n _globalMiddlewares: NamedMiddleware<string>[];\n\n /**\n * Register package routers (type-hidden)\n *\n * Package routes are:\n * - Recognized by RPC proxy and backend\n * - NOT exposed in client types (use package's own API like authApi, cmsApi)\n *\n * @example\n * ```ts\n * import { authRouter } from '@spfn/auth/server';\n * import { cmsAppRouter } from '@spfn/cms/server';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // Client usage:\n * // api.getRoot.call({}) - app routes\n * // authApi.login.call({}) - package API\n * ```\n */\n packages(routers: Router<any>[]): Router<TRoutes>;\n\n /**\n * Register global middlewares\n *\n * Applied to all routes unless explicitly skipped via .skip()\n *\n * @example\n * ```ts\n * import { authMiddleware, loggingMiddleware } from './middlewares';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n * ```\n */\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;\n}\n\n/**\n * Create a Router instance with chainable methods\n */\nfunction createRouterInstance<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes,\n packageRouters: Router<any>[] = [],\n globalMiddlewares: NamedMiddleware<string>[] = []\n): Router<TRoutes>\n{\n return {\n routes,\n _routes: routes,\n _packageRouters: packageRouters,\n _globalMiddlewares: globalMiddlewares,\n\n packages(routers: Router<any>[]): Router<TRoutes>\n {\n const newPackageRouters = [...this._packageRouters, ...routers];\n\n // Also include nested package routers if any\n for (const pkgRouter of routers)\n {\n if (pkgRouter._packageRouters?.length > 0)\n {\n newPackageRouters.push(...pkgRouter._packageRouters);\n }\n }\n\n return createRouterInstance(this.routes, newPackageRouters, this._globalMiddlewares);\n },\n\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>\n {\n return createRouterInstance(this.routes, this._packageRouters, [...this._globalMiddlewares, ...middlewares]);\n },\n };\n}\n\n/**\n * Define a router with multiple routes (tRPC-style)\n *\n * Supports chainable API for packages and middlewares:\n *\n * @example\n * ```ts\n * // Basic usage\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * listExamples,\n * });\n *\n * // With package routers (type-hidden)\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // With global middlewares\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n *\n * export type AppRouter = typeof appRouter;\n * ```\n *\n * Package routes:\n * - Recognized by RPC proxy and backend for routing\n * - NOT included in AppRouter type (use authApi, cmsApi instead)\n * - Prevents confusion between app API and package APIs\n */\nexport function defineRouter<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes\n): Router<TRoutes>\n{\n return createRouterInstance(routes);\n}","/**\n * Route Input Validation\n *\n * Provides unified validation logic for route input fields\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport type { Context } from 'hono';\nimport { ValidationError } from '@spfn/core/errors';\n\n/**\n * Validation error field info\n */\ninterface ValidationFieldError\n{\n path: string;\n message: string;\n value: unknown;\n}\n\n/**\n * Validate a value against a TypeBox schema\n *\n * @param schema - TypeBox schema to validate against\n * @param rawValue - Raw value to validate\n * @param fieldName - Field name for error messages\n * @returns Validated and converted value\n * @throws ValidationError if validation fails\n */\nexport function validateField<T extends Record<string, unknown>>(\n schema: TSchema | undefined,\n rawValue: Record<string, unknown>,\n fieldName: string\n): T\n{\n if (!schema)\n {\n return {} as T;\n }\n\n const converted = Value.Convert(schema, rawValue);\n const errors = [...Value.Errors(schema, converted)];\n\n if (errors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: errors.map((e): ValidationFieldError => ({\n path: e.path,\n message: e.message,\n value: e.value,\n })),\n });\n }\n\n return converted as T;\n}\n\n/**\n * Extract query parameters from request URL\n *\n * Handles array values (multiple params with same key)\n */\nexport function extractQueryParams(c: Context): Record<string, string | string[]>\n{\n const url = new URL(c.req.url);\n const queryObj: Record<string, string | string[]> = {};\n\n url.searchParams.forEach((v, k) =>\n {\n const existing = queryObj[k];\n if (existing)\n {\n queryObj[k] = Array.isArray(existing) ? [...existing, v] : [existing, v];\n }\n else\n {\n queryObj[k] = v;\n }\n });\n\n return queryObj;\n}\n\n/**\n * Extract headers from request (lowercase keys)\n */\nexport function extractHeaders(c: Context): Record<string, string>\n{\n const rawHeaders: Record<string, string> = {};\n\n c.req.raw.headers.forEach((value, key) =>\n {\n rawHeaders[key.toLowerCase()] = value;\n });\n\n return rawHeaders;\n}\n\n/**\n * Extract and parse cookies from request\n */\nexport function extractCookies(c: Context): Record<string, string>\n{\n const cookieHeader = c.req.header('cookie');\n const rawCookies: Record<string, string> = {};\n\n if (cookieHeader)\n {\n cookieHeader.split(';').forEach(cookie =>\n {\n const [key, value] = cookie.trim().split('=');\n if (key && value)\n {\n rawCookies[key] = decodeURIComponent(value);\n }\n });\n }\n\n return rawCookies;\n}\n\n/**\n * Parse JSON body from request\n *\n * @throws ValidationError if JSON parsing fails\n */\nexport async function parseJsonBody(c: Context): Promise<Record<string, unknown>>\n{\n try\n {\n return await c.req.json();\n }\n catch (error)\n {\n throw new ValidationError({\n message: 'Invalid JSON body',\n fields: [{\n path: '/',\n message: 'Failed to parse JSON',\n value: error instanceof Error ? error.message : 'Unknown error',\n }],\n });\n }\n}","/**\n * Route Registration for define-route style routing\n *\n * Registers routes defined with route.get()...handler() to Hono app\n */\n\nimport type { Context, Hono, MiddlewareHandler } from 'hono';\nimport type { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status';\nimport { logger } from '@spfn/core/logger';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { RouteDef } from './route-builder';\nimport type { Router } from './router';\nimport type { HttpMethod } from './types';\nimport {\n validateField,\n extractQueryParams,\n extractHeaders,\n extractCookies,\n parseJsonBody,\n} from './validation';\n\n/**\n * Registered route information for logging\n */\nexport interface RegisteredRoute\n{\n method: HttpMethod;\n path: string;\n name: string;\n}\n\n/**\n * Type guard to check if value is a Router\n */\nfunction isRouter(value: unknown): value is Router<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'routes' in value &&\n '_routes' in value;\n}\n\n/**\n * Type guard to check if value is a RouteDef\n */\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'handler' in value;\n}\n\n/**\n * Type guard to check if value is a NamedMiddleware\n */\nfunction isNamedMiddleware(value: unknown): value is NamedMiddleware<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n '_name' in value;\n}\n\n/**\n * Register routes from defineRouter() to Hono app\n *\n * @param app - Hono app instance\n * @param router - Router definition\n * @param namedMiddlewares - Optional server-level named middlewares\n *\n * @param collectedRoutes\n * @example\n * ```ts\n * const appRouter = defineRouter({\n * getUser: route.get('/users/:id')...\n * createUser: route.post('/users')...\n * });\n *\n * const app = new Hono();\n * const namedMiddlewares = [\n * { name: 'auth', handler: AuthMiddleware() },\n * { name: 'rateLimit', handler: RateLimitMiddleware() },\n * ];\n * registerRoutes(app, appRouter, namedMiddlewares);\n * ```\n */\nexport function registerRoutes<TRoutes extends Record<string, RouteDef<any> | Router<any>>>(\n app: Hono,\n router: Router<TRoutes>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>,\n collectedRoutes?: RegisteredRoute[]\n): RegisteredRoute[]\n{\n // Use provided array or create new one (top-level call)\n const routes = collectedRoutes ?? [];\n\n // Merge router's global middlewares with provided named middlewares\n const allNamedMiddlewares = [\n ...(namedMiddlewares ?? []),\n ...router._globalMiddlewares.map(mw => ({ name: mw.name, handler: mw.handler })),\n ];\n\n // 1. Register routes from router.routes\n for (const [name, routeOrRouter] of Object.entries(router.routes))\n {\n if (isRouter(routeOrRouter))\n {\n // Nested router - recursively register\n registerRoutes(app, routeOrRouter, allNamedMiddlewares, routes);\n }\n else if (isRouteDef(routeOrRouter))\n {\n // Single route - register\n const registered = registerRoute(app, name, routeOrRouter, allNamedMiddlewares);\n if (registered)\n {\n routes.push(registered);\n }\n }\n else\n {\n logger.warn(`Unknown route type for \"${name}\" - skipping`, {\n type: typeof routeOrRouter,\n });\n }\n }\n\n // 2. Register routes from package routers (_packageRouters)\n if (router._packageRouters && router._packageRouters.length > 0)\n {\n for (const pkgRouter of router._packageRouters)\n {\n registerRoutes(app, pkgRouter, allNamedMiddlewares, routes);\n }\n }\n\n return routes;\n}\n\n/**\n * Register a single route\n */\nfunction registerRoute(\n app: Hono,\n name: string,\n routeDef: RouteDef<any>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>\n): RegisteredRoute | null\n{\n const { method, path, input, middlewares = [], skipMiddlewares, handler } = routeDef;\n\n if (!method || !path)\n {\n logger.warn(`Route \"${name}\" is missing method or path - skipping`, {\n method,\n path,\n });\n\n return null;\n }\n\n // Create wrapped handler with validation\n const wrappedHandler = async (c: Context) =>\n {\n // Create RouteBuilderContext with validation\n const { context, responseMeta } = await createRouteBuilderContext(c, input || {});\n\n // Call user handler\n const result = await handler(context);\n\n // If handler returns Response, use it directly (e.g., c.json(), c.redirect())\n if (result instanceof Response)\n {\n return result;\n }\n\n // Handle empty responses (noContent, notModified, accepted without data)\n if (responseMeta.isEmpty)\n {\n return c.body(null, responseMeta.status);\n }\n\n // Return data as JSON with status and headers from helper methods\n const hasCustomHeaders = Object.keys(responseMeta.headers).length > 0;\n\n if (hasCustomHeaders)\n {\n return c.json(result, responseMeta.status, responseMeta.headers);\n }\n\n return c.json(result, responseMeta.status);\n };\n\n // Collect all middlewares: server-level (filtered) + route-level\n const allMiddlewares: MiddlewareHandler[] = [];\n\n // Track registered middlewares for deduplication\n const registeredNames = new Set<string>();\n const registeredHandlers = new Set<MiddlewareHandler>();\n\n // Check if skipping all middlewares\n const skipAll = skipMiddlewares === '*';\n\n // Add server-level named middlewares (skip those in skipMiddlewares or if '*')\n if (namedMiddlewares && namedMiddlewares.length > 0)\n {\n if (skipAll)\n {\n logger.debug(`⏭️ Skipping all middlewares (*) for route: ${method} ${path}`, { name });\n }\n else\n {\n const skipSet = new Set(Array.isArray(skipMiddlewares) ? skipMiddlewares : []);\n for (const middleware of namedMiddlewares)\n {\n if (!skipSet.has(middleware.name))\n {\n allMiddlewares.push(middleware.handler);\n registeredNames.add(middleware.name);\n registeredHandlers.add(middleware.handler);\n }\n else\n {\n logger.debug(`⏭️ Skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });\n }\n }\n }\n }\n\n // Add route-level middlewares (with deduplication by name or handler reference)\n for (const mw of middlewares)\n {\n if (isNamedMiddleware(mw))\n {\n // Named middleware: deduplicate by name\n if (registeredNames.has(mw.name))\n {\n logger.debug(`🔄 Skipping duplicate middleware '${mw.name}' for route: ${method} ${path}`, { name });\n continue;\n }\n registeredNames.add(mw.name);\n allMiddlewares.push(mw.handler);\n }\n else\n {\n // Regular middleware: deduplicate by handler reference\n if (registeredHandlers.has(mw))\n {\n logger.debug(`🔄 Skipping duplicate middleware handler for route: ${method} ${path}`, { name });\n continue;\n }\n registeredHandlers.add(mw);\n allMiddlewares.push(mw);\n }\n }\n\n // Register to Hono with correct HTTP method\n const methodLower = method.toLowerCase() as Lowercase<HttpMethod>;\n\n if (allMiddlewares.length > 0)\n {\n // Register with middlewares\n app[methodLower](path, ...allMiddlewares, wrappedHandler);\n }\n else\n {\n // Register without middlewares\n app[methodLower](path, wrappedHandler);\n }\n\n logger.debug(`Registered route: ${method} ${path}`, { name });\n\n return { method, path, name };\n}\n\n/**\n * Response metadata set by helper methods\n */\ninterface ResponseMeta\n{\n status: ContentfulStatusCode;\n headers: Record<string, string>;\n isEmpty: boolean;\n}\n\n/**\n * Create RouteBuilderContext from Hono Context\n *\n * Validates params, query, body, headers, cookies and returns structured input.\n * Helper methods (created, accepted, etc.) return data directly for type inference,\n * while storing response metadata internally for later use.\n */\nasync function createRouteBuilderContext<TInput extends RouteInput>(\n c: Context,\n input: TInput\n): Promise<{ context: RouteBuilderContext<TInput>; responseMeta: ResponseMeta }>\n{\n // Validate and extract all input fields\n const params = validateField(input.params, c.req.param(), 'path parameters');\n const query = validateField(input.query, extractQueryParams(c), 'query parameters');\n const headers = validateField(input.headers, extractHeaders(c), 'headers');\n const cookies = validateField(input.cookies, extractCookies(c), 'cookies');\n\n // Body requires async parsing\n let body: Record<string, unknown> = {};\n if (input.body)\n {\n const rawBody = await parseJsonBody(c);\n body = validateField(input.body, rawBody, 'request body');\n }\n\n // Cache for data() - avoid creating new object on each call\n let cachedData: any = null;\n\n // Response metadata - set by helper methods, used when building final Response\n const responseMeta: ResponseMeta = {\n status: 200 as ContentfulStatusCode,\n headers: {},\n isEmpty: false,\n };\n\n // Create context with structured data()\n const context: RouteBuilderContext<TInput> = {\n data: async () =>\n {\n if (!cachedData)\n {\n cachedData = { params, query, body, headers, cookies };\n }\n return cachedData;\n },\n\n json: (data, status, resHeaders) =>\n {\n return c.json(data, status, resHeaders);\n },\n\n created: <T>(data: T, location?: string): T =>\n {\n responseMeta.status = 201 as ContentfulStatusCode;\n if (location)\n {\n responseMeta.headers['Location'] = location;\n }\n return data;\n },\n\n accepted: <T>(data?: T): any =>\n {\n responseMeta.status = 202 as ContentfulStatusCode;\n if (data === undefined)\n {\n responseMeta.isEmpty = true;\n return undefined;\n }\n return data;\n },\n\n noContent: (): void =>\n {\n responseMeta.status = 204 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n notModified: (): void =>\n {\n responseMeta.status = 304 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n paginated: <T>(data: T[], page: number, limit: number, total: number) =>\n {\n return {\n items: data,\n pagination: {\n page,\n limit,\n total,\n totalPages: Math.ceil(total / limit),\n },\n };\n },\n\n redirect: (url, status) =>\n {\n return c.redirect(url, status as RedirectStatusCode);\n },\n\n raw: c,\n };\n\n return { context, responseMeta };\n}","/**\n * Middleware Definition Helper\n *\n * Provides type-safe middleware definition with name inference\n */\n\nimport type { MiddlewareHandler } from 'hono';\n\n/**\n * Named middleware with type inference\n *\n * @example\n * ```ts\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * // auth logic\n * c.set('user', { id: 1 });\n * await next();\n * });\n *\n * export const rateLimitMiddleware = defineMiddleware('rateLimit', async (c, next) => {\n * // rate limit logic\n * await next();\n * });\n * ```\n */\nexport type NamedMiddleware<TName extends string = string> = {\n name: TName;\n handler: MiddlewareHandler;\n _name: TName; // Type inference helper\n};\n\n/**\n * Named middleware factory with type inference\n *\n * Factory function that creates middleware instances with parameters\n *\n * @example\n * ```ts\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * // permission check logic\n * await next();\n * }\n * );\n * ```\n */\nexport type NamedMiddlewareFactory<TName extends string = string, TArgs extends any[] = any[]> = {\n name: TName;\n _name: TName; // Type inference helper\n} & ((...args: TArgs) => MiddlewareHandler);\n\n/**\n * Define a named middleware\n *\n * Creates a middleware with a unique name that can be referenced\n * in route-level skip() calls with full type safety.\n *\n * Supports two patterns:\n * 1. Regular middleware: Direct middleware handler\n * 2. Factory middleware: Function that creates middleware with parameters\n *\n * @param name - Unique middleware name\n * @param handler - Middleware handler function\n * @returns Named middleware with type inference\n *\n * @example\n * ```ts\n * // Regular middleware\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * const token = c.req.header('authorization');\n * if (!token) {\n * return c.json({ error: 'Unauthorized' }, 401);\n * }\n * c.set('user', await verifyToken(token));\n * await next();\n * });\n *\n * // Factory middleware\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * const user = c.get('user');\n * if (!hasPermissions(user, permissions)) {\n * return c.json({ error: 'Forbidden' }, 403);\n * }\n * await next();\n * }\n * );\n *\n * // server.config.ts\n * export default defineServerConfig()\n * .middlewares([authMiddleware])\n * .routes(appRouter)\n * .build();\n *\n * // routes.ts - skip by name\n * export const publicRoute = route.get('/health')\n * .skip(['auth']) // ✅ Type-safe! Autocomplete!\n * .handler(async (c) => c.success({ status: 'ok' }));\n *\n * // Use factory middleware inline\n * export const protectedRoute = route.get('/admin')\n * .use([requirePermissions('admin:write')])\n * .handler(async (c) => { ... });\n * ```\n */\nexport function defineMiddleware<TName extends string>(\n name: TName,\n handler: MiddlewareHandler\n): NamedMiddleware<TName>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler\n): NamedMiddlewareFactory<TName, TArgs>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[] = []>(\n name: TName,\n handlerOrFactory: MiddlewareHandler | ((...args: TArgs) => MiddlewareHandler)\n): NamedMiddleware<TName> | NamedMiddlewareFactory<TName, TArgs>\n{\n // Distinguish between regular middleware and factory by parameter count\n // MiddlewareHandler always has exactly 2 parameters: (c, next)\n // Factory has any other number of parameters\n if (typeof handlerOrFactory === 'function')\n {\n const paramCount = handlerOrFactory.length;\n\n // Regular middleware handler (c, next) => ...\n if (paramCount === 2)\n {\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n };\n }\n // Factory (...args) => (c, next) => ...\n else\n {\n // Create a new wrapper function to avoid \"Cannot assign to read only property 'name'\" error\n const factory = handlerOrFactory as (...args: TArgs) => MiddlewareHandler;\n const wrapper = (...args: TArgs) => factory(...args);\n\n // Use Object.defineProperty to set name property (which is read-only by default)\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n }\n }\n\n // Fallback: treat as regular middleware\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n };\n}\n\n/**\n * Define a middleware factory explicitly\n *\n * Use this when your factory function has exactly 2 parameters,\n * which would be incorrectly detected as a regular middleware handler.\n *\n * @param name - Unique middleware name\n * @param factory - Factory function that returns a middleware handler\n * @returns Named middleware factory with type inference\n *\n * @example\n * ```ts\n * // Factory with 2 params (would be misdetected by defineMiddleware)\n * export const rateLimiter = defineMiddlewareFactory('rateLimit',\n * (limit: number, window: number) => async (c, next) => {\n * // rate limit logic using limit and window\n * await next();\n * }\n * );\n *\n * // Usage\n * route.get('/api')\n * .use([rateLimiter(100, 60000)]) // 100 requests per minute\n * .handler(...)\n * ```\n */\nexport function defineMiddlewareFactory<TName extends string, TArgs extends unknown[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler\n): NamedMiddlewareFactory<TName, TArgs>\n{\n const wrapper = (...args: TArgs) => factory(...args);\n\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n}\n\n/**\n * Extract middleware names from an array of named middlewares\n *\n * @example\n * ```ts\n * type Middlewares = [\n * NamedMiddleware<'auth'>,\n * NamedMiddleware<'rateLimit'>\n * ];\n * type Names = ExtractMiddlewareNames<Middlewares>; // 'auth' | 'rateLimit'\n * ```\n */\nexport type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> =\n T[number]['_name'];","/**\n * Route Helper Functions\n *\n * Type guards and TypeBox utilities\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { Type } from '@sinclair/typebox';\nimport type { HttpMethod } from './types';\n\n/**\n * Type guard for HttpMethod\n */\nexport function isHttpMethod(value: unknown): value is HttpMethod\n{\n return (\n typeof value === 'string' &&\n ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(value)\n );\n}\n\n/**\n * Nullable - Creates a union of T | null\n *\n * @example\n * ```typescript\n * // string | null\n * firstName: Nullable(Type.String())\n * ```\n */\nexport const Nullable = <T extends TSchema>(schema: T) =>\n Type.Union([schema, Type.Null()]);\n\n/**\n * OptionalNullable - Creates a union of T | null | undefined\n *\n * @example\n * ```typescript\n * // string | null | undefined\n * lastName: OptionalNullable(Type.String())\n * ```\n */\nexport const OptionalNullable = <T extends TSchema>(schema: T) =>\n Type.Optional(Type.Union([schema, Type.Null()]));"]}
1
+ {"version":3,"sources":["../../src/route/route-builder.ts","../../src/route/router.ts","../../src/route/validation.ts","../../src/route/register-routes.ts","../../src/route/define-middleware.ts","../../src/route/helpers.ts"],"names":[],"mappings":";;;;;;AAgDO,IAAM,YAAA,GAAN,MAAM,aAAA,CAKb;AAAA,EACW,OAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,gBAAA;AAAA;AAAA;AAAA;AAAA,EAKC,MAIJ,SAAA,EAOJ;AACI,IAAA,MAAM,OAAA,GAAU,IAAI,aAAA,EAAoD;AACxE,IAAA,OAAA,CAAQ,UAAU,IAAA,CAAK,OAAA;AACvB,IAAA,OAAA,CAAQ,QAAQ,IAAA,CAAK,KAAA;AACrB,IAAA,OAAA,CAAQ,MAAA,GAAU,SAAA,EAAW,KAAA,IAAS,IAAA,CAAK,MAAA;AAC3C,IAAA,OAAA,CAAQ,YAAA,GAAgB,SAAA,EAAW,WAAA,IAAe,IAAA,CAAK,YAAA;AACvD,IAAA,OAAA,CAAQ,YAAA,GAAe,SAAA,EAAW,WAAA,IAAe,IAAA,CAAK,YAAA;AACtD,IAAA,OAAA,CAAQ,gBAAA,GAAmB,SAAA,EAAW,eAAA,IAAmB,IAAA,CAAK,gBAAA;AAC9D,IAAA,OAAO,OAAA;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAoC,KAAA,EACpC;AACI,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,EAAE,KAAA,EAAO,CAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsCA,YACI,WAAA,EAEJ;AACI,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,EAAE,WAAA,EAAa,CAAA;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,WAAW,WAAA,EACX;AACI,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,EAAE,WAAA,EAAa,CAAA;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,IAAI,WAAA,EACJ;AACI,IAAA,OAAO,IAAA,CAAK,WAAW,WAAW,CAAA;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BA,KAAK,eAAA,EACL;AACI,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,EAAE,eAAA,EAAiB,iBAAiB,CAAA;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgDA,QACI,EAAA,EAEJ;AACI,IAAA,OAAO;AAAA,MACH,QAAQ,IAAA,CAAK,OAAA;AAAA,MACb,MAAM,IAAA,CAAK,KAAA;AAAA,MACX,OAAO,IAAA,CAAK,MAAA;AAAA,MACZ,aAAa,IAAA,CAAK,YAAA;AAAA,MAClB,aAAa,IAAA,CAAK,YAAA;AAAA,MAClB,iBAAiB,IAAA,CAAK,gBAAA;AAAA,MACtB,OAAA,EAAS,EAAA;AAAA,MACT,QAAQ,EAAC;AAAA,MACT,cAAc,EAAC;AAAA,MACf,WAAW;AAAC,KAChB;AAAA,EACJ;AACJ,CAAA;AAKA,SAAS,kBAAkB,MAAA,EAC3B;AACI,EAAA,OAAO,CAAC,IAAA,KACR;AACI,IAAA,MAAM,OAAA,GAAU,IAAI,YAAA,EAAa;AACjC,IAAA,OAAA,CAAQ,OAAA,GAAU,MAAA;AAClB,IAAA,OAAA,CAAQ,KAAA,GAAQ,IAAA;AAChB,IAAA,OAAO,OAAA;AAAA,EACX,CAAA;AACJ;AAwBO,IAAM,KAAA,GAAQ;AAAA,EACjB,GAAA,EAAK,kBAAkB,KAAK,CAAA;AAAA,EAC5B,IAAA,EAAM,kBAAkB,MAAM,CAAA;AAAA,EAC9B,GAAA,EAAK,kBAAkB,KAAK,CAAA;AAAA,EAC5B,KAAA,EAAO,kBAAkB,OAAO,CAAA;AAAA,EAChC,MAAA,EAAQ,kBAAkB,QAAQ;AACtC;;;AClRA,SAAS,qBACL,MAAA,EACA,cAAA,GAAgC,EAAC,EACjC,iBAAA,GAA+C,EAAC,EAEpD;AACI,EAAA,OAAO;AAAA,IACH,MAAA;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,eAAA,EAAiB,cAAA;AAAA,IACjB,kBAAA,EAAoB,iBAAA;AAAA,IAEpB,SAAS,OAAA,EACT;AACI,MAAA,MAAM,oBAAoB,CAAC,GAAG,IAAA,CAAK,eAAA,EAAiB,GAAG,OAAO,CAAA;AAG9D,MAAA,KAAA,MAAW,aAAa,OAAA,EACxB;AACI,QAAA,IAAI,SAAA,CAAU,eAAA,EAAiB,MAAA,GAAS,CAAA,EACxC;AACI,UAAA,iBAAA,CAAkB,IAAA,CAAK,GAAG,SAAA,CAAU,eAAe,CAAA;AAAA,QACvD;AAAA,MACJ;AAEA,MAAA,OAAO,oBAAA,CAAqB,IAAA,CAAK,MAAA,EAAQ,iBAAA,EAAmB,KAAK,kBAAkB,CAAA;AAAA,IACvF,CAAA;AAAA,IAEA,IAAI,WAAA,EACJ;AACI,MAAA,OAAO,oBAAA,CAAqB,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,eAAA,EAAiB,CAAC,GAAG,IAAA,CAAK,kBAAA,EAAoB,GAAG,WAAW,CAAC,CAAA;AAAA,IAC/G;AAAA,GACJ;AACJ;AAuCO,SAAS,aACZ,MAAA,EAEJ;AACI,EAAA,OAAO,qBAAqB,MAAM,CAAA;AACtC;AC/HA,cAAA,CAAe,GAAA;AAAA,EAAI,OAAA;AAAA,EAAS,CAAC,KAAA,KACzB,OAAO,UAAU,QAAA,IAAY,4BAAA,CAA6B,KAAK,KAAK;AACxE,CAAA;AAEA,cAAA,CAAe,GAAA;AAAA,EAAI,KAAA;AAAA,EAAO,CAAC,KAAA,KACvB,OAAO,UAAU,QAAA,IAAY,gBAAA,CAAiB,KAAK,KAAK;AAC5D,CAAA;AAEA,cAAA,CAAe,GAAA;AAAA,EAAI,MAAA;AAAA,EAAQ,CAAC,KAAA,KACxB,OAAO,UAAU,QAAA,IAAY,iEAAA,CAAkE,KAAK,KAAK;AAC7G,CAAA;AAEA,cAAA,CAAe,GAAA;AAAA,EAAI,MAAA;AAAA,EAAQ,CAAC,KAAA,KACxB,OAAO,UAAU,QAAA,IAAY,qBAAA,CAAsB,KAAK,KAAK;AACjE,CAAA;AAEA,cAAA,CAAe,GAAA;AAAA,EAAI,WAAA;AAAA,EAAa,CAAC,KAAA,KAC7B,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,KAAK,CAAC;AACzD,CAAA;AAqBO,SAAS,aAAA,CACZ,MAAA,EACA,QAAA,EACA,SAAA,EAEJ;AACI,EAAA,IAAI,CAAC,MAAA,EACL;AACI,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,QAAQ,CAAA;AAChD,EAAA,MAAM,SAAS,CAAC,GAAG,MAAM,MAAA,CAAO,MAAA,EAAQ,SAAS,CAAC,CAAA;AAElD,EAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EACpB;AACI,IAAA,MAAM,IAAI,eAAA,CAAgB;AAAA,MACtB,OAAA,EAAS,WAAW,SAAS,CAAA,CAAA;AAAA,MAC7B,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAA6B;AAAA,QAC7C,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,SAAS,CAAA,CAAE,OAAA;AAAA,QACX,OAAO,CAAA,CAAE;AAAA,OACb,CAAE;AAAA,KACL,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,SAAA;AACX;AAOO,SAAS,mBAAmB,CAAA,EACnC;AACI,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,CAAA,CAAE,IAAI,GAAG,CAAA;AAC7B,EAAA,MAAM,WAA8C,EAAC;AAErD,EAAA,GAAA,CAAI,YAAA,CAAa,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAC7B;AACI,IAAA,MAAM,QAAA,GAAW,SAAS,CAAC,CAAA;AAC3B,IAAA,IAAI,QAAA,EACJ;AACI,MAAA,QAAA,CAAS,CAAC,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,GAAI,CAAC,GAAG,QAAA,EAAU,CAAC,CAAA,GAAI,CAAC,UAAU,CAAC,CAAA;AAAA,IAC3E,CAAA,MAEA;AACI,MAAA,QAAA,CAAS,CAAC,CAAA,GAAI,CAAA;AAAA,IAClB;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,OAAO,QAAA;AACX;AAKO,SAAS,eAAe,CAAA,EAC/B;AACI,EAAA,MAAM,aAAqC,EAAC;AAE5C,EAAA,CAAA,CAAE,IAAI,GAAA,CAAI,OAAA,CAAQ,OAAA,CAAQ,CAAC,OAAO,GAAA,KAClC;AACI,IAAA,UAAA,CAAW,GAAA,CAAI,WAAA,EAAa,CAAA,GAAI,KAAA;AAAA,EACpC,CAAC,CAAA;AAED,EAAA,OAAO,UAAA;AACX;AAKO,SAAS,eAAe,CAAA,EAC/B;AACI,EAAA,MAAM,YAAA,GAAe,CAAA,CAAE,GAAA,CAAI,MAAA,CAAO,QAAQ,CAAA;AAC1C,EAAA,MAAM,aAAqC,EAAC;AAE5C,EAAA,IAAI,YAAA,EACJ;AACI,IAAA,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,CAAA,MAAA,KAChC;AACI,MAAA,MAAM,CAAC,KAAK,KAAK,CAAA,GAAI,OAAO,IAAA,EAAK,CAAE,MAAM,GAAG,CAAA;AAC5C,MAAA,IAAI,OAAO,KAAA,EACX;AACI,QAAA,UAAA,CAAW,GAAG,CAAA,GAAI,kBAAA,CAAmB,KAAK,CAAA;AAAA,MAC9C;AAAA,IACJ,CAAC,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,UAAA;AACX;AAOA,eAAsB,cAAc,CAAA,EACpC;AACI,EAAA,IACA;AACI,IAAA,OAAO,MAAM,CAAA,CAAE,GAAA,CAAI,IAAA,EAAK;AAAA,EAC5B,SACO,KAAA,EACP;AACI,IAAA,MAAM,IAAI,eAAA,CAAgB;AAAA,MACtB,OAAA,EAAS,mBAAA;AAAA,MACT,QAAQ,CAAC;AAAA,QACL,IAAA,EAAM,GAAA;AAAA,QACN,OAAA,EAAS,sBAAA;AAAA,QACT,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,OACnD;AAAA,KACJ,CAAA;AAAA,EACL;AACJ;;;ACtIA,SAAS,SAAS,KAAA,EAClB;AACI,EAAA,OAAO,UAAU,IAAA,IACb,OAAO,UAAU,QAAA,IACjB,QAAA,IAAY,SACZ,SAAA,IAAa,KAAA;AACrB;AAKA,SAAS,WAAW,KAAA,EACpB;AACI,EAAA,OAAO,KAAA,KAAU,IAAA,IACb,OAAO,KAAA,KAAU,YACjB,SAAA,IAAa,KAAA;AACrB;AAKA,SAAS,kBAAkB,KAAA,EAC3B;AACI,EAAA,OAAO,KAAA,KAAU,QACb,OAAO,KAAA,KAAU,YACjB,MAAA,IAAU,KAAA,IACV,SAAA,IAAa,KAAA,IACb,OAAA,IAAW,KAAA;AACnB;AAyBO,SAAS,cAAA,CACZ,GAAA,EACA,MAAA,EACA,gBAAA,EACA,eAAA,EAEJ;AAEI,EAAA,MAAM,MAAA,GAAS,mBAAmB,EAAC;AAGnC,EAAA,MAAM,mBAAA,GAAsB;AAAA,IACxB,GAAI,oBAAoB,EAAC;AAAA,IACzB,GAAG,MAAA,CAAO,kBAAA,CAAmB,GAAA,CAAI,CAAA,EAAA,MAAO,EAAE,IAAA,EAAM,EAAA,CAAG,IAAA,EAAM,OAAA,EAAS,EAAA,CAAG,OAAA,EAAQ,CAAE;AAAA,GACnF;AAGA,EAAA,KAAA,MAAW,CAAC,MAAM,aAAa,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,MAAM,CAAA,EAChE;AACI,IAAA,IAAI,QAAA,CAAS,aAAa,CAAA,EAC1B;AAEI,MAAA,cAAA,CAAe,GAAA,EAAK,aAAA,EAAe,mBAAA,EAAqB,MAAM,CAAA;AAAA,IAClE,CAAA,MAAA,IACS,UAAA,CAAW,aAAa,CAAA,EACjC;AAEI,MAAA,MAAM,UAAA,GAAa,aAAA,CAAc,GAAA,EAAK,IAAA,EAAM,eAAe,mBAAmB,CAAA;AAC9E,MAAA,IAAI,UAAA,EACJ;AACI,QAAA,MAAA,CAAO,KAAK,UAAU,CAAA;AAAA,MAC1B;AAAA,IACJ,CAAA,MAEA;AACI,MAAA,MAAA,CAAO,IAAA,CAAK,CAAA,wBAAA,EAA2B,IAAI,CAAA,YAAA,CAAA,EAAgB;AAAA,QACvD,MAAM,OAAO;AAAA,OAChB,CAAA;AAAA,IACL;AAAA,EACJ;AAGA,EAAA,IAAI,MAAA,CAAO,eAAA,IAAmB,MAAA,CAAO,eAAA,CAAgB,SAAS,CAAA,EAC9D;AACI,IAAA,KAAA,MAAW,SAAA,IAAa,OAAO,eAAA,EAC/B;AACI,MAAA,cAAA,CAAe,GAAA,EAAK,SAAA,EAAW,mBAAA,EAAqB,MAAM,CAAA;AAAA,IAC9D;AAAA,EACJ;AAEA,EAAA,OAAO,MAAA;AACX;AAKA,SAAS,aAAA,CACL,GAAA,EACA,IAAA,EACA,QAAA,EACA,gBAAA,EAEJ;AACI,EAAA,MAAM,EAAE,QAAQ,IAAA,EAAM,KAAA,EAAO,cAAc,EAAC,EAAG,eAAA,EAAiB,OAAA,EAAQ,GAAI,QAAA;AAE5E,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,IAAA,EAChB;AACI,IAAA,MAAA,CAAO,IAAA,CAAK,CAAA,OAAA,EAAU,IAAI,CAAA,sCAAA,CAAA,EAA0C;AAAA,MAChE,MAAA;AAAA,MACA;AAAA,KACH,CAAA;AAED,IAAA,OAAO,IAAA;AAAA,EACX;AAGA,EAAA,MAAM,cAAA,GAAiB,OAAO,CAAA,KAC9B;AAEI,IAAA,MAAM,EAAE,SAAS,YAAA,EAAa,GAAI,MAAM,yBAAA,CAA0B,CAAA,EAAG,KAAA,IAAS,EAAE,CAAA;AAGhF,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,OAAO,CAAA;AAGpC,IAAA,IAAI,kBAAkB,QAAA,EACtB;AACI,MAAA,OAAO,MAAA;AAAA,IACX;AAGA,IAAA,IAAI,aAAa,OAAA,EACjB;AACI,MAAA,OAAO,CAAA,CAAE,IAAA,CAAK,IAAA,EAAM,YAAA,CAAa,MAAM,CAAA;AAAA,IAC3C;AAGA,IAAA,MAAM,mBAAmB,MAAA,CAAO,IAAA,CAAK,YAAA,CAAa,OAAO,EAAE,MAAA,GAAS,CAAA;AAEpE,IAAA,IAAI,gBAAA,EACJ;AACI,MAAA,OAAO,EAAE,IAAA,CAAK,MAAA,EAAQ,YAAA,CAAa,MAAA,EAAQ,aAAa,OAAO,CAAA;AAAA,IACnE;AAEA,IAAA,OAAO,CAAA,CAAE,IAAA,CAAK,MAAA,EAAQ,YAAA,CAAa,MAAM,CAAA;AAAA,EAC7C,CAAA;AAGA,EAAA,MAAM,iBAAsC,EAAC;AAG7C,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAY;AACxC,EAAA,MAAM,kBAAA,uBAAyB,GAAA,EAAuB;AAGtD,EAAA,MAAM,UAAU,eAAA,KAAoB,GAAA;AAGpC,EAAA,IAAI,gBAAA,IAAoB,gBAAA,CAAiB,MAAA,GAAS,CAAA,EAClD;AACI,IAAA,IAAI,OAAA,EACJ;AACI,MAAA,MAAA,CAAO,KAAA,CAAM,yDAA+C,MAAM,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,EAAE,MAAM,CAAA;AAAA,IAC1F,CAAA,MAEA;AACI,MAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,KAAA,CAAM,QAAQ,eAAe,CAAA,GAAI,eAAA,GAAkB,EAAE,CAAA;AAC7E,MAAA,KAAA,MAAW,cAAc,gBAAA,EACzB;AACI,QAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,EAChC;AACI,UAAA,cAAA,CAAe,IAAA,CAAK,WAAW,OAAO,CAAA;AACtC,UAAA,eAAA,CAAgB,GAAA,CAAI,WAAW,IAAI,CAAA;AACnC,UAAA,kBAAA,CAAmB,GAAA,CAAI,WAAW,OAAO,CAAA;AAAA,QAC7C,CAAA,MAEA;AACI,UAAA,MAAA,CAAO,KAAA,CAAM,CAAA,mCAAA,EAA4B,UAAA,CAAW,IAAI,CAAA,aAAA,EAAgB,MAAM,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,EAAE,IAAA,EAAM,CAAA;AAAA,QACtG;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAGA,EAAA,KAAA,MAAW,MAAM,WAAA,EACjB;AACI,IAAA,IAAI,iBAAA,CAAkB,EAAE,CAAA,EACxB;AAEI,MAAA,IAAI,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,IAAI,CAAA,EAC/B;AACI,QAAA,MAAA,CAAO,KAAA,CAAM,CAAA,yCAAA,EAAqC,EAAA,CAAG,IAAI,CAAA,aAAA,EAAgB,MAAM,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,EAAE,IAAA,EAAM,CAAA;AACnG,QAAA;AAAA,MACJ;AACA,MAAA,eAAA,CAAgB,GAAA,CAAI,GAAG,IAAI,CAAA;AAC3B,MAAA,cAAA,CAAe,IAAA,CAAK,GAAG,OAAO,CAAA;AAAA,IAClC,CAAA,MAEA;AAEI,MAAA,IAAI,kBAAA,CAAmB,GAAA,CAAI,EAAE,CAAA,EAC7B;AACI,QAAA,MAAA,CAAO,KAAA,CAAM,8DAAuD,MAAM,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,EAAE,MAAM,CAAA;AAC9F,QAAA;AAAA,MACJ;AACA,MAAA,kBAAA,CAAmB,IAAI,EAAE,CAAA;AACzB,MAAA,cAAA,CAAe,KAAK,EAAE,CAAA;AAAA,IAC1B;AAAA,EACJ;AAGA,EAAA,MAAM,WAAA,GAAc,OAAO,WAAA,EAAY;AAEvC,EAAA,IAAI,cAAA,CAAe,SAAS,CAAA,EAC5B;AAEI,IAAA,GAAA,CAAI,WAAW,CAAA,CAAE,IAAA,EAAM,GAAG,gBAAgB,cAAc,CAAA;AAAA,EAC5D,CAAA,MAEA;AAEI,IAAA,GAAA,CAAI,WAAW,CAAA,CAAE,IAAA,EAAM,cAAc,CAAA;AAAA,EACzC;AAEA,EAAA,MAAA,CAAO,KAAA,CAAM,qBAAqB,MAAM,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,EAAE,MAAM,CAAA;AAE5D,EAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAK;AAChC;AAmBA,eAAe,yBAAA,CACX,GACA,KAAA,EAEJ;AAEI,EAAA,MAAM,MAAA,GAAS,cAAc,KAAA,CAAM,MAAA,EAAQ,EAAE,GAAA,CAAI,KAAA,IAAS,iBAAiB,CAAA;AAC3E,EAAA,MAAM,QAAQ,aAAA,CAAc,KAAA,CAAM,OAAO,kBAAA,CAAmB,CAAC,GAAG,kBAAkB,CAAA;AAClF,EAAA,MAAM,UAAU,aAAA,CAAc,KAAA,CAAM,SAAS,cAAA,CAAe,CAAC,GAAG,SAAS,CAAA;AACzE,EAAA,MAAM,UAAU,aAAA,CAAc,KAAA,CAAM,SAAS,cAAA,CAAe,CAAC,GAAG,SAAS,CAAA;AAGzE,EAAA,IAAI,OAAgC,EAAC;AACrC,EAAA,IAAI,MAAM,IAAA,EACV;AACI,IAAA,MAAM,OAAA,GAAU,MAAM,aAAA,CAAc,CAAC,CAAA;AACrC,IAAA,IAAA,GAAO,aAAA,CAAc,KAAA,CAAM,IAAA,EAAM,OAAA,EAAS,cAAc,CAAA;AAAA,EAC5D;AAGA,EAAA,IAAI,UAAA,GAAkB,IAAA;AAGtB,EAAA,MAAM,YAAA,GAA6B;AAAA,IAC/B,MAAA,EAAQ,GAAA;AAAA,IACR,SAAS,EAAC;AAAA,IACV,OAAA,EAAS;AAAA,GACb;AAGA,EAAA,MAAM,OAAA,GAAuC;AAAA,IACzC,MAAM,YACN;AACI,MAAA,IAAI,CAAC,UAAA,EACL;AACI,QAAA,UAAA,GAAa,EAAE,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,SAAS,OAAA,EAAQ;AAAA,MACzD;AACA,MAAA,OAAO,UAAA;AAAA,IACX,CAAA;AAAA,IAEA,IAAA,EAAM,CAAC,IAAA,EAAM,MAAA,EAAQ,UAAA,KACrB;AACI,MAAA,OAAO,CAAA,CAAE,IAAA,CAAK,IAAA,EAAM,MAAA,EAAQ,UAAU,CAAA;AAAA,IAC1C,CAAA;AAAA,IAEA,OAAA,EAAS,CAAI,IAAA,EAAS,QAAA,KACtB;AACI,MAAA,YAAA,CAAa,MAAA,GAAS,GAAA;AACtB,MAAA,IAAI,QAAA,EACJ;AACI,QAAA,YAAA,CAAa,OAAA,CAAQ,UAAU,CAAA,GAAI,QAAA;AAAA,MACvC;AACA,MAAA,OAAO,IAAA;AAAA,IACX,CAAA;AAAA,IAEA,QAAA,EAAU,CAAI,IAAA,KACd;AACI,MAAA,YAAA,CAAa,MAAA,GAAS,GAAA;AACtB,MAAA,IAAI,SAAS,MAAA,EACb;AACI,QAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,QAAA,OAAO,MAAA;AAAA,MACX;AACA,MAAA,OAAO,IAAA;AAAA,IACX,CAAA;AAAA,IAEA,WAAW,MACX;AACI,MAAA,YAAA,CAAa,MAAA,GAAS,GAAA;AACtB,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AAAA,IAC3B,CAAA;AAAA,IAEA,aAAa,MACb;AACI,MAAA,YAAA,CAAa,MAAA,GAAS,GAAA;AACtB,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AAAA,IAC3B,CAAA;AAAA,IAEA,SAAA,EAAW,CAAI,IAAA,EAAW,IAAA,EAAc,OAAe,KAAA,KACvD;AACI,MAAA,OAAO;AAAA,QACH,KAAA,EAAO,IAAA;AAAA,QACP,UAAA,EAAY;AAAA,UACR,IAAA;AAAA,UACA,KAAA;AAAA,UACA,KAAA;AAAA,UACA,UAAA,EAAY,IAAA,CAAK,IAAA,CAAK,KAAA,GAAQ,KAAK;AAAA;AACvC,OACJ;AAAA,IACJ,CAAA;AAAA,IAEA,QAAA,EAAU,CAAC,GAAA,EAAK,MAAA,KAChB;AACI,MAAA,OAAO,CAAA,CAAE,QAAA,CAAS,GAAA,EAAK,MAA4B,CAAA;AAAA,IACvD,CAAA;AAAA,IAEA,GAAA,EAAK;AAAA,GACT;AAEA,EAAA,OAAO,EAAE,SAAS,YAAA,EAAa;AACnC;;;ACxRO,SAAS,gBAAA,CACZ,MACA,gBAAA,EAEJ;AAII,EAAA,IAAI,OAAO,qBAAqB,UAAA,EAChC;AACI,IAAA,MAAM,aAAa,gBAAA,CAAiB,MAAA;AAGpC,IAAA,IAAI,eAAe,CAAA,EACnB;AACI,MAAA,OAAO;AAAA,QACH,IAAA;AAAA,QACA,OAAA,EAAS,gBAAA;AAAA,QACT,KAAA,EAAO;AAAA,OACX;AAAA,IACJ,CAAA,MAGA;AAEI,MAAA,MAAM,OAAA,GAAU,gBAAA;AAChB,MAAA,MAAM,OAAA,GAAU,CAAA,GAAI,IAAA,KAAgB,OAAA,CAAQ,GAAG,IAAI,CAAA;AAGnD,MAAA,MAAA,CAAO,cAAA,CAAe,SAAS,MAAA,EAAQ;AAAA,QACnC,KAAA,EAAO,IAAA;AAAA,QACP,QAAA,EAAU,KAAA;AAAA,QACV,UAAA,EAAY,KAAA;AAAA,QACZ,YAAA,EAAc;AAAA,OACjB,CAAA;AAED,MAAA,MAAA,CAAO,cAAA,CAAe,SAAS,OAAA,EAAS;AAAA,QACpC,KAAA,EAAO,IAAA;AAAA,QACP,QAAA,EAAU,KAAA;AAAA,QACV,UAAA,EAAY,KAAA;AAAA,QACZ,YAAA,EAAc;AAAA,OACjB,CAAA;AAED,MAAA,OAAO,OAAA;AAAA,IACX;AAAA,EACJ;AAGA,EAAA,OAAO;AAAA,IACH,IAAA;AAAA,IACA,OAAA,EAAS,gBAAA;AAAA,IACT,KAAA,EAAO;AAAA,GACX;AACJ;AA4BO,SAAS,uBAAA,CACZ,MACA,OAAA,EAEJ;AACI,EAAA,MAAM,OAAA,GAAU,CAAA,GAAI,IAAA,KAAgB,OAAA,CAAQ,GAAG,IAAI,CAAA;AAEnD,EAAA,MAAA,CAAO,cAAA,CAAe,SAAS,MAAA,EAAQ;AAAA,IACnC,KAAA,EAAO,IAAA;AAAA,IACP,QAAA,EAAU,KAAA;AAAA,IACV,UAAA,EAAY,KAAA;AAAA,IACZ,YAAA,EAAc;AAAA,GACjB,CAAA;AAED,EAAA,MAAA,CAAO,cAAA,CAAe,SAAS,OAAA,EAAS;AAAA,IACpC,KAAA,EAAO,IAAA;AAAA,IACP,QAAA,EAAU,KAAA;AAAA,IACV,UAAA,EAAY,KAAA;AAAA,IACZ,YAAA,EAAc;AAAA,GACjB,CAAA;AAED,EAAA,OAAO,OAAA;AACX;AC7MO,SAAS,aAAa,KAAA,EAC7B;AACI,EAAA,OACI,OAAO,KAAA,KAAU,QAAA,IACjB,CAAC,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,QAAQ,CAAA,CAAE,QAAA,CAAS,KAAK,CAAA;AAEhE;AAWO,IAAM,QAAA,GAAW,CAAoB,MAAA,KACxC,IAAA,CAAK,KAAA,CAAM,CAAC,MAAA,EAAQ,IAAA,CAAK,IAAA,EAAM,CAAC;AAW7B,IAAM,gBAAA,GAAmB,CAAoB,MAAA,KAChD,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,CAAC,MAAA,EAAQ,IAAA,CAAK,IAAA,EAAM,CAAC,CAAC","file":"index.js","sourcesContent":["/**\n * Route Builder\n *\n * Provides tRPC-style chainable API for route definition\n */\n\nimport type { MiddlewareHandler } from 'hono';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { HttpMethod } from './types';\n\n/**\n * Route handler function\n */\nexport type RouteHandlerFn<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown\n> = (c: RouteBuilderContext<TInput, TInterceptor>) => Response | Promise<Response> | TResponse | Promise<TResponse>;\n\n/**\n * Route definition result\n *\n * Contains all information needed for type inference and registration\n */\nexport type RouteDef<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown\n> = {\n method?: HttpMethod;\n path?: string;\n input?: TInput;\n interceptor?: TInterceptor;\n middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares?: string[] | '*';\n handler: RouteHandlerFn<TInput, TInterceptor, TResponse>;\n\n // Type inference helpers\n _input: TInput;\n _interceptor: TInterceptor;\n _response: TResponse;\n};\n\n/**\n * Route builder with chainable API (tRPC-style)\n */\nexport class RouteBuilder<\n TInput extends RouteInput = {},\n TInterceptor extends RouteInput = {},\n TResponse = never\n>\n{\n public _method?: HttpMethod;\n public _path?: string;\n public _input?: TInput;\n public _interceptor?: TInterceptor;\n public _middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n public _skipMiddlewares?: string[] | '*';\n\n /**\n * Create a new RouteBuilder with copied properties and optional overrides\n */\n private clone<\n TNewInput extends RouteInput = TInput,\n TNewInterceptor extends RouteInput = TInterceptor\n >(\n overrides?: Partial<{\n input: TNewInput;\n interceptor: TNewInterceptor;\n middlewares: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares: string[] | '*';\n }>\n ): RouteBuilder<TNewInput, TNewInterceptor, TResponse>\n {\n const builder = new RouteBuilder<TNewInput, TNewInterceptor, TResponse>();\n builder._method = this._method;\n builder._path = this._path;\n builder._input = (overrides?.input ?? this._input) as TNewInput | undefined;\n builder._interceptor = (overrides?.interceptor ?? this._interceptor) as TNewInterceptor | undefined;\n builder._middlewares = overrides?.middlewares ?? this._middlewares;\n builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;\n return builder;\n }\n\n /**\n * Define input schemas\n *\n * @example\n * ```ts\n * route.get('/users/:id')\n * .input({\n * params: Type.Object({ id: Type.String() }),\n * query: Type.Object({ page: Type.Number() }),\n * headers: Type.Object({ authorization: Type.String() })\n * })\n * .handler(async (c) => {\n * const { params, query, headers } = await c.data();\n * // params = { id: string }\n * // query = { page: number }\n * // headers = { authorization: string }\n * })\n * ```\n */\n input<TNewInput extends RouteInput>(input: TNewInput): RouteBuilder<TNewInput, TInterceptor, TResponse>\n {\n return this.clone({ input });\n }\n\n /**\n * Define fields injected by interceptors\n *\n * These fields are:\n * - Available in the handler (merged with input)\n * - Excluded from client types (codegen uses only input)\n * - Not validated by route input schema (injected by middleware)\n *\n * Use this when middleware/interceptors add fields to the request\n * before it reaches the handler.\n *\n * @example\n * ```ts\n * // Auth interceptor injects crypto key fields\n * route.post('/_auth/login')\n * .input({\n * body: Type.Object({\n * email: Type.String(),\n * password: Type.String()\n * })\n * })\n * .interceptor({\n * body: Type.Object({\n * publicKey: Type.String(),\n * keyId: Type.String(),\n * fingerprint: Type.String()\n * })\n * })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * // body type: { email, password, publicKey, keyId, fingerprint }\n * // Client only sees: { email, password }\n * return loginService(body);\n * });\n * ```\n */\n interceptor<TNewInterceptor extends RouteInput>(\n interceptor: TNewInterceptor\n ): RouteBuilder<TInput, TNewInterceptor, TResponse>\n {\n return this.clone({ interceptor });\n }\n\n /**\n * Add middlewares to the route\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n middleware(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ middlewares });\n }\n\n /**\n * Add middlewares to the route (alias for `.middleware()`)\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.middleware(middlewares);\n }\n\n /**\n * Skip server-level named middlewares\n *\n * Useful for public endpoints that should bypass auth or rate limiting\n *\n * @param middlewareNames - Array of middleware names to skip, or '*' to skip all\n *\n * @example\n * ```ts\n * // Skip specific middlewares\n * route.get('/health')\n * .skip(['auth', 'rateLimit'])\n * .handler(async (c) => c.json({ status: 'ok' }));\n *\n * // Skip only auth (still apply rate limiting)\n * route.get('/public-data')\n * .skip(['auth'])\n * .handler(async (c) => { ... });\n *\n * // Skip all middlewares\n * route.get('/public-health')\n * .skip('*')\n * .handler(async (c) => c.json({ status: 'ok' }));\n * ```\n */\n skip(middlewareNames: string[] | '*'): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ skipMiddlewares: middlewareNames });\n }\n\n /**\n * Define handler function\n *\n * Response type is automatically inferred from the return value.\n * Use helper methods like `c.created()`, `c.paginated()` for proper type inference.\n *\n * @example\n * ```ts\n * // Direct return - type inferred from data\n * route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await getUser(params.id); // Type: User\n * })\n *\n * // Using c.created() - returns data with 201 status, type preserved\n * route.post('/users')\n * .input({ body: Type.Object({ name: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await createUser(body)); // Type: User\n * })\n *\n * // Using c.paginated() - returns PaginatedResult<T>\n * route.get('/users')\n * .handler(async (c) => {\n * const users = await getUsers();\n * return c.paginated(users, 1, 20, 100); // Type: PaginatedResult<User>\n * })\n *\n * // Using c.noContent() - returns void\n * route.delete('/users/:id')\n * .handler(async (c) => {\n * await deleteUser(params.id);\n * return c.noContent(); // Type: void\n * })\n *\n * // Using c.json() - returns Response (type inference lost)\n * // Use only when you need custom status codes not covered by helpers\n * route.get('/custom')\n * .handler(async (c) => {\n * return c.json({ data }, 418); // Type: Response\n * })\n * ```\n */\n handler<THandlerResponse>(\n fn: RouteHandlerFn<TInput, TInterceptor, THandlerResponse>\n ): RouteDef<TInput, TInterceptor, THandlerResponse>\n {\n return {\n method: this._method,\n path: this._path,\n input: this._input,\n interceptor: this._interceptor,\n middlewares: this._middlewares,\n skipMiddlewares: this._skipMiddlewares,\n handler: fn,\n _input: {} as TInput,\n _interceptor: {} as TInterceptor,\n _response: {} as THandlerResponse,\n };\n }\n}\n\n/**\n * Create a route definition with HTTP method shortcuts\n */\nfunction createMethodRoute(method: HttpMethod): (path: string) => RouteBuilder\n{\n return (path: string) =>\n {\n const builder = new RouteBuilder();\n builder._method = method;\n builder._path = path;\n return builder;\n };\n}\n\n/**\n * Route builder entry point\n *\n * @example\n * ```ts\n * // GET request\n * export const getUser = route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await db.user.findUnique({ where: { id: params.id } });\n * });\n *\n * // POST request\n * export const createUser = route.post('/users')\n * .input({ body: Type.Object({ name: Type.String(), email: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await db.user.create({ data: body }));\n * });\n * ```\n */\nexport const route = {\n get: createMethodRoute('GET'),\n post: createMethodRoute('POST'),\n put: createMethodRoute('PUT'),\n patch: createMethodRoute('PATCH'),\n delete: createMethodRoute('DELETE'),\n};","/**\n * Router Definition\n *\n * Provides router composition and middleware management\n */\n\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteDef } from './route-builder';\n\n/**\n * Router definition - holds all routes\n */\nexport interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>> {\n routes: TRoutes;\n _routes: TRoutes;\n _packageRouters: Router<any>[];\n _globalMiddlewares: NamedMiddleware<string>[];\n\n /**\n * Register package routers (type-hidden)\n *\n * Package routes are:\n * - Recognized by RPC proxy and backend\n * - NOT exposed in client types (use package's own API like authApi, cmsApi)\n *\n * @example\n * ```ts\n * import { authRouter } from '@spfn/auth/server';\n * import { cmsAppRouter } from '@spfn/cms/server';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // Client usage:\n * // api.getRoot.call({}) - app routes\n * // authApi.login.call({}) - package API\n * ```\n */\n packages(routers: Router<any>[]): Router<TRoutes>;\n\n /**\n * Register global middlewares\n *\n * Applied to all routes unless explicitly skipped via .skip()\n *\n * @example\n * ```ts\n * import { authMiddleware, loggingMiddleware } from './middlewares';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n * ```\n */\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;\n}\n\n/**\n * Create a Router instance with chainable methods\n */\nfunction createRouterInstance<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes,\n packageRouters: Router<any>[] = [],\n globalMiddlewares: NamedMiddleware<string>[] = []\n): Router<TRoutes>\n{\n return {\n routes,\n _routes: routes,\n _packageRouters: packageRouters,\n _globalMiddlewares: globalMiddlewares,\n\n packages(routers: Router<any>[]): Router<TRoutes>\n {\n const newPackageRouters = [...this._packageRouters, ...routers];\n\n // Also include nested package routers if any\n for (const pkgRouter of routers)\n {\n if (pkgRouter._packageRouters?.length > 0)\n {\n newPackageRouters.push(...pkgRouter._packageRouters);\n }\n }\n\n return createRouterInstance(this.routes, newPackageRouters, this._globalMiddlewares);\n },\n\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>\n {\n return createRouterInstance(this.routes, this._packageRouters, [...this._globalMiddlewares, ...middlewares]);\n },\n };\n}\n\n/**\n * Define a router with multiple routes (tRPC-style)\n *\n * Supports chainable API for packages and middlewares:\n *\n * @example\n * ```ts\n * // Basic usage\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * listExamples,\n * });\n *\n * // With package routers (type-hidden)\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // With global middlewares\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n *\n * export type AppRouter = typeof appRouter;\n * ```\n *\n * Package routes:\n * - Recognized by RPC proxy and backend for routing\n * - NOT included in AppRouter type (use authApi, cmsApi instead)\n * - Prevents confusion between app API and package APIs\n */\nexport function defineRouter<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes\n): Router<TRoutes>\n{\n return createRouterInstance(routes);\n}","/**\n * Route Input Validation\n *\n * Provides unified validation logic for route input fields\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { FormatRegistry } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport type { Context } from 'hono';\nimport { ValidationError } from '@spfn/core/errors';\n\n// ============================================\n// Format Registry\n// ============================================\n\nFormatRegistry.Set('email', (value) =>\n typeof value === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)\n);\n\nFormatRegistry.Set('uri', (value) =>\n typeof value === 'string' && /^https?:\\/\\/.+/.test(value)\n);\n\nFormatRegistry.Set('uuid', (value) =>\n typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)\n);\n\nFormatRegistry.Set('date', (value) =>\n typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)\n);\n\nFormatRegistry.Set('date-time', (value) =>\n typeof value === 'string' && !isNaN(Date.parse(value))\n);\n\n/**\n * Validation error field info\n */\ninterface ValidationFieldError\n{\n path: string;\n message: string;\n value: unknown;\n}\n\n/**\n * Validate a value against a TypeBox schema\n *\n * @param schema - TypeBox schema to validate against\n * @param rawValue - Raw value to validate\n * @param fieldName - Field name for error messages\n * @returns Validated and converted value\n * @throws ValidationError if validation fails\n */\nexport function validateField<T extends Record<string, unknown>>(\n schema: TSchema | undefined,\n rawValue: Record<string, unknown>,\n fieldName: string\n): T\n{\n if (!schema)\n {\n return {} as T;\n }\n\n const converted = Value.Convert(schema, rawValue);\n const errors = [...Value.Errors(schema, converted)];\n\n if (errors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: errors.map((e): ValidationFieldError => ({\n path: e.path,\n message: e.message,\n value: e.value,\n })),\n });\n }\n\n return converted as T;\n}\n\n/**\n * Extract query parameters from request URL\n *\n * Handles array values (multiple params with same key)\n */\nexport function extractQueryParams(c: Context): Record<string, string | string[]>\n{\n const url = new URL(c.req.url);\n const queryObj: Record<string, string | string[]> = {};\n\n url.searchParams.forEach((v, k) =>\n {\n const existing = queryObj[k];\n if (existing)\n {\n queryObj[k] = Array.isArray(existing) ? [...existing, v] : [existing, v];\n }\n else\n {\n queryObj[k] = v;\n }\n });\n\n return queryObj;\n}\n\n/**\n * Extract headers from request (lowercase keys)\n */\nexport function extractHeaders(c: Context): Record<string, string>\n{\n const rawHeaders: Record<string, string> = {};\n\n c.req.raw.headers.forEach((value, key) =>\n {\n rawHeaders[key.toLowerCase()] = value;\n });\n\n return rawHeaders;\n}\n\n/**\n * Extract and parse cookies from request\n */\nexport function extractCookies(c: Context): Record<string, string>\n{\n const cookieHeader = c.req.header('cookie');\n const rawCookies: Record<string, string> = {};\n\n if (cookieHeader)\n {\n cookieHeader.split(';').forEach(cookie =>\n {\n const [key, value] = cookie.trim().split('=');\n if (key && value)\n {\n rawCookies[key] = decodeURIComponent(value);\n }\n });\n }\n\n return rawCookies;\n}\n\n/**\n * Parse JSON body from request\n *\n * @throws ValidationError if JSON parsing fails\n */\nexport async function parseJsonBody(c: Context): Promise<Record<string, unknown>>\n{\n try\n {\n return await c.req.json();\n }\n catch (error)\n {\n throw new ValidationError({\n message: 'Invalid JSON body',\n fields: [{\n path: '/',\n message: 'Failed to parse JSON',\n value: error instanceof Error ? error.message : 'Unknown error',\n }],\n });\n }\n}","/**\n * Route Registration for define-route style routing\n *\n * Registers routes defined with route.get()...handler() to Hono app\n */\n\nimport type { Context, Hono, MiddlewareHandler } from 'hono';\nimport type { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status';\nimport { logger } from '@spfn/core/logger';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { RouteDef } from './route-builder';\nimport type { Router } from './router';\nimport type { HttpMethod } from './types';\nimport {\n validateField,\n extractQueryParams,\n extractHeaders,\n extractCookies,\n parseJsonBody,\n} from './validation';\n\n/**\n * Registered route information for logging\n */\nexport interface RegisteredRoute\n{\n method: HttpMethod;\n path: string;\n name: string;\n}\n\n/**\n * Type guard to check if value is a Router\n */\nfunction isRouter(value: unknown): value is Router<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'routes' in value &&\n '_routes' in value;\n}\n\n/**\n * Type guard to check if value is a RouteDef\n */\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'handler' in value;\n}\n\n/**\n * Type guard to check if value is a NamedMiddleware\n */\nfunction isNamedMiddleware(value: unknown): value is NamedMiddleware<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n '_name' in value;\n}\n\n/**\n * Register routes from defineRouter() to Hono app\n *\n * @param app - Hono app instance\n * @param router - Router definition\n * @param namedMiddlewares - Optional server-level named middlewares\n *\n * @param collectedRoutes\n * @example\n * ```ts\n * const appRouter = defineRouter({\n * getUser: route.get('/users/:id')...\n * createUser: route.post('/users')...\n * });\n *\n * const app = new Hono();\n * const namedMiddlewares = [\n * { name: 'auth', handler: AuthMiddleware() },\n * { name: 'rateLimit', handler: RateLimitMiddleware() },\n * ];\n * registerRoutes(app, appRouter, namedMiddlewares);\n * ```\n */\nexport function registerRoutes<TRoutes extends Record<string, RouteDef<any> | Router<any>>>(\n app: Hono,\n router: Router<TRoutes>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>,\n collectedRoutes?: RegisteredRoute[]\n): RegisteredRoute[]\n{\n // Use provided array or create new one (top-level call)\n const routes = collectedRoutes ?? [];\n\n // Merge router's global middlewares with provided named middlewares\n const allNamedMiddlewares = [\n ...(namedMiddlewares ?? []),\n ...router._globalMiddlewares.map(mw => ({ name: mw.name, handler: mw.handler })),\n ];\n\n // 1. Register routes from router.routes\n for (const [name, routeOrRouter] of Object.entries(router.routes))\n {\n if (isRouter(routeOrRouter))\n {\n // Nested router - recursively register\n registerRoutes(app, routeOrRouter, allNamedMiddlewares, routes);\n }\n else if (isRouteDef(routeOrRouter))\n {\n // Single route - register\n const registered = registerRoute(app, name, routeOrRouter, allNamedMiddlewares);\n if (registered)\n {\n routes.push(registered);\n }\n }\n else\n {\n logger.warn(`Unknown route type for \"${name}\" - skipping`, {\n type: typeof routeOrRouter,\n });\n }\n }\n\n // 2. Register routes from package routers (_packageRouters)\n if (router._packageRouters && router._packageRouters.length > 0)\n {\n for (const pkgRouter of router._packageRouters)\n {\n registerRoutes(app, pkgRouter, allNamedMiddlewares, routes);\n }\n }\n\n return routes;\n}\n\n/**\n * Register a single route\n */\nfunction registerRoute(\n app: Hono,\n name: string,\n routeDef: RouteDef<any>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>\n): RegisteredRoute | null\n{\n const { method, path, input, middlewares = [], skipMiddlewares, handler } = routeDef;\n\n if (!method || !path)\n {\n logger.warn(`Route \"${name}\" is missing method or path - skipping`, {\n method,\n path,\n });\n\n return null;\n }\n\n // Create wrapped handler with validation\n const wrappedHandler = async (c: Context) =>\n {\n // Create RouteBuilderContext with validation\n const { context, responseMeta } = await createRouteBuilderContext(c, input || {});\n\n // Call user handler\n const result = await handler(context);\n\n // If handler returns Response, use it directly (e.g., c.json(), c.redirect())\n if (result instanceof Response)\n {\n return result;\n }\n\n // Handle empty responses (noContent, notModified, accepted without data)\n if (responseMeta.isEmpty)\n {\n return c.body(null, responseMeta.status);\n }\n\n // Return data as JSON with status and headers from helper methods\n const hasCustomHeaders = Object.keys(responseMeta.headers).length > 0;\n\n if (hasCustomHeaders)\n {\n return c.json(result, responseMeta.status, responseMeta.headers);\n }\n\n return c.json(result, responseMeta.status);\n };\n\n // Collect all middlewares: server-level (filtered) + route-level\n const allMiddlewares: MiddlewareHandler[] = [];\n\n // Track registered middlewares for deduplication\n const registeredNames = new Set<string>();\n const registeredHandlers = new Set<MiddlewareHandler>();\n\n // Check if skipping all middlewares\n const skipAll = skipMiddlewares === '*';\n\n // Add server-level named middlewares (skip those in skipMiddlewares or if '*')\n if (namedMiddlewares && namedMiddlewares.length > 0)\n {\n if (skipAll)\n {\n logger.debug(`⏭️ Skipping all middlewares (*) for route: ${method} ${path}`, { name });\n }\n else\n {\n const skipSet = new Set(Array.isArray(skipMiddlewares) ? skipMiddlewares : []);\n for (const middleware of namedMiddlewares)\n {\n if (!skipSet.has(middleware.name))\n {\n allMiddlewares.push(middleware.handler);\n registeredNames.add(middleware.name);\n registeredHandlers.add(middleware.handler);\n }\n else\n {\n logger.debug(`⏭️ Skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });\n }\n }\n }\n }\n\n // Add route-level middlewares (with deduplication by name or handler reference)\n for (const mw of middlewares)\n {\n if (isNamedMiddleware(mw))\n {\n // Named middleware: deduplicate by name\n if (registeredNames.has(mw.name))\n {\n logger.debug(`🔄 Skipping duplicate middleware '${mw.name}' for route: ${method} ${path}`, { name });\n continue;\n }\n registeredNames.add(mw.name);\n allMiddlewares.push(mw.handler);\n }\n else\n {\n // Regular middleware: deduplicate by handler reference\n if (registeredHandlers.has(mw))\n {\n logger.debug(`🔄 Skipping duplicate middleware handler for route: ${method} ${path}`, { name });\n continue;\n }\n registeredHandlers.add(mw);\n allMiddlewares.push(mw);\n }\n }\n\n // Register to Hono with correct HTTP method\n const methodLower = method.toLowerCase() as Lowercase<HttpMethod>;\n\n if (allMiddlewares.length > 0)\n {\n // Register with middlewares\n app[methodLower](path, ...allMiddlewares, wrappedHandler);\n }\n else\n {\n // Register without middlewares\n app[methodLower](path, wrappedHandler);\n }\n\n logger.debug(`Registered route: ${method} ${path}`, { name });\n\n return { method, path, name };\n}\n\n/**\n * Response metadata set by helper methods\n */\ninterface ResponseMeta\n{\n status: ContentfulStatusCode;\n headers: Record<string, string>;\n isEmpty: boolean;\n}\n\n/**\n * Create RouteBuilderContext from Hono Context\n *\n * Validates params, query, body, headers, cookies and returns structured input.\n * Helper methods (created, accepted, etc.) return data directly for type inference,\n * while storing response metadata internally for later use.\n */\nasync function createRouteBuilderContext<TInput extends RouteInput>(\n c: Context,\n input: TInput\n): Promise<{ context: RouteBuilderContext<TInput>; responseMeta: ResponseMeta }>\n{\n // Validate and extract all input fields\n const params = validateField(input.params, c.req.param(), 'path parameters');\n const query = validateField(input.query, extractQueryParams(c), 'query parameters');\n const headers = validateField(input.headers, extractHeaders(c), 'headers');\n const cookies = validateField(input.cookies, extractCookies(c), 'cookies');\n\n // Body requires async parsing\n let body: Record<string, unknown> = {};\n if (input.body)\n {\n const rawBody = await parseJsonBody(c);\n body = validateField(input.body, rawBody, 'request body');\n }\n\n // Cache for data() - avoid creating new object on each call\n let cachedData: any = null;\n\n // Response metadata - set by helper methods, used when building final Response\n const responseMeta: ResponseMeta = {\n status: 200 as ContentfulStatusCode,\n headers: {},\n isEmpty: false,\n };\n\n // Create context with structured data()\n const context: RouteBuilderContext<TInput> = {\n data: async () =>\n {\n if (!cachedData)\n {\n cachedData = { params, query, body, headers, cookies };\n }\n return cachedData;\n },\n\n json: (data, status, resHeaders) =>\n {\n return c.json(data, status, resHeaders);\n },\n\n created: <T>(data: T, location?: string): T =>\n {\n responseMeta.status = 201 as ContentfulStatusCode;\n if (location)\n {\n responseMeta.headers['Location'] = location;\n }\n return data;\n },\n\n accepted: <T>(data?: T): any =>\n {\n responseMeta.status = 202 as ContentfulStatusCode;\n if (data === undefined)\n {\n responseMeta.isEmpty = true;\n return undefined;\n }\n return data;\n },\n\n noContent: (): void =>\n {\n responseMeta.status = 204 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n notModified: (): void =>\n {\n responseMeta.status = 304 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n paginated: <T>(data: T[], page: number, limit: number, total: number) =>\n {\n return {\n items: data,\n pagination: {\n page,\n limit,\n total,\n totalPages: Math.ceil(total / limit),\n },\n };\n },\n\n redirect: (url, status) =>\n {\n return c.redirect(url, status as RedirectStatusCode);\n },\n\n raw: c,\n };\n\n return { context, responseMeta };\n}","/**\n * Middleware Definition Helper\n *\n * Provides type-safe middleware definition with name inference\n */\n\nimport type { MiddlewareHandler } from 'hono';\n\n/**\n * Named middleware with type inference\n *\n * @example\n * ```ts\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * // auth logic\n * c.set('user', { id: 1 });\n * await next();\n * });\n *\n * export const rateLimitMiddleware = defineMiddleware('rateLimit', async (c, next) => {\n * // rate limit logic\n * await next();\n * });\n * ```\n */\nexport type NamedMiddleware<TName extends string = string> = {\n name: TName;\n handler: MiddlewareHandler;\n _name: TName; // Type inference helper\n};\n\n/**\n * Named middleware factory with type inference\n *\n * Factory function that creates middleware instances with parameters\n *\n * @example\n * ```ts\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * // permission check logic\n * await next();\n * }\n * );\n * ```\n */\nexport type NamedMiddlewareFactory<TName extends string = string, TArgs extends any[] = any[]> = {\n name: TName;\n _name: TName; // Type inference helper\n} & ((...args: TArgs) => MiddlewareHandler);\n\n/**\n * Define a named middleware\n *\n * Creates a middleware with a unique name that can be referenced\n * in route-level skip() calls with full type safety.\n *\n * Supports two patterns:\n * 1. Regular middleware: Direct middleware handler\n * 2. Factory middleware: Function that creates middleware with parameters\n *\n * @param name - Unique middleware name\n * @param handler - Middleware handler function\n * @returns Named middleware with type inference\n *\n * @example\n * ```ts\n * // Regular middleware\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * const token = c.req.header('authorization');\n * if (!token) {\n * return c.json({ error: 'Unauthorized' }, 401);\n * }\n * c.set('user', await verifyToken(token));\n * await next();\n * });\n *\n * // Factory middleware\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * const user = c.get('user');\n * if (!hasPermissions(user, permissions)) {\n * return c.json({ error: 'Forbidden' }, 403);\n * }\n * await next();\n * }\n * );\n *\n * // server.config.ts\n * export default defineServerConfig()\n * .middlewares([authMiddleware])\n * .routes(appRouter)\n * .build();\n *\n * // routes.ts - skip by name\n * export const publicRoute = route.get('/health')\n * .skip(['auth']) // ✅ Type-safe! Autocomplete!\n * .handler(async (c) => c.success({ status: 'ok' }));\n *\n * // Use factory middleware inline\n * export const protectedRoute = route.get('/admin')\n * .use([requirePermissions('admin:write')])\n * .handler(async (c) => { ... });\n * ```\n */\nexport function defineMiddleware<TName extends string>(\n name: TName,\n handler: MiddlewareHandler\n): NamedMiddleware<TName>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler\n): NamedMiddlewareFactory<TName, TArgs>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[] = []>(\n name: TName,\n handlerOrFactory: MiddlewareHandler | ((...args: TArgs) => MiddlewareHandler)\n): NamedMiddleware<TName> | NamedMiddlewareFactory<TName, TArgs>\n{\n // Distinguish between regular middleware and factory by parameter count\n // MiddlewareHandler always has exactly 2 parameters: (c, next)\n // Factory has any other number of parameters\n if (typeof handlerOrFactory === 'function')\n {\n const paramCount = handlerOrFactory.length;\n\n // Regular middleware handler (c, next) => ...\n if (paramCount === 2)\n {\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n };\n }\n // Factory (...args) => (c, next) => ...\n else\n {\n // Create a new wrapper function to avoid \"Cannot assign to read only property 'name'\" error\n const factory = handlerOrFactory as (...args: TArgs) => MiddlewareHandler;\n const wrapper = (...args: TArgs) => factory(...args);\n\n // Use Object.defineProperty to set name property (which is read-only by default)\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n }\n }\n\n // Fallback: treat as regular middleware\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n };\n}\n\n/**\n * Define a middleware factory explicitly\n *\n * Use this when your factory function has exactly 2 parameters,\n * which would be incorrectly detected as a regular middleware handler.\n *\n * @param name - Unique middleware name\n * @param factory - Factory function that returns a middleware handler\n * @returns Named middleware factory with type inference\n *\n * @example\n * ```ts\n * // Factory with 2 params (would be misdetected by defineMiddleware)\n * export const rateLimiter = defineMiddlewareFactory('rateLimit',\n * (limit: number, window: number) => async (c, next) => {\n * // rate limit logic using limit and window\n * await next();\n * }\n * );\n *\n * // Usage\n * route.get('/api')\n * .use([rateLimiter(100, 60000)]) // 100 requests per minute\n * .handler(...)\n * ```\n */\nexport function defineMiddlewareFactory<TName extends string, TArgs extends unknown[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler\n): NamedMiddlewareFactory<TName, TArgs>\n{\n const wrapper = (...args: TArgs) => factory(...args);\n\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n}\n\n/**\n * Extract middleware names from an array of named middlewares\n *\n * @example\n * ```ts\n * type Middlewares = [\n * NamedMiddleware<'auth'>,\n * NamedMiddleware<'rateLimit'>\n * ];\n * type Names = ExtractMiddlewareNames<Middlewares>; // 'auth' | 'rateLimit'\n * ```\n */\nexport type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> =\n T[number]['_name'];","/**\n * Route Helper Functions\n *\n * Type guards and TypeBox utilities\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { Type } from '@sinclair/typebox';\nimport type { HttpMethod } from './types';\n\n/**\n * Type guard for HttpMethod\n */\nexport function isHttpMethod(value: unknown): value is HttpMethod\n{\n return (\n typeof value === 'string' &&\n ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(value)\n );\n}\n\n/**\n * Nullable - Creates a union of T | null\n *\n * @example\n * ```typescript\n * // string | null\n * firstName: Nullable(Type.String())\n * ```\n */\nexport const Nullable = <T extends TSchema>(schema: T) =>\n Type.Union([schema, Type.Null()]);\n\n/**\n * OptionalNullable - Creates a union of T | null | undefined\n *\n * @example\n * ```typescript\n * // string | null | undefined\n * lastName: OptionalNullable(Type.String())\n * ```\n */\nexport const OptionalNullable = <T extends TSchema>(schema: T) =>\n Type.Optional(Type.Union([schema, Type.Null()]));"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spfn/core",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.5",
4
4
  "description": "SPFN Framework Core - File-based routing, transactions, repository pattern",
5
5
  "type": "module",
6
6
  "exports": {