json-fs-query 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +265 -17
  2. package/browser/jsonFsQuery.js +2274 -305
  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 +8 -2
  9. package/dist/data/introspect/create.min.js +1 -1
  10. package/dist/data/introspect/drop.js +2 -0
  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/queries/databaseStatements.d.ts +31 -0
  22. package/dist/data/queries/databaseStatements.js +97 -0
  23. package/dist/data/queries/databaseStatements.min.js +1 -0
  24. package/dist/data/queries/deleteEntity.js +6 -1
  25. package/dist/data/queries/deleteEntity.min.js +1 -1
  26. package/dist/data/queries/insertEntity.d.ts +8 -0
  27. package/dist/data/queries/insertEntity.js +342 -33
  28. package/dist/data/queries/insertEntity.min.js +1 -1
  29. package/dist/data/queries/query.d.ts +2 -1
  30. package/dist/data/queries/query.js +5 -2
  31. package/dist/data/queries/query.min.js +1 -1
  32. package/dist/data/queries/selectFields.d.ts +5 -4
  33. package/dist/data/queries/selectFields.js +25 -11
  34. package/dist/data/queries/selectFields.min.js +1 -1
  35. package/dist/data/queries/updateEntity.js +39 -5
  36. package/dist/data/queries/updateEntity.min.js +1 -1
  37. package/dist/data/storage/databases.d.ts +32 -0
  38. package/dist/data/storage/databases.js +93 -0
  39. package/dist/data/storage/databases.min.js +1 -0
  40. package/dist/data/storage/fileExists.d.ts +2 -1
  41. package/dist/data/storage/fileExists.js +9 -2
  42. package/dist/data/storage/fileExists.min.js +1 -1
  43. package/dist/data/storage/index.d.ts +5 -2
  44. package/dist/data/storage/index.js +64 -1
  45. package/dist/data/storage/index.min.js +1 -1
  46. package/dist/data/storage/listFiles.d.ts +8 -0
  47. package/dist/data/storage/listFiles.js +52 -0
  48. package/dist/data/storage/listFiles.min.js +1 -0
  49. package/dist/data/storage/makeDirectory.d.ts +2 -1
  50. package/dist/data/storage/makeDirectory.js +8 -2
  51. package/dist/data/storage/makeDirectory.min.js +1 -1
  52. package/dist/data/storage/readJson.d.ts +9 -1
  53. package/dist/data/storage/readJson.js +20 -7
  54. package/dist/data/storage/readJson.min.js +1 -1
  55. package/dist/data/storage/removeDirectory.d.ts +3 -1
  56. package/dist/data/storage/removeDirectory.js +7 -1
  57. package/dist/data/storage/removeDirectory.min.js +1 -1
  58. package/dist/data/storage/removeFile.d.ts +2 -1
  59. package/dist/data/storage/removeFile.js +10 -1
  60. package/dist/data/storage/removeFile.min.js +1 -1
  61. package/dist/data/storage/storagePath.js +1 -1
  62. package/dist/data/storage/storagePath.min.js +1 -1
  63. package/dist/data/storage/writeJson.d.ts +12 -3
  64. package/dist/data/storage/writeJson.js +21 -7
  65. package/dist/data/storage/writeJson.min.js +1 -1
  66. package/dist/data/storage/writeSet.d.ts +64 -0
  67. package/dist/data/storage/writeSet.js +306 -0
  68. package/dist/data/storage/writeSet.min.js +1 -0
  69. package/dist/data/utilities/config.d.ts +37 -0
  70. package/dist/data/utilities/config.js +77 -5
  71. package/dist/data/utilities/config.min.js +1 -1
  72. package/dist/data/utilities/databaseLock.d.ts +5 -0
  73. package/dist/data/utilities/databaseLock.js +15 -3
  74. package/dist/data/utilities/databaseLock.min.js +1 -1
  75. package/dist/data/utilities/getConditionIndexedList.js +3 -4
  76. package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
  77. package/dist/data/utilities/indexBuffer.d.ts +35 -0
  78. package/dist/data/utilities/indexBuffer.js +139 -0
  79. package/dist/data/utilities/indexBuffer.min.js +1 -0
  80. package/dist/data/utilities/joinBases.js +2 -10
  81. package/dist/data/utilities/joinBases.min.js +1 -1
  82. package/dist/data/utilities/knownFields.d.ts +26 -0
  83. package/dist/data/utilities/knownFields.js +128 -0
  84. package/dist/data/utilities/knownFields.min.js +1 -0
  85. package/dist/data/utilities/packedTable.d.ts +34 -0
  86. package/dist/data/utilities/packedTable.js +60 -0
  87. package/dist/data/utilities/packedTable.min.js +1 -0
  88. package/dist/data/utilities/parsers/findInsertValues.d.ts +3 -3
  89. package/dist/data/utilities/parsers/findInsertValues.js +28 -12
  90. package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
  91. package/dist/data/utilities/parsers/maskStrings.js +11 -2
  92. package/dist/data/utilities/parsers/maskStrings.min.js +1 -1
  93. package/dist/data/utilities/parsers/parseAlter.d.ts +33 -0
  94. package/dist/data/utilities/parsers/parseAlter.js +84 -0
  95. package/dist/data/utilities/parsers/parseAlter.min.js +1 -0
  96. package/dist/data/utilities/parsers/parseCreate.d.ts +14 -0
  97. package/dist/data/utilities/parsers/parseCreate.js +13 -5
  98. package/dist/data/utilities/parsers/parseCreate.min.js +1 -1
  99. package/dist/data/utilities/parsers/parseDatabaseStatement.d.ts +17 -0
  100. package/dist/data/utilities/parsers/parseDatabaseStatement.js +82 -0
  101. package/dist/data/utilities/parsers/parseDatabaseStatement.min.js +1 -0
  102. package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
  103. package/dist/data/utilities/parsers/parseQuery.js +24 -0
  104. package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
  105. package/dist/data/utilities/parsers/startsQuery.js +1 -1
  106. package/dist/data/utilities/parsers/startsQuery.min.js +1 -1
  107. package/dist/data/utilities/reconcileJoin.d.ts +12 -0
  108. package/dist/data/utilities/reconcileJoin.js +30 -10
  109. package/dist/data/utilities/reconcileJoin.min.js +1 -1
  110. package/dist/data/utilities/retrieveFiles.d.ts +15 -0
  111. package/dist/data/utilities/retrieveFiles.js +48 -0
  112. package/dist/data/utilities/retrieveFiles.min.js +1 -0
  113. package/dist/data/utilities/retrieveRecord.d.ts +6 -0
  114. package/dist/data/utilities/retrieveRecord.js +24 -1
  115. package/dist/data/utilities/retrieveRecord.min.js +1 -1
  116. package/dist/data/utilities/retrieveRecords.d.ts +22 -0
  117. package/dist/data/utilities/retrieveRecords.js +60 -10
  118. package/dist/data/utilities/retrieveRecords.min.js +1 -1
  119. package/dist/data/utilities/runQuery.js +27 -2
  120. package/dist/data/utilities/runQuery.min.js +1 -1
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.js +24 -0
  123. package/dist/index.min.js +1 -1
  124. package/package.json +8 -4
@@ -7,12 +7,18 @@ exports.removeDirectory = void 0;
7
7
  var _storagePath = require("./storagePath");
8
8
  var _nodeFiles = require("./nodeFiles");
9
9
  var _readCache = require("./readCache");
10
+ var _writeSet = require("./writeSet");
10
11
  /**
11
- * Delete a folder of the database and everything in it; a folder that doesn't exist is left alone. Node only.
12
+ * Delete a folder of the database and everything in it; a folder that doesn't exist is left alone. During a statement
13
+ * it's removed with the statement's other changes (see withWriteSet), and reads in the statement already find
14
+ * everything in it gone. Node only.
12
15
  * @param path - A path inside the database, such as `__indexes/users`.
13
16
  */
14
17
  const removeDirectory = async path => {
15
18
  const location = (0, _storagePath.storagePath)(path);
19
+ if ((0, _writeSet.stageRemoveDirectory)(location)) {
20
+ return;
21
+ }
16
22
  // Forgotten before and after: a read that starts while rm runs would otherwise keep a removed file's old text
17
23
  (0, _readCache.cacheRemove)(location, true);
18
24
  await (0, _nodeFiles.nodeFiles)().rm(location, {
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.removeDirectory=void 0;var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache");const removeDirectory=async e=>{const r=(0,_storagePath.storagePath)(e);(0,_readCache.cacheRemove)(r,!0),await(0,_nodeFiles.nodeFiles)().rm(r,{recursive:!0,force:!0}),(0,_readCache.cacheRemove)(r,!0)};exports.removeDirectory=removeDirectory;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.removeDirectory=void 0;var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache"),_writeSet=require("./writeSet");const removeDirectory=async e=>{const r=(0,_storagePath.storagePath)(e);(0,_writeSet.stageRemoveDirectory)(r)||((0,_readCache.cacheRemove)(r,!0),await(0,_nodeFiles.nodeFiles)().rm(r,{recursive:!0,force:!0}),(0,_readCache.cacheRemove)(r,!0))};exports.removeDirectory=removeDirectory;
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Delete one file of the database. Node only.
2
+ * Delete one file of the database. During a statement the delete waits in its write set until the statement succeeds
3
+ * (see withWriteSet), and reads in the statement already find the file gone. Node only.
3
4
  * @param path - A path inside the database, such as `users/1.json`.
4
5
  * @throws {Error} When the file doesn't exist.
5
6
  */
@@ -7,13 +7,22 @@ exports.removeFile = void 0;
7
7
  var _storagePath = require("./storagePath");
8
8
  var _nodeFiles = require("./nodeFiles");
9
9
  var _readCache = require("./readCache");
10
+ var _writeSet = require("./writeSet");
10
11
  /**
11
- * Delete one file of the database. Node only.
12
+ * Delete one file of the database. During a statement the delete waits in its write set until the statement succeeds
13
+ * (see withWriteSet), and reads in the statement already find the file gone. Node only.
12
14
  * @param path - A path inside the database, such as `users/1.json`.
13
15
  * @throws {Error} When the file doesn't exist.
14
16
  */
15
17
  const removeFile = async path => {
16
18
  const location = (0, _storagePath.storagePath)(path);
19
+ const pending = (0, _writeSet.pendingFile)(location);
20
+ if (pending === null || pending === undefined && !require('fs').existsSync(location)) {
21
+ throw new Error(`ENOENT: no such file or directory, rm '${location}'`);
22
+ }
23
+ if ((0, _writeSet.stageRemove)(location)) {
24
+ return;
25
+ }
17
26
  // Forgotten before and after: a read that starts while rm runs would otherwise keep the file's old text
18
27
  (0, _readCache.cacheRemove)(location);
19
28
  await (0, _nodeFiles.nodeFiles)().rm(location);
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.removeFile=void 0;var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache");const removeFile=async e=>{const r=(0,_storagePath.storagePath)(e);(0,_readCache.cacheRemove)(r),await(0,_nodeFiles.nodeFiles)().rm(r),(0,_readCache.cacheRemove)(r)};exports.removeFile=removeFile;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.removeFile=void 0;var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache"),_writeSet=require("./writeSet");const removeFile=async e=>{const r=(0,_storagePath.storagePath)(e),t=(0,_writeSet.pendingFile)(r);if(null===t||void 0===t&&!require("fs").existsSync(r))throw new Error(`ENOENT: no such file or directory, rm '${r}'`);(0,_writeSet.stageRemove)(r)||((0,_readCache.cacheRemove)(r),await(0,_nodeFiles.nodeFiles)().rm(r),(0,_readCache.cacheRemove)(r))};exports.removeFile=removeFile;
@@ -20,6 +20,6 @@ function _interopRequireWildcard(t, r) { if (!r && t && t.__esModule) return t;
20
20
  */
21
21
  const storagePath = (path = '') => {
22
22
  const relativePath = jsEnv.isBrowser ? (0, _config.getSetting)('relativePath', '') : '';
23
- return `${relativePath}${(0, _config.getSetting)('databasePath', 'database/')}${path}`;
23
+ return `${relativePath}${(0, _config.activeDatabasePath)()}${path}`;
24
24
  };
25
25
  exports.storagePath = storagePath;
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.storagePath=void 0;var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_config=require("../utilities/config");function _interopRequireWildcard(t,r){if(!r&&t&&t.__esModule)return t;var a,o,i=Object.defineProperty,s={__proto__:null,default:t};if(Object(t)!==t)return s;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(t))return e.get(t);e.set(t,s)}for(o in t)"default"!==o&&{}.hasOwnProperty.call(t,o)&&((a=i&&Object.getOwnPropertyDescriptor(t,o))&&(a.get||a.set)?i(s,o,a):s[o]=t[o]);return s}const storagePath=(e="")=>`${jsEnv.isBrowser?(0,_config.getSetting)("relativePath",""):""}${(0,_config.getSetting)("databasePath","database/")}${e}`;exports.storagePath=storagePath;
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.storagePath=void 0;var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_config=require("../utilities/config");function _interopRequireWildcard(t,r){if(!r&&t&&t.__esModule)return t;var o,a,i=Object.defineProperty,s={__proto__:null,default:t};if(Object(t)!==t)return s;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(t))return e.get(t);e.set(t,s)}for(a in t)"default"!==a&&{}.hasOwnProperty.call(t,a)&&((o=i&&Object.getOwnPropertyDescriptor(t,a))&&(o.get||o.set)?i(s,a,o):s[a]=t[a]);return s}const storagePath=(e="")=>`${jsEnv.isBrowser?(0,_config.getSetting)("relativePath",""):""}${(0,_config.activeDatabasePath)()}${e}`;exports.storagePath=storagePath;
@@ -1,8 +1,17 @@
1
1
  /**
2
- * Write `content` to one file of the database as JSON (indented by 2 spaces), replacing the file if it exists. Its
3
- * folder must already exist (see makeDirectory). Node only.
2
+ * The text a file of the database holds for `content`: compact JSON, or indented by 2 spaces for `pretty`.
3
+ * @param content - Anything JSON.stringify accepts.
4
+ * @param format - Defaults to the configured format (see configure).
5
+ */
6
+ export declare const jsonText: (content: any, format?: "compact" | "pretty") => string;
7
+ /**
8
+ * Write `content` to one file of the database as JSON in the configured format (see jsonText), replacing the file if it
9
+ * exists. Its folder must already exist (see makeDirectory). The file is replaced whole or not at all (see
10
+ * writeAtomically). During a statement the write waits in its write set until the statement succeeds (see
11
+ * withWriteSet). Node only.
4
12
  * @param path - A path inside the database, such as `users.json`.
5
13
  * @param content - Anything JSON.stringify accepts.
14
+ * @param format - Defaults to the configured format (see configure).
6
15
  */
7
- export declare const writeJson: (path: string, content: any) => Promise<void>;
16
+ export declare const writeJson: (path: string, content: any, format?: "compact" | "pretty") => Promise<void>;
8
17
  //# sourceMappingURL=writeJson.d.ts.map
@@ -3,21 +3,35 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.writeJson = void 0;
6
+ exports.writeJson = exports.jsonText = void 0;
7
7
  require("core-js/modules/es.json.stringify.js");
8
8
  var _storagePath = require("./storagePath");
9
- var _nodeFiles = require("./nodeFiles");
10
9
  var _readCache = require("./readCache");
10
+ var _writeSet = require("./writeSet");
11
+ var _config = require("../utilities/config");
11
12
  /**
12
- * Write `content` to one file of the database as JSON (indented by 2 spaces), replacing the file if it exists. Its
13
- * folder must already exist (see makeDirectory). Node only.
13
+ * The text a file of the database holds for `content`: compact JSON, or indented by 2 spaces for `pretty`.
14
+ * @param content - Anything JSON.stringify accepts.
15
+ * @param format - Defaults to the configured format (see configure).
16
+ */
17
+ const jsonText = (content, format = (0, _config.getSetting)('format', 'compact')) => format === 'pretty' ? JSON.stringify(content, null, 2) : JSON.stringify(content);
18
+ /**
19
+ * Write `content` to one file of the database as JSON in the configured format (see jsonText), replacing the file if it
20
+ * exists. Its folder must already exist (see makeDirectory). The file is replaced whole or not at all (see
21
+ * writeAtomically). During a statement the write waits in its write set until the statement succeeds (see
22
+ * withWriteSet). Node only.
14
23
  * @param path - A path inside the database, such as `users.json`.
15
24
  * @param content - Anything JSON.stringify accepts.
25
+ * @param format - Defaults to the configured format (see configure).
16
26
  */
17
- const writeJson = async (path, content) => {
27
+ exports.jsonText = jsonText;
28
+ const writeJson = async (path, content, format) => {
18
29
  const location = (0, _storagePath.storagePath)(path);
19
- const text = JSON.stringify(content, null, 2);
20
- await (0, _nodeFiles.nodeFiles)().writeFile(location, text);
30
+ const text = jsonText(content, format);
31
+ if ((0, _writeSet.stageWrite)(location, text)) {
32
+ return;
33
+ }
34
+ await (0, _writeSet.writeAtomically)(location, text);
21
35
  (0, _readCache.cacheWrite)(location, text);
22
36
  };
23
37
  exports.writeJson = writeJson;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.writeJson=void 0,require("core-js/modules/es.json.stringify.js");var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache");const writeJson=async(e,r)=>{const s=(0,_storagePath.storagePath)(e),t=JSON.stringify(r,null,2);await(0,_nodeFiles.nodeFiles)().writeFile(s,t),(0,_readCache.cacheWrite)(s,t)};exports.writeJson=writeJson;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.writeJson=exports.jsonText=void 0,require("core-js/modules/es.json.stringify.js");var _storagePath=require("./storagePath"),_readCache=require("./readCache"),_writeSet=require("./writeSet"),_config=require("../utilities/config");const jsonText=(e,t=(0,_config.getSetting)("format","compact"))=>"pretty"===t?JSON.stringify(e,null,2):JSON.stringify(e);exports.jsonText=jsonText;const writeJson=async(e,t,r)=>{const s=(0,_storagePath.storagePath)(e),i=jsonText(t,r);(0,_writeSet.stageWrite)(s,i)||(await(0,_writeSet.writeAtomically)(s,i),(0,_readCache.cacheWrite)(s,i))};exports.writeJson=writeJson;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Write `text` to `location` so that the file is always either its old or its new content, never half of one: the
3
+ * text goes to a temporary file next to it, which then replaces it (a rename is atomic within one folder). That holds
4
+ * when the process stops part way (a crash, a kill); there's no fsync, so after a power cut the file system may still
5
+ * lose a recent write. When the write or rename fails, the temporary file is removed and the original error thrown.
6
+ * @param location
7
+ * @param text
8
+ */
9
+ export declare const writeAtomically: (location: string, text: string) => Promise<void>;
10
+ /**
11
+ * Run one statement's `task` with a write set for the configured database: its writes and deletes are collected in
12
+ * memory, and reads during the task see them (see pendingFile). When the task finishes, everything it changed is put in
13
+ * storage at once, each file written once however many times the task changed it. When it throws, nothing it changed
14
+ * reaches storage: a multi-row insert or update whose third row fails leaves the first two unwritten too.
15
+ *
16
+ * A task inside another (a public call made from inside a statement) joins the outer write set. Node only: in a
17
+ * browser nothing can be written, so the task just runs.
18
+ * @param task
19
+ */
20
+ export declare const withWriteSet: <T>(task: () => Promise<T>) => Promise<T>;
21
+ /**
22
+ * What the running statement has done to the file at `location`: its pending text, `null` if the statement deleted it
23
+ * (or a folder it was in), or `undefined` when the statement hasn't touched it (or no statement is running).
24
+ * @param location - The file's full location (see storagePath).
25
+ */
26
+ export declare const pendingFile: (location: string) => string | null | undefined;
27
+ /**
28
+ * Whether the file or folder at `location` exists as far as the running statement knows: true for a file it wrote, a
29
+ * folder it made, or a folder holding something it wrote or made; false for a file it deleted or anything in a folder
30
+ * it removed; `undefined` when the statement hasn't touched it (or no statement is running), so storage decides.
31
+ * @param location - The full location (see storagePath).
32
+ */
33
+ export declare const pendingExists: (location: string) => boolean | undefined;
34
+ /**
35
+ * The files under `folder` as the running statement sees them: `onDisk` (full locations) without what it deleted or
36
+ * removed, plus what it wrote there. `onDisk` as it is when no statement is running.
37
+ * @param folder - The folder's full location (see storagePath).
38
+ * @param onDisk - The full locations of the files under it in storage.
39
+ */
40
+ export declare const pendingListing: (folder: string, onDisk: string[]) => string[];
41
+ /**
42
+ * Keep `text` as the new content of `location` in the running statement's write set. False when no statement is
43
+ * running, so the caller writes to storage itself.
44
+ * @param location
45
+ * @param text
46
+ */
47
+ export declare const stageWrite: (location: string, text: string) => boolean;
48
+ /**
49
+ * Mark `location` deleted in the running statement's write set. False when no statement is running.
50
+ * @param location
51
+ */
52
+ export declare const stageRemove: (location: string) => boolean;
53
+ /**
54
+ * Note a folder to create in the running statement's write set. False when no statement is running.
55
+ * @param location
56
+ */
57
+ export declare const stageMakeDirectory: (location: string) => boolean;
58
+ /**
59
+ * Mark a folder and everything in it removed in the running statement's write set: what the statement wrote or
60
+ * created inside it so far is dropped with it. False when no statement is running.
61
+ * @param location
62
+ */
63
+ export declare const stageRemoveDirectory: (location: string) => boolean;
64
+ //# sourceMappingURL=writeSet.d.ts.map
@@ -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;
@@ -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
  /**