@faasjs/knex 8.0.0-beta.6 → 8.0.0-beta.8

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/README.md CHANGED
@@ -13,19 +13,25 @@ npm install @faasjs/knex
13
13
 
14
14
  ## Functions
15
15
 
16
+ - [createPgliteKnex](functions/createPgliteKnex.md)
16
17
  - [initPostgresTypeParsers](functions/initPostgresTypeParsers.md)
18
+ - [mountFaasKnex](functions/mountFaasKnex.md)
17
19
  - [query](functions/query.md)
18
20
  - [raw](functions/raw.md)
19
21
  - [transaction](functions/transaction.md)
22
+ - [unmountFaasKnex](functions/unmountFaasKnex.md)
20
23
  - [useKnex](functions/useKnex.md)
21
24
 
22
25
  ## Classes
23
26
 
24
27
  - [Knex](classes/Knex.md)
28
+ - [KnexSchema](classes/KnexSchema.md)
25
29
 
26
30
  ## Type Aliases
27
31
 
28
32
  - [KnexConfig](type-aliases/KnexConfig.md)
33
+ - [MountedKnexAdapter](type-aliases/MountedKnexAdapter.md)
34
+ - [MountFaasKnexOptions](type-aliases/MountFaasKnexOptions.md)
29
35
 
30
36
  ## Variables
31
37
 
package/dist/index.cjs CHANGED
@@ -1,239 +1,340 @@
1
- 'use strict';
2
-
3
- var crypto = require('crypto');
4
- var deep_merge = require('@faasjs/deep_merge');
5
- var func = require('@faasjs/func');
6
- var load = require('@faasjs/load');
7
- var knex = require('knex');
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
8
27
 
9
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
28
+ //#endregion
29
+ let node_crypto = require("node:crypto");
30
+ let node_fs = require("node:fs");
31
+ let node_path = require("node:path");
32
+ let _faasjs_node_utils = require("@faasjs/node-utils");
33
+ let knex = require("knex");
34
+ knex = __toESM(knex);
35
+ let _faasjs_func = require("@faasjs/func");
10
36
 
11
- var knex__default = /*#__PURE__*/_interopDefault(knex);
37
+ //#region src/pglite.ts
38
+ function parsePgliteConnection(connection) {
39
+ if (typeof connection === "undefined" || connection === null) return `memory://${(0, node_crypto.randomUUID)()}`;
40
+ if (typeof connection !== "string" || !connection.trim().length) throw Error("[Knex] Invalid \"pglite\" connection, expected non-empty string in config.connection or SECRET_<NAME>_CONNECTION.");
41
+ return connection;
42
+ }
43
+ function ensurePgliteConnectionPath(connection) {
44
+ if (connection.includes("://")) return;
45
+ (0, node_fs.mkdirSync)((0, node_path.dirname)((0, node_path.resolve)(connection)), { recursive: true });
46
+ }
47
+ async function loadPglitePackages() {
48
+ try {
49
+ const pgliteModule = await (0, _faasjs_node_utils.loadPackage)("@electric-sql/pglite", []);
50
+ const PgliteDialect = await (0, _faasjs_node_utils.loadPackage)("knex-pglite");
51
+ if (typeof pgliteModule.PGlite !== "function" || !pgliteModule.types) throw Error("Invalid @electric-sql/pglite exports");
52
+ if (typeof PgliteDialect !== "function") throw Error("Invalid knex-pglite exports");
53
+ return {
54
+ PGlite: pgliteModule.PGlite,
55
+ types: pgliteModule.types,
56
+ PgliteDialect
57
+ };
58
+ } catch {
59
+ throw Error("[Knex] client \"pglite\" requires dependencies \"@electric-sql/pglite\" and \"knex-pglite\". Please install both packages in your project.");
60
+ }
61
+ }
62
+ /**
63
+ * Create a knex instance backed by `knex-pglite`.
64
+ * If connection is missing, it defaults to an in-memory database.
65
+ */
66
+ async function createPgliteKnex(config = {}, connection) {
67
+ const resolvedConnection = parsePgliteConnection(connection ?? config.connection);
68
+ ensurePgliteConnectionPath(resolvedConnection);
69
+ const { PGlite, types, PgliteDialect } = await loadPglitePackages();
70
+ const { client: _client, connection: _connection, pool: _pool, ...restConfig } = config;
71
+ const pglite = new PGlite(resolvedConnection, { parsers: {
72
+ [types.INT2]: (v) => Number.parseInt(v, 10),
73
+ [types.INT4]: (v) => Number.parseInt(v, 10),
74
+ [types.INT8]: (v) => Number.parseInt(v, 10),
75
+ [types.FLOAT4]: (v) => Number.parseFloat(v),
76
+ [types.FLOAT8]: (v) => Number.parseFloat(v),
77
+ [types.NUMERIC]: (v) => Number.parseFloat(v)
78
+ } });
79
+ return (0, knex.default)({
80
+ ...restConfig,
81
+ client: PgliteDialect,
82
+ connection: { pglite }
83
+ });
84
+ }
85
+ /**
86
+ * Mount a knex adapter to `globalThis.FaasJS_Knex` for `@faasjs/knex`.
87
+ */
88
+ function mountFaasKnex(db, options = {}) {
89
+ const globalWithFaasKnex = globalThis;
90
+ const name = options.name || "knex";
91
+ if (!globalWithFaasKnex.FaasJS_Knex) globalWithFaasKnex.FaasJS_Knex = {};
92
+ globalWithFaasKnex.FaasJS_Knex[name] = {
93
+ adapter: db,
94
+ query: db,
95
+ config: options.config || {}
96
+ };
97
+ }
98
+ /**
99
+ * Remove mounted knex adapter from `globalThis.FaasJS_Knex`.
100
+ */
101
+ function unmountFaasKnex(name = "knex") {
102
+ const globalWithFaasKnex = globalThis;
103
+ if (!globalWithFaasKnex.FaasJS_Knex) return;
104
+ delete globalWithFaasKnex.FaasJS_Knex[name];
105
+ }
12
106
 
13
- // src/index.ts
14
- var originKnex = knex__default.default;
15
- var Name = "knex";
16
- if (!global.FaasJS_Knex) {
17
- global.FaasJS_Knex = {};
107
+ //#endregion
108
+ //#region src/plugin.ts
109
+ /**
110
+ * Origin [knex](https://knexjs.org/) instance.
111
+ */
112
+ const originKnex = knex.default;
113
+ const Name = "knex";
114
+ if (!global.FaasJS_Knex) global.FaasJS_Knex = {};
115
+ function getGlobalKnexAdapters() {
116
+ if (!global.FaasJS_Knex) global.FaasJS_Knex = {};
117
+ return global.FaasJS_Knex;
18
118
  }
19
119
  async function initPostgresTypeParsers() {
20
- const pg = await load.loadPackage("pg");
21
- pg.types.setTypeParser(
22
- pg.types.builtins.INT2,
23
- (v) => Number.parseInt(v, 10)
24
- );
25
- pg.types.setTypeParser(
26
- pg.types.builtins.INT4,
27
- (v) => Number.parseInt(v, 10)
28
- );
29
- pg.types.setTypeParser(
30
- pg.types.builtins.INT8,
31
- (v) => Number.parseInt(v, 10)
32
- );
33
- pg.types.setTypeParser(
34
- pg.types.builtins.FLOAT4,
35
- (v) => Number.parseFloat(v)
36
- );
37
- pg.types.setTypeParser(
38
- pg.types.builtins.FLOAT8,
39
- (v) => Number.parseFloat(v)
40
- );
41
- pg.types.setTypeParser(
42
- pg.types.builtins.NUMERIC,
43
- (v) => Number.parseFloat(v)
44
- );
120
+ const pg = await (0, _faasjs_node_utils.loadPackage)("pg");
121
+ pg.types.setTypeParser(pg.types.builtins.INT2, (v) => Number.parseInt(v, 10));
122
+ pg.types.setTypeParser(pg.types.builtins.INT4, (v) => Number.parseInt(v, 10));
123
+ pg.types.setTypeParser(pg.types.builtins.INT8, (v) => Number.parseInt(v, 10));
124
+ pg.types.setTypeParser(pg.types.builtins.FLOAT4, (v) => Number.parseFloat(v));
125
+ pg.types.setTypeParser(pg.types.builtins.FLOAT8, (v) => Number.parseFloat(v));
126
+ pg.types.setTypeParser(pg.types.builtins.NUMERIC, (v) => Number.parseFloat(v));
45
127
  }
46
128
  var Knex = class {
47
- type = "knex";
48
- name = Name;
49
- config;
50
- adapter;
51
- query;
52
- logger;
53
- constructor(config) {
54
- if (config) {
55
- this.name = config.name || this.name;
56
- this.config = config.config || /* @__PURE__ */ Object.create(null);
57
- } else {
58
- this.config = /* @__PURE__ */ Object.create(null);
59
- }
60
- }
61
- async onMount(data, next) {
62
- this.logger = data.logger;
63
- if (global.FaasJS_Knex[this.name]) {
64
- this.config = global.FaasJS_Knex[this.name].config;
65
- this.adapter = global.FaasJS_Knex[this.name].adapter;
66
- this.query = this.adapter;
67
- this.logger.debug("use exists adapter");
68
- await next();
69
- return;
70
- }
71
- const prefix = `SECRET_${this.name.toUpperCase()}_`;
72
- for (let key in process.env)
73
- if (key.startsWith(prefix)) {
74
- const value = process.env[key];
75
- key = key.replace(prefix, "").toLowerCase();
76
- if (typeof this.config[key] === "undefined")
77
- if (key.startsWith("connection_")) {
78
- if (!this.config.connection) {
79
- this.config.connection = /* @__PURE__ */ Object.create(null);
80
- }
81
- this.config.connection[key.replace("connection_", "")] = value;
82
- } else this.config[key] = value;
83
- }
84
- if (data.config.plugins?.[this.name || this.type]?.config)
85
- this.config = deep_merge.deepMerge(
86
- data.config.plugins[this.name || this.type].config,
87
- this.config
88
- );
89
- switch (this.config.client) {
90
- case "sqlite3":
91
- this.config.client = "better-sqlite3";
92
- this.config.useNullAsDefault = true;
93
- break;
94
- case "pg":
95
- if (!this.config.pool) this.config.pool = /* @__PURE__ */ Object.create(null);
96
- this.config.pool = Object.assign(
97
- {
98
- propagateCreateError: false,
99
- min: 0,
100
- max: 10,
101
- acquireTimeoutMillis: 5e3,
102
- idleTimeoutMillis: 3e4
103
- },
104
- this.config.pool
105
- );
106
- if (typeof this.config.connection === "string" && !this.config.connection.includes("json=true"))
107
- this.config.connection = `${this.config.connection}?json=true`;
108
- break;
109
- default:
110
- if (typeof this.config.client === "string") {
111
- if (this.config.client.startsWith("npm:")) {
112
- const client = await load.loadPackage(
113
- this.config.client.replace("npm:", "")
114
- );
115
- if (!client) throw Error(`Invalid client: ${this.config.client}`);
116
- if (typeof client === "function") {
117
- this.config.client = client;
118
- break;
119
- }
120
- if (client.default && typeof client.default === "function") {
121
- this.config.client = client.default;
122
- break;
123
- }
124
- throw Error(`Invalid client: ${this.config.client}`);
125
- }
126
- }
127
- break;
128
- }
129
- this.adapter = knex__default.default(this.config);
130
- if (this.config.client === "pg") await initPostgresTypeParsers();
131
- this.query = this.adapter;
132
- this.query.on("query", ({ sql, __knexQueryUid, bindings }) => {
133
- if (!__knexQueryUid) return;
134
- this.logger.time(`Knex${this.name}${__knexQueryUid}`);
135
- this.logger.debug(
136
- "[%s] query begin: %s %j",
137
- __knexQueryUid,
138
- sql,
139
- bindings
140
- );
141
- }).on("query-response", (response, { sql, __knexQueryUid, bindings }) => {
142
- if (!__knexQueryUid) return;
143
- this.logger.timeEnd(
144
- `Knex${this.name}${__knexQueryUid}`,
145
- "[%s] query done: %s %j %j",
146
- __knexQueryUid,
147
- sql,
148
- bindings,
149
- response
150
- );
151
- }).on("query-error", (_, { __knexQueryUid, sql, bindings }) => {
152
- if (!__knexQueryUid) return;
153
- this.logger.timeEnd(
154
- `Knex${this.name}${__knexQueryUid}`,
155
- "[%s] query failed: %s %j",
156
- __knexQueryUid,
157
- sql,
158
- bindings
159
- );
160
- });
161
- data.logger.debug("connected");
162
- global.FaasJS_Knex[this.name] = this;
163
- await next();
164
- }
165
- async onInvoke(data, next) {
166
- this.logger = data.logger;
167
- await next();
168
- }
169
- async raw(sql, bindings = []) {
170
- if (!this.adapter) throw Error("[Knex] Client not initialized.");
171
- return this.adapter.raw(sql, bindings);
172
- }
173
- /**
174
- * Wraps a transaction, returning a promise that resolves to the return value of the callback.
175
- *
176
- * - Support 'commit' and 'rollback' event.
177
- */
178
- async transaction(scope, config, options) {
179
- if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
180
- if (options?.trx) return scope(options.trx);
181
- const trx = await this.adapter.transaction(config);
182
- const trxId = crypto.randomUUID();
183
- this.logger.debug("[%s] transaction begin", trxId);
184
- try {
185
- const result = await scope(trx);
186
- if (trx.isCompleted()) {
187
- this.logger.debug("[%s] transaction has been finished in scope", trxId);
188
- return result;
189
- }
190
- this.logger.debug("[%s] transaction begin commit", trxId);
191
- await trx.commit();
192
- this.logger.debug("[%s] transaction committed: %j", trxId, result);
193
- trx.emit("commit");
194
- return result;
195
- } catch (error) {
196
- await trx.rollback(error);
197
- this.logger.error("[%s] transaction rollback: %s", trxId, error);
198
- trx.emit("rollback", error);
199
- throw error;
200
- }
201
- }
202
- schema() {
203
- if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
204
- return this.adapter.schema;
205
- }
206
- async quit() {
207
- if (!global.FaasJS_Knex[this.name]) return;
208
- try {
209
- await global.FaasJS_Knex[this.name].adapter.destroy();
210
- delete global.FaasJS_Knex[this.name];
211
- } catch (error) {
212
- console.error(error);
213
- }
214
- }
129
+ type = "knex";
130
+ name = Name;
131
+ config;
132
+ adapter;
133
+ query;
134
+ logger;
135
+ constructor(config) {
136
+ if (config) {
137
+ this.name = config.name || this.name;
138
+ this.config = config.config || Object.create(null);
139
+ } else this.config = Object.create(null);
140
+ }
141
+ async onMount(data, next) {
142
+ this.logger = data.logger;
143
+ const existsAdapter = getGlobalKnexAdapters()[this.name];
144
+ if (existsAdapter) {
145
+ this.config = existsAdapter.config;
146
+ this.adapter = existsAdapter.adapter;
147
+ this.query = this.adapter;
148
+ this.logger.debug("use exists adapter");
149
+ await next();
150
+ return;
151
+ }
152
+ const prefix = `SECRET_${this.name.toUpperCase()}_`;
153
+ for (let key in process.env) if (key.startsWith(prefix)) {
154
+ const value = process.env[key];
155
+ key = key.replace(prefix, "").toLowerCase();
156
+ if (typeof this.config[key] === "undefined") if (key.startsWith("connection_")) {
157
+ if (typeof this.config.connection === "string") continue;
158
+ if (!this.config.connection || typeof this.config.connection !== "object") this.config.connection = Object.create(null);
159
+ this.config.connection[key.replace("connection_", "")] = value;
160
+ } else this.config[key] = value;
161
+ }
162
+ if (data.config.plugins?.[this.name || this.type]?.config) this.config = (0, _faasjs_node_utils.deepMerge)(data.config.plugins[this.name || this.type].config, this.config);
163
+ if (this.config.client === "pglite") {
164
+ const connectionEnvKeys = Object.keys(process.env).filter((key) => key.startsWith(`${prefix}CONNECTION_`));
165
+ if (connectionEnvKeys.length) throw Error(`[Knex] Invalid "pglite" env keys: ${connectionEnvKeys.join(", ")}. Use ${prefix}CONNECTION instead.`);
166
+ this.adapter = await createPgliteKnex(this.config);
167
+ } else {
168
+ switch (this.config.client) {
169
+ case "sqlite3":
170
+ this.config.client = "better-sqlite3";
171
+ this.config.useNullAsDefault = true;
172
+ break;
173
+ case "pg":
174
+ if (!this.config.pool) this.config.pool = Object.create(null);
175
+ this.config.pool = Object.assign({
176
+ propagateCreateError: false,
177
+ min: 0,
178
+ max: 10,
179
+ acquireTimeoutMillis: 5e3,
180
+ idleTimeoutMillis: 3e4
181
+ }, this.config.pool);
182
+ if (typeof this.config.connection === "string" && !this.config.connection.includes("json=true")) this.config.connection = `${this.config.connection}?json=true`;
183
+ break;
184
+ default:
185
+ if (typeof this.config.client === "string") {
186
+ if (this.config.client.startsWith("npm:")) {
187
+ const client = await (0, _faasjs_node_utils.loadPackage)(this.config.client.replace("npm:", ""));
188
+ if (!client) throw Error(`Invalid client: ${this.config.client}`);
189
+ if (typeof client === "function") {
190
+ this.config.client = client;
191
+ break;
192
+ }
193
+ if (client.default && typeof client.default === "function") {
194
+ this.config.client = client.default;
195
+ break;
196
+ }
197
+ throw Error(`Invalid client: ${this.config.client}`);
198
+ }
199
+ }
200
+ break;
201
+ }
202
+ this.adapter = (0, knex.default)(this.config);
203
+ if (this.config.client === "pg") await initPostgresTypeParsers();
204
+ }
205
+ this.query = this.adapter;
206
+ this.query.on("query", ({ sql, __knexQueryUid, bindings }) => {
207
+ if (!__knexQueryUid) return;
208
+ this.logger.time(`Knex${this.name}${__knexQueryUid}`);
209
+ this.logger.debug("[%s] query begin: %s %j", __knexQueryUid, sql, bindings);
210
+ }).on("query-response", (response, { sql, __knexQueryUid, bindings }) => {
211
+ if (!__knexQueryUid) return;
212
+ this.logger.timeEnd(`Knex${this.name}${__knexQueryUid}`, "[%s] query done: %s %j %j", __knexQueryUid, sql, bindings, response);
213
+ }).on("query-error", (_, { __knexQueryUid, sql, bindings }) => {
214
+ if (!__knexQueryUid) return;
215
+ this.logger.timeEnd(`Knex${this.name}${__knexQueryUid}`, "[%s] query failed: %s %j", __knexQueryUid, sql, bindings);
216
+ });
217
+ data.logger.debug("connected");
218
+ getGlobalKnexAdapters()[this.name] = this;
219
+ await next();
220
+ }
221
+ async onInvoke(data, next) {
222
+ this.logger = data.logger;
223
+ await next();
224
+ }
225
+ async raw(sql, bindings = []) {
226
+ if (!this.adapter) throw Error("[Knex] Client not initialized.");
227
+ return this.adapter.raw(sql, bindings);
228
+ }
229
+ /**
230
+ * Wraps a transaction, returning a promise that resolves to the return value of the callback.
231
+ *
232
+ * - Support 'commit' and 'rollback' event.
233
+ */
234
+ async transaction(scope, config, options) {
235
+ if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
236
+ if (options?.trx) return scope(options.trx);
237
+ const trx = await this.adapter.transaction(config);
238
+ const trxId = (0, node_crypto.randomUUID)();
239
+ this.logger.debug("[%s] transaction begin", trxId);
240
+ try {
241
+ const result = await scope(trx);
242
+ if (trx.isCompleted()) {
243
+ this.logger.debug("[%s] transaction has been finished in scope", trxId);
244
+ return result;
245
+ }
246
+ this.logger.debug("[%s] transaction begin commit", trxId);
247
+ await trx.commit();
248
+ this.logger.debug("[%s] transaction committed: %j", trxId, result);
249
+ trx.emit("commit");
250
+ return result;
251
+ } catch (error) {
252
+ await trx.rollback(error);
253
+ this.logger.error("[%s] transaction rollback: %s", trxId, error);
254
+ trx.emit("rollback", error);
255
+ throw error;
256
+ }
257
+ }
258
+ schema() {
259
+ if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
260
+ return this.adapter.schema;
261
+ }
262
+ async quit() {
263
+ const adapters = getGlobalKnexAdapters();
264
+ if (!adapters[this.name]) return;
265
+ try {
266
+ await adapters[this.name].adapter.destroy();
267
+ delete adapters[this.name];
268
+ } catch (error) {
269
+ console.error(error);
270
+ }
271
+ }
215
272
  };
216
273
  function useKnex(config) {
217
- const name = config?.name || Name;
218
- if (global.FaasJS_Knex[name]) return func.usePlugin(global.FaasJS_Knex[name]);
219
- return func.usePlugin(new Knex(config));
274
+ const name = config?.name || Name;
275
+ const adapters = getGlobalKnexAdapters();
276
+ if (adapters[name]) return (0, _faasjs_func.usePlugin)(adapters[name]);
277
+ return (0, _faasjs_func.usePlugin)(new Knex(config));
220
278
  }
221
279
  function query(table) {
222
- return useKnex().query(
223
- table
224
- );
280
+ return useKnex().query(table);
225
281
  }
226
282
  async function transaction(scope, config, options) {
227
- return useKnex().transaction(scope, config, options);
283
+ return useKnex().transaction(scope, config, options);
228
284
  }
229
285
  async function raw(sql, bindings = []) {
230
- return useKnex().raw(sql, bindings);
286
+ return useKnex().raw(sql, bindings);
231
287
  }
232
288
 
289
+ //#endregion
290
+ //#region src/schema.ts
291
+ const DefaultMigratorConfig = {
292
+ directory: "./src/db/migrations",
293
+ extension: "ts"
294
+ };
295
+ /**
296
+ * Migration helper for FaasJS's knex plugin.
297
+ */
298
+ var KnexSchema = class {
299
+ knex;
300
+ constructor(knex) {
301
+ this.knex = knex;
302
+ }
303
+ getAdapter() {
304
+ if (!this.knex.adapter) throw Error(`[${this.knex.name}] Client not initialized.`);
305
+ return this.knex.adapter;
306
+ }
307
+ getMigratorConfig() {
308
+ return Object.assign({}, DefaultMigratorConfig, this.knex.config?.migrations || Object.create(null));
309
+ }
310
+ async migrateLatest() {
311
+ return this.getAdapter().migrate.latest(this.getMigratorConfig());
312
+ }
313
+ async migrateRollback() {
314
+ return this.getAdapter().migrate.rollback(this.getMigratorConfig());
315
+ }
316
+ async migrateStatus() {
317
+ return this.getAdapter().migrate.status(this.getMigratorConfig());
318
+ }
319
+ async migrateCurrentVersion() {
320
+ return this.getAdapter().migrate.currentVersion(this.getMigratorConfig());
321
+ }
322
+ async migrateMake(name) {
323
+ const migrationName = name?.trim();
324
+ if (!migrationName) throw Error("[KnexSchema] Missing migration name. Usage: npm run migrate:make -- create_users");
325
+ return this.getAdapter().migrate.make(migrationName, this.getMigratorConfig());
326
+ }
327
+ };
328
+
329
+ //#endregion
233
330
  exports.Knex = Knex;
331
+ exports.KnexSchema = KnexSchema;
332
+ exports.createPgliteKnex = createPgliteKnex;
234
333
  exports.initPostgresTypeParsers = initPostgresTypeParsers;
334
+ exports.mountFaasKnex = mountFaasKnex;
235
335
  exports.originKnex = originKnex;
236
336
  exports.query = query;
237
337
  exports.raw = raw;
238
338
  exports.transaction = transaction;
239
- exports.useKnex = useKnex;
339
+ exports.unmountFaasKnex = unmountFaasKnex;
340
+ exports.useKnex = useKnex;
package/dist/index.d.ts CHANGED
@@ -1,68 +1,101 @@
1
- import { Plugin, MountData, Next, InvokeData, UseifyPlugin } from '@faasjs/func';
2
- import { Logger } from '@faasjs/logger';
3
- import knex, { Knex as Knex$1 } from 'knex';
4
- export { Knex as OriginKnex } from 'knex';
1
+ import knex, { Knex as Knex$1, Knex as OriginKnex } from "knex";
2
+ import { InvokeData, MountData, Next, Plugin, UseifyPlugin } from "@faasjs/func";
3
+ import { Logger } from "@faasjs/logger";
5
4
 
5
+ //#region src/pglite.d.ts
6
+ type MountedKnexAdapter = {
7
+ adapter: Knex$1;
8
+ query: Knex$1;
9
+ config: Record<string, unknown>;
10
+ };
11
+ type MountFaasKnexOptions = {
12
+ /** key of `globalThis.FaasJS_Knex`, default is `knex` */name?: string; /** optional config metadata passed through to `@faasjs/knex` */
13
+ config?: Record<string, unknown>;
14
+ };
6
15
  /**
7
- * FaasJS's sql plugin, base on [Knex](https://knexjs.org/).
8
- *
9
- * [![License: MIT](https://img.shields.io/npm/l/@faasjs/knex.svg)](https://github.com/faasjs/faasjs/blob/main/packages/knex/LICENSE)
10
- * [![NPM Version](https://img.shields.io/npm/v/@faasjs/knex.svg)](https://www.npmjs.com/package/@faasjs/knex)
11
- *
12
- * ## Install
13
- *
14
- * ```sh
15
- * npm install @faasjs/knex
16
- * ```
17
- * @packageDocumentation
16
+ * Create a knex instance backed by `knex-pglite`.
17
+ * If connection is missing, it defaults to an in-memory database.
18
18
  */
19
-
19
+ declare function createPgliteKnex(config?: Partial<Knex$1.Config>, connection?: string): Promise<Knex$1>;
20
+ /**
21
+ * Mount a knex adapter to `globalThis.FaasJS_Knex` for `@faasjs/knex`.
22
+ */
23
+ declare function mountFaasKnex(db: Knex$1, options?: MountFaasKnexOptions): void;
24
+ /**
25
+ * Remove mounted knex adapter from `globalThis.FaasJS_Knex`.
26
+ */
27
+ declare function unmountFaasKnex(name?: string): void;
28
+ //#endregion
29
+ //#region src/plugin.d.ts
20
30
  /**
21
31
  * Origin [knex](https://knexjs.org/) instance.
22
32
  */
23
33
  declare const originKnex: typeof knex;
24
-
25
34
  type KnexConfig = {
26
- name?: string;
27
- config?: Knex$1.Config;
35
+ name?: string;
36
+ config?: OriginKnex.Config;
28
37
  };
29
38
  declare function initPostgresTypeParsers(): Promise<void>;
30
39
  declare class Knex implements Plugin {
31
- readonly type = "knex";
32
- readonly name: string;
33
- config: Knex$1.Config;
34
- adapter: Knex$1;
35
- query: Knex$1;
36
- logger: Logger;
37
- constructor(config?: KnexConfig);
38
- onMount(data: MountData, next: Next): Promise<void>;
39
- onInvoke(data: InvokeData<any, any, any>, next: Next): Promise<void>;
40
- raw<TResult = any>(sql: string, bindings?: Knex$1.RawBinding[] | Knex$1.ValueDict): Promise<Knex$1.Raw<TResult>>;
41
- /**
42
- * Wraps a transaction, returning a promise that resolves to the return value of the callback.
43
- *
44
- * - Support 'commit' and 'rollback' event.
45
- */
46
- transaction<TResult = any>(scope: (trx: Knex$1.Transaction<any, any>) => Promise<TResult>, config?: Knex$1.TransactionConfig, options?: {
47
- trx?: Knex$1.Transaction;
48
- }): Promise<TResult>;
49
- schema(): Knex$1.SchemaBuilder;
50
- quit(): Promise<void>;
40
+ readonly type = "knex";
41
+ readonly name: string;
42
+ config: OriginKnex.Config;
43
+ adapter: OriginKnex;
44
+ query: OriginKnex;
45
+ logger: Logger;
46
+ constructor(config?: KnexConfig);
47
+ onMount(data: MountData, next: Next): Promise<void>;
48
+ onInvoke(data: InvokeData<any, any, any>, next: Next): Promise<void>;
49
+ raw<TResult = any>(sql: string, bindings?: OriginKnex.RawBinding[] | OriginKnex.ValueDict): Promise<OriginKnex.Raw<TResult>>;
50
+ /**
51
+ * Wraps a transaction, returning a promise that resolves to the return value of the callback.
52
+ *
53
+ * - Support 'commit' and 'rollback' event.
54
+ */
55
+ transaction<TResult = any>(scope: (trx: OriginKnex.Transaction<any, any>) => Promise<TResult>, config?: OriginKnex.TransactionConfig, options?: {
56
+ trx?: OriginKnex.Transaction;
57
+ }): Promise<TResult>;
58
+ schema(): OriginKnex.SchemaBuilder;
59
+ quit(): Promise<void>;
51
60
  }
52
61
  declare function useKnex(config?: KnexConfig): UseifyPlugin<Knex>;
53
- declare function query<TName extends Knex$1.TableNames>(table: TName): Knex$1.QueryBuilder<Knex$1.TableType<TName>, {
54
- _base: Knex$1.ResolveTableType<Knex$1.TableType<TName>, 'base'>;
55
- _hasSelection: false;
56
- _keys: never;
57
- _aliases: {};
58
- _single: false;
59
- _intersectProps: {};
60
- _unionProps: never;
62
+ declare function query<TName extends OriginKnex.TableNames>(table: TName): OriginKnex.QueryBuilder<OriginKnex.TableType<TName>, {
63
+ _base: OriginKnex.ResolveTableType<OriginKnex.TableType<TName>, 'base'>;
64
+ _hasSelection: false;
65
+ _keys: never;
66
+ _aliases: {};
67
+ _single: false;
68
+ _intersectProps: {};
69
+ _unionProps: never;
61
70
  }[]>;
62
- declare function query<TName extends {} = any, TResult = any[]>(table: string): Knex$1.QueryBuilder<TName, TResult>;
63
- declare function transaction<TResult = any>(scope: (trx: Knex$1.Transaction<any, any>) => Promise<TResult>, config?: Knex$1.TransactionConfig, options?: {
64
- trx?: Knex$1.Transaction;
71
+ declare function query<TName extends {} = any, TResult = any[]>(table: string): OriginKnex.QueryBuilder<TName, TResult>;
72
+ declare function transaction<TResult = any>(scope: (trx: OriginKnex.Transaction<any, any>) => Promise<TResult>, config?: OriginKnex.TransactionConfig, options?: {
73
+ trx?: OriginKnex.Transaction;
65
74
  }): Promise<TResult>;
66
- declare function raw<TResult = any>(sql: string, bindings?: Knex$1.RawBinding[] | Knex$1.ValueDict): Promise<Knex$1.Raw<TResult>>;
67
-
68
- export { Knex, type KnexConfig, initPostgresTypeParsers, originKnex, query, raw, transaction, useKnex };
75
+ declare function raw<TResult = any>(sql: string, bindings?: OriginKnex.RawBinding[] | OriginKnex.ValueDict): Promise<OriginKnex.Raw<TResult>>;
76
+ //#endregion
77
+ //#region src/schema.d.ts
78
+ /**
79
+ * Migration helper for FaasJS's knex plugin.
80
+ */
81
+ declare class KnexSchema {
82
+ readonly knex: {
83
+ name: string;
84
+ adapter?: Knex$1;
85
+ config: Knex$1.Config;
86
+ };
87
+ constructor(knex: {
88
+ name: string;
89
+ adapter?: Knex$1;
90
+ config: Knex$1.Config;
91
+ });
92
+ private getAdapter;
93
+ private getMigratorConfig;
94
+ migrateLatest(): Promise<any>;
95
+ migrateRollback(): Promise<any>;
96
+ migrateStatus(): Promise<number>;
97
+ migrateCurrentVersion(): Promise<string>;
98
+ migrateMake(name: string): Promise<string>;
99
+ }
100
+ //#endregion
101
+ export { Knex, KnexConfig, KnexSchema, MountFaasKnexOptions, MountedKnexAdapter, type OriginKnex, createPgliteKnex, initPostgresTypeParsers, mountFaasKnex, originKnex, query, raw, transaction, unmountFaasKnex, useKnex };
package/dist/index.mjs CHANGED
@@ -1,227 +1,301 @@
1
- import { randomUUID } from 'crypto';
2
- import { deepMerge } from '@faasjs/deep_merge';
3
- import { usePlugin } from '@faasjs/func';
4
- import { loadPackage } from '@faasjs/load';
5
- import knex from 'knex';
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdirSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { deepMerge, loadPackage } from "@faasjs/node-utils";
5
+ import knex from "knex";
6
+ import { usePlugin } from "@faasjs/func";
6
7
 
7
- // src/index.ts
8
- var originKnex = knex;
9
- var Name = "knex";
10
- if (!global.FaasJS_Knex) {
11
- global.FaasJS_Knex = {};
8
+ //#region src/pglite.ts
9
+ function parsePgliteConnection(connection) {
10
+ if (typeof connection === "undefined" || connection === null) return `memory://${randomUUID()}`;
11
+ if (typeof connection !== "string" || !connection.trim().length) throw Error("[Knex] Invalid \"pglite\" connection, expected non-empty string in config.connection or SECRET_<NAME>_CONNECTION.");
12
+ return connection;
13
+ }
14
+ function ensurePgliteConnectionPath(connection) {
15
+ if (connection.includes("://")) return;
16
+ mkdirSync(dirname(resolve(connection)), { recursive: true });
17
+ }
18
+ async function loadPglitePackages() {
19
+ try {
20
+ const pgliteModule = await loadPackage("@electric-sql/pglite", []);
21
+ const PgliteDialect = await loadPackage("knex-pglite");
22
+ if (typeof pgliteModule.PGlite !== "function" || !pgliteModule.types) throw Error("Invalid @electric-sql/pglite exports");
23
+ if (typeof PgliteDialect !== "function") throw Error("Invalid knex-pglite exports");
24
+ return {
25
+ PGlite: pgliteModule.PGlite,
26
+ types: pgliteModule.types,
27
+ PgliteDialect
28
+ };
29
+ } catch {
30
+ throw Error("[Knex] client \"pglite\" requires dependencies \"@electric-sql/pglite\" and \"knex-pglite\". Please install both packages in your project.");
31
+ }
32
+ }
33
+ /**
34
+ * Create a knex instance backed by `knex-pglite`.
35
+ * If connection is missing, it defaults to an in-memory database.
36
+ */
37
+ async function createPgliteKnex(config = {}, connection) {
38
+ const resolvedConnection = parsePgliteConnection(connection ?? config.connection);
39
+ ensurePgliteConnectionPath(resolvedConnection);
40
+ const { PGlite, types, PgliteDialect } = await loadPglitePackages();
41
+ const { client: _client, connection: _connection, pool: _pool, ...restConfig } = config;
42
+ const pglite = new PGlite(resolvedConnection, { parsers: {
43
+ [types.INT2]: (v) => Number.parseInt(v, 10),
44
+ [types.INT4]: (v) => Number.parseInt(v, 10),
45
+ [types.INT8]: (v) => Number.parseInt(v, 10),
46
+ [types.FLOAT4]: (v) => Number.parseFloat(v),
47
+ [types.FLOAT8]: (v) => Number.parseFloat(v),
48
+ [types.NUMERIC]: (v) => Number.parseFloat(v)
49
+ } });
50
+ return knex({
51
+ ...restConfig,
52
+ client: PgliteDialect,
53
+ connection: { pglite }
54
+ });
55
+ }
56
+ /**
57
+ * Mount a knex adapter to `globalThis.FaasJS_Knex` for `@faasjs/knex`.
58
+ */
59
+ function mountFaasKnex(db, options = {}) {
60
+ const globalWithFaasKnex = globalThis;
61
+ const name = options.name || "knex";
62
+ if (!globalWithFaasKnex.FaasJS_Knex) globalWithFaasKnex.FaasJS_Knex = {};
63
+ globalWithFaasKnex.FaasJS_Knex[name] = {
64
+ adapter: db,
65
+ query: db,
66
+ config: options.config || {}
67
+ };
68
+ }
69
+ /**
70
+ * Remove mounted knex adapter from `globalThis.FaasJS_Knex`.
71
+ */
72
+ function unmountFaasKnex(name = "knex") {
73
+ const globalWithFaasKnex = globalThis;
74
+ if (!globalWithFaasKnex.FaasJS_Knex) return;
75
+ delete globalWithFaasKnex.FaasJS_Knex[name];
76
+ }
77
+
78
+ //#endregion
79
+ //#region src/plugin.ts
80
+ /**
81
+ * Origin [knex](https://knexjs.org/) instance.
82
+ */
83
+ const originKnex = knex;
84
+ const Name = "knex";
85
+ if (!global.FaasJS_Knex) global.FaasJS_Knex = {};
86
+ function getGlobalKnexAdapters() {
87
+ if (!global.FaasJS_Knex) global.FaasJS_Knex = {};
88
+ return global.FaasJS_Knex;
12
89
  }
13
90
  async function initPostgresTypeParsers() {
14
- const pg = await loadPackage("pg");
15
- pg.types.setTypeParser(
16
- pg.types.builtins.INT2,
17
- (v) => Number.parseInt(v, 10)
18
- );
19
- pg.types.setTypeParser(
20
- pg.types.builtins.INT4,
21
- (v) => Number.parseInt(v, 10)
22
- );
23
- pg.types.setTypeParser(
24
- pg.types.builtins.INT8,
25
- (v) => Number.parseInt(v, 10)
26
- );
27
- pg.types.setTypeParser(
28
- pg.types.builtins.FLOAT4,
29
- (v) => Number.parseFloat(v)
30
- );
31
- pg.types.setTypeParser(
32
- pg.types.builtins.FLOAT8,
33
- (v) => Number.parseFloat(v)
34
- );
35
- pg.types.setTypeParser(
36
- pg.types.builtins.NUMERIC,
37
- (v) => Number.parseFloat(v)
38
- );
91
+ const pg = await loadPackage("pg");
92
+ pg.types.setTypeParser(pg.types.builtins.INT2, (v) => Number.parseInt(v, 10));
93
+ pg.types.setTypeParser(pg.types.builtins.INT4, (v) => Number.parseInt(v, 10));
94
+ pg.types.setTypeParser(pg.types.builtins.INT8, (v) => Number.parseInt(v, 10));
95
+ pg.types.setTypeParser(pg.types.builtins.FLOAT4, (v) => Number.parseFloat(v));
96
+ pg.types.setTypeParser(pg.types.builtins.FLOAT8, (v) => Number.parseFloat(v));
97
+ pg.types.setTypeParser(pg.types.builtins.NUMERIC, (v) => Number.parseFloat(v));
39
98
  }
40
99
  var Knex = class {
41
- type = "knex";
42
- name = Name;
43
- config;
44
- adapter;
45
- query;
46
- logger;
47
- constructor(config) {
48
- if (config) {
49
- this.name = config.name || this.name;
50
- this.config = config.config || /* @__PURE__ */ Object.create(null);
51
- } else {
52
- this.config = /* @__PURE__ */ Object.create(null);
53
- }
54
- }
55
- async onMount(data, next) {
56
- this.logger = data.logger;
57
- if (global.FaasJS_Knex[this.name]) {
58
- this.config = global.FaasJS_Knex[this.name].config;
59
- this.adapter = global.FaasJS_Knex[this.name].adapter;
60
- this.query = this.adapter;
61
- this.logger.debug("use exists adapter");
62
- await next();
63
- return;
64
- }
65
- const prefix = `SECRET_${this.name.toUpperCase()}_`;
66
- for (let key in process.env)
67
- if (key.startsWith(prefix)) {
68
- const value = process.env[key];
69
- key = key.replace(prefix, "").toLowerCase();
70
- if (typeof this.config[key] === "undefined")
71
- if (key.startsWith("connection_")) {
72
- if (!this.config.connection) {
73
- this.config.connection = /* @__PURE__ */ Object.create(null);
74
- }
75
- this.config.connection[key.replace("connection_", "")] = value;
76
- } else this.config[key] = value;
77
- }
78
- if (data.config.plugins?.[this.name || this.type]?.config)
79
- this.config = deepMerge(
80
- data.config.plugins[this.name || this.type].config,
81
- this.config
82
- );
83
- switch (this.config.client) {
84
- case "sqlite3":
85
- this.config.client = "better-sqlite3";
86
- this.config.useNullAsDefault = true;
87
- break;
88
- case "pg":
89
- if (!this.config.pool) this.config.pool = /* @__PURE__ */ Object.create(null);
90
- this.config.pool = Object.assign(
91
- {
92
- propagateCreateError: false,
93
- min: 0,
94
- max: 10,
95
- acquireTimeoutMillis: 5e3,
96
- idleTimeoutMillis: 3e4
97
- },
98
- this.config.pool
99
- );
100
- if (typeof this.config.connection === "string" && !this.config.connection.includes("json=true"))
101
- this.config.connection = `${this.config.connection}?json=true`;
102
- break;
103
- default:
104
- if (typeof this.config.client === "string") {
105
- if (this.config.client.startsWith("npm:")) {
106
- const client = await loadPackage(
107
- this.config.client.replace("npm:", "")
108
- );
109
- if (!client) throw Error(`Invalid client: ${this.config.client}`);
110
- if (typeof client === "function") {
111
- this.config.client = client;
112
- break;
113
- }
114
- if (client.default && typeof client.default === "function") {
115
- this.config.client = client.default;
116
- break;
117
- }
118
- throw Error(`Invalid client: ${this.config.client}`);
119
- }
120
- }
121
- break;
122
- }
123
- this.adapter = knex(this.config);
124
- if (this.config.client === "pg") await initPostgresTypeParsers();
125
- this.query = this.adapter;
126
- this.query.on("query", ({ sql, __knexQueryUid, bindings }) => {
127
- if (!__knexQueryUid) return;
128
- this.logger.time(`Knex${this.name}${__knexQueryUid}`);
129
- this.logger.debug(
130
- "[%s] query begin: %s %j",
131
- __knexQueryUid,
132
- sql,
133
- bindings
134
- );
135
- }).on("query-response", (response, { sql, __knexQueryUid, bindings }) => {
136
- if (!__knexQueryUid) return;
137
- this.logger.timeEnd(
138
- `Knex${this.name}${__knexQueryUid}`,
139
- "[%s] query done: %s %j %j",
140
- __knexQueryUid,
141
- sql,
142
- bindings,
143
- response
144
- );
145
- }).on("query-error", (_, { __knexQueryUid, sql, bindings }) => {
146
- if (!__knexQueryUid) return;
147
- this.logger.timeEnd(
148
- `Knex${this.name}${__knexQueryUid}`,
149
- "[%s] query failed: %s %j",
150
- __knexQueryUid,
151
- sql,
152
- bindings
153
- );
154
- });
155
- data.logger.debug("connected");
156
- global.FaasJS_Knex[this.name] = this;
157
- await next();
158
- }
159
- async onInvoke(data, next) {
160
- this.logger = data.logger;
161
- await next();
162
- }
163
- async raw(sql, bindings = []) {
164
- if (!this.adapter) throw Error("[Knex] Client not initialized.");
165
- return this.adapter.raw(sql, bindings);
166
- }
167
- /**
168
- * Wraps a transaction, returning a promise that resolves to the return value of the callback.
169
- *
170
- * - Support 'commit' and 'rollback' event.
171
- */
172
- async transaction(scope, config, options) {
173
- if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
174
- if (options?.trx) return scope(options.trx);
175
- const trx = await this.adapter.transaction(config);
176
- const trxId = randomUUID();
177
- this.logger.debug("[%s] transaction begin", trxId);
178
- try {
179
- const result = await scope(trx);
180
- if (trx.isCompleted()) {
181
- this.logger.debug("[%s] transaction has been finished in scope", trxId);
182
- return result;
183
- }
184
- this.logger.debug("[%s] transaction begin commit", trxId);
185
- await trx.commit();
186
- this.logger.debug("[%s] transaction committed: %j", trxId, result);
187
- trx.emit("commit");
188
- return result;
189
- } catch (error) {
190
- await trx.rollback(error);
191
- this.logger.error("[%s] transaction rollback: %s", trxId, error);
192
- trx.emit("rollback", error);
193
- throw error;
194
- }
195
- }
196
- schema() {
197
- if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
198
- return this.adapter.schema;
199
- }
200
- async quit() {
201
- if (!global.FaasJS_Knex[this.name]) return;
202
- try {
203
- await global.FaasJS_Knex[this.name].adapter.destroy();
204
- delete global.FaasJS_Knex[this.name];
205
- } catch (error) {
206
- console.error(error);
207
- }
208
- }
100
+ type = "knex";
101
+ name = Name;
102
+ config;
103
+ adapter;
104
+ query;
105
+ logger;
106
+ constructor(config) {
107
+ if (config) {
108
+ this.name = config.name || this.name;
109
+ this.config = config.config || Object.create(null);
110
+ } else this.config = Object.create(null);
111
+ }
112
+ async onMount(data, next) {
113
+ this.logger = data.logger;
114
+ const existsAdapter = getGlobalKnexAdapters()[this.name];
115
+ if (existsAdapter) {
116
+ this.config = existsAdapter.config;
117
+ this.adapter = existsAdapter.adapter;
118
+ this.query = this.adapter;
119
+ this.logger.debug("use exists adapter");
120
+ await next();
121
+ return;
122
+ }
123
+ const prefix = `SECRET_${this.name.toUpperCase()}_`;
124
+ for (let key in process.env) if (key.startsWith(prefix)) {
125
+ const value = process.env[key];
126
+ key = key.replace(prefix, "").toLowerCase();
127
+ if (typeof this.config[key] === "undefined") if (key.startsWith("connection_")) {
128
+ if (typeof this.config.connection === "string") continue;
129
+ if (!this.config.connection || typeof this.config.connection !== "object") this.config.connection = Object.create(null);
130
+ this.config.connection[key.replace("connection_", "")] = value;
131
+ } else this.config[key] = value;
132
+ }
133
+ if (data.config.plugins?.[this.name || this.type]?.config) this.config = deepMerge(data.config.plugins[this.name || this.type].config, this.config);
134
+ if (this.config.client === "pglite") {
135
+ const connectionEnvKeys = Object.keys(process.env).filter((key) => key.startsWith(`${prefix}CONNECTION_`));
136
+ if (connectionEnvKeys.length) throw Error(`[Knex] Invalid "pglite" env keys: ${connectionEnvKeys.join(", ")}. Use ${prefix}CONNECTION instead.`);
137
+ this.adapter = await createPgliteKnex(this.config);
138
+ } else {
139
+ switch (this.config.client) {
140
+ case "sqlite3":
141
+ this.config.client = "better-sqlite3";
142
+ this.config.useNullAsDefault = true;
143
+ break;
144
+ case "pg":
145
+ if (!this.config.pool) this.config.pool = Object.create(null);
146
+ this.config.pool = Object.assign({
147
+ propagateCreateError: false,
148
+ min: 0,
149
+ max: 10,
150
+ acquireTimeoutMillis: 5e3,
151
+ idleTimeoutMillis: 3e4
152
+ }, this.config.pool);
153
+ if (typeof this.config.connection === "string" && !this.config.connection.includes("json=true")) this.config.connection = `${this.config.connection}?json=true`;
154
+ break;
155
+ default:
156
+ if (typeof this.config.client === "string") {
157
+ if (this.config.client.startsWith("npm:")) {
158
+ const client = await loadPackage(this.config.client.replace("npm:", ""));
159
+ if (!client) throw Error(`Invalid client: ${this.config.client}`);
160
+ if (typeof client === "function") {
161
+ this.config.client = client;
162
+ break;
163
+ }
164
+ if (client.default && typeof client.default === "function") {
165
+ this.config.client = client.default;
166
+ break;
167
+ }
168
+ throw Error(`Invalid client: ${this.config.client}`);
169
+ }
170
+ }
171
+ break;
172
+ }
173
+ this.adapter = knex(this.config);
174
+ if (this.config.client === "pg") await initPostgresTypeParsers();
175
+ }
176
+ this.query = this.adapter;
177
+ this.query.on("query", ({ sql, __knexQueryUid, bindings }) => {
178
+ if (!__knexQueryUid) return;
179
+ this.logger.time(`Knex${this.name}${__knexQueryUid}`);
180
+ this.logger.debug("[%s] query begin: %s %j", __knexQueryUid, sql, bindings);
181
+ }).on("query-response", (response, { sql, __knexQueryUid, bindings }) => {
182
+ if (!__knexQueryUid) return;
183
+ this.logger.timeEnd(`Knex${this.name}${__knexQueryUid}`, "[%s] query done: %s %j %j", __knexQueryUid, sql, bindings, response);
184
+ }).on("query-error", (_, { __knexQueryUid, sql, bindings }) => {
185
+ if (!__knexQueryUid) return;
186
+ this.logger.timeEnd(`Knex${this.name}${__knexQueryUid}`, "[%s] query failed: %s %j", __knexQueryUid, sql, bindings);
187
+ });
188
+ data.logger.debug("connected");
189
+ getGlobalKnexAdapters()[this.name] = this;
190
+ await next();
191
+ }
192
+ async onInvoke(data, next) {
193
+ this.logger = data.logger;
194
+ await next();
195
+ }
196
+ async raw(sql, bindings = []) {
197
+ if (!this.adapter) throw Error("[Knex] Client not initialized.");
198
+ return this.adapter.raw(sql, bindings);
199
+ }
200
+ /**
201
+ * Wraps a transaction, returning a promise that resolves to the return value of the callback.
202
+ *
203
+ * - Support 'commit' and 'rollback' event.
204
+ */
205
+ async transaction(scope, config, options) {
206
+ if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
207
+ if (options?.trx) return scope(options.trx);
208
+ const trx = await this.adapter.transaction(config);
209
+ const trxId = randomUUID();
210
+ this.logger.debug("[%s] transaction begin", trxId);
211
+ try {
212
+ const result = await scope(trx);
213
+ if (trx.isCompleted()) {
214
+ this.logger.debug("[%s] transaction has been finished in scope", trxId);
215
+ return result;
216
+ }
217
+ this.logger.debug("[%s] transaction begin commit", trxId);
218
+ await trx.commit();
219
+ this.logger.debug("[%s] transaction committed: %j", trxId, result);
220
+ trx.emit("commit");
221
+ return result;
222
+ } catch (error) {
223
+ await trx.rollback(error);
224
+ this.logger.error("[%s] transaction rollback: %s", trxId, error);
225
+ trx.emit("rollback", error);
226
+ throw error;
227
+ }
228
+ }
229
+ schema() {
230
+ if (!this.adapter) throw Error(`[${this.name}] Client not initialized.`);
231
+ return this.adapter.schema;
232
+ }
233
+ async quit() {
234
+ const adapters = getGlobalKnexAdapters();
235
+ if (!adapters[this.name]) return;
236
+ try {
237
+ await adapters[this.name].adapter.destroy();
238
+ delete adapters[this.name];
239
+ } catch (error) {
240
+ console.error(error);
241
+ }
242
+ }
209
243
  };
210
244
  function useKnex(config) {
211
- const name = config?.name || Name;
212
- if (global.FaasJS_Knex[name]) return usePlugin(global.FaasJS_Knex[name]);
213
- return usePlugin(new Knex(config));
245
+ const name = config?.name || Name;
246
+ const adapters = getGlobalKnexAdapters();
247
+ if (adapters[name]) return usePlugin(adapters[name]);
248
+ return usePlugin(new Knex(config));
214
249
  }
215
250
  function query(table) {
216
- return useKnex().query(
217
- table
218
- );
251
+ return useKnex().query(table);
219
252
  }
220
253
  async function transaction(scope, config, options) {
221
- return useKnex().transaction(scope, config, options);
254
+ return useKnex().transaction(scope, config, options);
222
255
  }
223
256
  async function raw(sql, bindings = []) {
224
- return useKnex().raw(sql, bindings);
257
+ return useKnex().raw(sql, bindings);
225
258
  }
226
259
 
227
- export { Knex, initPostgresTypeParsers, originKnex, query, raw, transaction, useKnex };
260
+ //#endregion
261
+ //#region src/schema.ts
262
+ const DefaultMigratorConfig = {
263
+ directory: "./src/db/migrations",
264
+ extension: "ts"
265
+ };
266
+ /**
267
+ * Migration helper for FaasJS's knex plugin.
268
+ */
269
+ var KnexSchema = class {
270
+ knex;
271
+ constructor(knex) {
272
+ this.knex = knex;
273
+ }
274
+ getAdapter() {
275
+ if (!this.knex.adapter) throw Error(`[${this.knex.name}] Client not initialized.`);
276
+ return this.knex.adapter;
277
+ }
278
+ getMigratorConfig() {
279
+ return Object.assign({}, DefaultMigratorConfig, this.knex.config?.migrations || Object.create(null));
280
+ }
281
+ async migrateLatest() {
282
+ return this.getAdapter().migrate.latest(this.getMigratorConfig());
283
+ }
284
+ async migrateRollback() {
285
+ return this.getAdapter().migrate.rollback(this.getMigratorConfig());
286
+ }
287
+ async migrateStatus() {
288
+ return this.getAdapter().migrate.status(this.getMigratorConfig());
289
+ }
290
+ async migrateCurrentVersion() {
291
+ return this.getAdapter().migrate.currentVersion(this.getMigratorConfig());
292
+ }
293
+ async migrateMake(name) {
294
+ const migrationName = name?.trim();
295
+ if (!migrationName) throw Error("[KnexSchema] Missing migration name. Usage: npm run migrate:make -- create_users");
296
+ return this.getAdapter().migrate.make(migrationName, this.getMigratorConfig());
297
+ }
298
+ };
299
+
300
+ //#endregion
301
+ export { Knex, KnexSchema, createPgliteKnex, initPostgresTypeParsers, mountFaasKnex, originKnex, query, raw, transaction, unmountFaasKnex, useKnex };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/knex",
3
- "version": "8.0.0-beta.6",
3
+ "version": "8.0.0-beta.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -24,29 +24,26 @@
24
24
  },
25
25
  "funding": "https://github.com/sponsors/faasjs",
26
26
  "scripts": {
27
- "build": "tsup-node src/index.ts --config ../../tsup.config.ts"
27
+ "build": "tsdown src/index.ts --config ../../tsdown.config.ts"
28
28
  },
29
29
  "files": [
30
30
  "dist"
31
31
  ],
32
32
  "peerDependencies": {
33
33
  "knex": "*",
34
- "@faasjs/deep_merge": ">=8.0.0-beta.6",
35
- "@faasjs/func": ">=8.0.0-beta.6",
36
- "@faasjs/logger": ">=8.0.0-beta.6"
34
+ "@faasjs/func": ">=8.0.0-beta.8",
35
+ "@faasjs/logger": ">=8.0.0-beta.8",
36
+ "@faasjs/node-utils": ">=8.0.0-beta.8"
37
37
  },
38
38
  "devDependencies": {
39
39
  "knex": "*",
40
- "@faasjs/deep_merge": ">=8.0.0-beta.6",
41
- "@faasjs/func": ">=8.0.0-beta.6",
42
- "@faasjs/logger": ">=8.0.0-beta.6",
43
- "@faasjs/load": ">=8.0.0-beta.6",
44
- "@types/mysql": "*",
45
- "@types/node": "*",
40
+ "@faasjs/func": ">=8.0.0-beta.8",
41
+ "@faasjs/logger": ">=8.0.0-beta.8",
42
+ "@faasjs/node-utils": ">=8.0.0-beta.8",
43
+ "better-sqlite3": "*",
44
+ "knex-pglite": "*",
46
45
  "@types/pg": "*",
47
- "mysql": "*",
48
46
  "pg": "*",
49
- "better-sqlite3": "*",
50
47
  "cloudflare-d1-http-knex": "*"
51
48
  },
52
49
  "engines": {