json-fs-query 0.1.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 (336) hide show
  1. package/.browserslistrc +1 -0
  2. package/.nvmrc +1 -0
  3. package/LICENSE +674 -0
  4. package/MAIN.md +15 -0
  5. package/README.md +63 -0
  6. package/babel.config.js +3 -0
  7. package/build-tools.config.json +30 -0
  8. package/dist/data/introspect/create.d.ts +11 -0
  9. package/dist/data/introspect/create.js +45 -0
  10. package/dist/data/introspect/create.min.js +1 -0
  11. package/dist/data/introspect/definitions/field.d.ts +28 -0
  12. package/dist/data/introspect/definitions/field.js +36 -0
  13. package/dist/data/introspect/definitions/field.min.js +1 -0
  14. package/dist/data/introspect/definitions/key.d.ts +26 -0
  15. package/dist/data/introspect/definitions/key.js +53 -0
  16. package/dist/data/introspect/definitions/key.min.js +1 -0
  17. package/dist/data/introspect/definitions/keyReference.d.ts +18 -0
  18. package/dist/data/introspect/definitions/keyReference.js +20 -0
  19. package/dist/data/introspect/definitions/keyReference.min.js +1 -0
  20. package/dist/data/introspect/definitions/record.d.ts +26 -0
  21. package/dist/data/introspect/definitions/record.js +32 -0
  22. package/dist/data/introspect/definitions/record.min.js +1 -0
  23. package/dist/data/introspect/describe.d.ts +2 -0
  24. package/dist/data/introspect/describe.js +15 -0
  25. package/dist/data/introspect/describe.min.js +1 -0
  26. package/dist/data/introspect/drop.d.ts +2 -0
  27. package/dist/data/introspect/drop.js +37 -0
  28. package/dist/data/introspect/drop.min.js +1 -0
  29. package/dist/data/introspect/index.d.ts +8 -0
  30. package/dist/data/introspect/index.js +30 -0
  31. package/dist/data/introspect/index.min.js +1 -0
  32. package/dist/data/introspect/policies/foreignKeyExists.d.ts +9 -0
  33. package/dist/data/introspect/policies/foreignKeyExists.js +25 -0
  34. package/dist/data/introspect/policies/foreignKeyExists.min.js +1 -0
  35. package/dist/data/introspect/policies/keyGenerate.d.ts +2 -0
  36. package/dist/data/introspect/policies/keyGenerate.js +17 -0
  37. package/dist/data/introspect/policies/keyGenerate.min.js +1 -0
  38. package/dist/data/introspect/policies/keyUnique.d.ts +2 -0
  39. package/dist/data/introspect/policies/keyUnique.js +21 -0
  40. package/dist/data/introspect/policies/keyUnique.min.js +1 -0
  41. package/dist/data/introspect/policies/typeCheck.d.ts +12 -0
  42. package/dist/data/introspect/policies/typeCheck.js +57 -0
  43. package/dist/data/introspect/policies/typeCheck.min.js +1 -0
  44. package/dist/data/introspect/policies/typeEnum.d.ts +2 -0
  45. package/dist/data/introspect/policies/typeEnum.js +14 -0
  46. package/dist/data/introspect/policies/typeEnum.min.js +1 -0
  47. package/dist/data/introspect/policies/useDefault.d.ts +2 -0
  48. package/dist/data/introspect/policies/useDefault.js +13 -0
  49. package/dist/data/introspect/policies/useDefault.min.js +1 -0
  50. package/dist/data/queries/deleteEntity.d.ts +2 -0
  51. package/dist/data/queries/deleteEntity.js +79 -0
  52. package/dist/data/queries/deleteEntity.min.js +1 -0
  53. package/dist/data/queries/groupBy.d.ts +2 -0
  54. package/dist/data/queries/groupBy.js +22 -0
  55. package/dist/data/queries/groupBy.min.js +1 -0
  56. package/dist/data/queries/insertEntity.d.ts +2 -0
  57. package/dist/data/queries/insertEntity.js +115 -0
  58. package/dist/data/queries/insertEntity.min.js +1 -0
  59. package/dist/data/queries/joinEntity.d.ts +2 -0
  60. package/dist/data/queries/joinEntity.js +27 -0
  61. package/dist/data/queries/joinEntity.min.js +1 -0
  62. package/dist/data/queries/mergeJoins.d.ts +2 -0
  63. package/dist/data/queries/mergeJoins.js +19 -0
  64. package/dist/data/queries/mergeJoins.min.js +1 -0
  65. package/dist/data/queries/parser.d.ts +2 -0
  66. package/dist/data/queries/parser.js +30 -0
  67. package/dist/data/queries/parser.min.js +1 -0
  68. package/dist/data/queries/query.d.ts +2 -0
  69. package/dist/data/queries/query.js +18 -0
  70. package/dist/data/queries/query.min.js +1 -0
  71. package/dist/data/queries/readEntity.d.ts +2 -0
  72. package/dist/data/queries/readEntity.js +14 -0
  73. package/dist/data/queries/readEntity.min.js +1 -0
  74. package/dist/data/queries/sortBy.d.ts +2 -0
  75. package/dist/data/queries/sortBy.js +25 -0
  76. package/dist/data/queries/sortBy.min.js +1 -0
  77. package/dist/data/queries/updateEntity.d.ts +2 -0
  78. package/dist/data/queries/updateEntity.js +164 -0
  79. package/dist/data/queries/updateEntity.min.js +1 -0
  80. package/dist/data/queries/where.d.ts +7 -0
  81. package/dist/data/queries/where.js +56 -0
  82. package/dist/data/queries/where.min.js +1 -0
  83. package/dist/data/utilities/deregisterRecord.d.ts +8 -0
  84. package/dist/data/utilities/deregisterRecord.js +34 -0
  85. package/dist/data/utilities/deregisterRecord.min.js +1 -0
  86. package/dist/data/utilities/getConditionIndexedList.d.ts +3 -0
  87. package/dist/data/utilities/getConditionIndexedList.js +47 -0
  88. package/dist/data/utilities/getConditionIndexedList.min.js +1 -0
  89. package/dist/data/utilities/getJoinIndexedList.d.ts +3 -0
  90. package/dist/data/utilities/getJoinIndexedList.js +59 -0
  91. package/dist/data/utilities/getJoinIndexedList.min.js +1 -0
  92. package/dist/data/utilities/getMatchingIndexes.d.ts +2 -0
  93. package/dist/data/utilities/getMatchingIndexes.js +28 -0
  94. package/dist/data/utilities/getMatchingIndexes.min.js +1 -0
  95. package/dist/data/utilities/matchIndexedJoins.d.ts +2 -0
  96. package/dist/data/utilities/matchIndexedJoins.js +61 -0
  97. package/dist/data/utilities/matchIndexedJoins.min.js +1 -0
  98. package/dist/data/utilities/mergeJoin.d.ts +2 -0
  99. package/dist/data/utilities/mergeJoin.js +44 -0
  100. package/dist/data/utilities/mergeJoin.min.js +1 -0
  101. package/dist/data/utilities/parsers/findCommand.d.ts +10 -0
  102. package/dist/data/utilities/parsers/findCommand.js +25 -0
  103. package/dist/data/utilities/parsers/findCommand.min.js +1 -0
  104. package/dist/data/utilities/parsers/findConditions.d.ts +15 -0
  105. package/dist/data/utilities/parsers/findConditions.js +28 -0
  106. package/dist/data/utilities/parsers/findConditions.min.js +1 -0
  107. package/dist/data/utilities/parsers/findGroupBy.d.ts +9 -0
  108. package/dist/data/utilities/parsers/findGroupBy.js +22 -0
  109. package/dist/data/utilities/parsers/findGroupBy.min.js +1 -0
  110. package/dist/data/utilities/parsers/findInsertValues.d.ts +13 -0
  111. package/dist/data/utilities/parsers/findInsertValues.js +34 -0
  112. package/dist/data/utilities/parsers/findInsertValues.min.js +1 -0
  113. package/dist/data/utilities/parsers/findJoinClauses.d.ts +13 -0
  114. package/dist/data/utilities/parsers/findJoinClauses.js +27 -0
  115. package/dist/data/utilities/parsers/findJoinClauses.min.js +1 -0
  116. package/dist/data/utilities/parsers/findJoinEntity.d.ts +9 -0
  117. package/dist/data/utilities/parsers/findJoinEntity.js +22 -0
  118. package/dist/data/utilities/parsers/findJoinEntity.min.js +1 -0
  119. package/dist/data/utilities/parsers/findLimit.d.ts +9 -0
  120. package/dist/data/utilities/parsers/findLimit.js +22 -0
  121. package/dist/data/utilities/parsers/findLimit.min.js +1 -0
  122. package/dist/data/utilities/parsers/findMergeJoins.d.ts +12 -0
  123. package/dist/data/utilities/parsers/findMergeJoins.js +26 -0
  124. package/dist/data/utilities/parsers/findMergeJoins.min.js +1 -0
  125. package/dist/data/utilities/parsers/findOffset.d.ts +9 -0
  126. package/dist/data/utilities/parsers/findOffset.js +22 -0
  127. package/dist/data/utilities/parsers/findOffset.min.js +1 -0
  128. package/dist/data/utilities/parsers/findSelects.d.ts +12 -0
  129. package/dist/data/utilities/parsers/findSelects.js +30 -0
  130. package/dist/data/utilities/parsers/findSelects.min.js +1 -0
  131. package/dist/data/utilities/parsers/findSortClauses.d.ts +12 -0
  132. package/dist/data/utilities/parsers/findSortClauses.js +30 -0
  133. package/dist/data/utilities/parsers/findSortClauses.min.js +1 -0
  134. package/dist/data/utilities/parsers/findUpdateValues.d.ts +15 -0
  135. package/dist/data/utilities/parsers/findUpdateValues.js +34 -0
  136. package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -0
  137. package/dist/data/utilities/parsers/isEntityProperty.d.ts +2 -0
  138. package/dist/data/utilities/parsers/isEntityProperty.js +8 -0
  139. package/dist/data/utilities/parsers/isEntityProperty.min.js +1 -0
  140. package/dist/data/utilities/parsers/parseLiteralValue.d.ts +12 -0
  141. package/dist/data/utilities/parsers/parseLiteralValue.js +34 -0
  142. package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -0
  143. package/dist/data/utilities/parsers/parseQuery.d.ts +33 -0
  144. package/dist/data/utilities/parsers/parseQuery.js +51 -0
  145. package/dist/data/utilities/parsers/parseQuery.min.js +1 -0
  146. package/dist/data/utilities/parsers/removeFromQuery.d.ts +2 -0
  147. package/dist/data/utilities/parsers/removeFromQuery.js +8 -0
  148. package/dist/data/utilities/parsers/removeFromQuery.min.js +1 -0
  149. package/dist/data/utilities/parsers/splitEntityProperty.d.ts +5 -0
  150. package/dist/data/utilities/parsers/splitEntityProperty.js +23 -0
  151. package/dist/data/utilities/parsers/splitEntityProperty.min.js +1 -0
  152. package/dist/data/utilities/parsers/splitQueries.d.ts +2 -0
  153. package/dist/data/utilities/parsers/splitQueries.js +11 -0
  154. package/dist/data/utilities/parsers/splitQueries.min.js +1 -0
  155. package/dist/data/utilities/reconcileJoin.d.ts +2 -0
  156. package/dist/data/utilities/reconcileJoin.js +26 -0
  157. package/dist/data/utilities/reconcileJoin.min.js +1 -0
  158. package/dist/data/utilities/reconcileJoins.d.ts +2 -0
  159. package/dist/data/utilities/reconcileJoins.js +27 -0
  160. package/dist/data/utilities/reconcileJoins.min.js +1 -0
  161. package/dist/data/utilities/registerRecord.d.ts +8 -0
  162. package/dist/data/utilities/registerRecord.js +67 -0
  163. package/dist/data/utilities/registerRecord.min.js +1 -0
  164. package/dist/data/utilities/removeFromIndex.d.ts +14 -0
  165. package/dist/data/utilities/removeFromIndex.js +46 -0
  166. package/dist/data/utilities/removeFromIndex.min.js +1 -0
  167. package/dist/data/utilities/restrictIndexedList.d.ts +2 -0
  168. package/dist/data/utilities/restrictIndexedList.js +45 -0
  169. package/dist/data/utilities/restrictIndexedList.min.js +1 -0
  170. package/dist/data/utilities/retrieveFile.d.ts +2 -0
  171. package/dist/data/utilities/retrieveFile.js +28 -0
  172. package/dist/data/utilities/retrieveFile.min.js +1 -0
  173. package/dist/data/utilities/retrieveIndexes.d.ts +2 -0
  174. package/dist/data/utilities/retrieveIndexes.js +24 -0
  175. package/dist/data/utilities/retrieveIndexes.min.js +1 -0
  176. package/dist/data/utilities/retrieveRecord.d.ts +2 -0
  177. package/dist/data/utilities/retrieveRecord.js +9 -0
  178. package/dist/data/utilities/retrieveRecord.min.js +1 -0
  179. package/dist/data/utilities/retrieveRecords.d.ts +2 -0
  180. package/dist/data/utilities/retrieveRecords.js +21 -0
  181. package/dist/data/utilities/retrieveRecords.min.js +1 -0
  182. package/dist/data/utilities/runQuery.d.ts +2 -0
  183. package/dist/data/utilities/runQuery.js +72 -0
  184. package/dist/data/utilities/runQuery.min.js +1 -0
  185. package/dist/data/utilities/updateIndex.d.ts +17 -0
  186. package/dist/data/utilities/updateIndex.js +56 -0
  187. package/dist/data/utilities/updateIndex.min.js +1 -0
  188. package/dist/data/utilities/useJoinClause.d.ts +6 -0
  189. package/dist/data/utilities/useJoinClause.js +51 -0
  190. package/dist/data/utilities/useJoinClause.min.js +1 -0
  191. package/dist/index.d.ts +4 -0
  192. package/dist/index.js +35 -0
  193. package/dist/index.min.js +1 -0
  194. package/gulpfile.mjs +15 -0
  195. package/package.json +44 -0
  196. package/src/data/introspect/create.test.js +67 -0
  197. package/src/data/introspect/create.ts +29 -0
  198. package/src/data/introspect/definitions/field.test.js +23 -0
  199. package/src/data/introspect/definitions/field.ts +48 -0
  200. package/src/data/introspect/definitions/key.test.js +100 -0
  201. package/src/data/introspect/definitions/key.ts +64 -0
  202. package/src/data/introspect/definitions/keyReference.test.js +8 -0
  203. package/src/data/introspect/definitions/keyReference.ts +23 -0
  204. package/src/data/introspect/definitions/record.ts +42 -0
  205. package/src/data/introspect/describe.test.js +36 -0
  206. package/src/data/introspect/describe.ts +9 -0
  207. package/src/data/introspect/drop.test.js +77 -0
  208. package/src/data/introspect/drop.ts +20 -0
  209. package/src/data/introspect/index.ts +8 -0
  210. package/src/data/introspect/policies/foreignKeyExists.test.js +44 -0
  211. package/src/data/introspect/policies/foreignKeyExists.ts +15 -0
  212. package/src/data/introspect/policies/keyGenerate.ts +11 -0
  213. package/src/data/introspect/policies/keyUnique.ts +11 -0
  214. package/src/data/introspect/policies/typeCheck.test.js +84 -0
  215. package/src/data/introspect/policies/typeCheck.ts +49 -0
  216. package/src/data/introspect/policies/typeEnum.ts +6 -0
  217. package/src/data/introspect/policies/useDefault.ts +6 -0
  218. package/src/data/queries/deleteEntity.test.js +86 -0
  219. package/src/data/queries/deleteEntity.ts +56 -0
  220. package/src/data/queries/groupBy.test.js +135 -0
  221. package/src/data/queries/groupBy.ts +16 -0
  222. package/src/data/queries/insertEntity.test.js +156 -0
  223. package/src/data/queries/insertEntity.ts +113 -0
  224. package/src/data/queries/joinEntity.test.js +109 -0
  225. package/src/data/queries/joinEntity.ts +20 -0
  226. package/src/data/queries/mergeJoins.test.js +174 -0
  227. package/src/data/queries/mergeJoins.ts +13 -0
  228. package/src/data/queries/parser.test.js +343 -0
  229. package/src/data/queries/parser.ts +24 -0
  230. package/src/data/queries/query.test.js +233 -0
  231. package/src/data/queries/query.ts +12 -0
  232. package/src/data/queries/readEntity.test.js +85 -0
  233. package/src/data/queries/readEntity.ts +8 -0
  234. package/src/data/queries/sortBy.test.js +131 -0
  235. package/src/data/queries/sortBy.ts +18 -0
  236. package/src/data/queries/updateEntity.test.js +228 -0
  237. package/src/data/queries/updateEntity.ts +162 -0
  238. package/src/data/queries/where.test.js +129 -0
  239. package/src/data/queries/where.ts +54 -0
  240. package/src/data/utilities/deregisterRecord.test.js +26 -0
  241. package/src/data/utilities/deregisterRecord.ts +24 -0
  242. package/src/data/utilities/getConditionIndexedList.ts +43 -0
  243. package/src/data/utilities/getJoinIndexedList.ts +53 -0
  244. package/src/data/utilities/getMatchingIndexes.ts +21 -0
  245. package/src/data/utilities/matchIndexedJoins.ts +45 -0
  246. package/src/data/utilities/mergeJoin.test.js +102 -0
  247. package/src/data/utilities/mergeJoin.ts +28 -0
  248. package/src/data/utilities/parsers/findCommand.ts +25 -0
  249. package/src/data/utilities/parsers/findConditions.test.js +38 -0
  250. package/src/data/utilities/parsers/findConditions.ts +32 -0
  251. package/src/data/utilities/parsers/findGroupBy.ts +20 -0
  252. package/src/data/utilities/parsers/findInsertValues.test.js +26 -0
  253. package/src/data/utilities/parsers/findInsertValues.ts +32 -0
  254. package/src/data/utilities/parsers/findJoinClauses.ts +29 -0
  255. package/src/data/utilities/parsers/findJoinEntity.ts +20 -0
  256. package/src/data/utilities/parsers/findLimit.ts +20 -0
  257. package/src/data/utilities/parsers/findMergeJoins.ts +27 -0
  258. package/src/data/utilities/parsers/findOffset.ts +20 -0
  259. package/src/data/utilities/parsers/findSelects.ts +31 -0
  260. package/src/data/utilities/parsers/findSortClauses.ts +31 -0
  261. package/src/data/utilities/parsers/findUpdateValues.test.js +37 -0
  262. package/src/data/utilities/parsers/findUpdateValues.ts +32 -0
  263. package/src/data/utilities/parsers/isEntityProperty.ts +1 -0
  264. package/src/data/utilities/parsers/parseLiteralValue.test.js +41 -0
  265. package/src/data/utilities/parsers/parseLiteralValue.ts +29 -0
  266. package/src/data/utilities/parsers/parseQuery.ts +73 -0
  267. package/src/data/utilities/parsers/removeFromQuery.ts +1 -0
  268. package/src/data/utilities/parsers/splitEntityProperty.test.js +8 -0
  269. package/src/data/utilities/parsers/splitEntityProperty.ts +17 -0
  270. package/src/data/utilities/parsers/splitQueries.test.js +33 -0
  271. package/src/data/utilities/parsers/splitQueries.ts +4 -0
  272. package/src/data/utilities/reconcileJoin.test.js +38 -0
  273. package/src/data/utilities/reconcileJoin.ts +23 -0
  274. package/src/data/utilities/reconcileJoins.test.js +160 -0
  275. package/src/data/utilities/reconcileJoins.ts +15 -0
  276. package/src/data/utilities/registerRecord.test.js +44 -0
  277. package/src/data/utilities/registerRecord.ts +44 -0
  278. package/src/data/utilities/removeFromIndex.test.js +44 -0
  279. package/src/data/utilities/removeFromIndex.ts +34 -0
  280. package/src/data/utilities/restrictIndexedList.ts +38 -0
  281. package/src/data/utilities/retrieveFile.test.js +49 -0
  282. package/src/data/utilities/retrieveFile.ts +19 -0
  283. package/src/data/utilities/retrieveIndexes.ts +17 -0
  284. package/src/data/utilities/retrieveRecord.ts +3 -0
  285. package/src/data/utilities/retrieveRecords.test.js +223 -0
  286. package/src/data/utilities/retrieveRecords.ts +16 -0
  287. package/src/data/utilities/runQuery.test.js +88 -0
  288. package/src/data/utilities/runQuery.ts +75 -0
  289. package/src/data/utilities/updateIndex.test.js +44 -0
  290. package/src/data/utilities/updateIndex.ts +45 -0
  291. package/src/data/utilities/useJoinClause.test.js +94 -0
  292. package/src/data/utilities/useJoinClause.ts +44 -0
  293. package/src/global.d.ts +1 -0
  294. package/src/index.test.js +52 -0
  295. package/src/index.ts +17 -0
  296. package/test-data/__RECORDS.json +32 -0
  297. package/test-data/__indexes/exercise_muscle/fk_exercise_id.json +14 -0
  298. package/test-data/__indexes/exercise_muscle/fk_muscle_id.json +14 -0
  299. package/test-data/__indexes/exercise_muscle/pk__id.json +14 -0
  300. package/test-data/__indexes/exercises/pk__id.json +14 -0
  301. package/test-data/__indexes/muscles/pk__id.json +58 -0
  302. package/test-data/__indexes/workouts/fk_exercise_id.json +23 -0
  303. package/test-data/__indexes/workouts/index_date.json +18 -0
  304. package/test-data/__indexes/workouts/pk__id.json +26 -0
  305. package/test-data/copyDatabase.ts +64 -0
  306. package/test-data/exercise_muscle/bench_press_pectorals.json +6 -0
  307. package/test-data/exercise_muscle/deadlift_hamstrings.json +6 -0
  308. package/test-data/exercise_muscle/squat_quadriceps.json +6 -0
  309. package/test-data/exercise_muscle.json +69 -0
  310. package/test-data/exercises/bench_press.json +5 -0
  311. package/test-data/exercises/deadlift.json +5 -0
  312. package/test-data/exercises/squat.json +5 -0
  313. package/test-data/exercises.json +34 -0
  314. package/test-data/muscles/abdominal.json +6 -0
  315. package/test-data/muscles/biceps.json +6 -0
  316. package/test-data/muscles/calves.json +6 -0
  317. package/test-data/muscles/deltoids.json +6 -0
  318. package/test-data/muscles/erector_spinae.json +6 -0
  319. package/test-data/muscles/forearms.json +6 -0
  320. package/test-data/muscles/gluteus_maximus.json +6 -0
  321. package/test-data/muscles/hamstrings.json +6 -0
  322. package/test-data/muscles/latissimus_dorsi.json +6 -0
  323. package/test-data/muscles/obliques.json +6 -0
  324. package/test-data/muscles/pectorals.json +6 -0
  325. package/test-data/muscles/quadriceps.json +6 -0
  326. package/test-data/muscles/trapezius.json +6 -0
  327. package/test-data/muscles/triceps.json +6 -0
  328. package/test-data/muscles.json +50 -0
  329. package/test-data/workouts/2024-06-02-001.json +10 -0
  330. package/test-data/workouts/2024-06-02-002.json +10 -0
  331. package/test-data/workouts/2024-06-02-003.json +10 -0
  332. package/test-data/workouts/2024-06-05-001.json +10 -0
  333. package/test-data/workouts/2024-06-05-002.json +10 -0
  334. package/test-data/workouts/2024-06-05-003.json +10 -0
  335. package/test-data/workouts.json +84 -0
  336. package/tsconfig.json +15 -0
@@ -0,0 +1,77 @@
1
+ import { create } from './create'
2
+ import { drop } from './drop'
3
+ import { insertEntity } from '../queries/insertEntity'
4
+ import { testHelpers } from 'js-build-tools/testHelpers'
5
+
6
+ const { writeFile, readFile } = require('fs/promises')
7
+
8
+ const databasePath = 'drop-database/'
9
+ const gulpConfig = testHelpers.gulpConfig
10
+ gulpConfig.set('databasePath', databasePath)
11
+ testHelpers.setDefaults(databasePath)
12
+
13
+ beforeEach(testHelpers.beforeEach)
14
+
15
+ afterEach(testHelpers.afterEach)
16
+
17
+ describe('drop', () => {
18
+ test('returns null for the reserved __RECORDS name, without touching anything', async () => {
19
+ expect(await drop('__RECORDS')).toBeNull()
20
+ })
21
+
22
+ test('removes every entry file listed on the record', async () => {
23
+ const recordName = 'foo'
24
+ await create(recordName, [], [])
25
+ // insertEntity is not implemented yet, so simulate a real entry directly: write the data file, and record it
26
+ // on the record's own entries list, exactly as a working insertEntity would.
27
+ const entryFile = 'entry1.json'
28
+ await writeFile(`${databasePath}${recordName}/${entryFile}`, JSON.stringify({ _id: 1 }))
29
+ const recordContent = JSON.parse(await readFile(`${databasePath}${recordName}.json`, 'utf8'))
30
+ recordContent.entries = [entryFile]
31
+ await writeFile(`${databasePath}${recordName}.json`, JSON.stringify(recordContent))
32
+
33
+ expect(testHelpers.fileExists(`${databasePath}${recordName}/${entryFile}`)).toBeTruthy()
34
+ await drop(recordName)
35
+ expect(testHelpers.fileExists(`${databasePath}${recordName}/${entryFile}`)).toBeFalsy()
36
+ })
37
+
38
+ test('deregisters the record from __RECORDS', async () => {
39
+ await create('foo', [], [])
40
+ await create('bar', [], [])
41
+ await drop('foo')
42
+ const registry = JSON.parse(await readFile(`${databasePath}__RECORDS.json`, 'utf8'))
43
+ expect(registry.entries).toEqual(['bar.json'])
44
+ })
45
+
46
+ test('removes the record\'s own definition file and data directory', async () => {
47
+ await create('foo', [], [])
48
+ expect(testHelpers.fileExists(`${databasePath}foo.json`)).toBeTruthy()
49
+ expect(testHelpers.fileExists(`${databasePath}foo`)).toBeTruthy()
50
+
51
+ await drop('foo')
52
+
53
+ expect(testHelpers.fileExists(`${databasePath}foo.json`)).toBeFalsy()
54
+ expect(testHelpers.fileExists(`${databasePath}foo`)).toBeFalsy()
55
+ })
56
+
57
+ test('removes the record\'s own index directory', async () => {
58
+ const definition = [
59
+ { name: '_id', type: 'string', optional: false, autoGenerate: true }
60
+ ]
61
+ const keys = [
62
+ { type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
63
+ ]
64
+ await create('foo', definition, keys)
65
+ await insertEntity('foo', [{}], {})
66
+ expect(testHelpers.fileExists(`${databasePath}__indexes/foo/pk__id.json`)).toBeTruthy()
67
+
68
+ await drop('foo')
69
+
70
+ expect(testHelpers.fileExists(`${databasePath}__indexes/foo`)).toBeFalsy()
71
+ })
72
+
73
+ test('does not throw when the record has no index directory to remove', async () => {
74
+ await create('foo', [], [])
75
+ await expect(drop('foo')).resolves.not.toThrow()
76
+ })
77
+ })
@@ -0,0 +1,20 @@
1
+ import { retrieveFile } from '../utilities/retrieveFile'
2
+ import { deregisterRecord } from '../utilities/deregisterRecord'
3
+
4
+ const gulpConfig = require('js-build-tools/gulp.config')
5
+ const { rm } = require('fs/promises')
6
+
7
+ export const drop = async (record) => {
8
+ const databasePath = gulpConfig.get('databasePath', 'database/')
9
+ if (record === '__RECORDS') {
10
+ return null
11
+ }
12
+ const recordFile = await retrieveFile(`${record}.json`)
13
+ for (const file of recordFile.entries) {
14
+ await rm(`${databasePath}${recordFile.path}/${file}`)
15
+ }
16
+ await rm(`${databasePath}${recordFile.path}`, { recursive: true, force: true })
17
+ await rm(`${databasePath}__indexes/${recordFile.path}`, { recursive: true, force: true })
18
+ await rm(`${databasePath}${record}.json`)
19
+ await deregisterRecord(record)
20
+ }
@@ -0,0 +1,8 @@
1
+ export { create } from './create'
2
+ export { drop } from './drop'
3
+ export { describe } from './describe'
4
+
5
+ export { fieldProperties, fieldDefinition, fieldName } from './definitions/field'
6
+ export { keyProperties, keyDefinition, indexType, indexLocation } from './definitions/key'
7
+ export { referenceProperties, reference } from './definitions/keyReference'
8
+ export { recordProperties, recordDefinition, recordPath, entityPath } from './definitions/record'
@@ -0,0 +1,44 @@
1
+ import { create } from '../create'
2
+ import { insertEntity } from '../../queries/insertEntity'
3
+ import { foreignKeyExists } from './foreignKeyExists'
4
+ import { testHelpers } from 'js-build-tools/testHelpers'
5
+
6
+ const databasePath = 'foreign-key-exists-database/'
7
+ const gulpConfig = testHelpers.gulpConfig
8
+ gulpConfig.set('databasePath', databasePath)
9
+ testHelpers.setDefaults(databasePath)
10
+
11
+ beforeEach(testHelpers.beforeEach)
12
+
13
+ afterEach(testHelpers.afterEach)
14
+
15
+ const definition = [
16
+ { name: '_id', type: 'string', optional: false, autoGenerate: true },
17
+ { name: 'name', type: 'string', optional: true, autoGenerate: false }
18
+ ]
19
+
20
+ const keys = [
21
+ { type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
22
+ ]
23
+
24
+ describe('foreignKeyExists', () => {
25
+ test('returns true when a record has the given property set to the given value', async () => {
26
+ await create('exercises', definition, keys)
27
+ const inserted = await insertEntity('exercises', [{ name: 'Bench Press' }], {})
28
+ const exists = await foreignKeyExists('exercises', '_id', inserted.exercises[0]._id)
29
+ expect(exists).toBe(true)
30
+ })
31
+
32
+ test('returns false when no record matches', async () => {
33
+ await create('exercises', definition, keys)
34
+ await insertEntity('exercises', [{ name: 'Bench Press' }], {})
35
+ const exists = await foreignKeyExists('exercises', '_id', 'does-not-exist')
36
+ expect(exists).toBe(false)
37
+ })
38
+
39
+ test('returns false for an entity with no entries', async () => {
40
+ await create('exercises', definition, keys)
41
+ const exists = await foreignKeyExists('exercises', '_id', 'anything')
42
+ expect(exists).toBe(false)
43
+ })
44
+ })
@@ -0,0 +1,15 @@
1
+ import { retrieveRecords } from '../../utilities/retrieveRecords'
2
+ import { where } from '../../queries/where'
3
+
4
+ /**
5
+ * Check whether any record in the referenced entity has the given property set to the given
6
+ * value - the mirror image of keyUnique(), which checks that no record already has a value.
7
+ * @param entity
8
+ * @param property
9
+ * @param value
10
+ */
11
+ export const foreignKeyExists = async (entity: string, property: string, value: any): Promise<boolean> => {
12
+ const records = await retrieveRecords(entity)
13
+ const found = where(records, { property, comparator: '=', value })
14
+ return found.length > 0
15
+ }
@@ -0,0 +1,11 @@
1
+ import { keyUnique } from './keyUnique'
2
+
3
+ export const keyGenerate = async (entity, references = []) => {
4
+ let uuid = null
5
+ let isUnique = false
6
+ while (!isUnique) {
7
+ uuid = crypto.randomUUID()
8
+ isUnique = await keyUnique(entity, references, uuid)
9
+ }
10
+ return uuid
11
+ }
@@ -0,0 +1,11 @@
1
+ import { retrieveRecords } from '../../utilities/retrieveRecords'
2
+ import { where } from '../../queries/where'
3
+
4
+ export const keyUnique = async (entity, references = [], checkKey = null) => {
5
+ if (references.length !== 1) {
6
+ return false
7
+ }
8
+ const records = await retrieveRecords(entity)
9
+ const found = where(records, { property: references[0], comparator: '=', value: checkKey })
10
+ return !found.length
11
+ }
@@ -0,0 +1,84 @@
1
+ import { typeCheck } from './typeCheck'
2
+
3
+ describe('typeCheck', () => {
4
+ test('passes an undefined value through unchanged, for optional fields', () => {
5
+ expect(typeCheck('string', undefined)).toBeUndefined()
6
+ })
7
+
8
+ test('does not constrain an unrecognized type name', () => {
9
+ expect(typeCheck('whatever', 42)).toBe(42)
10
+ })
11
+
12
+ describe('string', () => {
13
+ test('leaves an existing string alone', () => {
14
+ expect(typeCheck('string', 'foo')).toBe('foo')
15
+ })
16
+
17
+ test('coerces a non-string to a string', () => {
18
+ expect(typeCheck('string', 42)).toBe('42')
19
+ })
20
+ })
21
+
22
+ describe('int', () => {
23
+ test('leaves an existing integer alone', () => {
24
+ expect(typeCheck('int', 42)).toBe(42)
25
+ })
26
+
27
+ test('coerces a numeric string to a number', () => {
28
+ expect(typeCheck('int', '42')).toBe(42)
29
+ })
30
+
31
+ test('rejects a non-integer number', () => {
32
+ expect(() => typeCheck('int', 4.2)).toThrow('Value "4.2" is not a valid int')
33
+ })
34
+
35
+ test('rejects a value that cannot be coerced to a number', () => {
36
+ expect(() => typeCheck('int', 'not a number')).toThrow('Value "not a number" is not a valid int')
37
+ })
38
+ })
39
+
40
+ describe('boolean', () => {
41
+ test('leaves an existing boolean alone', () => {
42
+ expect(typeCheck('boolean', false)).toBe(false)
43
+ })
44
+
45
+ test('coerces the strings "true"/"false" to real booleans', () => {
46
+ expect(typeCheck('boolean', 'true')).toBe(true)
47
+ expect(typeCheck('boolean', 'false')).toBe(false)
48
+ })
49
+
50
+ test('rejects anything else', () => {
51
+ expect(() => typeCheck('boolean', 'yes')).toThrow('Value "yes" is not a valid boolean')
52
+ })
53
+ })
54
+
55
+ describe('object', () => {
56
+ test('accepts a plain object', () => {
57
+ const value = { a: 1 }
58
+ expect(typeCheck('object', value)).toBe(value)
59
+ })
60
+
61
+ test('rejects null', () => {
62
+ expect(() => typeCheck('object', null)).toThrow('Value "null" is not a valid object')
63
+ })
64
+
65
+ test('rejects an array', () => {
66
+ expect(() => typeCheck('object', [])).toThrow('is not a valid object')
67
+ })
68
+
69
+ test('rejects a primitive', () => {
70
+ expect(() => typeCheck('object', 'foo')).toThrow('is not a valid object')
71
+ })
72
+ })
73
+
74
+ describe('array', () => {
75
+ test('accepts an array', () => {
76
+ const value = [1, 2, 3]
77
+ expect(typeCheck('array', value)).toBe(value)
78
+ })
79
+
80
+ test('rejects a non-array', () => {
81
+ expect(() => typeCheck('array', { a: 1 })).toThrow('is not a valid array')
82
+ })
83
+ })
84
+ })
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Validate (and where sensible, coerce) a value against a field's declared type. Runs only
3
+ * against values being written by insertEntity/updateEntity - it never re-validates data already
4
+ * on disk, so a field whose declared type doesn't match its already-stored values (which can
5
+ * happen, since nothing enforced this before) is left alone until something actually updates it.
6
+ * An unrecognized type name is treated as unconstrained rather than rejected, so a typo in a
7
+ * schema's field type can't silently block every write to that field.
8
+ * @param type
9
+ * @param value
10
+ */
11
+ export const typeCheck = (type: string = 'string', value: any = undefined): any => {
12
+ if (typeof value === 'undefined') {
13
+ return value
14
+ }
15
+ switch (type) {
16
+ case 'string':
17
+ return typeof value === 'string' ? value : String(value)
18
+ case 'int': {
19
+ const coerced = Number(value)
20
+ if (Number.isNaN(coerced) || !Number.isInteger(coerced)) {
21
+ throw new Error(`Value "${value}" is not a valid int`)
22
+ }
23
+ return coerced
24
+ }
25
+ case 'boolean':
26
+ if (typeof value === 'boolean') {
27
+ return value
28
+ }
29
+ if (value === 'true') {
30
+ return true
31
+ }
32
+ if (value === 'false') {
33
+ return false
34
+ }
35
+ throw new Error(`Value "${value}" is not a valid boolean`)
36
+ case 'object':
37
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
38
+ throw new Error(`Value "${value}" is not a valid object`)
39
+ }
40
+ return value
41
+ case 'array':
42
+ if (!Array.isArray(value)) {
43
+ throw new Error(`Value "${value}" is not a valid array`)
44
+ }
45
+ return value
46
+ default:
47
+ return value
48
+ }
49
+ }
@@ -0,0 +1,6 @@
1
+ export const typeEnum = (values = [], value = '', optional = false) => {
2
+ if (values.includes(value)) {
3
+ return true
4
+ }
5
+ return typeof value === 'undefined' && optional
6
+ }
@@ -0,0 +1,6 @@
1
+ export const useDefault = (defaultValue = '', value = undefined) => {
2
+ if (typeof value === 'undefined') {
3
+ return defaultValue
4
+ }
5
+ return value
6
+ }
@@ -0,0 +1,86 @@
1
+ import { create } from '../introspect/create'
2
+ import { insertEntity } from './insertEntity'
3
+ import { deleteEntity } from './deleteEntity'
4
+ import { testHelpers } from 'js-build-tools/testHelpers'
5
+
6
+ const { readFile } = require('fs/promises')
7
+
8
+ const databasePath = 'delete-entity-database/'
9
+ const gulpConfig = testHelpers.gulpConfig
10
+ gulpConfig.set('databasePath', databasePath)
11
+ testHelpers.setDefaults(databasePath)
12
+
13
+ beforeEach(testHelpers.beforeEach)
14
+
15
+ afterEach(testHelpers.afterEach)
16
+
17
+ const definition = [
18
+ { name: '_id', type: 'string', optional: false, autoGenerate: true },
19
+ { name: 'bar', type: 'string', optional: true, autoGenerate: false }
20
+ ]
21
+
22
+ const keys = [
23
+ { type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
24
+ ]
25
+
26
+ describe('deleteEntity', () => {
27
+ test('does nothing when dataSet has no rows to delete', async () => {
28
+ await create('foo', definition, keys)
29
+ const inserted = await insertEntity('foo', [{ bar: 'keep' }], {})
30
+ const result = await deleteEntity('foo', { foo: [] })
31
+ expect(result).toEqual({ foo: [] })
32
+ const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
33
+ expect(recordFile.entries).toEqual([`${inserted.foo[0]._id}.json`])
34
+ })
35
+
36
+ test('throws when the record has no single-field primary key', async () => {
37
+ await create('foo', definition, [])
38
+ await expect(deleteEntity('foo', { foo: [{ _id: '1' }] }))
39
+ .rejects.toThrow('Cannot delete from "foo" without a single-field primary key')
40
+ })
41
+
42
+ test('removes the matching entry file, entries[] entry, and index entry', async () => {
43
+ await create('foo', definition, keys)
44
+ const inserted = await insertEntity('foo', [{ bar: 'one' }, { bar: 'two' }], {})
45
+ const [keep, remove] = inserted.foo
46
+
47
+ await deleteEntity('foo', { foo: [remove] })
48
+
49
+ expect(testHelpers.fileExists(`${databasePath}foo/${remove._id}.json`)).toBeFalsy()
50
+ expect(testHelpers.fileExists(`${databasePath}foo/${keep._id}.json`)).toBeTruthy()
51
+
52
+ const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
53
+ expect(recordFile.entries).toEqual([`${keep._id}.json`])
54
+
55
+ const indexFile = JSON.parse(await readFile(`${databasePath}__indexes/foo/pk__id.json`, 'utf8'))
56
+ expect(indexFile).toEqual([{ value: keep._id, record: [`${keep._id}.json`] }])
57
+ })
58
+
59
+ test('finds the right file to delete even when a filename does not match the primary key', async () => {
60
+ await create('foo', definition, keys)
61
+ const inserted = await insertEntity('foo', [{ bar: 'one' }], {})
62
+ const entryFile = `${inserted.foo[0]._id}.json`
63
+ const { rename } = require('fs/promises')
64
+ await rename(`${databasePath}foo/${entryFile}`, `${databasePath}foo/custom-name.json`)
65
+ const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
66
+ recordFile.entries = ['custom-name.json']
67
+ const { writeFile } = require('fs/promises')
68
+ await writeFile(`${databasePath}foo.json`, JSON.stringify(recordFile))
69
+
70
+ await deleteEntity('foo', { foo: [inserted.foo[0]] })
71
+
72
+ expect(testHelpers.fileExists(`${databasePath}foo/custom-name.json`)).toBeFalsy()
73
+ const finalRecordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
74
+ expect(finalRecordFile.entries).toEqual([])
75
+ })
76
+
77
+ test('deletes multiple rows from one call', async () => {
78
+ await create('foo', definition, keys)
79
+ const inserted = await insertEntity('foo', [{ bar: 'one' }, { bar: 'two' }, { bar: 'three' }], {})
80
+
81
+ await deleteEntity('foo', { foo: [inserted.foo[0], inserted.foo[2]] })
82
+
83
+ const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
84
+ expect(recordFile.entries).toEqual([`${inserted.foo[1]._id}.json`])
85
+ })
86
+ })
@@ -0,0 +1,56 @@
1
+ import { retrieveRecord } from '../utilities/retrieveRecord'
2
+ import { retrieveFile } from '../utilities/retrieveFile'
3
+ import { removeFromIndex } from '../utilities/removeFromIndex'
4
+ import { recordDefinition } from '../introspect/definitions/record'
5
+
6
+ const gulpConfig = require('js-build-tools/gulp.config')
7
+ const { rm, writeFile } = require('fs/promises')
8
+
9
+ /**
10
+ * A record object carries no memory of which entry file it was read from, and files are not
11
+ * guaranteed to be named after any field's value, so the only reliable way to find a record's
12
+ * file is to re-scan every entry and match it by its primary key value.
13
+ * @param entity
14
+ * @param record
15
+ * @param primaryField
16
+ * @param deletedValues
17
+ */
18
+ const removeMatchingEntries = async (entity: string, record: recordDefinition, primaryField: string, deletedValues: Set<any>): Promise<string[]> => {
19
+ const databasePath = gulpConfig.get('databasePath', 'database/')
20
+ const remainingEntries = []
21
+ for (const fileName of record.entries) {
22
+ const data = await retrieveFile(`${record.path}/${fileName}`)
23
+ if (!deletedValues.has(data[primaryField])) {
24
+ remainingEntries.push(fileName)
25
+ continue
26
+ }
27
+ await rm(`${databasePath}${record.path}/${fileName}`)
28
+ for (const key of record.keys) {
29
+ if (key.fields.length === 1) {
30
+ await removeFromIndex(record.path, key, data[key.fields[0]], fileName)
31
+ }
32
+ }
33
+ }
34
+ return remainingEntries
35
+ }
36
+
37
+ export const deleteEntity = async (entity: string = '', dataSet: Object = {}): Promise<Object> => {
38
+ const databasePath = gulpConfig.get('databasePath', 'database/')
39
+ const record = await retrieveRecord(entity)
40
+ const primaryKey = record.keys.find(key => key.type === 'primary' && key.fields.length === 1)
41
+ if (!primaryKey) {
42
+ throw new Error(`Cannot delete from "${entity}" without a single-field primary key`)
43
+ }
44
+ const primaryField = primaryKey.fields[0]
45
+
46
+ const toDelete = dataSet[entity] || []
47
+ if (!toDelete.length) {
48
+ return dataSet
49
+ }
50
+ const deletedValues = new Set(toDelete.map(row => row[primaryField]))
51
+
52
+ record.entries = await removeMatchingEntries(entity, record, primaryField, deletedValues)
53
+ await writeFile(`${databasePath}${record.path}.json`, JSON.stringify(record, null, 2))
54
+
55
+ return dataSet
56
+ }
@@ -0,0 +1,135 @@
1
+ import { groupBy } from './groupBy'
2
+
3
+ describe('groupBy', () => {
4
+ test('it groups by the given property', () => {
5
+ const dataSet = [
6
+ {
7
+ _id: 2,
8
+ exercise_id: 2,
9
+ weight_effort: '155lbs',
10
+ reps: 10,
11
+ sets: 3,
12
+ note: '',
13
+ order: 1,
14
+ date: '2024-06-02'
15
+ },
16
+ {
17
+ _id: 1,
18
+ exercise_id: 1,
19
+ weight_effort: '115lbs',
20
+ reps: 10,
21
+ sets: 3,
22
+ note: '',
23
+ order: 0,
24
+ date: '2024-06-02'
25
+ },
26
+ {
27
+ _id: 1,
28
+ exercise_id: 1,
29
+ weight_effort: '115lbs',
30
+ reps: 10,
31
+ sets: 3,
32
+ note: '',
33
+ order: 0,
34
+ date: '2024-06-05'
35
+ },
36
+ {
37
+ _id: 3,
38
+ exercise_id: 3,
39
+ weight_effort: '155lbs',
40
+ reps: 10,
41
+ sets: 3,
42
+ note: '',
43
+ order: 2,
44
+ date: '2024-06-02'
45
+ },
46
+ {
47
+ _id: 2,
48
+ exercise_id: 2,
49
+ weight_effort: '155lbs',
50
+ reps: 10,
51
+ sets: 3,
52
+ note: '',
53
+ order: 1,
54
+ date: '2024-06-05'
55
+ },
56
+ {
57
+ _id: 3,
58
+ exercise_id: 3,
59
+ weight_effort: '155lbs',
60
+ reps: 10,
61
+ sets: 3,
62
+ note: '',
63
+ order: 2,
64
+ date: '2024-06-05'
65
+ }
66
+ ]
67
+ const result = groupBy(dataSet, 'date')
68
+ expect(result).toEqual({
69
+ '2024-06-02': [
70
+ {
71
+ _id: 2,
72
+ exercise_id: 2,
73
+ weight_effort: '155lbs',
74
+ reps: 10,
75
+ sets: 3,
76
+ note: '',
77
+ order: 1,
78
+ date: '2024-06-02'
79
+ },
80
+ {
81
+ _id: 1,
82
+ exercise_id: 1,
83
+ weight_effort: '115lbs',
84
+ reps: 10,
85
+ sets: 3,
86
+ note: '',
87
+ order: 0,
88
+ date: '2024-06-02'
89
+ },
90
+ {
91
+ _id: 3,
92
+ exercise_id: 3,
93
+ weight_effort: '155lbs',
94
+ reps: 10,
95
+ sets: 3,
96
+ note: '',
97
+ order: 2,
98
+ date: '2024-06-02'
99
+ }
100
+ ],
101
+ '2024-06-05': [
102
+ {
103
+ _id: 1,
104
+ exercise_id: 1,
105
+ weight_effort: '115lbs',
106
+ reps: 10,
107
+ sets: 3,
108
+ note: '',
109
+ order: 0,
110
+ date: '2024-06-05'
111
+ },
112
+ {
113
+ _id: 2,
114
+ exercise_id: 2,
115
+ weight_effort: '155lbs',
116
+ reps: 10,
117
+ sets: 3,
118
+ note: '',
119
+ order: 1,
120
+ date: '2024-06-05'
121
+ },
122
+ {
123
+ _id: 3,
124
+ exercise_id: 3,
125
+ weight_effort: '155lbs',
126
+ reps: 10,
127
+ sets: 3,
128
+ note: '',
129
+ order: 2,
130
+ date: '2024-06-05'
131
+ }
132
+ ]
133
+ })
134
+ })
135
+ })
@@ -0,0 +1,16 @@
1
+ export const groupBy = (dataSet = [], groupProperty = '') => {
2
+ if (!groupProperty) {
3
+ return dataSet
4
+ }
5
+ return dataSet.reduce(
6
+ (groupedData, data) => {
7
+ const property = data[groupProperty]
8
+ if (!groupedData.hasOwnProperty(property)) {
9
+ groupedData[property] = []
10
+ }
11
+ groupedData[property].push(data)
12
+ return groupedData
13
+ },
14
+ {}
15
+ )
16
+ }