@happyvertical/smrt-users 0.31.1 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"sveltekit.js","sources":["../src/sveltekit/resource-list-handler.ts","../src/sveltekit/types.ts","../src/sveltekit/index.ts"],"sourcesContent":["/**\n * Resource-list handler for `GET /api/_resources`.\n *\n * Returns the auth-aware list of resources and commands that an HTTP-based\n * CLI client (e.g. `@happyvertical/smrt-app-cli`) can invoke. Walks the\n * `ObjectRegistry` to find classes whose `@smrt({ cli: ... })` decorator\n * exposes commands, resolves each command's URL shape (scope, http method,\n * path segments) the same way the SvelteKit route generator does, and\n * filters the result through a caller-supplied `commandPolicy`.\n *\n * @packageDocumentation\n *\n * @example\n * ```ts\n * // apps/<app>/src/routes/api/_resources/+server.ts\n * import { createResourceListHandler } from '@happyvertical/smrt-users/sveltekit';\n * import './smrt-register';\n *\n * export const GET = createResourceListHandler({\n * ensureRegistry: async () => { await import('./smrt-register'); },\n * commandPolicy: ({ command, session }) => session.user != null,\n * kebabRoutes: true, // match the vite-plugin setting\n * });\n * ```\n */\n\n// Importing the registration shim here keeps this subpath self-sufficient\n// (mirrors the pattern in `./index.ts`).\nimport '../__smrt-register__.js';\n\nimport {\n ObjectRegistry,\n type SmartObjectConfig,\n} from '@happyvertical/smrt-core';\nimport { classnameToTablename } from '@happyvertical/smrt-core/utils';\nimport {\n methodNameToKebab,\n resolveApiActionSet,\n} from '@happyvertical/smrt-core/vite-plugin';\nimport type { TerminalAuthServiceOptions } from '../services/TerminalAuthService.js';\nimport { loadBearerSessionContext, parseBearerToken } from './index.js';\nimport type { SessionLocals } from './types.js';\n\n/** Shape returned by `ObjectRegistry.getAllClasses()` values. Mirrors the\n * relevant parts of `RegisteredClass` from `@happyvertical/smrt-core/registry/types`.\n * Kept structural so we don't depend on a non-public type export. */\ninterface RegisteredClassLike {\n name: string;\n qualifiedName?: string;\n packageName?: string;\n config: SmartObjectConfig;\n methods: Map<string, unknown>;\n tools?: ToolLike[];\n /**\n * Pluralized endpoint name from the manifest (e.g. `sourcecrawls`). Carried\n * on the registered class since smrt-core 0.26.4 (smrt#1311); used verbatim\n * as the URL segment so the CLI hits the same path the generator wrote.\n */\n collection?: string;\n /** Parent class simple name — `'SmrtCollection'` marks a collection class. */\n extends?: string;\n /** Generic type argument from `SmrtCollection<X>` — also marks collection. */\n extendsTypeArg?: string;\n /**\n * Live class constructor. Used as a runtime fallback to walk the prototype\n * chain when neither `extends` nor `extendsTypeArg` are set — which\n * happens for dev-mode classes registered via the decorator path before\n * the manifest is loaded. (#1311 review D-2.)\n */\n constructor?: { name?: string; prototype?: object } | unknown;\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Types */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nconst STANDARD_API_ACTIONS = [\n 'list',\n 'get',\n 'create',\n 'update',\n 'delete',\n] as const;\n\ntype StandardApiAction = (typeof STANDARD_API_ACTIONS)[number];\ntype ApiHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type CommandKind = 'crud' | 'custom';\nexport type CommandScope = 'item' | 'collection';\n\nexport interface CommandDefinition {\n /** Method name in source casing — source of truth for HTTP routing. */\n methodName: string;\n /** Kebab-case identifier used by the CLI argv parser. */\n commandName: string;\n kind: CommandKind;\n scope: CommandScope;\n httpMethod: ApiHttpMethod;\n /** URL path segments after `/<apiPath>[/<id>]/`. May be empty. */\n pathSegments: string[];\n description?: string;\n /** JSONSchema describing the command's argv-flag surface. */\n parameters?: Record<string, unknown>;\n}\n\nexport interface CliResource {\n /** Kebab-case identifier; the first positional argument after the CLI name. */\n slug: string;\n className: string;\n qualifiedName?: string;\n packageName?: string;\n label: string;\n /** Collection segment, no leading slash, no `/api` prefix. */\n apiPath: string;\n commands: CommandDefinition[];\n}\n\nexport interface ResolvedSession {\n user: SessionLocals['user'];\n membership?: SessionLocals['membership'];\n permissions: string[];\n tenantId: string | null;\n sessionId: string | null;\n}\n\nexport interface CommandPolicyContext {\n resource: Omit<CliResource, 'commands'>;\n command: CommandDefinition;\n session: ResolvedSession;\n /**\n * Stable identifiers for policy authors that need more than the\n * caller-facing `resource` view (e.g. package-scoped role checks).\n */\n classMeta: {\n name: string;\n qualifiedName?: string;\n packageName?: string;\n decoratorConfig: SmartObjectConfig;\n };\n}\n\nexport interface ResourceListResponseBody {\n user: { authenticated: boolean; id?: string };\n warnings: string[];\n resources: CliResource[];\n}\n\n/**\n * Per-command lint warning emitted by the handler when a method couldn't be\n * surfaced as a command (dynamic path params, DELETE-with-body, multi-arg,\n * etc.). Filtered through `commandPolicy` so callers only see warnings for\n * commands they would have been allowed to invoke.\n */\ninterface SkipWarning {\n /** The class/method this warning pertains to (`Praeco.discover`). */\n ref: string;\n /** Human-readable reason: `dynamic-path-params unsupported` etc. */\n reason: string;\n /** Candidate command — used by policy to decide visibility. */\n candidate: CommandDefinition;\n /** The owning class for visibility scoping. */\n resourceMeta: Omit<CliResource, 'commands'>;\n classMeta: CommandPolicyContext['classMeta'];\n}\n\nexport interface CreateResourceListHandlerOptions\n extends TerminalAuthServiceOptions {\n /**\n * Ensures `ObjectRegistry` is populated before the handler walks it.\n *\n * v0.1 escape hatch: the consumer app must trigger its `@smrt()` side\n * effects (typically by importing the generated `smrt-register.ts`).\n * Without this, a fresh request handler process may see an empty\n * registry and return zero resources.\n */\n ensureRegistry: () => void | Promise<void>;\n\n /**\n * Resolve the caller's session. Defaults to `event.locals` (set by\n * `createSessionHandler` in `hooks.server.ts`) with a `Bearer <token>`\n * fallback for terminal-auth CLI clients.\n *\n * If a bearer token is present but doesn't resolve to a live session,\n * the handler responds 401 — NOT silent anonymous, so a stale CLI token\n * gets a clear signal to re-authenticate.\n */\n resolveSession?: (event: SveltekitEvent) => Promise<ResolvedSession>;\n\n /**\n * Per-command permission filter. Default: deny everything when the\n * caller is anonymous; allow everything when authenticated.\n *\n * Note: this is _capability filtering_, not row-level authorization.\n * Per-route handlers remain authoritative for `can user X update record Y`.\n */\n commandPolicy?: (ctx: CommandPolicyContext) => boolean | Promise<boolean>;\n\n /**\n * Override the slug derivation. Default: kebab-case of `collection`\n * (which is already plural+lowercase from the manifest generator).\n */\n resourceSlug?: (meta: {\n className: string;\n collection: string;\n qualifiedName?: string;\n packageName?: string;\n }) => string;\n\n /**\n * Match the vite plugin's `svelteKit.kebabRoutes` setting. When `true`,\n * custom method URL segments are kebab-cased on the wire (the CLI sends\n * `/discover-from-url`); when `false`, source-cased (`/discoverFromUrl`).\n * Defaults to `false` to match the vite plugin default.\n */\n kebabRoutes?: boolean;\n}\n\ntype SveltekitEvent = {\n cookies: { get: (name: string) => string | undefined };\n locals?: Record<string, unknown>;\n request: Request;\n url: URL;\n};\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Public factory */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nexport function createResourceListHandler(\n options: CreateResourceListHandlerOptions,\n): (event: SveltekitEvent) => Promise<Response> {\n const {\n ensureRegistry,\n resolveSession = (event) => defaultResolveSession(event, options),\n commandPolicy = defaultCommandPolicy,\n resourceSlug = defaultResourceSlug,\n kebabRoutes = false,\n } = options;\n\n return async (event: SveltekitEvent): Promise<Response> => {\n await ensureRegistry();\n\n let session: ResolvedSession;\n try {\n session = await resolveSession(event);\n } catch (error) {\n if (error instanceof InvalidBearerError) {\n return jsonResponse({ error: error.message }, 401);\n }\n throw error;\n }\n\n const resources: CliResource[] = [];\n const skipWarnings: SkipWarning[] = [];\n const slugSeen = new Map<string, string>();\n\n for (const [registeredName, registered] of ObjectRegistry.getAllClasses()) {\n const def = synthesizeDefinition(\n registeredName,\n registered as unknown as RegisteredClassLike,\n );\n const cliConfig = def.decoratorConfig.cli;\n if (!cliConfig) continue;\n if (!isHttpCliConfig(cliConfig)) continue;\n\n // Skip SmrtCollection subclasses. The generator routes these through\n // a different code path that only emits collection-scoped custom\n // routes (no CRUD), so surfacing CRUD commands here would 404 and\n // item-scoped custom commands would call URLs the generator never\n // wrote. See #1311 review (codex P2).\n if (isCollectionClass(def)) continue;\n\n // Resolve included method set per v4 rules.\n // resolveApiActionSet only reads decoratorConfig + methods; cast is safe.\n const apiActionSet = resolveApiActionSet(\n def as unknown as Parameters<typeof resolveApiActionSet>[0],\n );\n const includedMethods = resolveCliIncludedMethods(\n cliConfig,\n apiActionSet,\n );\n if (includedMethods.length === 0) continue;\n\n // Compute the resource shell first; commands are derived from it.\n const slug = resourceSlug({\n className: def.className,\n collection: def.collection,\n qualifiedName: def.qualifiedName,\n packageName: def.packageName,\n });\n const previous = slugSeen.get(slug);\n if (previous && previous !== def.qualifiedName) {\n return jsonResponse(\n {\n error: 'resource-slug-collision',\n slug,\n classes: [previous, def.qualifiedName ?? def.className].filter(\n Boolean,\n ),\n },\n 500,\n );\n }\n slugSeen.set(slug, def.qualifiedName ?? def.className);\n\n const resourceShell: Omit<CliResource, 'commands'> = {\n slug,\n className: def.className,\n qualifiedName: def.qualifiedName,\n packageName: def.packageName,\n label: humanLabel(def.className),\n apiPath: resolveApiPath(def),\n };\n\n const classMeta: CommandPolicyContext['classMeta'] = {\n name: def.className,\n qualifiedName: def.qualifiedName,\n packageName: def.packageName,\n decoratorConfig: def.decoratorConfig,\n };\n\n // Build candidate commands + collect skip warnings.\n // `commandNameSeen` is empty at start; CRUD commands populate it as\n // they're built. Custom commands that kebab to a CRUD name are caught\n // by the explicit CRUD-shadowing check below before they get here.\n const commandNameSeen = new Set<string>();\n const candidates: CommandDefinition[] = [];\n\n for (const methodName of includedMethods) {\n const result = buildCommand(def, methodName, kebabRoutes);\n if (!result.ok) {\n skipWarnings.push({\n ref: `${def.className}.${methodName}`,\n reason: result.reason,\n candidate: result.candidate,\n resourceMeta: resourceShell,\n classMeta,\n });\n continue;\n }\n\n // Detect kebab collisions and CRUD shadowing inside this class.\n if (\n result.command.kind === 'custom' &&\n STANDARD_API_ACTIONS.includes(\n result.command.commandName as StandardApiAction,\n )\n ) {\n return jsonResponse(\n {\n error: 'command-name-shadows-crud',\n className: def.className,\n methodName,\n commandName: result.command.commandName,\n },\n 500,\n );\n }\n if (commandNameSeen.has(result.command.commandName)) {\n return jsonResponse(\n {\n error: 'command-name-collision',\n className: def.className,\n commandName: result.command.commandName,\n },\n 500,\n );\n }\n commandNameSeen.add(result.command.commandName);\n candidates.push(result.command);\n }\n\n // Run the command policy. Batch with Promise.all so an async\n // `commandPolicy` (e.g. one that hits a DB) fans out across all\n // candidates instead of serializing N round-trips per class. For\n // sync policies the difference is zero. (#1311 review P1.)\n const policyResults = await Promise.all(\n candidates.map((candidate) =>\n commandPolicy({\n resource: resourceShell,\n command: candidate,\n session,\n classMeta,\n }),\n ),\n );\n const allowed = candidates.filter((_, i) => policyResults[i]);\n if (allowed.length === 0) continue;\n\n resources.push({ ...resourceShell, commands: allowed });\n }\n\n // Filter warnings through policy: only surface warnings the caller could\n // have seen the underlying command for. Same Promise.all batching.\n const warnResults = await Promise.all(\n skipWarnings.map((w) =>\n commandPolicy({\n resource: w.resourceMeta,\n command: w.candidate,\n session,\n classMeta: w.classMeta,\n }),\n ),\n );\n const visibleWarnings = skipWarnings\n .filter((_, i) => warnResults[i])\n .map((w) => `${w.ref}: ${w.reason}`);\n\n const body: ResourceListResponseBody = {\n user: session.user\n ? { authenticated: true, id: String(session.user.id ?? '') }\n : { authenticated: false },\n warnings: visibleWarnings,\n resources,\n };\n\n return jsonResponse(body, 200, {\n 'cache-control': 'private, no-store',\n });\n };\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — synthesis */\n/* ────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Shape we feed into `resolveApiActionSet`. Mirrors the relevant parts of\n * `SmartObjectDefinition` from `@happyvertical/smrt-core/scanner` so we\n * can compute the API set against runtime-registered classes (which the\n * registry stores as `RegisteredClass`, not as `SmartObjectDefinition`).\n */\ninterface SynthesizedDefinition {\n className: string;\n qualifiedName?: string;\n packageName?: string;\n collection: string;\n decoratorConfig: SmartObjectConfig;\n methods: Record<string, MethodLike>;\n tools?: ToolLike[];\n extends?: string;\n extendsTypeArg?: string;\n /** Live constructor for prototype-chain fallback in isCollectionClass. */\n constructor?: unknown;\n}\n\ninterface MethodLike {\n name: string;\n isStatic?: boolean;\n isPublic?: boolean;\n parameters?: Array<{ name: string; type: string; optional?: boolean }>;\n returnType?: string;\n description?: string;\n}\n\ninterface ToolLike {\n type: 'function';\n function: {\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n };\n}\n\nfunction synthesizeDefinition(\n _registeredName: string,\n registered: RegisteredClassLike,\n): SynthesizedDefinition {\n const methods: Record<string, MethodLike> = {};\n for (const [name, value] of registered.methods.entries()) {\n methods[name] = value as MethodLike;\n }\n\n const decoratorConfig = (registered.config ?? {}) as SmartObjectConfig;\n // Prefer the manifest-derived `collection` the registry now carries\n // (smrt#1311). This is the SAME pluralized endpoint segment the SvelteKit\n // route generator writes to disk — e.g. `sourcecrawls` for `SourceCrawl`,\n // NOT the snake_case `tableName` (`source_crawls`). The tableName-based\n // derivation below is only a fallback for older smrt-core builds that\n // didn't store `collection` on the registered class.\n const collection =\n registered.collection ??\n deriveCollectionFromConfig(decoratorConfig) ??\n classnameToTablename(registered.name);\n\n return {\n className: registered.name,\n qualifiedName: registered.qualifiedName,\n packageName: registered.packageName,\n collection,\n decoratorConfig,\n methods,\n tools: registered.tools as ToolLike[] | undefined,\n extends: registered.extends,\n extendsTypeArg: registered.extendsTypeArg,\n constructor: registered.constructor,\n };\n}\n\n/**\n * Detect `SmrtCollection<T>` subclasses. The SvelteKit generator routes\n * these through a separate code path that ONLY emits collection-scoped\n * custom routes (no CRUD), so the CLI must do the same — treating them\n * as regular object classes would expose CRUD commands the generator\n * never wrote.\n *\n * Three signals, in order of confidence:\n *\n * 1. `extends === 'SmrtCollection'` — manifest-derived, always reliable\n * when classes come from a generated manifest.\n * 2. `extendsTypeArg` — also manifest-derived; covers the\n * `SmrtCollection<X>` generic form.\n * 3. Prototype-chain walk on `constructor` — runtime fallback for\n * classes registered via the decorator path in dev mode (or in any\n * no-manifest setup) where signals (1) and (2) may be absent.\n * Walks up `Object.getPrototypeOf(ctor)` looking for `name ===\n * 'SmrtCollection'`. Bounded to a few iterations by a depth guard.\n *\n * Mirrors the same predicate in `packages/core/src/vite-plugin/\n * sveltekit-generator.ts:isCollectionClass` but extends it with the\n * prototype-walk fallback. (#1311 review D-2.)\n */\nfunction isCollectionClass(def: SynthesizedDefinition): boolean {\n if (def.extends === 'SmrtCollection') return true;\n if (def.extendsTypeArg) return true;\n return ctorChainContains(def.constructor, 'SmrtCollection');\n}\n\nfunction ctorChainContains(\n ctor: unknown,\n name: string,\n depthCap = 16,\n): boolean {\n let current = ctor as { name?: string } | null;\n for (let i = 0; i < depthCap && current; i += 1) {\n if (current.name === name) return true;\n const next = Object.getPrototypeOf(current);\n // Stop when we've reached the root prototype (`Function.prototype`),\n // which has no `.name` field of its own.\n if (!next || next === current) break;\n current = next as { name?: string } | null;\n }\n return false;\n}\n\nfunction deriveCollectionFromConfig(\n config: SmartObjectConfig,\n): string | undefined {\n // `tableName` doubles as the collection-path source in the generator.\n return config?.tableName ?? undefined;\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — command resolution */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nfunction resolveCliIncludedMethods(\n cliConfig: NonNullable<SmartObjectConfig['cli']>,\n apiActionSet: Set<string>,\n): string[] {\n if (cliConfig === true) {\n // Boolean shorthand: CRUD only (intersected with what the API exposes).\n return STANDARD_API_ACTIONS.filter((a) => apiActionSet.has(a));\n }\n if (cliConfig === false) return [];\n\n // Decorator config comes from registered classes — we cast through\n // `unknown` at the registry boundary, so anything could be in here at\n // runtime: malformed manifests, JS-authored decorators with `cli:\n // 'true'`, typos like `cli: { include: 'methodName' }` (string instead\n // of array), null, arrays at the top level, etc.\n //\n // Two failure modes to guard against:\n // 1. Crash (e.g. `'discoverFromUrl'.filter(...)` → TypeError 500).\n // Always bad — the endpoint stops working for the whole app.\n // 2. Silently wrong behavior (e.g. malformed `include` falls through\n // to a CRUD-only default, so the developer who typed `include:\n // 'foo'` instead of `include: ['foo']` gets CRUD commands they\n // didn't ask for).\n //\n // Both are worse than returning [] for that class. So: if ANY\n // recognised field is the wrong type, treat the class as having no\n // CLI commands — the developer's misconfiguration is loud (no\n // commands surface), not silent. (#1311 review A1+A2.)\n if (typeof cliConfig !== 'object' || cliConfig === null) return [];\n if (Array.isArray(cliConfig)) return [];\n\n const obj = cliConfig as {\n include?: unknown;\n exclude?: unknown;\n mirror?: unknown;\n };\n if (obj.include !== undefined && !Array.isArray(obj.include)) return [];\n if (obj.exclude !== undefined && !Array.isArray(obj.exclude)) return [];\n if (obj.mirror !== undefined && obj.mirror !== 'api') return [];\n\n const include = obj.include as string[] | undefined;\n const exclude = obj.exclude as string[] | undefined;\n let candidates: string[];\n\n if (obj.mirror === 'api') {\n candidates = [...apiActionSet];\n } else if (include?.includes('*')) {\n candidates = [...apiActionSet];\n } else if (include && include.length > 0) {\n candidates = include.filter((m) => apiActionSet.has(m));\n } else {\n // No include + no mirror → CRUD only.\n candidates = STANDARD_API_ACTIONS.filter((a) => apiActionSet.has(a));\n }\n\n if (exclude && exclude.length > 0) {\n const excludeSet = new Set(exclude);\n candidates = candidates.filter((m) => !excludeSet.has(m));\n }\n return candidates;\n}\n\nfunction isHttpCliConfig(\n cliConfig: NonNullable<SmartObjectConfig['cli']>,\n): boolean {\n if (typeof cliConfig !== 'object' || cliConfig === null) return true;\n if (Array.isArray(cliConfig)) return true;\n return (cliConfig as { http?: unknown }).http !== false;\n}\n\ninterface BuildSuccess {\n ok: true;\n command: CommandDefinition;\n}\ninterface BuildSkip {\n ok: false;\n reason: string;\n candidate: CommandDefinition;\n}\n\nfunction buildCommand(\n def: SynthesizedDefinition,\n methodName: string,\n kebabRoutes: boolean,\n): BuildSuccess | BuildSkip {\n const isCrud = STANDARD_API_ACTIONS.includes(methodName as StandardApiAction);\n\n if (isCrud) {\n return {\n ok: true,\n command: buildCrudCommand(methodName as StandardApiAction),\n };\n }\n\n const methodDef = def.methods[methodName];\n if (!methodDef) {\n // Manifest doesn't know this method — fall through anyway so the user\n // sees something coherent rather than silently dropping it.\n return {\n ok: true,\n command: buildCustomCommand(def, methodName, undefined, kebabRoutes),\n };\n }\n\n // Multi-arg: skip with warning. v0.1 only supports single-options-object.\n const paramCount = methodDef.parameters?.length ?? 0;\n if (paramCount > 1) {\n return {\n ok: false,\n reason: 'multi-arg unsupported (use single-options-object convention)',\n candidate: buildCustomCommand(def, methodName, methodDef, kebabRoutes),\n };\n }\n\n const candidate = buildCustomCommand(def, methodName, methodDef, kebabRoutes);\n\n // Dynamic path-param segments (`[id]`, `:foo`) → unsupported in v0.1.\n if (hasDynamicSegments(candidate.pathSegments)) {\n return {\n ok: false,\n reason: 'dynamic-path-params unsupported',\n candidate,\n };\n }\n\n // DELETE-with-body — skip. DELETE is OK for `<col>/<id>` (id from positional),\n // but a custom DELETE method that accepts a body is too fragile through\n // proxies/frameworks. Detect by either a non-empty schema OR a methodDef\n // that takes any parameters at all.\n if (candidate.httpMethod === 'DELETE' && candidate.kind === 'custom') {\n const methodHasParams = (methodDef.parameters?.length ?? 0) > 0;\n if (methodHasParams || hasNonIdParameters(candidate.parameters)) {\n return {\n ok: false,\n reason: 'DELETE-with-body unsupported',\n candidate,\n };\n }\n }\n\n return { ok: true, command: candidate };\n}\n\nfunction buildCrudCommand(name: StandardApiAction): CommandDefinition {\n const item = name === 'get' || name === 'update' || name === 'delete';\n const httpMethod: ApiHttpMethod =\n name === 'list' || name === 'get'\n ? 'GET'\n : name === 'create'\n ? 'POST'\n : name === 'update'\n ? 'PUT'\n : 'DELETE';\n\n return {\n methodName: name,\n commandName: name,\n kind: 'crud',\n scope: item ? 'item' : 'collection',\n httpMethod,\n pathSegments: [],\n description: defaultCrudDescription(name),\n };\n}\n\nfunction buildCustomCommand(\n def: SynthesizedDefinition,\n methodName: string,\n methodDef: MethodLike | undefined,\n kebabRoutes: boolean,\n): CommandDefinition {\n const apiConfigObj = getApiConfigObject(def.decoratorConfig.api);\n const routeConfig = apiConfigObj?.routes?.[methodName] as\n | { scope?: CommandScope; method?: string; path?: string }\n | undefined;\n\n const defaultScope: CommandScope = methodDef?.isStatic\n ? 'collection'\n : 'item';\n const scope = routeConfig?.scope ?? defaultScope;\n const httpMethod = normalizeApiHttpMethod(routeConfig?.method);\n\n let pathSegments: string[];\n if (routeConfig?.path) {\n // Explicit override — used verbatim, no kebab transform.\n pathSegments = routeConfig.path\n .split('/')\n .map((s) => s.trim())\n .filter(Boolean);\n if (pathSegments.length === 0) pathSegments = [methodName];\n } else {\n pathSegments = [kebabRoutes ? methodNameToKebab(methodName) : methodName];\n }\n\n const parameters = resolveParametersSchema(def, methodName, methodDef);\n\n return {\n methodName,\n commandName: methodNameToKebab(methodName),\n kind: 'custom',\n scope,\n httpMethod,\n pathSegments,\n description: methodDef?.description,\n parameters,\n };\n}\n\nfunction resolveParametersSchema(\n def: SynthesizedDefinition,\n methodName: string,\n _methodDef: MethodLike | undefined,\n): Record<string, unknown> | undefined {\n // 1. Prefer the tool JSONSchema (build-time generated by `smrt scan`).\n const tool = def.tools?.find((t) => t.function.name === methodName);\n if (tool?.function.parameters) {\n const params = tool.function.parameters;\n // Reject empty schemas (`{}` or `{ type: 'object', properties: {} }`) —\n // these carry no real flag information so the parser would otherwise\n // treat them as \"supported\" and accept arbitrary string-typed flags\n // without coercion. Returning `undefined` routes the CLI to the\n // positional-JSON fallback with a clear stderr hint instead. See\n // #1311 review (codex P2 schema).\n if (hasUsableProperties(params)) return params;\n }\n\n // 2. No usable schema — surface `undefined` so the CLI parser switches\n // to the positional-JSON escape hatch. Inferring `{ type: 'object',\n // additionalProperties: true }` from a single `options: { … }` source-\n // type signal would silently accept untyped flags and send strings to\n // numeric fields — worse than just asking for a JSON payload.\n return undefined;\n}\n\nfunction hasUsableProperties(\n schema: Record<string, unknown> | undefined,\n): boolean {\n if (!schema || typeof schema !== 'object') return false;\n const props = (schema as { properties?: Record<string, unknown> }).properties;\n if (!props || typeof props !== 'object') return false;\n return Object.keys(props).length > 0;\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\nfunction getApiConfigObject(\n api: SmartObjectConfig['api'],\n): { routes?: Record<string, unknown> } | undefined {\n if (typeof api !== 'object' || api === null) return undefined;\n return api as { routes?: Record<string, unknown> };\n}\n\nfunction hasDynamicSegments(segments: string[]): boolean {\n return segments.some((s) => /^\\[.+\\]$/.test(s) || s.startsWith(':'));\n}\n\nfunction hasNonIdParameters(parameters?: Record<string, unknown>): boolean {\n if (!parameters || typeof parameters !== 'object') return false;\n const props = (parameters as { properties?: Record<string, unknown> })\n .properties;\n if (!props) return false;\n const keys = Object.keys(props).filter((k) => k !== 'id');\n return keys.length > 0;\n}\n\nfunction defaultCrudDescription(name: StandardApiAction): string {\n switch (name) {\n case 'list':\n return 'List records';\n case 'get':\n return 'Get a record by id';\n case 'create':\n return 'Create a new record';\n case 'update':\n return 'Update an existing record';\n case 'delete':\n return 'Delete a record';\n }\n}\n\nfunction resolveApiPath(def: SynthesizedDefinition): string {\n // IMPORTANT: must match what the sveltekit generator writes to disk.\n // The generator uses `objectDef.collection` verbatim and does NOT read\n // `api.path` for the directory name. If you want a custom URL segment,\n // override `tableName` on the decorator. Returning a kebab-cased\n // collection here would produce 404s for classes like `WeatherForecast`\n // whose default collection is `weather_forecasts`. (Issue #1311 review.)\n return def.collection;\n}\n\nfunction defaultResourceSlug(meta: {\n className: string;\n collection: string;\n}): string {\n // Slug is the user-facing CLI identifier. Keeping it in sync with the\n // URL segment (which is `collection` verbatim — see resolveApiPath)\n // makes `<cli> <slug>` reliably point to where the CLI will POST.\n return meta.collection;\n}\n\nfunction humanLabel(className: string): string {\n // Praeco → Praeco; XMLExport → Xml Export. Cheap default.\n return className\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2');\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — session resolution */\n/* ────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Thrown by the default `resolveSession` when a bearer token is present in\n * the request but doesn't resolve to a live session. The handler catches\n * this and responds 401. Exported so custom `resolveSession` implementations\n * can opt in to the same semantics.\n */\nexport class InvalidBearerError extends Error {\n constructor(message = 'Invalid or expired bearer token.') {\n super(message);\n this.name = 'InvalidBearerError';\n }\n}\n\nasync function defaultResolveSession(\n event: SveltekitEvent,\n options: TerminalAuthServiceOptions,\n): Promise<ResolvedSession> {\n // 1. Locals win when set (already validated by hooks.server.ts).\n const locals = (event.locals ?? {}) as Partial<SessionLocals>;\n if (locals.user) {\n return {\n user: locals.user,\n membership: locals.membership ?? null,\n permissions: locals.permissions ?? [],\n tenantId: locals.tenantId ?? null,\n sessionId: locals.sessionId ?? null,\n };\n }\n\n // 2. Bearer-token fallback for terminal-auth CLI clients.\n const bearer = parseBearerToken(event.request.headers.get('authorization'));\n if (bearer) {\n const ctx = await loadBearerSessionContext(bearer, options);\n if (!ctx) {\n // Bearer present but doesn't resolve — fail loud, not silent anonymous.\n throw new InvalidBearerError();\n }\n return {\n user: ctx.user,\n membership: ctx.membership ?? null,\n permissions: ctx.permissions,\n tenantId: ctx.tenantId,\n sessionId: ctx.sessionId,\n };\n }\n\n // 3. Truly anonymous.\n return {\n user: null,\n membership: null,\n permissions: [],\n tenantId: null,\n sessionId: null,\n };\n}\n\nfunction defaultCommandPolicy(ctx: CommandPolicyContext): boolean {\n return ctx.session.user != null;\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — HTTP */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nfunction jsonResponse(\n body: unknown,\n status = 200,\n extraHeaders: Record<string, string> = {},\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n 'content-type': 'application/json',\n ...extraHeaders,\n },\n });\n}\n","/**\n * SvelteKit-specific type definitions for session management\n * @packageDocumentation\n */\n\nimport type { Membership } from '../models/Membership.js';\nimport type { User } from '../models/User.js';\n\n/**\n * Extended locals interface for SvelteKit\n *\n * Add to your app.d.ts:\n * ```typescript\n * declare global {\n * namespace App {\n * interface Locals extends SessionLocals {}\n * }\n * }\n * ```\n */\nexport interface SessionLocals {\n /** The authenticated user (null if not authenticated) */\n user: User | null;\n /** Active membership for the current tenant (null if none) */\n membership?: Membership | null;\n /** User's resolved permissions */\n permissions: string[];\n /** Current tenant context (null if no tenant selected) */\n tenantId: string | null;\n /** Session ID (null if no session) */\n sessionId: string | null;\n}\n\n/**\n * Default session locals values\n */\nexport const defaultSessionLocals: SessionLocals = {\n user: null,\n membership: null,\n permissions: [],\n tenantId: null,\n sessionId: null,\n};\n","/**\n * SvelteKit integration for session management\n * @packageDocumentation\n *\n * @example\n * ```typescript\n * // hooks.server.ts\n * import { createSessionHandler } from '@happyvertical/smrt-users/sveltekit';\n * import { sequence } from '@sveltejs/kit/hooks';\n *\n * const sessionHandler = createSessionHandler({\n * db: { type: 'postgres', url: process.env.DATABASE_URL }\n * });\n *\n * export const handle = sequence(sessionHandler);\n * ```\n */\n\n// Consumers that import from this subpath (e.g.\n// `@happyvertical/smrt-users/sveltekit`) typically do NOT also import the\n// package root, so the root's `__smrt-register__` side effect never runs\n// — and `SessionService` / `Session` would then evaluate their @smrt()\n// decorators against an empty manifest, falling back to zero-field\n// metadata (the original bug from issue #1132). Importing the registration\n// shim here makes this subpath self-sufficient.\nimport '../__smrt-register__.js';\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { DEFAULT_SESSION_TTL } from '../models/Session.js';\nimport {\n decodeOidcTransaction,\n encodeOidcTransaction,\n getUsersOidcConfig,\n OidcLoginError,\n type OidcLoginResult,\n OidcLoginService,\n type OidcProviderConfig,\n type OidcProviderResolutionOptions,\n type OidcTransaction,\n type ResolvedOidcProviderConfig,\n resolveOidcProviderConfig,\n} from '../services/OidcLoginService.js';\nimport { withSessionPermissionContext } from '../services/SessionPermissionContext.js';\nimport { SessionService } from '../services/SessionService.js';\nimport {\n type ApproveCliAuthRequestInput,\n TerminalAuthError,\n TerminalAuthRateLimitError,\n TerminalAuthService,\n type TerminalAuthServiceOptions,\n} from '../services/TerminalAuthService.js';\n\nexport {\n type CliResource,\n type CommandDefinition,\n type CommandKind,\n type CommandPolicyContext,\n type CommandScope,\n type CreateResourceListHandlerOptions,\n createResourceListHandler,\n InvalidBearerError,\n type ResolvedSession,\n type ResourceListResponseBody,\n} from './resource-list-handler.js';\nexport { defaultSessionLocals, type SessionLocals } from './types.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Options for session handler\n */\nexport interface SessionHandlerOptions extends SmrtClassOptions {\n /** Cookie name (default: 'sid') */\n cookieName?: string;\n /** Session TTL in seconds (default: 7 days) */\n ttl?: number;\n /** Paths to skip session loading (e.g., '/api/health') */\n skipPaths?: string[];\n /** Whether to auto-extend sessions on each request (default: false) */\n autoExtend?: boolean;\n /**\n * Cookie domain (default: undefined, uses request domain). The session\n * handler only reads the cookie; this is consumed by `createSessionCookie`\n * / `destroySessionCookie` when the same options object is shared with them.\n */\n cookieDomain?: string;\n /** Cookie path (default: '/') */\n cookiePath?: string;\n /** Whether cookies are secure (default: true in production) */\n cookieSecure?: boolean;\n /** SameSite cookie attribute (default: 'lax') */\n cookieSameSite?: 'strict' | 'lax' | 'none';\n /** Whether to enter smrt-tenancy request context when tenant data exists */\n enterTenantContext?: boolean;\n /** Whether to enforce Postgres RLS via request-scoped transactions */\n postgresRls?: boolean;\n}\n\n/**\n * SvelteKit Handle type (minimal definition to avoid requiring @sveltejs/kit as dependency)\n */\ntype HandleInput = {\n event: {\n cookies: {\n get: (name: string) => string | undefined;\n set: (\n name: string,\n value: string,\n options?: Record<string, unknown>,\n ) => void;\n delete: (name: string, options?: Record<string, unknown>) => void;\n };\n locals: Record<string, unknown>;\n url: { pathname: string; protocol?: string };\n request: { headers: Headers };\n };\n resolve: (event: unknown) => Promise<Response>;\n};\n\ntype Handle = (input: HandleInput) => Promise<Response>;\n\ntype SvelteKitRequestEvent = {\n cookies: HandleInput['event']['cookies'];\n getClientAddress?: () => string;\n locals?: Record<string, unknown>;\n params?: Record<string, string | undefined>;\n request: Request;\n url: URL;\n};\n\ntype OidcProviderResolver =\n | string\n | ((event: SvelteKitRequestEvent) => string | undefined);\n\ntype OidcStringResolver<T> =\n | T\n | ((result: OidcLoginResult, event: SvelteKitRequestEvent) => T | Promise<T>);\n\nexport interface OidcSvelteKitOptions\n extends SmrtClassOptions,\n OidcProviderResolutionOptions {\n /** Optional fetch override for tests or custom runtimes. */\n fetch?: typeof fetch;\n /** JWT clock tolerance passed to jose. */\n clockTolerance?: number | string;\n /** Provider name, or a resolver. Defaults to event.params.provider. */\n provider?: OidcProviderResolver;\n /** Callback path used when provider.redirectUri is omitted. */\n callbackPath?: string | ((providerName: string) => string);\n /** Query parameter used to preserve post-login redirects. */\n returnToParam?: string;\n /** Prefix for the temporary OIDC transaction cookie. */\n transactionCookiePrefix?: string;\n /** Temporary transaction cookie TTL in seconds. Default: 10 minutes. */\n transactionTtl?: number;\n /** Cookie path for the temporary OIDC transaction. */\n transactionCookiePath?: string;\n /** Secure flag for the temporary OIDC transaction cookie. */\n transactionCookieSecure?: boolean;\n /** SameSite value for the temporary OIDC transaction cookie. */\n transactionCookieSameSite?: 'strict' | 'lax' | 'none';\n /** HMAC secret for transaction cookie integrity. Defaults to clientSecret. */\n transactionCookieSecret?: string;\n /** Session cookie name. Defaults to sid. */\n sessionCookieName?: string;\n /** Session cookie path. Defaults to /. */\n sessionCookiePath?: string;\n /** Secure flag for the session cookie. Defaults to true on HTTPS. */\n sessionCookieSecure?: boolean;\n /** SameSite value for the session cookie. */\n sessionCookieSameSite?: 'strict' | 'lax' | 'none';\n /** Session TTL in seconds. Defaults to the package session default. */\n sessionTtl?: number;\n /** Optional tenant to bind to the session. */\n tenantId?: OidcStringResolver<string | null | undefined>;\n /** Redirect target after successful callback. */\n successRedirect?: OidcStringResolver<string>;\n /** Redirect target after failed callback. If omitted, failures return 401. */\n failureRedirect?:\n | string\n | ((error: unknown, event: SvelteKitRequestEvent) => string);\n}\n\nexport interface BeginOidcLoginResult {\n providerName: string;\n transaction: OidcTransaction;\n url: URL;\n}\n\nexport interface CompleteOidcLoginResult extends OidcLoginResult {\n providerName: string;\n returnTo?: string;\n sessionId: string;\n}\n\n/**\n * Creates a SvelteKit handle hook for session management.\n *\n * This hook:\n * 1. Reads the session cookie\n * 2. Loads session context (user + permissions) if valid\n * 3. Populates event.locals with user, permissions, tenantId, sessionId\n * 4. Optionally extends session on each request\n *\n * @example\n * ```typescript\n * // hooks.server.ts\n * import { createSessionHandler } from '@happyvertical/smrt-users/sveltekit';\n *\n * const sessionHandler = createSessionHandler({\n * db: { type: 'sqlite', url: 'app.db' },\n * cookieName: 'sid',\n * ttl: 7 * 24 * 60 * 60, // 7 days\n * skipPaths: ['/api/health', '/api/public'],\n * });\n *\n * export const handle = sessionHandler;\n * // Or with sequence:\n * // export const handle = sequence(sessionHandler, otherHandler);\n * ```\n */\nexport function createSessionHandler(options: SessionHandlerOptions): Handle {\n const cookieName = options.cookieName ?? 'sid';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n const skipPaths = options.skipPaths ?? [];\n\n // Lazy-initialized session service\n let sessionService: SessionService | null = null;\n\n const getSessionService = async (): Promise<SessionService> => {\n if (!sessionService) {\n sessionService = await SessionService.create({\n ...options,\n defaultTTL: ttl,\n autoExtend: options.autoExtend ?? false,\n });\n }\n return sessionService;\n };\n\n return async ({ event, resolve }) => {\n // Initialize locals with defaults (use property assignment for type safety)\n event.locals.user = null;\n event.locals.membership = null;\n event.locals.permissions = [];\n event.locals.tenantId = null;\n event.locals.sessionId = null;\n\n // Skip session loading for certain paths\n if (skipPaths.some((path) => event.url.pathname.startsWith(path))) {\n return resolve(event);\n }\n\n // Get session ID from cookie\n const sessionId = event.cookies.get(cookieName);\n if (!sessionId && !options.postgresRls) {\n return resolve(event);\n }\n\n try {\n const service = await getSessionService();\n return await withSessionPermissionContext(\n {\n ...options,\n enterTenantContext: options.enterTenantContext,\n postgresRls: options.postgresRls,\n sessionId,\n sessionService: service,\n },\n async (context) => {\n if (context.session) {\n event.locals.user = context.user;\n event.locals.membership = context.membership ?? null;\n event.locals.permissions = context.permissions;\n event.locals.tenantId = context.tenantId;\n event.locals.sessionId = context.sessionId;\n }\n\n return resolve(event);\n },\n );\n } catch (error) {\n logger.error('Session or request context initialization error', {\n error,\n });\n\n if (options.postgresRls) {\n return new Response('Internal Server Error', { status: 500 });\n }\n\n return resolve(event);\n }\n };\n}\n\n/**\n * Options for creating a session cookie\n */\nexport interface CreateSessionCookieOptions {\n /** Session TTL in seconds (default: 7 days) */\n ttl?: number;\n /** User agent string */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Custom session data */\n data?: Record<string, unknown>;\n}\n\n// Store for session service instances (keyed by db config hash)\nconst sessionServiceCache = new Map<string, SessionService>();\n\n/**\n * Get or create a cached session service instance\n */\nasync function getOrCreateSessionService(\n options: SmrtClassOptions,\n ttl: number,\n): Promise<SessionService> {\n // Simple cache key based on db config\n const cacheKey = JSON.stringify(options.db);\n\n let service = sessionServiceCache.get(cacheKey);\n if (!service) {\n service = await SessionService.create({\n ...options,\n defaultTTL: ttl,\n });\n sessionServiceCache.set(cacheKey, service);\n }\n return service;\n}\n\n/**\n * Helper to create a session and set the cookie after login.\n *\n * @example\n * ```typescript\n * // +page.server.ts\n * import { createSessionCookie } from '@happyvertical/smrt-users/sveltekit';\n * import { redirect } from '@sveltejs/kit';\n *\n * export const actions = {\n * login: async (event) => {\n * // Validate credentials...\n * const user = await validateLogin(email, password);\n *\n * await createSessionCookie(event, user.id, tenantId, {\n * db: { type: 'sqlite', url: 'app.db' },\n * ipAddress: event.getClientAddress(),\n * userAgent: event.request.headers.get('user-agent') ?? '',\n * });\n *\n * throw redirect(303, '/dashboard');\n * }\n * };\n * ```\n */\nexport async function createSessionCookie(\n event: HandleInput['event'],\n userId: string,\n tenantId: string | undefined,\n options: SmrtClassOptions &\n CreateSessionCookieOptions & {\n cookieName?: string;\n cookiePath?: string;\n cookieDomain?: string;\n cookieSecure?: boolean;\n cookieSameSite?: 'strict' | 'lax' | 'none';\n },\n): Promise<string> {\n const cookieName = options.cookieName ?? 'sid';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n const cookiePath = options.cookiePath ?? '/';\n const cookieSameSite = options.cookieSameSite ?? 'lax';\n const cookieSecure = options.cookieSecure ?? event.url.protocol === 'https:';\n\n const service = await getOrCreateSessionService(options, ttl);\n\n const sessionId = await service.createSession(userId, tenantId, {\n ttl,\n userAgent: options.userAgent,\n ipAddress: options.ipAddress,\n data: options.data,\n });\n\n event.cookies.set(cookieName, sessionId, {\n path: cookiePath,\n // undefined => SvelteKit scopes the cookie to the request host.\n domain: options.cookieDomain,\n httpOnly: true,\n secure: cookieSecure,\n sameSite: cookieSameSite,\n maxAge: ttl,\n });\n\n return sessionId;\n}\n\n/**\n * Helper to destroy a session and delete the cookie on logout.\n *\n * @example\n * ```typescript\n * // +page.server.ts\n * import { destroySessionCookie } from '@happyvertical/smrt-users/sveltekit';\n * import { redirect } from '@sveltejs/kit';\n *\n * export const actions = {\n * logout: async (event) => {\n * await destroySessionCookie(event, {\n * db: { type: 'sqlite', url: 'app.db' }\n * });\n * throw redirect(303, '/');\n * }\n * };\n * ```\n */\nexport async function destroySessionCookie(\n event: HandleInput['event'],\n options: SmrtClassOptions & {\n cookieName?: string;\n cookiePath?: string;\n cookieDomain?: string;\n ttl?: number;\n },\n): Promise<void> {\n const cookieName = options.cookieName ?? 'sid';\n const cookiePath = options.cookiePath ?? '/';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n\n const sessionId = event.cookies.get(cookieName);\n\n if (sessionId) {\n try {\n const service = await getOrCreateSessionService(options, ttl);\n await service.destroySession(sessionId);\n } catch (error) {\n // Log but don't fail - cookie will be deleted regardless\n logger.error('Session destruction error', { error });\n }\n }\n\n // A cookie set with a domain must be cleared with the same domain attribute.\n event.cookies.delete(cookieName, {\n path: cookiePath,\n domain: options.cookieDomain,\n });\n}\n\n/**\n * Helper to switch tenant context for the current session.\n *\n * Returns `false` without switching when there is no session, or — fail-closed\n * (#1400) — when the session's user is not an active member of `tenantId`. The\n * target tenant id is therefore safe to take straight from untrusted form data,\n * but callers MUST honour the boolean result rather than assuming success.\n *\n * @example\n * ```typescript\n * // +page.server.ts\n * import { switchSessionTenant } from '@happyvertical/smrt-users/sveltekit';\n * import { fail } from '@sveltejs/kit';\n *\n * export const actions = {\n * switchTenant: async (event) => {\n * const data = await event.request.formData();\n * const tenantId = data.get('tenantId') as string;\n *\n * const switched = await switchSessionTenant(event, tenantId, {\n * db: { type: 'sqlite', url: 'app.db' }\n * });\n * if (!switched) {\n * return fail(403, { error: 'Not a member of that tenant.' });\n * }\n *\n * return { success: true };\n * }\n * };\n * ```\n */\nexport async function switchSessionTenant(\n event: HandleInput['event'],\n tenantId: string | null,\n options: SmrtClassOptions & {\n cookieName?: string;\n ttl?: number;\n },\n): Promise<boolean> {\n const cookieName = options.cookieName ?? 'sid';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n\n const sessionId = event.cookies.get(cookieName);\n if (!sessionId) return false;\n\n const service = await getOrCreateSessionService(options, ttl);\n return service.switchTenant(sessionId, tenantId);\n}\n\nfunction getOidcProviderName(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | undefined {\n if (typeof options.provider === 'function') {\n return options.provider(event);\n }\n\n return options.provider ?? event.params?.provider ?? options.defaultProvider;\n}\n\nfunction getOidcTransactionCookieName(\n providerName: string,\n options: OidcSvelteKitOptions,\n): string {\n const prefix = options.transactionCookiePrefix ?? 'smrt_oidc';\n const safeProvider = providerName.replace(/[^a-z0-9_-]/giu, '_');\n return `${prefix}_${safeProvider}`;\n}\n\nfunction useSecureCookie(\n event: SvelteKitRequestEvent,\n explicit?: boolean,\n): boolean {\n return explicit ?? event.url.protocol === 'https:';\n}\n\nfunction resolveCallbackPath(\n providerName: string,\n options: OidcSvelteKitOptions,\n): string {\n if (typeof options.callbackPath === 'function') {\n return options.callbackPath(providerName);\n }\n\n return options.callbackPath ?? `/auth/${providerName}/callback`;\n}\n\nfunction resolveProviderWithRedirectUri(\n event: SvelteKitRequestEvent,\n providerName: string,\n provider: OidcProviderConfig,\n options: OidcSvelteKitOptions,\n): ResolvedOidcProviderConfig {\n return {\n ...provider,\n redirectUri:\n provider.redirectUri ??\n new URL(\n resolveCallbackPath(providerName, options),\n event.url.origin,\n ).toString(),\n };\n}\n\nfunction createOidcService(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): OidcLoginService {\n const providerName = getOidcProviderName(event, options);\n const resolved = resolveOidcProviderConfig(providerName, options);\n const provider = resolveProviderWithRedirectUri(\n event,\n resolved.providerName,\n resolved.provider,\n options,\n );\n\n return new OidcLoginService({\n ...options,\n provider,\n providerName: resolved.providerName,\n });\n}\n\nfunction getReturnTo(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | undefined {\n const param = options.returnToParam ?? 'returnTo';\n return getLocalReturnTo(event.url.searchParams.get(param));\n}\n\nfunction getLocalReturnTo(\n returnTo: string | null | undefined,\n): string | undefined {\n if (!returnTo) return undefined;\n // Keep return targets local to avoid open redirects.\n if (returnTo.startsWith('/') && !returnTo.startsWith('//')) {\n return returnTo;\n }\n\n return undefined;\n}\n\nfunction getTransactionCookieSameSite(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): 'strict' | 'lax' | 'none' {\n if (options.transactionCookieSameSite) {\n return options.transactionCookieSameSite;\n }\n\n return useSecureCookie(event, options.transactionCookieSecure)\n ? 'none'\n : 'lax';\n}\n\nfunction getTransactionCookieSecret(\n provider: ResolvedOidcProviderConfig,\n options: OidcSvelteKitOptions,\n): string | undefined {\n const secret = options.transactionCookieSecret ?? provider.clientSecret;\n return secret && secret.length > 0 ? secret : undefined;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary)\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replace(/=+$/u, '');\n}\n\nasync function signTransactionPayload(\n payload: string,\n secret: string,\n): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { hash: 'SHA-256', name: 'HMAC' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign(\n 'HMAC',\n key,\n new TextEncoder().encode(payload),\n );\n return bytesToBase64Url(new Uint8Array(signature));\n}\n\nfunction timingSafeEqual(left: string, right: string): boolean {\n const leftBytes = new TextEncoder().encode(left);\n const rightBytes = new TextEncoder().encode(right);\n let diff = leftBytes.length ^ rightBytes.length;\n const length = Math.max(leftBytes.length, rightBytes.length);\n\n for (let index = 0; index < length; index += 1) {\n diff |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);\n }\n\n return diff === 0;\n}\n\nasync function encodeOidcTransactionCookie(\n transaction: OidcTransaction,\n secret?: string,\n): Promise<string> {\n const payload = encodeOidcTransaction(transaction);\n if (!secret) return payload;\n\n const signature = await signTransactionPayload(payload, secret);\n return `${payload}.${signature}`;\n}\n\nasync function decodeOidcTransactionCookie(\n value: string,\n secret?: string,\n): Promise<OidcTransaction> {\n if (!secret) return decodeOidcTransaction(value);\n\n const separatorIndex = value.lastIndexOf('.');\n if (separatorIndex < 0) {\n throw new OidcLoginError('Invalid OIDC login transaction signature.');\n }\n\n const payload = value.slice(0, separatorIndex);\n const signature = value.slice(separatorIndex + 1);\n const expectedSignature = await signTransactionPayload(payload, secret);\n if (!timingSafeEqual(signature, expectedSignature)) {\n throw new OidcLoginError('Invalid OIDC login transaction signature.');\n }\n\n return decodeOidcTransaction(payload);\n}\n\nfunction getTransactionTtl(options: OidcSvelteKitOptions): number {\n return (\n options.transactionTtl ?? getUsersOidcConfig().transactionTtl ?? 10 * 60\n );\n}\n\nasync function resolveTenantId(\n result: OidcLoginResult,\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): Promise<string | null | undefined> {\n if (typeof options.tenantId === 'function') {\n return options.tenantId(result, event);\n }\n\n return options.tenantId;\n}\n\nfunction redirectResponse(location: string | URL): Response {\n return new Response(null, {\n headers: { location: location.toString() },\n status: 303,\n });\n}\n\nfunction failureResponse(error: unknown): Response {\n const message = error instanceof Error ? error.message : 'OIDC login failed.';\n return new Response(message, {\n status: 401,\n headers: {\n 'content-type': 'text/plain; charset=utf-8',\n },\n });\n}\n\nfunction resolveSuccessRedirect(\n result: CompleteOidcLoginResult,\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | Promise<string> {\n if (typeof options.successRedirect === 'function') {\n return options.successRedirect(result, event);\n }\n\n return options.successRedirect ?? result.returnTo ?? '/';\n}\n\nfunction resolveFailureRedirect(\n error: unknown,\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | undefined {\n if (!options.failureRedirect) return undefined;\n\n if (typeof options.failureRedirect === 'function') {\n return options.failureRedirect(error, event);\n }\n\n return options.failureRedirect;\n}\n\n/**\n * Start an OIDC login from a SvelteKit route.\n *\n * Sets a short-lived, HTTP-only transaction cookie containing state, nonce,\n * and PKCE verifier, then returns the provider authorization URL.\n */\nexport async function beginOidcLogin(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): Promise<BeginOidcLoginResult> {\n const service = createOidcService(event, options);\n const transaction = service.createTransaction(getReturnTo(event, options));\n const result = await service.createAuthorizationUrl({ transaction });\n\n event.cookies.set(\n getOidcTransactionCookieName(service.providerName, options),\n await encodeOidcTransactionCookie(\n transaction,\n getTransactionCookieSecret(service.provider, options),\n ),\n {\n httpOnly: true,\n maxAge: getTransactionTtl(options),\n path: options.transactionCookiePath ?? '/',\n sameSite: getTransactionCookieSameSite(event, options),\n secure: useSecureCookie(event, options.transactionCookieSecure),\n },\n );\n\n return {\n providerName: service.providerName,\n transaction,\n url: result.url,\n };\n}\n\n/**\n * Complete an OIDC callback, create or update the SMRT user/profile, and set\n * the session cookie.\n */\nexport async function completeOidcLogin(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): Promise<CompleteOidcLoginResult> {\n const service = createOidcService(event, options);\n const cookieName = getOidcTransactionCookieName(\n service.providerName,\n options,\n );\n const cookiePath = options.transactionCookiePath ?? '/';\n\n try {\n const rawTransaction = event.cookies.get(cookieName);\n if (!rawTransaction) {\n throw new OidcLoginError('Missing OIDC login transaction cookie.');\n }\n\n const decodedTransaction = await decodeOidcTransactionCookie(\n rawTransaction,\n getTransactionCookieSecret(service.provider, options),\n );\n const transaction = {\n ...decodedTransaction,\n returnTo: getLocalReturnTo(decodedTransaction.returnTo),\n };\n const ttl = getTransactionTtl(options);\n if (Date.now() - transaction.createdAt > ttl * 1000) {\n throw new OidcLoginError('OIDC login transaction has expired.');\n }\n\n const result = await service.completeLogin(event.url, transaction);\n const tenantId = await resolveTenantId(result, event, options);\n const sessionId = await createSessionCookie(\n event as unknown as HandleInput['event'],\n result.user.id as string,\n tenantId ?? undefined,\n {\n ...options,\n cookieName: options.sessionCookieName,\n cookiePath: options.sessionCookiePath,\n cookieSameSite: options.sessionCookieSameSite,\n cookieSecure: useSecureCookie(event, options.sessionCookieSecure),\n data: {\n oidcIssuer: result.claims.iss,\n oidcProvider: service.providerName,\n },\n ipAddress: event.getClientAddress?.(),\n ttl: options.sessionTtl,\n userAgent: event.request.headers.get('user-agent') ?? undefined,\n },\n );\n\n return {\n ...result,\n providerName: service.providerName,\n returnTo: transaction.returnTo,\n sessionId,\n };\n } finally {\n event.cookies.delete(cookieName, { path: cookiePath });\n }\n}\n\n/**\n * Create a SvelteKit GET handler that redirects to an OIDC provider.\n *\n * @example\n * ```typescript\n * // src/routes/auth/[provider]/login/+server.ts\n * import { createOidcLoginHandler } from '@happyvertical/smrt-users/sveltekit';\n *\n * export const GET = createOidcLoginHandler({\n * db: { type: 'postgres', url: process.env.DATABASE_URL! },\n * });\n * ```\n */\nexport function createOidcLoginHandler(options: OidcSvelteKitOptions) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const { url } = await beginOidcLogin(event, options);\n return redirectResponse(url);\n };\n}\n\n/**\n * Create a SvelteKit GET handler for the provider callback.\n */\nexport function createOidcCallbackHandler(options: OidcSvelteKitOptions) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n try {\n const result = await completeOidcLogin(event, options);\n const redirectTo = await resolveSuccessRedirect(result, event, options);\n return redirectResponse(redirectTo);\n } catch (error) {\n const failureRedirect = resolveFailureRedirect(error, event, options);\n if (failureRedirect) return redirectResponse(failureRedirect);\n return failureResponse(error);\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Terminal / CLI device-code auth\n// ---------------------------------------------------------------------------\n\n/** Cached TerminalAuthService instances keyed by options identity. */\nconst terminalAuthServiceCache = new Map<string, TerminalAuthService>();\n\nfunction terminalAuthCacheKey(options: TerminalAuthServiceOptions): string {\n return JSON.stringify({\n db: options.db,\n cookie: options.sessionCookieName,\n prefix: options.userCodePrefix,\n reqTtl: options.requestTtlSeconds,\n sessTtl: options.sessionTtlSeconds,\n poll: options.pollIntervalSeconds,\n path: options.verificationPath,\n autoExtend: options.sessionAutoExtend,\n maxAttempts: options.maxApproveAttempts,\n attemptWindow: options.approveAttemptWindowSeconds,\n });\n}\n\nasync function getOrCreateTerminalAuthService(\n options: TerminalAuthServiceOptions,\n): Promise<TerminalAuthService> {\n const key = terminalAuthCacheKey(options);\n let service = terminalAuthServiceCache.get(key);\n if (!service) {\n service = await TerminalAuthService.create(options);\n terminalAuthServiceCache.set(key, service);\n }\n return service;\n}\n\n/**\n * Pull `Bearer <token>` out of an `Authorization` header. Returns `null` if\n * the header is missing or malformed.\n */\nexport function parseBearerToken(authorization: string | null): string | null {\n const match = authorization?.match(/^Bearer\\s+(.+)$/iu);\n return match?.[1]?.trim() || null;\n}\n\n/** Options for the terminal-auth start handler. */\nexport interface CreateTerminalAuthStartHandlerOptions\n extends TerminalAuthServiceOptions {\n /**\n * Override the verification origin returned to the CLI (e.g. when the\n * public origin differs from the request origin behind a proxy). Defaults\n * to `event.url.origin`.\n */\n verificationOrigin?: string | ((event: SvelteKitRequestEvent) => string);\n}\n\n/**\n * Create a SvelteKit POST handler that starts a new terminal-auth request.\n * Mount under `/api/cli/auth/start/+server.ts`:\n *\n * ```ts\n * export const POST = createTerminalAuthStartHandler({\n * db: { type: 'postgres', url: process.env.DATABASE_URL! },\n * userCodePrefix: 'WG',\n * });\n * ```\n */\nexport function createTerminalAuthStartHandler(\n options: CreateTerminalAuthStartHandlerOptions,\n) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const service = await getOrCreateTerminalAuthService(options);\n const origin =\n typeof options.verificationOrigin === 'function'\n ? options.verificationOrigin(event)\n : (options.verificationOrigin ?? event.url.origin);\n const result = await service.createRequest(origin);\n // Carries the device/user codes — must never be cached by an intermediary.\n return jsonResponse(result, 201, NO_STORE_HEADERS);\n };\n}\n\n/**\n * Create a SvelteKit POST handler that exchanges a polling device code for a\n * bearer token once the request has been approved. Mount under\n * `/api/cli/auth/token/+server.ts`.\n */\nexport function createTerminalAuthTokenHandler(\n options: TerminalAuthServiceOptions,\n) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const body = (await event.request.json().catch(() => null)) as {\n deviceCode?: unknown;\n } | null;\n const deviceCode =\n typeof body?.deviceCode === 'string' ? body.deviceCode.trim() : '';\n if (!deviceCode) {\n return jsonResponse({ error: 'deviceCode is required.' }, 400);\n }\n\n const service = await getOrCreateTerminalAuthService(options);\n const result = await service.exchangeDeviceCode(deviceCode);\n // Carries the bearer session token — must never be cached by an intermediary.\n return jsonResponse(result, 200, NO_STORE_HEADERS);\n };\n}\n\n/**\n * Create a SvelteKit DELETE handler that revokes the bearer token in the\n * request's `Authorization` header. Always returns `{ authenticated: false }`\n * — does not leak whether the token was actually live, by design.\n */\nexport function createBearerSessionDeleteHandler(\n options: TerminalAuthServiceOptions,\n) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const token = parseBearerToken(event.request.headers.get('authorization'));\n if (token) {\n try {\n const service = await getOrCreateTerminalAuthService(options);\n await service.destroyBearerSession(token);\n } catch (error) {\n logger.error('Terminal bearer session revocation error', { error });\n }\n }\n return jsonResponse({ authenticated: false });\n };\n}\n\n/**\n * Look up the session associated with a bearer token. Use from\n * `hooks.server.ts` to resolve `Authorization: Bearer <sid>` headers\n * alongside cookie-based sessions.\n */\nexport async function loadBearerSessionContext(\n token: string,\n options: TerminalAuthServiceOptions,\n) {\n const service = await getOrCreateTerminalAuthService(options);\n return service.loadBearerSession(token);\n}\n\n/** Shape passed back to `+page.server.ts` `load`. */\nexport interface TerminalLoginPageData {\n userCode: string;\n requestStatus: string | null;\n}\n\n/** Shape returned by the approve action on success. */\nexport interface TerminalLoginApproveSuccess {\n approved: true;\n requestStatus: string;\n userCode: string;\n}\n\n/** Shape returned by the approve action on failure (HTTP 4xx). */\nexport interface TerminalLoginApproveFailure {\n status: number;\n error: string;\n userCode: string;\n}\n\n/**\n * Page-server helper for the terminal-login approval page. Returns\n * `{ load, approve }` you can spread into a `+page.server.ts` module.\n *\n * `approve` is the action implementation, not a wrapped object — wire it up\n * as you like, e.g. `export const actions = { approve: handler.approve }`.\n *\n * @example\n * ```ts\n * // src/routes/terminal-login/+page.server.ts\n * import { mountTerminalLoginPage } from '@happyvertical/smrt-users/sveltekit';\n *\n * const handlers = mountTerminalLoginPage({\n * db: { type: 'postgres', url: process.env.DATABASE_URL! },\n * userCodePrefix: 'WG',\n * requireUser: (event) => Boolean(event.locals.user),\n * resolveUser: (event) => event.locals.user,\n * resolveTenantId: (event) => event.locals.tenantId,\n * });\n *\n * export const load = handlers.load;\n * export const actions = { approve: handlers.approve };\n * ```\n */\nexport interface MountTerminalLoginPageOptions\n extends TerminalAuthServiceOptions {\n /** Resolve the authenticated user from `event.locals`. */\n resolveUser: (\n event: SvelteKitRequestEvent,\n ) => { id?: string | null; email?: string | null } | null | undefined;\n /** Resolve the tenant id from `event.locals`. */\n resolveTenantId: (event: SvelteKitRequestEvent) => string | null | undefined;\n /** Query-string parameter holding the user code on the page URL. */\n codeQueryParam?: string;\n}\n\nexport interface MountedTerminalLoginPage {\n load: (event: SvelteKitRequestEvent) => Promise<TerminalLoginPageData>;\n approve: (\n event: SvelteKitRequestEvent,\n ) => Promise<\n | TerminalLoginApproveSuccess\n | { type: 'failure'; status: number; data: TerminalLoginApproveFailure }\n >;\n}\n\nexport function mountTerminalLoginPage(\n options: MountTerminalLoginPageOptions,\n): MountedTerminalLoginPage {\n const codeParam = options.codeQueryParam ?? 'code';\n\n return {\n load: async (event) => {\n const userCode =\n event.url.searchParams.get(codeParam)?.trim().toUpperCase() ?? '';\n let requestStatus: string | null = null;\n if (userCode) {\n const service = await getOrCreateTerminalAuthService(options);\n const request = await service.getRequestForUserCode(userCode);\n requestStatus = request?.status ?? null;\n }\n return { requestStatus, userCode };\n },\n\n approve: async (event) => {\n const formData = await event.request.formData();\n const userCode =\n formData.get('userCode')?.toString().trim().toUpperCase() ?? '';\n\n if (!userCode) {\n return {\n type: 'failure',\n status: 400,\n data: {\n status: 400,\n error: 'Enter a terminal login code.',\n userCode,\n },\n };\n }\n\n const user = options.resolveUser(event);\n const tenantId = options.resolveTenantId(event);\n if (!user?.id) {\n return {\n type: 'failure',\n status: 401,\n data: {\n status: 401,\n error: 'Sign in before approving terminal access.',\n userCode,\n },\n };\n }\n\n try {\n const service = await getOrCreateTerminalAuthService(options);\n const approveInput: ApproveCliAuthRequestInput = {\n ipAddress: event.getClientAddress?.(),\n tenantId: tenantId ?? null,\n user: {\n email: user.email ?? '',\n id: user.id,\n },\n userAgent: event.request.headers.get('user-agent') ?? undefined,\n userCode,\n };\n const request = await service.approveRequest(approveInput);\n return {\n approved: true,\n requestStatus: request.status,\n userCode,\n };\n } catch (error) {\n if (error instanceof TerminalAuthRateLimitError) {\n return {\n type: 'failure',\n status: 429,\n data: { status: 429, error: error.message, userCode },\n };\n }\n const message =\n error instanceof TerminalAuthError\n ? error.message\n : error instanceof Error\n ? error.message\n : 'Unable to approve terminal login.';\n return {\n type: 'failure',\n status: 400,\n data: { status: 400, error: message, userCode },\n };\n }\n },\n };\n}\n\nfunction jsonResponse(\n body: unknown,\n status = 200,\n extraHeaders?: Record<string, string>,\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json', ...extraHeaders },\n });\n}\n\n/**\n * Cache headers for terminal-auth responses that carry live credentials\n * (device/user codes, bearer session tokens). Mirrors the `private, no-store`\n * guard on the resource-list handler so no intermediary or CDN caches a token.\n */\nconst NO_STORE_HEADERS: Record<string, string> = {\n 'cache-control': 'private, no-store',\n pragma: 'no-cache',\n};\n\nexport {\n TerminalAuthError,\n TerminalAuthRateLimitError,\n TerminalAuthService,\n type TerminalAuthServiceOptions,\n};\n"],"names":["jsonResponse"],"mappings":"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"}
1
+ {"version":3,"file":"sveltekit.js","sources":["../src/sveltekit/resource-list-handler.ts","../src/sveltekit/types.ts","../src/sveltekit/index.ts"],"sourcesContent":["/**\n * Resource-list handler for `GET /api/_resources`.\n *\n * Returns the auth-aware list of resources and commands that an HTTP-based\n * CLI client (e.g. `@happyvertical/smrt-app-cli`) can invoke. Walks the\n * `ObjectRegistry` to find classes whose `@smrt({ cli: ... })` decorator\n * exposes commands, resolves each command's URL shape (scope, http method,\n * path segments) the same way the SvelteKit route generator does, and\n * filters the result through a caller-supplied `commandPolicy`.\n *\n * @packageDocumentation\n *\n * @example\n * ```ts\n * // apps/<app>/src/routes/api/_resources/+server.ts\n * import { createResourceListHandler } from '@happyvertical/smrt-users/sveltekit';\n * import './smrt-register';\n *\n * export const GET = createResourceListHandler({\n * ensureRegistry: async () => { await import('./smrt-register'); },\n * commandPolicy: ({ command, session }) => session.user != null,\n * kebabRoutes: true, // match the vite-plugin setting\n * });\n * ```\n */\n\n// Importing the registration shim here keeps this subpath self-sufficient\n// (mirrors the pattern in `./index.ts`).\nimport '../__smrt-register__.js';\n\nimport {\n ObjectRegistry,\n type SmartObjectConfig,\n} from '@happyvertical/smrt-core';\nimport { classnameToTablename } from '@happyvertical/smrt-core/utils';\nimport {\n methodNameToKebab,\n resolveApiActionSet,\n} from '@happyvertical/smrt-core/vite-plugin';\nimport type { TerminalAuthServiceOptions } from '../services/TerminalAuthService.js';\nimport { loadBearerSessionContext, parseBearerToken } from './index.js';\nimport type { SessionLocals } from './types.js';\n\n/** Shape returned by `ObjectRegistry.getAllClasses()` values. Mirrors the\n * relevant parts of `RegisteredClass` from `@happyvertical/smrt-core/registry/types`.\n * Kept structural so we don't depend on a non-public type export. */\ninterface RegisteredClassLike {\n name: string;\n qualifiedName?: string;\n packageName?: string;\n config: SmartObjectConfig;\n methods: Map<string, unknown>;\n tools?: ToolLike[];\n /**\n * Pluralized endpoint name from the manifest (e.g. `sourcecrawls`). Carried\n * on the registered class since smrt-core 0.26.4 (smrt#1311); used verbatim\n * as the URL segment so the CLI hits the same path the generator wrote.\n */\n collection?: string;\n /** Parent class simple name — `'SmrtCollection'` marks a collection class. */\n extends?: string;\n /** Generic type argument from `SmrtCollection<X>` — also marks collection. */\n extendsTypeArg?: string;\n /**\n * Live class constructor. Used as a runtime fallback to walk the prototype\n * chain when neither `extends` nor `extendsTypeArg` are set — which\n * happens for dev-mode classes registered via the decorator path before\n * the manifest is loaded. (#1311 review D-2.)\n */\n constructor?: { name?: string; prototype?: object } | unknown;\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Types */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nconst STANDARD_API_ACTIONS = [\n 'list',\n 'get',\n 'create',\n 'update',\n 'delete',\n] as const;\n\ntype StandardApiAction = (typeof STANDARD_API_ACTIONS)[number];\ntype ApiHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type CommandKind = 'crud' | 'custom';\nexport type CommandScope = 'item' | 'collection';\n\nexport interface CommandDefinition {\n /** Method name in source casing — source of truth for HTTP routing. */\n methodName: string;\n /** Kebab-case identifier used by the CLI argv parser. */\n commandName: string;\n kind: CommandKind;\n scope: CommandScope;\n httpMethod: ApiHttpMethod;\n /** URL path segments after `/<apiPath>[/<id>]/`. May be empty. */\n pathSegments: string[];\n description?: string;\n /** JSONSchema describing the command's argv-flag surface. */\n parameters?: Record<string, unknown>;\n}\n\nexport interface CliResource {\n /** Kebab-case identifier; the first positional argument after the CLI name. */\n slug: string;\n className: string;\n qualifiedName?: string;\n packageName?: string;\n label: string;\n /** Collection segment, no leading slash, no `/api` prefix. */\n apiPath: string;\n commands: CommandDefinition[];\n}\n\nexport interface ResolvedSession {\n user: SessionLocals['user'];\n membership?: SessionLocals['membership'];\n permissions: string[];\n tenantId: string | null;\n sessionId: string | null;\n}\n\nexport interface CommandPolicyContext {\n resource: Omit<CliResource, 'commands'>;\n command: CommandDefinition;\n session: ResolvedSession;\n /**\n * Stable identifiers for policy authors that need more than the\n * caller-facing `resource` view (e.g. package-scoped role checks).\n */\n classMeta: {\n name: string;\n qualifiedName?: string;\n packageName?: string;\n decoratorConfig: SmartObjectConfig;\n };\n}\n\nexport interface ResourceListResponseBody {\n user: { authenticated: boolean; id?: string };\n warnings: string[];\n resources: CliResource[];\n}\n\n/**\n * Per-command lint warning emitted by the handler when a method couldn't be\n * surfaced as a command (dynamic path params, DELETE-with-body, multi-arg,\n * etc.). Filtered through `commandPolicy` so callers only see warnings for\n * commands they would have been allowed to invoke.\n */\ninterface SkipWarning {\n /** The class/method this warning pertains to (`Praeco.discover`). */\n ref: string;\n /** Human-readable reason: `dynamic-path-params unsupported` etc. */\n reason: string;\n /** Candidate command — used by policy to decide visibility. */\n candidate: CommandDefinition;\n /** The owning class for visibility scoping. */\n resourceMeta: Omit<CliResource, 'commands'>;\n classMeta: CommandPolicyContext['classMeta'];\n}\n\nexport interface CreateResourceListHandlerOptions\n extends TerminalAuthServiceOptions {\n /**\n * Ensures `ObjectRegistry` is populated before the handler walks it.\n *\n * v0.1 escape hatch: the consumer app must trigger its `@smrt()` side\n * effects (typically by importing the generated `smrt-register.ts`).\n * Without this, a fresh request handler process may see an empty\n * registry and return zero resources.\n */\n ensureRegistry: () => void | Promise<void>;\n\n /**\n * Resolve the caller's session. Defaults to `event.locals` (set by\n * `createSessionHandler` in `hooks.server.ts`) with a `Bearer <token>`\n * fallback for terminal-auth CLI clients.\n *\n * If a bearer token is present but doesn't resolve to a live session,\n * the handler responds 401 — NOT silent anonymous, so a stale CLI token\n * gets a clear signal to re-authenticate.\n */\n resolveSession?: (event: SveltekitEvent) => Promise<ResolvedSession>;\n\n /**\n * Per-command permission filter. Default: deny everything when the\n * caller is anonymous; allow everything when authenticated.\n *\n * Note: this is _capability filtering_, not row-level authorization.\n * Per-route handlers remain authoritative for `can user X update record Y`.\n */\n commandPolicy?: (ctx: CommandPolicyContext) => boolean | Promise<boolean>;\n\n /**\n * Override the slug derivation. Default: kebab-case of `collection`\n * (which is already plural+lowercase from the manifest generator).\n */\n resourceSlug?: (meta: {\n className: string;\n collection: string;\n qualifiedName?: string;\n packageName?: string;\n }) => string;\n\n /**\n * Match the vite plugin's `svelteKit.kebabRoutes` setting. When `true`,\n * custom method URL segments are kebab-cased on the wire (the CLI sends\n * `/discover-from-url`); when `false`, source-cased (`/discoverFromUrl`).\n * Defaults to `false` to match the vite plugin default.\n */\n kebabRoutes?: boolean;\n}\n\ntype SveltekitEvent = {\n cookies: { get: (name: string) => string | undefined };\n locals?: Record<string, unknown>;\n request: Request;\n url: URL;\n};\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Public factory */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nexport function createResourceListHandler(\n options: CreateResourceListHandlerOptions,\n): (event: SveltekitEvent) => Promise<Response> {\n const {\n ensureRegistry,\n resolveSession = (event) => defaultResolveSession(event, options),\n commandPolicy = defaultCommandPolicy,\n resourceSlug = defaultResourceSlug,\n kebabRoutes = false,\n } = options;\n\n return async (event: SveltekitEvent): Promise<Response> => {\n await ensureRegistry();\n\n let session: ResolvedSession;\n try {\n session = await resolveSession(event);\n } catch (error) {\n if (error instanceof InvalidBearerError) {\n return jsonResponse({ error: error.message }, 401);\n }\n throw error;\n }\n\n const resources: CliResource[] = [];\n const skipWarnings: SkipWarning[] = [];\n const slugSeen = new Map<string, string>();\n\n for (const [registeredName, registered] of ObjectRegistry.getAllClasses()) {\n const def = synthesizeDefinition(\n registeredName,\n registered as unknown as RegisteredClassLike,\n );\n const cliConfig = def.decoratorConfig.cli;\n if (!cliConfig) continue;\n if (!isHttpCliConfig(cliConfig)) continue;\n\n // Skip SmrtCollection subclasses. The generator routes these through\n // a different code path that only emits collection-scoped custom\n // routes (no CRUD), so surfacing CRUD commands here would 404 and\n // item-scoped custom commands would call URLs the generator never\n // wrote. See #1311 review (codex P2).\n if (isCollectionClass(def)) continue;\n\n // Resolve included method set per v4 rules.\n // resolveApiActionSet only reads decoratorConfig + methods; cast is safe.\n const apiActionSet = resolveApiActionSet(\n def as unknown as Parameters<typeof resolveApiActionSet>[0],\n );\n const includedMethods = resolveCliIncludedMethods(\n cliConfig,\n apiActionSet,\n );\n if (includedMethods.length === 0) continue;\n\n // Compute the resource shell first; commands are derived from it.\n const slug = resourceSlug({\n className: def.className,\n collection: def.collection,\n qualifiedName: def.qualifiedName,\n packageName: def.packageName,\n });\n const previous = slugSeen.get(slug);\n if (previous && previous !== def.qualifiedName) {\n return jsonResponse(\n {\n error: 'resource-slug-collision',\n slug,\n classes: [previous, def.qualifiedName ?? def.className].filter(\n Boolean,\n ),\n },\n 500,\n );\n }\n slugSeen.set(slug, def.qualifiedName ?? def.className);\n\n const resourceShell: Omit<CliResource, 'commands'> = {\n slug,\n className: def.className,\n qualifiedName: def.qualifiedName,\n packageName: def.packageName,\n label: humanLabel(def.className),\n apiPath: resolveApiPath(def),\n };\n\n const classMeta: CommandPolicyContext['classMeta'] = {\n name: def.className,\n qualifiedName: def.qualifiedName,\n packageName: def.packageName,\n decoratorConfig: def.decoratorConfig,\n };\n\n // Build candidate commands + collect skip warnings.\n // `commandNameSeen` is empty at start; CRUD commands populate it as\n // they're built. Custom commands that kebab to a CRUD name are caught\n // by the explicit CRUD-shadowing check below before they get here.\n const commandNameSeen = new Set<string>();\n const candidates: CommandDefinition[] = [];\n\n for (const methodName of includedMethods) {\n const result = buildCommand(def, methodName, kebabRoutes);\n if (!result.ok) {\n skipWarnings.push({\n ref: `${def.className}.${methodName}`,\n reason: result.reason,\n candidate: result.candidate,\n resourceMeta: resourceShell,\n classMeta,\n });\n continue;\n }\n\n // Detect kebab collisions and CRUD shadowing inside this class.\n if (\n result.command.kind === 'custom' &&\n STANDARD_API_ACTIONS.includes(\n result.command.commandName as StandardApiAction,\n )\n ) {\n return jsonResponse(\n {\n error: 'command-name-shadows-crud',\n className: def.className,\n methodName,\n commandName: result.command.commandName,\n },\n 500,\n );\n }\n if (commandNameSeen.has(result.command.commandName)) {\n return jsonResponse(\n {\n error: 'command-name-collision',\n className: def.className,\n commandName: result.command.commandName,\n },\n 500,\n );\n }\n commandNameSeen.add(result.command.commandName);\n candidates.push(result.command);\n }\n\n // Run the command policy. Batch with Promise.all so an async\n // `commandPolicy` (e.g. one that hits a DB) fans out across all\n // candidates instead of serializing N round-trips per class. For\n // sync policies the difference is zero. (#1311 review P1.)\n const policyResults = await Promise.all(\n candidates.map((candidate) =>\n commandPolicy({\n resource: resourceShell,\n command: candidate,\n session,\n classMeta,\n }),\n ),\n );\n const allowed = candidates.filter((_, i) => policyResults[i]);\n if (allowed.length === 0) continue;\n\n resources.push({ ...resourceShell, commands: allowed });\n }\n\n // Filter warnings through policy: only surface warnings the caller could\n // have seen the underlying command for. Same Promise.all batching.\n const warnResults = await Promise.all(\n skipWarnings.map((w) =>\n commandPolicy({\n resource: w.resourceMeta,\n command: w.candidate,\n session,\n classMeta: w.classMeta,\n }),\n ),\n );\n const visibleWarnings = skipWarnings\n .filter((_, i) => warnResults[i])\n .map((w) => `${w.ref}: ${w.reason}`);\n\n const body: ResourceListResponseBody = {\n user: session.user\n ? { authenticated: true, id: String(session.user.id ?? '') }\n : { authenticated: false },\n warnings: visibleWarnings,\n resources,\n };\n\n return jsonResponse(body, 200, {\n 'cache-control': 'private, no-store',\n });\n };\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — synthesis */\n/* ────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Shape we feed into `resolveApiActionSet`. Mirrors the relevant parts of\n * `SmartObjectDefinition` from `@happyvertical/smrt-core/scanner` so we\n * can compute the API set against runtime-registered classes (which the\n * registry stores as `RegisteredClass`, not as `SmartObjectDefinition`).\n */\ninterface SynthesizedDefinition {\n className: string;\n qualifiedName?: string;\n packageName?: string;\n collection: string;\n decoratorConfig: SmartObjectConfig;\n methods: Record<string, MethodLike>;\n tools?: ToolLike[];\n extends?: string;\n extendsTypeArg?: string;\n /** Live constructor for prototype-chain fallback in isCollectionClass. */\n constructor?: unknown;\n}\n\ninterface MethodLike {\n name: string;\n isStatic?: boolean;\n isPublic?: boolean;\n parameters?: Array<{ name: string; type: string; optional?: boolean }>;\n returnType?: string;\n description?: string;\n}\n\ninterface ToolLike {\n type: 'function';\n function: {\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n };\n}\n\nfunction synthesizeDefinition(\n _registeredName: string,\n registered: RegisteredClassLike,\n): SynthesizedDefinition {\n const methods: Record<string, MethodLike> = {};\n for (const [name, value] of registered.methods.entries()) {\n methods[name] = value as MethodLike;\n }\n\n const decoratorConfig = (registered.config ?? {}) as SmartObjectConfig;\n // Prefer the manifest-derived `collection` the registry now carries\n // (smrt#1311). This is the SAME pluralized endpoint segment the SvelteKit\n // route generator writes to disk — e.g. `sourcecrawls` for `SourceCrawl`,\n // NOT the snake_case `tableName` (`source_crawls`). The tableName-based\n // derivation below is only a fallback for older smrt-core builds that\n // didn't store `collection` on the registered class.\n const collection =\n registered.collection ??\n deriveCollectionFromConfig(decoratorConfig) ??\n classnameToTablename(registered.name);\n\n return {\n className: registered.name,\n qualifiedName: registered.qualifiedName,\n packageName: registered.packageName,\n collection,\n decoratorConfig,\n methods,\n tools: registered.tools as ToolLike[] | undefined,\n extends: registered.extends,\n extendsTypeArg: registered.extendsTypeArg,\n constructor: registered.constructor,\n };\n}\n\n/**\n * Detect `SmrtCollection<T>` subclasses. The SvelteKit generator routes\n * these through a separate code path that ONLY emits collection-scoped\n * custom routes (no CRUD), so the CLI must do the same — treating them\n * as regular object classes would expose CRUD commands the generator\n * never wrote.\n *\n * Three signals, in order of confidence:\n *\n * 1. `extends === 'SmrtCollection'` — manifest-derived, always reliable\n * when classes come from a generated manifest.\n * 2. `extendsTypeArg` — also manifest-derived; covers the\n * `SmrtCollection<X>` generic form.\n * 3. Prototype-chain walk on `constructor` — runtime fallback for\n * classes registered via the decorator path in dev mode (or in any\n * no-manifest setup) where signals (1) and (2) may be absent.\n * Walks up `Object.getPrototypeOf(ctor)` looking for `name ===\n * 'SmrtCollection'`. Bounded to a few iterations by a depth guard.\n *\n * Mirrors the same predicate in `packages/core/src/vite-plugin/\n * sveltekit-generator.ts:isCollectionClass` but extends it with the\n * prototype-walk fallback. (#1311 review D-2.)\n */\nfunction isCollectionClass(def: SynthesizedDefinition): boolean {\n if (def.extends === 'SmrtCollection') return true;\n if (def.extendsTypeArg) return true;\n return ctorChainContains(def.constructor, 'SmrtCollection');\n}\n\nfunction ctorChainContains(\n ctor: unknown,\n name: string,\n depthCap = 16,\n): boolean {\n let current = ctor as { name?: string } | null;\n for (let i = 0; i < depthCap && current; i += 1) {\n if (current.name === name) return true;\n const next = Object.getPrototypeOf(current);\n // Stop when we've reached the root prototype (`Function.prototype`),\n // which has no `.name` field of its own.\n if (!next || next === current) break;\n current = next as { name?: string } | null;\n }\n return false;\n}\n\nfunction deriveCollectionFromConfig(\n config: SmartObjectConfig,\n): string | undefined {\n // `tableName` doubles as the collection-path source in the generator.\n return config?.tableName ?? undefined;\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — command resolution */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nfunction resolveCliIncludedMethods(\n cliConfig: NonNullable<SmartObjectConfig['cli']>,\n apiActionSet: Set<string>,\n): string[] {\n if (cliConfig === true) {\n // Boolean shorthand: CRUD only (intersected with what the API exposes).\n return STANDARD_API_ACTIONS.filter((a) => apiActionSet.has(a));\n }\n if (cliConfig === false) return [];\n\n // Decorator config comes from registered classes — we cast through\n // `unknown` at the registry boundary, so anything could be in here at\n // runtime: malformed manifests, JS-authored decorators with `cli:\n // 'true'`, typos like `cli: { include: 'methodName' }` (string instead\n // of array), null, arrays at the top level, etc.\n //\n // Two failure modes to guard against:\n // 1. Crash (e.g. `'discoverFromUrl'.filter(...)` → TypeError 500).\n // Always bad — the endpoint stops working for the whole app.\n // 2. Silently wrong behavior (e.g. malformed `include` falls through\n // to a CRUD-only default, so the developer who typed `include:\n // 'foo'` instead of `include: ['foo']` gets CRUD commands they\n // didn't ask for).\n //\n // Both are worse than returning [] for that class. So: if ANY\n // recognised field is the wrong type, treat the class as having no\n // CLI commands — the developer's misconfiguration is loud (no\n // commands surface), not silent. (#1311 review A1+A2.)\n if (typeof cliConfig !== 'object' || cliConfig === null) return [];\n if (Array.isArray(cliConfig)) return [];\n\n const obj = cliConfig as {\n include?: unknown;\n exclude?: unknown;\n mirror?: unknown;\n };\n if (obj.include !== undefined && !Array.isArray(obj.include)) return [];\n if (obj.exclude !== undefined && !Array.isArray(obj.exclude)) return [];\n if (obj.mirror !== undefined && obj.mirror !== 'api') return [];\n\n const include = obj.include as string[] | undefined;\n const exclude = obj.exclude as string[] | undefined;\n let candidates: string[];\n\n if (obj.mirror === 'api') {\n candidates = [...apiActionSet];\n } else if (include?.includes('*')) {\n candidates = [...apiActionSet];\n } else if (include && include.length > 0) {\n candidates = include.filter((m) => apiActionSet.has(m));\n } else {\n // No include + no mirror → CRUD only.\n candidates = STANDARD_API_ACTIONS.filter((a) => apiActionSet.has(a));\n }\n\n if (exclude && exclude.length > 0) {\n const excludeSet = new Set(exclude);\n candidates = candidates.filter((m) => !excludeSet.has(m));\n }\n return candidates;\n}\n\nfunction isHttpCliConfig(\n cliConfig: NonNullable<SmartObjectConfig['cli']>,\n): boolean {\n if (typeof cliConfig !== 'object' || cliConfig === null) return true;\n if (Array.isArray(cliConfig)) return true;\n return (cliConfig as { http?: unknown }).http !== false;\n}\n\ninterface BuildSuccess {\n ok: true;\n command: CommandDefinition;\n}\ninterface BuildSkip {\n ok: false;\n reason: string;\n candidate: CommandDefinition;\n}\n\nfunction buildCommand(\n def: SynthesizedDefinition,\n methodName: string,\n kebabRoutes: boolean,\n): BuildSuccess | BuildSkip {\n const isCrud = STANDARD_API_ACTIONS.includes(methodName as StandardApiAction);\n\n if (isCrud) {\n return {\n ok: true,\n command: buildCrudCommand(methodName as StandardApiAction),\n };\n }\n\n const methodDef = def.methods[methodName];\n if (!methodDef) {\n // Manifest doesn't know this method — fall through anyway so the user\n // sees something coherent rather than silently dropping it.\n return {\n ok: true,\n command: buildCustomCommand(def, methodName, undefined, kebabRoutes),\n };\n }\n\n // Multi-arg: skip with warning. v0.1 only supports single-options-object.\n const paramCount = methodDef.parameters?.length ?? 0;\n if (paramCount > 1) {\n return {\n ok: false,\n reason: 'multi-arg unsupported (use single-options-object convention)',\n candidate: buildCustomCommand(def, methodName, methodDef, kebabRoutes),\n };\n }\n\n const candidate = buildCustomCommand(def, methodName, methodDef, kebabRoutes);\n\n // Dynamic path-param segments (`[id]`, `:foo`) → unsupported in v0.1.\n if (hasDynamicSegments(candidate.pathSegments)) {\n return {\n ok: false,\n reason: 'dynamic-path-params unsupported',\n candidate,\n };\n }\n\n // DELETE-with-body — skip. DELETE is OK for `<col>/<id>` (id from positional),\n // but a custom DELETE method that accepts a body is too fragile through\n // proxies/frameworks. Detect by either a non-empty schema OR a methodDef\n // that takes any parameters at all.\n if (candidate.httpMethod === 'DELETE' && candidate.kind === 'custom') {\n const methodHasParams = (methodDef.parameters?.length ?? 0) > 0;\n if (methodHasParams || hasNonIdParameters(candidate.parameters)) {\n return {\n ok: false,\n reason: 'DELETE-with-body unsupported',\n candidate,\n };\n }\n }\n\n return { ok: true, command: candidate };\n}\n\nfunction buildCrudCommand(name: StandardApiAction): CommandDefinition {\n const item = name === 'get' || name === 'update' || name === 'delete';\n const httpMethod: ApiHttpMethod =\n name === 'list' || name === 'get'\n ? 'GET'\n : name === 'create'\n ? 'POST'\n : name === 'update'\n ? 'PUT'\n : 'DELETE';\n\n return {\n methodName: name,\n commandName: name,\n kind: 'crud',\n scope: item ? 'item' : 'collection',\n httpMethod,\n pathSegments: [],\n description: defaultCrudDescription(name),\n };\n}\n\nfunction buildCustomCommand(\n def: SynthesizedDefinition,\n methodName: string,\n methodDef: MethodLike | undefined,\n kebabRoutes: boolean,\n): CommandDefinition {\n const apiConfigObj = getApiConfigObject(def.decoratorConfig.api);\n const routeConfig = apiConfigObj?.routes?.[methodName] as\n | { scope?: CommandScope; method?: string; path?: string }\n | undefined;\n\n const defaultScope: CommandScope = methodDef?.isStatic\n ? 'collection'\n : 'item';\n const scope = routeConfig?.scope ?? defaultScope;\n const httpMethod = normalizeApiHttpMethod(routeConfig?.method);\n\n let pathSegments: string[];\n if (routeConfig?.path) {\n // Explicit override — used verbatim, no kebab transform.\n pathSegments = routeConfig.path\n .split('/')\n .map((s) => s.trim())\n .filter(Boolean);\n if (pathSegments.length === 0) pathSegments = [methodName];\n } else {\n pathSegments = [kebabRoutes ? methodNameToKebab(methodName) : methodName];\n }\n\n const parameters = resolveParametersSchema(def, methodName, methodDef);\n\n return {\n methodName,\n commandName: methodNameToKebab(methodName),\n kind: 'custom',\n scope,\n httpMethod,\n pathSegments,\n description: methodDef?.description,\n parameters,\n };\n}\n\nfunction resolveParametersSchema(\n def: SynthesizedDefinition,\n methodName: string,\n _methodDef: MethodLike | undefined,\n): Record<string, unknown> | undefined {\n // 1. Prefer the tool JSONSchema (build-time generated by `smrt scan`).\n const tool = def.tools?.find((t) => t.function.name === methodName);\n if (tool?.function.parameters) {\n const params = tool.function.parameters;\n // Reject empty schemas (`{}` or `{ type: 'object', properties: {} }`) —\n // these carry no real flag information so the parser would otherwise\n // treat them as \"supported\" and accept arbitrary string-typed flags\n // without coercion. Returning `undefined` routes the CLI to the\n // positional-JSON fallback with a clear stderr hint instead. See\n // #1311 review (codex P2 schema).\n if (hasUsableProperties(params)) return params;\n }\n\n // 2. No usable schema — surface `undefined` so the CLI parser switches\n // to the positional-JSON escape hatch. Inferring `{ type: 'object',\n // additionalProperties: true }` from a single `options: { … }` source-\n // type signal would silently accept untyped flags and send strings to\n // numeric fields — worse than just asking for a JSON payload.\n return undefined;\n}\n\nfunction hasUsableProperties(\n schema: Record<string, unknown> | undefined,\n): boolean {\n if (!schema || typeof schema !== 'object') return false;\n const props = (schema as { properties?: Record<string, unknown> }).properties;\n if (!props || typeof props !== 'object') return false;\n return Object.keys(props).length > 0;\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\nfunction getApiConfigObject(\n api: SmartObjectConfig['api'],\n): { routes?: Record<string, unknown> } | undefined {\n if (typeof api !== 'object' || api === null) return undefined;\n return api as { routes?: Record<string, unknown> };\n}\n\nfunction hasDynamicSegments(segments: string[]): boolean {\n return segments.some((s) => /^\\[.+\\]$/.test(s) || s.startsWith(':'));\n}\n\nfunction hasNonIdParameters(parameters?: Record<string, unknown>): boolean {\n if (!parameters || typeof parameters !== 'object') return false;\n const props = (parameters as { properties?: Record<string, unknown> })\n .properties;\n if (!props) return false;\n const keys = Object.keys(props).filter((k) => k !== 'id');\n return keys.length > 0;\n}\n\nfunction defaultCrudDescription(name: StandardApiAction): string {\n switch (name) {\n case 'list':\n return 'List records';\n case 'get':\n return 'Get a record by id';\n case 'create':\n return 'Create a new record';\n case 'update':\n return 'Update an existing record';\n case 'delete':\n return 'Delete a record';\n }\n}\n\nfunction resolveApiPath(def: SynthesizedDefinition): string {\n // IMPORTANT: must match what the sveltekit generator writes to disk.\n // The generator uses `objectDef.collection` verbatim and does NOT read\n // `api.path` for the directory name. If you want a custom URL segment,\n // override `tableName` on the decorator. Returning a kebab-cased\n // collection here would produce 404s for classes like `WeatherForecast`\n // whose default collection is `weather_forecasts`. (Issue #1311 review.)\n return def.collection;\n}\n\nfunction defaultResourceSlug(meta: {\n className: string;\n collection: string;\n}): string {\n // Slug is the user-facing CLI identifier. Keeping it in sync with the\n // URL segment (which is `collection` verbatim — see resolveApiPath)\n // makes `<cli> <slug>` reliably point to where the CLI will POST.\n return meta.collection;\n}\n\nfunction humanLabel(className: string): string {\n // Praeco → Praeco; XMLExport → Xml Export. Cheap default.\n return className\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2');\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — session resolution */\n/* ────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Thrown by the default `resolveSession` when a bearer token is present in\n * the request but doesn't resolve to a live session. The handler catches\n * this and responds 401. Exported so custom `resolveSession` implementations\n * can opt in to the same semantics.\n */\nexport class InvalidBearerError extends Error {\n constructor(message = 'Invalid or expired bearer token.') {\n super(message);\n this.name = 'InvalidBearerError';\n }\n}\n\nasync function defaultResolveSession(\n event: SveltekitEvent,\n options: TerminalAuthServiceOptions,\n): Promise<ResolvedSession> {\n // 1. Locals win when set (already validated by hooks.server.ts).\n const locals = (event.locals ?? {}) as Partial<SessionLocals>;\n if (locals.user) {\n return {\n user: locals.user,\n membership: locals.membership ?? null,\n permissions: locals.permissions ?? [],\n tenantId: locals.tenantId ?? null,\n sessionId: locals.sessionId ?? null,\n };\n }\n\n // 2. Bearer-token fallback for terminal-auth CLI clients.\n const bearer = parseBearerToken(event.request.headers.get('authorization'));\n if (bearer) {\n const ctx = await loadBearerSessionContext(bearer, options);\n if (!ctx) {\n // Bearer present but doesn't resolve — fail loud, not silent anonymous.\n throw new InvalidBearerError();\n }\n return {\n user: ctx.user,\n membership: ctx.membership ?? null,\n permissions: ctx.permissions,\n tenantId: ctx.tenantId,\n sessionId: ctx.sessionId,\n };\n }\n\n // 3. Truly anonymous.\n return {\n user: null,\n membership: null,\n permissions: [],\n tenantId: null,\n sessionId: null,\n };\n}\n\nfunction defaultCommandPolicy(ctx: CommandPolicyContext): boolean {\n return ctx.session.user != null;\n}\n\n/* ────────────────────────────────────────────────────────────────────────── */\n/* Helpers — HTTP */\n/* ────────────────────────────────────────────────────────────────────────── */\n\nfunction jsonResponse(\n body: unknown,\n status = 200,\n extraHeaders: Record<string, string> = {},\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n 'content-type': 'application/json',\n ...extraHeaders,\n },\n });\n}\n","/**\n * SvelteKit-specific type definitions for session management\n * @packageDocumentation\n */\n\nimport type { Membership } from '../models/Membership.js';\nimport type { User } from '../models/User.js';\n\n/**\n * Extended locals interface for SvelteKit\n *\n * Add to your app.d.ts:\n * ```typescript\n * declare global {\n * namespace App {\n * interface Locals extends SessionLocals {}\n * }\n * }\n * ```\n */\nexport interface SessionLocals {\n /** The authenticated user (null if not authenticated) */\n user: User | null;\n /** Active membership for the current tenant (null if none) */\n membership?: Membership | null;\n /** User's resolved permissions */\n permissions: string[];\n /** Current tenant context (null if no tenant selected) */\n tenantId: string | null;\n /** Session ID (null if no session) */\n sessionId: string | null;\n}\n\n/**\n * Default session locals values\n */\nexport const defaultSessionLocals: SessionLocals = {\n user: null,\n membership: null,\n permissions: [],\n tenantId: null,\n sessionId: null,\n};\n","/**\n * SvelteKit integration for session management\n * @packageDocumentation\n *\n * @example\n * ```typescript\n * // hooks.server.ts\n * import { createSessionHandler } from '@happyvertical/smrt-users/sveltekit';\n * import { sequence } from '@sveltejs/kit/hooks';\n *\n * const sessionHandler = createSessionHandler({\n * db: { type: 'postgres', url: process.env.DATABASE_URL }\n * });\n *\n * export const handle = sequence(sessionHandler);\n * ```\n */\n\n// Consumers that import from this subpath (e.g.\n// `@happyvertical/smrt-users/sveltekit`) typically do NOT also import the\n// package root, so the root's `__smrt-register__` side effect never runs\n// — and `SessionService` / `Session` would then evaluate their @smrt()\n// decorators against an empty manifest, falling back to zero-field\n// metadata (the original bug from issue #1132). Importing the registration\n// shim here makes this subpath self-sufficient.\nimport '../__smrt-register__.js';\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { DEFAULT_SESSION_TTL } from '../models/Session.js';\nimport {\n decodeOidcTransaction,\n encodeOidcTransaction,\n getUsersOidcConfig,\n OidcLoginError,\n type OidcLoginResult,\n OidcLoginService,\n type OidcProviderConfig,\n type OidcProviderResolutionOptions,\n type OidcTransaction,\n type ResolvedOidcProviderConfig,\n resolveOidcProviderConfig,\n} from '../services/OidcLoginService.js';\nimport { withSessionPermissionContext } from '../services/SessionPermissionContext.js';\nimport { SessionService } from '../services/SessionService.js';\nimport {\n type ApproveCliAuthRequestInput,\n TerminalAuthError,\n TerminalAuthRateLimitError,\n TerminalAuthService,\n type TerminalAuthServiceOptions,\n} from '../services/TerminalAuthService.js';\n\nexport {\n type CliResource,\n type CommandDefinition,\n type CommandKind,\n type CommandPolicyContext,\n type CommandScope,\n type CreateResourceListHandlerOptions,\n createResourceListHandler,\n InvalidBearerError,\n type ResolvedSession,\n type ResourceListResponseBody,\n} from './resource-list-handler.js';\nexport { defaultSessionLocals, type SessionLocals } from './types.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Options for session handler\n */\nexport interface SessionHandlerOptions extends SmrtClassOptions {\n /** Cookie name (default: 'sid') */\n cookieName?: string;\n /** Session TTL in seconds (default: 7 days) */\n ttl?: number;\n /** Paths to skip session loading (e.g., '/api/health') */\n skipPaths?: string[];\n /** Whether to auto-extend sessions on each request (default: false) */\n autoExtend?: boolean;\n /**\n * Cookie domain (default: undefined, uses request domain). The session\n * handler only reads the cookie; this is consumed by `createSessionCookie`\n * / `destroySessionCookie` when the same options object is shared with them.\n */\n cookieDomain?: string;\n /** Cookie path (default: '/') */\n cookiePath?: string;\n /** Whether cookies are secure (default: true in production) */\n cookieSecure?: boolean;\n /** SameSite cookie attribute (default: 'lax') */\n cookieSameSite?: 'strict' | 'lax' | 'none';\n /** Whether to enter smrt-tenancy request context when tenant data exists */\n enterTenantContext?: boolean;\n /** Whether to enforce Postgres RLS via request-scoped transactions */\n postgresRls?: boolean;\n}\n\n/**\n * SvelteKit Handle type (minimal definition to avoid requiring @sveltejs/kit as dependency)\n */\ntype HandleInput = {\n event: {\n cookies: {\n get: (name: string) => string | undefined;\n set: (\n name: string,\n value: string,\n options?: Record<string, unknown>,\n ) => void;\n delete: (name: string, options?: Record<string, unknown>) => void;\n };\n locals: Record<string, unknown>;\n url: { pathname: string; protocol?: string };\n request: { headers: Headers };\n };\n resolve: (event: unknown) => Promise<Response>;\n};\n\ntype Handle = (input: HandleInput) => Promise<Response>;\n\ntype SvelteKitRequestEvent = {\n cookies: HandleInput['event']['cookies'];\n getClientAddress?: () => string;\n locals?: Record<string, unknown>;\n params?: Record<string, string | undefined>;\n request: Request;\n url: URL;\n};\n\ntype OidcProviderResolver =\n | string\n | ((event: SvelteKitRequestEvent) => string | undefined);\n\ntype OidcStringResolver<T> =\n | T\n | ((result: OidcLoginResult, event: SvelteKitRequestEvent) => T | Promise<T>);\n\nexport interface OidcSvelteKitOptions\n extends SmrtClassOptions,\n OidcProviderResolutionOptions {\n /** Optional fetch override for tests or custom runtimes. */\n fetch?: typeof fetch;\n /** JWT clock tolerance passed to jose. */\n clockTolerance?: number | string;\n /** Provider name, or a resolver. Defaults to event.params.provider. */\n provider?: OidcProviderResolver;\n /** Callback path used when provider.redirectUri is omitted. */\n callbackPath?: string | ((providerName: string) => string);\n /** Query parameter used to preserve post-login redirects. */\n returnToParam?: string;\n /** Prefix for the temporary OIDC transaction cookie. */\n transactionCookiePrefix?: string;\n /** Temporary transaction cookie TTL in seconds. Default: 10 minutes. */\n transactionTtl?: number;\n /** Cookie path for the temporary OIDC transaction. */\n transactionCookiePath?: string;\n /** Secure flag for the temporary OIDC transaction cookie. */\n transactionCookieSecure?: boolean;\n /** SameSite value for the temporary OIDC transaction cookie. */\n transactionCookieSameSite?: 'strict' | 'lax' | 'none';\n /** HMAC secret for transaction cookie integrity. Defaults to clientSecret. */\n transactionCookieSecret?: string;\n /** Session cookie name. Defaults to sid. */\n sessionCookieName?: string;\n /** Session cookie path. Defaults to /. */\n sessionCookiePath?: string;\n /** Secure flag for the session cookie. Defaults to true on HTTPS. */\n sessionCookieSecure?: boolean;\n /** SameSite value for the session cookie. */\n sessionCookieSameSite?: 'strict' | 'lax' | 'none';\n /** Session TTL in seconds. Defaults to the package session default. */\n sessionTtl?: number;\n /** Optional tenant to bind to the session. */\n tenantId?: OidcStringResolver<string | null | undefined>;\n /** Redirect target after successful callback. */\n successRedirect?: OidcStringResolver<string>;\n /** Redirect target after failed callback. If omitted, failures return 401. */\n failureRedirect?:\n | string\n | ((error: unknown, event: SvelteKitRequestEvent) => string);\n}\n\nexport interface BeginOidcLoginResult {\n providerName: string;\n transaction: OidcTransaction;\n url: URL;\n}\n\nexport interface CompleteOidcLoginResult extends OidcLoginResult {\n providerName: string;\n returnTo?: string;\n sessionId: string;\n}\n\n/**\n * Creates a SvelteKit handle hook for session management.\n *\n * This hook:\n * 1. Reads the session cookie\n * 2. Loads session context (user + permissions) if valid\n * 3. Populates event.locals with user, permissions, tenantId, sessionId\n * 4. Optionally extends session on each request\n *\n * @example\n * ```typescript\n * // hooks.server.ts\n * import { createSessionHandler } from '@happyvertical/smrt-users/sveltekit';\n *\n * const sessionHandler = createSessionHandler({\n * db: { type: 'sqlite', url: 'app.db' },\n * cookieName: 'sid',\n * ttl: 7 * 24 * 60 * 60, // 7 days\n * skipPaths: ['/api/health', '/api/public'],\n * });\n *\n * export const handle = sessionHandler;\n * // Or with sequence:\n * // export const handle = sequence(sessionHandler, otherHandler);\n * ```\n */\nexport function createSessionHandler(options: SessionHandlerOptions): Handle {\n const cookieName = options.cookieName ?? 'sid';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n const skipPaths = options.skipPaths ?? [];\n\n // Lazy-initialized session service\n let sessionService: SessionService | null = null;\n\n const getSessionService = async (): Promise<SessionService> => {\n if (!sessionService) {\n sessionService = await SessionService.create({\n ...options,\n defaultTTL: ttl,\n autoExtend: options.autoExtend ?? false,\n });\n }\n return sessionService;\n };\n\n return async ({ event, resolve }) => {\n // Initialize locals with defaults (use property assignment for type safety)\n event.locals.user = null;\n event.locals.membership = null;\n event.locals.permissions = [];\n event.locals.tenantId = null;\n event.locals.sessionId = null;\n\n // Skip session loading for certain paths\n if (skipPaths.some((path) => event.url.pathname.startsWith(path))) {\n return resolve(event);\n }\n\n // Get session ID from cookie\n const sessionId = event.cookies.get(cookieName);\n if (!sessionId && !options.postgresRls) {\n return resolve(event);\n }\n\n try {\n const service = await getSessionService();\n return await withSessionPermissionContext(\n {\n ...options,\n enterTenantContext: options.enterTenantContext,\n postgresRls: options.postgresRls,\n sessionId,\n sessionService: service,\n },\n async (context) => {\n if (context.session) {\n event.locals.user = context.user;\n event.locals.membership = context.membership ?? null;\n event.locals.permissions = context.permissions;\n event.locals.tenantId = context.tenantId;\n event.locals.sessionId = context.sessionId;\n }\n\n return resolve(event);\n },\n );\n } catch (error) {\n logger.error('Session or request context initialization error', {\n error,\n });\n\n if (options.postgresRls) {\n return new Response('Internal Server Error', { status: 500 });\n }\n\n return resolve(event);\n }\n };\n}\n\n/**\n * Options for creating a session cookie\n */\nexport interface CreateSessionCookieOptions {\n /** Session TTL in seconds (default: 7 days) */\n ttl?: number;\n /** User agent string */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Custom session data */\n data?: Record<string, unknown>;\n}\n\n// Store for session service instances (keyed by db config hash)\nconst sessionServiceCache = new Map<string, SessionService>();\n\n/**\n * Get or create a cached session service instance\n */\nasync function getOrCreateSessionService(\n options: SmrtClassOptions,\n ttl: number,\n): Promise<SessionService> {\n // Simple cache key based on db config\n const cacheKey = JSON.stringify(options.db);\n\n let service = sessionServiceCache.get(cacheKey);\n if (!service) {\n service = await SessionService.create({\n ...options,\n defaultTTL: ttl,\n });\n sessionServiceCache.set(cacheKey, service);\n }\n return service;\n}\n\n/**\n * Helper to create a session and set the cookie after login.\n *\n * @example\n * ```typescript\n * // +page.server.ts\n * import { createSessionCookie } from '@happyvertical/smrt-users/sveltekit';\n * import { redirect } from '@sveltejs/kit';\n *\n * export const actions = {\n * login: async (event) => {\n * // Validate credentials...\n * const user = await validateLogin(email, password);\n *\n * await createSessionCookie(event, user.id, tenantId, {\n * db: { type: 'sqlite', url: 'app.db' },\n * ipAddress: event.getClientAddress(),\n * userAgent: event.request.headers.get('user-agent') ?? '',\n * });\n *\n * throw redirect(303, '/dashboard');\n * }\n * };\n * ```\n */\nexport async function createSessionCookie(\n event: HandleInput['event'],\n userId: string,\n tenantId: string | undefined,\n options: SmrtClassOptions &\n CreateSessionCookieOptions & {\n cookieName?: string;\n cookiePath?: string;\n cookieDomain?: string;\n cookieSecure?: boolean;\n cookieSameSite?: 'strict' | 'lax' | 'none';\n },\n): Promise<string> {\n const cookieName = options.cookieName ?? 'sid';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n const cookiePath = options.cookiePath ?? '/';\n const cookieSameSite = options.cookieSameSite ?? 'lax';\n const cookieSecure = options.cookieSecure ?? event.url.protocol === 'https:';\n\n const service = await getOrCreateSessionService(options, ttl);\n\n const sessionId = await service.createSession(userId, tenantId, {\n ttl,\n userAgent: options.userAgent,\n ipAddress: options.ipAddress,\n data: options.data,\n });\n\n event.cookies.set(cookieName, sessionId, {\n path: cookiePath,\n // undefined => SvelteKit scopes the cookie to the request host.\n domain: options.cookieDomain,\n httpOnly: true,\n secure: cookieSecure,\n sameSite: cookieSameSite,\n maxAge: ttl,\n });\n\n return sessionId;\n}\n\n/**\n * Helper to destroy a session and delete the cookie on logout.\n *\n * @example\n * ```typescript\n * // +page.server.ts\n * import { destroySessionCookie } from '@happyvertical/smrt-users/sveltekit';\n * import { redirect } from '@sveltejs/kit';\n *\n * export const actions = {\n * logout: async (event) => {\n * await destroySessionCookie(event, {\n * db: { type: 'sqlite', url: 'app.db' }\n * });\n * throw redirect(303, '/');\n * }\n * };\n * ```\n */\nexport async function destroySessionCookie(\n event: HandleInput['event'],\n options: SmrtClassOptions & {\n cookieName?: string;\n cookiePath?: string;\n cookieDomain?: string;\n ttl?: number;\n },\n): Promise<void> {\n const cookieName = options.cookieName ?? 'sid';\n const cookiePath = options.cookiePath ?? '/';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n\n const sessionId = event.cookies.get(cookieName);\n\n if (sessionId) {\n try {\n const service = await getOrCreateSessionService(options, ttl);\n await service.destroySession(sessionId);\n } catch (error) {\n // Log but don't fail - cookie will be deleted regardless\n logger.error('Session destruction error', { error });\n }\n }\n\n // A cookie set with a domain must be cleared with the same domain attribute.\n event.cookies.delete(cookieName, {\n path: cookiePath,\n domain: options.cookieDomain,\n });\n}\n\n/**\n * Helper to switch tenant context for the current session.\n *\n * Returns `false` without switching when there is no session, or — fail-closed\n * (#1400) — when the session's user is not an active member of `tenantId`. The\n * target tenant id is therefore safe to take straight from untrusted form data,\n * but callers MUST honour the boolean result rather than assuming success.\n *\n * Session-id ROTATION (#1354 follow-up): a successful switch into a non-null\n * tenant mints a fresh session and revokes the old one. This helper transparently\n * re-sets the session COOKIE to the new id (same flags), so the old cookie value\n * stops working and the browser carries the rotated id forward. A `null` clear\n * leaves the id (and cookie) unchanged.\n *\n * @example\n * ```typescript\n * // +page.server.ts\n * import { switchSessionTenant } from '@happyvertical/smrt-users/sveltekit';\n * import { fail } from '@sveltejs/kit';\n *\n * export const actions = {\n * switchTenant: async (event) => {\n * const data = await event.request.formData();\n * const tenantId = data.get('tenantId') as string;\n *\n * const switched = await switchSessionTenant(event, tenantId, {\n * db: { type: 'sqlite', url: 'app.db' }\n * });\n * if (!switched) {\n * return fail(403, { error: 'Not a member of that tenant.' });\n * }\n *\n * return { success: true };\n * }\n * };\n * ```\n */\nexport async function switchSessionTenant(\n event: HandleInput['event'],\n tenantId: string | null,\n options: SmrtClassOptions & {\n cookieName?: string;\n cookiePath?: string;\n cookieDomain?: string;\n cookieSecure?: boolean;\n cookieSameSite?: 'strict' | 'lax' | 'none';\n ttl?: number;\n },\n): Promise<boolean> {\n const cookieName = options.cookieName ?? 'sid';\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n\n const sessionId = event.cookies.get(cookieName);\n if (!sessionId) return false;\n\n const service = await getOrCreateSessionService(options, ttl);\n const result = await service.switchTenant(sessionId, tenantId);\n\n // On rotation the old id is now revoked; point the cookie at the new id so\n // the previous cookie value can no longer be replayed. Preserve cookie flags.\n if (result.rotated && result.sessionId) {\n const cookiePath = options.cookiePath ?? '/';\n const cookieSameSite = options.cookieSameSite ?? 'lax';\n const cookieSecure =\n options.cookieSecure ?? event.url.protocol === 'https:';\n // Match the cookie lifetime to the rotated session's ACTUAL expiry rather\n // than the request `ttl` — a cached SessionService (keyed on db config only)\n // may have been created with a different defaultTTL, so `ttl` here can\n // diverge from the session the service actually minted.\n const maxAge = result.session\n ? Math.max(\n 0,\n Math.round(\n (new Date(result.session.expiresAt).getTime() - Date.now()) / 1000,\n ),\n )\n : ttl;\n\n event.cookies.set(cookieName, result.sessionId, {\n path: cookiePath,\n // undefined => SvelteKit scopes the cookie to the request host.\n domain: options.cookieDomain,\n httpOnly: true,\n secure: cookieSecure,\n sameSite: cookieSameSite,\n maxAge,\n });\n }\n\n return result.switched;\n}\n\nfunction getOidcProviderName(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | undefined {\n if (typeof options.provider === 'function') {\n return options.provider(event);\n }\n\n return options.provider ?? event.params?.provider ?? options.defaultProvider;\n}\n\nfunction getOidcTransactionCookieName(\n providerName: string,\n options: OidcSvelteKitOptions,\n): string {\n const prefix = options.transactionCookiePrefix ?? 'smrt_oidc';\n const safeProvider = providerName.replace(/[^a-z0-9_-]/giu, '_');\n return `${prefix}_${safeProvider}`;\n}\n\nfunction useSecureCookie(\n event: SvelteKitRequestEvent,\n explicit?: boolean,\n): boolean {\n return explicit ?? event.url.protocol === 'https:';\n}\n\nfunction resolveCallbackPath(\n providerName: string,\n options: OidcSvelteKitOptions,\n): string {\n if (typeof options.callbackPath === 'function') {\n return options.callbackPath(providerName);\n }\n\n return options.callbackPath ?? `/auth/${providerName}/callback`;\n}\n\nfunction resolveProviderWithRedirectUri(\n event: SvelteKitRequestEvent,\n providerName: string,\n provider: OidcProviderConfig,\n options: OidcSvelteKitOptions,\n): ResolvedOidcProviderConfig {\n return {\n ...provider,\n redirectUri:\n provider.redirectUri ??\n new URL(\n resolveCallbackPath(providerName, options),\n event.url.origin,\n ).toString(),\n };\n}\n\nfunction createOidcService(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): OidcLoginService {\n const providerName = getOidcProviderName(event, options);\n const resolved = resolveOidcProviderConfig(providerName, options);\n const provider = resolveProviderWithRedirectUri(\n event,\n resolved.providerName,\n resolved.provider,\n options,\n );\n\n return new OidcLoginService({\n ...options,\n provider,\n providerName: resolved.providerName,\n });\n}\n\nfunction getReturnTo(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | undefined {\n const param = options.returnToParam ?? 'returnTo';\n return getLocalReturnTo(event.url.searchParams.get(param));\n}\n\nfunction getLocalReturnTo(\n returnTo: string | null | undefined,\n): string | undefined {\n if (!returnTo) return undefined;\n // Keep return targets local to avoid open redirects.\n if (returnTo.startsWith('/') && !returnTo.startsWith('//')) {\n return returnTo;\n }\n\n return undefined;\n}\n\nfunction getTransactionCookieSameSite(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): 'strict' | 'lax' | 'none' {\n if (options.transactionCookieSameSite) {\n return options.transactionCookieSameSite;\n }\n\n return useSecureCookie(event, options.transactionCookieSecure)\n ? 'none'\n : 'lax';\n}\n\nfunction getTransactionCookieSecret(\n provider: ResolvedOidcProviderConfig,\n options: OidcSvelteKitOptions,\n): string | undefined {\n const secret = options.transactionCookieSecret ?? provider.clientSecret;\n return secret && secret.length > 0 ? secret : undefined;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary)\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replace(/=+$/u, '');\n}\n\nasync function signTransactionPayload(\n payload: string,\n secret: string,\n): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { hash: 'SHA-256', name: 'HMAC' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign(\n 'HMAC',\n key,\n new TextEncoder().encode(payload),\n );\n return bytesToBase64Url(new Uint8Array(signature));\n}\n\nfunction timingSafeEqual(left: string, right: string): boolean {\n const leftBytes = new TextEncoder().encode(left);\n const rightBytes = new TextEncoder().encode(right);\n let diff = leftBytes.length ^ rightBytes.length;\n const length = Math.max(leftBytes.length, rightBytes.length);\n\n for (let index = 0; index < length; index += 1) {\n diff |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);\n }\n\n return diff === 0;\n}\n\nasync function encodeOidcTransactionCookie(\n transaction: OidcTransaction,\n secret?: string,\n): Promise<string> {\n const payload = encodeOidcTransaction(transaction);\n if (!secret) return payload;\n\n const signature = await signTransactionPayload(payload, secret);\n return `${payload}.${signature}`;\n}\n\nasync function decodeOidcTransactionCookie(\n value: string,\n secret?: string,\n): Promise<OidcTransaction> {\n if (!secret) return decodeOidcTransaction(value);\n\n const separatorIndex = value.lastIndexOf('.');\n if (separatorIndex < 0) {\n throw new OidcLoginError('Invalid OIDC login transaction signature.');\n }\n\n const payload = value.slice(0, separatorIndex);\n const signature = value.slice(separatorIndex + 1);\n const expectedSignature = await signTransactionPayload(payload, secret);\n if (!timingSafeEqual(signature, expectedSignature)) {\n throw new OidcLoginError('Invalid OIDC login transaction signature.');\n }\n\n return decodeOidcTransaction(payload);\n}\n\nfunction getTransactionTtl(options: OidcSvelteKitOptions): number {\n return (\n options.transactionTtl ?? getUsersOidcConfig().transactionTtl ?? 10 * 60\n );\n}\n\nasync function resolveTenantId(\n result: OidcLoginResult,\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): Promise<string | null | undefined> {\n if (typeof options.tenantId === 'function') {\n return options.tenantId(result, event);\n }\n\n return options.tenantId;\n}\n\nfunction redirectResponse(location: string | URL): Response {\n return new Response(null, {\n headers: { location: location.toString() },\n status: 303,\n });\n}\n\nfunction failureResponse(error: unknown): Response {\n const message = error instanceof Error ? error.message : 'OIDC login failed.';\n return new Response(message, {\n status: 401,\n headers: {\n 'content-type': 'text/plain; charset=utf-8',\n },\n });\n}\n\nfunction resolveSuccessRedirect(\n result: CompleteOidcLoginResult,\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | Promise<string> {\n if (typeof options.successRedirect === 'function') {\n return options.successRedirect(result, event);\n }\n\n return options.successRedirect ?? result.returnTo ?? '/';\n}\n\nfunction resolveFailureRedirect(\n error: unknown,\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): string | undefined {\n if (!options.failureRedirect) return undefined;\n\n if (typeof options.failureRedirect === 'function') {\n return options.failureRedirect(error, event);\n }\n\n return options.failureRedirect;\n}\n\n/**\n * Start an OIDC login from a SvelteKit route.\n *\n * Sets a short-lived, HTTP-only transaction cookie containing state, nonce,\n * and PKCE verifier, then returns the provider authorization URL.\n */\nexport async function beginOidcLogin(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): Promise<BeginOidcLoginResult> {\n const service = createOidcService(event, options);\n const transaction = service.createTransaction(getReturnTo(event, options));\n const result = await service.createAuthorizationUrl({ transaction });\n\n event.cookies.set(\n getOidcTransactionCookieName(service.providerName, options),\n await encodeOidcTransactionCookie(\n transaction,\n getTransactionCookieSecret(service.provider, options),\n ),\n {\n httpOnly: true,\n maxAge: getTransactionTtl(options),\n path: options.transactionCookiePath ?? '/',\n sameSite: getTransactionCookieSameSite(event, options),\n secure: useSecureCookie(event, options.transactionCookieSecure),\n },\n );\n\n return {\n providerName: service.providerName,\n transaction,\n url: result.url,\n };\n}\n\n/**\n * Complete an OIDC callback, create or update the SMRT user/profile, and set\n * the session cookie.\n */\nexport async function completeOidcLogin(\n event: SvelteKitRequestEvent,\n options: OidcSvelteKitOptions,\n): Promise<CompleteOidcLoginResult> {\n const service = createOidcService(event, options);\n const cookieName = getOidcTransactionCookieName(\n service.providerName,\n options,\n );\n const cookiePath = options.transactionCookiePath ?? '/';\n\n try {\n const rawTransaction = event.cookies.get(cookieName);\n if (!rawTransaction) {\n throw new OidcLoginError('Missing OIDC login transaction cookie.');\n }\n\n const decodedTransaction = await decodeOidcTransactionCookie(\n rawTransaction,\n getTransactionCookieSecret(service.provider, options),\n );\n const transaction = {\n ...decodedTransaction,\n returnTo: getLocalReturnTo(decodedTransaction.returnTo),\n };\n const ttl = getTransactionTtl(options);\n if (Date.now() - transaction.createdAt > ttl * 1000) {\n throw new OidcLoginError('OIDC login transaction has expired.');\n }\n\n const result = await service.completeLogin(event.url, transaction);\n const tenantId = await resolveTenantId(result, event, options);\n const sessionId = await createSessionCookie(\n event as unknown as HandleInput['event'],\n result.user.id as string,\n tenantId ?? undefined,\n {\n ...options,\n cookieName: options.sessionCookieName,\n cookiePath: options.sessionCookiePath,\n cookieSameSite: options.sessionCookieSameSite,\n cookieSecure: useSecureCookie(event, options.sessionCookieSecure),\n data: {\n oidcIssuer: result.claims.iss,\n oidcProvider: service.providerName,\n },\n ipAddress: event.getClientAddress?.(),\n ttl: options.sessionTtl,\n userAgent: event.request.headers.get('user-agent') ?? undefined,\n },\n );\n\n return {\n ...result,\n providerName: service.providerName,\n returnTo: transaction.returnTo,\n sessionId,\n };\n } finally {\n event.cookies.delete(cookieName, { path: cookiePath });\n }\n}\n\n/**\n * Create a SvelteKit GET handler that redirects to an OIDC provider.\n *\n * @example\n * ```typescript\n * // src/routes/auth/[provider]/login/+server.ts\n * import { createOidcLoginHandler } from '@happyvertical/smrt-users/sveltekit';\n *\n * export const GET = createOidcLoginHandler({\n * db: { type: 'postgres', url: process.env.DATABASE_URL! },\n * });\n * ```\n */\nexport function createOidcLoginHandler(options: OidcSvelteKitOptions) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const { url } = await beginOidcLogin(event, options);\n return redirectResponse(url);\n };\n}\n\n/**\n * Create a SvelteKit GET handler for the provider callback.\n */\nexport function createOidcCallbackHandler(options: OidcSvelteKitOptions) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n try {\n const result = await completeOidcLogin(event, options);\n const redirectTo = await resolveSuccessRedirect(result, event, options);\n return redirectResponse(redirectTo);\n } catch (error) {\n const failureRedirect = resolveFailureRedirect(error, event, options);\n if (failureRedirect) return redirectResponse(failureRedirect);\n return failureResponse(error);\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Terminal / CLI device-code auth\n// ---------------------------------------------------------------------------\n\n/** Cached TerminalAuthService instances keyed by options identity. */\nconst terminalAuthServiceCache = new Map<string, TerminalAuthService>();\n\nfunction terminalAuthCacheKey(options: TerminalAuthServiceOptions): string {\n return JSON.stringify({\n db: options.db,\n cookie: options.sessionCookieName,\n prefix: options.userCodePrefix,\n reqTtl: options.requestTtlSeconds,\n sessTtl: options.sessionTtlSeconds,\n poll: options.pollIntervalSeconds,\n path: options.verificationPath,\n autoExtend: options.sessionAutoExtend,\n maxAttempts: options.maxApproveAttempts,\n attemptWindow: options.approveAttemptWindowSeconds,\n });\n}\n\nasync function getOrCreateTerminalAuthService(\n options: TerminalAuthServiceOptions,\n): Promise<TerminalAuthService> {\n const key = terminalAuthCacheKey(options);\n let service = terminalAuthServiceCache.get(key);\n if (!service) {\n service = await TerminalAuthService.create(options);\n terminalAuthServiceCache.set(key, service);\n }\n return service;\n}\n\n/**\n * Pull `Bearer <token>` out of an `Authorization` header. Returns `null` if\n * the header is missing or malformed.\n */\nexport function parseBearerToken(authorization: string | null): string | null {\n const match = authorization?.match(/^Bearer\\s+(.+)$/iu);\n return match?.[1]?.trim() || null;\n}\n\n/** Options for the terminal-auth start handler. */\nexport interface CreateTerminalAuthStartHandlerOptions\n extends TerminalAuthServiceOptions {\n /**\n * Override the verification origin returned to the CLI (e.g. when the\n * public origin differs from the request origin behind a proxy). Defaults\n * to `event.url.origin`.\n */\n verificationOrigin?: string | ((event: SvelteKitRequestEvent) => string);\n}\n\n/**\n * Create a SvelteKit POST handler that starts a new terminal-auth request.\n * Mount under `/api/cli/auth/start/+server.ts`:\n *\n * ```ts\n * export const POST = createTerminalAuthStartHandler({\n * db: { type: 'postgres', url: process.env.DATABASE_URL! },\n * userCodePrefix: 'WG',\n * });\n * ```\n */\nexport function createTerminalAuthStartHandler(\n options: CreateTerminalAuthStartHandlerOptions,\n) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const service = await getOrCreateTerminalAuthService(options);\n const origin =\n typeof options.verificationOrigin === 'function'\n ? options.verificationOrigin(event)\n : (options.verificationOrigin ?? event.url.origin);\n const result = await service.createRequest(origin);\n // Carries the device/user codes — must never be cached by an intermediary.\n return jsonResponse(result, 201, NO_STORE_HEADERS);\n };\n}\n\n/**\n * Create a SvelteKit POST handler that exchanges a polling device code for a\n * bearer token once the request has been approved. Mount under\n * `/api/cli/auth/token/+server.ts`.\n */\nexport function createTerminalAuthTokenHandler(\n options: TerminalAuthServiceOptions,\n) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const body = (await event.request.json().catch(() => null)) as {\n deviceCode?: unknown;\n } | null;\n const deviceCode =\n typeof body?.deviceCode === 'string' ? body.deviceCode.trim() : '';\n if (!deviceCode) {\n return jsonResponse({ error: 'deviceCode is required.' }, 400);\n }\n\n const service = await getOrCreateTerminalAuthService(options);\n const result = await service.exchangeDeviceCode(deviceCode);\n // Carries the bearer session token — must never be cached by an intermediary.\n return jsonResponse(result, 200, NO_STORE_HEADERS);\n };\n}\n\n/**\n * Create a SvelteKit DELETE handler that revokes the bearer token in the\n * request's `Authorization` header. Always returns `{ authenticated: false }`\n * — does not leak whether the token was actually live, by design.\n */\nexport function createBearerSessionDeleteHandler(\n options: TerminalAuthServiceOptions,\n) {\n return async (event: SvelteKitRequestEvent): Promise<Response> => {\n const token = parseBearerToken(event.request.headers.get('authorization'));\n if (token) {\n try {\n const service = await getOrCreateTerminalAuthService(options);\n await service.destroyBearerSession(token);\n } catch (error) {\n logger.error('Terminal bearer session revocation error', { error });\n }\n }\n return jsonResponse({ authenticated: false });\n };\n}\n\n/**\n * Look up the session associated with a bearer token. Use from\n * `hooks.server.ts` to resolve `Authorization: Bearer <sid>` headers\n * alongside cookie-based sessions.\n */\nexport async function loadBearerSessionContext(\n token: string,\n options: TerminalAuthServiceOptions,\n) {\n const service = await getOrCreateTerminalAuthService(options);\n return service.loadBearerSession(token);\n}\n\n/** Shape passed back to `+page.server.ts` `load`. */\nexport interface TerminalLoginPageData {\n userCode: string;\n requestStatus: string | null;\n}\n\n/** Shape returned by the approve action on success. */\nexport interface TerminalLoginApproveSuccess {\n approved: true;\n requestStatus: string;\n userCode: string;\n}\n\n/** Shape returned by the approve action on failure (HTTP 4xx). */\nexport interface TerminalLoginApproveFailure {\n status: number;\n error: string;\n userCode: string;\n}\n\n/**\n * Page-server helper for the terminal-login approval page. Returns\n * `{ load, approve }` you can spread into a `+page.server.ts` module.\n *\n * `approve` is the action implementation, not a wrapped object — wire it up\n * as you like, e.g. `export const actions = { approve: handler.approve }`.\n *\n * @example\n * ```ts\n * // src/routes/terminal-login/+page.server.ts\n * import { mountTerminalLoginPage } from '@happyvertical/smrt-users/sveltekit';\n *\n * const handlers = mountTerminalLoginPage({\n * db: { type: 'postgres', url: process.env.DATABASE_URL! },\n * userCodePrefix: 'WG',\n * requireUser: (event) => Boolean(event.locals.user),\n * resolveUser: (event) => event.locals.user,\n * resolveTenantId: (event) => event.locals.tenantId,\n * });\n *\n * export const load = handlers.load;\n * export const actions = { approve: handlers.approve };\n * ```\n */\nexport interface MountTerminalLoginPageOptions\n extends TerminalAuthServiceOptions {\n /** Resolve the authenticated user from `event.locals`. */\n resolveUser: (\n event: SvelteKitRequestEvent,\n ) => { id?: string | null; email?: string | null } | null | undefined;\n /** Resolve the tenant id from `event.locals`. */\n resolveTenantId: (event: SvelteKitRequestEvent) => string | null | undefined;\n /** Query-string parameter holding the user code on the page URL. */\n codeQueryParam?: string;\n}\n\nexport interface MountedTerminalLoginPage {\n load: (event: SvelteKitRequestEvent) => Promise<TerminalLoginPageData>;\n approve: (\n event: SvelteKitRequestEvent,\n ) => Promise<\n | TerminalLoginApproveSuccess\n | { type: 'failure'; status: number; data: TerminalLoginApproveFailure }\n >;\n}\n\nexport function mountTerminalLoginPage(\n options: MountTerminalLoginPageOptions,\n): MountedTerminalLoginPage {\n const codeParam = options.codeQueryParam ?? 'code';\n\n return {\n load: async (event) => {\n const userCode =\n event.url.searchParams.get(codeParam)?.trim().toUpperCase() ?? '';\n let requestStatus: string | null = null;\n if (userCode) {\n const service = await getOrCreateTerminalAuthService(options);\n const request = await service.getRequestForUserCode(userCode);\n requestStatus = request?.status ?? null;\n }\n return { requestStatus, userCode };\n },\n\n approve: async (event) => {\n const formData = await event.request.formData();\n const userCode =\n formData.get('userCode')?.toString().trim().toUpperCase() ?? '';\n\n if (!userCode) {\n return {\n type: 'failure',\n status: 400,\n data: {\n status: 400,\n error: 'Enter a terminal login code.',\n userCode,\n },\n };\n }\n\n const user = options.resolveUser(event);\n const tenantId = options.resolveTenantId(event);\n if (!user?.id) {\n return {\n type: 'failure',\n status: 401,\n data: {\n status: 401,\n error: 'Sign in before approving terminal access.',\n userCode,\n },\n };\n }\n\n try {\n const service = await getOrCreateTerminalAuthService(options);\n const approveInput: ApproveCliAuthRequestInput = {\n ipAddress: event.getClientAddress?.(),\n tenantId: tenantId ?? null,\n user: {\n email: user.email ?? '',\n id: user.id,\n },\n userAgent: event.request.headers.get('user-agent') ?? undefined,\n userCode,\n };\n const request = await service.approveRequest(approveInput);\n return {\n approved: true,\n requestStatus: request.status,\n userCode,\n };\n } catch (error) {\n if (error instanceof TerminalAuthRateLimitError) {\n return {\n type: 'failure',\n status: 429,\n data: { status: 429, error: error.message, userCode },\n };\n }\n const message =\n error instanceof TerminalAuthError\n ? error.message\n : error instanceof Error\n ? error.message\n : 'Unable to approve terminal login.';\n return {\n type: 'failure',\n status: 400,\n data: { status: 400, error: message, userCode },\n };\n }\n },\n };\n}\n\nfunction jsonResponse(\n body: unknown,\n status = 200,\n extraHeaders?: Record<string, string>,\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json', ...extraHeaders },\n });\n}\n\n/**\n * Cache headers for terminal-auth responses that carry live credentials\n * (device/user codes, bearer session tokens). Mirrors the `private, no-store`\n * guard on the resource-list handler so no intermediary or CDN caches a token.\n */\nconst NO_STORE_HEADERS: Record<string, string> = {\n 'cache-control': 'private, no-store',\n pragma: 'no-cache',\n};\n\nexport {\n TerminalAuthError,\n TerminalAuthRateLimitError,\n TerminalAuthService,\n type TerminalAuthServiceOptions,\n};\n"],"names":["jsonResponse"],"mappings":";;;;;AA4EA,MAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAkJO,SAAS,0BACd,SAC8C;AAC9C,QAAM;AAAA,IACJ;AAAA,IACA,iBAAiB,CAAC,UAAU,sBAAsB,OAAO,OAAO;AAAA,IAChE,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,cAAc;AAAA,EAAA,IACZ;AAEJ,SAAO,OAAO,UAA6C;AACzD,UAAM,eAAA;AAEN,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,eAAe,KAAK;AAAA,IACtC,SAAS,OAAO;AACd,UAAI,iBAAiB,oBAAoB;AACvC,eAAOA,eAAa,EAAE,OAAO,MAAM,QAAA,GAAW,GAAG;AAAA,MACnD;AACA,YAAM;AAAA,IACR;AAEA,UAAM,YAA2B,CAAA;AACjC,UAAM,eAA8B,CAAA;AACpC,UAAM,+BAAe,IAAA;AAErB,eAAW,CAAC,gBAAgB,UAAU,KAAK,eAAe,iBAAiB;AACzE,YAAM,MAAM;AAAA,QACV;AAAA,QACA;AAAA,MAAA;AAEF,YAAM,YAAY,IAAI,gBAAgB;AACtC,UAAI,CAAC,UAAW;AAChB,UAAI,CAAC,gBAAgB,SAAS,EAAG;AAOjC,UAAI,kBAAkB,GAAG,EAAG;AAI5B,YAAM,eAAe;AAAA,QACnB;AAAA,MAAA;AAEF,YAAM,kBAAkB;AAAA,QACtB;AAAA,QACA;AAAA,MAAA;AAEF,UAAI,gBAAgB,WAAW,EAAG;AAGlC,YAAM,OAAO,aAAa;AAAA,QACxB,WAAW,IAAI;AAAA,QACf,YAAY,IAAI;AAAA,QAChB,eAAe,IAAI;AAAA,QACnB,aAAa,IAAI;AAAA,MAAA,CAClB;AACD,YAAM,WAAW,SAAS,IAAI,IAAI;AAClC,UAAI,YAAY,aAAa,IAAI,eAAe;AAC9C,eAAOA;AAAAA,UACL;AAAA,YACE,OAAO;AAAA,YACP;AAAA,YACA,SAAS,CAAC,UAAU,IAAI,iBAAiB,IAAI,SAAS,EAAE;AAAA,cACtD;AAAA,YAAA;AAAA,UACF;AAAA,UAEF;AAAA,QAAA;AAAA,MAEJ;AACA,eAAS,IAAI,MAAM,IAAI,iBAAiB,IAAI,SAAS;AAErD,YAAM,gBAA+C;AAAA,QACnD;AAAA,QACA,WAAW,IAAI;AAAA,QACf,eAAe,IAAI;AAAA,QACnB,aAAa,IAAI;AAAA,QACjB,OAAO,WAAW,IAAI,SAAS;AAAA,QAC/B,SAAS,eAAe,GAAG;AAAA,MAAA;AAG7B,YAAM,YAA+C;AAAA,QACnD,MAAM,IAAI;AAAA,QACV,eAAe,IAAI;AAAA,QACnB,aAAa,IAAI;AAAA,QACjB,iBAAiB,IAAI;AAAA,MAAA;AAOvB,YAAM,sCAAsB,IAAA;AAC5B,YAAM,aAAkC,CAAA;AAExC,iBAAW,cAAc,iBAAiB;AACxC,cAAM,SAAS,aAAa,KAAK,YAAY,WAAW;AACxD,YAAI,CAAC,OAAO,IAAI;AACd,uBAAa,KAAK;AAAA,YAChB,KAAK,GAAG,IAAI,SAAS,IAAI,UAAU;AAAA,YACnC,QAAQ,OAAO;AAAA,YACf,WAAW,OAAO;AAAA,YAClB,cAAc;AAAA,YACd;AAAA,UAAA,CACD;AACD;AAAA,QACF;AAGA,YACE,OAAO,QAAQ,SAAS,YACxB,qBAAqB;AAAA,UACnB,OAAO,QAAQ;AAAA,QAAA,GAEjB;AACA,iBAAOA;AAAAA,YACL;AAAA,cACE,OAAO;AAAA,cACP,WAAW,IAAI;AAAA,cACf;AAAA,cACA,aAAa,OAAO,QAAQ;AAAA,YAAA;AAAA,YAE9B;AAAA,UAAA;AAAA,QAEJ;AACA,YAAI,gBAAgB,IAAI,OAAO,QAAQ,WAAW,GAAG;AACnD,iBAAOA;AAAAA,YACL;AAAA,cACE,OAAO;AAAA,cACP,WAAW,IAAI;AAAA,cACf,aAAa,OAAO,QAAQ;AAAA,YAAA;AAAA,YAE9B;AAAA,UAAA;AAAA,QAEJ;AACA,wBAAgB,IAAI,OAAO,QAAQ,WAAW;AAC9C,mBAAW,KAAK,OAAO,OAAO;AAAA,MAChC;AAMA,YAAM,gBAAgB,MAAM,QAAQ;AAAA,QAClC,WAAW;AAAA,UAAI,CAAC,cACd,cAAc;AAAA,YACZ,UAAU;AAAA,YACV,SAAS;AAAA,YACT;AAAA,YACA;AAAA,UAAA,CACD;AAAA,QAAA;AAAA,MACH;AAEF,YAAM,UAAU,WAAW,OAAO,CAAC,GAAG,MAAM,cAAc,CAAC,CAAC;AAC5D,UAAI,QAAQ,WAAW,EAAG;AAE1B,gBAAU,KAAK,EAAE,GAAG,eAAe,UAAU,SAAS;AAAA,IACxD;AAIA,UAAM,cAAc,MAAM,QAAQ;AAAA,MAChC,aAAa;AAAA,QAAI,CAAC,MAChB,cAAc;AAAA,UACZ,UAAU,EAAE;AAAA,UACZ,SAAS,EAAE;AAAA,UACX;AAAA,UACA,WAAW,EAAE;AAAA,QAAA,CACd;AAAA,MAAA;AAAA,IACH;AAEF,UAAM,kBAAkB,aACrB,OAAO,CAAC,GAAG,MAAM,YAAY,CAAC,CAAC,EAC/B,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE;AAErC,UAAM,OAAiC;AAAA,MACrC,MAAM,QAAQ,OACV,EAAE,eAAe,MAAM,IAAI,OAAO,QAAQ,KAAK,MAAM,EAAE,EAAA,IACvD,EAAE,eAAe,MAAA;AAAA,MACrB,UAAU;AAAA,MACV;AAAA,IAAA;AAGF,WAAOA,eAAa,MAAM,KAAK;AAAA,MAC7B,iBAAiB;AAAA,IAAA,CAClB;AAAA,EACH;AACF;AA4CA,SAAS,qBACP,iBACA,YACuB;AACvB,QAAM,UAAsC,CAAA;AAC5C,aAAW,CAAC,MAAM,KAAK,KAAK,WAAW,QAAQ,WAAW;AACxD,YAAQ,IAAI,IAAI;AAAA,EAClB;AAEA,QAAM,kBAAmB,WAAW,UAAU,CAAA;AAO9C,QAAM,aACJ,WAAW,cACX,2BAA2B,eAAe,KAC1C,qBAAqB,WAAW,IAAI;AAEtC,SAAO;AAAA,IACL,WAAW,WAAW;AAAA,IACtB,eAAe,WAAW;AAAA,IAC1B,aAAa,WAAW;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,WAAW;AAAA,IAClB,SAAS,WAAW;AAAA,IACpB,gBAAgB,WAAW;AAAA,IAC3B,aAAa,WAAW;AAAA,EAAA;AAE5B;AAyBA,SAAS,kBAAkB,KAAqC;AAC9D,MAAI,IAAI,YAAY,iBAAkB,QAAO;AAC7C,MAAI,IAAI,eAAgB,QAAO;AAC/B,SAAO,kBAAkB,IAAI,aAAa,gBAAgB;AAC5D;AAEA,SAAS,kBACP,MACA,MACA,WAAW,IACF;AACT,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,YAAY,SAAS,KAAK,GAAG;AAC/C,QAAI,QAAQ,SAAS,KAAM,QAAO;AAClC,UAAM,OAAO,OAAO,eAAe,OAAO;AAG1C,QAAI,CAAC,QAAQ,SAAS,QAAS;AAC/B,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAEA,SAAS,2BACP,QACoB;AAEpB,SAAO,QAAQ,aAAa;AAC9B;AAMA,SAAS,0BACP,WACA,cACU;AACV,MAAI,cAAc,MAAM;AAEtB,WAAO,qBAAqB,OAAO,CAAC,MAAM,aAAa,IAAI,CAAC,CAAC;AAAA,EAC/D;AACA,MAAI,cAAc,MAAO,QAAO,CAAA;AAoBhC,MAAI,OAAO,cAAc,YAAY,cAAc,aAAa,CAAA;AAChE,MAAI,MAAM,QAAQ,SAAS,UAAU,CAAA;AAErC,QAAM,MAAM;AAKZ,MAAI,IAAI,YAAY,UAAa,CAAC,MAAM,QAAQ,IAAI,OAAO,EAAG,QAAO,CAAA;AACrE,MAAI,IAAI,YAAY,UAAa,CAAC,MAAM,QAAQ,IAAI,OAAO,EAAG,QAAO,CAAA;AACrE,MAAI,IAAI,WAAW,UAAa,IAAI,WAAW,cAAc,CAAA;AAE7D,QAAM,UAAU,IAAI;AACpB,QAAM,UAAU,IAAI;AACpB,MAAI;AAEJ,MAAI,IAAI,WAAW,OAAO;AACxB,iBAAa,CAAC,GAAG,YAAY;AAAA,EAC/B,WAAW,SAAS,SAAS,GAAG,GAAG;AACjC,iBAAa,CAAC,GAAG,YAAY;AAAA,EAC/B,WAAW,WAAW,QAAQ,SAAS,GAAG;AACxC,iBAAa,QAAQ,OAAO,CAAC,MAAM,aAAa,IAAI,CAAC,CAAC;AAAA,EACxD,OAAO;AAEL,iBAAa,qBAAqB,OAAO,CAAC,MAAM,aAAa,IAAI,CAAC,CAAC;AAAA,EACrE;AAEA,MAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,UAAM,aAAa,IAAI,IAAI,OAAO;AAClC,iBAAa,WAAW,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC;AAAA,EAC1D;AACA,SAAO;AACT;AAEA,SAAS,gBACP,WACS;AACT,MAAI,OAAO,cAAc,YAAY,cAAc,KAAM,QAAO;AAChE,MAAI,MAAM,QAAQ,SAAS,EAAG,QAAO;AACrC,SAAQ,UAAiC,SAAS;AACpD;AAYA,SAAS,aACP,KACA,YACA,aAC0B;AAC1B,QAAM,SAAS,qBAAqB,SAAS,UAA+B;AAE5E,MAAI,QAAQ;AACV,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS,iBAAiB,UAA+B;AAAA,IAAA;AAAA,EAE7D;AAEA,QAAM,YAAY,IAAI,QAAQ,UAAU;AACxC,MAAI,CAAC,WAAW;AAGd,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS,mBAAmB,KAAK,YAAY,QAAW,WAAW;AAAA,IAAA;AAAA,EAEvE;AAGA,QAAM,aAAa,UAAU,YAAY,UAAU;AACnD,MAAI,aAAa,GAAG;AAClB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW,mBAAmB,KAAK,YAAY,WAAW,WAAW;AAAA,IAAA;AAAA,EAEzE;AAEA,QAAM,YAAY,mBAAmB,KAAK,YAAY,WAAW,WAAW;AAG5E,MAAI,mBAAmB,UAAU,YAAY,GAAG;AAC9C,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAMA,MAAI,UAAU,eAAe,YAAY,UAAU,SAAS,UAAU;AACpE,UAAM,mBAAmB,UAAU,YAAY,UAAU,KAAK;AAC9D,QAAI,mBAAmB,mBAAmB,UAAU,UAAU,GAAG;AAC/D,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAEA,SAAO,EAAE,IAAI,MAAM,SAAS,UAAA;AAC9B;AAEA,SAAS,iBAAiB,MAA4C;AACpE,QAAM,OAAO,SAAS,SAAS,SAAS,YAAY,SAAS;AAC7D,QAAM,aACJ,SAAS,UAAU,SAAS,QACxB,QACA,SAAS,WACP,SACA,SAAS,WACP,QACA;AAEV,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,MAAM;AAAA,IACN,OAAO,OAAO,SAAS;AAAA,IACvB;AAAA,IACA,cAAc,CAAA;AAAA,IACd,aAAa,uBAAuB,IAAI;AAAA,EAAA;AAE5C;AAEA,SAAS,mBACP,KACA,YACA,WACA,aACmB;AACnB,QAAM,eAAe,mBAAmB,IAAI,gBAAgB,GAAG;AAC/D,QAAM,cAAc,cAAc,SAAS,UAAU;AAIrD,QAAM,eAA6B,WAAW,WAC1C,eACA;AACJ,QAAM,QAAQ,aAAa,SAAS;AACpC,QAAM,aAAa,uBAAuB,aAAa,MAAM;AAE7D,MAAI;AACJ,MAAI,aAAa,MAAM;AAErB,mBAAe,YAAY,KACxB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAA,CAAM,EACnB,OAAO,OAAO;AACjB,QAAI,aAAa,WAAW,EAAG,gBAAe,CAAC,UAAU;AAAA,EAC3D,OAAO;AACL,mBAAe,CAAC,cAAc,kBAAkB,UAAU,IAAI,UAAU;AAAA,EAC1E;AAEA,QAAM,aAAa,wBAAwB,KAAK,UAAqB;AAErE,SAAO;AAAA,IACL;AAAA,IACA,aAAa,kBAAkB,UAAU;AAAA,IACzC,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,WAAW;AAAA,IACxB;AAAA,EAAA;AAEJ;AAEA,SAAS,wBACP,KACA,YACA,YACqC;AAErC,QAAM,OAAO,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS,UAAU;AAClE,MAAI,MAAM,SAAS,YAAY;AAC7B,UAAM,SAAS,KAAK,SAAS;AAO7B,QAAI,oBAAoB,MAAM,EAAG,QAAO;AAAA,EAC1C;AAOA,SAAO;AACT;AAEA,SAAS,oBACP,QACS;AACT,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,QAAS,OAAoD;AACnE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS;AACrC;AAEA,SAAS,uBAAuB,QAAgC;AAC9D,UAAQ,QAAQ,eAAY;AAAA,IAC1B,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,OAAO,YAAA;AAAA,IAChB;AACE,aAAO;AAAA,EAAA;AAEb;AAEA,SAAS,mBACP,KACkD;AAClD,MAAI,OAAO,QAAQ,YAAY,QAAQ,KAAM,QAAO;AACpD,SAAO;AACT;AAEA,SAAS,mBAAmB,UAA6B;AACvD,SAAO,SAAS,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,CAAC;AACrE;AAEA,SAAS,mBAAmB,YAA+C;AACzE,MAAI,CAAC,cAAc,OAAO,eAAe,SAAU,QAAO;AAC1D,QAAM,QAAS,WACZ;AACH,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,MAAM,IAAI;AACxD,SAAO,KAAK,SAAS;AACvB;AAEA,SAAS,uBAAuB,MAAiC;AAC/D,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAEA,SAAS,eAAe,KAAoC;AAO1D,SAAO,IAAI;AACb;AAEA,SAAS,oBAAoB,MAGlB;AAIT,SAAO,KAAK;AACd;AAEA,SAAS,WAAW,WAA2B;AAE7C,SAAO,UACJ,QAAQ,yBAAyB,OAAO,EACxC,QAAQ,sBAAsB,OAAO;AAC1C;AAYO,MAAM,2BAA2B,MAAM;AAAA,EAC5C,YAAY,UAAU,oCAAoC;AACxD,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAe,sBACb,OACA,SAC0B;AAE1B,QAAM,SAAU,MAAM,UAAU,CAAA;AAChC,MAAI,OAAO,MAAM;AACf,WAAO;AAAA,MACL,MAAM,OAAO;AAAA,MACb,YAAY,OAAO,cAAc;AAAA,MACjC,aAAa,OAAO,eAAe,CAAA;AAAA,MACnC,UAAU,OAAO,YAAY;AAAA,MAC7B,WAAW,OAAO,aAAa;AAAA,IAAA;AAAA,EAEnC;AAGA,QAAM,SAAS,iBAAiB,MAAM,QAAQ,QAAQ,IAAI,eAAe,CAAC;AAC1E,MAAI,QAAQ;AACV,UAAM,MAAM,MAAM,yBAAyB,QAAQ,OAAO;AAC1D,QAAI,CAAC,KAAK;AAER,YAAM,IAAI,mBAAA;AAAA,IACZ;AACA,WAAO;AAAA,MACL,MAAM,IAAI;AAAA,MACV,YAAY,IAAI,cAAc;AAAA,MAC9B,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,MACd,WAAW,IAAI;AAAA,IAAA;AAAA,EAEnB;AAGA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa,CAAA;AAAA,IACb,UAAU;AAAA,IACV,WAAW;AAAA,EAAA;AAEf;AAEA,SAAS,qBAAqB,KAAoC;AAChE,SAAO,IAAI,QAAQ,QAAQ;AAC7B;AAMA,SAASA,eACP,MACA,SAAS,KACT,eAAuC,CAAA,GAC7B;AACV,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC;AAAA,IACA,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,GAAG;AAAA,IAAA;AAAA,EACL,CACD;AACH;ACt5BO,MAAM,uBAAsC;AAAA,EACjD,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,aAAa,CAAA;AAAA,EACb,UAAU;AAAA,EACV,WAAW;AACb;ACyBA,MAAM,SAAS,aAAa,EAAE,OAAO,QAAQ;AA2JtC,SAAS,qBAAqB,SAAwC;AAC3E,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,YAAY,QAAQ,aAAa,CAAA;AAGvC,MAAI,iBAAwC;AAE5C,QAAM,oBAAoB,YAAqC;AAC7D,QAAI,CAAC,gBAAgB;AACnB,uBAAiB,MAAM,eAAe,OAAO;AAAA,QAC3C,GAAG;AAAA,QACH,YAAY;AAAA,QACZ,YAAY,QAAQ,cAAc;AAAA,MAAA,CACnC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,EAAE,OAAO,cAAc;AAEnC,UAAM,OAAO,OAAO;AACpB,UAAM,OAAO,aAAa;AAC1B,UAAM,OAAO,cAAc,CAAA;AAC3B,UAAM,OAAO,WAAW;AACxB,UAAM,OAAO,YAAY;AAGzB,QAAI,UAAU,KAAK,CAAC,SAAS,MAAM,IAAI,SAAS,WAAW,IAAI,CAAC,GAAG;AACjE,aAAO,QAAQ,KAAK;AAAA,IACtB;AAGA,UAAM,YAAY,MAAM,QAAQ,IAAI,UAAU;AAC9C,QAAI,CAAC,aAAa,CAAC,QAAQ,aAAa;AACtC,aAAO,QAAQ,KAAK;AAAA,IACtB;AAEA,QAAI;AACF,YAAM,UAAU,MAAM,kBAAA;AACtB,aAAO,MAAM;AAAA,QACX;AAAA,UACE,GAAG;AAAA,UACH,oBAAoB,QAAQ;AAAA,UAC5B,aAAa,QAAQ;AAAA,UACrB;AAAA,UACA,gBAAgB;AAAA,QAAA;AAAA,QAElB,OAAO,YAAY;AACjB,cAAI,QAAQ,SAAS;AACnB,kBAAM,OAAO,OAAO,QAAQ;AAC5B,kBAAM,OAAO,aAAa,QAAQ,cAAc;AAChD,kBAAM,OAAO,cAAc,QAAQ;AACnC,kBAAM,OAAO,WAAW,QAAQ;AAChC,kBAAM,OAAO,YAAY,QAAQ;AAAA,UACnC;AAEA,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAAA,MAAA;AAAA,IAEJ,SAAS,OAAO;AACd,aAAO,MAAM,mDAAmD;AAAA,QAC9D;AAAA,MAAA,CACD;AAED,UAAI,QAAQ,aAAa;AACvB,eAAO,IAAI,SAAS,yBAAyB,EAAE,QAAQ,KAAK;AAAA,MAC9D;AAEA,aAAO,QAAQ,KAAK;AAAA,IACtB;AAAA,EACF;AACF;AAiBA,MAAM,0CAA0B,IAAA;AAKhC,eAAe,0BACb,SACA,KACyB;AAEzB,QAAM,WAAW,KAAK,UAAU,QAAQ,EAAE;AAE1C,MAAI,UAAU,oBAAoB,IAAI,QAAQ;AAC9C,MAAI,CAAC,SAAS;AACZ,cAAU,MAAM,eAAe,OAAO;AAAA,MACpC,GAAG;AAAA,MACH,YAAY;AAAA,IAAA,CACb;AACD,wBAAoB,IAAI,UAAU,OAAO;AAAA,EAC3C;AACA,SAAO;AACT;AA2BA,eAAsB,oBACpB,OACA,QACA,UACA,SAQiB;AACjB,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,eAAe,QAAQ,gBAAgB,MAAM,IAAI,aAAa;AAEpE,QAAM,UAAU,MAAM,0BAA0B,SAAS,GAAG;AAE5D,QAAM,YAAY,MAAM,QAAQ,cAAc,QAAQ,UAAU;AAAA,IAC9D;AAAA,IACA,WAAW,QAAQ;AAAA,IACnB,WAAW,QAAQ;AAAA,IACnB,MAAM,QAAQ;AAAA,EAAA,CACf;AAED,QAAM,QAAQ,IAAI,YAAY,WAAW;AAAA,IACvC,MAAM;AAAA;AAAA,IAEN,QAAQ,QAAQ;AAAA,IAChB,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,EAAA,CACT;AAED,SAAO;AACT;AAqBA,eAAsB,qBACpB,OACA,SAMe;AACf,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,MAAM,QAAQ,OAAO;AAE3B,QAAM,YAAY,MAAM,QAAQ,IAAI,UAAU;AAE9C,MAAI,WAAW;AACb,QAAI;AACF,YAAM,UAAU,MAAM,0BAA0B,SAAS,GAAG;AAC5D,YAAM,QAAQ,eAAe,SAAS;AAAA,IACxC,SAAS,OAAO;AAEd,aAAO,MAAM,6BAA6B,EAAE,MAAA,CAAO;AAAA,IACrD;AAAA,EACF;AAGA,QAAM,QAAQ,OAAO,YAAY;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,QAAQ;AAAA,EAAA,CACjB;AACH;AAuCA,eAAsB,oBACpB,OACA,UACA,SAQkB;AAClB,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,MAAM,QAAQ,OAAO;AAE3B,QAAM,YAAY,MAAM,QAAQ,IAAI,UAAU;AAC9C,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,UAAU,MAAM,0BAA0B,SAAS,GAAG;AAC5D,QAAM,SAAS,MAAM,QAAQ,aAAa,WAAW,QAAQ;AAI7D,MAAI,OAAO,WAAW,OAAO,WAAW;AACtC,UAAM,aAAa,QAAQ,cAAc;AACzC,UAAM,iBAAiB,QAAQ,kBAAkB;AACjD,UAAM,eACJ,QAAQ,gBAAgB,MAAM,IAAI,aAAa;AAKjD,UAAM,SAAS,OAAO,UAClB,KAAK;AAAA,MACH;AAAA,MACA,KAAK;AAAA,SACF,IAAI,KAAK,OAAO,QAAQ,SAAS,EAAE,QAAA,IAAY,KAAK,SAAS;AAAA,MAAA;AAAA,IAChE,IAEF;AAEJ,UAAM,QAAQ,IAAI,YAAY,OAAO,WAAW;AAAA,MAC9C,MAAM;AAAA;AAAA,MAEN,QAAQ,QAAQ;AAAA,MAChB,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,UAAU;AAAA,MACV;AAAA,IAAA,CACD;AAAA,EACH;AAEA,SAAO,OAAO;AAChB;AAEA,SAAS,oBACP,OACA,SACoB;AACpB,MAAI,OAAO,QAAQ,aAAa,YAAY;AAC1C,WAAO,QAAQ,SAAS,KAAK;AAAA,EAC/B;AAEA,SAAO,QAAQ,YAAY,MAAM,QAAQ,YAAY,QAAQ;AAC/D;AAEA,SAAS,6BACP,cACA,SACQ;AACR,QAAM,SAAS,QAAQ,2BAA2B;AAClD,QAAM,eAAe,aAAa,QAAQ,kBAAkB,GAAG;AAC/D,SAAO,GAAG,MAAM,IAAI,YAAY;AAClC;AAEA,SAAS,gBACP,OACA,UACS;AACT,SAAO,YAAY,MAAM,IAAI,aAAa;AAC5C;AAEA,SAAS,oBACP,cACA,SACQ;AACR,MAAI,OAAO,QAAQ,iBAAiB,YAAY;AAC9C,WAAO,QAAQ,aAAa,YAAY;AAAA,EAC1C;AAEA,SAAO,QAAQ,gBAAgB,SAAS,YAAY;AACtD;AAEA,SAAS,+BACP,OACA,cACA,UACA,SAC4B;AAC5B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,aACE,SAAS,eACT,IAAI;AAAA,MACF,oBAAoB,cAAc,OAAO;AAAA,MACzC,MAAM,IAAI;AAAA,IAAA,EACV,SAAA;AAAA,EAAS;AAEjB;AAEA,SAAS,kBACP,OACA,SACkB;AAClB,QAAM,eAAe,oBAAoB,OAAO,OAAO;AACvD,QAAM,WAAW,0BAA0B,cAAc,OAAO;AAChE,QAAM,WAAW;AAAA,IACf;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,IACT;AAAA,EAAA;AAGF,SAAO,IAAI,iBAAiB;AAAA,IAC1B,GAAG;AAAA,IACH;AAAA,IACA,cAAc,SAAS;AAAA,EAAA,CACxB;AACH;AAEA,SAAS,YACP,OACA,SACoB;AACpB,QAAM,QAAQ,QAAQ,iBAAiB;AACvC,SAAO,iBAAiB,MAAM,IAAI,aAAa,IAAI,KAAK,CAAC;AAC3D;AAEA,SAAS,iBACP,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI,SAAS,WAAW,GAAG,KAAK,CAAC,SAAS,WAAW,IAAI,GAAG;AAC1D,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,6BACP,OACA,SAC2B;AAC3B,MAAI,QAAQ,2BAA2B;AACrC,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO,gBAAgB,OAAO,QAAQ,uBAAuB,IACzD,SACA;AACN;AAEA,SAAS,2BACP,UACA,SACoB;AACpB,QAAM,SAAS,QAAQ,2BAA2B,SAAS;AAC3D,SAAO,UAAU,OAAO,SAAS,IAAI,SAAS;AAChD;AAEA,SAAS,iBAAiB,OAA2B;AACnD,MAAI,SAAS;AACb,aAAW,QAAQ,OAAO;AACxB,cAAU,OAAO,aAAa,IAAI;AAAA,EACpC;AAEA,SAAO,KAAK,MAAM,EACf,WAAW,KAAK,GAAG,EACnB,WAAW,KAAK,GAAG,EACnB,QAAQ,QAAQ,EAAE;AACvB;AAEA,eAAe,uBACb,SACA,QACiB;AACjB,QAAM,MAAM,MAAM,OAAO,OAAO;AAAA,IAC9B;AAAA,IACA,IAAI,YAAA,EAAc,OAAO,MAAM;AAAA,IAC/B,EAAE,MAAM,WAAW,MAAM,OAAA;AAAA,IACzB;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAET,QAAM,YAAY,MAAM,OAAO,OAAO;AAAA,IACpC;AAAA,IACA;AAAA,IACA,IAAI,YAAA,EAAc,OAAO,OAAO;AAAA,EAAA;AAElC,SAAO,iBAAiB,IAAI,WAAW,SAAS,CAAC;AACnD;AAEA,SAAS,gBAAgB,MAAc,OAAwB;AAC7D,QAAM,YAAY,IAAI,cAAc,OAAO,IAAI;AAC/C,QAAM,aAAa,IAAI,cAAc,OAAO,KAAK;AACjD,MAAI,OAAO,UAAU,SAAS,WAAW;AACzC,QAAM,SAAS,KAAK,IAAI,UAAU,QAAQ,WAAW,MAAM;AAE3D,WAAS,QAAQ,GAAG,QAAQ,QAAQ,SAAS,GAAG;AAC9C,aAAS,UAAU,KAAK,KAAK,MAAM,WAAW,KAAK,KAAK;AAAA,EAC1D;AAEA,SAAO,SAAS;AAClB;AAEA,eAAe,4BACb,aACA,QACiB;AACjB,QAAM,UAAU,sBAAsB,WAAW;AACjD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,YAAY,MAAM,uBAAuB,SAAS,MAAM;AAC9D,SAAO,GAAG,OAAO,IAAI,SAAS;AAChC;AAEA,eAAe,4BACb,OACA,QAC0B;AAC1B,MAAI,CAAC,OAAQ,QAAO,sBAAsB,KAAK;AAE/C,QAAM,iBAAiB,MAAM,YAAY,GAAG;AAC5C,MAAI,iBAAiB,GAAG;AACtB,UAAM,IAAI,eAAe,2CAA2C;AAAA,EACtE;AAEA,QAAM,UAAU,MAAM,MAAM,GAAG,cAAc;AAC7C,QAAM,YAAY,MAAM,MAAM,iBAAiB,CAAC;AAChD,QAAM,oBAAoB,MAAM,uBAAuB,SAAS,MAAM;AACtE,MAAI,CAAC,gBAAgB,WAAW,iBAAiB,GAAG;AAClD,UAAM,IAAI,eAAe,2CAA2C;AAAA,EACtE;AAEA,SAAO,sBAAsB,OAAO;AACtC;AAEA,SAAS,kBAAkB,SAAuC;AAChE,SACE,QAAQ,kBAAkB,mBAAA,EAAqB,kBAAkB,KAAK;AAE1E;AAEA,eAAe,gBACb,QACA,OACA,SACoC;AACpC,MAAI,OAAO,QAAQ,aAAa,YAAY;AAC1C,WAAO,QAAQ,SAAS,QAAQ,KAAK;AAAA,EACvC;AAEA,SAAO,QAAQ;AACjB;AAEA,SAAS,iBAAiB,UAAkC;AAC1D,SAAO,IAAI,SAAS,MAAM;AAAA,IACxB,SAAS,EAAE,UAAU,SAAS,WAAS;AAAA,IACvC,QAAQ;AAAA,EAAA,CACT;AACH;AAEA,SAAS,gBAAgB,OAA0B;AACjD,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,SAAO,IAAI,SAAS,SAAS;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,IAAA;AAAA,EAClB,CACD;AACH;AAEA,SAAS,uBACP,QACA,OACA,SAC0B;AAC1B,MAAI,OAAO,QAAQ,oBAAoB,YAAY;AACjD,WAAO,QAAQ,gBAAgB,QAAQ,KAAK;AAAA,EAC9C;AAEA,SAAO,QAAQ,mBAAmB,OAAO,YAAY;AACvD;AAEA,SAAS,uBACP,OACA,OACA,SACoB;AACpB,MAAI,CAAC,QAAQ,gBAAiB,QAAO;AAErC,MAAI,OAAO,QAAQ,oBAAoB,YAAY;AACjD,WAAO,QAAQ,gBAAgB,OAAO,KAAK;AAAA,EAC7C;AAEA,SAAO,QAAQ;AACjB;AAQA,eAAsB,eACpB,OACA,SAC+B;AAC/B,QAAM,UAAU,kBAAkB,OAAO,OAAO;AAChD,QAAM,cAAc,QAAQ,kBAAkB,YAAY,OAAO,OAAO,CAAC;AACzE,QAAM,SAAS,MAAM,QAAQ,uBAAuB,EAAE,aAAa;AAEnE,QAAM,QAAQ;AAAA,IACZ,6BAA6B,QAAQ,cAAc,OAAO;AAAA,IAC1D,MAAM;AAAA,MACJ;AAAA,MACA,2BAA2B,QAAQ,UAAU,OAAO;AAAA,IAAA;AAAA,IAEtD;AAAA,MACE,UAAU;AAAA,MACV,QAAQ,kBAAkB,OAAO;AAAA,MACjC,MAAM,QAAQ,yBAAyB;AAAA,MACvC,UAAU,6BAA6B,OAAO,OAAO;AAAA,MACrD,QAAQ,gBAAgB,OAAO,QAAQ,uBAAuB;AAAA,IAAA;AAAA,EAChE;AAGF,SAAO;AAAA,IACL,cAAc,QAAQ;AAAA,IACtB;AAAA,IACA,KAAK,OAAO;AAAA,EAAA;AAEhB;AAMA,eAAsB,kBACpB,OACA,SACkC;AAClC,QAAM,UAAU,kBAAkB,OAAO,OAAO;AAChD,QAAM,aAAa;AAAA,IACjB,QAAQ;AAAA,IACR;AAAA,EAAA;AAEF,QAAM,aAAa,QAAQ,yBAAyB;AAEpD,MAAI;AACF,UAAM,iBAAiB,MAAM,QAAQ,IAAI,UAAU;AACnD,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,eAAe,wCAAwC;AAAA,IACnE;AAEA,UAAM,qBAAqB,MAAM;AAAA,MAC/B;AAAA,MACA,2BAA2B,QAAQ,UAAU,OAAO;AAAA,IAAA;AAEtD,UAAM,cAAc;AAAA,MAClB,GAAG;AAAA,MACH,UAAU,iBAAiB,mBAAmB,QAAQ;AAAA,IAAA;AAExD,UAAM,MAAM,kBAAkB,OAAO;AACrC,QAAI,KAAK,IAAA,IAAQ,YAAY,YAAY,MAAM,KAAM;AACnD,YAAM,IAAI,eAAe,qCAAqC;AAAA,IAChE;AAEA,UAAM,SAAS,MAAM,QAAQ,cAAc,MAAM,KAAK,WAAW;AACjE,UAAM,WAAW,MAAM,gBAAgB,QAAQ,OAAO,OAAO;AAC7D,UAAM,YAAY,MAAM;AAAA,MACtB;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,YAAY;AAAA,MACZ;AAAA,QACE,GAAG;AAAA,QACH,YAAY,QAAQ;AAAA,QACpB,YAAY,QAAQ;AAAA,QACpB,gBAAgB,QAAQ;AAAA,QACxB,cAAc,gBAAgB,OAAO,QAAQ,mBAAmB;AAAA,QAChE,MAAM;AAAA,UACJ,YAAY,OAAO,OAAO;AAAA,UAC1B,cAAc,QAAQ;AAAA,QAAA;AAAA,QAExB,WAAW,MAAM,mBAAA;AAAA,QACjB,KAAK,QAAQ;AAAA,QACb,WAAW,MAAM,QAAQ,QAAQ,IAAI,YAAY,KAAK;AAAA,MAAA;AAAA,IACxD;AAGF,WAAO;AAAA,MACL,GAAG;AAAA,MACH,cAAc,QAAQ;AAAA,MACtB,UAAU,YAAY;AAAA,MACtB;AAAA,IAAA;AAAA,EAEJ,UAAA;AACE,UAAM,QAAQ,OAAO,YAAY,EAAE,MAAM,YAAY;AAAA,EACvD;AACF;AAeO,SAAS,uBAAuB,SAA+B;AACpE,SAAO,OAAO,UAAoD;AAChE,UAAM,EAAE,IAAA,IAAQ,MAAM,eAAe,OAAO,OAAO;AACnD,WAAO,iBAAiB,GAAG;AAAA,EAC7B;AACF;AAKO,SAAS,0BAA0B,SAA+B;AACvE,SAAO,OAAO,UAAoD;AAChE,QAAI;AACF,YAAM,SAAS,MAAM,kBAAkB,OAAO,OAAO;AACrD,YAAM,aAAa,MAAM,uBAAuB,QAAQ,OAAO,OAAO;AACtE,aAAO,iBAAiB,UAAU;AAAA,IACpC,SAAS,OAAO;AACd,YAAM,kBAAkB,uBAAuB,OAAO,OAAO,OAAO;AACpE,UAAI,gBAAiB,QAAO,iBAAiB,eAAe;AAC5D,aAAO,gBAAgB,KAAK;AAAA,IAC9B;AAAA,EACF;AACF;AAOA,MAAM,+CAA+B,IAAA;AAErC,SAAS,qBAAqB,SAA6C;AACzE,SAAO,KAAK,UAAU;AAAA,IACpB,IAAI,QAAQ;AAAA,IACZ,QAAQ,QAAQ;AAAA,IAChB,QAAQ,QAAQ;AAAA,IAChB,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,YAAY,QAAQ;AAAA,IACpB,aAAa,QAAQ;AAAA,IACrB,eAAe,QAAQ;AAAA,EAAA,CACxB;AACH;AAEA,eAAe,+BACb,SAC8B;AAC9B,QAAM,MAAM,qBAAqB,OAAO;AACxC,MAAI,UAAU,yBAAyB,IAAI,GAAG;AAC9C,MAAI,CAAC,SAAS;AACZ,cAAU,MAAM,oBAAoB,OAAO,OAAO;AAClD,6BAAyB,IAAI,KAAK,OAAO;AAAA,EAC3C;AACA,SAAO;AACT;AAMO,SAAS,iBAAiB,eAA6C;AAC5E,QAAM,QAAQ,eAAe,MAAM,mBAAmB;AACtD,SAAO,QAAQ,CAAC,GAAG,KAAA,KAAU;AAC/B;AAwBO,SAAS,+BACd,SACA;AACA,SAAO,OAAO,UAAoD;AAChE,UAAM,UAAU,MAAM,+BAA+B,OAAO;AAC5D,UAAM,SACJ,OAAO,QAAQ,uBAAuB,aAClC,QAAQ,mBAAmB,KAAK,IAC/B,QAAQ,sBAAsB,MAAM,IAAI;AAC/C,UAAM,SAAS,MAAM,QAAQ,cAAc,MAAM;AAEjD,WAAO,aAAa,QAAQ,KAAK,gBAAgB;AAAA,EACnD;AACF;AAOO,SAAS,+BACd,SACA;AACA,SAAO,OAAO,UAAoD;AAChE,UAAM,OAAQ,MAAM,MAAM,QAAQ,OAAO,MAAM,MAAM,IAAI;AAGzD,UAAM,aACJ,OAAO,MAAM,eAAe,WAAW,KAAK,WAAW,SAAS;AAClE,QAAI,CAAC,YAAY;AACf,aAAO,aAAa,EAAE,OAAO,0BAAA,GAA6B,GAAG;AAAA,IAC/D;AAEA,UAAM,UAAU,MAAM,+BAA+B,OAAO;AAC5D,UAAM,SAAS,MAAM,QAAQ,mBAAmB,UAAU;AAE1D,WAAO,aAAa,QAAQ,KAAK,gBAAgB;AAAA,EACnD;AACF;AAOO,SAAS,iCACd,SACA;AACA,SAAO,OAAO,UAAoD;AAChE,UAAM,QAAQ,iBAAiB,MAAM,QAAQ,QAAQ,IAAI,eAAe,CAAC;AACzE,QAAI,OAAO;AACT,UAAI;AACF,cAAM,UAAU,MAAM,+BAA+B,OAAO;AAC5D,cAAM,QAAQ,qBAAqB,KAAK;AAAA,MAC1C,SAAS,OAAO;AACd,eAAO,MAAM,4CAA4C,EAAE,MAAA,CAAO;AAAA,MACpE;AAAA,IACF;AACA,WAAO,aAAa,EAAE,eAAe,OAAO;AAAA,EAC9C;AACF;AAOA,eAAsB,yBACpB,OACA,SACA;AACA,QAAM,UAAU,MAAM,+BAA+B,OAAO;AAC5D,SAAO,QAAQ,kBAAkB,KAAK;AACxC;AAoEO,SAAS,uBACd,SAC0B;AAC1B,QAAM,YAAY,QAAQ,kBAAkB;AAE5C,SAAO;AAAA,IACL,MAAM,OAAO,UAAU;AACrB,YAAM,WACJ,MAAM,IAAI,aAAa,IAAI,SAAS,GAAG,KAAA,EAAO,YAAA,KAAiB;AACjE,UAAI,gBAA+B;AACnC,UAAI,UAAU;AACZ,cAAM,UAAU,MAAM,+BAA+B,OAAO;AAC5D,cAAM,UAAU,MAAM,QAAQ,sBAAsB,QAAQ;AAC5D,wBAAgB,SAAS,UAAU;AAAA,MACrC;AACA,aAAO,EAAE,eAAe,SAAA;AAAA,IAC1B;AAAA,IAEA,SAAS,OAAO,UAAU;AACxB,YAAM,WAAW,MAAM,MAAM,QAAQ,SAAA;AACrC,YAAM,WACJ,SAAS,IAAI,UAAU,GAAG,WAAW,KAAA,EAAO,YAAA,KAAiB;AAE/D,UAAI,CAAC,UAAU;AACb,eAAO;AAAA,UACL,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,MAAM;AAAA,YACJ,QAAQ;AAAA,YACR,OAAO;AAAA,YACP;AAAA,UAAA;AAAA,QACF;AAAA,MAEJ;AAEA,YAAM,OAAO,QAAQ,YAAY,KAAK;AACtC,YAAM,WAAW,QAAQ,gBAAgB,KAAK;AAC9C,UAAI,CAAC,MAAM,IAAI;AACb,eAAO;AAAA,UACL,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,MAAM;AAAA,YACJ,QAAQ;AAAA,YACR,OAAO;AAAA,YACP;AAAA,UAAA;AAAA,QACF;AAAA,MAEJ;AAEA,UAAI;AACF,cAAM,UAAU,MAAM,+BAA+B,OAAO;AAC5D,cAAM,eAA2C;AAAA,UAC/C,WAAW,MAAM,mBAAA;AAAA,UACjB,UAAU,YAAY;AAAA,UACtB,MAAM;AAAA,YACJ,OAAO,KAAK,SAAS;AAAA,YACrB,IAAI,KAAK;AAAA,UAAA;AAAA,UAEX,WAAW,MAAM,QAAQ,QAAQ,IAAI,YAAY,KAAK;AAAA,UACtD;AAAA,QAAA;AAEF,cAAM,UAAU,MAAM,QAAQ,eAAe,YAAY;AACzD,eAAO;AAAA,UACL,UAAU;AAAA,UACV,eAAe,QAAQ;AAAA,UACvB;AAAA,QAAA;AAAA,MAEJ,SAAS,OAAO;AACd,YAAI,iBAAiB,4BAA4B;AAC/C,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,MAAM,EAAE,QAAQ,KAAK,OAAO,MAAM,SAAS,SAAA;AAAA,UAAS;AAAA,QAExD;AACA,cAAM,UACJ,iBAAiB,oBACb,MAAM,UACN,iBAAiB,QACf,MAAM,UACN;AACR,eAAO;AAAA,UACL,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,MAAM,EAAE,QAAQ,KAAK,OAAO,SAAS,SAAA;AAAA,QAAS;AAAA,MAElD;AAAA,IACF;AAAA,EAAA;AAEJ;AAEA,SAAS,aACP,MACA,SAAS,KACT,cACU;AACV,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC;AAAA,IACA,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,aAAA;AAAA,EAAa,CAChE;AACH;AAOA,MAAM,mBAA2C;AAAA,EAC/C,iBAAiB;AAAA,EACjB,QAAQ;AACV;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-users",
3
- "version": "0.31.1",
3
+ "version": "0.32.0",
4
4
  "description": "Multi-tenant user management for the SMRT framework - users, tenants, roles, permissions, groups",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -42,12 +42,12 @@
42
42
  "dependencies": {
43
43
  "@happyvertical/logger": "^0.74.7",
44
44
  "jose": "^6.1.3",
45
- "@happyvertical/smrt-core": "0.31.1",
46
- "@happyvertical/smrt-profiles": "0.31.1",
47
- "@happyvertical/smrt-config": "0.31.1",
48
- "@happyvertical/smrt-tenancy": "0.31.1",
49
- "@happyvertical/smrt-types": "0.31.1",
50
- "@happyvertical/smrt-ui": "0.31.1"
45
+ "@happyvertical/smrt-config": "0.32.0",
46
+ "@happyvertical/smrt-profiles": "0.32.0",
47
+ "@happyvertical/smrt-core": "0.32.0",
48
+ "@happyvertical/smrt-tenancy": "0.32.0",
49
+ "@happyvertical/smrt-types": "0.32.0",
50
+ "@happyvertical/smrt-ui": "0.32.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@happyvertical/sql": "^0.74.7",
@@ -60,7 +60,7 @@
60
60
  "typescript": "^5.9.3",
61
61
  "vite": "^7.3.1",
62
62
  "vitest": "^4.0.17",
63
- "@happyvertical/smrt-vitest": "0.31.1"
63
+ "@happyvertical/smrt-vitest": "0.32.0"
64
64
  },
65
65
  "keywords": [
66
66
  "smrt",