@peers-app/peers-sdk 0.1.4

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 (234) hide show
  1. package/README.md +1 -0
  2. package/dist/context/data-context.d.ts +31 -0
  3. package/dist/context/data-context.js +56 -0
  4. package/dist/context/index.d.ts +3 -0
  5. package/dist/context/index.js +19 -0
  6. package/dist/context/user-context-singleton.d.ts +11 -0
  7. package/dist/context/user-context-singleton.js +121 -0
  8. package/dist/context/user-context.d.ts +55 -0
  9. package/dist/context/user-context.js +205 -0
  10. package/dist/data/assistants.d.ts +68 -0
  11. package/dist/data/assistants.js +64 -0
  12. package/dist/data/change-tracking.d.ts +219 -0
  13. package/dist/data/change-tracking.js +119 -0
  14. package/dist/data/channels.d.ts +29 -0
  15. package/dist/data/channels.js +25 -0
  16. package/dist/data/data-locks.d.ts +37 -0
  17. package/dist/data/data-locks.js +180 -0
  18. package/dist/data/data-locks.test.d.ts +1 -0
  19. package/dist/data/data-locks.test.js +456 -0
  20. package/dist/data/device-sync-info.d.ts +19 -0
  21. package/dist/data/device-sync-info.js +24 -0
  22. package/dist/data/devices.d.ts +51 -0
  23. package/dist/data/devices.js +36 -0
  24. package/dist/data/embeddings.d.ts +47 -0
  25. package/dist/data/embeddings.js +36 -0
  26. package/dist/data/files/file-read-stream.d.ts +27 -0
  27. package/dist/data/files/file-read-stream.js +195 -0
  28. package/dist/data/files/file-write-stream.d.ts +20 -0
  29. package/dist/data/files/file-write-stream.js +113 -0
  30. package/dist/data/files/file.types.d.ts +47 -0
  31. package/dist/data/files/file.types.js +55 -0
  32. package/dist/data/files/files.d.ts +28 -0
  33. package/dist/data/files/files.js +127 -0
  34. package/dist/data/files/files.test.d.ts +1 -0
  35. package/dist/data/files/files.test.js +728 -0
  36. package/dist/data/files/index.d.ts +4 -0
  37. package/dist/data/files/index.js +23 -0
  38. package/dist/data/group-member-roles.d.ts +9 -0
  39. package/dist/data/group-member-roles.js +25 -0
  40. package/dist/data/group-members.d.ts +39 -0
  41. package/dist/data/group-members.js +68 -0
  42. package/dist/data/group-members.test.d.ts +1 -0
  43. package/dist/data/group-members.test.js +287 -0
  44. package/dist/data/group-permissions.d.ts +8 -0
  45. package/dist/data/group-permissions.js +73 -0
  46. package/dist/data/group-share.d.ts +50 -0
  47. package/dist/data/group-share.js +196 -0
  48. package/dist/data/groups.d.ts +50 -0
  49. package/dist/data/groups.js +73 -0
  50. package/dist/data/groups.test.d.ts +1 -0
  51. package/dist/data/groups.test.js +153 -0
  52. package/dist/data/index.d.ts +31 -0
  53. package/dist/data/index.js +47 -0
  54. package/dist/data/knowledge/knowledge-frames.d.ts +34 -0
  55. package/dist/data/knowledge/knowledge-frames.js +34 -0
  56. package/dist/data/knowledge/knowledge-links.d.ts +30 -0
  57. package/dist/data/knowledge/knowledge-links.js +25 -0
  58. package/dist/data/knowledge/knowledge-values.d.ts +35 -0
  59. package/dist/data/knowledge/knowledge-values.js +35 -0
  60. package/dist/data/knowledge/peer-types.d.ts +112 -0
  61. package/dist/data/knowledge/peer-types.js +27 -0
  62. package/dist/data/knowledge/predicates.d.ts +34 -0
  63. package/dist/data/knowledge/predicates.js +27 -0
  64. package/dist/data/messages.d.ts +57 -0
  65. package/dist/data/messages.js +97 -0
  66. package/dist/data/orm/client-proxy.data-source.d.ts +27 -0
  67. package/dist/data/orm/client-proxy.data-source.js +65 -0
  68. package/dist/data/orm/cursor.d.ts +25 -0
  69. package/dist/data/orm/cursor.js +47 -0
  70. package/dist/data/orm/cursor.test.d.ts +1 -0
  71. package/dist/data/orm/cursor.test.js +315 -0
  72. package/dist/data/orm/data-query.d.ts +96 -0
  73. package/dist/data/orm/data-query.js +208 -0
  74. package/dist/data/orm/data-query.mongo.d.ts +17 -0
  75. package/dist/data/orm/data-query.mongo.js +267 -0
  76. package/dist/data/orm/data-query.mongo.test.d.ts +1 -0
  77. package/dist/data/orm/data-query.mongo.test.js +398 -0
  78. package/dist/data/orm/data-query.sqlite.d.ts +14 -0
  79. package/dist/data/orm/data-query.sqlite.js +297 -0
  80. package/dist/data/orm/data-query.sqlite.test.d.ts +1 -0
  81. package/dist/data/orm/data-query.sqlite.test.js +377 -0
  82. package/dist/data/orm/data-query.test.d.ts +1 -0
  83. package/dist/data/orm/data-query.test.js +553 -0
  84. package/dist/data/orm/decorators.d.ts +6 -0
  85. package/dist/data/orm/decorators.js +21 -0
  86. package/dist/data/orm/dependency-injection.test.d.ts +1 -0
  87. package/dist/data/orm/dependency-injection.test.js +171 -0
  88. package/dist/data/orm/doc.d.ts +26 -0
  89. package/dist/data/orm/doc.js +124 -0
  90. package/dist/data/orm/event-registry.d.ts +24 -0
  91. package/dist/data/orm/event-registry.js +40 -0
  92. package/dist/data/orm/event-registry.test.d.ts +1 -0
  93. package/dist/data/orm/event-registry.test.js +44 -0
  94. package/dist/data/orm/factory.d.ts +8 -0
  95. package/dist/data/orm/factory.js +147 -0
  96. package/dist/data/orm/index.d.ts +16 -0
  97. package/dist/data/orm/index.js +32 -0
  98. package/dist/data/orm/multi-cursors.d.ts +11 -0
  99. package/dist/data/orm/multi-cursors.js +146 -0
  100. package/dist/data/orm/multi-cursors.test.d.ts +1 -0
  101. package/dist/data/orm/multi-cursors.test.js +455 -0
  102. package/dist/data/orm/sql-db.d.ts +6 -0
  103. package/dist/data/orm/sql-db.js +2 -0
  104. package/dist/data/orm/sql.data-source.d.ts +38 -0
  105. package/dist/data/orm/sql.data-source.js +379 -0
  106. package/dist/data/orm/sql.data-source.test.d.ts +1 -0
  107. package/dist/data/orm/sql.data-source.test.js +406 -0
  108. package/dist/data/orm/subscribable.data-source.d.ts +25 -0
  109. package/dist/data/orm/subscribable.data-source.js +72 -0
  110. package/dist/data/orm/table-container-events.test.d.ts +1 -0
  111. package/dist/data/orm/table-container-events.test.js +93 -0
  112. package/dist/data/orm/table-container.d.ts +39 -0
  113. package/dist/data/orm/table-container.js +96 -0
  114. package/dist/data/orm/table-definitions.system.d.ts +9 -0
  115. package/dist/data/orm/table-definitions.system.js +29 -0
  116. package/dist/data/orm/table-definitions.type.d.ts +19 -0
  117. package/dist/data/orm/table-definitions.type.js +2 -0
  118. package/dist/data/orm/table-dependencies.d.ts +32 -0
  119. package/dist/data/orm/table-dependencies.js +2 -0
  120. package/dist/data/orm/table.d.ts +42 -0
  121. package/dist/data/orm/table.event-source.test.d.ts +1 -0
  122. package/dist/data/orm/table.event-source.test.js +341 -0
  123. package/dist/data/orm/table.js +244 -0
  124. package/dist/data/orm/types.d.ts +20 -0
  125. package/dist/data/orm/types.js +115 -0
  126. package/dist/data/orm/types.test.d.ts +1 -0
  127. package/dist/data/orm/types.test.js +71 -0
  128. package/dist/data/package-permissions.d.ts +7 -0
  129. package/dist/data/package-permissions.js +18 -0
  130. package/dist/data/packages.d.ts +92 -0
  131. package/dist/data/packages.js +90 -0
  132. package/dist/data/peer-events/peer-event-handlers.d.ts +21 -0
  133. package/dist/data/peer-events/peer-event-handlers.js +28 -0
  134. package/dist/data/peer-events/peer-event-types.d.ts +119 -0
  135. package/dist/data/peer-events/peer-event-types.js +29 -0
  136. package/dist/data/peer-events/peer-events.d.ts +41 -0
  137. package/dist/data/peer-events/peer-events.js +102 -0
  138. package/dist/data/persistent-vars.d.ts +87 -0
  139. package/dist/data/persistent-vars.js +230 -0
  140. package/dist/data/tool-tests.d.ts +37 -0
  141. package/dist/data/tool-tests.js +27 -0
  142. package/dist/data/tools.d.ts +358 -0
  143. package/dist/data/tools.js +48 -0
  144. package/dist/data/user-permissions.d.ts +15 -0
  145. package/dist/data/user-permissions.js +39 -0
  146. package/dist/data/user-permissions.test.d.ts +1 -0
  147. package/dist/data/user-permissions.test.js +252 -0
  148. package/dist/data/users.d.ts +38 -0
  149. package/dist/data/users.js +73 -0
  150. package/dist/data/workflow-logs.d.ts +106 -0
  151. package/dist/data/workflow-logs.js +67 -0
  152. package/dist/data/workflow-runs.d.ts +103 -0
  153. package/dist/data/workflow-runs.js +313 -0
  154. package/dist/data/workflows.d.ts +16 -0
  155. package/dist/data/workflows.js +21 -0
  156. package/dist/device/connection.d.ts +41 -0
  157. package/dist/device/connection.js +249 -0
  158. package/dist/device/connection.test.d.ts +1 -0
  159. package/dist/device/connection.test.js +292 -0
  160. package/dist/device/device-election.d.ts +36 -0
  161. package/dist/device/device-election.js +137 -0
  162. package/dist/device/device.d.ts +22 -0
  163. package/dist/device/device.js +110 -0
  164. package/dist/device/device.test.d.ts +1 -0
  165. package/dist/device/device.test.js +203 -0
  166. package/dist/device/get-trust-level.d.ts +3 -0
  167. package/dist/device/get-trust-level.js +87 -0
  168. package/dist/device/socket.type.d.ts +20 -0
  169. package/dist/device/socket.type.js +15 -0
  170. package/dist/device/streamed-socket.d.ts +27 -0
  171. package/dist/device/streamed-socket.js +154 -0
  172. package/dist/device/streamed-socket.test.d.ts +1 -0
  173. package/dist/device/streamed-socket.test.js +44 -0
  174. package/dist/events.d.ts +35 -0
  175. package/dist/events.js +128 -0
  176. package/dist/index.d.ts +33 -0
  177. package/dist/index.js +50 -0
  178. package/dist/keys.d.ts +51 -0
  179. package/dist/keys.js +234 -0
  180. package/dist/keys.test.d.ts +1 -0
  181. package/dist/keys.test.js +215 -0
  182. package/dist/mentions.d.ts +9 -0
  183. package/dist/mentions.js +46 -0
  184. package/dist/observable.d.ts +19 -0
  185. package/dist/observable.js +112 -0
  186. package/dist/observable.test.d.ts +1 -0
  187. package/dist/observable.test.js +183 -0
  188. package/dist/package-loader/get-require.d.ts +10 -0
  189. package/dist/package-loader/get-require.js +31 -0
  190. package/dist/package-loader/index.d.ts +1 -0
  191. package/dist/package-loader/index.js +17 -0
  192. package/dist/package-loader/package-loader.d.ts +16 -0
  193. package/dist/package-loader/package-loader.js +102 -0
  194. package/dist/peers-ui/peers-ui.d.ts +15 -0
  195. package/dist/peers-ui/peers-ui.js +23 -0
  196. package/dist/peers-ui/peers-ui.types.d.ts +35 -0
  197. package/dist/peers-ui/peers-ui.types.js +3 -0
  198. package/dist/rpc-types.d.ts +45 -0
  199. package/dist/rpc-types.js +47 -0
  200. package/dist/serial-json.d.ts +5 -0
  201. package/dist/serial-json.js +186 -0
  202. package/dist/serial-json.test.d.ts +1 -0
  203. package/dist/serial-json.test.js +86 -0
  204. package/dist/system-ids.d.ts +6 -0
  205. package/dist/system-ids.js +10 -0
  206. package/dist/tools/index.d.ts +1 -0
  207. package/dist/tools/index.js +17 -0
  208. package/dist/tools/tools-factory.d.ts +5 -0
  209. package/dist/tools/tools-factory.js +34 -0
  210. package/dist/types/app-nav.d.ts +18 -0
  211. package/dist/types/app-nav.js +10 -0
  212. package/dist/types/assistant-runner-args.d.ts +9 -0
  213. package/dist/types/assistant-runner-args.js +2 -0
  214. package/dist/types/field-type.d.ts +37 -0
  215. package/dist/types/field-type.js +26 -0
  216. package/dist/types/peer-device.d.ts +40 -0
  217. package/dist/types/peer-device.js +14 -0
  218. package/dist/types/peers-package.d.ts +23 -0
  219. package/dist/types/peers-package.js +2 -0
  220. package/dist/types/workflow-logger.d.ts +2 -0
  221. package/dist/types/workflow-logger.js +2 -0
  222. package/dist/types/workflow-run-context.d.ts +12 -0
  223. package/dist/types/workflow-run-context.js +2 -0
  224. package/dist/types/workflow.d.ts +72 -0
  225. package/dist/types/workflow.js +24 -0
  226. package/dist/types/zod-types.d.ts +7 -0
  227. package/dist/types/zod-types.js +12 -0
  228. package/dist/users.query.d.ts +13 -0
  229. package/dist/users.query.js +134 -0
  230. package/dist/utils.d.ts +39 -0
  231. package/dist/utils.js +240 -0
  232. package/dist/utils.test.d.ts +1 -0
  233. package/dist/utils.test.js +140 -0
  234. package/package.json +50 -0
@@ -0,0 +1,456 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const SQLiteDB = require("better-sqlite3");
4
+ const utils_1 = require("../utils");
5
+ const data_locks_1 = require("./data-locks");
6
+ const sql_data_source_1 = require("./orm/sql.data-source");
7
+ const types_1 = require("./orm/types");
8
+ const group_member_roles_1 = require("./group-member-roles");
9
+ class DBHarness {
10
+ _db = null;
11
+ get database() {
12
+ if (!this._db) {
13
+ this._db = new SQLiteDB(':memory:');
14
+ this._db.pragma('journal_mode = WAL');
15
+ }
16
+ return this._db;
17
+ }
18
+ async get(sql, params = []) {
19
+ return this.database.prepare(sql).get(params);
20
+ }
21
+ async all(sql, params = []) {
22
+ return this.database.prepare(sql).all(params);
23
+ }
24
+ async exec(sql, params = []) {
25
+ this.database.prepare(sql).run(params);
26
+ }
27
+ async close() {
28
+ this._db?.close();
29
+ this._db = null;
30
+ }
31
+ }
32
+ // Mock peer device for testing
33
+ class MockPeerDevice {
34
+ deviceId = 'mock-device';
35
+ userId = 'mock-user';
36
+ role = group_member_roles_1.GroupMemberRole.Owner;
37
+ connections = [];
38
+ changes = [];
39
+ constructor(connectionCount = 3) {
40
+ this.connections = Array.from({ length: connectionCount }, (_, i) => ({
41
+ deviceId: `peer-${i}`,
42
+ latencyMs: 50,
43
+ errorRate: 0,
44
+ timestampLastApplied: Date.now()
45
+ }));
46
+ }
47
+ async getNetworkInfo() {
48
+ return {
49
+ deviceId: this.deviceId,
50
+ timestampLastApplied: Date.now(),
51
+ connections: this.connections,
52
+ preferredDeviceIds: []
53
+ };
54
+ }
55
+ async listChanges(query) {
56
+ if (!query)
57
+ return this.changes;
58
+ return this.changes.filter(change => change.tableName === query.tableName &&
59
+ change.recordId === query.recordId &&
60
+ change.changeType === query.changeType);
61
+ }
62
+ async notifyOfChanges(deviceId, timestampLastApplied) {
63
+ // Mock implementation
64
+ }
65
+ async downloadFileChunk(_chunkHash) {
66
+ // Mock implementation - return null to indicate chunk not found
67
+ return null;
68
+ }
69
+ async getFileChunkInfo(_chunkHash) {
70
+ // Mock implementation - return that we don't have the chunk
71
+ return { hasChunk: false };
72
+ }
73
+ addAcknowledgment(recordId, acknowledged) {
74
+ this.changes.push({
75
+ changeId: (0, utils_1.newid)(),
76
+ tableName: 'DataLocks',
77
+ recordId,
78
+ changeType: 'update',
79
+ newRecord: { acknowledged },
80
+ timestamp: Date.now(),
81
+ timestampApplied: Date.now(),
82
+ jsonDiff: []
83
+ });
84
+ }
85
+ setConnectionCount(count) {
86
+ this.connections = Array.from({ length: count }, (_, i) => ({
87
+ deviceId: `peer-${i}`,
88
+ latencyMs: 50,
89
+ errorRate: 0,
90
+ timestampLastApplied: Date.now()
91
+ }));
92
+ }
93
+ }
94
+ describe('data-locks.ts', () => {
95
+ const fiveMinutesMs = 5 * 60 * 1000;
96
+ jest.setTimeout(fiveMinutesMs);
97
+ let db;
98
+ let dataSource;
99
+ let dataLocksTable;
100
+ let mockPeerDevice;
101
+ const dataLocksTableMetaData = {
102
+ name: 'DataLocks',
103
+ primaryKeyName: 'dataLockId',
104
+ description: 'Data locks table to track exclusive write access to records in other tables.',
105
+ fields: (0, types_1.schemaToFields)(data_locks_1.dataLockSchema),
106
+ indexes: [
107
+ { fields: ['recordId'] },
108
+ ],
109
+ };
110
+ beforeAll(async () => {
111
+ db = new DBHarness();
112
+ dataSource = new sql_data_source_1.SQLDataSource(db, dataLocksTableMetaData);
113
+ const mockDataContextForRegistry = {
114
+ dataContextId: 'data-locks-test'
115
+ };
116
+ const { EventRegistry } = require('./orm/event-registry');
117
+ const eventRegistry = new EventRegistry(mockDataContextForRegistry);
118
+ const mockDataContext = {
119
+ dataSourceFactory: () => dataSource,
120
+ eventRegistry: eventRegistry
121
+ };
122
+ const deps = {
123
+ dataSource,
124
+ eventRegistry,
125
+ schema: data_locks_1.dataLockSchema
126
+ };
127
+ dataLocksTable = new data_locks_1.DataLocksTable(dataLocksTableMetaData, deps);
128
+ mockPeerDevice = new MockPeerDevice();
129
+ dataLocksTable.peerDevice = mockPeerDevice;
130
+ await dataSource.dropTableIfExists();
131
+ });
132
+ afterAll(async () => {
133
+ await db.close();
134
+ });
135
+ beforeEach(async () => {
136
+ // Clean up any existing locks before each test
137
+ const allLocks = await dataLocksTable.list();
138
+ for (const lock of allLocks) {
139
+ await dataLocksTable.delete(lock);
140
+ }
141
+ mockPeerDevice.setConnectionCount(3);
142
+ });
143
+ describe('DataLocksTable', () => {
144
+ describe('constructor', () => {
145
+ it('should create a DataLocksTable instance with default constants', () => {
146
+ expect(dataLocksTable.DEFAULT_LOCK_TIME_MS).toBe(300_000); // 5 minutes
147
+ expect(dataLocksTable.DEFAULT_TIMEOUT_MS).toBe(20_000); // 20 seconds
148
+ expect(dataLocksTable.DEAD_PERIOD_MS).toBe(10_000); // 10 seconds
149
+ });
150
+ it('should have peerDevice property', () => {
151
+ expect(dataLocksTable.peerDevice).toBeDefined();
152
+ });
153
+ });
154
+ describe('getCurrentLock', () => {
155
+ it('should return undefined when no locks exist for recordId', async () => {
156
+ const result = await dataLocksTable.getCurrentLock('nonexistent-record');
157
+ expect(result).toBeUndefined();
158
+ });
159
+ it('should return undefined when only expired locks exist', async () => {
160
+ const recordId = (0, utils_1.newid)();
161
+ const expiredLock = {
162
+ dataLockId: (0, utils_1.newid)(),
163
+ recordId,
164
+ lockedUntil: Date.now() - 1000, // Expired 1 second ago
165
+ };
166
+ await dataLocksTable.insert(expiredLock);
167
+ const result = await dataLocksTable.getCurrentLock(recordId);
168
+ expect(result).toBeUndefined();
169
+ });
170
+ it('should return the current valid lock', async () => {
171
+ const recordId = (0, utils_1.newid)();
172
+ const validLock = {
173
+ dataLockId: (0, utils_1.newid)(),
174
+ recordId,
175
+ lockedUntil: Date.now() + 60000, // Expires in 1 minute
176
+ };
177
+ await dataLocksTable.insert(validLock);
178
+ const result = await dataLocksTable.getCurrentLock(recordId);
179
+ expect(result).toEqual(validLock);
180
+ });
181
+ it('should return the earliest lock when multiple valid locks exist', async () => {
182
+ const recordId = (0, utils_1.newid)();
183
+ const lock1 = {
184
+ dataLockId: (0, utils_1.newid)(),
185
+ recordId,
186
+ lockedUntil: Date.now() + 60000,
187
+ };
188
+ const lock2 = {
189
+ dataLockId: (0, utils_1.newid)(),
190
+ recordId,
191
+ lockedUntil: Date.now() + 60000,
192
+ };
193
+ // Insert them in order so we can predict which should be first
194
+ const firstLock = lock1.dataLockId < lock2.dataLockId ? lock1 : lock2;
195
+ const secondLock = lock1.dataLockId < lock2.dataLockId ? lock2 : lock1;
196
+ await dataLocksTable.insert(secondLock);
197
+ await dataLocksTable.insert(firstLock);
198
+ const result = await dataLocksTable.getCurrentLock(recordId);
199
+ expect(result?.dataLockId).toBe(firstLock.dataLockId);
200
+ });
201
+ });
202
+ describe('releaseLock', () => {
203
+ it('should delete the lock from the database', async () => {
204
+ const lock = {
205
+ dataLockId: (0, utils_1.newid)(),
206
+ recordId: (0, utils_1.newid)(),
207
+ lockedUntil: Date.now() + 60000,
208
+ };
209
+ await dataLocksTable.insert(lock);
210
+ expect(await dataLocksTable.get(lock.dataLockId)).toEqual(lock);
211
+ await dataLocksTable.releaseLock(lock);
212
+ expect(await dataLocksTable.get(lock.dataLockId)).toBeUndefined();
213
+ });
214
+ });
215
+ describe('acquireLock', () => {
216
+ it('should acquire a lock when no existing locks', async () => {
217
+ const recordId = (0, utils_1.newid)();
218
+ // Mock sufficient acknowledgments for lock confirmation
219
+ const mockAcquireLock = jest.spyOn(dataLocksTable, 'acquireLock');
220
+ mockAcquireLock.mockImplementation(async () => {
221
+ const lock = {
222
+ dataLockId: (0, utils_1.newid)(),
223
+ recordId,
224
+ lockedUntil: Date.now() + dataLocksTable.DEFAULT_LOCK_TIME_MS,
225
+ };
226
+ await dataLocksTable.insert(lock);
227
+ return lock;
228
+ });
229
+ const result = await dataLocksTable.acquireLock(recordId);
230
+ expect(result).toBeDefined();
231
+ expect(result?.recordId).toBe(recordId);
232
+ expect(result?.lockedUntil).toBeGreaterThan(Date.now());
233
+ mockAcquireLock.mockRestore();
234
+ });
235
+ it('should use custom timeout and lock time', async () => {
236
+ const recordId = (0, utils_1.newid)();
237
+ const customTimeout = 5000;
238
+ const customLockTime = 120000;
239
+ const mockAcquireLock = jest.spyOn(dataLocksTable, 'acquireLock');
240
+ mockAcquireLock.mockImplementation(async (recordId, timeoutMs, lockTimeMs) => {
241
+ expect(timeoutMs).toBe(customTimeout);
242
+ expect(lockTimeMs).toBe(customLockTime);
243
+ return {
244
+ dataLockId: (0, utils_1.newid)(),
245
+ recordId,
246
+ lockedUntil: Date.now() + (lockTimeMs || dataLocksTable.DEFAULT_LOCK_TIME_MS),
247
+ };
248
+ });
249
+ await dataLocksTable.acquireLock(recordId, customTimeout, customLockTime);
250
+ mockAcquireLock.mockRestore();
251
+ });
252
+ });
253
+ describe('renewLock', () => {
254
+ it('should renew a valid lock', async () => {
255
+ const lock = {
256
+ dataLockId: (0, utils_1.newid)(),
257
+ recordId: (0, utils_1.newid)(),
258
+ lockedUntil: Date.now() + 60000, // 1 minute from now
259
+ };
260
+ await dataLocksTable.insert(lock);
261
+ const originalLockedUntil = lock.lockedUntil;
262
+ // Small delay to ensure time difference
263
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
264
+ await sleep(10);
265
+ const renewedLock = await dataLocksTable.renewLock(lock, 120000);
266
+ expect(renewedLock).toBeDefined();
267
+ expect(renewedLock?.lockedUntil).toBeGreaterThan(originalLockedUntil);
268
+ });
269
+ it('should reject renewal of expired lock', async () => {
270
+ const lock = {
271
+ dataLockId: (0, utils_1.newid)(),
272
+ recordId: (0, utils_1.newid)(),
273
+ lockedUntil: Date.now() - 20000, // Expired 20 seconds ago (beyond DEAD_PERIOD_MS)
274
+ };
275
+ await dataLocksTable.insert(lock);
276
+ const result = await dataLocksTable.renewLock(lock);
277
+ expect(result).toBeUndefined();
278
+ });
279
+ it('should reject renewal when lock is no longer current', async () => {
280
+ const recordId = (0, utils_1.newid)();
281
+ const oldLock = {
282
+ dataLockId: 'old-lock',
283
+ recordId,
284
+ lockedUntil: Date.now() + 60000,
285
+ };
286
+ const newLock = {
287
+ dataLockId: 'new-lock', // Lexicographically later, but inserted after
288
+ recordId,
289
+ lockedUntil: Date.now() + 60000,
290
+ };
291
+ await dataLocksTable.insert(oldLock);
292
+ await dataLocksTable.insert(newLock);
293
+ // Try to renew the old lock - should fail because new lock is current
294
+ const result = await dataLocksTable.renewLock(oldLock);
295
+ expect(result).toBeUndefined();
296
+ });
297
+ });
298
+ describe('lockStatus', () => {
299
+ it('should return confirmed when sufficient acknowledgments', async () => {
300
+ const lock = {
301
+ dataLockId: (0, utils_1.newid)(),
302
+ recordId: (0, utils_1.newid)(),
303
+ lockedUntil: Date.now() + 60000,
304
+ };
305
+ // Add sufficient acknowledgments (need 4 for 3 connections: Math.ceil(3 * 0.7) + 1 = 4)
306
+ mockPeerDevice.addAcknowledgment(lock.dataLockId, Date.now());
307
+ mockPeerDevice.addAcknowledgment(lock.dataLockId, Date.now() + 1);
308
+ mockPeerDevice.addAcknowledgment(lock.dataLockId, Date.now() + 2);
309
+ mockPeerDevice.addAcknowledgment(lock.dataLockId, Date.now() + 3);
310
+ const status = await dataLocksTable.lockStatus(lock);
311
+ expect(status).toBe('confirmed');
312
+ });
313
+ it('should return pending when insufficient acknowledgments', async () => {
314
+ const lock = {
315
+ dataLockId: (0, utils_1.newid)(),
316
+ recordId: (0, utils_1.newid)(),
317
+ lockedUntil: Date.now() + 60000,
318
+ };
319
+ // Add insufficient acknowledgments (only 1, need 4)
320
+ mockPeerDevice.addAcknowledgment(lock.dataLockId, Date.now());
321
+ const status = await dataLocksTable.lockStatus(lock);
322
+ expect(status).toBe('pending');
323
+ });
324
+ it('should calculate acknowledgments needed correctly for different network sizes', async () => {
325
+ const lock = {
326
+ dataLockId: (0, utils_1.newid)(),
327
+ recordId: (0, utils_1.newid)(),
328
+ lockedUntil: Date.now() + 60000,
329
+ };
330
+ // Test with 1 connection (need Math.ceil(1 * 0.7) + 1 = 2 acknowledgments)
331
+ mockPeerDevice.setConnectionCount(1);
332
+ mockPeerDevice.addAcknowledgment(lock.dataLockId, Date.now());
333
+ mockPeerDevice.addAcknowledgment(lock.dataLockId, Date.now() + 1);
334
+ let status = await dataLocksTable.lockStatus(lock);
335
+ expect(status).toBe('confirmed');
336
+ // Test with 5 connections (need Math.ceil(5 * 0.7) + 1 = 5 acknowledgments)
337
+ mockPeerDevice.setConnectionCount(5);
338
+ status = await dataLocksTable.lockStatus(lock);
339
+ expect(status).toBe('pending'); // Still only 2 acknowledgments, need 5
340
+ });
341
+ it('should throw error when peerDevice is not set', async () => {
342
+ const lock = {
343
+ dataLockId: (0, utils_1.newid)(),
344
+ recordId: (0, utils_1.newid)(),
345
+ lockedUntil: Date.now() + 60000,
346
+ };
347
+ const originalPeerDevice = dataLocksTable.peerDevice;
348
+ dataLocksTable.peerDevice = undefined;
349
+ await expect(dataLocksTable.lockStatus(lock)).rejects.toThrow('Peer device not set for DataLocksTable');
350
+ dataLocksTable.peerDevice = originalPeerDevice;
351
+ });
352
+ });
353
+ });
354
+ describe('dataLockSchema', () => {
355
+ it('should validate valid lock data', () => {
356
+ const validLock = {
357
+ dataLockId: (0, utils_1.newid)(),
358
+ recordId: 'test-record',
359
+ lockedUntil: Date.now() + 60000,
360
+ };
361
+ const result = data_locks_1.dataLockSchema.safeParse(validLock);
362
+ expect(result.success).toBe(true);
363
+ });
364
+ it('should validate lock with acknowledgment', () => {
365
+ const validLock = {
366
+ dataLockId: (0, utils_1.newid)(),
367
+ recordId: 'test-record',
368
+ lockedUntil: Date.now() + 60000,
369
+ acknowledged: Date.now(),
370
+ };
371
+ const result = data_locks_1.dataLockSchema.safeParse(validLock);
372
+ expect(result.success).toBe(true);
373
+ });
374
+ it('should reject invalid lock data', () => {
375
+ const invalidLock = {
376
+ dataLockId: 123, // Should be string
377
+ recordId: 'test-record',
378
+ lockedUntil: 'invalid', // Should be number
379
+ };
380
+ const result = data_locks_1.dataLockSchema.safeParse(invalidLock);
381
+ expect(result.success).toBe(false);
382
+ });
383
+ it('should reject missing required fields', () => {
384
+ const incompleteLock = {
385
+ dataLockId: (0, utils_1.newid)(),
386
+ // Missing recordId and lockedUntil
387
+ };
388
+ const result = data_locks_1.dataLockSchema.safeParse(incompleteLock);
389
+ expect(result.success).toBe(false);
390
+ });
391
+ });
392
+ describe('DataLocks factory function', () => {
393
+ it('should return a DataLocksTable instance', () => {
394
+ // This is a simplified test - in practice you'd need to set up the factory properly
395
+ expect(typeof data_locks_1.DataLocks).toBe('function');
396
+ });
397
+ });
398
+ describe('integration scenarios', () => {
399
+ it('should handle concurrent lock attempts', async () => {
400
+ const recordId = (0, utils_1.newid)();
401
+ // This is a simplified version - real concurrent testing would require more complex setup
402
+ const mockInsert = jest.spyOn(dataLocksTable, 'insert');
403
+ let insertCount = 0;
404
+ mockInsert.mockImplementation(async (lock) => {
405
+ insertCount++;
406
+ return { ...lock, dataLockId: `lock-${insertCount}` };
407
+ });
408
+ const promises = [
409
+ dataLocksTable.insert({ dataLockId: (0, utils_1.newid)(), recordId, lockedUntil: Date.now() + 60000 }),
410
+ dataLocksTable.insert({ dataLockId: (0, utils_1.newid)(), recordId, lockedUntil: Date.now() + 60000 }),
411
+ dataLocksTable.insert({ dataLockId: (0, utils_1.newid)(), recordId, lockedUntil: Date.now() + 60000 }),
412
+ ];
413
+ const results = await Promise.all(promises);
414
+ expect(results).toHaveLength(3);
415
+ expect(insertCount).toBe(3);
416
+ mockInsert.mockRestore();
417
+ });
418
+ it('should handle lock expiration cleanup', async () => {
419
+ const recordId = (0, utils_1.newid)();
420
+ const expiredLock = {
421
+ dataLockId: (0, utils_1.newid)(),
422
+ recordId,
423
+ lockedUntil: Date.now() - 1000, // Expired
424
+ };
425
+ const validLock = {
426
+ dataLockId: (0, utils_1.newid)(),
427
+ recordId,
428
+ lockedUntil: Date.now() + 60000, // Valid
429
+ };
430
+ await dataLocksTable.insert(expiredLock);
431
+ await dataLocksTable.insert(validLock);
432
+ const currentLock = await dataLocksTable.getCurrentLock(recordId);
433
+ expect(currentLock?.dataLockId).toBe(validLock.dataLockId);
434
+ });
435
+ it('should handle multiple records with independent locks', async () => {
436
+ const record1 = (0, utils_1.newid)();
437
+ const record2 = (0, utils_1.newid)();
438
+ const lock1 = {
439
+ dataLockId: (0, utils_1.newid)(),
440
+ recordId: record1,
441
+ lockedUntil: Date.now() + 60000,
442
+ };
443
+ const lock2 = {
444
+ dataLockId: (0, utils_1.newid)(),
445
+ recordId: record2,
446
+ lockedUntil: Date.now() + 60000,
447
+ };
448
+ await dataLocksTable.insert(lock1);
449
+ await dataLocksTable.insert(lock2);
450
+ const currentLock1 = await dataLocksTable.getCurrentLock(record1);
451
+ const currentLock2 = await dataLocksTable.getCurrentLock(record2);
452
+ expect(currentLock1?.dataLockId).toBe(lock1.dataLockId);
453
+ expect(currentLock2?.dataLockId).toBe(lock2.dataLockId);
454
+ });
455
+ });
456
+ });
@@ -0,0 +1,19 @@
1
+ import { z } from "zod";
2
+ import { ITableMetaData } from "./orm";
3
+ import type { DataContext } from "../context/data-context";
4
+ export declare const deviceSyncInfoSchema: z.ZodObject<{
5
+ deviceId: z.ZodEffects<z.ZodString, string, string>;
6
+ timestampAppliedLast: z.ZodNumber;
7
+ }, "strip", z.ZodTypeAny, {
8
+ deviceId: string;
9
+ timestampAppliedLast: number;
10
+ }, {
11
+ deviceId: string;
12
+ timestampAppliedLast: number;
13
+ }>;
14
+ export type IDeviceSyncInfo = z.infer<typeof deviceSyncInfoSchema>;
15
+ export declare const deviceSyncInfoMetaData: ITableMetaData;
16
+ export declare function DeviceSyncInfos(dataContext?: DataContext): import("./orm").Table<{
17
+ deviceId: string;
18
+ timestampAppliedLast: number;
19
+ }>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deviceSyncInfoMetaData = exports.deviceSyncInfoSchema = void 0;
4
+ exports.DeviceSyncInfos = DeviceSyncInfos;
5
+ const zod_1 = require("zod");
6
+ const zod_types_1 = require("../types/zod-types");
7
+ const orm_1 = require("./orm");
8
+ const context_1 = require("../context");
9
+ const table_definitions_system_1 = require("./orm/table-definitions.system");
10
+ exports.deviceSyncInfoSchema = zod_1.z.object({
11
+ deviceId: zod_types_1.zodPeerId,
12
+ timestampAppliedLast: zod_1.z.number(),
13
+ });
14
+ exports.deviceSyncInfoMetaData = {
15
+ name: 'DeviceSyncTracking',
16
+ primaryKeyName: 'deviceId',
17
+ description: 'Sync tracking table',
18
+ fields: (0, orm_1.schemaToFields)(exports.deviceSyncInfoSchema),
19
+ localOnly: true,
20
+ };
21
+ (0, table_definitions_system_1.registerSystemTableDefinition)(exports.deviceSyncInfoMetaData, exports.deviceSyncInfoSchema);
22
+ function DeviceSyncInfos(dataContext) {
23
+ return (0, context_1.getTableContainer)(dataContext).getTable(exports.deviceSyncInfoMetaData, exports.deviceSyncInfoSchema);
24
+ }
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import { TrustLevel } from "../device/socket.type";
3
+ import type { DataContext } from "../context/data-context";
4
+ export declare const deviceSchema: z.ZodObject<{
5
+ deviceId: z.ZodString;
6
+ userId: z.ZodString;
7
+ firstSeen: z.ZodDate;
8
+ lastSeen: z.ZodDate;
9
+ name: z.ZodOptional<z.ZodString>;
10
+ serverUrl: z.ZodOptional<z.ZodString>;
11
+ trustLevel: z.ZodNativeEnum<typeof TrustLevel>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ userId: string;
14
+ trustLevel: TrustLevel;
15
+ deviceId: string;
16
+ firstSeen: Date;
17
+ lastSeen: Date;
18
+ name?: string | undefined;
19
+ serverUrl?: string | undefined;
20
+ }, {
21
+ userId: string;
22
+ trustLevel: TrustLevel;
23
+ deviceId: string;
24
+ firstSeen: Date;
25
+ lastSeen: Date;
26
+ name?: string | undefined;
27
+ serverUrl?: string | undefined;
28
+ }>;
29
+ export type IDevice = z.infer<typeof deviceSchema>;
30
+ export interface IDeviceInfo {
31
+ userId: string;
32
+ deviceId: string;
33
+ publicKey: string;
34
+ publicBoxKey: string;
35
+ }
36
+ export interface IDeviceHandshake extends IDeviceInfo {
37
+ connectionId: string;
38
+ serverAddress: string;
39
+ timestamp: number;
40
+ }
41
+ export declare function Devices(dataContext?: DataContext): import("./orm").Table<{
42
+ userId: string;
43
+ trustLevel: TrustLevel;
44
+ deviceId: string;
45
+ firstSeen: Date;
46
+ lastSeen: Date;
47
+ name?: string | undefined;
48
+ serverUrl?: string | undefined;
49
+ }>;
50
+ export declare const trustedServers: import("./persistent-vars").PersistentVar<string[]>;
51
+ export declare const thisDeviceId: import("./persistent-vars").PersistentVar<string>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.thisDeviceId = exports.trustedServers = exports.deviceSchema = void 0;
4
+ exports.Devices = Devices;
5
+ const zod_1 = require("zod");
6
+ const types_1 = require("./orm/types");
7
+ const persistent_vars_1 = require("./persistent-vars");
8
+ const user_context_singleton_1 = require("../context/user-context-singleton");
9
+ const table_definitions_system_1 = require("./orm/table-definitions.system");
10
+ const socket_type_1 = require("../device/socket.type");
11
+ exports.deviceSchema = zod_1.z.object({
12
+ deviceId: zod_1.z.string(),
13
+ userId: zod_1.z.string(),
14
+ firstSeen: zod_1.z.date(),
15
+ lastSeen: zod_1.z.date(),
16
+ name: zod_1.z.string().optional(),
17
+ serverUrl: zod_1.z.string().optional(),
18
+ trustLevel: zod_1.z.nativeEnum(socket_type_1.TrustLevel),
19
+ });
20
+ const metaData = {
21
+ name: 'Devices',
22
+ description: 'The Peer devices that this computer is aware of',
23
+ primaryKeyName: 'deviceId',
24
+ fields: (0, types_1.schemaToFields)(exports.deviceSchema),
25
+ };
26
+ (0, table_definitions_system_1.registerSystemTableDefinition)(metaData, exports.deviceSchema);
27
+ function Devices(dataContext) {
28
+ return (0, user_context_singleton_1.getTableContainer)(dataContext).getTable(metaData, exports.deviceSchema);
29
+ }
30
+ exports.trustedServers = (0, persistent_vars_1.groupVar)('trustedServers', {
31
+ defaultValue: [
32
+ 'https://peers.app',
33
+ 'https://peers-services.azurewebsites.net',
34
+ ],
35
+ });
36
+ exports.thisDeviceId = (0, persistent_vars_1.deviceVar)('thisDeviceId');
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import type { DataContext } from "../context/data-context";
3
+ export declare const embeddingSchema: z.ZodObject<{
4
+ embeddingId: z.ZodString;
5
+ textHash: z.ZodString;
6
+ embedding: z.ZodArray<z.ZodNumber, "many">;
7
+ embeddingType: z.ZodString;
8
+ metadata: z.ZodObject<{
9
+ fkId: z.ZodOptional<z.ZodString>;
10
+ }, "strip", z.ZodAny, z.objectOutputType<{
11
+ fkId: z.ZodOptional<z.ZodString>;
12
+ }, z.ZodAny, "strip">, z.objectInputType<{
13
+ fkId: z.ZodOptional<z.ZodString>;
14
+ }, z.ZodAny, "strip">>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ metadata: {
17
+ fkId?: string | undefined;
18
+ } & {
19
+ [k: string]: any;
20
+ };
21
+ embeddingId: string;
22
+ textHash: string;
23
+ embedding: number[];
24
+ embeddingType: string;
25
+ }, {
26
+ metadata: {
27
+ fkId?: string | undefined;
28
+ } & {
29
+ [k: string]: any;
30
+ };
31
+ embeddingId: string;
32
+ textHash: string;
33
+ embedding: number[];
34
+ embeddingType: string;
35
+ }>;
36
+ export type IEmbedding = z.infer<typeof embeddingSchema>;
37
+ export declare function Embeddings(dataContext?: DataContext): import("./orm").Table<{
38
+ metadata: {
39
+ fkId?: string | undefined;
40
+ } & {
41
+ [k: string]: any;
42
+ };
43
+ embeddingId: string;
44
+ textHash: string;
45
+ embedding: number[];
46
+ embeddingType: string;
47
+ }>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.embeddingSchema = void 0;
4
+ exports.Embeddings = Embeddings;
5
+ const zod_1 = require("zod");
6
+ const types_1 = require("./orm/types");
7
+ const user_context_singleton_1 = require("../context/user-context-singleton");
8
+ const table_definitions_system_1 = require("./orm/table-definitions.system");
9
+ exports.embeddingSchema = zod_1.z.object({
10
+ embeddingId: zod_1.z.string(),
11
+ textHash: zod_1.z.string().describe('The hash of the text'),
12
+ embedding: zod_1.z.array(zod_1.z.number()).describe('The embedding of the text'),
13
+ embeddingType: zod_1.z.string().describe('The type of embedding used'),
14
+ metadata: zod_1.z.object({
15
+ fkId: zod_1.z.string().optional(),
16
+ // fkTable: z.string().optional(),
17
+ // fkIdFieldName: z.string().optional(),
18
+ }).catchall(zod_1.z.any()).describe('Any metadata associated with the embedding'),
19
+ });
20
+ const metaData = {
21
+ name: 'Embeddings',
22
+ description: 'The embeddings for some specific text',
23
+ primaryKeyName: 'embeddingId',
24
+ fields: (0, types_1.schemaToFields)(exports.embeddingSchema),
25
+ indexes: [
26
+ {
27
+ fields: ['textHash', 'embeddingType'],
28
+ unique: true,
29
+ },
30
+ ],
31
+ localOnly: true,
32
+ };
33
+ (0, table_definitions_system_1.registerSystemTableDefinition)(metaData, exports.embeddingSchema);
34
+ function Embeddings(dataContext) {
35
+ return (0, user_context_singleton_1.getTableContainer)(dataContext).getTable(metaData, exports.embeddingSchema);
36
+ }