core-3nweb-client-lib 0.43.16 → 0.43.18

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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 2020, 2022, 2025 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -334,6 +334,10 @@ class FolderNode extends node_in_fs_1.NodeInFS {
334
334
  if (exc.objNotFound && fromCurrentNodes) {
335
335
  await this.fixMissingChildAndThrow(exc, info);
336
336
  }
337
+ else if (exc.failedCipherVerification) {
338
+ exc = (0, file_1.makeFileException)('ioError', `${this.name}/${info.name}`, exc);
339
+ }
340
+ // XXX why and what ?
337
341
  deferred.reject((0, error_1.errWithCause)(exc, `Failed to instantiate fs node '${this.name}/${info.name}'`));
338
342
  return nodeSet;
339
343
  }
@@ -367,6 +371,75 @@ class FolderNode extends node_in_fs_1.NodeInFS {
367
371
  fileExc.inconsistentStateOfFS = true;
368
372
  throw fileExc;
369
373
  }
374
+ async adoptItemsFromRemoteVersion(opts) {
375
+ const { state, remote } = await this.syncStatus();
376
+ if ((state !== 'conflicting') && (state !== 'behind')) {
377
+ return;
378
+ }
379
+ const remoteVersion = versionFromRemoteBranch(remote, opts === null || opts === void 0 ? void 0 : opts.remoteVersion);
380
+ if (!remoteVersion) {
381
+ throw 'something';
382
+ }
383
+ const { folderInfo: remoteState } = await this.readRemoteVersion(remoteVersion);
384
+ return await this.doChangeWhileTrackingVersion(opts === null || opts === void 0 ? void 0 : opts.localVersion, async (state) => {
385
+ const { inRemote, nameOverlaps, differentKeys } = diffNodes(state, remoteState);
386
+ if (differentKeys) {
387
+ throw 'something';
388
+ }
389
+ if (!inRemote) {
390
+ return [];
391
+ }
392
+ const addedNodes = new Set();
393
+ if (nameOverlaps) {
394
+ const postfix = opts === null || opts === void 0 ? void 0 : opts.postfixForNameOverlaps;
395
+ if ((postfix === undefined) || (postfix.length < 0)) {
396
+ throw 'something';
397
+ }
398
+ for (const itemName of nameOverlaps) {
399
+ const node = remoteState.nodes[itemName];
400
+ let newName = `${itemName}${postfix}`;
401
+ while (state.nodes[newName]) {
402
+ newName = `${newName}${postfix}`;
403
+ }
404
+ node.name = newName;
405
+ state[newName] = node;
406
+ addedNodes.add(itemName);
407
+ }
408
+ }
409
+ for (const { name: itemName } of inRemote) {
410
+ if (addedNodes.has(itemName)) {
411
+ continue;
412
+ }
413
+ const node = remoteState.nodes[itemName];
414
+ state[itemName] = node;
415
+ }
416
+ return [];
417
+ });
418
+ }
419
+ /**
420
+ * If no initial local version given, then this performs like doTransition(change) method.
421
+ * When initial local version is given and check, exceptions are thrown if versions mismatch,
422
+ * or when there is another concurrent process that is already transitioning folder state to new state.
423
+ * This returns value of new local version.
424
+ * @param initLocalVersion
425
+ * @param change
426
+ */
427
+ async doChangeWhileTrackingVersion(initLocalVersion, change) {
428
+ if (initLocalVersion) {
429
+ if (this.version === initLocalVersion) {
430
+ return await this.doChange(false, () => this.performTransition(change));
431
+ }
432
+ else {
433
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
434
+ versionMismatch: true,
435
+ message: `Given local version ${initLocalVersion} is not equal to current ${this.version}`
436
+ });
437
+ }
438
+ }
439
+ else {
440
+ return await this.doChange(true, () => this.performTransition(change));
441
+ }
442
+ }
370
443
  /**
371
444
  * This method prepares a transition state, runs given action, and completes
372
445
  * transition to a new version.
@@ -403,6 +476,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
403
476
  const { event, childObjId } = changeEvents;
404
477
  this.broadcastEvent(event, false, childObjId);
405
478
  }
479
+ return version;
406
480
  }
407
481
  /**
408
482
  * This function only creates folder node, but it doesn't insert it anywhere.
@@ -891,14 +965,6 @@ class FolderNode extends node_in_fs_1.NodeInFS {
891
965
  return removedNodes;
892
966
  }
893
967
  async adoptRemoteFolderItem(remoteItemName, opts) {
894
- if (opts === null || opts === void 0 ? void 0 : opts.localVersion) {
895
- if (this.version !== opts.localVersion) {
896
- throw (0, exceptions_1.makeFSSyncException)(this.name, {
897
- versionMismatch: true,
898
- message: `Given local version ${opts.localVersion} is not equal to current ${this.version}`
899
- });
900
- }
901
- }
902
968
  const remoteChildNode = await this.getRemoteChildNodeInfo(remoteItemName, opts === null || opts === void 0 ? void 0 : opts.remoteVersion);
903
969
  let dstItemName = remoteItemName;
904
970
  if (typeof (opts === null || opts === void 0 ? void 0 : opts.newItemName) === 'string') {
@@ -909,15 +975,14 @@ class FolderNode extends node_in_fs_1.NodeInFS {
909
975
  }
910
976
  const localNodeWithSameName = this.getCurrentNodeInfo(dstItemName, true);
911
977
  if (!localNodeWithSameName) {
912
- return await this.addRemoteChild(remoteChildNode, dstItemName);
978
+ return await this.addRemoteChild(remoteChildNode, dstItemName, opts === null || opts === void 0 ? void 0 : opts.localVersion);
913
979
  }
914
980
  else if (localNodeWithSameName.objId === remoteChildNode.objId) {
915
- (0, assert_1.assert)(typeOfNode(localNodeWithSameName) === typeOfNode(remoteChildNode));
916
981
  // XXX
917
982
  throw new Error(`Adaptation of changing keys needs implementation`);
918
983
  }
919
984
  else if (opts === null || opts === void 0 ? void 0 : opts.replaceLocalItem) {
920
- return await this.replaceLocalChildWithRemote(remoteChildNode);
985
+ return await this.replaceLocalChildWithRemote(remoteChildNode, opts === null || opts === void 0 ? void 0 : opts.localVersion);
921
986
  }
922
987
  else {
923
988
  throw (0, exceptions_1.makeFSSyncException)(this.name, {
@@ -961,10 +1026,8 @@ class FolderNode extends node_in_fs_1.NodeInFS {
961
1026
  });
962
1027
  }
963
1028
  }
964
- async addRemoteChild(remoteChildNode, childName) {
965
- let newVersion;
966
- await this.doTransition(async (state, version) => {
967
- newVersion = version;
1029
+ addRemoteChild(remoteChildNode, childName, initLocalVersion) {
1030
+ return this.doChangeWhileTrackingVersion(initLocalVersion, async (state, newVersion) => {
968
1031
  remoteChildNode.name = childName;
969
1032
  state.nodes[remoteChildNode.name] = remoteChildNode;
970
1033
  const event = {
@@ -976,13 +1039,10 @@ class FolderNode extends node_in_fs_1.NodeInFS {
976
1039
  };
977
1040
  return { event, childObjId: remoteChildNode.objId };
978
1041
  });
979
- return newVersion;
980
1042
  }
981
- async replaceLocalChildWithRemote(remoteChildNode) {
1043
+ async replaceLocalChildWithRemote(remoteChildNode, initLocalVersion) {
982
1044
  let origChildNode;
983
- let newVersion;
984
- await this.doTransition(async (state, version) => {
985
- newVersion = version;
1045
+ const newVersion = await this.doChangeWhileTrackingVersion(initLocalVersion, async (state, newVersion) => {
986
1046
  origChildNode = state.nodes[remoteChildNode.name];
987
1047
  state.nodes[remoteChildNode.name] = remoteChildNode;
988
1048
  const additionEvent = {
@@ -1008,8 +1068,12 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1008
1068
  this.uploadRemovalOfObjs(removalsToUpload);
1009
1069
  return newVersion;
1010
1070
  }
1071
+ async readRemoteVersion(remoteVersion) {
1072
+ const storage = this.syncedStorage();
1073
+ const srcOfRemote = await storage.getObjSrcOfRemoteVersion(this.objId, remoteVersion);
1074
+ return await this.crypto.read(srcOfRemote);
1075
+ }
1011
1076
  async diffCurrentAndRemote(remoteVersion) {
1012
- var _a;
1013
1077
  const { state, remote } = await this.syncStatus();
1014
1078
  let isCurrentLocal;
1015
1079
  if (state === 'behind') {
@@ -1021,26 +1085,13 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1021
1085
  else {
1022
1086
  return;
1023
1087
  }
1024
- if (remote.isArchived) {
1025
- return this.diffWithArchivedRemote(isCurrentLocal);
1088
+ remoteVersion = versionFromRemoteBranch(remote, remoteVersion);
1089
+ if (remoteVersion) {
1090
+ const { folderInfo, attrs, xattrs } = await this.readRemoteVersion(remoteVersion);
1091
+ return this.diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs);
1026
1092
  }
1027
1093
  else {
1028
- if (remoteVersion) {
1029
- if ((remoteVersion !== remote.latest)
1030
- && !((_a = remote.archived) === null || _a === void 0 ? void 0 : _a.includes(remoteVersion))) {
1031
- throw (0, exceptions_1.makeFSSyncException)(this.name, {
1032
- versionMismatch: true,
1033
- message: `Unknown remote version ${remoteVersion}`
1034
- });
1035
- }
1036
- }
1037
- else {
1038
- remoteVersion = remote.latest;
1039
- }
1040
- const storage = this.syncedStorage();
1041
- const srcOfRemote = await storage.getObjSrcOfRemoteVersion(this.objId, remote.latest);
1042
- const { folderInfo, attrs, xattrs } = await this.crypto.read(srcOfRemote);
1043
- return this.diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs);
1094
+ return this.diffWithArchivedRemote(isCurrentLocal);
1044
1095
  }
1045
1096
  }
1046
1097
  diffWithArchivedRemote(isCurrentLocal) {
@@ -1076,6 +1127,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1076
1127
  const remoteChildNodeInfo = await this.getRemoteChildNodeInfo(remoteItemName, remoteVersion);
1077
1128
  return await this.getOrMakeChildNodeForInfo(remoteChildNodeInfo, false);
1078
1129
  }
1130
+ getStorage() {
1131
+ return this.storage;
1132
+ }
1079
1133
  }
1080
1134
  exports.FolderNode = FolderNode;
1081
1135
  Object.freeze(FolderNode.prototype);
@@ -1112,20 +1166,6 @@ function nodeToListingEntry({ name, type }) {
1112
1166
  return { name };
1113
1167
  }
1114
1168
  }
1115
- function typeOfNode(nodeInfo) {
1116
- if (nodeInfo.isFolder) {
1117
- return 'folder';
1118
- }
1119
- else if (nodeInfo.isFile) {
1120
- return 'file';
1121
- }
1122
- else if (nodeInfo.isLink) {
1123
- return 'link';
1124
- }
1125
- else {
1126
- throw new Error(`Unknown type of node info`);
1127
- }
1128
- }
1129
1169
  function addToTransitionState(state, f, key) {
1130
1170
  const nodeInfo = {
1131
1171
  name: f.name,
@@ -1159,42 +1199,46 @@ function diffAttrs(current, remote) {
1159
1199
  };
1160
1200
  }
1161
1201
  function diffNodes(current, remote) {
1162
- const inCurrent = getOnlyNotEqualNodes(current.nodes, remote.nodes);
1163
- const inRemote = getOnlyNotEqualNodes(remote.nodes, current.nodes);
1164
- const combined = combineCheckingNameOverlaps(inCurrent, inRemote);
1165
- return (combined ? combined : {});
1166
- }
1167
- function getOnlyNotEqualNodes(src, exclude) {
1168
- const collected = [];
1169
- for (const [name, nodeInSrc] of Object.entries(src)) {
1170
- const nodeInExclude = exclude[name];
1171
- if (!nodeInExclude
1172
- || !areNodesEqual(nodeInSrc, nodeInExclude)) {
1173
- collected.push(nodeInfoToListingEntry(nodeInSrc));
1202
+ const inRemote = [];
1203
+ const nodesInCurrentById = new Map();
1204
+ for (const node of Object.values(current.nodes)) {
1205
+ nodesInCurrentById.set(node.objId, node);
1206
+ }
1207
+ const nameOverlaps = [];
1208
+ const differentNames = [];
1209
+ const differentKeys = [];
1210
+ for (const remoteNode of Object.values(remote.nodes)) {
1211
+ const localNode = nodesInCurrentById.get(remoteNode.objId);
1212
+ if (localNode) {
1213
+ if (localNode.name !== remoteNode.name) {
1214
+ differentNames.push({
1215
+ localName: localNode.name,
1216
+ remoteName: remoteNode.name
1217
+ });
1218
+ }
1219
+ if (!areBytesEqual(localNode.key, remoteNode.key)) {
1220
+ differentKeys.push(localNode.name);
1221
+ }
1222
+ nodesInCurrentById.delete(remoteNode.objId);
1223
+ }
1224
+ else {
1225
+ inRemote.push(nodeInfoToListingEntry(remoteNode));
1226
+ if (current.nodes[remoteNode.name]) {
1227
+ nameOverlaps.push(remoteNode.name);
1228
+ }
1174
1229
  }
1175
1230
  }
1176
- return ((collected.length > 0) ? collected : undefined);
1177
- }
1178
- function areNodesEqual(n1, n2) {
1179
- if (n1.name !== n2.name) {
1180
- return false;
1181
- }
1182
- if (n1.isFile && !n2.isFile) {
1183
- return false;
1184
- }
1185
- if (n1.isFolder && !n2.isFolder) {
1186
- return false;
1187
- }
1188
- if (n1.isLink && !n2.isLink) {
1189
- return false;
1190
- }
1191
- if (n1.objId !== n2.objId) {
1192
- return false;
1193
- }
1194
- if (!areBytesEqual(n1.key, n2.key)) {
1195
- return false;
1231
+ const inCurrent = [];
1232
+ for (const localNode of nodesInCurrentById.values()) {
1233
+ inCurrent.push(nodeInfoToListingEntry(localNode));
1196
1234
  }
1197
- return true;
1235
+ return {
1236
+ inCurrent: ((inCurrent.length > 0) ? inCurrent : undefined),
1237
+ inRemote: ((inRemote.length > 0) ? inRemote : undefined),
1238
+ differentNames: ((differentNames.length > 0) ? differentNames : undefined),
1239
+ nameOverlaps: ((nameOverlaps.length > 0) ? nameOverlaps : undefined),
1240
+ differentKeys: ((differentKeys.length > 0) ? differentKeys : undefined)
1241
+ };
1198
1242
  }
1199
1243
  function combineCheckingNameOverlaps(inCurrent, inRemote) {
1200
1244
  if (inCurrent) {
@@ -1337,4 +1381,22 @@ function identifyChanges(originalNodes, newNodes) {
1337
1381
  }
1338
1382
  return { addedNodes, removedNodes, renamedNodes };
1339
1383
  }
1384
+ function versionFromRemoteBranch(remote, remoteVersion) {
1385
+ var _a;
1386
+ if (remote.isArchived) {
1387
+ return;
1388
+ }
1389
+ if (remoteVersion) {
1390
+ if ((remoteVersion !== remote.latest)
1391
+ && !((_a = remote.archived) === null || _a === void 0 ? void 0 : _a.includes(remoteVersion))) {
1392
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
1393
+ versionMismatch: true,
1394
+ message: `Unknown remote version ${remoteVersion}`
1395
+ });
1396
+ }
1397
+ }
1398
+ else {
1399
+ return remote.latest;
1400
+ }
1401
+ }
1340
1402
  Object.freeze(exports);
@@ -28,6 +28,7 @@ type SyncStatus = web3n.files.SyncStatus;
28
28
  type WritableFSVersionedAPI = web3n.files.WritableFSVersionedAPI;
29
29
  type OptionsToAdopteRemote = web3n.files.OptionsToAdopteRemote;
30
30
  type OptionsToAdoptRemoteItem = web3n.files.OptionsToAdoptRemoteItem;
31
+ type OptionsToAdoptAllRemoteItems = web3n.files.OptionsToAdoptAllRemoteItems;
31
32
  type OptionsToUploadLocal = web3n.files.OptionsToUploadLocal;
32
33
  type FolderDiff = web3n.files.FolderDiff;
33
34
  export declare class XspFS implements WritableFS {
@@ -56,6 +57,7 @@ export declare class XspFS implements WritableFS {
56
57
  */
57
58
  static fromExistingRoot(storage: Storage, key: Uint8Array): Promise<WritableFS>;
58
59
  static fromASMailMsgRootFromJSON(storage: Storage, folderJson: FolderInJSON, rootName?: string): ReadonlyFS;
60
+ static fromFolderNode(folderNode: FolderNode): ReadonlyFS;
59
61
  /**
60
62
  * Note that this method doesn't close storage.
61
63
  */
@@ -180,5 +182,7 @@ declare class S implements WritableFSSyncAPI {
180
182
  statRemoteItem(path: string, remoteItemName: string, remoteVersion?: number): Promise<Stats>;
181
183
  listRemoteFolderItem(path: string, remoteItemName: string, remoteVersion?: number): Promise<ListingEntry[]>;
182
184
  getRemoteFileItem(path: string, remoteItemName: string, remoteVersion?: number): Promise<ReadonlyFile>;
185
+ getRemoteFolderItem(path: string, remoteItemName: string, remoteVersion?: number): Promise<ReadonlyFS>;
186
+ adoptAllRemoteItems(path: string, opts?: OptionsToAdoptAllRemoteItems): Promise<number | undefined>;
183
187
  }
184
188
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 2020, 2022, 2025 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -77,8 +77,7 @@ class XspFS {
77
77
  const pathParts = splitPathIntoParts(path);
78
78
  const root = this.v.getRootIfNotClosed(path);
79
79
  const folder = await root.getFolderInThisSubTree(pathParts, false).catch(setExcPath(path));
80
- const folderName = ((pathParts.length === 0) ?
81
- this.name : pathParts[pathParts.length - 1]);
80
+ const folderName = ((pathParts.length === 0) ? this.name : pathParts[pathParts.length - 1]);
82
81
  const fs = new XspFS(this.storage(), false, folder, folderName);
83
82
  return (0, files_1.wrapReadonlyFS)(fs);
84
83
  }
@@ -86,8 +85,7 @@ class XspFS {
86
85
  const pathParts = splitPathIntoParts(path);
87
86
  const root = this.v.getRootIfNotClosed(path);
88
87
  const folder = await root.getFolderInThisSubTree(pathParts, flags.create, flags.exclusive).catch(setExcPath(path));
89
- const folderName = ((pathParts.length === 0) ?
90
- this.name : pathParts[pathParts.length - 1]);
88
+ const folderName = ((pathParts.length === 0) ? this.name : pathParts[pathParts.length - 1]);
91
89
  const fs = new XspFS(this.storage(), true, folder, folderName);
92
90
  return (0, files_1.wrapWritableFS)(fs);
93
91
  }
@@ -119,6 +117,10 @@ class XspFS {
119
117
  const fs = new XspFS(storage, false, root, rootName);
120
118
  return (0, files_1.wrapIntoVersionlessReadonlyFS)(fs);
121
119
  }
120
+ static fromFolderNode(folderNode) {
121
+ const fs = new XspFS(folderNode.getStorage(), false, folderNode, folderNode.name);
122
+ return (0, files_1.wrapIntoVersionlessReadonlyFS)(fs);
123
+ }
122
124
  /**
123
125
  * Note that this method doesn't close storage.
124
126
  */
@@ -752,14 +754,12 @@ class S {
752
754
  }
753
755
  async updateStatusInfo(path) {
754
756
  const node = await this.n.get(path);
755
- const status = await node.updateStatusInfo();
756
- return status;
757
+ return await node.updateStatusInfo();
757
758
  }
758
759
  async isRemoteVersionOnDisk(path, version) {
759
760
  const node = await this.n.get(path);
760
761
  try {
761
- const isOnDisk = await node.isSyncedVersionOnDisk(version);
762
- return isOnDisk;
762
+ return await node.isSyncedVersionOnDisk(version);
763
763
  }
764
764
  catch (exc) {
765
765
  throw (0, common_1.setPathInExc)(exc, path);
@@ -793,8 +793,7 @@ class S {
793
793
  async diffCurrentAndRemoteFolderVersions(path, remoteVersion) {
794
794
  const node = await this.getFolderNode(path);
795
795
  try {
796
- const diff = await node.diffCurrentAndRemote(remoteVersion);
797
- return diff;
796
+ return await node.diffCurrentAndRemote(remoteVersion);
798
797
  }
799
798
  catch (exc) {
800
799
  throw (0, common_1.setPathInExc)(exc, path);
@@ -803,8 +802,7 @@ class S {
803
802
  async adoptRemoteFolderItem(path, itemName, opts) {
804
803
  const node = await this.getFolderNode(path);
805
804
  try {
806
- const newVersion = await node.adoptRemoteFolderItem(itemName, opts);
807
- return newVersion;
805
+ return await node.adoptRemoteFolderItem(itemName, opts);
808
806
  }
809
807
  catch (exc) {
810
808
  throw (0, common_1.setPathInExc)(exc, path);
@@ -835,6 +833,20 @@ class S {
835
833
  throw (0, file_1.makeFileException)('notFile', `${path}/${remoteItemName}`);
836
834
  }
837
835
  }
836
+ async getRemoteFolderItem(path, remoteItemName, remoteVersion) {
837
+ const folderNode = await this.getFolderNode(path);
838
+ const remoteChild = await folderNode.getRemoteItemNode(remoteItemName, remoteVersion);
839
+ if (remoteChild.type === 'folder') {
840
+ return XspFS.fromFolderNode(remoteChild);
841
+ }
842
+ else {
843
+ throw (0, file_1.makeFileException)('notDirectory', `${path}/${remoteItemName}`);
844
+ }
845
+ }
846
+ async adoptAllRemoteItems(path, opts) {
847
+ const folderNode = await this.getFolderNode(path);
848
+ return await folderNode.adoptItemsFromRemoteVersion(opts);
849
+ }
838
850
  }
839
851
  Object.freeze(S.prototype);
840
852
  Object.freeze(S);