@jskit-ai/database-runtime 0.1.159 → 0.1.161
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/package.json +18 -71
- package/src/server/databaseSetup.js +69 -0
- package/src/server/knexFactory.js +23 -0
- package/src/server/knexMigrationConfig.js +108 -0
- package/src/server/providers/DatabaseProvider.js +64 -0
- package/src/shared/databaseConnection.js +2 -1
- package/src/shared/index.js +2 -2
- package/src/shared/repositoryOptions.js +38 -1
- package/test/databaseConnection.test.js +2 -0
- package/test/databaseSetup.test.js +64 -0
- package/test/entrypoints.boundary.test.js +7 -7
- package/test/knexMigrationConfig.test.js +80 -0
- package/test/packageMetadata.test.js +11 -46
- package/test/providerRuntime.test.js +103 -161
- package/test/repositoryOptions.test.js +47 -0
- package/test/runtimeCore.test.js +1 -54
- package/src/server/providers/DatabaseRuntimeServiceProvider.js +0 -212
- package/src/shared/runtime.js +0 -58
- package/templates/knexfile.js +0 -36
- package/templates/migrations/.gitkeep +0 -1
- package/templates/migrations/constraints/.gitkeep +0 -1
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { symlinkSafeRequire } from "@jskit-ai/kernel/server/support";
|
|
2
|
-
import * as databaseRuntime from "../../shared/index.js";
|
|
3
|
-
import { createTransactionManager } from "../../shared/transactionManager.js";
|
|
4
|
-
import {
|
|
5
|
-
normalizeDatabaseClient,
|
|
6
|
-
toKnexClientId
|
|
7
|
-
} from "../../shared/databaseClient.js";
|
|
8
|
-
import { resolveKnexConnectionFromEnvironment } from "../../shared/databaseConnection.js";
|
|
9
|
-
|
|
10
|
-
const DATABASE_RUNTIME_SERVER_API = Object.freeze({
|
|
11
|
-
...databaseRuntime
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
function resolveDatabaseEnv(scope) {
|
|
15
|
-
const envFromScope = scope?.has?.("jskit.env") ? scope.make("jskit.env") : {};
|
|
16
|
-
const source = envFromScope && typeof envFromScope === "object" ? envFromScope : {};
|
|
17
|
-
return {
|
|
18
|
-
...process.env,
|
|
19
|
-
...source
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function resolveDriverDialectId(driver) {
|
|
24
|
-
const source = driver && typeof driver === "object" ? driver : {};
|
|
25
|
-
const fromConstant = normalizeDatabaseClient(source.DIALECT_ID || source.dialectId || source.dialect, {
|
|
26
|
-
allowEmpty: true
|
|
27
|
-
});
|
|
28
|
-
if (fromConstant) {
|
|
29
|
-
return fromConstant;
|
|
30
|
-
}
|
|
31
|
-
if (typeof source.getDialectId === "function") {
|
|
32
|
-
return normalizeDatabaseClient(source.getDialectId(), {
|
|
33
|
-
allowEmpty: true
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return "";
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function loadKnexFactory() {
|
|
40
|
-
let moduleValue;
|
|
41
|
-
try {
|
|
42
|
-
moduleValue = symlinkSafeRequire("knex");
|
|
43
|
-
} catch {
|
|
44
|
-
throw new Error(
|
|
45
|
-
"Knex package is not installed. Run `npm install`, then `npx jskit migrations sync`."
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const knexFactory =
|
|
50
|
-
typeof moduleValue === "function"
|
|
51
|
-
? moduleValue
|
|
52
|
-
: typeof moduleValue?.default === "function"
|
|
53
|
-
? moduleValue.default
|
|
54
|
-
: null;
|
|
55
|
-
if (!knexFactory) {
|
|
56
|
-
throw new Error("Knex package resolved but did not expose a callable factory.");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return knexFactory;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function resolveRegisteredDriver(scope) {
|
|
63
|
-
const drivers = resolveRegisteredDrivers(scope);
|
|
64
|
-
if (drivers.length === 1) {
|
|
65
|
-
return drivers[0];
|
|
66
|
-
}
|
|
67
|
-
if (drivers.length < 1) {
|
|
68
|
-
throw new Error("No database driver is registered. Install @jskit-ai/database-runtime-mysql or @jskit-ai/database-runtime-postgres.");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const env = resolveDatabaseEnv(scope);
|
|
72
|
-
const preferredClient = normalizeDatabaseClient(env.DB_CLIENT, {
|
|
73
|
-
allowEmpty: true
|
|
74
|
-
});
|
|
75
|
-
if (!preferredClient) {
|
|
76
|
-
throw new Error("Multiple database drivers are registered. Set DB_CLIENT to mysql2 or pg, or keep exactly one database runtime driver package installed.");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const matched = drivers.find((driver) => resolveDriverDialectId(driver) === preferredClient) || null;
|
|
80
|
-
if (!matched) {
|
|
81
|
-
throw new Error(`Multiple database drivers are registered, but DB_CLIENT="${preferredClient}" did not match any registered driver.`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return matched;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function resolveRegisteredDrivers(scope) {
|
|
88
|
-
const drivers = [];
|
|
89
|
-
|
|
90
|
-
if (scope.has("runtime.database.driver.mysql")) {
|
|
91
|
-
drivers.push(scope.make("runtime.database.driver.mysql"));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (scope.has("runtime.database.driver.postgres")) {
|
|
95
|
-
drivers.push(scope.make("runtime.database.driver.postgres"));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return drivers;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function resolveSingleRegisteredDriver(scope) {
|
|
102
|
-
return resolveRegisteredDriver(scope);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function createKnexConfig(scope) {
|
|
106
|
-
const env = resolveDatabaseEnv(scope);
|
|
107
|
-
const configuredClient = normalizeDatabaseClient(env.DB_CLIENT, {
|
|
108
|
-
allowEmpty: true
|
|
109
|
-
});
|
|
110
|
-
const driver = resolveRegisteredDriver(scope);
|
|
111
|
-
const dialectId = resolveDriverDialectId(driver);
|
|
112
|
-
|
|
113
|
-
if (!dialectId) {
|
|
114
|
-
throw new Error("Selected database driver did not expose a valid dialect id.");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (configuredClient && configuredClient !== dialectId) {
|
|
118
|
-
throw new Error(`DB_CLIENT="${configuredClient}" does not match installed database driver "${dialectId}".`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const client = toKnexClientId(dialectId);
|
|
122
|
-
const defaultPort = dialectId === "pg" ? 5432 : 3306;
|
|
123
|
-
const connection = resolveKnexConnectionFromEnvironment(env, {
|
|
124
|
-
client: dialectId,
|
|
125
|
-
defaultPort,
|
|
126
|
-
context: "database runtime"
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
client,
|
|
131
|
-
connection
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function createKnexInstance(scope) {
|
|
136
|
-
const knexFactory = loadKnexFactory();
|
|
137
|
-
const config = createKnexConfig(scope);
|
|
138
|
-
return knexFactory(config);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
class DatabaseRuntimeServiceProvider {
|
|
142
|
-
static id = "runtime.database";
|
|
143
|
-
|
|
144
|
-
constructor() {
|
|
145
|
-
this.knexInstance = null;
|
|
146
|
-
this.knexDestroyed = false;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
register(app) {
|
|
150
|
-
if (!app || typeof app.singleton !== "function" || typeof app.has !== "function") {
|
|
151
|
-
throw new Error("DatabaseRuntimeServiceProvider requires application singleton()/has().");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
app.singleton("runtime.database", () => DATABASE_RUNTIME_SERVER_API);
|
|
155
|
-
|
|
156
|
-
if (!app.has("runtime.database.driver")) {
|
|
157
|
-
app.singleton("runtime.database.driver", (scope) => resolveSingleRegisteredDriver(scope));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (!app.has("jskit.database.knex")) {
|
|
161
|
-
app.singleton("jskit.database.knex", (scope) => {
|
|
162
|
-
const knex = createKnexInstance(scope);
|
|
163
|
-
this.knexInstance = knex;
|
|
164
|
-
this.knexDestroyed = false;
|
|
165
|
-
return knex;
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (!app.has("jskit.database.transactionManager")) {
|
|
170
|
-
app.singleton("jskit.database.transactionManager", (scope) => {
|
|
171
|
-
const knex = scope.make("jskit.database.knex");
|
|
172
|
-
return createTransactionManager({ knex });
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
boot() {}
|
|
178
|
-
|
|
179
|
-
async shutdown(app) {
|
|
180
|
-
if (this.knexDestroyed) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
let knex = this.knexInstance || null;
|
|
185
|
-
|
|
186
|
-
if (!knex) {
|
|
187
|
-
const container = app?.container;
|
|
188
|
-
const instanceRecord =
|
|
189
|
-
container && typeof container.findInstanceRecord === "function"
|
|
190
|
-
? container.findInstanceRecord("jskit.database.knex")
|
|
191
|
-
: null;
|
|
192
|
-
knex = instanceRecord?.value || null;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (!knex && app && typeof app.has === "function" && app.has("jskit.database.knex") && typeof app.make === "function") {
|
|
196
|
-
try {
|
|
197
|
-
knex = app.make("jskit.database.knex");
|
|
198
|
-
} catch {
|
|
199
|
-
knex = null;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (!knex || typeof knex.destroy !== "function") {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
await knex.destroy();
|
|
208
|
-
this.knexDestroyed = true;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export { DatabaseRuntimeServiceProvider };
|
package/src/shared/runtime.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { createTransactionManager } from "./transactionManager.js";
|
|
2
|
-
import { DatabaseRuntimeError } from "./runtimeErrors.js";
|
|
3
|
-
|
|
4
|
-
function ensureContainerInterface(app) {
|
|
5
|
-
if (
|
|
6
|
-
!app ||
|
|
7
|
-
typeof app.instance !== "function" ||
|
|
8
|
-
typeof app.singleton !== "function" ||
|
|
9
|
-
typeof app.make !== "function" ||
|
|
10
|
-
typeof app.has !== "function"
|
|
11
|
-
) {
|
|
12
|
-
throw new DatabaseRuntimeError("registerDatabaseRuntime requires application instance/singleton/make/has methods.");
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function ensureKnexInterface(knex) {
|
|
17
|
-
if (!knex || typeof knex.transaction !== "function") {
|
|
18
|
-
throw new DatabaseRuntimeError("registerDatabaseRuntime requires knex with transaction().");
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function ensureKnexBinding(app, knex) {
|
|
23
|
-
if (!app.has("jskit.database.knex")) {
|
|
24
|
-
app.instance("jskit.database.knex", knex);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const existingKnex = app.make("jskit.database.knex");
|
|
29
|
-
if (existingKnex !== knex) {
|
|
30
|
-
throw new DatabaseRuntimeError("registerDatabaseRuntime received knex that differs from existing Knex binding.");
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function ensureTransactionManagerBinding(app) {
|
|
35
|
-
if (app.has("jskit.database.transactionManager")) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
app.singleton("jskit.database.transactionManager", (scope) => {
|
|
40
|
-
const knex = scope.make("jskit.database.knex");
|
|
41
|
-
return createTransactionManager({ knex });
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function registerDatabaseRuntime(app, { knex } = {}) {
|
|
46
|
-
ensureContainerInterface(app);
|
|
47
|
-
ensureKnexInterface(knex);
|
|
48
|
-
|
|
49
|
-
ensureKnexBinding(app, knex);
|
|
50
|
-
ensureTransactionManagerBinding(app);
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
knex: app.make("jskit.database.knex"),
|
|
54
|
-
transactionManager: app.make("jskit.database.transactionManager")
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export { registerDatabaseRuntime };
|
package/templates/knexfile.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import dotenv from "dotenv";
|
|
3
|
-
import {
|
|
4
|
-
normalizeText,
|
|
5
|
-
toKnexClientId,
|
|
6
|
-
resolveDatabaseClientFromEnvironment,
|
|
7
|
-
resolveKnexConnectionFromEnvironment
|
|
8
|
-
} from "@jskit-ai/database-runtime/shared";
|
|
9
|
-
|
|
10
|
-
const appRoot = process.cwd();
|
|
11
|
-
dotenv.config({
|
|
12
|
-
path: path.join(appRoot, ".env"),
|
|
13
|
-
quiet: true
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const dialectId = resolveDatabaseClientFromEnvironment(process.env);
|
|
17
|
-
const client = toKnexClientId(dialectId);
|
|
18
|
-
const defaultPort = dialectId === "pg" ? 5432 : 3306;
|
|
19
|
-
const migrationsDirectory = path.resolve(appRoot, normalizeText(process.env.DB_MIGRATIONS_DIR) || "migrations");
|
|
20
|
-
const deferredConstraintsDirectory = path.join(migrationsDirectory, "constraints");
|
|
21
|
-
|
|
22
|
-
export default {
|
|
23
|
-
client,
|
|
24
|
-
connection: resolveKnexConnectionFromEnvironment(process.env, {
|
|
25
|
-
client: dialectId,
|
|
26
|
-
defaultPort,
|
|
27
|
-
context: "knex migrations"
|
|
28
|
-
}),
|
|
29
|
-
migrations: {
|
|
30
|
-
// Run every table-creation migration before deferred constraints. This keeps
|
|
31
|
-
// valid mutual foreign keys rebuildable without disabling database checks.
|
|
32
|
-
directory: [migrationsDirectory, deferredConstraintsDirectory],
|
|
33
|
-
extension: "cjs",
|
|
34
|
-
sortDirsSeparately: true
|
|
35
|
-
}
|
|
36
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|