json-fs-query 1.1.0 → 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 (113) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +295 -98
  3. package/browser/jsonFsQuery.js +12733 -10934
  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 +28 -28
  15. package/dist/data/introspect/policies/hasDependents.js +53 -53
  16. package/dist/data/introspect/policies/keyUnique.js +34 -34
  17. package/dist/data/introspect/policies/typeCheck.d.ts +9 -1
  18. package/dist/data/introspect/policies/typeCheck.js +16 -2
  19. package/dist/data/introspect/policies/typeCheck.min.js +1 -1
  20. package/dist/data/queries/createRecord.d.ts +11 -0
  21. package/dist/data/queries/createRecord.js +58 -0
  22. package/dist/data/queries/createRecord.min.js +1 -0
  23. package/dist/data/queries/deleteEntity.js +100 -100
  24. package/dist/data/queries/describeRecord.d.ts +9 -0
  25. package/dist/data/queries/describeRecord.js +29 -0
  26. package/dist/data/queries/describeRecord.min.js +1 -0
  27. package/dist/data/queries/dropRecord.d.ts +9 -0
  28. package/dist/data/queries/dropRecord.js +61 -0
  29. package/dist/data/queries/dropRecord.min.js +1 -0
  30. package/dist/data/queries/insertEntity.js +122 -117
  31. package/dist/data/queries/insertEntity.min.js +1 -1
  32. package/dist/data/queries/parser.d.ts +1 -1
  33. package/dist/data/queries/parser.js +7 -15
  34. package/dist/data/queries/parser.min.js +1 -1
  35. package/dist/data/queries/query.d.ts +6 -0
  36. package/dist/data/queries/query.js +15 -6
  37. package/dist/data/queries/query.min.js +1 -1
  38. package/dist/data/queries/queryStatus.d.ts +15 -0
  39. package/dist/data/queries/queryStatus.js +52 -0
  40. package/dist/data/queries/queryStatus.min.js +1 -0
  41. package/dist/data/queries/quote.d.ts +11 -0
  42. package/dist/data/queries/quote.js +17 -0
  43. package/dist/data/queries/quote.min.js +1 -0
  44. package/dist/data/queries/showRecords.d.ts +6 -0
  45. package/dist/data/queries/showRecords.js +21 -0
  46. package/dist/data/queries/showRecords.min.js +1 -0
  47. package/dist/data/queries/sortBy.js +27 -27
  48. package/dist/data/queries/updateEntity.js +245 -193
  49. package/dist/data/queries/updateEntity.min.js +1 -1
  50. package/dist/data/queries/where.d.ts +7 -7
  51. package/dist/data/queries/where.js +67 -67
  52. package/dist/data/utilities/compareAs.d.ts +19 -19
  53. package/dist/data/utilities/compareAs.js +57 -57
  54. package/dist/data/utilities/config.d.ts +15 -0
  55. package/dist/data/utilities/config.js +31 -1
  56. package/dist/data/utilities/config.min.js +1 -1
  57. package/dist/data/utilities/databaseLock.d.ts +16 -0
  58. package/dist/data/utilities/databaseLock.js +63 -0
  59. package/dist/data/utilities/databaseLock.min.js +1 -0
  60. package/dist/data/utilities/fieldType.d.ts +15 -15
  61. package/dist/data/utilities/fieldType.js +22 -22
  62. package/dist/data/utilities/getConditionIndexedList.js +47 -47
  63. package/dist/data/utilities/matchIndexedJoins.js +62 -62
  64. package/dist/data/utilities/mergeJoin.js +47 -47
  65. package/dist/data/utilities/parsers/findConditions.d.ts +18 -15
  66. package/dist/data/utilities/parsers/findConditions.js +15 -6
  67. package/dist/data/utilities/parsers/findConditions.min.js +1 -1
  68. package/dist/data/utilities/parsers/findInsertValues.js +3 -2
  69. package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
  70. package/dist/data/utilities/parsers/findSelects.js +6 -3
  71. package/dist/data/utilities/parsers/findSelects.min.js +1 -1
  72. package/dist/data/utilities/parsers/findSortClauses.js +5 -2
  73. package/dist/data/utilities/parsers/findSortClauses.min.js +1 -1
  74. package/dist/data/utilities/parsers/findUpdateValues.js +3 -2
  75. package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -1
  76. package/dist/data/utilities/parsers/maskStrings.d.ts +39 -0
  77. package/dist/data/utilities/parsers/maskStrings.js +123 -0
  78. package/dist/data/utilities/parsers/maskStrings.min.js +1 -0
  79. package/dist/data/utilities/parsers/parseCreate.d.ts +20 -0
  80. package/dist/data/utilities/parsers/parseCreate.js +330 -0
  81. package/dist/data/utilities/parsers/parseCreate.min.js +1 -0
  82. package/dist/data/utilities/parsers/parseLiteralValue.d.ts +2 -1
  83. package/dist/data/utilities/parsers/parseLiteralValue.js +6 -3
  84. package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -1
  85. package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
  86. package/dist/data/utilities/parsers/parseQuery.js +28 -2
  87. package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
  88. package/dist/data/utilities/parsers/parseSchemaStatement.d.ts +13 -0
  89. package/dist/data/utilities/parsers/parseSchemaStatement.js +82 -0
  90. package/dist/data/utilities/parsers/parseSchemaStatement.min.js +1 -0
  91. package/dist/data/utilities/parsers/scanQuery.d.ts +52 -0
  92. package/dist/data/utilities/parsers/scanQuery.js +158 -0
  93. package/dist/data/utilities/parsers/scanQuery.min.js +1 -0
  94. package/dist/data/utilities/parsers/splitQueries.d.ts +6 -0
  95. package/dist/data/utilities/parsers/splitQueries.js +20 -2
  96. package/dist/data/utilities/parsers/splitQueries.min.js +1 -1
  97. package/dist/data/utilities/parsers/startsQuery.d.ts +5 -0
  98. package/dist/data/utilities/parsers/startsQuery.js +10 -0
  99. package/dist/data/utilities/parsers/startsQuery.min.js +1 -0
  100. package/dist/data/utilities/recordExists.d.ts +6 -0
  101. package/dist/data/utilities/recordExists.js +18 -0
  102. package/dist/data/utilities/recordExists.min.js +1 -0
  103. package/dist/data/utilities/removeFromIndex.d.ts +14 -14
  104. package/dist/data/utilities/removeFromIndex.js +45 -45
  105. package/dist/data/utilities/runQuery.js +135 -112
  106. package/dist/data/utilities/runQuery.min.js +1 -1
  107. package/dist/data/utilities/updateIndex.d.ts +17 -17
  108. package/dist/data/utilities/updateIndex.js +57 -57
  109. package/dist/data/utilities/useJoinClause.js +56 -56
  110. package/dist/index.d.ts +2 -0
  111. package/dist/index.js +14 -0
  112. package/dist/index.min.js +1 -1
  113. package/package.json +68 -60
@@ -2,7 +2,7 @@ import { recordDefinition } from './definitions/record';
2
2
  import { keyProperties } from './definitions/key';
3
3
  import { fieldProperties } from './definitions/field';
4
4
  /**
5
- * Create a new record.
5
+ * Create a new record. Throws if the definition is invalid (see assertValidDefinition).
6
6
  * @param recordName
7
7
  * @param definition
8
8
  * @param keys
@@ -5,11 +5,41 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.create = void 0;
7
7
  require("core-js/modules/es.json.stringify.js");
8
+ require("core-js/modules/esnext.iterator.constructor.js");
9
+ require("core-js/modules/esnext.iterator.find.js");
10
+ require("core-js/modules/esnext.iterator.some.js");
8
11
  var _record = require("./definitions/record");
9
12
  var _registerRecord = require("../utilities/registerRecord");
10
13
  var _config = require("../utilities/config");
14
+ var _typeCheck = require("./policies/typeCheck");
11
15
  /**
12
- * Create a new record.
16
+ * Reject a definition that could never be written correctly, before anything is created: a
17
+ * default that fails its field's type check (otherwise every insert leaving that field out would
18
+ * fail instead), including a null default on a field that isn't nullable, and a nullable field in
19
+ * the primary key (an entry's file name comes from its primary key value).
20
+ * @param recordName
21
+ * @param recordContent
22
+ */
23
+ const assertValidDefinition = (recordName, recordContent) => {
24
+ for (const field of recordContent.definition) {
25
+ if (!Object.prototype.hasOwnProperty.call(field, 'default')) {
26
+ continue;
27
+ }
28
+ try {
29
+ (0, _typeCheck.typeCheck)(field.type, field['default'], field.nullable);
30
+ } catch (err) {
31
+ throw new Error(`Invalid default for field "${field.name}" of ${recordName}: ${err.message}`);
32
+ }
33
+ }
34
+ const primaryKey = recordContent.keys.find(key => key.type === 'primary');
35
+ for (const fieldName of primaryKey ? primaryKey.fields : []) {
36
+ if (recordContent.definition.some(field => field.name === fieldName && field.nullable)) {
37
+ throw new Error(`Primary key field "${fieldName}" of ${recordName} can't be nullable`);
38
+ }
39
+ }
40
+ };
41
+ /**
42
+ * Create a new record. Throws if the definition is invalid (see assertValidDefinition).
13
43
  * @param recordName
14
44
  * @param definition
15
45
  * @param keys
@@ -29,14 +59,15 @@ const create = async (recordName, definition = [], keys = []) => {
29
59
  if (existsSync(`${databasePath}${recordName}.json`)) {
30
60
  return null;
31
61
  }
32
- await mkdir(databasePath, {
33
- recursive: true
34
- });
35
62
  const recordContent = (0, _record.record)({
36
63
  path: recordName,
37
64
  definition,
38
65
  keys
39
66
  });
67
+ assertValidDefinition(recordName, recordContent);
68
+ await mkdir(databasePath, {
69
+ recursive: true
70
+ });
40
71
  await writeFile(`${databasePath}${recordName}.json`, JSON.stringify(recordContent, null, 2));
41
72
  await mkdir(`${databasePath}${recordName}`, {
42
73
  recursive: true
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.create=void 0,require("core-js/modules/es.json.stringify.js");var _record=require("./definitions/record"),_registerRecord=require("../utilities/registerRecord"),_config=require("../utilities/config");const create=async(e,r=[],i=[])=>{const{writeFile:t,mkdir:s}=require("fs/promises"),{existsSync:o}=require("fs"),c=(0,_config.getSetting)("databasePath","database/");if("__RECORDS"===e)return null;if(o(`${c}${e}.json`))return null;await s(c,{recursive:!0});const n=(0,_record.record)({path:e,definition:r,keys:i});return await t(`${c}${e}.json`,JSON.stringify(n,null,2)),await s(`${c}${e}`,{recursive:!0}),await(0,_registerRecord.registerRecord)(e),n};exports.create=create;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.create=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.some.js");var _record=require("./definitions/record"),_registerRecord=require("../utilities/registerRecord"),_config=require("../utilities/config"),_typeCheck=require("./policies/typeCheck");const assertValidDefinition=(e,r)=>{for(const t of r.definition)if(Object.prototype.hasOwnProperty.call(t,"default"))try{(0,_typeCheck.typeCheck)(t.type,t.default,t.nullable)}catch(r){throw new Error(`Invalid default for field "${t.name}" of ${e}: ${r.message}`)}const t=r.keys.find((e=>"primary"===e.type));for(const i of t?t.fields:[])if(r.definition.some((e=>e.name===i&&e.nullable)))throw new Error(`Primary key field "${i}" of ${e} can't be nullable`)},create=async(e,r=[],t=[])=>{const{writeFile:i,mkdir:s}=require("fs/promises"),{existsSync:o}=require("fs"),n=(0,_config.getSetting)("databasePath","database/");if("__RECORDS"===e)return null;if(o(`${n}${e}.json`))return null;const a=(0,_record.record)({path:e,definition:r,keys:t});return assertValidDefinition(e,a),await s(n,{recursive:!0}),await i(`${n}${e}.json`,JSON.stringify(a,null,2)),await s(`${n}${e}`,{recursive:!0}),await(0,_registerRecord.registerRecord)(e),a};exports.create=create;
@@ -3,6 +3,7 @@ export type fieldDefinition = {
3
3
  name: fieldName;
4
4
  type: string;
5
5
  optional: boolean;
6
+ nullable?: boolean;
6
7
  default?: any;
7
8
  autoGenerate: boolean;
8
9
  };
@@ -10,19 +11,22 @@ export type fieldProperties = {
10
11
  name?: string;
11
12
  type?: string;
12
13
  optional?: boolean;
14
+ nullable?: boolean;
13
15
  useDefault?: boolean;
14
16
  defaultValue?: any;
15
17
  autoGenerate?: boolean;
16
18
  };
17
19
  /**
18
- * Generate a field for a record.
20
+ * Generate a field for a record. `nullable` is only stored when true (like `default`), so fields
21
+ * that never mention it, and schemas written before it existed, stay exactly as they were.
19
22
  * @param properties
20
23
  * @param properties.name
21
24
  * @param properties.type
22
25
  * @param properties.optional
26
+ * @param properties.nullable
23
27
  * @param properties.useDefault
24
28
  * @param properties.defaultValue
25
29
  * @param properties.autoGenerate
26
30
  */
27
- export declare const field: ({ name, type, optional, useDefault, defaultValue, autoGenerate }?: fieldProperties) => fieldDefinition;
31
+ export declare const field: ({ name, type, optional, nullable, useDefault, defaultValue, autoGenerate }?: fieldProperties) => fieldDefinition;
28
32
  //# sourceMappingURL=field.d.ts.map
@@ -5,11 +5,13 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.field = void 0;
7
7
  /**
8
- * Generate a field for a record.
8
+ * Generate a field for a record. `nullable` is only stored when true (like `default`), so fields
9
+ * that never mention it, and schemas written before it existed, stay exactly as they were.
9
10
  * @param properties
10
11
  * @param properties.name
11
12
  * @param properties.type
12
13
  * @param properties.optional
14
+ * @param properties.nullable
13
15
  * @param properties.useDefault
14
16
  * @param properties.defaultValue
15
17
  * @param properties.autoGenerate
@@ -18,6 +20,7 @@ const field = ({
18
20
  name = '',
19
21
  type = 'string',
20
22
  optional = false,
23
+ nullable = false,
21
24
  useDefault = false,
22
25
  defaultValue = '',
23
26
  autoGenerate = false
@@ -28,6 +31,9 @@ const field = ({
28
31
  optional,
29
32
  autoGenerate
30
33
  };
34
+ if (nullable) {
35
+ returnField['nullable'] = true;
36
+ }
31
37
  if (useDefault) {
32
38
  returnField['default'] = defaultValue;
33
39
  }
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.field=void 0;const field=({name:e="",type:t="string",optional:o=!1,useDefault:a=!1,defaultValue:l="",autoGenerate:n=!1}={})=>{const r={name:e,type:t,optional:o,autoGenerate:n};return a&&(r.default=l),r};exports.field=field;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.field=void 0;const field=({name:e="",type:t="string",optional:l=!1,nullable:a=!1,useDefault:o=!1,defaultValue:n="",autoGenerate:u=!1}={})=>{const r={name:e,type:t,optional:l,autoGenerate:u};return a&&(r.nullable=!0),o&&(r.default=n),r};exports.field=field;
@@ -1,6 +1,9 @@
1
- export { create } from './create';
2
- export { drop } from './drop';
3
- export { describe } from './describe';
1
+ import { create as createRecord } from './create';
2
+ import { drop as dropRecord } from './drop';
3
+ import { describe as describeRecord } from './describe';
4
+ export declare const create: (...args: Parameters<typeof createRecord>) => ReturnType<typeof createRecord>;
5
+ export declare const drop: (...args: Parameters<typeof dropRecord>) => ReturnType<typeof dropRecord>;
6
+ export declare const describe: (...args: Parameters<typeof describeRecord>) => ReturnType<typeof describeRecord>;
4
7
  export { fieldProperties, fieldDefinition, fieldName } from './definitions/field';
5
8
  export { keyProperties, keyDefinition, indexType, indexLocation } from './definitions/key';
6
9
  export { referenceProperties, reference } from './definitions/keyReference';
@@ -7,24 +7,15 @@ require("core-js/modules/esnext.iterator.drop.js");
7
7
  Object.defineProperty(exports, "__esModule", {
8
8
  value: true
9
9
  });
10
- Object.defineProperty(exports, "create", {
11
- enumerable: true,
12
- get: function () {
13
- return _create.create;
14
- }
15
- });
16
- Object.defineProperty(exports, "describe", {
17
- enumerable: true,
18
- get: function () {
19
- return _describe.describe;
20
- }
21
- });
22
- Object.defineProperty(exports, "drop", {
23
- enumerable: true,
24
- get: function () {
25
- return _drop.drop;
26
- }
27
- });
10
+ exports.drop = exports.describe = exports.create = void 0;
28
11
  var _create = require("./create");
29
12
  var _drop = require("./drop");
30
- var _describe = require("./describe");
13
+ var _describe = require("./describe");
14
+ var _databaseLock = require("../utilities/databaseLock");
15
+ // The public versions take the database lock (see withDatabaseLock); code inside the package imports the files directly
16
+ const create = (...args) => (0, _databaseLock.withDatabaseLock)(() => (0, _create.create)(...args));
17
+ exports.create = create;
18
+ const drop = (...args) => (0, _databaseLock.withDatabaseLock)(() => (0, _drop.drop)(...args));
19
+ exports.drop = drop;
20
+ const describe = (...args) => (0, _databaseLock.withDatabaseLock)(() => (0, _describe.describe)(...args));
21
+ exports.describe = describe;
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.async-iterator.constructor.js"),require("core-js/modules/esnext.async-iterator.drop.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.drop.js"),Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"create",{enumerable:!0,get:function(){return _create.create}}),Object.defineProperty(exports,"describe",{enumerable:!0,get:function(){return _describe.describe}}),Object.defineProperty(exports,"drop",{enumerable:!0,get:function(){return _drop.drop}});var _create=require("./create"),_drop=require("./drop"),_describe=require("./describe");
1
+ "use strict";require("core-js/modules/esnext.async-iterator.constructor.js"),require("core-js/modules/esnext.async-iterator.drop.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.drop.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.drop=exports.describe=exports.create=void 0;var _create=require("./create"),_drop=require("./drop"),_describe=require("./describe"),_databaseLock=require("../utilities/databaseLock");const create=(...e)=>(0,_databaseLock.withDatabaseLock)((()=>(0,_create.create)(...e)));exports.create=create;const drop=(...e)=>(0,_databaseLock.withDatabaseLock)((()=>(0,_drop.drop)(...e)));exports.drop=drop;const describe=(...e)=>(0,_databaseLock.withDatabaseLock)((()=>(0,_describe.describe)(...e)));exports.describe=describe;
@@ -1,29 +1,29 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.foreignKeyExists = void 0;
7
- var _retrieveRecords = require("../../utilities/retrieveRecords");
8
- var _where = require("../../queries/where");
9
- var _retrieveRecord = require("../../utilities/retrieveRecord");
10
- var _fieldType = require("../../utilities/fieldType");
11
- /**
12
- * Check whether any record in the referenced entity has the given property set to the given
13
- * value - the mirror image of keyUnique(), which checks that no record already has a value.
14
- * @param entity
15
- * @param property
16
- * @param value
17
- */
18
- const foreignKeyExists = async (entity, property, value) => {
19
- const records = await (0, _retrieveRecords.retrieveRecords)(entity);
20
- const type = (0, _fieldType.fieldType)(await (0, _retrieveRecord.retrieveRecord)(entity), property);
21
- const found = (0, _where.where)(records, {
22
- property,
23
- comparator: '=',
24
- value,
25
- type
26
- });
27
- return found.length > 0;
28
- };
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.foreignKeyExists = void 0;
7
+ var _retrieveRecords = require("../../utilities/retrieveRecords");
8
+ var _where = require("../../queries/where");
9
+ var _retrieveRecord = require("../../utilities/retrieveRecord");
10
+ var _fieldType = require("../../utilities/fieldType");
11
+ /**
12
+ * Check whether any record in the referenced entity has the given property set to the given
13
+ * value - the mirror image of keyUnique(), which checks that no record already has a value.
14
+ * @param entity
15
+ * @param property
16
+ * @param value
17
+ */
18
+ const foreignKeyExists = async (entity, property, value) => {
19
+ const records = await (0, _retrieveRecords.retrieveRecords)(entity);
20
+ const type = (0, _fieldType.fieldType)(await (0, _retrieveRecord.retrieveRecord)(entity), property);
21
+ const found = (0, _where.where)(records, {
22
+ property,
23
+ comparator: '=',
24
+ value,
25
+ type
26
+ });
27
+ return found.length > 0;
28
+ };
29
29
  exports.foreignKeyExists = foreignKeyExists;
@@ -1,54 +1,54 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.hasDependents = void 0;
7
- var _describe = require("../describe");
8
- var _retrieveRecord = require("../../utilities/retrieveRecord");
9
- var _retrieveRecords = require("../../utilities/retrieveRecords");
10
- var _where = require("../../queries/where");
11
- var _fieldType = require("../../utilities/fieldType");
12
- var _splitEntityProperty = require("../../utilities/parsers/splitEntityProperty");
13
- /**
14
- * Check whether any other record, in any entity, still has a single-field foreign key pointing
15
- * at entity.property = value - the mirror image of foreignKeyExists(), which checks the opposite
16
- * direction (that a new/updated foreign key value points at something real). Used to RESTRICT a
17
- * delete or update that would otherwise leave a dangling reference behind. Scans every record in
18
- * the database via the __RECORDS registry, the same "no index needed, just look" approach as
19
- * keyUnique()/foreignKeyExists() - correctness over speed, matching this project's overall scale.
20
- * @param entity
21
- * @param property
22
- * @param value
23
- */
24
- const hasDependents = async (entity, property, value) => {
25
- const entityFiles = await (0, _describe.describe)();
26
- for (const entityFile of entityFiles) {
27
- const otherEntity = entityFile.replace(/\.json$/, '');
28
- const otherRecord = await (0, _retrieveRecord.retrieveRecord)(otherEntity);
29
- for (const key of otherRecord.keys) {
30
- if (key.type !== 'foreign' || key.fields.length !== 1 || !key.references || key.references.length !== 1) {
31
- continue;
32
- }
33
- const {
34
- entity: refEntity,
35
- property: refProperty
36
- } = (0, _splitEntityProperty.splitEntityProperty)(key.references[0].fields[0]);
37
- if (refEntity !== entity || refProperty !== property) {
38
- continue;
39
- }
40
- const records = await (0, _retrieveRecords.retrieveRecords)(otherEntity);
41
- const found = (0, _where.where)(records, {
42
- property: key.fields[0],
43
- comparator: '=',
44
- value,
45
- type: (0, _fieldType.fieldType)(otherRecord, key.fields[0])
46
- });
47
- if (found.length) {
48
- return true;
49
- }
50
- }
51
- }
52
- return false;
53
- };
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.hasDependents = void 0;
7
+ var _describe = require("../describe");
8
+ var _retrieveRecord = require("../../utilities/retrieveRecord");
9
+ var _retrieveRecords = require("../../utilities/retrieveRecords");
10
+ var _where = require("../../queries/where");
11
+ var _fieldType = require("../../utilities/fieldType");
12
+ var _splitEntityProperty = require("../../utilities/parsers/splitEntityProperty");
13
+ /**
14
+ * Check whether any other record, in any entity, still has a single-field foreign key pointing
15
+ * at entity.property = value - the mirror image of foreignKeyExists(), which checks the opposite
16
+ * direction (that a new/updated foreign key value points at something real). Used to RESTRICT a
17
+ * delete or update that would otherwise leave a dangling reference behind. Scans every record in
18
+ * the database via the __RECORDS registry, the same "no index needed, just look" approach as
19
+ * keyUnique()/foreignKeyExists() - correctness over speed, matching this project's overall scale.
20
+ * @param entity
21
+ * @param property
22
+ * @param value
23
+ */
24
+ const hasDependents = async (entity, property, value) => {
25
+ const entityFiles = await (0, _describe.describe)();
26
+ for (const entityFile of entityFiles) {
27
+ const otherEntity = entityFile.replace(/\.json$/, '');
28
+ const otherRecord = await (0, _retrieveRecord.retrieveRecord)(otherEntity);
29
+ for (const key of otherRecord.keys) {
30
+ if (key.type !== 'foreign' || key.fields.length !== 1 || !key.references || key.references.length !== 1) {
31
+ continue;
32
+ }
33
+ const {
34
+ entity: refEntity,
35
+ property: refProperty
36
+ } = (0, _splitEntityProperty.splitEntityProperty)(key.references[0].fields[0]);
37
+ if (refEntity !== entity || refProperty !== property) {
38
+ continue;
39
+ }
40
+ const records = await (0, _retrieveRecords.retrieveRecords)(otherEntity);
41
+ const found = (0, _where.where)(records, {
42
+ property: key.fields[0],
43
+ comparator: '=',
44
+ value,
45
+ type: (0, _fieldType.fieldType)(otherRecord, key.fields[0])
46
+ });
47
+ if (found.length) {
48
+ return true;
49
+ }
50
+ }
51
+ }
52
+ return false;
53
+ };
54
54
  exports.hasDependents = hasDependents;
@@ -1,35 +1,35 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.keyUnique = void 0;
7
- var _retrieveRecords = require("../../utilities/retrieveRecords");
8
- var _where = require("../../queries/where");
9
- var _retrieveRecord = require("../../utilities/retrieveRecord");
10
- var _fieldType = require("../../utilities/fieldType");
11
- /**
12
- * Check that no existing record already has this combination of values across all of the given
13
- * fields - a single field for a normal unique/primary key, or several for a composite one.
14
- * references and checkKeys are parallel arrays: references[i]'s value must equal checkKeys[i].
15
- * @param entity
16
- * @param references
17
- * @param checkKeys
18
- */
19
- const keyUnique = async (entity, references = [], checkKeys = []) => {
20
- if (!references.length || references.length !== checkKeys.length) {
21
- return false;
22
- }
23
- let records = await (0, _retrieveRecords.retrieveRecords)(entity);
24
- const record = await (0, _retrieveRecord.retrieveRecord)(entity);
25
- for (let i = 0; i < references.length; i++) {
26
- records = (0, _where.where)(records, {
27
- property: references[i],
28
- comparator: '=',
29
- value: checkKeys[i],
30
- type: (0, _fieldType.fieldType)(record, references[i])
31
- });
32
- }
33
- return !records.length;
34
- };
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.keyUnique = void 0;
7
+ var _retrieveRecords = require("../../utilities/retrieveRecords");
8
+ var _where = require("../../queries/where");
9
+ var _retrieveRecord = require("../../utilities/retrieveRecord");
10
+ var _fieldType = require("../../utilities/fieldType");
11
+ /**
12
+ * Check that no existing record already has this combination of values across all of the given
13
+ * fields - a single field for a normal unique/primary key, or several for a composite one.
14
+ * references and checkKeys are parallel arrays: references[i]'s value must equal checkKeys[i].
15
+ * @param entity
16
+ * @param references
17
+ * @param checkKeys
18
+ */
19
+ const keyUnique = async (entity, references = [], checkKeys = []) => {
20
+ if (!references.length || references.length !== checkKeys.length) {
21
+ return false;
22
+ }
23
+ let records = await (0, _retrieveRecords.retrieveRecords)(entity);
24
+ const record = await (0, _retrieveRecord.retrieveRecord)(entity);
25
+ for (let i = 0; i < references.length; i++) {
26
+ records = (0, _where.where)(records, {
27
+ property: references[i],
28
+ comparator: '=',
29
+ value: checkKeys[i],
30
+ type: (0, _fieldType.fieldType)(record, references[i])
31
+ });
32
+ }
33
+ return !records.length;
34
+ };
35
35
  exports.keyUnique = keyUnique;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * The type names typeCheck knows. The create statement accepts only these; create() accepts any name (see below).
3
+ */
4
+ export declare const fieldTypes: string[];
1
5
  /**
2
6
  * Validate (and where sensible, coerce) a value against a field's declared type. Runs only
3
7
  * against values being written by insertEntity/updateEntity - it never re-validates data already
@@ -5,8 +9,12 @@
5
9
  * happen, since nothing enforced this before) is left alone until something actually updates it.
6
10
  * An unrecognized type name is treated as unconstrained rather than rejected, so a typo in a
7
11
  * schema's field type can't silently block every write to that field.
12
+ *
13
+ * A null is a real JSON null, kept as it is for a nullable field and rejected for any other, of
14
+ * any type: it is never coerced, so a string field can't store the text "null" or an int field 0.
8
15
  * @param type
9
16
  * @param value
17
+ * @param nullable - Whether the field may hold null (the field definition's `nullable`).
10
18
  */
11
- export declare const typeCheck: (type?: string, value?: any) => any;
19
+ export declare const typeCheck: (type?: string, value?: any, nullable?: boolean) => any;
12
20
  //# sourceMappingURL=typeCheck.d.ts.map
@@ -3,7 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.typeCheck = void 0;
6
+ exports.typeCheck = exports.fieldTypes = void 0;
7
+ /**
8
+ * The type names typeCheck knows. The create statement accepts only these; create() accepts any name (see below).
9
+ */
10
+ const fieldTypes = exports.fieldTypes = ['string', 'int', 'boolean', 'object', 'array'];
7
11
  /**
8
12
  * Validate (and where sensible, coerce) a value against a field's declared type. Runs only
9
13
  * against values being written by insertEntity/updateEntity - it never re-validates data already
@@ -11,13 +15,23 @@ exports.typeCheck = void 0;
11
15
  * happen, since nothing enforced this before) is left alone until something actually updates it.
12
16
  * An unrecognized type name is treated as unconstrained rather than rejected, so a typo in a
13
17
  * schema's field type can't silently block every write to that field.
18
+ *
19
+ * A null is a real JSON null, kept as it is for a nullable field and rejected for any other, of
20
+ * any type: it is never coerced, so a string field can't store the text "null" or an int field 0.
14
21
  * @param type
15
22
  * @param value
23
+ * @param nullable - Whether the field may hold null (the field definition's `nullable`).
16
24
  */
17
- const typeCheck = (type = 'string', value = undefined) => {
25
+ const typeCheck = (type = 'string', value = undefined, nullable = false) => {
18
26
  if (typeof value === 'undefined') {
19
27
  return value;
20
28
  }
29
+ if (value === null) {
30
+ if (nullable) {
31
+ return value;
32
+ }
33
+ throw new Error('Value "null" is not allowed: the field is not nullable');
34
+ }
21
35
  switch (type) {
22
36
  case 'string':
23
37
  return typeof value === 'string' ? value : String(value);
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.typeCheck=void 0;const typeCheck=(r="string",e=void 0)=>{if(void 0===e)return e;switch(r){case"string":return"string"==typeof e?e:String(e);case"int":{const r=Number(e);if(Number.isNaN(r)||!Number.isInteger(r))throw new Error(`Value "${e}" is not a valid int`);return r}case"boolean":if("boolean"==typeof e)return e;if("true"===e)return!0;if("false"===e)return!1;throw new Error(`Value "${e}" is not a valid boolean`);case"object":if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`Value "${e}" is not a valid object`);return e;case"array":if(!Array.isArray(e))throw new Error(`Value "${e}" is not a valid array`);return e;default:return e}};exports.typeCheck=typeCheck;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.typeCheck=exports.fieldTypes=void 0;const fieldTypes=exports.fieldTypes=["string","int","boolean","object","array"],typeCheck=(e="string",r=void 0,t=!1)=>{if(void 0===r)return r;if(null===r){if(t)return r;throw new Error('Value "null" is not allowed: the field is not nullable')}switch(e){case"string":return"string"==typeof r?r:String(r);case"int":{const e=Number(r);if(Number.isNaN(e)||!Number.isInteger(e))throw new Error(`Value "${r}" is not a valid int`);return e}case"boolean":if("boolean"==typeof r)return r;if("true"===r)return!0;if("false"===r)return!1;throw new Error(`Value "${r}" is not a valid boolean`);case"object":if("object"!=typeof r||null===r||Array.isArray(r))throw new Error(`Value "${r}" is not a valid object`);return r;case"array":if(!Array.isArray(r))throw new Error(`Value "${r}" is not a valid array`);return r;default:return r}};exports.typeCheck=typeCheck;
@@ -0,0 +1,11 @@
1
+ import { recordDefinition } from '../introspect/definitions/record';
2
+ import { ParsedCreate } from '../utilities/parsers/parseCreate';
3
+ /**
4
+ * Run a parsed create statement: an error if the record already exists, unless the statement says
5
+ * `if not exists` (then nothing happens and the result is null). Otherwise create() it and resolve to its
6
+ * definition, as create() does.
7
+ * @param entity
8
+ * @param parsed
9
+ */
10
+ export declare const createRecord: (entity: string, parsed: ParsedCreate) => Promise<recordDefinition | null>;
11
+ //# sourceMappingURL=createRecord.d.ts.map
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createRecord = void 0;
7
+ require("core-js/modules/es.array.includes.js");
8
+ require("core-js/modules/esnext.iterator.constructor.js");
9
+ require("core-js/modules/esnext.iterator.map.js");
10
+ var _create = require("../introspect/create");
11
+ var _retrieveRecord = require("../utilities/retrieveRecord");
12
+ var _splitEntityProperty = require("../utilities/parsers/splitEntityProperty");
13
+ var _recordExists = require("../utilities/recordExists");
14
+ /**
15
+ * Reject a foreign key whose referenced record, or field on it, doesn't exist. A key referencing the record being
16
+ * created is checked against its own fields.
17
+ * @param entity
18
+ * @param parsed
19
+ */
20
+ const assertReferencesExist = async (entity, parsed) => {
21
+ for (const key of parsed.keys) {
22
+ if (key.type !== 'foreign') {
23
+ continue;
24
+ }
25
+ const {
26
+ entity: refEntity,
27
+ property: refProperty
28
+ } = (0, _splitEntityProperty.splitEntityProperty)(key.references[0].fields[0]);
29
+ let fields = parsed.definition.map(field => field.name);
30
+ if (refEntity !== entity) {
31
+ if (!(0, _recordExists.recordExists)(refEntity)) {
32
+ throw new Error(`Foreign key "${key.fields[0]}" references "${refEntity}", which doesn't exist`);
33
+ }
34
+ fields = (await (0, _retrieveRecord.retrieveRecord)(refEntity)).definition.map(field => field.name);
35
+ }
36
+ if (!fields.includes(refProperty)) {
37
+ throw new Error(`Foreign key "${key.fields[0]}" references "${refEntity}.${refProperty}", which is not a field`);
38
+ }
39
+ }
40
+ };
41
+ /**
42
+ * Run a parsed create statement: an error if the record already exists, unless the statement says
43
+ * `if not exists` (then nothing happens and the result is null). Otherwise create() it and resolve to its
44
+ * definition, as create() does.
45
+ * @param entity
46
+ * @param parsed
47
+ */
48
+ const createRecord = async (entity, parsed) => {
49
+ if ((0, _recordExists.recordExists)(entity)) {
50
+ if (parsed.ifNotExists) {
51
+ return null;
52
+ }
53
+ throw new Error(`Record "${entity}" already exists`);
54
+ }
55
+ await assertReferencesExist(entity, parsed);
56
+ return (0, _create.create)(entity, parsed.definition, parsed.keys);
57
+ };
58
+ exports.createRecord = createRecord;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.createRecord=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _create=require("../introspect/create"),_retrieveRecord=require("../utilities/retrieveRecord"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_recordExists=require("../utilities/recordExists");const assertReferencesExist=async(e,r)=>{for(const t of r.keys){if("foreign"!==t.type)continue;const{entity:i,property:s}=(0,_splitEntityProperty.splitEntityProperty)(t.references[0].fields[0]);let o=r.definition.map((e=>e.name));if(i!==e){if(!(0,_recordExists.recordExists)(i))throw new Error(`Foreign key "${t.fields[0]}" references "${i}", which doesn't exist`);o=(await(0,_retrieveRecord.retrieveRecord)(i)).definition.map((e=>e.name))}if(!o.includes(s))throw new Error(`Foreign key "${t.fields[0]}" references "${i}.${s}", which is not a field`)}},createRecord=async(e,r)=>{if((0,_recordExists.recordExists)(e)){if(r.ifNotExists)return null;throw new Error(`Record "${e}" already exists`)}return await assertReferencesExist(e,r),(0,_create.create)(e,r.definition,r.keys)};exports.createRecord=createRecord;