core-3nweb-client-lib 0.49.5 → 0.49.7

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 (81) hide show
  1. package/build/api-defs/files.d.ts +11 -5
  2. package/build/core/asmail/delivery/common.d.ts +8 -28
  3. package/build/core/asmail/delivery/common.js +1 -47
  4. package/build/core/asmail/delivery/index.js +3 -8
  5. package/build/core/asmail/delivery/msg.js +1 -7
  6. package/build/core/asmail/delivery/per-recipient-wip.js +3 -29
  7. package/build/core/asmail/inbox/index.d.ts +1 -2
  8. package/build/core/asmail/inbox/index.js +14 -15
  9. package/build/core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.d.ts +11 -0
  10. package/build/core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.js +25 -0
  11. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.d.ts +33 -0
  12. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.js +72 -0
  13. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.d.ts +28 -0
  14. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.js +477 -0
  15. package/build/core/asmail/inbox/msg-indexing/index.d.ts +12 -26
  16. package/build/core/asmail/inbox/msg-indexing/index.js +47 -68
  17. package/build/core/asmail/inbox/msg-on-disk.js +1 -2
  18. package/build/core/asmail/index.js +8 -7
  19. package/build/core/asmail/msg/common.d.ts +0 -1
  20. package/build/core/asmail/msg/common.js +1 -1
  21. package/build/core/asmail/msg/packer.js +1 -3
  22. package/build/core/index.js +7 -4
  23. package/build/core/keyring/common.d.ts +1 -0
  24. package/build/core/keyring/common.js +2 -1
  25. package/build/core/keyring/index.d.ts +7 -44
  26. package/build/core/keyring/index.js +89 -222
  27. package/build/core/keyring/introductory-keys/published-intro-key.d.ts +17 -0
  28. package/build/core/keyring/introductory-keys/published-intro-key.js +190 -0
  29. package/build/core/keyring/peer-keys/dataset/v1/json-files.d.ts +105 -0
  30. package/build/core/keyring/peer-keys/dataset/v1/json-files.js +89 -0
  31. package/build/core/keyring/peer-keys/dataset/v2/logs.d.ts +43 -0
  32. package/build/core/keyring/peer-keys/dataset/v2/logs.js +116 -0
  33. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.d.ts +73 -0
  34. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.js +543 -0
  35. package/build/core/keyring/peer-keys/index.d.ts +24 -0
  36. package/build/core/keyring/peer-keys/index.js +208 -0
  37. package/build/core/storage/synced/obj-files.js +10 -3
  38. package/build/core/storage/synced/obj-status.d.ts +1 -1
  39. package/build/core/storage/synced/obj-status.js +8 -2
  40. package/build/core/storage/synced/storage.d.ts +4 -4
  41. package/build/core/storage/system-folders/apps-data.d.ts +0 -1
  42. package/build/core/storage/system-folders/apps-data.js +2 -6
  43. package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
  44. package/build/lib-client/fs-utils/fs-sync-utils.js +70 -13
  45. package/build/lib-client/xsp-fs/fs.js +3 -1
  46. package/build/lib-client/xsp-fs/node-in-fs.d.ts +1 -0
  47. package/build/lib-client/xsp-fs/node-in-fs.js +7 -2
  48. package/build/lib-client/xsp-fs/node-persistence.js +1 -3
  49. package/build/lib-client/xsp-fs/xsp-payload-v2.js +1 -2
  50. package/build/lib-common/dataset-sync/per-device-json-array-logs.d.ts +58 -0
  51. package/build/lib-common/dataset-sync/per-device-json-array-logs.js +432 -0
  52. package/build/lib-common/number-line.d.ts +10 -0
  53. package/build/lib-common/number-line.js +66 -0
  54. package/build/lib-common/objs-on-disk/obj-version-file.d.ts +1 -1
  55. package/build/lib-common/objs-on-disk/obj-version-file.js +3 -3
  56. package/build/lib-common/rng-def.d.ts +1 -0
  57. package/build/lib-common/rng-def.js +8 -0
  58. package/build/lib-sqlite-on-3nstorage/for-sqlite.d.ts +38 -0
  59. package/build/lib-sqlite-on-3nstorage/for-sqlite.js +162 -0
  60. package/build/lib-sqlite-on-3nstorage/index.d.ts +18 -5
  61. package/build/lib-sqlite-on-3nstorage/index.js +49 -26
  62. package/build/tests/caps-api/file-sink-checks/splice.js +72 -1
  63. package/build/tests/caps-api/fs-checks/sync-on-one-dev/upload.js +27 -1
  64. package/build/tests/caps-api/fs-checks/sync-with-two-devs/update-propagation.js +12 -3
  65. package/build/tests/computer.3nweb.core/inbox/msg-indexing.js +16 -12
  66. package/build/tests/computer.3nweb.core/keyrings.js +113 -28
  67. package/build/tests/jasmine.js +2 -2
  68. package/build/tests/units/number-line.js +9 -9
  69. package/package.json +2 -1
  70. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.d.ts +0 -52
  71. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.js +0 -333
  72. package/build/core/asmail/inbox/msg-indexing/sql-indexing.d.ts +0 -4
  73. package/build/core/asmail/inbox/msg-indexing/sql-indexing.js +0 -319
  74. package/build/core/keyring/correspondent-keys.d.ts +0 -149
  75. package/build/core/keyring/correspondent-keys.js +0 -403
  76. package/build/core/keyring/id-to-email-map.d.ts +0 -38
  77. package/build/core/keyring/id-to-email-map.js +0 -105
  78. package/build/core/keyring/keyring-storage.d.ts +0 -9
  79. package/build/core/keyring/keyring-storage.js +0 -54
  80. package/build/core/keyring/published-intro-key.d.ts +0 -45
  81. package/build/core/keyring/published-intro-key.js +0 -200
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (C) 2025 3NSoft Inc.
4
+
5
+ This program is free software: you can redistribute it and/or modify it under
6
+ the terms of the GNU General Public License as published by the Free Software
7
+ Foundation, either version 3 of the License, or (at your option) any later
8
+ version.
9
+
10
+ This program is distributed in the hope that it will be useful, but
11
+ WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
+ See the GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License along with
16
+ this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.optStringAsEmptyTransform = exports.booleanTransform = exports.jsonTransform = exports.optJsonTransform = void 0;
20
+ exports.queryParamsFrom = queryParamsFrom;
21
+ exports.fromQueryResult = fromQueryResult;
22
+ exports.queryParamsFromComplete = queryParamsFromComplete;
23
+ exports.forTableInsert = forTableInsert;
24
+ exports.performTableInsert = performTableInsert;
25
+ exports.selectFromTable = selectFromTable;
26
+ exports.updateInTable = updateInTable;
27
+ exports.setExprFor = setExprFor;
28
+ exports.andEqualExprFor = andEqualExprFor;
29
+ exports.passOmitting = passOmitting;
30
+ /* eslint-disable @typescript-eslint/no-explicit-any */
31
+ // @deno-types="../../shared-libs/sqlite-on-3nstorage/index.d.ts"
32
+ // @deno-types="../../shared-libs/sqlite-on-3nstorage/sqljs.d.ts"
33
+ const index_js_1 = require("./index.js");
34
+ function queryParamsFrom(record, transforms, omit) {
35
+ const params = {};
36
+ for (const field of Object.keys(record)) {
37
+ if (omit === null || omit === void 0 ? void 0 : omit.includes(field)) {
38
+ continue;
39
+ }
40
+ const value = record[field];
41
+ const transform = transforms[field];
42
+ if (!transform) {
43
+ throw new Error(`Field ${field} is not present in TransformDefinition object`);
44
+ }
45
+ else if (transform === 'as-is') {
46
+ params[`$${field}`] = value;
47
+ }
48
+ else {
49
+ const { toSQLValue } = transform;
50
+ params[`$${field}`] = toSQLValue(value);
51
+ }
52
+ }
53
+ return params;
54
+ }
55
+ function fromQueryResult(queryResult, transforms) {
56
+ const records = (0, index_js_1.objectFromQueryExecResult)(queryResult);
57
+ const objs = [];
58
+ for (const record of records) {
59
+ const obj = {};
60
+ for (const field of Object.keys(record)) {
61
+ const transform = transforms[field];
62
+ if (!transform || (transform === 'as-is')) {
63
+ obj[field] = record[field];
64
+ }
65
+ else {
66
+ const { fromSQLValue } = transform;
67
+ obj[field] = fromSQLValue(record[field]);
68
+ }
69
+ }
70
+ objs.push(obj);
71
+ }
72
+ return objs;
73
+ }
74
+ function queryParamsFromComplete(record, transforms) {
75
+ const params = queryParamsFrom(record, transforms);
76
+ if (Object.keys(params).length < Object.keys(transforms).length) {
77
+ throw new Error(`Not all fields are present in the record`);
78
+ }
79
+ return params;
80
+ }
81
+ function forTableInsert(record, transforms) {
82
+ const insertParams = queryParamsFromComplete(record, transforms);
83
+ const columns = [];
84
+ const values = [];
85
+ for (const field of Object.keys(record)) {
86
+ const paramField = `$${field}`;
87
+ if (insertParams[paramField] === undefined) {
88
+ throw new Error(`Mismatch in expected parameters: ${paramField} is missing`);
89
+ }
90
+ else {
91
+ columns.push(field);
92
+ values.push(paramField);
93
+ }
94
+ }
95
+ return {
96
+ insertParams,
97
+ orderedColumns: columns.join(', '),
98
+ orderedValues: values.join(', ')
99
+ };
100
+ }
101
+ function performTableInsert(db, tabName, record, transforms) {
102
+ const { insertParams, orderedColumns, orderedValues } = forTableInsert(record, transforms);
103
+ db.exec(`INSERT INTO ${tabName} (${orderedColumns}) VALUES (${orderedValues})`, insertParams);
104
+ }
105
+ function selectFromTable(db, tabName, selectColumns, selectWith, transforms) {
106
+ const whereParams = queryParamsFrom(selectWith, transforms);
107
+ const whereClause = andEqualExprFor(whereParams);
108
+ const [sqlValue] = db.exec(`SELECT ${Array.isArray(selectColumns) ? selectColumns.join(', ') : '*'} FROM ${tabName} WHERE ${whereClause}`, whereParams);
109
+ return sqlValue ? fromQueryResult(sqlValue, transforms) : undefined;
110
+ }
111
+ function updateInTable(db, tabName, valuesToSet, updateWith, transforms) {
112
+ const whereParams = queryParamsFrom(updateWith, transforms);
113
+ const whereClause = andEqualExprFor(whereParams);
114
+ const setParams = queryParamsFrom(valuesToSet, transforms);
115
+ const setExpr = setExprFor(setParams);
116
+ db.exec(`UPDATE ${tabName} SET ${setExpr} WHERE ${whereClause}`, { ...whereParams, ...setParams });
117
+ }
118
+ function setExprFor(params, omit) {
119
+ const setPairs = [];
120
+ for (const paramField of Object.keys(params)) {
121
+ const field = paramField.substring(1);
122
+ if (!(omit === null || omit === void 0 ? void 0 : omit.includes(field))) {
123
+ setPairs.push(`${field}=${paramField}`);
124
+ }
125
+ }
126
+ return setPairs.join(', ');
127
+ }
128
+ function andEqualExprFor(params, omit) {
129
+ const pairs = [];
130
+ for (const paramField of Object.keys(params)) {
131
+ const field = paramField.substring(1);
132
+ if (!(omit === null || omit === void 0 ? void 0 : omit.includes(field))) {
133
+ pairs.push(`${field}=${paramField}`);
134
+ }
135
+ }
136
+ return pairs.join(' AND ');
137
+ }
138
+ exports.optJsonTransform = {
139
+ toSQLValue: (v) => (v ? JSON.stringify(v) : null),
140
+ fromSQLValue: (sv) => (sv ? JSON.parse(sv) : null)
141
+ };
142
+ exports.jsonTransform = {
143
+ toSQLValue: (v) => JSON.stringify(v),
144
+ fromSQLValue: (sv) => JSON.parse(sv)
145
+ };
146
+ exports.booleanTransform = {
147
+ toSQLValue: (b) => (b ? 1 : 0),
148
+ fromSQLValue: (flag) => ((flag === 1) ? true : false)
149
+ };
150
+ exports.optStringAsEmptyTransform = {
151
+ toSQLValue: (s) => (s ? s : ''),
152
+ fromSQLValue: (s) => (s ? s : '')
153
+ };
154
+ function passOmitting(o, ...omitted) {
155
+ const copy = {};
156
+ for (const [field, value] of Object.entries(o)) {
157
+ if (!omitted.includes(field)) {
158
+ copy[field] = value;
159
+ }
160
+ }
161
+ return copy;
162
+ }
@@ -4,20 +4,24 @@ export type Database = DBClass;
4
4
  export type BindParams = QueryParams;
5
5
  export type QueryExecResult = QueryResult;
6
6
  type WritableFile = web3n.files.WritableFile;
7
- export interface SaveOpts {
8
- skipUpload?: boolean;
9
- }
7
+ type WritableFileSyncAPI = web3n.files.WritableFileSyncAPI;
10
8
  export declare abstract class SQLiteOn3NStorage {
11
9
  protected readonly database: Database;
12
10
  protected readonly file: WritableFile;
13
11
  protected readonly syncProc: SingleProc;
14
12
  protected constructor(database: Database, file: WritableFile);
15
- static makeAndStart(file: WritableFile): Promise<SQLiteOn3NStorage>;
13
+ static makeSynced(file: WritableFile, startWithFileContent?: boolean): Promise<SQLiteOnSyncedFS>;
16
14
  private start;
17
- saveToFile(opts?: SaveOpts): Promise<void>;
15
+ saveToFile(): Promise<void>;
18
16
  get db(): Database;
19
17
  sync<T>(action: Action<T>): Promise<T>;
20
18
  listTables(): string[];
19
+ makeCopyIn(file: WritableFile): Promise<SQLiteOn3NStorage>;
20
+ }
21
+ export declare class SQLiteOnSyncedFS extends SQLiteOn3NStorage {
22
+ constructor(db: Database, file: WritableFile);
23
+ get dbFile(): WritableFile;
24
+ get dbFileSync(): WritableFileSyncAPI;
21
25
  }
22
26
  export declare function objectFromQueryExecResult<T>(sqlResult: QueryExecResult): Array<T>;
23
27
  type KeyedInfo<T> = {
@@ -26,11 +30,20 @@ type KeyedInfo<T> = {
26
30
  export declare class TableColumnsAndParams<EntryType extends object> {
27
31
  readonly name: string;
28
32
  private readonly columnDefs;
33
+ /**
34
+ * c is column name, just column name
35
+ */
29
36
  readonly c: KeyedInfo<EntryType>;
30
37
  readonly cReversed: {
31
38
  [colName: string]: keyof EntryType;
32
39
  };
40
+ /**
41
+ * p is parameter for column
42
+ */
33
43
  readonly p: KeyedInfo<EntryType>;
44
+ /**
45
+ * q is a qualified column name with table name
46
+ */
34
47
  readonly q: KeyedInfo<EntryType>;
35
48
  readonly columnsCreateSection: string;
36
49
  constructor(name: string, columnDefs: {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2022 - 2023 3NSoft Inc.
3
+ Copyright (C) 2022 - 2023, 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
@@ -16,7 +16,7 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.TableColumnsAndParams = exports.SQLiteOn3NStorage = void 0;
19
+ exports.TableColumnsAndParams = exports.SQLiteOnSyncedFS = exports.SQLiteOn3NStorage = void 0;
20
20
  exports.objectFromQueryExecResult = objectFromQueryExecResult;
21
21
  const sqljs_js_1 = require("./sqljs.js");
22
22
  const synced_js_1 = require("./synced.js");
@@ -26,28 +26,35 @@ class SQLiteOn3NStorage {
26
26
  this.file = file;
27
27
  this.syncProc = new synced_js_1.SingleProc();
28
28
  }
29
- static async makeAndStart(file) {
29
+ // static async makeAndStart(file: WritableFile): Promise<SQLiteOn3NStorage> {
30
+ // const SQL = await initSqlJs(true);
31
+ // const fileContent = await readFileContent(file);
32
+ // const db = new SQL.Database(fileContent);
33
+ // let sqlite: SQLiteOn3NStorage;
34
+ // if (file.v?.sync) {
35
+ // sqlite = new SQLiteOnSyncedFS(db, file);
36
+ // } else if (file.v) {
37
+ // sqlite = new SQLiteOnLocalFS(db, file);
38
+ // } else {
39
+ // sqlite = new SQLiteOnDeviceFS(db, file);
40
+ // }
41
+ // await sqlite.start();
42
+ // return sqlite;
43
+ // }
44
+ static async makeSynced(file, startWithFileContent = true) {
30
45
  var _a;
31
- const SQL = await (0, sqljs_js_1.default)(true);
32
- const fileContent = await readFileContent(file);
33
- const db = new SQL.Database(fileContent);
34
- let sqlite;
35
- if ((_a = file.v) === null || _a === void 0 ? void 0 : _a.sync) {
36
- sqlite = new SQLiteOnSyncedFS(db, file);
46
+ if (!((_a = file.v) === null || _a === void 0 ? void 0 : _a.sync)) {
47
+ throw new TypeError(`Expected synced file to make synced db`);
37
48
  }
38
- else if (file.v) {
39
- sqlite = new SQLiteOnLocalFS(db, file);
40
- }
41
- else {
42
- sqlite = new SQLiteOnDeviceFS(db, file);
43
- }
44
- await sqlite.start();
45
- return sqlite;
49
+ const SQL = await (0, sqljs_js_1.default)(true);
50
+ const initialContent = startWithFileContent ? (await readFileContent(file)) : undefined;
51
+ const db = new SQL.Database(initialContent);
52
+ return new SQLiteOnSyncedFS(db, file);
46
53
  }
47
54
  async start() {
48
55
  // XXX add listening process(es)
49
56
  }
50
- async saveToFile(opts) {
57
+ async saveToFile() {
51
58
  await this.syncProc.startOrChain(async () => {
52
59
  const dbFileContent = this.database.export();
53
60
  await this.file.writeBytes(dbFileContent);
@@ -65,6 +72,24 @@ class SQLiteOn3NStorage {
65
72
  result[0].values.map(row => row[0]) :
66
73
  []);
67
74
  }
75
+ async makeCopyIn(file) {
76
+ var _a;
77
+ const SQL = await (0, sqljs_js_1.default)(true);
78
+ const fileContent = this.database.export();
79
+ const db = new SQL.Database(fileContent);
80
+ let sqlite;
81
+ if ((_a = file.v) === null || _a === void 0 ? void 0 : _a.sync) {
82
+ sqlite = new SQLiteOnSyncedFS(db, file);
83
+ }
84
+ else if (file.v) {
85
+ sqlite = new SQLiteOnLocalFS(db, file);
86
+ }
87
+ else {
88
+ sqlite = new SQLiteOnDeviceFS(db, file);
89
+ }
90
+ await sqlite.start();
91
+ return sqlite;
92
+ }
68
93
  }
69
94
  exports.SQLiteOn3NStorage = SQLiteOn3NStorage;
70
95
  Object.freeze(SQLiteOn3NStorage.prototype);
@@ -74,16 +99,14 @@ class SQLiteOnSyncedFS extends SQLiteOn3NStorage {
74
99
  super(db, file);
75
100
  Object.seal(this);
76
101
  }
77
- async saveToFile(opts) {
78
- await super.saveToFile();
79
- if (opts === null || opts === void 0 ? void 0 : opts.skipUpload) {
80
- return;
81
- }
82
- else {
83
- await this.file.v.sync.upload();
84
- }
102
+ get dbFile() {
103
+ return this.file;
104
+ }
105
+ get dbFileSync() {
106
+ return this.file.v.sync;
85
107
  }
86
108
  }
109
+ exports.SQLiteOnSyncedFS = SQLiteOnSyncedFS;
87
110
  Object.freeze(SQLiteOnSyncedFS.prototype);
88
111
  Object.freeze(SQLiteOnSyncedFS);
89
112
  class SQLiteOnLocalFS extends SQLiteOn3NStorage {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2020 3NSoft Inc.
3
+ Copyright (C) 2020, 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
@@ -19,6 +19,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.specs = void 0;
20
20
  const random_node_1 = require("../../../lib-common-on-node/random-node");
21
21
  const bytes_equal_1 = require("../../libs-for-tests/bytes-equal");
22
+ const buffer_utils_1 = require("../../../lib-common/buffer-utils");
23
+ const json_equal_1 = require("../../libs-for-tests/json-equal");
22
24
  exports.specs = {
23
25
  description: 'gives file sinks, able to splice file content',
24
26
  its: []
@@ -119,4 +121,73 @@ it.func = async function (s) {
119
121
  expect((0, bytes_equal_1.bytesEqual)(content.subarray(7000), chunk2)).toBe(true);
120
122
  };
121
123
  exports.specs.its.push(it);
124
+ // XXX enbale this when capturing this bug
125
+ it = {
126
+ expectation: 'usecase with removing and adding sections of json', disableIn: 'device-fs'
127
+ };
128
+ it.func = async function (s) {
129
+ const { testFS } = s;
130
+ const file = 'incremental-file';
131
+ const COMMA_BYTE = buffer_utils_1.utf8.pack(',');
132
+ const SQ_BRACKET_BYTE = buffer_utils_1.utf8.pack(']');
133
+ const completeContent = [
134
+ {
135
+ record: {
136
+ msgId: "F-vo1A4zInEBGq994-e3KvrE9a8QXLcr",
137
+ }
138
+ },
139
+ {
140
+ record: {
141
+ msgId: "JYXfEEcg3-iX7UDjG3BU0Jha9DYX_Jt-",
142
+ }
143
+ }
144
+ ];
145
+ const TEST_XATTR = 'attr-for-test';
146
+ const attrValue1 = Date.now();
147
+ const attrValue2 = { comment: 'now json value', ts: Date.now() };
148
+ // the following splice pattern hit an error, hence seemingly out of the blue use case, but it is an echo
149
+ // XXX removal of fs.updateXAttrs() call removes error in this situation
150
+ console.log(`p 1`);
151
+ await testFS.writeTxtFile(file, `[]`);
152
+ console.log(`p 2`);
153
+ let sink = await testFS.getByteSink(file, { truncate: false });
154
+ console.log(`p 2.1`);
155
+ await sink.splice(1, 1);
156
+ console.log(`p 2.2`);
157
+ let bytes = buffer_utils_1.utf8.pack(JSON.stringify(completeContent[0]));
158
+ console.log(`p 2.3`);
159
+ await sink.splice(1, 0, bytes);
160
+ console.log(`p 2.4`);
161
+ await sink.splice(1 + bytes.length, 0, SQ_BRACKET_BYTE);
162
+ console.log(`p 2.5`);
163
+ await sink.done();
164
+ console.log(`p 2.6`);
165
+ await testFS.updateXAttrs(file, { set: { [TEST_XATTR]: attrValue1 } });
166
+ console.log(`p 3`);
167
+ globalThis.log$ = true;
168
+ sink = await testFS.getByteSink(file, { truncate: false });
169
+ console.log(`p 3.1 -------- getting size`);
170
+ const len = await sink.getSize();
171
+ console.log(`p 3.2 ------------ replacing one last byte in content, \n layout`, await sink.showLayout());
172
+ await sink.splice(len - 1, 1, COMMA_BYTE);
173
+ // await sink.splice(len-1, 1);
174
+ // console.log(`p 3.2+ ---------------------------------- replacing one last byte in content, \n layout`, await sink.showLayout());
175
+ // await sink.splice(len-1, 0, COMMA_BYTE);
176
+ // await sink.splice(len-1, 1, utf8.pack(', '));
177
+ console.log(`p 3.3 ----------- writing content after last replaced byte, \n layout`, await sink.showLayout());
178
+ bytes = buffer_utils_1.utf8.pack(JSON.stringify(completeContent[1]));
179
+ await sink.splice(len, 0, bytes);
180
+ console.log(`p 3.4 -------- turning logging off, \n layout`, await sink.showLayout());
181
+ globalThis.log$ = false;
182
+ await sink.splice(len + bytes.length, 0, SQ_BRACKET_BYTE);
183
+ console.log(`p 3.5, \n layout`, await sink.showLayout());
184
+ const attrWrite = testFS.updateXAttrs(file, { set: { [TEST_XATTR]: attrValue2 } });
185
+ console.log(`p 3.6`);
186
+ await sink.done();
187
+ await attrWrite;
188
+ console.log(`p 4`);
189
+ expect((0, json_equal_1.deepEqual)(completeContent, await testFS.readJSONFile(file))).toBeTrue();
190
+ console.log(`p 5`);
191
+ };
192
+ // specs.its.push(it);
122
193
  Object.freeze(exports);
@@ -161,10 +161,36 @@ it.func = async function (s) {
161
161
  await testFS.v.sync.upload(folder);
162
162
  syncStatus = await testFS.v.sync.status(folder, true);
163
163
  expect(syncStatus.state).toBe('synced');
164
+ expect(syncStatus.existsInSyncedParent).toBeFalsy();
164
165
  // note again that upload is not uploading children
165
166
  syncStatus = await testFS.v.sync.status(childPath, true);
166
167
  expect(syncStatus.state).toBe('unsynced');
167
- expect(syncStatus.existsInSyncedParent).toBeTruthy();
168
+ expect(syncStatus.existsInSyncedParent).toBeTrue();
169
+ const subFS = await testFS.writableSubRoot(folder);
170
+ syncStatus = await subFS.v.sync.status('', true);
171
+ expect(syncStatus.existsInSyncedParent).toBeFalsy();
172
+ await testFS.v.sync.upload('');
173
+ syncStatus = await testFS.v.sync.status(folder, true);
174
+ expect(syncStatus.existsInSyncedParent).toBeTrue();
175
+ syncStatus = await subFS.v.sync.status('', true);
176
+ expect(syncStatus.existsInSyncedParent).toBeTrue();
177
+ const childFolder = `child-folder`;
178
+ await subFS.makeFolder(childFolder);
179
+ // child never uploaded at this point
180
+ await subFS.v.sync.upload('').then(() => fail(`upload of folder must fail with child that was never uploaded`), (exc) => {
181
+ expect(exc.type).toBe('fs-sync');
182
+ expect(exc.childNeverUploaded).toBeTrue();
183
+ expect(exc.childName).toBe(childFolder);
184
+ });
185
+ // uploading child, but it isn't yet in synced version of parent
186
+ await subFS.v.sync.upload(childFolder);
187
+ syncStatus = await subFS.v.sync.status(childFolder);
188
+ expect(syncStatus.state).toBe('synced');
189
+ expect(syncStatus.existsInSyncedParent).toBeFalsy();
190
+ // syncing parent makes child findable from upper synced point, and sync UX guarantees can be provided
191
+ await subFS.v.sync.upload('');
192
+ syncStatus = await subFS.v.sync.status(childFolder);
193
+ expect(syncStatus.existsInSyncedParent).toBeTrue();
168
194
  };
169
195
  exports.specs.its.push(it);
170
196
  it = { expectation: `should do own concurrently` };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 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
@@ -23,6 +23,7 @@ const random_node_1 = require("../../../../lib-common-on-node/random-node");
23
23
  const bytes_equal_1 = require("../../../libs-for-tests/bytes-equal");
24
24
  const json_equal_1 = require("../../../libs-for-tests/json-equal");
25
25
  const test_utils_1 = require("../test-utils");
26
+ const lodash_1 = require("lodash");
26
27
  exports.specs = {
27
28
  description: '--',
28
29
  its: []
@@ -141,7 +142,7 @@ it.func = async function ({ dev1FS, dev2FS }) {
141
142
  await fs1.v.sync.upload(file);
142
143
  let fileEv = await fileEventAt2;
143
144
  expect(fileEv.type).toBe('remote-change');
144
- expect((0, json_equal_1.deepEqual)(fileEv.syncStatus, await fs2.v.sync.status(file))).toBeTrue();
145
+ expect((0, json_equal_1.deepEqual)(fileEv.syncStatus, (0, lodash_1.omit)(await fs2.v.sync.status(file), 'existsInSyncedParent', 'uploading'))).toBeTrue();
145
146
  expect(await fs2.v.sync.isRemoteVersionOnDisk(file, fileEv.newRemoteVersion)).toBe('none');
146
147
  let events = collectFileDownloadEvents(fs2, file);
147
148
  startedDownload = await fs2.v.sync.startDownload(file, fileEv.newRemoteVersion);
@@ -198,6 +199,10 @@ it.func = async function ({ dev1FS, dev2FS }) {
198
199
  }
199
200
  }
200
201
  ];
202
+ // XXX enable attr writes, when sink's bug is fixed
203
+ // const TEST_XATTR = 'attr-for-test';
204
+ // const attrValue1 = Date.now();
205
+ // const attrValue2 = { comment: 'now json value', ts: Date.now() };
201
206
  // we do this file writing and uploading, cause this pattern hit an error,
202
207
  // hence, we add this test with this seemingly out of the blue setup
203
208
  await fs1.writeTxtFile(file, `[]`);
@@ -209,6 +214,7 @@ it.func = async function ({ dev1FS, dev2FS }) {
209
214
  await sink.splice(1, 0, bytes);
210
215
  await sink.splice(1 + bytes.length, 0, SQ_BRACKET_BYTE);
211
216
  await sink.done();
217
+ // await fs1.updateXAttrs(file, { set: { [TEST_XATTR]: attrValue1 } });
212
218
  await fs1.v.sync.upload(file);
213
219
  sink = await fs1.getByteSink(file, { truncate: false });
214
220
  const len = await sink.getSize();
@@ -216,14 +222,17 @@ it.func = async function ({ dev1FS, dev2FS }) {
216
222
  bytes = buffer_utils_1.utf8.pack(JSON.stringify(completeContent[1]));
217
223
  await sink.splice(len, 0, bytes);
218
224
  await sink.splice(len + bytes.length, 0, SQ_BRACKET_BYTE);
225
+ // const attrWrite = fs1.updateXAttrs(file, { set: { [TEST_XATTR]: attrValue2 } });
219
226
  await sink.done();
227
+ // await attrWrite;
220
228
  expect((0, json_equal_1.deepEqual)(completeContent, await fs1.readJSONFile(file))).toBeTrue();
221
229
  await fs1.v.sync.upload(file);
222
230
  // and on the second device
223
231
  expect((await fs2.v.sync.status('')).state).toBe('behind');
224
232
  await fs2.v.sync.adoptRemote('');
225
233
  expect((await fs2.v.sync.status(file)).state).toBe('synced');
226
- expect((0, json_equal_1.deepEqual)(completeContent, await fs2.readJSONFile(file))).toBeTrue();
234
+ expect((0, json_equal_1.deepEqual)(completeContent, await fs2.readJSONFile(file))).withContext(`contents of files should be same`).toBeTrue();
235
+ // expect(deepEqual(attrValue2, await fs2.getXAttr(file, TEST_XATTR))).withContext(`content of attr should be same`).toBeTrue();
227
236
  };
228
237
  exports.specs.its.push(it);
229
238
  Object.freeze(exports);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright 2022 3NSoft Inc.
3
+ Copyright 2022, 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
@@ -17,7 +17,6 @@
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  const msg_indexing_1 = require("../../../core/asmail/inbox/msg-indexing");
20
- // import { MsgKeyInfo } from "../core/asmail/keyring";
21
20
  const jasmine_utils_1 = require("../../libs-for-tests/jasmine-utils");
22
21
  const setups_1 = require("../../libs-for-tests/setups");
23
22
  const test_utils_1 = require("../test-utils");
@@ -91,7 +90,7 @@ const msgs = [
91
90
  }
92
91
  }
93
92
  ];
94
- xdescribe(`Inbox MsgIndex`, () => {
93
+ describe(`Inbox MsgIndex`, () => {
95
94
  const baseSetup = (0, setups_1.setupWithUsers)();
96
95
  const testFolder = `inbox-indexing-test`;
97
96
  let dev1Index;
@@ -100,7 +99,7 @@ xdescribe(`Inbox MsgIndex`, () => {
100
99
  await setupDevsAndFSs(baseSetup);
101
100
  }, 20000);
102
101
  beforeEach(async () => {
103
- dev1Index = await msg_indexing_1.MsgIndex.make(setup.dev1FS());
102
+ dev1Index = await (0, msg_indexing_1.makeMsgIndex)(setup.dev1FS(), setup.dev1LocalFS(), async (err, msg) => console.error(msg, err));
104
103
  });
105
104
  (0, jasmine_utils_1.afterEachCond)(async () => {
106
105
  if (!setup.isUp) {
@@ -125,9 +124,11 @@ xdescribe(`Inbox MsgIndex`, () => {
125
124
  await dev1Index.remove(m0.m.msgId);
126
125
  lst = await dev1Index.listMsgs(undefined);
127
126
  expect(lst.length).toBe(1);
128
- const keyInfo = await dev1Index.getKeyFor(m1.m.msgId, m1.m.deliveryTS);
129
- expect((0, bytes_equal_1.bytesEqual)(keyInfo.msgKey, m1.k.key))
130
- .toBeTrue();
127
+ let keyInfo = await dev1Index.getKeyFor(m1.m.msgId, m1.m.deliveryTS);
128
+ expect((0, bytes_equal_1.bytesEqual)(keyInfo.msgKey, m1.k.key)).toBeTrue();
129
+ // getting removed / non-existing message
130
+ keyInfo = await dev1Index.getKeyFor(m0.m.msgId, m0.m.deliveryTS);
131
+ expect(keyInfo).toBeUndefined();
131
132
  }, undefined, setup);
132
133
  (0, jasmine_utils_1.itCond)(`work with race conditions on different devices`, async () => {
133
134
  for (const m of msgs.slice(0, 2)) {
@@ -135,11 +136,14 @@ xdescribe(`Inbox MsgIndex`, () => {
135
136
  }
136
137
  // sleep to allow time for propagation to dev2
137
138
  await (0, sleep_1.sleep)(100);
138
- const dev2Index = await msg_indexing_1.MsgIndex.make(setup.dev2FS());
139
+ const dev2Index = await (0, msg_indexing_1.makeMsgIndex)(setup.dev2FS(), setup.dev2LocalFS(), async (err, msg) => console.error(msg, err));
140
+ await (0, sleep_1.sleep)(100);
139
141
  let lst = await dev2Index.listMsgs(undefined);
140
- expect(lst.length)
141
- .withContext(``)
142
- .toBe(2);
142
+ expect(lst.length).withContext(`messages at a start of 2nd device`).toBe(2);
143
+ await dev1Index.add(msgs[2].m, msgs[2].k);
144
+ await (0, sleep_1.sleep)(100);
145
+ lst = await dev2Index.listMsgs(undefined);
146
+ expect(lst.length).withContext(`added message on the 1st device should show up on the 2nd`).toBe(3);
143
147
  dev2Index.stopSyncing();
144
- });
148
+ }, 10000);
145
149
  });