// © 2026 Adobe. MIT License. See /LICENSE for details. import { LitElement } from 'lit'; import { iterateSelfAndAncestors } from '../functions/index.js'; import { Database } from '@adobe/data/ecs'; import { UIService } from '@adobe/data/service'; import { attachDecorator, withHooks } from '../index.js'; export abstract class DatabaseElement

extends LitElement { /** Full database, hard-private — invisible to subclasses and external callers. */ #database!: Database.Plugin.ToDatabase

; /** * The element's database surface. * - SET to inject the full database (DI). * - GET returns the UI-restricted view (every mutator rewritten to * fire-and-forget `void`). * Divergent get/set types are intentional: inject full, consume restricted. */ set service(db: Database.Plugin.ToDatabase

) { const old = this.#database; this.#database = db; this.requestUpdate('service', old); } get service(): UIService.FromService> { return UIService.restrict(this.#database); } constructor() { super(); attachDecorator(this, 'render', withHooks); } abstract get plugin(): P; connectedCallback(): void { if (!this.#database) { const ancestor = this.findAncestorService(); this.service = ancestor?.extend(this.plugin) ?? Database.create(this.plugin); } super.connectedCallback(); } protected findAncestorService(): Database | void { for (const element of iterateSelfAndAncestors(this)) { // Read each ancestor's `service`. A DatabaseElement returns its full // database here (UIService.restrict is identity at runtime); a foreign // host (`

`) returns whatever was bound. Database.is // keeps only a real database, skipping unconnected elements (undefined) // and unrelated services (e.g. an ApplicationElement's MainService). const { service } = element as { service?: unknown }; if (Database.is(service)) return service; } } public override render() { throw new Error('render function must be overridden'); } }