@context-action/lit-ui 0.0.0-stage → 0.1.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 +45 -0
- package/LICENSE +201 -0
- package/README.md +110 -2
- package/dist/hydrate.cjs +139 -0
- package/dist/hydrate.cjs.map +1 -0
- package/dist/hydrate.d.cts +18 -0
- package/dist/hydrate.d.cts.map +1 -0
- package/dist/hydrate.d.ts +18 -0
- package/dist/hydrate.d.ts.map +1 -0
- package/dist/hydrate.js +127 -0
- package/dist/hydrate.js.map +1 -0
- package/dist/index.cjs +1636 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +644 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +644 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1571 -0
- package/dist/index.js.map +1 -0
- package/dist/ssr.cjs +116 -0
- package/dist/ssr.cjs.map +1 -0
- package/dist/ssr.d.cts +53 -0
- package/dist/ssr.d.cts.map +1 -0
- package/dist/ssr.d.ts +53 -0
- package/dist/ssr.d.ts.map +1 -0
- package/dist/ssr.js +111 -0
- package/dist/ssr.js.map +1 -0
- package/docs/01-dom-ownership.md +133 -0
- package/docs/02-reactive-controller-vs-hooks.md +246 -0
- package/docs/03-form-associated-elements.md +585 -0
- package/docs/04-micro-frontend-communication.md +682 -0
- package/docs/05-interface-and-state-management-standards.md +210 -0
- package/docs/06-ssr-hydration.md +134 -0
- package/docs/07-release-and-verification.md +150 -0
- package/package.json +105 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/form-associated-element.ts","../src/context.ts","../src/facade.ts","../src/react-bridge.ts","../src/define-once.ts","../src/components/quantity-stepper.ts","../src/components/cart-badge.ts"],"sourcesContent":["/**\n * @fileoverview Abstract Form-Associated Custom Element (FACE) base class extending LitElement.\n * Provides ElementInternals integration, standard constraint validation, form value management,\n * and robust form reset lifecycle hooks across browser and headless (JSDOM/SSR) environments.\n *\n * @implements view-layer\n * @implements web-component-primitive\n * @memberof core-concepts\n */\n\nimport { LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\n\n/**\n * Standard form validity flags as defined in the HTML Living Standard.\n */\nexport interface ValidityStateFlags {\n badInput?: boolean;\n customError?: boolean;\n patternMismatch?: boolean;\n rangeOverflow?: boolean;\n rangeUnderflow?: boolean;\n stepMismatch?: boolean;\n tooLong?: boolean;\n tooShort?: boolean;\n typeMismatch?: boolean;\n valueMissing?: boolean;\n}\n\n/**\n * Synthesizes an immutable ValidityState object from a set of ValidityStateFlags.\n * Used as a high-fidelity fallback when ElementInternals.validity is unavailable in the environment.\n */\nfunction createSyntheticValidityState(flags: ValidityStateFlags): ValidityState {\n const badInput = !!flags.badInput;\n const customError = !!flags.customError;\n const patternMismatch = !!flags.patternMismatch;\n const rangeOverflow = !!flags.rangeOverflow;\n const rangeUnderflow = !!flags.rangeUnderflow;\n const stepMismatch = !!flags.stepMismatch;\n const tooLong = !!flags.tooLong;\n const tooShort = !!flags.tooShort;\n const typeMismatch = !!flags.typeMismatch;\n const valueMissing = !!flags.valueMissing;\n\n const valid = !(\n badInput ||\n customError ||\n patternMismatch ||\n rangeOverflow ||\n rangeUnderflow ||\n stepMismatch ||\n tooLong ||\n tooShort ||\n typeMismatch ||\n valueMissing\n );\n\n return {\n badInput,\n customError,\n patternMismatch,\n rangeOverflow,\n rangeUnderflow,\n stepMismatch,\n tooLong,\n tooShort,\n typeMismatch,\n valid,\n valueMissing,\n };\n}\n\n/**\n * Abstract Form-Associated Custom Element (FACE) base class extending LitElement.\n *\n * Provides:\n * - Native form participation via `ElementInternals` and `static formAssociated = true`\n * - Defensive fallbacks for server-side rendering (SSR) and headless test environments (JSDOM)\n * - Complete Form value management (`setFormValue`, `getFormValue`, `formValue`)\n * - Constraint validation APIs (`setValidity`, `validity`, `validationMessage`, `checkValidity`, `reportValidity`, `setCustomValidity`)\n * - Lifecycle callbacks (`formResetCallback`, `formDisabledCallback`, `formStateRestoreCallback`, `formAssociatedCallback`)\n * - Standard overridable hooks (`onFormReset`, `onFormDisabled`, `onFormStateRestore`, `onFormAssociated`)\n * - Automatic form reset event synchronization and leak-free unbinding on disconnect\n */\nexport class FormAssociatedLitElement extends LitElement {\n /**\n * Identifies this custom element as form-associated according to the HTML specification.\n * Enables the browser engine to attach ElementInternals and include this element in form submission.\n */\n static readonly formAssociated = true;\n\n /**\n * The ElementInternals controller instance for this custom element.\n * Undefined in environments where attachInternals is unsupported.\n */\n public readonly internals?: ElementInternals;\n\n // Reflected standard form control properties\n @property({ type: String, reflect: true })\n name = '';\n\n @property({ type: String, reflect: true })\n type = 'custom-field';\n\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n @property({ type: Boolean, reflect: true })\n required = false;\n\n // Internal fallback state storage\n #formValue: File | string | FormData | null = null;\n #formState: unknown = null;\n #validityFlags: ValidityStateFlags = {};\n #validationMessage = '';\n #customError = false;\n #isResetting = false;\n #associatedForm: HTMLFormElement | null = null;\n\n constructor() {\n super();\n\n if (typeof this.attachInternals === 'function') {\n try {\n this.internals = this.attachInternals();\n } catch {\n // Defensive fallback: ElementInternals already attached, or environment throws NotSupportedError\n }\n }\n }\n\n // ==========================================\n // Form Association & Enclosing Form Queries\n // ==========================================\n\n /**\n * The associated HTMLFormElement, if any.\n * Resolves first via ElementInternals.form, then checks the 'form' attribute by ID,\n * and finally falls back to the closest ancestor <form>.\n */\n get form(): HTMLFormElement | null {\n if (this.internals && 'form' in this.internals && this.internals.form !== undefined) {\n return this.internals.form;\n }\n\n const formAttr = this.getAttribute('form');\n if (formAttr && this.ownerDocument) {\n const el = this.ownerDocument.getElementById(formAttr);\n if (el instanceof HTMLFormElement) return el;\n }\n\n return this.closest('form');\n }\n\n // ==========================================\n // Form Value APIs\n // ==========================================\n\n /**\n * Sets the element's form submission value and optional state restoration value.\n *\n * @param value The value to submit with the form: string, File, FormData, or null (clears submission).\n * @param state Optional state object used for session restoration / autocomplete.\n */\n public setFormValue(\n value: File | string | FormData | null,\n state?: File | string | FormData | null,\n ): void {\n this.#formValue = value;\n this.#formState = state;\n\n if (this.internals && typeof this.internals.setFormValue === 'function') {\n try {\n this.internals.setFormValue(value, state);\n } catch {\n // Defensive fallback if environment rejects parameters\n }\n }\n }\n\n /**\n * Returns the current form submission value.\n */\n public getFormValue(): File | string | FormData | null {\n return this.#formValue;\n }\n\n /**\n * Getter alias for getFormValue() for uniform property access.\n */\n public get formValue(): File | string | FormData | null {\n return this.#formValue;\n }\n\n /**\n * Returns the current state restoration object.\n */\n public get formState(): unknown {\n return this.#formState;\n }\n\n // ==========================================\n // Constraint Validation APIs\n // ==========================================\n\n /**\n * Sets the validity flags, validation error message, and optional anchor element.\n *\n * @param flags A map of ValidityStateFlags indicating constraint violations.\n * @param message A human-readable validation error message (required if any flag is true).\n * @param anchor An optional HTMLElement inside the element's ShadowRoot used to anchor browser tooltips.\n */\n public setValidity(\n flags: ValidityStateFlags = {},\n message?: string,\n anchor?: HTMLElement,\n ): void {\n this.#validityFlags = { ...flags };\n this.#validationMessage = message ?? '';\n this.#customError = !!flags.customError;\n\n if (this.internals && typeof this.internals.setValidity === 'function') {\n try {\n this.internals.setValidity(flags, message, anchor);\n } catch {\n // Defensive fallback\n }\n }\n }\n\n /**\n * Returns the current ValidityState object representing constraint validation status.\n */\n get validity(): ValidityState {\n if (this.internals && 'validity' in this.internals && this.internals.validity) {\n return this.internals.validity;\n }\n return createSyntheticValidityState({\n ...this.#validityFlags,\n customError: this.#validityFlags.customError || this.#customError,\n });\n }\n\n /**\n * Returns the current validation error message, or an empty string if valid.\n */\n get validationMessage(): string {\n if (\n this.internals &&\n 'validationMessage' in this.internals &&\n typeof this.internals.validationMessage === 'string'\n ) {\n return this.internals.validationMessage;\n }\n return this.validity.valid ? '' : this.#validationMessage;\n }\n\n /**\n * Returns true if the element participates in constraint validation and is not disabled.\n */\n get willValidate(): boolean {\n if (\n this.internals &&\n 'willValidate' in this.internals &&\n typeof this.internals.willValidate === 'boolean'\n ) {\n return this.internals.willValidate;\n }\n return !this.disabled;\n }\n\n /**\n * Checks whether the element satisfies all validation constraints.\n * If invalid, fires a cancelable 'invalid' event on the element and returns false.\n * If valid (or validation is disabled), returns true.\n */\n public checkValidity(): boolean {\n if (this.internals && typeof this.internals.checkValidity === 'function') {\n try {\n return this.internals.checkValidity();\n } catch {\n // Fall back to synthetic verification\n }\n }\n\n if (!this.willValidate) return true;\n\n if (!this.validity.valid) {\n const event = new Event('invalid', { bubbles: false, cancelable: true });\n this.dispatchEvent(event);\n return false;\n }\n\n return true;\n }\n\n /**\n * Checks validity, reports errors to the user (e.g. browser popup), and returns validity status.\n */\n public reportValidity(): boolean {\n if (this.internals && typeof this.internals.reportValidity === 'function') {\n try {\n return this.internals.reportValidity();\n } catch {\n // Fall back to checkValidity\n }\n }\n return this.checkValidity();\n }\n\n /**\n * Sets a custom validation error message. If message is non-empty, sets customError to true.\n * If message is empty, clears custom validity errors.\n */\n public setCustomValidity(message: string): void {\n if (message) {\n this.setValidity({ customError: true }, message);\n } else {\n this.setValidity({});\n }\n }\n\n // ==========================================\n // Form-Associated Lifecycle Callbacks & Hooks\n // ==========================================\n\n /**\n * Native FACE callback invoked by the browser engine when the associated form is reset.\n * Dispatches to the overridable onFormReset() hook.\n */\n public formResetCallback(): void {\n if (this.#isResetting) return;\n this.#isResetting = true;\n try {\n this.onFormReset();\n } finally {\n queueMicrotask(() => {\n this.#isResetting = false;\n });\n }\n }\n\n /**\n * Overridable lifecycle hook called when the associated form is reset.\n * Subclasses should restore default values, re-evaluate validation, and request updates.\n */\n protected onFormReset(): void {}\n\n /**\n * Native FACE callback invoked when the element or parent fieldset is disabled/enabled.\n */\n public formDisabledCallback(disabled: boolean): void {\n this.disabled = disabled;\n this.onFormDisabled(disabled);\n this.requestUpdate();\n }\n\n /**\n * Overridable lifecycle hook called when the disabled state changes.\n */\n protected onFormDisabled(_disabled: boolean): void {}\n\n /**\n * Native FACE callback invoked when the browser restores form state (history navigation / autofill).\n */\n public formStateRestoreCallback(state: unknown, mode: 'restore' | 'autocomplete'): void {\n this.#formState = state;\n this.onFormStateRestore(state, mode);\n }\n\n /**\n * Overridable lifecycle hook called when the browser restores form state.\n */\n protected onFormStateRestore(_state: unknown, _mode: 'restore' | 'autocomplete'): void {}\n\n /**\n * Native FACE callback invoked when the element is associated or disassociated with a form.\n */\n public formAssociatedCallback(form: HTMLFormElement | null): void {\n this.#syncFormListener(form);\n this.onFormAssociated(form);\n }\n\n /**\n * Overridable lifecycle hook called when the element's form association changes.\n */\n protected onFormAssociated(_form: HTMLFormElement | null): void {}\n\n // ==========================================\n // Reset Event Coordination & Memory Safety\n // ==========================================\n\n #handleFormReset = (event: Event): void => {\n if (this.#isResetting || event.defaultPrevented) return;\n queueMicrotask(() => {\n if (this.#isResetting || event.defaultPrevented) return;\n this.formResetCallback();\n });\n };\n\n #syncFormListener(newForm: HTMLFormElement | null): void {\n if (this.#associatedForm === newForm) return;\n\n if (this.#associatedForm) {\n this.#associatedForm.removeEventListener('reset', this.#handleFormReset);\n }\n\n this.#associatedForm = newForm;\n\n if (this.#associatedForm) {\n this.#associatedForm.addEventListener('reset', this.#handleFormReset);\n }\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.#syncFormListener(this.form);\n }\n\n override disconnectedCallback(): void {\n this.#syncFormListener(null);\n super.disconnectedCallback();\n }\n}\n","/**\n * @fileoverview W3C Context Protocol (@lit/context) tokens and dependency injection utilities\n * for Context-Action stores, action registers, and dispatchers across Shadow DOM boundaries.\n *\n * @implements context-protocol\n * @implements dependency-injection\n * @implements store-integration-pattern\n * @implements action-pipeline-system\n * @memberof core-concepts\n */\n\nimport {\n createContext,\n ContextProvider,\n ContextConsumer,\n type Context,\n} from '@lit/context';\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport type {\n ActionRegister,\n ActionDispatcher,\n ActionPayloadMap,\n ActionNames,\n DispatchArgs,\n} from '@context-action/core';\nimport type {\n ReadableStore,\n Selector,\n EqualityFn,\n Unsubscribe,\n} from '@context-action/lit';\n\n// Re-export core context primitives from @lit/context for convenience\nexport { ContextProvider, ContextConsumer, createContext, type Context };\n\n// ============================================================================\n// 1. Context Tokens\n// ============================================================================\n\n/**\n * Standard W3C Context token for an ActionRegister.\n * Uses Symbol.for to ensure singleton identity across independent bundle chunks and micro-frontends.\n *\n * @implements action-pipeline-system\n * @memberof api-terms\n */\nexport const actionRegisterContext: Context<symbol, ActionRegister<any>> =\n createContext<ActionRegister<any>, symbol>(Symbol.for('context-action.register'));\n\n/**\n * Standard W3C Context token for an ActionDispatcher function.\n * Uses Symbol.for to ensure singleton identity across independent bundle chunks.\n *\n * @implements action-pipeline-system\n * @memberof api-terms\n */\nexport const actionDispatcherContext: Context<symbol, ActionDispatcher<any>> =\n createContext<ActionDispatcher<any>, symbol>(Symbol.for('context-action.dispatcher'));\n\n/**\n * Factory function creating a strongly-typed W3C Context token for a named Context-Action store.\n *\n * Distinct store names yield isolated contexts. Identical store names across different\n * modules or micro-frontend bundles share the same underlying global Symbol.\n *\n * @template T State type managed by the store.\n * @param name Unique name of the store domain (e.g. 'cart', 'auth', 'theme').\n * @returns A Context token for the named ReadableStore.\n *\n * @example\n * ```typescript\n * export const cartStoreContext = createStoreContext<CartState>('cart');\n * ```\n *\n * @implements store-integration-pattern\n * @memberof api-terms\n */\nexport function createStoreContext<T = unknown>(\n name: string\n): Context<symbol, ReadableStore<T>> {\n if (!name || typeof name !== 'string') {\n throw new TypeError('createStoreContext: \"name\" must be a non-empty string.');\n }\n return createContext<ReadableStore<T>, symbol>(\n Symbol.for(`context-action.store.${name}`)\n );\n}\n\n/**\n * Factory function creating a strongly-typed ActionRegister context token for a specific action map.\n *\n * @template A Action payload map.\n * @param name Optional domain qualifier. Defaults to the root action register.\n * @returns A typed Context token for ActionRegister<A>.\n */\nexport function createActionRegisterContext<A extends ActionPayloadMap = ActionPayloadMap>(\n name = 'default'\n): Context<symbol, ActionRegister<A>> {\n return createContext<ActionRegister<A>, symbol>(\n Symbol.for(`context-action.register.${name}`)\n );\n}\n\n/**\n * Factory function creating a strongly-typed ActionDispatcher context token for a specific action map.\n *\n * @template A Action payload map.\n * @param name Optional domain qualifier. Defaults to the root action dispatcher.\n * @returns A typed Context token for ActionDispatcher<A>.\n */\nexport function createActionDispatcherContext<A extends ActionPayloadMap = ActionPayloadMap>(\n name = 'default'\n): Context<symbol, ActionDispatcher<A>> {\n return createContext<ActionDispatcher<A>, symbol>(\n Symbol.for(`context-action.dispatcher.${name}`)\n );\n}\n\n// ============================================================================\n// 2. Imperative Provider Helpers\n// ============================================================================\n\n/**\n * Imperatively attaches an ActionRegister ContextProvider to a host element.\n *\n * @template A Action payload map.\n * @param host The hosting LitElement or ReactiveElement.\n * @param register The ActionRegister instance to provide.\n * @param context Optional custom context token (defaults to actionRegisterContext).\n * @returns The initialized ContextProvider instance.\n */\nexport function provideActionRegister<A extends ActionPayloadMap = ActionPayloadMap>(\n host: HTMLElement & ReactiveControllerHost,\n register: ActionRegister<A>,\n context: Context<unknown, ActionRegister<A>> = actionRegisterContext as Context<unknown, ActionRegister<A>>\n): ContextProvider<Context<unknown, ActionRegister<A>>> {\n return new ContextProvider(host, {\n context,\n initialValue: register,\n });\n}\n\n/**\n * Imperatively attaches an ActionDispatcher ContextProvider to a host element.\n *\n * @template A Action payload map.\n * @param host The hosting LitElement or ReactiveElement.\n * @param dispatcher The ActionDispatcher function to provide.\n * @param context Optional custom context token (defaults to actionDispatcherContext).\n * @returns The initialized ContextProvider instance.\n */\nexport function provideActionDispatcher<A extends ActionPayloadMap = ActionPayloadMap>(\n host: HTMLElement & ReactiveControllerHost,\n dispatcher: ActionDispatcher<A>,\n context: Context<unknown, ActionDispatcher<A>> = actionDispatcherContext as Context<unknown, ActionDispatcher<A>>\n): ContextProvider<Context<unknown, ActionDispatcher<A>>> {\n return new ContextProvider(host, {\n context,\n initialValue: dispatcher,\n });\n}\n\n/**\n * Imperatively attaches a ReadableStore ContextProvider to a host element.\n *\n * @template T Store state type.\n * @param host The hosting LitElement or ReactiveElement.\n * @param context The store Context token created via `createStoreContext`.\n * @param store The ReadableStore instance to provide.\n * @returns The initialized ContextProvider instance.\n */\nexport function provideStore<T>(\n host: HTMLElement & ReactiveControllerHost,\n context: Context<unknown, ReadableStore<T>>,\n store: ReadableStore<T>\n): ContextProvider<Context<unknown, ReadableStore<T>>> {\n return new ContextProvider(host, {\n context,\n initialValue: store,\n });\n}\n\n// ============================================================================\n// 3. Reactive Controllers for Context Dependency Injection\n// ============================================================================\n\n/**\n * Options for `ContextStoreController`.\n *\n * @template T Source store state type.\n * @template Selected Projected output type.\n */\nexport interface ContextStoreControllerOptions<T, Selected = T> {\n /** The W3C Context token created via `createStoreContext`. */\n context: Context<unknown, ReadableStore<T>>;\n /** Optional projection selector to derive a slice of state. */\n selector?: Selector<T, Selected>;\n /** Optional equality comparator to prevent redundant rerenders. Defaults to Object.is. */\n equalityFn?: EqualityFn<Selected>;\n}\n\n/**\n * ReactiveController that consumes a Context-Action ReadableStore from the W3C Context\n * hierarchy across Shadow DOM boundaries, subscribes to state updates, and triggers host re-renders.\n *\n * Key guarantees:\n * 1. Attaches to host via `host.addController(this)`.\n * 2. Uses `@lit/context` ContextConsumer to dynamically resolve store instances across Shadow DOM boundaries.\n * 3. Supports selective projections with memoization (`equalityFn`).\n * 4. Ensures ZERO memory leaks: completely terminates store subscription when `hostDisconnected()` runs.\n * 5. Re-evaluates store state and resubscribes on `hostConnected()`.\n *\n * @template T Source store state type.\n * @template Selected Derived slice type.\n *\n * @implements store-integration-pattern\n * @implements reactive-state-management\n * @memberof core-concepts\n */\nexport class ContextStoreController<T = unknown, Selected = T> implements ReactiveController {\n readonly #host: ReactiveControllerHost & HTMLElement;\n readonly #selector: Selector<T, Selected>;\n readonly #equalityFn: EqualityFn<Selected>;\n\n #currentStore: ReadableStore<T> | undefined = undefined;\n #unsubscribeStore: Unsubscribe | null = null;\n #selectedValue: Selected | undefined = undefined;\n #isResolved = false;\n\n constructor(\n host: ReactiveControllerHost & HTMLElement,\n options: ContextStoreControllerOptions<T, Selected>\n ) {\n if (!host || typeof host.addController !== 'function') {\n throw new TypeError(\n 'ContextStoreController requires a valid host implementing ReactiveControllerHost and HTMLElement.'\n );\n }\n if (!options?.context) {\n throw new TypeError('ContextStoreController requires a valid Context token.');\n }\n\n this.#host = host;\n this.#selector = options.selector ?? ((s: T) => s as unknown as Selected);\n this.#equalityFn = options.equalityFn ?? Object.is;\n\n // Attach ContextConsumer to host\n new ContextConsumer(host, {\n context: options.context,\n callback: (store) => this.#handleStoreResolved(store),\n subscribe: true,\n });\n\n host.addController(this);\n }\n\n /** Current projected state derived from the injected store, or undefined if not yet provided. */\n get value(): Selected | undefined {\n return this.#selectedValue;\n }\n\n /** The resolved ReadableStore instance currently bound to this controller. */\n get store(): ReadableStore<T> | undefined {\n return this.#currentStore;\n }\n\n /** Indicates whether the context request has been satisfied by an ancestor provider. */\n get isResolved(): boolean {\n return this.#isResolved;\n }\n\n #extractRawValue(store: ReadableStore<T>): T {\n if (typeof store.getValue === 'function') {\n return store.getValue();\n }\n if (typeof store.getSnapshot === 'function') {\n return store.getSnapshot() as T;\n }\n throw new TypeError(\n 'ContextStoreController: Provided store must implement getValue() or getSnapshot().'\n );\n }\n\n #handleStoreResolved(nextStore?: ReadableStore<T>): void {\n if (this.#currentStore === nextStore && this.#isResolved) {\n return;\n }\n\n // Teardown previous subscription\n this.#cleanupStoreSubscription();\n\n this.#currentStore = nextStore;\n\n if (!nextStore) {\n this.#isResolved = false;\n this.#selectedValue = undefined;\n this.#host.requestUpdate();\n return;\n }\n\n this.#isResolved = true;\n this.#syncStateAndSubscribe();\n this.#host.requestUpdate();\n }\n\n #syncStateAndSubscribe(): void {\n if (!this.#currentStore) return;\n\n // Compute initial value\n const raw = this.#extractRawValue(this.#currentStore);\n this.#selectedValue = this.#selector(raw);\n\n // Subscribe to store notifications\n this.#unsubscribeStore = this.#currentStore.subscribe(() => {\n if (!this.#currentStore) return;\n const nextRaw = this.#extractRawValue(this.#currentStore);\n const nextSelected = this.#selector(nextRaw);\n\n if (!this.#equalityFn(this.#selectedValue as Selected, nextSelected)) {\n this.#selectedValue = nextSelected;\n this.#host.requestUpdate();\n }\n });\n }\n\n #cleanupStoreSubscription(): void {\n if (this.#unsubscribeStore) {\n this.#unsubscribeStore();\n this.#unsubscribeStore = null;\n }\n }\n\n hostConnected(): void {\n // ContextConsumer automatically dispatches context-request when host connects.\n // If a store was already resolved, ensure we resubscribe and sync fresh data:\n if (this.#currentStore && !this.#unsubscribeStore) {\n this.#syncStateAndSubscribe();\n this.#host.requestUpdate();\n }\n }\n\n hostDisconnected(): void {\n // Terminate store subscription immediately: zero memory leaks when element unmounts\n this.#cleanupStoreSubscription();\n }\n}\n\n/**\n * Options for `ContextActionController`.\n *\n * @template A Action payload map.\n */\nexport interface ContextActionControllerOptions<A extends ActionPayloadMap = ActionPayloadMap> {\n /** Optional custom context token (defaults to actionRegisterContext). */\n context?: Context<unknown, ActionRegister<A>>;\n}\n\n/**\n * ReactiveController that consumes an ActionRegister from the W3C Context hierarchy\n * across Shadow DOM boundaries and exposes typed dispatch methods.\n *\n * @template A Action payload map.\n *\n * @implements action-pipeline-system\n * @implements action-controller\n * @memberof core-concepts\n */\nexport class ContextActionController<A extends ActionPayloadMap = ActionPayloadMap>\n implements ReactiveController\n{\n readonly #host: ReactiveControllerHost & HTMLElement;\n\n #register: ActionRegister<A> | undefined = undefined;\n #isPending = false;\n #lastError: Error | undefined = undefined;\n\n constructor(\n host: ReactiveControllerHost & HTMLElement,\n options?: ContextActionControllerOptions<A>\n ) {\n if (!host || typeof host.addController !== 'function') {\n throw new TypeError(\n 'ContextActionController requires a valid host implementing ReactiveControllerHost and HTMLElement.'\n );\n }\n\n this.#host = host;\n const ctx = (options?.context ?? actionRegisterContext) as Context<unknown, ActionRegister<A>>;\n\n new ContextConsumer(host, {\n context: ctx,\n callback: (reg) => {\n this.#register = reg;\n this.#host.requestUpdate();\n },\n subscribe: true,\n });\n\n host.addController(this);\n }\n\n /** The resolved ActionRegister instance, or undefined if no provider is present. */\n get register(): ActionRegister<A> | undefined {\n return this.#register;\n }\n\n /** Whether an action dispatch is currently in progress. */\n get isPending(): boolean {\n return this.#isPending;\n }\n\n /** The last error thrown during an action dispatch, if any. */\n get lastError(): Error | undefined {\n return this.#lastError;\n }\n\n /**\n * Dispatches an action through the context-injected ActionRegister pipeline.\n *\n * @template K Action name.\n * @param action Name of the registered action.\n * @param args Action payload arguments.\n */\n async dispatch<K extends ActionNames<A>>(\n action: K,\n ...args: DispatchArgs<A[K]>\n ): Promise<void> {\n if (!this.#register) {\n throw new Error(\n `ContextActionController: Cannot dispatch \"${String(action)}\". No ActionRegister found in context hierarchy.`\n );\n }\n\n this.#isPending = true;\n this.#lastError = undefined;\n this.#host.requestUpdate();\n\n try {\n await this.#register.dispatch(action, ...args);\n } catch (err) {\n const errorObj = err instanceof Error ? err : new Error(String(err));\n this.#lastError = errorObj;\n throw errorObj;\n } finally {\n this.#isPending = false;\n this.#host.requestUpdate();\n }\n }\n\n /**\n * Dispatches an action and returns the pipeline execution result.\n *\n * @template K Action name.\n * @template R Expected return value.\n * @param action Name of the registered action.\n * @param args Action payload arguments.\n * @returns Pipeline result promise.\n */\n async dispatchWithResult<K extends ActionNames<A>, R = unknown>(\n action: K,\n ...args: DispatchArgs<A[K]>\n ): Promise<R> {\n if (!this.#register) {\n throw new Error(\n `ContextActionController: Cannot dispatch \"${String(action)}\". No ActionRegister found in context hierarchy.`\n );\n }\n\n this.#isPending = true;\n this.#lastError = undefined;\n this.#host.requestUpdate();\n\n try {\n if (typeof (this.#register as any).dispatchWithResult === 'function') {\n return await (this.#register as any).dispatchWithResult(action, ...args);\n }\n return (await this.#register.dispatch(action, ...args)) as unknown as R;\n } catch (err) {\n const errorObj = err instanceof Error ? err : new Error(String(err));\n this.#lastError = errorObj;\n throw errorObj;\n } finally {\n this.#isPending = false;\n this.#host.requestUpdate();\n }\n }\n\n hostConnected(): void {}\n hostDisconnected(): void {}\n}\n","/**\n * @module facade\n *\n * Purpose:\n * Provides zero-dependency OOP Facade utilities (`PublicActionMethods`,\n * `ImperativeHandleFactory`, `createElementFacade`) that project a Custom\n * Element's public contract into an encapsulated handle without exposing raw\n * `HTMLElement` or `ShadowRoot` internals.\n *\n * Invariants:\n * - Pure TypeScript/ES2022 runtime with zero Lit or React dependencies,\n * usable in Vanilla DOM, Lit, or React bridges alike.\n * - `createElementFacade` returns a frozen object (`instanceof HTMLElement === false`),\n * binds methods lazily to the live element, and caches bound function\n * identities in a `WeakMap` so detached elements are never retained.\n * - Rejects writes to read-only properties with a `TypeError` and throws a\n * descriptive error when accessed while unmounted.\n */\n\n/**\n * Maps selected action keys from an ActionPayloadMap-compatible record into\n * object-oriented method signatures while hiding internal action keys.\n */\nexport type PublicActionMethods<\n // biome-ignore lint/suspicious/noExplicitAny: action payload map values can be any payload shape.\n Actions extends Record<string, any>,\n PublicKeys extends keyof Actions = keyof Actions,\n ReturnMap extends Partial<Record<PublicKeys, unknown>> = Record<never, never>,\n> = {\n // biome-ignore lint/suspicious/noConfusingVoidType: action payload maps use void for payload-less actions.\n readonly [K in PublicKeys]: [Actions[K]] extends [void | undefined]\n ? () => K extends keyof ReturnMap ? Promise<ReturnMap[K]> : Promise<void>\n : (payload: Actions[K]) => K extends keyof ReturnMap ? Promise<ReturnMap[K]> : Promise<void>;\n};\n\n/**\n * Host-agnostic factory function that creates an encapsulated OOP handle\n * from a lazy custom element getter, hiding raw `HTMLElement` internals.\n */\nexport type ImperativeHandleFactory<\n Element extends HTMLElement,\n Handle,\n> = (getElement: () => Element | null) => Handle;\n\nfunction findPropertyDescriptor(\n target: object,\n key: PropertyKey,\n): PropertyDescriptor | undefined {\n let current: object | null = target;\n while (current) {\n const descriptor = Object.getOwnPropertyDescriptor(current, key);\n if (descriptor) return descriptor;\n current = Object.getPrototypeOf(current);\n }\n return undefined;\n}\n\n/**\n * Creates a reusable host-agnostic handle factory that exposes only the specified\n * properties and methods from a Custom Element, hiding raw `HTMLElement` APIs.\n * Can be used directly with Vanilla DOM, Lit, or\n * `createLitElementBridge({ createHandle })`.\n *\n * Methods are bound to the current element and cached per element, so repeated\n * reads return the same function identity until the element or the underlying\n * method changes. The cache is keyed weakly and never retains a detached element.\n *\n * @example\n * ```ts\n * const TimelineElement = createLitElementBridge<TimelineProps, TimelineElement, TimelineHandle>({\n * tagName: 'st-timeline',\n * properties: ['input', 'set'],\n * createHandle: createElementFacade(['focusItem', 'scrollTo', 'commitSelection']),\n * });\n * ```\n */\nexport function createElementFacade<\n Element extends HTMLElement,\n const Keys extends readonly (keyof Element)[],\n>(\n keys: Keys,\n): ImperativeHandleFactory<Element, Pick<Element, Keys[number]>> {\n const uniqueKeys = Array.from(new Set(keys));\n return (getElement) => {\n const requireElement = (): Element => {\n const element = getElement();\n if (!element) {\n throw new Error('Custom element handle is not mounted');\n }\n return element;\n };\n const boundMethods = new WeakMap<\n Element,\n Map<PropertyKey, { source: unknown; bound: unknown }>\n >();\n const facade = {} as Pick<Element, Keys[number]>;\n for (const key of uniqueKeys) {\n Object.defineProperty(facade, key, {\n configurable: false,\n enumerable: true,\n get() {\n const element = requireElement();\n const value: unknown = Reflect.get(element, key, element);\n if (typeof value !== 'function') return value;\n let cache = boundMethods.get(element);\n if (!cache) {\n cache = new Map();\n boundMethods.set(element, cache);\n }\n const cached = cache.get(key);\n if (cached && cached.source === value) return cached.bound;\n const bound = value.bind(element);\n cache.set(key, { source: value, bound });\n return bound;\n },\n set(nextValue: unknown) {\n const element = requireElement();\n const descriptor = findPropertyDescriptor(element, key);\n if (descriptor && !descriptor.set && !descriptor.writable) {\n throw new TypeError(`Cannot assign to read-only property '${String(key)}'`);\n }\n const updated = Reflect.set(element, key, nextValue, element);\n if (!updated) {\n throw new TypeError(`Cannot assign to read-only property '${String(key)}'`);\n }\n },\n });\n }\n return Object.freeze(facade);\n };\n}\n","/**\n * @fileoverview React 18/19 bridge helper for consuming Lit Custom Elements.\n * Provides direct property assignment to bypass attribute stringification,\n * stable custom event listener mapping with deterministic cleanup, and\n * OOP Facade handle forwarding via `createHandle`.\n *\n * @implements view-layer\n * @implements web-component-primitive\n * @memberof core-concepts\n */\n\nimport * as React from 'react';\nimport type { ImperativeHandleFactory } from './facade.js';\n\nexport { createElementFacade } from './facade.js';\nexport type { ImperativeHandleFactory, PublicActionMethods } from './facade.js';\n\nconst useIsomorphicLayoutEffect =\n typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;\n\ninterface EventBinding {\n element: Element;\n eventName: string;\n handler: unknown;\n listener?: EventListener;\n}\n\ntype EventHandlerValue =\n | ((event: Event) => void)\n | { handleEvent(event: Event): void };\n\nfunction isEventHandler(value: unknown): value is EventHandlerValue {\n return (\n typeof value === 'function' ||\n (typeof value === 'object' &&\n value !== null &&\n typeof (value as { handleEvent?: unknown }).handleEvent === 'function')\n );\n}\n\n/**\n * Configuration options for the Lit React Bridge.\n * Controls direct property assignment, custom event mapping, and facade handles.\n */\nexport interface LitBridgeConfig<\n Props extends Record<string, any>,\n // `any` defaults keep a config annotated with only `LitBridgeConfig<Props>`\n // assignable to a bridge created with a concrete Element/Handle.\n // biome-ignore lint/suspicious/noExplicitAny: see comment above.\n Element extends HTMLElement = any,\n // biome-ignore lint/suspicious/noExplicitAny: see comment above.\n Handle = any,\n> {\n /**\n * Property keys that should be assigned directly as DOM element properties\n * rather than rendered as HTML attributes.\n *\n * This is critical for React 18 compatibility, which stringifies complex objects,\n * arrays, functions, and class instances (such as Context-Action stores) to `\"[object Object]\"`.\n *\n * On a later render, a key previously assigned by the bridge and then omitted\n * from props is reset to `undefined` so stale objects never remain on the\n * element. Untouched element defaults are preserved, and read-only properties\n * are left to the element adapter's interpretation.\n */\n properties?: readonly (keyof Props)[];\n\n /**\n * Map of React prop handler names to native DOM CustomEvent names.\n *\n * Listeners are reconciled by handler identity: a re-render with the same\n * handler performs no DOM work, while a changed handler (or `undefined`)\n * removes and re-attaches only that event.\n *\n * @example\n * ```ts\n * events: {\n * onQuantityChange: 'quantity-change',\n * onCartBadgeClick: 'cart-badge-click',\n * }\n * ```\n */\n events?: Record<string, string>;\n\n /**\n * Optional factory for `useImperativeHandle` that encapsulates the underlying\n * `Element` and exposes only an object-oriented Facade handle to parent refs.\n * When omitted, the raw `Element` instance is forwarded for backward compatibility.\n *\n * Use `createElementFacade(['focusItem', 'scrollTo'])` for a zero-dependency\n * allow-list facade.\n */\n createHandle?: ImperativeHandleFactory<Element, Handle>;\n}\n\n/**\n * Options object signature for createLitElementBridge, including the custom element tagName.\n */\nexport interface LitBridgeOptions<\n Props extends Record<string, any>,\n Element extends HTMLElement = HTMLElement,\n Handle = Element,\n> extends LitBridgeConfig<Props, Element, Handle> {\n /**\n * The registered custom element HTML tag name, e.g. 'lit-quantity-stepper'.\n */\n tagName: string;\n}\n\n/**\n * Strongly typed React ForwardRefExoticComponent representing the bridged Custom Element.\n */\nexport type LitElementComponent<\n Props extends Record<string, any>,\n Element extends HTMLElement = HTMLElement,\n Handle = Element,\n> = React.ForwardRefExoticComponent<\n React.PropsWithoutRef<Props & Omit<React.HTMLAttributes<Element>, keyof Props>> &\n React.RefAttributes<Handle>\n>;\n\n/**\n * Overload 1: Two-argument form `(tagName, config?)`.\n *\n * @example\n * ```tsx\n * const ReactStepper = createLitElementBridge<StepperProps, LitQuantityStepper>(\n * 'lit-quantity-stepper',\n * {\n * properties: ['metadata', 'cartStore'],\n * events: { onQuantityChange: 'quantity-change' },\n * }\n * );\n * ```\n */\nexport function createLitElementBridge<\n Props extends Record<string, any> = Record<string, any>,\n Element extends HTMLElement = HTMLElement,\n Handle = Element,\n>(\n tagName: string,\n config?: LitBridgeConfig<Props, Element, Handle>,\n): LitElementComponent<Props, Element, Handle>;\n\n/**\n * Overload 2: Single-argument options form `(options)`.\n *\n * @example\n * ```tsx\n * const ReactTimeline = createLitElementBridge<TimelineProps, TimelineElement, TimelineHandle>({\n * tagName: 'st-timeline',\n * properties: ['input', 'set'],\n * events: { onSelectionChange: 'selection-change' },\n * createHandle: createElementFacade(['focusItem', 'scrollTo', 'commitSelection']),\n * });\n * ```\n */\nexport function createLitElementBridge<\n Props extends Record<string, any> = Record<string, any>,\n Element extends HTMLElement = HTMLElement,\n Handle = Element,\n>(\n options: LitBridgeOptions<Props, Element, Handle>,\n): LitElementComponent<Props, Element, Handle>;\n\n/**\n * Creates a type-safe React bridge component for consuming any Lit Custom Element\n * with seamless property synchronization, custom event listeners, and ref forwarding.\n *\n * Solves standard React ↔ Web Component pain points:\n * 1. Direct Property Assignment: Directly assigns objects, arrays, and stores to the DOM\n * instance without stringifying to `\"[object Object]\"` in React 18/19.\n * 2. Property Removal: Clears keys removed from `properties` on later renders.\n * 3. Stable Custom Events: Attaches one listener per event and reads the latest\n * handler, so ordinary re-renders never re-register listeners.\n * 4. Ref Forwarding: Forwards an OOP Facade handle (`createHandle`) or the raw\n * Custom Element instance when no factory is provided.\n */\nexport function createLitElementBridge<\n Props extends Record<string, any> = Record<string, any>,\n Element extends HTMLElement = HTMLElement,\n Handle = Element,\n>(\n tagOrOptions: string | LitBridgeOptions<Props, Element, Handle>,\n maybeConfig?: LitBridgeConfig<Props, Element, Handle>,\n): LitElementComponent<Props, Element, Handle> {\n const {\n tagName,\n properties = [],\n events = {},\n createHandle,\n } =\n typeof tagOrOptions === 'string'\n ? { tagName: tagOrOptions, ...maybeConfig }\n : tagOrOptions;\n\n // Configuration is captured when the bridge component type is created, so these\n // collections are stable for the lifetime of this component type.\n const propertyKeys = Array.from(properties);\n const propertyKeySet = new Set<PropertyKey>(propertyKeys as readonly PropertyKey[]);\n const eventEntries = Object.entries(events);\n\n const Bridge = React.forwardRef<Handle, Props & Omit<React.HTMLAttributes<Element>, keyof Props>>(\n function LitCustomElementBridge(props, forwardedRef) {\n const elementRef = React.useRef<Element | null>(null);\n const [element, setElement] = React.useState<Element | null>(null);\n const appliedPropertiesRef = React.useRef<Set<PropertyKey>>(new Set());\n const appliedElementRef = React.useRef<Element | null>(null);\n const eventBindingsRef = React.useRef<EventBinding[]>([]);\n\n const handleRef = React.useCallback((node: Element | null) => {\n elementRef.current = node;\n setElement(node);\n }, []);\n\n React.useImperativeHandle(\n forwardedRef,\n () => {\n if (createHandle) {\n return createHandle(() => elementRef.current);\n }\n return elementRef.current as unknown as Handle;\n },\n [createHandle],\n );\n\n // Synchronize complex properties directly on the DOM element instance.\n // Runs before paint so the element never displays a stale snapshot.\n useIsomorphicLayoutEffect(() => {\n if (!element) return;\n\n if (appliedElementRef.current !== element) {\n appliedElementRef.current = element;\n appliedPropertiesRef.current = new Set();\n }\n\n const host = element as unknown as Record<PropertyKey, unknown>;\n const propsRecord = props as Record<PropertyKey, unknown>;\n const appliedProperties = appliedPropertiesRef.current;\n\n for (const propKey of propertyKeys) {\n if (propKey in propsRecord) {\n // Track ownership even when the element rejects the write so a\n // later removal still attempts to clear the bridge-owned value.\n appliedProperties.add(propKey);\n try {\n host[propKey] = propsRecord[propKey];\n } catch {\n // A read-only custom-element property may reject assignment;\n // its adapter remains responsible for interpreting the value.\n }\n } else if (appliedProperties.has(propKey)) {\n // React removed a property that this bridge previously assigned.\n // Clear it instead of leaving a stale object on the host, while\n // preserving untouched element defaults (for example `step`).\n try {\n host[propKey] = undefined;\n } catch {\n // A read-only custom-element property may reject assignment;\n // its adapter remains responsible for interpreting omission.\n } finally {\n appliedProperties.delete(propKey);\n }\n }\n }\n }, [element, props, propertyKeys]);\n\n // Attach one listener per event mapping. The effect reconciles by handler\n // identity: a re-render with the same handler does not touch the DOM,\n // while a changed handler (or `undefined`) removes the previous listener\n // and attaches the new one.\n // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs after every commit and compares handler identities before touching the DOM.\n React.useEffect(() => {\n if (!element) return;\n\n const previousBindings = eventBindingsRef.current;\n const nextBindings: EventBinding[] = [];\n const propsRecord = props as Record<string, unknown>;\n\n for (let index = 0; index < eventEntries.length; index++) {\n const entry = eventEntries[index]!;\n const [propHandlerName, customEventName] = entry;\n const handler = propsRecord[propHandlerName];\n const previous = previousBindings[index];\n\n if (\n previous &&\n previous.element === element &&\n previous.eventName === customEventName &&\n previous.handler === handler\n ) {\n nextBindings.push(previous);\n continue;\n }\n\n if (previous?.listener) {\n previous.element.removeEventListener(previous.eventName, previous.listener);\n }\n\n if (isEventHandler(handler)) {\n const listener = (event: Event) => {\n if (typeof handler === 'function') {\n handler(event);\n } else {\n handler.handleEvent(event);\n }\n };\n element.addEventListener(customEventName, listener);\n nextBindings.push({\n element,\n eventName: customEventName,\n handler,\n listener,\n });\n } else {\n nextBindings.push({\n element,\n eventName: customEventName,\n handler,\n });\n }\n }\n\n // Configuration is fixed per bridge, but release any trailing bindings\n // if a future implementation allows the mapping to shrink.\n for (let index = eventEntries.length; index < previousBindings.length; index++) {\n const stale = previousBindings[index]!;\n if (stale.listener) {\n stale.element.removeEventListener(stale.eventName, stale.listener);\n }\n }\n\n eventBindingsRef.current = nextBindings;\n });\n\n // Release every listener when the bridge element unmounts.\n React.useEffect(() => {\n return () => {\n for (const binding of eventBindingsRef.current) {\n if (binding.listener) {\n binding.element.removeEventListener(binding.eventName, binding.listener);\n }\n }\n eventBindingsRef.current = [];\n };\n }, []);\n\n // Filter out properties and event props so they aren't stringified as HTML attributes.\n // React 18 maps `className` to a `classname` attribute on unknown custom\n // elements, so normalize the standard aliases explicitly for both 18/19.\n const domProps: Record<string, unknown> = { ref: handleRef };\n for (const [key, value] of Object.entries(props)) {\n if (key === 'children') {\n domProps[key] = value;\n } else if (key === 'className') {\n domProps.class = value;\n } else if (key === 'htmlFor') {\n domProps.for = value;\n } else if (!propertyKeySet.has(key) && !(key in events)) {\n domProps[key] = value;\n }\n }\n\n return React.createElement(tagName, domProps);\n },\n );\n\n Bridge.displayName = `LitBridge(${tagName})`;\n return Bridge;\n}\n\n// Ergonomic aliases matching monorepo and ecosystem naming conventions\nexport { createLitElementBridge as createLitBridge };\nexport { createLitElementBridge as createCustomElementBridge };\n","/**\n * @fileoverview Idempotent custom element registration for the reference\n * components. ESM and CJS builds can both be loaded in one process, so an\n * unconditional `customElements.define()` would throw on the second load.\n *\n * @implements web-component-primitive\n * @memberof core-concepts\n */\n\nexport function defineCustomElement(\n tagName: string,\n elementConstructor: CustomElementConstructor,\n): void {\n if (typeof customElements === 'undefined') return;\n if (customElements.get(tagName)) return;\n customElements.define(tagName, elementConstructor);\n}\n","/**\n * @fileoverview Reference implementation of <lit-quantity-stepper> custom element.\n * Extends FormAssociatedLitElement for full native <form> and FormData participation,\n * constraint validation, form reset lifecycle, and Shadow DOM encapsulation.\n *\n * @implements view-layer\n * @implements web-component-primitive\n * @memberof core-concepts\n */\n\nimport { html, css, type PropertyValues } from 'lit';\nimport { property, state, query } from 'lit/decorators.js';\nimport { defineCustomElement } from '../define-once.js';\nimport { FormAssociatedLitElement } from '../form-associated-element.js';\n\n/**\n * Event detail payload dispatched by <lit-quantity-stepper> on change and input events.\n */\nexport interface QuantityChangeEventDetail {\n value: number;\n}\n\n/**\n * Reference Form-Associated Custom Element providing a numerical stepper control.\n *\n * Features:\n * - Native <form> submission and `new FormData()` integration via `ElementInternals`.\n * - HTML5 constraint validation (`rangeUnderflow`, `rangeOverflow`, `stepMismatch`, `badInput`).\n * - Form reset lifecycle restoration via `onFormReset()`.\n * - Disabled and readonly state propagation.\n * - Increment / Decrement methods with step precision and boundary clamping.\n * - Standard CustomEvent dispatching (`change`, `input`, `quantity-change`) with `{ bubbles: true, composed: true }`.\n * - Fully encapsulated Shadow DOM styling with `::part(button)` and `::part(input)` styling hooks.\n */\nexport class LitQuantityStepper extends FormAssociatedLitElement {\n static override styles = css`\n :host {\n display: inline-flex;\n vertical-align: middle;\n font-family: var(\n --stepper-font-family,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n Roboto,\n sans-serif\n );\n --_border-color: var(--stepper-border-color, #cbd5e1);\n --_focus-border-color: var(--stepper-focus-border-color, #3b82f6);\n --_invalid-border-color: var(--stepper-invalid-border-color, #ef4444);\n --_bg: var(--stepper-bg, #ffffff);\n --_btn-bg: var(--stepper-button-bg, #f8fafc);\n --_btn-hover-bg: var(--stepper-button-hover-bg, #e2e8f0);\n --_btn-active-bg: var(--stepper-button-active-bg, #cbd5e1);\n --_btn-color: var(--stepper-button-color, #334155);\n --_radius: var(--stepper-radius, 8px);\n --_font-size: var(--stepper-font-size, 14px);\n --_height: var(--stepper-height, 36px);\n }\n\n :host([disabled]) {\n opacity: 0.5;\n pointer-events: none;\n cursor: not-allowed;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .stepper {\n display: inline-flex;\n align-items: stretch;\n height: var(--_height);\n background: var(--_bg);\n border: 1px solid var(--_border-color);\n border-radius: var(--_radius);\n overflow: hidden;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n box-sizing: border-box;\n }\n\n .stepper.focused {\n border-color: var(--_focus-border-color);\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);\n }\n\n :host(:invalid) .stepper,\n .stepper.invalid {\n border-color: var(--_invalid-border-color);\n }\n\n :host(:invalid) .stepper.focused,\n .stepper.invalid.focused {\n box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2);\n }\n\n button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--_height);\n padding: 0;\n margin: 0;\n border: none;\n background: var(--_btn-bg);\n color: var(--_btn-color);\n font-size: calc(var(--_font-size) + 2px);\n font-weight: 600;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease, color 0.15s ease;\n box-sizing: border-box;\n }\n\n button:hover:not(:disabled) {\n background: var(--_btn-hover-bg);\n }\n\n button:active:not(:disabled) {\n background: var(--_btn-active-bg);\n }\n\n button:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n .btn-decrement {\n border-right: 1px solid var(--_border-color);\n }\n\n .btn-increment {\n border-left: 1px solid var(--_border-color);\n }\n\n input {\n width: 48px;\n padding: 0 6px;\n margin: 0;\n border: none;\n background: transparent;\n text-align: center;\n font-size: var(--_font-size);\n font-weight: 600;\n color: var(--stepper-text-color, #0f172a);\n font-family: inherit;\n box-sizing: border-box;\n -moz-appearance: textfield;\n }\n\n input::-webkit-outer-spin-button,\n input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n input:focus {\n outline: none;\n }\n `;\n\n /**\n * The current numerical value of the stepper.\n */\n @property({ type: Number })\n value = 1;\n\n /**\n * Minimum permissible value (inclusive).\n */\n @property({ type: Number })\n min = 1;\n\n /**\n * Maximum permissible value (inclusive).\n */\n @property({ type: Number })\n max = 99;\n\n /**\n * Step increment/decrement delta. Must be greater than 0.\n */\n @property({ type: Number })\n step = 1;\n\n /**\n * Whether the control prevents user modification while remaining form-submittable.\n */\n @property({ type: Boolean, reflect: true })\n readonly = false;\n\n /**\n * Default value used to restore state when the enclosing form is reset.\n */\n @property({ type: Number, attribute: 'default-value' })\n defaultValue?: number;\n\n @state()\n private focused = false;\n\n @query('input')\n private inputEl?: HTMLInputElement;\n\n #initialValue: number = 1;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.#initialValue = this.defaultValue ?? this.value;\n this.#syncFormState();\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('defaultValue') && this.defaultValue !== undefined) {\n this.#initialValue = this.defaultValue;\n }\n\n if (\n changedProperties.has('value') ||\n changedProperties.has('min') ||\n changedProperties.has('max') ||\n changedProperties.has('step') ||\n changedProperties.has('disabled')\n ) {\n this.#syncFormState();\n }\n }\n\n /**\n * Evaluates step alignment using floating-point safe division.\n */\n #checkStepMismatch(val: number, min: number, step: number): boolean {\n if (step <= 0 || Number.isNaN(val)) return false;\n const quotient = (val - min) / step;\n const rounded = Math.round(quotient);\n return Math.abs(quotient - rounded) > 1e-7;\n }\n\n /**\n * Formats a value for form submission and HTML input display,\n * preventing scientific notation drift.\n */\n #formatValue(val: unknown): string {\n if (typeof val === 'number') {\n if (Number.isNaN(val)) return 'NaN';\n if (Math.abs(val) < 1e-9) return '0';\n const str = String(val);\n if (str.includes('e-')) {\n return val.toFixed(10).replace(/\\.?0+$/, '');\n }\n return str;\n }\n return String(val ?? '');\n }\n\n /**\n * Derives decimal precision from numbers to avoid IEEE 754 precision drift.\n */\n #getPrecision(...numbers: number[]): number {\n let maxDecimals = 0;\n for (const n of numbers) {\n if (!Number.isFinite(n)) continue;\n const str = n.toString();\n const dot = str.indexOf('.');\n if (dot !== -1) {\n maxDecimals = Math.max(maxDecimals, str.length - dot - 1);\n }\n }\n return Math.min(12, maxDecimals);\n }\n\n /**\n * Synchronizes internal value with ElementInternals form value and constraint validation.\n */\n #syncFormState(): void {\n // 1. Submit current value (string representation)\n this.setFormValue(this.#formatValue(this.value));\n\n // 2. Evaluate standard constraint validation\n const flags: Record<string, boolean> = {};\n let message = '';\n\n const rawVal: unknown = this.value;\n const isBlank = typeof rawVal === 'string' && rawVal.trim() === '';\n const num = isBlank ? Number.NaN : Number(this.value);\n\n if (isBlank || Number.isNaN(num)) {\n flags.badInput = true;\n message = 'Please enter a valid number.';\n } else if (num < this.min) {\n flags.rangeUnderflow = true;\n message = `Value must be greater than or equal to ${this.min}.`;\n } else if (num > this.max) {\n flags.rangeOverflow = true;\n message = `Value must be less than or equal to ${this.max}.`;\n } else if (this.#checkStepMismatch(num, this.min, this.step)) {\n flags.stepMismatch = true;\n message = `Value must be an increment of ${this.step} from ${this.min}.`;\n }\n\n const anchor =\n this.inputEl ??\n (this.renderRoot?.querySelector('input') as HTMLElement | undefined);\n this.setValidity(flags, message, anchor);\n }\n\n /**\n * Emits input, change, and quantity-change CustomEvents across Shadow DOM boundaries.\n */\n #emitEvents(triggerType: 'input' | 'change' | 'both'): void {\n const detail: QuantityChangeEventDetail = { value: this.value };\n\n if (triggerType === 'input' || triggerType === 'both') {\n this.dispatchEvent(\n new CustomEvent<QuantityChangeEventDetail>('input', {\n bubbles: true,\n composed: true,\n detail,\n })\n );\n }\n\n if (triggerType === 'change' || triggerType === 'both') {\n this.dispatchEvent(\n new CustomEvent<QuantityChangeEventDetail>('change', {\n bubbles: true,\n composed: true,\n detail,\n })\n );\n this.dispatchEvent(\n new CustomEvent<QuantityChangeEventDetail>('quantity-change', {\n bubbles: true,\n composed: true,\n detail,\n })\n );\n }\n }\n\n /**\n * Increments the current value by the configured step.\n * If at or above max, or if disabled/readonly, this operation is a no-op.\n */\n public increment(): void {\n if (this.disabled || this.readonly) return;\n const current = Number(this.value);\n if (Number.isNaN(current)) return;\n\n const precision = this.#getPrecision(this.step, current, this.max);\n const next = Number((current + this.step).toFixed(precision));\n if (next > this.max + 1e-9) return;\n\n const clamped = Math.abs(next - this.max) < 1e-9 ? this.max : Math.min(next, this.max);\n this.value = Math.abs(clamped) < 1e-9 ? 0 : clamped;\n this.#syncFormState();\n this.requestUpdate();\n this.#emitEvents('both');\n }\n\n /**\n * Decrements the current value by the configured step.\n * If at or below min, or if disabled/readonly, this operation is a no-op.\n */\n public decrement(): void {\n if (this.disabled || this.readonly) return;\n const current = Number(this.value);\n if (Number.isNaN(current)) return;\n\n const precision = this.#getPrecision(this.step, current, this.min);\n const next = Number((current - this.step).toFixed(precision));\n if (next < this.min - 1e-9) return;\n\n const clamped = Math.abs(next - this.min) < 1e-9 ? this.min : Math.max(next, this.min);\n this.value = Math.abs(clamped) < 1e-9 ? 0 : clamped;\n this.#syncFormState();\n this.requestUpdate();\n this.#emitEvents('both');\n }\n\n /**\n * Sets the value imperatively with optional event dispatching.\n *\n * @param val New numerical value.\n * @param options.dispatchEvents If true, emits `input` and `change` events.\n */\n public setValue(val: number, options?: { dispatchEvents?: boolean }): void {\n const num = typeof val === 'number' ? val : Number(val);\n this.value = num;\n this.#syncFormState();\n this.requestUpdate();\n\n if (options?.dispatchEvents) {\n this.#emitEvents('both');\n }\n }\n\n /**\n * Handles user input from the internal number input field.\n */\n #handleInput = (e: Event): void => {\n e.stopPropagation();\n const target = e.target as HTMLInputElement;\n const parsed = parseFloat(target.value);\n this.value = Number.isNaN(parsed) ? (target.value as any) : parsed;\n this.#syncFormState();\n this.#emitEvents('input');\n };\n\n /**\n * Handles commit / change from the internal number input field.\n */\n #handleChange = (e: Event): void => {\n e.stopPropagation();\n const target = e.target as HTMLInputElement;\n const parsed = parseFloat(target.value);\n this.value = Number.isNaN(parsed) ? (target.value as any) : parsed;\n this.#syncFormState();\n this.#emitEvents('change');\n };\n\n /**\n * Invoked by FormAssociatedLitElement when the parent form executes a reset.\n * Restores initial value, clears validation, and synchronizes form value.\n */\n protected override onFormReset(): void {\n this.value = this.defaultValue ?? this.#initialValue;\n this.#syncFormState();\n this.requestUpdate();\n }\n\n override render() {\n const isInvalid = !this.validity.valid;\n const current = Number(this.value);\n const isDecDisabled =\n this.disabled ||\n this.readonly ||\n Number.isNaN(current) ||\n current <= this.min + 1e-9;\n const isIncDisabled =\n this.disabled ||\n this.readonly ||\n Number.isNaN(current) ||\n current >= this.max - 1e-9;\n\n return html`\n <div\n class=\"stepper ${this.focused ? 'focused' : ''} ${isInvalid ? 'invalid' : ''}\"\n role=\"group\"\n aria-label=${this.getAttribute('aria-label') || 'Quantity Stepper'}\n >\n <button\n type=\"button\"\n part=\"button button-decrement\"\n class=\"btn-decrement\"\n ?disabled=${isDecDisabled}\n @click=${this.decrement}\n tabindex=\"-1\"\n aria-label=\"Decrease quantity\"\n >−</button>\n <input\n type=\"number\"\n part=\"input\"\n .value=${this.#formatValue(this.value)}\n min=${this.min}\n max=${this.max}\n step=${this.step}\n ?disabled=${this.disabled}\n ?readonly=${this.readonly}\n @input=${this.#handleInput}\n @change=${this.#handleChange}\n @focus=${() => {\n this.focused = true;\n }}\n @blur=${() => {\n this.focused = false;\n }}\n aria-label=\"Quantity\"\n />\n <button\n type=\"button\"\n part=\"button button-increment\"\n class=\"btn-increment\"\n ?disabled=${isIncDisabled}\n @click=${this.increment}\n tabindex=\"-1\"\n aria-label=\"Increase quantity\"\n >+</button>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'lit-quantity-stepper': LitQuantityStepper;\n }\n}\n\ndefineCustomElement('lit-quantity-stepper', LitQuantityStepper);\n","/**\n * @fileoverview Reference implementation of <lit-cart-badge> custom element.\n * Demonstrates dual-mode store subscription (direct property vs W3C Context Protocol),\n * selective projection for total count, coalesced pending state tracking,\n * and zero-memory-leak unmounting.\n *\n * @implements view-layer\n * @implements store-integration-pattern\n * @implements action-pipeline-system\n * @memberof core-concepts\n */\n\nimport { LitElement, html, css, nothing, type PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { defineCustomElement } from '../define-once.js';\nimport type { Context } from '@lit/context';\nimport type { ActionRegister } from '@context-action/core';\nimport {\n StoreController,\n ActionController,\n type ReadableStore,\n type Selector,\n type EqualityFn,\n} from '@context-action/lit';\nimport {\n createStoreContext,\n ContextStoreController,\n ContextActionController,\n actionRegisterContext,\n} from '../context.js';\n\n/**\n * Standard W3C Context token for the shopping cart domain store.\n * Uses Symbol.for to guarantee singleton identity across independent bundle chunks.\n */\nexport const cartStoreContext: Context<symbol, ReadableStore<any>> =\n createStoreContext<any>('cart');\n\n/**\n * Custom event detail emitted when the cart badge button is clicked.\n */\nexport interface CartBadgeClickEventDetail {\n count: number;\n isPending: boolean;\n}\n\n/**\n * Default projection selector that extracts total quantity from diverse cart store state structures:\n * - Primitive numbers (e.g. `5`)\n * - State objects with `totalCount`, `count`, or `total` numerical fields\n * - State objects with `items` arrays containing quantity numbers or objects (`{ quantity: n }`)\n */\nexport function defaultCartCountSelector(state: unknown): number {\n if (typeof state === 'number') {\n return Number.isFinite(state) ? Math.max(0, Math.floor(state)) : 0;\n }\n if (state && typeof state === 'object') {\n const obj = state as Record<string, any>;\n if (typeof obj.totalCount === 'number' && Number.isFinite(obj.totalCount)) {\n return Math.max(0, Math.floor(obj.totalCount));\n }\n if (typeof obj.count === 'number' && Number.isFinite(obj.count)) {\n return Math.max(0, Math.floor(obj.count));\n }\n if (typeof obj.total === 'number' && Number.isFinite(obj.total)) {\n return Math.max(0, Math.floor(obj.total));\n }\n if (Array.isArray(obj.items)) {\n return obj.items.reduce((sum: number, item: any) => {\n const qty =\n typeof item === 'number'\n ? item\n : typeof item?.quantity === 'number'\n ? item.quantity\n : 1;\n return sum + (Number.isFinite(qty) ? Math.max(0, qty) : 0);\n }, 0);\n }\n }\n return 0;\n}\n\n/**\n * Reference Custom Element for displaying a dynamic shopping cart badge.\n *\n * Supports two consumption paradigms:\n * 1. Direct property assignment: `.store=${cartStore}` and `.register=${actionRegister}`.\n * 2. W3C Context Protocol: automatically injects `cartStoreContext` and `actionRegisterContext`\n * from ancestor providers across Shadow DOM boundaries.\n */\nexport class LitCartBadge extends LitElement {\n static override styles = css`\n :host {\n display: inline-flex;\n vertical-align: middle;\n font-family: var(\n --cart-badge-font-family,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n Roboto,\n sans-serif\n );\n --_bg: var(--cart-badge-bg, #2563eb);\n --_color: var(--cart-badge-color, #ffffff);\n --_hover-bg: var(--cart-badge-hover-bg, #1d4ed8);\n --_active-bg: var(--cart-badge-active-bg, #1e40af);\n --_radius: var(--cart-badge-radius, 9999px);\n --_font-size: var(--cart-badge-font-size, 0.875rem);\n --_font-weight: var(--cart-badge-font-weight, 600);\n --_padding: var(--cart-badge-padding, 4px 10px);\n --_pulse-scale: var(--cart-badge-pulse-scale, 1.2);\n }\n\n :host([variant='secondary']) {\n --_bg: var(--cart-badge-bg, #475569);\n --_hover-bg: var(--cart-badge-hover-bg, #334155);\n --_active-bg: var(--cart-badge-active-bg, #1e293b);\n }\n\n :host([variant='accent']) {\n --_bg: var(--cart-badge-bg, #7c3aed);\n --_hover-bg: var(--cart-badge-hover-bg, #6d28d9);\n --_active-bg: var(--cart-badge-active-bg, #5b21b6);\n }\n\n :host([variant='danger']) {\n --_bg: var(--cart-badge-bg, #dc2626);\n --_hover-bg: var(--cart-badge-hover-bg, #b91c1c);\n --_active-bg: var(--cart-badge-active-bg, #991b1b);\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n :host([hide-zero][count-zero]) {\n display: none !important;\n }\n\n :host([disabled]) {\n opacity: 0.6;\n pointer-events: none;\n cursor: not-allowed;\n }\n\n .badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: var(--_padding);\n background-color: var(--_bg);\n color: var(--_color);\n border-radius: var(--_radius);\n font-size: var(--_font-size);\n font-weight: var(--_font-weight);\n border: none;\n cursor: pointer;\n user-select: none;\n outline: none;\n transition: background-color 0.15s ease, transform 0.15s ease;\n box-sizing: border-box;\n line-height: 1;\n }\n\n .badge:hover:not(:disabled) {\n background-color: var(--_hover-bg);\n }\n\n .badge:active:not(:disabled) {\n background-color: var(--_active-bg);\n }\n\n .badge:focus-visible {\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);\n }\n\n .badge:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n .count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.25em;\n font-variant-numeric: tabular-nums;\n }\n\n .pulse {\n animation: cart-badge-pulse 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n @keyframes cart-badge-pulse {\n 0% {\n transform: scale(1);\n }\n 50% {\n transform: scale(var(--_pulse-scale));\n }\n 100% {\n transform: scale(1);\n }\n }\n\n .spinner {\n display: inline-block;\n width: 12px;\n height: 12px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: cart-badge-spin 0.8s linear infinite;\n box-sizing: border-box;\n }\n\n @keyframes cart-badge-spin {\n to {\n transform: rotate(360deg);\n }\n }\n `;\n\n /**\n * Explicit ReadableStore instance passed directly to this component.\n * If provided, overrides context-based store resolution.\n */\n @property({ attribute: false })\n store?: ReadableStore<any>;\n\n /**\n * Explicit ActionRegister instance passed directly to this component.\n * If provided, overrides context-based action register resolution.\n */\n @property({ attribute: false })\n register?: ActionRegister<any>;\n\n /**\n * Custom projection selector for deriving the total quantity from store state.\n */\n @property({ attribute: false })\n selector?: Selector<any, number>;\n\n /**\n * Custom equality comparator function used for selective projection memoization.\n */\n @property({ attribute: false })\n equalityFn?: EqualityFn<number>;\n\n /**\n * Optional custom W3C Context token for resolving a cart store from ancestor providers.\n */\n @property({ attribute: false })\n storeContext?: Context<unknown, ReadableStore<any>>;\n\n /**\n * Optional custom W3C Context token for resolving an ActionRegister from ancestor providers.\n */\n @property({ attribute: false })\n actionContext?: Context<unknown, ActionRegister<any>>;\n\n /**\n * Static numerical count override. If defined, overrides both store and context values.\n */\n @property({ type: Number })\n value: number | undefined = undefined;\n\n /**\n * Maximum count displayed before truncating with a '+' suffix (e.g. 99 -> '99+').\n */\n @property({ type: Number, attribute: 'max-count' })\n maxCount = 99;\n\n /**\n * When true, hides the badge completely when count is 0.\n */\n @property({ type: Boolean, reflect: true, attribute: 'hide-zero' })\n hideZero = false;\n\n /**\n * Label text rendered next to the count. Defaults to 'Cart'.\n */\n @property({ type: String })\n label = 'Cart';\n\n /**\n * Optional action name dispatched on click through the configured ActionRegister.\n */\n @property({ type: String, attribute: 'action-name' })\n actionName?: string;\n\n /**\n * Optional payload passed when dispatching actionName.\n */\n @property({ attribute: false })\n actionPayload?: unknown;\n\n /**\n * Whether the badge button is disabled.\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Visual variant: 'primary' | 'secondary' | 'accent' | 'danger'\n */\n @property({ type: String, reflect: true })\n variant: 'primary' | 'secondary' | 'accent' | 'danger' = 'primary';\n\n @state()\n private isPulsing = false;\n\n // Controllers\n #contextStoreController: ContextStoreController<any, number>;\n #contextActionController: ContextActionController<any>;\n #directStoreController: StoreController<any, number> | null = null;\n #directActionController: ActionController<any> | null = null;\n\n #previousCount?: number;\n\n constructor() {\n super();\n\n // Initialize context controllers to listen across Shadow DOM\n this.#contextStoreController = new ContextStoreController(this, {\n context: cartStoreContext,\n selector: (state) =>\n this.selector ? this.selector(state) : defaultCartCountSelector(state),\n equalityFn: (a, b) => (this.equalityFn ? this.equalityFn(a, b) : Object.is(a, b)),\n });\n\n this.#contextActionController = new ContextActionController(this, {\n context: actionRegisterContext,\n });\n }\n\n override willUpdate(changedProperties: PropertyValues<this>): void {\n super.willUpdate(changedProperties);\n\n // Sync direct store controller when store property changes\n if (changedProperties.has('store') || changedProperties.has('selector') || changedProperties.has('equalityFn')) {\n if (this.#directStoreController) {\n this.#directStoreController.dispose();\n this.#directStoreController = null;\n }\n\n if (this.store) {\n this.#directStoreController = new StoreController(this, this.store, {\n selector: (state) =>\n this.selector ? this.selector(state) : defaultCartCountSelector(state),\n equalityFn: this.equalityFn ?? Object.is,\n });\n }\n }\n\n // Sync direct action controller when register property changes\n if (changedProperties.has('register')) {\n if (this.register) {\n this.#directActionController = new ActionController(this, this.register);\n } else {\n this.#directActionController = null;\n }\n }\n // Track count and pulse animation\n const currentCount = this.count;\n if (this.#previousCount !== undefined && currentCount > this.#previousCount) {\n this.isPulsing = true;\n }\n this.#previousCount = currentCount;\n }\n\n /**\n * Derives the current total count from direct override, direct store, or context store.\n */\n get count(): number {\n if (this.value !== undefined) {\n return this.value;\n }\n if (this.#directStoreController) {\n return this.#directStoreController.value;\n }\n if (this.store) {\n const raw =\n typeof this.store.getValue === 'function'\n ? this.store.getValue()\n : typeof this.store.getSnapshot === 'function'\n ? this.store.getSnapshot()\n : undefined;\n return this.selector ? this.selector(raw) : defaultCartCountSelector(raw);\n }\n if (\n this.#contextStoreController.isResolved &&\n this.#contextStoreController.value !== undefined\n ) {\n return this.#contextStoreController.value;\n }\n return 0;\n }\n\n set count(val: number | undefined) {\n const oldVal = this.count;\n if (val === undefined || val === null) {\n this.value = undefined;\n } else {\n this.value = Number.isFinite(val) ? Math.max(0, Math.floor(val)) : 0;\n }\n this.requestUpdate('count', oldVal);\n }\n\n /**\n * Indicates whether an action dispatch is currently in flight.\n */\n get isPending(): boolean {\n if (this.#directActionController) {\n return this.#directActionController.isPending;\n }\n return this.#contextActionController.isPending;\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n const currentCount = this.count;\n // Reflect helper attributes for consumer CSS queries\n this.toggleAttribute('has-items', currentCount > 0);\n this.toggleAttribute('count-zero', currentCount === 0);\n }\n\n #handleClick = async (): Promise<void> => {\n if (this.disabled || this.isPending) return;\n\n // Dispatch standard DOM event across Shadow DOM\n this.dispatchEvent(\n new CustomEvent<CartBadgeClickEventDetail>('cart-badge-click', {\n bubbles: true,\n composed: true,\n detail: {\n count: this.count,\n isPending: this.isPending,\n },\n })\n );\n\n // If an action name is configured, dispatch through the active action pipeline\n if (this.actionName) {\n try {\n if (this.#directActionController) {\n await this.#directActionController.dispatch(\n this.actionName,\n this.actionPayload as any\n );\n } else if (this.#contextActionController.register) {\n await this.#contextActionController.dispatch(\n this.actionName,\n this.actionPayload as any\n );\n }\n } catch (error) {\n this.dispatchEvent(\n new CustomEvent('cart-action-error', {\n bubbles: true,\n composed: true,\n detail: { error },\n })\n );\n }\n }\n };\n\n override render() {\n const current = this.count;\n if (this.hideZero && current <= 0) {\n return nothing;\n }\n\n const pending = this.isPending;\n const displayCount =\n current > this.maxCount ? `${this.maxCount}+` : String(current);\n\n return html`\n <button\n type=\"button\"\n part=\"button badge\"\n class=\"badge ${this.isPulsing ? 'pulse' : ''}\"\n ?disabled=${this.disabled || pending}\n @click=${this.#handleClick}\n @animationend=${() => {\n this.isPulsing = false;\n }}\n aria-label=${this.getAttribute('aria-label') ||\n `${this.label}: ${displayCount} items`}\n >\n <slot name=\"icon\">\n <span part=\"icon\" class=\"icon\" aria-hidden=\"true\">🛒</span>\n </slot>\n <span part=\"label\" class=\"label\"><slot>${this.label}</slot></span>\n <span part=\"count\" class=\"count\">${displayCount}</span>\n ${pending\n ? html`<span\n part=\"spinner\"\n class=\"spinner\"\n aria-label=\"Loading\"\n ></span>`\n : nothing}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'lit-cart-badge': LitCartBadge;\n }\n}\n\ndefineCustomElement('lit-cart-badge', LitCartBadge);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAS,6BAA6B,OAA0C;CAC9E,MAAM,WAAW,CAAC,CAAC,MAAM;CACzB,MAAM,cAAc,CAAC,CAAC,MAAM;CAC5B,MAAM,kBAAkB,CAAC,CAAC,MAAM;CAChC,MAAM,gBAAgB,CAAC,CAAC,MAAM;CAC9B,MAAM,iBAAiB,CAAC,CAAC,MAAM;CAC/B,MAAM,eAAe,CAAC,CAAC,MAAM;CAC7B,MAAM,UAAU,CAAC,CAAC,MAAM;CACxB,MAAM,WAAW,CAAC,CAAC,MAAM;CACzB,MAAM,eAAe,CAAC,CAAC,MAAM;CAC7B,MAAM,eAAe,CAAC,CAAC,MAAM;CAe7B,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAA,EAtBA,YACA,eACA,mBACA,iBACA,kBACA,gBACA,WACA,YACA,gBACA;EAcA;CACF;AACF;;;;;;;;;;;;;AAcA,IAAa,2BAAb,cAA8C,WAAW;;EAKtB,KAAA,iBAAA;;CAsBjC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,cAAc;EACZ,MAAM;EArBD,KAAA,OAAA;EAGA,KAAA,OAAA;EAGI,KAAA,WAAA;EAGA,KAAA,WAAA;EAGmC,KAAA,aAAA;EACxB,KAAA,aAAA;EACe,KAAA,iBAAA,CAAC;EACjB,KAAA,qBAAA;EACN,KAAA,eAAA;EACA,KAAA,eAAA;EAC2B,KAAA,kBAAA;EAmRtB,KAAA,oBAAA,UAAuB;GACzC,IAAI,KAAK,gBAAgB,MAAM,kBAAkB;GACjD,qBAAqB;IACnB,IAAI,KAAK,gBAAgB,MAAM,kBAAkB;IACjD,KAAK,kBAAkB;GACzB,CAAC;EACH;EApRE,IAAI,OAAO,KAAK,oBAAoB,YAClC,IAAI;GACF,KAAK,YAAY,KAAK,gBAAgB;EACxC,QAAQ,CAER;CAEJ;;;;;;CAWA,IAAI,OAA+B;EACjC,IAAI,KAAK,aAAa,UAAU,KAAK,aAAa,KAAK,UAAU,SAAS,KAAA,GACxE,OAAO,KAAK,UAAU;EAGxB,MAAM,WAAW,KAAK,aAAa,MAAM;EACzC,IAAI,YAAY,KAAK,eAAe;GAClC,MAAM,KAAK,KAAK,cAAc,eAAe,QAAQ;GACrD,IAAI,cAAc,iBAAiB,OAAO;EAC5C;EAEA,OAAO,KAAK,QAAQ,MAAM;CAC5B;;;;;;;CAYA,aACE,OACA,OACM;EACN,KAAK,aAAa;EAClB,KAAK,aAAa;EAElB,IAAI,KAAK,aAAa,OAAO,KAAK,UAAU,iBAAiB,YAC3D,IAAI;GACF,KAAK,UAAU,aAAa,OAAO,KAAK;EAC1C,QAAQ,CAER;CAEJ;;;;CAKA,eAAuD;EACrD,OAAO,KAAK;CACd;;;;CAKA,IAAW,YAA6C;EACtD,OAAO,KAAK;CACd;;;;CAKA,IAAW,YAAqB;EAC9B,OAAO,KAAK;CACd;;;;;;;;CAaA,YACE,QAA4B,CAAC,GAC7B,SACA,QACM;EACN,KAAK,iBAAiB,EAAE,GAAG,MAAM;EACjC,KAAK,qBAAqB,WAAW;EACrC,KAAK,eAAe,CAAC,CAAC,MAAM;EAE5B,IAAI,KAAK,aAAa,OAAO,KAAK,UAAU,gBAAgB,YAC1D,IAAI;GACF,KAAK,UAAU,YAAY,OAAO,SAAS,MAAM;EACnD,QAAQ,CAER;CAEJ;;;;CAKA,IAAI,WAA0B;EAC5B,IAAI,KAAK,aAAa,cAAc,KAAK,aAAa,KAAK,UAAU,UACnE,OAAO,KAAK,UAAU;EAExB,OAAO,6BAA6B;GAClC,GAAG,KAAK;GACR,aAAa,KAAK,eAAe,eAAe,KAAK;EACvD,CAAC;CACH;;;;CAKA,IAAI,oBAA4B;EAC9B,IACE,KAAK,aACL,uBAAuB,KAAK,aAC5B,OAAO,KAAK,UAAU,sBAAsB,UAE5C,OAAO,KAAK,UAAU;EAExB,OAAO,KAAK,SAAS,QAAQ,KAAK,KAAK;CACzC;;;;CAKA,IAAI,eAAwB;EAC1B,IACE,KAAK,aACL,kBAAkB,KAAK,aACvB,OAAO,KAAK,UAAU,iBAAiB,WAEvC,OAAO,KAAK,UAAU;EAExB,OAAO,CAAC,KAAK;CACf;;;;;;CAOA,gBAAgC;EAC9B,IAAI,KAAK,aAAa,OAAO,KAAK,UAAU,kBAAkB,YAC5D,IAAI;GACF,OAAO,KAAK,UAAU,cAAc;EACtC,QAAQ,CAER;EAGF,IAAI,CAAC,KAAK,cAAc,OAAO;EAE/B,IAAI,CAAC,KAAK,SAAS,OAAO;GACxB,MAAM,QAAQ,IAAI,MAAM,WAAW;IAAE,SAAS;IAAO,YAAY;GAAK,CAAC;GACvE,KAAK,cAAc,KAAK;GACxB,OAAO;EACT;EAEA,OAAO;CACT;;;;CAKA,iBAAiC;EAC/B,IAAI,KAAK,aAAa,OAAO,KAAK,UAAU,mBAAmB,YAC7D,IAAI;GACF,OAAO,KAAK,UAAU,eAAe;EACvC,QAAQ,CAER;EAEF,OAAO,KAAK,cAAc;CAC5B;;;;;CAMA,kBAAyB,SAAuB;EAC9C,IAAI,SACF,KAAK,YAAY,EAAE,aAAa,KAAK,GAAG,OAAO;OAE/C,KAAK,YAAY,CAAC,CAAC;CAEvB;;;;;CAUA,oBAAiC;EAC/B,IAAI,KAAK,cAAc;EACvB,KAAK,eAAe;EACpB,IAAI;GACF,KAAK,YAAY;EACnB,UAAU;GACR,qBAAqB;IACnB,KAAK,eAAe;GACtB,CAAC;EACH;CACF;;;;;CAMA,cAA8B,CAAC;;;;CAK/B,qBAA4B,UAAyB;EACnD,KAAK,WAAW;EAChB,KAAK,eAAe,QAAQ;EAC5B,KAAK,cAAc;CACrB;;;;CAKA,eAAyB,WAA0B,CAAC;;;;CAKpD,yBAAgC,OAAgB,MAAwC;EACtF,KAAK,aAAa;EAClB,KAAK,mBAAmB,OAAO,IAAI;CACrC;;;;CAKA,mBAA6B,QAAiB,OAAyC,CAAC;;;;CAKxF,uBAA8B,MAAoC;EAChE,KAAK,kBAAkB,IAAI;EAC3B,KAAK,iBAAiB,IAAI;CAC5B;;;;CAKA,iBAA2B,OAAqC,CAAC;CAMjE;CAQA,kBAAkB,SAAuC;EACvD,IAAI,KAAK,oBAAoB,SAAS;EAEtC,IAAI,KAAK,iBACP,KAAK,gBAAgB,oBAAoB,SAAS,KAAK,gBAAgB;EAGzE,KAAK,kBAAkB;EAEvB,IAAI,KAAK,iBACP,KAAK,gBAAgB,iBAAiB,SAAS,KAAK,gBAAgB;CAExE;CAEA,oBAAmC;EACjC,MAAM,kBAAkB;EACxB,KAAK,kBAAkB,KAAK,IAAI;CAClC;CAEA,uBAAsC;EACpC,KAAK,kBAAkB,IAAI;EAC3B,MAAM,qBAAqB;CAC7B;AACF;AArUG,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,yBAAA,WAAA,QAAA,KAAA,CAAA;AAGxC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,yBAAA,WAAA,QAAA,KAAA,CAAA;AAGxC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,yBAAA,WAAA,YAAA,KAAA,CAAA;AAGzC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,yBAAA,WAAA,YAAA,KAAA,CAAA;;;;;;;;;;;;;;;;;;;AC9D5C,MAAa,wBACX,cAA2C,OAAO,IAAI,yBAAyB,CAAC;;;;;;;;AASlF,MAAa,0BACX,cAA6C,OAAO,IAAI,2BAA2B,CAAC;;;;;;;;;;;;;;;;;;;AAoBtF,SAAgB,mBACd,MACmC;CACnC,IAAI,CAAC,QAAQ,OAAO,SAAS,UAC3B,MAAM,IAAI,UAAU,0DAAwD;CAE9E,OAAO,cACL,OAAO,IAAI,wBAAwB,MAAM,CAC3C;AACF;;;;;;;;AASA,SAAgB,4BACd,OAAO,WAC6B;CACpC,OAAO,cACL,OAAO,IAAI,2BAA2B,MAAM,CAC9C;AACF;;;;;;;;AASA,SAAgB,8BACd,OAAO,WAC+B;CACtC,OAAO,cACL,OAAO,IAAI,6BAA6B,MAAM,CAChD;AACF;;;;;;;;;;AAeA,SAAgB,sBACd,MACA,UACA,UAA+C,uBACO;CACtD,OAAO,IAAI,gBAAgB,MAAM;EAC/B;EACA,cAAc;CAChB,CAAC;AACH;;;;;;;;;;AAWA,SAAgB,wBACd,MACA,YACA,UAAiD,yBACO;CACxD,OAAO,IAAI,gBAAgB,MAAM;EAC/B;EACA,cAAc;CAChB,CAAC;AACH;;;;;;;;;;AAWA,SAAgB,aACd,MACA,SACA,OACqD;CACrD,OAAO,IAAI,gBAAgB,MAAM;EAC/B;EACA,cAAc;CAChB,CAAC;AACH;;;;;;;;;;;;;;;;;;;AAuCA,IAAa,yBAAb,MAA6F;CAC3F;CACA;CACA;CAEA,gBAA8C,KAAA;CAC9C,oBAAwC;CACxC,iBAAuC,KAAA;CACvC,cAAc;CAEd,YACE,MACA,SACA;EACA,IAAI,CAAC,QAAQ,OAAO,KAAK,kBAAkB,YACzC,MAAM,IAAI,UACR,mGACF;EAEF,IAAI,CAAC,SAAS,SACZ,MAAM,IAAI,UAAU,wDAAwD;EAG9E,KAAK,QAAQ;EACb,KAAK,YAAY,QAAQ,cAAc,MAAS;EAChD,KAAK,cAAc,QAAQ,cAAc,OAAO;EAGhD,IAAI,gBAAgB,MAAM;GACxB,SAAS,QAAQ;GACjB,WAAW,UAAU,KAAK,qBAAqB,KAAK;GACpD,WAAW;EACb,CAAC;EAED,KAAK,cAAc,IAAI;CACzB;;CAGA,IAAI,QAA8B;EAChC,OAAO,KAAK;CACd;;CAGA,IAAI,QAAsC;EACxC,OAAO,KAAK;CACd;;CAGA,IAAI,aAAsB;EACxB,OAAO,KAAK;CACd;CAEA,iBAAiB,OAA4B;EAC3C,IAAI,OAAO,MAAM,aAAa,YAC5B,OAAO,MAAM,SAAS;EAExB,IAAI,OAAO,MAAM,gBAAgB,YAC/B,OAAO,MAAM,YAAY;EAE3B,MAAM,IAAI,UACR,oFACF;CACF;CAEA,qBAAqB,WAAoC;EACvD,IAAI,KAAK,kBAAkB,aAAa,KAAK,aAC3C;EAIF,KAAK,0BAA0B;EAE/B,KAAK,gBAAgB;EAErB,IAAI,CAAC,WAAW;GACd,KAAK,cAAc;GACnB,KAAK,iBAAiB,KAAA;GACtB,KAAK,MAAM,cAAc;GACzB;EACF;EAEA,KAAK,cAAc;EACnB,KAAK,uBAAuB;EAC5B,KAAK,MAAM,cAAc;CAC3B;CAEA,yBAA+B;EAC7B,IAAI,CAAC,KAAK,eAAe;EAGzB,MAAM,MAAM,KAAK,iBAAiB,KAAK,aAAa;EACpD,KAAK,iBAAiB,KAAK,UAAU,GAAG;EAGxC,KAAK,oBAAoB,KAAK,cAAc,gBAAgB;GAC1D,IAAI,CAAC,KAAK,eAAe;GACzB,MAAM,UAAU,KAAK,iBAAiB,KAAK,aAAa;GACxD,MAAM,eAAe,KAAK,UAAU,OAAO;GAE3C,IAAI,CAAC,KAAK,YAAY,KAAK,gBAA4B,YAAY,GAAG;IACpE,KAAK,iBAAiB;IACtB,KAAK,MAAM,cAAc;GAC3B;EACF,CAAC;CACH;CAEA,4BAAkC;EAChC,IAAI,KAAK,mBAAmB;GAC1B,KAAK,kBAAkB;GACvB,KAAK,oBAAoB;EAC3B;CACF;CAEA,gBAAsB;EAGpB,IAAI,KAAK,iBAAiB,CAAC,KAAK,mBAAmB;GACjD,KAAK,uBAAuB;GAC5B,KAAK,MAAM,cAAc;EAC3B;CACF;CAEA,mBAAyB;EAEvB,KAAK,0BAA0B;CACjC;AACF;;;;;;;;;;;AAsBA,IAAa,0BAAb,MAEA;CACE;CAEA,YAA2C,KAAA;CAC3C,aAAa;CACb,aAAgC,KAAA;CAEhC,YACE,MACA,SACA;EACA,IAAI,CAAC,QAAQ,OAAO,KAAK,kBAAkB,YACzC,MAAM,IAAI,UACR,oGACF;EAGF,KAAK,QAAQ;EACb,MAAM,MAAO,SAAS,WAAW;EAEjC,IAAI,gBAAgB,MAAM;GACxB,SAAS;GACT,WAAW,QAAQ;IACjB,KAAK,YAAY;IACjB,KAAK,MAAM,cAAc;GAC3B;GACA,WAAW;EACb,CAAC;EAED,KAAK,cAAc,IAAI;CACzB;;CAGA,IAAI,WAA0C;EAC5C,OAAO,KAAK;CACd;;CAGA,IAAI,YAAqB;EACvB,OAAO,KAAK;CACd;;CAGA,IAAI,YAA+B;EACjC,OAAO,KAAK;CACd;;;;;;;;CASA,MAAM,SACJ,QACA,GAAG,MACY;EACf,IAAI,CAAC,KAAK,WACR,MAAM,IAAI,MACR,6CAA6C,OAAO,MAAM,EAAE,iDAC9D;EAGF,KAAK,aAAa;EAClB,KAAK,aAAa,KAAA;EAClB,KAAK,MAAM,cAAc;EAEzB,IAAI;GACF,MAAM,KAAK,UAAU,SAAS,QAAQ,GAAG,IAAI;EAC/C,SAAS,KAAK;GACZ,MAAM,WAAW,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;GACnE,KAAK,aAAa;GAClB,MAAM;EACR,UAAU;GACR,KAAK,aAAa;GAClB,KAAK,MAAM,cAAc;EAC3B;CACF;;;;;;;;;;CAWA,MAAM,mBACJ,QACA,GAAG,MACS;EACZ,IAAI,CAAC,KAAK,WACR,MAAM,IAAI,MACR,6CAA6C,OAAO,MAAM,EAAE,iDAC9D;EAGF,KAAK,aAAa;EAClB,KAAK,aAAa,KAAA;EAClB,KAAK,MAAM,cAAc;EAEzB,IAAI;GACF,IAAI,OAAQ,KAAK,UAAkB,uBAAuB,YACxD,OAAO,MAAO,KAAK,UAAkB,mBAAmB,QAAQ,GAAG,IAAI;GAEzE,OAAQ,MAAM,KAAK,UAAU,SAAS,QAAQ,GAAG,IAAI;EACvD,SAAS,KAAK;GACZ,MAAM,WAAW,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;GACnE,KAAK,aAAa;GAClB,MAAM;EACR,UAAU;GACR,KAAK,aAAa;GAClB,KAAK,MAAM,cAAc;EAC3B;CACF;CAEA,gBAAsB,CAAC;CACvB,mBAAyB,CAAC;AAC5B;;AC7bA,SAAS,uBACP,QACA,KACgC;CAChC,IAAI,UAAyB;CAC7B,OAAO,SAAS;EACd,MAAM,aAAa,OAAO,yBAAyB,SAAS,GAAG;EAC/D,IAAI,YAAY,OAAO;EACvB,UAAU,OAAO,eAAe,OAAO;CACzC;AAEF;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,oBAId,MAC+D;CAC/D,MAAM,aAAa,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC;CAC3C,QAAQ,eAAe;EACrB,MAAM,uBAAgC;GACpC,MAAM,UAAU,WAAW;GAC3B,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,sCAAsC;GAExD,OAAO;EACT;EACA,MAAM,+BAAe,IAAI,QAGvB;EACF,MAAM,SAAS,CAAC;EAChB,KAAK,MAAM,OAAO,YAChB,OAAO,eAAe,QAAQ,KAAK;GACjC,cAAc;GACd,YAAY;GACZ,MAAM;IACJ,MAAM,UAAU,eAAe;IAC/B,MAAM,QAAiB,QAAQ,IAAI,SAAS,KAAK,OAAO;IACxD,IAAI,OAAO,UAAU,YAAY,OAAO;IACxC,IAAI,QAAQ,aAAa,IAAI,OAAO;IACpC,IAAI,CAAC,OAAO;KACV,wBAAQ,IAAI,IAAI;KAChB,aAAa,IAAI,SAAS,KAAK;IACjC;IACA,MAAM,SAAS,MAAM,IAAI,GAAG;IAC5B,IAAI,UAAU,OAAO,WAAW,OAAO,OAAO,OAAO;IACrD,MAAM,QAAQ,MAAM,KAAK,OAAO;IAChC,MAAM,IAAI,KAAK;KAAE,QAAQ;KAAO;IAAM,CAAC;IACvC,OAAO;GACT;GACA,IAAI,WAAoB;IACtB,MAAM,UAAU,eAAe;IAC/B,MAAM,aAAa,uBAAuB,SAAS,GAAG;IACtD,IAAI,cAAc,CAAC,WAAW,OAAO,CAAC,WAAW,UAC/C,MAAM,IAAI,UAAU,wCAAwC,OAAO,GAAG,EAAE,EAAE;IAG5E,IAAI,CADY,QAAQ,IAAI,SAAS,KAAK,WAAW,OAC1C,GACT,MAAM,IAAI,UAAU,wCAAwC,OAAO,GAAG,EAAE,EAAE;GAE9E;EACF,CAAC;EAEH,OAAO,OAAO,OAAO,MAAM;CAC7B;AACF;;;;;;;;;;;;ACjHA,MAAM,4BACJ,OAAO,WAAW,cAAc,MAAM,YAAY,MAAM;AAa1D,SAAS,eAAe,OAA4C;CAClE,OACE,OAAO,UAAU,cAChB,OAAO,UAAU,YAChB,UAAU,QACV,OAAQ,MAAoC,gBAAgB;AAElE;;;;;;;;;;;;;;AA4IA,SAAgB,uBAKd,cACA,aAC6C;CAC7C,MAAM,EACJ,SACA,aAAa,CAAC,GACd,SAAS,CAAC,GACV,iBAEA,OAAO,iBAAiB,WACpB;EAAE,SAAS;EAAc,GAAG;CAAY,IACxC;CAIN,MAAM,eAAe,MAAM,KAAK,UAAU;CAC1C,MAAM,iBAAiB,IAAI,IAAiB,YAAsC;CAClF,MAAM,eAAe,OAAO,QAAQ,MAAM;CAE1C,MAAM,SAAS,MAAM,WACnB,SAAS,uBAAuB,OAAO,cAAc;EACnD,MAAM,aAAa,MAAM,OAAuB,IAAI;EACpD,MAAM,CAAC,SAAS,cAAc,MAAM,SAAyB,IAAI;EACjE,MAAM,uBAAuB,MAAM,uBAAyB,IAAI,IAAI,CAAC;EACrE,MAAM,oBAAoB,MAAM,OAAuB,IAAI;EAC3D,MAAM,mBAAmB,MAAM,OAAuB,CAAC,CAAC;EAExD,MAAM,YAAY,MAAM,aAAa,SAAyB;GAC5D,WAAW,UAAU;GACrB,WAAW,IAAI;EACjB,GAAG,CAAC,CAAC;EAEL,MAAM,oBACJ,oBACM;GACJ,IAAI,cACF,OAAO,mBAAmB,WAAW,OAAO;GAE9C,OAAO,WAAW;EACpB,GACA,CAAC,YAAY,CACf;EAIA,gCAAgC;GAC9B,IAAI,CAAC,SAAS;GAEd,IAAI,kBAAkB,YAAY,SAAS;IACzC,kBAAkB,UAAU;IAC5B,qBAAqB,0BAAU,IAAI,IAAI;GACzC;GAEA,MAAM,OAAO;GACb,MAAM,cAAc;GACpB,MAAM,oBAAoB,qBAAqB;GAE/C,KAAK,MAAM,WAAW,cACpB,IAAI,WAAW,aAAa;IAG1B,kBAAkB,IAAI,OAAO;IAC7B,IAAI;KACF,KAAK,WAAW,YAAY;IAC9B,QAAQ,CAGR;GACF,OAAO,IAAI,kBAAkB,IAAI,OAAO,GAItC,IAAI;IACF,KAAK,WAAW,KAAA;GAClB,QAAQ,CAGR,UAAU;IACR,kBAAkB,OAAO,OAAO;GAClC;EAGN,GAAG;GAAC;GAAS;GAAO;EAAY,CAAC;EAOjC,MAAM,gBAAgB;GACpB,IAAI,CAAC,SAAS;GAEd,MAAM,mBAAmB,iBAAiB;GAC1C,MAAM,eAA+B,CAAC;GACtC,MAAM,cAAc;GAEpB,KAAK,IAAI,QAAQ,GAAG,QAAQ,aAAa,QAAQ,SAAS;IAExD,MAAM,CAAC,iBAAiB,mBADV,aAAa;IAE3B,MAAM,UAAU,YAAY;IAC5B,MAAM,WAAW,iBAAiB;IAElC,IACE,YACA,SAAS,YAAY,WACrB,SAAS,cAAc,mBACvB,SAAS,YAAY,SACrB;KACA,aAAa,KAAK,QAAQ;KAC1B;IACF;IAEA,IAAI,UAAU,UACZ,SAAS,QAAQ,oBAAoB,SAAS,WAAW,SAAS,QAAQ;IAG5E,IAAI,eAAe,OAAO,GAAG;KAC3B,MAAM,YAAY,UAAiB;MACjC,IAAI,OAAO,YAAY,YACrB,QAAQ,KAAK;WAEb,QAAQ,YAAY,KAAK;KAE7B;KACA,QAAQ,iBAAiB,iBAAiB,QAAQ;KAClD,aAAa,KAAK;MAChB;MACA,WAAW;MACX;MACA;KACF,CAAC;IACH,OACE,aAAa,KAAK;KAChB;KACA,WAAW;KACX;IACF,CAAC;GAEL;GAIA,KAAK,IAAI,QAAQ,aAAa,QAAQ,QAAQ,iBAAiB,QAAQ,SAAS;IAC9E,MAAM,QAAQ,iBAAiB;IAC/B,IAAI,MAAM,UACR,MAAM,QAAQ,oBAAoB,MAAM,WAAW,MAAM,QAAQ;GAErE;GAEA,iBAAiB,UAAU;EAC7B,CAAC;EAGD,MAAM,gBAAgB;GACpB,aAAa;IACX,KAAK,MAAM,WAAW,iBAAiB,SACrC,IAAI,QAAQ,UACV,QAAQ,QAAQ,oBAAoB,QAAQ,WAAW,QAAQ,QAAQ;IAG3E,iBAAiB,UAAU,CAAC;GAC9B;EACF,GAAG,CAAC,CAAC;EAKL,MAAM,WAAoC,EAAE,KAAK,UAAU;EAC3D,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,QAAQ,YACV,SAAS,OAAO;OACX,IAAI,QAAQ,aACjB,SAAS,QAAQ;OACZ,IAAI,QAAQ,WACjB,SAAS,MAAM;OACV,IAAI,CAAC,eAAe,IAAI,GAAG,KAAK,EAAE,OAAO,SAC9C,SAAS,OAAO;EAIpB,OAAO,MAAM,cAAc,SAAS,QAAQ;CAC9C,CACF;CAEA,OAAO,cAAc,aAAa,QAAQ;CAC1C,OAAO;AACT;;;;;;;;;;ACxWA,SAAgB,oBACd,SACA,oBACM;CACN,IAAI,OAAO,mBAAmB,aAAa;CAC3C,IAAI,eAAe,IAAI,OAAO,GAAG;CACjC,eAAe,OAAO,SAAS,kBAAkB;AACnD;;;;;;;;;;;;;;;;;;;;;;;ACkBA,IAAa,qBAAb,cAAwC,yBAAyB;;;EAqIvD,KAAA,QAAA;EAMF,KAAA,MAAA;EAMA,KAAA,MAAA;EAMC,KAAA,OAAA;EAMI,KAAA,WAAA;EASO,KAAA,UAAA;EAKM,KAAA,gBAAA;EAsMR,KAAA,gBAAA,MAAmB;GACjC,EAAE,gBAAgB;GAClB,MAAM,SAAS,EAAE;GACjB,MAAM,SAAS,WAAW,OAAO,KAAK;GACtC,KAAK,QAAQ,OAAO,MAAM,MAAM,IAAK,OAAO,QAAgB;GAC5D,KAAK,eAAe;GACpB,KAAK,YAAY,OAAO;EAC1B;EAKiB,KAAA,iBAAA,MAAmB;GAClC,EAAE,gBAAgB;GAClB,MAAM,SAAS,EAAE;GACjB,MAAM,SAAS,WAAW,OAAO,KAAK;GACtC,KAAK,QAAQ,OAAO,MAAM,MAAM,IAAK,OAAO,QAAgB;GAC5D,KAAK,eAAe;GACpB,KAAK,YAAY,QAAQ;EAC3B;;;EAnYyB,KAAA,SAAA,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0K5B;CAEA,oBAAmC;EACjC,MAAM,kBAAkB;EACxB,KAAK,gBAAgB,KAAK,gBAAgB,KAAK;EAC/C,KAAK,eAAe;CACtB;CAEA,QAAiB,mBAA+C;EAC9D,MAAM,QAAQ,iBAAiB;EAE/B,IAAI,kBAAkB,IAAI,cAAc,KAAK,KAAK,iBAAiB,KAAA,GACjE,KAAK,gBAAgB,KAAK;EAG5B,IACE,kBAAkB,IAAI,OAAO,KAC7B,kBAAkB,IAAI,KAAK,KAC3B,kBAAkB,IAAI,KAAK,KAC3B,kBAAkB,IAAI,MAAM,KAC5B,kBAAkB,IAAI,UAAU,GAEhC,KAAK,eAAe;CAExB;;;;CAKA,mBAAmB,KAAa,KAAa,MAAuB;EAClE,IAAI,QAAQ,KAAK,OAAO,MAAM,GAAG,GAAG,OAAO;EAC3C,MAAM,YAAY,MAAM,OAAO;EAE/B,OAAO,KAAK,IAAI,WADA,KAAK,MAAM,QACM,CAAC,IAAI;CACxC;;;;;CAMA,aAAa,KAAsB;EACjC,IAAI,OAAO,QAAQ,UAAU;GAC3B,IAAI,OAAO,MAAM,GAAG,GAAG,OAAO;GAC9B,IAAI,KAAK,IAAI,GAAG,IAAI,MAAM,OAAO;GACjC,MAAM,MAAM,OAAO,GAAG;GACtB,IAAI,IAAI,SAAS,IAAI,GACnB,OAAO,IAAI,QAAQ,EAAE,CAAC,CAAC,QAAQ,UAAU,EAAE;GAE7C,OAAO;EACT;EACA,OAAO,OAAO,OAAO,EAAE;CACzB;;;;CAKA,cAAc,GAAG,SAA2B;EAC1C,IAAI,cAAc;EAClB,KAAK,MAAM,KAAK,SAAS;GACvB,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG;GACzB,MAAM,MAAM,EAAE,SAAS;GACvB,MAAM,MAAM,IAAI,QAAQ,GAAG;GAC3B,IAAI,QAAQ,IACV,cAAc,KAAK,IAAI,aAAa,IAAI,SAAS,MAAM,CAAC;EAE5D;EACA,OAAO,KAAK,IAAI,IAAI,WAAW;CACjC;;;;CAKA,iBAAuB;EAErB,KAAK,aAAa,KAAK,aAAa,KAAK,KAAK,CAAC;EAG/C,MAAM,QAAiC,CAAC;EACxC,IAAI,UAAU;EAEd,MAAM,SAAkB,KAAK;EAC7B,MAAM,UAAU,OAAO,WAAW,YAAY,OAAO,KAAK,MAAM;EAChE,MAAM,MAAM,UAAU,MAAa,OAAO,KAAK,KAAK;EAEpD,IAAI,WAAW,OAAO,MAAM,GAAG,GAAG;GAChC,MAAM,WAAW;GACjB,UAAU;EACZ,OAAO,IAAI,MAAM,KAAK,KAAK;GACzB,MAAM,iBAAiB;GACvB,UAAU,0CAA0C,KAAK,IAAI;EAC/D,OAAO,IAAI,MAAM,KAAK,KAAK;GACzB,MAAM,gBAAgB;GACtB,UAAU,uCAAuC,KAAK,IAAI;EAC5D,OAAO,IAAI,KAAK,mBAAmB,KAAK,KAAK,KAAK,KAAK,IAAI,GAAG;GAC5D,MAAM,eAAe;GACrB,UAAU,iCAAiC,KAAK,KAAK,QAAQ,KAAK,IAAI;EACxE;EAEA,MAAM,SACJ,KAAK,WACJ,KAAK,YAAY,cAAc,OAAO;EACzC,KAAK,YAAY,OAAO,SAAS,MAAM;CACzC;;;;CAKA,YAAY,aAAgD;EAC1D,MAAM,SAAoC,EAAE,OAAO,KAAK,MAAM;EAE9D,IAAI,gBAAgB,WAAW,gBAAgB,QAC7C,KAAK,cACH,IAAI,YAAuC,SAAS;GAClD,SAAS;GACT,UAAU;GACV;EACF,CAAC,CACH;EAGF,IAAI,gBAAgB,YAAY,gBAAgB,QAAQ;GACtD,KAAK,cACH,IAAI,YAAuC,UAAU;IACnD,SAAS;IACT,UAAU;IACV;GACF,CAAC,CACH;GACA,KAAK,cACH,IAAI,YAAuC,mBAAmB;IAC5D,SAAS;IACT,UAAU;IACV;GACF,CAAC,CACH;EACF;CACF;;;;;CAMA,YAAyB;EACvB,IAAI,KAAK,YAAY,KAAK,UAAU;EACpC,MAAM,UAAU,OAAO,KAAK,KAAK;EACjC,IAAI,OAAO,MAAM,OAAO,GAAG;EAE3B,MAAM,YAAY,KAAK,cAAc,KAAK,MAAM,SAAS,KAAK,GAAG;EACjE,MAAM,OAAO,QAAQ,UAAU,KAAK,KAAA,CAAM,QAAQ,SAAS,CAAC;EAC5D,IAAI,OAAO,KAAK,MAAM,MAAM;EAE5B,MAAM,UAAU,KAAK,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;EACrF,KAAK,QAAQ,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI;EAC5C,KAAK,eAAe;EACpB,KAAK,cAAc;EACnB,KAAK,YAAY,MAAM;CACzB;;;;;CAMA,YAAyB;EACvB,IAAI,KAAK,YAAY,KAAK,UAAU;EACpC,MAAM,UAAU,OAAO,KAAK,KAAK;EACjC,IAAI,OAAO,MAAM,OAAO,GAAG;EAE3B,MAAM,YAAY,KAAK,cAAc,KAAK,MAAM,SAAS,KAAK,GAAG;EACjE,MAAM,OAAO,QAAQ,UAAU,KAAK,KAAA,CAAM,QAAQ,SAAS,CAAC;EAC5D,IAAI,OAAO,KAAK,MAAM,MAAM;EAE5B,MAAM,UAAU,KAAK,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;EACrF,KAAK,QAAQ,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI;EAC5C,KAAK,eAAe;EACpB,KAAK,cAAc;EACnB,KAAK,YAAY,MAAM;CACzB;;;;;;;CAQA,SAAgB,KAAa,SAA8C;EACzE,MAAM,MAAM,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG;EACtD,KAAK,QAAQ;EACb,KAAK,eAAe;EACpB,KAAK,cAAc;EAEnB,IAAI,SAAS,gBACX,KAAK,YAAY,MAAM;CAE3B;;;;CAKA;;;;CAYA;;;;;CAaA,cAAuC;EACrC,KAAK,QAAQ,KAAK,gBAAgB,KAAK;EACvC,KAAK,eAAe;EACpB,KAAK,cAAc;CACrB;CAEA,SAAkB;EAChB,MAAM,YAAY,CAAC,KAAK,SAAS;EACjC,MAAM,UAAU,OAAO,KAAK,KAAK;EACjC,MAAM,gBACJ,KAAK,YACL,KAAK,YACL,OAAO,MAAM,OAAO,KACpB,WAAW,KAAK,MAAM;EACxB,MAAM,gBACJ,KAAK,YACL,KAAK,YACL,OAAO,MAAM,OAAO,KACpB,WAAW,KAAK,MAAM;EAExB,OAAO,IAAI;;yBAEU,KAAK,UAAU,YAAY,GAAG,GAAG,YAAY,YAAY,GAAG;;qBAEhE,KAAK,aAAa,YAAY,KAAK,mBAAmB;;;;;;sBAMrD,cAAc;mBACjB,KAAK,UAAU;;;;;;;mBAOf,KAAK,aAAa,KAAK,KAAK,EAAE;gBACjC,KAAK,IAAI;gBACT,KAAK,IAAI;iBACR,KAAK,KAAK;sBACL,KAAK,SAAS;sBACd,KAAK,SAAS;mBACjB,KAAK,aAAa;oBACjB,KAAK,cAAc;yBACd;GACb,KAAK,UAAU;EACjB,EAAE;wBACY;GACZ,KAAK,UAAU;EACjB,EAAE;;;;;;;sBAOU,cAAc;mBACjB,KAAK,UAAU;;;;;;CAMhC;AACF;AAxUG,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,mBAAA,WAAA,SAAA,KAAA,CAAA;AAMzB,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,mBAAA,WAAA,OAAA,KAAA,CAAA;AAMzB,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,mBAAA,WAAA,OAAA,KAAA,CAAA;AAMzB,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,mBAAA,WAAA,QAAA,KAAA,CAAA;AAMzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,mBAAA,WAAA,YAAA,KAAA,CAAA;AAMzC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAgB,CAAC,CAAA,GAAA,mBAAA,WAAA,gBAAA,KAAA,CAAA;AAGrD,WAAA,CAAA,MAAM,CAAA,GAAA,mBAAA,WAAA,WAAA,KAAA,CAAA;AAGN,WAAA,CAAA,MAAM,OAAO,CAAA,GAAA,mBAAA,WAAA,WAAA,KAAA,CAAA;AA4ShB,oBAAoB,wBAAwB,kBAAkB;;;;;;;;;;;;;;;;;ACnd9D,MAAa,mBACX,mBAAwB,MAAM;;;;;;;AAgBhC,SAAgB,yBAAyB,OAAwB;CAC/D,IAAI,OAAO,UAAU,UACnB,OAAO,OAAO,SAAS,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC,IAAI;CAEnE,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,MAAM;EACZ,IAAI,OAAO,IAAI,eAAe,YAAY,OAAO,SAAS,IAAI,UAAU,GACtE,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,UAAU,CAAC;EAE/C,IAAI,OAAO,IAAI,UAAU,YAAY,OAAO,SAAS,IAAI,KAAK,GAC5D,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,KAAK,CAAC;EAE1C,IAAI,OAAO,IAAI,UAAU,YAAY,OAAO,SAAS,IAAI,KAAK,GAC5D,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,KAAK,CAAC;EAE1C,IAAI,MAAM,QAAQ,IAAI,KAAK,GACzB,OAAO,IAAI,MAAM,QAAQ,KAAa,SAAc;GAClD,MAAM,MACJ,OAAO,SAAS,WACZ,OACA,OAAO,MAAM,aAAa,WAC1B,KAAK,WACL;GACN,OAAO,OAAO,OAAO,SAAS,GAAG,IAAI,KAAK,IAAI,GAAG,GAAG,IAAI;EAC1D,GAAG,CAAC;CAER;CACA,OAAO;AACT;;;;;;;;;AAUA,IAAa,eAAb,cAAkC,WAAW;;EAClB,KAAA,SAAA,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgO5B;CACA;CACA;CACA;CAEA;CAEA,cAAc;EACZ,MAAM;EAxDoB,KAAA,QAAA,KAAA;EAMjB,KAAA,WAAA;EAMA,KAAA,WAAA;EAMH,KAAA,QAAA;EAkBG,KAAA,WAAA;EAM8C,KAAA,UAAA;EAGrC,KAAA,YAAA;EAK0C,KAAA,yBAAA;EACN,KAAA,0BAAA;EAgHzC,KAAA,eAAA,YAA2B;GACxC,IAAI,KAAK,YAAY,KAAK,WAAW;GAGrC,KAAK,cACH,IAAI,YAAuC,oBAAoB;IAC7D,SAAS;IACT,UAAU;IACV,QAAQ;KACN,OAAO,KAAK;KACZ,WAAW,KAAK;IAClB;GACF,CAAC,CACH;GAGA,IAAI,KAAK,YACP,IAAI;IACF,IAAI,KAAK,yBACP,MAAM,KAAK,wBAAwB,SACjC,KAAK,YACL,KAAK,aACP;SACK,IAAI,KAAK,yBAAyB,UACvC,MAAM,KAAK,yBAAyB,SAClC,KAAK,YACL,KAAK,aACP;GAEJ,SAAS,OAAO;IACd,KAAK,cACH,IAAI,YAAY,qBAAqB;KACnC,SAAS;KACT,UAAU;KACV,QAAQ,EAAE,MAAM;IAClB,CAAC,CACH;GACF;EAEJ;EA/IE,KAAK,0BAA0B,IAAI,uBAAuB,MAAM;GAC9D,SAAS;GACT,WAAW,UACT,KAAK,WAAW,KAAK,SAAS,KAAK,IAAI,yBAAyB,KAAK;GACvE,aAAa,GAAG,MAAO,KAAK,aAAa,KAAK,WAAW,GAAG,CAAC,IAAI,OAAO,GAAG,GAAG,CAAC;EACjF,CAAC;EAED,KAAK,2BAA2B,IAAI,wBAAwB,MAAM,EAChE,SAAS,sBACX,CAAC;CACH;CAEA,WAAoB,mBAA+C;EACjE,MAAM,WAAW,iBAAiB;EAGlC,IAAI,kBAAkB,IAAI,OAAO,KAAK,kBAAkB,IAAI,UAAU,KAAK,kBAAkB,IAAI,YAAY,GAAG;GAC9G,IAAI,KAAK,wBAAwB;IAC/B,KAAK,uBAAuB,QAAQ;IACpC,KAAK,yBAAyB;GAChC;GAEA,IAAI,KAAK,OACP,KAAK,yBAAyB,IAAI,gBAAgB,MAAM,KAAK,OAAO;IAClE,WAAW,UACT,KAAK,WAAW,KAAK,SAAS,KAAK,IAAI,yBAAyB,KAAK;IACvE,YAAY,KAAK,cAAc,OAAO;GACxC,CAAC;EAEL;EAGA,IAAI,kBAAkB,IAAI,UAAU,GAAG;GACrC,IAAI,KAAK,UACP,KAAK,0BAA0B,IAAI,iBAAiB,MAAM,KAAK,QAAQ;QAEvE,KAAK,0BAA0B;EAEnC;EAEA,MAAM,eAAe,KAAK;EAC1B,IAAI,KAAK,mBAAmB,KAAA,KAAa,eAAe,KAAK,gBAC3D,KAAK,YAAY;EAEnB,KAAK,iBAAiB;CACxB;;;;CAKA,IAAI,QAAgB;EAClB,IAAI,KAAK,UAAU,KAAA,GACjB,OAAO,KAAK;EAEd,IAAI,KAAK,wBACP,OAAO,KAAK,uBAAuB;EAErC,IAAI,KAAK,OAAO;GACd,MAAM,MACJ,OAAO,KAAK,MAAM,aAAa,aAC3B,KAAK,MAAM,SAAS,IACpB,OAAO,KAAK,MAAM,gBAAgB,aAClC,KAAK,MAAM,YAAY,IACvB,KAAA;GACN,OAAO,KAAK,WAAW,KAAK,SAAS,GAAG,IAAI,yBAAyB,GAAG;EAC1E;EACA,IACE,KAAK,wBAAwB,cAC7B,KAAK,wBAAwB,UAAU,KAAA,GAEvC,OAAO,KAAK,wBAAwB;EAEtC,OAAO;CACT;CAEA,IAAI,MAAM,KAAyB;EACjC,MAAM,SAAS,KAAK;EACpB,IAAI,QAAQ,KAAA,KAAa,QAAQ,MAC/B,KAAK,QAAQ,KAAA;OAEb,KAAK,QAAQ,OAAO,SAAS,GAAG,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,CAAC,IAAI;EAErE,KAAK,cAAc,SAAS,MAAM;CACpC;;;;CAKA,IAAI,YAAqB;EACvB,IAAI,KAAK,yBACP,OAAO,KAAK,wBAAwB;EAEtC,OAAO,KAAK,yBAAyB;CACvC;CAEA,QAAiB,mBAA+C;EAC9D,MAAM,QAAQ,iBAAiB;EAE/B,MAAM,eAAe,KAAK;EAE1B,KAAK,gBAAgB,aAAa,eAAe,CAAC;EAClD,KAAK,gBAAgB,cAAc,iBAAiB,CAAC;CACvD;CAEA;CAyCA,SAAkB;EAChB,MAAM,UAAU,KAAK;EACrB,IAAI,KAAK,YAAY,WAAW,GAC9B,OAAO;EAGT,MAAM,UAAU,KAAK;EACrB,MAAM,eACJ,UAAU,KAAK,WAAW,GAAG,KAAK,SAAS,KAAK,OAAO,OAAO;EAEhE,OAAO,IAAI;;;;uBAIQ,KAAK,YAAY,UAAU,GAAG;oBACjC,KAAK,YAAY,QAAQ;iBAC5B,KAAK,aAAa;8BACL;GACpB,KAAK,YAAY;EACnB,EAAE;qBACW,KAAK,aAAa,YAAY,KAC3C,GAAG,KAAK,MAAM,IAAI,aAAa,QAAQ;;;;;iDAKE,KAAK,MAAM;2CACjB,aAAa;UAC9C,UACE,IAAI;;;;wBAKJ,QAAQ;;;CAGlB;AACF;AAxRG,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,aAAA,WAAA,SAAA,KAAA,CAAA;AAO7B,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,aAAA,WAAA,YAAA,KAAA,CAAA;AAM7B,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,aAAA,WAAA,YAAA,KAAA,CAAA;AAM7B,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,aAAA,WAAA,cAAA,KAAA,CAAA;AAM7B,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,aAAA,WAAA,gBAAA,KAAA,CAAA;AAM7B,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,aAAA,WAAA,iBAAA,KAAA,CAAA;AAM7B,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,aAAA,WAAA,SAAA,KAAA,CAAA;AAMzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAY,CAAC,CAAA,GAAA,aAAA,WAAA,YAAA,KAAA,CAAA;AAMjD,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;AAAY,CAAC,CAAA,GAAA,aAAA,WAAA,YAAA,KAAA,CAAA;AAMjE,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,aAAA,WAAA,SAAA,KAAA,CAAA;AAMzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAc,CAAC,CAAA,GAAA,aAAA,WAAA,cAAA,KAAA,CAAA;AAMnD,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,aAAA,WAAA,iBAAA,KAAA,CAAA;AAM7B,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,aAAA,WAAA,YAAA,KAAA,CAAA;AAMzC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,aAAA,WAAA,WAAA,KAAA,CAAA;AAGxC,WAAA,CAAA,MAAM,CAAA,GAAA,aAAA,WAAA,aAAA,KAAA,CAAA;AA8MT,oBAAoB,kBAAkB,YAAY"}
|
package/dist/ssr.cjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_hydrate = require("./hydrate.cjs");
|
|
3
|
+
let _lit_labs_ssr = require("@lit-labs/ssr");
|
|
4
|
+
let _lit_labs_ssr_lib_render_result_js = require("@lit-labs/ssr/lib/render-result.js");
|
|
5
|
+
// src/ssr.ts
|
|
6
|
+
const PROPERTY_NAME_PATTERN = /^[A-Za-z_$][\w$-]*$/;
|
|
7
|
+
function reflectedAttribute(element, propertyName, declaration) {
|
|
8
|
+
if (!declaration?.reflect || declaration.attribute === false) return null;
|
|
9
|
+
const attributeName = typeof declaration.attribute === "string" ? declaration.attribute : propertyName.toLowerCase();
|
|
10
|
+
if (attributeName.length === 0) return null;
|
|
11
|
+
const value = element[propertyName];
|
|
12
|
+
if (value === void 0 || value === null) return null;
|
|
13
|
+
let attributeValue;
|
|
14
|
+
if (declaration.converter?.toAttribute) attributeValue = declaration.converter.toAttribute(value, declaration.type);
|
|
15
|
+
else if (declaration.type === Boolean) attributeValue = value ? "" : null;
|
|
16
|
+
else attributeValue = String(value);
|
|
17
|
+
return attributeValue === null ? null : {
|
|
18
|
+
name: attributeName,
|
|
19
|
+
value: attributeValue
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function isTemplateResult(value) {
|
|
23
|
+
return typeof value === "object" && value !== null && "_$litType$" in value;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Collects a Lit SSR render result synchronously.
|
|
27
|
+
*
|
|
28
|
+
* @throws When the template contains a value that resolves asynchronously
|
|
29
|
+
* (for example an asynchronous element renderer). Use
|
|
30
|
+
* {@link renderLitTemplateAsync} instead.
|
|
31
|
+
*/
|
|
32
|
+
function renderLitTemplate(template) {
|
|
33
|
+
if (!isTemplateResult(template)) throw new TypeError("renderLitTemplate() expects a Lit TemplateResult.");
|
|
34
|
+
return (0, _lit_labs_ssr_lib_render_result_js.collectResultSync)((0, _lit_labs_ssr.render)(template));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Collects a Lit SSR render result, awaiting asynchronous renderer chunks.
|
|
38
|
+
*/
|
|
39
|
+
async function renderLitTemplateAsync(template) {
|
|
40
|
+
if (!isTemplateResult(template)) throw new TypeError("renderLitTemplateAsync() expects a Lit TemplateResult.");
|
|
41
|
+
return (0, _lit_labs_ssr_lib_render_result_js.collectResult)((0, _lit_labs_ssr.render)(template));
|
|
42
|
+
}
|
|
43
|
+
function escapeAttributeValue(value) {
|
|
44
|
+
return value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a `TemplateResult` that reproduces an element instance's public
|
|
48
|
+
* attributes and reactive properties.
|
|
49
|
+
*
|
|
50
|
+
* Lit does not accept a raw `LitElement` instance in `render()` (it would be
|
|
51
|
+
* stringified). Constructing a template with `.property=${value}` bindings lets
|
|
52
|
+
* Lit's ElementRenderer create its own server-side instance with the same input.
|
|
53
|
+
*/
|
|
54
|
+
function createElementTemplate(element, options) {
|
|
55
|
+
const tagName = element.localName;
|
|
56
|
+
if (!tagName) throw new Error("renderLitElement() requires a registered custom element. Call customElements.define() before rendering.");
|
|
57
|
+
if (!globalThis.customElements?.get(tagName)) throw new Error(`renderLitElement(): custom element "${tagName}" is not registered. Call customElements.define() before rendering.`);
|
|
58
|
+
const strings = [`<${tagName}`];
|
|
59
|
+
const values = [];
|
|
60
|
+
if (options?.includeAttributes !== false) for (const attribute of Array.from(element.attributes)) strings[strings.length - 1] += ` ${attribute.name}="${escapeAttributeValue(attribute.value)}"`;
|
|
61
|
+
const elementProperties = element.constructor.elementProperties;
|
|
62
|
+
const propertyNames = options?.properties ?? (elementProperties ? Array.from(elementProperties.keys(), String) : []);
|
|
63
|
+
const emittedAttributeNames = new Set(Array.from(element.attributes, (attribute) => attribute.name));
|
|
64
|
+
const seen = /* @__PURE__ */ new Set();
|
|
65
|
+
for (const propertyName of propertyNames) {
|
|
66
|
+
if (seen.has(propertyName) || !PROPERTY_NAME_PATTERN.test(propertyName)) continue;
|
|
67
|
+
seen.add(propertyName);
|
|
68
|
+
const reflected = reflectedAttribute(element, propertyName, elementProperties?.get(propertyName));
|
|
69
|
+
if (reflected && !emittedAttributeNames.has(reflected.name)) {
|
|
70
|
+
emittedAttributeNames.add(reflected.name);
|
|
71
|
+
strings[strings.length - 1] += ` ${reflected.name}="${escapeAttributeValue(reflected.value)}"`;
|
|
72
|
+
}
|
|
73
|
+
strings[strings.length - 1] += ` .${propertyName}="`;
|
|
74
|
+
values.push(element[propertyName]);
|
|
75
|
+
strings.push("\"");
|
|
76
|
+
}
|
|
77
|
+
strings[strings.length - 1] += `>`;
|
|
78
|
+
if (options?.includeLightDom !== false) strings[strings.length - 1] += element.innerHTML;
|
|
79
|
+
strings[strings.length - 1] += `</${tagName}>`;
|
|
80
|
+
const templateStrings = strings;
|
|
81
|
+
Object.defineProperty(templateStrings, "raw", {
|
|
82
|
+
configurable: false,
|
|
83
|
+
enumerable: false,
|
|
84
|
+
value: [...strings]
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
"_$litType$": 1,
|
|
88
|
+
strings: templateStrings,
|
|
89
|
+
values
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Renders a Lit Element instance to server markup, including its declarative
|
|
94
|
+
* shadow root and adopted styles.
|
|
95
|
+
*
|
|
96
|
+
* The element must already be registered with `customElements.define()`.
|
|
97
|
+
* Reactive properties are serialized as Lit property bindings so complex
|
|
98
|
+
* values survive SSR without attribute stringification.
|
|
99
|
+
*/
|
|
100
|
+
function renderLitElement(element, options) {
|
|
101
|
+
return renderLitTemplate(createElementTemplate(element, options));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Asynchronous variant of {@link renderLitElement} for custom element
|
|
105
|
+
* renderers that yield asynchronous chunks.
|
|
106
|
+
*/
|
|
107
|
+
async function renderLitElementAsync(element, options) {
|
|
108
|
+
return renderLitTemplateAsync(createElementTemplate(element, options));
|
|
109
|
+
}
|
|
110
|
+
exports.hydrateLit = require_hydrate.hydrateLit;
|
|
111
|
+
exports.renderLitElement = renderLitElement;
|
|
112
|
+
exports.renderLitElementAsync = renderLitElementAsync;
|
|
113
|
+
exports.renderLitTemplate = renderLitTemplate;
|
|
114
|
+
exports.renderLitTemplateAsync = renderLitTemplateAsync;
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=ssr.cjs.map
|
package/dist/ssr.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr.cjs","names":["collectResultSync","render","collectResult"],"sources":["../src/ssr.ts"],"sourcesContent":["/**\n * @fileoverview Minimal server-side rendering and client hydration entry for\n * Lit Elements and Lit templates (`@context-action/lit-ui/ssr`).\n *\n * The module is safe to import in Node, Edge, and Worker environments: it does\n * not read `window` or `document` at module evaluation time. Rendering uses\n * Lit's own SSR renderer, so `part`, `data-*`, declarative shadow roots, and\n * imported styles are preserved in the produced markup.\n *\n * @implements view-layer\n * @implements web-component-primitive\n * @memberof core-concepts\n */\n\nimport type { LitElement, TemplateResult } from 'lit';\nimport { render } from '@lit-labs/ssr';\nimport { collectResult, collectResultSync } from '@lit-labs/ssr/lib/render-result.js';\n\nexport { hydrateLit } from './hydrate.js';\n\n/**\n * Options for {@link renderLitElement}.\n */\nexport interface RenderLitElementOptions {\n /**\n * Explicit reactive property names to serialize as Lit property bindings.\n * Defaults to the element's `static elementProperties` map.\n */\n properties?: readonly string[];\n\n /**\n * Copy the element's current HTML attributes into the server markup.\n * Defaults to `true`.\n */\n includeAttributes?: boolean;\n\n /**\n * Serialize the element's current light DOM children inside the host tag so\n * `<slot>` projection survives SSR. Defaults to `true`.\n *\n * The light DOM is treated as trusted server input; complex properties on\n * nested custom elements must still be supplied through their own SSR path.\n */\n includeLightDom?: boolean;\n}\n\nconst PROPERTY_NAME_PATTERN = /^[A-Za-z_$][\\w$-]*$/;\n\ninterface ReflectivePropertyDeclaration {\n readonly attribute?: string | boolean;\n readonly reflect?: boolean;\n readonly type?: unknown;\n readonly converter?: {\n toAttribute?(value: unknown, type?: unknown): string | null;\n };\n}\n\nfunction reflectedAttribute(\n element: LitElement,\n propertyName: string,\n declaration: ReflectivePropertyDeclaration | undefined,\n): { name: string; value: string } | null {\n if (!declaration?.reflect || declaration.attribute === false) return null;\n\n const attributeName =\n typeof declaration.attribute === 'string'\n ? declaration.attribute\n : propertyName.toLowerCase();\n if (attributeName.length === 0) return null;\n\n const value = (element as unknown as Record<string, unknown>)[propertyName];\n if (value === undefined || value === null) return null;\n\n let attributeValue: string | null;\n if (declaration.converter?.toAttribute) {\n attributeValue = declaration.converter.toAttribute(value, declaration.type);\n } else if (declaration.type === Boolean) {\n attributeValue = value ? '' : null;\n } else {\n attributeValue = String(value);\n }\n\n return attributeValue === null\n ? null\n : { name: attributeName, value: attributeValue };\n}\n\nfunction isTemplateResult(value: unknown): value is TemplateResult {\n return (\n typeof value === 'object' &&\n value !== null &&\n '_$litType$' in (value as Record<PropertyKey, unknown>)\n );\n}\n\n/**\n * Collects a Lit SSR render result synchronously.\n *\n * @throws When the template contains a value that resolves asynchronously\n * (for example an asynchronous element renderer). Use\n * {@link renderLitTemplateAsync} instead.\n */\nexport function renderLitTemplate(template: TemplateResult): string {\n if (!isTemplateResult(template)) {\n throw new TypeError('renderLitTemplate() expects a Lit TemplateResult.');\n }\n return collectResultSync(render(template));\n}\n\n/**\n * Collects a Lit SSR render result, awaiting asynchronous renderer chunks.\n */\nexport async function renderLitTemplateAsync(template: TemplateResult): Promise<string> {\n if (!isTemplateResult(template)) {\n throw new TypeError('renderLitTemplateAsync() expects a Lit TemplateResult.');\n }\n return collectResult(render(template));\n}\n\nfunction escapeAttributeValue(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('\"', '"')\n .replaceAll('<', '<')\n .replaceAll('>', '>');\n}\n\n/**\n * Creates a `TemplateResult` that reproduces an element instance's public\n * attributes and reactive properties.\n *\n * Lit does not accept a raw `LitElement` instance in `render()` (it would be\n * stringified). Constructing a template with `.property=${value}` bindings lets\n * Lit's ElementRenderer create its own server-side instance with the same input.\n */\nfunction createElementTemplate(\n element: LitElement,\n options?: RenderLitElementOptions,\n): TemplateResult {\n const tagName = element.localName;\n if (!tagName) {\n throw new Error(\n 'renderLitElement() requires a registered custom element. Call customElements.define() before rendering.',\n );\n }\n\n const registry = globalThis.customElements;\n if (!registry?.get(tagName)) {\n throw new Error(\n `renderLitElement(): custom element \"${tagName}\" is not registered. Call customElements.define() before rendering.`,\n );\n }\n\n const strings: string[] = [`<${tagName}`];\n const values: unknown[] = [];\n\n if (options?.includeAttributes !== false) {\n for (const attribute of Array.from(element.attributes)) {\n strings[strings.length - 1] += ` ${attribute.name}=\"${escapeAttributeValue(attribute.value)}\"`;\n }\n }\n\n const elementConstructor = element.constructor as unknown as {\n elementProperties?: Map<PropertyKey, ReflectivePropertyDeclaration>;\n };\n const elementProperties = elementConstructor.elementProperties;\n const propertyNames =\n options?.properties ??\n (elementProperties ? Array.from(elementProperties.keys(), String) : []);\n\n const emittedAttributeNames = new Set(\n Array.from(element.attributes, attribute => attribute.name),\n );\n const seen = new Set<string>();\n for (const propertyName of propertyNames) {\n if (seen.has(propertyName) || !PROPERTY_NAME_PATTERN.test(propertyName)) continue;\n seen.add(propertyName);\n\n const reflected = reflectedAttribute(\n element,\n propertyName,\n elementProperties?.get(propertyName),\n );\n if (reflected && !emittedAttributeNames.has(reflected.name)) {\n emittedAttributeNames.add(reflected.name);\n strings[strings.length - 1] +=\n ` ${reflected.name}=\"${escapeAttributeValue(reflected.value)}\"`;\n }\n\n strings[strings.length - 1] += ` .${propertyName}=\"`;\n values.push((element as unknown as Record<string, unknown>)[propertyName]);\n strings.push('\"');\n }\n\n strings[strings.length - 1] += `>`;\n if (options?.includeLightDom !== false) {\n strings[strings.length - 1] += element.innerHTML;\n }\n strings[strings.length - 1] += `</${tagName}>`;\n\n const templateStrings = strings as unknown as TemplateStringsArray;\n Object.defineProperty(templateStrings, 'raw', {\n configurable: false,\n enumerable: false,\n value: [...strings],\n });\n\n // Lit has no public API for constructing a template with dynamically named\n // property bindings. This shape is covered by the SSR unit and browser\n // hydration contracts so a Lit major upgrade fails loudly.\n return {\n '_$litType$': 1,\n strings: templateStrings,\n values,\n } as unknown as TemplateResult;\n}\n\n/**\n * Renders a Lit Element instance to server markup, including its declarative\n * shadow root and adopted styles.\n *\n * The element must already be registered with `customElements.define()`.\n * Reactive properties are serialized as Lit property bindings so complex\n * values survive SSR without attribute stringification.\n */\nexport function renderLitElement(\n element: LitElement,\n options?: RenderLitElementOptions,\n): string {\n return renderLitTemplate(createElementTemplate(element, options));\n}\n\n/**\n * Asynchronous variant of {@link renderLitElement} for custom element\n * renderers that yield asynchronous chunks.\n */\nexport async function renderLitElementAsync(\n element: LitElement,\n options?: RenderLitElementOptions,\n): Promise<string> {\n return renderLitTemplateAsync(createElementTemplate(element, options));\n}\n"],"mappings":"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"}
|
package/dist/ssr.d.cts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { hydrateLit } from "./hydrate.cjs";
|
|
2
|
+
import { LitElement, TemplateResult } from "lit";
|
|
3
|
+
/**
|
|
4
|
+
* Options for {@link renderLitElement}.
|
|
5
|
+
*/
|
|
6
|
+
export interface RenderLitElementOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Explicit reactive property names to serialize as Lit property bindings.
|
|
9
|
+
* Defaults to the element's `static elementProperties` map.
|
|
10
|
+
*/
|
|
11
|
+
properties?: readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Copy the element's current HTML attributes into the server markup.
|
|
14
|
+
* Defaults to `true`.
|
|
15
|
+
*/
|
|
16
|
+
includeAttributes?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Serialize the element's current light DOM children inside the host tag so
|
|
19
|
+
* `<slot>` projection survives SSR. Defaults to `true`.
|
|
20
|
+
*
|
|
21
|
+
* The light DOM is treated as trusted server input; complex properties on
|
|
22
|
+
* nested custom elements must still be supplied through their own SSR path.
|
|
23
|
+
*/
|
|
24
|
+
includeLightDom?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Collects a Lit SSR render result synchronously.
|
|
28
|
+
*
|
|
29
|
+
* @throws When the template contains a value that resolves asynchronously
|
|
30
|
+
* (for example an asynchronous element renderer). Use
|
|
31
|
+
* {@link renderLitTemplateAsync} instead.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderLitTemplate(template: TemplateResult): string;
|
|
34
|
+
/**
|
|
35
|
+
* Collects a Lit SSR render result, awaiting asynchronous renderer chunks.
|
|
36
|
+
*/
|
|
37
|
+
export declare function renderLitTemplateAsync(template: TemplateResult): Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Renders a Lit Element instance to server markup, including its declarative
|
|
40
|
+
* shadow root and adopted styles.
|
|
41
|
+
*
|
|
42
|
+
* The element must already be registered with `customElements.define()`.
|
|
43
|
+
* Reactive properties are serialized as Lit property bindings so complex
|
|
44
|
+
* values survive SSR without attribute stringification.
|
|
45
|
+
*/
|
|
46
|
+
export declare function renderLitElement(element: LitElement, options?: RenderLitElementOptions): string;
|
|
47
|
+
/**
|
|
48
|
+
* Asynchronous variant of {@link renderLitElement} for custom element
|
|
49
|
+
* renderers that yield asynchronous chunks.
|
|
50
|
+
*/
|
|
51
|
+
export declare function renderLitElementAsync(element: LitElement, options?: RenderLitElementOptions): Promise<string>;
|
|
52
|
+
export { hydrateLit };
|
|
53
|
+
//# sourceMappingURL=ssr.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr.d.cts","names":[],"sources":["../src/ssr.ts"],"mappings":";;;;;iBAuBiB;;;;;EAKf;;;;;EAMA;;;;;;;;EASA;;;;;;;;;wBA2Dc,kBAAkB,UAAU;;;;wBAUtB,uBAAuB,UAAU,iBAAiB;;;;;;;;;wBAiHxD,iBACd,SAAS,YACT,UAAU;;;;;wBASU,sBACpB,SAAS,YACT,UAAU,0BACT"}
|
package/dist/ssr.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { hydrateLit } from "./hydrate.js";
|
|
2
|
+
import { LitElement, TemplateResult } from "lit";
|
|
3
|
+
/**
|
|
4
|
+
* Options for {@link renderLitElement}.
|
|
5
|
+
*/
|
|
6
|
+
export interface RenderLitElementOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Explicit reactive property names to serialize as Lit property bindings.
|
|
9
|
+
* Defaults to the element's `static elementProperties` map.
|
|
10
|
+
*/
|
|
11
|
+
properties?: readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Copy the element's current HTML attributes into the server markup.
|
|
14
|
+
* Defaults to `true`.
|
|
15
|
+
*/
|
|
16
|
+
includeAttributes?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Serialize the element's current light DOM children inside the host tag so
|
|
19
|
+
* `<slot>` projection survives SSR. Defaults to `true`.
|
|
20
|
+
*
|
|
21
|
+
* The light DOM is treated as trusted server input; complex properties on
|
|
22
|
+
* nested custom elements must still be supplied through their own SSR path.
|
|
23
|
+
*/
|
|
24
|
+
includeLightDom?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Collects a Lit SSR render result synchronously.
|
|
28
|
+
*
|
|
29
|
+
* @throws When the template contains a value that resolves asynchronously
|
|
30
|
+
* (for example an asynchronous element renderer). Use
|
|
31
|
+
* {@link renderLitTemplateAsync} instead.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderLitTemplate(template: TemplateResult): string;
|
|
34
|
+
/**
|
|
35
|
+
* Collects a Lit SSR render result, awaiting asynchronous renderer chunks.
|
|
36
|
+
*/
|
|
37
|
+
export declare function renderLitTemplateAsync(template: TemplateResult): Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Renders a Lit Element instance to server markup, including its declarative
|
|
40
|
+
* shadow root and adopted styles.
|
|
41
|
+
*
|
|
42
|
+
* The element must already be registered with `customElements.define()`.
|
|
43
|
+
* Reactive properties are serialized as Lit property bindings so complex
|
|
44
|
+
* values survive SSR without attribute stringification.
|
|
45
|
+
*/
|
|
46
|
+
export declare function renderLitElement(element: LitElement, options?: RenderLitElementOptions): string;
|
|
47
|
+
/**
|
|
48
|
+
* Asynchronous variant of {@link renderLitElement} for custom element
|
|
49
|
+
* renderers that yield asynchronous chunks.
|
|
50
|
+
*/
|
|
51
|
+
export declare function renderLitElementAsync(element: LitElement, options?: RenderLitElementOptions): Promise<string>;
|
|
52
|
+
export { hydrateLit };
|
|
53
|
+
//# sourceMappingURL=ssr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr.d.ts","names":[],"sources":["../src/ssr.ts"],"mappings":";;;;;iBAuBiB;;;;;EAKf;;;;;EAMA;;;;;;;;EASA;;;;;;;;;wBA2Dc,kBAAkB,UAAU;;;;wBAUtB,uBAAuB,UAAU,iBAAiB;;;;;;;;;wBAiHxD,iBACd,SAAS,YACT,UAAU;;;;;wBASU,sBACpB,SAAS,YACT,UAAU,0BACT"}
|
package/dist/ssr.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { hydrateLit } from "./hydrate.js";
|
|
2
|
+
import { render } from "@lit-labs/ssr";
|
|
3
|
+
import { collectResult, collectResultSync } from "@lit-labs/ssr/lib/render-result.js";
|
|
4
|
+
// src/ssr.ts
|
|
5
|
+
const PROPERTY_NAME_PATTERN = /^[A-Za-z_$][\w$-]*$/;
|
|
6
|
+
function reflectedAttribute(element, propertyName, declaration) {
|
|
7
|
+
if (!declaration?.reflect || declaration.attribute === false) return null;
|
|
8
|
+
const attributeName = typeof declaration.attribute === "string" ? declaration.attribute : propertyName.toLowerCase();
|
|
9
|
+
if (attributeName.length === 0) return null;
|
|
10
|
+
const value = element[propertyName];
|
|
11
|
+
if (value === void 0 || value === null) return null;
|
|
12
|
+
let attributeValue;
|
|
13
|
+
if (declaration.converter?.toAttribute) attributeValue = declaration.converter.toAttribute(value, declaration.type);
|
|
14
|
+
else if (declaration.type === Boolean) attributeValue = value ? "" : null;
|
|
15
|
+
else attributeValue = String(value);
|
|
16
|
+
return attributeValue === null ? null : {
|
|
17
|
+
name: attributeName,
|
|
18
|
+
value: attributeValue
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function isTemplateResult(value) {
|
|
22
|
+
return typeof value === "object" && value !== null && "_$litType$" in value;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Collects a Lit SSR render result synchronously.
|
|
26
|
+
*
|
|
27
|
+
* @throws When the template contains a value that resolves asynchronously
|
|
28
|
+
* (for example an asynchronous element renderer). Use
|
|
29
|
+
* {@link renderLitTemplateAsync} instead.
|
|
30
|
+
*/
|
|
31
|
+
function renderLitTemplate(template) {
|
|
32
|
+
if (!isTemplateResult(template)) throw new TypeError("renderLitTemplate() expects a Lit TemplateResult.");
|
|
33
|
+
return collectResultSync(render(template));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Collects a Lit SSR render result, awaiting asynchronous renderer chunks.
|
|
37
|
+
*/
|
|
38
|
+
async function renderLitTemplateAsync(template) {
|
|
39
|
+
if (!isTemplateResult(template)) throw new TypeError("renderLitTemplateAsync() expects a Lit TemplateResult.");
|
|
40
|
+
return collectResult(render(template));
|
|
41
|
+
}
|
|
42
|
+
function escapeAttributeValue(value) {
|
|
43
|
+
return value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Creates a `TemplateResult` that reproduces an element instance's public
|
|
47
|
+
* attributes and reactive properties.
|
|
48
|
+
*
|
|
49
|
+
* Lit does not accept a raw `LitElement` instance in `render()` (it would be
|
|
50
|
+
* stringified). Constructing a template with `.property=${value}` bindings lets
|
|
51
|
+
* Lit's ElementRenderer create its own server-side instance with the same input.
|
|
52
|
+
*/
|
|
53
|
+
function createElementTemplate(element, options) {
|
|
54
|
+
const tagName = element.localName;
|
|
55
|
+
if (!tagName) throw new Error("renderLitElement() requires a registered custom element. Call customElements.define() before rendering.");
|
|
56
|
+
if (!globalThis.customElements?.get(tagName)) throw new Error(`renderLitElement(): custom element "${tagName}" is not registered. Call customElements.define() before rendering.`);
|
|
57
|
+
const strings = [`<${tagName}`];
|
|
58
|
+
const values = [];
|
|
59
|
+
if (options?.includeAttributes !== false) for (const attribute of Array.from(element.attributes)) strings[strings.length - 1] += ` ${attribute.name}="${escapeAttributeValue(attribute.value)}"`;
|
|
60
|
+
const elementProperties = element.constructor.elementProperties;
|
|
61
|
+
const propertyNames = options?.properties ?? (elementProperties ? Array.from(elementProperties.keys(), String) : []);
|
|
62
|
+
const emittedAttributeNames = new Set(Array.from(element.attributes, (attribute) => attribute.name));
|
|
63
|
+
const seen = /* @__PURE__ */ new Set();
|
|
64
|
+
for (const propertyName of propertyNames) {
|
|
65
|
+
if (seen.has(propertyName) || !PROPERTY_NAME_PATTERN.test(propertyName)) continue;
|
|
66
|
+
seen.add(propertyName);
|
|
67
|
+
const reflected = reflectedAttribute(element, propertyName, elementProperties?.get(propertyName));
|
|
68
|
+
if (reflected && !emittedAttributeNames.has(reflected.name)) {
|
|
69
|
+
emittedAttributeNames.add(reflected.name);
|
|
70
|
+
strings[strings.length - 1] += ` ${reflected.name}="${escapeAttributeValue(reflected.value)}"`;
|
|
71
|
+
}
|
|
72
|
+
strings[strings.length - 1] += ` .${propertyName}="`;
|
|
73
|
+
values.push(element[propertyName]);
|
|
74
|
+
strings.push("\"");
|
|
75
|
+
}
|
|
76
|
+
strings[strings.length - 1] += `>`;
|
|
77
|
+
if (options?.includeLightDom !== false) strings[strings.length - 1] += element.innerHTML;
|
|
78
|
+
strings[strings.length - 1] += `</${tagName}>`;
|
|
79
|
+
const templateStrings = strings;
|
|
80
|
+
Object.defineProperty(templateStrings, "raw", {
|
|
81
|
+
configurable: false,
|
|
82
|
+
enumerable: false,
|
|
83
|
+
value: [...strings]
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
"_$litType$": 1,
|
|
87
|
+
strings: templateStrings,
|
|
88
|
+
values
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Renders a Lit Element instance to server markup, including its declarative
|
|
93
|
+
* shadow root and adopted styles.
|
|
94
|
+
*
|
|
95
|
+
* The element must already be registered with `customElements.define()`.
|
|
96
|
+
* Reactive properties are serialized as Lit property bindings so complex
|
|
97
|
+
* values survive SSR without attribute stringification.
|
|
98
|
+
*/
|
|
99
|
+
function renderLitElement(element, options) {
|
|
100
|
+
return renderLitTemplate(createElementTemplate(element, options));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Asynchronous variant of {@link renderLitElement} for custom element
|
|
104
|
+
* renderers that yield asynchronous chunks.
|
|
105
|
+
*/
|
|
106
|
+
async function renderLitElementAsync(element, options) {
|
|
107
|
+
return renderLitTemplateAsync(createElementTemplate(element, options));
|
|
108
|
+
}
|
|
109
|
+
export { hydrateLit, renderLitElement, renderLitElementAsync, renderLitTemplate, renderLitTemplateAsync };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=ssr.js.map
|