strata-storage 2.4.2 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-INTEGRATION-GUIDE.md +208 -0
- package/README.md +427 -181
- package/android/AGENTS.md +34 -0
- package/android/CLAUDE.md +51 -0
- package/android/src/main/java/com/strata/storage/SQLiteStorage.java +35 -0
- package/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +191 -27
- package/dist/README.md +427 -181
- package/dist/adapters/capacitor/FilesystemAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/FilesystemAdapter.js +2 -1
- package/dist/adapters/capacitor/PreferencesAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/PreferencesAdapter.js +2 -1
- package/dist/adapters/capacitor/SecureAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/SecureAdapter.js +2 -1
- package/dist/adapters/capacitor/SqliteAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/SqliteAdapter.js +2 -1
- package/dist/adapters/web/CacheAdapter.d.ts.map +1 -1
- package/dist/adapters/web/CacheAdapter.js +11 -3
- package/dist/adapters/web/CookieAdapter.d.ts +37 -1
- package/dist/adapters/web/CookieAdapter.d.ts.map +1 -1
- package/dist/adapters/web/CookieAdapter.js +89 -9
- package/dist/adapters/web/IndexedDBAdapter.d.ts.map +1 -1
- package/dist/adapters/web/IndexedDBAdapter.js +10 -2
- package/dist/adapters/web/LocalStorageAdapter.d.ts +31 -0
- package/dist/adapters/web/LocalStorageAdapter.d.ts.map +1 -1
- package/dist/adapters/web/LocalStorageAdapter.js +92 -19
- package/dist/adapters/web/MemoryAdapter.d.ts +24 -0
- package/dist/adapters/web/MemoryAdapter.d.ts.map +1 -1
- package/dist/adapters/web/MemoryAdapter.js +69 -18
- package/dist/adapters/web/SessionStorageAdapter.d.ts +24 -0
- package/dist/adapters/web/SessionStorageAdapter.d.ts.map +1 -1
- package/dist/adapters/web/SessionStorageAdapter.js +71 -9
- package/dist/adapters/web/URLAdapter.d.ts +59 -0
- package/dist/adapters/web/URLAdapter.d.ts.map +1 -0
- package/dist/adapters/web/URLAdapter.js +234 -0
- package/dist/adapters/web/index.d.ts +1 -0
- package/dist/adapters/web/index.d.ts.map +1 -1
- package/dist/adapters/web/index.js +1 -0
- package/dist/android/AGENTS.md +34 -0
- package/dist/android/CLAUDE.md +51 -0
- package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +35 -0
- package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +191 -27
- package/dist/capacitor.d.ts.map +1 -1
- package/dist/capacitor.js +2 -1
- package/dist/config/support.d.ts +10 -0
- package/dist/config/support.d.ts.map +1 -0
- package/dist/config/support.js +9 -0
- package/dist/core/BaseAdapter.d.ts +8 -0
- package/dist/core/BaseAdapter.d.ts.map +1 -1
- package/dist/core/BaseAdapter.js +34 -14
- package/dist/core/Strata.d.ts +56 -2
- package/dist/core/Strata.d.ts.map +1 -1
- package/dist/core/Strata.js +501 -53
- package/dist/features/encryption.d.ts.map +1 -1
- package/dist/features/encryption.js +3 -2
- package/dist/features/integrity.d.ts +16 -0
- package/dist/features/integrity.d.ts.map +1 -0
- package/dist/features/integrity.js +28 -0
- package/dist/features/observer.d.ts.map +1 -1
- package/dist/features/observer.js +2 -1
- package/dist/features/query.d.ts +7 -1
- package/dist/features/query.d.ts.map +1 -1
- package/dist/features/query.js +9 -2
- package/dist/features/sync.d.ts.map +1 -1
- package/dist/features/sync.js +4 -3
- package/dist/index.d.ts +35 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -30
- package/dist/integrations/angular/index.d.ts +158 -0
- package/dist/integrations/angular/index.d.ts.map +1 -0
- package/dist/integrations/angular/index.js +395 -0
- package/dist/integrations/index.d.ts +15 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +18 -0
- package/dist/integrations/react/index.d.ts +75 -0
- package/dist/integrations/react/index.d.ts.map +1 -0
- package/dist/integrations/react/index.js +191 -0
- package/dist/integrations/vue/index.d.ts +103 -0
- package/dist/integrations/vue/index.d.ts.map +1 -0
- package/dist/integrations/vue/index.js +274 -0
- package/dist/ios/AGENTS.md +33 -0
- package/dist/ios/CLAUDE.md +49 -0
- package/dist/ios/Plugin/KeychainStorage.swift +139 -50
- package/dist/ios/Plugin/SQLiteStorage.swift +40 -0
- package/dist/ios/Plugin/StrataStoragePlugin.m +23 -0
- package/dist/ios/Plugin/StrataStoragePlugin.swift +201 -52
- package/dist/package.json +21 -5
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +2 -1
- package/dist/types/index.d.ts +58 -9
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -13
- package/dist/utils/errors.d.ts +7 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +15 -3
- package/dist/utils/index.d.ts +63 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +109 -16
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +63 -0
- package/ios/AGENTS.md +33 -0
- package/ios/CLAUDE.md +49 -0
- package/ios/Plugin/KeychainStorage.swift +139 -50
- package/ios/Plugin/SQLiteStorage.swift +40 -0
- package/ios/Plugin/StrataStoragePlugin.m +23 -0
- package/ios/Plugin/StrataStoragePlugin.swift +201 -52
- package/package.json +35 -23
- package/scripts/build.js +16 -5
- package/scripts/configure.js +2 -6
- package/scripts/postinstall.js +2 -2
- package/Readme.md +0 -271
package/README.md
CHANGED
|
@@ -1,269 +1,517 @@
|
|
|
1
1
|
# Strata Storage
|
|
2
2
|
|
|
3
|
-
> Zero-dependency universal storage
|
|
3
|
+
> Zero-dependency universal storage for the web, iOS, and Android. One API for `localStorage`, IndexedDB, cookies, the URL, native Keychain/Keystore, SQLite, and more — with optional React, Vue, Angular, Capacitor, and Firebase surfaces.
|
|
4
|
+
|
|
5
|
+
- **[AI Integration Guide](./AI-INTEGRATION-GUIDE.md)** — quick reference for AI development agents (Claude Code, Cursor, Copilot).
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/strata-storage)
|
|
6
8
|
[](LICENSE)
|
|
7
|
-
[](https://www.typescriptlang.org/)
|
|
8
10
|
[](https://github.com/aoneahsan/strata-storage)
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
- **Version:** `2.5.0`
|
|
13
|
+
- **License:** Apache-2.0
|
|
14
|
+
- **Node.js:** `>= 24.13.0`
|
|
15
|
+
- **Module format:** ESM only
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
- **🌐 Universal API** - Single consistent API across web, iOS, and Android
|
|
14
|
-
- **🔒 Built-in Encryption** - Secure data storage using native crypto APIs
|
|
15
|
-
- **📦 Compression** - Automatic data compression for large objects
|
|
16
|
-
- **⏱️ TTL Support** - Automatic expiration with time-to-live
|
|
17
|
-
- **🔄 Cross-Tab Sync** - Real-time synchronization across browser tabs
|
|
18
|
-
- **🎯 Advanced Queries** - Tag-based querying and filtering
|
|
19
|
-
- **📱 Mobile Ready** - Native iOS and Android storage with Capacitor
|
|
20
|
-
- **💾 Multiple Adapters** - localStorage, IndexedDB, SQLite, Keychain, and more
|
|
21
|
-
- **🎨 Framework Integrations** - React hooks, Vue composables, Angular services
|
|
17
|
+
## Why Strata Storage
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
Every product re-solves the same storage problem: pick a backend per platform, learn its quirks, wrap it for your framework, and bolt on encryption, expiry, and cross-tab sync by hand. Strata Storage replaces that with one adapter-based API that runs everywhere and keeps the runtime package free of dependencies.
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
- **Zero runtime dependencies.** The core is pure TypeScript. React, Vue, Angular, and `@capacitor/core` are optional peer dependencies — install only what you use.
|
|
22
|
+
- **One API, every backend.** `get`/`set`/`remove`/`query`/`subscribe` behave the same whether the value lives in `localStorage`, IndexedDB, the URL, or the iOS Keychain.
|
|
23
|
+
- **Provider-free.** `defineStorage()` returns a ready-to-use instance you create once and import anywhere — no React context, Vue plugin, or Angular module required (the Provider/Plugin/Module styles still work if you prefer them).
|
|
24
|
+
- **Opt-in power features.** Encryption, compression, TTL, queries, cross-tab sync, integrity checksums, durable writes, mirroring, and snapshots are all off by default and added per call or per instance.
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
## Installation
|
|
30
27
|
|
|
31
28
|
```bash
|
|
32
29
|
yarn add strata-storage
|
|
33
30
|
```
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
Framework adapters import from sub-paths; no extra install beyond the framework itself:
|
|
36
33
|
|
|
37
34
|
```bash
|
|
38
|
-
|
|
35
|
+
# React / Vue / Angular peers are optional — install the one you use
|
|
36
|
+
yarn add react # for strata-storage/react
|
|
37
|
+
yarn add vue # for strata-storage/vue
|
|
38
|
+
yarn add @angular/core @angular/forms # for strata-storage/angular
|
|
39
|
+
yarn add @capacitor/core # for strata-storage/capacitor
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
## Quick Start
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
The shortest path is the default `storage` instance. It registers the standard web adapters and initializes lazily on first use, so importing the package does no I/O.
|
|
44
45
|
|
|
45
46
|
```typescript
|
|
46
|
-
import {
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
await storage.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
await storage.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
import { storage } from 'strata-storage';
|
|
48
|
+
|
|
49
|
+
// No setup, no Provider, no initialize() call — works immediately.
|
|
50
|
+
await storage.set('user', { id: 123, name: 'John Doe' });
|
|
51
|
+
const user = await storage.get<{ id: number; name: string }>('user');
|
|
52
|
+
|
|
53
|
+
await storage.remove('user');
|
|
54
|
+
await storage.clear();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Need your own configured instance? Use `defineStorage()` — it is the same factory the default instance is built from.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { defineStorage } from 'strata-storage';
|
|
61
|
+
|
|
62
|
+
export const storage = defineStorage({
|
|
63
|
+
defaultStorages: ['indexedDB', 'localStorage'],
|
|
64
|
+
encryption: { enabled: true, password: process.env.STORAGE_KEY! },
|
|
58
65
|
});
|
|
59
66
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const user = await storage.get('user');
|
|
67
|
+
await storage.set('token', '...', { encrypt: true });
|
|
68
|
+
```
|
|
63
69
|
|
|
64
|
-
|
|
65
|
-
await storage.remove('username');
|
|
70
|
+
`defineStorage()` registers memory, `localStorage`, `sessionStorage`, IndexedDB, cookies, and the Cache API. Add the URL adapter or native adapters yourself when you need them (see below).
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
## Provider-Free Usage
|
|
73
|
+
|
|
74
|
+
The recommended pattern across all frameworks: create one instance and bind to it. No Provider, plugin, or module is required. The Provider-based styles remain available and are documented in [docs/examples/frameworks](./docs/examples/frameworks).
|
|
75
|
+
|
|
76
|
+
### Vanilla JavaScript / TypeScript
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { defineStorage } from 'strata-storage';
|
|
80
|
+
|
|
81
|
+
export const storage = defineStorage();
|
|
82
|
+
|
|
83
|
+
await storage.set('theme', 'dark');
|
|
84
|
+
const theme = await storage.get<string>('theme');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### React
|
|
88
|
+
|
|
89
|
+
Bind the hooks to an instance once at module scope with `createStrataHooks`, then use them in any component — no `<StrataProvider>` needed.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// storage.ts
|
|
93
|
+
import { defineStorage } from 'strata-storage';
|
|
94
|
+
import { createStrataHooks } from 'strata-storage/react';
|
|
95
|
+
|
|
96
|
+
export const storage = defineStorage();
|
|
97
|
+
export const { useStorage, useStorageQuery, useStorageTTL } = createStrataHooks(storage);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
// Settings.tsx
|
|
102
|
+
import { useStorage } from './storage';
|
|
103
|
+
|
|
104
|
+
function Settings() {
|
|
105
|
+
// [value, setValue, loading]
|
|
106
|
+
const [theme, setTheme, loading] = useStorage<string>('theme', 'light');
|
|
107
|
+
|
|
108
|
+
if (loading) return <p>Loading…</p>;
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
|
|
112
|
+
Theme: {theme}
|
|
113
|
+
</button>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Prefer context? `<StrataProvider>` still works and now accepts an `instance` prop so it can wrap an instance you created yourself:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import { StrataProvider, useStorage } from 'strata-storage/react';
|
|
122
|
+
import { storage } from './storage';
|
|
123
|
+
|
|
124
|
+
<StrataProvider instance={storage}>
|
|
125
|
+
<App />
|
|
126
|
+
</StrataProvider>;
|
|
69
127
|
```
|
|
70
128
|
|
|
71
|
-
###
|
|
129
|
+
### Vue
|
|
72
130
|
|
|
73
|
-
|
|
131
|
+
`createStrataComposables` binds the composables to an instance. Each built-in composable also accepts an optional instance as its last argument, and the classic `StrataPlugin` still works.
|
|
74
132
|
|
|
75
133
|
```typescript
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
134
|
+
// storage.ts
|
|
135
|
+
import { defineStorage } from 'strata-storage';
|
|
136
|
+
import { createStrataComposables } from 'strata-storage/vue';
|
|
137
|
+
|
|
138
|
+
export const storage = defineStorage();
|
|
139
|
+
export const { useStorage, useStorageQuery, useStorageTTL } = createStrataComposables(storage);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```vue
|
|
143
|
+
<script setup lang="ts">
|
|
144
|
+
import { useStorage } from './storage';
|
|
145
|
+
|
|
146
|
+
const { value: theme, update } = useStorage<string>('theme', 'light');
|
|
147
|
+
</script>
|
|
82
148
|
|
|
83
|
-
|
|
84
|
-
|
|
149
|
+
<template>
|
|
150
|
+
<button @click="update(theme === 'light' ? 'dark' : 'light')">Theme: {{ theme }}</button>
|
|
151
|
+
</template>
|
|
85
152
|
```
|
|
86
153
|
|
|
87
|
-
|
|
154
|
+
### Angular
|
|
155
|
+
|
|
156
|
+
`provideStrata` accepts either a pre-created instance or a config object, and registers `StrataService` for injection. It works in `bootstrapApplication` (standalone) or a component's `providers`. The `STRATA_INSTANCE` token holds the instance; `StrataModule.forRoot(config)` remains for NgModule apps.
|
|
88
157
|
|
|
89
158
|
```typescript
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
159
|
+
// main.ts
|
|
160
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
161
|
+
import { defineStorage } from 'strata-storage';
|
|
162
|
+
import { provideStrata } from 'strata-storage/angular';
|
|
163
|
+
import { AppComponent } from './app/app.component';
|
|
164
|
+
|
|
165
|
+
const storage = defineStorage();
|
|
166
|
+
|
|
167
|
+
bootstrapApplication(AppComponent, {
|
|
168
|
+
providers: [provideStrata(storage)], // or provideStrata({ defaultStorages: ['indexedDB'] })
|
|
93
169
|
});
|
|
94
170
|
```
|
|
95
171
|
|
|
96
|
-
#### Compression
|
|
97
|
-
|
|
98
172
|
```typescript
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
173
|
+
// any.component.ts
|
|
174
|
+
import { Component } from '@angular/core';
|
|
175
|
+
import { StrataService } from 'strata-storage/angular';
|
|
176
|
+
|
|
177
|
+
@Component({ /* ... */ })
|
|
178
|
+
export class AnyComponent {
|
|
179
|
+
constructor(private storage: StrataService) {}
|
|
180
|
+
|
|
181
|
+
save() {
|
|
182
|
+
// StrataService methods return RxJS Observables
|
|
183
|
+
this.storage.set('theme', 'dark').subscribe();
|
|
103
184
|
}
|
|
104
|
-
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Synchronous API
|
|
189
|
+
|
|
190
|
+
For UI code that must read or write without `await` — initial render, event handlers, synchronous state hydration — Strata exposes a synchronous API alongside the async one:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { defineStorage } from 'strata-storage';
|
|
194
|
+
|
|
195
|
+
const storage = defineStorage();
|
|
105
196
|
|
|
106
|
-
|
|
107
|
-
|
|
197
|
+
storage.setSync('lastTab', 'inbox');
|
|
198
|
+
const tab = storage.getSync<string>('lastTab'); // 'inbox'
|
|
199
|
+
storage.hasSync('lastTab'); // true
|
|
200
|
+
storage.keysSync(); // string[]
|
|
201
|
+
storage.removeSync('lastTab');
|
|
202
|
+
storage.clearSync();
|
|
108
203
|
```
|
|
109
204
|
|
|
110
|
-
|
|
205
|
+
### Limitations (read these)
|
|
206
|
+
|
|
207
|
+
The sync API only works on adapters that are genuinely synchronous: **`memory`, `localStorage`, `sessionStorage`, `cookies`, and `url`**. It does not paper over async backends, and it cannot do work that is inherently asynchronous:
|
|
208
|
+
|
|
209
|
+
- Targeting an async-only adapter (`indexedDB`, `cache`, `sqlite`, `filesystem`, `secure`, `preferences`) throws a `StorageError` telling you to use the async API.
|
|
210
|
+
- `setSync` with `{ encrypt: true }` or `{ compress: true }` throws — Web Crypto and compression are async, so use `await storage.set(...)`.
|
|
211
|
+
- `getSync` on a value that was stored encrypted or compressed throws — read it with `await storage.get(...)`.
|
|
212
|
+
|
|
213
|
+
TTL, tags, and metadata work with the sync API; encryption and compression do not.
|
|
111
214
|
|
|
112
215
|
```typescript
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
216
|
+
// Pick a sync-capable backend explicitly when needed:
|
|
217
|
+
storage.setSync('filters', { status: 'open' }, { storage: 'localStorage', ttl: 60_000 });
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## URL Adapter
|
|
116
221
|
|
|
117
|
-
|
|
222
|
+
The `URLAdapter` (storage type `'url'`) persists state in the page URL so it survives reloads and round-trips through shareable/bookmarkable links — filters, the active tab, pagination, and other small UI state. It is inherently synchronous and emits change events on `popstate`/`hashchange`, so back/forward navigation and manual URL edits notify subscribers.
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { defineStorage, URLAdapter } from 'strata-storage';
|
|
118
226
|
|
|
119
|
-
|
|
227
|
+
const storage = defineStorage();
|
|
228
|
+
storage.registerAdapter(new URLAdapter());
|
|
229
|
+
|
|
230
|
+
// Write to the URL (no await needed — URL access is synchronous)
|
|
231
|
+
storage.setSync('tab', 'pending', { storage: 'url' });
|
|
232
|
+
storage.setSync('page', 3, { storage: 'url' });
|
|
233
|
+
|
|
234
|
+
// Read it back (e.g. on reload)
|
|
235
|
+
const tab = storage.getSync<string>('tab', { storage: 'url' });
|
|
236
|
+
|
|
237
|
+
// React to back/forward navigation or manual edits
|
|
120
238
|
storage.subscribe((change) => {
|
|
121
|
-
|
|
122
|
-
});
|
|
239
|
+
if (change.key === 'tab') applyTab(change.newValue);
|
|
240
|
+
}, { storage: 'url' });
|
|
123
241
|
```
|
|
124
242
|
|
|
125
|
-
|
|
243
|
+
### Configuration
|
|
126
244
|
|
|
127
|
-
|
|
128
|
-
- **localStorage** - Simple key-value storage
|
|
129
|
-
- **sessionStorage** - Session-scoped storage
|
|
130
|
-
- **IndexedDB** - Large structured data
|
|
131
|
-
- **Cookies** - Cookie-based storage
|
|
132
|
-
- **Cache API** - HTTP cache storage
|
|
133
|
-
- **Memory** - In-memory storage
|
|
245
|
+
Pass `URLAdapterConfig` when constructing the adapter:
|
|
134
246
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
- **SQLite** - Database storage
|
|
139
|
-
- **FileManager** - File-based storage
|
|
247
|
+
```typescript
|
|
248
|
+
new URLAdapter(); // defaults below
|
|
249
|
+
```
|
|
140
250
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
251
|
+
| Option | Type | Default | Meaning |
|
|
252
|
+
|--------|------|---------|---------|
|
|
253
|
+
| `mode` | `'query'` \| `'hash'` | `'query'` | Store params in the query string (`?strata.tab=...`) or the hash fragment (`#strata.tab=...`). |
|
|
254
|
+
| `prefix` | `string` | `'strata.'` | Prefix on every param name to avoid collisions with your own query params. |
|
|
255
|
+
| `history` | `'push'` \| `'replace'` | `'replace'` | Whether each write adds a browser history entry or replaces the current one. |
|
|
256
|
+
| `maxLength` | `number` | `2000` | Soft warning threshold (chars) for total URL length. |
|
|
146
257
|
|
|
147
|
-
|
|
258
|
+
The adapter is configured through `StrataConfig.adapters.url` when you let Strata manage it:
|
|
148
259
|
|
|
149
|
-
|
|
260
|
+
```typescript
|
|
261
|
+
const storage = defineStorage({ adapters: { url: { mode: 'hash', history: 'push' } } });
|
|
262
|
+
storage.registerAdapter(new URLAdapter());
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Limitations (read these)
|
|
266
|
+
|
|
267
|
+
- **Length limits.** URLs have practical limits (~2000 chars in some browsers and servers). This adapter is for small, simple, serializable state — not bulk data. Writes past `maxLength` are allowed but logged as a warning.
|
|
268
|
+
- **Server visibility.** In `'query'` mode the data is sent to the server on every navigation and appears in server/proxy logs. Use `'hash'` mode to keep it client-only (the fragment is never sent to the server).
|
|
269
|
+
- **Browser only.** Outside a browser (SSR/Node) the adapter reports unavailable; `isAvailable()` returns `false`.
|
|
270
|
+
- **Not persistent.** State lives only as long as the URL does — it is not durable storage.
|
|
271
|
+
|
|
272
|
+
## Disaster Recovery
|
|
273
|
+
|
|
274
|
+
Strata includes opt-in recovery features for data you cannot afford to silently lose. **Everything here is off by default** — enable only what you need, since each adds overhead.
|
|
275
|
+
|
|
276
|
+
### Integrity checksums
|
|
277
|
+
|
|
278
|
+
Set `integrity: true` (or per call `{ verify: true }`) to compute and store an FNV-1a checksum with each value and verify it on read. On corruption, Strata first attempts mirror read-repair (below); otherwise it honors `{ ignoreCorruption: true }` (returns `null`) or throws a typed `IntegrityError`.
|
|
150
279
|
|
|
151
280
|
```typescript
|
|
152
|
-
import {
|
|
281
|
+
import { defineStorage } from 'strata-storage';
|
|
153
282
|
|
|
154
|
-
|
|
155
|
-
const { data, loading, set, remove } = useStrata('myKey');
|
|
283
|
+
const storage = defineStorage({ integrity: true });
|
|
156
284
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
<p>{data}</p>
|
|
160
|
-
<button onClick={() => set('newValue')}>Update</button>
|
|
161
|
-
</div>
|
|
162
|
-
);
|
|
163
|
-
}
|
|
285
|
+
await storage.set('config', settings); // checksum stored
|
|
286
|
+
const config = await storage.get('config'); // verified; throws IntegrityError if corrupted
|
|
164
287
|
```
|
|
165
288
|
|
|
166
|
-
|
|
289
|
+
> **Honest note:** checksums are **FNV-1a, non-cryptographic**. They cheaply detect *accidental* corruption (truncated writes, bit flips, partial storage). They do **not** resist tampering — for tamper resistance use the encryption feature.
|
|
290
|
+
|
|
291
|
+
### Durable writes
|
|
292
|
+
|
|
293
|
+
`durableWrites: true` (or per call `{ durable: true }`) reads each value back after writing and retries on mismatch, throwing `StorageError` if it cannot confirm the write after a few attempts. This adds one read per write.
|
|
167
294
|
|
|
168
295
|
```typescript
|
|
169
|
-
|
|
296
|
+
const storage = defineStorage({ durableWrites: true });
|
|
297
|
+
await storage.set('order', order); // confirmed written, or throws
|
|
298
|
+
```
|
|
170
299
|
|
|
171
|
-
|
|
172
|
-
setup() {
|
|
173
|
-
const { data, loading, set, remove } = useStrata('myKey');
|
|
300
|
+
### Mirroring (read-repair)
|
|
174
301
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
302
|
+
`mirror: [...]` copies every write/remove to backup storage types. On a primary read miss or corruption, Strata recovers the value from a mirror and repairs the primary in place.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const storage = defineStorage({
|
|
306
|
+
defaultStorages: ['localStorage'],
|
|
307
|
+
integrity: true,
|
|
308
|
+
mirror: ['indexedDB'], // localStorage is primary; indexedDB backs it up
|
|
309
|
+
});
|
|
178
310
|
```
|
|
179
311
|
|
|
180
|
-
###
|
|
312
|
+
### Snapshots and scheduled backups
|
|
313
|
+
|
|
314
|
+
`snapshot()` produces a portable, integrity-verified backup string (embedding a checksum manifest); `restore()` validates that checksum and throws `IntegrityError` on a corrupted backup. `autoBackup` schedules periodic snapshots to a durable adapter.
|
|
181
315
|
|
|
182
316
|
```typescript
|
|
183
|
-
|
|
317
|
+
const backup = await storage.snapshot(); // store/download this string
|
|
318
|
+
await storage.restore(backup); // validates, then restores
|
|
184
319
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
320
|
+
// Scheduled, every 5 minutes, into indexedDB
|
|
321
|
+
const storage = defineStorage({
|
|
322
|
+
autoBackup: { interval: 5 * 60_000, storage: 'indexedDB' },
|
|
323
|
+
});
|
|
324
|
+
```
|
|
188
325
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
326
|
+
Integrity helpers and error classes are exported for direct use:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { computeChecksum, verifyChecksum, IntegrityError } from 'strata-storage';
|
|
193
330
|
```
|
|
194
331
|
|
|
195
|
-
##
|
|
332
|
+
## Advanced Features
|
|
196
333
|
|
|
197
|
-
|
|
198
|
-
- **[Quick Start](docs/getting-started/quick-start.md)** - Get up and running in minutes
|
|
199
|
-
- **[API Reference](docs/api/README.md)** - Complete API documentation
|
|
200
|
-
- **[Platform Guides](docs/guides/platforms/web.md)** - Platform-specific information
|
|
201
|
-
- **[Examples](docs/examples/README.md)** - Real-world usage examples
|
|
202
|
-
- **[Migration Guide](docs/MIGRATION.md)** - Migrating from other storage solutions
|
|
203
|
-
|
|
204
|
-
## Storage Adapters
|
|
205
|
-
|
|
206
|
-
| Adapter | Platform | Use Case |
|
|
207
|
-
|---------|----------|----------|
|
|
208
|
-
| `localStorage` | Web | Simple key-value, persistent |
|
|
209
|
-
| `sessionStorage` | Web | Session-scoped data |
|
|
210
|
-
| `indexedDB` | Web | Large structured data |
|
|
211
|
-
| `cookies` | Web | Cookie-based storage |
|
|
212
|
-
| `cache` | Web | HTTP cache |
|
|
213
|
-
| `memory` | All | Temporary in-memory |
|
|
214
|
-
| `preferences` | Mobile | User preferences (UserDefaults/SharedPreferences) |
|
|
215
|
-
| `secure` | Mobile | Encrypted storage (Keychain/EncryptedSharedPreferences) |
|
|
216
|
-
| `sqlite` | Mobile | Database storage |
|
|
217
|
-
| `filesystem` | Mobile | File-based storage |
|
|
334
|
+
### Encryption (async only)
|
|
218
335
|
|
|
219
|
-
|
|
336
|
+
```typescript
|
|
337
|
+
const storage = defineStorage({ encryption: { enabled: true, password: 'secret' } });
|
|
338
|
+
await storage.set('secret', { token: 'abc' }); // encrypted with AES-GCM
|
|
339
|
+
await storage.set('one-off', data, { encrypt: true }); // per-call override
|
|
340
|
+
```
|
|
220
341
|
|
|
221
|
-
|
|
222
|
-
- **TypeScript**: 5.0+ (optional, but recommended)
|
|
223
|
-
- **Capacitor**: 5.x or 6.x (for mobile platforms)
|
|
342
|
+
### TTL / expiration
|
|
224
343
|
|
|
225
|
-
|
|
344
|
+
```typescript
|
|
345
|
+
await storage.set('session', data, { ttl: 3_600_000 }); // expires in 1 hour
|
|
346
|
+
await storage.set('cache', data, { ttl: 600_000, sliding: true }); // reset on access
|
|
347
|
+
const ms = await storage.getTTL('session');
|
|
348
|
+
await storage.persist('session'); // remove expiry
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Compression (async only)
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
const storage = defineStorage({ compression: { enabled: true, threshold: 1024 } });
|
|
355
|
+
await storage.set('largePayload', bigObject); // compressed above 1KB
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Cross-tab sync
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
const storage = defineStorage({ sync: { enabled: true } });
|
|
362
|
+
storage.subscribe((change) => {
|
|
363
|
+
console.log(`${change.key} changed`, change.newValue);
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Queries
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
await storage.set('user:1', user, { tags: ['users', 'active'] });
|
|
371
|
+
const active = await storage.query({
|
|
372
|
+
tags: { $in: ['active'] },
|
|
373
|
+
'value.age': { $gte: 18 },
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Platform Support
|
|
226
378
|
|
|
227
|
-
|
|
228
|
-
- Firefox: Latest 2 versions
|
|
229
|
-
- Safari: Latest 2 versions
|
|
230
|
-
- iOS Safari: iOS 13+
|
|
231
|
-
- Android WebView: Android 8+
|
|
379
|
+
### Web (works in any JS environment)
|
|
232
380
|
|
|
233
|
-
|
|
381
|
+
| Adapter | Backend | Use case |
|
|
382
|
+
|---------|---------|----------|
|
|
383
|
+
| `memory` | In-memory `Map` | Always-available fallback, tests |
|
|
384
|
+
| `localStorage` | `window.localStorage` | Persistent key-value (~5 MB) |
|
|
385
|
+
| `sessionStorage` | `window.sessionStorage` | Session-scoped data |
|
|
386
|
+
| `indexedDB` | IndexedDB | Large structured data |
|
|
387
|
+
| `cookies` | `document.cookie` | Small, server-accessible data |
|
|
388
|
+
| `cache` | Cache API | Service-worker / HTTP cache |
|
|
389
|
+
| `url` | `location` query/hash | Shareable UI state (see above) |
|
|
234
390
|
|
|
235
|
-
###
|
|
236
|
-
Unlike other storage solutions that depend on multiple packages, Strata Storage has **zero runtime dependencies**. Everything is built from scratch, ensuring:
|
|
237
|
-
- Smaller bundle size
|
|
238
|
-
- No dependency conflicts
|
|
239
|
-
- Better security
|
|
240
|
-
- Full control over implementation
|
|
391
|
+
### iOS and Android (via Capacitor)
|
|
241
392
|
|
|
242
|
-
|
|
243
|
-
One API works everywhere. No need to learn different APIs for web and mobile, or switch between libraries:
|
|
393
|
+
Register native adapters yourself when running under Capacitor:
|
|
244
394
|
|
|
245
395
|
```typescript
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
396
|
+
import { defineStorage } from 'strata-storage';
|
|
397
|
+
import { PreferencesAdapter, SecureStorageAdapter } from 'strata-storage/capacitor';
|
|
398
|
+
|
|
399
|
+
const storage = defineStorage();
|
|
400
|
+
storage.registerAdapter(new PreferencesAdapter()); // UserDefaults / SharedPreferences
|
|
401
|
+
storage.registerAdapter(new SecureStorageAdapter()); // Keychain / EncryptedSharedPreferences
|
|
402
|
+
|
|
403
|
+
await storage.set('secret', token, { storage: 'secure' });
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
| Adapter | iOS backend | Android backend |
|
|
407
|
+
|---------|-------------|-----------------|
|
|
408
|
+
| `preferences` | UserDefaults | SharedPreferences |
|
|
409
|
+
| `secure` | Keychain | EncryptedSharedPreferences |
|
|
410
|
+
| `sqlite` | SQLite | SQLite |
|
|
411
|
+
| `filesystem` | FileManager | Filesystem |
|
|
412
|
+
|
|
413
|
+
> **Honest note:** the native iOS/Android adapters depend on your downstream Capacitor project setup and platform configuration. Behavior on a real device should be verified on-device — native storage cannot be exercised by the web/Node test suite.
|
|
414
|
+
|
|
415
|
+
### Firebase (optional cloud sync)
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
import { defineStorage } from 'strata-storage';
|
|
419
|
+
import { enableFirebaseSync } from 'strata-storage/firebase';
|
|
420
|
+
|
|
421
|
+
const storage = defineStorage();
|
|
422
|
+
await enableFirebaseSync(storage, { apiKey: '…', projectId: '…', firestore: true });
|
|
423
|
+
await storage.set('data', value, { storage: 'firestore' });
|
|
249
424
|
```
|
|
250
425
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
-
|
|
258
|
-
|
|
426
|
+
## Storage Types
|
|
427
|
+
|
|
428
|
+
| Type | Platform | Synchronous | Encrypt/Compress | Notes |
|
|
429
|
+
|------|----------|-------------|-------------------|-------|
|
|
430
|
+
| `memory` | All | ✅ | async only | Always available |
|
|
431
|
+
| `localStorage` | Web | ✅ | async only | ~5 MB, persistent |
|
|
432
|
+
| `sessionStorage` | Web | ✅ | async only | Session-scoped |
|
|
433
|
+
| `indexedDB` | Web | ❌ | async only | Large structured data |
|
|
434
|
+
| `cookies` | Web | ✅ | async only | ~4 KB, server-readable |
|
|
435
|
+
| `cache` | Web | ❌ | async only | Cache API |
|
|
436
|
+
| `url` | Web | ✅ | async only | Shareable UI state, length-limited |
|
|
437
|
+
| `preferences` | Mobile | ❌ | async only | UserDefaults / SharedPreferences |
|
|
438
|
+
| `secure` | Mobile | ❌ | async only | Keychain / EncryptedSharedPreferences |
|
|
439
|
+
| `sqlite` | Mobile | ❌ | async only | Native SQLite |
|
|
440
|
+
| `filesystem` | Mobile | ❌ | async only | Native files |
|
|
441
|
+
|
|
442
|
+
"async only" means encryption and compression require the `await storage.set(...)` path — the synchronous API cannot encrypt or compress.
|
|
443
|
+
|
|
444
|
+
## Requirements
|
|
445
|
+
|
|
446
|
+
- **Node.js:** `>= 24.13.0`
|
|
447
|
+
- **TypeScript:** strict mode supported (optional, recommended)
|
|
448
|
+
- **Capacitor:** `@capacitor/core >= 8.0.0` (for native platforms; optional peer dependency)
|
|
449
|
+
|
|
450
|
+
Optional peer dependencies (install only the ones you use): `react >= 19.2.3`, `vue >= 3.5.26`, `@angular/core` & `@angular/forms >= 21.0.6`.
|
|
451
|
+
|
|
452
|
+
## Documentation
|
|
453
|
+
|
|
454
|
+
### Getting Started
|
|
455
|
+
- [Installation](./docs/getting-started/installation.md) — install and set up
|
|
456
|
+
- [Quick Start](./docs/getting-started/quick-start.md) — running in minutes
|
|
457
|
+
- [Configuration](./docs/getting-started/configuration.md) — configuration options
|
|
458
|
+
|
|
459
|
+
### Core
|
|
460
|
+
- [API Reference](./docs/api/README.md) — complete API
|
|
461
|
+
- [Core API (`Strata`)](./docs/api/core/strata.md) — the main class
|
|
462
|
+
- [Type Definitions](./docs/api/core/types.md) — TypeScript types
|
|
463
|
+
- [Error Handling](./docs/api/core/errors.md) — error classes
|
|
464
|
+
|
|
465
|
+
### Storage Adapters
|
|
466
|
+
- [Web Adapters](./docs/api/adapters/README.md#web-adapters)
|
|
467
|
+
- [localStorage](./docs/api/adapters/web/localstorage.md)
|
|
468
|
+
- [sessionStorage](./docs/api/adapters/web/sessionstorage.md)
|
|
469
|
+
- [IndexedDB](./docs/api/adapters/web/indexeddb.md)
|
|
470
|
+
- [Cookies](./docs/api/adapters/web/cookies.md)
|
|
471
|
+
- [Cache API](./docs/api/adapters/web/cache.md)
|
|
472
|
+
- [Memory](./docs/api/adapters/web/memory.md)
|
|
473
|
+
- [URL](./docs/api/adapters/web/url.md)
|
|
474
|
+
- [Capacitor Adapters](./docs/api/adapters/README.md#capacitor-adapters)
|
|
475
|
+
- [Preferences](./docs/api/adapters/capacitor/preferences.md)
|
|
476
|
+
- [Secure Storage](./docs/api/adapters/capacitor/secure.md)
|
|
477
|
+
- [SQLite](./docs/api/adapters/capacitor/sqlite.md)
|
|
478
|
+
- [Filesystem](./docs/api/adapters/capacitor/filesystem.md)
|
|
479
|
+
|
|
480
|
+
### Features
|
|
481
|
+
- [Encryption](./docs/guides/features/encryption.md)
|
|
482
|
+
- [Compression](./docs/guides/features/compression.md)
|
|
483
|
+
- [TTL Management](./docs/api/features/ttl.md)
|
|
484
|
+
- [Cross-Tab Sync](./docs/guides/features/sync.md)
|
|
485
|
+
- [Query Engine](./docs/guides/features/queries.md)
|
|
486
|
+
- [Migrations](./docs/guides/features/migrations.md)
|
|
487
|
+
- [Recovery & Integrity](./docs/api/features/recovery.md)
|
|
488
|
+
|
|
489
|
+
### Platform Guides
|
|
490
|
+
- [Web](./docs/guides/platforms/web.md)
|
|
491
|
+
- [iOS](./docs/guides/platforms/ios.md)
|
|
492
|
+
- [Android](./docs/guides/platforms/android.md)
|
|
493
|
+
- [Capacitor](./docs/guides/platforms/capacitor.md)
|
|
494
|
+
|
|
495
|
+
### Examples
|
|
496
|
+
- [Basic Usage](./docs/examples/basic-usage.md)
|
|
497
|
+
- [React Integration](./docs/examples/frameworks/react.md)
|
|
498
|
+
- [Vue Integration](./docs/examples/frameworks/vue.md)
|
|
499
|
+
- [Angular Integration](./docs/examples/frameworks/angular.md)
|
|
500
|
+
- [All Examples](./docs/examples/README.md)
|
|
501
|
+
|
|
502
|
+
### Reference
|
|
503
|
+
- [Changelog](./docs/reference/changelog.md)
|
|
504
|
+
- [FAQ](./docs/reference/faq.md)
|
|
505
|
+
- [Troubleshooting](./docs/reference/troubleshooting.md)
|
|
506
|
+
- [Migration Guide](./docs/MIGRATION.md)
|
|
259
507
|
|
|
260
508
|
## Contributing
|
|
261
509
|
|
|
262
|
-
Contributions are welcome
|
|
510
|
+
Contributions are welcome — please read the [Contributing Guide](./.github/CONTRIBUTING.md).
|
|
263
511
|
|
|
264
512
|
## License
|
|
265
513
|
|
|
266
|
-
Apache License 2.0
|
|
514
|
+
Apache License 2.0 — see [LICENSE](LICENSE). Free for commercial use, modification, and distribution with patent protection; attribution required; provided without warranty.
|
|
267
515
|
|
|
268
516
|
## Author
|
|
269
517
|
|
|
@@ -277,19 +525,17 @@ Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
|
|
|
277
525
|
|
|
278
526
|
## Links
|
|
279
527
|
|
|
280
|
-
- **NPM Package
|
|
281
|
-
- **GitHub Repository
|
|
282
|
-
- **Issue Tracker
|
|
283
|
-
- **Documentation
|
|
528
|
+
- **NPM Package:** [npmjs.com/package/strata-storage](https://www.npmjs.com/package/strata-storage)
|
|
529
|
+
- **GitHub Repository:** [github.com/aoneahsan/strata-storage](https://github.com/aoneahsan/strata-storage)
|
|
530
|
+
- **Issue Tracker:** [github.com/aoneahsan/strata-storage/issues](https://github.com/aoneahsan/strata-storage/issues)
|
|
531
|
+
- **Documentation:** [github.com/aoneahsan/strata-storage/tree/main/docs](https://github.com/aoneahsan/strata-storage/tree/main/docs)
|
|
284
532
|
|
|
285
533
|
## Support
|
|
286
534
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
1. Check the [FAQ](docs/reference/faq.md)
|
|
535
|
+
1. Check the [FAQ](./docs/reference/faq.md)
|
|
290
536
|
2. Search [existing issues](https://github.com/aoneahsan/strata-storage/issues)
|
|
291
|
-
3.
|
|
537
|
+
3. Open a [new issue](https://github.com/aoneahsan/strata-storage/issues/new)
|
|
292
538
|
|
|
293
539
|
---
|
|
294
540
|
|
|
295
|
-
Made
|
|
541
|
+
Made by [Ahsan Mahmood](https://aoneahsan.com). **One API. Every Storage. Everywhere.**
|