json-fs-query 1.0.6 → 2.0.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 (126) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +295 -98
  3. package/browser/jsonFsQuery.js +12733 -10777
  4. package/browser/jsonFsQuery.min.js +1 -1
  5. package/dist/data/introspect/create.d.ts +1 -1
  6. package/dist/data/introspect/create.js +35 -4
  7. package/dist/data/introspect/create.min.js +1 -1
  8. package/dist/data/introspect/definitions/field.d.ts +6 -2
  9. package/dist/data/introspect/definitions/field.js +7 -1
  10. package/dist/data/introspect/definitions/field.min.js +1 -1
  11. package/dist/data/introspect/index.d.ts +6 -3
  12. package/dist/data/introspect/index.js +10 -19
  13. package/dist/data/introspect/index.min.js +1 -1
  14. package/dist/data/introspect/policies/foreignKeyExists.js +5 -1
  15. package/dist/data/introspect/policies/foreignKeyExists.min.js +1 -1
  16. package/dist/data/introspect/policies/hasDependents.js +3 -1
  17. package/dist/data/introspect/policies/hasDependents.min.js +1 -1
  18. package/dist/data/introspect/policies/keyUnique.js +5 -1
  19. package/dist/data/introspect/policies/keyUnique.min.js +1 -1
  20. package/dist/data/introspect/policies/typeCheck.d.ts +9 -1
  21. package/dist/data/introspect/policies/typeCheck.js +16 -2
  22. package/dist/data/introspect/policies/typeCheck.min.js +1 -1
  23. package/dist/data/queries/createRecord.d.ts +11 -0
  24. package/dist/data/queries/createRecord.js +58 -0
  25. package/dist/data/queries/createRecord.min.js +1 -0
  26. package/dist/data/queries/deleteEntity.js +2 -1
  27. package/dist/data/queries/deleteEntity.min.js +1 -1
  28. package/dist/data/queries/describeRecord.d.ts +9 -0
  29. package/dist/data/queries/describeRecord.js +29 -0
  30. package/dist/data/queries/describeRecord.min.js +1 -0
  31. package/dist/data/queries/dropRecord.d.ts +9 -0
  32. package/dist/data/queries/dropRecord.js +61 -0
  33. package/dist/data/queries/dropRecord.min.js +1 -0
  34. package/dist/data/queries/insertEntity.js +11 -5
  35. package/dist/data/queries/insertEntity.min.js +1 -1
  36. package/dist/data/queries/parser.d.ts +1 -1
  37. package/dist/data/queries/parser.js +7 -15
  38. package/dist/data/queries/parser.min.js +1 -1
  39. package/dist/data/queries/query.d.ts +6 -0
  40. package/dist/data/queries/query.js +15 -6
  41. package/dist/data/queries/query.min.js +1 -1
  42. package/dist/data/queries/queryStatus.d.ts +15 -0
  43. package/dist/data/queries/queryStatus.js +52 -0
  44. package/dist/data/queries/queryStatus.min.js +1 -0
  45. package/dist/data/queries/quote.d.ts +11 -0
  46. package/dist/data/queries/quote.js +17 -0
  47. package/dist/data/queries/quote.min.js +1 -0
  48. package/dist/data/queries/showRecords.d.ts +6 -0
  49. package/dist/data/queries/showRecords.js +21 -0
  50. package/dist/data/queries/showRecords.min.js +1 -0
  51. package/dist/data/queries/sortBy.js +3 -0
  52. package/dist/data/queries/updateEntity.js +74 -18
  53. package/dist/data/queries/updateEntity.min.js +1 -1
  54. package/dist/data/queries/where.d.ts +2 -1
  55. package/dist/data/queries/where.js +18 -6
  56. package/dist/data/queries/where.min.js +1 -1
  57. package/dist/data/utilities/compareAs.d.ts +20 -0
  58. package/dist/data/utilities/compareAs.js +58 -0
  59. package/dist/data/utilities/compareAs.min.js +1 -0
  60. package/dist/data/utilities/config.d.ts +15 -0
  61. package/dist/data/utilities/config.js +31 -1
  62. package/dist/data/utilities/config.min.js +1 -1
  63. package/dist/data/utilities/databaseLock.d.ts +16 -0
  64. package/dist/data/utilities/databaseLock.js +63 -0
  65. package/dist/data/utilities/databaseLock.min.js +1 -0
  66. package/dist/data/utilities/fieldType.d.ts +16 -0
  67. package/dist/data/utilities/fieldType.js +23 -0
  68. package/dist/data/utilities/fieldType.min.js +1 -0
  69. package/dist/data/utilities/getConditionIndexedList.js +2 -1
  70. package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
  71. package/dist/data/utilities/matchIndexedJoins.js +3 -1
  72. package/dist/data/utilities/matchIndexedJoins.min.js +1 -1
  73. package/dist/data/utilities/mergeJoin.js +5 -1
  74. package/dist/data/utilities/mergeJoin.min.js +1 -1
  75. package/dist/data/utilities/parsers/findConditions.d.ts +5 -1
  76. package/dist/data/utilities/parsers/findConditions.js +15 -6
  77. package/dist/data/utilities/parsers/findConditions.min.js +1 -1
  78. package/dist/data/utilities/parsers/findInsertValues.js +3 -2
  79. package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
  80. package/dist/data/utilities/parsers/findSelects.js +6 -3
  81. package/dist/data/utilities/parsers/findSelects.min.js +1 -1
  82. package/dist/data/utilities/parsers/findSortClauses.js +5 -2
  83. package/dist/data/utilities/parsers/findSortClauses.min.js +1 -1
  84. package/dist/data/utilities/parsers/findUpdateValues.js +3 -2
  85. package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -1
  86. package/dist/data/utilities/parsers/maskStrings.d.ts +39 -0
  87. package/dist/data/utilities/parsers/maskStrings.js +123 -0
  88. package/dist/data/utilities/parsers/maskStrings.min.js +1 -0
  89. package/dist/data/utilities/parsers/parseCreate.d.ts +20 -0
  90. package/dist/data/utilities/parsers/parseCreate.js +330 -0
  91. package/dist/data/utilities/parsers/parseCreate.min.js +1 -0
  92. package/dist/data/utilities/parsers/parseLiteralValue.d.ts +2 -1
  93. package/dist/data/utilities/parsers/parseLiteralValue.js +6 -3
  94. package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -1
  95. package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
  96. package/dist/data/utilities/parsers/parseQuery.js +28 -2
  97. package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
  98. package/dist/data/utilities/parsers/parseSchemaStatement.d.ts +13 -0
  99. package/dist/data/utilities/parsers/parseSchemaStatement.js +82 -0
  100. package/dist/data/utilities/parsers/parseSchemaStatement.min.js +1 -0
  101. package/dist/data/utilities/parsers/scanQuery.d.ts +52 -0
  102. package/dist/data/utilities/parsers/scanQuery.js +158 -0
  103. package/dist/data/utilities/parsers/scanQuery.min.js +1 -0
  104. package/dist/data/utilities/parsers/splitQueries.d.ts +6 -0
  105. package/dist/data/utilities/parsers/splitQueries.js +20 -2
  106. package/dist/data/utilities/parsers/splitQueries.min.js +1 -1
  107. package/dist/data/utilities/parsers/startsQuery.d.ts +5 -0
  108. package/dist/data/utilities/parsers/startsQuery.js +10 -0
  109. package/dist/data/utilities/parsers/startsQuery.min.js +1 -0
  110. package/dist/data/utilities/recordExists.d.ts +6 -0
  111. package/dist/data/utilities/recordExists.js +18 -0
  112. package/dist/data/utilities/recordExists.min.js +1 -0
  113. package/dist/data/utilities/removeFromIndex.d.ts +2 -1
  114. package/dist/data/utilities/removeFromIndex.js +4 -2
  115. package/dist/data/utilities/removeFromIndex.min.js +1 -1
  116. package/dist/data/utilities/runQuery.js +49 -0
  117. package/dist/data/utilities/runQuery.min.js +1 -1
  118. package/dist/data/utilities/updateIndex.d.ts +2 -1
  119. package/dist/data/utilities/updateIndex.js +4 -2
  120. package/dist/data/utilities/updateIndex.min.js +1 -1
  121. package/dist/data/utilities/useJoinClause.js +8 -2
  122. package/dist/data/utilities/useJoinClause.min.js +1 -1
  123. package/dist/index.d.ts +2 -0
  124. package/dist/index.js +14 -0
  125. package/dist/index.min.js +1 -1
  126. package/package.json +68 -55
@@ -8,6 +8,7 @@ require("core-js/modules/es.json.stringify.js");
8
8
  require("core-js/modules/esnext.iterator.constructor.js");
9
9
  require("core-js/modules/esnext.iterator.find.js");
10
10
  require("core-js/modules/esnext.iterator.map.js");
11
+ require("core-js/modules/esnext.iterator.some.js");
11
12
  var _retrieveRecord = require("../utilities/retrieveRecord");
12
13
  var _updateIndex = require("../utilities/updateIndex");
13
14
  var _keyGenerate = require("../introspect/policies/keyGenerate");
@@ -17,6 +18,7 @@ var _typeCheck = require("../introspect/policies/typeCheck");
17
18
  var _foreignKeyExists = require("../introspect/policies/foreignKeyExists");
18
19
  var _splitEntityProperty = require("../utilities/parsers/splitEntityProperty");
19
20
  var _computeKeyValue = require("../utilities/computeKeyValue");
21
+ var _fieldType = require("../utilities/fieldType");
20
22
  var _config = require("../utilities/config");
21
23
  const {
22
24
  writeFile
@@ -43,13 +45,14 @@ const buildEntityValues = async (entity, record, rowValues = {}) => {
43
45
  }
44
46
  continue;
45
47
  }
46
- entityValues[field.name] = (0, _typeCheck.typeCheck)(field.type, value);
48
+ entityValues[field.name] = (0, _typeCheck.typeCheck)(field.type, value, field.nullable);
47
49
  }
48
50
  return entityValues;
49
51
  };
50
52
  /**
51
53
  * Reject the insert if it collides with an existing primary or unique key value - a single field,
52
- * or a composite of several for a multi-field key.
54
+ * or a composite of several for a multi-field key. As in SQL, a value with a null in it is never a
55
+ * duplicate, so a nullable unique field can hold null in any number of rows.
53
56
  * @param entity
54
57
  * @param record
55
58
  * @param entityValues
@@ -60,6 +63,9 @@ const assertUnique = async (entity, record, entityValues) => {
60
63
  continue;
61
64
  }
62
65
  const checkKeys = key.fields.map(field => entityValues[field]);
66
+ if (checkKeys.some(value => value === null)) {
67
+ continue;
68
+ }
63
69
  const isUnique = await (0, _keyUnique.keyUnique)(entity, key.fields, checkKeys);
64
70
  if (!isUnique) {
65
71
  throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
@@ -68,7 +74,7 @@ const assertUnique = async (entity, record, entityValues) => {
68
74
  };
69
75
  /**
70
76
  * Reject the insert if a single-field foreign key's value doesn't exist in the record it
71
- * references.
77
+ * references. A null references nothing, so it isn't checked (as in SQL).
72
78
  * @param entity
73
79
  * @param record
74
80
  * @param entityValues
@@ -79,7 +85,7 @@ const assertForeignKeys = async (entity, record, entityValues) => {
79
85
  continue;
80
86
  }
81
87
  const field = key.fields[0];
82
- if (!Object.prototype.hasOwnProperty.call(entityValues, field)) {
88
+ if (!Object.prototype.hasOwnProperty.call(entityValues, field) || entityValues[field] === null) {
83
89
  continue;
84
90
  }
85
91
  const {
@@ -108,7 +114,7 @@ const insertEntity = async (entity = '', values = [], dataSet = {}) => {
108
114
  record.entries.push(fileName);
109
115
  await writeFile(`${databasePath}${record.path}.json`, JSON.stringify(record, null, 2));
110
116
  for (const key of record.keys) {
111
- await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, entityValues), fileName);
117
+ await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, entityValues), fileName, (0, _fieldType.keyValueType)(record, key));
112
118
  }
113
119
  dataSet[entity].push(entityValues);
114
120
  }
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.insertEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_updateIndex=require("../utilities/updateIndex"),_keyGenerate=require("../introspect/policies/keyGenerate"),_keyUnique=require("../introspect/policies/keyUnique"),_useDefault=require("../introspect/policies/useDefault"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_computeKeyValue=require("../utilities/computeKeyValue"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),buildEntityValues=async(e,t,i={})=>{const r={};for(const s of t.definition){let t=i[s.name];if(s.autoGenerate?t=await(0,_keyGenerate.keyGenerate)(e,[s.name]):Object.prototype.hasOwnProperty.call(s,"default")&&(t=(0,_useDefault.useDefault)(s.default,t)),void 0!==t)r[s.name]=(0,_typeCheck.typeCheck)(s.type,t);else if(!s.optional)throw new Error(`Missing required field "${s.name}" for ${e}`)}return r},assertUnique=async(e,t,i)=>{for(const r of t.keys){if("primary"!==r.type&&"unique"!==r.type)continue;const t=r.fields.map((e=>i[e]));if(!await(0,_keyUnique.keyUnique)(e,r.fields,t))throw new Error(`Duplicate value for ${r.type} key "${r.fields.join(", ")}" on ${e}`)}},assertForeignKeys=async(e,t,i)=>{for(const r of t.keys){if("foreign"!==r.type||1!==r.fields.length||!r.references||1!==r.references.length)continue;const t=r.fields[0];if(!Object.prototype.hasOwnProperty.call(i,t))continue;const{entity:s,property:n}=(0,_splitEntityProperty.splitEntityProperty)(r.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(s,n,i[t]))throw new Error(`Foreign key "${t}" on ${e} references a nonexistent ${s}.${n} = ${i[t]}`)}},insertEntity=async(e="",t=[],i={})=>{const r=(0,_config.getSetting)("databasePath","database/"),s=await(0,_retrieveRecord.retrieveRecord)(e),n=s.keys.find((e=>"primary"===e.type&&1===e.fields.length));Object.prototype.hasOwnProperty.call(i,e)||(i[e]=[]);for(const o of t){const t=await buildEntityValues(e,s,o);await assertUnique(e,s,t),await assertForeignKeys(e,s,t);const a=`${n?t[n.fields[0]]:crypto.randomUUID()}.json`;await writeFile(`${r}${s.path}/${a}`,JSON.stringify(t,null,2)),s.entries.push(a),await writeFile(`${r}${s.path}.json`,JSON.stringify(s,null,2));for(const e of s.keys)await(0,_updateIndex.updateIndex)(s.path,e,(0,_computeKeyValue.computeKeyValue)(e.fields,t),a);i[e].push(t)}return i};exports.insertEntity=insertEntity;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.insertEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_updateIndex=require("../utilities/updateIndex"),_keyGenerate=require("../introspect/policies/keyGenerate"),_keyUnique=require("../introspect/policies/keyUnique"),_useDefault=require("../introspect/policies/useDefault"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_computeKeyValue=require("../utilities/computeKeyValue"),_fieldType=require("../utilities/fieldType"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),buildEntityValues=async(e,t,i={})=>{const r={};for(const s of t.definition){let t=i[s.name];if(s.autoGenerate?t=await(0,_keyGenerate.keyGenerate)(e,[s.name]):Object.prototype.hasOwnProperty.call(s,"default")&&(t=(0,_useDefault.useDefault)(s.default,t)),void 0!==t)r[s.name]=(0,_typeCheck.typeCheck)(s.type,t,s.nullable);else if(!s.optional)throw new Error(`Missing required field "${s.name}" for ${e}`)}return r},assertUnique=async(e,t,i)=>{for(const r of t.keys){if("primary"!==r.type&&"unique"!==r.type)continue;const t=r.fields.map((e=>i[e]));if(t.some((e=>null===e)))continue;if(!await(0,_keyUnique.keyUnique)(e,r.fields,t))throw new Error(`Duplicate value for ${r.type} key "${r.fields.join(", ")}" on ${e}`)}},assertForeignKeys=async(e,t,i)=>{for(const r of t.keys){if("foreign"!==r.type||1!==r.fields.length||!r.references||1!==r.references.length)continue;const t=r.fields[0];if(!Object.prototype.hasOwnProperty.call(i,t)||null===i[t])continue;const{entity:s,property:n}=(0,_splitEntityProperty.splitEntityProperty)(r.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(s,n,i[t]))throw new Error(`Foreign key "${t}" on ${e} references a nonexistent ${s}.${n} = ${i[t]}`)}},insertEntity=async(e="",t=[],i={})=>{const r=(0,_config.getSetting)("databasePath","database/"),s=await(0,_retrieveRecord.retrieveRecord)(e),n=s.keys.find((e=>"primary"===e.type&&1===e.fields.length));Object.prototype.hasOwnProperty.call(i,e)||(i[e]=[]);for(const o of t){const t=await buildEntityValues(e,s,o);await assertUnique(e,s,t),await assertForeignKeys(e,s,t);const a=`${n?t[n.fields[0]]:crypto.randomUUID()}.json`;await writeFile(`${r}${s.path}/${a}`,JSON.stringify(t,null,2)),s.entries.push(a),await writeFile(`${r}${s.path}.json`,JSON.stringify(s,null,2));for(const e of s.keys)await(0,_updateIndex.updateIndex)(s.path,e,(0,_computeKeyValue.computeKeyValue)(e.fields,t),a,(0,_fieldType.keyValueType)(s,e));i[e].push(t)}return i};exports.insertEntity=insertEntity;
@@ -1,2 +1,2 @@
1
- export declare const parser: (queries?: string) => any[];
1
+ export declare const parser: (queries?: string) => import("../utilities/parsers/parseQuery").ParsedQuery[];
2
2
  //# sourceMappingURL=parser.d.ts.map
@@ -4,27 +4,19 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.parser = void 0;
7
+ require("core-js/modules/esnext.iterator.constructor.js");
8
+ require("core-js/modules/esnext.iterator.map.js");
7
9
  var _splitQueries = require("../utilities/parsers/splitQueries");
8
10
  var _parseQuery = require("../utilities/parsers/parseQuery");
9
11
  const parser = (queries = '') => {
10
12
  const queryArray = (0, _splitQueries.splitQueries)(queries);
11
- const parsedQueries = [];
12
- let mergeQuery = '';
13
- for (const query of queryArray) {
14
- mergeQuery = mergeQuery ? `${mergeQuery}${query}` : query;
15
- let parsedQuery = {};
13
+ return queryArray.map((query, index) => {
16
14
  try {
17
- parsedQuery = (0, _parseQuery.parseQuery)(mergeQuery);
18
- mergeQuery = '';
15
+ return (0, _parseQuery.parseQuery)(query);
19
16
  } catch (e) {
20
- // attempt to combine queries to fix bad splits
21
- continue;
17
+ const which = queryArray.length > 1 ? ` query ${index + 1} of ${queryArray.length}` : '';
18
+ throw new Error(`Failed to interpret${which}: ${query} (${e.message})`);
22
19
  }
23
- parsedQueries.push(parsedQuery);
24
- }
25
- if (mergeQuery) {
26
- throw new Error(`Failed to interpret: ${mergeQuery}`);
27
- }
28
- return parsedQueries;
20
+ });
29
21
  };
30
22
  exports.parser = parser;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parser=void 0;var _splitQueries=require("../utilities/parsers/splitQueries"),_parseQuery=require("../utilities/parsers/parseQuery");const parser=(e="")=>{const r=(0,_splitQueries.splitQueries)(e),s=[];let t="";for(const e of r){t=t?`${t}${e}`:e;let r={};try{r=(0,_parseQuery.parseQuery)(t),t=""}catch(e){continue}s.push(r)}if(t)throw new Error(`Failed to interpret: ${t}`);return s};exports.parser=parser;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parser=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _splitQueries=require("../utilities/parsers/splitQueries"),_parseQuery=require("../utilities/parsers/parseQuery");const parser=(e="")=>{const r=(0,_splitQueries.splitQueries)(e);return r.map(((e,s)=>{try{return(0,_parseQuery.parseQuery)(e)}catch(t){const i=r.length>1?` query ${s+1} of ${r.length}`:"";throw new Error(`Failed to interpret${i}: ${e} (${t.message})`)}}))};exports.parser=parser;
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Parse a query string (one query, or several separated by `;`) and run each query in turn, resolving to one result
3
+ * per query. The queries of one call run together, under the database lock: no other call's queries run between
4
+ * them (see withDatabaseLock).
5
+ * @param queryString
6
+ */
1
7
  export declare const query: (queryString?: string) => Promise<any[]>;
2
8
  //# sourceMappingURL=query.d.ts.map
@@ -6,13 +6,22 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.query = void 0;
7
7
  var _parser = require("./parser");
8
8
  var _runQuery = require("../utilities/runQuery");
9
+ var _databaseLock = require("../utilities/databaseLock");
10
+ /**
11
+ * Parse a query string (one query, or several separated by `;`) and run each query in turn, resolving to one result
12
+ * per query. The queries of one call run together, under the database lock: no other call's queries run between
13
+ * them (see withDatabaseLock).
14
+ * @param queryString
15
+ */
9
16
  const query = async (queryString = '') => {
10
17
  const parsedQueries = (0, _parser.parser)(queryString);
11
- const queryResults = [];
12
- for (const parsed of parsedQueries) {
13
- const dataSet = await (0, _runQuery.runQuery)(parsed);
14
- queryResults.push(dataSet[parsed.entity]);
15
- }
16
- return queryResults;
18
+ return (0, _databaseLock.withDatabaseLock)(async () => {
19
+ const queryResults = [];
20
+ for (const parsed of parsedQueries) {
21
+ const dataSet = await (0, _runQuery.runQuery)(parsed);
22
+ queryResults.push(dataSet[parsed.entity]);
23
+ }
24
+ return queryResults;
25
+ });
17
26
  };
18
27
  exports.query = query;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.query=void 0;var _parser=require("./parser"),_runQuery=require("../utilities/runQuery");const query=async(r="")=>{const e=(0,_parser.parser)(r),u=[];for(const r of e){const e=await(0,_runQuery.runQuery)(r);u.push(e[r.entity])}return u};exports.query=query;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.query=void 0;var _parser=require("./parser"),_runQuery=require("../utilities/runQuery"),_databaseLock=require("../utilities/databaseLock");const query=async(e="")=>{const r=(0,_parser.parser)(e);return(0,_databaseLock.withDatabaseLock)((async()=>{const e=[];for(const t of r){const r=await(0,_runQuery.runQuery)(t);e.push(r[t.entity])}return e}))};exports.query=query;
@@ -0,0 +1,15 @@
1
+ export type QueryStatus = {
2
+ complete: boolean;
3
+ waitingFor: string;
4
+ };
5
+ /**
6
+ * Whether some query text, typed so far, is ready to run, and if not what it is waiting for: for a prompt which
7
+ * reads a query over several lines (such as the CLI). Uses the same rules as the parser, so a `;` inside a string,
8
+ * a comment or brackets doesn't count, and an open quote, comment or bracket keeps the query going.
9
+ * @example
10
+ * queryStatus("read notes where text = 'it") // { complete: false, waitingFor: "'" }
11
+ * queryStatus('read notes;') // { complete: true, waitingFor: '' }
12
+ * @param text - everything typed so far; join the lines with '\n', since a `--` comment ends at the end of a line
13
+ */
14
+ export declare const queryStatus: (text?: string) => QueryStatus;
15
+ //# sourceMappingURL=queryStatus.d.ts.map
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.queryStatus = void 0;
7
+ var _scanQuery = require("../utilities/parsers/scanQuery");
8
+ /**
9
+ * Whether some query text, typed so far, is ready to run, and if not what it is waiting for: for a prompt which
10
+ * reads a query over several lines (such as the CLI). Uses the same rules as the parser, so a `;` inside a string,
11
+ * a comment or brackets doesn't count, and an open quote, comment or bracket keeps the query going.
12
+ * @example
13
+ * queryStatus("read notes where text = 'it") // { complete: false, waitingFor: "'" }
14
+ * queryStatus('read notes;') // { complete: true, waitingFor: '' }
15
+ * @param text - everything typed so far; join the lines with '\n', since a `--` comment ends at the end of a line
16
+ */
17
+ const queryStatus = (text = '') => {
18
+ const {
19
+ end,
20
+ closed,
21
+ terminated
22
+ } = (0, _scanQuery.scanQuery)(text);
23
+ if (terminated) {
24
+ return {
25
+ complete: true,
26
+ waitingFor: ''
27
+ };
28
+ }
29
+ if (end.quote) {
30
+ return {
31
+ complete: false,
32
+ waitingFor: end.quote
33
+ };
34
+ }
35
+ if (end.blockComment) {
36
+ return {
37
+ complete: false,
38
+ waitingFor: '/*'
39
+ };
40
+ }
41
+ if (!closed) {
42
+ return {
43
+ complete: false,
44
+ waitingFor: end.brackets[end.brackets.length - 1]
45
+ };
46
+ }
47
+ return {
48
+ complete: false,
49
+ waitingFor: ';'
50
+ };
51
+ };
52
+ exports.queryStatus = queryStatus;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.queryStatus=void 0;var _scanQuery=require("../utilities/parsers/scanQuery");const queryStatus=(e="")=>{const{end:t,closed:r,terminated:o}=(0,_scanQuery.scanQuery)(e);return o?{complete:!0,waitingFor:""}:t.quote?{complete:!1,waitingFor:t.quote}:t.blockComment?{complete:!1,waitingFor:"/*"}:r?{complete:!1,waitingFor:";"}:{complete:!1,waitingFor:t.brackets[t.brackets.length-1]}};exports.queryStatus=queryStatus;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Write a value as a quoted string for a query, so any text can be used in one: the backslash and the quote are
3
+ * escaped, so the parser reads back exactly the text given.
4
+ * @example
5
+ * query(`insert exercises values name = ${quote("Farmer's walk")}`)
6
+ * // insert exercises values name = 'Farmer\'s walk'
7
+ * @param value - the text to quote (anything else is converted with String first)
8
+ * @param quoteChar - the quote to wrap it in
9
+ */
10
+ export declare const quote: (value: any, quoteChar?: "'" | "\"") => string;
11
+ //# sourceMappingURL=quote.d.ts.map
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.quote = void 0;
7
+ /**
8
+ * Write a value as a quoted string for a query, so any text can be used in one: the backslash and the quote are
9
+ * escaped, so the parser reads back exactly the text given.
10
+ * @example
11
+ * query(`insert exercises values name = ${quote("Farmer's walk")}`)
12
+ * // insert exercises values name = 'Farmer\'s walk'
13
+ * @param value - the text to quote (anything else is converted with String first)
14
+ * @param quoteChar - the quote to wrap it in
15
+ */
16
+ const quote = (value, quoteChar = '\'') => `${quoteChar}${String(value).replace(/\\/g, '\\\\').split(quoteChar).join(`\\${quoteChar}`)}${quoteChar}`;
17
+ exports.quote = quote;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.quote=void 0;const quote=(e,t="'")=>`${t}${String(e).replace(/\\/g,"\\\\").split(t).join(`\\${t}`)}${t}`;exports.quote=quote;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Run `show records`: the name of every record in the __RECORDS registry, in the order they were created. A database
3
+ * with no records yet (no registry file) gives an empty list.
4
+ */
5
+ export declare const showRecords: () => Promise<string[]>;
6
+ //# sourceMappingURL=showRecords.d.ts.map
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.showRecords = void 0;
7
+ require("core-js/modules/esnext.iterator.constructor.js");
8
+ require("core-js/modules/esnext.iterator.map.js");
9
+ var _retrieveRecord = require("../utilities/retrieveRecord");
10
+ var _recordExists = require("../utilities/recordExists");
11
+ /**
12
+ * Run `show records`: the name of every record in the __RECORDS registry, in the order they were created. A database
13
+ * with no records yet (no registry file) gives an empty list.
14
+ */
15
+ const showRecords = async () => {
16
+ if (!(0, _recordExists.recordExists)('__RECORDS')) {
17
+ return [];
18
+ }
19
+ return (await (0, _retrieveRecord.retrieveRecord)('__RECORDS')).entries.map(fileName => fileName.replace(/\.json$/, ''));
20
+ };
21
+ exports.showRecords = showRecords;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.showRecords=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_recordExists=require("../utilities/recordExists");const showRecords=async()=>(0,_recordExists.recordExists)("__RECORDS")?(await(0,_retrieveRecord.retrieveRecord)("__RECORDS")).entries.map((e=>e.replace(/\.json$/,""))):[];exports.showRecords=showRecords;
@@ -12,6 +12,9 @@ const sortBy = (dataSet = [], sortClauses = []) => {
12
12
  for (const sortClause of sortClauses) {
13
13
  const property = sortClause.property;
14
14
  const direction = sortClause.direction;
15
+ // Ordering compares the raw stored values, not by declared type (that's only for equality, see where), so
16
+ // numeric ids in a string field still sort numerically. Loose so '3' and 3 still tie.
17
+ // eslint-disable-next-line eqeqeq
15
18
  if (a[property] == b[property]) {
16
19
  continue;
17
20
  }
@@ -14,6 +14,8 @@ var _retrieveFile = require("../utilities/retrieveFile");
14
14
  var _updateIndex = require("../utilities/updateIndex");
15
15
  var _removeFromIndex = require("../utilities/removeFromIndex");
16
16
  var _computeKeyValue = require("../utilities/computeKeyValue");
17
+ var _fieldType = require("../utilities/fieldType");
18
+ var _compareAs = require("../utilities/compareAs");
17
19
  var _keyUnique = require("../introspect/policies/keyUnique");
18
20
  var _typeCheck = require("../introspect/policies/typeCheck");
19
21
  var _foreignKeyExists = require("../introspect/policies/foreignKeyExists");
@@ -34,7 +36,7 @@ const {
34
36
  const findEntryFile = async (record, primaryKey, primaryValue) => {
35
37
  for (const fileName of record.entries) {
36
38
  const data = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
37
- if ((0, _computeKeyValue.computeKeyValue)(primaryKey.fields, data) == primaryValue) {
39
+ if ((0, _compareAs.sameValue)((0, _fieldType.keyValueType)(record, primaryKey), (0, _computeKeyValue.computeKeyValue)(primaryKey.fields, data), primaryValue)) {
38
40
  return fileName;
39
41
  }
40
42
  }
@@ -43,15 +45,17 @@ const findEntryFile = async (record, primaryKey, primaryValue) => {
43
45
  /**
44
46
  * True if the update actually changes at least one of this key's fields - a key none of whose
45
47
  * fields appear in newValues, or whose given values are all unchanged, doesn't need re-checking.
48
+ * @param record
46
49
  * @param key
47
50
  * @param oldData
48
51
  * @param newValues
49
52
  */
50
- const keyFieldsChanged = (key, oldData, newValues) => key.fields.some(field => Object.prototype.hasOwnProperty.call(newValues, field) && newValues[field] != oldData[field]);
53
+ const keyFieldsChanged = (record, key, oldData, newValues) => key.fields.some(field => Object.prototype.hasOwnProperty.call(newValues, field) && !(0, _compareAs.sameValue)((0, _fieldType.fieldType)(record, field), newValues[field], oldData[field]));
51
54
  /**
52
55
  * Only check uniqueness for a primary/unique key (single-field, or composite) whose value is
53
56
  * actually changing - checking an unchanged value would find the record's own existing entry and
54
- * wrongly report it as a duplicate of itself.
57
+ * wrongly report it as a duplicate of itself. A value with a null in it is never a duplicate (as in
58
+ * SQL).
55
59
  * @param entity
56
60
  * @param record
57
61
  * @param oldData
@@ -60,10 +64,13 @@ const keyFieldsChanged = (key, oldData, newValues) => key.fields.some(field => O
60
64
  */
61
65
  const assertUniqueForUpdate = async (entity, record, oldData, newData, newValues) => {
62
66
  for (const key of record.keys) {
63
- if (key.type !== 'primary' && key.type !== 'unique' || !keyFieldsChanged(key, oldData, newValues)) {
67
+ if (key.type !== 'primary' && key.type !== 'unique' || !keyFieldsChanged(record, key, oldData, newValues)) {
64
68
  continue;
65
69
  }
66
70
  const checkKeys = key.fields.map(field => newData[field]);
71
+ if (checkKeys.some(value => value === null)) {
72
+ continue;
73
+ }
67
74
  const isUnique = await (0, _keyUnique.keyUnique)(entity, key.fields, checkKeys);
68
75
  if (!isUnique) {
69
76
  throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
@@ -73,7 +80,8 @@ const assertUniqueForUpdate = async (entity, record, oldData, newData, newValues
73
80
  /**
74
81
  * Same "only if it actually changed" scoping as assertUniqueForUpdate: re-checking an unchanged
75
82
  * foreign key value would re-validate a reference this update never touches, which could fail an
76
- * unrelated field's update if that parent record happened to be deleted since.
83
+ * unrelated field's update if that parent record happened to be deleted since. A null references
84
+ * nothing, so it isn't checked.
77
85
  * @param entity
78
86
  * @param record
79
87
  * @param oldData
@@ -85,7 +93,7 @@ const assertForeignKeysForUpdate = async (entity, record, oldData, newValues) =>
85
93
  continue;
86
94
  }
87
95
  const field = key.fields[0];
88
- if (!Object.prototype.hasOwnProperty.call(newValues, field) || newValues[field] == oldData[field]) {
96
+ if (!Object.prototype.hasOwnProperty.call(newValues, field) || newValues[field] === null || (0, _compareAs.sameValue)((0, _fieldType.fieldType)(record, field), newValues[field], oldData[field])) {
89
97
  continue;
90
98
  }
91
99
  const {
@@ -104,12 +112,13 @@ const assertForeignKeysForUpdate = async (entity, record, oldData, newValues) =>
104
112
  * dangling, since the row itself isn't gone, just re-keyed. Only meaningful for a single-field
105
113
  * primary key, matching foreign key support's own single-field-only scope.
106
114
  * @param entity
115
+ * @param record
107
116
  * @param primaryKey
108
117
  * @param oldData
109
118
  * @param newValues
110
119
  */
111
- const assertNoDependentsForUpdate = async (entity, primaryKey, oldData, newValues) => {
112
- if (!(0, _config.getSetting)('enforceForeignKeys', true) || primaryKey.fields.length !== 1 || !keyFieldsChanged(primaryKey, oldData, newValues)) {
120
+ const assertNoDependentsForUpdate = async (entity, record, primaryKey, oldData, newValues) => {
121
+ if (!(0, _config.getSetting)('enforceForeignKeys', true) || primaryKey.fields.length !== 1 || !keyFieldsChanged(record, primaryKey, oldData, newValues)) {
113
122
  return;
114
123
  }
115
124
  const property = primaryKey.fields[0];
@@ -128,11 +137,41 @@ const assertNoDependentsForUpdate = async (entity, primaryKey, oldData, newValue
128
137
  */
129
138
  const reconcileIndexes = async (record, oldData, newData, newValues, fileName) => {
130
139
  for (const key of record.keys) {
131
- if (!keyFieldsChanged(key, oldData, newValues)) {
140
+ if (!keyFieldsChanged(record, key, oldData, newValues)) {
141
+ continue;
142
+ }
143
+ await (0, _removeFromIndex.removeFromIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, oldData), fileName, (0, _fieldType.keyValueType)(record, key));
144
+ await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, newData), fileName, (0, _fieldType.keyValueType)(record, key));
145
+ }
146
+ };
147
+ /**
148
+ * The rows of one update are checked before any is written, so a check can't see the batch's own new values on disk.
149
+ * Reject the update if two of its rows would end with the same value for a primary or unique key that it changes (a
150
+ * value with a null in it is never a duplicate, as in assertUniqueForUpdate).
151
+ * @param entity
152
+ * @param record
153
+ * @param planned
154
+ * @param newValues
155
+ */
156
+ const assertUniqueWithinUpdate = (entity, record, planned, newValues) => {
157
+ for (const key of record.keys) {
158
+ if (key.type !== 'primary' && key.type !== 'unique') {
132
159
  continue;
133
160
  }
134
- await (0, _removeFromIndex.removeFromIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, oldData), fileName);
135
- await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, newData), fileName);
161
+ const claimed = [];
162
+ for (const {
163
+ oldData,
164
+ newData
165
+ } of planned) {
166
+ if (!keyFieldsChanged(record, key, oldData, newValues) || key.fields.some(field => newData[field] === null)) {
167
+ continue;
168
+ }
169
+ const value = (0, _computeKeyValue.computeKeyValue)(key.fields, newData);
170
+ if (claimed.some(other => (0, _compareAs.sameValue)((0, _fieldType.keyValueType)(record, key), other, value))) {
171
+ throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
172
+ }
173
+ claimed.push(value);
174
+ }
136
175
  }
137
176
  };
138
177
  const assertRequiredFields = (entity, record, newData) => {
@@ -154,7 +193,7 @@ const applyFieldTypes = (record, values) => {
154
193
  const typedValues = Object.assign({}, values);
155
194
  for (const field of record.definition) {
156
195
  if (Object.prototype.hasOwnProperty.call(typedValues, field.name)) {
157
- typedValues[field.name] = (0, _typeCheck.typeCheck)(field.type, typedValues[field.name]);
196
+ typedValues[field.name] = (0, _typeCheck.typeCheck)(field.type, typedValues[field.name], field.nullable);
158
197
  }
159
198
  }
160
199
  return typedValues;
@@ -167,24 +206,41 @@ const updateEntity = async (entity = '', values = {}, dataSet = {}) => {
167
206
  throw new Error(`Cannot update "${entity}" without a primary key`);
168
207
  }
169
208
  const typedValues = applyFieldTypes(record, values);
170
- const toUpdate = dataSet[entity] || [];
171
- const updatedRows = [];
172
- for (const row of toUpdate) {
209
+ // Check every row first and write only once all of them pass, so a row that fails leaves the others unchanged too
210
+ const planned = [];
211
+ for (const row of dataSet[entity] || []) {
173
212
  const fileName = await findEntryFile(record, primaryKey, (0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row));
174
213
  if (!fileName) {
175
214
  throw new Error(`Could not find entry for ${entity} where "${primaryKey.fields.join(', ')}" = ${(0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row)}`);
176
215
  }
216
+ if (planned.some(other => other.fileName === fileName)) {
217
+ // The same entry given twice is one row: updating it once is enough, and it can't collide with itself
218
+ continue;
219
+ }
177
220
  const oldData = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
178
221
  const newData = Object.assign({}, oldData, typedValues);
179
222
  await assertUniqueForUpdate(entity, record, oldData, newData, typedValues);
180
223
  await assertForeignKeysForUpdate(entity, record, oldData, typedValues);
181
- await assertNoDependentsForUpdate(entity, primaryKey, oldData, typedValues);
224
+ await assertNoDependentsForUpdate(entity, record, primaryKey, oldData, typedValues);
182
225
  assertRequiredFields(entity, record, newData);
226
+ planned.push({
227
+ fileName,
228
+ oldData,
229
+ newData
230
+ });
231
+ }
232
+ assertUniqueWithinUpdate(entity, record, planned, typedValues);
233
+ for (const {
234
+ fileName,
235
+ oldData,
236
+ newData
237
+ } of planned) {
183
238
  await reconcileIndexes(record, oldData, newData, typedValues, fileName);
184
239
  await writeFile(`${databasePath}${record.path}/${fileName}`, JSON.stringify(newData, null, 2));
185
- updatedRows.push(newData);
186
240
  }
187
- dataSet[entity] = updatedRows;
241
+ dataSet[entity] = planned.map(({
242
+ newData
243
+ }) => newData);
188
244
  return dataSet;
189
245
  };
190
246
  exports.updateEntity = updateEntity;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.updateEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_retrieveFile=require("../utilities/retrieveFile"),_updateIndex=require("../utilities/updateIndex"),_removeFromIndex=require("../utilities/removeFromIndex"),_computeKeyValue=require("../utilities/computeKeyValue"),_keyUnique=require("../introspect/policies/keyUnique"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_hasDependents=require("../introspect/policies/hasDependents"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),findEntryFile=async(e,t,r)=>{for(const i of e.entries){const s=await(0,_retrieveFile.retrieveFile)(`${e.path}/${i}`);if((0,_computeKeyValue.computeKeyValue)(t.fields,s)==r)return i}return null},keyFieldsChanged=(e,t,r)=>e.fields.some((e=>Object.prototype.hasOwnProperty.call(r,e)&&r[e]!=t[e])),assertUniqueForUpdate=async(e,t,r,i,s)=>{for(const o of t.keys){if("primary"!==o.type&&"unique"!==o.type||!keyFieldsChanged(o,r,s))continue;const t=o.fields.map((e=>i[e]));if(!await(0,_keyUnique.keyUnique)(e,o.fields,t))throw new Error(`Duplicate value for ${o.type} key "${o.fields.join(", ")}" on ${e}`)}},assertForeignKeysForUpdate=async(e,t,r,i)=>{for(const s of t.keys){if("foreign"!==s.type||1!==s.fields.length||!s.references||1!==s.references.length)continue;const t=s.fields[0];if(!Object.prototype.hasOwnProperty.call(i,t)||i[t]==r[t])continue;const{entity:o,property:n}=(0,_splitEntityProperty.splitEntityProperty)(s.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(o,n,i[t]))throw new Error(`Foreign key "${t}" on ${e} references a nonexistent ${o}.${n} = ${i[t]}`)}},assertNoDependentsForUpdate=async(e,t,r,i)=>{if(!(0,_config.getSetting)("enforceForeignKeys",!0)||1!==t.fields.length||!keyFieldsChanged(t,r,i))return;const s=t.fields[0];if(await(0,_hasDependents.hasDependents)(e,s,r[s]))throw new Error(`Cannot update "${e}" where "${s}" = ${r[s]} - other records still reference it`)},reconcileIndexes=async(e,t,r,i,s)=>{for(const o of e.keys)keyFieldsChanged(o,t,i)&&(await(0,_removeFromIndex.removeFromIndex)(e.path,o,(0,_computeKeyValue.computeKeyValue)(o.fields,t),s),await(0,_updateIndex.updateIndex)(e.path,o,(0,_computeKeyValue.computeKeyValue)(o.fields,r),s))},assertRequiredFields=(e,t,r)=>{for(const i of t.definition)if(!i.optional&&void 0===r[i.name])throw new Error(`Missing required field "${i.name}" for ${e}`)},applyFieldTypes=(e,t)=>{const r=Object.assign({},t);for(const t of e.definition)Object.prototype.hasOwnProperty.call(r,t.name)&&(r[t.name]=(0,_typeCheck.typeCheck)(t.type,r[t.name]));return r},updateEntity=async(e="",t={},r={})=>{const i=(0,_config.getSetting)("databasePath","database/"),s=await(0,_retrieveRecord.retrieveRecord)(e),o=s.keys.find((e=>"primary"===e.type));if(!o)throw new Error(`Cannot update "${e}" without a primary key`);const n=applyFieldTypes(s,t),a=r[e]||[],p=[];for(const t of a){const r=await findEntryFile(s,o,(0,_computeKeyValue.computeKeyValue)(o.fields,t));if(!r)throw new Error(`Could not find entry for ${e} where "${o.fields.join(", ")}" = ${(0,_computeKeyValue.computeKeyValue)(o.fields,t)}`);const a=await(0,_retrieveFile.retrieveFile)(`${s.path}/${r}`),u=Object.assign({},a,n);await assertUniqueForUpdate(e,s,a,u,n),await assertForeignKeysForUpdate(e,s,a,n),await assertNoDependentsForUpdate(e,o,a,n),assertRequiredFields(e,s,u),await reconcileIndexes(s,a,u,n,r),await writeFile(`${i}${s.path}/${r}`,JSON.stringify(u,null,2)),p.push(u)}return r[e]=p,r};exports.updateEntity=updateEntity;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.updateEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_retrieveFile=require("../utilities/retrieveFile"),_updateIndex=require("../utilities/updateIndex"),_removeFromIndex=require("../utilities/removeFromIndex"),_computeKeyValue=require("../utilities/computeKeyValue"),_fieldType=require("../utilities/fieldType"),_compareAs=require("../utilities/compareAs"),_keyUnique=require("../introspect/policies/keyUnique"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_hasDependents=require("../introspect/policies/hasDependents"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),findEntryFile=async(e,t,i)=>{for(const r of e.entries){const o=await(0,_retrieveFile.retrieveFile)(`${e.path}/${r}`);if((0,_compareAs.sameValue)((0,_fieldType.keyValueType)(e,t),(0,_computeKeyValue.computeKeyValue)(t.fields,o),i))return r}return null},keyFieldsChanged=(e,t,i,r)=>t.fields.some((t=>Object.prototype.hasOwnProperty.call(r,t)&&!(0,_compareAs.sameValue)((0,_fieldType.fieldType)(e,t),r[t],i[t]))),assertUniqueForUpdate=async(e,t,i,r,o)=>{for(const s of t.keys){if("primary"!==s.type&&"unique"!==s.type||!keyFieldsChanged(t,s,i,o))continue;const n=s.fields.map((e=>r[e]));if(n.some((e=>null===e)))continue;if(!await(0,_keyUnique.keyUnique)(e,s.fields,n))throw new Error(`Duplicate value for ${s.type} key "${s.fields.join(", ")}" on ${e}`)}},assertForeignKeysForUpdate=async(e,t,i,r)=>{for(const o of t.keys){if("foreign"!==o.type||1!==o.fields.length||!o.references||1!==o.references.length)continue;const s=o.fields[0];if(!Object.prototype.hasOwnProperty.call(r,s)||null===r[s]||(0,_compareAs.sameValue)((0,_fieldType.fieldType)(t,s),r[s],i[s]))continue;const{entity:n,property:a}=(0,_splitEntityProperty.splitEntityProperty)(o.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(n,a,r[s]))throw new Error(`Foreign key "${s}" on ${e} references a nonexistent ${n}.${a} = ${r[s]}`)}},assertNoDependentsForUpdate=async(e,t,i,r,o)=>{if(!(0,_config.getSetting)("enforceForeignKeys",!0)||1!==i.fields.length||!keyFieldsChanged(t,i,r,o))return;const s=i.fields[0];if(await(0,_hasDependents.hasDependents)(e,s,r[s]))throw new Error(`Cannot update "${e}" where "${s}" = ${r[s]} - other records still reference it`)},reconcileIndexes=async(e,t,i,r,o)=>{for(const s of e.keys)keyFieldsChanged(e,s,t,r)&&(await(0,_removeFromIndex.removeFromIndex)(e.path,s,(0,_computeKeyValue.computeKeyValue)(s.fields,t),o,(0,_fieldType.keyValueType)(e,s)),await(0,_updateIndex.updateIndex)(e.path,s,(0,_computeKeyValue.computeKeyValue)(s.fields,i),o,(0,_fieldType.keyValueType)(e,s)))},assertUniqueWithinUpdate=(e,t,i,r)=>{for(const o of t.keys){if("primary"!==o.type&&"unique"!==o.type)continue;const s=[];for(const{oldData:n,newData:a}of i){if(!keyFieldsChanged(t,o,n,r)||o.fields.some((e=>null===a[e])))continue;const i=(0,_computeKeyValue.computeKeyValue)(o.fields,a);if(s.some((e=>(0,_compareAs.sameValue)((0,_fieldType.keyValueType)(t,o),e,i))))throw new Error(`Duplicate value for ${o.type} key "${o.fields.join(", ")}" on ${e}`);s.push(i)}}},assertRequiredFields=(e,t,i)=>{for(const r of t.definition)if(!r.optional&&void 0===i[r.name])throw new Error(`Missing required field "${r.name}" for ${e}`)},applyFieldTypes=(e,t)=>{const i=Object.assign({},t);for(const t of e.definition)Object.prototype.hasOwnProperty.call(i,t.name)&&(i[t.name]=(0,_typeCheck.typeCheck)(t.type,i[t.name],t.nullable));return i},updateEntity=async(e="",t={},i={})=>{const r=(0,_config.getSetting)("databasePath","database/"),o=await(0,_retrieveRecord.retrieveRecord)(e),s=o.keys.find((e=>"primary"===e.type));if(!s)throw new Error(`Cannot update "${e}" without a primary key`);const n=applyFieldTypes(o,t),a=[];for(const t of i[e]||[]){const i=await findEntryFile(o,s,(0,_computeKeyValue.computeKeyValue)(s.fields,t));if(!i)throw new Error(`Could not find entry for ${e} where "${s.fields.join(", ")}" = ${(0,_computeKeyValue.computeKeyValue)(s.fields,t)}`);if(a.some((e=>e.fileName===i)))continue;const r=await(0,_retrieveFile.retrieveFile)(`${o.path}/${i}`),l=Object.assign({},r,n);await assertUniqueForUpdate(e,o,r,l,n),await assertForeignKeysForUpdate(e,o,r,n),await assertNoDependentsForUpdate(e,o,s,r,n),assertRequiredFields(e,o,l),a.push({fileName:i,oldData:r,newData:l})}assertUniqueWithinUpdate(e,o,a,n);for(const{fileName:e,oldData:t,newData:i}of a)await reconcileIndexes(o,t,i,n,e),await writeFile(`${r}${o.path}/${e}`,JSON.stringify(i,null,2));return i[e]=a.map((({newData:e})=>e)),i};exports.updateEntity=updateEntity;
@@ -1,7 +1,8 @@
1
- export declare const where: (dataSet?: any[], { property, joinEntity, comparator, value }?: {
1
+ export declare const where: (dataSet?: any[], { property, joinEntity, comparator, value, type }?: {
2
2
  property?: string;
3
3
  joinEntity?: any;
4
4
  comparator?: string;
5
5
  value?: any;
6
+ type?: any;
6
7
  }) => any[];
7
8
  //# sourceMappingURL=where.d.ts.map
@@ -7,6 +7,8 @@ exports.where = void 0;
7
7
  require("core-js/modules/es.array.includes.js");
8
8
  require("core-js/modules/esnext.iterator.constructor.js");
9
9
  require("core-js/modules/esnext.iterator.filter.js");
10
+ require("core-js/modules/esnext.iterator.some.js");
11
+ var _compareAs = require("../utilities/compareAs");
10
12
  const likeMatch = (dataValue, pattern) => {
11
13
  if (typeof dataValue !== 'string' || typeof pattern !== 'string') {
12
14
  return false;
@@ -14,17 +16,26 @@ const likeMatch = (dataValue, pattern) => {
14
16
  const regexPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/%/g, '.*').replace(/_/g, '.');
15
17
  return new RegExp(`^${regexPattern}$`, 'i').test(dataValue);
16
18
  };
17
- const makeCondition = (data, property, comparator, value) => {
19
+ /**
20
+ * Equality (=, !=, <>, in) compares by the field's declared type when given one (see sameValue), so
21
+ * '3' and 3 match in a string or int field. Ordering (>, <, between, like) compares the raw values.
22
+ * @param data
23
+ * @param property
24
+ * @param comparator
25
+ * @param value
26
+ * @param type
27
+ */
28
+ const makeCondition = (data, property, comparator, value, type = undefined) => {
18
29
  if (!Object.prototype.hasOwnProperty.call(data, property)) {
19
30
  return false;
20
31
  }
21
32
  const dataValue = data[property];
22
33
  switch (comparator) {
23
34
  case '=':
24
- return dataValue == value;
35
+ return (0, _compareAs.sameValue)(type, dataValue, value);
25
36
  case '!=':
26
37
  case '<>':
27
- return dataValue != value;
38
+ return !(0, _compareAs.sameValue)(type, dataValue, value);
28
39
  case '>':
29
40
  return dataValue > value;
30
41
  case '>=':
@@ -34,7 +45,7 @@ const makeCondition = (data, property, comparator, value) => {
34
45
  case '<=':
35
46
  return dataValue <= value;
36
47
  case 'in':
37
- return Array.isArray(value) && value.includes(dataValue);
48
+ return Array.isArray(value) && (type ? value.some(item => (0, _compareAs.sameValue)(type, dataValue, item)) : value.includes(dataValue));
38
49
  case 'between':
39
50
  return Array.isArray(value) && value.length === 2 && dataValue >= value[0] && dataValue <= value[1];
40
51
  case 'like':
@@ -46,11 +57,12 @@ const where = (dataSet = [], {
46
57
  property = '',
47
58
  joinEntity = undefined,
48
59
  comparator = '=',
49
- value = null
60
+ value = null,
61
+ type = undefined
50
62
  } = {}) => {
51
63
  return dataSet.filter(data => {
52
64
  const testProperty = joinEntity ? `${joinEntity}.${property}` : property;
53
- return makeCondition(data, testProperty, comparator, value);
65
+ return makeCondition(data, testProperty, comparator, value, type);
54
66
  });
55
67
  };
56
68
  exports.where = where;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.where=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js");const likeMatch=(e,r)=>{if("string"!=typeof e||"string"!=typeof r)return!1;const t=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${t}$`,"i").test(e)},makeCondition=(e,r,t,s)=>{if(!Object.prototype.hasOwnProperty.call(e,r))return!1;const n=e[r];switch(t){case"=":return n==s;case"!=":case"<>":return n!=s;case">":return n>s;case">=":return n>=s;case"<":return n<s;case"<=":return n<=s;case"in":return Array.isArray(s)&&s.includes(n);case"between":return Array.isArray(s)&&2===s.length&&n>=s[0]&&n<=s[1];case"like":return likeMatch(n,s)}return!1},where=(e=[],{property:r="",joinEntity:t,comparator:s="=",value:n=null}={})=>e.filter((e=>makeCondition(e,t?`${t}.${r}`:r,s,n)));exports.where=where;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.where=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.some.js");var _compareAs=require("../utilities/compareAs");const likeMatch=(e,r)=>{if("string"!=typeof e||"string"!=typeof r)return!1;const t=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${t}$`,"i").test(e)},makeCondition=(e,r,t,s,a=void 0)=>{if(!Object.prototype.hasOwnProperty.call(e,r))return!1;const o=e[r];switch(t){case"=":return(0,_compareAs.sameValue)(a,o,s);case"!=":case"<>":return!(0,_compareAs.sameValue)(a,o,s);case">":return o>s;case">=":return o>=s;case"<":return o<s;case"<=":return o<=s;case"in":return Array.isArray(s)&&(a?s.some((e=>(0,_compareAs.sameValue)(a,o,e))):s.includes(o));case"between":return Array.isArray(s)&&2===s.length&&o>=s[0]&&o<=s[1];case"like":return likeMatch(o,s)}return!1},where=(e=[],{property:r="",joinEntity:t,comparator:s="=",value:a=null,type:o}={})=>e.filter((e=>makeCondition(e,t?`${t}.${r}`:r,s,a,o)));exports.where=where;