shokupan 0.13.0 → 0.14.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/dist/{analyzer-BOtveWL-.cjs → analyzer-BZSVGTmP.cjs} +5 -4
- package/dist/analyzer-BZSVGTmP.cjs.map +1 -0
- package/dist/{analyzer-B0fMzeIo.js → analyzer-Faojwm7c.js} +5 -4
- package/dist/analyzer-Faojwm7c.js.map +1 -0
- package/dist/{analyzer.impl-CUDO6vpn.cjs → analyzer.impl-5aCqtook.cjs} +28 -11
- package/dist/analyzer.impl-5aCqtook.cjs.map +1 -0
- package/dist/{analyzer.impl-DmHe92Oi.js → analyzer.impl-COdN69gL.js} +28 -11
- package/dist/analyzer.impl-COdN69gL.js.map +1 -0
- package/dist/ast-analyzer-worker-C3jrQ8VR.js +184 -0
- package/dist/ast-analyzer-worker-C3jrQ8VR.js.map +1 -0
- package/dist/ast-analyzer-worker-D_uYkqmY.cjs +184 -0
- package/dist/ast-analyzer-worker-D_uYkqmY.cjs.map +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/context.d.ts +39 -4
- package/dist/decorators/di.d.ts +31 -0
- package/dist/decorators/hooks.d.ts +28 -0
- package/dist/decorators/http.d.ts +60 -0
- package/dist/decorators/index.d.ts +8 -0
- package/dist/decorators/mcp.d.ts +48 -0
- package/dist/decorators/util/container.d.ts +36 -0
- package/dist/decorators/websocket.d.ts +172 -0
- package/dist/index-BP7v0Hiv.cjs +12216 -0
- package/dist/index-BP7v0Hiv.cjs.map +1 -0
- package/dist/index-CUNBeZKj.js +12176 -0
- package/dist/index-CUNBeZKj.js.map +1 -0
- package/dist/index.cjs +137 -10518
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +137 -10477
- package/dist/index.js.map +1 -1
- package/dist/{json-parser-COdZ0fqY.cjs → json-parser-BA0mUgMF.cjs} +3 -3
- package/dist/json-parser-BA0mUgMF.cjs.map +1 -0
- package/dist/{json-parser-B3dnQmCC.js → json-parser-BFM-SnBR.js} +3 -3
- package/dist/json-parser-BFM-SnBR.js.map +1 -0
- package/dist/knex-DDPXR-sQ.js +218 -0
- package/dist/knex-DDPXR-sQ.js.map +1 -0
- package/dist/knex-DghF-jjm.cjs +240 -0
- package/dist/knex-DghF-jjm.cjs.map +1 -0
- package/dist/level-BU87Jbus.js +184 -0
- package/dist/level-BU87Jbus.js.map +1 -0
- package/dist/level-DNFl2n-m.cjs +184 -0
- package/dist/level-DNFl2n-m.cjs.map +1 -0
- package/dist/plugins/application/api-explorer/static/explorer-client.mjs +54 -28
- package/dist/plugins/application/asyncapi/plugin.d.ts +1 -0
- package/dist/plugins/application/asyncapi/static/asyncapi-client.mjs +22 -11
- package/dist/plugins/application/dashboard/fetch-interceptor.d.ts +3 -1
- package/dist/plugins/application/dashboard/metrics-collector.d.ts +5 -3
- package/dist/plugins/application/dashboard/plugin.d.ts +36 -3
- package/dist/plugins/application/dashboard/static/requests.js +517 -53
- package/dist/plugins/application/dashboard/static/tabs.js +2 -2
- package/dist/plugins/application/error-view/index.d.ts +25 -0
- package/dist/plugins/application/error-view/reason-phrases.d.ts +1 -0
- package/dist/plugins/application/openapi/analyzer.d.ts +3 -1
- package/dist/plugins/application/openapi/analyzer.impl.d.ts +4 -2
- package/dist/router.d.ts +56 -21
- package/dist/shokupan.d.ts +25 -11
- package/dist/sqlite-CLrcTkti.js +180 -0
- package/dist/sqlite-CLrcTkti.js.map +1 -0
- package/dist/sqlite-n7FQ6Ja6.cjs +180 -0
- package/dist/sqlite-n7FQ6Ja6.cjs.map +1 -0
- package/dist/surreal-6QONU6xa.cjs +210 -0
- package/dist/surreal-6QONU6xa.cjs.map +1 -0
- package/dist/surreal-w7DeGVI-.js +188 -0
- package/dist/surreal-w7DeGVI-.js.map +1 -0
- package/dist/util/adapter/datastore/knex.d.ts +29 -0
- package/dist/util/adapter/datastore/level.d.ts +26 -0
- package/dist/util/adapter/datastore/sqlite.d.ts +24 -0
- package/dist/util/adapter/datastore/surreal.d.ts +29 -0
- package/dist/util/adapter/datastore.d.ts +59 -0
- package/dist/util/adapter/h3.d.ts +8 -0
- package/dist/util/adapter/index.d.ts +1 -0
- package/dist/util/ast-analyzer-worker.d.ts +77 -0
- package/dist/util/ast-worker-thread.d.ts +1 -0
- package/dist/util/cookie-parser.d.ts +6 -0
- package/dist/util/env-loader.d.ts +7 -0
- package/dist/util/html.d.ts +15 -0
- package/dist/util/ide.d.ts +9 -0
- package/dist/util/logger.d.ts +25 -0
- package/dist/util/query-string.d.ts +8 -0
- package/dist/util/response-transformer.d.ts +87 -0
- package/dist/util/symbol.d.ts +1 -0
- package/dist/util/types.d.ts +116 -42
- package/dist/websocket.d.ts +163 -0
- package/package.json +27 -1
- package/dist/analyzer-B0fMzeIo.js.map +0 -1
- package/dist/analyzer-BOtveWL-.cjs.map +0 -1
- package/dist/analyzer.impl-CUDO6vpn.cjs.map +0 -1
- package/dist/analyzer.impl-DmHe92Oi.js.map +0 -1
- package/dist/json-parser-B3dnQmCC.js.map +0 -1
- package/dist/json-parser-COdZ0fqY.cjs.map +0 -1
- package/dist/plugins/application/error-view/views/error.d.ts +0 -2
- package/dist/plugins/application/error-view/views/status.d.ts +0 -2
- package/dist/util/decorators.d.ts +0 -134
- package/dist/util/di.d.ts +0 -13
- /package/dist/{util → decorators/util}/metadata.d.ts +0 -0
- /package/dist/{util → decorators/util}/stack.d.ts +0 -0
|
@@ -10,7 +10,7 @@ function getJSONParser(parserType = "native") {
|
|
|
10
10
|
const lib = require("parse-json");
|
|
11
11
|
parseJsonLib = lib.default || lib;
|
|
12
12
|
} catch (e) {
|
|
13
|
-
|
|
13
|
+
if (process.env.NODE_ENV !== "test") process.stderr.write("parse-json not installed, falling back to native JSON.parse. Install with: bun add parse-json\n");
|
|
14
14
|
return JSON.parse;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -21,7 +21,7 @@ function getJSONParser(parserType = "native") {
|
|
|
21
21
|
const lib = require("secure-json-parse");
|
|
22
22
|
secureJsonParseLib = lib.parse || lib.default?.parse || lib;
|
|
23
23
|
} catch (e) {
|
|
24
|
-
|
|
24
|
+
if (process.env.NODE_ENV !== "test") process.stderr.write("secure-json-parse not installed, falling back to native JSON.parse. Install with: bun add secure-json-parse\n");
|
|
25
25
|
return JSON.parse;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -32,4 +32,4 @@ function getJSONParser(parserType = "native") {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
exports.getJSONParser = getJSONParser;
|
|
35
|
-
//# sourceMappingURL=json-parser-
|
|
35
|
+
//# sourceMappingURL=json-parser-BA0mUgMF.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-parser-BA0mUgMF.cjs","sources":["../src/util/json-parser.ts"],"sourcesContent":["/**\n * JSON parser utilities for Shokupan\n * Supports multiple JSON parsing libraries with different performance characteristics\n */\n\ntype JSONParser = (text: string) => any;\n\nlet parseJsonLib: any;\nlet secureJsonParseLib: any;\n\n/**\n * Get the appropriate JSON parser based on the configuration\n * @param parserType - The type of parser to use\n * @returns A JSON parsing function\n */\nexport function getJSONParser(parserType: 'native' | 'parse-json' | 'secure-json-parse' = 'native'): JSONParser {\n switch (parserType) {\n case 'parse-json':\n if (!parseJsonLib) {\n try {\n const lib = require('parse-json');\n // parse-json exports a default function\n parseJsonLib = lib.default || lib;\n } catch (e) {\n if (process.env.NODE_ENV !== 'test') process.stderr.write('parse-json not installed, falling back to native JSON.parse. Install with: bun add parse-json\\n');\n return JSON.parse;\n }\n }\n return parseJsonLib;\n\n case 'secure-json-parse':\n if (!secureJsonParseLib) {\n try {\n const lib = require('secure-json-parse');\n secureJsonParseLib = lib.parse || lib.default?.parse || lib;\n } catch (e) {\n if (process.env.NODE_ENV !== 'test') process.stderr.write('secure-json-parse not installed, falling back to native JSON.parse. Install with: bun add secure-json-parse\\n');\n return JSON.parse;\n }\n }\n return secureJsonParseLib;\n\n case 'native':\n default:\n return JSON.parse;\n }\n}\n"],"names":[],"mappings":";;AAOA,IAAI;AACJ,IAAI;AAOG,SAAS,cAAc,aAA4D,UAAsB;AAC5G,UAAQ,YAAA;AAAA,IACJ,KAAK;AACD,UAAI,CAAC,cAAc;AACf,YAAI;AACA,gBAAM,MAAM,QAAQ,YAAY;AAEhC,yBAAe,IAAI,WAAW;AAAA,QAClC,SAAS,GAAG;AACR,cAAI,QAAQ,IAAI,aAAa,OAAQ,SAAQ,OAAO,MAAM,iGAAiG;AAC3J,iBAAO,KAAK;AAAA,QAChB;AAAA,MACJ;AACA,aAAO;AAAA,IAEX,KAAK;AACD,UAAI,CAAC,oBAAoB;AACrB,YAAI;AACA,gBAAM,MAAM,QAAQ,mBAAmB;AACvC,+BAAqB,IAAI,SAAS,IAAI,SAAS,SAAS;AAAA,QAC5D,SAAS,GAAG;AACR,cAAI,QAAQ,IAAI,aAAa,OAAQ,SAAQ,OAAO,MAAM,+GAA+G;AACzK,iBAAO,KAAK;AAAA,QAChB;AAAA,MACJ;AACA,aAAO;AAAA,IAEX,KAAK;AAAA,IACL;AACI,aAAO,KAAK;AAAA,EAAA;AAExB;;"}
|
|
@@ -8,7 +8,7 @@ function getJSONParser(parserType = "native") {
|
|
|
8
8
|
const lib = require("parse-json");
|
|
9
9
|
parseJsonLib = lib.default || lib;
|
|
10
10
|
} catch (e) {
|
|
11
|
-
|
|
11
|
+
if (process.env.NODE_ENV !== "test") process.stderr.write("parse-json not installed, falling back to native JSON.parse. Install with: bun add parse-json\n");
|
|
12
12
|
return JSON.parse;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -19,7 +19,7 @@ function getJSONParser(parserType = "native") {
|
|
|
19
19
|
const lib = require("secure-json-parse");
|
|
20
20
|
secureJsonParseLib = lib.parse || lib.default?.parse || lib;
|
|
21
21
|
} catch (e) {
|
|
22
|
-
|
|
22
|
+
if (process.env.NODE_ENV !== "test") process.stderr.write("secure-json-parse not installed, falling back to native JSON.parse. Install with: bun add secure-json-parse\n");
|
|
23
23
|
return JSON.parse;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -32,4 +32,4 @@ function getJSONParser(parserType = "native") {
|
|
|
32
32
|
export {
|
|
33
33
|
getJSONParser
|
|
34
34
|
};
|
|
35
|
-
//# sourceMappingURL=json-parser-
|
|
35
|
+
//# sourceMappingURL=json-parser-BFM-SnBR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-parser-BFM-SnBR.js","sources":["../src/util/json-parser.ts"],"sourcesContent":["/**\n * JSON parser utilities for Shokupan\n * Supports multiple JSON parsing libraries with different performance characteristics\n */\n\ntype JSONParser = (text: string) => any;\n\nlet parseJsonLib: any;\nlet secureJsonParseLib: any;\n\n/**\n * Get the appropriate JSON parser based on the configuration\n * @param parserType - The type of parser to use\n * @returns A JSON parsing function\n */\nexport function getJSONParser(parserType: 'native' | 'parse-json' | 'secure-json-parse' = 'native'): JSONParser {\n switch (parserType) {\n case 'parse-json':\n if (!parseJsonLib) {\n try {\n const lib = require('parse-json');\n // parse-json exports a default function\n parseJsonLib = lib.default || lib;\n } catch (e) {\n if (process.env.NODE_ENV !== 'test') process.stderr.write('parse-json not installed, falling back to native JSON.parse. Install with: bun add parse-json\\n');\n return JSON.parse;\n }\n }\n return parseJsonLib;\n\n case 'secure-json-parse':\n if (!secureJsonParseLib) {\n try {\n const lib = require('secure-json-parse');\n secureJsonParseLib = lib.parse || lib.default?.parse || lib;\n } catch (e) {\n if (process.env.NODE_ENV !== 'test') process.stderr.write('secure-json-parse not installed, falling back to native JSON.parse. Install with: bun add secure-json-parse\\n');\n return JSON.parse;\n }\n }\n return secureJsonParseLib;\n\n case 'native':\n default:\n return JSON.parse;\n }\n}\n"],"names":[],"mappings":"AAOA,IAAI;AACJ,IAAI;AAOG,SAAS,cAAc,aAA4D,UAAsB;AAC5G,UAAQ,YAAA;AAAA,IACJ,KAAK;AACD,UAAI,CAAC,cAAc;AACf,YAAI;AACA,gBAAM,MAAM,QAAQ,YAAY;AAEhC,yBAAe,IAAI,WAAW;AAAA,QAClC,SAAS,GAAG;AACR,cAAI,QAAQ,IAAI,aAAa,OAAQ,SAAQ,OAAO,MAAM,iGAAiG;AAC3J,iBAAO,KAAK;AAAA,QAChB;AAAA,MACJ;AACA,aAAO;AAAA,IAEX,KAAK;AACD,UAAI,CAAC,oBAAoB;AACrB,YAAI;AACA,gBAAM,MAAM,QAAQ,mBAAmB;AACvC,+BAAqB,IAAI,SAAS,IAAI,SAAS,SAAS;AAAA,QAC5D,SAAS,GAAG;AACR,cAAI,QAAQ,IAAI,aAAa,OAAQ,SAAQ,OAAO,MAAM,+GAA+G;AACzK,iBAAO,KAAK;AAAA,QAChB;AAAA,MACJ;AACA,aAAO;AAAA,IAEX,KAAK;AAAA,IACL;AACI,aAAO,KAAK;AAAA,EAAA;AAExB;"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { c as createLogger } from "./index-CUNBeZKj.js";
|
|
2
|
+
class KnexAdapter {
|
|
3
|
+
// 'json' or 'jsonb'
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
}
|
|
7
|
+
name = "knex";
|
|
8
|
+
db;
|
|
9
|
+
logger = createLogger("knex-adapter");
|
|
10
|
+
jsonColumnType = "json";
|
|
11
|
+
async connect() {
|
|
12
|
+
const { default: knex } = await import("knex");
|
|
13
|
+
this.db = knex(this.options);
|
|
14
|
+
try {
|
|
15
|
+
await this.db.raw("SELECT 1");
|
|
16
|
+
this.detectDialectFeatures();
|
|
17
|
+
} catch (e) {
|
|
18
|
+
this.logger.error("KnexAdapter", "Failed to connect to IO SQL database", e);
|
|
19
|
+
throw e;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
detectDialectFeatures() {
|
|
23
|
+
const client = this.db.client.driverName || this.db.client.dialect;
|
|
24
|
+
if (client === "pg" || client === "postgres") {
|
|
25
|
+
this.jsonColumnType = "jsonb";
|
|
26
|
+
} else {
|
|
27
|
+
this.jsonColumnType = "json";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async disconnect() {
|
|
31
|
+
await this.db.destroy();
|
|
32
|
+
}
|
|
33
|
+
async setupSchema() {
|
|
34
|
+
const tables = [
|
|
35
|
+
"failed_requests",
|
|
36
|
+
"sessions",
|
|
37
|
+
"users",
|
|
38
|
+
"idempotency",
|
|
39
|
+
"middleware_tracking",
|
|
40
|
+
"requests",
|
|
41
|
+
"metrics"
|
|
42
|
+
];
|
|
43
|
+
for (const table of tables) {
|
|
44
|
+
await this.ensureTable(table);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async ensureTable(table) {
|
|
48
|
+
const exists = await this.db.schema.hasTable(table);
|
|
49
|
+
if (!exists) {
|
|
50
|
+
await this.db.schema.createTable(table, (t) => {
|
|
51
|
+
t.string("id").primary();
|
|
52
|
+
if (this.jsonColumnType === "jsonb") {
|
|
53
|
+
t.jsonb("data");
|
|
54
|
+
} else {
|
|
55
|
+
t.json("data");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Helper to extract JSON field safely based on dialect
|
|
61
|
+
// Note: Knex ref replacement for JSON is handy.
|
|
62
|
+
jsonRef(field) {
|
|
63
|
+
const client = this.db.client.driverName || this.db.client.dialect;
|
|
64
|
+
if (client === "sqlite3" || client === "sqlite") {
|
|
65
|
+
return this.db.raw(`json_extract(data, '$.${field}')`);
|
|
66
|
+
}
|
|
67
|
+
if (client === "pg" || client === "postgres") {
|
|
68
|
+
return this.db.raw(`data->>'${field}'`);
|
|
69
|
+
}
|
|
70
|
+
if (client === "mysql" || client === "mysql2") {
|
|
71
|
+
return this.db.raw(`data->>'$.${field}'`);
|
|
72
|
+
}
|
|
73
|
+
return `data->${field}`;
|
|
74
|
+
}
|
|
75
|
+
async get(table, id) {
|
|
76
|
+
try {
|
|
77
|
+
const res = await this.db(table).where("id", id).first();
|
|
78
|
+
if (!res) return null;
|
|
79
|
+
let data = res.data;
|
|
80
|
+
if (typeof data === "string") {
|
|
81
|
+
try {
|
|
82
|
+
data = JSON.parse(data);
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { ...data, id };
|
|
87
|
+
} catch (e) {
|
|
88
|
+
if (e.message?.includes("no such table")) return null;
|
|
89
|
+
throw e;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async create(table, id, data) {
|
|
93
|
+
await this.ensureTable(table);
|
|
94
|
+
const payload = JSON.stringify(data);
|
|
95
|
+
await this.db(table).insert({
|
|
96
|
+
id,
|
|
97
|
+
data: payload
|
|
98
|
+
});
|
|
99
|
+
return data;
|
|
100
|
+
}
|
|
101
|
+
async update(table, id, data) {
|
|
102
|
+
return await this.db.transaction(async (trx) => {
|
|
103
|
+
const row = await trx(table).where("id", id).first();
|
|
104
|
+
if (!row) throw new Error(`Record ${id} does not exist`);
|
|
105
|
+
let current = row.data;
|
|
106
|
+
if (typeof current === "string") current = JSON.parse(current);
|
|
107
|
+
const updated = { ...current, ...data };
|
|
108
|
+
await trx(table).where("id", id).update({
|
|
109
|
+
data: JSON.stringify(updated)
|
|
110
|
+
});
|
|
111
|
+
return updated;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async upsert(table, id, data) {
|
|
115
|
+
await this.ensureTable(table);
|
|
116
|
+
const payload = JSON.stringify(data);
|
|
117
|
+
await this.db(table).insert({ id, data: payload }).onConflict("id").merge({ data: payload });
|
|
118
|
+
return data;
|
|
119
|
+
}
|
|
120
|
+
async delete(table, id) {
|
|
121
|
+
try {
|
|
122
|
+
await this.db(table).where("id", id).delete();
|
|
123
|
+
} catch (e) {
|
|
124
|
+
if (e.message?.includes("no such table")) return;
|
|
125
|
+
throw e;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async count(table, query) {
|
|
129
|
+
try {
|
|
130
|
+
const q = this.applyQuery(this.db(table), query);
|
|
131
|
+
const res = await q.count({ count: "*" }).first();
|
|
132
|
+
return res ? Number(res.count) : 0;
|
|
133
|
+
} catch (e) {
|
|
134
|
+
if (e.message?.includes("no such table")) return 0;
|
|
135
|
+
throw e;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async deleteMany(table, query) {
|
|
139
|
+
try {
|
|
140
|
+
const q = this.db(table);
|
|
141
|
+
this.applyFilters(q, query);
|
|
142
|
+
if (query?.sort || query?.limit) {
|
|
143
|
+
const ids = await this.findManyIDs(table, query);
|
|
144
|
+
if (ids.length > 0) {
|
|
145
|
+
await this.db(table).whereIn("id", ids).delete();
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await q.delete();
|
|
150
|
+
} catch (e) {
|
|
151
|
+
if (e.message?.includes("no such table")) return;
|
|
152
|
+
throw e;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async findManyIDs(table, query) {
|
|
156
|
+
const q = this.db(table).select("id");
|
|
157
|
+
this.applyQuery(q, query);
|
|
158
|
+
const res = await q;
|
|
159
|
+
return res.map((r) => r.id);
|
|
160
|
+
}
|
|
161
|
+
async findMany(table, query) {
|
|
162
|
+
try {
|
|
163
|
+
const q = this.db(table).select("*");
|
|
164
|
+
this.applyQuery(q, query);
|
|
165
|
+
const res = await q;
|
|
166
|
+
return res.map((r) => {
|
|
167
|
+
let d = r.data;
|
|
168
|
+
if (typeof d === "string") {
|
|
169
|
+
try {
|
|
170
|
+
d = JSON.parse(d);
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { ...d, id: r.id };
|
|
175
|
+
});
|
|
176
|
+
} catch (e) {
|
|
177
|
+
if (e.message?.includes("no such table")) return [];
|
|
178
|
+
throw e;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
applyFilters(q, query) {
|
|
182
|
+
if (!query) return;
|
|
183
|
+
if (query.where) {
|
|
184
|
+
for (const [k, v] of Object.entries(query.where)) {
|
|
185
|
+
q.where(this.jsonRef(k), "=", v);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (query.lt) {
|
|
189
|
+
for (const [k, v] of Object.entries(query.lt)) {
|
|
190
|
+
q.where(this.jsonRef(k), "<", v);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (query.gt) {
|
|
194
|
+
for (const [k, v] of Object.entries(query.gt)) {
|
|
195
|
+
q.where(this.jsonRef(k), ">", v);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
applyQuery(q, query) {
|
|
200
|
+
this.applyFilters(q, query);
|
|
201
|
+
if (query?.sort) {
|
|
202
|
+
for (const [k, v] of Object.entries(query.sort)) {
|
|
203
|
+
q.orderBy(this.jsonRef(k), v);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (query?.offset) {
|
|
207
|
+
q.offset(query.offset);
|
|
208
|
+
}
|
|
209
|
+
if (query?.limit) {
|
|
210
|
+
q.limit(query.limit);
|
|
211
|
+
}
|
|
212
|
+
return q;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export {
|
|
216
|
+
KnexAdapter
|
|
217
|
+
};
|
|
218
|
+
//# sourceMappingURL=knex-DDPXR-sQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knex-DDPXR-sQ.js","sources":["../src/util/adapter/datastore/knex.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport { createLogger } from '../../logger';\nimport type { DatastoreAdapter, QueryOptions } from '../datastore';\n\nexport interface KnexAdapterOptions extends Knex.Config {\n // Standard Knex config\n}\n\nexport class KnexAdapter implements DatastoreAdapter {\n name = 'knex';\n private db!: Knex;\n private logger = createLogger('knex-adapter');\n private jsonColumnType = 'json'; // 'json' or 'jsonb'\n\n constructor(private options: KnexAdapterOptions) { }\n\n async connect(): Promise<void> {\n const { default: knex } = await import('knex');\n this.db = knex(this.options);\n\n // Test connection\n try {\n await this.db.raw('SELECT 1');\n this.detectDialectFeatures();\n } catch (e) {\n this.logger.error('KnexAdapter', \"Failed to connect to IO SQL database\", e);\n throw e;\n }\n }\n\n private detectDialectFeatures() {\n const client = (this.db.client as any).driverName || (this.db.client as any).dialect;\n if (client === 'pg' || client === 'postgres') {\n this.jsonColumnType = 'jsonb';\n } else {\n this.jsonColumnType = 'json';\n }\n }\n\n async disconnect(): Promise<void> {\n await this.db.destroy();\n }\n\n async setupSchema(): Promise<void> {\n // Create standard tables if they don't exist\n const tables = [\n 'failed_requests',\n 'sessions',\n 'users',\n 'idempotency',\n 'middleware_tracking',\n 'requests',\n 'metrics'\n ];\n\n for (const table of tables) {\n await this.ensureTable(table);\n }\n }\n\n private async ensureTable(table: string) {\n const exists = await this.db.schema.hasTable(table);\n if (!exists) {\n await this.db.schema.createTable(table, (t) => {\n t.string('id').primary();\n if (this.jsonColumnType === 'jsonb') {\n t.jsonb('data');\n } else {\n t.json('data');\n }\n // We might want some standard indexes later (e.g. timestamp inside data?)\n // But for generic schemaless, tough to index inside JSON without expressions.\n });\n }\n }\n\n // Helper to extract JSON field safely based on dialect\n // Note: Knex ref replacement for JSON is handy.\n private jsonRef(field: string): any {\n // SQLite: json_extract(data, '$.field')\n // PG: data->>'field'\n // MySQL: data->>'$.field'\n\n // This is complex to generalize perfectly.\n // For basic generic usage, let's try to handle common top-level keys.\n const client = (this.db.client as any).driverName || (this.db.client as any).dialect;\n\n if (client === 'sqlite3' || client === 'sqlite') {\n return this.db.raw(`json_extract(data, '$.${field}')`);\n }\n if (client === 'pg' || client === 'postgres') {\n return this.db.raw(`data->>'${field}'`);\n }\n if (client === 'mysql' || client === 'mysql2') {\n return this.db.raw(`data->>'$.${field}'`);\n }\n\n // Fallback (might fail)\n return `data->${field}`;\n }\n\n async get<T>(table: string, id: string): Promise<T | null> {\n // Ensure table check might be skipped for performance in prod? \n // For now, assume setupSchema cleared it or we fail cleanly.\n try {\n const res = await this.db(table).where('id', id).first();\n if (!res) return null;\n\n // Check if data is string or object (sqlite returns string for JSON sometimes if not parsed)\n let data = res.data;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); } catch { }\n }\n return { ...data, id }; // Ensure ID is part of it\n } catch (e: any) {\n if (e.message?.includes('no such table')) return null;\n throw e;\n }\n }\n\n async create<T>(table: string, id: string, data: T): Promise<T> {\n await this.ensureTable(table);\n // data usually contains id too, or we merge it.\n const payload = JSON.stringify(data);\n await this.db(table).insert({\n id,\n data: payload\n });\n return data; // Return what was passed\n }\n\n async update<T>(table: string, id: string, data: Partial<T>): Promise<T> {\n // Read-Modify-Write transaction preferred for JSON merge if DB doesn't support deep merge easily\n // PG has || operator or jsonb_set, SQLite json_patch.\n // Generic approach: Read, Merge JS, Write.\n\n return await this.db.transaction(async trx => {\n const row = await trx(table).where('id', id).first();\n if (!row) throw new Error(`Record ${id} does not exist`);\n\n let current = row.data;\n if (typeof current === 'string') current = JSON.parse(current);\n\n const updated = { ...current, ...data };\n await trx(table).where('id', id).update({\n data: JSON.stringify(updated)\n });\n return updated;\n });\n }\n\n async upsert<T>(table: string, id: string, data: T): Promise<T> {\n await this.ensureTable(table);\n const payload = JSON.stringify(data);\n\n // Knex .onConflict().merge() works for standard upserts\n await this.db(table)\n .insert({ id, data: payload })\n .onConflict('id')\n .merge({ data: payload });\n\n return data;\n }\n\n async delete(table: string, id: string): Promise<void> {\n try {\n await this.db(table).where('id', id).delete();\n } catch (e: any) {\n if (e.message?.includes('no such table')) return;\n throw e;\n }\n }\n\n async count(table: string, query?: QueryOptions): Promise<number> {\n try {\n const q = this.applyQuery(this.db(table), query);\n const res = await q.count({ count: '*' }).first();\n // res could be { count: 5 } or { count: '5' }\n return res ? Number(res.count) : 0;\n } catch (e: any) {\n if (e.message?.includes('no such table')) return 0;\n throw e;\n }\n }\n\n async deleteMany(table: string, query?: QueryOptions): Promise<void> {\n try {\n // delete via subquery or direct delete with where\n const q = this.db(table);\n this.applyFilters(q, query);\n // applyFilters adds WHERE clauses.\n\n // Sorting/Limit on delete is dialect specific.\n // MySQL/SQLite support DELETE ... ORDER BY ... LIMIT ...\n // PG does not directly (needs CTE).\n\n // Safe Generic Approach: Select IDs then Delete IDs.\n if (query?.sort || query?.limit) {\n const ids = await this.findManyIDs(table, query);\n if (ids.length > 0) {\n await this.db(table).whereIn('id', ids).delete();\n }\n return;\n }\n\n // Direct delete if just filters\n await q.delete();\n } catch (e: any) {\n if (e.message?.includes('no such table')) return;\n throw e;\n }\n }\n\n private async findManyIDs(table: string, query?: QueryOptions): Promise<string[]> {\n const q = this.db(table).select('id');\n this.applyQuery(q, query);\n const res = await q;\n return res.map((r: any) => r.id);\n }\n\n async findMany<T>(table: string, query?: QueryOptions): Promise<T[]> {\n try {\n const q = this.db(table).select('*');\n this.applyQuery(q, query);\n const res = await q;\n return res.map((r: any) => {\n let d = r.data;\n if (typeof d === 'string') {\n try { d = JSON.parse(d); } catch { }\n }\n return { ...d, id: r.id };\n });\n } catch (e: any) {\n if (e.message?.includes('no such table')) return [];\n throw e;\n }\n }\n\n private applyFilters(q: Knex.QueryBuilder, query?: QueryOptions) {\n if (!query) return;\n\n if (query.where) {\n for (const [k, v] of Object.entries(query.where)) {\n q.where(this.jsonRef(k), '=', v);\n }\n }\n if (query.lt) {\n for (const [k, v] of Object.entries(query.lt)) {\n q.where(this.jsonRef(k), '<', v);\n }\n }\n if (query.gt) {\n for (const [k, v] of Object.entries(query.gt)) {\n q.where(this.jsonRef(k), '>', v);\n }\n }\n }\n\n private applyQuery(q: Knex.QueryBuilder, query?: QueryOptions) {\n this.applyFilters(q, query);\n\n if (query?.sort) {\n for (const [k, v] of Object.entries(query.sort)) {\n q.orderBy(this.jsonRef(k), v);\n }\n }\n if (query?.offset) {\n q.offset(query.offset);\n }\n if (query?.limit) {\n q.limit(query.limit);\n }\n return q;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
25
|
+
const index = require("./index-BP7v0Hiv.cjs");
|
|
26
|
+
class KnexAdapter {
|
|
27
|
+
// 'json' or 'jsonb'
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.options = options;
|
|
30
|
+
}
|
|
31
|
+
name = "knex";
|
|
32
|
+
db;
|
|
33
|
+
logger = index.createLogger("knex-adapter");
|
|
34
|
+
jsonColumnType = "json";
|
|
35
|
+
async connect() {
|
|
36
|
+
const { default: knex } = await import("knex");
|
|
37
|
+
this.db = knex(this.options);
|
|
38
|
+
try {
|
|
39
|
+
await this.db.raw("SELECT 1");
|
|
40
|
+
this.detectDialectFeatures();
|
|
41
|
+
} catch (e) {
|
|
42
|
+
this.logger.error("KnexAdapter", "Failed to connect to IO SQL database", e);
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
detectDialectFeatures() {
|
|
47
|
+
const client = this.db.client.driverName || this.db.client.dialect;
|
|
48
|
+
if (client === "pg" || client === "postgres") {
|
|
49
|
+
this.jsonColumnType = "jsonb";
|
|
50
|
+
} else {
|
|
51
|
+
this.jsonColumnType = "json";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async disconnect() {
|
|
55
|
+
await this.db.destroy();
|
|
56
|
+
}
|
|
57
|
+
async setupSchema() {
|
|
58
|
+
const tables = [
|
|
59
|
+
"failed_requests",
|
|
60
|
+
"sessions",
|
|
61
|
+
"users",
|
|
62
|
+
"idempotency",
|
|
63
|
+
"middleware_tracking",
|
|
64
|
+
"requests",
|
|
65
|
+
"metrics"
|
|
66
|
+
];
|
|
67
|
+
for (const table of tables) {
|
|
68
|
+
await this.ensureTable(table);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async ensureTable(table) {
|
|
72
|
+
const exists = await this.db.schema.hasTable(table);
|
|
73
|
+
if (!exists) {
|
|
74
|
+
await this.db.schema.createTable(table, (t) => {
|
|
75
|
+
t.string("id").primary();
|
|
76
|
+
if (this.jsonColumnType === "jsonb") {
|
|
77
|
+
t.jsonb("data");
|
|
78
|
+
} else {
|
|
79
|
+
t.json("data");
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Helper to extract JSON field safely based on dialect
|
|
85
|
+
// Note: Knex ref replacement for JSON is handy.
|
|
86
|
+
jsonRef(field) {
|
|
87
|
+
const client = this.db.client.driverName || this.db.client.dialect;
|
|
88
|
+
if (client === "sqlite3" || client === "sqlite") {
|
|
89
|
+
return this.db.raw(`json_extract(data, '$.${field}')`);
|
|
90
|
+
}
|
|
91
|
+
if (client === "pg" || client === "postgres") {
|
|
92
|
+
return this.db.raw(`data->>'${field}'`);
|
|
93
|
+
}
|
|
94
|
+
if (client === "mysql" || client === "mysql2") {
|
|
95
|
+
return this.db.raw(`data->>'$.${field}'`);
|
|
96
|
+
}
|
|
97
|
+
return `data->${field}`;
|
|
98
|
+
}
|
|
99
|
+
async get(table, id) {
|
|
100
|
+
try {
|
|
101
|
+
const res = await this.db(table).where("id", id).first();
|
|
102
|
+
if (!res) return null;
|
|
103
|
+
let data = res.data;
|
|
104
|
+
if (typeof data === "string") {
|
|
105
|
+
try {
|
|
106
|
+
data = JSON.parse(data);
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { ...data, id };
|
|
111
|
+
} catch (e) {
|
|
112
|
+
if (e.message?.includes("no such table")) return null;
|
|
113
|
+
throw e;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async create(table, id, data) {
|
|
117
|
+
await this.ensureTable(table);
|
|
118
|
+
const payload = JSON.stringify(data);
|
|
119
|
+
await this.db(table).insert({
|
|
120
|
+
id,
|
|
121
|
+
data: payload
|
|
122
|
+
});
|
|
123
|
+
return data;
|
|
124
|
+
}
|
|
125
|
+
async update(table, id, data) {
|
|
126
|
+
return await this.db.transaction(async (trx) => {
|
|
127
|
+
const row = await trx(table).where("id", id).first();
|
|
128
|
+
if (!row) throw new Error(`Record ${id} does not exist`);
|
|
129
|
+
let current = row.data;
|
|
130
|
+
if (typeof current === "string") current = JSON.parse(current);
|
|
131
|
+
const updated = { ...current, ...data };
|
|
132
|
+
await trx(table).where("id", id).update({
|
|
133
|
+
data: JSON.stringify(updated)
|
|
134
|
+
});
|
|
135
|
+
return updated;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async upsert(table, id, data) {
|
|
139
|
+
await this.ensureTable(table);
|
|
140
|
+
const payload = JSON.stringify(data);
|
|
141
|
+
await this.db(table).insert({ id, data: payload }).onConflict("id").merge({ data: payload });
|
|
142
|
+
return data;
|
|
143
|
+
}
|
|
144
|
+
async delete(table, id) {
|
|
145
|
+
try {
|
|
146
|
+
await this.db(table).where("id", id).delete();
|
|
147
|
+
} catch (e) {
|
|
148
|
+
if (e.message?.includes("no such table")) return;
|
|
149
|
+
throw e;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async count(table, query) {
|
|
153
|
+
try {
|
|
154
|
+
const q = this.applyQuery(this.db(table), query);
|
|
155
|
+
const res = await q.count({ count: "*" }).first();
|
|
156
|
+
return res ? Number(res.count) : 0;
|
|
157
|
+
} catch (e) {
|
|
158
|
+
if (e.message?.includes("no such table")) return 0;
|
|
159
|
+
throw e;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async deleteMany(table, query) {
|
|
163
|
+
try {
|
|
164
|
+
const q = this.db(table);
|
|
165
|
+
this.applyFilters(q, query);
|
|
166
|
+
if (query?.sort || query?.limit) {
|
|
167
|
+
const ids = await this.findManyIDs(table, query);
|
|
168
|
+
if (ids.length > 0) {
|
|
169
|
+
await this.db(table).whereIn("id", ids).delete();
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
await q.delete();
|
|
174
|
+
} catch (e) {
|
|
175
|
+
if (e.message?.includes("no such table")) return;
|
|
176
|
+
throw e;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async findManyIDs(table, query) {
|
|
180
|
+
const q = this.db(table).select("id");
|
|
181
|
+
this.applyQuery(q, query);
|
|
182
|
+
const res = await q;
|
|
183
|
+
return res.map((r) => r.id);
|
|
184
|
+
}
|
|
185
|
+
async findMany(table, query) {
|
|
186
|
+
try {
|
|
187
|
+
const q = this.db(table).select("*");
|
|
188
|
+
this.applyQuery(q, query);
|
|
189
|
+
const res = await q;
|
|
190
|
+
return res.map((r) => {
|
|
191
|
+
let d = r.data;
|
|
192
|
+
if (typeof d === "string") {
|
|
193
|
+
try {
|
|
194
|
+
d = JSON.parse(d);
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return { ...d, id: r.id };
|
|
199
|
+
});
|
|
200
|
+
} catch (e) {
|
|
201
|
+
if (e.message?.includes("no such table")) return [];
|
|
202
|
+
throw e;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
applyFilters(q, query) {
|
|
206
|
+
if (!query) return;
|
|
207
|
+
if (query.where) {
|
|
208
|
+
for (const [k, v] of Object.entries(query.where)) {
|
|
209
|
+
q.where(this.jsonRef(k), "=", v);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (query.lt) {
|
|
213
|
+
for (const [k, v] of Object.entries(query.lt)) {
|
|
214
|
+
q.where(this.jsonRef(k), "<", v);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (query.gt) {
|
|
218
|
+
for (const [k, v] of Object.entries(query.gt)) {
|
|
219
|
+
q.where(this.jsonRef(k), ">", v);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
applyQuery(q, query) {
|
|
224
|
+
this.applyFilters(q, query);
|
|
225
|
+
if (query?.sort) {
|
|
226
|
+
for (const [k, v] of Object.entries(query.sort)) {
|
|
227
|
+
q.orderBy(this.jsonRef(k), v);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (query?.offset) {
|
|
231
|
+
q.offset(query.offset);
|
|
232
|
+
}
|
|
233
|
+
if (query?.limit) {
|
|
234
|
+
q.limit(query.limit);
|
|
235
|
+
}
|
|
236
|
+
return q;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
exports.KnexAdapter = KnexAdapter;
|
|
240
|
+
//# sourceMappingURL=knex-DghF-jjm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knex-DghF-jjm.cjs","sources":["../src/util/adapter/datastore/knex.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport { createLogger } from '../../logger';\nimport type { DatastoreAdapter, QueryOptions } from '../datastore';\n\nexport interface KnexAdapterOptions extends Knex.Config {\n // Standard Knex config\n}\n\nexport class KnexAdapter implements DatastoreAdapter {\n name = 'knex';\n private db!: Knex;\n private logger = createLogger('knex-adapter');\n private jsonColumnType = 'json'; // 'json' or 'jsonb'\n\n constructor(private options: KnexAdapterOptions) { }\n\n async connect(): Promise<void> {\n const { default: knex } = await import('knex');\n this.db = knex(this.options);\n\n // Test connection\n try {\n await this.db.raw('SELECT 1');\n this.detectDialectFeatures();\n } catch (e) {\n this.logger.error('KnexAdapter', \"Failed to connect to IO SQL database\", e);\n throw e;\n }\n }\n\n private detectDialectFeatures() {\n const client = (this.db.client as any).driverName || (this.db.client as any).dialect;\n if (client === 'pg' || client === 'postgres') {\n this.jsonColumnType = 'jsonb';\n } else {\n this.jsonColumnType = 'json';\n }\n }\n\n async disconnect(): Promise<void> {\n await this.db.destroy();\n }\n\n async setupSchema(): Promise<void> {\n // Create standard tables if they don't exist\n const tables = [\n 'failed_requests',\n 'sessions',\n 'users',\n 'idempotency',\n 'middleware_tracking',\n 'requests',\n 'metrics'\n ];\n\n for (const table of tables) {\n await this.ensureTable(table);\n }\n }\n\n private async ensureTable(table: string) {\n const exists = await this.db.schema.hasTable(table);\n if (!exists) {\n await this.db.schema.createTable(table, (t) => {\n t.string('id').primary();\n if (this.jsonColumnType === 'jsonb') {\n t.jsonb('data');\n } else {\n t.json('data');\n }\n // We might want some standard indexes later (e.g. timestamp inside data?)\n // But for generic schemaless, tough to index inside JSON without expressions.\n });\n }\n }\n\n // Helper to extract JSON field safely based on dialect\n // Note: Knex ref replacement for JSON is handy.\n private jsonRef(field: string): any {\n // SQLite: json_extract(data, '$.field')\n // PG: data->>'field'\n // MySQL: data->>'$.field'\n\n // This is complex to generalize perfectly.\n // For basic generic usage, let's try to handle common top-level keys.\n const client = (this.db.client as any).driverName || (this.db.client as any).dialect;\n\n if (client === 'sqlite3' || client === 'sqlite') {\n return this.db.raw(`json_extract(data, '$.${field}')`);\n }\n if (client === 'pg' || client === 'postgres') {\n return this.db.raw(`data->>'${field}'`);\n }\n if (client === 'mysql' || client === 'mysql2') {\n return this.db.raw(`data->>'$.${field}'`);\n }\n\n // Fallback (might fail)\n return `data->${field}`;\n }\n\n async get<T>(table: string, id: string): Promise<T | null> {\n // Ensure table check might be skipped for performance in prod? \n // For now, assume setupSchema cleared it or we fail cleanly.\n try {\n const res = await this.db(table).where('id', id).first();\n if (!res) return null;\n\n // Check if data is string or object (sqlite returns string for JSON sometimes if not parsed)\n let data = res.data;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); } catch { }\n }\n return { ...data, id }; // Ensure ID is part of it\n } catch (e: any) {\n if (e.message?.includes('no such table')) return null;\n throw e;\n }\n }\n\n async create<T>(table: string, id: string, data: T): Promise<T> {\n await this.ensureTable(table);\n // data usually contains id too, or we merge it.\n const payload = JSON.stringify(data);\n await this.db(table).insert({\n id,\n data: payload\n });\n return data; // Return what was passed\n }\n\n async update<T>(table: string, id: string, data: Partial<T>): Promise<T> {\n // Read-Modify-Write transaction preferred for JSON merge if DB doesn't support deep merge easily\n // PG has || operator or jsonb_set, SQLite json_patch.\n // Generic approach: Read, Merge JS, Write.\n\n return await this.db.transaction(async trx => {\n const row = await trx(table).where('id', id).first();\n if (!row) throw new Error(`Record ${id} does not exist`);\n\n let current = row.data;\n if (typeof current === 'string') current = JSON.parse(current);\n\n const updated = { ...current, ...data };\n await trx(table).where('id', id).update({\n data: JSON.stringify(updated)\n });\n return updated;\n });\n }\n\n async upsert<T>(table: string, id: string, data: T): Promise<T> {\n await this.ensureTable(table);\n const payload = JSON.stringify(data);\n\n // Knex .onConflict().merge() works for standard upserts\n await this.db(table)\n .insert({ id, data: payload })\n .onConflict('id')\n .merge({ data: payload });\n\n return data;\n }\n\n async delete(table: string, id: string): Promise<void> {\n try {\n await this.db(table).where('id', id).delete();\n } catch (e: any) {\n if (e.message?.includes('no such table')) return;\n throw e;\n }\n }\n\n async count(table: string, query?: QueryOptions): Promise<number> {\n try {\n const q = this.applyQuery(this.db(table), query);\n const res = await q.count({ count: '*' }).first();\n // res could be { count: 5 } or { count: '5' }\n return res ? Number(res.count) : 0;\n } catch (e: any) {\n if (e.message?.includes('no such table')) return 0;\n throw e;\n }\n }\n\n async deleteMany(table: string, query?: QueryOptions): Promise<void> {\n try {\n // delete via subquery or direct delete with where\n const q = this.db(table);\n this.applyFilters(q, query);\n // applyFilters adds WHERE clauses.\n\n // Sorting/Limit on delete is dialect specific.\n // MySQL/SQLite support DELETE ... ORDER BY ... LIMIT ...\n // PG does not directly (needs CTE).\n\n // Safe Generic Approach: Select IDs then Delete IDs.\n if (query?.sort || query?.limit) {\n const ids = await this.findManyIDs(table, query);\n if (ids.length > 0) {\n await this.db(table).whereIn('id', ids).delete();\n }\n return;\n }\n\n // Direct delete if just filters\n await q.delete();\n } catch (e: any) {\n if (e.message?.includes('no such table')) return;\n throw e;\n }\n }\n\n private async findManyIDs(table: string, query?: QueryOptions): Promise<string[]> {\n const q = this.db(table).select('id');\n this.applyQuery(q, query);\n const res = await q;\n return res.map((r: any) => r.id);\n }\n\n async findMany<T>(table: string, query?: QueryOptions): Promise<T[]> {\n try {\n const q = this.db(table).select('*');\n this.applyQuery(q, query);\n const res = await q;\n return res.map((r: any) => {\n let d = r.data;\n if (typeof d === 'string') {\n try { d = JSON.parse(d); } catch { }\n }\n return { ...d, id: r.id };\n });\n } catch (e: any) {\n if (e.message?.includes('no such table')) return [];\n throw e;\n }\n }\n\n private applyFilters(q: Knex.QueryBuilder, query?: QueryOptions) {\n if (!query) return;\n\n if (query.where) {\n for (const [k, v] of Object.entries(query.where)) {\n q.where(this.jsonRef(k), '=', v);\n }\n }\n if (query.lt) {\n for (const [k, v] of Object.entries(query.lt)) {\n q.where(this.jsonRef(k), '<', v);\n }\n }\n if (query.gt) {\n for (const [k, v] of Object.entries(query.gt)) {\n q.where(this.jsonRef(k), '>', v);\n }\n }\n }\n\n private applyQuery(q: Knex.QueryBuilder, query?: QueryOptions) {\n this.applyFilters(q, query);\n\n if (query?.sort) {\n for (const [k, v] of Object.entries(query.sort)) {\n q.orderBy(this.jsonRef(k), v);\n }\n }\n if (query?.offset) {\n q.offset(query.offset);\n }\n if (query?.limit) {\n q.limit(query.limit);\n }\n return q;\n }\n}\n"],"names":["createLogger"],"mappings":"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"}
|