aegis-lock 2.0.1 → 2.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 +21 -0
- package/README.md +75 -28
- package/dist/adapters/mongodb.d.ts +19 -14
- package/dist/adapters/mongodb.d.ts.map +1 -1
- package/dist/adapters/mongodb.js +28 -14
- package/dist/adapters/mongodb.js.map +1 -1
- package/dist/adapters/supabase.d.ts +15 -2
- package/dist/adapters/supabase.d.ts.map +1 -1
- package/dist/adapters/supabase.js +22 -2
- package/dist/adapters/supabase.js.map +1 -1
- package/dist/client.d.ts +39 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +88 -155
- package/dist/client.js.map +1 -1
- package/dist/crypto.d.ts +49 -4
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +69 -43
- package/dist/crypto.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -22
- package/dist/types.js.map +1 -1
- package/package.json +13 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 API-Alchemist
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# aegis-lock
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/js/aegis-lock)
|
|
3
|
+
[](https://badge.fury.io/js/aegis-lock)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
**Database-agnostic client-side AES-256-GCM field-level encryption with Contextual Binding and
|
|
6
|
+
**Database-agnostic client-side AES-256-GCM field-level encryption with Contextual Binding, Blind Indexing, and Seamless Key Rotation.**
|
|
7
7
|
|
|
8
|
-
Encrypt sensitive fields before they leave the browser or server. Decrypt after select. Zero plaintext hits the wire or your database. Works with
|
|
8
|
+
Encrypt sensitive fields before they leave the browser or server. Decrypt after select. Zero plaintext hits the wire or your database. Works with Supabase, MongoDB, or any database via custom adapters.
|
|
9
9
|
|
|
10
10
|
> **⚠️ SECURITY WARNING:** `aegis-lock` provides the cryptographic primitives, but **you are responsible for your keys.** Never hardcode `CryptoKey` exports in your source code. Always use a secure Key Management Service (KMS) or strict, vault-backed environment variables to inject your keys at runtime.
|
|
11
11
|
|
|
@@ -22,17 +22,22 @@ npm install aegis-lock
|
|
|
22
22
|
|
|
23
23
|
```typescript
|
|
24
24
|
import { createClient } from "@supabase/supabase-js";
|
|
25
|
-
import { AegisClient, SupabaseAdapter,
|
|
25
|
+
import { AegisClient, SupabaseAdapter, importKey, generateBidxKey } from "aegis-lock";
|
|
26
26
|
|
|
27
27
|
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
|
28
28
|
const adapter = new SupabaseAdapter(supabase);
|
|
29
29
|
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
const
|
|
30
|
+
// 1. Setup your Key Ring for Key Rotation
|
|
31
|
+
// You should load these base64 strings securely from your environment variables
|
|
32
|
+
const keyRing = {
|
|
33
|
+
"v1": await importKey(process.env.AEGIS_KEY_V1),
|
|
34
|
+
"v2": await importKey(process.env.AEGIS_KEY_V2) // Your newest key
|
|
35
|
+
};
|
|
36
|
+
const activeVersion = "v2";
|
|
33
37
|
|
|
34
|
-
//
|
|
38
|
+
const bidxKey = await generateBidxKey(); // Or import your saved blind index key
|
|
35
39
|
|
|
40
|
+
// 2. Initialize the Client
|
|
36
41
|
const aegis = new AegisClient({
|
|
37
42
|
adapter,
|
|
38
43
|
primaryKeyField: "record_id", // REQUIRED: Binds ciphertext to the row to prevent tampering
|
|
@@ -42,9 +47,9 @@ const aegis = new AegisClient({
|
|
|
42
47
|
bidxFields: {
|
|
43
48
|
secure_fields: ["email"] // Optional: Creates an 'email_bidx' column for searching
|
|
44
49
|
}
|
|
45
|
-
},
|
|
50
|
+
}, keyRing, activeVersion, bidxKey);
|
|
46
51
|
|
|
47
|
-
// Insert — fields are auto-encrypted.
|
|
52
|
+
// 3. Insert — fields are auto-encrypted using the 'v2' key.
|
|
48
53
|
// Note: You MUST provide the primary key application-side!
|
|
49
54
|
await aegis.insert("secure_fields", {
|
|
50
55
|
record_id: "uuid-1234-5678",
|
|
@@ -52,25 +57,39 @@ await aegis.insert("secure_fields", {
|
|
|
52
57
|
encrypted_content: "Top secret",
|
|
53
58
|
});
|
|
54
59
|
|
|
55
|
-
// Select — Aegis automatically hashes the email to search the 'email_bidx' column securely
|
|
60
|
+
// 4. Select — Aegis automatically hashes the email to search the 'email_bidx' column securely.
|
|
61
|
+
// It will dynamically select the correct key from the KeyRing to decrypt the results.
|
|
56
62
|
const { data } = await aegis.select("secure_fields", {
|
|
57
63
|
column: "email",
|
|
58
64
|
value: "alice@example.com"
|
|
59
65
|
});
|
|
66
|
+
|
|
67
|
+
// 5. Update — automatically re-encrypts with a new IV and the active 'v2' key.
|
|
68
|
+
await aegis.update("secure_fields", {
|
|
69
|
+
record_id: "uuid-1234-5678", // Must include the primary key!
|
|
70
|
+
email: "new_alice@example.com",
|
|
71
|
+
encrypted_content: "New secret"
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 6. Delete — Aegis automatically hashes the email to find the correct row to delete
|
|
75
|
+
await aegis.delete("secure_fields", {
|
|
76
|
+
column: "email",
|
|
77
|
+
value: "new_alice@example.com"
|
|
78
|
+
});
|
|
60
79
|
```
|
|
61
80
|
|
|
62
81
|
## Quick Start — MongoDB
|
|
63
82
|
|
|
64
83
|
```typescript
|
|
65
84
|
import { MongoClient } from "mongodb";
|
|
66
|
-
import { AegisClient, MongoDBAdapter,
|
|
85
|
+
import { AegisClient, MongoDBAdapter, importKey } from "aegis-lock";
|
|
67
86
|
import { v4 as uuidv4 } from "uuid";
|
|
68
87
|
|
|
69
88
|
const mongo = new MongoClient("mongodb://localhost:27017");
|
|
70
89
|
await mongo.connect();
|
|
71
90
|
const adapter = new MongoDBAdapter(mongo.db("myapp"));
|
|
72
91
|
|
|
73
|
-
const
|
|
92
|
+
const keyRing = { "v1": await importKey(process.env.AEGIS_MASTER_KEY) };
|
|
74
93
|
|
|
75
94
|
const aegis = new AegisClient({
|
|
76
95
|
adapter,
|
|
@@ -78,13 +97,22 @@ const aegis = new AegisClient({
|
|
|
78
97
|
encryptedFields: {
|
|
79
98
|
users: ["ssn", "credit_card"]
|
|
80
99
|
}
|
|
81
|
-
},
|
|
100
|
+
}, keyRing, "v1");
|
|
101
|
+
|
|
102
|
+
const newUserId = uuidv4();
|
|
82
103
|
|
|
83
104
|
// Insert with a client-generated ID
|
|
84
|
-
await aegis.insert("users", {
|
|
105
|
+
await aegis.insert("users", {
|
|
106
|
+
_id: newUserId,
|
|
107
|
+
name: "Alice",
|
|
108
|
+
ssn: "123-45-6789"
|
|
109
|
+
});
|
|
85
110
|
|
|
86
111
|
// Select by an unencrypted field
|
|
87
|
-
const { data } = await aegis.select("users", {
|
|
112
|
+
const { data } = await aegis.select("users", {
|
|
113
|
+
column: "name",
|
|
114
|
+
value: "Alice"
|
|
115
|
+
});
|
|
88
116
|
// data[0].ssn → "123-45-6789" (decrypted)
|
|
89
117
|
```
|
|
90
118
|
|
|
@@ -104,21 +132,37 @@ class MyAdapter implements DatabaseAdapter {
|
|
|
104
132
|
// your select logic
|
|
105
133
|
return { data: [], error: null };
|
|
106
134
|
}
|
|
135
|
+
async update(table: string, data: Record<string, unknown>) {
|
|
136
|
+
// your update logic
|
|
137
|
+
return { data: [data], error: null };
|
|
138
|
+
}
|
|
139
|
+
async delete(table: string, query: { column: string; value: unknown }) {
|
|
140
|
+
// your delete logic
|
|
141
|
+
return { data: [], error: null };
|
|
142
|
+
}
|
|
107
143
|
}
|
|
108
144
|
```
|
|
109
145
|
|
|
110
146
|
## API
|
|
111
147
|
|
|
112
|
-
### `new AegisClient(config,
|
|
148
|
+
### `new AegisClient(config, keyRing, currentKeyVersion, bidxKey?)`
|
|
149
|
+
|
|
150
|
+
* **`config`** — `AegisConfig` object:
|
|
151
|
+
* **`adapter`**: any `DatabaseAdapter` (`SupabaseAdapter`, `MongoDBAdapter`, or custom)
|
|
152
|
+
* **`primaryKeyField`**: `string` (The ID field used for cryptographic contextual binding)
|
|
153
|
+
* **`encryptedFields`**: `Record<tableName, fieldName[]>`
|
|
154
|
+
* **`bidxFields`**: *(Optional)* `Record<tableName, fieldName[]>`
|
|
155
|
+
* **`keyRing`** — `Record<string, CryptoKey>` (A dictionary of all active and historical keys).
|
|
156
|
+
* **`currentKeyVersion`** — `string` (The version tag of the key to use for *new* encryptions).
|
|
157
|
+
* **`bidxKey`** — *(Optional)* `CryptoKey` from `generateBidxKey()` or `importKey()`
|
|
113
158
|
|
|
114
|
-
|
|
115
|
-
* `adapter`: any `DatabaseAdapter` (SupabaseAdapter, MongoDBAdapter, or custom)
|
|
116
|
-
* `primaryKeyField`: `string` (The ID field used for cryptographic contextual binding)
|
|
117
|
-
* `encryptedFields`: `Record<tableName, fieldName[]>`
|
|
118
|
-
* `bidxFields`: *(Optional)* `Record<tableName, fieldName[]>`
|
|
159
|
+
### `AegisClient` Methods
|
|
119
160
|
|
|
120
|
-
*
|
|
121
|
-
*
|
|
161
|
+
* **`insert(table, data)`**: Encrypts data and creates blind indexes before saving.
|
|
162
|
+
* **`select(table, query)`**: Fetches and automatically decrypts data. Intercepts blind index queries.
|
|
163
|
+
* **`update(table, data)`**: Re-encrypts modified fields with new IVs and updates blind indexes.
|
|
164
|
+
* **`delete(table, query)`**: Intercepts queries to securely delete by blind-indexed fields.
|
|
165
|
+
* **`selectRaw(table, query)`**: Bypasses decryption to return raw database records (useful for auditing or migrations).
|
|
122
166
|
|
|
123
167
|
### Adapters
|
|
124
168
|
|
|
@@ -130,14 +174,17 @@ class MyAdapter implements DatabaseAdapter {
|
|
|
130
174
|
* `generateKey()` / `generateBidxKey()`
|
|
131
175
|
* `exportKey(key)` / `importKey(base64)`
|
|
132
176
|
|
|
177
|
+
---
|
|
178
|
+
|
|
133
179
|
## How It Works (Security Architecture)
|
|
134
180
|
|
|
135
|
-
* **Web Crypto API (AES-256-GCM):** Runs natively in any modern browser or edge runtime.
|
|
136
|
-
* **
|
|
181
|
+
* **Web Crypto API (AES-256-GCM):** Runs natively in any modern browser or edge runtime without bloated dependencies.
|
|
182
|
+
* **Seamless Key Rotation:** Aegis-Lock accepts a dictionary of keys. It tags every database payload with the active key's version string. If a key is compromised, you simply introduce a `v2` key. New data is secured with `v2`, while historical data seamlessly decrypts using `v1` without a single second of database downtime.
|
|
183
|
+
* **Field-Level IVs:** Every single encrypted field gets a unique, mathematically random Initialization Vector (IV). The final database payload is formatted as `version:iv:ciphertext` to prevent keystream reuse attacks.
|
|
137
184
|
* **Contextual Binding (AAD):** Ciphertexts are cryptographically bound to the row's `primaryKeyField`. If an attacker copies an encrypted SSN from User A and pastes it into User B's row, the decryption will strictly fail.
|
|
138
185
|
* **Blind Indexing (HMAC-SHA256):** Because AES-GCM is non-deterministic (the same text encrypts differently every time), you cannot query standard encrypted fields. If configured via `bidxFields`, Aegis-Lock automatically generates deterministic, memory-aligned HMAC hashes. This allows you to search for exact matches (like looking up an email address) without exposing the plaintext to the database.
|
|
139
|
-
* **Database-
|
|
186
|
+
* **Database-Agnostic:** Swap adapters without changing your core application code.
|
|
140
187
|
|
|
141
188
|
## License
|
|
142
189
|
|
|
143
|
-
MIT
|
|
190
|
+
MIT
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from "../types";
|
|
2
2
|
/**
|
|
3
|
-
* MongoDB adapter for aegis-
|
|
3
|
+
* MongoDB adapter for aegis-lock.
|
|
4
4
|
*
|
|
5
|
-
* Works with any MongoDB client that exposes a `db()` method
|
|
6
|
-
* `mongodb` Node.js driver). Pass the Db instance directly.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { MongoClient } from "mongodb";
|
|
11
|
-
* import { MongoDBAdapter } from "aegis-mern/adapters/mongodb";
|
|
12
|
-
*
|
|
13
|
-
* const client = new MongoClient(uri);
|
|
14
|
-
* await client.connect();
|
|
15
|
-
* const adapter = new MongoDBAdapter(client.db("mydb"));
|
|
16
|
-
* ```
|
|
5
|
+
* Works with any MongoDB client that exposes a `db()` method.
|
|
17
6
|
*/
|
|
18
7
|
export declare class MongoDBAdapter implements DatabaseAdapter {
|
|
19
8
|
private db;
|
|
@@ -38,7 +27,23 @@ export declare class MongoDBAdapter implements DatabaseAdapter {
|
|
|
38
27
|
data: null;
|
|
39
28
|
error: unknown;
|
|
40
29
|
}>;
|
|
41
|
-
|
|
30
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
31
|
+
data: Record<string, unknown>[];
|
|
32
|
+
error: null;
|
|
33
|
+
} | {
|
|
34
|
+
data: null;
|
|
35
|
+
error: unknown;
|
|
36
|
+
}>;
|
|
37
|
+
delete(table: string, query: {
|
|
38
|
+
column: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
}): Promise<{
|
|
41
|
+
data: never[];
|
|
42
|
+
error: null;
|
|
43
|
+
} | {
|
|
44
|
+
data: null;
|
|
45
|
+
error: unknown;
|
|
46
|
+
}>;
|
|
42
47
|
get database(): any;
|
|
43
48
|
}
|
|
44
49
|
//# sourceMappingURL=mongodb.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb.d.ts","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD
|
|
1
|
+
{"version":3,"file":"mongodb.d.ts","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD;;;;GAIG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,OAAO,CAAC,EAAE,CAAM;gBAEJ,EAAE,EAAE,GAAG;IAIb,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;IAWnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAY7D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;;;;;;IAM9C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;IAqBnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE;;;;;;;IAWrE,IAAI,QAAQ,QAEX;CACF"}
|
package/dist/adapters/mongodb.js
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MongoDB adapter for aegis-
|
|
2
|
+
* MongoDB adapter for aegis-lock.
|
|
3
3
|
*
|
|
4
|
-
* Works with any MongoDB client that exposes a `db()` method
|
|
5
|
-
* `mongodb` Node.js driver). Pass the Db instance directly.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { MongoClient } from "mongodb";
|
|
10
|
-
* import { MongoDBAdapter } from "aegis-mern/adapters/mongodb";
|
|
11
|
-
*
|
|
12
|
-
* const client = new MongoClient(uri);
|
|
13
|
-
* await client.connect();
|
|
14
|
-
* const adapter = new MongoDBAdapter(client.db("mydb"));
|
|
15
|
-
* ```
|
|
4
|
+
* Works with any MongoDB client that exposes a `db()` method.
|
|
16
5
|
*/
|
|
17
6
|
export class MongoDBAdapter {
|
|
18
7
|
constructor(db) {
|
|
@@ -47,7 +36,32 @@ export class MongoDBAdapter {
|
|
|
47
36
|
return { data: null, error };
|
|
48
37
|
}
|
|
49
38
|
}
|
|
50
|
-
|
|
39
|
+
async update(table, data) {
|
|
40
|
+
try {
|
|
41
|
+
const collection = this.db.collection(table);
|
|
42
|
+
// Auto-detect the primary key field for MongoDB (usually _id or id)
|
|
43
|
+
const pkField = data._id ? '_id' : (data.id ? 'id' : null);
|
|
44
|
+
if (!pkField)
|
|
45
|
+
throw new Error("MongoDB Adapter: Missing '_id' or 'id' in data payload for update.");
|
|
46
|
+
const { [pkField]: pkValue, ...updateData } = data;
|
|
47
|
+
await collection.updateOne({ [pkField]: pkValue }, { $set: updateData });
|
|
48
|
+
return { data: [data], error: null };
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return { data: null, error };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async delete(table, query) {
|
|
55
|
+
try {
|
|
56
|
+
const collection = this.db.collection(table);
|
|
57
|
+
const filter = { [query.column]: query.value };
|
|
58
|
+
await collection.deleteMany(filter);
|
|
59
|
+
return { data: [], error: null };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return { data: null, error };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
51
65
|
get database() {
|
|
52
66
|
return this.db;
|
|
53
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb.js","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"mongodb.js","sourceRoot":"","sources":["../../adapters/mongodb.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,cAAc;IAGzB,YAAY,EAAO;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;YACrD,OAAO,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAA4D;QACtF,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,MAAM,GAA4B,EAAE,CAAC;YAC3C,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;gBAChD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;YACrC,CAAC;YACD,IAAI,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,KAAK,EAAE,KAAK,EAAE,CAAC;gBACjB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACrC,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,IAAiC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAE7C,oEAAoE;YACpE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3D,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;YAEpG,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC;YAEnD,MAAM,UAAU,CAAC,SAAS,CACxB,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EACtB,EAAE,IAAI,EAAE,UAAU,EAAE,CACrB,CAAC;YAEF,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAyC;QACnE,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/C,MAAM,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
2
|
import type { DatabaseAdapter } from "../types";
|
|
3
|
-
/** Supabase adapter for aegis-
|
|
3
|
+
/** Supabase adapter for aegis-lock. */
|
|
4
4
|
export declare class SupabaseAdapter implements DatabaseAdapter {
|
|
5
5
|
private supabase;
|
|
6
6
|
constructor(supabase: SupabaseClient);
|
|
@@ -16,7 +16,20 @@ export declare class SupabaseAdapter implements DatabaseAdapter {
|
|
|
16
16
|
data: Record<string, unknown>[] | null;
|
|
17
17
|
error: import("@supabase/postgrest-js").PostgrestError | null;
|
|
18
18
|
}>;
|
|
19
|
-
|
|
19
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
20
|
+
data: null;
|
|
21
|
+
error: Error;
|
|
22
|
+
} | {
|
|
23
|
+
data: Record<string, unknown>[] | null;
|
|
24
|
+
error: import("@supabase/postgrest-js").PostgrestError | null;
|
|
25
|
+
}>;
|
|
26
|
+
delete(table: string, query: {
|
|
27
|
+
column: string;
|
|
28
|
+
value: unknown;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
data: Record<string, unknown>[] | null;
|
|
31
|
+
error: import("@supabase/postgrest-js").PostgrestError | null;
|
|
32
|
+
}>;
|
|
20
33
|
get client(): SupabaseClient<any, "public", "public", any, any>;
|
|
21
34
|
}
|
|
22
35
|
//# sourceMappingURL=supabase.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,uCAAuC;AACvC,qBAAa,eAAgB,YAAW,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAiB;gBAErB,QAAQ,EAAE,cAAc;IAI9B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;cAE9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAS/D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,uCAAuC;AACvC,qBAAa,eAAgB,YAAW,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAiB;gBAErB,QAAQ,EAAE,cAAc;IAI9B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;cAE9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAS/D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;cAc9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAGrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE;cAO1C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;;;IAG3D,IAAI,MAAM,sDAET;CACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Supabase adapter for aegis-
|
|
1
|
+
/** Supabase adapter for aegis-lock. */
|
|
2
2
|
export class SupabaseAdapter {
|
|
3
3
|
constructor(supabase) {
|
|
4
4
|
this.supabase = supabase;
|
|
@@ -18,7 +18,27 @@ export class SupabaseAdapter {
|
|
|
18
18
|
const { data, error } = await q;
|
|
19
19
|
return { data: data, error };
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
async update(table, data) {
|
|
22
|
+
// Auto-detect primary key (Supabase usually uses 'id')
|
|
23
|
+
const pkField = data.id ? 'id' : (data._id ? '_id' : null);
|
|
24
|
+
if (!pkField) {
|
|
25
|
+
return { data: null, error: new Error("Supabase Adapter: Missing 'id' in data payload for update.") };
|
|
26
|
+
}
|
|
27
|
+
const { data: result, error } = await this.supabase
|
|
28
|
+
.from(table)
|
|
29
|
+
.update(data)
|
|
30
|
+
.eq(pkField, data[pkField])
|
|
31
|
+
.select();
|
|
32
|
+
return { data: result, error };
|
|
33
|
+
}
|
|
34
|
+
async delete(table, query) {
|
|
35
|
+
const { data: result, error } = await this.supabase
|
|
36
|
+
.from(table)
|
|
37
|
+
.delete()
|
|
38
|
+
.eq(query.column, query.value)
|
|
39
|
+
.select();
|
|
40
|
+
return { data: result, error };
|
|
41
|
+
}
|
|
22
42
|
get client() {
|
|
23
43
|
return this.supabase;
|
|
24
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAGA,uCAAuC;AACvC,MAAM,OAAO,eAAe;IAG1B,YAAY,QAAwB;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACtF,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAA4D;QACtF,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChD,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,KAAK,EAAE,KAAK,EAAE,CAAC;YACjB,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,IAAwC,EAAE,KAAK,EAAE,CAAC;IACnE,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../adapters/supabase.ts"],"names":[],"mappings":"AAGA,uCAAuC;AACvC,MAAM,OAAO,eAAe;IAG1B,YAAY,QAAwB;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACtF,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAA4D;QACtF,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChD,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,KAAK,EAAE,KAAK,EAAE,CAAC;YACjB,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,IAAwC,EAAE,KAAK,EAAE,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAA6B;QACvD,uDAAuD;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAE3D,IAAI,CAAC,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,4DAA4D,CAAC,EAAE,CAAC;QACzG,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;aAChD,IAAI,CAAC,KAAK,CAAC;aACX,MAAM,CAAC,IAAI,CAAC;aACZ,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;aAC1B,MAAM,EAAE,CAAC;QAEZ,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAyC;QACnE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;aAChD,IAAI,CAAC,KAAK,CAAC;aACX,MAAM,EAAE;aACR,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;aAC7B,MAAM,EAAE,CAAC;QAEZ,OAAO,EAAE,IAAI,EAAE,MAA0C,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
import type { DatabaseAdapter, AegisConfig } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* The primary client for aegis-lock.
|
|
4
|
+
* Handles automatic encryption, decryption, contextual binding, blind indexing,
|
|
5
|
+
* and Key Rotation before interacting with the underlying database adapter.
|
|
6
|
+
*/
|
|
2
7
|
export declare class AegisClient {
|
|
3
8
|
private adapter;
|
|
4
|
-
private
|
|
9
|
+
private keyRing;
|
|
10
|
+
private currentKeyVersion;
|
|
5
11
|
private bidxKey?;
|
|
6
12
|
private encryptedFields;
|
|
7
13
|
private bidxFields;
|
|
8
14
|
private primaryKeyField;
|
|
9
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Initializes the AegisClient with Key Rotation support.
|
|
17
|
+
*
|
|
18
|
+
* @param config - Configuration including the database adapter, primary key field, and field rules.
|
|
19
|
+
* @param keyRing - A dictionary of historical and active CryptoKeys (e.g., { v1: key1, v2: key2 }).
|
|
20
|
+
* @param currentKeyVersion - The string identifier of the key to use for NEW encryptions (e.g., "v2").
|
|
21
|
+
* @param bidxKey - (Optional) The CryptoKey used for generating HMAC-SHA256 blind indexes.
|
|
22
|
+
*/
|
|
23
|
+
constructor(config: AegisConfig, keyRing: Record<string, CryptoKey>, currentKeyVersion: string, bidxKey?: CryptoKey);
|
|
24
|
+
/**
|
|
25
|
+
* Securely inserts a new record into the database.
|
|
26
|
+
* Automatically generates blind indexes and encrypts configured fields using the CURRENT key version.
|
|
27
|
+
*/
|
|
10
28
|
insert(table: string, data: Record<string, unknown>): Promise<{
|
|
11
29
|
data: Record<string, unknown>[] | null;
|
|
12
30
|
error: unknown;
|
|
13
31
|
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves and automatically decrypts records from the database.
|
|
34
|
+
* Dynamically selects the correct decryption key based on the payload's version tag.
|
|
35
|
+
*/
|
|
14
36
|
select(table: string, query?: {
|
|
15
37
|
column?: string;
|
|
16
38
|
value?: unknown;
|
|
@@ -19,6 +41,21 @@ export declare class AegisClient {
|
|
|
19
41
|
data: Record<string, unknown>[] | null;
|
|
20
42
|
error: unknown;
|
|
21
43
|
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Securely modifies an existing record.
|
|
46
|
+
* Re-encrypts data with a new unique IV and the CURRENT key version.
|
|
47
|
+
*/
|
|
48
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
49
|
+
data: any[] | null;
|
|
50
|
+
error: any;
|
|
51
|
+
}>;
|
|
52
|
+
delete(table: string, query: {
|
|
53
|
+
column: string;
|
|
54
|
+
value: unknown;
|
|
55
|
+
}): Promise<{
|
|
56
|
+
data: any[] | null;
|
|
57
|
+
error: any;
|
|
58
|
+
}>;
|
|
22
59
|
selectRaw(table: string, query?: {
|
|
23
60
|
column?: string;
|
|
24
61
|
value?: unknown;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG5D,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG5D;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,OAAO,CAAC,CAAY;IAC5B,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,eAAe,CAAS;IAEhC;;;;;;;OAOG;gBAED,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAClC,iBAAiB,EAAE,MAAM,EACzB,OAAO,CAAC,EAAE,SAAS;IAWrB;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;IA6CzD;;;OAGG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;;;IAgE9D;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;IAsCnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE;;;;IAgB/D,SAAS,CACb,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;;;IAK9D,IAAI,EAAE,IAAI,eAAe,CAExB;CACF"}
|
package/dist/client.js
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
import { encrypt, decrypt, generateBlindIndex } from "./crypto";
|
|
2
|
+
/**
|
|
3
|
+
* The primary client for aegis-lock.
|
|
4
|
+
* Handles automatic encryption, decryption, contextual binding, blind indexing,
|
|
5
|
+
* and Key Rotation before interacting with the underlying database adapter.
|
|
6
|
+
*/
|
|
2
7
|
export class AegisClient {
|
|
3
|
-
|
|
4
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the AegisClient with Key Rotation support.
|
|
10
|
+
*
|
|
11
|
+
* @param config - Configuration including the database adapter, primary key field, and field rules.
|
|
12
|
+
* @param keyRing - A dictionary of historical and active CryptoKeys (e.g., { v1: key1, v2: key2 }).
|
|
13
|
+
* @param currentKeyVersion - The string identifier of the key to use for NEW encryptions (e.g., "v2").
|
|
14
|
+
* @param bidxKey - (Optional) The CryptoKey used for generating HMAC-SHA256 blind indexes.
|
|
15
|
+
*/
|
|
16
|
+
constructor(config, keyRing, currentKeyVersion, bidxKey) {
|
|
5
17
|
this.adapter = config.adapter;
|
|
6
|
-
this.
|
|
18
|
+
this.keyRing = keyRing;
|
|
19
|
+
this.currentKeyVersion = currentKeyVersion;
|
|
7
20
|
this.bidxKey = bidxKey;
|
|
8
21
|
this.encryptedFields = config.encryptedFields;
|
|
9
22
|
this.bidxFields = config.bidxFields || {};
|
|
10
23
|
this.primaryKeyField = config.primaryKeyField;
|
|
11
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Securely inserts a new record into the database.
|
|
27
|
+
* Automatically generates blind indexes and encrypts configured fields using the CURRENT key version.
|
|
28
|
+
*/
|
|
12
29
|
async insert(table, data) {
|
|
13
30
|
const fields = this.encryptedFields[table] || [];
|
|
14
31
|
const bidxFields = this.bidxFields[table] || [];
|
|
@@ -19,29 +36,32 @@ export class AegisClient {
|
|
|
19
36
|
throw new Error(`Aegis: Cannot encrypt. Missing primary key '${this.primaryKeyField}' in payload.`);
|
|
20
37
|
}
|
|
21
38
|
const aadContext = String(rowId);
|
|
22
|
-
|
|
39
|
+
const activeKey = this.keyRing[this.currentKeyVersion]; // Grab the active key
|
|
40
|
+
// 1. Handle Blind Indexing FIRST
|
|
23
41
|
for (const field of bidxFields) {
|
|
24
42
|
if (row[field] != null && this.bidxKey) {
|
|
25
|
-
row[`${field}_bidx`] = await generateBlindIndex(String(row[field]),
|
|
26
|
-
this.bidxKey);
|
|
43
|
+
row[`${field}_bidx`] = await generateBlindIndex(String(row[field]), this.bidxKey);
|
|
27
44
|
}
|
|
28
45
|
}
|
|
29
|
-
// 2. Handle Encryption SECOND
|
|
46
|
+
// 2. Handle Encryption SECOND
|
|
30
47
|
for (const field of fields) {
|
|
31
48
|
if (row[field] != null) {
|
|
32
|
-
|
|
33
|
-
row[field]
|
|
49
|
+
// Pass the active key and the version tag to the new encrypt function
|
|
50
|
+
const { version, ciphertext, iv } = await encrypt(String(row[field]), activeKey, this.currentKeyVersion, aadContext);
|
|
51
|
+
// Store it in the DB as "version:iv:ciphertext"
|
|
52
|
+
row[field] = `${version}:${iv}:${ciphertext}`;
|
|
34
53
|
}
|
|
35
54
|
}
|
|
36
55
|
}
|
|
37
56
|
return this.adapter.insert(table, row);
|
|
38
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Retrieves and automatically decrypts records from the database.
|
|
60
|
+
* Dynamically selects the correct decryption key based on the payload's version tag.
|
|
61
|
+
*/
|
|
39
62
|
async select(table, query) {
|
|
40
|
-
// Intercept and swap query for Blind Indexing (NEW)
|
|
41
63
|
const bidxFields = this.bidxFields[table] || [];
|
|
42
64
|
let activeQuery = query;
|
|
43
|
-
// Make sure this uses THIS.bidxKey
|
|
44
|
-
// Inside your select method in client.ts
|
|
45
65
|
if (query?.column &&
|
|
46
66
|
query?.value &&
|
|
47
67
|
bidxFields.includes(query.column) &&
|
|
@@ -50,7 +70,7 @@ export class AegisClient {
|
|
|
50
70
|
activeQuery = {
|
|
51
71
|
...query,
|
|
52
72
|
column: `${query.column}_bidx`,
|
|
53
|
-
value: blindIndexValue,
|
|
73
|
+
value: blindIndexValue,
|
|
54
74
|
};
|
|
55
75
|
}
|
|
56
76
|
const { data, error } = await this.adapter.select(table, activeQuery);
|
|
@@ -66,14 +86,19 @@ export class AegisClient {
|
|
|
66
86
|
return decRow;
|
|
67
87
|
const aadContext = String(rowId);
|
|
68
88
|
for (const field of fields) {
|
|
69
|
-
const
|
|
70
|
-
if (typeof
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
const payloadStr = decRow[field];
|
|
90
|
+
if (typeof payloadStr === "string" && payloadStr.includes(":")) {
|
|
91
|
+
const parts = payloadStr.split(":");
|
|
92
|
+
// Check for our new 3-part versioned payload
|
|
93
|
+
if (parts.length === 3) {
|
|
94
|
+
const [version, iv, ciphertext] = parts;
|
|
95
|
+
try {
|
|
96
|
+
// Pass the whole KeyRing. crypto.ts will pick the right one.
|
|
97
|
+
decRow[field] = await decrypt({ version, ciphertext, iv }, this.keyRing, aadContext);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Tampering detected or wrong key
|
|
101
|
+
}
|
|
77
102
|
}
|
|
78
103
|
}
|
|
79
104
|
}
|
|
@@ -81,6 +106,48 @@ export class AegisClient {
|
|
|
81
106
|
}));
|
|
82
107
|
return { data: decrypted, error };
|
|
83
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Securely modifies an existing record.
|
|
111
|
+
* Re-encrypts data with a new unique IV and the CURRENT key version.
|
|
112
|
+
*/
|
|
113
|
+
async update(table, data) {
|
|
114
|
+
const fields = this.encryptedFields[table] || [];
|
|
115
|
+
const bidxFields = this.bidxFields[table] || [];
|
|
116
|
+
const row = { ...data };
|
|
117
|
+
const rowId = row[this.primaryKeyField];
|
|
118
|
+
if (!rowId) {
|
|
119
|
+
throw new Error(`Aegis: Cannot update. Missing primary key '${this.primaryKeyField}' in payload.`);
|
|
120
|
+
}
|
|
121
|
+
const aadContext = String(rowId);
|
|
122
|
+
const activeKey = this.keyRing[this.currentKeyVersion];
|
|
123
|
+
if (fields.length > 0 || bidxFields.length > 0) {
|
|
124
|
+
for (const field of bidxFields) {
|
|
125
|
+
if (row[field] != null && this.bidxKey) {
|
|
126
|
+
row[`${field}_bidx`] = await generateBlindIndex(String(row[field]), this.bidxKey);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const field of fields) {
|
|
130
|
+
if (row[field] != null) {
|
|
131
|
+
const { version, ciphertext, iv } = await encrypt(String(row[field]), activeKey, this.currentKeyVersion, aadContext);
|
|
132
|
+
row[field] = `${version}:${iv}:${ciphertext}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return this.adapter.update(table, row);
|
|
137
|
+
}
|
|
138
|
+
async delete(table, query) {
|
|
139
|
+
const bidxFields = this.bidxFields[table] || [];
|
|
140
|
+
let activeQuery = query;
|
|
141
|
+
if (bidxFields.includes(query.column) && this.bidxKey) {
|
|
142
|
+
const blindIndexValue = await generateBlindIndex(String(query.value), this.bidxKey);
|
|
143
|
+
activeQuery = {
|
|
144
|
+
...query,
|
|
145
|
+
column: `${query.column}_bidx`,
|
|
146
|
+
value: blindIndexValue,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return this.adapter.delete(table, activeQuery);
|
|
150
|
+
}
|
|
84
151
|
async selectRaw(table, query) {
|
|
85
152
|
return this.adapter.select(table, query);
|
|
86
153
|
}
|
|
@@ -88,138 +155,4 @@ export class AegisClient {
|
|
|
88
155
|
return this.adapter;
|
|
89
156
|
}
|
|
90
157
|
}
|
|
91
|
-
// export class AegisClient {
|
|
92
|
-
// private adapter: DatabaseAdapter;
|
|
93
|
-
// private key: CryptoKey;
|
|
94
|
-
// private bidxKey?: CryptoKey;
|
|
95
|
-
// private encryptedFields: Record<string, string[]>;
|
|
96
|
-
// private bidxFields: Record<string, string[]>;
|
|
97
|
-
// private primaryKeyField: string;
|
|
98
|
-
// constructor(config: AegisConfig, key: CryptoKey) {
|
|
99
|
-
// this.adapter = config.adapter;
|
|
100
|
-
// this.key = key;
|
|
101
|
-
// this.bidxKey = bidxKey;
|
|
102
|
-
// this.encryptedFields = config.encryptedFields;
|
|
103
|
-
// this.bidxFields = config.bidxFields || {};
|
|
104
|
-
// this.primaryKeyField = config.primaryKeyField;
|
|
105
|
-
// }
|
|
106
|
-
// async insert(table: string, data: Record<string, unknown>) {
|
|
107
|
-
// const fields = this.encryptedFields[table] || [];
|
|
108
|
-
// const row: Record<string, unknown> = { ...data };
|
|
109
|
-
// if (fields.length > 0) {
|
|
110
|
-
// const rowId = row[this.primaryKeyField];
|
|
111
|
-
// if (!rowId) {
|
|
112
|
-
// throw new Error(`Aegis: Cannot encrypt. Missing primary key '${this.primaryKeyField}' in payload.`);
|
|
113
|
-
// }
|
|
114
|
-
// const aadContext = String(rowId);
|
|
115
|
-
// for (const field of fields) {
|
|
116
|
-
// if (row[field] != null) {
|
|
117
|
-
// // Generates unique IV per field and binds to the row ID
|
|
118
|
-
// const { ciphertext, iv } = await encrypt(String(row[field]), this.key, aadContext);
|
|
119
|
-
// row[field] = `${iv}:${ciphertext}`;
|
|
120
|
-
// }
|
|
121
|
-
// }
|
|
122
|
-
// }
|
|
123
|
-
// return this.adapter.insert(table, row);
|
|
124
|
-
// }
|
|
125
|
-
// async select(table: string, query?: { column?: string; value?: unknown; limit?: number }) {
|
|
126
|
-
// const { data, error } = await this.adapter.select(table, query);
|
|
127
|
-
// if (error || !data) return { data, error };
|
|
128
|
-
// const fields = this.encryptedFields[table] || [];
|
|
129
|
-
// if (fields.length === 0) return { data, error };
|
|
130
|
-
// const decrypted = await Promise.all(
|
|
131
|
-
// data.map(async (row: Record<string, unknown>) => {
|
|
132
|
-
// const decRow = { ...row };
|
|
133
|
-
// const rowId = decRow[this.primaryKeyField];
|
|
134
|
-
// if (!rowId) return decRow; // Cannot decrypt without context
|
|
135
|
-
// const aadContext = String(rowId);
|
|
136
|
-
// for (const field of fields) {
|
|
137
|
-
// const payload = decRow[field];
|
|
138
|
-
// if (typeof payload === 'string' && payload.includes(':')) {
|
|
139
|
-
// const [iv, ciphertext] = payload.split(':');
|
|
140
|
-
// try {
|
|
141
|
-
// decRow[field] = await decrypt({ ciphertext, iv }, this.key, aadContext);
|
|
142
|
-
// } catch {
|
|
143
|
-
// // Tampering detected (invalid tag or swapped ciphertext). Leave as raw string.
|
|
144
|
-
// }
|
|
145
|
-
// }
|
|
146
|
-
// }
|
|
147
|
-
// return decRow;
|
|
148
|
-
// })
|
|
149
|
-
// );
|
|
150
|
-
// return { data: decrypted, error };
|
|
151
|
-
// }
|
|
152
|
-
// async selectRaw(table: string, query?: { column?: string; value?: unknown; limit?: number }) {
|
|
153
|
-
// return this.adapter.select(table, query);
|
|
154
|
-
// }
|
|
155
|
-
// get db(): DatabaseAdapter {
|
|
156
|
-
// return this.adapter;
|
|
157
|
-
// }
|
|
158
|
-
// }
|
|
159
|
-
// import type { DatabaseAdapter } from "./types";
|
|
160
|
-
// import { encrypt, decrypt } from "./crypto";
|
|
161
|
-
// export class AegisClient {
|
|
162
|
-
// private adapter: DatabaseAdapter;
|
|
163
|
-
// private key: CryptoKey;
|
|
164
|
-
// private encryptedFields: Record<string, string[]>;
|
|
165
|
-
// constructor(adapter: DatabaseAdapter, key: CryptoKey, encryptedFields: Record<string, string[]>) {
|
|
166
|
-
// this.adapter = adapter;
|
|
167
|
-
// this.key = key;
|
|
168
|
-
// this.encryptedFields = encryptedFields;
|
|
169
|
-
// }
|
|
170
|
-
// /** Insert a row, auto-encrypting configured fields. Returns a shared IV for the row. */
|
|
171
|
-
// async insert(table: string, data: Record<string, unknown>) {
|
|
172
|
-
// const fields = this.encryptedFields[table] || [];
|
|
173
|
-
// const row: Record<string, unknown> = { ...data };
|
|
174
|
-
// // Generate a single IV and reuse it for all fields in this row
|
|
175
|
-
// const ivBuffer = crypto.getRandomValues(new Uint8Array(12));
|
|
176
|
-
// const sharedIv = btoa(String.fromCharCode(...ivBuffer));
|
|
177
|
-
// for (const field of fields) {
|
|
178
|
-
// if (row[field] != null) {
|
|
179
|
-
// const { ciphertext } = await encrypt(String(row[field]), this.key, ivBuffer);
|
|
180
|
-
// row[field] = ciphertext;
|
|
181
|
-
// }
|
|
182
|
-
// }
|
|
183
|
-
// if (fields.length > 0) {
|
|
184
|
-
// row.iv = sharedIv;
|
|
185
|
-
// }
|
|
186
|
-
// return this.adapter.insert(table, row);
|
|
187
|
-
// }
|
|
188
|
-
// /** Select rows, auto-decrypting configured fields. */
|
|
189
|
-
// async select(table: string, query?: { column?: string; value?: unknown; limit?: number }) {
|
|
190
|
-
// const { data, error } = await this.adapter.select(table, query);
|
|
191
|
-
// if (error || !data) return { data, error };
|
|
192
|
-
// const fields = this.encryptedFields[table] || [];
|
|
193
|
-
// if (fields.length === 0) return { data, error };
|
|
194
|
-
// const decrypted = await Promise.all(
|
|
195
|
-
// data.map(async (row: Record<string, unknown>) => {
|
|
196
|
-
// const decRow = { ...row };
|
|
197
|
-
// const iv = row.iv as string;
|
|
198
|
-
// if (!iv) return decRow;
|
|
199
|
-
// for (const field of fields) {
|
|
200
|
-
// if (decRow[field] != null) {
|
|
201
|
-
// try {
|
|
202
|
-
// decRow[field] = await decrypt(
|
|
203
|
-
// { ciphertext: String(decRow[field]), iv },
|
|
204
|
-
// this.key
|
|
205
|
-
// );
|
|
206
|
-
// } catch {
|
|
207
|
-
// // leave as ciphertext if decryption fails
|
|
208
|
-
// }
|
|
209
|
-
// }
|
|
210
|
-
// }
|
|
211
|
-
// return decRow;
|
|
212
|
-
// })
|
|
213
|
-
// );
|
|
214
|
-
// return { data: decrypted, error };
|
|
215
|
-
// }
|
|
216
|
-
// /** Select rows WITHOUT decrypting — exposes raw ciphertext. */
|
|
217
|
-
// async selectRaw(table: string, query?: { column?: string; value?: unknown; limit?: number }) {
|
|
218
|
-
// return this.adapter.select(table, query);
|
|
219
|
-
// }
|
|
220
|
-
// /** Access the underlying database adapter. */
|
|
221
|
-
// get db(): DatabaseAdapter {
|
|
222
|
-
// return this.adapter;
|
|
223
|
-
// }
|
|
224
|
-
// }
|
|
225
158
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,OAAO,WAAW;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"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"}
|
package/dist/crypto.d.ts
CHANGED
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
import { EncryptedPayload } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Generates a strong, 256-bit master key for AES-GCM encryption and decryption.
|
|
4
|
+
* * @returns A Promise resolving to a native Web Crypto API CryptoKey.
|
|
5
|
+
*/
|
|
2
6
|
export declare function generateKey(): Promise<CryptoKey>;
|
|
7
|
+
/**
|
|
8
|
+
* Exports a CryptoKey into a raw Base64 string so it can be safely stored
|
|
9
|
+
* in an environment variable or Key Management Service (KMS).
|
|
10
|
+
* * @param key - The CryptoKey to export.
|
|
11
|
+
* @returns A Promise resolving to the Base64 representation of the key.
|
|
12
|
+
*/
|
|
3
13
|
export declare function exportKey(key: CryptoKey): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Imports a Base64 string back into a usable CryptoKey for AES-GCM operations.
|
|
16
|
+
* * @param base64 - The Base64 encoded key string (usually from environment variables).
|
|
17
|
+
* @returns A Promise resolving to the imported CryptoKey.
|
|
18
|
+
*/
|
|
4
19
|
export declare function importKey(base64: string): Promise<CryptoKey>;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Encrypts a plaintext string using AES-256-GCM.
|
|
22
|
+
* Automatically generates a unique 12-byte Initialization Vector (IV) and applies
|
|
23
|
+
* Contextual Binding (AAD) to tie the ciphertext to a specific database row.
|
|
24
|
+
* @param plaintext - The sensitive data to encrypt.
|
|
25
|
+
* @param key - The master AES-GCM CryptoKey.
|
|
26
|
+
* @param keyVersion - The string identifier for the active key (e.g., "v2").
|
|
27
|
+
* @param aadContext - The row ID or primary key to bind this ciphertext to.
|
|
28
|
+
* @returns A Promise resolving to an object containing the Base64 `ciphertext` and `iv`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function encrypt(plaintext: string, key: CryptoKey, keyVersion: string, aadContext: string): Promise<EncryptedPayload>;
|
|
31
|
+
/**
|
|
32
|
+
* Decrypts an AES-256-GCM payload back into plaintext.
|
|
33
|
+
* Supports Key Rotation by reading the payload's version and selecting the correct key.
|
|
34
|
+
* Strictly verifies the Context (AAD) to ensure data integrity.
|
|
35
|
+
*
|
|
36
|
+
* @param payload - The EncryptedPayload containing `version`, `ciphertext`, and `iv`.
|
|
37
|
+
* @param keyRing - A dictionary of historical and active CryptoKeys (e.g., { "v1": key1, "v2": key2 }).
|
|
38
|
+
* @param aadContext - The row ID or primary key that this ciphertext is bound to.
|
|
39
|
+
* @returns A Promise resolving to the decrypted plaintext string.
|
|
40
|
+
*/
|
|
41
|
+
export declare function decrypt(payload: EncryptedPayload, keyRing: Record<string, CryptoKey>, // Accepts multiple keys
|
|
42
|
+
aadContext: string): Promise<string>;
|
|
43
|
+
/** * Generates a dedicated HMAC-SHA256 key for deterministic Blind Indexing.
|
|
44
|
+
* This key should be kept separate from the master encryption key.
|
|
45
|
+
* * @returns A Promise resolving to an HMAC CryptoKey.
|
|
46
|
+
*/
|
|
8
47
|
export declare function generateBidxKey(): Promise<CryptoKey>;
|
|
9
|
-
/** Generates a deterministic hash for database searching
|
|
48
|
+
/** * Generates a deterministic hash for database searching without exposing plaintext.
|
|
49
|
+
* Safely extracts the underlying memory buffer to guarantee identical hashes
|
|
50
|
+
* across different JavaScript runtimes (Node.js, Browsers, Edge).
|
|
51
|
+
* * @param plaintext - The data to hash (e.g., an email address).
|
|
52
|
+
* @param key - The HMAC Blind Index CryptoKey.
|
|
53
|
+
* @returns A Promise resolving to a deterministic Base64 hash string.
|
|
54
|
+
*/
|
|
10
55
|
export declare function generateBlindIndex(plaintext: string, key: CryptoKey): Promise<string>;
|
|
11
56
|
//# sourceMappingURL=crypto.d.ts.map
|
package/dist/crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAqC3C;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,SAAS,CAAC,CAKtD;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAG/D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CASlE;AAED;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAsB3B;AAED;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,wBAAwB;AAC5D,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAID;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAK1D;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
|
package/dist/crypto.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
const ALGO = "AES-GCM";
|
|
2
2
|
const KEY_LENGTH = 256;
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Safely converts an ArrayBuffer or Uint8Array to a Base64 string.
|
|
5
|
+
* Used internally to format ciphertexts, IVs, and exported keys for safe database storage.
|
|
6
|
+
* * @param buffer - The raw byte array to convert.
|
|
7
|
+
* @returns A Base64 encoded string.
|
|
8
|
+
*/
|
|
4
9
|
function bufferToBase64(buffer) {
|
|
5
10
|
let binary = "";
|
|
6
11
|
// Normalize to Uint8Array safely
|
|
@@ -11,6 +16,12 @@ function bufferToBase64(buffer) {
|
|
|
11
16
|
}
|
|
12
17
|
return btoa(binary);
|
|
13
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Converts a Base64 string back into a Uint8Array.
|
|
21
|
+
* Used internally to reconstruct buffers for decryption or key importing.
|
|
22
|
+
* * @param base64 - The Base64 encoded string.
|
|
23
|
+
* @returns A Uint8Array containing the raw bytes.
|
|
24
|
+
*/
|
|
14
25
|
function base64ToBuffer(base64) {
|
|
15
26
|
const binaryString = atob(base64);
|
|
16
27
|
const bytes = new Uint8Array(binaryString.length);
|
|
@@ -19,21 +30,46 @@ function base64ToBuffer(base64) {
|
|
|
19
30
|
}
|
|
20
31
|
return bytes;
|
|
21
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Generates a strong, 256-bit master key for AES-GCM encryption and decryption.
|
|
35
|
+
* * @returns A Promise resolving to a native Web Crypto API CryptoKey.
|
|
36
|
+
*/
|
|
22
37
|
export async function generateKey() {
|
|
23
38
|
return crypto.subtle.generateKey({ name: ALGO, length: KEY_LENGTH }, true, [
|
|
24
39
|
"encrypt",
|
|
25
40
|
"decrypt",
|
|
26
41
|
]);
|
|
27
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Exports a CryptoKey into a raw Base64 string so it can be safely stored
|
|
45
|
+
* in an environment variable or Key Management Service (KMS).
|
|
46
|
+
* * @param key - The CryptoKey to export.
|
|
47
|
+
* @returns A Promise resolving to the Base64 representation of the key.
|
|
48
|
+
*/
|
|
28
49
|
export async function exportKey(key) {
|
|
29
50
|
const raw = await crypto.subtle.exportKey("raw", key);
|
|
30
51
|
return bufferToBase64(raw);
|
|
31
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Imports a Base64 string back into a usable CryptoKey for AES-GCM operations.
|
|
55
|
+
* * @param base64 - The Base64 encoded key string (usually from environment variables).
|
|
56
|
+
* @returns A Promise resolving to the imported CryptoKey.
|
|
57
|
+
*/
|
|
32
58
|
export async function importKey(base64) {
|
|
33
59
|
const raw = base64ToBuffer(base64);
|
|
34
60
|
return crypto.subtle.importKey("raw", raw, { name: ALGO, length: KEY_LENGTH }, true, ["encrypt", "decrypt"]);
|
|
35
61
|
}
|
|
36
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Encrypts a plaintext string using AES-256-GCM.
|
|
64
|
+
* Automatically generates a unique 12-byte Initialization Vector (IV) and applies
|
|
65
|
+
* Contextual Binding (AAD) to tie the ciphertext to a specific database row.
|
|
66
|
+
* @param plaintext - The sensitive data to encrypt.
|
|
67
|
+
* @param key - The master AES-GCM CryptoKey.
|
|
68
|
+
* @param keyVersion - The string identifier for the active key (e.g., "v2").
|
|
69
|
+
* @param aadContext - The row ID or primary key to bind this ciphertext to.
|
|
70
|
+
* @returns A Promise resolving to an object containing the Base64 `ciphertext` and `iv`.
|
|
71
|
+
*/
|
|
72
|
+
export async function encrypt(plaintext, key, keyVersion, aadContext) {
|
|
37
73
|
const iv = crypto.getRandomValues(new Uint8Array(12)); // ALWAYS unique
|
|
38
74
|
const encoded = new TextEncoder().encode(plaintext);
|
|
39
75
|
const algorithm = {
|
|
@@ -44,9 +80,25 @@ export async function encrypt(plaintext, key, aadContext) {
|
|
|
44
80
|
algorithm.additionalData = new TextEncoder().encode(aadContext);
|
|
45
81
|
}
|
|
46
82
|
const cipherBuffer = await crypto.subtle.encrypt(algorithm, key, encoded);
|
|
47
|
-
return { ciphertext: bufferToBase64(cipherBuffer), iv: bufferToBase64(iv) };
|
|
83
|
+
return { version: keyVersion, ciphertext: bufferToBase64(cipherBuffer), iv: bufferToBase64(iv) };
|
|
48
84
|
}
|
|
49
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Decrypts an AES-256-GCM payload back into plaintext.
|
|
87
|
+
* Supports Key Rotation by reading the payload's version and selecting the correct key.
|
|
88
|
+
* Strictly verifies the Context (AAD) to ensure data integrity.
|
|
89
|
+
*
|
|
90
|
+
* @param payload - The EncryptedPayload containing `version`, `ciphertext`, and `iv`.
|
|
91
|
+
* @param keyRing - A dictionary of historical and active CryptoKeys (e.g., { "v1": key1, "v2": key2 }).
|
|
92
|
+
* @param aadContext - The row ID or primary key that this ciphertext is bound to.
|
|
93
|
+
* @returns A Promise resolving to the decrypted plaintext string.
|
|
94
|
+
*/
|
|
95
|
+
export async function decrypt(payload, keyRing, // Accepts multiple keys
|
|
96
|
+
aadContext) {
|
|
97
|
+
// Identify which key to use based on the payload's version tag
|
|
98
|
+
const targetKey = keyRing[payload.version];
|
|
99
|
+
if (!targetKey) {
|
|
100
|
+
throw new Error(`CRITICAL: Key version [${payload.version}] not found in provided KeyRing.`);
|
|
101
|
+
}
|
|
50
102
|
const cipherBytes = base64ToBuffer(payload.ciphertext);
|
|
51
103
|
const iv = base64ToBuffer(payload.iv);
|
|
52
104
|
const algorithm = {
|
|
@@ -56,18 +108,28 @@ export async function decrypt(payload, key, aadContext) {
|
|
|
56
108
|
if (aadContext) {
|
|
57
109
|
algorithm.additionalData = new TextEncoder().encode(aadContext);
|
|
58
110
|
}
|
|
59
|
-
const decrypted = await crypto.subtle.decrypt(algorithm,
|
|
111
|
+
const decrypted = await crypto.subtle.decrypt(algorithm, targetKey, // Use the dynamically selected key
|
|
112
|
+
cipherBytes);
|
|
60
113
|
return new TextDecoder().decode(decrypted);
|
|
61
114
|
}
|
|
62
115
|
const HMAC_ALGO = "HMAC";
|
|
63
|
-
/** Generates a dedicated key for deterministic Blind Indexing
|
|
116
|
+
/** * Generates a dedicated HMAC-SHA256 key for deterministic Blind Indexing.
|
|
117
|
+
* This key should be kept separate from the master encryption key.
|
|
118
|
+
* * @returns A Promise resolving to an HMAC CryptoKey.
|
|
119
|
+
*/
|
|
64
120
|
export async function generateBidxKey() {
|
|
65
121
|
return crypto.subtle.generateKey({ name: HMAC_ALGO, hash: "SHA-256" }, true, [
|
|
66
122
|
"sign",
|
|
67
123
|
"verify",
|
|
68
124
|
]);
|
|
69
125
|
}
|
|
70
|
-
/** Generates a deterministic hash for database searching
|
|
126
|
+
/** * Generates a deterministic hash for database searching without exposing plaintext.
|
|
127
|
+
* Safely extracts the underlying memory buffer to guarantee identical hashes
|
|
128
|
+
* across different JavaScript runtimes (Node.js, Browsers, Edge).
|
|
129
|
+
* * @param plaintext - The data to hash (e.g., an email address).
|
|
130
|
+
* @param key - The HMAC Blind Index CryptoKey.
|
|
131
|
+
* @returns A Promise resolving to a deterministic Base64 hash string.
|
|
132
|
+
*/
|
|
71
133
|
export async function generateBlindIndex(plaintext, key) {
|
|
72
134
|
const encoder = new TextEncoder();
|
|
73
135
|
const encoded = encoder.encode(plaintext);
|
|
@@ -77,40 +139,4 @@ export async function generateBlindIndex(plaintext, key) {
|
|
|
77
139
|
const signature = await crypto.subtle.sign("HMAC", key, cleanBuffer);
|
|
78
140
|
return bufferToBase64(signature);
|
|
79
141
|
}
|
|
80
|
-
// import { EncryptedPayload } from "./types";
|
|
81
|
-
// const ALGO = "AES-GCM";
|
|
82
|
-
// const KEY_LENGTH = 256;
|
|
83
|
-
// export async function generateKey(): Promise<CryptoKey> {
|
|
84
|
-
// return crypto.subtle.generateKey(
|
|
85
|
-
// { name: ALGO, length: KEY_LENGTH },
|
|
86
|
-
// true,
|
|
87
|
-
// ["encrypt", "decrypt"]
|
|
88
|
-
// );
|
|
89
|
-
// }
|
|
90
|
-
// export async function exportKey(key: CryptoKey): Promise<string> {
|
|
91
|
-
// const raw = await crypto.subtle.exportKey("raw", key);
|
|
92
|
-
// return btoa(String.fromCharCode(...new Uint8Array(raw)));
|
|
93
|
-
// }
|
|
94
|
-
// export async function importKey(base64: string): Promise<CryptoKey> {
|
|
95
|
-
// const raw = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
|
96
|
-
// return crypto.subtle.importKey("raw", raw, { name: ALGO, length: KEY_LENGTH }, true, [
|
|
97
|
-
// "encrypt",
|
|
98
|
-
// "decrypt",
|
|
99
|
-
// ]);
|
|
100
|
-
// }
|
|
101
|
-
// export async function encrypt(plaintext: string, key: CryptoKey, existingIv?: Uint8Array): Promise<EncryptedPayload> {
|
|
102
|
-
// const iv = existingIv ? new Uint8Array(existingIv) : crypto.getRandomValues(new Uint8Array(12));
|
|
103
|
-
// const encoded = new TextEncoder().encode(plaintext);
|
|
104
|
-
// const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);
|
|
105
|
-
// return {
|
|
106
|
-
// ciphertext: btoa(String.fromCharCode(...new Uint8Array(cipherBuffer))),
|
|
107
|
-
// iv: btoa(String.fromCharCode(...iv)),
|
|
108
|
-
// };
|
|
109
|
-
// }
|
|
110
|
-
// export async function decrypt(payload: EncryptedPayload, key: CryptoKey): Promise<string> {
|
|
111
|
-
// const cipherBytes = Uint8Array.from(atob(payload.ciphertext), (c) => c.charCodeAt(0));
|
|
112
|
-
// const iv = Uint8Array.from(atob(payload.iv), (c) => c.charCodeAt(0));
|
|
113
|
-
// const decrypted = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBytes);
|
|
114
|
-
// return new TextDecoder().decode(decrypted);
|
|
115
|
-
// }
|
|
116
142
|
//# sourceMappingURL=crypto.js.map
|
package/dist/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"AAEA,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AegisClient } from "./client";
|
|
2
|
-
export { generateKey, exportKey, importKey
|
|
2
|
+
export { generateKey, generateBidxKey, exportKey, importKey } from "./crypto";
|
|
3
3
|
export { SupabaseAdapter } from "./adapters/supabase";
|
|
4
4
|
export { MongoDBAdapter } from "./adapters/mongodb";
|
|
5
5
|
export type { AegisConfig, EncryptedPayload, AegisKeyPair, DatabaseAdapter } from "./types";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AegisClient } from "./client";
|
|
2
|
-
export { generateKey, exportKey, importKey
|
|
2
|
+
export { generateKey, generateBidxKey, exportKey, importKey } from "./crypto";
|
|
3
3
|
export { SupabaseAdapter } from "./adapters/supabase";
|
|
4
4
|
export { MongoDBAdapter } from "./adapters/mongodb";
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,17 @@ export interface DatabaseAdapter {
|
|
|
11
11
|
data: Record<string, unknown>[] | null;
|
|
12
12
|
error: unknown;
|
|
13
13
|
}>;
|
|
14
|
+
update(table: string, data: Record<string, unknown>): Promise<{
|
|
15
|
+
data: any[] | null;
|
|
16
|
+
error: any;
|
|
17
|
+
}>;
|
|
18
|
+
delete(table: string, query: {
|
|
19
|
+
column: string;
|
|
20
|
+
value: unknown;
|
|
21
|
+
}): Promise<{
|
|
22
|
+
data: any[] | null;
|
|
23
|
+
error: any;
|
|
24
|
+
}>;
|
|
14
25
|
}
|
|
15
26
|
export interface AegisConfig {
|
|
16
27
|
adapter: DatabaseAdapter;
|
|
@@ -19,6 +30,7 @@ export interface AegisConfig {
|
|
|
19
30
|
bidxFields?: Record<string, string[]>;
|
|
20
31
|
}
|
|
21
32
|
export interface EncryptedPayload {
|
|
33
|
+
version: string;
|
|
22
34
|
ciphertext: string;
|
|
23
35
|
iv: string;
|
|
24
36
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1H,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1H,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzJ,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAClG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;CAC/G;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB"}
|
package/dist/types.js
CHANGED
|
@@ -1,24 +1,2 @@
|
|
|
1
1
|
export {};
|
|
2
|
-
// /** Generic database adapter interface — implement this for any database. */
|
|
3
|
-
// export interface DatabaseAdapter {
|
|
4
|
-
// /** Insert a row into a table/collection. Returns the inserted data. */
|
|
5
|
-
// insert(table: string, data: Record<string, unknown>): Promise<{ data: Record<string, unknown>[] | null; error: unknown }>;
|
|
6
|
-
// /** Select rows from a table/collection with optional filters. */
|
|
7
|
-
// select(
|
|
8
|
-
// table: string,
|
|
9
|
-
// query?: { column?: string; value?: unknown; limit?: number }
|
|
10
|
-
// ): Promise<{ data: Record<string, unknown>[] | null; error: unknown }>;
|
|
11
|
-
// }
|
|
12
|
-
// export interface AegisConfig {
|
|
13
|
-
// adapter: DatabaseAdapter;
|
|
14
|
-
// encryptedFields: Record<string, string[]>;
|
|
15
|
-
// }
|
|
16
|
-
// export interface EncryptedPayload {
|
|
17
|
-
// ciphertext: string;
|
|
18
|
-
// iv: string;
|
|
19
|
-
// }
|
|
20
|
-
// export interface AegisKeyPair {
|
|
21
|
-
// key: CryptoKey;
|
|
22
|
-
// exported: string;
|
|
23
|
-
// }
|
|
24
2
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aegis-lock",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Database-agnostic client-side AES-256-GCM field-level encryption. Works with Supabase, MongoDB, or any database via pluggable adapters.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/hritesh-saha/aegis-lock.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/hritesh-saha/aegis-lock/issues"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/hritesh-saha/aegis-lock#readme",
|
|
5
13
|
"type": "module",
|
|
6
14
|
"main": "dist/index.js",
|
|
7
15
|
"module": "dist/index.js",
|
|
@@ -50,6 +58,9 @@
|
|
|
50
58
|
"peerDependenciesMeta": {
|
|
51
59
|
"@supabase/supabase-js": {
|
|
52
60
|
"optional": true
|
|
61
|
+
},
|
|
62
|
+
"mongodb": {
|
|
63
|
+
"optional": true
|
|
53
64
|
}
|
|
54
65
|
},
|
|
55
66
|
"devDependencies": {
|
|
@@ -57,6 +68,7 @@
|
|
|
57
68
|
"@types/jest": "^30.0.0",
|
|
58
69
|
"@types/node": "^25.3.5",
|
|
59
70
|
"jest": "^30.2.0",
|
|
71
|
+
"mongodb": "^7.1.1",
|
|
60
72
|
"ts-jest": "^29.4.6",
|
|
61
73
|
"typescript": "^5.9.3"
|
|
62
74
|
}
|