masquerade-orm 0.8.2 → 0.8.3

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.
package/README.md CHANGED
@@ -37,21 +37,22 @@ npm install masquerade-orm
37
37
  ```
38
38
 
39
39
  # Features
40
- - **Effortless setup** - no ORM-specific structures; just use your classes.
41
- - **Zero schema planning** - tables and schema are generated automatically.
42
- - **Powerful IntelliSense** - confidently build complex queries with real-time IDE feedback when something’s wrong.
43
- - **Minimal memory usage** - one class instance per database row, minimizing memory usage and avoiding duplicates through smart state management.
44
- - **Optimized querying** - fewer queries through intelligent transaction grouping without sacrificing data integrity.
45
- - **Relational WHERE clauses** - easily write conditions that compare two columns within the same table or columns across different tables.
46
- - **Write complex WHERE conditions using a template-literal helper** - enabling expressive comparisons like >=, LIKE, object-property access, and even array element matching, all without cluttering your query code.
47
- - **SQL injection protection** - all queries are parameterized.
48
- - **Lightweight** - minimal dependencies.
49
- - **Strong typing even in JavaScript** - powered by JSDoc, no compile step required.
50
- - **Reduced data transfer size** - improves performance in client-server setups (not applicable for embedded databases like SQLite).
51
- - **Abstract and non-abstract inheritance** - enables the use of abstract classes, even in JavaScript.
40
+ - **Effortless setup** - No ORM-specific structures; just use your classes.
41
+ - **Zero schema planning** - Tables and schema are generated automatically.
42
+ - **Powerful IntelliSense** - Confidently build complex queries with real-time IDE guidance and warnings when something’s wrong.
43
+ - **Minimal memory usage** - One class instance per database row, minimizing memory usage and avoiding duplicates through smart state management.
44
+ - **Optimized querying** - Fewer queries through intelligent transaction grouping without sacrificing data integrity.
45
+ - **Expressive template-literal WHERE clauses** - Write complex, readable conditions such as LIKE, ≥, nested property access, array element matching (and more) by using IntelliSense-enabled tagged template literals. Any SQL WHERE logic can be expressed through this API.
46
+ - **Cross-column conditions** - Easily write WHERE clauses that compare two columns (within the same table or across joined tables).
47
+ - **Powerful relation capabilities** - Full support for eager & lazy loading, unidirectional / bidirectional / self-referencing relationships, and modifying associations even when they are not loaded.
48
+ - **SQL injection protection** - All queries are parameterized.
49
+ - **Minimal data transfer size** - Improves performance in client-server setups (not applicable for embedded databases like SQLite).
50
+ - **Soft + hard deletion support**
51
+ - **Abstract and non-abstract inheritance** - Enables the use of abstract classes, even in JavaScript.
52
+ - **Strong typing even in JavaScript** - Powered by JSDoc, no compile step required.
53
+ - **Smart schema cleanup** - Automatically detect and easily remove unused tables and columns, reducing database bloat and improving performance.
54
+ - **Lightweight** - Minimal dependencies.
52
55
  - **Combines the convenience of embedded SQLite with the strict typing of RDBMS**
53
- - **Eager and lazy relations**
54
- - **Unidirectional, bidirectional, and self-referenced relations**
55
56
 
56
57
 
57
58
  # Example Code Implementation
@@ -1,59 +1,59 @@
1
- #!/usr/bin/env node
2
-
3
- import ts from "typescript"
4
- import fs from "fs"
5
- import path from "path"
6
- import { createSourceFile, SyntaxKind, ScriptKind, ScriptTarget } from "typescript"
7
- import { nodeArr2ClassDict } from "../src/ORM/bootOrm.js"
8
-
9
- const configPath = ts.findConfigFile(
10
- "./",
11
- ts.sys.fileExists,
12
- "tsconfig.json"
13
- )
14
-
15
- if (!configPath) {
16
- throw new Error("tsconfig.json not found")
17
- }
18
-
19
- const configFile = ts.readConfigFile(
20
- configPath,
21
- ts.sys.readFile
22
- )
23
-
24
- const parsed = ts.parseJsonConfigFileContent(
25
- configFile.config,
26
- ts.sys,
27
- "./"
28
- )
29
-
30
- const program = ts.createProgram({
31
- rootNames: parsed.fileNames,
32
- options: parsed.options
33
- })
34
-
35
- const classNodesArr = []
36
-
37
- for (const sourceFile of program.getSourceFiles()) {
38
- if (sourceFile.isDeclarationFile) continue
39
- const fileText = sourceFile.getFullText()
40
- const fileNodesArr = createSourceFile('', fileText, ScriptTarget.Latest, true, ScriptKind.TSX).statements
41
-
42
- for (const node of fileNodesArr) {
43
- //@ts-ignore
44
- if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && node.heritageClauses)
45
- classNodesArr.push(node)
46
- }
47
- }
48
-
49
- const classDict = nodeArr2ClassDict(classNodesArr)
50
- const filePath = path.join(
51
- process.cwd(),
52
- "ormTypeScriptSetup.js"
53
- )
54
- const content = `export function UniversalTsSetup() {globalThis.masqueradeClassDict_ = ${JSON.stringify(classDict)};\n}`
55
- fs.writeFileSync(filePath, content, "utf8")
56
-
57
-
58
-
59
-
1
+ #!/usr/bin/env node
2
+
3
+ import ts from "typescript"
4
+ import fs from "fs"
5
+ import path from "path"
6
+ import { createSourceFile, SyntaxKind, ScriptKind, ScriptTarget } from "typescript"
7
+ import { nodeArr2ClassDict } from "../src/ORM/bootOrm.js"
8
+
9
+ const configPath = ts.findConfigFile(
10
+ "./",
11
+ ts.sys.fileExists,
12
+ "tsconfig.json"
13
+ )
14
+
15
+ if (!configPath) {
16
+ throw new Error("tsconfig.json not found")
17
+ }
18
+
19
+ const configFile = ts.readConfigFile(
20
+ configPath,
21
+ ts.sys.readFile
22
+ )
23
+
24
+ const parsed = ts.parseJsonConfigFileContent(
25
+ configFile.config,
26
+ ts.sys,
27
+ "./"
28
+ )
29
+
30
+ const program = ts.createProgram({
31
+ rootNames: parsed.fileNames,
32
+ options: parsed.options
33
+ })
34
+
35
+ const classNodesArr = []
36
+
37
+ for (const sourceFile of program.getSourceFiles()) {
38
+ if (sourceFile.isDeclarationFile) continue
39
+ const fileText = sourceFile.getFullText()
40
+ const fileNodesArr = createSourceFile('', fileText, ScriptTarget.Latest, true, ScriptKind.TSX).statements
41
+
42
+ for (const node of fileNodesArr) {
43
+ //@ts-ignore
44
+ if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && node.heritageClauses)
45
+ classNodesArr.push(node)
46
+ }
47
+ }
48
+
49
+ const classDict = nodeArr2ClassDict(classNodesArr)
50
+ const filePath = path.join(
51
+ process.cwd(),
52
+ "ormTypeScriptSetup.js"
53
+ )
54
+ const content = `export function UniversalTsSetup() {globalThis.masqueradeClassDict_ = ${JSON.stringify(classDict)};\n}`
55
+ fs.writeFileSync(filePath, content, "utf8")
56
+
57
+
58
+
59
+
package/docs/deletion.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Deleting Instances from the Database
2
2
 
3
- ### Soft Deletion
3
+ ## Soft Deletion
4
4
  The ORM does not support soft deletion by default. To implement soft deletion, just create an abstract class like so:
5
5
 
6
6
  **TypeScript**
@@ -69,7 +69,7 @@ const softDelUser = new SoftDeletableUser('JohnDoe', 'JohnDoe@gmail.com', 'hashe
69
69
 
70
70
  **Note:** Unlike `Entity`, the `SoftDel` classes need to be passed into the `ORM.boot` method that is being called to init the ORM.
71
71
 
72
- ### Hard Deletion
72
+ ## Hard Deletion
73
73
 
74
74
  ```ts
75
75
  import { sql } from "masquerade-orm"
@@ -91,7 +91,7 @@ The `delete` method hard-deletes the instance from the database; the instance ca
91
91
  </div>
92
92
 
93
93
 
94
- ## Pre-Deletion Step
94
+ ## Pre-Deletion Step for Hard Deletion
95
95
 
96
96
  ### When is a pre-deletion step required?
97
97
  A pre-deletion step is required when the class instance has **dependents**.
package/docs/find.md CHANGED
@@ -13,7 +13,7 @@ The three optional fields are:
13
13
 
14
14
  - relations
15
15
  - where
16
- - relationalWhere
16
+ - templateWhere
17
17
 
18
18
 
19
19
 
@@ -138,6 +138,16 @@ await User.find({
138
138
  // 'where' conditions in an easy-to-read and easy-to-write manner.
139
139
  ```
140
140
 
141
+ <!-- **Equivalent Alternative Syntax:**
142
+ ```js
143
+ await User.find({
144
+ where: {
145
+ donations: (donations) => sql`1200 < ${donations} AND ${donations} < 5700`,
146
+ createdAt: (createdAt) => sql`${twoYearsAgo} <= ${createdAt} AND ${createdAt} <= ${oneYearAgo}`
147
+ }
148
+ })
149
+ ``` -->
150
+
141
151
  ### Using the `sql` function to create a `LIKE` `WHERE` condition
142
152
  ```js
143
153
  import { sql } from "masquerade-orm"
@@ -176,27 +186,27 @@ const completedOrders = await Order.find({
176
186
  - **Note:** for SQL-client specific guide for writing `WHERE` conditions involving JSON and array data, go to the bottom of this page or click **[here](https://github.com/MasqueradeORM/MasqueradeORM/blob/master/docs/find.md#array-and-json-where-conditions-guide)**.
177
187
 
178
188
 
179
- ## The `relationalWhere` Field:
189
+ ## The `templateWhere` Field:
180
190
 
181
191
  ```js
182
192
  import { sql } from "masquerade-orm"
183
193
 
184
194
  // Finds users that have at least one chat that contains at least one message whose sender's username is 'Glory2Christ'.
185
195
  await User.find({
186
- relationalWhere: (user) => sql`${user.chats.messages.sender.username} = 'Glory2Christ'`
196
+ templateWhere: (user) => sql`${user.chats.messages.sender.username} = 'Glory2Christ'`
187
197
  })
188
198
  ```
189
199
 
190
200
  ```js
191
201
  import { sql } from "masquerade-orm"
192
202
 
193
- // Identical to the previous example, but here the relational where is called from a different scope.
203
+ // Identical to the previous example, but here the 'templateWhere' is called from a different scope.
194
204
  // note: the field has an underscore, to prevent any (rather impossible) name collisions.
195
205
 
196
206
  await User.find({
197
207
  where: {
198
208
  chats: {
199
- relationalWhere_: (chat) => sql`${chat.messages.sender.username} = 'Glory2Christ'`,
209
+ templateWhere_: (chat) => sql`${chat.messages.sender.username} = 'Glory2Christ'`,
200
210
  // can be combined with regular 'where' conditions - below is valid code
201
211
  // chatName: 'The History of Orthodoxy'
202
212
  }
package/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
-
2
- export {ORM} from "./src/ORM/ORM.js"
3
- export {Entity} from './src/entity/entity'
4
- export {DbManager} from "./src/ORM/DbManager"
5
- export {sql, AND, OR} from "./src/entity/find/where/whereArgsFunctions"
6
- export {MasqueradePlugin} from "./src/webpack/plugin"
7
- export {Unique, integer, OrmConfigObj} from './src/misc/types'
1
+
2
+ export {ORM} from "./src/ORM/ORM"
3
+ export {Entity} from './src/entity/entity'
4
+ export {DbManager} from "./src/ORM/DbManager"
5
+ export {sql, AND, OR} from "./src/entity/find/where/whereArgsFunctions"
6
+ export {MasqueradePlugin} from "./src/webpack/plugin"
7
+ export {Unique, integer, OrmConfigObj} from './src/misc/types'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "masquerade-orm",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Lightweight ORM compatible with SQLite and Postgresql in Node.js",
5
5
  "keywords": [
6
6
  "orm",
@@ -20,8 +20,8 @@ export class ChangeLogger {
20
20
  static async save() {
21
21
  const dbChanges = OrmStore.store.dbChangesObj
22
22
  if (!Object.keys(dbChanges).length || ChangeLogger.currentlySaving) return
23
- // ChangeLogger.currentlySaving = true
24
-
23
+
24
+ ChangeLogger.currentlySaving = true
25
25
  let paramIndex = 1
26
26
  const { sqlClient, dbConnection } = OrmStore.store
27
27
  const deletedInstancesArr = dbChanges.deletedInstancesArr
@@ -11,7 +11,7 @@ import { aliasedFindWiki2QueryRes, parseFindWiki, destructureAndValidateArg } fr
11
11
  import { deproxifyScopeProxy, classWiki2ScopeProxy } from "./find/scopeProxies.js"
12
12
  import { postgresCreateProxyArray } from "./find/sqlClients/postgresFuncs.js"
13
13
  import { sqliteCreateProxyArray } from "./find/sqlClients/sqliteFuncs.js"
14
- import { mergeRelationalWhereScope } from "./find/where/relationalWhere.js"
14
+ import { mergeTemplateWhereScope } from "./find/where/templateWhere.js"
15
15
  import { mergeWhereScope } from "./find/where/where.js"
16
16
 
17
17
  /**
@@ -68,11 +68,11 @@ export class Entity {
68
68
 
69
69
  let classWiki = classWikiDict[this.name]
70
70
  if (!classWiki) throw new Error(`The class '${this.name}' has not been included in the ORM boot method.`)
71
- const [relationsArg, whereArg, relationalWhereArg] = destructureAndValidateArg(findObj)
71
+ const [relationsArg, whereArg, templateWhereArg] = destructureAndValidateArg(findObj)
72
72
  let findWiki
73
73
  const baseProxyMap = classWiki2ScopeProxy(classWiki)
74
74
  if (whereArg) mergeWhereScope(baseProxyMap, whereArg)
75
- if (relationalWhereArg) findWiki = mergeRelationalWhereScope(baseProxyMap, relationalWhereArg)
75
+ if (templateWhereArg) findWiki = mergeTemplateWhereScope(baseProxyMap, templateWhereArg)
76
76
  findWiki = deproxifyScopeProxy(baseProxyMap)
77
77
 
78
78
  const [aliasedFindMap, joinStatements, whereObj] = parseFindWiki(findWiki)
@@ -4,14 +4,14 @@ import { rowObj2InstanceProxy } from "../../proxies/instanceProxy.js"
4
4
  import { createRelationalArrayProxy } from "../../proxies/relationalArrayProxy.js"
5
5
  import { generateQueryStrWithCTEs } from "./queryBuilder.js"
6
6
  import { junctionJoin, parentJoin } from "./joins.js"
7
- import { relationalWhereFuncs2Statements } from "./where/relationalWhere.js"
7
+ import { relationalWhereFuncs2Statements } from "./where/templateWhere.js"
8
8
  import { whereValues2Statements } from "./where/where.js"
9
9
  import { OrmStore } from "../../misc/ormStore.js"
10
10
 
11
11
  export const proxyType = Symbol('proxyType')
12
12
 
13
13
  export function destructureAndValidateArg(findObj) {
14
- let { relations: eagerLoad, where, relationalWhere } = findObj
14
+ let { relations: eagerLoad, where, templateWhere } = findObj
15
15
 
16
16
  if (eagerLoad) {
17
17
  const type = getType(eagerLoad)
@@ -25,11 +25,11 @@ export function destructureAndValidateArg(findObj) {
25
25
  else if (Object.keys(where).length === 0) where = undefined
26
26
  }
27
27
 
28
- if (relationalWhere) {
29
- const type = getType(relationalWhere)
30
- if (type !== "function") throw new Error(`\nInvalid value in the 'relationalWhere' field of the 'find' function's argument. Expected a function.`)
28
+ if (templateWhere) {
29
+ const type = getType(templateWhere)
30
+ if (type !== "function") throw new Error(`\nInvalid value in the 'templateWhere' field of the 'find' function's argument. Expected a function.`)
31
31
  }
32
- return [eagerLoad, where, relationalWhere]
32
+ return [eagerLoad, where, templateWhere]
33
33
  }
34
34
 
35
35
  export function removeRelationFromUnusedRelations(classMap, key) {
@@ -63,7 +63,7 @@ export function parseFindWiki(findWiki, aliasBase = 'a', aliasArr = [], joinStat
63
63
  returnedWiki.junctions[key] = parseFindWiki(joinedTable, aliasBase, aliasArr, joinStatements, whereObj, selectArr)[0]
64
64
  }
65
65
  }
66
- if (returnedWiki.relationalWhere) relationalWhereFuncs2Statements(returnedWiki, whereObj)
66
+ if (returnedWiki.templateWhere) relationalWhereFuncs2Statements(returnedWiki, whereObj)
67
67
  if (returnedWiki.where) whereValues2Statements(returnedWiki, whereObj)
68
68
  return [returnedWiki, joinStatements, whereObj, selectArr]
69
69
  }
@@ -40,7 +40,7 @@ export function classWiki2ScopeProxy(classWiki) {
40
40
  || key === "uncalledJunctions_"
41
41
  || key === "junctions_"
42
42
  || key === "where_"
43
- || key === "relationalWhere_"
43
+ || key === "templateWhere_"
44
44
  || key === "isArray_"
45
45
  ) return target[key]
46
46
  else if (key === proxyType) return 'categorizingProxy'
@@ -56,5 +56,5 @@ export function findPropOnScopeProxy(scopeProxy, key, rootClassName) {
56
56
  else if (scopeProxy.uncalledJunctions_ && scopeProxy.uncalledJunctions_[key]) return [scopeProxy.uncalledJunctions_[key], scopeProxy, "uncalledJunctions_"]
57
57
  else if (scopeProxy.junctions_ && scopeProxy.junctions_[key]) return [scopeProxy.junctions_[key], scopeProxy, "junctions_"]
58
58
  else if (scopeProxy.parent_) return findPropOnScopeProxy(scopeProxy.parent_, key, rootClassName)
59
- else throw new Error(`\n'${key}' is not a valid property of class ${rootClassName}. Please fix the find function's argument. \nhint: use intellisense by pressing CNTRL + space to see all viable options.`)
59
+ else throw new Error(`\n'${key}' is not a valid property of class ${rootClassName}. Please fix the find function's argument. \nhint: use IntelliSense.`)
60
60
  }
@@ -5,7 +5,7 @@ import { proxyType, removeRelationFromUnusedRelations } from "../find.js"
5
5
  import { deproxifyScopeProxy, findPropOnScopeProxy, classWiki2ScopeObj } from "../scopeProxies.js"
6
6
 
7
7
  export function relationalWhereFuncs2Statements(mapObj, whereObj, queryStr = ``) {
8
- const relationalWhereFunc = mapObj.relationalWhere
8
+ const relationalWhereFunc = mapObj.templateWhere
9
9
  let AliasObj4func = createFullAndFlatAliasObj(mapObj)
10
10
  const sqlWhereObj = relationalWhereFunc(AliasObj4func)
11
11
  while (sqlWhereObj.params.length + sqlWhereObj.strings.length > 0) {
@@ -49,24 +49,24 @@ function createFullAndFlatAliasObj(mapObj, fullFlatAliasObj = {}) {
49
49
  return fullFlatAliasObj
50
50
  }
51
51
 
52
- export function mergeRelationalWhereScope(proxyMap, relationalWhereFunc) {
52
+ export function mergeTemplateWhereScope(proxyMap, relationalWhereFunc) {
53
53
  if (typeof relationalWhereFunc !== "function") throw new Error(`Relational where expects a function.`)
54
54
  let mapObj = deproxifyScopeProxy(proxyMap)
55
55
  const classWiki = OrmStore.store.classWikiDict[mapObj.className_]
56
- const relationalWhereMapProxy = createRelationalWhereProxy(mapObj, classWiki)
56
+ const relationalWhereMapProxy = createTemplateWhereProxy(mapObj, classWiki)
57
57
  relationalWhereFunc(relationalWhereMapProxy)
58
58
  mapObj = deproxifyScopeProxy(relationalWhereMapProxy)
59
- mapObj.relationalWhere_ = relationalWhereFunc
60
- return reproxyMapObjPostRelationalWhere(mapObj, classWiki)
59
+ mapObj.templateWhere_ = relationalWhereFunc
60
+ return reproxyWikiPostTemplateWhere(mapObj, classWiki)
61
61
  }
62
62
 
63
- function reproxyMapObjPostRelationalWhere(mapObj, classWiki) {
64
- if (mapObj.parent_) mapObj.parent_ = reproxyMapObjPostRelationalWhere(mapObj.parent_, classWiki.parent)
63
+ function reproxyWikiPostTemplateWhere(mapObj, classWiki) {
64
+ if (mapObj.parent_) mapObj.parent_ = reproxyWikiPostTemplateWhere(mapObj.parent_, classWiki.parent)
65
65
 
66
66
  if (mapObj.junctions_) {
67
67
  const mapRelations = mapObj.junctions_
68
68
  for (const key of Object.keys(mapRelations))
69
- mapRelations[key] = reproxyMapObjPostRelationalWhere(mapRelations[key], classWiki.junctions[key])
69
+ mapRelations[key] = reproxyWikiPostTemplateWhere(mapRelations[key], classWiki.junctions[key])
70
70
  }
71
71
 
72
72
  const proxy = new Proxy(mapObj, {
@@ -78,7 +78,7 @@ function reproxyMapObjPostRelationalWhere(mapObj, classWiki) {
78
78
  || key === "uncalledJunctions_"
79
79
  || key === "junctions_"
80
80
  || key === "where_"
81
- || key === "relationalWhere_"
81
+ || key === "templateWhere_"
82
82
  || key === "isArray_"
83
83
  ) return target[key]
84
84
  else if (key === "raw_") return target
@@ -89,20 +89,20 @@ function reproxyMapObjPostRelationalWhere(mapObj, classWiki) {
89
89
  return proxy
90
90
  }
91
91
 
92
- export function createRelationalWhereProxy(mapObj, classWiki) {
92
+ export function createTemplateWhereProxy(mapObj, classWiki) {
93
93
  if (classWiki.parent) {
94
94
  let parent
95
95
  if (!mapObj.parent_) parent = classWiki2ScopeObj(classWiki)
96
96
  else parent = mapObj.parent_
97
97
  const parentOrmMap = classWiki.parent
98
- mapObj.parent_ = createRelationalWhereProxy(parent, parentOrmMap)
98
+ mapObj.parent_ = createTemplateWhereProxy(parent, parentOrmMap)
99
99
  }
100
100
 
101
101
  if (mapObj.junctions_) {
102
102
  const mapRelations = mapObj.junctions_
103
103
  for (const key of Object.keys(mapRelations)) {
104
104
  const relationOrmMap = classWiki.junctions[key]
105
- mapRelations[key] = createRelationalWhereProxy(mapRelations[key], relationOrmMap)
105
+ mapRelations[key] = createTemplateWhereProxy(mapRelations[key], relationOrmMap)
106
106
  }
107
107
  }
108
108
 
@@ -115,28 +115,28 @@ export function createRelationalWhereProxy(mapObj, classWiki) {
115
115
  || key === "uncalledJunctions_"
116
116
  || key === "junctions_"
117
117
  || key === "where_"
118
- || key === "relationalWhere_"
118
+ || key === "templateWhere_"
119
119
  || key === "isArray_"
120
120
  ) return target[key]
121
121
  else if (key === "raw_") return target
122
122
  else if (key === proxyType) return 'relationalWhereProxy'
123
- else return findPropOnRelationalWhereMapProxy(target, key, classWiki)
123
+ else return findPropOnTemplateWhereProxy(target, key, classWiki)
124
124
  }
125
125
  })
126
126
  return proxy
127
127
  }
128
128
 
129
- export function findPropOnRelationalWhereMapProxy(mapObj, key, classWiki) {
129
+ export function findPropOnTemplateWhereProxy(mapObj, key, classWiki) {
130
130
  if (mapObj.uncalledJunctions_[key]) {
131
131
  const relation = mapObj.uncalledJunctions_[key]
132
132
  const formattedRelationObj = classWiki2ScopeObj(relation)
133
133
  removeRelationFromUnusedRelations(mapObj, key)
134
134
  const proxyRelations = mapObj.junctions_ ??= {}
135
- proxyRelations[key] = createRelationalWhereProxy(formattedRelationObj, classWiki.junctions[key])
135
+ proxyRelations[key] = createTemplateWhereProxy(formattedRelationObj, classWiki.junctions[key])
136
136
  return proxyRelations[key]
137
137
  }
138
138
  else if (mapObj.columns_[key]) return mapObj.columns_[key]
139
139
  else if (mapObj.junctions_[key]) return mapObj.junctions_[key]
140
- else if (mapObj.parent_) return findPropOnRelationalWhereMapProxy(mapObj.parent_, key, classWiki.parent)
141
- else throw new Error(`\n'${key}' is not a valid property of class ${classWiki.className}. Please fix the find function's relationalWhere. \nhint: use intellisense by pressing CNTRL + space to see all viable options.`)
140
+ else if (mapObj.parent_) return findPropOnTemplateWhereProxy(mapObj.parent_, key, classWiki.parent)
141
+ else throw new Error(`\n'${key}' is not a valid property of class ${classWiki.className}. Please fix the find function's templateWhere. \nhint: use intellisense by pressing CNTRL + space to see all viable options.`)
142
142
  }
@@ -4,7 +4,7 @@ import { array2String, getType, nonSnake2Snake } from "../../../misc/miscFunctio
4
4
  import { OrmStore } from "../../../misc/ormStore.js"
5
5
  import { removeRelationFromUnusedRelations } from "../find.js"
6
6
  import { classWiki2ScopeProxy } from "../scopeProxies.js"
7
- import { mergeRelationalWhereScope } from "./relationalWhere.js"
7
+ import { mergeTemplateWhereScope } from "./templateWhere.js"
8
8
  /**@typedef {import('../../../misc/classes').AndArray} AndArray */
9
9
  /**@typedef {import('../../../misc/classes').AndArray} OrArray */
10
10
 
@@ -209,7 +209,7 @@ export function mergeWhereScope(proxyMap, whereObj) {
209
209
  for (const [key, whereVal] of entries) {
210
210
 
211
211
  if (key === "_relationalWhere") {
212
- proxyMap = mergeRelationalWhereScope(proxyMap, whereVal)
212
+ proxyMap = mergeTemplateWhereScope(proxyMap, whereVal)
213
213
  continue
214
214
  }
215
215
 
@@ -236,8 +236,8 @@ export function mergeWhereScope(proxyMap, whereObj) {
236
236
  }
237
237
 
238
238
  const passedMap = classMap.junctions_[key]
239
- if (whereValType === "function") passedMap.relationalWhere_ = [whereVal]
240
- else if (whereValType === "array") passedMap.relationalWhere_ = whereVal
239
+ if (whereValType === "function") passedMap.templateWhere_ = [whereVal]
240
+ else if (whereValType === "array") passedMap.templateWhere_ = whereVal
241
241
  else mergeWhereScope(passedMap, whereVal)
242
242
  }
243
243
  }
@@ -1,5 +1,7 @@
1
1
  /**@typedef {import('./types').PrimitivesNoNull} PrimitivesNoNull*/
2
2
 
3
+ import { OrmStore } from './ormStore.js'
4
+
3
5
 
4
6
  /**
5
7
  * @template T
@@ -51,13 +53,65 @@ export class Alias {
51
53
 
52
54
 
53
55
  export class LazyPromise {
54
- constructor(className) {
55
- this.promise =
56
- className
56
+ constructor(instance, property, promiseType, executor) {
57
+ // if (typeof executor !== 'function') {
58
+ // throw new TypeError(
59
+ // `LazyPromise executor must be a function, got ${typeof executor}`
60
+ // )
61
+ // }
62
+ this.instanceContext = {
63
+ instanceId: instance.id,
64
+ instanceClass: instance.constructor.name,
65
+ property,
66
+ promiseType
67
+ }
68
+ this._executor = executor
69
+ this._promise = null
70
+ }
71
+
72
+ _getPromise() {
73
+ if (!this._promise) {
74
+ this._promise = new Promise((resolve, reject) => {
75
+ try {
76
+ this._executor(resolve, reject)
77
+ } catch (err) {
78
+ reject(err)
79
+ }
80
+ })
81
+ }
82
+ return this._promise
83
+ }
57
84
 
85
+ then(onFulfilled, onRejected) {
86
+ return this._getPromise().then(onFulfilled, onRejected)
87
+ }
88
+
89
+ catch(onRejected) {
90
+ return this._getPromise().catch(onRejected)
91
+ }
92
+
93
+ finally(onFinally) {
94
+ return this._getPromise().finally(onFinally)
58
95
  }
59
96
 
60
97
  toString() {
61
- return `Promise<${this.promise}>`
98
+ return `Promise<${this.instanceContext.promiseType}>`
99
+ }
100
+
101
+ push(...items) {
102
+ const { instanceClass, instanceId, property, promiseType } = this.instanceContext
103
+ const cleanedType = promiseType.endsWith('[]') ? promiseType.slice(0, -2) : promiseType
104
+ const addedIds = []
105
+ for (const item of items) {
106
+ if (item.constructor.name !== cleanedType) throw new Error(`${item} is of type ${item.constructor.name} but must be of type ${cleanedType}.`)
107
+ addedIds.push(item.id)
108
+ }
109
+
110
+ const changesObj = OrmStore.store.dbChangesObj
111
+ changesObj[instanceClass] ??= {}
112
+ const instanceChangeObj = changesObj[instanceClass][instanceId] ??= {}
113
+ const relationsLogger = instanceChangeObj[property] ??= { added: [], removed: [] }
114
+ relationsLogger.added.push(...addedIds)
62
115
  }
63
- }
116
+
117
+ }
@@ -12,7 +12,10 @@ export class OrmStore {
12
12
  }
13
13
 
14
14
  static getClassWiki(instanceClass) {
15
+ if (typeof instanceClass === 'string')
15
16
  return this.store.classWikiDict[instanceClass]
17
+
18
+ return this.store.classWikiDict[instanceClass.constructor.name]
16
19
  }
17
20
  }
18
21
 
@@ -204,7 +204,7 @@ type NonRelationsProperties<T> = Exclude<keyof T, RelationsProperties<T>>
204
204
  export type FindObj<T> = {
205
205
  relations?: Partial<RelationsOnly<T>>
206
206
  where?: WhereProperties<T>
207
- relationalWhere?: sqlArrowFnTable<T> | (sqlArrowFn<T> | null)[] | null
207
+ templateWhere?: sqlArrowFnTable<T> | (sqlArrowFn<T> | null)[] | null
208
208
  }
209
209
 
210
210
  // ---------------------------------------------------------
@@ -455,7 +455,7 @@ type ArrColumnsRawParams =
455
455
  // export type FindObj<T> = {
456
456
  // relations?: Partial<RelationsOnly<T>>
457
457
  // where?: WhereProperties<T>
458
- // relationalWhere?: sqlArrowFn<T> | sqlArrowFn<T>[]
458
+ // templateWhere?: sqlArrowFn<T> | sqlArrowFn<T>[]
459
459
  // }
460
460
 
461
461
  // // ---------------------------------------------------------
@@ -65,8 +65,8 @@ export function rowObj2InstanceProxy(resultObj, findWiki, Entities) {
65
65
  while (!currentWiki.uncalledJunctions[property]) currentWiki = currentWiki.parent
66
66
  const uncalledJunctionObj = currentWiki.uncalledJunctions[property]
67
67
  const nameOfMapWithJunction = uncalledJunctionObj.className
68
- const promiseOf = uncalledJunctionObj.isArray ? nameOfMapWithJunction + `[]` : nameOfMapWithJunction
69
- instance[property] = new LazyPromise(promiseOf)
68
+ const promiseType = uncalledJunctionObj.isArray ? nameOfMapWithJunction + `[]` : nameOfMapWithJunction
69
+ instance[property] = new LazyPromise(instance, property, promiseType, (resolve, reject) => promiseExecutor(instance, property, resolve, reject))
70
70
  }
71
71
  }
72
72
 
@@ -76,43 +76,57 @@ export function rowObj2InstanceProxy(resultObj, findWiki, Entities) {
76
76
  }
77
77
  }
78
78
 
79
- export function insertProxyIntoEntityMap(proxy, entityMap) {
80
- entityMap.set(proxy.id, new WeakRef(proxy))
81
- ORM[FinalizationRegistrySymb].register(proxy, [proxy.constructor.name, proxy.id])
82
- }
83
-
84
- export function instanceProxyGetHandler(target, key, classWiki) {
85
- const val = target[key]
86
- if (!(val instanceof LazyPromise)) return val
87
-
79
+ function promiseExecutor(target, key, resolve, reject) {
80
+ if (ChangeLogger.scheduledFlush) ChangeLogger.save().then()
88
81
  const { sqlClient, dbConnection } = OrmStore.store
89
- const [classification, joinedClassMap, mapWithProp] = getPropertyClassification(key, classWiki)
90
- let joinedTable
91
-
92
- if (classification === "Join") joinedTable = classWiki.junctions[key]
93
- else joinedTable = mapWithProp.junctions[key]
94
-
95
- const isArrayOfInstances = joinedClassMap.isArray
82
+ const classWiki = OrmStore.getClassWiki(target)
83
+ const [classification, joinedClassWiki, mapWithProp] = getPropertyClassification(key, classWiki)
84
+ const isArrayOfInstances = joinedClassWiki.isArray
96
85
 
97
86
  let queryStr = `SELECT entity.* FROM ${nonSnake2Snake(mapWithProp.className)}___${nonSnake2Snake(key)}_jt jt` +
98
- ` LEFT JOIN ${nonSnake2Snake(joinedClassMap.className)} entity ON jt.joined_id = entity.id WHERE jt.joining_id = `
87
+ ` LEFT JOIN ${nonSnake2Snake(joinedClassWiki.className)} entity ON jt.joined_id = entity.id WHERE jt.joining_id = `
99
88
  queryStr += sqlClient === "postgresql" ? `$1` : `?`
100
89
 
101
90
  let queryFunc
102
91
  if (sqlClient === "postgresql") queryFunc = (queryStr, id) => dbConnection.query(queryStr, [id])
103
92
  else queryFunc = (queryStr, id) => dbConnection.prepare(queryStr).all(id)
104
93
 
105
- let promise
106
94
  try {
107
- let queryRes = queryFunc(queryStr, target.id)
108
- promise = createLazyPromise(target, key, queryRes, mapWithProp.junctions[key], isArrayOfInstances, sqlClient)
109
- promise.then(res => target[key] = res)
95
+ Promise.resolve(queryFunc(queryStr, target.id))
96
+ .then(rows => {
97
+ const { className, columns, junctions } = joinedClassWiki
98
+ const findWiki = { className, columns, uncalledJunctions: junctions }
99
+ let currentWiki = joinedClassWiki
100
+ let currentScopedMap = findWiki
101
+ while (currentWiki.parent) {
102
+ const { className, columns, junctions } = currentWiki.parent
103
+ currentScopedMap.parent = { className, columns, uncalledJunctions: junctions }
104
+ currentScopedMap = currentScopedMap.parent
105
+ currentWiki = currentWiki.parent
106
+ }
107
+
108
+ const proxyArr = []
109
+ for (const row of rows) {
110
+ const rowWithCamelCasedProps = Object.fromEntries(Object.entries(row).map(([key, val]) => [snake2Pascal(key, true), val]))
111
+ proxyArr.push(rowObj2InstanceProxy(rowWithCamelCasedProps, findWiki, OrmStore.store.entities))
112
+ }
113
+
114
+ if (isArrayOfInstances) target[key] = createRelationalArrayProxy(target, key, proxyArr, classWiki.className)
115
+ else target[key] = proxyArr[0]
116
+
117
+ resolve(target[key])
118
+ })
119
+ .catch(reject)
110
120
  }
111
- catch (e) {
112
- coloredBackgroundConsoleLog(`Lazy loading failed. ${e}\n`, `failure`)
121
+ catch (err) {
122
+ coloredBackgroundConsoleLog(`Lazy loading failed. ${err}`, 'failure')
123
+ reject(err)
113
124
  }
125
+ }
114
126
 
115
- return promise
127
+ export function insertProxyIntoEntityMap(proxy, entityMap) {
128
+ entityMap.set(proxy.id, new WeakRef(proxy))
129
+ ORM[FinalizationRegistrySymb].register(proxy, [proxy.constructor.name, proxy.id])
116
130
  }
117
131
 
118
132
  export function instanceProxySetHandler(target, key, value, eventListenersObj, classWiki) {
@@ -356,57 +370,6 @@ export function createLazyLoadQueryStr(property, classWiki) {
356
370
  }
357
371
 
358
372
 
359
- export function createLazyPromise(target, key, queryRes, classWiki, isArrayOfInstances, client) {
360
- return new Promise(async (resolve) => {
361
- let resultArr
362
- try {
363
- if (client === "postgresql") resultArr = (await queryRes).rows
364
- else resultArr = queryRes
365
-
366
- if (!resultArr.length) {
367
- if (isArrayOfInstances) {
368
- target[key] = []
369
- resolve([])
370
- }
371
- else {
372
- target[key] = undefined
373
- resolve(undefined)
374
- }
375
- }
376
-
377
- const { className, columns, junctions } = classWiki
378
- const findWiki = { className, columns, uncalledJunctions: junctions }
379
- let currentWiki = classWiki
380
- let currentScopedMap = findWiki
381
- while (currentWiki.parent) {
382
- const { className, columns, junctions } = currentWiki.parent
383
- currentScopedMap.parent = { className, columns, uncalledJunctions: junctions }
384
- currentScopedMap = currentScopedMap.parent
385
- currentWiki = currentWiki.parent
386
- }
387
-
388
- const proxyArr = []
389
- for (const row of resultArr) {
390
- const rowWithCamelCasedProps = Object.fromEntries(Object.entries(row).map(([key, val]) => [snake2Pascal(key, true), val]))
391
- proxyArr.push(rowObj2InstanceProxy(rowWithCamelCasedProps, findWiki, OrmStore.store.entities))
392
- }
393
-
394
- if (isArrayOfInstances) {
395
- target[key] = createRelationalArrayProxy(target, key, proxyArr, classWiki.className)
396
- resolve(target[key])
397
- }
398
- else {
399
- target[key] = proxyArr[0]
400
- resolve(target[key])
401
- }
402
- }
403
- catch (e) {
404
- coloredBackgroundConsoleLog(`Lazy loading failed. ${e}\n`, `failure`)
405
- }
406
- })
407
- }
408
-
409
-
410
373
  export function uncalledPropertySetHandler(target, key, value, columnClassificationArr) {
411
374
  const [propertyType, propertyTypeObj, mapWithProp] = columnClassificationArr
412
375
  const joiningId = target.id
@@ -496,7 +459,8 @@ export function proxifyEntityInstanceObj(instance, uncalledRelationalProperties)
496
459
  else if (key === "eEmitter_") return emitter
497
460
  else if (key === "eListener_") return eventListenersObj
498
461
  else if (key === "_isDeleted_") return target[key]
499
- return instanceProxyGetHandler(target, key, classWiki)
462
+ return target[key]
463
+ //return instanceProxyGetHandler(target, key, classWiki)
500
464
  },
501
465
  set: (target, /**@type {string}*/ key, value) => {
502
466
  instanceProxySetHandler(target, key, value, eventListenersObj, classWiki)
@@ -1,14 +1,16 @@
1
- import { store } from "./store.js"
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
- }
1
+ import { store } from "./store.js"
2
+ import { SyntaxKind } from "typescript"
3
+ import ts from "typescript"
4
+
5
+ export default function (source) {
6
+ const sourceFile = ts.createSourceFile("", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
7
+ const nodes = sourceFile.statements
8
+ for (const node of nodes) {
9
+ if (!node) continue
10
+ //@ts-ignore
11
+ const isValid = (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && node.heritageClauses
12
+ //@ts-ignore
13
+ if (isValid) store.nodeArr.push(node)
14
+ }
15
+ return source
16
+ }
@@ -1,43 +1,42 @@
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
-
1
+
2
+ import { store } from "./store.js"
3
+ import { nodeArr2ClassDict } from "../ORM/bootOrm.js"
4
+
5
+ export class MasqueradePlugin {
6
+ apply(compiler) {
7
+ compiler.hooks.compilation.tap(this.constructor.name, (compilation) => {
8
+ compilation.hooks.processAssets.tap(
9
+ {
10
+ name: this.constructor.name,
11
+ stage: compilation.constructor.PROCESS_ASSETS_STAGE_ADDITIONS,
12
+ },
13
+ (assets) => {
14
+ const classDict = nodeArr2ClassDict(store.nodeArr)
15
+ const prefix = `globalThis.masqueradeClassDict_ = ${JSON.stringify(classDict)};\n`
16
+
17
+ for (const entry of compilation.entrypoints.values()) {
18
+ for (const file of entry.getFiles()) {
19
+ if (!file.endsWith(".js")) continue
20
+
21
+ const asset = compilation.getAsset(file)
22
+ const source = asset.source.source()
23
+
24
+ compilation.updateAsset(
25
+ file,
26
+ new compiler.webpack.sources.RawSource(prefix + source)
27
+ )
28
+ }
29
+ }
30
+ }
31
+ )
32
+ })
33
+ }
34
+ }
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
@@ -10,7 +10,7 @@ import { OrmStore } from '../src/misc/ormStore.js'
10
10
 
11
11
  const { House, Person, NonRelationalClass2 } = classes
12
12
 
13
- const configObj = createConfigObj()
13
+ const configObj = createConfigObj('postgres', '123456789')
14
14
  await resetPostgresDb(configObj.dbConnection)
15
15
  await initORM(configObj, classes)
16
16
  let dbChanges = OrmStore.store.dbChangesObj
@@ -0,0 +1,34 @@
1
+ import { ORM } from "../index.js"
2
+ import { DatabaseSync } from 'node:sqlite'
3
+ import { Pool } from 'pg'
4
+ /**@typedef {import('../index.js').OrmConfigObj} OrmConfigObj */
5
+
6
+
7
+ export function createConfigObj(client, /**@type {undefined | string}*/ dbPaswword = undefined) {
8
+ if (client === `sqlite`) return {
9
+ dbConnection: new DatabaseSync('test'),
10
+ idTypeDefault: "INT"
11
+ }
12
+
13
+ return {
14
+ dbConnection: new Pool({
15
+ user: 'postgres', // e.g., 'postgres'
16
+ host: 'localhost', // database host
17
+ database: 'masquerade-test', // database name
18
+ password: `${dbPaswword}`, // your password
19
+ port: 5432, // default PostgreSQL port
20
+ }),
21
+ idTypeDefault: "INT"
22
+ }
23
+ }
24
+
25
+ export async function initORM(configObj, ...classes) {
26
+ await ORM.javascriptBoot(configObj, ...classes)
27
+ }
28
+
29
+ export async function resetPostgresDb(pool) {
30
+ await pool.query(
31
+ `DROP SCHEMA public CASCADE;
32
+ CREATE SCHEMA public;`
33
+ )
34
+ }