@mrbelloc/encrypted-store 0.2.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/LICENSE.md +11 -0
- package/README.md +212 -0
- package/dist/encryptedStore.d.ts +89 -0
- package/dist/encryptedStore.d.ts.map +1 -0
- package/dist/encryptedStore.js +293 -0
- package/dist/encryptedStore.js.map +1 -0
- package/dist/encryption.d.ts +34 -0
- package/dist/encryption.d.ts.map +1 -0
- package/dist/encryption.js +66 -0
- package/dist/encryption.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Released under MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 Mark Otto.
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2017 Andrew Fong.
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
|
+
|
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Encrypted Store
|
|
2
|
+
|
|
3
|
+
Client-side encrypted storage with change detection for PWAs. Built on [Fireproof](https://use-fireproof.com).
|
|
4
|
+
|
|
5
|
+
**For small data that can live in memory** - Designed for PWAs that manage datasets that fit comfortably in browser memory.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- π AES-256-GCM encryption before storage
|
|
10
|
+
- π Real-time change detection (added/changed/deleted events)
|
|
11
|
+
- π± PWA-ready with offline-first support
|
|
12
|
+
- π Optional remote sync (PartyKit, Netlify)
|
|
13
|
+
- π¦ TypeScript with full type safety
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @mrbelloc/encrypted-store
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { EncryptedStore, fireproof } from "@mrbelloc/encrypted-store";
|
|
25
|
+
|
|
26
|
+
// Create database and encrypted store
|
|
27
|
+
const db = fireproof("myapp");
|
|
28
|
+
const store = new EncryptedStore(db, "my-password", {
|
|
29
|
+
docsAdded: (events) => {
|
|
30
|
+
events.forEach(({ table, docs }) => {
|
|
31
|
+
console.log(`New ${table}:`, docs);
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
docsChanged: (events) => {
|
|
35
|
+
events.forEach(({ table, docs }) => {
|
|
36
|
+
console.log(`Updated ${table}:`, docs);
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
docsDeleted: (events) => {
|
|
40
|
+
events.forEach(({ table, docs }) => {
|
|
41
|
+
console.log(`Deleted ${table}:`, docs);
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Load existing data
|
|
47
|
+
await store.loadAll();
|
|
48
|
+
|
|
49
|
+
// Create/update documents
|
|
50
|
+
await store.put("users", { _id: "alice", name: "Alice", age: 30 });
|
|
51
|
+
|
|
52
|
+
// Get documents
|
|
53
|
+
const user = await store.get("users", "alice");
|
|
54
|
+
|
|
55
|
+
// Delete documents
|
|
56
|
+
await store.delete("users", "alice");
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## API Reference
|
|
60
|
+
|
|
61
|
+
### `new EncryptedStore(db, password, listener)`
|
|
62
|
+
|
|
63
|
+
Creates an encrypted store.
|
|
64
|
+
|
|
65
|
+
- `db`: Fireproof database instance
|
|
66
|
+
- `password`: Encryption password (string)
|
|
67
|
+
- `listener`: Object with three callbacks:
|
|
68
|
+
- `docsAdded(events: TableEvent[])`: Fired when new documents are added
|
|
69
|
+
- `docsChanged(events: TableEvent[])`: Fired when documents are updated
|
|
70
|
+
- `docsDeleted(events: TableEvent[])`: Fired when documents are deleted
|
|
71
|
+
|
|
72
|
+
Each `TableEvent` has:
|
|
73
|
+
|
|
74
|
+
- `table`: Document type (e.g., "users", "transactions")
|
|
75
|
+
- `docs`: Array of documents with that type
|
|
76
|
+
|
|
77
|
+
### `await store.loadAll()`
|
|
78
|
+
|
|
79
|
+
Loads all existing documents and sets up change detection. Call this once after creating the store.
|
|
80
|
+
|
|
81
|
+
### `await store.put(type, doc)`
|
|
82
|
+
|
|
83
|
+
Creates or updates a document.
|
|
84
|
+
|
|
85
|
+
- `type`: Document type / table name (string)
|
|
86
|
+
- `doc`: Document object with `_id` field (will be generated if missing)
|
|
87
|
+
|
|
88
|
+
Returns the document.
|
|
89
|
+
|
|
90
|
+
### `await store.get(type, id)`
|
|
91
|
+
|
|
92
|
+
Retrieves a document by type and ID. Returns `null` if not found.
|
|
93
|
+
|
|
94
|
+
### `await store.delete(type, id)`
|
|
95
|
+
|
|
96
|
+
Deletes a document by type and ID.
|
|
97
|
+
|
|
98
|
+
## Remote Sync
|
|
99
|
+
|
|
100
|
+
Sync encrypted data across devices with any Fireproof connector:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// Install the connector you want
|
|
104
|
+
// npm install @fireproof/partykit
|
|
105
|
+
// or
|
|
106
|
+
// npm install @fireproof/netlify
|
|
107
|
+
|
|
108
|
+
import { connect } from "@fireproof/partykit";
|
|
109
|
+
// or
|
|
110
|
+
// import { connect } from "@fireproof/netlify";
|
|
111
|
+
|
|
112
|
+
// Connect using the connector function
|
|
113
|
+
await store.connectRemote(connect, {
|
|
114
|
+
namespace: "my-app",
|
|
115
|
+
host: "http://localhost:1999", // or your server URL
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Disconnect
|
|
119
|
+
store.disconnectRemote();
|
|
120
|
+
|
|
121
|
+
// Works with any connector that follows the Fireproof connector interface
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Note:** Remote servers only see encrypted blobs - they cannot read your data.
|
|
125
|
+
|
|
126
|
+
## How It Works
|
|
127
|
+
|
|
128
|
+
1. **Encryption**: Documents are encrypted with AES-256-GCM before storage
|
|
129
|
+
2. **Storage**: Encrypted blobs stored in Fireproof (local-first IndexedDB)
|
|
130
|
+
3. **Change Detection**: Fireproof's subscribe notifies us of changes
|
|
131
|
+
4. **Diff Computation**: We track IDs and decrypt only changed documents
|
|
132
|
+
5. **Events**: Your app gets organized events by table (added/changed/deleted)
|
|
133
|
+
|
|
134
|
+
## Example: React Integration
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { useState, useEffect } from "react";
|
|
138
|
+
import { EncryptedStore, fireproof } from "@mrbelloc/encrypted-store";
|
|
139
|
+
|
|
140
|
+
function useEncryptedStore(dbName: string, password: string) {
|
|
141
|
+
const [users, setUsers] = useState<Map<string, any>>(new Map());
|
|
142
|
+
const [store, setStore] = useState<EncryptedStore | null>(null);
|
|
143
|
+
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
const db = fireproof(dbName);
|
|
146
|
+
const encryptedStore = new EncryptedStore(db, password, {
|
|
147
|
+
docsAdded: (events) => {
|
|
148
|
+
events.forEach(({ table, docs }) => {
|
|
149
|
+
if (table === "users") {
|
|
150
|
+
setUsers((prev) => {
|
|
151
|
+
const next = new Map(prev);
|
|
152
|
+
docs.forEach((doc) => next.set(doc._id, doc));
|
|
153
|
+
return next;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
docsChanged: (events) => {
|
|
159
|
+
events.forEach(({ table, docs }) => {
|
|
160
|
+
if (table === "users") {
|
|
161
|
+
setUsers((prev) => {
|
|
162
|
+
const next = new Map(prev);
|
|
163
|
+
docs.forEach((doc) => next.set(doc._id, doc));
|
|
164
|
+
return next;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
docsDeleted: (events) => {
|
|
170
|
+
events.forEach(({ table, docs }) => {
|
|
171
|
+
if (table === "users") {
|
|
172
|
+
setUsers((prev) => {
|
|
173
|
+
const next = new Map(prev);
|
|
174
|
+
docs.forEach((doc) => next.delete(doc._id));
|
|
175
|
+
return next;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
encryptedStore.loadAll();
|
|
183
|
+
setStore(encryptedStore);
|
|
184
|
+
}, [dbName, password]);
|
|
185
|
+
|
|
186
|
+
return { users: Array.from(users.values()), store };
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## TypeScript Types
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
interface TableEvent {
|
|
194
|
+
table: string;
|
|
195
|
+
docs: Doc[];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface StoreListener {
|
|
199
|
+
docsAdded: (events: TableEvent[]) => void;
|
|
200
|
+
docsChanged: (events: TableEvent[]) => void;
|
|
201
|
+
docsDeleted: (events: TableEvent[]) => void;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
interface Doc {
|
|
205
|
+
_id: string;
|
|
206
|
+
[key: string]: any;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypted storage with change detection for small datasets
|
|
3
|
+
* Wraps Fireproof with AES-256-GCM encryption + real-time event system
|
|
4
|
+
*/
|
|
5
|
+
interface Doc {
|
|
6
|
+
_id: string;
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}
|
|
9
|
+
export interface TableEvent {
|
|
10
|
+
table: string;
|
|
11
|
+
docs: Doc[];
|
|
12
|
+
}
|
|
13
|
+
export interface StoreListener {
|
|
14
|
+
docsAdded: (events: TableEvent[]) => void;
|
|
15
|
+
docsChanged: (events: TableEvent[]) => void;
|
|
16
|
+
docsDeleted: (events: TableEvent[]) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface RemoteConnectOptions {
|
|
19
|
+
namespace: string;
|
|
20
|
+
host: string;
|
|
21
|
+
}
|
|
22
|
+
export interface SyncConnection {
|
|
23
|
+
ready?: Promise<void>;
|
|
24
|
+
disconnect?: () => void;
|
|
25
|
+
}
|
|
26
|
+
export type ConnectorFunction = (db: FireproofDb, namespace: string, host: string) => SyncConnection;
|
|
27
|
+
export interface FireproofDb {
|
|
28
|
+
put(doc: any): Promise<{
|
|
29
|
+
id: string;
|
|
30
|
+
rev?: string;
|
|
31
|
+
}>;
|
|
32
|
+
get(id: string): Promise<any>;
|
|
33
|
+
query(field: string, options?: {
|
|
34
|
+
limit?: number;
|
|
35
|
+
descending?: boolean;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
docs?: any[];
|
|
38
|
+
rows: Array<{
|
|
39
|
+
key: string;
|
|
40
|
+
doc?: any;
|
|
41
|
+
value?: any;
|
|
42
|
+
}>;
|
|
43
|
+
}>;
|
|
44
|
+
subscribe(callback: (changes: any[]) => void, remote?: boolean): void;
|
|
45
|
+
del(id: string, rev?: string): Promise<any>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* EncryptedStore class
|
|
49
|
+
*
|
|
50
|
+
* Main entry point for encrypted storage with change detection
|
|
51
|
+
*/
|
|
52
|
+
export declare class EncryptedStore {
|
|
53
|
+
private db;
|
|
54
|
+
private encryptionHelper;
|
|
55
|
+
private listener;
|
|
56
|
+
private knownIds;
|
|
57
|
+
private fullIdMap;
|
|
58
|
+
private isSubscribed;
|
|
59
|
+
private connection;
|
|
60
|
+
constructor(db: FireproofDb, password: string, listener: StoreListener);
|
|
61
|
+
/** Load all documents and set up change detection (call once after creating store) */
|
|
62
|
+
loadAll(): Promise<void>;
|
|
63
|
+
/** Create or update a document */
|
|
64
|
+
put(type: string, doc: any): Promise<Doc>;
|
|
65
|
+
/** Get a document (returns null if not found) */
|
|
66
|
+
get(type: string, id: string): Promise<Doc | null>;
|
|
67
|
+
/** Delete a document */
|
|
68
|
+
delete(type: string, id: string): Promise<void>;
|
|
69
|
+
/** Connect to remote sync with any Fireproof connector */
|
|
70
|
+
connectRemote(connector: ConnectorFunction, options: RemoteConnectOptions): Promise<void>;
|
|
71
|
+
/** Disconnect from remote sync */
|
|
72
|
+
disconnectRemote(): void;
|
|
73
|
+
/** Read all encrypted documents (without decrypting) */
|
|
74
|
+
private readAllEncrypted;
|
|
75
|
+
/**
|
|
76
|
+
* Process Fireproof changes: { _id, d? }
|
|
77
|
+
* With d = create/update, without d = deletion
|
|
78
|
+
*/
|
|
79
|
+
private handleChange;
|
|
80
|
+
private decryptFromEncryptedData;
|
|
81
|
+
private encryptDoc;
|
|
82
|
+
private decryptDoc;
|
|
83
|
+
private parseFullId;
|
|
84
|
+
private generateId;
|
|
85
|
+
private groupByTable;
|
|
86
|
+
private groupDeletedByTable;
|
|
87
|
+
}
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=encryptedStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryptedStore.d.ts","sourceRoot":"","sources":["../src/encryptedStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypted storage with change detection for small datasets
|
|
3
|
+
* Wraps Fireproof with AES-256-GCM encryption + real-time event system
|
|
4
|
+
*/
|
|
5
|
+
import { EncryptionHelper } from "./encryption";
|
|
6
|
+
/**
|
|
7
|
+
* EncryptedStore class
|
|
8
|
+
*
|
|
9
|
+
* Main entry point for encrypted storage with change detection
|
|
10
|
+
*/
|
|
11
|
+
export class EncryptedStore {
|
|
12
|
+
constructor(db, password, listener) {
|
|
13
|
+
this.knownIds = new Set(); // Set of known document IDs (stripped, e.g., "alice")
|
|
14
|
+
this.fullIdMap = new Map(); // stripped id -> full id with table
|
|
15
|
+
this.isSubscribed = false;
|
|
16
|
+
this.connection = null;
|
|
17
|
+
this.db = db;
|
|
18
|
+
this.encryptionHelper = new EncryptionHelper(password);
|
|
19
|
+
this.listener = listener;
|
|
20
|
+
}
|
|
21
|
+
/** Load all documents and set up change detection (call once after creating store) */
|
|
22
|
+
async loadAll() {
|
|
23
|
+
const { encryptedMap, fullIdMap } = await this.readAllEncrypted();
|
|
24
|
+
this.fullIdMap = fullIdMap;
|
|
25
|
+
// Build initial set of known IDs
|
|
26
|
+
this.knownIds = new Set(encryptedMap.keys());
|
|
27
|
+
// Decrypt all documents for initial docsAdded event
|
|
28
|
+
const docs = [];
|
|
29
|
+
for (const [id, encryptedData] of encryptedMap) {
|
|
30
|
+
try {
|
|
31
|
+
const decrypted = await this.decryptFromEncryptedData(encryptedData, id);
|
|
32
|
+
docs.push(decrypted);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
// Skip documents we can't decrypt
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Fire initial docsAdded for everything, grouped by table
|
|
39
|
+
if (docs.length > 0) {
|
|
40
|
+
const events = this.groupByTable(docs, fullIdMap);
|
|
41
|
+
this.listener.docsAdded(events);
|
|
42
|
+
}
|
|
43
|
+
// Set up subscribe (only once)
|
|
44
|
+
if (!this.isSubscribed) {
|
|
45
|
+
this.db.subscribe((changes) => {
|
|
46
|
+
this.handleChange(changes).catch((err) => {
|
|
47
|
+
console.error("EncryptedStore: Error handling change:", err);
|
|
48
|
+
});
|
|
49
|
+
}, true); // Include remote changes
|
|
50
|
+
this.isSubscribed = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Create or update a document */
|
|
54
|
+
async put(type, doc) {
|
|
55
|
+
// Generate ID if not provided
|
|
56
|
+
if (!doc._id) {
|
|
57
|
+
doc._id = this.generateId();
|
|
58
|
+
}
|
|
59
|
+
// Build full ID with type prefix
|
|
60
|
+
const fullId = `${type}_${doc._id}`;
|
|
61
|
+
// Encrypt the document
|
|
62
|
+
const encryptedDoc = await this.encryptDoc(doc, fullId);
|
|
63
|
+
// Store in Fireproof
|
|
64
|
+
await this.db.put(encryptedDoc);
|
|
65
|
+
// Fireproof's subscribe will trigger handleChange()
|
|
66
|
+
// which will reload, compute diff, and fire events
|
|
67
|
+
return doc;
|
|
68
|
+
}
|
|
69
|
+
/** Get a document (returns null if not found) */
|
|
70
|
+
async get(type, id) {
|
|
71
|
+
const fullId = `${type}_${id}`;
|
|
72
|
+
try {
|
|
73
|
+
const encryptedDoc = await this.db.get(fullId);
|
|
74
|
+
return await this.decryptDoc(encryptedDoc, id);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// Document not found
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Delete a document */
|
|
82
|
+
async delete(type, id) {
|
|
83
|
+
// Build full ID with type prefix
|
|
84
|
+
const fullId = `${type}_${id}`;
|
|
85
|
+
// Delete from Fireproof
|
|
86
|
+
await this.db.del(fullId);
|
|
87
|
+
// Fireproof's subscribe will trigger handleChange()
|
|
88
|
+
// which will detect the deletion and fire docsDeleted event
|
|
89
|
+
}
|
|
90
|
+
/** Connect to remote sync with any Fireproof connector */
|
|
91
|
+
async connectRemote(connector, options) {
|
|
92
|
+
this.disconnectRemote();
|
|
93
|
+
try {
|
|
94
|
+
console.log(`[EncryptedStore] Connecting to ${options.host} with namespace: ${options.namespace}`);
|
|
95
|
+
// Call the connector function
|
|
96
|
+
const connection = connector(this.db, options.namespace, options.host);
|
|
97
|
+
// Store connection
|
|
98
|
+
this.connection = {
|
|
99
|
+
ready: connection.ready || Promise.resolve(),
|
|
100
|
+
disconnect: connection.disconnect,
|
|
101
|
+
};
|
|
102
|
+
// Wait for connection to be ready
|
|
103
|
+
await this.connection.ready;
|
|
104
|
+
console.log(`[EncryptedStore] β Connected to remote`);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error(`[EncryptedStore] β Failed to connect:`, error);
|
|
108
|
+
this.connection = null;
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Disconnect from remote sync */
|
|
113
|
+
disconnectRemote() {
|
|
114
|
+
if (this.connection && this.connection.disconnect) {
|
|
115
|
+
this.connection.disconnect();
|
|
116
|
+
console.log("[EncryptedStore] Disconnected from remote");
|
|
117
|
+
}
|
|
118
|
+
this.connection = null;
|
|
119
|
+
}
|
|
120
|
+
/** Read all encrypted documents (without decrypting) */
|
|
121
|
+
async readAllEncrypted() {
|
|
122
|
+
const result = await this.db.query("_id", { descending: false });
|
|
123
|
+
const encryptedMap = new Map();
|
|
124
|
+
const fullIdMap = new Map();
|
|
125
|
+
// Get docs from either result.docs or result.rows
|
|
126
|
+
const allDocs = result.docs || result.rows.map((row) => row.doc).filter(Boolean);
|
|
127
|
+
for (const doc of allDocs) {
|
|
128
|
+
try {
|
|
129
|
+
const { type, id } = this.parseFullId(doc._id);
|
|
130
|
+
encryptedMap.set(id, doc.d); // Store encrypted data
|
|
131
|
+
fullIdMap.set(id, doc._id); // Map "alice" -> "users_alice"
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
// Skip documents with invalid ID format
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { encryptedMap, fullIdMap };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Process Fireproof changes: { _id, d? }
|
|
141
|
+
* With d = create/update, without d = deletion
|
|
142
|
+
*/
|
|
143
|
+
async handleChange(changes) {
|
|
144
|
+
const newDocs = [];
|
|
145
|
+
const changedDocs = [];
|
|
146
|
+
const deletedDocs = [];
|
|
147
|
+
for (const change of changes) {
|
|
148
|
+
if (change.d) {
|
|
149
|
+
// Has encrypted data - it's a create or update
|
|
150
|
+
try {
|
|
151
|
+
const { type, id } = this.parseFullId(change._id);
|
|
152
|
+
// Decrypt the document
|
|
153
|
+
const decrypted = await this.decryptFromEncryptedData(change.d, id);
|
|
154
|
+
// Check if it's new or changed
|
|
155
|
+
if (this.knownIds.has(id)) {
|
|
156
|
+
changedDocs.push(decrypted);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
newDocs.push(decrypted);
|
|
160
|
+
this.knownIds.add(id);
|
|
161
|
+
this.fullIdMap.set(id, change._id); // Update fullIdMap for new docs
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
// Skip documents we can't decrypt or parse
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// No encrypted data - it's a deletion
|
|
170
|
+
try {
|
|
171
|
+
const { type, id } = this.parseFullId(change._id);
|
|
172
|
+
if (this.knownIds.has(id)) {
|
|
173
|
+
deletedDocs.push({ _id: id });
|
|
174
|
+
this.knownIds.delete(id);
|
|
175
|
+
// Keep fullIdMap entry for the deletion event, remove after
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// Skip invalid IDs
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Fire events grouped by table
|
|
184
|
+
if (newDocs.length > 0) {
|
|
185
|
+
const events = this.groupByTable(newDocs, this.fullIdMap);
|
|
186
|
+
this.listener.docsAdded(events);
|
|
187
|
+
}
|
|
188
|
+
if (changedDocs.length > 0) {
|
|
189
|
+
const events = this.groupByTable(changedDocs, this.fullIdMap);
|
|
190
|
+
this.listener.docsChanged(events);
|
|
191
|
+
}
|
|
192
|
+
if (deletedDocs.length > 0) {
|
|
193
|
+
const events = this.groupDeletedByTable(deletedDocs);
|
|
194
|
+
this.listener.docsDeleted(events);
|
|
195
|
+
// Clean up fullIdMap entries for deleted docs
|
|
196
|
+
for (const doc of deletedDocs) {
|
|
197
|
+
this.fullIdMap.delete(doc._id);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async decryptFromEncryptedData(encryptedData, id) {
|
|
202
|
+
// Decrypt the data
|
|
203
|
+
const decryptedJson = await this.encryptionHelper.decrypt(encryptedData);
|
|
204
|
+
const decryptedData = JSON.parse(decryptedJson);
|
|
205
|
+
// Build final document
|
|
206
|
+
const doc = {
|
|
207
|
+
_id: id,
|
|
208
|
+
...decryptedData,
|
|
209
|
+
};
|
|
210
|
+
return doc;
|
|
211
|
+
}
|
|
212
|
+
async encryptDoc(doc, fullId) {
|
|
213
|
+
// Extract fields to encrypt (everything except _id)
|
|
214
|
+
const dataToEncrypt = {};
|
|
215
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
216
|
+
if (!key.startsWith("_")) {
|
|
217
|
+
dataToEncrypt[key] = value;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Encrypt as JSON
|
|
221
|
+
const encrypted = await this.encryptionHelper.encrypt(JSON.stringify(dataToEncrypt));
|
|
222
|
+
// Build encrypted doc
|
|
223
|
+
const encryptedDoc = {
|
|
224
|
+
_id: fullId,
|
|
225
|
+
d: encrypted,
|
|
226
|
+
};
|
|
227
|
+
return encryptedDoc;
|
|
228
|
+
}
|
|
229
|
+
async decryptDoc(encryptedDoc, id) {
|
|
230
|
+
// Decrypt the 'd' field
|
|
231
|
+
const decryptedJson = await this.encryptionHelper.decrypt(encryptedDoc.d);
|
|
232
|
+
const decryptedData = JSON.parse(decryptedJson);
|
|
233
|
+
// Build final document
|
|
234
|
+
const doc = {
|
|
235
|
+
_id: id,
|
|
236
|
+
...decryptedData,
|
|
237
|
+
};
|
|
238
|
+
return doc;
|
|
239
|
+
}
|
|
240
|
+
parseFullId(fullId) {
|
|
241
|
+
const firstUnderscore = fullId.indexOf("_");
|
|
242
|
+
if (firstUnderscore === -1) {
|
|
243
|
+
throw new Error(`Invalid document ID format: ${fullId}`);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
type: fullId.substring(0, firstUnderscore),
|
|
247
|
+
id: fullId.substring(firstUnderscore + 1),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
generateId() {
|
|
251
|
+
// Use crypto.randomUUID if available, otherwise fallback
|
|
252
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
253
|
+
return crypto.randomUUID();
|
|
254
|
+
}
|
|
255
|
+
// Fallback for environments without crypto.randomUUID
|
|
256
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
257
|
+
}
|
|
258
|
+
groupByTable(docs, fullIdMap) {
|
|
259
|
+
const grouped = new Map();
|
|
260
|
+
for (const doc of docs) {
|
|
261
|
+
const fullId = fullIdMap.get(doc._id);
|
|
262
|
+
if (fullId) {
|
|
263
|
+
const { type } = this.parseFullId(fullId);
|
|
264
|
+
if (!grouped.has(type)) {
|
|
265
|
+
grouped.set(type, []);
|
|
266
|
+
}
|
|
267
|
+
grouped.get(type).push(doc);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return Array.from(grouped.entries()).map(([table, docs]) => ({
|
|
271
|
+
table,
|
|
272
|
+
docs,
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
groupDeletedByTable(deletedDocs) {
|
|
276
|
+
const grouped = new Map();
|
|
277
|
+
for (const doc of deletedDocs) {
|
|
278
|
+
const fullId = this.fullIdMap.get(doc._id);
|
|
279
|
+
if (fullId) {
|
|
280
|
+
const { type } = this.parseFullId(fullId);
|
|
281
|
+
if (!grouped.has(type)) {
|
|
282
|
+
grouped.set(type, []);
|
|
283
|
+
}
|
|
284
|
+
grouped.get(type).push(doc);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return Array.from(grouped.entries()).map(([table, docs]) => ({
|
|
288
|
+
table,
|
|
289
|
+
docs: docs, // Cast since deletedDocs is simpler structure
|
|
290
|
+
}));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=encryptedStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryptedStore.js","sourceRoot":"","sources":["../src/encryptedStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface defining the subset of the Web Crypto API that we use
|
|
3
|
+
*/
|
|
4
|
+
interface CryptoInterface {
|
|
5
|
+
subtle: {
|
|
6
|
+
digest(algorithm: string, data: BufferSource): Promise<ArrayBuffer>;
|
|
7
|
+
importKey(format: string, keyData: BufferSource, algorithm: string | object, extractable: boolean, keyUsages: string[]): Promise<CryptoKey>;
|
|
8
|
+
encrypt(algorithm: string | object, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>;
|
|
9
|
+
decrypt(algorithm: string | object, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>;
|
|
10
|
+
};
|
|
11
|
+
getRandomValues<T extends ArrayBufferView>(array: T): T;
|
|
12
|
+
}
|
|
13
|
+
declare class DecryptionError extends Error {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
declare class EncryptionHelper {
|
|
17
|
+
private keyPromise;
|
|
18
|
+
private readonly passphrase;
|
|
19
|
+
private readonly crypto;
|
|
20
|
+
/**
|
|
21
|
+
* @param passphrase - The passphrase used for encryption/decryption
|
|
22
|
+
* @param crypto - Optional crypto implementation. If not provided, uses the global crypto object.
|
|
23
|
+
* This parameter is primarily for testing purposes.
|
|
24
|
+
*/
|
|
25
|
+
constructor(passphrase: string, crypto?: CryptoInterface);
|
|
26
|
+
private getKey;
|
|
27
|
+
private static fromHexString;
|
|
28
|
+
private static toHexString;
|
|
29
|
+
encrypt(data: string): Promise<string>;
|
|
30
|
+
decrypt(data: string): Promise<string>;
|
|
31
|
+
}
|
|
32
|
+
export { EncryptionHelper, DecryptionError };
|
|
33
|
+
export type { CryptoInterface };
|
|
34
|
+
//# sourceMappingURL=encryption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,UAAU,eAAe;IACvB,MAAM,EAAE;QACN,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACpE,SAAS,CACP,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,SAAS,CAAC,CAAC;QACtB,OAAO,CACL,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,WAAW,CAAC,CAAC;QACxB,OAAO,CACL,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,WAAW,CAAC,CAAC;KACzB,CAAC;IACF,eAAe,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;CACzD;AAED,cAAM,eAAgB,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAK5B;AAED,cAAM,gBAAgB;IACpB,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC;;;;OAIG;gBACS,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe;YAO1C,MAAM;IAkBpB,OAAO,CAAC,MAAM,CAAC,aAAa;IAM5B,OAAO,CAAC,MAAM,CAAC,WAAW;IAOpB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBtC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAqB7C;AAED,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class DecryptionError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "DecryptionError";
|
|
5
|
+
Object.setPrototypeOf(this, DecryptionError.prototype);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
class EncryptionHelper {
|
|
9
|
+
/**
|
|
10
|
+
* @param passphrase - The passphrase used for encryption/decryption
|
|
11
|
+
* @param crypto - Optional crypto implementation. If not provided, uses the global crypto object.
|
|
12
|
+
* This parameter is primarily for testing purposes.
|
|
13
|
+
*/
|
|
14
|
+
constructor(passphrase, crypto) {
|
|
15
|
+
this.keyPromise = null;
|
|
16
|
+
this.passphrase = passphrase;
|
|
17
|
+
this.crypto =
|
|
18
|
+
crypto ||
|
|
19
|
+
(typeof window !== "undefined" ? window.crypto : global.crypto);
|
|
20
|
+
}
|
|
21
|
+
async getKey() {
|
|
22
|
+
if (this.keyPromise) {
|
|
23
|
+
return this.keyPromise;
|
|
24
|
+
}
|
|
25
|
+
const enc = new TextEncoder();
|
|
26
|
+
const pwUtf8 = enc.encode(this.passphrase);
|
|
27
|
+
const pwHash = await this.crypto.subtle.digest("SHA-256", pwUtf8);
|
|
28
|
+
this.keyPromise = this.crypto.subtle.importKey("raw", pwHash, "AES-GCM", true, ["encrypt", "decrypt"]);
|
|
29
|
+
return this.keyPromise;
|
|
30
|
+
}
|
|
31
|
+
static fromHexString(hexString) {
|
|
32
|
+
return new Uint8Array(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
|
33
|
+
}
|
|
34
|
+
static toHexString(bytes) {
|
|
35
|
+
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
|
36
|
+
}
|
|
37
|
+
async encrypt(data) {
|
|
38
|
+
const enc = new TextEncoder();
|
|
39
|
+
const key = await this.getKey();
|
|
40
|
+
const encoded = enc.encode(data);
|
|
41
|
+
const iv = this.crypto.getRandomValues(new Uint8Array(12));
|
|
42
|
+
const ciphertext = await this.crypto.subtle.encrypt({
|
|
43
|
+
name: "AES-GCM",
|
|
44
|
+
iv: iv,
|
|
45
|
+
}, key, encoded);
|
|
46
|
+
return `${EncryptionHelper.toHexString(iv)}|${EncryptionHelper.toHexString(new Uint8Array(ciphertext))}`;
|
|
47
|
+
}
|
|
48
|
+
async decrypt(data) {
|
|
49
|
+
const key = await this.getKey();
|
|
50
|
+
const [iv, ciphertext] = data
|
|
51
|
+
.split("|")
|
|
52
|
+
.map((s) => EncryptionHelper.fromHexString(s));
|
|
53
|
+
try {
|
|
54
|
+
const decrypted = await this.crypto.subtle.decrypt({
|
|
55
|
+
name: "AES-GCM",
|
|
56
|
+
iv: iv,
|
|
57
|
+
}, key, ciphertext);
|
|
58
|
+
return new TextDecoder().decode(decrypted);
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
throw new DecryptionError(`Could not decrypt: ${e instanceof Error ? e.message : String(e)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export { EncryptionHelper, DecryptionError };
|
|
66
|
+
//# sourceMappingURL=encryption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AA2BA,MAAM,eAAgB,SAAQ,KAAK;IACjC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC;CACF;AAED,MAAM,gBAAgB;IAKpB;;;;OAIG;IACH,YAAY,UAAkB,EAAE,MAAwB;QAThD,eAAU,GAA8B,IAAI,CAAC;QAUnD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,MAAM;YACT,MAAM;gBACN,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,MAAc,CAAC,MAAM,CAAC,CAAC;IAC7E,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5C,KAAK,EACL,MAAM,EACN,SAAS,EACT,IAAI,EACJ,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAC;QACF,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAEO,MAAM,CAAC,aAAa,CAAC,SAAiB;QAC5C,OAAO,IAAI,UAAU,CACnB,SAAS,CAAC,KAAK,CAAC,SAAS,CAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAC9D,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,WAAW,CAAC,KAAiB;QAC1C,OAAO,KAAK,CAAC,MAAM,CACjB,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EACvD,EAAE,CACH,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CACjD;YACE,IAAI,EAAE,SAAS;YACf,EAAE,EAAE,EAAE;SACP,EACD,GAAG,EACH,OAAO,CACR,CAAC;QACF,OAAO,GAAG,gBAAgB,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,gBAAgB,CAAC,WAAW,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;IAC3G,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,GAAG,IAAI;aAC1B,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAChD;gBACE,IAAI,EAAE,SAAS;gBACf,EAAE,EAAE,EAAE;aACP,EACD,GAAG,EACH,UAA0B,CAC3B,CAAC;YACF,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,eAAe,CACvB,sBAAsB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAED,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypted storage with change detection for small datasets in PWAs
|
|
3
|
+
* Built on Fireproof with AES-256-GCM encryption
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
export { EncryptedStore } from "./encryptedStore";
|
|
7
|
+
export type { StoreListener, FireproofDb, TableEvent, ConnectorFunction, RemoteConnectOptions, SyncConnection, } from "./encryptedStore";
|
|
8
|
+
export { EncryptionHelper, DecryptionError } from "./encryption";
|
|
9
|
+
export type { CryptoInterface } from "./encryption";
|
|
10
|
+
export declare const VERSION = "0.2.0";
|
|
11
|
+
export { fireproof } from "use-fireproof";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EACV,aAAa,EACb,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,eAAO,MAAM,OAAO,UAAU,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypted storage with change detection for small datasets in PWAs
|
|
3
|
+
* Built on Fireproof with AES-256-GCM encryption
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
export { EncryptedStore } from "./encryptedStore";
|
|
7
|
+
export { EncryptionHelper, DecryptionError } from "./encryption";
|
|
8
|
+
export const VERSION = "0.2.0";
|
|
9
|
+
export { fireproof } from "use-fireproof";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAUlD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGjE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mrbelloc/encrypted-store",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Client-side encrypted storage with change detection for small datasets in PWAs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
10
|
+
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
|
|
11
|
+
"build": "tsc --project tsconfig.build.json",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"clean": "rm -rf dist",
|
|
14
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
15
|
+
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
16
|
+
"prepare": "husky"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"encryption",
|
|
20
|
+
"storage",
|
|
21
|
+
"fireproof",
|
|
22
|
+
"pwa"
|
|
23
|
+
],
|
|
24
|
+
"author": "Pablo de LeΓ³n Belloc <pablolb@gmail.com>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/pablolb/encrypted-store.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/pablolb/encrypted-store#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/pablolb/encrypted-store/issues"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"!dist/**/__tests__",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@fireproof/partykit": "^0.19.118",
|
|
41
|
+
"@types/jest": "^29.5.11",
|
|
42
|
+
"@types/node": "^20.10.6",
|
|
43
|
+
"husky": "^9.1.7",
|
|
44
|
+
"jest": "^29.7.0",
|
|
45
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
46
|
+
"lint-staged": "^16.2.7",
|
|
47
|
+
"partykit": "^0.0.115",
|
|
48
|
+
"partysocket": "^1.1.10",
|
|
49
|
+
"prettier": "^3.7.4",
|
|
50
|
+
"ts-jest": "^29.1.1",
|
|
51
|
+
"typescript": "^5.3.3"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"multiformats": "^13.4.2",
|
|
55
|
+
"use-fireproof": "^0.19.0"
|
|
56
|
+
},
|
|
57
|
+
"lint-staged": {
|
|
58
|
+
"src/**/*.ts": [
|
|
59
|
+
"prettier --write"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|