@happyvertical/smrt-core 0.40.42 → 0.40.44
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/AGENTS.md +12 -1
- package/README.md +24 -0
- package/agents/generators.md +3 -1
- package/dist/decorators/index.d.ts +12 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +1 -1
- package/dist/generators/mcp.js.map +1 -1
- package/dist/generators/tool-schema.d.ts.map +1 -1
- package/dist/generators/tool-schema.js +1 -1
- package/dist/generators/tool-schema.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +2 -2
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
- package/dist/manifest/test-manifest-stub.js +100 -2
- package/dist/manifest/test-manifest-stub.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/prebuild/index.d.ts.map +1 -1
- package/dist/prebuild/index.js +23 -1
- package/dist/prebuild/index.js.map +1 -1
- package/dist/registry/types.d.ts +6 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/scanner/types.d.ts +30 -0
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +8 -8
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +16 -4
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +64 -35
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/web-collections.d.ts +22 -2
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +37 -3
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sveltekit-generator.js","names":[],"sources":["../../src/vite-plugin/sveltekit-generator.ts"],"sourcesContent":["/**\n * SvelteKit route auto-generation from SMRT objects\n * Generates real TypeScript files in src/routes/api/ based on discovered SMRT objects\n */\n\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { DomainKnowledgeConfig } from '@happyvertical/smrt-types';\nimport { generateConditionalGetRouteHelper } from '../generators/conditional-get.js';\nimport type {\n ApiConfig,\n ApiCustomRouteConfig,\n ApiHttpMethod,\n ApiSerializerReference,\n} from '../registry/types.js';\nimport type {\n MethodDefinition,\n SmartObjectDefinition,\n SmartObjectManifest,\n} from '../scanner/types';\nimport { generateChangesRoute } from './changes-route.js';\nimport { generateEventsRoute } from './events-route.js';\nimport { AUTO_GENERATED_ROUTE_HEADER } from './route-header.js';\nimport { generateSyncApplyRoute } from './sync-apply-route.js';\nimport {\n computeWebManifestHash,\n isCollectionManifestClass,\n resolveCollectionItemObject,\n resolveCollectionItemTypeName,\n} from './web-collections.js';\n\nexport interface SvelteKitOptions {\n enabled: boolean;\n routesDir: string;\n objectsDir: string;\n configPath?: string; // default: 'src/lib/server'\n configFileName?: string; // default: 'smrt.ts'\n /**\n * Apply kebab-case to custom-method URL segments (e.g. `discoverFromUrl`\n * becomes `/discover-from-url`). Opt-in for one minor; default flips in the\n * next major. An explicit `api.routes[name].path` always wins over this.\n */\n kebabRoutes?: boolean;\n /** Domain knowledge API route generation. Disabled unless explicitly enabled. */\n knowledge?: DomainKnowledgeConfig;\n /**\n * Change-feed `_changes` route generation (#1758). Enabled by default\n * (the route is auth-guarded fail-closed); set `enabled: false` to skip.\n */\n changesRoute?: { enabled?: boolean };\n /**\n * Live `_events` SSE route generation (#1763). Enabled by default (the route\n * is auth-guarded fail-closed, same-origin only); set `enabled: false` to\n * skip. `maxSubscribers` caps active streams per process (#1860); 0 means\n * unlimited.\n *\n * Cross-origin subscription (#1861) is opt-in and fail-closed: provide\n * `allowedOrigins` (an explicit allowlist — never `*`) and set\n * `allowCredentials: true` so an allow-listed browser client can subscribe\n * with a credentialed `EventSource` (`withCredentials: true`). The generated\n * route echoes the specific request `Origin` only when it is allow-listed,\n * adds `Access-Control-Allow-Credentials: true`, and otherwise stays\n * same-origin only. Auth (an authenticated principal on `locals`) is\n * unchanged — CORS only lets the cookie reach the guard.\n */\n eventsRoute?: {\n enabled?: boolean;\n maxSubscribers?: number;\n allowedOrigins?: string[];\n allowCredentials?: boolean;\n };\n}\n\n// Keep this aligned with biome.json formatter.lineWidth.\nconst BIOME_LINE_WIDTH = 80;\nconst STANDARD_API_ACTIONS = ['list', 'get', 'create', 'update', 'delete'];\n\nexport interface ResolvedApiActionRouteConfig {\n scope: 'item' | 'collection';\n method: ApiHttpMethod;\n pathSegments: string[];\n pathParamNames: string[];\n}\n\ninterface GeneratedActionRouteSpec {\n lookupClassName: string;\n lookupObjectDef?: SmartObjectDefinition;\n hostClassName: string;\n actionName: string;\n actionDef: MethodDefinition;\n routeConfig: ResolvedApiActionRouteConfig;\n hostType: 'item' | 'collection';\n}\n\ninterface ResolvedStandardRouteSerializers {\n importStatements: string[];\n itemSerializerName?: string;\n listItemSerializerName?: string;\n}\n\ninterface GeneratedTypeReference {\n typeName: string;\n importStatement?: string;\n}\n\n/**\n * Extract simple class name from potentially qualified name.\n * Qualified names follow the pattern: @scope/package:ClassName\n *\n * Issue #870: Manifests may contain qualified names which generate invalid\n * import statements like: import { @pkg:Class } from '@pkg'\n *\n * @param qualifiedName - Class name that may be qualified (e.g., \"@pkg:Class\" or \"Class\")\n * @returns Simple class name (e.g., \"Class\")\n */\nfunction extractSimpleClassName(qualifiedName: string): string {\n const colonIndex = qualifiedName.indexOf(':');\n if (colonIndex !== -1) {\n return qualifiedName.substring(colonIndex + 1);\n }\n return qualifiedName;\n}\n\n/**\n * Check if an object definition represents a SmrtCollection class.\n * Collection classes share route paths with their item class (via inherited `collection` field),\n * so generating routes for both would cause file collisions.\n */\nfunction isCollectionClass(objectDef: SmartObjectDefinition): boolean {\n return objectDef.extends === 'SmrtCollection' || !!objectDef.extendsTypeArg;\n}\n\nfunction getRegistrationPackageName(\n manifest: SmartObjectManifest,\n objectDef: SmartObjectDefinition,\n isLocal: boolean,\n): string | undefined {\n if (isLocal) {\n return manifest.packageName ?? objectDef.packageName;\n }\n\n return objectDef.packageName;\n}\n\nfunction toSingleQuotedStringLiteral(value: string): string {\n const jsonLiteral = JSON.stringify(value);\n const jsonContent = jsonLiteral.slice(1, -1).replaceAll(\"'\", \"\\\\'\");\n return `'${jsonContent}'`;\n}\n\nfunction getApiConfigObject(apiConfig: unknown): ApiConfig | null {\n if (!apiConfig || typeof apiConfig !== 'object') {\n return null;\n }\n\n return apiConfig as ApiConfig;\n}\n\nfunction isSameSerializerReference(\n left?: ApiSerializerReference,\n right?: ApiSerializerReference,\n): boolean {\n return (\n !!left &&\n !!right &&\n left.importPath === right.importPath &&\n left.exportName === right.exportName\n );\n}\n\nfunction resolveStandardRouteSerializers(\n apiConfig: unknown,\n): ResolvedStandardRouteSerializers {\n const config = getApiConfigObject(apiConfig);\n const itemSerializer = config?.serializers?.item;\n const listItemSerializer =\n config?.serializers?.listItem || config?.serializers?.item;\n\n const importStatements: string[] = [];\n let itemSerializerName: string | undefined;\n let listItemSerializerName: string | undefined;\n\n if (itemSerializer) {\n itemSerializerName = 'serializeItemResponse';\n importStatements.push(\n `import { ${itemSerializer.exportName} as ${itemSerializerName} } from '${itemSerializer.importPath}';`,\n );\n }\n\n if (listItemSerializer) {\n if (isSameSerializerReference(listItemSerializer, itemSerializer)) {\n listItemSerializerName = itemSerializerName;\n } else {\n listItemSerializerName = 'serializeListItemResponse';\n importStatements.push(\n `import { ${listItemSerializer.exportName} as ${listItemSerializerName} } from '${listItemSerializer.importPath}';`,\n );\n }\n }\n\n return {\n importStatements,\n itemSerializerName,\n listItemSerializerName,\n };\n}\n\nfunction isValidTypeIdentifier(name: string): boolean {\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);\n}\n\nfunction resolveObjectTypeReference(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition | undefined,\n options: SvelteKitOptions,\n routeDir?: string,\n): GeneratedTypeReference {\n const simpleClassName = extractSimpleClassName(className);\n\n if (!objectDef || !isValidTypeIdentifier(simpleClassName)) {\n return { typeName: \"import('@happyvertical/smrt-core').SmrtObject\" };\n }\n\n const importPath =\n isLocalObject(projectRoot, objectDef) || !objectDef.packageName\n ? getRouteTypeImportPath(\n projectRoot,\n objectDef,\n options,\n simpleClassName,\n routeDir,\n )\n : objectDef.packageName;\n\n return {\n typeName: simpleClassName,\n importStatement: `import type { ${simpleClassName} } from '${importPath}';`,\n };\n}\n\nfunction getRouteTypeImportPath(\n projectRoot: string,\n objectDef: SmartObjectDefinition,\n options: SvelteKitOptions,\n simpleClassName: string,\n routeDir?: string,\n): string {\n if (objectDef.filePath) {\n const srcLibDir = join(projectRoot, 'src/lib');\n if (objectDef.filePath.startsWith(`${srcLibDir}/`)) {\n const libRelative = relative(srcLibDir, objectDef.filePath)\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|js|tsx|jsx)$/, '');\n return `$lib/${libRelative}`.replace(/\\/+/g, '/');\n }\n\n if (routeDir) {\n const routeRelative = relative(routeDir, objectDef.filePath)\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|js|tsx|jsx)$/, '');\n return routeRelative.startsWith('.')\n ? routeRelative\n : `./${routeRelative}`;\n }\n }\n\n return getSvelteKitImportPath(\n projectRoot,\n objectDef.filePath,\n options.objectsDir,\n simpleClassName,\n );\n}\n\nfunction mergeImportStatements(\n importStatements: Array<string | undefined>,\n): string {\n return Array.from(new Set(importStatements.filter(Boolean))).join('\\n');\n}\n\nfunction findObjectDefByRegistryKey(\n manifest: SmartObjectManifest,\n className: string,\n): SmartObjectDefinition | undefined {\n return (\n manifest.objects[className] ??\n Object.values(manifest.objects).find(\n (objectDef) => objectDef.className === extractSimpleClassName(className),\n )\n );\n}\n\n/**\n * Collect property names marked `@field({ readonly: true })` for an object, so\n * the generated write surfaces can strip them from request bodies (#1540, 2b).\n */\nfunction collectReadonlyFieldNames(objectDef: SmartObjectDefinition): string[] {\n const fields = objectDef.fields || {};\n const names: string[] = [];\n for (const [name, def] of Object.entries(fields)) {\n const meta = (def as { _meta?: Record<string, unknown> })._meta;\n if (\n (def as { readonly?: boolean }).readonly === true ||\n meta?.readonly === true\n ) {\n names.push(name);\n }\n }\n return names;\n}\n\n/**\n * Resolve the optional `@smrt({ api: { writable: [...] } })` allowlist. When\n * present, only these fields may be set from a create/update request body.\n */\nfunction getApiWritableAllowlist(apiConfig: unknown): string[] | null {\n const config = getApiConfigObject(apiConfig);\n return Array.isArray(config?.writable) ? config.writable : null;\n}\n\n/**\n * Emit the mass-assignment guard helper injected into generated route files\n * (#1540, 2b). It strips framework/server-managed (`id`, `tenantId`,\n * timestamps, `_`-prefixed) and `@field({ readonly: true })` fields from\n * request bodies, and — when a `writable` allowlist is configured — intersects\n * with it. Applied to every generated `create`/`update` handler.\n */\nfunction generateWritablePolicyHelper(\n objectDef: SmartObjectDefinition,\n): string {\n const readonly = collectReadonlyFieldNames(objectDef);\n const writable = getApiWritableAllowlist(objectDef.decoratorConfig?.api);\n\n return `\n// Mass-assignment guard (#1540): strip framework/server-managed + read-only\n// fields from create/update request bodies before they reach the model.\nconst WRITABLE_ALLOWLIST: string[] | null = ${\n writable ? JSON.stringify(writable) : 'null'\n };\nconst READONLY_FIELDS: string[] = ${JSON.stringify(readonly)};\nconst SERVER_MANAGED_FIELDS = [\n 'id',\n 'tenantId',\n 'tenant_id',\n 'createdAt',\n 'created_at',\n 'updatedAt',\n 'updated_at',\n];\n\nfunction applyWritablePolicy(data: unknown): Record<string, unknown> {\n if (!data || typeof data !== 'object') return {};\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n if (key.startsWith('_')) continue;\n if (SERVER_MANAGED_FIELDS.includes(key)) continue;\n if (READONLY_FIELDS.includes(key)) continue;\n if (WRITABLE_ALLOWLIST && !WRITABLE_ALLOWLIST.includes(key)) continue;\n result[key] = value;\n }\n return result;\n}\n`;\n}\n\n/**\n * Resolve the `@smrt({ api: { public } })` posture (fail-closed default).\n * - `false`/unset → all routes require an authenticated principal.\n * - `true` → all routes are public.\n * - `'read'` → reads are public, mutations still require auth.\n */\nfunction getApiPublicAccess(apiConfig: unknown): boolean | 'read' {\n const config = getApiConfigObject(apiConfig);\n const value = config?.public;\n if (value === true || value === 'read') {\n return value;\n }\n return false;\n}\n\nfunction toFieldColumnAlias(fieldName: string): string {\n return fieldName\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n .replace(/([a-z\\d])([A-Z])/g, '$1_$2')\n .replace(/[-\\s]+/g, '_')\n .toLowerCase();\n}\n\nfunction manifestObjectKey(objectDef: SmartObjectDefinition): string {\n return objectDef.qualifiedName ?? objectDef.className;\n}\n\nfunction manifestObjectExtends(\n objectDef: SmartObjectDefinition,\n parent: SmartObjectDefinition,\n): boolean {\n const extendsQualified = objectDef.extendsQualified;\n const extendsName = objectDef.extends;\n return (\n (typeof extendsQualified === 'string' &&\n extendsQualified === parent.qualifiedName) ||\n (typeof extendsName === 'string' &&\n (extendsName === parent.className ||\n extendsName === parent.qualifiedName))\n );\n}\n\nfunction findManifestParent(\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n): SmartObjectDefinition | undefined {\n return Object.values(manifest.objects).find((candidate) =>\n manifestObjectExtends(objectDef, candidate),\n );\n}\n\nfunction collectStiHierarchyObjects(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): SmartObjectDefinition[] {\n if (!manifest) return [objectDef];\n\n let cursor: SmartObjectDefinition | undefined = objectDef;\n let stiBase: SmartObjectDefinition | undefined =\n cursor.decoratorConfig?.tableStrategy === 'sti' ? cursor : undefined;\n while (cursor) {\n const parent = findManifestParent(cursor, manifest);\n if (!parent) break;\n if (parent.decoratorConfig?.tableStrategy === 'sti') {\n stiBase = parent;\n }\n cursor = parent;\n }\n\n if (!stiBase) return [objectDef];\n\n const members = new Map<string, SmartObjectDefinition>();\n const visit = (current: SmartObjectDefinition) => {\n const key = manifestObjectKey(current);\n if (members.has(key)) return;\n members.set(key, current);\n for (const candidate of Object.values(manifest.objects)) {\n if (candidate === current) continue;\n if (manifestObjectExtends(candidate, current)) {\n visit(candidate);\n }\n }\n };\n\n visit(stiBase);\n return [...members.values()];\n}\n\nfunction collectReadPermissionFields(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): Array<[string, string]> {\n const result: Array<[string, string]> = [];\n const seen = new Set<string>();\n for (const member of collectStiHierarchyObjects(objectDef, manifest)) {\n for (const [name, def] of Object.entries(member.fields ?? {})) {\n const permission =\n typeof def.readPermission === 'string'\n ? def.readPermission\n : typeof def._meta?.readPermission === 'string'\n ? def._meta.readPermission\n : undefined;\n if (!permission) continue;\n for (const fieldName of [name, toFieldColumnAlias(name)]) {\n const key = `${fieldName}\\0${permission}`;\n if (seen.has(key)) continue;\n seen.add(key);\n result.push([fieldName, permission]);\n }\n }\n }\n return result;\n}\n\n/**\n * Emit the fail-closed authorization guard injected into generated route files\n * (#1540, 2c). Every generated CRUD/action handler calls `requireRouteAuth`,\n * which throws 401 unless the route is `public` or `locals` carries an\n * authenticated principal. Mirrors the knowledge-route `isKnowledgeAdmin`\n * precedent. Mutating verbs require auth even when reads are public.\n */\nfunction generateAuthGuardHelper(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): string {\n const publicAccess = getApiPublicAccess(objectDef.decoratorConfig?.api);\n const readPermissionFields = collectReadPermissionFields(objectDef, manifest);\n\n return `\n// Fail-closed authorization (#1540): generated routes require an authenticated\n// principal on \\`locals\\` unless explicitly marked \\`@smrt({ api: { public } })\\`.\nconst PUBLIC_ACCESS: boolean | 'read' = ${JSON.stringify(publicAccess)};\nconst READ_PERMISSION_FIELDS: Array<[string, string]> = ${JSON.stringify(readPermissionFields)};\n\ninterface PublicJsonOptions {\n permissions?: Iterable<string>;\n}\n\nfunction hasAuthenticatedPrincipal(locals: unknown): boolean {\n if (!locals || typeof locals !== 'object') return false;\n const l = locals as Record<string, unknown>;\n // Only a resolved, object-shaped principal counts. We intentionally do NOT\n // treat \\`locals.auth\\` as a signal: Auth.js/SvelteKit put a callable\n // \\`auth()\\` helper on every request (including anonymous ones), so honoring\n // it would fail OPEN. Booleans don't count either (no convention sets\n // \\`locals.user = true\\`); the only boolean accepted is the explicit\n // \\`smrtAuth\\` opt-in marker.\n const isResolvedPrincipal = (v: unknown) =>\n typeof v === 'object' && v !== null;\n return (\n isResolvedPrincipal(l.user) ||\n isResolvedPrincipal(l.session) ||\n l.smrtAuth === true\n );\n}\n\nfunction requireRouteAuth(locals: unknown, mutating: boolean): void {\n if (PUBLIC_ACCESS === true) return;\n if (PUBLIC_ACCESS === 'read' && !mutating) return;\n if (!hasAuthenticatedPrincipal(locals)) {\n throw error(401, 'Authentication required');\n }\n}\n\nfunction getPublicJsonOptions(locals: unknown): PublicJsonOptions {\n const l = readJsonRecord(locals);\n const permissions = l.permissions ?? l.permissionSet ?? l.smrtPermissions;\n if (Array.isArray(permissions) || permissions instanceof Set) {\n return { permissions };\n }\n return {};\n}\n\nfunction hasReadPermission(\n options: PublicJsonOptions,\n permission: string,\n): boolean {\n if (!options.permissions) return false;\n for (const granted of options.permissions) {\n if (granted === permission) return true;\n }\n return false;\n}\n\nfunction applyReadPermissionRedaction(\n value: unknown,\n options: PublicJsonOptions,\n seen: WeakSet<object> = new WeakSet(),\n): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (seen.has(value)) return null;\n if (hasPublicJson(value)) {\n seen.add(value);\n return applyReadPermissionRedaction(\n value.toPublicJSON(options),\n options,\n seen,\n );\n }\n if (Array.isArray(value)) {\n seen.add(value);\n return value.map((entry) =>\n applyReadPermissionRedaction(entry, options, seen),\n );\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n seen.add(value);\n const out = { ...(value as Record<string, unknown>) };\n const metaData =\n out._meta_data && typeof out._meta_data === 'object'\n ? { ...(out._meta_data as Record<string, unknown>) }\n : null;\n for (const [fieldName, permission] of READ_PERMISSION_FIELDS) {\n if (hasReadPermission(options, permission)) continue;\n delete out[fieldName];\n if (metaData) delete metaData[fieldName];\n }\n if (metaData) out._meta_data = metaData;\n for (const [key, entry] of Object.entries(out)) {\n out[key] = applyReadPermissionRedaction(entry, options, seen);\n }\n return out;\n}\n\n// Sensitive-field-safe serialization for custom-action results (#1540): a\n// custom method may return a SmrtObject (or one nested in an array/plain\n// object), so recurse and route each through toPublicJSON() rather than letting\n// JSON.stringify call toJSON(). Non-plain instances (Date, etc.) and primitives\n// pass through; a cycle guard prevents infinite loops.\ninterface PublicJsonSource {\n toPublicJSON(options?: PublicJsonOptions): unknown;\n}\n\nfunction hasPublicJson(value: object): value is PublicJsonSource {\n return (\n 'toPublicJSON' in value &&\n typeof (value as { toPublicJSON?: unknown }).toPublicJSON === 'function'\n );\n}\n\nfunction readJsonRecord(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return {};\n return value as Record<string, unknown>;\n}\n\nfunction toPublicResult(\n value: unknown,\n options: PublicJsonOptions,\n seen: WeakSet<object> = new WeakSet(),\n): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (seen.has(value)) return null;\n if (hasPublicJson(value)) {\n seen.add(value);\n return toPublicResult(value.toPublicJSON(options), options, seen);\n }\n if (Array.isArray(value)) {\n seen.add(value);\n return value.map((entry) => toPublicResult(entry, options, seen));\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(\n value as Record<string, unknown>,\n )) {\n out[key] = toPublicResult(entry, options, seen);\n }\n return out;\n}\n`;\n}\n\n/**\n * Whether an object is `@TenantScoped` (carries a `tenantScoped` config in its\n * `@smrt()`/`@TenantScoped()` decorator). Drives tenant-context establishment in\n * generated routes (#1540, facet 2).\n */\nfunction isTenantScoped(objectDef: SmartObjectDefinition): boolean {\n return !!objectDef.decoratorConfig?.tenantScoped;\n}\n\n/**\n * Emit the tenant-context establishment + fail-closed read-scope helpers for\n * `@TenantScoped` objects (#1540 facet 2; #1782). Generated handlers call\n * `establishTenantContext(locals)` after the auth guard so every query runs\n * inside the tenant context derived from the authenticated principal — engaging\n * the `@TenantScoped` interceptors that auto-filter by tenant.\n *\n * `establishTenantContext` reads the canonical `locals.tenantId` set by the\n * smrt-users auth hook (with `user`/`session` fallbacks). It is a no-op when a\n * context is already active (e.g. an upstream tenancy handle) or when the\n * request carries no tenant (anonymous / global principal).\n *\n * When no context could be established, an `optional`-mode read would otherwise\n * fall through the interceptor UNFILTERED and return rows from ALL tenants (the\n * interceptor only hard-fails `required` mode) — the #1782 leak. So read\n * handlers additionally call `tenantReadScope()`, which restricts reads to\n * NULL-tenant (global) rows only when tenancy is enabled but no context is\n * active. Both helpers are imported only for tenant-scoped routes, which already\n * depend on `@happyvertical/smrt-tenancy`.\n */\nfunction generateTenantContextHelper(): string {\n return `\nimport { enterTenantContext, getCurrentTenant, hasTenantContext, isSuperAdminBypass, isTenancyEnabled } from '@happyvertical/smrt-tenancy';\n\nfunction establishTenantContext(locals: unknown): void {\n if (hasTenantContext()) return;\n if (!locals || typeof locals !== 'object') return;\n const l = locals as Record<string, unknown>;\n const user = l.user as Record<string, unknown> | undefined;\n const session = l.session as Record<string, unknown> | undefined;\n const tenantId = l.tenantId ?? user?.tenantId ?? session?.tenantId;\n if (typeof tenantId === 'string' && tenantId) {\n enterTenantContext({ tenantId });\n }\n}\n\n// Fail-closed read scope (#1782): a public/anonymous read on a @TenantScoped\n// model has no tenant context, so the tenancy interceptor (optional mode) would\n// pass the query through UNFILTERED and return every tenant's rows. When tenancy\n// is enabled but no context was established, restrict reads to NULL-tenant\n// (global) rows only — mirroring the dispatch resolver + _changes convention:\n// tenancy enforced with no context => global rows only. Returns undefined when a\n// context is active (the interceptor filters by it) or tenancy is disabled.\nfunction tenantReadScope(): { tenantId: null } | undefined {\n return isTenancyEnabled() && !hasTenantContext()\n ? { tenantId: null }\n : undefined;\n}\n\n// Custom GET actions that accept a single options object may perform their\n// own reads instead of calling generated list/get handlers. Give those actions\n// an explicit tenant scope: active tenant when present, NULL-tenant global rows\n// when tenancy is enabled but no tenant context exists. Super-admin bypass\n// stays unscoped so admin callers keep deliberate cross-tenant access.\nfunction tenantReadOptionsScope(): { tenantId: string | null } | undefined {\n if (!isTenancyEnabled() || isSuperAdminBypass()) {\n return undefined;\n }\n return { tenantId: getCurrentTenant()?.tenantId ?? null };\n}\n`;\n}\n\n/**\n * The per-handler guard preamble: the fail-closed auth check (#1540 2c) plus, for\n * tenant-scoped objects, tenant-context establishment (#1540 facet 2).\n */\nfunction routeGuardPreamble(\n objectDef: SmartObjectDefinition,\n mutating: boolean,\n): string {\n const lines = [` requireRouteAuth(locals, ${mutating});`];\n if (isTenantScoped(objectDef)) {\n lines.push(' establishTenantContext(locals);');\n }\n return lines.join('\\n');\n}\n\nfunction normalizeApiHttpMethod(method?: string): ApiHttpMethod {\n switch (method?.toUpperCase()) {\n case 'GET':\n case 'POST':\n case 'PUT':\n case 'PATCH':\n case 'DELETE':\n return method.toUpperCase() as ApiHttpMethod;\n default:\n return 'POST';\n }\n}\n\n/**\n * Convert a camelCase / PascalCase method name to a kebab-case URL segment.\n * Examples:\n * discoverFromUrl -> discover-from-url\n * XMLExport -> xml-export\n * URL -> url\n * a1B2c3 -> a1-b2c3\n *\n * Known limitation: consecutive uppercase acronyms aren't split apart, e.g.\n * `HTMLAPIExport` becomes `htmlapi-export` rather than `html-api-export`.\n * Override with an explicit `api.routes[name].path` if you need a specific\n * segmentation.\n */\nexport function methodNameToKebab(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .toLowerCase();\n}\n\nfunction normalizeCustomRoutePath(\n actionName: string,\n path?: string,\n options: { kebabRoutes?: boolean } = {},\n): string[] {\n // Explicit path override always wins, and is used verbatim (split on /).\n if (path) {\n const segments = path\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean);\n if (segments.length > 0) {\n return segments;\n }\n }\n\n const segment = options.kebabRoutes\n ? methodNameToKebab(actionName)\n : actionName;\n return [segment];\n}\n\nfunction extractRoutePathParamNames(pathSegments: string[]): string[] {\n return pathSegments\n .filter((segment) => /^\\[[^\\]]+\\]$/.test(segment))\n .map((segment) => segment.slice(1, -1))\n .filter(Boolean);\n}\n\nexport function resolveApiActionRouteConfig(\n actionName: string,\n actionDef: { isStatic?: boolean },\n apiConfig: unknown,\n routeOptions: { kebabRoutes?: boolean } = {},\n defaultScope: 'item' | 'collection' = actionDef.isStatic\n ? 'collection'\n : 'item',\n): ResolvedApiActionRouteConfig {\n const config = getApiConfigObject(apiConfig);\n const routeConfig: ApiCustomRouteConfig | undefined =\n config?.routes?.[actionName];\n\n const pathSegments = normalizeCustomRoutePath(\n actionName,\n routeConfig?.path,\n routeOptions,\n );\n\n return {\n scope: routeConfig?.scope || defaultScope,\n method: normalizeApiHttpMethod(routeConfig?.method),\n pathSegments,\n pathParamNames: extractRoutePathParamNames(pathSegments),\n };\n}\n\nfunction buildRouteHandlerArgs(\n includeParams: boolean,\n includeRequest: boolean,\n): string {\n // `locals` is always destructured so the fail-closed auth guard (#1540) can\n // inspect the authenticated principal.\n const args: string[] = ['locals'];\n\n if (includeParams) {\n args.push('params');\n }\n\n if (includeRequest) {\n args.push('request');\n }\n\n return `{ ${args.join(', ')} }`;\n}\n\nfunction buildPathParamsObjectLiteral(pathParamNames: string[]): string {\n if (pathParamNames.length === 0) {\n return '{}';\n }\n\n return `{\n${pathParamNames\n .map(\n (paramName) =>\n ` ${buildObjectTypeProperty(paramName)}: ${buildRouteParamAccess(paramName)},`,\n )\n .join('\\n')}\n }`;\n}\n\nfunction hasSingleOptionsParameter(actionDef: MethodDefinition): boolean {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n\n return parameters.length === 1 && parameters[0]?.name === 'options';\n}\n\nfunction buildActionInvocationArgs(\n actionDef: MethodDefinition,\n optionsIdentifier = 'options',\n): string[] {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n\n if (parameters.length === 0) {\n return [];\n }\n\n if (hasSingleOptionsParameter(actionDef)) {\n return [optionsIdentifier];\n }\n\n return parameters.map((parameter) =>\n buildOptionsPropertyAccess(parameter.name),\n );\n}\n\nfunction buildObjectTypeProperty(propertyName: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyName)) {\n return propertyName;\n }\n\n return JSON.stringify(propertyName);\n}\n\nfunction buildSingleQuotedStringLiteral(value: string): string {\n return `'${value.replaceAll('\\\\', '\\\\\\\\').replaceAll(\"'\", \"\\\\'\")}'`;\n}\n\nfunction buildRouteParamAccess(paramName: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(paramName)) {\n return `params.${paramName}`;\n }\n\n return `params[${buildSingleQuotedStringLiteral(paramName)}]`;\n}\n\nfunction buildActionTargetTypeExpression(\n targetTypeName: string,\n isStatic: boolean,\n): string {\n return isStatic ? `(typeof ${targetTypeName})` : targetTypeName;\n}\n\nfunction buildActionArgsTypeAlias(\n actionName: string,\n targetTypeName: string,\n isStatic: boolean,\n): string {\n const targetTypeExpression = buildActionTargetTypeExpression(\n targetTypeName,\n isStatic,\n );\n return ` type ActionArgs = Parameters<${targetTypeExpression}[${buildSingleQuotedStringLiteral(actionName)}]>;`;\n}\n\nfunction buildActionOptionsTypeAlias(actionDef: MethodDefinition): string {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n\n if (parameters.length <= 1 && parameters[0]?.name === 'options') {\n return '';\n }\n\n return [\n ' type ActionOptions = {',\n ...parameters.map(\n (parameter, index) =>\n ` ${buildObjectTypeProperty(parameter.name)}: ActionArgs[${index}];`,\n ),\n ' };',\n ].join('\\n');\n}\n\nfunction buildActionOptionsLoad(\n actionName: string,\n actionDef: MethodDefinition,\n routeConfig: ResolvedApiActionRouteConfig,\n targetTypeName: string,\n isStatic: boolean,\n): string {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n if (parameters.length === 0) {\n return '';\n }\n\n const hasPathParams = routeConfig.pathParamNames.length > 0;\n const pathParamsObjectLiteral = buildPathParamsObjectLiteral(\n routeConfig.pathParamNames,\n );\n const isSingleOptionsParameter =\n parameters.length === 1 && parameters[0]?.name === 'options';\n const lines = [\n buildActionArgsTypeAlias(actionName, targetTypeName, isStatic),\n ];\n const optionsTypeAlias = buildActionOptionsTypeAlias(actionDef);\n if (optionsTypeAlias) {\n lines.push(optionsTypeAlias);\n }\n\n if (routeConfig.method === 'GET') {\n if (hasPathParams) {\n lines.push(\n ` const pathParams = ${pathParamsObjectLiteral};`,\n ' const options = {',\n ' ...Object.fromEntries(new URL(request.url).searchParams.entries()),',\n ' ...pathParams,',\n ` } as ${isSingleOptionsParameter ? 'ActionArgs[0]' : 'ActionOptions'};`,\n '',\n );\n } else {\n lines.push(\n ' const options = Object.fromEntries(',\n ' new URL(request.url).searchParams.entries(),',\n ` ) as ${isSingleOptionsParameter ? 'ActionArgs[0]' : 'ActionOptions'};`,\n '',\n );\n }\n return lines.join('\\n');\n }\n\n if (hasPathParams) {\n lines.push(\n ` const pathParams = ${pathParamsObjectLiteral};`,\n ' const body: unknown = await request.json();',\n ' const options = {',\n ' ...readJsonRecord(body),',\n ' ...pathParams,',\n ` } as ${isSingleOptionsParameter ? 'ActionArgs[0]' : 'ActionOptions'};`,\n '',\n );\n return lines.join('\\n');\n }\n\n lines.push(' const body: unknown = await request.json();');\n if (isSingleOptionsParameter) {\n lines.push(' const options = body as ActionArgs[0];', '');\n } else {\n lines.push(' const options = readJsonRecord(body) as ActionOptions;', '');\n }\n return lines.join('\\n');\n}\n\nfunction buildOptionsPropertyAccess(propertyName: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyName)) {\n return `options.${propertyName}`;\n }\n\n const escapedPropertyName = propertyName\n .replaceAll('\\\\', '\\\\\\\\')\n .replaceAll(\"'\", \"\\\\'\");\n\n return `options['${escapedPropertyName}']`;\n}\n\nfunction buildActionInvocationExpression(\n receiverExpression: string,\n actionName: string,\n invocationArgs: string[],\n): string {\n const singleLineInvocation = `await ${receiverExpression}.${actionName}(${invocationArgs.join(', ')})`;\n const singleLineResultAssignment = ` const result = ${singleLineInvocation};`;\n\n if (singleLineResultAssignment.length <= 100) {\n return singleLineInvocation;\n }\n\n return [\n `await ${receiverExpression}.${actionName}(`,\n ...invocationArgs.map((argument) => ` ${argument},`),\n ' )',\n ].join('\\n');\n}\n\nfunction buildScopedOptionsForTenantRead(\n actionDef: MethodDefinition,\n routeConfig: ResolvedApiActionRouteConfig,\n tenantScoped: boolean,\n): { source: string; optionsIdentifier: string } {\n // The generator can only pass tenant read scope to methods that expose an\n // options object. Zero-argument methods that perform their own raw reads must\n // infer tenant scope in their implementation or adopt the options contract.\n if (\n !tenantScoped ||\n routeConfig.method !== 'GET' ||\n !hasSingleOptionsParameter(actionDef)\n ) {\n return { source: '', optionsIdentifier: 'options' };\n }\n\n return {\n source: ` const readScope = tenantReadOptionsScope();\n const scopedOptions = readScope\n ? ({ ...options, ...readScope } as ActionArgs[0])\n : options;\n\n`,\n optionsIdentifier: 'scopedOptions',\n };\n}\n\nfunction findItemClassRegistryKey(\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n): string {\n const itemObject = resolveCollectionItemObject(manifest, objectDef);\n if (!itemObject) {\n return resolveCollectionItemTypeName(manifest, objectDef) || className;\n }\n\n const manifestMatch = Object.entries(manifest.objects).find(\n ([, manifestObject]) => manifestObject === itemObject,\n );\n return manifestMatch?.[0] || itemObject.className;\n}\n\nfunction groupCustomActionRoutes(\n actionSpecs: Array<{\n routeDir: string;\n spec: GeneratedActionRouteSpec;\n }>,\n): Map<string, GeneratedActionRouteSpec[]> {\n const groupedRoutes = new Map<string, GeneratedActionRouteSpec[]>();\n\n for (const { routeDir, spec } of actionSpecs) {\n const existing = groupedRoutes.get(routeDir) || [];\n const duplicateMethod = existing.find(\n (candidate) => candidate.routeConfig.method === spec.routeConfig.method,\n );\n\n if (duplicateMethod) {\n throw new Error(\n `Duplicate custom API route handler for ${routeDir} (${spec.routeConfig.method}). ` +\n `Methods ${duplicateMethod.actionName} and ${spec.actionName} resolve to the same generated route.`,\n );\n }\n\n existing.push(spec);\n groupedRoutes.set(routeDir, existing);\n }\n\n return groupedRoutes;\n}\n\n/**\n * Generates SvelteKit API routes from manifest\n */\nexport async function generateSvelteKitRoutes(\n projectRoot: string,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<void> {\n if (!options.enabled) return;\n\n console.log('[smrt] Generating SvelteKit routes...');\n\n clearGeneratedRouteFiles(join(projectRoot, options.routesDir));\n clearGeneratedKnowledgeRoute(projectRoot, options);\n\n // Generate centralized configuration file first (if it doesn't exist)\n await generateSmrtConfigFile(projectRoot, manifest, options);\n\n let generatedCount = 0;\n let skippedCollections = 0;\n for (const [className, objectDef] of Object.entries(manifest.objects)) {\n if (isCollectionManifestClass(manifest, objectDef)) {\n const generatedCollectionRoutes = await generateCollectionRoutesForObject(\n projectRoot,\n className,\n objectDef,\n manifest,\n options,\n );\n if (generatedCollectionRoutes) {\n generatedCount++;\n } else {\n console.log(\n `[smrt] Skipping ${className} - no collection API routes to generate`,\n );\n skippedCollections++;\n }\n continue;\n }\n await generateRoutesForObject(\n projectRoot,\n className,\n objectDef,\n manifest,\n options,\n );\n generatedCount++;\n }\n\n if (options.knowledge?.api?.enabled) {\n generateKnowledgeRoute(projectRoot, options);\n }\n\n // Batch write contract route (#1759): {routesDir}/sync/apply/+server.ts.\n generateSyncApplyRoute(projectRoot, manifest, options);\n // Change-feed route (#1758) — cleanup rides clearGeneratedRouteFiles above.\n generateChangesRoute(projectRoot, manifest, options);\n // Live change-signal SSE route (#1763) — cleanup rides the sweep above.\n generateEventsRoute(\n projectRoot,\n manifest,\n options,\n computeWebManifestHash(manifest),\n );\n\n // Update .gitignore to exclude generated routes\n updateGitignore(projectRoot, options);\n\n const skippedMsg =\n skippedCollections > 0\n ? ` (skipped ${skippedCollections} collection classes)`\n : '';\n console.log(\n `[smrt] Generated routes for ${generatedCount} SMRT objects${skippedMsg}`,\n );\n}\n\nfunction clearGeneratedRouteFiles(routesRoot: string): void {\n if (!existsSync(routesRoot)) {\n return;\n }\n\n for (const entry of readdirSync(routesRoot, { withFileTypes: true })) {\n const entryPath = join(routesRoot, entry.name);\n\n if (entry.isDirectory()) {\n clearGeneratedRouteFiles(entryPath);\n continue;\n }\n\n if (!entry.isFile() || entry.name !== '+server.ts') {\n continue;\n }\n\n const fileContent = readFileSync(entryPath, 'utf-8');\n if (fileContent.startsWith(AUTO_GENERATED_ROUTE_HEADER)) {\n unlinkSync(entryPath);\n }\n }\n}\n\nfunction knowledgeRouteDir(projectRoot: string, options: SvelteKitOptions) {\n const basePath = String(\n options.knowledge?.api?.basePath || '/__smrt/knowledge',\n );\n const routeRoot = svelteKitRouteRoot(options.routesDir);\n const segments = basePath\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean);\n\n return join(projectRoot, routeRoot, ...segments);\n}\n\nfunction svelteKitRouteRoot(routesDir: string): string {\n const normalized = routesDir.replaceAll('\\\\', '/').replace(/\\/+$/, '');\n const marker = '/routes';\n const markerIndex = normalized.indexOf(marker);\n\n if (normalized === 'src/routes' || normalized.endsWith('/routes')) {\n return normalized;\n }\n if (markerIndex !== -1) {\n return normalized.slice(0, markerIndex + marker.length);\n }\n return 'src/routes';\n}\n\nfunction clearGeneratedKnowledgeRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): void {\n const routeDir = knowledgeRouteDir(projectRoot, options);\n const routePath = join(routeDir, '+server.ts');\n if (!existsSync(routePath)) return;\n\n const content = readFileSync(routePath, 'utf-8');\n if (content.startsWith(AUTO_GENERATED_ROUTE_HEADER)) {\n unlinkSync(routePath);\n }\n}\n\nfunction generateKnowledgeRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): void {\n const routeDir = knowledgeRouteDir(projectRoot, options);\n const route = generateKnowledgeRouteTemplate(\n options.knowledge ?? {},\n readKnowledgeRouteArtifact(projectRoot),\n );\n writeRoute(routeDir, '+server.ts', route);\n}\n\nfunction readKnowledgeRouteArtifact(\n projectRoot: string,\n): Record<string, unknown> | null {\n for (const relativePath of [\n '.smrt/smrt-knowledge.json',\n 'dist/smrt-knowledge.json',\n ]) {\n const fullPath = join(projectRoot, relativePath);\n if (!existsSync(fullPath)) continue;\n try {\n return JSON.parse(readFileSync(fullPath, 'utf-8'));\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Generates SMRT object registration file\n * This file imports all SMRT objects to trigger their @smrt() decorators\n */\nasync function generateRegistrationFile(\n projectRoot: string,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<void> {\n const configPath = options.configPath || 'src/lib/server';\n const configDir = join(projectRoot, configPath);\n const registrationFilePath = join(configDir, 'smrt-register.ts');\n\n // Group objects by package for efficient imports\n const localObjects: Array<[string, (typeof manifest.objects)[string]]> = [];\n const packageObjects = new Map<\n string,\n { classNames: string[]; hasCollectionImport: boolean }\n >();\n\n for (const [className, objectDef] of Object.entries(manifest.objects)) {\n if (isLocalObject(projectRoot, objectDef)) {\n // Local object (source in project, not node_modules) - use $lib path\n localObjects.push([className, objectDef]);\n } else if (objectDef.packageName) {\n // External package - group by package name\n const packageEntry = packageObjects.get(objectDef.packageName) || {\n classNames: [],\n hasCollectionImport: false,\n };\n\n if (isCollectionManifestClass(manifest, objectDef)) {\n packageEntry.hasCollectionImport = true;\n } else {\n packageEntry.classNames.push(className);\n }\n\n packageObjects.set(objectDef.packageName, packageEntry);\n } else {\n // No package name and not local - treat as local fallback\n localObjects.push([className, objectDef]);\n }\n }\n\n // Generate imports for local objects\n // Issue #870: Extract simple class names from qualified names\n const localNamedImports = new Map<string, string[]>();\n const localSideEffectImports = new Set<string>();\n\n for (const [className, objectDef] of localObjects) {\n const simpleClassName = extractSimpleClassName(className);\n\n // Calculate direct relative path from the configDir where smrt-register.ts lives\n // to the actual source file of the object.\n let importPath = '';\n if (objectDef.filePath) {\n const relativeToConfig = relative(configDir, objectDef.filePath);\n const normalized = relativeToConfig\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|js|tsx|jsx)$/, '');\n importPath = normalized.startsWith('.') ? normalized : `./${normalized}`;\n } else {\n // Fallback for missing file paths\n importPath = getSvelteKitImportPath(\n projectRoot,\n undefined,\n options.objectsDir,\n simpleClassName,\n );\n }\n\n if (isCollectionManifestClass(manifest, objectDef)) {\n localSideEffectImports.add(importPath);\n continue;\n }\n\n const existing = localNamedImports.get(importPath) ?? [];\n existing.push(simpleClassName);\n localNamedImports.set(importPath, existing);\n }\n\n const localImports = [\n ...Array.from(localNamedImports.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([importPath, simpleNames]) => {\n const sortedNames = simpleNames.sort((a, b) => a.localeCompare(b));\n return `import { ${sortedNames.join(', ')} } from '${importPath}';`;\n }),\n ...Array.from(localSideEffectImports.values())\n .sort((a, b) => a.localeCompare(b))\n .map((importPath) => `import '${importPath}';`),\n ].join('\\n');\n\n // Generate imports for external packages\n // Issue #870: Extract simple class names from qualified names\n const packageImports = Array.from(packageObjects.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .flatMap(([packageName, packageEntry]) => {\n const imports: string[] = [];\n\n if (packageEntry.hasCollectionImport) {\n imports.push(`import '${packageName}';`);\n }\n\n if (packageEntry.classNames.length > 0) {\n // Extract simple class names for valid import syntax\n const simpleNames = packageEntry.classNames\n .map(extractSimpleClassName)\n .sort((a, b) => a.localeCompare(b));\n imports.push(\n `import { ${simpleNames.join(', ')} } from '${packageName}';`,\n );\n }\n\n return imports;\n })\n .join('\\n');\n\n const imports = [packageImports, localImports].filter(Boolean).join('\\n');\n const registrations = Object.entries(manifest.objects)\n .map(([className, objectDef]) => {\n if (isCollectionManifestClass(manifest, objectDef)) {\n // Importing the collection class is enough to trigger its decorator.\n // Explicit package-qualified re-registration only applies to object\n // classes, because ObjectRegistry.register() is object-only.\n return null;\n }\n\n const simpleClassName = extractSimpleClassName(className);\n const localObject = isLocalObject(projectRoot, objectDef);\n const packageName = getRegistrationPackageName(\n manifest,\n objectDef,\n localObject,\n );\n\n if (!packageName) {\n return null;\n }\n\n const packageNameLiteral = toSingleQuotedStringLiteral(packageName);\n const singleLineRegistration = `ObjectRegistry.register(${simpleClassName}, { name: '${simpleClassName}', packageName: ${packageNameLiteral} });`;\n\n if (singleLineRegistration.length <= BIOME_LINE_WIDTH) {\n return singleLineRegistration;\n }\n\n return [\n `ObjectRegistry.register(${simpleClassName}, {`,\n ` name: '${simpleClassName}',`,\n ` packageName: ${packageNameLiteral},`,\n `});`,\n ].join('\\n');\n })\n .filter((registration): registration is string => registration !== null)\n .join('\\n');\n\n const registrationContent = `/**\n * Auto-generated SMRT object registration\n * DO NOT EDIT - changes will be overwritten\n *\n * Importing these modules triggers their @smrt() decorators, which perform\n * the initial registration. The explicit re-registration below is intentional:\n * it upgrades bundled runtimes to deterministic qualified registrations.\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n${imports}\n\n// Re-register imported objects with explicit package names for bundled runtimes\n${registrations}\n`;\n\n // Create directory if it doesn't exist\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(registrationFilePath, registrationContent, 'utf-8');\n console.log(`[smrt] Generated registration file: ${registrationFilePath}`);\n}\n\n/**\n * Generates centralized SMRT configuration file\n * Only creates if file doesn't exist (preserves user customizations)\n */\nasync function generateSmrtConfigFile(\n projectRoot: string,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<void> {\n const configPath = options.configPath || 'src/lib/server';\n const configFileName = options.configFileName || 'smrt.ts';\n const configDir = join(projectRoot, configPath);\n const configFilePath = join(configDir, configFileName);\n\n // Always generate registration file (it gets overwritten)\n await generateRegistrationFile(projectRoot, manifest, options);\n\n // Don't overwrite existing config file\n if (existsSync(configFilePath)) {\n console.log('[smrt] Config file already exists, skipping generation');\n return;\n }\n\n const configContent = `/**\n * Centralized SMRT configuration with per-object overrides\n * Generated by @smrt/core vite plugin\n *\n * Most objects will use the default configuration.\n * Add entries to \\`objectOverrides\\` for objects that need different backends.\n */\n\n// Import SMRT objects to register them via @smrt() decorators\nimport './smrt-register.js';\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtGetRequestScopedDatabase:\n | (() => SmrtClassOptions['db'] | undefined)\n | undefined;\n}\n\n/**\n * Per-object configuration overrides\n * Define specific backends for objects that differ from project defaults\n *\n * @example\n * const objectOverrides: Record<string, Partial<SmrtClassOptions>> = {\n * // Analytics uses a separate PostgreSQL database\n * Analytics: {\n * db: {\n * url: process.env.ANALYTICS_DATABASE_URL!,\n * type: 'postgres'\n * }\n * },\n *\n * // AuditLog uses dedicated database with no AI\n * AuditLog: {\n * db: {\n * url: process.env.AUDIT_DATABASE_URL!,\n * type: 'postgres'\n * },\n * ai: undefined\n * },\n *\n * // Cache uses REST adapter (e.g., Redis)\n * Cache: {\n * persistence: {\n * type: 'rest',\n * baseUrl: process.env.REDIS_URL!\n * }\n * }\n * };\n */\nconst objectOverrides: Record<string, Partial<SmrtClassOptions>> = {\n // Add your per-object configuration overrides here\n};\n\n/**\n * Default configuration for most SMRT objects\n * Customize this to change project-wide defaults\n */\nfunction getDefaultConfig(): SmrtClassOptions {\n return {\n db: {\n url: process.env.DATABASE_URL || ':memory:',\n type: (process.env.DATABASE_TYPE as 'sqlite' | 'postgres') || 'sqlite'\n },\n ai: process.env.OPENAI_API_KEY ? {\n type: 'openai',\n apiKey: process.env.OPENAI_API_KEY\n } : process.env.ANTHROPIC_API_KEY ? {\n type: 'anthropic',\n apiKey: process.env.ANTHROPIC_API_KEY\n } : undefined\n };\n}\n\nfunction getRequestScopedDatabase(): SmrtClassOptions['db'] | undefined {\n const getter = globalThis.__smrtGetRequestScopedDatabase;\n return typeof getter === 'function' ? getter() : undefined;\n}\n\n/**\n * Get configuration for a specific SMRT object\n * Merges project defaults with per-object overrides if defined\n */\nexport function getSmrtConfig(className: string): SmrtClassOptions {\n const defaults = getDefaultConfig();\n const override = objectOverrides[className];\n\n if (override) {\n // Deep merge: override specific properties while keeping defaults\n return {\n ...defaults,\n ...override,\n // Ensure nested objects are merged properly\n db: override.db\n ? { ...(defaults.db as any), ...(override.db as any) }\n : defaults.db,\n ai: override.ai !== undefined ? override.ai : defaults.ai\n };\n }\n\n return defaults;\n}\n\n/**\n * Helper to get a collection with centralized configuration\n * Automatically applies project defaults or object-specific overrides\n */\nexport async function getCollection<\n T extends import('@happyvertical/smrt-core').SmrtObject,\n>(className: string, overrides: Partial<SmrtClassOptions> = {}) {\n const config = getSmrtConfig(className);\n const objectOverride = objectOverrides[className];\n const requestScopedDb =\n !overrides.db && !objectOverride?.db\n ? getRequestScopedDatabase()\n : undefined;\n\n return await ObjectRegistry.getCollection<T>(\n className,\n {\n ...config,\n ...overrides,\n db: overrides.db\n ? { ...(config.db as any), ...(overrides.db as any) }\n : requestScopedDb ?? config.db,\n ai: overrides.ai !== undefined ? overrides.ai : config.ai\n }\n );\n}\n`;\n\n // Create directory if it doesn't exist\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(configFilePath, configContent, 'utf-8');\n console.log(`[smrt] Generated configuration file: ${configFilePath}`);\n}\n\n/**\n * Generates route files for a single SMRT object\n */\nasync function generateRoutesForObject(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<void> {\n const collectionName = objectDef.collection;\n const routeDir = join(projectRoot, options.routesDir, collectionName);\n\n // Check if API is enabled for this object\n const apiConfig = objectDef.decoratorConfig?.api;\n if (apiConfig === false) {\n console.log(`[smrt] Skipping ${className} - API disabled`);\n return;\n }\n\n // Determine which CRUD actions to include via the shared resolver.\n const includedActions = resolveStandardCrudActions(apiConfig);\n\n // Generate collection route (list, create)\n if (includedActions.includes('list') || includedActions.includes('create')) {\n const collectionRoute = generateCollectionRouteTemplate(\n projectRoot,\n className,\n objectDef,\n manifest,\n includedActions,\n options,\n routeDir,\n );\n writeRoute(routeDir, '+server.ts', collectionRoute);\n }\n\n // Generate item route (get, update, delete)\n if (\n includedActions.includes('get') ||\n includedActions.includes('update') ||\n includedActions.includes('delete')\n ) {\n const itemRoute = generateItemRouteTemplate(\n projectRoot,\n className,\n objectDef,\n manifest,\n includedActions,\n options,\n join(routeDir, '[id]'),\n );\n writeRoute(join(routeDir, '[id]'), '+server.ts', itemRoute);\n }\n\n // Generate custom action routes\n const customActions = Object.entries(objectDef.methods).filter(\n ([name, method]) =>\n !STANDARD_API_ACTIONS.includes(name) &&\n method.isPublic &&\n shouldIncludeInApi(name, apiConfig),\n );\n\n const actionSpecs: Array<{\n routeDir: string;\n spec: GeneratedActionRouteSpec;\n }> = [];\n\n for (const [actionName, actionDef] of customActions) {\n const routeConfig = resolveApiActionRouteConfig(\n actionName,\n actionDef,\n apiConfig,\n { kebabRoutes: options.kebabRoutes },\n );\n\n if (routeConfig.scope === 'collection' && !actionDef.isStatic) {\n console.warn(\n `[smrt] Skipping ${className}.${actionName} - collection API routes require a static method`,\n );\n continue;\n }\n\n const actionBaseDir =\n routeConfig.scope === 'collection' ? routeDir : join(routeDir, '[id]');\n actionSpecs.push({\n routeDir: join(actionBaseDir, ...routeConfig.pathSegments),\n spec: {\n lookupClassName: className,\n lookupObjectDef: objectDef,\n hostClassName: className,\n actionName,\n actionDef,\n routeConfig,\n hostType: 'item',\n },\n });\n }\n\n for (const [actionRouteDir, routeSpecs] of groupCustomActionRoutes(\n actionSpecs,\n )) {\n const actionRoute = generateActionRouteTemplate(\n projectRoot,\n actionRouteDir,\n routeSpecs,\n objectDef,\n manifest,\n options,\n );\n writeRoute(actionRouteDir, '+server.ts', actionRoute);\n }\n}\n\nasync function generateCollectionRoutesForObject(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<boolean> {\n const apiConfig = objectDef.decoratorConfig?.api;\n if (apiConfig === false) {\n console.log(`[smrt] Skipping ${className} - API disabled`);\n return false;\n }\n\n const routeDir = join(projectRoot, options.routesDir, objectDef.collection);\n const lookupClassName = findItemClassRegistryKey(\n className,\n objectDef,\n manifest,\n );\n const lookupObjectDef = findObjectDefByRegistryKey(manifest, lookupClassName);\n\n const customActions = Object.entries(objectDef.methods).filter(\n ([name, method]) =>\n !STANDARD_API_ACTIONS.includes(name) &&\n method.isPublic &&\n shouldIncludeInApi(name, apiConfig),\n );\n\n if (customActions.length === 0) {\n return false;\n }\n\n let generatedAnyRoutes = false;\n const actionSpecs: Array<{\n routeDir: string;\n spec: GeneratedActionRouteSpec;\n }> = [];\n\n for (const [actionName, actionDef] of customActions) {\n const routeConfig = resolveApiActionRouteConfig(\n actionName,\n actionDef,\n apiConfig,\n { kebabRoutes: options.kebabRoutes },\n 'collection',\n );\n\n if (routeConfig.scope !== 'collection') {\n console.warn(\n `[smrt] Skipping ${className}.${actionName} - collection class methods only support collection-scoped API routes`,\n );\n continue;\n }\n\n actionSpecs.push({\n routeDir: join(routeDir, ...routeConfig.pathSegments),\n spec: {\n lookupClassName,\n lookupObjectDef,\n hostClassName: className,\n actionName,\n actionDef,\n routeConfig,\n hostType: 'collection',\n },\n });\n }\n\n for (const [actionRouteDir, routeSpecs] of groupCustomActionRoutes(\n actionSpecs,\n )) {\n const actionRoute = generateActionRouteTemplate(\n projectRoot,\n actionRouteDir,\n routeSpecs,\n objectDef,\n manifest,\n options,\n );\n writeRoute(actionRouteDir, '+server.ts', actionRoute);\n generatedAnyRoutes = true;\n }\n\n return generatedAnyRoutes;\n}\n\n/**\n * Resolve the list of standard CRUD actions exposed by the API for a given\n * apiConfig. Single source of truth for both the route generator and the\n * cli↔api coherence lint.\n */\nfunction resolveStandardCrudActions(apiConfig: unknown): string[] {\n if (apiConfig === false) return [];\n if (apiConfig === true || apiConfig === undefined) {\n return [...STANDARD_API_ACTIONS];\n }\n if (typeof apiConfig !== 'object') return [...STANDARD_API_ACTIONS];\n\n const config = apiConfig as { include?: string[]; exclude?: string[] };\n let crud: string[] = config.include\n ? config.include.filter((a) => STANDARD_API_ACTIONS.includes(a))\n : [...STANDARD_API_ACTIONS];\n if (Array.isArray(config.exclude)) {\n const exclude = config.exclude;\n crud = crud.filter((a) => !exclude.includes(a));\n }\n return crud;\n}\n\n/**\n * Check if a custom action should be included in API.\n *\n * Mirrors the CRUD inclusion path: intersect with `include` (when set), then\n * subtract `exclude`. Both filters apply together, matching principle of least\n * surprise for users supplying both lists.\n */\nfunction shouldIncludeInApi(actionName: string, apiConfig: unknown): boolean {\n if (apiConfig === false) return false;\n if (apiConfig === true || apiConfig === undefined) return true;\n\n if (typeof apiConfig === 'object' && apiConfig !== null) {\n const config = apiConfig as { include?: unknown; exclude?: unknown };\n // Narrow the scanned decorator config defensively: a non-array\n // include/exclude is treated as unset rather than throwing on .includes().\n const included = Array.isArray(config.include)\n ? config.include.includes(actionName)\n : true;\n const excluded = Array.isArray(config.exclude)\n ? config.exclude.includes(actionName)\n : false;\n return included && !excluded;\n }\n\n return true;\n}\n\n/**\n * Compute the set of action names (standard CRUD + custom methods) that the\n * API exposes for a given object definition. This is the same resolution used\n * to drive route generation, exposed so coherence checks (e.g. CLI vs API)\n * can ask \"what does the API expose?\" without re-implementing the logic.\n */\nexport function resolveApiActionSet(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): Set<string> {\n const apiConfig = objectDef.decoratorConfig?.api;\n if (apiConfig === false) return new Set();\n\n const objectIsCollectionClass = manifest\n ? isCollectionManifestClass(manifest, objectDef)\n : isCollectionClass(objectDef);\n const actions = objectIsCollectionClass\n ? new Set<string>()\n : new Set<string>(resolveStandardCrudActions(apiConfig));\n const config = getApiConfigObject(apiConfig);\n\n // Custom (non-CRUD, public) methods. We mirror BOTH the include/exclude\n // filter AND the scope/static skip rules that the route generators apply\n // (sveltekit-generator.ts generateRoutesForObject and\n // generateCollectionRoutesForObject). Otherwise the cli↔api coherence lint\n // would claim \"reachable via API\" for methods that produce no HTTP route.\n for (const [name, method] of Object.entries(objectDef.methods || {})) {\n if (STANDARD_API_ACTIONS.includes(name)) continue;\n if (!method.isPublic) continue;\n if (!shouldIncludeInApi(name, apiConfig)) continue;\n\n const routeConfig: ApiCustomRouteConfig | undefined =\n config?.routes?.[name];\n const defaultScope: 'item' | 'collection' = objectIsCollectionClass\n ? 'collection'\n : method.isStatic\n ? 'collection'\n : 'item';\n const scope = routeConfig?.scope || defaultScope;\n\n if (objectIsCollectionClass) {\n // Collection classes only emit collection-scoped custom routes.\n if (scope !== 'collection') continue;\n } else {\n // Non-collection classes skip collection-scoped non-static methods\n // (the generator emits a console warning in this case).\n if (scope === 'collection' && !method.isStatic) continue;\n }\n\n actions.add(name);\n }\n\n return actions;\n}\n\n/**\n * Detail for a single cli-vs-api coherence violation.\n */\nexport interface CliApiCoherenceViolation {\n className: string;\n unreachable: string[];\n}\n\n/**\n * Inspect a manifest and return classes whose `cli.include` references\n * methods not exposed via the API. Classes that opt out via\n * `cli: { skipApiCheck: true }` are skipped.\n *\n * Throws nothing; returns the violation list so callers can choose to throw\n * or warn.\n */\nexport function findCliApiCoherenceViolations(\n manifest: SmartObjectManifest,\n): CliApiCoherenceViolation[] {\n const violations: CliApiCoherenceViolation[] = [];\n\n for (const [className, objectDef] of Object.entries(manifest.objects)) {\n const cliConfig = objectDef.decoratorConfig?.cli;\n if (!cliConfig || typeof cliConfig !== 'object') continue;\n if (cliConfig.skipApiCheck) continue;\n if (!cliConfig.include || cliConfig.include.length === 0) continue;\n\n // Match generateCLIModule's effective command set: include − exclude.\n // A command in both lists is not actually registered, so the lint must\n // not flag it as unreachable.\n const cliExclude = Array.isArray(cliConfig.exclude)\n ? cliConfig.exclude\n : [];\n const effectiveCliCommands = cliConfig.include.filter(\n (cmd: string) => !cliExclude.includes(cmd),\n );\n if (effectiveCliCommands.length === 0) continue;\n\n const apiActionSet = resolveApiActionSet(objectDef, manifest);\n const unreachable = effectiveCliCommands.filter(\n (action: string) => !apiActionSet.has(action),\n );\n\n if (unreachable.length > 0) {\n violations.push({ className, unreachable });\n }\n }\n\n return violations;\n}\n\n/**\n * Throw if any class in the manifest has `cli.include` methods that aren't\n * reachable via the API. Default build-time gate; opt-out per-class via\n * `cli: { skipApiCheck: true }` (or globally via the vite plugin option\n * `validateCliApiCoherence: false`).\n */\nexport function validateCliIncludeAgainstApi(\n manifest: SmartObjectManifest,\n): void {\n const violations = findCliApiCoherenceViolations(manifest);\n if (violations.length === 0) return;\n\n const messages = violations.flatMap(({ className, unreachable }) =>\n unreachable.map(\n (action) =>\n `[smrt] ${className}.${action} is declared in cli.include but is not exposed via the api.\\n` +\n ` Either:\\n` +\n ` - Add '${action}' to api.include, or\\n` +\n ` - Remove '${action}' from cli.include.\\n` +\n ` The CLI invokes methods over HTTP; methods without API routes are unreachable.\\n` +\n ` If this CLI is intentionally invoked in-process (no HTTP), set\\n` +\n ` \\`cli: { skipApiCheck: true }\\` on the @smrt() decorator to acknowledge.`,\n ),\n );\n\n throw new Error(messages.join('\\n\\n'));\n}\n\n/**\n * Writes a route file, creating directories as needed\n */\nfunction writeRoute(dir: string, filename: string, content: string): void {\n try {\n // Create directory if needed\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Write the file (writeFileSync is synchronous and will throw on failure)\n const filePath = join(dir, filename);\n writeFileSync(filePath, content, 'utf-8');\n console.log(`[smrt] Generated: ${filePath}`);\n } catch (error) {\n console.error(`[smrt] [ERROR] Failed to write route file:`);\n console.error(`[smrt] [ERROR] Directory: ${dir}`);\n console.error(`[smrt] [ERROR] Filename: ${filename}`);\n console.error(`[smrt] [ERROR] Error:`, error);\n throw error;\n }\n}\n\n/**\n * Converts absolute file path to SvelteKit $lib alias import\n * For SvelteKit projects, uses $lib alias for better module resolution\n */\nfunction getSvelteKitImportPath(\n projectRoot: string,\n objectFilePath: string | undefined,\n objectsDir: string,\n className?: string,\n): string {\n // Handle undefined filePath by generating a default path\n // This allows tests to work without providing explicit file paths\n const filePath =\n objectFilePath ||\n join(projectRoot, objectsDir, `${className || 'Object'}.ts`);\n\n // Convert objectsDir to absolute path if it's relative\n const absoluteObjectsDir = objectsDir.startsWith('/')\n ? objectsDir\n : join(projectRoot, objectsDir);\n\n // Get the relative path from objectsDir to the file\n const relativePath = relative(absoluteObjectsDir, filePath);\n\n // Convert to forward slashes and remove extension\n const normalizedPath = relativePath.replace(/\\\\/g, '/');\n const withoutExtension = normalizedPath.replace(/\\.(ts|js|tsx|jsx)$/, '');\n\n // If objectsDir is under src/lib, use $lib alias\n if (objectsDir.includes('src/lib')) {\n const libSubpath = objectsDir.split('src/lib')[1] || '';\n const fullPath = libSubpath\n ? `${libSubpath}/${withoutExtension}`\n : withoutExtension;\n return `$lib${fullPath}`.replace(/\\/+/g, '/'); // Clean up double slashes\n }\n\n // Otherwise use relative path (fallback for non-standard layouts)\n return withoutExtension.startsWith('.')\n ? withoutExtension\n : `./${withoutExtension}`;\n}\n\n/**\n * Check if an object's source file is local to the project (not in node_modules).\n * The scanner sets packageName for all objects (including local ones from the\n * project's own package.json), so we check the filePath to distinguish.\n */\nfunction isLocalObject(\n projectRoot: string,\n objectDef: SmartObjectDefinition,\n): boolean {\n if (!objectDef.filePath) return false;\n return (\n objectDef.filePath.startsWith(projectRoot) &&\n !objectDef.filePath.includes('/node_modules/')\n );\n}\n\n/**\n * Generates collection route template (GET list, POST create)\n */\nfunction generateCollectionRouteTemplate(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n includedActions: string[],\n options: SvelteKitOptions,\n routeDir: string,\n): string {\n const hasGet = includedActions.includes('list');\n const hasPost = includedActions.includes('create');\n const modelType = resolveObjectTypeReference(\n projectRoot,\n className,\n objectDef,\n options,\n routeDir,\n );\n const serializers = resolveStandardRouteSerializers(\n objectDef.decoratorConfig?.api,\n );\n const serializerImports = serializers.importStatements.join('\\n');\n // A custom list serializer can render related-table data the per-table\n // change-feed version cannot observe (#1765), so such routes keep the v1\n // body-hash ETag; the default toPublicJSON path uses the v2 version source.\n const listUsesSerializer = !!serializers.listItemSerializerName;\n const readPermissionFields = collectReadPermissionFields(objectDef, manifest);\n const listUsesPermissionScopedBody = readPermissionFields.length > 0;\n const listUsesBodyHash = listUsesSerializer || listUsesPermissionScopedBody;\n // The build-time web-collection shape digest (#1764) salts the v2 read ETag so\n // a shape-only deploy (no table write) busts every read validator. Deterministic\n // for a given manifest — same value the generated virt-web module exports and\n // the client persistence namespace keys on.\n const webManifestHash = computeWebManifestHash(manifest);\n\n const imports = `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\nimport { error${hasPost ? ', json' : ''} } from '@sveltejs/kit';\n${\n serializerImports ? `${serializerImports}\\n` : ''\n}import { getCollection } from '$lib/server/smrt';\n${modelType.importStatement ? `${modelType.importStatement}\\n` : ''}import type { RequestHandler } from './$types';\n// Note: ${className} is auto-registered by the Vite plugin scanner\n${generateAuthGuardHelper(objectDef, manifest)}${isTenantScoped(objectDef) ? generateTenantContextHelper() : ''}${hasPost ? generateWritablePolicyHelper(objectDef) : ''}${hasGet ? generateConditionalGetRouteHelper(objectDef.decoratorConfig?.api, { tenantScoped: isTenantScoped(objectDef), permissionScoped: listUsesPermissionScopedBody, modelName: className, useBodyHash: listUsesBodyHash, manifestHash: webManifestHash }) : ''}`;\n\n // #1782: tenant-scoped reads fail closed to global (NULL-tenant) rows when no\n // tenant context is active (public/anonymous read). Non-tenant models keep the\n // plain list/count.\n const listAndCount = isTenantScoped(objectDef)\n ? ` const readScope = tenantReadScope();\n const items = await collection.list({ limit, offset, where: readScope });\n const count = await collection.count({ where: readScope });`\n : ` const items = await collection.list({ limit, offset });\n const count = await collection.count();`;\n\n const getHandler = hasGet\n ? `\n// List all ${className.toLowerCase()}s\nexport const GET: RequestHandler = async ({ locals, url, request }) => {\n${routeGuardPreamble(objectDef, false)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n const limit = Number(url.searchParams.get('limit')) || 50;\n const offset = Number(url.searchParams.get('offset')) || 0;\n\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n${\n listUsesSerializer\n ? `${listAndCount}\n // Custom serializer may render related-table data → v1 body-hash ETag (#1765).\n const serializedItems = await Promise.all(\n items.map(async (item) =>\n applyReadPermissionRedaction(\n await ${serializers.listItemSerializerName}(item),\n publicJsonOptions,\n ),\n ),\n );\n return conditionalJson(request, { items: serializedItems, count, limit, offset });`\n : listUsesPermissionScopedBody\n ? `${listAndCount}\n // Field read permissions make the body vary by caller permissions → v1 body-hash ETag (#1565).\n const items_public = items.map((item) => item.toPublicJSON(publicJsonOptions));\n return conditionalJson(request, { items: items_public, count, limit, offset });`\n : ` // ETag v2 (#1765): the table-version ETag is checked first; on a concrete\n // If-None-Match the list query below never runs (zero-query 304).\n return conditionalVersionedRead(request, collection.db, collection.tableName, async () => {\n${listAndCount}\n const items_public = items.map((item) => item.toPublicJSON(publicJsonOptions));\n return { items: items_public, count, limit, offset };\n });`\n}\n};\n`\n : '';\n\n const postHandler = hasPost\n ? `\n// Create new ${className.toLowerCase()}\nexport const POST: RequestHandler = async ({ locals, request }) => {\n${routeGuardPreamble(objectDef, true)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n const body: unknown = await request.json();\n const data = applyWritablePolicy(body);\n\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n const item = await collection.create(data);\n await item.save();\n${\n serializers.itemSerializerName\n ? `\n const serializedItem = await ${serializers.itemSerializerName}(item);\n\n return json(\n applyReadPermissionRedaction(serializedItem, publicJsonOptions),\n { status: 201 },\n );`\n : `\n return json(item.toPublicJSON(publicJsonOptions), { status: 201 });`\n}\n};\n`\n : '';\n\n return imports + getHandler + postHandler;\n}\n\nfunction generateCollectionLoad(\n className: string,\n options: { typeName?: string } = {},\n): string {\n const genericSuffix = options.typeName ? `<${options.typeName}>` : '';\n\n return [\n ` const collection = await getCollection${genericSuffix}(`,\n ` '${className}',`,\n ' );',\n ].join('\\n');\n}\n\nfunction generateNotFoundError(className: string): string {\n const singleLineNotFoundError = ` if (!item) throw error(404, '${className} not found');`;\n\n if (singleLineNotFoundError.length <= 100) {\n return singleLineNotFoundError;\n }\n\n return [\n ' if (!item)',\n ' throw error(',\n ' 404,',\n ` '${className} not found',`,\n ' );',\n ].join('\\n');\n}\n\nfunction generateCollectionNotRegisteredError(className: string): string {\n return [\n ' if (!collection)',\n ' throw error(',\n ' 500,',\n ` '${className} collection is not registered',`,\n ' );',\n ].join('\\n');\n}\n\nfunction generateClassNotRegisteredError(className: string): string {\n return [\n ' if (!registered)',\n ' throw error(',\n ' 500,',\n ` '${className} is not registered',`,\n ' );',\n ].join('\\n');\n}\n\n/**\n * Generates item route template (GET, PUT, DELETE)\n */\nfunction generateItemRouteTemplate(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n includedActions: string[],\n options: SvelteKitOptions,\n routeDir: string,\n): string {\n const hasGet = includedActions.includes('get');\n const hasPut = includedActions.includes('update');\n const hasDelete = includedActions.includes('delete');\n const simpleClassName = extractSimpleClassName(className);\n const modelType = resolveObjectTypeReference(\n projectRoot,\n className,\n objectDef,\n options,\n routeDir,\n );\n const serializers = resolveStandardRouteSerializers(\n objectDef.decoratorConfig?.api,\n );\n const serializerImports = serializers.importStatements.join('\\n');\n // A custom item serializer can render related-table data the per-table\n // change-feed version cannot observe (#1765), so such routes keep the v1\n // body-hash ETag; the default toPublicJSON path uses the v2 version source.\n const getUsesSerializer = !!serializers.itemSerializerName;\n const readPermissionFields = collectReadPermissionFields(objectDef, manifest);\n const getUsesPermissionScopedBody = readPermissionFields.length > 0;\n const getUsesBodyHash = getUsesSerializer || getUsesPermissionScopedBody;\n // The build-time web-collection shape digest (#1764) salts the v2 read ETag —\n // same value as the list route and the generated virt-web module (see above).\n const webManifestHash = computeWebManifestHash(manifest);\n\n const imports = `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\nimport { error${hasPut || hasDelete ? ', json' : ''} } from '@sveltejs/kit';\n${serializerImports ? `${serializerImports}\\n` : ''}import { getCollection } from '$lib/server/smrt';\n${modelType.importStatement ? `${modelType.importStatement}\\n` : ''}import type { RequestHandler } from './$types';\n${generateAuthGuardHelper(objectDef, manifest)}${isTenantScoped(objectDef) ? generateTenantContextHelper() : ''}${hasPut ? generateWritablePolicyHelper(objectDef) : ''}${hasGet ? generateConditionalGetRouteHelper(objectDef.decoratorConfig?.api, { tenantScoped: isTenantScoped(objectDef), permissionScoped: getUsesPermissionScopedBody, modelName: className, useBodyHash: getUsesBodyHash, manifestHash: webManifestHash }) : ''}`;\n\n // #1782: a tenant-scoped single read fails closed to global (NULL-tenant)\n // rows when no tenant context is active, so a public/anonymous GET /:id can't\n // fetch another tenant's row by id. Mutations (PUT/DELETE) require auth, so\n // they keep the plain id lookup and rely on the interceptor.\n const getForRead = isTenantScoped(objectDef)\n ? ` const readScope = tenantReadScope();\n const item = await collection.get(\n readScope ? { id: params.id, ...readScope } : params.id,\n );`\n : ` const item = await collection.get(params.id);`;\n\n const getHandler = hasGet\n ? `\n// Get single ${simpleClassName.toLowerCase()}\nexport const GET: RequestHandler = async ({ locals, params, request }) => {\n${routeGuardPreamble(objectDef, false)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n${\n getUsesSerializer\n ? `${getForRead}\n${generateNotFoundError(className)}\n // Custom serializer may render related-table data → v1 body-hash ETag (#1765).\n const serializedItem = await ${serializers.itemSerializerName}(item);\n return conditionalJson(\n request,\n applyReadPermissionRedaction(serializedItem, publicJsonOptions),\n );`\n : getUsesPermissionScopedBody\n ? `${getForRead}\n${generateNotFoundError(className)}\n // Field read permissions make the body vary by caller permissions → v1 body-hash ETag (#1565).\n return conditionalJson(request, item.toPublicJSON(publicJsonOptions));`\n : ` // ETag v2 (#1765): a concrete If-None-Match answers 304 without the row fetch\n // (a delete advances the version, so a since-deleted row can't false-match);\n // a wildcard \\`*\\` is deferred until the fetch confirms the row exists.\n return conditionalVersionedRead(request, collection.db, collection.tableName, async () => {\n${getForRead}\n${generateNotFoundError(className)}\n return item.toPublicJSON(publicJsonOptions);\n });`\n}\n};\n`\n : '';\n\n const putHandler = hasPut\n ? `\n// Update ${simpleClassName.toLowerCase()}\nexport const PUT: RequestHandler = async ({ locals, params, request }) => {\n${routeGuardPreamble(objectDef, true)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n const item = await collection.get(params.id);\n${generateNotFoundError(className)}\n\n const body: unknown = await request.json();\n const data = applyWritablePolicy(body);\n Object.assign(item, data);\n await item.save();\n${\n serializers.itemSerializerName\n ? `\n const serializedItem = await ${serializers.itemSerializerName}(item);\n\n return json(applyReadPermissionRedaction(serializedItem, publicJsonOptions));`\n : `\n return json(item.toPublicJSON(publicJsonOptions));`\n}\n};\n`\n : '';\n\n const deleteHandler = hasDelete\n ? `\n// Delete ${simpleClassName.toLowerCase()}\nexport const DELETE: RequestHandler = async ({ locals, params }) => {\n${routeGuardPreamble(objectDef, true)}\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n const item = await collection.get(params.id);\n${generateNotFoundError(className)}\n\n await item.delete();\n return json({ success: true });\n};\n`\n : '';\n\n return imports + getHandler + putHandler + deleteHandler;\n}\n\n/**\n * Generates custom action route template\n */\nfunction generateActionRouteTemplate(\n projectRoot: string,\n routeDir: string,\n routeSpecs: GeneratedActionRouteSpec[],\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): string {\n if (routeSpecs.length === 0) {\n throw new Error('Cannot generate a custom action route without handlers');\n }\n\n const [firstSpec] = routeSpecs;\n const { lookupClassName, routeConfig, hostType } = firstSpec;\n const lookupModelType = resolveObjectTypeReference(\n projectRoot,\n lookupClassName,\n firstSpec.lookupObjectDef,\n options,\n routeDir,\n );\n const hostModelType = resolveObjectTypeReference(\n projectRoot,\n firstSpec.hostClassName,\n objectDef,\n options,\n routeDir,\n );\n const typeImports = mergeImportStatements([\n lookupModelType.importStatement,\n hostModelType.importStatement,\n ]);\n\n const hasMixedHosts = routeSpecs.some(\n (spec) =>\n spec.hostType !== hostType ||\n spec.lookupClassName !== lookupClassName ||\n spec.routeConfig.scope !== routeConfig.scope,\n );\n\n if (hasMixedHosts) {\n throw new Error(\n `Cannot generate mixed custom route handlers for ${lookupClassName}. ` +\n 'All handlers sharing a route path must target the same host type and scope.',\n );\n }\n\n const importBlock = [\n \"import { error, json } from '@sveltejs/kit';\",\n hostType === 'collection' || routeConfig.scope !== 'collection'\n ? \"import { getCollection } from '$lib/server/smrt';\"\n : \"import { ObjectRegistry } from '@happyvertical/smrt-core';\",\n typeImports,\n \"import type { RequestHandler } from './$types';\",\n ]\n .filter(Boolean)\n .join('\\n');\n\n const tenantScoped =\n isTenantScoped(objectDef) ||\n routeSpecs.some(\n (spec) => !!spec.lookupObjectDef && isTenantScoped(spec.lookupObjectDef),\n );\n const handlers = routeSpecs\n .map((spec) =>\n generateActionRouteHandler(\n spec.lookupClassName,\n spec.actionName,\n spec.actionDef,\n spec.routeConfig,\n spec.hostType,\n tenantScoped,\n lookupModelType.typeName,\n hostModelType.typeName,\n ),\n )\n .join('\\n');\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\n${importBlock}\n${generateAuthGuardHelper(objectDef, manifest)}${tenantScoped ? generateTenantContextHelper() : ''}\n${handlers}`;\n}\n\nfunction generateActionRouteHandler(\n lookupClassName: string,\n actionName: string,\n actionDef: MethodDefinition,\n routeConfig: ResolvedApiActionRouteConfig,\n hostType: 'item' | 'collection',\n tenantScoped: boolean,\n lookupTypeName: string,\n hostTypeName: string,\n): string {\n const handlerName = routeConfig.method;\n // Mutating verbs require auth even when reads are public (#1540). Tenant-scoped\n // objects also establish tenant context so the action runs filtered.\n const guardLines = [\n ` requireRouteAuth(locals, ${routeConfig.method !== 'GET'});`,\n ];\n if (tenantScoped) {\n guardLines.push(' establishTenantContext(locals);');\n }\n const authGuardLine = guardLines.join('\\n');\n const hasInput = actionDef.parameters.length > 0;\n const needsRequest = hasInput;\n const collectionHandlerArgs = buildRouteHandlerArgs(\n routeConfig.pathParamNames.length > 0,\n needsRequest,\n );\n const itemHandlerArgs = buildRouteHandlerArgs(true, needsRequest);\n\n if (hostType === 'collection') {\n const optionsLoad = buildActionOptionsLoad(\n actionName,\n actionDef,\n routeConfig,\n hostTypeName,\n actionDef.isStatic,\n );\n const receiverExpression = actionDef.isStatic\n ? 'CollectionClass'\n : 'typedCollection';\n const staticTargetLoad = actionDef.isStatic\n ? ` const CollectionClass = typedCollection.constructor as typeof ${hostTypeName};\\n`\n : '';\n const scopedOptions = buildScopedOptionsForTenantRead(\n actionDef,\n routeConfig,\n tenantScoped,\n );\n const invocationArgs = buildActionInvocationArgs(\n actionDef,\n scopedOptions.optionsIdentifier,\n );\n\n return `// Custom collection method: ${actionName}\nexport const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {\n${authGuardLine}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(lookupClassName, { typeName: lookupTypeName })}\n const typedCollection = collection as unknown as ${hostTypeName};\n${generateCollectionNotRegisteredError(lookupClassName)}\n${staticTargetLoad}\n\n${optionsLoad}${scopedOptions.source} const result = ${buildActionInvocationExpression(\n receiverExpression,\n actionName,\n invocationArgs,\n )};\n\n return json({\n action: '${actionName}',\n result: toPublicResult(result, publicJsonOptions),\n });\n};\n`;\n }\n\n if (routeConfig.scope === 'collection') {\n const optionsLoad = buildActionOptionsLoad(\n actionName,\n actionDef,\n routeConfig,\n hostTypeName,\n true,\n );\n const scopedOptions = buildScopedOptionsForTenantRead(\n actionDef,\n routeConfig,\n tenantScoped,\n );\n const invocationArgs = buildActionInvocationArgs(\n actionDef,\n scopedOptions.optionsIdentifier,\n );\n return `// Custom collection action: ${actionName}\nexport const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {\n${authGuardLine}\n const publicJsonOptions = getPublicJsonOptions(locals);\n const registered = ObjectRegistry.getClass('${lookupClassName}');\n${generateClassNotRegisteredError(lookupClassName)}\n\n${optionsLoad}${scopedOptions.source} const ClassRef = registered.constructor as typeof ${hostTypeName};\n const result = ${buildActionInvocationExpression(\n 'ClassRef',\n actionName,\n invocationArgs,\n )};\n\n return json({\n action: '${actionName}',\n result: toPublicResult(result, publicJsonOptions),\n });\n};\n`;\n }\n\n const optionsLoad = buildActionOptionsLoad(\n actionName,\n actionDef,\n routeConfig,\n hostTypeName,\n false,\n );\n const scopedItemLoad =\n tenantScoped && routeConfig.method === 'GET'\n ? ` const readScope = tenantReadScope();\n const item = await collection.get(\n readScope ? { id: params.id, ...readScope } : params.id,\n );`\n : ' const item = await collection.get(params.id);';\n const invocationArgs = buildActionInvocationArgs(actionDef);\n return `// Custom action: ${actionName}\nexport const ${handlerName}: RequestHandler = async (${itemHandlerArgs}) => {\n${authGuardLine}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(lookupClassName, { typeName: lookupTypeName })}\n${scopedItemLoad}\n${generateNotFoundError(lookupClassName)}\n\n${optionsLoad} const result = ${buildActionInvocationExpression(\n 'item',\n actionName,\n invocationArgs,\n )};\n\n return json({\n action: '${actionName}',\n result: toPublicResult(result, publicJsonOptions),\n });\n};\n`;\n}\n\nfunction generateKnowledgeRouteTemplate(\n knowledge: DomainKnowledgeConfig,\n artifact: Record<string, unknown> | null,\n): string {\n const api = knowledge.api ?? {};\n const includeDocs = api.includeDocs === true;\n const includePrompts = api.includePrompts === true;\n const requireAdmin = api.requireAdmin !== false;\n const artifactLiteral = artifact ? JSON.stringify(artifact) : 'null';\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\nimport { dev } from '$app/environment';\nimport { error, json } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\n\nconst INCLUDE_DOCS_BY_DEFAULT = ${JSON.stringify(includeDocs)};\nconst INCLUDE_PROMPTS_BY_DEFAULT = ${JSON.stringify(includePrompts)};\nconst REQUIRE_ADMIN = ${JSON.stringify(requireAdmin)};\nconst KNOWLEDGE_ARTIFACT: Record<string, unknown> | null = ${artifactLiteral};\n\nif (!dev && !REQUIRE_ADMIN) {\n console.warn('[smrt] PUBLIC knowledge API route enabled; unauthenticated responses are sanitized.');\n}\n\nexport const GET: RequestHandler = async ({ locals, url, setHeaders }) => {\n setHeaders({ 'cache-control': 'private, no-store' });\n\n if (!dev && REQUIRE_ADMIN && !isKnowledgeAdmin(locals)) {\n throw error(403, 'SMRT knowledge requires dev mode or admin access');\n }\n\n const artifact = readKnowledgeArtifact();\n const includeDocs = queryBoolean(url, 'includeDocs', INCLUDE_DOCS_BY_DEFAULT);\n const includePrompts = queryBoolean(\n url,\n 'includePrompts',\n INCLUDE_PROMPTS_BY_DEFAULT,\n );\n\n return json(sanitizeKnowledgeArtifact(artifact, {\n includeDocs,\n includePrompts,\n publicAccess: !REQUIRE_ADMIN,\n }));\n};\n\nfunction readKnowledgeArtifact(): Record<string, unknown> {\n if (!KNOWLEDGE_ARTIFACT) throw error(404, 'SMRT knowledge artifact not found');\n return KNOWLEDGE_ARTIFACT;\n}\n\nfunction queryBoolean(url: URL, name: string, defaultValue: boolean): boolean {\n const value = url.searchParams.get(name);\n if (value === null) return defaultValue;\n return value === 'true';\n}\n\nfunction sanitizeKnowledgeArtifact(\n artifact: Record<string, unknown>,\n options: { includeDocs: boolean; includePrompts: boolean; publicAccess: boolean },\n): Record<string, unknown> {\n const sanitized = { ...artifact };\n\n if (!options.includeDocs) {\n delete sanitized.agentDoc;\n // Linked module docs (#2108) are the same authored prose as agentDoc, just\n // split across siblings — they must drop with it, not leak past the flag.\n delete sanitized.moduleDocs;\n }\n\n if (!options.includePrompts) {\n sanitized.prompts = [];\n }\n\n if (options.publicAccess) {\n delete sanitized.dependencies;\n delete sanitized.sourceHashes;\n delete sanitized.sourceManifestPath;\n delete sanitized.agentDocPath;\n }\n\n return sanitized;\n}\n\nfunction recordValue(value: unknown, key: string): unknown {\n if (!value || typeof value !== 'object') return undefined;\n return (value as Record<string, unknown>)[key];\n}\n\nfunction isKnowledgeAdmin(locals: unknown): boolean {\n if (!locals || typeof locals !== 'object') return false;\n const localRecord = locals as Record<string, unknown>;\n if (\n localRecord.smrtKnowledgeAdmin === true ||\n localRecord.smrtAdmin === true\n ) {\n return true;\n }\n\n const userRoles = rolesFrom(localRecord.user);\n const sessionRoles = rolesFrom(recordValue(localRecord.session, 'user'));\n return [...userRoles, ...sessionRoles].some((role) =>\n ['admin', 'owner', 'superadmin'].includes(role),\n );\n}\n\nfunction rolesFrom(value: unknown): string[] {\n if (!value || typeof value !== 'object') return [];\n const roleList = recordValue(value, 'roles');\n const roles = Array.isArray(roleList) ? roleList : [recordValue(value, 'role')];\n return roles\n .filter((role): role is string => typeof role === 'string')\n .map((role) => role.toLowerCase());\n}\n`;\n}\n\n/**\n * Updates .gitignore to exclude auto-generated routes\n */\nfunction updateGitignore(projectRoot: string, options: SvelteKitOptions): void {\n const gitignorePath = join(projectRoot, '.gitignore');\n\n // Patterns to add\n const patternsToAdd = [\n '# SMRT auto-generated routes (from Vite plugin)',\n `${options.routesDir}/**/+server.ts`,\n ];\n if (options.knowledge?.api?.enabled) {\n const knowledgeRoute = relative(\n projectRoot,\n join(knowledgeRouteDir(projectRoot, options), '+server.ts'),\n ).replaceAll('\\\\', '/');\n patternsToAdd.push(knowledgeRoute);\n }\n\n // Read existing .gitignore or create empty string\n let gitignoreContent = '';\n if (existsSync(gitignorePath)) {\n gitignoreContent = readFileSync(gitignorePath, 'utf-8');\n }\n\n // Check if patterns already exist\n let needsUpdate = false;\n const linesToAdd: string[] = [];\n\n for (const pattern of patternsToAdd) {\n // Skip if pattern already exists (check for exact match or similar)\n if (!gitignoreContent.includes(pattern)) {\n linesToAdd.push(pattern);\n needsUpdate = true;\n }\n }\n\n if (needsUpdate) {\n // Ensure file ends with newline before adding\n if (gitignoreContent.length > 0 && !gitignoreContent.endsWith('\\n')) {\n gitignoreContent += '\\n';\n }\n\n // Add a blank line before our section if file has content\n if (gitignoreContent.length > 0 && !gitignoreContent.endsWith('\\n\\n')) {\n gitignoreContent += '\\n';\n }\n\n // Add our patterns\n gitignoreContent += `${linesToAdd.join('\\n')}\\n`;\n\n writeFileSync(gitignorePath, gitignoreContent, 'utf-8');\n console.log('[smrt] Updated .gitignore with generated route patterns');\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sveltekit-generator.js","names":[],"sources":["../../src/vite-plugin/sveltekit-generator.ts"],"sourcesContent":["/**\n * SvelteKit route auto-generation from SMRT objects\n * Generates real TypeScript files in src/routes/api/ based on discovered SMRT objects\n */\n\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { DomainKnowledgeConfig } from '@happyvertical/smrt-types';\nimport { generateConditionalGetRouteHelper } from '../generators/conditional-get.js';\nimport type {\n ApiConfig,\n ApiCustomRouteConfig,\n ApiHttpMethod,\n ApiSerializerReference,\n} from '../registry/types.js';\nimport type {\n MethodDefinition,\n SmartObjectDefinition,\n SmartObjectManifest,\n} from '../scanner/types';\nimport { generateChangesRoute } from './changes-route.js';\nimport { generateEventsRoute } from './events-route.js';\nimport { AUTO_GENERATED_ROUTE_HEADER } from './route-header.js';\nimport { generateSyncApplyRoute } from './sync-apply-route.js';\nimport {\n computeWebManifestHash,\n isCollectionManifestClass,\n resolveCollectionItemObject,\n resolveCollectionItemTypeName,\n} from './web-collections.js';\n\nexport interface SvelteKitOptions {\n enabled: boolean;\n routesDir: string;\n objectsDir: string;\n configPath?: string; // default: 'src/lib/server'\n configFileName?: string; // default: 'smrt.ts'\n /**\n * Apply kebab-case to custom-method URL segments (e.g. `discoverFromUrl`\n * becomes `/discover-from-url`). Opt-in for one minor; default flips in the\n * next major. An explicit `api.routes[name].path` always wins over this.\n */\n kebabRoutes?: boolean;\n /** Domain knowledge API route generation. Disabled unless explicitly enabled. */\n knowledge?: DomainKnowledgeConfig;\n /**\n * Change-feed `_changes` route generation (#1758). Enabled by default\n * (the route is auth-guarded fail-closed); set `enabled: false` to skip.\n */\n changesRoute?: { enabled?: boolean };\n /**\n * Live `_events` SSE route generation (#1763). Enabled by default (the route\n * is auth-guarded fail-closed, same-origin only); set `enabled: false` to\n * skip. `maxSubscribers` caps active streams per process (#1860); 0 means\n * unlimited.\n *\n * Cross-origin subscription (#1861) is opt-in and fail-closed: provide\n * `allowedOrigins` (an explicit allowlist — never `*`) and set\n * `allowCredentials: true` so an allow-listed browser client can subscribe\n * with a credentialed `EventSource` (`withCredentials: true`). The generated\n * route echoes the specific request `Origin` only when it is allow-listed,\n * adds `Access-Control-Allow-Credentials: true`, and otherwise stays\n * same-origin only. Auth (an authenticated principal on `locals`) is\n * unchanged — CORS only lets the cookie reach the guard.\n */\n eventsRoute?: {\n enabled?: boolean;\n maxSubscribers?: number;\n allowedOrigins?: string[];\n allowCredentials?: boolean;\n };\n}\n\n// Keep this aligned with biome.json formatter.lineWidth.\nconst BIOME_LINE_WIDTH = 80;\nconst STANDARD_API_ACTIONS = ['list', 'get', 'create', 'update', 'delete'];\nconst GITIGNORE_MANAGED_BLOCK_START =\n '# BEGIN SMRT auto-generated routes (Vite plugin)';\nconst GITIGNORE_MANAGED_BLOCK_END =\n '# END SMRT auto-generated routes (Vite plugin)';\nconst LEGACY_GITIGNORE_HEADER =\n '# SMRT auto-generated routes (from Vite plugin)';\n\nexport interface ResolvedApiActionRouteConfig {\n scope: 'item' | 'collection';\n method: ApiHttpMethod;\n pathSegments: string[];\n pathParamNames: string[];\n}\n\ninterface GeneratedActionRouteSpec {\n lookupClassName: string;\n lookupObjectDef?: SmartObjectDefinition;\n hostClassName: string;\n actionName: string;\n actionDef: MethodDefinition;\n routeConfig: ResolvedApiActionRouteConfig;\n hostType: 'item' | 'collection';\n}\n\ninterface ResolvedStandardRouteSerializers {\n importStatements: string[];\n itemSerializerName?: string;\n listItemSerializerName?: string;\n}\n\ninterface GeneratedTypeReference {\n typeName: string;\n importStatement?: string;\n}\n\n/**\n * Extract simple class name from potentially qualified name.\n * Qualified names follow the pattern: @scope/package:ClassName\n *\n * Issue #870: Manifests may contain qualified names which generate invalid\n * import statements like: import { @pkg:Class } from '@pkg'\n *\n * @param qualifiedName - Class name that may be qualified (e.g., \"@pkg:Class\" or \"Class\")\n * @returns Simple class name (e.g., \"Class\")\n */\nfunction extractSimpleClassName(qualifiedName: string): string {\n const colonIndex = qualifiedName.indexOf(':');\n if (colonIndex !== -1) {\n return qualifiedName.substring(colonIndex + 1);\n }\n return qualifiedName;\n}\n\n/**\n * Check if an object definition represents a SmrtCollection class.\n * Collection classes share route paths with their item class (via inherited `collection` field),\n * so generating routes for both would cause file collisions.\n */\nfunction isCollectionClass(objectDef: SmartObjectDefinition): boolean {\n return objectDef.extends === 'SmrtCollection' || !!objectDef.extendsTypeArg;\n}\n\nfunction getRegistrationPackageName(\n manifest: SmartObjectManifest,\n objectDef: SmartObjectDefinition,\n isLocal: boolean,\n): string | undefined {\n if (isLocal) {\n return manifest.packageName ?? objectDef.packageName;\n }\n\n return objectDef.packageName;\n}\n\nfunction toSingleQuotedStringLiteral(value: string): string {\n const jsonLiteral = JSON.stringify(value);\n const jsonContent = jsonLiteral.slice(1, -1).replaceAll(\"'\", \"\\\\'\");\n return `'${jsonContent}'`;\n}\n\nfunction getApiConfigObject(apiConfig: unknown): ApiConfig | null {\n if (!apiConfig || typeof apiConfig !== 'object') {\n return null;\n }\n\n return apiConfig as ApiConfig;\n}\n\nfunction isSameSerializerReference(\n left?: ApiSerializerReference,\n right?: ApiSerializerReference,\n): boolean {\n return (\n !!left &&\n !!right &&\n left.importPath === right.importPath &&\n left.exportName === right.exportName\n );\n}\n\nfunction resolveStandardRouteSerializers(\n apiConfig: unknown,\n): ResolvedStandardRouteSerializers {\n const config = getApiConfigObject(apiConfig);\n const itemSerializer = config?.serializers?.item;\n const listItemSerializer =\n config?.serializers?.listItem || config?.serializers?.item;\n\n const importStatements: string[] = [];\n let itemSerializerName: string | undefined;\n let listItemSerializerName: string | undefined;\n\n if (itemSerializer) {\n itemSerializerName = 'serializeItemResponse';\n importStatements.push(\n `import { ${itemSerializer.exportName} as ${itemSerializerName} } from '${itemSerializer.importPath}';`,\n );\n }\n\n if (listItemSerializer) {\n if (isSameSerializerReference(listItemSerializer, itemSerializer)) {\n listItemSerializerName = itemSerializerName;\n } else {\n listItemSerializerName = 'serializeListItemResponse';\n importStatements.push(\n `import { ${listItemSerializer.exportName} as ${listItemSerializerName} } from '${listItemSerializer.importPath}';`,\n );\n }\n }\n\n return {\n importStatements,\n itemSerializerName,\n listItemSerializerName,\n };\n}\n\nfunction isValidTypeIdentifier(name: string): boolean {\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);\n}\n\nfunction resolveObjectTypeReference(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition | undefined,\n options: SvelteKitOptions,\n routeDir?: string,\n): GeneratedTypeReference {\n const simpleClassName = extractSimpleClassName(className);\n\n if (!objectDef || !isValidTypeIdentifier(simpleClassName)) {\n return { typeName: \"import('@happyvertical/smrt-core').SmrtObject\" };\n }\n\n const importPath =\n isLocalObject(projectRoot, objectDef) || !objectDef.packageName\n ? getRouteTypeImportPath(\n projectRoot,\n objectDef,\n options,\n simpleClassName,\n routeDir,\n )\n : objectDef.packageName;\n\n return {\n typeName: simpleClassName,\n importStatement: `import type { ${simpleClassName} } from '${importPath}';`,\n };\n}\n\nfunction getRouteTypeImportPath(\n projectRoot: string,\n objectDef: SmartObjectDefinition,\n options: SvelteKitOptions,\n simpleClassName: string,\n routeDir?: string,\n): string {\n if (objectDef.filePath) {\n const srcLibDir = join(projectRoot, 'src/lib');\n if (objectDef.filePath.startsWith(`${srcLibDir}/`)) {\n const libRelative = relative(srcLibDir, objectDef.filePath)\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|js|tsx|jsx)$/, '');\n return `$lib/${libRelative}`.replace(/\\/+/g, '/');\n }\n\n if (routeDir) {\n const routeRelative = relative(routeDir, objectDef.filePath)\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|js|tsx|jsx)$/, '');\n return routeRelative.startsWith('.')\n ? routeRelative\n : `./${routeRelative}`;\n }\n }\n\n return getSvelteKitImportPath(\n projectRoot,\n objectDef.filePath,\n options.objectsDir,\n simpleClassName,\n );\n}\n\nfunction mergeImportStatements(\n importStatements: Array<string | undefined>,\n): string {\n return Array.from(new Set(importStatements.filter(Boolean))).join('\\n');\n}\n\nfunction findObjectDefByRegistryKey(\n manifest: SmartObjectManifest,\n className: string,\n): SmartObjectDefinition | undefined {\n return (\n manifest.objects[className] ??\n Object.values(manifest.objects).find(\n (objectDef) => objectDef.className === extractSimpleClassName(className),\n )\n );\n}\n\n/**\n * Collect property names marked `@field({ readonly: true })` for an object, so\n * the generated write surfaces can strip them from request bodies (#1540, 2b).\n */\nfunction collectReadonlyFieldNames(objectDef: SmartObjectDefinition): string[] {\n const fields = objectDef.fields || {};\n const names: string[] = [];\n for (const [name, def] of Object.entries(fields)) {\n const meta = (def as { _meta?: Record<string, unknown> })._meta;\n if (\n (def as { readonly?: boolean }).readonly === true ||\n meta?.readonly === true\n ) {\n names.push(name);\n }\n }\n return names;\n}\n\n/**\n * Resolve the optional `@smrt({ api: { writable: [...] } })` allowlist. When\n * present, only these fields may be set from a create/update request body.\n */\nfunction getApiWritableAllowlist(apiConfig: unknown): string[] | null {\n const config = getApiConfigObject(apiConfig);\n return Array.isArray(config?.writable) ? config.writable : null;\n}\n\n/**\n * Emit the mass-assignment guard helper injected into generated route files\n * (#1540, 2b). It strips framework/server-managed (`id`, `tenantId`,\n * timestamps, `_`-prefixed) and `@field({ readonly: true })` fields from\n * request bodies, and — when a `writable` allowlist is configured — intersects\n * with it. Applied to every generated `create`/`update` handler.\n */\nfunction generateWritablePolicyHelper(\n objectDef: SmartObjectDefinition,\n): string {\n const readonly = collectReadonlyFieldNames(objectDef);\n const writable = getApiWritableAllowlist(objectDef.decoratorConfig?.api);\n\n return `\n// Mass-assignment guard (#1540): strip framework/server-managed + read-only\n// fields from create/update request bodies before they reach the model.\nconst WRITABLE_ALLOWLIST: string[] | null = ${\n writable ? JSON.stringify(writable) : 'null'\n };\nconst READONLY_FIELDS: string[] = ${JSON.stringify(readonly)};\nconst SERVER_MANAGED_FIELDS = [\n 'id',\n 'tenantId',\n 'tenant_id',\n 'createdAt',\n 'created_at',\n 'updatedAt',\n 'updated_at',\n];\n\nfunction applyWritablePolicy(data: unknown): Record<string, unknown> {\n if (!data || typeof data !== 'object') return {};\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n if (key.startsWith('_')) continue;\n if (SERVER_MANAGED_FIELDS.includes(key)) continue;\n if (READONLY_FIELDS.includes(key)) continue;\n if (WRITABLE_ALLOWLIST && !WRITABLE_ALLOWLIST.includes(key)) continue;\n result[key] = value;\n }\n return result;\n}\n`;\n}\n\n/**\n * Resolve the `@smrt({ api: { public } })` posture (fail-closed default).\n * - `false`/unset → all routes require an authenticated principal.\n * - `true` → all routes are public.\n * - `'read'` → reads are public, mutations still require auth.\n */\nfunction getApiPublicAccess(apiConfig: unknown): boolean | 'read' {\n const config = getApiConfigObject(apiConfig);\n const value = config?.public;\n if (value === true || value === 'read') {\n return value;\n }\n return false;\n}\n\nfunction toFieldColumnAlias(fieldName: string): string {\n return fieldName\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n .replace(/([a-z\\d])([A-Z])/g, '$1_$2')\n .replace(/[-\\s]+/g, '_')\n .toLowerCase();\n}\n\nfunction manifestObjectKey(objectDef: SmartObjectDefinition): string {\n return objectDef.qualifiedName ?? objectDef.className;\n}\n\nfunction manifestObjectExtends(\n objectDef: SmartObjectDefinition,\n parent: SmartObjectDefinition,\n): boolean {\n const extendsQualified = objectDef.extendsQualified;\n const extendsName = objectDef.extends;\n return (\n (typeof extendsQualified === 'string' &&\n extendsQualified === parent.qualifiedName) ||\n (typeof extendsName === 'string' &&\n (extendsName === parent.className ||\n extendsName === parent.qualifiedName))\n );\n}\n\nfunction findManifestParent(\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n): SmartObjectDefinition | undefined {\n return Object.values(manifest.objects).find((candidate) =>\n manifestObjectExtends(objectDef, candidate),\n );\n}\n\nfunction collectStiHierarchyObjects(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): SmartObjectDefinition[] {\n if (!manifest) return [objectDef];\n\n let cursor: SmartObjectDefinition | undefined = objectDef;\n let stiBase: SmartObjectDefinition | undefined =\n cursor.decoratorConfig?.tableStrategy === 'sti' ? cursor : undefined;\n while (cursor) {\n const parent = findManifestParent(cursor, manifest);\n if (!parent) break;\n if (parent.decoratorConfig?.tableStrategy === 'sti') {\n stiBase = parent;\n }\n cursor = parent;\n }\n\n if (!stiBase) return [objectDef];\n\n const members = new Map<string, SmartObjectDefinition>();\n const visit = (current: SmartObjectDefinition) => {\n const key = manifestObjectKey(current);\n if (members.has(key)) return;\n members.set(key, current);\n for (const candidate of Object.values(manifest.objects)) {\n if (candidate === current) continue;\n if (manifestObjectExtends(candidate, current)) {\n visit(candidate);\n }\n }\n };\n\n visit(stiBase);\n return [...members.values()];\n}\n\nfunction collectReadPermissionFields(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): Array<[string, string]> {\n const result: Array<[string, string]> = [];\n const seen = new Set<string>();\n for (const member of collectStiHierarchyObjects(objectDef, manifest)) {\n for (const [name, def] of Object.entries(member.fields ?? {})) {\n const permission =\n typeof def.readPermission === 'string'\n ? def.readPermission\n : typeof def._meta?.readPermission === 'string'\n ? def._meta.readPermission\n : undefined;\n if (!permission) continue;\n for (const fieldName of [name, toFieldColumnAlias(name)]) {\n const key = `${fieldName}\\0${permission}`;\n if (seen.has(key)) continue;\n seen.add(key);\n result.push([fieldName, permission]);\n }\n }\n }\n return result;\n}\n\n/**\n * Emit the fail-closed authorization guard injected into generated route files\n * (#1540, 2c). Every generated CRUD/action handler calls `requireRouteAuth`,\n * which throws 401 unless the route is `public` or `locals` carries an\n * authenticated principal. Mirrors the knowledge-route `isKnowledgeAdmin`\n * precedent. Mutating verbs require auth even when reads are public.\n */\nfunction generateAuthGuardHelper(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): string {\n const publicAccess = getApiPublicAccess(objectDef.decoratorConfig?.api);\n const readPermissionFields = collectReadPermissionFields(objectDef, manifest);\n\n return `\n// Fail-closed authorization (#1540): generated routes require an authenticated\n// principal on \\`locals\\` unless explicitly marked \\`@smrt({ api: { public } })\\`.\nconst PUBLIC_ACCESS: boolean | 'read' = ${JSON.stringify(publicAccess)};\nconst READ_PERMISSION_FIELDS: Array<[string, string]> = ${JSON.stringify(readPermissionFields)};\n\ninterface PublicJsonOptions {\n permissions?: Iterable<string>;\n}\n\nfunction hasAuthenticatedPrincipal(locals: unknown): boolean {\n if (!locals || typeof locals !== 'object') return false;\n const l = locals as Record<string, unknown>;\n // Only a resolved, object-shaped principal counts. We intentionally do NOT\n // treat \\`locals.auth\\` as a signal: Auth.js/SvelteKit put a callable\n // \\`auth()\\` helper on every request (including anonymous ones), so honoring\n // it would fail OPEN. Booleans don't count either (no convention sets\n // \\`locals.user = true\\`); the only boolean accepted is the explicit\n // \\`smrtAuth\\` opt-in marker.\n const isResolvedPrincipal = (v: unknown) =>\n typeof v === 'object' && v !== null;\n return (\n isResolvedPrincipal(l.user) ||\n isResolvedPrincipal(l.session) ||\n l.smrtAuth === true\n );\n}\n\nfunction requireRouteAuth(locals: unknown, mutating: boolean): void {\n if (PUBLIC_ACCESS === true) return;\n if (PUBLIC_ACCESS === 'read' && !mutating) return;\n if (!hasAuthenticatedPrincipal(locals)) {\n throw error(401, 'Authentication required');\n }\n}\n\nfunction getPublicJsonOptions(locals: unknown): PublicJsonOptions {\n const l = readJsonRecord(locals);\n const permissions = l.permissions ?? l.permissionSet ?? l.smrtPermissions;\n if (Array.isArray(permissions) || permissions instanceof Set) {\n return { permissions };\n }\n return {};\n}\n\nfunction hasReadPermission(\n options: PublicJsonOptions,\n permission: string,\n): boolean {\n if (!options.permissions) return false;\n for (const granted of options.permissions) {\n if (granted === permission) return true;\n }\n return false;\n}\n\nfunction applyReadPermissionRedaction(\n value: unknown,\n options: PublicJsonOptions,\n seen: WeakSet<object> = new WeakSet(),\n): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (seen.has(value)) return null;\n if (hasPublicJson(value)) {\n seen.add(value);\n return applyReadPermissionRedaction(\n value.toPublicJSON(options),\n options,\n seen,\n );\n }\n if (Array.isArray(value)) {\n seen.add(value);\n return value.map((entry) =>\n applyReadPermissionRedaction(entry, options, seen),\n );\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n seen.add(value);\n const out = { ...(value as Record<string, unknown>) };\n const metaData =\n out._meta_data && typeof out._meta_data === 'object'\n ? { ...(out._meta_data as Record<string, unknown>) }\n : null;\n for (const [fieldName, permission] of READ_PERMISSION_FIELDS) {\n if (hasReadPermission(options, permission)) continue;\n delete out[fieldName];\n if (metaData) delete metaData[fieldName];\n }\n if (metaData) out._meta_data = metaData;\n for (const [key, entry] of Object.entries(out)) {\n out[key] = applyReadPermissionRedaction(entry, options, seen);\n }\n return out;\n}\n\n// Sensitive-field-safe serialization for custom-action results (#1540): a\n// custom method may return a SmrtObject (or one nested in an array/plain\n// object), so recurse and route each through toPublicJSON() rather than letting\n// JSON.stringify call toJSON(). Non-plain instances (Date, etc.) and primitives\n// pass through; a cycle guard prevents infinite loops.\ninterface PublicJsonSource {\n toPublicJSON(options?: PublicJsonOptions): unknown;\n}\n\nfunction hasPublicJson(value: object): value is PublicJsonSource {\n return (\n 'toPublicJSON' in value &&\n typeof (value as { toPublicJSON?: unknown }).toPublicJSON === 'function'\n );\n}\n\nfunction readJsonRecord(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return {};\n return value as Record<string, unknown>;\n}\n\nfunction toPublicResult(\n value: unknown,\n options: PublicJsonOptions,\n seen: WeakSet<object> = new WeakSet(),\n): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (seen.has(value)) return null;\n if (hasPublicJson(value)) {\n seen.add(value);\n return toPublicResult(value.toPublicJSON(options), options, seen);\n }\n if (Array.isArray(value)) {\n seen.add(value);\n return value.map((entry) => toPublicResult(entry, options, seen));\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(\n value as Record<string, unknown>,\n )) {\n out[key] = toPublicResult(entry, options, seen);\n }\n return out;\n}\n`;\n}\n\n/**\n * Whether an object is `@TenantScoped` (carries a `tenantScoped` config in its\n * `@smrt()`/`@TenantScoped()` decorator). Drives tenant-context establishment in\n * generated routes (#1540, facet 2).\n */\nfunction isTenantScoped(objectDef: SmartObjectDefinition): boolean {\n return !!objectDef.decoratorConfig?.tenantScoped;\n}\n\n/**\n * Emit the tenant-context establishment + fail-closed read-scope helpers for\n * `@TenantScoped` objects (#1540 facet 2; #1782). Generated handlers call\n * `establishTenantContext(locals)` after the auth guard so every query runs\n * inside the tenant context derived from the authenticated principal — engaging\n * the `@TenantScoped` interceptors that auto-filter by tenant.\n *\n * `establishTenantContext` reads the canonical `locals.tenantId` set by the\n * smrt-users auth hook (with `user`/`session` fallbacks). It is a no-op when a\n * context is already active (e.g. an upstream tenancy handle) or when the\n * request carries no tenant (anonymous / global principal).\n *\n * When no context could be established, an `optional`-mode read would otherwise\n * fall through the interceptor UNFILTERED and return rows from ALL tenants (the\n * interceptor only hard-fails `required` mode) — the #1782 leak. So read\n * handlers additionally call `tenantReadScope()`, which restricts reads to\n * NULL-tenant (global) rows only when tenancy is enabled but no context is\n * active. Both helpers are imported only for tenant-scoped routes, which already\n * depend on `@happyvertical/smrt-tenancy`.\n */\nfunction generateTenantContextHelper(): string {\n return `\nimport { enterTenantContext, getCurrentTenant, hasTenantContext, isSuperAdminBypass, isTenancyEnabled } from '@happyvertical/smrt-tenancy';\n\nfunction establishTenantContext(locals: unknown): void {\n if (hasTenantContext()) return;\n if (!locals || typeof locals !== 'object') return;\n const l = locals as Record<string, unknown>;\n const user = l.user as Record<string, unknown> | undefined;\n const session = l.session as Record<string, unknown> | undefined;\n const tenantId = l.tenantId ?? user?.tenantId ?? session?.tenantId;\n if (typeof tenantId === 'string' && tenantId) {\n enterTenantContext({ tenantId });\n }\n}\n\n// Fail-closed read scope (#1782): a public/anonymous read on a @TenantScoped\n// model has no tenant context, so the tenancy interceptor (optional mode) would\n// pass the query through UNFILTERED and return every tenant's rows. When tenancy\n// is enabled but no context was established, restrict reads to NULL-tenant\n// (global) rows only — mirroring the dispatch resolver + _changes convention:\n// tenancy enforced with no context => global rows only. Returns undefined when a\n// context is active (the interceptor filters by it) or tenancy is disabled.\nfunction tenantReadScope(): { tenantId: null } | undefined {\n return isTenancyEnabled() && !hasTenantContext()\n ? { tenantId: null }\n : undefined;\n}\n\n// Custom GET actions that accept a single options object may perform their\n// own reads instead of calling generated list/get handlers. Give those actions\n// an explicit tenant scope: active tenant when present, NULL-tenant global rows\n// when tenancy is enabled but no tenant context exists. Super-admin bypass\n// stays unscoped so admin callers keep deliberate cross-tenant access.\nfunction tenantReadOptionsScope(): { tenantId: string | null } | undefined {\n if (!isTenancyEnabled() || isSuperAdminBypass()) {\n return undefined;\n }\n return { tenantId: getCurrentTenant()?.tenantId ?? null };\n}\n`;\n}\n\n/**\n * The per-handler guard preamble: the fail-closed auth check (#1540 2c) plus, for\n * tenant-scoped objects, tenant-context establishment (#1540 facet 2).\n */\nfunction routeGuardPreamble(\n objectDef: SmartObjectDefinition,\n mutating: boolean,\n): string {\n const lines = [` requireRouteAuth(locals, ${mutating});`];\n if (isTenantScoped(objectDef)) {\n lines.push(' establishTenantContext(locals);');\n }\n return lines.join('\\n');\n}\n\nfunction normalizeApiHttpMethod(method?: string): ApiHttpMethod {\n switch (method?.toUpperCase()) {\n case 'GET':\n case 'POST':\n case 'PUT':\n case 'PATCH':\n case 'DELETE':\n return method.toUpperCase() as ApiHttpMethod;\n default:\n return 'POST';\n }\n}\n\n/**\n * Convert a camelCase / PascalCase method name to a kebab-case URL segment.\n * Examples:\n * discoverFromUrl -> discover-from-url\n * XMLExport -> xml-export\n * URL -> url\n * a1B2c3 -> a1-b2c3\n *\n * Known limitation: consecutive uppercase acronyms aren't split apart, e.g.\n * `HTMLAPIExport` becomes `htmlapi-export` rather than `html-api-export`.\n * Override with an explicit `api.routes[name].path` if you need a specific\n * segmentation.\n */\nexport function methodNameToKebab(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .toLowerCase();\n}\n\nfunction normalizeCustomRoutePath(\n actionName: string,\n path?: string,\n options: { kebabRoutes?: boolean } = {},\n): string[] {\n // Explicit path override always wins, and is used verbatim (split on /).\n if (path) {\n const segments = path\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean);\n if (segments.length > 0) {\n return segments;\n }\n }\n\n const segment = options.kebabRoutes\n ? methodNameToKebab(actionName)\n : actionName;\n return [segment];\n}\n\nfunction extractRoutePathParamNames(pathSegments: string[]): string[] {\n return pathSegments\n .filter((segment) => /^\\[[^\\]]+\\]$/.test(segment))\n .map((segment) => segment.slice(1, -1))\n .filter(Boolean);\n}\n\nexport function resolveApiActionRouteConfig(\n actionName: string,\n actionDef: { isStatic?: boolean },\n apiConfig: unknown,\n routeOptions: { kebabRoutes?: boolean } = {},\n defaultScope: 'item' | 'collection' = actionDef.isStatic\n ? 'collection'\n : 'item',\n): ResolvedApiActionRouteConfig {\n const config = getApiConfigObject(apiConfig);\n const routeConfig: ApiCustomRouteConfig | undefined =\n config?.routes?.[actionName];\n\n const pathSegments = normalizeCustomRoutePath(\n actionName,\n routeConfig?.path,\n routeOptions,\n );\n\n return {\n scope: routeConfig?.scope || defaultScope,\n method: normalizeApiHttpMethod(routeConfig?.method),\n pathSegments,\n pathParamNames: extractRoutePathParamNames(pathSegments),\n };\n}\n\nfunction buildRouteHandlerArgs(\n includeParams: boolean,\n includeRequest: boolean,\n): string {\n // `locals` is always destructured so the fail-closed auth guard (#1540) can\n // inspect the authenticated principal.\n const args: string[] = ['locals'];\n\n if (includeParams) {\n args.push('params');\n }\n\n if (includeRequest) {\n args.push('request');\n }\n\n return `{ ${args.join(', ')} }`;\n}\n\nfunction buildPathParamsObjectLiteral(pathParamNames: string[]): string {\n if (pathParamNames.length === 0) {\n return '{}';\n }\n\n return `{\n${pathParamNames\n .map(\n (paramName) =>\n ` ${buildObjectTypeProperty(paramName)}: ${buildRouteParamAccess(paramName)},`,\n )\n .join('\\n')}\n }`;\n}\n\nfunction hasSingleOptionsParameter(actionDef: MethodDefinition): boolean {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n\n return parameters.length === 1 && parameters[0]?.name === 'options';\n}\n\nfunction buildActionInvocationArgs(\n actionDef: MethodDefinition,\n optionsIdentifier = 'options',\n): string[] {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n\n if (parameters.length === 0) {\n return [];\n }\n\n if (hasSingleOptionsParameter(actionDef)) {\n return [optionsIdentifier];\n }\n\n return parameters.map((parameter) =>\n buildOptionsPropertyAccess(parameter.name),\n );\n}\n\nfunction buildObjectTypeProperty(propertyName: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyName)) {\n return propertyName;\n }\n\n return JSON.stringify(propertyName);\n}\n\nfunction buildSingleQuotedStringLiteral(value: string): string {\n return `'${value.replaceAll('\\\\', '\\\\\\\\').replaceAll(\"'\", \"\\\\'\")}'`;\n}\n\nfunction buildRouteParamAccess(paramName: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(paramName)) {\n return `params.${paramName}`;\n }\n\n return `params[${buildSingleQuotedStringLiteral(paramName)}]`;\n}\n\nfunction buildActionTargetTypeExpression(\n targetTypeName: string,\n isStatic: boolean,\n): string {\n return isStatic ? `(typeof ${targetTypeName})` : targetTypeName;\n}\n\nfunction buildActionArgsTypeAlias(\n actionName: string,\n targetTypeName: string,\n isStatic: boolean,\n): string {\n const targetTypeExpression = buildActionTargetTypeExpression(\n targetTypeName,\n isStatic,\n );\n return ` type ActionArgs = Parameters<${targetTypeExpression}[${buildSingleQuotedStringLiteral(actionName)}]>;`;\n}\n\nfunction buildActionOptionsTypeAlias(actionDef: MethodDefinition): string {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n\n if (parameters.length <= 1 && parameters[0]?.name === 'options') {\n return '';\n }\n\n return [\n ' type ActionOptions = {',\n ...parameters.map(\n (parameter, index) =>\n ` ${buildObjectTypeProperty(parameter.name)}: ActionArgs[${index}];`,\n ),\n ' };',\n ].join('\\n');\n}\n\nfunction buildActionOptionsLoad(\n actionName: string,\n actionDef: MethodDefinition,\n routeConfig: ResolvedApiActionRouteConfig,\n targetTypeName: string,\n isStatic: boolean,\n): string {\n const parameters = Array.isArray(actionDef.parameters)\n ? actionDef.parameters\n : [];\n if (parameters.length === 0) {\n return '';\n }\n\n const hasPathParams = routeConfig.pathParamNames.length > 0;\n const pathParamsObjectLiteral = buildPathParamsObjectLiteral(\n routeConfig.pathParamNames,\n );\n const isSingleOptionsParameter =\n parameters.length === 1 && parameters[0]?.name === 'options';\n const lines = [\n buildActionArgsTypeAlias(actionName, targetTypeName, isStatic),\n ];\n const optionsTypeAlias = buildActionOptionsTypeAlias(actionDef);\n if (optionsTypeAlias) {\n lines.push(optionsTypeAlias);\n }\n\n if (routeConfig.method === 'GET') {\n if (hasPathParams) {\n lines.push(\n ` const pathParams = ${pathParamsObjectLiteral};`,\n ' const options = {',\n ' ...Object.fromEntries(new URL(request.url).searchParams.entries()),',\n ' ...pathParams,',\n ` } as ${isSingleOptionsParameter ? 'ActionArgs[0]' : 'ActionOptions'};`,\n '',\n );\n } else {\n lines.push(\n ' const options = Object.fromEntries(',\n ' new URL(request.url).searchParams.entries(),',\n ` ) as ${isSingleOptionsParameter ? 'ActionArgs[0]' : 'ActionOptions'};`,\n '',\n );\n }\n return lines.join('\\n');\n }\n\n if (hasPathParams) {\n lines.push(\n ` const pathParams = ${pathParamsObjectLiteral};`,\n ' const body: unknown = await request.json();',\n ' const options = {',\n ' ...readJsonRecord(body),',\n ' ...pathParams,',\n ` } as ${isSingleOptionsParameter ? 'ActionArgs[0]' : 'ActionOptions'};`,\n '',\n );\n return lines.join('\\n');\n }\n\n lines.push(' const body: unknown = await request.json();');\n if (isSingleOptionsParameter) {\n lines.push(' const options = body as ActionArgs[0];', '');\n } else {\n lines.push(' const options = readJsonRecord(body) as ActionOptions;', '');\n }\n return lines.join('\\n');\n}\n\nfunction buildOptionsPropertyAccess(propertyName: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyName)) {\n return `options.${propertyName}`;\n }\n\n const escapedPropertyName = propertyName\n .replaceAll('\\\\', '\\\\\\\\')\n .replaceAll(\"'\", \"\\\\'\");\n\n return `options['${escapedPropertyName}']`;\n}\n\nfunction buildActionInvocationExpression(\n receiverExpression: string,\n actionName: string,\n invocationArgs: string[],\n): string {\n const singleLineInvocation = `await ${receiverExpression}.${actionName}(${invocationArgs.join(', ')})`;\n const singleLineResultAssignment = ` const result = ${singleLineInvocation};`;\n\n if (singleLineResultAssignment.length <= 100) {\n return singleLineInvocation;\n }\n\n return [\n `await ${receiverExpression}.${actionName}(`,\n ...invocationArgs.map((argument) => ` ${argument},`),\n ' )',\n ].join('\\n');\n}\n\nfunction buildScopedOptionsForTenantRead(\n actionDef: MethodDefinition,\n routeConfig: ResolvedApiActionRouteConfig,\n tenantScoped: boolean,\n): { source: string; optionsIdentifier: string } {\n // The generator can only pass tenant read scope to methods that expose an\n // options object. Zero-argument methods that perform their own raw reads must\n // infer tenant scope in their implementation or adopt the options contract.\n if (\n !tenantScoped ||\n routeConfig.method !== 'GET' ||\n !hasSingleOptionsParameter(actionDef)\n ) {\n return { source: '', optionsIdentifier: 'options' };\n }\n\n return {\n source: ` const readScope = tenantReadOptionsScope();\n const scopedOptions = readScope\n ? ({ ...options, ...readScope } as ActionArgs[0])\n : options;\n\n`,\n optionsIdentifier: 'scopedOptions',\n };\n}\n\nfunction findItemClassRegistryKey(\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n): string {\n const itemObject = resolveCollectionItemObject(manifest, objectDef);\n if (!itemObject) {\n return resolveCollectionItemTypeName(manifest, objectDef) || className;\n }\n\n const manifestMatch = Object.entries(manifest.objects).find(\n ([, manifestObject]) => manifestObject === itemObject,\n );\n return manifestMatch?.[0] || itemObject.className;\n}\n\nfunction groupCustomActionRoutes(\n actionSpecs: Array<{\n routeDir: string;\n spec: GeneratedActionRouteSpec;\n }>,\n): Map<string, GeneratedActionRouteSpec[]> {\n const groupedRoutes = new Map<string, GeneratedActionRouteSpec[]>();\n\n for (const { routeDir, spec } of actionSpecs) {\n const existing = groupedRoutes.get(routeDir) || [];\n const duplicateMethod = existing.find(\n (candidate) => candidate.routeConfig.method === spec.routeConfig.method,\n );\n\n if (duplicateMethod) {\n throw new Error(\n `Duplicate custom API route handler for ${routeDir} (${spec.routeConfig.method}). ` +\n `Methods ${duplicateMethod.actionName} and ${spec.actionName} resolve to the same generated route.`,\n );\n }\n\n existing.push(spec);\n groupedRoutes.set(routeDir, existing);\n }\n\n return groupedRoutes;\n}\n\n/**\n * Generates SvelteKit API routes from manifest\n */\nexport async function generateSvelteKitRoutes(\n projectRoot: string,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<void> {\n if (!options.enabled) return;\n\n console.log('[smrt] Generating SvelteKit routes...');\n\n clearGeneratedRouteFiles(join(projectRoot, options.routesDir));\n clearGeneratedKnowledgeRoute(projectRoot, options);\n\n // Generate centralized configuration file first (if it doesn't exist)\n await generateSmrtConfigFile(projectRoot, manifest, options);\n\n const generatedRoutePaths: string[] = [];\n let generatedCount = 0;\n let skippedCollections = 0;\n for (const [className, objectDef] of Object.entries(manifest.objects)) {\n if (isCollectionManifestClass(manifest, objectDef)) {\n const collectionRoutePaths = await generateCollectionRoutesForObject(\n projectRoot,\n className,\n objectDef,\n manifest,\n options,\n );\n generatedRoutePaths.push(...collectionRoutePaths);\n if (collectionRoutePaths.length > 0) {\n generatedCount++;\n } else {\n console.log(\n `[smrt] Skipping ${className} - no collection API routes to generate`,\n );\n skippedCollections++;\n }\n continue;\n }\n generatedRoutePaths.push(\n ...(await generateRoutesForObject(\n projectRoot,\n className,\n objectDef,\n manifest,\n options,\n )),\n );\n generatedCount++;\n }\n\n if (options.knowledge?.api?.enabled) {\n generatedRoutePaths.push(generateKnowledgeRoute(projectRoot, options));\n }\n\n // Batch write contract route (#1759): {routesDir}/sync/apply/+server.ts.\n if (generateSyncApplyRoute(projectRoot, manifest, options)) {\n generatedRoutePaths.push(\n join(projectRoot, options.routesDir, 'sync', 'apply', '+server.ts'),\n );\n }\n // Change-feed route (#1758) — cleanup rides clearGeneratedRouteFiles above.\n if (generateChangesRoute(projectRoot, manifest, options)) {\n generatedRoutePaths.push(\n join(projectRoot, options.routesDir, '_changes', '+server.ts'),\n );\n }\n // Live change-signal SSE route (#1763) — cleanup rides the sweep above.\n if (\n generateEventsRoute(\n projectRoot,\n manifest,\n options,\n computeWebManifestHash(manifest),\n )\n ) {\n generatedRoutePaths.push(\n join(projectRoot, options.routesDir, '_events', '+server.ts'),\n );\n }\n\n // Ignore only the concrete route files generated in this pass. This keeps\n // handwritten handlers below routesDir visible to Git.\n updateGitignore(projectRoot, options, generatedRoutePaths);\n\n const skippedMsg =\n skippedCollections > 0\n ? ` (skipped ${skippedCollections} collection classes)`\n : '';\n console.log(\n `[smrt] Generated routes for ${generatedCount} SMRT objects${skippedMsg}`,\n );\n}\n\nfunction clearGeneratedRouteFiles(routesRoot: string): void {\n if (!existsSync(routesRoot)) {\n return;\n }\n\n for (const entry of readdirSync(routesRoot, { withFileTypes: true })) {\n const entryPath = join(routesRoot, entry.name);\n\n if (entry.isDirectory()) {\n clearGeneratedRouteFiles(entryPath);\n continue;\n }\n\n if (!entry.isFile() || entry.name !== '+server.ts') {\n continue;\n }\n\n const fileContent = readFileSync(entryPath, 'utf-8');\n if (fileContent.startsWith(AUTO_GENERATED_ROUTE_HEADER)) {\n unlinkSync(entryPath);\n }\n }\n}\n\nfunction knowledgeRouteDir(projectRoot: string, options: SvelteKitOptions) {\n const basePath = String(\n options.knowledge?.api?.basePath || '/__smrt/knowledge',\n );\n const routeRoot = svelteKitRouteRoot(options.routesDir);\n const segments = basePath\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean);\n\n return join(projectRoot, routeRoot, ...segments);\n}\n\nfunction svelteKitRouteRoot(routesDir: string): string {\n const normalized = routesDir.replaceAll('\\\\', '/').replace(/\\/+$/, '');\n const marker = '/routes';\n const markerIndex = normalized.indexOf(marker);\n\n if (normalized === 'src/routes' || normalized.endsWith('/routes')) {\n return normalized;\n }\n if (markerIndex !== -1) {\n return normalized.slice(0, markerIndex + marker.length);\n }\n return 'src/routes';\n}\n\nfunction clearGeneratedKnowledgeRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): void {\n const routeDir = knowledgeRouteDir(projectRoot, options);\n const routePath = join(routeDir, '+server.ts');\n if (!existsSync(routePath)) return;\n\n const content = readFileSync(routePath, 'utf-8');\n if (content.startsWith(AUTO_GENERATED_ROUTE_HEADER)) {\n unlinkSync(routePath);\n }\n}\n\nfunction generateKnowledgeRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): string {\n const routeDir = knowledgeRouteDir(projectRoot, options);\n const route = generateKnowledgeRouteTemplate(\n options.knowledge ?? {},\n readKnowledgeRouteArtifact(projectRoot),\n );\n return writeRoute(routeDir, '+server.ts', route);\n}\n\nfunction readKnowledgeRouteArtifact(\n projectRoot: string,\n): Record<string, unknown> | null {\n for (const relativePath of [\n '.smrt/smrt-knowledge.json',\n 'dist/smrt-knowledge.json',\n ]) {\n const fullPath = join(projectRoot, relativePath);\n if (!existsSync(fullPath)) continue;\n try {\n return JSON.parse(readFileSync(fullPath, 'utf-8'));\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Generates SMRT object registration file\n * This file imports all SMRT objects to trigger their @smrt() decorators\n */\nasync function generateRegistrationFile(\n projectRoot: string,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<void> {\n const configPath = options.configPath || 'src/lib/server';\n const configDir = join(projectRoot, configPath);\n const registrationFilePath = join(configDir, 'smrt-register.ts');\n\n // Group objects by package for efficient imports\n const localObjects: Array<[string, (typeof manifest.objects)[string]]> = [];\n const packageObjects = new Map<\n string,\n { classNames: string[]; hasCollectionImport: boolean }\n >();\n\n for (const [className, objectDef] of Object.entries(manifest.objects)) {\n if (isLocalObject(projectRoot, objectDef)) {\n // Local object (source in project, not node_modules) - use $lib path\n localObjects.push([className, objectDef]);\n } else if (objectDef.packageName) {\n // External package - group by package name\n const packageEntry = packageObjects.get(objectDef.packageName) || {\n classNames: [],\n hasCollectionImport: false,\n };\n\n if (isCollectionManifestClass(manifest, objectDef)) {\n packageEntry.hasCollectionImport = true;\n } else {\n packageEntry.classNames.push(className);\n }\n\n packageObjects.set(objectDef.packageName, packageEntry);\n } else {\n // No package name and not local - treat as local fallback\n localObjects.push([className, objectDef]);\n }\n }\n\n // Generate imports for local objects\n // Issue #870: Extract simple class names from qualified names\n const localNamedImports = new Map<string, string[]>();\n const localSideEffectImports = new Set<string>();\n\n for (const [className, objectDef] of localObjects) {\n const simpleClassName = extractSimpleClassName(className);\n\n // Calculate direct relative path from the configDir where smrt-register.ts lives\n // to the actual source file of the object.\n let importPath = '';\n if (objectDef.filePath) {\n const relativeToConfig = relative(configDir, objectDef.filePath);\n const normalized = relativeToConfig\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|js|tsx|jsx)$/, '');\n importPath = normalized.startsWith('.') ? normalized : `./${normalized}`;\n } else {\n // Fallback for missing file paths\n importPath = getSvelteKitImportPath(\n projectRoot,\n undefined,\n options.objectsDir,\n simpleClassName,\n );\n }\n\n if (isCollectionManifestClass(manifest, objectDef)) {\n localSideEffectImports.add(importPath);\n continue;\n }\n\n const existing = localNamedImports.get(importPath) ?? [];\n existing.push(simpleClassName);\n localNamedImports.set(importPath, existing);\n }\n\n const localImports = [\n ...Array.from(localNamedImports.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([importPath, simpleNames]) => {\n const sortedNames = simpleNames.sort((a, b) => a.localeCompare(b));\n return `import { ${sortedNames.join(', ')} } from '${importPath}';`;\n }),\n ...Array.from(localSideEffectImports.values())\n .sort((a, b) => a.localeCompare(b))\n .map((importPath) => `import '${importPath}';`),\n ].join('\\n');\n\n // Generate imports for external packages\n // Issue #870: Extract simple class names from qualified names\n const packageImports = Array.from(packageObjects.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .flatMap(([packageName, packageEntry]) => {\n const imports: string[] = [];\n\n if (packageEntry.hasCollectionImport) {\n imports.push(`import '${packageName}';`);\n }\n\n if (packageEntry.classNames.length > 0) {\n // Extract simple class names for valid import syntax\n const simpleNames = packageEntry.classNames\n .map(extractSimpleClassName)\n .sort((a, b) => a.localeCompare(b));\n imports.push(\n `import { ${simpleNames.join(', ')} } from '${packageName}';`,\n );\n }\n\n return imports;\n })\n .join('\\n');\n\n const imports = [packageImports, localImports].filter(Boolean).join('\\n');\n const registrations = Object.entries(manifest.objects)\n .map(([className, objectDef]) => {\n if (isCollectionManifestClass(manifest, objectDef)) {\n // Importing the collection class is enough to trigger its decorator.\n // Explicit package-qualified re-registration only applies to object\n // classes, because ObjectRegistry.register() is object-only.\n return null;\n }\n\n const simpleClassName = extractSimpleClassName(className);\n const localObject = isLocalObject(projectRoot, objectDef);\n const packageName = getRegistrationPackageName(\n manifest,\n objectDef,\n localObject,\n );\n\n if (!packageName) {\n return null;\n }\n\n const packageNameLiteral = toSingleQuotedStringLiteral(packageName);\n const singleLineRegistration = `ObjectRegistry.register(${simpleClassName}, { name: '${simpleClassName}', packageName: ${packageNameLiteral} });`;\n\n if (singleLineRegistration.length <= BIOME_LINE_WIDTH) {\n return singleLineRegistration;\n }\n\n return [\n `ObjectRegistry.register(${simpleClassName}, {`,\n ` name: '${simpleClassName}',`,\n ` packageName: ${packageNameLiteral},`,\n `});`,\n ].join('\\n');\n })\n .filter((registration): registration is string => registration !== null)\n .join('\\n');\n\n const registrationContent = `/**\n * Auto-generated SMRT object registration\n * DO NOT EDIT - changes will be overwritten\n *\n * Importing these modules triggers their @smrt() decorators, which perform\n * the initial registration. The explicit re-registration below is intentional:\n * it upgrades bundled runtimes to deterministic qualified registrations.\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n${imports}\n\n// Re-register imported objects with explicit package names for bundled runtimes\n${registrations}\n`;\n\n // Create directory if it doesn't exist\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(registrationFilePath, registrationContent, 'utf-8');\n console.log(`[smrt] Generated registration file: ${registrationFilePath}`);\n}\n\n/**\n * Generates centralized SMRT configuration file\n * Only creates if file doesn't exist (preserves user customizations)\n */\nasync function generateSmrtConfigFile(\n projectRoot: string,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<void> {\n const configPath = options.configPath || 'src/lib/server';\n const configFileName = options.configFileName || 'smrt.ts';\n const configDir = join(projectRoot, configPath);\n const configFilePath = join(configDir, configFileName);\n\n // Always generate registration file (it gets overwritten)\n await generateRegistrationFile(projectRoot, manifest, options);\n\n // Don't overwrite existing config file\n if (existsSync(configFilePath)) {\n console.log('[smrt] Config file already exists, skipping generation');\n return;\n }\n\n const configContent = `/**\n * Centralized SMRT configuration with per-object overrides\n * Generated by @smrt/core vite plugin\n *\n * Most objects will use the default configuration.\n * Add entries to \\`objectOverrides\\` for objects that need different backends.\n */\n\n// Import SMRT objects to register them via @smrt() decorators\nimport './smrt-register.js';\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtGetRequestScopedDatabase:\n | (() => SmrtClassOptions['db'] | undefined)\n | undefined;\n}\n\n/**\n * Per-object configuration overrides\n * Define specific backends for objects that differ from project defaults\n *\n * @example\n * const objectOverrides: Record<string, Partial<SmrtClassOptions>> = {\n * // Analytics uses a separate PostgreSQL database\n * Analytics: {\n * db: {\n * url: process.env.ANALYTICS_DATABASE_URL!,\n * type: 'postgres'\n * }\n * },\n *\n * // AuditLog uses dedicated database with no AI\n * AuditLog: {\n * db: {\n * url: process.env.AUDIT_DATABASE_URL!,\n * type: 'postgres'\n * },\n * ai: undefined\n * },\n *\n * // Cache uses REST adapter (e.g., Redis)\n * Cache: {\n * persistence: {\n * type: 'rest',\n * baseUrl: process.env.REDIS_URL!\n * }\n * }\n * };\n */\nconst objectOverrides: Record<string, Partial<SmrtClassOptions>> = {\n // Add your per-object configuration overrides here\n};\n\n/**\n * Default configuration for most SMRT objects\n * Customize this to change project-wide defaults\n */\nfunction getDefaultConfig(): SmrtClassOptions {\n return {\n db: {\n url: process.env.DATABASE_URL || ':memory:',\n type: (process.env.DATABASE_TYPE as 'sqlite' | 'postgres') || 'sqlite'\n },\n ai: process.env.OPENAI_API_KEY ? {\n type: 'openai',\n apiKey: process.env.OPENAI_API_KEY\n } : process.env.ANTHROPIC_API_KEY ? {\n type: 'anthropic',\n apiKey: process.env.ANTHROPIC_API_KEY\n } : undefined\n };\n}\n\nfunction getRequestScopedDatabase(): SmrtClassOptions['db'] | undefined {\n const getter = globalThis.__smrtGetRequestScopedDatabase;\n return typeof getter === 'function' ? getter() : undefined;\n}\n\n/**\n * Get configuration for a specific SMRT object\n * Merges project defaults with per-object overrides if defined\n */\nexport function getSmrtConfig(className: string): SmrtClassOptions {\n const defaults = getDefaultConfig();\n const override = objectOverrides[className];\n\n if (override) {\n // Deep merge: override specific properties while keeping defaults\n return {\n ...defaults,\n ...override,\n // Ensure nested objects are merged properly\n db: override.db\n ? { ...(defaults.db as any), ...(override.db as any) }\n : defaults.db,\n ai: override.ai !== undefined ? override.ai : defaults.ai\n };\n }\n\n return defaults;\n}\n\n/**\n * Helper to get a collection with centralized configuration\n * Automatically applies project defaults or object-specific overrides\n */\nexport async function getCollection<\n T extends import('@happyvertical/smrt-core').SmrtObject,\n>(className: string, overrides: Partial<SmrtClassOptions> = {}) {\n const config = getSmrtConfig(className);\n const objectOverride = objectOverrides[className];\n const requestScopedDb =\n !overrides.db && !objectOverride?.db\n ? getRequestScopedDatabase()\n : undefined;\n\n return await ObjectRegistry.getCollection<T>(\n className,\n {\n ...config,\n ...overrides,\n db: overrides.db\n ? { ...(config.db as any), ...(overrides.db as any) }\n : requestScopedDb ?? config.db,\n ai: overrides.ai !== undefined ? overrides.ai : config.ai\n }\n );\n}\n`;\n\n // Create directory if it doesn't exist\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(configFilePath, configContent, 'utf-8');\n console.log(`[smrt] Generated configuration file: ${configFilePath}`);\n}\n\n/**\n * Generates route files for a single SMRT object\n */\nasync function generateRoutesForObject(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<string[]> {\n const collectionName = objectDef.collection;\n const routeDir = join(projectRoot, options.routesDir, collectionName);\n const generatedRoutePaths: string[] = [];\n\n // Check if API is enabled for this object\n const apiConfig = objectDef.decoratorConfig?.api;\n if (apiConfig === false) {\n console.log(`[smrt] Skipping ${className} - API disabled`);\n return generatedRoutePaths;\n }\n\n // Determine which CRUD actions to include via the shared resolver.\n const includedActions = resolveStandardCrudActions(apiConfig);\n\n // Generate collection route (list, create)\n if (includedActions.includes('list') || includedActions.includes('create')) {\n const collectionRoute = generateCollectionRouteTemplate(\n projectRoot,\n className,\n objectDef,\n manifest,\n includedActions,\n options,\n routeDir,\n );\n generatedRoutePaths.push(\n writeRoute(routeDir, '+server.ts', collectionRoute),\n );\n }\n\n // Generate item route (get, update, delete)\n if (\n includedActions.includes('get') ||\n includedActions.includes('update') ||\n includedActions.includes('delete')\n ) {\n const itemRoute = generateItemRouteTemplate(\n projectRoot,\n className,\n objectDef,\n manifest,\n includedActions,\n options,\n join(routeDir, '[id]'),\n );\n generatedRoutePaths.push(\n writeRoute(join(routeDir, '[id]'), '+server.ts', itemRoute),\n );\n }\n\n // Generate custom action routes\n const customActions = Object.entries(objectDef.methods).filter(\n ([name, method]) =>\n !STANDARD_API_ACTIONS.includes(name) &&\n method.isPublic &&\n shouldIncludeInApi(name, apiConfig),\n );\n\n const actionSpecs: Array<{\n routeDir: string;\n spec: GeneratedActionRouteSpec;\n }> = [];\n\n for (const [actionName, actionDef] of customActions) {\n const routeConfig = resolveApiActionRouteConfig(\n actionName,\n actionDef,\n apiConfig,\n { kebabRoutes: options.kebabRoutes },\n );\n\n if (routeConfig.scope === 'collection' && !actionDef.isStatic) {\n console.warn(\n `[smrt] Skipping ${className}.${actionName} - collection API routes require a static method`,\n );\n continue;\n }\n\n const actionBaseDir =\n routeConfig.scope === 'collection' ? routeDir : join(routeDir, '[id]');\n actionSpecs.push({\n routeDir: join(actionBaseDir, ...routeConfig.pathSegments),\n spec: {\n lookupClassName: className,\n lookupObjectDef: objectDef,\n hostClassName: className,\n actionName,\n actionDef,\n routeConfig,\n hostType: 'item',\n },\n });\n }\n\n for (const [actionRouteDir, routeSpecs] of groupCustomActionRoutes(\n actionSpecs,\n )) {\n const actionRoute = generateActionRouteTemplate(\n projectRoot,\n actionRouteDir,\n routeSpecs,\n objectDef,\n manifest,\n options,\n );\n generatedRoutePaths.push(\n writeRoute(actionRouteDir, '+server.ts', actionRoute),\n );\n }\n\n return generatedRoutePaths;\n}\n\nasync function generateCollectionRoutesForObject(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): Promise<string[]> {\n const generatedRoutePaths: string[] = [];\n const apiConfig = objectDef.decoratorConfig?.api;\n if (apiConfig === false) {\n console.log(`[smrt] Skipping ${className} - API disabled`);\n return generatedRoutePaths;\n }\n\n const routeDir = join(projectRoot, options.routesDir, objectDef.collection);\n const lookupClassName = findItemClassRegistryKey(\n className,\n objectDef,\n manifest,\n );\n const lookupObjectDef = findObjectDefByRegistryKey(manifest, lookupClassName);\n\n const customActions = Object.entries(objectDef.methods).filter(\n ([name, method]) =>\n !STANDARD_API_ACTIONS.includes(name) &&\n method.isPublic &&\n shouldIncludeInApi(name, apiConfig),\n );\n\n if (customActions.length === 0) {\n return generatedRoutePaths;\n }\n const actionSpecs: Array<{\n routeDir: string;\n spec: GeneratedActionRouteSpec;\n }> = [];\n\n for (const [actionName, actionDef] of customActions) {\n const routeConfig = resolveApiActionRouteConfig(\n actionName,\n actionDef,\n apiConfig,\n { kebabRoutes: options.kebabRoutes },\n 'collection',\n );\n\n if (routeConfig.scope !== 'collection') {\n console.warn(\n `[smrt] Skipping ${className}.${actionName} - collection class methods only support collection-scoped API routes`,\n );\n continue;\n }\n\n actionSpecs.push({\n routeDir: join(routeDir, ...routeConfig.pathSegments),\n spec: {\n lookupClassName,\n lookupObjectDef,\n hostClassName: className,\n actionName,\n actionDef,\n routeConfig,\n hostType: 'collection',\n },\n });\n }\n\n for (const [actionRouteDir, routeSpecs] of groupCustomActionRoutes(\n actionSpecs,\n )) {\n const actionRoute = generateActionRouteTemplate(\n projectRoot,\n actionRouteDir,\n routeSpecs,\n objectDef,\n manifest,\n options,\n );\n generatedRoutePaths.push(\n writeRoute(actionRouteDir, '+server.ts', actionRoute),\n );\n }\n\n return generatedRoutePaths;\n}\n\n/**\n * Resolve the list of standard CRUD actions exposed by the API for a given\n * apiConfig. Single source of truth for both the route generator and the\n * cli↔api coherence lint.\n */\nfunction resolveStandardCrudActions(apiConfig: unknown): string[] {\n if (apiConfig === false) return [];\n if (apiConfig === true || apiConfig === undefined) {\n return [...STANDARD_API_ACTIONS];\n }\n if (typeof apiConfig !== 'object') return [...STANDARD_API_ACTIONS];\n\n const config = apiConfig as { include?: string[]; exclude?: string[] };\n let crud: string[] = config.include\n ? config.include.filter((a) => STANDARD_API_ACTIONS.includes(a))\n : [...STANDARD_API_ACTIONS];\n if (Array.isArray(config.exclude)) {\n const exclude = config.exclude;\n crud = crud.filter((a) => !exclude.includes(a));\n }\n return crud;\n}\n\n/**\n * Check if a custom action should be included in API.\n *\n * Mirrors the CRUD inclusion path: intersect with `include` (when set), then\n * subtract `exclude`. Both filters apply together, matching principle of least\n * surprise for users supplying both lists.\n */\nfunction shouldIncludeInApi(actionName: string, apiConfig: unknown): boolean {\n if (apiConfig === false) return false;\n if (apiConfig === true || apiConfig === undefined) return true;\n\n if (typeof apiConfig === 'object' && apiConfig !== null) {\n const config = apiConfig as { include?: unknown; exclude?: unknown };\n // Narrow the scanned decorator config defensively: a non-array\n // include/exclude is treated as unset rather than throwing on .includes().\n const included = Array.isArray(config.include)\n ? config.include.includes(actionName)\n : true;\n const excluded = Array.isArray(config.exclude)\n ? config.exclude.includes(actionName)\n : false;\n return included && !excluded;\n }\n\n return true;\n}\n\n/**\n * Compute the set of action names (standard CRUD + custom methods) that the\n * API exposes for a given object definition. This is the same resolution used\n * to drive route generation, exposed so coherence checks (e.g. CLI vs API)\n * can ask \"what does the API expose?\" without re-implementing the logic.\n */\nexport function resolveApiActionSet(\n objectDef: SmartObjectDefinition,\n manifest?: SmartObjectManifest,\n): Set<string> {\n const apiConfig = objectDef.decoratorConfig?.api;\n if (apiConfig === false) return new Set();\n\n const objectIsCollectionClass = manifest\n ? isCollectionManifestClass(manifest, objectDef)\n : isCollectionClass(objectDef);\n const actions = objectIsCollectionClass\n ? new Set<string>()\n : new Set<string>(resolveStandardCrudActions(apiConfig));\n const config = getApiConfigObject(apiConfig);\n\n // Custom (non-CRUD, public) methods. We mirror BOTH the include/exclude\n // filter AND the scope/static skip rules that the route generators apply\n // (sveltekit-generator.ts generateRoutesForObject and\n // generateCollectionRoutesForObject). Otherwise the cli↔api coherence lint\n // would claim \"reachable via API\" for methods that produce no HTTP route.\n for (const [name, method] of Object.entries(objectDef.methods || {})) {\n if (STANDARD_API_ACTIONS.includes(name)) continue;\n if (!method.isPublic) continue;\n if (!shouldIncludeInApi(name, apiConfig)) continue;\n\n const routeConfig: ApiCustomRouteConfig | undefined =\n config?.routes?.[name];\n const defaultScope: 'item' | 'collection' = objectIsCollectionClass\n ? 'collection'\n : method.isStatic\n ? 'collection'\n : 'item';\n const scope = routeConfig?.scope || defaultScope;\n\n if (objectIsCollectionClass) {\n // Collection classes only emit collection-scoped custom routes.\n if (scope !== 'collection') continue;\n } else {\n // Non-collection classes skip collection-scoped non-static methods\n // (the generator emits a console warning in this case).\n if (scope === 'collection' && !method.isStatic) continue;\n }\n\n actions.add(name);\n }\n\n return actions;\n}\n\n/**\n * Detail for a single cli-vs-api coherence violation.\n */\nexport interface CliApiCoherenceViolation {\n className: string;\n unreachable: string[];\n}\n\n/**\n * Inspect a manifest and return classes whose `cli.include` references\n * methods not exposed via the API. Classes that opt out via\n * `cli: { skipApiCheck: true }` are skipped.\n *\n * Throws nothing; returns the violation list so callers can choose to throw\n * or warn.\n */\nexport function findCliApiCoherenceViolations(\n manifest: SmartObjectManifest,\n): CliApiCoherenceViolation[] {\n const violations: CliApiCoherenceViolation[] = [];\n\n for (const [className, objectDef] of Object.entries(manifest.objects)) {\n const cliConfig = objectDef.decoratorConfig?.cli;\n if (!cliConfig || typeof cliConfig !== 'object') continue;\n if (cliConfig.skipApiCheck) continue;\n if (!cliConfig.include || cliConfig.include.length === 0) continue;\n\n // Match generateCLIModule's effective command set: include − exclude.\n // A command in both lists is not actually registered, so the lint must\n // not flag it as unreachable.\n const cliExclude = Array.isArray(cliConfig.exclude)\n ? cliConfig.exclude\n : [];\n const effectiveCliCommands = cliConfig.include.filter(\n (cmd: string) => !cliExclude.includes(cmd),\n );\n if (effectiveCliCommands.length === 0) continue;\n\n const apiActionSet = resolveApiActionSet(objectDef, manifest);\n const unreachable = effectiveCliCommands.filter(\n (action: string) => !apiActionSet.has(action),\n );\n\n if (unreachable.length > 0) {\n violations.push({ className, unreachable });\n }\n }\n\n return violations;\n}\n\n/**\n * Throw if any class in the manifest has `cli.include` methods that aren't\n * reachable via the API. Default build-time gate; opt-out per-class via\n * `cli: { skipApiCheck: true }` (or globally via the vite plugin option\n * `validateCliApiCoherence: false`).\n */\nexport function validateCliIncludeAgainstApi(\n manifest: SmartObjectManifest,\n): void {\n const violations = findCliApiCoherenceViolations(manifest);\n if (violations.length === 0) return;\n\n const messages = violations.flatMap(({ className, unreachable }) =>\n unreachable.map(\n (action) =>\n `[smrt] ${className}.${action} is declared in cli.include but is not exposed via the api.\\n` +\n ` Either:\\n` +\n ` - Add '${action}' to api.include, or\\n` +\n ` - Remove '${action}' from cli.include.\\n` +\n ` The CLI invokes methods over HTTP; methods without API routes are unreachable.\\n` +\n ` If this CLI is intentionally invoked in-process (no HTTP), set\\n` +\n ` \\`cli: { skipApiCheck: true }\\` on the @smrt() decorator to acknowledge.`,\n ),\n );\n\n throw new Error(messages.join('\\n\\n'));\n}\n\n/**\n * Writes a route file, creating directories as needed\n */\nfunction writeRoute(dir: string, filename: string, content: string): string {\n try {\n // Create directory if needed\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Write the file (writeFileSync is synchronous and will throw on failure)\n const filePath = join(dir, filename);\n writeFileSync(filePath, content, 'utf-8');\n console.log(`[smrt] Generated: ${filePath}`);\n return filePath;\n } catch (error) {\n console.error(`[smrt] [ERROR] Failed to write route file:`);\n console.error(`[smrt] [ERROR] Directory: ${dir}`);\n console.error(`[smrt] [ERROR] Filename: ${filename}`);\n console.error(`[smrt] [ERROR] Error:`, error);\n throw error;\n }\n}\n\n/**\n * Converts absolute file path to SvelteKit $lib alias import\n * For SvelteKit projects, uses $lib alias for better module resolution\n */\nfunction getSvelteKitImportPath(\n projectRoot: string,\n objectFilePath: string | undefined,\n objectsDir: string,\n className?: string,\n): string {\n // Handle undefined filePath by generating a default path\n // This allows tests to work without providing explicit file paths\n const filePath =\n objectFilePath ||\n join(projectRoot, objectsDir, `${className || 'Object'}.ts`);\n\n // Convert objectsDir to absolute path if it's relative\n const absoluteObjectsDir = objectsDir.startsWith('/')\n ? objectsDir\n : join(projectRoot, objectsDir);\n\n // Get the relative path from objectsDir to the file\n const relativePath = relative(absoluteObjectsDir, filePath);\n\n // Convert to forward slashes and remove extension\n const normalizedPath = relativePath.replace(/\\\\/g, '/');\n const withoutExtension = normalizedPath.replace(/\\.(ts|js|tsx|jsx)$/, '');\n\n // If objectsDir is under src/lib, use $lib alias\n if (objectsDir.includes('src/lib')) {\n const libSubpath = objectsDir.split('src/lib')[1] || '';\n const fullPath = libSubpath\n ? `${libSubpath}/${withoutExtension}`\n : withoutExtension;\n return `$lib${fullPath}`.replace(/\\/+/g, '/'); // Clean up double slashes\n }\n\n // Otherwise use relative path (fallback for non-standard layouts)\n return withoutExtension.startsWith('.')\n ? withoutExtension\n : `./${withoutExtension}`;\n}\n\n/**\n * Check if an object's source file is local to the project (not in node_modules).\n * The scanner sets packageName for all objects (including local ones from the\n * project's own package.json), so we check the filePath to distinguish.\n */\nfunction isLocalObject(\n projectRoot: string,\n objectDef: SmartObjectDefinition,\n): boolean {\n if (!objectDef.filePath) return false;\n return (\n objectDef.filePath.startsWith(projectRoot) &&\n !objectDef.filePath.includes('/node_modules/')\n );\n}\n\n/**\n * Generates collection route template (GET list, POST create)\n */\nfunction generateCollectionRouteTemplate(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n includedActions: string[],\n options: SvelteKitOptions,\n routeDir: string,\n): string {\n const hasGet = includedActions.includes('list');\n const hasPost = includedActions.includes('create');\n const modelType = resolveObjectTypeReference(\n projectRoot,\n className,\n objectDef,\n options,\n routeDir,\n );\n const serializers = resolveStandardRouteSerializers(\n objectDef.decoratorConfig?.api,\n );\n const serializerImports = serializers.importStatements.join('\\n');\n // A custom list serializer can render related-table data the per-table\n // change-feed version cannot observe (#1765), so such routes keep the v1\n // body-hash ETag; the default toPublicJSON path uses the v2 version source.\n const listUsesSerializer = !!serializers.listItemSerializerName;\n const readPermissionFields = collectReadPermissionFields(objectDef, manifest);\n const listUsesPermissionScopedBody = readPermissionFields.length > 0;\n const listUsesBodyHash = listUsesSerializer || listUsesPermissionScopedBody;\n // The build-time web-collection shape digest (#1764) salts the v2 read ETag so\n // a shape-only deploy (no table write) busts every read validator. Deterministic\n // for a given manifest — same value the generated virt-web module exports and\n // the client persistence namespace keys on.\n const webManifestHash = computeWebManifestHash(manifest);\n\n const imports = `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\nimport { error${hasPost ? ', json' : ''} } from '@sveltejs/kit';\n${\n serializerImports ? `${serializerImports}\\n` : ''\n}import { getCollection } from '$lib/server/smrt';\n${modelType.importStatement ? `${modelType.importStatement}\\n` : ''}import type { RequestHandler } from './$types';\n// Note: ${className} is auto-registered by the Vite plugin scanner\n${generateAuthGuardHelper(objectDef, manifest)}${isTenantScoped(objectDef) ? generateTenantContextHelper() : ''}${hasPost ? generateWritablePolicyHelper(objectDef) : ''}${hasGet ? generateConditionalGetRouteHelper(objectDef.decoratorConfig?.api, { tenantScoped: isTenantScoped(objectDef), permissionScoped: listUsesPermissionScopedBody, modelName: className, useBodyHash: listUsesBodyHash, manifestHash: webManifestHash }) : ''}`;\n\n // #1782: tenant-scoped reads fail closed to global (NULL-tenant) rows when no\n // tenant context is active (public/anonymous read). Non-tenant models keep the\n // plain list/count.\n const listAndCount = isTenantScoped(objectDef)\n ? ` const readScope = tenantReadScope();\n const items = await collection.list({ limit, offset, where: readScope });\n const count = await collection.count({ where: readScope });`\n : ` const items = await collection.list({ limit, offset });\n const count = await collection.count();`;\n\n const getHandler = hasGet\n ? `\n// List all ${className.toLowerCase()}s\nexport const GET: RequestHandler = async ({ locals, url, request }) => {\n${routeGuardPreamble(objectDef, false)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n const limit = Number(url.searchParams.get('limit')) || 50;\n const offset = Number(url.searchParams.get('offset')) || 0;\n\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n${\n listUsesSerializer\n ? `${listAndCount}\n // Custom serializer may render related-table data → v1 body-hash ETag (#1765).\n const serializedItems = await Promise.all(\n items.map(async (item) =>\n applyReadPermissionRedaction(\n await ${serializers.listItemSerializerName}(item),\n publicJsonOptions,\n ),\n ),\n );\n return conditionalJson(request, { items: serializedItems, count, limit, offset });`\n : listUsesPermissionScopedBody\n ? `${listAndCount}\n // Field read permissions make the body vary by caller permissions → v1 body-hash ETag (#1565).\n const items_public = items.map((item) => item.toPublicJSON(publicJsonOptions));\n return conditionalJson(request, { items: items_public, count, limit, offset });`\n : ` // ETag v2 (#1765): the table-version ETag is checked first; on a concrete\n // If-None-Match the list query below never runs (zero-query 304).\n return conditionalVersionedRead(request, collection.db, collection.tableName, async () => {\n${listAndCount}\n const items_public = items.map((item) => item.toPublicJSON(publicJsonOptions));\n return { items: items_public, count, limit, offset };\n });`\n}\n};\n`\n : '';\n\n const postHandler = hasPost\n ? `\n// Create new ${className.toLowerCase()}\nexport const POST: RequestHandler = async ({ locals, request }) => {\n${routeGuardPreamble(objectDef, true)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n const body: unknown = await request.json();\n const data = applyWritablePolicy(body);\n\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n const item = await collection.create(data);\n await item.save();\n${\n serializers.itemSerializerName\n ? `\n const serializedItem = await ${serializers.itemSerializerName}(item);\n\n return json(\n applyReadPermissionRedaction(serializedItem, publicJsonOptions),\n { status: 201 },\n );`\n : `\n return json(item.toPublicJSON(publicJsonOptions), { status: 201 });`\n}\n};\n`\n : '';\n\n return imports + getHandler + postHandler;\n}\n\nfunction generateCollectionLoad(\n className: string,\n options: { typeName?: string } = {},\n): string {\n const genericSuffix = options.typeName ? `<${options.typeName}>` : '';\n\n return [\n ` const collection = await getCollection${genericSuffix}(`,\n ` '${className}',`,\n ' );',\n ].join('\\n');\n}\n\nfunction generateNotFoundError(className: string): string {\n const singleLineNotFoundError = ` if (!item) throw error(404, '${className} not found');`;\n\n if (singleLineNotFoundError.length <= 100) {\n return singleLineNotFoundError;\n }\n\n return [\n ' if (!item)',\n ' throw error(',\n ' 404,',\n ` '${className} not found',`,\n ' );',\n ].join('\\n');\n}\n\nfunction generateCollectionNotRegisteredError(className: string): string {\n return [\n ' if (!collection)',\n ' throw error(',\n ' 500,',\n ` '${className} collection is not registered',`,\n ' );',\n ].join('\\n');\n}\n\nfunction generateClassNotRegisteredError(className: string): string {\n return [\n ' if (!registered)',\n ' throw error(',\n ' 500,',\n ` '${className} is not registered',`,\n ' );',\n ].join('\\n');\n}\n\n/**\n * Generates item route template (GET, PUT, DELETE)\n */\nfunction generateItemRouteTemplate(\n projectRoot: string,\n className: string,\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n includedActions: string[],\n options: SvelteKitOptions,\n routeDir: string,\n): string {\n const hasGet = includedActions.includes('get');\n const hasPut = includedActions.includes('update');\n const hasDelete = includedActions.includes('delete');\n const simpleClassName = extractSimpleClassName(className);\n const modelType = resolveObjectTypeReference(\n projectRoot,\n className,\n objectDef,\n options,\n routeDir,\n );\n const serializers = resolveStandardRouteSerializers(\n objectDef.decoratorConfig?.api,\n );\n const serializerImports = serializers.importStatements.join('\\n');\n // A custom item serializer can render related-table data the per-table\n // change-feed version cannot observe (#1765), so such routes keep the v1\n // body-hash ETag; the default toPublicJSON path uses the v2 version source.\n const getUsesSerializer = !!serializers.itemSerializerName;\n const readPermissionFields = collectReadPermissionFields(objectDef, manifest);\n const getUsesPermissionScopedBody = readPermissionFields.length > 0;\n const getUsesBodyHash = getUsesSerializer || getUsesPermissionScopedBody;\n // The build-time web-collection shape digest (#1764) salts the v2 read ETag —\n // same value as the list route and the generated virt-web module (see above).\n const webManifestHash = computeWebManifestHash(manifest);\n\n const imports = `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\nimport { error${hasPut || hasDelete ? ', json' : ''} } from '@sveltejs/kit';\n${serializerImports ? `${serializerImports}\\n` : ''}import { getCollection } from '$lib/server/smrt';\n${modelType.importStatement ? `${modelType.importStatement}\\n` : ''}import type { RequestHandler } from './$types';\n${generateAuthGuardHelper(objectDef, manifest)}${isTenantScoped(objectDef) ? generateTenantContextHelper() : ''}${hasPut ? generateWritablePolicyHelper(objectDef) : ''}${hasGet ? generateConditionalGetRouteHelper(objectDef.decoratorConfig?.api, { tenantScoped: isTenantScoped(objectDef), permissionScoped: getUsesPermissionScopedBody, modelName: className, useBodyHash: getUsesBodyHash, manifestHash: webManifestHash }) : ''}`;\n\n // #1782: a tenant-scoped single read fails closed to global (NULL-tenant)\n // rows when no tenant context is active, so a public/anonymous GET /:id can't\n // fetch another tenant's row by id. Mutations (PUT/DELETE) require auth, so\n // they keep the plain id lookup and rely on the interceptor.\n const getForRead = isTenantScoped(objectDef)\n ? ` const readScope = tenantReadScope();\n const item = await collection.get(\n readScope ? { id: params.id, ...readScope } : params.id,\n );`\n : ` const item = await collection.get(params.id);`;\n\n const getHandler = hasGet\n ? `\n// Get single ${simpleClassName.toLowerCase()}\nexport const GET: RequestHandler = async ({ locals, params, request }) => {\n${routeGuardPreamble(objectDef, false)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n${\n getUsesSerializer\n ? `${getForRead}\n${generateNotFoundError(className)}\n // Custom serializer may render related-table data → v1 body-hash ETag (#1765).\n const serializedItem = await ${serializers.itemSerializerName}(item);\n return conditionalJson(\n request,\n applyReadPermissionRedaction(serializedItem, publicJsonOptions),\n );`\n : getUsesPermissionScopedBody\n ? `${getForRead}\n${generateNotFoundError(className)}\n // Field read permissions make the body vary by caller permissions → v1 body-hash ETag (#1565).\n return conditionalJson(request, item.toPublicJSON(publicJsonOptions));`\n : ` // ETag v2 (#1765): a concrete If-None-Match answers 304 without the row fetch\n // (a delete advances the version, so a since-deleted row can't false-match);\n // a wildcard \\`*\\` is deferred until the fetch confirms the row exists.\n return conditionalVersionedRead(request, collection.db, collection.tableName, async () => {\n${getForRead}\n${generateNotFoundError(className)}\n return item.toPublicJSON(publicJsonOptions);\n });`\n}\n};\n`\n : '';\n\n const putHandler = hasPut\n ? `\n// Update ${simpleClassName.toLowerCase()}\nexport const PUT: RequestHandler = async ({ locals, params, request }) => {\n${routeGuardPreamble(objectDef, true)}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n const item = await collection.get(params.id);\n${generateNotFoundError(className)}\n\n const body: unknown = await request.json();\n const data = applyWritablePolicy(body);\n Object.assign(item, data);\n await item.save();\n${\n serializers.itemSerializerName\n ? `\n const serializedItem = await ${serializers.itemSerializerName}(item);\n\n return json(applyReadPermissionRedaction(serializedItem, publicJsonOptions));`\n : `\n return json(item.toPublicJSON(publicJsonOptions));`\n}\n};\n`\n : '';\n\n const deleteHandler = hasDelete\n ? `\n// Delete ${simpleClassName.toLowerCase()}\nexport const DELETE: RequestHandler = async ({ locals, params }) => {\n${routeGuardPreamble(objectDef, true)}\n${generateCollectionLoad(className, { typeName: modelType.typeName })}\n const item = await collection.get(params.id);\n${generateNotFoundError(className)}\n\n await item.delete();\n return json({ success: true });\n};\n`\n : '';\n\n return imports + getHandler + putHandler + deleteHandler;\n}\n\n/**\n * Generates custom action route template\n */\nfunction generateActionRouteTemplate(\n projectRoot: string,\n routeDir: string,\n routeSpecs: GeneratedActionRouteSpec[],\n objectDef: SmartObjectDefinition,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): string {\n if (routeSpecs.length === 0) {\n throw new Error('Cannot generate a custom action route without handlers');\n }\n\n const [firstSpec] = routeSpecs;\n const { lookupClassName, routeConfig, hostType } = firstSpec;\n const lookupModelType = resolveObjectTypeReference(\n projectRoot,\n lookupClassName,\n firstSpec.lookupObjectDef,\n options,\n routeDir,\n );\n const hostModelType = resolveObjectTypeReference(\n projectRoot,\n firstSpec.hostClassName,\n objectDef,\n options,\n routeDir,\n );\n const typeImports = mergeImportStatements([\n lookupModelType.importStatement,\n hostModelType.importStatement,\n ]);\n\n const hasMixedHosts = routeSpecs.some(\n (spec) =>\n spec.hostType !== hostType ||\n spec.lookupClassName !== lookupClassName ||\n spec.routeConfig.scope !== routeConfig.scope,\n );\n\n if (hasMixedHosts) {\n throw new Error(\n `Cannot generate mixed custom route handlers for ${lookupClassName}. ` +\n 'All handlers sharing a route path must target the same host type and scope.',\n );\n }\n\n const importBlock = [\n \"import { error, json } from '@sveltejs/kit';\",\n hostType === 'collection' || routeConfig.scope !== 'collection'\n ? \"import { getCollection } from '$lib/server/smrt';\"\n : \"import { ObjectRegistry } from '@happyvertical/smrt-core';\",\n typeImports,\n \"import type { RequestHandler } from './$types';\",\n ]\n .filter(Boolean)\n .join('\\n');\n\n const tenantScoped =\n isTenantScoped(objectDef) ||\n routeSpecs.some(\n (spec) => !!spec.lookupObjectDef && isTenantScoped(spec.lookupObjectDef),\n );\n const handlers = routeSpecs\n .map((spec) =>\n generateActionRouteHandler(\n spec.lookupClassName,\n spec.actionName,\n spec.actionDef,\n spec.routeConfig,\n spec.hostType,\n tenantScoped,\n lookupModelType.typeName,\n hostModelType.typeName,\n ),\n )\n .join('\\n');\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\n${importBlock}\n${generateAuthGuardHelper(objectDef, manifest)}${tenantScoped ? generateTenantContextHelper() : ''}\n${handlers}`;\n}\n\nfunction generateActionRouteHandler(\n lookupClassName: string,\n actionName: string,\n actionDef: MethodDefinition,\n routeConfig: ResolvedApiActionRouteConfig,\n hostType: 'item' | 'collection',\n tenantScoped: boolean,\n lookupTypeName: string,\n hostTypeName: string,\n): string {\n const handlerName = routeConfig.method;\n // Mutating verbs require auth even when reads are public (#1540). Tenant-scoped\n // objects also establish tenant context so the action runs filtered.\n const guardLines = [\n ` requireRouteAuth(locals, ${routeConfig.method !== 'GET'});`,\n ];\n if (tenantScoped) {\n guardLines.push(' establishTenantContext(locals);');\n }\n const authGuardLine = guardLines.join('\\n');\n const hasInput = actionDef.parameters.length > 0;\n const needsRequest = hasInput;\n const collectionHandlerArgs = buildRouteHandlerArgs(\n routeConfig.pathParamNames.length > 0,\n needsRequest,\n );\n const itemHandlerArgs = buildRouteHandlerArgs(true, needsRequest);\n\n if (hostType === 'collection') {\n const optionsLoad = buildActionOptionsLoad(\n actionName,\n actionDef,\n routeConfig,\n hostTypeName,\n actionDef.isStatic,\n );\n const receiverExpression = actionDef.isStatic\n ? 'CollectionClass'\n : 'typedCollection';\n const staticTargetLoad = actionDef.isStatic\n ? ` const CollectionClass = typedCollection.constructor as typeof ${hostTypeName};\\n`\n : '';\n const scopedOptions = buildScopedOptionsForTenantRead(\n actionDef,\n routeConfig,\n tenantScoped,\n );\n const invocationArgs = buildActionInvocationArgs(\n actionDef,\n scopedOptions.optionsIdentifier,\n );\n\n return `// Custom collection method: ${actionName}\nexport const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {\n${authGuardLine}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(lookupClassName, { typeName: lookupTypeName })}\n const typedCollection = collection as unknown as ${hostTypeName};\n${generateCollectionNotRegisteredError(lookupClassName)}\n${staticTargetLoad}\n\n${optionsLoad}${scopedOptions.source} const result = ${buildActionInvocationExpression(\n receiverExpression,\n actionName,\n invocationArgs,\n )};\n\n return json({\n action: '${actionName}',\n result: toPublicResult(result, publicJsonOptions),\n });\n};\n`;\n }\n\n if (routeConfig.scope === 'collection') {\n const optionsLoad = buildActionOptionsLoad(\n actionName,\n actionDef,\n routeConfig,\n hostTypeName,\n true,\n );\n const scopedOptions = buildScopedOptionsForTenantRead(\n actionDef,\n routeConfig,\n tenantScoped,\n );\n const invocationArgs = buildActionInvocationArgs(\n actionDef,\n scopedOptions.optionsIdentifier,\n );\n return `// Custom collection action: ${actionName}\nexport const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {\n${authGuardLine}\n const publicJsonOptions = getPublicJsonOptions(locals);\n const registered = ObjectRegistry.getClass('${lookupClassName}');\n${generateClassNotRegisteredError(lookupClassName)}\n\n${optionsLoad}${scopedOptions.source} const ClassRef = registered.constructor as typeof ${hostTypeName};\n const result = ${buildActionInvocationExpression(\n 'ClassRef',\n actionName,\n invocationArgs,\n )};\n\n return json({\n action: '${actionName}',\n result: toPublicResult(result, publicJsonOptions),\n });\n};\n`;\n }\n\n const optionsLoad = buildActionOptionsLoad(\n actionName,\n actionDef,\n routeConfig,\n hostTypeName,\n false,\n );\n const scopedItemLoad =\n tenantScoped && routeConfig.method === 'GET'\n ? ` const readScope = tenantReadScope();\n const item = await collection.get(\n readScope ? { id: params.id, ...readScope } : params.id,\n );`\n : ' const item = await collection.get(params.id);';\n const invocationArgs = buildActionInvocationArgs(actionDef);\n return `// Custom action: ${actionName}\nexport const ${handlerName}: RequestHandler = async (${itemHandlerArgs}) => {\n${authGuardLine}\n const publicJsonOptions = getPublicJsonOptions(locals);\n${generateCollectionLoad(lookupClassName, { typeName: lookupTypeName })}\n${scopedItemLoad}\n${generateNotFoundError(lookupClassName)}\n\n${optionsLoad} const result = ${buildActionInvocationExpression(\n 'item',\n actionName,\n invocationArgs,\n )};\n\n return json({\n action: '${actionName}',\n result: toPublicResult(result, publicJsonOptions),\n });\n};\n`;\n}\n\nfunction generateKnowledgeRouteTemplate(\n knowledge: DomainKnowledgeConfig,\n artifact: Record<string, unknown> | null,\n): string {\n const api = knowledge.api ?? {};\n const includeDocs = api.includeDocs === true;\n const includePrompts = api.includePrompts === true;\n const requireAdmin = api.requireAdmin !== false;\n const artifactLiteral = artifact ? JSON.stringify(artifact) : 'null';\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n\nimport { dev } from '$app/environment';\nimport { error, json } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\n\nconst INCLUDE_DOCS_BY_DEFAULT = ${JSON.stringify(includeDocs)};\nconst INCLUDE_PROMPTS_BY_DEFAULT = ${JSON.stringify(includePrompts)};\nconst REQUIRE_ADMIN = ${JSON.stringify(requireAdmin)};\nconst KNOWLEDGE_ARTIFACT: Record<string, unknown> | null = ${artifactLiteral};\n\nif (!dev && !REQUIRE_ADMIN) {\n console.warn('[smrt] PUBLIC knowledge API route enabled; unauthenticated responses are sanitized.');\n}\n\nexport const GET: RequestHandler = async ({ locals, url, setHeaders }) => {\n setHeaders({ 'cache-control': 'private, no-store' });\n\n if (!dev && REQUIRE_ADMIN && !isKnowledgeAdmin(locals)) {\n throw error(403, 'SMRT knowledge requires dev mode or admin access');\n }\n\n const artifact = readKnowledgeArtifact();\n const includeDocs = queryBoolean(url, 'includeDocs', INCLUDE_DOCS_BY_DEFAULT);\n const includePrompts = queryBoolean(\n url,\n 'includePrompts',\n INCLUDE_PROMPTS_BY_DEFAULT,\n );\n\n return json(sanitizeKnowledgeArtifact(artifact, {\n includeDocs,\n includePrompts,\n publicAccess: !REQUIRE_ADMIN,\n }));\n};\n\nfunction readKnowledgeArtifact(): Record<string, unknown> {\n if (!KNOWLEDGE_ARTIFACT) throw error(404, 'SMRT knowledge artifact not found');\n return KNOWLEDGE_ARTIFACT;\n}\n\nfunction queryBoolean(url: URL, name: string, defaultValue: boolean): boolean {\n const value = url.searchParams.get(name);\n if (value === null) return defaultValue;\n return value === 'true';\n}\n\nfunction sanitizeKnowledgeArtifact(\n artifact: Record<string, unknown>,\n options: { includeDocs: boolean; includePrompts: boolean; publicAccess: boolean },\n): Record<string, unknown> {\n const sanitized = { ...artifact };\n\n if (!options.includeDocs) {\n delete sanitized.agentDoc;\n // Linked module docs (#2108) are the same authored prose as agentDoc, just\n // split across siblings — they must drop with it, not leak past the flag.\n delete sanitized.moduleDocs;\n }\n\n if (!options.includePrompts) {\n sanitized.prompts = [];\n }\n\n if (options.publicAccess) {\n delete sanitized.dependencies;\n delete sanitized.sourceHashes;\n delete sanitized.sourceManifestPath;\n delete sanitized.agentDocPath;\n }\n\n return sanitized;\n}\n\nfunction recordValue(value: unknown, key: string): unknown {\n if (!value || typeof value !== 'object') return undefined;\n return (value as Record<string, unknown>)[key];\n}\n\nfunction isKnowledgeAdmin(locals: unknown): boolean {\n if (!locals || typeof locals !== 'object') return false;\n const localRecord = locals as Record<string, unknown>;\n if (\n localRecord.smrtKnowledgeAdmin === true ||\n localRecord.smrtAdmin === true\n ) {\n return true;\n }\n\n const userRoles = rolesFrom(localRecord.user);\n const sessionRoles = rolesFrom(recordValue(localRecord.session, 'user'));\n return [...userRoles, ...sessionRoles].some((role) =>\n ['admin', 'owner', 'superadmin'].includes(role),\n );\n}\n\nfunction rolesFrom(value: unknown): string[] {\n if (!value || typeof value !== 'object') return [];\n const roleList = recordValue(value, 'roles');\n const roles = Array.isArray(roleList) ? roleList : [recordValue(value, 'role')];\n return roles\n .filter((role): role is string => typeof role === 'string')\n .map((role) => role.toLowerCase());\n}\n`;\n}\n\n/**\n * Updates .gitignore to exclude auto-generated routes\n */\nfunction updateGitignore(\n projectRoot: string,\n options: SvelteKitOptions,\n generatedRoutePaths: readonly string[],\n): void {\n const gitignorePath = join(projectRoot, '.gitignore');\n\n // Read existing .gitignore or create empty string\n let gitignoreContent = '';\n if (existsSync(gitignorePath)) {\n gitignoreContent = readFileSync(gitignorePath, 'utf-8');\n }\n\n const generatedPaths = [\n ...new Set(\n generatedRoutePaths.map((path) =>\n gitignorePatternForPath(relative(projectRoot, path)),\n ),\n ),\n ].sort((a, b) => a.localeCompare(b));\n const updatedContent = updateGeneratedRouteIgnoreBlock(\n gitignoreContent,\n options.routesDir,\n generatedPaths,\n );\n\n if (updatedContent !== gitignoreContent) {\n writeFileSync(gitignorePath, updatedContent, 'utf-8');\n console.log('[smrt] Updated .gitignore with generated route paths');\n }\n}\n\nfunction gitignorePatternForPath(path: string): string {\n // Generated SvelteKit item routes include literal `[id]` segments. Escape\n // every Git pattern metacharacter so the managed block ignores files, not\n // glob expressions that happen to resemble their paths.\n return path.replaceAll('\\\\', '/').replaceAll(/([*?[\\]\\\\!#])/g, '\\\\$1');\n}\n\nfunction updateGeneratedRouteIgnoreBlock(\n gitignoreContent: string,\n routesDir: string,\n generatedPaths: readonly string[],\n): string {\n const lines = gitignoreContent.split('\\n');\n const startIndex = lines.indexOf(GITIGNORE_MANAGED_BLOCK_START);\n const endIndex = lines.indexOf(GITIGNORE_MANAGED_BLOCK_END);\n const managedBlock = [\n GITIGNORE_MANAGED_BLOCK_START,\n ...generatedPaths,\n GITIGNORE_MANAGED_BLOCK_END,\n ];\n\n if (startIndex !== -1 || endIndex !== -1) {\n if (startIndex === -1 || endIndex < startIndex) {\n // An incomplete marker pair may have been authored by a consumer. Do\n // not guess at its boundary or remove any user-managed ignore rules.\n return gitignoreContent;\n }\n\n lines.splice(\n startIndex,\n endIndex - startIndex + 1,\n ...(generatedPaths.length > 0 ? managedBlock : []),\n );\n return lines.join('\\n');\n }\n\n // Versions before #2185 wrote exactly this header followed by a recursive\n // wildcard. Remove only that adjacent, generator-owned pair; an equivalent\n // user rule elsewhere is intentionally preserved.\n const legacyPattern = `${routesDir.replaceAll('\\\\', '/')}/**/+server.ts`;\n const legacyIndex = lines.findIndex(\n (line, index) =>\n line === LEGACY_GITIGNORE_HEADER && lines[index + 1] === legacyPattern,\n );\n if (legacyIndex !== -1) {\n lines.splice(legacyIndex, 2);\n }\n\n if (generatedPaths.length === 0) {\n return lines.join('\\n');\n }\n\n let updatedContent = lines.join('\\n');\n if (updatedContent.length > 0 && !updatedContent.endsWith('\\n')) {\n updatedContent += '\\n';\n }\n if (updatedContent.length > 0 && !updatedContent.endsWith('\\n\\n')) {\n updatedContent += '\\n';\n }\n\n return `${updatedContent}${managedBlock.join('\\n')}\\n`;\n}\n"],"mappings":";;;;;;;;;;;;;AAiFA,IAAM,mBAAmB;AACzB,IAAM,uBAAuB;CAAC;CAAQ;CAAO;CAAU;CAAU;AAAQ;AACzE,IAAM,gCACJ;AACF,IAAM,8BACJ;AACF,IAAM,0BACJ;;;;;;;;;;;AAwCF,SAAS,uBAAuB,eAA+B;CAC7D,MAAM,aAAa,cAAc,QAAQ,GAAG;CAC5C,IAAI,eAAe,IACjB,OAAO,cAAc,UAAU,aAAa,CAAC;CAE/C,OAAO;AACT;;;;;;AAOA,SAAS,kBAAkB,WAA2C;CACpE,OAAO,UAAU,YAAY,oBAAoB,CAAC,CAAC,UAAU;AAC/D;AAEA,SAAS,2BACP,UACA,WACA,SACoB;CACpB,IAAI,SACF,OAAO,SAAS,eAAe,UAAU;CAG3C,OAAO,UAAU;AACnB;AAEA,SAAS,4BAA4B,OAAuB;CAG1D,OAAO,IAFa,KAAK,UAAU,KACf,CAAA,CAAY,MAAM,GAAG,EAAE,CAAC,CAAC,WAAW,KAAK,KAClD,EAAY;AACzB;AAEA,SAAS,mBAAmB,WAAsC;CAChE,IAAI,CAAC,aAAa,OAAO,cAAc,UACrC,OAAO;CAGT,OAAO;AACT;AAEA,SAAS,0BACP,MACA,OACS;CACT,OACE,CAAC,CAAC,QACF,CAAC,CAAC,SACF,KAAK,eAAe,MAAM,cAC1B,KAAK,eAAe,MAAM;AAE9B;AAEA,SAAS,gCACP,WACkC;CAClC,MAAM,SAAS,mBAAmB,SAAS;CAC3C,MAAM,iBAAiB,QAAQ,aAAa;CAC5C,MAAM,qBACJ,QAAQ,aAAa,YAAY,QAAQ,aAAa;CAExD,MAAM,mBAA6B,CAAC;CACpC,IAAI;CACJ,IAAI;CAEJ,IAAI,gBAAgB;EAClB,qBAAqB;EACrB,iBAAiB,KACf,YAAY,eAAe,WAAW,MAAM,mBAAmB,WAAW,eAAe,WAAW,GACtG;CACF;CAEA,IAAI,oBACF,IAAI,0BAA0B,oBAAoB,cAAc,GAC9D,yBAAyB;MACpB;EACL,yBAAyB;EACzB,iBAAiB,KACf,YAAY,mBAAmB,WAAW,MAAM,uBAAuB,WAAW,mBAAmB,WAAW,GAClH;CACF;CAGF,OAAO;EACL;EACA;EACA;CACF;AACF;AAEA,SAAS,sBAAsB,MAAuB;CACpD,OAAO,6BAA6B,KAAK,IAAI;AAC/C;AAEA,SAAS,2BACP,aACA,WACA,WACA,SACA,UACwB;CACxB,MAAM,kBAAkB,uBAAuB,SAAS;CAExD,IAAI,CAAC,aAAa,CAAC,sBAAsB,eAAe,GACtD,OAAO,EAAE,UAAU,gDAAgD;CAcrE,OAAO;EACL,UAAU;EACV,iBAAiB,iBAAiB,gBAAgB,WAZlD,cAAc,aAAa,SAAS,KAAK,CAAC,UAAU,cAChD,uBACE,aACA,WACA,SACA,iBACA,QACF,IACA,UAAU,YAI0D;CAC1E;AACF;AAEA,SAAS,uBACP,aACA,WACA,SACA,iBACA,UACQ;CACR,IAAI,UAAU,UAAU;EACtB,MAAM,YAAY,KAAK,aAAa,SAAS;EAC7C,IAAI,UAAU,SAAS,WAAW,GAAG,UAAU,EAAE,GAI/C,OAAO,QAHa,SAAS,WAAW,UAAU,QAAQ,CAAC,CACxD,QAAQ,OAAO,GAAG,CAAC,CACnB,QAAQ,sBAAsB,EAClB,IAAc,QAAQ,QAAQ,GAAG;EAGlD,IAAI,UAAU;GACZ,MAAM,gBAAgB,SAAS,UAAU,UAAU,QAAQ,CAAC,CACzD,QAAQ,OAAO,GAAG,CAAC,CACnB,QAAQ,sBAAsB,EAAE;GACnC,OAAO,cAAc,WAAW,GAAG,IAC/B,gBACA,KAAK;EACX;CACF;CAEA,OAAO,uBACL,aACA,UAAU,UACV,QAAQ,YACR,eACF;AACF;AAEA,SAAS,sBACP,kBACQ;CACR,OAAO,MAAM,KAAK,IAAI,IAAI,iBAAiB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;AACxE;AAEA,SAAS,2BACP,UACA,WACmC;CACnC,OACE,SAAS,QAAQ,cACjB,OAAO,OAAO,SAAS,OAAO,CAAC,CAAC,MAC7B,cAAc,UAAU,cAAc,uBAAuB,SAAS,CACzE;AAEJ;;;;;AAMA,SAAS,0BAA0B,WAA4C;CAC7E,MAAM,SAAS,UAAU,UAAU,CAAC;CACpC,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,MAAM,GAAG;EAChD,MAAM,OAAQ,IAA4C;EAC1D,IACG,IAA+B,aAAa,QAC7C,MAAM,aAAa,MAEnB,MAAM,KAAK,IAAI;CAEnB;CACA,OAAO;AACT;;;;;AAMA,SAAS,wBAAwB,WAAqC;CACpE,MAAM,SAAS,mBAAmB,SAAS;CAC3C,OAAO,MAAM,QAAQ,QAAQ,QAAQ,IAAI,OAAO,WAAW;AAC7D;;;;;;;;AASA,SAAS,6BACP,WACQ;CACR,MAAM,WAAW,0BAA0B,SAAS;CACpD,MAAM,WAAW,wBAAwB,UAAU,iBAAiB,GAAG;CAEvE,OAAO;;;8CAIL,WAAW,KAAK,UAAU,QAAQ,IAAI,OACvC;oCACiC,KAAK,UAAU,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;AAwB7D;;;;;;;AAQA,SAAS,mBAAmB,WAAsC;CAEhE,MAAM,QADS,mBAAmB,SACpB,CAAA,EAAQ;CACtB,IAAI,UAAU,QAAQ,UAAU,QAC9B,OAAO;CAET,OAAO;AACT;AAEA,SAAS,mBAAmB,WAA2B;CACrD,OAAO,UACJ,QAAQ,yBAAyB,OAAO,CAAC,CACzC,QAAQ,qBAAqB,OAAO,CAAC,CACrC,QAAQ,WAAW,GAAG,CAAC,CACvB,YAAY;AACjB;AAEA,SAAS,kBAAkB,WAA0C;CACnE,OAAO,UAAU,iBAAiB,UAAU;AAC9C;AAEA,SAAS,sBACP,WACA,QACS;CACT,MAAM,mBAAmB,UAAU;CACnC,MAAM,cAAc,UAAU;CAC9B,OACG,OAAO,qBAAqB,YAC3B,qBAAqB,OAAO,iBAC7B,OAAO,gBAAgB,aACrB,gBAAgB,OAAO,aACtB,gBAAgB,OAAO;AAE/B;AAEA,SAAS,mBACP,WACA,UACmC;CACnC,OAAO,OAAO,OAAO,SAAS,OAAO,CAAC,CAAC,MAAM,cAC3C,sBAAsB,WAAW,SAAS,CAC5C;AACF;AAEA,SAAS,2BACP,WACA,UACyB;CACzB,IAAI,CAAC,UAAU,OAAO,CAAC,SAAS;CAEhC,IAAI,SAA4C;CAChD,IAAI,UACF,OAAO,iBAAiB,kBAAkB,QAAQ,SAAS,KAAA;CAC7D,OAAO,QAAQ;EACb,MAAM,SAAS,mBAAmB,QAAQ,QAAQ;EAClD,IAAI,CAAC,QAAQ;EACb,IAAI,OAAO,iBAAiB,kBAAkB,OAC5C,UAAU;EAEZ,SAAS;CACX;CAEA,IAAI,CAAC,SAAS,OAAO,CAAC,SAAS;CAE/B,MAAM,0BAAU,IAAI,IAAmC;CACvD,MAAM,SAAS,YAAmC;EAChD,MAAM,MAAM,kBAAkB,OAAO;EACrC,IAAI,QAAQ,IAAI,GAAG,GAAG;EACtB,QAAQ,IAAI,KAAK,OAAO;EACxB,KAAK,MAAM,aAAa,OAAO,OAAO,SAAS,OAAO,GAAG;GACvD,IAAI,cAAc,SAAS;GAC3B,IAAI,sBAAsB,WAAW,OAAO,GAC1C,MAAM,SAAS;EAEnB;CACF;CAEA,MAAM,OAAO;CACb,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC;AAC7B;AAEA,SAAS,4BACP,WACA,UACyB;CACzB,MAAM,SAAkC,CAAC;CACzC,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,UAAU,2BAA2B,WAAW,QAAQ,GACjE,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,OAAO,UAAU,CAAC,CAAC,GAAG;EAC7D,MAAM,aACJ,OAAO,IAAI,mBAAmB,WAC1B,IAAI,iBACJ,OAAO,IAAI,OAAO,mBAAmB,WACnC,IAAI,MAAM,iBACV,KAAA;EACR,IAAI,CAAC,YAAY;EACjB,KAAK,MAAM,aAAa,CAAC,MAAM,mBAAmB,IAAI,CAAC,GAAG;GACxD,MAAM,MAAM,GAAG,UAAU,IAAI;GAC7B,IAAI,KAAK,IAAI,GAAG,GAAG;GACnB,KAAK,IAAI,GAAG;GACZ,OAAO,KAAK,CAAC,WAAW,UAAU,CAAC;EACrC;CACF;CAEF,OAAO;AACT;;;;;;;;AASA,SAAS,wBACP,WACA,UACQ;CACR,MAAM,eAAe,mBAAmB,UAAU,iBAAiB,GAAG;CACtE,MAAM,uBAAuB,4BAA4B,WAAW,QAAQ;CAE5E,OAAO;;;0CAGiC,KAAK,UAAU,YAAY,EAAE;0DACb,KAAK,UAAU,oBAAoB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6I/F;;;;;;AAOA,SAAS,eAAe,WAA2C;CACjE,OAAO,CAAC,CAAC,UAAU,iBAAiB;AACtC;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAS,8BAAsC;CAC7C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCT;;;;;AAMA,SAAS,mBACP,WACA,UACQ;CACR,MAAM,QAAQ,CAAC,8BAA8B,SAAS,GAAG;CACzD,IAAI,eAAe,SAAS,GAC1B,MAAM,KAAK,mCAAmC;CAEhD,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,uBAAuB,QAAgC;CAC9D,QAAQ,QAAQ,YAAY,GAA5B;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO,OAAO,YAAY;EAC5B,SACE,OAAO;CACX;AACF;;;;;;;;;;;;;;AAeA,SAAgB,kBAAkB,MAAsB;CACtD,OAAO,KACJ,QAAQ,sBAAsB,OAAO,CAAC,CACtC,QAAQ,yBAAyB,OAAO,CAAC,CACzC,YAAY;AACjB;AAEA,SAAS,yBACP,YACA,MACA,UAAqC,CAAC,GAC5B;CAEV,IAAI,MAAM;EACR,MAAM,WAAW,KACd,MAAM,GAAG,CAAC,CACV,KAAK,YAAY,QAAQ,KAAK,CAAC,CAAC,CAChC,OAAO,OAAO;EACjB,IAAI,SAAS,SAAS,GACpB,OAAO;CAEX;CAKA,OAAO,CAHS,QAAQ,cACpB,kBAAkB,UAAU,IAC5B,UACW;AACjB;AAEA,SAAS,2BAA2B,cAAkC;CACpE,OAAO,aACJ,QAAQ,YAAY,eAAe,KAAK,OAAO,CAAC,CAAC,CACjD,KAAK,YAAY,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC,CACtC,OAAO,OAAO;AACnB;AAEA,SAAgB,4BACd,YACA,WACA,WACA,eAA0C,CAAC,GAC3C,eAAsC,UAAU,WAC5C,eACA,QAC0B;CAE9B,MAAM,cADS,mBAAmB,SAEhC,CAAA,EAAQ,SAAS;CAEnB,MAAM,eAAe,yBACnB,YACA,aAAa,MACb,YACF;CAEA,OAAO;EACL,OAAO,aAAa,SAAS;EAC7B,QAAQ,uBAAuB,aAAa,MAAM;EAClD;EACA,gBAAgB,2BAA2B,YAAY;CACzD;AACF;AAEA,SAAS,sBACP,eACA,gBACQ;CAGR,MAAM,OAAiB,CAAC,QAAQ;CAEhC,IAAI,eACF,KAAK,KAAK,QAAQ;CAGpB,IAAI,gBACF,KAAK,KAAK,SAAS;CAGrB,OAAO,KAAK,KAAK,KAAK,IAAI,EAAE;AAC9B;AAEA,SAAS,6BAA6B,gBAAkC;CACtE,IAAI,eAAe,WAAW,GAC5B,OAAO;CAGT,OAAO;EACP,eACC,KACE,cACC,OAAO,wBAAwB,SAAS,EAAE,IAAI,sBAAsB,SAAS,EAAE,EACnF,CAAC,CACA,KAAK,IAAI,EAAE;;AAEd;AAEA,SAAS,0BAA0B,WAAsC;CACvE,MAAM,aAAa,MAAM,QAAQ,UAAU,UAAU,IACjD,UAAU,aACV,CAAC;CAEL,OAAO,WAAW,WAAW,KAAK,WAAW,EAAE,EAAE,SAAS;AAC5D;AAEA,SAAS,0BACP,WACA,oBAAoB,WACV;CACV,MAAM,aAAa,MAAM,QAAQ,UAAU,UAAU,IACjD,UAAU,aACV,CAAC;CAEL,IAAI,WAAW,WAAW,GACxB,OAAO,CAAC;CAGV,IAAI,0BAA0B,SAAS,GACrC,OAAO,CAAC,iBAAiB;CAG3B,OAAO,WAAW,KAAK,cACrB,2BAA2B,UAAU,IAAI,CAC3C;AACF;AAEA,SAAS,wBAAwB,cAA8B;CAC7D,IAAI,6BAA6B,KAAK,YAAY,GAChD,OAAO;CAGT,OAAO,KAAK,UAAU,YAAY;AACpC;AAEA,SAAS,+BAA+B,OAAuB;CAC7D,OAAO,IAAI,MAAM,WAAW,MAAM,MAAM,CAAC,CAAC,WAAW,KAAK,KAAK,EAAE;AACnE;AAEA,SAAS,sBAAsB,WAA2B;CACxD,IAAI,6BAA6B,KAAK,SAAS,GAC7C,OAAO,UAAU;CAGnB,OAAO,UAAU,+BAA+B,SAAS,EAAE;AAC7D;AAEA,SAAS,gCACP,gBACA,UACQ;CACR,OAAO,WAAW,WAAW,eAAe,KAAK;AACnD;AAEA,SAAS,yBACP,YACA,gBACA,UACQ;CAKR,OAAO,kCAJsB,gCAC3B,gBACA,QAEuC,EAAqB,GAAG,+BAA+B,UAAU,EAAE;AAC9G;AAEA,SAAS,4BAA4B,WAAqC;CACxE,MAAM,aAAa,MAAM,QAAQ,UAAU,UAAU,IACjD,UAAU,aACV,CAAC;CAEL,IAAI,WAAW,UAAU,KAAK,WAAW,EAAE,EAAE,SAAS,WACpD,OAAO;CAGT,OAAO;EACL;EACA,GAAG,WAAW,KACX,WAAW,UACV,OAAO,wBAAwB,UAAU,IAAI,EAAE,eAAe,MAAM,GACxE;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAEA,SAAS,uBACP,YACA,WACA,aACA,gBACA,UACQ;CACR,MAAM,aAAa,MAAM,QAAQ,UAAU,UAAU,IACjD,UAAU,aACV,CAAC;CACL,IAAI,WAAW,WAAW,GACxB,OAAO;CAGT,MAAM,gBAAgB,YAAY,eAAe,SAAS;CAC1D,MAAM,0BAA0B,6BAC9B,YAAY,cACd;CACA,MAAM,2BACJ,WAAW,WAAW,KAAK,WAAW,EAAE,EAAE,SAAS;CACrD,MAAM,QAAQ,CACZ,yBAAyB,YAAY,gBAAgB,QAAQ,CAC/D;CACA,MAAM,mBAAmB,4BAA4B,SAAS;CAC9D,IAAI,kBACF,MAAM,KAAK,gBAAgB;CAG7B,IAAI,YAAY,WAAW,OAAO;EAChC,IAAI,eACF,MAAM,KACJ,wBAAwB,wBAAwB,IAChD,uBACA,2EACA,sBACA,UAAU,2BAA2B,kBAAkB,gBAAgB,IACvE,EACF;OAEA,MAAM,KACJ,yCACA,oDACA,UAAU,2BAA2B,kBAAkB,gBAAgB,IACvE,EACF;EAEF,OAAO,MAAM,KAAK,IAAI;CACxB;CAEA,IAAI,eAAe;EACjB,MAAM,KACJ,wBAAwB,wBAAwB,IAChD,iDACA,uBACA,gCACA,sBACA,UAAU,2BAA2B,kBAAkB,gBAAgB,IACvE,EACF;EACA,OAAO,MAAM,KAAK,IAAI;CACxB;CAEA,MAAM,KAAK,+CAA+C;CAC1D,IAAI,0BACF,MAAM,KAAK,4CAA4C,EAAE;MAEzD,MAAM,KAAK,4DAA4D,EAAE;CAE3E,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,2BAA2B,cAA8B;CAChE,IAAI,6BAA6B,KAAK,YAAY,GAChD,OAAO,WAAW;CAOpB,OAAO,YAJqB,aACzB,WAAW,MAAM,MAAM,CAAC,CACxB,WAAW,KAAK,KAEA,EAAoB;AACzC;AAEA,SAAS,gCACP,oBACA,YACA,gBACQ;CACR,MAAM,uBAAuB,SAAS,mBAAmB,GAAG,WAAW,GAAG,eAAe,KAAK,IAAI,EAAE;CAGpG,IAAI,oBAFmD,qBAAqB,GAE7C,UAAU,KACvC,OAAO;CAGT,OAAO;EACL,SAAS,mBAAmB,GAAG,WAAW;EAC1C,GAAG,eAAe,KAAK,aAAa,OAAO,SAAS,EAAE;EACtD;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAEA,SAAS,gCACP,WACA,aACA,cAC+C;CAI/C,IACE,CAAC,gBACD,YAAY,WAAW,SACvB,CAAC,0BAA0B,SAAS,GAEpC,OAAO;EAAE,QAAQ;EAAI,mBAAmB;CAAU;CAGpD,OAAO;EACL,QAAQ;;;;;;EAMR,mBAAmB;CACrB;AACF;AAEA,SAAS,yBACP,WACA,WACA,UACQ;CACR,MAAM,aAAa,4BAA4B,UAAU,SAAS;CAClE,IAAI,CAAC,YACH,OAAO,8BAA8B,UAAU,SAAS,KAAK;CAM/D,OAHsB,OAAO,QAAQ,SAAS,OAAO,CAAC,CAAC,MACpD,GAAG,oBAAoB,mBAAmB,UAEtC,CAAA,GAAgB,MAAM,WAAW;AAC1C;AAEA,SAAS,wBACP,aAIyC;CACzC,MAAM,gCAAgB,IAAI,IAAwC;CAElE,KAAK,MAAM,EAAE,UAAU,UAAU,aAAa;EAC5C,MAAM,WAAW,cAAc,IAAI,QAAQ,KAAK,CAAC;EACjD,MAAM,kBAAkB,SAAS,MAC9B,cAAc,UAAU,YAAY,WAAW,KAAK,YAAY,MACnE;EAEA,IAAI,iBACF,MAAM,IAAI,MACR,0CAA0C,SAAS,IAAI,KAAK,YAAY,OAAO,aAClE,gBAAgB,WAAW,OAAO,KAAK,WAAW,sCACjE;EAGF,SAAS,KAAK,IAAI;EAClB,cAAc,IAAI,UAAU,QAAQ;CACtC;CAEA,OAAO;AACT;;;;AAKA,eAAsB,wBACpB,aACA,UACA,SACe;CACf,IAAI,CAAC,QAAQ,SAAS;CAEtB,QAAQ,IAAI,uCAAuC;CAEnD,yBAAyB,KAAK,aAAa,QAAQ,SAAS,CAAC;CAC7D,6BAA6B,aAAa,OAAO;CAGjD,MAAM,uBAAuB,aAAa,UAAU,OAAO;CAE3D,MAAM,sBAAgC,CAAC;CACvC,IAAI,iBAAiB;CACrB,IAAI,qBAAqB;CACzB,KAAK,MAAM,CAAC,WAAW,cAAc,OAAO,QAAQ,SAAS,OAAO,GAAG;EACrE,IAAI,0BAA0B,UAAU,SAAS,GAAG;GAClD,MAAM,uBAAuB,MAAM,kCACjC,aACA,WACA,WACA,UACA,OACF;GACA,oBAAoB,KAAK,GAAG,oBAAoB;GAChD,IAAI,qBAAqB,SAAS,GAChC;QACK;IACL,QAAQ,IACN,mBAAmB,UAAU,wCAC/B;IACA;GACF;GACA;EACF;EACA,oBAAoB,KAClB,GAAI,MAAM,wBACR,aACA,WACA,WACA,UACA,OACF,CACF;EACA;CACF;CAEA,IAAI,QAAQ,WAAW,KAAK,SAC1B,oBAAoB,KAAK,uBAAuB,aAAa,OAAO,CAAC;CAIvE,IAAI,uBAAuB,aAAa,UAAU,OAAO,GACvD,oBAAoB,KAClB,KAAK,aAAa,QAAQ,WAAW,QAAQ,SAAS,YAAY,CACpE;CAGF,IAAI,qBAAqB,aAAa,UAAU,OAAO,GACrD,oBAAoB,KAClB,KAAK,aAAa,QAAQ,WAAW,YAAY,YAAY,CAC/D;CAGF,IACE,oBACE,aACA,UACA,SACA,uBAAuB,QAAQ,CACjC,GAEA,oBAAoB,KAClB,KAAK,aAAa,QAAQ,WAAW,WAAW,YAAY,CAC9D;CAKF,gBAAgB,aAAa,SAAS,mBAAmB;CAEzD,MAAM,aACJ,qBAAqB,IACjB,aAAa,mBAAmB,wBAChC;CACN,QAAQ,IACN,+BAA+B,eAAe,eAAe,YAC/D;AACF;AAEA,SAAS,yBAAyB,YAA0B;CAC1D,IAAI,CAAC,WAAW,UAAU,GACxB;CAGF,KAAK,MAAM,SAAS,YAAY,YAAY,EAAE,eAAe,KAAK,CAAC,GAAG;EACpE,MAAM,YAAY,KAAK,YAAY,MAAM,IAAI;EAE7C,IAAI,MAAM,YAAY,GAAG;GACvB,yBAAyB,SAAS;GAClC;EACF;EAEA,IAAI,CAAC,MAAM,OAAO,KAAK,MAAM,SAAS,cACpC;EAIF,IADoB,aAAa,WAAW,OACxC,CAAA,CAAY,WAAA,6CAAsC,GACpD,WAAW,SAAS;CAExB;AACF;AAEA,SAAS,kBAAkB,aAAqB,SAA2B;CACzE,MAAM,WAAW,OACf,QAAQ,WAAW,KAAK,YAAY,mBACtC;CAOA,OAAO,KAAK,aANM,mBAAmB,QAAQ,SAMpB,GAAW,GALnB,SACd,MAAM,GAAG,CAAC,CACV,KAAK,YAAY,QAAQ,KAAK,CAAC,CAAC,CAChC,OAAO,OAE6B,CAAQ;AACjD;AAEA,SAAS,mBAAmB,WAA2B;CACrD,MAAM,aAAa,UAAU,WAAW,MAAM,GAAG,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAErE,MAAM,cAAc,WAAW,QAAQ,SAAM;CAE7C,IAAI,eAAe,gBAAgB,WAAW,SAAS,SAAS,GAC9D,OAAO;CAET,IAAI,gBAAgB,IAClB,OAAO,WAAW,MAAM,GAAG,cAAc,CAAa;CAExD,OAAO;AACT;AAEA,SAAS,6BACP,aACA,SACM;CAEN,MAAM,YAAY,KADD,kBAAkB,aAAa,OACzB,GAAU,YAAY;CAC7C,IAAI,CAAC,WAAW,SAAS,GAAG;CAG5B,IADgB,aAAa,WAAW,OACpC,CAAA,CAAQ,WAAA,6CAAsC,GAChD,WAAW,SAAS;AAExB;AAEA,SAAS,uBACP,aACA,SACQ;CAMR,OAAO,WALU,kBAAkB,aAAa,OAK9B,GAAU,cAJd,+BACZ,QAAQ,aAAa,CAAC,GACtB,2BAA2B,WAAW,CAEE,CAAK;AACjD;AAEA,SAAS,2BACP,aACgC;CAChC,KAAK,MAAM,gBAAgB,CACzB,6BACA,0BACF,GAAG;EACD,MAAM,WAAW,KAAK,aAAa,YAAY;EAC/C,IAAI,CAAC,WAAW,QAAQ,GAAG;EAC3B,IAAI;GACF,OAAO,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;EACnD,QAAQ;GACN,OAAO;EACT;CACF;CAEA,OAAO;AACT;;;;;AAMA,eAAe,yBACb,aACA,UACA,SACe;CAEf,MAAM,YAAY,KAAK,aADJ,QAAQ,cAAc,gBACK;CAC9C,MAAM,uBAAuB,KAAK,WAAW,kBAAkB;CAG/D,MAAM,eAAmE,CAAC;CAC1E,MAAM,iCAAiB,IAAI,IAGzB;CAEF,KAAK,MAAM,CAAC,WAAW,cAAc,OAAO,QAAQ,SAAS,OAAO,GAClE,IAAI,cAAc,aAAa,SAAS,GAEtC,aAAa,KAAK,CAAC,WAAW,SAAS,CAAC;MACnC,IAAI,UAAU,aAAa;EAEhC,MAAM,eAAe,eAAe,IAAI,UAAU,WAAW,KAAK;GAChE,YAAY,CAAC;GACb,qBAAqB;EACvB;EAEA,IAAI,0BAA0B,UAAU,SAAS,GAC/C,aAAa,sBAAsB;OAEnC,aAAa,WAAW,KAAK,SAAS;EAGxC,eAAe,IAAI,UAAU,aAAa,YAAY;CACxD,OAEE,aAAa,KAAK,CAAC,WAAW,SAAS,CAAC;CAM5C,MAAM,oCAAoB,IAAI,IAAsB;CACpD,MAAM,yCAAyB,IAAI,IAAY;CAE/C,KAAK,MAAM,CAAC,WAAW,cAAc,cAAc;EACjD,MAAM,kBAAkB,uBAAuB,SAAS;EAIxD,IAAI,aAAa;EACjB,IAAI,UAAU,UAAU;GAEtB,MAAM,aADmB,SAAS,WAAW,UAAU,QACpC,CAAA,CAChB,QAAQ,OAAO,GAAG,CAAC,CACnB,QAAQ,sBAAsB,EAAE;GACnC,aAAa,WAAW,WAAW,GAAG,IAAI,aAAa,KAAK;EAC9D,OAEE,aAAa,uBACX,aACA,KAAA,GACA,QAAQ,YACR,eACF;EAGF,IAAI,0BAA0B,UAAU,SAAS,GAAG;GAClD,uBAAuB,IAAI,UAAU;GACrC;EACF;EAEA,MAAM,WAAW,kBAAkB,IAAI,UAAU,KAAK,CAAC;EACvD,SAAS,KAAK,eAAe;EAC7B,kBAAkB,IAAI,YAAY,QAAQ;CAC5C;CAEA,MAAM,eAAe,CACnB,GAAG,MAAM,KAAK,kBAAkB,QAAQ,CAAC,CAAC,CACvC,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,KAAK,CAAC,CAAC,CACpD,KAAK,CAAC,YAAY,iBAAiB;EAElC,OAAO,YADa,YAAY,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAC7C,CAAA,CAAY,KAAK,IAAI,EAAE,WAAW,WAAW;CAClE,CAAC,GACH,GAAG,MAAM,KAAK,uBAAuB,OAAO,CAAC,CAAC,CAC3C,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAClC,KAAK,eAAe,WAAW,WAAW,GAAG,CAClD,CAAC,CAAC,KAAK,IAAI;CAkEX,MAAM,sBAAsB;;;;;;;;;;;EAvCZ,CAvBO,MAAM,KAAK,eAAe,QAAQ,CAAC,CAAC,CACxD,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,KAAK,CAAC,CAAC,CACpD,SAAS,CAAC,aAAa,kBAAkB;EACxC,MAAM,UAAoB,CAAC;EAE3B,IAAI,aAAa,qBACf,QAAQ,KAAK,WAAW,YAAY,GAAG;EAGzC,IAAI,aAAa,WAAW,SAAS,GAAG;GAEtC,MAAM,cAAc,aAAa,WAC9B,IAAI,sBAAsB,CAAC,CAC3B,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;GACpC,QAAQ,KACN,YAAY,YAAY,KAAK,IAAI,EAAE,WAAW,YAAY,GAC5D;EACF;EAEA,OAAO;CACT,CAAC,CAAC,CACD,KAAK,IAES,GAAgB,YAAY,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAkDpE,EAAQ;;;EAjDc,OAAO,QAAQ,SAAS,OAAO,CAAC,CACnD,KAAK,CAAC,WAAW,eAAe;EAC/B,IAAI,0BAA0B,UAAU,SAAS,GAI/C,OAAO;EAGT,MAAM,kBAAkB,uBAAuB,SAAS;EAExD,MAAM,cAAc,2BAClB,UACA,WAHkB,cAAc,aAAa,SAI7C,CACF;EAEA,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,qBAAqB,4BAA4B,WAAW;EAClE,MAAM,yBAAyB,2BAA2B,gBAAgB,aAAa,gBAAgB,kBAAkB,mBAAmB;EAE5I,IAAI,uBAAuB,UAAU,kBACnC,OAAO;EAGT,OAAO;GACL,2BAA2B,gBAAgB;GAC3C,YAAY,gBAAgB;GAC5B,kBAAkB,mBAAmB;GACrC;EACF,CAAC,CAAC,KAAK,IAAI;CACb,CAAC,CAAC,CACD,QAAQ,iBAAyC,iBAAiB,IAAI,CAAC,CACvE,KAAK,IAgBR,EAAc;;CAId,IAAI,CAAC,WAAW,SAAS,GACvB,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAG1C,cAAc,sBAAsB,qBAAqB,OAAO;CAChE,QAAQ,IAAI,uCAAuC,sBAAsB;AAC3E;;;;;AAMA,eAAe,uBACb,aACA,UACA,SACe;CACf,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,YAAY,KAAK,aAAa,UAAU;CAC9C,MAAM,iBAAiB,KAAK,WAAW,cAAc;CAGrD,MAAM,yBAAyB,aAAa,UAAU,OAAO;CAG7D,IAAI,WAAW,cAAc,GAAG;EAC9B,QAAQ,IAAI,wDAAwD;EACpE;CACF;CAEA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuItB,IAAI,CAAC,WAAW,SAAS,GACvB,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAG1C,cAAc,gBAAgB,eAAe,OAAO;CACpD,QAAQ,IAAI,wCAAwC,gBAAgB;AACtE;;;;AAKA,eAAe,wBACb,aACA,WACA,WACA,UACA,SACmB;CACnB,MAAM,iBAAiB,UAAU;CACjC,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,cAAc;CACpE,MAAM,sBAAgC,CAAC;CAGvC,MAAM,YAAY,UAAU,iBAAiB;CAC7C,IAAI,cAAc,OAAO;EACvB,QAAQ,IAAI,mBAAmB,UAAU,gBAAgB;EACzD,OAAO;CACT;CAGA,MAAM,kBAAkB,2BAA2B,SAAS;CAG5D,IAAI,gBAAgB,SAAS,MAAM,KAAK,gBAAgB,SAAS,QAAQ,GAAG;EAC1E,MAAM,kBAAkB,gCACtB,aACA,WACA,WACA,UACA,iBACA,SACA,QACF;EACA,oBAAoB,KAClB,WAAW,UAAU,cAAc,eAAe,CACpD;CACF;CAGA,IACE,gBAAgB,SAAS,KAAK,KAC9B,gBAAgB,SAAS,QAAQ,KACjC,gBAAgB,SAAS,QAAQ,GACjC;EACA,MAAM,YAAY,0BAChB,aACA,WACA,WACA,UACA,iBACA,SACA,KAAK,UAAU,MAAM,CACvB;EACA,oBAAoB,KAClB,WAAW,KAAK,UAAU,MAAM,GAAG,cAAc,SAAS,CAC5D;CACF;CAGA,MAAM,gBAAgB,OAAO,QAAQ,UAAU,OAAO,CAAC,CAAC,QACrD,CAAC,MAAM,YACN,CAAC,qBAAqB,SAAS,IAAI,KACnC,OAAO,YACP,mBAAmB,MAAM,SAAS,CACtC;CAEA,MAAM,cAGD,CAAC;CAEN,KAAK,MAAM,CAAC,YAAY,cAAc,eAAe;EACnD,MAAM,cAAc,4BAClB,YACA,WACA,WACA,EAAE,aAAa,QAAQ,YAAY,CACrC;EAEA,IAAI,YAAY,UAAU,gBAAgB,CAAC,UAAU,UAAU;GAC7D,QAAQ,KACN,mBAAmB,UAAU,GAAG,WAAW,iDAC7C;GACA;EACF;EAEA,MAAM,gBACJ,YAAY,UAAU,eAAe,WAAW,KAAK,UAAU,MAAM;EACvE,YAAY,KAAK;GACf,UAAU,KAAK,eAAe,GAAG,YAAY,YAAY;GACzD,MAAM;IACJ,iBAAiB;IACjB,iBAAiB;IACjB,eAAe;IACf;IACA;IACA;IACA,UAAU;GACZ;EACF,CAAC;CACH;CAEA,KAAK,MAAM,CAAC,gBAAgB,eAAe,wBACzC,WACF,GAAG;EACD,MAAM,cAAc,4BAClB,aACA,gBACA,YACA,WACA,UACA,OACF;EACA,oBAAoB,KAClB,WAAW,gBAAgB,cAAc,WAAW,CACtD;CACF;CAEA,OAAO;AACT;AAEA,eAAe,kCACb,aACA,WACA,WACA,UACA,SACmB;CACnB,MAAM,sBAAgC,CAAC;CACvC,MAAM,YAAY,UAAU,iBAAiB;CAC7C,IAAI,cAAc,OAAO;EACvB,QAAQ,IAAI,mBAAmB,UAAU,gBAAgB;EACzD,OAAO;CACT;CAEA,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,UAAU,UAAU;CAC1E,MAAM,kBAAkB,yBACtB,WACA,WACA,QACF;CACA,MAAM,kBAAkB,2BAA2B,UAAU,eAAe;CAE5E,MAAM,gBAAgB,OAAO,QAAQ,UAAU,OAAO,CAAC,CAAC,QACrD,CAAC,MAAM,YACN,CAAC,qBAAqB,SAAS,IAAI,KACnC,OAAO,YACP,mBAAmB,MAAM,SAAS,CACtC;CAEA,IAAI,cAAc,WAAW,GAC3B,OAAO;CAET,MAAM,cAGD,CAAC;CAEN,KAAK,MAAM,CAAC,YAAY,cAAc,eAAe;EACnD,MAAM,cAAc,4BAClB,YACA,WACA,WACA,EAAE,aAAa,QAAQ,YAAY,GACnC,YACF;EAEA,IAAI,YAAY,UAAU,cAAc;GACtC,QAAQ,KACN,mBAAmB,UAAU,GAAG,WAAW,sEAC7C;GACA;EACF;EAEA,YAAY,KAAK;GACf,UAAU,KAAK,UAAU,GAAG,YAAY,YAAY;GACpD,MAAM;IACJ;IACA;IACA,eAAe;IACf;IACA;IACA;IACA,UAAU;GACZ;EACF,CAAC;CACH;CAEA,KAAK,MAAM,CAAC,gBAAgB,eAAe,wBACzC,WACF,GAAG;EACD,MAAM,cAAc,4BAClB,aACA,gBACA,YACA,WACA,UACA,OACF;EACA,oBAAoB,KAClB,WAAW,gBAAgB,cAAc,WAAW,CACtD;CACF;CAEA,OAAO;AACT;;;;;;AAOA,SAAS,2BAA2B,WAA8B;CAChE,IAAI,cAAc,OAAO,OAAO,CAAC;CACjC,IAAI,cAAc,QAAQ,cAAc,KAAA,GACtC,OAAO,CAAC,GAAG,oBAAoB;CAEjC,IAAI,OAAO,cAAc,UAAU,OAAO,CAAC,GAAG,oBAAoB;CAElE,MAAM,SAAS;CACf,IAAI,OAAiB,OAAO,UACxB,OAAO,QAAQ,QAAQ,MAAM,qBAAqB,SAAS,CAAC,CAAC,IAC7D,CAAC,GAAG,oBAAoB;CAC5B,IAAI,MAAM,QAAQ,OAAO,OAAO,GAAG;EACjC,MAAM,UAAU,OAAO;EACvB,OAAO,KAAK,QAAQ,MAAM,CAAC,QAAQ,SAAS,CAAC,CAAC;CAChD;CACA,OAAO;AACT;;;;;;;;AASA,SAAS,mBAAmB,YAAoB,WAA6B;CAC3E,IAAI,cAAc,OAAO,OAAO;CAChC,IAAI,cAAc,QAAQ,cAAc,KAAA,GAAW,OAAO;CAE1D,IAAI,OAAO,cAAc,YAAY,cAAc,MAAM;EACvD,MAAM,SAAS;EAGf,MAAM,WAAW,MAAM,QAAQ,OAAO,OAAO,IACzC,OAAO,QAAQ,SAAS,UAAU,IAClC;EACJ,MAAM,WAAW,MAAM,QAAQ,OAAO,OAAO,IACzC,OAAO,QAAQ,SAAS,UAAU,IAClC;EACJ,OAAO,YAAY,CAAC;CACtB;CAEA,OAAO;AACT;;;;;;;AAQA,SAAgB,oBACd,WACA,UACa;CACb,MAAM,YAAY,UAAU,iBAAiB;CAC7C,IAAI,cAAc,OAAO,uBAAO,IAAI,IAAI;CAExC,MAAM,0BAA0B,WAC5B,0BAA0B,UAAU,SAAS,IAC7C,kBAAkB,SAAS;CAC/B,MAAM,UAAU,0CACZ,IAAI,IAAY,IAChB,IAAI,IAAY,2BAA2B,SAAS,CAAC;CACzD,MAAM,SAAS,mBAAmB,SAAS;CAO3C,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,UAAU,WAAW,CAAC,CAAC,GAAG;EACpE,IAAI,qBAAqB,SAAS,IAAI,GAAG;EACzC,IAAI,CAAC,OAAO,UAAU;EACtB,IAAI,CAAC,mBAAmB,MAAM,SAAS,GAAG;EAE1C,MAAM,cACJ,QAAQ,SAAS;EACnB,MAAM,eAAsC,0BACxC,eACA,OAAO,WACL,eACA;EACN,MAAM,QAAQ,aAAa,SAAS;EAEpC,IAAI;OAEE,UAAU,cAAc;EAAA,OAI5B,IAAI,UAAU,gBAAgB,CAAC,OAAO,UAAU;EAGlD,QAAQ,IAAI,IAAI;CAClB;CAEA,OAAO;AACT;;;;;;;;;AAkBA,SAAgB,8BACd,UAC4B;CAC5B,MAAM,aAAyC,CAAC;CAEhD,KAAK,MAAM,CAAC,WAAW,cAAc,OAAO,QAAQ,SAAS,OAAO,GAAG;EACrE,MAAM,YAAY,UAAU,iBAAiB;EAC7C,IAAI,CAAC,aAAa,OAAO,cAAc,UAAU;EACjD,IAAI,UAAU,cAAc;EAC5B,IAAI,CAAC,UAAU,WAAW,UAAU,QAAQ,WAAW,GAAG;EAK1D,MAAM,aAAa,MAAM,QAAQ,UAAU,OAAO,IAC9C,UAAU,UACV,CAAC;EACL,MAAM,uBAAuB,UAAU,QAAQ,QAC5C,QAAgB,CAAC,WAAW,SAAS,GAAG,CAC3C;EACA,IAAI,qBAAqB,WAAW,GAAG;EAEvC,MAAM,eAAe,oBAAoB,WAAW,QAAQ;EAC5D,MAAM,cAAc,qBAAqB,QACtC,WAAmB,CAAC,aAAa,IAAI,MAAM,CAC9C;EAEA,IAAI,YAAY,SAAS,GACvB,WAAW,KAAK;GAAE;GAAW;EAAY,CAAC;CAE9C;CAEA,OAAO;AACT;;;;;;;AAQA,SAAgB,6BACd,UACM;CACN,MAAM,aAAa,8BAA8B,QAAQ;CACzD,IAAI,WAAW,WAAW,GAAG;CAE7B,MAAM,WAAW,WAAW,SAAS,EAAE,WAAW,kBAChD,YAAY,KACT,WACC,UAAU,UAAU,GAAG,OAAO,qFAEhB,OAAO,sCACJ,OAAO,oPAI5B,CACF;CAEA,MAAM,IAAI,MAAM,SAAS,KAAK,MAAM,CAAC;AACvC;;;;AAKA,SAAS,WAAW,KAAa,UAAkB,SAAyB;CAC1E,IAAI;EAEF,IAAI,CAAC,WAAW,GAAG,GACjB,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;EAIpC,MAAM,WAAW,KAAK,KAAK,QAAQ;EACnC,cAAc,UAAU,SAAS,OAAO;EACxC,QAAQ,IAAI,qBAAqB,UAAU;EAC3C,OAAO;CACT,SAAS,OAAO;EACd,QAAQ,MAAM,4CAA4C;EAC1D,QAAQ,MAAM,+BAA+B,KAAK;EAClD,QAAQ,MAAM,8BAA8B,UAAU;EACtD,QAAQ,MAAM,2BAA2B,KAAK;EAC9C,MAAM;CACR;AACF;;;;;AAMA,SAAS,uBACP,aACA,gBACA,YACA,WACQ;CAGR,MAAM,WACJ,kBACA,KAAK,aAAa,YAAY,GAAG,aAAa,SAAS,IAAI;CAY7D,MAAM,mBAJe,SALM,WAAW,WAAW,GAAG,IAChD,aACA,KAAK,aAAa,UAAU,GAGkB,QAG3B,CAAA,CAAa,QAAQ,OAAO,GAC1B,CAAA,CAAe,QAAQ,sBAAsB,EAAE;CAGxE,IAAI,WAAW,SAAS,SAAS,GAAG;EAClC,MAAM,aAAa,WAAW,MAAM,SAAS,CAAC,CAAC,MAAM;EAIrD,OAAO,OAHU,aACb,GAAG,WAAW,GAAG,qBACjB,mBACqB,QAAQ,QAAQ,GAAG;CAC9C;CAGA,OAAO,iBAAiB,WAAW,GAAG,IAClC,mBACA,KAAK;AACX;;;;;;AAOA,SAAS,cACP,aACA,WACS;CACT,IAAI,CAAC,UAAU,UAAU,OAAO;CAChC,OACE,UAAU,SAAS,WAAW,WAAW,KACzC,CAAC,UAAU,SAAS,SAAS,gBAAgB;AAEjD;;;;AAKA,SAAS,gCACP,aACA,WACA,WACA,UACA,iBACA,SACA,UACQ;CACR,MAAM,SAAS,gBAAgB,SAAS,MAAM;CAC9C,MAAM,UAAU,gBAAgB,SAAS,QAAQ;CACjD,MAAM,YAAY,2BAChB,aACA,WACA,WACA,SACA,QACF;CACA,MAAM,cAAc,gCAClB,UAAU,iBAAiB,GAC7B;CACA,MAAM,oBAAoB,YAAY,iBAAiB,KAAK,IAAI;CAIhE,MAAM,qBAAqB,CAAC,CAAC,YAAY;CAEzC,MAAM,+BADuB,4BAA4B,WAAW,QAC/B,CAAA,CAAqB,SAAS;CACnE,MAAM,mBAAmB,sBAAsB;CAK/C,MAAM,kBAAkB,uBAAuB,QAAQ;CAEvD,MAAM,UAAU,GAAG,4BAA4B;;;gBAGjC,UAAU,WAAW,GAAG;EAEtC,oBAAoB,GAAG,kBAAkB,MAAM,GAChD;EACC,UAAU,kBAAkB,GAAG,UAAU,gBAAgB,MAAM,GAAG;WACzD,UAAU;EACnB,wBAAwB,WAAW,QAAQ,IAAI,eAAe,SAAS,IAAI,4BAA4B,IAAI,KAAK,UAAU,6BAA6B,SAAS,IAAI,KAAK,SAAS,kCAAkC,UAAU,iBAAiB,KAAK;EAAE,cAAc,eAAe,SAAS;EAAG,kBAAkB;EAA8B,WAAW;EAAW,aAAa;EAAkB,cAAc;CAAgB,CAAC,IAAI;CAKva,MAAM,eAAe,eAAe,SAAS,IACzC;;iEAGA;;CAGJ,MAAM,aAAa,SACf;cACQ,UAAU,YAAY,EAAE;;EAEpC,mBAAmB,WAAW,KAAK,EAAE;;;;;EAKrC,uBAAuB,WAAW,EAAE,UAAU,UAAU,SAAS,CAAC,EAAE;EAEpE,qBACI,GAAG,aAAa;;;;;gBAKN,YAAY,uBAAuB;;;;;wFAM7C,+BACE,GAAG,aAAa;;;qFAIhB;;;EAGN,aAAa;;;OAId;;IAGK;CAEJ,MAAM,cAAc,UAChB;gBACU,UAAU,YAAY,EAAE;;EAEtC,mBAAmB,WAAW,IAAI,EAAE;;;;;EAKpC,uBAAuB,WAAW,EAAE,UAAU,UAAU,SAAS,CAAC,EAAE;;;EAIpE,YAAY,qBACR;iCAC2B,YAAY,mBAAmB;;;;;QAM1D;uEAEL;;IAGK;CAEJ,OAAO,UAAU,aAAa;AAChC;AAEA,SAAS,uBACP,WACA,UAAiC,CAAC,GAC1B;CAGR,OAAO;EACL,2CAHoB,QAAQ,WAAW,IAAI,QAAQ,SAAS,KAAK,GAGR;EACzD,QAAQ,UAAU;EAClB;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAEA,SAAS,sBAAsB,WAA2B;CACxD,MAAM,0BAA0B,kCAAkC,UAAU;CAE5E,IAAI,wBAAwB,UAAU,KACpC,OAAO;CAGT,OAAO;EACL;EACA;EACA;EACA,UAAU,UAAU;EACpB;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAEA,SAAS,qCAAqC,WAA2B;CACvE,OAAO;EACL;EACA;EACA;EACA,UAAU,UAAU;EACpB;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAEA,SAAS,gCAAgC,WAA2B;CAClE,OAAO;EACL;EACA;EACA;EACA,UAAU,UAAU;EACpB;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;AAKA,SAAS,0BACP,aACA,WACA,WACA,UACA,iBACA,SACA,UACQ;CACR,MAAM,SAAS,gBAAgB,SAAS,KAAK;CAC7C,MAAM,SAAS,gBAAgB,SAAS,QAAQ;CAChD,MAAM,YAAY,gBAAgB,SAAS,QAAQ;CACnD,MAAM,kBAAkB,uBAAuB,SAAS;CACxD,MAAM,YAAY,2BAChB,aACA,WACA,WACA,SACA,QACF;CACA,MAAM,cAAc,gCAClB,UAAU,iBAAiB,GAC7B;CACA,MAAM,oBAAoB,YAAY,iBAAiB,KAAK,IAAI;CAIhE,MAAM,oBAAoB,CAAC,CAAC,YAAY;CAExC,MAAM,8BADuB,4BAA4B,WAAW,QAChC,CAAA,CAAqB,SAAS;CAClE,MAAM,kBAAkB,qBAAqB;CAG7C,MAAM,kBAAkB,uBAAuB,QAAQ;CAEvD,MAAM,UAAU,GAAG,4BAA4B;;;gBAGjC,UAAU,YAAY,WAAW,GAAG;EAClD,oBAAoB,GAAG,kBAAkB,MAAM,GAAG;EAClD,UAAU,kBAAkB,GAAG,UAAU,gBAAgB,MAAM,GAAG;EAClE,wBAAwB,WAAW,QAAQ,IAAI,eAAe,SAAS,IAAI,4BAA4B,IAAI,KAAK,SAAS,6BAA6B,SAAS,IAAI,KAAK,SAAS,kCAAkC,UAAU,iBAAiB,KAAK;EAAE,cAAc,eAAe,SAAS;EAAG,kBAAkB;EAA6B,WAAW;EAAW,aAAa;EAAiB,cAAc;CAAgB,CAAC,IAAI;CAMpa,MAAM,aAAa,eAAe,SAAS,IACvC;;;QAIA;CAEJ,MAAM,aAAa,SACf;gBACU,gBAAgB,YAAY,EAAE;;EAE5C,mBAAmB,WAAW,KAAK,EAAE;;EAErC,uBAAuB,WAAW,EAAE,UAAU,UAAU,SAAS,CAAC,EAAE;EAEpE,oBACI,GAAG,WAAW;EAClB,sBAAsB,SAAS,EAAE;;iCAEF,YAAY,mBAAmB;;;;QAK1D,8BACE,GAAG,WAAW;EACpB,sBAAsB,SAAS,EAAE;;4EAG3B;;;;EAIN,WAAW;EACX,sBAAsB,SAAS,EAAE;;OAGlC;;IAGK;CAEJ,MAAM,aAAa,SACf;YACM,gBAAgB,YAAY,EAAE;;EAExC,mBAAmB,WAAW,IAAI,EAAE;;EAEpC,uBAAuB,WAAW,EAAE,UAAU,UAAU,SAAS,CAAC,EAAE;;EAEpE,sBAAsB,SAAS,EAAE;;;;;;EAOjC,YAAY,qBACR;iCAC2B,YAAY,mBAAmB;;mFAG1D;sDAEL;;IAGK;CAEJ,MAAM,gBAAgB,YAClB;YACM,gBAAgB,YAAY,EAAE;;EAExC,mBAAmB,WAAW,IAAI,EAAE;EACpC,uBAAuB,WAAW,EAAE,UAAU,UAAU,SAAS,CAAC,EAAE;;EAEpE,sBAAsB,SAAS,EAAE;;;;;IAM7B;CAEJ,OAAO,UAAU,aAAa,aAAa;AAC7C;;;;AAKA,SAAS,4BACP,aACA,UACA,YACA,WACA,UACA,SACQ;CACR,IAAI,WAAW,WAAW,GACxB,MAAM,IAAI,MAAM,wDAAwD;CAG1E,MAAM,CAAC,aAAa;CACpB,MAAM,EAAE,iBAAiB,aAAa,aAAa;CACnD,MAAM,kBAAkB,2BACtB,aACA,iBACA,UAAU,iBACV,SACA,QACF;CACA,MAAM,gBAAgB,2BACpB,aACA,UAAU,eACV,WACA,SACA,QACF;CACA,MAAM,cAAc,sBAAsB,CACxC,gBAAgB,iBAChB,cAAc,eAChB,CAAC;CASD,IAPsB,WAAW,MAC9B,SACC,KAAK,aAAa,YAClB,KAAK,oBAAoB,mBACzB,KAAK,YAAY,UAAU,YAAY,KAGvC,GACF,MAAM,IAAI,MACR,mDAAmD,gBAAgB,8EAErE;CAGF,MAAM,cAAc;EAClB;EACA,aAAa,gBAAgB,YAAY,UAAU,eAC/C,sDACA;EACJ;EACA;CACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,IAAI;CAEZ,MAAM,eACJ,eAAe,SAAS,KACxB,WAAW,MACR,SAAS,CAAC,CAAC,KAAK,mBAAmB,eAAe,KAAK,eAAe,CACzE;CACF,MAAM,WAAW,WACd,KAAK,SACJ,2BACE,KAAK,iBACL,KAAK,YACL,KAAK,WACL,KAAK,aACL,KAAK,UACL,cACA,gBAAgB,UAChB,cAAc,QAChB,CACF,CAAC,CACA,KAAK,IAAI;CAEZ,OAAO,GAAG,4BAA4B;;;EAGtC,YAAY;EACZ,wBAAwB,WAAW,QAAQ,IAAI,eAAe,4BAA4B,IAAI,GAAG;EACjG;AACF;AAEA,SAAS,2BACP,iBACA,YACA,WACA,aACA,UACA,cACA,gBACA,cACQ;CACR,MAAM,cAAc,YAAY;CAGhC,MAAM,aAAa,CACjB,8BAA8B,YAAY,WAAW,MAAM,GAC7D;CACA,IAAI,cACF,WAAW,KAAK,mCAAmC;CAErD,MAAM,gBAAgB,WAAW,KAAK,IAAI;CAE1C,MAAM,eADW,UAAU,WAAW,SAAS;CAE/C,MAAM,wBAAwB,sBAC5B,YAAY,eAAe,SAAS,GACpC,YACF;CACA,MAAM,kBAAkB,sBAAsB,MAAM,YAAY;CAEhE,IAAI,aAAa,cAAc;EAC7B,MAAM,cAAc,uBAClB,YACA,WACA,aACA,cACA,UAAU,QACZ;EACA,MAAM,qBAAqB,UAAU,WACjC,oBACA;EACJ,MAAM,mBAAmB,UAAU,WAC/B,mEAAmE,aAAa,OAChF;EACJ,MAAM,gBAAgB,gCACpB,WACA,aACA,YACF;EACA,MAAM,iBAAiB,0BACrB,WACA,cAAc,iBAChB;EAEA,OAAO,gCAAgC,WAAW;eACvC,YAAY,4BAA4B,sBAAsB;EAC3E,cAAc;;EAEd,uBAAuB,iBAAiB,EAAE,UAAU,eAAe,CAAC,EAAE;qDACnB,aAAa;EAChE,qCAAqC,eAAe,EAAE;EACtD,iBAAiB;;EAEjB,cAAc,cAAc,OAAO,mBAAmB,gCAClD,oBACA,YACA,cACF,EAAE;;;eAGS,WAAW;;;;;CAKxB;CAEA,IAAI,YAAY,UAAU,cAAc;EACtC,MAAM,cAAc,uBAClB,YACA,WACA,aACA,cACA,IACF;EACA,MAAM,gBAAgB,gCACpB,WACA,aACA,YACF;EACA,MAAM,iBAAiB,0BACrB,WACA,cAAc,iBAChB;EACA,OAAO,gCAAgC,WAAW;eACvC,YAAY,4BAA4B,sBAAsB;EAC3E,cAAc;;gDAEgC,gBAAgB;EAC9D,gCAAgC,eAAe,EAAE;;EAEjD,cAAc,cAAc,OAAO,sDAAsD,aAAa;mBACrF,gCACf,YACA,YACA,cACF,EAAE;;;eAGW,WAAW;;;;;CAKxB;CAEA,MAAM,cAAc,uBAClB,YACA,WACA,aACA,cACA,KACF;CACA,MAAM,iBACJ,gBAAgB,YAAY,WAAW,QACnC;;;QAIA;CACN,MAAM,iBAAiB,0BAA0B,SAAS;CAC1D,OAAO,qBAAqB,WAAW;eAC1B,YAAY,4BAA4B,gBAAgB;EACrE,cAAc;;EAEd,uBAAuB,iBAAiB,EAAE,UAAU,eAAe,CAAC,EAAE;EACtE,eAAe;EACf,sBAAsB,eAAe,EAAE;;EAEvC,YAAY,mBAAmB,gCAC7B,QACA,YACA,cACF,EAAE;;;eAGW,WAAW;;;;;AAK1B;AAEA,SAAS,+BACP,WACA,UACQ;CACR,MAAM,MAAM,UAAU,OAAO,CAAC;CAC9B,MAAM,cAAc,IAAI,gBAAgB;CACxC,MAAM,iBAAiB,IAAI,mBAAmB;CAC9C,MAAM,eAAe,IAAI,iBAAiB;CAC1C,MAAM,kBAAkB,WAAW,KAAK,UAAU,QAAQ,IAAI;CAE9D,OAAO,GAAG,4BAA4B;;;;;;;kCAON,KAAK,UAAU,WAAW,EAAE;qCACzB,KAAK,UAAU,cAAc,EAAE;wBAC5C,KAAK,UAAU,YAAY,EAAE;6DACQ,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiG7E;;;;AAKA,SAAS,gBACP,aACA,SACA,qBACM;CACN,MAAM,gBAAgB,KAAK,aAAa,YAAY;CAGpD,IAAI,mBAAmB;CACvB,IAAI,WAAW,aAAa,GAC1B,mBAAmB,aAAa,eAAe,OAAO;CAGxD,MAAM,iBAAiB,CACrB,GAAG,IAAI,IACL,oBAAoB,KAAK,SACvB,wBAAwB,SAAS,aAAa,IAAI,CAAC,CACrD,CACF,CACF,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;CACnC,MAAM,iBAAiB,gCACrB,kBACA,QAAQ,WACR,cACF;CAEA,IAAI,mBAAmB,kBAAkB;EACvC,cAAc,eAAe,gBAAgB,OAAO;EACpD,QAAQ,IAAI,sDAAsD;CACpE;AACF;AAEA,SAAS,wBAAwB,MAAsB;CAIrD,OAAO,KAAK,WAAW,MAAM,GAAG,CAAC,CAAC,WAAW,kBAAkB,MAAM;AACvE;AAEA,SAAS,gCACP,kBACA,WACA,gBACQ;CACR,MAAM,QAAQ,iBAAiB,MAAM,IAAI;CACzC,MAAM,aAAa,MAAM,QAAQ,6BAA6B;CAC9D,MAAM,WAAW,MAAM,QAAQ,2BAA2B;CAC1D,MAAM,eAAe;EACnB;EACA,GAAG;EACH;CACF;CAEA,IAAI,eAAe,MAAM,aAAa,IAAI;EACxC,IAAI,eAAe,MAAM,WAAW,YAGlC,OAAO;EAGT,MAAM,OACJ,YACA,WAAW,aAAa,GACxB,GAAI,eAAe,SAAS,IAAI,eAAe,CAAC,CAClD;EACA,OAAO,MAAM,KAAK,IAAI;CACxB;CAKA,MAAM,gBAAgB,GAAG,UAAU,WAAW,MAAM,GAAG,EAAE;CACzD,MAAM,cAAc,MAAM,WACvB,MAAM,UACL,SAAS,2BAA2B,MAAM,QAAQ,OAAO,aAC7D;CACA,IAAI,gBAAgB,IAClB,MAAM,OAAO,aAAa,CAAC;CAG7B,IAAI,eAAe,WAAW,GAC5B,OAAO,MAAM,KAAK,IAAI;CAGxB,IAAI,iBAAiB,MAAM,KAAK,IAAI;CACpC,IAAI,eAAe,SAAS,KAAK,CAAC,eAAe,SAAS,IAAI,GAC5D,kBAAkB;CAEpB,IAAI,eAAe,SAAS,KAAK,CAAC,eAAe,SAAS,MAAM,GAC9D,kBAAkB;CAGpB,OAAO,GAAG,iBAAiB,aAAa,KAAK,IAAI,EAAE;AACrD"}
|