Can-Local-Store
can-local-store is a client-side data persistence library for the CanJS framework, providing a localStorage-backed database for `can/Map` and `can/List` instances. It enables developers to automatically serialize and deserialize data to and from the browser's `localStorage` API, making it easy to store and retrieve application state or user data directly in the browser. The current stable version is 1.0.1, last released in October 2017. Due to its age and lack of updates since then, the package is considered abandoned, with no ongoing development or maintenance. Its key differentiator was its deep integration with CanJS's observable data structures, allowing seamless persistence without boilerplate. However, modern CanJS applications or new projects would typically opt for more actively maintained state management solutions or direct `localStorage` wrappers.
Common errors
-
DOMException: QuotaExceededError: The quota has been exceeded.
cause Attempting to write more data to localStorage than the browser's allocated limit (typically 5-10 MB per origin) allows.fixReduce the amount of data being stored, implement a caching strategy to only store essential data, or migrate to a different storage mechanism like IndexedDB for larger datasets. Ensure `localStorage.removeItem()` or `localStorage.clear()` is used for cleanup when data is no longer needed. -
TypeError: Converting circular structure to JSON
cause `can-local-store` uses `JSON.stringify` internally, which cannot handle objects with circular references (where an object directly or indirectly refers back to itself).fixBefore saving, ensure that any data passed to `can-local-store` (via `Map` or `List` instances) does not contain circular references. Manually transform the data to a non-circular structure if necessary. -
Property 'save' does not exist on type 'Map<T>'
cause This usually indicates an issue with how the CanJS `Map` is defined or how `can-local-store/map` is integrated, possibly due to incorrect peer dependency versions or an outdated CanJS setup.fixVerify that `can-define` and other peer dependencies are installed and compatible with `can-local-store@1.0.1`. Ensure the `Map` is correctly extended with `can-local-store/map` and that the instance methods like `save()` are available on the created model instance.
Warnings
- breaking The package is considered abandoned, with its last release in October 2017. It is unlikely to receive updates for newer browser features, JavaScript standards, or security patches, and may have compatibility issues with modern CanJS versions (beyond those specified in its peer dependencies) or other contemporary libraries.
- gotcha `localStorage` has a limited storage capacity (typically 5-10 MB per origin). Attempting to store too much data will result in a `QuotaExceededError` or data loss, without graceful degradation unless explicitly handled.
- gotcha `localStorage` operations are synchronous and can block the main thread. Storing or retrieving large amounts of data can lead to UI freezes and a poor user experience, especially on slower devices.
- gotcha Data stored in `localStorage` is not secure and can be accessed or modified by any JavaScript code running on the same origin (e.g., via Cross-Site Scripting). Sensitive user information, authentication tokens, or highly confidential data should never be stored here.
- gotcha can-local-store automatically serializes data using `JSON.stringify` and deserializes using `JSON.parse`. This means only JSON-serializable data types (strings, numbers, booleans, null, arrays, plain objects) can be reliably stored. Custom classes, Dates, RegExps, or functions will lose their type or information upon serialization.
Install
-
npm install can-local-store -
yarn add can-local-store -
pnpm add can-local-store
Imports
- Map
import { Map } from 'can-local-store'; // Incorrect named import from root import Map from 'can-local-store'; // Root default import usually refers to the main 'Map' store, but subpath is clearerimport Map from 'can-local-store/map';
- List
import { List } from 'can-local-store'; // Incorrect named import from rootimport List from 'can-local-store/list';
- localStore
const localStore = require('can-local-store');import localStore from 'can-local-store'; // Or import Store from 'can-local-store';
Quickstart
import Map from 'can-local-store/map';
// Define a Todo model that uses can-local-store/map
interface TodoAttributes {
id?: number;
name: string;
completed: boolean;
}
const Todo = Map.extend<TodoAttributes>('todo', {
// `todo` is the localStorage key for this model's data
seal: false, // Recommended for can-define-stream compatibility
init: function(this: any) {
if (!this.id) {
// Simple ID generation for example purposes
this.id = Date.now();
}
}
});
async function runExample() {
console.log('--- can-local-store example ---');
// Create a new todo item
const newTodo = new Todo({ name: 'Learn can-local-store', completed: false });
await newTodo.save(); // Saves to localStorage under the key 'todo'
console.log('Created todo:', newTodo.serialize());
console.log('localStorage content (key "todo"):', localStorage.getItem('todo'));
// Find all todos (loads from localStorage)
const todos = await Todo.findAll({});
console.log('All todos after creation:', todos.serialize());
// Update an existing todo
const firstTodo = todos.get(0);
if (firstTodo) {
firstTodo.completed = true;
await firstTodo.save(); // Updates in localStorage
console.log('Updated todo:', firstTodo.serialize());
console.log('localStorage content (key "todo"):', localStorage.getItem('todo'));
}
// Find a specific todo by ID
const foundTodo = await Todo.findOne({ id: newTodo.id });
console.log('Found todo by ID:', foundTodo?.serialize());
// Delete a todo
if (foundTodo) {
await foundTodo.destroy(); // Removes from localStorage
console.log('Deleted todo:', foundTodo.serialize());
console.log('localStorage content (key "todo"):', localStorage.getItem('todo'));
}
// Verify deletion by fetching all remaining todos
const remainingTodos = await Todo.findAll({});
console.log('Remaining todos:', remainingTodos.serialize());
// Clean up localStorage completely for this example
localStorage.removeItem('todo');
console.log('localStorage "todo" key cleared.');
}
runExample().catch(console.error);