json-fs-query 3.1.4 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +257 -15
  2. package/browser/jsonFsQuery.js +3884 -5057
  3. package/browser/jsonFsQuery.min.js +1 -1
  4. package/dist/data/introspect/alter.d.ts +12 -0
  5. package/dist/data/introspect/alter.js +304 -0
  6. package/dist/data/introspect/alter.min.js +1 -0
  7. package/dist/data/introspect/create.d.ts +10 -0
  8. package/dist/data/introspect/create.js +9 -23
  9. package/dist/data/introspect/create.min.js +1 -1
  10. package/dist/data/introspect/drop.js +7 -15
  11. package/dist/data/introspect/drop.min.js +1 -1
  12. package/dist/data/introspect/formatDatabase.d.ts +17 -0
  13. package/dist/data/introspect/formatDatabase.js +56 -0
  14. package/dist/data/introspect/formatDatabase.min.js +1 -0
  15. package/dist/data/introspect/index.d.ts +9 -0
  16. package/dist/data/introspect/index.js +18 -5
  17. package/dist/data/introspect/index.min.js +1 -1
  18. package/dist/data/introspect/packDatabase.d.ts +27 -0
  19. package/dist/data/introspect/packDatabase.js +113 -0
  20. package/dist/data/introspect/packDatabase.min.js +1 -0
  21. package/dist/data/introspect/policies/foreignKeyExists.d.ts +2 -0
  22. package/dist/data/introspect/policies/foreignKeyExists.js +9 -3
  23. package/dist/data/introspect/policies/foreignKeyExists.min.js +1 -1
  24. package/dist/data/introspect/policies/hasDependents.d.ts +3 -3
  25. package/dist/data/introspect/policies/hasDependents.js +11 -3
  26. package/dist/data/introspect/policies/hasDependents.min.js +1 -1
  27. package/dist/data/introspect/policies/keyUnique.d.ts +3 -0
  28. package/dist/data/introspect/policies/keyUnique.js +10 -0
  29. package/dist/data/introspect/policies/keyUnique.min.js +1 -1
  30. package/dist/data/queries/databaseStatements.d.ts +31 -0
  31. package/dist/data/queries/databaseStatements.js +97 -0
  32. package/dist/data/queries/databaseStatements.min.js +1 -0
  33. package/dist/data/queries/deleteEntity.js +35 -19
  34. package/dist/data/queries/deleteEntity.min.js +1 -1
  35. package/dist/data/queries/insertEntity.d.ts +8 -0
  36. package/dist/data/queries/insertEntity.js +340 -43
  37. package/dist/data/queries/insertEntity.min.js +1 -1
  38. package/dist/data/queries/joinEntity.d.ts +15 -0
  39. package/dist/data/queries/joinEntity.js +91 -15
  40. package/dist/data/queries/joinEntity.min.js +1 -1
  41. package/dist/data/queries/query.d.ts +2 -1
  42. package/dist/data/queries/query.js +5 -2
  43. package/dist/data/queries/query.min.js +1 -1
  44. package/dist/data/queries/selectFields.d.ts +5 -4
  45. package/dist/data/queries/selectFields.js +25 -11
  46. package/dist/data/queries/selectFields.min.js +1 -1
  47. package/dist/data/queries/updateEntity.js +54 -15
  48. package/dist/data/queries/updateEntity.min.js +1 -1
  49. package/dist/data/storage/databases.d.ts +32 -0
  50. package/dist/data/storage/databases.js +93 -0
  51. package/dist/data/storage/databases.min.js +1 -0
  52. package/dist/data/storage/fileExists.d.ts +7 -0
  53. package/dist/data/storage/fileExists.js +25 -0
  54. package/dist/data/storage/fileExists.min.js +1 -0
  55. package/dist/data/storage/index.d.ts +19 -0
  56. package/dist/data/storage/index.js +124 -0
  57. package/dist/data/storage/index.min.js +1 -0
  58. package/dist/data/storage/listFiles.d.ts +8 -0
  59. package/dist/data/storage/listFiles.js +52 -0
  60. package/dist/data/storage/listFiles.min.js +1 -0
  61. package/dist/data/storage/makeDirectory.d.ts +7 -0
  62. package/dist/data/storage/makeDirectory.js +24 -0
  63. package/dist/data/storage/makeDirectory.min.js +1 -0
  64. package/dist/data/storage/nodeFiles.d.ts +6 -0
  65. package/dist/data/storage/nodeFiles.js +12 -0
  66. package/dist/data/storage/nodeFiles.min.js +1 -0
  67. package/dist/data/storage/readCache.d.ts +37 -0
  68. package/dist/data/storage/readCache.js +117 -0
  69. package/dist/data/storage/readCache.min.js +1 -0
  70. package/dist/data/storage/readJson.d.ts +16 -0
  71. package/dist/data/storage/readJson.js +38 -0
  72. package/dist/data/storage/readJson.min.js +1 -0
  73. package/dist/data/storage/removeDirectory.d.ts +8 -0
  74. package/dist/data/storage/removeDirectory.js +30 -0
  75. package/dist/data/storage/removeDirectory.min.js +1 -0
  76. package/dist/data/storage/removeFile.d.ts +8 -0
  77. package/dist/data/storage/removeFile.js +31 -0
  78. package/dist/data/storage/removeFile.min.js +1 -0
  79. package/dist/data/storage/storagePath.d.ts +11 -0
  80. package/dist/data/storage/storagePath.js +25 -0
  81. package/dist/data/storage/storagePath.min.js +1 -0
  82. package/dist/data/storage/writeJson.d.ts +17 -0
  83. package/dist/data/storage/writeJson.js +37 -0
  84. package/dist/data/storage/writeJson.min.js +1 -0
  85. package/dist/data/storage/writeSet.d.ts +64 -0
  86. package/dist/data/storage/writeSet.js +306 -0
  87. package/dist/data/storage/writeSet.min.js +1 -0
  88. package/dist/data/utilities/compareAs.d.ts +1 -0
  89. package/dist/data/utilities/compareAs.js +2 -2
  90. package/dist/data/utilities/compareAs.min.js +1 -1
  91. package/dist/data/utilities/config.d.ts +37 -0
  92. package/dist/data/utilities/config.js +77 -5
  93. package/dist/data/utilities/config.min.js +1 -1
  94. package/dist/data/utilities/databaseLock.d.ts +7 -0
  95. package/dist/data/utilities/databaseLock.js +19 -4
  96. package/dist/data/utilities/databaseLock.min.js +1 -1
  97. package/dist/data/utilities/deregisterRecord.js +4 -10
  98. package/dist/data/utilities/deregisterRecord.min.js +1 -1
  99. package/dist/data/utilities/getConditionIndexedList.js +3 -4
  100. package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
  101. package/dist/data/utilities/indexBuffer.d.ts +35 -0
  102. package/dist/data/utilities/indexBuffer.js +139 -0
  103. package/dist/data/utilities/indexBuffer.min.js +1 -0
  104. package/dist/data/utilities/indexHas.d.ts +13 -0
  105. package/dist/data/utilities/indexHas.js +48 -0
  106. package/dist/data/utilities/indexHas.min.js +1 -0
  107. package/dist/data/utilities/joinBases.d.ts +29 -0
  108. package/dist/data/utilities/joinBases.js +128 -0
  109. package/dist/data/utilities/joinBases.min.js +1 -0
  110. package/dist/data/utilities/joinKey.d.ts +17 -0
  111. package/dist/data/utilities/joinKey.js +31 -0
  112. package/dist/data/utilities/joinKey.min.js +1 -0
  113. package/dist/data/utilities/keyIndexFiles.d.ts +16 -0
  114. package/dist/data/utilities/keyIndexFiles.js +60 -0
  115. package/dist/data/utilities/keyIndexFiles.min.js +1 -0
  116. package/dist/data/utilities/knownFields.d.ts +26 -0
  117. package/dist/data/utilities/knownFields.js +128 -0
  118. package/dist/data/utilities/knownFields.min.js +1 -0
  119. package/dist/data/utilities/packedTable.d.ts +34 -0
  120. package/dist/data/utilities/packedTable.js +60 -0
  121. package/dist/data/utilities/packedTable.min.js +1 -0
  122. package/dist/data/utilities/parsers/findInsertValues.d.ts +3 -3
  123. package/dist/data/utilities/parsers/findInsertValues.js +28 -12
  124. package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
  125. package/dist/data/utilities/parsers/maskStrings.js +11 -2
  126. package/dist/data/utilities/parsers/maskStrings.min.js +1 -1
  127. package/dist/data/utilities/parsers/parseAlter.d.ts +33 -0
  128. package/dist/data/utilities/parsers/parseAlter.js +84 -0
  129. package/dist/data/utilities/parsers/parseAlter.min.js +1 -0
  130. package/dist/data/utilities/parsers/parseCreate.d.ts +14 -0
  131. package/dist/data/utilities/parsers/parseCreate.js +13 -5
  132. package/dist/data/utilities/parsers/parseCreate.min.js +1 -1
  133. package/dist/data/utilities/parsers/parseDatabaseStatement.d.ts +17 -0
  134. package/dist/data/utilities/parsers/parseDatabaseStatement.js +82 -0
  135. package/dist/data/utilities/parsers/parseDatabaseStatement.min.js +1 -0
  136. package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
  137. package/dist/data/utilities/parsers/parseQuery.js +24 -0
  138. package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
  139. package/dist/data/utilities/parsers/startsQuery.js +1 -1
  140. package/dist/data/utilities/parsers/startsQuery.min.js +1 -1
  141. package/dist/data/utilities/recordExists.js +2 -7
  142. package/dist/data/utilities/recordExists.min.js +1 -1
  143. package/dist/data/utilities/registerRecord.js +4 -10
  144. package/dist/data/utilities/registerRecord.min.js +1 -1
  145. package/dist/data/utilities/removeFromIndex.js +4 -10
  146. package/dist/data/utilities/removeFromIndex.min.js +1 -1
  147. package/dist/data/utilities/retrieveFile.d.ts +5 -0
  148. package/dist/data/utilities/retrieveFile.js +7 -16
  149. package/dist/data/utilities/retrieveFile.min.js +1 -1
  150. package/dist/data/utilities/retrieveFiles.d.ts +15 -0
  151. package/dist/data/utilities/retrieveFiles.js +48 -0
  152. package/dist/data/utilities/retrieveFiles.min.js +1 -0
  153. package/dist/data/utilities/retrieveRecord.d.ts +6 -0
  154. package/dist/data/utilities/retrieveRecord.js +24 -1
  155. package/dist/data/utilities/retrieveRecord.min.js +1 -1
  156. package/dist/data/utilities/retrieveRecords.d.ts +22 -0
  157. package/dist/data/utilities/retrieveRecords.js +60 -10
  158. package/dist/data/utilities/retrieveRecords.min.js +1 -1
  159. package/dist/data/utilities/rowLookup.d.ts +38 -0
  160. package/dist/data/utilities/rowLookup.js +99 -0
  161. package/dist/data/utilities/rowLookup.min.js +1 -0
  162. package/dist/data/utilities/runQuery.js +28 -4
  163. package/dist/data/utilities/runQuery.min.js +1 -1
  164. package/dist/data/utilities/semiJoin.d.ts +27 -0
  165. package/dist/data/utilities/semiJoin.js +97 -0
  166. package/dist/data/utilities/semiJoin.min.js +1 -0
  167. package/dist/data/utilities/updateIndex.js +5 -14
  168. package/dist/data/utilities/updateIndex.min.js +1 -1
  169. package/dist/index.d.ts +1 -1
  170. package/dist/index.js +24 -0
  171. package/dist/index.min.js +1 -1
  172. package/package.json +10 -4
  173. package/dist/data/utilities/getJoinIndexedList.d.ts +0 -3
  174. package/dist/data/utilities/getJoinIndexedList.js +0 -76
  175. package/dist/data/utilities/getJoinIndexedList.min.js +0 -1
  176. package/dist/data/utilities/getMatchingIndexes.d.ts +0 -2
  177. package/dist/data/utilities/getMatchingIndexes.js +0 -28
  178. package/dist/data/utilities/getMatchingIndexes.min.js +0 -1
  179. package/dist/data/utilities/matchIndexedJoins.d.ts +0 -2
  180. package/dist/data/utilities/matchIndexedJoins.js +0 -63
  181. package/dist/data/utilities/matchIndexedJoins.min.js +0 -1
  182. package/dist/data/utilities/restrictIndexedList.d.ts +0 -2
  183. package/dist/data/utilities/restrictIndexedList.js +0 -45
  184. package/dist/data/utilities/restrictIndexedList.min.js +0 -1
  185. package/dist/data/utilities/useJoinClause.d.ts +0 -6
  186. package/dist/data/utilities/useJoinClause.js +0 -57
  187. package/dist/data/utilities/useJoinClause.min.js +0 -1
@@ -0,0 +1,306 @@
1
+ "use strict";
2
+
3
+ require("core-js/modules/esnext.weak-map.delete-all.js");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.writeAtomically = exports.withWriteSet = exports.stageWrite = exports.stageRemoveDirectory = exports.stageRemove = exports.stageMakeDirectory = exports.pendingListing = exports.pendingFile = exports.pendingExists = void 0;
8
+ require("core-js/modules/esnext.iterator.constructor.js");
9
+ require("core-js/modules/esnext.iterator.filter.js");
10
+ require("core-js/modules/esnext.map.delete-all.js");
11
+ require("core-js/modules/esnext.map.every.js");
12
+ require("core-js/modules/esnext.map.filter.js");
13
+ require("core-js/modules/esnext.map.find.js");
14
+ require("core-js/modules/esnext.map.find-key.js");
15
+ require("core-js/modules/esnext.map.includes.js");
16
+ require("core-js/modules/esnext.map.key-of.js");
17
+ require("core-js/modules/esnext.map.map-keys.js");
18
+ require("core-js/modules/esnext.map.map-values.js");
19
+ require("core-js/modules/esnext.map.merge.js");
20
+ require("core-js/modules/esnext.map.reduce.js");
21
+ require("core-js/modules/esnext.map.some.js");
22
+ require("core-js/modules/esnext.map.update.js");
23
+ require("core-js/modules/esnext.set.add-all.js");
24
+ require("core-js/modules/esnext.set.delete-all.js");
25
+ require("core-js/modules/esnext.set.difference.js");
26
+ require("core-js/modules/esnext.set.every.js");
27
+ require("core-js/modules/esnext.set.filter.js");
28
+ require("core-js/modules/esnext.set.find.js");
29
+ require("core-js/modules/esnext.set.intersection.js");
30
+ require("core-js/modules/esnext.set.is-disjoint-from.js");
31
+ require("core-js/modules/esnext.set.is-subset-of.js");
32
+ require("core-js/modules/esnext.set.is-superset-of.js");
33
+ require("core-js/modules/esnext.set.join.js");
34
+ require("core-js/modules/esnext.set.map.js");
35
+ require("core-js/modules/esnext.set.reduce.js");
36
+ require("core-js/modules/esnext.set.some.js");
37
+ require("core-js/modules/esnext.set.symmetric-difference.js");
38
+ require("core-js/modules/esnext.set.union.js");
39
+ var jsEnv = _interopRequireWildcard(require("browser-or-node"));
40
+ var _storagePath = require("./storagePath");
41
+ var _nodeFiles = require("./nodeFiles");
42
+ var _readCache = require("./readCache");
43
+ var e;
44
+ function _interopRequireWildcard(t, r) { if (!r && t && t.__esModule) return t; var n, a, o = Object.defineProperty, f = { __proto__: null, default: t }; if (Object(t) !== t) return f; if (e || "function" != typeof WeakMap || (e = new WeakMap()), e) { if (e.has(t)) return e.get(t); e.set(t, f); } for (a in t) "default" !== a && {}.hasOwnProperty.call(t, a) && ((n = o && Object.getOwnPropertyDescriptor(t, a)) && (n.get || n.set) ? o(f, a, n) : f[a] = t[a]); return f; }
45
+ const writeSets = new Map();
46
+ /**
47
+ * The write set of the database configured now (by its location, see storagePath), if a statement on it is running.
48
+ */
49
+ const currentWriteSet = () => writeSets.get((0, _storagePath.storagePath)());
50
+ const folderPrefix = location => location.endsWith('/') ? location : `${location}/`;
51
+ /**
52
+ * Whether `location` is inside one of the folders `folders` (or is one of them).
53
+ * @param location
54
+ * @param folders
55
+ */
56
+ const insideAny = (location, folders) => {
57
+ for (const folder of folders) {
58
+ if (location === folder || location.startsWith(folderPrefix(folder))) {
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ };
64
+ /**
65
+ * Write `text` to `location` so that the file is always either its old or its new content, never half of one: the
66
+ * text goes to a temporary file next to it, which then replaces it (a rename is atomic within one folder). That holds
67
+ * when the process stops part way (a crash, a kill); there's no fsync, so after a power cut the file system may still
68
+ * lose a recent write. When the write or rename fails, the temporary file is removed and the original error thrown.
69
+ * @param location
70
+ * @param text
71
+ */
72
+ const writeAtomically = async (location, text) => {
73
+ const temporary = `${location}.${crypto.randomUUID()}.tmp`;
74
+ const files = (0, _nodeFiles.nodeFiles)();
75
+ try {
76
+ await files.writeFile(temporary, text);
77
+ await files.rename(temporary, location);
78
+ } catch (error) {
79
+ // Cleaning up is best effort: its own failure must not hide why the write failed
80
+ await files.rm(temporary, {
81
+ force: true
82
+ }).catch(() => undefined);
83
+ throw error;
84
+ }
85
+ };
86
+ /**
87
+ * Remove a folder from storage and from the read cache.
88
+ * @param location
89
+ */
90
+ exports.writeAtomically = writeAtomically;
91
+ const removeFolder = async location => {
92
+ (0, _readCache.cacheRemove)(location, true);
93
+ await (0, _nodeFiles.nodeFiles)().rm(location, {
94
+ recursive: true,
95
+ force: true
96
+ });
97
+ (0, _readCache.cacheRemove)(location, true);
98
+ };
99
+ /**
100
+ * Put a write set's changes in storage, in an order where a stop part way leaves at worst an unlisted file or folder
101
+ * behind, never a record file or index that lists something missing:
102
+ * - folders the statement removed and then wrote into again (none today), so the new content survives;
103
+ * - new folders;
104
+ * - every written file once, in the order each was last written (a record's entry list and indexes come after the
105
+ * entries they list);
106
+ * - deleted files (a delete has already rewritten the record file and indexes; `drop` removes its record file here);
107
+ * - the other removed folders (`drop`'s entries and indexes, after its record file is gone).
108
+ * Each change also goes into the read cache, which holds what's in storage.
109
+ * @param writeSet
110
+ */
111
+ const commit = async writeSet => {
112
+ const files = (0, _nodeFiles.nodeFiles)();
113
+ const writtenInto = folder => {
114
+ const prefix = folderPrefix(folder);
115
+ return [...writeSet.madeFolders].some(made => made === folder || made.startsWith(prefix)) || [...writeSet.files].some(([location, text]) => text !== null && location.startsWith(prefix));
116
+ };
117
+ const removedFolders = [...writeSet.removedFolders];
118
+ for (const folder of removedFolders.filter(writtenInto)) {
119
+ await removeFolder(folder);
120
+ }
121
+ for (const folder of writeSet.madeFolders) {
122
+ await files.mkdir(folder, {
123
+ recursive: true
124
+ });
125
+ }
126
+ for (const [location, text] of writeSet.files) {
127
+ if (text !== null) {
128
+ await writeAtomically(location, text);
129
+ (0, _readCache.cacheWrite)(location, text);
130
+ }
131
+ }
132
+ for (const [location, text] of writeSet.files) {
133
+ if (text === null) {
134
+ (0, _readCache.cacheRemove)(location);
135
+ await files.rm(location, {
136
+ force: true
137
+ });
138
+ (0, _readCache.cacheRemove)(location);
139
+ }
140
+ }
141
+ for (const folder of removedFolders.filter(folder => !writtenInto(folder))) {
142
+ await removeFolder(folder);
143
+ }
144
+ };
145
+ /**
146
+ * Run one statement's `task` with a write set for the configured database: its writes and deletes are collected in
147
+ * memory, and reads during the task see them (see pendingFile). When the task finishes, everything it changed is put in
148
+ * storage at once, each file written once however many times the task changed it. When it throws, nothing it changed
149
+ * reaches storage: a multi-row insert or update whose third row fails leaves the first two unwritten too.
150
+ *
151
+ * A task inside another (a public call made from inside a statement) joins the outer write set. Node only: in a
152
+ * browser nothing can be written, so the task just runs.
153
+ * @param task
154
+ */
155
+ const withWriteSet = async task => {
156
+ const database = (0, _storagePath.storagePath)();
157
+ if (!jsEnv.isNode || writeSets.has(database)) {
158
+ return task();
159
+ }
160
+ const writeSet = {
161
+ files: new Map(),
162
+ madeFolders: new Set(),
163
+ removedFolders: new Set()
164
+ };
165
+ writeSets.set(database, writeSet);
166
+ let result;
167
+ try {
168
+ result = await task();
169
+ } finally {
170
+ writeSets.delete(database);
171
+ }
172
+ await commit(writeSet);
173
+ return result;
174
+ };
175
+ /**
176
+ * What the running statement has done to the file at `location`: its pending text, `null` if the statement deleted it
177
+ * (or a folder it was in), or `undefined` when the statement hasn't touched it (or no statement is running).
178
+ * @param location - The file's full location (see storagePath).
179
+ */
180
+ exports.withWriteSet = withWriteSet;
181
+ const pendingFile = location => {
182
+ const writeSet = currentWriteSet();
183
+ if (!writeSet) {
184
+ return undefined;
185
+ }
186
+ if (writeSet.files.has(location)) {
187
+ return writeSet.files.get(location);
188
+ }
189
+ return insideAny(location, writeSet.removedFolders) ? null : undefined;
190
+ };
191
+ /**
192
+ * Whether the file or folder at `location` exists as far as the running statement knows: true for a file it wrote, a
193
+ * folder it made, or a folder holding something it wrote or made; false for a file it deleted or anything in a folder
194
+ * it removed; `undefined` when the statement hasn't touched it (or no statement is running), so storage decides.
195
+ * @param location - The full location (see storagePath).
196
+ */
197
+ exports.pendingFile = pendingFile;
198
+ const pendingExists = location => {
199
+ const writeSet = currentWriteSet();
200
+ if (!writeSet) {
201
+ return undefined;
202
+ }
203
+ if (writeSet.files.has(location)) {
204
+ return writeSet.files.get(location) !== null;
205
+ }
206
+ const prefix = folderPrefix(location);
207
+ const madeInside = path => path === location || path.startsWith(prefix);
208
+ if ([...writeSet.madeFolders].some(madeInside) || [...writeSet.files].some(([path, text]) => text !== null && path.startsWith(prefix))) {
209
+ return true;
210
+ }
211
+ return insideAny(location, writeSet.removedFolders) ? false : undefined;
212
+ };
213
+ /**
214
+ * The files under `folder` as the running statement sees them: `onDisk` (full locations) without what it deleted or
215
+ * removed, plus what it wrote there. `onDisk` as it is when no statement is running.
216
+ * @param folder - The folder's full location (see storagePath).
217
+ * @param onDisk - The full locations of the files under it in storage.
218
+ */
219
+ exports.pendingExists = pendingExists;
220
+ const pendingListing = (folder, onDisk) => {
221
+ const writeSet = currentWriteSet();
222
+ if (!writeSet) {
223
+ return onDisk;
224
+ }
225
+ const prefix = folderPrefix(folder);
226
+ const listed = new Set(onDisk.filter(location => !insideAny(location, writeSet.removedFolders)));
227
+ for (const [location, text] of writeSet.files) {
228
+ if (location.startsWith(prefix)) {
229
+ if (text === null) {
230
+ listed.delete(location);
231
+ } else {
232
+ listed.add(location);
233
+ }
234
+ }
235
+ }
236
+ return [...listed];
237
+ };
238
+ /**
239
+ * Keep `text` as the new content of `location` in the running statement's write set. False when no statement is
240
+ * running, so the caller writes to storage itself.
241
+ * @param location
242
+ * @param text
243
+ */
244
+ exports.pendingListing = pendingListing;
245
+ const stageWrite = (location, text) => {
246
+ const writeSet = currentWriteSet();
247
+ if (!writeSet) {
248
+ return false;
249
+ }
250
+ // Moved to the end, so files are written in the order they were last changed
251
+ writeSet.files.delete(location);
252
+ writeSet.files.set(location, text);
253
+ return true;
254
+ };
255
+ /**
256
+ * Mark `location` deleted in the running statement's write set. False when no statement is running.
257
+ * @param location
258
+ */
259
+ exports.stageWrite = stageWrite;
260
+ const stageRemove = location => {
261
+ const writeSet = currentWriteSet();
262
+ if (!writeSet) {
263
+ return false;
264
+ }
265
+ writeSet.files.delete(location);
266
+ writeSet.files.set(location, null);
267
+ return true;
268
+ };
269
+ /**
270
+ * Note a folder to create in the running statement's write set. False when no statement is running.
271
+ * @param location
272
+ */
273
+ exports.stageRemove = stageRemove;
274
+ const stageMakeDirectory = location => {
275
+ const writeSet = currentWriteSet();
276
+ if (!writeSet) {
277
+ return false;
278
+ }
279
+ writeSet.madeFolders.add(location);
280
+ return true;
281
+ };
282
+ /**
283
+ * Mark a folder and everything in it removed in the running statement's write set: what the statement wrote or
284
+ * created inside it so far is dropped with it. False when no statement is running.
285
+ * @param location
286
+ */
287
+ exports.stageMakeDirectory = stageMakeDirectory;
288
+ const stageRemoveDirectory = location => {
289
+ const writeSet = currentWriteSet();
290
+ if (!writeSet) {
291
+ return false;
292
+ }
293
+ for (const file of [...writeSet.files.keys()]) {
294
+ if (insideAny(file, new Set([location]))) {
295
+ writeSet.files.delete(file);
296
+ }
297
+ }
298
+ for (const folder of [...writeSet.madeFolders]) {
299
+ if (insideAny(folder, new Set([location]))) {
300
+ writeSet.madeFolders.delete(folder);
301
+ }
302
+ }
303
+ writeSet.removedFolders.add(location);
304
+ return true;
305
+ };
306
+ exports.stageRemoveDirectory = stageRemoveDirectory;
@@ -0,0 +1 @@
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.writeAtomically=exports.withWriteSet=exports.stageWrite=exports.stageRemoveDirectory=exports.stageRemove=exports.stageMakeDirectory=exports.pendingListing=exports.pendingFile=exports.pendingExists=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.map.delete-all.js"),require("core-js/modules/esnext.map.every.js"),require("core-js/modules/esnext.map.filter.js"),require("core-js/modules/esnext.map.find.js"),require("core-js/modules/esnext.map.find-key.js"),require("core-js/modules/esnext.map.includes.js"),require("core-js/modules/esnext.map.key-of.js"),require("core-js/modules/esnext.map.map-keys.js"),require("core-js/modules/esnext.map.map-values.js"),require("core-js/modules/esnext.map.merge.js"),require("core-js/modules/esnext.map.reduce.js"),require("core-js/modules/esnext.map.some.js"),require("core-js/modules/esnext.map.update.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js");var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache");function _interopRequireWildcard(r,s){if(!s&&r&&r.__esModule)return r;var t,o,i=Object.defineProperty,n={__proto__:null,default:r};if(Object(r)!==r)return n;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(r))return e.get(r);e.set(r,n)}for(o in r)"default"!==o&&{}.hasOwnProperty.call(r,o)&&((t=i&&Object.getOwnPropertyDescriptor(r,o))&&(t.get||t.set)?i(n,o,t):n[o]=r[o]);return n}const writeSets=new Map,currentWriteSet=()=>writeSets.get((0,_storagePath.storagePath)()),folderPrefix=e=>e.endsWith("/")?e:`${e}/`,insideAny=(e,r)=>{for(const s of r)if(e===s||e.startsWith(folderPrefix(s)))return!0;return!1},writeAtomically=async(e,r)=>{const s=`${e}.${crypto.randomUUID()}.tmp`,t=(0,_nodeFiles.nodeFiles)();try{await t.writeFile(s,r),await t.rename(s,e)}catch(e){throw await t.rm(s,{force:!0}).catch((()=>{})),e}};exports.writeAtomically=writeAtomically;const removeFolder=async e=>{(0,_readCache.cacheRemove)(e,!0),await(0,_nodeFiles.nodeFiles)().rm(e,{recursive:!0,force:!0}),(0,_readCache.cacheRemove)(e,!0)},commit=async e=>{const r=(0,_nodeFiles.nodeFiles)(),s=r=>{const s=folderPrefix(r);return[...e.madeFolders].some((e=>e===r||e.startsWith(s)))||[...e.files].some((([e,r])=>null!==r&&e.startsWith(s)))},t=[...e.removedFolders];for(const e of t.filter(s))await removeFolder(e);for(const s of e.madeFolders)await r.mkdir(s,{recursive:!0});for(const[r,s]of e.files)null!==s&&(await writeAtomically(r,s),(0,_readCache.cacheWrite)(r,s));for(const[s,t]of e.files)null===t&&((0,_readCache.cacheRemove)(s),await r.rm(s,{force:!0}),(0,_readCache.cacheRemove)(s));for(const e of t.filter((e=>!s(e))))await removeFolder(e)},withWriteSet=async e=>{const r=(0,_storagePath.storagePath)();if(!jsEnv.isNode||writeSets.has(r))return e();const s={files:new Map,madeFolders:new Set,removedFolders:new Set};let t;writeSets.set(r,s);try{t=await e()}finally{writeSets.delete(r)}return await commit(s),t};exports.withWriteSet=withWriteSet;const pendingFile=e=>{const r=currentWriteSet();if(r)return r.files.has(e)?r.files.get(e):insideAny(e,r.removedFolders)?null:void 0};exports.pendingFile=pendingFile;const pendingExists=e=>{const r=currentWriteSet();if(!r)return;if(r.files.has(e))return null!==r.files.get(e);const s=folderPrefix(e);return!(![...r.madeFolders].some((r=>r===e||r.startsWith(s)))&&![...r.files].some((([e,r])=>null!==r&&e.startsWith(s))))||!insideAny(e,r.removedFolders)&&void 0};exports.pendingExists=pendingExists;const pendingListing=(e,r)=>{const s=currentWriteSet();if(!s)return r;const t=folderPrefix(e),o=new Set(r.filter((e=>!insideAny(e,s.removedFolders))));for(const[e,r]of s.files)e.startsWith(t)&&(null===r?o.delete(e):o.add(e));return[...o]};exports.pendingListing=pendingListing;const stageWrite=(e,r)=>{const s=currentWriteSet();return!!s&&(s.files.delete(e),s.files.set(e,r),!0)};exports.stageWrite=stageWrite;const stageRemove=e=>{const r=currentWriteSet();return!!r&&(r.files.delete(e),r.files.set(e,null),!0)};exports.stageRemove=stageRemove;const stageMakeDirectory=e=>{const r=currentWriteSet();return!!r&&(r.madeFolders.add(e),!0)};exports.stageMakeDirectory=stageMakeDirectory;const stageRemoveDirectory=e=>{const r=currentWriteSet();if(!r)return!1;for(const s of[...r.files.keys()])insideAny(s,new Set([e]))&&r.files.delete(s);for(const s of[...r.madeFolders])insideAny(s,new Set([e]))&&r.madeFolders.delete(s);return r.removedFolders.add(e),!0};exports.stageRemoveDirectory=stageRemoveDirectory;
@@ -8,6 +8,7 @@
8
8
  * @param value
9
9
  */
10
10
  export declare const compareAs: (type: string | undefined, value: any) => any;
11
+ export declare const comparableTypes: string[];
11
12
  /**
12
13
  * Whether two values are equal for a field of the given declared type: both are normalized to
13
14
  * that type (see compareAs) and compared strictly. A field with no declared type, or one that
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.sameValue = exports.compareAs = void 0;
6
+ exports.sameValue = exports.compareAs = exports.comparableTypes = void 0;
7
7
  require("core-js/modules/es.array.includes.js");
8
8
  /**
9
9
  * Normalize a value to a field's declared type for an equality comparison - never written back,
@@ -39,7 +39,7 @@ const compareAs = (type, value) => {
39
39
  }
40
40
  };
41
41
  exports.compareAs = compareAs;
42
- const comparableTypes = ['string', 'int', 'boolean'];
42
+ const comparableTypes = exports.comparableTypes = ['string', 'int', 'boolean'];
43
43
  /**
44
44
  * Whether two values are equal for a field of the given declared type: both are normalized to
45
45
  * that type (see compareAs) and compared strictly. A field with no declared type, or one that
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.sameValue=exports.compareAs=void 0,require("core-js/modules/es.array.includes.js");const compareAs=(e,r)=>{if(null==r)return r;switch(e){case"string":return"object"==typeof r?r:String(r);case"int":{const e=Number(r);return Number.isNaN(e)?r:e}case"boolean":return"true"===r||"false"!==r&&r;default:return r}};exports.compareAs=compareAs;const comparableTypes=["string","int","boolean"],sameValue=(e,r,s)=>comparableTypes.includes(e)?compareAs(e,r)===compareAs(e,s):r==s;exports.sameValue=sameValue;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.sameValue=exports.compareAs=exports.comparableTypes=void 0,require("core-js/modules/es.array.includes.js");const compareAs=(e,r)=>{if(null==r)return r;switch(e){case"string":return"object"==typeof r?r:String(r);case"int":{const e=Number(r);return Number.isNaN(e)?r:e}case"boolean":return"true"===r||"false"!==r&&r;default:return r}};exports.compareAs=compareAs;const comparableTypes=exports.comparableTypes=["string","int","boolean"],sameValue=(e,r,s)=>comparableTypes.includes(e)?compareAs(e,r)===compareAs(e,s):r==s;exports.sameValue=sameValue;
@@ -2,7 +2,33 @@ export type RuntimeSettings = {
2
2
  databasePath: string;
3
3
  relativePath: string;
4
4
  enforceForeignKeys: boolean;
5
+ readConcurrency: number;
6
+ format: 'compact' | 'pretty';
7
+ packedReads?: boolean;
8
+ databasesPath?: string;
9
+ database?: string;
5
10
  };
11
+ /**
12
+ * The folder of the database in use: `<databasesPath>/<database>/` once both are configured (see configure and the
13
+ * `use` statement), otherwise databasePath, as before there could be several.
14
+ */
15
+ export declare const activeDatabasePath: () => string;
16
+ /**
17
+ * The folder whose calls are queued together (see withDatabaseLock): with several databases, all of them, since a
18
+ * call can switch between them (`use`) part way; otherwise the one database.
19
+ */
20
+ export declare const lockPath: () => string;
21
+ /**
22
+ * The folder holding the databases, for create database, drop database, use and show databases.
23
+ * @throws {Error} When databasesPath isn't configured.
24
+ */
25
+ export declare const databasesFolder: () => string;
26
+ /**
27
+ * Switch the database in use, from inside a call (the `use` statement, which holds the lock for every database in
28
+ * databasesPath, so no other call is running): configure() refuses to while calls run.
29
+ * @param name
30
+ */
31
+ export declare const switchDatabase: (name: string) => void;
6
32
  /**
7
33
  * The folder a databasePath points to, so that `database/`, `./database` and its absolute path count as one database.
8
34
  * Node only (only Node calls are counted, see trackDatabaseCall).
@@ -26,9 +52,20 @@ export declare const trackDatabaseCall: () => (() => void);
26
52
  * mirroring MySQL's own foreign_key_checks setting, which is also toggleable rather than always
27
53
  * on, since strict enforcement isn't always what every consumer wants.
28
54
  *
55
+ * readConcurrency (default 16) is how many entry files a read has in flight at once (see retrieveFiles): in a browser,
56
+ * how many requests run together.
57
+ *
58
+ * format (default 'compact') is how files are written: 'compact' (no whitespace: smaller files that parse faster) or
59
+ * 'pretty' (indented by 2 spaces, for reading data in diffs). Either is read; formatDatabase rewrites a whole database
60
+ * in one.
61
+ *
62
+ * packedReads (default: true in a browser, false in Node) is whether reads use a record's packed file (see
63
+ * packDatabase) when it has one that matches the record, instead of reading each entry file.
64
+ *
29
65
  * Changing databasePath while calls are still running or queued throws (see trackDatabaseCall): await them first.
30
66
  * @param newSettings
31
67
  * @throws {Error} When databasePath would point to another folder while database calls are in progress.
68
+ * @throws {Error} When format is neither 'compact' nor 'pretty'.
32
69
  */
33
70
  export declare const configure: (newSettings: Partial<RuntimeSettings>) => void;
34
71
  /**
@@ -3,21 +3,61 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.trackDatabaseCall = exports.resolveDatabasePath = exports.getSetting = exports.configure = void 0;
6
+ exports.trackDatabaseCall = exports.switchDatabase = exports.resolveDatabasePath = exports.lockPath = exports.getSetting = exports.databasesFolder = exports.configure = exports.activeDatabasePath = void 0;
7
+ require("core-js/modules/es.json.stringify.js");
7
8
  const defaults = {
8
9
  databasePath: 'database/',
9
10
  relativePath: '',
10
- enforceForeignKeys: true
11
+ enforceForeignKeys: true,
12
+ readConcurrency: 16,
13
+ format: 'compact'
11
14
  };
12
15
  let settings = {
13
16
  ...defaults
14
17
  };
15
18
  let activeCalls = 0;
19
+ const databaseNameMatch = /^[a-z0-9_-]+$/i;
20
+ const withSlash = path => path.endsWith('/') ? path : `${path}/`;
21
+ /**
22
+ * The folder of the database in use: `<databasesPath>/<database>/` once both are configured (see configure and the
23
+ * `use` statement), otherwise databasePath, as before there could be several.
24
+ */
25
+ const activeDatabasePath = () => settings.databasesPath && settings.database ? `${withSlash(settings.databasesPath)}${settings.database}/` : getSetting('databasePath', 'database/');
26
+ /**
27
+ * The folder whose calls are queued together (see withDatabaseLock): with several databases, all of them, since a
28
+ * call can switch between them (`use`) part way; otherwise the one database.
29
+ */
30
+ exports.activeDatabasePath = activeDatabasePath;
31
+ const lockPath = () => settings.databasesPath ? withSlash(settings.databasesPath) : activeDatabasePath();
32
+ /**
33
+ * The folder holding the databases, for create database, drop database, use and show databases.
34
+ * @throws {Error} When databasesPath isn't configured.
35
+ */
36
+ exports.lockPath = lockPath;
37
+ const databasesFolder = () => {
38
+ if (!settings.databasesPath) {
39
+ throw new Error("Several databases need a folder to hold them: configure({ databasesPath: 'databases/' }) first");
40
+ }
41
+ return withSlash(settings.databasesPath);
42
+ };
43
+ /**
44
+ * Switch the database in use, from inside a call (the `use` statement, which holds the lock for every database in
45
+ * databasesPath, so no other call is running): configure() refuses to while calls run.
46
+ * @param name
47
+ */
48
+ exports.databasesFolder = databasesFolder;
49
+ const switchDatabase = name => {
50
+ settings = {
51
+ ...settings,
52
+ database: name
53
+ };
54
+ };
16
55
  /**
17
56
  * The folder a databasePath points to, so that `database/`, `./database` and its absolute path count as one database.
18
57
  * Node only (only Node calls are counted, see trackDatabaseCall).
19
58
  * @param databasePath
20
59
  */
60
+ exports.switchDatabase = switchDatabase;
21
61
  const resolveDatabasePath = databasePath => require('path').resolve(databasePath);
22
62
  /**
23
63
  * Count a database call (taken by withDatabaseLock as soon as a call is queued) until the returned function is called.
@@ -46,19 +86,51 @@ const trackDatabaseCall = () => {
46
86
  * mirroring MySQL's own foreign_key_checks setting, which is also toggleable rather than always
47
87
  * on, since strict enforcement isn't always what every consumer wants.
48
88
  *
89
+ * readConcurrency (default 16) is how many entry files a read has in flight at once (see retrieveFiles): in a browser,
90
+ * how many requests run together.
91
+ *
92
+ * format (default 'compact') is how files are written: 'compact' (no whitespace: smaller files that parse faster) or
93
+ * 'pretty' (indented by 2 spaces, for reading data in diffs). Either is read; formatDatabase rewrites a whole database
94
+ * in one.
95
+ *
96
+ * packedReads (default: true in a browser, false in Node) is whether reads use a record's packed file (see
97
+ * packDatabase) when it has one that matches the record, instead of reading each entry file.
98
+ *
49
99
  * Changing databasePath while calls are still running or queued throws (see trackDatabaseCall): await them first.
50
100
  * @param newSettings
51
101
  * @throws {Error} When databasePath would point to another folder while database calls are in progress.
102
+ * @throws {Error} When format is neither 'compact' nor 'pretty'.
52
103
  */
53
104
  exports.trackDatabaseCall = trackDatabaseCall;
54
105
  const configure = newSettings => {
55
- if (activeCalls > 0 && typeof newSettings.databasePath !== 'undefined' && resolveDatabasePath(newSettings.databasePath) !== resolveDatabasePath(settings.databasePath)) {
56
- throw new Error(`Can't change databasePath while ${activeCalls} database call(s) are still running or queued: await them first`);
106
+ if (typeof newSettings.format !== 'undefined' && newSettings.format !== 'compact' && newSettings.format !== 'pretty') {
107
+ throw new Error(`Unknown format "${newSettings.format}": use 'compact' or 'pretty'`);
57
108
  }
58
- settings = {
109
+ if (typeof newSettings.database !== 'undefined' && newSettings.database !== null && !databaseNameMatch.test(newSettings.database)) {
110
+ throw new Error(`Invalid database name ${JSON.stringify(newSettings.database)}: names use letters, digits, _ and - only`);
111
+ }
112
+ const next = {
59
113
  ...settings,
60
114
  ...newSettings
61
115
  };
116
+ if (next.database && !next.databasesPath) {
117
+ throw new Error("A database name needs the folder that holds the databases: configure({ databasesPath: 'databases/', database: '...' })");
118
+ }
119
+ if (activeCalls > 0) {
120
+ const before = {
121
+ active: resolveDatabasePath(activeDatabasePath()),
122
+ lock: resolveDatabasePath(lockPath())
123
+ };
124
+ const previous = settings;
125
+ settings = next;
126
+ const changed = resolveDatabasePath(activeDatabasePath()) !== before.active || resolveDatabasePath(lockPath()) !== before.lock;
127
+ settings = previous;
128
+ if (changed) {
129
+ const named = ['databasePath', 'databasesPath', 'database'].filter(key => typeof newSettings[key] !== 'undefined').join(' / ');
130
+ throw new Error(`Can't change ${named} while ${activeCalls} database call(s) are still running or queued: await them first`);
131
+ }
132
+ }
133
+ settings = next;
62
134
  };
63
135
  /**
64
136
  * Read one runtime setting, falling back to the given default if it was never configured. This is
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.trackDatabaseCall=exports.resolveDatabasePath=exports.getSetting=exports.configure=void 0;const defaults={databasePath:"database/",relativePath:"",enforceForeignKeys:!0};let settings={...defaults},activeCalls=0;const resolveDatabasePath=t=>require("path").resolve(t);exports.resolveDatabasePath=resolveDatabasePath;const trackDatabaseCall=()=>{activeCalls++;let t=!1;return()=>{t||(t=!0,activeCalls--)}};exports.trackDatabaseCall=trackDatabaseCall;const configure=t=>{if(activeCalls>0&&void 0!==t.databasePath&&resolveDatabasePath(t.databasePath)!==resolveDatabasePath(settings.databasePath))throw new Error(`Can't change databasePath while ${activeCalls} database call(s) are still running or queued: await them first`);settings={...settings,...t}};exports.configure=configure;const getSetting=(t,a)=>void 0!==settings[t]?settings[t]:a;exports.getSetting=getSetting;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.trackDatabaseCall=exports.switchDatabase=exports.resolveDatabasePath=exports.lockPath=exports.getSetting=exports.databasesFolder=exports.configure=exports.activeDatabasePath=void 0,require("core-js/modules/es.json.stringify.js");const defaults={databasePath:"database/",relativePath:"",enforceForeignKeys:!0,readConcurrency:16,format:"compact"};let settings={...defaults},activeCalls=0;const databaseNameMatch=/^[a-z0-9_-]+$/i,withSlash=a=>a.endsWith("/")?a:`${a}/`,activeDatabasePath=()=>settings.databasesPath&&settings.database?`${withSlash(settings.databasesPath)}${settings.database}/`:getSetting("databasePath","database/");exports.activeDatabasePath=activeDatabasePath;const lockPath=()=>settings.databasesPath?withSlash(settings.databasesPath):activeDatabasePath();exports.lockPath=lockPath;const databasesFolder=()=>{if(!settings.databasesPath)throw new Error("Several databases need a folder to hold them: configure({ databasesPath: 'databases/' }) first");return withSlash(settings.databasesPath)};exports.databasesFolder=databasesFolder;const switchDatabase=a=>{settings={...settings,database:a}};exports.switchDatabase=switchDatabase;const resolveDatabasePath=a=>require("path").resolve(a);exports.resolveDatabasePath=resolveDatabasePath;const trackDatabaseCall=()=>{activeCalls++;let a=!1;return()=>{a||(a=!0,activeCalls--)}};exports.trackDatabaseCall=trackDatabaseCall;const configure=a=>{if(void 0!==a.format&&"compact"!==a.format&&"pretty"!==a.format)throw new Error(`Unknown format "${a.format}": use 'compact' or 'pretty'`);if(void 0!==a.database&&null!==a.database&&!databaseNameMatch.test(a.database))throw new Error(`Invalid database name ${JSON.stringify(a.database)}: names use letters, digits, _ and - only`);const t={...settings,...a};if(t.database&&!t.databasesPath)throw new Error("A database name needs the folder that holds the databases: configure({ databasesPath: 'databases/', database: '...' })");if(activeCalls>0){const e={active:resolveDatabasePath(activeDatabasePath()),lock:resolveDatabasePath(lockPath())},s=settings;settings=t;const o=resolveDatabasePath(activeDatabasePath())!==e.active||resolveDatabasePath(lockPath())!==e.lock;if(settings=s,o){const t=["databasePath","databasesPath","database"].filter((t=>void 0!==a[t])).join(" / ");throw new Error(`Can't change ${t} while ${activeCalls} database call(s) are still running or queued: await them first`)}}settings=t};exports.configure=configure;const getSetting=(a,t)=>void 0!==settings[a]?settings[a]:t;exports.getSetting=getSetting;
@@ -1,3 +1,8 @@
1
+ /**
2
+ * How many calls are running in this browser page right now (they aren't queued there, see withDatabaseLock): `use`
3
+ * refuses to switch the database while any other call is, as that call would carry on in the other database.
4
+ */
5
+ export declare const browserCallsRunning: () => number;
1
6
  /**
2
7
  * Run `task` once every earlier task on the same database (the configured databasePath) has finished, so that calls
3
8
  * never interleave. Every write is several files in turn (an entry, the record's entry list, each index), each read and
@@ -8,6 +13,8 @@
8
13
  * Only the public functions (query, create, drop, describe) take the lock; what they call inside doesn't take it
9
14
  * again, so it can't deadlock. In the browser nothing can be written, so tasks just run.
10
15
  *
16
+ * Each task also runs with a read cache (see withReadCache): the files it reads are read once for the whole call.
17
+ *
11
18
  * The queue is per folder (`database/` and `./database` share one). Every step of a task reads databasePath when it
12
19
  * runs, so configure() refuses to change it while tasks are queued or running (see trackDatabaseCall).
13
20
  * @param task
@@ -4,7 +4,7 @@ require("core-js/modules/esnext.weak-map.delete-all.js");
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.withDatabaseLock = void 0;
7
+ exports.withDatabaseLock = exports.browserCallsRunning = void 0;
8
8
  require("core-js/modules/esnext.map.delete-all.js");
9
9
  require("core-js/modules/esnext.map.every.js");
10
10
  require("core-js/modules/esnext.map.filter.js");
@@ -20,9 +20,16 @@ require("core-js/modules/esnext.map.some.js");
20
20
  require("core-js/modules/esnext.map.update.js");
21
21
  var jsEnv = _interopRequireWildcard(require("browser-or-node"));
22
22
  var _config = require("./config");
23
+ var _storage = require("../storage");
23
24
  var e;
24
25
  function _interopRequireWildcard(t, r) { if (!r && t && t.__esModule) return t; var n, a, o = Object.defineProperty, f = { __proto__: null, default: t }; if (Object(t) !== t) return f; if (e || "function" != typeof WeakMap || (e = new WeakMap()), e) { if (e.has(t)) return e.get(t); e.set(t, f); } for (a in t) "default" !== a && {}.hasOwnProperty.call(t, a) && ((n = o && Object.getOwnPropertyDescriptor(t, a)) && (n.get || n.set) ? o(f, a, n) : f[a] = t[a]); return f; }
25
26
  const queues = new Map();
27
+ let browserCalls = 0;
28
+ /**
29
+ * How many calls are running in this browser page right now (they aren't queued there, see withDatabaseLock): `use`
30
+ * refuses to switch the database while any other call is, as that call would carry on in the other database.
31
+ */
32
+ const browserCallsRunning = () => browserCalls;
26
33
  /**
27
34
  * Run `task` once every earlier task on the same database (the configured databasePath) has finished, so that calls
28
35
  * never interleave. Every write is several files in turn (an entry, the record's entry list, each index), each read and
@@ -33,15 +40,23 @@ const queues = new Map();
33
40
  * Only the public functions (query, create, drop, describe) take the lock; what they call inside doesn't take it
34
41
  * again, so it can't deadlock. In the browser nothing can be written, so tasks just run.
35
42
  *
43
+ * Each task also runs with a read cache (see withReadCache): the files it reads are read once for the whole call.
44
+ *
36
45
  * The queue is per folder (`database/` and `./database` share one). Every step of a task reads databasePath when it
37
46
  * runs, so configure() refuses to change it while tasks are queued or running (see trackDatabaseCall).
38
47
  * @param task
39
48
  */
49
+ exports.browserCallsRunning = browserCallsRunning;
40
50
  const withDatabaseLock = async task => {
41
51
  if (!jsEnv.isNode) {
42
- return task();
52
+ browserCalls++;
53
+ try {
54
+ return await (0, _storage.withReadCache)(task);
55
+ } finally {
56
+ browserCalls--;
57
+ }
43
58
  }
44
- const database = (0, _config.resolveDatabasePath)((0, _config.getSetting)('databasePath', 'database/'));
59
+ const database = (0, _config.resolveDatabasePath)((0, _config.lockPath)());
45
60
  const ended = (0, _config.trackDatabaseCall)();
46
61
  const previous = queues.get(database) || Promise.resolve();
47
62
  let release;
@@ -52,7 +67,7 @@ const withDatabaseLock = async task => {
52
67
  queues.set(database, queued);
53
68
  await previous;
54
69
  try {
55
- return await task();
70
+ return await (0, _storage.withReadCache)(task);
56
71
  } finally {
57
72
  ended();
58
73
  release();
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.withDatabaseLock=void 0,require("core-js/modules/esnext.map.delete-all.js"),require("core-js/modules/esnext.map.every.js"),require("core-js/modules/esnext.map.filter.js"),require("core-js/modules/esnext.map.find.js"),require("core-js/modules/esnext.map.find-key.js"),require("core-js/modules/esnext.map.includes.js"),require("core-js/modules/esnext.map.key-of.js"),require("core-js/modules/esnext.map.map-keys.js"),require("core-js/modules/esnext.map.map-values.js"),require("core-js/modules/esnext.map.merge.js"),require("core-js/modules/esnext.map.reduce.js"),require("core-js/modules/esnext.map.some.js"),require("core-js/modules/esnext.map.update.js");var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_config=require("./config");function _interopRequireWildcard(s,r){if(!r&&s&&s.__esModule)return s;var t,a,o=Object.defineProperty,u={__proto__:null,default:s};if(Object(s)!==s)return u;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(s))return e.get(s);e.set(s,u)}for(a in s)"default"!==a&&{}.hasOwnProperty.call(s,a)&&((t=o&&Object.getOwnPropertyDescriptor(s,a))&&(t.get||t.set)?o(u,a,t):u[a]=s[a]);return u}const queues=new Map,withDatabaseLock=async e=>{if(!jsEnv.isNode)return e();const s=(0,_config.resolveDatabasePath)((0,_config.getSetting)("databasePath","database/")),r=(0,_config.trackDatabaseCall)(),t=queues.get(s)||Promise.resolve();let a;const o=new Promise((e=>{a=e})),u=t.then((()=>o));queues.set(s,u),await t;try{return await e()}finally{r(),a(),queues.get(s)===u&&queues.delete(s)}};exports.withDatabaseLock=withDatabaseLock;
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.withDatabaseLock=exports.browserCallsRunning=void 0,require("core-js/modules/esnext.map.delete-all.js"),require("core-js/modules/esnext.map.every.js"),require("core-js/modules/esnext.map.filter.js"),require("core-js/modules/esnext.map.find.js"),require("core-js/modules/esnext.map.find-key.js"),require("core-js/modules/esnext.map.includes.js"),require("core-js/modules/esnext.map.key-of.js"),require("core-js/modules/esnext.map.map-keys.js"),require("core-js/modules/esnext.map.map-values.js"),require("core-js/modules/esnext.map.merge.js"),require("core-js/modules/esnext.map.reduce.js"),require("core-js/modules/esnext.map.some.js"),require("core-js/modules/esnext.map.update.js");var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_config=require("./config"),_storage=require("../storage");function _interopRequireWildcard(r,s){if(!s&&r&&r.__esModule)return r;var t,o,a=Object.defineProperty,n={__proto__:null,default:r};if(Object(r)!==r)return n;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(r))return e.get(r);e.set(r,n)}for(o in r)"default"!==o&&{}.hasOwnProperty.call(r,o)&&((t=a&&Object.getOwnPropertyDescriptor(r,o))&&(t.get||t.set)?a(n,o,t):n[o]=r[o]);return n}const queues=new Map;let browserCalls=0;const browserCallsRunning=()=>browserCalls;exports.browserCallsRunning=browserCallsRunning;const withDatabaseLock=async e=>{if(!jsEnv.isNode){browserCalls++;try{return await(0,_storage.withReadCache)(e)}finally{browserCalls--}}const r=(0,_config.resolveDatabasePath)((0,_config.lockPath)()),s=(0,_config.trackDatabaseCall)(),t=queues.get(r)||Promise.resolve();let o;const a=new Promise((e=>{o=e})),n=t.then((()=>a));queues.set(r,n),await t;try{return await(0,_storage.withReadCache)(e)}finally{s(),o(),queues.get(r)===n&&queues.delete(r)}};exports.withDatabaseLock=withDatabaseLock;