@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,358 @@
1
+ import type {
2
+ DRecord,
3
+ DRecordMetadata,
4
+ DSharedFolder,
5
+ DSharedFolderFolder,
6
+ DUserFolder,
7
+ VaultStorageData,
8
+ VaultStorageKind,
9
+ } from '@keeper-security/keeperapi'
10
+ import { InMemoryStorage } from '../storage/InMemoryStorage'
11
+ import { KeeperSdkError } from '../utils'
12
+ import { getRecordTitle, getRecordType } from '../records/RecordUtils'
13
+ import {
14
+ FolderKind,
15
+ VaultObjectKind,
16
+ getUserFolderParentMap,
17
+ globToRegex,
18
+ sharedFolderFolderName,
19
+ sharedFolderName,
20
+ userFolderName,
21
+ } from './folderHelpers'
22
+
23
+ export type ListFolderOptions = {
24
+ folderUid?: string | null
25
+ pattern?: string | null
26
+ showFolders?: boolean
27
+ showRecords?: boolean
28
+ detail?: boolean
29
+ }
30
+
31
+ export type ListFolderFolderSimple = {
32
+ uid: string
33
+ name: string
34
+ folderKind: FolderKind
35
+ }
36
+
37
+ export type ListFolderRecordSimple = {
38
+ uid: string
39
+ name: string
40
+ type: string
41
+ }
42
+
43
+ export type ListFolderFolderDetail = ListFolderFolderSimple & {
44
+ flags: string
45
+ recordCount: number
46
+ subfolderCount: number
47
+ }
48
+
49
+ export type ListFolderRecordDetail = ListFolderRecordSimple & {
50
+ flags: string
51
+ version: number
52
+ isOwner: boolean
53
+ hasAttachments: boolean
54
+ isShared: boolean
55
+ }
56
+
57
+ export type ListFolderResult =
58
+ | {
59
+ detail: true
60
+ folders: ListFolderFolderDetail[]
61
+ records: ListFolderRecordDetail[]
62
+ }
63
+ | {
64
+ detail: false
65
+ folders: ListFolderFolderSimple[]
66
+ records: ListFolderRecordSimple[]
67
+ }
68
+
69
+ function recordHasAttachments(record: DRecord): boolean {
70
+ const fileIds = record.udata?.file_ids
71
+ if (Array.isArray(fileIds) && fileIds.length > 0) return true
72
+ const files = (record.extra as { files?: unknown[] } | undefined)?.files
73
+ return Array.isArray(files) && files.length > 0
74
+ }
75
+
76
+ function buildFolderFlags(folderKind: ListFolderFolderSimple['folderKind']): string {
77
+ const isShared = folderKind !== FolderKind.UserFolder
78
+ return `f--${isShared ? 'S' : '-'}`
79
+ }
80
+
81
+ function buildRecordFlags(record: DRecord, metadata: DRecordMetadata | undefined): string {
82
+ const isOwner = metadata?.owner === true
83
+ const hasAttachments = recordHasAttachments(record)
84
+ const isShared = !!record.shared
85
+ return `r${isOwner ? 'O' : '-'}${hasAttachments ? 'A' : '-'}${isShared ? 'S' : '-'}`
86
+ }
87
+
88
+ export async function listRootUserFolders(storage: InMemoryStorage): Promise<DUserFolder[]> {
89
+ const userFolders = storage.getAll<DUserFolder>(FolderKind.UserFolder)
90
+ const childToParent = await getUserFolderParentMap(storage)
91
+ return userFolders.filter((userFolder) => !childToParent.has(userFolder.uid))
92
+ }
93
+
94
+ async function buildFolderParentMap(
95
+ storage: InMemoryStorage
96
+ ): Promise<Map<string, { uid: string; kind: VaultStorageKind }>> {
97
+ const childToParent = new Map<string, { uid: string; kind: VaultStorageKind }>()
98
+ const parentKinds: VaultStorageKind[] = [
99
+ FolderKind.UserFolder,
100
+ FolderKind.SharedFolder,
101
+ FolderKind.SharedFolderFolder,
102
+ ]
103
+ for (const kind of parentKinds) {
104
+ for (const candidate of storage.getAll<{ uid: string } & VaultStorageData>(kind)) {
105
+ const candidateUid = (candidate as { uid: string }).uid
106
+ if (!candidateUid) continue
107
+ const dependencies = (await storage.getDependencies(candidateUid)) || []
108
+ for (const dependency of dependencies) {
109
+ if (
110
+ dependency.kind === FolderKind.UserFolder ||
111
+ dependency.kind === FolderKind.SharedFolder ||
112
+ dependency.kind === FolderKind.SharedFolderFolder
113
+ ) {
114
+ if (!childToParent.has(dependency.uid)) {
115
+ childToParent.set(dependency.uid, { uid: candidateUid, kind })
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ return childToParent
122
+ }
123
+
124
+ export async function listVaultRootFolders(storage: InMemoryStorage): Promise<{
125
+ rows: ListFolderFolderSimple[]
126
+ promotedRootSharedUids: Set<string>
127
+ }> {
128
+ const rows: ListFolderFolderSimple[] = []
129
+ const seen = new Set<string>()
130
+ const promotedRootSharedUids = new Set<string>()
131
+
132
+ for (const userFolder of await listRootUserFolders(storage)) {
133
+ if (seen.has(userFolder.uid)) continue
134
+ seen.add(userFolder.uid)
135
+ rows.push({
136
+ uid: userFolder.uid,
137
+ name: userFolderName(userFolder),
138
+ folderKind: FolderKind.UserFolder,
139
+ })
140
+ }
141
+
142
+ const rootDependencies = (await storage.getDependencies('')) || []
143
+ for (const dependency of rootDependencies) {
144
+ if (dependency.kind === FolderKind.SharedFolder) {
145
+ const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, dependency.uid)
146
+ if (!sharedFolder || seen.has(sharedFolder.uid)) continue
147
+ seen.add(sharedFolder.uid)
148
+ rows.push({
149
+ uid: sharedFolder.uid,
150
+ name: sharedFolderName(sharedFolder),
151
+ folderKind: FolderKind.SharedFolder,
152
+ })
153
+ } else if (dependency.kind === FolderKind.SharedFolderFolder) {
154
+ const sharedFolderFolder = storage.getByUid<DSharedFolderFolder>(
155
+ FolderKind.SharedFolderFolder,
156
+ dependency.uid
157
+ )
158
+ if (!sharedFolderFolder || seen.has(sharedFolderFolder.uid)) continue
159
+ seen.add(sharedFolderFolder.uid)
160
+ rows.push({
161
+ uid: sharedFolderFolder.uid,
162
+ name: sharedFolderFolderName(sharedFolderFolder),
163
+ folderKind: FolderKind.SharedFolderFolder,
164
+ })
165
+ }
166
+ }
167
+
168
+ const parentMap = await buildFolderParentMap(storage)
169
+ for (const sharedFolder of storage.getAll<DSharedFolder>(FolderKind.SharedFolder)) {
170
+ if (seen.has(sharedFolder.uid)) continue
171
+ const parent = parentMap.get(sharedFolder.uid)
172
+ if (parent && (parent.kind === FolderKind.SharedFolder || parent.kind === FolderKind.SharedFolderFolder)) {
173
+ continue
174
+ }
175
+ seen.add(sharedFolder.uid)
176
+ promotedRootSharedUids.add(sharedFolder.uid)
177
+ rows.push({
178
+ uid: sharedFolder.uid,
179
+ name: sharedFolderName(sharedFolder),
180
+ folderKind: FolderKind.SharedFolder,
181
+ })
182
+ }
183
+
184
+ rows.sort((rowA, rowB) => rowA.name.localeCompare(rowB.name, undefined, { sensitivity: 'base' }))
185
+
186
+ return { rows, promotedRootSharedUids }
187
+ }
188
+
189
+ function resolveFolderContainer(storage: InMemoryStorage, folderUid: string): { kind: VaultStorageKind; uid: string } {
190
+ if (storage.getByUid<DUserFolder>(FolderKind.UserFolder, folderUid)) {
191
+ return { kind: FolderKind.UserFolder, uid: folderUid }
192
+ }
193
+ if (storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, folderUid)) {
194
+ return { kind: FolderKind.SharedFolder, uid: folderUid }
195
+ }
196
+ if (storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, folderUid)) {
197
+ return { kind: FolderKind.SharedFolderFolder, uid: folderUid }
198
+ }
199
+ throw new KeeperSdkError(`Folder "${folderUid}" not found`, 'folder_not_found')
200
+ }
201
+
202
+ function getRecordMetadata(storage: InMemoryStorage, recordUid: string): DRecordMetadata | undefined {
203
+ return storage.getByUid<DRecordMetadata>(VaultObjectKind.Metadata, recordUid)
204
+ }
205
+
206
+ export function findFolderUidByNameOrUid(storage: InMemoryStorage, nameOrUid: string): string | undefined {
207
+ const trimmedNameOrUid = nameOrUid.trim()
208
+ if (!trimmedNameOrUid) return undefined
209
+
210
+ if (
211
+ storage.getByUid<DUserFolder>(FolderKind.UserFolder, trimmedNameOrUid) ||
212
+ storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, trimmedNameOrUid) ||
213
+ storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, trimmedNameOrUid)
214
+ ) {
215
+ return trimmedNameOrUid
216
+ }
217
+
218
+ const lowerNameOrUid = trimmedNameOrUid.toLowerCase()
219
+ for (const userFolder of storage.getAll<DUserFolder>(FolderKind.UserFolder)) {
220
+ if (userFolderName(userFolder).toLowerCase() === lowerNameOrUid) return userFolder.uid
221
+ }
222
+ for (const sharedFolder of storage.getAll<DSharedFolder>(FolderKind.SharedFolder)) {
223
+ if (sharedFolderName(sharedFolder).toLowerCase() === lowerNameOrUid) return sharedFolder.uid
224
+ }
225
+ for (const sharedFolderFolder of storage.getAll<DSharedFolderFolder>(FolderKind.SharedFolderFolder)) {
226
+ if (sharedFolderFolderName(sharedFolderFolder).toLowerCase() === lowerNameOrUid) return sharedFolderFolder.uid
227
+ }
228
+ return undefined
229
+ }
230
+
231
+ async function countFolderChildren(
232
+ storage: InMemoryStorage,
233
+ uid: string
234
+ ): Promise<{ records: number; subfolders: number }> {
235
+ const dependencies = (await storage.getDependencies(uid)) || []
236
+ let records = 0
237
+ let subfolders = 0
238
+ for (const dependency of dependencies) {
239
+ if (dependency.kind === VaultObjectKind.Record) records++
240
+ else if (
241
+ dependency.kind === FolderKind.UserFolder ||
242
+ dependency.kind === FolderKind.SharedFolder ||
243
+ dependency.kind === FolderKind.SharedFolderFolder
244
+ ) {
245
+ subfolders++
246
+ }
247
+ }
248
+ return { records, subfolders }
249
+ }
250
+
251
+ export async function listFolder(storage: InMemoryStorage, options: ListFolderOptions = {}): Promise<ListFolderResult> {
252
+ const showFolders = options.showFolders !== false
253
+ const showRecords = options.showRecords !== false
254
+ const detail = options.detail === true
255
+ const patternRaw = options.pattern?.trim() || null
256
+ const regex = patternRaw ? globToRegex(patternRaw) : null
257
+
258
+ const folderUidOpt = options.folderUid
259
+ let parentKey: string | null = null
260
+
261
+ if (folderUidOpt !== undefined && folderUidOpt !== null && folderUidOpt !== '') {
262
+ parentKey = resolveFolderContainer(storage, folderUidOpt).uid
263
+ }
264
+
265
+ const dependencies =
266
+ parentKey === null
267
+ ? (await storage.getDependencies('')) || []
268
+ : (await storage.getDependencies(parentKey)) || []
269
+
270
+ const folderRows: ListFolderFolderSimple[] = []
271
+ const recordRows: ListFolderRecordSimple[] = []
272
+
273
+ const matches = (name: string, uid: string): boolean => {
274
+ if (!regex) return true
275
+ return regex.test(name) || regex.test(uid)
276
+ }
277
+
278
+ if (showFolders && parentKey === null) {
279
+ const { rows } = await listVaultRootFolders(storage)
280
+ for (const row of rows) {
281
+ if (!matches(row.name, row.uid)) continue
282
+ folderRows.push(row)
283
+ }
284
+ }
285
+
286
+ for (const dependency of dependencies) {
287
+ if (dependency.kind === FolderKind.UserFolder && showFolders && parentKey !== null) {
288
+ const userFolder = storage.getByUid<DUserFolder>(FolderKind.UserFolder, dependency.uid)
289
+ if (!userFolder) continue
290
+ const name = userFolderName(userFolder)
291
+ if (!matches(name, userFolder.uid)) continue
292
+ folderRows.push({ uid: userFolder.uid, name, folderKind: FolderKind.UserFolder })
293
+ } else if (dependency.kind === FolderKind.SharedFolder && showFolders && parentKey !== null) {
294
+ const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, dependency.uid)
295
+ if (!sharedFolder) continue
296
+ const name = sharedFolderName(sharedFolder)
297
+ if (!matches(name, sharedFolder.uid)) continue
298
+ folderRows.push({ uid: sharedFolder.uid, name, folderKind: FolderKind.SharedFolder })
299
+ } else if (dependency.kind === FolderKind.SharedFolderFolder && showFolders && parentKey !== null) {
300
+ const sharedFolderFolder = storage.getByUid<DSharedFolderFolder>(
301
+ FolderKind.SharedFolderFolder,
302
+ dependency.uid
303
+ )
304
+ if (!sharedFolderFolder) continue
305
+ const name = sharedFolderFolderName(sharedFolderFolder)
306
+ if (!matches(name, sharedFolderFolder.uid)) continue
307
+ folderRows.push({
308
+ uid: sharedFolderFolder.uid,
309
+ name,
310
+ folderKind: FolderKind.SharedFolderFolder,
311
+ })
312
+ } else if (dependency.kind === VaultObjectKind.Record && showRecords) {
313
+ const record = storage.getByUid<DRecord>(VaultObjectKind.Record, dependency.uid)
314
+ if (!record || (record.version !== 2 && record.version !== 3)) continue
315
+ const title = getRecordTitle(record)
316
+ if (!matches(title, record.uid)) continue
317
+ recordRows.push({
318
+ uid: record.uid,
319
+ name: title,
320
+ type: getRecordType(record),
321
+ })
322
+ }
323
+ }
324
+
325
+ folderRows.sort((rowA, rowB) => rowA.name.localeCompare(rowB.name, undefined, { sensitivity: 'base' }))
326
+ recordRows.sort((rowA, rowB) => rowA.name.localeCompare(rowB.name, undefined, { sensitivity: 'base' }))
327
+
328
+ if (!detail) {
329
+ return { detail: false, folders: folderRows, records: recordRows }
330
+ }
331
+
332
+ const folderDetails: ListFolderFolderDetail[] = await Promise.all(
333
+ folderRows.map(async (row) => {
334
+ const counts = await countFolderChildren(storage, row.uid)
335
+ return {
336
+ ...row,
337
+ flags: buildFolderFlags(row.folderKind),
338
+ recordCount: counts.records,
339
+ subfolderCount: counts.subfolders,
340
+ }
341
+ })
342
+ )
343
+
344
+ const recordDetails: ListFolderRecordDetail[] = recordRows.map((row) => {
345
+ const record = storage.getByUid<DRecord>(VaultObjectKind.Record, row.uid)!
346
+ const metadata = getRecordMetadata(storage, row.uid)
347
+ return {
348
+ ...row,
349
+ flags: buildRecordFlags(record, metadata),
350
+ version: record.version,
351
+ isOwner: metadata?.owner === true,
352
+ hasAttachments: recordHasAttachments(record),
353
+ isShared: !!record.shared,
354
+ }
355
+ })
356
+
357
+ return { detail: true, folders: folderDetails, records: recordDetails }
358
+ }
@@ -0,0 +1,210 @@
1
+ import type { Auth, FolderUpdateRequest } from '@keeper-security/keeperapi'
2
+ import {
3
+ encryptForStorage,
4
+ encryptObjectForStorage,
5
+ folderUpdateCommand,
6
+ platform,
7
+ } from '@keeper-security/keeperapi'
8
+ import type { DSharedFolder, DSharedFolderFolder, DUserFolder } from '@keeper-security/keeperapi'
9
+ import { InMemoryStorage } from '../storage/InMemoryStorage'
10
+ import { anyIsBoolean, isBoolean, isObject, KeeperSdkError, extractErrorMessage } from '../utils'
11
+ import { resolveSingleFolder, type VaultFolderSession } from './changeDirectory'
12
+ import { FolderKind, FolderResultStatus } from './folderHelpers'
13
+
14
+ export type UpdateFolderInput = {
15
+ folderUid: string
16
+ folderName?: string | null
17
+ manageUsers?: boolean | null
18
+ manageRecords?: boolean | null
19
+ canShare?: boolean | null
20
+ canEdit?: boolean | null
21
+ }
22
+
23
+ export type UpdateFolderResult = {
24
+ folderUid: string
25
+ success: boolean
26
+ message?: string
27
+ }
28
+
29
+ export type RenameFolderResult = {
30
+ folderUid: string
31
+ oldName: string
32
+ newName: string
33
+ success: boolean
34
+ message?: string
35
+ }
36
+
37
+ type ResolvedFolder =
38
+ | { kind: FolderKind.UserFolder; folder: DUserFolder }
39
+ | { kind: FolderKind.SharedFolder; folder: DSharedFolder }
40
+ | { kind: FolderKind.SharedFolderFolder; folder: DSharedFolderFolder }
41
+
42
+ function resolveFolderEntity(storage: InMemoryStorage, folderUid: string): ResolvedFolder | undefined {
43
+ const userFolder = storage.getByUid<DUserFolder>(FolderKind.UserFolder, folderUid)
44
+ if (userFolder) return { kind: FolderKind.UserFolder, folder: userFolder }
45
+ const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, folderUid)
46
+ if (sharedFolder) return { kind: FolderKind.SharedFolder, folder: sharedFolder }
47
+ const sharedFolderFolder = storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, folderUid)
48
+ if (sharedFolderFolder) return { kind: FolderKind.SharedFolderFolder, folder: sharedFolderFolder }
49
+ return undefined
50
+ }
51
+
52
+ function mergeFolderData(existing: unknown, folderName: string | null | undefined): Record<string, unknown> {
53
+ const base = isObject(existing) ? { ...existing } : {}
54
+ const trimmed = folderName?.trim()
55
+ if (trimmed) {
56
+ base.title = trimmed
57
+ base.name = trimmed
58
+ }
59
+ return base
60
+ }
61
+
62
+ export async function updateFolder(
63
+ auth: Auth,
64
+ storage: InMemoryStorage,
65
+ input: UpdateFolderInput
66
+ ): Promise<UpdateFolderResult> {
67
+ const folderUid = input.folderUid
68
+ const resolved = resolveFolderEntity(storage, folderUid)
69
+ if (!resolved) {
70
+ throw new KeeperSdkError(`Folder "${folderUid}" does not exist.`, 'folder_not_found')
71
+ }
72
+
73
+ const folderKey = await storage.getKeyBytes(folderUid)
74
+ if (!folderKey) {
75
+ throw new KeeperSdkError(
76
+ 'Folder encryption key not available. Sync the vault and try again.',
77
+ 'folder_key_missing'
78
+ )
79
+ }
80
+
81
+ const trimmedName = input.folderName?.trim() || ''
82
+
83
+ const hasPermissionUpdate = anyIsBoolean(
84
+ input.manageUsers,
85
+ input.manageRecords,
86
+ input.canShare,
87
+ input.canEdit
88
+ )
89
+
90
+ if (resolved.kind === FolderKind.UserFolder || resolved.kind === FolderKind.SharedFolderFolder) {
91
+ if (!trimmedName) {
92
+ throw new KeeperSdkError('Folder name is required.', 'folder_name_required')
93
+ }
94
+ } else if (resolved.kind === FolderKind.SharedFolder) {
95
+ if (!trimmedName && !hasPermissionUpdate) {
96
+ throw new KeeperSdkError(
97
+ 'Provide a new name or at least one permission to update.',
98
+ 'shared_folder_update_empty'
99
+ )
100
+ }
101
+ }
102
+
103
+ const effectiveName =
104
+ resolved.kind === FolderKind.SharedFolder ? trimmedName || resolved.folder.name || undefined : trimmedName
105
+ const mergedData = mergeFolderData(resolved.folder.data, effectiveName)
106
+
107
+ const request: FolderUpdateRequest = {
108
+ folder_uid: folderUid,
109
+ folder_type: resolved.kind,
110
+ data: await encryptObjectForStorage(mergedData, folderKey),
111
+ }
112
+
113
+ if (resolved.kind === FolderKind.SharedFolder) {
114
+ const sharedFolder = resolved.folder
115
+ const displayName = trimmedName || sharedFolder.name || folderUid
116
+ request.shared_folder_uid = folderUid
117
+ request.name = await encryptForStorage(platform.stringToBytes(displayName), folderKey)
118
+ request.manage_users = isBoolean(input.manageUsers) ? input.manageUsers : sharedFolder.defaultManageUsers
119
+ request.manage_records = isBoolean(input.manageRecords)
120
+ ? input.manageRecords
121
+ : sharedFolder.defaultManageRecords
122
+ request.can_edit = isBoolean(input.canEdit) ? input.canEdit : sharedFolder.defaultCanEdit
123
+ request.can_share = isBoolean(input.canShare) ? input.canShare : sharedFolder.defaultCanShare
124
+ } else if (resolved.kind === FolderKind.SharedFolderFolder) {
125
+ request.shared_folder_uid = resolved.folder.sharedFolderUid
126
+ }
127
+
128
+ const folderLabel = effectiveName || folderUid
129
+
130
+ try {
131
+ const response = await auth.executeRestCommand(folderUpdateCommand(request))
132
+ const succeeded =
133
+ response.result === FolderResultStatus.Success || response.result_code === FolderResultStatus.Success
134
+ if (!succeeded) {
135
+ const reason =
136
+ response.message ||
137
+ response.result_code ||
138
+ `folder_update failed for "${folderLabel}" (uid=${folderUid}, type=${resolved.kind}): server returned no message or result_code`
139
+ return {
140
+ folderUid,
141
+ success: false,
142
+ message: reason,
143
+ }
144
+ }
145
+ return { folderUid, success: true }
146
+ } catch (err) {
147
+ return {
148
+ folderUid,
149
+ success: false,
150
+ message: `folder_update failed for "${folderLabel}" (uid=${folderUid}, type=${resolved.kind}): ${extractErrorMessage(err)}`,
151
+ }
152
+ }
153
+ }
154
+
155
+ export async function renameFolder(
156
+ auth: Auth,
157
+ storage: InMemoryStorage,
158
+ session: VaultFolderSession,
159
+ folderPath: string,
160
+ newName: string
161
+ ): Promise<RenameFolderResult> {
162
+ const trimmedPath = folderPath.trim()
163
+ if (!trimmedPath) {
164
+ throw new KeeperSdkError('Folder cannot be empty.', 'folder_required')
165
+ }
166
+ const trimmedName = newName.trim()
167
+ if (!trimmedName) {
168
+ throw new KeeperSdkError('New folder name is required.', 'folder_name_required')
169
+ }
170
+
171
+ const resolved = await resolveSingleFolder(storage, session, trimmedPath)
172
+ if (resolved.folderUid === null) {
173
+ throw new KeeperSdkError('Cannot rename the root folder.', 'folder_root_rename')
174
+ }
175
+
176
+ const result = await updateFolder(auth, storage, {
177
+ folderUid: resolved.folderUid,
178
+ folderName: trimmedName,
179
+ })
180
+
181
+ return {
182
+ folderUid: resolved.folderUid,
183
+ oldName: resolved.name,
184
+ newName: trimmedName,
185
+ success: result.success,
186
+ message: result.message,
187
+ }
188
+ }
189
+
190
+ export async function updateSharedFolderPermissions(
191
+ auth: Auth,
192
+ storage: InMemoryStorage,
193
+ sharedFolderUid: string,
194
+ permissions: {
195
+ manageUsers?: boolean | null
196
+ manageRecords?: boolean | null
197
+ canShare?: boolean | null
198
+ canEdit?: boolean | null
199
+ }
200
+ ): Promise<UpdateFolderResult> {
201
+ const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, sharedFolderUid)
202
+ if (!sharedFolder) {
203
+ throw new KeeperSdkError(`"${sharedFolderUid}" is not a shared folder.`, 'not_shared_folder')
204
+ }
205
+ return updateFolder(auth, storage, {
206
+ folderUid: sharedFolderUid,
207
+ folderName: null,
208
+ ...permissions,
209
+ })
210
+ }