xansql 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.
Files changed (232) hide show
  1. package/Types/fields/Array.d.ts +7 -0
  2. package/Types/fields/Array.js +6 -0
  3. package/Types/fields/Array.js.map +1 -0
  4. package/Types/fields/Array.mjs +6 -0
  5. package/Types/fields/Array.mjs.map +1 -0
  6. package/Types/fields/Boolean.d.ts +8 -0
  7. package/Types/fields/Boolean.js +11 -0
  8. package/Types/fields/Boolean.js.map +1 -0
  9. package/Types/fields/Boolean.mjs +11 -0
  10. package/Types/fields/Boolean.mjs.map +1 -0
  11. package/Types/fields/Date.d.ts +10 -0
  12. package/Types/fields/Date.js +22 -0
  13. package/Types/fields/Date.js.map +1 -0
  14. package/Types/fields/Date.mjs +22 -0
  15. package/Types/fields/Date.mjs.map +1 -0
  16. package/Types/fields/Enum.d.ts +8 -0
  17. package/Types/fields/Enum.js +10 -0
  18. package/Types/fields/Enum.js.map +1 -0
  19. package/Types/fields/Enum.mjs +10 -0
  20. package/Types/fields/Enum.mjs.map +1 -0
  21. package/Types/fields/File.d.ts +7 -0
  22. package/Types/fields/File.js +6 -0
  23. package/Types/fields/File.js.map +1 -0
  24. package/Types/fields/File.mjs +6 -0
  25. package/Types/fields/File.mjs.map +1 -0
  26. package/Types/fields/IDField.d.ts +6 -0
  27. package/Types/fields/IDField.js +2 -0
  28. package/Types/fields/IDField.js.map +1 -0
  29. package/Types/fields/IDField.mjs +2 -0
  30. package/Types/fields/IDField.mjs.map +1 -0
  31. package/Types/fields/Number.d.ts +8 -0
  32. package/Types/fields/Number.js +11 -0
  33. package/Types/fields/Number.js.map +1 -0
  34. package/Types/fields/Number.mjs +11 -0
  35. package/Types/fields/Number.mjs.map +1 -0
  36. package/Types/fields/Object.d.ts +8 -0
  37. package/Types/fields/Object.js +11 -0
  38. package/Types/fields/Object.js.map +1 -0
  39. package/Types/fields/Object.mjs +11 -0
  40. package/Types/fields/Object.mjs.map +1 -0
  41. package/Types/fields/Record.d.ts +8 -0
  42. package/Types/fields/Record.js +11 -0
  43. package/Types/fields/Record.js.map +1 -0
  44. package/Types/fields/Record.mjs +11 -0
  45. package/Types/fields/Record.mjs.map +1 -0
  46. package/Types/fields/Schema.d.ts +16 -0
  47. package/Types/fields/Schema.js +34 -0
  48. package/Types/fields/Schema.js.map +1 -0
  49. package/Types/fields/Schema.mjs +34 -0
  50. package/Types/fields/Schema.mjs.map +1 -0
  51. package/Types/fields/String.d.ts +10 -0
  52. package/Types/fields/String.js +20 -0
  53. package/Types/fields/String.js.map +1 -0
  54. package/Types/fields/String.mjs +20 -0
  55. package/Types/fields/String.mjs.map +1 -0
  56. package/Types/fields/Tuple.d.ts +8 -0
  57. package/Types/fields/Tuple.js +11 -0
  58. package/Types/fields/Tuple.js.map +1 -0
  59. package/Types/fields/Tuple.mjs +11 -0
  60. package/Types/fields/Tuple.mjs.map +1 -0
  61. package/Types/fields/Union.d.ts +8 -0
  62. package/Types/fields/Union.js +11 -0
  63. package/Types/fields/Union.js.map +1 -0
  64. package/Types/fields/Union.mjs +11 -0
  65. package/Types/fields/Union.mjs.map +1 -0
  66. package/Types/index.d.ts +56 -0
  67. package/Types/index.js +129 -0
  68. package/Types/index.js.map +1 -0
  69. package/Types/index.mjs +129 -0
  70. package/Types/index.mjs.map +1 -0
  71. package/Types/types.d.ts +20 -0
  72. package/core/ExcuteMeta.d.ts +11 -0
  73. package/core/ExcuteMeta.js +22 -0
  74. package/core/ExcuteMeta.js.map +1 -0
  75. package/core/ExcuteMeta.mjs +22 -0
  76. package/core/ExcuteMeta.mjs.map +1 -0
  77. package/core/Xansql.d.ts +46 -0
  78. package/core/Xansql.js +132 -0
  79. package/core/Xansql.js.map +1 -0
  80. package/core/Xansql.mjs +132 -0
  81. package/core/Xansql.mjs.map +1 -0
  82. package/core/XansqlError.js +11 -0
  83. package/core/XansqlError.js.map +1 -0
  84. package/core/XansqlError.mjs +11 -0
  85. package/core/XansqlError.mjs.map +1 -0
  86. package/core/XansqlResult.d.ts +12 -0
  87. package/core/XansqlResult.js +32 -0
  88. package/core/XansqlResult.js.map +1 -0
  89. package/core/XansqlResult.mjs +32 -0
  90. package/core/XansqlResult.mjs.map +1 -0
  91. package/core/classes/EventManager.d.ts +72 -0
  92. package/core/classes/EventManager.js +21 -0
  93. package/core/classes/EventManager.js.map +1 -0
  94. package/core/classes/EventManager.mjs +21 -0
  95. package/core/classes/EventManager.mjs.map +1 -0
  96. package/core/classes/ForeignInfo.js +51 -0
  97. package/core/classes/ForeignInfo.js.map +1 -0
  98. package/core/classes/ForeignInfo.mjs +51 -0
  99. package/core/classes/ForeignInfo.mjs.map +1 -0
  100. package/core/classes/Migration/ForeingMigration.d.ts +12 -0
  101. package/core/classes/Migration/ForeingMigration.js +52 -0
  102. package/core/classes/Migration/ForeingMigration.js.map +1 -0
  103. package/core/classes/Migration/ForeingMigration.mjs +52 -0
  104. package/core/classes/Migration/ForeingMigration.mjs.map +1 -0
  105. package/core/classes/Migration/IndexMigration.d.ts +12 -0
  106. package/core/classes/Migration/IndexMigration.js +49 -0
  107. package/core/classes/Migration/IndexMigration.js.map +1 -0
  108. package/core/classes/Migration/IndexMigration.mjs +49 -0
  109. package/core/classes/Migration/IndexMigration.mjs.map +1 -0
  110. package/core/classes/Migration/TableMigration.d.ts +33 -0
  111. package/core/classes/Migration/TableMigration.js +215 -0
  112. package/core/classes/Migration/TableMigration.js.map +1 -0
  113. package/core/classes/Migration/TableMigration.mjs +215 -0
  114. package/core/classes/Migration/TableMigration.mjs.map +1 -0
  115. package/core/classes/Migration/index.d.ts +12 -0
  116. package/core/classes/Migration/index.js +189 -0
  117. package/core/classes/Migration/index.js.map +1 -0
  118. package/core/classes/Migration/index.mjs +189 -0
  119. package/core/classes/Migration/index.mjs.map +1 -0
  120. package/core/classes/ModelFormatter.js +166 -0
  121. package/core/classes/ModelFormatter.js.map +1 -0
  122. package/core/classes/ModelFormatter.mjs +166 -0
  123. package/core/classes/ModelFormatter.mjs.map +1 -0
  124. package/core/classes/TypesGenerator.d.ts +13 -0
  125. package/core/classes/TypesGenerator.js +170 -0
  126. package/core/classes/TypesGenerator.js.map +1 -0
  127. package/core/classes/TypesGenerator.mjs +170 -0
  128. package/core/classes/TypesGenerator.mjs.map +1 -0
  129. package/core/classes/XansqlConfig.js +33 -0
  130. package/core/classes/XansqlConfig.js.map +1 -0
  131. package/core/classes/XansqlConfig.mjs +33 -0
  132. package/core/classes/XansqlConfig.mjs.map +1 -0
  133. package/core/classes/XansqlFetch.js +304 -0
  134. package/core/classes/XansqlFetch.js.map +1 -0
  135. package/core/classes/XansqlFetch.mjs +304 -0
  136. package/core/classes/XansqlFetch.mjs.map +1 -0
  137. package/core/classes/XansqlTransaction.d.ts +13 -0
  138. package/core/classes/XansqlTransaction.js +46 -0
  139. package/core/classes/XansqlTransaction.js.map +1 -0
  140. package/core/classes/XansqlTransaction.mjs +46 -0
  141. package/core/classes/XansqlTransaction.mjs.map +1 -0
  142. package/core/type.d.ts +117 -0
  143. package/index.d.ts +3 -0
  144. package/index.js +1 -0
  145. package/index.js.map +1 -0
  146. package/index.mjs +1 -0
  147. package/index.mjs.map +1 -0
  148. package/model/Args/RelationExcuteArgs.js +5 -0
  149. package/model/Args/RelationExcuteArgs.js.map +1 -0
  150. package/model/Args/RelationExcuteArgs.mjs +5 -0
  151. package/model/Args/RelationExcuteArgs.mjs.map +1 -0
  152. package/model/Args/WhereArgs.js +226 -0
  153. package/model/Args/WhereArgs.js.map +1 -0
  154. package/model/Args/WhereArgs.mjs +226 -0
  155. package/model/Args/WhereArgs.mjs.map +1 -0
  156. package/model/Base.d.ts +26 -0
  157. package/model/Base.js +64 -0
  158. package/model/Base.js.map +1 -0
  159. package/model/Base.mjs +64 -0
  160. package/model/Base.mjs.map +1 -0
  161. package/model/Executer/Aggregate/SelectArgs.js +59 -0
  162. package/model/Executer/Aggregate/SelectArgs.js.map +1 -0
  163. package/model/Executer/Aggregate/SelectArgs.mjs +59 -0
  164. package/model/Executer/Aggregate/SelectArgs.mjs.map +1 -0
  165. package/model/Executer/Aggregate/index.js +59 -0
  166. package/model/Executer/Aggregate/index.js.map +1 -0
  167. package/model/Executer/Aggregate/index.mjs +59 -0
  168. package/model/Executer/Aggregate/index.mjs.map +1 -0
  169. package/model/Executer/Create/CreateDataArgs.js +145 -0
  170. package/model/Executer/Create/CreateDataArgs.js.map +1 -0
  171. package/model/Executer/Create/CreateDataArgs.mjs +145 -0
  172. package/model/Executer/Create/CreateDataArgs.mjs.map +1 -0
  173. package/model/Executer/Create/index.js +101 -0
  174. package/model/Executer/Create/index.js.map +1 -0
  175. package/model/Executer/Create/index.mjs +101 -0
  176. package/model/Executer/Create/index.mjs.map +1 -0
  177. package/model/Executer/Delete/index.js +112 -0
  178. package/model/Executer/Delete/index.js.map +1 -0
  179. package/model/Executer/Delete/index.mjs +112 -0
  180. package/model/Executer/Delete/index.mjs.map +1 -0
  181. package/model/Executer/Find/DistinctArgs.js +32 -0
  182. package/model/Executer/Find/DistinctArgs.js.map +1 -0
  183. package/model/Executer/Find/DistinctArgs.mjs +32 -0
  184. package/model/Executer/Find/DistinctArgs.mjs.map +1 -0
  185. package/model/Executer/Find/LimitArgs.js +31 -0
  186. package/model/Executer/Find/LimitArgs.js.map +1 -0
  187. package/model/Executer/Find/LimitArgs.mjs +31 -0
  188. package/model/Executer/Find/LimitArgs.mjs.map +1 -0
  189. package/model/Executer/Find/OrderByArgs.js +29 -0
  190. package/model/Executer/Find/OrderByArgs.js.map +1 -0
  191. package/model/Executer/Find/OrderByArgs.mjs +29 -0
  192. package/model/Executer/Find/OrderByArgs.mjs.map +1 -0
  193. package/model/Executer/Find/SelectArgs.js +119 -0
  194. package/model/Executer/Find/SelectArgs.js.map +1 -0
  195. package/model/Executer/Find/SelectArgs.mjs +119 -0
  196. package/model/Executer/Find/SelectArgs.mjs.map +1 -0
  197. package/model/Executer/Find/index.js +338 -0
  198. package/model/Executer/Find/index.js.map +1 -0
  199. package/model/Executer/Find/index.mjs +338 -0
  200. package/model/Executer/Find/index.mjs.map +1 -0
  201. package/model/Executer/Update/UpdateDataArgs.js +124 -0
  202. package/model/Executer/Update/UpdateDataArgs.js.map +1 -0
  203. package/model/Executer/Update/UpdateDataArgs.mjs +124 -0
  204. package/model/Executer/Update/UpdateDataArgs.mjs.map +1 -0
  205. package/model/Executer/Update/index.js +207 -0
  206. package/model/Executer/Update/index.js.map +1 -0
  207. package/model/Executer/Update/index.mjs +207 -0
  208. package/model/Executer/Update/index.mjs.map +1 -0
  209. package/model/include/ValueFormatter.js +99 -0
  210. package/model/include/ValueFormatter.js.map +1 -0
  211. package/model/include/ValueFormatter.mjs +99 -0
  212. package/model/include/ValueFormatter.mjs.map +1 -0
  213. package/model/index.d.ts +29 -0
  214. package/model/index.js +236 -0
  215. package/model/index.js.map +1 -0
  216. package/model/index.mjs +236 -0
  217. package/model/index.mjs.map +1 -0
  218. package/model/type.d.ts +106 -0
  219. package/package.json +32 -0
  220. package/readme.md +359 -0
  221. package/utils/chunker.js +53 -0
  222. package/utils/chunker.js.map +1 -0
  223. package/utils/chunker.mjs +53 -0
  224. package/utils/chunker.mjs.map +1 -0
  225. package/utils/index.js +49 -0
  226. package/utils/index.js.map +1 -0
  227. package/utils/index.mjs +49 -0
  228. package/utils/index.mjs.map +1 -0
  229. package/utils/sha256.js +66 -0
  230. package/utils/sha256.js.map +1 -0
  231. package/utils/sha256.mjs +66 -0
  232. package/utils/sha256.mjs.map +1 -0
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.
@@ -0,0 +1,53 @@
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});/**
2
+ * Utility functions to chunk arrays or numbers into smaller parts
3
+ * @param length number of items
4
+ * @param perPage number of items per page
5
+ * @returns number of items per page
6
+ */
7
+ const dynamicPerPage = (length, perPage) => {
8
+ if (perPage)
9
+ return perPage;
10
+ if (length <= 100)
11
+ return 50;
12
+ if (length <= 500)
13
+ return 250;
14
+ if (length <= 1000)
15
+ return 500;
16
+ if (length <= 3000)
17
+ return 1000;
18
+ if (length <= 5000)
19
+ return 1500;
20
+ if (length <= 10000)
21
+ return 2000;
22
+ return Math.min(Math.ceil(length / 10), 5000);
23
+ };
24
+ /**
25
+ * Generator: chunk an array into sub-arrays
26
+ * Example: chunkArray([1,2,3,4,5], 2) → [[1,2],[3,4],[5]]
27
+ */
28
+ function* chunkArray(array, perPage) {
29
+ const length = array.length;
30
+ perPage = dynamicPerPage(length, perPage);
31
+ let chunkIndex = 0;
32
+ for (let i = 0; i < length; i += perPage) {
33
+ yield {
34
+ index: chunkIndex,
35
+ chunk: array.slice(i, i + perPage),
36
+ };
37
+ chunkIndex++;
38
+ }
39
+ }
40
+ /**
41
+ * Generate batching steps as { take, skip }
42
+ * @param total Total items to process
43
+ * @param batchSize Size of each batch
44
+ * @param skipStart Optional starting skip value (default 0)
45
+ */
46
+ function* chunkNumbers(total, skipStart = 0, perPage) {
47
+ perPage = dynamicPerPage(total, perPage);
48
+ for (let i = 0; i < total; i += perPage) {
49
+ const take = Math.min(perPage, total - i);
50
+ const skip = skipStart + i;
51
+ yield { take, skip };
52
+ }
53
+ }exports.chunkArray=chunkArray;exports.chunkNumbers=chunkNumbers;//# sourceMappingURL=chunker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunker.js","sources":["../../src/utils/chunker.ts"],"sourcesContent":["\n/**\n * Utility functions to chunk arrays or numbers into smaller parts\n * @param length number of items\n * @param perPage number of items per page\n * @returns number of items per page\n */\nconst dynamicPerPage = (length: number, perPage?: number) => {\n if (perPage) return perPage;\n if (length <= 100) return 50;\n if (length <= 500) return 250;\n if (length <= 1000) return 500;\n if (length <= 3000) return 1000;\n if (length <= 5000) return 1500;\n if (length <= 10000) return 2000;\n return Math.min(Math.ceil(length / 10), 5000);\n}\n\n/**\n * Generator: chunk an array into sub-arrays\n * Example: chunkArray([1,2,3,4,5], 2) → [[1,2],[3,4],[5]]\n */\nexport function* chunkArray<T = any>(array: T[], perPage?: number) {\n const length = array.length;\n perPage = dynamicPerPage(length, perPage);\n\n let chunkIndex = 0;\n for (let i = 0; i < length; i += perPage) {\n yield {\n index: chunkIndex,\n chunk: array.slice(i, i + perPage),\n };\n chunkIndex++;\n }\n}\n\n\n/**\n * Generate batching steps as { take, skip }\n * @param total Total items to process\n * @param batchSize Size of each batch\n * @param skipStart Optional starting skip value (default 0)\n */\nexport function* chunkNumbers(total: number, skipStart = 0, perPage?: number) {\n perPage = dynamicPerPage(total, perPage);\n for (let i = 0; i < total; i += perPage) {\n const take = Math.min(perPage, total - i);\n const skip = skipStart + i;\n yield { take, skip };\n }\n}\n"],"names":[],"mappings":"sEACA;;;;;AAKG;AACH,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,OAAgB,KAAI;AACzD,IAAA,IAAI,OAAO;AAAE,QAAA,OAAO,OAAO;IAC3B,IAAI,MAAM,IAAI,GAAG;AAAE,QAAA,OAAO,EAAE;IAC5B,IAAI,MAAM,IAAI,GAAG;AAAE,QAAA,OAAO,GAAG;IAC7B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,GAAG;IAC9B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,IAAI,MAAM,IAAI,KAAK;AAAE,QAAA,OAAO,IAAI;AAChC,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC;AAChD,CAAC;AAED;;;AAGG;UACc,UAAU,CAAU,KAAU,EAAE,OAAgB,EAAA;AAC9D,IAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM;AAC3B,IAAA,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC;IAEzC,IAAI,UAAU,GAAG,CAAC;AAClB,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,OAAO,EAAE;QACvC,MAAM;AACH,YAAA,KAAK,EAAE,UAAU;YACjB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;SACpC;AACD,QAAA,UAAU,EAAE;IACf;AACH;AAGA;;;;;AAKG;AACG,UAAW,YAAY,CAAC,KAAa,EAAE,SAAS,GAAG,CAAC,EAAE,OAAgB,EAAA;AACzE,IAAA,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC;AACxC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,OAAO,EAAE;AACtC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC;AACzC,QAAA,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC;AAC1B,QAAA,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE;IACvB;AACH"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Utility functions to chunk arrays or numbers into smaller parts
3
+ * @param length number of items
4
+ * @param perPage number of items per page
5
+ * @returns number of items per page
6
+ */
7
+ const dynamicPerPage = (length, perPage) => {
8
+ if (perPage)
9
+ return perPage;
10
+ if (length <= 100)
11
+ return 50;
12
+ if (length <= 500)
13
+ return 250;
14
+ if (length <= 1000)
15
+ return 500;
16
+ if (length <= 3000)
17
+ return 1000;
18
+ if (length <= 5000)
19
+ return 1500;
20
+ if (length <= 10000)
21
+ return 2000;
22
+ return Math.min(Math.ceil(length / 10), 5000);
23
+ };
24
+ /**
25
+ * Generator: chunk an array into sub-arrays
26
+ * Example: chunkArray([1,2,3,4,5], 2) → [[1,2],[3,4],[5]]
27
+ */
28
+ function* chunkArray(array, perPage) {
29
+ const length = array.length;
30
+ perPage = dynamicPerPage(length, perPage);
31
+ let chunkIndex = 0;
32
+ for (let i = 0; i < length; i += perPage) {
33
+ yield {
34
+ index: chunkIndex,
35
+ chunk: array.slice(i, i + perPage),
36
+ };
37
+ chunkIndex++;
38
+ }
39
+ }
40
+ /**
41
+ * Generate batching steps as { take, skip }
42
+ * @param total Total items to process
43
+ * @param batchSize Size of each batch
44
+ * @param skipStart Optional starting skip value (default 0)
45
+ */
46
+ function* chunkNumbers(total, skipStart = 0, perPage) {
47
+ perPage = dynamicPerPage(total, perPage);
48
+ for (let i = 0; i < total; i += perPage) {
49
+ const take = Math.min(perPage, total - i);
50
+ const skip = skipStart + i;
51
+ yield { take, skip };
52
+ }
53
+ }export{chunkArray,chunkNumbers};//# sourceMappingURL=chunker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunker.mjs","sources":["../../src/utils/chunker.ts"],"sourcesContent":["\n/**\n * Utility functions to chunk arrays or numbers into smaller parts\n * @param length number of items\n * @param perPage number of items per page\n * @returns number of items per page\n */\nconst dynamicPerPage = (length: number, perPage?: number) => {\n if (perPage) return perPage;\n if (length <= 100) return 50;\n if (length <= 500) return 250;\n if (length <= 1000) return 500;\n if (length <= 3000) return 1000;\n if (length <= 5000) return 1500;\n if (length <= 10000) return 2000;\n return Math.min(Math.ceil(length / 10), 5000);\n}\n\n/**\n * Generator: chunk an array into sub-arrays\n * Example: chunkArray([1,2,3,4,5], 2) → [[1,2],[3,4],[5]]\n */\nexport function* chunkArray<T = any>(array: T[], perPage?: number) {\n const length = array.length;\n perPage = dynamicPerPage(length, perPage);\n\n let chunkIndex = 0;\n for (let i = 0; i < length; i += perPage) {\n yield {\n index: chunkIndex,\n chunk: array.slice(i, i + perPage),\n };\n chunkIndex++;\n }\n}\n\n\n/**\n * Generate batching steps as { take, skip }\n * @param total Total items to process\n * @param batchSize Size of each batch\n * @param skipStart Optional starting skip value (default 0)\n */\nexport function* chunkNumbers(total: number, skipStart = 0, perPage?: number) {\n perPage = dynamicPerPage(total, perPage);\n for (let i = 0; i < total; i += perPage) {\n const take = Math.min(perPage, total - i);\n const skip = skipStart + i;\n yield { take, skip };\n }\n}\n"],"names":[],"mappings":"AACA;;;;;AAKG;AACH,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,OAAgB,KAAI;AACzD,IAAA,IAAI,OAAO;AAAE,QAAA,OAAO,OAAO;IAC3B,IAAI,MAAM,IAAI,GAAG;AAAE,QAAA,OAAO,EAAE;IAC5B,IAAI,MAAM,IAAI,GAAG;AAAE,QAAA,OAAO,GAAG;IAC7B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,GAAG;IAC9B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,IAAI,MAAM,IAAI,KAAK;AAAE,QAAA,OAAO,IAAI;AAChC,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC;AAChD,CAAC;AAED;;;AAGG;UACc,UAAU,CAAU,KAAU,EAAE,OAAgB,EAAA;AAC9D,IAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM;AAC3B,IAAA,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC;IAEzC,IAAI,UAAU,GAAG,CAAC;AAClB,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,OAAO,EAAE;QACvC,MAAM;AACH,YAAA,KAAK,EAAE,UAAU;YACjB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;SACpC;AACD,QAAA,UAAU,EAAE;IACf;AACH;AAGA;;;;;AAKG;AACG,UAAW,YAAY,CAAC,KAAa,EAAE,SAAS,GAAG,CAAC,EAAE,OAAgB,EAAA;AACzE,IAAA,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC;AACxC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,OAAO,EAAE;AACtC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC;AACzC,QAAA,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC;AAC1B,QAAA,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE;IACvB;AACH"}
package/utils/index.js ADDED
@@ -0,0 +1,49 @@
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});const isArray = (v) => Array.isArray(v);
2
+ // export const isObject = (v: any) => typeof v === 'object' && v !== null && !isArray(v) && !(v instanceof Date) && !(v instanceof RegExp) && !(v instanceof Buffer) && !(v instanceof Uint8Array) && !(v instanceof ArrayBuffer)
3
+ const isObject = (v) => Object.prototype.toString.call(v) === '[object Object]';
4
+ const isNumber = (v) => typeof v === 'number' && !isNaN(v);
5
+ const escapeSqlValue = (value) => {
6
+ return value
7
+ .replace(/'/g, "''") // Escape single quote
8
+ .replace(/\x00/g, '\\0'); // Escape null byte (rare but can break SQL)
9
+ };
10
+ // export const ErrorWhene = (_if: any, message: string) => {
11
+ // if (_if) {
12
+ // throw new Error(message);
13
+ // }
14
+ // }
15
+ const quote = (engine, identifier) => {
16
+ if (engine === 'mysql')
17
+ return `\`${identifier}\``;
18
+ if (engine === 'postgresql' || engine === 'sqlite')
19
+ return `"${identifier}"`;
20
+ return identifier;
21
+ };
22
+ const uid = (str, length = 28) => {
23
+ let h1 = 0x811c9dc5;
24
+ let h2 = 0x811c9dc5 ^ str.length;
25
+ // Simple dual-hash loop
26
+ for (let i = 0; i < str.length; i++) {
27
+ const c = str.charCodeAt(i);
28
+ h1 = Math.imul(h1 ^ c, 0x1000193);
29
+ h2 = Math.imul(h2 ^ (c + i * 17), 0x85ebca6b);
30
+ }
31
+ // Base36 mix gives letters + digits
32
+ let base = (h1 >>> 0).toString(36) + (h2 >>> 0).toString(36);
33
+ // Add derived chars from original string to strengthen variety
34
+ for (let i = 0; i < str.length; i++) {
35
+ const code = str.charCodeAt(i);
36
+ base += ((code * (i + 31)) % 36).toString(36);
37
+ }
38
+ // Scramble characters deterministically based on input
39
+ const arr = base.split('');
40
+ for (let i = arr.length - 1; i > 0; i--) {
41
+ const j = (str.charCodeAt(i % str.length) + i * 19) % arr.length;
42
+ [arr[i], arr[j]] = [arr[j], arr[i]];
43
+ }
44
+ // Ensure letters are mixed in
45
+ const mixed = arr
46
+ .map((c, i) => i % 3 === 0 ? String.fromCharCode(97 + (c.charCodeAt(0) % 26)) : c)
47
+ .join('');
48
+ return mixed.slice(0, length);
49
+ };exports.escapeSqlValue=escapeSqlValue;exports.isArray=isArray;exports.isNumber=isNumber;exports.isObject=isObject;exports.quote=quote;exports.uid=uid;//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/utils/index.ts"],"sourcesContent":["import XanvType from \"xanv/XanvType\"\nimport { XansqlDialectEngine } from \"../core/type\";\n\n\nexport const isServer = () => typeof window === 'undefined'\nexport const isArray = (v: any) => Array.isArray(v)\n// export const isObject = (v: any) => typeof v === 'object' && v !== null && !isArray(v) && !(v instanceof Date) && !(v instanceof RegExp) && !(v instanceof Buffer) && !(v instanceof Uint8Array) && !(v instanceof ArrayBuffer)\nexport const isObject = (v: any) => Object.prototype.toString.call(v) === '[object Object]';\nexport const isString = (v: any) => typeof v === 'string'\nexport const isNumber = (v: any) => typeof v === 'number' && !isNaN(v)\nexport const isBoolean = (v: any) => typeof v === 'boolean'\n\nexport const formatValue = (v: any, xanv?: XanvType<any, any>): any => {\n if (isArray(v)) return v.map((item) => formatValue(item, xanv)).join(',')\n !!xanv && xanv.parse(v);\n if (v instanceof Date) v = v.toISOString()\n if (isString(v)) return `'${escapeSqlValue(v)}'`\n if (isNumber(v)) return v\n if (isBoolean(v)) return v ? 'TRUE' : 'FALSE'\n if (v === null) return 'NULL'\n if (v === undefined) return 'NULL'\n}\n\nexport const arrayMove = (arr: any[], fromIndex: number, toIndex: number) => {\n const newArr = [...arr];\n const item = newArr.splice(fromIndex, 1)[0];\n newArr.splice(toIndex, 0, item);\n return newArr;\n}\n\nexport const escapeSqlValue = (value: string): string => {\n return value\n .replace(/'/g, \"''\") // Escape single quote\n .replace(/\\x00/g, '\\\\0'); // Escape null byte (rare but can break SQL)\n}\n\n\nexport const freezeObject = (obj: any) => {\n Object.getOwnPropertyNames(obj).forEach((prop) => {\n const value = obj[prop];\n if (value && typeof value === \"object\") {\n freezeObject(value); // recursively freeze\n }\n });\n return Object.freeze(obj);\n}\n\n\n\n// export const ErrorWhene = (_if: any, message: string) => {\n// if (_if) {\n// throw new Error(message);\n// }\n// }\n\nexport const quote = (engine: XansqlDialectEngine, identifier: string) => {\n if (engine === 'mysql') return `\\`${identifier}\\``;\n if (engine === 'postgresql' || engine === 'sqlite') return `\"${identifier}\"`;\n return identifier;\n}\n\nexport const uid = (str: string, length = 28): string => {\n let h1 = 0x811c9dc5;\n let h2 = 0x811c9dc5 ^ str.length;\n\n // Simple dual-hash loop\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n h1 = Math.imul(h1 ^ c, 0x1000193);\n h2 = Math.imul(h2 ^ (c + i * 17), 0x85ebca6b);\n }\n\n // Base36 mix gives letters + digits\n let base = (h1 >>> 0).toString(36) + (h2 >>> 0).toString(36);\n\n // Add derived chars from original string to strengthen variety\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n base += ((code * (i + 31)) % 36).toString(36);\n }\n\n // Scramble characters deterministically based on input\n const arr = base.split('');\n for (let i = arr.length - 1; i > 0; i--) {\n const j = (str.charCodeAt(i % str.length) + i * 19) % arr.length;\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n\n // Ensure letters are mixed in\n const mixed = arr\n .map((c, i) =>\n i % 3 === 0 ? String.fromCharCode(97 + (c.charCodeAt(0) % 26)) : c\n )\n .join('');\n\n return mixed.slice(0, length);\n}\n\nexport function hash(length = 16): string {\n let result = '';\n while (result.length < length) {\n result += Math.random().toString(36).slice(2);\n }\n return result.slice(0, length);\n}\n\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ const isArray = (v) => Array.isArray(v);
2
+ // export const isObject = (v: any) => typeof v === 'object' && v !== null && !isArray(v) && !(v instanceof Date) && !(v instanceof RegExp) && !(v instanceof Buffer) && !(v instanceof Uint8Array) && !(v instanceof ArrayBuffer)
3
+ const isObject = (v) => Object.prototype.toString.call(v) === '[object Object]';
4
+ const isNumber = (v) => typeof v === 'number' && !isNaN(v);
5
+ const escapeSqlValue = (value) => {
6
+ return value
7
+ .replace(/'/g, "''") // Escape single quote
8
+ .replace(/\x00/g, '\\0'); // Escape null byte (rare but can break SQL)
9
+ };
10
+ // export const ErrorWhene = (_if: any, message: string) => {
11
+ // if (_if) {
12
+ // throw new Error(message);
13
+ // }
14
+ // }
15
+ const quote = (engine, identifier) => {
16
+ if (engine === 'mysql')
17
+ return `\`${identifier}\``;
18
+ if (engine === 'postgresql' || engine === 'sqlite')
19
+ return `"${identifier}"`;
20
+ return identifier;
21
+ };
22
+ const uid = (str, length = 28) => {
23
+ let h1 = 0x811c9dc5;
24
+ let h2 = 0x811c9dc5 ^ str.length;
25
+ // Simple dual-hash loop
26
+ for (let i = 0; i < str.length; i++) {
27
+ const c = str.charCodeAt(i);
28
+ h1 = Math.imul(h1 ^ c, 0x1000193);
29
+ h2 = Math.imul(h2 ^ (c + i * 17), 0x85ebca6b);
30
+ }
31
+ // Base36 mix gives letters + digits
32
+ let base = (h1 >>> 0).toString(36) + (h2 >>> 0).toString(36);
33
+ // Add derived chars from original string to strengthen variety
34
+ for (let i = 0; i < str.length; i++) {
35
+ const code = str.charCodeAt(i);
36
+ base += ((code * (i + 31)) % 36).toString(36);
37
+ }
38
+ // Scramble characters deterministically based on input
39
+ const arr = base.split('');
40
+ for (let i = arr.length - 1; i > 0; i--) {
41
+ const j = (str.charCodeAt(i % str.length) + i * 19) % arr.length;
42
+ [arr[i], arr[j]] = [arr[j], arr[i]];
43
+ }
44
+ // Ensure letters are mixed in
45
+ const mixed = arr
46
+ .map((c, i) => i % 3 === 0 ? String.fromCharCode(97 + (c.charCodeAt(0) % 26)) : c)
47
+ .join('');
48
+ return mixed.slice(0, length);
49
+ };export{escapeSqlValue,isArray,isNumber,isObject,quote,uid};//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../src/utils/index.ts"],"sourcesContent":["import XanvType from \"xanv/XanvType\"\nimport { XansqlDialectEngine } from \"../core/type\";\n\n\nexport const isServer = () => typeof window === 'undefined'\nexport const isArray = (v: any) => Array.isArray(v)\n// export const isObject = (v: any) => typeof v === 'object' && v !== null && !isArray(v) && !(v instanceof Date) && !(v instanceof RegExp) && !(v instanceof Buffer) && !(v instanceof Uint8Array) && !(v instanceof ArrayBuffer)\nexport const isObject = (v: any) => Object.prototype.toString.call(v) === '[object Object]';\nexport const isString = (v: any) => typeof v === 'string'\nexport const isNumber = (v: any) => typeof v === 'number' && !isNaN(v)\nexport const isBoolean = (v: any) => typeof v === 'boolean'\n\nexport const formatValue = (v: any, xanv?: XanvType<any, any>): any => {\n if (isArray(v)) return v.map((item) => formatValue(item, xanv)).join(',')\n !!xanv && xanv.parse(v);\n if (v instanceof Date) v = v.toISOString()\n if (isString(v)) return `'${escapeSqlValue(v)}'`\n if (isNumber(v)) return v\n if (isBoolean(v)) return v ? 'TRUE' : 'FALSE'\n if (v === null) return 'NULL'\n if (v === undefined) return 'NULL'\n}\n\nexport const arrayMove = (arr: any[], fromIndex: number, toIndex: number) => {\n const newArr = [...arr];\n const item = newArr.splice(fromIndex, 1)[0];\n newArr.splice(toIndex, 0, item);\n return newArr;\n}\n\nexport const escapeSqlValue = (value: string): string => {\n return value\n .replace(/'/g, \"''\") // Escape single quote\n .replace(/\\x00/g, '\\\\0'); // Escape null byte (rare but can break SQL)\n}\n\n\nexport const freezeObject = (obj: any) => {\n Object.getOwnPropertyNames(obj).forEach((prop) => {\n const value = obj[prop];\n if (value && typeof value === \"object\") {\n freezeObject(value); // recursively freeze\n }\n });\n return Object.freeze(obj);\n}\n\n\n\n// export const ErrorWhene = (_if: any, message: string) => {\n// if (_if) {\n// throw new Error(message);\n// }\n// }\n\nexport const quote = (engine: XansqlDialectEngine, identifier: string) => {\n if (engine === 'mysql') return `\\`${identifier}\\``;\n if (engine === 'postgresql' || engine === 'sqlite') return `\"${identifier}\"`;\n return identifier;\n}\n\nexport const uid = (str: string, length = 28): string => {\n let h1 = 0x811c9dc5;\n let h2 = 0x811c9dc5 ^ str.length;\n\n // Simple dual-hash loop\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n h1 = Math.imul(h1 ^ c, 0x1000193);\n h2 = Math.imul(h2 ^ (c + i * 17), 0x85ebca6b);\n }\n\n // Base36 mix gives letters + digits\n let base = (h1 >>> 0).toString(36) + (h2 >>> 0).toString(36);\n\n // Add derived chars from original string to strengthen variety\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n base += ((code * (i + 31)) % 36).toString(36);\n }\n\n // Scramble characters deterministically based on input\n const arr = base.split('');\n for (let i = arr.length - 1; i > 0; i--) {\n const j = (str.charCodeAt(i % str.length) + i * 19) % arr.length;\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n\n // Ensure letters are mixed in\n const mixed = arr\n .map((c, i) =>\n i % 3 === 0 ? String.fromCharCode(97 + (c.charCodeAt(0) % 26)) : c\n )\n .join('');\n\n return mixed.slice(0, length);\n}\n\nexport function hash(length = 16): string {\n let result = '';\n while (result.length < length) {\n result += Math.random().toString(36).slice(2);\n }\n return result.slice(0, length);\n}\n\n"],"names":[],"mappings":"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"}