datafolder 1.0.10 → 1.0.11

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  The best mechanism to index and partition data is **by the nature of data**. Instead of forcing data to transform, it should seamlessly embrace and fully cater to the **uniqueness** of each and every data to accomplish nature-level efficiency, **mentally and mechanically**. Whatever data is, it follows like fluid.
4
4
 
5
- This is the **design philosophy** behind datafolder, a lightweight schemaless JSON database, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
5
+ This is the **design philosophy** behind datafolder, a lightning fluid schemaless JSON database, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. Data can be organized naturally into folders, sub folders and files. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
6
6
 
7
7
  ### User Level Data Layout
8
8
 
@@ -24,6 +24,13 @@ Airports can be stored as
24
24
  airports/{country}/{state}/{airport_id}
25
25
  ```
26
26
 
27
+ AI training data can be stored as
28
+
29
+ ```
30
+ AITraining/{year}/{month}/{day}/chat_id/prompt
31
+ /answer
32
+ ```
33
+
27
34
  When you conceive the data model, you don't need to think about tables, schemas, indexes etc. All you need to know is the nature of the data and then go with the most comfortable one for your mental mind.
28
35
 
29
36
  ### In-memory Tree Structure
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
1
  # datafolder
2
2
 
3
- datafolder is a lightning fluid JSON database for SSD, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
3
+ datafolder is a lightning fluid JSON database for high speed SSDs, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
4
4
 
5
5
  ### Changelog
6
6
 
7
+ 1.0.11 Added [SKILL.md](https://github.com/datafolderdev/datafolder/blob/main/SKILL.md) for feeding to AI
8
+
7
9
  1.0.10 Added document [datafolder for high speed SSDs](https://github.com/datafolderdev/datafolder/wiki/datafolder-for-high-speed-SSDs)
8
10
 
9
11
  1.0.9 Added Typescript type declarations.
package/SKILL.md ADDED
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: datafolder-builder
3
+ description: >
4
+ Use this skill when building applications on top of datafolder — a lightning fluid JSON database for high speed SSDs, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. Data can be organized naturally into folders, sub folders and files. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing. Covers installation, basic usages, CRUD, batch APIs, powerful query support which auto creates needed indexes, flexible trigger support to monitor any changes.
5
+ Trigger when a user asks to build, query, or operate with datafolder, write application code against it, or understand how to structure data and queries.
6
+ ---
7
+
8
+ # datafolder Builder Skill
9
+
10
+ ## What Is datafolder?
11
+
12
+ The best mechanism to index and partition data is **by the nature of data**. Instead of forcing data to transform, it should seamlessly embrace and fully cater to the **uniqueness** of each and every data to accomplish nature-level efficiency, **mentally and mechanically**. Whatever data is, it follows like fluid.
13
+
14
+ This is the **design philosophy** behind datafolder, a lightning fluid schemaless JSON database, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. Data can be organized naturally into folders, sub folders and files. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
15
+
16
+ ## Features
17
+
18
+ 🤖 AI-Friendly, Simple API: Designed for easy code generation by AI and long-term maintainability by humans.
19
+
20
+ ⚡ Extreme Data Access Speed: Supports fully in-memory operation for millisecond response, with configurable persistence to high-speed SSD.
21
+
22
+ 🌐 Built-in Browser Management: Visually browse and manage your data as a folder hierarchy directly within the browser, no separate tools needed.
23
+
24
+ 🧩 Plain JavaScript: Built with plain JavaScript, offers full TypeScript support and safe atomic writes.
25
+
26
+ 🔧 Designed for application/game backends, IoT, and edge computing scenarios, easily deployable on servers, in browsers, and even on embedded hardware.
27
+
28
+ ## User Level Data Layout
29
+
30
+ Data can be organized naturally into folders, sub folders and files. For example, Orders can be stored in
31
+
32
+ ```
33
+ orders/{year}/{month}/{day}/{order_id}
34
+ ```
35
+
36
+ Flights can be stored as
37
+
38
+ ```
39
+ flights/{from}/{to}/{flight_id}
40
+ ```
41
+
42
+ Airports can be stored as
43
+
44
+ ```
45
+ airports/{country}/{state}/{airport_id}
46
+ ```
47
+
48
+ AI training data can be stored as
49
+
50
+ ```
51
+ AITraining/{year}/{month}/{day}/chat_id/prompt
52
+ /answer
53
+ ```
54
+
55
+ When you conceive the data model, you don't need to think about tables, schemas, indexes etc. All you need to know is the nature of the data and then go with the most comfortable one for your mental mind.
56
+
57
+ ## Installation
58
+
59
+ ```javascript
60
+ npm install "datafolder"
61
+ ```
62
+
63
+ ## How to use
64
+
65
+ ```javascript
66
+ import DataFolder from "datafolder";
67
+ const dataFolder = new DataFolder(); //Specify where to store the data. Default is ./data/sampleDataFolder
68
+ await dataFolder.start(); //start() and stop() are the only two async places. All other code doesn't need await.
69
+ dataFolder.insert("traindata/2026/1/20/chat_0001", {
70
+ userid: "user_0001",
71
+ email: "a@b.com",
72
+ feedbackScore: 5,
73
+ address: { city: "a", zipcode: 123 },
74
+ });
75
+ dataFolder.insert("traindata/2026/1/20/chat_0001/prompt", "What is async/await?");
76
+ dataFolder.insert("traindata/2026/1/20/chat_0001/answer", "async/await is ...");
77
+ const { email, password, address, feedbackScore } = dataFolder.view("traindata/2026/1/20/chat_0001");
78
+ address.zipcode = 678;
79
+ dataFolder.insert("traindata/2026/1/20/chat_0001", { feedbackScore: feedbackScore + 1, address });
80
+ await dataFolder.stop();
81
+ ```
82
+
83
+ Or operate with dir/file objects:
84
+
85
+ ```javascript
86
+ const file = dataFolder.file("traindata/2026/1/20/chat_0001");
87
+ const { feedbackScore, address } = file.view({ feedbackScore: 1, address: 1 });
88
+ dataFolder.insert(file, { feedbackScore: feedbackScore + 1, address: { city: "c", zipcode: 663 } });
89
+ ```
90
+
91
+ ### Simple CRUD APIs
92
+
93
+ #### Create
94
+
95
+ ```javascript
96
+ dataFolder.createFile("path/to/file");
97
+ dataFolder.createDir("path/to/dir");
98
+ dataFolder.insert("path/to/file", { email, address: { city, zipcode } });
99
+ ```
100
+
101
+ #### Read
102
+
103
+ ```javascript
104
+ const dir = dataFolder.dir("path/to/dir");
105
+ const { subdirList, fileList, sortedSubdirList } = dir;
106
+ const file = dataFolder.file("path/to/file");
107
+ const { email, address } = dataFolder.view("path/to/file", { email: 1, addres: 1 });
108
+ ```
109
+
110
+ #### Update
111
+
112
+ ```javascript
113
+ dataFolder.insert("path/to/file", { email: newEmail, address: { zipcode: newZipcode } });
114
+ dataFolder.insert(file, { feedbackScore: feedbackScore + 1 });
115
+ ```
116
+
117
+ #### Delete
118
+
119
+ ```javascript
120
+ dataFolder.delDir("path/to/dir");
121
+ dataFolder.delFile("path/to/file");
122
+ dataFolder.delContent("path/to/file");
123
+ dataFolder.remove("path/to/file", { email: 1, address: { zipcode: 1 } });
124
+ dataFolder.remove(file, { email: 1, address: { zipcode: 1 } });
125
+ ```
126
+
127
+ ### Batch APIs
128
+
129
+ ```javascript
130
+ const [dir, file, { email, feedbackScore }] = dataFolder.fetch
131
+ .dir("path/to/dir")
132
+ .file("path/to/file")
133
+ .view("path/to/file")
134
+ .run();
135
+ dataFolder.batch
136
+ .insert("path/to/chat_details", { user: "user_0001", feedbackScore: 100 })
137
+ .insert("path/to/prompt", "what is LLM?")
138
+ .insert("path/to/answer", "LLM is AI...")
139
+ .remove("path/to/file", { address: { city: 1 } })
140
+ .run();
141
+ ```
142
+
143
+ ### Powerful Query Support. Auto Create Needed Indexes
144
+
145
+ ```javascript
146
+ // get all the training data in year 2026 with feedbackScore between 60 and 85.
147
+ dataFolder.queryFiles("traindata/2026/**", { feedbackScore: (x) => x >= 60 && x <= 85 });
148
+
149
+ // get training data of user_001 from July to Oct in year 2025, 2026 with feedbackScore higher than 90.
150
+ dataFolder.queryFiles(["traindata", { $in: [2025, 2026] }, { $gt: 6, $lte: 10 }], {
151
+ feedbackScore: { $gt: 90 },
152
+ userId: "user_0001",
153
+ });
154
+
155
+ dataFolder.queryFiles("flights/**", { _from: "airports/LAX" });
156
+ dataFolder.queryFiles(["flights", "*", "jan", (x) => x >= 5 && x <= 7], {
157
+ $or: [{ _from: "airports/BIS" }, { _to: "airports/BIS" }],
158
+ });
159
+ dataFolder.queryFiles(["flights", "*", "feb", { $or: [5, 6, 7] }], {
160
+ $or: [{ _from: "airports/BIS" }, { _to: "airports/BIS" }],
161
+ });
162
+ dataFolder.queryFiles(["flights", "*", "mar", { $gte: 5, lte: 7 }], {
163
+ $or: [{ _from: "airports/BIS" }, { _to: "airports/BIS" }],
164
+ });
165
+ dataFolder.queryFilesMulti([
166
+ [["airbnb", "Loft", "Entire home/apt", "Real Bed"], { address: { street: "Porto, Porto, Portugal" } }],
167
+ ["airbnb/Loft/**", { minimum_nights: (x) => x >= 60 }],
168
+ ]);
169
+ ```
170
+
171
+ ### Flexible Trigger Support to Monitor Any Changes
172
+
173
+ ```javascript
174
+ function onPromptScoreChanged({ promptId, feedbackScore }) {
175
+ const { oldValue, newValue } = feedbackScore;
176
+ // Logic to handle prompt feedback score change.
177
+ }
178
+ dataFolder.insertTrigger("promptScoreChanged", "traindata/{promptId}", "feedbackScore");
179
+ dataFolder.on("promptScoreChanged", onPromptScoreChanged);
180
+ dataFolder.insert("traindata/prompt_1", { feedbackScore: 80 });
181
+ dataFolder.insert("traindata/prompt_2", { feedbackScore: 63 });
182
+ dataFolder.insert("traindata/prompt_1", { feedbackScore: 32 });
183
+ ```
184
+
185
+ Will trigger the following calls:
186
+
187
+ ```javascript
188
+ onPromptScoreChanged({ promptId: "student1", feedbackScore: { oldValue: undefined, newValue: 80 } });
189
+ onPromptScoreChanged({ promptId: "student2", feedbackScore: { oldValue: undefined, newValue: 63 } });
190
+ onPromptScoreChanged({ promptId: "student1", feedbackScore: { oldValue: 80, newValue: 32 } });
191
+ ```
package/SSD.md CHANGED
@@ -34,7 +34,7 @@ Key Characteristics and Benefits of LSM trees to run on SSDs
34
34
 
35
35
  ### datafolder, a lightning fluid JSON database, is a natural fit for high speed SSDs
36
36
 
37
- datafolder is [based on LSM trees](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder), but taked to a higher level.
37
+ datafolder is [based on LSM trees](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder), but taken to a higher level.
38
38
 
39
39
  - Complex JSON data in a single virtual file for a record
40
40
 
@@ -42,7 +42,7 @@ The data is just a normal JSON [object](https://github.com/datafolderdev/datafol
42
42
 
43
43
  - Changes are stored as deltas in append-only mode.
44
44
 
45
- When changes are maded to each virtual file, only [the deltas](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder#accumulated-change-list) are stored. And the delta changes from different virtual files/folders are merged and appended to ACL log files.
45
+ When changes are made to each virtual file, only [the deltas](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder#accumulated-change-list) are stored. And the delta changes from different virtual files/folders are merged and appended to ACL log files.
46
46
 
47
47
  - Never overwrite. Always append or create new file.
48
48
 
@@ -57,4 +57,4 @@ datafolder never overwrites. It always appends to an ACL file, or create new [sn
57
57
  - Snapshot Position Mapping maps a key to its position in the corresponding valueList file.
58
58
  - Snapshot Prefix Position Mapping maps a key prefix to its range position in the corresponding Positing Mapping file.
59
59
 
60
- ### Future: Make datafolder runs directly on SSDs without the FTL
60
+ ### Future: Make datafolder runs directly on SSDs without the FTL, or Zoned Namespaces(ZNS) SSDs (formerly Open-Channel SSDs)
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "datafolder",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "author": {
5
5
  "name": "lysh",
6
6
  "email": "datafolderdev@outlook.com"
7
7
  },
8
- "description": "datafolder is a lightning fluid JSON database for SSD, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.",
8
+ "description": "datafolder is a lightning fluid JSON database for high speed SSDs, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  import { QyBinWriter } from "./QyBinWriter.ts";
2
2
  export declare class QyAclWriter extends QyBinWriter {
3
3
  qyAclCmdGenerator: any;
4
- constructor(options: any, filePath?: undefined);
4
+ constructor(options: any, filePath?: any);
5
5
  getBinData(dataQueue: any, changeId: any): any;
6
6
  }
@@ -3,7 +3,7 @@ export declare class QyBinWriter extends QyOpRunner {
3
3
  _filePath: any;
4
4
  _totalSavedSize: number;
5
5
  fileHandle: any;
6
- constructor(options?: any, filePath?: undefined);
6
+ constructor(options?: any, filePath?: any);
7
7
  get filePath(): any;
8
8
  set filePath(val: any);
9
9
  get totalSavedSize(): number;
package/src/QyKVData.d.ts CHANGED
@@ -10,7 +10,7 @@ export declare class QyKVData {
10
10
  compactMaxChangeId: number;
11
11
  snapshotSavedChangeId: number;
12
12
  maxChangeId: number;
13
- emptySnapshotNumVersionList: any[];
13
+ emptySnapshotNumVerList: any[];
14
14
  isStopping: boolean;
15
15
  qySnapshots: any;
16
16
  aclKeyValueMap: any;
package/src/QyKVData.js CHANGED
@@ -11,11 +11,11 @@ import {
11
11
  } from "./QyDefaultOptions.js";
12
12
 
13
13
  import {
14
- QyKVDataSaver as i
14
+ QyKVDataSaver as p
15
15
  } from "./QyKVDataSaver.js";
16
16
 
17
17
  import {
18
- QySnapshotCompactor as p
18
+ QySnapshotCompactor as i
19
19
  } from "./QySnapshotCompactor.js";
20
20
 
21
21
  import {
@@ -44,7 +44,7 @@ class a {
44
44
  compactMaxChangeId = 0;
45
45
  snapshotSavedChangeId = 0;
46
46
  maxChangeId = 0;
47
- emptySnapshotNumVersionList = [];
47
+ emptySnapshotNumVerList = [];
48
48
  isStopping = !1;
49
49
  qySnapshots;
50
50
  aclKeyValueMap;
@@ -61,8 +61,8 @@ class a {
61
61
  options: t,
62
62
  kvFolder: a,
63
63
  snapshotFolder: e,
64
- qyKVDataSaver: new i(this, a, t),
65
- qySnapshotCompactor: new p(this, a, t),
64
+ qyKVDataSaver: new p(this, a, t),
65
+ qySnapshotCompactor: new i(this, a, t),
66
66
  qyKVDataCleaner: new r(this, a, t)
67
67
  });
68
68
  }
@@ -70,12 +70,12 @@ class a {
70
70
  var {
71
71
  options: n,
72
72
  qyKVDataSaver: s,
73
- qySnapshotCompactor: i,
74
- qyKVDataCleaner: p
73
+ qySnapshotCompactor: p,
74
+ qyKVDataCleaner: i
75
75
  } = this, {
76
76
  maxSnapshotNum: r,
77
77
  snapshotMaxChangeId: h
78
- } = o, s = (s.start(a, r, t, e), i.start(), p.start(), setInterval(() => l(this), n.compactSnapshotsInterval)), r = setInterval(() => (async o => {
78
+ } = o, s = (s.start(a, r, t, e), p.start(), i.start(), setInterval(() => l(this), n.compactSnapshotsInterval)), r = setInterval(() => (async o => {
79
79
  if (!o.unloadingMemory) {
80
80
  o.unloadingMemory = !0;
81
81
  var {
@@ -84,9 +84,9 @@ class a {
84
84
  } = o;
85
85
  let a = 0, t = 0;
86
86
  if (e) {
87
- var s, i = n.maxFileCountToUnload, n = e.rootDir;
87
+ var s, p = n.maxFileCountToUnload, n = e.rootDir;
88
88
  t = d(o, n), a = t;
89
- for (s of n.allLoadedSubdirs()) if ((t += d(o, s)) > i && (a += t,
89
+ for (s of n.allLoadedSubdirs()) if ((t += d(o, s)) > p && (a += t,
90
90
  t = 0, await c(0), o.isStopping)) break;
91
91
  }
92
92
  a += t, global.gc && global.gc(), o.unloadingMemory = !1;
@@ -106,11 +106,11 @@ class a {
106
106
  } = a;
107
107
  console.time("bgLoadPosMaps");
108
108
  for (t of Object.keys(o.numToSnapshotMap).sort((a, t) => t - a)) {
109
- if (await async function s(i, p, r = void 0, h = void 0, c = void 0, l = void 0) {
110
- if (i.isStopping) return void (l && l.close());
111
- let d = i.qySnapshots;
109
+ if (await async function s(p, i, r = void 0, h = void 0, c = void 0, l = void 0) {
110
+ if (p.isStopping) return void (l && l.close());
111
+ let d = p.qySnapshots;
112
112
  let m = d.numToSnapshotMap;
113
- let S = m[p];
113
+ let S = m[i];
114
114
  if (!S) return void (l && l.close());
115
115
  let {
116
116
  info: u,
@@ -119,19 +119,19 @@ class a {
119
119
  r = r || Object.keys(v).sort().reverse();
120
120
  if (0 == r.length) return void (l && l.close());
121
121
  if (null == h || h != u.pMVersion) return l && l.close(), c = d.getPMFilePath(S),
122
- s(i, p, r, u.pMVersion, c, await f(c));
122
+ s(p, i, r, u.pMVersion, c, await f(c));
123
123
  for (;0 < r.length; ) {
124
124
  let e = y(r), n = v[e];
125
125
  if (n) {
126
126
  let [ a, t ] = n, o = await C(c, l, a, t);
127
- if (i.isStopping || !m[p]) return void l.close();
128
- if (h != u.pMVersion) return l.close(), s(i, p, r);
127
+ if (p.isStopping || !m[i]) return void l.close();
128
+ if (h != u.pMVersion) return l.close(), s(p, i, r);
129
129
  e in v && d.loadPartialPosMapBuffer(S, e, o);
130
130
  }
131
131
  r.pop();
132
132
  }
133
133
  l.close();
134
- g.info("bgLoadPosMap:" + p);
134
+ g.info("bgLoadPosMap:" + i);
135
135
  }(a, t), a.isStopping) break;
136
136
  if (await c(0), a.isStopping) break;
137
137
  }
@@ -185,12 +185,12 @@ class a {
185
185
  qyKVDataSaver: n
186
186
  } = this;
187
187
  if (3 == o && e <= a && (L(this), this.compactState = 1), t) {
188
- var s, o = t, i = this.qySnapshots, p = i.keyToSnapshotMap;
188
+ var s, o = t, p = this.qySnapshots, i = p.keyToSnapshotMap;
189
189
  for (s in o.posMap) {
190
- var r = p[s];
191
- r && i.removeKeyFromSnapshot(r, s);
190
+ var r = i[s];
191
+ r && p.removeKeyFromSnapshot(r, s);
192
192
  }
193
- i.addMaxSnapshot(o), g.info("addPMOnlySnapshot: " + i.snapshotMaxChangeId),
193
+ p.addMaxSnapshot(o), g.info("addPMOnlySnapshot: " + p.snapshotMaxChangeId),
194
194
  n.releaseSnapshot(t.snapshotNum);
195
195
  }
196
196
  }
@@ -205,11 +205,11 @@ class a {
205
205
  var t = a, o = l.qySnapshots.numToSnapshotMap;
206
206
  for (s in t) {
207
207
  var e, n = t[s], s = o[s], {
208
- prefixPM: i,
209
- info: p
208
+ prefixPM: p,
209
+ info: i
210
210
  } = s;
211
- for (e in ++p.pMVersion, p.verList[0] = p.pMVersion + p.vLVersion,
212
- i) i[e] = n[e];
211
+ for (e in ++i.pMVersion, i.verList[0] = i.pMVersion + i.vLVersion,
212
+ p) p[e] = n[e];
213
213
  (a => {
214
214
  var {
215
215
  info: t,
@@ -243,26 +243,26 @@ class a {
243
243
  keyToSnapshotMap: s
244
244
  } = e, {
245
245
  snapshotNum: a,
246
- combinedSnapshotNumVersionList: t,
247
- posMap: i
248
- } = d, p = n[a], {
246
+ combinedSnapshotNumVerList: t,
247
+ posMap: p
248
+ } = d, i = n[a], {
249
249
  posMap: r,
250
250
  info: o
251
- } = p;
251
+ } = i;
252
252
  for (M in ++o.pMVersion, ++o.vLVersion, o.verList[0] = o.pMVersion + o.vLVersion,
253
- r) r[M] = i[M];
254
- q(p);
255
- let h = o.totalCount, c = p.outdatedCount;
253
+ r) r[M] = p[M];
254
+ q(i);
255
+ let h = o.totalCount, c = i.outdatedCount;
256
256
  for (let [ o ] of t) {
257
257
  var V, I = n[o];
258
258
  let {
259
259
  posMap: a,
260
260
  info: t
261
261
  } = I;
262
- for (V in a) r[V] = i[V], s[V] = p;
262
+ for (V in a) r[V] = p[V], s[V] = i;
263
263
  q(I), h += t.totalCount, c += I.outdatedCount, e.delSnapshot(I);
264
264
  }
265
- o.totalCount = h, p.outdatedCount = c;
265
+ o.totalCount = h, i.outdatedCount = c;
266
266
  }
267
267
  this.snapshotSavedChangeId >= this.compactMaxChangeId ? (L(this), this.compactState = 1) : this.compactState = 3;
268
268
  }
@@ -285,20 +285,20 @@ function l(a) {
285
285
  qySnapshots: o,
286
286
  qySnapshotCompactor: e
287
287
  } = a, {
288
- emptySnapshotNumVersionList: o,
288
+ emptySnapshotNumVerList: o,
289
289
  pMCompactSnapshotList: n,
290
290
  vLCompactSnapshotList: s,
291
- combiningSnapshotList: i
291
+ combiningSnapshotList: p
292
292
  } = o.calOutdates();
293
- if (0 < i.length || 0 < n.length || 0 < s.length) {
293
+ if (0 < p.length || 0 < n.length || 0 < s.length) {
294
294
  Object.assign(a, {
295
295
  compactState: 2,
296
296
  compactMaxChangeId: t
297
297
  });
298
- for (var p of [ n, s, i ]) for (var r of p) r.compactOutedCount = r.outdatedCount;
299
- e.compactSnapshots(n, s, i);
298
+ for (var i of [ n, s, p ]) for (var r of i) r.compactOutedCount = r.outdatedCount;
299
+ e.compactSnapshots(n, s, p);
300
300
  }
301
- 0 < o.length && (a.emptySnapshotNumVersionList.push(...o), a.snapshotSavedChangeId >= t ? h(a) : 1 == a.compactState && Object.assign(a, {
301
+ 0 < o.length && (a.emptySnapshotNumVerList.push(...o), a.snapshotSavedChangeId >= t ? h(a) : 1 == a.compactState && Object.assign(a, {
302
302
  compactState: 3,
303
303
  compactMaxChangeId: t
304
304
  }));
@@ -315,7 +315,7 @@ function d(a, t) {
315
315
  function h(a) {
316
316
  var {
317
317
  qyKVDataCleaner: a,
318
- emptySnapshotNumVersionList: t
318
+ emptySnapshotNumVerList: t
319
319
  } = a;
320
320
  0 < t.length && (a.removeSnapshotsFiles(t), t.length = 0);
321
321
  }
@@ -13,7 +13,7 @@ import {
13
13
  } from "./QyDefaultOptions.js";
14
14
 
15
15
  import {
16
- QyMessageWorker as s
16
+ QyMessageWorker as e
17
17
  } from "./QyMessageWorker.js";
18
18
 
19
19
  import {
@@ -31,10 +31,10 @@ import {
31
31
  } from "./QyLogger.js";
32
32
 
33
33
  import {
34
- getSnapshotInfoFileName as l
34
+ getInfoFileName as l
35
35
  } from "./QySnapshots.js";
36
36
 
37
- export default class extends s {
37
+ export default class extends e {
38
38
  kvFolder;
39
39
  snapshotFolder;
40
40
  snapshotInfoFolder;
@@ -42,82 +42,82 @@ export default class extends s {
42
42
  historyAclFolder;
43
43
  historySnapshotFolder;
44
44
  constructor({
45
- kvFolder: s,
46
- options: e
45
+ kvFolder: e,
46
+ options: s
47
47
  }) {
48
- super(e = {
48
+ super(s = {
49
49
  ...a("QyKVDataCleaner_Worker"),
50
- ...e
50
+ ...s
51
51
  }, [], [ "cleanAclFiles", "cleanPMFiles", "cleanVLFiles", "removeSnapshotsFiles" ]);
52
- var e = m(s, "history"), o = m(s, "snapshot");
52
+ var s = m(e, "history"), o = m(e, "snapshot");
53
53
  Object.assign(this, {
54
- kvFolder: s,
54
+ kvFolder: e,
55
55
  snapshotFolder: o,
56
56
  snapshotInfoFolder: m(o, "info"),
57
- aclFolder: m(s, "acl"),
58
- historyAclFolder: m(e, "acl"),
59
- historySnapshotFolder: m(e, "snapshot")
57
+ aclFolder: m(e, "acl"),
58
+ historyAclFolder: m(s, "acl"),
59
+ historySnapshotFolder: m(s, "snapshot")
60
60
  });
61
61
  }
62
62
  _op_start() {}
63
63
  _op_stop() {}
64
- async _op_cleanAclFiles(s) {
65
- var e = this, o = s, s = e.aclFolder;
66
- if (await d(s)) {
64
+ async _op_cleanAclFiles(e) {
65
+ var s = this, o = e, e = s.aclFolder;
66
+ if (await d(e)) {
67
67
  var a, r = [];
68
- for (a of (await h(s)).filter(s => /^[1-9]/.test(s))) parseInt(a) <= o && r.push((async (s, e) => {
68
+ for (a of (await h(e)).filter(e => /^[1-9]/.test(e))) parseInt(a) <= o && r.push((async (e, s) => {
69
69
  var {
70
70
  aclFolder: o,
71
71
  historyAclFolder: a
72
- } = s;
73
- return await F(a), y(s, e, o, a);
74
- })(e, a));
72
+ } = e;
73
+ return await F(a), y(e, s, o, a);
74
+ })(s, a));
75
75
  0 < r.length && await Promise.allSettled(r);
76
76
  }
77
77
  }
78
- async _op_cleanPMFiles(s) {
79
- await Promise.allSettled(s.map(([ s, e ]) => {
80
- return o = this, a = s, r = e, Promise.all([ "pM", "prefixPM" ].map(s => n(o, a, r, s)));
78
+ async _op_cleanPMFiles(e) {
79
+ await Promise.allSettled(e.map(([ e, s ]) => {
80
+ return o = this, a = e, r = s, Promise.all([ "pM", "prefixPM" ].map(e => n(o, a, r, e)));
81
81
  var o, a, r;
82
82
  }));
83
83
  }
84
- async _op_cleanVLFiles(s, e) {
85
- await n(this, s, e, "vL");
84
+ async _op_cleanVLFiles(e, s) {
85
+ await n(this, e, s, "vL");
86
86
  }
87
- async _op_removeSnapshotsFiles(s) {
87
+ async _op_removeSnapshotsFiles(e) {
88
88
  let {
89
89
  snapshotFolder: o,
90
90
  snapshotInfoFolder: a
91
91
  } = this;
92
- await Promise.all(s.map(async ([ e, s ]) => {
93
- await c(m(a, l(e, s))), await Promise.all([ "pM", "prefixPM", "vL" ].map(s => n(this, e, 1 / 0, s))),
94
- await c(m(o, "" + e), {
92
+ await Promise.all(e.map(async ([ s, e ]) => {
93
+ await c(m(a, l(s, e))), await Promise.all([ "pM", "prefixPM", "vL" ].map(e => n(this, s, 1 / 0, e))),
94
+ await c(m(o, "" + s), {
95
95
  recursive: !0
96
96
  });
97
97
  }));
98
98
  }
99
99
  }
100
100
 
101
- function y(s, e, o, a) {
102
- o = m(o, e), a = m(a, e);
103
- return s.options.compressRedundantFile ? t(o, a + i, {
101
+ function y(e, s, o, a) {
102
+ o = m(o, s), a = m(a, s);
103
+ return e.options.compressRedundantFile ? t(o, a + i, {
104
104
  override: !0,
105
105
  removeSrcAfterSuccess: !0
106
106
  }) : (f.info(`rename ${o} to ` + a), r(o, a));
107
107
  }
108
108
 
109
- async function n(e, o, a, r) {
109
+ async function n(s, o, a, r) {
110
110
  var {
111
- snapshotFolder: s,
111
+ snapshotFolder: e,
112
112
  historySnapshotFolder: t
113
- } = e, i = m(s, "" + o);
113
+ } = s, i = m(e, "" + o);
114
114
  if (await d(i)) {
115
- let s;
115
+ let e;
116
116
  var l, n = [];
117
117
  for (l of await h(i)) {
118
118
  var p = new RegExp("^([0-9]+)_" + r).exec(l);
119
- p && (p = parseInt(p[1])) < a && (1 == p ? (s || (s = m(t, "" + o),
120
- await F(s)), n.push(y(e, l, i, s))) : (p = m(i, l), f.info("Removing " + p),
119
+ p && (p = parseInt(p[1])) < a && (1 == p ? (e || (e = m(t, "" + o),
120
+ await F(e)), n.push(y(s, l, i, e))) : (p = m(i, l), f.info("Removing " + p),
121
121
  n.push(c(p))));
122
122
  }
123
123
  0 < n.length && await Promise.allSettled(n);
package/src/QyLogger.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export declare const ThreadMark: string;
1
2
  declare class QyLogger {
2
3
  _level: number;
3
4
  fileConsole: any;
package/src/QyLogger.js CHANGED
@@ -126,5 +126,6 @@ function p(o) {
126
126
  var f = new h();
127
127
 
128
128
  export {
129
+ a as ThreadMark,
129
130
  f as logger
130
131
  };
@@ -14,11 +14,12 @@ import {
14
14
  } from "node:worker_threads";
15
15
 
16
16
  import {
17
- logger as s
17
+ logger as s,
18
+ ThreadMark as n
18
19
  } from "./QyLogger.js";
19
20
 
20
21
  import {
21
- __dirname as n
22
+ __dirname as m
22
23
  } from "./QyUtils.js";
23
24
 
24
25
  (async () => {
@@ -27,5 +28,5 @@ import {
27
28
  workerTypeFileName: e,
28
29
  initArgMap: r
29
30
  } = i;
30
- new (await import(t("file://" + o(n, e)))).default(r);
31
+ new (await import(t("file://" + o(m, e)))).default(r), s.log("" + n + e);
31
32
  })();
@@ -6,7 +6,7 @@ export default class QySnapshotCompactor_Worker extends QyMessageWorker {
6
6
  qyKVDataCleaner: any;
7
7
  pMInfoMap: any;
8
8
  vLInfoMap: any;
9
- combinedSnapshotNumVersionList: any[];
9
+ combinedSnapshotNumVerList: any[];
10
10
  constructor({ kvFolder, options }: {
11
11
  kvFolder: any;
12
12
  options: any;
@@ -35,7 +35,7 @@ export default class extends a {
35
35
  qyKVDataCleaner;
36
36
  pMInfoMap = {};
37
37
  vLInfoMap = {};
38
- combinedSnapshotNumVersionList = [];
38
+ combinedSnapshotNumVerList = [];
39
39
  constructor({
40
40
  kvFolder: a,
41
41
  options: s
@@ -97,7 +97,7 @@ export default class extends a {
97
97
  let {
98
98
  qySnapshots: o,
99
99
  vLInfoMap: e,
100
- combinedSnapshotNumVersionList: p
100
+ combinedSnapshotNumVerList: p
101
101
  } = t, n = o.getMaxCombinedSnapshot(a), {
102
102
  snapshotNum: i,
103
103
  info: r,
@@ -122,7 +122,7 @@ export default class extends a {
122
122
  }
123
123
  await o.saveKeyValueMap(n), e[i] = r, s.combinationUpdate = {
124
124
  snapshotNum: i,
125
- combinedSnapshotNumVersionList: p,
125
+ combinedSnapshotNumVerList: p,
126
126
  posMap: n.posMap
127
127
  };
128
128
  })(this, s, t)), await Promise.all(e), this.castParent("onCompactUpdates", t);
@@ -132,7 +132,7 @@ export default class extends a {
132
132
  qySnapshots: o,
133
133
  pMInfoMap: t,
134
134
  vLInfoMap: e,
135
- combinedSnapshotNumVersionList: p,
135
+ combinedSnapshotNumVerList: p,
136
136
  qyKVDataCleaner: n
137
137
  } = this, i = (Object.assign(this, {
138
138
  pMInfoMap: {},
@@ -87,7 +87,7 @@ export default class extends a {
87
87
  snapshotNum: a,
88
88
  maxChangeId: t
89
89
  } = await a, {
90
- emptySnapshotNumVersionList: s,
90
+ emptySnapshotNumVerList: s,
91
91
  pMChangeList: o,
92
92
  vLChange: n,
93
93
  snapshotInfoMap: p,
@@ -120,7 +120,7 @@ export default class extends a {
120
120
  function h(a, s, e, t) {
121
121
  var o = (a => {
122
122
  var s, e = a.qySnapshots, {
123
- emptySnapshotNumVersionList: t,
123
+ emptySnapshotNumVerList: t,
124
124
  combiningSnapshotList: a,
125
125
  pMCompactSnapshotList: o
126
126
  } = e.calOutdates(), n = {}, p = [], h = [], r = [];
@@ -155,7 +155,7 @@ function h(a, s, e, t) {
155
155
  };
156
156
  }
157
157
  return {
158
- emptySnapshotNumVersionList: t,
158
+ emptySnapshotNumVerList: t,
159
159
  pMChangeList: h,
160
160
  vLChange: r,
161
161
  snapshotInfoMap: n,
@@ -21,7 +21,7 @@ export declare class QySnapshots {
21
21
  loadPartialPosMapBuffer(snapshot: any, prefix: any, buffer: any): void;
22
22
  delSnapshot(snapshot: any): void;
23
23
  calOutdates(): {
24
- emptySnapshotNumVersionList: any[];
24
+ emptySnapshotNumVerList: any[];
25
25
  combiningSnapshotList: any[];
26
26
  pMCompactSnapshotList: any[];
27
27
  vLCompactSnapshotList: any[];
@@ -36,4 +36,4 @@ export declare class QySnapshots {
36
36
  }
37
37
  export declare function iterPMBuffer(buffer: any, fun: any): void;
38
38
  export declare function loadPrefixPMFile(prefixPMFilePath: any): Promise<{}>;
39
- export declare function getSnapshotInfoFileName(snapshotNum: any, version: any): string;
39
+ export declare function getInfoFileName(snapshotNum: any, version: any): string;
@@ -32,9 +32,9 @@ import {
32
32
  } from "./QyBinWriter.js";
33
33
 
34
34
  import {
35
- bufferToNums as g,
36
- numsToBuffer as y,
37
- applyAclChange as c,
35
+ bufferToNums as y,
36
+ numsToBuffer as c,
37
+ applyAclChange as g,
38
38
  iterCmdKeys as S
39
39
  } from "./QyAcl.js";
40
40
 
@@ -46,7 +46,7 @@ import {
46
46
  logger as P
47
47
  } from "./QyLogger.js";
48
48
 
49
- let x = Array.isArray, F = Buffer.from("\n"), L = Buffer.from("'"), O = Buffer.from("z"), V = L[0], J = O[0];
49
+ let x = Array.isArray, F = Buffer.from("\n"), L = Buffer.from("'"), O = Buffer.from("z"), j = L[0], J = O[0];
50
50
 
51
51
  class t {
52
52
  snapshotFolder;
@@ -98,7 +98,7 @@ class t {
98
98
  outdatedCount: 0
99
99
  }), t = (++this.snapshotCount, t > e && (e = t, n = a.maxChangeId),
100
100
  this), a = o;
101
- t = B(t, a), P.info("Loading " + t), a.prefixPM = await b(t);
101
+ t = N(t, a), P.info("Loading " + t), a.prefixPM = await b(t);
102
102
  })), Object.assign(this, {
103
103
  maxSnapshotNum: e,
104
104
  snapshotMaxChangeId: n
@@ -173,7 +173,7 @@ class t {
173
173
  }
174
174
  S(t, t => {
175
175
  t in o || (o[t] = this.getValueSync(t)), this.removeKey(t);
176
- }), c(o, t);
176
+ }), g(o, t);
177
177
  }
178
178
  getValueSync(t) {
179
179
  var o = ((t, o) => {
@@ -190,7 +190,7 @@ class t {
190
190
  posMap: n,
191
191
  prefixPM: s
192
192
  } = o;
193
- if (s = s[a]) return [ s, e ] = s, s = N(t, t.getPMFilePath(o), s, e),
193
+ if (s = s[a]) return [ s, e ] = s, s = T(t, t.getPMFilePath(o), s, e),
194
194
  t.loadPartialPosMapBuffer(o, a, s), n;
195
195
  })(t, n, s);
196
196
  if (i && o in i) return n;
@@ -207,7 +207,7 @@ class t {
207
207
  var [ e, a ] = a;
208
208
  if (0 != a) {
209
209
  var n = this;
210
- if (o = n.getVLFilePath(o), n = N(n, o, e, a)) return (o = n[0]) == V ? n.toString("utf8", 1) : o == J ? m.zstdDecompressSync(n.subarray(1)).toString() : (e = JSON.parse(n),
210
+ if (o = n.getVLFilePath(o), n = T(n, o, e, a)) return (o = n[0]) == j ? n.toString("utf8", 1) : o == J ? m.zstdDecompressSync(n.subarray(1)).toString() : (e = JSON.parse(n),
211
211
  I(t) ? f(e) : e);
212
212
  }
213
213
  }
@@ -218,7 +218,7 @@ class t {
218
218
  posMap: e,
219
219
  prefixPM: n
220
220
  } = a, s = (delete n[t], this).keyToSnapshotMap;
221
- j(o, (t, o) => {
221
+ V(o, (t, o) => {
222
222
  t in s ? ++a.outdatedCount : (e[t] = o, s[t] = a);
223
223
  });
224
224
  }
@@ -227,25 +227,25 @@ class t {
227
227
  o(this, this.getPMFilePath(t));
228
228
  }
229
229
  calOutdates() {
230
- var t, o = [], a = [], e = [], n = [], {
231
- options: s,
232
- numToSnapshotMap: r
230
+ var t, o, a, e = [], n = [], s = [], r = [], {
231
+ options: i,
232
+ numToSnapshotMap: p
233
233
  } = this, {
234
- pMOutdatedPercent: i,
235
- vLOutdatedPercent: p
236
- } = s;
237
- for (t in r) {
238
- var u, f = r[t];
239
- f.isOut || (100 == (u = Q(f)) ? (this.delSnapshot(f), o.push([ t, f.info.verList[0] ])) : p < u ? (T(f) ? a : n).push(f) : Math.floor(f.outdatedCount / f.info.totalCount * 100) > i ? e.push(f) : (u = this,
240
- f.info.totalCount < u.options.minSnapshotKeyCount && T(f) && a.push(f)));
234
+ pMOutdatedPercent: u,
235
+ vLOutdatedPercent: f
236
+ } = i;
237
+ for (t in p) {
238
+ var h, l = p[t];
239
+ l.isOut || (100 == (h = Q(l)) ? (this.delSnapshot(l), e.push([ t, l.info.verList[0] ])) : f < h ? (B(l) ? n : r).push(l) : Math.floor(l.outdatedCount / l.info.totalCount * 100) > u ? s.push(l) : (h = this,
240
+ l.info.totalCount < h.options.minSnapshotKeyCount && B(l) && n.push(l)));
241
241
  }
242
- return 1 == a.length && Q(a[0]) < p && (a.length = 0), 0 < e.length && P.log("pMCompacts:" + e.map(t => t.snapshotNum)),
243
- 0 < n.length && P.log("vLCompacts:" + n.map(t => t.snapshotNum)), 0 < a.length && P.log("combiningSnapshots:" + a.map(t => t.snapshotNum + "." + t.info.verList[0])),
244
- {
245
- emptySnapshotNumVersionList: o,
246
- combiningSnapshotList: a,
247
- pMCompactSnapshotList: e,
248
- vLCompactSnapshotList: n
242
+ 1 == n.length && Q(n[0]) < f && (n.length = 0);
243
+ for ([ o, a ] of [ [ "pMCompacts", s ], [ "vLCompacts", r ], [ "combiningSnapshots", n ] ]) 0 < a.length && P.log(o + ":" + a.map(t => t.snapshotNum + "." + t.info.verList[0]));
244
+ return {
245
+ emptySnapshotNumVerList: e,
246
+ combiningSnapshotList: n,
247
+ pMCompactSnapshotList: s,
248
+ vLCompactSnapshotList: r
249
249
  };
250
250
  }
251
251
  getMaxCombinedSnapshot(t) {
@@ -278,7 +278,7 @@ class t {
278
278
  outdatedCount: r,
279
279
  totalCount: i
280
280
  } = o, p = s[0], s = s[0] = e + n;
281
- return o.outdated = Math.floor(100 * r / i) + "%", P.log(`Saving snapshotInfo:${t}.${s} oldVersion:` + p),
281
+ return o.outdated = Math.floor(100 * r / i) + "%", P.log(`Saving snapshotInfo:${p ? t + `.${p}->` : ""}${t}.` + s),
282
282
  !!await a.saveToFile({
283
283
  ...o,
284
284
  verList: void 0
@@ -295,12 +295,12 @@ class t {
295
295
  for (a of o = o || Object.keys(s).sort()) {
296
296
  null == f ? f = w(a) : a.startsWith(f) || (i[f] = [ p, u ], f = w(a),
297
297
  p += u, u = 0);
298
- var h = Buffer.from(a), l = y(s[a]);
298
+ var h = Buffer.from(a), l = c(s[a]);
299
299
  r.save([ h, l ]), u += h.length + l.length;
300
300
  }
301
301
  return null != f && (i[f] = [ p, u ]), Promise.all([ r.stop(), ((t, o) => {
302
- var a, e = new M(), n = (e.start(B(t, o)), o).prefixPM;
303
- for (a in n) e.save([ Buffer.from(a), y(n[a]) ]);
302
+ var a, e = new M(), n = (e.start(N(t, o)), o).prefixPM;
303
+ for (a in n) e.save([ Buffer.from(a), c(n[a]) ]);
304
304
  return e.stop();
305
305
  })(this, t) ]);
306
306
  }
@@ -320,11 +320,11 @@ class t {
320
320
  }
321
321
  }
322
322
 
323
- function j(o, a) {
323
+ function V(o, a) {
324
324
  var e = o.length;
325
325
  for (let t = 0; t < e; ) {
326
326
  var n = t + u, s = o.toString("latin1", t, n), r = [];
327
- t = g(o, n, r), a(s, r);
327
+ t = y(o, n, r), a(s, r);
328
328
  }
329
329
  }
330
330
 
@@ -332,12 +332,12 @@ async function b(t) {
332
332
  var o = await r(t), a = o.length, e = {};
333
333
  for (let t = 0; t < a; ) {
334
334
  var n = t + k, s = e[o.toString("latin1", t, n)] = [];
335
- t = g(o, n, s);
335
+ t = y(o, n, s);
336
336
  }
337
337
  return e;
338
338
  }
339
339
 
340
- function N(t, o, a, e) {
340
+ function T(t, o, a, e) {
341
341
  var n = ((t, o) => {
342
342
  let a = t.fDMap, e = a[o];
343
343
  if (!e) {
@@ -358,7 +358,7 @@ function N(t, o, a, e) {
358
358
  return s;
359
359
  }
360
360
 
361
- function T(t) {
361
+ function B(t) {
362
362
  var {
363
363
  keyValueMap: o,
364
364
  prefixPM: a
@@ -366,7 +366,7 @@ function T(t) {
366
366
  return o || null == a || h(t.prefixPM);
367
367
  }
368
368
 
369
- function B(t, o) {
369
+ function N(t, o) {
370
370
  var {
371
371
  snapshotNum: o,
372
372
  info: a
@@ -403,7 +403,7 @@ function D(t, o) {
403
403
 
404
404
  export {
405
405
  t as QySnapshots,
406
- j as iterPMBuffer,
406
+ V as iterPMBuffer,
407
407
  b as loadPrefixPMFile,
408
- D as getSnapshotInfoFileName
408
+ D as getInfoFileName
409
409
  };
package/src/simulate2.js CHANGED
@@ -101,7 +101,7 @@ function b(t) {
101
101
  await !console.timeEnd("loadData"), console.log(`airports:${d.length}, flights:` + h.length);
102
102
  var s = new n(p, {
103
103
  fileLogLevel: "basic",
104
- maxInMemSnapshotCount: 2
104
+ maxInMemSnapshotCount: 3
105
105
  }), r = (t && (await s.start(), b(s), await s.stop()), await s.start(), g(s),
106
106
  `simulate ${e} ` + o), i = (console.time(r), console.time("simulateStop"), []);
107
107
  for (let t = 0; t < e; ++t) i.push(y(s, o));