@an5/adapters 0.2.4 → 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.
Files changed (82) hide show
  1. package/README.md +189 -304
  2. package/dist/an5Adapter.d.ts +85 -7
  3. package/dist/an5Adapter.js +736 -64
  4. package/dist/base/metadata.d.ts +10 -0
  5. package/dist/base/metadata.js +107 -0
  6. package/dist/base/sql.d.ts +19 -1
  7. package/dist/base/sql.js +152 -24
  8. package/dist/base/types.d.ts +4 -1
  9. package/dist/browser.d.ts +5 -2
  10. package/dist/browser.js +10 -2
  11. package/dist/googlesheets/adapter.d.ts +1 -0
  12. package/dist/googlesheets/adapter.js +9 -2
  13. package/dist/googlesheets/config.d.ts +4 -4
  14. package/dist/googlesheets/helpers.js +2 -0
  15. package/dist/googlesheets/parseConnectionString.js +5 -2
  16. package/dist/googlesheets/sqlExecutor.js +29 -24
  17. package/dist/googlesheets/tableClient.js +14 -5
  18. package/dist/index.d.ts +5 -1
  19. package/dist/index.js +12 -1
  20. package/dist/mssql/engine.js +8 -4
  21. package/dist/mysql/engine.js +1 -1
  22. package/dist/postgres/engine.js +1 -1
  23. package/dist/sqlite/browserEngine.d.ts +27 -0
  24. package/dist/sqlite/browserEngine.js +161 -0
  25. package/dist/sqlite/engine.js +15 -3
  26. package/dist/sqlite/index.d.ts +1 -0
  27. package/dist/sqlite/index.js +1 -0
  28. package/dist/typed.d.ts +5 -0
  29. package/dist/typed.js +2 -0
  30. package/dotnet/An5Adapter.cs +1089 -275
  31. package/dotnet/Base/Core.cs +340 -82
  32. package/dotnet/Mssql/MssqlEngine.cs +145 -144
  33. package/dotnet/Postgres/PostgresEngine.cs +121 -121
  34. package/golang/an5_adapter.go +314 -0
  35. package/golang/base/base.go +100 -0
  36. package/golang/base/metadata.go +116 -0
  37. package/golang/base/where.go +311 -0
  38. package/golang/go.mod +3 -0
  39. package/golang/mssql/mssql.go +32 -0
  40. package/golang/postgres/postgres.go +37 -0
  41. package/golang/sqlite/sqlite.go +14 -0
  42. package/golang/sqlite_test.go +42 -0
  43. package/golang/table_client.go +1329 -0
  44. package/package.json +18 -8
  45. package/python/an5_adapter.py +119 -97
  46. package/python/base/__init__.py +3 -3
  47. package/python/base/dialects.py +11 -8
  48. package/python/base/metadata.py +35 -10
  49. package/python/base/sql.py +172 -121
  50. package/python/mssql/__init__.py +1 -1
  51. package/python/mssql/provider.py +62 -62
  52. package/python/postgres/__init__.py +1 -1
  53. package/python/postgres/provider.py +15 -15
  54. package/python/table_client.py +619 -216
  55. package/typescript/src/an5Adapter.ts +1220 -505
  56. package/typescript/src/base/index.ts +3 -3
  57. package/typescript/src/base/metadata.ts +148 -28
  58. package/typescript/src/base/sql.ts +264 -98
  59. package/typescript/src/base/types.ts +27 -24
  60. package/typescript/src/browser.ts +10 -6
  61. package/typescript/src/config.ts +95 -95
  62. package/typescript/src/googlesheets/adapter.ts +269 -259
  63. package/typescript/src/googlesheets/an5SheetsAdapter.ts +3 -3
  64. package/typescript/src/googlesheets/config.ts +47 -47
  65. package/typescript/src/googlesheets/helpers.ts +144 -143
  66. package/typescript/src/googlesheets/index.ts +3 -3
  67. package/typescript/src/googlesheets/parseConnectionString.ts +48 -46
  68. package/typescript/src/googlesheets/retry.ts +126 -126
  69. package/typescript/src/googlesheets/sqlExecutor.ts +304 -301
  70. package/typescript/src/googlesheets/tableClient.ts +461 -455
  71. package/typescript/src/googlesheets/types.ts +6 -6
  72. package/typescript/src/index.ts +32 -19
  73. package/typescript/src/mssql/engine.ts +134 -132
  74. package/typescript/src/mssql/index.ts +1 -1
  75. package/typescript/src/mysql/engine.ts +80 -80
  76. package/typescript/src/mysql/index.ts +1 -1
  77. package/typescript/src/postgres/engine.ts +87 -87
  78. package/typescript/src/postgres/index.ts +1 -1
  79. package/typescript/src/sqlite/browserEngine.ts +192 -0
  80. package/typescript/src/sqlite/engine.ts +66 -55
  81. package/typescript/src/sqlite/index.ts +3 -1
  82. package/typescript/src/typed.ts +52 -0
@@ -1,82 +1,340 @@
1
- using System;
2
- using System.Collections.Generic;
3
- using System.Data.Common;
4
-
5
- namespace An5Orm
6
- {
7
- public enum Dialect
8
- {
9
- Mssql,
10
- Postgres
11
- }
12
-
13
- // ─── Config ────────────────────────────────────────────────────────────────
14
-
15
- public class An5AdapterOptions
16
- {
17
- public string ConnectionString { get; set; }
18
- public int CommandTimeout { get; set; } = 60;
19
- public int ConnectRetryCount { get; set; } = 3;
20
- }
21
-
22
- // ─── Query Engine Interface ────────────────────────────────────────────────
23
-
24
- internal interface IQueryEngine
25
- {
26
- Dialect Dialect { get; }
27
- List<Dictionary<string, object>> QueryRaw(string sql, Dictionary<string, object> parameters);
28
- List<T> QueryRaw<T>(string sql, Dictionary<string, object> parameters) where T : new();
29
- int ExecuteRaw(string sql, Dictionary<string, object> parameters);
30
- An5TransactionBase BeginTransaction();
31
- }
32
-
33
- // ─── Transaction base ──────────────────────────────────────────────────────
34
-
35
- internal abstract class An5TransactionBase : IDisposable
36
- {
37
- public abstract void Commit();
38
- public abstract void Rollback();
39
- public abstract void Dispose();
40
- }
41
-
42
- // ─── Dialect detection ─────────────────────────────────────────────────────
43
-
44
- internal static class DialectDetector
45
- {
46
- public static Dialect Detect(string connectionString)
47
- {
48
- var cs = (connectionString ?? "").Trim().ToLowerInvariant();
49
- if (cs.StartsWith("postgres://") || cs.StartsWith("postgresql://") || cs.Contains("host="))
50
- return Dialect.Postgres;
51
- return Dialect.Mssql;
52
- }
53
- }
54
-
55
- // ─── Quoting ───────────────────────────────────────────────────────────────
56
-
57
- internal static class SqlQuote
58
- {
59
- public static string QuoteName(string name, Dialect dialect)
60
- {
61
- if (dialect == Dialect.Postgres)
62
- {
63
- if (name.StartsWith("[") && name.EndsWith("]"))
64
- return "\"" + name.Substring(1, name.Length - 2) + "\"";
65
- return "\"" + name + "\"";
66
- }
67
- return name.StartsWith("[") ? name : "[" + name + "]";
68
- }
69
-
70
- public static string QuoteTable(string tableName, Dialect dialect)
71
- {
72
- if (tableName.StartsWith("[") || tableName.StartsWith("\""))
73
- return tableName;
74
- if (tableName.Contains("."))
75
- {
76
- var parts = tableName.Split('.');
77
- return string.Join(".", Array.ConvertAll(parts, p => QuoteName(p, dialect)));
78
- }
79
- return QuoteName(tableName, dialect);
80
- }
81
- }
82
- }
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Data.Common;
4
+
5
+ namespace An5Orm
6
+ {
7
+ public enum Dialect
8
+ {
9
+ Mssql,
10
+ Postgres
11
+ }
12
+
13
+ // ─── Config ────────────────────────────────────────────────────────────────
14
+
15
+ public class An5AdapterOptions
16
+ {
17
+ public string ConnectionString { get; set; }
18
+ public int CommandTimeout { get; set; } = 60;
19
+ public int ConnectRetryCount { get; set; } = 3;
20
+ }
21
+
22
+ // ─── Query Engine Interface ────────────────────────────────────────────────
23
+
24
+ internal interface IQueryEngine
25
+ {
26
+ Dialect Dialect { get; }
27
+ List<Dictionary<string, object>> QueryRaw(string sql, Dictionary<string, object> parameters);
28
+ List<T> QueryRaw<T>(string sql, Dictionary<string, object> parameters) where T : new();
29
+ int ExecuteRaw(string sql, Dictionary<string, object> parameters);
30
+ An5TransactionBase BeginTransaction();
31
+ }
32
+
33
+ // ─── Transaction base ──────────────────────────────────────────────────────
34
+
35
+ internal abstract class An5TransactionBase : IDisposable
36
+ {
37
+ public abstract void Commit();
38
+ public abstract void Rollback();
39
+ public abstract void Dispose();
40
+ }
41
+
42
+ // ─── Dialect detection ─────────────────────────────────────────────────────
43
+
44
+ internal static class DialectDetector
45
+ {
46
+ public static Dialect Detect(string connectionString)
47
+ {
48
+ var cs = (connectionString ?? "").Trim().ToLowerInvariant();
49
+ if (cs.StartsWith("postgres://") || cs.StartsWith("postgresql://") || cs.Contains("host="))
50
+ return Dialect.Postgres;
51
+ return Dialect.Mssql;
52
+ }
53
+ }
54
+
55
+ // ─── Quoting ───────────────────────────────────────────────────────────────
56
+
57
+ internal static class SqlQuote
58
+ {
59
+ private static string StripWrapping(string name, string left, string right)
60
+ {
61
+ return name.StartsWith(left) && name.EndsWith(right)
62
+ ? name.Substring(left.Length, name.Length - left.Length - right.Length)
63
+ : name;
64
+ }
65
+
66
+ public static string QuoteName(string name, Dialect dialect)
67
+ {
68
+ var raw = name ?? "";
69
+ if (dialect == Dialect.Postgres)
70
+ {
71
+ var unwrapped = StripWrapping(StripWrapping(raw, "[", "]"), "\"", "\"");
72
+ return "\"" + unwrapped.Replace("\"", "\"\"") + "\"";
73
+ }
74
+ var bracketless = StripWrapping(raw, "[", "]");
75
+ return "[" + bracketless.Replace("]", "]]") + "]";
76
+ }
77
+
78
+ public static string QuoteTable(string tableName, Dialect dialect)
79
+ {
80
+ if (tableName.StartsWith("[") || tableName.StartsWith("\""))
81
+ return tableName;
82
+ if (tableName.Contains("."))
83
+ {
84
+ var parts = tableName.Split('.');
85
+ return string.Join(".", Array.ConvertAll(parts, p => QuoteName(p, dialect)));
86
+ }
87
+ return QuoteName(tableName, dialect);
88
+ }
89
+ }
90
+
91
+ // ─── Metadata registry ────────────────────────────────────────────────────
92
+
93
+ public class RelationDef
94
+ {
95
+ public string ModelName { get; set; }
96
+ public string RelationType { get; set; } // "many" | "one"
97
+ public string ForeignKey { get; set; }
98
+ public string LocalKey { get; set; }
99
+ }
100
+
101
+ public class AdapterMetadata
102
+ {
103
+ public Dictionary<string, string> ModelToTable { get; set; } = new();
104
+ public Dictionary<string, object> ModelFields { get; set; } = new();
105
+ public Dictionary<string, Dictionary<string, RelationDef>> RelationMap { get; set; } = new();
106
+ }
107
+
108
+ internal static class An5Metadata
109
+ {
110
+ private static readonly object _lock = new();
111
+ private static Dictionary<string, string> _modelToTable = new();
112
+ private static Dictionary<string, object> _modelFields = new();
113
+ private static Dictionary<string, Dictionary<string, RelationDef>> _relationMap = new();
114
+
115
+ public static void SetAdapterMetadata(AdapterMetadata meta)
116
+ {
117
+ lock (_lock)
118
+ {
119
+ _modelToTable = meta?.ModelToTable != null ? new Dictionary<string, string>(meta.ModelToTable) : new();
120
+ _modelFields = meta?.ModelFields != null ? new Dictionary<string, object>(meta.ModelFields) : new();
121
+ _relationMap = new Dictionary<string, Dictionary<string, RelationDef>>();
122
+ if (meta?.RelationMap != null)
123
+ {
124
+ foreach (var kv in meta.RelationMap)
125
+ {
126
+ _relationMap[kv.Key] = new Dictionary<string, RelationDef>(kv.Value);
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ public static Dictionary<string, string> GetModelToTable()
133
+ {
134
+ lock (_lock) return new Dictionary<string, string>(_modelToTable);
135
+ }
136
+
137
+ public static object GetFieldsForModel(string modelName)
138
+ {
139
+ lock (_lock)
140
+ {
141
+ return _modelFields.TryGetValue(modelName, out var v) ? v : null;
142
+ }
143
+ }
144
+
145
+ public static Dictionary<string, RelationDef> GetRelationsForModel(string modelName)
146
+ {
147
+ lock (_lock)
148
+ {
149
+ return _relationMap.TryGetValue(modelName, out var r) ? new Dictionary<string, RelationDef>(r) : new Dictionary<string, RelationDef>();
150
+ }
151
+ }
152
+ }
153
+
154
+ // ─── Where / OrderBy builder ───────────────────────────────────────────────
155
+ // Mirrors the TypeScript parseWhere/buildOrderBy used by the reference adapter.
156
+
157
+ internal static class SqlBuilder
158
+ {
159
+ private static readonly string[] OperatorKeys =
160
+ { "equals", "in", "notIn", "contains", "startsWith", "endsWith", "not", "gte", "lte", "gt", "lt" };
161
+
162
+ private static bool IsOperatorValue(object value)
163
+ {
164
+ if (!(value is Dictionary<string, object> dict)) return false;
165
+ foreach (var op in OperatorKeys)
166
+ if (dict.ContainsKey(op)) return true;
167
+ return false;
168
+ }
169
+
170
+ internal static string SanitizeParamName(string name)
171
+ {
172
+ var sb = new System.Text.StringBuilder();
173
+ foreach (var ch in (name ?? ""))
174
+ sb.Append(char.IsLetterOrDigit(ch) || ch == '_' ? ch : '_');
175
+ var cleaned = sb.ToString();
176
+ return cleaned.Length > 0 && char.IsLetter(cleaned[0]) ? cleaned : "p_" + cleaned;
177
+ }
178
+
179
+ private static string NormalizeSortDirection(object direction)
180
+ {
181
+ return direction is string s && s.ToUpperInvariant() == "DESC" ? "DESC" : "ASC";
182
+ }
183
+
184
+ private static string Placeholder(string paramName) => "@" + paramName;
185
+
186
+ public static string BuildOrderBy(object orderBy, Dialect dialect)
187
+ {
188
+ if (orderBy == null) return "";
189
+ var entries = orderBy is System.Collections.IList list ? list : new List<object> { orderBy };
190
+ var parts = new List<string>();
191
+ foreach (var entry in entries)
192
+ {
193
+ if (!(entry is Dictionary<string, object> map)) continue;
194
+ foreach (var kv in map)
195
+ parts.Add($"{SqlQuote.QuoteName(kv.Key, dialect)} {NormalizeSortDirection(kv.Value)}");
196
+ }
197
+ return parts.Count > 0 ? "ORDER BY " + string.Join(", ", parts) : "";
198
+ }
199
+
200
+ public static string ParseWhere(object where, Dictionary<string, object> parameters, Dialect dialect, string prefix = "")
201
+ {
202
+ if (!(where is Dictionary<string, object> dict) || dict.Count == 0) return "";
203
+
204
+ var clean = new Dictionary<string, object>();
205
+ foreach (var kv in dict)
206
+ {
207
+ if (kv.Key.Contains("_") && kv.Value is Dictionary<string, object> inner && !IsOperatorValue(inner))
208
+ {
209
+ foreach (var sub in inner) clean[sub.Key] = sub.Value;
210
+ }
211
+ else
212
+ {
213
+ clean[kv.Key] = kv.Value;
214
+ }
215
+ }
216
+
217
+ var conditions = new List<string>();
218
+ foreach (var kv in clean)
219
+ {
220
+ var key = kv.Key;
221
+ var value = kv.Value;
222
+ if (key == "OR" && value is System.Collections.IList orList && orList.Count > 0)
223
+ {
224
+ var subs = new List<string>();
225
+ var i = 0;
226
+ foreach (var item in orList)
227
+ {
228
+ if (item is Dictionary<string, object> subMap)
229
+ {
230
+ var s = ParseWhere(subMap, parameters, dialect, $"{prefix}or{i++}_");
231
+ if (!string.IsNullOrEmpty(s)) subs.Add(s);
232
+ }
233
+ }
234
+ if (subs.Count > 0) conditions.Add("(" + string.Join(" OR ", subs) + ")");
235
+ continue;
236
+ }
237
+ if (key == "AND" && value is System.Collections.IList andList && andList.Count > 0)
238
+ {
239
+ var subs = new List<string>();
240
+ var i = 0;
241
+ foreach (var item in andList)
242
+ {
243
+ if (item is Dictionary<string, object> subMap)
244
+ {
245
+ var s = ParseWhere(subMap, parameters, dialect, $"{prefix}and{i++}_");
246
+ if (!string.IsNullOrEmpty(s)) subs.Add(s);
247
+ }
248
+ }
249
+ if (subs.Count > 0) conditions.Add("(" + string.Join(" AND ", subs) + ")");
250
+ continue;
251
+ }
252
+ if (key == "NOT" && value is Dictionary<string, object> notMap)
253
+ {
254
+ var s = ParseWhere(notMap, parameters, dialect, $"{prefix}not_");
255
+ if (!string.IsNullOrEmpty(s)) conditions.Add("NOT (" + s + ")");
256
+ continue;
257
+ }
258
+
259
+ var col = SqlQuote.QuoteName(key, dialect);
260
+ var baseName = SanitizeParamName($"{prefix}{key}");
261
+ if (value == null)
262
+ {
263
+ conditions.Add($"{col} IS NULL");
264
+ }
265
+ else if (value is Dictionary<string, object> opMap)
266
+ {
267
+ foreach (var op in OperatorKeys)
268
+ {
269
+ if (!opMap.TryGetValue(op, out var opVal)) continue;
270
+ switch (op)
271
+ {
272
+ case "not":
273
+ if (opVal == null) conditions.Add($"{col} IS NOT NULL");
274
+ else if (opVal is Dictionary<string, object> nested)
275
+ {
276
+ var s = ParseWhere(new Dictionary<string, object> { { key, nested } }, parameters, dialect, $"{prefix}{key}_not_");
277
+ if (!string.IsNullOrEmpty(s)) conditions.Add("NOT (" + s + ")");
278
+ }
279
+ else { parameters[baseName + "_not"] = opVal; conditions.Add($"{col} <> {Placeholder(baseName + "_not")}"); }
280
+ break;
281
+ case "equals":
282
+ if (opVal == null) conditions.Add($"{col} IS NULL");
283
+ else { parameters[baseName + "_eq"] = opVal; conditions.Add($"{col} = {Placeholder(baseName + "_eq")}"); }
284
+ break;
285
+ case "contains":
286
+ parameters[baseName + "_co"] = "%" + opVal + "%"; conditions.Add($"{col} LIKE {Placeholder(baseName + "_co")}");
287
+ break;
288
+ case "startsWith":
289
+ parameters[baseName + "_sw"] = opVal + "%"; conditions.Add($"{col} LIKE {Placeholder(baseName + "_sw")}");
290
+ break;
291
+ case "endsWith":
292
+ parameters[baseName + "_ew"] = "%" + opVal; conditions.Add($"{col} LIKE {Placeholder(baseName + "_ew")}");
293
+ break;
294
+ case "gte":
295
+ parameters[baseName + "_gte"] = opVal; conditions.Add($"{col} >= {Placeholder(baseName + "_gte")}");
296
+ break;
297
+ case "lte":
298
+ parameters[baseName + "_lte"] = opVal; conditions.Add($"{col} <= {Placeholder(baseName + "_lte")}");
299
+ break;
300
+ case "gt":
301
+ parameters[baseName + "_gt"] = opVal; conditions.Add($"{col} > {Placeholder(baseName + "_gt")}");
302
+ break;
303
+ case "lt":
304
+ parameters[baseName + "_lt"] = opVal; conditions.Add($"{col} < {Placeholder(baseName + "_lt")}");
305
+ break;
306
+ case "in":
307
+ case "notIn":
308
+ if (opVal is System.Collections.IList vals && vals.Count > 0)
309
+ {
310
+ var phs = new List<string>();
311
+ var idx = 0;
312
+ foreach (var v in vals)
313
+ {
314
+ var pn = op == "in" ? $"{baseName}_in{idx}" : $"{baseName}_notin{idx}";
315
+ parameters[pn] = v;
316
+ phs.Add(Placeholder(pn));
317
+ idx++;
318
+ }
319
+ conditions.Add(op == "in"
320
+ ? $"{col} IN ({string.Join(", ", phs)})"
321
+ : $"{col} NOT IN ({string.Join(", ", phs)})");
322
+ }
323
+ else
324
+ {
325
+ conditions.Add(op == "in" ? "1=0" : "1=1");
326
+ }
327
+ break;
328
+ }
329
+ }
330
+ }
331
+ else
332
+ {
333
+ parameters[baseName] = value;
334
+ conditions.Add($"{col} = {Placeholder(baseName)}");
335
+ }
336
+ }
337
+ return string.Join(" AND ", conditions);
338
+ }
339
+ }
340
+ }