@keeper-security/keeper-sdk-javascript 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 (173) hide show
  1. package/dist/auth/ConsoleAuthUI.d.ts +10 -0
  2. package/dist/auth/ConsoleAuthUI.js +152 -0
  3. package/dist/auth/ConsoleAuthUI.js.map +1 -0
  4. package/dist/auth/ConsoleLogin.d.ts +8 -0
  5. package/dist/auth/ConsoleLogin.js +266 -0
  6. package/dist/auth/ConsoleLogin.js.map +1 -0
  7. package/dist/auth/SessionManager.d.ts +66 -0
  8. package/dist/auth/SessionManager.js +211 -0
  9. package/dist/auth/SessionManager.js.map +1 -0
  10. package/dist/index.d.ts +17 -0
  11. package/dist/index.js +61 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/records/RecordOperations.d.ts +79 -0
  14. package/dist/records/RecordOperations.js +346 -0
  15. package/dist/records/RecordOperations.js.map +1 -0
  16. package/dist/records/RecordUtils.d.ts +36 -0
  17. package/dist/records/RecordUtils.js +224 -0
  18. package/dist/records/RecordUtils.js.map +1 -0
  19. package/dist/sharing/Sharing.d.ts +27 -0
  20. package/dist/sharing/Sharing.js +125 -0
  21. package/dist/sharing/Sharing.js.map +1 -0
  22. package/dist/src/auth/ConsoleAuthUI.d.ts +10 -0
  23. package/dist/src/auth/ConsoleAuthUI.js +161 -0
  24. package/dist/src/auth/ConsoleAuthUI.js.map +1 -0
  25. package/dist/src/auth/ConsoleLogin.d.ts +8 -0
  26. package/dist/src/auth/ConsoleLogin.js +311 -0
  27. package/dist/src/auth/ConsoleLogin.js.map +1 -0
  28. package/dist/src/auth/SessionManager.d.ts +67 -0
  29. package/dist/src/auth/SessionManager.js +212 -0
  30. package/dist/src/auth/SessionManager.js.map +1 -0
  31. package/dist/src/folders/FolderManager.d.ts +57 -0
  32. package/dist/src/folders/FolderManager.js +108 -0
  33. package/dist/src/folders/FolderManager.js.map +1 -0
  34. package/dist/src/folders/addFolder.d.ts +32 -0
  35. package/dist/src/folders/addFolder.js +207 -0
  36. package/dist/src/folders/addFolder.js.map +1 -0
  37. package/dist/src/folders/changeDirectory.d.ts +19 -0
  38. package/dist/src/folders/changeDirectory.js +171 -0
  39. package/dist/src/folders/changeDirectory.js.map +1 -0
  40. package/dist/src/folders/deleteFolder.d.ts +17 -0
  41. package/dist/src/folders/deleteFolder.js +237 -0
  42. package/dist/src/folders/deleteFolder.js.map +1 -0
  43. package/dist/src/folders/folderHelpers.d.ts +48 -0
  44. package/dist/src/folders/folderHelpers.js +100 -0
  45. package/dist/src/folders/folderHelpers.js.map +1 -0
  46. package/dist/src/folders/folderTree.d.ts +29 -0
  47. package/dist/src/folders/folderTree.js +250 -0
  48. package/dist/src/folders/folderTree.js.map +1 -0
  49. package/dist/src/folders/getFolder.d.ts +56 -0
  50. package/dist/src/folders/getFolder.js +143 -0
  51. package/dist/src/folders/getFolder.js.map +1 -0
  52. package/dist/src/folders/listFolder.d.ts +48 -0
  53. package/dist/src/folders/listFolder.js +276 -0
  54. package/dist/src/folders/listFolder.js.map +1 -0
  55. package/dist/src/folders/updateFolder.d.ts +31 -0
  56. package/dist/src/folders/updateFolder.js +137 -0
  57. package/dist/src/folders/updateFolder.js.map +1 -0
  58. package/dist/src/index.d.ts +49 -0
  59. package/dist/src/index.js +151 -0
  60. package/dist/src/index.js.map +1 -0
  61. package/dist/src/records/RecordOperations.d.ts +80 -0
  62. package/dist/src/records/RecordOperations.js +356 -0
  63. package/dist/src/records/RecordOperations.js.map +1 -0
  64. package/dist/src/records/RecordUtils.d.ts +37 -0
  65. package/dist/src/records/RecordUtils.js +263 -0
  66. package/dist/src/records/RecordUtils.js.map +1 -0
  67. package/dist/src/records/Totp.d.ts +14 -0
  68. package/dist/src/records/Totp.js +111 -0
  69. package/dist/src/records/Totp.js.map +1 -0
  70. package/dist/src/sharedFolders/SharedFolderManager.d.ts +20 -0
  71. package/dist/src/sharedFolders/SharedFolderManager.js +33 -0
  72. package/dist/src/sharedFolders/SharedFolderManager.js.map +1 -0
  73. package/dist/src/sharedFolders/listSharedFolders.d.ts +29 -0
  74. package/dist/src/sharedFolders/listSharedFolders.js +127 -0
  75. package/dist/src/sharedFolders/listSharedFolders.js.map +1 -0
  76. package/dist/src/sharedFolders/shareFolder.d.ts +36 -0
  77. package/dist/src/sharedFolders/shareFolder.js +352 -0
  78. package/dist/src/sharedFolders/shareFolder.js.map +1 -0
  79. package/dist/src/sharing/Sharing.d.ts +50 -0
  80. package/dist/src/sharing/Sharing.js +195 -0
  81. package/dist/src/sharing/Sharing.js.map +1 -0
  82. package/dist/src/storage/InMemoryStorage.d.ts +24 -0
  83. package/dist/src/storage/InMemoryStorage.js +139 -0
  84. package/dist/src/storage/InMemoryStorage.js.map +1 -0
  85. package/dist/src/teams/TeamManager.d.ts +17 -0
  86. package/dist/src/teams/TeamManager.js +38 -0
  87. package/dist/src/teams/TeamManager.js.map +1 -0
  88. package/dist/src/teams/enterpriseData.d.ts +106 -0
  89. package/dist/src/teams/enterpriseData.js +319 -0
  90. package/dist/src/teams/enterpriseData.js.map +1 -0
  91. package/dist/src/teams/listTeams.d.ts +42 -0
  92. package/dist/src/teams/listTeams.js +308 -0
  93. package/dist/src/teams/listTeams.js.map +1 -0
  94. package/dist/src/teams/viewTeam.d.ts +35 -0
  95. package/dist/src/teams/viewTeam.js +177 -0
  96. package/dist/src/teams/viewTeam.js.map +1 -0
  97. package/dist/src/utils/Logger.d.ts +28 -0
  98. package/dist/src/utils/Logger.js +62 -0
  99. package/dist/src/utils/Logger.js.map +1 -0
  100. package/dist/src/utils/constants.d.ts +50 -0
  101. package/dist/src/utils/constants.js +64 -0
  102. package/dist/src/utils/constants.js.map +1 -0
  103. package/dist/src/utils/errors.d.ts +10 -0
  104. package/dist/src/utils/errors.js +117 -0
  105. package/dist/src/utils/errors.js.map +1 -0
  106. package/dist/src/utils/guards.d.ts +7 -0
  107. package/dist/src/utils/guards.js +29 -0
  108. package/dist/src/utils/guards.js.map +1 -0
  109. package/dist/src/utils/index.d.ts +7 -0
  110. package/dist/src/utils/index.js +39 -0
  111. package/dist/src/utils/index.js.map +1 -0
  112. package/dist/src/utils/patterns.d.ts +9 -0
  113. package/dist/src/utils/patterns.js +20 -0
  114. package/dist/src/utils/patterns.js.map +1 -0
  115. package/dist/src/utils/types.d.ts +12 -0
  116. package/dist/src/utils/types.js +3 -0
  117. package/dist/src/utils/types.js.map +1 -0
  118. package/dist/src/vault/KeeperVault.d.ts +116 -0
  119. package/dist/src/vault/KeeperVault.js +443 -0
  120. package/dist/src/vault/KeeperVault.js.map +1 -0
  121. package/dist/storage/InMemoryStorage.d.ts +24 -0
  122. package/dist/storage/InMemoryStorage.js +132 -0
  123. package/dist/storage/InMemoryStorage.js.map +1 -0
  124. package/dist/utils/Logger.d.ts +28 -0
  125. package/dist/utils/Logger.js +62 -0
  126. package/dist/utils/Logger.js.map +1 -0
  127. package/dist/utils/constants.d.ts +26 -0
  128. package/dist/utils/constants.js +37 -0
  129. package/dist/utils/constants.js.map +1 -0
  130. package/dist/utils/errors.d.ts +10 -0
  131. package/dist/utils/errors.js +117 -0
  132. package/dist/utils/errors.js.map +1 -0
  133. package/dist/utils/index.d.ts +4 -0
  134. package/dist/utils/index.js +22 -0
  135. package/dist/utils/index.js.map +1 -0
  136. package/dist/vault/KeeperVault.d.ts +72 -0
  137. package/dist/vault/KeeperVault.js +338 -0
  138. package/dist/vault/KeeperVault.js.map +1 -0
  139. package/package.json +32 -0
  140. package/src/auth/ConsoleAuthUI.ts +169 -0
  141. package/src/auth/ConsoleLogin.ts +351 -0
  142. package/src/auth/SessionManager.ts +293 -0
  143. package/src/folders/FolderManager.ts +174 -0
  144. package/src/folders/addFolder.ts +294 -0
  145. package/src/folders/changeDirectory.ts +217 -0
  146. package/src/folders/deleteFolder.ts +293 -0
  147. package/src/folders/folderHelpers.ts +99 -0
  148. package/src/folders/folderTree.ts +321 -0
  149. package/src/folders/getFolder.ts +234 -0
  150. package/src/folders/listFolder.ts +358 -0
  151. package/src/folders/updateFolder.ts +210 -0
  152. package/src/index.ts +242 -0
  153. package/src/records/RecordOperations.ts +549 -0
  154. package/src/records/RecordUtils.ts +282 -0
  155. package/src/records/Totp.ts +119 -0
  156. package/src/sharedFolders/SharedFolderManager.ts +57 -0
  157. package/src/sharedFolders/listSharedFolders.ts +173 -0
  158. package/src/sharedFolders/shareFolder.ts +457 -0
  159. package/src/sharing/Sharing.ts +282 -0
  160. package/src/storage/InMemoryStorage.ts +163 -0
  161. package/src/teams/TeamManager.ts +61 -0
  162. package/src/teams/enterpriseData.ts +453 -0
  163. package/src/teams/listTeams.ts +373 -0
  164. package/src/teams/viewTeam.ts +248 -0
  165. package/src/utils/Logger.ts +71 -0
  166. package/src/utils/constants.ts +63 -0
  167. package/src/utils/errors.ts +108 -0
  168. package/src/utils/guards.ts +24 -0
  169. package/src/utils/index.ts +22 -0
  170. package/src/utils/patterns.ts +20 -0
  171. package/src/utils/types.ts +11 -0
  172. package/src/vault/KeeperVault.ts +612 -0
  173. package/tsconfig.json +16 -0
@@ -0,0 +1,612 @@
1
+ import {
2
+ Auth,
3
+ KeeperEnvironment,
4
+ syncDown,
5
+ DRecord,
6
+ DRecordMetadata,
7
+ DSharedFolder,
8
+ DTeam,
9
+ DUserFolder,
10
+ Authentication,
11
+ } from '@keeper-security/keeperapi'
12
+ import type { SyncResult, SyncLogFormat, VaultStorage, SessionStorage, AuthUI3 } from '@keeper-security/keeperapi'
13
+ import { InMemoryStorage } from '../storage/InMemoryStorage'
14
+ import { SessionManager } from '../auth/SessionManager'
15
+ import { ConsoleAuthUI } from '../auth/ConsoleAuthUI'
16
+ import { searchRecords, formatRecord, getRecordTitle, getRecordType } from '../records/RecordUtils'
17
+ import {
18
+ addRecord as addRecordOp,
19
+ updateRecord as updateRecordOp,
20
+ deleteRecord as deleteRecordOp,
21
+ getRecordHistory as getRecordHistoryOp,
22
+ moveRecord as moveRecordOp,
23
+ } from '../records/RecordOperations'
24
+ import type {
25
+ NewRecordInput,
26
+ TypedRecordData,
27
+ AddRecordResult,
28
+ UpdateRecordResult,
29
+ DeleteRecordResult,
30
+ RecordHistoryResult,
31
+ MoveRecordInput,
32
+ MoveRecordResult,
33
+ } from '../records/RecordOperations'
34
+ import {
35
+ shareRecord as shareRecordOp,
36
+ removeRecordShare as removeRecordShareOp,
37
+ getRecordShareInfo as getRecordShareInfoOp,
38
+ } from '../sharing/Sharing'
39
+ import type {
40
+ ShareRecordInput,
41
+ ShareRecordResult,
42
+ RemoveShareInput,
43
+ RemoveShareResult,
44
+ RecordShareInfo,
45
+ } from '../sharing/Sharing'
46
+ import type { ListFolderOptions, ListFolderResult } from '../folders/listFolder'
47
+ import { FolderKind, VaultObjectKind } from '../folders/folderHelpers'
48
+ import type { ChangeDirectoryResult, VaultFolderSession } from '../folders/changeDirectory'
49
+ import type { AddFolderInput, AddFolderResult, MkdirOptions } from '../folders/addFolder'
50
+ import type { UpdateFolderInput, UpdateFolderResult, RenameFolderResult } from '../folders/updateFolder'
51
+ import type { DeleteFolderResult, RmdirOptions } from '../folders/deleteFolder'
52
+ import type { FolderTreeBuildOptions } from '../folders/folderTree'
53
+ import type { GetFolderOptions, GetFolderResult } from '../folders/getFolder'
54
+ import type { ListSharedFolderRow, ListSharedFoldersOptions } from '../sharedFolders/listSharedFolders'
55
+ import type { ShareFolderInput, ShareFolderResult } from '../sharedFolders/shareFolder'
56
+ import { FolderManager } from '../folders/FolderManager'
57
+ import type { SharedFolderPermissionsInput } from '../folders/FolderManager'
58
+ import { SharedFolderManager } from '../sharedFolders/SharedFolderManager'
59
+ import { TeamManager } from '../teams/TeamManager'
60
+ import type { ListTeamRow, ListTeamsOptions } from '../teams/listTeams'
61
+ import type { TeamView } from '../teams/viewTeam'
62
+ import { ConsoleLogger, LogLevel, KeeperSdkError, extractErrorMessage, SdkDefaults, ResultCodes } from '../utils'
63
+ import type { ILogger } from '../utils'
64
+
65
+ enum VaultStatus {
66
+ RecordNotFound = 'RECORD_NOT_FOUND',
67
+ RecordKeyNotFound = 'RECORD_KEY_NOT_FOUND',
68
+ }
69
+
70
+ export type KeeperVaultConfig = {
71
+ host?: string
72
+ clientVersion?: string
73
+ configDir?: string
74
+ useConsoleAuth?: boolean
75
+ logFormat?: SyncLogFormat
76
+ logLevel?: LogLevel
77
+ autoSync?: boolean
78
+ storage?: InMemoryStorage
79
+ sessionStorage?: SessionManager
80
+ authUI?: AuthUI3
81
+ }
82
+
83
+ export type VaultSummary = {
84
+ recordCount: number
85
+ sharedFolderCount: number
86
+ teamCount: number
87
+ folderCount: number
88
+ }
89
+
90
+ export class KeeperVault {
91
+ private auth: Auth | null = null
92
+ private readonly storage: InMemoryStorage
93
+ private readonly sessionManager: SessionManager
94
+ private readonly authUI: AuthUI3
95
+ private readonly config: Required<Omit<KeeperVaultConfig, 'storage' | 'sessionStorage' | 'authUI'>>
96
+ private readonly log: ILogger
97
+ private synced = false
98
+ private batchDepth = 0
99
+ private readonly folderSession: VaultFolderSession = FolderManager.createSession()
100
+ private readonly folderManager: FolderManager
101
+ private readonly sharedFolderManager: SharedFolderManager
102
+ private readonly teamManager: TeamManager
103
+
104
+ constructor(config?: KeeperVaultConfig) {
105
+ this.config = {
106
+ host: config?.host || KeeperEnvironment.Prod,
107
+ clientVersion: config?.clientVersion || SdkDefaults.CLIENT_VERSION,
108
+ configDir: config?.configDir ?? '',
109
+ useConsoleAuth: config?.useConsoleAuth !== false,
110
+ logFormat: config?.logFormat || SdkDefaults.LOG_FORMAT,
111
+ logLevel: config?.logLevel ?? LogLevel.INFO,
112
+ autoSync: config?.autoSync !== false,
113
+ }
114
+
115
+ this.log = new ConsoleLogger(this.config.logLevel)
116
+ this.storage = config?.storage || new InMemoryStorage()
117
+ this.sessionManager = config?.sessionStorage || new SessionManager(this.config.configDir || undefined)
118
+ this.authUI = config?.authUI || new ConsoleAuthUI()
119
+
120
+ const authProvider = () => this.getAuthOrThrow()
121
+ this.folderManager = new FolderManager(this.storage, this.folderSession, authProvider)
122
+ this.sharedFolderManager = new SharedFolderManager(this.storage, authProvider)
123
+ this.teamManager = new TeamManager(authProvider)
124
+ }
125
+
126
+ public getFolderManager(): FolderManager {
127
+ return this.folderManager
128
+ }
129
+
130
+ public getSharedFolderManager(): SharedFolderManager {
131
+ return this.sharedFolderManager
132
+ }
133
+
134
+ public getTeamManager(): TeamManager {
135
+ return this.teamManager
136
+ }
137
+
138
+ private async createAuth(options?: { useSessionResumption?: boolean }): Promise<Auth> {
139
+ const host = this.config.host
140
+ const baseDeviceConfig = await this.sessionManager.getDeviceConfig(host)
141
+ const deviceConfig = {
142
+ ...baseDeviceConfig,
143
+ deviceName: baseDeviceConfig.deviceName || SdkDefaults.DEVICE_NAME,
144
+ }
145
+
146
+ const sessionStorage: SessionStorage =
147
+ options?.useSessionResumption === false
148
+ ? {
149
+ getCloneCode: async () => null,
150
+ saveCloneCode: (h, u, c) => this.sessionManager.saveCloneCode(h, u, c),
151
+ getSessionParameters: () => this.sessionManager.getSessionParameters(),
152
+ saveSessionParameters: (p) => this.sessionManager.saveSessionParameters(p),
153
+ }
154
+ : this.sessionManager
155
+
156
+ return new Auth({
157
+ host,
158
+ clientVersion: this.config.clientVersion,
159
+ deviceConfig,
160
+ authUI3: this.config.useConsoleAuth ? this.authUI : undefined,
161
+ sessionStorage,
162
+ onDeviceConfig: this.sessionManager.createOnDeviceConfig(host),
163
+ useSessionResumption: options?.useSessionResumption,
164
+ })
165
+ }
166
+
167
+ private getAuthOrThrow(): Auth {
168
+ if (!this.auth || !this.auth.sessionToken) {
169
+ throw new KeeperSdkError('Not logged in. Call login() first.', ResultCodes.NOT_LOGGED_IN)
170
+ }
171
+ return this.auth
172
+ }
173
+
174
+ public async login(username: string, password: string): Promise<void> {
175
+ this.auth = await this.createAuth({ useSessionResumption: false })
176
+ this.sessionManager.setLastUsername(username)
177
+
178
+ await this.auth.loginV3({
179
+ username,
180
+ password,
181
+ loginType: Authentication.LoginType.NORMAL,
182
+ loginMethod: Authentication.LoginMethod.EXISTING_ACCOUNT,
183
+ })
184
+
185
+ this.synced = false
186
+ this.log.info(`Logged in as ${username}`)
187
+ }
188
+
189
+ public async loginWithSessionToken(username: string, sessionToken: string): Promise<void> {
190
+ const deviceConfig = await this.sessionManager.getDeviceConfig(this.config.host)
191
+
192
+ if (!deviceConfig.deviceToken || !deviceConfig.privateKey) {
193
+ throw new KeeperSdkError(
194
+ 'Device is not registered for this host. Perform a normal login first to register the device before using session token login.',
195
+ ResultCodes.DEVICE_NOT_REGISTERED
196
+ )
197
+ }
198
+
199
+ this.auth = await this.createAuth()
200
+ this.sessionManager.setLastUsername(username)
201
+
202
+ await this.auth.loginV3({
203
+ username,
204
+ givenSessionToken: sessionToken,
205
+ loginType: Authentication.LoginType.NORMAL,
206
+ loginMethod: Authentication.LoginMethod.EXISTING_ACCOUNT,
207
+ })
208
+
209
+ if (!this.auth.sessionToken) {
210
+ throw new KeeperSdkError(
211
+ 'Session token login failed — token may be expired or invalid.',
212
+ ResultCodes.SESSION_TOKEN_EXPIRED
213
+ )
214
+ }
215
+
216
+ this.synced = false
217
+ this.log.info(`Logged in as ${username} (via session token)`)
218
+ }
219
+
220
+ public getSessionToken(): string | undefined {
221
+ return this.auth?.sessionToken || undefined
222
+ }
223
+
224
+ public async resumeSession(): Promise<void> {
225
+ const username = await this.sessionManager.getLastUsername()
226
+ if (!username) {
227
+ throw new KeeperSdkError(
228
+ 'No previous login found. Perform a normal login first.',
229
+ ResultCodes.NO_PREVIOUS_LOGIN
230
+ )
231
+ }
232
+
233
+ this.sessionManager.setLastUsername(username)
234
+
235
+ const deviceConfig = await this.sessionManager.getDeviceConfig(this.config.host)
236
+ if (!deviceConfig.deviceToken || !deviceConfig.privateKey) {
237
+ throw new KeeperSdkError(
238
+ 'Device is not registered for this host. Perform a normal login first.',
239
+ ResultCodes.DEVICE_NOT_REGISTERED
240
+ )
241
+ }
242
+
243
+ const cloneCode = await this.sessionManager.getCloneCode(this.config.host, username)
244
+ if (!cloneCode) {
245
+ throw new KeeperSdkError(
246
+ 'No clone code found. Persistent login not enabled or clone code expired. Perform a normal login.',
247
+ ResultCodes.NO_CLONE_CODE
248
+ )
249
+ }
250
+
251
+ this.auth = await this.createAuth({ useSessionResumption: true })
252
+
253
+ await this.auth.loginV3({
254
+ loginType: Authentication.LoginType.NORMAL,
255
+ resumeSessionOnly: true,
256
+ })
257
+
258
+ if (!this.auth.sessionToken) {
259
+ throw new KeeperSdkError(
260
+ 'Persistent login failed — clone code may be expired or persistent login not enabled. Perform a normal login.',
261
+ ResultCodes.PERSISTENT_LOGIN_FAILED
262
+ )
263
+ }
264
+
265
+ this.synced = false
266
+ this.log.info(`Session resumed for ${username} (persistent login)`)
267
+ }
268
+
269
+ public async sync(): Promise<SyncResult> {
270
+ const auth = this.getAuthOrThrow()
271
+
272
+ const result = await syncDown({
273
+ auth,
274
+ storage: this.storage,
275
+ logFormat: this.config.logFormat,
276
+ })
277
+
278
+ this.synced = true
279
+ return result
280
+ }
281
+
282
+ public async batch(fn: () => Promise<void>): Promise<void> {
283
+ this.batchDepth++
284
+ try {
285
+ await fn()
286
+ } finally {
287
+ this.batchDepth--
288
+ if (this.batchDepth === 0 && this.config.autoSync) {
289
+ await this.sync()
290
+ }
291
+ }
292
+ }
293
+
294
+ private async syncIfNeeded(): Promise<void> {
295
+ if (this.batchDepth > 0) {
296
+ this.synced = false
297
+ return
298
+ }
299
+ if (this.config.autoSync) {
300
+ await this.sync()
301
+ } else {
302
+ this.synced = false
303
+ }
304
+ }
305
+
306
+ public getRecords(): DRecord[] {
307
+ return this.storage.getRecords()
308
+ }
309
+
310
+ public getRecordByUid(uid: string): DRecord | undefined {
311
+ return this.storage.getByUid<DRecord>(VaultObjectKind.Record, uid)
312
+ }
313
+
314
+ public findRecord(uidOrTitle: string): DRecord | undefined {
315
+ const byUid = this.getRecordByUid(uidOrTitle)
316
+ if (byUid) return byUid
317
+
318
+ const lowerUidOrTitle = uidOrTitle.toLowerCase()
319
+ return this.getRecords().find((record) => getRecordTitle(record).toLowerCase() === lowerUidOrTitle)
320
+ }
321
+
322
+ public findRecords(criteria: string): DRecord[] {
323
+ return searchRecords(this.getRecords(), criteria)
324
+ }
325
+
326
+ public getRecordsByVersion(version: number): DRecord[] {
327
+ return this.getRecords().filter((record) => record.version === version)
328
+ }
329
+
330
+ public getRecordsByType(recordType: string): DRecord[] {
331
+ return this.getRecords().filter((record) => getRecordType(record) === recordType)
332
+ }
333
+
334
+ public getRecordMetadata(): DRecordMetadata[] {
335
+ return this.storage.getAll<DRecordMetadata>(VaultObjectKind.Metadata)
336
+ }
337
+
338
+ public getRecordMetadataByUid(uid: string): DRecordMetadata | undefined {
339
+ return this.storage.getByUid<DRecordMetadata>(VaultObjectKind.Metadata, uid)
340
+ }
341
+
342
+ public getSharedFolders(): DSharedFolder[] {
343
+ return this.storage.getAll<DSharedFolder>(FolderKind.SharedFolder)
344
+ }
345
+
346
+ public getTeams(): DTeam[] {
347
+ return this.storage.getAll<DTeam>(VaultObjectKind.Team)
348
+ }
349
+
350
+ public getUserFolders(): DUserFolder[] {
351
+ return this.storage.getAll<DUserFolder>(FolderKind.UserFolder)
352
+ }
353
+
354
+ public async listFolder(options?: ListFolderOptions): Promise<ListFolderResult> {
355
+ return this.folderManager.listFolder(options ?? {})
356
+ }
357
+
358
+ public listSharedFolders(options?: ListSharedFoldersOptions): ListSharedFolderRow[] {
359
+ return this.sharedFolderManager.listSharedFolders(options ?? {})
360
+ }
361
+
362
+ public async listTeams(options?: ListTeamsOptions): Promise<ListTeamRow[]> {
363
+ return this.teamManager.listTeams(options ?? {})
364
+ }
365
+
366
+ public async viewTeam(identifier: string): Promise<TeamView> {
367
+ return this.teamManager.viewTeam(identifier)
368
+ }
369
+
370
+ public async changeDirectory(path: string): Promise<ChangeDirectoryResult> {
371
+ return this.folderManager.changeDirectory(path)
372
+ }
373
+
374
+ public getCurrentFolderUid(): string | null {
375
+ return this.folderManager.getCurrentFolderUid()
376
+ }
377
+
378
+ public getWorkingFolderDisplayName(): string {
379
+ return this.folderManager.getWorkingFolderDisplayName()
380
+ }
381
+
382
+ public async getFolder(uidOrName: string, options?: GetFolderOptions): Promise<GetFolderResult> {
383
+ return this.folderManager.getFolder(uidOrName, options ?? {})
384
+ }
385
+
386
+ public async addFolder(input: AddFolderInput): Promise<AddFolderResult> {
387
+ const result = await this.folderManager.addFolder(input)
388
+ if (result.success) await this.syncIfNeeded()
389
+ return result
390
+ }
391
+
392
+ public async mkdir(
393
+ path: string,
394
+ options?: MkdirOptions
395
+ ): Promise<{ folderUid: string; success: boolean; message?: string }> {
396
+ const result = await this.folderManager.mkdir(path, options ?? {})
397
+ if (result.success) await this.syncIfNeeded()
398
+ return result
399
+ }
400
+
401
+ public async updateFolder(input: UpdateFolderInput): Promise<UpdateFolderResult> {
402
+ const result = await this.folderManager.updateFolder(input)
403
+ if (result.success) await this.syncIfNeeded()
404
+ return result
405
+ }
406
+
407
+ public async renameFolder(folderPath: string, newName: string): Promise<RenameFolderResult> {
408
+ const result = await this.folderManager.renameFolder(folderPath, newName)
409
+ if (result.success) await this.syncIfNeeded()
410
+ return result
411
+ }
412
+
413
+ public async updateSharedFolderPermissions(
414
+ sharedFolderUid: string,
415
+ permissions: SharedFolderPermissionsInput
416
+ ): Promise<UpdateFolderResult> {
417
+ const result = await this.folderManager.updateSharedFolderPermissions(sharedFolderUid, permissions)
418
+ if (result.success) await this.syncIfNeeded()
419
+ return result
420
+ }
421
+
422
+ public async deleteFolder(
423
+ folderRefs: string[],
424
+ confirm?: (summary: string) => boolean | Promise<boolean>
425
+ ): Promise<DeleteFolderResult> {
426
+ const result = await this.folderManager.deleteFolder(folderRefs, confirm)
427
+ if (result.success) await this.syncIfNeeded()
428
+ return result
429
+ }
430
+
431
+ public async rmdir(patterns: string[], options?: RmdirOptions): Promise<DeleteFolderResult> {
432
+ const result = await this.folderManager.rmdir(patterns, options ?? {})
433
+ if (result.success) await this.syncIfNeeded()
434
+ return result
435
+ }
436
+
437
+ public async tree(options?: FolderTreeBuildOptions): Promise<string> {
438
+ this.getAuthOrThrow()
439
+ return this.folderManager.folderTreeAscii(options ?? {})
440
+ }
441
+
442
+ public getSummary(): VaultSummary {
443
+ return {
444
+ recordCount: this.storage.getCount(VaultObjectKind.Record),
445
+ sharedFolderCount: this.storage.getCount(FolderKind.SharedFolder),
446
+ teamCount: this.storage.getCount(VaultObjectKind.Team),
447
+ folderCount: this.storage.getCount(FolderKind.UserFolder),
448
+ }
449
+ }
450
+
451
+ public printRecords(showDetails = false): void {
452
+ const records = this.getRecords()
453
+ if (records.length === 0) {
454
+ this.log.info('No records found in vault.')
455
+ return
456
+ }
457
+ this.log.info(`\n=== Vault Records (${records.length}) ===\n`)
458
+ for (const record of records) {
459
+ this.log.info(formatRecord(record, showDetails))
460
+ }
461
+ }
462
+
463
+ public async addRecord(input: NewRecordInput): Promise<AddRecordResult> {
464
+ const auth = this.getAuthOrThrow()
465
+ const result = await addRecordOp(auth, input)
466
+ if (result.success) await this.syncIfNeeded()
467
+ return result
468
+ }
469
+
470
+ public async updateRecord(recordUid: string, data: TypedRecordData): Promise<UpdateRecordResult> {
471
+ const auth = this.getAuthOrThrow()
472
+
473
+ const record = this.getRecordByUid(recordUid)
474
+ if (!record) {
475
+ return { recordUid, success: false, status: VaultStatus.RecordNotFound }
476
+ }
477
+
478
+ const keyBytes = await this.storage.getKeyBytes(recordUid)
479
+ if (!keyBytes) {
480
+ return {
481
+ recordUid,
482
+ success: false,
483
+ status: VaultStatus.RecordKeyNotFound,
484
+ }
485
+ }
486
+
487
+ const result = await updateRecordOp(auth, recordUid, data, record.revision, keyBytes)
488
+ if (result.success) await this.syncIfNeeded()
489
+ return result
490
+ }
491
+
492
+ public async deleteRecord(recordUid: string): Promise<DeleteRecordResult> {
493
+ const auth = this.getAuthOrThrow()
494
+ const result = await deleteRecordOp(auth, recordUid)
495
+ if (result.success) await this.syncIfNeeded()
496
+ return result
497
+ }
498
+
499
+ public async moveRecord(input: MoveRecordInput): Promise<MoveRecordResult> {
500
+ const auth = this.getAuthOrThrow()
501
+ const result = await moveRecordOp(auth, this.storage, input)
502
+ if (result.success) await this.syncIfNeeded()
503
+ return result
504
+ }
505
+
506
+ public async shareRecord(input: ShareRecordInput): Promise<ShareRecordResult> {
507
+ const auth = this.getAuthOrThrow()
508
+
509
+ const record = this.getRecordByUid(input.recordUid) || this.findRecord(input.recordUid)
510
+ if (!record) {
511
+ return {
512
+ recordUid: input.recordUid,
513
+ email: input.email,
514
+ success: false,
515
+ status: VaultStatus.RecordNotFound,
516
+ message: `Record "${input.recordUid}" not found`,
517
+ }
518
+ }
519
+
520
+ const keyBytes = await this.storage.getKeyBytes(record.uid)
521
+ if (!keyBytes) {
522
+ return {
523
+ recordUid: record.uid,
524
+ email: input.email,
525
+ success: false,
526
+ status: VaultStatus.RecordKeyNotFound,
527
+ message: 'Record key not available',
528
+ }
529
+ }
530
+
531
+ const result = await shareRecordOp(auth, keyBytes, {
532
+ ...input,
533
+ recordUid: record.uid,
534
+ })
535
+ if (result.success) await this.syncIfNeeded()
536
+ return result
537
+ }
538
+
539
+ public async removeRecordShare(input: RemoveShareInput): Promise<RemoveShareResult> {
540
+ const auth = this.getAuthOrThrow()
541
+ const result = await removeRecordShareOp(auth, input)
542
+ if (result.success) await this.syncIfNeeded()
543
+ return result
544
+ }
545
+
546
+ public async getRecordShareInfo(recordUid: string): Promise<RecordShareInfo | null> {
547
+ const auth = this.getAuthOrThrow()
548
+ return getRecordShareInfoOp(auth, recordUid)
549
+ }
550
+
551
+ public async shareFolder(input: ShareFolderInput): Promise<ShareFolderResult> {
552
+ const result = await this.sharedFolderManager.shareFolder(input)
553
+ if (result.success) await this.syncIfNeeded()
554
+ return result
555
+ }
556
+
557
+ public async getRecordHistory(recordUid: string): Promise<RecordHistoryResult> {
558
+ const auth = this.getAuthOrThrow()
559
+
560
+ const keyBytes = await this.storage.getKeyBytes(recordUid)
561
+ if (!keyBytes) {
562
+ return { recordUid, history: [] }
563
+ }
564
+
565
+ return getRecordHistoryOp(auth, recordUid, keyBytes)
566
+ }
567
+
568
+ public getStorage(): VaultStorage {
569
+ return this.storage
570
+ }
571
+
572
+ public getAuth(): Auth {
573
+ return this.getAuthOrThrow()
574
+ }
575
+
576
+ public disconnect(): void {
577
+ if (this.auth) {
578
+ try {
579
+ this.auth.disconnect()
580
+ } catch (err) {
581
+ this.log.debug('disconnect error:', extractErrorMessage(err))
582
+ }
583
+ this.auth = null
584
+ }
585
+ this.synced = false
586
+ this.folderSession.currentFolderUid = null
587
+ }
588
+
589
+ public async logout(): Promise<void> {
590
+ if (this.auth) {
591
+ try {
592
+ await this.auth.logout()
593
+ } catch (err) {
594
+ this.log.debug('logout error:', extractErrorMessage(err))
595
+ }
596
+ }
597
+ this.disconnect()
598
+ this.log.info('Logged out.')
599
+ }
600
+
601
+ public get host(): string {
602
+ return this.config.host
603
+ }
604
+
605
+ public get isLoggedIn(): boolean {
606
+ return this.auth !== null && !!this.auth.sessionToken
607
+ }
608
+
609
+ public get isSynced(): boolean {
610
+ return this.synced
611
+ }
612
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "es2018",
5
+ "sourceMap": true,
6
+ "strict": false,
7
+ "skipLibCheck": true,
8
+ "esModuleInterop": true,
9
+ "declaration": true,
10
+ "rootDir": ".",
11
+ "outDir": "dist",
12
+ "types": ["node"]
13
+ },
14
+ "include": ["src"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }