pure-orm 4.0.3 → 4.1.4

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 (204) hide show
  1. package/.benchmarks/bench-core-baseline.json +303 -0
  2. package/README.md +0 -7
  3. package/babel.config.js +4 -1
  4. package/coverage/clover.xml +1492 -1231
  5. package/coverage/coverage-final.json +103 -103
  6. package/coverage/lcov-report/dist/src/core.js.html +590 -383
  7. package/coverage/lcov-report/dist/src/driver-integrations/index.html +20 -20
  8. package/coverage/lcov-report/dist/src/driver-integrations/pgp.js.html +52 -52
  9. package/coverage/lcov-report/dist/src/index.html +28 -28
  10. package/coverage/lcov-report/dist/src/index.js.html +2 -2
  11. package/coverage/lcov-report/dist/src/orm.js.html +392 -209
  12. package/coverage/lcov-report/dist/test-utils/blog/entities.js.html +1 -1
  13. package/coverage/lcov-report/dist/test-utils/blog/index.html +1 -1
  14. package/coverage/lcov-report/dist/test-utils/blog/models/article.js.html +15 -15
  15. package/coverage/lcov-report/dist/test-utils/blog/models/article_tag.js.html +2 -2
  16. package/coverage/lcov-report/dist/test-utils/blog/models/index.html +1 -1
  17. package/coverage/lcov-report/dist/test-utils/blog/models/person.js.html +13 -13
  18. package/coverage/lcov-report/dist/test-utils/blog/models/tag.js.html +1 -1
  19. package/coverage/lcov-report/dist/test-utils/five/entities.js.html +1 -1
  20. package/coverage/lcov-report/dist/test-utils/five/index.html +1 -1
  21. package/coverage/lcov-report/dist/test-utils/five/models/index.html +1 -1
  22. package/coverage/lcov-report/dist/test-utils/five/models/line-item.js.html +4 -4
  23. package/coverage/lcov-report/dist/test-utils/five/models/order.js.html +3 -3
  24. package/coverage/lcov-report/dist/test-utils/five/models/parcel-event.js.html +6 -6
  25. package/coverage/lcov-report/dist/test-utils/five/models/parcel-line-item.js.html +7 -7
  26. package/coverage/lcov-report/dist/test-utils/five/models/parcel.js.html +2 -2
  27. package/coverage/lcov-report/dist/test-utils/fourteen/entities.js.html +1 -1
  28. package/coverage/lcov-report/dist/test-utils/fourteen/index.html +1 -1
  29. package/coverage/lcov-report/dist/test-utils/fourteen/models/customer.js.html +1 -1
  30. package/coverage/lcov-report/dist/test-utils/fourteen/models/index.html +1 -1
  31. package/coverage/lcov-report/dist/test-utils/fourteen/models/person.js.html +1 -1
  32. package/coverage/lcov-report/dist/test-utils/nine/entities.js.html +1 -1
  33. package/coverage/lcov-report/dist/test-utils/nine/index.html +1 -1
  34. package/coverage/lcov-report/dist/test-utils/nine/models/feature-switch.js.html +6 -6
  35. package/coverage/lcov-report/dist/test-utils/nine/models/index.html +1 -1
  36. package/coverage/lcov-report/dist/test-utils/order/entities.js.html +9 -9
  37. package/coverage/lcov-report/dist/test-utils/order/index.html +1 -1
  38. package/coverage/lcov-report/dist/test-utils/order/models/index.html +14 -14
  39. package/coverage/lcov-report/dist/test-utils/order/models/line-item.js.html +11 -11
  40. package/coverage/lcov-report/dist/test-utils/order/models/order.js.html +41 -41
  41. package/coverage/lcov-report/dist/test-utils/order/models/product-variant.js.html +18 -18
  42. package/coverage/lcov-report/dist/test-utils/order/models/product.js.html +17 -17
  43. package/coverage/lcov-report/dist/test-utils/order/models/utm-source.js.html +12 -12
  44. package/coverage/lcov-report/dist/test-utils/order-more/entities.js.html +1 -1
  45. package/coverage/lcov-report/dist/test-utils/order-more/index.html +1 -1
  46. package/coverage/lcov-report/dist/test-utils/order-more/models/actual-product-variant.js.html +3 -3
  47. package/coverage/lcov-report/dist/test-utils/order-more/models/color.js.html +6 -6
  48. package/coverage/lcov-report/dist/test-utils/order-more/models/customer.js.html +3 -3
  49. package/coverage/lcov-report/dist/test-utils/order-more/models/gender.js.html +4 -4
  50. package/coverage/lcov-report/dist/test-utils/order-more/models/index.html +23 -23
  51. package/coverage/lcov-report/dist/test-utils/order-more/models/inventory-level.js.html +11 -11
  52. package/coverage/lcov-report/dist/test-utils/order-more/models/line-item.js.html +15 -15
  53. package/coverage/lcov-report/dist/test-utils/order-more/models/order.js.html +39 -39
  54. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-event.js.html +6 -6
  55. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-line-item.js.html +7 -7
  56. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel.js.html +2 -2
  57. package/coverage/lcov-report/dist/test-utils/order-more/models/physical-address.js.html +12 -12
  58. package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant-image.js.html +7 -7
  59. package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant.js.html +22 -22
  60. package/coverage/lcov-report/dist/test-utils/order-more/models/product.js.html +11 -11
  61. package/coverage/lcov-report/dist/test-utils/order-more/models/refund.js.html +11 -11
  62. package/coverage/lcov-report/dist/test-utils/order-more/models/shipment-actual-product-variant.js.html +9 -9
  63. package/coverage/lcov-report/dist/test-utils/order-more/models/shipment.js.html +4 -4
  64. package/coverage/lcov-report/dist/test-utils/order-more/models/size.js.html +4 -4
  65. package/coverage/lcov-report/dist/test-utils/order-more/models/utm-medium.js.html +15 -15
  66. package/coverage/lcov-report/dist/test-utils/order-more/models/utm-source.js.html +17 -17
  67. package/coverage/lcov-report/dist/test-utils/six/entities.js.html +1 -1
  68. package/coverage/lcov-report/dist/test-utils/six/index.html +1 -1
  69. package/coverage/lcov-report/dist/test-utils/six/models/customer.js.html +3 -3
  70. package/coverage/lcov-report/dist/test-utils/six/models/index.html +21 -21
  71. package/coverage/lcov-report/dist/test-utils/six/models/line-item.js.html +10 -10
  72. package/coverage/lcov-report/dist/test-utils/six/models/order.js.html +13 -13
  73. package/coverage/lcov-report/dist/test-utils/six/models/parcel-line-item.js.html +2 -2
  74. package/coverage/lcov-report/dist/test-utils/six/models/parcel.js.html +2 -2
  75. package/coverage/lcov-report/dist/test-utils/thirteen/entities.js.html +1 -1
  76. package/coverage/lcov-report/dist/test-utils/thirteen/index.html +1 -1
  77. package/coverage/lcov-report/dist/test-utils/thirteen/models/audience.js.html +2 -2
  78. package/coverage/lcov-report/dist/test-utils/thirteen/models/brand.js.html +2 -2
  79. package/coverage/lcov-report/dist/test-utils/thirteen/models/category.js.html +1 -1
  80. package/coverage/lcov-report/dist/test-utils/thirteen/models/index.html +14 -14
  81. package/coverage/lcov-report/dist/test-utils/thirteen/models/member.js.html +2 -2
  82. package/coverage/lcov-report/dist/test-utils/thirteen/models/passion.js.html +2 -2
  83. package/coverage/lcov-report/dist/test-utils/thirteen/models/product.js.html +10 -10
  84. package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation-audience.js.html +2 -2
  85. package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation.js.html +2 -2
  86. package/coverage/lcov-report/dist/test-utils/three/index.html +1 -1
  87. package/coverage/lcov-report/dist/test-utils/three/results.js.html +1 -1
  88. package/coverage/lcov-report/dist/test-utils/twelve/entities.js.html +1 -1
  89. package/coverage/lcov-report/dist/test-utils/twelve/index.html +1 -1
  90. package/coverage/lcov-report/dist/test-utils/twelve/models/index.html +1 -1
  91. package/coverage/lcov-report/dist/test-utils/twelve/models/member.js.html +1 -1
  92. package/coverage/lcov-report/dist/test-utils/twelve/models/prompt.js.html +2 -2
  93. package/coverage/lcov-report/dist/test-utils/two/index.html +1 -1
  94. package/coverage/lcov-report/dist/test-utils/two/results.js.html +1 -1
  95. package/coverage/lcov-report/index.html +103 -103
  96. package/coverage/lcov-report/src/core.ts.html +841 -415
  97. package/coverage/lcov-report/src/driver-integrations/index.html +20 -20
  98. package/coverage/lcov-report/src/driver-integrations/pgp.ts.html +58 -58
  99. package/coverage/lcov-report/src/index.html +28 -28
  100. package/coverage/lcov-report/src/index.ts.html +1 -1
  101. package/coverage/lcov-report/src/orm.ts.html +361 -298
  102. package/coverage/lcov-report/test-utils/blog/entities.ts.html +1 -1
  103. package/coverage/lcov-report/test-utils/blog/index.html +1 -1
  104. package/coverage/lcov-report/test-utils/blog/models/article.ts.html +15 -15
  105. package/coverage/lcov-report/test-utils/blog/models/article_tag.ts.html +2 -2
  106. package/coverage/lcov-report/test-utils/blog/models/index.html +1 -1
  107. package/coverage/lcov-report/test-utils/blog/models/person.ts.html +13 -13
  108. package/coverage/lcov-report/test-utils/blog/models/tag.ts.html +1 -1
  109. package/coverage/lcov-report/test-utils/five/entities.ts.html +1 -1
  110. package/coverage/lcov-report/test-utils/five/index.html +1 -1
  111. package/coverage/lcov-report/test-utils/five/models/index.html +1 -1
  112. package/coverage/lcov-report/test-utils/five/models/line-item.ts.html +4 -4
  113. package/coverage/lcov-report/test-utils/five/models/order.ts.html +3 -3
  114. package/coverage/lcov-report/test-utils/five/models/parcel-event.ts.html +6 -6
  115. package/coverage/lcov-report/test-utils/five/models/parcel-line-item.ts.html +7 -7
  116. package/coverage/lcov-report/test-utils/five/models/parcel.ts.html +2 -2
  117. package/coverage/lcov-report/test-utils/fourteen/entities.ts.html +1 -1
  118. package/coverage/lcov-report/test-utils/fourteen/index.html +1 -1
  119. package/coverage/lcov-report/test-utils/fourteen/models/customer.ts.html +1 -1
  120. package/coverage/lcov-report/test-utils/fourteen/models/index.html +1 -1
  121. package/coverage/lcov-report/test-utils/fourteen/models/person.ts.html +1 -1
  122. package/coverage/lcov-report/test-utils/nine/entities.ts.html +1 -1
  123. package/coverage/lcov-report/test-utils/nine/index.html +1 -1
  124. package/coverage/lcov-report/test-utils/nine/models/feature-switch.ts.html +8 -8
  125. package/coverage/lcov-report/test-utils/nine/models/index.html +1 -1
  126. package/coverage/lcov-report/test-utils/order/entities.ts.html +2 -2
  127. package/coverage/lcov-report/test-utils/order/index.html +1 -1
  128. package/coverage/lcov-report/test-utils/order/models/index.html +14 -14
  129. package/coverage/lcov-report/test-utils/order/models/line-item.ts.html +5 -5
  130. package/coverage/lcov-report/test-utils/order/models/order.ts.html +36 -36
  131. package/coverage/lcov-report/test-utils/order/models/product-variant.ts.html +13 -13
  132. package/coverage/lcov-report/test-utils/order/models/product.ts.html +13 -13
  133. package/coverage/lcov-report/test-utils/order/models/utm-source.ts.html +8 -8
  134. package/coverage/lcov-report/test-utils/order-more/entities.ts.html +1 -1
  135. package/coverage/lcov-report/test-utils/order-more/index.html +1 -1
  136. package/coverage/lcov-report/test-utils/order-more/models/actual-product-variant.ts.html +3 -3
  137. package/coverage/lcov-report/test-utils/order-more/models/color.ts.html +6 -6
  138. package/coverage/lcov-report/test-utils/order-more/models/customer.ts.html +3 -3
  139. package/coverage/lcov-report/test-utils/order-more/models/gender.ts.html +4 -4
  140. package/coverage/lcov-report/test-utils/order-more/models/index.html +23 -23
  141. package/coverage/lcov-report/test-utils/order-more/models/inventory-level.ts.html +11 -11
  142. package/coverage/lcov-report/test-utils/order-more/models/line-item.ts.html +15 -15
  143. package/coverage/lcov-report/test-utils/order-more/models/order.ts.html +45 -45
  144. package/coverage/lcov-report/test-utils/order-more/models/parcel-event.ts.html +6 -6
  145. package/coverage/lcov-report/test-utils/order-more/models/parcel-line-item.ts.html +7 -7
  146. package/coverage/lcov-report/test-utils/order-more/models/parcel.ts.html +2 -2
  147. package/coverage/lcov-report/test-utils/order-more/models/physical-address.ts.html +12 -12
  148. package/coverage/lcov-report/test-utils/order-more/models/product-variant-image.ts.html +7 -7
  149. package/coverage/lcov-report/test-utils/order-more/models/product-variant.ts.html +22 -22
  150. package/coverage/lcov-report/test-utils/order-more/models/product.ts.html +11 -11
  151. package/coverage/lcov-report/test-utils/order-more/models/refund.ts.html +11 -11
  152. package/coverage/lcov-report/test-utils/order-more/models/shipment-actual-product-variant.ts.html +9 -9
  153. package/coverage/lcov-report/test-utils/order-more/models/shipment.ts.html +4 -4
  154. package/coverage/lcov-report/test-utils/order-more/models/size.ts.html +4 -4
  155. package/coverage/lcov-report/test-utils/order-more/models/utm-medium.ts.html +15 -15
  156. package/coverage/lcov-report/test-utils/order-more/models/utm-source.ts.html +17 -17
  157. package/coverage/lcov-report/test-utils/six/entities.ts.html +1 -1
  158. package/coverage/lcov-report/test-utils/six/index.html +1 -1
  159. package/coverage/lcov-report/test-utils/six/models/customer.ts.html +3 -3
  160. package/coverage/lcov-report/test-utils/six/models/index.html +21 -21
  161. package/coverage/lcov-report/test-utils/six/models/line-item.ts.html +10 -10
  162. package/coverage/lcov-report/test-utils/six/models/order.ts.html +13 -13
  163. package/coverage/lcov-report/test-utils/six/models/parcel-line-item.ts.html +2 -2
  164. package/coverage/lcov-report/test-utils/six/models/parcel.ts.html +2 -2
  165. package/coverage/lcov-report/test-utils/thirteen/entities.ts.html +1 -1
  166. package/coverage/lcov-report/test-utils/thirteen/index.html +1 -1
  167. package/coverage/lcov-report/test-utils/thirteen/models/audience.ts.html +2 -2
  168. package/coverage/lcov-report/test-utils/thirteen/models/brand.ts.html +2 -2
  169. package/coverage/lcov-report/test-utils/thirteen/models/category.ts.html +1 -1
  170. package/coverage/lcov-report/test-utils/thirteen/models/index.html +14 -14
  171. package/coverage/lcov-report/test-utils/thirteen/models/member.ts.html +2 -2
  172. package/coverage/lcov-report/test-utils/thirteen/models/passion.ts.html +2 -2
  173. package/coverage/lcov-report/test-utils/thirteen/models/product.ts.html +10 -10
  174. package/coverage/lcov-report/test-utils/thirteen/models/recommendation-audience.ts.html +2 -2
  175. package/coverage/lcov-report/test-utils/thirteen/models/recommendation.ts.html +2 -2
  176. package/coverage/lcov-report/test-utils/three/index.html +1 -1
  177. package/coverage/lcov-report/test-utils/three/results.js.html +1 -1
  178. package/coverage/lcov-report/test-utils/twelve/entities.ts.html +1 -1
  179. package/coverage/lcov-report/test-utils/twelve/index.html +1 -1
  180. package/coverage/lcov-report/test-utils/twelve/models/index.html +1 -1
  181. package/coverage/lcov-report/test-utils/twelve/models/member.ts.html +1 -1
  182. package/coverage/lcov-report/test-utils/twelve/models/prompt.ts.html +2 -2
  183. package/coverage/lcov-report/test-utils/two/index.html +1 -1
  184. package/coverage/lcov-report/test-utils/two/results.js.html +1 -1
  185. package/coverage/lcov.info +2134 -1989
  186. package/dist/src/core.d.ts +6 -0
  187. package/dist/src/core.js +258 -189
  188. package/dist/src/core.spec.js +413 -0
  189. package/dist/src/driver-integrations/pgp.spec.d.ts +1 -0
  190. package/dist/src/driver-integrations/pgp.spec.js +376 -0
  191. package/dist/src/orm.d.ts +1 -1
  192. package/dist/src/orm.js +137 -76
  193. package/dist/src/orm.spec.js +535 -85
  194. package/dist/test-utils/nine/models/feature-switch.d.ts +2 -2
  195. package/dist/test-utils/nine/models/feature-switch.ts +2 -2
  196. package/package.json +5 -3
  197. package/scripts/bench-core.js +636 -0
  198. package/scripts/check-bench-scenarios.js +47 -0
  199. package/src/core.spec.ts +485 -2
  200. package/src/core.ts +369 -227
  201. package/src/driver-integrations/pgp.spec.ts +444 -0
  202. package/src/orm.spec.ts +592 -88
  203. package/src/orm.ts +149 -128
  204. package/test-utils/nine/models/feature-switch.ts +2 -2
@@ -5,13 +5,13 @@ export const tableName: string = 'feature_switch';
5
5
  export const columns: IColumns = ['id', 'label', 'on'];
6
6
 
7
7
  interface IFeatureSwitchProps {
8
- id: number;
8
+ id: string | number;
9
9
  label: string;
10
10
  on: boolean;
11
11
  }
12
12
 
13
13
  export class FeatureSwitch implements IModel {
14
- id: number;
14
+ id: string | number;
15
15
  label: string;
16
16
  on: boolean;
17
17
 
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "pure-orm",
3
- "version": "4.0.3",
3
+ "version": "4.1.4",
4
4
  "main": "dist/src/index.js",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "engines": {
7
7
  "node": ">=6"
8
8
  },
9
9
  "scripts": {
10
- "test": "npm run test:unit && npm run test:format",
10
+ "test": "npm run test:unit && npm run test:bench-guard && npm run test:format",
11
11
  "test:unit": "npm run build && jest",
12
12
  "test:unit:watch": "jest --watch",
13
13
  "test:unit:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --verbose",
@@ -16,7 +16,9 @@
16
16
  "lint": "eslint .",
17
17
  "pub": "npm run test && npm run build && np",
18
18
  "build": "rm -rf dist && mkdir -p dist && cp -r test-utils dist/test-utils && tsc",
19
- "build:watch": "rm -rf dist && mkdir -p dist && cp -r test-utils dist/test-utils && tsc --watch"
19
+ "build:watch": "rm -rf dist && mkdir -p dist && cp -r test-utils dist/test-utils && tsc --watch",
20
+ "bench:core": "npm run build && node scripts/bench-core.js",
21
+ "test:bench-guard": "node scripts/check-bench-scenarios.js"
20
22
  },
21
23
  "dependencies": {
22
24
  "@babel/core": "^7.17.9",
@@ -0,0 +1,636 @@
1
+ /* eslint-disable no-console */
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { createCore } = require('../dist/src/core');
5
+ const { create: createOrm } = require('../dist/src/orm');
6
+ const {
7
+ entities: orderEntities
8
+ } = require('../dist/test-utils/order/entities');
9
+ const { entities: blogEntities } = require('../dist/test-utils/blog/entities');
10
+ const {
11
+ entities: orderMoreEntities
12
+ } = require('../dist/test-utils/order-more/entities');
13
+ const { entities: nineEntities } = require('../dist/test-utils/nine/entities');
14
+ const { entities: fiveEntities } = require('../dist/test-utils/five/entities');
15
+ const { entities: sixEntities } = require('../dist/test-utils/six/entities');
16
+ const {
17
+ entities: twelveEntities
18
+ } = require('../dist/test-utils/twelve/entities');
19
+ const {
20
+ entities: thirteenEntities
21
+ } = require('../dist/test-utils/thirteen/entities');
22
+ const {
23
+ entities: fourteenEntities
24
+ } = require('../dist/test-utils/fourteen/entities');
25
+
26
+ const two = require('../dist/test-utils/two/results');
27
+ const three = require('../dist/test-utils/three/results');
28
+ const one = require('../dist/test-utils/one/results.json');
29
+ const four = require('../dist/test-utils/four/results.json');
30
+ const five = require('../dist/test-utils/five/results.json');
31
+ const six = require('../dist/test-utils/six/results.json');
32
+ const seven = require('../dist/test-utils/seven/results.json');
33
+ const eight = require('../dist/test-utils/eight/results.json');
34
+ const nine = require('../dist/test-utils/nine/results.json');
35
+ const ten = require('../dist/test-utils/ten/results.json');
36
+ const eleven = require('../dist/test-utils/eleven/results.json');
37
+ const twelve = require('../dist/test-utils/twelve/results.json');
38
+ const thirteen = require('../dist/test-utils/thirteen/results.json');
39
+ const fourteen = require('../dist/test-utils/fourteen/results.json');
40
+ const { Order } = require('../dist/test-utils/order/models/order');
41
+
42
+ const SAMPLE_COUNT = Number(process.env.BENCH_SAMPLES || 5);
43
+ const HELPER_ITERATIONS = Number(process.env.BENCH_HELPER_ITERS || 300000);
44
+ const SAVE_BASELINE_PATH = process.env.BENCH_SAVE_BASELINE || '';
45
+ const COMPARE_BASELINE_PATH = process.env.BENCH_COMPARE_BASELINE || '';
46
+ const BENCH_SEED = Number(process.env.BENCH_SEED || 1337);
47
+
48
+ const FIXTURE_CASES = [
49
+ { label: 'order/one', entities: orderEntities, rows: one },
50
+ { label: 'blog/two', entities: blogEntities, rows: two },
51
+ { label: 'blog/three', entities: blogEntities, rows: three },
52
+ { label: 'order-more/four', entities: orderMoreEntities, rows: four },
53
+ { label: 'five/five', entities: fiveEntities, rows: five },
54
+ { label: 'six/six', entities: sixEntities, rows: six },
55
+ { label: 'order-more/seven', entities: orderMoreEntities, rows: seven },
56
+ { label: 'order-more/eight', entities: orderMoreEntities, rows: eight },
57
+ { label: 'nine/nine', entities: nineEntities, rows: nine },
58
+ { label: 'order-more/ten', entities: orderMoreEntities, rows: ten },
59
+ { label: 'order-more/eleven', entities: orderMoreEntities, rows: eleven },
60
+ { label: 'twelve/twelve', entities: twelveEntities, rows: twelve },
61
+ { label: 'thirteen/thirteen', entities: thirteenEntities, rows: thirteen },
62
+ { label: 'fourteen/fourteen', entities: fourteenEntities, rows: fourteen }
63
+ ];
64
+
65
+ const STRESS_SCENARIOS = [
66
+ {
67
+ label: 'stress/same-root x120',
68
+ entities: orderMoreEntities,
69
+ baseRows: eleven,
70
+ multiplier: 120,
71
+ distributeRoots: false,
72
+ sparseJoins: false,
73
+ rounds: 5
74
+ },
75
+ {
76
+ label: 'stress/many-roots x120',
77
+ entities: orderMoreEntities,
78
+ baseRows: eleven,
79
+ multiplier: 120,
80
+ distributeRoots: true,
81
+ sparseJoins: false,
82
+ rounds: 5
83
+ },
84
+ {
85
+ label: 'stress/sparse-joins x120',
86
+ entities: orderMoreEntities,
87
+ baseRows: eleven,
88
+ multiplier: 120,
89
+ distributeRoots: false,
90
+ sparseJoins: true,
91
+ rounds: 8
92
+ },
93
+ {
94
+ label: 'stress/sparse-many x120',
95
+ entities: orderMoreEntities,
96
+ baseRows: eleven,
97
+ multiplier: 120,
98
+ distributeRoots: true,
99
+ sparseJoins: true,
100
+ rounds: 8
101
+ },
102
+ {
103
+ label: 'stress/unstable-order x120',
104
+ entities: orderMoreEntities,
105
+ baseRows: eleven,
106
+ multiplier: 120,
107
+ distributeRoots: false,
108
+ unstableColumnOrder: true,
109
+ rounds: 8
110
+ },
111
+ {
112
+ label: 'stress/unstable-many x120',
113
+ entities: orderMoreEntities,
114
+ baseRows: eleven,
115
+ multiplier: 120,
116
+ distributeRoots: true,
117
+ unstableColumnOrder: true,
118
+ rounds: 8
119
+ },
120
+ {
121
+ label: 'stress/mixed-fixtures x80',
122
+ entities: orderMoreEntities,
123
+ baseRowsSet: [seven, eight, ten, eleven],
124
+ multiplier: 80,
125
+ distributeRoots: true,
126
+ sparseJoins: false,
127
+ shuffleRows: true,
128
+ rounds: 6
129
+ },
130
+ {
131
+ label: 'stress/mixed-sparse x80',
132
+ entities: orderMoreEntities,
133
+ baseRowsSet: [seven, eight, ten, eleven],
134
+ multiplier: 80,
135
+ distributeRoots: true,
136
+ sparseJoins: true,
137
+ shuffleRows: true,
138
+ rounds: 8
139
+ },
140
+ {
141
+ label: 'stress/composite-pk x120',
142
+ entities: null,
143
+ baseRows: null,
144
+ multiplier: 120,
145
+ distributeRoots: false,
146
+ compositePk: true,
147
+ rounds: 8
148
+ }
149
+ ];
150
+
151
+ const createRng = (seed) => {
152
+ let state = seed >>> 0 || 1;
153
+ return () => {
154
+ state = (state * 1664525 + 1013904223) >>> 0;
155
+ return state / 0x100000000;
156
+ };
157
+ };
158
+
159
+ const shuffleInPlace = (items, random) => {
160
+ for (let i = items.length - 1; i > 0; i--) {
161
+ const j = Math.floor(random() * (i + 1));
162
+ const tmp = items[i];
163
+ items[i] = items[j];
164
+ items[j] = tmp;
165
+ }
166
+ };
167
+
168
+ const sparsifyRow = ({ row, rootTablePrefix }) => {
169
+ const sparse = {};
170
+ for (const key in row) {
171
+ if (!Object.prototype.hasOwnProperty.call(row, key)) {
172
+ continue;
173
+ }
174
+ sparse[key] = key.startsWith(`${rootTablePrefix}#`) ? row[key] : null;
175
+ }
176
+ return sparse;
177
+ };
178
+
179
+ const reorderRowKeys = (row) => {
180
+ const keys = Object.keys(row);
181
+ const reordered = {};
182
+ for (let i = 0; i < keys.length; i++) {
183
+ const idx = (i * 7 + 3) % keys.length;
184
+ const key = keys[idx];
185
+ reordered[key] = row[key];
186
+ }
187
+ return reordered;
188
+ };
189
+
190
+ const createCompositePkScenario = ({ multiplier }) => {
191
+ class CompositeRoot {
192
+ constructor(props) {
193
+ Object.assign(this, props);
194
+ }
195
+ }
196
+ class CompositeRoots {
197
+ constructor({ models }) {
198
+ this.models = models;
199
+ }
200
+ }
201
+ class CompositeChild {
202
+ constructor(props) {
203
+ Object.assign(this, props);
204
+ }
205
+ }
206
+ class CompositeChildren {
207
+ constructor({ models }) {
208
+ this.models = models;
209
+ }
210
+ }
211
+
212
+ const entities = [
213
+ {
214
+ tableName: 'comp_root',
215
+ columns: [
216
+ { column: 'tenant_id', primaryKey: true },
217
+ { column: 'order_id', primaryKey: true },
218
+ 'label'
219
+ ],
220
+ Model: CompositeRoot,
221
+ Collection: CompositeRoots
222
+ },
223
+ {
224
+ tableName: 'comp_child',
225
+ columns: [
226
+ 'id',
227
+ { column: 'root_key', references: CompositeRoot },
228
+ 'value'
229
+ ],
230
+ Model: CompositeChild,
231
+ Collection: CompositeChildren
232
+ }
233
+ ];
234
+
235
+ const rows = [];
236
+ let childId = 1;
237
+ for (let m = 0; m < multiplier; m++) {
238
+ for (let tenantId = 1; tenantId <= 20; tenantId++) {
239
+ const orderId = m + 1000;
240
+ const rootKey = `${tenantId}${orderId}`;
241
+ for (let c = 0; c < 3; c++) {
242
+ rows.push({
243
+ 'comp_root#tenant_id': tenantId,
244
+ 'comp_root#order_id': orderId,
245
+ 'comp_root#label': `r-${tenantId}-${orderId}`,
246
+ 'comp_child#id': childId++,
247
+ 'comp_child#root_key': rootKey,
248
+ 'comp_child#value': `v-${c}`
249
+ });
250
+ }
251
+ }
252
+ }
253
+ return { entities, rows };
254
+ };
255
+
256
+ const buildStressRows = ({
257
+ baseRows,
258
+ baseRowsSet,
259
+ multiplier,
260
+ distributeRoots,
261
+ sparseJoins = false,
262
+ unstableColumnOrder = false,
263
+ shuffleRows = false,
264
+ rng
265
+ }) => {
266
+ const sourceRows = baseRowsSet ? baseRowsSet.flat() : baseRows;
267
+ const rows = [];
268
+ const firstKey = Object.keys(sourceRows[0]).find((k) => k.endsWith('#id'));
269
+ const rootTable = firstKey ? firstKey.split('#')[0] : 'order';
270
+ for (let i = 0; i < multiplier; i++) {
271
+ for (const row of sourceRows) {
272
+ let base = sparseJoins
273
+ ? sparsifyRow({ row, rootTablePrefix: rootTable })
274
+ : row;
275
+ if (unstableColumnOrder) {
276
+ base = reorderRowKeys(base);
277
+ }
278
+ const cloned = { ...base };
279
+ if (distributeRoots) {
280
+ const rootIdKey = `${rootTable}#id`;
281
+ cloned[rootIdKey] = row[rootIdKey] + (i + 1) * 10000;
282
+ }
283
+ rows.push(cloned);
284
+ }
285
+ }
286
+ if (shuffleRows && rows.length > 1) {
287
+ shuffleInPlace(rows, rng || Math.random);
288
+ }
289
+ return rows;
290
+ };
291
+
292
+ const runBench = ({ label, rows, rounds, core }) => {
293
+ // Warm JIT and hidden classes before timing.
294
+ core.createFromDatabase(rows);
295
+ const samples = [];
296
+ for (let sample = 0; sample < SAMPLE_COUNT; sample++) {
297
+ if (global.gc) {
298
+ global.gc();
299
+ }
300
+ const start = process.hrtime.bigint();
301
+ for (let i = 0; i < rounds; i++) {
302
+ core.createFromDatabase(rows);
303
+ }
304
+ const elapsedMs = Number(process.hrtime.bigint() - start) / 1e6;
305
+ samples.push(elapsedMs);
306
+ }
307
+ const sortedSamples = [...samples].sort((a, b) => a - b);
308
+ const medianElapsedMs = sortedSamples[Math.floor(sortedSamples.length / 2)];
309
+ const minElapsedMs = sortedSamples[0];
310
+ const maxElapsedMs = sortedSamples[sortedSamples.length - 1];
311
+ const meanElapsedMs = samples.reduce((acc, n) => acc + n, 0) / samples.length;
312
+ const perIterMs = medianElapsedMs / rounds;
313
+ const rowsPerSecond = (rows.length * rounds * 1000) / medianElapsedMs;
314
+ return {
315
+ label,
316
+ rows: rows.length,
317
+ rounds,
318
+ samples: SAMPLE_COUNT,
319
+ medianElapsedMs,
320
+ meanElapsedMs,
321
+ minElapsedMs,
322
+ maxElapsedMs,
323
+ perIterMs,
324
+ rowsPerSecond
325
+ };
326
+ };
327
+
328
+ const format = (n, decimals = 2) => n.toFixed(decimals);
329
+ const formatPercent = (n) => `${n > 0 ? '+' : ''}${format(n, 2)}%`;
330
+ const geometricMean = (values) => {
331
+ const positives = values.filter((v) => v > 0);
332
+ if (positives.length === 0) {
333
+ return 0;
334
+ }
335
+ let sum = 0;
336
+ for (let i = 0; i < positives.length; i++) {
337
+ sum += Math.log(positives[i]);
338
+ }
339
+ return Math.exp(sum / positives.length);
340
+ };
341
+ const indexByLabel = (items) => {
342
+ const index = new Map();
343
+ for (let i = 0; i < items.length; i++) {
344
+ index.set(items[i].label, items[i]);
345
+ }
346
+ return index;
347
+ };
348
+
349
+ const printDeltaTable = ({
350
+ title,
351
+ currentItems,
352
+ baselineItems,
353
+ metric,
354
+ unit
355
+ }) => {
356
+ const baselineByLabel = indexByLabel(baselineItems);
357
+ console.log(`\n${title}`);
358
+ for (let i = 0; i < currentItems.length; i++) {
359
+ const current = currentItems[i];
360
+ const baseline = baselineByLabel.get(current.label);
361
+ if (!baseline || !baseline[metric]) {
362
+ console.log(`${current.label.padEnd(30)} | baseline: n/a`);
363
+ continue;
364
+ }
365
+ const baselineValue = baseline[metric];
366
+ const currentValue = current[metric];
367
+ const deltaPct = ((currentValue - baselineValue) / baselineValue) * 100;
368
+ console.log(
369
+ `${current.label.padEnd(30)} | ${format(
370
+ baselineValue,
371
+ metric === 'rowsPerSecond' || metric === 'opsPerSec' ? 0 : 2
372
+ )} -> ${format(
373
+ currentValue,
374
+ metric === 'rowsPerSecond' || metric === 'opsPerSec' ? 0 : 2
375
+ )} ${unit} (${formatPercent(deltaPct)})`
376
+ );
377
+ }
378
+ };
379
+
380
+ const saveBaseline = ({ outputPath, payload }) => {
381
+ const resolved = path.resolve(outputPath);
382
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
383
+ fs.writeFileSync(resolved, JSON.stringify(payload, null, 2));
384
+ console.log(`\nSaved baseline: ${resolved}`);
385
+ };
386
+
387
+ const loadBaseline = (inputPath) => {
388
+ const resolved = path.resolve(inputPath);
389
+ const raw = fs.readFileSync(resolved, 'utf8');
390
+ return { resolved, data: JSON.parse(raw) };
391
+ };
392
+
393
+ const main = () => {
394
+ const rng = createRng(BENCH_SEED);
395
+ console.log('Pure ORM core.createFromDatabase benchmark');
396
+ console.log(
397
+ 'Coverage: all core.spec test-utils fixtures + stress scenarios\n'
398
+ );
399
+ console.log(
400
+ `Sampling: ${SAMPLE_COUNT} samples/scenario, ${
401
+ global.gc ? 'GC enabled' : 'GC not enabled'
402
+ }, seed=${BENCH_SEED}`
403
+ );
404
+ if (SAVE_BASELINE_PATH) {
405
+ console.log(`Save baseline: ${path.resolve(SAVE_BASELINE_PATH)}`);
406
+ }
407
+ if (COMPARE_BASELINE_PATH) {
408
+ console.log(`Compare baseline: ${path.resolve(COMPARE_BASELINE_PATH)}`);
409
+ }
410
+ console.log('');
411
+
412
+ const fixtureResults = FIXTURE_CASES.map((fixture) => {
413
+ const core = createCore({ entities: fixture.entities });
414
+ const rounds = 200;
415
+ return runBench({
416
+ label: fixture.label,
417
+ rows: fixture.rows,
418
+ rounds,
419
+ core
420
+ });
421
+ });
422
+
423
+ console.log('Fixture scenarios');
424
+ for (const r of fixtureResults) {
425
+ console.log(
426
+ `${r.label.padEnd(22)} | rows=${String(r.rows).padStart(
427
+ 4
428
+ )} | rounds=${String(r.rounds).padStart(3)} | ${format(
429
+ r.perIterMs,
430
+ 4
431
+ )} ms/iter | ${format(r.rowsPerSecond, 0)} rows/sec | med=${format(
432
+ r.medianElapsedMs,
433
+ 2
434
+ )}ms mean=${format(r.meanElapsedMs, 2)}ms min=${format(
435
+ r.minElapsedMs,
436
+ 2
437
+ )}ms max=${format(r.maxElapsedMs, 2)}ms`
438
+ );
439
+ }
440
+ const fixtureGeomean = geometricMean(
441
+ fixtureResults.map((r) => r.rowsPerSecond)
442
+ );
443
+ console.log(`fixture geomean rows/sec: ${format(fixtureGeomean, 0)}\n`);
444
+
445
+ const stressResults = STRESS_SCENARIOS.map((scenario) => {
446
+ let entities = scenario.entities;
447
+ let rows;
448
+ if (scenario.compositePk) {
449
+ const composite = createCompositePkScenario({
450
+ multiplier: scenario.multiplier
451
+ });
452
+ entities = composite.entities;
453
+ rows = composite.rows;
454
+ } else {
455
+ rows = buildStressRows({ ...scenario, rng });
456
+ }
457
+ const core = createCore({ entities });
458
+ return runBench({
459
+ label: scenario.label,
460
+ rows,
461
+ rounds: scenario.rounds,
462
+ core
463
+ });
464
+ });
465
+
466
+ console.log('Stress scenarios');
467
+ for (const r of stressResults) {
468
+ console.log(
469
+ `${r.label.padEnd(22)} | rows=${String(r.rows).padStart(
470
+ 5
471
+ )} | rounds=${String(r.rounds).padStart(2)} | ${format(
472
+ r.perIterMs
473
+ )} ms/iter | ${format(r.rowsPerSecond, 0)} rows/sec | med=${format(
474
+ r.medianElapsedMs,
475
+ 2
476
+ )}ms mean=${format(r.meanElapsedMs, 2)}ms min=${format(
477
+ r.minElapsedMs,
478
+ 2
479
+ )}ms max=${format(r.maxElapsedMs, 2)}ms`
480
+ );
481
+ }
482
+ const stressGeomean = geometricMean(
483
+ stressResults.map((r) => r.rowsPerSecond)
484
+ );
485
+ console.log(`stress geomean rows/sec: ${format(stressGeomean, 0)}`);
486
+
487
+ const fakeDb = {
488
+ $config: { pgp: true },
489
+ many: () => Promise.resolve([]),
490
+ any: () => Promise.resolve([]),
491
+ result: () => Promise.resolve({ rows: [], fields: [{ name: 'order#id' }] }),
492
+ none: () => Promise.resolve()
493
+ };
494
+ const orm = createOrm({ entities: orderEntities, db: fakeDb });
495
+ const benchmarkOrders = Array.from({ length: 32 }, (_, i) => {
496
+ const ts = 1700000000000 + i * 86400000;
497
+ return new Order({
498
+ id: i + 1,
499
+ email: `user${i}@example.com`,
500
+ browserIp: `127.0.0.${(i % 250) + 1}`,
501
+ browserUserAgent: `ua-${i % 5}`,
502
+ kujoImportedDate: new Date(ts),
503
+ createdDate: new Date(ts - 1000),
504
+ cancelReason: i % 7 === 0 ? 'test' : null,
505
+ cancelledDate: i % 7 === 0 ? new Date(ts + 5000) : null,
506
+ closedDate: null,
507
+ processedDate: new Date(ts + 1000),
508
+ updatedDate: new Date(ts + 2000),
509
+ note: `n-${i}`,
510
+ subtotalPrice: i + 1,
511
+ taxesIncluded: i % 2 === 0,
512
+ totalDiscounts: i % 3,
513
+ totalPrice: i + 2,
514
+ totalTax: i % 5,
515
+ totalWeight: i % 11,
516
+ orderStatusUrl: `url-${i}`,
517
+ utmSourceId: (i % 4) + 1,
518
+ utmMediumId: (i % 6) + 1,
519
+ utmCampaign: `camp-${i % 3}`,
520
+ utmContent: `content-${i % 8}`,
521
+ utmTerm: `term-${i % 9}`
522
+ });
523
+ });
524
+ const benchmarkProperties = [
525
+ 'updatedDate',
526
+ 'email',
527
+ 'totalPrice',
528
+ 'utmCampaign'
529
+ ];
530
+ const ormHelperScenarios = [
531
+ {
532
+ label: 'orm/getSqlInsertParts',
533
+ fn: (i) => orm.getSqlInsertParts(benchmarkOrders[i & 31])
534
+ },
535
+ {
536
+ label: 'orm/getSqlUpdateParts',
537
+ fn: (i) => orm.getSqlUpdateParts(benchmarkOrders[i & 31], 'id')
538
+ },
539
+ {
540
+ label: 'orm/getMatchingParts',
541
+ fn: (i) => orm.getMatchingParts(benchmarkOrders[i & 31])
542
+ },
543
+ {
544
+ label: 'orm/getMatchingPartsObject',
545
+ fn: (i) => orm.getMatchingPartsObject(benchmarkOrders[i & 31])
546
+ },
547
+ {
548
+ label: 'orm/getSqlColumnForPropertyName',
549
+ fn: (i) =>
550
+ orm.getSqlColumnForPropertyName(
551
+ benchmarkOrders[i & 31],
552
+ benchmarkProperties[i % benchmarkProperties.length]
553
+ )
554
+ }
555
+ ];
556
+ console.log('\nORM helper microbench');
557
+ const ormHelperResults = [];
558
+ for (const scenario of ormHelperScenarios) {
559
+ const sampleMs = [];
560
+ for (let sample = 0; sample < SAMPLE_COUNT; sample++) {
561
+ scenario.fn(0);
562
+ if (global.gc) {
563
+ global.gc();
564
+ }
565
+ const start = process.hrtime.bigint();
566
+ for (let i = 0; i < HELPER_ITERATIONS; i++) {
567
+ scenario.fn(i);
568
+ }
569
+ sampleMs.push(Number(process.hrtime.bigint() - start) / 1e6);
570
+ }
571
+ const sorted = [...sampleMs].sort((a, b) => a - b);
572
+ const medianMs = sorted[Math.floor(sorted.length / 2)];
573
+ const minMs = sorted[0];
574
+ const maxMs = sorted[sorted.length - 1];
575
+ const meanMs = sampleMs.reduce((acc, n) => acc + n, 0) / sampleMs.length;
576
+ const opsPerSec = (HELPER_ITERATIONS * 1000) / medianMs;
577
+ ormHelperResults.push({
578
+ label: scenario.label,
579
+ medianMs,
580
+ meanMs,
581
+ minMs,
582
+ maxMs,
583
+ opsPerSec
584
+ });
585
+ console.log(
586
+ `${scenario.label.padEnd(30)} | ${format(
587
+ medianMs,
588
+ 2
589
+ )} ms total (median) | ${format(opsPerSec, 0)} ops/sec | mean=${format(
590
+ meanMs,
591
+ 2
592
+ )}ms min=${format(minMs, 2)}ms max=${format(maxMs, 2)}ms`
593
+ );
594
+ }
595
+
596
+ const payload = {
597
+ generatedAt: new Date().toISOString(),
598
+ sampleCount: SAMPLE_COUNT,
599
+ helperIterations: HELPER_ITERATIONS,
600
+ fixtureResults,
601
+ stressResults,
602
+ ormHelperResults
603
+ };
604
+
605
+ if (SAVE_BASELINE_PATH) {
606
+ saveBaseline({ outputPath: SAVE_BASELINE_PATH, payload });
607
+ }
608
+
609
+ if (COMPARE_BASELINE_PATH) {
610
+ const baseline = loadBaseline(COMPARE_BASELINE_PATH);
611
+ console.log(`\nLoaded baseline: ${baseline.resolved}`);
612
+ printDeltaTable({
613
+ title: 'Fixture rows/sec delta vs baseline',
614
+ currentItems: fixtureResults,
615
+ baselineItems: baseline.data.fixtureResults || [],
616
+ metric: 'rowsPerSecond',
617
+ unit: 'rows/sec'
618
+ });
619
+ printDeltaTable({
620
+ title: 'Stress rows/sec delta vs baseline',
621
+ currentItems: stressResults,
622
+ baselineItems: baseline.data.stressResults || [],
623
+ metric: 'rowsPerSecond',
624
+ unit: 'rows/sec'
625
+ });
626
+ printDeltaTable({
627
+ title: 'ORM helper ops/sec delta vs baseline',
628
+ currentItems: ormHelperResults,
629
+ baselineItems: baseline.data.ormHelperResults || [],
630
+ metric: 'opsPerSec',
631
+ unit: 'ops/sec'
632
+ });
633
+ }
634
+ };
635
+
636
+ main();