Skip to content
IN-MEMORY • NAMESPACE-AWARE • TYPESCRIPT

Transient state.
Properly organized.

MemoryX is a lightweight, namespace-aware in-memory key-value store for JavaScript and TypeScript. Keep transient browser data isolated, addressable and observable — without bringing a full state-management stack.

Open source • MIT • ESM • v2.1.1
PLAYGROUND

A memory layer you can actually see.

Namespaces create boundaries. Paths make nested data addressable. Subscriptions make mutations observable. This is real MemoryX running in your browser.

GLOBAL ROOT · window._oOMemoryXOo_ → app
NAMESPACE
Instances with same namespace share state — even across bundles.
TREE · app2 top-level keys
user{3}
└─ nameAlicename
└─ emailalice@example.comemail
├─ prefs{2}
└─ themedarktheme
└─ alertstruealerts
cart{2}
└─ count3count
items["book","lamp","cable"]
Try: user.prefs.theme → light
MUTATION LOG0 entries
No mutations yet — change a value.

Not every piece of state deserves
a state-management framework.

Sometimes you need a small, explicit memory layer for transient application data: preferences, UI state, session-scoped data, feature-local caches, temporary objects and other runtime values. MemoryX gives that data a home without forcing an architectural commitment around it.

local variableMemoryX — small + structuredfull state platform
01 — NAMESPACES

One memory space.
Multiple boundaries.

Namespaces keep unrelated data isolated while still living inside the same shared in-memory root. Same namespace — shared state. Different namespaces — independent.

APP
usercartpreferences
ANALYTICS
eventspageviewstimings
SESSION
tokenstatusflags
02 — DEEP PATHS

Address data the way
your object is shaped.

Lodash-compatible property paths — dot notation, array paths and bracket syntax — so nested values are addressable without manually walking object trees.

user.profile.name"Alice"
memory.get('user.profile.name')memory.set('a[0].b["k.k"]', 1)memory.has(['user','name'])
03 — OBSERVABLE

When memory changes,
you can listen.

Subscribe to a path or to the whole namespace. Notifications are synchronous and tied directly to mutations via subscribe.

SET user.name
├─ subscriber: user└─ subscriber: *
returns unsubscribe()

Small API.
Deliberate surface area.

api.tsTS • ESM
import { MemoryX } from '@darcas/memoryx'

const memory = new MemoryX('app')

memory.set('user.name', 'Alice')
memory.set('user.age', 31)

memory.get('user.name')        // "Alice"
memory.has('user.name')        // true
memory.inc('user.age')         // 32
memory.merge('user', { age: 32,  })
memory.push('cart.items', 'book')

memory.subscribe('user', (value, previous) => {
  console.log({ value, previous })
})

memory.snapshot()               // JSON string
memory.restore(json)            // throws TypeError if invalid
METHOD MATRIX
COREget, set, del, has, destroy
INSPECTall, keys, MemoryX.namespaces
MUTATEmerge, push, inc, dec
OBSERVEsubscribe
STATE TRANSFERsnapshot, restore
Paths: string | string[] | number | symbol — lodash castPath semantics, bracket & dot compatible.

Small by design.
Not small by accident.

EXPLICIT SCOPE

Memory is organised by namespace rather than hidden in a giant application-wide object.

FAMILIAR PATHS

Property-path semantics follow the lodash mental model — differential-tested against real lodash.

OBSERVABLE

Subscriptions are synchronous, once per mutation, ancestor-aware. Listener exceptions propagate.

DEFENSIVE INTERNALS

Path writes to __proto__, constructor or prototype abort the whole operation — prototype-pollution guard.

Know what it is.
Know what it isn’t.

IT IS
  • in-memory storage (session-scoped)
  • namespace-aware, key-value, path-addressable
  • synchronously observable (no batching)
  • browser-first, globalThis fallback for SSR/Node
  • ESM-only, ES2015, MIT licensed
  • shallow all() by reference · JSON snapshot limits
IT IS NOT
  • browser persistence across restarts
  • a database or distributed cache
  • an encrypted secret store
  • asynchronous or framework-reactive state
  • a complete state-management framework
  • persistent / server-side storage

Install it.
Give memory a name.

bash
$ npm install @darcas/memoryx
app.ts
import { MemoryX } from '@darcas/memoryx'

const memory = new MemoryX('app')

memory.set('user.name', 'Alice')
memory.set('user.age', 31)

const name = memory.get('user.name')
memory.inc('user.age')
subscribe.ts
const unsubscribe = memory.subscribe('user', (value, previous) => {
  console.log({ value, previous })
})
BEHAVIOR NOTES
  • get() returns default only when value is undefined — use has() to check existence.
  • all() is shallow copy by reference; nested objects mutate the store.
  • snapshot() is JSON — Maps/Sets/functions degrade.

Memory belongs in the open.

MemoryX is open source under the MIT license. Read the source, inspect the implementation, run the tests, or contribute on GitHub.

Questions about MemoryX.

What is MemoryX?
MemoryX is a lightweight, namespace-aware in-memory key-value storage library for JavaScript and TypeScript, designed primarily for browser environments. It keeps transient data organized, isolated and observable without a heavyweight state-management stack.
Does MemoryX persist data across page reloads?
No. MemoryX stores data in memory through a shared global object (window._oOMemoryXOo_ falling back to globalThis in SSR). It is not a browser persistence layer such as localStorage or IndexedDB.
What is a namespace in MemoryX?
A namespace is an isolated storage scope. Instances using the same namespace share state — even across separate bundles on the same page — while different namespaces keep their data separate.
Can MemoryX store nested data?
Yes. MemoryX supports deep property paths with lodash-compatible semantics: dot notation (user.name), array paths (["user","name"]) and bracket syntax (a[0], a["k.k"]).
Can I subscribe to changes?
Yes. subscribe(path, listener) observes changes at a path and its descendants. The "*" wildcard listens to every change in a namespace. Notifications are synchronous, once per mutation, and return an unsubscribe function.
Is MemoryX compatible with SSR?
Yes. MemoryX resolves its root from window when available and falls back to globalThis, so it runs in SSR/Node. In Node the shared store is process-local and not shared across workers.
Is MemoryX persistent?
No. It is deliberately an in-memory utility rather than persistent storage. Values live for the session/environment lifetime.
Is MemoryX ESM compatible?
Yes. The package is ESM-only ("type": "module") and targets ES2015. CommonJS consumers need to use dynamic import().
Is MemoryX safe against prototype pollution through property paths?
Yes. Writes containing __proto__, constructor or prototype abort the entire operation — an intentional divergence from lodash that guards against prototype pollution.
What license does MemoryX use?
MemoryX is released under the MIT license.