@xansql/bridge 1.0.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/base.js +127 -0
- package/base.js.map +1 -0
- package/base.mjs +123 -0
- package/base.mjs.map +1 -0
- package/index.d.ts +13 -0
- package/index.js +99 -0
- package/index.js.map +1 -0
- package/index.mjs +97 -0
- package/index.mjs.map +1 -0
- package/package.json +34 -0
- package/readme.md +359 -0
- package/server.d.ts +19 -0
- package/server.js +111 -0
- package/server.js.map +1 -0
- package/server.mjs +109 -0
- package/server.mjs.map +1 -0
- package/types.d.ts +17 -0
package/base.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var securequ = require('securequ');
|
|
4
|
+
var core = require('@xansql/core');
|
|
5
|
+
|
|
6
|
+
let secretCache = null;
|
|
7
|
+
const makeSecret = async (xansql) => {
|
|
8
|
+
if (secretCache)
|
|
9
|
+
return secretCache;
|
|
10
|
+
const models = xansql.models;
|
|
11
|
+
let uid = [];
|
|
12
|
+
for (let model of models.values()) {
|
|
13
|
+
uid.push(model.table);
|
|
14
|
+
for (let column in model.schema) {
|
|
15
|
+
const field = model.schema[column];
|
|
16
|
+
if (!core.Foreign.isArray(model.schema[column])) {
|
|
17
|
+
uid.push(column);
|
|
18
|
+
const meta = field.meta || {};
|
|
19
|
+
const keys = Object.keys(meta);
|
|
20
|
+
if (keys.length) {
|
|
21
|
+
keys.sort();
|
|
22
|
+
uid.push(...keys);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
uid = Array.from(new Set(uid)); // unique
|
|
28
|
+
uid = uid.sort();
|
|
29
|
+
secretCache = await securequ.crypto.hash(uid.join(""));
|
|
30
|
+
return secretCache;
|
|
31
|
+
};
|
|
32
|
+
const makePath = async (path, xansql) => {
|
|
33
|
+
const secret = await makeSecret(xansql);
|
|
34
|
+
const gen = `/${await securequ.crypto.hash(path + secret)}`;
|
|
35
|
+
return gen;
|
|
36
|
+
};
|
|
37
|
+
const sqlparser = (sql) => {
|
|
38
|
+
const normalized = sql.trim().replace(/\s+/g, " ");
|
|
39
|
+
// Detect the primary verb (first SQL keyword)
|
|
40
|
+
const verbMatch = normalized.match(/^(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE|REPLACE|DESCRIBE|DESC|SHOW|USE)\b/i);
|
|
41
|
+
let verb = "UNKNOWN";
|
|
42
|
+
if (verbMatch) {
|
|
43
|
+
const v = verbMatch[1].toUpperCase();
|
|
44
|
+
verb = v === "DESC" ? "DESCRIBE" : v;
|
|
45
|
+
}
|
|
46
|
+
// Matches identifiers: table, `table`, "table", [table]
|
|
47
|
+
const IDENT = `([\\\`"\\[]?)([A-Za-z0-9_.-]+)\\1`;
|
|
48
|
+
const extractTable = () => {
|
|
49
|
+
switch (verb) {
|
|
50
|
+
case "SELECT": {
|
|
51
|
+
// 1. Normal SELECT ... FROM table
|
|
52
|
+
const normal = normalized.match(/\bFROM\s+([`"\[]?)([A-Za-z0-9_.-]+)\1/i);
|
|
53
|
+
if (normal && normal[2] !== "(") {
|
|
54
|
+
return normal[2]; // real table
|
|
55
|
+
}
|
|
56
|
+
// 2. Extract real tables inside a subquery: SELECT ... FROM ( SELECT ... FROM table )
|
|
57
|
+
const inner = normalized.match(/\(\s*SELECT[\s\S]+?\bFROM\s+([`"\[]?)([A-Za-z0-9_.-]+)\1/i);
|
|
58
|
+
if (inner)
|
|
59
|
+
return inner[2];
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
case "INSERT": {
|
|
63
|
+
const match = new RegExp(`\\bINTO\\s+${IDENT}`, "i").exec(normalized);
|
|
64
|
+
return match ? match[2] : null;
|
|
65
|
+
}
|
|
66
|
+
case "UPDATE": {
|
|
67
|
+
const match = new RegExp(`^UPDATE\\s+${IDENT}`, "i").exec(normalized);
|
|
68
|
+
return match ? match[2] : null;
|
|
69
|
+
}
|
|
70
|
+
case "DELETE": {
|
|
71
|
+
const match = new RegExp(`\\bFROM\\s+${IDENT}`, "i").exec(normalized);
|
|
72
|
+
return match ? match[2] : null;
|
|
73
|
+
}
|
|
74
|
+
// CREATE TABLE tableName
|
|
75
|
+
case "CREATE": {
|
|
76
|
+
const match = new RegExp(`\\bCREATE\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
77
|
+
return match ? match[2] : null;
|
|
78
|
+
}
|
|
79
|
+
// DROP TABLE tableName
|
|
80
|
+
case "DROP": {
|
|
81
|
+
const match = new RegExp(`\\bDROP\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
82
|
+
return match ? match[2] : null;
|
|
83
|
+
}
|
|
84
|
+
// ALTER TABLE tableName
|
|
85
|
+
case "ALTER": {
|
|
86
|
+
const match = new RegExp(`\\bALTER\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
87
|
+
return match ? match[2] : null;
|
|
88
|
+
}
|
|
89
|
+
// TRUNCATE TABLE tableName
|
|
90
|
+
case "TRUNCATE": {
|
|
91
|
+
const match = new RegExp(`\\bTRUNCATE\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
92
|
+
return match ? match[2] : null;
|
|
93
|
+
}
|
|
94
|
+
// REPLACE INTO tableName
|
|
95
|
+
case "REPLACE": {
|
|
96
|
+
const match = new RegExp(`\\bREPLACE\\s+INTO\\s+${IDENT}`, "i").exec(normalized);
|
|
97
|
+
return match ? match[2] : null;
|
|
98
|
+
}
|
|
99
|
+
// DESCRIBE tableName
|
|
100
|
+
case "DESCRIBE": {
|
|
101
|
+
const match = new RegExp(`\\b(DESCRIBE|DESC)\\s+${IDENT}`, "i").exec(normalized);
|
|
102
|
+
return match ? match[2] : null;
|
|
103
|
+
}
|
|
104
|
+
// SHOW TABLES / SHOW DATABASES => no table
|
|
105
|
+
case "SHOW": {
|
|
106
|
+
const match = new RegExp(`\\bSHOW\\s+${IDENT}`, "i").exec(normalized);
|
|
107
|
+
return match ? match[2] : null; // e.g. SHOW TABLES
|
|
108
|
+
}
|
|
109
|
+
// USE databaseName
|
|
110
|
+
case "USE": {
|
|
111
|
+
const match = new RegExp(`\\bUSE\\s+${IDENT}`, "i").exec(normalized);
|
|
112
|
+
return match ? match[2] : null;
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
action: verb,
|
|
120
|
+
table: extractTable(),
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
exports.makePath = makePath;
|
|
125
|
+
exports.makeSecret = makeSecret;
|
|
126
|
+
exports.sqlparser = sqlparser;
|
|
127
|
+
//# sourceMappingURL=base.js.map
|
package/base.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sources":["../src/base.ts"],"sourcesContent":["import { crypto } from \"securequ\";\nimport { Xansql, Foreign } from \"@xansql/core\";\n\nlet secretCache: string | null = null;\nexport const makeSecret = async (xansql: Xansql) => {\n if (secretCache) return secretCache;\n const models = xansql.models\n let uid = []\n for (let model of models.values()) {\n uid.push(model.table)\n for (let column in model.schema) {\n const field = model.schema[column]\n if (!Foreign.isArray(model.schema[column])) {\n uid.push(column)\n const meta = field.meta || {}\n const keys = Object.keys(meta)\n if (keys.length) {\n keys.sort()\n uid.push(...keys)\n }\n }\n }\n }\n uid = Array.from(new Set(uid)) // unique\n uid = uid.sort()\n\n secretCache = await crypto.hash(uid.join(\"\"))\n return secretCache;\n}\n\nexport const makePath = async (path: string, xansql: Xansql) => {\n const secret = await makeSecret(xansql)\n const gen = `/${await crypto.hash(path + secret)}`\n return gen;\n}\n\n\nexport type XansqlBridgeAction =\n | \"SELECT\"\n | \"INSERT\"\n | \"UPDATE\"\n | \"DELETE\"\n | \"DROP\"\n | \"ALTER\"\n | \"CREATE\"\n | \"TRUNCATE\"\n | \"REPLACE\"\n | \"DESCRIBE\"\n | \"SHOW\"\n | \"USE\"\n | \"UNKNOWN\";\n\nexport interface SqlParserResult {\n action: XansqlBridgeAction;\n table: string | null;\n}\n\nexport const sqlparser = (sql: string): SqlParserResult => {\n const normalized = sql.trim().replace(/\\s+/g, \" \");\n\n // Detect the primary verb (first SQL keyword)\n const verbMatch = normalized.match(\n /^(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE|REPLACE|DESCRIBE|DESC|SHOW|USE)\\b/i\n );\n\n let verb: XansqlBridgeAction = \"UNKNOWN\";\n\n if (verbMatch) {\n const v = verbMatch[1].toUpperCase();\n verb = v === \"DESC\" ? \"DESCRIBE\" : (v as XansqlBridgeAction);\n }\n\n // Matches identifiers: table, `table`, \"table\", [table]\n const IDENT = `([\\\\\\`\"\\\\[]?)([A-Za-z0-9_.-]+)\\\\1`;\n\n const extractTable = (): string | null => {\n switch (verb) {\n case \"SELECT\": {\n // 1. Normal SELECT ... FROM table\n const normal = normalized.match(/\\bFROM\\s+([`\"\\[]?)([A-Za-z0-9_.-]+)\\1/i);\n if (normal && normal[2] !== \"(\") {\n return normal[2]; // real table\n }\n\n // 2. Extract real tables inside a subquery: SELECT ... FROM ( SELECT ... FROM table )\n const inner = normalized.match(\n /\\(\\s*SELECT[\\s\\S]+?\\bFROM\\s+([`\"\\[]?)([A-Za-z0-9_.-]+)\\1/i\n );\n if (inner) return inner[2];\n\n return null;\n }\n case \"INSERT\": {\n const match = new RegExp(`\\\\bINTO\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n case \"UPDATE\": {\n const match = new RegExp(`^UPDATE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n case \"DELETE\": {\n const match = new RegExp(`\\\\bFROM\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // CREATE TABLE tableName\n case \"CREATE\": {\n const match = new RegExp(`\\\\bCREATE\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // DROP TABLE tableName\n case \"DROP\": {\n const match = new RegExp(`\\\\bDROP\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // ALTER TABLE tableName\n case \"ALTER\": {\n const match = new RegExp(`\\\\bALTER\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // TRUNCATE TABLE tableName\n case \"TRUNCATE\": {\n const match = new RegExp(`\\\\bTRUNCATE\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // REPLACE INTO tableName\n case \"REPLACE\": {\n const match = new RegExp(`\\\\bREPLACE\\\\s+INTO\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // DESCRIBE tableName\n case \"DESCRIBE\": {\n const match = new RegExp(`\\\\b(DESCRIBE|DESC)\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // SHOW TABLES / SHOW DATABASES => no table\n case \"SHOW\": {\n const match = new RegExp(`\\\\bSHOW\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null; // e.g. SHOW TABLES\n }\n\n // USE databaseName\n case \"USE\": {\n const match = new RegExp(`\\\\bUSE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n default:\n return null;\n }\n };\n\n return {\n action: verb,\n table: extractTable(),\n };\n};\n"],"names":["Foreign","crypto"],"mappings":"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"}
|
package/base.mjs
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { crypto } from 'securequ';
|
|
2
|
+
import { Foreign } from '@xansql/core';
|
|
3
|
+
|
|
4
|
+
let secretCache = null;
|
|
5
|
+
const makeSecret = async (xansql) => {
|
|
6
|
+
if (secretCache)
|
|
7
|
+
return secretCache;
|
|
8
|
+
const models = xansql.models;
|
|
9
|
+
let uid = [];
|
|
10
|
+
for (let model of models.values()) {
|
|
11
|
+
uid.push(model.table);
|
|
12
|
+
for (let column in model.schema) {
|
|
13
|
+
const field = model.schema[column];
|
|
14
|
+
if (!Foreign.isArray(model.schema[column])) {
|
|
15
|
+
uid.push(column);
|
|
16
|
+
const meta = field.meta || {};
|
|
17
|
+
const keys = Object.keys(meta);
|
|
18
|
+
if (keys.length) {
|
|
19
|
+
keys.sort();
|
|
20
|
+
uid.push(...keys);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
uid = Array.from(new Set(uid)); // unique
|
|
26
|
+
uid = uid.sort();
|
|
27
|
+
secretCache = await crypto.hash(uid.join(""));
|
|
28
|
+
return secretCache;
|
|
29
|
+
};
|
|
30
|
+
const makePath = async (path, xansql) => {
|
|
31
|
+
const secret = await makeSecret(xansql);
|
|
32
|
+
const gen = `/${await crypto.hash(path + secret)}`;
|
|
33
|
+
return gen;
|
|
34
|
+
};
|
|
35
|
+
const sqlparser = (sql) => {
|
|
36
|
+
const normalized = sql.trim().replace(/\s+/g, " ");
|
|
37
|
+
// Detect the primary verb (first SQL keyword)
|
|
38
|
+
const verbMatch = normalized.match(/^(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE|REPLACE|DESCRIBE|DESC|SHOW|USE)\b/i);
|
|
39
|
+
let verb = "UNKNOWN";
|
|
40
|
+
if (verbMatch) {
|
|
41
|
+
const v = verbMatch[1].toUpperCase();
|
|
42
|
+
verb = v === "DESC" ? "DESCRIBE" : v;
|
|
43
|
+
}
|
|
44
|
+
// Matches identifiers: table, `table`, "table", [table]
|
|
45
|
+
const IDENT = `([\\\`"\\[]?)([A-Za-z0-9_.-]+)\\1`;
|
|
46
|
+
const extractTable = () => {
|
|
47
|
+
switch (verb) {
|
|
48
|
+
case "SELECT": {
|
|
49
|
+
// 1. Normal SELECT ... FROM table
|
|
50
|
+
const normal = normalized.match(/\bFROM\s+([`"\[]?)([A-Za-z0-9_.-]+)\1/i);
|
|
51
|
+
if (normal && normal[2] !== "(") {
|
|
52
|
+
return normal[2]; // real table
|
|
53
|
+
}
|
|
54
|
+
// 2. Extract real tables inside a subquery: SELECT ... FROM ( SELECT ... FROM table )
|
|
55
|
+
const inner = normalized.match(/\(\s*SELECT[\s\S]+?\bFROM\s+([`"\[]?)([A-Za-z0-9_.-]+)\1/i);
|
|
56
|
+
if (inner)
|
|
57
|
+
return inner[2];
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
case "INSERT": {
|
|
61
|
+
const match = new RegExp(`\\bINTO\\s+${IDENT}`, "i").exec(normalized);
|
|
62
|
+
return match ? match[2] : null;
|
|
63
|
+
}
|
|
64
|
+
case "UPDATE": {
|
|
65
|
+
const match = new RegExp(`^UPDATE\\s+${IDENT}`, "i").exec(normalized);
|
|
66
|
+
return match ? match[2] : null;
|
|
67
|
+
}
|
|
68
|
+
case "DELETE": {
|
|
69
|
+
const match = new RegExp(`\\bFROM\\s+${IDENT}`, "i").exec(normalized);
|
|
70
|
+
return match ? match[2] : null;
|
|
71
|
+
}
|
|
72
|
+
// CREATE TABLE tableName
|
|
73
|
+
case "CREATE": {
|
|
74
|
+
const match = new RegExp(`\\bCREATE\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
75
|
+
return match ? match[2] : null;
|
|
76
|
+
}
|
|
77
|
+
// DROP TABLE tableName
|
|
78
|
+
case "DROP": {
|
|
79
|
+
const match = new RegExp(`\\bDROP\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
80
|
+
return match ? match[2] : null;
|
|
81
|
+
}
|
|
82
|
+
// ALTER TABLE tableName
|
|
83
|
+
case "ALTER": {
|
|
84
|
+
const match = new RegExp(`\\bALTER\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
85
|
+
return match ? match[2] : null;
|
|
86
|
+
}
|
|
87
|
+
// TRUNCATE TABLE tableName
|
|
88
|
+
case "TRUNCATE": {
|
|
89
|
+
const match = new RegExp(`\\bTRUNCATE\\s+TABLE\\s+${IDENT}`, "i").exec(normalized);
|
|
90
|
+
return match ? match[2] : null;
|
|
91
|
+
}
|
|
92
|
+
// REPLACE INTO tableName
|
|
93
|
+
case "REPLACE": {
|
|
94
|
+
const match = new RegExp(`\\bREPLACE\\s+INTO\\s+${IDENT}`, "i").exec(normalized);
|
|
95
|
+
return match ? match[2] : null;
|
|
96
|
+
}
|
|
97
|
+
// DESCRIBE tableName
|
|
98
|
+
case "DESCRIBE": {
|
|
99
|
+
const match = new RegExp(`\\b(DESCRIBE|DESC)\\s+${IDENT}`, "i").exec(normalized);
|
|
100
|
+
return match ? match[2] : null;
|
|
101
|
+
}
|
|
102
|
+
// SHOW TABLES / SHOW DATABASES => no table
|
|
103
|
+
case "SHOW": {
|
|
104
|
+
const match = new RegExp(`\\bSHOW\\s+${IDENT}`, "i").exec(normalized);
|
|
105
|
+
return match ? match[2] : null; // e.g. SHOW TABLES
|
|
106
|
+
}
|
|
107
|
+
// USE databaseName
|
|
108
|
+
case "USE": {
|
|
109
|
+
const match = new RegExp(`\\bUSE\\s+${IDENT}`, "i").exec(normalized);
|
|
110
|
+
return match ? match[2] : null;
|
|
111
|
+
}
|
|
112
|
+
default:
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
action: verb,
|
|
118
|
+
table: extractTable(),
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export { makePath, makeSecret, sqlparser };
|
|
123
|
+
//# sourceMappingURL=base.mjs.map
|
package/base.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.mjs","sources":["../src/base.ts"],"sourcesContent":["import { crypto } from \"securequ\";\nimport { Xansql, Foreign } from \"@xansql/core\";\n\nlet secretCache: string | null = null;\nexport const makeSecret = async (xansql: Xansql) => {\n if (secretCache) return secretCache;\n const models = xansql.models\n let uid = []\n for (let model of models.values()) {\n uid.push(model.table)\n for (let column in model.schema) {\n const field = model.schema[column]\n if (!Foreign.isArray(model.schema[column])) {\n uid.push(column)\n const meta = field.meta || {}\n const keys = Object.keys(meta)\n if (keys.length) {\n keys.sort()\n uid.push(...keys)\n }\n }\n }\n }\n uid = Array.from(new Set(uid)) // unique\n uid = uid.sort()\n\n secretCache = await crypto.hash(uid.join(\"\"))\n return secretCache;\n}\n\nexport const makePath = async (path: string, xansql: Xansql) => {\n const secret = await makeSecret(xansql)\n const gen = `/${await crypto.hash(path + secret)}`\n return gen;\n}\n\n\nexport type XansqlBridgeAction =\n | \"SELECT\"\n | \"INSERT\"\n | \"UPDATE\"\n | \"DELETE\"\n | \"DROP\"\n | \"ALTER\"\n | \"CREATE\"\n | \"TRUNCATE\"\n | \"REPLACE\"\n | \"DESCRIBE\"\n | \"SHOW\"\n | \"USE\"\n | \"UNKNOWN\";\n\nexport interface SqlParserResult {\n action: XansqlBridgeAction;\n table: string | null;\n}\n\nexport const sqlparser = (sql: string): SqlParserResult => {\n const normalized = sql.trim().replace(/\\s+/g, \" \");\n\n // Detect the primary verb (first SQL keyword)\n const verbMatch = normalized.match(\n /^(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE|REPLACE|DESCRIBE|DESC|SHOW|USE)\\b/i\n );\n\n let verb: XansqlBridgeAction = \"UNKNOWN\";\n\n if (verbMatch) {\n const v = verbMatch[1].toUpperCase();\n verb = v === \"DESC\" ? \"DESCRIBE\" : (v as XansqlBridgeAction);\n }\n\n // Matches identifiers: table, `table`, \"table\", [table]\n const IDENT = `([\\\\\\`\"\\\\[]?)([A-Za-z0-9_.-]+)\\\\1`;\n\n const extractTable = (): string | null => {\n switch (verb) {\n case \"SELECT\": {\n // 1. Normal SELECT ... FROM table\n const normal = normalized.match(/\\bFROM\\s+([`\"\\[]?)([A-Za-z0-9_.-]+)\\1/i);\n if (normal && normal[2] !== \"(\") {\n return normal[2]; // real table\n }\n\n // 2. Extract real tables inside a subquery: SELECT ... FROM ( SELECT ... FROM table )\n const inner = normalized.match(\n /\\(\\s*SELECT[\\s\\S]+?\\bFROM\\s+([`\"\\[]?)([A-Za-z0-9_.-]+)\\1/i\n );\n if (inner) return inner[2];\n\n return null;\n }\n case \"INSERT\": {\n const match = new RegExp(`\\\\bINTO\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n case \"UPDATE\": {\n const match = new RegExp(`^UPDATE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n case \"DELETE\": {\n const match = new RegExp(`\\\\bFROM\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // CREATE TABLE tableName\n case \"CREATE\": {\n const match = new RegExp(`\\\\bCREATE\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // DROP TABLE tableName\n case \"DROP\": {\n const match = new RegExp(`\\\\bDROP\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // ALTER TABLE tableName\n case \"ALTER\": {\n const match = new RegExp(`\\\\bALTER\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // TRUNCATE TABLE tableName\n case \"TRUNCATE\": {\n const match = new RegExp(`\\\\bTRUNCATE\\\\s+TABLE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // REPLACE INTO tableName\n case \"REPLACE\": {\n const match = new RegExp(`\\\\bREPLACE\\\\s+INTO\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // DESCRIBE tableName\n case \"DESCRIBE\": {\n const match = new RegExp(`\\\\b(DESCRIBE|DESC)\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n // SHOW TABLES / SHOW DATABASES => no table\n case \"SHOW\": {\n const match = new RegExp(`\\\\bSHOW\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null; // e.g. SHOW TABLES\n }\n\n // USE databaseName\n case \"USE\": {\n const match = new RegExp(`\\\\bUSE\\\\s+${IDENT}`, \"i\").exec(normalized);\n return match ? match[2] : null;\n }\n\n default:\n return null;\n }\n };\n\n return {\n action: verb,\n table: extractTable(),\n };\n};\n"],"names":[],"mappings":";;;AAGA,IAAI,WAAW,GAAkB,IAAI;MACxB,UAAU,GAAG,OAAO,MAAc,KAAI;AAChD,IAAA,IAAI,WAAW;AAAE,QAAA,OAAO,WAAW;AACnC,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM;IAC5B,IAAI,GAAG,GAAG,EAAE;IACZ,KAAK,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE;AAChC,QAAA,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AACrB,QAAA,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE;YAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;AAClC,YAAA,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE;AACzC,gBAAA,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;AAChB,gBAAA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE;gBAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AAC9B,gBAAA,IAAI,IAAI,CAAC,MAAM,EAAE;oBACd,IAAI,CAAC,IAAI,EAAE;AACX,oBAAA,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;gBACpB;YACH;QACH;IACH;AACA,IAAA,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;AAC9B,IAAA,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE;AAEhB,IAAA,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC7C,IAAA,OAAO,WAAW;AACrB;AAEO,MAAM,QAAQ,GAAG,OAAO,IAAY,EAAE,MAAc,KAAI;AAC5D,IAAA,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC;AACvC,IAAA,MAAM,GAAG,GAAG,CAAA,CAAA,EAAI,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE;AAClD,IAAA,OAAO,GAAG;AACb;AAuBO,MAAM,SAAS,GAAG,CAAC,GAAW,KAAqB;AACvD,IAAA,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;;IAGlD,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAC/B,6FAA6F,CAC/F;IAED,IAAI,IAAI,GAAuB,SAAS;IAExC,IAAI,SAAS,EAAE;QACZ,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;AACpC,QAAA,IAAI,GAAG,CAAC,KAAK,MAAM,GAAG,UAAU,GAAI,CAAwB;IAC/D;;IAGA,MAAM,KAAK,GAAG,CAAA,iCAAA,CAAmC;IAEjD,MAAM,YAAY,GAAG,MAAoB;QACtC,QAAQ,IAAI;YACT,KAAK,QAAQ,EAAE;;gBAEZ,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,wCAAwC,CAAC;gBACzE,IAAI,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;AAC9B,oBAAA,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;gBACpB;;gBAGA,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAC3B,2DAA2D,CAC7D;AACD,gBAAA,IAAI,KAAK;AAAE,oBAAA,OAAO,KAAK,CAAC,CAAC,CAAC;AAE1B,gBAAA,OAAO,IAAI;YACd;YACA,KAAK,QAAQ,EAAE;AACZ,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,cAAc,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AACrE,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;YACA,KAAK,QAAQ,EAAE;AACZ,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,cAAc,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AACrE,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;YACA,KAAK,QAAQ,EAAE;AACZ,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,cAAc,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AACrE,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;;YAGA,KAAK,QAAQ,EAAE;AACZ,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,yBAAyB,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AAChF,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;;YAGA,KAAK,MAAM,EAAE;AACV,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,uBAAuB,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AAC9E,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;;YAGA,KAAK,OAAO,EAAE;AACX,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,wBAAwB,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AAC/E,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;;YAGA,KAAK,UAAU,EAAE;AACd,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,2BAA2B,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AAClF,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;;YAGA,KAAK,SAAS,EAAE;AACb,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,yBAAyB,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AAChF,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;;YAGA,KAAK,UAAU,EAAE;AACd,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,yBAAyB,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AAChF,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;;YAGA,KAAK,MAAM,EAAE;AACV,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,cAAc,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AACrE,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YAClC;;YAGA,KAAK,KAAK,EAAE;AACT,gBAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,aAAa,KAAK,CAAA,CAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;AACpE,gBAAA,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;YACjC;AAEA,YAAA;AACG,gBAAA,OAAO,IAAI;;AAEpB,IAAA,CAAC;IAED,OAAO;AACJ,QAAA,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,YAAY,EAAE;KACvB;AACJ;;;;"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { XansqlDialectEngine, Xansql, ExecuterResult, XansqlFileMeta } from '@xansql/core';
|
|
2
|
+
|
|
3
|
+
declare const XansqlBridge: (url: string, engine?: XansqlDialectEngine) => {
|
|
4
|
+
engine: XansqlDialectEngine;
|
|
5
|
+
execute: (sql: string, xansql: Xansql) => Promise<ExecuterResult>;
|
|
6
|
+
getSchema: (xansql: Xansql) => Promise<any>;
|
|
7
|
+
file: {
|
|
8
|
+
upload: (file: File, xansql: Xansql) => Promise<XansqlFileMeta>;
|
|
9
|
+
delete: (fileId: string, xansql: Xansql) => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export { XansqlBridge as default };
|
package/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var securequ = require('securequ');
|
|
4
|
+
var core = require('@xansql/core');
|
|
5
|
+
var base = require('./base.js');
|
|
6
|
+
|
|
7
|
+
const XansqlBridge = (url, engine) => {
|
|
8
|
+
let clientInstance = null;
|
|
9
|
+
const getClient = async (xansql) => {
|
|
10
|
+
if (!clientInstance) {
|
|
11
|
+
const secret = await base.makeSecret(xansql);
|
|
12
|
+
clientInstance = new securequ.SecurequClient({
|
|
13
|
+
secret,
|
|
14
|
+
url
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return clientInstance;
|
|
18
|
+
};
|
|
19
|
+
const execute = async (sql, xansql) => {
|
|
20
|
+
if (typeof window === 'undefined') {
|
|
21
|
+
throw new core.XansqlError({
|
|
22
|
+
message: "XansqlBridge dialect can only be used in browser environment.",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const client = await getClient(xansql);
|
|
26
|
+
const meta = base.sqlparser(sql);
|
|
27
|
+
const data = {
|
|
28
|
+
sql,
|
|
29
|
+
table: meta.table,
|
|
30
|
+
action: meta.action,
|
|
31
|
+
};
|
|
32
|
+
if (meta.action === "SELECT") {
|
|
33
|
+
let res = await client.get(await base.makePath('find', xansql), { params: data });
|
|
34
|
+
if (!res.success) {
|
|
35
|
+
throw new core.XansqlError(res.message);
|
|
36
|
+
}
|
|
37
|
+
return res.data || null;
|
|
38
|
+
}
|
|
39
|
+
else if (meta.action === "INSERT") {
|
|
40
|
+
let res = await client.post(await base.makePath('insert', xansql), { body: data });
|
|
41
|
+
if (!res.success) {
|
|
42
|
+
throw new core.XansqlError(res.message);
|
|
43
|
+
}
|
|
44
|
+
return res.data || null;
|
|
45
|
+
}
|
|
46
|
+
else if (meta.action === "UPDATE") {
|
|
47
|
+
let res = await client.put(await base.makePath('update', xansql), { body: data });
|
|
48
|
+
if (!res.success) {
|
|
49
|
+
throw new core.XansqlError(res.message);
|
|
50
|
+
}
|
|
51
|
+
return res.data || null;
|
|
52
|
+
}
|
|
53
|
+
else if (meta.action === "DELETE") {
|
|
54
|
+
let res = await client.delete(await base.makePath('delete', xansql), { params: data });
|
|
55
|
+
if (!res.success) {
|
|
56
|
+
throw new core.XansqlError(res.message);
|
|
57
|
+
}
|
|
58
|
+
return res.data || null;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
let res = await client.post(await base.makePath('executer', xansql), { body: data });
|
|
62
|
+
if (!res.success) {
|
|
63
|
+
throw new core.XansqlError(res.message);
|
|
64
|
+
}
|
|
65
|
+
return res.data || null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const getSchema = async (xansql) => {
|
|
69
|
+
const client = await getClient(xansql);
|
|
70
|
+
const res = await client.get(await base.makePath('raw_schema', xansql));
|
|
71
|
+
if (!res.success) {
|
|
72
|
+
throw new core.XansqlError({
|
|
73
|
+
message: `Failed to fetch schema: ${res.message || 'Unknown error'}`
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return res.data;
|
|
77
|
+
};
|
|
78
|
+
const uploadFile = async (file, xansql) => {
|
|
79
|
+
const client = await getClient(xansql);
|
|
80
|
+
const res = await client.uploadFile(file);
|
|
81
|
+
return res.data;
|
|
82
|
+
};
|
|
83
|
+
const deleteFile = async (fileId, xansql) => {
|
|
84
|
+
const client = await getClient(xansql);
|
|
85
|
+
await client.deleteFile(fileId);
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
engine: engine || 'mysql',
|
|
89
|
+
execute,
|
|
90
|
+
getSchema,
|
|
91
|
+
file: {
|
|
92
|
+
upload: uploadFile,
|
|
93
|
+
delete: deleteFile
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
module.exports = XansqlBridge;
|
|
99
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { SecurequClient } from \"securequ\";\nimport { Xansql, XansqlError, ExecuterResult, XansqlDialectEngine, XansqlFileMeta } from \"@xansql/core\";\nimport { makePath, makeSecret, sqlparser } from \"./base\";\n\nconst XansqlBridge = (url: string, engine?: XansqlDialectEngine) => {\n\n let clientInstance: SecurequClient | null = null;\n const getClient = async (xansql: Xansql) => {\n if (!clientInstance) {\n const secret = await makeSecret(xansql)\n clientInstance = new SecurequClient({\n secret,\n url\n });\n }\n return clientInstance;\n }\n\n const execute = async (sql: string, xansql: Xansql): Promise<ExecuterResult> => {\n if (typeof window === 'undefined') {\n throw new XansqlError({\n message: \"XansqlBridge dialect can only be used in browser environment.\",\n })\n }\n const client = await getClient(xansql)\n const meta = sqlparser(sql);\n const data = {\n sql,\n table: meta.table,\n action: meta.action,\n };\n\n if (meta.action === \"SELECT\") {\n let res = await client.get(await makePath('find', xansql), { params: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else if (meta.action === \"INSERT\") {\n let res = await client.post(await makePath('insert', xansql), { body: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else if (meta.action === \"UPDATE\") {\n let res = await client.put(await makePath('update', xansql), { body: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else if (meta.action === \"DELETE\") {\n let res = await client.delete(await makePath('delete', xansql), { params: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else {\n let res = await client.post(await makePath('executer', xansql), { body: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n }\n };\n\n const getSchema = async (xansql: Xansql) => {\n const client = await getClient(xansql)\n const res = await client.get(await makePath('raw_schema', xansql))\n if (!res.success) {\n throw new XansqlError({\n message: `Failed to fetch schema: ${res.message || 'Unknown error'}`\n })\n }\n return res.data\n };\n\n const uploadFile = async (file: File, xansql: Xansql): Promise<XansqlFileMeta> => {\n const client = await getClient(xansql);\n const res = await client.uploadFile(file);\n return res.data\n }\n\n const deleteFile = async (fileId: string, xansql: Xansql) => {\n const client = await getClient(xansql);\n await client.deleteFile(fileId);\n }\n\n return {\n engine: engine || 'mysql',\n execute,\n getSchema,\n file: {\n upload: uploadFile,\n delete: deleteFile\n }\n };\n};\n\nexport default XansqlBridge;\n"],"names":["makeSecret","SecurequClient","XansqlError","sqlparser","makePath"],"mappings":"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"}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { SecurequClient } from 'securequ';
|
|
2
|
+
import { XansqlError } from '@xansql/core';
|
|
3
|
+
import { makePath, sqlparser, makeSecret } from './base.mjs';
|
|
4
|
+
|
|
5
|
+
const XansqlBridge = (url, engine) => {
|
|
6
|
+
let clientInstance = null;
|
|
7
|
+
const getClient = async (xansql) => {
|
|
8
|
+
if (!clientInstance) {
|
|
9
|
+
const secret = await makeSecret(xansql);
|
|
10
|
+
clientInstance = new SecurequClient({
|
|
11
|
+
secret,
|
|
12
|
+
url
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return clientInstance;
|
|
16
|
+
};
|
|
17
|
+
const execute = async (sql, xansql) => {
|
|
18
|
+
if (typeof window === 'undefined') {
|
|
19
|
+
throw new XansqlError({
|
|
20
|
+
message: "XansqlBridge dialect can only be used in browser environment.",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const client = await getClient(xansql);
|
|
24
|
+
const meta = sqlparser(sql);
|
|
25
|
+
const data = {
|
|
26
|
+
sql,
|
|
27
|
+
table: meta.table,
|
|
28
|
+
action: meta.action,
|
|
29
|
+
};
|
|
30
|
+
if (meta.action === "SELECT") {
|
|
31
|
+
let res = await client.get(await makePath('find', xansql), { params: data });
|
|
32
|
+
if (!res.success) {
|
|
33
|
+
throw new XansqlError(res.message);
|
|
34
|
+
}
|
|
35
|
+
return res.data || null;
|
|
36
|
+
}
|
|
37
|
+
else if (meta.action === "INSERT") {
|
|
38
|
+
let res = await client.post(await makePath('insert', xansql), { body: data });
|
|
39
|
+
if (!res.success) {
|
|
40
|
+
throw new XansqlError(res.message);
|
|
41
|
+
}
|
|
42
|
+
return res.data || null;
|
|
43
|
+
}
|
|
44
|
+
else if (meta.action === "UPDATE") {
|
|
45
|
+
let res = await client.put(await makePath('update', xansql), { body: data });
|
|
46
|
+
if (!res.success) {
|
|
47
|
+
throw new XansqlError(res.message);
|
|
48
|
+
}
|
|
49
|
+
return res.data || null;
|
|
50
|
+
}
|
|
51
|
+
else if (meta.action === "DELETE") {
|
|
52
|
+
let res = await client.delete(await makePath('delete', xansql), { params: data });
|
|
53
|
+
if (!res.success) {
|
|
54
|
+
throw new XansqlError(res.message);
|
|
55
|
+
}
|
|
56
|
+
return res.data || null;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
let res = await client.post(await makePath('executer', xansql), { body: data });
|
|
60
|
+
if (!res.success) {
|
|
61
|
+
throw new XansqlError(res.message);
|
|
62
|
+
}
|
|
63
|
+
return res.data || null;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const getSchema = async (xansql) => {
|
|
67
|
+
const client = await getClient(xansql);
|
|
68
|
+
const res = await client.get(await makePath('raw_schema', xansql));
|
|
69
|
+
if (!res.success) {
|
|
70
|
+
throw new XansqlError({
|
|
71
|
+
message: `Failed to fetch schema: ${res.message || 'Unknown error'}`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return res.data;
|
|
75
|
+
};
|
|
76
|
+
const uploadFile = async (file, xansql) => {
|
|
77
|
+
const client = await getClient(xansql);
|
|
78
|
+
const res = await client.uploadFile(file);
|
|
79
|
+
return res.data;
|
|
80
|
+
};
|
|
81
|
+
const deleteFile = async (fileId, xansql) => {
|
|
82
|
+
const client = await getClient(xansql);
|
|
83
|
+
await client.deleteFile(fileId);
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
engine: engine || 'mysql',
|
|
87
|
+
execute,
|
|
88
|
+
getSchema,
|
|
89
|
+
file: {
|
|
90
|
+
upload: uploadFile,
|
|
91
|
+
delete: deleteFile
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export { XansqlBridge as default };
|
|
97
|
+
//# sourceMappingURL=index.mjs.map
|
package/index.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { SecurequClient } from \"securequ\";\nimport { Xansql, XansqlError, ExecuterResult, XansqlDialectEngine, XansqlFileMeta } from \"@xansql/core\";\nimport { makePath, makeSecret, sqlparser } from \"./base\";\n\nconst XansqlBridge = (url: string, engine?: XansqlDialectEngine) => {\n\n let clientInstance: SecurequClient | null = null;\n const getClient = async (xansql: Xansql) => {\n if (!clientInstance) {\n const secret = await makeSecret(xansql)\n clientInstance = new SecurequClient({\n secret,\n url\n });\n }\n return clientInstance;\n }\n\n const execute = async (sql: string, xansql: Xansql): Promise<ExecuterResult> => {\n if (typeof window === 'undefined') {\n throw new XansqlError({\n message: \"XansqlBridge dialect can only be used in browser environment.\",\n })\n }\n const client = await getClient(xansql)\n const meta = sqlparser(sql);\n const data = {\n sql,\n table: meta.table,\n action: meta.action,\n };\n\n if (meta.action === \"SELECT\") {\n let res = await client.get(await makePath('find', xansql), { params: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else if (meta.action === \"INSERT\") {\n let res = await client.post(await makePath('insert', xansql), { body: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else if (meta.action === \"UPDATE\") {\n let res = await client.put(await makePath('update', xansql), { body: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else if (meta.action === \"DELETE\") {\n let res = await client.delete(await makePath('delete', xansql), { params: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n } else {\n let res = await client.post(await makePath('executer', xansql), { body: data })\n if (!res.success) {\n throw new XansqlError(res.message);\n }\n return res.data || null\n }\n };\n\n const getSchema = async (xansql: Xansql) => {\n const client = await getClient(xansql)\n const res = await client.get(await makePath('raw_schema', xansql))\n if (!res.success) {\n throw new XansqlError({\n message: `Failed to fetch schema: ${res.message || 'Unknown error'}`\n })\n }\n return res.data\n };\n\n const uploadFile = async (file: File, xansql: Xansql): Promise<XansqlFileMeta> => {\n const client = await getClient(xansql);\n const res = await client.uploadFile(file);\n return res.data\n }\n\n const deleteFile = async (fileId: string, xansql: Xansql) => {\n const client = await getClient(xansql);\n await client.deleteFile(fileId);\n }\n\n return {\n engine: engine || 'mysql',\n execute,\n getSchema,\n file: {\n upload: uploadFile,\n delete: deleteFile\n }\n };\n};\n\nexport default XansqlBridge;\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xansql/bridge",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "./index.js",
|
|
5
|
+
"module": "./index.mjs",
|
|
6
|
+
"types": "./index.d.ts",
|
|
7
|
+
"description": "Xansql bridge",
|
|
8
|
+
"author": "Devnax",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [],
|
|
13
|
+
"sideEffects": [],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/xansql/bridge.git"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"securequ": "^1.1.12"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./index.mjs",
|
|
24
|
+
"require": "./index.js",
|
|
25
|
+
"types": "./index.d.ts"
|
|
26
|
+
},
|
|
27
|
+
"./server": {
|
|
28
|
+
"import": "./src/server.mjs",
|
|
29
|
+
"require": "./src/server.js",
|
|
30
|
+
"types": "./src/server.d.ts"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# xansql
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>Type-safe, event-driven SQL ORM with automatic schema synchronization, composable relations, granular hooks, and optional client execution bridge.</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<!-- Badges (replace placeholders when public) -->
|
|
9
|
+
<a href="#"><img alt="license" src="https://img.shields.io/badge/license-MIT-blue"/></a>
|
|
10
|
+
<a href="#"><img alt="status" src="https://img.shields.io/badge/status-beta-orange"/></a>
|
|
11
|
+
<a href="#"><img alt="dialects" src="https://img.shields.io/badge/dialects-mysql%20%7C%20postgresql%20%7C%20sqlite-6A5ACD"/></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Executive Summary
|
|
17
|
+
xansql is a minimalist but powerful ORM focusing on:
|
|
18
|
+
- Deterministic schema definition (single source of truth) with non-destructive migration.
|
|
19
|
+
- Relation traversal via declarative `select` trees (preventing circular graphs).
|
|
20
|
+
- Rich predicate language in `where` supporting deep `EXISTS` on nested relations.
|
|
21
|
+
- Event system & lifecycle hooks (global + per-model) for observability & cross-cutting concerns.
|
|
22
|
+
- Pluggable caching, file storage, fetch bridge (browser safe), and socket integration.
|
|
23
|
+
- Lightweight execution pipeline: thin SQL generation, no heavy runtime proxies.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
## Contents
|
|
27
|
+
1. Features
|
|
28
|
+
2. Architecture Overview
|
|
29
|
+
3. Installation
|
|
30
|
+
4. Quick Start
|
|
31
|
+
5. Configuration Reference
|
|
32
|
+
6. Defining Models & Fields
|
|
33
|
+
7. Relations
|
|
34
|
+
8. Querying & Predicates
|
|
35
|
+
9. Aggregation & Helpers
|
|
36
|
+
10. Pagination & Convenience APIs
|
|
37
|
+
11. Transactions
|
|
38
|
+
12. Migrations
|
|
39
|
+
13. Events & Hooks
|
|
40
|
+
14. File Handling
|
|
41
|
+
15. Client Fetch Bridge
|
|
42
|
+
16. Caching Interface
|
|
43
|
+
17. Dialects & Custom Implementation
|
|
44
|
+
18. Error Handling & Validation
|
|
45
|
+
19. Security Considerations
|
|
46
|
+
20. Performance Guidance
|
|
47
|
+
21. FAQ
|
|
48
|
+
22. Roadmap
|
|
49
|
+
23. License
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
## 1. Features
|
|
53
|
+
- Multi-dialect: MySQL, PostgreSQL, SQLite (custom adapter friendly)
|
|
54
|
+
- Auto aliasing + integrity checks
|
|
55
|
+
- Declarative relations (`xt.schema` / array reverse mapping)
|
|
56
|
+
- Non-destructive migrate (add/modify/remove columns) + force rebuild
|
|
57
|
+
- Granular lifecycle hooks & event emission
|
|
58
|
+
- Rich `where` condition operators (logical AND/OR composition)
|
|
59
|
+
- Nested relational filtering through `EXISTS` semantics
|
|
60
|
+
- Aggregation inline or via helper methods
|
|
61
|
+
- Optional caching module contract
|
|
62
|
+
- Integrated file meta handling & streaming upload abstraction
|
|
63
|
+
- Client-side safe execution (no raw SQL leakage) via signed execution meta
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
## 2. Architecture Overview
|
|
67
|
+
Layered components:
|
|
68
|
+
- Core: `Xansql` orchestrates config, model registry, transactions, migration, fetch bridge and events.
|
|
69
|
+
- Model: Provides CRUD + query generation + relation resolution.
|
|
70
|
+
- Executers: Specialized operation builders (Find / Create / Update / Delete / Aggregate).
|
|
71
|
+
- Migration: Computes delta from declared schema vs dialect metadata and issues SQL.
|
|
72
|
+
- Types System: Field factories (`xt.*`) with metadata (length, unique, index, validators, transforms).
|
|
73
|
+
- Foreign Resolver: Normalizes forward & reverse relation mapping for join/exists generation.
|
|
74
|
+
- Fetch Bridge: Validates request meta for client-originated operations (server controlled).
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
## 3. Installation
|
|
78
|
+
```bash
|
|
79
|
+
npm install xansql mysql2 pg better-sqlite3
|
|
80
|
+
# Or only the drivers you need
|
|
81
|
+
```
|
|
82
|
+
SQLite usage recommends `better-sqlite3` for synchronous performance.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
## 4. Quick Start
|
|
86
|
+
```ts
|
|
87
|
+
import { Xansql, Model, xt } from 'xansql';
|
|
88
|
+
import MysqlDialect from 'xansql/dist/libs/MysqlDialect';
|
|
89
|
+
|
|
90
|
+
const db = new Xansql({
|
|
91
|
+
dialect: MysqlDialect({ host: '127.0.0.1', user: 'root', password: '', database: 'app' })
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const User = db.model('users', {
|
|
95
|
+
id: xt.id(),
|
|
96
|
+
username: xt.username(),
|
|
97
|
+
email: xt.email().unique(),
|
|
98
|
+
password: xt.password().strong(),
|
|
99
|
+
role: xt.role(['admin', 'member']),
|
|
100
|
+
createdAt: xt.createdAt(),
|
|
101
|
+
updatedAt: xt.updatedAt()
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await db.migrate();
|
|
105
|
+
await User.create({ data: [{ username: 'alice', email: 'a@b.com', password: 'Pwd@1234', role: 'member' }] });
|
|
106
|
+
const result = await User.find({ where: { username: { equals: 'alice' } } });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
## 5. Configuration Reference
|
|
111
|
+
```ts
|
|
112
|
+
new Xansql({
|
|
113
|
+
dialect: MysqlDialect({...}), // REQUIRED
|
|
114
|
+
fetch: { url: '/xansql', mode: 'production' }, // optional (client bridge)
|
|
115
|
+
socket: { open, message, close }, // optional WebSocket handlers
|
|
116
|
+
cache: { cache, clear, onFind, onCreate, onUpdate, onDelete }, // optional
|
|
117
|
+
file: { maxFilesize, chunkSize, upload, delete }, // optional file storage
|
|
118
|
+
maxLimit: { find, create, update, delete }, // safety caps (default 100)
|
|
119
|
+
hooks: { beforeFind, afterFind, transform, ... } // global async hooks
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
Required dialect interface:
|
|
123
|
+
```ts
|
|
124
|
+
interface XansqlDialect {
|
|
125
|
+
engine: 'mysql' | 'postgresql' | 'sqlite';
|
|
126
|
+
execute(sql: string): Promise<{ results: any[]; affectedRows: number; insertId: number | null }>;
|
|
127
|
+
getSchema(): Promise<{ [table: string]: { name: string; type: string; notnull: boolean; default_value: any; pk: boolean; index: boolean; unique: boolean }[] }>
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
## 6. Defining Models & Fields
|
|
133
|
+
```ts
|
|
134
|
+
const Post = db.model('posts', {
|
|
135
|
+
id: xt.id(),
|
|
136
|
+
title: xt.title().index(),
|
|
137
|
+
slug: xt.slug().unique(),
|
|
138
|
+
author: xt.schema('users', 'id'), // FK forward
|
|
139
|
+
tags: xt.array(xt.string(30)), // array (not in where predicate)
|
|
140
|
+
images: xt.array(xt.file()), // file metadata entries
|
|
141
|
+
createdAt: xt.createdAt(),
|
|
142
|
+
updatedAt: xt.updatedAt()
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
Per-model hooks:
|
|
146
|
+
```ts
|
|
147
|
+
Post.options.hooks = {
|
|
148
|
+
beforeCreate: async (args) => args,
|
|
149
|
+
transform: async (row) => { delete row.password; return row; }
|
|
150
|
+
};
|
|
151
|
+
```
|
|
152
|
+
Field factory highlights: `id, string, number, boolean, date, enum, array, object, record, tuple, union, file, schema` + semantic shortcuts (`username`, `email`, `password`, `slug`, `role`, `title`, `amount`, etc.). Most fields accept chainable validators (`min`, `max`, `unique`, `index`, `transform`).
|
|
153
|
+
|
|
154
|
+
Foreign key patterns:
|
|
155
|
+
- Forward: `xt.schema('users','id')`
|
|
156
|
+
- Reverse (one-to-many): `xt.array(xt.schema('posts','id'))`
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
## 7. Relations
|
|
160
|
+
Select nested relations:
|
|
161
|
+
```ts
|
|
162
|
+
await User.find({
|
|
163
|
+
select: {
|
|
164
|
+
id: true,
|
|
165
|
+
username: true,
|
|
166
|
+
posts: {
|
|
167
|
+
select: { id: true, title: true },
|
|
168
|
+
where: { title: { contains: 'SQL' } },
|
|
169
|
+
limit: { take: 5 }
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
Circular graphs are rejected early.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
## 8. Querying & Predicates
|
|
178
|
+
Operators: `equals, not, lt, lte, gt, gte, in, notIn, between, notBetween, contains, notContains, startsWith, endsWith, isNull, isNotNull, isEmpty, isNotEmpty, isTrue, isFalse`.
|
|
179
|
+
- Object => AND
|
|
180
|
+
- Array of objects => OR
|
|
181
|
+
- Nested relation in `where` => EXISTS subquery
|
|
182
|
+
Example:
|
|
183
|
+
```ts
|
|
184
|
+
await Post.find({
|
|
185
|
+
where: {
|
|
186
|
+
author: { username: { startsWith: 'a' } },
|
|
187
|
+
slug: { notContains: 'draft' },
|
|
188
|
+
title: [{ contains: 'Guide' }, { contains: 'Intro' }]
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
## 9. Aggregation & Helpers
|
|
195
|
+
Inline:
|
|
196
|
+
```ts
|
|
197
|
+
await User.find({ aggregate: { id: { count: true } } });
|
|
198
|
+
```
|
|
199
|
+
Helpers: `count(where)`, `min(col, where)`, `max`, `sum`, `avg`, `exists(where)`.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
## 10. Pagination & Convenience
|
|
203
|
+
```ts
|
|
204
|
+
const page = await User.paginate(2, { perpage: 20, where: { role: { equals: 'member' } } });
|
|
205
|
+
// { page, perpage, pagecount, rowcount, results }
|
|
206
|
+
```
|
|
207
|
+
Also: `findOne(args)`, `findById(id, args)`.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
## 11. Transactions
|
|
211
|
+
Automatic for create/update/delete unless within chained relation execution.
|
|
212
|
+
Manual wrapper:
|
|
213
|
+
```ts
|
|
214
|
+
await db.transaction(async () => {
|
|
215
|
+
await User.create({ data: [{ username: 'temp' }] });
|
|
216
|
+
await User.update({ data: { role: 'admin' }, where: { username: 'temp' } });
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
Rollback on error.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
## 12. Migrations
|
|
223
|
+
```ts
|
|
224
|
+
await db.migrate(); // sync non-destructively
|
|
225
|
+
await db.migrate(true); // drop + recreate (files cleaned)
|
|
226
|
+
const preview = await db.generateMigration(); // array of SQL statements
|
|
227
|
+
```
|
|
228
|
+
Rules:
|
|
229
|
+
- Skips ID column alterations.
|
|
230
|
+
- Adds new columns; drops removed ones; issues ALTER for changed definition.
|
|
231
|
+
- Force rebuild executes reverse-order drops then creates.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
## 13. Events & Hooks
|
|
235
|
+
Events emitted: `BEFORE_CREATE, CREATE, BEFORE_UPDATE, UPDATE, BEFORE_DELETE, DELETE, BEFORE_FIND, FIND, BEFORE_AGGREGATE, AGGREGATE, BEFORE_FETCH, FETCH`.
|
|
236
|
+
Usage:
|
|
237
|
+
```ts
|
|
238
|
+
db.on('CREATE', ({ model, results }) => { /* audit */ });
|
|
239
|
+
```
|
|
240
|
+
Hooks (global & model-level) allow mutation of args/results or row transform.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
## 14. File Handling
|
|
244
|
+
Define file fields: `xt.file(size?)` / arrays.
|
|
245
|
+
Configure storage:
|
|
246
|
+
```ts
|
|
247
|
+
file: {
|
|
248
|
+
maxFilesize: 2048, // KB
|
|
249
|
+
chunkSize: 256, // KB (streaming)
|
|
250
|
+
upload: async (chunk, meta) => {},
|
|
251
|
+
delete: async (filename) => {}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
Client helpers: `uploadFile(file, executeId)`, `deleteFile(name, executeId)`.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
## 15. Client Fetch Bridge
|
|
258
|
+
Provide `fetch: string | { url, mode }`.
|
|
259
|
+
Client side raw SQL blocked; operations require internally generated `executeId` (granted per model action via metadata).
|
|
260
|
+
Server integrates:
|
|
261
|
+
```ts
|
|
262
|
+
const response = await db.onFetch(req.url, {
|
|
263
|
+
body: req.body,
|
|
264
|
+
headers: req.headers,
|
|
265
|
+
cookies: parseCookies(req),
|
|
266
|
+
isAuthorized: async (meta) => {/* check meta.action, meta.model */ return true; }
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
## 16. Caching Interface
|
|
272
|
+
Implement partial or full row caching:
|
|
273
|
+
```ts
|
|
274
|
+
cache: {
|
|
275
|
+
cache: async (sql, model) => /* rows or undefined */,
|
|
276
|
+
clear: async (model) => {},
|
|
277
|
+
onFind: async (sql, model, row) => {},
|
|
278
|
+
onCreate: async (model, insertId) => {},
|
|
279
|
+
onUpdate: async (model, rows) => {},
|
|
280
|
+
onDelete: async (model, rows) => {},
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
You decide strategy (memory, redis, browser IndexedDB via example adapters).
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
## 17. Dialects & Custom Implementation
|
|
287
|
+
Built-ins: `MysqlDialect`, `PostgresDialect`, `SqliteDialect`.
|
|
288
|
+
Custom:
|
|
289
|
+
```ts
|
|
290
|
+
const CustomDialect = () => ({
|
|
291
|
+
engine: 'mysql',
|
|
292
|
+
execute: async (sql) => {/* run */ return { results: [], affectedRows: 0, insertId: 0 };},
|
|
293
|
+
getSchema: async () => ({ /* table: columns[] */ })
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
`getSchema` must supply column index/unique flags for migration diffing.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
## 18. Error Handling & Validation
|
|
300
|
+
Common thrown errors:
|
|
301
|
+
- Missing dialect or execute function
|
|
302
|
+
- Unsupported engine
|
|
303
|
+
- Model without ID field
|
|
304
|
+
- Duplicate model name / alias collision
|
|
305
|
+
- Invalid where operator or disallowed field type in predicate (array/object/record/tuple)
|
|
306
|
+
- Circular relation selection / where nesting
|
|
307
|
+
- Client usage without fetch configuration
|
|
308
|
+
- Raw query attempt from client without `executeId`
|
|
309
|
+
- Invalid foreign key definition
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
## 19. Security Considerations
|
|
313
|
+
- All value interpolation passes through escaping utilities.
|
|
314
|
+
- Client cannot send arbitrary SQL (requires signed meta created server-side).
|
|
315
|
+
- Hooks & events can enforce auditing, RBAC, masking.
|
|
316
|
+
- Password field helper automatically hashes via SHA-256 transform.
|
|
317
|
+
- Recommend additional app-layer input validation before invoking ORM.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
## 20. Performance Guidance
|
|
321
|
+
- Prefer selective `select` trees over full-table scans.
|
|
322
|
+
- Use indexes via field `.index()` / `.unique()` early (migration will create).
|
|
323
|
+
- Enable caching for heavy read patterns.
|
|
324
|
+
- Use pagination helpers (`paginate`) to avoid large offset scans.
|
|
325
|
+
- Keep relation depth shallow to limit EXISTS nesting.
|
|
326
|
+
- Batch `create` with array `data` for reduced round trips.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
## 21. FAQ
|
|
330
|
+
Q: Does xansql generate JOINs?
|
|
331
|
+
A: Relation filters use `EXISTS` subqueries; selection fetches related sets separately.
|
|
332
|
+
|
|
333
|
+
Q: How are reverse (one-to-many) relations defined?
|
|
334
|
+
A: `xt.array(xt.schema('childTable','id'))` inside the parent references children.
|
|
335
|
+
|
|
336
|
+
Q: Can I rename columns automatically?
|
|
337
|
+
A: Rename support is planned (see roadmap). Current diff treats rename as drop + add.
|
|
338
|
+
|
|
339
|
+
Q: Can I use raw SQL?
|
|
340
|
+
A: Server side `db.execute(sql)` is allowed; client side raw is blocked.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
## 22. Roadmap
|
|
344
|
+
- Column / index rename migration operations
|
|
345
|
+
- CLI code generation & schema inspector
|
|
346
|
+
- Enhanced diff reporting (explain changes)
|
|
347
|
+
- Advanced relation eager constraints (depth limiting strategies)
|
|
348
|
+
- Pluggable authorization middleware bundle
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
## 23. License
|
|
352
|
+
MIT
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
## Attributions
|
|
356
|
+
Internal field validation leverages concepts from `xanv`. File handling meta uses `securequ` upload structures.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
> Need adjustments (badges, examples, tutorials)? Open an issue or contribute.
|
package/server.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SecurequServer } from 'securequ';
|
|
2
|
+
import { Xansql } from '@xansql/core';
|
|
3
|
+
import { XansqlBridgeServerConfig, XansqlBridgeAuthorizedInfo, ListenOptions } from './types.js';
|
|
4
|
+
|
|
5
|
+
declare class XansqlBridgeServer {
|
|
6
|
+
readonly XANFETCH_CONTENT_TYPE = "application/octet-stream";
|
|
7
|
+
xansql: Xansql;
|
|
8
|
+
config: XansqlBridgeServerConfig;
|
|
9
|
+
private server;
|
|
10
|
+
constructor(xansql: Xansql, config: XansqlBridgeServerConfig);
|
|
11
|
+
authorized(info: XansqlBridgeAuthorizedInfo): Promise<void>;
|
|
12
|
+
initial(): Promise<SecurequServer>;
|
|
13
|
+
listen(url: string, options: ListenOptions): Promise<{
|
|
14
|
+
status: number;
|
|
15
|
+
value: string | Uint8Array<ArrayBufferLike>;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { XansqlBridgeServer as default };
|
package/server.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var securequ = require('securequ');
|
|
4
|
+
var core = require('@xansql/core');
|
|
5
|
+
var base = require('./base.js');
|
|
6
|
+
|
|
7
|
+
class XansqlBridgeServer {
|
|
8
|
+
constructor(xansql, config) {
|
|
9
|
+
this.XANFETCH_CONTENT_TYPE = 'application/octet-stream';
|
|
10
|
+
this.server = null;
|
|
11
|
+
this.xansql = xansql;
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
async authorized(info) {
|
|
15
|
+
const config = this.config;
|
|
16
|
+
if (config.isAuthorized) {
|
|
17
|
+
const isPermit = await config.isAuthorized(info);
|
|
18
|
+
if (!isPermit)
|
|
19
|
+
throw new core.XansqlError({
|
|
20
|
+
message: "isAuthorized denied for server initialization.",
|
|
21
|
+
model: info.model ? info.model.table : undefined,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async initial() {
|
|
26
|
+
if (this.server)
|
|
27
|
+
return this.server;
|
|
28
|
+
const config = this.config;
|
|
29
|
+
const xansql = this.xansql;
|
|
30
|
+
const secret = await base.makeSecret(this.xansql);
|
|
31
|
+
const server = new securequ.SecurequServer(Object.assign(Object.assign({}, (config || {})), { clients: [
|
|
32
|
+
{
|
|
33
|
+
origin: `*`,
|
|
34
|
+
secret
|
|
35
|
+
}
|
|
36
|
+
] }));
|
|
37
|
+
server.get(await base.makePath('find', xansql), async (req) => {
|
|
38
|
+
const params = req.searchParams;
|
|
39
|
+
await this.authorized({
|
|
40
|
+
method: "GET",
|
|
41
|
+
model: xansql.models.get(params.table),
|
|
42
|
+
action: params.action,
|
|
43
|
+
});
|
|
44
|
+
throw await xansql.execute(params.sql);
|
|
45
|
+
});
|
|
46
|
+
server.post(await base.makePath('insert', xansql), async (req) => {
|
|
47
|
+
const params = req.body;
|
|
48
|
+
await this.authorized({
|
|
49
|
+
method: "POST",
|
|
50
|
+
model: xansql.models.get(params.table),
|
|
51
|
+
action: params.action,
|
|
52
|
+
});
|
|
53
|
+
throw await xansql.execute(params.sql);
|
|
54
|
+
});
|
|
55
|
+
server.put(await base.makePath('update', xansql), async (req) => {
|
|
56
|
+
const params = req.body;
|
|
57
|
+
await this.authorized({
|
|
58
|
+
method: "PUT",
|
|
59
|
+
model: xansql.models.get(params.table),
|
|
60
|
+
action: params.action,
|
|
61
|
+
});
|
|
62
|
+
throw await xansql.execute(params.sql);
|
|
63
|
+
});
|
|
64
|
+
server.delete(await base.makePath('delete', xansql), async (req) => {
|
|
65
|
+
const params = req.searchParams;
|
|
66
|
+
await this.authorized({
|
|
67
|
+
method: "DELETE",
|
|
68
|
+
model: xansql.models.get(params.table),
|
|
69
|
+
action: params.action,
|
|
70
|
+
});
|
|
71
|
+
throw await xansql.execute(params.sql);
|
|
72
|
+
});
|
|
73
|
+
server.post(await base.makePath('executer', xansql), async (req) => {
|
|
74
|
+
const params = req.body;
|
|
75
|
+
await this.authorized({
|
|
76
|
+
method: "POST",
|
|
77
|
+
model: xansql.models.get(params.table),
|
|
78
|
+
action: params.action,
|
|
79
|
+
});
|
|
80
|
+
throw await xansql.execute(params.sql);
|
|
81
|
+
});
|
|
82
|
+
server.get(await base.makePath('raw_schema', xansql), async (req) => {
|
|
83
|
+
throw await xansql.dialect.getSchema(xansql);
|
|
84
|
+
});
|
|
85
|
+
this.server = server;
|
|
86
|
+
return this.server;
|
|
87
|
+
}
|
|
88
|
+
async listen(url, options) {
|
|
89
|
+
const server = await this.initial();
|
|
90
|
+
try {
|
|
91
|
+
const res = await server.listen(url, options);
|
|
92
|
+
return {
|
|
93
|
+
status: res.status,
|
|
94
|
+
value: res.value,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const secret = await base.makeSecret(this.xansql);
|
|
99
|
+
return {
|
|
100
|
+
status: 500,
|
|
101
|
+
value: await securequ.crypto.encryptBuffer({
|
|
102
|
+
success: false,
|
|
103
|
+
message: error.message || 'Internal Server Error'
|
|
104
|
+
}, secret)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = XansqlBridgeServer;
|
|
111
|
+
//# sourceMappingURL=server.js.map
|
package/server.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../src/server.ts"],"sourcesContent":["import { crypto, SecurequServer } from \"securequ\";\nimport { Xansql, XansqlError, Model } from \"@xansql/core\";\nimport { makePath, makeSecret } from \"./base\";\nimport { ListenOptions, XansqlBridgeAuthorizedInfo, XansqlBridgeServerConfig } from \"./types\";\n\nclass XansqlBridgeServer {\n readonly XANFETCH_CONTENT_TYPE = 'application/octet-stream';\n xansql: Xansql;\n config: XansqlBridgeServerConfig;\n private server: SecurequServer | null = null;\n\n constructor(xansql: Xansql, config: XansqlBridgeServerConfig) {\n this.xansql = xansql;\n this.config = config;\n }\n\n async authorized(info: XansqlBridgeAuthorizedInfo) {\n const config = this.config;\n if (config.isAuthorized) {\n const isPermit = await config.isAuthorized(info)\n if (!isPermit) throw new XansqlError({\n message: \"isAuthorized denied for server initialization.\",\n model: info.model ? info.model.table : undefined,\n })\n }\n }\n\n async initial() {\n if (this.server) return this.server;\n\n const config = this.config;\n const xansql = this.xansql\n const secret = await makeSecret(this.xansql);\n\n const server = new SecurequServer({\n ...(config || {}),\n clients: [\n {\n origin: `*`,\n secret\n }\n ]\n });\n\n server.get(await makePath('find', xansql), async (req: any) => {\n\n const params: any = req.searchParams\n await this.authorized({\n method: \"GET\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n\n server.post(await makePath('insert', xansql), async (req: any) => {\n const params: any = req.body\n await this.authorized({\n method: \"POST\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n server.put(await makePath('update', xansql), async (req: any) => {\n const params: any = req.body\n await this.authorized({\n method: \"PUT\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n server.delete(await makePath('delete', xansql), async (req: any) => {\n const params: any = req.searchParams\n await this.authorized({\n method: \"DELETE\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n\n server.post(await makePath('executer', xansql), async (req: any) => {\n const params: any = req.body\n await this.authorized({\n method: \"POST\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n server.get(await makePath('raw_schema', xansql), async (req: any) => {\n throw await xansql.dialect.getSchema(xansql);\n })\n\n this.server = server;\n return this.server;\n }\n\n async listen(url: string, options: ListenOptions) {\n\n const server = await this.initial()\n try {\n const res = await server.listen(url, options)\n return {\n status: res.status,\n value: res.value,\n }\n } catch (error: any) {\n\n const secret = await makeSecret(this.xansql)\n return {\n status: 500,\n value: await crypto.encryptBuffer({\n success: false,\n message: error.message || 'Internal Server Error'\n }, secret)\n }\n }\n }\n}\n\nexport default XansqlBridgeServer"],"names":["XansqlError","makeSecret","SecurequServer","makePath","crypto"],"mappings":"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"}
|
package/server.mjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { SecurequServer, crypto } from 'securequ';
|
|
2
|
+
import { XansqlError } from '@xansql/core';
|
|
3
|
+
import { makeSecret, makePath } from './base.mjs';
|
|
4
|
+
|
|
5
|
+
class XansqlBridgeServer {
|
|
6
|
+
constructor(xansql, config) {
|
|
7
|
+
this.XANFETCH_CONTENT_TYPE = 'application/octet-stream';
|
|
8
|
+
this.server = null;
|
|
9
|
+
this.xansql = xansql;
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
async authorized(info) {
|
|
13
|
+
const config = this.config;
|
|
14
|
+
if (config.isAuthorized) {
|
|
15
|
+
const isPermit = await config.isAuthorized(info);
|
|
16
|
+
if (!isPermit)
|
|
17
|
+
throw new XansqlError({
|
|
18
|
+
message: "isAuthorized denied for server initialization.",
|
|
19
|
+
model: info.model ? info.model.table : undefined,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async initial() {
|
|
24
|
+
if (this.server)
|
|
25
|
+
return this.server;
|
|
26
|
+
const config = this.config;
|
|
27
|
+
const xansql = this.xansql;
|
|
28
|
+
const secret = await makeSecret(this.xansql);
|
|
29
|
+
const server = new SecurequServer(Object.assign(Object.assign({}, (config || {})), { clients: [
|
|
30
|
+
{
|
|
31
|
+
origin: `*`,
|
|
32
|
+
secret
|
|
33
|
+
}
|
|
34
|
+
] }));
|
|
35
|
+
server.get(await makePath('find', xansql), async (req) => {
|
|
36
|
+
const params = req.searchParams;
|
|
37
|
+
await this.authorized({
|
|
38
|
+
method: "GET",
|
|
39
|
+
model: xansql.models.get(params.table),
|
|
40
|
+
action: params.action,
|
|
41
|
+
});
|
|
42
|
+
throw await xansql.execute(params.sql);
|
|
43
|
+
});
|
|
44
|
+
server.post(await makePath('insert', xansql), async (req) => {
|
|
45
|
+
const params = req.body;
|
|
46
|
+
await this.authorized({
|
|
47
|
+
method: "POST",
|
|
48
|
+
model: xansql.models.get(params.table),
|
|
49
|
+
action: params.action,
|
|
50
|
+
});
|
|
51
|
+
throw await xansql.execute(params.sql);
|
|
52
|
+
});
|
|
53
|
+
server.put(await makePath('update', xansql), async (req) => {
|
|
54
|
+
const params = req.body;
|
|
55
|
+
await this.authorized({
|
|
56
|
+
method: "PUT",
|
|
57
|
+
model: xansql.models.get(params.table),
|
|
58
|
+
action: params.action,
|
|
59
|
+
});
|
|
60
|
+
throw await xansql.execute(params.sql);
|
|
61
|
+
});
|
|
62
|
+
server.delete(await makePath('delete', xansql), async (req) => {
|
|
63
|
+
const params = req.searchParams;
|
|
64
|
+
await this.authorized({
|
|
65
|
+
method: "DELETE",
|
|
66
|
+
model: xansql.models.get(params.table),
|
|
67
|
+
action: params.action,
|
|
68
|
+
});
|
|
69
|
+
throw await xansql.execute(params.sql);
|
|
70
|
+
});
|
|
71
|
+
server.post(await makePath('executer', xansql), async (req) => {
|
|
72
|
+
const params = req.body;
|
|
73
|
+
await this.authorized({
|
|
74
|
+
method: "POST",
|
|
75
|
+
model: xansql.models.get(params.table),
|
|
76
|
+
action: params.action,
|
|
77
|
+
});
|
|
78
|
+
throw await xansql.execute(params.sql);
|
|
79
|
+
});
|
|
80
|
+
server.get(await makePath('raw_schema', xansql), async (req) => {
|
|
81
|
+
throw await xansql.dialect.getSchema(xansql);
|
|
82
|
+
});
|
|
83
|
+
this.server = server;
|
|
84
|
+
return this.server;
|
|
85
|
+
}
|
|
86
|
+
async listen(url, options) {
|
|
87
|
+
const server = await this.initial();
|
|
88
|
+
try {
|
|
89
|
+
const res = await server.listen(url, options);
|
|
90
|
+
return {
|
|
91
|
+
status: res.status,
|
|
92
|
+
value: res.value,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const secret = await makeSecret(this.xansql);
|
|
97
|
+
return {
|
|
98
|
+
status: 500,
|
|
99
|
+
value: await crypto.encryptBuffer({
|
|
100
|
+
success: false,
|
|
101
|
+
message: error.message || 'Internal Server Error'
|
|
102
|
+
}, secret)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export { XansqlBridgeServer as default };
|
|
109
|
+
//# sourceMappingURL=server.mjs.map
|
package/server.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","sources":["../src/server.ts"],"sourcesContent":["import { crypto, SecurequServer } from \"securequ\";\nimport { Xansql, XansqlError, Model } from \"@xansql/core\";\nimport { makePath, makeSecret } from \"./base\";\nimport { ListenOptions, XansqlBridgeAuthorizedInfo, XansqlBridgeServerConfig } from \"./types\";\n\nclass XansqlBridgeServer {\n readonly XANFETCH_CONTENT_TYPE = 'application/octet-stream';\n xansql: Xansql;\n config: XansqlBridgeServerConfig;\n private server: SecurequServer | null = null;\n\n constructor(xansql: Xansql, config: XansqlBridgeServerConfig) {\n this.xansql = xansql;\n this.config = config;\n }\n\n async authorized(info: XansqlBridgeAuthorizedInfo) {\n const config = this.config;\n if (config.isAuthorized) {\n const isPermit = await config.isAuthorized(info)\n if (!isPermit) throw new XansqlError({\n message: \"isAuthorized denied for server initialization.\",\n model: info.model ? info.model.table : undefined,\n })\n }\n }\n\n async initial() {\n if (this.server) return this.server;\n\n const config = this.config;\n const xansql = this.xansql\n const secret = await makeSecret(this.xansql);\n\n const server = new SecurequServer({\n ...(config || {}),\n clients: [\n {\n origin: `*`,\n secret\n }\n ]\n });\n\n server.get(await makePath('find', xansql), async (req: any) => {\n\n const params: any = req.searchParams\n await this.authorized({\n method: \"GET\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n\n server.post(await makePath('insert', xansql), async (req: any) => {\n const params: any = req.body\n await this.authorized({\n method: \"POST\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n server.put(await makePath('update', xansql), async (req: any) => {\n const params: any = req.body\n await this.authorized({\n method: \"PUT\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n server.delete(await makePath('delete', xansql), async (req: any) => {\n const params: any = req.searchParams\n await this.authorized({\n method: \"DELETE\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n\n server.post(await makePath('executer', xansql), async (req: any) => {\n const params: any = req.body\n await this.authorized({\n method: \"POST\",\n model: xansql.models.get(params.table) as Model,\n action: params.action,\n })\n throw await xansql.execute(params.sql);\n })\n\n server.get(await makePath('raw_schema', xansql), async (req: any) => {\n throw await xansql.dialect.getSchema(xansql);\n })\n\n this.server = server;\n return this.server;\n }\n\n async listen(url: string, options: ListenOptions) {\n\n const server = await this.initial()\n try {\n const res = await server.listen(url, options)\n return {\n status: res.status,\n value: res.value,\n }\n } catch (error: any) {\n\n const secret = await makeSecret(this.xansql)\n return {\n status: 500,\n value: await crypto.encryptBuffer({\n success: false,\n message: error.message || 'Internal Server Error'\n }, secret)\n }\n }\n }\n}\n\nexport default XansqlBridgeServer"],"names":[],"mappings":"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"}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SecurequServerConfig, ListenerInfo } from 'securequ';
|
|
2
|
+
import { Model } from '@xansql/core';
|
|
3
|
+
|
|
4
|
+
type XansqlBridgeAuthorizedInfo = {
|
|
5
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
6
|
+
model: Model | null;
|
|
7
|
+
action: string;
|
|
8
|
+
};
|
|
9
|
+
type XansqlBridgeServerConfig = {
|
|
10
|
+
basepath: string;
|
|
11
|
+
mode?: "production" | "development";
|
|
12
|
+
isAuthorized?: (info: XansqlBridgeAuthorizedInfo) => Promise<boolean>;
|
|
13
|
+
file?: SecurequServerConfig["file"];
|
|
14
|
+
};
|
|
15
|
+
type ListenOptions = ListenerInfo;
|
|
16
|
+
|
|
17
|
+
export type { ListenOptions, XansqlBridgeAuthorizedInfo, XansqlBridgeServerConfig };
|