@an5/adapters 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -305
- package/dist/an5Adapter.d.ts +85 -7
- package/dist/an5Adapter.js +736 -64
- package/dist/base/metadata.d.ts +10 -0
- package/dist/base/metadata.js +107 -0
- package/dist/base/sql.d.ts +19 -1
- package/dist/base/sql.js +152 -24
- package/dist/base/types.d.ts +4 -1
- package/dist/browser.d.ts +4 -1
- package/dist/browser.js +9 -2
- package/dist/googlesheets/adapter.d.ts +1 -0
- package/dist/googlesheets/adapter.js +9 -2
- package/dist/googlesheets/config.d.ts +4 -4
- package/dist/googlesheets/helpers.js +2 -0
- package/dist/googlesheets/parseConnectionString.js +5 -2
- package/dist/googlesheets/sqlExecutor.js +29 -24
- package/dist/googlesheets/tableClient.js +14 -5
- package/dist/index.d.ts +3 -1
- package/dist/index.js +7 -1
- package/dist/mssql/engine.js +8 -4
- package/dist/mysql/engine.js +1 -1
- package/dist/postgres/engine.js +1 -1
- package/dist/sqlite/browserEngine.d.ts +27 -0
- package/dist/sqlite/browserEngine.js +161 -0
- package/dist/sqlite/engine.js +15 -3
- package/dist/sqlite/index.d.ts +1 -0
- package/dist/sqlite/index.js +1 -0
- package/dist/typed.d.ts +5 -0
- package/dist/typed.js +2 -0
- package/dotnet/An5Adapter.cs +1089 -275
- package/dotnet/Base/Core.cs +340 -82
- package/dotnet/Mssql/MssqlEngine.cs +145 -144
- package/dotnet/Postgres/PostgresEngine.cs +121 -121
- package/golang/an5_adapter.go +314 -0
- package/golang/base/base.go +100 -0
- package/golang/base/metadata.go +116 -0
- package/golang/base/where.go +311 -0
- package/golang/go.mod +3 -0
- package/golang/mssql/mssql.go +32 -0
- package/golang/postgres/postgres.go +37 -0
- package/golang/sqlite/sqlite.go +14 -0
- package/golang/sqlite_test.go +42 -0
- package/golang/table_client.go +1329 -0
- package/package.json +17 -7
- package/python/an5_adapter.py +119 -97
- package/python/base/__init__.py +3 -3
- package/python/base/dialects.py +11 -8
- package/python/base/metadata.py +35 -10
- package/python/base/sql.py +172 -121
- package/python/mssql/__init__.py +1 -1
- package/python/mssql/provider.py +62 -62
- package/python/postgres/__init__.py +1 -1
- package/python/postgres/provider.py +15 -15
- package/python/table_client.py +619 -216
- package/typescript/src/an5Adapter.ts +1220 -505
- package/typescript/src/base/index.ts +3 -3
- package/typescript/src/base/metadata.ts +148 -28
- package/typescript/src/base/sql.ts +264 -98
- package/typescript/src/base/types.ts +27 -24
- package/typescript/src/browser.ts +10 -6
- package/typescript/src/config.ts +95 -95
- package/typescript/src/googlesheets/adapter.ts +269 -259
- package/typescript/src/googlesheets/an5SheetsAdapter.ts +3 -3
- package/typescript/src/googlesheets/config.ts +47 -47
- package/typescript/src/googlesheets/helpers.ts +144 -143
- package/typescript/src/googlesheets/index.ts +3 -3
- package/typescript/src/googlesheets/parseConnectionString.ts +48 -46
- package/typescript/src/googlesheets/retry.ts +126 -126
- package/typescript/src/googlesheets/sqlExecutor.ts +304 -301
- package/typescript/src/googlesheets/tableClient.ts +461 -455
- package/typescript/src/googlesheets/types.ts +6 -6
- package/typescript/src/index.ts +31 -27
- package/typescript/src/mssql/engine.ts +134 -132
- package/typescript/src/mssql/index.ts +1 -1
- package/typescript/src/mysql/engine.ts +80 -80
- package/typescript/src/mysql/index.ts +1 -1
- package/typescript/src/postgres/engine.ts +87 -87
- package/typescript/src/postgres/index.ts +1 -1
- package/typescript/src/sqlite/browserEngine.ts +192 -0
- package/typescript/src/sqlite/engine.ts +66 -55
- package/typescript/src/sqlite/index.ts +3 -1
- package/typescript/src/typed.ts +52 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@an5/adapters",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Standalone language adapters for AN5 ORM - TypeScript, Python, .NET query execution.",
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "Standalone language adapters for AN5 ORM - TypeScript, Python, .NET, Go, and Google Sheets query execution.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"./typescript": "./dist/index.js",
|
|
21
21
|
"./typescript/*": "./dist/*",
|
|
22
22
|
"./python": "./python/an5_adapter.py",
|
|
23
|
-
"./dotnet": "./dotnet/An5Adapter.cs"
|
|
23
|
+
"./dotnet": "./dotnet/An5Adapter.cs",
|
|
24
|
+
"./golang": "./golang/an5_adapter.go"
|
|
24
25
|
},
|
|
25
26
|
"author": "an5ORM",
|
|
26
27
|
"homepage": "https://github.com/an5ORM/an5",
|
|
@@ -34,20 +35,29 @@
|
|
|
34
35
|
"adapter",
|
|
35
36
|
"typescript",
|
|
36
37
|
"python",
|
|
37
|
-
"dotnet"
|
|
38
|
+
"dotnet",
|
|
39
|
+
"go",
|
|
40
|
+
"golang"
|
|
38
41
|
],
|
|
39
42
|
"files": [
|
|
40
43
|
"dist/**/*.{js,d.ts}",
|
|
41
44
|
"typescript/src/**/*.ts",
|
|
42
45
|
"python/**/*.py",
|
|
43
46
|
"dotnet/**/*",
|
|
47
|
+
"golang/**/*",
|
|
44
48
|
"README.md"
|
|
45
49
|
],
|
|
46
50
|
"scripts": {
|
|
47
51
|
"build": "tsc -p tsconfig.json",
|
|
48
|
-
"
|
|
52
|
+
"pretest": "npm run build",
|
|
53
|
+
"prepack": "npm run build",
|
|
54
|
+
"build:python": "(python -m build --outdir dist-py || python3 -m build --outdir dist-py)",
|
|
49
55
|
"test": "node test/unit.test.js && node test/package.test.js",
|
|
50
|
-
"test:
|
|
56
|
+
"test:package:smoke": "node scripts/package-smoke.js",
|
|
57
|
+
"test:integration:live": "node test/live-db.integration.test.js",
|
|
58
|
+
"test:python": "(python -m compileall python 2>&1 || python3 -m compileall python 2>&1)",
|
|
59
|
+
"test:dotnet": "node scripts/dotnet-compile-check.js",
|
|
60
|
+
"test:go": "cd golang && go test ./..."
|
|
51
61
|
},
|
|
52
62
|
"dependencies": {
|
|
53
63
|
"googleapis": "^134.0.0",
|
|
@@ -58,7 +68,7 @@
|
|
|
58
68
|
"@types/mssql": "^9.1.8",
|
|
59
69
|
"@types/node": "^20.11.0",
|
|
60
70
|
"@types/pg": "^8.11.0",
|
|
61
|
-
"typescript": "^
|
|
71
|
+
"typescript": "^7.0.2"
|
|
62
72
|
},
|
|
63
73
|
"publishConfig": {
|
|
64
74
|
"access": "public"
|
package/python/an5_adapter.py
CHANGED
|
@@ -1,97 +1,119 @@
|
|
|
1
|
-
"""Standalone Python runtime adapter for AN5 ORM."""
|
|
2
|
-
|
|
3
|
-
from typing import Dict, List, Optional
|
|
4
|
-
|
|
5
|
-
try:
|
|
6
|
-
from .base import DIALECT_MSSQL, DIALECT_POSTGRES, detect_dialect, set_adapter_metadata
|
|
7
|
-
from .mssql import connect as connect_mssql
|
|
8
|
-
from .postgres import connect as connect_postgres
|
|
9
|
-
from .table_client import AdapterTableClient
|
|
10
|
-
except ImportError:
|
|
11
|
-
from base import DIALECT_MSSQL, DIALECT_POSTGRES, detect_dialect, set_adapter_metadata
|
|
12
|
-
from mssql import connect as connect_mssql
|
|
13
|
-
from postgres import connect as connect_postgres
|
|
14
|
-
from table_client import AdapterTableClient
|
|
15
|
-
|
|
16
|
-
# Backward-compatible aliases used by tests and older imports.
|
|
17
|
-
_detect_dialect = detect_dialect
|
|
18
|
-
try:
|
|
19
|
-
from .mssql import parse_connection_string as _parse_connection_string
|
|
20
|
-
except ImportError:
|
|
21
|
-
from mssql import parse_connection_string as _parse_connection_string
|
|
22
|
-
|
|
23
|
-
class An5Adapter:
|
|
24
|
-
def __init__(self, connection_string: str):
|
|
25
|
-
self._dialect = detect_dialect(connection_string)
|
|
26
|
-
self._conn_str = connection_string
|
|
27
|
-
|
|
28
|
-
def _connect(self):
|
|
29
|
-
if self._dialect == DIALECT_POSTGRES:
|
|
30
|
-
return connect_postgres(self._conn_str)
|
|
31
|
-
return connect_mssql(self._conn_str)
|
|
32
|
-
|
|
33
|
-
def _to_dicts(self, cursor, query: str) -> List[Dict]:
|
|
34
|
-
if cursor.description:
|
|
35
|
-
cols = [col[0] for col in cursor.description]
|
|
36
|
-
rows = cursor.fetchall() if cursor.description else []
|
|
37
|
-
return [dict(zip(cols, row)) for row in rows]
|
|
38
|
-
return []
|
|
39
|
-
|
|
40
|
-
def exec(self, query: str, params: Optional[List] = None) -> List[Dict]:
|
|
41
|
-
conn = self._connect()
|
|
42
|
-
try:
|
|
43
|
-
cursor = conn.cursor()
|
|
44
|
-
cursor.execute(query, params or [])
|
|
45
|
-
return self._to_dicts(cursor, query)
|
|
46
|
-
finally:
|
|
47
|
-
conn.close()
|
|
48
|
-
|
|
49
|
-
def execute(self, query: str, params: Optional[List] = None) -> int:
|
|
50
|
-
conn = self._connect()
|
|
51
|
-
try:
|
|
52
|
-
cursor = conn.cursor()
|
|
53
|
-
cursor.execute(query, params or [])
|
|
54
|
-
return cursor.rowcount
|
|
55
|
-
finally:
|
|
56
|
-
conn.close()
|
|
57
|
-
|
|
58
|
-
def query_raw(self, query: str, *values) -> List[Dict]:
|
|
59
|
-
return self.exec(query, list(values))
|
|
60
|
-
|
|
61
|
-
def execute_raw(self, query: str, *values) -> int:
|
|
62
|
-
return self.execute(query, list(values))
|
|
63
|
-
|
|
64
|
-
def table(self, model_name: str) -> AdapterTableClient:
|
|
65
|
-
return AdapterTableClient(self, model_name)
|
|
66
|
-
|
|
67
|
-
def
|
|
68
|
-
return self
|
|
69
|
-
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
"""Standalone Python runtime adapter for AN5 ORM."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Optional, Any
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from .base import DIALECT_MSSQL, DIALECT_POSTGRES, detect_dialect, set_adapter_metadata
|
|
7
|
+
from .mssql import connect as connect_mssql
|
|
8
|
+
from .postgres import connect as connect_postgres
|
|
9
|
+
from .table_client import AdapterTableClient, ViewClient
|
|
10
|
+
except ImportError:
|
|
11
|
+
from base import DIALECT_MSSQL, DIALECT_POSTGRES, detect_dialect, set_adapter_metadata
|
|
12
|
+
from mssql import connect as connect_mssql
|
|
13
|
+
from postgres import connect as connect_postgres
|
|
14
|
+
from table_client import AdapterTableClient, ViewClient
|
|
15
|
+
|
|
16
|
+
# Backward-compatible aliases used by tests and older imports.
|
|
17
|
+
_detect_dialect = detect_dialect
|
|
18
|
+
try:
|
|
19
|
+
from .mssql import parse_connection_string as _parse_connection_string
|
|
20
|
+
except ImportError:
|
|
21
|
+
from mssql import parse_connection_string as _parse_connection_string
|
|
22
|
+
|
|
23
|
+
class An5Adapter:
|
|
24
|
+
def __init__(self, connection_string: str):
|
|
25
|
+
self._dialect = detect_dialect(connection_string)
|
|
26
|
+
self._conn_str = connection_string
|
|
27
|
+
|
|
28
|
+
def _connect(self):
|
|
29
|
+
if self._dialect == DIALECT_POSTGRES:
|
|
30
|
+
return connect_postgres(self._conn_str)
|
|
31
|
+
return connect_mssql(self._conn_str)
|
|
32
|
+
|
|
33
|
+
def _to_dicts(self, cursor, query: str) -> List[Dict]:
|
|
34
|
+
if cursor.description:
|
|
35
|
+
cols = [col[0] for col in cursor.description]
|
|
36
|
+
rows = cursor.fetchall() if cursor.description else []
|
|
37
|
+
return [dict(zip(cols, row)) for row in rows]
|
|
38
|
+
return []
|
|
39
|
+
|
|
40
|
+
def exec(self, query: str, params: Optional[List] = None) -> List[Dict]:
|
|
41
|
+
conn = self._connect()
|
|
42
|
+
try:
|
|
43
|
+
cursor = conn.cursor()
|
|
44
|
+
cursor.execute(query, params or [])
|
|
45
|
+
return self._to_dicts(cursor, query)
|
|
46
|
+
finally:
|
|
47
|
+
conn.close()
|
|
48
|
+
|
|
49
|
+
def execute(self, query: str, params: Optional[List] = None) -> int:
|
|
50
|
+
conn = self._connect()
|
|
51
|
+
try:
|
|
52
|
+
cursor = conn.cursor()
|
|
53
|
+
cursor.execute(query, params or [])
|
|
54
|
+
return cursor.rowcount
|
|
55
|
+
finally:
|
|
56
|
+
conn.close()
|
|
57
|
+
|
|
58
|
+
def query_raw(self, query: str, *values) -> List[Dict]:
|
|
59
|
+
return self.exec(query, list(values))
|
|
60
|
+
|
|
61
|
+
def execute_raw(self, query: str, *values) -> int:
|
|
62
|
+
return self.execute(query, list(values))
|
|
63
|
+
|
|
64
|
+
def table(self, model_name: str) -> AdapterTableClient[Any]:
|
|
65
|
+
return AdapterTableClient(self, model_name)
|
|
66
|
+
|
|
67
|
+
def view(self, view_name: str) -> ViewClient:
|
|
68
|
+
return ViewClient(self, view_name)
|
|
69
|
+
|
|
70
|
+
def query_proc(self, proc_name: str, params: Optional[List] = None) -> List[Dict]:
|
|
71
|
+
if self._dialect == DIALECT_POSTGRES:
|
|
72
|
+
placeholders = ", ".join(["%s"] * len(params or []))
|
|
73
|
+
sql = f"CALL {proc_name}({placeholders})" if placeholders else f"CALL {proc_name}()"
|
|
74
|
+
else:
|
|
75
|
+
placeholders = ", ".join(["?"] * len(params or []))
|
|
76
|
+
sql = f"EXEC {proc_name} {placeholders}" if placeholders else f"EXEC {proc_name}"
|
|
77
|
+
return self.exec(sql, params or [])
|
|
78
|
+
|
|
79
|
+
def execute_proc(self, proc_name: str, params: Optional[List] = None) -> int:
|
|
80
|
+
if self._dialect == DIALECT_POSTGRES:
|
|
81
|
+
placeholders = ", ".join(["%s"] * len(params or []))
|
|
82
|
+
sql = f"CALL {proc_name}({placeholders})" if placeholders else f"CALL {proc_name}()"
|
|
83
|
+
else:
|
|
84
|
+
placeholders = ", ".join(["?"] * len(params or []))
|
|
85
|
+
sql = f"EXEC {proc_name} {placeholders}" if placeholders else f"EXEC {proc_name}"
|
|
86
|
+
return self.execute(sql, params or [])
|
|
87
|
+
|
|
88
|
+
def __getattr__(self, model_name: str) -> AdapterTableClient:
|
|
89
|
+
return self.table(model_name)
|
|
90
|
+
|
|
91
|
+
def transaction(self, fn):
|
|
92
|
+
conn = self._connect()
|
|
93
|
+
conn.autocommit = False
|
|
94
|
+
try:
|
|
95
|
+
result = fn(self)
|
|
96
|
+
conn.commit()
|
|
97
|
+
return result
|
|
98
|
+
except Exception:
|
|
99
|
+
conn.rollback()
|
|
100
|
+
raise
|
|
101
|
+
finally:
|
|
102
|
+
if self._dialect == DIALECT_POSTGRES:
|
|
103
|
+
conn.autocommit = True
|
|
104
|
+
conn.close()
|
|
105
|
+
|
|
106
|
+
def create_an5_adapter(connection_string: str) -> An5Adapter:
|
|
107
|
+
return An5Adapter(connection_string)
|
|
108
|
+
|
|
109
|
+
__all__ = [
|
|
110
|
+
"An5Adapter",
|
|
111
|
+
"AdapterTableClient",
|
|
112
|
+
"ViewClient",
|
|
113
|
+
"create_an5_adapter",
|
|
114
|
+
"DIALECT_MSSQL",
|
|
115
|
+
"DIALECT_POSTGRES",
|
|
116
|
+
"_detect_dialect",
|
|
117
|
+
"_parse_connection_string",
|
|
118
|
+
"set_adapter_metadata",
|
|
119
|
+
]
|
package/python/base/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from .dialects import DIALECT_MSSQL, DIALECT_POSTGRES, detect_dialect
|
|
2
|
-
from .metadata import model_to_table, model_fields, set_adapter_metadata
|
|
3
|
-
from .sql import _quote, _parse_where, _build_order_by, _resolve_table, _quote_table
|
|
1
|
+
from .dialects import DIALECT_MSSQL, DIALECT_POSTGRES, detect_dialect
|
|
2
|
+
from .metadata import model_to_table, model_fields, relation_map, set_adapter_metadata, get_relations_for_model, get_model_to_table, get_fields_for_model
|
|
3
|
+
from .sql import _quote, _parse_where, _build_order_by, _resolve_table, _quote_table
|
package/python/base/dialects.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
DIALECT_MSSQL = "mssql"
|
|
2
|
-
DIALECT_POSTGRES = "postgres"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
DIALECT_MSSQL = "mssql"
|
|
2
|
+
DIALECT_POSTGRES = "postgres"
|
|
3
|
+
DIALECT_SQLITE = "sqlite"
|
|
4
|
+
|
|
5
|
+
def detect_dialect(connection_string: str) -> str:
|
|
6
|
+
cs = connection_string.strip().lower()
|
|
7
|
+
if cs.startswith("postgres://") or cs.startswith("postgresql://"):
|
|
8
|
+
return DIALECT_POSTGRES
|
|
9
|
+
if cs.startswith("sqlite://") or cs.startswith("sqlite:") or cs.startswith("file:") or cs.endswith(".db") or cs.endswith(".sqlite") or cs.endswith(".sqlite3") or cs == ":memory:":
|
|
10
|
+
return DIALECT_SQLITE
|
|
11
|
+
return DIALECT_MSSQL
|
package/python/base/metadata.py
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
2
|
-
|
|
3
|
-
model_to_table: Dict[str, str] = {}
|
|
4
|
-
model_fields: Dict[str, Any] = {}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
model_to_table.
|
|
9
|
-
|
|
10
|
-
model_fields.
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
model_to_table: Dict[str, str] = {}
|
|
4
|
+
model_fields: Dict[str, Any] = {}
|
|
5
|
+
relation_map: Dict[str, Dict[str, Dict[str, str]]] = {}
|
|
6
|
+
|
|
7
|
+
def set_adapter_metadata(metadata: Dict[str, Any]) -> None:
|
|
8
|
+
model_to_table.clear()
|
|
9
|
+
model_to_table.update(metadata.get("model_to_table") or metadata.get("modelToTable") or {})
|
|
10
|
+
model_fields.clear()
|
|
11
|
+
model_fields.update(metadata.get("model_fields") or metadata.get("modelFields") or {})
|
|
12
|
+
relation_map.clear()
|
|
13
|
+
rels = metadata.get("relation_map") or metadata.get("relationMap") or metadata.get("RELATION_MAP") or {}
|
|
14
|
+
if isinstance(rels, dict):
|
|
15
|
+
for model, relations in rels.items():
|
|
16
|
+
if not isinstance(relations, dict):
|
|
17
|
+
continue
|
|
18
|
+
relation_map[model] = {}
|
|
19
|
+
for key, defn in relations.items():
|
|
20
|
+
if isinstance(defn, dict):
|
|
21
|
+
relation_map[model][key] = {
|
|
22
|
+
"modelName": defn.get("modelName") or defn.get("model_name") or "",
|
|
23
|
+
"relationType": defn.get("relationType") or defn.get("relation_type") or "many",
|
|
24
|
+
"foreignKey": defn.get("foreignKey") or defn.get("foreign_key") or "",
|
|
25
|
+
"localKey": defn.get("localKey") or defn.get("local_key") or "",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def get_relations_for_model(model_name: str) -> Dict[str, Dict[str, str]]:
|
|
29
|
+
return relation_map.get(model_name) or {}
|
|
30
|
+
|
|
31
|
+
def get_model_to_table() -> Dict[str, str]:
|
|
32
|
+
return dict(model_to_table)
|
|
33
|
+
|
|
34
|
+
def get_fields_for_model(model_name: str) -> Any:
|
|
35
|
+
return model_fields.get(model_name)
|