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.
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/bin/universalTsInit.js +59 -0
- package/docs/deletion.md +186 -0
- package/docs/find.md +265 -0
- package/docs/getting-started-javascript.md +112 -0
- package/docs/getting-started-typescript.md +159 -0
- package/docs/in-depth-class-definitions.md +193 -0
- package/docs/jsdoc-ux-tips.md +17 -0
- package/docs/managing-the-database.md +37 -0
- package/docs/saving-to-database.md +37 -0
- package/index.d.ts +7 -0
- package/index.js +11 -0
- package/jsconfig.json +29 -0
- package/package.json +40 -0
- package/src/ORM/DbManager.js +76 -0
- package/src/ORM/ORM.js +181 -0
- package/src/ORM/bootOrm.js +929 -0
- package/src/changeLogger/changeLogger.js +55 -0
- package/src/changeLogger/save.js +237 -0
- package/src/changeLogger/sqlClients/postgres.js +97 -0
- package/src/changeLogger/sqlClients/sqlite.js +120 -0
- package/src/entity/delete/delete.js +20 -0
- package/src/entity/delete/getDependents.js +46 -0
- package/src/entity/entity.d.ts +46 -0
- package/src/entity/entity.js +228 -0
- package/src/entity/find/find.js +157 -0
- package/src/entity/find/joins.js +52 -0
- package/src/entity/find/queryBuilder.js +95 -0
- package/src/entity/find/relations.js +23 -0
- package/src/entity/find/scopeProxies.js +60 -0
- package/src/entity/find/sqlClients/postgresFuncs.js +171 -0
- package/src/entity/find/sqlClients/sqliteFuncs.js +211 -0
- package/src/entity/find/where/relationalWhere.js +142 -0
- package/src/entity/find/where/where.js +244 -0
- package/src/entity/find/where/whereArgsFunctions.js +49 -0
- package/src/misc/classes.js +63 -0
- package/src/misc/constants.js +42 -0
- package/src/misc/miscFunctions.js +167 -0
- package/src/misc/ormStore.js +18 -0
- package/src/misc/types.d.ts +560 -0
- package/src/proxies/instanceProxy.js +544 -0
- package/src/proxies/nonRelationalArrayProxy.js +76 -0
- package/src/proxies/objectProxy.js +50 -0
- package/src/proxies/relationalArrayProxy.js +130 -0
- package/src/webpack/masquerade-loader.js +14 -0
- package/src/webpack/plugin.js +43 -0
- package/src/webpack/store.js +2 -0
- package/src/webpack/webpack.config.js +41 -0
- package/testing/dev-doc.md +7 -0
- package/testing/generationFuncs.js +21 -0
- package/testing/miscFunctions.js +97 -0
- package/testing/postgres.test.js +254 -0
- package/testing/sqlite.test.js +257 -0
- 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
|
+
|