@jskit-ai/database-runtime 0.1.57 → 0.1.58

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/database-runtime",
4
- version: "0.1.57",
4
+ version: "0.1.58",
5
5
  kind: "runtime",
6
6
  dependsOn: [
7
7
  "@jskit-ai/kernel"
@@ -58,7 +58,7 @@ export default Object.freeze({
58
58
  mutations: {
59
59
  dependencies: {
60
60
  runtime: {
61
- "@jskit-ai/kernel": "0.1.57",
61
+ "@jskit-ai/kernel": "0.1.58",
62
62
  "dotenv": "^16.4.5",
63
63
  "knex": "^3.1.0"
64
64
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/database-runtime",
3
- "version": "0.1.57",
3
+ "version": "0.1.58",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -25,6 +25,6 @@
25
25
  "./shared/transactions": "./src/shared/transactions.js"
26
26
  },
27
27
  "dependencies": {
28
- "@jskit-ai/kernel": "0.1.57"
28
+ "@jskit-ai/kernel": "0.1.58"
29
29
  }
30
30
  }
@@ -141,6 +141,11 @@ function createKnexInstance(scope) {
141
141
  class DatabaseRuntimeServiceProvider {
142
142
  static id = "runtime.database";
143
143
 
144
+ constructor() {
145
+ this.knexInstance = null;
146
+ this.knexDestroyed = false;
147
+ }
148
+
144
149
  register(app) {
145
150
  if (!app || typeof app.singleton !== "function" || typeof app.has !== "function") {
146
151
  throw new Error("DatabaseRuntimeServiceProvider requires application singleton()/has().");
@@ -153,7 +158,12 @@ class DatabaseRuntimeServiceProvider {
153
158
  }
154
159
 
155
160
  if (!app.has("jskit.database.knex")) {
156
- app.singleton("jskit.database.knex", (scope) => createKnexInstance(scope));
161
+ app.singleton("jskit.database.knex", (scope) => {
162
+ const knex = createKnexInstance(scope);
163
+ this.knexInstance = knex;
164
+ this.knexDestroyed = false;
165
+ return knex;
166
+ });
157
167
  }
158
168
 
159
169
  if (!app.has("jskit.database.transactionManager")) {
@@ -165,6 +175,38 @@ class DatabaseRuntimeServiceProvider {
165
175
  }
166
176
 
167
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
+ }
168
210
  }
169
211
 
170
212
  export { DatabaseRuntimeServiceProvider };
@@ -42,6 +42,14 @@ function toNullableDateTime(value) {
42
42
  }
43
43
 
44
44
  function toDatabaseDateTimeUtc(value) {
45
+ if (value == null) {
46
+ return null;
47
+ }
48
+
49
+ if (typeof value === "string" && value.trim() === "") {
50
+ return null;
51
+ }
52
+
45
53
  const date = toDateOrThrow(value);
46
54
  const year = date.getUTCFullYear();
47
55
  const month = pad(date.getUTCMonth() + 1);
@@ -11,6 +11,13 @@ test("toDatabaseDateTimeUtc formats DATETIME(3) UTC string", () => {
11
11
  assert.equal(toDatabaseDateTimeUtc(new Date("2024-01-01T01:02:03.045Z")), "2024-01-01 01:02:03.045");
12
12
  });
13
13
 
14
+ test("toDatabaseDateTimeUtc preserves nullable empty values", () => {
15
+ assert.equal(toDatabaseDateTimeUtc(null), null);
16
+ assert.equal(toDatabaseDateTimeUtc(undefined), null);
17
+ assert.equal(toDatabaseDateTimeUtc(""), null);
18
+ assert.equal(toDatabaseDateTimeUtc(" "), null);
19
+ });
20
+
14
21
  test("date utils throw on invalid date", () => {
15
22
  assert.throws(() => toIsoString("not-a-date"), /Invalid date value\./);
16
23
  assert.throws(() => toDatabaseDateTimeUtc("not-a-date"), /Invalid date value\./);
@@ -50,8 +50,12 @@ function createSingletonApp() {
50
50
 
51
51
  function createKnexStub() {
52
52
  return {
53
+ destroyCalls: 0,
53
54
  async transaction(callback) {
54
55
  return callback({ trxId: "trx-1" });
56
+ },
57
+ async destroy() {
58
+ this.destroyCalls += 1;
55
59
  }
56
60
  };
57
61
  }
@@ -198,3 +202,18 @@ test("DatabaseRuntimeServiceProvider resolves knex config from DATABASE_URL when
198
202
  assert.equal(knex.__config.connection.password, "urlpass");
199
203
  });
200
204
  });
205
+
206
+ test("DatabaseRuntimeServiceProvider destroys resolved knex during shutdown", async () => {
207
+ const app = createSingletonApp();
208
+ const knex = createKnexStub();
209
+ app.instance("jskit.database.knex", knex);
210
+
211
+ const provider = new DatabaseRuntimeServiceProvider();
212
+ provider.register(app);
213
+
214
+ await provider.shutdown(app);
215
+ assert.equal(knex.destroyCalls, 1);
216
+
217
+ await provider.shutdown(app);
218
+ assert.equal(knex.destroyCalls, 1);
219
+ });