@objectstack/service-settings 6.2.0 → 6.4.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,"sources":["../src/index.ts","../src/crypto-adapter.ts","../src/settings-service.types.ts","../src/settings-service.ts","../src/in-memory-crypto-provider.ts","../src/settings-routes.ts","../src/manifest.ts","../src/manifests/mail.manifest.ts","../src/manifests/branding.manifest.ts","../src/manifests/feature-flags.manifest.ts","../src/manifests/storage.manifest.ts","../src/manifests/index.ts","../src/translations/en.ts","../src/translations/zh-CN.ts","../src/translations/ja-JP.ts","../src/translations/index.ts","../src/settings-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Public entrypoint for `@objectstack/service-settings`.\n * See ADR-0007 and `README.md`.\n */\n\nexport { SettingsService } from './settings-service.js';\nexport {\n type CryptoAdapter,\n NoopCryptoAdapter,\n} from './crypto-adapter.js';\nexport {\n type SettingsActionHandler,\n type SettingsAuditSink,\n type SettingsContext,\n type SettingsEngine,\n type SettingsRow,\n type SettingsServiceOptions,\n envKeyOf,\n SettingsLockedError,\n UnknownKeyError,\n UnknownNamespaceError,\n} from './settings-service.types.js';\nexport {\n SettingsServicePlugin,\n type SettingsServicePluginOptions,\n} from './settings-service-plugin.js';\nexport {\n registerSettingsRoutes,\n type SettingsRoutesOptions,\n} from './settings-routes.js';\nexport {\n settingsObjects,\n settingsPluginManifestHeader,\n SETTINGS_PLUGIN_ID,\n SETTINGS_PLUGIN_VERSION,\n} from './manifest.js';\n\n// Reference manifests (mail / branding / feature flags) and the\n// convenience aggregate. Hosts can pass `builtinSettingsManifests`\n// directly to `new SettingsServicePlugin({ manifests })`.\nexport {\n builtinSettingsManifests,\n brandingSettingsManifest,\n featureFlagsSettingsManifest,\n mailSettingsManifest,\n mailTestActionHandler,\n storageSettingsManifest,\n storageTestActionHandler,\n} from './manifests/index.js';\n\n// Re-export the spec types for convenience so plugin authors only need\n// one import.\nexport type {\n SettingsManifest,\n ResolvedSettingValue,\n SettingsNamespacePayload,\n SettingsActionResult,\n SpecifierScope,\n} from '@objectstack/spec/system';\n\n// Built-in translations (en / zh-CN / ja-JP) for the reference manifests.\n// Hosts merge `settingsBuiltinTranslations` into their i18next resource tree\n// so SettingsView resolves labels via `<ns>.settings.<namespace>.…`.\nexport {\n settingsBuiltinTranslations,\n en as settingsTranslationsEn,\n zhCN as settingsTranslationsZhCN,\n jaJP as settingsTranslationsJaJP,\n} from './translations/index.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Pluggable adapter for at-rest encryption of `Specifier.encrypted: true`\n * values. The default {@link NoopCryptoAdapter} provides a transparent\n * base64 wrapping suitable for development and tests; production\n * deployments MUST inject a real KMS-backed adapter.\n *\n * encrypt/decrypt are async to leave room for KMS round-trips.\n */\nexport interface CryptoAdapter {\n /** Returns the ciphertext blob to store in `sys_setting.value_enc`. */\n encrypt(plaintext: string, ctx: { namespace: string; key: string }): Promise<string>;\n /** Returns the plaintext used by the resolver. */\n decrypt(ciphertext: string, ctx: { namespace: string; key: string }): Promise<string>;\n /**\n * Stable, short, non-reversible digest used for audit-log entries so\n * operators can correlate value changes without leaking secrets.\n */\n digest(plaintext: string): string;\n}\n\n/**\n * Development / test default. Base64-wraps the plaintext so the column\n * isn't a literal mirror but provides no real confidentiality.\n *\n * Operators are expected to override this via\n * `SettingsServicePluginOptions.crypto`.\n */\nexport class NoopCryptoAdapter implements CryptoAdapter {\n async encrypt(plaintext: string): Promise<string> {\n return 'b64:' + Buffer.from(plaintext, 'utf8').toString('base64');\n }\n async decrypt(ciphertext: string): Promise<string> {\n if (!ciphertext.startsWith('b64:')) {\n // Tolerate legacy plaintext rows during the dev rollout.\n return ciphertext;\n }\n return Buffer.from(ciphertext.slice(4), 'base64').toString('utf8');\n }\n digest(plaintext: string): string {\n // FNV-1a 32-bit — short, stable, non-cryptographic. Audit-only.\n let h = 0x811c9dc5;\n for (let i = 0; i < plaintext.length; i++) {\n h ^= plaintext.charCodeAt(i);\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;\n }\n return 'fnv32:' + h.toString(16).padStart(8, '0');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * SettingsService — the runtime implementation of ADR-0007.\n *\n * Responsibilities:\n * - Maintain an in-memory registry of `SettingsManifest` instances.\n * - Read/write values from the shared `sys_setting` K/V table via the\n * `objectql` data engine, with an in-memory fallback so the service\n * is usable before a real persistence layer is wired up (e.g. unit\n * tests, bootstrap, control-plane mock).\n * - Resolve effective values with `Env > Tenant > User > Default`\n * precedence and tag every value with provenance.\n * - Encrypt-at-rest for `encrypted: true` specifiers using a pluggable\n * {@link CryptoAdapter}.\n * - Emit `sys_audit_log` rows for every successful write (encrypted\n * values are masked).\n * - Dispatch `runAction` for `action_button` specifiers — used by\n * \"Test connection\" / \"Send test email\" etc.\n *\n * The service is intentionally framework-agnostic: it doesn't import\n * the HTTP server, the plugin context, or the audit object schema. The\n * plugin wires those pieces up.\n */\n\nimport type { SettingsActionResult, SpecifierScope } from '@objectstack/spec/system';\nimport { type CryptoAdapter } from './crypto-adapter.js';\n\n/** Caller identity used by the resolver and audit log. */\nexport interface SettingsContext {\n /** Calling user id, when known. Required for `scope: 'user'` reads. */\n userId?: string;\n /** Tenant / project id. Reserved for multi-tenant deployments. */\n tenantId?: string;\n /** Permissions held by the caller (used by REST authz). */\n permissions?: string[];\n /** Source IP / request id for audit correlation. */\n requestId?: string;\n}\n\n/** Storage row shape used by both the engine and the in-memory store. */\nexport interface SettingsRow {\n namespace: string;\n key: string;\n scope: SpecifierScope;\n user_id: string | null;\n value: unknown | null;\n value_enc: string | null;\n encrypted: boolean;\n /**\n * When true, lower-scope rows for the same (namespace, key) are\n * read-only — the resolver still returns this row's value and the\n * mutation API throws `SettingsLockedError`. Only meaningful on\n * upper-scope rows (`global`, `tenant`). (Phase 2)\n */\n locked?: boolean;\n /** Human-readable reason the lock was applied (UI tooltip). */\n locked_reason?: string | null;\n updated_at?: string;\n updated_by?: string | null;\n}\n\n/**\n * Minimal data-engine surface used by the SettingsService. Mirrors the\n * methods we actually call so we can stub it cleanly in tests without\n * pulling the whole `IDataEngine`.\n */\nexport interface SettingsEngine {\n find(\n objectName: string,\n opts: { where?: Record<string, unknown>; limit?: number; bypassTenantAudit?: boolean },\n ): Promise<any[]>;\n insert(\n objectName: string,\n data: Record<string, unknown>,\n opts?: { bypassTenantAudit?: boolean },\n ): Promise<any>;\n update(\n objectName: string,\n opts: {\n where: Record<string, unknown>;\n data: Record<string, unknown>;\n bypassTenantAudit?: boolean;\n },\n ): Promise<any>;\n delete?(objectName: string, opts: { where: Record<string, unknown> }): Promise<any>;\n}\n\n/** Optional audit hook — service-settings won't crash if absent. */\nexport interface SettingsAuditSink {\n record(entry: {\n namespace: string;\n key: string;\n scope: SpecifierScope;\n userId?: string;\n actor?: string;\n action: 'set' | 'reset';\n valueDigest: string;\n encrypted: boolean;\n requestId?: string;\n }): Promise<void> | void;\n}\n\n/**\n * Persistence hook for the `sys_secret` object — used by the secret\n * split introduced in Phase 3. When provided, `SettingsService` writes\n * encrypted specifier values via `ICryptoProvider` into `sys_secret`\n * and stores only the handle id in `sys_setting.value_enc`. When\n * absent, the legacy inline `crypto.encrypt → value_enc` path is used.\n */\nexport interface SettingsSecretStore {\n /** Insert a new secret row; returns the row id (handle id). */\n insert(row: {\n id: string;\n namespace: string;\n key: string;\n kms_key_id: string;\n alg: string;\n version: number;\n ciphertext: string;\n }): Promise<{ id: string }>;\n /** Look up the latest ciphertext for a handle id; null when missing. */\n get(id: string): Promise<{\n id: string;\n namespace: string;\n key: string;\n kms_key_id: string;\n alg: string;\n version: number;\n ciphertext: string;\n } | null>;\n /** Replace an existing secret row (used by rotateKey). */\n update(id: string, patch: {\n kms_key_id?: string;\n alg?: string;\n version?: number;\n ciphertext?: string;\n }): Promise<void>;\n}\n\n/**\n * Append-only writer for the `sys_setting_audit` object — Phase 3\n * audit trail. Distinct from `SettingsAuditSink` (which still writes\n * to the generic `sys_audit_log`) so audit consumers can subscribe\n * to settings activity without scanning the firehose.\n */\nexport interface SettingsAuditWriter {\n write(entry: {\n namespace: string;\n key: string;\n scope: SpecifierScope;\n action: 'set' | 'reset' | 'lock' | 'unlock' | 'rotate';\n source?: 'ui' | 'api' | 'migration' | 'import' | 'system';\n actorId?: string;\n oldHash?: string | null;\n newHash?: string | null;\n encrypted: boolean;\n requestId?: string;\n reason?: string;\n }): Promise<void> | void;\n}\n\n/** Action handler signature for `Specifier.type === 'action_button'`. */\nexport type SettingsActionHandler = (input: {\n namespace: string;\n actionId: string;\n values: Record<string, unknown>;\n payload?: unknown;\n ctx: SettingsContext;\n}) => Promise<SettingsActionResult> | SettingsActionResult;\n\nexport interface SettingsServiceOptions {\n /** Persistence engine. When undefined, an in-memory store is used. */\n engine?: SettingsEngine;\n /** Crypto adapter for `encrypted` values. Defaults to NoopCryptoAdapter. */\n crypto?: CryptoAdapter;\n /**\n * Phase 3 ICryptoProvider used together with `secretStore`. When both\n * are wired, encrypted writes flow to `sys_secret` and `value_enc`\n * holds the handle id. When omitted, the legacy inline `crypto`\n * adapter path remains in effect (back-compat).\n */\n cryptoProvider?: import('@objectstack/spec/contracts').ICryptoProvider;\n /** Phase 3 secret store backing the `sys_secret` object. */\n secretStore?: SettingsSecretStore;\n /** Audit sink. When undefined, writes still succeed but are not logged. */\n audit?: SettingsAuditSink;\n /** Phase 3 dedicated writer for `sys_setting_audit`. */\n auditWriter?: SettingsAuditWriter;\n /**\n * `process.env`-like map. Defaults to `process.env`. Injected so\n * unit tests can simulate locked values without polluting the host\n * environment.\n */\n env?: Record<string, string | undefined>;\n /** Object name backing the K/V store. Defaults to 'sys_setting'. */\n objectName?: string;\n}\n\n/**\n * Convert `(namespace, key)` to the env var convention defined in\n * ADR-0007: uppercase, dots → underscores, hyphens → underscores.\n */\nexport function envKeyOf(namespace: string, key: string): string {\n const slug = `${namespace}_${key}`.replace(/[.-]/g, '_').toUpperCase();\n return slug;\n}\n\n/** Thrown when a caller tries to write a value pinned by env. */\nexport class SettingsLockedError extends Error {\n readonly code = 'SETTINGS_LOCKED' as const;\n constructor(\n readonly namespace: string,\n readonly key: string,\n readonly reason = 'locked-by-env',\n ) {\n super(`Setting '${namespace}.${key}' is locked (${reason}).`);\n }\n}\n\n/** Thrown when the requested namespace has no registered manifest. */\nexport class UnknownNamespaceError extends Error {\n readonly code = 'SETTINGS_UNKNOWN_NAMESPACE' as const;\n constructor(readonly namespace: string) {\n super(`No settings manifest registered for namespace '${namespace}'.`);\n }\n}\n\n/** Thrown when a key isn't declared by the namespace's manifest. */\nexport class UnknownKeyError extends Error {\n readonly code = 'SETTINGS_UNKNOWN_KEY' as const;\n constructor(readonly namespace: string, readonly key: string) {\n super(`Key '${key}' is not declared in manifest '${namespace}'.`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n SettingsManifest,\n ResolvedSettingValue,\n SettingsNamespacePayload,\n SettingsActionResult,\n SpecifierScope,\n SettingsChangeEvent,\n SettingsChangeHandler,\n SettingsUnsubscribe,\n} from '@objectstack/spec/system';\nimport {\n type CryptoAdapter,\n NoopCryptoAdapter,\n} from './crypto-adapter.js';\nimport {\n type SettingsActionHandler,\n type SettingsAuditSink,\n type SettingsContext,\n type SettingsEngine,\n type SettingsRow,\n type SettingsServiceOptions,\n envKeyOf,\n SettingsLockedError,\n UnknownKeyError,\n UnknownNamespaceError,\n} from './settings-service.types.js';\n\nconst DEFAULT_OBJECT = 'sys_setting';\n\n/**\n * Value-bearing specifier types — drives which entries we expect to\n * find in the K/V store. Keeps the resolver in sync with the spec\n * without importing the (large) Zod enum at runtime.\n */\nconst LAYOUT_ONLY_TYPES = new Set([\n 'group',\n 'info_banner',\n 'child_pane',\n 'title_value',\n 'action_button',\n]);\n\ninterface RegisteredManifest {\n manifest: SettingsManifest;\n /** Resolved specifier scopes for fast lookup. */\n scopes: Map<string, SpecifierScope>;\n /** Specifiers marked encrypted (or implicit for `password`). */\n encryptedKeys: Set<string>;\n /** Default values from the manifest, keyed by specifier key. */\n defaults: Map<string, unknown>;\n /** Action handlers registered alongside this manifest. */\n actions: Map<string, SettingsActionHandler>;\n}\n\n/**\n * Concrete SettingsService. See `src/settings-service.types.ts` for\n * the supporting types and `README.md` for the high-level contract.\n */\nexport class SettingsService {\n private engine?: SettingsEngine;\n private readonly crypto: CryptoAdapter;\n private cryptoProvider?: import('@objectstack/spec/contracts').ICryptoProvider;\n private secretStore?: import('./settings-service.types.js').SettingsSecretStore;\n private audit?: SettingsAuditSink;\n private auditWriter?: import('./settings-service.types.js').SettingsAuditWriter;\n private readonly env: Record<string, string | undefined>;\n private readonly objectName: string;\n private readonly registry = new Map<string, RegisteredManifest>();\n /** In-memory fallback when no engine is wired. */\n private readonly memory: SettingsRow[] = [];\n /** Change subscribers, optionally scoped to a namespace. */\n private readonly subscribers = new Set<{\n ns?: string;\n handler: SettingsChangeHandler;\n }>();\n\n constructor(opts: SettingsServiceOptions = {}) {\n this.engine = opts.engine;\n this.crypto = opts.crypto ?? new NoopCryptoAdapter();\n this.cryptoProvider = opts.cryptoProvider;\n this.secretStore = opts.secretStore;\n this.audit = opts.audit;\n this.auditWriter = opts.auditWriter;\n this.env = opts.env ?? (typeof process !== 'undefined' ? process.env : {});\n this.objectName = opts.objectName ?? DEFAULT_OBJECT;\n }\n\n /**\n * Late-bind a data engine and (optionally) an audit sink. Plugins\n * call this from `kernel:ready` once `objectql` is wired so the\n * SettingsService swaps from its in-memory fallback to the real\n * `sys_setting` table without re-registering the service.\n */\n bindEngine(\n engine: SettingsEngine,\n audit?: SettingsAuditSink,\n extras?: {\n secretStore?: import('./settings-service.types.js').SettingsSecretStore;\n auditWriter?: import('./settings-service.types.js').SettingsAuditWriter;\n cryptoProvider?: import('@objectstack/spec/contracts').ICryptoProvider;\n },\n ): void {\n this.engine = engine;\n if (audit) this.audit = audit;\n if (extras?.secretStore) this.secretStore = extras.secretStore;\n if (extras?.auditWriter) this.auditWriter = extras.auditWriter;\n if (extras?.cryptoProvider) this.cryptoProvider = extras.cryptoProvider;\n }\n\n /**\n * Cascade priority ranks for lock comparisons (lower = higher\n * precedence). env<global<tenant<user<default. A locked row at a\n * lower rank blocks writes at all higher ranks.\n */\n private scopeRank(scope: SpecifierScope | 'env' | 'default'): number {\n switch (scope) {\n case 'global': return 1;\n case 'tenant': return 2;\n case 'user': return 3;\n default: return 99;\n }\n }\n\n // ---------------------------------------------------------------------\n // Change events (Phase 1)\n // ---------------------------------------------------------------------\n\n /**\n * Subscribe to `settings:changed` events. When `namespace` is set the\n * handler only fires for that namespace, otherwise it fires for every\n * mutation across the service.\n *\n * Returns an idempotent unsubscribe handle — call it from the\n * consumer's shutdown hook to avoid leaks.\n */\n subscribe(\n namespace: string | undefined,\n handler: SettingsChangeHandler,\n ): SettingsUnsubscribe {\n const entry = { ns: namespace, handler };\n this.subscribers.add(entry);\n return () => {\n this.subscribers.delete(entry);\n };\n }\n\n /**\n * Dispatch a change event to all matching subscribers. Errors thrown\n * by a handler are swallowed to keep the bus crash-safe — handlers\n * are expected to enqueue async work themselves.\n */\n private emitChange(event: SettingsChangeEvent): void {\n if (this.subscribers.size === 0) return;\n for (const sub of this.subscribers) {\n if (sub.ns && sub.ns !== event.namespace) continue;\n try {\n sub.handler(event);\n } catch {\n // Swallow — never break the writer because a listener misbehaves.\n }\n }\n }\n\n // ---------------------------------------------------------------------\n // Manifest registry\n // ---------------------------------------------------------------------\n\n /** Register (or replace) a manifest. Idempotent. */\n registerManifest(manifest: SettingsManifest): void {\n const scopes = new Map<string, SpecifierScope>();\n const encryptedKeys = new Set<string>();\n const defaults = new Map<string, unknown>();\n const defaultScope = manifest.scope ?? 'tenant';\n for (const spec of manifest.specifiers) {\n if (!spec.key || LAYOUT_ONLY_TYPES.has(spec.type)) continue;\n scopes.set(spec.key, spec.scope ?? defaultScope);\n if (spec.encrypted || spec.type === 'password') encryptedKeys.add(spec.key);\n if (typeof spec.default !== 'undefined') defaults.set(spec.key, spec.default);\n }\n const prev = this.registry.get(manifest.namespace);\n const actions = prev?.actions ?? new Map<string, SettingsActionHandler>();\n this.registry.set(manifest.namespace, { manifest, scopes, encryptedKeys, defaults, actions });\n }\n\n /** Look up a manifest, or throw `UnknownNamespaceError`. */\n getManifest(namespace: string): SettingsManifest {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n return reg.manifest;\n }\n\n /** List all registered manifests, optionally filtered by permission. */\n listManifests(ctx: SettingsContext = {}): SettingsManifest[] {\n const perms = new Set(ctx.permissions ?? []);\n const all = Array.from(this.registry.values()).map((r) => r.manifest);\n // Empty permissions ⇒ pass-through (server-side trust, e.g. boot tests).\n if (perms.size === 0) return all;\n return all.filter((m) => perms.has(m.readPermission ?? 'setup.access'));\n }\n\n /** Register a handler for an `action_button` declared in a manifest. */\n registerAction(namespace: string, actionId: string, handler: SettingsActionHandler): void {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n reg.actions.set(actionId, handler);\n }\n\n // ---------------------------------------------------------------------\n // Resolver\n // ---------------------------------------------------------------------\n\n /** Resolve a single key. */\n async get<T = unknown>(\n namespace: string,\n key: string,\n ctx: SettingsContext = {},\n ): Promise<ResolvedSettingValue<T>> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n if (!reg.scopes.has(key)) throw new UnknownKeyError(namespace, key);\n\n // 1. env\n const envName = envKeyOf(namespace, key);\n const envRaw = this.env[envName];\n if (typeof envRaw === 'string') {\n const def = reg.defaults.get(key);\n const value = coerceEnvValue(envRaw, def);\n return {\n value: value as T,\n source: 'env',\n locked: true,\n lockedReason: `Set via env: ${envName}`,\n cascadeChain: [\n { scope: 'env', value, locked: true, lockedReason: `Set via env: ${envName}`, effective: true },\n ],\n };\n }\n\n const scope = reg.scopes.get(key)!;\n // For 'user' scope we pre-filter by user_id; for 'tenant' and 'global'\n // we load everything for the namespace and pick the right row below.\n const rows = await this.loadRows(namespace, scope === 'user' ? ctx.userId ?? null : null);\n\n // 2. cascade walk — env (handled above) > global > tenant > user > default\n //\n // Build the full chain in declared order so the UI can render\n // \"Inherited from Global / Locked by Global / Overrides tenant\"\n // badges. The first non-null entry wins as `source`.\n const chain: NonNullable<ResolvedSettingValue['cascadeChain']> = [];\n\n const globalRow = rows.find((r) => r.key === key && r.scope === 'global');\n if (globalRow) {\n const value = await this.materialiseRow(globalRow);\n chain.push({\n scope: 'global',\n value,\n locked: !!globalRow.locked,\n lockedReason: globalRow.locked_reason ?? undefined,\n });\n }\n\n if (scope === 'tenant' || scope === 'user') {\n const tenantRow = rows.find((r) => r.key === key && r.scope === 'tenant');\n if (tenantRow) {\n chain.push({\n scope: 'tenant',\n value: await this.materialiseRow(tenantRow),\n locked: !!tenantRow.locked,\n lockedReason: tenantRow.locked_reason ?? undefined,\n });\n }\n }\n\n if (scope === 'user') {\n const userRow = rows.find((r) => r.key === key && r.scope === 'user');\n if (userRow) {\n chain.push({\n scope: 'user',\n value: await this.materialiseRow(userRow),\n });\n }\n }\n\n const def = reg.defaults.get(key);\n chain.push({ scope: 'default', value: def ?? null });\n\n // Effective row: highest priority entry. Lock anywhere up the chain\n // locks the effective value (lower scopes can't shadow it).\n const lockedEntry = chain.find((e) => e.locked === true);\n const effective = chain.find((e) => e.value !== null && e.value !== undefined) ?? chain[chain.length - 1];\n effective.effective = true;\n\n return {\n value: effective.value as T,\n source: effective.scope as ResolvedSettingValue['source'],\n locked: !!lockedEntry,\n lockedReason: lockedEntry?.lockedReason,\n cascadeChain: chain,\n };\n }\n\n /** Resolve every value in a namespace + return the manifest. */\n async getNamespace(\n namespace: string,\n ctx: SettingsContext = {},\n ): Promise<SettingsNamespacePayload> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n\n const values: Record<string, ResolvedSettingValue> = {};\n for (const [key] of reg.scopes) {\n values[key] = await this.get(namespace, key, ctx);\n }\n return { manifest: reg.manifest, values };\n }\n\n // ---------------------------------------------------------------------\n // Reactive client (Phase 1)\n // ---------------------------------------------------------------------\n\n /**\n * Build a reactive `ISettingsClient` for a namespace.\n *\n * The client maintains an internal snapshot of the resolved values,\n * refreshing on every `settings:changed` event for the namespace.\n * Consumers call `current` / `get(key)` for synchronous reads and\n * register handlers via `onChange()`.\n *\n * `schema` is optional. When supplied, the snapshot is parsed (and\n * defaulted) through the Zod schema on each refresh — this gives\n * plugins strong types and runtime validation in one call. When\n * absent, raw resolved values flow through unchanged (used by the\n * dynamic console UI which validates per-field).\n */\n async createClient<T extends Record<string, unknown> = Record<string, unknown>>(\n namespace: string,\n opts: {\n ctx?: SettingsContext;\n parse?: (raw: Record<string, unknown>) => T;\n } = {},\n ): Promise<{\n readonly namespace: string;\n readonly current: T;\n get<K extends keyof T>(key: K): T[K];\n onChange(handler: SettingsChangeHandler): SettingsUnsubscribe;\n refresh(): Promise<void>;\n dispose(): void;\n }> {\n const ctx = opts.ctx ?? {};\n let snapshot: T = await this.snapshotOf<T>(namespace, ctx, opts.parse);\n\n const off = this.subscribe(namespace, () => {\n // Fire-and-forget refresh; new readers see the latest snapshot.\n void this.snapshotOf<T>(namespace, ctx, opts.parse).then((next) => {\n snapshot = next;\n });\n });\n\n return {\n namespace,\n get current() {\n return snapshot;\n },\n get<K extends keyof T>(key: K): T[K] {\n return snapshot[key];\n },\n onChange: (handler) => this.subscribe(namespace, handler),\n refresh: async () => {\n snapshot = await this.snapshotOf<T>(namespace, ctx, opts.parse);\n },\n dispose: off,\n };\n }\n\n private async snapshotOf<T>(\n namespace: string,\n ctx: SettingsContext,\n parse?: (raw: Record<string, unknown>) => T,\n ): Promise<T> {\n const payload = await this.getNamespace(namespace, ctx);\n const raw: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(payload.values)) raw[k] = v.value;\n return parse ? parse(raw) : (raw as T);\n }\n\n // ---------------------------------------------------------------------\n // Mutations\n // ---------------------------------------------------------------------\n\n /** Persist a single key. Throws SettingsLockedError when env-locked. */\n async set(\n namespace: string,\n key: string,\n value: unknown,\n ctx: SettingsContext = {},\n ): Promise<ResolvedSettingValue> {\n return (await this.setMany(namespace, { [key]: value }, ctx))[key];\n }\n\n /** Persist multiple keys atomically (best-effort). */\n async setMany(\n namespace: string,\n patch: Record<string, unknown>,\n ctx: SettingsContext = {},\n ): Promise<Record<string, ResolvedSettingValue>> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n\n // Pre-flight: reject the whole batch if any key is locked or unknown.\n for (const key of Object.keys(patch)) {\n if (!reg.scopes.has(key)) throw new UnknownKeyError(namespace, key);\n const envRaw = this.env[envKeyOf(namespace, key)];\n if (typeof envRaw === 'string') throw new SettingsLockedError(namespace, key);\n\n // Phase 2 lock: a row at an upper scope marked locked=true\n // refuses writes at this (lower) scope. Writing AT the same\n // scope as the lock is still permitted (i.e. a platform admin\n // can edit a globally-locked value; a tenant admin cannot).\n const scope = reg.scopes.get(key)!;\n const rows = await this.loadRows(namespace, scope === 'user' ? ctx.userId ?? null : null);\n const upper = rows.find(\n (r) =>\n r.key === key &&\n r.locked === true &&\n this.scopeRank(r.scope) < this.scopeRank(scope),\n );\n if (upper) {\n throw new SettingsLockedError(namespace, key, `locked-by-${upper.scope}`);\n }\n }\n\n for (const [key, rawValue] of Object.entries(patch)) {\n const scope = reg.scopes.get(key)!;\n // global rows are platform-wide (tenant_id=null, user_id=null);\n // user rows pin to ctx.userId; tenant rows leave user_id null and\n // let the engine's tenant scoping fill in tenant_id from ctx.\n const userId = scope === 'user' ? ctx.userId ?? null : null;\n const isEncrypted = reg.encryptedKeys.has(key);\n const isNull = rawValue === null || typeof rawValue === 'undefined';\n\n let storedValue: unknown | null = null;\n let storedEnc: string | null = null;\n let digest = '';\n\n if (!isNull) {\n if (isEncrypted) {\n const plain = typeof rawValue === 'string' ? rawValue : JSON.stringify(rawValue);\n // Phase 3 split: when a sys_secret store + ICryptoProvider are\n // wired, persist the ciphertext in sys_secret and keep the\n // handle id in sys_setting.value_enc. Otherwise fall back to\n // the legacy inline crypto adapter path for back-compat.\n if (this.cryptoProvider && this.secretStore) {\n const handle = await this.cryptoProvider.encrypt(plain, {\n namespace,\n key,\n tenantId: ctx.tenantId,\n });\n await this.secretStore.insert({\n id: handle.id,\n namespace,\n key,\n kms_key_id: handle.kmsKeyId,\n alg: handle.alg,\n version: handle.version,\n ciphertext: handle.ciphertext,\n });\n storedEnc = handle.id;\n digest = this.cryptoProvider.digest(plain);\n } else {\n storedEnc = await this.crypto.encrypt(plain, { namespace, key });\n digest = this.crypto.digest(plain);\n }\n } else {\n storedValue = rawValue;\n digest = this.crypto.digest(stableStringify(rawValue));\n }\n }\n\n await this.upsertRow({\n namespace,\n key,\n scope,\n user_id: userId,\n value: storedValue,\n value_enc: storedEnc,\n encrypted: isEncrypted,\n updated_at: new Date().toISOString(),\n updated_by: ctx.userId ?? null,\n });\n\n if (this.audit) {\n await this.audit.record({\n namespace,\n key,\n scope,\n userId: ctx.userId,\n action: isNull ? 'reset' : 'set',\n valueDigest: isEncrypted ? '<encrypted:' + digest + '>' : digest,\n encrypted: isEncrypted,\n requestId: ctx.requestId,\n });\n }\n\n if (this.auditWriter) {\n try {\n await this.auditWriter.write({\n namespace,\n key,\n scope,\n action: isNull ? 'reset' : 'set',\n source: 'api',\n actorId: ctx.userId,\n oldHash: null,\n newHash: isNull ? null : digest,\n encrypted: isEncrypted,\n requestId: ctx.requestId,\n });\n } catch {\n // never fail a write because the audit table is unhappy.\n }\n }\n\n this.emitChange({\n namespace,\n key,\n scope,\n action: isNull ? 'reset' : 'set',\n at: new Date().toISOString(),\n });\n }\n\n // Re-resolve so callers see the post-write effective values.\n const out: Record<string, ResolvedSettingValue> = {};\n for (const key of Object.keys(patch)) {\n out[key] = await this.get(namespace, key, ctx);\n }\n return out;\n }\n\n /** Invoke a declared action (test connection, rotate, …). */\n async runAction(\n namespace: string,\n actionId: string,\n payload: unknown,\n ctx: SettingsContext = {},\n ): Promise<SettingsActionResult> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n const handler = reg.actions.get(actionId);\n if (!handler) {\n return {\n ok: false,\n severity: 'error',\n message: `No handler registered for action '${actionId}' in '${namespace}'.`,\n };\n }\n const values: Record<string, unknown> = {};\n for (const [key] of reg.scopes) {\n values[key] = (await this.get(namespace, key, ctx)).value;\n }\n try {\n return await handler({ namespace, actionId, values, payload, ctx });\n } catch (err: any) {\n return {\n ok: false,\n severity: 'error',\n message: err?.message ?? 'Action handler threw.',\n };\n }\n }\n\n // ---------------------------------------------------------------------\n // Persistence helpers (engine or in-memory)\n // ---------------------------------------------------------------------\n\n private async loadRows(namespace: string, userId: string | null): Promise<SettingsRow[]> {\n if (this.engine) {\n const where: Record<string, unknown> = { namespace };\n if (userId !== null) where.user_id = userId;\n // Settings rows include platform-wide (`global` scope, tenant_id=null)\n // entries; bypass the tenant-scoping audit warning so loads work\n // uniformly across global/tenant/user without log noise. Per-tenant\n // isolation for `tenant`-scope rows is still enforced by the engine\n // once an ExecutionContext.tenantId is plumbed through (Phase 2+).\n const rows = await this.engine.find(this.objectName, {\n where,\n bypassTenantAudit: true,\n } as any);\n return rows.map((r) => ({\n namespace: r.namespace,\n key: r.key,\n scope: r.scope as SpecifierScope,\n user_id: r.user_id ?? null,\n value: r.value ?? null,\n value_enc: r.value_enc ?? null,\n encrypted: Boolean(r.encrypted),\n locked: Boolean(r.locked),\n locked_reason: r.locked_reason ?? null,\n updated_at: r.updated_at,\n updated_by: r.updated_by ?? null,\n }));\n }\n return this.memory.filter(\n (r) =>\n r.namespace === namespace &&\n (userId === null || r.user_id === userId || r.scope === 'tenant' || r.scope === 'global'),\n );\n }\n\n private async upsertRow(row: SettingsRow): Promise<void> {\n if (this.engine) {\n const where: Record<string, unknown> = {\n namespace: row.namespace,\n key: row.key,\n scope: row.scope,\n user_id: row.user_id ?? null,\n };\n // global rows are platform-wide — bypass the tenant audit warning\n // (we intentionally write tenant_id=null). tenant/user rows still\n // benefit from the warning when ctx.tenantId is missing.\n const bypass = row.scope === 'global' ? { bypassTenantAudit: true } : {};\n const existing = await this.engine.find(this.objectName, {\n where,\n limit: 1,\n ...bypass,\n } as any);\n if (existing[0]) {\n await this.engine.update(this.objectName, {\n where,\n data: { ...row },\n ...bypass,\n } as any);\n } else {\n await this.engine.insert(this.objectName, { ...row }, bypass as any);\n }\n return;\n }\n const idx = this.memory.findIndex(\n (r) =>\n r.namespace === row.namespace &&\n r.key === row.key &&\n r.scope === row.scope &&\n (r.user_id ?? null) === (row.user_id ?? null),\n );\n if (idx >= 0) this.memory[idx] = row;\n else this.memory.push(row);\n }\n\n private async materialiseRow(row: SettingsRow): Promise<unknown> {\n if (row.encrypted) {\n if (!row.value_enc) return null;\n let plain: string;\n // Phase 3: when the value_enc looks like a sys_secret handle and\n // both the secretStore + cryptoProvider are wired, dereference\n // through sys_secret. Otherwise (legacy rows or in-memory tests)\n // fall back to inline crypto-adapter decryption.\n if (\n this.cryptoProvider &&\n this.secretStore &&\n typeof row.value_enc === 'string' &&\n row.value_enc.startsWith('sec_')\n ) {\n const secret = await this.secretStore.get(row.value_enc);\n if (!secret) return null;\n plain = await this.cryptoProvider.decrypt(\n {\n id: secret.id,\n kmsKeyId: secret.kms_key_id,\n alg: secret.alg,\n version: secret.version,\n ciphertext: secret.ciphertext,\n },\n { namespace: row.namespace, key: row.key },\n );\n } else {\n plain = await this.crypto.decrypt(row.value_enc, {\n namespace: row.namespace,\n key: row.key,\n });\n }\n // Try JSON parse so non-string secrets round-trip.\n try {\n return JSON.parse(plain);\n } catch {\n return plain;\n }\n }\n return row.value ?? null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Local helpers\n// ---------------------------------------------------------------------------\n\n/** Stable stringify so the audit digest is order-independent. */\nfunction stableStringify(input: unknown): string {\n if (input === null || typeof input !== 'object') return JSON.stringify(input);\n if (Array.isArray(input)) return '[' + input.map(stableStringify).join(',') + ']';\n const obj = input as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n return '{' + keys.map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k])).join(',') + '}';\n}\n\n/** Re-typed env coercer (the canonical one lives in settings-service.types). */\nfunction coerceEnvValue(raw: string, hint: unknown): unknown {\n if (typeof hint === 'boolean') return raw === 'true' || raw === '1' || raw === 'yes';\n if (typeof hint === 'number') {\n const n = Number(raw);\n return Number.isFinite(n) ? n : raw;\n }\n if (Array.isArray(hint) || (hint && typeof hint === 'object')) {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n return raw;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n CryptoContext,\n CryptoHandle,\n ICryptoProvider,\n} from '@objectstack/spec/contracts';\nimport { createHash, randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\n\n/**\n * InMemoryCryptoProvider — default ICryptoProvider used by the\n * SettingsService when the host application does not wire a real KMS.\n *\n * Encryption: AES-256-GCM with a per-process random data key. The data\n * key lives only in memory; restarting the process loses the ability\n * to decrypt previously-written rows. This is intentional — operators\n * MUST replace this with a KMS-backed provider before relying on\n * `sys_secret` for production secrets. The provider's purpose is to:\n *\n * - exercise the round-trip in unit tests and dev kernels;\n * - provide a \"real-looking\" handle format so consumers don't depend\n * on accidental implementation details of a no-op adapter;\n * - serve as a reference for what AwsKmsCryptoProvider /\n * GcpKmsCryptoProvider implementations need to satisfy.\n *\n * Handle format:\n * id — `sec_` + 32 hex chars (122 bits of entropy)\n * kmsKeyId — `local:in-memory:v<version>`\n * alg — `aes-256-gcm`\n * version — bumps on rotateKey()\n * ciphertext— base64(iv (12) || authTag (16) || cipher)\n *\n * AAD binding: the CryptoContext (namespace + key + tenantId) is\n * folded into AES-GCM AAD so a ciphertext rewrapped from a different\n * (ns, key) tuple fails decryption — guards against operators\n * accidentally copying rows between namespaces.\n */\nexport class InMemoryCryptoProvider implements ICryptoProvider {\n private readonly key: Buffer;\n\n constructor(opts: { key?: Buffer } = {}) {\n this.key = opts.key ?? randomBytes(32);\n }\n\n async encrypt(plain: string, ctx: CryptoContext): Promise<CryptoHandle> {\n const iv = randomBytes(12);\n const cipher = createCipheriv('aes-256-gcm', this.key, iv);\n cipher.setAAD(Buffer.from(this.aadOf(ctx), 'utf8'));\n const enc = Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()]);\n const tag = cipher.getAuthTag();\n const blob = Buffer.concat([iv, tag, enc]).toString('base64');\n return {\n id: 'sec_' + randomBytes(16).toString('hex'),\n kmsKeyId: 'local:in-memory:v1',\n alg: 'aes-256-gcm',\n version: 1,\n ciphertext: blob,\n };\n }\n\n async decrypt(handle: CryptoHandle, ctx: CryptoContext): Promise<string> {\n const buf = Buffer.from(handle.ciphertext, 'base64');\n const iv = buf.subarray(0, 12);\n const tag = buf.subarray(12, 28);\n const data = buf.subarray(28);\n const decipher = createDecipheriv('aes-256-gcm', this.key, iv);\n decipher.setAAD(Buffer.from(this.aadOf(ctx), 'utf8'));\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8');\n }\n\n async rotateKey(handle: CryptoHandle, ctx: CryptoContext): Promise<CryptoHandle> {\n const plain = await this.decrypt(handle, ctx);\n const next = await this.encrypt(plain, ctx);\n return {\n ...next,\n id: handle.id,\n kmsKeyId: `local:in-memory:v${handle.version + 1}`,\n version: handle.version + 1,\n };\n }\n\n digest(plain: string): string {\n return 'sha256:' + createHash('sha256').update(plain, 'utf8').digest('hex');\n }\n\n private aadOf(ctx: CryptoContext): string {\n // Bind ciphertext to (namespace,key) so a row cannot be moved across\n // specifiers. Tenant binding is intentionally omitted because the\n // handle is dereferenced from a `sys_setting` row already scoped to\n // its tenant — adding tenant here would force the decrypt path to\n // re-read that scope.\n return [ctx.namespace, ctx.key].join('|');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * REST surface for the SettingsService — see ADR-0007 §REST.\n *\n * GET /api/settings → visible manifests\n * GET /api/settings/:namespace → { manifest, values }\n * PUT /api/settings/:namespace → batch upsert\n * POST /api/settings/:namespace/:actionId → invoke declared action\n *\n * The route layer is a thin wrapper that maps thrown service errors\n * into proper HTTP status codes; all business logic lives in\n * `SettingsService`.\n */\n\nimport type { IHttpServer, IHttpRequest, IHttpResponse, RouteHandler } from '@objectstack/spec/contracts';\nimport { SettingsService } from './settings-service.js';\nimport {\n SettingsLockedError,\n UnknownKeyError,\n UnknownNamespaceError,\n type SettingsContext,\n} from './settings-service.types.js';\n\nexport interface SettingsRoutesOptions {\n /** Base path. Default `/api/settings`. */\n basePath?: string;\n /**\n * Extract caller identity from the request. The default reads\n * `x-user-id` / `x-tenant-id` headers and parses\n * `x-permissions` as a comma-separated list — fine for dev and\n * straightforward to override in production wiring.\n */\n contextFromRequest?: (req: IHttpRequest) => SettingsContext;\n}\n\nconst defaultContext = (req: IHttpRequest): SettingsContext => {\n const header = (name: string): string | undefined => {\n const v = req.headers?.[name];\n return Array.isArray(v) ? v[0] : v;\n };\n const perms = header('x-permissions');\n return {\n userId: header('x-user-id'),\n tenantId: header('x-tenant-id'),\n permissions: perms ? perms.split(',').map((s) => s.trim()).filter(Boolean) : undefined,\n requestId: header('x-request-id'),\n };\n};\n\nfunction sendError(res: IHttpResponse, status: number, code: string, message: string, extra?: Record<string, unknown>) {\n res.status(status).json({ error: { code, message, ...extra } });\n}\n\nexport function registerSettingsRoutes(\n http: IHttpServer,\n service: SettingsService,\n opts: SettingsRoutesOptions = {},\n): void {\n const base = opts.basePath ?? '/api/settings';\n const ctxOf = opts.contextFromRequest ?? defaultContext;\n\n http.get(base, (async (req, res) => {\n try {\n const ctx = ctxOf(req);\n const manifests = service.listManifests(ctx);\n await res.json({ manifests });\n } catch (err: any) {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Failed to list manifests');\n }\n }) satisfies RouteHandler);\n\n http.get(`${base}/:namespace`, (async (req, res) => {\n const ns = req.params.namespace;\n try {\n const ctx = ctxOf(req);\n const payload = await service.getNamespace(ns, ctx);\n await res.json(payload);\n } catch (err: any) {\n if (err instanceof UnknownNamespaceError) {\n sendError(res, 404, 'UNKNOWN_NAMESPACE', err.message);\n } else {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Failed to read namespace');\n }\n }\n }) satisfies RouteHandler);\n\n http.put(`${base}/:namespace`, (async (req, res) => {\n const ns = req.params.namespace;\n const body = (req.body ?? {}) as Record<string, unknown>;\n try {\n const ctx = ctxOf(req);\n const result = await service.setMany(ns, body, ctx);\n await res.json({ values: result });\n } catch (err: any) {\n if (err instanceof SettingsLockedError) {\n sendError(res, 409, 'SETTINGS_LOCKED', err.message, {\n namespace: err.namespace,\n key: err.key,\n reason: err.reason,\n });\n } else if (err instanceof UnknownNamespaceError) {\n sendError(res, 404, 'UNKNOWN_NAMESPACE', err.message);\n } else if (err instanceof UnknownKeyError) {\n sendError(res, 400, 'UNKNOWN_KEY', err.message, { namespace: err.namespace, key: err.key });\n } else {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Failed to write namespace');\n }\n }\n }) satisfies RouteHandler);\n\n http.post(`${base}/:namespace/:actionId`, (async (req, res) => {\n const { namespace, actionId } = req.params;\n try {\n const ctx = ctxOf(req);\n const result = await service.runAction(namespace, actionId, req.body, ctx);\n const status = result.ok ? 200 : 400;\n await res.status(status).json(result);\n } catch (err: any) {\n if (err instanceof UnknownNamespaceError) {\n sendError(res, 404, 'UNKNOWN_NAMESPACE', err.message);\n } else {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Action failed');\n }\n }\n }) satisfies RouteHandler);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SysSetting, SysSecret, SysSettingAudit } from '@objectstack/platform-objects/system';\n\nexport const SETTINGS_PLUGIN_ID = 'com.objectstack.service.settings';\nexport const SETTINGS_PLUGIN_VERSION = '0.1.0';\n\n/** Objects owned by service-settings. Currently just the K/V store. */\nexport const settingsObjects: any[] = [SysSetting, SysSecret, SysSettingAudit];\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const settingsPluginManifestHeader = {\n id: SETTINGS_PLUGIN_ID,\n namespace: 'sys',\n version: SETTINGS_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'project' as const,\n name: 'Settings Service',\n description:\n 'Generic settings registry + K/V resolver with Env > Tenant > User > Default precedence. ADR-0007.',\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\nimport type { SettingsActionHandler } from '../settings-service.types.js';\n\n// Visibility expressions are written as inline strings here for\n// readability. The spec's ExpressionInputSchema accepts a bare string\n// and normalises it at parse time, but the inferred TypeScript output\n// type expects `{ dialect, source }` objects. Build the manifest as\n// `unknown` first, then cast — keeps the manifest source compact.\nconst manifest = {\n namespace: 'mail',\n version: 1,\n label: 'Mail Delivery',\n icon: 'Mail',\n description: 'SMTP and transactional email provider configuration.',\n scope: 'global',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Communication',\n order: 10,\n specifiers: [\n { type: 'group', id: 'provider', label: 'Provider', required: false,\n description: 'Choose how this workspace sends outbound email.' },\n\n { type: 'select', key: 'provider', label: 'Provider', required: true, default: 'smtp',\n options: [\n { value: 'smtp', label: 'SMTP' },\n { value: 'sendgrid', label: 'SendGrid' },\n { value: 'ses', label: 'Amazon SES' },\n { value: 'postmark', label: 'Postmark' },\n ],\n },\n\n { type: 'group', id: 'smtp', label: 'SMTP', required: false, visible: \"${data.provider === 'smtp'}\" },\n { type: 'text', key: 'smtp_host', label: 'Host', required: true,\n description: 'Example: smtp.example.com', visible: \"${data.provider === 'smtp'}\" },\n { type: 'number', key: 'smtp_port', label: 'Port', required: false, default: 587,\n min: 1, max: 65535, visible: \"${data.provider === 'smtp'}\" },\n { type: 'toggle', key: 'smtp_secure', label: 'Use TLS', required: false, default: true,\n visible: \"${data.provider === 'smtp'}\" },\n { type: 'text', key: 'smtp_user', label: 'Username', required: false,\n visible: \"${data.provider === 'smtp'}\" },\n { type: 'password', key: 'smtp_password', label: 'Password', required: false,\n visible: \"${data.provider === 'smtp'}\" },\n\n { type: 'group', id: 'api_key', label: 'API key', required: false, visible: \"${data.provider !== 'smtp'}\" },\n { type: 'password', key: 'api_key', label: 'API key', required: true, encrypted: true,\n visible: \"${data.provider !== 'smtp'}\" },\n\n { type: 'group', id: 'from_address', label: 'From address', required: false },\n { type: 'email', key: 'from_email', label: 'From email', required: true,\n description: 'Example: no-reply@example.com' },\n { type: 'text', key: 'from_name', label: 'From name', required: false, default: 'ObjectStack' },\n\n { type: 'action_button', id: 'test', label: 'Send test email', required: false, icon: 'Send',\n handler: { kind: 'http', method: 'POST', url: '/api/settings/mail/test' } },\n ],\n};\n\n/** Mail Delivery — SMTP / API provider configuration. */\nexport const mailSettingsManifest = manifest as unknown as SettingsManifest;\n\n/** Built-in action handler stub for `mail/test`. */\nexport const mailTestActionHandler: SettingsActionHandler = async ({ values }) => {\n const provider = String(values.provider ?? 'smtp');\n const fromEmail = values.from_email as string | undefined;\n if (!fromEmail) {\n return { ok: false, severity: 'error', message: 'Configure a from address before testing.' };\n }\n if (provider === 'smtp' && !values.smtp_host) {\n return { ok: false, severity: 'error', message: 'SMTP host is required.' };\n }\n if (provider !== 'smtp' && !values.api_key) {\n return { ok: false, severity: 'error', message: 'API key is required.' };\n }\n return {\n ok: true,\n severity: 'info',\n message: `Configuration looks valid (provider=${provider}). Wire @objectstack/plugin-mail for actual delivery.`,\n };\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\n\n/** Branding — workspace identity (name, logo, theme). */\nexport const brandingSettingsManifest: SettingsManifest = {\n namespace: 'branding',\n version: 1,\n label: 'Branding',\n icon: 'Palette',\n description: 'Workspace name, logo, and accent colour.',\n scope: 'tenant',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Workspace',\n order: 5,\n specifiers: [\n { type: 'group', id: 'identity', label: 'Identity', required: false },\n { type: 'text', key: 'workspace_name', label: 'Workspace name', required: true,\n default: 'ObjectStack', minLength: 1, maxLength: 60 },\n { type: 'email', key: 'support_email', label: 'Support email', required: false,\n description: 'Example: support@example.com' },\n\n { type: 'group', id: 'appearance', label: 'Appearance', required: false },\n { type: 'select', key: 'theme_mode', label: 'Default theme', required: false, default: 'system',\n options: [\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n { value: 'system', label: 'Match system' },\n ],\n },\n { type: 'color', key: 'accent_color', label: 'Accent colour', required: false, default: '#6366f1' },\n { type: 'url', key: 'logo_url', label: 'Logo URL', required: false,\n description: 'Example: https://…/logo.svg' },\n ],\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\n\n/** Feature Flags — opt into experimental capabilities. */\nexport const featureFlagsSettingsManifest: SettingsManifest = {\n namespace: 'feature_flags',\n version: 1,\n label: 'Feature Flags',\n icon: 'FlaskConical',\n description: 'Toggle experimental and beta features for this workspace.',\n scope: 'tenant',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Beta',\n order: 100,\n beta: true,\n specifiers: [\n { type: 'info_banner', id: 'beta_notice', label: 'Heads up', required: false,\n bannerText:\n 'Beta features may change without notice. Pin via env vars (e.g. `FEATURE_FLAGS_AI_ENABLED=true`) to lock for the whole deployment.',\n bannerSeverity: 'warning' },\n\n { type: 'group', id: 'productivity', label: 'Productivity', required: false },\n { type: 'toggle', key: 'ai_enabled', label: 'AI Assistant', required: false, default: false,\n description: 'Enables the in-app AI assistant panel.' },\n { type: 'toggle', key: 'kanban_swimlanes', label: 'Kanban swimlanes', required: false, default: false },\n\n { type: 'group', id: 'collaboration', label: 'Collaboration', required: false },\n { type: 'toggle', key: 'realtime_cursors', label: 'Realtime cursors', required: false, default: false },\n { type: 'toggle', key: 'inline_comments', label: 'Inline comments', required: false, default: true },\n ],\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\nimport type { SettingsActionHandler } from '../settings-service.types.js';\n\n// Mirrors the shape of `mail.manifest.ts`. The actual adapter rebuild\n// + `storage/test` probe live in `@objectstack/service-storage`; this\n// manifest only declares the form + acts as a safe fallback when the\n// storage plugin is not present.\nconst manifest = {\n namespace: 'storage',\n version: 1,\n label: 'File Storage',\n icon: 'HardDrive',\n description:\n 'Backend used for attachments, exports, and user uploads. ' +\n '⚠ Switching adapter does not migrate existing files — files ' +\n 'uploaded under the previous adapter become unreachable through ' +\n 'the new one.',\n scope: 'global',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Infrastructure',\n order: 20,\n specifiers: [\n { type: 'group', id: 'adapter', label: 'Backend', required: false,\n description: 'Choose where uploaded files are stored.' },\n { type: 'select', key: 'adapter', label: 'Adapter', required: true, default: 'local',\n options: [\n { value: 'local', label: 'Local filesystem' },\n { value: 's3', label: 'S3 / S3-compatible' },\n ],\n },\n\n { type: 'group', id: 'local', label: 'Local', required: false,\n visible: \"${data.adapter === 'local'}\" },\n { type: 'text', key: 'local_root', label: 'Root directory', required: false,\n default: './.objectstack/data/uploads',\n description: 'Filesystem path under which files are stored. Relative paths resolve from the server CWD.',\n visible: \"${data.adapter === 'local'}\" },\n\n { type: 'group', id: 's3', label: 'S3', required: false,\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_bucket', label: 'Bucket', required: true,\n description: 'Shared host bucket. Per-environment files are namespaced via the projects/<environmentId>/ prefix.',\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_region', label: 'Region', required: true,\n description: 'Example: us-east-1',\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_endpoint', label: 'Endpoint', required: false,\n description: 'Custom endpoint for S3-compatible providers (R2, MinIO, Wasabi). Leave blank for AWS S3.',\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_access_key_id', label: 'Access key ID', required: true,\n visible: \"${data.adapter === 's3'}\" },\n { type: 'password', key: 's3_secret_access_key', label: 'Secret access key',\n required: true, encrypted: true,\n visible: \"${data.adapter === 's3'}\" },\n { type: 'toggle', key: 's3_force_path_style', label: 'Force path-style URLs',\n required: false, default: false,\n description: 'Enable for MinIO and most S3-compatible providers; disable for AWS S3.',\n visible: \"${data.adapter === 's3'}\" },\n\n { type: 'group', id: 'limits', label: 'Limits', required: false },\n { type: 'number', key: 'presigned_ttl', label: 'Presigned URL TTL (seconds)',\n required: false, default: 3600, min: 60, max: 604800 },\n { type: 'number', key: 'session_ttl', label: 'Upload session TTL (seconds)',\n required: false, default: 86400, min: 300, max: 604800,\n description: 'How long a chunked-upload session stays resumable.' },\n { type: 'number', key: 'max_upload_mb', label: 'Max upload size (MB)',\n required: false, default: 100, min: 1, max: 10240 },\n\n { type: 'action_button', id: 'test', label: 'Test connection',\n required: false, icon: 'Plug',\n handler: { kind: 'http', method: 'POST', url: '/api/settings/storage/test' } },\n ],\n};\n\n/** File Storage — local FS / S3-compatible backend configuration. */\nexport const storageSettingsManifest = manifest as unknown as SettingsManifest;\n\n/**\n * Built-in fallback action handler for `storage/test`. The real\n * implementation lives in `@objectstack/service-storage` and is\n * registered by `StorageServicePlugin` on `kernel:ready` (it overrides\n * this stub via `registerAction`). This fallback only validates the\n * form so the button is still useful when the storage plugin is\n * absent (e.g. in a unit-test kernel that mounts settings only).\n */\nexport const storageTestActionHandler: SettingsActionHandler = async ({ values }) => {\n const adapter = String(values.adapter ?? 'local');\n if (adapter === 'local') {\n const root = values.local_root as string | undefined;\n if (!root) {\n return { ok: false, severity: 'error', message: 'Configure a root directory before testing.' };\n }\n return {\n ok: true,\n severity: 'info',\n message: `Local adapter configured (root=${root}). Mount @objectstack/service-storage to exercise live I/O.`,\n };\n }\n if (adapter === 's3') {\n const missing: string[] = [];\n if (!values.s3_bucket) missing.push('s3_bucket');\n if (!values.s3_region) missing.push('s3_region');\n if (!values.s3_access_key_id) missing.push('s3_access_key_id');\n if (!values.s3_secret_access_key) missing.push('s3_secret_access_key');\n if (missing.length) {\n return { ok: false, severity: 'error', message: `Missing required field${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}` };\n }\n return {\n ok: true,\n severity: 'info',\n message: `S3 adapter configured (bucket=${values.s3_bucket}, region=${values.s3_region}). Mount @objectstack/service-storage to exercise live I/O.`,\n };\n }\n return { ok: false, severity: 'error', message: `Unknown adapter: ${adapter}` };\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/** Reference manifests bundled with service-settings. */\nexport { mailSettingsManifest, mailTestActionHandler } from './mail.manifest.js';\nexport { brandingSettingsManifest } from './branding.manifest.js';\nexport { featureFlagsSettingsManifest } from './feature-flags.manifest.js';\nexport { storageSettingsManifest, storageTestActionHandler } from './storage.manifest.js';\n\nimport { mailSettingsManifest } from './mail.manifest.js';\nimport { brandingSettingsManifest } from './branding.manifest.js';\nimport { featureFlagsSettingsManifest } from './feature-flags.manifest.js';\nimport { storageSettingsManifest } from './storage.manifest.js';\n\n/** Convenience aggregate — pass to `SettingsServicePlugin({ manifests })`. */\nexport const builtinSettingsManifests = [\n brandingSettingsManifest,\n mailSettingsManifest,\n storageSettingsManifest,\n featureFlagsSettingsManifest,\n];\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\n/**\n * English (en) — built-in settings manifest translations.\n *\n * Mirrors literals in `manifests/{mail,branding,feature-flags,storage}.manifest.ts`.\n * Keeping them explicit here lets the resolver chain (locale → fallback → literal)\n * always have at least an English entry to fall back to.\n */\nexport const en: TranslationData = {\n settingsCommon: {\n sourceLabels: {\n env: 'Env',\n global: 'Global',\n tenant: 'Tenant',\n user: 'User',\n default: 'Default',\n },\n },\n settings: {\n mail: {\n title: 'Mail Delivery',\n description: 'SMTP and transactional email provider configuration.',\n groups: {\n provider: { title: 'Provider', description: 'Choose how this workspace sends outbound email.' },\n smtp: { title: 'SMTP' },\n api_key: { title: 'API key' },\n from_address: { title: 'From address' },\n },\n keys: {\n provider: {\n label: 'Provider',\n options: {\n smtp: 'SMTP',\n sendgrid: 'SendGrid',\n ses: 'Amazon SES',\n postmark: 'Postmark',\n },\n },\n smtp_host: { label: 'Host', help: 'Example: smtp.example.com' },\n smtp_port: { label: 'Port' },\n smtp_secure: { label: 'Use TLS' },\n smtp_user: { label: 'Username' },\n smtp_password: { label: 'Password' },\n api_key: { label: 'API key' },\n from_email: { label: 'From email', help: 'Example: no-reply@example.com' },\n from_name: { label: 'From name' },\n },\n actions: {\n test: { label: 'Send test email' },\n },\n },\n\n branding: {\n title: 'Branding',\n description: 'Workspace name, logo, and accent colour.',\n groups: {\n identity: { title: 'Identity' },\n appearance: { title: 'Appearance' },\n },\n keys: {\n workspace_name: { label: 'Workspace name' },\n support_email: { label: 'Support email', help: 'Example: support@example.com' },\n theme_mode: {\n label: 'Default theme',\n options: { light: 'Light', dark: 'Dark', system: 'Match system' },\n },\n accent_color: { label: 'Accent colour' },\n logo_url: { label: 'Logo URL', help: 'Example: https://…/logo.svg' },\n },\n },\n\n feature_flags: {\n title: 'Feature Flags',\n description: 'Toggle experimental and beta features for this workspace.',\n groups: {\n productivity: { title: 'Productivity' },\n collaboration: { title: 'Collaboration' },\n },\n keys: {\n ai_enabled: {\n label: 'AI Assistant',\n help: 'Enables the in-app AI assistant panel.',\n },\n kanban_swimlanes: { label: 'Kanban swimlanes' },\n realtime_cursors: { label: 'Realtime cursors' },\n inline_comments: { label: 'Inline comments' },\n },\n },\n\n storage: {\n title: 'File Storage',\n description:\n 'Backend used for attachments, exports, and user uploads. ' +\n '⚠ Switching adapter does not migrate existing files — files ' +\n 'uploaded under the previous adapter become unreachable through ' +\n 'the new one.',\n groups: {\n adapter: { title: 'Backend', description: 'Choose where uploaded files are stored.' },\n local: { title: 'Local' },\n s3: { title: 'S3' },\n limits: { title: 'Limits' },\n },\n keys: {\n adapter: {\n label: 'Adapter',\n options: { local: 'Local filesystem', s3: 'S3 / S3-compatible' },\n },\n local_root: { label: 'Root directory',\n help: 'Filesystem path under which files are stored. Relative paths resolve from the server CWD.' },\n s3_bucket: { label: 'Bucket',\n help: 'Shared host bucket. Per-environment files are namespaced via the projects/<environmentId>/ prefix.' },\n s3_region: { label: 'Region', help: 'Example: us-east-1' },\n s3_endpoint: { label: 'Endpoint',\n help: 'Custom endpoint for S3-compatible providers (R2, MinIO, Wasabi). Leave blank for AWS S3.' },\n s3_access_key_id: { label: 'Access key ID' },\n s3_secret_access_key: { label: 'Secret access key' },\n s3_force_path_style: { label: 'Force path-style URLs',\n help: 'Enable for MinIO and most S3-compatible providers; disable for AWS S3.' },\n presigned_ttl: { label: 'Presigned URL TTL (seconds)' },\n session_ttl: { label: 'Upload session TTL (seconds)',\n help: 'How long a chunked-upload session stays resumable.' },\n max_upload_mb: { label: 'Max upload size (MB)' },\n },\n actions: {\n test: { label: 'Test connection' },\n },\n },\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\n/**\n * 简体中文 (zh-CN) — built-in settings manifest translations.\n */\nexport const zhCN: TranslationData = {\n settingsCommon: {\n sourceLabels: {\n env: '环境变量',\n global: '全局',\n tenant: '租户',\n user: '用户',\n default: '默认',\n },\n },\n settings: {\n mail: {\n title: '邮件投递',\n description: 'SMTP 与事务性邮件服务商配置。',\n groups: {\n provider: { title: '服务商', description: '选择此工作区如何发送邮件。' },\n smtp: { title: 'SMTP' },\n api_key: { title: 'API 密钥' },\n from_address: { title: '发件地址' },\n },\n keys: {\n provider: {\n label: '服务商',\n options: {\n smtp: 'SMTP',\n sendgrid: 'SendGrid',\n ses: 'Amazon SES',\n postmark: 'Postmark',\n },\n },\n smtp_host: { label: '主机', help: '示例:smtp.example.com' },\n smtp_port: { label: '端口' },\n smtp_secure: { label: '启用 TLS' },\n smtp_user: { label: '用户名' },\n smtp_password: { label: '密码' },\n api_key: { label: 'API 密钥' },\n from_email: { label: '发件地址', help: '示例:no-reply@example.com' },\n from_name: { label: '发件人名称' },\n },\n actions: {\n test: { label: '发送测试邮件' },\n },\n },\n\n branding: {\n title: '品牌',\n description: '工作区名称、Logo 与主题色。',\n groups: {\n identity: { title: '身份' },\n appearance: { title: '外观' },\n },\n keys: {\n workspace_name: { label: '工作区名称' },\n support_email: { label: '客服邮箱', help: '示例:support@example.com' },\n theme_mode: {\n label: '默认主题',\n options: { light: '浅色', dark: '深色', system: '跟随系统' },\n },\n accent_color: { label: '主题色' },\n logo_url: { label: 'Logo 链接', help: '示例:https://…/logo.svg' },\n },\n },\n\n feature_flags: {\n title: '功能开关',\n description: '为当前工作区开启实验性与测试功能。',\n groups: {\n productivity: { title: '生产力' },\n collaboration: { title: '协作' },\n },\n keys: {\n ai_enabled: {\n label: 'AI 助手',\n help: '启用应用内 AI 助手面板。',\n },\n kanban_swimlanes: { label: '看板泳道' },\n realtime_cursors: { label: '实时光标' },\n inline_comments: { label: '行内评论' },\n },\n },\n\n storage: {\n title: '文件存储',\n description:\n '附件、导出文件与用户上传所使用的存储后端。' +\n '⚠ 切换适配器不会迁移已有文件 —— 通过旧适配器上传的文件,在新适配器中将不可访问。',\n groups: {\n adapter: { title: '存储后端', description: '选择上传文件的存放位置。' },\n local: { title: '本地' },\n s3: { title: 'S3' },\n limits: { title: '限制' },\n },\n keys: {\n adapter: {\n label: '适配器',\n options: { local: '本地文件系统', s3: 'S3 / S3 兼容' },\n },\n local_root: { label: '根目录',\n help: '文件存放的文件系统路径。相对路径相对于服务进程的工作目录。' },\n s3_bucket: { label: 'Bucket',\n help: '共享主机 Bucket。各项目的文件通过 projects/<environmentId>/ 前缀进行隔离。' },\n s3_region: { label: '区域', help: '示例:us-east-1' },\n s3_endpoint: { label: 'Endpoint',\n help: 'S3 兼容服务(R2、MinIO、Wasabi)的自定义 Endpoint;AWS S3 请留空。' },\n s3_access_key_id: { label: 'Access Key ID' },\n s3_secret_access_key: { label: 'Secret Access Key' },\n s3_force_path_style: { label: '强制路径风格 URL',\n help: 'MinIO 与大多数 S3 兼容服务请开启;AWS S3 请关闭。' },\n presigned_ttl: { label: '预签名 URL 有效期(秒)' },\n session_ttl: { label: '分片上传会话有效期(秒)',\n help: '分片上传会话保持可续传的时长。' },\n max_upload_mb: { label: '单文件最大上传(MB)' },\n },\n actions: {\n test: { label: '测试连接' },\n },\n },\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\n/**\n * 日本語 (ja-JP) — built-in settings manifest translations.\n */\nexport const jaJP: TranslationData = {\n settingsCommon: {\n sourceLabels: {\n env: '環境変数',\n global: 'グローバル',\n tenant: 'テナント',\n user: 'ユーザー',\n default: 'デフォルト',\n },\n },\n settings: {\n mail: {\n title: 'メール配信',\n description: 'SMTP およびトランザクションメールプロバイダー設定。',\n groups: {\n provider: { title: 'プロバイダー', description: 'このワークスペースの送信方法を選択します。' },\n smtp: { title: 'SMTP' },\n api_key: { title: 'API キー' },\n from_address: { title: '差出人アドレス' },\n },\n keys: {\n provider: {\n label: 'プロバイダー',\n options: {\n smtp: 'SMTP',\n sendgrid: 'SendGrid',\n ses: 'Amazon SES',\n postmark: 'Postmark',\n },\n },\n smtp_host: { label: 'ホスト', help: '例: smtp.example.com' },\n smtp_port: { label: 'ポート' },\n smtp_secure: { label: 'TLS を使用' },\n smtp_user: { label: 'ユーザー名' },\n smtp_password: { label: 'パスワード' },\n api_key: { label: 'API キー' },\n from_email: { label: '差出人アドレス', help: '例: no-reply@example.com' },\n from_name: { label: '差出人名' },\n },\n actions: {\n test: { label: 'テストメール送信' },\n },\n },\n\n branding: {\n title: 'ブランディング',\n description: 'ワークスペース名・ロゴ・アクセントカラー。',\n groups: {\n identity: { title: 'アイデンティティ' },\n appearance: { title: '外観' },\n },\n keys: {\n workspace_name: { label: 'ワークスペース名' },\n support_email: { label: 'サポートメール', help: '例: support@example.com' },\n theme_mode: {\n label: 'デフォルトテーマ',\n options: { light: 'ライト', dark: 'ダーク', system: 'システムに従う' },\n },\n accent_color: { label: 'アクセントカラー' },\n logo_url: { label: 'ロゴ URL', help: '例: https://…/logo.svg' },\n },\n },\n\n feature_flags: {\n title: '機能フラグ',\n description: 'このワークスペースで実験的・ベータ機能を切替えます。',\n groups: {\n productivity: { title: '生産性' },\n collaboration: { title: 'コラボレーション' },\n },\n keys: {\n ai_enabled: {\n label: 'AI アシスタント',\n help: 'アプリ内 AI アシスタントパネルを有効化します。',\n },\n kanban_swimlanes: { label: 'カンバンのスイムレーン' },\n realtime_cursors: { label: 'リアルタイムカーソル' },\n inline_comments: { label: 'インラインコメント' },\n },\n },\n\n storage: {\n title: 'ファイルストレージ',\n description:\n '添付ファイル・エクスポート・ユーザーアップロードに使用するバックエンド。' +\n '⚠ アダプターを切替えても既存ファイルは移行されません。以前のアダプターでアップロードされたファイルは新しいアダプターからアクセスできなくなります。',\n groups: {\n adapter: { title: 'バックエンド', description: 'アップロードファイルの保存先を選択します。' },\n local: { title: 'ローカル' },\n s3: { title: 'S3' },\n limits: { title: '制限' },\n },\n keys: {\n adapter: {\n label: 'アダプター',\n options: { local: 'ローカルファイルシステム', s3: 'S3 / S3 互換' },\n },\n local_root: { label: 'ルートディレクトリ',\n help: 'ファイルを保存するファイルシステムパス。相対パスはサーバーの CWD から解決されます。' },\n s3_bucket: { label: 'バケット',\n help: '共有ホストバケット。プロジェクト毎のファイルは projects/<environmentId>/ プレフィックスで分離されます。' },\n s3_region: { label: 'リージョン', help: '例: us-east-1' },\n s3_endpoint: { label: 'エンドポイント',\n help: 'S3 互換プロバイダ (R2, MinIO, Wasabi) のカスタムエンドポイント。AWS S3 の場合は空欄。' },\n s3_access_key_id: { label: 'アクセスキー ID' },\n s3_secret_access_key: { label: 'シークレットアクセスキー' },\n s3_force_path_style: { label: 'パススタイル URL を強制',\n help: 'MinIO や多くの S3 互換プロバイダで有効化。AWS S3 では無効化。' },\n presigned_ttl: { label: '署名付き URL の有効期間 (秒)' },\n session_ttl: { label: 'アップロードセッション TTL (秒)',\n help: 'チャンクアップロードの再開可能期間。' },\n max_upload_mb: { label: '最大アップロードサイズ (MB)' },\n },\n actions: {\n test: { label: '接続テスト' },\n },\n },\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Built-in Settings translations.\n *\n * Mirrors the CRM example's `src/translations/{en,zh-CN,ja-JP}.ts` convention —\n * one file per locale, aggregated into a `TranslationBundle` here.\n *\n * Hosts merge `settingsBuiltinTranslations` into the i18next resource tree\n * under whatever namespace makes sense (the console wires it as `system`),\n * making keys resolvable as `<ns>.settings.<namespace>.{title,description,...}`.\n */\n\nimport type { TranslationBundle } from '@objectstack/spec/system';\nimport { en } from './en.js';\nimport { zhCN } from './zh-CN.js';\nimport { jaJP } from './ja-JP.js';\n\nexport { en, zhCN, jaJP };\n\nexport const settingsBuiltinTranslations: TranslationBundle = {\n en,\n 'zh-CN': zhCN,\n 'ja-JP': jaJP,\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { IHttpServer, IDataEngine } from '@objectstack/spec/contracts';\nimport type { SettingsManifest } from '@objectstack/spec/system';\nimport { SettingsService } from './settings-service.js';\nimport type { ICryptoProvider } from '@objectstack/spec/contracts';\nimport type { SettingsAuditSink, SettingsAuditWriter, SettingsEngine, SettingsSecretStore } from './settings-service.types.js';\nimport type { CryptoAdapter } from './crypto-adapter.js';\nimport { InMemoryCryptoProvider } from './in-memory-crypto-provider.js';\nimport { registerSettingsRoutes } from './settings-routes.js';\nimport {\n settingsObjects,\n settingsPluginManifestHeader,\n SETTINGS_PLUGIN_ID,\n SETTINGS_PLUGIN_VERSION,\n} from './manifest.js';\nimport {\n builtinSettingsManifests,\n mailTestActionHandler,\n storageTestActionHandler,\n} from './manifests/index.js';\nimport { settingsBuiltinTranslations } from './translations/index.js';\n\n/** Configuration options for the SettingsServicePlugin. */\nexport interface SettingsServicePluginOptions {\n /**\n * Pre-register these manifests at boot. When omitted, the bundled\n * builtin manifests (mail / branding / feature_flags) are loaded so\n * a host gets a working Settings hub out of the box. Pass an empty\n * array to opt out entirely.\n */\n manifests?: SettingsManifest[];\n /** Override the default crypto adapter. */\n crypto?: CryptoAdapter;\n\n /**\n * Phase 3 KMS hook. When provided, encrypted specifier values are\n * routed through this provider into `sys_secret`; `sys_setting.value_enc`\n * holds the handle id only. Defaults to `InMemoryCryptoProvider`\n * (NOT suitable for production secrets — replace with an AWS / GCP\n * KMS-backed implementation).\n */\n cryptoProvider?: ICryptoProvider;\n /** Override the default base path (`/api/settings`). */\n basePath?: string;\n /** Disable REST route registration. */\n registerRoutes?: boolean;\n /** Override the env source. Defaults to `process.env`. */\n env?: Record<string, string | undefined>;\n /**\n * Action handlers to register at boot, keyed by namespace and action\n * id. The bundled `mail.test` handler is registered automatically\n * unless this object is provided.\n */\n actionHandlers?: Record<string, Record<string, import('./settings-service.types.js').SettingsActionHandler>>;\n}\n\n/**\n * SettingsServicePlugin — wires the SettingsService into the kernel.\n *\n * 1. `init`: instantiate the service, register it under `'settings'`,\n * and ship `sys_setting` to the manifest service so the engine\n * auto-provisions the table.\n * 2. `start` → `kernel:ready`: bind the data engine (when present),\n * wire the audit sink (when present), mount REST routes.\n */\nexport class SettingsServicePlugin implements Plugin {\n name = SETTINGS_PLUGIN_ID;\n version = SETTINGS_PLUGIN_VERSION;\n type = 'standard' as const;\n\n private readonly opts: SettingsServicePluginOptions;\n private service: SettingsService | null = null;\n\n constructor(opts: SettingsServicePluginOptions = {}) {\n this.opts = {\n ...opts,\n manifests: opts.manifests ?? builtinSettingsManifests,\n actionHandlers: opts.actionHandlers ?? {\n mail: { test: mailTestActionHandler },\n storage: { test: storageTestActionHandler },\n },\n };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.service = new SettingsService({\n crypto: this.opts.crypto,\n env: this.opts.env,\n });\n for (const m of this.opts.manifests ?? []) this.service.registerManifest(m);\n for (const [ns, handlers] of Object.entries(this.opts.actionHandlers ?? {})) {\n for (const [id, fn] of Object.entries(handlers)) {\n this.service.registerAction(ns, id, fn);\n }\n }\n\n ctx.registerService('settings', this.service);\n ctx.logger?.info?.(\n `SettingsServicePlugin: registered (manifests=${this.opts.manifests?.length ?? 0})`,\n );\n\n // Register the K/V object so the engine creates the table.\n try {\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...settingsPluginManifestHeader,\n objects: settingsObjects,\n });\n } catch {\n // manifest service is optional — skip in lean test kernels.\n }\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (!this.service) return;\n\n ctx.hook('kernel:ready', async () => {\n // Contribute built-in settings translations into the i18n service.\n // Done in `kernel:ready` (not `init`) because the i18n service plugin\n // is typically registered AFTER capability-loaded service plugins.\n try {\n const i18n = ctx.getService<{\n loadTranslations: (locale: string, data: Record<string, unknown>) => void;\n }>('i18n');\n let loaded = 0;\n for (const [locale, data] of Object.entries(settingsBuiltinTranslations)) {\n if (data && typeof data === 'object') {\n try {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n loaded++;\n } catch (err: any) {\n ctx.logger?.warn?.(\n `SettingsServicePlugin: failed to load translations for '${locale}': ${err?.message ?? err}`,\n );\n }\n }\n }\n if (loaded > 0) {\n ctx.logger?.info?.(\n `SettingsServicePlugin: contributed built-in translations (${loaded} locale${loaded > 1 ? 's' : ''})`,\n );\n }\n } catch {\n // i18n service not registered — manifest literals remain authoritative.\n }\n\n // Late-bind the data engine.\n let engine: IDataEngine | null = null;\n try {\n engine = ctx.getService<IDataEngine>('objectql');\n } catch {\n // ok — fall back to in-memory.\n }\n if (engine) {\n // Late-bind the engine + audit sink on the existing service\n // instance. We avoid re-registering the service because the\n // kernel disallows `registerService` for an already-registered\n // name.\n this.service!.bindEngine(\n engine as unknown as SettingsEngine,\n this.buildAuditSink(ctx, engine),\n {\n secretStore: this.buildSecretStore(engine),\n auditWriter: this.buildAuditWriter(ctx, engine),\n cryptoProvider: this.opts.cryptoProvider ?? new InMemoryCryptoProvider(),\n },\n );\n }\n\n if (this.opts.registerRoutes === false) return;\n\n let http: IHttpServer | null = null;\n try {\n http = ctx.getService<IHttpServer>('http-server');\n } catch {\n // ok — no HTTP server in this deployment.\n }\n if (!http) {\n ctx.logger?.warn?.(\n 'SettingsServicePlugin: no HTTP server available — REST routes not registered. ' +\n 'SettingsService is still reachable via kernel.getService(\"settings\").',\n );\n return;\n }\n registerSettingsRoutes(http, this.service!, { basePath: this.opts.basePath });\n ctx.logger?.info?.(\n 'SettingsServicePlugin: REST routes registered at ' + (this.opts.basePath ?? '/api/settings'),\n );\n });\n }\n\n /** Glue an `engine.insert('sys_audit_log', …)` audit sink. */\n private buildAuditSink(ctx: PluginContext, engine: IDataEngine): SettingsAuditSink {\n return {\n record: async (entry) => {\n try {\n await (engine as any).insert?.('sys_audit_log', {\n actor_id: entry.userId ?? null,\n entity_type: 'sys_setting',\n entity_id: `${entry.namespace}.${entry.key}`,\n action: entry.action,\n payload: {\n namespace: entry.namespace,\n key: entry.key,\n scope: entry.scope,\n encrypted: entry.encrypted,\n digest: entry.valueDigest,\n },\n request_id: entry.requestId ?? null,\n occurred_at: new Date().toISOString(),\n });\n } catch (err: any) {\n ctx.logger?.warn?.('SettingsServicePlugin: audit record failed: ' + (err?.message ?? err));\n }\n },\n };\n }\n\n /**\n * Phase 3: build a `sys_secret`-backed implementation of\n * `SettingsSecretStore`. The store bypasses the tenant audit\n * warning because secrets are scoped through their owning\n * `sys_setting` row (which already carries the tenant context).\n */\n private buildSecretStore(engine: IDataEngine): SettingsSecretStore {\n const eng: any = engine;\n return {\n async insert(row) {\n await eng.insert('sys_secret', row, { bypassTenantAudit: true });\n return { id: row.id };\n },\n async get(id) {\n const rows = await eng.find('sys_secret', {\n where: { id },\n limit: 1,\n bypassTenantAudit: true,\n });\n const row = Array.isArray(rows) ? rows[0] : rows?.data?.[0];\n return row ?? null;\n },\n async update(id, patch) {\n await eng.update('sys_secret', {\n where: { id },\n data: patch,\n bypassTenantAudit: true,\n });\n },\n };\n }\n\n /**\n * Phase 3: append-only writer for `sys_setting_audit`. Failures here\n * MUST NOT abort the settings write, so all calls are wrapped in a\n * try/catch and reported through the plugin logger.\n */\n private buildAuditWriter(ctx: PluginContext, engine: IDataEngine): SettingsAuditWriter {\n const eng: any = engine;\n return {\n write: async (entry) => {\n try {\n await eng.insert('sys_setting_audit', {\n namespace: entry.namespace,\n key: entry.key,\n scope: entry.scope,\n action: entry.action,\n source: entry.source ?? 'api',\n actor_id: entry.actorId ?? null,\n old_hash: entry.oldHash ?? null,\n new_hash: entry.newHash ?? null,\n encrypted: !!entry.encrypted,\n request_id: entry.requestId ?? null,\n reason: entry.reason ?? null,\n created_at: new Date().toISOString(),\n }, { bypassTenantAudit: true });\n } catch (err: any) {\n ctx.logger?.warn?.('SettingsServicePlugin: setting-audit write failed: ' + (err?.message ?? err));\n }\n },\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC6BO,IAAM,oBAAN,MAAiD;AAAA,EACtD,MAAM,QAAQ,WAAoC;AAChD,WAAO,SAAS,OAAO,KAAK,WAAW,MAAM,EAAE,SAAS,QAAQ;AAAA,EAClE;AAAA,EACA,MAAM,QAAQ,YAAqC;AACjD,QAAI,CAAC,WAAW,WAAW,MAAM,GAAG;AAElC,aAAO;AAAA,IACT;AACA,WAAO,OAAO,KAAK,WAAW,MAAM,CAAC,GAAG,QAAQ,EAAE,SAAS,MAAM;AAAA,EACnE;AAAA,EACA,OAAO,WAA2B;AAEhC,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,WAAK,UAAU,WAAW,CAAC;AAC3B,UAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,SAAU;AAAA,IACxE;AACA,WAAO,WAAW,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EAClD;AACF;;;AC0JO,SAAS,SAAS,WAAmB,KAAqB;AAC/D,QAAM,OAAO,GAAG,SAAS,IAAI,GAAG,GAAG,QAAQ,SAAS,GAAG,EAAE,YAAY;AACrE,SAAO;AACT;AAGO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAE7C,YACW,WACA,KACA,SAAS,iBAClB;AACA,UAAM,YAAY,SAAS,IAAI,GAAG,gBAAgB,MAAM,IAAI;AAJnD;AACA;AACA;AAJX,SAAS,OAAO;AAAA,EAOhB;AACF;AAGO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAE/C,YAAqB,WAAmB;AACtC,UAAM,kDAAkD,SAAS,IAAI;AADlD;AADrB,SAAS,OAAO;AAAA,EAGhB;AACF;AAGO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAEzC,YAAqB,WAA4B,KAAa;AAC5D,UAAM,QAAQ,GAAG,kCAAkC,SAAS,IAAI;AAD7C;AAA4B;AADjD,SAAS,OAAO;AAAA,EAGhB;AACF;;;AC7MA,IAAM,iBAAiB;AAOvB,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAkBM,IAAM,kBAAN,MAAsB;AAAA,EAkB3B,YAAY,OAA+B,CAAC,GAAG;AAT/C,SAAiB,WAAW,oBAAI,IAAgC;AAEhE;AAAA,SAAiB,SAAwB,CAAC;AAE1C;AAAA,SAAiB,cAAc,oBAAI,IAGhC;AAGD,SAAK,SAAS,KAAK;AACnB,SAAK,SAAS,KAAK,UAAU,IAAI,kBAAkB;AACnD,SAAK,iBAAiB,KAAK;AAC3B,SAAK,cAAc,KAAK;AACxB,SAAK,QAAQ,KAAK;AAClB,SAAK,cAAc,KAAK;AACxB,SAAK,MAAM,KAAK,QAAQ,OAAO,YAAY,cAAc,QAAQ,MAAM,CAAC;AACxE,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,WACE,QACA,OACA,QAKM;AACN,SAAK,SAAS;AACd,QAAI,MAAO,MAAK,QAAQ;AACxB,QAAI,QAAQ,YAAa,MAAK,cAAc,OAAO;AACnD,QAAI,QAAQ,YAAa,MAAK,cAAc,OAAO;AACnD,QAAI,QAAQ,eAAgB,MAAK,iBAAiB,OAAO;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,UAAU,OAAmD;AACnE,YAAQ,OAAO;AAAA,MACb,KAAK;AAAW,eAAO;AAAA,MACvB,KAAK;AAAW,eAAO;AAAA,MACvB,KAAK;AAAW,eAAO;AAAA,MACvB;AAAgB,eAAO;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,UACE,WACA,SACqB;AACrB,UAAM,QAAQ,EAAE,IAAI,WAAW,QAAQ;AACvC,SAAK,YAAY,IAAI,KAAK;AAC1B,WAAO,MAAM;AACX,WAAK,YAAY,OAAO,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,OAAkC;AACnD,QAAI,KAAK,YAAY,SAAS,EAAG;AACjC,eAAW,OAAO,KAAK,aAAa;AAClC,UAAI,IAAI,MAAM,IAAI,OAAO,MAAM,UAAW;AAC1C,UAAI;AACF,YAAI,QAAQ,KAAK;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiBA,WAAkC;AACjD,UAAM,SAAS,oBAAI,IAA4B;AAC/C,UAAM,gBAAgB,oBAAI,IAAY;AACtC,UAAM,WAAW,oBAAI,IAAqB;AAC1C,UAAM,eAAeA,UAAS,SAAS;AACvC,eAAW,QAAQA,UAAS,YAAY;AACtC,UAAI,CAAC,KAAK,OAAO,kBAAkB,IAAI,KAAK,IAAI,EAAG;AACnD,aAAO,IAAI,KAAK,KAAK,KAAK,SAAS,YAAY;AAC/C,UAAI,KAAK,aAAa,KAAK,SAAS,WAAY,eAAc,IAAI,KAAK,GAAG;AAC1E,UAAI,OAAO,KAAK,YAAY,YAAa,UAAS,IAAI,KAAK,KAAK,KAAK,OAAO;AAAA,IAC9E;AACA,UAAM,OAAO,KAAK,SAAS,IAAIA,UAAS,SAAS;AACjD,UAAM,UAAU,MAAM,WAAW,oBAAI,IAAmC;AACxE,SAAK,SAAS,IAAIA,UAAS,WAAW,EAAE,UAAAA,WAAU,QAAQ,eAAe,UAAU,QAAQ,CAAC;AAAA,EAC9F;AAAA;AAAA,EAGA,YAAY,WAAqC;AAC/C,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,WAAO,IAAI;AAAA,EACb;AAAA;AAAA,EAGA,cAAc,MAAuB,CAAC,GAAuB;AAC3D,UAAM,QAAQ,IAAI,IAAI,IAAI,eAAe,CAAC,CAAC;AAC3C,UAAM,MAAM,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ;AAEpE,QAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,WAAO,IAAI,OAAO,CAAC,MAAM,MAAM,IAAI,EAAE,kBAAkB,cAAc,CAAC;AAAA,EACxE;AAAA;AAAA,EAGA,eAAe,WAAmB,UAAkB,SAAsC;AACxF,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,QAAI,QAAQ,IAAI,UAAU,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IACJ,WACA,KACA,MAAuB,CAAC,GACU;AAClC,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,QAAI,CAAC,IAAI,OAAO,IAAI,GAAG,EAAG,OAAM,IAAI,gBAAgB,WAAW,GAAG;AAGlE,UAAM,UAAU,SAAS,WAAW,GAAG;AACvC,UAAM,SAAS,KAAK,IAAI,OAAO;AAC/B,QAAI,OAAO,WAAW,UAAU;AAC9B,YAAMC,OAAM,IAAI,SAAS,IAAI,GAAG;AAChC,YAAM,QAAQ,eAAe,QAAQA,IAAG;AACxC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,cAAc,gBAAgB,OAAO;AAAA,QACrC,cAAc;AAAA,UACZ,EAAE,OAAO,OAAO,OAAO,QAAQ,MAAM,cAAc,gBAAgB,OAAO,IAAI,WAAW,KAAK;AAAA,QAChG;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,OAAO,IAAI,GAAG;AAGhC,UAAM,OAAO,MAAM,KAAK,SAAS,WAAW,UAAU,SAAS,IAAI,UAAU,OAAO,IAAI;AAOxF,UAAM,QAA2D,CAAC;AAElE,UAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,UAAU,QAAQ;AACxE,QAAI,WAAW;AACb,YAAM,QAAQ,MAAM,KAAK,eAAe,SAAS;AACjD,YAAM,KAAK;AAAA,QACT,OAAO;AAAA,QACP;AAAA,QACA,QAAQ,CAAC,CAAC,UAAU;AAAA,QACpB,cAAc,UAAU,iBAAiB;AAAA,MAC3C,CAAC;AAAA,IACH;AAEA,QAAI,UAAU,YAAY,UAAU,QAAQ;AAC1C,YAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,UAAU,QAAQ;AACxE,UAAI,WAAW;AACb,cAAM,KAAK;AAAA,UACT,OAAO;AAAA,UACP,OAAO,MAAM,KAAK,eAAe,SAAS;AAAA,UAC1C,QAAQ,CAAC,CAAC,UAAU;AAAA,UACpB,cAAc,UAAU,iBAAiB;AAAA,QAC3C,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,UAAU,QAAQ;AACpB,YAAM,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,UAAU,MAAM;AACpE,UAAI,SAAS;AACX,cAAM,KAAK;AAAA,UACT,OAAO;AAAA,UACP,OAAO,MAAM,KAAK,eAAe,OAAO;AAAA,QAC1C,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,SAAS,IAAI,GAAG;AAChC,UAAM,KAAK,EAAE,OAAO,WAAW,OAAO,OAAO,KAAK,CAAC;AAInD,UAAM,cAAc,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,IAAI;AACvD,UAAM,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,UAAU,QAAQ,EAAE,UAAU,MAAS,KAAK,MAAM,MAAM,SAAS,CAAC;AACxG,cAAU,YAAY;AAEtB,WAAO;AAAA,MACL,OAAO,UAAU;AAAA,MACjB,QAAQ,UAAU;AAAA,MAClB,QAAQ,CAAC,CAAC;AAAA,MACV,cAAc,aAAa;AAAA,MAC3B,cAAc;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,aACJ,WACA,MAAuB,CAAC,GACW;AACnC,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AAEnD,UAAM,SAA+C,CAAC;AACtD,eAAW,CAAC,GAAG,KAAK,IAAI,QAAQ;AAC9B,aAAO,GAAG,IAAI,MAAM,KAAK,IAAI,WAAW,KAAK,GAAG;AAAA,IAClD;AACA,WAAO,EAAE,UAAU,IAAI,UAAU,OAAO;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,aACJ,WACA,OAGI,CAAC,GAQJ;AACD,UAAM,MAAM,KAAK,OAAO,CAAC;AACzB,QAAI,WAAc,MAAM,KAAK,WAAc,WAAW,KAAK,KAAK,KAAK;AAErE,UAAM,MAAM,KAAK,UAAU,WAAW,MAAM;AAE1C,WAAK,KAAK,WAAc,WAAW,KAAK,KAAK,KAAK,EAAE,KAAK,CAAC,SAAS;AACjE,mBAAW;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA,IAAI,UAAU;AACZ,eAAO;AAAA,MACT;AAAA,MACA,IAAuB,KAAc;AACnC,eAAO,SAAS,GAAG;AAAA,MACrB;AAAA,MACA,UAAU,CAAC,YAAY,KAAK,UAAU,WAAW,OAAO;AAAA,MACxD,SAAS,YAAY;AACnB,mBAAW,MAAM,KAAK,WAAc,WAAW,KAAK,KAAK,KAAK;AAAA,MAChE;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAc,WACZ,WACA,KACA,OACY;AACZ,UAAM,UAAU,MAAM,KAAK,aAAa,WAAW,GAAG;AACtD,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,MAAM,EAAG,KAAI,CAAC,IAAI,EAAE;AAChE,WAAO,QAAQ,MAAM,GAAG,IAAK;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IACJ,WACA,KACA,OACA,MAAuB,CAAC,GACO;AAC/B,YAAQ,MAAM,KAAK,QAAQ,WAAW,EAAE,CAAC,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,GAAG;AAAA,EACnE;AAAA;AAAA,EAGA,MAAM,QACJ,WACA,OACA,MAAuB,CAAC,GACuB;AAC/C,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AAGnD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,CAAC,IAAI,OAAO,IAAI,GAAG,EAAG,OAAM,IAAI,gBAAgB,WAAW,GAAG;AAClE,YAAM,SAAS,KAAK,IAAI,SAAS,WAAW,GAAG,CAAC;AAChD,UAAI,OAAO,WAAW,SAAU,OAAM,IAAI,oBAAoB,WAAW,GAAG;AAM5E,YAAM,QAAQ,IAAI,OAAO,IAAI,GAAG;AAChC,YAAM,OAAO,MAAM,KAAK,SAAS,WAAW,UAAU,SAAS,IAAI,UAAU,OAAO,IAAI;AACxF,YAAM,QAAQ,KAAK;AAAA,QACjB,CAAC,MACC,EAAE,QAAQ,OACV,EAAE,WAAW,QACb,KAAK,UAAU,EAAE,KAAK,IAAI,KAAK,UAAU,KAAK;AAAA,MAClD;AACA,UAAI,OAAO;AACT,cAAM,IAAI,oBAAoB,WAAW,KAAK,aAAa,MAAM,KAAK,EAAE;AAAA,MAC1E;AAAA,IACF;AAEA,eAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACnD,YAAM,QAAQ,IAAI,OAAO,IAAI,GAAG;AAIhC,YAAM,SAAS,UAAU,SAAS,IAAI,UAAU,OAAO;AACvD,YAAM,cAAc,IAAI,cAAc,IAAI,GAAG;AAC7C,YAAM,SAAS,aAAa,QAAQ,OAAO,aAAa;AAExD,UAAI,cAA8B;AAClC,UAAI,YAA2B;AAC/B,UAAI,SAAS;AAEb,UAAI,CAAC,QAAQ;AACX,YAAI,aAAa;AACf,gBAAM,QAAQ,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,QAAQ;AAK/E,cAAI,KAAK,kBAAkB,KAAK,aAAa;AAC3C,kBAAM,SAAS,MAAM,KAAK,eAAe,QAAQ,OAAO;AAAA,cACtD;AAAA,cACA;AAAA,cACA,UAAU,IAAI;AAAA,YAChB,CAAC;AACD,kBAAM,KAAK,YAAY,OAAO;AAAA,cAC5B,IAAI,OAAO;AAAA,cACX;AAAA,cACA;AAAA,cACA,YAAY,OAAO;AAAA,cACnB,KAAK,OAAO;AAAA,cACZ,SAAS,OAAO;AAAA,cAChB,YAAY,OAAO;AAAA,YACrB,CAAC;AACD,wBAAY,OAAO;AACnB,qBAAS,KAAK,eAAe,OAAO,KAAK;AAAA,UAC3C,OAAO;AACL,wBAAY,MAAM,KAAK,OAAO,QAAQ,OAAO,EAAE,WAAW,IAAI,CAAC;AAC/D,qBAAS,KAAK,OAAO,OAAO,KAAK;AAAA,UACnC;AAAA,QACF,OAAO;AACL,wBAAc;AACd,mBAAS,KAAK,OAAO,OAAO,gBAAgB,QAAQ,CAAC;AAAA,QACvD;AAAA,MACF;AAEA,YAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,OAAO;AAAA,QACP,WAAW;AAAA,QACX,WAAW;AAAA,QACX,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,QACnC,YAAY,IAAI,UAAU;AAAA,MAC5B,CAAC;AAED,UAAI,KAAK,OAAO;AACd,cAAM,KAAK,MAAM,OAAO;AAAA,UACtB;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ,IAAI;AAAA,UACZ,QAAQ,SAAS,UAAU;AAAA,UAC3B,aAAa,cAAc,gBAAgB,SAAS,MAAM;AAAA,UAC1D,WAAW;AAAA,UACX,WAAW,IAAI;AAAA,QACjB,CAAC;AAAA,MACH;AAEA,UAAI,KAAK,aAAa;AACpB,YAAI;AACF,gBAAM,KAAK,YAAY,MAAM;AAAA,YAC3B;AAAA,YACA;AAAA,YACA;AAAA,YACA,QAAQ,SAAS,UAAU;AAAA,YAC3B,QAAQ;AAAA,YACR,SAAS,IAAI;AAAA,YACb,SAAS;AAAA,YACT,SAAS,SAAS,OAAO;AAAA,YACzB,WAAW;AAAA,YACX,WAAW,IAAI;AAAA,UACjB,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,WAAK,WAAW;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,SAAS,UAAU;AAAA,QAC3B,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC7B,CAAC;AAAA,IACH;AAGA,UAAM,MAA4C,CAAC;AACnD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,GAAG,IAAI,MAAM,KAAK,IAAI,WAAW,KAAK,GAAG;AAAA,IAC/C;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,UACJ,WACA,UACA,SACA,MAAuB,CAAC,GACO;AAC/B,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,UAAM,UAAU,IAAI,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS,qCAAqC,QAAQ,SAAS,SAAS;AAAA,MAC1E;AAAA,IACF;AACA,UAAM,SAAkC,CAAC;AACzC,eAAW,CAAC,GAAG,KAAK,IAAI,QAAQ;AAC9B,aAAO,GAAG,KAAK,MAAM,KAAK,IAAI,WAAW,KAAK,GAAG,GAAG;AAAA,IACtD;AACA,QAAI;AACF,aAAO,MAAM,QAAQ,EAAE,WAAW,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACpE,SAAS,KAAU;AACjB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS,KAAK,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,SAAS,WAAmB,QAA+C;AACvF,QAAI,KAAK,QAAQ;AACf,YAAM,QAAiC,EAAE,UAAU;AACnD,UAAI,WAAW,KAAM,OAAM,UAAU;AAMrC,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,KAAK,YAAY;AAAA,QACnD;AAAA,QACA,mBAAmB;AAAA,MACrB,CAAQ;AACR,aAAO,KAAK,IAAI,CAAC,OAAO;AAAA,QACtB,WAAW,EAAE;AAAA,QACb,KAAK,EAAE;AAAA,QACP,OAAO,EAAE;AAAA,QACT,SAAS,EAAE,WAAW;AAAA,QACtB,OAAO,EAAE,SAAS;AAAA,QAClB,WAAW,EAAE,aAAa;AAAA,QAC1B,WAAW,QAAQ,EAAE,SAAS;AAAA,QAC9B,QAAQ,QAAQ,EAAE,MAAM;AAAA,QACxB,eAAe,EAAE,iBAAiB;AAAA,QAClC,YAAY,EAAE;AAAA,QACd,YAAY,EAAE,cAAc;AAAA,MAC9B,EAAE;AAAA,IACJ;AACA,WAAO,KAAK,OAAO;AAAA,MACjB,CAAC,MACC,EAAE,cAAc,cACf,WAAW,QAAQ,EAAE,YAAY,UAAU,EAAE,UAAU,YAAY,EAAE,UAAU;AAAA,IACpF;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,KAAiC;AACvD,QAAI,KAAK,QAAQ;AACf,YAAM,QAAiC;AAAA,QACrC,WAAW,IAAI;AAAA,QACf,KAAK,IAAI;AAAA,QACT,OAAO,IAAI;AAAA,QACX,SAAS,IAAI,WAAW;AAAA,MAC1B;AAIA,YAAM,SAAS,IAAI,UAAU,WAAW,EAAE,mBAAmB,KAAK,IAAI,CAAC;AACvE,YAAM,WAAW,MAAM,KAAK,OAAO,KAAK,KAAK,YAAY;AAAA,QACvD;AAAA,QACA,OAAO;AAAA,QACP,GAAG;AAAA,MACL,CAAQ;AACR,UAAI,SAAS,CAAC,GAAG;AACf,cAAM,KAAK,OAAO,OAAO,KAAK,YAAY;AAAA,UACxC;AAAA,UACA,MAAM,EAAE,GAAG,IAAI;AAAA,UACf,GAAG;AAAA,QACL,CAAQ;AAAA,MACV,OAAO;AACL,cAAM,KAAK,OAAO,OAAO,KAAK,YAAY,EAAE,GAAG,IAAI,GAAG,MAAa;AAAA,MACrE;AACA;AAAA,IACF;AACA,UAAM,MAAM,KAAK,OAAO;AAAA,MACtB,CAAC,MACC,EAAE,cAAc,IAAI,aACpB,EAAE,QAAQ,IAAI,OACd,EAAE,UAAU,IAAI,UACf,EAAE,WAAW,WAAW,IAAI,WAAW;AAAA,IAC5C;AACA,QAAI,OAAO,EAAG,MAAK,OAAO,GAAG,IAAI;AAAA,QAC5B,MAAK,OAAO,KAAK,GAAG;AAAA,EAC3B;AAAA,EAEA,MAAc,eAAe,KAAoC;AAC/D,QAAI,IAAI,WAAW;AACjB,UAAI,CAAC,IAAI,UAAW,QAAO;AAC3B,UAAI;AAKJ,UACE,KAAK,kBACL,KAAK,eACL,OAAO,IAAI,cAAc,YACzB,IAAI,UAAU,WAAW,MAAM,GAC/B;AACA,cAAM,SAAS,MAAM,KAAK,YAAY,IAAI,IAAI,SAAS;AACvD,YAAI,CAAC,OAAQ,QAAO;AACpB,gBAAQ,MAAM,KAAK,eAAe;AAAA,UAChC;AAAA,YACE,IAAI,OAAO;AAAA,YACX,UAAU,OAAO;AAAA,YACjB,KAAK,OAAO;AAAA,YACZ,SAAS,OAAO;AAAA,YAChB,YAAY,OAAO;AAAA,UACrB;AAAA,UACA,EAAE,WAAW,IAAI,WAAW,KAAK,IAAI,IAAI;AAAA,QAC3C;AAAA,MACF,OAAO;AACL,gBAAQ,MAAM,KAAK,OAAO,QAAQ,IAAI,WAAW;AAAA,UAC/C,WAAW,IAAI;AAAA,UACf,KAAK,IAAI;AAAA,QACX,CAAC;AAAA,MACH;AAEA,UAAI;AACF,eAAO,KAAK,MAAM,KAAK;AAAA,MACzB,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,IAAI,SAAS;AAAA,EACtB;AACF;AAOA,SAAS,gBAAgB,OAAwB;AAC/C,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK;AAC5E,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,MAAM,IAAI,eAAe,EAAE,KAAK,GAAG,IAAI;AAC9E,QAAM,MAAM;AACZ,QAAM,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK;AACnC,SAAO,MAAM,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,IAAI,MAAM,gBAAgB,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI;AAC9F;AAGA,SAAS,eAAe,KAAa,MAAwB;AAC3D,MAAI,OAAO,SAAS,UAAW,QAAO,QAAQ,UAAU,QAAQ,OAAO,QAAQ;AAC/E,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,IAAI,OAAO,GAAG;AACpB,WAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,EAClC;AACA,MAAI,MAAM,QAAQ,IAAI,KAAM,QAAQ,OAAO,SAAS,UAAW;AAC7D,QAAI;AACF,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC1sBA,yBAA0E;AA8BnE,IAAM,yBAAN,MAAwD;AAAA,EAG7D,YAAY,OAAyB,CAAC,GAAG;AACvC,SAAK,MAAM,KAAK,WAAO,gCAAY,EAAE;AAAA,EACvC;AAAA,EAEA,MAAM,QAAQ,OAAe,KAA2C;AACtE,UAAM,SAAK,gCAAY,EAAE;AACzB,UAAM,aAAS,mCAAe,eAAe,KAAK,KAAK,EAAE;AACzD,WAAO,OAAO,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM,CAAC;AAClD,UAAM,MAAM,OAAO,OAAO,CAAC,OAAO,OAAO,OAAO,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;AACxE,UAAM,MAAM,OAAO,WAAW;AAC9B,UAAM,OAAO,OAAO,OAAO,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,SAAS,QAAQ;AAC5D,WAAO;AAAA,MACL,IAAI,aAAS,gCAAY,EAAE,EAAE,SAAS,KAAK;AAAA,MAC3C,UAAU;AAAA,MACV,KAAK;AAAA,MACL,SAAS;AAAA,MACT,YAAY;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,QAAsB,KAAqC;AACvE,UAAM,MAAM,OAAO,KAAK,OAAO,YAAY,QAAQ;AACnD,UAAM,KAAK,IAAI,SAAS,GAAG,EAAE;AAC7B,UAAM,MAAM,IAAI,SAAS,IAAI,EAAE;AAC/B,UAAM,OAAO,IAAI,SAAS,EAAE;AAC5B,UAAM,eAAW,qCAAiB,eAAe,KAAK,KAAK,EAAE;AAC7D,aAAS,OAAO,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM,CAAC;AACpD,aAAS,WAAW,GAAG;AACvB,WAAO,OAAO,OAAO,CAAC,SAAS,OAAO,IAAI,GAAG,SAAS,MAAM,CAAC,CAAC,EAAE,SAAS,MAAM;AAAA,EACjF;AAAA,EAEA,MAAM,UAAU,QAAsB,KAA2C;AAC/E,UAAM,QAAQ,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC5C,UAAM,OAAO,MAAM,KAAK,QAAQ,OAAO,GAAG;AAC1C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,IAAI,OAAO;AAAA,MACX,UAAU,oBAAoB,OAAO,UAAU,CAAC;AAAA,MAChD,SAAS,OAAO,UAAU;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,OAAO,OAAuB;AAC5B,WAAO,gBAAY,+BAAW,QAAQ,EAAE,OAAO,OAAO,MAAM,EAAE,OAAO,KAAK;AAAA,EAC5E;AAAA,EAEQ,MAAM,KAA4B;AAMxC,WAAO,CAAC,IAAI,WAAW,IAAI,GAAG,EAAE,KAAK,GAAG;AAAA,EAC1C;AACF;;;AC1DA,IAAM,iBAAiB,CAAC,QAAuC;AAC7D,QAAM,SAAS,CAAC,SAAqC;AACnD,UAAM,IAAI,IAAI,UAAU,IAAI;AAC5B,WAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI;AAAA,EACnC;AACA,QAAM,QAAQ,OAAO,eAAe;AACpC,SAAO;AAAA,IACL,QAAQ,OAAO,WAAW;AAAA,IAC1B,UAAU,OAAO,aAAa;AAAA,IAC9B,aAAa,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IAAI;AAAA,IAC7E,WAAW,OAAO,cAAc;AAAA,EAClC;AACF;AAEA,SAAS,UAAU,KAAoB,QAAgB,MAAc,SAAiB,OAAiC;AACrH,MAAI,OAAO,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;AAChE;AAEO,SAAS,uBACd,MACA,SACA,OAA8B,CAAC,GACzB;AACN,QAAM,OAAO,KAAK,YAAY;AAC9B,QAAM,QAAQ,KAAK,sBAAsB;AAEzC,OAAK,IAAI,OAAO,OAAO,KAAK,QAAQ;AAClC,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,YAAY,QAAQ,cAAc,GAAG;AAC3C,YAAM,IAAI,KAAK,EAAE,UAAU,CAAC;AAAA,IAC9B,SAAS,KAAU;AACjB,gBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,0BAA0B;AAAA,IAC5E;AAAA,EACF,EAAyB;AAEzB,OAAK,IAAI,GAAG,IAAI,gBAAgB,OAAO,KAAK,QAAQ;AAClD,UAAM,KAAK,IAAI,OAAO;AACtB,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,UAAU,MAAM,QAAQ,aAAa,IAAI,GAAG;AAClD,YAAM,IAAI,KAAK,OAAO;AAAA,IACxB,SAAS,KAAU;AACjB,UAAI,eAAe,uBAAuB;AACxC,kBAAU,KAAK,KAAK,qBAAqB,IAAI,OAAO;AAAA,MACtD,OAAO;AACL,kBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,0BAA0B;AAAA,MAC5E;AAAA,IACF;AAAA,EACF,EAAyB;AAEzB,OAAK,IAAI,GAAG,IAAI,gBAAgB,OAAO,KAAK,QAAQ;AAClD,UAAM,KAAK,IAAI,OAAO;AACtB,UAAM,OAAQ,IAAI,QAAQ,CAAC;AAC3B,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,MAAM,GAAG;AAClD,YAAM,IAAI,KAAK,EAAE,QAAQ,OAAO,CAAC;AAAA,IACnC,SAAS,KAAU;AACjB,UAAI,eAAe,qBAAqB;AACtC,kBAAU,KAAK,KAAK,mBAAmB,IAAI,SAAS;AAAA,UAClD,WAAW,IAAI;AAAA,UACf,KAAK,IAAI;AAAA,UACT,QAAQ,IAAI;AAAA,QACd,CAAC;AAAA,MACH,WAAW,eAAe,uBAAuB;AAC/C,kBAAU,KAAK,KAAK,qBAAqB,IAAI,OAAO;AAAA,MACtD,WAAW,eAAe,iBAAiB;AACzC,kBAAU,KAAK,KAAK,eAAe,IAAI,SAAS,EAAE,WAAW,IAAI,WAAW,KAAK,IAAI,IAAI,CAAC;AAAA,MAC5F,OAAO;AACL,kBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,2BAA2B;AAAA,MAC7E;AAAA,IACF;AAAA,EACF,EAAyB;AAEzB,OAAK,KAAK,GAAG,IAAI,0BAA0B,OAAO,KAAK,QAAQ;AAC7D,UAAM,EAAE,WAAW,SAAS,IAAI,IAAI;AACpC,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,SAAS,MAAM,QAAQ,UAAU,WAAW,UAAU,IAAI,MAAM,GAAG;AACzE,YAAM,SAAS,OAAO,KAAK,MAAM;AACjC,YAAM,IAAI,OAAO,MAAM,EAAE,KAAK,MAAM;AAAA,IACtC,SAAS,KAAU;AACjB,UAAI,eAAe,uBAAuB;AACxC,kBAAU,KAAK,KAAK,qBAAqB,IAAI,OAAO;AAAA,MACtD,OAAO;AACL,kBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,eAAe;AAAA,MACjE;AAAA,IACF;AAAA,EACF,EAAyB;AAC3B;;;AC5HA,oBAAuD;AAEhD,IAAM,qBAAqB;AAC3B,IAAM,0BAA0B;AAGhC,IAAM,kBAAyB,CAAC,0BAAY,yBAAW,6BAAe;AAGtE,IAAM,+BAA+B;AAAA,EAC1C,IAAI;AAAA,EACJ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aACE;AACJ;;;ACVA,IAAM,WAAW;AAAA,EACf,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,IACV;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAY,OAAO;AAAA,MAAY,UAAU;AAAA,MAC5D,aAAa;AAAA,IAAkD;AAAA,IAEjE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAY,OAAO;AAAA,MAAY,UAAU;AAAA,MAAM,SAAS;AAAA,MAC7E,SAAS;AAAA,QACP,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,QAC/B,EAAE,OAAO,YAAY,OAAO,WAAW;AAAA,QACvC,EAAE,OAAO,OAAO,OAAO,aAAa;AAAA,QACpC,EAAE,OAAO,YAAY,OAAO,WAAW;AAAA,MACzC;AAAA,IACF;AAAA,IAEA,EAAE,MAAM,SAAS,IAAI,QAAQ,OAAO,QAAQ,UAAU,OAAO,SAAS,8BAA8B;AAAA,IACpG;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAQ,UAAU;AAAA,MACzD,aAAa;AAAA,MAA6B,SAAS;AAAA,IAA8B;AAAA,IACnF;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAa,OAAO;AAAA,MAAQ,UAAU;AAAA,MAAO,SAAS;AAAA,MAC3E,KAAK;AAAA,MAAG,KAAK;AAAA,MAAO,SAAS;AAAA,IAA8B;AAAA,IAC7D;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAe,OAAO;AAAA,MAAW,UAAU;AAAA,MAAO,SAAS;AAAA,MAChF,SAAS;AAAA,IAA8B;AAAA,IACzC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAY,UAAU;AAAA,MAC7D,SAAS;AAAA,IAA8B;AAAA,IACzC;AAAA,MAAE,MAAM;AAAA,MAAY,KAAK;AAAA,MAAiB,OAAO;AAAA,MAAY,UAAU;AAAA,MACrE,SAAS;AAAA,IAA8B;AAAA,IAEzC,EAAE,MAAM,SAAS,IAAI,WAAW,OAAO,WAAW,UAAU,OAAO,SAAS,8BAA8B;AAAA,IAC1G;AAAA,MAAE,MAAM;AAAA,MAAY,KAAK;AAAA,MAAW,OAAO;AAAA,MAAW,UAAU;AAAA,MAAM,WAAW;AAAA,MAC/E,SAAS;AAAA,IAA8B;AAAA,IAEzC,EAAE,MAAM,SAAS,IAAI,gBAAgB,OAAO,gBAAgB,UAAU,MAAM;AAAA,IAC5E;AAAA,MAAE,MAAM;AAAA,MAAS,KAAK;AAAA,MAAc,OAAO;AAAA,MAAc,UAAU;AAAA,MACjE,aAAa;AAAA,IAAgC;AAAA,IAC/C,EAAE,MAAM,QAAQ,KAAK,aAAa,OAAO,aAAa,UAAU,OAAO,SAAS,cAAc;AAAA,IAE9F;AAAA,MAAE,MAAM;AAAA,MAAiB,IAAI;AAAA,MAAQ,OAAO;AAAA,MAAmB,UAAU;AAAA,MAAO,MAAM;AAAA,MACpF,SAAS,EAAE,MAAM,QAAQ,QAAQ,QAAQ,KAAK,0BAA0B;AAAA,IAAE;AAAA,EAC9E;AACF;AAGO,IAAM,uBAAuB;AAG7B,IAAM,wBAA+C,OAAO,EAAE,OAAO,MAAM;AAChF,QAAM,WAAW,OAAO,OAAO,YAAY,MAAM;AACjD,QAAM,YAAY,OAAO;AACzB,MAAI,CAAC,WAAW;AACd,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,2CAA2C;AAAA,EAC7F;AACA,MAAI,aAAa,UAAU,CAAC,OAAO,WAAW;AAC5C,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,yBAAyB;AAAA,EAC3E;AACA,MAAI,aAAa,UAAU,CAAC,OAAO,SAAS;AAC1C,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,uBAAuB;AAAA,EACzE;AACA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,SAAS,uCAAuC,QAAQ;AAAA,EAC1D;AACF;;;AC5EO,IAAM,2BAA6C;AAAA,EACxD,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,IACV,EAAE,MAAM,SAAS,IAAI,YAAY,OAAO,YAAY,UAAU,MAAM;AAAA,IACpE;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAkB,OAAO;AAAA,MAAkB,UAAU;AAAA,MACxE,SAAS;AAAA,MAAe,WAAW;AAAA,MAAG,WAAW;AAAA,IAAG;AAAA,IACtD;AAAA,MAAE,MAAM;AAAA,MAAS,KAAK;AAAA,MAAiB,OAAO;AAAA,MAAiB,UAAU;AAAA,MACvE,aAAa;AAAA,IAA+B;AAAA,IAE9C,EAAE,MAAM,SAAS,IAAI,cAAc,OAAO,cAAc,UAAU,MAAM;AAAA,IACxE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAc,OAAO;AAAA,MAAiB,UAAU;AAAA,MAAO,SAAS;AAAA,MACrF,SAAS;AAAA,QACP,EAAE,OAAO,SAAS,OAAO,QAAQ;AAAA,QACjC,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,QAC/B,EAAE,OAAO,UAAU,OAAO,eAAe;AAAA,MAC3C;AAAA,IACF;AAAA,IACA,EAAE,MAAM,SAAS,KAAK,gBAAgB,OAAO,iBAAiB,UAAU,OAAO,SAAS,UAAU;AAAA,IAClG;AAAA,MAAE,MAAM;AAAA,MAAO,KAAK;AAAA,MAAY,OAAO;AAAA,MAAY,UAAU;AAAA,MAC3D,aAAa;AAAA,IAA8B;AAAA,EAC/C;AACF;;;AC9BO,IAAM,+BAAiD;AAAA,EAC5D,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,MAAM;AAAA,EACN,YAAY;AAAA,IACV;AAAA,MAAE,MAAM;AAAA,MAAe,IAAI;AAAA,MAAe,OAAO;AAAA,MAAY,UAAU;AAAA,MACrE,YACE;AAAA,MACF,gBAAgB;AAAA,IAAU;AAAA,IAE5B,EAAE,MAAM,SAAS,IAAI,gBAAgB,OAAO,gBAAgB,UAAU,MAAM;AAAA,IAC5E;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAc,OAAO;AAAA,MAAgB,UAAU;AAAA,MAAO,SAAS;AAAA,MACpF,aAAa;AAAA,IAAyC;AAAA,IACxD,EAAE,MAAM,UAAU,KAAK,oBAAoB,OAAO,oBAAoB,UAAU,OAAO,SAAS,MAAM;AAAA,IAEtG,EAAE,MAAM,SAAS,IAAI,iBAAiB,OAAO,iBAAiB,UAAU,MAAM;AAAA,IAC9E,EAAE,MAAM,UAAU,KAAK,oBAAoB,OAAO,oBAAoB,UAAU,OAAO,SAAS,MAAM;AAAA,IACtG,EAAE,MAAM,UAAU,KAAK,mBAAmB,OAAO,mBAAmB,UAAU,OAAO,SAAS,KAAK;AAAA,EACrG;AACF;;;ACvBA,IAAMC,YAAW;AAAA,EACf,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aACE;AAAA,EAIF,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,IACV;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAW,OAAO;AAAA,MAAW,UAAU;AAAA,MAC1D,aAAa;AAAA,IAA0C;AAAA,IACzD;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAW,OAAO;AAAA,MAAW,UAAU;AAAA,MAAM,SAAS;AAAA,MAC3E,SAAS;AAAA,QACP,EAAE,OAAO,SAAS,OAAO,mBAAmB;AAAA,QAC5C,EAAE,OAAO,MAAM,OAAO,qBAAqB;AAAA,MAC7C;AAAA,IACF;AAAA,IAEA;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAS,OAAO;AAAA,MAAS,UAAU;AAAA,MACtD,SAAS;AAAA,IAA8B;AAAA,IACzC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAc,OAAO;AAAA,MAAkB,UAAU;AAAA,MACpE,SAAS;AAAA,MACT,aAAa;AAAA,MACb,SAAS;AAAA,IAA8B;AAAA,IAEzC;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAM,OAAO;AAAA,MAAM,UAAU;AAAA,MAChD,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAU,UAAU;AAAA,MAC3D,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAU,UAAU;AAAA,MAC3D,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAe,OAAO;AAAA,MAAY,UAAU;AAAA,MAC/D,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAoB,OAAO;AAAA,MAAiB,UAAU;AAAA,MACzE,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAY,KAAK;AAAA,MAAwB,OAAO;AAAA,MACtD,UAAU;AAAA,MAAM,WAAW;AAAA,MAC3B,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAuB,OAAO;AAAA,MACnD,UAAU;AAAA,MAAO,SAAS;AAAA,MAC1B,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IAEtC,EAAE,MAAM,SAAS,IAAI,UAAU,OAAO,UAAU,UAAU,MAAM;AAAA,IAChE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAiB,OAAO;AAAA,MAC7C,UAAU;AAAA,MAAO,SAAS;AAAA,MAAM,KAAK;AAAA,MAAI,KAAK;AAAA,IAAO;AAAA,IACvD;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3C,UAAU;AAAA,MAAO,SAAS;AAAA,MAAO,KAAK;AAAA,MAAK,KAAK;AAAA,MAChD,aAAa;AAAA,IAAqD;AAAA,IACpE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAiB,OAAO;AAAA,MAC7C,UAAU;AAAA,MAAO,SAAS;AAAA,MAAK,KAAK;AAAA,MAAG,KAAK;AAAA,IAAM;AAAA,IAEpD;AAAA,MAAE,MAAM;AAAA,MAAiB,IAAI;AAAA,MAAQ,OAAO;AAAA,MAC1C,UAAU;AAAA,MAAO,MAAM;AAAA,MACvB,SAAS,EAAE,MAAM,QAAQ,QAAQ,QAAQ,KAAK,6BAA6B;AAAA,IAAE;AAAA,EACjF;AACF;AAGO,IAAM,0BAA0BA;AAUhC,IAAM,2BAAkD,OAAO,EAAE,OAAO,MAAM;AACnF,QAAM,UAAU,OAAO,OAAO,WAAW,OAAO;AAChD,MAAI,YAAY,SAAS;AACvB,UAAM,OAAO,OAAO;AACpB,QAAI,CAAC,MAAM;AACT,aAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,6CAA6C;AAAA,IAC/F;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,SAAS,kCAAkC,IAAI;AAAA,IACjD;AAAA,EACF;AACA,MAAI,YAAY,MAAM;AACpB,UAAM,UAAoB,CAAC;AAC3B,QAAI,CAAC,OAAO,UAAW,SAAQ,KAAK,WAAW;AAC/C,QAAI,CAAC,OAAO,UAAW,SAAQ,KAAK,WAAW;AAC/C,QAAI,CAAC,OAAO,iBAAkB,SAAQ,KAAK,kBAAkB;AAC7D,QAAI,CAAC,OAAO,qBAAsB,SAAQ,KAAK,sBAAsB;AACrE,QAAI,QAAQ,QAAQ;AAClB,aAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,yBAAyB,QAAQ,SAAS,IAAI,MAAM,EAAE,KAAK,QAAQ,KAAK,IAAI,CAAC,GAAG;AAAA,IAClI;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,SAAS,iCAAiC,OAAO,SAAS,YAAY,OAAO,SAAS;AAAA,IACxF;AAAA,EACF;AACA,SAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,oBAAoB,OAAO,GAAG;AAChF;;;ACvGO,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACRO,IAAM,KAAsB;AAAA,EACjC,gBAAgB;AAAA,IACd,cAAc;AAAA,MACZ,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,YAAY,aAAa,kDAAkD;AAAA,QAC9F,MAAM,EAAE,OAAO,OAAO;AAAA,QACtB,SAAS,EAAE,OAAO,UAAU;AAAA,QAC5B,cAAc,EAAE,OAAO,eAAe;AAAA,MACxC;AAAA,MACA,MAAM;AAAA,QACJ,UAAU;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,YACV,KAAK;AAAA,YACL,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,QACA,WAAW,EAAE,OAAO,QAAQ,MAAM,4BAA4B;AAAA,QAC9D,WAAW,EAAE,OAAO,OAAO;AAAA,QAC3B,aAAa,EAAE,OAAO,UAAU;AAAA,QAChC,WAAW,EAAE,OAAO,WAAW;AAAA,QAC/B,eAAe,EAAE,OAAO,WAAW;AAAA,QACnC,SAAS,EAAE,OAAO,UAAU;AAAA,QAC5B,YAAY,EAAE,OAAO,cAAc,MAAM,gCAAgC;AAAA,QACzE,WAAW,EAAE,OAAO,YAAY;AAAA,MAClC;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,kBAAkB;AAAA,MACnC;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,WAAW;AAAA,QAC9B,YAAY,EAAE,OAAO,aAAa;AAAA,MACpC;AAAA,MACA,MAAM;AAAA,QACJ,gBAAgB,EAAE,OAAO,iBAAiB;AAAA,QAC1C,eAAe,EAAE,OAAO,iBAAiB,MAAM,+BAA+B;AAAA,QAC9E,YAAY;AAAA,UACV,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,SAAS,MAAM,QAAQ,QAAQ,eAAe;AAAA,QAClE;AAAA,QACA,cAAc,EAAE,OAAO,gBAAgB;AAAA,QACvC,UAAU,EAAE,OAAO,YAAY,MAAM,mCAA8B;AAAA,MACrE;AAAA,IACF;AAAA,IAEA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,cAAc,EAAE,OAAO,eAAe;AAAA,QACtC,eAAe,EAAE,OAAO,gBAAgB;AAAA,MAC1C;AAAA,MACA,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA,kBAAkB,EAAE,OAAO,mBAAmB;AAAA,QAC9C,kBAAkB,EAAE,OAAO,mBAAmB;AAAA,QAC9C,iBAAiB,EAAE,OAAO,kBAAkB;AAAA,MAC9C;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,MACP,OAAO;AAAA,MACP,aACE;AAAA,MAIF,QAAQ;AAAA,QACN,SAAS,EAAE,OAAO,WAAW,aAAa,0CAA0C;AAAA,QACpF,OAAO,EAAE,OAAO,QAAQ;AAAA,QACxB,IAAI,EAAE,OAAO,KAAK;AAAA,QAClB,QAAQ,EAAE,OAAO,SAAS;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,oBAAoB,IAAI,qBAAqB;AAAA,QACjE;AAAA,QACA,YAAY;AAAA,UAAE,OAAO;AAAA,UACnB,MAAM;AAAA,QAA4F;AAAA,QACpG,WAAW;AAAA,UAAE,OAAO;AAAA,UAClB,MAAM;AAAA,QAAqG;AAAA,QAC7G,WAAW,EAAE,OAAO,UAAU,MAAM,qBAAqB;AAAA,QACzD,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAA2F;AAAA,QACnG,kBAAkB,EAAE,OAAO,gBAAgB;AAAA,QAC3C,sBAAsB,EAAE,OAAO,oBAAoB;AAAA,QACnD,qBAAqB;AAAA,UAAE,OAAO;AAAA,UAC5B,MAAM;AAAA,QAAyE;AAAA,QACjF,eAAe,EAAE,OAAO,8BAA8B;AAAA,QACtD,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAqD;AAAA,QAC7D,eAAe,EAAE,OAAO,uBAAuB;AAAA,MACjD;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,kBAAkB;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACF;;;AC5HO,IAAM,OAAwB;AAAA,EACnC,gBAAgB;AAAA,IACd,cAAc;AAAA,MACZ,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,sBAAO,aAAa,iFAAgB;AAAA,QACvD,MAAM,EAAE,OAAO,OAAO;AAAA,QACtB,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,cAAc,EAAE,OAAO,2BAAO;AAAA,MAChC;AAAA,MACA,MAAM;AAAA,QACJ,UAAU;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,YACV,KAAK;AAAA,YACL,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,QACA,WAAW,EAAE,OAAO,gBAAM,MAAM,gCAAsB;AAAA,QACtD,WAAW,EAAE,OAAO,eAAK;AAAA,QACzB,aAAa,EAAE,OAAO,mBAAS;AAAA,QAC/B,WAAW,EAAE,OAAO,qBAAM;AAAA,QAC1B,eAAe,EAAE,OAAO,eAAK;AAAA,QAC7B,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,YAAY,EAAE,OAAO,4BAAQ,MAAM,oCAA0B;AAAA,QAC7D,WAAW,EAAE,OAAO,iCAAQ;AAAA,MAC9B;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,uCAAS;AAAA,MAC1B;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,eAAK;AAAA,QACxB,YAAY,EAAE,OAAO,eAAK;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,QACJ,gBAAgB,EAAE,OAAO,iCAAQ;AAAA,QACjC,eAAe,EAAE,OAAO,4BAAQ,MAAM,mCAAyB;AAAA,QAC/D,YAAY;AAAA,UACV,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,gBAAM,MAAM,gBAAM,QAAQ,2BAAO;AAAA,QACrD;AAAA,QACA,cAAc,EAAE,OAAO,qBAAM;AAAA,QAC7B,UAAU,EAAE,OAAO,qBAAW,MAAM,uCAAwB;AAAA,MAC9D;AAAA,IACF;AAAA,IAEA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,cAAc,EAAE,OAAO,qBAAM;AAAA,QAC7B,eAAe,EAAE,OAAO,eAAK;AAAA,MAC/B;AAAA,MACA,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA,kBAAkB,EAAE,OAAO,2BAAO;AAAA,QAClC,kBAAkB,EAAE,OAAO,2BAAO;AAAA,QAClC,iBAAiB,EAAE,OAAO,2BAAO;AAAA,MACnC;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,MACP,OAAO;AAAA,MACP,aACE;AAAA,MAEF,QAAQ;AAAA,QACN,SAAS,EAAE,OAAO,4BAAQ,aAAa,2EAAe;AAAA,QACtD,OAAO,EAAE,OAAO,eAAK;AAAA,QACrB,IAAI,EAAE,OAAO,KAAK;AAAA,QAClB,QAAQ,EAAE,OAAO,eAAK;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,wCAAU,IAAI,uBAAa;AAAA,QAC/C;AAAA,QACA,YAAY;AAAA,UAAE,OAAO;AAAA,UACnB,MAAM;AAAA,QAAgC;AAAA,QACxC,WAAW;AAAA,UAAE,OAAO;AAAA,UAClB,MAAM;AAAA,QAAyD;AAAA,QACjE,WAAW,EAAE,OAAO,gBAAM,MAAM,yBAAe;AAAA,QAC/C,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAoD;AAAA,QAC5D,kBAAkB,EAAE,OAAO,gBAAgB;AAAA,QAC3C,sBAAsB,EAAE,OAAO,oBAAoB;AAAA,QACnD,qBAAqB;AAAA,UAAE,OAAO;AAAA,UAC5B,MAAM;AAAA,QAAoC;AAAA,QAC5C,eAAe,EAAE,OAAO,oDAAiB;AAAA,QACzC,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAkB;AAAA,QAC1B,eAAe,EAAE,OAAO,iDAAc;AAAA,MACxC;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,2BAAO;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACF;;;ACtHO,IAAM,OAAwB;AAAA,EACnC,gBAAgB;AAAA,IACd,cAAc;AAAA,MACZ,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,wCAAU,aAAa,iIAAwB;AAAA,QAClE,MAAM,EAAE,OAAO,OAAO;AAAA,QACtB,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,cAAc,EAAE,OAAO,6CAAU;AAAA,MACnC;AAAA,MACA,MAAM;AAAA,QACJ,UAAU;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,YACV,KAAK;AAAA,YACL,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,QACA,WAAW,EAAE,OAAO,sBAAO,MAAM,2BAAsB;AAAA,QACvD,WAAW,EAAE,OAAO,qBAAM;AAAA,QAC1B,aAAa,EAAE,OAAO,yBAAU;AAAA,QAChC,WAAW,EAAE,OAAO,iCAAQ;AAAA,QAC5B,eAAe,EAAE,OAAO,iCAAQ;AAAA,QAChC,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,YAAY,EAAE,OAAO,8CAAW,MAAM,+BAA0B;AAAA,QAChE,WAAW,EAAE,OAAO,2BAAO;AAAA,MAC7B;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,mDAAW;AAAA,MAC5B;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,mDAAW;AAAA,QAC9B,YAAY,EAAE,OAAO,eAAK;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,QACJ,gBAAgB,EAAE,OAAO,mDAAW;AAAA,QACpC,eAAe,EAAE,OAAO,8CAAW,MAAM,8BAAyB;AAAA,QAClE,YAAY;AAAA,UACV,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,sBAAO,MAAM,sBAAO,QAAQ,6CAAU;AAAA,QAC1D;AAAA,QACA,cAAc,EAAE,OAAO,mDAAW;AAAA,QAClC,UAAU,EAAE,OAAO,oBAAU,MAAM,kCAAwB;AAAA,MAC7D;AAAA,IACF;AAAA,IAEA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,cAAc,EAAE,OAAO,qBAAM;AAAA,QAC7B,eAAe,EAAE,OAAO,mDAAW;AAAA,MACrC;AAAA,MACA,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA,kBAAkB,EAAE,OAAO,qEAAc;AAAA,QACzC,kBAAkB,EAAE,OAAO,+DAAa;AAAA,QACxC,iBAAiB,EAAE,OAAO,yDAAY;AAAA,MACxC;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,MACP,OAAO;AAAA,MACP,aACE;AAAA,MAEF,QAAQ;AAAA,QACN,SAAS,EAAE,OAAO,wCAAU,aAAa,iIAAwB;AAAA,QACjE,OAAO,EAAE,OAAO,2BAAO;AAAA,QACvB,IAAI,EAAE,OAAO,KAAK;AAAA,QAClB,QAAQ,EAAE,OAAO,eAAK;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,4EAAgB,IAAI,uBAAa;AAAA,QACrD;AAAA,QACA,YAAY;AAAA,UAAE,OAAO;AAAA,UACnB,MAAM;AAAA,QAA+C;AAAA,QACvD,WAAW;AAAA,UAAE,OAAO;AAAA,UAClB,MAAM;AAAA,QAAoE;AAAA,QAC5E,WAAW,EAAE,OAAO,kCAAS,MAAM,oBAAe;AAAA,QAClD,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAA6D;AAAA,QACrE,kBAAkB,EAAE,OAAO,0CAAY;AAAA,QACvC,sBAAsB,EAAE,OAAO,2EAAe;AAAA,QAC9C,qBAAqB;AAAA,UAAE,OAAO;AAAA,UAC5B,MAAM;AAAA,QAA0C;AAAA,QAClD,eAAe,EAAE,OAAO,uEAAqB;AAAA,QAC7C,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAqB;AAAA,QAC7B,eAAe,EAAE,OAAO,0EAAmB;AAAA,MAC7C;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,iCAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACF;;;ACzGO,IAAM,8BAAiD;AAAA,EAC5D;AAAA,EACA,SAAS;AAAA,EACT,SAAS;AACX;;;AC2CO,IAAM,wBAAN,MAA8C;AAAA,EAQnD,YAAY,OAAqC,CAAC,GAAG;AAPrD,gBAAO;AACP,mBAAU;AACV,gBAAO;AAGP,SAAQ,UAAkC;AAGxC,SAAK,OAAO;AAAA,MACV,GAAG;AAAA,MACH,WAAW,KAAK,aAAa;AAAA,MAC7B,gBAAgB,KAAK,kBAAkB;AAAA,QACrC,MAAM,EAAE,MAAM,sBAAsB;AAAA,QACpC,SAAS,EAAE,MAAM,yBAAyB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,SAAK,UAAU,IAAI,gBAAgB;AAAA,MACjC,QAAQ,KAAK,KAAK;AAAA,MAClB,KAAK,KAAK,KAAK;AAAA,IACjB,CAAC;AACD,eAAW,KAAK,KAAK,KAAK,aAAa,CAAC,EAAG,MAAK,QAAQ,iBAAiB,CAAC;AAC1E,eAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,KAAK,KAAK,kBAAkB,CAAC,CAAC,GAAG;AAC3E,iBAAW,CAAC,IAAI,EAAE,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC/C,aAAK,QAAQ,eAAe,IAAI,IAAI,EAAE;AAAA,MACxC;AAAA,IACF;AAEA,QAAI,gBAAgB,YAAY,KAAK,OAAO;AAC5C,QAAI,QAAQ;AAAA,MACV,gDAAgD,KAAK,KAAK,WAAW,UAAU,CAAC;AAAA,IAClF;AAGA,QAAI;AACF,UAAI,WAAuC,UAAU,EAAE,SAAS;AAAA,QAC9D,GAAG;AAAA,QACH,SAAS;AAAA,MACX,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAmC;AAC7C,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI,KAAK,gBAAgB,YAAY;AAInC,UAAI;AACF,cAAM,OAAO,IAAI,WAEd,MAAM;AACT,YAAI,SAAS;AACb,mBAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,2BAA2B,GAAG;AACxE,cAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,gBAAI;AACF,mBAAK,iBAAiB,QAAQ,IAA+B;AAC7D;AAAA,YACF,SAAS,KAAU;AACjB,kBAAI,QAAQ;AAAA,gBACV,2DAA2D,MAAM,MAAM,KAAK,WAAW,GAAG;AAAA,cAC5F;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,SAAS,GAAG;AACd,cAAI,QAAQ;AAAA,YACV,6DAA6D,MAAM,UAAU,SAAS,IAAI,MAAM,EAAE;AAAA,UACpG;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAGA,UAAI,SAA6B;AACjC,UAAI;AACF,iBAAS,IAAI,WAAwB,UAAU;AAAA,MACjD,QAAQ;AAAA,MAER;AACA,UAAI,QAAQ;AAKV,aAAK,QAAS;AAAA,UACZ;AAAA,UACA,KAAK,eAAe,KAAK,MAAM;AAAA,UAC/B;AAAA,YACE,aAAa,KAAK,iBAAiB,MAAM;AAAA,YACzC,aAAa,KAAK,iBAAiB,KAAK,MAAM;AAAA,YAC9C,gBAAgB,KAAK,KAAK,kBAAkB,IAAI,uBAAuB;AAAA,UACzE;AAAA,QACF;AAAA,MACF;AAEA,UAAI,KAAK,KAAK,mBAAmB,MAAO;AAExC,UAAI,OAA2B;AAC/B,UAAI;AACF,eAAO,IAAI,WAAwB,aAAa;AAAA,MAClD,QAAQ;AAAA,MAER;AACA,UAAI,CAAC,MAAM;AACT,YAAI,QAAQ;AAAA,UACV;AAAA,QAEF;AACA;AAAA,MACF;AACA,6BAAuB,MAAM,KAAK,SAAU,EAAE,UAAU,KAAK,KAAK,SAAS,CAAC;AAC5E,UAAI,QAAQ;AAAA,QACV,uDAAuD,KAAK,KAAK,YAAY;AAAA,MAC/E;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGQ,eAAe,KAAoB,QAAwC;AACjF,WAAO;AAAA,MACL,QAAQ,OAAO,UAAU;AACvB,YAAI;AACF,gBAAO,OAAe,SAAS,iBAAiB;AAAA,YAC9C,UAAU,MAAM,UAAU;AAAA,YAC1B,aAAa;AAAA,YACb,WAAW,GAAG,MAAM,SAAS,IAAI,MAAM,GAAG;AAAA,YAC1C,QAAQ,MAAM;AAAA,YACd,SAAS;AAAA,cACP,WAAW,MAAM;AAAA,cACjB,KAAK,MAAM;AAAA,cACX,OAAO,MAAM;AAAA,cACb,WAAW,MAAM;AAAA,cACjB,QAAQ,MAAM;AAAA,YAChB;AAAA,YACA,YAAY,MAAM,aAAa;AAAA,YAC/B,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,UACtC,CAAC;AAAA,QACH,SAAS,KAAU;AACjB,cAAI,QAAQ,OAAO,kDAAkD,KAAK,WAAW,IAAI;AAAA,QAC3F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiB,QAA0C;AACjE,UAAM,MAAW;AACjB,WAAO;AAAA,MACL,MAAM,OAAO,KAAK;AAChB,cAAM,IAAI,OAAO,cAAc,KAAK,EAAE,mBAAmB,KAAK,CAAC;AAC/D,eAAO,EAAE,IAAI,IAAI,GAAG;AAAA,MACtB;AAAA,MACA,MAAM,IAAI,IAAI;AACZ,cAAM,OAAO,MAAM,IAAI,KAAK,cAAc;AAAA,UACxC,OAAO,EAAE,GAAG;AAAA,UACZ,OAAO;AAAA,UACP,mBAAmB;AAAA,QACrB,CAAC;AACD,cAAM,MAAM,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,OAAO,CAAC;AAC1D,eAAO,OAAO;AAAA,MAChB;AAAA,MACA,MAAM,OAAO,IAAI,OAAO;AACtB,cAAM,IAAI,OAAO,cAAc;AAAA,UAC7B,OAAO,EAAE,GAAG;AAAA,UACZ,MAAM;AAAA,UACN,mBAAmB;AAAA,QACrB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAiB,KAAoB,QAA0C;AACrF,UAAM,MAAW;AACjB,WAAO;AAAA,MACL,OAAO,OAAO,UAAU;AACtB,YAAI;AACF,gBAAM,IAAI,OAAO,qBAAqB;AAAA,YACpC,WAAW,MAAM;AAAA,YACjB,KAAK,MAAM;AAAA,YACX,OAAO,MAAM;AAAA,YACb,QAAQ,MAAM;AAAA,YACd,QAAQ,MAAM,UAAU;AAAA,YACxB,UAAU,MAAM,WAAW;AAAA,YAC3B,UAAU,MAAM,WAAW;AAAA,YAC3B,UAAU,MAAM,WAAW;AAAA,YAC3B,WAAW,CAAC,CAAC,MAAM;AAAA,YACnB,YAAY,MAAM,aAAa;AAAA,YAC/B,QAAQ,MAAM,UAAU;AAAA,YACxB,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,UACrC,GAAG,EAAE,mBAAmB,KAAK,CAAC;AAAA,QAChC,SAAS,KAAU;AACjB,cAAI,QAAQ,OAAO,yDAAyD,KAAK,WAAW,IAAI;AAAA,QAClG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":["manifest","def","manifest"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/crypto-adapter.ts","../src/settings-service.types.ts","../src/settings-service.ts","../src/in-memory-crypto-provider.ts","../src/settings-routes.ts","../src/manifest.ts","../src/manifests/mail.manifest.ts","../src/manifests/branding.manifest.ts","../src/manifests/feature-flags.manifest.ts","../src/manifests/storage.manifest.ts","../src/manifests/index.ts","../src/translations/en.ts","../src/translations/zh-CN.ts","../src/translations/ja-JP.ts","../src/translations/index.ts","../src/settings-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Public entrypoint for `@objectstack/service-settings`.\n * See ADR-0007 and `README.md`.\n */\n\nexport { SettingsService } from './settings-service.js';\nexport {\n type CryptoAdapter,\n NoopCryptoAdapter,\n} from './crypto-adapter.js';\nexport {\n type SettingsActionHandler,\n type SettingsAuditSink,\n type SettingsContext,\n type SettingsEngine,\n type SettingsRow,\n type SettingsServiceOptions,\n envKeyOf,\n SettingsLockedError,\n UnknownKeyError,\n UnknownNamespaceError,\n} from './settings-service.types.js';\nexport {\n SettingsServicePlugin,\n type SettingsServicePluginOptions,\n} from './settings-service-plugin.js';\nexport {\n registerSettingsRoutes,\n type SettingsRoutesOptions,\n} from './settings-routes.js';\nexport {\n settingsObjects,\n settingsPluginManifestHeader,\n SETTINGS_PLUGIN_ID,\n SETTINGS_PLUGIN_VERSION,\n} from './manifest.js';\n\n// Reference manifests (mail / branding / feature flags) and the\n// convenience aggregate. Hosts can pass `builtinSettingsManifests`\n// directly to `new SettingsServicePlugin({ manifests })`.\nexport {\n builtinSettingsManifests,\n brandingSettingsManifest,\n featureFlagsSettingsManifest,\n mailSettingsManifest,\n mailTestActionHandler,\n storageSettingsManifest,\n storageTestActionHandler,\n} from './manifests/index.js';\n\n// Re-export the spec types for convenience so plugin authors only need\n// one import.\nexport type {\n SettingsManifest,\n ResolvedSettingValue,\n SettingsNamespacePayload,\n SettingsActionResult,\n SpecifierScope,\n} from '@objectstack/spec/system';\n\n// Built-in translations (en / zh-CN / ja-JP) for the reference manifests.\n// Hosts merge `settingsBuiltinTranslations` into their i18next resource tree\n// so SettingsView resolves labels via `<ns>.settings.<namespace>.…`.\nexport {\n settingsBuiltinTranslations,\n en as settingsTranslationsEn,\n zhCN as settingsTranslationsZhCN,\n jaJP as settingsTranslationsJaJP,\n} from './translations/index.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Pluggable adapter for at-rest encryption of `Specifier.encrypted: true`\n * values. The default {@link NoopCryptoAdapter} provides a transparent\n * base64 wrapping suitable for development and tests; production\n * deployments MUST inject a real KMS-backed adapter.\n *\n * encrypt/decrypt are async to leave room for KMS round-trips.\n */\nexport interface CryptoAdapter {\n /** Returns the ciphertext blob to store in `sys_setting.value_enc`. */\n encrypt(plaintext: string, ctx: { namespace: string; key: string }): Promise<string>;\n /** Returns the plaintext used by the resolver. */\n decrypt(ciphertext: string, ctx: { namespace: string; key: string }): Promise<string>;\n /**\n * Stable, short, non-reversible digest used for audit-log entries so\n * operators can correlate value changes without leaking secrets.\n */\n digest(plaintext: string): string;\n}\n\n/**\n * Development / test default. Base64-wraps the plaintext so the column\n * isn't a literal mirror but provides no real confidentiality.\n *\n * Operators are expected to override this via\n * `SettingsServicePluginOptions.crypto`.\n */\nexport class NoopCryptoAdapter implements CryptoAdapter {\n async encrypt(plaintext: string): Promise<string> {\n return 'b64:' + Buffer.from(plaintext, 'utf8').toString('base64');\n }\n async decrypt(ciphertext: string): Promise<string> {\n if (!ciphertext.startsWith('b64:')) {\n // Tolerate legacy plaintext rows during the dev rollout.\n return ciphertext;\n }\n return Buffer.from(ciphertext.slice(4), 'base64').toString('utf8');\n }\n digest(plaintext: string): string {\n // FNV-1a 32-bit — short, stable, non-cryptographic. Audit-only.\n let h = 0x811c9dc5;\n for (let i = 0; i < plaintext.length; i++) {\n h ^= plaintext.charCodeAt(i);\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;\n }\n return 'fnv32:' + h.toString(16).padStart(8, '0');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * SettingsService — the runtime implementation of ADR-0007.\n *\n * Responsibilities:\n * - Maintain an in-memory registry of `SettingsManifest` instances.\n * - Read/write values from the shared `sys_setting` K/V table via the\n * `objectql` data engine, with an in-memory fallback so the service\n * is usable before a real persistence layer is wired up (e.g. unit\n * tests, bootstrap, control-plane mock).\n * - Resolve effective values with `Env > Tenant > User > Default`\n * precedence and tag every value with provenance.\n * - Encrypt-at-rest for `encrypted: true` specifiers using a pluggable\n * {@link CryptoAdapter}.\n * - Emit `sys_audit_log` rows for every successful write (encrypted\n * values are masked).\n * - Dispatch `runAction` for `action_button` specifiers — used by\n * \"Test connection\" / \"Send test email\" etc.\n *\n * The service is intentionally framework-agnostic: it doesn't import\n * the HTTP server, the plugin context, or the audit object schema. The\n * plugin wires those pieces up.\n */\n\nimport type { SettingsActionResult, SpecifierScope } from '@objectstack/spec/system';\nimport { type CryptoAdapter } from './crypto-adapter.js';\n\n/** Caller identity used by the resolver and audit log. */\nexport interface SettingsContext {\n /** Calling user id, when known. Required for `scope: 'user'` reads. */\n userId?: string;\n /** Tenant / project id. Reserved for multi-tenant deployments. */\n tenantId?: string;\n /** Permissions held by the caller (used by REST authz). */\n permissions?: string[];\n /** Source IP / request id for audit correlation. */\n requestId?: string;\n}\n\n/** Storage row shape used by both the engine and the in-memory store. */\nexport interface SettingsRow {\n namespace: string;\n key: string;\n scope: SpecifierScope;\n user_id: string | null;\n value: unknown | null;\n value_enc: string | null;\n encrypted: boolean;\n /**\n * When true, lower-scope rows for the same (namespace, key) are\n * read-only — the resolver still returns this row's value and the\n * mutation API throws `SettingsLockedError`. Only meaningful on\n * upper-scope rows (`global`, `tenant`). (Phase 2)\n */\n locked?: boolean;\n /** Human-readable reason the lock was applied (UI tooltip). */\n locked_reason?: string | null;\n updated_at?: string;\n updated_by?: string | null;\n}\n\n/**\n * Minimal data-engine surface used by the SettingsService. Mirrors the\n * methods we actually call so we can stub it cleanly in tests without\n * pulling the whole `IDataEngine`.\n */\nexport interface SettingsEngine {\n find(\n objectName: string,\n opts: { where?: Record<string, unknown>; limit?: number; bypassTenantAudit?: boolean },\n ): Promise<any[]>;\n insert(\n objectName: string,\n data: Record<string, unknown>,\n opts?: { bypassTenantAudit?: boolean },\n ): Promise<any>;\n update(\n objectName: string,\n opts: {\n where: Record<string, unknown>;\n data: Record<string, unknown>;\n bypassTenantAudit?: boolean;\n },\n ): Promise<any>;\n delete?(objectName: string, opts: { where: Record<string, unknown> }): Promise<any>;\n}\n\n/** Optional audit hook — service-settings won't crash if absent. */\nexport interface SettingsAuditSink {\n record(entry: {\n namespace: string;\n key: string;\n scope: SpecifierScope;\n userId?: string;\n actor?: string;\n action: 'set' | 'reset';\n valueDigest: string;\n encrypted: boolean;\n requestId?: string;\n }): Promise<void> | void;\n}\n\n/**\n * Persistence hook for the `sys_secret` object — used by the secret\n * split introduced in Phase 3. When provided, `SettingsService` writes\n * encrypted specifier values via `ICryptoProvider` into `sys_secret`\n * and stores only the handle id in `sys_setting.value_enc`. When\n * absent, the legacy inline `crypto.encrypt → value_enc` path is used.\n */\nexport interface SettingsSecretStore {\n /** Insert a new secret row; returns the row id (handle id). */\n insert(row: {\n id: string;\n namespace: string;\n key: string;\n kms_key_id: string;\n alg: string;\n version: number;\n ciphertext: string;\n }): Promise<{ id: string }>;\n /** Look up the latest ciphertext for a handle id; null when missing. */\n get(id: string): Promise<{\n id: string;\n namespace: string;\n key: string;\n kms_key_id: string;\n alg: string;\n version: number;\n ciphertext: string;\n } | null>;\n /** Replace an existing secret row (used by rotateKey). */\n update(id: string, patch: {\n kms_key_id?: string;\n alg?: string;\n version?: number;\n ciphertext?: string;\n }): Promise<void>;\n}\n\n/**\n * Append-only writer for the `sys_setting_audit` object — Phase 3\n * audit trail. Distinct from `SettingsAuditSink` (which still writes\n * to the generic `sys_audit_log`) so audit consumers can subscribe\n * to settings activity without scanning the firehose.\n */\nexport interface SettingsAuditWriter {\n write(entry: {\n namespace: string;\n key: string;\n scope: SpecifierScope;\n action: 'set' | 'reset' | 'lock' | 'unlock' | 'rotate';\n source?: 'ui' | 'api' | 'migration' | 'import' | 'system';\n actorId?: string;\n oldHash?: string | null;\n newHash?: string | null;\n encrypted: boolean;\n requestId?: string;\n reason?: string;\n }): Promise<void> | void;\n}\n\n/** Action handler signature for `Specifier.type === 'action_button'`. */\nexport type SettingsActionHandler = (input: {\n namespace: string;\n actionId: string;\n values: Record<string, unknown>;\n payload?: unknown;\n ctx: SettingsContext;\n}) => Promise<SettingsActionResult> | SettingsActionResult;\n\nexport interface SettingsServiceOptions {\n /** Persistence engine. When undefined, an in-memory store is used. */\n engine?: SettingsEngine;\n /** Crypto adapter for `encrypted` values. Defaults to NoopCryptoAdapter. */\n crypto?: CryptoAdapter;\n /**\n * Phase 3 ICryptoProvider used together with `secretStore`. When both\n * are wired, encrypted writes flow to `sys_secret` and `value_enc`\n * holds the handle id. When omitted, the legacy inline `crypto`\n * adapter path remains in effect (back-compat).\n */\n cryptoProvider?: import('@objectstack/spec/contracts').ICryptoProvider;\n /** Phase 3 secret store backing the `sys_secret` object. */\n secretStore?: SettingsSecretStore;\n /** Audit sink. When undefined, writes still succeed but are not logged. */\n audit?: SettingsAuditSink;\n /** Phase 3 dedicated writer for `sys_setting_audit`. */\n auditWriter?: SettingsAuditWriter;\n /**\n * `process.env`-like map. Defaults to `process.env`. Injected so\n * unit tests can simulate locked values without polluting the host\n * environment.\n */\n env?: Record<string, string | undefined>;\n /** Object name backing the K/V store. Defaults to 'sys_setting'. */\n objectName?: string;\n}\n\n/**\n * Convert `(namespace, key)` to the env var convention defined in\n * ADR-0007: uppercase, dots → underscores, hyphens → underscores.\n */\nexport function envKeyOf(namespace: string, key: string): string {\n const slug = `${namespace}_${key}`.replace(/[.-]/g, '_').toUpperCase();\n return slug;\n}\n\n/** Thrown when a caller tries to write a value pinned by env. */\nexport class SettingsLockedError extends Error {\n readonly code = 'SETTINGS_LOCKED' as const;\n constructor(\n readonly namespace: string,\n readonly key: string,\n readonly reason = 'locked-by-env',\n ) {\n super(`Setting '${namespace}.${key}' is locked (${reason}).`);\n }\n}\n\n/** Thrown when the requested namespace has no registered manifest. */\nexport class UnknownNamespaceError extends Error {\n readonly code = 'SETTINGS_UNKNOWN_NAMESPACE' as const;\n constructor(readonly namespace: string) {\n super(`No settings manifest registered for namespace '${namespace}'.`);\n }\n}\n\n/** Thrown when a key isn't declared by the namespace's manifest. */\nexport class UnknownKeyError extends Error {\n readonly code = 'SETTINGS_UNKNOWN_KEY' as const;\n constructor(readonly namespace: string, readonly key: string) {\n super(`Key '${key}' is not declared in manifest '${namespace}'.`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n SettingsManifest,\n ResolvedSettingValue,\n SettingsNamespacePayload,\n SettingsActionResult,\n SpecifierScope,\n SettingsChangeEvent,\n SettingsChangeHandler,\n SettingsUnsubscribe,\n} from '@objectstack/spec/system';\nimport {\n type CryptoAdapter,\n NoopCryptoAdapter,\n} from './crypto-adapter.js';\nimport {\n type SettingsActionHandler,\n type SettingsAuditSink,\n type SettingsContext,\n type SettingsEngine,\n type SettingsRow,\n type SettingsServiceOptions,\n envKeyOf,\n SettingsLockedError,\n UnknownKeyError,\n UnknownNamespaceError,\n} from './settings-service.types.js';\n\nconst DEFAULT_OBJECT = 'sys_setting';\n\n/**\n * Value-bearing specifier types — drives which entries we expect to\n * find in the K/V store. Keeps the resolver in sync with the spec\n * without importing the (large) Zod enum at runtime.\n */\nconst LAYOUT_ONLY_TYPES = new Set([\n 'group',\n 'info_banner',\n 'child_pane',\n 'title_value',\n 'action_button',\n]);\n\ninterface RegisteredManifest {\n manifest: SettingsManifest;\n /** Resolved specifier scopes for fast lookup. */\n scopes: Map<string, SpecifierScope>;\n /** Specifiers marked encrypted (or implicit for `password`). */\n encryptedKeys: Set<string>;\n /** Default values from the manifest, keyed by specifier key. */\n defaults: Map<string, unknown>;\n /** Action handlers registered alongside this manifest. */\n actions: Map<string, SettingsActionHandler>;\n}\n\n/**\n * Concrete SettingsService. See `src/settings-service.types.ts` for\n * the supporting types and `README.md` for the high-level contract.\n */\nexport class SettingsService {\n private engine?: SettingsEngine;\n private readonly crypto: CryptoAdapter;\n private cryptoProvider?: import('@objectstack/spec/contracts').ICryptoProvider;\n private secretStore?: import('./settings-service.types.js').SettingsSecretStore;\n private audit?: SettingsAuditSink;\n private auditWriter?: import('./settings-service.types.js').SettingsAuditWriter;\n private readonly env: Record<string, string | undefined>;\n private readonly objectName: string;\n private readonly registry = new Map<string, RegisteredManifest>();\n /** In-memory fallback when no engine is wired. */\n private readonly memory: SettingsRow[] = [];\n /** Change subscribers, optionally scoped to a namespace. */\n private readonly subscribers = new Set<{\n ns?: string;\n handler: SettingsChangeHandler;\n }>();\n\n constructor(opts: SettingsServiceOptions = {}) {\n this.engine = opts.engine;\n this.crypto = opts.crypto ?? new NoopCryptoAdapter();\n this.cryptoProvider = opts.cryptoProvider;\n this.secretStore = opts.secretStore;\n this.audit = opts.audit;\n this.auditWriter = opts.auditWriter;\n this.env = opts.env ?? (typeof process !== 'undefined' ? process.env : {});\n this.objectName = opts.objectName ?? DEFAULT_OBJECT;\n }\n\n /**\n * Late-bind a data engine and (optionally) an audit sink. Plugins\n * call this from `kernel:ready` once `objectql` is wired so the\n * SettingsService swaps from its in-memory fallback to the real\n * `sys_setting` table without re-registering the service.\n */\n bindEngine(\n engine: SettingsEngine,\n audit?: SettingsAuditSink,\n extras?: {\n secretStore?: import('./settings-service.types.js').SettingsSecretStore;\n auditWriter?: import('./settings-service.types.js').SettingsAuditWriter;\n cryptoProvider?: import('@objectstack/spec/contracts').ICryptoProvider;\n },\n ): void {\n this.engine = engine;\n if (audit) this.audit = audit;\n if (extras?.secretStore) this.secretStore = extras.secretStore;\n if (extras?.auditWriter) this.auditWriter = extras.auditWriter;\n if (extras?.cryptoProvider) this.cryptoProvider = extras.cryptoProvider;\n }\n\n /**\n * Cascade priority ranks for lock comparisons (lower = higher\n * precedence). env<global<tenant<user<default. A locked row at a\n * lower rank blocks writes at all higher ranks.\n */\n private scopeRank(scope: SpecifierScope | 'env' | 'default'): number {\n switch (scope) {\n case 'global': return 1;\n case 'tenant': return 2;\n case 'user': return 3;\n default: return 99;\n }\n }\n\n // ---------------------------------------------------------------------\n // Change events (Phase 1)\n // ---------------------------------------------------------------------\n\n /**\n * Subscribe to `settings:changed` events. When `namespace` is set the\n * handler only fires for that namespace, otherwise it fires for every\n * mutation across the service.\n *\n * Returns an idempotent unsubscribe handle — call it from the\n * consumer's shutdown hook to avoid leaks.\n */\n subscribe(\n namespace: string | undefined,\n handler: SettingsChangeHandler,\n ): SettingsUnsubscribe {\n const entry = { ns: namespace, handler };\n this.subscribers.add(entry);\n return () => {\n this.subscribers.delete(entry);\n };\n }\n\n /**\n * Dispatch a change event to all matching subscribers. Errors thrown\n * by a handler are swallowed to keep the bus crash-safe — handlers\n * are expected to enqueue async work themselves.\n */\n private emitChange(event: SettingsChangeEvent): void {\n if (this.subscribers.size === 0) return;\n for (const sub of this.subscribers) {\n if (sub.ns && sub.ns !== event.namespace) continue;\n try {\n sub.handler(event);\n } catch {\n // Swallow — never break the writer because a listener misbehaves.\n }\n }\n }\n\n // ---------------------------------------------------------------------\n // Manifest registry\n // ---------------------------------------------------------------------\n\n /** Register (or replace) a manifest. Idempotent. */\n registerManifest(manifest: SettingsManifest): void {\n const scopes = new Map<string, SpecifierScope>();\n const encryptedKeys = new Set<string>();\n const defaults = new Map<string, unknown>();\n const defaultScope = manifest.scope ?? 'tenant';\n for (const spec of manifest.specifiers) {\n if (!spec.key || LAYOUT_ONLY_TYPES.has(spec.type)) continue;\n scopes.set(spec.key, spec.scope ?? defaultScope);\n if (spec.encrypted || spec.type === 'password') encryptedKeys.add(spec.key);\n if (typeof spec.default !== 'undefined') defaults.set(spec.key, spec.default);\n }\n const prev = this.registry.get(manifest.namespace);\n const actions = prev?.actions ?? new Map<string, SettingsActionHandler>();\n this.registry.set(manifest.namespace, { manifest, scopes, encryptedKeys, defaults, actions });\n }\n\n /** Look up a manifest, or throw `UnknownNamespaceError`. */\n getManifest(namespace: string): SettingsManifest {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n return reg.manifest;\n }\n\n /** List all registered manifests, optionally filtered by permission. */\n listManifests(ctx: SettingsContext = {}): SettingsManifest[] {\n const perms = new Set(ctx.permissions ?? []);\n const all = Array.from(this.registry.values()).map((r) => r.manifest);\n // Empty permissions ⇒ pass-through (server-side trust, e.g. boot tests).\n if (perms.size === 0) return all;\n return all.filter((m) => perms.has(m.readPermission ?? 'setup.access'));\n }\n\n /** Register a handler for an `action_button` declared in a manifest. */\n registerAction(namespace: string, actionId: string, handler: SettingsActionHandler): void {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n reg.actions.set(actionId, handler);\n }\n\n // ---------------------------------------------------------------------\n // Resolver\n // ---------------------------------------------------------------------\n\n /** Resolve a single key. */\n async get<T = unknown>(\n namespace: string,\n key: string,\n ctx: SettingsContext = {},\n ): Promise<ResolvedSettingValue<T>> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n if (!reg.scopes.has(key)) throw new UnknownKeyError(namespace, key);\n\n // 1. env\n const envName = envKeyOf(namespace, key);\n const envRaw = this.env[envName];\n if (typeof envRaw === 'string') {\n const def = reg.defaults.get(key);\n const value = coerceEnvValue(envRaw, def);\n return {\n value: value as T,\n source: 'env',\n locked: true,\n lockedReason: `Set via env: ${envName}`,\n cascadeChain: [\n { scope: 'env', value, locked: true, lockedReason: `Set via env: ${envName}`, effective: true },\n ],\n };\n }\n\n const scope = reg.scopes.get(key)!;\n // For 'user' scope we pre-filter by user_id; for 'tenant' and 'global'\n // we load everything for the namespace and pick the right row below.\n const rows = await this.loadRows(namespace, scope === 'user' ? ctx.userId ?? null : null);\n\n // 2. cascade walk — env (handled above) > global > tenant > user > default\n //\n // Build the full chain in declared order so the UI can render\n // \"Inherited from Global / Locked by Global / Overrides tenant\"\n // badges. The first non-null entry wins as `source`.\n const chain: NonNullable<ResolvedSettingValue['cascadeChain']> = [];\n\n const globalRow = rows.find((r) => r.key === key && r.scope === 'global');\n if (globalRow) {\n const value = await this.materialiseRow(globalRow);\n chain.push({\n scope: 'global',\n value,\n locked: !!globalRow.locked,\n lockedReason: globalRow.locked_reason ?? undefined,\n });\n }\n\n if (scope === 'tenant' || scope === 'user') {\n const tenantRow = rows.find((r) => r.key === key && r.scope === 'tenant');\n if (tenantRow) {\n chain.push({\n scope: 'tenant',\n value: await this.materialiseRow(tenantRow),\n locked: !!tenantRow.locked,\n lockedReason: tenantRow.locked_reason ?? undefined,\n });\n }\n }\n\n if (scope === 'user') {\n const userRow = rows.find((r) => r.key === key && r.scope === 'user');\n if (userRow) {\n chain.push({\n scope: 'user',\n value: await this.materialiseRow(userRow),\n });\n }\n }\n\n const def = reg.defaults.get(key);\n chain.push({ scope: 'default', value: def ?? null });\n\n // Effective row: highest priority entry. Lock anywhere up the chain\n // locks the effective value (lower scopes can't shadow it).\n const lockedEntry = chain.find((e) => e.locked === true);\n const effective = chain.find((e) => e.value !== null && e.value !== undefined) ?? chain[chain.length - 1];\n effective.effective = true;\n\n return {\n value: effective.value as T,\n source: effective.scope as ResolvedSettingValue['source'],\n locked: !!lockedEntry,\n lockedReason: lockedEntry?.lockedReason,\n cascadeChain: chain,\n };\n }\n\n /** Resolve every value in a namespace + return the manifest. */\n async getNamespace(\n namespace: string,\n ctx: SettingsContext = {},\n ): Promise<SettingsNamespacePayload> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n\n const values: Record<string, ResolvedSettingValue> = {};\n for (const [key] of reg.scopes) {\n values[key] = await this.get(namespace, key, ctx);\n }\n return { manifest: reg.manifest, values };\n }\n\n // ---------------------------------------------------------------------\n // Reactive client (Phase 1)\n // ---------------------------------------------------------------------\n\n /**\n * Build a reactive `ISettingsClient` for a namespace.\n *\n * The client maintains an internal snapshot of the resolved values,\n * refreshing on every `settings:changed` event for the namespace.\n * Consumers call `current` / `get(key)` for synchronous reads and\n * register handlers via `onChange()`.\n *\n * `schema` is optional. When supplied, the snapshot is parsed (and\n * defaulted) through the Zod schema on each refresh — this gives\n * plugins strong types and runtime validation in one call. When\n * absent, raw resolved values flow through unchanged (used by the\n * dynamic console UI which validates per-field).\n */\n async createClient<T extends Record<string, unknown> = Record<string, unknown>>(\n namespace: string,\n opts: {\n ctx?: SettingsContext;\n parse?: (raw: Record<string, unknown>) => T;\n } = {},\n ): Promise<{\n readonly namespace: string;\n readonly current: T;\n get<K extends keyof T>(key: K): T[K];\n onChange(handler: SettingsChangeHandler): SettingsUnsubscribe;\n refresh(): Promise<void>;\n dispose(): void;\n }> {\n const ctx = opts.ctx ?? {};\n let snapshot: T = await this.snapshotOf<T>(namespace, ctx, opts.parse);\n\n const off = this.subscribe(namespace, () => {\n // Fire-and-forget refresh; new readers see the latest snapshot.\n void this.snapshotOf<T>(namespace, ctx, opts.parse).then((next) => {\n snapshot = next;\n });\n });\n\n return {\n namespace,\n get current() {\n return snapshot;\n },\n get<K extends keyof T>(key: K): T[K] {\n return snapshot[key];\n },\n onChange: (handler) => this.subscribe(namespace, handler),\n refresh: async () => {\n snapshot = await this.snapshotOf<T>(namespace, ctx, opts.parse);\n },\n dispose: off,\n };\n }\n\n private async snapshotOf<T>(\n namespace: string,\n ctx: SettingsContext,\n parse?: (raw: Record<string, unknown>) => T,\n ): Promise<T> {\n const payload = await this.getNamespace(namespace, ctx);\n const raw: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(payload.values)) raw[k] = v.value;\n return parse ? parse(raw) : (raw as T);\n }\n\n // ---------------------------------------------------------------------\n // Mutations\n // ---------------------------------------------------------------------\n\n /** Persist a single key. Throws SettingsLockedError when env-locked. */\n async set(\n namespace: string,\n key: string,\n value: unknown,\n ctx: SettingsContext = {},\n ): Promise<ResolvedSettingValue> {\n return (await this.setMany(namespace, { [key]: value }, ctx))[key];\n }\n\n /** Persist multiple keys atomically (best-effort). */\n async setMany(\n namespace: string,\n patch: Record<string, unknown>,\n ctx: SettingsContext = {},\n ): Promise<Record<string, ResolvedSettingValue>> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n\n // Pre-flight: reject the whole batch if any key is locked or unknown.\n for (const key of Object.keys(patch)) {\n if (!reg.scopes.has(key)) throw new UnknownKeyError(namespace, key);\n const envRaw = this.env[envKeyOf(namespace, key)];\n if (typeof envRaw === 'string') throw new SettingsLockedError(namespace, key);\n\n // Phase 2 lock: a row at an upper scope marked locked=true\n // refuses writes at this (lower) scope. Writing AT the same\n // scope as the lock is still permitted (i.e. a platform admin\n // can edit a globally-locked value; a tenant admin cannot).\n const scope = reg.scopes.get(key)!;\n const rows = await this.loadRows(namespace, scope === 'user' ? ctx.userId ?? null : null);\n const upper = rows.find(\n (r) =>\n r.key === key &&\n r.locked === true &&\n this.scopeRank(r.scope) < this.scopeRank(scope),\n );\n if (upper) {\n throw new SettingsLockedError(namespace, key, `locked-by-${upper.scope}`);\n }\n }\n\n for (const [key, rawValue] of Object.entries(patch)) {\n const scope = reg.scopes.get(key)!;\n // global rows are platform-wide (tenant_id=null, user_id=null);\n // user rows pin to ctx.userId; tenant rows leave user_id null and\n // let the engine's tenant scoping fill in tenant_id from ctx.\n const userId = scope === 'user' ? ctx.userId ?? null : null;\n const isEncrypted = reg.encryptedKeys.has(key);\n const isNull = rawValue === null || typeof rawValue === 'undefined';\n\n let storedValue: unknown | null = null;\n let storedEnc: string | null = null;\n let digest = '';\n\n if (!isNull) {\n if (isEncrypted) {\n const plain = typeof rawValue === 'string' ? rawValue : JSON.stringify(rawValue);\n // Phase 3 split: when a sys_secret store + ICryptoProvider are\n // wired, persist the ciphertext in sys_secret and keep the\n // handle id in sys_setting.value_enc. Otherwise fall back to\n // the legacy inline crypto adapter path for back-compat.\n if (this.cryptoProvider && this.secretStore) {\n const handle = await this.cryptoProvider.encrypt(plain, {\n namespace,\n key,\n tenantId: ctx.tenantId,\n });\n await this.secretStore.insert({\n id: handle.id,\n namespace,\n key,\n kms_key_id: handle.kmsKeyId,\n alg: handle.alg,\n version: handle.version,\n ciphertext: handle.ciphertext,\n });\n storedEnc = handle.id;\n digest = this.cryptoProvider.digest(plain);\n } else {\n storedEnc = await this.crypto.encrypt(plain, { namespace, key });\n digest = this.crypto.digest(plain);\n }\n } else {\n storedValue = rawValue;\n digest = this.crypto.digest(stableStringify(rawValue));\n }\n }\n\n await this.upsertRow({\n namespace,\n key,\n scope,\n user_id: userId,\n value: storedValue,\n value_enc: storedEnc,\n encrypted: isEncrypted,\n updated_at: new Date().toISOString(),\n updated_by: ctx.userId ?? null,\n });\n\n if (this.audit) {\n await this.audit.record({\n namespace,\n key,\n scope,\n userId: ctx.userId,\n action: isNull ? 'reset' : 'set',\n valueDigest: isEncrypted ? '<encrypted:' + digest + '>' : digest,\n encrypted: isEncrypted,\n requestId: ctx.requestId,\n });\n }\n\n if (this.auditWriter) {\n try {\n await this.auditWriter.write({\n namespace,\n key,\n scope,\n action: isNull ? 'reset' : 'set',\n source: 'api',\n actorId: ctx.userId,\n oldHash: null,\n newHash: isNull ? null : digest,\n encrypted: isEncrypted,\n requestId: ctx.requestId,\n });\n } catch {\n // never fail a write because the audit table is unhappy.\n }\n }\n\n this.emitChange({\n namespace,\n key,\n scope,\n action: isNull ? 'reset' : 'set',\n at: new Date().toISOString(),\n });\n }\n\n // Re-resolve so callers see the post-write effective values.\n const out: Record<string, ResolvedSettingValue> = {};\n for (const key of Object.keys(patch)) {\n out[key] = await this.get(namespace, key, ctx);\n }\n return out;\n }\n\n /** Invoke a declared action (test connection, rotate, …). */\n async runAction(\n namespace: string,\n actionId: string,\n payload: unknown,\n ctx: SettingsContext = {},\n ): Promise<SettingsActionResult> {\n const reg = this.registry.get(namespace);\n if (!reg) throw new UnknownNamespaceError(namespace);\n const handler = reg.actions.get(actionId);\n if (!handler) {\n return {\n ok: false,\n severity: 'error',\n message: `No handler registered for action '${actionId}' in '${namespace}'.`,\n };\n }\n const values: Record<string, unknown> = {};\n for (const [key] of reg.scopes) {\n values[key] = (await this.get(namespace, key, ctx)).value;\n }\n try {\n return await handler({ namespace, actionId, values, payload, ctx });\n } catch (err: any) {\n return {\n ok: false,\n severity: 'error',\n message: err?.message ?? 'Action handler threw.',\n };\n }\n }\n\n // ---------------------------------------------------------------------\n // Persistence helpers (engine or in-memory)\n // ---------------------------------------------------------------------\n\n private async loadRows(namespace: string, userId: string | null): Promise<SettingsRow[]> {\n if (this.engine) {\n const where: Record<string, unknown> = { namespace };\n if (userId !== null) where.user_id = userId;\n // Settings rows include platform-wide (`global` scope, tenant_id=null)\n // entries; bypass the tenant-scoping audit warning so loads work\n // uniformly across global/tenant/user without log noise. Per-tenant\n // isolation for `tenant`-scope rows is still enforced by the engine\n // once an ExecutionContext.tenantId is plumbed through (Phase 2+).\n const rows = await this.engine.find(this.objectName, {\n where,\n bypassTenantAudit: true,\n } as any);\n return rows.map((r) => ({\n namespace: r.namespace,\n key: r.key,\n scope: r.scope as SpecifierScope,\n user_id: r.user_id ?? null,\n value: r.value ?? null,\n value_enc: r.value_enc ?? null,\n encrypted: Boolean(r.encrypted),\n locked: Boolean(r.locked),\n locked_reason: r.locked_reason ?? null,\n updated_at: r.updated_at,\n updated_by: r.updated_by ?? null,\n }));\n }\n return this.memory.filter(\n (r) =>\n r.namespace === namespace &&\n (userId === null || r.user_id === userId || r.scope === 'tenant' || r.scope === 'global'),\n );\n }\n\n private async upsertRow(row: SettingsRow): Promise<void> {\n if (this.engine) {\n const where: Record<string, unknown> = {\n namespace: row.namespace,\n key: row.key,\n scope: row.scope,\n user_id: row.user_id ?? null,\n };\n // global rows are platform-wide — bypass the tenant audit warning\n // (we intentionally write tenant_id=null). tenant/user rows still\n // benefit from the warning when ctx.tenantId is missing.\n const bypass = row.scope === 'global' ? { bypassTenantAudit: true } : {};\n const existing = await this.engine.find(this.objectName, {\n where,\n limit: 1,\n ...bypass,\n } as any);\n if (existing[0]) {\n await this.engine.update(this.objectName, {\n where,\n data: { ...row },\n ...bypass,\n } as any);\n } else {\n await this.engine.insert(this.objectName, { ...row }, bypass as any);\n }\n return;\n }\n const idx = this.memory.findIndex(\n (r) =>\n r.namespace === row.namespace &&\n r.key === row.key &&\n r.scope === row.scope &&\n (r.user_id ?? null) === (row.user_id ?? null),\n );\n if (idx >= 0) this.memory[idx] = row;\n else this.memory.push(row);\n }\n\n private async materialiseRow(row: SettingsRow): Promise<unknown> {\n if (row.encrypted) {\n if (!row.value_enc) return null;\n let plain: string;\n // Phase 3: when the value_enc looks like a sys_secret handle and\n // both the secretStore + cryptoProvider are wired, dereference\n // through sys_secret. Otherwise (legacy rows or in-memory tests)\n // fall back to inline crypto-adapter decryption.\n if (\n this.cryptoProvider &&\n this.secretStore &&\n typeof row.value_enc === 'string' &&\n row.value_enc.startsWith('sec_')\n ) {\n const secret = await this.secretStore.get(row.value_enc);\n if (!secret) return null;\n plain = await this.cryptoProvider.decrypt(\n {\n id: secret.id,\n kmsKeyId: secret.kms_key_id,\n alg: secret.alg,\n version: secret.version,\n ciphertext: secret.ciphertext,\n },\n { namespace: row.namespace, key: row.key },\n );\n } else {\n plain = await this.crypto.decrypt(row.value_enc, {\n namespace: row.namespace,\n key: row.key,\n });\n }\n // Try JSON parse so non-string secrets round-trip.\n try {\n return JSON.parse(plain);\n } catch {\n return plain;\n }\n }\n return row.value ?? null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Local helpers\n// ---------------------------------------------------------------------------\n\n/** Stable stringify so the audit digest is order-independent. */\nfunction stableStringify(input: unknown): string {\n if (input === null || typeof input !== 'object') return JSON.stringify(input);\n if (Array.isArray(input)) return '[' + input.map(stableStringify).join(',') + ']';\n const obj = input as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n return '{' + keys.map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k])).join(',') + '}';\n}\n\n/** Re-typed env coercer (the canonical one lives in settings-service.types). */\nfunction coerceEnvValue(raw: string, hint: unknown): unknown {\n if (typeof hint === 'boolean') return raw === 'true' || raw === '1' || raw === 'yes';\n if (typeof hint === 'number') {\n const n = Number(raw);\n return Number.isFinite(n) ? n : raw;\n }\n if (Array.isArray(hint) || (hint && typeof hint === 'object')) {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n return raw;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n CryptoContext,\n CryptoHandle,\n ICryptoProvider,\n} from '@objectstack/spec/contracts';\nimport { createHash, randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\n\n/**\n * InMemoryCryptoProvider — default ICryptoProvider used by the\n * SettingsService when the host application does not wire a real KMS.\n *\n * Encryption: AES-256-GCM with a per-process random data key. The data\n * key lives only in memory; restarting the process loses the ability\n * to decrypt previously-written rows. This is intentional — operators\n * MUST replace this with a KMS-backed provider before relying on\n * `sys_secret` for production secrets. The provider's purpose is to:\n *\n * - exercise the round-trip in unit tests and dev kernels;\n * - provide a \"real-looking\" handle format so consumers don't depend\n * on accidental implementation details of a no-op adapter;\n * - serve as a reference for what AwsKmsCryptoProvider /\n * GcpKmsCryptoProvider implementations need to satisfy.\n *\n * Handle format:\n * id — `sec_` + 32 hex chars (122 bits of entropy)\n * kmsKeyId — `local:in-memory:v<version>`\n * alg — `aes-256-gcm`\n * version — bumps on rotateKey()\n * ciphertext— base64(iv (12) || authTag (16) || cipher)\n *\n * AAD binding: the CryptoContext (namespace + key + tenantId) is\n * folded into AES-GCM AAD so a ciphertext rewrapped from a different\n * (ns, key) tuple fails decryption — guards against operators\n * accidentally copying rows between namespaces.\n *\n * WebContainer (StackBlitz) note: `node:crypto.createCipheriv('aes-256-gcm', …)`\n * is not implemented in WebContainer. When we detect that runtime, we\n * swap to a pure-JS AES-GCM from `@noble/ciphers/aes.js`, producing the\n * same `iv || tag || ciphertext` byte layout so the handle shape is\n * unchanged. The swap is best-effort: if the dependency is missing,\n * we fall back to the Node implementation and let it throw, surfacing\n * the configuration problem clearly.\n */\nconst isWebContainerRuntime = (): boolean => {\n const g = globalThis as any;\n return (\n typeof g !== 'undefined' &&\n (Boolean(g.process?.versions?.webcontainer) ||\n Boolean(g.process?.env?.SHELL?.includes?.('jsh')) ||\n Boolean(g.process?.env?.STACKBLITZ))\n );\n};\n\ntype GcmFactory = (key: Uint8Array, nonce: Uint8Array, aad?: Uint8Array) => {\n encrypt: (plain: Uint8Array) => Uint8Array;\n decrypt: (cipher: Uint8Array) => Uint8Array;\n};\n\nlet nobleGcmPromise: Promise<GcmFactory | undefined> | undefined;\nconst loadNobleGcm = (): Promise<GcmFactory | undefined> => {\n if (!nobleGcmPromise) {\n nobleGcmPromise = (async () => {\n try {\n const mod = await import('@noble/ciphers/aes.js');\n return mod.gcm as unknown as GcmFactory;\n } catch (err: any) {\n console.warn(\n `[InMemoryCryptoProvider] WebContainer detected but @noble/ciphers not installed: ${err?.message ?? err}. Falling back to node:crypto (will throw).`,\n );\n return undefined;\n }\n })();\n }\n return nobleGcmPromise;\n};\n\nexport class InMemoryCryptoProvider implements ICryptoProvider {\n private readonly key: Buffer;\n private readonly useNoble: boolean;\n\n constructor(opts: { key?: Buffer } = {}) {\n this.key = opts.key ?? randomBytes(32);\n this.useNoble = isWebContainerRuntime();\n }\n\n async encrypt(plain: string, ctx: CryptoContext): Promise<CryptoHandle> {\n const iv = randomBytes(12);\n const aad = Buffer.from(this.aadOf(ctx), 'utf8');\n const plainBytes = Buffer.from(plain, 'utf8');\n\n let blob: string;\n if (this.useNoble) {\n const gcm = await loadNobleGcm();\n if (gcm) {\n const cipher = gcm(this.key, iv, aad);\n const ctWithTag = cipher.encrypt(plainBytes); // ciphertext || tag(16)\n const ct = ctWithTag.subarray(0, ctWithTag.length - 16);\n const tag = ctWithTag.subarray(ctWithTag.length - 16);\n blob = Buffer.concat([iv, Buffer.from(tag), Buffer.from(ct)]).toString('base64');\n } else {\n blob = this.encryptNode(plainBytes, iv, aad);\n }\n } else {\n blob = this.encryptNode(plainBytes, iv, aad);\n }\n\n return {\n id: 'sec_' + randomBytes(16).toString('hex'),\n kmsKeyId: 'local:in-memory:v1',\n alg: 'aes-256-gcm',\n version: 1,\n ciphertext: blob,\n };\n }\n\n async decrypt(handle: CryptoHandle, ctx: CryptoContext): Promise<string> {\n const buf = Buffer.from(handle.ciphertext, 'base64');\n const iv = buf.subarray(0, 12);\n const tag = buf.subarray(12, 28);\n const data = buf.subarray(28);\n const aad = Buffer.from(this.aadOf(ctx), 'utf8');\n\n if (this.useNoble) {\n const gcm = await loadNobleGcm();\n if (gcm) {\n const cipher = gcm(this.key, iv, aad);\n const ctWithTag = Buffer.concat([data, tag]); // noble expects ciphertext || tag\n const out = cipher.decrypt(ctWithTag);\n return Buffer.from(out).toString('utf8');\n }\n }\n const decipher = createDecipheriv('aes-256-gcm', this.key, iv);\n decipher.setAAD(aad);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8');\n }\n\n async rotateKey(handle: CryptoHandle, ctx: CryptoContext): Promise<CryptoHandle> {\n const plain = await this.decrypt(handle, ctx);\n const next = await this.encrypt(plain, ctx);\n return {\n ...next,\n id: handle.id,\n kmsKeyId: `local:in-memory:v${handle.version + 1}`,\n version: handle.version + 1,\n };\n }\n\n digest(plain: string): string {\n return 'sha256:' + createHash('sha256').update(plain, 'utf8').digest('hex');\n }\n\n private encryptNode(plainBytes: Buffer, iv: Buffer, aad: Buffer): string {\n const cipher = createCipheriv('aes-256-gcm', this.key, iv);\n cipher.setAAD(aad);\n const enc = Buffer.concat([cipher.update(plainBytes), cipher.final()]);\n const tag = cipher.getAuthTag();\n return Buffer.concat([iv, tag, enc]).toString('base64');\n }\n\n private aadOf(ctx: CryptoContext): string {\n // Bind ciphertext to (namespace,key) so a row cannot be moved across\n // specifiers. Tenant binding is intentionally omitted because the\n // handle is dereferenced from a `sys_setting` row already scoped to\n // its tenant — adding tenant here would force the decrypt path to\n // re-read that scope.\n return [ctx.namespace, ctx.key].join('|');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * REST surface for the SettingsService — see ADR-0007 §REST.\n *\n * GET /api/settings → visible manifests\n * GET /api/settings/:namespace → { manifest, values }\n * PUT /api/settings/:namespace → batch upsert\n * POST /api/settings/:namespace/:actionId → invoke declared action\n *\n * The route layer is a thin wrapper that maps thrown service errors\n * into proper HTTP status codes; all business logic lives in\n * `SettingsService`.\n */\n\nimport type { IHttpServer, IHttpRequest, IHttpResponse, RouteHandler } from '@objectstack/spec/contracts';\nimport { SettingsService } from './settings-service.js';\nimport {\n SettingsLockedError,\n UnknownKeyError,\n UnknownNamespaceError,\n type SettingsContext,\n} from './settings-service.types.js';\n\nexport interface SettingsRoutesOptions {\n /** Base path. Default `/api/settings`. */\n basePath?: string;\n /**\n * Extract caller identity from the request. The default reads\n * `x-user-id` / `x-tenant-id` headers and parses\n * `x-permissions` as a comma-separated list — fine for dev and\n * straightforward to override in production wiring.\n */\n contextFromRequest?: (req: IHttpRequest) => SettingsContext;\n}\n\nconst defaultContext = (req: IHttpRequest): SettingsContext => {\n const header = (name: string): string | undefined => {\n const v = req.headers?.[name];\n return Array.isArray(v) ? v[0] : v;\n };\n const perms = header('x-permissions');\n return {\n userId: header('x-user-id'),\n tenantId: header('x-tenant-id'),\n permissions: perms ? perms.split(',').map((s) => s.trim()).filter(Boolean) : undefined,\n requestId: header('x-request-id'),\n };\n};\n\nfunction sendError(res: IHttpResponse, status: number, code: string, message: string, extra?: Record<string, unknown>) {\n res.status(status).json({ error: { code, message, ...extra } });\n}\n\nexport function registerSettingsRoutes(\n http: IHttpServer,\n service: SettingsService,\n opts: SettingsRoutesOptions = {},\n): void {\n const base = opts.basePath ?? '/api/settings';\n const ctxOf = opts.contextFromRequest ?? defaultContext;\n\n http.get(base, (async (req, res) => {\n try {\n const ctx = ctxOf(req);\n const manifests = service.listManifests(ctx);\n await res.json({ manifests });\n } catch (err: any) {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Failed to list manifests');\n }\n }) satisfies RouteHandler);\n\n http.get(`${base}/:namespace`, (async (req, res) => {\n const ns = req.params.namespace;\n try {\n const ctx = ctxOf(req);\n const payload = await service.getNamespace(ns, ctx);\n await res.json(payload);\n } catch (err: any) {\n if (err instanceof UnknownNamespaceError) {\n sendError(res, 404, 'UNKNOWN_NAMESPACE', err.message);\n } else {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Failed to read namespace');\n }\n }\n }) satisfies RouteHandler);\n\n http.put(`${base}/:namespace`, (async (req, res) => {\n const ns = req.params.namespace;\n const body = (req.body ?? {}) as Record<string, unknown>;\n try {\n const ctx = ctxOf(req);\n const result = await service.setMany(ns, body, ctx);\n await res.json({ values: result });\n } catch (err: any) {\n if (err instanceof SettingsLockedError) {\n sendError(res, 409, 'SETTINGS_LOCKED', err.message, {\n namespace: err.namespace,\n key: err.key,\n reason: err.reason,\n });\n } else if (err instanceof UnknownNamespaceError) {\n sendError(res, 404, 'UNKNOWN_NAMESPACE', err.message);\n } else if (err instanceof UnknownKeyError) {\n sendError(res, 400, 'UNKNOWN_KEY', err.message, { namespace: err.namespace, key: err.key });\n } else {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Failed to write namespace');\n }\n }\n }) satisfies RouteHandler);\n\n http.post(`${base}/:namespace/:actionId`, (async (req, res) => {\n const { namespace, actionId } = req.params;\n try {\n const ctx = ctxOf(req);\n const result = await service.runAction(namespace, actionId, req.body, ctx);\n const status = result.ok ? 200 : 400;\n await res.status(status).json(result);\n } catch (err: any) {\n if (err instanceof UnknownNamespaceError) {\n sendError(res, 404, 'UNKNOWN_NAMESPACE', err.message);\n } else {\n sendError(res, 500, 'INTERNAL', err?.message ?? 'Action failed');\n }\n }\n }) satisfies RouteHandler);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SysSetting, SysSecret, SysSettingAudit } from '@objectstack/platform-objects/system';\n\nexport const SETTINGS_PLUGIN_ID = 'com.objectstack.service.settings';\nexport const SETTINGS_PLUGIN_VERSION = '0.1.0';\n\n/** Objects owned by service-settings. Currently just the K/V store. */\nexport const settingsObjects: any[] = [SysSetting, SysSecret, SysSettingAudit];\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const settingsPluginManifestHeader = {\n id: SETTINGS_PLUGIN_ID,\n namespace: 'sys',\n version: SETTINGS_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'project' as const,\n name: 'Settings Service',\n description:\n 'Generic settings registry + K/V resolver with Env > Tenant > User > Default precedence. ADR-0007.',\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\nimport type { SettingsActionHandler } from '../settings-service.types.js';\n\n// Visibility expressions are written as inline strings here for\n// readability. The spec's ExpressionInputSchema accepts a bare string\n// and normalises it at parse time, but the inferred TypeScript output\n// type expects `{ dialect, source }` objects. Build the manifest as\n// `unknown` first, then cast — keeps the manifest source compact.\nconst manifest = {\n namespace: 'mail',\n version: 1,\n label: 'Mail Delivery',\n icon: 'Mail',\n description: 'SMTP and transactional email provider configuration.',\n scope: 'global',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Communication',\n order: 10,\n specifiers: [\n { type: 'group', id: 'provider', label: 'Provider', required: false,\n description: 'Choose how this workspace sends outbound email.' },\n\n { type: 'select', key: 'provider', label: 'Provider', required: true, default: 'smtp',\n options: [\n { value: 'smtp', label: 'SMTP' },\n { value: 'sendgrid', label: 'SendGrid' },\n { value: 'ses', label: 'Amazon SES' },\n { value: 'postmark', label: 'Postmark' },\n ],\n },\n\n { type: 'group', id: 'smtp', label: 'SMTP', required: false, visible: \"${data.provider === 'smtp'}\" },\n { type: 'text', key: 'smtp_host', label: 'Host', required: true,\n description: 'Example: smtp.example.com', visible: \"${data.provider === 'smtp'}\" },\n { type: 'number', key: 'smtp_port', label: 'Port', required: false, default: 587,\n min: 1, max: 65535, visible: \"${data.provider === 'smtp'}\" },\n { type: 'toggle', key: 'smtp_secure', label: 'Use TLS', required: false, default: true,\n visible: \"${data.provider === 'smtp'}\" },\n { type: 'text', key: 'smtp_user', label: 'Username', required: false,\n visible: \"${data.provider === 'smtp'}\" },\n { type: 'password', key: 'smtp_password', label: 'Password', required: false,\n visible: \"${data.provider === 'smtp'}\" },\n\n { type: 'group', id: 'api_key', label: 'API key', required: false, visible: \"${data.provider !== 'smtp'}\" },\n { type: 'password', key: 'api_key', label: 'API key', required: true, encrypted: true,\n visible: \"${data.provider !== 'smtp'}\" },\n\n { type: 'group', id: 'from_address', label: 'From address', required: false },\n { type: 'email', key: 'from_email', label: 'From email', required: true,\n description: 'Example: no-reply@example.com' },\n { type: 'text', key: 'from_name', label: 'From name', required: false, default: 'ObjectStack' },\n\n { type: 'action_button', id: 'test', label: 'Send test email', required: false, icon: 'Send',\n handler: { kind: 'http', method: 'POST', url: '/api/settings/mail/test' } },\n ],\n};\n\n/** Mail Delivery — SMTP / API provider configuration. */\nexport const mailSettingsManifest = manifest as unknown as SettingsManifest;\n\n/** Built-in action handler stub for `mail/test`. */\nexport const mailTestActionHandler: SettingsActionHandler = async ({ values }) => {\n const provider = String(values.provider ?? 'smtp');\n const fromEmail = values.from_email as string | undefined;\n if (!fromEmail) {\n return { ok: false, severity: 'error', message: 'Configure a from address before testing.' };\n }\n if (provider === 'smtp' && !values.smtp_host) {\n return { ok: false, severity: 'error', message: 'SMTP host is required.' };\n }\n if (provider !== 'smtp' && !values.api_key) {\n return { ok: false, severity: 'error', message: 'API key is required.' };\n }\n return {\n ok: true,\n severity: 'info',\n message: `Configuration looks valid (provider=${provider}). Wire @objectstack/plugin-mail for actual delivery.`,\n };\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\n\n/** Branding — workspace identity (name, logo, theme). */\nexport const brandingSettingsManifest: SettingsManifest = {\n namespace: 'branding',\n version: 1,\n label: 'Branding',\n icon: 'Palette',\n description: 'Workspace name, logo, and accent colour.',\n scope: 'tenant',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Workspace',\n order: 5,\n specifiers: [\n { type: 'group', id: 'identity', label: 'Identity', required: false },\n { type: 'text', key: 'workspace_name', label: 'Workspace name', required: true,\n default: 'ObjectStack', minLength: 1, maxLength: 60 },\n { type: 'email', key: 'support_email', label: 'Support email', required: false,\n description: 'Example: support@example.com' },\n\n { type: 'group', id: 'appearance', label: 'Appearance', required: false },\n { type: 'select', key: 'theme_mode', label: 'Default theme', required: false, default: 'system',\n options: [\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n { value: 'system', label: 'Match system' },\n ],\n },\n { type: 'color', key: 'accent_color', label: 'Accent colour', required: false, default: '#6366f1' },\n { type: 'url', key: 'logo_url', label: 'Logo URL', required: false,\n description: 'Example: https://…/logo.svg' },\n ],\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\n\n/** Feature Flags — opt into experimental capabilities. */\nexport const featureFlagsSettingsManifest: SettingsManifest = {\n namespace: 'feature_flags',\n version: 1,\n label: 'Feature Flags',\n icon: 'FlaskConical',\n description: 'Toggle experimental and beta features for this workspace.',\n scope: 'tenant',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Beta',\n order: 100,\n beta: true,\n specifiers: [\n { type: 'info_banner', id: 'beta_notice', label: 'Heads up', required: false,\n bannerText:\n 'Beta features may change without notice. Pin via env vars (e.g. `FEATURE_FLAGS_AI_ENABLED=true`) to lock for the whole deployment.',\n bannerSeverity: 'warning' },\n\n { type: 'group', id: 'productivity', label: 'Productivity', required: false },\n { type: 'toggle', key: 'ai_enabled', label: 'AI Assistant', required: false, default: false,\n description: 'Enables the in-app AI assistant panel.' },\n { type: 'toggle', key: 'kanban_swimlanes', label: 'Kanban swimlanes', required: false, default: false },\n\n { type: 'group', id: 'collaboration', label: 'Collaboration', required: false },\n { type: 'toggle', key: 'realtime_cursors', label: 'Realtime cursors', required: false, default: false },\n { type: 'toggle', key: 'inline_comments', label: 'Inline comments', required: false, default: true },\n ],\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { SettingsManifest } from '@objectstack/spec/system';\nimport type { SettingsActionHandler } from '../settings-service.types.js';\n\n// Mirrors the shape of `mail.manifest.ts`. The actual adapter rebuild\n// + `storage/test` probe live in `@objectstack/service-storage`; this\n// manifest only declares the form + acts as a safe fallback when the\n// storage plugin is not present.\nconst manifest = {\n namespace: 'storage',\n version: 1,\n label: 'File Storage',\n icon: 'HardDrive',\n description:\n 'Backend used for attachments, exports, and user uploads. ' +\n '⚠ Switching adapter does not migrate existing files — files ' +\n 'uploaded under the previous adapter become unreachable through ' +\n 'the new one.',\n scope: 'global',\n readPermission: 'setup.access',\n writePermission: 'setup.write',\n category: 'Infrastructure',\n order: 20,\n specifiers: [\n { type: 'group', id: 'adapter', label: 'Backend', required: false,\n description: 'Choose where uploaded files are stored.' },\n { type: 'select', key: 'adapter', label: 'Adapter', required: true, default: 'local',\n options: [\n { value: 'local', label: 'Local filesystem' },\n { value: 's3', label: 'S3 / S3-compatible' },\n ],\n },\n\n { type: 'group', id: 'local', label: 'Local', required: false,\n visible: \"${data.adapter === 'local'}\" },\n { type: 'text', key: 'local_root', label: 'Root directory', required: false,\n default: './.objectstack/data/uploads',\n description: 'Filesystem path under which files are stored. Relative paths resolve from the server CWD.',\n visible: \"${data.adapter === 'local'}\" },\n\n { type: 'group', id: 's3', label: 'S3', required: false,\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_bucket', label: 'Bucket', required: true,\n description: 'Shared host bucket. Per-environment files are namespaced via the projects/<environmentId>/ prefix.',\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_region', label: 'Region', required: true,\n description: 'Example: us-east-1',\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_endpoint', label: 'Endpoint', required: false,\n description: 'Custom endpoint for S3-compatible providers (R2, MinIO, Wasabi). Leave blank for AWS S3.',\n visible: \"${data.adapter === 's3'}\" },\n { type: 'text', key: 's3_access_key_id', label: 'Access key ID', required: true,\n visible: \"${data.adapter === 's3'}\" },\n { type: 'password', key: 's3_secret_access_key', label: 'Secret access key',\n required: true, encrypted: true,\n visible: \"${data.adapter === 's3'}\" },\n { type: 'toggle', key: 's3_force_path_style', label: 'Force path-style URLs',\n required: false, default: false,\n description: 'Enable for MinIO and most S3-compatible providers; disable for AWS S3.',\n visible: \"${data.adapter === 's3'}\" },\n\n { type: 'group', id: 'limits', label: 'Limits', required: false },\n { type: 'number', key: 'presigned_ttl', label: 'Presigned URL TTL (seconds)',\n required: false, default: 3600, min: 60, max: 604800 },\n { type: 'number', key: 'session_ttl', label: 'Upload session TTL (seconds)',\n required: false, default: 86400, min: 300, max: 604800,\n description: 'How long a chunked-upload session stays resumable.' },\n { type: 'number', key: 'max_upload_mb', label: 'Max upload size (MB)',\n required: false, default: 100, min: 1, max: 10240 },\n\n { type: 'action_button', id: 'test', label: 'Test connection',\n required: false, icon: 'Plug',\n handler: { kind: 'http', method: 'POST', url: '/api/settings/storage/test' } },\n ],\n};\n\n/** File Storage — local FS / S3-compatible backend configuration. */\nexport const storageSettingsManifest = manifest as unknown as SettingsManifest;\n\n/**\n * Built-in fallback action handler for `storage/test`. The real\n * implementation lives in `@objectstack/service-storage` and is\n * registered by `StorageServicePlugin` on `kernel:ready` (it overrides\n * this stub via `registerAction`). This fallback only validates the\n * form so the button is still useful when the storage plugin is\n * absent (e.g. in a unit-test kernel that mounts settings only).\n */\nexport const storageTestActionHandler: SettingsActionHandler = async ({ values }) => {\n const adapter = String(values.adapter ?? 'local');\n if (adapter === 'local') {\n const root = values.local_root as string | undefined;\n if (!root) {\n return { ok: false, severity: 'error', message: 'Configure a root directory before testing.' };\n }\n return {\n ok: true,\n severity: 'info',\n message: `Local adapter configured (root=${root}). Mount @objectstack/service-storage to exercise live I/O.`,\n };\n }\n if (adapter === 's3') {\n const missing: string[] = [];\n if (!values.s3_bucket) missing.push('s3_bucket');\n if (!values.s3_region) missing.push('s3_region');\n if (!values.s3_access_key_id) missing.push('s3_access_key_id');\n if (!values.s3_secret_access_key) missing.push('s3_secret_access_key');\n if (missing.length) {\n return { ok: false, severity: 'error', message: `Missing required field${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}` };\n }\n return {\n ok: true,\n severity: 'info',\n message: `S3 adapter configured (bucket=${values.s3_bucket}, region=${values.s3_region}). Mount @objectstack/service-storage to exercise live I/O.`,\n };\n }\n return { ok: false, severity: 'error', message: `Unknown adapter: ${adapter}` };\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/** Reference manifests bundled with service-settings. */\nexport { mailSettingsManifest, mailTestActionHandler } from './mail.manifest.js';\nexport { brandingSettingsManifest } from './branding.manifest.js';\nexport { featureFlagsSettingsManifest } from './feature-flags.manifest.js';\nexport { storageSettingsManifest, storageTestActionHandler } from './storage.manifest.js';\n\nimport { mailSettingsManifest } from './mail.manifest.js';\nimport { brandingSettingsManifest } from './branding.manifest.js';\nimport { featureFlagsSettingsManifest } from './feature-flags.manifest.js';\nimport { storageSettingsManifest } from './storage.manifest.js';\n\n/** Convenience aggregate — pass to `SettingsServicePlugin({ manifests })`. */\nexport const builtinSettingsManifests = [\n brandingSettingsManifest,\n mailSettingsManifest,\n storageSettingsManifest,\n featureFlagsSettingsManifest,\n];\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\n/**\n * English (en) — built-in settings manifest translations.\n *\n * Mirrors literals in `manifests/{mail,branding,feature-flags,storage}.manifest.ts`.\n * Keeping them explicit here lets the resolver chain (locale → fallback → literal)\n * always have at least an English entry to fall back to.\n */\nexport const en: TranslationData = {\n settingsCommon: {\n sourceLabels: {\n env: 'Env',\n global: 'Global',\n tenant: 'Tenant',\n user: 'User',\n default: 'Default',\n },\n },\n settings: {\n mail: {\n title: 'Mail Delivery',\n description: 'SMTP and transactional email provider configuration.',\n groups: {\n provider: { title: 'Provider', description: 'Choose how this workspace sends outbound email.' },\n smtp: { title: 'SMTP' },\n api_key: { title: 'API key' },\n from_address: { title: 'From address' },\n },\n keys: {\n provider: {\n label: 'Provider',\n options: {\n smtp: 'SMTP',\n sendgrid: 'SendGrid',\n ses: 'Amazon SES',\n postmark: 'Postmark',\n },\n },\n smtp_host: { label: 'Host', help: 'Example: smtp.example.com' },\n smtp_port: { label: 'Port' },\n smtp_secure: { label: 'Use TLS' },\n smtp_user: { label: 'Username' },\n smtp_password: { label: 'Password' },\n api_key: { label: 'API key' },\n from_email: { label: 'From email', help: 'Example: no-reply@example.com' },\n from_name: { label: 'From name' },\n },\n actions: {\n test: { label: 'Send test email' },\n },\n },\n\n branding: {\n title: 'Branding',\n description: 'Workspace name, logo, and accent colour.',\n groups: {\n identity: { title: 'Identity' },\n appearance: { title: 'Appearance' },\n },\n keys: {\n workspace_name: { label: 'Workspace name' },\n support_email: { label: 'Support email', help: 'Example: support@example.com' },\n theme_mode: {\n label: 'Default theme',\n options: { light: 'Light', dark: 'Dark', system: 'Match system' },\n },\n accent_color: { label: 'Accent colour' },\n logo_url: { label: 'Logo URL', help: 'Example: https://…/logo.svg' },\n },\n },\n\n feature_flags: {\n title: 'Feature Flags',\n description: 'Toggle experimental and beta features for this workspace.',\n groups: {\n productivity: { title: 'Productivity' },\n collaboration: { title: 'Collaboration' },\n },\n keys: {\n ai_enabled: {\n label: 'AI Assistant',\n help: 'Enables the in-app AI assistant panel.',\n },\n kanban_swimlanes: { label: 'Kanban swimlanes' },\n realtime_cursors: { label: 'Realtime cursors' },\n inline_comments: { label: 'Inline comments' },\n },\n },\n\n storage: {\n title: 'File Storage',\n description:\n 'Backend used for attachments, exports, and user uploads. ' +\n '⚠ Switching adapter does not migrate existing files — files ' +\n 'uploaded under the previous adapter become unreachable through ' +\n 'the new one.',\n groups: {\n adapter: { title: 'Backend', description: 'Choose where uploaded files are stored.' },\n local: { title: 'Local' },\n s3: { title: 'S3' },\n limits: { title: 'Limits' },\n },\n keys: {\n adapter: {\n label: 'Adapter',\n options: { local: 'Local filesystem', s3: 'S3 / S3-compatible' },\n },\n local_root: { label: 'Root directory',\n help: 'Filesystem path under which files are stored. Relative paths resolve from the server CWD.' },\n s3_bucket: { label: 'Bucket',\n help: 'Shared host bucket. Per-environment files are namespaced via the projects/<environmentId>/ prefix.' },\n s3_region: { label: 'Region', help: 'Example: us-east-1' },\n s3_endpoint: { label: 'Endpoint',\n help: 'Custom endpoint for S3-compatible providers (R2, MinIO, Wasabi). Leave blank for AWS S3.' },\n s3_access_key_id: { label: 'Access key ID' },\n s3_secret_access_key: { label: 'Secret access key' },\n s3_force_path_style: { label: 'Force path-style URLs',\n help: 'Enable for MinIO and most S3-compatible providers; disable for AWS S3.' },\n presigned_ttl: { label: 'Presigned URL TTL (seconds)' },\n session_ttl: { label: 'Upload session TTL (seconds)',\n help: 'How long a chunked-upload session stays resumable.' },\n max_upload_mb: { label: 'Max upload size (MB)' },\n },\n actions: {\n test: { label: 'Test connection' },\n },\n },\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\n/**\n * 简体中文 (zh-CN) — built-in settings manifest translations.\n */\nexport const zhCN: TranslationData = {\n settingsCommon: {\n sourceLabels: {\n env: '环境变量',\n global: '全局',\n tenant: '租户',\n user: '用户',\n default: '默认',\n },\n },\n settings: {\n mail: {\n title: '邮件投递',\n description: 'SMTP 与事务性邮件服务商配置。',\n groups: {\n provider: { title: '服务商', description: '选择此工作区如何发送邮件。' },\n smtp: { title: 'SMTP' },\n api_key: { title: 'API 密钥' },\n from_address: { title: '发件地址' },\n },\n keys: {\n provider: {\n label: '服务商',\n options: {\n smtp: 'SMTP',\n sendgrid: 'SendGrid',\n ses: 'Amazon SES',\n postmark: 'Postmark',\n },\n },\n smtp_host: { label: '主机', help: '示例:smtp.example.com' },\n smtp_port: { label: '端口' },\n smtp_secure: { label: '启用 TLS' },\n smtp_user: { label: '用户名' },\n smtp_password: { label: '密码' },\n api_key: { label: 'API 密钥' },\n from_email: { label: '发件地址', help: '示例:no-reply@example.com' },\n from_name: { label: '发件人名称' },\n },\n actions: {\n test: { label: '发送测试邮件' },\n },\n },\n\n branding: {\n title: '品牌',\n description: '工作区名称、Logo 与主题色。',\n groups: {\n identity: { title: '身份' },\n appearance: { title: '外观' },\n },\n keys: {\n workspace_name: { label: '工作区名称' },\n support_email: { label: '客服邮箱', help: '示例:support@example.com' },\n theme_mode: {\n label: '默认主题',\n options: { light: '浅色', dark: '深色', system: '跟随系统' },\n },\n accent_color: { label: '主题色' },\n logo_url: { label: 'Logo 链接', help: '示例:https://…/logo.svg' },\n },\n },\n\n feature_flags: {\n title: '功能开关',\n description: '为当前工作区开启实验性与测试功能。',\n groups: {\n productivity: { title: '生产力' },\n collaboration: { title: '协作' },\n },\n keys: {\n ai_enabled: {\n label: 'AI 助手',\n help: '启用应用内 AI 助手面板。',\n },\n kanban_swimlanes: { label: '看板泳道' },\n realtime_cursors: { label: '实时光标' },\n inline_comments: { label: '行内评论' },\n },\n },\n\n storage: {\n title: '文件存储',\n description:\n '附件、导出文件与用户上传所使用的存储后端。' +\n '⚠ 切换适配器不会迁移已有文件 —— 通过旧适配器上传的文件,在新适配器中将不可访问。',\n groups: {\n adapter: { title: '存储后端', description: '选择上传文件的存放位置。' },\n local: { title: '本地' },\n s3: { title: 'S3' },\n limits: { title: '限制' },\n },\n keys: {\n adapter: {\n label: '适配器',\n options: { local: '本地文件系统', s3: 'S3 / S3 兼容' },\n },\n local_root: { label: '根目录',\n help: '文件存放的文件系统路径。相对路径相对于服务进程的工作目录。' },\n s3_bucket: { label: 'Bucket',\n help: '共享主机 Bucket。各项目的文件通过 projects/<environmentId>/ 前缀进行隔离。' },\n s3_region: { label: '区域', help: '示例:us-east-1' },\n s3_endpoint: { label: 'Endpoint',\n help: 'S3 兼容服务(R2、MinIO、Wasabi)的自定义 Endpoint;AWS S3 请留空。' },\n s3_access_key_id: { label: 'Access Key ID' },\n s3_secret_access_key: { label: 'Secret Access Key' },\n s3_force_path_style: { label: '强制路径风格 URL',\n help: 'MinIO 与大多数 S3 兼容服务请开启;AWS S3 请关闭。' },\n presigned_ttl: { label: '预签名 URL 有效期(秒)' },\n session_ttl: { label: '分片上传会话有效期(秒)',\n help: '分片上传会话保持可续传的时长。' },\n max_upload_mb: { label: '单文件最大上传(MB)' },\n },\n actions: {\n test: { label: '测试连接' },\n },\n },\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\n/**\n * 日本語 (ja-JP) — built-in settings manifest translations.\n */\nexport const jaJP: TranslationData = {\n settingsCommon: {\n sourceLabels: {\n env: '環境変数',\n global: 'グローバル',\n tenant: 'テナント',\n user: 'ユーザー',\n default: 'デフォルト',\n },\n },\n settings: {\n mail: {\n title: 'メール配信',\n description: 'SMTP およびトランザクションメールプロバイダー設定。',\n groups: {\n provider: { title: 'プロバイダー', description: 'このワークスペースの送信方法を選択します。' },\n smtp: { title: 'SMTP' },\n api_key: { title: 'API キー' },\n from_address: { title: '差出人アドレス' },\n },\n keys: {\n provider: {\n label: 'プロバイダー',\n options: {\n smtp: 'SMTP',\n sendgrid: 'SendGrid',\n ses: 'Amazon SES',\n postmark: 'Postmark',\n },\n },\n smtp_host: { label: 'ホスト', help: '例: smtp.example.com' },\n smtp_port: { label: 'ポート' },\n smtp_secure: { label: 'TLS を使用' },\n smtp_user: { label: 'ユーザー名' },\n smtp_password: { label: 'パスワード' },\n api_key: { label: 'API キー' },\n from_email: { label: '差出人アドレス', help: '例: no-reply@example.com' },\n from_name: { label: '差出人名' },\n },\n actions: {\n test: { label: 'テストメール送信' },\n },\n },\n\n branding: {\n title: 'ブランディング',\n description: 'ワークスペース名・ロゴ・アクセントカラー。',\n groups: {\n identity: { title: 'アイデンティティ' },\n appearance: { title: '外観' },\n },\n keys: {\n workspace_name: { label: 'ワークスペース名' },\n support_email: { label: 'サポートメール', help: '例: support@example.com' },\n theme_mode: {\n label: 'デフォルトテーマ',\n options: { light: 'ライト', dark: 'ダーク', system: 'システムに従う' },\n },\n accent_color: { label: 'アクセントカラー' },\n logo_url: { label: 'ロゴ URL', help: '例: https://…/logo.svg' },\n },\n },\n\n feature_flags: {\n title: '機能フラグ',\n description: 'このワークスペースで実験的・ベータ機能を切替えます。',\n groups: {\n productivity: { title: '生産性' },\n collaboration: { title: 'コラボレーション' },\n },\n keys: {\n ai_enabled: {\n label: 'AI アシスタント',\n help: 'アプリ内 AI アシスタントパネルを有効化します。',\n },\n kanban_swimlanes: { label: 'カンバンのスイムレーン' },\n realtime_cursors: { label: 'リアルタイムカーソル' },\n inline_comments: { label: 'インラインコメント' },\n },\n },\n\n storage: {\n title: 'ファイルストレージ',\n description:\n '添付ファイル・エクスポート・ユーザーアップロードに使用するバックエンド。' +\n '⚠ アダプターを切替えても既存ファイルは移行されません。以前のアダプターでアップロードされたファイルは新しいアダプターからアクセスできなくなります。',\n groups: {\n adapter: { title: 'バックエンド', description: 'アップロードファイルの保存先を選択します。' },\n local: { title: 'ローカル' },\n s3: { title: 'S3' },\n limits: { title: '制限' },\n },\n keys: {\n adapter: {\n label: 'アダプター',\n options: { local: 'ローカルファイルシステム', s3: 'S3 / S3 互換' },\n },\n local_root: { label: 'ルートディレクトリ',\n help: 'ファイルを保存するファイルシステムパス。相対パスはサーバーの CWD から解決されます。' },\n s3_bucket: { label: 'バケット',\n help: '共有ホストバケット。プロジェクト毎のファイルは projects/<environmentId>/ プレフィックスで分離されます。' },\n s3_region: { label: 'リージョン', help: '例: us-east-1' },\n s3_endpoint: { label: 'エンドポイント',\n help: 'S3 互換プロバイダ (R2, MinIO, Wasabi) のカスタムエンドポイント。AWS S3 の場合は空欄。' },\n s3_access_key_id: { label: 'アクセスキー ID' },\n s3_secret_access_key: { label: 'シークレットアクセスキー' },\n s3_force_path_style: { label: 'パススタイル URL を強制',\n help: 'MinIO や多くの S3 互換プロバイダで有効化。AWS S3 では無効化。' },\n presigned_ttl: { label: '署名付き URL の有効期間 (秒)' },\n session_ttl: { label: 'アップロードセッション TTL (秒)',\n help: 'チャンクアップロードの再開可能期間。' },\n max_upload_mb: { label: '最大アップロードサイズ (MB)' },\n },\n actions: {\n test: { label: '接続テスト' },\n },\n },\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Built-in Settings translations.\n *\n * Mirrors the CRM example's `src/translations/{en,zh-CN,ja-JP}.ts` convention —\n * one file per locale, aggregated into a `TranslationBundle` here.\n *\n * Hosts merge `settingsBuiltinTranslations` into the i18next resource tree\n * under whatever namespace makes sense (the console wires it as `system`),\n * making keys resolvable as `<ns>.settings.<namespace>.{title,description,...}`.\n */\n\nimport type { TranslationBundle } from '@objectstack/spec/system';\nimport { en } from './en.js';\nimport { zhCN } from './zh-CN.js';\nimport { jaJP } from './ja-JP.js';\n\nexport { en, zhCN, jaJP };\n\nexport const settingsBuiltinTranslations: TranslationBundle = {\n en,\n 'zh-CN': zhCN,\n 'ja-JP': jaJP,\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { IHttpServer, IDataEngine } from '@objectstack/spec/contracts';\nimport type { SettingsManifest } from '@objectstack/spec/system';\nimport { SettingsService } from './settings-service.js';\nimport type { ICryptoProvider } from '@objectstack/spec/contracts';\nimport type { SettingsAuditSink, SettingsAuditWriter, SettingsEngine, SettingsSecretStore } from './settings-service.types.js';\nimport type { CryptoAdapter } from './crypto-adapter.js';\nimport { InMemoryCryptoProvider } from './in-memory-crypto-provider.js';\nimport { registerSettingsRoutes } from './settings-routes.js';\nimport {\n settingsObjects,\n settingsPluginManifestHeader,\n SETTINGS_PLUGIN_ID,\n SETTINGS_PLUGIN_VERSION,\n} from './manifest.js';\nimport {\n builtinSettingsManifests,\n mailTestActionHandler,\n storageTestActionHandler,\n} from './manifests/index.js';\nimport { settingsBuiltinTranslations } from './translations/index.js';\n\n/** Configuration options for the SettingsServicePlugin. */\nexport interface SettingsServicePluginOptions {\n /**\n * Pre-register these manifests at boot. When omitted, the bundled\n * builtin manifests (mail / branding / feature_flags) are loaded so\n * a host gets a working Settings hub out of the box. Pass an empty\n * array to opt out entirely.\n */\n manifests?: SettingsManifest[];\n /** Override the default crypto adapter. */\n crypto?: CryptoAdapter;\n\n /**\n * Phase 3 KMS hook. When provided, encrypted specifier values are\n * routed through this provider into `sys_secret`; `sys_setting.value_enc`\n * holds the handle id only. Defaults to `InMemoryCryptoProvider`\n * (NOT suitable for production secrets — replace with an AWS / GCP\n * KMS-backed implementation).\n */\n cryptoProvider?: ICryptoProvider;\n /** Override the default base path (`/api/settings`). */\n basePath?: string;\n /** Disable REST route registration. */\n registerRoutes?: boolean;\n /** Override the env source. Defaults to `process.env`. */\n env?: Record<string, string | undefined>;\n /**\n * Action handlers to register at boot, keyed by namespace and action\n * id. The bundled `mail.test` handler is registered automatically\n * unless this object is provided.\n */\n actionHandlers?: Record<string, Record<string, import('./settings-service.types.js').SettingsActionHandler>>;\n}\n\n/**\n * SettingsServicePlugin — wires the SettingsService into the kernel.\n *\n * 1. `init`: instantiate the service, register it under `'settings'`,\n * and ship `sys_setting` to the manifest service so the engine\n * auto-provisions the table.\n * 2. `start` → `kernel:ready`: bind the data engine (when present),\n * wire the audit sink (when present), mount REST routes.\n */\nexport class SettingsServicePlugin implements Plugin {\n name = SETTINGS_PLUGIN_ID;\n version = SETTINGS_PLUGIN_VERSION;\n type = 'standard' as const;\n\n private readonly opts: SettingsServicePluginOptions;\n private service: SettingsService | null = null;\n\n constructor(opts: SettingsServicePluginOptions = {}) {\n this.opts = {\n ...opts,\n manifests: opts.manifests ?? builtinSettingsManifests,\n actionHandlers: opts.actionHandlers ?? {\n mail: { test: mailTestActionHandler },\n storage: { test: storageTestActionHandler },\n },\n };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.service = new SettingsService({\n crypto: this.opts.crypto,\n env: this.opts.env,\n });\n for (const m of this.opts.manifests ?? []) this.service.registerManifest(m);\n for (const [ns, handlers] of Object.entries(this.opts.actionHandlers ?? {})) {\n for (const [id, fn] of Object.entries(handlers)) {\n this.service.registerAction(ns, id, fn);\n }\n }\n\n ctx.registerService('settings', this.service);\n ctx.logger?.info?.(\n `SettingsServicePlugin: registered (manifests=${this.opts.manifests?.length ?? 0})`,\n );\n\n // Register the K/V object so the engine creates the table.\n try {\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...settingsPluginManifestHeader,\n objects: settingsObjects,\n });\n } catch {\n // manifest service is optional — skip in lean test kernels.\n }\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (!this.service) return;\n\n ctx.hook('kernel:ready', async () => {\n // Contribute built-in settings translations into the i18n service.\n // Done in `kernel:ready` (not `init`) because the i18n service plugin\n // is typically registered AFTER capability-loaded service plugins.\n try {\n const i18n = ctx.getService<{\n loadTranslations: (locale: string, data: Record<string, unknown>) => void;\n }>('i18n');\n let loaded = 0;\n for (const [locale, data] of Object.entries(settingsBuiltinTranslations)) {\n if (data && typeof data === 'object') {\n try {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n loaded++;\n } catch (err: any) {\n ctx.logger?.warn?.(\n `SettingsServicePlugin: failed to load translations for '${locale}': ${err?.message ?? err}`,\n );\n }\n }\n }\n if (loaded > 0) {\n ctx.logger?.info?.(\n `SettingsServicePlugin: contributed built-in translations (${loaded} locale${loaded > 1 ? 's' : ''})`,\n );\n }\n } catch {\n // i18n service not registered — manifest literals remain authoritative.\n }\n\n // Late-bind the data engine.\n let engine: IDataEngine | null = null;\n try {\n engine = ctx.getService<IDataEngine>('objectql');\n } catch {\n // ok — fall back to in-memory.\n }\n if (engine) {\n // Late-bind the engine + audit sink on the existing service\n // instance. We avoid re-registering the service because the\n // kernel disallows `registerService` for an already-registered\n // name.\n this.service!.bindEngine(\n engine as unknown as SettingsEngine,\n this.buildAuditSink(ctx, engine),\n {\n secretStore: this.buildSecretStore(engine),\n auditWriter: this.buildAuditWriter(ctx, engine),\n cryptoProvider: this.opts.cryptoProvider ?? new InMemoryCryptoProvider(),\n },\n );\n }\n\n if (this.opts.registerRoutes === false) return;\n\n let http: IHttpServer | null = null;\n try {\n http = ctx.getService<IHttpServer>('http-server');\n } catch {\n // ok — no HTTP server in this deployment.\n }\n if (!http) {\n ctx.logger?.warn?.(\n 'SettingsServicePlugin: no HTTP server available — REST routes not registered. ' +\n 'SettingsService is still reachable via kernel.getService(\"settings\").',\n );\n return;\n }\n registerSettingsRoutes(http, this.service!, { basePath: this.opts.basePath });\n ctx.logger?.info?.(\n 'SettingsServicePlugin: REST routes registered at ' + (this.opts.basePath ?? '/api/settings'),\n );\n });\n }\n\n /** Glue an `engine.insert('sys_audit_log', …)` audit sink. */\n private buildAuditSink(ctx: PluginContext, engine: IDataEngine): SettingsAuditSink {\n return {\n record: async (entry) => {\n try {\n await (engine as any).insert?.('sys_audit_log', {\n actor_id: entry.userId ?? null,\n entity_type: 'sys_setting',\n entity_id: `${entry.namespace}.${entry.key}`,\n action: entry.action,\n payload: {\n namespace: entry.namespace,\n key: entry.key,\n scope: entry.scope,\n encrypted: entry.encrypted,\n digest: entry.valueDigest,\n },\n request_id: entry.requestId ?? null,\n occurred_at: new Date().toISOString(),\n });\n } catch (err: any) {\n ctx.logger?.warn?.('SettingsServicePlugin: audit record failed: ' + (err?.message ?? err));\n }\n },\n };\n }\n\n /**\n * Phase 3: build a `sys_secret`-backed implementation of\n * `SettingsSecretStore`. The store bypasses the tenant audit\n * warning because secrets are scoped through their owning\n * `sys_setting` row (which already carries the tenant context).\n */\n private buildSecretStore(engine: IDataEngine): SettingsSecretStore {\n const eng: any = engine;\n return {\n async insert(row) {\n await eng.insert('sys_secret', row, { bypassTenantAudit: true });\n return { id: row.id };\n },\n async get(id) {\n const rows = await eng.find('sys_secret', {\n where: { id },\n limit: 1,\n bypassTenantAudit: true,\n });\n const row = Array.isArray(rows) ? rows[0] : rows?.data?.[0];\n return row ?? null;\n },\n async update(id, patch) {\n await eng.update('sys_secret', {\n where: { id },\n data: patch,\n bypassTenantAudit: true,\n });\n },\n };\n }\n\n /**\n * Phase 3: append-only writer for `sys_setting_audit`. Failures here\n * MUST NOT abort the settings write, so all calls are wrapped in a\n * try/catch and reported through the plugin logger.\n */\n private buildAuditWriter(ctx: PluginContext, engine: IDataEngine): SettingsAuditWriter {\n const eng: any = engine;\n return {\n write: async (entry) => {\n try {\n await eng.insert('sys_setting_audit', {\n namespace: entry.namespace,\n key: entry.key,\n scope: entry.scope,\n action: entry.action,\n source: entry.source ?? 'api',\n actor_id: entry.actorId ?? null,\n old_hash: entry.oldHash ?? null,\n new_hash: entry.newHash ?? null,\n encrypted: !!entry.encrypted,\n request_id: entry.requestId ?? null,\n reason: entry.reason ?? null,\n created_at: new Date().toISOString(),\n }, { bypassTenantAudit: true });\n } catch (err: any) {\n ctx.logger?.warn?.('SettingsServicePlugin: setting-audit write failed: ' + (err?.message ?? err));\n }\n },\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC6BO,IAAM,oBAAN,MAAiD;AAAA,EACtD,MAAM,QAAQ,WAAoC;AAChD,WAAO,SAAS,OAAO,KAAK,WAAW,MAAM,EAAE,SAAS,QAAQ;AAAA,EAClE;AAAA,EACA,MAAM,QAAQ,YAAqC;AACjD,QAAI,CAAC,WAAW,WAAW,MAAM,GAAG;AAElC,aAAO;AAAA,IACT;AACA,WAAO,OAAO,KAAK,WAAW,MAAM,CAAC,GAAG,QAAQ,EAAE,SAAS,MAAM;AAAA,EACnE;AAAA,EACA,OAAO,WAA2B;AAEhC,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,WAAK,UAAU,WAAW,CAAC;AAC3B,UAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,SAAU;AAAA,IACxE;AACA,WAAO,WAAW,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EAClD;AACF;;;AC0JO,SAAS,SAAS,WAAmB,KAAqB;AAC/D,QAAM,OAAO,GAAG,SAAS,IAAI,GAAG,GAAG,QAAQ,SAAS,GAAG,EAAE,YAAY;AACrE,SAAO;AACT;AAGO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAE7C,YACW,WACA,KACA,SAAS,iBAClB;AACA,UAAM,YAAY,SAAS,IAAI,GAAG,gBAAgB,MAAM,IAAI;AAJnD;AACA;AACA;AAJX,SAAS,OAAO;AAAA,EAOhB;AACF;AAGO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAE/C,YAAqB,WAAmB;AACtC,UAAM,kDAAkD,SAAS,IAAI;AADlD;AADrB,SAAS,OAAO;AAAA,EAGhB;AACF;AAGO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAEzC,YAAqB,WAA4B,KAAa;AAC5D,UAAM,QAAQ,GAAG,kCAAkC,SAAS,IAAI;AAD7C;AAA4B;AADjD,SAAS,OAAO;AAAA,EAGhB;AACF;;;AC7MA,IAAM,iBAAiB;AAOvB,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAkBM,IAAM,kBAAN,MAAsB;AAAA,EAkB3B,YAAY,OAA+B,CAAC,GAAG;AAT/C,SAAiB,WAAW,oBAAI,IAAgC;AAEhE;AAAA,SAAiB,SAAwB,CAAC;AAE1C;AAAA,SAAiB,cAAc,oBAAI,IAGhC;AAGD,SAAK,SAAS,KAAK;AACnB,SAAK,SAAS,KAAK,UAAU,IAAI,kBAAkB;AACnD,SAAK,iBAAiB,KAAK;AAC3B,SAAK,cAAc,KAAK;AACxB,SAAK,QAAQ,KAAK;AAClB,SAAK,cAAc,KAAK;AACxB,SAAK,MAAM,KAAK,QAAQ,OAAO,YAAY,cAAc,QAAQ,MAAM,CAAC;AACxE,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,WACE,QACA,OACA,QAKM;AACN,SAAK,SAAS;AACd,QAAI,MAAO,MAAK,QAAQ;AACxB,QAAI,QAAQ,YAAa,MAAK,cAAc,OAAO;AACnD,QAAI,QAAQ,YAAa,MAAK,cAAc,OAAO;AACnD,QAAI,QAAQ,eAAgB,MAAK,iBAAiB,OAAO;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,UAAU,OAAmD;AACnE,YAAQ,OAAO;AAAA,MACb,KAAK;AAAW,eAAO;AAAA,MACvB,KAAK;AAAW,eAAO;AAAA,MACvB,KAAK;AAAW,eAAO;AAAA,MACvB;AAAgB,eAAO;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,UACE,WACA,SACqB;AACrB,UAAM,QAAQ,EAAE,IAAI,WAAW,QAAQ;AACvC,SAAK,YAAY,IAAI,KAAK;AAC1B,WAAO,MAAM;AACX,WAAK,YAAY,OAAO,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,OAAkC;AACnD,QAAI,KAAK,YAAY,SAAS,EAAG;AACjC,eAAW,OAAO,KAAK,aAAa;AAClC,UAAI,IAAI,MAAM,IAAI,OAAO,MAAM,UAAW;AAC1C,UAAI;AACF,YAAI,QAAQ,KAAK;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiBA,WAAkC;AACjD,UAAM,SAAS,oBAAI,IAA4B;AAC/C,UAAM,gBAAgB,oBAAI,IAAY;AACtC,UAAM,WAAW,oBAAI,IAAqB;AAC1C,UAAM,eAAeA,UAAS,SAAS;AACvC,eAAW,QAAQA,UAAS,YAAY;AACtC,UAAI,CAAC,KAAK,OAAO,kBAAkB,IAAI,KAAK,IAAI,EAAG;AACnD,aAAO,IAAI,KAAK,KAAK,KAAK,SAAS,YAAY;AAC/C,UAAI,KAAK,aAAa,KAAK,SAAS,WAAY,eAAc,IAAI,KAAK,GAAG;AAC1E,UAAI,OAAO,KAAK,YAAY,YAAa,UAAS,IAAI,KAAK,KAAK,KAAK,OAAO;AAAA,IAC9E;AACA,UAAM,OAAO,KAAK,SAAS,IAAIA,UAAS,SAAS;AACjD,UAAM,UAAU,MAAM,WAAW,oBAAI,IAAmC;AACxE,SAAK,SAAS,IAAIA,UAAS,WAAW,EAAE,UAAAA,WAAU,QAAQ,eAAe,UAAU,QAAQ,CAAC;AAAA,EAC9F;AAAA;AAAA,EAGA,YAAY,WAAqC;AAC/C,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,WAAO,IAAI;AAAA,EACb;AAAA;AAAA,EAGA,cAAc,MAAuB,CAAC,GAAuB;AAC3D,UAAM,QAAQ,IAAI,IAAI,IAAI,eAAe,CAAC,CAAC;AAC3C,UAAM,MAAM,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ;AAEpE,QAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,WAAO,IAAI,OAAO,CAAC,MAAM,MAAM,IAAI,EAAE,kBAAkB,cAAc,CAAC;AAAA,EACxE;AAAA;AAAA,EAGA,eAAe,WAAmB,UAAkB,SAAsC;AACxF,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,QAAI,QAAQ,IAAI,UAAU,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IACJ,WACA,KACA,MAAuB,CAAC,GACU;AAClC,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,QAAI,CAAC,IAAI,OAAO,IAAI,GAAG,EAAG,OAAM,IAAI,gBAAgB,WAAW,GAAG;AAGlE,UAAM,UAAU,SAAS,WAAW,GAAG;AACvC,UAAM,SAAS,KAAK,IAAI,OAAO;AAC/B,QAAI,OAAO,WAAW,UAAU;AAC9B,YAAMC,OAAM,IAAI,SAAS,IAAI,GAAG;AAChC,YAAM,QAAQ,eAAe,QAAQA,IAAG;AACxC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,cAAc,gBAAgB,OAAO;AAAA,QACrC,cAAc;AAAA,UACZ,EAAE,OAAO,OAAO,OAAO,QAAQ,MAAM,cAAc,gBAAgB,OAAO,IAAI,WAAW,KAAK;AAAA,QAChG;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,OAAO,IAAI,GAAG;AAGhC,UAAM,OAAO,MAAM,KAAK,SAAS,WAAW,UAAU,SAAS,IAAI,UAAU,OAAO,IAAI;AAOxF,UAAM,QAA2D,CAAC;AAElE,UAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,UAAU,QAAQ;AACxE,QAAI,WAAW;AACb,YAAM,QAAQ,MAAM,KAAK,eAAe,SAAS;AACjD,YAAM,KAAK;AAAA,QACT,OAAO;AAAA,QACP;AAAA,QACA,QAAQ,CAAC,CAAC,UAAU;AAAA,QACpB,cAAc,UAAU,iBAAiB;AAAA,MAC3C,CAAC;AAAA,IACH;AAEA,QAAI,UAAU,YAAY,UAAU,QAAQ;AAC1C,YAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,UAAU,QAAQ;AACxE,UAAI,WAAW;AACb,cAAM,KAAK;AAAA,UACT,OAAO;AAAA,UACP,OAAO,MAAM,KAAK,eAAe,SAAS;AAAA,UAC1C,QAAQ,CAAC,CAAC,UAAU;AAAA,UACpB,cAAc,UAAU,iBAAiB;AAAA,QAC3C,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,UAAU,QAAQ;AACpB,YAAM,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,UAAU,MAAM;AACpE,UAAI,SAAS;AACX,cAAM,KAAK;AAAA,UACT,OAAO;AAAA,UACP,OAAO,MAAM,KAAK,eAAe,OAAO;AAAA,QAC1C,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,SAAS,IAAI,GAAG;AAChC,UAAM,KAAK,EAAE,OAAO,WAAW,OAAO,OAAO,KAAK,CAAC;AAInD,UAAM,cAAc,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,IAAI;AACvD,UAAM,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,UAAU,QAAQ,EAAE,UAAU,MAAS,KAAK,MAAM,MAAM,SAAS,CAAC;AACxG,cAAU,YAAY;AAEtB,WAAO;AAAA,MACL,OAAO,UAAU;AAAA,MACjB,QAAQ,UAAU;AAAA,MAClB,QAAQ,CAAC,CAAC;AAAA,MACV,cAAc,aAAa;AAAA,MAC3B,cAAc;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,aACJ,WACA,MAAuB,CAAC,GACW;AACnC,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AAEnD,UAAM,SAA+C,CAAC;AACtD,eAAW,CAAC,GAAG,KAAK,IAAI,QAAQ;AAC9B,aAAO,GAAG,IAAI,MAAM,KAAK,IAAI,WAAW,KAAK,GAAG;AAAA,IAClD;AACA,WAAO,EAAE,UAAU,IAAI,UAAU,OAAO;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,aACJ,WACA,OAGI,CAAC,GAQJ;AACD,UAAM,MAAM,KAAK,OAAO,CAAC;AACzB,QAAI,WAAc,MAAM,KAAK,WAAc,WAAW,KAAK,KAAK,KAAK;AAErE,UAAM,MAAM,KAAK,UAAU,WAAW,MAAM;AAE1C,WAAK,KAAK,WAAc,WAAW,KAAK,KAAK,KAAK,EAAE,KAAK,CAAC,SAAS;AACjE,mBAAW;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA,IAAI,UAAU;AACZ,eAAO;AAAA,MACT;AAAA,MACA,IAAuB,KAAc;AACnC,eAAO,SAAS,GAAG;AAAA,MACrB;AAAA,MACA,UAAU,CAAC,YAAY,KAAK,UAAU,WAAW,OAAO;AAAA,MACxD,SAAS,YAAY;AACnB,mBAAW,MAAM,KAAK,WAAc,WAAW,KAAK,KAAK,KAAK;AAAA,MAChE;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAc,WACZ,WACA,KACA,OACY;AACZ,UAAM,UAAU,MAAM,KAAK,aAAa,WAAW,GAAG;AACtD,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,MAAM,EAAG,KAAI,CAAC,IAAI,EAAE;AAChE,WAAO,QAAQ,MAAM,GAAG,IAAK;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IACJ,WACA,KACA,OACA,MAAuB,CAAC,GACO;AAC/B,YAAQ,MAAM,KAAK,QAAQ,WAAW,EAAE,CAAC,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,GAAG;AAAA,EACnE;AAAA;AAAA,EAGA,MAAM,QACJ,WACA,OACA,MAAuB,CAAC,GACuB;AAC/C,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AAGnD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,CAAC,IAAI,OAAO,IAAI,GAAG,EAAG,OAAM,IAAI,gBAAgB,WAAW,GAAG;AAClE,YAAM,SAAS,KAAK,IAAI,SAAS,WAAW,GAAG,CAAC;AAChD,UAAI,OAAO,WAAW,SAAU,OAAM,IAAI,oBAAoB,WAAW,GAAG;AAM5E,YAAM,QAAQ,IAAI,OAAO,IAAI,GAAG;AAChC,YAAM,OAAO,MAAM,KAAK,SAAS,WAAW,UAAU,SAAS,IAAI,UAAU,OAAO,IAAI;AACxF,YAAM,QAAQ,KAAK;AAAA,QACjB,CAAC,MACC,EAAE,QAAQ,OACV,EAAE,WAAW,QACb,KAAK,UAAU,EAAE,KAAK,IAAI,KAAK,UAAU,KAAK;AAAA,MAClD;AACA,UAAI,OAAO;AACT,cAAM,IAAI,oBAAoB,WAAW,KAAK,aAAa,MAAM,KAAK,EAAE;AAAA,MAC1E;AAAA,IACF;AAEA,eAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACnD,YAAM,QAAQ,IAAI,OAAO,IAAI,GAAG;AAIhC,YAAM,SAAS,UAAU,SAAS,IAAI,UAAU,OAAO;AACvD,YAAM,cAAc,IAAI,cAAc,IAAI,GAAG;AAC7C,YAAM,SAAS,aAAa,QAAQ,OAAO,aAAa;AAExD,UAAI,cAA8B;AAClC,UAAI,YAA2B;AAC/B,UAAI,SAAS;AAEb,UAAI,CAAC,QAAQ;AACX,YAAI,aAAa;AACf,gBAAM,QAAQ,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,QAAQ;AAK/E,cAAI,KAAK,kBAAkB,KAAK,aAAa;AAC3C,kBAAM,SAAS,MAAM,KAAK,eAAe,QAAQ,OAAO;AAAA,cACtD;AAAA,cACA;AAAA,cACA,UAAU,IAAI;AAAA,YAChB,CAAC;AACD,kBAAM,KAAK,YAAY,OAAO;AAAA,cAC5B,IAAI,OAAO;AAAA,cACX;AAAA,cACA;AAAA,cACA,YAAY,OAAO;AAAA,cACnB,KAAK,OAAO;AAAA,cACZ,SAAS,OAAO;AAAA,cAChB,YAAY,OAAO;AAAA,YACrB,CAAC;AACD,wBAAY,OAAO;AACnB,qBAAS,KAAK,eAAe,OAAO,KAAK;AAAA,UAC3C,OAAO;AACL,wBAAY,MAAM,KAAK,OAAO,QAAQ,OAAO,EAAE,WAAW,IAAI,CAAC;AAC/D,qBAAS,KAAK,OAAO,OAAO,KAAK;AAAA,UACnC;AAAA,QACF,OAAO;AACL,wBAAc;AACd,mBAAS,KAAK,OAAO,OAAO,gBAAgB,QAAQ,CAAC;AAAA,QACvD;AAAA,MACF;AAEA,YAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,OAAO;AAAA,QACP,WAAW;AAAA,QACX,WAAW;AAAA,QACX,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,QACnC,YAAY,IAAI,UAAU;AAAA,MAC5B,CAAC;AAED,UAAI,KAAK,OAAO;AACd,cAAM,KAAK,MAAM,OAAO;AAAA,UACtB;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ,IAAI;AAAA,UACZ,QAAQ,SAAS,UAAU;AAAA,UAC3B,aAAa,cAAc,gBAAgB,SAAS,MAAM;AAAA,UAC1D,WAAW;AAAA,UACX,WAAW,IAAI;AAAA,QACjB,CAAC;AAAA,MACH;AAEA,UAAI,KAAK,aAAa;AACpB,YAAI;AACF,gBAAM,KAAK,YAAY,MAAM;AAAA,YAC3B;AAAA,YACA;AAAA,YACA;AAAA,YACA,QAAQ,SAAS,UAAU;AAAA,YAC3B,QAAQ;AAAA,YACR,SAAS,IAAI;AAAA,YACb,SAAS;AAAA,YACT,SAAS,SAAS,OAAO;AAAA,YACzB,WAAW;AAAA,YACX,WAAW,IAAI;AAAA,UACjB,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,WAAK,WAAW;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,SAAS,UAAU;AAAA,QAC3B,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC7B,CAAC;AAAA,IACH;AAGA,UAAM,MAA4C,CAAC;AACnD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,GAAG,IAAI,MAAM,KAAK,IAAI,WAAW,KAAK,GAAG;AAAA,IAC/C;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,UACJ,WACA,UACA,SACA,MAAuB,CAAC,GACO;AAC/B,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK,OAAM,IAAI,sBAAsB,SAAS;AACnD,UAAM,UAAU,IAAI,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS,qCAAqC,QAAQ,SAAS,SAAS;AAAA,MAC1E;AAAA,IACF;AACA,UAAM,SAAkC,CAAC;AACzC,eAAW,CAAC,GAAG,KAAK,IAAI,QAAQ;AAC9B,aAAO,GAAG,KAAK,MAAM,KAAK,IAAI,WAAW,KAAK,GAAG,GAAG;AAAA,IACtD;AACA,QAAI;AACF,aAAO,MAAM,QAAQ,EAAE,WAAW,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACpE,SAAS,KAAU;AACjB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS,KAAK,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,SAAS,WAAmB,QAA+C;AACvF,QAAI,KAAK,QAAQ;AACf,YAAM,QAAiC,EAAE,UAAU;AACnD,UAAI,WAAW,KAAM,OAAM,UAAU;AAMrC,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,KAAK,YAAY;AAAA,QACnD;AAAA,QACA,mBAAmB;AAAA,MACrB,CAAQ;AACR,aAAO,KAAK,IAAI,CAAC,OAAO;AAAA,QACtB,WAAW,EAAE;AAAA,QACb,KAAK,EAAE;AAAA,QACP,OAAO,EAAE;AAAA,QACT,SAAS,EAAE,WAAW;AAAA,QACtB,OAAO,EAAE,SAAS;AAAA,QAClB,WAAW,EAAE,aAAa;AAAA,QAC1B,WAAW,QAAQ,EAAE,SAAS;AAAA,QAC9B,QAAQ,QAAQ,EAAE,MAAM;AAAA,QACxB,eAAe,EAAE,iBAAiB;AAAA,QAClC,YAAY,EAAE;AAAA,QACd,YAAY,EAAE,cAAc;AAAA,MAC9B,EAAE;AAAA,IACJ;AACA,WAAO,KAAK,OAAO;AAAA,MACjB,CAAC,MACC,EAAE,cAAc,cACf,WAAW,QAAQ,EAAE,YAAY,UAAU,EAAE,UAAU,YAAY,EAAE,UAAU;AAAA,IACpF;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,KAAiC;AACvD,QAAI,KAAK,QAAQ;AACf,YAAM,QAAiC;AAAA,QACrC,WAAW,IAAI;AAAA,QACf,KAAK,IAAI;AAAA,QACT,OAAO,IAAI;AAAA,QACX,SAAS,IAAI,WAAW;AAAA,MAC1B;AAIA,YAAM,SAAS,IAAI,UAAU,WAAW,EAAE,mBAAmB,KAAK,IAAI,CAAC;AACvE,YAAM,WAAW,MAAM,KAAK,OAAO,KAAK,KAAK,YAAY;AAAA,QACvD;AAAA,QACA,OAAO;AAAA,QACP,GAAG;AAAA,MACL,CAAQ;AACR,UAAI,SAAS,CAAC,GAAG;AACf,cAAM,KAAK,OAAO,OAAO,KAAK,YAAY;AAAA,UACxC;AAAA,UACA,MAAM,EAAE,GAAG,IAAI;AAAA,UACf,GAAG;AAAA,QACL,CAAQ;AAAA,MACV,OAAO;AACL,cAAM,KAAK,OAAO,OAAO,KAAK,YAAY,EAAE,GAAG,IAAI,GAAG,MAAa;AAAA,MACrE;AACA;AAAA,IACF;AACA,UAAM,MAAM,KAAK,OAAO;AAAA,MACtB,CAAC,MACC,EAAE,cAAc,IAAI,aACpB,EAAE,QAAQ,IAAI,OACd,EAAE,UAAU,IAAI,UACf,EAAE,WAAW,WAAW,IAAI,WAAW;AAAA,IAC5C;AACA,QAAI,OAAO,EAAG,MAAK,OAAO,GAAG,IAAI;AAAA,QAC5B,MAAK,OAAO,KAAK,GAAG;AAAA,EAC3B;AAAA,EAEA,MAAc,eAAe,KAAoC;AAC/D,QAAI,IAAI,WAAW;AACjB,UAAI,CAAC,IAAI,UAAW,QAAO;AAC3B,UAAI;AAKJ,UACE,KAAK,kBACL,KAAK,eACL,OAAO,IAAI,cAAc,YACzB,IAAI,UAAU,WAAW,MAAM,GAC/B;AACA,cAAM,SAAS,MAAM,KAAK,YAAY,IAAI,IAAI,SAAS;AACvD,YAAI,CAAC,OAAQ,QAAO;AACpB,gBAAQ,MAAM,KAAK,eAAe;AAAA,UAChC;AAAA,YACE,IAAI,OAAO;AAAA,YACX,UAAU,OAAO;AAAA,YACjB,KAAK,OAAO;AAAA,YACZ,SAAS,OAAO;AAAA,YAChB,YAAY,OAAO;AAAA,UACrB;AAAA,UACA,EAAE,WAAW,IAAI,WAAW,KAAK,IAAI,IAAI;AAAA,QAC3C;AAAA,MACF,OAAO;AACL,gBAAQ,MAAM,KAAK,OAAO,QAAQ,IAAI,WAAW;AAAA,UAC/C,WAAW,IAAI;AAAA,UACf,KAAK,IAAI;AAAA,QACX,CAAC;AAAA,MACH;AAEA,UAAI;AACF,eAAO,KAAK,MAAM,KAAK;AAAA,MACzB,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,IAAI,SAAS;AAAA,EACtB;AACF;AAOA,SAAS,gBAAgB,OAAwB;AAC/C,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK;AAC5E,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,MAAM,IAAI,eAAe,EAAE,KAAK,GAAG,IAAI;AAC9E,QAAM,MAAM;AACZ,QAAM,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK;AACnC,SAAO,MAAM,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,IAAI,MAAM,gBAAgB,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI;AAC9F;AAGA,SAAS,eAAe,KAAa,MAAwB;AAC3D,MAAI,OAAO,SAAS,UAAW,QAAO,QAAQ,UAAU,QAAQ,OAAO,QAAQ;AAC/E,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,IAAI,OAAO,GAAG;AACpB,WAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,EAClC;AACA,MAAI,MAAM,QAAQ,IAAI,KAAM,QAAQ,OAAO,SAAS,UAAW;AAC7D,QAAI;AACF,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC1sBA,yBAA0E;AAsC1E,IAAM,wBAAwB,MAAe;AAC3C,QAAM,IAAI;AACV,SACE,OAAO,MAAM,gBACZ,QAAQ,EAAE,SAAS,UAAU,YAAY,KACxC,QAAQ,EAAE,SAAS,KAAK,OAAO,WAAW,KAAK,CAAC,KAChD,QAAQ,EAAE,SAAS,KAAK,UAAU;AAExC;AAOA,IAAI;AACJ,IAAM,eAAe,MAAuC;AAC1D,MAAI,CAAC,iBAAiB;AACpB,uBAAmB,YAAY;AAC7B,UAAI;AACF,cAAM,MAAM,MAAM,OAAO,uBAAuB;AAChD,eAAO,IAAI;AAAA,MACb,SAAS,KAAU;AACjB,gBAAQ;AAAA,UACN,oFAAoF,KAAK,WAAW,GAAG;AAAA,QACzG;AACA,eAAO;AAAA,MACT;AAAA,IACF,GAAG;AAAA,EACL;AACA,SAAO;AACT;AAEO,IAAM,yBAAN,MAAwD;AAAA,EAI7D,YAAY,OAAyB,CAAC,GAAG;AACvC,SAAK,MAAM,KAAK,WAAO,gCAAY,EAAE;AACrC,SAAK,WAAW,sBAAsB;AAAA,EACxC;AAAA,EAEA,MAAM,QAAQ,OAAe,KAA2C;AACtE,UAAM,SAAK,gCAAY,EAAE;AACzB,UAAM,MAAM,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM;AAC/C,UAAM,aAAa,OAAO,KAAK,OAAO,MAAM;AAE5C,QAAI;AACJ,QAAI,KAAK,UAAU;AACjB,YAAM,MAAM,MAAM,aAAa;AAC/B,UAAI,KAAK;AACP,cAAM,SAAS,IAAI,KAAK,KAAK,IAAI,GAAG;AACpC,cAAM,YAAY,OAAO,QAAQ,UAAU;AAC3C,cAAM,KAAK,UAAU,SAAS,GAAG,UAAU,SAAS,EAAE;AACtD,cAAM,MAAM,UAAU,SAAS,UAAU,SAAS,EAAE;AACpD,eAAO,OAAO,OAAO,CAAC,IAAI,OAAO,KAAK,GAAG,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC,EAAE,SAAS,QAAQ;AAAA,MACjF,OAAO;AACL,eAAO,KAAK,YAAY,YAAY,IAAI,GAAG;AAAA,MAC7C;AAAA,IACF,OAAO;AACL,aAAO,KAAK,YAAY,YAAY,IAAI,GAAG;AAAA,IAC7C;AAEA,WAAO;AAAA,MACL,IAAI,aAAS,gCAAY,EAAE,EAAE,SAAS,KAAK;AAAA,MAC3C,UAAU;AAAA,MACV,KAAK;AAAA,MACL,SAAS;AAAA,MACT,YAAY;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,QAAsB,KAAqC;AACvE,UAAM,MAAM,OAAO,KAAK,OAAO,YAAY,QAAQ;AACnD,UAAM,KAAK,IAAI,SAAS,GAAG,EAAE;AAC7B,UAAM,MAAM,IAAI,SAAS,IAAI,EAAE;AAC/B,UAAM,OAAO,IAAI,SAAS,EAAE;AAC5B,UAAM,MAAM,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM;AAE/C,QAAI,KAAK,UAAU;AACjB,YAAM,MAAM,MAAM,aAAa;AAC/B,UAAI,KAAK;AACP,cAAM,SAAS,IAAI,KAAK,KAAK,IAAI,GAAG;AACpC,cAAM,YAAY,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC;AAC3C,cAAM,MAAM,OAAO,QAAQ,SAAS;AACpC,eAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;AAAA,MACzC;AAAA,IACF;AACA,UAAM,eAAW,qCAAiB,eAAe,KAAK,KAAK,EAAE;AAC7D,aAAS,OAAO,GAAG;AACnB,aAAS,WAAW,GAAG;AACvB,WAAO,OAAO,OAAO,CAAC,SAAS,OAAO,IAAI,GAAG,SAAS,MAAM,CAAC,CAAC,EAAE,SAAS,MAAM;AAAA,EACjF;AAAA,EAEA,MAAM,UAAU,QAAsB,KAA2C;AAC/E,UAAM,QAAQ,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC5C,UAAM,OAAO,MAAM,KAAK,QAAQ,OAAO,GAAG;AAC1C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,IAAI,OAAO;AAAA,MACX,UAAU,oBAAoB,OAAO,UAAU,CAAC;AAAA,MAChD,SAAS,OAAO,UAAU;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,OAAO,OAAuB;AAC5B,WAAO,gBAAY,+BAAW,QAAQ,EAAE,OAAO,OAAO,MAAM,EAAE,OAAO,KAAK;AAAA,EAC5E;AAAA,EAEQ,YAAY,YAAoB,IAAY,KAAqB;AACvE,UAAM,aAAS,mCAAe,eAAe,KAAK,KAAK,EAAE;AACzD,WAAO,OAAO,GAAG;AACjB,UAAM,MAAM,OAAO,OAAO,CAAC,OAAO,OAAO,UAAU,GAAG,OAAO,MAAM,CAAC,CAAC;AACrE,UAAM,MAAM,OAAO,WAAW;AAC9B,WAAO,OAAO,OAAO,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,SAAS,QAAQ;AAAA,EACxD;AAAA,EAEQ,MAAM,KAA4B;AAMxC,WAAO,CAAC,IAAI,WAAW,IAAI,GAAG,EAAE,KAAK,GAAG;AAAA,EAC1C;AACF;;;ACtIA,IAAM,iBAAiB,CAAC,QAAuC;AAC7D,QAAM,SAAS,CAAC,SAAqC;AACnD,UAAM,IAAI,IAAI,UAAU,IAAI;AAC5B,WAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI;AAAA,EACnC;AACA,QAAM,QAAQ,OAAO,eAAe;AACpC,SAAO;AAAA,IACL,QAAQ,OAAO,WAAW;AAAA,IAC1B,UAAU,OAAO,aAAa;AAAA,IAC9B,aAAa,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IAAI;AAAA,IAC7E,WAAW,OAAO,cAAc;AAAA,EAClC;AACF;AAEA,SAAS,UAAU,KAAoB,QAAgB,MAAc,SAAiB,OAAiC;AACrH,MAAI,OAAO,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;AAChE;AAEO,SAAS,uBACd,MACA,SACA,OAA8B,CAAC,GACzB;AACN,QAAM,OAAO,KAAK,YAAY;AAC9B,QAAM,QAAQ,KAAK,sBAAsB;AAEzC,OAAK,IAAI,OAAO,OAAO,KAAK,QAAQ;AAClC,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,YAAY,QAAQ,cAAc,GAAG;AAC3C,YAAM,IAAI,KAAK,EAAE,UAAU,CAAC;AAAA,IAC9B,SAAS,KAAU;AACjB,gBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,0BAA0B;AAAA,IAC5E;AAAA,EACF,EAAyB;AAEzB,OAAK,IAAI,GAAG,IAAI,gBAAgB,OAAO,KAAK,QAAQ;AAClD,UAAM,KAAK,IAAI,OAAO;AACtB,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,UAAU,MAAM,QAAQ,aAAa,IAAI,GAAG;AAClD,YAAM,IAAI,KAAK,OAAO;AAAA,IACxB,SAAS,KAAU;AACjB,UAAI,eAAe,uBAAuB;AACxC,kBAAU,KAAK,KAAK,qBAAqB,IAAI,OAAO;AAAA,MACtD,OAAO;AACL,kBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,0BAA0B;AAAA,MAC5E;AAAA,IACF;AAAA,EACF,EAAyB;AAEzB,OAAK,IAAI,GAAG,IAAI,gBAAgB,OAAO,KAAK,QAAQ;AAClD,UAAM,KAAK,IAAI,OAAO;AACtB,UAAM,OAAQ,IAAI,QAAQ,CAAC;AAC3B,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,MAAM,GAAG;AAClD,YAAM,IAAI,KAAK,EAAE,QAAQ,OAAO,CAAC;AAAA,IACnC,SAAS,KAAU;AACjB,UAAI,eAAe,qBAAqB;AACtC,kBAAU,KAAK,KAAK,mBAAmB,IAAI,SAAS;AAAA,UAClD,WAAW,IAAI;AAAA,UACf,KAAK,IAAI;AAAA,UACT,QAAQ,IAAI;AAAA,QACd,CAAC;AAAA,MACH,WAAW,eAAe,uBAAuB;AAC/C,kBAAU,KAAK,KAAK,qBAAqB,IAAI,OAAO;AAAA,MACtD,WAAW,eAAe,iBAAiB;AACzC,kBAAU,KAAK,KAAK,eAAe,IAAI,SAAS,EAAE,WAAW,IAAI,WAAW,KAAK,IAAI,IAAI,CAAC;AAAA,MAC5F,OAAO;AACL,kBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,2BAA2B;AAAA,MAC7E;AAAA,IACF;AAAA,EACF,EAAyB;AAEzB,OAAK,KAAK,GAAG,IAAI,0BAA0B,OAAO,KAAK,QAAQ;AAC7D,UAAM,EAAE,WAAW,SAAS,IAAI,IAAI;AACpC,QAAI;AACF,YAAM,MAAM,MAAM,GAAG;AACrB,YAAM,SAAS,MAAM,QAAQ,UAAU,WAAW,UAAU,IAAI,MAAM,GAAG;AACzE,YAAM,SAAS,OAAO,KAAK,MAAM;AACjC,YAAM,IAAI,OAAO,MAAM,EAAE,KAAK,MAAM;AAAA,IACtC,SAAS,KAAU;AACjB,UAAI,eAAe,uBAAuB;AACxC,kBAAU,KAAK,KAAK,qBAAqB,IAAI,OAAO;AAAA,MACtD,OAAO;AACL,kBAAU,KAAK,KAAK,YAAY,KAAK,WAAW,eAAe;AAAA,MACjE;AAAA,IACF;AAAA,EACF,EAAyB;AAC3B;;;AC5HA,oBAAuD;AAEhD,IAAM,qBAAqB;AAC3B,IAAM,0BAA0B;AAGhC,IAAM,kBAAyB,CAAC,0BAAY,yBAAW,6BAAe;AAGtE,IAAM,+BAA+B;AAAA,EAC1C,IAAI;AAAA,EACJ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aACE;AACJ;;;ACVA,IAAM,WAAW;AAAA,EACf,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,IACV;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAY,OAAO;AAAA,MAAY,UAAU;AAAA,MAC5D,aAAa;AAAA,IAAkD;AAAA,IAEjE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAY,OAAO;AAAA,MAAY,UAAU;AAAA,MAAM,SAAS;AAAA,MAC7E,SAAS;AAAA,QACP,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,QAC/B,EAAE,OAAO,YAAY,OAAO,WAAW;AAAA,QACvC,EAAE,OAAO,OAAO,OAAO,aAAa;AAAA,QACpC,EAAE,OAAO,YAAY,OAAO,WAAW;AAAA,MACzC;AAAA,IACF;AAAA,IAEA,EAAE,MAAM,SAAS,IAAI,QAAQ,OAAO,QAAQ,UAAU,OAAO,SAAS,8BAA8B;AAAA,IACpG;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAQ,UAAU;AAAA,MACzD,aAAa;AAAA,MAA6B,SAAS;AAAA,IAA8B;AAAA,IACnF;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAa,OAAO;AAAA,MAAQ,UAAU;AAAA,MAAO,SAAS;AAAA,MAC3E,KAAK;AAAA,MAAG,KAAK;AAAA,MAAO,SAAS;AAAA,IAA8B;AAAA,IAC7D;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAe,OAAO;AAAA,MAAW,UAAU;AAAA,MAAO,SAAS;AAAA,MAChF,SAAS;AAAA,IAA8B;AAAA,IACzC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAY,UAAU;AAAA,MAC7D,SAAS;AAAA,IAA8B;AAAA,IACzC;AAAA,MAAE,MAAM;AAAA,MAAY,KAAK;AAAA,MAAiB,OAAO;AAAA,MAAY,UAAU;AAAA,MACrE,SAAS;AAAA,IAA8B;AAAA,IAEzC,EAAE,MAAM,SAAS,IAAI,WAAW,OAAO,WAAW,UAAU,OAAO,SAAS,8BAA8B;AAAA,IAC1G;AAAA,MAAE,MAAM;AAAA,MAAY,KAAK;AAAA,MAAW,OAAO;AAAA,MAAW,UAAU;AAAA,MAAM,WAAW;AAAA,MAC/E,SAAS;AAAA,IAA8B;AAAA,IAEzC,EAAE,MAAM,SAAS,IAAI,gBAAgB,OAAO,gBAAgB,UAAU,MAAM;AAAA,IAC5E;AAAA,MAAE,MAAM;AAAA,MAAS,KAAK;AAAA,MAAc,OAAO;AAAA,MAAc,UAAU;AAAA,MACjE,aAAa;AAAA,IAAgC;AAAA,IAC/C,EAAE,MAAM,QAAQ,KAAK,aAAa,OAAO,aAAa,UAAU,OAAO,SAAS,cAAc;AAAA,IAE9F;AAAA,MAAE,MAAM;AAAA,MAAiB,IAAI;AAAA,MAAQ,OAAO;AAAA,MAAmB,UAAU;AAAA,MAAO,MAAM;AAAA,MACpF,SAAS,EAAE,MAAM,QAAQ,QAAQ,QAAQ,KAAK,0BAA0B;AAAA,IAAE;AAAA,EAC9E;AACF;AAGO,IAAM,uBAAuB;AAG7B,IAAM,wBAA+C,OAAO,EAAE,OAAO,MAAM;AAChF,QAAM,WAAW,OAAO,OAAO,YAAY,MAAM;AACjD,QAAM,YAAY,OAAO;AACzB,MAAI,CAAC,WAAW;AACd,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,2CAA2C;AAAA,EAC7F;AACA,MAAI,aAAa,UAAU,CAAC,OAAO,WAAW;AAC5C,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,yBAAyB;AAAA,EAC3E;AACA,MAAI,aAAa,UAAU,CAAC,OAAO,SAAS;AAC1C,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,uBAAuB;AAAA,EACzE;AACA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,SAAS,uCAAuC,QAAQ;AAAA,EAC1D;AACF;;;AC5EO,IAAM,2BAA6C;AAAA,EACxD,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,IACV,EAAE,MAAM,SAAS,IAAI,YAAY,OAAO,YAAY,UAAU,MAAM;AAAA,IACpE;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAkB,OAAO;AAAA,MAAkB,UAAU;AAAA,MACxE,SAAS;AAAA,MAAe,WAAW;AAAA,MAAG,WAAW;AAAA,IAAG;AAAA,IACtD;AAAA,MAAE,MAAM;AAAA,MAAS,KAAK;AAAA,MAAiB,OAAO;AAAA,MAAiB,UAAU;AAAA,MACvE,aAAa;AAAA,IAA+B;AAAA,IAE9C,EAAE,MAAM,SAAS,IAAI,cAAc,OAAO,cAAc,UAAU,MAAM;AAAA,IACxE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAc,OAAO;AAAA,MAAiB,UAAU;AAAA,MAAO,SAAS;AAAA,MACrF,SAAS;AAAA,QACP,EAAE,OAAO,SAAS,OAAO,QAAQ;AAAA,QACjC,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,QAC/B,EAAE,OAAO,UAAU,OAAO,eAAe;AAAA,MAC3C;AAAA,IACF;AAAA,IACA,EAAE,MAAM,SAAS,KAAK,gBAAgB,OAAO,iBAAiB,UAAU,OAAO,SAAS,UAAU;AAAA,IAClG;AAAA,MAAE,MAAM;AAAA,MAAO,KAAK;AAAA,MAAY,OAAO;AAAA,MAAY,UAAU;AAAA,MAC3D,aAAa;AAAA,IAA8B;AAAA,EAC/C;AACF;;;AC9BO,IAAM,+BAAiD;AAAA,EAC5D,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,MAAM;AAAA,EACN,YAAY;AAAA,IACV;AAAA,MAAE,MAAM;AAAA,MAAe,IAAI;AAAA,MAAe,OAAO;AAAA,MAAY,UAAU;AAAA,MACrE,YACE;AAAA,MACF,gBAAgB;AAAA,IAAU;AAAA,IAE5B,EAAE,MAAM,SAAS,IAAI,gBAAgB,OAAO,gBAAgB,UAAU,MAAM;AAAA,IAC5E;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAc,OAAO;AAAA,MAAgB,UAAU;AAAA,MAAO,SAAS;AAAA,MACpF,aAAa;AAAA,IAAyC;AAAA,IACxD,EAAE,MAAM,UAAU,KAAK,oBAAoB,OAAO,oBAAoB,UAAU,OAAO,SAAS,MAAM;AAAA,IAEtG,EAAE,MAAM,SAAS,IAAI,iBAAiB,OAAO,iBAAiB,UAAU,MAAM;AAAA,IAC9E,EAAE,MAAM,UAAU,KAAK,oBAAoB,OAAO,oBAAoB,UAAU,OAAO,SAAS,MAAM;AAAA,IACtG,EAAE,MAAM,UAAU,KAAK,mBAAmB,OAAO,mBAAmB,UAAU,OAAO,SAAS,KAAK;AAAA,EACrG;AACF;;;ACvBA,IAAMC,YAAW;AAAA,EACf,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,aACE;AAAA,EAIF,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,IACV;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAW,OAAO;AAAA,MAAW,UAAU;AAAA,MAC1D,aAAa;AAAA,IAA0C;AAAA,IACzD;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAW,OAAO;AAAA,MAAW,UAAU;AAAA,MAAM,SAAS;AAAA,MAC3E,SAAS;AAAA,QACP,EAAE,OAAO,SAAS,OAAO,mBAAmB;AAAA,QAC5C,EAAE,OAAO,MAAM,OAAO,qBAAqB;AAAA,MAC7C;AAAA,IACF;AAAA,IAEA;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAS,OAAO;AAAA,MAAS,UAAU;AAAA,MACtD,SAAS;AAAA,IAA8B;AAAA,IACzC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAc,OAAO;AAAA,MAAkB,UAAU;AAAA,MACpE,SAAS;AAAA,MACT,aAAa;AAAA,MACb,SAAS;AAAA,IAA8B;AAAA,IAEzC;AAAA,MAAE,MAAM;AAAA,MAAS,IAAI;AAAA,MAAM,OAAO;AAAA,MAAM,UAAU;AAAA,MAChD,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAU,UAAU;AAAA,MAC3D,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAa,OAAO;AAAA,MAAU,UAAU;AAAA,MAC3D,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAe,OAAO;AAAA,MAAY,UAAU;AAAA,MAC/D,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAQ,KAAK;AAAA,MAAoB,OAAO;AAAA,MAAiB,UAAU;AAAA,MACzE,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAY,KAAK;AAAA,MAAwB,OAAO;AAAA,MACtD,UAAU;AAAA,MAAM,WAAW;AAAA,MAC3B,SAAS;AAAA,IAA2B;AAAA,IACtC;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAuB,OAAO;AAAA,MACnD,UAAU;AAAA,MAAO,SAAS;AAAA,MAC1B,aAAa;AAAA,MACb,SAAS;AAAA,IAA2B;AAAA,IAEtC,EAAE,MAAM,SAAS,IAAI,UAAU,OAAO,UAAU,UAAU,MAAM;AAAA,IAChE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAiB,OAAO;AAAA,MAC7C,UAAU;AAAA,MAAO,SAAS;AAAA,MAAM,KAAK;AAAA,MAAI,KAAK;AAAA,IAAO;AAAA,IACvD;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3C,UAAU;AAAA,MAAO,SAAS;AAAA,MAAO,KAAK;AAAA,MAAK,KAAK;AAAA,MAChD,aAAa;AAAA,IAAqD;AAAA,IACpE;AAAA,MAAE,MAAM;AAAA,MAAU,KAAK;AAAA,MAAiB,OAAO;AAAA,MAC7C,UAAU;AAAA,MAAO,SAAS;AAAA,MAAK,KAAK;AAAA,MAAG,KAAK;AAAA,IAAM;AAAA,IAEpD;AAAA,MAAE,MAAM;AAAA,MAAiB,IAAI;AAAA,MAAQ,OAAO;AAAA,MAC1C,UAAU;AAAA,MAAO,MAAM;AAAA,MACvB,SAAS,EAAE,MAAM,QAAQ,QAAQ,QAAQ,KAAK,6BAA6B;AAAA,IAAE;AAAA,EACjF;AACF;AAGO,IAAM,0BAA0BA;AAUhC,IAAM,2BAAkD,OAAO,EAAE,OAAO,MAAM;AACnF,QAAM,UAAU,OAAO,OAAO,WAAW,OAAO;AAChD,MAAI,YAAY,SAAS;AACvB,UAAM,OAAO,OAAO;AACpB,QAAI,CAAC,MAAM;AACT,aAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,6CAA6C;AAAA,IAC/F;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,SAAS,kCAAkC,IAAI;AAAA,IACjD;AAAA,EACF;AACA,MAAI,YAAY,MAAM;AACpB,UAAM,UAAoB,CAAC;AAC3B,QAAI,CAAC,OAAO,UAAW,SAAQ,KAAK,WAAW;AAC/C,QAAI,CAAC,OAAO,UAAW,SAAQ,KAAK,WAAW;AAC/C,QAAI,CAAC,OAAO,iBAAkB,SAAQ,KAAK,kBAAkB;AAC7D,QAAI,CAAC,OAAO,qBAAsB,SAAQ,KAAK,sBAAsB;AACrE,QAAI,QAAQ,QAAQ;AAClB,aAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,yBAAyB,QAAQ,SAAS,IAAI,MAAM,EAAE,KAAK,QAAQ,KAAK,IAAI,CAAC,GAAG;AAAA,IAClI;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,SAAS,iCAAiC,OAAO,SAAS,YAAY,OAAO,SAAS;AAAA,IACxF;AAAA,EACF;AACA,SAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,oBAAoB,OAAO,GAAG;AAChF;;;ACvGO,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACRO,IAAM,KAAsB;AAAA,EACjC,gBAAgB;AAAA,IACd,cAAc;AAAA,MACZ,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,YAAY,aAAa,kDAAkD;AAAA,QAC9F,MAAM,EAAE,OAAO,OAAO;AAAA,QACtB,SAAS,EAAE,OAAO,UAAU;AAAA,QAC5B,cAAc,EAAE,OAAO,eAAe;AAAA,MACxC;AAAA,MACA,MAAM;AAAA,QACJ,UAAU;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,YACV,KAAK;AAAA,YACL,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,QACA,WAAW,EAAE,OAAO,QAAQ,MAAM,4BAA4B;AAAA,QAC9D,WAAW,EAAE,OAAO,OAAO;AAAA,QAC3B,aAAa,EAAE,OAAO,UAAU;AAAA,QAChC,WAAW,EAAE,OAAO,WAAW;AAAA,QAC/B,eAAe,EAAE,OAAO,WAAW;AAAA,QACnC,SAAS,EAAE,OAAO,UAAU;AAAA,QAC5B,YAAY,EAAE,OAAO,cAAc,MAAM,gCAAgC;AAAA,QACzE,WAAW,EAAE,OAAO,YAAY;AAAA,MAClC;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,kBAAkB;AAAA,MACnC;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,WAAW;AAAA,QAC9B,YAAY,EAAE,OAAO,aAAa;AAAA,MACpC;AAAA,MACA,MAAM;AAAA,QACJ,gBAAgB,EAAE,OAAO,iBAAiB;AAAA,QAC1C,eAAe,EAAE,OAAO,iBAAiB,MAAM,+BAA+B;AAAA,QAC9E,YAAY;AAAA,UACV,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,SAAS,MAAM,QAAQ,QAAQ,eAAe;AAAA,QAClE;AAAA,QACA,cAAc,EAAE,OAAO,gBAAgB;AAAA,QACvC,UAAU,EAAE,OAAO,YAAY,MAAM,mCAA8B;AAAA,MACrE;AAAA,IACF;AAAA,IAEA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,cAAc,EAAE,OAAO,eAAe;AAAA,QACtC,eAAe,EAAE,OAAO,gBAAgB;AAAA,MAC1C;AAAA,MACA,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA,kBAAkB,EAAE,OAAO,mBAAmB;AAAA,QAC9C,kBAAkB,EAAE,OAAO,mBAAmB;AAAA,QAC9C,iBAAiB,EAAE,OAAO,kBAAkB;AAAA,MAC9C;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,MACP,OAAO;AAAA,MACP,aACE;AAAA,MAIF,QAAQ;AAAA,QACN,SAAS,EAAE,OAAO,WAAW,aAAa,0CAA0C;AAAA,QACpF,OAAO,EAAE,OAAO,QAAQ;AAAA,QACxB,IAAI,EAAE,OAAO,KAAK;AAAA,QAClB,QAAQ,EAAE,OAAO,SAAS;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,oBAAoB,IAAI,qBAAqB;AAAA,QACjE;AAAA,QACA,YAAY;AAAA,UAAE,OAAO;AAAA,UACnB,MAAM;AAAA,QAA4F;AAAA,QACpG,WAAW;AAAA,UAAE,OAAO;AAAA,UAClB,MAAM;AAAA,QAAqG;AAAA,QAC7G,WAAW,EAAE,OAAO,UAAU,MAAM,qBAAqB;AAAA,QACzD,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAA2F;AAAA,QACnG,kBAAkB,EAAE,OAAO,gBAAgB;AAAA,QAC3C,sBAAsB,EAAE,OAAO,oBAAoB;AAAA,QACnD,qBAAqB;AAAA,UAAE,OAAO;AAAA,UAC5B,MAAM;AAAA,QAAyE;AAAA,QACjF,eAAe,EAAE,OAAO,8BAA8B;AAAA,QACtD,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAqD;AAAA,QAC7D,eAAe,EAAE,OAAO,uBAAuB;AAAA,MACjD;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,kBAAkB;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACF;;;AC5HO,IAAM,OAAwB;AAAA,EACnC,gBAAgB;AAAA,IACd,cAAc;AAAA,MACZ,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,sBAAO,aAAa,iFAAgB;AAAA,QACvD,MAAM,EAAE,OAAO,OAAO;AAAA,QACtB,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,cAAc,EAAE,OAAO,2BAAO;AAAA,MAChC;AAAA,MACA,MAAM;AAAA,QACJ,UAAU;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,YACV,KAAK;AAAA,YACL,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,QACA,WAAW,EAAE,OAAO,gBAAM,MAAM,gCAAsB;AAAA,QACtD,WAAW,EAAE,OAAO,eAAK;AAAA,QACzB,aAAa,EAAE,OAAO,mBAAS;AAAA,QAC/B,WAAW,EAAE,OAAO,qBAAM;AAAA,QAC1B,eAAe,EAAE,OAAO,eAAK;AAAA,QAC7B,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,YAAY,EAAE,OAAO,4BAAQ,MAAM,oCAA0B;AAAA,QAC7D,WAAW,EAAE,OAAO,iCAAQ;AAAA,MAC9B;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,uCAAS;AAAA,MAC1B;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,eAAK;AAAA,QACxB,YAAY,EAAE,OAAO,eAAK;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,QACJ,gBAAgB,EAAE,OAAO,iCAAQ;AAAA,QACjC,eAAe,EAAE,OAAO,4BAAQ,MAAM,mCAAyB;AAAA,QAC/D,YAAY;AAAA,UACV,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,gBAAM,MAAM,gBAAM,QAAQ,2BAAO;AAAA,QACrD;AAAA,QACA,cAAc,EAAE,OAAO,qBAAM;AAAA,QAC7B,UAAU,EAAE,OAAO,qBAAW,MAAM,uCAAwB;AAAA,MAC9D;AAAA,IACF;AAAA,IAEA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,cAAc,EAAE,OAAO,qBAAM;AAAA,QAC7B,eAAe,EAAE,OAAO,eAAK;AAAA,MAC/B;AAAA,MACA,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA,kBAAkB,EAAE,OAAO,2BAAO;AAAA,QAClC,kBAAkB,EAAE,OAAO,2BAAO;AAAA,QAClC,iBAAiB,EAAE,OAAO,2BAAO;AAAA,MACnC;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,MACP,OAAO;AAAA,MACP,aACE;AAAA,MAEF,QAAQ;AAAA,QACN,SAAS,EAAE,OAAO,4BAAQ,aAAa,2EAAe;AAAA,QACtD,OAAO,EAAE,OAAO,eAAK;AAAA,QACrB,IAAI,EAAE,OAAO,KAAK;AAAA,QAClB,QAAQ,EAAE,OAAO,eAAK;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,wCAAU,IAAI,uBAAa;AAAA,QAC/C;AAAA,QACA,YAAY;AAAA,UAAE,OAAO;AAAA,UACnB,MAAM;AAAA,QAAgC;AAAA,QACxC,WAAW;AAAA,UAAE,OAAO;AAAA,UAClB,MAAM;AAAA,QAAyD;AAAA,QACjE,WAAW,EAAE,OAAO,gBAAM,MAAM,yBAAe;AAAA,QAC/C,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAoD;AAAA,QAC5D,kBAAkB,EAAE,OAAO,gBAAgB;AAAA,QAC3C,sBAAsB,EAAE,OAAO,oBAAoB;AAAA,QACnD,qBAAqB;AAAA,UAAE,OAAO;AAAA,UAC5B,MAAM;AAAA,QAAoC;AAAA,QAC5C,eAAe,EAAE,OAAO,oDAAiB;AAAA,QACzC,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAkB;AAAA,QAC1B,eAAe,EAAE,OAAO,iDAAc;AAAA,MACxC;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,2BAAO;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACF;;;ACtHO,IAAM,OAAwB;AAAA,EACnC,gBAAgB;AAAA,IACd,cAAc;AAAA,MACZ,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,wCAAU,aAAa,iIAAwB;AAAA,QAClE,MAAM,EAAE,OAAO,OAAO;AAAA,QACtB,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,cAAc,EAAE,OAAO,6CAAU;AAAA,MACnC;AAAA,MACA,MAAM;AAAA,QACJ,UAAU;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,YACV,KAAK;AAAA,YACL,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,QACA,WAAW,EAAE,OAAO,sBAAO,MAAM,2BAAsB;AAAA,QACvD,WAAW,EAAE,OAAO,qBAAM;AAAA,QAC1B,aAAa,EAAE,OAAO,yBAAU;AAAA,QAChC,WAAW,EAAE,OAAO,iCAAQ;AAAA,QAC5B,eAAe,EAAE,OAAO,iCAAQ;AAAA,QAChC,SAAS,EAAE,OAAO,mBAAS;AAAA,QAC3B,YAAY,EAAE,OAAO,8CAAW,MAAM,+BAA0B;AAAA,QAChE,WAAW,EAAE,OAAO,2BAAO;AAAA,MAC7B;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,mDAAW;AAAA,MAC5B;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,UAAU,EAAE,OAAO,mDAAW;AAAA,QAC9B,YAAY,EAAE,OAAO,eAAK;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,QACJ,gBAAgB,EAAE,OAAO,mDAAW;AAAA,QACpC,eAAe,EAAE,OAAO,8CAAW,MAAM,8BAAyB;AAAA,QAClE,YAAY;AAAA,UACV,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,sBAAO,MAAM,sBAAO,QAAQ,6CAAU;AAAA,QAC1D;AAAA,QACA,cAAc,EAAE,OAAO,mDAAW;AAAA,QAClC,UAAU,EAAE,OAAO,oBAAU,MAAM,kCAAwB;AAAA,MAC7D;AAAA,IACF;AAAA,IAEA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,QACN,cAAc,EAAE,OAAO,qBAAM;AAAA,QAC7B,eAAe,EAAE,OAAO,mDAAW;AAAA,MACrC;AAAA,MACA,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA,kBAAkB,EAAE,OAAO,qEAAc;AAAA,QACzC,kBAAkB,EAAE,OAAO,+DAAa;AAAA,QACxC,iBAAiB,EAAE,OAAO,yDAAY;AAAA,MACxC;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,MACP,OAAO;AAAA,MACP,aACE;AAAA,MAEF,QAAQ;AAAA,QACN,SAAS,EAAE,OAAO,wCAAU,aAAa,iIAAwB;AAAA,QACjE,OAAO,EAAE,OAAO,2BAAO;AAAA,QACvB,IAAI,EAAE,OAAO,KAAK;AAAA,QAClB,QAAQ,EAAE,OAAO,eAAK;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,EAAE,OAAO,4EAAgB,IAAI,uBAAa;AAAA,QACrD;AAAA,QACA,YAAY;AAAA,UAAE,OAAO;AAAA,UACnB,MAAM;AAAA,QAA+C;AAAA,QACvD,WAAW;AAAA,UAAE,OAAO;AAAA,UAClB,MAAM;AAAA,QAAoE;AAAA,QAC5E,WAAW,EAAE,OAAO,kCAAS,MAAM,oBAAe;AAAA,QAClD,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAA6D;AAAA,QACrE,kBAAkB,EAAE,OAAO,0CAAY;AAAA,QACvC,sBAAsB,EAAE,OAAO,2EAAe;AAAA,QAC9C,qBAAqB;AAAA,UAAE,OAAO;AAAA,UAC5B,MAAM;AAAA,QAA0C;AAAA,QAClD,eAAe,EAAE,OAAO,uEAAqB;AAAA,QAC7C,aAAa;AAAA,UAAE,OAAO;AAAA,UACpB,MAAM;AAAA,QAAqB;AAAA,QAC7B,eAAe,EAAE,OAAO,0EAAmB;AAAA,MAC7C;AAAA,MACA,SAAS;AAAA,QACP,MAAM,EAAE,OAAO,iCAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACF;;;ACzGO,IAAM,8BAAiD;AAAA,EAC5D;AAAA,EACA,SAAS;AAAA,EACT,SAAS;AACX;;;AC2CO,IAAM,wBAAN,MAA8C;AAAA,EAQnD,YAAY,OAAqC,CAAC,GAAG;AAPrD,gBAAO;AACP,mBAAU;AACV,gBAAO;AAGP,SAAQ,UAAkC;AAGxC,SAAK,OAAO;AAAA,MACV,GAAG;AAAA,MACH,WAAW,KAAK,aAAa;AAAA,MAC7B,gBAAgB,KAAK,kBAAkB;AAAA,QACrC,MAAM,EAAE,MAAM,sBAAsB;AAAA,QACpC,SAAS,EAAE,MAAM,yBAAyB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,SAAK,UAAU,IAAI,gBAAgB;AAAA,MACjC,QAAQ,KAAK,KAAK;AAAA,MAClB,KAAK,KAAK,KAAK;AAAA,IACjB,CAAC;AACD,eAAW,KAAK,KAAK,KAAK,aAAa,CAAC,EAAG,MAAK,QAAQ,iBAAiB,CAAC;AAC1E,eAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,KAAK,KAAK,kBAAkB,CAAC,CAAC,GAAG;AAC3E,iBAAW,CAAC,IAAI,EAAE,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC/C,aAAK,QAAQ,eAAe,IAAI,IAAI,EAAE;AAAA,MACxC;AAAA,IACF;AAEA,QAAI,gBAAgB,YAAY,KAAK,OAAO;AAC5C,QAAI,QAAQ;AAAA,MACV,gDAAgD,KAAK,KAAK,WAAW,UAAU,CAAC;AAAA,IAClF;AAGA,QAAI;AACF,UAAI,WAAuC,UAAU,EAAE,SAAS;AAAA,QAC9D,GAAG;AAAA,QACH,SAAS;AAAA,MACX,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAmC;AAC7C,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI,KAAK,gBAAgB,YAAY;AAInC,UAAI;AACF,cAAM,OAAO,IAAI,WAEd,MAAM;AACT,YAAI,SAAS;AACb,mBAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,2BAA2B,GAAG;AACxE,cAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,gBAAI;AACF,mBAAK,iBAAiB,QAAQ,IAA+B;AAC7D;AAAA,YACF,SAAS,KAAU;AACjB,kBAAI,QAAQ;AAAA,gBACV,2DAA2D,MAAM,MAAM,KAAK,WAAW,GAAG;AAAA,cAC5F;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,SAAS,GAAG;AACd,cAAI,QAAQ;AAAA,YACV,6DAA6D,MAAM,UAAU,SAAS,IAAI,MAAM,EAAE;AAAA,UACpG;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAGA,UAAI,SAA6B;AACjC,UAAI;AACF,iBAAS,IAAI,WAAwB,UAAU;AAAA,MACjD,QAAQ;AAAA,MAER;AACA,UAAI,QAAQ;AAKV,aAAK,QAAS;AAAA,UACZ;AAAA,UACA,KAAK,eAAe,KAAK,MAAM;AAAA,UAC/B;AAAA,YACE,aAAa,KAAK,iBAAiB,MAAM;AAAA,YACzC,aAAa,KAAK,iBAAiB,KAAK,MAAM;AAAA,YAC9C,gBAAgB,KAAK,KAAK,kBAAkB,IAAI,uBAAuB;AAAA,UACzE;AAAA,QACF;AAAA,MACF;AAEA,UAAI,KAAK,KAAK,mBAAmB,MAAO;AAExC,UAAI,OAA2B;AAC/B,UAAI;AACF,eAAO,IAAI,WAAwB,aAAa;AAAA,MAClD,QAAQ;AAAA,MAER;AACA,UAAI,CAAC,MAAM;AACT,YAAI,QAAQ;AAAA,UACV;AAAA,QAEF;AACA;AAAA,MACF;AACA,6BAAuB,MAAM,KAAK,SAAU,EAAE,UAAU,KAAK,KAAK,SAAS,CAAC;AAC5E,UAAI,QAAQ;AAAA,QACV,uDAAuD,KAAK,KAAK,YAAY;AAAA,MAC/E;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGQ,eAAe,KAAoB,QAAwC;AACjF,WAAO;AAAA,MACL,QAAQ,OAAO,UAAU;AACvB,YAAI;AACF,gBAAO,OAAe,SAAS,iBAAiB;AAAA,YAC9C,UAAU,MAAM,UAAU;AAAA,YAC1B,aAAa;AAAA,YACb,WAAW,GAAG,MAAM,SAAS,IAAI,MAAM,GAAG;AAAA,YAC1C,QAAQ,MAAM;AAAA,YACd,SAAS;AAAA,cACP,WAAW,MAAM;AAAA,cACjB,KAAK,MAAM;AAAA,cACX,OAAO,MAAM;AAAA,cACb,WAAW,MAAM;AAAA,cACjB,QAAQ,MAAM;AAAA,YAChB;AAAA,YACA,YAAY,MAAM,aAAa;AAAA,YAC/B,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,UACtC,CAAC;AAAA,QACH,SAAS,KAAU;AACjB,cAAI,QAAQ,OAAO,kDAAkD,KAAK,WAAW,IAAI;AAAA,QAC3F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiB,QAA0C;AACjE,UAAM,MAAW;AACjB,WAAO;AAAA,MACL,MAAM,OAAO,KAAK;AAChB,cAAM,IAAI,OAAO,cAAc,KAAK,EAAE,mBAAmB,KAAK,CAAC;AAC/D,eAAO,EAAE,IAAI,IAAI,GAAG;AAAA,MACtB;AAAA,MACA,MAAM,IAAI,IAAI;AACZ,cAAM,OAAO,MAAM,IAAI,KAAK,cAAc;AAAA,UACxC,OAAO,EAAE,GAAG;AAAA,UACZ,OAAO;AAAA,UACP,mBAAmB;AAAA,QACrB,CAAC;AACD,cAAM,MAAM,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,OAAO,CAAC;AAC1D,eAAO,OAAO;AAAA,MAChB;AAAA,MACA,MAAM,OAAO,IAAI,OAAO;AACtB,cAAM,IAAI,OAAO,cAAc;AAAA,UAC7B,OAAO,EAAE,GAAG;AAAA,UACZ,MAAM;AAAA,UACN,mBAAmB;AAAA,QACrB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAiB,KAAoB,QAA0C;AACrF,UAAM,MAAW;AACjB,WAAO;AAAA,MACL,OAAO,OAAO,UAAU;AACtB,YAAI;AACF,gBAAM,IAAI,OAAO,qBAAqB;AAAA,YACpC,WAAW,MAAM;AAAA,YACjB,KAAK,MAAM;AAAA,YACX,OAAO,MAAM;AAAA,YACb,QAAQ,MAAM;AAAA,YACd,QAAQ,MAAM,UAAU;AAAA,YACxB,UAAU,MAAM,WAAW;AAAA,YAC3B,UAAU,MAAM,WAAW;AAAA,YAC3B,UAAU,MAAM,WAAW;AAAA,YAC3B,WAAW,CAAC,CAAC,MAAM;AAAA,YACnB,YAAY,MAAM,aAAa;AAAA,YAC/B,QAAQ,MAAM,UAAU;AAAA,YACxB,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,UACrC,GAAG,EAAE,mBAAmB,KAAK,CAAC;AAAA,QAChC,SAAS,KAAU;AACjB,cAAI,QAAQ,OAAO,yDAAyD,KAAK,WAAW,IAAI;AAAA,QAClG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":["manifest","def","manifest"]}