core-3nweb-client-lib 0.26.0 → 0.27.1

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 (199) hide show
  1. package/build/api-defs/asmail.d.ts +1 -1
  2. package/build/api-defs/files.d.ts +278 -69
  3. package/build/core/app-files.js +7 -7
  4. package/build/core/asmail/config/common.js +2 -2
  5. package/build/core/asmail/config/index.js +2 -2
  6. package/build/core/asmail/config/published-intro-key.js +1 -1
  7. package/build/core/asmail/delivery/common.js +7 -7
  8. package/build/core/asmail/delivery/index.js +5 -5
  9. package/build/core/asmail/delivery/msg.js +4 -4
  10. package/build/core/asmail/delivery/per-recipient-wip.js +1 -1
  11. package/build/core/asmail/inbox/attachments/fs.js +5 -1
  12. package/build/core/asmail/inbox/cached-msgs.js +1 -1
  13. package/build/core/asmail/inbox/inbox-events.js +4 -4
  14. package/build/core/asmail/inbox/index.js +10 -10
  15. package/build/core/asmail/inbox/msg-downloader.js +1 -1
  16. package/build/core/asmail/inbox/msg-indexing.js +1 -1
  17. package/build/core/asmail/inbox/msg-on-disk.js +5 -5
  18. package/build/core/asmail/index.d.ts +3 -3
  19. package/build/core/asmail/index.js +13 -8
  20. package/build/core/asmail/key-verification.js +5 -5
  21. package/build/core/asmail/keyring/common.js +7 -6
  22. package/build/core/asmail/keyring/correspondent-keys.js +8 -7
  23. package/build/core/asmail/keyring/id-to-email-map.js +2 -1
  24. package/build/core/asmail/keyring/index.d.ts +7 -8
  25. package/build/core/asmail/keyring/index.js +15 -14
  26. package/build/core/asmail/keyring/keyring-storage.js +2 -1
  27. package/build/core/asmail/msg/opener.js +3 -3
  28. package/build/core/asmail/msg/packer.js +13 -13
  29. package/build/core/asmail/sending-params/own-params.js +2 -2
  30. package/build/core/asmail/sending-params/params-from-others.js +1 -1
  31. package/build/core/id-manager.js +6 -3
  32. package/build/core/index.d.ts +2 -1
  33. package/build/core/index.js +14 -14
  34. package/build/core/sign-in.js +5 -5
  35. package/build/core/sign-up.js +9 -9
  36. package/build/core/storage/common/json-saving.js +2 -2
  37. package/build/core/storage/common/obj-info-file.d.ts +12 -4
  38. package/build/core/storage/common/obj-info-file.js +66 -34
  39. package/build/core/storage/common/utils.d.ts +2 -0
  40. package/build/core/storage/common/utils.js +32 -0
  41. package/build/core/storage/index.d.ts +3 -17
  42. package/build/core/storage/index.js +56 -76
  43. package/build/core/storage/local/obj-files-gc.d.ts +2 -0
  44. package/build/core/storage/local/obj-files-gc.js +49 -37
  45. package/build/core/storage/local/obj-files.d.ts +4 -7
  46. package/build/core/storage/local/obj-files.js +7 -10
  47. package/build/core/storage/local/obj-status.d.ts +12 -6
  48. package/build/core/storage/local/obj-status.js +24 -9
  49. package/build/core/storage/local/storage.d.ts +9 -6
  50. package/build/core/storage/local/storage.js +29 -18
  51. package/build/core/storage/synced/downloader.js +1 -1
  52. package/build/core/storage/synced/obj-files-gc.d.ts +5 -1
  53. package/build/core/storage/synced/obj-files-gc.js +91 -37
  54. package/build/core/storage/synced/obj-files.d.ts +42 -36
  55. package/build/core/storage/synced/obj-files.js +178 -147
  56. package/build/core/storage/synced/obj-status.d.ts +87 -85
  57. package/build/core/storage/synced/obj-status.js +463 -259
  58. package/build/core/storage/synced/remote-events.d.ts +11 -12
  59. package/build/core/storage/synced/remote-events.js +73 -56
  60. package/build/core/storage/synced/storage.d.ts +18 -9
  61. package/build/core/storage/synced/storage.js +108 -48
  62. package/build/core/storage/synced/upload-header-file.d.ts +4 -0
  63. package/build/core/storage/synced/upload-header-file.js +64 -0
  64. package/build/core/storage/synced/upsyncer.d.ts +12 -7
  65. package/build/core/storage/synced/upsyncer.js +204 -280
  66. package/build/core/storage/system-folders/apps-data.d.ts +16 -0
  67. package/build/core/storage/system-folders/apps-data.js +110 -0
  68. package/build/core/storage/system-folders/index.d.ts +18 -0
  69. package/build/core/storage/system-folders/index.js +77 -0
  70. package/build/core-ipc/common-caps.js +3 -3
  71. package/build/core-ipc/generic.js +8 -8
  72. package/build/core-ipc/startup-caps.js +2 -2
  73. package/build/cryptors.js +6 -2
  74. package/build/ipc-via-protobuf/asmail-cap.js +58 -57
  75. package/build/ipc-via-protobuf/bytes.js +16 -17
  76. package/build/ipc-via-protobuf/connector-clients-side.d.ts +3 -0
  77. package/build/ipc-via-protobuf/connector-clients-side.js +61 -24
  78. package/build/ipc-via-protobuf/connector-services-side.js +10 -10
  79. package/build/ipc-via-protobuf/connector.js +4 -4
  80. package/build/ipc-via-protobuf/file.d.ts +48 -12
  81. package/build/ipc-via-protobuf/file.js +474 -126
  82. package/build/ipc-via-protobuf/fs.d.ts +8 -0
  83. package/build/ipc-via-protobuf/fs.js +577 -142
  84. package/build/ipc-via-protobuf/log-cap.js +2 -2
  85. package/build/ipc-via-protobuf/mailerid.js +3 -3
  86. package/build/ipc-via-protobuf/protobuf-msg.d.ts +1 -0
  87. package/build/ipc-via-protobuf/protobuf-msg.js +11 -7
  88. package/build/ipc-via-protobuf/startup-cap.js +21 -21
  89. package/build/ipc-via-protobuf/storage-cap.js +12 -12
  90. package/build/ipc.js +7 -2
  91. package/build/lib-client/3nstorage/exceptions.d.ts +3 -0
  92. package/build/lib-client/3nstorage/exceptions.js +13 -1
  93. package/build/lib-client/3nstorage/service.d.ts +15 -2
  94. package/build/lib-client/3nstorage/service.js +104 -38
  95. package/build/lib-client/3nstorage/util/file-based-json.d.ts +2 -1
  96. package/build/lib-client/3nstorage/util/file-based-json.js +1 -1
  97. package/build/lib-client/3nstorage/xsp-fs/attrs.js +17 -17
  98. package/build/lib-client/3nstorage/xsp-fs/common.d.ts +42 -18
  99. package/build/lib-client/3nstorage/xsp-fs/common.js +29 -19
  100. package/build/lib-client/3nstorage/xsp-fs/file-node.d.ts +1 -0
  101. package/build/lib-client/3nstorage/xsp-fs/file-node.js +17 -13
  102. package/build/lib-client/3nstorage/xsp-fs/file.d.ts +31 -6
  103. package/build/lib-client/3nstorage/xsp-fs/file.js +73 -25
  104. package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.js +4 -4
  105. package/build/lib-client/3nstorage/xsp-fs/folder-node.d.ts +24 -11
  106. package/build/lib-client/3nstorage/xsp-fs/folder-node.js +575 -179
  107. package/build/lib-client/3nstorage/xsp-fs/fs.d.ts +35 -4
  108. package/build/lib-client/3nstorage/xsp-fs/fs.js +231 -110
  109. package/build/lib-client/3nstorage/xsp-fs/link-node.d.ts +1 -0
  110. package/build/lib-client/3nstorage/xsp-fs/link-node.js +7 -2
  111. package/build/lib-client/3nstorage/xsp-fs/node-in-fs.d.ts +30 -24
  112. package/build/lib-client/3nstorage/xsp-fs/node-in-fs.js +229 -123
  113. package/build/lib-client/3nstorage/xsp-fs/node-persistence.d.ts +1 -1
  114. package/build/lib-client/3nstorage/xsp-fs/node-persistence.js +17 -18
  115. package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.js +3 -3
  116. package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.js +53 -53
  117. package/build/lib-client/3nweb-signup.js +4 -4
  118. package/build/lib-client/asmail/recipient.js +15 -15
  119. package/build/lib-client/asmail/sender.js +22 -22
  120. package/build/lib-client/asmail/service-config.js +3 -3
  121. package/build/lib-client/cryptor/cryptor-in-worker.js +18 -16
  122. package/build/lib-client/cryptor/cryptor-wasm.js +1 -1
  123. package/build/lib-client/cryptor/cryptor.js +4 -2
  124. package/build/lib-client/cryptor/cryptor.wasm +0 -0
  125. package/build/lib-client/cryptor/in-proc-js.js +1 -1
  126. package/build/lib-client/cryptor/in-proc-wasm.js +6 -6
  127. package/build/lib-client/cryptor/worker-js.js +2 -2
  128. package/build/lib-client/cryptor/worker-wasm.js +2 -2
  129. package/build/lib-client/files-select.js +1 -1
  130. package/build/lib-client/files.d.ts +1 -1
  131. package/build/lib-client/files.js +71 -6
  132. package/build/lib-client/fs-collection.js +1 -1
  133. package/build/lib-client/fs-sync-utils.d.ts +5 -0
  134. package/build/lib-client/fs-sync-utils.js +61 -0
  135. package/build/lib-client/fs-view.d.ts +14 -0
  136. package/build/lib-client/fs-view.js +33 -0
  137. package/build/lib-client/key-derivation.js +1 -1
  138. package/build/lib-client/local-files/dev-file-sink.js +9 -9
  139. package/build/lib-client/local-files/dev-file-src.js +2 -2
  140. package/build/lib-client/local-files/device-fs.d.ts +1 -1
  141. package/build/lib-client/local-files/device-fs.js +56 -54
  142. package/build/lib-client/logging/log-to-file.d.ts +1 -1
  143. package/build/lib-client/logging/log-to-file.js +7 -7
  144. package/build/lib-client/mailer-id/login.js +7 -7
  145. package/build/lib-client/mailer-id/provisioner.js +12 -12
  146. package/build/lib-client/objs-on-disk/file-writing-proc.js +3 -3
  147. package/build/lib-client/objs-on-disk/obj-folders.js +31 -31
  148. package/build/lib-client/objs-on-disk/obj-on-disk.d.ts +13 -2
  149. package/build/lib-client/objs-on-disk/obj-on-disk.js +24 -9
  150. package/build/lib-client/request-utils.d.ts +1 -0
  151. package/build/lib-client/request-utils.js +13 -13
  152. package/build/lib-client/server-events.d.ts +3 -3
  153. package/build/lib-client/server-events.js +9 -8
  154. package/build/lib-client/service-locator.js +10 -10
  155. package/build/lib-client/user-with-mid-session.js +7 -7
  156. package/build/lib-client/user-with-pkl-session.js +25 -25
  157. package/build/lib-client/ws-utils.js +2 -2
  158. package/build/lib-common/async-cryptor-wrap.js +4 -4
  159. package/build/lib-common/async-fs-node.d.ts +5 -3
  160. package/build/lib-common/async-fs-node.js +16 -16
  161. package/build/lib-common/byte-streaming/pipe.js +1 -1
  162. package/build/lib-common/byte-streaming/wrapping.js +13 -13
  163. package/build/lib-common/canonical-address.js +1 -1
  164. package/build/lib-common/exceptions/error.d.ts +1 -0
  165. package/build/lib-common/exceptions/error.js +7 -6
  166. package/build/lib-common/exceptions/file.js +4 -0
  167. package/build/lib-common/ipc/ws-ipc.js +2 -2
  168. package/build/lib-common/mid-sigs-NaCl-Ed.js +14 -14
  169. package/build/lib-common/objs-on-disk/file-layout.d.ts +19 -0
  170. package/build/lib-common/objs-on-disk/file-layout.js +130 -12
  171. package/build/lib-common/objs-on-disk/obj-file.d.ts +13 -2
  172. package/build/lib-common/objs-on-disk/obj-file.js +96 -35
  173. package/build/lib-common/objs-on-disk/utils.d.ts +1 -0
  174. package/build/lib-common/objs-on-disk/utils.js +3 -3
  175. package/build/lib-common/objs-on-disk/v1-obj-file-format.js +14 -14
  176. package/build/lib-common/processes/labelled-exec-pools.d.ts +1 -1
  177. package/build/lib-common/processes/labelled-exec-pools.js +1 -1
  178. package/build/lib-common/processes/pressure.js +2 -2
  179. package/build/lib-common/processes/synced.js +1 -1
  180. package/build/lib-common/processes/timeout.js +2 -2
  181. package/build/lib-common/random-node.js +7 -7
  182. package/build/lib-common/service-api/3nstorage/owner.d.ts +95 -39
  183. package/build/lib-common/service-api/3nstorage/owner.js +82 -40
  184. package/build/lib-common/service-api/asmail/delivery.js +2 -2
  185. package/build/lib-common/service-api/asmail/retrieval.js +1 -1
  186. package/build/lib-common/timed-cache.d.ts +1 -0
  187. package/build/lib-common/timed-non-weak-cache.d.ts +1 -0
  188. package/build/lib-common/timed-non-weak-cache.js +11 -0
  189. package/build/lib-common/utils-for-observables.js +4 -4
  190. package/build/lib-common/weak-cache.d.ts +1 -0
  191. package/build/lib-common/weak-cache.js +12 -1
  192. package/build/lib-index.d.ts +2 -1
  193. package/build/lib-index.js +10 -7
  194. package/build/protos/asmail.proto.js +12955 -7496
  195. package/build/protos/file.proto.js +4867 -2744
  196. package/build/protos/fs.proto.js +9227 -3768
  197. package/package.json +6 -5
  198. package/protos/file.proto +91 -19
  199. package/protos/fs.proto +107 -8
@@ -24,9 +24,11 @@ exports.FolderNode = void 0;
24
24
  const buffer_utils_1 = require("../../../lib-common/buffer-utils");
25
25
  const file_1 = require("../../../lib-common/exceptions/file");
26
26
  const error_1 = require("../../../lib-common/exceptions/error");
27
+ const common_1 = require("./common");
27
28
  const node_in_fs_1 = require("./node-in-fs");
28
29
  const file_node_1 = require("./file-node");
29
30
  const link_node_1 = require("./link-node");
31
+ const exceptions_1 = require("../exceptions");
30
32
  const deferred_1 = require("../../../lib-common/processes/deferred");
31
33
  const json_utils_1 = require("../../../lib-common/json-utils");
32
34
  const xsp_files_1 = require("xsp-files");
@@ -34,9 +36,10 @@ const random = require("../../../lib-common/random-node");
34
36
  const folder_node_serialization_1 = require("./folder-node-serialization");
35
37
  const attrs_1 = require("./attrs");
36
38
  const node_persistence_1 = require("./node-persistence");
39
+ const assert_1 = require("../../../lib-common/assert");
37
40
  function jsonToInfoAndAttrs(json) {
38
41
  const folderInfo = {
39
- nodes: json_utils_1.copy(json.nodes)
42
+ nodes: (0, json_utils_1.copy)(json.nodes)
40
43
  };
41
44
  for (const node of Object.values(folderInfo.nodes)) {
42
45
  node.key = buffer_utils_1.base64.open(node.key);
@@ -51,12 +54,12 @@ class FolderPersistance extends node_persistence_1.NodePersistance {
51
54
  Object.seal(this);
52
55
  }
53
56
  async write(folderInfo, version, attrs, xattrs) {
54
- const bytes = folder_node_serialization_1.serializeFolderInfo(folderInfo);
57
+ const bytes = (0, folder_node_serialization_1.serializeFolderInfo)(folderInfo);
55
58
  return this.writeWhole(bytes, version, attrs, xattrs);
56
59
  }
57
60
  async read(src) {
58
61
  const { content, xattrs, attrs } = await this.readAll(src);
59
- const folderInfo = folder_node_serialization_1.parseFolderInfo(content);
62
+ const folderInfo = (0, folder_node_serialization_1.parseFolderInfo)(content);
60
63
  return { folderInfo, xattrs, attrs: attrs_1.CommonAttrs.fromAttrs(attrs) };
61
64
  }
62
65
  }
@@ -77,19 +80,12 @@ class FolderNode extends node_in_fs_1.NodeInFS {
77
80
  constructor(storage, name, objId, zNonce, version, parentId, key, setNewAttrs) {
78
81
  super(storage, 'folder', name, objId, version, parentId);
79
82
  this.currentState = { nodes: {} };
80
- if (!name && (objId || parentId)) {
81
- throw new Error("Root folder must " +
82
- "have both objId and parent as nulls.");
83
+ if (name === undefined) {
84
+ (0, assert_1.assert)(!objId && !parentId, `Root folder must have both objId and parent as nulls.`);
83
85
  }
84
86
  else if (objId === null) {
85
87
  new Error("Missing objId for non-root folder");
86
88
  }
87
- if (!zNonce) {
88
- if (!objId) {
89
- throw new Error(`Missing object id for folder, when zeroth nonce is not given`);
90
- }
91
- zNonce = xsp_files_1.idToHeaderNonce(objId);
92
- }
93
89
  this.crypto = new FolderPersistance(zNonce, key, storage.cryptor);
94
90
  if (setNewAttrs) {
95
91
  this.attrs = attrs_1.CommonAttrs.makeForTimeNow();
@@ -104,20 +100,21 @@ class FolderNode extends node_in_fs_1.NodeInFS {
104
100
  return rf;
105
101
  }
106
102
  static async rootFromObjBytes(storage, name, objId, src, key) {
107
- let zNonce = undefined;
108
- if (!objId) {
109
- const header = await src.readHeader();
110
- zNonce = xsp_files_1.calculateNonce(header.subarray(0, xsp_files_1.NONCE_LENGTH), -src.version);
111
- }
112
- const rf = await FolderNode.readNodeFromObjBytes(storage, name, objId, zNonce, src, key);
103
+ const rf = await FolderNode.readNodeFromObjBytes(storage, name, objId, src, key);
113
104
  rf.storage.nodes.set(rf);
114
105
  return rf;
115
106
  }
116
- static async readNodeFromObjBytes(storage, name, objId, zNonce, src, key) {
107
+ static async readNodeFromObjBytes(storage, name, objId, src, key) {
108
+ let zNonce;
109
+ if (objId) {
110
+ zNonce = (0, xsp_files_1.idToHeaderNonce)(objId);
111
+ }
112
+ else {
113
+ const header = await src.readHeader();
114
+ zNonce = (0, xsp_files_1.calculateNonce)(header.subarray(0, xsp_files_1.NONCE_LENGTH), -src.version);
115
+ }
117
116
  const rf = new FolderNode(storage, name, objId, zNonce, src.version, undefined, key, false);
118
- const { folderInfo, attrs, xattrs } = await rf.crypto.read(src);
119
- rf.currentState = folderInfo;
120
- rf.setUpdatedParams(src.version, attrs, xattrs);
117
+ await rf.setCurrentStateFrom(src);
121
118
  return rf;
122
119
  }
123
120
  static async rootFromLinkParams(storage, params) {
@@ -132,36 +129,94 @@ class FolderNode extends node_in_fs_1.NodeInFS {
132
129
  existingNode.crypto.compareKey(params.fKey);
133
130
  return existingNode;
134
131
  }
135
- const src = await storage.getObj(params.objId);
132
+ const src = await storage.getObjSrc(params.objId);
136
133
  const key = buffer_utils_1.base64.open(params.fKey);
137
134
  return FolderNode.rootFromObjBytes(storage, params.folderName, params.objId, src, key);
138
135
  }
139
136
  static rootFromJSON(storage, name, folderJson) {
140
- const rf = new FolderNode(storage, name, 'readonly-root', EMPTY_ARR, 0, undefined, undefined, false);
137
+ const rf = new FolderNode(storage, name, 'readonly-root', EMPTY_ARR, 0, undefined, EMPTY_ARR, false);
141
138
  const { folderInfo, attrs, xattrs } = jsonToInfoAndAttrs(folderJson);
142
139
  rf.currentState = folderInfo;
143
140
  rf.setUpdatedParams(0, attrs, xattrs);
144
141
  return rf;
145
142
  }
146
- list() {
147
- const names = Object.keys(this.currentState.nodes);
148
- const lst = new Array(names.length);
149
- for (let i = 0; i < names.length; i += 1) {
150
- const entity = this.currentState.nodes[names[i]];
151
- const info = { name: entity.name };
152
- if (entity.isFolder) {
153
- info.isFolder = true;
154
- }
155
- else if (entity.isFile) {
156
- info.isFile = true;
143
+ async setCurrentStateFrom(src) {
144
+ const { folderInfo, attrs, xattrs } = await this.crypto.read(src);
145
+ this.currentState = folderInfo;
146
+ this.setUpdatedParams(src.version, attrs, xattrs);
147
+ }
148
+ async adoptRemote(opts) {
149
+ const objsToRm = await this.doChange(true, async () => {
150
+ try {
151
+ const adopted = await this.syncedStorage().adoptRemote(this.objId, opts);
152
+ if (!adopted) {
153
+ return;
154
+ }
155
+ const src = await this.storage.getObjSrc(this.objId, adopted);
156
+ const originalState = this.currentState;
157
+ await this.setCurrentStateFrom(src);
158
+ const newState = this.currentState;
159
+ const { renamedNodes, addedNodes, removedNodes } = identifyChanges(originalState.nodes, newState.nodes);
160
+ for (const node of removedNodes) {
161
+ const event = {
162
+ type: 'entry-removal',
163
+ src: 'sync',
164
+ path: this.name,
165
+ newVersion: this.version,
166
+ name: node.name
167
+ };
168
+ this.broadcastEvent(event, false, node.objId);
169
+ }
170
+ for (const node of addedNodes) {
171
+ const event = {
172
+ type: 'entry-addition',
173
+ src: 'sync',
174
+ entry: nodeInfoToListingEntry(node),
175
+ path: this.name,
176
+ newVersion: this.version
177
+ };
178
+ this.broadcastEvent(event, false, node.objId);
179
+ }
180
+ for (const { objId, oldName, newName } of renamedNodes) {
181
+ const event = {
182
+ type: 'entry-renaming',
183
+ src: 'sync',
184
+ oldName,
185
+ newName,
186
+ path: this.name,
187
+ newVersion: this.version
188
+ };
189
+ this.broadcastEvent(event, false, objId);
190
+ }
191
+ return removedNodes;
157
192
  }
158
- else if (entity.isLink) {
159
- info.isLink = true;
193
+ catch (exc) {
194
+ throw (0, common_1.setPathInExc)(exc, this.name);
160
195
  }
161
- lst[i] = info;
196
+ });
197
+ if (!objsToRm) {
198
+ return;
199
+ }
200
+ for (const objToRm of objsToRm) {
201
+ (await this.getOrMakeChildNodeForInfo(objToRm)).removeObj('sync');
162
202
  }
203
+ }
204
+ list() {
205
+ const lst = Object.values(this.currentState.nodes)
206
+ .map(nodeInfoToListingEntry);
163
207
  return { lst, version: this.version };
164
208
  }
209
+ async childExistsInSyncedVersion(childObjId) {
210
+ const status = await this.syncStatus();
211
+ if (status.state === 'synced') {
212
+ return true;
213
+ }
214
+ if (!status.synced) {
215
+ return false;
216
+ }
217
+ // XXX FolderNode.childExistsInSyncedVersion() should implement check in synced version
218
+ return false;
219
+ }
165
220
  listFolders() {
166
221
  return Object.keys(this.currentState.nodes).filter(name => !!this.currentState.nodes[name].isFolder);
167
222
  }
@@ -174,30 +229,12 @@ class FolderNode extends node_in_fs_1.NodeInFS {
174
229
  return;
175
230
  }
176
231
  else {
177
- throw file_1.makeFileException(file_1.Code.notFound, name);
232
+ throw (0, file_1.makeFileException)(file_1.Code.notFound, name);
178
233
  }
179
234
  }
180
235
  hasChild(childName, throwIfMissing = false) {
181
236
  return !!this.getNodeInfo(childName, !throwIfMissing);
182
237
  }
183
- /**
184
- * @param objId
185
- * @return either node (promise for node), or a deferred, which promise has
186
- * been registered under a given id, and, therefore, has to be resolved with
187
- * node.
188
- */
189
- getNodeOrArrangePromise(objId) {
190
- const { node, nodePromise } = this.storage.nodes.getNodeOrPromise(objId);
191
- if (node) {
192
- return { nodeOrPromise: node };
193
- }
194
- if (nodePromise) {
195
- return { nodeOrPromise: nodePromise };
196
- }
197
- const deferred = deferred_1.defer();
198
- this.storage.nodes.setPromise(objId, deferred.promise);
199
- return { deferred };
200
- }
201
238
  async getNode(type, name, undefOnMissing = false) {
202
239
  const childInfo = this.getNodeInfo(name, undefOnMissing);
203
240
  if (!childInfo) {
@@ -205,36 +242,38 @@ class FolderNode extends node_in_fs_1.NodeInFS {
205
242
  }
206
243
  if (type) {
207
244
  if ((type === 'file') && !childInfo.isFile) {
208
- throw file_1.makeFileException(file_1.Code.notFile, childInfo.name);
245
+ throw (0, file_1.makeFileException)(file_1.Code.notFile, childInfo.name);
209
246
  }
210
247
  else if ((type === 'folder') && !childInfo.isFolder) {
211
- throw file_1.makeFileException(file_1.Code.notDirectory, childInfo.name);
248
+ throw (0, file_1.makeFileException)(file_1.Code.notDirectory, childInfo.name);
212
249
  }
213
250
  else if ((type === 'link') && !childInfo.isLink) {
214
- throw file_1.makeFileException(file_1.Code.notLink, childInfo.name);
251
+ throw (0, file_1.makeFileException)(file_1.Code.notLink, childInfo.name);
215
252
  }
216
253
  }
217
- const { nodeOrPromise: child, deferred } = this.getNodeOrArrangePromise(childInfo.objId);
218
- if (child) {
219
- return child;
254
+ return this.getOrMakeChildNodeForInfo(childInfo);
255
+ }
256
+ async getOrMakeChildNodeForInfo(info) {
257
+ const { node, nodePromise } = this.storage.nodes.getNodeOrPromise(info.objId);
258
+ if (node) {
259
+ return node;
260
+ }
261
+ if (nodePromise) {
262
+ return nodePromise;
220
263
  }
264
+ const deferred = (0, deferred_1.defer)();
265
+ this.storage.nodes.setPromise(info.objId, deferred.promise);
221
266
  try {
222
267
  let node;
223
- if (childInfo.isFile) {
224
- node = await file_node_1.FileNode.makeForExisting(this.storage, this.objId, name, childInfo.objId, childInfo.key);
225
- }
226
- else if (childInfo.isFolder) {
227
- const src = await this.storage.getObj(childInfo.objId);
228
- // const f = new FolderNode(
229
- // this.storage, childInfo.name, childInfo.objId, undefined,
230
- // src.version, this.objId, childInfo.key, false);
231
- // const { folderInfo, attrs } = await f.crypto.open(src);
232
- // f.currentState = folderInfo;
233
- // f.attrs = (attrs ? attrs : AttrsHolder.makeReadonlyForFolder(0));
234
- node = await FolderNode.readNodeFromObjBytes(this.storage, childInfo.name, childInfo.objId, undefined, src, childInfo.key);
235
- }
236
- else if (childInfo.isLink) {
237
- node = await link_node_1.LinkNode.makeForExisting(this.storage, this.objId, name, childInfo.objId, childInfo.key);
268
+ if (info.isFile) {
269
+ node = await file_node_1.FileNode.makeForExisting(this.storage, this.objId, info.name, info.objId, info.key);
270
+ }
271
+ else if (info.isFolder) {
272
+ const src = await this.storage.getObjSrc(info.objId);
273
+ node = await FolderNode.readNodeFromObjBytes(this.storage, info.name, info.objId, src, info.key);
274
+ }
275
+ else if (info.isLink) {
276
+ node = await link_node_1.LinkNode.makeForExisting(this.storage, this.objId, info.name, info.objId, info.key);
238
277
  }
239
278
  else {
240
279
  throw new Error(`Unknown type of fs node`);
@@ -245,9 +284,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
245
284
  catch (exc) {
246
285
  deferred.reject(exc);
247
286
  if (exc.objNotFound) {
248
- await this.fixMissingChildAndThrow(exc, childInfo);
287
+ await this.fixMissingChildAndThrow(exc, info);
249
288
  }
250
- throw error_1.errWithCause(exc, `Cannot instantiate ${type} node '${this.name}/${childInfo.name}' from obj ${childInfo.objId}`);
289
+ throw (0, error_1.errWithCause)(exc, `Failed to instantiate fs node '${this.name}/${info.name}'`);
251
290
  }
252
291
  }
253
292
  getFolder(name, undefOnMissing = false) {
@@ -261,16 +300,21 @@ class FolderNode extends node_in_fs_1.NodeInFS {
261
300
  }
262
301
  async fixMissingChildAndThrow(exc, childInfo) {
263
302
  await this.doTransition(async (state, version) => {
303
+ const presentChild = state.nodes[childInfo.name];
304
+ if (!presentChild || (presentChild.objId !== childInfo.objId)) {
305
+ return [];
306
+ }
264
307
  delete state.nodes[childInfo.name];
265
308
  const event = {
266
309
  type: 'entry-removal',
267
310
  path: this.name,
311
+ src: 'local',
268
312
  name: childInfo.name,
269
313
  newVersion: version
270
314
  };
271
- return event;
315
+ return { event, childObjId: childInfo.objId };
272
316
  }).catch(noop);
273
- const fileExc = file_1.makeFileException(file_1.Code.notFound, childInfo.name, exc);
317
+ const fileExc = (0, file_1.makeFileException)(file_1.Code.notFound, childInfo.name, exc);
274
318
  fileExc.inconsistentStateOfFS = true;
275
319
  throw fileExc;
276
320
  }
@@ -289,11 +333,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
289
333
  async performTransition(change) {
290
334
  // start transition and prepare transition state
291
335
  // Note on copy: byte arrays are not cloned
292
- const state = json_utils_1.copy(this.currentState);
336
+ const state = (0, json_utils_1.copy)(this.currentState);
293
337
  const version = this.version + 1;
294
338
  const attrs = this.attrs.copy();
295
339
  // do action within transition state
296
- const event = await change(state, version);
340
+ const changeEvents = await change(state, version);
297
341
  // save transition state
298
342
  const encSub = await this.crypto.write(state, version, attrs, this.xattrs);
299
343
  await this.storage.saveObj(this.objId, version, encSub);
@@ -301,7 +345,15 @@ class FolderNode extends node_in_fs_1.NodeInFS {
301
345
  this.currentState = state;
302
346
  this.setCurrentVersion(version);
303
347
  this.attrs = attrs;
304
- this.broadcastEvent(event);
348
+ if (Array.isArray(changeEvents)) {
349
+ for (const { event, childObjId } of changeEvents) {
350
+ this.broadcastEvent(event, false, childObjId);
351
+ }
352
+ }
353
+ else {
354
+ const { event, childObjId } = changeEvents;
355
+ this.broadcastEvent(event, false, childObjId);
356
+ }
305
357
  }
306
358
  /**
307
359
  * This function only creates folder node, but it doesn't insert it anywhere.
@@ -310,7 +362,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
310
362
  async makeAndSaveNewChildFolderNode(name, changes) {
311
363
  const key = await random.bytes(xsp_files_1.KEY_LENGTH);
312
364
  const childObjId = await this.storage.generateNewObjId();
313
- const node = new FolderNode(this.storage, name, childObjId, undefined, 0, this.objId, key, true);
365
+ const node = new FolderNode(this.storage, name, childObjId, (0, xsp_files_1.idToHeaderNonce)(childObjId), 0, this.objId, key, true);
314
366
  await node.saveFirstVersion(changes).catch((exc) => {
315
367
  if (!exc.objExists) {
316
368
  throw exc;
@@ -369,7 +421,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
369
421
  // do check for concurrent creation of a node
370
422
  if (this.getNodeInfo(name, true)) {
371
423
  if (exclusive) {
372
- throw file_1.makeFileException(file_1.Code.alreadyExists, name);
424
+ throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, name);
373
425
  }
374
426
  else if (type === 'folder') {
375
427
  return (await this.getNode('folder', name));
@@ -405,15 +457,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
405
457
  const event = {
406
458
  type: 'entry-addition',
407
459
  path: this.name,
460
+ src: 'local',
408
461
  newVersion: version,
409
- entry: {
410
- name: node.name,
411
- isFile: (node.type === 'file'),
412
- isFolder: (node.type === 'folder'),
413
- isLink: (node.type === 'link')
414
- }
462
+ entry: nodeToListingEntry(node)
415
463
  };
416
- return event;
464
+ return { event, childObjId: node.objId };
417
465
  });
418
466
  return node;
419
467
  });
@@ -437,15 +485,16 @@ class FolderNode extends node_in_fs_1.NodeInFS {
437
485
  const event = {
438
486
  type: 'entry-removal',
439
487
  path: this.name,
488
+ src: 'local',
440
489
  name: f.name,
441
490
  newVersion: version
442
491
  };
443
- return event;
492
+ return { event, childObjId: f.objId };
444
493
  });
445
494
  // explicitly do not wait on a result of child's delete, cause if it fails
446
495
  // we just get traceable garbage, yet, the rest of a live/non-deleted tree
447
496
  // stays consistent
448
- f.localDelete();
497
+ f.removeObj();
449
498
  }
450
499
  changeChildName(initName, newName) {
451
500
  return this.doTransition(async (state, version) => {
@@ -460,16 +509,17 @@ class FolderNode extends node_in_fs_1.NodeInFS {
460
509
  const event = {
461
510
  type: 'entry-renaming',
462
511
  path: this.name,
512
+ src: 'local',
463
513
  newName,
464
514
  oldName: initName,
465
515
  newVersion: version
466
516
  };
467
- return event;
517
+ return { event, childObjId: child.objId };
468
518
  });
469
519
  }
470
520
  async moveChildTo(childName, dst, nameInDst) {
471
521
  if (dst.hasChild(nameInDst)) {
472
- throw file_1.makeFileException(file_1.Code.alreadyExists, nameInDst);
522
+ throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, nameInDst);
473
523
  }
474
524
  if (dst === this) {
475
525
  // In this case we only need to change child's name
@@ -480,40 +530,38 @@ class FolderNode extends node_in_fs_1.NodeInFS {
480
530
  // we have two transitions here, in this and in dst.
481
531
  const moveLabel = getMoveLabel();
482
532
  await dst.moveChildIn(nameInDst, childJSON, moveLabel);
483
- await this.moveChildOut(childName, moveLabel);
533
+ await this.moveChildOut(childName, childJSON.objId, moveLabel);
484
534
  }
485
535
  }
486
- async moveChildOut(name, moveLabel) {
536
+ async moveChildOut(name, childObjId, moveLabel) {
487
537
  await this.doTransition(async (state, version) => {
538
+ const child = state.nodes[name];
488
539
  delete state.nodes[name];
489
540
  const event = {
490
541
  type: 'entry-removal',
491
542
  path: this.name,
543
+ src: 'local',
492
544
  name,
493
545
  newVersion: version,
494
546
  moveLabel
495
547
  };
496
- return event;
548
+ return { event, childObjId };
497
549
  });
498
550
  }
499
551
  async moveChildIn(newName, child, moveLabel) {
500
- child = json_utils_1.copy(child);
552
+ child = (0, json_utils_1.copy)(child);
501
553
  await this.doTransition(async (state, version) => {
502
554
  child.name = newName;
503
555
  state.nodes[child.name] = child;
504
556
  const event = {
505
557
  type: 'entry-addition',
506
558
  path: this.name,
507
- entry: {
508
- name: child.name,
509
- isFile: child.isFile,
510
- isFolder: child.isFolder,
511
- isLink: child.isLink
512
- },
559
+ src: 'local',
560
+ entry: nodeInfoToListingEntry(child),
513
561
  newVersion: version,
514
562
  moveLabel
515
563
  };
516
- return event;
564
+ return { event, childObjId: child.objId };
517
565
  });
518
566
  }
519
567
  async getFolderInThisSubTree(path, createIfMissing = false, exclusiveCreate = false) {
@@ -526,7 +574,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
526
574
  // existing folder at this point
527
575
  if (path.length === 1) {
528
576
  if (exclusiveCreate) {
529
- throw file_1.makeFileException(file_1.Code.alreadyExists, path[0]);
577
+ throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, path[0]);
530
578
  }
531
579
  else {
532
580
  return f;
@@ -583,10 +631,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
583
631
  }
584
632
  return content;
585
633
  }
586
- async delete(remoteEvent) {
587
- if (remoteEvent) {
588
- return super.delete(true);
589
- }
634
+ async delete(src) {
590
635
  const childrenNodes = await this.getAllNodes();
591
636
  await this.storage.removeObj(this.objId);
592
637
  this.storage.nodes.delete(this);
@@ -595,14 +640,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
595
640
  const event = {
596
641
  type: 'removed',
597
642
  path: this.name,
598
- isRemote: remoteEvent
643
+ src
599
644
  };
600
645
  this.broadcastEvent(event, true);
601
- // explicitly do not wait on a result of child's delete, cause if it fails
602
- // we just get traceable garbage, yet, the rest of a live/non-deleted tree
603
- // stays consistent
604
646
  for (const node of childrenNodes) {
605
- node.localDelete();
647
+ node.removeObj(src);
606
648
  }
607
649
  }
608
650
  getParamsForLink() {
@@ -621,77 +663,214 @@ class FolderNode extends node_in_fs_1.NodeInFS {
621
663
  };
622
664
  return linkParams;
623
665
  }
624
- // XXX make default conflict resolution for folder
625
- // async resolveConflict(remoteVersion: number): Promise<void> {
626
- // // XXX we need to read remote version outside of doChange
627
- // const src = await (this.storage as SyncedStorage).getSyncedObjVersion(
628
- // this.objId, remoteVersion);
629
- // const remInfo = await this.crypto.open(src);
630
- // await this.doChange(true, async () => {
631
- // // XXX this is a default conflict resolution
632
- // // XXX drop all previous local versions(!)
633
- // });
634
- // }
635
- async doOnExternalChange(expectedVersion) {
636
- if (expectedVersion <= this.version) {
666
+ async upload(opts) {
667
+ // XXX temporary use of super's method
668
+ return super.upload(opts);
669
+ // const storage = this.syncedStorage();
670
+ // XXX
671
+ // - use identifyChanges() to find changes, minding particular version,
672
+ // ensuring that concurrent change won't mess things up.
673
+ // - items not present in uploaded version, relative to latest sync,
674
+ // i.e. removed, removal should be uploaded with
675
+ // storage.uploadObjRemoval(objId);
676
+ }
677
+ async needUpload(localVersion) {
678
+ const toUpload = await super.needUpload(localVersion);
679
+ if (!toUpload) {
637
680
  return;
638
681
  }
639
- const src = await this.storage.getObj(this.objId);
640
- const newVersion = src.version;
641
- if (newVersion <= this.version) {
642
- return;
682
+ // XXX can put the following into private ensureAllChildrenUploaded(),
683
+ // but it also need to get a consistent state, while current one can
684
+ // be broken by simultaneous change in folder.
685
+ const storage = this.syncedStorage();
686
+ if (toUpload.localVersion === this.version) {
687
+ for (const { objId, name } of Object.values(this.currentState.nodes)) {
688
+ if ((await storage.status(objId)).neverUploaded()) {
689
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
690
+ childNeverUploaded: true, childName: name
691
+ });
692
+ }
693
+ }
694
+ }
695
+ else {
696
+ throw new Error(`Reading particular version is not implemented, yet`);
697
+ }
698
+ return toUpload;
699
+ }
700
+ async adoptRemoteFolderItem(itemName, opts) {
701
+ if (opts === null || opts === void 0 ? void 0 : opts.localVersion) {
702
+ if (this.version !== opts.localVersion) {
703
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
704
+ versionMismatch: true,
705
+ message: `Given local version ${opts.localVersion} is not equal to current ${this.version}`
706
+ });
707
+ }
708
+ }
709
+ const { remote } = await this.syncStatus();
710
+ if (!remote) {
711
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
712
+ versionMismatch: true,
713
+ message: `No remote versions`
714
+ });
643
715
  }
644
- const { folderInfo, attrs } = await this.crypto.read(src);
645
- const initState = this.currentState;
646
- this.currentState = checkFolderInfo(folderInfo);
647
- this.setCurrentVersion(newVersion);
648
- if (attrs) {
649
- this.attrs = attrs;
650
- }
651
- const addedEntries = Object.keys(this.currentState.nodes)
652
- .filter(name => !initState.nodes[name]);
653
- const removedEntries = Object.keys(initState.nodes)
654
- .filter(name => !this.currentState.nodes[name]);
655
- if ((addedEntries.length === 1) && (removedEntries.length === 1)) {
716
+ if (opts === null || opts === void 0 ? void 0 : opts.remoteVersion) {
717
+ if (opts.remoteVersion !== remote.latest) {
718
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
719
+ versionMismatch: true,
720
+ message: `Unknown remote version ${opts.remoteVersion}`
721
+ });
722
+ }
723
+ }
724
+ else if (!remote.latest) {
725
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
726
+ remoteIsArchived: true
727
+ });
728
+ }
729
+ const storage = this.syncedStorage();
730
+ const srcOfRemote = await storage.getObjSrcOfRemoteVersion(this.objId, remote.latest);
731
+ const { folderInfo: { nodes: remoteNodes } } = await this.crypto.read(srcOfRemote);
732
+ const remoteChildNode = remoteNodes[itemName];
733
+ if (!remoteChildNode) {
734
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
735
+ remoteFolderItemNotFound: true,
736
+ message: `Item '${itemName}' is not found in remote version`
737
+ });
738
+ }
739
+ const localNodeToRm = this.getNodeInfo(itemName, true);
740
+ if (!localNodeToRm) {
741
+ return await this.addRemoteChild(remoteChildNode);
742
+ }
743
+ else if (localNodeToRm.objId === remoteChildNode.objId) {
744
+ return await this.setRemoteNodeInfo(remoteChildNode);
745
+ }
746
+ else {
747
+ return await this.replaceLocalChildWithRemote(localNodeToRm, remoteChildNode);
748
+ }
749
+ }
750
+ async addRemoteChild(remoteChildNode) {
751
+ let newVersion;
752
+ await this.doTransition(async (state, version) => {
753
+ newVersion = version;
754
+ state.nodes[remoteChildNode.name] = remoteChildNode;
656
755
  const event = {
657
- type: 'entry-renaming',
756
+ type: 'entry-addition',
757
+ path: this.name,
758
+ src: 'sync',
759
+ entry: nodeInfoToListingEntry(remoteChildNode),
760
+ newVersion
761
+ };
762
+ return { event, childObjId: remoteChildNode.objId };
763
+ });
764
+ // XXX child should update sync info, and adopt change.
765
+ return newVersion;
766
+ }
767
+ async setRemoteNodeInfo(remoteChildNode) {
768
+ let newVersion;
769
+ await this.doTransition(async (state, version) => {
770
+ newVersion = version;
771
+ state.nodes[remoteChildNode.name] = remoteChildNode;
772
+ return [];
773
+ });
774
+ // XXX child should update sync info, and adopt change, cause changed key
775
+ // requires changed cypher.
776
+ return newVersion;
777
+ }
778
+ async replaceLocalChildWithRemote(origChildNode, remoteChildNode) {
779
+ let newVersion;
780
+ await this.doTransition(async (state, version) => {
781
+ newVersion = version;
782
+ const presentNode = state.nodes[remoteChildNode.name];
783
+ state.nodes[remoteChildNode.name] = remoteChildNode;
784
+ const additionEvent = {
785
+ type: 'entry-addition',
658
786
  path: this.name,
659
- isRemote: true,
660
- oldName: removedEntries[0],
661
- newName: addedEntries[0],
787
+ src: 'sync',
788
+ entry: nodeInfoToListingEntry(remoteChildNode),
662
789
  newVersion
663
790
  };
664
- this.broadcastEvent(event);
791
+ const rmEvent = {
792
+ type: 'entry-removal',
793
+ path: this.name,
794
+ src: 'sync',
795
+ name: origChildNode.name
796
+ };
797
+ return [
798
+ { event: rmEvent, childObjId: origChildNode.objId },
799
+ { event: additionEvent, childObjId: remoteChildNode.objId }
800
+ ];
801
+ });
802
+ // XXX
803
+ // remote child should update sync info, and adopt change.
804
+ // original child:
805
+ // - if only local, should be removed,
806
+ // - else should update sync info, and adopt both removal and change.
807
+ (await this.getOrMakeChildNodeForInfo(origChildNode)).removeObj('sync');
808
+ return newVersion;
809
+ }
810
+ async diffCurrentAndRemote(remoteVersion) {
811
+ var _a;
812
+ const { state, remote } = await this.syncStatus();
813
+ let isCurrentLocal;
814
+ if (state === 'behind') {
815
+ isCurrentLocal = false;
816
+ }
817
+ else if (state === 'conflicting') {
818
+ isCurrentLocal = true;
665
819
  }
666
820
  else {
667
- addedEntries.forEach(name => {
668
- const addedNode = this.currentState.nodes[name];
669
- const event = {
670
- type: 'entry-addition',
671
- path: this.name,
672
- isRemote: true,
673
- entry: {
674
- name: addedNode.name,
675
- isFile: addedNode.isFile,
676
- isFolder: addedNode.isFolder,
677
- isLink: addedNode.isLink
678
- },
679
- newVersion
680
- };
681
- this.broadcastEvent(event);
682
- });
683
- removedEntries.forEach(name => {
684
- const event = {
685
- type: 'entry-removal',
686
- path: this.name,
687
- isRemote: true,
688
- name,
689
- newVersion
690
- };
691
- this.broadcastEvent(event);
692
- });
821
+ return;
822
+ }
823
+ if (remote.isArchived) {
824
+ return this.diffWithArchivedRemote(isCurrentLocal);
825
+ }
826
+ else {
827
+ if (remoteVersion) {
828
+ if ((remoteVersion !== remote.latest)
829
+ && !((_a = remote.archived) === null || _a === void 0 ? void 0 : _a.includes(remoteVersion))) {
830
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
831
+ versionMismatch: true,
832
+ message: `Unknown remote version ${remoteVersion}`
833
+ });
834
+ }
835
+ }
836
+ else {
837
+ remoteVersion = remote.latest;
838
+ }
839
+ const storage = this.syncedStorage();
840
+ const srcOfRemote = await storage.getObjSrcOfRemoteVersion(this.objId, remote.latest);
841
+ const { folderInfo, attrs, xattrs } = await this.crypto.read(srcOfRemote);
842
+ return this.diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs);
693
843
  }
694
844
  }
845
+ diffWithArchivedRemote(isCurrentLocal) {
846
+ return {
847
+ currentVersion: this.version,
848
+ isCurrentLocal,
849
+ isRemoteArchived: true,
850
+ ctime: {
851
+ current: new Date(this.attrs.ctime)
852
+ },
853
+ mtime: {
854
+ current: new Date(this.attrs.ctime)
855
+ }
856
+ };
857
+ }
858
+ diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs) {
859
+ const { ctime, mtime } = diffAttrs(this.attrs, attrs);
860
+ const { inCurrent, inRemote, nameOverlaps } = diffNodes(this.currentState, folderInfo);
861
+ return {
862
+ currentVersion: this.version,
863
+ isCurrentLocal,
864
+ isRemoteArchived: false,
865
+ remoteVersion,
866
+ inCurrent,
867
+ inRemote,
868
+ nameOverlaps,
869
+ ctime,
870
+ mtime,
871
+ xattrs: diffXAttrs(this.xattrs, xattrs)
872
+ };
873
+ }
695
874
  }
696
875
  exports.FolderNode = FolderNode;
697
876
  Object.freeze(FolderNode.prototype);
@@ -702,6 +881,32 @@ function checkFolderInfo(folderJson) {
702
881
  // TODO throw if folderJson is not ok
703
882
  return folderJson;
704
883
  }
884
+ function nodeInfoToListingEntry({ name, isFile, isFolder, isLink }) {
885
+ if (isFolder) {
886
+ return { name, isFolder };
887
+ }
888
+ else if (isFile) {
889
+ return { name, isFile };
890
+ }
891
+ else if (isLink) {
892
+ return { name, isLink };
893
+ }
894
+ else {
895
+ return { name };
896
+ }
897
+ }
898
+ function nodeToListingEntry({ name, type }) {
899
+ switch (type) {
900
+ case 'file':
901
+ return { name, isFolder: true };
902
+ case 'folder':
903
+ return { name, isFolder: true };
904
+ case 'link':
905
+ return { name, isLink: true };
906
+ default:
907
+ return { name };
908
+ }
909
+ }
705
910
  function addToTransitionState(state, f, key) {
706
911
  const nodeInfo = {
707
912
  name: f.name,
@@ -722,4 +927,195 @@ function addToTransitionState(state, f, key) {
722
927
  }
723
928
  state.nodes[nodeInfo.name] = nodeInfo;
724
929
  }
930
+ function diffAttrs(current, remote) {
931
+ return {
932
+ ctime: {
933
+ current: new Date(current.ctime),
934
+ remote: new Date(remote.ctime)
935
+ },
936
+ mtime: {
937
+ current: new Date(current.mtime),
938
+ remote: new Date(remote.mtime)
939
+ }
940
+ };
941
+ }
942
+ function diffNodes(current, remote) {
943
+ const inCurrent = getOnlyNotEqualNodes(current.nodes, remote.nodes);
944
+ const inRemote = getOnlyNotEqualNodes(remote.nodes, current.nodes);
945
+ const combined = combineCheckingNameOverlaps(inCurrent, inRemote);
946
+ return (combined ? combined : {});
947
+ }
948
+ function getOnlyNotEqualNodes(src, exclude) {
949
+ const collected = [];
950
+ for (const [name, nodeInSrc] of Object.entries(src)) {
951
+ const nodeInExclude = exclude[name];
952
+ if (!nodeInExclude
953
+ || !areNodesEqual(nodeInSrc, nodeInExclude)) {
954
+ collected.push(nodeInfoToListingEntry(nodeInSrc));
955
+ }
956
+ }
957
+ return ((collected.length > 0) ? collected : undefined);
958
+ }
959
+ function areNodesEqual(n1, n2) {
960
+ if (n1.name !== n2.name) {
961
+ return false;
962
+ }
963
+ if (n1.isFile && !n2.isFile) {
964
+ return false;
965
+ }
966
+ if (n1.isFolder && !n2.isFolder) {
967
+ return false;
968
+ }
969
+ if (n1.isLink && !n2.isLink) {
970
+ return false;
971
+ }
972
+ if (n1.objId !== n2.objId) {
973
+ return false;
974
+ }
975
+ if (!areBytesEqual(n1.key, n2.key)) {
976
+ return false;
977
+ }
978
+ return true;
979
+ }
980
+ function combineCheckingNameOverlaps(inCurrent, inRemote) {
981
+ if (inCurrent) {
982
+ if (inRemote) {
983
+ const nameOverlaps = [];
984
+ for (const { name: nameInCurrent } of inCurrent) {
985
+ if (inRemote.find(({ name }) => (name === nameInCurrent))) {
986
+ nameOverlaps.push(nameInCurrent);
987
+ }
988
+ }
989
+ if (nameOverlaps.length > 0) {
990
+ return { inCurrent, inRemote, nameOverlaps };
991
+ }
992
+ else {
993
+ return { inCurrent, inRemote };
994
+ }
995
+ }
996
+ else {
997
+ return { inCurrent };
998
+ }
999
+ }
1000
+ else {
1001
+ if (inRemote) {
1002
+ return { inRemote };
1003
+ }
1004
+ else {
1005
+ return;
1006
+ }
1007
+ }
1008
+ }
1009
+ function diffXAttrs(current, remote) {
1010
+ const haveCurrentXAttrs = (current && (current.list().length > 0));
1011
+ const haveRemoteXAttrs = (remote && (remote.list().length > 0));
1012
+ if (haveCurrentXAttrs && haveRemoteXAttrs) {
1013
+ const inCurrent = getOnlyNotEqualXAttrs(current, remote);
1014
+ const inRemote = getOnlyNotEqualXAttrs(remote, current);
1015
+ return combineCheckingNameOverlaps(inCurrent, inRemote);
1016
+ }
1017
+ else if (haveCurrentXAttrs && !haveRemoteXAttrs) {
1018
+ return { inCurrent: allXAttrsToDiff(current) };
1019
+ }
1020
+ else if (!haveCurrentXAttrs && haveRemoteXAttrs) {
1021
+ return { inRemote: allXAttrsToDiff(remote) };
1022
+ }
1023
+ else {
1024
+ return;
1025
+ }
1026
+ }
1027
+ function allXAttrsToDiff(src) {
1028
+ const collected = [];
1029
+ for (const name of src.list()) {
1030
+ const value = src.get(name);
1031
+ if (value !== undefined) {
1032
+ collected.push({ name, value });
1033
+ }
1034
+ }
1035
+ return collected;
1036
+ }
1037
+ function getOnlyNotEqualXAttrs(src, exclude) {
1038
+ const collected = [];
1039
+ for (const name of src.list()) {
1040
+ const valueInSrc = src.get(name);
1041
+ if (valueInSrc === undefined) {
1042
+ continue;
1043
+ }
1044
+ const valueInExclude = exclude.get(name);
1045
+ if ((valueInExclude === undefined)
1046
+ || !areXAttrValuesEqual(valueInSrc, valueInExclude)) {
1047
+ collected.push({ name, value: valueInSrc });
1048
+ }
1049
+ }
1050
+ return ((collected.length > 0) ? collected : undefined);
1051
+ }
1052
+ function areXAttrValuesEqual(v1, v2) {
1053
+ if (Buffer.isBuffer(v1) || ArrayBuffer.isView(v1)) {
1054
+ if (Buffer.isBuffer(v2) || ArrayBuffer.isView(v2)) {
1055
+ return areBytesEqual(v1, v2);
1056
+ }
1057
+ {
1058
+ return false;
1059
+ }
1060
+ }
1061
+ else if (typeof v1 === 'string') {
1062
+ if (typeof v2 === 'string') {
1063
+ return (v1 === v2);
1064
+ }
1065
+ else {
1066
+ return false;
1067
+ }
1068
+ }
1069
+ else {
1070
+ if (Buffer.isBuffer(v2) || ArrayBuffer.isView(v2)
1071
+ || (typeof v2 === 'string')) {
1072
+ return false;
1073
+ }
1074
+ else {
1075
+ return (0, json_utils_1.deepEqual)(v1, v2);
1076
+ }
1077
+ }
1078
+ }
1079
+ function areBytesEqual(b1, b2) {
1080
+ if (b1.length !== b2.length) {
1081
+ return false;
1082
+ }
1083
+ for (let i = 0; i < b1.length; i += 1) {
1084
+ if (b1[i] !== b2[i]) {
1085
+ return false;
1086
+ }
1087
+ }
1088
+ return true;
1089
+ }
1090
+ function identifyChanges(originalNodes, newNodes) {
1091
+ const removedNodes = [];
1092
+ const addedNodes = [];
1093
+ const renamedNodes = [];
1094
+ for (const [name, node] of Object.entries(originalNodes)) {
1095
+ const newNode = newNodes[name];
1096
+ if (newNode && (newNode.objId === node.objId)) {
1097
+ continue;
1098
+ }
1099
+ removedNodes.push(node);
1100
+ }
1101
+ for (const [name, newNode] of Object.entries(newNodes)) {
1102
+ const node = originalNodes[name];
1103
+ if (node && (newNode.objId === node.objId)) {
1104
+ continue;
1105
+ }
1106
+ const indInRm = removedNodes.findIndex(n => (n.objId === newNode.objId));
1107
+ if (indInRm < 0) {
1108
+ addedNodes.push(newNode);
1109
+ }
1110
+ else {
1111
+ const oldNode = removedNodes.splice(indInRm, 1)[0];
1112
+ renamedNodes.push({
1113
+ oldName: oldNode.name,
1114
+ newName: newNode.name,
1115
+ objId: newNode.objId
1116
+ });
1117
+ }
1118
+ }
1119
+ return { addedNodes, removedNodes, renamedNodes };
1120
+ }
725
1121
  Object.freeze(exports);