@objectstack/core 16.1.0 → 17.0.0-rc.1

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Logger, IServiceRegistry } from '@objectstack/spec/contracts';
2
- export { IDataDriver, IDataEngine, IHttpRequest, IHttpResponse, IHttpServer, Logger, Middleware, RouteHandler } from '@objectstack/spec/contracts';
1
+ import { Logger, LifecycleEventName, IServiceRegistry } from '@objectstack/spec/contracts';
2
+ export { EngineSchemaRegistryView, IDataDriver, IDataEngine, IHttpRequest, IHttpResponse, IHttpServer, IObjectQLEngine, Logger, Middleware, RouteHandler } from '@objectstack/spec/contracts';
3
3
  import { z } from 'zod';
4
4
  import { LoggerConfig } from '@objectstack/spec/system';
5
5
  import { ObjectLogger } from './logger.js';
@@ -9,6 +9,7 @@ import * as QA from '@objectstack/spec/qa';
9
9
  import { KeyObject } from 'node:crypto';
10
10
  import { PluginCapability, PluginPermissions as PluginPermissions$1, PluginPermissionSet, ResourceType, PermissionAction, PluginPermission, SandboxConfig, KernelSecurityScanResult, KernelSecurityVulnerability, PluginHealthCheck, PluginHealthStatus as PluginHealthStatus$1, PluginHealthReport, HotReloadConfig, VersionConstraint, DependencyConflict, SemanticVersion, CompatibilityLevel } from '@objectstack/spec/kernel';
11
11
  import { AuthzPosture } from '@objectstack/spec/security';
12
+ export { nextUtcCalendarDay, utcInstantMs } from '@objectstack/spec/data';
12
13
 
13
14
  /**
14
15
  * Service Lifecycle Types
@@ -213,6 +214,12 @@ declare class ObjectKernel {
213
214
  private startedPlugins;
214
215
  private pluginStartTimes;
215
216
  private shutdownHandlers;
217
+ /**
218
+ * Name of the plugin whose init() is currently executing (Phase 1 is
219
+ * sequential, so at most one). Lets a getService miss during init name
220
+ * the structural fault (#4131) instead of only the symptom.
221
+ */
222
+ private currentlyInitializing?;
216
223
  constructor(config?: ObjectKernelConfig);
217
224
  /**
218
225
  * Register a plugin with enhanced validation
@@ -290,9 +297,27 @@ declare class ObjectKernel {
290
297
  */
291
298
  getState(): string;
292
299
  private initPluginWithTimeout;
300
+ /**
301
+ * Whether a service is resolvable on this kernel right now — direct
302
+ * registration or a loader-registered factory. Backs the init-service
303
+ * contract checks (#4131).
304
+ */
305
+ private hasAnyService;
306
+ /**
307
+ * When a getService miss happens while a plugin's init() is running,
308
+ * append the structural diagnosis (#4131): which plugin was initializing,
309
+ * and — when a composed plugin declares the service — who provides it.
310
+ * Empty string outside Phase 1, so non-boot messages stay unchanged.
311
+ */
312
+ private describeInitOrderFault;
293
313
  private startPluginWithTimeout;
294
314
  private rollbackStartedPlugins;
295
315
  private performShutdown;
316
+ /**
317
+ * Topological order over `dependencies` (hard) + `optionalDependencies`
318
+ * (order-if-present) — ADR-0116, #4131. One implementation shared with
319
+ * LiteKernel via `plugin-order.ts`.
320
+ */
296
321
  private resolveDependencies;
297
322
  private registerShutdownSignals;
298
323
  /**
@@ -350,17 +375,22 @@ interface PluginContext {
350
375
  */
351
376
  getServices(): Map<string, any>;
352
377
  /**
353
- * Register a hook handler
378
+ * Register a hook handler.
379
+ *
380
+ * Known lifecycle-bus names (see `IPluginLifecycleEvents` in
381
+ * `@objectstack/spec`) autocomplete; the bus stays open to custom
382
+ * cross-plugin event names, so any string remains valid.
383
+ *
354
384
  * @param name - Hook name (e.g., 'kernel:ready', 'data:beforeInsert')
355
385
  * @param handler - Hook handler function
356
386
  */
357
- hook(name: string, handler: (...args: any[]) => void | Promise<void>): void;
387
+ hook(name: LifecycleEventName | (string & {}), handler: (...args: any[]) => void | Promise<void>): void;
358
388
  /**
359
389
  * Trigger a hook
360
- * @param name - Hook name
390
+ * @param name - Hook name (known lifecycle names autocomplete; custom names stay legal)
361
391
  * @param args - Arguments to pass to hook handlers
362
392
  */
363
- trigger(name: string, ...args: any[]): Promise<void>;
393
+ trigger(name: LifecycleEventName | (string & {}), ...args: any[]): Promise<void>;
364
394
  /**
365
395
  * Logger instance
366
396
  */
@@ -393,8 +423,35 @@ interface Plugin {
393
423
  /**
394
424
  * List of other plugin names that this plugin depends on.
395
425
  * The kernel ensures these plugins are initialized before this one.
426
+ * A name that is not registered on the kernel is a boot error.
396
427
  */
397
428
  dependencies?: string[];
429
+ /**
430
+ * Soft dependencies — order-if-present (ADR-0116, #4131).
431
+ * Registered names are hoisted ahead exactly like `dependencies`;
432
+ * absent names are silently skipped instead of failing the boot.
433
+ * For plugins that DEGRADE gracefully without the dependency but must
434
+ * never initialize before it when both are composed (e.g. AppPlugin on
435
+ * an engine-less metadata-only kernel).
436
+ */
437
+ optionalDependencies?: string[];
438
+ /**
439
+ * Services this plugin resolves SYNCHRONOUSLY during `init()`
440
+ * (ADR-0116, #4131). The kernel validates the resolved order before
441
+ * Phase 1 (a required service whose only declared provider initializes
442
+ * later is a named boot error) and re-checks immediately before this
443
+ * plugin's init runs. Declare only hard init-time needs — a service the
444
+ * init merely probes behind a try/catch does not belong here.
445
+ */
446
+ requiresServices?: string[];
447
+ /**
448
+ * Services this plugin's `init()` UNCONDITIONALLY registers
449
+ * (ADR-0116, #4131). Powers the pre-Phase-1 ordering validation and
450
+ * lets misordering errors name the provider. Never declare a service
451
+ * that is registered conditionally (option-gated, environment-gated):
452
+ * the kernel would blame orderings this plugin cannot satisfy.
453
+ */
454
+ providesServices?: string[];
398
455
  /**
399
456
  * Init Phase: Register services
400
457
  * Called when kernel is initializing.
@@ -437,6 +494,12 @@ declare abstract class ObjectKernelBase {
437
494
  protected state: KernelState;
438
495
  protected logger: Logger;
439
496
  protected context: PluginContext;
497
+ /**
498
+ * Name of the plugin whose init() is currently executing (Phase 1 runs
499
+ * sequentially, so there is at most one). Lets a getService miss during
500
+ * init name the structural fault (#4131) instead of only the symptom.
501
+ */
502
+ protected currentlyInitializing?: string;
440
503
  constructor(logger: Logger);
441
504
  /**
442
505
  * Validate kernel state
@@ -454,10 +517,30 @@ declare abstract class ObjectKernelBase {
454
517
  */
455
518
  protected createContext(): PluginContext;
456
519
  /**
457
- * Resolve plugin dependencies using topological sort
520
+ * Resolve plugin dependencies using topological sort — `dependencies`
521
+ * hard, `optionalDependencies` order-if-present (ADR-0116, #4131). One
522
+ * implementation shared with ObjectKernel via `plugin-order.ts`.
458
523
  * @returns Ordered list of plugins (dependencies first)
459
524
  */
460
525
  protected resolveDependencies(): Plugin[];
526
+ /**
527
+ * Whether a service is registered on this kernel right now. Backs the
528
+ * init-service contract checks (#4131).
529
+ */
530
+ protected hasRegisteredService(name: string): boolean;
531
+ /**
532
+ * Pre-Phase-1 ordering validation (ADR-0116, #4131): a plugin whose
533
+ * `requiresServices` names a service provided only by a LATER plugin is
534
+ * a named boot error before any init side effects.
535
+ */
536
+ protected validateInitServices(ordered: Plugin[]): void;
537
+ /**
538
+ * When a getService miss happens while a plugin's init() is running,
539
+ * append the structural diagnosis (#4131): which plugin was initializing,
540
+ * and — when a composed plugin declares the service — who provides it.
541
+ * Empty string outside Phase 1, so non-boot messages stay unchanged.
542
+ */
543
+ protected describeInitOrderFault(serviceName: string): string;
461
544
  /**
462
545
  * Run plugin init phase
463
546
  * @param plugin - Plugin to initialize
@@ -495,6 +578,90 @@ declare abstract class ObjectKernelBase {
495
578
  abstract destroy(): Promise<void>;
496
579
  }
497
580
 
581
+ /**
582
+ * Plugin ordering + init-service contract (ADR-0116, #4131).
583
+ *
584
+ * The kernel resolves BOTH init and start order from the plugin dependency
585
+ * graph, so `kernel.use()` registration order proves nothing. Twice a plugin
586
+ * relied on list position anyway and shipped a boot that dies inside init —
587
+ * the first cut of DefaultDatasourcePlugin (started after boot schema-sync;
588
+ * server with no tables) and AppPlugin (#4085: `manifest` grabbed in init
589
+ * before ObjectQLPlugin registered it). Both times the fix existed only as a
590
+ * convention: put the plugin in the right slot, write a comment. This module
591
+ * is the enforced form of that contract, shared by ObjectKernel and
592
+ * LiteKernel so there is exactly one ordering semantic:
593
+ *
594
+ * - `dependencies` — hard: hoisted ahead, missing ⇒ boot error (unchanged).
595
+ * - `optionalDependencies` — order-if-present: hoisted ahead when composed,
596
+ * silently skipped when absent. For plugins that DEGRADE without the
597
+ * dependency but must never init before it (AppPlugin on an engine-less
598
+ * metadata-only kernel).
599
+ * - `requiresServices` — services a plugin resolves SYNCHRONOUSLY during
600
+ * `init()`. Validated before Phase 1 (provable misordering ⇒ named error
601
+ * instead of a crash inside init) and again immediately before each init
602
+ * (authoritative: the service is either registered by now or init dies).
603
+ * - `providesServices` — services a plugin's `init()` UNCONDITIONALLY
604
+ * registers. Powers the pre-Phase-1 check and the named diagnostics.
605
+ * Declare only unconditional registrations: a conditional service (e.g.
606
+ * one gated behind an option) would indict this plugin for orderings it
607
+ * cannot actually satisfy.
608
+ */
609
+ /**
610
+ * The ordering-relevant surface of a kernel plugin. Structural on purpose:
611
+ * ObjectKernel sorts `PluginMetadata`, LiteKernel sorts `Plugin`, and both
612
+ * satisfy this shape.
613
+ */
614
+ interface OrderablePlugin {
615
+ name: string;
616
+ /** Hard dependencies — hoisted ahead; missing ⇒ boot error. */
617
+ dependencies?: string[];
618
+ /** Soft dependencies — hoisted ahead when composed, skipped when absent. */
619
+ optionalDependencies?: string[];
620
+ /** Services resolved synchronously during init(). */
621
+ requiresServices?: string[];
622
+ /** Services init() unconditionally registers. */
623
+ providesServices?: string[];
624
+ }
625
+ /**
626
+ * Topologically order plugins: every plugin's `dependencies` (throw when
627
+ * missing) and `optionalDependencies` (skip when missing) init before it.
628
+ * Insertion order is preserved for plugins with no edges between them.
629
+ * Cycles through either edge kind throw — an optional dependency is a real
630
+ * edge whenever both sides are composed.
631
+ */
632
+ declare function resolvePluginOrder<P extends OrderablePlugin>(plugins: Map<string, P>): P[];
633
+ /**
634
+ * Pre-Phase-1 check: walk the resolved order and prove no plugin requires a
635
+ * service whose only declared provider initializes AFTER it. A violation is
636
+ * the exact #4085 class — misplaced composition — reported as a named,
637
+ * structural boot error BEFORE any init side effects, instead of a bare
638
+ * "Service not found" thrown from inside the victim's init.
639
+ *
640
+ * Deliberately does NOT fail when a required service has no declared provider
641
+ * and is not yet registered: an earlier plugin may register it without
642
+ * declaring `providesServices`. That case is settled authoritatively by
643
+ * {@link assertInitServiceRequirements} immediately before the requiring
644
+ * plugin's init runs.
645
+ */
646
+ declare function validateInitServiceContract<P extends OrderablePlugin>(ordered: P[], isServiceRegistered: (name: string) => boolean): void;
647
+ /**
648
+ * Diagnosis suffix for a getService miss that happens WHILE a plugin's
649
+ * init() is running: names the initializing plugin and — when a composed
650
+ * plugin declares the service — the provider and the directive to declare
651
+ * the ordering. Returns '' when no plugin is initializing, so non-boot
652
+ * error messages stay byte-identical. Shared by both kernels.
653
+ */
654
+ declare function describeInitOrderFault(currentlyInitializing: string | undefined, plugins: Iterable<OrderablePlugin>, serviceName: string): string;
655
+ /**
656
+ * Just-before-init check: every service in `requiresServices` must be
657
+ * registered at the moment the plugin's init() is about to run. At this
658
+ * point the verdict is authoritative — Phase 1 runs sequentially, so a
659
+ * service absent now is absent for this init, and the init would die on a
660
+ * bare "Service not found" anyway. This turns that crash into a named
661
+ * composition error.
662
+ */
663
+ declare function assertInitServiceRequirements(plugin: OrderablePlugin, isServiceRegistered: (name: string) => boolean): void;
664
+
498
665
  /**
499
666
  * ObjectKernel - MiniKernel Architecture
500
667
  *
@@ -1751,6 +1918,14 @@ interface ResolvedAuthzContext {
1751
1918
  tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
1752
1919
  /** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */
1753
1920
  org_user_ids: string[];
1921
+ /**
1922
+ * [ADR-0105 D2] Every organization this principal currently holds a VALID
1923
+ * membership in — the caller's org access set, and the read reach of the
1924
+ * `group` tenancy posture (Layer 0 becomes `organization_id IN (...)`).
1925
+ * Resolved here, once, so no surface re-derives it; empty for an anonymous or
1926
+ * membership-less principal, which fails the group wall closed.
1927
+ */
1928
+ accessible_org_ids: string[];
1754
1929
  /**
1755
1930
  * [ADR-0095 D2/D3] The monotonic posture rung this principal resolves to,
1756
1931
  * DERIVED once here from held capability grants (never a better-auth role):
@@ -1788,6 +1963,8 @@ interface UserAuthzGrants {
1788
1963
  systemPermissions: string[];
1789
1964
  /** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */
1790
1965
  org_user_ids: string[];
1966
+ /** [ADR-0105 D2] Organizations this user holds a currently-valid membership in. */
1967
+ accessible_org_ids: string[];
1791
1968
  tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
1792
1969
  posture?: AuthzPosture;
1793
1970
  /** The user's unique email (`sys_user`), for `current_user.email` owner RLS. */
@@ -1987,18 +2164,16 @@ declare function evaluateAuthGate(sessionUser: any, path: string): AuthGate | nu
1987
2164
 
1988
2165
  /** HTTP status every seam returns for an anonymous-denied request. */
1989
2166
  declare const ANONYMOUS_DENY_STATUS: 401;
1990
- /** Stable machine code (mirrors the REST `enforceAuth` seam). */
1991
- declare const ANONYMOUS_DENY_CODE: "unauthenticated";
2167
+ /** Stable machine code (mirrors the REST `enforceAuth` seam). ADR-0112: SCREAMING, a `StandardErrorCode` member. */
2168
+ declare const ANONYMOUS_DENY_CODE: "UNAUTHENTICATED";
1992
2169
  /** Human-facing message. */
1993
2170
  declare const ANONYMOUS_DENY_MESSAGE = "Authentication is required to access this endpoint.";
1994
2171
  /** The single 401 body shape every seam returns: `{ error, message }`. */
1995
2172
  declare const ANONYMOUS_DENY_BODY: {
1996
- readonly error: "unauthenticated";
2173
+ readonly error: "UNAUTHENTICATED";
1997
2174
  readonly message: "Authentication is required to access this endpoint.";
1998
2175
  };
1999
2176
  interface AnonymousDenyInput {
2000
- /** The `requireAuth` posture. Falsy ⇒ no-op (demo / single-tenant). */
2001
- requireAuth: boolean | undefined;
2002
2177
  /** Resolved caller id, if any. */
2003
2178
  userId?: string | null;
2004
2179
  /** Internal system context (never set on inbound HTTP; cannot be forged). */
@@ -2105,6 +2280,7 @@ declare function calendarPartsInTzOrUtc(d: Date, tz?: string): CalendarParts;
2105
2280
  * reference-tz calendar, so its bucket boundary is that tz's midnight instant.
2106
2281
  */
2107
2282
  declare function zonedDateStartToUtcMs(ymd: string, tz?: string): number;
2283
+
2108
2284
  /**
2109
2285
  * Granularity of a canonical date-bucket key. Mirrors `@objectstack/spec`'s
2110
2286
  * `DateGranularity` enum but kept as a local literal union so this low-level
@@ -2123,12 +2299,16 @@ type BucketGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year';
2123
2299
  * `datetime` field in a reference timezone layers that on top (and, per
2124
2300
  * ADR-0053, a `date` field compares against these `YYYY-MM-DD` bounds directly).
2125
2301
  *
2126
- * Returns `null` for the null/empty bucket, an unparseable key, or a key that
2127
- * is shape-valid but out of range (e.g. `2026-13`, a `-W53` in a 52-week year,
2302
+ * Returns `null` for the empty bucket, an unparseable key, or a key that is
2303
+ * shape-valid but out of range (e.g. `2026-13`, a `-W53` in a 52-week year,
2128
2304
  * `2026-02-30`). Callers drop the range and fall back to an unscoped (superset)
2129
2305
  * drill rather than emit a wrong bound.
2306
+ *
2307
+ * `key` admits `null` because that IS the empty bucket's key on both aggregation
2308
+ * paths (#3839); callers pass a grouped row's dimension value straight through
2309
+ * rather than casting a lie.
2130
2310
  */
2131
- declare function bucketKeyToCalendarRange(key: string, granularity: BucketGranularity): {
2311
+ declare function bucketKeyToCalendarRange(key: string | null | undefined, granularity: BucketGranularity): {
2132
2312
  start: string;
2133
2313
  end: string;
2134
2314
  } | null;
@@ -2251,15 +2431,114 @@ declare function withTransientRetry<T>(fn: (attempt: number) => Promise<T>, opts
2251
2431
  */
2252
2432
  declare function bulkWrite<TRow, TRecord = any>(rows: TRow[], opts: BulkWriteOptions<TRow, TRecord>): Promise<BulkWriteRowResult<TRecord>[]>;
2253
2433
 
2434
+ /**
2435
+ * The slice of an execution context the resolver reads. Structural on purpose —
2436
+ * see {@link filterTokenContextFrom}.
2437
+ */
2438
+ interface ExecutionContextLike {
2439
+ readonly userId?: string;
2440
+ readonly tenantId?: string;
2441
+ readonly timezone?: string;
2442
+ }
2443
+ /**
2444
+ * The request-scoped values a placeholder can resolve against.
2445
+ *
2446
+ * `now` is captured ONCE per resolve call so every token in one filter shares
2447
+ * an instant — otherwise a `$gte {current_month_start}` / `$lt
2448
+ * {next_month_start}` pair evaluated microseconds apart could straddle a
2449
+ * month boundary and silently drop a row.
2450
+ */
2451
+ interface FilterTokenResolutionContext {
2452
+ /** Reference instant. Defaults to `new Date()` at call time. */
2453
+ now?: Date;
2454
+ /** IANA reference timezone for calendar boundaries. Defaults to UTC. */
2455
+ timezone?: string;
2456
+ /** Resolves `{current_user_id}`. */
2457
+ userId?: string;
2458
+ /** Resolves `{current_org_id}`. */
2459
+ orgId?: string;
2460
+ }
2461
+ /**
2462
+ * Raised when a filter carries a placeholder outside the vocabulary.
2463
+ *
2464
+ * Carries `status`/`code` so the REST layer's generic 4xx passthrough maps it
2465
+ * to a **400 with a fixable message** rather than a 500: the caller's filter is
2466
+ * malformed, the server is fine. (Same convention plugin-sharing uses for its
2467
+ * record-scope denial — no runtime dependency in either direction.)
2468
+ */
2469
+ declare class UnknownFilterTokenError extends Error {
2470
+ readonly token: string;
2471
+ readonly suggestion?: string;
2472
+ readonly status = 400;
2473
+ readonly code = "FILTER_TOKEN_UNKNOWN";
2474
+ constructor(token: string, suggestion?: string);
2475
+ }
2476
+ /**
2477
+ * Raised when a token IS in the vocabulary but the request carries no value
2478
+ * for it — an unauthenticated caller filtering on `{current_user_id}`.
2479
+ *
2480
+ * Distinct from {@link UnknownFilterTokenError} because the fix is different:
2481
+ * the metadata is correct, the context is not. Never silently resolves to
2482
+ * `null`/`undefined`, which on most drivers degrades to `IS NULL` and would
2483
+ * quietly hand back rows the filter was written to exclude.
2484
+ */
2485
+ declare class UnresolvedFilterTokenError extends Error {
2486
+ readonly token: string;
2487
+ /** 400, not 500 — see {@link UnknownFilterTokenError}. */
2488
+ readonly status = 400;
2489
+ readonly code = "FILTER_TOKEN_UNRESOLVED";
2490
+ constructor(token: string, detail: string);
2491
+ }
2492
+ /**
2493
+ * Resolve one token NAME (the bit inside the braces) to its concrete value.
2494
+ * Throws {@link UnresolvedFilterTokenError} for a vocabulary token the request
2495
+ * carries no value for. Returns `undefined` only when the token is outside the
2496
+ * vocabulary — callers turn that into {@link UnknownFilterTokenError}.
2497
+ */
2498
+ declare function resolveFilterToken(token: string, ctx?: FilterTokenResolutionContext): unknown;
2499
+ /**
2500
+ * Deep-replace every fully-wrapped placeholder in `filter` with its resolved
2501
+ * value, returning a NEW tree (the caller's metadata is never mutated — a view
2502
+ * or dataset definition is shared across requests, so resolving in place would
2503
+ * bake one request's user id, and one day's dates, into every later render).
2504
+ *
2505
+ * Returns the input unchanged, by reference, when it holds no placeholders.
2506
+ */
2507
+ declare function resolveFilterTokens<T>(filter: T, ctx?: FilterTokenResolutionContext): T;
2508
+ /**
2509
+ * Convenience bridge from an execution context to the resolver's inputs.
2510
+ * `{current_org_id}` reads `tenantId` — the active organization IS the tenant
2511
+ * on the read path (same value the RLS compiler binds to
2512
+ * `current_user.organization_id`).
2513
+ *
2514
+ * Typed structurally, not as `ExecutionContext`, so both the parsed context
2515
+ * (defaults applied) and the pre-parse `ExecutionContextInput` a caller holds
2516
+ * mid-pipeline satisfy it. The three fields read here are optional in both.
2517
+ */
2518
+ declare function filterTokenContextFrom(execCtx: ExecutionContextLike | undefined, now?: Date): FilterTokenResolutionContext;
2519
+
2254
2520
  /**
2255
2521
  * In-memory Map-backed cache fallback.
2256
2522
  *
2257
2523
  * Implements the ICacheService contract with basic get/set/delete/has/clear
2258
2524
  * and TTL expiry. Used by ObjectKernel as an automatic fallback when no
2259
2525
  * real cache plugin (e.g. Redis) is registered.
2526
+ *
2527
+ * [#4058] Self-describes as `degraded`, not `stub` (ADR-0076 D12): this is a
2528
+ * real cache — it stores, expires, and reports true stats — just process-local
2529
+ * and unshared. The non-standard `_fallback: true` it used to carry was read by
2530
+ * nothing (`readServiceSelfInfo` reads only `__serviceInfo` — `_dev`, the other
2531
+ * marker it knew back then, was itself retired in #4319), so discovery reported
2532
+ * it as fully `available`. `handlerReady: false` because
2533
+ * no HTTP surface is mounted for `cache` at all — the same reason realtime
2534
+ * reports false.
2260
2535
  */
2261
2536
  declare function createMemoryCache(): {
2262
- _fallback: boolean;
2537
+ __serviceInfo: {
2538
+ status: "degraded";
2539
+ handlerReady: boolean;
2540
+ message: string;
2541
+ };
2263
2542
  _serviceName: string;
2264
2543
  get<T = unknown>(key: string): Promise<T | undefined>;
2265
2544
  set<T = unknown>(key: string, value: T, ttl?: number): Promise<void>;
@@ -2279,9 +2558,18 @@ declare function createMemoryCache(): {
2279
2558
  * Implements the IQueueService contract with synchronous in-process delivery.
2280
2559
  * Used by ObjectKernel as an automatic fallback when no real queue plugin
2281
2560
  * (e.g. BullMQ / RabbitMQ) is registered.
2561
+ *
2562
+ * [#4058] `degraded`, not `stub` (ADR-0076 D12): messages really reach real
2563
+ * subscribers — synchronously, in-process, with no durability or retry.
2564
+ * `getQueueSize()` answering 0 follows from that rather than faking it: nothing
2565
+ * is ever buffered. `handlerReady: false` — no HTTP surface exists for `queue`.
2282
2566
  */
2283
2567
  declare function createMemoryQueue(): {
2284
- _fallback: boolean;
2568
+ __serviceInfo: {
2569
+ status: "degraded";
2570
+ handlerReady: boolean;
2571
+ message: string;
2572
+ };
2285
2573
  _serviceName: string;
2286
2574
  publish<T = unknown>(queue: string, data: T): Promise<string>;
2287
2575
  subscribe(queue: string, handler: (msg: any) => Promise<void>): Promise<void>;
@@ -2296,9 +2584,19 @@ declare function createMemoryQueue(): {
2296
2584
  * Implements the IJobService contract with basic schedule/cancel/trigger
2297
2585
  * operations. Used by ObjectKernel as an automatic fallback when no real
2298
2586
  * job plugin (e.g. Agenda / BullMQ) is registered.
2587
+ *
2588
+ * [#4058] `degraded` (ADR-0076 D12), with the missing half named in the
2589
+ * message rather than left for a deployer to discover: `trigger()` really runs
2590
+ * the registered handler, but nothing here owns a timer, so a `schedule()`d job
2591
+ * NEVER fires on its own. That is reduced capability, not fabricated output —
2592
+ * no call returns a made-up answer. `handlerReady: false`: no HTTP surface.
2299
2593
  */
2300
2594
  declare function createMemoryJob(): {
2301
- _fallback: boolean;
2595
+ __serviceInfo: {
2596
+ status: "degraded";
2597
+ handlerReady: boolean;
2598
+ message: string;
2599
+ };
2302
2600
  _serviceName: string;
2303
2601
  schedule(name: string, schedule: any, handler: any): Promise<void>;
2304
2602
  cancel(name: string): Promise<void>;
@@ -2339,7 +2637,10 @@ declare function resolveLocale(requestedLocale: string, availableLocales: string
2339
2637
  * Does not load files from disk — operates purely in-memory.
2340
2638
  */
2341
2639
  declare function createMemoryI18n(): {
2342
- _fallback: boolean;
2640
+ __serviceInfo: {
2641
+ status: "degraded";
2642
+ message: string;
2643
+ };
2343
2644
  _serviceName: string;
2344
2645
  t(key: string, locale: string, params?: Record<string, unknown>): string;
2345
2646
  getTranslations(locale: string): Record<string, unknown>;
@@ -2364,7 +2665,10 @@ declare function createMemoryI18n(): {
2364
2665
  * (e.g. MetadataPlugin with file-system persistence) is registered.
2365
2666
  */
2366
2667
  declare function createMemoryMetadata(): {
2367
- _fallback: boolean;
2668
+ __serviceInfo: {
2669
+ status: "degraded";
2670
+ message: string;
2671
+ };
2368
2672
  _serviceName: string;
2369
2673
  register(type: string, name: string, data: any): Promise<void>;
2370
2674
  registerInMemory(type: string, name: string, data: any): void;
@@ -2687,4 +2991,4 @@ declare class NamespaceResolver {
2687
2991
  private suggestAlternative;
2688
2992
  }
2689
2993
 
2690
- export { ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, type AnonymousDenyInput, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type UserAuthzGrants, type VersionCompatibility, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, evaluateAuthGate, extractApiKey, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, parseScopes, parseSignature, postureVisibleRows, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveLocale, resolveLocalizationContext, resolveUserAuthzGrants, safeExit, shouldDenyAnonymous, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, zonedDateStartToUtcMs };
2994
+ export { ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, type AnonymousDenyInput, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type ExecutionContextLike, type FilterTokenResolutionContext, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, type OrderablePlugin, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, UnknownFilterTokenError, UnresolvedFilterTokenError, type UserAuthzGrants, type VersionCompatibility, assertInitServiceRequirements, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, describeInitOrderFault, evaluateAuthGate, extractApiKey, filterTokenContextFrom, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, parseScopes, parseSignature, postureVisibleRows, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveFilterToken, resolveFilterTokens, resolveLocale, resolveLocalizationContext, resolvePluginOrder, resolveUserAuthzGrants, safeExit, shouldDenyAnonymous, signPayload, validateInitServiceContract, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, zonedDateStartToUtcMs };