@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 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":"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"}
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 };