masquerade-orm 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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -0
  3. package/bin/universalTsInit.js +59 -0
  4. package/docs/deletion.md +186 -0
  5. package/docs/find.md +265 -0
  6. package/docs/getting-started-javascript.md +112 -0
  7. package/docs/getting-started-typescript.md +159 -0
  8. package/docs/in-depth-class-definitions.md +193 -0
  9. package/docs/jsdoc-ux-tips.md +17 -0
  10. package/docs/managing-the-database.md +37 -0
  11. package/docs/saving-to-database.md +37 -0
  12. package/index.d.ts +7 -0
  13. package/index.js +11 -0
  14. package/jsconfig.json +29 -0
  15. package/package.json +40 -0
  16. package/src/ORM/DbManager.js +76 -0
  17. package/src/ORM/ORM.js +181 -0
  18. package/src/ORM/bootOrm.js +929 -0
  19. package/src/changeLogger/changeLogger.js +55 -0
  20. package/src/changeLogger/save.js +237 -0
  21. package/src/changeLogger/sqlClients/postgres.js +97 -0
  22. package/src/changeLogger/sqlClients/sqlite.js +120 -0
  23. package/src/entity/delete/delete.js +20 -0
  24. package/src/entity/delete/getDependents.js +46 -0
  25. package/src/entity/entity.d.ts +46 -0
  26. package/src/entity/entity.js +228 -0
  27. package/src/entity/find/find.js +157 -0
  28. package/src/entity/find/joins.js +52 -0
  29. package/src/entity/find/queryBuilder.js +95 -0
  30. package/src/entity/find/relations.js +23 -0
  31. package/src/entity/find/scopeProxies.js +60 -0
  32. package/src/entity/find/sqlClients/postgresFuncs.js +171 -0
  33. package/src/entity/find/sqlClients/sqliteFuncs.js +211 -0
  34. package/src/entity/find/where/relationalWhere.js +142 -0
  35. package/src/entity/find/where/where.js +244 -0
  36. package/src/entity/find/where/whereArgsFunctions.js +49 -0
  37. package/src/misc/classes.js +63 -0
  38. package/src/misc/constants.js +42 -0
  39. package/src/misc/miscFunctions.js +167 -0
  40. package/src/misc/ormStore.js +18 -0
  41. package/src/misc/types.d.ts +560 -0
  42. package/src/proxies/instanceProxy.js +544 -0
  43. package/src/proxies/nonRelationalArrayProxy.js +76 -0
  44. package/src/proxies/objectProxy.js +50 -0
  45. package/src/proxies/relationalArrayProxy.js +130 -0
  46. package/src/webpack/masquerade-loader.js +14 -0
  47. package/src/webpack/plugin.js +43 -0
  48. package/src/webpack/store.js +2 -0
  49. package/src/webpack/webpack.config.js +41 -0
  50. package/testing/dev-doc.md +7 -0
  51. package/testing/generationFuncs.js +21 -0
  52. package/testing/miscFunctions.js +97 -0
  53. package/testing/postgres.test.js +254 -0
  54. package/testing/sqlite.test.js +257 -0
  55. package/testing/testing-classes.js +102 -0
@@ -0,0 +1,130 @@
1
+ import { ChangeLogger } from "../changeLogger/changeLogger.js"
2
+ import { getValidTypedArray } from "../misc/miscFunctions.js"
3
+ import { OrmStore } from "../misc/ormStore.js"
4
+ import { setUpdatedAtValue } from "./instanceProxy.js"
5
+
6
+
7
+ export function createRelationalArrayProxy(instance, propertyName, array = [], /**@type {string | undefined}*/ arrElementValidType = undefined) {
8
+ const instanceId = instance.id
9
+ const instanceClass = instance.constructor.name
10
+ const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
11
+ const eventListenersObj = {}
12
+ //if arrElementValidType is undefined, it just means that the array we are proxifying is an array we got from the db, so the typing is correct.
13
+ if (arrElementValidType) array = getValidTypedArray(array, arrElementValidType, true)
14
+ array.forEach(proxy => addEventListener2ArrayProxy(proxy, array, eventListenersObj, instance.id, propertyName, instanceClass))
15
+
16
+ return new Proxy(array, {
17
+ //ARRAY PROXIES ARE FOR RELATIONAL X:N
18
+ get(target, key, receiver) {
19
+ if (key === "source_") return target
20
+ else if (key === "eListener_") return eventListenersObj
21
+ else {
22
+ if (key === "includes") return (instance) => {
23
+ //@ts-ignore
24
+ if (eventListenersObj[instance.id]) return true
25
+ return false
26
+ }
27
+ return target[key]
28
+ }
29
+ },
30
+ set(/**@type {any[]}*/ target, key, value, receiver) {
31
+ if (key === "length") return Reflect.set(target, key, value, receiver)
32
+ return relationalArrayProxySetHandler(target, key, value, propertyName, instanceId, eventListenersObj, instanceClass)
33
+ },
34
+ deleteProperty(target, key) {
35
+ return relationalArrayProxyDeleteHandler(target, key, propertyName, instanceId, eventListenersObj, instanceClass)
36
+ }
37
+ })
38
+ }
39
+
40
+
41
+ export function relationalArrayProxySetHandler(target, key, newInstanceProxy, propertyName, instanceId, eventListenersObj, instanceClass) {
42
+ const index = parseInt(key)
43
+ if (index > -1) {
44
+ const oldInstanceProxy = target[index]
45
+ const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
46
+ const instanceChangesObj = classChangeObj[instanceId] ??= {}
47
+
48
+ if (eventListenersObj[newInstanceProxy.id]) throw new Error(`Do not insert duplicate entity instances into a relational array. Use the 'includes' method for O(1) lookup.`)
49
+ const relationChangeLogger = instanceChangesObj[propertyName] ??= { added: [], removed: [] }
50
+
51
+ if (oldInstanceProxy) {
52
+ const oldValueInAdded = relationChangeLogger.added.indexOf(oldInstanceProxy.id)
53
+ if (oldValueInAdded > -1) relationChangeLogger.added.splice(oldValueInAdded, 1)
54
+ else relationChangeLogger.removed.push(oldInstanceProxy.id)
55
+
56
+ const oldProxyEventEmitter = oldInstanceProxy.eEmitter_
57
+ oldProxyEventEmitter.removeEventListener("delete", eventListenersObj[oldInstanceProxy.id])
58
+ delete eventListenersObj[oldInstanceProxy.id]
59
+ }
60
+ const newValueInRemoved = relationChangeLogger.removed.indexOf(newInstanceProxy.id)
61
+ if (newValueInRemoved > -1) relationChangeLogger.removed.splice(newValueInRemoved, 1)
62
+ else relationChangeLogger.added.push(newInstanceProxy.id)
63
+
64
+ target[index] = newInstanceProxy
65
+ addEventListener2ArrayProxy(newInstanceProxy, target, eventListenersObj, instanceId, propertyName, instanceClass)
66
+ setUpdatedAtValue(target, instanceChangesObj)
67
+ return true
68
+ }
69
+ else if (typeof index === `string` || typeof index === `symbol`) {
70
+ target[index] = newInstanceProxy
71
+ return true
72
+ }
73
+ return false
74
+ }
75
+
76
+ export function relationalArrayProxyDeleteHandler(target, key, propertyName, instanceId, eventListenersObj, instanceClass) {
77
+ const validProp = Object.hasOwn(target, key)
78
+ const deletedArrEl = target[key]
79
+
80
+ if (validProp) {
81
+ //@ts-ignore
82
+ const index = parseInt(key)
83
+ if (index > -1) {
84
+ const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
85
+ const instanceChangesObj = classChangeObj[instanceId] ??= {}
86
+ const removedEventFunc = eventListenersObj[deletedArrEl.id]
87
+ deletedArrEl.eEmitter_.removeEventListener("delete", removedEventFunc)
88
+ logRelationalArrayRemoval(target, propertyName, deletedArrEl.id, instanceChangesObj)
89
+ }
90
+ delete target[key]
91
+ return true
92
+ }
93
+ return false
94
+ }
95
+
96
+ export function logRelationalArrayRemoval(instance, propertyName, removedId, instanceChangesObj) {
97
+ if (!instanceChangesObj[propertyName]) {
98
+ instanceChangesObj[propertyName] = { added: [], removed: [removedId] }
99
+ }
100
+ else {
101
+ const relationalArrChangeLogger = instanceChangesObj[propertyName]
102
+ const oldIndex = relationalArrChangeLogger.added.indexOf(removedId)
103
+ if (oldIndex > -1) relationalArrChangeLogger.added.splice(oldIndex, 1)
104
+ else relationalArrChangeLogger.removed.push(removedId)
105
+ }
106
+ setUpdatedAtValue(instance, instanceChangesObj)
107
+ }
108
+
109
+ export function addEventListener2ArrayProxy(proxy, array, eventListenersObj, idOfInstanceWithArray, propertyOfArray, instanceClass) {
110
+ const emitter = proxy.eEmitter_
111
+ const eventFunc = (event) => {
112
+ if (proxy._isDeleted_) return
113
+ const id2delete = event.detail.id
114
+ // console.log(`Delete event sent from ${id2delete}_${proxy.constructor.name} to proxy array`)
115
+ const index = array.findIndex(proxy => proxy.id === id2delete)
116
+ array.splice(index, 1)
117
+ delete eventListenersObj[id2delete]
118
+ const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
119
+ const instanceChangesObj = classChangeObj[idOfInstanceWithArray] ??= {}
120
+ logRelationalArrayRemoval(proxy, propertyOfArray, id2delete, instanceChangesObj)
121
+ }
122
+
123
+ emitter.addEventListener(
124
+ "delete",
125
+ eventFunc,
126
+ { once: true }
127
+ )
128
+
129
+ eventListenersObj[proxy.id] = eventFunc
130
+ }
@@ -0,0 +1,14 @@
1
+ import { store } from "./store"
2
+ import { createSourceFile, SyntaxKind, ScriptKind, ScriptTarget } from "typescript"
3
+
4
+
5
+ export default function (source) {
6
+ const nodes = Object.values(source).map(source => createSourceFile('', source.toString(), ScriptTarget.Latest, true, ScriptKind.TSX).statements[0])
7
+ for (const node of nodes) {
8
+ //@ts-ignore
9
+ if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && node.heritageClauses)
10
+ //@ts-ignore
11
+ store.nodeArr.push(node)
12
+ }
13
+ return source
14
+ }
@@ -0,0 +1,43 @@
1
+
2
+ import { store } from "./store.js"
3
+
4
+ import { nodeArr2ClassDict } from "../ORM/bootOrm.js"
5
+
6
+ export class MasqueradePlugin {
7
+ apply(compiler) {
8
+ compiler.hooks.compilation.tap(this.constructor.name, (compilation) => {
9
+ compilation.hooks.processAssets.tap(
10
+ {
11
+ name: this.constructor.name,
12
+ stage: compilation.constructor.PROCESS_ASSETS_STAGE_ADDITIONS,
13
+ },
14
+ (assets) => {
15
+ const classDict = nodeArr2ClassDict(store.nodeArr)
16
+ const prefix = `globalThis.masqueradeClassDict_ = ${JSON.stringify(classDict)};\n`
17
+
18
+ for (const entry of compilation.entrypoints.values()) {
19
+ for (const file of entry.getFiles()) {
20
+ if (!file.endsWith(".js")) continue
21
+
22
+ const asset = compilation.getAsset(file)
23
+ const source = asset.source.source()
24
+
25
+ compilation.updateAsset(
26
+ file,
27
+ new compiler.webpack.sources.RawSource(prefix + source)
28
+ )
29
+ }
30
+ }
31
+ }
32
+ )
33
+ })
34
+ }
35
+ }
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
@@ -0,0 +1,2 @@
1
+ const store = { classArr:[], nodeArr:[], ormSettings:[] }
2
+ export {store}
@@ -0,0 +1,41 @@
1
+ import path from 'path'
2
+ import { MasqueradePlugin } from './plugin.js'
3
+
4
+
5
+ export default {
6
+ mode: 'development',
7
+ target: 'node',
8
+ entry: './src/project.ts',
9
+ devtool: 'source-map',
10
+ plugins: [
11
+ //other plugins...
12
+ new MasqueradePlugin() //this should be last
13
+ ],
14
+ module: {
15
+ rules: [
16
+ {
17
+ test: /\.tsx?$/,
18
+ use: ['ts-loader', 'masquerade-loader'],
19
+ exclude: /node_modules/,
20
+ },
21
+ ],
22
+ },
23
+ resolve: {
24
+ extensions: ['.tsx', '.ts', '.js'],
25
+ },
26
+ output: {
27
+ filename: 'bundle.js',
28
+ devtoolModuleFilenameTemplate: '[absolute-resource-path]', // Helps VS Code find original files
29
+ path: path.resolve(import.meta.dirname, 'dist'),
30
+ library: {
31
+ type: "module"
32
+ }
33
+ },
34
+ experiments: {
35
+ outputModule: true
36
+ },
37
+ resolveLoader: {
38
+ modules: ["node_modules", path.resolve(import.meta.dirname, 'loaders')]
39
+ }
40
+
41
+ };
@@ -0,0 +1,7 @@
1
+ # SQL Client Differences
2
+ - major differences in the find (postgres gives back a nested object, while sqlite is flat and manually nested)
3
+ - slight differences in the save
4
+ - slight differences in type mapping
5
+
6
+ anything internal (proxies and their state management data structures) is agnostic to sql clients.
7
+
@@ -0,0 +1,21 @@
1
+ import { familyAgeGenerator, familyNameGenerator } from "./miscFunctions.js"
2
+ import { Person, House } from "./testing-classes.js"
3
+
4
+ export function generateFamiliesAndHouses() {
5
+ for (let i = 0; i < 30; i++) {
6
+ const familySize = 3 + Math.floor(Math.random() * 7)
7
+ const [fatherName, motherName, ...childrenNames] = familyNameGenerator(familySize)
8
+ const [fatherAge, motherAge, ...childrenAges] = familyAgeGenerator(familySize)
9
+
10
+ const father = new Person(fatherName, fatherAge)
11
+ const mother = new Person(motherName, motherAge)
12
+ const children = []
13
+ for (const i in childrenNames) {
14
+ const child = new Person(childrenNames[i], childrenAges[i], father, mother)
15
+ children.push(child)
16
+ }
17
+ father.children.push(...children)
18
+ mother.children.push(...children)
19
+ new House(father, [father, mother, ...children])
20
+ }
21
+ }
@@ -0,0 +1,97 @@
1
+ export function jsonGenerator() {
2
+ return {
3
+ booleanField: true,
4
+ floatVal: 15.7,
5
+ someInt: 5,
6
+ stringArr: ['a', 'b', 'c']
7
+ }
8
+ }
9
+
10
+ const nameArrMale = [
11
+ "James",
12
+ "John",
13
+ "Robert",
14
+ "Michael",
15
+ "William",
16
+ "David",
17
+ "Richard",
18
+ "Joseph",
19
+ "Charles",
20
+ "Thomas",
21
+ "Daniel",
22
+ "Matthew",
23
+ "Anthony",
24
+ "Mark",
25
+ "Paul",
26
+ "Steven",
27
+ "Andrew",
28
+ "Kenneth",
29
+ "Joshua",
30
+ "Kevin"
31
+ ]
32
+
33
+ const nameArrFemale = [
34
+ "Mary",
35
+ "Patricia",
36
+ "Jennifer",
37
+ "Linda",
38
+ "Elizabeth",
39
+ "Barbara",
40
+ "Susan",
41
+ "Jessica",
42
+ "Sarah",
43
+ "Karen",
44
+ "Nancy",
45
+ "Lisa",
46
+ "Margaret",
47
+ "Betty",
48
+ "Sandra",
49
+ "Ashley",
50
+ "Kimberly",
51
+ "Emily",
52
+ "Donna",
53
+ "Michelle"
54
+ ]
55
+
56
+ const surnames = [
57
+ "Smith",
58
+ "Johnson",
59
+ "Williams",
60
+ "Brown",
61
+ "Jones",
62
+ "Garcia",
63
+ "Miller",
64
+ "Davis",
65
+ "Rodriguez",
66
+ "Martinez"
67
+ ]
68
+
69
+ export function arrayRandomPick(array) {
70
+ return array[Math.floor(Math.random() * array.length)]
71
+ }
72
+
73
+ export function familyNameGenerator(/**@type {number}*/ familySize) {
74
+ const familyNames = []
75
+ let firstName = arrayRandomPick(nameArrMale)
76
+ const surname = arrayRandomPick(surnames)
77
+ familyNames.push(`${firstName} ${surname}`)
78
+ firstName = arrayRandomPick(nameArrFemale)
79
+ familyNames.push(`${firstName} ${surname}`)
80
+
81
+ const childrenFirstNames = [...nameArrMale, ...nameArrFemale]
82
+ for (let i = 0; i < familySize - 2; i++) familyNames.push(`${arrayRandomPick(childrenFirstNames)} ${surname}`)
83
+ return familyNames
84
+ }
85
+
86
+ export function familyAgeGenerator(/**@type {number}*/ familySize) {
87
+ const fatherAge = 18 + Math.floor(Math.random() * 20)
88
+ const motherAge = 18 + Math.floor(Math.random() * 20)
89
+ const familyAges = [fatherAge, motherAge]
90
+ for (let i = 0; i < familySize - 2; i++) familyAges.push(1 + Math.floor(Math.random() * (Math.min(motherAge, fatherAge) - 18)))
91
+ return familyAges
92
+ }
93
+
94
+ export function validateUpdatedAt(updatedAt, currentTime) {
95
+ return updatedAt.getTime() - currentTime.getTime() >= 0
96
+ }
97
+
@@ -0,0 +1,254 @@
1
+
2
+ import test from 'node:test'
3
+ import assert from "node:assert"
4
+ import * as classes from './testing-classes.js'
5
+ import { resetPostgresDb, initORM, createConfigObj } from "./testInit.js"
6
+ import { sql } from '..//src/entity/find/where/whereArgsFunctions.js'
7
+ import { generateFamiliesAndHouses } from "./generationFuncs.js"
8
+ import { validateUpdatedAt } from "./miscFunctions.js"
9
+ import { OrmStore } from '../src/misc/ormStore.js'
10
+
11
+ const { House, Person, NonRelationalClass2 } = classes
12
+
13
+ const configObj = createConfigObj()
14
+ await resetPostgresDb(configObj.dbConnection)
15
+ await initORM(configObj, classes)
16
+ let dbChanges = OrmStore.store.dbChangesObj
17
+ generateFamiliesAndHouses()
18
+ for (let i = 0; i < 3; i++) new NonRelationalClass2()
19
+
20
+ const nonRelationalTest = await NonRelationalClass2.find({ where: { json: sql`->>'someInt' = '5'` } })
21
+ test('test 1 - find basics and change logging', async (t) => {
22
+
23
+ await t.test('find basics', async (t) => {
24
+
25
+ await t.test('find result length is correct', async () => {
26
+ assert.strictEqual(nonRelationalTest.length, 3)
27
+ })
28
+
29
+ await t.test('find returns correct typing', async () => {
30
+ assert.strictEqual(typeof nonRelationalTest[0].bigint, 'bigint')
31
+ assert.strictEqual(typeof nonRelationalTest[0].boolean, 'boolean')
32
+ assert.strictEqual(typeof nonRelationalTest[0].stringArr[0], 'string')
33
+ assert.strictEqual(typeof nonRelationalTest[0].id, 'number')
34
+ assert.strictEqual(typeof nonRelationalTest[0].json, 'object')
35
+ assert.strictEqual(typeof nonRelationalTest[0].jsonArr[0], 'object')
36
+ assert.ok(Array.isArray(nonRelationalTest[0].stringArr))
37
+ assert.ok(Array.isArray(nonRelationalTest[0].jsonArr))
38
+ })
39
+ })
40
+
41
+ await t.test('assignments and dbChanges', async (t) => {
42
+ const typeTestId = nonRelationalTest[0].id.toString()
43
+ nonRelationalTest[0].int = 157
44
+
45
+ const testInstanceChangeLog = dbChanges.NonRelationalClass2[typeTestId]
46
+
47
+ /** @type {any} */
48
+ let expectedVal = {
49
+ booleanField: false,
50
+ floatVal: 12.33,
51
+ someInt: 7,
52
+ stringArr: ['masquerade', 'orm', 'best', 'orm'],
53
+ unstructuredData: 'random data'
54
+ }
55
+
56
+ let currentTime
57
+
58
+ await t.test('non-relational non-arrays', async () => {
59
+ currentTime = new Date()
60
+ nonRelationalTest[0].boolean = false
61
+
62
+ assert.strictEqual(testInstanceChangeLog.boolean, false)
63
+ assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
64
+
65
+ currentTime = new Date()
66
+ nonRelationalTest[0].json = {
67
+ booleanField: false,
68
+ floatVal: 12.33,
69
+ someInt: 7,
70
+ stringArr: ['masquerade', 'orm']
71
+ }
72
+
73
+ nonRelationalTest[0].json.unstructuredData = 'random data'
74
+ nonRelationalTest[0].json.stringArr.push('best', 'orm')
75
+
76
+ assert.deepStrictEqual(testInstanceChangeLog.json, expectedVal)
77
+ assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
78
+ })
79
+
80
+ await t.test('non-relational arrays', async () => {
81
+ currentTime = new Date()
82
+ nonRelationalTest[0].stringArr = ['good', 'day']
83
+ nonRelationalTest[0].stringArr.push('sir')
84
+
85
+ assert.deepStrictEqual(testInstanceChangeLog.stringArr, ['good', 'day', 'sir'])
86
+ assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
87
+
88
+ currentTime = new Date()
89
+ nonRelationalTest[0].jsonArr.push({
90
+ someInt: 7,
91
+ booleanField: false,
92
+ floatVal: 7.7,
93
+ stringArr: ['hola', 'mundo']
94
+ })
95
+
96
+ expectedVal =
97
+ '[{"booleanField":true,"floatVal":15.7,"someInt":5,"stringArr":["a","b","c"]},{"someInt":7,"booleanField":false,"floatVal":7.7,"stringArr":["hola","mundo"]}]'
98
+
99
+ assert.deepStrictEqual(testInstanceChangeLog.jsonArr, expectedVal)
100
+ assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
101
+
102
+ currentTime = new Date()
103
+ nonRelationalTest[0].jsonArr[0].stringArr = ['someString']
104
+
105
+ expectedVal =
106
+ '[{"booleanField":true,"floatVal":15.7,"someInt":5,"stringArr":["someString"]},{"someInt":7,"booleanField":false,"floatVal":7.7,"stringArr":["hola","mundo"]}]'
107
+
108
+ assert.deepStrictEqual(testInstanceChangeLog.jsonArr, expectedVal)
109
+ assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
110
+ })
111
+ })
112
+
113
+ let undefinedTest = await House.find({ relations: { owner: true, tenants: true } })
114
+
115
+ const lastHouse = undefinedTest[undefinedTest.length - 1]
116
+ lastHouse.owner = undefined
117
+ lastHouse.tenants = undefined
118
+
119
+ undefinedTest = await House.find({
120
+ where: { id: lastHouse.id },
121
+ relations: { owner: true, tenants: true }
122
+ })
123
+
124
+ await t.test('assigning undefined values to relational props', async () => {
125
+ assert.strictEqual(undefinedTest[0].owner, undefined)
126
+ assert.deepStrictEqual(undefinedTest[0].tenants, [])
127
+ })
128
+
129
+ await t.test('presave new instances', async (t) => {
130
+ await t.test('new instance proxies communicate with dbChanges correctly', async () => {
131
+ const preSaveChanges = new NonRelationalClass2()
132
+ preSaveChanges.stringArr.push('please rewrite this in rust')
133
+ preSaveChanges.bigint = 12n
134
+
135
+ const preSaveChangesObj =
136
+ dbChanges.NonRelationalClass2[preSaveChanges.id.toString()]
137
+
138
+ assert.deepStrictEqual(
139
+ preSaveChangesObj.stringArr,
140
+ ['hello', 'world', 'please rewrite this in rust']
141
+ )
142
+ assert.deepStrictEqual(preSaveChangesObj.bigint, 12n)
143
+ })
144
+ })
145
+ })
146
+
147
+
148
+ test('test 2 - promises and instance logging', async (t) => {
149
+
150
+ await t.test('overwriting promises without loading', async (t) => {
151
+ let firstRelationalTest = (await House.find({ where: { id: 1 } }))[0]
152
+ const mrClean = (firstRelationalTest.owner = new Person('Mr Clean', 30))
153
+ firstRelationalTest.tenants = [mrClean, new Person('Mrs Clean', 24)]
154
+
155
+ firstRelationalTest = (await House.find({ where: { id: 1 } }))[0]
156
+ await initORM(configObj, classes)
157
+
158
+ firstRelationalTest = (
159
+ await House.find({
160
+ where: { id: 1 },
161
+ relations: { owner: true, tenants: true }
162
+ })
163
+ )[0]
164
+
165
+ await t.test('1-to-1 relational promise assignment', async () => {
166
+ assert.strictEqual(firstRelationalTest?.owner?.fullName, 'Mr Clean')
167
+ })
168
+
169
+ await t.test('1-to-many relational promise assignment', async () => {
170
+ assert.strictEqual(firstRelationalTest?.tenants?.length, 2)
171
+ assert.strictEqual(firstRelationalTest.tenants[0].fullName, 'Mr Clean')
172
+ assert.strictEqual(firstRelationalTest.tenants[1].fullName, 'Mrs Clean')
173
+ })
174
+ })
175
+
176
+ await t.test('check instance logging', async (t) => {
177
+ const original = (
178
+ await House.find({
179
+ where: { id: 2 },
180
+ relations: { owner: true, tenants: true }
181
+ })
182
+ )[0]
183
+
184
+ const sameInstance = (await House.find({ where: { id: 2 } }))[0]
185
+
186
+ await t.test('instance logging is working correctly', async () => {
187
+ assert.strictEqual(original === sameInstance, true)
188
+ })
189
+ })
190
+ })
191
+
192
+ test('test 3 - deletion', async (t) => {
193
+ let house = (await House.find({
194
+ relations:
195
+ {
196
+ owner:
197
+ { children: true },
198
+ tenants:
199
+ {
200
+ father: true,
201
+ mother: true
202
+ }
203
+ },
204
+ where: { id: 5 }
205
+ }))[0]
206
+
207
+ const father = house.owner
208
+ const childrenCount = father?.children.length
209
+ const tenantCount = house.tenants?.length
210
+ //@ts-ignore
211
+ let children = [...father?.children]
212
+ const firstChild = children.shift()
213
+
214
+ await t.test('deletion events - relational arrays', async () => {
215
+ await firstChild.delete()
216
+ //@ts-ignore
217
+ assert.strictEqual(childrenCount - father?.children.length === 1, true)
218
+ assert.strictEqual(father?.children.includes(firstChild), false)
219
+ })
220
+
221
+ await t.test('deletion events - 1-to-1 relation', async () => {
222
+ await father?.delete()
223
+ await House.find({})
224
+ await initORM(configObj, classes)
225
+
226
+ house = (await House.find({
227
+ relations:
228
+ {
229
+ owner:
230
+ { children: true },
231
+ tenants:
232
+ {
233
+ father: true,
234
+ mother: true,
235
+ children: true
236
+ }
237
+ },
238
+ where: { id: 5 }
239
+ }))[0]
240
+
241
+ const childrenIds = children.map(child => child.id)
242
+ house.tenants && tenantCount && assert.strictEqual(tenantCount - house.tenants?.length, 2)
243
+ assert.strictEqual(childrenIds.includes(firstChild.id), false)
244
+ for (const person of house.tenants ?? []) assert.strictEqual(person.father, undefined)
245
+ })
246
+ })
247
+
248
+ test.after(async () => {
249
+ await resetPostgresDb(configObj.dbConnection)
250
+ console.log('db reset')
251
+ // @ts-ignore
252
+ configObj.dbConnection.end()
253
+ })
254
+