clawfire 0.1.0
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.
- package/README.md +182 -0
- package/dist/admin.cjs +309 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +93 -0
- package/dist/admin.d.ts +93 -0
- package/dist/admin.js +274 -0
- package/dist/admin.js.map +1 -0
- package/dist/auth-DQ3cifhb.d.cts +55 -0
- package/dist/auth-DtnUPbXT.d.ts +55 -0
- package/dist/chunk-37Y2XI7X.js +75 -0
- package/dist/chunk-YGIPORYL.js +339 -0
- package/dist/cli.js +241 -0
- package/dist/client.cjs +97 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +4 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/codegen.cjs +648 -0
- package/dist/codegen.cjs.map +1 -0
- package/dist/codegen.d.cts +25 -0
- package/dist/codegen.d.ts +25 -0
- package/dist/codegen.js +617 -0
- package/dist/codegen.js.map +1 -0
- package/dist/config-QMBJRn9G.d.cts +46 -0
- package/dist/config-QMBJRn9G.d.ts +46 -0
- package/dist/dev-server-QAVWINAT.js +973 -0
- package/dist/dev.cjs +1388 -0
- package/dist/dev.cjs.map +1 -0
- package/dist/dev.d.cts +111 -0
- package/dist/dev.d.ts +111 -0
- package/dist/dev.js +1349 -0
- package/dist/dev.js.map +1 -0
- package/dist/discover-BPMAZFBD.js +9 -0
- package/dist/discover-DYNqz_ym.d.cts +28 -0
- package/dist/discover-DYNqz_ym.d.ts +28 -0
- package/dist/errors-s_mP7rs9.d.cts +33 -0
- package/dist/errors-s_mP7rs9.d.ts +33 -0
- package/dist/functions.cjs +1156 -0
- package/dist/functions.cjs.map +1 -0
- package/dist/functions.d.cts +115 -0
- package/dist/functions.d.ts +115 -0
- package/dist/functions.js +1108 -0
- package/dist/functions.js.map +1 -0
- package/dist/hosting-7WVFHAYJ.js +85 -0
- package/dist/html-PCUCJGBH.js +7 -0
- package/dist/index.cjs +349 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +312 -0
- package/dist/index.js.map +1 -0
- package/dist/playground.cjs +364 -0
- package/dist/playground.cjs.map +1 -0
- package/dist/playground.d.cts +12 -0
- package/dist/playground.d.ts +12 -0
- package/dist/playground.js +337 -0
- package/dist/playground.js.map +1 -0
- package/dist/router-BVB_I-tu.d.ts +65 -0
- package/dist/router-Cikk8Heq.d.cts +65 -0
- package/dist/schema-BJsictSV.d.cts +172 -0
- package/dist/schema-BJsictSV.d.ts +172 -0
- package/package.json +150 -0
- package/templates/CLAUDE.md +71 -0
- package/templates/app/routes/auth/login.ts +35 -0
- package/templates/app/routes/health.ts +20 -0
- package/templates/app/schemas/user.ts +26 -0
- package/templates/clawfire.config.ts +25 -0
- package/templates/functions/index.ts +43 -0
- package/templates/starter/.claude/skills/clawfire-api/SKILL.md +131 -0
- package/templates/starter/.claude/skills/clawfire-auth/SKILL.md +111 -0
- package/templates/starter/.claude/skills/clawfire-deploy/SKILL.md +95 -0
- package/templates/starter/.claude/skills/clawfire-diagnose/SKILL.md +99 -0
- package/templates/starter/.claude/skills/clawfire-model/SKILL.md +128 -0
- package/templates/starter/CLAUDE.md +227 -0
- package/templates/starter/app/routes/health.ts +20 -0
- package/templates/starter/app/routes/todos/create.ts +25 -0
- package/templates/starter/app/routes/todos/delete.ts +20 -0
- package/templates/starter/app/routes/todos/list.ts +26 -0
- package/templates/starter/app/routes/todos/update.ts +32 -0
- package/templates/starter/app/schemas/todo.ts +16 -0
- package/templates/starter/app/store.ts +56 -0
- package/templates/starter/clawfire.config.ts +25 -0
- package/templates/starter/dev.ts +12 -0
- package/templates/starter/package.json +19 -0
- package/templates/starter/public/index.html +365 -0
- package/templates/starter/tsconfig.json +17 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/schema.ts","../src/core/errors.ts","../src/core/logger.ts","../src/core/config.ts"],"sourcesContent":["/**\n * Clawfire — AI-First Firebase App Framework\n *\n * Main entry point. Re-exports core types and utilities.\n *\n * @example\n * ```ts\n * import { defineAPI, defineModel, Errors, z } from \"clawfire\";\n * ```\n */\n\n// Re-export Zod for convenience\nexport { z } from \"zod\";\n\n// Core\nexport {\n defineAPI,\n defineModel,\n zodToJsonSchema,\n modelToZodSchema,\n contractToManifest,\n type APIContract,\n type APIMeta,\n type AuthLevel,\n type AuthContext,\n type HandlerContext,\n type ModelDefinition,\n type ModelField,\n type ModelIndex,\n type ModelRules,\n type ManifestEntry,\n type Manifest,\n} from \"./core/index.js\";\n\nexport {\n ClawfireError,\n Errors,\n type ClawfireErrorCode,\n} from \"./core/errors.js\";\n\nexport {\n logger,\n setLogLevel,\n maskSensitive,\n} from \"./core/logger.js\";\n\nexport {\n configureClawfire,\n getConfig,\n type ClawfireConfig,\n} from \"./core/config.js\";\n","/**\n * Clawfire Schema & Contract System\n *\n * 모든 API는 input/output schema + meta + handler로 구성된 \"계약(Contract)\"으로 정의됩니다.\n * Zod를 사용하여 타입 안전성과 런타임 검증을 동시에 보장합니다.\n */\nimport { z, type ZodType, type ZodObject, type ZodRawShape } from \"zod\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\n/** 인증 컨텍스트 */\nexport interface AuthContext {\n uid: string;\n email?: string;\n emailVerified?: boolean;\n role?: string;\n customClaims?: Record<string, unknown>;\n token?: string;\n}\n\n/** API 핸들러에 전달되는 컨텍스트 */\nexport interface HandlerContext {\n auth: AuthContext | null;\n /** 재인증 여부 (민감 작업용) */\n reauthenticated?: boolean;\n /** 원본 요청 헤더 */\n headers?: Record<string, string>;\n /** 요청 IP */\n ip?: string;\n}\n\n/** 권한 수준 */\nexport type AuthLevel = \"public\" | \"authenticated\" | \"role\" | \"reauth\";\n\n/** API 메타데이터 */\nexport interface APIMeta {\n /** API 설명 (AI/Playground용) */\n description: string;\n /** 태그 (그룹화용) */\n tags?: string[];\n /** 인증 요구 수준 */\n auth?: AuthLevel;\n /** 필요 역할 (auth가 'role'일 때) */\n roles?: string[];\n /** 재인증 필요 여부 */\n reauth?: boolean;\n /** Rate limit (초당 요청 수) */\n rateLimit?: number;\n /** 비활성화 여부 */\n deprecated?: boolean;\n /** 예시 입력값 */\n exampleInput?: unknown;\n /** 예시 출력값 */\n exampleOutput?: unknown;\n}\n\n/** API 계약 정의 */\nexport interface APIContract<\n TInput extends ZodType = ZodType,\n TOutput extends ZodType = ZodType,\n> {\n /** 입력 스키마 */\n input: TInput;\n /** 출력 스키마 */\n output: TOutput;\n /** 메타데이터 */\n meta: APIMeta;\n /** 핸들러 함수 */\n handler: (\n input: z.infer<TInput>,\n ctx: HandlerContext,\n ) => Promise<z.infer<TOutput>>;\n}\n\n/** 모델 필드 정의 */\nexport interface ModelField {\n type: \"string\" | \"number\" | \"boolean\" | \"timestamp\" | \"array\" | \"map\" | \"reference\" | \"geopoint\";\n required?: boolean;\n description?: string;\n default?: unknown;\n /** 배열 아이템 타입 */\n items?: ModelField;\n /** 맵 값 타입 */\n values?: ModelField;\n /** 참조 대상 컬렉션 */\n ref?: string;\n /** enum 값 리스트 */\n enum?: string[];\n}\n\n/** 모델 정의 */\nexport interface ModelDefinition {\n /** 컬렉션 이름 */\n collection: string;\n /** 필드 정의 */\n fields: Record<string, ModelField>;\n /** 서브컬렉션 */\n subcollections?: Record<string, ModelDefinition>;\n /** 인덱스 */\n indexes?: ModelIndex[];\n /** 보안 규칙 */\n rules?: ModelRules;\n /** 타임스탬프 자동 생성 */\n timestamps?: boolean;\n /** 소프트 삭제 */\n softDelete?: boolean;\n}\n\n/** Firestore 인덱스 */\nexport interface ModelIndex {\n fields: Array<{ field: string; order?: \"asc\" | \"desc\" }>;\n}\n\n/** 모델 보안 규칙 */\nexport interface ModelRules {\n read?: AuthLevel;\n create?: AuthLevel;\n update?: AuthLevel;\n delete?: AuthLevel;\n readRoles?: string[];\n createRoles?: string[];\n updateRoles?: string[];\n deleteRoles?: string[];\n /** 소유자만 읽기/쓰기 가능 필드 */\n ownerField?: string;\n}\n\n// ─── Builders ────────────────────────────────────────────────────────\n\n/**\n * API 계약 정의\n *\n * @example\n * ```ts\n * export default defineAPI({\n * input: z.object({ name: z.string() }),\n * output: z.object({ id: z.string(), name: z.string() }),\n * meta: { description: \"상품 생성\", auth: \"authenticated\" },\n * handler: async (input, ctx) => {\n * const id = await db.create(\"products\", input);\n * return { id, ...input };\n * }\n * });\n * ```\n */\nexport function defineAPI<\n TInput extends ZodType,\n TOutput extends ZodType,\n>(contract: APIContract<TInput, TOutput>): APIContract<TInput, TOutput> {\n return contract;\n}\n\n/**\n * 모델(Firestore 컬렉션) 정의\n *\n * @example\n * ```ts\n * export const Product = defineModel({\n * collection: \"products\",\n * fields: {\n * name: { type: \"string\", required: true },\n * price: { type: \"number\", required: true },\n * tags: { type: \"array\", items: { type: \"string\" } },\n * },\n * timestamps: true,\n * rules: { read: \"public\", create: \"authenticated\" }\n * });\n * ```\n */\nexport function defineModel(definition: ModelDefinition): ModelDefinition {\n return {\n timestamps: true,\n ...definition,\n };\n}\n\n// ─── Schema Utilities ────────────────────────────────────────────────\n\n/** Zod 스키마에서 JSON Schema 생성 (Playground/문서용) */\nexport function zodToJsonSchema(schema: ZodType): Record<string, unknown> {\n return extractZodShape(schema);\n}\n\nfunction extractZodShape(schema: ZodType): Record<string, unknown> {\n const def = (schema as any)._def;\n\n if (!def) return { type: \"unknown\" };\n\n switch (def.typeName) {\n case \"ZodObject\": {\n const shape = (schema as ZodObject<ZodRawShape>).shape;\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = extractZodShape(value as ZodType);\n if (!(value as any).isOptional?.()) {\n const innerDef = (value as any)._def;\n if (innerDef?.typeName !== \"ZodOptional\" && innerDef?.typeName !== \"ZodDefault\") {\n required.push(key);\n }\n }\n }\n\n return { type: \"object\", properties, ...(required.length > 0 ? { required } : {}) };\n }\n case \"ZodString\":\n return { type: \"string\", ...(def.checks?.length ? extractStringChecks(def.checks) : {}) };\n case \"ZodNumber\":\n return { type: \"number\" };\n case \"ZodBoolean\":\n return { type: \"boolean\" };\n case \"ZodArray\":\n return { type: \"array\", items: extractZodShape(def.type) };\n case \"ZodEnum\":\n return { type: \"string\", enum: def.values };\n case \"ZodOptional\":\n return { ...extractZodShape(def.innerType), optional: true };\n case \"ZodDefault\":\n return { ...extractZodShape(def.innerType), default: def.defaultValue() };\n case \"ZodNullable\":\n return { ...extractZodShape(def.innerType), nullable: true };\n case \"ZodLiteral\":\n return { type: typeof def.value, const: def.value };\n case \"ZodUnion\":\n return { oneOf: def.options.map((o: ZodType) => extractZodShape(o)) };\n case \"ZodRecord\":\n return { type: \"object\", additionalProperties: extractZodShape(def.valueType) };\n case \"ZodDate\":\n return { type: \"string\", format: \"date-time\" };\n default:\n return { type: \"unknown\" };\n }\n}\n\nfunction extractStringChecks(checks: Array<{ kind: string; value?: unknown }>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const check of checks) {\n switch (check.kind) {\n case \"min\": result.minLength = check.value; break;\n case \"max\": result.maxLength = check.value; break;\n case \"email\": result.format = \"email\"; break;\n case \"url\": result.format = \"uri\"; break;\n case \"uuid\": result.format = \"uuid\"; break;\n }\n }\n return result;\n}\n\n/** 모델 정의에서 Zod 스키마 자동 생성 */\nexport function modelToZodSchema(model: ModelDefinition): ZodObject<ZodRawShape> {\n const shape: ZodRawShape = {};\n\n for (const [key, field] of Object.entries(model.fields)) {\n let fieldSchema: ZodType = fieldToZod(field);\n if (!field.required) {\n fieldSchema = fieldSchema.optional();\n }\n shape[key] = fieldSchema;\n }\n\n if (model.timestamps) {\n shape.createdAt = z.string().datetime().optional();\n shape.updatedAt = z.string().datetime().optional();\n }\n\n if (model.softDelete) {\n shape.deletedAt = z.string().datetime().nullable().optional();\n }\n\n return z.object(shape);\n}\n\nfunction fieldToZod(field: ModelField): ZodType {\n switch (field.type) {\n case \"string\":\n if (field.enum) return z.enum(field.enum as [string, ...string[]]);\n return z.string();\n case \"number\":\n return z.number();\n case \"boolean\":\n return z.boolean();\n case \"timestamp\":\n return z.string().datetime();\n case \"array\":\n if (field.items) return z.array(fieldToZod(field.items));\n return z.array(z.unknown());\n case \"map\":\n if (field.values) return z.record(z.string(), fieldToZod(field.values));\n return z.record(z.string(), z.unknown());\n case \"reference\":\n return z.string(); // 참조는 문서 경로 문자열\n case \"geopoint\":\n return z.object({ latitude: z.number(), longitude: z.number() });\n default:\n return z.unknown();\n }\n}\n\n// ─── Manifest ────────────────────────────────────────────────────────\n\n/** API 매니페스트 항목 */\nexport interface ManifestEntry {\n path: string;\n method: \"POST\"; // Clawfire는 모두 POST\n meta: APIMeta;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown>;\n}\n\n/** 전체 매니페스트 */\nexport interface Manifest {\n version: string;\n generatedAt: string;\n apis: ManifestEntry[];\n models: Record<string, ModelDefinition>;\n}\n\n/** 계약에서 매니페스트 항목 생성 */\nexport function contractToManifest(\n path: string,\n contract: APIContract,\n): ManifestEntry {\n return {\n path,\n method: \"POST\",\n meta: contract.meta,\n inputSchema: zodToJsonSchema(contract.input),\n outputSchema: zodToJsonSchema(contract.output),\n };\n}\n","/**\n * Clawfire Error System\n *\n * 표준화된 에러 코드와 구조로 일관된 에러 응답을 제공합니다.\n */\n\nexport type ClawfireErrorCode =\n | \"VALIDATION_ERROR\"\n | \"UNAUTHORIZED\"\n | \"FORBIDDEN\"\n | \"NOT_FOUND\"\n | \"CONFLICT\"\n | \"RATE_LIMITED\"\n | \"REAUTH_REQUIRED\"\n | \"INTERNAL_ERROR\"\n | \"SERVICE_UNAVAILABLE\";\n\nconst HTTP_STATUS_MAP: Record<ClawfireErrorCode, number> = {\n VALIDATION_ERROR: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n RATE_LIMITED: 429,\n REAUTH_REQUIRED: 401,\n INTERNAL_ERROR: 500,\n SERVICE_UNAVAILABLE: 503,\n};\n\nexport class ClawfireError extends Error {\n readonly code: ClawfireErrorCode;\n readonly statusCode: number;\n readonly details?: unknown;\n\n constructor(code: ClawfireErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = \"ClawfireError\";\n this.code = code;\n this.statusCode = HTTP_STATUS_MAP[code];\n this.details = details;\n }\n\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n ...(this.details ? { details: this.details } : {}),\n },\n };\n }\n}\n\n/** 편의 팩토리 함수 */\nexport const Errors = {\n validation: (message: string, details?: unknown) =>\n new ClawfireError(\"VALIDATION_ERROR\", message, details),\n\n unauthorized: (message = \"Authentication required\") =>\n new ClawfireError(\"UNAUTHORIZED\", message),\n\n forbidden: (message = \"Insufficient permissions\") =>\n new ClawfireError(\"FORBIDDEN\", message),\n\n notFound: (message = \"Resource not found\") =>\n new ClawfireError(\"NOT_FOUND\", message),\n\n conflict: (message: string) =>\n new ClawfireError(\"CONFLICT\", message),\n\n rateLimited: (message = \"Too many requests\") =>\n new ClawfireError(\"RATE_LIMITED\", message),\n\n reauthRequired: (message = \"Re-authentication required for this action\") =>\n new ClawfireError(\"REAUTH_REQUIRED\", message),\n\n internal: (message = \"Internal server error\") =>\n new ClawfireError(\"INTERNAL_ERROR\", message),\n\n unavailable: (message = \"Service temporarily unavailable\") =>\n new ClawfireError(\"SERVICE_UNAVAILABLE\", message),\n};\n","/**\n * Clawfire Logger\n *\n * 보안 로깅 (민감 정보 마스킹 포함)\n */\n\n/** 마스킹할 필드명 패턴 */\nconst SENSITIVE_FIELDS = [\n \"password\",\n \"token\",\n \"secret\",\n \"apiKey\",\n \"api_key\",\n \"authorization\",\n \"credit_card\",\n \"creditCard\",\n \"ssn\",\n \"cardNumber\",\n \"card_number\",\n \"cvv\",\n \"pin\",\n];\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet currentLevel: LogLevel = \"info\";\n\nexport function setLogLevel(level: LogLevel) {\n currentLevel = level;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];\n}\n\n/** 민감 필드 마스킹 */\nexport function maskSensitive(obj: unknown): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === \"string\") return obj;\n if (typeof obj !== \"object\") return obj;\n\n if (Array.isArray(obj)) {\n return obj.map(maskSensitive);\n }\n\n const masked: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (SENSITIVE_FIELDS.some((f) => key.toLowerCase().includes(f.toLowerCase()))) {\n masked[key] = \"***MASKED***\";\n } else if (typeof value === \"object\" && value !== null) {\n masked[key] = maskSensitive(value);\n } else {\n masked[key] = value;\n }\n }\n return masked;\n}\n\nfunction formatLog(level: LogLevel, message: string, data?: unknown): string {\n const timestamp = new Date().toISOString();\n const prefix = `[${timestamp}] [CLAWFIRE] [${level.toUpperCase()}]`;\n if (data !== undefined) {\n return `${prefix} ${message} ${JSON.stringify(maskSensitive(data))}`;\n }\n return `${prefix} ${message}`;\n}\n\nexport const logger = {\n debug(message: string, data?: unknown) {\n if (shouldLog(\"debug\")) console.debug(formatLog(\"debug\", message, data));\n },\n info(message: string, data?: unknown) {\n if (shouldLog(\"info\")) console.info(formatLog(\"info\", message, data));\n },\n warn(message: string, data?: unknown) {\n if (shouldLog(\"warn\")) console.warn(formatLog(\"warn\", message, data));\n },\n error(message: string, data?: unknown) {\n if (shouldLog(\"error\")) console.error(formatLog(\"error\", message, data));\n },\n};\n","/**\n * Clawfire Configuration\n */\n\nexport interface ClawfireConfig {\n /** Firebase 프로젝트 설정 */\n firebase: {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket?: string;\n messagingSenderId?: string;\n appId: string;\n measurementId?: string;\n };\n /** 서버 설정 */\n server?: {\n /** Firebase Functions 리전 */\n region?: string;\n /** CORS 허용 도메인 */\n cors?: string[];\n /** 전역 rate limit (초당 요청) */\n rateLimit?: number;\n };\n /** 보안 설정 */\n security?: {\n /** 입력 검증 활성화 (기본: true) */\n validateInput?: boolean;\n /** 출력 검증 활성화 (기본: false) */\n validateOutput?: boolean;\n /** 안전 헤더 자동 추가 (기본: true) */\n safeHeaders?: boolean;\n /** 로그 마스킹 활성화 (기본: true) */\n maskLogs?: boolean;\n };\n /** 플레이그라운드 설정 */\n playground?: {\n /** 활성화 여부 (기본: development에서만) */\n enabled?: boolean;\n /** 경로 */\n path?: string;\n };\n}\n\nconst defaultConfig: ClawfireConfig = {\n firebase: {\n apiKey: \"\",\n authDomain: \"\",\n projectId: \"\",\n appId: \"\",\n },\n server: {\n region: \"us-central1\",\n cors: [],\n rateLimit: 100,\n },\n security: {\n validateInput: true,\n validateOutput: false,\n safeHeaders: true,\n maskLogs: true,\n },\n playground: {\n enabled: undefined,\n path: \"/__playground\",\n },\n};\n\nlet _config: ClawfireConfig = defaultConfig;\n\nexport function configureClawfire(config: ClawfireConfig): ClawfireConfig {\n _config = {\n ...defaultConfig,\n ...config,\n firebase: { ...defaultConfig.firebase, ...config.firebase },\n server: { ...defaultConfig.server, ...config.server },\n security: { ...defaultConfig.security, ...config.security },\n playground: { ...defaultConfig.playground, ...config.playground },\n };\n return _config;\n}\n\nexport function getConfig(): ClawfireConfig {\n return _config;\n}\n"],"mappings":"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","names":["import_zod"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { z } from 'zod';
|
|
2
|
+
export { A as APIContract, a as APIMeta, b as AuthContext, c as AuthLevel, H as HandlerContext, M as Manifest, d as ManifestEntry, e as ModelDefinition, f as ModelField, i as ModelIndex, j as ModelRules, k as contractToManifest, g as defineAPI, h as defineModel, m as modelToZodSchema, z as zodToJsonSchema } from './schema-BJsictSV.cjs';
|
|
3
|
+
export { C as ClawfireError, a as ClawfireErrorCode, E as Errors } from './errors-s_mP7rs9.cjs';
|
|
4
|
+
export { C as ClawfireConfig, c as configureClawfire, g as getConfig } from './config-QMBJRn9G.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Clawfire Logger
|
|
8
|
+
*
|
|
9
|
+
* 보안 로깅 (민감 정보 마스킹 포함)
|
|
10
|
+
*/
|
|
11
|
+
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
12
|
+
declare function setLogLevel(level: LogLevel): void;
|
|
13
|
+
/** 민감 필드 마스킹 */
|
|
14
|
+
declare function maskSensitive(obj: unknown): unknown;
|
|
15
|
+
declare const logger: {
|
|
16
|
+
debug(message: string, data?: unknown): void;
|
|
17
|
+
info(message: string, data?: unknown): void;
|
|
18
|
+
warn(message: string, data?: unknown): void;
|
|
19
|
+
error(message: string, data?: unknown): void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { logger, maskSensitive, setLogLevel };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { z } from 'zod';
|
|
2
|
+
export { A as APIContract, a as APIMeta, b as AuthContext, c as AuthLevel, H as HandlerContext, M as Manifest, d as ManifestEntry, e as ModelDefinition, f as ModelField, i as ModelIndex, j as ModelRules, k as contractToManifest, g as defineAPI, h as defineModel, m as modelToZodSchema, z as zodToJsonSchema } from './schema-BJsictSV.js';
|
|
3
|
+
export { C as ClawfireError, a as ClawfireErrorCode, E as Errors } from './errors-s_mP7rs9.js';
|
|
4
|
+
export { C as ClawfireConfig, c as configureClawfire, g as getConfig } from './config-QMBJRn9G.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Clawfire Logger
|
|
8
|
+
*
|
|
9
|
+
* 보안 로깅 (민감 정보 마스킹 포함)
|
|
10
|
+
*/
|
|
11
|
+
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
12
|
+
declare function setLogLevel(level: LogLevel): void;
|
|
13
|
+
/** 민감 필드 마스킹 */
|
|
14
|
+
declare function maskSensitive(obj: unknown): unknown;
|
|
15
|
+
declare const logger: {
|
|
16
|
+
debug(message: string, data?: unknown): void;
|
|
17
|
+
info(message: string, data?: unknown): void;
|
|
18
|
+
warn(message: string, data?: unknown): void;
|
|
19
|
+
error(message: string, data?: unknown): void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { logger, maskSensitive, setLogLevel };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/core/schema.ts
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
function defineAPI(contract) {
|
|
7
|
+
return contract;
|
|
8
|
+
}
|
|
9
|
+
function defineModel(definition) {
|
|
10
|
+
return {
|
|
11
|
+
timestamps: true,
|
|
12
|
+
...definition
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function zodToJsonSchema(schema) {
|
|
16
|
+
return extractZodShape(schema);
|
|
17
|
+
}
|
|
18
|
+
function extractZodShape(schema) {
|
|
19
|
+
const def = schema._def;
|
|
20
|
+
if (!def) return { type: "unknown" };
|
|
21
|
+
switch (def.typeName) {
|
|
22
|
+
case "ZodObject": {
|
|
23
|
+
const shape = schema.shape;
|
|
24
|
+
const properties = {};
|
|
25
|
+
const required = [];
|
|
26
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
27
|
+
properties[key] = extractZodShape(value);
|
|
28
|
+
if (!value.isOptional?.()) {
|
|
29
|
+
const innerDef = value._def;
|
|
30
|
+
if (innerDef?.typeName !== "ZodOptional" && innerDef?.typeName !== "ZodDefault") {
|
|
31
|
+
required.push(key);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { type: "object", properties, ...required.length > 0 ? { required } : {} };
|
|
36
|
+
}
|
|
37
|
+
case "ZodString":
|
|
38
|
+
return { type: "string", ...def.checks?.length ? extractStringChecks(def.checks) : {} };
|
|
39
|
+
case "ZodNumber":
|
|
40
|
+
return { type: "number" };
|
|
41
|
+
case "ZodBoolean":
|
|
42
|
+
return { type: "boolean" };
|
|
43
|
+
case "ZodArray":
|
|
44
|
+
return { type: "array", items: extractZodShape(def.type) };
|
|
45
|
+
case "ZodEnum":
|
|
46
|
+
return { type: "string", enum: def.values };
|
|
47
|
+
case "ZodOptional":
|
|
48
|
+
return { ...extractZodShape(def.innerType), optional: true };
|
|
49
|
+
case "ZodDefault":
|
|
50
|
+
return { ...extractZodShape(def.innerType), default: def.defaultValue() };
|
|
51
|
+
case "ZodNullable":
|
|
52
|
+
return { ...extractZodShape(def.innerType), nullable: true };
|
|
53
|
+
case "ZodLiteral":
|
|
54
|
+
return { type: typeof def.value, const: def.value };
|
|
55
|
+
case "ZodUnion":
|
|
56
|
+
return { oneOf: def.options.map((o) => extractZodShape(o)) };
|
|
57
|
+
case "ZodRecord":
|
|
58
|
+
return { type: "object", additionalProperties: extractZodShape(def.valueType) };
|
|
59
|
+
case "ZodDate":
|
|
60
|
+
return { type: "string", format: "date-time" };
|
|
61
|
+
default:
|
|
62
|
+
return { type: "unknown" };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function extractStringChecks(checks) {
|
|
66
|
+
const result = {};
|
|
67
|
+
for (const check of checks) {
|
|
68
|
+
switch (check.kind) {
|
|
69
|
+
case "min":
|
|
70
|
+
result.minLength = check.value;
|
|
71
|
+
break;
|
|
72
|
+
case "max":
|
|
73
|
+
result.maxLength = check.value;
|
|
74
|
+
break;
|
|
75
|
+
case "email":
|
|
76
|
+
result.format = "email";
|
|
77
|
+
break;
|
|
78
|
+
case "url":
|
|
79
|
+
result.format = "uri";
|
|
80
|
+
break;
|
|
81
|
+
case "uuid":
|
|
82
|
+
result.format = "uuid";
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
function modelToZodSchema(model) {
|
|
89
|
+
const shape = {};
|
|
90
|
+
for (const [key, field] of Object.entries(model.fields)) {
|
|
91
|
+
let fieldSchema = fieldToZod(field);
|
|
92
|
+
if (!field.required) {
|
|
93
|
+
fieldSchema = fieldSchema.optional();
|
|
94
|
+
}
|
|
95
|
+
shape[key] = fieldSchema;
|
|
96
|
+
}
|
|
97
|
+
if (model.timestamps) {
|
|
98
|
+
shape.createdAt = z.string().datetime().optional();
|
|
99
|
+
shape.updatedAt = z.string().datetime().optional();
|
|
100
|
+
}
|
|
101
|
+
if (model.softDelete) {
|
|
102
|
+
shape.deletedAt = z.string().datetime().nullable().optional();
|
|
103
|
+
}
|
|
104
|
+
return z.object(shape);
|
|
105
|
+
}
|
|
106
|
+
function fieldToZod(field) {
|
|
107
|
+
switch (field.type) {
|
|
108
|
+
case "string":
|
|
109
|
+
if (field.enum) return z.enum(field.enum);
|
|
110
|
+
return z.string();
|
|
111
|
+
case "number":
|
|
112
|
+
return z.number();
|
|
113
|
+
case "boolean":
|
|
114
|
+
return z.boolean();
|
|
115
|
+
case "timestamp":
|
|
116
|
+
return z.string().datetime();
|
|
117
|
+
case "array":
|
|
118
|
+
if (field.items) return z.array(fieldToZod(field.items));
|
|
119
|
+
return z.array(z.unknown());
|
|
120
|
+
case "map":
|
|
121
|
+
if (field.values) return z.record(z.string(), fieldToZod(field.values));
|
|
122
|
+
return z.record(z.string(), z.unknown());
|
|
123
|
+
case "reference":
|
|
124
|
+
return z.string();
|
|
125
|
+
// 참조는 문서 경로 문자열
|
|
126
|
+
case "geopoint":
|
|
127
|
+
return z.object({ latitude: z.number(), longitude: z.number() });
|
|
128
|
+
default:
|
|
129
|
+
return z.unknown();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function contractToManifest(path, contract) {
|
|
133
|
+
return {
|
|
134
|
+
path,
|
|
135
|
+
method: "POST",
|
|
136
|
+
meta: contract.meta,
|
|
137
|
+
inputSchema: zodToJsonSchema(contract.input),
|
|
138
|
+
outputSchema: zodToJsonSchema(contract.output)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/core/errors.ts
|
|
143
|
+
var HTTP_STATUS_MAP = {
|
|
144
|
+
VALIDATION_ERROR: 400,
|
|
145
|
+
UNAUTHORIZED: 401,
|
|
146
|
+
FORBIDDEN: 403,
|
|
147
|
+
NOT_FOUND: 404,
|
|
148
|
+
CONFLICT: 409,
|
|
149
|
+
RATE_LIMITED: 429,
|
|
150
|
+
REAUTH_REQUIRED: 401,
|
|
151
|
+
INTERNAL_ERROR: 500,
|
|
152
|
+
SERVICE_UNAVAILABLE: 503
|
|
153
|
+
};
|
|
154
|
+
var ClawfireError = class extends Error {
|
|
155
|
+
code;
|
|
156
|
+
statusCode;
|
|
157
|
+
details;
|
|
158
|
+
constructor(code, message, details) {
|
|
159
|
+
super(message);
|
|
160
|
+
this.name = "ClawfireError";
|
|
161
|
+
this.code = code;
|
|
162
|
+
this.statusCode = HTTP_STATUS_MAP[code];
|
|
163
|
+
this.details = details;
|
|
164
|
+
}
|
|
165
|
+
toJSON() {
|
|
166
|
+
return {
|
|
167
|
+
error: {
|
|
168
|
+
code: this.code,
|
|
169
|
+
message: this.message,
|
|
170
|
+
...this.details ? { details: this.details } : {}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
var Errors = {
|
|
176
|
+
validation: (message, details) => new ClawfireError("VALIDATION_ERROR", message, details),
|
|
177
|
+
unauthorized: (message = "Authentication required") => new ClawfireError("UNAUTHORIZED", message),
|
|
178
|
+
forbidden: (message = "Insufficient permissions") => new ClawfireError("FORBIDDEN", message),
|
|
179
|
+
notFound: (message = "Resource not found") => new ClawfireError("NOT_FOUND", message),
|
|
180
|
+
conflict: (message) => new ClawfireError("CONFLICT", message),
|
|
181
|
+
rateLimited: (message = "Too many requests") => new ClawfireError("RATE_LIMITED", message),
|
|
182
|
+
reauthRequired: (message = "Re-authentication required for this action") => new ClawfireError("REAUTH_REQUIRED", message),
|
|
183
|
+
internal: (message = "Internal server error") => new ClawfireError("INTERNAL_ERROR", message),
|
|
184
|
+
unavailable: (message = "Service temporarily unavailable") => new ClawfireError("SERVICE_UNAVAILABLE", message)
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// src/core/logger.ts
|
|
188
|
+
var SENSITIVE_FIELDS = [
|
|
189
|
+
"password",
|
|
190
|
+
"token",
|
|
191
|
+
"secret",
|
|
192
|
+
"apiKey",
|
|
193
|
+
"api_key",
|
|
194
|
+
"authorization",
|
|
195
|
+
"credit_card",
|
|
196
|
+
"creditCard",
|
|
197
|
+
"ssn",
|
|
198
|
+
"cardNumber",
|
|
199
|
+
"card_number",
|
|
200
|
+
"cvv",
|
|
201
|
+
"pin"
|
|
202
|
+
];
|
|
203
|
+
var LOG_LEVELS = {
|
|
204
|
+
debug: 0,
|
|
205
|
+
info: 1,
|
|
206
|
+
warn: 2,
|
|
207
|
+
error: 3
|
|
208
|
+
};
|
|
209
|
+
var currentLevel = "info";
|
|
210
|
+
function setLogLevel(level) {
|
|
211
|
+
currentLevel = level;
|
|
212
|
+
}
|
|
213
|
+
function shouldLog(level) {
|
|
214
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];
|
|
215
|
+
}
|
|
216
|
+
function maskSensitive(obj) {
|
|
217
|
+
if (obj === null || obj === void 0) return obj;
|
|
218
|
+
if (typeof obj === "string") return obj;
|
|
219
|
+
if (typeof obj !== "object") return obj;
|
|
220
|
+
if (Array.isArray(obj)) {
|
|
221
|
+
return obj.map(maskSensitive);
|
|
222
|
+
}
|
|
223
|
+
const masked = {};
|
|
224
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
225
|
+
if (SENSITIVE_FIELDS.some((f) => key.toLowerCase().includes(f.toLowerCase()))) {
|
|
226
|
+
masked[key] = "***MASKED***";
|
|
227
|
+
} else if (typeof value === "object" && value !== null) {
|
|
228
|
+
masked[key] = maskSensitive(value);
|
|
229
|
+
} else {
|
|
230
|
+
masked[key] = value;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return masked;
|
|
234
|
+
}
|
|
235
|
+
function formatLog(level, message, data) {
|
|
236
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
237
|
+
const prefix = `[${timestamp}] [CLAWFIRE] [${level.toUpperCase()}]`;
|
|
238
|
+
if (data !== void 0) {
|
|
239
|
+
return `${prefix} ${message} ${JSON.stringify(maskSensitive(data))}`;
|
|
240
|
+
}
|
|
241
|
+
return `${prefix} ${message}`;
|
|
242
|
+
}
|
|
243
|
+
var logger = {
|
|
244
|
+
debug(message, data) {
|
|
245
|
+
if (shouldLog("debug")) console.debug(formatLog("debug", message, data));
|
|
246
|
+
},
|
|
247
|
+
info(message, data) {
|
|
248
|
+
if (shouldLog("info")) console.info(formatLog("info", message, data));
|
|
249
|
+
},
|
|
250
|
+
warn(message, data) {
|
|
251
|
+
if (shouldLog("warn")) console.warn(formatLog("warn", message, data));
|
|
252
|
+
},
|
|
253
|
+
error(message, data) {
|
|
254
|
+
if (shouldLog("error")) console.error(formatLog("error", message, data));
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/core/config.ts
|
|
259
|
+
var defaultConfig = {
|
|
260
|
+
firebase: {
|
|
261
|
+
apiKey: "",
|
|
262
|
+
authDomain: "",
|
|
263
|
+
projectId: "",
|
|
264
|
+
appId: ""
|
|
265
|
+
},
|
|
266
|
+
server: {
|
|
267
|
+
region: "us-central1",
|
|
268
|
+
cors: [],
|
|
269
|
+
rateLimit: 100
|
|
270
|
+
},
|
|
271
|
+
security: {
|
|
272
|
+
validateInput: true,
|
|
273
|
+
validateOutput: false,
|
|
274
|
+
safeHeaders: true,
|
|
275
|
+
maskLogs: true
|
|
276
|
+
},
|
|
277
|
+
playground: {
|
|
278
|
+
enabled: void 0,
|
|
279
|
+
path: "/__playground"
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var _config = defaultConfig;
|
|
283
|
+
function configureClawfire(config) {
|
|
284
|
+
_config = {
|
|
285
|
+
...defaultConfig,
|
|
286
|
+
...config,
|
|
287
|
+
firebase: { ...defaultConfig.firebase, ...config.firebase },
|
|
288
|
+
server: { ...defaultConfig.server, ...config.server },
|
|
289
|
+
security: { ...defaultConfig.security, ...config.security },
|
|
290
|
+
playground: { ...defaultConfig.playground, ...config.playground }
|
|
291
|
+
};
|
|
292
|
+
return _config;
|
|
293
|
+
}
|
|
294
|
+
function getConfig() {
|
|
295
|
+
return _config;
|
|
296
|
+
}
|
|
297
|
+
export {
|
|
298
|
+
ClawfireError,
|
|
299
|
+
Errors,
|
|
300
|
+
configureClawfire,
|
|
301
|
+
contractToManifest,
|
|
302
|
+
defineAPI,
|
|
303
|
+
defineModel,
|
|
304
|
+
getConfig,
|
|
305
|
+
logger,
|
|
306
|
+
maskSensitive,
|
|
307
|
+
modelToZodSchema,
|
|
308
|
+
setLogLevel,
|
|
309
|
+
z2 as z,
|
|
310
|
+
zodToJsonSchema
|
|
311
|
+
};
|
|
312
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/schema.ts","../src/core/errors.ts","../src/core/logger.ts","../src/core/config.ts"],"sourcesContent":["/**\n * Clawfire — AI-First Firebase App Framework\n *\n * Main entry point. Re-exports core types and utilities.\n *\n * @example\n * ```ts\n * import { defineAPI, defineModel, Errors, z } from \"clawfire\";\n * ```\n */\n\n// Re-export Zod for convenience\nexport { z } from \"zod\";\n\n// Core\nexport {\n defineAPI,\n defineModel,\n zodToJsonSchema,\n modelToZodSchema,\n contractToManifest,\n type APIContract,\n type APIMeta,\n type AuthLevel,\n type AuthContext,\n type HandlerContext,\n type ModelDefinition,\n type ModelField,\n type ModelIndex,\n type ModelRules,\n type ManifestEntry,\n type Manifest,\n} from \"./core/index.js\";\n\nexport {\n ClawfireError,\n Errors,\n type ClawfireErrorCode,\n} from \"./core/errors.js\";\n\nexport {\n logger,\n setLogLevel,\n maskSensitive,\n} from \"./core/logger.js\";\n\nexport {\n configureClawfire,\n getConfig,\n type ClawfireConfig,\n} from \"./core/config.js\";\n","/**\n * Clawfire Schema & Contract System\n *\n * 모든 API는 input/output schema + meta + handler로 구성된 \"계약(Contract)\"으로 정의됩니다.\n * Zod를 사용하여 타입 안전성과 런타임 검증을 동시에 보장합니다.\n */\nimport { z, type ZodType, type ZodObject, type ZodRawShape } from \"zod\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\n/** 인증 컨텍스트 */\nexport interface AuthContext {\n uid: string;\n email?: string;\n emailVerified?: boolean;\n role?: string;\n customClaims?: Record<string, unknown>;\n token?: string;\n}\n\n/** API 핸들러에 전달되는 컨텍스트 */\nexport interface HandlerContext {\n auth: AuthContext | null;\n /** 재인증 여부 (민감 작업용) */\n reauthenticated?: boolean;\n /** 원본 요청 헤더 */\n headers?: Record<string, string>;\n /** 요청 IP */\n ip?: string;\n}\n\n/** 권한 수준 */\nexport type AuthLevel = \"public\" | \"authenticated\" | \"role\" | \"reauth\";\n\n/** API 메타데이터 */\nexport interface APIMeta {\n /** API 설명 (AI/Playground용) */\n description: string;\n /** 태그 (그룹화용) */\n tags?: string[];\n /** 인증 요구 수준 */\n auth?: AuthLevel;\n /** 필요 역할 (auth가 'role'일 때) */\n roles?: string[];\n /** 재인증 필요 여부 */\n reauth?: boolean;\n /** Rate limit (초당 요청 수) */\n rateLimit?: number;\n /** 비활성화 여부 */\n deprecated?: boolean;\n /** 예시 입력값 */\n exampleInput?: unknown;\n /** 예시 출력값 */\n exampleOutput?: unknown;\n}\n\n/** API 계약 정의 */\nexport interface APIContract<\n TInput extends ZodType = ZodType,\n TOutput extends ZodType = ZodType,\n> {\n /** 입력 스키마 */\n input: TInput;\n /** 출력 스키마 */\n output: TOutput;\n /** 메타데이터 */\n meta: APIMeta;\n /** 핸들러 함수 */\n handler: (\n input: z.infer<TInput>,\n ctx: HandlerContext,\n ) => Promise<z.infer<TOutput>>;\n}\n\n/** 모델 필드 정의 */\nexport interface ModelField {\n type: \"string\" | \"number\" | \"boolean\" | \"timestamp\" | \"array\" | \"map\" | \"reference\" | \"geopoint\";\n required?: boolean;\n description?: string;\n default?: unknown;\n /** 배열 아이템 타입 */\n items?: ModelField;\n /** 맵 값 타입 */\n values?: ModelField;\n /** 참조 대상 컬렉션 */\n ref?: string;\n /** enum 값 리스트 */\n enum?: string[];\n}\n\n/** 모델 정의 */\nexport interface ModelDefinition {\n /** 컬렉션 이름 */\n collection: string;\n /** 필드 정의 */\n fields: Record<string, ModelField>;\n /** 서브컬렉션 */\n subcollections?: Record<string, ModelDefinition>;\n /** 인덱스 */\n indexes?: ModelIndex[];\n /** 보안 규칙 */\n rules?: ModelRules;\n /** 타임스탬프 자동 생성 */\n timestamps?: boolean;\n /** 소프트 삭제 */\n softDelete?: boolean;\n}\n\n/** Firestore 인덱스 */\nexport interface ModelIndex {\n fields: Array<{ field: string; order?: \"asc\" | \"desc\" }>;\n}\n\n/** 모델 보안 규칙 */\nexport interface ModelRules {\n read?: AuthLevel;\n create?: AuthLevel;\n update?: AuthLevel;\n delete?: AuthLevel;\n readRoles?: string[];\n createRoles?: string[];\n updateRoles?: string[];\n deleteRoles?: string[];\n /** 소유자만 읽기/쓰기 가능 필드 */\n ownerField?: string;\n}\n\n// ─── Builders ────────────────────────────────────────────────────────\n\n/**\n * API 계약 정의\n *\n * @example\n * ```ts\n * export default defineAPI({\n * input: z.object({ name: z.string() }),\n * output: z.object({ id: z.string(), name: z.string() }),\n * meta: { description: \"상품 생성\", auth: \"authenticated\" },\n * handler: async (input, ctx) => {\n * const id = await db.create(\"products\", input);\n * return { id, ...input };\n * }\n * });\n * ```\n */\nexport function defineAPI<\n TInput extends ZodType,\n TOutput extends ZodType,\n>(contract: APIContract<TInput, TOutput>): APIContract<TInput, TOutput> {\n return contract;\n}\n\n/**\n * 모델(Firestore 컬렉션) 정의\n *\n * @example\n * ```ts\n * export const Product = defineModel({\n * collection: \"products\",\n * fields: {\n * name: { type: \"string\", required: true },\n * price: { type: \"number\", required: true },\n * tags: { type: \"array\", items: { type: \"string\" } },\n * },\n * timestamps: true,\n * rules: { read: \"public\", create: \"authenticated\" }\n * });\n * ```\n */\nexport function defineModel(definition: ModelDefinition): ModelDefinition {\n return {\n timestamps: true,\n ...definition,\n };\n}\n\n// ─── Schema Utilities ────────────────────────────────────────────────\n\n/** Zod 스키마에서 JSON Schema 생성 (Playground/문서용) */\nexport function zodToJsonSchema(schema: ZodType): Record<string, unknown> {\n return extractZodShape(schema);\n}\n\nfunction extractZodShape(schema: ZodType): Record<string, unknown> {\n const def = (schema as any)._def;\n\n if (!def) return { type: \"unknown\" };\n\n switch (def.typeName) {\n case \"ZodObject\": {\n const shape = (schema as ZodObject<ZodRawShape>).shape;\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = extractZodShape(value as ZodType);\n if (!(value as any).isOptional?.()) {\n const innerDef = (value as any)._def;\n if (innerDef?.typeName !== \"ZodOptional\" && innerDef?.typeName !== \"ZodDefault\") {\n required.push(key);\n }\n }\n }\n\n return { type: \"object\", properties, ...(required.length > 0 ? { required } : {}) };\n }\n case \"ZodString\":\n return { type: \"string\", ...(def.checks?.length ? extractStringChecks(def.checks) : {}) };\n case \"ZodNumber\":\n return { type: \"number\" };\n case \"ZodBoolean\":\n return { type: \"boolean\" };\n case \"ZodArray\":\n return { type: \"array\", items: extractZodShape(def.type) };\n case \"ZodEnum\":\n return { type: \"string\", enum: def.values };\n case \"ZodOptional\":\n return { ...extractZodShape(def.innerType), optional: true };\n case \"ZodDefault\":\n return { ...extractZodShape(def.innerType), default: def.defaultValue() };\n case \"ZodNullable\":\n return { ...extractZodShape(def.innerType), nullable: true };\n case \"ZodLiteral\":\n return { type: typeof def.value, const: def.value };\n case \"ZodUnion\":\n return { oneOf: def.options.map((o: ZodType) => extractZodShape(o)) };\n case \"ZodRecord\":\n return { type: \"object\", additionalProperties: extractZodShape(def.valueType) };\n case \"ZodDate\":\n return { type: \"string\", format: \"date-time\" };\n default:\n return { type: \"unknown\" };\n }\n}\n\nfunction extractStringChecks(checks: Array<{ kind: string; value?: unknown }>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const check of checks) {\n switch (check.kind) {\n case \"min\": result.minLength = check.value; break;\n case \"max\": result.maxLength = check.value; break;\n case \"email\": result.format = \"email\"; break;\n case \"url\": result.format = \"uri\"; break;\n case \"uuid\": result.format = \"uuid\"; break;\n }\n }\n return result;\n}\n\n/** 모델 정의에서 Zod 스키마 자동 생성 */\nexport function modelToZodSchema(model: ModelDefinition): ZodObject<ZodRawShape> {\n const shape: ZodRawShape = {};\n\n for (const [key, field] of Object.entries(model.fields)) {\n let fieldSchema: ZodType = fieldToZod(field);\n if (!field.required) {\n fieldSchema = fieldSchema.optional();\n }\n shape[key] = fieldSchema;\n }\n\n if (model.timestamps) {\n shape.createdAt = z.string().datetime().optional();\n shape.updatedAt = z.string().datetime().optional();\n }\n\n if (model.softDelete) {\n shape.deletedAt = z.string().datetime().nullable().optional();\n }\n\n return z.object(shape);\n}\n\nfunction fieldToZod(field: ModelField): ZodType {\n switch (field.type) {\n case \"string\":\n if (field.enum) return z.enum(field.enum as [string, ...string[]]);\n return z.string();\n case \"number\":\n return z.number();\n case \"boolean\":\n return z.boolean();\n case \"timestamp\":\n return z.string().datetime();\n case \"array\":\n if (field.items) return z.array(fieldToZod(field.items));\n return z.array(z.unknown());\n case \"map\":\n if (field.values) return z.record(z.string(), fieldToZod(field.values));\n return z.record(z.string(), z.unknown());\n case \"reference\":\n return z.string(); // 참조는 문서 경로 문자열\n case \"geopoint\":\n return z.object({ latitude: z.number(), longitude: z.number() });\n default:\n return z.unknown();\n }\n}\n\n// ─── Manifest ────────────────────────────────────────────────────────\n\n/** API 매니페스트 항목 */\nexport interface ManifestEntry {\n path: string;\n method: \"POST\"; // Clawfire는 모두 POST\n meta: APIMeta;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown>;\n}\n\n/** 전체 매니페스트 */\nexport interface Manifest {\n version: string;\n generatedAt: string;\n apis: ManifestEntry[];\n models: Record<string, ModelDefinition>;\n}\n\n/** 계약에서 매니페스트 항목 생성 */\nexport function contractToManifest(\n path: string,\n contract: APIContract,\n): ManifestEntry {\n return {\n path,\n method: \"POST\",\n meta: contract.meta,\n inputSchema: zodToJsonSchema(contract.input),\n outputSchema: zodToJsonSchema(contract.output),\n };\n}\n","/**\n * Clawfire Error System\n *\n * 표준화된 에러 코드와 구조로 일관된 에러 응답을 제공합니다.\n */\n\nexport type ClawfireErrorCode =\n | \"VALIDATION_ERROR\"\n | \"UNAUTHORIZED\"\n | \"FORBIDDEN\"\n | \"NOT_FOUND\"\n | \"CONFLICT\"\n | \"RATE_LIMITED\"\n | \"REAUTH_REQUIRED\"\n | \"INTERNAL_ERROR\"\n | \"SERVICE_UNAVAILABLE\";\n\nconst HTTP_STATUS_MAP: Record<ClawfireErrorCode, number> = {\n VALIDATION_ERROR: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n RATE_LIMITED: 429,\n REAUTH_REQUIRED: 401,\n INTERNAL_ERROR: 500,\n SERVICE_UNAVAILABLE: 503,\n};\n\nexport class ClawfireError extends Error {\n readonly code: ClawfireErrorCode;\n readonly statusCode: number;\n readonly details?: unknown;\n\n constructor(code: ClawfireErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = \"ClawfireError\";\n this.code = code;\n this.statusCode = HTTP_STATUS_MAP[code];\n this.details = details;\n }\n\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n ...(this.details ? { details: this.details } : {}),\n },\n };\n }\n}\n\n/** 편의 팩토리 함수 */\nexport const Errors = {\n validation: (message: string, details?: unknown) =>\n new ClawfireError(\"VALIDATION_ERROR\", message, details),\n\n unauthorized: (message = \"Authentication required\") =>\n new ClawfireError(\"UNAUTHORIZED\", message),\n\n forbidden: (message = \"Insufficient permissions\") =>\n new ClawfireError(\"FORBIDDEN\", message),\n\n notFound: (message = \"Resource not found\") =>\n new ClawfireError(\"NOT_FOUND\", message),\n\n conflict: (message: string) =>\n new ClawfireError(\"CONFLICT\", message),\n\n rateLimited: (message = \"Too many requests\") =>\n new ClawfireError(\"RATE_LIMITED\", message),\n\n reauthRequired: (message = \"Re-authentication required for this action\") =>\n new ClawfireError(\"REAUTH_REQUIRED\", message),\n\n internal: (message = \"Internal server error\") =>\n new ClawfireError(\"INTERNAL_ERROR\", message),\n\n unavailable: (message = \"Service temporarily unavailable\") =>\n new ClawfireError(\"SERVICE_UNAVAILABLE\", message),\n};\n","/**\n * Clawfire Logger\n *\n * 보안 로깅 (민감 정보 마스킹 포함)\n */\n\n/** 마스킹할 필드명 패턴 */\nconst SENSITIVE_FIELDS = [\n \"password\",\n \"token\",\n \"secret\",\n \"apiKey\",\n \"api_key\",\n \"authorization\",\n \"credit_card\",\n \"creditCard\",\n \"ssn\",\n \"cardNumber\",\n \"card_number\",\n \"cvv\",\n \"pin\",\n];\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet currentLevel: LogLevel = \"info\";\n\nexport function setLogLevel(level: LogLevel) {\n currentLevel = level;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];\n}\n\n/** 민감 필드 마스킹 */\nexport function maskSensitive(obj: unknown): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === \"string\") return obj;\n if (typeof obj !== \"object\") return obj;\n\n if (Array.isArray(obj)) {\n return obj.map(maskSensitive);\n }\n\n const masked: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (SENSITIVE_FIELDS.some((f) => key.toLowerCase().includes(f.toLowerCase()))) {\n masked[key] = \"***MASKED***\";\n } else if (typeof value === \"object\" && value !== null) {\n masked[key] = maskSensitive(value);\n } else {\n masked[key] = value;\n }\n }\n return masked;\n}\n\nfunction formatLog(level: LogLevel, message: string, data?: unknown): string {\n const timestamp = new Date().toISOString();\n const prefix = `[${timestamp}] [CLAWFIRE] [${level.toUpperCase()}]`;\n if (data !== undefined) {\n return `${prefix} ${message} ${JSON.stringify(maskSensitive(data))}`;\n }\n return `${prefix} ${message}`;\n}\n\nexport const logger = {\n debug(message: string, data?: unknown) {\n if (shouldLog(\"debug\")) console.debug(formatLog(\"debug\", message, data));\n },\n info(message: string, data?: unknown) {\n if (shouldLog(\"info\")) console.info(formatLog(\"info\", message, data));\n },\n warn(message: string, data?: unknown) {\n if (shouldLog(\"warn\")) console.warn(formatLog(\"warn\", message, data));\n },\n error(message: string, data?: unknown) {\n if (shouldLog(\"error\")) console.error(formatLog(\"error\", message, data));\n },\n};\n","/**\n * Clawfire Configuration\n */\n\nexport interface ClawfireConfig {\n /** Firebase 프로젝트 설정 */\n firebase: {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket?: string;\n messagingSenderId?: string;\n appId: string;\n measurementId?: string;\n };\n /** 서버 설정 */\n server?: {\n /** Firebase Functions 리전 */\n region?: string;\n /** CORS 허용 도메인 */\n cors?: string[];\n /** 전역 rate limit (초당 요청) */\n rateLimit?: number;\n };\n /** 보안 설정 */\n security?: {\n /** 입력 검증 활성화 (기본: true) */\n validateInput?: boolean;\n /** 출력 검증 활성화 (기본: false) */\n validateOutput?: boolean;\n /** 안전 헤더 자동 추가 (기본: true) */\n safeHeaders?: boolean;\n /** 로그 마스킹 활성화 (기본: true) */\n maskLogs?: boolean;\n };\n /** 플레이그라운드 설정 */\n playground?: {\n /** 활성화 여부 (기본: development에서만) */\n enabled?: boolean;\n /** 경로 */\n path?: string;\n };\n}\n\nconst defaultConfig: ClawfireConfig = {\n firebase: {\n apiKey: \"\",\n authDomain: \"\",\n projectId: \"\",\n appId: \"\",\n },\n server: {\n region: \"us-central1\",\n cors: [],\n rateLimit: 100,\n },\n security: {\n validateInput: true,\n validateOutput: false,\n safeHeaders: true,\n maskLogs: true,\n },\n playground: {\n enabled: undefined,\n path: \"/__playground\",\n },\n};\n\nlet _config: ClawfireConfig = defaultConfig;\n\nexport function configureClawfire(config: ClawfireConfig): ClawfireConfig {\n _config = {\n ...defaultConfig,\n ...config,\n firebase: { ...defaultConfig.firebase, ...config.firebase },\n server: { ...defaultConfig.server, ...config.server },\n security: { ...defaultConfig.security, ...config.security },\n playground: { ...defaultConfig.playground, ...config.playground },\n };\n return _config;\n}\n\nexport function getConfig(): ClawfireConfig {\n return _config;\n}\n"],"mappings":";AAYA,SAAS,KAAAA,UAAS;;;ACNlB,SAAS,SAAyD;AA2I3D,SAAS,UAGd,UAAsE;AACtE,SAAO;AACT;AAmBO,SAAS,YAAY,YAA8C;AACxE,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,GAAG;AAAA,EACL;AACF;AAKO,SAAS,gBAAgB,QAA0C;AACxE,SAAO,gBAAgB,MAAM;AAC/B;AAEA,SAAS,gBAAgB,QAA0C;AACjE,QAAM,MAAO,OAAe;AAE5B,MAAI,CAAC,IAAK,QAAO,EAAE,MAAM,UAAU;AAEnC,UAAQ,IAAI,UAAU;AAAA,IACpB,KAAK,aAAa;AAChB,YAAM,QAAS,OAAkC;AACjD,YAAM,aAAsC,CAAC;AAC7C,YAAM,WAAqB,CAAC;AAE5B,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,mBAAW,GAAG,IAAI,gBAAgB,KAAgB;AAClD,YAAI,CAAE,MAAc,aAAa,GAAG;AAClC,gBAAM,WAAY,MAAc;AAChC,cAAI,UAAU,aAAa,iBAAiB,UAAU,aAAa,cAAc;AAC/E,qBAAS,KAAK,GAAG;AAAA,UACnB;AAAA,QACF;AAAA,MACF;AAEA,aAAO,EAAE,MAAM,UAAU,YAAY,GAAI,SAAS,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC,EAAG;AAAA,IACpF;AAAA,IACA,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,GAAI,IAAI,QAAQ,SAAS,oBAAoB,IAAI,MAAM,IAAI,CAAC,EAAG;AAAA,IAC1F,KAAK;AACH,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B,KAAK;AACH,aAAO,EAAE,MAAM,UAAU;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,MAAM,SAAS,OAAO,gBAAgB,IAAI,IAAI,EAAE;AAAA,IAC3D,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,MAAM,IAAI,OAAO;AAAA,IAC5C,KAAK;AACH,aAAO,EAAE,GAAG,gBAAgB,IAAI,SAAS,GAAG,UAAU,KAAK;AAAA,IAC7D,KAAK;AACH,aAAO,EAAE,GAAG,gBAAgB,IAAI,SAAS,GAAG,SAAS,IAAI,aAAa,EAAE;AAAA,IAC1E,KAAK;AACH,aAAO,EAAE,GAAG,gBAAgB,IAAI,SAAS,GAAG,UAAU,KAAK;AAAA,IAC7D,KAAK;AACH,aAAO,EAAE,MAAM,OAAO,IAAI,OAAO,OAAO,IAAI,MAAM;AAAA,IACpD,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,QAAQ,IAAI,CAAC,MAAe,gBAAgB,CAAC,CAAC,EAAE;AAAA,IACtE,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,sBAAsB,gBAAgB,IAAI,SAAS,EAAE;AAAA,IAChF,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,QAAQ,YAAY;AAAA,IAC/C;AACE,aAAO,EAAE,MAAM,UAAU;AAAA,EAC7B;AACF;AAEA,SAAS,oBAAoB,QAA2E;AACtG,QAAM,SAAkC,CAAC;AACzC,aAAW,SAAS,QAAQ;AAC1B,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AAAO,eAAO,YAAY,MAAM;AAAO;AAAA,MAC5C,KAAK;AAAO,eAAO,YAAY,MAAM;AAAO;AAAA,MAC5C,KAAK;AAAS,eAAO,SAAS;AAAS;AAAA,MACvC,KAAK;AAAO,eAAO,SAAS;AAAO;AAAA,MACnC,KAAK;AAAQ,eAAO,SAAS;AAAQ;AAAA,IACvC;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,OAAgD;AAC/E,QAAM,QAAqB,CAAC;AAE5B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AACvD,QAAI,cAAuB,WAAW,KAAK;AAC3C,QAAI,CAAC,MAAM,UAAU;AACnB,oBAAc,YAAY,SAAS;AAAA,IACrC;AACA,UAAM,GAAG,IAAI;AAAA,EACf;AAEA,MAAI,MAAM,YAAY;AACpB,UAAM,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AACjD,UAAM,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACnD;AAEA,MAAI,MAAM,YAAY;AACpB,UAAM,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9D;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAEA,SAAS,WAAW,OAA4B;AAC9C,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,UAAI,MAAM,KAAM,QAAO,EAAE,KAAK,MAAM,IAA6B;AACjE,aAAO,EAAE,OAAO;AAAA,IAClB,KAAK;AACH,aAAO,EAAE,OAAO;AAAA,IAClB,KAAK;AACH,aAAO,EAAE,QAAQ;AAAA,IACnB,KAAK;AACH,aAAO,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,KAAK;AACH,UAAI,MAAM,MAAO,QAAO,EAAE,MAAM,WAAW,MAAM,KAAK,CAAC;AACvD,aAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;AAAA,IAC5B,KAAK;AACH,UAAI,MAAM,OAAQ,QAAO,EAAE,OAAO,EAAE,OAAO,GAAG,WAAW,MAAM,MAAM,CAAC;AACtE,aAAO,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,IACzC,KAAK;AACH,aAAO,EAAE,OAAO;AAAA;AAAA,IAClB,KAAK;AACH,aAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,CAAC;AAAA,IACjE;AACE,aAAO,EAAE,QAAQ;AAAA,EACrB;AACF;AAsBO,SAAS,mBACd,MACA,UACe;AACf,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR,MAAM,SAAS;AAAA,IACf,aAAa,gBAAgB,SAAS,KAAK;AAAA,IAC3C,cAAc,gBAAgB,SAAS,MAAM;AAAA,EAC/C;AACF;;;ACzTA,IAAM,kBAAqD;AAAA,EACzD,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,qBAAqB;AACvB;AAEO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAAyB,SAAiB,SAAmB;AACvE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa,gBAAgB,IAAI;AACtC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,SAAS;AACP,WAAO;AAAA,MACL,OAAO;AAAA,QACL,MAAM,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,SAAS;AAAA,EACpB,YAAY,CAAC,SAAiB,YAC5B,IAAI,cAAc,oBAAoB,SAAS,OAAO;AAAA,EAExD,cAAc,CAAC,UAAU,8BACvB,IAAI,cAAc,gBAAgB,OAAO;AAAA,EAE3C,WAAW,CAAC,UAAU,+BACpB,IAAI,cAAc,aAAa,OAAO;AAAA,EAExC,UAAU,CAAC,UAAU,yBACnB,IAAI,cAAc,aAAa,OAAO;AAAA,EAExC,UAAU,CAAC,YACT,IAAI,cAAc,YAAY,OAAO;AAAA,EAEvC,aAAa,CAAC,UAAU,wBACtB,IAAI,cAAc,gBAAgB,OAAO;AAAA,EAE3C,gBAAgB,CAAC,UAAU,iDACzB,IAAI,cAAc,mBAAmB,OAAO;AAAA,EAE9C,UAAU,CAAC,UAAU,4BACnB,IAAI,cAAc,kBAAkB,OAAO;AAAA,EAE7C,aAAa,CAAC,UAAU,sCACtB,IAAI,cAAc,uBAAuB,OAAO;AACpD;;;AC1EA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,IAAM,aAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAI,eAAyB;AAEtB,SAAS,YAAY,OAAiB;AAC3C,iBAAe;AACjB;AAEA,SAAS,UAAU,OAA0B;AAC3C,SAAO,WAAW,KAAK,KAAK,WAAW,YAAY;AACrD;AAGO,SAAS,cAAc,KAAuB;AACnD,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,MAAI,OAAO,QAAQ,SAAU,QAAO;AAEpC,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,aAAa;AAAA,EAC9B;AAEA,QAAM,SAAkC,CAAC;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAA8B,GAAG;AACzE,QAAI,iBAAiB,KAAK,CAAC,MAAM,IAAI,YAAY,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,GAAG;AAC7E,aAAO,GAAG,IAAI;AAAA,IAChB,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AACtD,aAAO,GAAG,IAAI,cAAc,KAAK;AAAA,IACnC,OAAO;AACL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,UAAU,OAAiB,SAAiB,MAAwB;AAC3E,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,SAAS,IAAI,SAAS,iBAAiB,MAAM,YAAY,CAAC;AAChE,MAAI,SAAS,QAAW;AACtB,WAAO,GAAG,MAAM,IAAI,OAAO,IAAI,KAAK,UAAU,cAAc,IAAI,CAAC,CAAC;AAAA,EACpE;AACA,SAAO,GAAG,MAAM,IAAI,OAAO;AAC7B;AAEO,IAAM,SAAS;AAAA,EACpB,MAAM,SAAiB,MAAgB;AACrC,QAAI,UAAU,OAAO,EAAG,SAAQ,MAAM,UAAU,SAAS,SAAS,IAAI,CAAC;AAAA,EACzE;AAAA,EACA,KAAK,SAAiB,MAAgB;AACpC,QAAI,UAAU,MAAM,EAAG,SAAQ,KAAK,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,EACtE;AAAA,EACA,KAAK,SAAiB,MAAgB;AACpC,QAAI,UAAU,MAAM,EAAG,SAAQ,KAAK,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,EACtE;AAAA,EACA,MAAM,SAAiB,MAAgB;AACrC,QAAI,UAAU,OAAO,EAAG,SAAQ,MAAM,UAAU,SAAS,SAAS,IAAI,CAAC;AAAA,EACzE;AACF;;;AC3CA,IAAM,gBAAgC;AAAA,EACpC,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,OAAO;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,WAAW;AAAA,EACb;AAAA,EACA,UAAU;AAAA,IACR,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,EACR;AACF;AAEA,IAAI,UAA0B;AAEvB,SAAS,kBAAkB,QAAwC;AACxE,YAAU;AAAA,IACR,GAAG;AAAA,IACH,GAAG;AAAA,IACH,UAAU,EAAE,GAAG,cAAc,UAAU,GAAG,OAAO,SAAS;AAAA,IAC1D,QAAQ,EAAE,GAAG,cAAc,QAAQ,GAAG,OAAO,OAAO;AAAA,IACpD,UAAU,EAAE,GAAG,cAAc,UAAU,GAAG,OAAO,SAAS;AAAA,IAC1D,YAAY,EAAE,GAAG,cAAc,YAAY,GAAG,OAAO,WAAW;AAAA,EAClE;AACA,SAAO;AACT;AAEO,SAAS,YAA4B;AAC1C,SAAO;AACT;","names":["z"]}
|