@orbitmem/sdk 0.1.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.
Files changed (176) hide show
  1. package/README.md +104 -0
  2. package/dist/agent/agent-adapter.d.ts +3 -0
  3. package/dist/agent/agent-adapter.d.ts.map +1 -0
  4. package/dist/agent/agent-adapter.js +3 -0
  5. package/dist/agent/agent-adapter.js.map +1 -0
  6. package/dist/agent/client.d.ts +5 -0
  7. package/dist/agent/client.d.ts.map +1 -0
  8. package/dist/agent/client.js +146 -0
  9. package/dist/agent/client.js.map +1 -0
  10. package/dist/agent/index.d.ts +2 -0
  11. package/dist/agent/index.d.ts.map +1 -0
  12. package/dist/agent/index.js +2 -0
  13. package/dist/agent/index.js.map +1 -0
  14. package/dist/client.d.ts +3 -0
  15. package/dist/client.d.ts.map +1 -0
  16. package/dist/client.js +118 -0
  17. package/dist/client.js.map +1 -0
  18. package/dist/contracts.d.ts +19 -0
  19. package/dist/contracts.d.ts.map +1 -0
  20. package/dist/contracts.js +28 -0
  21. package/dist/contracts.js.map +1 -0
  22. package/dist/data/index.d.ts +5 -0
  23. package/dist/data/index.d.ts.map +1 -0
  24. package/dist/data/index.js +5 -0
  25. package/dist/data/index.js.map +1 -0
  26. package/dist/data/orbitdb.d.ts +10 -0
  27. package/dist/data/orbitdb.d.ts.map +1 -0
  28. package/dist/data/orbitdb.js +39 -0
  29. package/dist/data/orbitdb.js.map +1 -0
  30. package/dist/data/pricing.d.ts +7 -0
  31. package/dist/data/pricing.d.ts.map +1 -0
  32. package/dist/data/pricing.js +55 -0
  33. package/dist/data/pricing.js.map +1 -0
  34. package/dist/data/serialization.d.ts +28 -0
  35. package/dist/data/serialization.d.ts.map +1 -0
  36. package/dist/data/serialization.js +76 -0
  37. package/dist/data/serialization.js.map +1 -0
  38. package/dist/data/vault.d.ts +21 -0
  39. package/dist/data/vault.d.ts.map +1 -0
  40. package/dist/data/vault.js +284 -0
  41. package/dist/data/vault.js.map +1 -0
  42. package/dist/discovery/discovery-layer.d.ts +3 -0
  43. package/dist/discovery/discovery-layer.d.ts.map +1 -0
  44. package/dist/discovery/discovery-layer.js +205 -0
  45. package/dist/discovery/discovery-layer.js.map +1 -0
  46. package/dist/discovery/index.d.ts +4 -0
  47. package/dist/discovery/index.d.ts.map +1 -0
  48. package/dist/discovery/index.js +4 -0
  49. package/dist/discovery/index.js.map +1 -0
  50. package/dist/discovery/mock-registry.d.ts +30 -0
  51. package/dist/discovery/mock-registry.d.ts.map +1 -0
  52. package/dist/discovery/mock-registry.js +71 -0
  53. package/dist/discovery/mock-registry.js.map +1 -0
  54. package/dist/discovery/on-chain-registry.d.ts +35 -0
  55. package/dist/discovery/on-chain-registry.d.ts.map +1 -0
  56. package/dist/discovery/on-chain-registry.js +199 -0
  57. package/dist/discovery/on-chain-registry.js.map +1 -0
  58. package/dist/encryption/aes.d.ts +15 -0
  59. package/dist/encryption/aes.d.ts.map +1 -0
  60. package/dist/encryption/aes.js +63 -0
  61. package/dist/encryption/aes.js.map +1 -0
  62. package/dist/encryption/encryption-layer.d.ts +8 -0
  63. package/dist/encryption/encryption-layer.d.ts.map +1 -0
  64. package/dist/encryption/encryption-layer.js +82 -0
  65. package/dist/encryption/encryption-layer.js.map +1 -0
  66. package/dist/encryption/index.d.ts +6 -0
  67. package/dist/encryption/index.d.ts.map +1 -0
  68. package/dist/encryption/index.js +4 -0
  69. package/dist/encryption/index.js.map +1 -0
  70. package/dist/encryption/lit.d.ts +23 -0
  71. package/dist/encryption/lit.d.ts.map +1 -0
  72. package/dist/encryption/lit.js +113 -0
  73. package/dist/encryption/lit.js.map +1 -0
  74. package/dist/encryption/vault-key.d.ts +37 -0
  75. package/dist/encryption/vault-key.d.ts.map +1 -0
  76. package/dist/encryption/vault-key.js +43 -0
  77. package/dist/encryption/vault-key.js.map +1 -0
  78. package/dist/identity/identity-layer.d.ts +3 -0
  79. package/dist/identity/identity-layer.d.ts.map +1 -0
  80. package/dist/identity/identity-layer.js +99 -0
  81. package/dist/identity/identity-layer.js.map +1 -0
  82. package/dist/identity/index.d.ts +4 -0
  83. package/dist/identity/index.d.ts.map +1 -0
  84. package/dist/identity/index.js +4 -0
  85. package/dist/identity/index.js.map +1 -0
  86. package/dist/identity/ows-adapter.d.ts +15 -0
  87. package/dist/identity/ows-adapter.d.ts.map +1 -0
  88. package/dist/identity/ows-adapter.js +67 -0
  89. package/dist/identity/ows-adapter.js.map +1 -0
  90. package/dist/identity/session.d.ts +10 -0
  91. package/dist/identity/session.d.ts.map +1 -0
  92. package/dist/identity/session.js +36 -0
  93. package/dist/identity/session.js.map +1 -0
  94. package/dist/index.d.ts +12 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +12 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/persistence/create-agent.d.ts +11 -0
  99. package/dist/persistence/create-agent.d.ts.map +1 -0
  100. package/dist/persistence/create-agent.js +47 -0
  101. package/dist/persistence/create-agent.js.map +1 -0
  102. package/dist/persistence/index.d.ts +3 -0
  103. package/dist/persistence/index.d.ts.map +1 -0
  104. package/dist/persistence/index.js +3 -0
  105. package/dist/persistence/index.js.map +1 -0
  106. package/dist/persistence/persistence-layer.d.ts +12 -0
  107. package/dist/persistence/persistence-layer.d.ts.map +1 -0
  108. package/dist/persistence/persistence-layer.js +194 -0
  109. package/dist/persistence/persistence-layer.js.map +1 -0
  110. package/dist/transport/index.d.ts +3 -0
  111. package/dist/transport/index.d.ts.map +1 -0
  112. package/dist/transport/index.js +3 -0
  113. package/dist/transport/index.js.map +1 -0
  114. package/dist/transport/relay-session.d.ts +41 -0
  115. package/dist/transport/relay-session.d.ts.map +1 -0
  116. package/dist/transport/relay-session.js +86 -0
  117. package/dist/transport/relay-session.js.map +1 -0
  118. package/dist/transport/transport-layer.d.ts +32 -0
  119. package/dist/transport/transport-layer.d.ts.map +1 -0
  120. package/dist/transport/transport-layer.js +110 -0
  121. package/dist/transport/transport-layer.js.map +1 -0
  122. package/dist/types.d.ts +1319 -0
  123. package/dist/types.d.ts.map +1 -0
  124. package/dist/types.js +7 -0
  125. package/dist/types.js.map +1 -0
  126. package/package.json +91 -0
  127. package/src/__tests__/client.test.ts +30 -0
  128. package/src/__tests__/orbitdb-availability.ts +8 -0
  129. package/src/agent/__tests__/agent-adapter.test.ts +50 -0
  130. package/src/agent/__tests__/client.test.ts +50 -0
  131. package/src/agent/agent-adapter.ts +2 -0
  132. package/src/agent/client.ts +158 -0
  133. package/src/agent/index.ts +1 -0
  134. package/src/client.ts +134 -0
  135. package/src/contracts.ts +44 -0
  136. package/src/data/__tests__/pricing.test.ts +73 -0
  137. package/src/data/__tests__/vault-encryption.test.ts +346 -0
  138. package/src/data/__tests__/vault.test.ts +75 -0
  139. package/src/data/index.ts +8 -0
  140. package/src/data/orbitdb.ts +47 -0
  141. package/src/data/pricing.ts +63 -0
  142. package/src/data/serialization.ts +108 -0
  143. package/src/data/vault.ts +382 -0
  144. package/src/discovery/__tests__/discovery.test.ts +49 -0
  145. package/src/discovery/__tests__/on-chain-registry.test.ts +176 -0
  146. package/src/discovery/discovery-layer.ts +244 -0
  147. package/src/discovery/index.ts +3 -0
  148. package/src/discovery/mock-registry.ts +96 -0
  149. package/src/discovery/on-chain-registry.ts +237 -0
  150. package/src/encryption/__tests__/aes.test.ts +64 -0
  151. package/src/encryption/__tests__/encryption-layer.test.ts +80 -0
  152. package/src/encryption/__tests__/lit.test.ts +97 -0
  153. package/src/encryption/aes.ts +109 -0
  154. package/src/encryption/encryption-layer.ts +100 -0
  155. package/src/encryption/index.ts +5 -0
  156. package/src/encryption/lit.ts +161 -0
  157. package/src/encryption/vault-key.ts +63 -0
  158. package/src/identity/__tests__/identity.test.ts +31 -0
  159. package/src/identity/__tests__/ows-adapter.test.ts +47 -0
  160. package/src/identity/identity-layer.ts +123 -0
  161. package/src/identity/index.ts +3 -0
  162. package/src/identity/ows-adapter.ts +80 -0
  163. package/src/identity/session.ts +57 -0
  164. package/src/index.ts +12 -0
  165. package/src/persistence/__tests__/create-agent.test.ts +9 -0
  166. package/src/persistence/__tests__/persistence.test.ts +242 -0
  167. package/src/persistence/create-agent.ts +55 -0
  168. package/src/persistence/index.ts +2 -0
  169. package/src/persistence/persistence-layer.ts +236 -0
  170. package/src/transport/__tests__/solana-transport.test.ts +112 -0
  171. package/src/transport/__tests__/transport.test.ts +84 -0
  172. package/src/transport/index.ts +2 -0
  173. package/src/transport/relay-session.ts +118 -0
  174. package/src/transport/transport-layer.ts +171 -0
  175. package/src/types/orbitdb.d.ts +9 -0
  176. package/src/types.ts +1496 -0
@@ -0,0 +1,73 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+
3
+ import { hasOrbitDbNativeSupport } from "../../__tests__/orbitdb-availability.js";
4
+
5
+ const orbitdbAvailable = hasOrbitDbNativeSupport();
6
+
7
+ describe.skipIf(!orbitdbAvailable)("Vault Pricing", () => {
8
+ let vault: any;
9
+ let pricing: any;
10
+ let cleanup: () => Promise<void>;
11
+
12
+ beforeAll(async () => {
13
+ const { createOrbitDBInstance, createVault } = await import("../index.js");
14
+ const { createVaultPricing } = await import("../pricing.js");
15
+ const { orbitdb, cleanup: c } = await createOrbitDBInstance({
16
+ directory: "./.test-orbitdb-pricing",
17
+ });
18
+ cleanup = c;
19
+ vault = await createVault(orbitdb, {});
20
+ pricing = createVaultPricing(vault.metaDb);
21
+ });
22
+
23
+ afterAll(async () => {
24
+ await vault?.close?.();
25
+ await cleanup?.();
26
+ });
27
+
28
+ test("getPrice returns null when no price set and no default", async () => {
29
+ const price = await pricing.getPrice("some/path");
30
+ expect(price).toBeNull();
31
+ });
32
+
33
+ test("setPrice and getPrice round-trip", async () => {
34
+ await pricing.setPrice("agent/memory", { amount: "0.005", currency: "USDC" });
35
+ const price = await pricing.getPrice("agent/memory");
36
+ expect(price).toEqual({ amount: "0.005", currency: "USDC" });
37
+ });
38
+
39
+ test("getPrice falls back to _default", async () => {
40
+ await pricing.setPrice("_default", { amount: "0.001", currency: "USDC" });
41
+ const price = await pricing.getPrice("unpriced/path");
42
+ expect(price).toEqual({ amount: "0.001", currency: "USDC" });
43
+ });
44
+
45
+ test("per-path price overrides _default", async () => {
46
+ const price = await pricing.getPrice("agent/memory");
47
+ expect(price).toEqual({ amount: "0.005", currency: "USDC" });
48
+ });
49
+
50
+ test("removePrice reverts to _default", async () => {
51
+ await pricing.removePrice("agent/memory");
52
+ const price = await pricing.getPrice("agent/memory");
53
+ expect(price).toEqual({ amount: "0.001", currency: "USDC" });
54
+ });
55
+
56
+ test("removePrice with no _default returns null", async () => {
57
+ await pricing.removePrice("_default");
58
+ const price = await pricing.getPrice("agent/memory");
59
+ expect(price).toBeNull();
60
+ });
61
+
62
+ test("listPrices returns all priced paths", async () => {
63
+ await pricing.setPrice("a", { amount: "0.01", currency: "USDC" });
64
+ await pricing.setPrice("b", { amount: "0.02", currency: "USDC" });
65
+ const list = await pricing.listPrices();
66
+ expect(list).toEqual(
67
+ expect.arrayContaining([
68
+ { path: "a", amount: "0.01", currency: "USDC" },
69
+ { path: "b", amount: "0.02", currency: "USDC" },
70
+ ]),
71
+ );
72
+ });
73
+ });
@@ -0,0 +1,346 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { AESEngine } from "../../encryption/aes.js";
4
+ import { isSerializedEncrypted } from "../serialization.js";
5
+ import { createVault } from "../vault.js";
6
+
7
+ /** In-memory mock OrbitDB for unit tests (no network/native deps) */
8
+ function createMockOrbitDB() {
9
+ const stores = new Map<string, Map<string, any>>();
10
+ return {
11
+ async open(name: string) {
12
+ if (!stores.has(name)) stores.set(name, new Map());
13
+ const store = stores.get(name)!;
14
+ return {
15
+ async put(key: string, value: any) {
16
+ store.set(key, value);
17
+ return `hash-${key}`;
18
+ },
19
+ async get(key: string) {
20
+ return store.get(key);
21
+ },
22
+ async del(key: string) {
23
+ store.delete(key);
24
+ },
25
+ async all() {
26
+ const obj: Record<string, any> = {};
27
+ for (const [k, v] of store) obj[k] = v;
28
+ return obj;
29
+ },
30
+ async close() {},
31
+ events: { on() {}, off() {} },
32
+ };
33
+ },
34
+ };
35
+ }
36
+
37
+ async function deriveTestKey(aes: AESEngine) {
38
+ const raw = crypto.getRandomValues(new Uint8Array(32));
39
+ return aes.deriveKey({ type: "raw", key: raw });
40
+ }
41
+
42
+ describe("Vault Encryption", () => {
43
+ test("private+aes: put encrypts, get auto-decrypts", async () => {
44
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
45
+ const key = await deriveTestKey(aes);
46
+ const vault = await createVault(createMockOrbitDB(), { aesEngine: aes });
47
+ vault.setDefaultKey(key);
48
+
49
+ const entry = await vault.put("secrets/password", "hunter2", { visibility: "private" });
50
+ expect(entry.value).toBe("hunter2");
51
+ expect(entry.encrypted).toBe(true);
52
+ expect(entry.encryptionEngine).toBe("aes");
53
+
54
+ // Raw OrbitDB value should be an encrypted blob, not plaintext
55
+ const raw = await vault.db.get("secrets/password");
56
+ expect(raw).not.toBe("hunter2");
57
+ expect(isSerializedEncrypted(raw)).toBe(true);
58
+
59
+ // get() should auto-decrypt
60
+ const retrieved = await vault.get("secrets/password");
61
+ expect(retrieved).not.toBeNull();
62
+ expect(retrieved!.value).toBe("hunter2");
63
+ expect(retrieved!.encrypted).toBe(true);
64
+ });
65
+
66
+ test("private+aes: works with object values", async () => {
67
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
68
+ const key = await deriveTestKey(aes);
69
+ const vault = await createVault(createMockOrbitDB(), { aesEngine: aes });
70
+ vault.setDefaultKey(key);
71
+
72
+ const data = { vegan: true, allergies: ["peanuts"] };
73
+ await vault.put("travel/dietary", data, { visibility: "private" });
74
+
75
+ const retrieved = await vault.get("travel/dietary");
76
+ expect(retrieved).not.toBeNull();
77
+ expect(retrieved!.value).toEqual(data);
78
+ });
79
+
80
+ test("public: stores and retrieves plaintext", async () => {
81
+ const vault = await createVault(createMockOrbitDB(), {});
82
+
83
+ await vault.put("profile/name", "Alice", { visibility: "public" });
84
+
85
+ const raw = await vault.db.get("profile/name");
86
+ expect(raw).toBe("Alice");
87
+
88
+ const retrieved = await vault.get("profile/name");
89
+ expect(retrieved!.value).toBe("Alice");
90
+ expect(retrieved!.encrypted).toBe(false);
91
+ });
92
+
93
+ test("private without aesEngine: throws", async () => {
94
+ const vault = await createVault(createMockOrbitDB(), {});
95
+
96
+ await expect(vault.put("secret", "data", { visibility: "private" })).rejects.toThrow(
97
+ /AES engine not configured/,
98
+ );
99
+ });
100
+
101
+ test("private without defaultKey: throws", async () => {
102
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
103
+ const vault = await createVault(createMockOrbitDB(), { aesEngine: aes });
104
+ // setDefaultKey not called
105
+
106
+ await expect(vault.put("secret", "data", { visibility: "private" })).rejects.toThrow(
107
+ /No default key/,
108
+ );
109
+ });
110
+
111
+ test("shared+aes: encrypts, get returns encrypted blob", async () => {
112
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
113
+ const defaultKey = await deriveTestKey(aes);
114
+ const sharedRaw = crypto.getRandomValues(new Uint8Array(32));
115
+ const vault = await createVault(createMockOrbitDB(), { aesEngine: aes });
116
+ vault.setDefaultKey(defaultKey);
117
+
118
+ await vault.put(
119
+ "shared/data",
120
+ { score: 42 },
121
+ {
122
+ visibility: "shared",
123
+ engine: "aes",
124
+ sharedKeySource: { type: "raw", key: sharedRaw },
125
+ },
126
+ );
127
+
128
+ // get() cannot auto-decrypt shared data (no shared key internally)
129
+ const retrieved = await vault.get("shared/data");
130
+ expect(retrieved).not.toBeNull();
131
+ expect(isSerializedEncrypted(retrieved!.value)).toBe(true);
132
+ });
133
+
134
+ test("insert encrypts all leaves for private visibility", async () => {
135
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
136
+ const key = await deriveTestKey(aes);
137
+ const vault = await createVault(createMockOrbitDB(), { aesEngine: aes });
138
+ vault.setDefaultKey(key);
139
+
140
+ await vault.insert({ prefs: { diet: "vegan", budget: 5000 } }, { visibility: "private" });
141
+
142
+ // Raw storage should be encrypted
143
+ const rawDiet = await vault.db.get("prefs/diet");
144
+ expect(isSerializedEncrypted(rawDiet)).toBe(true);
145
+
146
+ const rawBudget = await vault.db.get("prefs/budget");
147
+ expect(isSerializedEncrypted(rawBudget)).toBe(true);
148
+
149
+ // get() should auto-decrypt
150
+ const diet = await vault.get("prefs/diet");
151
+ expect(diet!.value).toBe("vegan");
152
+
153
+ const budget = await vault.get("prefs/budget");
154
+ expect(budget!.value).toBe(5000);
155
+ });
156
+
157
+ test("insert with public visibility stores plaintext", async () => {
158
+ const vault = await createVault(createMockOrbitDB(), {});
159
+
160
+ await vault.insert({ profile: { name: "Bob" } }, { visibility: "public" });
161
+
162
+ const raw = await vault.db.get("profile/name");
163
+ expect(raw).toBe("Bob");
164
+ });
165
+
166
+ test("backward compat: no encryption config + public works", async () => {
167
+ const vault = await createVault(createMockOrbitDB(), {});
168
+
169
+ await vault.put("hello", "world", { visibility: "public" });
170
+ const entry = await vault.get("hello");
171
+ expect(entry!.value).toBe("world");
172
+ });
173
+
174
+ test("del removes encrypted data", async () => {
175
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
176
+ const key = await deriveTestKey(aes);
177
+ const vault = await createVault(createMockOrbitDB(), { aesEngine: aes });
178
+ vault.setDefaultKey(key);
179
+
180
+ await vault.put("temp", 42, { visibility: "private" });
181
+ await vault.del("temp");
182
+ const result = await vault.get("temp");
183
+ expect(result).toBeNull();
184
+ });
185
+
186
+ test("setAuthSig enables Lit auto-decryption in get()", async () => {
187
+ const mockEncryptionLayer = {
188
+ encrypt: async (_data: any, opts: any) => ({
189
+ engine: "lit" as const,
190
+ ciphertext: new TextEncoder().encode("encrypted"),
191
+ dataToEncryptHash: "hash123",
192
+ accessControlConditions: opts.accessConditions ?? [],
193
+ chain: "base" as const,
194
+ }),
195
+ decrypt: async (_encrypted: any, opts: any) => {
196
+ if (!opts?.authSig) throw new Error("No authSig");
197
+ return new TextEncoder().encode(JSON.stringify("decrypted-value"));
198
+ },
199
+ };
200
+
201
+ const vault = await createVault(createMockOrbitDB(), {
202
+ encryptionLayer: mockEncryptionLayer as any,
203
+ });
204
+
205
+ const authSig = {
206
+ sig: "0xsig",
207
+ derivedVia: "web3.eth.personal.sign",
208
+ signedMessage: "test",
209
+ address: "0xABCD",
210
+ };
211
+ vault.setAuthSig(authSig);
212
+
213
+ // Put with Lit encryption
214
+ await vault.put("shared/secret", "my-data", {
215
+ visibility: "shared",
216
+ engine: "lit",
217
+ accessConditions: [],
218
+ });
219
+
220
+ // get() should auto-decrypt using stored authSig
221
+ const entry = await vault.get("shared/secret");
222
+ expect(entry).not.toBeNull();
223
+ expect(entry!.value).toBe("decrypted-value");
224
+ });
225
+
226
+ test("updateAccess: decrypt → re-encrypt → store with new conditions", async () => {
227
+ let lastEncryptConditions: any[] = [];
228
+ const mockEncryptionLayer = {
229
+ encrypt: async (_data: any, opts: any) => {
230
+ lastEncryptConditions = opts.accessConditions ?? [];
231
+ return {
232
+ engine: "lit" as const,
233
+ ciphertext: new TextEncoder().encode("encrypted"),
234
+ dataToEncryptHash: "hash-new",
235
+ accessControlConditions: opts.accessConditions ?? [],
236
+ chain: "base" as const,
237
+ };
238
+ },
239
+ decrypt: async (_encrypted: any, opts: any) => {
240
+ if (!opts?.authSig) throw new Error("No authSig");
241
+ return new TextEncoder().encode(JSON.stringify("secret-data"));
242
+ },
243
+ };
244
+
245
+ const vault = await createVault(createMockOrbitDB(), {
246
+ encryptionLayer: mockEncryptionLayer as any,
247
+ });
248
+
249
+ const authSig = {
250
+ sig: "0xsig",
251
+ derivedVia: "web3.eth.personal.sign",
252
+ signedMessage: "test",
253
+ address: "0xABCD",
254
+ };
255
+ vault.setAuthSig(authSig);
256
+
257
+ // Store with initial conditions
258
+ await vault.put("gated/data", "secret-data", {
259
+ visibility: "shared",
260
+ engine: "lit",
261
+ accessConditions: [
262
+ {
263
+ conditionType: "evmBasic",
264
+ contractAddress: "",
265
+ standardContractType: "",
266
+ chain: "base-sepolia",
267
+ method: "",
268
+ parameters: [":userAddress"],
269
+ returnValueTest: { comparator: "=", value: "0xOLD" },
270
+ },
271
+ ],
272
+ });
273
+
274
+ // Update access to a new address
275
+ const newConditions = [
276
+ {
277
+ conditionType: "evmBasic" as const,
278
+ contractAddress: "" as any,
279
+ standardContractType: "" as const,
280
+ chain: "base-sepolia" as const,
281
+ method: "",
282
+ parameters: [":userAddress"],
283
+ returnValueTest: { comparator: "=" as const, value: "0xNEW" },
284
+ },
285
+ ];
286
+
287
+ const entry = await vault.updateAccess("gated/data", newConditions);
288
+ expect(entry.value).toBe("secret-data");
289
+ expect(entry.encrypted).toBe(true);
290
+ expect(entry.encryptionEngine).toBe("lit");
291
+
292
+ // Verify the re-encrypt call received the new conditions
293
+ expect(lastEncryptConditions).toEqual(newConditions);
294
+ });
295
+
296
+ test("updateAccess: throws for non-Lit entry", async () => {
297
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
298
+ const key = await deriveTestKey(aes);
299
+ const mockEncLayer = {
300
+ encrypt: async () => ({}),
301
+ decrypt: async () => new Uint8Array(),
302
+ };
303
+ const vault = await createVault(createMockOrbitDB(), {
304
+ aesEngine: aes,
305
+ encryptionLayer: mockEncLayer as any,
306
+ });
307
+ vault.setDefaultKey(key);
308
+ vault.setAuthSig({ sig: "0x", derivedVia: "test", signedMessage: "t", address: "0x1" });
309
+
310
+ await vault.put("private/data", "hello", { visibility: "private" });
311
+ await expect(vault.updateAccess("private/data", [])).rejects.toThrow(/not Lit-encrypted/);
312
+ });
313
+
314
+ test("updateAccess: throws for missing path", async () => {
315
+ const vault = await createVault(createMockOrbitDB(), {
316
+ encryptionLayer: { encrypt: async () => ({}), decrypt: async () => new Uint8Array() } as any,
317
+ });
318
+ vault.setAuthSig({ sig: "0x", derivedVia: "test", signedMessage: "t", address: "0x1" });
319
+
320
+ await expect(vault.updateAccess("nonexistent", [])).rejects.toThrow(/Not found/);
321
+ });
322
+
323
+ test("updateAccess: throws without authSig", async () => {
324
+ const vault = await createVault(createMockOrbitDB(), {
325
+ encryptionLayer: { encrypt: async () => ({}), decrypt: async () => new Uint8Array() } as any,
326
+ });
327
+
328
+ await expect(vault.updateAccess("some/path", [])).rejects.toThrow(/authSig/);
329
+ });
330
+
331
+ test("query auto-decrypts private entries", async () => {
332
+ const aes = new AESEngine({ kdf: "hkdf-sha256" });
333
+ const key = await deriveTestKey(aes);
334
+ const vault = await createVault(createMockOrbitDB(), { aesEngine: aes });
335
+ vault.setDefaultKey(key);
336
+
337
+ await vault.put("search/a", "alpha", { visibility: "private" });
338
+ await vault.put("search/b", "beta", { visibility: "private" });
339
+
340
+ const results = await vault.query({ prefix: "search/" });
341
+ expect(results).toHaveLength(2);
342
+ const values = results.map((r: any) => r.value);
343
+ expect(values).toContain("alpha");
344
+ expect(values).toContain("beta");
345
+ });
346
+ });
@@ -0,0 +1,75 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+
3
+ import { hasOrbitDbNativeSupport } from "../../__tests__/orbitdb-availability.js";
4
+
5
+ // Skip if native OrbitDB dependencies are unavailable (e.g. CI without the compiled binary)
6
+ const orbitdbAvailable = hasOrbitDbNativeSupport();
7
+
8
+ describe.skipIf(!orbitdbAvailable)("DataLayer — Vault", () => {
9
+ let vault: any;
10
+ let cleanup: () => Promise<void>;
11
+
12
+ beforeAll(async () => {
13
+ const { createOrbitDBInstance, createVault } = await import("../index.js");
14
+ const { orbitdb, cleanup: c } = await createOrbitDBInstance({ directory: "./.test-orbitdb" });
15
+ cleanup = c;
16
+ vault = await createVault(orbitdb, {});
17
+ });
18
+
19
+ afterAll(async () => {
20
+ await vault?.close?.();
21
+ await cleanup?.();
22
+ });
23
+
24
+ test("put and get a public value", async () => {
25
+ const entry = await vault.put("test/greeting", "hello", { visibility: "public" });
26
+ expect(entry.value).toBe("hello");
27
+ expect(entry.visibility).toBe("public");
28
+ expect(entry.encrypted).toBe(false);
29
+
30
+ const retrieved = await vault.get("test/greeting");
31
+ expect(retrieved).not.toBeNull();
32
+ expect(retrieved!.value).toBe("hello");
33
+ });
34
+
35
+ test("put and get nested path", async () => {
36
+ await vault.put("travel/dietary", "vegan", { visibility: "public" });
37
+ await vault.put("travel/budget", 3000, { visibility: "public" });
38
+
39
+ const travel = await vault.get("travel");
40
+ expect(travel).not.toBeNull();
41
+ expect(travel!.value).toMatchObject({ dietary: "vegan", budget: 3000 });
42
+ });
43
+
44
+ test("insert merges nested object", async () => {
45
+ await vault.insert(
46
+ {
47
+ profile: { name: "Alice", interests: ["travel"] },
48
+ },
49
+ { visibility: "public" },
50
+ );
51
+
52
+ const profile = await vault.get("profile");
53
+ expect(profile).not.toBeNull();
54
+ expect(profile!.value).toMatchObject({ name: "Alice", interests: ["travel"] });
55
+ });
56
+
57
+ test("del removes a path", async () => {
58
+ await vault.put("temp/data", 42, { visibility: "public" });
59
+ await vault.del("temp/data");
60
+ const result = await vault.get("temp/data");
61
+ expect(result).toBeNull();
62
+ });
63
+
64
+ test("keys returns all leaf paths", async () => {
65
+ const allKeys = await vault.keys();
66
+ expect(allKeys).toContain("test/greeting");
67
+ expect(allKeys).toContain("travel/dietary");
68
+ });
69
+
70
+ test("all returns full nested object", async () => {
71
+ const everything = await vault.all();
72
+ expect(everything).toHaveProperty("test");
73
+ expect(everything).toHaveProperty("travel");
74
+ });
75
+ });
@@ -0,0 +1,8 @@
1
+ export { createOrbitDBInstance } from "./orbitdb.js";
2
+ export {
3
+ deserializeEncrypted,
4
+ isSerializedEncrypted,
5
+ serializeEncrypted,
6
+ } from "./serialization.js";
7
+ export { createVault } from "./vault.js";
8
+ export { createVaultPricing } from "./pricing.js";
@@ -0,0 +1,47 @@
1
+ // oxlint-disable-next-line typescript/triple-slash-reference
2
+ /// <reference path="../types/orbitdb.d.ts" />
3
+
4
+ import { gossipsub } from "@chainsafe/libp2p-gossipsub";
5
+ import { noise } from "@chainsafe/libp2p-noise";
6
+ import { yamux } from "@chainsafe/libp2p-yamux";
7
+ import { identify } from "@libp2p/identify";
8
+ import { tcp } from "@libp2p/tcp";
9
+ import { createOrbitDB, useDatabaseType } from "@orbitdb/core";
10
+ import { Nested } from "@orbitdb/nested-db";
11
+ import { LevelBlockstore } from "blockstore-level";
12
+ import { createHelia } from "helia";
13
+ import { createLibp2p } from "libp2p";
14
+
15
+ // Register the Nested database type (must happen before createOrbitDB)
16
+ useDatabaseType(Nested);
17
+
18
+ export async function createOrbitDBInstance(opts: {
19
+ directory?: string;
20
+ listenAddrs?: string[];
21
+ }): Promise<{ orbitdb: any; ipfs: any; libp2p: any; cleanup: () => Promise<void> }> {
22
+ const blockstore = new LevelBlockstore(opts.directory ?? "./orbitdb/blocks");
23
+
24
+ const libp2p = await createLibp2p({
25
+ addresses: { listen: opts.listenAddrs ?? ["/ip4/0.0.0.0/tcp/0"] },
26
+ transports: [tcp()],
27
+ connectionEncrypters: [noise()],
28
+ streamMuxers: [yamux()],
29
+ services: {
30
+ identify: identify(),
31
+ pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }),
32
+ },
33
+ });
34
+
35
+ const ipfs = await createHelia({ libp2p, blockstore });
36
+ const orbitdb = await createOrbitDB({ ipfs });
37
+
38
+ return {
39
+ orbitdb,
40
+ ipfs,
41
+ libp2p,
42
+ cleanup: async () => {
43
+ await orbitdb.stop();
44
+ await ipfs.stop();
45
+ },
46
+ };
47
+ }
@@ -0,0 +1,63 @@
1
+ import type { IVaultPricing, VaultPricing } from "../types.js";
2
+
3
+ const PRICING_PREFIX = "pricing/";
4
+ const DEFAULT_KEY = "pricing/_default";
5
+
6
+ /**
7
+ * Create pricing CRUD methods backed by a vault's `-meta` OrbitDB store.
8
+ * Pricing keys live under the `pricing/` prefix in metadata.
9
+ */
10
+ export function createVaultPricing(metaDb: any): IVaultPricing {
11
+ return {
12
+ async setPrice(path: string, pricing: VaultPricing): Promise<void> {
13
+ const key = path === "_default" ? DEFAULT_KEY : `${PRICING_PREFIX}${path}`;
14
+ await metaDb.put(key, { amount: pricing.amount, currency: pricing.currency });
15
+ },
16
+
17
+ async getPrice(path: string): Promise<VaultPricing | null> {
18
+ // Try per-path price first
19
+ const key = `${PRICING_PREFIX}${path}`;
20
+ const perPath = await metaDb.get(key);
21
+ if (perPath?.amount != null) {
22
+ return { amount: perPath.amount, currency: perPath.currency };
23
+ }
24
+
25
+ // Fall back to _default
26
+ const fallback = await metaDb.get(DEFAULT_KEY);
27
+ if (fallback?.amount != null) {
28
+ return { amount: fallback.amount, currency: fallback.currency };
29
+ }
30
+
31
+ return null;
32
+ },
33
+
34
+ async removePrice(path: string): Promise<void> {
35
+ const key = path === "_default" ? DEFAULT_KEY : `${PRICING_PREFIX}${path}`;
36
+ await metaDb.del(key);
37
+ },
38
+
39
+ async listPrices(): Promise<Array<{ path: string } & VaultPricing>> {
40
+ const all = await metaDb.all();
41
+ const results: Array<{ path: string } & VaultPricing> = [];
42
+
43
+ // Navigate into the "pricing" subtree of the nested OrbitDB
44
+ const pricingTree = all?.pricing;
45
+ if (!pricingTree || typeof pricingTree !== "object") return results;
46
+
47
+ // Walk the pricing subtree to find all pricing entries (may be nested)
48
+ const walk = (obj: any, prefix: string) => {
49
+ for (const [k, v] of Object.entries(obj)) {
50
+ const path = prefix ? `${prefix}/${k}` : k;
51
+ if (v && typeof v === "object" && "amount" in (v as any) && "currency" in (v as any)) {
52
+ results.push({ path, ...(v as VaultPricing) });
53
+ } else if (v && typeof v === "object") {
54
+ walk(v, path);
55
+ }
56
+ }
57
+ };
58
+
59
+ walk(pricingTree, "");
60
+ return results;
61
+ },
62
+ };
63
+ }