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,76 @@
1
+ import { OrmStore } from "../misc/ormStore.js"
2
+
3
+ export class DbManager {
4
+
5
+ /**
6
+ * Can be given an entity table name to delete unused columns in, otherwise will delete all unused columns across all entity tables.
7
+ */
8
+ static async dropUnusedColumns(/**@type {string | undefined}*/ tableName = undefined) {
9
+ const queryFunc = getQueryFunc(false)
10
+ const queryStrGenerator = (tableName, columnName) => `ALTER TABLE ${tableName} DROP COLUMN ${columnName}`
11
+ const columnDroppingDict = DbManagerStore.dropColumnsDict
12
+ if (tableName) {
13
+ const droppedColumnNames = columnDroppingDict[tableName]
14
+ for (const columnName of droppedColumnNames) {
15
+ const queryStr = queryStrGenerator(tableName, columnName)
16
+ await queryFunc(queryStr)
17
+ }
18
+ }
19
+ else {
20
+ for (const [tableName, droppedColumnNames] of Object.entries(columnDroppingDict)) {
21
+ for (const columnName of droppedColumnNames) {
22
+ const queryStr = queryStrGenerator(tableName, columnName)
23
+ await queryFunc(queryStr)
24
+ }
25
+ }
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Can be given an entity table name to delete, otherwise will delete all unused entity tables.
31
+ */
32
+ static async dropUnusedTables(/**@type {string | undefined}*/ tableName = undefined) {
33
+ const tableNameArr = tableName ? [tableName] : DbManagerStore.deleteTables
34
+ if (!tableNameArr.length) return
35
+ await dropTables(tableNameArr)
36
+ }
37
+
38
+ /**
39
+ * Can be given a junction table name to delete, otherwise will delete all unused junction tables.
40
+ */
41
+ static async dropUnusedJunctions(/**@type {string | undefined}*/ tableName = undefined) {
42
+ const junctionNameArr = tableName ? [tableName] : DbManagerStore.deleteJunctions
43
+ if (!junctionNameArr.length) return
44
+ await dropTables(junctionNameArr)
45
+ }
46
+ }
47
+
48
+ async function dropTables(tableNameArr) {
49
+ const queryFunc = getQueryFunc()
50
+ if (!tableNameArr.length) return
51
+ await queryFunc(tableNameArr)
52
+ }
53
+
54
+ function getQueryFunc(dropTables = true) {
55
+ const { sqlClient, dbConnection } = OrmStore.store
56
+ if (dropTables) {
57
+ const queryFunc = sqlClient === `postgresql`
58
+ ? async (junctionNameArr) => await dbConnection.query(`DROP TABLE IF EXISTS ${junctionNameArr.join(`, `)}`)
59
+ : (junctionNameArr) => {
60
+ for (const junctionName of junctionNameArr) dbConnection.exec(`DROP TABLE IF EXISTS ${junctionName}`)
61
+ }
62
+ return queryFunc
63
+ }
64
+
65
+ const queryFunc = sqlClient === `postgresql`
66
+ ? dbConnection.query.bind(dbConnection)
67
+ : dbConnection.exec.bind(dbConnection)
68
+ return queryFunc
69
+ }
70
+
71
+ export class DbManagerStore {
72
+ static dropColumnsDict = {}
73
+ static deleteTables = []
74
+ static deleteJunctions = []
75
+ }
76
+
package/src/ORM/ORM.js ADDED
@@ -0,0 +1,181 @@
1
+ import { addChildrenToClasses, alterTables, compareAgainstDb, createBranches, createClassMap, createJunctionColumnContext, createTableObject, entities2NodeArr, formatForCreation, generateTableCreationQueryObject, getInitIdValues, handleSpecialSettingId, nodeArr2ClassDict, returnEntityClassObj, sendTableCreationQueries } from "./bootOrm.js"
2
+ export { Entity } from "../entity/entity.js"
3
+ import { DatabaseSync } from "node:sqlite"
4
+ import { OrmStore } from "../misc/ormStore.js"
5
+ import { coloredBackgroundConsoleLog } from "../misc/miscFunctions.js"
6
+ /**@typedef {import('../misc/types.js').SqlClient} SqlClient */
7
+ /**@typedef {import('../misc/types.js').DbPrimaryKey} DbPrimaryKey */
8
+ /**@typedef {import('../misc/types.js').OrmConfigObj} OrmConfigObj */
9
+ /**@typedef {import('pg').Pool} Pool*/
10
+
11
+ export const FinalizationRegistrySymb = Symbol("FinalizationRegistry")
12
+ export const DependentsFinalizationRegistry = Symbol("DependentsFinalizationRegistry")
13
+ export class ORM {
14
+ static [FinalizationRegistrySymb] = new FinalizationRegistry(([className, id]) => OrmStore.store.entityMapsObj[className].delete(id))
15
+ static [DependentsFinalizationRegistry] = new FinalizationRegistry(([className, id]) => OrmStore.store.dependentsMapsObj[className].delete(id))
16
+
17
+
18
+ /**
19
+ * Boot the ORM when using JavaScript.
20
+ * This method processes a combination of class constructor functions or class-like objects
21
+ * and bootstraps them into the ORM configuration.
22
+ *
23
+ * You can pass either:
24
+ * - **Class constructor functions** (e.g., `class MyClass {}`) or
25
+ * - **Objects** that map class names to class constructors (e.g., `{ MyClass: class {}}`).
26
+ * @param {OrmConfigObj} config - The ORM configuration object used to initialize the ORM system.
27
+ * @param {...(function|object)} classes - A rest parameter that can accept multiple class constructors
28
+ * (functions) or class dictionaries (objects) where the key is the
29
+ * class name and the value is the class constructor.
30
+ * @returns {Promise<void>}
31
+ */
32
+ static async javascriptBoot(config, ...classes) {
33
+ let classFuncsDict = {}
34
+ let i = 2
35
+ for (const element of classes) {
36
+ if (typeof element === 'function') classFuncsDict[element.name] = element
37
+ else if (typeof element === 'object') classFuncsDict = { ...classFuncsDict, ...element }
38
+ else throw new Error(`Argument number ${i} in 'javascriptBoot' is invalid.`)
39
+ i++
40
+ }
41
+
42
+ /**@type {{[key: string]: function}}*/ const classFuncs = Object.fromEntries(Object.entries(classFuncsDict))
43
+ const nodeArray = entities2NodeArr(classFuncs)
44
+ const classDict = nodeArr2ClassDict(nodeArray)
45
+ await universalBoot(classDict, classFuncs, config)
46
+ }
47
+
48
+
49
+ /**
50
+ * Boot the ORM when using TypeScript.
51
+ * This method processes a combination of class constructor functions or class-like objects
52
+ * and bootstraps them into the ORM configuration.
53
+ *
54
+ * You can pass either:
55
+ * - **Class constructor functions** (e.g., `class MyClass {}`) or
56
+ * - **Objects** that map class names to class constructors (e.g., `{ MyClass: class {}}`).
57
+ * @param {OrmConfigObj} config - The ORM configuration object used to initialize the ORM system.
58
+ * @param {...(function|object)} classes - A rest parameter that can accept multiple class constructors
59
+ * (functions) or class dictionaries (objects) where the key is the
60
+ * class name and the value is the class constructor.
61
+ * @returns {Promise<void>}
62
+ */
63
+ static async typescriptBoot(config, ...classes) {
64
+ const classDict = globalThis.masqueradeClassDict_
65
+ let classFuncsDict = {}
66
+ let i = 2
67
+ for (const element of classes) {
68
+ if (typeof element === 'function') classFuncsDict[element.name] = element
69
+ else if (typeof element === 'object') classFuncsDict = { ...classFuncsDict, ...element }
70
+ else throw new Error(`Argument number ${i} in 'typescriptBoot' is invalid.`)
71
+ i++
72
+ }
73
+
74
+ /**@type {{[key: string]: function}}*/ const classFuncs = Object.fromEntries(Object.entries(classFuncsDict))
75
+ await universalBoot(classDict, classFuncs, config)
76
+ }
77
+ }
78
+
79
+ async function universalBoot(classDict, classFuncs, /**@type {OrmConfigObj}*/ config) {
80
+ configure(config)
81
+ classDict.entity = returnEntityClassObj()
82
+ OrmStore.store.entities = classFuncs
83
+ addChildrenToClasses(classDict)
84
+ handleSpecialSettingId(classDict)
85
+ const branchesArr = createBranches(classDict)
86
+ const tablesDict = createTableObject(branchesArr)
87
+ createJunctionColumnContext(tablesDict)
88
+ createClassMap(tablesDict) //JUNCTIONS MAY NOT EXIST ON ORM MAP, BUT COLUMNS ALWAYS WILL
89
+ await compareAgainstDb(tablesDict)
90
+ await getInitIdValues(tablesDict)
91
+
92
+ if (config.skipTableCreation === true) return
93
+ const [tables4creation, tables2alter] = formatForCreation(tablesDict)
94
+ const tableCreationObj = generateTableCreationQueryObject(tables4creation)
95
+ await sendTableCreationQueries(tableCreationObj)
96
+ await alterTables(tables2alter)
97
+ coloredBackgroundConsoleLog(`Updated the database successfully.\n`, `success`)
98
+ }
99
+
100
+
101
+ function configure(/**@type {OrmConfigObj}*/ configObj) {
102
+ const { idTypeDefault, dbConnection } = configObj
103
+ if (!idTypeDefault || !dbConnection) throw new Error("Invalid ORM configuration object.")
104
+ const sqlClient = dbConnection instanceof DatabaseSync ? 'sqlite' : 'postgresql'
105
+ OrmStore.store = {
106
+ idTypeDefault,
107
+ dbConnection,
108
+ sqlClient,
109
+ dbChangesObj: {},
110
+ entityMapsObj: {},
111
+ dependentsMapsObj: {},
112
+ entities: undefined
113
+ }
114
+ }
115
+ // function columnsWithCasting(classWiki) {
116
+ // const returnedArr = []
117
+ // const Js2Db = {
118
+ // string: "TEXT",
119
+ // number: "INTEGER",
120
+ // boolean: "BOOLEAN",
121
+ // object: "JSONB",
122
+ // OrmJSON: "JSONB",
123
+ // Date: "TIMESTAMPTZ",
124
+ // bigint: "BIGINT",
125
+ // BIGINT: "BIGINT",
126
+ // UUID: "UUID"
127
+ // }
128
+ // for (const [columnName, columnType] of Object.entries(classWiki.columns).filter(column => column[0] !== "id")) {
129
+ // let type = Js2Db[columnType.type]
130
+ // if (columnType.isArray) type += `[]`
131
+ // returnedArr.push([columnName, type])
132
+ // }
133
+
134
+ // const idType = Js2Db[Entity[primaryTypeSymb]]
135
+ // returnedArr.push(["id", idType])
136
+ // return returnedArr
137
+ // }
138
+
139
+ // function toPgString(value) {
140
+ // if (value === undefined || value === null) {
141
+ // return null // or you can decide how to handle nulls
142
+ // }
143
+ // else if (typeof value === 'string') {
144
+ // return value // assume already properly formatted or '_ORM_UNCHANGED_VAL_'
145
+ // }
146
+ // else if (typeof value === 'boolean' || typeof value === 'number') {
147
+ // return value.toString()
148
+ // }
149
+ // else if (value instanceof Date) {
150
+ // return value.toISOString() // ISO string works well for timestamps
151
+ // }
152
+ // else if (Array.isArray(value)) {
153
+ // // Convert JS array to Postgres array literal, e.g. {1,2,3}
154
+ // // This handles only flat arrays of primitives (numbers or strings)
155
+ // return '{' + value.map(item => {
156
+ // if (typeof item === 'string') {
157
+ // return item.replace(/"/g, '\\"') // escape quotes inside strings
158
+ // }
159
+ // return item;
160
+ // }).join(',') + '}'
161
+ // }
162
+ // else if (typeof value === 'object') {
163
+ // // For JSON or JSONB columns, convert object to JSON string
164
+ // return JSON.stringify(value)
165
+ // }
166
+ // // Fallback: convert whatever else to string
167
+ // return String(value)
168
+ // }
169
+
170
+ // CAST($1 AS INTEGER) AS as_int,
171
+ // CAST($1 AS BIGINT) AS as_bigint,
172
+ // CAST($1 AS DATE) AS as_date,
173
+ // CAST($1 AS BOOLEAN) AS as_boolean,
174
+ // CAST($1 AS JSONB) AS as_jsonb,
175
+ // CAST($1 AS TEXT[]) AS as_text_array,
176
+ // CAST($1 AS INTEGER[]) AS as_int_array;
177
+
178
+
179
+
180
+
181
+