@wcstack/storage 1.11.1 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -299,8 +299,14 @@ function handleClick(event) {
299
299
  const storageId = triggerElement.getAttribute(config.triggerAttribute);
300
300
  if (!storageId)
301
301
  return;
302
+ // Resolve the registered constructor at call time instead of importing Storage
303
+ // as a value. The value import created a components/Storage.ts ⇄ autoTrigger.ts
304
+ // cycle (Storage.connectedCallback() calls registerAutoTrigger()). instanceof
305
+ // against the customElements registry keeps the exact same identity guarantee
306
+ // — only the registered <wcs-storage> class matches — without the import cycle.
307
+ const StorageCtor = customElements.get(config.tagNames.storage);
302
308
  const storageElement = document.getElementById(storageId);
303
- if (!storageElement || !(storageElement instanceof Storage))
309
+ if (!StorageCtor || !(storageElement instanceof StorageCtor))
304
310
  return;
305
311
  event.preventDefault();
306
312
  storageElement.save();
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/events.ts","../src/raiseError.ts","../src/core/StorageCore.ts","../src/autoTrigger.ts","../src/components/Storage.ts","../src/registerComponents.ts","../src/bootstrapStorage.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n storage: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-storagetarget\",\n tagNames: {\n storage: \"wcs-storage\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n // Validate each tagNames entry individually instead of a blanket\n // Object.assign: a non-string (e.g. { storage: undefined }) would otherwise\n // poison the config and make customElements.define(undefined, …) throw at\n // registration time. Mirrors the typeof guards on autoTrigger / triggerAttribute.\n for (const [key, value] of Object.entries(partialConfig.tagNames)) {\n if (typeof value === \"string\") {\n (_config.tagNames as Record<string, string>)[key] = value;\n }\n }\n }\n frozenConfig = null;\n}\n","// Single source of truth for the custom event names dispatched by StorageCore /\n// Storage. These names appear in two places that must stay in lock-step:\n// 1. the `wcBindable.properties[].event` declarations (consumed by bind())\n// 2. the `dispatchEvent(new CustomEvent(...))` calls that emit them\n// Hard-coding the same string literal in both places risks a silent typo that\n// makes bind() listen for an event no one ever fires. Referencing these\n// constants from both sites keeps them in sync.\nexport const STORAGE_EVENTS = {\n valueChanged: \"wcs-storage:value-changed\",\n loadingChanged: \"wcs-storage:loading-changed\",\n error: \"wcs-storage:error\",\n triggerChanged: \"wcs-storage:trigger-changed\",\n} as const;\n","export function raiseError(message: string): never {\n throw new Error(`[@wcstack/storage] ${message}`);\n}\n","import { raiseError } from \"../raiseError.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType, WcsStorageError } from \"../types.js\";\n\nexport class StorageCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: STORAGE_EVENTS.valueChanged, getter: (e: Event) => (e as CustomEvent).detail },\n { name: \"loading\", event: STORAGE_EVENTS.loadingChanged },\n { name: \"error\", event: STORAGE_EVENTS.error },\n ],\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n ],\n // load / save / remove are synchronous, so none carry the `async` hint.\n commands: [\n { name: \"load\" },\n { name: \"save\" },\n { name: \"remove\" },\n ],\n };\n\n private _target: EventTarget;\n private _value: any = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _key: string = \"\";\n private _type: StorageType = \"local\";\n private _storageListener: ((e: StorageEvent) => void) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get value(): any {\n return this._value;\n }\n\n // Set the current value *without* persisting it. Persistence happens only via\n // save() / remove() / a cross-tab storage event. This setter exists so the\n // Shell (manual mode) can stage a value handed in via a `value` binding and\n // then commit it later with save()/trigger. It mirrors the value to observers\n // through the same `value-changed` event load()/save() use (CSBC: a Core value\n // change is observable), but it deliberately does not touch storage.\n //\n // Same-value writes are skipped to break a potential feedback loop:\n // value-changed → state binding → value setter → value-changed → …\n set value(v: any) {\n if (Object.is(v, this._value)) return;\n this._setValue(v);\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get key(): string {\n return this._key;\n }\n\n set key(value: string) {\n // Defensive normalization for direct Core use (the Shell already passes a\n // string via `getAttribute(\"key\") || \"\"`). Coercing to String keeps a\n // non-string assignment from poisoning the cross-tab `e.key !== _key`\n // comparison; empty keys are still rejected at operation time.\n this._key = String(value);\n }\n\n get type(): StorageType {\n return this._type;\n }\n\n set type(value: StorageType) {\n if (value !== \"local\" && value !== \"session\") {\n raiseError(`Invalid storage type: \"${value}\". Must be \"local\" or \"session\".`);\n }\n this._type = value;\n }\n\n private _getStorage(): globalThis.Storage {\n return this._type === \"session\" ? sessionStorage : localStorage;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.loadingChanged, {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.error, {\n detail: error,\n bubbles: true,\n }));\n }\n\n // Wrap a caught storage exception into the documented WcsStorageError shape,\n // tagging it with the failing operation so consumers know which call failed.\n private _toStorageError(operation: WcsStorageError[\"operation\"], e: unknown): WcsStorageError {\n return {\n operation,\n message: e instanceof Error ? e.message : String(e),\n };\n }\n\n private _setValue(value: any): void {\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.valueChanged, {\n detail: value,\n bubbles: true,\n }));\n }\n\n load(): any {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n const raw = storage.getItem(this._key);\n\n if (raw === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(raw));\n } catch {\n this._setValue(raw);\n }\n }\n\n this._setLoading(false);\n return this._value;\n } catch (e: any) {\n this._setError(this._toStorageError(\"load\", e));\n this._setLoading(false);\n return null;\n }\n }\n\n save(value: any): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n\n if (value === null || value === undefined) {\n storage.removeItem(this._key);\n // Normalize the removed value to null (matching remove() and load() of a\n // missing key) so saving `undefined` does not leave the getter returning\n // `undefined`. README's serialization table documents null/undefined as\n // \"null\" on read-back.\n this._setValue(null);\n } else if (typeof value === \"string\") {\n storage.setItem(this._key, value);\n this._setValue(value);\n } else {\n storage.setItem(this._key, JSON.stringify(value));\n this._setValue(value);\n }\n\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"save\", e));\n this._setLoading(false);\n }\n }\n\n remove(): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n storage.removeItem(this._key);\n this._setValue(null);\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"remove\", e));\n this._setLoading(false);\n }\n }\n\n startSync(): void {\n if (this._storageListener) return;\n\n this._storageListener = (e: StorageEvent) => {\n if (e.key !== this._key) return;\n if (this._type === \"session\") return;\n\n // A fresh value arriving from another tab supersedes any stale error from\n // a prior failed load/save/remove. Clearing it here keeps the sync path\n // consistent with load()/save()/remove(), which all reset error to null at\n // the start of a successful operation — otherwise an \"error present + fresh\n // value\" inconsistency could persist after a cross-tab update.\n this._setError(null);\n\n if (e.newValue === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(e.newValue));\n } catch {\n this._setValue(e.newValue);\n }\n }\n };\n\n globalThis.addEventListener(\"storage\", this._storageListener);\n }\n\n stopSync(): void {\n if (!this._storageListener) return;\n globalThis.removeEventListener(\"storage\", this._storageListener);\n this._storageListener = null;\n }\n}\n","import { config } from \"./config.js\";\nimport { Storage } from \"./components/Storage.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const storageId = triggerElement.getAttribute(config.triggerAttribute);\n if (!storageId) return;\n\n const storageElement = document.getElementById(storageId) as Storage | null;\n if (!storageElement || !(storageElement instanceof Storage)) return;\n\n event.preventDefault();\n storageElement.save();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType } from \"../types.js\";\nimport { StorageCore } from \"../core/StorageCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Storage extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...StorageCore.wcBindable,\n properties: [\n ...StorageCore.wcBindable.properties,\n { name: \"trigger\", event: STORAGE_EVENTS.triggerChanged },\n ],\n // Shell-level input surface. The Core declares only the portable `key` / `type`;\n // the Shell adds the DOM-driven settable surface. No `attribute` hints are given:\n // the `key` / `type` / `manual` setters already reflect to their attributes, so a\n // binding system that mirrors inputs[].attribute would set the attribute twice\n // (`value` / `trigger` are not attribute-backed). `commands` (load / save / remove)\n // are inherited unchanged from the Core via the spread above.\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n { name: \"value\" },\n { name: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"key\", \"type\"]; }\n\n private _core: StorageCore;\n private _trigger: boolean = false;\n // Storage load()/save() are synchronous, so connection work never defers.\n // This stays an already-resolved Promise for the whole lifecycle; it exists\n // only to satisfy the `hasConnectedCallbackPromise` protocol (consumers may\n // `await el.connectedCallbackPromise`). connectedCallback intentionally does\n // not reassign it — there is nothing async to wait for, unlike <wcs-fetch>.\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new StorageCore(this);\n }\n\n // Push the Shell's current attribute-derived key / type down into the Core.\n // Every operation (load / save / remove / value setter) and every lifecycle\n // hook that may run a Core operation or cross-tab sync must do this first, so\n // the Core never acts on a stale key / type. Centralizing it here avoids the\n // previous pattern of repeating `_core.key = …; _core.type = …;` at each call\n // site, which risked a future call site forgetting one of the two.\n private _syncCore(): void {\n this._core.key = this.key;\n this._core.type = this.type;\n }\n\n get key(): string {\n return this.getAttribute(\"key\") || \"\";\n }\n\n set key(value: string) {\n this.setAttribute(\"key\", value);\n }\n\n get type(): StorageType {\n // Normalize at the Shell boundary: any attribute value other than the\n // exact \"session\" falls back to \"local\". This keeps an invalid attribute\n // (e.g. type=\"foo\") from reaching the Core's validating setter and throwing\n // out of setAttribute / connectedCallback.\n return this.getAttribute(\"type\") === \"session\" ? \"session\" : \"local\";\n }\n\n set type(value: StorageType) {\n this.setAttribute(\"type\", value);\n }\n\n get value(): any {\n return this._core.value;\n }\n\n set value(v: any) {\n // Non-manual mode: assigning `value` auto-saves the *assigned* argument `v`\n // (write-through). Note this differs from save()/trigger, which persist the\n // *current* `_core.value` (which load() or a cross-tab `storage` event may\n // have updated). See README \"Design Notes\" for the rationale.\n //\n // Manual mode: assigning `value` does NOT persist — it only stages the value\n // into the Core (no storage write). This keeps the getter/setter consistent\n // (`el.value = x; el.value === x`) and lets a later save()/trigger commit the\n // staged value, so a `value: …` + `trigger: …` binding pair works as\n // documented. The actual write still happens only via save()/trigger.\n if (!this.manual) {\n this._syncCore();\n this._core.save(v);\n } else {\n this._core.value = v;\n }\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n const v = !!value;\n if (v) {\n this._trigger = true;\n // save() may raise (e.g. key unset). Guarantee the trigger resets to\n // false and the completion event fires even on failure, so the trigger\n // never gets stuck in the `true` state.\n try {\n this.save();\n } finally {\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(STORAGE_EVENTS.triggerChanged, {\n detail: false,\n bubbles: true,\n }));\n }\n }\n }\n\n load(): any {\n this._syncCore();\n return this._core.load();\n }\n\n // The `save` command differs in arity between the two CSBC surfaces:\n // - Core: save(value) — caller supplies the value to persist\n // - Shell: save() — persists the current `_core.value` (no argument)\n // Both are exposed under the same `commands` entry name \"save\". The protocol\n // `commands` list is descriptive metadata only and carries no arity, so this\n // is not a protocol violation; the difference is contractual and documented\n // in the README (\"Design Notes\").\n save(): void {\n this._syncCore();\n this._core.save(this._core.value);\n }\n\n remove(): void {\n this._syncCore();\n this._core.remove();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected) return;\n if (name === \"key\") {\n // Always keep the Core's key in sync with the attribute, regardless of\n // mode or whether the new value is empty. The cross-tab `storage` listener\n // compares `e.key !== _core.key`, so a stale Core key would make sync watch\n // the wrong (old/empty) key after a runtime key change. load() (which also\n // syncs the Core) only runs for non-manual mode with a non-empty key.\n this._syncCore();\n if (newValue && !this.manual) {\n this.load();\n }\n }\n if (name === \"type\") {\n // Route through the normalizing getter so an invalid attribute value\n // (e.g. type=\"foo\") falls back to \"local\" instead of throwing.\n this._syncCore();\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.key) {\n this.load();\n }\n // Always bind the cross-tab watcher to the Shell's current key/type before\n // starting sync. In paths where load()/save() never run (e.g. manual mode,\n // or key set via JS without a load), _core.key/_core.type would otherwise\n // keep a stale/empty value and the storage listener's `e.key !== _key`\n // check would compare against the wrong key. This also covers detach →\n // re-attach: stale Core key from a previous session is overwritten here.\n this._syncCore();\n this._core.startSync();\n }\n\n disconnectedCallback(): void {\n this._core.stopSync();\n }\n}\n","import { Storage } from \"./components/Storage.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.storage)) {\n customElements.define(config.tagNames.storage, Storage);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapStorage(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/events.ts","../src/raiseError.ts","../src/core/StorageCore.ts","../src/autoTrigger.ts","../src/components/Storage.ts","../src/registerComponents.ts","../src/bootstrapStorage.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n storage: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-storagetarget\",\n tagNames: {\n storage: \"wcs-storage\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n // Validate each tagNames entry individually instead of a blanket\n // Object.assign: a non-string (e.g. { storage: undefined }) would otherwise\n // poison the config and make customElements.define(undefined, …) throw at\n // registration time. Mirrors the typeof guards on autoTrigger / triggerAttribute.\n for (const [key, value] of Object.entries(partialConfig.tagNames)) {\n if (typeof value === \"string\") {\n (_config.tagNames as Record<string, string>)[key] = value;\n }\n }\n }\n frozenConfig = null;\n}\n","// Single source of truth for the custom event names dispatched by StorageCore /\n// Storage. These names appear in two places that must stay in lock-step:\n// 1. the `wcBindable.properties[].event` declarations (consumed by bind())\n// 2. the `dispatchEvent(new CustomEvent(...))` calls that emit them\n// Hard-coding the same string literal in both places risks a silent typo that\n// makes bind() listen for an event no one ever fires. Referencing these\n// constants from both sites keeps them in sync.\nexport const STORAGE_EVENTS = {\n valueChanged: \"wcs-storage:value-changed\",\n loadingChanged: \"wcs-storage:loading-changed\",\n error: \"wcs-storage:error\",\n triggerChanged: \"wcs-storage:trigger-changed\",\n} as const;\n","export function raiseError(message: string): never {\n throw new Error(`[@wcstack/storage] ${message}`);\n}\n","import { raiseError } from \"../raiseError.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType, WcsStorageError } from \"../types.js\";\n\nexport class StorageCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: STORAGE_EVENTS.valueChanged, getter: (e: Event) => (e as CustomEvent).detail },\n { name: \"loading\", event: STORAGE_EVENTS.loadingChanged },\n { name: \"error\", event: STORAGE_EVENTS.error },\n ],\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n ],\n // load / save / remove are synchronous, so none carry the `async` hint.\n commands: [\n { name: \"load\" },\n { name: \"save\" },\n { name: \"remove\" },\n ],\n };\n\n private _target: EventTarget;\n private _value: any = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _key: string = \"\";\n private _type: StorageType = \"local\";\n private _storageListener: ((e: StorageEvent) => void) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get value(): any {\n return this._value;\n }\n\n // Set the current value *without* persisting it. Persistence happens only via\n // save() / remove() / a cross-tab storage event. This setter exists so the\n // Shell (manual mode) can stage a value handed in via a `value` binding and\n // then commit it later with save()/trigger. It mirrors the value to observers\n // through the same `value-changed` event load()/save() use (CSBC: a Core value\n // change is observable), but it deliberately does not touch storage.\n //\n // Same-value writes are skipped to break a potential feedback loop:\n // value-changed → state binding → value setter → value-changed → …\n set value(v: any) {\n if (Object.is(v, this._value)) return;\n this._setValue(v);\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get key(): string {\n return this._key;\n }\n\n set key(value: string) {\n // Defensive normalization for direct Core use (the Shell already passes a\n // string via `getAttribute(\"key\") || \"\"`). Coercing to String keeps a\n // non-string assignment from poisoning the cross-tab `e.key !== _key`\n // comparison; empty keys are still rejected at operation time.\n this._key = String(value);\n }\n\n get type(): StorageType {\n return this._type;\n }\n\n set type(value: StorageType) {\n if (value !== \"local\" && value !== \"session\") {\n raiseError(`Invalid storage type: \"${value}\". Must be \"local\" or \"session\".`);\n }\n this._type = value;\n }\n\n private _getStorage(): globalThis.Storage {\n return this._type === \"session\" ? sessionStorage : localStorage;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.loadingChanged, {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.error, {\n detail: error,\n bubbles: true,\n }));\n }\n\n // Wrap a caught storage exception into the documented WcsStorageError shape,\n // tagging it with the failing operation so consumers know which call failed.\n private _toStorageError(operation: WcsStorageError[\"operation\"], e: unknown): WcsStorageError {\n return {\n operation,\n message: e instanceof Error ? e.message : String(e),\n };\n }\n\n private _setValue(value: any): void {\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.valueChanged, {\n detail: value,\n bubbles: true,\n }));\n }\n\n load(): any {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n const raw = storage.getItem(this._key);\n\n if (raw === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(raw));\n } catch {\n this._setValue(raw);\n }\n }\n\n this._setLoading(false);\n return this._value;\n } catch (e: any) {\n this._setError(this._toStorageError(\"load\", e));\n this._setLoading(false);\n return null;\n }\n }\n\n save(value: any): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n\n if (value === null || value === undefined) {\n storage.removeItem(this._key);\n // Normalize the removed value to null (matching remove() and load() of a\n // missing key) so saving `undefined` does not leave the getter returning\n // `undefined`. README's serialization table documents null/undefined as\n // \"null\" on read-back.\n this._setValue(null);\n } else if (typeof value === \"string\") {\n storage.setItem(this._key, value);\n this._setValue(value);\n } else {\n storage.setItem(this._key, JSON.stringify(value));\n this._setValue(value);\n }\n\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"save\", e));\n this._setLoading(false);\n }\n }\n\n remove(): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n storage.removeItem(this._key);\n this._setValue(null);\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"remove\", e));\n this._setLoading(false);\n }\n }\n\n startSync(): void {\n if (this._storageListener) return;\n\n this._storageListener = (e: StorageEvent) => {\n if (e.key !== this._key) return;\n if (this._type === \"session\") return;\n\n // A fresh value arriving from another tab supersedes any stale error from\n // a prior failed load/save/remove. Clearing it here keeps the sync path\n // consistent with load()/save()/remove(), which all reset error to null at\n // the start of a successful operation — otherwise an \"error present + fresh\n // value\" inconsistency could persist after a cross-tab update.\n this._setError(null);\n\n if (e.newValue === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(e.newValue));\n } catch {\n this._setValue(e.newValue);\n }\n }\n };\n\n globalThis.addEventListener(\"storage\", this._storageListener);\n }\n\n stopSync(): void {\n if (!this._storageListener) return;\n globalThis.removeEventListener(\"storage\", this._storageListener);\n this._storageListener = null;\n }\n}\n","import { config } from \"./config.js\";\nimport type { Storage } from \"./components/Storage.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const storageId = triggerElement.getAttribute(config.triggerAttribute);\n if (!storageId) return;\n\n // Resolve the registered constructor at call time instead of importing Storage\n // as a value. The value import created a components/Storage.ts ⇄ autoTrigger.ts\n // cycle (Storage.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the exact same identity guarantee\n // — only the registered <wcs-storage> class matches — without the import cycle.\n const StorageCtor = customElements.get(config.tagNames.storage);\n const storageElement = document.getElementById(storageId);\n if (!StorageCtor || !(storageElement instanceof StorageCtor)) return;\n\n event.preventDefault();\n (storageElement as Storage).save();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType } from \"../types.js\";\nimport { StorageCore } from \"../core/StorageCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Storage extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...StorageCore.wcBindable,\n properties: [\n ...StorageCore.wcBindable.properties,\n { name: \"trigger\", event: STORAGE_EVENTS.triggerChanged },\n ],\n // Shell-level input surface. The Core declares only the portable `key` / `type`;\n // the Shell adds the DOM-driven settable surface. No `attribute` hints are given:\n // the `key` / `type` / `manual` setters already reflect to their attributes, so a\n // binding system that mirrors inputs[].attribute would set the attribute twice\n // (`value` / `trigger` are not attribute-backed). `commands` (load / save / remove)\n // are inherited unchanged from the Core via the spread above.\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n { name: \"value\" },\n { name: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"key\", \"type\"]; }\n\n private _core: StorageCore;\n private _trigger: boolean = false;\n // Storage load()/save() are synchronous, so connection work never defers.\n // This stays an already-resolved Promise for the whole lifecycle; it exists\n // only to satisfy the `hasConnectedCallbackPromise` protocol (consumers may\n // `await el.connectedCallbackPromise`). connectedCallback intentionally does\n // not reassign it — there is nothing async to wait for, unlike <wcs-fetch>.\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new StorageCore(this);\n }\n\n // Push the Shell's current attribute-derived key / type down into the Core.\n // Every operation (load / save / remove / value setter) and every lifecycle\n // hook that may run a Core operation or cross-tab sync must do this first, so\n // the Core never acts on a stale key / type. Centralizing it here avoids the\n // previous pattern of repeating `_core.key = …; _core.type = …;` at each call\n // site, which risked a future call site forgetting one of the two.\n private _syncCore(): void {\n this._core.key = this.key;\n this._core.type = this.type;\n }\n\n get key(): string {\n return this.getAttribute(\"key\") || \"\";\n }\n\n set key(value: string) {\n this.setAttribute(\"key\", value);\n }\n\n get type(): StorageType {\n // Normalize at the Shell boundary: any attribute value other than the\n // exact \"session\" falls back to \"local\". This keeps an invalid attribute\n // (e.g. type=\"foo\") from reaching the Core's validating setter and throwing\n // out of setAttribute / connectedCallback.\n return this.getAttribute(\"type\") === \"session\" ? \"session\" : \"local\";\n }\n\n set type(value: StorageType) {\n this.setAttribute(\"type\", value);\n }\n\n get value(): any {\n return this._core.value;\n }\n\n set value(v: any) {\n // Non-manual mode: assigning `value` auto-saves the *assigned* argument `v`\n // (write-through). Note this differs from save()/trigger, which persist the\n // *current* `_core.value` (which load() or a cross-tab `storage` event may\n // have updated). See README \"Design Notes\" for the rationale.\n //\n // Manual mode: assigning `value` does NOT persist — it only stages the value\n // into the Core (no storage write). This keeps the getter/setter consistent\n // (`el.value = x; el.value === x`) and lets a later save()/trigger commit the\n // staged value, so a `value: …` + `trigger: …` binding pair works as\n // documented. The actual write still happens only via save()/trigger.\n if (!this.manual) {\n this._syncCore();\n this._core.save(v);\n } else {\n this._core.value = v;\n }\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n const v = !!value;\n if (v) {\n this._trigger = true;\n // save() may raise (e.g. key unset). Guarantee the trigger resets to\n // false and the completion event fires even on failure, so the trigger\n // never gets stuck in the `true` state.\n try {\n this.save();\n } finally {\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(STORAGE_EVENTS.triggerChanged, {\n detail: false,\n bubbles: true,\n }));\n }\n }\n }\n\n load(): any {\n this._syncCore();\n return this._core.load();\n }\n\n // The `save` command differs in arity between the two CSBC surfaces:\n // - Core: save(value) — caller supplies the value to persist\n // - Shell: save() — persists the current `_core.value` (no argument)\n // Both are exposed under the same `commands` entry name \"save\". The protocol\n // `commands` list is descriptive metadata only and carries no arity, so this\n // is not a protocol violation; the difference is contractual and documented\n // in the README (\"Design Notes\").\n save(): void {\n this._syncCore();\n this._core.save(this._core.value);\n }\n\n remove(): void {\n this._syncCore();\n this._core.remove();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected) return;\n if (name === \"key\") {\n // Always keep the Core's key in sync with the attribute, regardless of\n // mode or whether the new value is empty. The cross-tab `storage` listener\n // compares `e.key !== _core.key`, so a stale Core key would make sync watch\n // the wrong (old/empty) key after a runtime key change. load() (which also\n // syncs the Core) only runs for non-manual mode with a non-empty key.\n this._syncCore();\n if (newValue && !this.manual) {\n this.load();\n }\n }\n if (name === \"type\") {\n // Route through the normalizing getter so an invalid attribute value\n // (e.g. type=\"foo\") falls back to \"local\" instead of throwing.\n this._syncCore();\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.key) {\n this.load();\n }\n // Always bind the cross-tab watcher to the Shell's current key/type before\n // starting sync. In paths where load()/save() never run (e.g. manual mode,\n // or key set via JS without a load), _core.key/_core.type would otherwise\n // keep a stale/empty value and the storage listener's `e.key !== _key`\n // check would compare against the wrong key. This also covers detach →\n // re-attach: stale Core key from a previous session is overwritten here.\n this._syncCore();\n this._core.startSync();\n }\n\n disconnectedCallback(): void {\n this._core.stopSync();\n }\n}\n","import { Storage } from \"./components/Storage.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.storage)) {\n customElements.define(config.tagNames.storage, Storage);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapStorage(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e={autoTrigger:!0,triggerAttribute:"data-storagetarget",tagNames:{storage:"wcs-storage"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const s of Object.keys(e))t(e[s]);return e}function s(e){if(null===e||"object"!=typeof e)return e;const t={};for(const r of Object.keys(e))t[r]=s(e[r]);return t}let r=null;const i=e;function n(){return r||(r=t(s(e))),r}const a="wcs-storage:value-changed",o="wcs-storage:loading-changed",l="wcs-storage:error",g="wcs-storage:trigger-changed";function u(e){throw new Error(`[@wcstack/storage] ${e}`)}class c extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"value",event:a,getter:e=>e.detail},{name:"loading",event:o},{name:"error",event:l}],inputs:[{name:"key"},{name:"type"}],commands:[{name:"load"},{name:"save"},{name:"remove"}]};_target;_value=null;_loading=!1;_error=null;_key="";_type="local";_storageListener=null;constructor(e){super(),this._target=e??this}get value(){return this._value}set value(e){Object.is(e,this._value)||this._setValue(e)}get loading(){return this._loading}get error(){return this._error}get key(){return this._key}set key(e){this._key=String(e)}get type(){return this._type}set type(e){"local"!==e&&"session"!==e&&u(`Invalid storage type: "${e}". Must be "local" or "session".`),this._type=e}_getStorage(){return"session"===this._type?sessionStorage:localStorage}_setLoading(e){this._loading=e,this._target.dispatchEvent(new CustomEvent(o,{detail:e,bubbles:!0}))}_setError(e){this._error=e,this._target.dispatchEvent(new CustomEvent(l,{detail:e,bubbles:!0}))}_toStorageError(e,t){return{operation:e,message:t instanceof Error?t.message:String(t)}}_setValue(e){this._value=e,this._target.dispatchEvent(new CustomEvent(a,{detail:e,bubbles:!0}))}load(){this._key||u("key is required."),this._setLoading(!0),this._setError(null);try{const e=this._getStorage().getItem(this._key);if(null===e)this._setValue(null);else try{this._setValue(JSON.parse(e))}catch{this._setValue(e)}return this._setLoading(!1),this._value}catch(e){return this._setError(this._toStorageError("load",e)),this._setLoading(!1),null}}save(e){this._key||u("key is required."),this._setLoading(!0),this._setError(null);try{const t=this._getStorage();null==e?(t.removeItem(this._key),this._setValue(null)):"string"==typeof e?(t.setItem(this._key,e),this._setValue(e)):(t.setItem(this._key,JSON.stringify(e)),this._setValue(e)),this._setLoading(!1)}catch(e){this._setError(this._toStorageError("save",e)),this._setLoading(!1)}}remove(){this._key||u("key is required."),this._setLoading(!0),this._setError(null);try{this._getStorage().removeItem(this._key),this._setValue(null),this._setLoading(!1)}catch(e){this._setError(this._toStorageError("remove",e)),this._setLoading(!1)}}startSync(){this._storageListener||(this._storageListener=e=>{if(e.key===this._key&&"session"!==this._type)if(this._setError(null),null===e.newValue)this._setValue(null);else try{this._setValue(JSON.parse(e.newValue))}catch{this._setValue(e.newValue)}},globalThis.addEventListener("storage",this._storageListener))}stopSync(){this._storageListener&&(globalThis.removeEventListener("storage",this._storageListener),this._storageListener=null)}}let h=!1;function _(e){const t=e.target;if(!(t instanceof Element))return;const s=t.closest(`[${i.triggerAttribute}]`);if(!s)return;const r=s.getAttribute(i.triggerAttribute);if(!r)return;const n=document.getElementById(r);n&&n instanceof y&&(e.preventDefault(),n.save())}class y extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...c.wcBindable,properties:[...c.wcBindable.properties,{name:"trigger",event:g}],inputs:[{name:"key"},{name:"type"},{name:"value"},{name:"manual"},{name:"trigger"}]};static get observedAttributes(){return["key","type"]}_core;_trigger=!1;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new c(this)}_syncCore(){this._core.key=this.key,this._core.type=this.type}get key(){return this.getAttribute("key")||""}set key(e){this.setAttribute("key",e)}get type(){return"session"===this.getAttribute("type")?"session":"local"}set type(e){this.setAttribute("type",e)}get value(){return this._core.value}set value(e){this.manual?this._core.value=e:(this._syncCore(),this._core.save(e))}get loading(){return this._core.loading}get error(){return this._core.error}get connectedCallbackPromise(){return this._connectedCallbackPromise}get manual(){return this.hasAttribute("manual")}set manual(e){e?this.setAttribute("manual",""):this.removeAttribute("manual")}get trigger(){return this._trigger}set trigger(e){if(!!e){this._trigger=!0;try{this.save()}finally{this._trigger=!1,this.dispatchEvent(new CustomEvent(g,{detail:!1,bubbles:!0}))}}}load(){return this._syncCore(),this._core.load()}save(){this._syncCore(),this._core.save(this._core.value)}remove(){this._syncCore(),this._core.remove()}attributeChangedCallback(e,t,s){this.isConnected&&("key"===e&&(this._syncCore(),s&&!this.manual&&this.load()),"type"===e&&this._syncCore())}connectedCallback(){this.style.display="none",i.autoTrigger&&(h||(h=!0,document.addEventListener("click",_))),!this.manual&&this.key&&this.load(),this._syncCore(),this._core.startSync()}disconnectedCallback(){this._core.stopSync()}}function d(t){t&&function(t){if("boolean"==typeof t.autoTrigger&&(e.autoTrigger=t.autoTrigger),"string"==typeof t.triggerAttribute&&(e.triggerAttribute=t.triggerAttribute),t.tagNames)for(const[s,r]of Object.entries(t.tagNames))"string"==typeof r&&(e.tagNames[s]=r);r=null}(t),customElements.get(i.tagNames.storage)||customElements.define(i.tagNames.storage,y)}export{c as StorageCore,y as WcsStorage,d as bootstrapStorage,n as getConfig};
1
+ const e={autoTrigger:!0,triggerAttribute:"data-storagetarget",tagNames:{storage:"wcs-storage"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const s of Object.keys(e))t(e[s]);return e}function s(e){if(null===e||"object"!=typeof e)return e;const t={};for(const r of Object.keys(e))t[r]=s(e[r]);return t}let r=null;const i=e;function a(){return r||(r=t(s(e))),r}const n="wcs-storage:value-changed",o="wcs-storage:loading-changed",l="wcs-storage:error",g="wcs-storage:trigger-changed";function u(e){throw new Error(`[@wcstack/storage] ${e}`)}class c extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"value",event:n,getter:e=>e.detail},{name:"loading",event:o},{name:"error",event:l}],inputs:[{name:"key"},{name:"type"}],commands:[{name:"load"},{name:"save"},{name:"remove"}]};_target;_value=null;_loading=!1;_error=null;_key="";_type="local";_storageListener=null;constructor(e){super(),this._target=e??this}get value(){return this._value}set value(e){Object.is(e,this._value)||this._setValue(e)}get loading(){return this._loading}get error(){return this._error}get key(){return this._key}set key(e){this._key=String(e)}get type(){return this._type}set type(e){"local"!==e&&"session"!==e&&u(`Invalid storage type: "${e}". Must be "local" or "session".`),this._type=e}_getStorage(){return"session"===this._type?sessionStorage:localStorage}_setLoading(e){this._loading=e,this._target.dispatchEvent(new CustomEvent(o,{detail:e,bubbles:!0}))}_setError(e){this._error=e,this._target.dispatchEvent(new CustomEvent(l,{detail:e,bubbles:!0}))}_toStorageError(e,t){return{operation:e,message:t instanceof Error?t.message:String(t)}}_setValue(e){this._value=e,this._target.dispatchEvent(new CustomEvent(n,{detail:e,bubbles:!0}))}load(){this._key||u("key is required."),this._setLoading(!0),this._setError(null);try{const e=this._getStorage().getItem(this._key);if(null===e)this._setValue(null);else try{this._setValue(JSON.parse(e))}catch{this._setValue(e)}return this._setLoading(!1),this._value}catch(e){return this._setError(this._toStorageError("load",e)),this._setLoading(!1),null}}save(e){this._key||u("key is required."),this._setLoading(!0),this._setError(null);try{const t=this._getStorage();null==e?(t.removeItem(this._key),this._setValue(null)):"string"==typeof e?(t.setItem(this._key,e),this._setValue(e)):(t.setItem(this._key,JSON.stringify(e)),this._setValue(e)),this._setLoading(!1)}catch(e){this._setError(this._toStorageError("save",e)),this._setLoading(!1)}}remove(){this._key||u("key is required."),this._setLoading(!0),this._setError(null);try{this._getStorage().removeItem(this._key),this._setValue(null),this._setLoading(!1)}catch(e){this._setError(this._toStorageError("remove",e)),this._setLoading(!1)}}startSync(){this._storageListener||(this._storageListener=e=>{if(e.key===this._key&&"session"!==this._type)if(this._setError(null),null===e.newValue)this._setValue(null);else try{this._setValue(JSON.parse(e.newValue))}catch{this._setValue(e.newValue)}},globalThis.addEventListener("storage",this._storageListener))}stopSync(){this._storageListener&&(globalThis.removeEventListener("storage",this._storageListener),this._storageListener=null)}}let h=!1;function _(e){const t=e.target;if(!(t instanceof Element))return;const s=t.closest(`[${i.triggerAttribute}]`);if(!s)return;const r=s.getAttribute(i.triggerAttribute);if(!r)return;const a=customElements.get(i.tagNames.storage),n=document.getElementById(r);a&&n instanceof a&&(e.preventDefault(),n.save())}class y extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...c.wcBindable,properties:[...c.wcBindable.properties,{name:"trigger",event:g}],inputs:[{name:"key"},{name:"type"},{name:"value"},{name:"manual"},{name:"trigger"}]};static get observedAttributes(){return["key","type"]}_core;_trigger=!1;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new c(this)}_syncCore(){this._core.key=this.key,this._core.type=this.type}get key(){return this.getAttribute("key")||""}set key(e){this.setAttribute("key",e)}get type(){return"session"===this.getAttribute("type")?"session":"local"}set type(e){this.setAttribute("type",e)}get value(){return this._core.value}set value(e){this.manual?this._core.value=e:(this._syncCore(),this._core.save(e))}get loading(){return this._core.loading}get error(){return this._core.error}get connectedCallbackPromise(){return this._connectedCallbackPromise}get manual(){return this.hasAttribute("manual")}set manual(e){e?this.setAttribute("manual",""):this.removeAttribute("manual")}get trigger(){return this._trigger}set trigger(e){if(!!e){this._trigger=!0;try{this.save()}finally{this._trigger=!1,this.dispatchEvent(new CustomEvent(g,{detail:!1,bubbles:!0}))}}}load(){return this._syncCore(),this._core.load()}save(){this._syncCore(),this._core.save(this._core.value)}remove(){this._syncCore(),this._core.remove()}attributeChangedCallback(e,t,s){this.isConnected&&("key"===e&&(this._syncCore(),s&&!this.manual&&this.load()),"type"===e&&this._syncCore())}connectedCallback(){this.style.display="none",i.autoTrigger&&(h||(h=!0,document.addEventListener("click",_))),!this.manual&&this.key&&this.load(),this._syncCore(),this._core.startSync()}disconnectedCallback(){this._core.stopSync()}}function d(t){t&&function(t){if("boolean"==typeof t.autoTrigger&&(e.autoTrigger=t.autoTrigger),"string"==typeof t.triggerAttribute&&(e.triggerAttribute=t.triggerAttribute),t.tagNames)for(const[s,r]of Object.entries(t.tagNames))"string"==typeof r&&(e.tagNames[s]=r);r=null}(t),customElements.get(i.tagNames.storage)||customElements.define(i.tagNames.storage,y)}export{c as StorageCore,y as WcsStorage,d as bootstrapStorage,a as getConfig};
2
2
  //# sourceMappingURL=index.esm.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/events.ts","../src/raiseError.ts","../src/core/StorageCore.ts","../src/autoTrigger.ts","../src/components/Storage.ts","../src/bootstrapStorage.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n storage: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-storagetarget\",\n tagNames: {\n storage: \"wcs-storage\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n // Validate each tagNames entry individually instead of a blanket\n // Object.assign: a non-string (e.g. { storage: undefined }) would otherwise\n // poison the config and make customElements.define(undefined, …) throw at\n // registration time. Mirrors the typeof guards on autoTrigger / triggerAttribute.\n for (const [key, value] of Object.entries(partialConfig.tagNames)) {\n if (typeof value === \"string\") {\n (_config.tagNames as Record<string, string>)[key] = value;\n }\n }\n }\n frozenConfig = null;\n}\n","// Single source of truth for the custom event names dispatched by StorageCore /\n// Storage. These names appear in two places that must stay in lock-step:\n// 1. the `wcBindable.properties[].event` declarations (consumed by bind())\n// 2. the `dispatchEvent(new CustomEvent(...))` calls that emit them\n// Hard-coding the same string literal in both places risks a silent typo that\n// makes bind() listen for an event no one ever fires. Referencing these\n// constants from both sites keeps them in sync.\nexport const STORAGE_EVENTS = {\n valueChanged: \"wcs-storage:value-changed\",\n loadingChanged: \"wcs-storage:loading-changed\",\n error: \"wcs-storage:error\",\n triggerChanged: \"wcs-storage:trigger-changed\",\n} as const;\n","export function raiseError(message: string): never {\n throw new Error(`[@wcstack/storage] ${message}`);\n}\n","import { raiseError } from \"../raiseError.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType, WcsStorageError } from \"../types.js\";\n\nexport class StorageCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: STORAGE_EVENTS.valueChanged, getter: (e: Event) => (e as CustomEvent).detail },\n { name: \"loading\", event: STORAGE_EVENTS.loadingChanged },\n { name: \"error\", event: STORAGE_EVENTS.error },\n ],\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n ],\n // load / save / remove are synchronous, so none carry the `async` hint.\n commands: [\n { name: \"load\" },\n { name: \"save\" },\n { name: \"remove\" },\n ],\n };\n\n private _target: EventTarget;\n private _value: any = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _key: string = \"\";\n private _type: StorageType = \"local\";\n private _storageListener: ((e: StorageEvent) => void) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get value(): any {\n return this._value;\n }\n\n // Set the current value *without* persisting it. Persistence happens only via\n // save() / remove() / a cross-tab storage event. This setter exists so the\n // Shell (manual mode) can stage a value handed in via a `value` binding and\n // then commit it later with save()/trigger. It mirrors the value to observers\n // through the same `value-changed` event load()/save() use (CSBC: a Core value\n // change is observable), but it deliberately does not touch storage.\n //\n // Same-value writes are skipped to break a potential feedback loop:\n // value-changed → state binding → value setter → value-changed → …\n set value(v: any) {\n if (Object.is(v, this._value)) return;\n this._setValue(v);\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get key(): string {\n return this._key;\n }\n\n set key(value: string) {\n // Defensive normalization for direct Core use (the Shell already passes a\n // string via `getAttribute(\"key\") || \"\"`). Coercing to String keeps a\n // non-string assignment from poisoning the cross-tab `e.key !== _key`\n // comparison; empty keys are still rejected at operation time.\n this._key = String(value);\n }\n\n get type(): StorageType {\n return this._type;\n }\n\n set type(value: StorageType) {\n if (value !== \"local\" && value !== \"session\") {\n raiseError(`Invalid storage type: \"${value}\". Must be \"local\" or \"session\".`);\n }\n this._type = value;\n }\n\n private _getStorage(): globalThis.Storage {\n return this._type === \"session\" ? sessionStorage : localStorage;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.loadingChanged, {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.error, {\n detail: error,\n bubbles: true,\n }));\n }\n\n // Wrap a caught storage exception into the documented WcsStorageError shape,\n // tagging it with the failing operation so consumers know which call failed.\n private _toStorageError(operation: WcsStorageError[\"operation\"], e: unknown): WcsStorageError {\n return {\n operation,\n message: e instanceof Error ? e.message : String(e),\n };\n }\n\n private _setValue(value: any): void {\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.valueChanged, {\n detail: value,\n bubbles: true,\n }));\n }\n\n load(): any {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n const raw = storage.getItem(this._key);\n\n if (raw === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(raw));\n } catch {\n this._setValue(raw);\n }\n }\n\n this._setLoading(false);\n return this._value;\n } catch (e: any) {\n this._setError(this._toStorageError(\"load\", e));\n this._setLoading(false);\n return null;\n }\n }\n\n save(value: any): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n\n if (value === null || value === undefined) {\n storage.removeItem(this._key);\n // Normalize the removed value to null (matching remove() and load() of a\n // missing key) so saving `undefined` does not leave the getter returning\n // `undefined`. README's serialization table documents null/undefined as\n // \"null\" on read-back.\n this._setValue(null);\n } else if (typeof value === \"string\") {\n storage.setItem(this._key, value);\n this._setValue(value);\n } else {\n storage.setItem(this._key, JSON.stringify(value));\n this._setValue(value);\n }\n\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"save\", e));\n this._setLoading(false);\n }\n }\n\n remove(): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n storage.removeItem(this._key);\n this._setValue(null);\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"remove\", e));\n this._setLoading(false);\n }\n }\n\n startSync(): void {\n if (this._storageListener) return;\n\n this._storageListener = (e: StorageEvent) => {\n if (e.key !== this._key) return;\n if (this._type === \"session\") return;\n\n // A fresh value arriving from another tab supersedes any stale error from\n // a prior failed load/save/remove. Clearing it here keeps the sync path\n // consistent with load()/save()/remove(), which all reset error to null at\n // the start of a successful operation — otherwise an \"error present + fresh\n // value\" inconsistency could persist after a cross-tab update.\n this._setError(null);\n\n if (e.newValue === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(e.newValue));\n } catch {\n this._setValue(e.newValue);\n }\n }\n };\n\n globalThis.addEventListener(\"storage\", this._storageListener);\n }\n\n stopSync(): void {\n if (!this._storageListener) return;\n globalThis.removeEventListener(\"storage\", this._storageListener);\n this._storageListener = null;\n }\n}\n","import { config } from \"./config.js\";\nimport { Storage } from \"./components/Storage.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const storageId = triggerElement.getAttribute(config.triggerAttribute);\n if (!storageId) return;\n\n const storageElement = document.getElementById(storageId) as Storage | null;\n if (!storageElement || !(storageElement instanceof Storage)) return;\n\n event.preventDefault();\n storageElement.save();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType } from \"../types.js\";\nimport { StorageCore } from \"../core/StorageCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Storage extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...StorageCore.wcBindable,\n properties: [\n ...StorageCore.wcBindable.properties,\n { name: \"trigger\", event: STORAGE_EVENTS.triggerChanged },\n ],\n // Shell-level input surface. The Core declares only the portable `key` / `type`;\n // the Shell adds the DOM-driven settable surface. No `attribute` hints are given:\n // the `key` / `type` / `manual` setters already reflect to their attributes, so a\n // binding system that mirrors inputs[].attribute would set the attribute twice\n // (`value` / `trigger` are not attribute-backed). `commands` (load / save / remove)\n // are inherited unchanged from the Core via the spread above.\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n { name: \"value\" },\n { name: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"key\", \"type\"]; }\n\n private _core: StorageCore;\n private _trigger: boolean = false;\n // Storage load()/save() are synchronous, so connection work never defers.\n // This stays an already-resolved Promise for the whole lifecycle; it exists\n // only to satisfy the `hasConnectedCallbackPromise` protocol (consumers may\n // `await el.connectedCallbackPromise`). connectedCallback intentionally does\n // not reassign it — there is nothing async to wait for, unlike <wcs-fetch>.\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new StorageCore(this);\n }\n\n // Push the Shell's current attribute-derived key / type down into the Core.\n // Every operation (load / save / remove / value setter) and every lifecycle\n // hook that may run a Core operation or cross-tab sync must do this first, so\n // the Core never acts on a stale key / type. Centralizing it here avoids the\n // previous pattern of repeating `_core.key = …; _core.type = …;` at each call\n // site, which risked a future call site forgetting one of the two.\n private _syncCore(): void {\n this._core.key = this.key;\n this._core.type = this.type;\n }\n\n get key(): string {\n return this.getAttribute(\"key\") || \"\";\n }\n\n set key(value: string) {\n this.setAttribute(\"key\", value);\n }\n\n get type(): StorageType {\n // Normalize at the Shell boundary: any attribute value other than the\n // exact \"session\" falls back to \"local\". This keeps an invalid attribute\n // (e.g. type=\"foo\") from reaching the Core's validating setter and throwing\n // out of setAttribute / connectedCallback.\n return this.getAttribute(\"type\") === \"session\" ? \"session\" : \"local\";\n }\n\n set type(value: StorageType) {\n this.setAttribute(\"type\", value);\n }\n\n get value(): any {\n return this._core.value;\n }\n\n set value(v: any) {\n // Non-manual mode: assigning `value` auto-saves the *assigned* argument `v`\n // (write-through). Note this differs from save()/trigger, which persist the\n // *current* `_core.value` (which load() or a cross-tab `storage` event may\n // have updated). See README \"Design Notes\" for the rationale.\n //\n // Manual mode: assigning `value` does NOT persist — it only stages the value\n // into the Core (no storage write). This keeps the getter/setter consistent\n // (`el.value = x; el.value === x`) and lets a later save()/trigger commit the\n // staged value, so a `value: …` + `trigger: …` binding pair works as\n // documented. The actual write still happens only via save()/trigger.\n if (!this.manual) {\n this._syncCore();\n this._core.save(v);\n } else {\n this._core.value = v;\n }\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n const v = !!value;\n if (v) {\n this._trigger = true;\n // save() may raise (e.g. key unset). Guarantee the trigger resets to\n // false and the completion event fires even on failure, so the trigger\n // never gets stuck in the `true` state.\n try {\n this.save();\n } finally {\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(STORAGE_EVENTS.triggerChanged, {\n detail: false,\n bubbles: true,\n }));\n }\n }\n }\n\n load(): any {\n this._syncCore();\n return this._core.load();\n }\n\n // The `save` command differs in arity between the two CSBC surfaces:\n // - Core: save(value) — caller supplies the value to persist\n // - Shell: save() — persists the current `_core.value` (no argument)\n // Both are exposed under the same `commands` entry name \"save\". The protocol\n // `commands` list is descriptive metadata only and carries no arity, so this\n // is not a protocol violation; the difference is contractual and documented\n // in the README (\"Design Notes\").\n save(): void {\n this._syncCore();\n this._core.save(this._core.value);\n }\n\n remove(): void {\n this._syncCore();\n this._core.remove();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected) return;\n if (name === \"key\") {\n // Always keep the Core's key in sync with the attribute, regardless of\n // mode or whether the new value is empty. The cross-tab `storage` listener\n // compares `e.key !== _core.key`, so a stale Core key would make sync watch\n // the wrong (old/empty) key after a runtime key change. load() (which also\n // syncs the Core) only runs for non-manual mode with a non-empty key.\n this._syncCore();\n if (newValue && !this.manual) {\n this.load();\n }\n }\n if (name === \"type\") {\n // Route through the normalizing getter so an invalid attribute value\n // (e.g. type=\"foo\") falls back to \"local\" instead of throwing.\n this._syncCore();\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.key) {\n this.load();\n }\n // Always bind the cross-tab watcher to the Shell's current key/type before\n // starting sync. In paths where load()/save() never run (e.g. manual mode,\n // or key set via JS without a load), _core.key/_core.type would otherwise\n // keep a stale/empty value and the storage listener's `e.key !== _key`\n // check would compare against the wrong key. This also covers detach →\n // re-attach: stale Core key from a previous session is overwritten here.\n this._syncCore();\n this._core.startSync();\n }\n\n disconnectedCallback(): void {\n this._core.stopSync();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapStorage(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { Storage } from \"./components/Storage.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.storage)) {\n customElements.define(config.tagNames.storage, Storage);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","tagNames","storage","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","STORAGE_EVENTS","raiseError","message","Error","StorageCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","inputs","commands","_target","_value","_loading","_error","_key","_type","_storageListener","constructor","target","super","this","value","v","is","_setValue","loading","error","String","type","_getStorage","sessionStorage","localStorage","_setLoading","dispatchEvent","CustomEvent","bubbles","_setError","_toStorageError","operation","load","raw","getItem","JSON","parse","save","removeItem","setItem","stringify","remove","startSync","newValue","globalThis","addEventListener","stopSync","removeEventListener","registered","handleClick","Element","triggerElement","closest","storageId","getAttribute","storageElement","document","getElementById","Storage","preventDefault","HTMLElement","wcBindable","observedAttributes","_core","_trigger","_connectedCallbackPromise","Promise","resolve","_syncCore","setAttribute","manual","connectedCallbackPromise","hasAttribute","removeAttribute","trigger","attributeChangedCallback","_oldValue","isConnected","connectedCallback","style","display","disconnectedCallback","bootstrapStorage","userConfig","partialConfig","entries","setConfig","customElements","get","define"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/events.ts","../src/raiseError.ts","../src/core/StorageCore.ts","../src/autoTrigger.ts","../src/components/Storage.ts","../src/bootstrapStorage.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n storage: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-storagetarget\",\n tagNames: {\n storage: \"wcs-storage\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n // Validate each tagNames entry individually instead of a blanket\n // Object.assign: a non-string (e.g. { storage: undefined }) would otherwise\n // poison the config and make customElements.define(undefined, …) throw at\n // registration time. Mirrors the typeof guards on autoTrigger / triggerAttribute.\n for (const [key, value] of Object.entries(partialConfig.tagNames)) {\n if (typeof value === \"string\") {\n (_config.tagNames as Record<string, string>)[key] = value;\n }\n }\n }\n frozenConfig = null;\n}\n","// Single source of truth for the custom event names dispatched by StorageCore /\n// Storage. These names appear in two places that must stay in lock-step:\n// 1. the `wcBindable.properties[].event` declarations (consumed by bind())\n// 2. the `dispatchEvent(new CustomEvent(...))` calls that emit them\n// Hard-coding the same string literal in both places risks a silent typo that\n// makes bind() listen for an event no one ever fires. Referencing these\n// constants from both sites keeps them in sync.\nexport const STORAGE_EVENTS = {\n valueChanged: \"wcs-storage:value-changed\",\n loadingChanged: \"wcs-storage:loading-changed\",\n error: \"wcs-storage:error\",\n triggerChanged: \"wcs-storage:trigger-changed\",\n} as const;\n","export function raiseError(message: string): never {\n throw new Error(`[@wcstack/storage] ${message}`);\n}\n","import { raiseError } from \"../raiseError.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType, WcsStorageError } from \"../types.js\";\n\nexport class StorageCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: STORAGE_EVENTS.valueChanged, getter: (e: Event) => (e as CustomEvent).detail },\n { name: \"loading\", event: STORAGE_EVENTS.loadingChanged },\n { name: \"error\", event: STORAGE_EVENTS.error },\n ],\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n ],\n // load / save / remove are synchronous, so none carry the `async` hint.\n commands: [\n { name: \"load\" },\n { name: \"save\" },\n { name: \"remove\" },\n ],\n };\n\n private _target: EventTarget;\n private _value: any = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _key: string = \"\";\n private _type: StorageType = \"local\";\n private _storageListener: ((e: StorageEvent) => void) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get value(): any {\n return this._value;\n }\n\n // Set the current value *without* persisting it. Persistence happens only via\n // save() / remove() / a cross-tab storage event. This setter exists so the\n // Shell (manual mode) can stage a value handed in via a `value` binding and\n // then commit it later with save()/trigger. It mirrors the value to observers\n // through the same `value-changed` event load()/save() use (CSBC: a Core value\n // change is observable), but it deliberately does not touch storage.\n //\n // Same-value writes are skipped to break a potential feedback loop:\n // value-changed → state binding → value setter → value-changed → …\n set value(v: any) {\n if (Object.is(v, this._value)) return;\n this._setValue(v);\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get key(): string {\n return this._key;\n }\n\n set key(value: string) {\n // Defensive normalization for direct Core use (the Shell already passes a\n // string via `getAttribute(\"key\") || \"\"`). Coercing to String keeps a\n // non-string assignment from poisoning the cross-tab `e.key !== _key`\n // comparison; empty keys are still rejected at operation time.\n this._key = String(value);\n }\n\n get type(): StorageType {\n return this._type;\n }\n\n set type(value: StorageType) {\n if (value !== \"local\" && value !== \"session\") {\n raiseError(`Invalid storage type: \"${value}\". Must be \"local\" or \"session\".`);\n }\n this._type = value;\n }\n\n private _getStorage(): globalThis.Storage {\n return this._type === \"session\" ? sessionStorage : localStorage;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.loadingChanged, {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.error, {\n detail: error,\n bubbles: true,\n }));\n }\n\n // Wrap a caught storage exception into the documented WcsStorageError shape,\n // tagging it with the failing operation so consumers know which call failed.\n private _toStorageError(operation: WcsStorageError[\"operation\"], e: unknown): WcsStorageError {\n return {\n operation,\n message: e instanceof Error ? e.message : String(e),\n };\n }\n\n private _setValue(value: any): void {\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(STORAGE_EVENTS.valueChanged, {\n detail: value,\n bubbles: true,\n }));\n }\n\n load(): any {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n const raw = storage.getItem(this._key);\n\n if (raw === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(raw));\n } catch {\n this._setValue(raw);\n }\n }\n\n this._setLoading(false);\n return this._value;\n } catch (e: any) {\n this._setError(this._toStorageError(\"load\", e));\n this._setLoading(false);\n return null;\n }\n }\n\n save(value: any): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n\n if (value === null || value === undefined) {\n storage.removeItem(this._key);\n // Normalize the removed value to null (matching remove() and load() of a\n // missing key) so saving `undefined` does not leave the getter returning\n // `undefined`. README's serialization table documents null/undefined as\n // \"null\" on read-back.\n this._setValue(null);\n } else if (typeof value === \"string\") {\n storage.setItem(this._key, value);\n this._setValue(value);\n } else {\n storage.setItem(this._key, JSON.stringify(value));\n this._setValue(value);\n }\n\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"save\", e));\n this._setLoading(false);\n }\n }\n\n remove(): void {\n if (!this._key) {\n raiseError(\"key is required.\");\n }\n\n this._setLoading(true);\n this._setError(null);\n\n try {\n const storage = this._getStorage();\n storage.removeItem(this._key);\n this._setValue(null);\n this._setLoading(false);\n } catch (e: any) {\n this._setError(this._toStorageError(\"remove\", e));\n this._setLoading(false);\n }\n }\n\n startSync(): void {\n if (this._storageListener) return;\n\n this._storageListener = (e: StorageEvent) => {\n if (e.key !== this._key) return;\n if (this._type === \"session\") return;\n\n // A fresh value arriving from another tab supersedes any stale error from\n // a prior failed load/save/remove. Clearing it here keeps the sync path\n // consistent with load()/save()/remove(), which all reset error to null at\n // the start of a successful operation — otherwise an \"error present + fresh\n // value\" inconsistency could persist after a cross-tab update.\n this._setError(null);\n\n if (e.newValue === null) {\n this._setValue(null);\n } else {\n try {\n this._setValue(JSON.parse(e.newValue));\n } catch {\n this._setValue(e.newValue);\n }\n }\n };\n\n globalThis.addEventListener(\"storage\", this._storageListener);\n }\n\n stopSync(): void {\n if (!this._storageListener) return;\n globalThis.removeEventListener(\"storage\", this._storageListener);\n this._storageListener = null;\n }\n}\n","import { config } from \"./config.js\";\nimport type { Storage } from \"./components/Storage.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const storageId = triggerElement.getAttribute(config.triggerAttribute);\n if (!storageId) return;\n\n // Resolve the registered constructor at call time instead of importing Storage\n // as a value. The value import created a components/Storage.ts ⇄ autoTrigger.ts\n // cycle (Storage.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the exact same identity guarantee\n // — only the registered <wcs-storage> class matches — without the import cycle.\n const StorageCtor = customElements.get(config.tagNames.storage);\n const storageElement = document.getElementById(storageId);\n if (!StorageCtor || !(storageElement instanceof StorageCtor)) return;\n\n event.preventDefault();\n (storageElement as Storage).save();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { STORAGE_EVENTS } from \"../events.js\";\nimport { IWcBindable, StorageType } from \"../types.js\";\nimport { StorageCore } from \"../core/StorageCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Storage extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...StorageCore.wcBindable,\n properties: [\n ...StorageCore.wcBindable.properties,\n { name: \"trigger\", event: STORAGE_EVENTS.triggerChanged },\n ],\n // Shell-level input surface. The Core declares only the portable `key` / `type`;\n // the Shell adds the DOM-driven settable surface. No `attribute` hints are given:\n // the `key` / `type` / `manual` setters already reflect to their attributes, so a\n // binding system that mirrors inputs[].attribute would set the attribute twice\n // (`value` / `trigger` are not attribute-backed). `commands` (load / save / remove)\n // are inherited unchanged from the Core via the spread above.\n inputs: [\n { name: \"key\" },\n { name: \"type\" },\n { name: \"value\" },\n { name: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"key\", \"type\"]; }\n\n private _core: StorageCore;\n private _trigger: boolean = false;\n // Storage load()/save() are synchronous, so connection work never defers.\n // This stays an already-resolved Promise for the whole lifecycle; it exists\n // only to satisfy the `hasConnectedCallbackPromise` protocol (consumers may\n // `await el.connectedCallbackPromise`). connectedCallback intentionally does\n // not reassign it — there is nothing async to wait for, unlike <wcs-fetch>.\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new StorageCore(this);\n }\n\n // Push the Shell's current attribute-derived key / type down into the Core.\n // Every operation (load / save / remove / value setter) and every lifecycle\n // hook that may run a Core operation or cross-tab sync must do this first, so\n // the Core never acts on a stale key / type. Centralizing it here avoids the\n // previous pattern of repeating `_core.key = …; _core.type = …;` at each call\n // site, which risked a future call site forgetting one of the two.\n private _syncCore(): void {\n this._core.key = this.key;\n this._core.type = this.type;\n }\n\n get key(): string {\n return this.getAttribute(\"key\") || \"\";\n }\n\n set key(value: string) {\n this.setAttribute(\"key\", value);\n }\n\n get type(): StorageType {\n // Normalize at the Shell boundary: any attribute value other than the\n // exact \"session\" falls back to \"local\". This keeps an invalid attribute\n // (e.g. type=\"foo\") from reaching the Core's validating setter and throwing\n // out of setAttribute / connectedCallback.\n return this.getAttribute(\"type\") === \"session\" ? \"session\" : \"local\";\n }\n\n set type(value: StorageType) {\n this.setAttribute(\"type\", value);\n }\n\n get value(): any {\n return this._core.value;\n }\n\n set value(v: any) {\n // Non-manual mode: assigning `value` auto-saves the *assigned* argument `v`\n // (write-through). Note this differs from save()/trigger, which persist the\n // *current* `_core.value` (which load() or a cross-tab `storage` event may\n // have updated). See README \"Design Notes\" for the rationale.\n //\n // Manual mode: assigning `value` does NOT persist — it only stages the value\n // into the Core (no storage write). This keeps the getter/setter consistent\n // (`el.value = x; el.value === x`) and lets a later save()/trigger commit the\n // staged value, so a `value: …` + `trigger: …` binding pair works as\n // documented. The actual write still happens only via save()/trigger.\n if (!this.manual) {\n this._syncCore();\n this._core.save(v);\n } else {\n this._core.value = v;\n }\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n const v = !!value;\n if (v) {\n this._trigger = true;\n // save() may raise (e.g. key unset). Guarantee the trigger resets to\n // false and the completion event fires even on failure, so the trigger\n // never gets stuck in the `true` state.\n try {\n this.save();\n } finally {\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(STORAGE_EVENTS.triggerChanged, {\n detail: false,\n bubbles: true,\n }));\n }\n }\n }\n\n load(): any {\n this._syncCore();\n return this._core.load();\n }\n\n // The `save` command differs in arity between the two CSBC surfaces:\n // - Core: save(value) — caller supplies the value to persist\n // - Shell: save() — persists the current `_core.value` (no argument)\n // Both are exposed under the same `commands` entry name \"save\". The protocol\n // `commands` list is descriptive metadata only and carries no arity, so this\n // is not a protocol violation; the difference is contractual and documented\n // in the README (\"Design Notes\").\n save(): void {\n this._syncCore();\n this._core.save(this._core.value);\n }\n\n remove(): void {\n this._syncCore();\n this._core.remove();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected) return;\n if (name === \"key\") {\n // Always keep the Core's key in sync with the attribute, regardless of\n // mode or whether the new value is empty. The cross-tab `storage` listener\n // compares `e.key !== _core.key`, so a stale Core key would make sync watch\n // the wrong (old/empty) key after a runtime key change. load() (which also\n // syncs the Core) only runs for non-manual mode with a non-empty key.\n this._syncCore();\n if (newValue && !this.manual) {\n this.load();\n }\n }\n if (name === \"type\") {\n // Route through the normalizing getter so an invalid attribute value\n // (e.g. type=\"foo\") falls back to \"local\" instead of throwing.\n this._syncCore();\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.key) {\n this.load();\n }\n // Always bind the cross-tab watcher to the Shell's current key/type before\n // starting sync. In paths where load()/save() never run (e.g. manual mode,\n // or key set via JS without a load), _core.key/_core.type would otherwise\n // keep a stale/empty value and the storage listener's `e.key !== _key`\n // check would compare against the wrong key. This also covers detach →\n // re-attach: stale Core key from a previous session is overwritten here.\n this._syncCore();\n this._core.startSync();\n }\n\n disconnectedCallback(): void {\n this._core.stopSync();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapStorage(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { Storage } from \"./components/Storage.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.storage)) {\n customElements.define(config.tagNames.storage, Storage);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","tagNames","storage","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","STORAGE_EVENTS","raiseError","message","Error","StorageCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","inputs","commands","_target","_value","_loading","_error","_key","_type","_storageListener","constructor","target","super","this","value","v","is","_setValue","loading","error","String","type","_getStorage","sessionStorage","localStorage","_setLoading","dispatchEvent","CustomEvent","bubbles","_setError","_toStorageError","operation","load","raw","getItem","JSON","parse","save","removeItem","setItem","stringify","remove","startSync","newValue","globalThis","addEventListener","stopSync","removeEventListener","registered","handleClick","Element","triggerElement","closest","storageId","getAttribute","StorageCtor","customElements","get","storageElement","document","getElementById","preventDefault","Storage","HTMLElement","wcBindable","observedAttributes","_core","_trigger","_connectedCallbackPromise","Promise","resolve","_syncCore","setAttribute","manual","connectedCallbackPromise","hasAttribute","removeAttribute","trigger","attributeChangedCallback","_oldValue","isConnected","connectedCallback","style","display","disconnectedCallback","bootstrapStorage","userConfig","partialConfig","entries","setConfig","define"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/storage",
3
- "version": "1.11.1",
3
+ "version": "1.12.1",
4
4
  "description": "Declarative persistent storage component for Web Components. Framework-agnostic localStorage/sessionStorage via wc-bindable-protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",