@objectstack/core 17.0.0-rc.5 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3293 -0
- package/dist/index.cjs +370 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +588 -43
- package/dist/index.d.ts +588 -43
- package/dist/index.js +346 -63
- package/dist/index.js.map +1 -1
- package/dist/logger.cjs +9 -1
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.js +9 -1
- package/dist/logger.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/plugin-order.ts","../src/kernel-base.ts","../src/logger.ts","../src/kernel.ts","../src/security/plugin-config-validator.ts","../src/security/plugin-artifact-signature.ts","../src/plugin-loader.ts","../src/utils/env.ts","../src/fallbacks/memory-cache.ts","../src/fallbacks/memory-queue.ts","../src/fallbacks/memory-job.ts","../src/fallbacks/memory-i18n.ts","../src/fallbacks/memory-metadata.ts","../src/fallbacks/authored-translation-sync.ts","../src/fallbacks/index.ts","../src/lite-kernel.ts","../src/qa/index.ts","../src/qa/runner.ts","../src/qa/http-adapter.ts","../src/security/plugin-signature-verifier.ts","../src/security/plugin-permission-enforcer.ts","../src/security/permission-manager.ts","../src/security/sandbox-runtime.ts","../src/security/security-scanner.ts","../src/security/api-key.ts","../src/security/resolve-authz-context.ts","../src/security/grant-validity.ts","../src/security/posture-ladder.ts","../src/security/auth-gate.ts","../src/security/anonymous-deny.ts","../src/utils/datetime.ts","../src/utils/bulk-write.ts","../src/utils/migration-journal.ts","../src/utils/filter-tokens.ts","../src/health-monitor.ts","../src/hot-reload.ts","../src/dependency-resolver.ts","../src/namespace-resolver.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Plugin ordering + init-service contract (ADR-0116, #4131).\n *\n * The kernel resolves BOTH init and start order from the plugin dependency\n * graph, so `kernel.use()` registration order proves nothing. Twice a plugin\n * relied on list position anyway and shipped a boot that dies inside init —\n * the first cut of DefaultDatasourcePlugin (started after boot schema-sync;\n * server with no tables) and AppPlugin (#4085: `manifest` grabbed in init\n * before ObjectQLPlugin registered it). Both times the fix existed only as a\n * convention: put the plugin in the right slot, write a comment. This module\n * is the enforced form of that contract, shared by ObjectKernel and\n * LiteKernel so there is exactly one ordering semantic:\n *\n * - `dependencies` — hard: hoisted ahead, missing ⇒ boot error (unchanged).\n * - `optionalDependencies` — order-if-present: hoisted ahead when composed,\n * silently skipped when absent. For plugins that DEGRADE without the\n * dependency but must never init before it (AppPlugin on an engine-less\n * metadata-only kernel).\n * - `requiresServices` — services a plugin resolves SYNCHRONOUSLY during\n * `init()`. Validated before Phase 1 (provable misordering ⇒ named error\n * instead of a crash inside init) and again immediately before each init\n * (authoritative: the service is either registered by now or init dies).\n * - `providesServices` — services a plugin's `init()` UNCONDITIONALLY\n * registers. Powers the pre-Phase-1 check and the named diagnostics.\n * Declare only unconditional registrations: a conditional service (e.g.\n * one gated behind an option) would indict this plugin for orderings it\n * cannot actually satisfy.\n *\n * Declaring is NOT voluntary (#4471). Everything above can only enforce what\n * a plugin declares — a plugin that resolves `getService('X')` during init()\n * and declares nothing was invisible to all of it, failing only under\n * unlucky composition orders (#4085, and #4420 at data-consistency cost).\n * `scripts/check-init-service-contract.mjs` (CI: `check:init-service-contract`)\n * closes that gap: it walks every plugin's init() call graph and errors on\n * any init-reachable getService of a workspace-provided service that no\n * declaration covers. Best-effort tolerance is declared IN the plugin via\n * `optionalDependencies`, never exempted in the checker.\n */\n\n/**\n * The ordering-relevant surface of a kernel plugin. Structural on purpose:\n * ObjectKernel sorts `PluginMetadata`, LiteKernel sorts `Plugin`, and both\n * satisfy this shape.\n */\nexport interface OrderablePlugin {\n name: string;\n /** Hard dependencies — hoisted ahead; missing ⇒ boot error. */\n dependencies?: string[];\n /** Soft dependencies — hoisted ahead when composed, skipped when absent. */\n optionalDependencies?: string[];\n /** Services resolved synchronously during init(). */\n requiresServices?: string[];\n /** Services init() unconditionally registers. */\n providesServices?: string[];\n}\n\n/**\n * Topologically order plugins: every plugin's `dependencies` (throw when\n * missing) and `optionalDependencies` (skip when missing) init before it.\n * Insertion order is preserved for plugins with no edges between them.\n * Cycles through either edge kind throw — an optional dependency is a real\n * edge whenever both sides are composed.\n */\nexport function resolvePluginOrder<P extends OrderablePlugin>(plugins: Map<string, P>): P[] {\n const resolved: P[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n const visit = (pluginName: string) => {\n if (visited.has(pluginName)) return;\n\n if (visiting.has(pluginName)) {\n throw new Error(`[Kernel] Circular dependency detected: ${pluginName}`);\n }\n\n const plugin = plugins.get(pluginName);\n if (!plugin) {\n throw new Error(`[Kernel] Plugin '${pluginName}' not found`);\n }\n\n visiting.add(pluginName);\n\n for (const dep of plugin.dependencies ?? []) {\n if (!plugins.has(dep)) {\n throw new Error(\n `[Kernel] Dependency '${dep}' not found for plugin '${pluginName}'`\n );\n }\n visit(dep);\n }\n for (const dep of plugin.optionalDependencies ?? []) {\n if (plugins.has(dep)) visit(dep);\n }\n\n visiting.delete(pluginName);\n visited.add(pluginName);\n resolved.push(plugin);\n };\n\n for (const pluginName of plugins.keys()) {\n visit(pluginName);\n }\n\n return resolved;\n}\n\n/**\n * Pre-Phase-1 check: walk the resolved order and prove no plugin requires a\n * service whose only declared provider initializes AFTER it. A violation is\n * the exact #4085 class — misplaced composition — reported as a named,\n * structural boot error BEFORE any init side effects, instead of a bare\n * \"Service not found\" thrown from inside the victim's init.\n *\n * Deliberately does NOT fail when a required service has no declared provider\n * and is not yet registered: an earlier plugin may register it without\n * declaring `providesServices`. That case is settled authoritatively by\n * {@link assertInitServiceRequirements} immediately before the requiring\n * plugin's init runs.\n */\nexport function validateInitServiceContract<P extends OrderablePlugin>(\n ordered: P[],\n isServiceRegistered: (name: string) => boolean,\n): void {\n const providerSlot = new Map<string, { plugin: string; slot: number }>();\n ordered.forEach((plugin, slot) => {\n for (const service of plugin.providesServices ?? []) {\n if (!providerSlot.has(service)) {\n providerSlot.set(service, { plugin: plugin.name, slot });\n }\n }\n });\n\n const violations: string[] = [];\n ordered.forEach((plugin, slot) => {\n for (const service of plugin.requiresServices ?? []) {\n if (isServiceRegistered(service)) continue;\n const provider = providerSlot.get(service);\n if (provider && provider.slot > slot) {\n violations.push(\n `'${plugin.name}' requires service '${service}' during init, but '${service}' is ` +\n `provided by '${provider.plugin}', which initializes later (slot ${provider.slot} vs ${slot}). ` +\n `Registration order is not a contract — declare '${provider.plugin}' in ` +\n `'${plugin.name}'.dependencies (hard) or .optionalDependencies (order-if-present) ` +\n `so the kernel hoists it.`\n );\n }\n }\n });\n\n if (violations.length > 0) {\n throw new Error(\n `[Kernel] Plugin ordering contract violated (#4131):\\n - ${violations.join('\\n - ')}`\n );\n }\n}\n\n/**\n * Diagnosis suffix for a getService miss that happens WHILE a plugin's\n * init() is running: names the initializing plugin and — when a composed\n * plugin declares the service — the provider and the directive to declare\n * the ordering. Returns '' when no plugin is initializing, so non-boot\n * error messages stay byte-identical. Shared by both kernels.\n */\nexport function describeInitOrderFault(\n currentlyInitializing: string | undefined,\n plugins: Iterable<OrderablePlugin>,\n serviceName: string,\n): string {\n if (!currentlyInitializing) return '';\n let providerHint = '';\n for (const plugin of plugins) {\n if (plugin.providesServices?.includes(serviceName)) {\n providerHint = ` '${serviceName}' is provided by composed plugin '${plugin.name}', which has ` +\n `not initialized yet — declare it in the requiring plugin's dependencies/optionalDependencies.`;\n break;\n }\n }\n return ` (while plugin '${currentlyInitializing}' was initializing — a composition/` +\n `ordering fault, #4131.${providerHint})`;\n}\n\n/**\n * Just-before-init check: every service in `requiresServices` must be\n * registered at the moment the plugin's init() is about to run. At this\n * point the verdict is authoritative — Phase 1 runs sequentially, so a\n * service absent now is absent for this init, and the init would die on a\n * bare \"Service not found\" anyway. This turns that crash into a named\n * composition error.\n */\nexport function assertInitServiceRequirements(\n plugin: OrderablePlugin,\n isServiceRegistered: (name: string) => boolean,\n): void {\n for (const service of plugin.requiresServices ?? []) {\n if (isServiceRegistered(service)) continue;\n throw new Error(\n `[Kernel] Plugin '${plugin.name}' requires service '${service}' at init, but no such ` +\n `service is registered at this point of the boot. No composed plugin that initializes ` +\n `earlier provides it — compose a provider (and, if it initializes later without declaring ` +\n `'${service}' in providesServices, order it ahead via this plugin's dependencies/` +\n `optionalDependencies) (#4131).`\n );\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from './types.js';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { IServiceRegistry } from '@objectstack/spec/contracts';\nimport {\n resolvePluginOrder,\n validateInitServiceContract,\n assertInitServiceRequirements,\n describeInitOrderFault,\n} from './plugin-order.js';\n\n/**\n * Kernel state machine\n */\nexport type KernelState = 'idle' | 'initializing' | 'running' | 'stopping' | 'stopped';\n\n/**\n * ObjectKernelBase - Abstract Base Class for Microkernel\n * \n * Provides common functionality for ObjectKernel and LiteKernel:\n * - Plugin management (Map storage)\n * - Dependency resolution (topological sort)\n * - Hook/Event system\n * - Context creation\n * - State validation\n * \n * This eliminates code duplication between the implementations.\n */\nexport abstract class ObjectKernelBase {\n protected plugins: Map<string, Plugin> = new Map();\n protected services: IServiceRegistry | Map<string, any> = new Map();\n protected hooks: Map<string, Array<(...args: any[]) => void | Promise<void>>> = new Map();\n protected state: KernelState = 'idle';\n protected logger: Logger;\n protected context!: PluginContext;\n /**\n * Name of the plugin whose init() is currently executing (Phase 1 runs\n * sequentially, so there is at most one). Lets a getService miss during\n * init name the structural fault (#4131) instead of only the symptom.\n */\n protected currentlyInitializing?: string;\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n /**\n * Validate kernel state\n * @param requiredState - Required state for the operation\n * @throws Error if current state doesn't match\n */\n protected validateState(requiredState: KernelState): void {\n if (this.state !== requiredState) {\n throw new Error(\n `[Kernel] Invalid state: expected '${requiredState}', got '${this.state}'`\n );\n }\n }\n\n /**\n * Validate kernel is in idle state (for plugin registration)\n */\n protected validateIdle(): void {\n if (this.state !== 'idle') {\n throw new Error('[Kernel] Cannot register plugins after bootstrap has started');\n }\n }\n\n /**\n * Create the plugin context\n * Subclasses can override to customize context creation\n */\n protected createContext(): PluginContext {\n return {\n registerService: (name, service) => {\n if (this.services instanceof Map) {\n if (this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' already registered`);\n }\n this.services.set(name, service);\n } else {\n // IServiceRegistry implementation\n this.services.register(name, service);\n }\n this.logger.info(`Service '${name}' registered`, { service: name });\n },\n getService: <T>(name: string): T => {\n if (this.services instanceof Map) {\n const service = this.services.get(name);\n if (!service) {\n throw new Error(\n `[Kernel] Service '${name}' not found${this.describeInitOrderFault(name)}`\n );\n }\n return service as T;\n } else {\n // IServiceRegistry implementation\n return this.services.get<T>(name);\n }\n },\n replaceService: <T>(name: string, implementation: T): void => {\n if (this.services instanceof Map) {\n if (!this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' not found. Use registerService() to add new services.`);\n }\n this.services.set(name, implementation);\n } else {\n // IServiceRegistry implementation\n if (!this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' not found. Use registerService() to add new services.`);\n }\n this.services.register(name, implementation);\n }\n this.logger.info(`Service '${name}' replaced`, { service: name });\n },\n hook: (name, handler) => {\n if (!this.hooks.has(name)) {\n this.hooks.set(name, []);\n }\n this.hooks.get(name)!.push(handler);\n },\n trigger: async (name, ...args) => {\n const handlers = this.hooks.get(name) || [];\n for (const handler of handlers) {\n await handler(...args);\n }\n },\n getServices: () => {\n if (this.services instanceof Map) {\n return new Map(this.services);\n } else {\n // For IServiceRegistry, we need to return the underlying Map\n // This is a compatibility method\n return new Map();\n }\n },\n logger: this.logger,\n getKernel: () => this as any,\n registerServiceFactory: (_name, _factory, _lifecycle, _dependencies) => {\n throw new Error('[KernelBase] registerServiceFactory not supported — use ObjectKernel');\n },\n getServiceScoped: async <T>(_name: string, _scopeId: string): Promise<T> => {\n throw new Error('[KernelBase] getServiceScoped not supported — use ObjectKernel');\n },\n };\n }\n\n /**\n * Resolve plugin dependencies using topological sort — `dependencies`\n * hard, `optionalDependencies` order-if-present (ADR-0116, #4131). One\n * implementation shared with ObjectKernel via `plugin-order.ts`.\n * @returns Ordered list of plugins (dependencies first)\n */\n protected resolveDependencies(): Plugin[] {\n return resolvePluginOrder(this.plugins);\n }\n\n /**\n * Whether a service is registered on this kernel right now. Backs the\n * init-service contract checks (#4131).\n */\n protected hasRegisteredService(name: string): boolean {\n // Both the plain Map and IServiceRegistry expose `has`.\n return this.services.has(name);\n }\n\n /**\n * Pre-Phase-1 ordering validation (ADR-0116, #4131): a plugin whose\n * `requiresServices` names a service provided only by a LATER plugin is\n * a named boot error before any init side effects.\n */\n protected validateInitServices(ordered: Plugin[]): void {\n validateInitServiceContract(ordered, (name) => this.hasRegisteredService(name));\n }\n\n /**\n * When a getService miss happens while a plugin's init() is running,\n * append the structural diagnosis (#4131): which plugin was initializing,\n * and — when a composed plugin declares the service — who provides it.\n * Empty string outside Phase 1, so non-boot messages stay unchanged.\n */\n protected describeInitOrderFault(serviceName: string): string {\n return describeInitOrderFault(this.currentlyInitializing, this.plugins.values(), serviceName);\n }\n\n /**\n * Run plugin init phase\n * @param plugin - Plugin to initialize\n */\n protected async runPluginInit(plugin: Plugin): Promise<void> {\n const pluginName = plugin.name;\n this.logger.info(`Initializing plugin: ${pluginName}`);\n\n // Authoritative init-service check (#4131): Phase 1 is sequential,\n // so a required service absent NOW is absent for this init.\n assertInitServiceRequirements(plugin, (name) => this.hasRegisteredService(name));\n\n this.currentlyInitializing = pluginName;\n try {\n await plugin.init(this.context);\n this.logger.info(`Plugin initialized: ${pluginName}`);\n } catch (error) {\n this.logger.error(`Plugin init failed: ${pluginName}`, error as Error);\n throw error;\n } finally {\n this.currentlyInitializing = undefined;\n }\n }\n\n /**\n * Run plugin start phase\n * @param plugin - Plugin to start\n */\n protected async runPluginStart(plugin: Plugin): Promise<void> {\n if (!plugin.start) return;\n \n const pluginName = plugin.name;\n this.logger.info(`Starting plugin: ${pluginName}`);\n \n try {\n await plugin.start(this.context);\n this.logger.info(`Plugin started: ${pluginName}`);\n } catch (error) {\n this.logger.error(`Plugin start failed: ${pluginName}`, error as Error);\n throw error;\n }\n }\n\n /**\n * Run plugin destroy phase\n * @param plugin - Plugin to destroy\n */\n protected async runPluginDestroy(plugin: Plugin): Promise<void> {\n if (!plugin.destroy) return;\n \n const pluginName = plugin.name;\n this.logger.info(`Destroying plugin: ${pluginName}`);\n \n try {\n await plugin.destroy();\n this.logger.info(`Plugin destroyed: ${pluginName}`);\n } catch (error) {\n this.logger.error(`Plugin destroy failed: ${pluginName}`, error as Error);\n throw error;\n }\n }\n\n /**\n * Trigger a hook with all registered handlers, ISOLATING failures: a\n * handler that throws is logged and the remaining handlers still run.\n *\n * Use this for hooks where one subscriber's failure must not deny the\n * others their turn — notification-style hooks, and `kernel:shutdown`,\n * where the handlers still queued behind the failing one are the cleanup\n * that flushes buffers and releases resources (#5257).\n *\n * It is the WRONG dispatcher for anything on the BOOT path. Every hook\n * dispatched before \"✅ Bootstrap complete\" is a precondition of that\n * claim, so swallowing a throw there does not rescue the boot — it only\n * hides the failure behind a process that reports success. Those hooks\n * (`kernel:ready`, `kernel:bootstrapped`, `kernel:listening`) use\n * {@link triggerHookOrThrow} (#5170, #5257).\n *\n * @param name - Hook name\n * @param args - Arguments to pass to handlers\n */\n protected async triggerHook(name: string, ...args: any[]): Promise<void> {\n const handlers = this.hooks.get(name) || [];\n this.logger.debug(`Triggering hook: ${name}`, { \n hook: name, \n handlerCount: handlers.length \n });\n \n for (const handler of handlers) {\n try {\n await handler(...args);\n } catch (error) {\n this.logger.error(`Hook handler failed: ${name}`, error as Error);\n // Continue with other handlers even if one fails\n }\n }\n }\n\n /**\n * Trigger a hook with all registered handlers, PROPAGATING the first\n * failure: the remaining handlers do not run and the original error\n * reaches the caller unwrapped.\n *\n * This is the dispatch semantics `ObjectKernel` has always had for every\n * lifecycle hook (its `context.trigger` is a bare awaited loop that never\n * catches). `LiteKernel` used the isolating {@link triggerHook} for all of\n * them, so one hook name meant two opposite things depending on which\n * kernel booted the same plugin code (#5170).\n *\n * `LiteKernel` now uses this dispatcher for all three BOOT-path hooks:\n *\n * - `kernel:ready` (#5170) — the only correct moment for a plugin to\n * assert that the preconditions it declared were actually met (the\n * registries are still filling during `init()`), so \"declared but not\n * deliverable ⇒ refuse to boot\" gates live there. On LiteKernel, which\n * is what vitest/serverless/edge run, they were downgraded to an error\n * log while the process carried on serving traffic without the\n * guarantee it claimed.\n * - `kernel:bootstrapped` and `kernel:listening` (#5257) — the same\n * argument one hook later. `kernel:listening` is where HTTP server\n * plugins open their socket, so a swallowed failure there produced the\n * worst shape available: a live process printing \"✅ Bootstrap complete\"\n * with nothing listening. `kernel:bootstrapped` carries reconcile and\n * audit passes whose silent failure is a quieter version of the same\n * lie.\n *\n * Deliberately NOT applied to `kernel:shutdown`, which keeps\n * {@link triggerHook}: on the teardown path a failing handler must not\n * block the cleanup queued behind it. That is a per-hook judgement\n * recorded at the dispatch site in `lite-kernel.ts`, not an inherited\n * default — and it is the reason this dispatcher is chosen per hook rather\n * than swapped in wholesale.\n *\n * @param name - Hook name\n * @param args - Arguments to pass to handlers\n */\n protected async triggerHookOrThrow(name: string, ...args: any[]): Promise<void> {\n const handlers = this.hooks.get(name) || [];\n this.logger.debug(`Triggering hook: ${name}`, {\n hook: name,\n handlerCount: handlers.length,\n });\n\n for (const handler of handlers) {\n await handler(...args);\n }\n }\n\n /**\n * Get current kernel state\n */\n getState(): KernelState {\n return this.state;\n }\n\n /**\n * Get all registered plugins\n */\n getPlugins(): Map<string, Plugin> {\n return new Map(this.plugins);\n }\n\n /**\n * Abstract methods to be implemented by subclasses\n */\n abstract use(plugin: Plugin): this | Promise<this>;\n abstract bootstrap(): Promise<void>;\n abstract destroy(): Promise<void>;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { LoggerConfig, LogLevel } from '@objectstack/spec/system';\nimport type { Logger } from '@objectstack/spec/contracts';\n\n// Re-export the contract type so consumers can do\n// `import type { Logger } from '@objectstack/core/logger'` without also\n// pulling `@objectstack/spec` into their bundle graph manually.\nexport type { Logger };\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n fatal: 4,\n silent: 5,\n};\n\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n debug: '\\x1b[36m',\n info: '\\x1b[32m',\n warn: '\\x1b[33m',\n error: '\\x1b[31m',\n fatal: '\\x1b[35m',\n silent: '',\n};\n\nconst RESET = '\\x1b[0m';\n\n/**\n * Split a field name into lowercase words on camelCase, `snake_case`,\n * `kebab-case`, dot and letter/digit boundaries.\n *\n * `apiKey` / `api_key` / `API_KEY` / `x-api-key` all tokenize to\n * `['api','key']`, while `monkey`, `keyword` and `tokenizer` stay a single\n * word. That difference is the whole point: it is what makes the redactor a\n * **word-boundary** matcher instead of the substring matcher it used to be\n * (#5573) — a plain `keys` field no longer reads as a secret.\n */\nfunction tokenizeFieldName(name: string): string[] {\n return name\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2') // apiKey -> api Key\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // APIKey -> API Key\n .replace(/([a-zA-Z])([0-9])/g, '$1 $2') // key2 -> key 2\n .split(/[^A-Za-z0-9]+/) // _ - . / space\n .filter(Boolean)\n .map((word) => word.toLowerCase());\n}\n\n/**\n * Singular form of the plural spellings the redact vocabulary actually meets\n * (`keys`, `tokens`, `secrets`, `passwords`, `passes`). Deliberately not a\n * general inflector — it only has to be right for words that end up next to a\n * redact word, and it must never turn `address`/`status` into a new word.\n */\nfunction singularizeWord(word: string): string {\n if (/(?:ss|us|is)$/.test(word)) return word; // address / status / axis\n if (/(?:ch|sh|s|x|z)es$/.test(word)) return word.slice(0, -2); // passes / boxes\n if (/[a-z0-9]s$/.test(word)) return word.slice(0, -1); // keys / tokens\n return word;\n}\n\n/**\n * Words that mark the *secret* sense of a redact word when they are glued to\n * it with no boundary to split on: `apikey`, `accesstoken`, `clientsecret`.\n *\n * Word-boundary matching covers every field name spelled the way this repo\n * spells names (camelCase config keys / snake_case machine names — Prime\n * Directive #3), but an all-lowercase concatenation has no boundary at all, so\n * `apikey` would tokenize to one word and stop being redacted. A bare\n * \"ends with `key`\" rule cannot be used to rescue it, because `monkey`,\n * `turkey` and `whiskey` end with `key` too — the exact false positives #5573\n * exists to remove. So the rescue is scoped to this explicit qualifier list:\n * `<qualifier><redact word>` is a secret, anything else glued to a redact word\n * is not.\n *\n * Consequences, on purpose:\n * - Only a **suffix** concatenation counts. `secretary` and `keyword` start\n * with a redact word and stay clear.\n * - An unlisted qualifier (`foobarkey`) is not redacted. The fix is to spell\n * the field `fooBarKey` / `foo_bar_key`, which matches generically — or to\n * add the word here.\n */\nconst CONCATENATED_SECRET_QUALIFIERS = new Set([\n 'access',\n 'account',\n 'admin',\n 'api',\n 'app',\n 'auth',\n 'bearer',\n 'client',\n 'csrf',\n 'db',\n 'database',\n 'encryption',\n 'id',\n 'jwt',\n 'master',\n 'oauth',\n 'private',\n 'public',\n 'refresh',\n 'root',\n 'secret',\n 'service',\n 'session',\n 'shared',\n 'sign',\n 'signing',\n 'ssh',\n 'token',\n 'user',\n 'webhook',\n 'xsrf',\n]);\n\n/** `apikey`/`apikeys` vs `key` — see {@link CONCATENATED_SECRET_QUALIFIERS}. */\nfunction isQualifiedConcatenation(word: string, redactWord: string): boolean {\n for (const base of [word, singularizeWord(word)]) {\n if (base.length <= redactWord.length || !base.endsWith(redactWord)) continue;\n if (CONCATENATED_SECRET_QUALIFIERS.has(base.slice(0, base.length - redactWord.length))) return true;\n }\n return false;\n}\n\n/** Does `words` contain `run` as a consecutive sub-sequence? */\nfunction containsWordRun(words: string[], run: string[]): boolean {\n for (let i = 0; i + run.length <= words.length; i++) {\n if (run.every((word, offset) => words[i + offset] === word)) return true;\n }\n return false;\n}\n\n/**\n * Word-boundary match of one configured redact pattern against one field name,\n * both already tokenized by {@link tokenizeFieldName}.\n *\n * The plural rule is the one subtlety, and it is the maintainer's ruling on\n * #5573 made consistent with itself: a **bare** plural names a collection or a\n * count, not a secret (`keys` on a Zod `unrecognized_keys` issue, `tokens` on\n * an LLM usage record), so it is left alone; a plural **inside a compound**\n * still names the secret (`apiKeys: ['sk-…']`, `refresh_tokens`) and is\n * redacted. Singular words match everywhere, compound or not.\n */\nfunction fieldWordsMatchPattern(nameWords: string[], patternWords: string[]): boolean {\n if (patternWords.length === 0 || nameWords.length === 0) return false;\n\n // A multi-word pattern (`apiKey`, `api_key`) matches a consecutive run of\n // the same words, or those words written as one concatenated token.\n if (patternWords.length > 1) {\n const glued = patternWords.join('');\n return (\n containsWordRun(nameWords, patternWords) ||\n nameWords.some((word) => word === glued || singularizeWord(word) === glued)\n );\n }\n\n const redactWord = patternWords[0];\n const isCompound = nameWords.filter((word) => /[a-z]/.test(word)).length > 1;\n return nameWords.some(\n (word) =>\n word === redactWord ||\n (isCompound && singularizeWord(word) === redactWord) ||\n isQualifiedConcatenation(word, redactWord),\n );\n}\n\n/**\n * Whether ANSI color may be written to the given stream.\n *\n * Follows the https://no-color.org convention: a non-empty `NO_COLOR` env var\n * disables color regardless of TTY, and non-TTY destinations (pipes, CI logs,\n * redirected output) always get plain text so plain-text log scanners see\n * uncolored level tags. Browser bundles have no `process`/TTY → plain text.\n */\nfunction colorEnabled(stream: { isTTY?: boolean } | undefined): boolean {\n if (typeof process !== 'undefined') {\n const noColor = (process as any).env?.NO_COLOR;\n if (noColor !== undefined && noColor !== '') return false;\n }\n return Boolean(stream?.isTTY);\n}\n\n/**\n * Resolve a Node builtin without putting it in this module's import graph.\n *\n * This entry is deliberately browser-safe — `@objectstack/client` bundles it —\n * so `fs`/`path` must never be imported statically. A lazy `require()` used to\n * meet that bar, but esbuild rewrites it to the `__require` shim in the ESM\n * output, which throws `Dynamic require of \"fs\" is not supported`. Every Node\n * ESM consumer (`os serve`, `os dev`) therefore lost file logging (#3110).\n * `process.getBuiltinModule` is a plain method call — opaque to bundlers — and\n * works in both module systems.\n */\nfunction loadNodeBuiltin<T>(id: string): T | undefined {\n if (typeof process === 'undefined') return undefined;\n\n const getBuiltinModule = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule;\n if (typeof getBuiltinModule === 'function') {\n try {\n return getBuiltinModule.call(process, `node:${id}`) as T;\n } catch {\n return undefined;\n }\n }\n\n // Node < 20.16 / < 22.3 predates `getBuiltinModule`. Real `require` still\n // resolves in the CJS build; in the ESM build this is the shim that throws,\n // which the caller now reports rather than swallows.\n try {\n return require(id) as T;\n } catch {\n return undefined;\n }\n}\n\nexport class ObjectLogger implements Logger {\n private config: Required<Omit<LoggerConfig, 'file' | 'rotation' | 'name'>> & {\n file?: string;\n rotation?: { maxSize: string; maxFiles: number };\n name?: string;\n };\n private bindings: Record<string, any>;\n /** `config.redact`, tokenized once — see {@link fieldWordsMatchPattern}. */\n private redactPatterns: string[][];\n private fileStream?: any;\n /** Only the logger that opened the stream may close it — children share it. */\n private ownsFileStream = false;\n private fileLoggingDisabled = false;\n\n constructor(config: Partial<LoggerConfig> = {}, bindings: Record<string, any> = {}) {\n this.config = {\n name: config.name,\n level: config.level ?? 'info',\n format: config.format ?? 'pretty',\n redact: config.redact ?? ['password', 'token', 'secret', 'key'],\n sourceLocation: config.sourceLocation ?? false,\n file: config.file,\n rotation: config.rotation ?? { maxSize: '10m', maxFiles: 5 },\n };\n this.bindings = bindings;\n this.redactPatterns = this.config.redact.map(tokenizeFieldName).filter((words) => words.length > 0);\n\n if (this.config.file && typeof process !== 'undefined') {\n this.openFileStream(this.config.file);\n }\n }\n\n private openFileStream(path: string) {\n const fs = loadNodeBuiltin<typeof import('node:fs')>('fs');\n const nodePath = loadNodeBuiltin<typeof import('node:path')>('path');\n if (!fs || !nodePath) {\n this.disableFileLogging(path, 'no filesystem access in this runtime');\n return;\n }\n\n try {\n fs.mkdirSync(nodePath.dirname(path), { recursive: true });\n const stream = fs.createWriteStream(path, { flags: 'a' });\n // `createWriteStream` reports open failures (EACCES, EISDIR, …)\n // asynchronously. An 'error' event with no listener is fatal to the\n // process, so file logging must degrade here rather than take the\n // host down.\n stream.on('error', (err: Error) => this.disableFileLogging(path, err.message));\n this.fileStream = stream;\n this.ownsFileStream = true;\n } catch (err) {\n this.disableFileLogging(path, (err as Error).message);\n }\n }\n\n /**\n * Report — once — that an explicitly configured `file` destination is not\n * being written, and stop trying.\n *\n * Deliberately not routed through `write()`: this says the logger cannot\n * honour its own config, so `level` must not filter it. The bare `catch {}`\n * this replaces is exactly how #3110 stayed hidden.\n */\n private disableFileLogging(path: string, reason: string) {\n this.fileStream = undefined;\n this.ownsFileStream = false;\n if (this.fileLoggingDisabled) return;\n this.fileLoggingDisabled = true;\n\n const label = this.config.name ? `[${this.config.name}] ` : '';\n const notice = `${label}logger: file logging disabled — cannot write to ${path}: ${reason}`;\n if (typeof process !== 'undefined' && (process as any).stderr) {\n (process as any).stderr.write(notice + '\\n');\n } else if (typeof console !== 'undefined') {\n console.warn(notice);\n }\n }\n\n private isEnabled(level: LogLevel): boolean {\n return LEVEL_ORDER[level] >= LEVEL_ORDER[this.config.level];\n }\n\n /**\n * Whether a meta field name names one of the configured secrets.\n *\n * Until #5573 this was `lower.includes(pattern)`, which redacted every\n * field whose name merely *contained* a redact word — `keys`, `keyword`,\n * `tokens`, `monkey`, `secretary` — and replaced its value with\n * `***REDACTED***`, so the reader lost the fact AND was told a secret had\n * been withheld. Matching is now on word boundaries: `key` matches\n * `apiKey` / `api_key`, not `keys` / `monkey` / `keyword`.\n */\n private isRedactedFieldName(key: string): boolean {\n const nameWords = tokenizeFieldName(key);\n return this.redactPatterns.some((pattern) => fieldWordsMatchPattern(nameWords, pattern));\n }\n\n private redactSensitive(obj: any): any {\n if (!obj || typeof obj !== 'object') return obj;\n const redacted = Array.isArray(obj) ? [...obj] : { ...obj };\n for (const key in redacted) {\n if (this.isRedactedFieldName(key)) {\n redacted[key] = '***REDACTED***';\n } else if (typeof redacted[key] === 'object' && redacted[key] !== null) {\n redacted[key] = this.redactSensitive(redacted[key]);\n }\n }\n return redacted;\n }\n\n private write(level: LogLevel, message: string, meta?: Record<string, any>, error?: Error) {\n if (!this.isEnabled(level)) return;\n\n const context = this.redactSensitive({\n ...this.bindings,\n ...meta,\n ...(error ? { error: { message: error.message, stack: error.stack } } : {}),\n });\n\n const hasContext = Object.keys(context).length > 0;\n const ts = new Date().toISOString();\n\n const isErrorLevel = level === 'error' || level === 'fatal';\n const proc = typeof process !== 'undefined' ? (process as any) : undefined;\n const stream = proc ? (isErrorLevel ? proc.stderr : proc.stdout) : undefined;\n\n let line: string; // console output — may carry ANSI color\n let plainLine: string; // file output — never colored\n\n if (this.config.format === 'json') {\n line = plainLine = JSON.stringify({\n time: ts,\n level,\n ...(this.config.name ? { name: this.config.name } : {}),\n msg: message,\n ...context,\n });\n } else if (this.config.format === 'text') {\n const parts = [ts, level.toUpperCase(), message];\n if (hasContext) parts.push(JSON.stringify(context));\n line = plainLine = parts.join(' | ');\n } else {\n // pretty\n const label = this.config.name ? `[${this.config.name}] ` : '';\n const head = `${ts} ${level.toUpperCase()}`;\n let tail = ` ${label}${message}`;\n if (hasContext) tail += ` ${JSON.stringify(context)}`;\n plainLine = head + tail;\n const color = LEVEL_COLORS[level] || '';\n line = color && colorEnabled(stream) ? `${color}${head}${RESET}${tail}` : plainLine;\n }\n\n // Browser-safe output: prefer process streams when available, otherwise\n // fall back to console. `process` may be missing entirely (browsers) or\n // present without stdio streams (bundler shims) — both fall through to\n // console. The previous unguarded `process.stderr?.write` threw\n // `ReferenceError: process is not defined` in browsers because\n // `process` itself is the missing global, not just its `stderr` field.\n if (stream) {\n stream.write(line + '\\n');\n } else if (typeof console !== 'undefined') {\n const fn =\n level === 'error' || level === 'fatal' ? console.error\n : level === 'warn' ? console.warn\n : level === 'debug' ? console.debug\n : console.log;\n fn(line);\n }\n\n if (this.fileStream) {\n this.fileStream.write(plainLine + '\\n');\n }\n }\n\n debug(message: string, meta?: Record<string, any>): void {\n this.write('debug', message, meta);\n }\n\n info(message: string, meta?: Record<string, any>): void {\n this.write('info', message, meta);\n }\n\n warn(message: string, meta?: Record<string, any>): void {\n this.write('warn', message, meta);\n }\n\n error(message: string, errorOrMeta?: Error | Record<string, any>, meta?: Record<string, any>): void {\n this.writeErrorLike('error', message, errorOrMeta, meta);\n }\n\n fatal(message: string, errorOrMeta?: Error | Record<string, any>, meta?: Record<string, any>): void {\n this.writeErrorLike('fatal', message, errorOrMeta, meta);\n }\n\n /**\n * `error`/`fatal` dispatch — the two levels whose contract has an `Error`\n * slot in front of `meta`.\n *\n * The `Logger` contract declares `error(message, error?: Error, meta?)`, and\n * `ObjectLogger` additionally tolerates a **meta object** in the `error`\n * slot because many in-repo call sites write `logger.error(msg, { … })`.\n * That tolerance is fine; dropping a parameter the contract *declares* is\n * not, and that is what the previous dispatch did:\n *\n * if (errorOrMeta instanceof Error) this.write(level, message, meta, errorOrMeta);\n * else this.write(level, message, errorOrMeta);\n *\n * With `error === undefined` the `else` branch passed `undefined` as the\n * meta and **never read the third argument**, so every contract-shaped\n * `logger.error(msg, undefined, { … })` call rendered a bare message with\n * its diagnostics silently gone — ~15 such call sites across `metadata`,\n * `metadata-protocol`, `client` and `core/security`, plus the connector\n * reconcile seam that found this (#5575). The contract's two sibling\n * implementations (`ConsoleLogger`/`JsonLogger` in `@objectstack/observability`)\n * both honour the slot, so the contract was right and this class was the\n * outlier — declared ≠ enforced, Prime Directive #10.\n *\n * All three shapes are now honoured. When both slots carry meta, `meta`\n * (the later, more specific argument) wins on a key collision.\n */\n private writeErrorLike(\n level: 'error' | 'fatal',\n message: string,\n errorOrMeta?: Error | Record<string, any>,\n meta?: Record<string, any>,\n ): void {\n if (errorOrMeta instanceof Error) {\n this.write(level, message, meta, errorOrMeta);\n return;\n }\n const merged = errorOrMeta && meta ? { ...errorOrMeta, ...meta } : (errorOrMeta ?? meta);\n this.write(level, message, merged);\n }\n\n log(message: string, ...args: any[]): void {\n this.info(message, args.length > 0 ? { args } : undefined);\n }\n\n child(context: Record<string, any>): ObjectLogger {\n // Construct without `file`, then share the parent's stream: the\n // constructor opens eagerly, so passing `file` through would open a\n // second stream per child and immediately orphan it. That leak was\n // unreachable while #3110 kept the ESM open path dead.\n const child = new ObjectLogger({ ...this.config, file: undefined }, { ...this.bindings, ...context });\n child.config.file = this.config.file;\n child.fileStream = this.fileStream;\n return child;\n }\n\n withTrace(traceId: string, spanId?: string): ObjectLogger {\n return this.child({ traceId, spanId });\n }\n\n async destroy(): Promise<void> {\n const stream = this.fileStream;\n this.fileStream = undefined;\n // Children share the opener's stream; if they closed it too, one child's\n // teardown would end file logging for the parent and every sibling,\n // whose writes then land on a closed stream and only trip the 'error'\n // handler above.\n if (!stream || !this.ownsFileStream) return;\n this.ownsFileStream = false;\n await new Promise<void>((resolve) => stream.end(resolve));\n }\n}\n\nexport function createLogger(config?: Partial<LoggerConfig>): ObjectLogger {\n return new ObjectLogger(config);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext } from './types.js';\nimport { createLogger, ObjectLogger } from './logger.js';\nimport type { LoggerConfig } from '@objectstack/spec/system';\nimport { ServiceRequirementDef } from '@objectstack/spec/system';\nimport { PluginLoader, PluginMetadata, ServiceLifecycle, ServiceFactory, PluginStartupResult } from './plugin-loader.js';\nimport { isNode, safeExit } from './utils/env.js';\nimport { CORE_FALLBACK_FACTORIES } from './fallbacks/index.js';\nimport {\n resolvePluginOrder,\n validateInitServiceContract,\n assertInitServiceRequirements,\n describeInitOrderFault,\n} from './plugin-order.js';\n\n/**\n * Enhanced Kernel Configuration\n */\nexport interface ObjectKernelConfig {\n logger?: Partial<LoggerConfig>;\n \n /** Default plugin startup timeout in milliseconds */\n defaultStartupTimeout?: number;\n \n /** Whether to enable graceful shutdown */\n gracefulShutdown?: boolean;\n \n /** Graceful shutdown timeout in milliseconds */\n shutdownTimeout?: number;\n \n /** Whether to rollback on startup failure */\n rollbackOnFailure?: boolean;\n \n /** Whether to skip strict system requirement validation (Critical for testing) */\n skipSystemValidation?: boolean;\n}\n\n/**\n * Enhanced ObjectKernel with Advanced Plugin Management\n * \n * Extends the basic ObjectKernel with:\n * - Async plugin loading with validation\n * - Version compatibility checking\n * - Plugin signature verification\n * - Configuration validation (Zod)\n * - Factory-based dependency injection\n * - Service lifecycle management (singleton/transient/scoped)\n * - Circular dependency detection\n * - Lazy loading services\n * - Graceful shutdown\n * - Plugin startup timeout control\n * - Startup failure rollback\n * - Plugin health checks\n */\nexport class ObjectKernel {\n private plugins: Map<string, PluginMetadata> = new Map();\n private services: Map<string, any> = new Map();\n private hooks: Map<string, Array<(...args: any[]) => void | Promise<void>>> = new Map();\n private state: 'idle' | 'initializing' | 'running' | 'stopping' | 'stopped' = 'idle';\n private logger: ObjectLogger;\n private context: PluginContext;\n private pluginLoader: PluginLoader;\n private config: ObjectKernelConfig;\n private startedPlugins: Set<string> = new Set();\n private pluginStartTimes: Map<string, number> = new Map();\n private shutdownHandlers: Array<() => Promise<void>> = [];\n /**\n * Name of the plugin whose init() is currently executing (Phase 1 is\n * sequential, so at most one). Lets a getService miss during init name\n * the structural fault (#4131) instead of only the symptom.\n */\n private currentlyInitializing?: string;\n\n constructor(config: ObjectKernelConfig = {}) {\n this.config = {\n defaultStartupTimeout: 30000, // 30 seconds\n gracefulShutdown: true,\n shutdownTimeout: 60000, // 60 seconds\n rollbackOnFailure: true,\n ...config,\n };\n\n this.logger = createLogger(config.logger);\n this.pluginLoader = new PluginLoader(this.logger);\n \n // Initialize context\n this.context = {\n registerService: (name, service) => {\n this.registerService(name, service);\n },\n registerServiceFactory: (name, factory, lifecycle, dependencies) => {\n this.registerServiceFactory(name, factory, lifecycle, dependencies);\n },\n getService: <T>(name: string) => {\n // 1. Try direct service map first (synchronous cache)\n const service = this.services.get(name);\n if (service) {\n return service as T;\n }\n\n // 2. Try to get from plugin loader cache (Sync access to factories)\n const loaderService = this.pluginLoader.getServiceInstance<T>(name);\n if (loaderService) {\n // Cache it locally for faster next access\n this.services.set(name, loaderService);\n return loaderService;\n }\n\n // 3. Neither sync map has it. Two very different faults share\n // this branch and MUST NOT share one message (#4085):\n // (a) nothing ever registered `name` — a composition /\n // ordering fault at the CALLER (e.g. a plugin reaching\n // for `manifest` in init() before the engine plugin\n // registered it);\n // (b) `name` IS registered, as a factory that has not been\n // instantiated yet — the caller merely used the wrong\n // accessor and needs `getServiceAsync`.\n // `pluginLoader.getService` is an `async` method, so its\n // return value is ALWAYS a Promise and its internal\n // \"not found\" rejection can never surface synchronously.\n // Reading (a) off that Promise therefore reported every\n // missing service as \"is async - use await\" — the wrong fix,\n // pointing at the wrong layer. Decide from the registry\n // instead, which is synchronous and authoritative.\n if (!this.pluginLoader.hasService(name)) {\n throw new Error(\n `[Kernel] Service '${name}' not found${this.describeInitOrderFault(name)}`\n );\n }\n\n // Registered but not instantiated ⇒ factory-backed. Message\n // kept verbatim: callers that tolerate an async-only service\n // (console static assets, the HTTP dispatcher) match on\n // `is async`.\n throw new Error(`Service '${name}' is async - use await`);\n },\n replaceService: <T>(name: string, implementation: T): void => {\n const hasService = this.services.has(name) || this.pluginLoader.hasService(name);\n if (!hasService) {\n throw new Error(`[Kernel] Service '${name}' not found. Use registerService() to add new services.`);\n }\n this.services.set(name, implementation);\n this.pluginLoader.replaceService(name, implementation);\n this.logger.info(`Service '${name}' replaced`, { service: name });\n },\n hook: (name, handler) => {\n if (!this.hooks.has(name)) {\n this.hooks.set(name, []);\n }\n this.hooks.get(name)!.push(handler);\n },\n trigger: async (name, ...args) => {\n const handlers = this.hooks.get(name) || [];\n for (const handler of handlers) {\n await handler(...args);\n }\n },\n getServices: () => {\n return new Map(this.services);\n },\n getServiceScoped: <T>(name: string, scopeId: string): Promise<T> => {\n return this.pluginLoader.getService<T>(name, scopeId);\n },\n logger: this.logger,\n getKernel: () => this as any, // Type compatibility\n };\n\n this.pluginLoader.setContext(this.context);\n\n // Register shutdown handler\n if (this.config.gracefulShutdown) {\n this.registerShutdownSignals();\n }\n }\n\n /**\n * Register a plugin with enhanced validation\n */\n async use(plugin: Plugin): Promise<this> {\n if (this.state !== 'idle') {\n throw new Error('[Kernel] Cannot register plugins after bootstrap has started');\n }\n\n // Load plugin through enhanced loader\n const result = await this.pluginLoader.loadPlugin(plugin);\n \n if (!result.success || !result.plugin) {\n throw new Error(`Failed to load plugin: ${plugin.name} - ${result.error?.message}`);\n }\n\n const pluginMeta = result.plugin;\n this.plugins.set(pluginMeta.name, pluginMeta);\n \n this.logger.info(`Plugin registered: ${pluginMeta.name}@${pluginMeta.version}`, {\n plugin: pluginMeta.name,\n version: pluginMeta.version,\n });\n\n return this;\n }\n\n /**\n * Register a service instance directly\n */\n registerService<T>(name: string, service: T): this {\n if (this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' already registered`);\n }\n this.services.set(name, service);\n this.pluginLoader.registerService(name, service);\n this.logger.info(`Service '${name}' registered`, { service: name });\n return this;\n }\n\n /**\n * Register a service factory with lifecycle management\n */\n registerServiceFactory<T>(\n name: string,\n factory: ServiceFactory<T>,\n lifecycle: ServiceLifecycle = ServiceLifecycle.SINGLETON,\n dependencies?: string[]\n ): this {\n this.pluginLoader.registerServiceFactory({\n name,\n factory,\n lifecycle,\n dependencies,\n });\n return this;\n }\n\n /**\n * Pre-inject in-memory fallbacks for 'core' services that were not registered\n * by plugins during Phase 1. Called before Phase 2 so that all core services\n * (e.g. 'metadata', 'cache', 'queue') are resolvable via ctx.getService()\n * when plugin start() methods execute.\n */\n private preInjectCoreFallbacks() {\n if (this.config.skipSystemValidation) return;\n for (const [serviceName, criticality] of Object.entries(ServiceRequirementDef)) {\n if (criticality !== 'core') continue;\n const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);\n if (!hasService) {\n const factory = CORE_FALLBACK_FACTORIES[serviceName];\n if (factory) {\n const fallback = factory();\n this.registerService(serviceName, fallback);\n this.logger.debug(`[Kernel] Pre-injected in-memory fallback for '${serviceName}' before Phase 2`);\n }\n }\n }\n }\n\n /**\n * Validate Critical System Requirements\n */\n private validateSystemRequirements() {\n if (this.config.skipSystemValidation) {\n this.logger.debug('System requirement validation skipped');\n return;\n }\n\n this.logger.debug('Validating system service requirements...');\n const missingServices: string[] = [];\n const missingCoreServices: string[] = [];\n \n // Iterate through all defined requirements\n for (const [serviceName, criticality] of Object.entries(ServiceRequirementDef)) {\n const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);\n \n if (!hasService) {\n if (criticality === 'required') {\n this.logger.error(`CRITICAL: Required service missing: ${serviceName}`);\n missingServices.push(serviceName);\n } else if (criticality === 'core') {\n // Auto-inject in-memory fallback if available\n const factory = CORE_FALLBACK_FACTORIES[serviceName];\n if (factory) {\n const fallback = factory();\n this.registerService(serviceName, fallback);\n this.logger.warn(`Service '${serviceName}' not provided — using in-memory fallback`);\n } else {\n this.logger.warn(`CORE: Core service missing, functionality may be degraded: ${serviceName}`);\n missingCoreServices.push(serviceName);\n }\n } else {\n this.logger.info(`Info: Optional service not present: ${serviceName}`);\n }\n }\n }\n\n if (missingServices.length > 0) {\n const errorMsg = `System failed to start. Missing critical services: ${missingServices.join(', ')}`;\n this.logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n if (missingCoreServices.length > 0) {\n this.logger.warn(`System started with degraded capabilities. Missing core services: ${missingCoreServices.join(', ')}`);\n }\n \n this.logger.info('System requirement check passed');\n }\n\n /**\n * Bootstrap the kernel with enhanced features\n */\n async bootstrap(): Promise<void> {\n if (this.state !== 'idle') {\n throw new Error('[Kernel] Kernel already bootstrapped');\n }\n\n this.state = 'initializing';\n this.logger.info('Bootstrap started');\n\n try {\n // Check for circular dependencies\n const cycles = this.pluginLoader.detectCircularDependencies();\n if (cycles.length > 0) {\n this.logger.warn('Circular service dependencies detected:', { cycles });\n }\n\n // Resolve plugin dependencies\n const orderedPlugins = this.resolveDependencies();\n\n // Pre-Phase-1 ordering contract (ADR-0116, #4131): a plugin that\n // requires a service provided only by a later plugin fails HERE,\n // named, before any init side effects.\n validateInitServiceContract(orderedPlugins, (name) => this.hasAnyService(name));\n\n // Phase 1: Init - Plugins register services\n this.logger.info('Phase 1: Init plugins');\n for (const plugin of orderedPlugins) {\n await this.initPluginWithTimeout(plugin);\n }\n\n // Pre-inject in-memory fallbacks for 'core' services that were not\n // registered by any plugin during Phase 1. This ensures services like\n // 'metadata', 'cache', 'queue', etc. are always available when plugins\n // call ctx.getService() during their start() methods.\n this.preInjectCoreFallbacks();\n\n // Phase 2: Start - Plugins execute business logic\n this.logger.info('Phase 2: Start plugins');\n this.state = 'running';\n \n for (const plugin of orderedPlugins) {\n const result = await this.startPluginWithTimeout(plugin);\n \n if (!result.success) {\n this.logger.error(`Plugin startup failed: ${plugin.name}`, result.error);\n const origMsg = result.error instanceof Error ? result.error.message : String(result.error);\n const origStack = result.error instanceof Error ? result.error.stack : '';\n console.error(`[Kernel] Plugin startup failed: ${plugin.name}`, origMsg, origStack);\n\n if (this.config.rollbackOnFailure) {\n this.logger.warn('Rolling back started plugins...');\n await this.rollbackStartedPlugins();\n // Propagate the original cause through the thrown error\n // so callers (e.g. cloud auth-proxy) can surface the\n // real failure instead of an opaque \"rollback complete\"\n // string. Without this, every kernel-boot failure looks\n // identical from the outside.\n const err: any = new Error(\n `Plugin ${plugin.name} failed to start - rollback complete: ${origMsg}`,\n );\n if (result.error instanceof Error) {\n err.cause = result.error;\n err.originalStack = origStack;\n }\n throw err;\n }\n }\n }\n\n // Phase 3: Trigger kernel:ready hook\n this.validateSystemRequirements(); // Final check before ready\n this.logger.debug('Triggering kernel:ready hook');\n await this.context.trigger('kernel:ready');\n\n // Phase 3.5: Trigger kernel:bootstrapped AFTER every kernel:ready\n // handler has settled — the \"all synchronous bootstrap has settled\"\n // anchor. Reconcile/backfill work that consumes data produced by a\n // later-starting plugin's kernel:ready handler belongs here, not in\n // kernel:ready (where handler order would race the data). NOTE: this\n // does NOT guarantee background app seed data has settled (an inline\n // seed that overruns OS_INLINE_SEED_BUDGET_MS finishes later) —\n // subscribe `app:seeded` for that. See\n // packages/spec/src/contracts/plugin-lifecycle-events.ts.\n this.logger.debug('Triggering kernel:bootstrapped hook');\n await this.context.trigger('kernel:bootstrapped');\n\n // Phase 4: Trigger kernel:listening hook AFTER all kernel:ready\n // handlers have completed. This is the cue for HTTP server\n // plugins to actually open the listening socket — by now every\n // other plugin has finished registering routes/middleware.\n // See `kernel:listening` docs in\n // packages/spec/src/contracts/plugin-lifecycle-events.ts\n // for the race-condition rationale.\n this.logger.debug('Triggering kernel:listening hook');\n await this.context.trigger('kernel:listening');\n\n this.logger.info('✅ Bootstrap complete');\n } catch (error) {\n this.state = 'stopped';\n throw error;\n }\n }\n\n /**\n * Graceful shutdown with timeout\n */\n async shutdown(): Promise<void> {\n if (this.state === 'stopped' || this.state === 'stopping') {\n this.logger.warn('Kernel already stopped or stopping');\n return;\n }\n\n if (this.state !== 'running') {\n throw new Error('[Kernel] Kernel not running');\n }\n\n this.state = 'stopping';\n this.logger.info('Graceful shutdown started');\n\n // The ONE rejection that means \"teardown hung\". Created here so the\n // catch below can discriminate by IDENTITY (#5274): only this\n // `setTimeout` can produce this exact object, so no message match, no\n // `instanceof`, and nothing a plugin throws can ever impersonate it —\n // not even a handler throwing `new Error('Shutdown timeout exceeded')`.\n // That discrimination is the whole point: the catch used to be reached\n // by BOTH the timer and any exception escaping `performShutdown()`, and\n // it treated them identically — `process.exit(1)` under a log line\n // reading \"Shutdown timed out\" when nothing had timed out.\n const shutdownTimeoutError = new Error('Shutdown timeout exceeded');\n\n try {\n const shutdownPromise = this.performShutdown();\n const timeoutPromise = new Promise<void>((_, reject) => {\n const t = setTimeout(() => {\n reject(shutdownTimeoutError);\n }, this.config.shutdownTimeout);\n // Don't let this timer keep the event loop alive\n if (t.unref) t.unref();\n });\n\n await Promise.race([shutdownPromise, timeoutPromise]);\n\n this.state = 'stopped';\n this.logger.info('✅ Graceful shutdown complete');\n } catch (error) {\n this.state = 'stopped';\n\n if (error === shutdownTimeoutError) {\n // GENUINE timeout: `performShutdown()` is still running and has\n // stopped making progress, so the process would otherwise hang\n // holding whatever it failed to release. Hard-exit stays — it\n // is the only branch it was ever right for.\n this.logger.error('Shutdown timed out — forcing exit', error as Error);\n // Flush logger then hard-exit; the process would otherwise hang\n await this.logger.destroy();\n process.exit(1);\n } else {\n // NOT a timeout. `performShutdown()` isolates every teardown\n // step it owns (hook dispatch, each destroy(), each shutdown\n // handler), so reaching here means something outside those\n // loops failed — the teardown is over either way, and there is\n // nothing hung to escape from. Killing the host process here\n // would take away the embedding host's (cloud auth-proxy, CLI,\n // a test runner) chance to do its own cleanup, over a fault\n // that did not require it. Log and return down the normal\n // path; `shutdown()` still never rejects.\n this.logger.error(\n 'Shutdown finished with an unexpected teardown error — the kernel is stopped and the process is NOT being exited; some cleanup may not have run',\n error as Error,\n );\n }\n } finally {\n await this.logger.destroy();\n }\n }\n\n /**\n * Check health of a specific plugin\n */\n async checkPluginHealth(pluginName: string): Promise<any> {\n return await this.pluginLoader.checkPluginHealth(pluginName);\n }\n\n /**\n * Check health of all plugins\n */\n async checkAllPluginsHealth(): Promise<Map<string, any>> {\n const results = new Map();\n \n for (const pluginName of this.plugins.keys()) {\n const health = await this.checkPluginHealth(pluginName);\n results.set(pluginName, health);\n }\n \n return results;\n }\n\n /**\n * Get plugin startup metrics\n */\n getPluginMetrics(): Map<string, number> {\n return new Map(this.pluginStartTimes);\n }\n\n /**\n * Whether a plugin with the given name has been registered on this kernel.\n *\n * Registration happens synchronously in `use()` before any plugin's\n * `start()` runs, so a plugin may use this during its own start() to make\n * composition-dependent decisions deterministically — e.g. the dispatcher\n * bridge cedes `${prefix}/discovery` to `com.objectstack.rest.api` when\n * both are mounted (ADR-0076 D11: single owner per route, not\n * first-registration-wins).\n */\n hasPlugin(name: string): boolean {\n return this.plugins.has(name);\n }\n\n /**\n * Get a service (sync helper)\n */\n getService<T>(name: string): T {\n return this.context.getService<T>(name);\n }\n\n /**\n * Get a service asynchronously (supports factories)\n */\n async getServiceAsync<T>(name: string, scopeId?: string): Promise<T> {\n return await this.pluginLoader.getService<T>(name, scopeId);\n }\n\n /**\n * Clear all scoped service instances for a given scope (e.g., environmentId).\n * Releases driver connections and metadata caches for idle projects.\n */\n clearScope(scopeId: string): void {\n this.pluginLoader.clearScope(scopeId);\n }\n\n /**\n * Check if kernel is running\n */\n isRunning(): boolean {\n return this.state === 'running';\n }\n\n /**\n * Get kernel state\n */\n getState(): string {\n return this.state;\n }\n\n // Private methods\n\n private async initPluginWithTimeout(plugin: PluginMetadata): Promise<void> {\n const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout!;\n\n this.logger.debug(`Init: ${plugin.name}`, { plugin: plugin.name });\n\n // Authoritative init-service check (#4131): Phase 1 is sequential,\n // so a required service absent NOW is absent for this init.\n assertInitServiceRequirements(plugin, (name) => this.hasAnyService(name));\n\n this.currentlyInitializing = plugin.name;\n try {\n await this.raceStartupTimeout(\n plugin.init(this.context),\n timeout,\n `Plugin ${plugin.name} init timeout after ${timeout}ms`\n );\n } finally {\n this.currentlyInitializing = undefined;\n }\n }\n\n /**\n * Race a plugin lifecycle hook against its startup-timeout guard, and\n * reclaim the guard the moment the race settles (#4813).\n *\n * The guard used to be armed and then abandoned: when the plugin won the\n * race, its `setTimeout` stayed ref'd in the event loop for the full\n * `startupTimeout`, so every process idled that long after its work was\n * done. One `os migrate` finished in 3s and then sat for 120s\n * (`ObjectQLPlugin.startupTimeout`), held open by 8 orphaned guards — one\n * per init plus one per start.\n *\n * Clearing on settle rather than `unref()`-ing at arm time is deliberate.\n * An unref'd guard also stops pinning the loop, but it stops being a guard\n * as well: if the hook never settles and nothing else keeps the loop alive,\n * Node exits before the timer can fire and the timeout is never reported.\n * The guard has to stay ref'd exactly as long as the race is undecided,\n * which is what `clearTimeout` in a `finally` expresses.\n *\n * `operation` is widened to `T | PromiseLike<T>` because the Plugin\n * contract permits a synchronous hook (`init`/`start` return\n * `void | Promise<void>`); such a hook wins the race immediately and the\n * guard is reclaimed on the same turn.\n */\n private async raceStartupTimeout<T>(\n operation: T | PromiseLike<T>,\n timeout: number,\n message: string\n ): Promise<T> {\n let guard: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n guard = setTimeout(() => {\n reject(new Error(message));\n }, timeout);\n });\n\n try {\n return await Promise.race([operation, timeoutPromise]);\n } finally {\n clearTimeout(guard);\n }\n }\n\n /**\n * Whether a service is resolvable on this kernel right now — direct\n * registration or a loader-registered factory. Backs the init-service\n * contract checks (#4131).\n */\n private hasAnyService(name: string): boolean {\n return this.services.has(name) || this.pluginLoader.hasService(name);\n }\n\n /**\n * When a getService miss happens while a plugin's init() is running,\n * append the structural diagnosis (#4131): which plugin was initializing,\n * and — when a composed plugin declares the service — who provides it.\n * Empty string outside Phase 1, so non-boot messages stay unchanged.\n */\n private describeInitOrderFault(serviceName: string): string {\n return describeInitOrderFault(this.currentlyInitializing, this.plugins.values(), serviceName);\n }\n\n private async startPluginWithTimeout(plugin: PluginMetadata): Promise<PluginStartupResult> {\n if (!plugin.start) {\n return { success: true, pluginName: plugin.name };\n }\n\n const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout!;\n const startTime = Date.now();\n \n this.logger.debug(`Start: ${plugin.name}`, { plugin: plugin.name });\n \n try {\n await this.raceStartupTimeout(\n plugin.start(this.context),\n timeout,\n `Plugin ${plugin.name} start timeout after ${timeout}ms`\n );\n\n const duration = Date.now() - startTime;\n this.startedPlugins.add(plugin.name);\n this.pluginStartTimes.set(plugin.name, duration);\n \n this.logger.debug(`Plugin started: ${plugin.name} (${duration}ms)`);\n \n return {\n success: true,\n pluginName: plugin.name,\n startTime: duration,\n };\n } catch (error) {\n const duration = Date.now() - startTime;\n const isTimeout = (error as Error).message.includes('timeout');\n \n return {\n success: false,\n pluginName: plugin.name,\n error: error as Error,\n startTime: duration,\n timedOut: isTimeout,\n };\n }\n }\n\n private async rollbackStartedPlugins(): Promise<void> {\n const pluginsToRollback = Array.from(this.startedPlugins).reverse();\n \n for (const pluginName of pluginsToRollback) {\n const plugin = this.plugins.get(pluginName);\n if (plugin?.destroy) {\n try {\n this.logger.debug(`Rollback: ${pluginName}`);\n await plugin.destroy();\n } catch (error) {\n this.logger.error(`Rollback failed for ${pluginName}`, error as Error);\n }\n }\n }\n \n this.startedPlugins.clear();\n }\n\n /**\n * Dispatch `kernel:shutdown`, ISOLATING failures: a handler that throws is\n * logged and the remaining handlers still run (#5274).\n *\n * This is a per-hook judgement, deliberately NOT the bare awaited loop\n * `context.trigger` runs for every other hook — the boot-path hooks\n * (`kernel:ready`, `kernel:bootstrapped`, `kernel:listening`) keep\n * propagating, because everything dispatched before \"✅ Bootstrap complete\"\n * is a precondition of that claim and swallowing a throw there only hides\n * the failure behind a process reporting success (#5170, #5257).\n *\n * On the teardown path there is no \"refuse to proceed\" left to buy. What is\n * queued behind a failing shutdown handler is the rest of the cleanup —\n * every other subscriber, then each plugin's `destroy()` in reverse order —\n * which is what flushes buffers, closes connections and releases locks. So\n * one bad handler must not amplify into leaked resources and unflushed\n * writes. Same reasoning, same wording, same `Hook handler failed:\n * kernel:shutdown` log line as `LiteKernel`'s dispatch site, which reaches\n * the shared isolating dispatcher `ObjectKernelBase.triggerHook` (#5257).\n *\n * `ObjectKernel` cannot call that dispatcher: it does not extend\n * `ObjectKernelBase` (only `LiteKernel` does) and owns its own `hooks` map,\n * so the semantics are mirrored here rather than shared. One hook name\n * meaning two opposite things across the two kernels is exactly the bug\n * #5170/#5257 closed, so the pin for this one lives on both sides too.\n */\n private async triggerShutdownHookIsolating(): Promise<void> {\n const handlers = this.hooks.get('kernel:shutdown') || [];\n this.logger.debug('Triggering hook: kernel:shutdown', {\n hook: 'kernel:shutdown',\n handlerCount: handlers.length,\n });\n\n for (const handler of handlers) {\n try {\n await handler();\n } catch (error) {\n this.logger.error('Hook handler failed: kernel:shutdown', error as Error);\n // Continue with other handlers even if one fails\n }\n }\n }\n\n private async performShutdown(): Promise<void> {\n // Trigger shutdown hook — ISOLATING dispatch, see the method's own\n // rationale. The two loops below already isolate per plugin and per\n // handler; before #5274 this line was the one teardown step that did\n // not, so a single throwing subscriber skipped BOTH of them.\n await this.triggerShutdownHookIsolating();\n\n // Destroy plugins in reverse order\n const orderedPlugins = Array.from(this.plugins.values()).reverse();\n for (const plugin of orderedPlugins) {\n if (plugin.destroy) {\n this.logger.debug(`Destroy: ${plugin.name}`, { plugin: plugin.name });\n try {\n await plugin.destroy();\n } catch (error) {\n this.logger.error(`Error destroying plugin ${plugin.name}`, error as Error);\n }\n }\n }\n\n // Execute custom shutdown handlers\n for (const handler of this.shutdownHandlers) {\n try {\n await handler();\n } catch (error) {\n this.logger.error('Shutdown handler error', error as Error);\n }\n }\n }\n\n /**\n * Topological order over `dependencies` (hard) + `optionalDependencies`\n * (order-if-present) — ADR-0116, #4131. One implementation shared with\n * LiteKernel via `plugin-order.ts`.\n */\n private resolveDependencies(): PluginMetadata[] {\n return resolvePluginOrder(this.plugins);\n }\n\n private registerShutdownSignals(): void {\n const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGQUIT'];\n let shutdownInProgress = false;\n \n const handleShutdown = async (signal: string) => {\n if (shutdownInProgress) {\n this.logger.warn(`Shutdown already in progress, ignoring ${signal}`);\n return;\n }\n \n shutdownInProgress = true;\n this.logger.info(`Received ${signal} - initiating graceful shutdown`);\n \n try {\n await this.shutdown();\n safeExit(0);\n } catch (error) {\n this.logger.error('Shutdown failed', error as Error);\n safeExit(1);\n }\n };\n \n if (isNode) {\n for (const signal of signals) {\n process.on(signal, () => handleShutdown(signal));\n }\n }\n }\n\n /**\n * Register a custom shutdown handler\n */\n onShutdown(handler: () => Promise<void>): void {\n this.shutdownHandlers.push(handler);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { z } from 'zod';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { PluginMetadata } from '../plugin-loader.js';\n\n/**\n * Plugin Configuration Validator\n * \n * Validates plugin configurations against Zod schemas to ensure:\n * 1. Type safety - all config values have correct types\n * 2. Business rules - values meet constraints (min/max, regex, etc.)\n * 3. Required fields - all mandatory configuration is provided\n * 4. Default values - missing optional fields get defaults\n * \n * Architecture:\n * - Uses Zod for runtime validation\n * - Provides detailed error messages with field paths\n * - Supports nested configuration objects\n * - Allows partial validation for incremental updates\n * \n * Usage:\n * ```typescript\n * const validator = new PluginConfigValidator(logger);\n * const validConfig = validator.validatePluginConfig(plugin, userConfig);\n * ```\n */\nexport class PluginConfigValidator {\n private logger: Logger;\n \n constructor(logger: Logger) {\n this.logger = logger;\n }\n \n /**\n * Validate plugin configuration against its Zod schema\n * \n * @param plugin - Plugin metadata with configSchema\n * @param config - User-provided configuration\n * @returns Validated and typed configuration\n * @throws Error with detailed validation errors\n */\n validatePluginConfig<T = any>(plugin: PluginMetadata, config: any): T {\n if (!plugin.configSchema) {\n this.logger.debug(`Plugin ${plugin.name} has no config schema - skipping validation`);\n return config as T;\n }\n \n try {\n // Use Zod to parse and validate\n const validatedConfig = plugin.configSchema.parse(config);\n \n this.logger.debug(`✅ Plugin config validated: ${plugin.name}`, {\n plugin: plugin.name,\n configKeys: Object.keys(config || {}).length,\n });\n \n return validatedConfig as T;\n } catch (error) {\n if (error instanceof z.ZodError) {\n const formattedErrors = this.formatZodErrors(error);\n const errorMessage = [\n `Plugin ${plugin.name} configuration validation failed:`,\n ...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),\n ].join('\\n');\n \n this.logger.error(errorMessage, undefined, {\n plugin: plugin.name,\n errors: formattedErrors,\n });\n \n throw new Error(errorMessage);\n }\n \n // Re-throw other errors\n throw error;\n }\n }\n \n /**\n * Validate partial configuration (for incremental updates)\n * \n * @param plugin - Plugin metadata\n * @param partialConfig - Partial configuration to validate\n * @returns Validated partial configuration\n */\n validatePartialConfig<T = any>(plugin: PluginMetadata, partialConfig: any): Partial<T> {\n if (!plugin.configSchema) {\n return partialConfig as Partial<T>;\n }\n \n try {\n // Use Zod's partial() method for partial validation\n // Cast to ZodObject to access partial() method\n const partialSchema = (plugin.configSchema as any).partial();\n const validatedConfig = partialSchema.parse(partialConfig);\n \n this.logger.debug(`✅ Partial config validated: ${plugin.name}`);\n return validatedConfig as Partial<T>;\n } catch (error) {\n if (error instanceof z.ZodError) {\n const formattedErrors = this.formatZodErrors(error);\n const errorMessage = [\n `Plugin ${plugin.name} partial configuration validation failed:`,\n ...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),\n ].join('\\n');\n \n throw new Error(errorMessage);\n }\n \n throw error;\n }\n }\n \n /**\n * Get default configuration from schema\n * \n * @param plugin - Plugin metadata\n * @returns Default configuration object\n */\n getDefaultConfig<T = any>(plugin: PluginMetadata): T | undefined {\n if (!plugin.configSchema) {\n return undefined;\n }\n \n try {\n // Parse empty object to get defaults\n const defaults = plugin.configSchema.parse({});\n this.logger.debug(`Default config extracted: ${plugin.name}`);\n return defaults as T;\n } catch (error) {\n // Schema may require some fields - return undefined\n this.logger.debug(`No default config available: ${plugin.name}`);\n return undefined;\n }\n }\n \n /**\n * Check if configuration is valid without throwing\n * \n * @param plugin - Plugin metadata\n * @param config - Configuration to check\n * @returns True if valid, false otherwise\n */\n isConfigValid(plugin: PluginMetadata, config: any): boolean {\n if (!plugin.configSchema) {\n return true;\n }\n \n const result = plugin.configSchema.safeParse(config);\n return result.success;\n }\n \n /**\n * Get configuration errors without throwing\n * \n * @param plugin - Plugin metadata\n * @param config - Configuration to check\n * @returns Array of validation errors, or empty array if valid\n */\n getConfigErrors(plugin: PluginMetadata, config: any): Array<{path: string; message: string}> {\n if (!plugin.configSchema) {\n return [];\n }\n \n const result = plugin.configSchema.safeParse(config);\n \n if (result.success) {\n return [];\n }\n \n return this.formatZodErrors(result.error);\n }\n \n // Private methods\n \n private formatZodErrors(error: z.ZodError<any>): Array<{path: string; message: string}> {\n return error.issues.map((e: z.ZodIssue) => ({\n path: e.path.join('.') || 'root',\n message: e.message,\n }));\n }\n}\n\n/**\n * Create a plugin config validator\n * \n * @param logger - Logger instance\n * @returns Plugin config validator\n */\nexport function createPluginConfigValidator(logger: Logger): PluginConfigValidator {\n return new PluginConfigValidator(logger);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Plugin artifact signing & verification (ADR-0025 §3.4–§3.7, framework F3).\n *\n * This is the CANONICAL Ed25519 detached-signature contract shared by the\n * whole plugin distribution pipeline. It is intentionally byte-for-byte\n * compatible with the cloud control plane's `package-signing.ts` so the\n * two never drift:\n *\n * - signature string format: `ed25519:<keyId>:<base64url(signature)>`\n * - publisher signature: Ed25519 over the raw `.osplugin` artifact bytes,\n * produced by `os plugin sign`, verified by cloud at publish time and by\n * the runtime when it materializes the artifact.\n * - platform counter-signature: Ed25519 over {@link counterSignPayload}\n * (the version identity), produced by cloud at approval, verified by the\n * runtime at load time as the marketplace's \"reviewed + approved\" attest.\n *\n * Algorithm: Ed25519 via node:crypto (`sign(null, …)` / `verify(null, …)`):\n * short, deterministic, no padding ambiguity. The `keyId` is an opaque\n * rotation handle used to resolve the verifying public key.\n *\n * The two trust chains the runtime checks before loading a third-party\n * plugin are combined in {@link verifyPluginArtifact}.\n */\n\nimport {\n sign as cryptoSign,\n verify as cryptoVerify,\n createPublicKey,\n createPrivateKey,\n generateKeyPairSync,\n type KeyObject,\n} from 'node:crypto';\n\nexport const SIGNATURE_ALG = 'ed25519';\nconst SIG_PREFIX = 'ed25519:';\n\nexport type KeyInput = string | KeyObject;\n\nfunction toPrivateKey(key: KeyInput): KeyObject {\n return typeof key === 'string' ? createPrivateKey(key) : key;\n}\nfunction toPublicKey(key: KeyInput): KeyObject {\n return typeof key === 'string' ? createPublicKey(key) : key;\n}\nfunction toBytes(payload: string | Uint8Array): Uint8Array {\n return typeof payload === 'string' ? new TextEncoder().encode(payload) : payload;\n}\n\n/** Generate an Ed25519 keypair as PEM strings (publisher bootstrap / tests). */\nexport function generateEd25519KeyPair(): { publicKeyPem: string; privateKeyPem: string } {\n const { publicKey, privateKey } = generateKeyPairSync('ed25519');\n return {\n publicKeyPem: publicKey.export({ type: 'spki', format: 'pem' }).toString(),\n privateKeyPem: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(),\n };\n}\n\n/**\n * Sign `payload` with an Ed25519 private key, returning the formatted\n * signature string `ed25519:<keyId>:<base64url(sig)>`.\n */\nexport function signPayload(\n payload: string | Uint8Array,\n privateKey: KeyInput,\n keyId = 'default',\n): string {\n if (keyId.includes(':')) throw new Error('keyId must not contain \":\"');\n const sig = cryptoSign(null, toBytes(payload), toPrivateKey(privateKey));\n return `${SIG_PREFIX}${keyId}:${sig.toString('base64url')}`;\n}\n\nexport interface ParsedSignature {\n alg: 'ed25519';\n keyId: string;\n signature: Uint8Array;\n}\n\n/** Parse an `ed25519:<keyId>:<base64url>` signature string. Returns null if malformed. */\nexport function parseSignature(s: string | undefined | null): ParsedSignature | null {\n if (typeof s !== 'string' || !s.startsWith(SIG_PREFIX)) return null;\n const rest = s.slice(SIG_PREFIX.length);\n const idx = rest.indexOf(':');\n if (idx <= 0) return null;\n const keyId = rest.slice(0, idx);\n const b64 = rest.slice(idx + 1);\n if (!keyId || !b64) return null;\n try {\n return { alg: 'ed25519', keyId, signature: new Uint8Array(Buffer.from(b64, 'base64url')) };\n } catch {\n return null;\n }\n}\n\n/** Verify a formatted signature string over `payload` with the given public key. */\nexport function verifyPayload(\n payload: string | Uint8Array,\n signature: string,\n publicKey: KeyInput,\n): boolean {\n const parsed = parseSignature(signature);\n if (!parsed) return false;\n try {\n return cryptoVerify(null, toBytes(payload), toPublicKey(publicKey), parsed.signature);\n } catch {\n return false;\n }\n}\n\n/**\n * Canonical payload the platform counter-signs at approval. Binds the\n * attestation to the version identity + artifact location + the publisher\n * signature (which itself binds the artifact bytes). MUST match the cloud\n * control plane's `counterSignPayload` exactly.\n */\nexport function counterSignPayload(version: {\n package_id: string;\n version: string;\n blob_key?: string | null;\n signature?: string | null;\n}): string {\n return [\n version.package_id,\n version.version,\n version.blob_key ?? '',\n version.signature ?? '',\n ].join('\\n');\n}\n\nexport interface PublisherVerifyResult {\n /** Whether loading may proceed on signature grounds. */\n ok: boolean;\n /** True when a signature was present AND cryptographically verified. */\n verified: boolean;\n reason?: string;\n}\n\n/**\n * Verify a publisher signature over the raw artifact bytes. `getPublicKey`\n * resolves the verifying key from the signature's embedded keyId.\n *\n * Mirrors cloud's publish-time policy:\n * - no signature → ok, verified=false (caller decides via trust tier).\n * - malformed / fails verification → NOT ok.\n * - unknown keyId → NOT ok (never silently trust).\n */\nexport async function verifyPublisherSignature(\n args: { artifact: Uint8Array; signature?: string | null },\n getPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null,\n): Promise<PublisherVerifyResult> {\n const sig = args.signature;\n if (!sig) return { ok: true, verified: false, reason: 'no signature supplied' };\n\n const parsed = parseSignature(sig);\n if (!parsed) return { ok: false, verified: false, reason: 'signature is malformed' };\n\n if (!getPublicKey) {\n return { ok: true, verified: false, reason: 'no publisher key registry configured' };\n }\n\n const pub = await getPublicKey(parsed.keyId);\n if (!pub) return { ok: false, verified: false, reason: `unknown publisher key '${parsed.keyId}'` };\n\n return verifyPayload(args.artifact, sig, pub)\n ? { ok: true, verified: true }\n : { ok: false, verified: false, reason: 'publisher signature does not match artifact' };\n}\n\n/** Verify a platform counter-signature against the version identity + platform public key. */\nexport function verifyPlatformSignature(\n version: {\n package_id: string;\n version: string;\n blob_key?: string | null;\n signature?: string | null;\n platform_signature?: string | null;\n },\n platformPublicKey: KeyInput,\n): boolean {\n if (!version.platform_signature) return false;\n return verifyPayload(counterSignPayload(version), version.platform_signature, platformPublicKey);\n}\n\nexport interface PluginArtifactVerifyResult {\n /** Overall verdict: both required chains satisfied under the given policy. */\n ok: boolean;\n publisherVerified: boolean;\n platformVerified: boolean;\n reason?: string;\n}\n\n/**\n * Verify both trust chains for a downloaded plugin artifact at load time\n * (ADR-0025 §3.7). The platform counter-signature is the authoritative\n * marketplace attestation; the publisher signature additionally binds the\n * exact bytes. `requirePlatform` (default true) rejects artifacts that lack\n * a valid platform counter-sign — set false for first-party / local builds.\n */\nexport async function verifyPluginArtifact(\n input: {\n artifact: Uint8Array;\n version: {\n package_id: string;\n version: string;\n blob_key?: string | null;\n signature?: string | null;\n platform_signature?: string | null;\n };\n },\n keys: {\n platformPublicKey?: KeyInput;\n getPublisherPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null;\n requirePlatform?: boolean;\n },\n): Promise<PluginArtifactVerifyResult> {\n const requirePlatform = keys.requirePlatform ?? true;\n\n const publisher = await verifyPublisherSignature(\n { artifact: input.artifact, signature: input.version.signature },\n keys.getPublisherPublicKey,\n );\n if (!publisher.ok) {\n return { ok: false, publisherVerified: false, platformVerified: false, reason: publisher.reason };\n }\n\n let platformVerified = false;\n if (keys.platformPublicKey) {\n platformVerified = verifyPlatformSignature(input.version, keys.platformPublicKey);\n }\n if (requirePlatform && !platformVerified) {\n return {\n ok: false,\n publisherVerified: publisher.verified,\n platformVerified,\n reason: keys.platformPublicKey\n ? 'platform counter-signature missing or invalid'\n : 'no platform public key configured',\n };\n }\n\n return { ok: true, publisherVerified: publisher.verified, platformVerified };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext } from './types.js';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { z } from 'zod';\nimport { PluginConfigValidator } from './security/plugin-config-validator.js';\nimport { parseSignature } from './security/plugin-artifact-signature.js';\n\n/**\n * Service Lifecycle Types\n * Defines how services are instantiated and managed\n */\nexport enum ServiceLifecycle {\n /** Single instance shared across all requests */\n SINGLETON = 'singleton',\n /** New instance created for each request */\n TRANSIENT = 'transient',\n /** New instance per scope (e.g., per HTTP request) */\n SCOPED = 'scoped',\n}\n\n/**\n * Service Factory\n * Function that creates a service instance\n */\nexport type ServiceFactory<T = any> = (ctx: PluginContext, scopeId?: string) => T | Promise<T>;\n\n/**\n * Service Registration Options\n */\nexport interface ServiceRegistration {\n name: string;\n factory: ServiceFactory;\n lifecycle: ServiceLifecycle;\n dependencies?: string[];\n}\n\n/**\n * Plugin Metadata with Enhanced Features\n */\nexport interface PluginMetadata extends Plugin {\n /** Semantic version (e.g., \"1.0.0\") */\n version: string;\n \n /** Configuration schema for validation */\n configSchema?: z.ZodSchema;\n \n /** Plugin signature for security verification */\n signature?: string;\n \n /** Plugin health check function */\n healthCheck?(): Promise<PluginHealthStatus>;\n \n /** Startup timeout in milliseconds (default: 30000) */\n startupTimeout?: number;\n \n /** Whether plugin supports hot reload */\n hotReloadable?: boolean;\n}\n\n/**\n * Plugin Health Status\n */\nexport interface PluginHealthStatus {\n healthy: boolean;\n message?: string;\n details?: Record<string, any>;\n lastCheck?: Date;\n}\n\n/**\n * Plugin Load Result\n */\nexport interface PluginLoadResult {\n success: boolean;\n plugin?: PluginMetadata;\n error?: Error;\n loadTime?: number;\n}\n\n/**\n * Plugin Startup Result\n */\nexport interface PluginStartupResult {\n success: boolean;\n pluginName: string;\n startTime?: number;\n error?: Error;\n timedOut?: boolean;\n}\n\n/**\n * Version Compatibility Result\n */\nexport interface VersionCompatibility {\n compatible: boolean;\n pluginVersion: string;\n requiredVersion?: string;\n message?: string;\n}\n\n/**\n * Enhanced Plugin Loader\n * Provides advanced plugin loading capabilities with validation, security, and lifecycle management\n */\nexport class PluginLoader {\n private logger: Logger;\n private context?: PluginContext;\n private configValidator: PluginConfigValidator;\n private loadedPlugins: Map<string, PluginMetadata> = new Map();\n private serviceFactories: Map<string, ServiceRegistration> = new Map();\n private serviceInstances: Map<string, any> = new Map();\n private scopedServices: Map<string, Map<string, any>> = new Map();\n private creating: Set<string> = new Set();\n\n constructor(logger: Logger) {\n this.logger = logger;\n this.configValidator = new PluginConfigValidator(logger);\n }\n\n /**\n * Set the plugin context for service factories\n */\n setContext(context: PluginContext): void {\n this.context = context;\n }\n\n /**\n * Get a synchronous service instance if it exists (Sync Helper)\n */\n getServiceInstance<T>(name: string): T | undefined {\n return this.serviceInstances.get(name) as T;\n }\n\n /**\n * Load a plugin asynchronously with validation\n */\n async loadPlugin(plugin: Plugin): Promise<PluginLoadResult> {\n const startTime = Date.now();\n \n try {\n this.logger.info(`Loading plugin: ${plugin.name}`);\n \n // Convert to PluginMetadata\n const metadata = this.toPluginMetadata(plugin);\n \n // Validate plugin structure\n this.validatePluginStructure(metadata);\n \n // Check version compatibility\n const versionCheck = this.checkVersionCompatibility(metadata);\n if (!versionCheck.compatible) {\n throw new Error(`Version incompatible: ${versionCheck.message}`);\n }\n \n // Validate configuration if schema is provided\n if (metadata.configSchema) {\n this.validatePluginConfig(metadata);\n }\n \n // Verify signature if provided\n if (metadata.signature) {\n await this.verifyPluginSignature(metadata);\n }\n \n // Store loaded plugin\n this.loadedPlugins.set(metadata.name, metadata);\n \n const loadTime = Date.now() - startTime;\n this.logger.info(`Plugin loaded: ${plugin.name} (${loadTime}ms)`);\n \n return {\n success: true,\n plugin: metadata,\n loadTime,\n };\n } catch (error) {\n this.logger.error(`Failed to load plugin: ${plugin.name}`, error as Error);\n return {\n success: false,\n error: error as Error,\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Register a service with factory function\n */\n registerServiceFactory(registration: ServiceRegistration): void {\n if (this.serviceFactories.has(registration.name)) {\n throw new Error(`Service factory '${registration.name}' already registered`);\n }\n \n this.serviceFactories.set(registration.name, registration);\n this.logger.debug(`Service factory registered: ${registration.name} (${registration.lifecycle})`);\n }\n\n /**\n * Get or create a service instance based on lifecycle type\n */\n async getService<T>(name: string, scopeId?: string): Promise<T> {\n const registration = this.serviceFactories.get(name);\n \n if (!registration) {\n // Fall back to static service instances\n const instance = this.serviceInstances.get(name);\n if (!instance) {\n throw new Error(`Service '${name}' not found`);\n }\n return instance as T;\n }\n \n switch (registration.lifecycle) {\n case ServiceLifecycle.SINGLETON:\n return await this.getSingletonService<T>(registration);\n \n case ServiceLifecycle.TRANSIENT:\n return await this.createTransientService<T>(registration);\n \n case ServiceLifecycle.SCOPED:\n if (!scopeId) {\n throw new Error(`Scope ID required for scoped service '${name}'`);\n }\n return await this.getScopedService<T>(registration, scopeId);\n \n default:\n throw new Error(`Unknown service lifecycle: ${registration.lifecycle}`);\n }\n }\n\n /**\n * Register a static service instance (legacy support)\n */\n registerService(name: string, service: any): void {\n if (this.serviceInstances.has(name)) {\n throw new Error(`Service '${name}' already registered`);\n }\n this.serviceInstances.set(name, service);\n }\n\n /**\n * Replace an existing service instance.\n * Used by optimization plugins to swap kernel internals.\n * @throws Error if service does not exist\n */\n replaceService(name: string, service: any): void {\n if (!this.hasService(name)) {\n throw new Error(`Service '${name}' not found`);\n }\n this.serviceInstances.set(name, service);\n }\n\n /**\n * Check if a service is registered (either as instance or factory)\n */\n hasService(name: string): boolean {\n return this.serviceInstances.has(name) || this.serviceFactories.has(name);\n }\n\n /**\n * Detect circular dependencies in service factories\n * Note: This only detects cycles in service dependencies, not plugin dependencies.\n * Plugin dependency cycles are detected in the kernel's resolveDependencies method.\n */\n detectCircularDependencies(): string[] {\n const cycles: string[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n \n const visit = (serviceName: string, path: string[] = []) => {\n if (visiting.has(serviceName)) {\n const cycle = [...path, serviceName].join(' -> ');\n cycles.push(cycle);\n return;\n }\n \n if (visited.has(serviceName)) {\n return;\n }\n \n visiting.add(serviceName);\n \n const registration = this.serviceFactories.get(serviceName);\n if (registration?.dependencies) {\n for (const dep of registration.dependencies) {\n visit(dep, [...path, serviceName]);\n }\n }\n \n visiting.delete(serviceName);\n visited.add(serviceName);\n };\n \n for (const serviceName of this.serviceFactories.keys()) {\n visit(serviceName);\n }\n \n return cycles;\n }\n\n /**\n * Check plugin health\n */\n async checkPluginHealth(pluginName: string): Promise<PluginHealthStatus> {\n const plugin = this.loadedPlugins.get(pluginName);\n \n if (!plugin) {\n return {\n healthy: false,\n message: 'Plugin not found',\n lastCheck: new Date(),\n };\n }\n \n if (!plugin.healthCheck) {\n return {\n healthy: true,\n message: 'No health check defined',\n lastCheck: new Date(),\n };\n }\n \n try {\n const status = await plugin.healthCheck();\n return {\n ...status,\n lastCheck: new Date(),\n };\n } catch (error) {\n return {\n healthy: false,\n message: `Health check failed: ${(error as Error).message}`,\n lastCheck: new Date(),\n };\n }\n }\n\n /**\n * Clear scoped services for a scope\n */\n clearScope(scopeId: string): void {\n this.scopedServices.delete(scopeId);\n this.logger.debug(`Cleared scope: ${scopeId}`);\n }\n\n /**\n * Get all loaded plugins\n */\n getLoadedPlugins(): Map<string, PluginMetadata> {\n return new Map(this.loadedPlugins);\n }\n\n // Private helper methods\n\n private toPluginMetadata(plugin: Plugin): PluginMetadata {\n // Fix: Do not use object spread {...plugin} as it destroys the prototype chain for Class-based plugins.\n // Instead, cast the original object and inject default values if missing.\n const metadata = plugin as PluginMetadata;\n \n if (!metadata.version) {\n metadata.version = '0.0.0';\n }\n \n return metadata;\n }\n\n private validatePluginStructure(plugin: PluginMetadata): void {\n if (!plugin.name) {\n throw new Error('Plugin name is required');\n }\n \n if (!plugin.init) {\n throw new Error('Plugin init function is required');\n }\n \n if (!this.isValidSemanticVersion(plugin.version)) {\n throw new Error(`Invalid semantic version: ${plugin.version}`);\n }\n }\n\n private checkVersionCompatibility(plugin: PluginMetadata): VersionCompatibility {\n // Basic semantic version compatibility check\n // In a real implementation, this would check against kernel version\n const version = plugin.version;\n \n if (!this.isValidSemanticVersion(version)) {\n return {\n compatible: false,\n pluginVersion: version,\n message: 'Invalid semantic version format',\n };\n }\n \n return {\n compatible: true,\n pluginVersion: version,\n };\n }\n\n private isValidSemanticVersion(version: string): boolean {\n const semverRegex = /^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/;\n return semverRegex.test(version);\n }\n\n private validatePluginConfig(plugin: PluginMetadata, config?: any): void {\n if (!plugin.configSchema) {\n return;\n }\n\n if (config === undefined) {\n // In loadPlugin, we often don't have the config yet.\n // We skip validation here or valid against empty object if schema allows?\n // For now, let's keep the logging behavior but note it's delegating\n this.logger.debug(`Plugin ${plugin.name} has configuration schema (config validation postponed)`);\n return;\n }\n\n this.configValidator.validatePluginConfig(plugin, config);\n }\n\n private async verifyPluginSignature(plugin: PluginMetadata): Promise<void> {\n if (!plugin.signature) {\n return;\n }\n \n // Cryptographic verification of a third-party plugin's PUBLISHER and\n // PLATFORM signatures is performed against the `.osplugin` artifact\n // bytes + version identity at materialize/install time, by\n // `verifyPluginArtifact` (security/plugin-artifact-signature.ts —\n // ADR-0025 §3.7). By the time a plugin reaches loadPlugin() it is an\n // in-memory module with no artifact bytes, so we cannot re-run the\n // artifact chains here; we only validate that any signature carried\n // on the metadata is well-formed (`ed25519:<keyId>:<base64url>`) and\n // surface its keyId, failing fast on a malformed value.\n const parsed = parseSignature(plugin.signature);\n if (!parsed) {\n throw new Error(\n `Plugin ${plugin.name} carries a malformed signature (expected ed25519:<keyId>:<base64url>)`,\n );\n }\n this.logger.debug(\n `Plugin ${plugin.name} signature well-formed (alg=${parsed.alg}, keyId=${parsed.keyId}); ` +\n `artifact verification occurs at materialize time`,\n );\n }\n\n private async getSingletonService<T>(registration: ServiceRegistration): Promise<T> {\n let instance = this.serviceInstances.get(registration.name);\n \n if (!instance) {\n // Create instance (would need context)\n instance = await this.createServiceInstance(registration);\n this.serviceInstances.set(registration.name, instance);\n this.logger.debug(`Singleton service created: ${registration.name}`);\n }\n \n return instance as T;\n }\n\n private async createTransientService<T>(registration: ServiceRegistration): Promise<T> {\n const instance = await this.createServiceInstance(registration);\n this.logger.debug(`Transient service created: ${registration.name}`);\n return instance as T;\n }\n\n private async getScopedService<T>(registration: ServiceRegistration, scopeId: string): Promise<T> {\n if (!this.scopedServices.has(scopeId)) {\n this.scopedServices.set(scopeId, new Map());\n }\n\n const scope = this.scopedServices.get(scopeId)!;\n let instance = scope.get(registration.name);\n\n if (!instance) {\n instance = await this.createServiceInstance(registration, scopeId);\n scope.set(registration.name, instance);\n this.logger.debug(`Scoped service created: ${registration.name} (scope: ${scopeId})`);\n }\n\n return instance as T;\n }\n\n private async createServiceInstance(registration: ServiceRegistration, scopeId?: string): Promise<any> {\n if (!this.context) {\n throw new Error(`[PluginLoader] Context not set - cannot create service '${registration.name}'`);\n }\n\n if (this.creating.has(registration.name)) {\n throw new Error(`Circular dependency detected: ${Array.from(this.creating).join(' -> ')} -> ${registration.name}`);\n }\n\n this.creating.add(registration.name);\n try {\n return await registration.factory(this.context, scopeId);\n } finally {\n this.creating.delete(registration.name);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Environment utilities for universal (Node/Browser) compatibility.\n */\n\n// Check if running in a Node.js environment\nexport const isNode = typeof process !== 'undefined' && \n process.versions != null && \n process.versions.node != null;\n\n/**\n * Safely access environment variables\n */\nexport function getEnv(key: string, defaultValue?: string): string | undefined {\n // Node.js\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key] || defaultValue;\n }\n \n // Browser (Vite/Webpack replacement usually handles process.env, \n // but if not, we check safe global access)\n try {\n // @ts-ignore\n if (typeof globalThis !== 'undefined' && globalThis.process?.env) {\n // @ts-ignore\n return globalThis.process.env[key] || defaultValue;\n }\n } catch (e) {\n // Ignore access errors\n }\n \n return defaultValue;\n}\n\n/**\n * Safely exit the process if in Node.js\n */\nexport function safeExit(code: number = 0): void {\n if (isNode) {\n process.exit(code);\n }\n}\n\n/**\n * Safely get memory usage\n */\nexport function getMemoryUsage(): { heapUsed: number; heapTotal: number } {\n if (isNode) {\n return process.memoryUsage();\n }\n return { heapUsed: 0, heapTotal: 0 };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * In-memory Map-backed cache fallback.\n *\n * Implements the ICacheService contract with basic get/set/delete/has/clear\n * and TTL expiry. Used by ObjectKernel as an automatic fallback when no\n * real cache plugin (e.g. Redis) is registered.\n *\n * [#4058] Self-describes as `degraded`, not `stub` (ADR-0076 D12): this is a\n * real cache — it stores, expires, and reports true stats — just process-local\n * and unshared. The non-standard `_fallback: true` it used to carry was read by\n * nothing (`readServiceSelfInfo` reads only `__serviceInfo` — `_dev`, the other\n * marker it knew back then, was itself retired in #4319), so discovery reported\n * it as fully `available`. `handlerReady: false` because\n * no HTTP surface is mounted for `cache` at all — the same reason realtime\n * reports false.\n */\nexport function createMemoryCache() {\n const store = new Map<string, { value: unknown; expires?: number }>();\n let hits = 0;\n let misses = 0;\n return {\n __serviceInfo: {\n status: 'degraded' as const,\n handlerReady: false,\n message: 'In-process Map cache — not shared across instances, lost on restart. Register a cache plugin (e.g. Redis) for a real one.',\n },\n _serviceName: 'cache',\n async get<T = unknown>(key: string): Promise<T | undefined> {\n const entry = store.get(key);\n if (!entry || (entry.expires && Date.now() > entry.expires)) {\n store.delete(key);\n misses++;\n return undefined;\n }\n hits++;\n return entry.value as T;\n },\n async set<T = unknown>(key: string, value: T, ttl?: number): Promise<void> {\n store.set(key, { value, expires: ttl ? Date.now() + ttl * 1000 : undefined });\n },\n async delete(key: string): Promise<boolean> { return store.delete(key); },\n async has(key: string): Promise<boolean> { return store.has(key); },\n async clear(): Promise<void> { store.clear(); },\n async stats() { return { hits, misses, keyCount: store.size }; },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * In-memory publish/subscribe queue fallback.\n *\n * Implements the IQueueService contract with synchronous in-process delivery.\n * Used by ObjectKernel as an automatic fallback when no real queue plugin\n * (e.g. BullMQ / RabbitMQ) is registered.\n *\n * [#4058] `degraded`, not `stub` (ADR-0076 D12): messages really reach real\n * subscribers — synchronously, in-process, with no durability or retry.\n * `getQueueSize()` answering 0 follows from that rather than faking it: nothing\n * is ever buffered. `handlerReady: false` — no HTTP surface exists for `queue`.\n */\nexport function createMemoryQueue() {\n const handlers = new Map<string, Function[]>();\n let msgId = 0;\n return {\n __serviceInfo: {\n status: 'degraded' as const,\n handlerReady: false,\n message: 'Synchronous in-process delivery — no durability, retry, or cross-instance fan-out. Register a queue plugin (e.g. BullMQ) for a real one.',\n },\n _serviceName: 'queue',\n async publish<T = unknown>(queue: string, data: T): Promise<string> {\n const id = `fallback-msg-${++msgId}`;\n const fns = handlers.get(queue) ?? [];\n for (const fn of fns) fn({ id, data, attempts: 1, timestamp: Date.now() });\n return id;\n },\n async subscribe(queue: string, handler: (msg: any) => Promise<void>): Promise<void> {\n handlers.set(queue, [...(handlers.get(queue) ?? []), handler]);\n },\n async unsubscribe(queue: string): Promise<void> { handlers.delete(queue); },\n async getQueueSize(): Promise<number> { return 0; },\n async purge(queue: string): Promise<void> { handlers.delete(queue); },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * In-memory job scheduler fallback.\n *\n * Implements the IJobService contract with basic schedule/cancel/trigger\n * operations. Used by ObjectKernel as an automatic fallback when no real\n * job plugin (e.g. Agenda / BullMQ) is registered.\n *\n * [#4058] `degraded` (ADR-0076 D12), with the missing half named in the\n * message rather than left for a deployer to discover: `trigger()` really runs\n * the registered handler, but nothing here owns a timer, so a `schedule()`d job\n * NEVER fires on its own. That is reduced capability, not fabricated output —\n * no call returns a made-up answer. `handlerReady: false`: no HTTP surface.\n */\nexport function createMemoryJob() {\n const jobs = new Map<string, any>();\n return {\n __serviceInfo: {\n status: 'degraded' as const,\n handlerReady: false,\n message: 'In-process job registry — trigger() runs handlers, but scheduled jobs never fire on their own (no timer). Register a job plugin (e.g. Agenda) for real scheduling.',\n },\n _serviceName: 'job',\n async schedule(name: string, schedule: any, handler: any): Promise<void> { jobs.set(name, { schedule, handler }); },\n async cancel(name: string): Promise<void> { jobs.delete(name); },\n async trigger(name: string, data?: unknown): Promise<void> {\n const job = jobs.get(name);\n if (job?.handler) await job.handler({ jobId: name, data });\n },\n async getExecutions(): Promise<any[]> { return []; },\n async listJobs(): Promise<string[]> { return [...jobs.keys()]; },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Recursively merge `source` into `target`. Nested plain objects are merged\n * rather than replaced, so multiple plugins can each contribute their own\n * slice of a locale's translations (e.g. `{objects: {account: ...}}` and\n * `{objects: {task: ...}}`) without clobbering one another.\n * Exported for the authored-translation sync (#2591).\n */\nexport function deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...target };\n for (const key of Object.keys(source)) {\n const tVal = target[key];\n const sVal = source[key];\n if (\n tVal && sVal\n && typeof tVal === 'object' && !Array.isArray(tVal)\n && typeof sVal === 'object' && !Array.isArray(sVal)\n ) {\n result[key] = deepMerge(\n tVal as Record<string, unknown>,\n sVal as Record<string, unknown>,\n );\n } else {\n result[key] = sVal;\n }\n }\n return result;\n}\n\n/**\n * Resolve a locale code against available locales with fallback.\n *\n * Fallback chain:\n * 1. Exact match (e.g. `zh-CN` → `zh-CN`)\n * 2. Case-insensitive match (e.g. `zh-cn` → `zh-CN`)\n * 3. Base language match (e.g. `zh-CN` → `zh`)\n * 4. Variant expansion (e.g. `zh` → `zh-CN`)\n *\n * Returns the matched locale code, or `undefined` when no match is found.\n */\nexport function resolveLocale(requestedLocale: string, availableLocales: string[]): string | undefined {\n if (availableLocales.length === 0) return undefined;\n\n // 1. Exact match\n if (availableLocales.includes(requestedLocale)) return requestedLocale;\n\n // 2. Case-insensitive match\n const lower = requestedLocale.toLowerCase();\n const caseMatch = availableLocales.find(l => l.toLowerCase() === lower);\n if (caseMatch) return caseMatch;\n\n // 3. Base language match (zh-CN → zh)\n const baseLang = requestedLocale.split('-')[0].toLowerCase();\n const baseMatch = availableLocales.find(l => l.toLowerCase() === baseLang);\n if (baseMatch) return baseMatch;\n\n // 4. Variant expansion (zh → zh-CN, zh-TW, etc. — first match wins)\n const variantMatch = availableLocales.find(l => l.split('-')[0].toLowerCase() === baseLang);\n if (variantMatch) return variantMatch;\n\n return undefined;\n}\n\n/**\n * In-memory i18n service fallback.\n *\n * Implements the II18nService contract with basic translate/load/getLocales\n * operations. Used by ObjectKernel as an automatic fallback when no real\n * i18n plugin (e.g. I18nServicePlugin) is registered.\n *\n * Supports runtime translation loading, locale management, and\n * locale code fallback (e.g. `zh` → `zh-CN`).\n * Does not load files from disk — operates purely in-memory.\n */\nexport function createMemoryI18n() {\n const translations = new Map<string, Record<string, unknown>>();\n // Runtime-AUTHORED overlay (#2591): translations published as `translation`\n // metadata. Kept separate from the static map so a re-sync can REPLACE the\n // whole authored layer (clear-then-reload — deleted keys must not linger),\n // while authored values win over static bundle values on read.\n const authored = new Map<string, Record<string, unknown>>();\n let defaultLocale = 'en';\n\n /**\n * Resolve a dot-notation key from a nested object.\n */\n function resolveKey(data: Record<string, unknown>, key: string): string | undefined {\n const parts = key.split('.');\n let current: unknown = data;\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return typeof current === 'string' ? current : undefined;\n }\n\n /** Merged (static ⊕ authored) view of a single, exact locale key. */\n function mergedLocale(locale: string): Record<string, unknown> | undefined {\n const stat = translations.get(locale);\n const auth = authored.get(locale);\n if (stat && auth) return deepMerge(stat, auth);\n return auth ?? stat;\n }\n\n /**\n * Find translation data for a locale, with fallback resolution.\n */\n function resolveTranslations(locale: string): Record<string, unknown> | undefined {\n // Exact match\n const exact = mergedLocale(locale);\n if (exact) return exact;\n\n // Locale fallback (zh → zh-CN, en-us → en-US, etc.)\n const allLocales = [...new Set([...translations.keys(), ...authored.keys()])];\n const resolved = resolveLocale(locale, allLocales);\n if (resolved) return mergedLocale(resolved);\n\n return undefined;\n }\n\n return {\n // [#4058] `degraded` (ADR-0076 D12): translations, locale fallback and\n // interpolation are all real — what is missing is persistence and the\n // authoring surface service-i18n adds. `handlerReady` left at the\n // `degraded` default (true): the dispatcher's `/i18n` domain does serve\n // this implementation.\n __serviceInfo: {\n status: 'degraded' as const,\n message: 'In-memory translations — real lookup and locale fallback, but nothing is persisted. Register I18nServicePlugin from @objectstack/service-i18n for the full implementation.',\n },\n _serviceName: 'i18n',\n\n t(key: string, locale: string, params?: Record<string, unknown>): string {\n const data = resolveTranslations(locale) ?? mergedLocale(defaultLocale);\n const value = data ? resolveKey(data, key) : undefined;\n if (value == null) return key;\n if (!params) return value;\n // Interpolation format: {{paramName}} — matches FileI18nAdapter convention\n return value.replace(/\\{\\{(\\w+)\\}\\}/g, (_, name) => String(params[name] ?? `{{${name}}}`));\n },\n\n getTranslations(locale: string): Record<string, unknown> {\n return resolveTranslations(locale) ?? {};\n },\n\n loadTranslations(locale: string, data: Record<string, unknown>): void {\n const existing = translations.get(locale);\n if (existing) {\n translations.set(locale, deepMerge(existing, data));\n } else {\n translations.set(locale, { ...data });\n }\n },\n\n /**\n * Replace the ENTIRE runtime-authored translation layer (#2591). Called\n * by the authored-translation sync with the full current set of active\n * `translation` metadata items keyed by locale. Wholesale replacement —\n * not a merge — so deleted items/keys stop resolving on the next sync.\n */\n replaceAuthoredTranslations(byLocale: Record<string, Record<string, unknown>>): void {\n authored.clear();\n for (const [locale, data] of Object.entries(byLocale ?? {})) {\n if (!data || typeof data !== 'object') continue;\n authored.set(locale, { ...data });\n }\n },\n\n getLocales(): string[] {\n return [...new Set([...translations.keys(), ...authored.keys()])];\n },\n\n getDefaultLocale(): string {\n return defaultLocale;\n },\n\n setDefaultLocale(locale: string): void {\n defaultLocale = locale;\n },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * In-memory metadata service fallback.\n *\n * Implements the IMetadataService contract with a simple Map-of-Maps store.\n * Used by ObjectKernel as an automatic fallback when no real metadata plugin\n * (e.g. MetadataPlugin with file-system persistence) is registered.\n */\nexport function createMemoryMetadata() {\n // type -> name -> data\n const store = new Map<string, Map<string, any>>();\n\n function getTypeMap(type: string): Map<string, any> {\n let map = store.get(type);\n if (!map) {\n map = new Map();\n store.set(type, map);\n }\n return map;\n }\n\n return {\n // [#4058] `degraded` (ADR-0076 D12): the registry is real — everything\n // registered is listable and readable back — it simply never reaches disk\n // or a database. `handlerReady` keeps the `degraded` default (true): the\n // dispatcher's `/meta` domain serves this implementation.\n __serviceInfo: {\n status: 'degraded' as const,\n message: 'In-memory metadata registry — real reads and writes, no persistence (lost on restart). Register MetadataPlugin for a persisted registry.',\n },\n _serviceName: 'metadata',\n async register(type: string, name: string, data: any): Promise<void> {\n getTypeMap(type).set(name, data);\n },\n // Mirror MetadataManager.registerInMemory (synchronous, no persistence).\n // AppPlugin gates code-defined-datasource / stack-RBAC registration on\n // `typeof metadata.registerInMemory === 'function'` (it must register\n // GitOps-managed artefacts *listably* but never persist them). Without this\n // method the guard was false on the host-config / standalone boot path —\n // where this fallback (not MetadataPlugin) provides the `metadata` service —\n // so `defineStack({ datasources })` entries silently never reached the\n // registry and were absent from GET /api/v1/datasources and\n // GET /api/v1/meta/datasource (ADR-0015 §18). This store is already\n // in-memory only, so registerInMemory and register share an implementation.\n registerInMemory(type: string, name: string, data: any): void {\n getTypeMap(type).set(name, data);\n },\n async get(type: string, name: string): Promise<any> {\n return getTypeMap(type).get(name);\n },\n async list(type: string): Promise<any[]> {\n return Array.from(getTypeMap(type).values());\n },\n async unregister(type: string, name: string): Promise<void> {\n getTypeMap(type).delete(name);\n },\n async exists(type: string, name: string): Promise<boolean> {\n return getTypeMap(type).has(name);\n },\n async listNames(type: string): Promise<string[]> {\n return Array.from(getTypeMap(type).keys());\n },\n async getObject(name: string): Promise<any> {\n return getTypeMap('object').get(name);\n },\n async listObjects(): Promise<any[]> {\n return Array.from(getTypeMap('object').values());\n },\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Runtime-authored translation sync (#2591).\n *\n * Translations authored in the Studio persist as `translation` sys_metadata\n * rows (`allowRuntimeCreate: true`), but historically only STATIC bundles\n * (app `bundle.translations`, plugin `translations/`) were ever loaded into\n * the i18n runtime — a published translation was a dead-end on publish AND\n * after a restart.\n *\n * This module is the single shared implementation of the fix, wired by both\n * hosts an `i18n` service can come from:\n * - the runtime's AppPlugin (covers the kernel's in-memory fallback — the\n * dev/standalone reality) and\n * - @objectstack/service-i18n's I18nServicePlugin (the file-based adapter).\n * Both adapters expose `replaceAuthoredTranslations(byLocale)`; the sync\n * computes the full authored layer from the rows and REPLACES it wholesale\n * (clear-then-reload), so deleted items/keys stop resolving.\n *\n * Item payload is a single-locale `TranslationItem` — the same `objects.`\n * groups the file-authored bundles use, plus the `locale` it translates\n * (#3778; before that, this type was registered against an object-first\n * `o.<object>` dialect no resolver read, so an authored translation saved\n * cleanly and rendered nothing). Locale resolution: the top-level `locale`,\n * then the item name when it looks like a BCP-47 tag — the name fallback\n * covers rows written before `locale` became required. Rows still carrying\n * the retired shape are skipped with a warning naming the row, since their\n * content can never resolve. Multiple items on one locale deep-merge in name\n * order (deterministic).\n *\n * Trigger points (wired by {@link wireAuthoredTranslationSync}):\n * • `kernel:ready` — cold-boot coverage;\n * • `metadata:reloaded` — publish-while-running coverage (#2576);\n * • protocol `onMetadataMutation` — direct-active saves / deletes of\n * `translation` rows that don't go through a package publish (#2588's\n * mutation stream).\n *\n * Rows are read straight from `sys_metadata` through the engine — the same\n * discipline as the authored-hook re-sync (#2588): env-scoped kernels\n * surface authored rows nowhere else, and the i18n map is process-wide so\n * rows are taken across all organizations. Best-effort: a failed read keeps\n * the currently applied authored layer.\n */\n\nimport { LEGACY_OBJECT_FIRST_KEYS } from '@objectstack/spec/system';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\n\nimport { deepMerge } from './memory-i18n.js';\n\ntype AnyRecord = Record<string, any>;\n\ninterface MinimalCtx {\n logger: { debug?: (...a: any[]) => void; info?: (...a: any[]) => void; warn?: (...a: any[]) => void };\n getService(name: string): any;\n hook?(name: string, fn: () => Promise<void> | void): void;\n}\n\n/**\n * Ownership marker: several plugins may wire the sync against the same\n * kernel (AppPlugin AND I18nServicePlugin on a production server). The first\n * wirer to touch a given i18n service instance claims it; later wirers\n * no-op, so the layer is computed once per change instead of N times.\n */\nconst OWNER_PROP = '__authoredTranslationSyncOwner';\n\n/**\n * The `i18n` slot as this wirer uses it: the authored-layer replace seam, plus\n * the ownership marker stamped on the instance.\n *\n * [#4251] `replaceAuthoredTranslations` is NOT on `II18nService` — it is the\n * authored-overlay seam service-i18n grew for this sync, and every call site\n * probes for it. `OWNER_PROP` is this module's own marker, stamped on whatever\n * object occupies the slot so two wirers cannot both drive one instance.\n * Declared here rather than erased to `any` so both facts stay legible: what\n * the slot must supply, and what this module writes onto it.\n */\ninterface AuthoredTranslationSink {\n replaceAuthoredTranslations(layer: Record<string, unknown>): void;\n [OWNER_PROP]?: symbol;\n}\n\n// Deliberately narrow (language + optional script/region only): item names\n// are snake_case, so a permissive multi-segment pattern would classify names\n// like `my_custom_strings` as locales.\nconst LOCALE_LIKE = /^[a-z]{2,3}([_-]([A-Za-z]{4}|[A-Za-z]{2}|[0-9]{3}))?$/;\n\n/**\n * Read ACTIVE `translation` metadata rows and compute the authored layer,\n * keyed by locale. Returns `null` when the read failed (callers must keep\n * the current layer, never tear it down on an error).\n */\nexport async function readAuthoredTranslationLayer(\n engine: { find(object: string, opts?: AnyRecord): Promise<any[]> },\n logger?: MinimalCtx['logger'],\n): Promise<Record<string, Record<string, unknown>> | null> {\n let rows: any[];\n try {\n rows = (await engine.find('sys_metadata', {\n where: { type: 'translation', state: 'active' },\n })) ?? [];\n if (rows.length === 0) {\n // Legacy plural rows — mirrors the protocol's singular/plural fallback.\n rows = (await engine.find('sys_metadata', {\n where: { type: 'translations', state: 'active' },\n })) ?? [];\n }\n } catch (err: any) {\n logger?.debug?.('[i18n] authored-translation read failed — keeping current layer', {\n error: err?.message,\n });\n return null;\n }\n\n const byLocale: Record<string, Record<string, unknown>> = {};\n const sorted = [...rows].sort((a, b) => String(a?.name ?? '').localeCompare(String(b?.name ?? '')));\n for (const row of sorted) {\n let data: any;\n try {\n data = typeof row.metadata === 'string' ? JSON.parse(row.metadata) : row.metadata;\n } catch {\n continue; // malformed row — skip it, keep the rest\n }\n if (!data || typeof data !== 'object') continue;\n\n // Rows written against the retired object-first shape resolve to nothing\n // no matter what locale they claim, so say that plainly rather than\n // letting them look loaded. New saves are rejected at the metadata door\n // by `TranslationItemSchema`; this covers rows that predate it.\n const legacyKeys = LEGACY_OBJECT_FIRST_KEYS.filter((key) => data[key] !== undefined);\n if (legacyKeys.length > 0) {\n logger?.warn?.(\n `[i18n] authored translation '${row?.name}' uses the retired object-first shape `\n + `(${legacyKeys.join(', ')}) — nothing resolves from it; re-author it under `\n + \"'objects.<object_name>' with a top-level 'locale' — skipped\",\n );\n continue;\n }\n\n const locale: string | undefined =\n (typeof data?.locale === 'string' && data.locale)\n || (typeof row?.name === 'string' && LOCALE_LIKE.test(row.name) ? row.name : undefined)\n || undefined;\n if (!locale) {\n logger?.warn?.(\n `[i18n] authored translation '${row?.name}' has no resolvable locale `\n + \"(set the top-level 'locale', or name the item after its BCP-47 locale) — skipped\",\n );\n continue;\n }\n // Strip authoring bookkeeping; everything else is translation data. The\n // lock/package fields are stamped by the metadata protocol on published\n // rows — merging them would seed junk keys into the i18n layer.\n const {\n name: _n, locale: _l,\n _packageId: _p, _packageVersion: _pv, _provenance: _pr,\n _lock: _lk, _lockReason: _lr, _lockDocsUrl: _ld, _lockSource: _ls,\n ...payload\n } = data;\n byLocale[locale] = deepMerge(byLocale[locale] ?? {}, payload as Record<string, unknown>);\n }\n return byLocale;\n}\n\n/**\n * Wire the authored-translation sync into a plugin context: registers the\n * `kernel:ready` / `metadata:reloaded` hooks and (at kernel:ready) the\n * protocol mutation subscription. Idempotent per i18n service instance via\n * the ownership marker. Safe to call on kernels with no engine, no protocol,\n * or an i18n service without `replaceAuthoredTranslations` — every path\n * degrades to a no-op.\n */\nexport function wireAuthoredTranslationSync(ctx: MinimalCtx): void {\n if (typeof ctx.hook !== 'function') return;\n\n const token = Symbol('authored-translation-sync');\n const resolveOwnedI18n = (): AuthoredTranslationSink | null => {\n let i18n: AuthoredTranslationSink | undefined;\n try { i18n = ctx.getService('i18n'); } catch { return null; }\n if (!i18n || typeof i18n.replaceAuthoredTranslations !== 'function') return null;\n const current = i18n[OWNER_PROP];\n if (current === undefined) {\n i18n[OWNER_PROP] = token;\n return i18n;\n }\n return current === token ? i18n : null; // another wirer owns this instance\n };\n\n // Serialized: overlapping publishes must not finish out of order and leave\n // the older authored snapshot applied.\n let chain: Promise<void> = Promise.resolve();\n const sync = (): Promise<void> => {\n const run = chain.then(async () => {\n const i18n = resolveOwnedI18n();\n if (!i18n) return;\n let engine: IDataEngine | undefined;\n try { engine = ctx.getService('objectql'); } catch { return; }\n if (!engine || typeof engine.find !== 'function') return;\n const layer = await readAuthoredTranslationLayer(engine, ctx.logger);\n if (layer === null) return; // failed read — keep current layer\n i18n.replaceAuthoredTranslations(layer);\n ctx.logger.info?.('[i18n] synced runtime-authored translations', {\n locales: Object.keys(layer),\n });\n });\n chain = run.catch(() => undefined);\n return run;\n };\n\n ctx.hook('kernel:ready', async () => {\n // Subscribe to translation mutations through the protocol choke point\n // (#2588). Only the owning wirer subscribes.\n if (resolveOwnedI18n()) {\n let protocol: any = null;\n try { protocol = ctx.getService('protocol'); } catch { /* no protocol on this kernel */ }\n if (protocol && typeof protocol.onMetadataMutation === 'function') {\n protocol.onMetadataMutation((evt: { type: string; name: string; state: string }) => {\n if (evt?.type !== 'translation' || evt.state === 'draft') return;\n void sync().catch((err: any) => {\n ctx.logger.warn?.('[i18n] authored-translation re-sync after mutation failed', {\n item: evt.name,\n error: err?.message,\n });\n });\n });\n }\n }\n await sync();\n });\n ctx.hook('metadata:reloaded', async () => {\n await sync();\n });\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createMemoryCache } from './memory-cache.js';\nimport { createMemoryQueue } from './memory-queue.js';\nimport { createMemoryJob } from './memory-job.js';\nimport { createMemoryI18n } from './memory-i18n.js';\nimport { createMemoryMetadata } from './memory-metadata.js';\n\nexport { createMemoryCache } from './memory-cache.js';\nexport { createMemoryQueue } from './memory-queue.js';\nexport { createMemoryJob } from './memory-job.js';\nexport { createMemoryI18n, resolveLocale, deepMerge } from './memory-i18n.js';\nexport { createMemoryMetadata } from './memory-metadata.js';\nexport {\n wireAuthoredTranslationSync,\n readAuthoredTranslationLayer,\n} from './authored-translation-sync.js';\n\n/**\n * Map of core-criticality service names to their in-memory fallback factories.\n * Used by ObjectKernel.validateSystemRequirements() to auto-inject fallbacks\n * when no real plugin provides the service.\n */\nexport const CORE_FALLBACK_FACTORIES: Record<string, () => Record<string, any>> = {\n metadata: createMemoryMetadata,\n cache: createMemoryCache,\n queue: createMemoryQueue,\n job: createMemoryJob,\n i18n: createMemoryI18n,\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin } from './types.js';\nimport { createLogger, ObjectLogger } from './logger.js';\nimport type { LoggerConfig } from '@objectstack/spec/system';\nimport { ObjectKernelBase } from './kernel-base.js';\n\n/**\n * ObjectKernel - MiniKernel Architecture\n * \n * A highly modular, plugin-based microkernel that:\n * - Manages plugin lifecycle (init, start, destroy)\n * - Provides dependency injection via service registry\n * - Implements event/hook system for inter-plugin communication\n * - Handles dependency resolution (topological sort)\n * - Provides configurable logging for server and browser\n * \n * Core philosophy:\n * - Business logic is completely separated into plugins\n * - Kernel only manages lifecycle, DI, and hooks\n * - Plugins are loaded as equal building blocks\n */\nexport class LiteKernel extends ObjectKernelBase {\n constructor(config?: { logger?: Partial<LoggerConfig> }) {\n const logger = createLogger(config?.logger);\n super(logger);\n \n // Initialize context after logger is created\n this.context = this.createContext();\n }\n\n /**\n * Register a plugin\n * @param plugin - Plugin instance\n */\n use(plugin: Plugin): this {\n this.validateIdle();\n\n const pluginName = plugin.name;\n if (this.plugins.has(pluginName)) {\n throw new Error(`[Kernel] Plugin '${pluginName}' already registered`);\n }\n\n this.plugins.set(pluginName, plugin);\n return this;\n }\n\n /**\n * Bootstrap the kernel\n * 1. Resolve dependencies (topological sort)\n * 2. Init phase - plugins register services\n * 3. Start phase - plugins execute business logic\n * 4. Trigger 'kernel:ready' hook\n */\n async bootstrap(): Promise<void> {\n this.validateState('idle');\n\n this.state = 'initializing';\n this.logger.info('Bootstrap started');\n\n // Resolve dependencies\n const orderedPlugins = this.resolveDependencies();\n\n // Pre-Phase-1 ordering contract (ADR-0116, #4131): a plugin that\n // requires a service provided only by a later plugin fails HERE,\n // named, before any init side effects.\n this.validateInitServices(orderedPlugins);\n\n // Phase 1: Init - Plugins register services\n this.logger.info('Phase 1: Init plugins');\n for (const plugin of orderedPlugins) {\n await this.runPluginInit(plugin);\n }\n\n // Phase 2: Start - Plugins execute business logic\n this.logger.info('Phase 2: Start plugins');\n this.state = 'running';\n \n for (const plugin of orderedPlugins) {\n await this.runPluginStart(plugin);\n }\n\n // The three boot-path lifecycle hooks all use PROPAGATING dispatch,\n // identical to `ObjectKernel.bootstrap()`'s `context.trigger` (a bare\n // awaited loop that never catches): a handler that throws FAILS THE\n // BOOT on both kernels, the remaining handlers are skipped, the\n // original error reaches the caller unwrapped, and the kernel is left\n // 'stopped' rather than 'running' so a failed boot never reads as a\n // live kernel. `kernel:ready` got this in #5170; `kernel:bootstrapped`\n // and `kernel:listening` in #5257.\n //\n // Why the boot path is the wrong place to be forgiving: everything\n // between here and the log line below is a PRECONDITION of the\n // \"✅ Bootstrap complete\" this method is about to print. Swallowing a\n // throw does not make the boot succeed — it only makes the failure\n // invisible while `bootstrap()` resolves normally. `kernel:listening`\n // is the sharpest case: it is where HTTP server plugins actually open\n // their socket (`HonoServerPlugin` awaits `server.listen(port)` with\n // no try/catch of its own, deliberately — propagation is the correct\n // behaviour there), so a swallowed EACCES / unavailable-listen on an\n // edge or serverless host produced a live process, a cheerful\n // \"Bootstrap complete\", and not one socket listening.\n try {\n // Route/middleware registration phase, and the only correct moment\n // for a plugin to assert that what it DECLARED can actually be\n // delivered — the registries are still filling during init(), so a\n // boot gate has nowhere earlier to run (#5170).\n await this.triggerHookOrThrow('kernel:ready');\n // \"All synchronous bootstrap has settled\" anchor, strictly after\n // every kernel:ready handler has settled and before any HTTP socket\n // opens. Carries reconcile/backfill/audit work. NOTE: does not\n // guarantee background app seed data has settled — subscribe\n // `app:seeded` for that (see plugin-lifecycle-events.ts).\n await this.triggerHookOrThrow('kernel:bootstrapped');\n // HTTP servers open their listening socket here — strictly after\n // every kernel:ready and kernel:bootstrapped handler has completed.\n await this.triggerHookOrThrow('kernel:listening');\n } catch (error) {\n this.state = 'stopped';\n throw error;\n }\n this.logger.info('✅ Bootstrap complete', {\n pluginCount: this.plugins.size\n });\n }\n\n /**\n * Shutdown the kernel\n * Calls destroy on all plugins in reverse order\n */\n async shutdown(): Promise<void> {\n await this.destroy();\n }\n\n /**\n * Graceful shutdown - destroy all plugins in reverse order\n */\n async destroy(): Promise<void> {\n if (this.state === 'stopped') {\n this.logger.warn('Kernel already stopped');\n return;\n }\n\n this.state = 'stopping';\n this.logger.info('Shutdown started');\n\n // Trigger shutdown hook — FAIL-SOFT dispatch ({@link triggerHook}),\n // deliberately, and NOT the propagating dispatcher the boot-path hooks\n // above use (#5257). This is a per-hook judgement written down, not an\n // inherited default: on the shutdown path there is no \"refuse to\n // proceed\" left to buy. The remaining work — every other subscriber's\n // cleanup, then each plugin's destroy() in reverse order — is what\n // flushes buffers, closes connections and releases locks, so letting\n // one subscriber's failure abort the rest converts a single bad\n // handler into leaked resources and unflushed writes. A failing\n // shutdown handler is logged (`Hook handler failed: kernel:shutdown`)\n // and the cleanup continues.\n await this.triggerHook('kernel:shutdown');\n\n // Destroy plugins in reverse order\n const orderedPlugins = this.resolveDependencies();\n for (const plugin of orderedPlugins.reverse()) {\n await this.runPluginDestroy(plugin);\n }\n\n this.state = 'stopped';\n this.logger.info('✅ Shutdown complete');\n \n // Cleanup logger resources\n if (this.logger && typeof (this.logger as ObjectLogger).destroy === 'function') {\n await (this.logger as ObjectLogger).destroy();\n }\n }\n\n /**\n * Get a service from the registry\n * Convenience method for external access\n */\n getService<T>(name: string): T {\n return this.context.getService<T>(name);\n }\n\n /**\n * Check if kernel is running\n */\n isRunning(): boolean {\n return this.state === 'running';\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './adapter.js';\nexport * from './runner.js';\nexport * from './http-adapter.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as QA from '@objectstack/spec/qa';\nimport { TestExecutionAdapter } from './adapter.js';\n\nexport interface TestResult {\n scenarioId: string;\n passed: boolean;\n steps: StepResult[];\n error?: unknown;\n duration: number;\n}\n\nexport interface StepResult {\n stepName: string;\n passed: boolean;\n error?: unknown;\n output?: unknown;\n duration: number;\n}\n\nexport class TestRunner {\n constructor(private adapter: TestExecutionAdapter) {}\n\n async runSuite(suite: QA.TestSuite): Promise<TestResult[]> {\n const results: TestResult[] = [];\n for (const scenario of suite.scenarios) {\n results.push(await this.runScenario(scenario));\n }\n return results;\n }\n\n async runScenario(scenario: QA.TestScenario): Promise<TestResult> {\n const startTime = Date.now();\n const context: Record<string, unknown> = {}; // Variable context\n \n // Initialize context from initial payload if needed? Currently schema doesn't have initial context prop on Scenario\n // But we defined TestContextSchema separately.\n \n // Setup\n if (scenario.setup) {\n for (const step of scenario.setup) {\n try {\n await this.runStep(step, context);\n } catch (e) {\n return {\n scenarioId: scenario.id,\n passed: false,\n steps: [],\n error: `Setup failed: ${e instanceof Error ? e.message : String(e)}`,\n duration: Date.now() - startTime\n };\n }\n }\n }\n\n const stepResults: StepResult[] = [];\n let scenarioPassed = true;\n let scenarioError: unknown = undefined;\n\n // Main Steps\n for (const step of scenario.steps) {\n const stepStartTime = Date.now();\n try {\n const output = await this.runStep(step, context);\n stepResults.push({\n stepName: step.name,\n passed: true,\n output,\n duration: Date.now() - stepStartTime\n });\n } catch (e) {\n scenarioPassed = false;\n scenarioError = e;\n stepResults.push({\n stepName: step.name,\n passed: false,\n error: e,\n duration: Date.now() - stepStartTime\n });\n break; // Stop on first failure\n }\n }\n\n // Teardown (run even if failed)\n if (scenario.teardown) {\n for (const step of scenario.teardown) {\n try {\n await this.runStep(step, context);\n } catch (e) {\n // Log teardown failure but don't override main failure if it exists\n if (scenarioPassed) {\n scenarioPassed = false;\n scenarioError = `Teardown failed: ${e instanceof Error ? e.message : String(e)}`;\n }\n }\n }\n }\n\n return {\n scenarioId: scenario.id,\n passed: scenarioPassed,\n steps: stepResults,\n error: scenarioError,\n duration: Date.now() - startTime\n };\n }\n\n private async runStep(step: QA.TestStep, context: Record<string, unknown>): Promise<unknown> {\n // 1. Resolve Variables with Context (Simple interpolation or just pass context?)\n // For now, assume adpater handles context resolution or we do basic replacement\n const resolvedAction = this.resolveVariables(step.action, context);\n\n // 2. Execute Action\n const result = await this.adapter.execute(resolvedAction, context);\n\n // 3. Capture Outputs\n if (step.capture) {\n for (const [varName, path] of Object.entries(step.capture)) {\n context[varName] = this.getValueByPath(result, path);\n }\n }\n\n // 4. Run Assertions\n if (step.assertions) {\n for (const assertion of step.assertions) {\n this.assert(result, assertion, context);\n }\n }\n\n return result;\n }\n\n private resolveVariables(action: QA.TestAction, context: Record<string, unknown>): QA.TestAction {\n const actionStr = JSON.stringify(action);\n const resolved = actionStr.replace(/\\{\\{([^}]+)\\}\\}/g, (_match, varPath: string) => {\n const value = this.getValueByPath(context, varPath.trim());\n if (value === undefined) return _match; // Keep unresolved\n return typeof value === 'string' ? value : JSON.stringify(value);\n });\n try {\n return JSON.parse(resolved) as QA.TestAction;\n } catch {\n return action; // Fallback to original if parse fails\n }\n }\n\n private getValueByPath(obj: unknown, path: string): unknown {\n if (!path) return obj;\n const parts = path.split('.');\n let current: any = obj;\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n current = current[part];\n }\n return current;\n }\n\n private assert(result: unknown, assertion: QA.TestAssertion, _context: Record<string, unknown>) {\n const actual = this.getValueByPath(result, assertion.field);\n // Resolve expected value if it's a variable ref? \n const expected = assertion.expectedValue; // Simplify for now\n\n switch (assertion.operator) {\n case 'equals':\n if (actual !== expected) throw new Error(`Assertion failed: ${assertion.field} expected ${expected}, got ${actual}`);\n break;\n case 'not_equals':\n if (actual === expected) throw new Error(`Assertion failed: ${assertion.field} expected not ${expected}, got ${actual}`);\n break;\n case 'contains':\n if (Array.isArray(actual)) {\n if (!actual.includes(expected)) throw new Error(`Assertion failed: ${assertion.field} array does not contain ${expected}`);\n } else if (typeof actual === 'string') {\n if (!actual.includes(String(expected))) throw new Error(`Assertion failed: ${assertion.field} string does not contain ${expected}`);\n }\n break;\n case 'not_null':\n if (actual === null || actual === undefined) throw new Error(`Assertion failed: ${assertion.field} is null`);\n break;\n case 'is_null':\n if (actual !== null && actual !== undefined) throw new Error(`Assertion failed: ${assertion.field} is not null`);\n break;\n // ... Add other operators\n default:\n throw new Error(`Unknown assertion operator: ${assertion.operator}`);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as QA from '@objectstack/spec/qa';\nimport { TestExecutionAdapter } from './adapter.js';\n\nexport class HttpTestAdapter implements TestExecutionAdapter {\n constructor(private baseUrl: string, private authToken?: string) {}\n\n async execute(action: QA.TestAction, _context: Record<string, unknown>): Promise<unknown> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.authToken) {\n headers['Authorization'] = `Bearer ${this.authToken}`;\n }\n // If action.user is specified, maybe add a specific header for impersonation if supported?\n if (action.user) {\n headers['X-Run-As'] = action.user;\n }\n\n switch (action.type) {\n case 'create_record':\n return this.createRecord(action.target, action.payload || {}, headers);\n case 'update_record':\n return this.updateRecord(action.target, action.payload || {}, headers);\n case 'delete_record':\n return this.deleteRecord(action.target, action.payload || {}, headers);\n case 'read_record':\n return this.readRecord(action.target, action.payload || {}, headers);\n case 'query_records':\n return this.queryRecords(action.target, action.payload || {}, headers);\n case 'api_call':\n return this.rawApiCall(action.target, action.payload || {}, headers);\n case 'wait':\n const ms = Number(action.payload?.duration || 1000);\n return new Promise(resolve => setTimeout(() => resolve({ waited: ms }), ms));\n default:\n throw new Error(`Unsupported action type in HttpAdapter: ${action.type}`);\n }\n }\n\n private async createRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const response = await fetch(`${this.baseUrl}/api/data/${objectName}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(data)\n });\n return this.handleResponse(response);\n }\n\n private async updateRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const id = data.id;\n if (!id) throw new Error('Update record requires id in payload');\n const response = await fetch(`${this.baseUrl}/api/data/${objectName}/${id}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(data)\n });\n return this.handleResponse(response);\n }\n\n private async deleteRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const id = data.id;\n if (!id) throw new Error('Delete record requires id in payload');\n const response = await fetch(`${this.baseUrl}/api/data/${objectName}/${id}`, {\n method: 'DELETE',\n headers\n });\n return this.handleResponse(response);\n }\n\n private async readRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const id = data.id;\n if (!id) throw new Error('Read record requires id in payload');\n const response = await fetch(`${this.baseUrl}/api/data/${objectName}/${id}`, {\n method: 'GET',\n headers\n });\n return this.handleResponse(response);\n }\n\n private async queryRecords(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n // Assuming query via POST or GraphQL-like endpoint\n const response = await fetch(`${this.baseUrl}/api/data/${objectName}/query`, {\n method: 'POST',\n headers,\n body: JSON.stringify(data)\n });\n return this.handleResponse(response);\n }\n\n private async rawApiCall(endpoint: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const method = (data.method as string) || 'GET';\n const body = data.body ? JSON.stringify(data.body) : undefined;\n const url = endpoint.startsWith('http') ? endpoint : `${this.baseUrl}${endpoint}`;\n \n const response = await fetch(url, {\n method,\n headers,\n body\n });\n return this.handleResponse(response);\n }\n\n private async handleResponse(response: Response) {\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`HTTP Error ${response.status}: ${text}`);\n }\n const contentType = response.headers.get('content-type');\n if (contentType && contentType.includes('application/json')) {\n return response.json();\n }\n return response.text();\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { PluginMetadata } from '../plugin-loader.js';\n\n// Conditionally import crypto for Node.js environments\nlet cryptoModule: typeof import('crypto') | null = null;\n\n\n/**\n * Plugin Signature Configuration\n * Controls how plugin signatures are verified\n */\nexport interface PluginSignatureConfig {\n /**\n * Map of publisher IDs to their trusted public keys\n * Format: { 'com.objectstack': '-----BEGIN PUBLIC KEY-----...' }\n */\n trustedPublicKeys: Map<string, string>;\n \n /**\n * Signature algorithm to use\n * - RS256: RSA with SHA-256\n * - ES256: ECDSA with SHA-256\n */\n algorithm: 'RS256' | 'ES256';\n \n /**\n * Strict mode: reject plugins without signatures\n * - true: All plugins must be signed\n * - false: Unsigned plugins are allowed with warning\n */\n strictMode: boolean;\n \n /**\n * Allow self-signed plugins in development\n */\n allowSelfSigned?: boolean;\n}\n\n/**\n * Plugin Signature Verification Result\n */\nexport interface SignatureVerificationResult {\n verified: boolean;\n error?: string;\n publisherId?: string;\n algorithm?: string;\n signedAt?: Date;\n}\n\n/**\n * Plugin Signature Verifier\n * \n * Implements cryptographic verification of plugin signatures to ensure:\n * 1. Plugin integrity - code hasn't been tampered with\n * 2. Publisher authenticity - plugin comes from trusted source\n * 3. Non-repudiation - publisher cannot deny signing\n * \n * Architecture:\n * - Uses Node.js crypto module for signature verification\n * - Supports RSA (RS256) and ECDSA (ES256) algorithms\n * - Verifies against trusted public key registry\n * - Computes hash of plugin code for integrity check\n * \n * Security Model:\n * - Public keys are pre-registered and trusted\n * - Plugin signature is verified before loading\n * - Strict mode rejects unsigned plugins\n * - Development mode allows self-signed plugins\n */\nexport class PluginSignatureVerifier {\n private config: PluginSignatureConfig;\n private logger: Logger;\n \n constructor(config: PluginSignatureConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n \n this.validateConfig();\n }\n \n /**\n * Verify plugin signature\n * \n * @param plugin - Plugin metadata with signature\n * @returns Verification result\n * @throws Error if verification fails in strict mode\n */\n async verifyPluginSignature(plugin: PluginMetadata): Promise<SignatureVerificationResult> {\n // Handle unsigned plugins\n if (!plugin.signature) {\n return this.handleUnsignedPlugin(plugin);\n }\n \n try {\n // 1. Extract publisher ID from plugin name (reverse domain notation)\n const publisherId = this.extractPublisherId(plugin.name);\n \n // 2. Get trusted public key for publisher\n const publicKey = this.config.trustedPublicKeys.get(publisherId);\n if (!publicKey) {\n const error = `No trusted public key for publisher: ${publisherId}`;\n this.logger.warn(error, { plugin: plugin.name, publisherId });\n \n if (this.config.strictMode && !this.config.allowSelfSigned) {\n throw new Error(error);\n }\n \n return {\n verified: false,\n error,\n publisherId,\n };\n }\n \n // 3. Compute plugin code hash\n const pluginHash = this.computePluginHash(plugin);\n \n // 4. Verify signature using crypto module\n const isValid = await this.verifyCryptoSignature(\n pluginHash,\n plugin.signature,\n publicKey\n );\n \n if (!isValid) {\n const error = `Signature verification failed for plugin: ${plugin.name}`;\n this.logger.error(error, undefined, { plugin: plugin.name, publisherId });\n throw new Error(error);\n }\n \n this.logger.info(`✅ Plugin signature verified: ${plugin.name}`, {\n plugin: plugin.name,\n publisherId,\n algorithm: this.config.algorithm,\n });\n \n return {\n verified: true,\n publisherId,\n algorithm: this.config.algorithm,\n };\n \n } catch (error) {\n this.logger.error(`Signature verification error: ${plugin.name}`, error as Error);\n \n if (this.config.strictMode) {\n throw error;\n }\n \n return {\n verified: false,\n error: (error as Error).message,\n };\n }\n }\n \n /**\n * Register a trusted public key for a publisher\n */\n registerPublicKey(publisherId: string, publicKey: string): void {\n this.config.trustedPublicKeys.set(publisherId, publicKey);\n this.logger.info(`Trusted public key registered for: ${publisherId}`);\n }\n \n /**\n * Remove a trusted public key\n */\n revokePublicKey(publisherId: string): void {\n this.config.trustedPublicKeys.delete(publisherId);\n this.logger.warn(`Public key revoked for: ${publisherId}`);\n }\n \n /**\n * Get list of trusted publishers\n */\n getTrustedPublishers(): string[] {\n return Array.from(this.config.trustedPublicKeys.keys());\n }\n \n // Private methods\n \n private handleUnsignedPlugin(plugin: PluginMetadata): SignatureVerificationResult {\n if (this.config.strictMode) {\n const error = `Plugin missing signature (strict mode): ${plugin.name}`;\n this.logger.error(error, undefined, { plugin: plugin.name });\n throw new Error(error);\n }\n \n this.logger.warn(`⚠️ Plugin not signed: ${plugin.name}`, {\n plugin: plugin.name,\n recommendation: 'Consider signing plugins for production environments',\n });\n \n return {\n verified: false,\n error: 'Plugin not signed',\n };\n }\n \n private extractPublisherId(pluginName: string): string {\n // Extract publisher from reverse domain notation\n // Example: \"com.objectstack.engine.objectql\" -> \"com.objectstack\"\n const parts = pluginName.split('.');\n \n if (parts.length < 2) {\n throw new Error(`Invalid plugin name format: ${pluginName} (expected reverse domain notation)`);\n }\n \n // Return first two parts (domain reversed)\n return `${parts[0]}.${parts[1]}`;\n }\n \n private computePluginHash(plugin: PluginMetadata): string {\n // In browser environment, use SubtleCrypto\n if (typeof (globalThis as any).window !== 'undefined') {\n return this.computePluginHashBrowser(plugin);\n }\n \n // In Node.js environment, use crypto module\n return this.computePluginHashNode(plugin);\n }\n \n private computePluginHashNode(plugin: PluginMetadata): string {\n // Use pre-loaded crypto module\n if (!cryptoModule) {\n this.logger.warn('crypto module not available, using fallback hash');\n return this.computePluginHashFallback(plugin);\n }\n \n // Compute hash of plugin code\n const pluginCode = this.serializePluginCode(plugin);\n return cryptoModule.createHash('sha256').update(pluginCode).digest('hex');\n }\n \n private computePluginHashBrowser(plugin: PluginMetadata): string {\n // Browser environment - use simple hash for now\n // In production, should use SubtleCrypto for proper cryptographic hash\n this.logger.debug('Using browser hash (SubtleCrypto integration pending)');\n return this.computePluginHashFallback(plugin);\n }\n \n private computePluginHashFallback(plugin: PluginMetadata): string {\n // Simple hash fallback (not cryptographically secure)\n const pluginCode = this.serializePluginCode(plugin);\n let hash = 0;\n \n for (let i = 0; i < pluginCode.length; i++) {\n const char = pluginCode.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n \n return hash.toString(16);\n }\n \n private serializePluginCode(plugin: PluginMetadata): string {\n // Serialize plugin code for hashing\n // Include init, start, destroy functions\n const parts: string[] = [\n plugin.name,\n plugin.version,\n plugin.init.toString(),\n ];\n \n if (plugin.start) {\n parts.push(plugin.start.toString());\n }\n \n if (plugin.destroy) {\n parts.push(plugin.destroy.toString());\n }\n \n return parts.join('|');\n }\n \n private async verifyCryptoSignature(\n data: string,\n signature: string,\n publicKey: string\n ): Promise<boolean> {\n // In browser environment, use SubtleCrypto\n if (typeof (globalThis as any).window !== 'undefined') {\n return this.verifyCryptoSignatureBrowser(data, signature, publicKey);\n }\n \n // In Node.js environment, use crypto module\n return this.verifyCryptoSignatureNode(data, signature, publicKey);\n }\n \n private async verifyCryptoSignatureNode(\n data: string,\n signature: string,\n publicKey: string\n ): Promise<boolean> {\n if (!cryptoModule) {\n try {\n // @ts-ignore\n cryptoModule = await import('crypto');\n } catch (e) {\n // ignore\n }\n }\n\n if (!cryptoModule) {\n this.logger.error('Crypto module not available for signature verification');\n return false;\n }\n \n try {\n // Create verify object based on algorithm\n if (this.config.algorithm === 'ES256') {\n // ECDSA verification - requires lowercase 'sha256'\n const verify = cryptoModule.createVerify('sha256');\n verify.update(data);\n return verify.verify(\n {\n key: publicKey,\n format: 'pem',\n type: 'spki',\n },\n signature,\n 'base64'\n );\n } else {\n // RSA verification (RS256)\n const verify = cryptoModule.createVerify('RSA-SHA256');\n verify.update(data);\n return verify.verify(publicKey, signature, 'base64');\n }\n } catch (error) {\n this.logger.error('Signature verification failed', error as Error);\n return false;\n }\n }\n \n private async verifyCryptoSignatureBrowser(\n data: string,\n signature: string,\n publicKey: string\n ): Promise<boolean> {\n try {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n this.logger.error('SubtleCrypto not available in this environment');\n return false;\n }\n\n // Decode PEM public key to raw DER bytes\n const pemBody = publicKey\n .replace(/-----BEGIN PUBLIC KEY-----/, '')\n .replace(/-----END PUBLIC KEY-----/, '')\n .replace(/\\s/g, '');\n const keyBytes = Uint8Array.from(atob(pemBody), c => c.charCodeAt(0));\n\n // Configure algorithms based on RS256 or ES256\n let importAlgorithm: { name: string; hash?: string; namedCurve?: string };\n let verifyAlgorithm: { name: string; hash?: string };\n\n if (this.config.algorithm === 'ES256') {\n importAlgorithm = { name: 'ECDSA', namedCurve: 'P-256' };\n verifyAlgorithm = { name: 'ECDSA', hash: 'SHA-256' };\n } else {\n importAlgorithm = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };\n verifyAlgorithm = { name: 'RSASSA-PKCS1-v1_5' };\n }\n\n const cryptoKey = await subtle.importKey(\n 'spki',\n keyBytes,\n importAlgorithm,\n false,\n ['verify']\n );\n\n // Decode base64 signature to ArrayBuffer\n const signatureBytes = Uint8Array.from(atob(signature), c => c.charCodeAt(0));\n\n // Encode data to ArrayBuffer\n const dataBytes = new TextEncoder().encode(data);\n\n return await subtle.verify(verifyAlgorithm, cryptoKey, signatureBytes, dataBytes);\n } catch (error) {\n this.logger.error('Browser signature verification failed', error as Error);\n return false;\n }\n }\n \n private validateConfig(): void {\n if (!this.config.trustedPublicKeys || this.config.trustedPublicKeys.size === 0) {\n this.logger.warn('No trusted public keys configured - all signatures will fail');\n }\n \n if (!this.config.algorithm) {\n throw new Error('Signature algorithm must be specified');\n }\n \n if (!['RS256', 'ES256'].includes(this.config.algorithm)) {\n throw new Error(`Unsupported algorithm: ${this.config.algorithm}`);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { PluginCapability, PluginPermissions as GrantedPermissions } from '@objectstack/spec/kernel';\nimport type { PluginContext } from '../types.js';\n\n/**\n * Plugin Permissions\n * Defines what actions a plugin is allowed to perform\n */\nexport interface PluginPermissions {\n canAccessService(serviceName: string): boolean;\n canTriggerHook(hookName: string): boolean;\n canReadFile(path: string): boolean;\n canWriteFile(path: string): boolean;\n canNetworkRequest(url: string): boolean;\n}\n\n/**\n * Permission Check Result\n */\nexport interface PermissionCheckResult {\n allowed: boolean;\n reason?: string;\n capability?: string;\n}\n\n/**\n * Plugin Permission Enforcer\n * \n * Implements capability-based security model to enforce:\n * 1. Service access control - which services a plugin can use\n * 2. Hook restrictions - which hooks a plugin can trigger\n * 3. File system permissions - what files a plugin can read/write\n * 4. Network permissions - what URLs a plugin can access\n * \n * Architecture:\n * - Uses capability declarations from plugin manifest\n * - Checks permissions before allowing operations\n * - Logs all permission denials for security audit\n * - Supports allowlist and denylist patterns\n * \n * Security Model:\n * - Principle of least privilege - plugins get minimal permissions\n * - Explicit declaration - all capabilities must be declared\n * - Runtime enforcement - checks happen at operation time\n * - Audit trail - all denials are logged\n * \n * Usage:\n * ```typescript\n * const enforcer = new PluginPermissionEnforcer(logger);\n * enforcer.registerPluginPermissions(pluginName, capabilities);\n * enforcer.enforceServiceAccess(pluginName, 'database');\n * ```\n */\nexport class PluginPermissionEnforcer {\n private logger: Logger;\n private permissionRegistry: Map<string, PluginPermissions> = new Map();\n private capabilityRegistry: Map<string, PluginCapability[]> = new Map();\n \n constructor(logger: Logger) {\n this.logger = logger;\n }\n \n /**\n * Register plugin capabilities and build permission set\n * \n * @param pluginName - Plugin identifier\n * @param capabilities - Array of capability declarations\n */\n registerPluginPermissions(pluginName: string, capabilities: PluginCapability[]): void {\n this.capabilityRegistry.set(pluginName, capabilities);\n \n const permissions: PluginPermissions = {\n canAccessService: (service) => this.checkServiceAccess(capabilities, service),\n canTriggerHook: (hook) => this.checkHookAccess(capabilities, hook),\n canReadFile: (path) => this.checkFileRead(capabilities, path),\n canWriteFile: (path) => this.checkFileWrite(capabilities, path),\n canNetworkRequest: (url) => this.checkNetworkAccess(capabilities, url),\n };\n \n this.permissionRegistry.set(pluginName, permissions);\n \n this.logger.info(`Permissions registered for plugin: ${pluginName}`, {\n plugin: pluginName,\n capabilityCount: capabilities.length,\n });\n }\n \n /**\n * Register the install-time GRANTED permission set for a plugin\n * (ADR-0025 F4). This is the structured `{ services, hooks, network, fs }`\n * grant that the cloud control plane persists to\n * `sys_package_installation.granted_permissions` after the user consents\n * at install (ADR §3.5 step 2). The runtime calls this when materializing\n * a third-party plugin so {@link SecurePluginContext} enforces exactly the\n * consented surface — independent of whatever the manifest *requested*.\n *\n * Prefer this over {@link registerPluginPermissions} for distributed\n * plugins: it enforces what was granted, not what was declared.\n */\n registerGrantedPermissions(pluginName: string, granted: GrantedPermissions | null | undefined): void {\n this.permissionRegistry.set(pluginName, buildPermissionsFromGrants(granted));\n this.logger.info(`Granted permissions registered for plugin: ${pluginName}`, {\n plugin: pluginName,\n services: granted?.services?.length ?? 0,\n hooks: granted?.hooks?.length ?? 0,\n network: granted?.network?.length ?? 0,\n fs: granted?.fs?.length ?? 0,\n });\n }\n\n /**\n * Enforce service access permission\n *\n * @param pluginName - Plugin requesting access\n * @param serviceName - Service to access\n * @throws Error if permission denied\n */\n enforceServiceAccess(pluginName: string, serviceName: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canAccessService(serviceName));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot access service ${serviceName}`;\n this.logger.warn(error, {\n plugin: pluginName,\n service: serviceName,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`Service access granted: ${pluginName} -> ${serviceName}`);\n }\n \n /**\n * Enforce hook trigger permission\n * \n * @param pluginName - Plugin requesting access\n * @param hookName - Hook to trigger\n * @throws Error if permission denied\n */\n enforceHookTrigger(pluginName: string, hookName: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canTriggerHook(hookName));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot trigger hook ${hookName}`;\n this.logger.warn(error, {\n plugin: pluginName,\n hook: hookName,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`Hook trigger granted: ${pluginName} -> ${hookName}`);\n }\n \n /**\n * Enforce file read permission\n * \n * @param pluginName - Plugin requesting access\n * @param path - File path to read\n * @throws Error if permission denied\n */\n enforceFileRead(pluginName: string, path: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canReadFile(path));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot read file ${path}`;\n this.logger.warn(error, {\n plugin: pluginName,\n path,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`File read granted: ${pluginName} -> ${path}`);\n }\n \n /**\n * Enforce file write permission\n * \n * @param pluginName - Plugin requesting access\n * @param path - File path to write\n * @throws Error if permission denied\n */\n enforceFileWrite(pluginName: string, path: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canWriteFile(path));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot write file ${path}`;\n this.logger.warn(error, {\n plugin: pluginName,\n path,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`File write granted: ${pluginName} -> ${path}`);\n }\n \n /**\n * Enforce network request permission\n * \n * @param pluginName - Plugin requesting access\n * @param url - URL to access\n * @throws Error if permission denied\n */\n enforceNetworkRequest(pluginName: string, url: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canNetworkRequest(url));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot access URL ${url}`;\n this.logger.warn(error, {\n plugin: pluginName,\n url,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`Network request granted: ${pluginName} -> ${url}`);\n }\n \n /**\n * Get plugin capabilities\n * \n * @param pluginName - Plugin identifier\n * @returns Array of capabilities or undefined\n */\n getPluginCapabilities(pluginName: string): PluginCapability[] | undefined {\n return this.capabilityRegistry.get(pluginName);\n }\n \n /**\n * Get plugin permissions\n * \n * @param pluginName - Plugin identifier\n * @returns Permissions object or undefined\n */\n getPluginPermissions(pluginName: string): PluginPermissions | undefined {\n return this.permissionRegistry.get(pluginName);\n }\n \n /**\n * Revoke all permissions for a plugin\n * \n * @param pluginName - Plugin identifier\n */\n revokePermissions(pluginName: string): void {\n this.permissionRegistry.delete(pluginName);\n this.capabilityRegistry.delete(pluginName);\n this.logger.warn(`Permissions revoked for plugin: ${pluginName}`);\n }\n \n // Private methods\n \n private checkPermission(\n pluginName: string,\n check: (perms: PluginPermissions) => boolean\n ): PermissionCheckResult {\n const permissions = this.permissionRegistry.get(pluginName);\n \n if (!permissions) {\n return {\n allowed: false,\n reason: 'Plugin permissions not registered',\n };\n }\n \n const allowed = check(permissions);\n \n return {\n allowed,\n reason: allowed ? undefined : 'No matching capability found',\n };\n }\n \n private checkServiceAccess(capabilities: PluginCapability[], serviceName: string): boolean {\n // Check if plugin has capability to access this service\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for wildcard service access\n if (protocolId.includes('protocol.service.all')) {\n return true;\n }\n \n // Check for specific service protocol\n if (protocolId.includes(`protocol.service.${serviceName}`)) {\n return true;\n }\n \n // Check for service category match\n const serviceCategory = serviceName.split('.')[0];\n if (protocolId.includes(`protocol.service.${serviceCategory}`)) {\n return true;\n }\n \n return false;\n });\n }\n \n private checkHookAccess(capabilities: PluginCapability[], hookName: string): boolean {\n // Check if plugin has capability to trigger this hook\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for wildcard hook access\n if (protocolId.includes('protocol.hook.all')) {\n return true;\n }\n \n // Check for specific hook protocol\n if (protocolId.includes(`protocol.hook.${hookName}`)) {\n return true;\n }\n \n // Check for hook category match\n const hookCategory = hookName.split(':')[0];\n if (protocolId.includes(`protocol.hook.${hookCategory}`)) {\n return true;\n }\n \n return false;\n });\n }\n \n private matchGlob(pattern: string, str: string): boolean {\n const regexStr = pattern\n .split('**')\n .map(segment => {\n const escaped = segment.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return escaped.replace(/\\*/g, '[^/]*');\n })\n .join('.*');\n return new RegExp(`^${regexStr}$`).test(str);\n }\n \n private checkFileRead(capabilities: PluginCapability[], path: string): boolean {\n // Check if plugin has capability to read this file\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for file read capability\n if (protocolId.includes('protocol.filesystem.read')) {\n const paths = cap.metadata?.paths;\n if (!Array.isArray(paths) || paths.length === 0) {\n return true;\n }\n return paths.some(p => typeof p === 'string' && this.matchGlob(p, path));\n }\n \n return false;\n });\n }\n \n private checkFileWrite(capabilities: PluginCapability[], path: string): boolean {\n // Check if plugin has capability to write this file\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for file write capability\n if (protocolId.includes('protocol.filesystem.write')) {\n const paths = cap.metadata?.paths;\n if (!Array.isArray(paths) || paths.length === 0) {\n return true;\n }\n return paths.some(p => typeof p === 'string' && this.matchGlob(p, path));\n }\n \n return false;\n });\n }\n \n private checkNetworkAccess(capabilities: PluginCapability[], url: string): boolean {\n // Check if plugin has capability to access this URL\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for network capability\n if (protocolId.includes('protocol.network')) {\n const hosts = cap.metadata?.hosts;\n if (!Array.isArray(hosts) || hosts.length === 0) {\n return true;\n }\n return hosts.some(h => typeof h === 'string' && this.matchGlob(h, url));\n }\n \n return false;\n });\n }\n}\n\n/**\n * Secure Plugin Context\n * Wraps PluginContext with permission checks\n */\nexport class SecurePluginContext implements PluginContext {\n constructor(\n private pluginName: string,\n private permissionEnforcer: PluginPermissionEnforcer,\n private baseContext: PluginContext\n ) {}\n \n registerService(name: string, service: any): void {\n // No permission check for service registration (handled during init)\n this.baseContext.registerService(name, service);\n }\n \n getService<T>(name: string): T {\n // Check permission before accessing service\n this.permissionEnforcer.enforceServiceAccess(this.pluginName, name);\n return this.baseContext.getService<T>(name);\n }\n \n replaceService<T>(name: string, implementation: T): void {\n // Check permission before replacing service\n this.permissionEnforcer.enforceServiceAccess(this.pluginName, name);\n this.baseContext.replaceService(name, implementation);\n }\n \n getServices(): Map<string, any> {\n // Return all services (no permission check for listing)\n return this.baseContext.getServices();\n }\n \n hook(name: string, handler: (...args: any[]) => void | Promise<void>): void {\n // No permission check for registering hooks (handled during init)\n this.baseContext.hook(name, handler);\n }\n \n async trigger(name: string, ...args: any[]): Promise<void> {\n // Check permission before triggering hook\n this.permissionEnforcer.enforceHookTrigger(this.pluginName, name);\n await this.baseContext.trigger(name, ...args);\n }\n \n get logger() {\n return this.baseContext.logger;\n }\n \n getKernel() {\n return this.baseContext.getKernel();\n }\n\n registerServiceFactory(name: string, factory: (ctx: PluginContext, scopeId?: string) => any, lifecycle?: import('../plugin-loader.js').ServiceLifecycle, dependencies?: string[]): void {\n this.baseContext.registerServiceFactory(name, factory, lifecycle, dependencies);\n }\n\n getServiceScoped<T>(name: string, scopeId: string): Promise<T> {\n return this.baseContext.getServiceScoped<T>(name, scopeId);\n }\n}\n\n/**\n * Create a plugin permission enforcer\n *\n * @param logger - Logger instance\n * @returns Plugin permission enforcer\n */\nexport function createPluginPermissionEnforcer(logger: Logger): PluginPermissionEnforcer {\n return new PluginPermissionEnforcer(logger);\n}\n\n/**\n * Glob match supporting `*` (within a path segment) and `**` (across\n * segments). A bare `*` entry matches everything.\n */\nfunction grantGlobMatch(pattern: string, value: string): boolean {\n if (pattern === '*' || pattern === '**') return true;\n const regexStr = pattern\n .split('**')\n .map((segment) => segment.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '[^/]*'))\n .join('.*');\n return new RegExp(`^${regexStr}$`).test(value);\n}\n\n/** Extract the host from a URL for network-grant matching; falls back to the raw value. */\nfunction hostOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return url;\n }\n}\n\nconst inList = (list: string[] | undefined, value: string): boolean =>\n Array.isArray(list) && list.some((p) => p === value || grantGlobMatch(p, value));\n\n/**\n * Build the runtime {@link PluginPermissions} bag from a structured\n * install-time grant set (ADR-0025 §3.2 `{ services, hooks, network, fs }`).\n *\n * Matching: an entry allows when it equals the requested value, is a glob\n * that matches it, or is the wildcard `*`. Network grants match against the\n * request URL's host (or the raw URL). `fs` governs both read and write —\n * the structured grant set does not split the two. A null/empty grant set\n * denies everything (principle of least privilege).\n */\nexport function buildPermissionsFromGrants(\n granted: GrantedPermissions | null | undefined,\n): PluginPermissions {\n const services = granted?.services;\n const hooks = granted?.hooks;\n const network = granted?.network;\n const fs = granted?.fs;\n return {\n canAccessService: (name) => inList(services, name),\n canTriggerHook: (name) => inList(hooks, name),\n canReadFile: (path) => inList(fs, path),\n canWriteFile: (path) => inList(fs, path),\n canNetworkRequest: (url) =>\n inList(network, hostOf(url)) || inList(network, url),\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n PluginPermission,\n PluginPermissionSet,\n PermissionAction,\n ResourceType\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from '../logger.js';\n\n/**\n * Permission Grant\n * Represents a granted permission at runtime\n */\nexport interface PermissionGrant {\n permissionId: string;\n pluginId: string;\n grantedAt: Date;\n grantedBy?: string;\n expiresAt?: Date;\n conditions?: Record<string, any>;\n}\n\n/**\n * Permission Check Result\n */\nexport interface PermissionCheckResult {\n allowed: boolean;\n reason?: string;\n requiredPermission?: string;\n grantedPermissions?: string[];\n}\n\n/**\n * Plugin Permission Manager\n * \n * Manages fine-grained permissions for plugin security and access control\n */\nexport class PluginPermissionManager {\n private logger: ObjectLogger;\n \n // Plugin permission definitions\n private permissionSets = new Map<string, PluginPermissionSet>();\n \n // Granted permissions (pluginId -> Set of permission IDs)\n private grants = new Map<string, Set<string>>();\n \n // Permission grant details\n private grantDetails = new Map<string, PermissionGrant>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'PermissionManager' });\n }\n\n /**\n * Register permission requirements for a plugin\n */\n registerPermissions(pluginId: string, permissionSet: PluginPermissionSet): void {\n this.permissionSets.set(pluginId, permissionSet);\n \n this.logger.info('Permissions registered for plugin', { \n pluginId,\n permissionCount: permissionSet.permissions.length\n });\n }\n\n /**\n * Grant a permission to a plugin\n */\n grantPermission(\n pluginId: string,\n permissionId: string,\n grantedBy?: string,\n expiresAt?: Date\n ): void {\n // Verify permission exists in plugin's declared permissions\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n throw new Error(`No permissions registered for plugin: ${pluginId}`);\n }\n\n const permission = permissionSet.permissions.find(p => p.id === permissionId);\n if (!permission) {\n throw new Error(`Permission ${permissionId} not declared by plugin ${pluginId}`);\n }\n\n // Create grant\n if (!this.grants.has(pluginId)) {\n this.grants.set(pluginId, new Set());\n }\n this.grants.get(pluginId)!.add(permissionId);\n\n // Store grant details\n const grantKey = `${pluginId}:${permissionId}`;\n this.grantDetails.set(grantKey, {\n permissionId,\n pluginId,\n grantedAt: new Date(),\n grantedBy,\n expiresAt,\n });\n\n this.logger.info('Permission granted', { \n pluginId, \n permissionId,\n grantedBy \n });\n }\n\n /**\n * Revoke a permission from a plugin\n */\n revokePermission(pluginId: string, permissionId: string): void {\n const grants = this.grants.get(pluginId);\n if (grants) {\n grants.delete(permissionId);\n \n const grantKey = `${pluginId}:${permissionId}`;\n this.grantDetails.delete(grantKey);\n\n this.logger.info('Permission revoked', { pluginId, permissionId });\n }\n }\n\n /**\n * Grant all permissions for a plugin\n */\n grantAllPermissions(pluginId: string, grantedBy?: string): void {\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n throw new Error(`No permissions registered for plugin: ${pluginId}`);\n }\n\n for (const permission of permissionSet.permissions) {\n this.grantPermission(pluginId, permission.id, grantedBy);\n }\n\n this.logger.info('All permissions granted', { pluginId, grantedBy });\n }\n\n /**\n * Check if a plugin has a specific permission\n */\n hasPermission(pluginId: string, permissionId: string): boolean {\n const grants = this.grants.get(pluginId);\n if (!grants) {\n return false;\n }\n\n // Check if granted\n if (!grants.has(permissionId)) {\n return false;\n }\n\n // Check expiration\n const grantKey = `${pluginId}:${permissionId}`;\n const grantDetails = this.grantDetails.get(grantKey);\n if (grantDetails?.expiresAt && grantDetails.expiresAt < new Date()) {\n this.revokePermission(pluginId, permissionId);\n return false;\n }\n\n return true;\n }\n\n /**\n * Check if plugin can perform an action on a resource\n */\n checkAccess(\n pluginId: string,\n resource: ResourceType,\n action: PermissionAction,\n resourceId?: string\n ): PermissionCheckResult {\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n return {\n allowed: false,\n reason: 'No permissions registered for plugin',\n };\n }\n\n // Find matching permissions\n const matchingPermissions = permissionSet.permissions.filter(p => {\n // Check resource type\n if (p.resource !== resource) {\n return false;\n }\n\n // Check action\n if (!p.actions.includes(action)) {\n return false;\n }\n\n // Check resource filter if specified\n if (resourceId && p.filter?.resourceIds) {\n if (!p.filter.resourceIds.includes(resourceId)) {\n return false;\n }\n }\n\n return true;\n });\n\n if (matchingPermissions.length === 0) {\n return {\n allowed: false,\n reason: `No permission found for ${action} on ${resource}`,\n };\n }\n\n // Check if any matching permission is granted\n const grantedPermissions = matchingPermissions.filter(p => \n this.hasPermission(pluginId, p.id)\n );\n\n if (grantedPermissions.length === 0) {\n return {\n allowed: false,\n reason: 'Required permissions not granted',\n requiredPermission: matchingPermissions[0].id,\n };\n }\n\n return {\n allowed: true,\n grantedPermissions: grantedPermissions.map(p => p.id),\n };\n }\n\n /**\n * Get all permissions for a plugin\n */\n getPluginPermissions(pluginId: string): PluginPermission[] {\n const permissionSet = this.permissionSets.get(pluginId);\n return permissionSet?.permissions || [];\n }\n\n /**\n * Get granted permissions for a plugin\n */\n getGrantedPermissions(pluginId: string): string[] {\n const grants = this.grants.get(pluginId);\n return grants ? Array.from(grants) : [];\n }\n\n /**\n * Get required but not granted permissions\n */\n getMissingPermissions(pluginId: string): PluginPermission[] {\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n return [];\n }\n\n const granted = this.grants.get(pluginId) || new Set();\n \n return permissionSet.permissions.filter(p => \n p.required && !granted.has(p.id)\n );\n }\n\n /**\n * Check if all required permissions are granted\n */\n hasAllRequiredPermissions(pluginId: string): boolean {\n return this.getMissingPermissions(pluginId).length === 0;\n }\n\n /**\n * Get permission grant details\n */\n getGrantDetails(pluginId: string, permissionId: string): PermissionGrant | undefined {\n const grantKey = `${pluginId}:${permissionId}`;\n return this.grantDetails.get(grantKey);\n }\n\n /**\n * Validate permission against scope constraints\n */\n validatePermissionScope(\n permission: PluginPermission,\n context: {\n tenantId?: string;\n userId?: string;\n resourceId?: string;\n }\n ): boolean {\n switch (permission.scope) {\n case 'global':\n return true;\n\n case 'tenant':\n return !!context.tenantId;\n\n case 'user':\n return !!context.userId;\n\n case 'resource':\n return !!context.resourceId;\n\n case 'plugin':\n return true;\n\n default:\n return false;\n }\n }\n\n /**\n * Clear all permissions for a plugin\n */\n clearPluginPermissions(pluginId: string): void {\n this.permissionSets.delete(pluginId);\n \n const grants = this.grants.get(pluginId);\n if (grants) {\n for (const permissionId of grants) {\n const grantKey = `${pluginId}:${permissionId}`;\n this.grantDetails.delete(grantKey);\n }\n this.grants.delete(pluginId);\n }\n\n this.logger.info('All permissions cleared', { pluginId });\n }\n\n /**\n * Shutdown permission manager\n */\n shutdown(): void {\n this.permissionSets.clear();\n this.grants.clear();\n this.grantDetails.clear();\n \n this.logger.info('Permission manager shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport nodePath from 'node:path';\n\nimport type { \n SandboxConfig\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from '../logger.js';\nimport { getMemoryUsage } from '../utils/env.js';\n\n/**\n * Resource Usage Statistics\n */\nexport interface ResourceUsage {\n memory: {\n current: number;\n peak: number;\n limit?: number;\n };\n cpu: {\n current: number;\n average: number;\n limit?: number;\n };\n connections: {\n current: number;\n limit?: number;\n };\n}\n\n/**\n * Sandbox Execution Context\n * Represents an isolated execution environment for a plugin\n */\nexport interface SandboxContext {\n pluginId: string;\n config: SandboxConfig;\n startTime: Date;\n resourceUsage: ResourceUsage;\n}\n\n/**\n * Plugin Sandbox Runtime\n * \n * Provides isolated execution environments for plugins with resource limits\n * and access controls\n */\nexport class PluginSandboxRuntime {\n private static readonly MONITORING_INTERVAL_MS = 5000;\n\n private logger: ObjectLogger;\n \n // Active sandboxes (pluginId -> context)\n private sandboxes = new Map<string, SandboxContext>();\n \n // Resource monitoring intervals\n private monitoringIntervals = new Map<string, NodeJS.Timeout>();\n\n // Per-plugin resource baselines for delta tracking\n private memoryBaselines = new Map<string, number>();\n private cpuBaselines = new Map<string, { user: number; system: number }>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'SandboxRuntime' });\n }\n\n /**\n * Create a sandbox for a plugin\n */\n createSandbox(pluginId: string, config: SandboxConfig): SandboxContext {\n if (this.sandboxes.has(pluginId)) {\n throw new Error(`Sandbox already exists for plugin: ${pluginId}`);\n }\n\n const context: SandboxContext = {\n pluginId,\n config,\n startTime: new Date(),\n resourceUsage: {\n memory: { current: 0, peak: 0, limit: config.memory?.maxHeap },\n cpu: { current: 0, average: 0, limit: config.cpu?.maxCpuPercent },\n connections: { current: 0, limit: config.network?.maxConnections },\n },\n };\n\n this.sandboxes.set(pluginId, context);\n\n // Capture resource baselines for per-plugin delta tracking\n const baselineMemory = getMemoryUsage();\n this.memoryBaselines.set(pluginId, baselineMemory.heapUsed);\n this.cpuBaselines.set(pluginId, process.cpuUsage());\n\n // Start resource monitoring\n this.startResourceMonitoring(pluginId);\n\n this.logger.info('Sandbox created', { \n pluginId,\n level: config.level,\n memoryLimit: config.memory?.maxHeap,\n cpuLimit: config.cpu?.maxCpuPercent\n });\n\n return context;\n }\n\n /**\n * Destroy a sandbox\n */\n destroySandbox(pluginId: string): void {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return;\n }\n\n // Stop monitoring\n this.stopResourceMonitoring(pluginId);\n\n this.memoryBaselines.delete(pluginId);\n this.cpuBaselines.delete(pluginId);\n this.sandboxes.delete(pluginId);\n\n this.logger.info('Sandbox destroyed', { pluginId });\n }\n\n /**\n * Check if resource access is allowed\n */\n checkResourceAccess(\n pluginId: string,\n resourceType: 'file' | 'network' | 'process' | 'env',\n resourcePath?: string\n ): { allowed: boolean; reason?: string } {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return { allowed: false, reason: 'Sandbox not found' };\n }\n\n const { config } = context;\n\n switch (resourceType) {\n case 'file':\n return this.checkFileAccess(config, resourcePath);\n \n case 'network':\n return this.checkNetworkAccess(config, resourcePath);\n \n case 'process':\n return this.checkProcessAccess(config);\n \n case 'env':\n return this.checkEnvAccess(config, resourcePath);\n \n default:\n return { allowed: false, reason: 'Unknown resource type' };\n }\n }\n\n /**\n * Check file system access\n * Uses path.resolve() and path.normalize() to prevent directory traversal.\n */\n private checkFileAccess(\n config: SandboxConfig,\n filePath?: string\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.filesystem) {\n return { allowed: false, reason: 'File system access not configured' };\n }\n\n // If no path specified, check general access\n if (!filePath) {\n return { allowed: config.filesystem.mode !== 'none' };\n }\n\n // Check allowed paths using proper path resolution to prevent directory traversal\n const allowedPaths = config.filesystem.allowedPaths || [];\n const resolvedPath = nodePath.normalize(nodePath.resolve(filePath));\n const isAllowed = allowedPaths.some(allowed => {\n const resolvedAllowed = nodePath.normalize(nodePath.resolve(allowed));\n return resolvedPath.startsWith(resolvedAllowed);\n });\n\n if (allowedPaths.length > 0 && !isAllowed) {\n return { \n allowed: false, \n reason: `Path not in allowed list: ${filePath}` \n };\n }\n\n // Check denied paths using proper path resolution\n const deniedPaths = config.filesystem.deniedPaths || [];\n const isDenied = deniedPaths.some(denied => {\n const resolvedDenied = nodePath.normalize(nodePath.resolve(denied));\n return resolvedPath.startsWith(resolvedDenied);\n });\n\n if (isDenied) {\n return { \n allowed: false, \n reason: `Path is explicitly denied: ${filePath}` \n };\n }\n\n return { allowed: true };\n }\n\n /**\n * Check network access\n * Uses URL parsing to properly validate hostnames.\n */\n private checkNetworkAccess(\n config: SandboxConfig,\n url?: string\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.network) {\n return { allowed: false, reason: 'Network access not configured' };\n }\n\n // Check if network access is enabled\n if (config.network.mode === 'none') {\n return { allowed: false, reason: 'Network access disabled' };\n }\n\n // If no URL specified, check general access\n if (!url) {\n return { allowed: (config.network.mode as string) !== 'none' };\n }\n\n // Parse URL and check hostname against allowed/denied hosts\n let parsedHostname: string;\n try {\n parsedHostname = new URL(url).hostname;\n } catch {\n return { allowed: false, reason: `Invalid URL: ${url}` };\n }\n\n // Check allowed hosts\n const allowedHosts = config.network.allowedHosts || [];\n if (allowedHosts.length > 0) {\n const isAllowed = allowedHosts.some(host => {\n return parsedHostname === host;\n });\n\n if (!isAllowed) {\n return { \n allowed: false, \n reason: `Host not in allowed list: ${url}` \n };\n }\n }\n\n // Check denied hosts\n const deniedHosts = config.network.deniedHosts || [];\n const isDenied = deniedHosts.some(host => {\n return parsedHostname === host;\n });\n\n if (isDenied) {\n return { \n allowed: false, \n reason: `Host is blocked: ${url}` \n };\n }\n\n return { allowed: true };\n }\n\n /**\n * Check process spawning access\n */\n private checkProcessAccess(\n config: SandboxConfig\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.process) {\n return { allowed: false, reason: 'Process access not configured' };\n }\n\n if (!config.process.allowSpawn) {\n return { allowed: false, reason: 'Process spawning not allowed' };\n }\n\n return { allowed: true };\n }\n\n /**\n * Check environment variable access\n */\n private checkEnvAccess(\n config: SandboxConfig,\n varName?: string\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.process) {\n return { allowed: false, reason: 'Environment access not configured' };\n }\n\n // If no variable specified, check general access\n if (!varName) {\n return { allowed: true };\n }\n\n // For now, allow all env access if process is configured\n // In a real implementation, would check specific allowed vars\n return { allowed: true };\n }\n\n /**\n * Check resource limits\n */\n checkResourceLimits(pluginId: string): { \n withinLimits: boolean; \n violations: string[] \n } {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return { withinLimits: true, violations: [] };\n }\n\n const violations: string[] = [];\n const { resourceUsage, config } = context;\n\n // Check memory limit\n if (config.memory?.maxHeap && \n resourceUsage.memory.current > config.memory.maxHeap) {\n violations.push(`Memory limit exceeded: ${resourceUsage.memory.current} > ${config.memory.maxHeap}`);\n }\n\n // Check CPU limit (would need runtime config)\n if (config.runtime?.resourceLimits?.maxCpu && \n resourceUsage.cpu.current > config.runtime.resourceLimits.maxCpu) {\n violations.push(`CPU limit exceeded: ${resourceUsage.cpu.current}% > ${config.runtime.resourceLimits.maxCpu}%`);\n }\n\n // Check connection limit\n if (config.network?.maxConnections && \n resourceUsage.connections.current > config.network.maxConnections) {\n violations.push(`Connection limit exceeded: ${resourceUsage.connections.current} > ${config.network.maxConnections}`);\n }\n\n return {\n withinLimits: violations.length === 0,\n violations,\n };\n }\n\n /**\n * Get resource usage for a plugin\n */\n getResourceUsage(pluginId: string): ResourceUsage | undefined {\n const context = this.sandboxes.get(pluginId);\n return context?.resourceUsage;\n }\n\n /**\n * Start monitoring resource usage\n */\n private startResourceMonitoring(pluginId: string): void {\n // Monitor at the configured interval\n const interval = setInterval(() => {\n this.updateResourceUsage(pluginId);\n }, PluginSandboxRuntime.MONITORING_INTERVAL_MS);\n\n this.monitoringIntervals.set(pluginId, interval);\n }\n\n /**\n * Stop monitoring resource usage\n */\n private stopResourceMonitoring(pluginId: string): void {\n const interval = this.monitoringIntervals.get(pluginId);\n if (interval) {\n clearInterval(interval);\n this.monitoringIntervals.delete(pluginId);\n }\n }\n\n /**\n * Update resource usage statistics\n * \n * Tracks per-plugin memory and CPU usage using delta from baseline\n * captured at sandbox creation time. This is an approximation since\n * true per-plugin isolation isn't possible in a single Node.js process.\n */\n private updateResourceUsage(pluginId: string): void {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return;\n }\n\n // In a real implementation, this would collect actual metrics\n // For now, this is a placeholder structure\n \n // Update memory usage using delta from baseline for per-plugin approximation\n const memoryUsage = getMemoryUsage();\n const memoryBaseline = this.memoryBaselines.get(pluginId) ?? 0;\n const memoryDelta = Math.max(0, memoryUsage.heapUsed - memoryBaseline);\n context.resourceUsage.memory.current = memoryDelta;\n context.resourceUsage.memory.peak = Math.max(\n context.resourceUsage.memory.peak,\n memoryDelta\n );\n\n // Update CPU usage using delta from baseline for per-plugin approximation\n const cpuBaseline = this.cpuBaselines.get(pluginId) ?? { user: 0, system: 0 };\n const cpuCurrent = process.cpuUsage();\n const cpuDeltaUser = cpuCurrent.user - cpuBaseline.user;\n const cpuDeltaSystem = cpuCurrent.system - cpuBaseline.system;\n // Convert microseconds to a percentage approximation over the monitoring interval\n const totalCpuMicros = cpuDeltaUser + cpuDeltaSystem;\n const intervalMicros = PluginSandboxRuntime.MONITORING_INTERVAL_MS * 1000;\n context.resourceUsage.cpu.current = (totalCpuMicros / intervalMicros) * 100;\n // Update baseline for next interval\n this.cpuBaselines.set(pluginId, cpuCurrent);\n\n // Check for violations\n const { withinLimits, violations } = this.checkResourceLimits(pluginId);\n if (!withinLimits) {\n this.logger.warn('Resource limit violations detected', { \n pluginId, \n violations \n });\n }\n }\n\n /**\n * Get all active sandboxes\n */\n getAllSandboxes(): Map<string, SandboxContext> {\n return new Map(this.sandboxes);\n }\n\n /**\n * Shutdown sandbox runtime\n */\n shutdown(): void {\n // Stop all monitoring\n for (const pluginId of this.monitoringIntervals.keys()) {\n this.stopResourceMonitoring(pluginId);\n }\n\n this.sandboxes.clear();\n this.memoryBaselines.clear();\n this.cpuBaselines.clear();\n \n this.logger.info('Sandbox runtime shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n KernelSecurityVulnerability,\n KernelSecurityScanResult\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from '../logger.js';\n\n/**\n * Scan Target\n */\nexport interface ScanTarget {\n pluginId: string;\n version: string;\n files?: string[];\n dependencies?: Record<string, string>;\n}\n\n/**\n * Security Issue\n */\nexport interface SecurityIssue {\n id: string;\n severity: 'critical' | 'high' | 'medium' | 'low' | 'info';\n category: 'vulnerability' | 'malware' | 'license' | 'code-quality' | 'configuration';\n title: string;\n description: string;\n location?: {\n file?: string;\n line?: number;\n column?: number;\n };\n remediation?: string;\n cve?: string;\n cvss?: number;\n}\n\n/**\n * Plugin Security Scanner\n * \n * Scans plugins for security vulnerabilities, malware, and license issues\n */\nexport class PluginSecurityScanner {\n private logger: ObjectLogger;\n \n // Known vulnerabilities database (CVE cache)\n private vulnerabilityDb = new Map<string, KernelSecurityVulnerability>();\n \n // Scan results cache\n private scanResults = new Map<string, KernelSecurityScanResult>();\n\n private passThreshold: number = 70;\n\n constructor(logger: ObjectLogger, config?: { passThreshold?: number }) {\n this.logger = logger.child({ component: 'SecurityScanner' });\n if (config?.passThreshold !== undefined) {\n this.passThreshold = config.passThreshold;\n }\n }\n\n /**\n * Perform a comprehensive security scan on a plugin\n */\n async scan(target: ScanTarget): Promise<KernelSecurityScanResult> {\n this.logger.info('Starting security scan', { \n pluginId: target.pluginId,\n version: target.version \n });\n\n const issues: SecurityIssue[] = [];\n\n try {\n // 1. Scan for code vulnerabilities\n const codeIssues = await this.scanCode(target);\n issues.push(...codeIssues);\n\n // 2. Scan dependencies for known vulnerabilities\n const depIssues = await this.scanDependencies(target);\n issues.push(...depIssues);\n\n // 3. Scan for malware patterns\n const malwareIssues = await this.scanMalware(target);\n issues.push(...malwareIssues);\n\n // 4. Check license compliance\n const licenseIssues = await this.scanLicenses(target);\n issues.push(...licenseIssues);\n\n // 5. Check configuration security\n const configIssues = await this.scanConfiguration(target);\n issues.push(...configIssues);\n\n // Calculate security score (0-100, higher is better)\n const score = this.calculateSecurityScore(issues);\n\n const result: KernelSecurityScanResult = {\n timestamp: new Date().toISOString(),\n scanner: { name: 'ObjectStack Security Scanner', version: '1.0.0' },\n status: score >= this.passThreshold ? 'passed' : 'failed',\n vulnerabilities: issues.map(issue => ({\n id: issue.id,\n severity: issue.severity,\n category: issue.category,\n title: issue.title,\n description: issue.description,\n location: issue.location ? `${issue.location.file}:${issue.location.line}` : undefined,\n remediation: issue.remediation,\n affectedVersions: [],\n exploitAvailable: false,\n patchAvailable: false,\n })),\n summary: {\n totalVulnerabilities: issues.length,\n criticalCount: issues.filter(i => i.severity === 'critical').length,\n highCount: issues.filter(i => i.severity === 'high').length,\n mediumCount: issues.filter(i => i.severity === 'medium').length,\n lowCount: issues.filter(i => i.severity === 'low').length,\n infoCount: issues.filter(i => i.severity === 'info').length,\n },\n };\n\n this.scanResults.set(`${target.pluginId}:${target.version}`, result);\n\n this.logger.info('Security scan complete', { \n pluginId: target.pluginId,\n score,\n status: result.status,\n summary: result.summary\n });\n\n return result;\n } catch (error) {\n this.logger.error('Security scan failed', { \n pluginId: target.pluginId, \n error \n });\n\n throw error;\n }\n }\n\n /**\n * Scan code for vulnerabilities\n */\n private async scanCode(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n // In a real implementation, this would:\n // - Parse code with AST (e.g., using @typescript-eslint/parser)\n // - Check for dangerous patterns (eval, Function constructor, etc.)\n // - Check for XSS vulnerabilities\n // - Check for SQL injection patterns\n // - Check for insecure crypto usage\n // - Check for path traversal vulnerabilities\n\n this.logger.debug('Code scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Scan dependencies for known vulnerabilities\n */\n private async scanDependencies(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n if (!target.dependencies) {\n return issues;\n }\n\n // In a real implementation, this would:\n // - Query npm audit API\n // - Check GitHub Advisory Database\n // - Check Snyk vulnerability database\n // - Check OSV (Open Source Vulnerabilities)\n\n for (const [depName, version] of Object.entries(target.dependencies)) {\n const vulnKey = `${depName}@${version}`;\n const vulnerability = this.vulnerabilityDb.get(vulnKey);\n\n if (vulnerability) {\n issues.push({\n id: `vuln-${vulnerability.cve || depName}`,\n severity: vulnerability.severity,\n category: 'vulnerability',\n title: `Vulnerable dependency: ${depName}`,\n description: `${depName}@${version} has known security vulnerabilities`,\n remediation: vulnerability.fixedIn \n ? `Upgrade to ${vulnerability.fixedIn.join(' or ')}`\n : 'No fix available',\n cve: vulnerability.cve,\n });\n }\n }\n\n this.logger.debug('Dependency scan complete', { \n pluginId: target.pluginId,\n dependencies: Object.keys(target.dependencies).length,\n vulnerabilities: issues.length \n });\n\n return issues;\n }\n\n /**\n * Scan for malware patterns\n */\n private async scanMalware(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n // In a real implementation, this would:\n // - Check for obfuscated code\n // - Check for suspicious network activity patterns\n // - Check for crypto mining patterns\n // - Check for data exfiltration patterns\n // - Use ML-based malware detection\n // - Check file hashes against known malware databases\n\n this.logger.debug('Malware scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Check license compliance\n */\n private async scanLicenses(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n if (!target.dependencies) {\n return issues;\n }\n\n // In a real implementation, this would:\n // - Check license compatibility\n // - Detect GPL contamination\n // - Flag proprietary dependencies\n // - Check for missing licenses\n // - Verify SPDX identifiers\n\n this.logger.debug('License scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Check configuration security\n */\n private async scanConfiguration(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n // In a real implementation, this would:\n // - Check for hardcoded secrets\n // - Check for weak permissions\n // - Check for insecure defaults\n // - Check for missing security headers\n // - Check CSP policies\n\n this.logger.debug('Configuration scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Calculate security score based on issues\n */\n private calculateSecurityScore(issues: SecurityIssue[]): number {\n // Start with perfect score\n let score = 100;\n\n // Deduct points based on severity\n for (const issue of issues) {\n switch (issue.severity) {\n case 'critical':\n score -= 20;\n break;\n case 'high':\n score -= 10;\n break;\n case 'medium':\n score -= 5;\n break;\n case 'low':\n score -= 2;\n break;\n case 'info':\n score -= 0;\n break;\n }\n }\n\n // Ensure score doesn't go below 0\n return Math.max(0, score);\n }\n\n /**\n * Add a vulnerability to the database\n */\n addVulnerability(\n packageName: string,\n version: string,\n vulnerability: KernelSecurityVulnerability\n ): void {\n const key = `${packageName}@${version}`;\n this.vulnerabilityDb.set(key, vulnerability);\n \n this.logger.debug('Vulnerability added to database', { \n package: packageName, \n version,\n cve: vulnerability.cve \n });\n }\n\n /**\n * Get scan result from cache\n */\n getScanResult(pluginId: string, version: string): KernelSecurityScanResult | undefined {\n return this.scanResults.get(`${pluginId}:${version}`);\n }\n\n /**\n * Clear scan results cache\n */\n clearCache(): void {\n this.scanResults.clear();\n this.logger.debug('Scan results cache cleared');\n }\n\n /**\n * Update vulnerability database from external source\n */\n async updateVulnerabilityDatabase(): Promise<void> {\n this.logger.info('Updating vulnerability database');\n\n // In a real implementation, this would:\n // - Fetch from GitHub Advisory Database\n // - Fetch from npm audit\n // - Fetch from NVD (National Vulnerability Database)\n // - Parse and cache vulnerability data\n\n this.logger.info('Vulnerability database updated', { \n entries: this.vulnerabilityDb.size \n });\n }\n\n /**\n * Shutdown security scanner\n */\n shutdown(): void {\n this.vulnerabilityDb.clear();\n this.scanResults.clear();\n \n this.logger.info('Security scanner shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * api-key — hand-rolled API-key primitives + verifier for `sys_api_key`.\n *\n * better-auth 1.6.x ships no apiKey plugin, so ObjectStack owns the full\n * lifecycle: generation, at-rest hashing, header extraction, validation, and\n * the verify-time principal lookup. This is the SINGLE shared source of truth\n * used by BOTH inbound surfaces — the runtime dispatcher / MCP path\n * (`resolveExecutionContext`) and the REST data API (`@objectstack/rest`) — so\n * the two can never drift on how a key authenticates. It lives in\n * `@objectstack/core` (server-side; both `runtime` and `rest` depend on it,\n * and `core` depends on neither, so there is no cycle).\n *\n * SECURITY (zero-tolerance):\n * - The raw key is returned EXACTLY ONCE, by {@link generateApiKey}. It is\n * never persisted; only `sha256(raw)` (hex) is stored in `sys_api_key.key`.\n * - The raw key and its hash must never enter logs, HTTP responses, error\n * messages, commit messages or comments.\n * - Validation is fail-closed: anything ambiguous (missing, revoked, expired,\n * malformed) resolves to \"no principal\", never to an elevated one.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\n\n/** Default visible prefix for generated keys (helps users identify a key). */\nexport const API_KEY_PREFIX = 'osk_';\n\n/** Bytes of entropy in the secret portion of a generated key (256 bits). */\nconst API_KEY_ENTROPY_BYTES = 32;\n\n/** Length of the human-visible prefix stored in `sys_api_key.prefix`. */\nconst VISIBLE_PREFIX_LEN = 12;\n\n/**\n * Derive the at-rest hash for an API key. Inbound keys are hashed the same way\n * before the DB lookup. Because the lookup matches an indexed, high-entropy\n * hash exactly, this doubles as a constant-effort comparison: an attacker\n * cannot recover the raw key by probing for partial matches.\n */\nexport function hashApiKey(raw: string): string {\n return createHash('sha256').update(raw, 'utf8').digest('hex');\n}\n\n/** Result of {@link generateApiKey}. `raw` is shown to the user only once. */\nexport interface GeneratedApiKey {\n /** The full secret to hand to the client. NEVER persist this. */\n raw: string;\n /** `sha256(raw)` hex — store this in `sys_api_key.key`. */\n hash: string;\n /** Short non-secret prefix for display/identification (`sys_api_key.prefix`). */\n prefix: string;\n}\n\n/**\n * Generate a fresh API key. Returns the raw secret (caller must surface it to\n * the user exactly once and then discard it), its at-rest hash, and a short\n * non-secret prefix for display.\n */\nexport function generateApiKey(prefix: string = API_KEY_PREFIX): GeneratedApiKey {\n // base64url so the token is URL/header-safe with no padding.\n const secret = randomBytes(API_KEY_ENTROPY_BYTES).toString('base64url');\n const raw = `${prefix}${secret}`;\n return {\n raw,\n hash: hashApiKey(raw),\n prefix: raw.slice(0, VISIBLE_PREFIX_LEN),\n };\n}\n\n/**\n * Extract an API key from request headers. Accepts, in order:\n * - `X-API-Key: <token>`\n * - `Authorization: ApiKey <token>` (case-insensitive scheme)\n * - `Authorization: Bearer <token>` ONLY when `<token>` carries the ObjectStack\n * api-key prefix (`osk_`). Remote MCP clients (Claude Desktop / Cursor /\n * Claude Code) authenticate to `/api/v1/mcp` with the key as a Bearer per the\n * MCP spec, so rejecting Bearer outright made every standard MCP client fail.\n * A better-auth *session* token never starts with `osk_`, so a session Bearer\n * still falls through to the session path — this can't shadow it.\n */\nexport function extractApiKey(headers: any): string | undefined {\n const x = readHeader(headers, 'x-api-key');\n if (x && x.trim()) return x.trim();\n const auth = readHeader(headers, 'authorization');\n if (!auth) return undefined;\n const apiKeyScheme = auth.match(/^ApiKey\\s+(\\S.*)$/i);\n if (apiKeyScheme?.[1]?.trim()) return apiKeyScheme[1].trim();\n // Bearer is accepted only for prefixed api-keys (never for session tokens).\n const bearer = auth.match(/^Bearer\\s+(\\S.*)$/i)?.[1]?.trim();\n if (bearer && bearer.startsWith(API_KEY_PREFIX)) return bearer;\n return undefined;\n}\n\n/** Parse a `scopes` value that may be a JSON-string textarea or a real array. */\nexport function parseScopes(value: unknown): string[] {\n if (Array.isArray(value)) {\n return value.filter((s): s is string => typeof s === 'string' && s.length > 0);\n }\n if (typeof value === 'string' && value.trim()) {\n const parsed = safeJsonParse<unknown>(value, []);\n if (Array.isArray(parsed)) {\n return parsed.filter((s): s is string => typeof s === 'string' && s.length > 0);\n }\n }\n return [];\n}\n\n/** Return true when an expiry timestamp is in the past (i.e. the key is dead). */\nexport function isExpired(value: unknown, nowMs: number): boolean {\n if (value == null) return false;\n let ms: number;\n if (typeof value === 'number') {\n // Heuristic: seconds vs milliseconds epoch.\n ms = value < 1e12 ? value * 1000 : value;\n } else if (value instanceof Date) {\n ms = value.getTime();\n } else if (typeof value === 'string') {\n ms = Date.parse(value);\n } else {\n return false;\n }\n if (Number.isNaN(ms)) return false;\n return ms <= nowMs;\n}\n\n/** The principal resolved from a valid `sys_api_key`. */\nexport interface ApiKeyPrincipal {\n userId: string;\n tenantId?: string;\n scopes: string[];\n}\n\n/**\n * Verify an inbound API key against `sys_api_key` and resolve its principal.\n * This is the ONE verify path shared by the dispatcher/MCP and REST surfaces.\n *\n * Fail-closed: returns `undefined` for a missing key, an unusable data engine,\n * a lookup error, or a key that is unknown / revoked / expired / owner-less.\n *\n * @param ql A data engine with `find(object, { where, limit, context })`.\n * @param headers Request headers (Web `Headers` or a plain object).\n * @param nowMs Clock for expiry checks (injectable for tests).\n */\nexport async function resolveApiKeyPrincipal(\n ql: any,\n headers: any,\n nowMs: number = Date.now(),\n): Promise<ApiKeyPrincipal | undefined> {\n const apiKey = extractApiKey(headers);\n if (!apiKey) return undefined;\n if (!ql || typeof ql.find !== 'function') return undefined;\n\n // Match by the indexed at-rest hash only — never query by the raw key.\n let rows: any;\n try {\n rows = await ql.find('sys_api_key', {\n where: { key: hashApiKey(apiKey), revoked: false },\n limit: 1,\n context: { isSystem: true },\n });\n } catch {\n return undefined;\n }\n if (rows && (rows as any).value) rows = (rows as any).value;\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || row.revoked === true) return undefined;\n\n const expiresAt = row.expires_at ?? row.expiresAt;\n if (isExpired(expiresAt, nowMs)) return undefined;\n\n const userId = row.user_id ?? row.userId;\n if (!userId || typeof userId !== 'string') return undefined;\n\n return {\n userId,\n tenantId: row.organization_id ?? row.organizationId ?? undefined,\n scopes: parseScopes(row.scopes),\n };\n}\n\nfunction readHeader(headers: any, name: string): string | undefined {\n if (!headers) return undefined;\n const lower = name.toLowerCase();\n if (typeof headers.get === 'function') {\n const v = headers.get(name) ?? headers.get(lower);\n return v == null ? undefined : String(v);\n }\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === lower) {\n const v = headers[key];\n return Array.isArray(v) ? v[0] : v == null ? undefined : String(v);\n }\n }\n return undefined;\n}\n\nfunction safeJsonParse<T>(s: string, fallback: T): T {\n try {\n return JSON.parse(s) as T;\n } catch {\n return fallback;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * resolveAuthzContext — the SINGLE source of truth for resolving an inbound\n * request's identity + authorization context (positions, permissions, RLS scoping).\n *\n * Every HTTP entry point (REST server, runtime dispatcher, MCP, any future\n * transport) MUST resolve authorization through this function — never by\n * re-reading `sys_member` / `sys_user_position` / `sys_*_permission_set` itself.\n *\n * Why this exists: authorization resolution used to be DUPLICATED across the\n * REST server (`@objectstack/rest`) and the runtime dispatcher\n * (`@objectstack/runtime`). On a security path, duplicated logic drifts and the\n * drift is silent: the REST copy had quietly omitted `sys_user_position` (so custom\n * roles granted via the ADR-0057 D4 platform-RBAC path didn't apply over REST),\n * `sys_position_permission_set`, `mapMembershipRole` normalization, the\n * platform-admin derivation, and the `ai_seat` synthesis. The API-key half was\n * already shared here (`resolveApiKeyPrincipal`); this completes the extraction\n * by bringing session + role/permission aggregation home too. There is now ONE\n * implementation; both entry points are thin adapters that supply `ql` /\n * `getSession` their own way and delegate here.\n *\n * Fail-closed: every read is defensive. Missing services / tables yield a\n * partial context (even `{ positions: [], permissions: [] }`) — enforcement is the\n * SecurityPlugin's job, never this resolver's.\n */\n\nimport {\n mapMembershipRole,\n BUILTIN_IDENTITY_PLATFORM_ADMIN,\n ADMIN_FULL_ACCESS,\n ORGANIZATION_ADMIN_GRANTS,\n} from '@objectstack/spec';\nimport type { AuthzPosture } from '@objectstack/spec/security';\n\nimport { resolveApiKeyPrincipal } from './api-key.js';\nimport { isGrantActive } from './grant-validity.js';\nimport { derivePosture } from './posture-ladder.js';\n\n/** The transport-agnostic authorization envelope produced from a request. */\nexport interface ResolvedAuthzContext {\n userId?: string;\n tenantId?: string;\n email?: string;\n accessToken?: string;\n positions: string[];\n permissions: string[];\n systemPermissions: string[];\n tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;\n /** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */\n org_user_ids: string[];\n /**\n * [ADR-0105 D2] Every organization this principal currently holds a VALID\n * membership in — the caller's org access set, and the read reach of the\n * `group` tenancy posture (Layer 0 becomes `organization_id IN (...)`).\n * Resolved here, once, so no surface re-derives it; empty for an anonymous or\n * membership-less principal, which fails the group wall closed.\n */\n accessible_org_ids: string[];\n /**\n * [ADR-0095 D2/D3] The monotonic posture rung this principal resolves to,\n * DERIVED once here from held capability grants (never a better-auth role):\n * `PLATFORM_ADMIN` (unscoped `admin_full_access`) > `TENANT_ADMIN`\n * (`organization_admin`) > `MEMBER` (the authenticated floor). `EXTERNAL` is\n * defined/test-locked but never resolved yet (no external principal type —\n * see `posture-ladder.ts`). Present only for an authenticated principal;\n * anonymous requests carry no rung.\n */\n posture?: AuthzPosture;\n}\n\nexport interface ResolveAuthzInput {\n /** Data engine (ObjectQL) exposing `find(object, { where, limit, context })`. */\n ql: any;\n /** Inbound request headers (Web `Headers` or a plain record). */\n headers: any;\n /**\n * Resolve a better-auth session from `headers`, returning `{ user?, session? }`\n * (or undefined). Optional — when omitted or throwing, only the API-key path\n * runs and anonymous requests resolve to an empty context.\n */\n getSession?: (headers: any) => Promise<any> | any;\n /** Clock injection for API-key expiry (tests). */\n nowMs?: number;\n}\n\nfunction safeJsonParse<T>(s: string, fallback: T): T {\n try { return JSON.parse(s) as T; } catch { return fallback; }\n}\n\nasync function tryFind(ql: any, object: string, where: any, limit = 100): Promise<any[]> {\n if (!ql || typeof ql.find !== 'function') return [];\n try {\n let rows = await ql.find(object, { where, limit, context: { isSystem: true } } as any);\n if (rows && (rows as any).value) rows = (rows as any).value;\n return Array.isArray(rows) ? rows : [];\n } catch {\n return [];\n }\n}\n\n/**\n * Resolve the authorization context for an inbound request. Always resolves —\n * never throws. Anonymous requests yield `{ positions: [], permissions: [], ... }`.\n */\nexport async function resolveAuthzContext(input: ResolveAuthzInput): Promise<ResolvedAuthzContext> {\n const { ql, headers } = input;\n const ctx: ResolvedAuthzContext = {\n positions: [],\n permissions: [],\n systemPermissions: [],\n org_user_ids: [],\n accessible_org_ids: [],\n };\n\n let userId: string | undefined;\n let tenantId: string | undefined;\n\n // 1. API key (explicit opt-in via header) takes precedence over session.\n const keyPrincipal = await resolveApiKeyPrincipal(ql, headers, input.nowMs);\n if (keyPrincipal) {\n userId = keyPrincipal.userId;\n tenantId = keyPrincipal.tenantId;\n for (const scope of keyPrincipal.scopes) {\n if (!ctx.permissions.includes(scope)) ctx.permissions.push(scope);\n }\n }\n\n // 2. Session / Bearer path — fall back when no API key resolved a user.\n if (!userId && typeof input.getSession === 'function') {\n try {\n const sessionData = await input.getSession(headers);\n userId = sessionData?.user?.id ?? sessionData?.session?.userId;\n tenantId = tenantId ?? sessionData?.session?.activeOrganizationId;\n ctx.accessToken = sessionData?.session?.token ?? ctx.accessToken;\n if (sessionData?.user?.email) ctx.email = String(sessionData.user.email);\n } catch {\n // no auth configured / bad session → anonymous\n }\n }\n\n if (!userId) return ctx;\n ctx.userId = userId;\n if (tenantId) ctx.tenantId = tenantId;\n if (!ql || typeof ql.find !== 'function') return ctx;\n\n // The principal is now known — delegate ALL position/permission/RLS\n // aggregation to the shared userId-driven resolver. Seed it with the API-key\n // scopes already collected (step 1) and any session-supplied email so the\n // resulting order + email fallback are byte-identical to the logic this\n // replaced. `resolveUserAuthzGrants` is the single place that reads\n // `sys_member` / `sys_user_position` / `sys_*_permission_set`, so a non-HTTP\n // surface that already knows the user id (a `runAs:'user'` automation run,\n // #3356) can build the SAME envelope without re-implementing any of it.\n const grants = await resolveUserAuthzGrants(ql, userId, {\n tenantId,\n nowMs: input.nowMs,\n seedPermissions: ctx.permissions,\n seedEmail: ctx.email,\n });\n ctx.positions = grants.positions;\n ctx.permissions = grants.permissions;\n ctx.systemPermissions = grants.systemPermissions;\n ctx.org_user_ids = grants.org_user_ids;\n ctx.accessible_org_ids = grants.accessible_org_ids;\n if (grants.tabPermissions) ctx.tabPermissions = grants.tabPermissions;\n if (grants.posture) ctx.posture = grants.posture;\n if (grants.email && !ctx.email) ctx.email = grants.email;\n\n return ctx;\n}\n\n/** The authorization grants a KNOWN user holds — a subset of {@link ResolvedAuthzContext}. */\nexport interface UserAuthzGrants {\n positions: string[];\n permissions: string[];\n systemPermissions: string[];\n /** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */\n org_user_ids: string[];\n /** [ADR-0105 D2] Organizations this user holds a currently-valid membership in. */\n accessible_org_ids: string[];\n tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;\n posture?: AuthzPosture;\n /** The user's unique email (`sys_user`), for `current_user.email` owner RLS. */\n email?: string;\n}\n\nexport interface ResolveUserAuthzGrantsOptions {\n /** Active org/tenant id — scopes org-bound grants (a null-org row is global). */\n tenantId?: string;\n /** Clock injection for grant validity windows (tests). */\n nowMs?: number;\n /**\n * Permission names the CALLER already resolved (e.g. API-key scopes) to seed\n * `permissions` BEFORE permission-set names are appended, so a mixed\n * API-key+session principal keeps every scope and the ordering is preserved.\n * Copied, never mutated.\n */\n seedPermissions?: string[];\n /** A caller-supplied email (e.g. from the session) that wins over the `sys_user` read. */\n seedEmail?: string;\n}\n\n/**\n * resolveUserAuthzGrants — the userId-driven core of {@link resolveAuthzContext}.\n *\n * Given a KNOWN user id, aggregate the authorization grants that user holds:\n * org-admin positions (`sys_member`), platform-RBAC positions\n * (`sys_user_position`), user- and position-bound permission sets\n * (`sys_user_permission_set` / `sys_position_permission_set` →\n * `sys_permission_set`), the derived `platform_admin` built-in + posture rung,\n * fellow-org peers for identity-table RLS, and the env-side `ai_seat`.\n *\n * Factored out of `resolveAuthzContext` so a surface that already knows WHO the\n * principal is — with no HTTP request to resolve it from — can build the SAME\n * envelope through the ONE resolver, instead of re-reading `sys_member` /\n * `sys_user_position` / `sys_*_permission_set` itself. The motivating consumer\n * is a `runAs:'user'` automation run resolving the triggering user's grants\n * (#3356): the record-change hook session carries only a `userId`, so the\n * automation engine calls this to run the flow's data ops exactly as that user\n * — not the bare member/everyone fallback the missing grants used to leave it.\n *\n * Fail-closed like its parent: every read is defensive, a missing engine/table\n * yields an empty-but-valid envelope, and it never throws.\n */\nexport async function resolveUserAuthzGrants(\n ql: any,\n userId: string,\n opts: ResolveUserAuthzGrantsOptions = {},\n): Promise<UserAuthzGrants> {\n const { tenantId } = opts;\n const grants: UserAuthzGrants = {\n positions: [],\n permissions: Array.isArray(opts.seedPermissions) ? [...opts.seedPermissions] : [],\n systemPermissions: [],\n org_user_ids: [userId],\n accessible_org_ids: [],\n };\n if (opts.seedEmail) grants.email = opts.seedEmail;\n if (!ql || typeof ql.find !== 'function') return grants;\n\n // sys_user is needed for both the `current_user.email` fallback (API-key auth,\n // where the session didn't supply an email) and the ai_seat synthesis below.\n // Read the row at most once per resolution — the two reads were a duplicate\n // query on the API-key path.\n let userRowLoaded = false;\n let userRow: any;\n const getUserRow = async (): Promise<any> => {\n if (!userRowLoaded) {\n userRowLoaded = true;\n const rows = await tryFind(ql, 'sys_user', { id: userId }, 1);\n userRow = rows[0];\n }\n return userRow;\n };\n\n // Resolve the caller's unique email for `current_user.email` RLS owner\n // policies when the caller didn't supply it (e.g. API-key auth).\n if (!grants.email) {\n const u = await getUserRow();\n if (u?.email) grants.email = String(u.email);\n }\n\n // Single clock for every validity-window check in this resolution\n // (ADR-0091 D2 — a grant row outside [valid_from, valid_until) does not\n // resolve, fail-closed, with no background job involved).\n const nowMs = opts.nowMs ?? Date.now();\n\n // 3. Memberships via sys_member (better-auth). ONE read serves two purposes,\n // so the two facts can never disagree about what the user belongs to:\n //\n // (a) [ADR-0095 D3] Org-administration roles for the ACTIVE organization,\n // normalized to the canonical built-in names (owner→org_owner,\n // admin→org_admin, …). This is the ONE PROVISIONING boundary where a\n // better-auth role is read: it is projected into `positions` here, and\n // separately drives the `organization_admin` capability grant\n // (auto-org-admin-grant.ts). No enforcement code path reads the raw\n // role — posture/adjudication run off the resulting capability grants,\n // so the #2836 dual-track cannot recur.\n //\n // (b) [ADR-0105 D2] `accessible_org_ids` — EVERY organization the user\n // currently belongs to, regardless of which one is active. This is the\n // `group` posture's read reach (Layer 0 becomes `organization_id IN\n // (...)`), so it must span the whole membership set, not the active\n // org. Rows outside their ADR-0091 validity window do not resolve; the\n // columns are absent on `sys_member` today, and `isGrantActive` treats\n // an absent bound as unbounded, so this is a no-op until they exist and\n // correct the moment they do.\n const members = await tryFind(ql, 'sys_member', { user_id: userId }, 200);\n const accessibleOrgIds = new Set<string>();\n for (const m of members) {\n if (!isGrantActive(m, nowMs)) continue;\n const org = m.organization_id ?? m.organizationId;\n if (typeof org === 'string' && org) accessibleOrgIds.add(org);\n }\n grants.accessible_org_ids = Array.from(accessibleOrgIds);\n\n // Positions come from the ACTIVE org's membership only (unchanged): a role\n // held in one organization must not grant its capabilities while the caller\n // operates in another. With no active org, every membership contributes —\n // exactly the pre-D2 behavior of the org-less read.\n const activeMembers = tenantId\n ? members.filter((m) => (m.organization_id ?? m.organizationId) === tenantId)\n : members;\n for (const m of activeMembers) {\n if (m.role && typeof m.role === 'string') {\n for (const raw of m.role.split(',').map((s: string) => s.trim()).filter(Boolean)) {\n const r = mapMembershipRole(raw);\n if (!grants.positions.includes(r)) grants.positions.push(r);\n }\n }\n }\n\n // 4. [ADR-0057 D4] Platform-owned RBAC role assignments (sys_user_position) — the\n // source of truth for custom roles, decoupled from sys_member.role.\n // `organization_id = null` = global (cross-tenant); else match active org.\n const userPositionRows = await tryFind(ql, 'sys_user_position', { user_id: userId }, 200);\n for (const ur of userPositionRows) {\n const org = ur.organization_id ?? null;\n if (org && tenantId && org !== tenantId) continue;\n if (!isGrantActive(ur, nowMs)) continue;\n const r = ur.position;\n if (typeof r === 'string' && r && !grants.positions.includes(r)) grants.positions.push(r);\n }\n\n // 5. Fellow-org user IDs so RLS can scope identity tables to collaborators.\n if (tenantId) {\n const orgMembers = await tryFind(ql, 'sys_member', { organization_id: tenantId }, 1000);\n const ids = new Set<string>(\n orgMembers\n .map((m) => m.user_id ?? m.userId)\n .filter((v): v is string => typeof v === 'string' && v.length > 0),\n );\n ids.add(userId);\n grants.org_user_ids = Array.from(ids);\n }\n\n // 6. Permission sets — user-scoped grants (null org = global, else active org).\n // Rows outside their validity window are dropped BEFORE any derivation, so\n // an expired admin_full_access grant cannot yield platform_admin either.\n const upsRowsAll = await tryFind(ql, 'sys_user_permission_set', { user_id: userId }, 100);\n const upsRows = upsRowsAll.filter((r) => isGrantActive(r, nowMs));\n const psIds = new Set<string>(\n upsRows\n .filter((r) => {\n const org = (r.organization_id ?? r.organizationId) ?? null;\n return !(org && tenantId && org !== tenantId);\n })\n .map((r) => r.permission_set_id ?? r.permissionSetId)\n .filter(Boolean),\n );\n // platform_admin (ADR-0068 D2) is DERIVED from an UNSCOPED admin_full_access\n // USER grant — the single source of truth (no trusted stored boolean).\n const unscopedUserPsIds = new Set<string>(\n upsRows\n .filter((r) => ((r.organization_id ?? r.organizationId) ?? null) === null)\n .map((r) => r.permission_set_id ?? r.permissionSetId)\n .filter(Boolean),\n );\n let hasPlatformAdminGrant = false;\n\n // 5b. [ADR-0090 D5] Audience anchor: every AUTHENTICATED member implicitly\n // holds the built-in `everyone` position, so sets bound to it resolve\n // below exactly like any other position-bound grant — ADDITIVE, with no\n // \"only when the user has nothing else\" cliff.\n if (!grants.positions.includes('everyone')) grants.positions.push('everyone');\n\n // 6a. Position-bound permission sets (sys_position_permission_set): a position\n // carries its permission sets.\n if (grants.positions.length > 0) {\n const positionRows = await tryFind(ql, 'sys_position', { name: { $in: grants.positions } }, 100);\n const positionIds = positionRows.map((r) => r.id).filter(Boolean);\n if (positionIds.length > 0) {\n const rpsRows = await tryFind(ql, 'sys_position_permission_set', { position_id: { $in: positionIds } }, 500);\n for (const r of rpsRows) {\n const id = r.permission_set_id ?? r.permissionSetId;\n if (id) psIds.add(id);\n }\n }\n }\n\n // 6b. Resolve permission-set details (names → grants.permissions; system_permissions;\n // tab_permissions merged by highest visibility).\n if (psIds.size > 0) {\n const psRows = await tryFind(ql, 'sys_permission_set', { id: { $in: Array.from(psIds) } }, 500);\n const tabRank: Record<string, number> = { hidden: 0, default_off: 1, default_on: 2, visible: 3 };\n const mergedTabs: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'> = {};\n for (const ps of psRows) {\n if (ps.name && !grants.permissions.includes(ps.name)) grants.permissions.push(ps.name);\n if (ps.name === ADMIN_FULL_ACCESS && unscopedUserPsIds.has(ps.id)) hasPlatformAdminGrant = true;\n const sysPerms = typeof ps.system_permissions === 'string'\n ? safeJsonParse(ps.system_permissions, [])\n : (ps.system_permissions ?? ps.systemPermissions);\n if (Array.isArray(sysPerms)) {\n for (const p of sysPerms) {\n if (typeof p === 'string' && !grants.systemPermissions.includes(p)) grants.systemPermissions.push(p);\n }\n }\n const tabs = typeof ps.tab_permissions === 'string'\n ? safeJsonParse(ps.tab_permissions, {})\n : (ps.tab_permissions ?? ps.tabPermissions);\n if (tabs && typeof tabs === 'object') {\n for (const [app, val] of Object.entries(tabs as Record<string, unknown>)) {\n if (typeof val !== 'string' || !(val in tabRank)) continue;\n const cur = mergedTabs[app];\n if (!cur || tabRank[val] > tabRank[cur]) {\n mergedTabs[app] = val as 'visible' | 'hidden' | 'default_on' | 'default_off';\n }\n }\n }\n }\n if (Object.keys(mergedTabs).length > 0) grants.tabPermissions = mergedTabs;\n }\n\n // 6c. Project the derived platform_admin built-in role (leads the list).\n if (hasPlatformAdminGrant && !grants.positions.includes(BUILTIN_IDENTITY_PLATFORM_ADMIN)) {\n grants.positions.unshift(BUILTIN_IDENTITY_PLATFORM_ADMIN);\n }\n\n // 6d. [ADR-0095 D2/D3] Resolve the posture rung ONCE, from held CAPABILITY\n // grants — never from a better-auth role. `PLATFORM_ADMIN` from the\n // unscoped `admin_full_access` grant (the same `viewAllRecords`/\n // `modifyAllRecords` evidence the superuser bypass trusts); `TENANT_ADMIN`\n // from the `organization_admin` grant (auto-provisioned from the better-\n // auth owner/admin role at §3 above — a provisioning source, not an\n // enforcement input, closing the #2836 dual-track class). Enforcement\n // behavior is unchanged: the per-object Layer 0 exemption + per-side\n // superuser bypass still gate access; posture is the carried, explainable\n // tier. `EXTERNAL` is never derived (no external principal type yet).\n grants.posture = derivePosture({\n isPlatformAdmin: hasPlatformAdminGrant,\n // [ADR-0105 D4] Either org-admin capability set resolves the rung — the\n // wall-less variant differs only by withholding the superuser bits.\n isTenantAdmin: ORGANIZATION_ADMIN_GRANTS.some((n: string) => grants.permissions.includes(n)),\n });\n\n // 7. [ADR-0024] Env-side AI seat: synthesize the `ai_seat` capability from the\n // boolean sys_user.ai_access (sqlite returns 1/0; memory returns boolean).\n if (!grants.permissions.includes('ai_seat')) {\n const aiAccess = ((await getUserRow()) as { ai_access?: unknown } | undefined)?.ai_access;\n if (aiAccess === true || aiAccess === 1 || aiAccess === '1') grants.permissions.push('ai_seat');\n }\n\n return grants;\n}\n\n// ── Localization (ADR-0053 Phase 2) ─────────────────────────────────────────\n\nfunction isValidTimeZone(tz: string): boolean {\n try { new Intl.DateTimeFormat('en-US', { timeZone: tz }); return true; } catch { return false; }\n}\nfunction coerceTimeZone(value: unknown): string | undefined {\n const s = typeof value === 'string' ? value.trim() : value != null ? String(value).trim() : '';\n return s && isValidTimeZone(s) ? s : undefined;\n}\nfunction coerceLocale(value: unknown): string | undefined {\n const s = typeof value === 'string' ? value.trim() : value != null ? String(value).trim() : '';\n return s || undefined;\n}\nfunction coerceCurrency(value: unknown): string | undefined {\n const s = typeof value === 'string' ? value.trim().toUpperCase() : '';\n return /^[A-Z]{3}$/.test(s) ? s : undefined;\n}\n\nexport interface ResolveLocalizationInput {\n ql: any;\n /** Settings service exposing `get(namespace, key, { tenantId, userId })`. */\n settings?: any;\n tenantId?: string;\n userId?: string;\n}\n\n/**\n * Resolve workspace localization defaults (reference `timezone` / `locale` /\n * `currency`). Canonical path is the `localization` SettingsManifest (cascade:\n * platform default → global → tenant); falls back to direct tenant-scoped\n * `sys_setting` rows, then the built-ins `UTC` / `en-US`. Never throws.\n */\nexport async function resolveLocalizationContext(\n input: ResolveLocalizationInput,\n): Promise<{ timezone: string; locale: string; currency?: string }> {\n const { ql, settings, tenantId, userId } = input;\n try {\n if (settings && typeof settings.get === 'function') {\n const sctx = { tenantId, userId } as any;\n const [tzRes, localeRes, currencyRes] = await Promise.all([\n settings.get('localization', 'timezone', sctx).catch(() => undefined),\n settings.get('localization', 'locale', sctx).catch(() => undefined),\n settings.get('localization', 'currency', sctx).catch(() => undefined),\n ]);\n const tz = coerceTimeZone(tzRes?.value);\n const locale = coerceLocale(localeRes?.value);\n const currency = coerceCurrency(currencyRes?.value);\n if (tz || locale || currency) return { timezone: tz ?? 'UTC', locale: locale ?? 'en-US', currency };\n }\n } catch {\n // settings service unavailable → direct read\n }\n // One read for all three keys instead of a query per key (`$in` on `key`).\n const rows = await tryFind(\n ql,\n 'sys_setting',\n { namespace: 'localization', key: { $in: ['timezone', 'locale', 'currency'] }, scope: 'tenant' },\n 10,\n );\n const valueOf = (k: string) => rows.find((r) => r.key === k)?.value;\n return {\n timezone: coerceTimeZone(valueOf('timezone')) ?? 'UTC',\n locale: coerceLocale(valueOf('locale')) ?? 'en-US',\n currency: coerceCurrency(valueOf('currency')),\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Grant validity windows (ADR-0091 D1/D2).\n *\n * `sys_user_position` and `sys_user_permission_set` rows carry optional\n * `valid_from` / `valid_until` columns. A row outside its window MUST NOT\n * resolve — anywhere, symmetrically: `resolveAuthzContext`, the explain\n * engine's `buildContextForUser`, plugin-sharing's `expandPositionUsers`,\n * and (transitively) the delegated-admin gate's held-scope resolution.\n *\n * Correctness lives HERE, at resolution time — never in a cleanup job\n * (ADR-0049: no unenforced security properties). The window is half-open\n * `[from, until)` in UTC: a grant is inactive before `valid_from` and\n * inactive AT and AFTER `valid_until`. Null/absent bounds mean unbounded,\n * so pre-ADR-0091 rows behave exactly as before.\n *\n * Fail-closed: a bound that is PRESENT but unparseable disables the grant\n * (unlike API-key `isExpired`, which tolerates garbage — an API key is a\n * single credential, a grant row is standing authority).\n */\n\n/**\n * Coerce a stored timestamp to epoch milliseconds.\n * Returns `undefined` for absent (null/undefined/'') values — \"no bound\" —\n * and `NaN` for present-but-unparseable values, which callers treat as\n * out-of-window (fail closed).\n */\nfunction toEpochMs(value: unknown): number | undefined {\n if (value == null || value === '') return undefined;\n if (typeof value === 'number') {\n // Heuristic: seconds vs milliseconds epoch (same rule as api-key.ts).\n return value < 1e12 ? value * 1000 : value;\n }\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'string') return Date.parse(value);\n return Number.NaN;\n}\n\n/** The validity-window shape shared by both user-grant tables (ADR-0091 D1). */\nexport interface GrantValidityWindow {\n valid_from?: unknown;\n valid_until?: unknown;\n}\n\n/**\n * True when a grant row is inside its validity window at `nowMs`.\n * The single predicate every resolver uses (ADR-0091 D2):\n * `(valid_from is null or valid_from <= now) and (valid_until is null or valid_until > now)`.\n */\nexport function isGrantActive(row: GrantValidityWindow | null | undefined, nowMs: number): boolean {\n if (!row) return false;\n const from = toEpochMs((row as any).valid_from ?? (row as any).validFrom);\n // NaN comparisons are always false, so an unparseable bound fails closed.\n if (from !== undefined && !(nowMs >= from)) return false;\n const until = toEpochMs((row as any).valid_until ?? (row as any).validUntil);\n if (until !== undefined && !(nowMs < until)) return false;\n return true;\n}\n\n/**\n * True when a grant row carries a `valid_until` that has already passed —\n * i.e. it WAS active and expired (not merely not-yet-active). The explain\n * engine uses this to report the dedicated \"held until … — expired\"\n * contributor state (ADR-0091 D2).\n */\nexport function isGrantExpired(row: GrantValidityWindow | null | undefined, nowMs: number): boolean {\n if (!row) return false;\n const until = toEpochMs((row as any).valid_until ?? (row as any).validUntil);\n if (until === undefined) return false;\n return !(nowMs < until);\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * ── The monotonic posture ladder (ADR-0095 D2/D3) ───────────────────────────\n *\n * The principal-tiering enum resolved ONCE in `resolveAuthzContext`\n * (`PLATFORM_ADMIN > TENANT_ADMIN > MEMBER > EXTERNAL`). This module owns two\n * things and deliberately nothing more:\n *\n * 1. **Derivation (D3).** {@link derivePosture} maps held *capability grants*\n * — never a better-auth role — to a rung. `PLATFORM_ADMIN` derives from the\n * unscoped `admin_full_access` grant (the `viewAllRecords`/`modifyAllRecords`\n * evidence the superuser bypass already trusts); `TENANT_ADMIN` from the\n * `organization_admin` grant. The better-auth `role='admin'` is upstream a\n * *provisioning source* of those grants (`auto-org-admin-grant.ts`), so it\n * never re-enters adjudication here — the #2836 dual-track class is closed\n * by construction.\n *\n * 2. **The rung → injection-rule mapping + its tested invariants (D2).** Each\n * rung maps to EXACTLY ONE row-visibility injection rule\n * ({@link POSTURE_INJECTION_RULE}). {@link postureVisibleRows} is the\n * REFERENCE MODEL of those rules over a synthetic row-set — it locks the two\n * properties the ADR requires as invariants: strict nesting (rung n's\n * visible set ⊇ rung n−1's) and the EXTERNAL deny-by-default semantics\n * (explicit shares only, OWD never widens it).\n *\n * This module is NOT the enforcement path. The effective read/write filter is\n * `Layer0(tenant) AND Layer1(business RLS)`, computed in `@objectstack/plugin-\n * security` (`tenant-layer.ts` + `security-plugin.ts`), and the real behavior\n * guard is the `authz-matrix-gate` unit snapshot + the dogfood conformance\n * matrix. The reference model here exists so the ladder's *mathematical*\n * properties can be asserted at the unit layer without an enforcement boot, and\n * so the EXTERNAL rung — which has no enforcement path yet — cannot be\n * reinvented differently when portal/external membership arrives.\n */\n\nimport type { AuthzPosture } from '@objectstack/spec/security';\n\n/**\n * The rung ordering, high privilege → low, matching the spec enum's numeric\n * values (`PLATFORM_ADMIN=3 … EXTERNAL=0`). Visibility grows monotonically UP\n * this ladder (see {@link postureVisibleRows}).\n */\nexport const POSTURE_LADDER = [\n 'PLATFORM_ADMIN',\n 'TENANT_ADMIN',\n 'MEMBER',\n 'EXTERNAL',\n] as const satisfies readonly AuthzPosture[];\n\n/** Numeric rank per rung (mirrors the spec `AuthzPosture` enum values). */\nexport const POSTURE_RANK: Record<AuthzPosture, number> = {\n PLATFORM_ADMIN: 3,\n TENANT_ADMIN: 2,\n MEMBER: 1,\n EXTERNAL: 0,\n};\n\n/**\n * The ONE row-visibility injection rule each rung maps to (ADR-0095 D2). Prose,\n * because the machine artifacts live in enforcement (Layer 0 + the per-rung\n * Layer 1 rule); this is the enumerable contract the explain track reports and\n * {@link postureVisibleRows} models.\n */\nexport const POSTURE_INJECTION_RULE: Record<AuthzPosture, string> = {\n PLATFORM_ADMIN:\n 'Layer 0 exemption where the object posture permits (private / platform-global / better-auth-managed) — crosses the tenant wall; org-scoped like TENANT_ADMIN on ordinary tenant business objects.',\n TENANT_ADMIN:\n 'All rows within the active organization (organization_id == ctx.tenantId); no ownership / depth / sharing narrowing.',\n MEMBER:\n 'Business RLS within the organization — ownership (owner / unit depth), the OWD baseline, and explicit sharing.',\n EXTERNAL:\n 'Explicitly shared rows ONLY — OWD baselines and sharing rules never apply; a misconfiguration can only shrink visibility, never widen it.',\n};\n\n/** Capability-grant evidence the posture derivation consumes (ADR-0095 D3). */\nexport interface PostureEvidence {\n /**\n * Holds the UNSCOPED platform-admin capability grant (`admin_full_access` →\n * `viewAllRecords`/`modifyAllRecords`) — the same evidence the superuser\n * bypass trusts. NOT a better-auth role.\n */\n isPlatformAdmin: boolean;\n /**\n * Holds the org-admin capability grant (`organization_admin`, tenant-scoped\n * `viewAllRecords`/`modifyAllRecords`). Provisioned from the better-auth\n * owner/admin role upstream, consumed here only as a held capability.\n */\n isTenantAdmin: boolean;\n}\n\n/**\n * Resolve the principal's posture rung from held capability grants (ADR-0095 D3).\n *\n * Returns `PLATFORM_ADMIN` | `TENANT_ADMIN` | `MEMBER`. It NEVER returns\n * `EXTERNAL`: no external principal type exists yet (the sharing chain has no\n * portal/guest-share concept — ADR-0095 W4). The `EXTERNAL` rung, its injection\n * rule, and its semantics are defined and test-locked ({@link postureVisibleRows},\n * {@link POSTURE_INJECTION_RULE}) so that when portal/external membership lands\n * (ADR-0093) the derivation gains an EXTERNAL branch HERE without the rung being\n * reinvented. `MEMBER` is the authenticated-principal floor.\n */\nexport function derivePosture(evidence: PostureEvidence): AuthzPosture {\n if (evidence.isPlatformAdmin) return 'PLATFORM_ADMIN';\n if (evidence.isTenantAdmin) return 'TENANT_ADMIN';\n return 'MEMBER';\n}\n\n// ── Reference visibility model (invariant lock, NOT enforcement) ─────────────\n\n/** A synthetic record for the ladder reference model. */\nexport interface LadderRow {\n id: string;\n /** The row's tenant. `undefined` = a non-tenant (platform-global) row. */\n organization_id?: string;\n /** The row's owner (drives the MEMBER ownership disjunct). */\n owner_id?: string;\n /**\n * Whether an OWD-derived source would admit this row for a member (public\n * baseline / criteria sharing). EXTERNAL deliberately ignores this field.\n */\n owdVisible?: boolean;\n /** User ids this row is EXPLICITLY shared to (the only EXTERNAL source). */\n sharedTo?: readonly string[];\n}\n\n/** The principal the reference model evaluates a rung for. */\nexport interface LadderPrincipal {\n userId: string;\n /** The principal's active organization (undefined for an unscoped principal). */\n organizationId?: string;\n}\n\nfunction isSharedTo(row: LadderRow, userId: string): boolean {\n return (row.sharedTo ?? []).includes(userId);\n}\n\n/** EXTERNAL rung: explicitly shared rows ONLY — never OWD, never org-wide. */\nfunction externalVisible(rows: readonly LadderRow[], p: LadderPrincipal): LadderRow[] {\n return rows.filter((r) => isSharedTo(r, p.userId));\n}\n\n/**\n * MEMBER rung: business RLS within the org — ownership OR OWD baseline OR\n * explicit sharing. Composed as `EXTERNAL ∪ (in-org ownership/OWD)` so the\n * EXTERNAL ⊆ MEMBER leg of the nesting invariant holds by construction.\n */\nfunction memberVisible(rows: readonly LadderRow[], p: LadderPrincipal): LadderRow[] {\n const shared = new Set(externalVisible(rows, p));\n return rows.filter(\n (r) =>\n shared.has(r) ||\n (r.organization_id === p.organizationId && (r.owner_id === p.userId || r.owdVisible === true)),\n );\n}\n\n/**\n * TENANT_ADMIN rung: all rows in the active organization. Composed as\n * `MEMBER ∪ (all in-org)` so MEMBER ⊆ TENANT_ADMIN holds by construction.\n */\nfunction tenantAdminVisible(rows: readonly LadderRow[], p: LadderPrincipal): LadderRow[] {\n const member = new Set(memberVisible(rows, p));\n return rows.filter((r) => member.has(r) || r.organization_id === p.organizationId);\n}\n\n/** PLATFORM_ADMIN rung: crosses the tenant wall — every row (⊇ TENANT_ADMIN trivially). */\nfunction platformAdminVisible(rows: readonly LadderRow[]): LadderRow[] {\n return [...rows];\n}\n\n/**\n * Reference model of the per-rung injection rule: the visible-row set a rung\n * would resolve to over `rows` for `principal`. Used to lock the ADR-0095 D2\n * invariants (strict nesting + EXTERNAL deny-by-default). NOT an enforcement\n * path — see the module header.\n */\nexport function postureVisibleRows(\n posture: AuthzPosture,\n rows: readonly LadderRow[],\n principal: LadderPrincipal,\n): LadderRow[] {\n switch (posture) {\n case 'PLATFORM_ADMIN':\n return platformAdminVisible(rows);\n case 'TENANT_ADMIN':\n return tenantAdminVisible(rows, principal);\n case 'MEMBER':\n return memberVisible(rows, principal);\n case 'EXTERNAL':\n return externalVisible(rows, principal);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * ADR-0069 — authentication-policy session gate.\n *\n * Some auth policies (password expiry, enforced MFA) must block an\n * authenticated user from PROTECTED RESOURCES until they remediate, while\n * still letting them reach the auth endpoints (change-password, two-factor\n * enrollment, sign-out) and a few UI-bootstrap reads.\n *\n * The posture is computed ONCE, in the auth `customSession` enrichment, and\n * attached to the session user as `user.authGate = { code, message }`. The\n * transport seams (REST middleware, dispatcher) then call\n * {@link evaluateAuthGate} to decide whether THIS request is blocked. Keeping\n * the allow-list + decision in one pure function means the seams can never\n * drift on what is blocked.\n */\n\nexport interface AuthGate {\n /** Stable machine code, e.g. `PASSWORD_EXPIRED` / `MFA_REQUIRED`. */\n code: string;\n /** Human-facing message. */\n message: string;\n}\n\n// Endpoints a gated user MUST still reach to remediate or bootstrap the\n// remediation UI. Matched against the request path (query stripped). Covers\n// both REST (`/api/v1/auth/…`) and dispatcher (`/auth/…`) path shapes.\nconst ALLOW_PREFIXES = ['/api/v1/auth/', '/api/auth/', '/auth/'];\nconst ALLOW_SUFFIXES = ['/health', '/ready', '/discovery', '/me/apps', '/me/localization'];\n\n/** True when `path` is exempt from the auth gate (auth + remediation + health). */\nexport function isAuthGateAllowlisted(rawPath: string | undefined | null): boolean {\n if (!rawPath) return true;\n // Strip query + trailing slashes WITHOUT a regex (avoids ReDoS on a\n // path of many '/'). char 47 = '/'.\n let path = rawPath.split('?')[0] || '/';\n let end = path.length;\n while (end > 1 && path.charCodeAt(end - 1) === 47) end--;\n path = path.slice(0, end) || '/';\n // Any path with an `/auth/` segment is an auth endpoint (covers project-\n // scoped mounts like `/api/v1/environments/:env/auth/...`).\n if (path.includes('/auth/')) return true;\n for (const p of ALLOW_PREFIXES) {\n if (path.startsWith(p) || path === p.replace(/\\/$/, '')) return true;\n }\n for (const s of ALLOW_SUFFIXES) {\n if (path.endsWith(s)) return true;\n }\n return false;\n}\n\n/**\n * Returns the active gate when `sessionUser` carries an `authGate` AND `path`\n * is not allow-listed; otherwise null. Anonymous users (no `authGate`) and\n * allow-listed paths always pass.\n */\nexport function evaluateAuthGate(sessionUser: any, path: string): AuthGate | null {\n const gate = sessionUser?.authGate;\n if (!gate || typeof gate.code !== 'string') return null;\n if (isAuthGateAllowlisted(path)) return null;\n return {\n code: gate.code,\n message:\n typeof gate.message === 'string' && gate.message\n ? gate.message\n : 'Access is blocked by an authentication policy.',\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * #2567 — the single anonymous-deny decision, shared by every HTTP seam.\n *\n * ADR-0056 D2 made the platform deny anonymous callers by default. Phase 1 gated\n * each surface (REST `/data`, dispatcher `/graphql` + `/meta`, raw-hono `/data`)\n * but every seam hand-rolled the same `!userId && !isSystem → 401` check. This\n * centralises that DECISION into one pure, tested function — the exact pattern\n * {@link ./auth-gate.ts} established for the ADR-0069 auth-policy gate: keeping\n * the decision in one function means the seams can never drift on who is denied.\n *\n * ## The `requireAuth` opt-out is gone (#3963)\n *\n * This used to take a `requireAuth` posture and no-op when it was falsy, so a\n * deployment could open its ENTIRE data plane with one config key. That key is\n * retired: auth is a kernel concern, and every surface that legitimately serves\n * a caller with no session derives its own narrow authorization from a\n * DECLARATION instead of from the deployment posture —\n *\n * - control plane (`/auth/*`, `/health`, `/ready`, `/discovery`, the ADR-0069\n * remediation paths) → the {@link isAuthGateAllowlisted} allowlist, below;\n * - public form submission → `publicFormGrant` (ADR-0056 Option A), derived\n * from the form view's own declaration;\n * - share links → the capability token, validated then read as SYSTEM;\n * - a `book.audience: 'public'` read → the ADR-0046 §6.7 audience gate (#3963);\n * - MCP → an OAuth token or API key, never anonymous.\n *\n * Those run UPSTREAM of this function and set the execution context (a `userId`,\n * or `isSystem`) or bypass the seam entirely, so this only ever inspects the\n * already-resolved context. Nothing else gets in.\n */\n\nimport { isAuthGateAllowlisted } from './auth-gate.js';\n\n/** HTTP status every seam returns for an anonymous-denied request. */\nexport const ANONYMOUS_DENY_STATUS = 401 as const;\n/** Stable machine code (mirrors the REST `enforceAuth` seam). ADR-0112: SCREAMING, a `StandardErrorCode` member. */\nexport const ANONYMOUS_DENY_CODE = 'UNAUTHENTICATED' as const;\n/** Human-facing message. */\nexport const ANONYMOUS_DENY_MESSAGE = 'Authentication is required to access this endpoint.';\n/**\n * The **REST seam's** 401 body — flat `{ error, message }`. NOT the platform's\n * only one; see the two-envelope table below before you reuse this shape.\n *\n * Exactly one consumer writes it: `@objectstack/rest`'s `enforceAuth`\n * (`rest-server.ts` — `res.status(ANONYMOUS_DENY_STATUS).json(ANONYMOUS_DENY_BODY)`),\n * which owns the `/data/*` and `/meta` surfaces.\n *\n * ## Two live envelopes, one denial (#5632)\n *\n * Every HTTP seam shares the DECISION ({@link shouldDenyAnonymous}) and the\n * semantics ({@link ANONYMOUS_DENY_STATUS} / {@link ANONYMOUS_DENY_CODE} /\n * {@link ANONYMOUS_DENY_MESSAGE}). What differs is the **wrapper**:\n *\n * - **REST seam** — `@objectstack/rest` `enforceAuth`, this constant, verbatim:\n * `{ error: 'UNAUTHENTICATED', message: '…' }`. The code is the value of the\n * top-level `error` key; there is no `success` key and no nesting.\n * - **Dispatcher seams** — the five runtime domains `domains/ai.ts`,\n * `domains/meta.ts`, `domains/security.ts`, `domains/actions.ts` and\n * `domains/automation.ts` do NOT use this constant. Each calls\n * `deps.error(ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, { code: ANONYMOUS_DENY_CODE })`,\n * so the wire body is the dispatcher's standard wrapper:\n * `{ success: false, error: { code, message, httpStatus } }`.\n *\n * Both shapes are **live and sanctioned** — ADR-0112's 2026-07-30 amendment\n * (#4007) records the flat and wrapped envelopes as the two live ones, and\n * assigns retiring one of them to the envelope-convergence line (#3843 family).\n * Converging them is a breaking wire change; it is not this module's to make,\n * and this constant must not be read as if it had already happened.\n *\n * ## Reading this from a consumer (human or AI author)\n *\n * Read the envelope the seam you called DECLARES — flat from `/data` + `/meta`,\n * wrapped from a dispatcher-mounted surface. Do **not** write a tolerant\n * `body.error?.code ?? body.error` chain that swallows both: that fallback is\n * precisely where an envelope regression hides, and this docstring claiming to\n * be \"the single shape every seam returns\" is what used to invite it (#5632).\n *\n * Both shapes are pinned against a real booted showcase by\n * `packages/qa/dogfood/test/showcase-anonymous-deny-surfaces.dogfood.test.ts`,\n * which classifies every anonymous 401 into exactly one of the two families and\n * fails on a third dialect or on a seam that changes family.\n */\nexport const ANONYMOUS_DENY_BODY = {\n error: ANONYMOUS_DENY_CODE,\n message: ANONYMOUS_DENY_MESSAGE,\n} as const;\n\nexport interface AnonymousDenyInput {\n /** Resolved caller id, if any. */\n userId?: string | null;\n /** Internal system context (never set on inbound HTTP; cannot be forged). */\n isSystem?: boolean;\n /** HTTP method — `OPTIONS` (CORS preflight) always passes. */\n method?: string | null;\n /**\n * OPTIONAL request path. When a NON-EMPTY string, a control-plane path\n * (auth / health / ready / discovery — see {@link isAuthGateAllowlisted}) is\n * exempt. Body-routed seams (GraphQL) have no meaningful path and pass\n * `undefined`; see the guard below for why that is load-bearing.\n */\n path?: string | null;\n}\n\n/**\n * True when the request MUST be rejected with 401. The one decision every HTTP\n * seam shares.\n */\nexport function shouldDenyAnonymous(input: AnonymousDenyInput): boolean {\n if (typeof input.method === 'string' && input.method.toUpperCase() === 'OPTIONS') {\n return false; // CORS preflight\n }\n if (input.userId || input.isSystem) return false; // authenticated / system\n // Control-plane exemption — ONLY for a real, non-empty path.\n //\n // ⚠️ `isAuthGateAllowlisted(undefined)` returns `true` (it treats \"no path\"\n // as allow-listed for the auth-gate's purposes). A body-routed seam such as\n // GraphQL has no meaningful request path; if it passed `undefined` straight\n // through, the allowlist would exempt EVERY anonymous query and silently\n // reopen exactly the hole #2567 closes. The non-empty guard is mandatory.\n if (typeof input.path === 'string' && input.path.length > 0 && isAuthGateAllowlisted(input.path)) {\n return false;\n }\n return true;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Timezone-aware calendar utilities (ADR-0053 Phase 2).\n *\n * The one primitive everything else builds on is {@link calendarPartsInTz}:\n * the year/month/day an instant falls on *as seen in a reference timezone*.\n * It uses `Intl.DateTimeFormat().formatToParts()` so DST transitions are\n * handled by the platform's tz database — never hand-rolled offset math, which\n * is the classic source of off-by-one-hour bucket errors.\n *\n * This lives in `@objectstack/core` (not `@objectstack/formula`) because both\n * the ObjectQL aggregation engine and the analytics service need it and both\n * already depend on core, whereas neither depends on formula's public surface.\n * (`@objectstack/formula` keeps its own private copy for `today()`/`daysFromNow`\n * to avoid a layering dependency on core.)\n */\n\n/** Calendar-day parts in a reference timezone. `month` is 1-12. */\nexport interface CalendarParts {\n year: number;\n month: number;\n day: number;\n}\n\n/**\n * The year/month/day an instant falls on in `tz`. Throws if `tz` is not a\n * valid IANA zone (callers treat that as a fall-through to UTC).\n */\nexport function calendarPartsInTz(d: Date, tz: string): CalendarParts {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(d);\n const get = (t: string) => Number(parts.find((p) => p.type === t)?.value);\n return { year: get('year'), month: get('month'), day: get('day') };\n}\n\n/**\n * The calendar-day parts of an instant, in `tz` when it's a real non-UTC zone,\n * otherwise in UTC. Never throws: an unset, `'UTC'`, or invalid zone falls back\n * to the UTC calendar day. This is the safe entry point for bucketing code that\n * must degrade to the historical UTC behavior rather than error.\n */\nexport function calendarPartsInTzOrUtc(d: Date, tz?: string): CalendarParts {\n if (tz && tz !== 'UTC') {\n try {\n return calendarPartsInTz(d, tz);\n } catch {\n // unknown zone → fall through to UTC\n }\n }\n return {\n year: d.getUTCFullYear(),\n month: d.getUTCMonth() + 1,\n day: d.getUTCDate(),\n };\n}\n\n/**\n * The UTC instant (epoch ms) at which calendar day `ymd` (`YYYY-MM-DD`) *begins*\n * in reference timezone `tz` — i.e. local **midnight** of that day rendered as a\n * UTC instant. The inverse direction of {@link calendarPartsInTz}.\n *\n * DST-safe: the zone offset is read from the platform tz database via\n * `Intl.DateTimeFormat` (never hand-computed), and a two-pass resolution settles\n * the rare case where the offset differs side-to-side of the target instant. An\n * unset, `'UTC'`, invalid, or unparseable input returns plain UTC midnight.\n *\n * Used by date-bucket drill ranges (#1752): a `datetime` field buckets on the\n * reference-tz calendar, so its bucket boundary is that tz's midnight instant.\n */\nexport function zonedDateStartToUtcMs(ymd: string, tz?: string): number {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(ymd);\n const wallAsUtc = m ? Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : NaN;\n if (!tz || tz === 'UTC' || Number.isNaN(wallAsUtc)) return wallAsUtc;\n try {\n // The tz offset (local − UTC, in ms) at instant `t`: read t's wall clock in\n // `tz`, re-interpret those parts as UTC, and subtract t.\n const offsetAt = (t: number): number => {\n const p = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n hourCycle: 'h23',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n }).formatToParts(new Date(t));\n const g = (k: string) => Number(p.find((x) => x.type === k)?.value);\n return Date.UTC(g('year'), g('month') - 1, g('day'), g('hour'), g('minute'), g('second')) - t;\n };\n // Want U such that localParts(U) == midnight, i.e. U = wallAsUtc − offset(U).\n // Iterate from the zero-offset guess; converges in ≤2 steps off a DST edge.\n const off1 = offsetAt(wallAsUtc - offsetAt(wallAsUtc));\n return wallAsUtc - off1;\n } catch {\n return wallAsUtc; // unknown zone → UTC midnight\n }\n}\n\n/**\n * Calendar-day bound semantics (ADR-0053 D-D) now live in `@objectstack/spec`,\n * beside the date-macro vocabulary they give meaning to — the fifth consumer\n * (`@objectstack/formula`'s RLS write-side `check` evaluator) cannot depend on\n * this package, and a second copy of the rule is exactly the divergence #3777\n * catalogued.\n *\n * Re-exported here so the published `@objectstack/core` surface is unchanged\n * for the drivers and analytics strategies that already import it from here.\n */\nexport { nextUtcCalendarDay, utcInstantMs } from '@objectstack/spec/data';\n\n/**\n * Granularity of a canonical date-bucket key. Mirrors `@objectstack/spec`'s\n * `DateGranularity` enum but kept as a local literal union so this low-level\n * package needs no dependency on spec.\n */\nexport type BucketGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year';\n\n/**\n * ISO-8601 week label (Mon-start weeks, week 1 = the week of the first\n * Thursday) of a UTC calendar day. The forward-direction companion used to\n * *validate* a reconstructed week boundary; it mirrors the week branch of\n * `@objectstack/objectql`'s `bucketDateValue` (kept in lockstep by the\n * round-trip parity test in objectql).\n */\nfunction isoWeekLabelUtc(d: Date): string {\n const target = new Date(d.getTime());\n const dayNum = (target.getUTCDay() + 6) % 7; // Mon=0..Sun=6\n target.setUTCDate(target.getUTCDate() - dayNum + 3); // shift to that week's Thursday\n const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));\n const weekNo =\n 1 +\n Math.round(\n ((target.getTime() - firstThursday.getTime()) / 86400000 -\n 3 +\n ((firstThursday.getUTCDay() + 6) % 7)) /\n 7,\n );\n return `${target.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n\n/**\n * The half-open calendar span `[start, end)` of a canonical date-bucket KEY,\n * as `YYYY-MM-DD` strings (`start` inclusive, `end` exclusive — the next\n * bucket's first day).\n *\n * The input MUST be the canonical key produced by `bucketDateValue` /\n * `buildDateBucketExpr` (`2026`, `2026-Q2`, `2026-06`, `2026-06-15`,\n * `2026-W23`) — NEVER a localized / humanized display label. The span is pure,\n * timezone-naive calendar arithmetic; a caller that needs instant bounds for a\n * `datetime` field in a reference timezone layers that on top (and, per\n * ADR-0053, a `date` field compares against these `YYYY-MM-DD` bounds directly).\n *\n * Returns `null` for the empty bucket, an unparseable key, or a key that is\n * shape-valid but out of range (e.g. `2026-13`, a `-W53` in a 52-week year,\n * `2026-02-30`). Callers drop the range and fall back to an unscoped (superset)\n * drill rather than emit a wrong bound.\n *\n * `key` admits `null` because that IS the empty bucket's key on both aggregation\n * paths (#3839); callers pass a grouped row's dimension value straight through\n * rather than casting a lie.\n */\nexport function bucketKeyToCalendarRange(\n key: string | null | undefined,\n granularity: BucketGranularity,\n): { start: string; end: string } | null {\n if (typeof key !== 'string' || key.length === 0) return null;\n const fmt = (dt: Date) =>\n `${String(dt.getUTCFullYear()).padStart(4, '0')}-${String(dt.getUTCMonth() + 1).padStart(\n 2,\n '0',\n )}-${String(dt.getUTCDate()).padStart(2, '0')}`;\n\n switch (granularity) {\n case 'year': {\n const m = /^(\\d{4})$/.exec(key);\n if (!m) return null;\n const y = Number(m[1]);\n return { start: fmt(new Date(Date.UTC(y, 0, 1))), end: fmt(new Date(Date.UTC(y + 1, 0, 1))) };\n }\n case 'quarter': {\n const m = /^(\\d{4})-Q([1-4])$/.exec(key);\n if (!m) return null;\n const y = Number(m[1]);\n const startMonth = (Number(m[2]) - 1) * 3; // Q1→0, Q2→3, Q3→6, Q4→9\n return {\n start: fmt(new Date(Date.UTC(y, startMonth, 1))),\n end: fmt(new Date(Date.UTC(y, startMonth + 3, 1))), // Date.UTC rolls Q4 into next year\n };\n }\n case 'month': {\n const m = /^(\\d{4})-(\\d{2})$/.exec(key);\n if (!m) return null;\n const mo = Number(m[2]);\n if (mo < 1 || mo > 12) return null;\n const y = Number(m[1]);\n return {\n start: fmt(new Date(Date.UTC(y, mo - 1, 1))),\n end: fmt(new Date(Date.UTC(y, mo, 1))),\n };\n }\n case 'day': {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(key);\n if (!m) return null;\n const y = Number(m[1]);\n const mo = Number(m[2]);\n const d = Number(m[3]);\n const start = new Date(Date.UTC(y, mo - 1, d));\n if (fmt(start) !== key) return null; // reject an impossible day that rolled over\n return { start: key, end: fmt(new Date(Date.UTC(y, mo - 1, d + 1))) };\n }\n case 'week': {\n const m = /^(\\d{4})-W(\\d{2})$/.exec(key);\n if (!m) return null;\n const isoYear = Number(m[1]);\n const week = Number(m[2]);\n if (week < 1 || week > 53) return null;\n // Monday of ISO week 1 is the Monday on/before Jan 4; add (week-1) weeks.\n const jan4 = new Date(Date.UTC(isoYear, 0, 4));\n const jan4Dow = (jan4.getUTCDay() + 6) % 7; // Mon=0..Sun=6\n const start = new Date(jan4.getTime());\n start.setUTCDate(jan4.getUTCDate() - jan4Dow + (week - 1) * 7);\n if (isoWeekLabelUtc(start) !== key) return null; // reject -W53 overflow etc.\n const end = new Date(start.getTime());\n end.setUTCDate(start.getUTCDate() + 7);\n return { start: fmt(start), end: fmt(end) };\n }\n default:\n return null;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `bulkWrite` — the shared batched-write helper used by BOTH the seed loader\n * (`@objectstack/metadata-protocol`) and the data-import runner\n * (`@objectstack/rest`), so neither reimplements batching, transient-error\n * retry, or per-row degradation. See framework#2678.\n *\n * ObjectQL's engine already does the efficient thing when handed an ARRAY —\n * one `driver.bulkCreate` round-trip plus parent-deduplicated summary\n * recompute (`engine.insert(object, rows[])`) — but seed/import fed it one\n * record at a time, so neither got the benefit. This module re-chunks rows\n * into batches and drives them through a caller-supplied batch-write\n * function, adding:\n *\n * - transient-error retry (network blip / timeout) with exponential\n * backoff, so a dropped connection doesn't silently drop the row (the\n * 2026-07-06 HotCRM incident: a turso `fetch failed` mid-seed dropped rows\n * silently because nothing retried);\n * - per-row degradation when a batch fails for a non-transient (logical /\n * validation) reason, so one bad row can't fail the other N-1 — needed\n * because `driver.bulkCreate` is a single multi-row statement/`Promise.all`\n * on every driver in this repo (sql, memory, mongodb): one bad row fails\n * the whole call;\n * - a stable per-row result keyed by the row's original index, so callers\n * can reassemble output in input order even though rows are processed in\n * batches (and a batch's flush may be interleaved with other, immediate,\n * per-row work such as updates).\n *\n * Delivery semantics: **at-least-once**. Transient retry and per-row\n * degradation both RE-RUN a write whose outcome was unknown — e.g. a turso\n * `fetch failed` that arrived *after* the row was already committed\n * (framework#3149), or a result-count mismatch that voids the batch\n * (framework#3151). A caller that needs exactly-once must make its\n * `writeBatch`/`writeOne` idempotent; both receive an `attempt` counter for\n * exactly this — see the natural-key recheck the seed loader and import\n * runner perform on `attempt > 1`. `writeBatch` MUST also resolve exactly one\n * record per input row, in input order: a short / long / non-array return is\n * rejected as a failed batch (framework#3151), never silently backfilled.\n */\n\nexport interface BulkWriteRowResult<TRecord = any> {\n /** Index into the original `rows` array passed to {@link bulkWrite}. */\n index: number;\n ok: boolean;\n record?: TRecord;\n error?: unknown;\n}\n\nexport interface RetryOptions {\n /** Max attempts for one write (batch or single-row), including the first. Default 3. */\n maxRetries?: number;\n /** Base backoff in ms; doubled each retry, plus jitter. Default 200. */\n backoffBaseMs?: number;\n /** Classifies an error as transient (worth retrying) vs logical (the row/batch is just bad). */\n isTransientError?: (err: unknown) => boolean;\n /** Injectable sleep, for deterministic tests. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface BulkWriteOptions<TRow, TRecord = any> extends RetryOptions {\n /** Rows per batch. Default 200 (framework#2678 suggests 100-500). */\n batchSize?: number;\n /**\n * Write one batch. MUST resolve to one record per input row, in the SAME\n * order as `batch` — {@link bulkWrite} correlates `records[i]` back to\n * `batch[i]` positionally (this is how every `bulkCreate` implementation in\n * this repo already behaves: sql's single `INSERT ... VALUES (...), (...)\n * RETURNING *`, memory's `Promise.all`, mongodb's ordered `insertMany`).\n *\n * `ctx.attempt` is the 1-based attempt number. `attempt > 1` means a prior\n * attempt's outcome is UNKNOWN (a transient blip that may have landed after\n * commit) — an exactly-once caller should recheck by natural key and skip\n * rows already present before re-writing (framework#3149).\n */\n writeBatch: (batch: TRow[], ctx: { attempt: number }) => Promise<TRecord[]>;\n /**\n * Write a single row — used only to degrade a failed batch. `ctx.attempt`\n * carries the same recheck signal as {@link writeBatch}.\n */\n writeOne: (row: TRow, ctx: { attempt: number }) => Promise<TRecord>;\n /**\n * Partial-success batch write (framework#3172). When provided it is used\n * INSTEAD of {@link writeBatch}: it must resolve one outcome per input row,\n * in input order — `{ ok: true, record }` for written rows, `{ ok: false,\n * error }` for rows that failed individually (e.g. validation). Per-row\n * failures are final verdicts: bulkWrite records them as-is and does NOT\n * degrade to `writeOne` for them — that is the whole point (a degradation\n * re-run would re-fire beforeInsert hooks on the good rows). Only a THROWN\n * error (a transient infra failure, a result-count mismatch) falls back to\n * the per-row `writeOne` degradation, exactly like `writeBatch`.\n */\n writeBatchPartial?: (\n batch: TRow[],\n ctx: { attempt: number },\n ) => Promise<Array<{ ok: boolean; record?: TRecord; error?: unknown }>>;\n}\n\nconst DEFAULT_BATCH_SIZE = 200;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_BACKOFF_BASE_MS = 200;\n\n/**\n * Transient-error signatures shared by common HTTP/TCP-backed drivers\n * (turso/libsql's fetch-based transport included). Deliberately excludes\n * anything that looks like a validation/constraint error — those must NOT be\n * retried, only degraded to per-row.\n */\nconst TRANSIENT_PATTERNS: RegExp[] = [\n /fetch failed/i,\n /network/i,\n /timed?\\s*out/i,\n /timeout/i,\n /socket hang ?up/i,\n /connection.*(closed|reset|refused|terminated|aborted)/i,\n /\\b(502|503|504)\\b/,\n /server.*unavailable/i,\n /too many connections/i,\n];\n\nconst TRANSIENT_CODES = /^(ECONNRESET|ECONNREFUSED|ECONNABORTED|EPIPE|EAI_AGAIN|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|ENOTFOUND)$/i;\n\n/**\n * Validation / constraint / schema signatures that are DEFINITIVELY logical,\n * never worth retrying. Checked before {@link TRANSIENT_PATTERNS} so a message\n * that happens to mention both (e.g. `CHECK constraint failed: network_zone`,\n * `column network_id is not allowed`) is classified as logical rather than\n * burning retries on a row that will fail identically every time (framework\n * #3150).\n */\nconst NON_TRANSIENT_PATTERNS: RegExp[] = [\n /validation/i,\n /constraint/i,\n /\\brequired\\b/i,\n /\\bunique\\b/i,\n /duplicate/i,\n /not[\\s_-]*null/i,\n /invalid/i,\n /not allowed/i,\n /out of range/i,\n];\n\nexport function defaultIsTransientError(err: unknown): boolean {\n const message = (err as { message?: unknown } | null)?.message;\n const text = typeof message === 'string' ? message : String(err ?? '');\n // A definitive logical signature wins even if a transient word also appears.\n if (NON_TRANSIENT_PATTERNS.some((re) => re.test(text))) return false;\n const code = (err as { code?: unknown } | null)?.code;\n if (typeof code === 'string' && TRANSIENT_CODES.test(code)) return true;\n return TRANSIENT_PATTERNS.some((re) => re.test(text));\n}\n\nconst defaultSleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\ninterface ResolvedRetryOptions {\n maxRetries: number;\n backoffBaseMs: number;\n isTransientError: (err: unknown) => boolean;\n sleep: (ms: number) => Promise<void>;\n}\n\nasync function withRetry<T>(fn: (attempt: number) => Promise<T>, opts: ResolvedRetryOptions): Promise<T> {\n let lastError: unknown;\n for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {\n try {\n return await fn(attempt);\n } catch (err) {\n lastError = err;\n if (attempt >= opts.maxRetries || !opts.isTransientError(err)) throw err;\n const jitter = Math.floor(Math.random() * 50);\n await opts.sleep(opts.backoffBaseMs * 2 ** (attempt - 1) + jitter);\n }\n }\n // Unreachable — the loop above always returns or throws — but keeps TS's\n // control-flow analysis happy about a guaranteed return type.\n throw lastError;\n}\n\n/**\n * Retry a single write (e.g. an `engine.update()` call the seed loader or\n * import runner makes outside the batched-insert path) with the same\n * transient-error backoff {@link bulkWrite} applies to batches — so a\n * network blip doesn't drop an update the way it used to drop an insert.\n */\nexport async function withTransientRetry<T>(fn: (attempt: number) => Promise<T>, opts: RetryOptions = {}): Promise<T> {\n return withRetry(fn, {\n maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),\n backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,\n isTransientError: opts.isTransientError ?? defaultIsTransientError,\n sleep: opts.sleep ?? defaultSleep,\n });\n}\n\n/**\n * Write `rows` through `opts.writeBatch` in chunks of `opts.batchSize`,\n * retrying a whole-batch transient failure with backoff, and degrading to\n * per-row `opts.writeOne` calls (each itself retried) when a batch fails for\n * a non-transient reason — so one bad row can't drop the rest of the batch.\n *\n * Returns one {@link BulkWriteRowResult} per input row, indexed to match\n * `rows`' original order.\n */\nexport async function bulkWrite<TRow, TRecord = any>(\n rows: TRow[],\n opts: BulkWriteOptions<TRow, TRecord>,\n): Promise<BulkWriteRowResult<TRecord>[]> {\n const batchSize = Math.max(1, opts.batchSize ?? DEFAULT_BATCH_SIZE);\n const retryOpts: ResolvedRetryOptions = {\n maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),\n backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,\n isTransientError: opts.isTransientError ?? defaultIsTransientError,\n sleep: opts.sleep ?? defaultSleep,\n };\n\n const results: BulkWriteRowResult<TRecord>[] = new Array(rows.length);\n\n for (let start = 0; start < rows.length; start += batchSize) {\n const batch = rows.slice(start, start + batchSize);\n try {\n // Partial-success path (framework#3172): one call yields a final per-row\n // verdict, so a row that fails validation never triggers the whole-batch\n // degradation that re-runs beforeInsert hooks on its siblings.\n if (opts.writeBatchPartial) {\n const outcomes = await withRetry((attempt) => opts.writeBatchPartial!(batch, { attempt }), retryOpts);\n if (!Array.isArray(outcomes) || outcomes.length !== batch.length) {\n throw Object.assign(\n new Error(\n `bulkWrite: writeBatchPartial returned ${\n Array.isArray(outcomes) ? `${outcomes.length} outcome(s)` : String(typeof outcomes)\n } for a ${batch.length}-row batch — treating batch as failed`,\n ),\n { code: 'ERR_BULK_RESULT_MISMATCH' },\n );\n }\n for (let i = 0; i < batch.length; i++) {\n const o = outcomes[i];\n results[start + i] = o.ok\n ? { index: start + i, ok: true, record: o.record }\n : { index: start + i, ok: false, error: o.error };\n }\n continue;\n }\n const records = await withRetry((attempt) => opts.writeBatch(batch, { attempt }), retryOpts);\n // Contract guard (framework#3151): `writeBatch` must resolve one record\n // per input row. A short / long / non-array return breaks the positional\n // correlation below, so backfilling it would report phantom successes\n // (`record: undefined`) or drop records. Treat the whole batch as failed\n // and fall through to per-row degradation (each row re-attempted via\n // `writeOne`, which under an idempotent caller rechecks before writing).\n // The message deliberately avoids any transient signature so this never\n // reads as a retryable blip — and it is thrown *outside* `withRetry`, so\n // the batch is not retried on it.\n if (!Array.isArray(records) || records.length !== batch.length) {\n throw Object.assign(\n new Error(\n `bulkWrite: writeBatch returned ${\n Array.isArray(records) ? `${records.length} record(s)` : String(typeof records)\n } for a ${batch.length}-row batch — treating batch as failed`,\n ),\n { code: 'ERR_BULK_RESULT_MISMATCH' },\n );\n }\n for (let i = 0; i < batch.length; i++) {\n results[start + i] = { index: start + i, ok: true, record: records[i] };\n }\n } catch (batchErr) {\n // A single-row \"batch\" already IS the per-row attempt — its failure\n // (after transient retry) is the row's final outcome; calling\n // `writeOne` again would just repeat the identical work.\n if (batch.length === 1) {\n results[start] = { index: start, ok: false, error: batchErr };\n continue;\n }\n // The batch failed even after transient retry, or failed for a logical\n // reason retry wouldn't fix. Degrade to per-row so one bad row can't\n // fail the other rows in this batch. Each row still gets its own\n // transient retry — the batch-level failure doesn't tell us which row\n // (if any) was actually the transient one.\n for (let i = 0; i < batch.length; i++) {\n const idx = start + i;\n try {\n const record = await withRetry((attempt) => opts.writeOne(batch[i], { attempt }), retryOpts);\n results[idx] = { index: idx, ok: true, record };\n } catch (err) {\n results[idx] = { index: idx, ok: false, error: err };\n }\n }\n }\n }\n\n return results;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `runMigrationJournal` — the framework-owned runner for data migrations that\n * are too big, too long, or too multi-step to live in one transaction\n * (ADR-0119 D2, #4617).\n *\n * ## Why this is framework-owned rather than four hand-rolled copies\n *\n * Four migration-class consumers independently converged on the same four\n * moves — dry-run preflight, an undo journal, LIFO compensation, re-entrant\n * forward recovery: ADR-0105 D13 promotion, ADR-0117 D8's ownership backfill,\n * the org lifecycle transitions, and ADR-0119 D10's master-data distribution\n * (#4585). One copy is engineering. Four is platform debt, and the fourth\n * author would have had to rediscover the `chunk_done`-inside-the-transaction\n * subtlety below from scratch — or, far more likely, not rediscover it.\n *\n * ## Why a journal at all, given ADR-0034 gave us transactions\n *\n * ADR-0119 D1 made `engine.transaction()` reachable through the contract, but\n * a transaction cannot be the whole answer here:\n *\n * - a million-row backfill cannot hold one write-lock for its duration;\n * - `driver-memory`'s `beginTransaction` deep-clones the entire database, so\n * \"just wrap the whole thing\" is O(db) per begin;\n * - `ObjectQL.transaction()` binds the DEFAULT driver only, so a migration\n * spanning datasources silently commits part of its work outside it;\n * - a process KILLED — as distinct from a thrown error — defeats in-process\n * rollback entirely, and that is the case operators actually hit.\n *\n * So the unit of atomicity is the CHUNK, and durability across chunks is the\n * journal. Everything else in this file follows from that one sentence.\n *\n * ## The invariant that carries the whole design\n *\n * `chunk_done(i)` is written INSIDE the chunk's own transaction, so\n * `done ⇔ committed` holds by construction rather than by luck.\n * `chunk_started(i)` is written autonomously BEFORE it. A reader who \"tidies\"\n * that asymmetry destroys recovery: it is what gives `started ∧ ¬done` exactly\n * one meaning — **the outcome is unknown** — which is the only state a crash\n * can leave and the only state recovery has to reason about.\n *\n * ## Delivery semantics: at-least-once, idempotency is the caller's job\n *\n * Inherited verbatim from `./bulk-write.ts` rather than re-derived, because a\n * second delivery-semantics story in the same codebase is a second thing to\n * get subtly wrong. Forward and compensate callbacks receive an `attempt`\n * counter; `attempt > 1` means the previous outcome is UNKNOWN — the write may\n * or may not have committed — and the callback must recheck by natural key\n * before re-writing. That is the same contract the seed loader and import\n * runner already honour on `attempt > 1`.\n */\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport type { IObjectQLEngine } from '@objectstack/spec/contracts';\nimport {\n MIGRATION_JOURNAL_OBJECT,\n type MigrationJournalEvent,\n type MigrationJournalKind,\n type MigrationOnCrashPolicy,\n} from '@objectstack/spec/system';\n\n/** Journal writes and recovery reads run as the platform, never as a user. */\nconst SYSTEM_CTX = { isSystem: true } as const;\n\n/** Rows per chunk when a plan does not choose. Matches `bulk-write.ts`. */\nconst DEFAULT_CHUNK_SIZE = 200;\n\n/**\n * Can this runtime actually roll back? — the ADR-0119 D4 gate, shared.\n *\n * Exported from `@objectstack/core` and consumed by\n * `@objectstack/metadata-protocol`'s `batchData` (which depends on core, so\n * the direction is legal) so the two cannot drift. They were the same two-line\n * condition written twice, which is precisely the shape that drifts by one\n * clause and leaves one caller believing it has atomicity it does not have.\n *\n * TWO levels, both necessary. `engine.transaction()` exists but runs the\n * callback with NO transaction and NO rollback when the default driver lacks\n * `beginTransaction` — a declared caveat of the contract member (ADR-0119 D1),\n * and one that turns \"atomic\" back into a lie precisely where it matters. So\n * where the driver registry is inspectable the driver is checked too; where it\n * is not (test doubles), the engine-level probe is all there is.\n *\n * A type predicate, not a bare boolean: every caller's next move is to CALL\n * `transaction`, and on the host surfaces that declare it optionally\n * (`MetadataHostEngine`) a boolean would leave each one re-narrowing by hand —\n * which is the same restatement this helper exists to remove.\n */\nexport function engineCanRollBack<T>(engine: T): engine is T & EngineWithTransaction {\n const e = engine as {\n transaction?: unknown;\n getDefaultDriverName?: () => string | undefined;\n getDriverByName?: (name: string) => unknown;\n } | null | undefined;\n if (typeof e?.transaction !== 'function') return false;\n const defaultDriverName = e.getDefaultDriverName?.();\n const defaultDriver = defaultDriverName ? e.getDriverByName?.(defaultDriverName) : undefined;\n return !defaultDriver || typeof (defaultDriver as { beginTransaction?: unknown }).beginTransaction === 'function';\n}\n\n/**\n * What {@link engineCanRollBack} proves is present.\n *\n * Typed FROM the contract rather than transcribed from it (#5696): a hand-copy\n * mirrors the signature only until the contract moves, and this one had already\n * started to — it predates `opts.require` and the callback's `owned` argument.\n * ADR-0119 D1 blessed exactly this shape for the narrow host surfaces\n * (`transaction?: IObjectQLEngine['transaction']`); a *narrow* surface may stay\n * narrow, but it may not drift from the real signature.\n */\nexport interface EngineWithTransaction {\n transaction: IObjectQLEngine['transaction'];\n}\n\n/** What a forward/compensate callback is told about the chunk it is running. */\nexport interface MigrationChunkContext {\n readonly runId: string;\n /** Run-global chunk index — the LIFO ordering key, stable across a resume. */\n readonly chunkIndex: number;\n /**\n * 1 on the first try. `> 1` means a previous attempt's outcome is UNKNOWN:\n * recheck by natural key before re-writing (see this file's header).\n */\n readonly attempt: number;\n /**\n * The transaction-bound execution context. Thread it to every engine call\n * this callback makes — `engine.insert(obj, row, { context })` — so the\n * write joins the chunk's transaction instead of committing beside it.\n */\n readonly context: unknown;\n}\n\n/** One step of a plan. Steps run in declaration order; each is chunked. */\nexport interface MigrationPlanStep<TRow = unknown> {\n readonly name: string;\n /**\n * Read-only preflight. Throw to refuse the run. Runs for EVERY step before\n * any step writes — a plan that would fail at step 3 must not have written\n * step 1 (ADR-0117 D8's fail-closed enable gate, generalized).\n */\n preflight?(engine: IObjectQLEngine): Promise<void>;\n /** The rows this step processes. Called once, before chunking. */\n load(engine: IObjectQLEngine): Promise<TRow[]>;\n /** Forward work for one chunk. Runs INSIDE the chunk's transaction. */\n forward(rows: TRow[], ctx: MigrationChunkContext, engine: IObjectQLEngine): Promise<void>;\n /**\n * Undo one previously-committed chunk. Runs in its OWN transaction.\n * A step without one makes the plan non-compensable — which the runner\n * refuses up front rather than discovering at the worst possible moment\n * (see {@link runMigrationJournal}'s preflight).\n */\n compensate?(rows: TRow[], ctx: MigrationChunkContext, engine: IObjectQLEngine): Promise<void>;\n}\n\nexport interface MigrationPlan {\n /** Stable plan id. Part of the plan hash; identifies the plan across runs. */\n readonly id: string;\n /** Optional join to `sys_migration.id` when this plan implements a named migration. */\n readonly migrationId?: string;\n readonly steps: ReadonlyArray<MigrationPlanStep<any>>;\n readonly chunkSize?: number;\n /**\n * What a REDISCOVERED (crashed) run should do. Note this governs restart\n * only — an in-run failure always compensates, because the runner is still\n * alive to do it and a half-applied plan is nobody's intent.\n */\n readonly onCrash?: MigrationOnCrashPolicy;\n}\n\n/** One chunk in the run-global chunk plan. */\nexport interface MigrationChunk {\n /** Run-global index, 0-based, stable for a given plan hash. */\n readonly index: number;\n readonly stepIndex: number;\n readonly stepName: string;\n readonly offset: number;\n readonly length: number;\n}\n\nexport interface MigrationRunResult {\n readonly runId: string;\n /**\n * `completed` — every chunk committed.\n * `compensated` — a chunk failed and every committed chunk was undone.\n * `failed` — a chunk failed AND compensation could not finish. The database\n * is in a partial state that needs a human; the journal says exactly where.\n */\n readonly status: 'completed' | 'compensated' | 'failed';\n readonly chunksTotal: number;\n readonly chunksCommitted: number;\n readonly chunksCompensated: number;\n readonly planHash: string;\n /** The failure that ended a non-`completed` run. */\n readonly error?: unknown;\n}\n\n/**\n * Where a resume finds the plan it has to re-run (#4617).\n *\n * A journal cannot hold a plan. `forward` and `compensate` are FUNCTIONS, and\n * the rows a chunk covers are produced by `load()` against the live database —\n * none of it survives a process boundary, which is why the journal records the\n * plan HASH rather than the plan. So recovery needs the plan handed back to it\n * by whoever owns the code, and that is what this registry is: the seam between\n * \"the journal knows a run stopped at chunk 7\" and \"something in this process\n * knows what chunk 7 was supposed to do\".\n *\n * Registered as the `migration-plans` kernel service. An interrupted run whose\n * plan no loaded plugin registers is REPORTED, never silently skipped — the\n * operator is told which plan id is missing, because \"nothing to resume\" and\n * \"the code that owns this run is not loaded\" are different facts and only one\n * of them is safe to ignore.\n */\nexport interface MigrationPlanProvider {\n register(plan: MigrationPlan): void;\n get(planId: string): MigrationPlan | undefined;\n list(): MigrationPlan[];\n}\n\n/** The default {@link MigrationPlanProvider}. Last registration for an id wins. */\nexport class MigrationPlanRegistry implements MigrationPlanProvider {\n private readonly plans = new Map<string, MigrationPlan>();\n\n register(plan: MigrationPlan): void {\n this.plans.set(plan.id, plan);\n }\n\n get(planId: string): MigrationPlan | undefined {\n return this.plans.get(planId);\n }\n\n list(): MigrationPlan[] {\n return [...this.plans.values()];\n }\n}\n\n/** A run found by {@link findInterruptedRuns} — started, never concluded. */\nexport interface InterruptedRun {\n readonly runId: string;\n readonly planId: string;\n readonly planHash: string;\n readonly migrationId?: string;\n readonly startedAt?: string;\n /** Chunks whose `chunk_done` is present — known committed. */\n readonly committedChunks: number[];\n /** Chunks with `chunk_started` and no `chunk_done` — outcome UNKNOWN. */\n readonly unknownChunks: number[];\n readonly compensatedChunks: number[];\n}\n\n/** Raised when the runner refuses to start or to resume. Never a partial run. */\nexport class MigrationJournalRefusal extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = 'MigrationJournalRefusal';\n this.code = code;\n }\n}\n\n// ── plan shape ────────────────────────────────────────────────────────────\n\n/** Flatten steps × rows into the run-global chunk list. */\nexport function planChunks(\n plan: MigrationPlan,\n rowCounts: readonly number[],\n chunkSize = plan.chunkSize ?? DEFAULT_CHUNK_SIZE,\n): MigrationChunk[] {\n const size = Math.max(1, chunkSize);\n const chunks: MigrationChunk[] = [];\n plan.steps.forEach((step, stepIndex) => {\n const total = rowCounts[stepIndex] ?? 0;\n for (let offset = 0; offset < total; offset += size) {\n chunks.push({\n index: chunks.length,\n stepIndex,\n stepName: step.name,\n offset,\n length: Math.min(size, total - offset),\n });\n }\n });\n return chunks;\n}\n\n/**\n * Hash the plan SHAPE — id, step names, and the chunk boundaries.\n *\n * Resuming a changed plan against an old journal would apply chunk boundaries\n * the journal never described: \"chunk 7 done\" would name a different range of\n * different rows, and the resume would skip work it never did. So the hash\n * covers exactly what a chunk index means, and a mismatch REFUSES.\n */\nexport function hashMigrationPlan(plan: MigrationPlan, chunks: readonly MigrationChunk[]): string {\n const shape = JSON.stringify({\n id: plan.id,\n steps: plan.steps.map((s) => s.name),\n chunks: chunks.map((c) => [c.stepIndex, c.offset, c.length]),\n });\n return createHash('sha256').update(shape, 'utf8').digest('hex').slice(0, 32);\n}\n\n// ── journal I/O ───────────────────────────────────────────────────────────\n\n/**\n * Append one event.\n *\n * `execContext` is the transaction-bound context when the event must share a\n * chunk's fate (`chunk_done`, `compensated`) and undefined when it must NOT\n * (`chunk_started`, and every run-level event). Passing the wrong one is the\n * single most consequential mistake available in this file — see the header.\n */\nasync function appendEvent(\n engine: IObjectQLEngine,\n event: MigrationJournalEvent,\n execContext?: unknown,\n): Promise<void> {\n await engine.insert(\n MIGRATION_JOURNAL_OBJECT,\n { ...event, created_at: event.created_at ?? new Date().toISOString() },\n { context: execContext ?? { ...SYSTEM_CTX } },\n );\n}\n\n/**\n * Every event for a run, ordered by `seq`.\n *\n * Sorted in memory, deliberately. `seq` is the ordering authority (wall-clock\n * stamps tie at coarse resolution and skew), and a run's journal is bounded by\n * its chunk count, so this costs nothing and removes recovery's dependence on\n * driver-side sort behaviour — which is not something a recovery path should\n * be discovering the edges of.\n */\nexport async function readRunJournal(\n engine: IObjectQLEngine,\n runId: string,\n): Promise<MigrationJournalEvent[]> {\n const rows = (await engine.find(\n MIGRATION_JOURNAL_OBJECT,\n { where: { run_id: runId } },\n { context: { ...SYSTEM_CTX } },\n )) as MigrationJournalEvent[];\n return [...(rows ?? [])].sort((a, b) => Number(a.seq) - Number(b.seq));\n}\n\n/** Chunk indices carrying `kind`, as a set. */\nfunction chunkSetOf(events: readonly MigrationJournalEvent[], kind: MigrationJournalKind): Set<number> {\n const out = new Set<number>();\n for (const e of events) {\n if (e.kind === kind && typeof e.chunk_index === 'number') out.add(e.chunk_index);\n }\n return out;\n}\n\n/**\n * Runs that started and never concluded — the boot scanner's input.\n *\n * \"Concluded\" means `run_done` (finished forward) or `run_failed` with every\n * committed chunk compensated (finished backward). Anything else is a run that\n * stopped mid-flight and still owes the operator an answer.\n */\nexport async function findInterruptedRuns(engine: IObjectQLEngine): Promise<InterruptedRun[]> {\n const started = (await engine.find(\n MIGRATION_JOURNAL_OBJECT,\n { where: { kind: 'run_started' } },\n { context: { ...SYSTEM_CTX } },\n )) as MigrationJournalEvent[];\n\n const out: InterruptedRun[] = [];\n for (const start of started ?? []) {\n const events = await readRunJournal(engine, start.run_id);\n if (events.some((e) => e.kind === 'run_done')) continue;\n\n const committed = chunkSetOf(events, 'chunk_done');\n const compensated = chunkSetOf(events, 'compensated');\n const outstanding = [...committed].filter((i) => !compensated.has(i));\n // A failed run whose committed chunks were all undone is settled: it ended\n // backward, on purpose, and its rows prove it.\n if (events.some((e) => e.kind === 'run_failed') && outstanding.length === 0) continue;\n\n const unknown = [...chunkSetOf(events, 'chunk_started')].filter((i) => !committed.has(i));\n let planId = start.run_id;\n try {\n planId = start.detail ? (JSON.parse(start.detail).planId ?? start.run_id) : start.run_id;\n } catch {\n // A malformed detail payload must not hide an interrupted run — the run\n // is still reported, just without its friendly plan id.\n }\n out.push({\n runId: start.run_id,\n planId,\n planHash: start.plan_hash ?? '',\n migrationId: start.migration_id,\n startedAt: start.created_at,\n committedChunks: [...committed].sort((a, b) => a - b),\n unknownChunks: unknown.sort((a, b) => a - b),\n compensatedChunks: [...compensated].sort((a, b) => a - b),\n });\n }\n return out;\n}\n\n// ── the runner ────────────────────────────────────────────────────────────\n\nexport interface RunMigrationJournalOptions {\n /** Supply to resume an existing run; omit to start a new one. */\n readonly runId?: string;\n readonly chunkSize?: number;\n /** Injectable for deterministic tests. */\n readonly now?: () => string;\n}\n\ninterface LoadedPlan {\n readonly chunks: MigrationChunk[];\n readonly planHash: string;\n readonly rowsByStep: unknown[][];\n}\n\n/** Load every step's rows, derive the chunk plan, hash it. */\nasync function loadPlan(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n chunkSize?: number,\n): Promise<LoadedPlan> {\n const rowsByStep: unknown[][] = [];\n for (const step of plan.steps) rowsByStep.push((await step.load(engine)) ?? []);\n const chunks = planChunks(plan, rowsByStep.map((r) => r.length), chunkSize ?? plan.chunkSize);\n return { chunks, planHash: hashMigrationPlan(plan, chunks), rowsByStep };\n}\n\n/**\n * Run `plan` under the journal, or resume a run left behind by a crash.\n *\n * Refuses (never partially runs) when: the runtime cannot roll back; any\n * step's preflight fails; the plan declares `onCrash: 'compensate'` but some\n * step cannot compensate; or a resume's plan hash disagrees with the journal.\n */\nexport async function runMigrationJournal(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n options: RunMigrationJournalOptions = {},\n): Promise<MigrationRunResult> {\n const now = options.now ?? (() => new Date().toISOString());\n\n // ── capability gate ───────────────────────────────────────────────────\n // Refuse rather than degrade. A runner whose chunks are not actually\n // atomic writes `chunk_done` rows that mean nothing, and a journal that\n // cannot be trusted is worse than no journal — it will be believed.\n if (!engineCanRollBack(engine)) {\n throw new MigrationJournalRefusal(\n 'NOT_IMPLEMENTED',\n `Migration plan '${plan.id}' requires engine transaction support; this runtime cannot roll back. ` +\n `The journal's chunk_done markers would not mean \"committed\", so the run is refused rather than started.`,\n );\n }\n\n const { chunks, planHash, rowsByStep } = await loadPlan(engine, plan, options.chunkSize);\n\n // ── resume bookkeeping ────────────────────────────────────────────────\n const resuming = Boolean(options.runId);\n const runId = options.runId ?? randomUUID();\n let events: MigrationJournalEvent[] = [];\n let seq = 0;\n let committed = new Set<number>();\n let compensated = new Set<number>();\n const attemptsByChunk = new Map<number, number>();\n\n if (resuming) {\n events = await readRunJournal(engine, runId);\n if (events.length === 0) {\n throw new MigrationJournalRefusal('NO_SUCH_RUN', `No journal rows for run '${runId}'.`);\n }\n const start = events.find((e) => e.kind === 'run_started');\n if (start?.plan_hash && start.plan_hash !== planHash) {\n // The plan changed under a journal that describes the old one. Chunk 7\n // in the journal and chunk 7 in this plan are different rows; resuming\n // would skip work that was never done.\n throw new MigrationJournalRefusal(\n 'PLAN_CHANGED',\n `Refusing to resume run '${runId}': plan hash ${planHash} does not match the journal's ${start.plan_hash}. ` +\n `The chunk boundaries recorded in the journal describe a different plan.`,\n );\n }\n if (events.some((e) => e.kind === 'run_done')) {\n return {\n runId, status: 'completed', chunksTotal: chunks.length,\n chunksCommitted: chunkSetOf(events, 'chunk_done').size,\n chunksCompensated: chunkSetOf(events, 'compensated').size, planHash,\n };\n }\n seq = events.reduce((m, e) => Math.max(m, Number(e.seq) + 1), 0);\n committed = chunkSetOf(events, 'chunk_done');\n compensated = chunkSetOf(events, 'compensated');\n for (const e of events) {\n if (e.kind === 'chunk_started' && typeof e.chunk_index === 'number') {\n attemptsByChunk.set(e.chunk_index, (attemptsByChunk.get(e.chunk_index) ?? 0) + 1);\n }\n }\n }\n\n // ── preflight ─────────────────────────────────────────────────────────\n // Every validator runs before any write, so a plan that would fail at step 3\n // has not written step 1. On a resume this re-runs too: the world moved\n // while the process was dead, and the reason to refuse may have appeared\n // since.\n for (const step of plan.steps) {\n if (!step.preflight) continue;\n try {\n await step.preflight(engine);\n } catch (err) {\n throw new MigrationJournalRefusal(\n 'PREFLIGHT_FAILED',\n `Migration plan '${plan.id}' refused: preflight for step '${step.name}' failed: ${errText(err)}`,\n );\n }\n }\n\n // A plan that says \"undo me on crash\" must be able to. Discovering that it\n // cannot at compensation time means discovering it with rows already\n // written and no way back.\n if (plan.onCrash === 'compensate') {\n const missing = plan.steps.filter((s) => !s.compensate).map((s) => s.name);\n if (missing.length > 0) {\n throw new MigrationJournalRefusal(\n 'NOT_COMPENSABLE',\n `Migration plan '${plan.id}' declares onCrash: 'compensate' but step(s) ${missing.join(', ')} declare no compensate().`,\n );\n }\n }\n\n const rowsOf = (c: MigrationChunk): unknown[] => rowsByStep[c.stepIndex].slice(c.offset, c.offset + c.length);\n const next = (): number => seq++;\n\n if (!resuming) {\n await appendEvent(engine, {\n run_id: runId, seq: next(), kind: 'run_started', plan_hash: planHash,\n migration_id: plan.migrationId, created_at: now(),\n detail: JSON.stringify({\n planId: plan.id,\n onCrash: plan.onCrash ?? 'resume',\n chunks: chunks.map((c) => ({ i: c.index, step: c.stepName, offset: c.offset, length: c.length })),\n }),\n });\n }\n\n // A rediscovered run whose policy is 'compensate' does not go forward at\n // all — it unwinds what it already did and stops.\n if (resuming && plan.onCrash === 'compensate') {\n return await unwind(engine, plan, {\n runId, planHash, chunks, rowsOf, next, now,\n committed, compensated, chunksTotal: chunks.length,\n cause: new Error(`run '${runId}' rediscovered after interruption; plan policy is compensate`),\n });\n }\n\n // ── forward ───────────────────────────────────────────────────────────\n for (const chunk of chunks) {\n if (committed.has(chunk.index)) continue; // already durable — skip, do not redo\n const attempt = (attemptsByChunk.get(chunk.index) ?? 0) + 1;\n attemptsByChunk.set(chunk.index, attempt);\n const step = plan.steps[chunk.stepIndex];\n const rows = rowsOf(chunk);\n\n // Autonomous, BEFORE the transaction: this is what makes an interrupted\n // chunk visible as \"started, outcome unknown\" rather than invisible.\n await appendEvent(engine, {\n run_id: runId, seq: next(), kind: 'chunk_started',\n chunk_index: chunk.index, attempt, migration_id: plan.migrationId, created_at: now(),\n });\n\n try {\n await engine.transaction(async (trxCtx: unknown) => {\n await step.forward(rows, { runId, chunkIndex: chunk.index, attempt, context: trxCtx }, engine);\n // INSIDE the transaction — `done ⇔ committed`, not a race.\n await appendEvent(\n engine,\n {\n run_id: runId, seq: next(), kind: 'chunk_done',\n chunk_index: chunk.index, attempt, migration_id: plan.migrationId, created_at: now(),\n },\n trxCtx,\n );\n }, { ...SYSTEM_CTX });\n committed.add(chunk.index);\n } catch (err) {\n // The chunk rolled back, so nothing of it is on disk — including its\n // `chunk_done`. Unwind what earlier chunks committed.\n return await unwind(engine, plan, {\n runId, planHash, chunks, rowsOf, next, now,\n committed, compensated, chunksTotal: chunks.length, cause: err,\n });\n }\n }\n\n await appendEvent(engine, {\n run_id: runId, seq: next(), kind: 'run_done', migration_id: plan.migrationId, created_at: now(),\n });\n return {\n runId, status: 'completed', chunksTotal: chunks.length,\n chunksCommitted: committed.size, chunksCompensated: compensated.size, planHash,\n };\n}\n\ninterface UnwindArgs {\n runId: string;\n planHash: string;\n chunks: readonly MigrationChunk[];\n rowsOf: (c: MigrationChunk) => unknown[];\n next: () => number;\n now: () => string;\n committed: Set<number>;\n compensated: Set<number>;\n chunksTotal: number;\n cause: unknown;\n}\n\n/**\n * LIFO compensation over committed chunks.\n *\n * Newest-first because later chunks may depend on earlier ones; undoing in\n * commit order can hit a state the compensator was never written for.\n *\n * A compensation failure HALTS and is journalled — never swallowed, never\n * \"best effort, carry on\". Continuing past it would produce a database whose\n * state no journal describes, which is the one outcome this whole file exists\n * to prevent. The run ends `failed`, and the rows say exactly which chunk\n * resisted.\n */\nasync function unwind(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n a: UnwindArgs,\n): Promise<MigrationRunResult> {\n const order = [...a.committed].sort((x, y) => y - x); // newest-first\n for (const index of order) {\n if (a.compensated.has(index)) continue;\n const chunk = a.chunks[index];\n const step = plan.steps[chunk.stepIndex];\n\n if (!step.compensate) {\n // Nothing to undo this with. Say so loudly and stop — a silent skip\n // would leave the row written and the journal claiming a clean unwind.\n await appendEvent(engine, {\n run_id: a.runId, seq: a.next(), kind: 'run_failed',\n chunk_index: index, migration_id: plan.migrationId, created_at: a.now(),\n detail: JSON.stringify({\n phase: 'compensate', reason: 'step declares no compensate()',\n step: step.name, cause: errText(a.cause),\n }),\n });\n return {\n runId: a.runId, status: 'failed', chunksTotal: a.chunksTotal,\n chunksCommitted: a.committed.size, chunksCompensated: a.compensated.size,\n planHash: a.planHash, error: a.cause,\n };\n }\n\n const attempt = 1;\n try {\n await engine.transaction(async (trxCtx: unknown) => {\n await step.compensate!(a.rowsOf(chunk), { runId: a.runId, chunkIndex: index, attempt, context: trxCtx }, engine);\n await appendEvent(\n engine,\n {\n run_id: a.runId, seq: a.next(), kind: 'compensated',\n chunk_index: index, attempt, migration_id: plan.migrationId, created_at: a.now(),\n },\n trxCtx,\n );\n }, { ...SYSTEM_CTX });\n a.compensated.add(index);\n } catch (err) {\n await appendEvent(engine, {\n run_id: a.runId, seq: a.next(), kind: 'run_failed',\n chunk_index: index, migration_id: plan.migrationId, created_at: a.now(),\n detail: JSON.stringify({\n phase: 'compensate', step: step.name,\n error: errText(err), cause: errText(a.cause),\n }),\n });\n return {\n runId: a.runId, status: 'failed', chunksTotal: a.chunksTotal,\n chunksCommitted: a.committed.size, chunksCompensated: a.compensated.size,\n planHash: a.planHash, error: err,\n };\n }\n }\n\n await appendEvent(engine, {\n run_id: a.runId, seq: a.next(), kind: 'run_failed',\n migration_id: plan.migrationId, created_at: a.now(),\n detail: JSON.stringify({ phase: 'forward', error: errText(a.cause), compensated: [...a.compensated].sort((x, y) => x - y) }),\n });\n return {\n runId: a.runId, status: 'compensated', chunksTotal: a.chunksTotal,\n chunksCommitted: a.committed.size, chunksCompensated: a.compensated.size,\n planHash: a.planHash, error: a.cause,\n };\n}\n\n/** Resume a run the journal says was interrupted. Thin alias for intent at call sites. */\nexport async function resumeMigrationJournal(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n runId: string,\n options: Omit<RunMigrationJournalOptions, 'runId'> = {},\n): Promise<MigrationRunResult> {\n return runMigrationJournal(engine, plan, { ...options, runId });\n}\n\nfunction errText(err: unknown): string {\n if (err instanceof Error) return err.message;\n try {\n return String(err);\n } catch {\n return '<unprintable error>';\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Runtime resolution of filter placeholders — the server-side half of the\n * `{token}` contract that `@objectstack/spec` declares (framework#3582).\n *\n * `date-macros.zod.ts` and `context-tokens.zod.ts` freeze the *vocabulary*;\n * `@objectstack/lint`'s `validate-filter-tokens` rejects a token outside it at\n * authoring time. Neither one ever substituted a value: every server-side\n * consumer handed the literal `'{current_year_start}'` to the database, where\n * it compared as a string and matched nothing. The failure was invisible —\n * an empty widget, an unfiltered list — so apps worked around it by computing\n * dates at module load, freezing \"this year\" into the built artifact.\n *\n * This module is the missing evaluator. It walks a filter tree and replaces\n * every fully-wrapped placeholder with a concrete value:\n *\n * { close_date: { $gte: '{current_year_start}' } }\n * → { close_date: { $gte: '2026-01-01' } }\n * { owner: '{current_user_id}' }\n * → { owner: 'usr_7f3a…' }\n *\n * # Output form: ISO strings, not driver-native values\n *\n * Date tokens resolve to `YYYY-MM-DD` (or a full ISO timestamp for the\n * sub-day tokens `{now}` / `{N_hours_ago}` / `{N_minutes_ago}`), exactly the\n * form the spec's module doc promises the data engine sees. Translating that\n * to a column's on-disk form is the DRIVER's job and already exists —\n * `SqlDriver.coerceFilterValue` / `temporalFilterValue` turn an ISO comparand\n * into SQLite epoch-ms or leave it alone on native-timestamp dialects. Emitting\n * a driver-native value here would fork that convention into a second source of\n * truth and break the moment a query crosses datasources.\n *\n * # Period `_end` is the last calendar DAY, not the last instant\n *\n * `{current_year_end}` is `2026-12-31`, per the spec's own\n * `DATE_MACRO_DESCRIPTIONS` (\"Dec 31 of this year\"). On a `datetime` column\n * that means `<= {current_year_end}` excludes everything after midnight on the\n * 31st — the classic half-open-range trap. Authors filtering a timestamp want\n * `< {next_year_start}`. This is a documented property of the vocabulary, not\n * something the resolver may quietly \"fix\": silently widening a bound would\n * make the same token mean different things on different column types.\n *\n * # An unknown token throws\n *\n * A value that is entirely `{something}` is a placeholder by construction — no\n * author means the literal six characters `{foo}`. Passing an unrecognised one\n * through is precisely the silent-zero bug this module exists to end, so it is\n * a hard error carrying the near-miss suggestion (`{current_user}` →\n * `{current_user_id}`). Values that merely CONTAIN braces are left untouched.\n */\n\nimport {\n classifyFilterToken,\n parseDateMacroParam,\n type DateMacroUnit,\n} from '@objectstack/spec/data';\nimport { calendarPartsInTzOrUtc } from './datetime.js';\n\n/**\n * The slice of an execution context the resolver reads. Structural on purpose —\n * see {@link filterTokenContextFrom}.\n */\nexport interface ExecutionContextLike {\n readonly userId?: string;\n readonly tenantId?: string;\n readonly timezone?: string;\n}\n\n/**\n * The request-scoped values a placeholder can resolve against.\n *\n * `now` is captured ONCE per resolve call so every token in one filter shares\n * an instant — otherwise a `$gte {current_month_start}` / `$lt\n * {next_month_start}` pair evaluated microseconds apart could straddle a\n * month boundary and silently drop a row.\n */\nexport interface FilterTokenResolutionContext {\n /** Reference instant. Defaults to `new Date()` at call time. */\n now?: Date;\n /** IANA reference timezone for calendar boundaries. Defaults to UTC. */\n timezone?: string;\n /** Resolves `{current_user_id}`. */\n userId?: string;\n /** Resolves `{current_org_id}`. */\n orgId?: string;\n}\n\n/**\n * Raised when a filter carries a placeholder outside the vocabulary.\n *\n * Carries `status`/`code` so the REST layer's generic 4xx passthrough maps it\n * to a **400 with a fixable message** rather than a 500: the caller's filter is\n * malformed, the server is fine. (Same convention plugin-sharing uses for its\n * record-scope denial — no runtime dependency in either direction.)\n */\nexport class UnknownFilterTokenError extends Error {\n readonly token: string;\n readonly suggestion?: string;\n readonly status = 400;\n readonly code = 'FILTER_TOKEN_UNKNOWN';\n\n constructor(token: string, suggestion?: string) {\n super(\n `Unresolvable filter placeholder \"{${token}}\". ` +\n (suggestion\n ? `Did you mean \"{${suggestion}}\"? `\n : 'Resolvable placeholders are the context tokens ({current_user_id}, ' +\n '{current_org_id}) and the date macros ({today}, {current_quarter_start}, ' +\n '{30_days_ago}, …). ') +\n 'Sending it to the data engine verbatim would compare it as a literal ' +\n 'string and match nothing, which is indistinguishable from an empty result.',\n );\n this.name = 'UnknownFilterTokenError';\n this.token = token;\n this.suggestion = suggestion;\n }\n}\n\n/**\n * Raised when a token IS in the vocabulary but the request carries no value\n * for it — an unauthenticated caller filtering on `{current_user_id}`.\n *\n * Distinct from {@link UnknownFilterTokenError} because the fix is different:\n * the metadata is correct, the context is not. Never silently resolves to\n * `null`/`undefined`, which on most drivers degrades to `IS NULL` and would\n * quietly hand back rows the filter was written to exclude.\n */\nexport class UnresolvedFilterTokenError extends Error {\n readonly token: string;\n /** 400, not 500 — see {@link UnknownFilterTokenError}. */\n readonly status = 400;\n readonly code = 'FILTER_TOKEN_UNRESOLVED';\n\n constructor(token: string, detail: string) {\n super(`Filter placeholder \"{${token}}\" cannot be resolved: ${detail}`);\n this.name = 'UnresolvedFilterTokenError';\n this.token = token;\n }\n}\n\n/** `YYYY-MM-DD` for a calendar day, zero-padded. */\nfunction ymd(year: number, month: number, day: number): string {\n const p = (n: number) => String(n).padStart(2, '0');\n return `${year}-${p(month)}-${p(day)}`;\n}\n\n/**\n * Calendar arithmetic is done on a UTC \"proxy\" date built from the reference\n * timezone's calendar parts. Working in UTC keeps the math free of DST jumps\n * (a local-midnight `Date` can shift by an hour when `setMonth` crosses a\n * transition); the zone only decides WHICH calendar day \"now\" is, which\n * {@link calendarPartsInTzOrUtc} answers from the platform tz database.\n */\nfunction proxyDay(now: Date, timezone?: string): Date {\n const { year, month, day } = calendarPartsInTzOrUtc(now, timezone);\n return new Date(Date.UTC(year, month - 1, day));\n}\n\nconst asYmd = (d: Date): string => ymd(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n\ntype PeriodKind = 'week' | 'month' | 'quarter' | 'year';\n\n/** Monday-based week start — matches the spec's \"Monday 00:00 of this week\". */\nfunction startOfPeriod(kind: PeriodKind, d: Date): Date {\n const r = new Date(d.getTime());\n switch (kind) {\n case 'week': {\n const dow = (r.getUTCDay() + 6) % 7; // 0 = Monday\n r.setUTCDate(r.getUTCDate() - dow);\n return r;\n }\n case 'month':\n return new Date(Date.UTC(r.getUTCFullYear(), r.getUTCMonth(), 1));\n case 'quarter':\n return new Date(Date.UTC(r.getUTCFullYear(), Math.floor(r.getUTCMonth() / 3) * 3, 1));\n case 'year':\n return new Date(Date.UTC(r.getUTCFullYear(), 0, 1));\n }\n}\n\n/** Days in the given (0-based) month of `year`. */\nfunction daysInMonth(year: number, month: number): number {\n return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();\n}\n\n/**\n * Shift `d` by `n` months, CLAMPING the day to the target month's length.\n *\n * Bare `setUTCMonth(m - 1)` on the 31st rolls FORWARD into the following month\n * (Mar 31 minus one month = \"Feb 31\" = Mar 3), which would make\n * `{1_month_ago}` land after `{today}` on five days of the year. Clamping to\n * Feb 28 is what every calendar library does and the only answer an author\n * would call correct.\n */\nfunction addMonthsClamped(d: Date, n: number): Date {\n const year = d.getUTCFullYear();\n const month = d.getUTCMonth() + n;\n const targetYear = year + Math.floor(month / 12);\n const targetMonth = ((month % 12) + 12) % 12;\n const day = Math.min(d.getUTCDate(), daysInMonth(targetYear, targetMonth));\n return new Date(Date.UTC(\n targetYear, targetMonth, day,\n d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds(),\n ));\n}\n\n/** Shift `d` by `n` whole periods of `kind` (negative shifts backwards). */\nfunction addPeriods(kind: PeriodKind, d: Date, n: number): Date {\n switch (kind) {\n case 'week': {\n const r = new Date(d.getTime());\n r.setUTCDate(r.getUTCDate() + n * 7);\n return r;\n }\n case 'month': return addMonthsClamped(d, n);\n case 'quarter': return addMonthsClamped(d, n * 3);\n case 'year': return addMonthsClamped(d, n * 12);\n }\n}\n\n/** Shift `d` by `n` units of the parameterised grammar. */\nfunction addUnits(unit: DateMacroUnit, d: Date, n: number): Date {\n const r = new Date(d.getTime());\n switch (unit) {\n case 'minute': r.setUTCMinutes(r.getUTCMinutes() + n); return r;\n case 'hour': r.setUTCHours(r.getUTCHours() + n); return r;\n case 'day': r.setUTCDate(r.getUTCDate() + n); return r;\n case 'week': r.setUTCDate(r.getUTCDate() + n * 7); return r;\n // Month/year steps clamp rather than overflow — see addMonthsClamped.\n case 'month': return addMonthsClamped(d, n);\n case 'year': return addMonthsClamped(d, n * 12);\n }\n}\n\n/**\n * `current|last|next` × `week|month|quarter|year` × `start|end`, plus the bare\n * `week_start`-style aliases (which mean `current_`). Returns `undefined` when\n * the token is not a period token.\n */\nconst PERIOD_RE = /^(?:(current|last|next)_)?(week|month|quarter|year)_(start|end)$/;\n\nfunction resolvePeriodToken(token: string, today: Date): string | undefined {\n const m = PERIOD_RE.exec(token);\n if (!m) return undefined;\n const rel = (m[1] ?? 'current') as 'current' | 'last' | 'next';\n const kind = m[2] as PeriodKind;\n const bound = m[3] as 'start' | 'end';\n const offset = rel === 'last' ? -1 : rel === 'next' ? 1 : 0;\n\n // Normalize to the period's own start BEFORE stepping. Day 1 of a\n // month/quarter/year (and a Monday) shifts exactly — no month-length clamping\n // is involved at all — so the answer never depends on the clamp policy, and\n // the arithmetic reads the same for every `kind`.\n const periodStart = startOfPeriod(kind, addPeriods(kind, startOfPeriod(kind, today), offset));\n if (bound === 'start') return asYmd(periodStart);\n // `_end` = the last calendar DAY of the period: the day before the next\n // period begins. See the module doc on half-open ranges.\n const next = addPeriods(kind, periodStart, 1);\n next.setUTCDate(next.getUTCDate() - 1);\n return asYmd(next);\n}\n\n/**\n * Resolve one token NAME (the bit inside the braces) to its concrete value.\n * Throws {@link UnresolvedFilterTokenError} for a vocabulary token the request\n * carries no value for. Returns `undefined` only when the token is outside the\n * vocabulary — callers turn that into {@link UnknownFilterTokenError}.\n */\nexport function resolveFilterToken(\n token: string,\n ctx: FilterTokenResolutionContext = {},\n): unknown {\n const now = ctx.now ?? new Date();\n\n // ── Context tokens ────────────────────────────────────────────────────\n if (token === 'current_user_id') {\n if (!ctx.userId) {\n throw new UnresolvedFilterTokenError(\n token,\n 'the request has no authenticated user. A filter scoped to the signed-in ' +\n 'user cannot run for an anonymous or system caller — gate the surface on ' +\n 'authentication, or drop the token from the filter.',\n );\n }\n return ctx.userId;\n }\n if (token === 'current_org_id') {\n if (!ctx.orgId) {\n throw new UnresolvedFilterTokenError(\n token,\n 'the request carries no active organization (ExecutionContext.tenantId is ' +\n 'unset). Set the active org on the request, or drop the token from the filter.',\n );\n }\n return ctx.orgId;\n }\n\n // ── Date macros ───────────────────────────────────────────────────────\n const today = proxyDay(now, ctx.timezone);\n\n switch (token) {\n case 'now': return now.toISOString();\n case 'today': return asYmd(today);\n case 'yesterday': return asYmd(addUnits('day', today, -1));\n case 'tomorrow': return asYmd(addUnits('day', today, 1));\n }\n\n const period = resolvePeriodToken(token, today);\n if (period !== undefined) return period;\n\n const param = parseDateMacroParam(token);\n if (param) {\n const sign = param.direction === 'ago' ? -1 : 1;\n // Sub-day units are instants — they must keep their time-of-day, so they\n // shift `now` and render as a full ISO timestamp. Day-and-coarser units are\n // calendar quantities and render as `YYYY-MM-DD` off the reference day.\n if (param.unit === 'minute' || param.unit === 'hour') {\n return addUnits(param.unit, now, sign * param.n).toISOString();\n }\n return asYmd(addUnits(param.unit, today, sign * param.n));\n }\n\n return undefined;\n}\n\n/**\n * Does this tree contain any fully-wrapped placeholder at all?\n *\n * A read-only pre-pass so the overwhelmingly common case — an internal query\n * whose filter is entirely literal — costs one allocation-free walk instead of\n * a full structural copy. This runs on every server-side read, so \"no\n * placeholders\" must be close to free.\n */\nfunction hasFilterToken(node: unknown): boolean {\n if (typeof node === 'string') return classifyFilterToken(node) !== null;\n if (Array.isArray(node)) return node.some(hasFilterToken);\n if (node && typeof node === 'object' && !(node instanceof Date)) {\n return Object.values(node as Record<string, unknown>).some(hasFilterToken);\n }\n return false;\n}\n\n/**\n * Deep-replace every fully-wrapped placeholder in `filter` with its resolved\n * value, returning a NEW tree (the caller's metadata is never mutated — a view\n * or dataset definition is shared across requests, so resolving in place would\n * bake one request's user id, and one day's dates, into every later render).\n *\n * Returns the input unchanged, by reference, when it holds no placeholders.\n */\nexport function resolveFilterTokens<T>(\n filter: T,\n ctx: FilterTokenResolutionContext = {},\n): T {\n if (filter == null) return filter;\n if (!hasFilterToken(filter)) return filter;\n\n // One instant for the whole tree (see FilterTokenResolutionContext.now).\n const pinned: FilterTokenResolutionContext = { ...ctx, now: ctx.now ?? new Date() };\n\n const walk = (node: unknown): unknown => {\n if (typeof node === 'string') {\n const cls = classifyFilterToken(node);\n if (!cls) return node;\n if (cls.kind === 'unknown') throw new UnknownFilterTokenError(cls.token, cls.suggestion);\n const resolved = resolveFilterToken(cls.token, pinned);\n // `classifyFilterToken` already vouched for the token, so `undefined`\n // here would mean the spec vocabulary and this resolver have drifted\n // apart — surface it loudly rather than silently emitting `undefined`.\n if (resolved === undefined) throw new UnknownFilterTokenError(cls.token);\n return resolved;\n }\n if (Array.isArray(node)) return node.map(walk);\n if (node && typeof node === 'object') {\n // Dates and other class instances are comparands, not filter structure.\n if (node instanceof Date) return node;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(node as Record<string, unknown>)) out[k] = walk(v);\n return out;\n }\n return node;\n };\n\n return walk(filter) as T;\n}\n\n/**\n * Convenience bridge from an execution context to the resolver's inputs.\n * `{current_org_id}` reads `tenantId` — the active organization IS the tenant\n * on the read path (same value the RLS compiler binds to\n * `current_user.organization_id`).\n *\n * Typed structurally, not as `ExecutionContext`, so both the parsed context\n * (defaults applied) and the pre-parse `ExecutionContextInput` a caller holds\n * mid-pipeline satisfy it. The three fields read here are optional in both.\n */\nexport function filterTokenContextFrom(\n execCtx: ExecutionContextLike | undefined,\n now?: Date,\n): FilterTokenResolutionContext {\n return {\n now,\n timezone: execCtx?.timezone,\n userId: execCtx?.userId,\n orgId: execCtx?.tenantId,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n PluginHealthStatus, \n PluginHealthCheck, \n PluginHealthReport \n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from './logger.js';\nimport type { Plugin } from './types.js';\n\n/**\n * Plugin Health Monitor\n * \n * Monitors plugin health status and performs automatic recovery actions.\n * Implements the advanced lifecycle health monitoring protocol.\n */\nexport class PluginHealthMonitor {\n private logger: ObjectLogger;\n private healthChecks = new Map<string, PluginHealthCheck>();\n private healthStatus = new Map<string, PluginHealthStatus>();\n private healthReports = new Map<string, PluginHealthReport>();\n private checkIntervals = new Map<string, NodeJS.Timeout>();\n private failureCounters = new Map<string, number>();\n private successCounters = new Map<string, number>();\n private restartAttempts = new Map<string, number>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'HealthMonitor' });\n }\n\n /**\n * Register a plugin for health monitoring\n */\n registerPlugin(pluginName: string, config: PluginHealthCheck): void {\n this.healthChecks.set(pluginName, config);\n this.healthStatus.set(pluginName, 'unknown');\n this.failureCounters.set(pluginName, 0);\n this.successCounters.set(pluginName, 0);\n this.restartAttempts.set(pluginName, 0);\n\n this.logger.info('Plugin registered for health monitoring', { \n plugin: pluginName,\n interval: config.interval \n });\n }\n\n /**\n * Start monitoring a plugin\n */\n startMonitoring(pluginName: string, plugin: Plugin): void {\n const config = this.healthChecks.get(pluginName);\n if (!config) {\n this.logger.warn('Cannot start monitoring - plugin not registered', { plugin: pluginName });\n return;\n }\n\n // Clear any existing interval\n this.stopMonitoring(pluginName);\n\n // Set up periodic health checks\n const interval = setInterval(() => {\n this.performHealthCheck(pluginName, plugin, config).catch(error => {\n this.logger.error('Health check failed with error', { \n plugin: pluginName, \n error \n });\n });\n }, config.interval);\n\n this.checkIntervals.set(pluginName, interval);\n this.logger.info('Health monitoring started', { plugin: pluginName });\n\n // Perform initial health check\n this.performHealthCheck(pluginName, plugin, config).catch(error => {\n this.logger.error('Initial health check failed', { \n plugin: pluginName, \n error \n });\n });\n }\n\n /**\n * Stop monitoring a plugin\n */\n stopMonitoring(pluginName: string): void {\n const interval = this.checkIntervals.get(pluginName);\n if (interval) {\n clearInterval(interval);\n this.checkIntervals.delete(pluginName);\n this.logger.info('Health monitoring stopped', { plugin: pluginName });\n }\n }\n\n /**\n * Perform a health check on a plugin\n */\n private async performHealthCheck(\n pluginName: string,\n plugin: Plugin,\n config: PluginHealthCheck\n ): Promise<void> {\n const startTime = Date.now();\n let status: PluginHealthStatus = 'healthy';\n let message: string | undefined;\n const checks: Array<{ name: string; status: 'passed' | 'failed' | 'warning'; message?: string }> = [];\n\n try {\n // Check if plugin has a custom health check method\n if (config.checkMethod && typeof (plugin as any)[config.checkMethod] === 'function') {\n const checkResult = await this.raceCheckTimeout(\n (plugin as any)[config.checkMethod](),\n config.timeout,\n `Health check timeout after ${config.timeout}ms`\n );\n\n if (checkResult === false || (checkResult && checkResult.status === 'unhealthy')) {\n status = 'unhealthy';\n message = checkResult?.message || 'Custom health check failed';\n checks.push({ name: config.checkMethod, status: 'failed', message });\n } else {\n checks.push({ name: config.checkMethod, status: 'passed' });\n }\n } else {\n // Default health check - just verify plugin is loaded\n checks.push({ name: 'plugin-loaded', status: 'passed' });\n }\n\n // Update counters based on result\n if (status === 'healthy') {\n this.successCounters.set(pluginName, (this.successCounters.get(pluginName) || 0) + 1);\n this.failureCounters.set(pluginName, 0);\n\n // Recover from unhealthy state if we have enough successes\n const currentStatus = this.healthStatus.get(pluginName);\n if (currentStatus === 'unhealthy' || currentStatus === 'degraded') {\n const successCount = this.successCounters.get(pluginName) || 0;\n if (successCount >= config.successThreshold) {\n this.healthStatus.set(pluginName, 'healthy');\n this.logger.info('Plugin recovered to healthy state', { plugin: pluginName });\n } else {\n this.healthStatus.set(pluginName, 'recovering');\n }\n } else {\n this.healthStatus.set(pluginName, 'healthy');\n }\n } else {\n this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);\n this.successCounters.set(pluginName, 0);\n\n const failureCount = this.failureCounters.get(pluginName) || 0;\n if (failureCount >= config.failureThreshold) {\n this.healthStatus.set(pluginName, 'unhealthy');\n this.logger.warn('Plugin marked as unhealthy', { \n plugin: pluginName, \n failures: failureCount \n });\n\n // Attempt auto-restart if configured\n if (config.autoRestart) {\n await this.attemptRestart(pluginName, plugin, config);\n }\n } else {\n this.healthStatus.set(pluginName, 'degraded');\n }\n }\n } catch (error) {\n status = 'failed';\n message = error instanceof Error ? error.message : 'Unknown error';\n this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);\n this.healthStatus.set(pluginName, 'failed');\n \n checks.push({ \n name: 'health-check', \n status: 'failed', \n message: message \n });\n\n this.logger.error('Health check exception', { \n plugin: pluginName, \n error \n });\n }\n\n // Create health report\n const report: PluginHealthReport = {\n status: this.healthStatus.get(pluginName) || 'unknown',\n timestamp: new Date().toISOString(),\n message,\n metrics: {\n uptime: Date.now() - startTime,\n },\n checks: checks.length > 0 ? checks : undefined,\n };\n\n this.healthReports.set(pluginName, report);\n }\n\n /**\n * Attempt to restart a plugin\n */\n private async attemptRestart(\n pluginName: string,\n plugin: Plugin,\n config: PluginHealthCheck\n ): Promise<void> {\n const attempts = this.restartAttempts.get(pluginName) || 0;\n \n if (attempts >= config.maxRestartAttempts) {\n this.logger.error('Max restart attempts reached, giving up', { \n plugin: pluginName, \n attempts \n });\n this.healthStatus.set(pluginName, 'failed');\n return;\n }\n\n this.restartAttempts.set(pluginName, attempts + 1);\n \n // Calculate backoff delay\n const delay = this.calculateBackoff(attempts, config.restartBackoff);\n \n this.logger.info('Scheduling plugin restart', { \n plugin: pluginName, \n attempt: attempts + 1, \n delay \n });\n\n await new Promise(resolve => setTimeout(resolve, delay));\n\n try {\n // Call destroy and init to restart\n if (plugin.destroy) {\n await plugin.destroy();\n }\n \n // Note: Full restart would require kernel context\n // This is a simplified version - actual implementation would need kernel integration\n this.logger.info('Plugin restarted', { plugin: pluginName });\n \n // Reset counters on successful restart\n this.failureCounters.set(pluginName, 0);\n this.successCounters.set(pluginName, 0);\n this.healthStatus.set(pluginName, 'recovering');\n } catch (error) {\n this.logger.error('Plugin restart failed', { \n plugin: pluginName, \n error \n });\n this.healthStatus.set(pluginName, 'failed');\n }\n }\n\n /**\n * Calculate backoff delay for restarts\n */\n private calculateBackoff(attempt: number, strategy: 'fixed' | 'linear' | 'exponential'): number {\n const baseDelay = 1000; // 1 second base\n\n switch (strategy) {\n case 'fixed':\n return baseDelay;\n case 'linear':\n return baseDelay * (attempt + 1);\n case 'exponential':\n return baseDelay * Math.pow(2, attempt);\n default:\n return baseDelay;\n }\n }\n\n /**\n * Get current health status of a plugin\n */\n getHealthStatus(pluginName: string): PluginHealthStatus | undefined {\n return this.healthStatus.get(pluginName);\n }\n\n /**\n * Get latest health report for a plugin\n */\n getHealthReport(pluginName: string): PluginHealthReport | undefined {\n return this.healthReports.get(pluginName);\n }\n\n /**\n * Get all health statuses\n */\n getAllHealthStatuses(): Map<string, PluginHealthStatus> {\n return new Map(this.healthStatus);\n }\n\n /**\n * Shutdown health monitor\n */\n shutdown(): void {\n // Stop all monitoring intervals\n for (const pluginName of this.checkIntervals.keys()) {\n this.stopMonitoring(pluginName);\n }\n \n this.healthChecks.clear();\n this.healthStatus.clear();\n this.healthReports.clear();\n this.failureCounters.clear();\n this.successCounters.clear();\n this.restartAttempts.clear();\n \n this.logger.info('Health monitor shutdown complete');\n }\n\n /**\n * Race a plugin's custom health check against its timeout guard, and\n * reclaim the guard the moment the race settles (#4875).\n *\n * Same shape, same reasoning as `ObjectKernel.raceStartupTimeout()` (#4813,\n * PR #4874): the guard used to be armed and then abandoned — when the check\n * won the race, its `setTimeout` stayed ref'd in the event loop for the full\n * `config.timeout`. Health checks are *periodic*, so unlike the kernel's\n * one-shot startup guards the orphans here accumulate: one per plugin per\n * round, each pinning the loop for `config.timeout`.\n *\n * Clearing on settle rather than `unref()`-ing at arm time is deliberate.\n * An unref'd guard also stops pinning the loop, but it stops being a guard\n * as well: if the check never settles and nothing else keeps the loop alive,\n * Node exits before the timer can fire and the timeout is never reported.\n * The guard has to stay ref'd exactly as long as the race is undecided,\n * which is what `clearTimeout` in a `finally` expresses.\n *\n * `check` is widened to `T | PromiseLike<T>` because `checkMethod` is called\n * dynamically off the plugin and may be synchronous; such a check wins the\n * race immediately and the guard is reclaimed on the same turn.\n */\n private async raceCheckTimeout<T>(\n check: T | PromiseLike<T>,\n ms: number,\n message: string\n ): Promise<T> {\n let guard: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n guard = setTimeout(() => {\n reject(new Error(message));\n }, ms);\n });\n\n try {\n return await Promise.race([check, timeoutPromise]);\n } finally {\n clearTimeout(guard);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createHash } from 'node:crypto';\n\nimport type { \n HotReloadConfig, \n PluginStateSnapshot \n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from './logger.js';\nimport type { Plugin } from './types.js';\n\n// Polyfill for UUID generation to support both Node.js and Browser\nconst generateUUID = () => {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Basic UUID v4 fallback\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n};\n\n/**\n * Plugin State Manager\n * \n * Handles state persistence and restoration during hot reloads\n */\nclass PluginStateManager {\n private logger: ObjectLogger;\n private stateSnapshots = new Map<string, PluginStateSnapshot>();\n private memoryStore = new Map<string, any>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'StateManager' });\n }\n\n /**\n * Save plugin state before reload\n */\n async saveState(\n pluginId: string,\n version: string,\n state: Record<string, any>,\n config: HotReloadConfig\n ): Promise<string> {\n const snapshot: PluginStateSnapshot = {\n pluginId,\n version,\n timestamp: new Date().toISOString(),\n state,\n metadata: {\n checksum: this.calculateChecksum(state),\n compressed: false,\n },\n };\n\n const snapshotId = generateUUID();\n\n switch (config.stateStrategy) {\n case 'memory':\n this.memoryStore.set(snapshotId, snapshot);\n this.logger.debug('State saved to memory', { pluginId, snapshotId });\n break;\n\n case 'disk':\n // For disk storage, we would write to file system\n // For now, store in memory as fallback\n this.memoryStore.set(snapshotId, snapshot);\n this.logger.debug('State saved to disk (memory fallback)', { pluginId, snapshotId });\n break;\n\n case 'distributed':\n // For distributed storage, would use Redis/etcd\n // For now, store in memory as fallback\n this.memoryStore.set(snapshotId, snapshot);\n this.logger.debug('State saved to distributed store (memory fallback)', { \n pluginId, \n snapshotId \n });\n break;\n\n case 'none':\n this.logger.debug('State persistence disabled', { pluginId });\n break;\n }\n\n this.stateSnapshots.set(pluginId, snapshot);\n return snapshotId;\n }\n\n /**\n * Restore plugin state after reload\n */\n async restoreState(\n pluginId: string,\n snapshotId?: string\n ): Promise<Record<string, any> | undefined> {\n // Try to get from snapshot ID first, otherwise use latest for plugin\n let snapshot: PluginStateSnapshot | undefined;\n\n if (snapshotId) {\n snapshot = this.memoryStore.get(snapshotId);\n } else {\n snapshot = this.stateSnapshots.get(pluginId);\n }\n\n if (!snapshot) {\n this.logger.warn('No state snapshot found', { pluginId, snapshotId });\n return undefined;\n }\n\n // Verify checksum if available\n if (snapshot.metadata?.checksum) {\n const currentChecksum = this.calculateChecksum(snapshot.state);\n if (currentChecksum !== snapshot.metadata.checksum) {\n this.logger.error('State checksum mismatch - data may be corrupted', { \n pluginId,\n expected: snapshot.metadata.checksum,\n actual: currentChecksum\n });\n return undefined;\n }\n }\n\n this.logger.debug('State restored', { pluginId, version: snapshot.version });\n return snapshot.state;\n }\n\n /**\n * Clear state for a plugin\n */\n clearState(pluginId: string): void {\n this.stateSnapshots.delete(pluginId);\n // Note: We don't clear memory store as it might have multiple snapshots\n this.logger.debug('State cleared', { pluginId });\n }\n\n /**\n * Calculate checksum for state verification using SHA-256.\n */\n private calculateChecksum(state: Record<string, any>): string {\n const stateStr = JSON.stringify(state);\n return createHash('sha256').update(stateStr).digest('hex');\n }\n\n /**\n * Shutdown state manager\n */\n shutdown(): void {\n this.stateSnapshots.clear();\n this.memoryStore.clear();\n this.logger.info('State manager shutdown complete');\n }\n}\n\n/**\n * Hot Reload Manager\n * \n * Manages hot reloading of plugins with state preservation\n */\nexport class HotReloadManager {\n private logger: ObjectLogger;\n private stateManager: PluginStateManager;\n private reloadConfigs = new Map<string, HotReloadConfig>();\n private watchHandles = new Map<string, any>();\n private reloadTimers = new Map<string, NodeJS.Timeout>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'HotReload' });\n this.stateManager = new PluginStateManager(logger);\n }\n\n /**\n * Register a plugin for hot reload\n */\n registerPlugin(pluginName: string, config: HotReloadConfig): void {\n if (!config.enabled) {\n this.logger.debug('Hot reload disabled for plugin', { plugin: pluginName });\n return;\n }\n\n this.reloadConfigs.set(pluginName, config);\n this.logger.info('Plugin registered for hot reload', { \n plugin: pluginName,\n watchPatterns: config.watchPatterns,\n stateStrategy: config.stateStrategy\n });\n }\n\n /**\n * Start watching for changes (requires file system integration)\n */\n startWatching(pluginName: string): void {\n const config = this.reloadConfigs.get(pluginName);\n if (!config || !config.enabled) {\n return;\n }\n\n // Note: Actual file watching would require chokidar or similar\n // This is a placeholder for the integration point\n this.logger.info('File watching started', { \n plugin: pluginName,\n patterns: config.watchPatterns \n });\n }\n\n /**\n * Stop watching for changes\n */\n stopWatching(pluginName: string): void {\n const handle = this.watchHandles.get(pluginName);\n if (handle) {\n // Stop watching (would call chokidar close())\n this.watchHandles.delete(pluginName);\n this.logger.info('File watching stopped', { plugin: pluginName });\n }\n\n // Clear any pending reload timers\n const timer = this.reloadTimers.get(pluginName);\n if (timer) {\n clearTimeout(timer);\n this.reloadTimers.delete(pluginName);\n }\n }\n\n /**\n * Trigger hot reload for a plugin\n */\n async reloadPlugin(\n pluginName: string,\n plugin: Plugin,\n version: string,\n getPluginState: () => Record<string, any>,\n restorePluginState: (state: Record<string, any>) => void\n ): Promise<boolean> {\n const config = this.reloadConfigs.get(pluginName);\n if (!config) {\n this.logger.warn('Cannot reload - plugin not registered', { plugin: pluginName });\n return false;\n }\n\n this.logger.info('Starting hot reload', { plugin: pluginName });\n\n try {\n // Call before reload hooks\n if (config.beforeReload) {\n this.logger.debug('Executing before reload hooks', { \n plugin: pluginName,\n hooks: config.beforeReload \n });\n // Hook execution would be done through kernel's hook system\n }\n\n // Save state if configured\n let snapshotId: string | undefined;\n if (config.preserveState && config.stateStrategy !== 'none') {\n const state = getPluginState();\n snapshotId = await this.stateManager.saveState(\n pluginName,\n version,\n state,\n config\n );\n this.logger.debug('Plugin state saved', { plugin: pluginName, snapshotId });\n }\n\n // Gracefully shutdown the plugin\n if (plugin.destroy) {\n this.logger.debug('Destroying plugin', { plugin: pluginName });\n \n await this.raceShutdownTimeout(\n plugin.destroy(),\n config.shutdownTimeout,\n 'Shutdown timeout'\n );\n this.logger.debug('Plugin destroyed successfully', { plugin: pluginName });\n }\n\n // At this point, the kernel would reload the plugin module\n // This would be handled by the plugin loader\n this.logger.debug('Plugin module would be reloaded here', { plugin: pluginName });\n\n // Restore state if we saved it\n if (snapshotId && config.preserveState) {\n const restoredState = await this.stateManager.restoreState(pluginName, snapshotId);\n if (restoredState) {\n restorePluginState(restoredState);\n this.logger.debug('Plugin state restored', { plugin: pluginName });\n }\n }\n\n // Call after reload hooks\n if (config.afterReload) {\n this.logger.debug('Executing after reload hooks', { \n plugin: pluginName,\n hooks: config.afterReload \n });\n // Hook execution would be done through kernel's hook system\n }\n\n this.logger.info('Hot reload completed successfully', { plugin: pluginName });\n return true;\n } catch (error) {\n this.logger.error('Hot reload failed', { \n plugin: pluginName, \n error \n });\n return false;\n }\n }\n\n /**\n * Race a plugin's `destroy()` against its shutdown-timeout guard, and\n * reclaim the guard the moment the race settles (#4952).\n *\n * The guard used to be armed and then abandoned — byte-for-byte the leak\n * #4813 fixed in the kernel's startup guards (PR #4874) and #4875 fixed in\n * the periodic health checks (PR #4950): when `destroy()` won the race, its\n * `setTimeout` stayed ref'd in the event loop for the full\n * `shutdownTimeout`, so a hot reload that finished in milliseconds still\n * pinned the loop for the whole budget — once per reload, per plugin.\n *\n * Clearing on settle rather than `unref()`-ing at arm time is deliberate.\n * An unref'd guard also stops pinning the loop, but it stops being a guard\n * as well: if `destroy()` never settles and nothing else keeps the loop\n * alive, Node exits before the timer can fire and the timeout is never\n * reported. The guard has to stay ref'd exactly as long as the race is\n * undecided, which is what `clearTimeout` in a `finally` expresses.\n *\n * `shutdown` is widened to `T | PromiseLike<T>` because the Plugin contract\n * permits a synchronous `destroy()` (`Promise<void> | void`); such a hook\n * wins the race immediately and the guard is reclaimed on the same turn.\n */\n private async raceShutdownTimeout<T>(\n shutdown: T | PromiseLike<T>,\n timeout: number,\n message: string\n ): Promise<T> {\n let guard: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n guard = setTimeout(() => {\n reject(new Error(message));\n }, timeout);\n });\n\n try {\n return await Promise.race([shutdown, timeoutPromise]);\n } finally {\n clearTimeout(guard);\n }\n }\n\n /**\n * Schedule a reload with debouncing\n */\n scheduleReload(\n pluginName: string,\n reloadFn: () => Promise<void>\n ): void {\n const config = this.reloadConfigs.get(pluginName);\n if (!config) {\n return;\n }\n\n // Clear existing timer\n const existingTimer = this.reloadTimers.get(pluginName);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Schedule new reload with debounce\n const timer = setTimeout(() => {\n this.logger.debug('Debounce period elapsed, executing reload', { \n plugin: pluginName \n });\n reloadFn().catch(error => {\n this.logger.error('Scheduled reload failed', { \n plugin: pluginName, \n error \n });\n });\n this.reloadTimers.delete(pluginName);\n }, config.debounceDelay);\n\n this.reloadTimers.set(pluginName, timer);\n this.logger.debug('Reload scheduled with debounce', { \n plugin: pluginName,\n delay: config.debounceDelay \n });\n }\n\n /**\n * Get state manager for direct access\n */\n getStateManager(): PluginStateManager {\n return this.stateManager;\n }\n\n /**\n * Shutdown hot reload manager\n */\n shutdown(): void {\n // Stop all watching\n for (const pluginName of this.watchHandles.keys()) {\n this.stopWatching(pluginName);\n }\n\n // Clear all timers\n for (const timer of this.reloadTimers.values()) {\n clearTimeout(timer);\n }\n\n this.reloadConfigs.clear();\n this.watchHandles.clear();\n this.reloadTimers.clear();\n this.stateManager.shutdown();\n \n this.logger.info('Hot reload manager shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n SemanticVersion,\n VersionConstraint,\n CompatibilityLevel,\n DependencyConflict\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from './logger.js';\n\n/**\n * Semantic Version Parser and Comparator\n * \n * Implements semantic versioning comparison and constraint matching\n */\nexport class SemanticVersionManager {\n /**\n * Parse a version string into semantic version components\n */\n static parse(versionStr: string): SemanticVersion {\n // Remove 'v' prefix if present\n const cleanVersion = versionStr.replace(/^v/, '');\n \n // Match semver pattern: major.minor.patch[-prerelease][+build]\n const match = cleanVersion.match(\n /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([a-zA-Z0-9.-]+))?(?:\\+([a-zA-Z0-9.-]+))?$/\n );\n\n if (!match) {\n throw new Error(`Invalid semantic version: ${versionStr}`);\n }\n\n return {\n major: parseInt(match[1], 10),\n minor: parseInt(match[2], 10),\n patch: parseInt(match[3], 10),\n preRelease: match[4],\n build: match[5],\n };\n }\n\n /**\n * Convert semantic version back to string\n */\n static toString(version: SemanticVersion): string {\n let str = `${version.major}.${version.minor}.${version.patch}`;\n if (version.preRelease) {\n str += `-${version.preRelease}`;\n }\n if (version.build) {\n str += `+${version.build}`;\n }\n return str;\n }\n\n /**\n * Compare two semantic versions\n * Returns: -1 if a < b, 0 if a === b, 1 if a > b\n */\n static compare(a: SemanticVersion, b: SemanticVersion): number {\n // Compare major, minor, patch\n if (a.major !== b.major) return a.major - b.major;\n if (a.minor !== b.minor) return a.minor - b.minor;\n if (a.patch !== b.patch) return a.patch - b.patch;\n\n // Pre-release versions have lower precedence\n if (a.preRelease && !b.preRelease) return -1;\n if (!a.preRelease && b.preRelease) return 1;\n \n // Compare pre-release versions\n if (a.preRelease && b.preRelease) {\n return a.preRelease.localeCompare(b.preRelease);\n }\n\n return 0;\n }\n\n /**\n * Check if version satisfies constraint\n */\n static satisfies(version: SemanticVersion, constraint: VersionConstraint): boolean {\n const constraintStr = constraint as string;\n\n // Any version\n if (constraintStr === '*' || constraintStr === 'latest') {\n return true;\n }\n\n // Exact version\n if (/^[\\d.]+$/.test(constraintStr)) {\n const exact = this.parse(constraintStr);\n return this.compare(version, exact) === 0;\n }\n\n // Caret range (^): Compatible with version\n if (constraintStr.startsWith('^')) {\n const base = this.parse(constraintStr.slice(1));\n return (\n version.major === base.major &&\n this.compare(version, base) >= 0\n );\n }\n\n // Tilde range (~): Approximately equivalent\n if (constraintStr.startsWith('~')) {\n const base = this.parse(constraintStr.slice(1));\n return (\n version.major === base.major &&\n version.minor === base.minor &&\n this.compare(version, base) >= 0\n );\n }\n\n // Greater than or equal\n if (constraintStr.startsWith('>=')) {\n const base = this.parse(constraintStr.slice(2));\n return this.compare(version, base) >= 0;\n }\n\n // Greater than\n if (constraintStr.startsWith('>')) {\n const base = this.parse(constraintStr.slice(1));\n return this.compare(version, base) > 0;\n }\n\n // Less than or equal\n if (constraintStr.startsWith('<=')) {\n const base = this.parse(constraintStr.slice(2));\n return this.compare(version, base) <= 0;\n }\n\n // Less than\n if (constraintStr.startsWith('<')) {\n const base = this.parse(constraintStr.slice(1));\n return this.compare(version, base) < 0;\n }\n\n // Range (1.2.3 - 2.3.4)\n const rangeMatch = constraintStr.match(/^([\\d.]+)\\s*-\\s*([\\d.]+)$/);\n if (rangeMatch) {\n const min = this.parse(rangeMatch[1]);\n const max = this.parse(rangeMatch[2]);\n return this.compare(version, min) >= 0 && this.compare(version, max) <= 0;\n }\n\n return false;\n }\n\n /**\n * Determine compatibility level between two versions\n */\n static getCompatibilityLevel(from: SemanticVersion, to: SemanticVersion): CompatibilityLevel {\n const cmp = this.compare(from, to);\n\n // Same version\n if (cmp === 0) {\n return 'fully-compatible';\n }\n\n // Major version changed - breaking changes\n if (from.major !== to.major) {\n return 'breaking-changes';\n }\n\n // Minor version increased - backward compatible\n if (from.minor < to.minor) {\n return 'backward-compatible';\n }\n\n // Patch version increased - fully compatible\n if (from.patch < to.patch) {\n return 'fully-compatible';\n }\n\n // Downgrade - incompatible\n return 'incompatible';\n }\n}\n\n/**\n * Plugin Dependency Resolver\n * \n * Resolves plugin dependencies using topological sorting and conflict detection\n */\nexport class DependencyResolver {\n private logger: ObjectLogger;\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'DependencyResolver' });\n }\n\n /**\n * Resolve dependencies using topological sort\n */\n resolve(\n plugins: Map<string, { version?: string; dependencies?: string[] }>\n ): string[] {\n const graph = new Map<string, string[]>();\n const inDegree = new Map<string, number>();\n\n // Build dependency graph\n for (const [pluginName, pluginInfo] of plugins) {\n if (!graph.has(pluginName)) {\n graph.set(pluginName, []);\n inDegree.set(pluginName, 0);\n }\n\n const deps = pluginInfo.dependencies || [];\n for (const dep of deps) {\n // Check if dependency exists\n if (!plugins.has(dep)) {\n throw new Error(`Missing dependency: ${pluginName} requires ${dep}`);\n }\n\n // Add edge\n if (!graph.has(dep)) {\n graph.set(dep, []);\n inDegree.set(dep, 0);\n }\n graph.get(dep)!.push(pluginName);\n inDegree.set(pluginName, (inDegree.get(pluginName) || 0) + 1);\n }\n }\n\n // Topological sort using Kahn's algorithm\n const queue: string[] = [];\n const result: string[] = [];\n\n // Add all nodes with no incoming edges\n for (const [node, degree] of inDegree) {\n if (degree === 0) {\n queue.push(node);\n }\n }\n\n while (queue.length > 0) {\n const node = queue.shift()!;\n result.push(node);\n\n // Reduce in-degree for dependent nodes\n const dependents = graph.get(node) || [];\n for (const dependent of dependents) {\n const newDegree = (inDegree.get(dependent) || 0) - 1;\n inDegree.set(dependent, newDegree);\n \n if (newDegree === 0) {\n queue.push(dependent);\n }\n }\n }\n\n // Check for circular dependencies\n if (result.length !== plugins.size) {\n const remaining = Array.from(plugins.keys()).filter(p => !result.includes(p));\n this.logger.error('Circular dependency detected', { remaining });\n throw new Error(`Circular dependency detected among: ${remaining.join(', ')}`);\n }\n\n this.logger.debug('Dependencies resolved', { order: result });\n return result;\n }\n\n /**\n * Detect dependency conflicts\n */\n detectConflicts(\n plugins: Map<string, { version: string; dependencies?: Record<string, VersionConstraint> }>\n ): DependencyConflict[] {\n const conflicts: DependencyConflict[] = [];\n const versionRequirements = new Map<string, Map<string, VersionConstraint>>();\n\n // Collect all version requirements\n for (const [pluginName, pluginInfo] of plugins) {\n if (!pluginInfo.dependencies) continue;\n\n for (const [depName, constraint] of Object.entries(pluginInfo.dependencies)) {\n if (!versionRequirements.has(depName)) {\n versionRequirements.set(depName, new Map());\n }\n versionRequirements.get(depName)!.set(pluginName, constraint);\n }\n }\n\n // Check for version mismatches\n for (const [depName, requirements] of versionRequirements) {\n const depInfo = plugins.get(depName);\n if (!depInfo) continue;\n\n const depVersion = SemanticVersionManager.parse(depInfo.version);\n const unsatisfied: Array<{ pluginId: string; version: string }> = [];\n\n for (const [requiringPlugin, constraint] of requirements) {\n if (!SemanticVersionManager.satisfies(depVersion, constraint)) {\n unsatisfied.push({\n pluginId: requiringPlugin,\n version: constraint as string,\n });\n }\n }\n\n if (unsatisfied.length > 0) {\n conflicts.push({\n type: 'version-mismatch',\n severity: 'error',\n description: `Version mismatch for ${depName}: detected ${unsatisfied.length} unsatisfied requirements`,\n plugins: [\n { pluginId: depName, version: depInfo.version },\n ...unsatisfied,\n ],\n resolutions: [{\n strategy: 'upgrade',\n description: `Upgrade ${depName} to satisfy all constraints`,\n targetPlugins: [depName],\n automatic: false,\n } as any],\n });\n }\n }\n\n // Check for circular dependencies (will be caught by resolve())\n try {\n this.resolve(new Map(\n Array.from(plugins.entries()).map(([name, info]) => [\n name,\n { version: info.version, dependencies: info.dependencies ? Object.keys(info.dependencies) : [] }\n ])\n ));\n } catch (error) {\n if (error instanceof Error && error.message.includes('Circular dependency')) {\n conflicts.push({\n type: 'circular-dependency',\n severity: 'critical',\n description: error.message,\n plugins: [], // Would need to extract from error\n resolutions: [{\n strategy: 'manual',\n description: 'Remove circular dependency by restructuring plugins',\n automatic: false,\n } as any],\n });\n }\n }\n\n return conflicts;\n }\n\n /**\n * Find best version that satisfies all constraints\n */\n findBestVersion(\n availableVersions: string[],\n constraints: VersionConstraint[]\n ): string | undefined {\n // Parse and sort versions (highest first)\n const versions = availableVersions\n .map(v => ({ str: v, parsed: SemanticVersionManager.parse(v) }))\n .sort((a, b) => -SemanticVersionManager.compare(a.parsed, b.parsed));\n\n // Find highest version that satisfies all constraints\n for (const version of versions) {\n const satisfiesAll = constraints.every(constraint =>\n SemanticVersionManager.satisfies(version.parsed, constraint)\n );\n\n if (satisfiesAll) {\n return version.str;\n }\n }\n\n return undefined;\n }\n\n /**\n * Check if dependencies form a valid DAG (no cycles)\n */\n isAcyclic(dependencies: Map<string, string[]>): boolean {\n try {\n const plugins = new Map(\n Array.from(dependencies.entries()).map(([name, deps]) => [\n name,\n { dependencies: deps }\n ])\n );\n this.resolve(plugins);\n return true;\n } catch {\n return false;\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { ObjectLogger } from './logger.js';\n\n/**\n * Namespace entry representing an object/view/flow etc. registered by a package.\n */\nexport interface NamespaceEntry {\n /** The namespace path (e.g. \"objects.project_task\", \"views.task_list\") */\n namespace: string;\n /** The package that owns this namespace */\n packageId: string;\n /** When this entry was registered */\n registeredAt: string;\n}\n\n/**\n * Result of a namespace conflict check.\n */\nexport interface NamespaceConflict {\n /** The conflicting namespace path */\n namespace: string;\n /** The package that currently owns this namespace */\n existingPackageId: string;\n /** The package attempting to register the same namespace */\n incomingPackageId: string;\n /** A suggested alternative name to avoid the conflict */\n suggestion?: string;\n}\n\n/**\n * Result of namespace availability check.\n */\nexport interface NamespaceCheckResult {\n /** Whether all requested namespaces are available */\n available: boolean;\n /** List of conflicts detected */\n conflicts: NamespaceConflict[];\n /** Suggested alternatives for each conflict */\n suggestions: Record<string, string>;\n}\n\n/**\n * Namespace Resolver\n *\n * Manages namespace registration for installed packages and detects collisions\n * during install-time. Each metadata item (object, view, flow, page, etc.)\n * produces a namespace like `objects.<name>` or `views.<name>`.\n *\n * When a new package declares objects, views, or other metadata that would\n * collide with an existing package's metadata, this resolver reports the\n * conflicts and suggests prefixed alternatives.\n */\nexport class NamespaceResolver {\n private logger: ObjectLogger;\n private registry: Map<string, NamespaceEntry> = new Map();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'NamespaceResolver' });\n }\n\n /**\n * Register namespaces owned by a package.\n */\n register(packageId: string, namespaces: string[]): void {\n const now = new Date().toISOString();\n for (const ns of namespaces) {\n if (this.registry.has(ns)) {\n const existing = this.registry.get(ns)!;\n if (existing.packageId !== packageId) {\n this.logger.warn('Overwriting namespace entry', { namespace: ns, existing: existing.packageId, incoming: packageId });\n }\n }\n this.registry.set(ns, { namespace: ns, packageId, registeredAt: now });\n this.logger.debug('Namespace registered', { namespace: ns, packageId });\n }\n }\n\n /**\n * Unregister all namespaces belonging to a package.\n */\n unregister(packageId: string): string[] {\n const removed: string[] = [];\n for (const [ns, entry] of this.registry) {\n if (entry.packageId === packageId) {\n this.registry.delete(ns);\n removed.push(ns);\n }\n }\n this.logger.debug('Namespaces unregistered', { packageId, count: removed.length });\n return removed;\n }\n\n /**\n * Check whether a set of namespaces is available for a given package.\n */\n checkAvailability(packageId: string, namespaces: string[]): NamespaceCheckResult {\n const conflicts: NamespaceConflict[] = [];\n const suggestions: Record<string, string> = {};\n\n for (const ns of namespaces) {\n const existing = this.registry.get(ns);\n if (existing && existing.packageId !== packageId) {\n const suggestion = this.suggestAlternative(ns, packageId);\n conflicts.push({\n namespace: ns,\n existingPackageId: existing.packageId,\n incomingPackageId: packageId,\n suggestion,\n });\n suggestions[ns] = suggestion;\n }\n }\n\n return {\n available: conflicts.length === 0,\n conflicts,\n suggestions,\n };\n }\n\n /**\n * Extract namespace strings from a package's metadata definition.\n */\n extractNamespaces(config: Record<string, unknown>): string[] {\n const namespaces: string[] = [];\n const categories = [\n 'objects', 'views', 'pages', 'flows', 'workflows',\n 'apps', 'dashboards', 'reports', 'actions', 'agents',\n ];\n\n for (const category of categories) {\n const items = config[category];\n if (Array.isArray(items)) {\n for (const item of items) {\n const name = (item as Record<string, unknown>)?.name;\n if (typeof name === 'string') {\n namespaces.push(`${category}.${name}`);\n }\n }\n } else if (items && typeof items === 'object') {\n for (const key of Object.keys(items as object)) {\n namespaces.push(`${category}.${key}`);\n }\n }\n }\n\n return namespaces;\n }\n\n /**\n * Get all registered entries.\n */\n getRegistry(): ReadonlyMap<string, NamespaceEntry> {\n return this.registry;\n }\n\n /**\n * Get all namespaces belonging to a specific package.\n */\n getPackageNamespaces(packageId: string): string[] {\n const namespaces: string[] = [];\n for (const [ns, entry] of this.registry) {\n if (entry.packageId === packageId) {\n namespaces.push(ns);\n }\n }\n return namespaces;\n }\n\n /**\n * Generate a prefixed alternative namespace to avoid conflicts.\n */\n private suggestAlternative(ns: string, packageId: string): string {\n // Extract the short package name for prefixing\n const shortName = packageId\n .replace(/^@[^/]+\\//, '')\n .replace(/^plugin-/, '')\n .replace(/-/g, '_');\n\n const parts = ns.split('.');\n if (parts.length >= 2) {\n // e.g. \"objects.task\" → \"objects.crm_task\"\n return `${parts[0]}.${shortName}_${parts.slice(1).join('.')}`;\n }\n return `${shortName}_${ns}`;\n }\n}\n"],"mappings":"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","names":["nodePath","ServiceLifecycle","safeJsonParse","ymd","createHash","createHash"]}
|
|
1
|
+
{"version":3,"sources":["../src/plugin-order.ts","../src/hook-dispatch.ts","../src/kernel-base.ts","../src/logger.ts","../src/kernel.ts","../src/security/plugin-config-validator.ts","../src/security/plugin-artifact-signature.ts","../src/plugin-loader.ts","../src/utils/env.ts","../src/fallbacks/memory-cache.ts","../src/fallbacks/memory-queue.ts","../src/fallbacks/memory-job.ts","../src/fallbacks/memory-i18n.ts","../src/metadata-service-contract.ts","../src/fallbacks/memory-metadata.ts","../src/fallbacks/authored-translation-sync.ts","../src/fallbacks/index.ts","../src/lite-kernel.ts","../src/qa/index.ts","../src/qa/runner.ts","../src/qa/http-adapter.ts","../src/security/plugin-signature-verifier.ts","../src/security/plugin-permission-enforcer.ts","../src/security/permission-manager.ts","../src/security/sandbox-runtime.ts","../src/security/security-scanner.ts","../src/security/api-key.ts","../src/security/resolve-authz-context.ts","../src/security/grant-validity.ts","../src/security/posture-ladder.ts","../src/security/assemble-execution-context.ts","../src/security/auth-gate.ts","../src/security/anonymous-deny.ts","../src/security/audience-binding-suggestion-status.ts","../src/security/operation-private-keys.ts","../src/utils/datetime.ts","../src/utils/bulk-write.ts","../src/utils/internal-write-response.ts","../src/utils/migration-journal.ts","../src/utils/filter-tokens.ts","../src/utils/record-not-found.ts","../src/health-monitor.ts","../src/hot-reload.ts","../src/dependency-resolver.ts","../src/namespace-resolver.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Plugin ordering + init-service contract (ADR-0116, #4131).\n *\n * The kernel resolves BOTH init and start order from the plugin dependency\n * graph, so `kernel.use()` registration order proves nothing. Twice a plugin\n * relied on list position anyway and shipped a boot that dies inside init —\n * the first cut of DefaultDatasourcePlugin (started after boot schema-sync;\n * server with no tables) and AppPlugin (#4085: `manifest` grabbed in init\n * before ObjectQLPlugin registered it). Both times the fix existed only as a\n * convention: put the plugin in the right slot, write a comment. This module\n * is the enforced form of that contract, shared by ObjectKernel and\n * LiteKernel so there is exactly one ordering semantic:\n *\n * - `dependencies` — hard: hoisted ahead, missing ⇒ boot error (unchanged).\n * - `optionalDependencies` — order-if-present: hoisted ahead when composed,\n * silently skipped when absent. For plugins that DEGRADE without the\n * dependency but must never init before it (AppPlugin on an engine-less\n * metadata-only kernel).\n * - `requiresServices` — services a plugin resolves SYNCHRONOUSLY during\n * `init()`. Validated before Phase 1 (provable misordering ⇒ named error\n * instead of a crash inside init) and again immediately before each init\n * (authoritative: the service is either registered by now or init dies).\n * - `providesServices` — services a plugin's `init()` UNCONDITIONALLY\n * registers. Powers the pre-Phase-1 check and the named diagnostics.\n * Declare only unconditional registrations: a conditional service (e.g.\n * one gated behind an option) would indict this plugin for orderings it\n * cannot actually satisfy.\n *\n * Declaring is NOT voluntary (#4471). Everything above can only enforce what\n * a plugin declares — a plugin that resolves `getService('X')` during init()\n * and declares nothing was invisible to all of it, failing only under\n * unlucky composition orders (#4085, and #4420 at data-consistency cost).\n * `scripts/check-init-service-contract.mjs` (CI: `check:init-service-contract`)\n * closes that gap: it walks every plugin's init() call graph and errors on\n * any init-reachable getService of a workspace-provided service that no\n * declaration covers. Best-effort tolerance is declared IN the plugin via\n * `optionalDependencies`, never exempted in the checker.\n */\n\n/**\n * The ordering-relevant surface of a kernel plugin. Structural on purpose:\n * ObjectKernel sorts `PluginMetadata`, LiteKernel sorts `Plugin`, and both\n * satisfy this shape.\n */\nexport interface OrderablePlugin {\n name: string;\n /** Hard dependencies — hoisted ahead; missing ⇒ boot error. */\n dependencies?: string[];\n /** Soft dependencies — hoisted ahead when composed, skipped when absent. */\n optionalDependencies?: string[];\n /** Services resolved synchronously during init(). */\n requiresServices?: string[];\n /** Services init() unconditionally registers. */\n providesServices?: string[];\n}\n\n/**\n * Topologically order plugins: every plugin's `dependencies` (throw when\n * missing) and `optionalDependencies` (skip when missing) init before it.\n * Insertion order is preserved for plugins with no edges between them.\n * Cycles through either edge kind throw — an optional dependency is a real\n * edge whenever both sides are composed.\n */\nexport function resolvePluginOrder<P extends OrderablePlugin>(plugins: Map<string, P>): P[] {\n const resolved: P[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n const visit = (pluginName: string) => {\n if (visited.has(pluginName)) return;\n\n if (visiting.has(pluginName)) {\n throw new Error(`[Kernel] Circular dependency detected: ${pluginName}`);\n }\n\n const plugin = plugins.get(pluginName);\n if (!plugin) {\n throw new Error(`[Kernel] Plugin '${pluginName}' not found`);\n }\n\n visiting.add(pluginName);\n\n for (const dep of plugin.dependencies ?? []) {\n if (!plugins.has(dep)) {\n throw new Error(\n `[Kernel] Dependency '${dep}' not found for plugin '${pluginName}'`\n );\n }\n visit(dep);\n }\n for (const dep of plugin.optionalDependencies ?? []) {\n if (plugins.has(dep)) visit(dep);\n }\n\n visiting.delete(pluginName);\n visited.add(pluginName);\n resolved.push(plugin);\n };\n\n for (const pluginName of plugins.keys()) {\n visit(pluginName);\n }\n\n return resolved;\n}\n\n/**\n * Pre-Phase-1 check: walk the resolved order and prove no plugin requires a\n * service whose only declared provider initializes AFTER it. A violation is\n * the exact #4085 class — misplaced composition — reported as a named,\n * structural boot error BEFORE any init side effects, instead of a bare\n * \"Service not found\" thrown from inside the victim's init.\n *\n * Deliberately does NOT fail when a required service has no declared provider\n * and is not yet registered: an earlier plugin may register it without\n * declaring `providesServices`. That case is settled authoritatively by\n * {@link assertInitServiceRequirements} immediately before the requiring\n * plugin's init runs.\n */\nexport function validateInitServiceContract<P extends OrderablePlugin>(\n ordered: P[],\n isServiceRegistered: (name: string) => boolean,\n): void {\n const providerSlot = new Map<string, { plugin: string; slot: number }>();\n ordered.forEach((plugin, slot) => {\n for (const service of plugin.providesServices ?? []) {\n if (!providerSlot.has(service)) {\n providerSlot.set(service, { plugin: plugin.name, slot });\n }\n }\n });\n\n const violations: string[] = [];\n ordered.forEach((plugin, slot) => {\n for (const service of plugin.requiresServices ?? []) {\n if (isServiceRegistered(service)) continue;\n const provider = providerSlot.get(service);\n if (provider && provider.slot > slot) {\n violations.push(\n `'${plugin.name}' requires service '${service}' during init, but '${service}' is ` +\n `provided by '${provider.plugin}', which initializes later (slot ${provider.slot} vs ${slot}). ` +\n `Registration order is not a contract — declare '${provider.plugin}' in ` +\n `'${plugin.name}'.dependencies (hard) or .optionalDependencies (order-if-present) ` +\n `so the kernel hoists it.`\n );\n }\n }\n });\n\n if (violations.length > 0) {\n throw new Error(\n `[Kernel] Plugin ordering contract violated (#4131):\\n - ${violations.join('\\n - ')}`\n );\n }\n}\n\n/**\n * Diagnosis suffix for a getService miss that happens WHILE a plugin's\n * init() is running: names the initializing plugin and — when a composed\n * plugin declares the service — the provider and the directive to declare\n * the ordering. Returns '' when no plugin is initializing, so non-boot\n * error messages stay byte-identical. Shared by both kernels.\n */\nexport function describeInitOrderFault(\n currentlyInitializing: string | undefined,\n plugins: Iterable<OrderablePlugin>,\n serviceName: string,\n): string {\n if (!currentlyInitializing) return '';\n let providerHint = '';\n for (const plugin of plugins) {\n if (plugin.providesServices?.includes(serviceName)) {\n providerHint = ` '${serviceName}' is provided by composed plugin '${plugin.name}', which has ` +\n `not initialized yet — declare it in the requiring plugin's dependencies/optionalDependencies.`;\n break;\n }\n }\n return ` (while plugin '${currentlyInitializing}' was initializing — a composition/` +\n `ordering fault, #4131.${providerHint})`;\n}\n\n/**\n * Just-before-init check: every service in `requiresServices` must be\n * registered at the moment the plugin's init() is about to run. At this\n * point the verdict is authoritative — Phase 1 runs sequentially, so a\n * service absent now is absent for this init, and the init would die on a\n * bare \"Service not found\" anyway. This turns that crash into a named\n * composition error.\n */\nexport function assertInitServiceRequirements(\n plugin: OrderablePlugin,\n isServiceRegistered: (name: string) => boolean,\n): void {\n for (const service of plugin.requiresServices ?? []) {\n if (isServiceRegistered(service)) continue;\n throw new Error(\n `[Kernel] Plugin '${plugin.name}' requires service '${service}' at init, but no such ` +\n `service is registered at this point of the boot. No composed plugin that initializes ` +\n `earlier provides it — compose a provider (and, if it initializes later without declaring ` +\n `'${service}' in providesServices, order it ahead via this plugin's dependencies/` +\n `optionalDependencies) (#4131).`\n );\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared lifecycle-hook dispatchers (#5282).\n *\n * ## Why this module exists\n *\n * `ObjectKernel` does not extend `ObjectKernelBase` — it is a standalone\n * production kernel with its own `plugins`/`services`/`hooks`/`state`/`logger`\n * fields, and only `LiteKernel` extends the base. Making it inherit was\n * considered and **rejected** (#5282, option A: an inheritance refactor of a\n * 800-line production kernel, risk out of proportion to the benefit). What was\n * ruled instead is this module (option B): the two *dispatch flavours* become\n * module-level functions both kernels call, so a change to how a hook is\n * dispatched is written once and lands on both kernels.\n *\n * Before this, dispatch existed twice with no shared code path — the base's\n * `triggerHook` / `triggerHookOrThrow` / `context.trigger` on one side, and\n * `ObjectKernel`'s private `triggerShutdownHookIsolating` / `context.trigger`\n * on the other. The two isolating loops printed the same log line **because\n * someone typed it twice**. That hand-mirroring is the structural seam three\n * consecutive bugs grew on, every one of them the same shape — one hook name\n * meaning opposite things on the two kernels:\n *\n * - #5170 — `kernel:ready`: LiteKernel swallowed a throwing handler while\n * ObjectKernel failed the boot.\n * - #5257 — `kernel:bootstrapped` / `kernel:listening`: same, and the ugliest\n * specimen — `HonoServerPlugin` awaits `server.listen(port)` inside\n * `kernel:listening`, so on LiteKernel a failed listen was swallowed and\n * the process printed \"✅ Bootstrap complete\" with nothing listening.\n * - #5274 — `kernel:shutdown`, in the opposite direction: ObjectKernel's\n * propagating dispatch let one bad handler skip every `destroy()` and\n * `process.exit(1)`.\n *\n * The **storage** is deliberately still two maps (out of the ruling's scope);\n * only the dispatch is shared. The paired-pin gate\n * (`scripts/check-kernel-hook-pairs.mjs`, option C of the same ruling) covers\n * the residue: every `kernel:*` hook dispatched in `packages/core/src` must\n * carry a named pin in BOTH `kernel.test.ts` and `lite-kernel.test.ts`, so a\n * fifth lifecycle hook cannot arrive paired on one kernel only.\n *\n * ## Choosing a flavour\n *\n * The choice is per hook, not per kernel, and it is a judgement recorded at\n * each dispatch site:\n *\n * - **Boot path** (`kernel:ready`, `kernel:bootstrapped`, `kernel:listening`)\n * ⇒ {@link dispatchHookPropagating}. Everything dispatched before\n * \"✅ Bootstrap complete\" is a precondition of that claim; swallowing a\n * throw there does not rescue the boot, it only hides the failure behind a\n * process reporting success.\n * - **Teardown path** (`kernel:shutdown`) ⇒ {@link dispatchHookIsolating}.\n * There is no \"refuse to proceed\" left to buy: what is queued behind a\n * failing handler is the rest of the cleanup — the other subscribers, then\n * each plugin's `destroy()` — which is what flushes buffers, closes\n * connections and releases locks.\n *\n * ⛔ Internal module: not exported from `packages/core/src/index.ts`. These are\n * kernel internals, not a public dispatch API.\n */\n\n/** A registered lifecycle-hook handler, exactly as `PluginContext.hook` stores it. */\nexport type HookHandler = (...args: any[]) => void | Promise<void>;\n\n/**\n * The slice of the logger contract a dispatcher uses. Structural on purpose:\n * both `ObjectLogger` (what `ObjectKernel` holds) and the spec `Logger`\n * contract (what `ObjectKernelBase` holds) satisfy it without either kernel\n * importing the other's logger type.\n */\nexport interface HookDispatchLogger {\n debug(message: string, meta?: Record<string, any>): void;\n error(message: string, error?: Error, meta?: Record<string, any>): void;\n}\n\n/**\n * The trace line every *logged* dispatch emits before running handlers.\n *\n * Kept in one place because it is asserted verbatim on both kernels. Note the\n * handler count is read here, before the loop — the same instant both\n * hand-written copies read it.\n */\nfunction traceDispatch(name: string, handlers: readonly HookHandler[], logger: HookDispatchLogger): void {\n logger.debug(`Triggering hook: ${name}`, {\n hook: name,\n handlerCount: handlers.length,\n });\n}\n\n/**\n * Dispatch a hook ISOLATING failures: a handler that throws is logged as\n * `Hook handler failed: <name>` and the remaining handlers still run.\n *\n * Used by `ObjectKernelBase.triggerHook` and by `ObjectKernel`'s own\n * `kernel:shutdown` dispatch — the two sites that until #5282 were separate\n * hand-written loops printing the same line.\n *\n * ⛔ Wrong dispatcher for anything on the BOOT path — see the module docs and\n * {@link dispatchHookPropagating}.\n *\n * `handlers` is taken by reference, never copied: a handler that subscribes\n * another handler to the same hook mid-dispatch is picked up by this loop, and\n * that has always been true of both originals.\n *\n * @param name - Hook name\n * @param handlers - The hook's registered handlers, in registration order\n * @param logger - Receives the dispatch trace and one error line per failure\n * @param args - Arguments to pass to each handler\n */\nexport async function dispatchHookIsolating(\n name: string,\n handlers: readonly HookHandler[],\n logger: HookDispatchLogger,\n args: readonly any[] = [],\n): Promise<void> {\n traceDispatch(name, handlers, logger);\n\n for (const handler of handlers) {\n try {\n await handler(...args);\n } catch (error) {\n logger.error(`Hook handler failed: ${name}`, error as Error);\n // Continue with other handlers even if one fails\n }\n }\n}\n\n/**\n * Dispatch a hook PROPAGATING the first failure: the remaining handlers do not\n * run and the original error reaches the caller unwrapped.\n *\n * Used by `ObjectKernelBase.triggerHookOrThrow` (the boot-path hooks on\n * `LiteKernel`) and by BOTH kernels' `PluginContext.trigger`.\n *\n * `logger` is optional, and its absence is the one behavioural difference\n * between those callers: `context.trigger` has never emitted the\n * `Triggering hook: <name>` trace, so it passes no logger. Handing it one would\n * add a debug line to a path that has never had one — a behaviour change, and\n * #5282 preserves every call path's semantics exactly.\n *\n * @param name - Hook name\n * @param handlers - The hook's registered handlers, in registration order\n * @param logger - When given, receives the dispatch trace; omit to stay silent\n * @param args - Arguments to pass to each handler\n */\nexport async function dispatchHookPropagating(\n name: string,\n handlers: readonly HookHandler[],\n logger: HookDispatchLogger | undefined,\n args: readonly any[] = [],\n): Promise<void> {\n if (logger) traceDispatch(name, handlers, logger);\n\n for (const handler of handlers) {\n await handler(...args);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from './types.js';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { IServiceRegistry } from '@objectstack/spec/contracts';\nimport {\n resolvePluginOrder,\n validateInitServiceContract,\n assertInitServiceRequirements,\n describeInitOrderFault,\n} from './plugin-order.js';\nimport { dispatchHookIsolating, dispatchHookPropagating } from './hook-dispatch.js';\n\n/**\n * Kernel state machine\n */\nexport type KernelState = 'idle' | 'initializing' | 'running' | 'stopping' | 'stopped';\n\n/**\n * ObjectKernelBase - Abstract Base Class for Microkernel\n * \n * Provides common functionality for ObjectKernel and LiteKernel:\n * - Plugin management (Map storage)\n * - Dependency resolution (topological sort)\n * - Hook/Event system\n * - Context creation\n * - State validation\n * \n * This eliminates code duplication between the implementations.\n */\nexport abstract class ObjectKernelBase {\n protected plugins: Map<string, Plugin> = new Map();\n protected services: IServiceRegistry | Map<string, any> = new Map();\n protected hooks: Map<string, Array<(...args: any[]) => void | Promise<void>>> = new Map();\n protected state: KernelState = 'idle';\n protected logger: Logger;\n protected context!: PluginContext;\n /**\n * Name of the plugin whose init() is currently executing (Phase 1 runs\n * sequentially, so there is at most one). Lets a getService miss during\n * init name the structural fault (#4131) instead of only the symptom.\n */\n protected currentlyInitializing?: string;\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n /**\n * Validate kernel state\n * @param requiredState - Required state for the operation\n * @throws Error if current state doesn't match\n */\n protected validateState(requiredState: KernelState): void {\n if (this.state !== requiredState) {\n throw new Error(\n `[Kernel] Invalid state: expected '${requiredState}', got '${this.state}'`\n );\n }\n }\n\n /**\n * Validate kernel is in idle state (for plugin registration)\n */\n protected validateIdle(): void {\n if (this.state !== 'idle') {\n throw new Error('[Kernel] Cannot register plugins after bootstrap has started');\n }\n }\n\n /**\n * Create the plugin context\n * Subclasses can override to customize context creation\n */\n protected createContext(): PluginContext {\n return {\n registerService: (name, service) => {\n if (this.services instanceof Map) {\n if (this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' already registered`);\n }\n this.services.set(name, service);\n } else {\n // IServiceRegistry implementation\n this.services.register(name, service);\n }\n this.logger.info(`Service '${name}' registered`, { service: name });\n },\n getService: <T>(name: string): T => {\n if (this.services instanceof Map) {\n const service = this.services.get(name);\n if (!service) {\n throw new Error(\n `[Kernel] Service '${name}' not found${this.describeInitOrderFault(name)}`\n );\n }\n return service as T;\n } else {\n // IServiceRegistry implementation\n return this.services.get<T>(name);\n }\n },\n replaceService: <T>(name: string, implementation: T): void => {\n if (this.services instanceof Map) {\n if (!this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' not found. Use registerService() to add new services.`);\n }\n this.services.set(name, implementation);\n } else {\n // IServiceRegistry implementation\n if (!this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' not found. Use registerService() to add new services.`);\n }\n this.services.register(name, implementation);\n }\n this.logger.info(`Service '${name}' replaced`, { service: name });\n },\n hook: (name, handler) => {\n if (!this.hooks.has(name)) {\n this.hooks.set(name, []);\n }\n this.hooks.get(name)!.push(handler);\n },\n // PROPAGATING dispatch, and deliberately WITHOUT the trace line the\n // kernel's own dispatch sites emit — `context.trigger` has never\n // logged one, so no logger is handed over (#5282).\n trigger: async (name, ...args) => {\n await dispatchHookPropagating(name, this.hooks.get(name) || [], undefined, args);\n },\n getServices: () => {\n if (this.services instanceof Map) {\n return new Map(this.services);\n } else {\n // For IServiceRegistry, we need to return the underlying Map\n // This is a compatibility method\n return new Map();\n }\n },\n logger: this.logger,\n getKernel: () => this as any,\n registerServiceFactory: (_name, _factory, _lifecycle, _dependencies) => {\n throw new Error('[KernelBase] registerServiceFactory not supported — use ObjectKernel');\n },\n getServiceScoped: async <T>(_name: string, _scopeId: string): Promise<T> => {\n throw new Error('[KernelBase] getServiceScoped not supported — use ObjectKernel');\n },\n };\n }\n\n /**\n * Resolve plugin dependencies using topological sort — `dependencies`\n * hard, `optionalDependencies` order-if-present (ADR-0116, #4131). One\n * implementation shared with ObjectKernel via `plugin-order.ts`.\n * @returns Ordered list of plugins (dependencies first)\n */\n protected resolveDependencies(): Plugin[] {\n return resolvePluginOrder(this.plugins);\n }\n\n /**\n * Whether a service is registered on this kernel right now. Backs the\n * init-service contract checks (#4131).\n */\n protected hasRegisteredService(name: string): boolean {\n // Both the plain Map and IServiceRegistry expose `has`.\n return this.services.has(name);\n }\n\n /**\n * Pre-Phase-1 ordering validation (ADR-0116, #4131): a plugin whose\n * `requiresServices` names a service provided only by a LATER plugin is\n * a named boot error before any init side effects.\n */\n protected validateInitServices(ordered: Plugin[]): void {\n validateInitServiceContract(ordered, (name) => this.hasRegisteredService(name));\n }\n\n /**\n * When a getService miss happens while a plugin's init() is running,\n * append the structural diagnosis (#4131): which plugin was initializing,\n * and — when a composed plugin declares the service — who provides it.\n * Empty string outside Phase 1, so non-boot messages stay unchanged.\n */\n protected describeInitOrderFault(serviceName: string): string {\n return describeInitOrderFault(this.currentlyInitializing, this.plugins.values(), serviceName);\n }\n\n /**\n * Run plugin init phase\n * @param plugin - Plugin to initialize\n */\n protected async runPluginInit(plugin: Plugin): Promise<void> {\n const pluginName = plugin.name;\n this.logger.info(`Initializing plugin: ${pluginName}`);\n\n // Authoritative init-service check (#4131): Phase 1 is sequential,\n // so a required service absent NOW is absent for this init.\n assertInitServiceRequirements(plugin, (name) => this.hasRegisteredService(name));\n\n this.currentlyInitializing = pluginName;\n try {\n await plugin.init(this.context);\n this.logger.info(`Plugin initialized: ${pluginName}`);\n } catch (error) {\n this.logger.error(`Plugin init failed: ${pluginName}`, error as Error);\n throw error;\n } finally {\n this.currentlyInitializing = undefined;\n }\n }\n\n /**\n * Run plugin start phase\n * @param plugin - Plugin to start\n */\n protected async runPluginStart(plugin: Plugin): Promise<void> {\n if (!plugin.start) return;\n \n const pluginName = plugin.name;\n this.logger.info(`Starting plugin: ${pluginName}`);\n \n try {\n await plugin.start(this.context);\n this.logger.info(`Plugin started: ${pluginName}`);\n } catch (error) {\n this.logger.error(`Plugin start failed: ${pluginName}`, error as Error);\n throw error;\n }\n }\n\n /**\n * Run plugin destroy phase\n * @param plugin - Plugin to destroy\n */\n protected async runPluginDestroy(plugin: Plugin): Promise<void> {\n if (!plugin.destroy) return;\n \n const pluginName = plugin.name;\n this.logger.info(`Destroying plugin: ${pluginName}`);\n \n try {\n await plugin.destroy();\n this.logger.info(`Plugin destroyed: ${pluginName}`);\n } catch (error) {\n this.logger.error(`Plugin destroy failed: ${pluginName}`, error as Error);\n throw error;\n }\n }\n\n /**\n * Trigger a hook with all registered handlers, ISOLATING failures: a\n * handler that throws is logged and the remaining handlers still run.\n *\n * Use this for hooks where one subscriber's failure must not deny the\n * others their turn — notification-style hooks, and `kernel:shutdown`,\n * where the handlers still queued behind the failing one are the cleanup\n * that flushes buffers and releases resources (#5257).\n *\n * It is the WRONG dispatcher for anything on the BOOT path. Every hook\n * dispatched before \"✅ Bootstrap complete\" is a precondition of that\n * claim, so swallowing a throw there does not rescue the boot — it only\n * hides the failure behind a process that reports success. Those hooks\n * (`kernel:ready`, `kernel:bootstrapped`, `kernel:listening`) use\n * {@link triggerHookOrThrow} (#5170, #5257).\n *\n * The loop itself lives in {@link dispatchHookIsolating} — one\n * implementation shared with `ObjectKernel`'s own `kernel:shutdown`\n * dispatch, which cannot inherit this method (`ObjectKernel` does not\n * extend this class) and used to hand-mirror it (#5282).\n *\n * @param name - Hook name\n * @param args - Arguments to pass to handlers\n */\n protected async triggerHook(name: string, ...args: any[]): Promise<void> {\n await dispatchHookIsolating(name, this.hooks.get(name) || [], this.logger, args);\n }\n\n /**\n * Trigger a hook with all registered handlers, PROPAGATING the first\n * failure: the remaining handlers do not run and the original error\n * reaches the caller unwrapped.\n *\n * This is the dispatch semantics `ObjectKernel` has always had for every\n * lifecycle hook (its `context.trigger` is a bare awaited loop that never\n * catches). `LiteKernel` used the isolating {@link triggerHook} for all of\n * them, so one hook name meant two opposite things depending on which\n * kernel booted the same plugin code (#5170).\n *\n * `LiteKernel` now uses this dispatcher for all three BOOT-path hooks:\n *\n * - `kernel:ready` (#5170) — the only correct moment for a plugin to\n * assert that the preconditions it declared were actually met (the\n * registries are still filling during `init()`), so \"declared but not\n * deliverable ⇒ refuse to boot\" gates live there. On LiteKernel, which\n * is what vitest/serverless/edge run, they were downgraded to an error\n * log while the process carried on serving traffic without the\n * guarantee it claimed.\n * - `kernel:bootstrapped` and `kernel:listening` (#5257) — the same\n * argument one hook later. `kernel:listening` is where HTTP server\n * plugins open their socket, so a swallowed failure there produced the\n * worst shape available: a live process printing \"✅ Bootstrap complete\"\n * with nothing listening. `kernel:bootstrapped` carries reconcile and\n * audit passes whose silent failure is a quieter version of the same\n * lie.\n *\n * Deliberately NOT applied to `kernel:shutdown`, which keeps\n * {@link triggerHook}: on the teardown path a failing handler must not\n * block the cleanup queued behind it. That is a per-hook judgement\n * recorded at the dispatch site in `lite-kernel.ts`, not an inherited\n * default — and it is the reason this dispatcher is chosen per hook rather\n * than swapped in wholesale.\n *\n * The loop itself lives in {@link dispatchHookPropagating} — the same\n * function `PluginContext.trigger` runs on both kernels, so \"propagating\"\n * means one thing repo-wide (#5282).\n *\n * @param name - Hook name\n * @param args - Arguments to pass to handlers\n */\n protected async triggerHookOrThrow(name: string, ...args: any[]): Promise<void> {\n await dispatchHookPropagating(name, this.hooks.get(name) || [], this.logger, args);\n }\n\n /**\n * Get current kernel state\n */\n getState(): KernelState {\n return this.state;\n }\n\n /**\n * Get all registered plugins\n */\n getPlugins(): Map<string, Plugin> {\n return new Map(this.plugins);\n }\n\n /**\n * Abstract methods to be implemented by subclasses\n */\n abstract use(plugin: Plugin): this | Promise<this>;\n abstract bootstrap(): Promise<void>;\n abstract destroy(): Promise<void>;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { LoggerConfig, LogLevel } from '@objectstack/spec/system';\nimport type { Logger } from '@objectstack/spec/contracts';\n\n// Re-export the contract type so consumers can do\n// `import type { Logger } from '@objectstack/core/logger'` without also\n// pulling `@objectstack/spec` into their bundle graph manually.\nexport type { Logger };\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n fatal: 4,\n silent: 5,\n};\n\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n debug: '\\x1b[36m',\n info: '\\x1b[32m',\n warn: '\\x1b[33m',\n error: '\\x1b[31m',\n fatal: '\\x1b[35m',\n silent: '',\n};\n\nconst RESET = '\\x1b[0m';\n\n/**\n * Split a field name into lowercase words on camelCase, `snake_case`,\n * `kebab-case`, dot and letter/digit boundaries.\n *\n * `apiKey` / `api_key` / `API_KEY` / `x-api-key` all tokenize to\n * `['api','key']`, while `monkey`, `keyword` and `tokenizer` stay a single\n * word. That difference is the whole point: it is what makes the redactor a\n * **word-boundary** matcher instead of the substring matcher it used to be\n * (#5573) — a plain `keys` field no longer reads as a secret.\n */\nfunction tokenizeFieldName(name: string): string[] {\n return name\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2') // apiKey -> api Key\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // APIKey -> API Key\n .replace(/([a-zA-Z])([0-9])/g, '$1 $2') // key2 -> key 2\n .split(/[^A-Za-z0-9]+/) // _ - . / space\n .filter(Boolean)\n .map((word) => word.toLowerCase());\n}\n\n/**\n * Singular form of the plural spellings the redact vocabulary actually meets\n * (`keys`, `tokens`, `secrets`, `passwords`, `passes`). Deliberately not a\n * general inflector — it only has to be right for words that end up next to a\n * redact word, and it must never turn `address`/`status` into a new word.\n */\nfunction singularizeWord(word: string): string {\n if (/(?:ss|us|is)$/.test(word)) return word; // address / status / axis\n if (/(?:ch|sh|s|x|z)es$/.test(word)) return word.slice(0, -2); // passes / boxes\n if (/[a-z0-9]s$/.test(word)) return word.slice(0, -1); // keys / tokens\n return word;\n}\n\n/**\n * Words that mark the *secret* sense of a redact word when they are glued to\n * it with no boundary to split on: `apikey`, `accesstoken`, `clientsecret`.\n *\n * Word-boundary matching covers every field name spelled the way this repo\n * spells names (camelCase config keys / snake_case machine names — Prime\n * Directive #3), but an all-lowercase concatenation has no boundary at all, so\n * `apikey` would tokenize to one word and stop being redacted. A bare\n * \"ends with `key`\" rule cannot be used to rescue it, because `monkey`,\n * `turkey` and `whiskey` end with `key` too — the exact false positives #5573\n * exists to remove. So the rescue is scoped to this explicit qualifier list:\n * `<qualifier><redact word>` is a secret, anything else glued to a redact word\n * is not.\n *\n * Consequences, on purpose:\n * - Only a **suffix** concatenation counts. `secretary` and `keyword` start\n * with a redact word and stay clear.\n * - An unlisted qualifier (`foobarkey`) is not redacted. The fix is to spell\n * the field `fooBarKey` / `foo_bar_key`, which matches generically — or to\n * add the word here.\n */\nconst CONCATENATED_SECRET_QUALIFIERS = new Set([\n 'access',\n 'account',\n 'admin',\n 'api',\n 'app',\n 'auth',\n 'bearer',\n 'client',\n 'csrf',\n 'db',\n 'database',\n 'encryption',\n 'id',\n 'jwt',\n 'master',\n 'oauth',\n 'private',\n 'public',\n 'refresh',\n 'root',\n 'secret',\n 'service',\n 'session',\n 'shared',\n 'sign',\n 'signing',\n 'ssh',\n 'token',\n 'user',\n 'webhook',\n 'xsrf',\n]);\n\n/** `apikey`/`apikeys` vs `key` — see {@link CONCATENATED_SECRET_QUALIFIERS}. */\nfunction isQualifiedConcatenation(word: string, redactWord: string): boolean {\n for (const base of [word, singularizeWord(word)]) {\n if (base.length <= redactWord.length || !base.endsWith(redactWord)) continue;\n if (CONCATENATED_SECRET_QUALIFIERS.has(base.slice(0, base.length - redactWord.length))) return true;\n }\n return false;\n}\n\n/** Does `words` contain `run` as a consecutive sub-sequence? */\nfunction containsWordRun(words: string[], run: string[]): boolean {\n for (let i = 0; i + run.length <= words.length; i++) {\n if (run.every((word, offset) => words[i + offset] === word)) return true;\n }\n return false;\n}\n\n/**\n * Word-boundary match of one configured redact pattern against one field name,\n * both already tokenized by {@link tokenizeFieldName}.\n *\n * The plural rule is the one subtlety, and it is the maintainer's ruling on\n * #5573 made consistent with itself: a **bare** plural names a collection or a\n * count, not a secret (`keys` on a Zod `unrecognized_keys` issue, `tokens` on\n * an LLM usage record), so it is left alone; a plural **inside a compound**\n * still names the secret (`apiKeys: ['sk-…']`, `refresh_tokens`) and is\n * redacted. Singular words match everywhere, compound or not.\n */\nfunction fieldWordsMatchPattern(nameWords: string[], patternWords: string[]): boolean {\n if (patternWords.length === 0 || nameWords.length === 0) return false;\n\n // A multi-word pattern (`apiKey`, `api_key`) matches a consecutive run of\n // the same words, or those words written as one concatenated token.\n if (patternWords.length > 1) {\n const glued = patternWords.join('');\n return (\n containsWordRun(nameWords, patternWords) ||\n nameWords.some((word) => word === glued || singularizeWord(word) === glued)\n );\n }\n\n const redactWord = patternWords[0];\n const isCompound = nameWords.filter((word) => /[a-z]/.test(word)).length > 1;\n return nameWords.some(\n (word) =>\n word === redactWord ||\n (isCompound && singularizeWord(word) === redactWord) ||\n isQualifiedConcatenation(word, redactWord),\n );\n}\n\n/**\n * Whether ANSI color may be written to the given stream.\n *\n * Follows the https://no-color.org convention: a non-empty `NO_COLOR` env var\n * disables color regardless of TTY, and non-TTY destinations (pipes, CI logs,\n * redirected output) always get plain text so plain-text log scanners see\n * uncolored level tags. Browser bundles have no `process`/TTY → plain text.\n */\nfunction colorEnabled(stream: { isTTY?: boolean } | undefined): boolean {\n if (typeof process !== 'undefined') {\n const noColor = (process as any).env?.NO_COLOR;\n if (noColor !== undefined && noColor !== '') return false;\n }\n return Boolean(stream?.isTTY);\n}\n\n/**\n * Resolve a Node builtin without putting it in this module's import graph.\n *\n * This entry is deliberately browser-safe — `@objectstack/client` bundles it —\n * so `fs`/`path` must never be imported statically. A lazy `require()` used to\n * meet that bar, but esbuild rewrites it to the `__require` shim in the ESM\n * output, which throws `Dynamic require of \"fs\" is not supported`. Every Node\n * ESM consumer (`os serve`, `os dev`) therefore lost file logging (#3110).\n * `process.getBuiltinModule` is a plain method call — opaque to bundlers — and\n * works in both module systems.\n */\nfunction loadNodeBuiltin<T>(id: string): T | undefined {\n if (typeof process === 'undefined') return undefined;\n\n const getBuiltinModule = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule;\n if (typeof getBuiltinModule === 'function') {\n try {\n return getBuiltinModule.call(process, `node:${id}`) as T;\n } catch {\n return undefined;\n }\n }\n\n // Node < 20.16 / < 22.3 predates `getBuiltinModule`. Real `require` still\n // resolves in the CJS build; in the ESM build this is the shim that throws,\n // which the caller now reports rather than swallows.\n try {\n return require(id) as T;\n } catch {\n return undefined;\n }\n}\n\nexport class ObjectLogger implements Logger {\n private config: Required<Omit<LoggerConfig, 'file' | 'rotation' | 'name'>> & {\n file?: string;\n rotation?: { maxSize: string; maxFiles: number };\n name?: string;\n };\n private bindings: Record<string, any>;\n /** `config.redact`, tokenized once — see {@link fieldWordsMatchPattern}. */\n private redactPatterns: string[][];\n private fileStream?: any;\n /** Only the logger that opened the stream may close it — children share it. */\n private ownsFileStream = false;\n private fileLoggingDisabled = false;\n\n constructor(config: Partial<LoggerConfig> = {}, bindings: Record<string, any> = {}) {\n this.config = {\n name: config.name,\n level: config.level ?? 'info',\n format: config.format ?? 'pretty',\n redact: config.redact ?? ['password', 'token', 'secret', 'key'],\n sourceLocation: config.sourceLocation ?? false,\n file: config.file,\n // Per-key, because `LoggerConfig` is the AUTHOR state (ADR-0122): the\n // schema defaults `maxSize`/`maxFiles` *inside* `rotation`, so a caller\n // may legitimately write `{ rotation: { maxSize: '5m' } }` and this\n // constructor — which does not parse — has to fill the other half the\n // same way `LoggerConfigSchema.parse` would.\n rotation: {\n maxSize: config.rotation?.maxSize ?? '10m',\n maxFiles: config.rotation?.maxFiles ?? 5,\n },\n };\n this.bindings = bindings;\n this.redactPatterns = this.config.redact.map(tokenizeFieldName).filter((words) => words.length > 0);\n\n if (this.config.file && typeof process !== 'undefined') {\n this.openFileStream(this.config.file);\n }\n }\n\n private openFileStream(path: string) {\n const fs = loadNodeBuiltin<typeof import('node:fs')>('fs');\n const nodePath = loadNodeBuiltin<typeof import('node:path')>('path');\n if (!fs || !nodePath) {\n this.disableFileLogging(path, 'no filesystem access in this runtime');\n return;\n }\n\n try {\n fs.mkdirSync(nodePath.dirname(path), { recursive: true });\n const stream = fs.createWriteStream(path, { flags: 'a' });\n // `createWriteStream` reports open failures (EACCES, EISDIR, …)\n // asynchronously. An 'error' event with no listener is fatal to the\n // process, so file logging must degrade here rather than take the\n // host down.\n stream.on('error', (err: Error) => this.disableFileLogging(path, err.message));\n this.fileStream = stream;\n this.ownsFileStream = true;\n } catch (err) {\n this.disableFileLogging(path, (err as Error).message);\n }\n }\n\n /**\n * Report — once — that an explicitly configured `file` destination is not\n * being written, and stop trying.\n *\n * Deliberately not routed through `write()`: this says the logger cannot\n * honour its own config, so `level` must not filter it. The bare `catch {}`\n * this replaces is exactly how #3110 stayed hidden.\n */\n private disableFileLogging(path: string, reason: string) {\n this.fileStream = undefined;\n this.ownsFileStream = false;\n if (this.fileLoggingDisabled) return;\n this.fileLoggingDisabled = true;\n\n const label = this.config.name ? `[${this.config.name}] ` : '';\n const notice = `${label}logger: file logging disabled — cannot write to ${path}: ${reason}`;\n if (typeof process !== 'undefined' && (process as any).stderr) {\n (process as any).stderr.write(notice + '\\n');\n } else if (typeof console !== 'undefined') {\n console.warn(notice);\n }\n }\n\n private isEnabled(level: LogLevel): boolean {\n return LEVEL_ORDER[level] >= LEVEL_ORDER[this.config.level];\n }\n\n /**\n * Whether a meta field name names one of the configured secrets.\n *\n * Until #5573 this was `lower.includes(pattern)`, which redacted every\n * field whose name merely *contained* a redact word — `keys`, `keyword`,\n * `tokens`, `monkey`, `secretary` — and replaced its value with\n * `***REDACTED***`, so the reader lost the fact AND was told a secret had\n * been withheld. Matching is now on word boundaries: `key` matches\n * `apiKey` / `api_key`, not `keys` / `monkey` / `keyword`.\n */\n private isRedactedFieldName(key: string): boolean {\n const nameWords = tokenizeFieldName(key);\n return this.redactPatterns.some((pattern) => fieldWordsMatchPattern(nameWords, pattern));\n }\n\n private redactSensitive(obj: any): any {\n if (!obj || typeof obj !== 'object') return obj;\n const redacted = Array.isArray(obj) ? [...obj] : { ...obj };\n for (const key in redacted) {\n if (this.isRedactedFieldName(key)) {\n redacted[key] = '***REDACTED***';\n } else if (typeof redacted[key] === 'object' && redacted[key] !== null) {\n redacted[key] = this.redactSensitive(redacted[key]);\n }\n }\n return redacted;\n }\n\n private write(level: LogLevel, message: string, meta?: Record<string, any>, error?: Error) {\n if (!this.isEnabled(level)) return;\n\n const context = this.redactSensitive({\n ...this.bindings,\n ...meta,\n ...(error ? { error: { message: error.message, stack: error.stack } } : {}),\n });\n\n const hasContext = Object.keys(context).length > 0;\n const ts = new Date().toISOString();\n\n const isErrorLevel = level === 'error' || level === 'fatal';\n const proc = typeof process !== 'undefined' ? (process as any) : undefined;\n const stream = proc ? (isErrorLevel ? proc.stderr : proc.stdout) : undefined;\n\n let line: string; // console output — may carry ANSI color\n let plainLine: string; // file output — never colored\n\n if (this.config.format === 'json') {\n line = plainLine = JSON.stringify({\n time: ts,\n level,\n ...(this.config.name ? { name: this.config.name } : {}),\n msg: message,\n ...context,\n });\n } else if (this.config.format === 'text') {\n const parts = [ts, level.toUpperCase(), message];\n if (hasContext) parts.push(JSON.stringify(context));\n line = plainLine = parts.join(' | ');\n } else {\n // pretty\n const label = this.config.name ? `[${this.config.name}] ` : '';\n const head = `${ts} ${level.toUpperCase()}`;\n let tail = ` ${label}${message}`;\n if (hasContext) tail += ` ${JSON.stringify(context)}`;\n plainLine = head + tail;\n const color = LEVEL_COLORS[level] || '';\n line = color && colorEnabled(stream) ? `${color}${head}${RESET}${tail}` : plainLine;\n }\n\n // Browser-safe output: prefer process streams when available, otherwise\n // fall back to console. `process` may be missing entirely (browsers) or\n // present without stdio streams (bundler shims) — both fall through to\n // console. The previous unguarded `process.stderr?.write` threw\n // `ReferenceError: process is not defined` in browsers because\n // `process` itself is the missing global, not just its `stderr` field.\n if (stream) {\n stream.write(line + '\\n');\n } else if (typeof console !== 'undefined') {\n const fn =\n level === 'error' || level === 'fatal' ? console.error\n : level === 'warn' ? console.warn\n : level === 'debug' ? console.debug\n : console.log;\n fn(line);\n }\n\n if (this.fileStream) {\n this.fileStream.write(plainLine + '\\n');\n }\n }\n\n debug(message: string, meta?: Record<string, any>): void {\n this.write('debug', message, meta);\n }\n\n info(message: string, meta?: Record<string, any>): void {\n this.write('info', message, meta);\n }\n\n warn(message: string, meta?: Record<string, any>): void {\n this.write('warn', message, meta);\n }\n\n error(message: string, errorOrMeta?: Error | Record<string, any>, meta?: Record<string, any>): void {\n this.writeErrorLike('error', message, errorOrMeta, meta);\n }\n\n fatal(message: string, errorOrMeta?: Error | Record<string, any>, meta?: Record<string, any>): void {\n this.writeErrorLike('fatal', message, errorOrMeta, meta);\n }\n\n /**\n * `error`/`fatal` dispatch — the two levels whose contract has an `Error`\n * slot in front of `meta`.\n *\n * The `Logger` contract declares `error(message, error?: Error, meta?)`, and\n * `ObjectLogger` additionally tolerates a **meta object** in the `error`\n * slot because many in-repo call sites write `logger.error(msg, { … })`.\n * That tolerance is fine; dropping a parameter the contract *declares* is\n * not, and that is what the previous dispatch did:\n *\n * if (errorOrMeta instanceof Error) this.write(level, message, meta, errorOrMeta);\n * else this.write(level, message, errorOrMeta);\n *\n * With `error === undefined` the `else` branch passed `undefined` as the\n * meta and **never read the third argument**, so every contract-shaped\n * `logger.error(msg, undefined, { … })` call rendered a bare message with\n * its diagnostics silently gone — ~15 such call sites across `metadata`,\n * `metadata-protocol`, `client` and `core/security`, plus the connector\n * reconcile seam that found this (#5575). The contract's two sibling\n * implementations (`ConsoleLogger`/`JsonLogger` in `@objectstack/observability`)\n * both honour the slot, so the contract was right and this class was the\n * outlier — declared ≠ enforced, Prime Directive #10.\n *\n * All three shapes are now honoured. When both slots carry meta, `meta`\n * (the later, more specific argument) wins on a key collision.\n */\n private writeErrorLike(\n level: 'error' | 'fatal',\n message: string,\n errorOrMeta?: Error | Record<string, any>,\n meta?: Record<string, any>,\n ): void {\n if (errorOrMeta instanceof Error) {\n this.write(level, message, meta, errorOrMeta);\n return;\n }\n const merged = errorOrMeta && meta ? { ...errorOrMeta, ...meta } : (errorOrMeta ?? meta);\n this.write(level, message, merged);\n }\n\n log(message: string, ...args: any[]): void {\n this.info(message, args.length > 0 ? { args } : undefined);\n }\n\n child(context: Record<string, any>): ObjectLogger {\n // Construct without `file`, then share the parent's stream: the\n // constructor opens eagerly, so passing `file` through would open a\n // second stream per child and immediately orphan it. That leak was\n // unreachable while #3110 kept the ESM open path dead.\n const child = new ObjectLogger({ ...this.config, file: undefined }, { ...this.bindings, ...context });\n child.config.file = this.config.file;\n child.fileStream = this.fileStream;\n return child;\n }\n\n withTrace(traceId: string, spanId?: string): ObjectLogger {\n return this.child({ traceId, spanId });\n }\n\n async destroy(): Promise<void> {\n const stream = this.fileStream;\n this.fileStream = undefined;\n // Children share the opener's stream; if they closed it too, one child's\n // teardown would end file logging for the parent and every sibling,\n // whose writes then land on a closed stream and only trip the 'error'\n // handler above.\n if (!stream || !this.ownsFileStream) return;\n this.ownsFileStream = false;\n await new Promise<void>((resolve) => stream.end(resolve));\n }\n}\n\nexport function createLogger(config?: Partial<LoggerConfig>): ObjectLogger {\n return new ObjectLogger(config);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext } from './types.js';\nimport { createLogger, ObjectLogger } from './logger.js';\nimport type { LoggerConfig } from '@objectstack/spec/system';\nimport { ServiceRequirementDef } from '@objectstack/spec/system';\nimport { PluginLoader, PluginMetadata, ServiceLifecycle, ServiceFactory, PluginStartupResult } from './plugin-loader.js';\nimport { isNode, safeExit } from './utils/env.js';\nimport { CORE_FALLBACK_FACTORIES } from './fallbacks/index.js';\nimport {\n resolvePluginOrder,\n validateInitServiceContract,\n assertInitServiceRequirements,\n describeInitOrderFault,\n} from './plugin-order.js';\nimport { dispatchHookIsolating, dispatchHookPropagating } from './hook-dispatch.js';\n\n/**\n * Enhanced Kernel Configuration\n */\nexport interface ObjectKernelConfig {\n logger?: Partial<LoggerConfig>;\n \n /** Default plugin startup timeout in milliseconds */\n defaultStartupTimeout?: number;\n \n /** Whether to enable graceful shutdown */\n gracefulShutdown?: boolean;\n \n /** Graceful shutdown timeout in milliseconds */\n shutdownTimeout?: number;\n \n /** Whether to rollback on startup failure */\n rollbackOnFailure?: boolean;\n \n /** Whether to skip strict system requirement validation (Critical for testing) */\n skipSystemValidation?: boolean;\n}\n\n/**\n * Enhanced ObjectKernel with Advanced Plugin Management\n * \n * Extends the basic ObjectKernel with:\n * - Async plugin loading with validation\n * - Version compatibility checking\n * - Plugin signature verification\n * - Configuration validation (Zod)\n * - Factory-based dependency injection\n * - Service lifecycle management (singleton/transient/scoped)\n * - Circular dependency detection\n * - Lazy loading services\n * - Graceful shutdown\n * - Plugin startup timeout control\n * - Startup failure rollback\n * - Plugin health checks\n */\nexport class ObjectKernel {\n private plugins: Map<string, PluginMetadata> = new Map();\n private services: Map<string, any> = new Map();\n private hooks: Map<string, Array<(...args: any[]) => void | Promise<void>>> = new Map();\n private state: 'idle' | 'initializing' | 'running' | 'stopping' | 'stopped' = 'idle';\n private logger: ObjectLogger;\n private context: PluginContext;\n private pluginLoader: PluginLoader;\n private config: ObjectKernelConfig;\n private startedPlugins: Set<string> = new Set();\n private pluginStartTimes: Map<string, number> = new Map();\n private shutdownHandlers: Array<() => Promise<void>> = [];\n /**\n * Name of the plugin whose init() is currently executing (Phase 1 is\n * sequential, so at most one). Lets a getService miss during init name\n * the structural fault (#4131) instead of only the symptom.\n */\n private currentlyInitializing?: string;\n\n constructor(config: ObjectKernelConfig = {}) {\n this.config = {\n defaultStartupTimeout: 30000, // 30 seconds\n gracefulShutdown: true,\n shutdownTimeout: 60000, // 60 seconds\n rollbackOnFailure: true,\n ...config,\n };\n\n this.logger = createLogger(config.logger);\n this.pluginLoader = new PluginLoader(this.logger);\n \n // Initialize context\n this.context = {\n registerService: (name, service) => {\n this.registerService(name, service);\n },\n registerServiceFactory: (name, factory, lifecycle, dependencies) => {\n this.registerServiceFactory(name, factory, lifecycle, dependencies);\n },\n getService: <T>(name: string) => {\n // 1. Try direct service map first (synchronous cache)\n const service = this.services.get(name);\n if (service) {\n return service as T;\n }\n\n // 2. Try to get from plugin loader cache (Sync access to factories)\n const loaderService = this.pluginLoader.getServiceInstance<T>(name);\n if (loaderService) {\n // Cache it locally for faster next access\n this.services.set(name, loaderService);\n return loaderService;\n }\n\n // 3. Neither sync map has it. Two very different faults share\n // this branch and MUST NOT share one message (#4085):\n // (a) nothing ever registered `name` — a composition /\n // ordering fault at the CALLER (e.g. a plugin reaching\n // for `manifest` in init() before the engine plugin\n // registered it);\n // (b) `name` IS registered, as a factory that has not been\n // instantiated yet — the caller merely used the wrong\n // accessor and needs `getServiceAsync`.\n // `pluginLoader.getService` is an `async` method, so its\n // return value is ALWAYS a Promise and its internal\n // \"not found\" rejection can never surface synchronously.\n // Reading (a) off that Promise therefore reported every\n // missing service as \"is async - use await\" — the wrong fix,\n // pointing at the wrong layer. Decide from the registry\n // instead, which is synchronous and authoritative.\n if (!this.pluginLoader.hasService(name)) {\n throw new Error(\n `[Kernel] Service '${name}' not found${this.describeInitOrderFault(name)}`\n );\n }\n\n // Registered but not instantiated ⇒ factory-backed. Message\n // kept verbatim: callers that tolerate an async-only service\n // (console static assets, the HTTP dispatcher) match on\n // `is async`.\n throw new Error(`Service '${name}' is async - use await`);\n },\n replaceService: <T>(name: string, implementation: T): void => {\n const hasService = this.services.has(name) || this.pluginLoader.hasService(name);\n if (!hasService) {\n throw new Error(`[Kernel] Service '${name}' not found. Use registerService() to add new services.`);\n }\n this.services.set(name, implementation);\n this.pluginLoader.replaceService(name, implementation);\n this.logger.info(`Service '${name}' replaced`, { service: name });\n },\n hook: (name, handler) => {\n if (!this.hooks.has(name)) {\n this.hooks.set(name, []);\n }\n this.hooks.get(name)!.push(handler);\n },\n // PROPAGATING dispatch — the same shared loop `LiteKernel`'s\n // context.trigger runs, and deliberately WITHOUT a trace line:\n // `context.trigger` has never emitted one on either kernel, so no\n // logger is handed over (#5282).\n trigger: async (name, ...args) => {\n await dispatchHookPropagating(name, this.hooks.get(name) || [], undefined, args);\n },\n getServices: () => {\n return new Map(this.services);\n },\n getServiceScoped: <T>(name: string, scopeId: string): Promise<T> => {\n return this.pluginLoader.getService<T>(name, scopeId);\n },\n logger: this.logger,\n getKernel: () => this as any, // Type compatibility\n };\n\n this.pluginLoader.setContext(this.context);\n\n // Register shutdown handler\n if (this.config.gracefulShutdown) {\n this.registerShutdownSignals();\n }\n }\n\n /**\n * Register a plugin with enhanced validation\n */\n async use(plugin: Plugin): Promise<this> {\n if (this.state !== 'idle') {\n throw new Error('[Kernel] Cannot register plugins after bootstrap has started');\n }\n\n // Load plugin through enhanced loader\n const result = await this.pluginLoader.loadPlugin(plugin);\n \n if (!result.success || !result.plugin) {\n throw new Error(`Failed to load plugin: ${plugin.name} - ${result.error?.message}`);\n }\n\n const pluginMeta = result.plugin;\n this.plugins.set(pluginMeta.name, pluginMeta);\n \n this.logger.info(`Plugin registered: ${pluginMeta.name}@${pluginMeta.version}`, {\n plugin: pluginMeta.name,\n version: pluginMeta.version,\n });\n\n return this;\n }\n\n /**\n * Register a service instance directly\n */\n registerService<T>(name: string, service: T): this {\n if (this.services.has(name)) {\n throw new Error(`[Kernel] Service '${name}' already registered`);\n }\n this.services.set(name, service);\n this.pluginLoader.registerService(name, service);\n this.logger.info(`Service '${name}' registered`, { service: name });\n return this;\n }\n\n /**\n * Register a service factory with lifecycle management\n */\n registerServiceFactory<T>(\n name: string,\n factory: ServiceFactory<T>,\n lifecycle: ServiceLifecycle = ServiceLifecycle.SINGLETON,\n dependencies?: string[]\n ): this {\n this.pluginLoader.registerServiceFactory({\n name,\n factory,\n lifecycle,\n dependencies,\n });\n return this;\n }\n\n /**\n * Pre-inject in-memory fallbacks for 'core' services that were not registered\n * by plugins during Phase 1. Called before Phase 2 so that all core services\n * (e.g. 'metadata', 'cache', 'queue') are resolvable via ctx.getService()\n * when plugin start() methods execute.\n */\n private preInjectCoreFallbacks() {\n if (this.config.skipSystemValidation) return;\n for (const [serviceName, criticality] of Object.entries(ServiceRequirementDef)) {\n if (criticality !== 'core') continue;\n const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);\n if (!hasService) {\n const factory = CORE_FALLBACK_FACTORIES[serviceName];\n if (factory) {\n const fallback = factory();\n this.registerService(serviceName, fallback);\n this.logger.debug(`[Kernel] Pre-injected in-memory fallback for '${serviceName}' before Phase 2`);\n }\n }\n }\n }\n\n /**\n * Validate Critical System Requirements\n */\n private validateSystemRequirements() {\n if (this.config.skipSystemValidation) {\n this.logger.debug('System requirement validation skipped');\n return;\n }\n\n this.logger.debug('Validating system service requirements...');\n const missingServices: string[] = [];\n const missingCoreServices: string[] = [];\n \n // Iterate through all defined requirements\n for (const [serviceName, criticality] of Object.entries(ServiceRequirementDef)) {\n const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);\n \n if (!hasService) {\n if (criticality === 'required') {\n this.logger.error(`CRITICAL: Required service missing: ${serviceName}`);\n missingServices.push(serviceName);\n } else if (criticality === 'core') {\n // Auto-inject in-memory fallback if available\n const factory = CORE_FALLBACK_FACTORIES[serviceName];\n if (factory) {\n const fallback = factory();\n this.registerService(serviceName, fallback);\n this.logger.warn(`Service '${serviceName}' not provided — using in-memory fallback`);\n } else {\n this.logger.warn(`CORE: Core service missing, functionality may be degraded: ${serviceName}`);\n missingCoreServices.push(serviceName);\n }\n } else {\n this.logger.info(`Info: Optional service not present: ${serviceName}`);\n }\n }\n }\n\n if (missingServices.length > 0) {\n const errorMsg = `System failed to start. Missing critical services: ${missingServices.join(', ')}`;\n this.logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n if (missingCoreServices.length > 0) {\n this.logger.warn(`System started with degraded capabilities. Missing core services: ${missingCoreServices.join(', ')}`);\n }\n \n this.logger.info('System requirement check passed');\n }\n\n /**\n * Bootstrap the kernel with enhanced features\n */\n async bootstrap(): Promise<void> {\n if (this.state !== 'idle') {\n throw new Error('[Kernel] Kernel already bootstrapped');\n }\n\n this.state = 'initializing';\n this.logger.info('Bootstrap started');\n\n try {\n // Check for circular dependencies\n const cycles = this.pluginLoader.detectCircularDependencies();\n if (cycles.length > 0) {\n this.logger.warn('Circular service dependencies detected:', { cycles });\n }\n\n // Resolve plugin dependencies\n const orderedPlugins = this.resolveDependencies();\n\n // Pre-Phase-1 ordering contract (ADR-0116, #4131): a plugin that\n // requires a service provided only by a later plugin fails HERE,\n // named, before any init side effects.\n validateInitServiceContract(orderedPlugins, (name) => this.hasAnyService(name));\n\n // Phase 1: Init - Plugins register services\n this.logger.info('Phase 1: Init plugins');\n for (const plugin of orderedPlugins) {\n await this.initPluginWithTimeout(plugin);\n }\n\n // Pre-inject in-memory fallbacks for 'core' services that were not\n // registered by any plugin during Phase 1. This ensures services like\n // 'metadata', 'cache', 'queue', etc. are always available when plugins\n // call ctx.getService() during their start() methods.\n this.preInjectCoreFallbacks();\n\n // Phase 2: Start - Plugins execute business logic\n this.logger.info('Phase 2: Start plugins');\n this.state = 'running';\n \n for (const plugin of orderedPlugins) {\n const result = await this.startPluginWithTimeout(plugin);\n \n if (!result.success) {\n this.logger.error(`Plugin startup failed: ${plugin.name}`, result.error);\n const origMsg = result.error instanceof Error ? result.error.message : String(result.error);\n const origStack = result.error instanceof Error ? result.error.stack : '';\n console.error(`[Kernel] Plugin startup failed: ${plugin.name}`, origMsg, origStack);\n\n if (this.config.rollbackOnFailure) {\n this.logger.warn('Rolling back started plugins...');\n await this.rollbackStartedPlugins();\n // Propagate the original cause through the thrown error\n // so callers (e.g. cloud auth-proxy) can surface the\n // real failure instead of an opaque \"rollback complete\"\n // string. Without this, every kernel-boot failure looks\n // identical from the outside.\n const err: any = new Error(\n `Plugin ${plugin.name} failed to start - rollback complete: ${origMsg}`,\n );\n if (result.error instanceof Error) {\n err.cause = result.error;\n err.originalStack = origStack;\n }\n throw err;\n }\n }\n }\n\n // Phase 3: Trigger kernel:ready hook\n this.validateSystemRequirements(); // Final check before ready\n this.logger.debug('Triggering kernel:ready hook');\n await this.context.trigger('kernel:ready');\n\n // Phase 3.5: Trigger kernel:bootstrapped AFTER every kernel:ready\n // handler has settled — the \"all synchronous bootstrap has settled\"\n // anchor. Reconcile/backfill work that consumes data produced by a\n // later-starting plugin's kernel:ready handler belongs here, not in\n // kernel:ready (where handler order would race the data). NOTE: this\n // does NOT guarantee background app seed data has settled (an inline\n // seed that overruns OS_INLINE_SEED_BUDGET_MS finishes later) —\n // subscribe `app:seeded` for that. See\n // packages/spec/src/contracts/plugin-lifecycle-events.ts.\n this.logger.debug('Triggering kernel:bootstrapped hook');\n await this.context.trigger('kernel:bootstrapped');\n\n // Phase 4: Trigger kernel:listening hook AFTER all kernel:ready\n // handlers have completed. This is the cue for HTTP server\n // plugins to actually open the listening socket — by now every\n // other plugin has finished registering routes/middleware.\n // See `kernel:listening` docs in\n // packages/spec/src/contracts/plugin-lifecycle-events.ts\n // for the race-condition rationale.\n this.logger.debug('Triggering kernel:listening hook');\n await this.context.trigger('kernel:listening');\n\n this.logger.info('✅ Bootstrap complete');\n } catch (error) {\n this.state = 'stopped';\n throw error;\n }\n }\n\n /**\n * Graceful shutdown with timeout\n */\n async shutdown(): Promise<void> {\n if (this.state === 'stopped' || this.state === 'stopping') {\n this.logger.warn('Kernel already stopped or stopping');\n return;\n }\n\n if (this.state !== 'running') {\n throw new Error('[Kernel] Kernel not running');\n }\n\n this.state = 'stopping';\n this.logger.info('Graceful shutdown started');\n\n // The ONE rejection that means \"teardown hung\". Created here so the\n // catch below can discriminate by IDENTITY (#5274): only this\n // `setTimeout` can produce this exact object, so no message match, no\n // `instanceof`, and nothing a plugin throws can ever impersonate it —\n // not even a handler throwing `new Error('Shutdown timeout exceeded')`.\n // That discrimination is the whole point: the catch used to be reached\n // by BOTH the timer and any exception escaping `performShutdown()`, and\n // it treated them identically — `process.exit(1)` under a log line\n // reading \"Shutdown timed out\" when nothing had timed out.\n const shutdownTimeoutError = new Error('Shutdown timeout exceeded');\n\n try {\n const shutdownPromise = this.performShutdown();\n const timeoutPromise = new Promise<void>((_, reject) => {\n const t = setTimeout(() => {\n reject(shutdownTimeoutError);\n }, this.config.shutdownTimeout);\n // Don't let this timer keep the event loop alive\n if (t.unref) t.unref();\n });\n\n await Promise.race([shutdownPromise, timeoutPromise]);\n\n this.state = 'stopped';\n this.logger.info('✅ Graceful shutdown complete');\n } catch (error) {\n this.state = 'stopped';\n\n if (error === shutdownTimeoutError) {\n // GENUINE timeout: `performShutdown()` is still running and has\n // stopped making progress, so the process would otherwise hang\n // holding whatever it failed to release. Hard-exit stays — it\n // is the only branch it was ever right for.\n this.logger.error('Shutdown timed out — forcing exit', error as Error);\n // Flush logger then hard-exit; the process would otherwise hang\n await this.logger.destroy();\n process.exit(1);\n } else {\n // NOT a timeout. `performShutdown()` isolates every teardown\n // step it owns (hook dispatch, each destroy(), each shutdown\n // handler), so reaching here means something outside those\n // loops failed — the teardown is over either way, and there is\n // nothing hung to escape from. Killing the host process here\n // would take away the embedding host's (cloud auth-proxy, CLI,\n // a test runner) chance to do its own cleanup, over a fault\n // that did not require it. Log and return down the normal\n // path; `shutdown()` still never rejects.\n this.logger.error(\n 'Shutdown finished with an unexpected teardown error — the kernel is stopped and the process is NOT being exited; some cleanup may not have run',\n error as Error,\n );\n }\n } finally {\n await this.logger.destroy();\n }\n }\n\n /**\n * Check health of a specific plugin\n */\n async checkPluginHealth(pluginName: string): Promise<any> {\n return await this.pluginLoader.checkPluginHealth(pluginName);\n }\n\n /**\n * Check health of all plugins\n */\n async checkAllPluginsHealth(): Promise<Map<string, any>> {\n const results = new Map();\n \n for (const pluginName of this.plugins.keys()) {\n const health = await this.checkPluginHealth(pluginName);\n results.set(pluginName, health);\n }\n \n return results;\n }\n\n /**\n * Get plugin startup metrics\n */\n getPluginMetrics(): Map<string, number> {\n return new Map(this.pluginStartTimes);\n }\n\n /**\n * Whether a plugin with the given name has been registered on this kernel.\n *\n * Registration happens synchronously in `use()` before any plugin's\n * `start()` runs, so a plugin may use this during its own start() to make\n * composition-dependent decisions deterministically — e.g. the dispatcher\n * bridge cedes `${prefix}/discovery` to `com.objectstack.rest.api` when\n * both are mounted (ADR-0076 D11: single owner per route, not\n * first-registration-wins).\n */\n hasPlugin(name: string): boolean {\n return this.plugins.has(name);\n }\n\n /**\n * Get a service (sync helper)\n */\n getService<T>(name: string): T {\n return this.context.getService<T>(name);\n }\n\n /**\n * Get a service asynchronously (supports factories)\n */\n async getServiceAsync<T>(name: string, scopeId?: string): Promise<T> {\n return await this.pluginLoader.getService<T>(name, scopeId);\n }\n\n /**\n * Clear all scoped service instances for a given scope (e.g., environmentId).\n * Releases driver connections and metadata caches for idle projects.\n */\n clearScope(scopeId: string): void {\n this.pluginLoader.clearScope(scopeId);\n }\n\n /**\n * Check if kernel is running\n */\n isRunning(): boolean {\n return this.state === 'running';\n }\n\n /**\n * Get kernel state\n */\n getState(): string {\n return this.state;\n }\n\n // Private methods\n\n private async initPluginWithTimeout(plugin: PluginMetadata): Promise<void> {\n const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout!;\n\n this.logger.debug(`Init: ${plugin.name}`, { plugin: plugin.name });\n\n // Authoritative init-service check (#4131): Phase 1 is sequential,\n // so a required service absent NOW is absent for this init.\n assertInitServiceRequirements(plugin, (name) => this.hasAnyService(name));\n\n this.currentlyInitializing = plugin.name;\n try {\n await this.raceStartupTimeout(\n plugin.init(this.context),\n timeout,\n `Plugin ${plugin.name} init timeout after ${timeout}ms`\n );\n } finally {\n this.currentlyInitializing = undefined;\n }\n }\n\n /**\n * Race a plugin lifecycle hook against its startup-timeout guard, and\n * reclaim the guard the moment the race settles (#4813).\n *\n * The guard used to be armed and then abandoned: when the plugin won the\n * race, its `setTimeout` stayed ref'd in the event loop for the full\n * `startupTimeout`, so every process idled that long after its work was\n * done. One `os migrate` finished in 3s and then sat for 120s\n * (`ObjectQLPlugin.startupTimeout`), held open by 8 orphaned guards — one\n * per init plus one per start.\n *\n * Clearing on settle rather than `unref()`-ing at arm time is deliberate.\n * An unref'd guard also stops pinning the loop, but it stops being a guard\n * as well: if the hook never settles and nothing else keeps the loop alive,\n * Node exits before the timer can fire and the timeout is never reported.\n * The guard has to stay ref'd exactly as long as the race is undecided,\n * which is what `clearTimeout` in a `finally` expresses.\n *\n * `operation` is widened to `T | PromiseLike<T>` because the Plugin\n * contract permits a synchronous hook (`init`/`start` return\n * `void | Promise<void>`); such a hook wins the race immediately and the\n * guard is reclaimed on the same turn.\n */\n private async raceStartupTimeout<T>(\n operation: T | PromiseLike<T>,\n timeout: number,\n message: string\n ): Promise<T> {\n let guard: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n guard = setTimeout(() => {\n reject(new Error(message));\n }, timeout);\n });\n\n try {\n return await Promise.race([operation, timeoutPromise]);\n } finally {\n clearTimeout(guard);\n }\n }\n\n /**\n * Whether a service is resolvable on this kernel right now — direct\n * registration or a loader-registered factory. Backs the init-service\n * contract checks (#4131).\n */\n private hasAnyService(name: string): boolean {\n return this.services.has(name) || this.pluginLoader.hasService(name);\n }\n\n /**\n * When a getService miss happens while a plugin's init() is running,\n * append the structural diagnosis (#4131): which plugin was initializing,\n * and — when a composed plugin declares the service — who provides it.\n * Empty string outside Phase 1, so non-boot messages stay unchanged.\n */\n private describeInitOrderFault(serviceName: string): string {\n return describeInitOrderFault(this.currentlyInitializing, this.plugins.values(), serviceName);\n }\n\n private async startPluginWithTimeout(plugin: PluginMetadata): Promise<PluginStartupResult> {\n if (!plugin.start) {\n return { success: true, pluginName: plugin.name };\n }\n\n const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout!;\n const startTime = Date.now();\n \n this.logger.debug(`Start: ${plugin.name}`, { plugin: plugin.name });\n \n try {\n await this.raceStartupTimeout(\n plugin.start(this.context),\n timeout,\n `Plugin ${plugin.name} start timeout after ${timeout}ms`\n );\n\n const duration = Date.now() - startTime;\n this.startedPlugins.add(plugin.name);\n this.pluginStartTimes.set(plugin.name, duration);\n \n this.logger.debug(`Plugin started: ${plugin.name} (${duration}ms)`);\n \n return {\n success: true,\n pluginName: plugin.name,\n startTime: duration,\n };\n } catch (error) {\n const duration = Date.now() - startTime;\n const isTimeout = (error as Error).message.includes('timeout');\n \n return {\n success: false,\n pluginName: plugin.name,\n error: error as Error,\n startTime: duration,\n timedOut: isTimeout,\n };\n }\n }\n\n private async rollbackStartedPlugins(): Promise<void> {\n const pluginsToRollback = Array.from(this.startedPlugins).reverse();\n \n for (const pluginName of pluginsToRollback) {\n const plugin = this.plugins.get(pluginName);\n if (plugin?.destroy) {\n try {\n this.logger.debug(`Rollback: ${pluginName}`);\n await plugin.destroy();\n } catch (error) {\n this.logger.error(`Rollback failed for ${pluginName}`, error as Error);\n }\n }\n }\n \n this.startedPlugins.clear();\n }\n\n /**\n * Dispatch `kernel:shutdown`, ISOLATING failures: a handler that throws is\n * logged and the remaining handlers still run (#5274).\n *\n * This is a per-hook judgement, deliberately NOT the bare awaited loop\n * `context.trigger` runs for every other hook — the boot-path hooks\n * (`kernel:ready`, `kernel:bootstrapped`, `kernel:listening`) keep\n * propagating, because everything dispatched before \"✅ Bootstrap complete\"\n * is a precondition of that claim and swallowing a throw there only hides\n * the failure behind a process reporting success (#5170, #5257).\n *\n * On the teardown path there is no \"refuse to proceed\" left to buy. What is\n * queued behind a failing shutdown handler is the rest of the cleanup —\n * every other subscriber, then each plugin's `destroy()` in reverse order —\n * which is what flushes buffers, closes connections and releases locks. So\n * one bad handler must not amplify into leaked resources and unflushed\n * writes. Same reasoning, same wording, same `Hook handler failed:\n * kernel:shutdown` log line as `LiteKernel`'s dispatch site, which reaches\n * the isolating dispatcher through `ObjectKernelBase.triggerHook` (#5257).\n *\n * Until #5282 \"same wording\" was literally that — the loop was typed out a\n * second time here, because `ObjectKernel` does not extend\n * `ObjectKernelBase` (only `LiteKernel` does) and owns its own `hooks` map,\n * so the base's `protected triggerHook` is out of reach. The loop now lives\n * in {@link dispatchHookIsolating}, which BOTH sides call: the storage is\n * still two maps (deliberately — unifying it was out of #5282's scope), but\n * \"isolating\" is one implementation, so it can no longer drift on one\n * kernel while the other keeps the old shape. That drift is exactly the bug\n * #5170 / #5257 / #5274 each closed one hook at a time, and the paired-pin\n * gate (`scripts/check-kernel-hook-pairs.mjs`) covers the residue.\n */\n private async triggerShutdownHookIsolating(): Promise<void> {\n await dispatchHookIsolating('kernel:shutdown', this.hooks.get('kernel:shutdown') || [], this.logger);\n }\n\n private async performShutdown(): Promise<void> {\n // Trigger shutdown hook — ISOLATING dispatch, see the method's own\n // rationale. The two loops below already isolate per plugin and per\n // handler; before #5274 this line was the one teardown step that did\n // not, so a single throwing subscriber skipped BOTH of them.\n await this.triggerShutdownHookIsolating();\n\n // Destroy plugins in reverse order\n const orderedPlugins = Array.from(this.plugins.values()).reverse();\n for (const plugin of orderedPlugins) {\n if (plugin.destroy) {\n this.logger.debug(`Destroy: ${plugin.name}`, { plugin: plugin.name });\n try {\n await plugin.destroy();\n } catch (error) {\n this.logger.error(`Error destroying plugin ${plugin.name}`, error as Error);\n }\n }\n }\n\n // Execute custom shutdown handlers\n for (const handler of this.shutdownHandlers) {\n try {\n await handler();\n } catch (error) {\n this.logger.error('Shutdown handler error', error as Error);\n }\n }\n }\n\n /**\n * Topological order over `dependencies` (hard) + `optionalDependencies`\n * (order-if-present) — ADR-0116, #4131. One implementation shared with\n * LiteKernel via `plugin-order.ts`.\n */\n private resolveDependencies(): PluginMetadata[] {\n return resolvePluginOrder(this.plugins);\n }\n\n private registerShutdownSignals(): void {\n const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGQUIT'];\n let shutdownInProgress = false;\n \n const handleShutdown = async (signal: string) => {\n if (shutdownInProgress) {\n this.logger.warn(`Shutdown already in progress, ignoring ${signal}`);\n return;\n }\n \n shutdownInProgress = true;\n this.logger.info(`Received ${signal} - initiating graceful shutdown`);\n \n try {\n await this.shutdown();\n safeExit(0);\n } catch (error) {\n this.logger.error('Shutdown failed', error as Error);\n safeExit(1);\n }\n };\n \n if (isNode) {\n for (const signal of signals) {\n process.on(signal, () => handleShutdown(signal));\n }\n }\n }\n\n /**\n * Register a custom shutdown handler\n */\n onShutdown(handler: () => Promise<void>): void {\n this.shutdownHandlers.push(handler);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { z } from 'zod';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { PluginMetadata } from '../plugin-loader.js';\n\n/**\n * Plugin Configuration Validator\n * \n * Validates plugin configurations against Zod schemas to ensure:\n * 1. Type safety - all config values have correct types\n * 2. Business rules - values meet constraints (min/max, regex, etc.)\n * 3. Required fields - all mandatory configuration is provided\n * 4. Default values - missing optional fields get defaults\n * \n * Architecture:\n * - Uses Zod for runtime validation\n * - Provides detailed error messages with field paths\n * - Supports nested configuration objects\n * - Allows partial validation for incremental updates\n * \n * Usage:\n * ```typescript\n * const validator = new PluginConfigValidator(logger);\n * const validConfig = validator.validatePluginConfig(plugin, userConfig);\n * ```\n */\nexport class PluginConfigValidator {\n private logger: Logger;\n \n constructor(logger: Logger) {\n this.logger = logger;\n }\n \n /**\n * Validate plugin configuration against its Zod schema\n * \n * @param plugin - Plugin metadata with configSchema\n * @param config - User-provided configuration\n * @returns Validated and typed configuration\n * @throws Error with detailed validation errors\n */\n validatePluginConfig<T = any>(plugin: PluginMetadata, config: any): T {\n if (!plugin.configSchema) {\n this.logger.debug(`Plugin ${plugin.name} has no config schema - skipping validation`);\n return config as T;\n }\n \n try {\n // Use Zod to parse and validate\n const validatedConfig = plugin.configSchema.parse(config);\n \n this.logger.debug(`✅ Plugin config validated: ${plugin.name}`, {\n plugin: plugin.name,\n configKeys: Object.keys(config || {}).length,\n });\n \n return validatedConfig as T;\n } catch (error) {\n if (error instanceof z.ZodError) {\n const formattedErrors = this.formatZodErrors(error);\n const errorMessage = [\n `Plugin ${plugin.name} configuration validation failed:`,\n ...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),\n ].join('\\n');\n \n this.logger.error(errorMessage, undefined, {\n plugin: plugin.name,\n errors: formattedErrors,\n });\n \n throw new Error(errorMessage);\n }\n \n // Re-throw other errors\n throw error;\n }\n }\n \n /**\n * Validate partial configuration (for incremental updates)\n * \n * @param plugin - Plugin metadata\n * @param partialConfig - Partial configuration to validate\n * @returns Validated partial configuration\n */\n validatePartialConfig<T = any>(plugin: PluginMetadata, partialConfig: any): Partial<T> {\n if (!plugin.configSchema) {\n return partialConfig as Partial<T>;\n }\n \n try {\n // Use Zod's partial() method for partial validation\n // Cast to ZodObject to access partial() method\n const partialSchema = (plugin.configSchema as any).partial();\n const validatedConfig = partialSchema.parse(partialConfig);\n \n this.logger.debug(`✅ Partial config validated: ${plugin.name}`);\n return validatedConfig as Partial<T>;\n } catch (error) {\n if (error instanceof z.ZodError) {\n const formattedErrors = this.formatZodErrors(error);\n const errorMessage = [\n `Plugin ${plugin.name} partial configuration validation failed:`,\n ...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),\n ].join('\\n');\n \n throw new Error(errorMessage);\n }\n \n throw error;\n }\n }\n \n /**\n * Get default configuration from schema\n * \n * @param plugin - Plugin metadata\n * @returns Default configuration object\n */\n getDefaultConfig<T = any>(plugin: PluginMetadata): T | undefined {\n if (!plugin.configSchema) {\n return undefined;\n }\n \n try {\n // Parse empty object to get defaults\n const defaults = plugin.configSchema.parse({});\n this.logger.debug(`Default config extracted: ${plugin.name}`);\n return defaults as T;\n } catch (error) {\n // Schema may require some fields - return undefined\n this.logger.debug(`No default config available: ${plugin.name}`);\n return undefined;\n }\n }\n \n /**\n * Check if configuration is valid without throwing\n * \n * @param plugin - Plugin metadata\n * @param config - Configuration to check\n * @returns True if valid, false otherwise\n */\n isConfigValid(plugin: PluginMetadata, config: any): boolean {\n if (!plugin.configSchema) {\n return true;\n }\n \n const result = plugin.configSchema.safeParse(config);\n return result.success;\n }\n \n /**\n * Get configuration errors without throwing\n * \n * @param plugin - Plugin metadata\n * @param config - Configuration to check\n * @returns Array of validation errors, or empty array if valid\n */\n getConfigErrors(plugin: PluginMetadata, config: any): Array<{path: string; message: string}> {\n if (!plugin.configSchema) {\n return [];\n }\n \n const result = plugin.configSchema.safeParse(config);\n \n if (result.success) {\n return [];\n }\n \n return this.formatZodErrors(result.error);\n }\n \n // Private methods\n \n private formatZodErrors(error: z.ZodError<any>): Array<{path: string; message: string}> {\n return error.issues.map((e: z.ZodIssue) => ({\n path: e.path.join('.') || 'root',\n message: e.message,\n }));\n }\n}\n\n/**\n * Create a plugin config validator\n * \n * @param logger - Logger instance\n * @returns Plugin config validator\n */\nexport function createPluginConfigValidator(logger: Logger): PluginConfigValidator {\n return new PluginConfigValidator(logger);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Plugin artifact signing & verification (ADR-0025 §3.4–§3.7, framework F3).\n *\n * This is the CANONICAL Ed25519 detached-signature contract shared by the\n * whole plugin distribution pipeline. It is intentionally byte-for-byte\n * compatible with the cloud control plane's `package-signing.ts` so the\n * two never drift:\n *\n * - signature string format: `ed25519:<keyId>:<base64url(signature)>`\n * - publisher signature: Ed25519 over the raw `.osplugin` artifact bytes,\n * produced by `os plugin sign`, verified by cloud at publish time and by\n * the runtime when it materializes the artifact.\n * - platform counter-signature: Ed25519 over {@link counterSignPayload}\n * (the version identity), produced by cloud at approval, verified by the\n * runtime at load time as the marketplace's \"reviewed + approved\" attest.\n *\n * Algorithm: Ed25519 via node:crypto (`sign(null, …)` / `verify(null, …)`):\n * short, deterministic, no padding ambiguity. The `keyId` is an opaque\n * rotation handle used to resolve the verifying public key.\n *\n * The two trust chains the runtime checks before loading a third-party\n * plugin are combined in {@link verifyPluginArtifact}.\n */\n\nimport {\n sign as cryptoSign,\n verify as cryptoVerify,\n createPublicKey,\n createPrivateKey,\n generateKeyPairSync,\n type KeyObject,\n} from 'node:crypto';\n\nexport const SIGNATURE_ALG = 'ed25519';\nconst SIG_PREFIX = 'ed25519:';\n\nexport type KeyInput = string | KeyObject;\n\nfunction toPrivateKey(key: KeyInput): KeyObject {\n return typeof key === 'string' ? createPrivateKey(key) : key;\n}\nfunction toPublicKey(key: KeyInput): KeyObject {\n return typeof key === 'string' ? createPublicKey(key) : key;\n}\nfunction toBytes(payload: string | Uint8Array): Uint8Array {\n return typeof payload === 'string' ? new TextEncoder().encode(payload) : payload;\n}\n\n/** Generate an Ed25519 keypair as PEM strings (publisher bootstrap / tests). */\nexport function generateEd25519KeyPair(): { publicKeyPem: string; privateKeyPem: string } {\n const { publicKey, privateKey } = generateKeyPairSync('ed25519');\n return {\n publicKeyPem: publicKey.export({ type: 'spki', format: 'pem' }).toString(),\n privateKeyPem: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(),\n };\n}\n\n/**\n * Sign `payload` with an Ed25519 private key, returning the formatted\n * signature string `ed25519:<keyId>:<base64url(sig)>`.\n */\nexport function signPayload(\n payload: string | Uint8Array,\n privateKey: KeyInput,\n keyId = 'default',\n): string {\n if (keyId.includes(':')) throw new Error('keyId must not contain \":\"');\n const sig = cryptoSign(null, toBytes(payload), toPrivateKey(privateKey));\n return `${SIG_PREFIX}${keyId}:${sig.toString('base64url')}`;\n}\n\nexport interface ParsedSignature {\n alg: 'ed25519';\n keyId: string;\n signature: Uint8Array;\n}\n\n/** Parse an `ed25519:<keyId>:<base64url>` signature string. Returns null if malformed. */\nexport function parseSignature(s: string | undefined | null): ParsedSignature | null {\n if (typeof s !== 'string' || !s.startsWith(SIG_PREFIX)) return null;\n const rest = s.slice(SIG_PREFIX.length);\n const idx = rest.indexOf(':');\n if (idx <= 0) return null;\n const keyId = rest.slice(0, idx);\n const b64 = rest.slice(idx + 1);\n if (!keyId || !b64) return null;\n try {\n return { alg: 'ed25519', keyId, signature: new Uint8Array(Buffer.from(b64, 'base64url')) };\n } catch {\n return null;\n }\n}\n\n/** Verify a formatted signature string over `payload` with the given public key. */\nexport function verifyPayload(\n payload: string | Uint8Array,\n signature: string,\n publicKey: KeyInput,\n): boolean {\n const parsed = parseSignature(signature);\n if (!parsed) return false;\n try {\n return cryptoVerify(null, toBytes(payload), toPublicKey(publicKey), parsed.signature);\n } catch {\n return false;\n }\n}\n\n/**\n * Canonical payload the platform counter-signs at approval. Binds the\n * attestation to the version identity + artifact location + the publisher\n * signature (which itself binds the artifact bytes). MUST match the cloud\n * control plane's `counterSignPayload` exactly.\n */\nexport function counterSignPayload(version: {\n package_id: string;\n version: string;\n blob_key?: string | null;\n signature?: string | null;\n}): string {\n return [\n version.package_id,\n version.version,\n version.blob_key ?? '',\n version.signature ?? '',\n ].join('\\n');\n}\n\nexport interface PublisherVerifyResult {\n /** Whether loading may proceed on signature grounds. */\n ok: boolean;\n /** True when a signature was present AND cryptographically verified. */\n verified: boolean;\n reason?: string;\n}\n\n/**\n * Verify a publisher signature over the raw artifact bytes. `getPublicKey`\n * resolves the verifying key from the signature's embedded keyId.\n *\n * Mirrors cloud's publish-time policy:\n * - no signature → ok, verified=false (caller decides via trust tier).\n * - malformed / fails verification → NOT ok.\n * - unknown keyId → NOT ok (never silently trust).\n */\nexport async function verifyPublisherSignature(\n args: { artifact: Uint8Array; signature?: string | null },\n getPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null,\n): Promise<PublisherVerifyResult> {\n const sig = args.signature;\n if (!sig) return { ok: true, verified: false, reason: 'no signature supplied' };\n\n const parsed = parseSignature(sig);\n if (!parsed) return { ok: false, verified: false, reason: 'signature is malformed' };\n\n if (!getPublicKey) {\n return { ok: true, verified: false, reason: 'no publisher key registry configured' };\n }\n\n const pub = await getPublicKey(parsed.keyId);\n if (!pub) return { ok: false, verified: false, reason: `unknown publisher key '${parsed.keyId}'` };\n\n return verifyPayload(args.artifact, sig, pub)\n ? { ok: true, verified: true }\n : { ok: false, verified: false, reason: 'publisher signature does not match artifact' };\n}\n\n/** Verify a platform counter-signature against the version identity + platform public key. */\nexport function verifyPlatformSignature(\n version: {\n package_id: string;\n version: string;\n blob_key?: string | null;\n signature?: string | null;\n platform_signature?: string | null;\n },\n platformPublicKey: KeyInput,\n): boolean {\n if (!version.platform_signature) return false;\n return verifyPayload(counterSignPayload(version), version.platform_signature, platformPublicKey);\n}\n\nexport interface PluginArtifactVerifyResult {\n /** Overall verdict: both required chains satisfied under the given policy. */\n ok: boolean;\n publisherVerified: boolean;\n platformVerified: boolean;\n reason?: string;\n}\n\n/**\n * Verify both trust chains for a downloaded plugin artifact at load time\n * (ADR-0025 §3.7). The platform counter-signature is the authoritative\n * marketplace attestation; the publisher signature additionally binds the\n * exact bytes. `requirePlatform` (default true) rejects artifacts that lack\n * a valid platform counter-sign — set false for first-party / local builds.\n */\nexport async function verifyPluginArtifact(\n input: {\n artifact: Uint8Array;\n version: {\n package_id: string;\n version: string;\n blob_key?: string | null;\n signature?: string | null;\n platform_signature?: string | null;\n };\n },\n keys: {\n platformPublicKey?: KeyInput;\n getPublisherPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null;\n requirePlatform?: boolean;\n },\n): Promise<PluginArtifactVerifyResult> {\n const requirePlatform = keys.requirePlatform ?? true;\n\n const publisher = await verifyPublisherSignature(\n { artifact: input.artifact, signature: input.version.signature },\n keys.getPublisherPublicKey,\n );\n if (!publisher.ok) {\n return { ok: false, publisherVerified: false, platformVerified: false, reason: publisher.reason };\n }\n\n let platformVerified = false;\n if (keys.platformPublicKey) {\n platformVerified = verifyPlatformSignature(input.version, keys.platformPublicKey);\n }\n if (requirePlatform && !platformVerified) {\n return {\n ok: false,\n publisherVerified: publisher.verified,\n platformVerified,\n reason: keys.platformPublicKey\n ? 'platform counter-signature missing or invalid'\n : 'no platform public key configured',\n };\n }\n\n return { ok: true, publisherVerified: publisher.verified, platformVerified };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext } from './types.js';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { z } from 'zod';\nimport { PluginConfigValidator } from './security/plugin-config-validator.js';\nimport { parseSignature } from './security/plugin-artifact-signature.js';\n\n/**\n * Service Lifecycle Types\n * Defines how services are instantiated and managed\n */\nexport enum ServiceLifecycle {\n /** Single instance shared across all requests */\n SINGLETON = 'singleton',\n /** New instance created for each request */\n TRANSIENT = 'transient',\n /** New instance per scope (e.g., per HTTP request) */\n SCOPED = 'scoped',\n}\n\n/**\n * Service Factory\n * Function that creates a service instance\n */\nexport type ServiceFactory<T = any> = (ctx: PluginContext, scopeId?: string) => T | Promise<T>;\n\n/**\n * Service Registration Options\n */\nexport interface ServiceRegistration {\n name: string;\n factory: ServiceFactory;\n lifecycle: ServiceLifecycle;\n dependencies?: string[];\n}\n\n/**\n * Plugin Metadata with Enhanced Features\n */\nexport interface PluginMetadata extends Plugin {\n /** Semantic version (e.g., \"1.0.0\") */\n version: string;\n \n /** Configuration schema for validation */\n configSchema?: z.ZodSchema;\n \n /** Plugin signature for security verification */\n signature?: string;\n \n /** Plugin health check function */\n healthCheck?(): Promise<PluginHealthStatus>;\n \n /** Startup timeout in milliseconds (default: 30000) */\n startupTimeout?: number;\n \n /** Whether plugin supports hot reload */\n hotReloadable?: boolean;\n}\n\n/**\n * Plugin Health Status\n */\nexport interface PluginHealthStatus {\n healthy: boolean;\n message?: string;\n details?: Record<string, any>;\n lastCheck?: Date;\n}\n\n/**\n * Plugin Load Result\n */\nexport interface PluginLoadResult {\n success: boolean;\n plugin?: PluginMetadata;\n error?: Error;\n loadTime?: number;\n}\n\n/**\n * Plugin Startup Result\n */\nexport interface PluginStartupResult {\n success: boolean;\n pluginName: string;\n startTime?: number;\n error?: Error;\n timedOut?: boolean;\n}\n\n/**\n * Version Compatibility Result\n */\nexport interface VersionCompatibility {\n compatible: boolean;\n pluginVersion: string;\n requiredVersion?: string;\n message?: string;\n}\n\n/**\n * Enhanced Plugin Loader\n * Provides advanced plugin loading capabilities with validation, security, and lifecycle management\n */\nexport class PluginLoader {\n private logger: Logger;\n private context?: PluginContext;\n private configValidator: PluginConfigValidator;\n private loadedPlugins: Map<string, PluginMetadata> = new Map();\n private serviceFactories: Map<string, ServiceRegistration> = new Map();\n private serviceInstances: Map<string, any> = new Map();\n private scopedServices: Map<string, Map<string, any>> = new Map();\n private creating: Set<string> = new Set();\n\n constructor(logger: Logger) {\n this.logger = logger;\n this.configValidator = new PluginConfigValidator(logger);\n }\n\n /**\n * Set the plugin context for service factories\n */\n setContext(context: PluginContext): void {\n this.context = context;\n }\n\n /**\n * Get a synchronous service instance if it exists (Sync Helper)\n */\n getServiceInstance<T>(name: string): T | undefined {\n return this.serviceInstances.get(name) as T;\n }\n\n /**\n * Load a plugin asynchronously with validation\n */\n async loadPlugin(plugin: Plugin): Promise<PluginLoadResult> {\n const startTime = Date.now();\n \n try {\n this.logger.info(`Loading plugin: ${plugin.name}`);\n \n // Convert to PluginMetadata\n const metadata = this.toPluginMetadata(plugin);\n \n // Validate plugin structure\n this.validatePluginStructure(metadata);\n \n // Check version compatibility\n const versionCheck = this.checkVersionCompatibility(metadata);\n if (!versionCheck.compatible) {\n throw new Error(`Version incompatible: ${versionCheck.message}`);\n }\n \n // Validate configuration if schema is provided\n if (metadata.configSchema) {\n this.validatePluginConfig(metadata);\n }\n \n // Verify signature if provided\n if (metadata.signature) {\n await this.verifyPluginSignature(metadata);\n }\n \n // Store loaded plugin\n this.loadedPlugins.set(metadata.name, metadata);\n \n const loadTime = Date.now() - startTime;\n this.logger.info(`Plugin loaded: ${plugin.name} (${loadTime}ms)`);\n \n return {\n success: true,\n plugin: metadata,\n loadTime,\n };\n } catch (error) {\n this.logger.error(`Failed to load plugin: ${plugin.name}`, error as Error);\n return {\n success: false,\n error: error as Error,\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Register a service with factory function\n */\n registerServiceFactory(registration: ServiceRegistration): void {\n if (this.serviceFactories.has(registration.name)) {\n throw new Error(`Service factory '${registration.name}' already registered`);\n }\n \n this.serviceFactories.set(registration.name, registration);\n this.logger.debug(`Service factory registered: ${registration.name} (${registration.lifecycle})`);\n }\n\n /**\n * Get or create a service instance based on lifecycle type\n */\n async getService<T>(name: string, scopeId?: string): Promise<T> {\n const registration = this.serviceFactories.get(name);\n \n if (!registration) {\n // Fall back to static service instances\n const instance = this.serviceInstances.get(name);\n if (!instance) {\n throw new Error(`Service '${name}' not found`);\n }\n return instance as T;\n }\n \n switch (registration.lifecycle) {\n case ServiceLifecycle.SINGLETON:\n return await this.getSingletonService<T>(registration);\n \n case ServiceLifecycle.TRANSIENT:\n return await this.createTransientService<T>(registration);\n \n case ServiceLifecycle.SCOPED:\n if (!scopeId) {\n throw new Error(`Scope ID required for scoped service '${name}'`);\n }\n return await this.getScopedService<T>(registration, scopeId);\n \n default:\n throw new Error(`Unknown service lifecycle: ${registration.lifecycle}`);\n }\n }\n\n /**\n * Register a static service instance (legacy support)\n */\n registerService(name: string, service: any): void {\n if (this.serviceInstances.has(name)) {\n throw new Error(`Service '${name}' already registered`);\n }\n this.serviceInstances.set(name, service);\n }\n\n /**\n * Replace an existing service instance.\n * Used by optimization plugins to swap kernel internals.\n * @throws Error if service does not exist\n */\n replaceService(name: string, service: any): void {\n if (!this.hasService(name)) {\n throw new Error(`Service '${name}' not found`);\n }\n this.serviceInstances.set(name, service);\n }\n\n /**\n * Check if a service is registered (either as instance or factory)\n */\n hasService(name: string): boolean {\n return this.serviceInstances.has(name) || this.serviceFactories.has(name);\n }\n\n /**\n * Detect circular dependencies in service factories\n * Note: This only detects cycles in service dependencies, not plugin dependencies.\n * Plugin dependency cycles are detected in the kernel's resolveDependencies method.\n */\n detectCircularDependencies(): string[] {\n const cycles: string[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n \n const visit = (serviceName: string, path: string[] = []) => {\n if (visiting.has(serviceName)) {\n const cycle = [...path, serviceName].join(' -> ');\n cycles.push(cycle);\n return;\n }\n \n if (visited.has(serviceName)) {\n return;\n }\n \n visiting.add(serviceName);\n \n const registration = this.serviceFactories.get(serviceName);\n if (registration?.dependencies) {\n for (const dep of registration.dependencies) {\n visit(dep, [...path, serviceName]);\n }\n }\n \n visiting.delete(serviceName);\n visited.add(serviceName);\n };\n \n for (const serviceName of this.serviceFactories.keys()) {\n visit(serviceName);\n }\n \n return cycles;\n }\n\n /**\n * Check plugin health\n */\n async checkPluginHealth(pluginName: string): Promise<PluginHealthStatus> {\n const plugin = this.loadedPlugins.get(pluginName);\n \n if (!plugin) {\n return {\n healthy: false,\n message: 'Plugin not found',\n lastCheck: new Date(),\n };\n }\n \n if (!plugin.healthCheck) {\n return {\n healthy: true,\n message: 'No health check defined',\n lastCheck: new Date(),\n };\n }\n \n try {\n const status = await plugin.healthCheck();\n return {\n ...status,\n lastCheck: new Date(),\n };\n } catch (error) {\n return {\n healthy: false,\n message: `Health check failed: ${(error as Error).message}`,\n lastCheck: new Date(),\n };\n }\n }\n\n /**\n * Clear scoped services for a scope\n */\n clearScope(scopeId: string): void {\n this.scopedServices.delete(scopeId);\n this.logger.debug(`Cleared scope: ${scopeId}`);\n }\n\n /**\n * Get all loaded plugins\n */\n getLoadedPlugins(): Map<string, PluginMetadata> {\n return new Map(this.loadedPlugins);\n }\n\n // Private helper methods\n\n private toPluginMetadata(plugin: Plugin): PluginMetadata {\n // Fix: Do not use object spread {...plugin} as it destroys the prototype chain for Class-based plugins.\n // Instead, cast the original object and inject default values if missing.\n const metadata = plugin as PluginMetadata;\n \n if (!metadata.version) {\n metadata.version = '0.0.0';\n }\n \n return metadata;\n }\n\n private validatePluginStructure(plugin: PluginMetadata): void {\n if (!plugin.name) {\n throw new Error('Plugin name is required');\n }\n \n if (!plugin.init) {\n throw new Error('Plugin init function is required');\n }\n \n if (!this.isValidSemanticVersion(plugin.version)) {\n throw new Error(`Invalid semantic version: ${plugin.version}`);\n }\n }\n\n private checkVersionCompatibility(plugin: PluginMetadata): VersionCompatibility {\n // Basic semantic version compatibility check\n // In a real implementation, this would check against kernel version\n const version = plugin.version;\n \n if (!this.isValidSemanticVersion(version)) {\n return {\n compatible: false,\n pluginVersion: version,\n message: 'Invalid semantic version format',\n };\n }\n \n return {\n compatible: true,\n pluginVersion: version,\n };\n }\n\n private isValidSemanticVersion(version: string): boolean {\n const semverRegex = /^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/;\n return semverRegex.test(version);\n }\n\n private validatePluginConfig(plugin: PluginMetadata, config?: any): void {\n if (!plugin.configSchema) {\n return;\n }\n\n if (config === undefined) {\n // In loadPlugin, we often don't have the config yet.\n // We skip validation here or valid against empty object if schema allows?\n // For now, let's keep the logging behavior but note it's delegating\n this.logger.debug(`Plugin ${plugin.name} has configuration schema (config validation postponed)`);\n return;\n }\n\n this.configValidator.validatePluginConfig(plugin, config);\n }\n\n private async verifyPluginSignature(plugin: PluginMetadata): Promise<void> {\n if (!plugin.signature) {\n return;\n }\n \n // Cryptographic verification of a third-party plugin's PUBLISHER and\n // PLATFORM signatures is performed against the `.osplugin` artifact\n // bytes + version identity at materialize/install time, by\n // `verifyPluginArtifact` (security/plugin-artifact-signature.ts —\n // ADR-0025 §3.7). By the time a plugin reaches loadPlugin() it is an\n // in-memory module with no artifact bytes, so we cannot re-run the\n // artifact chains here; we only validate that any signature carried\n // on the metadata is well-formed (`ed25519:<keyId>:<base64url>`) and\n // surface its keyId, failing fast on a malformed value.\n const parsed = parseSignature(plugin.signature);\n if (!parsed) {\n throw new Error(\n `Plugin ${plugin.name} carries a malformed signature (expected ed25519:<keyId>:<base64url>)`,\n );\n }\n this.logger.debug(\n `Plugin ${plugin.name} signature well-formed (alg=${parsed.alg}, keyId=${parsed.keyId}); ` +\n `artifact verification occurs at materialize time`,\n );\n }\n\n private async getSingletonService<T>(registration: ServiceRegistration): Promise<T> {\n let instance = this.serviceInstances.get(registration.name);\n \n if (!instance) {\n // Create instance (would need context)\n instance = await this.createServiceInstance(registration);\n this.serviceInstances.set(registration.name, instance);\n this.logger.debug(`Singleton service created: ${registration.name}`);\n }\n \n return instance as T;\n }\n\n private async createTransientService<T>(registration: ServiceRegistration): Promise<T> {\n const instance = await this.createServiceInstance(registration);\n this.logger.debug(`Transient service created: ${registration.name}`);\n return instance as T;\n }\n\n private async getScopedService<T>(registration: ServiceRegistration, scopeId: string): Promise<T> {\n if (!this.scopedServices.has(scopeId)) {\n this.scopedServices.set(scopeId, new Map());\n }\n\n const scope = this.scopedServices.get(scopeId)!;\n let instance = scope.get(registration.name);\n\n if (!instance) {\n instance = await this.createServiceInstance(registration, scopeId);\n scope.set(registration.name, instance);\n this.logger.debug(`Scoped service created: ${registration.name} (scope: ${scopeId})`);\n }\n\n return instance as T;\n }\n\n private async createServiceInstance(registration: ServiceRegistration, scopeId?: string): Promise<any> {\n if (!this.context) {\n throw new Error(`[PluginLoader] Context not set - cannot create service '${registration.name}'`);\n }\n\n if (this.creating.has(registration.name)) {\n throw new Error(`Circular dependency detected: ${Array.from(this.creating).join(' -> ')} -> ${registration.name}`);\n }\n\n this.creating.add(registration.name);\n try {\n return await registration.factory(this.context, scopeId);\n } finally {\n this.creating.delete(registration.name);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Environment utilities for universal (Node/Browser) compatibility.\n */\n\n// Check if running in a Node.js environment\nexport const isNode = typeof process !== 'undefined' && \n process.versions != null && \n process.versions.node != null;\n\n/**\n * Safely access environment variables\n */\nexport function getEnv(key: string, defaultValue?: string): string | undefined {\n // Node.js\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key] || defaultValue;\n }\n \n // Browser (Vite/Webpack replacement usually handles process.env, \n // but if not, we check safe global access)\n try {\n // @ts-ignore\n if (typeof globalThis !== 'undefined' && globalThis.process?.env) {\n // @ts-ignore\n return globalThis.process.env[key] || defaultValue;\n }\n } catch (e) {\n // Ignore access errors\n }\n \n return defaultValue;\n}\n\n/**\n * Safely exit the process if in Node.js\n */\nexport function safeExit(code: number = 0): void {\n if (isNode) {\n process.exit(code);\n }\n}\n\n/**\n * Safely get memory usage\n */\nexport function getMemoryUsage(): { heapUsed: number; heapTotal: number } {\n if (isNode) {\n return process.memoryUsage();\n }\n return { heapUsed: 0, heapTotal: 0 };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * In-memory Map-backed cache fallback.\n *\n * Implements the ICacheService contract with basic get/set/delete/has/clear\n * and TTL expiry. Used by ObjectKernel as an automatic fallback when no\n * real cache plugin (e.g. Redis) is registered.\n *\n * [#4058] Self-describes as `degraded`, not `stub` (ADR-0076 D12): this is a\n * real cache — it stores, expires, and reports true stats — just process-local\n * and unshared. The non-standard `_fallback: true` it used to carry was read by\n * nothing (`readServiceSelfInfo` reads only `__serviceInfo` — `_dev`, the other\n * marker it knew back then, was itself retired in #4319), so discovery reported\n * it as fully `available`. `handlerReady: false` because\n * no HTTP surface is mounted for `cache` at all — the same reason realtime\n * reports false.\n */\nexport function createMemoryCache() {\n const store = new Map<string, { value: unknown; expires?: number }>();\n let hits = 0;\n let misses = 0;\n return {\n __serviceInfo: {\n status: 'degraded' as const,\n handlerReady: false,\n message: 'In-process Map cache — not shared across instances, lost on restart. Register a cache plugin (e.g. Redis) for a real one.',\n },\n _serviceName: 'cache',\n async get<T = unknown>(key: string): Promise<T | undefined> {\n const entry = store.get(key);\n if (!entry || (entry.expires && Date.now() > entry.expires)) {\n store.delete(key);\n misses++;\n return undefined;\n }\n hits++;\n return entry.value as T;\n },\n async set<T = unknown>(key: string, value: T, ttl?: number): Promise<void> {\n store.set(key, { value, expires: ttl ? Date.now() + ttl * 1000 : undefined });\n },\n async delete(key: string): Promise<boolean> { return store.delete(key); },\n async has(key: string): Promise<boolean> { return store.has(key); },\n async clear(): Promise<void> { store.clear(); },\n async stats() { return { hits, misses, keyCount: store.size }; },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * In-memory publish/subscribe queue fallback.\n *\n * Implements the IQueueService contract with synchronous in-process delivery.\n * Used by ObjectKernel as an automatic fallback when no real queue plugin\n * (e.g. BullMQ / RabbitMQ) is registered.\n *\n * [#4058] `degraded`, not `stub` (ADR-0076 D12): messages really reach real\n * subscribers — synchronously, in-process, with no durability or retry.\n * `getQueueSize()` answering 0 follows from that rather than faking it: nothing\n * is ever buffered. `handlerReady: false` — no HTTP surface exists for `queue`.\n */\nexport function createMemoryQueue() {\n const handlers = new Map<string, Function[]>();\n let msgId = 0;\n return {\n __serviceInfo: {\n status: 'degraded' as const,\n handlerReady: false,\n message: 'Synchronous in-process delivery — no durability, retry, or cross-instance fan-out. Register a queue plugin (e.g. BullMQ) for a real one.',\n },\n _serviceName: 'queue',\n async publish<T = unknown>(queue: string, data: T): Promise<string> {\n const id = `fallback-msg-${++msgId}`;\n const fns = handlers.get(queue) ?? [];\n for (const fn of fns) fn({ id, data, attempts: 1, timestamp: Date.now() });\n return id;\n },\n async subscribe(queue: string, handler: (msg: any) => Promise<void>): Promise<void> {\n handlers.set(queue, [...(handlers.get(queue) ?? []), handler]);\n },\n async unsubscribe(queue: string): Promise<void> { handlers.delete(queue); },\n async getQueueSize(): Promise<number> { return 0; },\n async purge(queue: string): Promise<void> { handlers.delete(queue); },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * In-memory job scheduler fallback.\n *\n * Implements the IJobService contract with basic schedule/cancel/trigger\n * operations. Used by ObjectKernel as an automatic fallback when no real\n * job plugin (e.g. Agenda / BullMQ) is registered.\n *\n * [#4058] `degraded` (ADR-0076 D12), with the missing half named in the\n * message rather than left for a deployer to discover: `trigger()` really runs\n * the registered handler, but nothing here owns a timer, so a `schedule()`d job\n * NEVER fires on its own. That is reduced capability, not fabricated output —\n * no call returns a made-up answer. `handlerReady: false`: no HTTP surface.\n */\nexport function createMemoryJob() {\n const jobs = new Map<string, any>();\n return {\n __serviceInfo: {\n status: 'degraded' as const,\n handlerReady: false,\n message: 'In-process job registry — trigger() runs handlers, but scheduled jobs never fire on their own (no timer). Register a job plugin (e.g. Agenda) for real scheduling.',\n },\n _serviceName: 'job',\n async schedule(name: string, schedule: any, handler: any): Promise<void> { jobs.set(name, { schedule, handler }); },\n async cancel(name: string): Promise<void> { jobs.delete(name); },\n async trigger(name: string, data?: unknown): Promise<void> {\n const job = jobs.get(name);\n if (job?.handler) await job.handler({ jobId: name, data });\n },\n async getExecutions(): Promise<any[]> { return []; },\n async listJobs(): Promise<string[]> { return [...jobs.keys()]; },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { normalizeSupportedLocales } from '@objectstack/spec/system';\n\n/**\n * Recursively merge `source` into `target`. Nested plain objects are merged\n * rather than replaced, so multiple plugins can each contribute their own\n * slice of a locale's translations (e.g. `{objects: {account: ...}}` and\n * `{objects: {task: ...}}`) without clobbering one another.\n * Exported for the authored-translation sync (#2591).\n */\nexport function deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...target };\n for (const key of Object.keys(source)) {\n const tVal = target[key];\n const sVal = source[key];\n if (\n tVal && sVal\n && typeof tVal === 'object' && !Array.isArray(tVal)\n && typeof sVal === 'object' && !Array.isArray(sVal)\n ) {\n result[key] = deepMerge(\n tVal as Record<string, unknown>,\n sVal as Record<string, unknown>,\n );\n } else {\n result[key] = sVal;\n }\n }\n return result;\n}\n\n/**\n * Resolve a locale code against available locales with fallback.\n *\n * Fallback chain:\n * 1. Exact match (e.g. `zh-CN` → `zh-CN`)\n * 2. Case-insensitive match (e.g. `zh-cn` → `zh-CN`)\n * 3. Base language match (e.g. `zh-CN` → `zh`)\n * 4. Variant expansion (e.g. `zh` → `zh-CN`)\n *\n * Returns the matched locale code, or `undefined` when no match is found.\n */\nexport function resolveLocale(requestedLocale: string, availableLocales: string[]): string | undefined {\n if (availableLocales.length === 0) return undefined;\n\n // 1. Exact match\n if (availableLocales.includes(requestedLocale)) return requestedLocale;\n\n // 2. Case-insensitive match\n const lower = requestedLocale.toLowerCase();\n const caseMatch = availableLocales.find(l => l.toLowerCase() === lower);\n if (caseMatch) return caseMatch;\n\n // 3. Base language match (zh-CN → zh)\n const baseLang = requestedLocale.split('-')[0].toLowerCase();\n const baseMatch = availableLocales.find(l => l.toLowerCase() === baseLang);\n if (baseMatch) return baseMatch;\n\n // 4. Variant expansion (zh → zh-CN, zh-TW, etc. — first match wins)\n const variantMatch = availableLocales.find(l => l.split('-')[0].toLowerCase() === baseLang);\n if (variantMatch) return variantMatch;\n\n return undefined;\n}\n\n/**\n * In-memory i18n service fallback.\n *\n * Implements the II18nService contract with basic translate/load/getLocales\n * operations. Used by ObjectKernel as an automatic fallback when no real\n * i18n plugin (e.g. I18nServicePlugin) is registered.\n *\n * Supports runtime translation loading, locale management, and\n * locale code fallback (e.g. `zh` → `zh-CN`).\n * Does not load files from disk — operates purely in-memory.\n */\nexport function createMemoryI18n() {\n const translations = new Map<string, Record<string, unknown>>();\n // Runtime-AUTHORED overlay (#2591): translations published as `translation`\n // metadata. Kept separate from the static map so a re-sync can REPLACE the\n // whole authored layer (clear-then-reload — deleted keys must not linger),\n // while authored values win over static bundle values on read.\n const authored = new Map<string, Record<string, unknown>>();\n let defaultLocale = 'en';\n // [#7679] The app's DECLARED `i18n.supportedLocales`, injected by\n // `AppPlugin.loadTranslations` the same way `defaultLocale` is. `undefined`\n // means the app declared nothing, which must keep reporting every loaded\n // locale. Held as a read-time filter, never as a prune of `translations`:\n // platform plugins push their bundles at `kernel:ready`, after the app\n // plugin has run, so anything pruned once would grow back.\n let supportedLocales: string[] | undefined;\n\n /**\n * Resolve a dot-notation key from a nested object.\n */\n function resolveKey(data: Record<string, unknown>, key: string): string | undefined {\n const parts = key.split('.');\n let current: unknown = data;\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return typeof current === 'string' ? current : undefined;\n }\n\n /** Merged (static ⊕ authored) view of a single, exact locale key. */\n function mergedLocale(locale: string): Record<string, unknown> | undefined {\n const stat = translations.get(locale);\n const auth = authored.get(locale);\n if (stat && auth) return deepMerge(stat, auth);\n return auth ?? stat;\n }\n\n /**\n * Find translation data for a locale, with fallback resolution.\n */\n function resolveTranslations(locale: string): Record<string, unknown> | undefined {\n // Exact match\n const exact = mergedLocale(locale);\n if (exact) return exact;\n\n // Locale fallback (zh → zh-CN, en-us → en-US, etc.)\n const allLocales = [...new Set([...translations.keys(), ...authored.keys()])];\n const resolved = resolveLocale(locale, allLocales);\n if (resolved) return mergedLocale(resolved);\n\n return undefined;\n }\n\n return {\n // [#4058] `degraded` (ADR-0076 D12): translations, locale fallback and\n // interpolation are all real — what is missing is persistence and the\n // authoring surface service-i18n adds. `handlerReady` left at the\n // `degraded` default (true): the dispatcher's `/i18n` domain does serve\n // this implementation.\n __serviceInfo: {\n status: 'degraded' as const,\n message: 'In-memory translations — real lookup and locale fallback, but nothing is persisted. Register I18nServicePlugin from @objectstack/service-i18n for the full implementation.',\n },\n _serviceName: 'i18n',\n\n t(key: string, locale: string, params?: Record<string, unknown>): string {\n const data = resolveTranslations(locale) ?? mergedLocale(defaultLocale);\n const value = data ? resolveKey(data, key) : undefined;\n if (value == null) return key;\n if (!params) return value;\n // Interpolation format: {{paramName}} — matches FileI18nAdapter convention\n return value.replace(/\\{\\{(\\w+)\\}\\}/g, (_, name) => String(params[name] ?? `{{${name}}}`));\n },\n\n getTranslations(locale: string): Record<string, unknown> {\n return resolveTranslations(locale) ?? {};\n },\n\n loadTranslations(locale: string, data: Record<string, unknown>): void {\n const existing = translations.get(locale);\n if (existing) {\n translations.set(locale, deepMerge(existing, data));\n } else {\n translations.set(locale, { ...data });\n }\n },\n\n /**\n * Replace the ENTIRE runtime-authored translation layer (#2591). Called\n * by the authored-translation sync with the full current set of active\n * `translation` metadata items keyed by locale. Wholesale replacement —\n * not a merge — so deleted items/keys stop resolving on the next sync.\n */\n replaceAuthoredTranslations(byLocale: Record<string, Record<string, unknown>>): void {\n authored.clear();\n for (const [locale, data] of Object.entries(byLocale ?? {})) {\n if (!data || typeof data !== 'object') continue;\n authored.set(locale, { ...data });\n }\n },\n\n /**\n * Report the locales this stack offers.\n *\n * [#7679] When the app declared `i18n.supportedLocales`, that declaration\n * IS the answer — in declared order, and including a declared locale no\n * bundle was ever loaded for (declared-but-unserved). Reporting the\n * declaration rather than an intersection is what gives a client the\n * signal that the locale it is being offered has nothing behind it yet;\n * quietly dropping it would leave the gap invisible on both sides. It is\n * also the only answer that does not depend on how much had loaded by the\n * time this was called.\n *\n * With nothing declared, the loaded set — the behaviour every app that\n * never opted in already has.\n */\n getLocales(): string[] {\n if (supportedLocales) return [...supportedLocales];\n return [...new Set([...translations.keys(), ...authored.keys()])];\n },\n\n /** @see II18nService.setSupportedLocales — [#7679] */\n setSupportedLocales(locales: readonly string[] | undefined): void {\n supportedLocales = normalizeSupportedLocales(locales);\n },\n\n getDefaultLocale(): string {\n return defaultLocale;\n },\n\n setDefaultLocale(locale: string): void {\n defaultLocale = locale;\n },\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [#7378] The `IMetadataService` register/read argument contract, enforced —\n * the shared guard every shipped implementation of the contract's CRUD members\n * calls, so the maintainer's three-cell ruling has ONE implementation instead\n * of three re-derivations that can drift.\n *\n * Maintainer ruling, 2026-08-12 (#7378, 裁定人:维护者 huangyiirene), quoted\n * verbatim and untranslated:\n *\n * > 1. **Row 1(key 归属)= (c) 响亮拒绝。** `register(type, name, data)` 中\n * > `name` 参数与 `data.name` 不一致时,所有实现统一拒绝并报错定位 ——\n * > 不一致几乎必是作者 bug,任一方向的静默解决都可能把条目放错位置。\n * > 2. **Row 2(objects/object 别名)= 所有实现一个答案,与\n * > `check:meta-type-normalized` 收敛。** 类型名归一化是契约级规则,不是\n * > 各实现自留的民俗。\n * > 3. **Row 3(非对象 data 静默丢弃)= 响亮拒绝(throw)。** 接受后丢失、且\n * > 任何成员都读不回来,无可辩护;拒绝一个实现无法键控的 `data` 与契约\n * > 同样一致。修的是「接受再丢」,不强求「必须存下」。\n *\n * This module hosts rows 1 and 3 ({@link assertMetadataRegisterContract}) and\n * row 2 ({@link canonicalMetadataServiceType}). It lives in `@objectstack/core`\n * because core is the lowest common dependency of the three shipped\n * implementations — `createMemoryMetadata` (this package), `MetadataManager`\n * (`@objectstack/metadata`) and `MetadataFacade` (`@objectstack/objectql`).\n * The contract's own reference double (`packages/spec`) cannot import from\n * here — spec is the dependency root — so its copy of these rules rides the\n * spec-side half of the ruling, tracked on #7378.\n *\n * ## Row 2 — why the fold, and whose direction it is\n *\n * Plural→singular type-name folding is a decided, enforced platform direction,\n * not this module's invention. The owners this converges with, per the ruling's\n * own instruction to read the gate's existing direction first (「实现者先读该\n * 闸门的既有方向再落」):\n *\n * - `canonicalMetaType` (`metadata-protocol/src/protocol.ts`, #4432)\n * canonicalizes every `/meta` request type at the protocol boundary via the\n * same `PLURAL_TO_SINGULAR` map this module reads;\n * - `check:meta-type-normalized` (`scripts/check-meta-type-normalized.mjs`)\n * is the CI gate whose whole job is to refuse a DECISION made on the\n * un-normalized `:type` — its header carries the three authorization\n * bypasses (#3984, #5881, #6241) that made the direction a rule. Its scan\n * surface is `packages/rest/src`; what this module converges with is its\n * DIRECTION: normalize once, at the entry, and let every decision — here,\n * every store key — read the normalized value;\n * - Prime Directive #3: metadata type names are canonically **singular**.\n *\n * Before this ruling, `MetadataManager` and `createMemoryMetadata` keyed their\n * type stores on the raw string, so `register('objects', n, d)` landed in a\n * store `get('object', n)` never read — two stores for one type, differing\n * from `MetadataFacade`, whose `SchemaRegistry` reads alias both spellings.\n * One answer now: the store key is the canonical type.\n *\n * ## Rows 1 and 3 — what the refusals close\n *\n * Row 1: a `data.name` that disagrees with the `name` argument was resolved\n * silently in both directions in shipped code — argument-wins\n * (`MetadataManager`, `createMemoryMetadata`) and document-wins (the\n * pre-ruling `MetadataFacade`) — and either way an author's item could be\n * filed under a key the author never wrote. Refusing is the only answer that\n * cannot misplace the item.\n *\n * Row 3: a `data` that is not a plain object cannot be a metadata document.\n * The pre-ruling `MetadataFacade` accepted such a write and filed it under the\n * literal key `undefined` — readable back through no member (silent loss, the\n * #6725 family) — and the interim fix coerced it into a `{ name, content }`\n * box, which collides with `content` being a REAL authorable field on live\n * metadata types (`doc`, `knowledge_document`). The ruling forbids both:\n * refuse, do not coerce into storability. `null` and arrays are refused with\n * primitives — neither can carry the document identity a metadata store keys\n * on, and `{ ...[a, b] }` is `{ 0: a, 1: b }`, the same corruption one shape\n * over.\n *\n * The executable form of all three rows is `METADATA_ROUNDTRIP_CASES`\n * (`@objectstack/spec/contracts`) replayed by\n * `packages/objectql/src/metadata-service-roundtrip-conformance.test.ts`.\n */\n\n// The `/api` import is TYPE-ONLY on purpose — erased at compile time, so this\n// module makes no runtime demand on that subpath. This module is loaded by\n// every consumer of `@objectstack/core`, and several packages' vitest configs\n// alias the bare `@objectstack/spec` specifier to `spec/src/index.ts` (a FILE)\n// with per-subpath entries spelled out above it; an alias list matches by\n// PREFIX, so any subpath NOT spelled out resolves under the file and dies with\n// ENOTDIR at import time (measured: `@objectstack/plugin-hono-server` and\n// `@objectstack/driver-memory`, 39 test files dead at load between them). The\n// typed literal below keeps the closed-set compile check without the runtime\n// import — the `packages/spec/src/contracts/storage-service.ts` pattern.\n// `/shared` cannot get the same treatment: `pluralToSingular` is a runtime\n// value and its map has ONE owner (#7378 row 2 — copying it here would be the\n// per-implementation folk normalization the ruling forbids), so the consumer\n// configs carry a `/shared` alias entry instead.\nimport type { StandardErrorCode } from '@objectstack/spec/api';\nimport { pluralToSingular } from '@objectstack/spec/shared';\n\n/** The standard catalog's generic argument-validation code, type-checked against the closed set. */\nconst REGISTER_REFUSAL_CODE: StandardErrorCode = 'VALIDATION_ERROR';\n\n/**\n * The canonical spelling an `IMetadataService` type store is keyed on\n * (#7378 row 2). Folds a plural manifest spelling to the singular metadata\n * type name (`'objects'` → `'object'`, `'views'` → `'view'`, …) through the\n * platform's one plural↔singular map (`PLURAL_TO_SINGULAR`,\n * `@objectstack/spec/shared`); a name with no plural mapping — which includes\n * every canonical singular type — passes through unchanged.\n */\nexport function canonicalMetadataServiceType(type: string): string {\n return pluralToSingular(type);\n}\n\n/**\n * An ADR-0112-enveloped refusal (`code` + `status` on the error), so a caller\n * — and a rejection-class test — can assert the refusal rather than merely\n * \"it threw\". `VALIDATION_ERROR` is the standard catalog's generic\n * argument-validation code; the ledger's own guidance is to use the standard\n * catalog rather than register a synonym for a generic condition.\n */\nfunction registerRefusal(message: string): Error & { code: string; status: number } {\n const err = new Error(message) as Error & { code: string; status: number };\n err.code = REGISTER_REFUSAL_CODE;\n err.status = 400;\n return err;\n}\n\n/**\n * Enforce rows 1 and 3 of the #7378 ruling on a\n * `register(type, name, data)` payload — call it before the first store write,\n * so a refusal writes nothing anywhere.\n *\n * Refuses, with a locating `VALIDATION_ERROR` (status 400):\n *\n * - **a non-document `data`** (row 3): anything that is not a plain object —\n * primitives, `null`, arrays. The contract declares `data: unknown`, so\n * this is a runtime refusal, not a type error;\n * - **a `data.name` that disagrees with the `name` argument** (row 1), in\n * either direction. A document with NO `name` of its own is fine — the\n * argument is the key, and there is no disagreement to refuse.\n *\n * Deliberately NOT called by `registerInMemory`: that optional member is a\n * boot-time seeding primitive outside the ruled surface (the ruling names\n * `register`), and its callers hand it artefacts whose shape source control\n * owns. It shares the row-2 canonical fold — a store key is a store fact, not\n * a per-member choice — just not the refusals.\n */\nexport function assertMetadataRegisterContract(\n type: string,\n name: string,\n data: unknown,\n): asserts data is Record<string, unknown> {\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n const shape = data === null ? 'null' : Array.isArray(data) ? 'an array' : `a ${typeof data}`;\n throw registerRefusal(\n `IMetadataService.register('${type}', '${name}'): data is ${shape}, not a metadata document. ` +\n `register() stores plain-object documents only — accepting a value the service cannot key was measured as ` +\n `accept-then-drop on document-keyed stores (#7378 row 3: refuse loudly, never coerce into storability). ` +\n `Wrap the value in a document object whose shape the '${type}' type's schema accepts, or store it under a type that declares one.`,\n );\n }\n const documentName = (data as { name?: unknown }).name;\n if (documentName !== undefined && documentName !== name) {\n throw registerRefusal(\n `IMetadataService.register('${type}', '${name}'): data.name is '${String(documentName)}', which disagrees with the ` +\n `name argument '${name}'. A disagreement is almost always an authoring bug, and resolving it silently in either ` +\n `direction can file the item under a key the caller never wrote (#7378 row 1: refuse loudly, locate the mismatch). ` +\n `Register under one name: pass the intended key as the argument and make data.name match it, or omit data.name.`,\n );\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport {\n assertMetadataRegisterContract,\n canonicalMetadataServiceType,\n} from '../metadata-service-contract.js';\n\n/**\n * In-memory metadata service fallback.\n *\n * Implements the IMetadataService contract with a simple Map-of-Maps store.\n * Used by ObjectKernel as an automatic fallback when no real metadata plugin\n * (e.g. MetadataPlugin with file-system persistence) is registered.\n *\n * [#7378] Carries the ruled register/read argument contract\n * (`../metadata-service-contract.ts` — the ruling is quoted there):\n * `register` refuses a `data.name` that disagrees with the `name` argument and\n * refuses a non-document `data` (rows 1/3), and every type store is keyed on\n * the CANONICAL type (row 2), so `register('objects', n, d)` and\n * `get('object', n)` address one store rather than two.\n */\nexport function createMemoryMetadata() {\n // canonical type -> name -> data\n const store = new Map<string, Map<string, any>>();\n\n // [#7378 row 2] The fold lives on the single accessor every member reads\n // and writes through, so no member can address a raw-spelling store.\n function getTypeMap(type: string): Map<string, any> {\n const canonical = canonicalMetadataServiceType(type);\n let map = store.get(canonical);\n if (!map) {\n map = new Map();\n store.set(canonical, map);\n }\n return map;\n }\n\n return {\n // [#4058] `degraded` (ADR-0076 D12): the registry is real — everything\n // registered is listable and readable back — it simply never reaches disk\n // or a database. `handlerReady` keeps the `degraded` default (true): the\n // dispatcher's `/meta` domain serves this implementation.\n __serviceInfo: {\n status: 'degraded' as const,\n message: 'In-memory metadata registry — real reads and writes, no persistence (lost on restart). Register MetadataPlugin for a persisted registry.',\n },\n _serviceName: 'metadata',\n async register(type: string, name: string, data: any): Promise<void> {\n // [#7378 rows 1/3] Refuse — before the store is touched — a data.name\n // that disagrees with the name argument, and a non-document data. The\n // guard's own header carries the ruling and the reasons.\n assertMetadataRegisterContract(type, name, data);\n getTypeMap(type).set(name, data);\n },\n // Mirror MetadataManager.registerInMemory (synchronous, no persistence).\n // AppPlugin gates code-defined-datasource / stack-RBAC registration on\n // `typeof metadata.registerInMemory === 'function'` (it must register\n // GitOps-managed artefacts *listably* but never persist them). Without this\n // method the guard was false on the host-config / standalone boot path —\n // where this fallback (not MetadataPlugin) provides the `metadata` service —\n // so `defineStack({ datasources })` entries silently never reached the\n // registry and were absent from GET /api/v1/datasources and\n // GET /api/v1/meta/datasource (ADR-0015 §18). This store is already\n // in-memory only, so registerInMemory and register share a store — but\n // NOT the [#7378] refusals: the ruling names `register`, and this member\n // is a boot-time seeding primitive for source-control-owned artefacts\n // (see assertMetadataRegisterContract's header for the boundary). It does\n // share the row-2 canonical type fold, via getTypeMap.\n registerInMemory(type: string, name: string, data: any): void {\n getTypeMap(type).set(name, data);\n },\n async get(type: string, name: string): Promise<any> {\n return getTypeMap(type).get(name);\n },\n async list(type: string): Promise<any[]> {\n return Array.from(getTypeMap(type).values());\n },\n async unregister(type: string, name: string): Promise<void> {\n getTypeMap(type).delete(name);\n },\n async exists(type: string, name: string): Promise<boolean> {\n return getTypeMap(type).has(name);\n },\n async listNames(type: string): Promise<string[]> {\n return Array.from(getTypeMap(type).keys());\n },\n async getObject(name: string): Promise<any> {\n return getTypeMap('object').get(name);\n },\n async listObjects(): Promise<any[]> {\n return Array.from(getTypeMap('object').values());\n },\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Runtime-authored translation sync (#2591).\n *\n * Translations authored in the Studio persist as `translation` sys_metadata\n * rows (`allowRuntimeCreate: true`), but historically only STATIC bundles\n * (app `bundle.translations`, plugin `translations/`) were ever loaded into\n * the i18n runtime — a published translation was a dead-end on publish AND\n * after a restart.\n *\n * This module is the single shared implementation of the fix, wired by both\n * hosts an `i18n` service can come from:\n * - the runtime's AppPlugin (covers the kernel's in-memory fallback — the\n * dev/standalone reality) and\n * - @objectstack/service-i18n's I18nServicePlugin (the file-based adapter).\n * Both adapters expose `replaceAuthoredTranslations(byLocale)`; the sync\n * computes the full authored layer from the rows and REPLACES it wholesale\n * (clear-then-reload), so deleted items/keys stop resolving.\n *\n * Item payload is a single-locale `TranslationItem` — the same `objects.`\n * groups the file-authored bundles use, plus the `locale` it translates\n * (#3778; before that, this type was registered against an object-first\n * `o.<object>` dialect no resolver read, so an authored translation saved\n * cleanly and rendered nothing). Locale resolution: the top-level `locale`,\n * then the item name when it looks like a BCP-47 tag — the name fallback\n * covers rows written before `locale` became required. Rows still carrying\n * the retired shape are skipped with a warning naming the row, since their\n * content can never resolve. Multiple items on one locale deep-merge in name\n * order (deterministic).\n *\n * Trigger points (wired by {@link wireAuthoredTranslationSync}):\n * • `kernel:ready` — cold-boot coverage;\n * • `metadata:reloaded` — publish-while-running coverage (#2576);\n * • protocol `onMetadataMutation` — direct-active saves / deletes of\n * `translation` rows that don't go through a package publish (#2588's\n * mutation stream).\n *\n * Rows are read straight from `sys_metadata` through the engine — the same\n * discipline as the authored-hook re-sync (#2588): env-scoped kernels\n * surface authored rows nowhere else, and the i18n map is process-wide so\n * rows are taken across all organizations. Best-effort: a failed read keeps\n * the currently applied authored layer.\n */\n\nimport { LEGACY_OBJECT_FIRST_KEYS } from '@objectstack/spec/system';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\n\nimport { deepMerge } from './memory-i18n.js';\n\ntype AnyRecord = Record<string, any>;\n\ninterface MinimalCtx {\n logger: { debug?: (...a: any[]) => void; info?: (...a: any[]) => void; warn?: (...a: any[]) => void };\n getService(name: string): any;\n hook?(name: string, fn: () => Promise<void> | void): void;\n}\n\n/**\n * Ownership marker: several plugins may wire the sync against the same\n * kernel (AppPlugin AND I18nServicePlugin on a production server). The first\n * wirer to touch a given i18n service instance claims it; later wirers\n * no-op, so the layer is computed once per change instead of N times.\n */\nconst OWNER_PROP = '__authoredTranslationSyncOwner';\n\n/**\n * The `i18n` slot as this wirer uses it: the authored-layer replace seam, plus\n * the ownership marker stamped on the instance.\n *\n * [#4251] `replaceAuthoredTranslations` is NOT on `II18nService` — it is the\n * authored-overlay seam service-i18n grew for this sync, and every call site\n * probes for it. `OWNER_PROP` is this module's own marker, stamped on whatever\n * object occupies the slot so two wirers cannot both drive one instance.\n * Declared here rather than erased to `any` so both facts stay legible: what\n * the slot must supply, and what this module writes onto it.\n */\ninterface AuthoredTranslationSink {\n replaceAuthoredTranslations(layer: Record<string, unknown>): void;\n [OWNER_PROP]?: symbol;\n}\n\n// Deliberately narrow (language + optional script/region only): item names\n// are snake_case, so a permissive multi-segment pattern would classify names\n// like `my_custom_strings` as locales.\nconst LOCALE_LIKE = /^[a-z]{2,3}([_-]([A-Za-z]{4}|[A-Za-z]{2}|[0-9]{3}))?$/;\n\n/**\n * Read ACTIVE `translation` metadata rows and compute the authored layer,\n * keyed by locale. Returns `null` when the read failed (callers must keep\n * the current layer, never tear it down on an error).\n */\nexport async function readAuthoredTranslationLayer(\n engine: { find(object: string, opts?: AnyRecord): Promise<any[]> },\n logger?: MinimalCtx['logger'],\n): Promise<Record<string, Record<string, unknown>> | null> {\n let rows: any[];\n try {\n rows = (await engine.find('sys_metadata', {\n where: { type: 'translation', state: 'active' },\n })) ?? [];\n if (rows.length === 0) {\n // Legacy plural rows — mirrors the protocol's singular/plural fallback.\n rows = (await engine.find('sys_metadata', {\n where: { type: 'translations', state: 'active' },\n })) ?? [];\n }\n } catch (err: any) {\n logger?.debug?.('[i18n] authored-translation read failed — keeping current layer', {\n error: err?.message,\n });\n return null;\n }\n\n const byLocale: Record<string, Record<string, unknown>> = {};\n const sorted = [...rows].sort((a, b) => String(a?.name ?? '').localeCompare(String(b?.name ?? '')));\n for (const row of sorted) {\n let data: any;\n try {\n data = typeof row.metadata === 'string' ? JSON.parse(row.metadata) : row.metadata;\n } catch {\n continue; // malformed row — skip it, keep the rest\n }\n if (!data || typeof data !== 'object') continue;\n\n // Rows written against the retired object-first shape resolve to nothing\n // no matter what locale they claim, so say that plainly rather than\n // letting them look loaded. New saves are rejected at the metadata door\n // by `TranslationItemSchema`; this covers rows that predate it.\n const legacyKeys = LEGACY_OBJECT_FIRST_KEYS.filter((key) => data[key] !== undefined);\n if (legacyKeys.length > 0) {\n logger?.warn?.(\n `[i18n] authored translation '${row?.name}' uses the retired object-first shape `\n + `(${legacyKeys.join(', ')}) — nothing resolves from it; re-author it under `\n + \"'objects.<object_name>' with a top-level 'locale' — skipped\",\n );\n continue;\n }\n\n const locale: string | undefined =\n (typeof data?.locale === 'string' && data.locale)\n || (typeof row?.name === 'string' && LOCALE_LIKE.test(row.name) ? row.name : undefined)\n || undefined;\n if (!locale) {\n logger?.warn?.(\n `[i18n] authored translation '${row?.name}' has no resolvable locale `\n + \"(set the top-level 'locale', or name the item after its BCP-47 locale) — skipped\",\n );\n continue;\n }\n // Strip authoring bookkeeping; everything else is translation data. The\n // lock/package fields are stamped by the metadata protocol on published\n // rows — merging them would seed junk keys into the i18n layer.\n const {\n name: _n, locale: _l,\n _packageId: _p, _packageVersion: _pv, _provenance: _pr,\n _lock: _lk, _lockReason: _lr, _lockDocsUrl: _ld, _lockSource: _ls,\n ...payload\n } = data;\n byLocale[locale] = deepMerge(byLocale[locale] ?? {}, payload as Record<string, unknown>);\n }\n return byLocale;\n}\n\n/**\n * Wire the authored-translation sync into a plugin context: registers the\n * `kernel:ready` / `metadata:reloaded` hooks and (at kernel:ready) the\n * protocol mutation subscription. Idempotent per i18n service instance via\n * the ownership marker. Safe to call on kernels with no engine, no protocol,\n * or an i18n service without `replaceAuthoredTranslations` — every path\n * degrades to a no-op.\n */\nexport function wireAuthoredTranslationSync(ctx: MinimalCtx): void {\n if (typeof ctx.hook !== 'function') return;\n\n const token = Symbol('authored-translation-sync');\n const resolveOwnedI18n = (): AuthoredTranslationSink | null => {\n let i18n: AuthoredTranslationSink | undefined;\n try { i18n = ctx.getService('i18n'); } catch { return null; }\n if (!i18n || typeof i18n.replaceAuthoredTranslations !== 'function') return null;\n const current = i18n[OWNER_PROP];\n if (current === undefined) {\n i18n[OWNER_PROP] = token;\n return i18n;\n }\n return current === token ? i18n : null; // another wirer owns this instance\n };\n\n // Serialized: overlapping publishes must not finish out of order and leave\n // the older authored snapshot applied.\n let chain: Promise<void> = Promise.resolve();\n const sync = (): Promise<void> => {\n const run = chain.then(async () => {\n const i18n = resolveOwnedI18n();\n if (!i18n) return;\n let engine: IDataEngine | undefined;\n try { engine = ctx.getService('objectql'); } catch { return; }\n if (!engine || typeof engine.find !== 'function') return;\n const layer = await readAuthoredTranslationLayer(engine, ctx.logger);\n if (layer === null) return; // failed read — keep current layer\n i18n.replaceAuthoredTranslations(layer);\n ctx.logger.info?.('[i18n] synced runtime-authored translations', {\n locales: Object.keys(layer),\n });\n });\n chain = run.catch(() => undefined);\n return run;\n };\n\n ctx.hook('kernel:ready', async () => {\n // Subscribe to translation mutations through the protocol choke point\n // (#2588). Only the owning wirer subscribes.\n if (resolveOwnedI18n()) {\n let protocol: any = null;\n try { protocol = ctx.getService('protocol'); } catch { /* no protocol on this kernel */ }\n if (protocol && typeof protocol.onMetadataMutation === 'function') {\n protocol.onMetadataMutation((evt: { type: string; name: string; state: string }) => {\n if (evt?.type !== 'translation' || evt.state === 'draft') return;\n void sync().catch((err: any) => {\n ctx.logger.warn?.('[i18n] authored-translation re-sync after mutation failed', {\n item: evt.name,\n error: err?.message,\n });\n });\n });\n }\n }\n await sync();\n });\n ctx.hook('metadata:reloaded', async () => {\n await sync();\n });\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createMemoryCache } from './memory-cache.js';\nimport { createMemoryQueue } from './memory-queue.js';\nimport { createMemoryJob } from './memory-job.js';\nimport { createMemoryI18n } from './memory-i18n.js';\nimport { createMemoryMetadata } from './memory-metadata.js';\n\nexport { createMemoryCache } from './memory-cache.js';\nexport { createMemoryQueue } from './memory-queue.js';\nexport { createMemoryJob } from './memory-job.js';\nexport { createMemoryI18n, resolveLocale, deepMerge } from './memory-i18n.js';\nexport { createMemoryMetadata } from './memory-metadata.js';\nexport {\n wireAuthoredTranslationSync,\n readAuthoredTranslationLayer,\n} from './authored-translation-sync.js';\n\n/**\n * Map of core-criticality service names to their in-memory fallback factories.\n * Used by ObjectKernel.validateSystemRequirements() to auto-inject fallbacks\n * when no real plugin provides the service.\n */\nexport const CORE_FALLBACK_FACTORIES: Record<string, () => Record<string, any>> = {\n metadata: createMemoryMetadata,\n cache: createMemoryCache,\n queue: createMemoryQueue,\n job: createMemoryJob,\n i18n: createMemoryI18n,\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin } from './types.js';\nimport { createLogger, ObjectLogger } from './logger.js';\nimport type { LoggerConfig } from '@objectstack/spec/system';\nimport { ObjectKernelBase } from './kernel-base.js';\n\n/**\n * ObjectKernel - MiniKernel Architecture\n * \n * A highly modular, plugin-based microkernel that:\n * - Manages plugin lifecycle (init, start, destroy)\n * - Provides dependency injection via service registry\n * - Implements event/hook system for inter-plugin communication\n * - Handles dependency resolution (topological sort)\n * - Provides configurable logging for server and browser\n * \n * Core philosophy:\n * - Business logic is completely separated into plugins\n * - Kernel only manages lifecycle, DI, and hooks\n * - Plugins are loaded as equal building blocks\n */\nexport class LiteKernel extends ObjectKernelBase {\n constructor(config?: { logger?: Partial<LoggerConfig> }) {\n const logger = createLogger(config?.logger);\n super(logger);\n \n // Initialize context after logger is created\n this.context = this.createContext();\n }\n\n /**\n * Register a plugin\n * @param plugin - Plugin instance\n */\n use(plugin: Plugin): this {\n this.validateIdle();\n\n const pluginName = plugin.name;\n if (this.plugins.has(pluginName)) {\n throw new Error(`[Kernel] Plugin '${pluginName}' already registered`);\n }\n\n this.plugins.set(pluginName, plugin);\n return this;\n }\n\n /**\n * Bootstrap the kernel\n * 1. Resolve dependencies (topological sort)\n * 2. Init phase - plugins register services\n * 3. Start phase - plugins execute business logic\n * 4. Trigger 'kernel:ready' hook\n */\n async bootstrap(): Promise<void> {\n this.validateState('idle');\n\n this.state = 'initializing';\n this.logger.info('Bootstrap started');\n\n // Resolve dependencies\n const orderedPlugins = this.resolveDependencies();\n\n // Pre-Phase-1 ordering contract (ADR-0116, #4131): a plugin that\n // requires a service provided only by a later plugin fails HERE,\n // named, before any init side effects.\n this.validateInitServices(orderedPlugins);\n\n // Phase 1: Init - Plugins register services\n this.logger.info('Phase 1: Init plugins');\n for (const plugin of orderedPlugins) {\n await this.runPluginInit(plugin);\n }\n\n // Phase 2: Start - Plugins execute business logic\n this.logger.info('Phase 2: Start plugins');\n this.state = 'running';\n \n for (const plugin of orderedPlugins) {\n await this.runPluginStart(plugin);\n }\n\n // The three boot-path lifecycle hooks all use PROPAGATING dispatch,\n // identical to `ObjectKernel.bootstrap()`'s `context.trigger` (a bare\n // awaited loop that never catches): a handler that throws FAILS THE\n // BOOT on both kernels, the remaining handlers are skipped, the\n // original error reaches the caller unwrapped, and the kernel is left\n // 'stopped' rather than 'running' so a failed boot never reads as a\n // live kernel. `kernel:ready` got this in #5170; `kernel:bootstrapped`\n // and `kernel:listening` in #5257.\n //\n // Why the boot path is the wrong place to be forgiving: everything\n // between here and the log line below is a PRECONDITION of the\n // \"✅ Bootstrap complete\" this method is about to print. Swallowing a\n // throw does not make the boot succeed — it only makes the failure\n // invisible while `bootstrap()` resolves normally. `kernel:listening`\n // is the sharpest case: it is where HTTP server plugins actually open\n // their socket (`HonoServerPlugin` awaits `server.listen(port)` with\n // no try/catch of its own, deliberately — propagation is the correct\n // behaviour there), so a swallowed EACCES / unavailable-listen on an\n // edge or serverless host produced a live process, a cheerful\n // \"Bootstrap complete\", and not one socket listening.\n try {\n // Route/middleware registration phase, and the only correct moment\n // for a plugin to assert that what it DECLARED can actually be\n // delivered — the registries are still filling during init(), so a\n // boot gate has nowhere earlier to run (#5170).\n await this.triggerHookOrThrow('kernel:ready');\n // \"All synchronous bootstrap has settled\" anchor, strictly after\n // every kernel:ready handler has settled and before any HTTP socket\n // opens. Carries reconcile/backfill/audit work. NOTE: does not\n // guarantee background app seed data has settled — subscribe\n // `app:seeded` for that (see plugin-lifecycle-events.ts).\n await this.triggerHookOrThrow('kernel:bootstrapped');\n // HTTP servers open their listening socket here — strictly after\n // every kernel:ready and kernel:bootstrapped handler has completed.\n await this.triggerHookOrThrow('kernel:listening');\n } catch (error) {\n this.state = 'stopped';\n throw error;\n }\n this.logger.info('✅ Bootstrap complete', {\n pluginCount: this.plugins.size\n });\n }\n\n /**\n * Shutdown the kernel\n * Calls destroy on all plugins in reverse order\n */\n async shutdown(): Promise<void> {\n await this.destroy();\n }\n\n /**\n * Graceful shutdown - destroy all plugins in reverse order\n */\n async destroy(): Promise<void> {\n if (this.state === 'stopped') {\n this.logger.warn('Kernel already stopped');\n return;\n }\n\n this.state = 'stopping';\n this.logger.info('Shutdown started');\n\n // Trigger shutdown hook — FAIL-SOFT dispatch ({@link triggerHook}),\n // deliberately, and NOT the propagating dispatcher the boot-path hooks\n // above use (#5257). This is a per-hook judgement written down, not an\n // inherited default: on the shutdown path there is no \"refuse to\n // proceed\" left to buy. The remaining work — every other subscriber's\n // cleanup, then each plugin's destroy() in reverse order — is what\n // flushes buffers, closes connections and releases locks, so letting\n // one subscriber's failure abort the rest converts a single bad\n // handler into leaked resources and unflushed writes. A failing\n // shutdown handler is logged (`Hook handler failed: kernel:shutdown`)\n // and the cleanup continues.\n await this.triggerHook('kernel:shutdown');\n\n // Destroy plugins in reverse order\n const orderedPlugins = this.resolveDependencies();\n for (const plugin of orderedPlugins.reverse()) {\n await this.runPluginDestroy(plugin);\n }\n\n this.state = 'stopped';\n this.logger.info('✅ Shutdown complete');\n \n // Cleanup logger resources\n if (this.logger && typeof (this.logger as ObjectLogger).destroy === 'function') {\n await (this.logger as ObjectLogger).destroy();\n }\n }\n\n /**\n * Get a service from the registry\n * Convenience method for external access\n */\n getService<T>(name: string): T {\n return this.context.getService<T>(name);\n }\n\n /**\n * Check if kernel is running\n */\n isRunning(): boolean {\n return this.state === 'running';\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './adapter.js';\nexport * from './runner.js';\nexport * from './http-adapter.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as QA from '@objectstack/spec/qa';\nimport { TestExecutionAdapter } from './adapter.js';\n\nexport interface TestResult {\n scenarioId: string;\n passed: boolean;\n steps: StepResult[];\n error?: unknown;\n duration: number;\n}\n\nexport interface StepResult {\n stepName: string;\n passed: boolean;\n error?: unknown;\n output?: unknown;\n duration: number;\n}\n\n/**\n * Name the runtime shape of a value the way a suite author sees it in their fixture.\n * `typeof` answers `object` for both `null` and an array, which are the two shapes a\n * `contains` author most needs told apart from a plain record.\n */\nfunction describeActualType(value: unknown): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return 'array';\n return typeof value;\n}\n\n/**\n * Say WHICH of the two things is wrong, because the message is the only thing the\n * author has: `undefined`/`null` mean the path did not resolve to a value, so the\n * FIXTURE (the field path, or the response shape it was written against) is the\n * suspect; anything else means the path resolved fine and the ASSERTION picked an\n * operator that does not apply to what it found.\n */\nfunction containsInapplicableHint(actual: unknown): string {\n if (actual === undefined) {\n return (\n 'The path resolved to nothing — the field is absent from the result, or the path is misspelled. ' +\n \"Use 'is_null' if asserting absence is what you meant.\"\n );\n }\n if (actual === null) {\n return \"The path resolved to null. Use 'is_null' if asserting absence is what you meant.\";\n }\n return (\n \"'contains' tests array membership and string substrings only. \" +\n \"Use 'equals' to compare a scalar, or point the field at the array or string you meant to look inside.\"\n );\n}\n\nexport class TestRunner {\n constructor(private adapter: TestExecutionAdapter) {}\n\n async runSuite(suite: QA.TestSuite): Promise<TestResult[]> {\n const results: TestResult[] = [];\n for (const scenario of suite.scenarios) {\n results.push(await this.runScenario(scenario));\n }\n return results;\n }\n\n async runScenario(scenario: QA.TestScenario): Promise<TestResult> {\n const startTime = Date.now();\n const context: Record<string, unknown> = {}; // Variable context\n \n // Initialize context from initial payload if needed? Currently schema doesn't have initial context prop on Scenario\n // But we defined TestContextSchema separately.\n \n // Setup\n if (scenario.setup) {\n for (const step of scenario.setup) {\n try {\n await this.runStep(step, context);\n } catch (e) {\n return {\n scenarioId: scenario.id,\n passed: false,\n steps: [],\n error: `Setup failed: ${e instanceof Error ? e.message : String(e)}`,\n duration: Date.now() - startTime\n };\n }\n }\n }\n\n const stepResults: StepResult[] = [];\n let scenarioPassed = true;\n let scenarioError: unknown = undefined;\n\n // Main Steps\n for (const step of scenario.steps) {\n const stepStartTime = Date.now();\n try {\n const output = await this.runStep(step, context);\n stepResults.push({\n stepName: step.name,\n passed: true,\n output,\n duration: Date.now() - stepStartTime\n });\n } catch (e) {\n scenarioPassed = false;\n scenarioError = e;\n stepResults.push({\n stepName: step.name,\n passed: false,\n error: e,\n duration: Date.now() - stepStartTime\n });\n break; // Stop on first failure\n }\n }\n\n // Teardown (run even if failed)\n if (scenario.teardown) {\n for (const step of scenario.teardown) {\n try {\n await this.runStep(step, context);\n } catch (e) {\n // Log teardown failure but don't override main failure if it exists\n if (scenarioPassed) {\n scenarioPassed = false;\n scenarioError = `Teardown failed: ${e instanceof Error ? e.message : String(e)}`;\n }\n }\n }\n }\n\n return {\n scenarioId: scenario.id,\n passed: scenarioPassed,\n steps: stepResults,\n error: scenarioError,\n duration: Date.now() - startTime\n };\n }\n\n private async runStep(step: QA.TestStep, context: Record<string, unknown>): Promise<unknown> {\n // 1. Resolve Variables with Context (Simple interpolation or just pass context?)\n // For now, assume adpater handles context resolution or we do basic replacement\n const resolvedAction = this.resolveVariables(step.action, context);\n\n // 2. Execute Action\n const result = await this.adapter.execute(resolvedAction, context);\n\n // 3. Capture Outputs\n if (step.capture) {\n for (const [varName, path] of Object.entries(step.capture)) {\n context[varName] = this.getValueByPath(result, path);\n }\n }\n\n // 4. Run Assertions\n if (step.assertions) {\n for (const assertion of step.assertions) {\n this.assert(result, assertion, context);\n }\n }\n\n return result;\n }\n\n private resolveVariables(action: QA.TestAction, context: Record<string, unknown>): QA.TestAction {\n const actionStr = JSON.stringify(action);\n const resolved = actionStr.replace(/\\{\\{([^}]+)\\}\\}/g, (_match, varPath: string) => {\n const value = this.getValueByPath(context, varPath.trim());\n if (value === undefined) return _match; // Keep unresolved\n return typeof value === 'string' ? value : JSON.stringify(value);\n });\n try {\n return JSON.parse(resolved) as QA.TestAction;\n } catch {\n return action; // Fallback to original if parse fails\n }\n }\n\n private getValueByPath(obj: unknown, path: string): unknown {\n if (!path) return obj;\n const parts = path.split('.');\n let current: any = obj;\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n current = current[part];\n }\n return current;\n }\n\n private assert(result: unknown, assertion: QA.TestAssertion, _context: Record<string, unknown>) {\n const actual = this.getValueByPath(result, assertion.field);\n // Resolve expected value if it's a variable ref? \n const expected = assertion.expectedValue; // Simplify for now\n\n switch (assertion.operator) {\n case 'equals':\n if (actual !== expected) throw new Error(`Assertion failed: ${assertion.field} expected ${expected}, got ${actual}`);\n break;\n case 'not_equals':\n if (actual === expected) throw new Error(`Assertion failed: ${assertion.field} expected not ${expected}, got ${actual}`);\n break;\n case 'contains':\n if (Array.isArray(actual)) {\n if (!actual.includes(expected)) throw new Error(`Assertion failed: ${assertion.field} array does not contain ${expected}`);\n } else if (typeof actual === 'string') {\n if (!actual.includes(String(expected))) throw new Error(`Assertion failed: ${assertion.field} string does not contain ${expected}`);\n } else {\n // `contains` is defined over arrays (membership) and strings (substring), and\n // over nothing else. This branch used to be absent, so every other shape fell\n // out of the switch and the assertion reported PASSED (#7256) — a `contains`\n // written against a path the result does not carry was the test silently\n // deleting itself, and CI believed the green. An assertion the engine cannot\n // evaluate is a FAILED assertion, which is the posture every other unhandled\n // shape in this engine already takes (`default:` below; the HTTP adapter's\n // unknown action type).\n throw new Error(\n `Assertion failed: ${assertion.field} cannot be evaluated by 'contains' — ` +\n `expected an array or a string at that path, got ${describeActualType(actual)}. ` +\n containsInapplicableHint(actual)\n );\n }\n break;\n case 'not_null':\n if (actual === null || actual === undefined) throw new Error(`Assertion failed: ${assertion.field} is null`);\n break;\n case 'is_null':\n if (actual !== null && actual !== undefined) throw new Error(`Assertion failed: ${assertion.field} is not null`);\n break;\n // ... Add other operators\n default:\n throw new Error(`Unknown assertion operator: ${assertion.operator}`);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as QA from '@objectstack/spec/qa';\nimport { RestApiConfigSchema, CrudEndpointsConfigSchema } from '@objectstack/spec/api';\nimport { TestExecutionAdapter } from './adapter.js';\n\n/** Memoised {@link defaultDataPath} — the schemas are `lazySchema`, so build them once. */\nlet dataPathCache: string | undefined;\n\n/**\n * The path prefix a stock ObjectStack server serves the Data Protocol under.\n *\n * ## [#7848] Why this is derived and not written down\n *\n * Every record-shaped action type used to build `${baseUrl}/api/data/:object` —\n * a literal, one version segment short of the route the server registers, so\n * `create_record`, `read_record`, `update_record`, `delete_record` and\n * `query_records` (5 of the 8 declared `TestActionTypeSchema` members) answered\n * `HTTP Error 404: {\"error\":\"Not found\"}` against a stock boot. The suite author\n * reading that 404 has every reason to think it is their own URL.\n *\n * Replacing one literal with a corrected literal only moves the drift: the\n * server composes this path out of two declared pieces, and both of them are\n * configurable. So this asks the SAME schemas the server's own resolution asks:\n *\n * - `RestApiConfigSchema` → `apiPath ?? `${basePath}/${version}`` — the exact\n * expression `RestServer.getApiBasePath()` evaluates (`/api` + `v1`);\n * - `CrudEndpointsConfigSchema.dataPrefix` — what `RestServer` appends to it\n * to get `dataPath` (`/data`).\n *\n * Defaults only: this adapter is handed an origin, not a deployment's config,\n * so a host that overrides `api.apiPath` or `crud.dataPrefix` is still out of\n * reach here (tracked separately — the `api_call` action type is the escape\n * hatch until then). What the derivation buys is that the DEFAULT can never\n * again disagree with the schema that declares it.\n */\nfunction defaultDataPath(): string {\n if (dataPathCache === undefined) {\n const api = RestApiConfigSchema.parse({});\n const crud = CrudEndpointsConfigSchema.parse({});\n dataPathCache = `${api.apiPath ?? `${api.basePath}/${api.version}`}${crud.dataPrefix}`;\n }\n return dataPathCache;\n}\n\nexport class HttpTestAdapter implements TestExecutionAdapter {\n constructor(private baseUrl: string, private authToken?: string) {}\n\n /** `{baseUrl}{apiBasePath}{dataPrefix}/{object}` — the collection URL. */\n private collectionUrl(objectName: string): string {\n return `${this.baseUrl}${defaultDataPath()}/${encodeURIComponent(objectName)}`;\n }\n\n /** `{collection}/{id}` — the single-record URL. */\n private recordUrl(objectName: string, id: unknown): string {\n return `${this.collectionUrl(objectName)}/${encodeURIComponent(String(id))}`;\n }\n\n async execute(action: QA.TestAction, _context: Record<string, unknown>): Promise<unknown> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.authToken) {\n headers['Authorization'] = `Bearer ${this.authToken}`;\n }\n // If action.user is specified, maybe add a specific header for impersonation if supported?\n if (action.user) {\n headers['X-Run-As'] = action.user;\n }\n\n switch (action.type) {\n case 'create_record':\n return this.createRecord(action.target, action.payload || {}, headers);\n case 'update_record':\n return this.updateRecord(action.target, action.payload || {}, headers);\n case 'delete_record':\n return this.deleteRecord(action.target, action.payload || {}, headers);\n case 'read_record':\n return this.readRecord(action.target, action.payload || {}, headers);\n case 'query_records':\n return this.queryRecords(action.target, action.payload || {}, headers);\n case 'api_call':\n return this.rawApiCall(action.target, action.payload || {}, headers);\n case 'wait':\n const ms = Number(action.payload?.duration || 1000);\n return new Promise(resolve => setTimeout(() => resolve({ waited: ms }), ms));\n default:\n throw new Error(`Unsupported action type in HttpAdapter: ${action.type}`);\n }\n }\n\n private async createRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const response = await fetch(this.collectionUrl(objectName), {\n method: 'POST',\n headers,\n body: JSON.stringify(data)\n });\n return this.handleResponse(response);\n }\n\n private async updateRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const { id, ...fields } = data;\n if (!id) throw new Error('Update record requires id in payload');\n // PATCH, not PUT: `PATCH {apiPath}/data/:object/:id` is the route the server\n // registers, and there is no PUT sibling — the old verb 404'd even once the\n // path was right (#7848). The body is the field patch, so `id` is peeled off\n // rather than posted back as a column write.\n const response = await fetch(this.recordUrl(objectName, id), {\n method: 'PATCH',\n headers,\n body: JSON.stringify(fields)\n });\n return this.handleResponse(response);\n }\n\n private async deleteRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const id = data.id;\n if (!id) throw new Error('Delete record requires id in payload');\n const response = await fetch(this.recordUrl(objectName, id), {\n method: 'DELETE',\n headers\n });\n return this.handleResponse(response);\n }\n\n private async readRecord(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const id = data.id;\n if (!id) throw new Error('Read record requires id in payload');\n const response = await fetch(this.recordUrl(objectName, id), {\n method: 'GET',\n headers\n });\n return this.handleResponse(response);\n }\n\n private async queryRecords(objectName: string, data: Record<string, unknown>, headers: Record<string, string>) {\n // `POST {apiPath}/data/:object/query` — the spec-shape advanced query\n // (QueryAST in the body), the same route `client.data.query()` posts to.\n const response = await fetch(`${this.collectionUrl(objectName)}/query`, {\n method: 'POST',\n headers,\n body: JSON.stringify(data)\n });\n return this.handleResponse(response);\n }\n\n private async rawApiCall(endpoint: string, data: Record<string, unknown>, headers: Record<string, string>) {\n const method = (data.method as string) || 'GET';\n const body = data.body ? JSON.stringify(data.body) : undefined;\n const url = endpoint.startsWith('http') ? endpoint : `${this.baseUrl}${endpoint}`;\n \n const response = await fetch(url, {\n method,\n headers,\n body\n });\n return this.handleResponse(response);\n }\n\n private async handleResponse(response: Response) {\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`HTTP Error ${response.status}: ${text}`);\n }\n const contentType = response.headers.get('content-type');\n if (contentType && contentType.includes('application/json')) {\n return response.json();\n }\n return response.text();\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { PluginMetadata } from '../plugin-loader.js';\n\n// Conditionally import crypto for Node.js environments\nlet cryptoModule: typeof import('crypto') | null = null;\n\n\n/**\n * Plugin Signature Configuration\n * Controls how plugin signatures are verified\n */\nexport interface PluginSignatureConfig {\n /**\n * Map of publisher IDs to their trusted public keys\n * Format: { 'com.objectstack': '-----BEGIN PUBLIC KEY-----...' }\n */\n trustedPublicKeys: Map<string, string>;\n \n /**\n * Signature algorithm to use\n * - RS256: RSA with SHA-256\n * - ES256: ECDSA with SHA-256\n */\n algorithm: 'RS256' | 'ES256';\n \n /**\n * Strict mode: reject plugins without signatures\n * - true: All plugins must be signed\n * - false: Unsigned plugins are allowed with warning\n */\n strictMode: boolean;\n \n /**\n * Allow self-signed plugins in development\n */\n allowSelfSigned?: boolean;\n}\n\n/**\n * Plugin Signature Verification Result\n */\nexport interface SignatureVerificationResult {\n verified: boolean;\n error?: string;\n publisherId?: string;\n algorithm?: string;\n signedAt?: Date;\n}\n\n/**\n * Plugin Signature Verifier\n * \n * Implements cryptographic verification of plugin signatures to ensure:\n * 1. Plugin integrity - code hasn't been tampered with\n * 2. Publisher authenticity - plugin comes from trusted source\n * 3. Non-repudiation - publisher cannot deny signing\n * \n * Architecture:\n * - Uses Node.js crypto module for signature verification\n * - Supports RSA (RS256) and ECDSA (ES256) algorithms\n * - Verifies against trusted public key registry\n * - Computes hash of plugin code for integrity check\n * \n * Security Model:\n * - Public keys are pre-registered and trusted\n * - Plugin signature is verified before loading\n * - Strict mode rejects unsigned plugins\n * - Development mode allows self-signed plugins\n */\nexport class PluginSignatureVerifier {\n private config: PluginSignatureConfig;\n private logger: Logger;\n \n constructor(config: PluginSignatureConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n \n this.validateConfig();\n }\n \n /**\n * Verify plugin signature\n * \n * @param plugin - Plugin metadata with signature\n * @returns Verification result\n * @throws Error if verification fails in strict mode\n */\n async verifyPluginSignature(plugin: PluginMetadata): Promise<SignatureVerificationResult> {\n // Handle unsigned plugins\n if (!plugin.signature) {\n return this.handleUnsignedPlugin(plugin);\n }\n \n try {\n // 1. Extract publisher ID from plugin name (reverse domain notation)\n const publisherId = this.extractPublisherId(plugin.name);\n \n // 2. Get trusted public key for publisher\n const publicKey = this.config.trustedPublicKeys.get(publisherId);\n if (!publicKey) {\n const error = `No trusted public key for publisher: ${publisherId}`;\n this.logger.warn(error, { plugin: plugin.name, publisherId });\n \n if (this.config.strictMode && !this.config.allowSelfSigned) {\n throw new Error(error);\n }\n \n return {\n verified: false,\n error,\n publisherId,\n };\n }\n \n // 3. Compute plugin code hash\n const pluginHash = this.computePluginHash(plugin);\n \n // 4. Verify signature using crypto module\n const isValid = await this.verifyCryptoSignature(\n pluginHash,\n plugin.signature,\n publicKey\n );\n \n if (!isValid) {\n const error = `Signature verification failed for plugin: ${plugin.name}`;\n this.logger.error(error, undefined, { plugin: plugin.name, publisherId });\n throw new Error(error);\n }\n \n this.logger.info(`✅ Plugin signature verified: ${plugin.name}`, {\n plugin: plugin.name,\n publisherId,\n algorithm: this.config.algorithm,\n });\n \n return {\n verified: true,\n publisherId,\n algorithm: this.config.algorithm,\n };\n \n } catch (error) {\n this.logger.error(`Signature verification error: ${plugin.name}`, error as Error);\n \n if (this.config.strictMode) {\n throw error;\n }\n \n return {\n verified: false,\n error: (error as Error).message,\n };\n }\n }\n \n /**\n * Register a trusted public key for a publisher\n */\n registerPublicKey(publisherId: string, publicKey: string): void {\n this.config.trustedPublicKeys.set(publisherId, publicKey);\n this.logger.info(`Trusted public key registered for: ${publisherId}`);\n }\n \n /**\n * Remove a trusted public key\n */\n revokePublicKey(publisherId: string): void {\n this.config.trustedPublicKeys.delete(publisherId);\n this.logger.warn(`Public key revoked for: ${publisherId}`);\n }\n \n /**\n * Get list of trusted publishers\n */\n getTrustedPublishers(): string[] {\n return Array.from(this.config.trustedPublicKeys.keys());\n }\n \n // Private methods\n \n private handleUnsignedPlugin(plugin: PluginMetadata): SignatureVerificationResult {\n if (this.config.strictMode) {\n const error = `Plugin missing signature (strict mode): ${plugin.name}`;\n this.logger.error(error, undefined, { plugin: plugin.name });\n throw new Error(error);\n }\n \n this.logger.warn(`⚠️ Plugin not signed: ${plugin.name}`, {\n plugin: plugin.name,\n recommendation: 'Consider signing plugins for production environments',\n });\n \n return {\n verified: false,\n error: 'Plugin not signed',\n };\n }\n \n private extractPublisherId(pluginName: string): string {\n // Extract publisher from reverse domain notation\n // Example: \"com.objectstack.engine.objectql\" -> \"com.objectstack\"\n const parts = pluginName.split('.');\n \n if (parts.length < 2) {\n throw new Error(`Invalid plugin name format: ${pluginName} (expected reverse domain notation)`);\n }\n \n // Return first two parts (domain reversed)\n return `${parts[0]}.${parts[1]}`;\n }\n \n private computePluginHash(plugin: PluginMetadata): string {\n // In browser environment, use SubtleCrypto\n if (typeof (globalThis as any).window !== 'undefined') {\n return this.computePluginHashBrowser(plugin);\n }\n \n // In Node.js environment, use crypto module\n return this.computePluginHashNode(plugin);\n }\n \n private computePluginHashNode(plugin: PluginMetadata): string {\n // Use pre-loaded crypto module\n if (!cryptoModule) {\n this.logger.warn('crypto module not available, using fallback hash');\n return this.computePluginHashFallback(plugin);\n }\n \n // Compute hash of plugin code\n const pluginCode = this.serializePluginCode(plugin);\n return cryptoModule.createHash('sha256').update(pluginCode).digest('hex');\n }\n \n private computePluginHashBrowser(plugin: PluginMetadata): string {\n // Browser environment - use simple hash for now\n // In production, should use SubtleCrypto for proper cryptographic hash\n this.logger.debug('Using browser hash (SubtleCrypto integration pending)');\n return this.computePluginHashFallback(plugin);\n }\n \n private computePluginHashFallback(plugin: PluginMetadata): string {\n // Simple hash fallback (not cryptographically secure)\n const pluginCode = this.serializePluginCode(plugin);\n let hash = 0;\n \n for (let i = 0; i < pluginCode.length; i++) {\n const char = pluginCode.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n \n return hash.toString(16);\n }\n \n private serializePluginCode(plugin: PluginMetadata): string {\n // Serialize plugin code for hashing\n // Include init, start, destroy functions\n const parts: string[] = [\n plugin.name,\n plugin.version,\n plugin.init.toString(),\n ];\n \n if (plugin.start) {\n parts.push(plugin.start.toString());\n }\n \n if (plugin.destroy) {\n parts.push(plugin.destroy.toString());\n }\n \n return parts.join('|');\n }\n \n private async verifyCryptoSignature(\n data: string,\n signature: string,\n publicKey: string\n ): Promise<boolean> {\n // In browser environment, use SubtleCrypto\n if (typeof (globalThis as any).window !== 'undefined') {\n return this.verifyCryptoSignatureBrowser(data, signature, publicKey);\n }\n \n // In Node.js environment, use crypto module\n return this.verifyCryptoSignatureNode(data, signature, publicKey);\n }\n \n private async verifyCryptoSignatureNode(\n data: string,\n signature: string,\n publicKey: string\n ): Promise<boolean> {\n if (!cryptoModule) {\n try {\n // @ts-ignore\n cryptoModule = await import('crypto');\n } catch (e) {\n // ignore\n }\n }\n\n if (!cryptoModule) {\n this.logger.error('Crypto module not available for signature verification');\n return false;\n }\n \n try {\n // Create verify object based on algorithm\n if (this.config.algorithm === 'ES256') {\n // ECDSA verification - requires lowercase 'sha256'\n const verify = cryptoModule.createVerify('sha256');\n verify.update(data);\n return verify.verify(\n {\n key: publicKey,\n format: 'pem',\n type: 'spki',\n },\n signature,\n 'base64'\n );\n } else {\n // RSA verification (RS256)\n const verify = cryptoModule.createVerify('RSA-SHA256');\n verify.update(data);\n return verify.verify(publicKey, signature, 'base64');\n }\n } catch (error) {\n this.logger.error('Signature verification failed', error as Error);\n return false;\n }\n }\n \n private async verifyCryptoSignatureBrowser(\n data: string,\n signature: string,\n publicKey: string\n ): Promise<boolean> {\n try {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n this.logger.error('SubtleCrypto not available in this environment');\n return false;\n }\n\n // Decode PEM public key to raw DER bytes\n const pemBody = publicKey\n .replace(/-----BEGIN PUBLIC KEY-----/, '')\n .replace(/-----END PUBLIC KEY-----/, '')\n .replace(/\\s/g, '');\n const keyBytes = Uint8Array.from(atob(pemBody), c => c.charCodeAt(0));\n\n // Configure algorithms based on RS256 or ES256\n let importAlgorithm: { name: string; hash?: string; namedCurve?: string };\n let verifyAlgorithm: { name: string; hash?: string };\n\n if (this.config.algorithm === 'ES256') {\n importAlgorithm = { name: 'ECDSA', namedCurve: 'P-256' };\n verifyAlgorithm = { name: 'ECDSA', hash: 'SHA-256' };\n } else {\n importAlgorithm = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };\n verifyAlgorithm = { name: 'RSASSA-PKCS1-v1_5' };\n }\n\n const cryptoKey = await subtle.importKey(\n 'spki',\n keyBytes,\n importAlgorithm,\n false,\n ['verify']\n );\n\n // Decode base64 signature to ArrayBuffer\n const signatureBytes = Uint8Array.from(atob(signature), c => c.charCodeAt(0));\n\n // Encode data to ArrayBuffer\n const dataBytes = new TextEncoder().encode(data);\n\n return await subtle.verify(verifyAlgorithm, cryptoKey, signatureBytes, dataBytes);\n } catch (error) {\n this.logger.error('Browser signature verification failed', error as Error);\n return false;\n }\n }\n \n private validateConfig(): void {\n if (!this.config.trustedPublicKeys || this.config.trustedPublicKeys.size === 0) {\n this.logger.warn('No trusted public keys configured - all signatures will fail');\n }\n \n if (!this.config.algorithm) {\n throw new Error('Signature algorithm must be specified');\n }\n \n if (!['RS256', 'ES256'].includes(this.config.algorithm)) {\n throw new Error(`Unsupported algorithm: ${this.config.algorithm}`);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { PluginCapability, PluginPermissions as GrantedPermissions } from '@objectstack/spec/kernel';\nimport type { PluginContext } from '../types.js';\n\n/**\n * Plugin Permissions\n * Defines what actions a plugin is allowed to perform\n */\nexport interface PluginPermissions {\n canAccessService(serviceName: string): boolean;\n canTriggerHook(hookName: string): boolean;\n canReadFile(path: string): boolean;\n canWriteFile(path: string): boolean;\n canNetworkRequest(url: string): boolean;\n}\n\n/**\n * Permission Check Result\n */\nexport interface PermissionCheckResult {\n allowed: boolean;\n reason?: string;\n capability?: string;\n}\n\n/**\n * Plugin Permission Enforcer\n * \n * Implements capability-based security model to enforce:\n * 1. Service access control - which services a plugin can use\n * 2. Hook restrictions - which hooks a plugin can trigger\n * 3. File system permissions - what files a plugin can read/write\n * 4. Network permissions - what URLs a plugin can access\n * \n * Architecture:\n * - Uses capability declarations from plugin manifest\n * - Checks permissions before allowing operations\n * - Logs all permission denials for security audit\n * - Supports allowlist and denylist patterns\n * \n * Security Model:\n * - Principle of least privilege - plugins get minimal permissions\n * - Explicit declaration - all capabilities must be declared\n * - Runtime enforcement - checks happen at operation time\n * - Audit trail - all denials are logged\n * \n * Usage:\n * ```typescript\n * const enforcer = new PluginPermissionEnforcer(logger);\n * enforcer.registerPluginPermissions(pluginName, capabilities);\n * enforcer.enforceServiceAccess(pluginName, 'database');\n * ```\n */\nexport class PluginPermissionEnforcer {\n private logger: Logger;\n private permissionRegistry: Map<string, PluginPermissions> = new Map();\n private capabilityRegistry: Map<string, PluginCapability[]> = new Map();\n \n constructor(logger: Logger) {\n this.logger = logger;\n }\n \n /**\n * Register plugin capabilities and build permission set\n * \n * @param pluginName - Plugin identifier\n * @param capabilities - Array of capability declarations\n */\n registerPluginPermissions(pluginName: string, capabilities: PluginCapability[]): void {\n this.capabilityRegistry.set(pluginName, capabilities);\n \n const permissions: PluginPermissions = {\n canAccessService: (service) => this.checkServiceAccess(capabilities, service),\n canTriggerHook: (hook) => this.checkHookAccess(capabilities, hook),\n canReadFile: (path) => this.checkFileRead(capabilities, path),\n canWriteFile: (path) => this.checkFileWrite(capabilities, path),\n canNetworkRequest: (url) => this.checkNetworkAccess(capabilities, url),\n };\n \n this.permissionRegistry.set(pluginName, permissions);\n \n this.logger.info(`Permissions registered for plugin: ${pluginName}`, {\n plugin: pluginName,\n capabilityCount: capabilities.length,\n });\n }\n \n /**\n * Register the install-time GRANTED permission set for a plugin\n * (ADR-0025 F4). This is the structured `{ services, hooks, network, fs }`\n * grant that the cloud control plane persists to\n * `sys_package_installation.granted_permissions` after the user consents\n * at install (ADR §3.5 step 2). The runtime calls this when materializing\n * a third-party plugin so {@link SecurePluginContext} enforces exactly the\n * consented surface — independent of whatever the manifest *requested*.\n *\n * Prefer this over {@link registerPluginPermissions} for distributed\n * plugins: it enforces what was granted, not what was declared.\n */\n registerGrantedPermissions(pluginName: string, granted: GrantedPermissions | null | undefined): void {\n this.permissionRegistry.set(pluginName, buildPermissionsFromGrants(granted));\n this.logger.info(`Granted permissions registered for plugin: ${pluginName}`, {\n plugin: pluginName,\n services: granted?.services?.length ?? 0,\n hooks: granted?.hooks?.length ?? 0,\n network: granted?.network?.length ?? 0,\n fs: granted?.fs?.length ?? 0,\n });\n }\n\n /**\n * Enforce service access permission\n *\n * @param pluginName - Plugin requesting access\n * @param serviceName - Service to access\n * @throws Error if permission denied\n */\n enforceServiceAccess(pluginName: string, serviceName: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canAccessService(serviceName));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot access service ${serviceName}`;\n this.logger.warn(error, {\n plugin: pluginName,\n service: serviceName,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`Service access granted: ${pluginName} -> ${serviceName}`);\n }\n \n /**\n * Enforce hook trigger permission\n * \n * @param pluginName - Plugin requesting access\n * @param hookName - Hook to trigger\n * @throws Error if permission denied\n */\n enforceHookTrigger(pluginName: string, hookName: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canTriggerHook(hookName));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot trigger hook ${hookName}`;\n this.logger.warn(error, {\n plugin: pluginName,\n hook: hookName,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`Hook trigger granted: ${pluginName} -> ${hookName}`);\n }\n \n /**\n * Enforce file read permission\n * \n * @param pluginName - Plugin requesting access\n * @param path - File path to read\n * @throws Error if permission denied\n */\n enforceFileRead(pluginName: string, path: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canReadFile(path));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot read file ${path}`;\n this.logger.warn(error, {\n plugin: pluginName,\n path,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`File read granted: ${pluginName} -> ${path}`);\n }\n \n /**\n * Enforce file write permission\n * \n * @param pluginName - Plugin requesting access\n * @param path - File path to write\n * @throws Error if permission denied\n */\n enforceFileWrite(pluginName: string, path: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canWriteFile(path));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot write file ${path}`;\n this.logger.warn(error, {\n plugin: pluginName,\n path,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`File write granted: ${pluginName} -> ${path}`);\n }\n \n /**\n * Enforce network request permission\n * \n * @param pluginName - Plugin requesting access\n * @param url - URL to access\n * @throws Error if permission denied\n */\n enforceNetworkRequest(pluginName: string, url: string): void {\n const result = this.checkPermission(pluginName, (perms) => perms.canNetworkRequest(url));\n \n if (!result.allowed) {\n const error = `Permission denied: Plugin ${pluginName} cannot access URL ${url}`;\n this.logger.warn(error, {\n plugin: pluginName,\n url,\n reason: result.reason,\n });\n throw new Error(error);\n }\n \n this.logger.debug(`Network request granted: ${pluginName} -> ${url}`);\n }\n \n /**\n * Get plugin capabilities\n * \n * @param pluginName - Plugin identifier\n * @returns Array of capabilities or undefined\n */\n getPluginCapabilities(pluginName: string): PluginCapability[] | undefined {\n return this.capabilityRegistry.get(pluginName);\n }\n \n /**\n * Get plugin permissions\n * \n * @param pluginName - Plugin identifier\n * @returns Permissions object or undefined\n */\n getPluginPermissions(pluginName: string): PluginPermissions | undefined {\n return this.permissionRegistry.get(pluginName);\n }\n \n /**\n * Revoke all permissions for a plugin\n * \n * @param pluginName - Plugin identifier\n */\n revokePermissions(pluginName: string): void {\n this.permissionRegistry.delete(pluginName);\n this.capabilityRegistry.delete(pluginName);\n this.logger.warn(`Permissions revoked for plugin: ${pluginName}`);\n }\n \n // Private methods\n \n private checkPermission(\n pluginName: string,\n check: (perms: PluginPermissions) => boolean\n ): PermissionCheckResult {\n const permissions = this.permissionRegistry.get(pluginName);\n \n if (!permissions) {\n return {\n allowed: false,\n reason: 'Plugin permissions not registered',\n };\n }\n \n const allowed = check(permissions);\n \n return {\n allowed,\n reason: allowed ? undefined : 'No matching capability found',\n };\n }\n \n private checkServiceAccess(capabilities: PluginCapability[], serviceName: string): boolean {\n // Check if plugin has capability to access this service\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for wildcard service access\n if (protocolId.includes('protocol.service.all')) {\n return true;\n }\n \n // Check for specific service protocol\n if (protocolId.includes(`protocol.service.${serviceName}`)) {\n return true;\n }\n \n // Check for service category match\n const serviceCategory = serviceName.split('.')[0];\n if (protocolId.includes(`protocol.service.${serviceCategory}`)) {\n return true;\n }\n \n return false;\n });\n }\n \n private checkHookAccess(capabilities: PluginCapability[], hookName: string): boolean {\n // Check if plugin has capability to trigger this hook\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for wildcard hook access\n if (protocolId.includes('protocol.hook.all')) {\n return true;\n }\n \n // Check for specific hook protocol\n if (protocolId.includes(`protocol.hook.${hookName}`)) {\n return true;\n }\n \n // Check for hook category match\n const hookCategory = hookName.split(':')[0];\n if (protocolId.includes(`protocol.hook.${hookCategory}`)) {\n return true;\n }\n \n return false;\n });\n }\n \n private matchGlob(pattern: string, str: string): boolean {\n const regexStr = pattern\n .split('**')\n .map(segment => {\n const escaped = segment.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return escaped.replace(/\\*/g, '[^/]*');\n })\n .join('.*');\n return new RegExp(`^${regexStr}$`).test(str);\n }\n \n private checkFileRead(capabilities: PluginCapability[], path: string): boolean {\n // Check if plugin has capability to read this file\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for file read capability\n if (protocolId.includes('protocol.filesystem.read')) {\n const paths = cap.metadata?.paths;\n if (!Array.isArray(paths) || paths.length === 0) {\n return true;\n }\n return paths.some(p => typeof p === 'string' && this.matchGlob(p, path));\n }\n \n return false;\n });\n }\n \n private checkFileWrite(capabilities: PluginCapability[], path: string): boolean {\n // Check if plugin has capability to write this file\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for file write capability\n if (protocolId.includes('protocol.filesystem.write')) {\n const paths = cap.metadata?.paths;\n if (!Array.isArray(paths) || paths.length === 0) {\n return true;\n }\n return paths.some(p => typeof p === 'string' && this.matchGlob(p, path));\n }\n \n return false;\n });\n }\n \n private checkNetworkAccess(capabilities: PluginCapability[], url: string): boolean {\n // Check if plugin has capability to access this URL\n return capabilities.some(cap => {\n const protocolId = cap.protocol.id;\n \n // Check for network capability\n if (protocolId.includes('protocol.network')) {\n const hosts = cap.metadata?.hosts;\n if (!Array.isArray(hosts) || hosts.length === 0) {\n return true;\n }\n return hosts.some(h => typeof h === 'string' && this.matchGlob(h, url));\n }\n \n return false;\n });\n }\n}\n\n/**\n * Secure Plugin Context\n * Wraps PluginContext with permission checks\n */\nexport class SecurePluginContext implements PluginContext {\n constructor(\n private pluginName: string,\n private permissionEnforcer: PluginPermissionEnforcer,\n private baseContext: PluginContext\n ) {}\n \n registerService(name: string, service: any): void {\n // No permission check for service registration (handled during init)\n this.baseContext.registerService(name, service);\n }\n \n getService<T>(name: string): T {\n // Check permission before accessing service\n this.permissionEnforcer.enforceServiceAccess(this.pluginName, name);\n return this.baseContext.getService<T>(name);\n }\n \n replaceService<T>(name: string, implementation: T): void {\n // Check permission before replacing service\n this.permissionEnforcer.enforceServiceAccess(this.pluginName, name);\n this.baseContext.replaceService(name, implementation);\n }\n \n getServices(): Map<string, any> {\n // Return all services (no permission check for listing)\n return this.baseContext.getServices();\n }\n \n hook(name: string, handler: (...args: any[]) => void | Promise<void>): void {\n // No permission check for registering hooks (handled during init)\n this.baseContext.hook(name, handler);\n }\n \n async trigger(name: string, ...args: any[]): Promise<void> {\n // Check permission before triggering hook\n this.permissionEnforcer.enforceHookTrigger(this.pluginName, name);\n await this.baseContext.trigger(name, ...args);\n }\n \n get logger() {\n return this.baseContext.logger;\n }\n \n getKernel() {\n return this.baseContext.getKernel();\n }\n\n registerServiceFactory(name: string, factory: (ctx: PluginContext, scopeId?: string) => any, lifecycle?: import('../plugin-loader.js').ServiceLifecycle, dependencies?: string[]): void {\n this.baseContext.registerServiceFactory(name, factory, lifecycle, dependencies);\n }\n\n getServiceScoped<T>(name: string, scopeId: string): Promise<T> {\n return this.baseContext.getServiceScoped<T>(name, scopeId);\n }\n}\n\n/**\n * Create a plugin permission enforcer\n *\n * @param logger - Logger instance\n * @returns Plugin permission enforcer\n */\nexport function createPluginPermissionEnforcer(logger: Logger): PluginPermissionEnforcer {\n return new PluginPermissionEnforcer(logger);\n}\n\n/**\n * Glob match supporting `*` (within a path segment) and `**` (across\n * segments). A bare `*` entry matches everything.\n */\nfunction grantGlobMatch(pattern: string, value: string): boolean {\n if (pattern === '*' || pattern === '**') return true;\n const regexStr = pattern\n .split('**')\n .map((segment) => segment.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '[^/]*'))\n .join('.*');\n return new RegExp(`^${regexStr}$`).test(value);\n}\n\n/** Extract the host from a URL for network-grant matching; falls back to the raw value. */\nfunction hostOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return url;\n }\n}\n\nconst inList = (list: string[] | undefined, value: string): boolean =>\n Array.isArray(list) && list.some((p) => p === value || grantGlobMatch(p, value));\n\n/**\n * Build the runtime {@link PluginPermissions} bag from a structured\n * install-time grant set (ADR-0025 §3.2 `{ services, hooks, network, fs }`).\n *\n * Matching: an entry allows when it equals the requested value, is a glob\n * that matches it, or is the wildcard `*`. Network grants match against the\n * request URL's host (or the raw URL). `fs` governs both read and write —\n * the structured grant set does not split the two. A null/empty grant set\n * denies everything (principle of least privilege).\n */\nexport function buildPermissionsFromGrants(\n granted: GrantedPermissions | null | undefined,\n): PluginPermissions {\n const services = granted?.services;\n const hooks = granted?.hooks;\n const network = granted?.network;\n const fs = granted?.fs;\n return {\n canAccessService: (name) => inList(services, name),\n canTriggerHook: (name) => inList(hooks, name),\n canReadFile: (path) => inList(fs, path),\n canWriteFile: (path) => inList(fs, path),\n canNetworkRequest: (url) =>\n inList(network, hostOf(url)) || inList(network, url),\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n PluginPermission,\n PluginPermissionSet,\n PermissionAction,\n ResourceType\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from '../logger.js';\n\n/**\n * Permission Grant\n * Represents a granted permission at runtime\n */\nexport interface PermissionGrant {\n permissionId: string;\n pluginId: string;\n grantedAt: Date;\n grantedBy?: string;\n expiresAt?: Date;\n conditions?: Record<string, any>;\n}\n\n/**\n * Permission Check Result\n */\nexport interface PermissionCheckResult {\n allowed: boolean;\n reason?: string;\n requiredPermission?: string;\n grantedPermissions?: string[];\n}\n\n/**\n * Plugin Permission Manager\n * \n * Manages fine-grained permissions for plugin security and access control\n */\nexport class PluginPermissionManager {\n private logger: ObjectLogger;\n \n // Plugin permission definitions\n private permissionSets = new Map<string, PluginPermissionSet>();\n \n // Granted permissions (pluginId -> Set of permission IDs)\n private grants = new Map<string, Set<string>>();\n \n // Permission grant details\n private grantDetails = new Map<string, PermissionGrant>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'PermissionManager' });\n }\n\n /**\n * Register permission requirements for a plugin\n */\n registerPermissions(pluginId: string, permissionSet: PluginPermissionSet): void {\n this.permissionSets.set(pluginId, permissionSet);\n \n this.logger.info('Permissions registered for plugin', { \n pluginId,\n permissionCount: permissionSet.permissions.length\n });\n }\n\n /**\n * Grant a permission to a plugin\n */\n grantPermission(\n pluginId: string,\n permissionId: string,\n grantedBy?: string,\n expiresAt?: Date\n ): void {\n // Verify permission exists in plugin's declared permissions\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n throw new Error(`No permissions registered for plugin: ${pluginId}`);\n }\n\n const permission = permissionSet.permissions.find(p => p.id === permissionId);\n if (!permission) {\n throw new Error(`Permission ${permissionId} not declared by plugin ${pluginId}`);\n }\n\n // Create grant\n if (!this.grants.has(pluginId)) {\n this.grants.set(pluginId, new Set());\n }\n this.grants.get(pluginId)!.add(permissionId);\n\n // Store grant details\n const grantKey = `${pluginId}:${permissionId}`;\n this.grantDetails.set(grantKey, {\n permissionId,\n pluginId,\n grantedAt: new Date(),\n grantedBy,\n expiresAt,\n });\n\n this.logger.info('Permission granted', { \n pluginId, \n permissionId,\n grantedBy \n });\n }\n\n /**\n * Revoke a permission from a plugin\n */\n revokePermission(pluginId: string, permissionId: string): void {\n const grants = this.grants.get(pluginId);\n if (grants) {\n grants.delete(permissionId);\n \n const grantKey = `${pluginId}:${permissionId}`;\n this.grantDetails.delete(grantKey);\n\n this.logger.info('Permission revoked', { pluginId, permissionId });\n }\n }\n\n /**\n * Grant all permissions for a plugin\n */\n grantAllPermissions(pluginId: string, grantedBy?: string): void {\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n throw new Error(`No permissions registered for plugin: ${pluginId}`);\n }\n\n for (const permission of permissionSet.permissions) {\n this.grantPermission(pluginId, permission.id, grantedBy);\n }\n\n this.logger.info('All permissions granted', { pluginId, grantedBy });\n }\n\n /**\n * Check if a plugin has a specific permission\n */\n hasPermission(pluginId: string, permissionId: string): boolean {\n const grants = this.grants.get(pluginId);\n if (!grants) {\n return false;\n }\n\n // Check if granted\n if (!grants.has(permissionId)) {\n return false;\n }\n\n // Check expiration\n const grantKey = `${pluginId}:${permissionId}`;\n const grantDetails = this.grantDetails.get(grantKey);\n if (grantDetails?.expiresAt && grantDetails.expiresAt < new Date()) {\n this.revokePermission(pluginId, permissionId);\n return false;\n }\n\n return true;\n }\n\n /**\n * Check if plugin can perform an action on a resource\n */\n checkAccess(\n pluginId: string,\n resource: ResourceType,\n action: PermissionAction,\n resourceId?: string\n ): PermissionCheckResult {\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n return {\n allowed: false,\n reason: 'No permissions registered for plugin',\n };\n }\n\n // Find matching permissions\n const matchingPermissions = permissionSet.permissions.filter(p => {\n // Check resource type\n if (p.resource !== resource) {\n return false;\n }\n\n // Check action\n if (!p.actions.includes(action)) {\n return false;\n }\n\n // Check resource filter if specified\n if (resourceId && p.filter?.resourceIds) {\n if (!p.filter.resourceIds.includes(resourceId)) {\n return false;\n }\n }\n\n return true;\n });\n\n if (matchingPermissions.length === 0) {\n return {\n allowed: false,\n reason: `No permission found for ${action} on ${resource}`,\n };\n }\n\n // Check if any matching permission is granted\n const grantedPermissions = matchingPermissions.filter(p => \n this.hasPermission(pluginId, p.id)\n );\n\n if (grantedPermissions.length === 0) {\n return {\n allowed: false,\n reason: 'Required permissions not granted',\n requiredPermission: matchingPermissions[0].id,\n };\n }\n\n return {\n allowed: true,\n grantedPermissions: grantedPermissions.map(p => p.id),\n };\n }\n\n /**\n * Get all permissions for a plugin\n */\n getPluginPermissions(pluginId: string): PluginPermission[] {\n const permissionSet = this.permissionSets.get(pluginId);\n return permissionSet?.permissions || [];\n }\n\n /**\n * Get granted permissions for a plugin\n */\n getGrantedPermissions(pluginId: string): string[] {\n const grants = this.grants.get(pluginId);\n return grants ? Array.from(grants) : [];\n }\n\n /**\n * Get required but not granted permissions\n */\n getMissingPermissions(pluginId: string): PluginPermission[] {\n const permissionSet = this.permissionSets.get(pluginId);\n if (!permissionSet) {\n return [];\n }\n\n const granted = this.grants.get(pluginId) || new Set();\n \n return permissionSet.permissions.filter(p => \n p.required && !granted.has(p.id)\n );\n }\n\n /**\n * Check if all required permissions are granted\n */\n hasAllRequiredPermissions(pluginId: string): boolean {\n return this.getMissingPermissions(pluginId).length === 0;\n }\n\n /**\n * Get permission grant details\n */\n getGrantDetails(pluginId: string, permissionId: string): PermissionGrant | undefined {\n const grantKey = `${pluginId}:${permissionId}`;\n return this.grantDetails.get(grantKey);\n }\n\n /**\n * Validate permission against scope constraints\n */\n validatePermissionScope(\n permission: PluginPermission,\n context: {\n tenantId?: string;\n userId?: string;\n resourceId?: string;\n }\n ): boolean {\n switch (permission.scope) {\n case 'global':\n return true;\n\n case 'tenant':\n return !!context.tenantId;\n\n case 'user':\n return !!context.userId;\n\n case 'resource':\n return !!context.resourceId;\n\n case 'plugin':\n return true;\n\n default:\n return false;\n }\n }\n\n /**\n * Clear all permissions for a plugin\n */\n clearPluginPermissions(pluginId: string): void {\n this.permissionSets.delete(pluginId);\n \n const grants = this.grants.get(pluginId);\n if (grants) {\n for (const permissionId of grants) {\n const grantKey = `${pluginId}:${permissionId}`;\n this.grantDetails.delete(grantKey);\n }\n this.grants.delete(pluginId);\n }\n\n this.logger.info('All permissions cleared', { pluginId });\n }\n\n /**\n * Shutdown permission manager\n */\n shutdown(): void {\n this.permissionSets.clear();\n this.grants.clear();\n this.grantDetails.clear();\n \n this.logger.info('Permission manager shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport nodePath from 'node:path';\n\nimport type { \n SandboxConfig\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from '../logger.js';\nimport { getMemoryUsage } from '../utils/env.js';\n\n/**\n * Resource Usage Statistics\n */\nexport interface ResourceUsage {\n memory: {\n current: number;\n peak: number;\n limit?: number;\n };\n cpu: {\n current: number;\n average: number;\n limit?: number;\n };\n connections: {\n current: number;\n limit?: number;\n };\n}\n\n/**\n * Sandbox Execution Context\n * Represents an isolated execution environment for a plugin\n */\nexport interface SandboxContext {\n pluginId: string;\n config: SandboxConfig;\n startTime: Date;\n resourceUsage: ResourceUsage;\n}\n\n/**\n * Plugin Sandbox Runtime\n * \n * Provides isolated execution environments for plugins with resource limits\n * and access controls\n */\nexport class PluginSandboxRuntime {\n private static readonly MONITORING_INTERVAL_MS = 5000;\n\n private logger: ObjectLogger;\n \n // Active sandboxes (pluginId -> context)\n private sandboxes = new Map<string, SandboxContext>();\n \n // Resource monitoring intervals\n private monitoringIntervals = new Map<string, NodeJS.Timeout>();\n\n // Per-plugin resource baselines for delta tracking\n private memoryBaselines = new Map<string, number>();\n private cpuBaselines = new Map<string, { user: number; system: number }>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'SandboxRuntime' });\n }\n\n /**\n * Create a sandbox for a plugin\n */\n createSandbox(pluginId: string, config: SandboxConfig): SandboxContext {\n if (this.sandboxes.has(pluginId)) {\n throw new Error(`Sandbox already exists for plugin: ${pluginId}`);\n }\n\n const context: SandboxContext = {\n pluginId,\n config,\n startTime: new Date(),\n resourceUsage: {\n memory: { current: 0, peak: 0, limit: config.memory?.maxHeap },\n cpu: { current: 0, average: 0, limit: config.cpu?.maxCpuPercent },\n connections: { current: 0, limit: config.network?.maxConnections },\n },\n };\n\n this.sandboxes.set(pluginId, context);\n\n // Capture resource baselines for per-plugin delta tracking\n const baselineMemory = getMemoryUsage();\n this.memoryBaselines.set(pluginId, baselineMemory.heapUsed);\n this.cpuBaselines.set(pluginId, process.cpuUsage());\n\n // Start resource monitoring\n this.startResourceMonitoring(pluginId);\n\n this.logger.info('Sandbox created', { \n pluginId,\n level: config.level,\n memoryLimit: config.memory?.maxHeap,\n cpuLimit: config.cpu?.maxCpuPercent\n });\n\n return context;\n }\n\n /**\n * Destroy a sandbox\n */\n destroySandbox(pluginId: string): void {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return;\n }\n\n // Stop monitoring\n this.stopResourceMonitoring(pluginId);\n\n this.memoryBaselines.delete(pluginId);\n this.cpuBaselines.delete(pluginId);\n this.sandboxes.delete(pluginId);\n\n this.logger.info('Sandbox destroyed', { pluginId });\n }\n\n /**\n * Check if resource access is allowed\n */\n checkResourceAccess(\n pluginId: string,\n resourceType: 'file' | 'network' | 'process' | 'env',\n resourcePath?: string\n ): { allowed: boolean; reason?: string } {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return { allowed: false, reason: 'Sandbox not found' };\n }\n\n const { config } = context;\n\n switch (resourceType) {\n case 'file':\n return this.checkFileAccess(config, resourcePath);\n \n case 'network':\n return this.checkNetworkAccess(config, resourcePath);\n \n case 'process':\n return this.checkProcessAccess(config);\n \n case 'env':\n return this.checkEnvAccess(config, resourcePath);\n \n default:\n return { allowed: false, reason: 'Unknown resource type' };\n }\n }\n\n /**\n * Check file system access\n * Uses path.resolve() and path.normalize() to prevent directory traversal.\n */\n private checkFileAccess(\n config: SandboxConfig,\n filePath?: string\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.filesystem) {\n return { allowed: false, reason: 'File system access not configured' };\n }\n\n // If no path specified, check general access\n if (!filePath) {\n return { allowed: config.filesystem.mode !== 'none' };\n }\n\n // Check allowed paths using proper path resolution to prevent directory traversal\n const allowedPaths = config.filesystem.allowedPaths || [];\n const resolvedPath = nodePath.normalize(nodePath.resolve(filePath));\n const isAllowed = allowedPaths.some(allowed => {\n const resolvedAllowed = nodePath.normalize(nodePath.resolve(allowed));\n return resolvedPath.startsWith(resolvedAllowed);\n });\n\n if (allowedPaths.length > 0 && !isAllowed) {\n return { \n allowed: false, \n reason: `Path not in allowed list: ${filePath}` \n };\n }\n\n // Check denied paths using proper path resolution\n const deniedPaths = config.filesystem.deniedPaths || [];\n const isDenied = deniedPaths.some(denied => {\n const resolvedDenied = nodePath.normalize(nodePath.resolve(denied));\n return resolvedPath.startsWith(resolvedDenied);\n });\n\n if (isDenied) {\n return { \n allowed: false, \n reason: `Path is explicitly denied: ${filePath}` \n };\n }\n\n return { allowed: true };\n }\n\n /**\n * Check network access\n * Uses URL parsing to properly validate hostnames.\n */\n private checkNetworkAccess(\n config: SandboxConfig,\n url?: string\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.network) {\n return { allowed: false, reason: 'Network access not configured' };\n }\n\n // Check if network access is enabled\n if (config.network.mode === 'none') {\n return { allowed: false, reason: 'Network access disabled' };\n }\n\n // If no URL specified, check general access\n if (!url) {\n return { allowed: (config.network.mode as string) !== 'none' };\n }\n\n // Parse URL and check hostname against allowed/denied hosts\n let parsedHostname: string;\n try {\n parsedHostname = new URL(url).hostname;\n } catch {\n return { allowed: false, reason: `Invalid URL: ${url}` };\n }\n\n // Check allowed hosts\n const allowedHosts = config.network.allowedHosts || [];\n if (allowedHosts.length > 0) {\n const isAllowed = allowedHosts.some(host => {\n return parsedHostname === host;\n });\n\n if (!isAllowed) {\n return { \n allowed: false, \n reason: `Host not in allowed list: ${url}` \n };\n }\n }\n\n // Check denied hosts\n const deniedHosts = config.network.deniedHosts || [];\n const isDenied = deniedHosts.some(host => {\n return parsedHostname === host;\n });\n\n if (isDenied) {\n return { \n allowed: false, \n reason: `Host is blocked: ${url}` \n };\n }\n\n return { allowed: true };\n }\n\n /**\n * Check process spawning access\n */\n private checkProcessAccess(\n config: SandboxConfig\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.process) {\n return { allowed: false, reason: 'Process access not configured' };\n }\n\n if (!config.process.allowSpawn) {\n return { allowed: false, reason: 'Process spawning not allowed' };\n }\n\n return { allowed: true };\n }\n\n /**\n * Check environment variable access\n */\n private checkEnvAccess(\n config: SandboxConfig,\n varName?: string\n ): { allowed: boolean; reason?: string } {\n if (config.level === 'none') {\n return { allowed: true };\n }\n\n if (!config.process) {\n return { allowed: false, reason: 'Environment access not configured' };\n }\n\n // If no variable specified, check general access\n if (!varName) {\n return { allowed: true };\n }\n\n // For now, allow all env access if process is configured\n // In a real implementation, would check specific allowed vars\n return { allowed: true };\n }\n\n /**\n * Check resource limits\n */\n checkResourceLimits(pluginId: string): { \n withinLimits: boolean; \n violations: string[] \n } {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return { withinLimits: true, violations: [] };\n }\n\n const violations: string[] = [];\n const { resourceUsage, config } = context;\n\n // Check memory limit\n if (config.memory?.maxHeap && \n resourceUsage.memory.current > config.memory.maxHeap) {\n violations.push(`Memory limit exceeded: ${resourceUsage.memory.current} > ${config.memory.maxHeap}`);\n }\n\n // Check CPU limit (would need runtime config)\n if (config.runtime?.resourceLimits?.maxCpu && \n resourceUsage.cpu.current > config.runtime.resourceLimits.maxCpu) {\n violations.push(`CPU limit exceeded: ${resourceUsage.cpu.current}% > ${config.runtime.resourceLimits.maxCpu}%`);\n }\n\n // Check connection limit\n if (config.network?.maxConnections && \n resourceUsage.connections.current > config.network.maxConnections) {\n violations.push(`Connection limit exceeded: ${resourceUsage.connections.current} > ${config.network.maxConnections}`);\n }\n\n return {\n withinLimits: violations.length === 0,\n violations,\n };\n }\n\n /**\n * Get resource usage for a plugin\n */\n getResourceUsage(pluginId: string): ResourceUsage | undefined {\n const context = this.sandboxes.get(pluginId);\n return context?.resourceUsage;\n }\n\n /**\n * Start monitoring resource usage\n */\n private startResourceMonitoring(pluginId: string): void {\n // Monitor at the configured interval\n const interval = setInterval(() => {\n this.updateResourceUsage(pluginId);\n }, PluginSandboxRuntime.MONITORING_INTERVAL_MS);\n\n this.monitoringIntervals.set(pluginId, interval);\n }\n\n /**\n * Stop monitoring resource usage\n */\n private stopResourceMonitoring(pluginId: string): void {\n const interval = this.monitoringIntervals.get(pluginId);\n if (interval) {\n clearInterval(interval);\n this.monitoringIntervals.delete(pluginId);\n }\n }\n\n /**\n * Update resource usage statistics\n * \n * Tracks per-plugin memory and CPU usage using delta from baseline\n * captured at sandbox creation time. This is an approximation since\n * true per-plugin isolation isn't possible in a single Node.js process.\n */\n private updateResourceUsage(pluginId: string): void {\n const context = this.sandboxes.get(pluginId);\n if (!context) {\n return;\n }\n\n // In a real implementation, this would collect actual metrics\n // For now, this is a placeholder structure\n \n // Update memory usage using delta from baseline for per-plugin approximation\n const memoryUsage = getMemoryUsage();\n const memoryBaseline = this.memoryBaselines.get(pluginId) ?? 0;\n const memoryDelta = Math.max(0, memoryUsage.heapUsed - memoryBaseline);\n context.resourceUsage.memory.current = memoryDelta;\n context.resourceUsage.memory.peak = Math.max(\n context.resourceUsage.memory.peak,\n memoryDelta\n );\n\n // Update CPU usage using delta from baseline for per-plugin approximation\n const cpuBaseline = this.cpuBaselines.get(pluginId) ?? { user: 0, system: 0 };\n const cpuCurrent = process.cpuUsage();\n const cpuDeltaUser = cpuCurrent.user - cpuBaseline.user;\n const cpuDeltaSystem = cpuCurrent.system - cpuBaseline.system;\n // Convert microseconds to a percentage approximation over the monitoring interval\n const totalCpuMicros = cpuDeltaUser + cpuDeltaSystem;\n const intervalMicros = PluginSandboxRuntime.MONITORING_INTERVAL_MS * 1000;\n context.resourceUsage.cpu.current = (totalCpuMicros / intervalMicros) * 100;\n // Update baseline for next interval\n this.cpuBaselines.set(pluginId, cpuCurrent);\n\n // Check for violations\n const { withinLimits, violations } = this.checkResourceLimits(pluginId);\n if (!withinLimits) {\n this.logger.warn('Resource limit violations detected', { \n pluginId, \n violations \n });\n }\n }\n\n /**\n * Get all active sandboxes\n */\n getAllSandboxes(): Map<string, SandboxContext> {\n return new Map(this.sandboxes);\n }\n\n /**\n * Shutdown sandbox runtime\n */\n shutdown(): void {\n // Stop all monitoring\n for (const pluginId of this.monitoringIntervals.keys()) {\n this.stopResourceMonitoring(pluginId);\n }\n\n this.sandboxes.clear();\n this.memoryBaselines.clear();\n this.cpuBaselines.clear();\n \n this.logger.info('Sandbox runtime shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n KernelSecurityVulnerability,\n KernelSecurityScanResult\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from '../logger.js';\n\n/**\n * Scan Target\n */\nexport interface ScanTarget {\n pluginId: string;\n version: string;\n files?: string[];\n dependencies?: Record<string, string>;\n}\n\n/**\n * Security Issue\n */\nexport interface SecurityIssue {\n id: string;\n severity: 'critical' | 'high' | 'medium' | 'low' | 'info';\n category: 'vulnerability' | 'malware' | 'license' | 'code-quality' | 'configuration';\n title: string;\n description: string;\n location?: {\n file?: string;\n line?: number;\n column?: number;\n };\n remediation?: string;\n cve?: string;\n cvss?: number;\n}\n\n/**\n * Plugin Security Scanner\n * \n * Scans plugins for security vulnerabilities, malware, and license issues\n */\nexport class PluginSecurityScanner {\n private logger: ObjectLogger;\n \n // Known vulnerabilities database (CVE cache)\n private vulnerabilityDb = new Map<string, KernelSecurityVulnerability>();\n \n // Scan results cache\n private scanResults = new Map<string, KernelSecurityScanResult>();\n\n private passThreshold: number = 70;\n\n constructor(logger: ObjectLogger, config?: { passThreshold?: number }) {\n this.logger = logger.child({ component: 'SecurityScanner' });\n if (config?.passThreshold !== undefined) {\n this.passThreshold = config.passThreshold;\n }\n }\n\n /**\n * Perform a comprehensive security scan on a plugin\n */\n async scan(target: ScanTarget): Promise<KernelSecurityScanResult> {\n this.logger.info('Starting security scan', { \n pluginId: target.pluginId,\n version: target.version \n });\n\n const issues: SecurityIssue[] = [];\n\n try {\n // 1. Scan for code vulnerabilities\n const codeIssues = await this.scanCode(target);\n issues.push(...codeIssues);\n\n // 2. Scan dependencies for known vulnerabilities\n const depIssues = await this.scanDependencies(target);\n issues.push(...depIssues);\n\n // 3. Scan for malware patterns\n const malwareIssues = await this.scanMalware(target);\n issues.push(...malwareIssues);\n\n // 4. Check license compliance\n const licenseIssues = await this.scanLicenses(target);\n issues.push(...licenseIssues);\n\n // 5. Check configuration security\n const configIssues = await this.scanConfiguration(target);\n issues.push(...configIssues);\n\n // Calculate security score (0-100, higher is better)\n const score = this.calculateSecurityScore(issues);\n\n const result: KernelSecurityScanResult = {\n timestamp: new Date().toISOString(),\n scanner: { name: 'ObjectStack Security Scanner', version: '1.0.0' },\n status: score >= this.passThreshold ? 'passed' : 'failed',\n vulnerabilities: issues.map(issue => ({\n id: issue.id,\n severity: issue.severity,\n category: issue.category,\n title: issue.title,\n description: issue.description,\n location: issue.location ? `${issue.location.file}:${issue.location.line}` : undefined,\n remediation: issue.remediation,\n affectedVersions: [],\n exploitAvailable: false,\n patchAvailable: false,\n })),\n summary: {\n totalVulnerabilities: issues.length,\n criticalCount: issues.filter(i => i.severity === 'critical').length,\n highCount: issues.filter(i => i.severity === 'high').length,\n mediumCount: issues.filter(i => i.severity === 'medium').length,\n lowCount: issues.filter(i => i.severity === 'low').length,\n infoCount: issues.filter(i => i.severity === 'info').length,\n },\n };\n\n this.scanResults.set(`${target.pluginId}:${target.version}`, result);\n\n this.logger.info('Security scan complete', { \n pluginId: target.pluginId,\n score,\n status: result.status,\n summary: result.summary\n });\n\n return result;\n } catch (error) {\n this.logger.error('Security scan failed', { \n pluginId: target.pluginId, \n error \n });\n\n throw error;\n }\n }\n\n /**\n * Scan code for vulnerabilities\n */\n private async scanCode(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n // In a real implementation, this would:\n // - Parse code with AST (e.g., using @typescript-eslint/parser)\n // - Check for dangerous patterns (eval, Function constructor, etc.)\n // - Check for XSS vulnerabilities\n // - Check for SQL injection patterns\n // - Check for insecure crypto usage\n // - Check for path traversal vulnerabilities\n\n this.logger.debug('Code scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Scan dependencies for known vulnerabilities\n */\n private async scanDependencies(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n if (!target.dependencies) {\n return issues;\n }\n\n // In a real implementation, this would:\n // - Query npm audit API\n // - Check GitHub Advisory Database\n // - Check Snyk vulnerability database\n // - Check OSV (Open Source Vulnerabilities)\n\n for (const [depName, version] of Object.entries(target.dependencies)) {\n const vulnKey = `${depName}@${version}`;\n const vulnerability = this.vulnerabilityDb.get(vulnKey);\n\n if (vulnerability) {\n issues.push({\n id: `vuln-${vulnerability.cve || depName}`,\n severity: vulnerability.severity,\n category: 'vulnerability',\n title: `Vulnerable dependency: ${depName}`,\n description: `${depName}@${version} has known security vulnerabilities`,\n remediation: vulnerability.fixedIn \n ? `Upgrade to ${vulnerability.fixedIn.join(' or ')}`\n : 'No fix available',\n cve: vulnerability.cve,\n });\n }\n }\n\n this.logger.debug('Dependency scan complete', { \n pluginId: target.pluginId,\n dependencies: Object.keys(target.dependencies).length,\n vulnerabilities: issues.length \n });\n\n return issues;\n }\n\n /**\n * Scan for malware patterns\n */\n private async scanMalware(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n // In a real implementation, this would:\n // - Check for obfuscated code\n // - Check for suspicious network activity patterns\n // - Check for crypto mining patterns\n // - Check for data exfiltration patterns\n // - Use ML-based malware detection\n // - Check file hashes against known malware databases\n\n this.logger.debug('Malware scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Check license compliance\n */\n private async scanLicenses(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n if (!target.dependencies) {\n return issues;\n }\n\n // In a real implementation, this would:\n // - Check license compatibility\n // - Detect GPL contamination\n // - Flag proprietary dependencies\n // - Check for missing licenses\n // - Verify SPDX identifiers\n\n this.logger.debug('License scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Check configuration security\n */\n private async scanConfiguration(target: ScanTarget): Promise<SecurityIssue[]> {\n const issues: SecurityIssue[] = [];\n\n // In a real implementation, this would:\n // - Check for hardcoded secrets\n // - Check for weak permissions\n // - Check for insecure defaults\n // - Check for missing security headers\n // - Check CSP policies\n\n this.logger.debug('Configuration scan complete', { \n pluginId: target.pluginId,\n issuesFound: issues.length \n });\n\n return issues;\n }\n\n /**\n * Calculate security score based on issues\n */\n private calculateSecurityScore(issues: SecurityIssue[]): number {\n // Start with perfect score\n let score = 100;\n\n // Deduct points based on severity\n for (const issue of issues) {\n switch (issue.severity) {\n case 'critical':\n score -= 20;\n break;\n case 'high':\n score -= 10;\n break;\n case 'medium':\n score -= 5;\n break;\n case 'low':\n score -= 2;\n break;\n case 'info':\n score -= 0;\n break;\n }\n }\n\n // Ensure score doesn't go below 0\n return Math.max(0, score);\n }\n\n /**\n * Add a vulnerability to the database\n */\n addVulnerability(\n packageName: string,\n version: string,\n vulnerability: KernelSecurityVulnerability\n ): void {\n const key = `${packageName}@${version}`;\n this.vulnerabilityDb.set(key, vulnerability);\n \n this.logger.debug('Vulnerability added to database', { \n package: packageName, \n version,\n cve: vulnerability.cve \n });\n }\n\n /**\n * Get scan result from cache\n */\n getScanResult(pluginId: string, version: string): KernelSecurityScanResult | undefined {\n return this.scanResults.get(`${pluginId}:${version}`);\n }\n\n /**\n * Clear scan results cache\n */\n clearCache(): void {\n this.scanResults.clear();\n this.logger.debug('Scan results cache cleared');\n }\n\n /**\n * Update vulnerability database from external source\n */\n async updateVulnerabilityDatabase(): Promise<void> {\n this.logger.info('Updating vulnerability database');\n\n // In a real implementation, this would:\n // - Fetch from GitHub Advisory Database\n // - Fetch from npm audit\n // - Fetch from NVD (National Vulnerability Database)\n // - Parse and cache vulnerability data\n\n this.logger.info('Vulnerability database updated', { \n entries: this.vulnerabilityDb.size \n });\n }\n\n /**\n * Shutdown security scanner\n */\n shutdown(): void {\n this.vulnerabilityDb.clear();\n this.scanResults.clear();\n \n this.logger.info('Security scanner shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * api-key — hand-rolled API-key primitives + verifier for `sys_api_key`.\n *\n * better-auth 1.6.x ships no apiKey plugin, so ObjectStack owns the full\n * lifecycle: generation, at-rest hashing, header extraction, validation, and\n * the verify-time principal lookup. This is the SINGLE shared source of truth\n * used by BOTH inbound surfaces — the runtime dispatcher / MCP path\n * (`resolveExecutionContext`) and the REST data API (`@objectstack/rest`) — so\n * the two can never drift on how a key authenticates. It lives in\n * `@objectstack/core` (server-side; both `runtime` and `rest` depend on it,\n * and `core` depends on neither, so there is no cycle).\n *\n * SECURITY (zero-tolerance):\n * - The raw key is returned EXACTLY ONCE, by {@link generateApiKey}. It is\n * never persisted; only `sha256(raw)` (hex) is stored in `sys_api_key.key`.\n * - The raw key and its hash must never enter logs, HTTP responses, error\n * messages, commit messages or comments.\n * - Validation is fail-closed: anything ambiguous (missing, revoked, expired,\n * malformed) resolves to \"no principal\", never to an elevated one.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\n\n/** Default visible prefix for generated keys (helps users identify a key). */\nexport const API_KEY_PREFIX = 'osk_';\n\n/** Bytes of entropy in the secret portion of a generated key (256 bits). */\nconst API_KEY_ENTROPY_BYTES = 32;\n\n/** Length of the human-visible prefix stored in `sys_api_key.prefix`. */\nconst VISIBLE_PREFIX_LEN = 12;\n\n/**\n * Derive the at-rest hash for an API key. Inbound keys are hashed the same way\n * before the DB lookup. Because the lookup matches an indexed, high-entropy\n * hash exactly, this doubles as a constant-effort comparison: an attacker\n * cannot recover the raw key by probing for partial matches.\n */\nexport function hashApiKey(raw: string): string {\n return createHash('sha256').update(raw, 'utf8').digest('hex');\n}\n\n/** Result of {@link generateApiKey}. `raw` is shown to the user only once. */\nexport interface GeneratedApiKey {\n /** The full secret to hand to the client. NEVER persist this. */\n raw: string;\n /** `sha256(raw)` hex — store this in `sys_api_key.key`. */\n hash: string;\n /** Short non-secret prefix for display/identification (`sys_api_key.prefix`). */\n prefix: string;\n}\n\n/**\n * Generate a fresh API key. Returns the raw secret (caller must surface it to\n * the user exactly once and then discard it), its at-rest hash, and a short\n * non-secret prefix for display.\n */\nexport function generateApiKey(prefix: string = API_KEY_PREFIX): GeneratedApiKey {\n // base64url so the token is URL/header-safe with no padding.\n const secret = randomBytes(API_KEY_ENTROPY_BYTES).toString('base64url');\n const raw = `${prefix}${secret}`;\n return {\n raw,\n hash: hashApiKey(raw),\n prefix: raw.slice(0, VISIBLE_PREFIX_LEN),\n };\n}\n\n/**\n * Extract an API key from request headers. Accepts, in order:\n * - `X-API-Key: <token>`\n * - `Authorization: ApiKey <token>` (case-insensitive scheme)\n * - `Authorization: Bearer <token>` ONLY when `<token>` carries the ObjectStack\n * api-key prefix (`osk_`). Remote MCP clients (Claude Desktop / Cursor /\n * Claude Code) authenticate to `/api/v1/mcp` with the key as a Bearer per the\n * MCP spec, so rejecting Bearer outright made every standard MCP client fail.\n * A better-auth *session* token never starts with `osk_`, so a session Bearer\n * still falls through to the session path — this can't shadow it.\n */\nexport function extractApiKey(headers: any): string | undefined {\n const x = readHeader(headers, 'x-api-key');\n if (x && x.trim()) return x.trim();\n const auth = readHeader(headers, 'authorization');\n if (!auth) return undefined;\n const apiKeyScheme = auth.match(/^ApiKey\\s+(\\S.*)$/i);\n if (apiKeyScheme?.[1]?.trim()) return apiKeyScheme[1].trim();\n // Bearer is accepted only for prefixed api-keys (never for session tokens).\n const bearer = auth.match(/^Bearer\\s+(\\S.*)$/i)?.[1]?.trim();\n if (bearer && bearer.startsWith(API_KEY_PREFIX)) return bearer;\n return undefined;\n}\n\n/** Parse a `scopes` value that may be a JSON-string textarea or a real array. */\nexport function parseScopes(value: unknown): string[] {\n if (Array.isArray(value)) {\n return value.filter((s): s is string => typeof s === 'string' && s.length > 0);\n }\n if (typeof value === 'string' && value.trim()) {\n const parsed = safeJsonParse<unknown>(value, []);\n if (Array.isArray(parsed)) {\n return parsed.filter((s): s is string => typeof s === 'string' && s.length > 0);\n }\n }\n return [];\n}\n\n/** Return true when an expiry timestamp is in the past (i.e. the key is dead). */\nexport function isExpired(value: unknown, nowMs: number): boolean {\n if (value == null) return false;\n let ms: number;\n if (typeof value === 'number') {\n // Heuristic: seconds vs milliseconds epoch.\n ms = value < 1e12 ? value * 1000 : value;\n } else if (value instanceof Date) {\n ms = value.getTime();\n } else if (typeof value === 'string') {\n ms = Date.parse(value);\n } else {\n return false;\n }\n if (Number.isNaN(ms)) return false;\n return ms <= nowMs;\n}\n\n/** The principal resolved from a valid `sys_api_key`. */\nexport interface ApiKeyPrincipal {\n userId: string;\n tenantId?: string;\n scopes: string[];\n}\n\n/**\n * Verify an inbound API key against `sys_api_key` and resolve its principal.\n * This is the ONE verify path shared by the dispatcher/MCP and REST surfaces.\n *\n * Fail-closed: returns `undefined` for a missing key, an unusable data engine,\n * a lookup error, or a key that is unknown / revoked / expired / owner-less.\n *\n * @param ql A data engine with `find(object, { where, limit, context })`.\n * @param headers Request headers (Web `Headers` or a plain object).\n * @param nowMs Clock for expiry checks (injectable for tests).\n */\nexport async function resolveApiKeyPrincipal(\n ql: any,\n headers: any,\n nowMs: number = Date.now(),\n): Promise<ApiKeyPrincipal | undefined> {\n const apiKey = extractApiKey(headers);\n if (!apiKey) return undefined;\n if (!ql || typeof ql.find !== 'function') return undefined;\n\n // Match by the indexed at-rest hash only — never query by the raw key.\n let rows: any;\n try {\n rows = await ql.find('sys_api_key', {\n where: { key: hashApiKey(apiKey), revoked: false },\n limit: 1,\n context: { isSystem: true },\n });\n } catch {\n return undefined;\n }\n if (rows && (rows as any).value) rows = (rows as any).value;\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || row.revoked === true) return undefined;\n\n const expiresAt = row.expires_at ?? row.expiresAt;\n if (isExpired(expiresAt, nowMs)) return undefined;\n\n const userId = row.user_id ?? row.userId;\n if (!userId || typeof userId !== 'string') return undefined;\n\n return {\n userId,\n tenantId: row.organization_id ?? row.organizationId ?? undefined,\n scopes: parseScopes(row.scopes),\n };\n}\n\nfunction readHeader(headers: any, name: string): string | undefined {\n if (!headers) return undefined;\n const lower = name.toLowerCase();\n if (typeof headers.get === 'function') {\n const v = headers.get(name) ?? headers.get(lower);\n return v == null ? undefined : String(v);\n }\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === lower) {\n const v = headers[key];\n return Array.isArray(v) ? v[0] : v == null ? undefined : String(v);\n }\n }\n return undefined;\n}\n\nfunction safeJsonParse<T>(s: string, fallback: T): T {\n try {\n return JSON.parse(s) as T;\n } catch {\n return fallback;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * resolveAuthzContext — the SINGLE source of truth for resolving an inbound\n * request's identity + authorization context (positions, permissions, RLS scoping).\n *\n * Every HTTP entry point (REST server, runtime dispatcher, MCP, any future\n * transport) MUST resolve authorization through this function — never by\n * re-reading `sys_member` / `sys_user_position` / `sys_*_permission_set` itself.\n *\n * Why this exists: authorization resolution used to be DUPLICATED across the\n * REST server (`@objectstack/rest`) and the runtime dispatcher\n * (`@objectstack/runtime`). On a security path, duplicated logic drifts and the\n * drift is silent: the REST copy had quietly omitted `sys_user_position` (so custom\n * roles granted via the ADR-0057 D4 platform-RBAC path didn't apply over REST),\n * `sys_position_permission_set`, `mapMembershipRole` normalization, the\n * platform-admin derivation, and the `ai_seat` synthesis. The API-key half was\n * already shared here (`resolveApiKeyPrincipal`); this completes the extraction\n * by bringing session + role/permission aggregation home too. There is now ONE\n * implementation; both entry points are thin adapters that supply `ql` /\n * `getSession` their own way and delegate here.\n *\n * Fail-closed: every read is defensive. Missing services / tables yield a\n * partial context (even `{ positions: [], permissions: [] }`) — enforcement is the\n * SecurityPlugin's job, never this resolver's.\n */\n\nimport {\n mapMembershipRole,\n BUILTIN_IDENTITY_PLATFORM_ADMIN,\n ADMIN_FULL_ACCESS,\n ORGANIZATION_ADMIN_GRANTS,\n} from '@objectstack/spec';\nimport type { AuthzPosture } from '@objectstack/spec/security';\n\nimport { resolveApiKeyPrincipal } from './api-key.js';\nimport { isGrantActive } from './grant-validity.js';\nimport { derivePosture } from './posture-ladder.js';\n\n/** The transport-agnostic authorization envelope produced from a request. */\nexport interface ResolvedAuthzContext {\n userId?: string;\n tenantId?: string;\n email?: string;\n accessToken?: string;\n positions: string[];\n permissions: string[];\n systemPermissions: string[];\n tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;\n /** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */\n org_user_ids: string[];\n /**\n * [ADR-0105 D2] Every organization this principal currently holds a VALID\n * membership in — the caller's org access set, and the read reach of the\n * `group` tenancy posture (Layer 0 becomes `organization_id IN (...)`).\n * Resolved here, once, so no surface re-derives it; empty for an anonymous or\n * membership-less principal, which fails the group wall closed.\n */\n accessible_org_ids: string[];\n /**\n * [ADR-0095 D2/D3] The monotonic posture rung this principal resolves to,\n * DERIVED once here from held capability grants (never a better-auth role):\n * `PLATFORM_ADMIN` (unscoped `admin_full_access`) > `TENANT_ADMIN`\n * (`organization_admin`) > `MEMBER` (the authenticated floor). `EXTERNAL` is\n * defined/test-locked but never resolved yet (no external principal type —\n * see `posture-ladder.ts`). Present only for an authenticated principal;\n * anonymous requests carry no rung.\n */\n posture?: AuthzPosture;\n}\n\nexport interface ResolveAuthzInput {\n /** Data engine (ObjectQL) exposing `find(object, { where, limit, context })`. */\n ql: any;\n /** Inbound request headers (Web `Headers` or a plain record). */\n headers: any;\n /**\n * Resolve a better-auth session from `headers`, returning `{ user?, session? }`\n * (or undefined). Optional — when omitted or throwing, only the API-key path\n * runs and anonymous requests resolve to an empty context.\n */\n getSession?: (headers: any) => Promise<any> | any;\n /** Clock injection for API-key expiry (tests). */\n nowMs?: number;\n}\n\nfunction safeJsonParse<T>(s: string, fallback: T): T {\n try { return JSON.parse(s) as T; } catch { return fallback; }\n}\n\nasync function tryFind(ql: any, object: string, where: any, limit = 100): Promise<any[]> {\n if (!ql || typeof ql.find !== 'function') return [];\n try {\n let rows = await ql.find(object, { where, limit, context: { isSystem: true } } as any);\n if (rows && (rows as any).value) rows = (rows as any).value;\n return Array.isArray(rows) ? rows : [];\n } catch {\n return [];\n }\n}\n\n/**\n * Resolve the authorization context for an inbound request. Always resolves —\n * never throws. Anonymous requests yield `{ positions: [], permissions: [], ... }`.\n */\nexport async function resolveAuthzContext(input: ResolveAuthzInput): Promise<ResolvedAuthzContext> {\n const { ql, headers } = input;\n const ctx: ResolvedAuthzContext = {\n positions: [],\n permissions: [],\n systemPermissions: [],\n org_user_ids: [],\n accessible_org_ids: [],\n };\n\n let userId: string | undefined;\n let tenantId: string | undefined;\n\n // 1. API key (explicit opt-in via header) takes precedence over session.\n const keyPrincipal = await resolveApiKeyPrincipal(ql, headers, input.nowMs);\n if (keyPrincipal) {\n userId = keyPrincipal.userId;\n tenantId = keyPrincipal.tenantId;\n for (const scope of keyPrincipal.scopes) {\n if (!ctx.permissions.includes(scope)) ctx.permissions.push(scope);\n }\n }\n\n // 2. Session / Bearer path — fall back when no API key resolved a user.\n if (!userId && typeof input.getSession === 'function') {\n try {\n const sessionData = await input.getSession(headers);\n userId = sessionData?.user?.id ?? sessionData?.session?.userId;\n tenantId = tenantId ?? sessionData?.session?.activeOrganizationId;\n ctx.accessToken = sessionData?.session?.token ?? ctx.accessToken;\n if (sessionData?.user?.email) ctx.email = String(sessionData.user.email);\n } catch {\n // no auth configured / bad session → anonymous\n }\n }\n\n if (!userId) return ctx;\n ctx.userId = userId;\n if (tenantId) ctx.tenantId = tenantId;\n if (!ql || typeof ql.find !== 'function') return ctx;\n\n // The principal is now known — delegate ALL position/permission/RLS\n // aggregation to the shared userId-driven resolver. Seed it with the API-key\n // scopes already collected (step 1) and any session-supplied email so the\n // resulting order + email fallback are byte-identical to the logic this\n // replaced. `resolveUserAuthzGrants` is the single place that reads\n // `sys_member` / `sys_user_position` / `sys_*_permission_set`, so a non-HTTP\n // surface that already knows the user id (a `runAs:'user'` automation run,\n // #3356) can build the SAME envelope without re-implementing any of it.\n const grants = await resolveUserAuthzGrants(ql, userId, {\n tenantId,\n nowMs: input.nowMs,\n seedPermissions: ctx.permissions,\n seedEmail: ctx.email,\n });\n ctx.positions = grants.positions;\n ctx.permissions = grants.permissions;\n ctx.systemPermissions = grants.systemPermissions;\n ctx.org_user_ids = grants.org_user_ids;\n ctx.accessible_org_ids = grants.accessible_org_ids;\n if (grants.tabPermissions) ctx.tabPermissions = grants.tabPermissions;\n if (grants.posture) ctx.posture = grants.posture;\n if (grants.email && !ctx.email) ctx.email = grants.email;\n\n return ctx;\n}\n\n/** The authorization grants a KNOWN user holds — a subset of {@link ResolvedAuthzContext}. */\nexport interface UserAuthzGrants {\n positions: string[];\n permissions: string[];\n systemPermissions: string[];\n /** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */\n org_user_ids: string[];\n /** [ADR-0105 D2] Organizations this user holds a currently-valid membership in. */\n accessible_org_ids: string[];\n tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;\n posture?: AuthzPosture;\n /** The user's unique email (`sys_user`), for `current_user.email` owner RLS. */\n email?: string;\n}\n\nexport interface ResolveUserAuthzGrantsOptions {\n /** Active org/tenant id — scopes org-bound grants (a null-org row is global). */\n tenantId?: string;\n /** Clock injection for grant validity windows (tests). */\n nowMs?: number;\n /**\n * Permission names the CALLER already resolved (e.g. API-key scopes) to seed\n * `permissions` BEFORE permission-set names are appended, so a mixed\n * API-key+session principal keeps every scope and the ordering is preserved.\n * Copied, never mutated.\n */\n seedPermissions?: string[];\n /** A caller-supplied email (e.g. from the session) that wins over the `sys_user` read. */\n seedEmail?: string;\n}\n\n/**\n * resolveUserAuthzGrants — the userId-driven core of {@link resolveAuthzContext}.\n *\n * Given a KNOWN user id, aggregate the authorization grants that user holds:\n * org-admin positions (`sys_member`), platform-RBAC positions\n * (`sys_user_position`), user- and position-bound permission sets\n * (`sys_user_permission_set` / `sys_position_permission_set` →\n * `sys_permission_set`), the derived `platform_admin` built-in + posture rung,\n * fellow-org peers for identity-table RLS, and the env-side `ai_seat`.\n *\n * Factored out of `resolveAuthzContext` so a surface that already knows WHO the\n * principal is — with no HTTP request to resolve it from — can build the SAME\n * envelope through the ONE resolver, instead of re-reading `sys_member` /\n * `sys_user_position` / `sys_*_permission_set` itself. The motivating consumer\n * is a `runAs:'user'` automation run resolving the triggering user's grants\n * (#3356): the record-change hook session carries only a `userId`, so the\n * automation engine calls this to run the flow's data ops exactly as that user\n * — not the bare member/everyone fallback the missing grants used to leave it.\n *\n * Fail-closed like its parent: every read is defensive, a missing engine/table\n * yields an empty-but-valid envelope, and it never throws.\n */\nexport async function resolveUserAuthzGrants(\n ql: any,\n userId: string,\n opts: ResolveUserAuthzGrantsOptions = {},\n): Promise<UserAuthzGrants> {\n const { tenantId } = opts;\n const grants: UserAuthzGrants = {\n positions: [],\n permissions: Array.isArray(opts.seedPermissions) ? [...opts.seedPermissions] : [],\n systemPermissions: [],\n org_user_ids: [userId],\n accessible_org_ids: [],\n };\n if (opts.seedEmail) grants.email = opts.seedEmail;\n if (!ql || typeof ql.find !== 'function') return grants;\n\n // sys_user is needed for both the `current_user.email` fallback (API-key auth,\n // where the session didn't supply an email) and the ai_seat synthesis below.\n // Read the row at most once per resolution — the two reads were a duplicate\n // query on the API-key path.\n let userRowLoaded = false;\n let userRow: any;\n const getUserRow = async (): Promise<any> => {\n if (!userRowLoaded) {\n userRowLoaded = true;\n const rows = await tryFind(ql, 'sys_user', { id: userId }, 1);\n userRow = rows[0];\n }\n return userRow;\n };\n\n // Resolve the caller's unique email for `current_user.email` RLS owner\n // policies when the caller didn't supply it (e.g. API-key auth).\n if (!grants.email) {\n const u = await getUserRow();\n if (u?.email) grants.email = String(u.email);\n }\n\n // Single clock for every validity-window check in this resolution\n // (ADR-0091 D2 — a grant row outside [valid_from, valid_until) does not\n // resolve, fail-closed, with no background job involved).\n const nowMs = opts.nowMs ?? Date.now();\n\n // 3. Memberships via sys_member (better-auth). ONE read serves two purposes,\n // so the two facts can never disagree about what the user belongs to:\n //\n // (a) [ADR-0095 D3] Org-administration roles for the ACTIVE organization,\n // normalized to the canonical built-in names (owner→org_owner,\n // admin→org_admin, …). This is the ONE PROVISIONING boundary where a\n // better-auth role is read: it is projected into `positions` here, and\n // separately drives the `organization_admin` capability grant\n // (auto-org-admin-grant.ts). No enforcement code path reads the raw\n // role — posture/adjudication run off the resulting capability grants,\n // so the #2836 dual-track cannot recur.\n //\n // (b) [ADR-0105 D2] `accessible_org_ids` — EVERY organization the user\n // currently belongs to, regardless of which one is active. This is the\n // `group` posture's read reach (Layer 0 becomes `organization_id IN\n // (...)`), so it must span the whole membership set, not the active\n // org. Rows outside their ADR-0091 validity window do not resolve; the\n // columns are absent on `sys_member` today, and `isGrantActive` treats\n // an absent bound as unbounded, so this is a no-op until they exist and\n // correct the moment they do.\n const members = await tryFind(ql, 'sys_member', { user_id: userId }, 200);\n const accessibleOrgIds = new Set<string>();\n for (const m of members) {\n if (!isGrantActive(m, nowMs)) continue;\n const org = m.organization_id ?? m.organizationId;\n if (typeof org === 'string' && org) accessibleOrgIds.add(org);\n }\n grants.accessible_org_ids = Array.from(accessibleOrgIds);\n\n // Positions come from the ACTIVE org's membership only (unchanged): a role\n // held in one organization must not grant its capabilities while the caller\n // operates in another. With no active org, every membership contributes —\n // exactly the pre-D2 behavior of the org-less read.\n const activeMembers = tenantId\n ? members.filter((m) => (m.organization_id ?? m.organizationId) === tenantId)\n : members;\n for (const m of activeMembers) {\n if (m.role && typeof m.role === 'string') {\n for (const raw of m.role.split(',').map((s: string) => s.trim()).filter(Boolean)) {\n const r = mapMembershipRole(raw);\n if (!grants.positions.includes(r)) grants.positions.push(r);\n }\n }\n }\n\n // 4. [ADR-0057 D4] Platform-owned RBAC role assignments (sys_user_position) — the\n // source of truth for custom roles, decoupled from sys_member.role.\n // `organization_id = null` = global (cross-tenant); else match active org.\n const userPositionRows = await tryFind(ql, 'sys_user_position', { user_id: userId }, 200);\n for (const ur of userPositionRows) {\n const org = ur.organization_id ?? null;\n if (org && tenantId && org !== tenantId) continue;\n if (!isGrantActive(ur, nowMs)) continue;\n const r = ur.position;\n if (typeof r === 'string' && r && !grants.positions.includes(r)) grants.positions.push(r);\n }\n\n // 5. Fellow-org user IDs so RLS can scope identity tables to collaborators.\n if (tenantId) {\n const orgMembers = await tryFind(ql, 'sys_member', { organization_id: tenantId }, 1000);\n const ids = new Set<string>(\n orgMembers\n .map((m) => m.user_id ?? m.userId)\n .filter((v): v is string => typeof v === 'string' && v.length > 0),\n );\n ids.add(userId);\n grants.org_user_ids = Array.from(ids);\n }\n\n // 6. Permission sets — user-scoped grants (null org = global, else active org).\n // Rows outside their validity window are dropped BEFORE any derivation, so\n // an expired admin_full_access grant cannot yield platform_admin either.\n const upsRowsAll = await tryFind(ql, 'sys_user_permission_set', { user_id: userId }, 100);\n const upsRows = upsRowsAll.filter((r) => isGrantActive(r, nowMs));\n const psIds = new Set<string>(\n upsRows\n .filter((r) => {\n const org = (r.organization_id ?? r.organizationId) ?? null;\n return !(org && tenantId && org !== tenantId);\n })\n .map((r) => r.permission_set_id ?? r.permissionSetId)\n .filter(Boolean),\n );\n // platform_admin (ADR-0068 D2) is DERIVED from an UNSCOPED admin_full_access\n // USER grant — the single source of truth (no trusted stored boolean).\n const unscopedUserPsIds = new Set<string>(\n upsRows\n .filter((r) => ((r.organization_id ?? r.organizationId) ?? null) === null)\n .map((r) => r.permission_set_id ?? r.permissionSetId)\n .filter(Boolean),\n );\n let hasPlatformAdminGrant = false;\n\n // 5b. [ADR-0090 D5] Audience anchor: every AUTHENTICATED member implicitly\n // holds the built-in `everyone` position, so sets bound to it resolve\n // below exactly like any other position-bound grant — ADDITIVE, with no\n // \"only when the user has nothing else\" cliff.\n if (!grants.positions.includes('everyone')) grants.positions.push('everyone');\n\n // 6a. Position-bound permission sets (sys_position_permission_set): a position\n // carries its permission sets.\n if (grants.positions.length > 0) {\n const positionRows = await tryFind(ql, 'sys_position', { name: { $in: grants.positions } }, 100);\n const positionIds = positionRows.map((r) => r.id).filter(Boolean);\n if (positionIds.length > 0) {\n const rpsRows = await tryFind(ql, 'sys_position_permission_set', { position_id: { $in: positionIds } }, 500);\n for (const r of rpsRows) {\n const id = r.permission_set_id ?? r.permissionSetId;\n if (id) psIds.add(id);\n }\n }\n }\n\n // 6b. Resolve permission-set details (names → grants.permissions; system_permissions;\n // tab_permissions merged by highest visibility).\n if (psIds.size > 0) {\n const psRows = await tryFind(ql, 'sys_permission_set', { id: { $in: Array.from(psIds) } }, 500);\n const tabRank: Record<string, number> = { hidden: 0, default_off: 1, default_on: 2, visible: 3 };\n const mergedTabs: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'> = {};\n for (const ps of psRows) {\n if (ps.name && !grants.permissions.includes(ps.name)) grants.permissions.push(ps.name);\n if (ps.name === ADMIN_FULL_ACCESS && unscopedUserPsIds.has(ps.id)) hasPlatformAdminGrant = true;\n const sysPerms = typeof ps.system_permissions === 'string'\n ? safeJsonParse(ps.system_permissions, [])\n : (ps.system_permissions ?? ps.systemPermissions);\n if (Array.isArray(sysPerms)) {\n for (const p of sysPerms) {\n if (typeof p === 'string' && !grants.systemPermissions.includes(p)) grants.systemPermissions.push(p);\n }\n }\n const tabs = typeof ps.tab_permissions === 'string'\n ? safeJsonParse(ps.tab_permissions, {})\n : (ps.tab_permissions ?? ps.tabPermissions);\n if (tabs && typeof tabs === 'object') {\n for (const [app, val] of Object.entries(tabs as Record<string, unknown>)) {\n if (typeof val !== 'string' || !(val in tabRank)) continue;\n const cur = mergedTabs[app];\n if (!cur || tabRank[val] > tabRank[cur]) {\n mergedTabs[app] = val as 'visible' | 'hidden' | 'default_on' | 'default_off';\n }\n }\n }\n }\n if (Object.keys(mergedTabs).length > 0) grants.tabPermissions = mergedTabs;\n }\n\n // 6c. Project the derived platform_admin built-in role (leads the list).\n if (hasPlatformAdminGrant && !grants.positions.includes(BUILTIN_IDENTITY_PLATFORM_ADMIN)) {\n grants.positions.unshift(BUILTIN_IDENTITY_PLATFORM_ADMIN);\n }\n\n // 6d. [ADR-0095 D2/D3] Resolve the posture rung ONCE, from held CAPABILITY\n // grants — never from a better-auth role. `PLATFORM_ADMIN` from the\n // unscoped `admin_full_access` grant (the same `viewAllRecords`/\n // `modifyAllRecords` evidence the superuser bypass trusts); `TENANT_ADMIN`\n // from the `organization_admin` grant (auto-provisioned from the better-\n // auth owner/admin role at §3 above — a provisioning source, not an\n // enforcement input, closing the #2836 dual-track class). Enforcement\n // behavior is unchanged: the per-object Layer 0 exemption + per-side\n // superuser bypass still gate access; posture is the carried, explainable\n // tier. `EXTERNAL` is never derived (no external principal type yet).\n grants.posture = derivePosture({\n isPlatformAdmin: hasPlatformAdminGrant,\n // [ADR-0105 D4] Either org-admin capability set resolves the rung — the\n // wall-less variant differs only by withholding the superuser bits.\n isTenantAdmin: ORGANIZATION_ADMIN_GRANTS.some((n: string) => grants.permissions.includes(n)),\n });\n\n // 7. [ADR-0024] Env-side AI seat: synthesize the `ai_seat` capability from the\n // boolean sys_user.ai_access (sqlite returns 1/0; memory returns boolean).\n if (!grants.permissions.includes('ai_seat')) {\n const aiAccess = ((await getUserRow()) as { ai_access?: unknown } | undefined)?.ai_access;\n if (aiAccess === true || aiAccess === 1 || aiAccess === '1') grants.permissions.push('ai_seat');\n }\n\n return grants;\n}\n\n// ── Localization (ADR-0053 Phase 2) ─────────────────────────────────────────\n\nfunction isValidTimeZone(tz: string): boolean {\n try { new Intl.DateTimeFormat('en-US', { timeZone: tz }); return true; } catch { return false; }\n}\nfunction coerceTimeZone(value: unknown): string | undefined {\n const s = typeof value === 'string' ? value.trim() : value != null ? String(value).trim() : '';\n return s && isValidTimeZone(s) ? s : undefined;\n}\nfunction coerceLocale(value: unknown): string | undefined {\n const s = typeof value === 'string' ? value.trim() : value != null ? String(value).trim() : '';\n return s || undefined;\n}\nfunction coerceCurrency(value: unknown): string | undefined {\n const s = typeof value === 'string' ? value.trim().toUpperCase() : '';\n return /^[A-Z]{3}$/.test(s) ? s : undefined;\n}\n\nexport interface ResolveLocalizationInput {\n ql: any;\n /** Settings service exposing `get(namespace, key, { tenantId, userId })`. */\n settings?: any;\n tenantId?: string;\n userId?: string;\n}\n\n/**\n * Resolve workspace localization defaults (reference `timezone` / `locale` /\n * `currency`). Canonical path is the `localization` SettingsManifest (cascade:\n * platform default → global → tenant); falls back to direct tenant-scoped\n * `sys_setting` rows, then the built-ins `UTC` / `en-US`. Never throws.\n */\nexport async function resolveLocalizationContext(\n input: ResolveLocalizationInput,\n): Promise<{ timezone: string; locale: string; currency?: string }> {\n const { ql, settings, tenantId, userId } = input;\n try {\n if (settings && typeof settings.get === 'function') {\n const sctx = { tenantId, userId } as any;\n const [tzRes, localeRes, currencyRes] = await Promise.all([\n settings.get('localization', 'timezone', sctx).catch(() => undefined),\n settings.get('localization', 'locale', sctx).catch(() => undefined),\n settings.get('localization', 'currency', sctx).catch(() => undefined),\n ]);\n const tz = coerceTimeZone(tzRes?.value);\n const locale = coerceLocale(localeRes?.value);\n const currency = coerceCurrency(currencyRes?.value);\n if (tz || locale || currency) return { timezone: tz ?? 'UTC', locale: locale ?? 'en-US', currency };\n }\n } catch {\n // settings service unavailable → direct read\n }\n // One read for all three keys instead of a query per key (`$in` on `key`).\n const rows = await tryFind(\n ql,\n 'sys_setting',\n { namespace: 'localization', key: { $in: ['timezone', 'locale', 'currency'] }, scope: 'tenant' },\n 10,\n );\n const valueOf = (k: string) => rows.find((r) => r.key === k)?.value;\n return {\n timezone: coerceTimeZone(valueOf('timezone')) ?? 'UTC',\n locale: coerceLocale(valueOf('locale')) ?? 'en-US',\n currency: coerceCurrency(valueOf('currency')),\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Grant validity windows (ADR-0091 D1/D2).\n *\n * `sys_user_position` and `sys_user_permission_set` rows carry optional\n * `valid_from` / `valid_until` columns. A row outside its window MUST NOT\n * resolve — anywhere, symmetrically: `resolveAuthzContext`, the explain\n * engine's `buildContextForUser`, plugin-sharing's `expandPositionUsers`,\n * and (transitively) the delegated-admin gate's held-scope resolution.\n *\n * Correctness lives HERE, at resolution time — never in a cleanup job\n * (ADR-0049: no unenforced security properties). The window is half-open\n * `[from, until)` in UTC: a grant is inactive before `valid_from` and\n * inactive AT and AFTER `valid_until`. Null/absent bounds mean unbounded,\n * so pre-ADR-0091 rows behave exactly as before.\n *\n * Fail-closed: a bound that is PRESENT but unparseable disables the grant\n * (unlike API-key `isExpired`, which tolerates garbage — an API key is a\n * single credential, a grant row is standing authority).\n */\n\n/**\n * Coerce a stored timestamp to epoch milliseconds.\n * Returns `undefined` for absent (null/undefined/'') values — \"no bound\" —\n * and `NaN` for present-but-unparseable values, which callers treat as\n * out-of-window (fail closed).\n */\nfunction toEpochMs(value: unknown): number | undefined {\n if (value == null || value === '') return undefined;\n if (typeof value === 'number') {\n // Heuristic: seconds vs milliseconds epoch (same rule as api-key.ts).\n return value < 1e12 ? value * 1000 : value;\n }\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'string') return Date.parse(value);\n return Number.NaN;\n}\n\n/** The validity-window shape shared by both user-grant tables (ADR-0091 D1). */\nexport interface GrantValidityWindow {\n valid_from?: unknown;\n valid_until?: unknown;\n}\n\n/**\n * True when a grant row is inside its validity window at `nowMs`.\n * The single predicate every resolver uses (ADR-0091 D2):\n * `(valid_from is null or valid_from <= now) and (valid_until is null or valid_until > now)`.\n */\nexport function isGrantActive(row: GrantValidityWindow | null | undefined, nowMs: number): boolean {\n if (!row) return false;\n const from = toEpochMs((row as any).valid_from ?? (row as any).validFrom);\n // NaN comparisons are always false, so an unparseable bound fails closed.\n if (from !== undefined && !(nowMs >= from)) return false;\n const until = toEpochMs((row as any).valid_until ?? (row as any).validUntil);\n if (until !== undefined && !(nowMs < until)) return false;\n return true;\n}\n\n/**\n * True when a grant row carries a `valid_until` that has already passed —\n * i.e. it WAS active and expired (not merely not-yet-active). The explain\n * engine uses this to report the dedicated \"held until … — expired\"\n * contributor state (ADR-0091 D2).\n */\nexport function isGrantExpired(row: GrantValidityWindow | null | undefined, nowMs: number): boolean {\n if (!row) return false;\n const until = toEpochMs((row as any).valid_until ?? (row as any).validUntil);\n if (until === undefined) return false;\n return !(nowMs < until);\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * ── The monotonic posture ladder (ADR-0095 D2/D3) ───────────────────────────\n *\n * The principal-tiering enum resolved ONCE in `resolveAuthzContext`\n * (`PLATFORM_ADMIN > TENANT_ADMIN > MEMBER > EXTERNAL`). This module owns two\n * things and deliberately nothing more:\n *\n * 1. **Derivation (D3).** {@link derivePosture} maps held *capability grants*\n * — never a better-auth role — to a rung. `PLATFORM_ADMIN` derives from the\n * unscoped `admin_full_access` grant (the `viewAllRecords`/`modifyAllRecords`\n * evidence the superuser bypass already trusts); `TENANT_ADMIN` from the\n * `organization_admin` grant. The better-auth `role='admin'` is upstream a\n * *provisioning source* of those grants (`auto-org-admin-grant.ts`), so it\n * never re-enters adjudication here — the #2836 dual-track class is closed\n * by construction.\n *\n * 2. **The rung → injection-rule mapping + its tested invariants (D2).** Each\n * rung maps to EXACTLY ONE row-visibility injection rule\n * ({@link POSTURE_INJECTION_RULE}). {@link postureVisibleRows} is the\n * REFERENCE MODEL of those rules over a synthetic row-set — it locks the two\n * properties the ADR requires as invariants: strict nesting (rung n's\n * visible set ⊇ rung n−1's) and the EXTERNAL deny-by-default semantics\n * (explicit shares only, OWD never widens it).\n *\n * This module is NOT the enforcement path. The effective read/write filter is\n * `Layer0(tenant) AND Layer1(business RLS)`, computed in `@objectstack/plugin-\n * security` (`tenant-layer.ts` + `security-plugin.ts`), and the real behavior\n * guard is the `authz-matrix-gate` unit snapshot + the dogfood conformance\n * matrix. The reference model here exists so the ladder's *mathematical*\n * properties can be asserted at the unit layer without an enforcement boot, and\n * so the EXTERNAL rung — which has no enforcement path yet — cannot be\n * reinvented differently when portal/external membership arrives.\n */\n\nimport type { AuthzPosture } from '@objectstack/spec/security';\n\n/**\n * The rung ordering, high privilege → low, matching the spec enum's numeric\n * values (`PLATFORM_ADMIN=3 … EXTERNAL=0`). Visibility grows monotonically UP\n * this ladder (see {@link postureVisibleRows}).\n */\nexport const POSTURE_LADDER = [\n 'PLATFORM_ADMIN',\n 'TENANT_ADMIN',\n 'MEMBER',\n 'EXTERNAL',\n] as const satisfies readonly AuthzPosture[];\n\n/** Numeric rank per rung (mirrors the spec `AuthzPosture` enum values). */\nexport const POSTURE_RANK: Record<AuthzPosture, number> = {\n PLATFORM_ADMIN: 3,\n TENANT_ADMIN: 2,\n MEMBER: 1,\n EXTERNAL: 0,\n};\n\n/**\n * The ONE row-visibility injection rule each rung maps to (ADR-0095 D2). Prose,\n * because the machine artifacts live in enforcement (Layer 0 + the per-rung\n * Layer 1 rule); this is the enumerable contract the explain track reports and\n * {@link postureVisibleRows} models.\n */\nexport const POSTURE_INJECTION_RULE: Record<AuthzPosture, string> = {\n PLATFORM_ADMIN:\n 'Layer 0 exemption where the object posture permits (private / platform-global / better-auth-managed) — crosses the tenant wall; org-scoped like TENANT_ADMIN on ordinary tenant business objects.',\n TENANT_ADMIN:\n 'All rows within the active organization (organization_id == ctx.tenantId); no ownership / depth / sharing narrowing.',\n MEMBER:\n 'Business RLS within the organization — ownership (owner / unit depth), the OWD baseline, and explicit sharing.',\n EXTERNAL:\n 'Explicitly shared rows ONLY — OWD baselines and sharing rules never apply; a misconfiguration can only shrink visibility, never widen it.',\n};\n\n/** Capability-grant evidence the posture derivation consumes (ADR-0095 D3). */\nexport interface PostureEvidence {\n /**\n * Holds the UNSCOPED platform-admin capability grant (`admin_full_access` →\n * `viewAllRecords`/`modifyAllRecords`) — the same evidence the superuser\n * bypass trusts. NOT a better-auth role.\n */\n isPlatformAdmin: boolean;\n /**\n * Holds the org-admin capability grant (`organization_admin`, tenant-scoped\n * `viewAllRecords`/`modifyAllRecords`). Provisioned from the better-auth\n * owner/admin role upstream, consumed here only as a held capability.\n */\n isTenantAdmin: boolean;\n}\n\n/**\n * Resolve the principal's posture rung from held capability grants (ADR-0095 D3).\n *\n * Returns `PLATFORM_ADMIN` | `TENANT_ADMIN` | `MEMBER`. It NEVER returns\n * `EXTERNAL`: no external principal type exists yet (the sharing chain has no\n * portal/guest-share concept — ADR-0095 W4). The `EXTERNAL` rung, its injection\n * rule, and its semantics are defined and test-locked ({@link postureVisibleRows},\n * {@link POSTURE_INJECTION_RULE}) so that when portal/external membership lands\n * (ADR-0093) the derivation gains an EXTERNAL branch HERE without the rung being\n * reinvented. `MEMBER` is the authenticated-principal floor.\n */\nexport function derivePosture(evidence: PostureEvidence): AuthzPosture {\n if (evidence.isPlatformAdmin) return 'PLATFORM_ADMIN';\n if (evidence.isTenantAdmin) return 'TENANT_ADMIN';\n return 'MEMBER';\n}\n\n// ── Reference visibility model (invariant lock, NOT enforcement) ─────────────\n\n/** A synthetic record for the ladder reference model. */\nexport interface LadderRow {\n id: string;\n /** The row's tenant. `undefined` = a non-tenant (platform-global) row. */\n organization_id?: string;\n /** The row's owner (drives the MEMBER ownership disjunct). */\n owner_id?: string;\n /**\n * Whether an OWD-derived source would admit this row for a member (public\n * baseline / criteria sharing). EXTERNAL deliberately ignores this field.\n */\n owdVisible?: boolean;\n /** User ids this row is EXPLICITLY shared to (the only EXTERNAL source). */\n sharedTo?: readonly string[];\n}\n\n/** The principal the reference model evaluates a rung for. */\nexport interface LadderPrincipal {\n userId: string;\n /** The principal's active organization (undefined for an unscoped principal). */\n organizationId?: string;\n}\n\nfunction isSharedTo(row: LadderRow, userId: string): boolean {\n return (row.sharedTo ?? []).includes(userId);\n}\n\n/** EXTERNAL rung: explicitly shared rows ONLY — never OWD, never org-wide. */\nfunction externalVisible(rows: readonly LadderRow[], p: LadderPrincipal): LadderRow[] {\n return rows.filter((r) => isSharedTo(r, p.userId));\n}\n\n/**\n * MEMBER rung: business RLS within the org — ownership OR OWD baseline OR\n * explicit sharing. Composed as `EXTERNAL ∪ (in-org ownership/OWD)` so the\n * EXTERNAL ⊆ MEMBER leg of the nesting invariant holds by construction.\n */\nfunction memberVisible(rows: readonly LadderRow[], p: LadderPrincipal): LadderRow[] {\n const shared = new Set(externalVisible(rows, p));\n return rows.filter(\n (r) =>\n shared.has(r) ||\n (r.organization_id === p.organizationId && (r.owner_id === p.userId || r.owdVisible === true)),\n );\n}\n\n/**\n * TENANT_ADMIN rung: all rows in the active organization. Composed as\n * `MEMBER ∪ (all in-org)` so MEMBER ⊆ TENANT_ADMIN holds by construction.\n */\nfunction tenantAdminVisible(rows: readonly LadderRow[], p: LadderPrincipal): LadderRow[] {\n const member = new Set(memberVisible(rows, p));\n return rows.filter((r) => member.has(r) || r.organization_id === p.organizationId);\n}\n\n/** PLATFORM_ADMIN rung: crosses the tenant wall — every row (⊇ TENANT_ADMIN trivially). */\nfunction platformAdminVisible(rows: readonly LadderRow[]): LadderRow[] {\n return [...rows];\n}\n\n/**\n * Reference model of the per-rung injection rule: the visible-row set a rung\n * would resolve to over `rows` for `principal`. Used to lock the ADR-0095 D2\n * invariants (strict nesting + EXTERNAL deny-by-default). NOT an enforcement\n * path — see the module header.\n */\nexport function postureVisibleRows(\n posture: AuthzPosture,\n rows: readonly LadderRow[],\n principal: LadderPrincipal,\n): LadderRow[] {\n switch (posture) {\n case 'PLATFORM_ADMIN':\n return platformAdminVisible(rows);\n case 'TENANT_ADMIN':\n return tenantAdminVisible(rows, principal);\n case 'MEMBER':\n return memberVisible(rows, principal);\n case 'EXTERNAL':\n return externalVisible(rows, principal);\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * assembleExecutionContext — the SINGLE assembly of an inbound request's\n * {@link ExecutionContext}, shared by every transport entry point.\n *\n * `resolveAuthzContext` (next door) already made AUTHORIZATION resolution\n * single-sourced. The step AFTER it — turning the resolved\n * {@link ResolvedAuthzContext} into the `ExecutionContext` envelope that\n * reaches enforcement — stayed hand-written per transport, and that duplication\n * produced a measured defect family:\n *\n * - **#6071 — field drift.** The REST copy never set `principalKind`, so every\n * enforcement judgment reading it (explain's guest⇒EXTERNAL floor, the\n * security plugin's agent baseline, the perf-disclosure gate) was silently\n * never-true on that face.\n * - **#6206 / #6551 — dropped fields.** The share-link copies omitted\n * `accessible_org_ids`, and the `group` posture's Layer 0 wall reads it\n * directly: real 403s for callers who should have been let through. Both\n * surfaces were since converted to pass the WHOLE envelope through.\n *\n * Both defects are the same shape: a field exists on `ExecutionContext`, one\n * copy carries it, another silently does not. This module makes that shape\n * unrepresentable by CLOSING the field set with a type\n * ({@link ExecutionContextEntryFields}) — every field a transport entry point\n * decides must be decided HERE, explicitly, and a new `ExecutionContext` field\n * fails to compile until it is either assembled or listed as\n * non-entry-resolved.\n *\n * ## Two named entries — the anonymous face is genuinely divergent (#6216)\n *\n * The maintainer ruling of 2026-08-08 on #6216 (Option A) settled the one\n * question that blocked convergence: what an anonymous request yields.\n *\n * - {@link assembleExecutionContext} — the DEFAULT, fail-closed entry. No\n * resolved principal → `undefined`, and the surface answers 401. This is the\n * REST face's contract, unchanged.\n * - {@link assembleExecutionContextOrGuest} — the EXPLICIT guest entry. No\n * resolved principal → a first-class guest envelope\n * (`principalKind: 'guest'`, `positions: ['guest']`), which the runtime /\n * MCP dispatcher has always produced and whose consumers are live\n * (`plugin-security/explain-engine.ts`: guest ⇒ `EXTERNAL` posture). A\n * surface adopts this entry ONLY when its product semantics serve anonymous\n * principals.\n *\n * Neither surface's runtime behaviour changes. What changes is that the\n * divergence is now NAMED API rather than drift — and the same is true of the\n * per-face values below (`accessToken`, `oauth`, `localization`): they are\n * REQUIRED inputs, so a face cannot silently omit one, and what a face chooses\n * to withhold it withholds on the record.\n *\n * Options B (guest everywhere — turns REST's anonymous 401s into authz-shaped\n * denials and makes anonymous-serving the DEFAULT posture of a new surface) and\n * C (no-ctx everywhere — deletes the guest principal, the `guest` position and\n * explain's `EXTERNAL` floor) were both considered and rejected: each breaks a\n * live consumer side.\n */\n\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\n\nimport type { AuthGate } from './auth-gate.js';\nimport type { ResolvedAuthzContext } from './resolve-authz-context.js';\n\n/**\n * `ExecutionContext` fields a transport ENTRY POINT does not resolve — they are\n * per-operation flags, engine internals, or attribution supplied further down\n * the stack. Listing one here is a deliberate, reviewable statement that no\n * request-identity resolution produces it; everything NOT listed is part of the\n * closed entry set below and must be assembled.\n *\n * - `actor` / `attributedUserId` — audit attribution, set by the host or the\n * hook layer (ADR-0014 D2, #4586), never by identity resolution.\n * - `rlsMembership` — engine-side RLS scoping cache.\n * - `transaction` / `traceId` — per-operation handles.\n * - `flowRunId`, `skipTriggers`, `skipAutomations`, `seedReplay`,\n * `skipStateMachine`, `preserveAudit` — per-write behaviour flags,\n * server-constructed at the call site.\n */\ntype NonEntryExecutionContextField =\n | 'actor'\n | 'attributedUserId'\n | 'rlsMembership'\n | 'transaction'\n | 'traceId'\n | 'flowRunId'\n | 'skipTriggers'\n | 'skipAutomations'\n | 'seedReplay'\n | 'skipStateMachine'\n | 'preserveAudit';\n\n/**\n * The CLOSED field set every transport entry point must decide. Derived from\n * `ExecutionContext` itself, so adding a field to `ExecutionContextSchema`\n * widens this union automatically — and the assembly below stops compiling\n * until the new field is either assembled or declared non-entry-resolved.\n */\nexport type EntryExecutionContextField = Exclude<\n keyof ExecutionContext,\n NonEntryExecutionContextField\n>;\n\n/**\n * One value per closed field. Every key is REQUIRED (`-?`) while the VALUE may\n * be `undefined` — the decision may not be omitted, only made explicitly. This\n * is the type that makes the #6071 drift class unrepresentable.\n */\nexport type ExecutionContextEntryFields = {\n [K in EntryExecutionContextField]-?: ExecutionContext[K];\n};\n\n/**\n * Emission order of the assembled envelope, and a second, independent\n * exhaustiveness bite: `satisfies` rejects a stale name, and\n * `_ENTRY_FIELDS_EXHAUSTIVE` below rejects a missing one.\n */\nexport const ENTRY_EXECUTION_CONTEXT_FIELDS = [\n 'positions',\n 'permissions',\n 'systemPermissions',\n 'isSystem',\n 'principalKind',\n 'onBehalfOf',\n 'audience',\n 'userId',\n 'tenantId',\n 'email',\n 'accessToken',\n 'tabPermissions',\n 'posture',\n 'authGate',\n 'org_user_ids',\n 'accessible_org_ids',\n 'oauthScopes',\n 'timezone',\n 'locale',\n 'currency',\n] as const satisfies readonly EntryExecutionContextField[];\n\n/**\n * Compile-time proof that {@link ENTRY_EXECUTION_CONTEXT_FIELDS} covers the\n * whole closed set. A new `ExecutionContext` field that is neither assembled\n * nor listed as non-entry-resolved makes this `never`, and the initializer\n * below fails to compile.\n */\ntype MissingEntryField = Exclude<\n EntryExecutionContextField,\n (typeof ENTRY_EXECUTION_CONTEXT_FIELDS)[number]\n>;\nconst _ENTRY_FIELDS_EXHAUSTIVE: [MissingEntryField] extends [never] ? true : never = true;\nvoid _ENTRY_FIELDS_EXHAUSTIVE;\n\n/**\n * OAuth 2.1 access-token provenance. Reaches the assembler from the `/mcp`\n * dispatch door ALONE (`acceptOAuthAccessToken`) — OAuth bearers carry coarse\n * tool-family scopes enforced at MCP tool dispatch, so honouring them on\n * another surface would bypass that scope model entirely.\n */\nexport interface OAuthTokenProvenance {\n /** The human `sub` the token was issued for. */\n userId: string;\n /** Granted scopes, surfaced on the envelope so MCP can narrow tool families. */\n scopes: string[];\n /**\n * The authorized client (`azp`). Present ⇒ this is an AI AGENT acting on\n * behalf of the human `userId`; absent ⇒ the token names no client and the\n * principal stays human (the scopes are still surfaced).\n */\n clientId?: string;\n /**\n * The agent's OWN permission CEILING, derived from {@link scopes} by the door\n * that speaks the OAuth scope vocabulary\n * (`scopesToAgentPermissionSets`, `@objectstack/spec/ai`).\n *\n * Interpreted THERE and not here on purpose: the scope vocabulary is\n * MCP-domain knowledge and `@objectstack/core` is the microkernel — it should\n * not acquire a dependency on the AI subdomain (concretely, every package\n * whose test config aliases `@objectstack/core` to its source would then have\n * to resolve `@objectstack/spec/ai` too, down to `driver-memory`). What the\n * ceiling REPLACES on the envelope is decided below, once, for every face —\n * and that is the part that drifted.\n */\n scopePermissions: string[];\n /**\n * Whether the token carries the user's consent to let this agent invoke\n * actions on their behalf — the `actions:execute` scope\n * (`MCP_OAUTH_SCOPE_ACTIONS`), evaluated at the same door for the same\n * reason.\n */\n delegatesActions: boolean;\n}\n\n/** Reference localization for an authenticated principal (@see resolveLocalizationContext). */\nexport interface EntryLocalization {\n timezone?: string;\n locale?: string;\n currency?: string;\n}\n\n/**\n * Everything the shared assembly needs. Every key is REQUIRED so a face cannot\n * silently omit one — a face that has no value for an input passes `undefined`\n * on the record, which is what turns the remaining divergences into named API.\n */\nexport interface ExecutionContextAssemblyInput {\n /** The shared authorization envelope (@see resolveAuthzContext). */\n authz: ResolvedAuthzContext;\n /**\n * OAuth access-token provenance, or `undefined` on a face that does not\n * accept one. Only the `/mcp` dispatch door passes a value; REST passes\n * `undefined` — which is why `principalKind: 'agent'`, `onBehalfOf` and\n * `oauthScopes` are not representable there.\n */\n oauth: OAuthTokenProvenance | undefined;\n /**\n * Resolved reference localization, or `undefined` when the face resolved\n * none (anonymous requests have no scope to resolve against).\n */\n localization: EntryLocalization | undefined;\n /**\n * The request's OWN locale preference (`Accept-Language`, `?locale`, …),\n * which wins over the workspace default; `undefined` when the caller\n * expresses none. Each face extracts it its own way — the PRECEDENCE lives\n * here so the two cannot disagree about it (#3957).\n */\n requestLocale: string | undefined;\n /**\n * The session bearer to carry on the envelope, surfaced to hooks as\n * `session.accessToken` (`objectql/engine.ts` `buildSession`,\n * `spec/data/hook.zod.ts`).\n *\n * A NAMED per-face divergence, preserved deliberately (#6216): the runtime /\n * MCP dispatcher passes `authz.accessToken`; the REST face has never carried\n * it and passes `undefined`, because widening a published hook surface to\n * expose the session token on a second transport is a product decision, not a\n * refactor. Being a required input, the choice is on the record at each face\n * instead of being an omission nobody can see.\n */\n accessToken: string | undefined;\n /**\n * [ADR-0069] The AUTHENTICATION-policy gate posture resolved for this\n * request's session (expired password / enforced MFA), or `undefined` when\n * the face resolves none — normalize a session user through\n * `normalizeAuthGate` rather than copying its `authGate` verbatim.\n *\n * A NAMED per-face divergence, on the same footing as {@link accessToken}\n * (#7280):\n *\n * - the **REST** face lifts it onto the envelope, because that is where its\n * consumer reads it (`RestServer.enforceAuth` → `403 { code, message }`);\n * - the **runtime / MCP dispatcher** passes `undefined`, because it enforces\n * the same ADR-0069 gate at its OWN seam (`HttpDispatcher.enforceAuthGate`\n * re-reads the session and calls `evaluateAuthGate` there) and never reads\n * `context.authGate` — carrying it would be a second, unread copy.\n *\n * Until #7280 declared it, this posture reached the envelope through an\n * `as any` spread AFTER assembly, which put it outside this closed set\n * entirely — the blind spot the set exists to remove.\n */\n authGate: AuthGate | undefined;\n}\n\n/** Drop `undefined`-valued keys, emitting in the closed set's declared order. */\nfunction emit(fields: ExecutionContextEntryFields): ExecutionContext {\n const ctx: Record<string, unknown> = {};\n for (const key of ENTRY_EXECUTION_CONTEXT_FIELDS) {\n const value = fields[key];\n if (value !== undefined) ctx[key] = value;\n }\n return ctx as ExecutionContext;\n}\n\n/**\n * Decide every field of the closed set. The one branch is the principal's\n * PROVENANCE (agent / human / guest); everything else is a single expression\n * shared by every face.\n */\nfunction entryFields(\n input: ExecutionContextAssemblyInput,\n anonymous: boolean,\n): ExecutionContextEntryFields {\n const { authz, oauth, localization, requestLocale, accessToken, authGate } = input;\n\n // [ADR-0090 D10 — agent principal] An OAuth access token naming an authorized\n // client (`azp`) is an AI agent acting ON BEHALF OF the human `sub`. The\n // agent's OWN grants are its scope-derived CEILING, NOT the user's — so the\n // user-derived positions/permissions/systemPermissions are REPLACED with that\n // ceiling. The human stays the delegator (`onBehalfOf`), and the security\n // engine intersects the two so the agent can never exceed EITHER its\n // consented scope OR the user's own reach (confused-deputy prevention).\n // `userId` stays the human so owner-stamping and `current_user.*` RLS resolve\n // to them.\n const agent = !anonymous && oauth?.clientId ? oauth : undefined;\n\n return {\n // [ADR-0090 D9/D10] Principal taxonomy at the HTTP entry: a session-backed\n // request is a human principal; a sessionless one is a guest, holding the\n // built-in `guest` position implicitly and exclusively. Internal engine\n // calls that construct bare contexts never pass through here, so the\n // security plugin's empty-context skip path keeps its meaning.\n positions: agent ? [] : anonymous ? ['guest'] : authz.positions,\n permissions: agent ? agent.scopePermissions : authz.permissions,\n // [ADR-0090 D10] System capabilities on the agent principal gate business\n // ACTION invocation (`actionPermissionError` reads `ctx.systemPermissions`)\n // — a door SEPARATE from the object CRUD/FLS/RLS intersection, which is\n // driven by the resolved ceiling SETS (they carry no caps, so cap-gated\n // OBJECT access stays denied to the agent regardless of this line). The\n // `actions:execute` scope IS the user's consent to let this agent invoke\n // actions on their behalf; without it the agent holds none.\n systemPermissions: agent\n ? agent.delegatesActions\n ? (authz.systemPermissions ?? [])\n : []\n : authz.systemPermissions,\n isSystem: false,\n principalKind: agent ? 'agent' : anonymous ? 'guest' : 'human',\n onBehalfOf: agent ? { userId: authz.userId!, principalKind: 'human' } : undefined,\n // [ADR-0090 D10/D11 — P1 shape] No transport resolves an external\n // (portal/partner) audience yet; `undefined` reads as 'internal'. Named\n // here rather than excluded so the gap is visible in the closed set instead\n // of being invisible outside it — when an external principal type lands,\n // this is the line that must change, on every face at once.\n audience: undefined,\n userId: authz.userId,\n tenantId: authz.tenantId,\n email: authz.email,\n accessToken,\n tabPermissions: authz.tabPermissions,\n // [ADR-0095 D2 / #2947] The derived posture rung, carried so every\n // transport presents enforcement the SAME value. Present only for an\n // authenticated principal (guest → absent).\n posture: authz.posture,\n // [ADR-0069 / #7280] The AUTHENTICATION-policy gate, carried for the seam\n // that reads it off the envelope (REST's `enforceAuth`). Anonymous → never:\n // a guest has no authenticated session for a policy gate to attach to, so\n // \"gated guest\" is not a state this entry can emit even if a face passed\n // one.\n authGate: anonymous ? undefined : authGate,\n /** Fellow-org user IDs for RLS scoping of identity tables. */\n org_user_ids: authz.org_user_ids,\n // [ADR-0105 D2] The caller's org access set — the `group` posture's Layer 0\n // wall reads it directly, so every transport must carry it (#6206).\n accessible_org_ids: authz.accessible_org_ids,\n // OAuth provenance: surface the token's granted scopes so the MCP\n // dispatcher can narrow the exposed tool families (undefined for every\n // other provenance = not scope-limited).\n oauthScopes: oauth && authz.userId === oauth.userId ? oauth.scopes : undefined,\n // Anonymous → no localization (no scope to resolve against); the engine\n // default stands. [#3957] The request's OWN language preference wins over\n // the workspace default, so a rejection message is not rendered in English\n // beside the Chinese label of the very field it names.\n timezone: anonymous ? undefined : localization?.timezone,\n locale: anonymous ? undefined : (requestLocale ?? localization?.locale),\n currency: anonymous ? undefined : localization?.currency,\n };\n}\n\n/**\n * The DEFAULT, fail-closed entry (#6216 Option A). An unauthenticated request\n * yields NO context — the surface answers 401. Every surface uses this one\n * unless serving anonymous principals is part of its product semantics.\n */\nexport function assembleExecutionContext(\n input: ExecutionContextAssemblyInput,\n): ExecutionContext | undefined {\n if (!input.authz.userId) return undefined;\n return emit(entryFields(input, false));\n}\n\n/**\n * The EXPLICIT guest entry (#6216 Option A). An unauthenticated request becomes\n * a first-class guest principal — `principalKind: 'guest'`, `positions:\n * ['guest']` — which enforcement consumers read today\n * (`plugin-security/explain-engine.ts`: guest ⇒ `EXTERNAL` posture).\n *\n * Adopt this ONLY on a surface that genuinely serves anonymous principals: the\n * built-in `guest` position is the declared vocabulary for \"what anonymous may\n * do\", and handing a guest envelope to a surface that previously answered 401\n * converts an authentication failure into an authorization evaluation.\n */\nexport function assembleExecutionContextOrGuest(\n input: ExecutionContextAssemblyInput,\n): ExecutionContext {\n return emit(entryFields(input, !input.authz.userId));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * ADR-0069 — authentication-policy session gate.\n *\n * Some auth policies (password expiry, enforced MFA) must block an\n * authenticated user from PROTECTED RESOURCES until they remediate, while\n * still letting them reach the auth endpoints (change-password, two-factor\n * enrollment, sign-out) and a few UI-bootstrap reads.\n *\n * The posture is computed ONCE, in the auth `customSession` enrichment, and\n * attached to the session user as `user.authGate = { code, message }`. The\n * transport seams (REST middleware, dispatcher) then call\n * {@link evaluateAuthGate} to decide whether THIS request is blocked. Keeping\n * the allow-list + decision in one pure function means the seams can never\n * drift on what is blocked.\n */\n\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\n\n/**\n * The gate posture, DERIVED from its declaration on `ExecutionContextSchema`\n * (`packages/spec/src/kernel/execution-context.zod.ts`) rather than restated\n * here (#7280).\n *\n * It was a hand-written interface while the envelope field was undeclared, so\n * the two could have drifted with nothing to catch it — the exact class of\n * defect the closed entry field set (#6216) exists to make unrepresentable.\n * One declaration, one type.\n */\nexport type AuthGate = NonNullable<ExecutionContext['authGate']>;\n\n/** Message used when a session's gate names a `code` but no usable `message`. */\nconst DEFAULT_AUTH_GATE_MESSAGE = 'Access is blocked by an authentication policy.';\n\n/**\n * Normalize the `authGate` a better-auth session user carries into the shape\n * `ExecutionContextSchema` declares — or `null` when there is no gate.\n *\n * The session user crosses an external boundary as `any`, so this is where the\n * declared contract is actually met: a gate naming no string `code` is not a\n * gate, and a missing/blank `message` is filled with the default rather than\n * riding onto the envelope (and into a `403` body) as `undefined`. Both\n * consumers normalize HERE, at the one producer, instead of tolerating a loose\n * shape downstream: {@link evaluateAuthGate} for the seams that decide per\n * path, and REST's `computeExecCtx` for the seam that lifts the posture onto\n * the execution context.\n */\nexport function normalizeAuthGate(sessionUser: any): AuthGate | null {\n const gate = sessionUser?.authGate;\n if (!gate || typeof gate.code !== 'string') return null;\n return {\n code: gate.code,\n message:\n typeof gate.message === 'string' && gate.message ? gate.message : DEFAULT_AUTH_GATE_MESSAGE,\n };\n}\n\n// Endpoints a gated user MUST still reach to remediate or bootstrap the\n// remediation UI. Matched against the request path (query stripped). Covers\n// both REST (`/api/v1/auth/…`) and dispatcher (`/auth/…`) path shapes.\nconst ALLOW_PREFIXES = ['/api/v1/auth/', '/api/auth/', '/auth/'];\nconst ALLOW_SUFFIXES = ['/health', '/ready', '/discovery', '/me/apps', '/me/localization'];\n\n/** True when `path` is exempt from the auth gate (auth + remediation + health). */\nexport function isAuthGateAllowlisted(rawPath: string | undefined | null): boolean {\n if (!rawPath) return true;\n // Strip query + trailing slashes WITHOUT a regex (avoids ReDoS on a\n // path of many '/'). char 47 = '/'.\n let path = rawPath.split('?')[0] || '/';\n let end = path.length;\n while (end > 1 && path.charCodeAt(end - 1) === 47) end--;\n path = path.slice(0, end) || '/';\n // Any path with an `/auth/` segment is an auth endpoint (covers project-\n // scoped mounts like `/api/v1/environments/:env/auth/...`).\n if (path.includes('/auth/')) return true;\n for (const p of ALLOW_PREFIXES) {\n if (path.startsWith(p) || path === p.replace(/\\/$/, '')) return true;\n }\n for (const s of ALLOW_SUFFIXES) {\n if (path.endsWith(s)) return true;\n }\n return false;\n}\n\n/**\n * Returns the active gate when `sessionUser` carries an `authGate` AND `path`\n * is not allow-listed; otherwise null. Anonymous users (no `authGate`) and\n * allow-listed paths always pass.\n */\nexport function evaluateAuthGate(sessionUser: any, path: string): AuthGate | null {\n const gate = normalizeAuthGate(sessionUser);\n if (!gate) return null;\n if (isAuthGateAllowlisted(path)) return null;\n return gate;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * #2567 — the single anonymous-deny decision, shared by every HTTP seam.\n *\n * ADR-0056 D2 made the platform deny anonymous callers by default. Phase 1 gated\n * each surface (REST `/data`, dispatcher `/graphql` + `/meta`, raw-hono `/data`)\n * but every seam hand-rolled the same `!userId && !isSystem → 401` check. This\n * centralises that DECISION into one pure, tested function — the exact pattern\n * {@link ./auth-gate.ts} established for the ADR-0069 auth-policy gate: keeping\n * the decision in one function means the seams can never drift on who is denied.\n *\n * ## The `requireAuth` opt-out is gone (#3963)\n *\n * This used to take a `requireAuth` posture and no-op when it was falsy, so a\n * deployment could open its ENTIRE data plane with one config key. That key is\n * retired: auth is a kernel concern, and every surface that legitimately serves\n * a caller with no session derives its own narrow authorization from a\n * DECLARATION instead of from the deployment posture —\n *\n * - control plane (`/auth/*`, `/health`, `/ready`, `/discovery`, the ADR-0069\n * remediation paths) → the {@link isAuthGateAllowlisted} allowlist, below;\n * - public form submission → `publicFormGrant` (ADR-0056 Option A), derived\n * from the form view's own declaration;\n * - share links → the capability token, validated then read as SYSTEM;\n * - a `book.audience: 'public'` read → the ADR-0046 §6.7 audience gate (#3963);\n * - MCP → an OAuth token or API key, never anonymous.\n *\n * Those run UPSTREAM of this function and set the execution context (a `userId`,\n * or `isSystem`) or bypass the seam entirely, so this only ever inspects the\n * already-resolved context. Nothing else gets in.\n */\n\nimport { isAuthGateAllowlisted } from './auth-gate.js';\n\n/** HTTP status every seam returns for an anonymous-denied request. */\nexport const ANONYMOUS_DENY_STATUS = 401 as const;\n/** Stable machine code (mirrors the REST `enforceAuth` seam). ADR-0112: SCREAMING, a `StandardErrorCode` member. */\nexport const ANONYMOUS_DENY_CODE = 'UNAUTHENTICATED' as const;\n/** Human-facing message. */\nexport const ANONYMOUS_DENY_MESSAGE = 'Authentication is required to access this endpoint.';\n/**\n * The **REST seam's** 401 body — flat `{ error, message }`. NOT the platform's\n * only one; see the two-envelope table below before you reuse this shape.\n *\n * Exactly one consumer writes it: `@objectstack/rest`'s `enforceAuth`\n * (`rest-server.ts` — `res.status(ANONYMOUS_DENY_STATUS).json(ANONYMOUS_DENY_BODY)`),\n * which owns the `/data/*` and `/meta` surfaces.\n *\n * ## Two live envelopes, one denial (#5632)\n *\n * Every HTTP seam shares the DECISION ({@link shouldDenyAnonymous}) and the\n * semantics ({@link ANONYMOUS_DENY_STATUS} / {@link ANONYMOUS_DENY_CODE} /\n * {@link ANONYMOUS_DENY_MESSAGE}). What differs is the **wrapper**:\n *\n * - **REST seam** — `@objectstack/rest` `enforceAuth`, this constant, verbatim:\n * `{ error: 'UNAUTHENTICATED', message: '…' }`. The code is the value of the\n * top-level `error` key; there is no `success` key and no nesting.\n * - **Dispatcher seams** — the five runtime domains `domains/ai.ts`,\n * `domains/meta.ts`, `domains/security.ts`, `domains/actions.ts` and\n * `domains/automation.ts` do NOT use this constant. Each calls\n * `deps.error(ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, { code: ANONYMOUS_DENY_CODE })`,\n * so the wire body is the dispatcher's standard wrapper:\n * `{ success: false, error: { code, message, httpStatus } }`.\n *\n * Both shapes are **live and sanctioned** — ADR-0112's 2026-07-30 amendment\n * (#4007) records the flat and wrapped envelopes as the two live ones, and\n * assigns retiring one of them to the envelope-convergence line (#3843 family).\n * Converging them is a breaking wire change; it is not this module's to make,\n * and this constant must not be read as if it had already happened.\n *\n * ## Reading this from a consumer (human or AI author)\n *\n * Read the envelope the seam you called DECLARES — flat from `/data` + `/meta`,\n * wrapped from a dispatcher-mounted surface. Do **not** write a tolerant\n * `body.error?.code ?? body.error` chain that swallows both: that fallback is\n * precisely where an envelope regression hides, and this docstring claiming to\n * be \"the single shape every seam returns\" is what used to invite it (#5632).\n *\n * Both shapes are pinned against a real booted showcase by\n * `packages/qa/dogfood/test/showcase-anonymous-deny-surfaces.dogfood.test.ts`,\n * which classifies every anonymous 401 into exactly one of the two families and\n * fails on a third dialect or on a seam that changes family.\n */\nexport const ANONYMOUS_DENY_BODY = {\n error: ANONYMOUS_DENY_CODE,\n message: ANONYMOUS_DENY_MESSAGE,\n} as const;\n\nexport interface AnonymousDenyInput {\n /** Resolved caller id, if any. */\n userId?: string | null;\n /** Internal system context (never set on inbound HTTP; cannot be forged). */\n isSystem?: boolean;\n /** HTTP method — `OPTIONS` (CORS preflight) always passes. */\n method?: string | null;\n /**\n * OPTIONAL request path. When a NON-EMPTY string, a control-plane path\n * (auth / health / ready / discovery — see {@link isAuthGateAllowlisted}) is\n * exempt. Body-routed seams (GraphQL) have no meaningful path and pass\n * `undefined`; see the guard below for why that is load-bearing.\n */\n path?: string | null;\n}\n\n/**\n * True when the request MUST be rejected with 401. The one decision every HTTP\n * seam shares.\n */\nexport function shouldDenyAnonymous(input: AnonymousDenyInput): boolean {\n if (typeof input.method === 'string' && input.method.toUpperCase() === 'OPTIONS') {\n return false; // CORS preflight\n }\n if (input.userId || input.isSystem) return false; // authenticated / system\n // Control-plane exemption — ONLY for a real, non-empty path.\n //\n // ⚠️ `isAuthGateAllowlisted(undefined)` returns `true` (it treats \"no path\"\n // as allow-listed for the auth-gate's purposes). A body-routed seam such as\n // GraphQL has no meaningful request path; if it passed `undefined` straight\n // through, the allowlist would exempt EVERY anonymous query and silently\n // reopen exactly the hole #2567 closes. The non-empty guard is mandatory.\n if (typeof input.path === 'string' && input.path.length > 0 && isAuthGateAllowlisted(input.path)) {\n return false;\n }\n return true;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [#7678] The `?status=` vocabulary of the audience-binding suggestion list\n * (ADR-0090 D5/D9) — ONE owner for a rule that had exactly one implementation\n * and two seams needing it.\n *\n * The predicate was written for the runtime dispatcher's `/security` domain and\n * lived there, private. The **live** REST route\n * (`rest-server.ts` → `registerSecurityEndpoints`) is a second seam onto the\n * same service call and never had it, so `?status=garbage` reached the service,\n * matched no row, and answered **200 with an empty list** — which reads as\n * \"there are no suggestions\", a plausible and actionable-looking answer, rather\n * than \"your filter was not a status\". That silent arm is the defect; the two\n * seams disagreeing about one contract is the cause.\n *\n * So this module is the convergence, not a copy: `domains/security.ts` and\n * `rest-server.ts` both import from here, and the vocabulary — including the\n * refusal wording — exists once.\n *\n * The record is keyed BY the contract type on purpose (carried over from the\n * original): adding a status to `AudienceBindingSuggestionFilter` leaves a key\n * missing here and renaming one leaves a key excess, and either way this fails\n * to compile. A plain `['pending', …]` array would silently drift.\n */\n\nimport type { AudienceBindingSuggestionFilter } from '@objectstack/spec/contracts';\n\n/** The `status` arm of {@link AudienceBindingSuggestionFilter}, named. */\nexport type AudienceBindingSuggestionStatus = NonNullable<AudienceBindingSuggestionFilter['status']>;\n\n/** The accepted `?status=` values, keyed by the contract type (see module note). */\nexport const AUDIENCE_BINDING_SUGGESTION_STATUSES: Record<AudienceBindingSuggestionStatus, true> = {\n pending: true,\n confirmed: true,\n dismissed: true,\n};\n\n/**\n * The same vocabulary as a list — for refusal messages, and for tests that must\n * enumerate every valid value FROM the type rather than hand-picking one.\n */\nexport const AUDIENCE_BINDING_SUGGESTION_STATUS_VALUES = Object.keys(\n AUDIENCE_BINDING_SUGGESTION_STATUSES,\n) as readonly AudienceBindingSuggestionStatus[];\n\n/**\n * Is `value` one of the three statuses the contract declares? Case-sensitive on\n * purpose — the contract's values are lowercase, so `PENDING` is not a status\n * and gets the same refusal as `garbage`.\n */\nexport const isAudienceBindingSuggestionStatus = (\n value: string,\n): value is AudienceBindingSuggestionStatus =>\n Object.prototype.hasOwnProperty.call(AUDIENCE_BINDING_SUGGESTION_STATUSES, value);\n\n/** The refusal wording, shared so both seams answer an unknown status identically. */\nexport const unknownAudienceBindingSuggestionStatusMessage = (value: string): string =>\n `Unknown status filter '${value}' — expected one of: ${AUDIENCE_BINDING_SUGGESTION_STATUS_VALUES.join(', ')}`;\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [#7284] The `__` operation-private-key convention — one owner, on the\n * CONSUMER side.\n *\n * `assemble-execution-context.ts` next door is the single place an\n * `ExecutionContext` is BUILT at a transport entry point (#6216). This file is\n * its counterpart at the other end: the single place one is stripped back down\n * before being forwarded to a question it was not resolved for.\n *\n * ## What a `__` key is\n *\n * plugin-security's middleware STAMPS keys onto the operation context, resolved\n * for the object of the operation IN FLIGHT. They are middleware-private\n * vocabulary, not fields of `ExecutionContext`, and every one of them is read as\n * a WIDENING input by whoever consumes it:\n *\n * - the ADR-0057 D1 access DEPTH the sharing owner-match expands to —\n * `__readScope` / `__writeScope`, plus the ADR-0090 D10 delegator halves\n * `__delegatorReadScope` / `__delegatorWriteScope`, stamped in place by\n * `security-plugin.ts` (`sc.__readScope = …`);\n * - the engine's internal privilege markers on the same channel —\n * `__expandRead` marks a read as a lookup EXPANSION sub-read (it no longer\n * relaxes any gate — #7626 removed that waiver — but it still travels with\n * one operation and must not be inherited by another), `__referentialFieldClear`\n * authorizes the referential-clear write.\n *\n * plugin-security is the PRODUCER of that vocabulary and would be the most\n * honest owner of the rule for consuming it, but none of the three consumers\n * depends on it and a string-prefix filter does not justify three new dependency\n * edges onto a plugin (the trade the filing card priced, #7284). `@objectstack/\n * spec` is fenced off by Prime Directive #2. `@objectstack/core` is the only\n * candidate every consumer already depends on, so the rule lives here and the\n * producer stays free of reverse edges.\n *\n * ## Why a consumer must drop them\n *\n * A caller's envelope carries a depth resolved for the object the middleware\n * last saw. A consumer that forwards that envelope to ask about a DIFFERENT\n * object applies one object's widening to another object's question — the exact\n * stale-scope leak `resolveWriteScopeForSharing` was extracted to prevent (\"a\n * stale value can never leak in through a spread\", `security-plugin.ts`).\n *\n * The leak is not hypothetical and does not require the consumer to be careless:\n * plugin-security only OVERWRITES `__readScope` when it actually resolves\n * permission sets for the new object (`if (permissionSets.length > 0)`), so a\n * stale depth SURVIVES into a question it was never resolved for whenever that\n * branch does not fire. A REST request that touched another object before\n * reaching, say, `/reports/:id/run` hands over an envelope the middleware has\n * already written into.\n *\n * Dropping is safe in the one direction that matters: the middleware re-stamps\n * the depth for THIS object when it resolves any set, so the only thing dropping\n * can do is leave the sharing owner-match at its narrowest (`own`) — the safe\n * direction.\n *\n * ## Why by PREFIX and never by a name list\n *\n * The `__` convention is what marks a key as belonging to the operation in\n * flight. A hand-maintained list of the six names above would go stale the day\n * the middleware stamps a seventh, and it would go stale SILENTLY — a forwarded\n * key nobody remembered to add reads exactly like a key that was meant to be\n * forwarded. The prefix is the contract; the names are its current membership.\n *\n * ⛔ The corollary, for whoever changes the middleware: a key that is\n * operation-private MUST carry the `__` prefix. Stamping one without it makes it\n * invisible to every consumer at once, and there is no compiler error.\n *\n * ## Known consumers\n *\n * `plugin-audit` (`comment-access-hooks.ts`, #7141), `service-storage`\n * (`attachment-access-hooks.ts`, #7145) and `plugin-reports`\n * (`report-service.ts`, #7204) — each forwarding a caller envelope to a gate or\n * a read that asks about a parent/target object rather than about the object the\n * middleware resolved for. Each of the three grew its own byte-equivalent copy\n * of this file by hand before #7284 gave the rule a home; `operation-private-\n * keys.pin.test.ts` is what now catches a fourth.\n */\n\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\n\n/**\n * The prefix marking a key as private to the operation plugin-security has in\n * flight. See this module's header for why the convention is a prefix and not a\n * list of names.\n */\nexport const OPERATION_PRIVATE_KEY_PREFIX = '__';\n\n/**\n * The caller's execution envelope, minus the operation-private keys.\n *\n * A FRESH object every time, and that is load-bearing in BOTH directions:\n *\n * - outbound — a callee that stamps its own `__writeScope` onto what it\n * receives (which is exactly what plugin-security does before it calls the\n * sharing service) can never write back into the operation context the caller\n * was handed;\n * - inbound — the engine's middleware stamps a depth for the object it is about\n * to read onto whatever it is handed, so forwarding a caller's envelope BY\n * REFERENCE would write that depth back into the request context the route\n * goes on using.\n *\n * ⛔ Never `return exec;` on the \"nothing to strip\" path. The copy is the point,\n * not an optimisation to skip when the envelope happens to be clean — the two\n * hazards above are about the callee's future writes, not about the current\n * contents.\n *\n * Note what this deliberately does NOT do: it strips, it never SYNTHESISES a\n * depth for the new object. Absent depth leaves the sharing owner-match at its\n * narrowest (`own`), which is the safe direction and byte-for-byte what the\n * five-field projections these call sites replaced produced.\n *\n * @param exec the caller's envelope, as a bare record\n * @returns a new envelope carrying only the non-operation-private keys\n */\nexport function withoutOperationPrivateKeys(exec: Record<string, unknown>): ExecutionContext {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(exec)) {\n if (key.startsWith(OPERATION_PRIVATE_KEY_PREFIX)) continue;\n out[key] = value;\n }\n return out as ExecutionContext;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Timezone-aware calendar utilities (ADR-0053 Phase 2).\n *\n * The one primitive everything else builds on is {@link calendarPartsInTz}:\n * the year/month/day an instant falls on *as seen in a reference timezone*.\n * It uses `Intl.DateTimeFormat().formatToParts()` so DST transitions are\n * handled by the platform's tz database — never hand-rolled offset math, which\n * is the classic source of off-by-one-hour bucket errors.\n *\n * This lives in `@objectstack/core` (not `@objectstack/formula`) because both\n * the ObjectQL aggregation engine and the analytics service need it and both\n * already depend on core, whereas neither depends on formula's public surface.\n * (`@objectstack/formula` keeps its own private copy for `today()`/`daysFromNow`\n * to avoid a layering dependency on core.)\n */\n\n/** Calendar-day parts in a reference timezone. `month` is 1-12. */\nexport interface CalendarParts {\n year: number;\n month: number;\n day: number;\n}\n\n/**\n * The year/month/day an instant falls on in `tz`. Throws if `tz` is not a\n * valid IANA zone (callers treat that as a fall-through to UTC).\n */\nexport function calendarPartsInTz(d: Date, tz: string): CalendarParts {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(d);\n const get = (t: string) => Number(parts.find((p) => p.type === t)?.value);\n return { year: get('year'), month: get('month'), day: get('day') };\n}\n\n/**\n * The calendar-day parts of an instant, in `tz` when it's a real non-UTC zone,\n * otherwise in UTC. Never throws: an unset, `'UTC'`, or invalid zone falls back\n * to the UTC calendar day. This is the safe entry point for bucketing code that\n * must degrade to the historical UTC behavior rather than error.\n */\nexport function calendarPartsInTzOrUtc(d: Date, tz?: string): CalendarParts {\n if (tz && tz !== 'UTC') {\n try {\n return calendarPartsInTz(d, tz);\n } catch {\n // unknown zone → fall through to UTC\n }\n }\n return {\n year: d.getUTCFullYear(),\n month: d.getUTCMonth() + 1,\n day: d.getUTCDate(),\n };\n}\n\n/**\n * The UTC instant (epoch ms) at which calendar day `ymd` (`YYYY-MM-DD`) *begins*\n * in reference timezone `tz` — i.e. local **midnight** of that day rendered as a\n * UTC instant. The inverse direction of {@link calendarPartsInTz}.\n *\n * DST-safe: the zone offset is read from the platform tz database via\n * `Intl.DateTimeFormat` (never hand-computed), and a two-pass resolution settles\n * the rare case where the offset differs side-to-side of the target instant. An\n * unset, `'UTC'`, invalid, or unparseable input returns plain UTC midnight.\n *\n * Used by date-bucket drill ranges (#1752): a `datetime` field buckets on the\n * reference-tz calendar, so its bucket boundary is that tz's midnight instant.\n */\nexport function zonedDateStartToUtcMs(ymd: string, tz?: string): number {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(ymd);\n const wallAsUtc = m ? Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : NaN;\n if (!tz || tz === 'UTC' || Number.isNaN(wallAsUtc)) return wallAsUtc;\n try {\n // The tz offset (local − UTC, in ms) at instant `t`: read t's wall clock in\n // `tz`, re-interpret those parts as UTC, and subtract t.\n const offsetAt = (t: number): number => {\n const p = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n hourCycle: 'h23',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n }).formatToParts(new Date(t));\n const g = (k: string) => Number(p.find((x) => x.type === k)?.value);\n return Date.UTC(g('year'), g('month') - 1, g('day'), g('hour'), g('minute'), g('second')) - t;\n };\n // Want U such that localParts(U) == midnight, i.e. U = wallAsUtc − offset(U).\n // Iterate from the zero-offset guess; converges in ≤2 steps off a DST edge.\n const off1 = offsetAt(wallAsUtc - offsetAt(wallAsUtc));\n return wallAsUtc - off1;\n } catch {\n return wallAsUtc; // unknown zone → UTC midnight\n }\n}\n\n/**\n * Calendar-day bound semantics (ADR-0053 D-D) now live in `@objectstack/spec`,\n * beside the date-macro vocabulary they give meaning to — the fifth consumer\n * (`@objectstack/formula`'s RLS write-side `check` evaluator) cannot depend on\n * this package, and a second copy of the rule is exactly the divergence #3777\n * catalogued.\n *\n * Re-exported here so the published `@objectstack/core` surface is unchanged\n * for the drivers and analytics strategies that already import it from here.\n */\nexport { nextUtcCalendarDay, utcInstantMs } from '@objectstack/spec/data';\n\n/**\n * Granularity of a canonical date-bucket key. Mirrors `@objectstack/spec`'s\n * `DateGranularity` enum but kept as a local literal union so this low-level\n * package needs no dependency on spec.\n */\nexport type BucketGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year';\n\n/**\n * ISO-8601 week label (Mon-start weeks, week 1 = the week of the first\n * Thursday) of a UTC calendar day. The forward-direction companion used to\n * *validate* a reconstructed week boundary; it mirrors the week branch of\n * `@objectstack/objectql`'s `bucketDateValue` (kept in lockstep by the\n * round-trip parity test in objectql).\n */\nfunction isoWeekLabelUtc(d: Date): string {\n const target = new Date(d.getTime());\n const dayNum = (target.getUTCDay() + 6) % 7; // Mon=0..Sun=6\n target.setUTCDate(target.getUTCDate() - dayNum + 3); // shift to that week's Thursday\n const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));\n const weekNo =\n 1 +\n Math.round(\n ((target.getTime() - firstThursday.getTime()) / 86400000 -\n 3 +\n ((firstThursday.getUTCDay() + 6) % 7)) /\n 7,\n );\n return `${target.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n\n/**\n * The half-open calendar span `[start, end)` of a canonical date-bucket KEY,\n * as `YYYY-MM-DD` strings (`start` inclusive, `end` exclusive — the next\n * bucket's first day).\n *\n * The input MUST be the canonical key produced by `bucketDateValue` /\n * `buildDateBucketExpr` (`2026`, `2026-Q2`, `2026-06`, `2026-06-15`,\n * `2026-W23`) — NEVER a localized / humanized display label. The span is pure,\n * timezone-naive calendar arithmetic; a caller that needs instant bounds for a\n * `datetime` field in a reference timezone layers that on top (and, per\n * ADR-0053, a `date` field compares against these `YYYY-MM-DD` bounds directly).\n *\n * Returns `null` for the empty bucket, an unparseable key, or a key that is\n * shape-valid but out of range (e.g. `2026-13`, a `-W53` in a 52-week year,\n * `2026-02-30`). Callers drop the range and fall back to an unscoped (superset)\n * drill rather than emit a wrong bound.\n *\n * `key` admits `null` because that IS the empty bucket's key on both aggregation\n * paths (#3839); callers pass a grouped row's dimension value straight through\n * rather than casting a lie.\n */\nexport function bucketKeyToCalendarRange(\n key: string | null | undefined,\n granularity: BucketGranularity,\n): { start: string; end: string } | null {\n if (typeof key !== 'string' || key.length === 0) return null;\n const fmt = (dt: Date) =>\n `${String(dt.getUTCFullYear()).padStart(4, '0')}-${String(dt.getUTCMonth() + 1).padStart(\n 2,\n '0',\n )}-${String(dt.getUTCDate()).padStart(2, '0')}`;\n\n switch (granularity) {\n case 'year': {\n const m = /^(\\d{4})$/.exec(key);\n if (!m) return null;\n const y = Number(m[1]);\n return { start: fmt(new Date(Date.UTC(y, 0, 1))), end: fmt(new Date(Date.UTC(y + 1, 0, 1))) };\n }\n case 'quarter': {\n const m = /^(\\d{4})-Q([1-4])$/.exec(key);\n if (!m) return null;\n const y = Number(m[1]);\n const startMonth = (Number(m[2]) - 1) * 3; // Q1→0, Q2→3, Q3→6, Q4→9\n return {\n start: fmt(new Date(Date.UTC(y, startMonth, 1))),\n end: fmt(new Date(Date.UTC(y, startMonth + 3, 1))), // Date.UTC rolls Q4 into next year\n };\n }\n case 'month': {\n const m = /^(\\d{4})-(\\d{2})$/.exec(key);\n if (!m) return null;\n const mo = Number(m[2]);\n if (mo < 1 || mo > 12) return null;\n const y = Number(m[1]);\n return {\n start: fmt(new Date(Date.UTC(y, mo - 1, 1))),\n end: fmt(new Date(Date.UTC(y, mo, 1))),\n };\n }\n case 'day': {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(key);\n if (!m) return null;\n const y = Number(m[1]);\n const mo = Number(m[2]);\n const d = Number(m[3]);\n const start = new Date(Date.UTC(y, mo - 1, d));\n if (fmt(start) !== key) return null; // reject an impossible day that rolled over\n return { start: key, end: fmt(new Date(Date.UTC(y, mo - 1, d + 1))) };\n }\n case 'week': {\n const m = /^(\\d{4})-W(\\d{2})$/.exec(key);\n if (!m) return null;\n const isoYear = Number(m[1]);\n const week = Number(m[2]);\n if (week < 1 || week > 53) return null;\n // Monday of ISO week 1 is the Monday on/before Jan 4; add (week-1) weeks.\n const jan4 = new Date(Date.UTC(isoYear, 0, 4));\n const jan4Dow = (jan4.getUTCDay() + 6) % 7; // Mon=0..Sun=6\n const start = new Date(jan4.getTime());\n start.setUTCDate(jan4.getUTCDate() - jan4Dow + (week - 1) * 7);\n if (isoWeekLabelUtc(start) !== key) return null; // reject -W53 overflow etc.\n const end = new Date(start.getTime());\n end.setUTCDate(start.getUTCDate() + 7);\n return { start: fmt(start), end: fmt(end) };\n }\n default:\n return null;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `bulkWrite` — the shared batched-write helper used by BOTH the seed loader\n * (`@objectstack/metadata-protocol`) and the data-import runner\n * (`@objectstack/rest`), so neither reimplements batching, transient-error\n * retry, or per-row degradation. See framework#2678.\n *\n * ObjectQL's engine already does the efficient thing when handed an ARRAY —\n * one `driver.bulkCreate` round-trip plus parent-deduplicated summary\n * recompute (`engine.insert(object, rows[])`) — but seed/import fed it one\n * record at a time, so neither got the benefit. This module re-chunks rows\n * into batches and drives them through a caller-supplied batch-write\n * function, adding:\n *\n * - transient-error retry (network blip / timeout) with exponential\n * backoff, so a dropped connection doesn't silently drop the row (the\n * 2026-07-06 HotCRM incident: a turso `fetch failed` mid-seed dropped rows\n * silently because nothing retried);\n * - per-row degradation when a batch fails for a non-transient (logical /\n * validation) reason, so one bad row can't fail the other N-1 — needed\n * because `driver.bulkCreate` is a single multi-row statement/`Promise.all`\n * on every driver in this repo (sql, memory, mongodb): one bad row fails\n * the whole call;\n * - a stable per-row result keyed by the row's original index, so callers\n * can reassemble output in input order even though rows are processed in\n * batches (and a batch's flush may be interleaved with other, immediate,\n * per-row work such as updates).\n *\n * Delivery semantics: **at-least-once**. Transient retry and per-row\n * degradation both RE-RUN a write whose outcome was unknown — e.g. a turso\n * `fetch failed` that arrived *after* the row was already committed\n * (framework#3149), or a result-count mismatch that voids the batch\n * (framework#3151). A caller that needs exactly-once must make its\n * `writeBatch`/`writeOne` idempotent; both receive an `attempt` counter for\n * exactly this — see the natural-key recheck the seed loader and import\n * runner perform on `attempt > 1`. `writeBatch` MUST also resolve exactly one\n * record per input row, in input order: a short / long / non-array return is\n * rejected as a failed batch (framework#3151), never silently backfilled.\n */\n\nexport interface BulkWriteRowResult<TRecord = any> {\n /** Index into the original `rows` array passed to {@link bulkWrite}. */\n index: number;\n ok: boolean;\n record?: TRecord;\n error?: unknown;\n}\n\nexport interface RetryOptions {\n /** Max attempts for one write (batch or single-row), including the first. Default 3. */\n maxRetries?: number;\n /** Base backoff in ms; doubled each retry, plus jitter. Default 200. */\n backoffBaseMs?: number;\n /** Classifies an error as transient (worth retrying) vs logical (the row/batch is just bad). */\n isTransientError?: (err: unknown) => boolean;\n /** Injectable sleep, for deterministic tests. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface BulkWriteOptions<TRow, TRecord = any> extends RetryOptions {\n /** Rows per batch. Default 200 (framework#2678 suggests 100-500). */\n batchSize?: number;\n /**\n * Write one batch. MUST resolve to one record per input row, in the SAME\n * order as `batch` — {@link bulkWrite} correlates `records[i]` back to\n * `batch[i]` positionally (this is how every `bulkCreate` implementation in\n * this repo already behaves: sql's single `INSERT ... VALUES (...), (...)\n * RETURNING *`, memory's `Promise.all`, mongodb's ordered `insertMany`).\n *\n * `ctx.attempt` is the 1-based attempt number. `attempt > 1` means a prior\n * attempt's outcome is UNKNOWN (a transient blip that may have landed after\n * commit) — an exactly-once caller should recheck by natural key and skip\n * rows already present before re-writing (framework#3149).\n */\n writeBatch: (batch: TRow[], ctx: { attempt: number }) => Promise<TRecord[]>;\n /**\n * Write a single row — used only to degrade a failed batch. `ctx.attempt`\n * carries the same recheck signal as {@link writeBatch}.\n */\n writeOne: (row: TRow, ctx: { attempt: number }) => Promise<TRecord>;\n /**\n * Partial-success batch write (framework#3172). When provided it is used\n * INSTEAD of {@link writeBatch}: it must resolve one outcome per input row,\n * in input order — `{ ok: true, record }` for written rows, `{ ok: false,\n * error }` for rows that failed individually (e.g. validation). Per-row\n * failures are final verdicts: bulkWrite records them as-is and does NOT\n * degrade to `writeOne` for them — that is the whole point (a degradation\n * re-run would re-fire beforeInsert hooks on the good rows). Only a THROWN\n * error (a transient infra failure, a result-count mismatch) falls back to\n * the per-row `writeOne` degradation, exactly like `writeBatch`.\n */\n writeBatchPartial?: (\n batch: TRow[],\n ctx: { attempt: number },\n ) => Promise<Array<{ ok: boolean; record?: TRecord; error?: unknown }>>;\n}\n\nconst DEFAULT_BATCH_SIZE = 200;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_BACKOFF_BASE_MS = 200;\n\n/**\n * Transient-error signatures shared by common HTTP/TCP-backed drivers\n * (turso/libsql's fetch-based transport included). Deliberately excludes\n * anything that looks like a validation/constraint error — those must NOT be\n * retried, only degraded to per-row.\n */\nconst TRANSIENT_PATTERNS: RegExp[] = [\n /fetch failed/i,\n /network/i,\n /timed?\\s*out/i,\n /timeout/i,\n /socket hang ?up/i,\n /connection.*(closed|reset|refused|terminated|aborted)/i,\n /\\b(502|503|504)\\b/,\n /server.*unavailable/i,\n /too many connections/i,\n];\n\nconst TRANSIENT_CODES = /^(ECONNRESET|ECONNREFUSED|ECONNABORTED|EPIPE|EAI_AGAIN|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|ENOTFOUND)$/i;\n\n/**\n * Validation / constraint / schema signatures that are DEFINITIVELY logical,\n * never worth retrying. Checked before {@link TRANSIENT_PATTERNS} so a message\n * that happens to mention both (e.g. `CHECK constraint failed: network_zone`,\n * `column network_id is not allowed`) is classified as logical rather than\n * burning retries on a row that will fail identically every time (framework\n * #3150).\n */\nconst NON_TRANSIENT_PATTERNS: RegExp[] = [\n /validation/i,\n /constraint/i,\n /\\brequired\\b/i,\n /\\bunique\\b/i,\n /duplicate/i,\n /not[\\s_-]*null/i,\n /invalid/i,\n /not allowed/i,\n /out of range/i,\n];\n\nexport function defaultIsTransientError(err: unknown): boolean {\n const message = (err as { message?: unknown } | null)?.message;\n const text = typeof message === 'string' ? message : String(err ?? '');\n // A definitive logical signature wins even if a transient word also appears.\n if (NON_TRANSIENT_PATTERNS.some((re) => re.test(text))) return false;\n const code = (err as { code?: unknown } | null)?.code;\n if (typeof code === 'string' && TRANSIENT_CODES.test(code)) return true;\n return TRANSIENT_PATTERNS.some((re) => re.test(text));\n}\n\nconst defaultSleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\ninterface ResolvedRetryOptions {\n maxRetries: number;\n backoffBaseMs: number;\n isTransientError: (err: unknown) => boolean;\n sleep: (ms: number) => Promise<void>;\n}\n\nasync function withRetry<T>(fn: (attempt: number) => Promise<T>, opts: ResolvedRetryOptions): Promise<T> {\n let lastError: unknown;\n for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {\n try {\n return await fn(attempt);\n } catch (err) {\n lastError = err;\n if (attempt >= opts.maxRetries || !opts.isTransientError(err)) throw err;\n const jitter = Math.floor(Math.random() * 50);\n await opts.sleep(opts.backoffBaseMs * 2 ** (attempt - 1) + jitter);\n }\n }\n // Unreachable — the loop above always returns or throws — but keeps TS's\n // control-flow analysis happy about a guaranteed return type.\n throw lastError;\n}\n\n/**\n * Retry a single write (e.g. an `engine.update()` call the seed loader or\n * import runner makes outside the batched-insert path) with the same\n * transient-error backoff {@link bulkWrite} applies to batches — so a\n * network blip doesn't drop an update the way it used to drop an insert.\n */\nexport async function withTransientRetry<T>(fn: (attempt: number) => Promise<T>, opts: RetryOptions = {}): Promise<T> {\n return withRetry(fn, {\n maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),\n backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,\n isTransientError: opts.isTransientError ?? defaultIsTransientError,\n sleep: opts.sleep ?? defaultSleep,\n });\n}\n\n/**\n * Write `rows` through `opts.writeBatch` in chunks of `opts.batchSize`,\n * retrying a whole-batch transient failure with backoff, and degrading to\n * per-row `opts.writeOne` calls (each itself retried) when a batch fails for\n * a non-transient reason — so one bad row can't drop the rest of the batch.\n *\n * Returns one {@link BulkWriteRowResult} per input row, indexed to match\n * `rows`' original order.\n */\nexport async function bulkWrite<TRow, TRecord = any>(\n rows: TRow[],\n opts: BulkWriteOptions<TRow, TRecord>,\n): Promise<BulkWriteRowResult<TRecord>[]> {\n const batchSize = Math.max(1, opts.batchSize ?? DEFAULT_BATCH_SIZE);\n const retryOpts: ResolvedRetryOptions = {\n maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),\n backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,\n isTransientError: opts.isTransientError ?? defaultIsTransientError,\n sleep: opts.sleep ?? defaultSleep,\n };\n\n const results: BulkWriteRowResult<TRecord>[] = new Array(rows.length);\n\n for (let start = 0; start < rows.length; start += batchSize) {\n const batch = rows.slice(start, start + batchSize);\n try {\n // Partial-success path (framework#3172): one call yields a final per-row\n // verdict, so a row that fails validation never triggers the whole-batch\n // degradation that re-runs beforeInsert hooks on its siblings.\n if (opts.writeBatchPartial) {\n const outcomes = await withRetry((attempt) => opts.writeBatchPartial!(batch, { attempt }), retryOpts);\n if (!Array.isArray(outcomes) || outcomes.length !== batch.length) {\n throw Object.assign(\n new Error(\n `bulkWrite: writeBatchPartial returned ${\n Array.isArray(outcomes) ? `${outcomes.length} outcome(s)` : String(typeof outcomes)\n } for a ${batch.length}-row batch — treating batch as failed`,\n ),\n { code: 'ERR_BULK_RESULT_MISMATCH' },\n );\n }\n for (let i = 0; i < batch.length; i++) {\n const o = outcomes[i];\n results[start + i] = o.ok\n ? { index: start + i, ok: true, record: o.record }\n : { index: start + i, ok: false, error: o.error };\n }\n continue;\n }\n const records = await withRetry((attempt) => opts.writeBatch(batch, { attempt }), retryOpts);\n // Contract guard (framework#3151): `writeBatch` must resolve one record\n // per input row. A short / long / non-array return breaks the positional\n // correlation below, so backfilling it would report phantom successes\n // (`record: undefined`) or drop records. Treat the whole batch as failed\n // and fall through to per-row degradation (each row re-attempted via\n // `writeOne`, which under an idempotent caller rechecks before writing).\n // The message deliberately avoids any transient signature so this never\n // reads as a retryable blip — and it is thrown *outside* `withRetry`, so\n // the batch is not retried on it.\n if (!Array.isArray(records) || records.length !== batch.length) {\n throw Object.assign(\n new Error(\n `bulkWrite: writeBatch returned ${\n Array.isArray(records) ? `${records.length} record(s)` : String(typeof records)\n } for a ${batch.length}-row batch — treating batch as failed`,\n ),\n { code: 'ERR_BULK_RESULT_MISMATCH' },\n );\n }\n for (let i = 0; i < batch.length; i++) {\n results[start + i] = { index: start + i, ok: true, record: records[i] };\n }\n } catch (batchErr) {\n // A single-row \"batch\" already IS the per-row attempt — its failure\n // (after transient retry) is the row's final outcome; calling\n // `writeOne` again would just repeat the identical work.\n if (batch.length === 1) {\n results[start] = { index: start, ok: false, error: batchErr };\n continue;\n }\n // The batch failed even after transient retry, or failed for a logical\n // reason retry wouldn't fix. Degrade to per-row so one bad row can't\n // fail the other rows in this batch. Each row still gets its own\n // transient retry — the batch-level failure doesn't tell us which row\n // (if any) was actually the transient one.\n for (let i = 0; i < batch.length; i++) {\n const idx = start + i;\n try {\n const record = await withRetry((attempt) => opts.writeOne(batch[i], { attempt }), retryOpts);\n results[idx] = { index: idx, ok: true, record };\n } catch (err) {\n results[idx] = { index: idx, ok: false, error: err };\n }\n }\n }\n }\n\n return results;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [#7823 / #8497] The write-response half of the `internal: true` guarantee —\n * THE single helper every write mouth that returns a body to an external\n * caller passes its record(s) through.\n *\n * ## The contract\n *\n * A field declared `internal: true` is *never returned on the generic data\n * path* (#7728). The READ half lives in the engine (`omitInternalFields` runs\n * on every find/findOne result). The WRITE-RESPONSE half lives HERE.\n *\n * ## Why the ingress and not the engine (the measured history, #7823)\n *\n * The first shape stripped `internal` fields inside the engine's insert and\n * by-id-update paths. That conflated two different guarantees:\n *\n * - \"never returned on the generic data path\" — the flag's sentence, about\n * what an EXTERNAL caller receives; and\n * - \"never returned to the engine-level caller that performed the write\" —\n * which no ruling ever asked for, and which is FALSE for credential mint:\n * better-auth's `createWithHooks` reads the minted `sys_session` row back\n * off the insert result, so the engine-side strip broke `signIn`/`signUp`\n * outright (measured: `verify signIn: no token in response`).\n *\n * Plain removal of the engine limbs was ALSO measured wrong: the by-id-update\n * strip was the sole closure of #7728's fourth surface — with it neutralised,\n * `PATCH /data/sys_api_key/{id}` answered 200 with the stored 64-hex `key`\n * hash in the body. Both measurements are satisfiable at exactly one boundary:\n * the mouth that builds the external 201/200 body. Engine write results keep\n * the stored row whole (mint works); every external write response is stripped\n * through this helper (the hash never leaves); the read path is untouched.\n *\n * ## Why this module sits in `@objectstack/core` (#8497)\n *\n * It shipped inside `@objectstack/metadata-protocol`, next to the protocol\n * class that was then its only caller. That placement encoded an assumption\n * the surface does not honour: **the generic write mouths are not all on the\n * protocol class.** Two transports reach the engine directly —\n *\n * - `@objectstack/rest` (`rest-server.ts`, the cross-object `POST /batch`\n * update arm's direct `ql.update`), and\n * - `@objectstack/mcp` (`stdio-data-bridge.ts`, whose `create` handed the\n * engine's insert result straight back to the MCP client — a MEASURED leak\n * of the flagged column, found by widening this guard's scope in #8497),\n *\n * — and neither package depends on `@objectstack/metadata-protocol`. The old\n * home therefore forced each new mouth to choose between a duck-typed reach\n * through a protocol instance (what `rest` does) and a private restatement of\n * the rule (a third copy of a security-relevant predicate). `@objectstack/core`\n * is the floor all three already depend on, and it already hosts exactly this\n * class of shared write-path helper (`bulk-write.ts`, used by both\n * `metadata-protocol` and `rest` so neither reimplements batching). One helper,\n * reachable from every mouth, is the whole point of the flag being structural.\n *\n * `@objectstack/metadata-protocol` re-exports both functions unchanged, so its\n * public API is byte-identical across the move.\n *\n * ## The residual risk, and what gates it\n *\n * Response-body policy at the mouth means a FUTURE write mouth that forgets\n * this helper leaks silently. Three tripwires hold the property, each an\n * enumeration no author can dodge by adding code without touching it:\n *\n * - `protocol.write-response-internal-fields.tripwire.test.ts`\n * (`metadata-protocol`) walks the protocol class's prototype for `*Data`\n * faces;\n * - `rest-write-response-internal-fields.tripwire.test.ts` (`rest`) walks\n * `RestServer.getRoutes()` for HTTP write routes;\n * - `mcp-write-response-internal-fields.tripwire.test.ts` (`mcp`) walks the\n * `McpDataBridge` write faces.\n *\n * Together they assert the PROPERTY — \"no response body an external caller\n * receives from a write carries an `internal: true` value\" — rather than the\n * shape of any one class. Adding a write mouth? Route its response records\n * through this helper and register it with the tripwire that enumerates its\n * surface.\n *\n * ## Semantics\n *\n * Mirrors the engine's `collectInternalReadFields` rule exactly — a field\n * participates iff its declaration carries `internal === true` (strict\n * boolean; truthy strings and numbers do not count, same as the engine).\n * `@objectstack/core` cannot import that collector (`@objectstack/objectql`\n * sits above this package), so the rule is restated here in full;\n * `internal-fields.test.ts` in objectql and the tripwires above pin the same\n * spelling from both sides. OMIT, not mask, for the #7728 reasons: the flag's\n * columns are `required`, so a mask carries zero bits while still shipping a\n * value under a field whose description promises none.\n *\n * Deletion is IN PLACE and idempotent: records that already lack the field\n * (a re-stripped read result, a fake engine that never returned it) pass\n * through unchanged, and non-record values (`null`, an affected-row count, a\n * driver's boolean delete verdict) are skipped rather than judged.\n */\n\n/** Minimal view of an object schema this module reads — the field map only. */\ninterface SchemaWithFields {\n fields?: Record<string, { internal?: unknown } | undefined> | undefined;\n}\n\n/**\n * Collect the names of fields declared `internal: true` on `schema`.\n *\n * Same verdicts as objectql's `collectInternalReadFields` (see the module\n * header for why it is restated rather than imported): strict `=== true`,\n * empty result for a missing/field-less schema.\n */\nexport function collectInternalWriteResponseFields(schema: unknown): string[] {\n const fields = (schema as SchemaWithFields | null | undefined)?.fields;\n if (!fields || typeof fields !== 'object') return [];\n const out: string[] = [];\n for (const [name, def] of Object.entries(fields)) {\n if (def && def.internal === true) out.push(name);\n }\n return out;\n}\n\n/**\n * Drop every `internal: true` field from a write response's record(s), in\n * place. THE single helper every external write mouth goes through — see the\n * module header; the three tripwires enforce the \"every\".\n *\n * @param schema The registered object schema (`engine.registry.getObject(...)`\n * / the protocol's own registry view / `metadataService\n * .getObject(...)`). An unknown object (no schema) strips\n * nothing — the write itself would have been refused upstream\n * by the object-existence gate.\n * @param records A single record, an array of records, or anything a write\n * mouth hands back where a record could sit (`null`, a count, a\n * boolean): non-objects are skipped, arrays are walked.\n */\nexport function omitInternalFieldsFromWriteResponse(schema: unknown, records: unknown): void {\n if (!records) return;\n const internalFields = collectInternalWriteResponseFields(schema);\n if (internalFields.length === 0) return;\n const list = Array.isArray(records) ? records : [records];\n for (const row of list) {\n if (!row || typeof row !== 'object') continue;\n for (const field of internalFields) delete (row as Record<string, unknown>)[field];\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `runMigrationJournal` — the framework-owned runner for data migrations that\n * are too big, too long, or too multi-step to live in one transaction\n * (ADR-0119 D2, #4617).\n *\n * ## Why this is framework-owned rather than four hand-rolled copies\n *\n * Four migration-class consumers independently converged on the same four\n * moves — dry-run preflight, an undo journal, LIFO compensation, re-entrant\n * forward recovery: ADR-0105 D13 promotion, ADR-0117 D8's ownership backfill,\n * the org lifecycle transitions, and ADR-0119 D10's master-data distribution\n * (#4585). One copy is engineering. Four is platform debt, and the fourth\n * author would have had to rediscover the `chunk_done`-inside-the-transaction\n * subtlety below from scratch — or, far more likely, not rediscover it.\n *\n * ## Why a journal at all, given ADR-0034 gave us transactions\n *\n * ADR-0119 D1 made `engine.transaction()` reachable through the contract, but\n * a transaction cannot be the whole answer here:\n *\n * - a million-row backfill cannot hold one write-lock for its duration;\n * - `driver-memory`'s `beginTransaction` deep-clones the entire database, so\n * \"just wrap the whole thing\" is O(db) per begin;\n * - `ObjectQL.transaction()` binds the DEFAULT driver only, so a migration\n * spanning datasources silently commits part of its work outside it;\n * - a process KILLED — as distinct from a thrown error — defeats in-process\n * rollback entirely, and that is the case operators actually hit.\n *\n * So the unit of atomicity is the CHUNK, and durability across chunks is the\n * journal. Everything else in this file follows from that one sentence.\n *\n * ## The invariant that carries the whole design\n *\n * `chunk_done(i)` is written INSIDE the chunk's own transaction, so\n * `done ⇔ committed` holds by construction rather than by luck.\n * `chunk_started(i)` is written autonomously BEFORE it. A reader who \"tidies\"\n * that asymmetry destroys recovery: it is what gives `started ∧ ¬done` exactly\n * one meaning — **the outcome is unknown** — which is the only state a crash\n * can leave and the only state recovery has to reason about.\n *\n * ## Delivery semantics: at-least-once, idempotency is the caller's job\n *\n * Inherited verbatim from `./bulk-write.ts` rather than re-derived, because a\n * second delivery-semantics story in the same codebase is a second thing to\n * get subtly wrong. Forward and compensate callbacks receive an `attempt`\n * counter; `attempt > 1` means the previous outcome is UNKNOWN — the write may\n * or may not have committed — and the callback must recheck by natural key\n * before re-writing. That is the same contract the seed loader and import\n * runner already honour on `attempt > 1`.\n */\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport type { IObjectQLEngine } from '@objectstack/spec/contracts';\nimport {\n MIGRATION_JOURNAL_OBJECT,\n type MigrationJournalEvent,\n type MigrationJournalKind,\n type MigrationOnCrashPolicy,\n} from '@objectstack/spec/system';\n\n/** Journal writes and recovery reads run as the platform, never as a user. */\nconst SYSTEM_CTX = { isSystem: true } as const;\n\n/** Rows per chunk when a plan does not choose. Matches `bulk-write.ts`. */\nconst DEFAULT_CHUNK_SIZE = 200;\n\n/**\n * Can this runtime actually roll back? — the ADR-0119 D4 gate, shared.\n *\n * Exported from `@objectstack/core` and consumed by\n * `@objectstack/metadata-protocol`'s `batchData` (which depends on core, so\n * the direction is legal) so the two cannot drift. They were the same two-line\n * condition written twice, which is precisely the shape that drifts by one\n * clause and leaves one caller believing it has atomicity it does not have.\n *\n * TWO levels, both necessary. `engine.transaction()` exists but runs the\n * callback with NO transaction and NO rollback when the default driver lacks\n * `beginTransaction` — a declared caveat of the contract member (ADR-0119 D1),\n * and one that turns \"atomic\" back into a lie precisely where it matters. So\n * where the driver registry is inspectable the driver is checked too; where it\n * is not (test doubles), the engine-level probe is all there is.\n *\n * A type predicate, not a bare boolean: every caller's next move is to CALL\n * `transaction`, and on the host surfaces that declare it optionally\n * (`MetadataHostEngine`) a boolean would leave each one re-narrowing by hand —\n * which is the same restatement this helper exists to remove.\n */\nexport function engineCanRollBack<T>(engine: T): engine is T & EngineWithTransaction {\n const e = engine as {\n transaction?: unknown;\n getDefaultDriverName?: () => string | undefined;\n getDriverByName?: (name: string) => unknown;\n } | null | undefined;\n if (typeof e?.transaction !== 'function') return false;\n const defaultDriverName = e.getDefaultDriverName?.();\n const defaultDriver = defaultDriverName ? e.getDriverByName?.(defaultDriverName) : undefined;\n return !defaultDriver || typeof (defaultDriver as { beginTransaction?: unknown }).beginTransaction === 'function';\n}\n\n/**\n * What {@link engineCanRollBack} proves is present.\n *\n * Typed FROM the contract rather than transcribed from it (#5696): a hand-copy\n * mirrors the signature only until the contract moves, and this one had already\n * started to — it predates `opts.require` and the callback's `owned` argument.\n * ADR-0119 D1 blessed exactly this shape for the narrow host surfaces\n * (`transaction?: IObjectQLEngine['transaction']`); a *narrow* surface may stay\n * narrow, but it may not drift from the real signature.\n */\nexport interface EngineWithTransaction {\n transaction: IObjectQLEngine['transaction'];\n}\n\n/** What a forward/compensate callback is told about the chunk it is running. */\nexport interface MigrationChunkContext {\n readonly runId: string;\n /** Run-global chunk index — the LIFO ordering key, stable across a resume. */\n readonly chunkIndex: number;\n /**\n * 1 on the first try. `> 1` means a previous attempt's outcome is UNKNOWN:\n * recheck by natural key before re-writing (see this file's header).\n */\n readonly attempt: number;\n /**\n * The transaction-bound execution context. Thread it to every engine call\n * this callback makes — `engine.insert(obj, row, { context })` — so the\n * write joins the chunk's transaction instead of committing beside it.\n */\n readonly context: unknown;\n}\n\n/** One step of a plan. Steps run in declaration order; each is chunked. */\nexport interface MigrationPlanStep<TRow = unknown> {\n readonly name: string;\n /**\n * Read-only preflight. Throw to refuse the run. Runs for EVERY step before\n * any step writes — a plan that would fail at step 3 must not have written\n * step 1 (ADR-0117 D8's fail-closed enable gate, generalized).\n */\n preflight?(engine: IObjectQLEngine): Promise<void>;\n /** The rows this step processes. Called once, before chunking. */\n load(engine: IObjectQLEngine): Promise<TRow[]>;\n /** Forward work for one chunk. Runs INSIDE the chunk's transaction. */\n forward(rows: TRow[], ctx: MigrationChunkContext, engine: IObjectQLEngine): Promise<void>;\n /**\n * Undo one previously-committed chunk. Runs in its OWN transaction.\n * A step without one makes the plan non-compensable — which the runner\n * refuses up front rather than discovering at the worst possible moment\n * (see {@link runMigrationJournal}'s preflight).\n */\n compensate?(rows: TRow[], ctx: MigrationChunkContext, engine: IObjectQLEngine): Promise<void>;\n}\n\nexport interface MigrationPlan {\n /** Stable plan id. Part of the plan hash; identifies the plan across runs. */\n readonly id: string;\n /** Optional join to `sys_migration.id` when this plan implements a named migration. */\n readonly migrationId?: string;\n readonly steps: ReadonlyArray<MigrationPlanStep<any>>;\n readonly chunkSize?: number;\n /**\n * What a REDISCOVERED (crashed) run should do. Note this governs restart\n * only — an in-run failure always compensates, because the runner is still\n * alive to do it and a half-applied plan is nobody's intent.\n */\n readonly onCrash?: MigrationOnCrashPolicy;\n}\n\n/** One chunk in the run-global chunk plan. */\nexport interface MigrationChunk {\n /** Run-global index, 0-based, stable for a given plan hash. */\n readonly index: number;\n readonly stepIndex: number;\n readonly stepName: string;\n readonly offset: number;\n readonly length: number;\n}\n\nexport interface MigrationRunResult {\n readonly runId: string;\n /**\n * `completed` — every chunk committed.\n * `compensated` — a chunk failed and every committed chunk was undone.\n * `failed` — a chunk failed AND compensation could not finish. The database\n * is in a partial state that needs a human; the journal says exactly where.\n */\n readonly status: 'completed' | 'compensated' | 'failed';\n readonly chunksTotal: number;\n readonly chunksCommitted: number;\n readonly chunksCompensated: number;\n readonly planHash: string;\n /** The failure that ended a non-`completed` run. */\n readonly error?: unknown;\n}\n\n/**\n * Where a resume finds the plan it has to re-run (#4617).\n *\n * A journal cannot hold a plan. `forward` and `compensate` are FUNCTIONS, and\n * the rows a chunk covers are produced by `load()` against the live database —\n * none of it survives a process boundary, which is why the journal records the\n * plan HASH rather than the plan. So recovery needs the plan handed back to it\n * by whoever owns the code, and that is what this registry is: the seam between\n * \"the journal knows a run stopped at chunk 7\" and \"something in this process\n * knows what chunk 7 was supposed to do\".\n *\n * Registered as the `migration-plans` kernel service. An interrupted run whose\n * plan no loaded plugin registers is REPORTED, never silently skipped — the\n * operator is told which plan id is missing, because \"nothing to resume\" and\n * \"the code that owns this run is not loaded\" are different facts and only one\n * of them is safe to ignore.\n */\nexport interface MigrationPlanProvider {\n register(plan: MigrationPlan): void;\n get(planId: string): MigrationPlan | undefined;\n list(): MigrationPlan[];\n}\n\n/** The default {@link MigrationPlanProvider}. Last registration for an id wins. */\nexport class MigrationPlanRegistry implements MigrationPlanProvider {\n private readonly plans = new Map<string, MigrationPlan>();\n\n register(plan: MigrationPlan): void {\n this.plans.set(plan.id, plan);\n }\n\n get(planId: string): MigrationPlan | undefined {\n return this.plans.get(planId);\n }\n\n list(): MigrationPlan[] {\n return [...this.plans.values()];\n }\n}\n\n/** A run found by {@link findInterruptedRuns} — started, never concluded. */\nexport interface InterruptedRun {\n readonly runId: string;\n readonly planId: string;\n readonly planHash: string;\n readonly migrationId?: string;\n readonly startedAt?: string;\n /** Chunks whose `chunk_done` is present — known committed. */\n readonly committedChunks: number[];\n /** Chunks with `chunk_started` and no `chunk_done` — outcome UNKNOWN. */\n readonly unknownChunks: number[];\n readonly compensatedChunks: number[];\n}\n\n/** Raised when the runner refuses to start or to resume. Never a partial run. */\nexport class MigrationJournalRefusal extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = 'MigrationJournalRefusal';\n this.code = code;\n }\n}\n\n// ── plan shape ────────────────────────────────────────────────────────────\n\n/** Flatten steps × rows into the run-global chunk list. */\nexport function planChunks(\n plan: MigrationPlan,\n rowCounts: readonly number[],\n chunkSize = plan.chunkSize ?? DEFAULT_CHUNK_SIZE,\n): MigrationChunk[] {\n const size = Math.max(1, chunkSize);\n const chunks: MigrationChunk[] = [];\n plan.steps.forEach((step, stepIndex) => {\n const total = rowCounts[stepIndex] ?? 0;\n for (let offset = 0; offset < total; offset += size) {\n chunks.push({\n index: chunks.length,\n stepIndex,\n stepName: step.name,\n offset,\n length: Math.min(size, total - offset),\n });\n }\n });\n return chunks;\n}\n\n/**\n * Hash the plan SHAPE — id, step names, and the chunk boundaries.\n *\n * Resuming a changed plan against an old journal would apply chunk boundaries\n * the journal never described: \"chunk 7 done\" would name a different range of\n * different rows, and the resume would skip work it never did. So the hash\n * covers exactly what a chunk index means, and a mismatch REFUSES.\n */\nexport function hashMigrationPlan(plan: MigrationPlan, chunks: readonly MigrationChunk[]): string {\n const shape = JSON.stringify({\n id: plan.id,\n steps: plan.steps.map((s) => s.name),\n chunks: chunks.map((c) => [c.stepIndex, c.offset, c.length]),\n });\n return createHash('sha256').update(shape, 'utf8').digest('hex').slice(0, 32);\n}\n\n// ── journal I/O ───────────────────────────────────────────────────────────\n\n/**\n * Append one event.\n *\n * `execContext` is the transaction-bound context when the event must share a\n * chunk's fate (`chunk_done`, `compensated`) and undefined when it must NOT\n * (`chunk_started`, and every run-level event). Passing the wrong one is the\n * single most consequential mistake available in this file — see the header.\n */\nasync function appendEvent(\n engine: IObjectQLEngine,\n event: MigrationJournalEvent,\n execContext?: unknown,\n): Promise<void> {\n await engine.insert(\n MIGRATION_JOURNAL_OBJECT,\n { ...event, created_at: event.created_at ?? new Date().toISOString() },\n { context: execContext ?? { ...SYSTEM_CTX } },\n );\n}\n\n/**\n * Every event for a run, ordered by `seq`.\n *\n * Sorted in memory, deliberately. `seq` is the ordering authority (wall-clock\n * stamps tie at coarse resolution and skew), and a run's journal is bounded by\n * its chunk count, so this costs nothing and removes recovery's dependence on\n * driver-side sort behaviour — which is not something a recovery path should\n * be discovering the edges of.\n */\nexport async function readRunJournal(\n engine: IObjectQLEngine,\n runId: string,\n): Promise<MigrationJournalEvent[]> {\n const rows = (await engine.find(\n MIGRATION_JOURNAL_OBJECT,\n { where: { run_id: runId } },\n { context: { ...SYSTEM_CTX } },\n )) as MigrationJournalEvent[];\n return [...(rows ?? [])].sort((a, b) => Number(a.seq) - Number(b.seq));\n}\n\n/** Chunk indices carrying `kind`, as a set. */\nfunction chunkSetOf(events: readonly MigrationJournalEvent[], kind: MigrationJournalKind): Set<number> {\n const out = new Set<number>();\n for (const e of events) {\n if (e.kind === kind && typeof e.chunk_index === 'number') out.add(e.chunk_index);\n }\n return out;\n}\n\n/**\n * Runs that started and never concluded — the boot scanner's input.\n *\n * \"Concluded\" means `run_done` (finished forward) or `run_failed` with every\n * committed chunk compensated (finished backward). Anything else is a run that\n * stopped mid-flight and still owes the operator an answer.\n */\nexport async function findInterruptedRuns(engine: IObjectQLEngine): Promise<InterruptedRun[]> {\n const started = (await engine.find(\n MIGRATION_JOURNAL_OBJECT,\n { where: { kind: 'run_started' } },\n { context: { ...SYSTEM_CTX } },\n )) as MigrationJournalEvent[];\n\n const out: InterruptedRun[] = [];\n for (const start of started ?? []) {\n const events = await readRunJournal(engine, start.run_id);\n if (events.some((e) => e.kind === 'run_done')) continue;\n\n const committed = chunkSetOf(events, 'chunk_done');\n const compensated = chunkSetOf(events, 'compensated');\n const outstanding = [...committed].filter((i) => !compensated.has(i));\n // A failed run whose committed chunks were all undone is settled: it ended\n // backward, on purpose, and its rows prove it.\n if (events.some((e) => e.kind === 'run_failed') && outstanding.length === 0) continue;\n\n const unknown = [...chunkSetOf(events, 'chunk_started')].filter((i) => !committed.has(i));\n let planId = start.run_id;\n try {\n planId = start.detail ? (JSON.parse(start.detail).planId ?? start.run_id) : start.run_id;\n } catch {\n // A malformed detail payload must not hide an interrupted run — the run\n // is still reported, just without its friendly plan id.\n }\n out.push({\n runId: start.run_id,\n planId,\n planHash: start.plan_hash ?? '',\n migrationId: start.migration_id,\n startedAt: start.created_at,\n committedChunks: [...committed].sort((a, b) => a - b),\n unknownChunks: unknown.sort((a, b) => a - b),\n compensatedChunks: [...compensated].sort((a, b) => a - b),\n });\n }\n return out;\n}\n\n// ── the runner ────────────────────────────────────────────────────────────\n\nexport interface RunMigrationJournalOptions {\n /** Supply to resume an existing run; omit to start a new one. */\n readonly runId?: string;\n readonly chunkSize?: number;\n /** Injectable for deterministic tests. */\n readonly now?: () => string;\n}\n\ninterface LoadedPlan {\n readonly chunks: MigrationChunk[];\n readonly planHash: string;\n readonly rowsByStep: unknown[][];\n}\n\n/** Load every step's rows, derive the chunk plan, hash it. */\nasync function loadPlan(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n chunkSize?: number,\n): Promise<LoadedPlan> {\n const rowsByStep: unknown[][] = [];\n for (const step of plan.steps) rowsByStep.push((await step.load(engine)) ?? []);\n const chunks = planChunks(plan, rowsByStep.map((r) => r.length), chunkSize ?? plan.chunkSize);\n return { chunks, planHash: hashMigrationPlan(plan, chunks), rowsByStep };\n}\n\n/**\n * Run `plan` under the journal, or resume a run left behind by a crash.\n *\n * Refuses (never partially runs) when: the runtime cannot roll back; any\n * step's preflight fails; the plan declares `onCrash: 'compensate'` but some\n * step cannot compensate; or a resume's plan hash disagrees with the journal.\n */\nexport async function runMigrationJournal(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n options: RunMigrationJournalOptions = {},\n): Promise<MigrationRunResult> {\n const now = options.now ?? (() => new Date().toISOString());\n\n // ── capability gate ───────────────────────────────────────────────────\n // Refuse rather than degrade. A runner whose chunks are not actually\n // atomic writes `chunk_done` rows that mean nothing, and a journal that\n // cannot be trusted is worse than no journal — it will be believed.\n if (!engineCanRollBack(engine)) {\n throw new MigrationJournalRefusal(\n 'NOT_IMPLEMENTED',\n `Migration plan '${plan.id}' requires engine transaction support; this runtime cannot roll back. ` +\n `The journal's chunk_done markers would not mean \"committed\", so the run is refused rather than started.`,\n );\n }\n\n const { chunks, planHash, rowsByStep } = await loadPlan(engine, plan, options.chunkSize);\n\n // ── resume bookkeeping ────────────────────────────────────────────────\n const resuming = Boolean(options.runId);\n const runId = options.runId ?? randomUUID();\n let events: MigrationJournalEvent[] = [];\n let seq = 0;\n let committed = new Set<number>();\n let compensated = new Set<number>();\n const attemptsByChunk = new Map<number, number>();\n\n if (resuming) {\n events = await readRunJournal(engine, runId);\n if (events.length === 0) {\n throw new MigrationJournalRefusal('NO_SUCH_RUN', `No journal rows for run '${runId}'.`);\n }\n const start = events.find((e) => e.kind === 'run_started');\n if (start?.plan_hash && start.plan_hash !== planHash) {\n // The plan changed under a journal that describes the old one. Chunk 7\n // in the journal and chunk 7 in this plan are different rows; resuming\n // would skip work that was never done.\n throw new MigrationJournalRefusal(\n 'PLAN_CHANGED',\n `Refusing to resume run '${runId}': plan hash ${planHash} does not match the journal's ${start.plan_hash}. ` +\n `The chunk boundaries recorded in the journal describe a different plan.`,\n );\n }\n if (events.some((e) => e.kind === 'run_done')) {\n return {\n runId, status: 'completed', chunksTotal: chunks.length,\n chunksCommitted: chunkSetOf(events, 'chunk_done').size,\n chunksCompensated: chunkSetOf(events, 'compensated').size, planHash,\n };\n }\n seq = events.reduce((m, e) => Math.max(m, Number(e.seq) + 1), 0);\n committed = chunkSetOf(events, 'chunk_done');\n compensated = chunkSetOf(events, 'compensated');\n for (const e of events) {\n if (e.kind === 'chunk_started' && typeof e.chunk_index === 'number') {\n attemptsByChunk.set(e.chunk_index, (attemptsByChunk.get(e.chunk_index) ?? 0) + 1);\n }\n }\n }\n\n // ── preflight ─────────────────────────────────────────────────────────\n // Every validator runs before any write, so a plan that would fail at step 3\n // has not written step 1. On a resume this re-runs too: the world moved\n // while the process was dead, and the reason to refuse may have appeared\n // since.\n for (const step of plan.steps) {\n if (!step.preflight) continue;\n try {\n await step.preflight(engine);\n } catch (err) {\n throw new MigrationJournalRefusal(\n 'PREFLIGHT_FAILED',\n `Migration plan '${plan.id}' refused: preflight for step '${step.name}' failed: ${errText(err)}`,\n );\n }\n }\n\n // A plan that says \"undo me on crash\" must be able to. Discovering that it\n // cannot at compensation time means discovering it with rows already\n // written and no way back.\n if (plan.onCrash === 'compensate') {\n const missing = plan.steps.filter((s) => !s.compensate).map((s) => s.name);\n if (missing.length > 0) {\n throw new MigrationJournalRefusal(\n 'NOT_COMPENSABLE',\n `Migration plan '${plan.id}' declares onCrash: 'compensate' but step(s) ${missing.join(', ')} declare no compensate().`,\n );\n }\n }\n\n const rowsOf = (c: MigrationChunk): unknown[] => rowsByStep[c.stepIndex].slice(c.offset, c.offset + c.length);\n const next = (): number => seq++;\n\n if (!resuming) {\n await appendEvent(engine, {\n run_id: runId, seq: next(), kind: 'run_started', plan_hash: planHash,\n migration_id: plan.migrationId, created_at: now(),\n detail: JSON.stringify({\n planId: plan.id,\n onCrash: plan.onCrash ?? 'resume',\n chunks: chunks.map((c) => ({ i: c.index, step: c.stepName, offset: c.offset, length: c.length })),\n }),\n });\n }\n\n // A rediscovered run whose policy is 'compensate' does not go forward at\n // all — it unwinds what it already did and stops.\n if (resuming && plan.onCrash === 'compensate') {\n return await unwind(engine, plan, {\n runId, planHash, chunks, rowsOf, next, now,\n committed, compensated, chunksTotal: chunks.length,\n cause: new Error(`run '${runId}' rediscovered after interruption; plan policy is compensate`),\n });\n }\n\n // ── forward ───────────────────────────────────────────────────────────\n for (const chunk of chunks) {\n if (committed.has(chunk.index)) continue; // already durable — skip, do not redo\n const attempt = (attemptsByChunk.get(chunk.index) ?? 0) + 1;\n attemptsByChunk.set(chunk.index, attempt);\n const step = plan.steps[chunk.stepIndex];\n const rows = rowsOf(chunk);\n\n // Autonomous, BEFORE the transaction: this is what makes an interrupted\n // chunk visible as \"started, outcome unknown\" rather than invisible.\n await appendEvent(engine, {\n run_id: runId, seq: next(), kind: 'chunk_started',\n chunk_index: chunk.index, attempt, migration_id: plan.migrationId, created_at: now(),\n });\n\n try {\n await engine.transaction(async (trxCtx: unknown) => {\n await step.forward(rows, { runId, chunkIndex: chunk.index, attempt, context: trxCtx }, engine);\n // INSIDE the transaction — `done ⇔ committed`, not a race.\n await appendEvent(\n engine,\n {\n run_id: runId, seq: next(), kind: 'chunk_done',\n chunk_index: chunk.index, attempt, migration_id: plan.migrationId, created_at: now(),\n },\n trxCtx,\n );\n }, { ...SYSTEM_CTX });\n committed.add(chunk.index);\n } catch (err) {\n // The chunk rolled back, so nothing of it is on disk — including its\n // `chunk_done`. Unwind what earlier chunks committed.\n return await unwind(engine, plan, {\n runId, planHash, chunks, rowsOf, next, now,\n committed, compensated, chunksTotal: chunks.length, cause: err,\n });\n }\n }\n\n await appendEvent(engine, {\n run_id: runId, seq: next(), kind: 'run_done', migration_id: plan.migrationId, created_at: now(),\n });\n return {\n runId, status: 'completed', chunksTotal: chunks.length,\n chunksCommitted: committed.size, chunksCompensated: compensated.size, planHash,\n };\n}\n\ninterface UnwindArgs {\n runId: string;\n planHash: string;\n chunks: readonly MigrationChunk[];\n rowsOf: (c: MigrationChunk) => unknown[];\n next: () => number;\n now: () => string;\n committed: Set<number>;\n compensated: Set<number>;\n chunksTotal: number;\n cause: unknown;\n}\n\n/**\n * LIFO compensation over committed chunks.\n *\n * Newest-first because later chunks may depend on earlier ones; undoing in\n * commit order can hit a state the compensator was never written for.\n *\n * A compensation failure HALTS and is journalled — never swallowed, never\n * \"best effort, carry on\". Continuing past it would produce a database whose\n * state no journal describes, which is the one outcome this whole file exists\n * to prevent. The run ends `failed`, and the rows say exactly which chunk\n * resisted.\n */\nasync function unwind(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n a: UnwindArgs,\n): Promise<MigrationRunResult> {\n const order = [...a.committed].sort((x, y) => y - x); // newest-first\n for (const index of order) {\n if (a.compensated.has(index)) continue;\n const chunk = a.chunks[index];\n const step = plan.steps[chunk.stepIndex];\n\n if (!step.compensate) {\n // Nothing to undo this with. Say so loudly and stop — a silent skip\n // would leave the row written and the journal claiming a clean unwind.\n await appendEvent(engine, {\n run_id: a.runId, seq: a.next(), kind: 'run_failed',\n chunk_index: index, migration_id: plan.migrationId, created_at: a.now(),\n detail: JSON.stringify({\n phase: 'compensate', reason: 'step declares no compensate()',\n step: step.name, cause: errText(a.cause),\n }),\n });\n return {\n runId: a.runId, status: 'failed', chunksTotal: a.chunksTotal,\n chunksCommitted: a.committed.size, chunksCompensated: a.compensated.size,\n planHash: a.planHash, error: a.cause,\n };\n }\n\n const attempt = 1;\n try {\n await engine.transaction(async (trxCtx: unknown) => {\n await step.compensate!(a.rowsOf(chunk), { runId: a.runId, chunkIndex: index, attempt, context: trxCtx }, engine);\n await appendEvent(\n engine,\n {\n run_id: a.runId, seq: a.next(), kind: 'compensated',\n chunk_index: index, attempt, migration_id: plan.migrationId, created_at: a.now(),\n },\n trxCtx,\n );\n }, { ...SYSTEM_CTX });\n a.compensated.add(index);\n } catch (err) {\n await appendEvent(engine, {\n run_id: a.runId, seq: a.next(), kind: 'run_failed',\n chunk_index: index, migration_id: plan.migrationId, created_at: a.now(),\n detail: JSON.stringify({\n phase: 'compensate', step: step.name,\n error: errText(err), cause: errText(a.cause),\n }),\n });\n return {\n runId: a.runId, status: 'failed', chunksTotal: a.chunksTotal,\n chunksCommitted: a.committed.size, chunksCompensated: a.compensated.size,\n planHash: a.planHash, error: err,\n };\n }\n }\n\n await appendEvent(engine, {\n run_id: a.runId, seq: a.next(), kind: 'run_failed',\n migration_id: plan.migrationId, created_at: a.now(),\n detail: JSON.stringify({ phase: 'forward', error: errText(a.cause), compensated: [...a.compensated].sort((x, y) => x - y) }),\n });\n return {\n runId: a.runId, status: 'compensated', chunksTotal: a.chunksTotal,\n chunksCommitted: a.committed.size, chunksCompensated: a.compensated.size,\n planHash: a.planHash, error: a.cause,\n };\n}\n\n/** Resume a run the journal says was interrupted. Thin alias for intent at call sites. */\nexport async function resumeMigrationJournal(\n engine: IObjectQLEngine,\n plan: MigrationPlan,\n runId: string,\n options: Omit<RunMigrationJournalOptions, 'runId'> = {},\n): Promise<MigrationRunResult> {\n return runMigrationJournal(engine, plan, { ...options, runId });\n}\n\nfunction errText(err: unknown): string {\n if (err instanceof Error) return err.message;\n try {\n return String(err);\n } catch {\n return '<unprintable error>';\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Runtime resolution of filter placeholders — the server-side half of the\n * `{token}` contract that `@objectstack/spec` declares (framework#3582).\n *\n * `date-macros.zod.ts` and `context-tokens.zod.ts` freeze the *vocabulary*;\n * `@objectstack/lint`'s `validate-filter-tokens` rejects a token outside it at\n * authoring time. Neither one ever substituted a value: every server-side\n * consumer handed the literal `'{current_year_start}'` to the database, where\n * it compared as a string and matched nothing. The failure was invisible —\n * an empty widget, an unfiltered list — so apps worked around it by computing\n * dates at module load, freezing \"this year\" into the built artifact.\n *\n * This module is the missing evaluator. It walks a filter tree and replaces\n * every fully-wrapped placeholder with a concrete value:\n *\n * { close_date: { $gte: '{current_year_start}' } }\n * → { close_date: { $gte: '2026-01-01' } }\n * { owner: '{current_user_id}' }\n * → { owner: 'usr_7f3a…' }\n *\n * # Output form: ISO strings, not driver-native values\n *\n * Date tokens resolve to `YYYY-MM-DD` (or a full ISO timestamp for the\n * sub-day tokens `{now}` / `{N_hours_ago}` / `{N_minutes_ago}`), exactly the\n * form the spec's module doc promises the data engine sees. Translating that\n * to a column's on-disk form is the DRIVER's job and already exists —\n * `SqlDriver.coerceFilterValue` / `temporalFilterValue` turn an ISO comparand\n * into SQLite epoch-ms or leave it alone on native-timestamp dialects. Emitting\n * a driver-native value here would fork that convention into a second source of\n * truth and break the moment a query crosses datasources.\n *\n * # Period `_end` is the last calendar DAY, not the last instant\n *\n * `{current_year_end}` is `2026-12-31`, per the spec's own\n * `DATE_MACRO_DESCRIPTIONS` (\"Dec 31 of this year\"). On a `datetime` column\n * that means `<= {current_year_end}` excludes everything after midnight on the\n * 31st — the classic half-open-range trap. Authors filtering a timestamp want\n * `< {next_year_start}`. This is a documented property of the vocabulary, not\n * something the resolver may quietly \"fix\": silently widening a bound would\n * make the same token mean different things on different column types.\n *\n * # An unknown token throws\n *\n * A value that is entirely `{something}` is a placeholder by construction — no\n * author means the literal six characters `{foo}`. Passing an unrecognised one\n * through is precisely the silent-zero bug this module exists to end, so it is\n * a hard error carrying the near-miss suggestion (`{current_user}` →\n * `{current_user_id}`). Values that merely CONTAIN braces are left untouched.\n *\n * \"Entirely `{something}`\" means ANY character between the braces (#5586).\n * Until then the recognition grammar was the token-NAME grammar\n * (`[a-zA-Z0-9_]+`), so a placeholder carrying a non-word character —\n * `{TODAY()}`, `{current-user-id}`, `{30 days ago}`, `{user.id}` — was not\n * recognised as a token at all and fell straight through to the literal\n * comparison this module exists to abolish. The failure was inverted against\n * the author: `{TODAY}` threw (diagnostic working), `{TODAY()}` returned rows\n * (diagnostic bypassed) — and the parenthesised, kebab-case and\n * natural-language spellings are exactly what an author migrating from another\n * system's macro syntax reaches for first. See `FILTER_TOKEN_WRAPPED_RE` in\n * `@objectstack/spec`.\n */\n\nimport {\n classifyFilterToken,\n parseDateMacroParam,\n type DateMacroUnit,\n} from '@objectstack/spec/data';\nimport { calendarPartsInTzOrUtc } from './datetime.js';\n\n/**\n * The slice of an execution context the resolver reads. Structural on purpose —\n * see {@link filterTokenContextFrom}.\n */\nexport interface ExecutionContextLike {\n readonly userId?: string;\n readonly tenantId?: string;\n readonly timezone?: string;\n}\n\n/**\n * The request-scoped values a placeholder can resolve against.\n *\n * `now` is captured ONCE per resolve call so every token in one filter shares\n * an instant — otherwise a `$gte {current_month_start}` / `$lt\n * {next_month_start}` pair evaluated microseconds apart could straddle a\n * month boundary and silently drop a row.\n */\nexport interface FilterTokenResolutionContext {\n /** Reference instant. Defaults to `new Date()` at call time. */\n now?: Date;\n /** IANA reference timezone for calendar boundaries. Defaults to UTC. */\n timezone?: string;\n /** Resolves `{current_user_id}`. */\n userId?: string;\n /** Resolves `{current_org_id}`. */\n orgId?: string;\n}\n\n/**\n * Raised when a filter carries a placeholder outside the vocabulary.\n *\n * Carries `status`/`code` so the REST layer's generic 4xx passthrough maps it\n * to a **400 with a fixable message** rather than a 500: the caller's filter is\n * malformed, the server is fine. (Same convention plugin-sharing uses for its\n * record-scope denial — no runtime dependency in either direction.)\n */\nexport class UnknownFilterTokenError extends Error {\n readonly token: string;\n readonly suggestion?: string;\n readonly status = 400;\n readonly code = 'FILTER_TOKEN_UNKNOWN';\n\n constructor(token: string, suggestion?: string) {\n super(\n `Unresolvable filter placeholder \"{${token}}\". ` +\n (suggestion\n ? `Did you mean \"{${suggestion}}\"? `\n : 'Resolvable placeholders are the context tokens ({current_user_id}, ' +\n '{current_org_id}) and the date macros ({today}, {current_quarter_start}, ' +\n '{30_days_ago}, …). ') +\n 'Sending it to the data engine verbatim would compare it as a literal ' +\n 'string and match nothing, which is indistinguishable from an empty result.',\n );\n this.name = 'UnknownFilterTokenError';\n this.token = token;\n this.suggestion = suggestion;\n }\n}\n\n/**\n * Raised when a token IS in the vocabulary but the request carries no value\n * for it — an unauthenticated caller filtering on `{current_user_id}`.\n *\n * Distinct from {@link UnknownFilterTokenError} because the fix is different:\n * the metadata is correct, the context is not. Never silently resolves to\n * `null`/`undefined`, which on most drivers degrades to `IS NULL` and would\n * quietly hand back rows the filter was written to exclude.\n */\nexport class UnresolvedFilterTokenError extends Error {\n readonly token: string;\n /** 400, not 500 — see {@link UnknownFilterTokenError}. */\n readonly status = 400;\n readonly code = 'FILTER_TOKEN_UNRESOLVED';\n\n constructor(token: string, detail: string) {\n super(`Filter placeholder \"{${token}}\" cannot be resolved: ${detail}`);\n this.name = 'UnresolvedFilterTokenError';\n this.token = token;\n }\n}\n\n/** `YYYY-MM-DD` for a calendar day, zero-padded. */\nfunction ymd(year: number, month: number, day: number): string {\n const p = (n: number) => String(n).padStart(2, '0');\n return `${year}-${p(month)}-${p(day)}`;\n}\n\n/**\n * Calendar arithmetic is done on a UTC \"proxy\" date built from the reference\n * timezone's calendar parts. Working in UTC keeps the math free of DST jumps\n * (a local-midnight `Date` can shift by an hour when `setMonth` crosses a\n * transition); the zone only decides WHICH calendar day \"now\" is, which\n * {@link calendarPartsInTzOrUtc} answers from the platform tz database.\n */\nfunction proxyDay(now: Date, timezone?: string): Date {\n const { year, month, day } = calendarPartsInTzOrUtc(now, timezone);\n return new Date(Date.UTC(year, month - 1, day));\n}\n\nconst asYmd = (d: Date): string => ymd(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n\ntype PeriodKind = 'week' | 'month' | 'quarter' | 'year';\n\n/** Monday-based week start — matches the spec's \"Monday 00:00 of this week\". */\nfunction startOfPeriod(kind: PeriodKind, d: Date): Date {\n const r = new Date(d.getTime());\n switch (kind) {\n case 'week': {\n const dow = (r.getUTCDay() + 6) % 7; // 0 = Monday\n r.setUTCDate(r.getUTCDate() - dow);\n return r;\n }\n case 'month':\n return new Date(Date.UTC(r.getUTCFullYear(), r.getUTCMonth(), 1));\n case 'quarter':\n return new Date(Date.UTC(r.getUTCFullYear(), Math.floor(r.getUTCMonth() / 3) * 3, 1));\n case 'year':\n return new Date(Date.UTC(r.getUTCFullYear(), 0, 1));\n }\n}\n\n/** Days in the given (0-based) month of `year`. */\nfunction daysInMonth(year: number, month: number): number {\n return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();\n}\n\n/**\n * Shift `d` by `n` months, CLAMPING the day to the target month's length.\n *\n * Bare `setUTCMonth(m - 1)` on the 31st rolls FORWARD into the following month\n * (Mar 31 minus one month = \"Feb 31\" = Mar 3), which would make\n * `{1_month_ago}` land after `{today}` on five days of the year. Clamping to\n * Feb 28 is what every calendar library does and the only answer an author\n * would call correct.\n */\nfunction addMonthsClamped(d: Date, n: number): Date {\n const year = d.getUTCFullYear();\n const month = d.getUTCMonth() + n;\n const targetYear = year + Math.floor(month / 12);\n const targetMonth = ((month % 12) + 12) % 12;\n const day = Math.min(d.getUTCDate(), daysInMonth(targetYear, targetMonth));\n return new Date(Date.UTC(\n targetYear, targetMonth, day,\n d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds(),\n ));\n}\n\n/** Shift `d` by `n` whole periods of `kind` (negative shifts backwards). */\nfunction addPeriods(kind: PeriodKind, d: Date, n: number): Date {\n switch (kind) {\n case 'week': {\n const r = new Date(d.getTime());\n r.setUTCDate(r.getUTCDate() + n * 7);\n return r;\n }\n case 'month': return addMonthsClamped(d, n);\n case 'quarter': return addMonthsClamped(d, n * 3);\n case 'year': return addMonthsClamped(d, n * 12);\n }\n}\n\n/** Shift `d` by `n` units of the parameterised grammar. */\nfunction addUnits(unit: DateMacroUnit, d: Date, n: number): Date {\n const r = new Date(d.getTime());\n switch (unit) {\n case 'minute': r.setUTCMinutes(r.getUTCMinutes() + n); return r;\n case 'hour': r.setUTCHours(r.getUTCHours() + n); return r;\n case 'day': r.setUTCDate(r.getUTCDate() + n); return r;\n case 'week': r.setUTCDate(r.getUTCDate() + n * 7); return r;\n // Month/year steps clamp rather than overflow — see addMonthsClamped.\n case 'month': return addMonthsClamped(d, n);\n case 'year': return addMonthsClamped(d, n * 12);\n }\n}\n\n/**\n * `current|last|next` × `week|month|quarter|year` × `start|end`, plus the bare\n * `week_start`-style aliases (which mean `current_`). Returns `undefined` when\n * the token is not a period token.\n */\nconst PERIOD_RE = /^(?:(current|last|next)_)?(week|month|quarter|year)_(start|end)$/;\n\nfunction resolvePeriodToken(token: string, today: Date): string | undefined {\n const m = PERIOD_RE.exec(token);\n if (!m) return undefined;\n const rel = (m[1] ?? 'current') as 'current' | 'last' | 'next';\n const kind = m[2] as PeriodKind;\n const bound = m[3] as 'start' | 'end';\n const offset = rel === 'last' ? -1 : rel === 'next' ? 1 : 0;\n\n // Normalize to the period's own start BEFORE stepping. Day 1 of a\n // month/quarter/year (and a Monday) shifts exactly — no month-length clamping\n // is involved at all — so the answer never depends on the clamp policy, and\n // the arithmetic reads the same for every `kind`.\n const periodStart = startOfPeriod(kind, addPeriods(kind, startOfPeriod(kind, today), offset));\n if (bound === 'start') return asYmd(periodStart);\n // `_end` = the last calendar DAY of the period: the day before the next\n // period begins. See the module doc on half-open ranges.\n const next = addPeriods(kind, periodStart, 1);\n next.setUTCDate(next.getUTCDate() - 1);\n return asYmd(next);\n}\n\n/**\n * Resolve one token NAME (the bit inside the braces) to its concrete value.\n * Throws {@link UnresolvedFilterTokenError} for a vocabulary token the request\n * carries no value for. Returns `undefined` only when the token is outside the\n * vocabulary — callers turn that into {@link UnknownFilterTokenError}.\n */\nexport function resolveFilterToken(\n token: string,\n ctx: FilterTokenResolutionContext = {},\n): unknown {\n const now = ctx.now ?? new Date();\n\n // ── Context tokens ────────────────────────────────────────────────────\n if (token === 'current_user_id') {\n if (!ctx.userId) {\n throw new UnresolvedFilterTokenError(\n token,\n 'the request has no authenticated user. A filter scoped to the signed-in ' +\n 'user cannot run for an anonymous or system caller — gate the surface on ' +\n 'authentication, or drop the token from the filter.',\n );\n }\n return ctx.userId;\n }\n if (token === 'current_org_id') {\n if (!ctx.orgId) {\n throw new UnresolvedFilterTokenError(\n token,\n 'the request carries no active organization (ExecutionContext.tenantId is ' +\n 'unset). Set the active org on the request, or drop the token from the filter.',\n );\n }\n return ctx.orgId;\n }\n\n // ── Date macros ───────────────────────────────────────────────────────\n const today = proxyDay(now, ctx.timezone);\n\n switch (token) {\n case 'now': return now.toISOString();\n case 'today': return asYmd(today);\n case 'yesterday': return asYmd(addUnits('day', today, -1));\n case 'tomorrow': return asYmd(addUnits('day', today, 1));\n }\n\n const period = resolvePeriodToken(token, today);\n if (period !== undefined) return period;\n\n const param = parseDateMacroParam(token);\n if (param) {\n const sign = param.direction === 'ago' ? -1 : 1;\n // Sub-day units are instants — they must keep their time-of-day, so they\n // shift `now` and render as a full ISO timestamp. Day-and-coarser units are\n // calendar quantities and render as `YYYY-MM-DD` off the reference day.\n if (param.unit === 'minute' || param.unit === 'hour') {\n return addUnits(param.unit, now, sign * param.n).toISOString();\n }\n return asYmd(addUnits(param.unit, today, sign * param.n));\n }\n\n return undefined;\n}\n\n/**\n * Does this tree contain any fully-wrapped placeholder at all?\n *\n * A read-only pre-pass so the overwhelmingly common case — an internal query\n * whose filter is entirely literal — costs one allocation-free walk instead of\n * a full structural copy. This runs on every server-side read, so \"no\n * placeholders\" must be close to free.\n */\nfunction hasFilterToken(node: unknown): boolean {\n if (typeof node === 'string') return classifyFilterToken(node) !== null;\n if (Array.isArray(node)) return node.some(hasFilterToken);\n if (node && typeof node === 'object' && !(node instanceof Date)) {\n return Object.values(node as Record<string, unknown>).some(hasFilterToken);\n }\n return false;\n}\n\n/**\n * Deep-replace every fully-wrapped placeholder in `filter` with its resolved\n * value, returning a NEW tree (the caller's metadata is never mutated — a view\n * or dataset definition is shared across requests, so resolving in place would\n * bake one request's user id, and one day's dates, into every later render).\n *\n * Returns the input unchanged, by reference, when it holds no placeholders.\n */\nexport function resolveFilterTokens<T>(\n filter: T,\n ctx: FilterTokenResolutionContext = {},\n): T {\n if (filter == null) return filter;\n if (!hasFilterToken(filter)) return filter;\n\n // One instant for the whole tree (see FilterTokenResolutionContext.now).\n const pinned: FilterTokenResolutionContext = { ...ctx, now: ctx.now ?? new Date() };\n\n const walk = (node: unknown): unknown => {\n if (typeof node === 'string') {\n const cls = classifyFilterToken(node);\n if (!cls) return node;\n if (cls.kind === 'unknown') throw new UnknownFilterTokenError(cls.token, cls.suggestion);\n const resolved = resolveFilterToken(cls.token, pinned);\n // `classifyFilterToken` already vouched for the token, so `undefined`\n // here would mean the spec vocabulary and this resolver have drifted\n // apart — surface it loudly rather than silently emitting `undefined`.\n if (resolved === undefined) throw new UnknownFilterTokenError(cls.token);\n return resolved;\n }\n if (Array.isArray(node)) return node.map(walk);\n if (node && typeof node === 'object') {\n // Dates and other class instances are comparands, not filter structure.\n if (node instanceof Date) return node;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(node as Record<string, unknown>)) out[k] = walk(v);\n return out;\n }\n return node;\n };\n\n return walk(filter) as T;\n}\n\n/**\n * Convenience bridge from an execution context to the resolver's inputs.\n * `{current_org_id}` reads `tenantId` — the active organization IS the tenant\n * on the read path (same value the RLS compiler binds to\n * `current_user.organization_id`).\n *\n * Typed structurally, not as `ExecutionContext`, so both the parsed context\n * (`ExecutionContextParsed`, defaults applied) and the pre-parse\n * `ExecutionContext` a caller holds mid-pipeline satisfy it. The three fields\n * read here are optional in both.\n */\nexport function filterTokenContextFrom(\n execCtx: ExecutionContextLike | undefined,\n now?: Date,\n): FilterTokenResolutionContext {\n return {\n now,\n timezone: execCtx?.timezone,\n userId: execCtx?.userId,\n orgId: execCtx?.tenantId,\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [#4435] The 404 a single-record operation answers when the id names no row.\n *\n * Extracted so the READ and the two WRITE paths cannot disagree about it. They\n * did: `getData` answered `404 RECORD_NOT_FOUND` while `updateData` returned\n * `200 { record: null }` and `deleteData` returned `200 { success: true }` for\n * any string in the path — so a typo'd id, an already-deleted row and a real\n * deletion were indistinguishable, and a client PATCHing a concurrently deleted\n * record was told its write had landed.\n *\n * That is the same silent-no-op shape the v17 train removed everywhere else\n * this window (#4240/#4303/#4315 refuse missing fields, #4169 refuses unknown\n * params, #4190 stopped dropping filters) — a write that touched zero rows\n * reporting 200 is that shape one level up, on the verb where it costs the\n * most.\n *\n * [#5138] EXPORTED, for the same \"cannot disagree about it\" reason one layer\n * out. `@objectstack/runtime`'s `callData` is protocol-first with an ObjectQL\n * FALLBACK, and the fallback had reinvented this fact three incompatible ways\n * (`get` → `null`, `update` → a bare `Error` with no status ⇒ 500, `delete` →\n * no check at all ⇒ `200 { deleted: true }` for a row that never existed). It\n * now calls THIS function, so the two paths behind one `callData` answer a\n * missing id identically — which is the only reason a caller may stop caring\n * which of them served it. Re-spelling the envelope there would have been a\n * second not-found envelope; `RECORD_NOT_FOUND` (#5088) is the one this repo\n * has.\n *\n * ── [#7867] Why it lives in `@objectstack/core` and not where it was written ──\n *\n * Because the THIRD path that needed it could not reach the second one. An\n * action body's `ctx.api.object(name).update({ id, … })` traverses neither\n * `protocol.updateData` nor `callData`: it reaches `ObjectQL.update()`'s by-id\n * branch directly, which had no existence gate at all, so a ghost id was a\n * silent no-op that then died on whatever the pipeline complained about first\n * (a `HookConditionError` 400 on a hooked object, a required-field\n * `VALIDATION_FAILED` 400 on an unhooked one — the 400 class varied with the\n * object's declarations; the missing 404 was the constant).\n *\n * The gate for that path belongs in the engine, and `packages/objectql` cannot\n * import `@objectstack/metadata-protocol` where this function was written:\n * ADR-0076 D2's boundary ratchet (`core-boundary.ratchet.test.ts`) forbids the\n * whole `@objectstack/objectql/core` closure — `engine.ts` included — from\n * pulling that package in. So the choice was a FOURTH spelling of the envelope\n * or one home both layers already depend on. #5138's own sentence rules the\n * first out, so this is the second: the factory moved down to the lowest\n * package the three producers share, and `@objectstack/metadata-protocol`\n * re-exports it unchanged for every existing importer.\n *\n * This is the same move `engineCanRollBack` made for the same reason — a fact\n * two layers must agree on lives in the layer beneath both, not in a copy each.\n */\nexport function recordNotFoundError(object: string, id: string | number): Error {\n const err = new Error(`Record ${id} not found in ${object}`) as Error & {\n code?: string;\n status?: number;\n object?: string;\n };\n err.code = 'RECORD_NOT_FOUND';\n err.status = 404;\n err.object = object;\n return err;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n PluginHealthStatus, \n PluginHealthCheckParsed, \n PluginHealthReport \n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from './logger.js';\nimport type { Plugin } from './types.js';\n\n/**\n * Plugin Health Monitor\n * \n * Monitors plugin health status and performs automatic recovery actions.\n * Implements the advanced lifecycle health monitoring protocol.\n */\nexport class PluginHealthMonitor {\n private logger: ObjectLogger;\n private healthChecks = new Map<string, PluginHealthCheckParsed>();\n private healthStatus = new Map<string, PluginHealthStatus>();\n private healthReports = new Map<string, PluginHealthReport>();\n private checkIntervals = new Map<string, NodeJS.Timeout>();\n private failureCounters = new Map<string, number>();\n private successCounters = new Map<string, number>();\n private restartAttempts = new Map<string, number>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'HealthMonitor' });\n }\n\n /**\n * Register a plugin for health monitoring\n */\n registerPlugin(pluginName: string, config: PluginHealthCheckParsed): void {\n this.healthChecks.set(pluginName, config);\n this.healthStatus.set(pluginName, 'unknown');\n this.failureCounters.set(pluginName, 0);\n this.successCounters.set(pluginName, 0);\n this.restartAttempts.set(pluginName, 0);\n\n this.logger.info('Plugin registered for health monitoring', { \n plugin: pluginName,\n interval: config.interval \n });\n }\n\n /**\n * Start monitoring a plugin\n */\n startMonitoring(pluginName: string, plugin: Plugin): void {\n const config = this.healthChecks.get(pluginName);\n if (!config) {\n this.logger.warn('Cannot start monitoring - plugin not registered', { plugin: pluginName });\n return;\n }\n\n // Clear any existing interval\n this.stopMonitoring(pluginName);\n\n // Set up periodic health checks\n const interval = setInterval(() => {\n this.performHealthCheck(pluginName, plugin, config).catch(error => {\n this.logger.error('Health check failed with error', { \n plugin: pluginName, \n error \n });\n });\n }, config.interval);\n\n this.checkIntervals.set(pluginName, interval);\n this.logger.info('Health monitoring started', { plugin: pluginName });\n\n // Perform initial health check\n this.performHealthCheck(pluginName, plugin, config).catch(error => {\n this.logger.error('Initial health check failed', { \n plugin: pluginName, \n error \n });\n });\n }\n\n /**\n * Stop monitoring a plugin\n */\n stopMonitoring(pluginName: string): void {\n const interval = this.checkIntervals.get(pluginName);\n if (interval) {\n clearInterval(interval);\n this.checkIntervals.delete(pluginName);\n this.logger.info('Health monitoring stopped', { plugin: pluginName });\n }\n }\n\n /**\n * Perform a health check on a plugin\n */\n private async performHealthCheck(\n pluginName: string,\n plugin: Plugin,\n config: PluginHealthCheckParsed\n ): Promise<void> {\n const startTime = Date.now();\n let status: PluginHealthStatus = 'healthy';\n let message: string | undefined;\n const checks: Array<{ name: string; status: 'passed' | 'failed' | 'warning'; message?: string }> = [];\n\n try {\n // Check if plugin has a custom health check method\n if (config.checkMethod && typeof (plugin as any)[config.checkMethod] === 'function') {\n const checkResult = await this.raceCheckTimeout(\n (plugin as any)[config.checkMethod](),\n config.timeout,\n `Health check timeout after ${config.timeout}ms`\n );\n\n if (checkResult === false || (checkResult && checkResult.status === 'unhealthy')) {\n status = 'unhealthy';\n message = checkResult?.message || 'Custom health check failed';\n checks.push({ name: config.checkMethod, status: 'failed', message });\n } else {\n checks.push({ name: config.checkMethod, status: 'passed' });\n }\n } else {\n // Default health check - just verify plugin is loaded\n checks.push({ name: 'plugin-loaded', status: 'passed' });\n }\n\n // Update counters based on result\n if (status === 'healthy') {\n this.successCounters.set(pluginName, (this.successCounters.get(pluginName) || 0) + 1);\n this.failureCounters.set(pluginName, 0);\n\n // Recover from unhealthy state if we have enough successes\n const currentStatus = this.healthStatus.get(pluginName);\n if (currentStatus === 'unhealthy' || currentStatus === 'degraded') {\n const successCount = this.successCounters.get(pluginName) || 0;\n if (successCount >= config.successThreshold) {\n this.healthStatus.set(pluginName, 'healthy');\n this.logger.info('Plugin recovered to healthy state', { plugin: pluginName });\n } else {\n this.healthStatus.set(pluginName, 'recovering');\n }\n } else {\n this.healthStatus.set(pluginName, 'healthy');\n }\n } else {\n this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);\n this.successCounters.set(pluginName, 0);\n\n const failureCount = this.failureCounters.get(pluginName) || 0;\n if (failureCount >= config.failureThreshold) {\n this.healthStatus.set(pluginName, 'unhealthy');\n this.logger.warn('Plugin marked as unhealthy', { \n plugin: pluginName, \n failures: failureCount \n });\n\n // Attempt auto-restart if configured\n if (config.autoRestart) {\n await this.attemptRestart(pluginName, plugin, config);\n }\n } else {\n this.healthStatus.set(pluginName, 'degraded');\n }\n }\n } catch (error) {\n status = 'failed';\n message = error instanceof Error ? error.message : 'Unknown error';\n this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);\n this.healthStatus.set(pluginName, 'failed');\n \n checks.push({ \n name: 'health-check', \n status: 'failed', \n message: message \n });\n\n this.logger.error('Health check exception', { \n plugin: pluginName, \n error \n });\n }\n\n // Create health report\n const report: PluginHealthReport = {\n status: this.healthStatus.get(pluginName) || 'unknown',\n timestamp: new Date().toISOString(),\n message,\n metrics: {\n uptime: Date.now() - startTime,\n },\n checks: checks.length > 0 ? checks : undefined,\n };\n\n this.healthReports.set(pluginName, report);\n }\n\n /**\n * Attempt to restart a plugin\n */\n private async attemptRestart(\n pluginName: string,\n plugin: Plugin,\n config: PluginHealthCheckParsed\n ): Promise<void> {\n const attempts = this.restartAttempts.get(pluginName) || 0;\n \n if (attempts >= config.maxRestartAttempts) {\n this.logger.error('Max restart attempts reached, giving up', { \n plugin: pluginName, \n attempts \n });\n this.healthStatus.set(pluginName, 'failed');\n return;\n }\n\n this.restartAttempts.set(pluginName, attempts + 1);\n \n // Calculate backoff delay\n const delay = this.calculateBackoff(attempts, config.restartBackoff);\n \n this.logger.info('Scheduling plugin restart', { \n plugin: pluginName, \n attempt: attempts + 1, \n delay \n });\n\n await new Promise(resolve => setTimeout(resolve, delay));\n\n try {\n // Call destroy and init to restart\n if (plugin.destroy) {\n await plugin.destroy();\n }\n \n // Note: Full restart would require kernel context\n // This is a simplified version - actual implementation would need kernel integration\n this.logger.info('Plugin restarted', { plugin: pluginName });\n \n // Reset counters on successful restart\n this.failureCounters.set(pluginName, 0);\n this.successCounters.set(pluginName, 0);\n this.healthStatus.set(pluginName, 'recovering');\n } catch (error) {\n this.logger.error('Plugin restart failed', { \n plugin: pluginName, \n error \n });\n this.healthStatus.set(pluginName, 'failed');\n }\n }\n\n /**\n * Calculate backoff delay for restarts\n */\n private calculateBackoff(attempt: number, strategy: 'fixed' | 'linear' | 'exponential'): number {\n const baseDelay = 1000; // 1 second base\n\n switch (strategy) {\n case 'fixed':\n return baseDelay;\n case 'linear':\n return baseDelay * (attempt + 1);\n case 'exponential':\n return baseDelay * Math.pow(2, attempt);\n default:\n return baseDelay;\n }\n }\n\n /**\n * Get current health status of a plugin\n */\n getHealthStatus(pluginName: string): PluginHealthStatus | undefined {\n return this.healthStatus.get(pluginName);\n }\n\n /**\n * Get latest health report for a plugin\n */\n getHealthReport(pluginName: string): PluginHealthReport | undefined {\n return this.healthReports.get(pluginName);\n }\n\n /**\n * Get all health statuses\n */\n getAllHealthStatuses(): Map<string, PluginHealthStatus> {\n return new Map(this.healthStatus);\n }\n\n /**\n * Shutdown health monitor\n */\n shutdown(): void {\n // Stop all monitoring intervals\n for (const pluginName of this.checkIntervals.keys()) {\n this.stopMonitoring(pluginName);\n }\n \n this.healthChecks.clear();\n this.healthStatus.clear();\n this.healthReports.clear();\n this.failureCounters.clear();\n this.successCounters.clear();\n this.restartAttempts.clear();\n \n this.logger.info('Health monitor shutdown complete');\n }\n\n /**\n * Race a plugin's custom health check against its timeout guard, and\n * reclaim the guard the moment the race settles (#4875).\n *\n * Same shape, same reasoning as `ObjectKernel.raceStartupTimeout()` (#4813,\n * PR #4874): the guard used to be armed and then abandoned — when the check\n * won the race, its `setTimeout` stayed ref'd in the event loop for the full\n * `config.timeout`. Health checks are *periodic*, so unlike the kernel's\n * one-shot startup guards the orphans here accumulate: one per plugin per\n * round, each pinning the loop for `config.timeout`.\n *\n * Clearing on settle rather than `unref()`-ing at arm time is deliberate.\n * An unref'd guard also stops pinning the loop, but it stops being a guard\n * as well: if the check never settles and nothing else keeps the loop alive,\n * Node exits before the timer can fire and the timeout is never reported.\n * The guard has to stay ref'd exactly as long as the race is undecided,\n * which is what `clearTimeout` in a `finally` expresses.\n *\n * `check` is widened to `T | PromiseLike<T>` because `checkMethod` is called\n * dynamically off the plugin and may be synchronous; such a check wins the\n * race immediately and the guard is reclaimed on the same turn.\n */\n private async raceCheckTimeout<T>(\n check: T | PromiseLike<T>,\n ms: number,\n message: string\n ): Promise<T> {\n let guard: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n guard = setTimeout(() => {\n reject(new Error(message));\n }, ms);\n });\n\n try {\n return await Promise.race([check, timeoutPromise]);\n } finally {\n clearTimeout(guard);\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createHash } from 'node:crypto';\n\nimport type { \n HotReloadConfigParsed, \n PluginStateSnapshot \n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from './logger.js';\nimport type { Plugin } from './types.js';\n\n// Polyfill for UUID generation to support both Node.js and Browser\nconst generateUUID = () => {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Basic UUID v4 fallback\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n};\n\n/**\n * Plugin State Manager\n * \n * Handles state persistence and restoration during hot reloads\n */\nclass PluginStateManager {\n private logger: ObjectLogger;\n private stateSnapshots = new Map<string, PluginStateSnapshot>();\n private memoryStore = new Map<string, any>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'StateManager' });\n }\n\n /**\n * Save plugin state before reload\n */\n async saveState(\n pluginId: string,\n version: string,\n state: Record<string, any>,\n config: HotReloadConfigParsed\n ): Promise<string> {\n const snapshot: PluginStateSnapshot = {\n pluginId,\n version,\n timestamp: new Date().toISOString(),\n state,\n metadata: {\n checksum: this.calculateChecksum(state),\n compressed: false,\n },\n };\n\n const snapshotId = generateUUID();\n\n switch (config.stateStrategy) {\n case 'memory':\n this.memoryStore.set(snapshotId, snapshot);\n this.logger.debug('State saved to memory', { pluginId, snapshotId });\n break;\n\n case 'disk':\n // For disk storage, we would write to file system\n // For now, store in memory as fallback\n this.memoryStore.set(snapshotId, snapshot);\n this.logger.debug('State saved to disk (memory fallback)', { pluginId, snapshotId });\n break;\n\n case 'distributed':\n // For distributed storage, would use Redis/etcd\n // For now, store in memory as fallback\n this.memoryStore.set(snapshotId, snapshot);\n this.logger.debug('State saved to distributed store (memory fallback)', { \n pluginId, \n snapshotId \n });\n break;\n\n case 'none':\n this.logger.debug('State persistence disabled', { pluginId });\n break;\n }\n\n this.stateSnapshots.set(pluginId, snapshot);\n return snapshotId;\n }\n\n /**\n * Restore plugin state after reload\n */\n async restoreState(\n pluginId: string,\n snapshotId?: string\n ): Promise<Record<string, any> | undefined> {\n // Try to get from snapshot ID first, otherwise use latest for plugin\n let snapshot: PluginStateSnapshot | undefined;\n\n if (snapshotId) {\n snapshot = this.memoryStore.get(snapshotId);\n } else {\n snapshot = this.stateSnapshots.get(pluginId);\n }\n\n if (!snapshot) {\n this.logger.warn('No state snapshot found', { pluginId, snapshotId });\n return undefined;\n }\n\n // Verify checksum if available\n if (snapshot.metadata?.checksum) {\n const currentChecksum = this.calculateChecksum(snapshot.state);\n if (currentChecksum !== snapshot.metadata.checksum) {\n this.logger.error('State checksum mismatch - data may be corrupted', { \n pluginId,\n expected: snapshot.metadata.checksum,\n actual: currentChecksum\n });\n return undefined;\n }\n }\n\n this.logger.debug('State restored', { pluginId, version: snapshot.version });\n return snapshot.state;\n }\n\n /**\n * Clear state for a plugin\n */\n clearState(pluginId: string): void {\n this.stateSnapshots.delete(pluginId);\n // Note: We don't clear memory store as it might have multiple snapshots\n this.logger.debug('State cleared', { pluginId });\n }\n\n /**\n * Calculate checksum for state verification using SHA-256.\n */\n private calculateChecksum(state: Record<string, any>): string {\n const stateStr = JSON.stringify(state);\n return createHash('sha256').update(stateStr).digest('hex');\n }\n\n /**\n * Shutdown state manager\n */\n shutdown(): void {\n this.stateSnapshots.clear();\n this.memoryStore.clear();\n this.logger.info('State manager shutdown complete');\n }\n}\n\n/**\n * Hot Reload Manager\n * \n * Manages hot reloading of plugins with state preservation\n */\nexport class HotReloadManager {\n private logger: ObjectLogger;\n private stateManager: PluginStateManager;\n private reloadConfigs = new Map<string, HotReloadConfigParsed>();\n private watchHandles = new Map<string, any>();\n private reloadTimers = new Map<string, NodeJS.Timeout>();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'HotReload' });\n this.stateManager = new PluginStateManager(logger);\n }\n\n /**\n * Register a plugin for hot reload\n */\n registerPlugin(pluginName: string, config: HotReloadConfigParsed): void {\n if (!config.enabled) {\n this.logger.debug('Hot reload disabled for plugin', { plugin: pluginName });\n return;\n }\n\n this.reloadConfigs.set(pluginName, config);\n this.logger.info('Plugin registered for hot reload', { \n plugin: pluginName,\n watchPatterns: config.watchPatterns,\n stateStrategy: config.stateStrategy\n });\n }\n\n /**\n * Start watching for changes (requires file system integration)\n */\n startWatching(pluginName: string): void {\n const config = this.reloadConfigs.get(pluginName);\n if (!config || !config.enabled) {\n return;\n }\n\n // Note: Actual file watching would require chokidar or similar\n // This is a placeholder for the integration point\n this.logger.info('File watching started', { \n plugin: pluginName,\n patterns: config.watchPatterns \n });\n }\n\n /**\n * Stop watching for changes\n */\n stopWatching(pluginName: string): void {\n const handle = this.watchHandles.get(pluginName);\n if (handle) {\n // Stop watching (would call chokidar close())\n this.watchHandles.delete(pluginName);\n this.logger.info('File watching stopped', { plugin: pluginName });\n }\n\n // Clear any pending reload timers\n const timer = this.reloadTimers.get(pluginName);\n if (timer) {\n clearTimeout(timer);\n this.reloadTimers.delete(pluginName);\n }\n }\n\n /**\n * Trigger hot reload for a plugin\n */\n async reloadPlugin(\n pluginName: string,\n plugin: Plugin,\n version: string,\n getPluginState: () => Record<string, any>,\n restorePluginState: (state: Record<string, any>) => void\n ): Promise<boolean> {\n const config = this.reloadConfigs.get(pluginName);\n if (!config) {\n this.logger.warn('Cannot reload - plugin not registered', { plugin: pluginName });\n return false;\n }\n\n this.logger.info('Starting hot reload', { plugin: pluginName });\n\n try {\n // Call before reload hooks\n if (config.beforeReload) {\n this.logger.debug('Executing before reload hooks', { \n plugin: pluginName,\n hooks: config.beforeReload \n });\n // Hook execution would be done through kernel's hook system\n }\n\n // Save state if configured\n let snapshotId: string | undefined;\n if (config.preserveState && config.stateStrategy !== 'none') {\n const state = getPluginState();\n snapshotId = await this.stateManager.saveState(\n pluginName,\n version,\n state,\n config\n );\n this.logger.debug('Plugin state saved', { plugin: pluginName, snapshotId });\n }\n\n // Gracefully shutdown the plugin\n if (plugin.destroy) {\n this.logger.debug('Destroying plugin', { plugin: pluginName });\n \n await this.raceShutdownTimeout(\n plugin.destroy(),\n config.shutdownTimeout,\n 'Shutdown timeout'\n );\n this.logger.debug('Plugin destroyed successfully', { plugin: pluginName });\n }\n\n // At this point, the kernel would reload the plugin module\n // This would be handled by the plugin loader\n this.logger.debug('Plugin module would be reloaded here', { plugin: pluginName });\n\n // Restore state if we saved it\n if (snapshotId && config.preserveState) {\n const restoredState = await this.stateManager.restoreState(pluginName, snapshotId);\n if (restoredState) {\n restorePluginState(restoredState);\n this.logger.debug('Plugin state restored', { plugin: pluginName });\n }\n }\n\n // Call after reload hooks\n if (config.afterReload) {\n this.logger.debug('Executing after reload hooks', { \n plugin: pluginName,\n hooks: config.afterReload \n });\n // Hook execution would be done through kernel's hook system\n }\n\n this.logger.info('Hot reload completed successfully', { plugin: pluginName });\n return true;\n } catch (error) {\n this.logger.error('Hot reload failed', { \n plugin: pluginName, \n error \n });\n return false;\n }\n }\n\n /**\n * Race a plugin's `destroy()` against its shutdown-timeout guard, and\n * reclaim the guard the moment the race settles (#4952).\n *\n * The guard used to be armed and then abandoned — byte-for-byte the leak\n * #4813 fixed in the kernel's startup guards (PR #4874) and #4875 fixed in\n * the periodic health checks (PR #4950): when `destroy()` won the race, its\n * `setTimeout` stayed ref'd in the event loop for the full\n * `shutdownTimeout`, so a hot reload that finished in milliseconds still\n * pinned the loop for the whole budget — once per reload, per plugin.\n *\n * Clearing on settle rather than `unref()`-ing at arm time is deliberate.\n * An unref'd guard also stops pinning the loop, but it stops being a guard\n * as well: if `destroy()` never settles and nothing else keeps the loop\n * alive, Node exits before the timer can fire and the timeout is never\n * reported. The guard has to stay ref'd exactly as long as the race is\n * undecided, which is what `clearTimeout` in a `finally` expresses.\n *\n * `shutdown` is widened to `T | PromiseLike<T>` because the Plugin contract\n * permits a synchronous `destroy()` (`Promise<void> | void`); such a hook\n * wins the race immediately and the guard is reclaimed on the same turn.\n */\n private async raceShutdownTimeout<T>(\n shutdown: T | PromiseLike<T>,\n timeout: number,\n message: string\n ): Promise<T> {\n let guard: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n guard = setTimeout(() => {\n reject(new Error(message));\n }, timeout);\n });\n\n try {\n return await Promise.race([shutdown, timeoutPromise]);\n } finally {\n clearTimeout(guard);\n }\n }\n\n /**\n * Schedule a reload with debouncing\n */\n scheduleReload(\n pluginName: string,\n reloadFn: () => Promise<void>\n ): void {\n const config = this.reloadConfigs.get(pluginName);\n if (!config) {\n return;\n }\n\n // Clear existing timer\n const existingTimer = this.reloadTimers.get(pluginName);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Schedule new reload with debounce\n const timer = setTimeout(() => {\n this.logger.debug('Debounce period elapsed, executing reload', { \n plugin: pluginName \n });\n reloadFn().catch(error => {\n this.logger.error('Scheduled reload failed', { \n plugin: pluginName, \n error \n });\n });\n this.reloadTimers.delete(pluginName);\n }, config.debounceDelay);\n\n this.reloadTimers.set(pluginName, timer);\n this.logger.debug('Reload scheduled with debounce', { \n plugin: pluginName,\n delay: config.debounceDelay \n });\n }\n\n /**\n * Get state manager for direct access\n */\n getStateManager(): PluginStateManager {\n return this.stateManager;\n }\n\n /**\n * Shutdown hot reload manager\n */\n shutdown(): void {\n // Stop all watching\n for (const pluginName of this.watchHandles.keys()) {\n this.stopWatching(pluginName);\n }\n\n // Clear all timers\n for (const timer of this.reloadTimers.values()) {\n clearTimeout(timer);\n }\n\n this.reloadConfigs.clear();\n this.watchHandles.clear();\n this.reloadTimers.clear();\n this.stateManager.shutdown();\n \n this.logger.info('Hot reload manager shutdown complete');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { \n SemanticVersion,\n VersionConstraint,\n CompatibilityLevel,\n DependencyConflict\n} from '@objectstack/spec/kernel';\nimport type { ObjectLogger } from './logger.js';\n\n/**\n * Semantic Version Parser and Comparator\n * \n * Implements semantic versioning comparison and constraint matching\n */\nexport class SemanticVersionManager {\n /**\n * Parse a version string into semantic version components\n */\n static parse(versionStr: string): SemanticVersion {\n // Remove 'v' prefix if present\n const cleanVersion = versionStr.replace(/^v/, '');\n \n // Match semver pattern: major.minor.patch[-prerelease][+build]\n const match = cleanVersion.match(\n /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([a-zA-Z0-9.-]+))?(?:\\+([a-zA-Z0-9.-]+))?$/\n );\n\n if (!match) {\n throw new Error(`Invalid semantic version: ${versionStr}`);\n }\n\n return {\n major: parseInt(match[1], 10),\n minor: parseInt(match[2], 10),\n patch: parseInt(match[3], 10),\n preRelease: match[4],\n build: match[5],\n };\n }\n\n /**\n * Convert semantic version back to string\n */\n static toString(version: SemanticVersion): string {\n let str = `${version.major}.${version.minor}.${version.patch}`;\n if (version.preRelease) {\n str += `-${version.preRelease}`;\n }\n if (version.build) {\n str += `+${version.build}`;\n }\n return str;\n }\n\n /**\n * Compare two semantic versions\n * Returns: -1 if a < b, 0 if a === b, 1 if a > b\n */\n static compare(a: SemanticVersion, b: SemanticVersion): number {\n // Compare major, minor, patch\n if (a.major !== b.major) return a.major - b.major;\n if (a.minor !== b.minor) return a.minor - b.minor;\n if (a.patch !== b.patch) return a.patch - b.patch;\n\n // Pre-release versions have lower precedence\n if (a.preRelease && !b.preRelease) return -1;\n if (!a.preRelease && b.preRelease) return 1;\n \n // Compare pre-release versions\n if (a.preRelease && b.preRelease) {\n return a.preRelease.localeCompare(b.preRelease);\n }\n\n return 0;\n }\n\n /**\n * Check if version satisfies constraint\n */\n static satisfies(version: SemanticVersion, constraint: VersionConstraint): boolean {\n const constraintStr = constraint as string;\n\n // Any version\n if (constraintStr === '*' || constraintStr === 'latest') {\n return true;\n }\n\n // Exact version\n if (/^[\\d.]+$/.test(constraintStr)) {\n const exact = this.parse(constraintStr);\n return this.compare(version, exact) === 0;\n }\n\n // Caret range (^): Compatible with version\n if (constraintStr.startsWith('^')) {\n const base = this.parse(constraintStr.slice(1));\n return (\n version.major === base.major &&\n this.compare(version, base) >= 0\n );\n }\n\n // Tilde range (~): Approximately equivalent\n if (constraintStr.startsWith('~')) {\n const base = this.parse(constraintStr.slice(1));\n return (\n version.major === base.major &&\n version.minor === base.minor &&\n this.compare(version, base) >= 0\n );\n }\n\n // Greater than or equal\n if (constraintStr.startsWith('>=')) {\n const base = this.parse(constraintStr.slice(2));\n return this.compare(version, base) >= 0;\n }\n\n // Greater than\n if (constraintStr.startsWith('>')) {\n const base = this.parse(constraintStr.slice(1));\n return this.compare(version, base) > 0;\n }\n\n // Less than or equal\n if (constraintStr.startsWith('<=')) {\n const base = this.parse(constraintStr.slice(2));\n return this.compare(version, base) <= 0;\n }\n\n // Less than\n if (constraintStr.startsWith('<')) {\n const base = this.parse(constraintStr.slice(1));\n return this.compare(version, base) < 0;\n }\n\n // Range (1.2.3 - 2.3.4)\n const rangeMatch = constraintStr.match(/^([\\d.]+)\\s*-\\s*([\\d.]+)$/);\n if (rangeMatch) {\n const min = this.parse(rangeMatch[1]);\n const max = this.parse(rangeMatch[2]);\n return this.compare(version, min) >= 0 && this.compare(version, max) <= 0;\n }\n\n return false;\n }\n\n /**\n * Determine compatibility level between two versions\n */\n static getCompatibilityLevel(from: SemanticVersion, to: SemanticVersion): CompatibilityLevel {\n const cmp = this.compare(from, to);\n\n // Same version\n if (cmp === 0) {\n return 'fully-compatible';\n }\n\n // Major version changed - breaking changes\n if (from.major !== to.major) {\n return 'breaking-changes';\n }\n\n // Minor version increased - backward compatible\n if (from.minor < to.minor) {\n return 'backward-compatible';\n }\n\n // Patch version increased - fully compatible\n if (from.patch < to.patch) {\n return 'fully-compatible';\n }\n\n // Downgrade - incompatible\n return 'incompatible';\n }\n}\n\n/**\n * Plugin Dependency Resolver\n * \n * Resolves plugin dependencies using topological sorting and conflict detection\n */\nexport class DependencyResolver {\n private logger: ObjectLogger;\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'DependencyResolver' });\n }\n\n /**\n * Resolve dependencies using topological sort\n */\n resolve(\n plugins: Map<string, { version?: string; dependencies?: string[] }>\n ): string[] {\n const graph = new Map<string, string[]>();\n const inDegree = new Map<string, number>();\n\n // Build dependency graph\n for (const [pluginName, pluginInfo] of plugins) {\n if (!graph.has(pluginName)) {\n graph.set(pluginName, []);\n inDegree.set(pluginName, 0);\n }\n\n const deps = pluginInfo.dependencies || [];\n for (const dep of deps) {\n // Check if dependency exists\n if (!plugins.has(dep)) {\n throw new Error(`Missing dependency: ${pluginName} requires ${dep}`);\n }\n\n // Add edge\n if (!graph.has(dep)) {\n graph.set(dep, []);\n inDegree.set(dep, 0);\n }\n graph.get(dep)!.push(pluginName);\n inDegree.set(pluginName, (inDegree.get(pluginName) || 0) + 1);\n }\n }\n\n // Topological sort using Kahn's algorithm\n const queue: string[] = [];\n const result: string[] = [];\n\n // Add all nodes with no incoming edges\n for (const [node, degree] of inDegree) {\n if (degree === 0) {\n queue.push(node);\n }\n }\n\n while (queue.length > 0) {\n const node = queue.shift()!;\n result.push(node);\n\n // Reduce in-degree for dependent nodes\n const dependents = graph.get(node) || [];\n for (const dependent of dependents) {\n const newDegree = (inDegree.get(dependent) || 0) - 1;\n inDegree.set(dependent, newDegree);\n \n if (newDegree === 0) {\n queue.push(dependent);\n }\n }\n }\n\n // Check for circular dependencies\n if (result.length !== plugins.size) {\n const remaining = Array.from(plugins.keys()).filter(p => !result.includes(p));\n this.logger.error('Circular dependency detected', { remaining });\n throw new Error(`Circular dependency detected among: ${remaining.join(', ')}`);\n }\n\n this.logger.debug('Dependencies resolved', { order: result });\n return result;\n }\n\n /**\n * Detect dependency conflicts\n */\n detectConflicts(\n plugins: Map<string, { version: string; dependencies?: Record<string, VersionConstraint> }>\n ): DependencyConflict[] {\n const conflicts: DependencyConflict[] = [];\n const versionRequirements = new Map<string, Map<string, VersionConstraint>>();\n\n // Collect all version requirements\n for (const [pluginName, pluginInfo] of plugins) {\n if (!pluginInfo.dependencies) continue;\n\n for (const [depName, constraint] of Object.entries(pluginInfo.dependencies)) {\n if (!versionRequirements.has(depName)) {\n versionRequirements.set(depName, new Map());\n }\n versionRequirements.get(depName)!.set(pluginName, constraint);\n }\n }\n\n // Check for version mismatches\n for (const [depName, requirements] of versionRequirements) {\n const depInfo = plugins.get(depName);\n if (!depInfo) continue;\n\n const depVersion = SemanticVersionManager.parse(depInfo.version);\n const unsatisfied: Array<{ pluginId: string; version: string }> = [];\n\n for (const [requiringPlugin, constraint] of requirements) {\n if (!SemanticVersionManager.satisfies(depVersion, constraint)) {\n unsatisfied.push({\n pluginId: requiringPlugin,\n version: constraint as string,\n });\n }\n }\n\n if (unsatisfied.length > 0) {\n conflicts.push({\n type: 'version-mismatch',\n severity: 'error',\n description: `Version mismatch for ${depName}: detected ${unsatisfied.length} unsatisfied requirements`,\n plugins: [\n { pluginId: depName, version: depInfo.version },\n ...unsatisfied,\n ],\n resolutions: [{\n strategy: 'upgrade',\n description: `Upgrade ${depName} to satisfy all constraints`,\n targetPlugins: [depName],\n automatic: false,\n } as any],\n });\n }\n }\n\n // Check for circular dependencies (will be caught by resolve())\n try {\n this.resolve(new Map(\n Array.from(plugins.entries()).map(([name, info]) => [\n name,\n { version: info.version, dependencies: info.dependencies ? Object.keys(info.dependencies) : [] }\n ])\n ));\n } catch (error) {\n if (error instanceof Error && error.message.includes('Circular dependency')) {\n conflicts.push({\n type: 'circular-dependency',\n severity: 'critical',\n description: error.message,\n plugins: [], // Would need to extract from error\n resolutions: [{\n strategy: 'manual',\n description: 'Remove circular dependency by restructuring plugins',\n automatic: false,\n } as any],\n });\n }\n }\n\n return conflicts;\n }\n\n /**\n * Find best version that satisfies all constraints\n */\n findBestVersion(\n availableVersions: string[],\n constraints: VersionConstraint[]\n ): string | undefined {\n // Parse and sort versions (highest first)\n const versions = availableVersions\n .map(v => ({ str: v, parsed: SemanticVersionManager.parse(v) }))\n .sort((a, b) => -SemanticVersionManager.compare(a.parsed, b.parsed));\n\n // Find highest version that satisfies all constraints\n for (const version of versions) {\n const satisfiesAll = constraints.every(constraint =>\n SemanticVersionManager.satisfies(version.parsed, constraint)\n );\n\n if (satisfiesAll) {\n return version.str;\n }\n }\n\n return undefined;\n }\n\n /**\n * Check if dependencies form a valid DAG (no cycles)\n */\n isAcyclic(dependencies: Map<string, string[]>): boolean {\n try {\n const plugins = new Map(\n Array.from(dependencies.entries()).map(([name, deps]) => [\n name,\n { dependencies: deps }\n ])\n );\n this.resolve(plugins);\n return true;\n } catch {\n return false;\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { ObjectLogger } from './logger.js';\n\n/**\n * Namespace entry representing an object/view/flow etc. registered by a package.\n */\nexport interface NamespaceEntry {\n /** The namespace path (e.g. \"objects.project_task\", \"views.task_list\") */\n namespace: string;\n /** The package that owns this namespace */\n packageId: string;\n /** When this entry was registered */\n registeredAt: string;\n}\n\n/**\n * Result of a namespace conflict check.\n */\nexport interface NamespaceConflict {\n /** The conflicting namespace path */\n namespace: string;\n /** The package that currently owns this namespace */\n existingPackageId: string;\n /** The package attempting to register the same namespace */\n incomingPackageId: string;\n /** A suggested alternative name to avoid the conflict */\n suggestion?: string;\n}\n\n/**\n * Result of namespace availability check.\n */\nexport interface NamespaceCheckResult {\n /** Whether all requested namespaces are available */\n available: boolean;\n /** List of conflicts detected */\n conflicts: NamespaceConflict[];\n /** Suggested alternatives for each conflict */\n suggestions: Record<string, string>;\n}\n\n/**\n * Namespace Resolver\n *\n * Manages namespace registration for installed packages and detects collisions\n * during install-time. Each metadata item (object, view, flow, page, etc.)\n * produces a namespace like `objects.<name>` or `views.<name>`.\n *\n * When a new package declares objects, views, or other metadata that would\n * collide with an existing package's metadata, this resolver reports the\n * conflicts and suggests prefixed alternatives.\n */\nexport class NamespaceResolver {\n private logger: ObjectLogger;\n private registry: Map<string, NamespaceEntry> = new Map();\n\n constructor(logger: ObjectLogger) {\n this.logger = logger.child({ component: 'NamespaceResolver' });\n }\n\n /**\n * Register namespaces owned by a package.\n */\n register(packageId: string, namespaces: string[]): void {\n const now = new Date().toISOString();\n for (const ns of namespaces) {\n if (this.registry.has(ns)) {\n const existing = this.registry.get(ns)!;\n if (existing.packageId !== packageId) {\n this.logger.warn('Overwriting namespace entry', { namespace: ns, existing: existing.packageId, incoming: packageId });\n }\n }\n this.registry.set(ns, { namespace: ns, packageId, registeredAt: now });\n this.logger.debug('Namespace registered', { namespace: ns, packageId });\n }\n }\n\n /**\n * Unregister all namespaces belonging to a package.\n */\n unregister(packageId: string): string[] {\n const removed: string[] = [];\n for (const [ns, entry] of this.registry) {\n if (entry.packageId === packageId) {\n this.registry.delete(ns);\n removed.push(ns);\n }\n }\n this.logger.debug('Namespaces unregistered', { packageId, count: removed.length });\n return removed;\n }\n\n /**\n * Check whether a set of namespaces is available for a given package.\n */\n checkAvailability(packageId: string, namespaces: string[]): NamespaceCheckResult {\n const conflicts: NamespaceConflict[] = [];\n const suggestions: Record<string, string> = {};\n\n for (const ns of namespaces) {\n const existing = this.registry.get(ns);\n if (existing && existing.packageId !== packageId) {\n const suggestion = this.suggestAlternative(ns, packageId);\n conflicts.push({\n namespace: ns,\n existingPackageId: existing.packageId,\n incomingPackageId: packageId,\n suggestion,\n });\n suggestions[ns] = suggestion;\n }\n }\n\n return {\n available: conflicts.length === 0,\n conflicts,\n suggestions,\n };\n }\n\n /**\n * Extract namespace strings from a package's metadata definition.\n */\n extractNamespaces(config: Record<string, unknown>): string[] {\n const namespaces: string[] = [];\n const categories = [\n 'objects', 'views', 'pages', 'flows', 'workflows',\n 'apps', 'dashboards', 'reports', 'actions', 'agents',\n ];\n\n for (const category of categories) {\n const items = config[category];\n if (Array.isArray(items)) {\n for (const item of items) {\n const name = (item as Record<string, unknown>)?.name;\n if (typeof name === 'string') {\n namespaces.push(`${category}.${name}`);\n }\n }\n } else if (items && typeof items === 'object') {\n for (const key of Object.keys(items as object)) {\n namespaces.push(`${category}.${key}`);\n }\n }\n }\n\n return namespaces;\n }\n\n /**\n * Get all registered entries.\n */\n getRegistry(): ReadonlyMap<string, NamespaceEntry> {\n return this.registry;\n }\n\n /**\n * Get all namespaces belonging to a specific package.\n */\n getPackageNamespaces(packageId: string): string[] {\n const namespaces: string[] = [];\n for (const [ns, entry] of this.registry) {\n if (entry.packageId === packageId) {\n namespaces.push(ns);\n }\n }\n return namespaces;\n }\n\n /**\n * Generate a prefixed alternative namespace to avoid conflicts.\n */\n private suggestAlternative(ns: string, packageId: string): string {\n // Extract the short package name for prefixing\n const shortName = packageId\n .replace(/^@[^/]+\\//, '')\n .replace(/^plugin-/, '')\n .replace(/-/g, '_');\n\n const parts = ns.split('.');\n if (parts.length >= 2) {\n // e.g. \"objects.task\" → \"objects.crm_task\"\n return `${parts[0]}.${shortName}_${parts.slice(1).join('.')}`;\n }\n return `${shortName}_${ns}`;\n }\n}\n"],"mappings":"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","names":["nodePath","ServiceLifecycle","safeJsonParse","ymd","createHash","createHash"]}
|