{"version":3,"file":"atom.mjs","names":[],"sources":["../../../../@mongez/atom/src/atom.ts"],"sourcesContent":["/* eslint-disable no-multi-assign */\n/* eslint-disable guard-for-in */\n/* eslint-disable @typescript-eslint/no-shadow */\n/* eslint-disable prefer-template */\nimport events, { EventSubscription } from \"@mongez/events\";\nimport { clone, get } from \"@mongez/reinforcements\";\nimport { attachPersist, resolvePersistAdapter } from \"./persist\";\nimport {\n  Atom,\n  AtomActions,\n  AtomOptions,\n  AtomPartialChangeCallback,\n  AtomValue,\n} from \"./types\";\n\nexport const atoms: Record<string, Atom<any>> = {};\n\nlet cloneCounter = 0;\n\n/**\n * Get atom by name\n */\nexport function getAtom<T>(name: string): Atom<T> | undefined {\n  return atoms[name];\n}\n\n/**\n * Options that control how an atom is constructed.\n * Internal-only; used by store-scoped clones to opt out of the global registry.\n */\nexport type CreateAtomOptions = {\n  /**\n   * When false, the new atom will NOT be inserted into the module-level\n   * `atoms` registry. Used by `AtomStore` to create per-store clones that\n   * stay isolated from the global lookup table.\n   *\n   * Defaults to true.\n   */\n  register?: boolean;\n};\n\n/**\n * Create a new atom\n */\nexport function createAtom<\n  Value = any,\n  Actions extends AtomActions<Value> = AtomActions<Value>\n>(\n  data: AtomOptions<AtomValue<Value>, Actions>,\n  options: CreateAtomOptions = {}\n): Atom<Value, Actions> {\n  let defaultValue = data.default;\n  let atomValue = data.default;\n\n  let atomValueIsObject = false;\n\n  if (defaultValue && typeof defaultValue === \"object\") {\n    atomValue = defaultValue = clone(defaultValue);\n    atomValueIsObject = true;\n  }\n\n  const atomType = Array.isArray(defaultValue) ? \"array\" : typeof defaultValue;\n\n  const atomEvent = `atoms.${data.key}`;\n\n  const event = (type: string): string => `${atomEvent}.${type}`;\n\n  const watchers: any = {};\n\n  const atomKey = data.key;\n\n  const atom: Atom<Value, Actions> = {\n    default: defaultValue,\n    currentValue: atomValue,\n    key: atomKey,\n    get type() {\n      return atomType;\n    },\n    watch<T extends keyof Value>(\n      key: T,\n      callback: AtomPartialChangeCallback\n    ): EventSubscription {\n      if (!watchers[key]) {\n        watchers[key] = [];\n      }\n\n      watchers[key].push(callback);\n\n      return {\n        unsubscribe: () => {\n          watchers[key] = watchers[key].filter(\n            (cb: AtomPartialChangeCallback) => cb !== callback\n          );\n        },\n      } as EventSubscription;\n    },\n    get defaultValue(): Value {\n      return this.default;\n    },\n    get value(): Value {\n      return this.currentValue;\n    },\n    change<T extends keyof Value>(key: T, newValue: any) {\n      this.update({\n        ...this.currentValue,\n        [key]: newValue,\n      });\n    },\n    silentChange<T extends keyof Value>(key: T, newValue: any) {\n      this.silentUpdate({\n        ...this.currentValue,\n        [key]: newValue,\n      });\n    },\n    merge(newValue: Partial<Value>) {\n      this.update({\n        ...this.currentValue,\n        ...newValue,\n      });\n    },\n    update(newValue: (oldValue: Value, atom: Atom<Value, Actions>) => Value) {\n      if (newValue === this.currentValue) return;\n\n      const oldValue = this.currentValue;\n      let updatedValue: Value;\n\n      if (typeof newValue === \"function\") {\n        updatedValue = newValue(oldValue, this);\n      } else {\n        updatedValue = newValue;\n      }\n\n      if (data.beforeUpdate) {\n        const beforeUpdateOutput = data.beforeUpdate(\n          updatedValue,\n          oldValue,\n          this\n        );\n\n        if (beforeUpdateOutput !== undefined) {\n          updatedValue = beforeUpdateOutput;\n        }\n      }\n\n      this.currentValue = updatedValue;\n      events.trigger(event(\"update\"), this.currentValue, oldValue, this);\n      if (atomValueIsObject) {\n        for (const key in watchers) {\n          const keyOldValue = get(oldValue, key);\n          const keyNewValue = get(updatedValue, key);\n\n          if (keyOldValue !== keyNewValue) {\n            watchers[key].forEach(\n              (callback: AtomPartialChangeCallback) =>\n                callback(keyNewValue, keyOldValue, this)\n            );\n          }\n        }\n      }\n    },\n    silentUpdate(\n      newValue: ((oldValue: Value, atom: Atom<Value>) => Value) | Value\n    ) {\n      if (newValue === this.currentValue) return;\n\n      const oldValue = this.currentValue;\n      let updatedValue: Value;\n\n      if (typeof newValue === \"function\") {\n        updatedValue = (newValue as any)(oldValue, this);\n      } else {\n        updatedValue = newValue;\n      }\n\n      if (data.beforeUpdate) {\n        const beforeUpdateOutput = data.beforeUpdate(\n          updatedValue,\n          oldValue,\n          this\n        );\n\n        if (beforeUpdateOutput !== undefined) {\n          updatedValue = beforeUpdateOutput;\n        }\n      }\n\n      this.currentValue = updatedValue;\n    },\n    onChange(\n      callback: (newValue: Value, oldValue: Value, atom: Atom<Value>) => void\n    ): EventSubscription {\n      return events.subscribe(event(\"update\"), callback);\n    },\n    onReset(callback: (atom: Atom<Value>) => void): EventSubscription {\n      return events.subscribe(event(\"reset\"), callback);\n    },\n    get<T extends keyof Value>(key: T, defaultValue?: any): Value[T] {\n      if (data.get) {\n        return data.get(\n          key as string,\n          defaultValue,\n          this.currentValue\n        ) as Value[T];\n      }\n\n      return get(this.currentValue, key as string, defaultValue);\n    },\n    destroy() {\n      events.trigger(event(\"delete\"), this);\n\n      events.unsubscribeNamespace(atomEvent);\n      delete atoms[this.key];\n    },\n    onDestroy(callback: (atom: Atom<Value>) => void): EventSubscription {\n      return events.subscribe(`atoms.${this.key}.delete`, callback);\n    },\n    reset() {\n      this.update(clone(this.defaultValue));\n      events.trigger(event(\"reset\"), this);\n    },\n    /**\n     * Reset the value without triggering the update event\n     * But this will trigger the reset event\n     */\n    silentReset() {\n      this.currentValue = clone(this.defaultValue);\n      events.trigger(event(\"reset\"), this);\n    },\n    clone(cloneOptions?: CreateAtomOptions) {\n      return createAtom(\n        {\n          key: this.key + \".clone.\" + (++cloneCounter),\n          default: clone(this.currentValue),\n          beforeUpdate: data.beforeUpdate,\n          get: data.get,\n          onUpdate: data.onUpdate,\n          actions: data.actions,\n        },\n        { register: cloneOptions?.register ?? true }\n      );\n    },\n  } as any;\n\n  // Install actions on the atom instance.\n  //\n  // Three kinds of entries can appear in `actions`:\n  //   1. Plain functions — bound to the atom so `this` refers to it.\n  //   2. Property getters (e.g. `atomCollection`'s `length`) — forwarded\n  //      as getters bound to the atom; calling `.bind(...)` on them would\n  //      blow up because the getter is invoked the moment we touch it.\n  //   3. Anything else — assigned by value as a fallback.\n  if (data.actions) {\n    const actions = data.actions as Record<string, unknown>;\n    for (const actionKey of Object.keys(actions)) {\n      const descriptor = Object.getOwnPropertyDescriptor(actions, actionKey);\n      if (descriptor?.get) {\n        Object.defineProperty(atom, actionKey, {\n          get: descriptor.get.bind(atom),\n          set: descriptor.set ? descriptor.set.bind(atom) : undefined,\n          enumerable: true,\n          configurable: true,\n        });\n        continue;\n      }\n      const value = actions[actionKey];\n      if (typeof value === \"function\") {\n        (atom as any)[actionKey] = (value as Function).bind(atom);\n      } else {\n        (atom as any)[actionKey] = value;\n      }\n    }\n  }\n\n  if (data.onUpdate) {\n    events.subscribe(event(\"update\"), data.onUpdate.bind(atom));\n  }\n\n  if (options.register !== false) {\n    atoms[atomKey] = atom;\n  }\n\n  // Persistence wiring. Resolve the adapter (boolean → built-in\n  // localStorage, object → as-is, falsy → skip). The adapter handles\n  // the initial read and the write-through; we just hand it the atom.\n  const adapter = resolvePersistAdapter(data.persist);\n  if (adapter) {\n    attachPersist(atom, adapter, data);\n  }\n\n  return atom;\n}\n\n/**\n * Get all atoms list\n */\nexport function atomsList(): Atom<any>[] {\n  return Object.values(atoms);\n}\n\n/**\n * Return atoms in object format\n */\nexport function atomsObject(): Record<string, Atom<any>> {\n  return atoms;\n}\n"],"mappings":"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"}