@imqueue/pg-sequelize 4.2.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/CHANGELOG.md +50 -0
- package/CONTRIBUTING.md +58 -0
- package/CONTRIBUTION-TERMS.md +79 -0
- package/LICENSE +585 -0
- package/README.md +94 -0
- package/SECURITY.md +41 -0
- package/index.d.ts +86 -0
- package/index.js +87 -0
- package/package.json +75 -0
- package/src/BaseModel.d.ts +695 -0
- package/src/BaseModel.js +917 -0
- package/src/Graph.d.ts +215 -0
- package/src/Graph.js +257 -0
- package/src/decorators/AssociatedWith.d.ts +94 -0
- package/src/decorators/AssociatedWith.js +71 -0
- package/src/decorators/ColumnIndex.d.ts +206 -0
- package/src/decorators/ColumnIndex.js +98 -0
- package/src/decorators/CreatedBy.d.ts +27 -0
- package/src/decorators/CreatedBy.js +84 -0
- package/src/decorators/DeletedBy.d.ts +30 -0
- package/src/decorators/DeletedBy.js +89 -0
- package/src/decorators/DynamicView.d.ts +124 -0
- package/src/decorators/DynamicView.js +113 -0
- package/src/decorators/Emittable.d.ts +39 -0
- package/src/decorators/Emittable.js +42 -0
- package/src/decorators/NullableIndex.d.ts +77 -0
- package/src/decorators/NullableIndex.js +64 -0
- package/src/decorators/UpdatedBy.d.ts +27 -0
- package/src/decorators/UpdatedBy.js +105 -0
- package/src/decorators/View.d.ts +87 -0
- package/src/decorators/View.js +93 -0
- package/src/decorators/index.d.ts +32 -0
- package/src/decorators/index.js +33 -0
- package/src/helpers/index.d.ts +24 -0
- package/src/helpers/index.js +25 -0
- package/src/helpers/js.d.ts +61 -0
- package/src/helpers/js.js +88 -0
- package/src/helpers/query.d.ts +445 -0
- package/src/helpers/query.js +1095 -0
- package/src/index.d.ts +162 -0
- package/src/index.js +223 -0
- package/src/types/DataPage.d.ts +52 -0
- package/src/types/DataPage.js +2 -0
- package/src/types/FieldsInput.d.ts +41 -0
- package/src/types/FieldsInput.js +75 -0
- package/src/types/FilterInput.d.ts +136 -0
- package/src/types/FilterInput.js +291 -0
- package/src/types/JsonObject.d.ts +16 -0
- package/src/types/JsonObject.js +50 -0
- package/src/types/OrderByInput.d.ts +45 -0
- package/src/types/OrderByInput.js +80 -0
- package/src/types/PaginationInput.d.ts +44 -0
- package/src/types/PaginationInput.js +90 -0
- package/src/types/index.d.ts +30 -0
- package/src/types/index.js +31 -0
- package/src/types/ranges/DateRange.d.ts +27 -0
- package/src/types/ranges/DateRange.js +69 -0
- package/src/types/ranges/IRange.d.ts +47 -0
- package/src/types/ranges/IRange.js +2 -0
- package/src/types/ranges/NumericRange.d.ts +19 -0
- package/src/types/ranges/NumericRange.js +61 -0
- package/src/types/ranges/index.d.ts +26 -0
- package/src/types/ranges/index.js +27 -0
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @imqueue/pg-sequelize - Sequelize ORM refines for @imqueue
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2019, imqueue.com <support@imqueue.com>
|
|
5
|
+
*
|
|
6
|
+
* Permission to use, copy, modify, and/or distribute this software for any
|
|
7
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
8
|
+
* copyright notice and this permission notice appear in all copies.
|
|
9
|
+
*
|
|
10
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
11
|
+
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
12
|
+
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
13
|
+
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
14
|
+
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
15
|
+
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
16
|
+
* PERFORMANCE OF THIS SOFTWARE.
|
|
17
|
+
*/
|
|
18
|
+
import { type ILogger } from '@imqueue/rpc';
|
|
19
|
+
import { type SequelizeOptions } from 'sequelize-typescript';
|
|
20
|
+
import { Sequelize } from './BaseModel.js';
|
|
21
|
+
export * from './Graph.js';
|
|
22
|
+
export * from './BaseModel.js';
|
|
23
|
+
export * from './helpers/index.js';
|
|
24
|
+
export * from './decorators/index.js';
|
|
25
|
+
export * from './types/index.js';
|
|
26
|
+
/**
|
|
27
|
+
* Everything {@link database} needs on its first call.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* Only the first `database()` call reads this, so treat it as start-up config rather
|
|
31
|
+
* than something to vary per call. Note also that `database()` WRITES to the object
|
|
32
|
+
* it is given — it fills in `connectionString` from the environment and replaces
|
|
33
|
+
* `sequelize.logging` — so a config exported as a module constant and passed at every
|
|
34
|
+
* call site does not stay as written.
|
|
35
|
+
*/
|
|
36
|
+
export interface IMQORMOptions {
|
|
37
|
+
/**
|
|
38
|
+
* Where SQL statements are logged.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* Falls back to the `@imqueue/rpc` service default, then to `console`, so it can
|
|
42
|
+
* be left out. `@imqueue/async-logger` is the usual choice.
|
|
43
|
+
*/
|
|
44
|
+
logger: ILogger;
|
|
45
|
+
/**
|
|
46
|
+
* Postgres connection string.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* Optional only because {@link DB_CONN_STR} can supply it. With neither,
|
|
50
|
+
* `database()` throws.
|
|
51
|
+
*/
|
|
52
|
+
connectionString?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Options passed through to the Sequelize constructor.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* `logging` is the one property `database()` overwrites — see {@link database}
|
|
58
|
+
* for what each value means.
|
|
59
|
+
*/
|
|
60
|
+
sequelize: SequelizeOptions;
|
|
61
|
+
/**
|
|
62
|
+
* Directory holding the COMPILED model files.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* Walked recursively for `.js` files, so point it at build output rather than at
|
|
66
|
+
* `.ts` sources. Each file must export a symbol named exactly after the file —
|
|
67
|
+
* `Lead.js` must export `Lead` — because that is how the loader picks the model
|
|
68
|
+
* out of the module. A mismatch hands Sequelize `undefined` instead of a model.
|
|
69
|
+
*/
|
|
70
|
+
modelsPath: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `DB_CONN_STR` from the environment, read once when this module loads.
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* The fallback {@link database} uses when {@link IMQORMOptions.connectionString} is
|
|
77
|
+
* absent. Because it is captured at import time, changing `process.env` later has no
|
|
78
|
+
* effect on it.
|
|
79
|
+
*/
|
|
80
|
+
export declare const DB_CONN_STR: string | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Whether logged SQL is reformatted across multiple lines. Off by default.
|
|
83
|
+
*
|
|
84
|
+
* @remarks
|
|
85
|
+
* Set `SQL_PRETTIFY` to a positive number to enable. Read once at import, so it is a
|
|
86
|
+
* deployment setting rather than something to toggle at runtime. Affects logging
|
|
87
|
+
* only — never the SQL that is executed.
|
|
88
|
+
*/
|
|
89
|
+
export declare const SQL_PRETTIFY: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Whether logged SQL carries ANSI colour. Off by default.
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* Set `SQL_COLORIZE` to a positive number to enable. Worth leaving off wherever logs
|
|
95
|
+
* are collected rather than read in a terminal, since the escape sequences end up in
|
|
96
|
+
* the stored line. Independent of {@link SQL_PRETTIFY}.
|
|
97
|
+
*/
|
|
98
|
+
export declare const SQL_COLORIZE: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Reformats a SQL string for logging, when {@link SQL_PRETTIFY} is on.
|
|
101
|
+
*
|
|
102
|
+
* @remarks
|
|
103
|
+
* A pass-through when prettifying is off, so it is always safe to call. Otherwise
|
|
104
|
+
* `sql-formatter` does the work and a handful of substitutions tidy up what it does
|
|
105
|
+
* to Postgres-specific syntax — casts, `&&`, and bind-parameter markers, which the
|
|
106
|
+
* formatter would otherwise break across lines.
|
|
107
|
+
*
|
|
108
|
+
* For logs only. It is not a parser and the result is not guaranteed to be
|
|
109
|
+
* executable.
|
|
110
|
+
*
|
|
111
|
+
* @param sql - Statement as sequelize reports it.
|
|
112
|
+
* @returns The reformatted statement, or `sql` unchanged when prettifying is off.
|
|
113
|
+
*/
|
|
114
|
+
export declare function formatSql(sql: string): string;
|
|
115
|
+
/**
|
|
116
|
+
* A sequelize `logging` callback: the SQL, and the timing when benchmarking is on.
|
|
117
|
+
*/
|
|
118
|
+
export type SqlLoggingFunction = (sql: string, time?: number) => void;
|
|
119
|
+
/**
|
|
120
|
+
* Connects to the database, loads the models, and returns the Sequelize instance.
|
|
121
|
+
*
|
|
122
|
+
* @remarks
|
|
123
|
+
* A process-wide singleton. The first call does all the work; every call after it
|
|
124
|
+
* returns the cached instance and DOES NOT LOOK AT ITS ARGUMENT — so passing a
|
|
125
|
+
* different config later is silently ignored rather than reconnecting. That is why
|
|
126
|
+
* services can import `database` anywhere and call `database(dbConfig)` freely, and
|
|
127
|
+
* also why there is no way to swap the connection once it is up.
|
|
128
|
+
*
|
|
129
|
+
* The first call resolves its configuration in this order:
|
|
130
|
+
*
|
|
131
|
+
* - `connectionString`, or {@link DB_CONN_STR} from the environment. Neither throws.
|
|
132
|
+
* - `logger`, or the `@imqueue/rpc` service default, or `console`.
|
|
133
|
+
* - `sequelize.logging`: left alone if explicitly falsy, so `false` disables logging;
|
|
134
|
+
* otherwise replaced with this package's formatter, honouring {@link SQL_PRETTIFY}
|
|
135
|
+
* and {@link SQL_COLORIZE}. A function you supply is wrapped rather than discarded,
|
|
136
|
+
* and receives the formatted SQL.
|
|
137
|
+
*
|
|
138
|
+
* Models are then discovered by walking `modelsPath` for compiled `.js` files and
|
|
139
|
+
* taking the export whose name matches the filename — see
|
|
140
|
+
* {@link IMQORMOptions.modelsPath}, since a mismatch there fails in a way the error
|
|
141
|
+
* does not explain. The require is synchronous and CommonJS-scoped, which is what
|
|
142
|
+
* makes the models loadable from an ESM package.
|
|
143
|
+
*
|
|
144
|
+
* @param options - Required on the first call, ignored on every later one.
|
|
145
|
+
* @returns The single Sequelize instance for this process, with models registered.
|
|
146
|
+
* @throws TypeError when the first call has no options, or when no connection string
|
|
147
|
+
* can be resolved from either the options or the environment.
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* // config.ts — built once at start-up
|
|
151
|
+
* export const dbConfig: IMQORMOptions = {
|
|
152
|
+
* logger,
|
|
153
|
+
* connectionString: process.env.DB_CONN_STR || '',
|
|
154
|
+
* sequelize: toSequelizeConfig(process.env.DB_CONN_STR, process.env.DB_DIALECT),
|
|
155
|
+
* modelsPath: './src/orm/models',
|
|
156
|
+
* };
|
|
157
|
+
*
|
|
158
|
+
* // anywhere in the service — the first call wins, the rest are free
|
|
159
|
+
* const orm = database(dbConfig);
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare function database(options?: IMQORMOptions): Sequelize;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @imqueue/pg-sequelize - Sequelize ORM refines for @imqueue
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2019, imqueue.com <support@imqueue.com>
|
|
5
|
+
*
|
|
6
|
+
* Permission to use, copy, modify, and/or distribute this software for any
|
|
7
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
8
|
+
* copyright notice and this permission notice appear in all copies.
|
|
9
|
+
*
|
|
10
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
11
|
+
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
12
|
+
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
13
|
+
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
14
|
+
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
15
|
+
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
16
|
+
* PERFORMANCE OF THIS SOFTWARE.
|
|
17
|
+
*/
|
|
18
|
+
import { DEFAULT_IMQ_SERVICE_OPTIONS } from '@imqueue/rpc';
|
|
19
|
+
import { createRequire } from 'node:module';
|
|
20
|
+
import { styleText } from 'node:util';
|
|
21
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
22
|
+
import { resolve, sep } from 'node:path';
|
|
23
|
+
import {} from 'sequelize-typescript';
|
|
24
|
+
import { Sequelize } from './BaseModel.js';
|
|
25
|
+
import { isDefined, isOk } from './helpers/js.js';
|
|
26
|
+
/* models exports! */
|
|
27
|
+
export * from './Graph.js';
|
|
28
|
+
export * from './BaseModel.js';
|
|
29
|
+
export * from './helpers/index.js';
|
|
30
|
+
export * from './decorators/index.js';
|
|
31
|
+
export * from './types/index.js';
|
|
32
|
+
const JS_EXT_RX = /\.js$/;
|
|
33
|
+
/**
|
|
34
|
+
* Every file under a directory, recursively.
|
|
35
|
+
*
|
|
36
|
+
* @param dir - Directory to walk.
|
|
37
|
+
* @returns Absolute paths of every file found, directories excluded.
|
|
38
|
+
*/
|
|
39
|
+
function walk(dir) {
|
|
40
|
+
let results = [];
|
|
41
|
+
for (let file of readdirSync(dir)) {
|
|
42
|
+
file = resolve(dir, file);
|
|
43
|
+
const stat = statSync(file);
|
|
44
|
+
if (stat && stat.isDirectory()) {
|
|
45
|
+
results = results.concat(walk(file));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
results.push(file);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `DB_CONN_STR` from the environment, read once when this module loads.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* The fallback {@link database} uses when {@link IMQORMOptions.connectionString} is
|
|
58
|
+
* absent. Because it is captured at import time, changing `process.env` later has no
|
|
59
|
+
* effect on it.
|
|
60
|
+
*/
|
|
61
|
+
export const DB_CONN_STR = process.env.DB_CONN_STR;
|
|
62
|
+
/**
|
|
63
|
+
* Whether logged SQL is reformatted across multiple lines. Off by default.
|
|
64
|
+
*
|
|
65
|
+
* @remarks
|
|
66
|
+
* Set `SQL_PRETTIFY` to a positive number to enable. Read once at import, so it is a
|
|
67
|
+
* deployment setting rather than something to toggle at runtime. Affects logging
|
|
68
|
+
* only — never the SQL that is executed.
|
|
69
|
+
*/
|
|
70
|
+
export const SQL_PRETTIFY = +(process.env.SQL_PRETTIFY || 0) > 0;
|
|
71
|
+
/**
|
|
72
|
+
* Whether logged SQL carries ANSI colour. Off by default.
|
|
73
|
+
*
|
|
74
|
+
* @remarks
|
|
75
|
+
* Set `SQL_COLORIZE` to a positive number to enable. Worth leaving off wherever logs
|
|
76
|
+
* are collected rather than read in a terminal, since the escape sequences end up in
|
|
77
|
+
* the stored line. Independent of {@link SQL_PRETTIFY}.
|
|
78
|
+
*/
|
|
79
|
+
export const SQL_COLORIZE = +(process.env.SQL_COLORIZE || 0) > 0;
|
|
80
|
+
import { format as sqlFormat } from 'sql-formatter';
|
|
81
|
+
const RX_SQL_NUM_LAYOUT = /\s+(['"]?\d+['"]?,?)\r?\n/g;
|
|
82
|
+
const RX_SQL_NUM_END = /(\d+['"]?)\s+(\))/g;
|
|
83
|
+
const RX_BRK_DBL_AND = /&\s+&/g;
|
|
84
|
+
const RX_BRK_CAST = /\s+(\[|::)(\s+)?/g;
|
|
85
|
+
const RX_BRK_POCKETS = /(\$)\s+(\d)/g;
|
|
86
|
+
const RX_SQL_PREFIX = /Execut(ed|ing) \(default\):/;
|
|
87
|
+
/**
|
|
88
|
+
* Reformats a SQL string for logging, when {@link SQL_PRETTIFY} is on.
|
|
89
|
+
*
|
|
90
|
+
* @remarks
|
|
91
|
+
* A pass-through when prettifying is off, so it is always safe to call. Otherwise
|
|
92
|
+
* `sql-formatter` does the work and a handful of substitutions tidy up what it does
|
|
93
|
+
* to Postgres-specific syntax — casts, `&&`, and bind-parameter markers, which the
|
|
94
|
+
* formatter would otherwise break across lines.
|
|
95
|
+
*
|
|
96
|
+
* For logs only. It is not a parser and the result is not guaranteed to be
|
|
97
|
+
* executable.
|
|
98
|
+
*
|
|
99
|
+
* @param sql - Statement as sequelize reports it.
|
|
100
|
+
* @returns The reformatted statement, or `sql` unchanged when prettifying is off.
|
|
101
|
+
*/
|
|
102
|
+
export function formatSql(sql) {
|
|
103
|
+
return SQL_PRETTIFY
|
|
104
|
+
? sqlFormat(sql)
|
|
105
|
+
.replace(RX_SQL_NUM_LAYOUT, '$1 ')
|
|
106
|
+
.replace(RX_SQL_NUM_END, '$1$2')
|
|
107
|
+
.replace(RX_BRK_DBL_AND, '&&')
|
|
108
|
+
.replace(RX_BRK_CAST, '$1')
|
|
109
|
+
.replace(RX_BRK_POCKETS, '$1$2')
|
|
110
|
+
: sql;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Builds the sequelize `logging` callback, honouring `SQL_PRETTIFY` and
|
|
114
|
+
* `SQL_COLORIZE`.
|
|
115
|
+
*
|
|
116
|
+
* @remarks
|
|
117
|
+
* Accepts either an `ILogger` or a plain callback, and that distinction is
|
|
118
|
+
* load-bearing rather than convenience: a caller who sets
|
|
119
|
+
* `sequelize.logging` to their own function used to have it swapped for this one
|
|
120
|
+
* with the function itself in the logger slot, so the first query to be logged threw
|
|
121
|
+
* `TypeError: logger.log is not a function`. Both shapes now receive the formatted
|
|
122
|
+
* SQL.
|
|
123
|
+
*
|
|
124
|
+
* @param sink - Where to write: a logger, or a callback in sequelize's own shape.
|
|
125
|
+
* @returns The callback to hand to sequelize as `options.logging`.
|
|
126
|
+
*/
|
|
127
|
+
const logging = (sink) => (sql, time) => {
|
|
128
|
+
const message = SQL_COLORIZE
|
|
129
|
+
? `${styleText(['bold', 'yellow'], 'SQL Query:')} ${styleText('cyan', formatSql(sql.replace(RX_SQL_PREFIX, '')))}`
|
|
130
|
+
: `SQL Query: ${formatSql(sql.replace(RX_SQL_PREFIX, ''))}`;
|
|
131
|
+
if (typeof sink === 'function') {
|
|
132
|
+
sink(message, time);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
sink.log(message, typeof time === 'number' ? `executed in ${time} ms` : '');
|
|
136
|
+
};
|
|
137
|
+
let orm;
|
|
138
|
+
/**
|
|
139
|
+
* Connects to the database, loads the models, and returns the Sequelize instance.
|
|
140
|
+
*
|
|
141
|
+
* @remarks
|
|
142
|
+
* A process-wide singleton. The first call does all the work; every call after it
|
|
143
|
+
* returns the cached instance and DOES NOT LOOK AT ITS ARGUMENT — so passing a
|
|
144
|
+
* different config later is silently ignored rather than reconnecting. That is why
|
|
145
|
+
* services can import `database` anywhere and call `database(dbConfig)` freely, and
|
|
146
|
+
* also why there is no way to swap the connection once it is up.
|
|
147
|
+
*
|
|
148
|
+
* The first call resolves its configuration in this order:
|
|
149
|
+
*
|
|
150
|
+
* - `connectionString`, or {@link DB_CONN_STR} from the environment. Neither throws.
|
|
151
|
+
* - `logger`, or the `@imqueue/rpc` service default, or `console`.
|
|
152
|
+
* - `sequelize.logging`: left alone if explicitly falsy, so `false` disables logging;
|
|
153
|
+
* otherwise replaced with this package's formatter, honouring {@link SQL_PRETTIFY}
|
|
154
|
+
* and {@link SQL_COLORIZE}. A function you supply is wrapped rather than discarded,
|
|
155
|
+
* and receives the formatted SQL.
|
|
156
|
+
*
|
|
157
|
+
* Models are then discovered by walking `modelsPath` for compiled `.js` files and
|
|
158
|
+
* taking the export whose name matches the filename — see
|
|
159
|
+
* {@link IMQORMOptions.modelsPath}, since a mismatch there fails in a way the error
|
|
160
|
+
* does not explain. The require is synchronous and CommonJS-scoped, which is what
|
|
161
|
+
* makes the models loadable from an ESM package.
|
|
162
|
+
*
|
|
163
|
+
* @param options - Required on the first call, ignored on every later one.
|
|
164
|
+
* @returns The single Sequelize instance for this process, with models registered.
|
|
165
|
+
* @throws TypeError when the first call has no options, or when no connection string
|
|
166
|
+
* can be resolved from either the options or the environment.
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* // config.ts — built once at start-up
|
|
170
|
+
* export const dbConfig: IMQORMOptions = {
|
|
171
|
+
* logger,
|
|
172
|
+
* connectionString: process.env.DB_CONN_STR || '',
|
|
173
|
+
* sequelize: toSequelizeConfig(process.env.DB_CONN_STR, process.env.DB_DIALECT),
|
|
174
|
+
* modelsPath: './src/orm/models',
|
|
175
|
+
* };
|
|
176
|
+
*
|
|
177
|
+
* // anywhere in the service — the first call wins, the rest are free
|
|
178
|
+
* const orm = database(dbConfig);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export function database(options) {
|
|
182
|
+
if (typeof orm !== 'undefined') {
|
|
183
|
+
return orm;
|
|
184
|
+
}
|
|
185
|
+
else if (typeof options === 'undefined') {
|
|
186
|
+
throw new TypeError('First call of database() must provide valid options!');
|
|
187
|
+
}
|
|
188
|
+
if (!options.connectionString) {
|
|
189
|
+
if (!DB_CONN_STR) {
|
|
190
|
+
throw new TypeError('Either environment DB_CONN_STR should be set or ' +
|
|
191
|
+
'connectionString property given!');
|
|
192
|
+
}
|
|
193
|
+
options.connectionString = DB_CONN_STR;
|
|
194
|
+
}
|
|
195
|
+
if (!options.connectionString) {
|
|
196
|
+
throw new TypeError('Database connection string is required!');
|
|
197
|
+
}
|
|
198
|
+
if (!options.logger) {
|
|
199
|
+
options.logger =
|
|
200
|
+
DEFAULT_IMQ_SERVICE_OPTIONS.logger || console;
|
|
201
|
+
}
|
|
202
|
+
// A caller-supplied function is a logging SINK, not a logger object — it has
|
|
203
|
+
// no `.log`, so casting it to ILogger threw on the first query that got
|
|
204
|
+
// logged. It is still wrapped rather than used raw, so SQL_PRETTIFY and
|
|
205
|
+
// SQL_COLORIZE keep working for this path too.
|
|
206
|
+
options.sequelize.logging =
|
|
207
|
+
!isDefined(options.sequelize.logging) || isOk(options.sequelize.logging)
|
|
208
|
+
? logging(typeof options.sequelize.logging === 'function'
|
|
209
|
+
? options.sequelize.logging
|
|
210
|
+
: options.logger)
|
|
211
|
+
: options.sequelize.logging;
|
|
212
|
+
orm = new Sequelize(options.connectionString, options.sequelize);
|
|
213
|
+
// model files are loaded synchronously with a CommonJS require
|
|
214
|
+
// scoped to this module: consumer model builds are CJS today, and
|
|
215
|
+
// require(esm) covers them if they migrate (Node >= 22.12)
|
|
216
|
+
const requireModel = createRequire(import.meta.url);
|
|
217
|
+
orm.addModels(walk(resolve(options.modelsPath))
|
|
218
|
+
.filter(name => JS_EXT_RX.test(name))
|
|
219
|
+
.map(filename => requireModel(filename)[filename.split(sep).reverse()[0].replace(JS_EXT_RX, '')]));
|
|
220
|
+
options.logger.log('Database models initialized...');
|
|
221
|
+
return orm;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @imqueue/pg-sequelize - Sequelize ORM refines for @imqueue
|
|
3
|
+
*
|
|
4
|
+
* I'm Queue Software Project
|
|
5
|
+
* Copyright (C) 2025 imqueue.com <support@imqueue.com>
|
|
6
|
+
*
|
|
7
|
+
* This program is free software: you can redistribute it and/or modify
|
|
8
|
+
* it under the terms of the GNU General Public License as published by
|
|
9
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
* (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* This program is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
* GNU General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* You should have received a copy of the GNU General Public License
|
|
18
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
*
|
|
20
|
+
* If you want to use this code in a closed source (commercial) project, you can
|
|
21
|
+
* purchase a proprietary commercial license. Please contact us at
|
|
22
|
+
* <support@imqueue.com> to get commercial licensing options.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* One page of results together with the size of the whole set.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* The envelope a paginated service method returns, so a caller that asked for 20
|
|
29
|
+
* rows can still render "20 of 1,340" without a second round trip. Nothing in this
|
|
30
|
+
* package produces one — it is a shape for your own service methods to declare and
|
|
31
|
+
* fill, which is why `data` is whatever you put in it rather than an array.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* @expose()
|
|
36
|
+
* public async listReservations(
|
|
37
|
+
* page?: PaginationInput,
|
|
38
|
+
* ): Promise<DataPage<Reservation[]>> {
|
|
39
|
+
* const { rows, count } = await Reservation.findAndCountAll(
|
|
40
|
+
* query.toLimitOptions(page),
|
|
41
|
+
* );
|
|
42
|
+
*
|
|
43
|
+
* return { total: count, data: rows };
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export interface DataPage<T> {
|
|
48
|
+
/** Rows matching the query in total, ignoring the page window. */
|
|
49
|
+
total: number;
|
|
50
|
+
/** The page itself. */
|
|
51
|
+
data: T;
|
|
52
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which fields to return, nested to match the shape of the relations.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Selection is by KEY PRESENCE, not by value — the single most important thing to
|
|
6
|
+
* know about this type, and the opposite of what `false` suggests. A key names a
|
|
7
|
+
* field to return; `false` means return that column and nothing further; a nested
|
|
8
|
+
* `FieldsInput` on a relation includes the relation and selects within it. So
|
|
9
|
+
* `{ id: false }` DOES return `id`. Omit a field entirely to leave it out, and pass
|
|
10
|
+
* no map at all to get everything the model declares.
|
|
11
|
+
*
|
|
12
|
+
* A value other than `false` is also read as a filter for that column by
|
|
13
|
+
* `query.autoQuery`, which is why the declared value type is `false | FieldsInput`:
|
|
14
|
+
* the two roles are told apart by that `false`.
|
|
15
|
+
*
|
|
16
|
+
* The recursion is what makes it worth a type of its own — one map describes a whole
|
|
17
|
+
* object graph, so a caller asking for a reservation with only its car's id does not
|
|
18
|
+
* need one argument per level. `query.createEntity` threads the nested map down to
|
|
19
|
+
* each related model as it goes, and `autoQuery` turns each nested level into a
|
|
20
|
+
* Sequelize `include`.
|
|
21
|
+
*
|
|
22
|
+
* Note that `autoQuery` MUTATES the map it is given: a column named in the `order`
|
|
23
|
+
* that the map does not mention is added to it as `false`, so ordering cannot break
|
|
24
|
+
* the selection. Do not share one map across calls and expect it to stay as written.
|
|
25
|
+
*
|
|
26
|
+
* It is a class rather than an interface because `@indexed` describes it to
|
|
27
|
+
* `@imqueue/rpc`, which needs a runtime value to attach the description to.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // the reservation with its type and id, and its car's id and model
|
|
32
|
+
* const fields: FieldsInput = {
|
|
33
|
+
* id: false,
|
|
34
|
+
* type: false,
|
|
35
|
+
* car: { id: false, model: false },
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class FieldsInput {
|
|
40
|
+
[fieldName: string]: false | FieldsInput;
|
|
41
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/*!
|
|
8
|
+
* @imqueue/pg-sequelize - Sequelize ORM refines for @imqueue
|
|
9
|
+
*
|
|
10
|
+
* I'm Queue Software Project
|
|
11
|
+
* Copyright (C) 2025 imqueue.com <support@imqueue.com>
|
|
12
|
+
*
|
|
13
|
+
* This program is free software: you can redistribute it and/or modify
|
|
14
|
+
* it under the terms of the GNU General Public License as published by
|
|
15
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
16
|
+
* (at your option) any later version.
|
|
17
|
+
*
|
|
18
|
+
* This program is distributed in the hope that it will be useful,
|
|
19
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21
|
+
* GNU General Public License for more details.
|
|
22
|
+
*
|
|
23
|
+
* You should have received a copy of the GNU General Public License
|
|
24
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
25
|
+
*
|
|
26
|
+
* If you want to use this code in a closed source (commercial) project, you can
|
|
27
|
+
* purchase a proprietary commercial license. Please contact us at
|
|
28
|
+
* <support@imqueue.com> to get commercial licensing options.
|
|
29
|
+
*/
|
|
30
|
+
import { indexed } from '@imqueue/rpc';
|
|
31
|
+
/**
|
|
32
|
+
* Which fields to return, nested to match the shape of the relations.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* Selection is by KEY PRESENCE, not by value — the single most important thing to
|
|
36
|
+
* know about this type, and the opposite of what `false` suggests. A key names a
|
|
37
|
+
* field to return; `false` means return that column and nothing further; a nested
|
|
38
|
+
* `FieldsInput` on a relation includes the relation and selects within it. So
|
|
39
|
+
* `{ id: false }` DOES return `id`. Omit a field entirely to leave it out, and pass
|
|
40
|
+
* no map at all to get everything the model declares.
|
|
41
|
+
*
|
|
42
|
+
* A value other than `false` is also read as a filter for that column by
|
|
43
|
+
* `query.autoQuery`, which is why the declared value type is `false | FieldsInput`:
|
|
44
|
+
* the two roles are told apart by that `false`.
|
|
45
|
+
*
|
|
46
|
+
* The recursion is what makes it worth a type of its own — one map describes a whole
|
|
47
|
+
* object graph, so a caller asking for a reservation with only its car's id does not
|
|
48
|
+
* need one argument per level. `query.createEntity` threads the nested map down to
|
|
49
|
+
* each related model as it goes, and `autoQuery` turns each nested level into a
|
|
50
|
+
* Sequelize `include`.
|
|
51
|
+
*
|
|
52
|
+
* Note that `autoQuery` MUTATES the map it is given: a column named in the `order`
|
|
53
|
+
* that the map does not mention is added to it as `false`, so ordering cannot break
|
|
54
|
+
* the selection. Do not share one map across calls and expect it to stay as written.
|
|
55
|
+
*
|
|
56
|
+
* It is a class rather than an interface because `@indexed` describes it to
|
|
57
|
+
* `@imqueue/rpc`, which needs a runtime value to attach the description to.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* // the reservation with its type and id, and its car's id and model
|
|
62
|
+
* const fields: FieldsInput = {
|
|
63
|
+
* id: false,
|
|
64
|
+
* type: false,
|
|
65
|
+
* car: { id: false, model: false },
|
|
66
|
+
* };
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
let FieldsInput = class FieldsInput {
|
|
70
|
+
};
|
|
71
|
+
FieldsInput = __decorate([
|
|
72
|
+
indexed(() => `[fieldName: string]: false | ${FieldsInput.name}`)
|
|
73
|
+
], FieldsInput);
|
|
74
|
+
export { FieldsInput };
|
|
75
|
+
//# sourceMappingURL=FieldsInput.js.map
|