hola-server 2.0.1 → 3.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 (242) hide show
  1. package/README.md +318 -132
  2. package/dist/config/index.d.ts +46 -0
  3. package/dist/config/index.d.ts.map +1 -0
  4. package/dist/config/index.js +55 -0
  5. package/dist/config/index.js.map +1 -0
  6. package/dist/core/array.d.ts +27 -0
  7. package/dist/core/array.d.ts.map +1 -0
  8. package/dist/core/array.js +66 -0
  9. package/dist/core/array.js.map +1 -0
  10. package/dist/core/bash.d.ts +51 -0
  11. package/dist/core/bash.d.ts.map +1 -0
  12. package/dist/core/bash.js +161 -0
  13. package/dist/core/bash.js.map +1 -0
  14. package/dist/core/chart.d.ts +11 -0
  15. package/dist/core/chart.d.ts.map +1 -0
  16. package/dist/core/chart.js +35 -0
  17. package/dist/core/chart.js.map +1 -0
  18. package/dist/core/date.d.ts +11 -0
  19. package/dist/core/date.d.ts.map +1 -0
  20. package/dist/core/date.js +18 -0
  21. package/dist/core/date.js.map +1 -0
  22. package/dist/core/encrypt.d.ts +18 -0
  23. package/dist/core/encrypt.d.ts.map +1 -0
  24. package/dist/core/encrypt.js +50 -0
  25. package/dist/core/encrypt.js.map +1 -0
  26. package/dist/core/file.d.ts +22 -0
  27. package/dist/core/file.d.ts.map +1 -0
  28. package/dist/core/file.js +21 -0
  29. package/dist/core/file.js.map +1 -0
  30. package/dist/core/lhs.d.ts +17 -0
  31. package/dist/core/lhs.d.ts.map +1 -0
  32. package/dist/core/lhs.js +30 -0
  33. package/dist/core/lhs.js.map +1 -0
  34. package/dist/core/meta.d.ts +200 -0
  35. package/dist/core/meta.d.ts.map +1 -0
  36. package/dist/core/meta.js +336 -0
  37. package/dist/core/meta.js.map +1 -0
  38. package/dist/core/number.d.ts +37 -0
  39. package/dist/core/number.d.ts.map +1 -0
  40. package/dist/core/number.js +99 -0
  41. package/dist/core/number.js.map +1 -0
  42. package/dist/core/obj.d.ts +9 -0
  43. package/dist/core/obj.d.ts.map +1 -0
  44. package/dist/core/obj.js +15 -0
  45. package/dist/core/obj.js.map +1 -0
  46. package/dist/core/random.d.ts +7 -0
  47. package/dist/core/random.d.ts.map +1 -0
  48. package/dist/core/random.js +7 -0
  49. package/dist/core/random.js.map +1 -0
  50. package/dist/core/role.d.ts +42 -0
  51. package/dist/core/role.d.ts.map +1 -0
  52. package/dist/core/role.js +81 -0
  53. package/dist/core/role.js.map +1 -0
  54. package/dist/core/thread.d.ts +7 -0
  55. package/dist/core/thread.d.ts.map +1 -0
  56. package/dist/core/thread.js +7 -0
  57. package/dist/core/thread.js.map +1 -0
  58. package/dist/core/type.d.ts +46 -0
  59. package/dist/core/type.d.ts.map +1 -0
  60. package/dist/core/type.js +281 -0
  61. package/dist/core/type.js.map +1 -0
  62. package/dist/core/url.d.ts +20 -0
  63. package/dist/core/url.d.ts.map +1 -0
  64. package/dist/core/url.js +24 -0
  65. package/dist/core/url.js.map +1 -0
  66. package/dist/core/validate.d.ts +11 -0
  67. package/dist/core/validate.d.ts.map +1 -0
  68. package/dist/core/validate.js +19 -0
  69. package/dist/core/validate.js.map +1 -0
  70. package/dist/db/db.d.ts +72 -0
  71. package/dist/db/db.d.ts.map +1 -0
  72. package/dist/db/db.js +225 -0
  73. package/dist/db/db.js.map +1 -0
  74. package/dist/db/entity.d.ts +77 -0
  75. package/dist/db/entity.d.ts.map +1 -0
  76. package/dist/db/entity.js +671 -0
  77. package/dist/db/entity.js.map +1 -0
  78. package/dist/db/gridfs.d.ts +29 -0
  79. package/dist/db/gridfs.d.ts.map +1 -0
  80. package/dist/db/gridfs.js +125 -0
  81. package/dist/db/gridfs.js.map +1 -0
  82. package/dist/db/index.d.ts +8 -0
  83. package/dist/db/index.d.ts.map +1 -0
  84. package/dist/db/index.js +8 -0
  85. package/dist/db/index.js.map +1 -0
  86. package/dist/errors/auth.d.ts +15 -0
  87. package/dist/errors/auth.d.ts.map +1 -0
  88. package/dist/errors/auth.js +21 -0
  89. package/dist/errors/auth.js.map +1 -0
  90. package/dist/errors/http.d.ts +15 -0
  91. package/dist/errors/http.d.ts.map +1 -0
  92. package/dist/errors/http.js +21 -0
  93. package/dist/errors/http.js.map +1 -0
  94. package/dist/errors/index.d.ts +18 -0
  95. package/dist/errors/index.d.ts.map +1 -0
  96. package/dist/errors/index.js +18 -0
  97. package/dist/errors/index.js.map +1 -0
  98. package/dist/errors/validation.d.ts +11 -0
  99. package/dist/errors/validation.d.ts.map +1 -0
  100. package/dist/errors/validation.js +15 -0
  101. package/dist/errors/validation.js.map +1 -0
  102. package/dist/http/code.d.ts +21 -0
  103. package/dist/http/code.d.ts.map +1 -0
  104. package/dist/http/code.js +27 -0
  105. package/dist/http/code.js.map +1 -0
  106. package/dist/index.d.ts +57 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +61 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/meta/index.d.ts +9 -0
  111. package/dist/meta/index.d.ts.map +1 -0
  112. package/dist/meta/index.js +11 -0
  113. package/dist/meta/index.js.map +1 -0
  114. package/dist/meta/router.d.ts +26 -0
  115. package/dist/meta/router.d.ts.map +1 -0
  116. package/dist/meta/router.js +258 -0
  117. package/dist/meta/router.js.map +1 -0
  118. package/dist/meta/schema.d.ts +41 -0
  119. package/dist/meta/schema.d.ts.map +1 -0
  120. package/dist/meta/schema.js +69 -0
  121. package/dist/meta/schema.js.map +1 -0
  122. package/dist/plugins/auth.d.ts +248 -0
  123. package/dist/plugins/auth.d.ts.map +1 -0
  124. package/dist/plugins/auth.js +121 -0
  125. package/dist/plugins/auth.js.map +1 -0
  126. package/dist/plugins/body.d.ts +47 -0
  127. package/dist/plugins/body.d.ts.map +1 -0
  128. package/dist/plugins/body.js +36 -0
  129. package/dist/plugins/body.js.map +1 -0
  130. package/dist/plugins/cors.d.ts +62 -0
  131. package/dist/plugins/cors.d.ts.map +1 -0
  132. package/dist/plugins/cors.js +17 -0
  133. package/dist/plugins/cors.js.map +1 -0
  134. package/dist/plugins/error.d.ts +51 -0
  135. package/dist/plugins/error.d.ts.map +1 -0
  136. package/dist/plugins/error.js +51 -0
  137. package/dist/plugins/error.js.map +1 -0
  138. package/dist/plugins/index.d.ts +9 -0
  139. package/dist/plugins/index.d.ts.map +1 -0
  140. package/dist/plugins/index.js +9 -0
  141. package/dist/plugins/index.js.map +1 -0
  142. package/dist/setting.d.ts +66 -0
  143. package/dist/setting.d.ts.map +1 -0
  144. package/dist/setting.js +27 -0
  145. package/dist/setting.js.map +1 -0
  146. package/dist/tool/gen_i18n.d.ts +10 -0
  147. package/dist/tool/gen_i18n.d.ts.map +1 -0
  148. package/{tool → dist/tool}/gen_i18n.js +9 -22
  149. package/dist/tool/gen_i18n.js.map +1 -0
  150. package/dist/tool/vector_store.d.ts +72 -0
  151. package/dist/tool/vector_store.d.ts.map +1 -0
  152. package/dist/tool/vector_store.js +203 -0
  153. package/dist/tool/vector_store.js.map +1 -0
  154. package/package.json +37 -22
  155. package/core/array.js +0 -124
  156. package/core/bash.js +0 -294
  157. package/core/chart.js +0 -46
  158. package/core/cron.js +0 -21
  159. package/core/date.js +0 -26
  160. package/core/encrypt.js +0 -26
  161. package/core/file.js +0 -51
  162. package/core/lhs.js +0 -53
  163. package/core/meta.js +0 -283
  164. package/core/msg.js +0 -24
  165. package/core/number.js +0 -181
  166. package/core/obj.js +0 -25
  167. package/core/random.js +0 -12
  168. package/core/role.js +0 -108
  169. package/core/thread.js +0 -13
  170. package/core/type.js +0 -368
  171. package/core/url.js +0 -30
  172. package/core/validate.js +0 -35
  173. package/db/db.js +0 -446
  174. package/db/entity.js +0 -920
  175. package/db/gridfs.js +0 -175
  176. package/design/add_default_field_attr.md +0 -56
  177. package/http/code.js +0 -18
  178. package/http/context.js +0 -31
  179. package/http/cors.js +0 -32
  180. package/http/error.js +0 -39
  181. package/http/express.js +0 -104
  182. package/http/params.js +0 -85
  183. package/http/router.js +0 -83
  184. package/http/session.js +0 -73
  185. package/index.js +0 -112
  186. package/router/clone.js +0 -65
  187. package/router/create.js +0 -54
  188. package/router/delete.js +0 -49
  189. package/router/read.js +0 -191
  190. package/router/update.js +0 -89
  191. package/setting.js +0 -67
  192. package/skills/array.md +0 -155
  193. package/skills/bash.md +0 -91
  194. package/skills/chart.md +0 -54
  195. package/skills/code.md +0 -422
  196. package/skills/context.md +0 -177
  197. package/skills/date.md +0 -58
  198. package/skills/express.md +0 -255
  199. package/skills/file.md +0 -60
  200. package/skills/lhs.md +0 -54
  201. package/skills/meta.md +0 -1023
  202. package/skills/msg.md +0 -30
  203. package/skills/number.md +0 -88
  204. package/skills/obj.md +0 -36
  205. package/skills/params.md +0 -206
  206. package/skills/random.md +0 -22
  207. package/skills/role.md +0 -59
  208. package/skills/session.md +0 -281
  209. package/skills/storage.md +0 -743
  210. package/skills/thread.md +0 -22
  211. package/skills/type.md +0 -547
  212. package/skills/url.md +0 -34
  213. package/skills/validate.md +0 -48
  214. package/test/cleanup/close-db.js +0 -5
  215. package/test/core/array.js +0 -226
  216. package/test/core/chart.js +0 -51
  217. package/test/core/date.js +0 -37
  218. package/test/core/encrypt.js +0 -14
  219. package/test/core/file.js +0 -59
  220. package/test/core/lhs.js +0 -44
  221. package/test/core/meta.js +0 -594
  222. package/test/core/number.js +0 -172
  223. package/test/core/obj.js +0 -47
  224. package/test/core/random.js +0 -24
  225. package/test/core/thread.js +0 -20
  226. package/test/core/type.js +0 -216
  227. package/test/core/validate.js +0 -67
  228. package/test/db/db-ops.js +0 -99
  229. package/test/db/db.js +0 -72
  230. package/test/db/pipe_test.txt +0 -0
  231. package/test/db/test_case_design.md +0 -528
  232. package/test/db/test_db_class.js +0 -613
  233. package/test/db/test_entity_class.js +0 -414
  234. package/test/db/test_gridfs_class.js +0 -234
  235. package/test/entity/create.js +0 -442
  236. package/test/entity/delete-mixed.js +0 -156
  237. package/test/entity/delete.js +0 -480
  238. package/test/entity/read.js +0 -285
  239. package/test/entity/ref-filter.js +0 -63
  240. package/test/entity/update.js +0 -252
  241. package/test/router/role.js +0 -15
  242. package/tool/test.json +0 -25
package/README.md CHANGED
@@ -1,196 +1,382 @@
1
1
  # Hola Server
2
2
 
3
- A meta-programming framework for building Node.js RESTful APIs with MongoDB. Hola Server provides a declarative, metadata-driven approach to building CRUD APIs with built-in authentication, role-based access control, and file handling.
4
-
5
- ## Features
6
-
7
- - **Meta-Driven CRUD**: Define entity schemas with metadata and get full CRUD APIs automatically
8
- - **Role-Based Access Control**: Fine-grained permissions per entity, operation, and view
9
- - **Type System**: Extensible type conversion and validation system
10
- - **File Handling**: Built-in GridFS integration for file uploads
11
- - **Session Management**: Express session with MongoDB store
12
- - **Entity Relationships**: Support for references with cascade/keep delete behavior
13
- - **Query Building**: Advanced search, filtering, and pagination
14
- - **Async Context**: Request-scoped context using AsyncLocalStorage
15
- - **Comprehensive Testing**: Full test suite with 111+ passing tests
16
-
17
- ## Installation
3
+ A meta-programming framework for building RESTful APIs with **Bun + Elysia + MongoDB**. Define entity schemas declaratively and get fully-featured CRUD APIs with built-in authentication, role-based access control, file handling, and vector search — all out of the box.
4
+
5
+ ## Features
6
+
7
+ - **Meta-Driven CRUD** Define entity schemas with metadata, get full RESTful APIs automatically
8
+ - **Role-Based Access Control** Fine-grained permissions per entity and operation (`admin:*`, `user:r`, `editor:cru`)
9
+ - **JWT Authentication** Access + refresh token flow with hybrid delivery (cookies & Authorization header)
10
+ - **Type System** — 20+ built-in types with extensible validation and conversion
11
+ - **Schema Validation** Auto-generated TypeBox schemas for request body validation
12
+ - **File Handling** Built-in GridFS integration for file uploads and streaming
13
+ - **Vector Search** SQLite-based vector similarity search via `sqlite-vec` for AI applications
14
+ - **Entity Relationships** References with cascade/keep delete behavior and ref validation
15
+ - **Lifecycle Hooks** Before/after hooks for create, update, clone, delete, and read operations
16
+ - **Query Building** — Advanced search, filtering, comparison operators, and pagination
17
+ - **Environment Config** — Configuration loader with environment-specific files (dev/test/prod)
18
+ - **Comprehensive Testing** — 25 test files covering core, database, entity, and cleanup
19
+
20
+ ## 📦 Tech Stack
21
+
22
+ | Layer | Technology |
23
+ |-------|-----------|
24
+ | Runtime | [Bun](https://bun.sh) |
25
+ | HTTP Framework | [Elysia](https://elysiajs.com) v1.2+ |
26
+ | Database | [MongoDB](https://www.mongodb.com) 6.x |
27
+ | File Storage | MongoDB GridFS |
28
+ | Vector Search | [sqlite-vec](https://github.com/asg017/sqlite-vec) + better-sqlite3 |
29
+ | Auth | [@elysiajs/jwt](https://www.npmjs.com/package/@elysiajs/jwt) |
30
+ | Language | TypeScript 5.7+ |
31
+
32
+ ## 🚀 Quick Start
33
+
34
+ ### Installation
18
35
 
19
36
  ```bash
20
- npm install
37
+ bun install
21
38
  ```
22
39
 
23
- ## Quick Start
24
-
25
- ### 1. Configure Settings
40
+ ### 1. Define an Entity
26
41
 
27
- ```javascript
28
- const { init_settings } = require("hola-server");
29
-
30
- init_settings({
31
- mongo: {
32
- url: "mongodb://localhost/myapp",
33
- pool: 10,
34
- },
35
- server: {
36
- service_port: 8088,
37
- client_web_url: ["http://localhost:3000"],
38
- session: {
39
- secret: "your-secret-key",
40
- cookie_max_age: 86400000, // 1 day
41
- },
42
- },
43
- roles: [{ name: "admin", root: true }, { name: "user" }],
44
- });
45
- ```
42
+ ```typescript
43
+ import { init_router } from "hola-server";
46
44
 
47
- ### 2. Define Entity Metadata
45
+ export const userRouter = init_router({
46
+ collection: "user",
47
+ primary_keys: ["email"],
48
+ ref_label: "name",
48
49
 
49
- ```javascript
50
- const { EntityMeta } = require("hola-server");
50
+ creatable: true,
51
+ readable: true,
52
+ updatable: true,
53
+ deleteable: true,
51
54
 
52
- const user_meta = {
53
- collection: "user",
54
- mode: "crud",
55
55
  fields: [
56
56
  { name: "name", type: "string", required: true },
57
- { name: "email", type: "string", required: true, primary: true },
57
+ { name: "email", type: "string", required: true },
58
58
  { name: "age", type: "uint" },
59
- { name: "role", type: "obj", ref: "role" },
59
+ { name: "role", ref: "role" },
60
+ { name: "avatar", type: "file" },
60
61
  ],
61
- roles: ["admin:crud:*", "user:r:*"],
62
- };
62
+
63
+ roles: ["admin:*", "user:r", "editor:cru"],
64
+ });
63
65
  ```
64
66
 
65
- ### 3. Create Router
67
+ ### 2. Start the Server
68
+
69
+ ```typescript
70
+ import { Elysia } from "elysia";
71
+ import { plugins, db, meta } from "hola-server";
72
+ import { userRouter } from "./router/user.js";
73
+
74
+ const app = new Elysia()
75
+ .use(plugins.holaCors({ origin: ["http://localhost:5173"] }))
76
+ .use(plugins.holaBody({ limit: "10mb" }))
77
+ .use(plugins.holaAuth({ secret: process.env.JWT_SECRET! }))
78
+ .use(plugins.holaError())
79
+ .use(userRouter)
80
+ .onStart(async () => {
81
+ await db.get_db();
82
+ meta.validate_all_metas();
83
+ console.log("✓ Server ready on port 3000");
84
+ })
85
+ .listen(3000);
86
+ ```
66
87
 
67
- ```javascript
68
- const { init_router } = require("hola-server");
88
+ ## 📡 API Endpoints
89
+
90
+ Each entity router automatically generates these endpoints:
91
+
92
+ | Method | Path | Description |
93
+ |--------|------|-------------|
94
+ | `GET` | `/{entity}` | Simple list with query params |
95
+ | `POST` | `/{entity}/list` | List entities (full query: filter, sort, paginate) |
96
+ | `GET` | `/{entity}/:id` | Get single entity by ID |
97
+ | `POST` | `/{entity}` | Create entity |
98
+ | `PUT` | `/{entity}/:id` | Update entity |
99
+ | `DELETE` | `/{entity}/:id` | Delete entity |
100
+ | `GET` | `/{entity}/meta` | Get entity metadata and permissions |
101
+ | `GET` | `/{entity}/ref` | Get reference labels |
102
+ | `POST` | `/{entity}/:id/clone` | Clone entity (if cloneable) |
103
+
104
+ ## 🏗 Entity Metadata
105
+
106
+ ### Field Definition
107
+
108
+ ```typescript
109
+ interface FieldDefinition {
110
+ name: string; // Field name
111
+ type?: string; // Type for validation/conversion (default: "string")
112
+ required?: boolean; // Required on create
113
+ default?: FieldValue;// Default value
114
+ ref?: string; // Reference to another entity (collection name)
115
+ link?: string; // Link to another entity
116
+ delete?: "keep" | "cascade"; // Ref delete behavior
117
+ create?: boolean; // Include in create operation
118
+ list?: boolean; // Include in list response
119
+ search?: boolean; // Enable as search field
120
+ update?: boolean; // Include in update operation
121
+ clone?: boolean; // Include in clone operation
122
+ sys?: boolean; // System field (auto-managed)
123
+ secure?: boolean; // Exclude from client responses
124
+ view?: string; // View-based visibility
125
+ role?: string | string[]; // Role-based field visibility
126
+ }
127
+ ```
69
128
 
70
- const router = init_router(user_meta);
71
- module.exports = router;
129
+ ### Built-in Types
130
+
131
+ | Category | Types |
132
+ |----------|-------|
133
+ | **String** | `string`, `lstr`, `text`, `enum`, `email`, `url`, `ip` |
134
+ | **Numeric** | `number`, `int`, `uint`, `float`, `ufloat`, `decimal`, `percentage`, `currency` |
135
+ | **Boolean** | `boolean` |
136
+ | **Date/Time** | `date`, `datetime` |
137
+ | **Security** | `password` (bcrypt hashed), `secret` (AES-256 encrypted) |
138
+ | **Other** | `file`, `array`, `obj`, `json`, `log_category` |
139
+
140
+ Custom types can be registered via `register_type()`.
141
+
142
+ ### Operation Flags
143
+
144
+ ```typescript
145
+ {
146
+ creatable: true, // Enable POST endpoint
147
+ readable: true, // Enable GET endpoints
148
+ updatable: true, // Enable PUT endpoint
149
+ deleteable: true, // Enable DELETE endpoint
150
+ cloneable: true, // Enable clone endpoint
151
+ importable: true, // Enable bulk import
152
+ }
72
153
  ```
73
154
 
74
- ### 4. Start Server
155
+ ### Role Configuration
156
+
157
+ Format: `role_name:permissions` or `role_name:permissions:view`
158
+
159
+ ```typescript
160
+ roles: [
161
+ "admin:*", // Admin can do everything
162
+ "user:r", // User can only read
163
+ "editor:cru", // Editor can create/read/update
164
+ "viewer:r:basic" // Viewer can read with "basic" view only
165
+ ]
166
+ ```
167
+
168
+ ### Lifecycle Hooks
169
+
170
+ ```typescript
171
+ init_router({
172
+ collection: "order",
173
+ // ...fields and options...
174
+
175
+ before_create: async ({ entity, data }) => { /* validate before insert */ },
176
+ create: async ({ entity, data }) => { /* custom create logic */ },
177
+ after_create: async ({ entity, data }) => { /* post-create side effects */ },
75
178
 
76
- ```javascript
77
- const { init_express_server } = require("hola-server");
179
+ before_update: async ({ id, entity, data }) => { /* pre-update checks */ },
180
+ update: async ({ id, entity, data }) => { /* custom update logic */ },
181
+ after_update: async ({ id, entity, data }) => { /* post-update actions */ },
78
182
 
79
- init_express_server(__dirname, "service_port", async () => {
80
- console.log("Server started on port 8088");
183
+ before_delete: async ({ entity, ids }) => { /* pre-delete validation */ },
184
+ after_delete: async ({ entity, ids }) => { /* cleanup after delete */ },
185
+
186
+ after_read: async ({ id, entity, result }) => { /* transform read results */ },
187
+ list_query: async ({ entity, query }) => { /* modify list queries */ },
188
+
189
+ route: (router, meta) => { /* add custom routes */ },
81
190
  });
82
191
  ```
83
192
 
84
- ## Entity Metadata
193
+ ## 🔌 Plugins
85
194
 
86
- ### Field Types
195
+ ```typescript
196
+ import { plugins } from "hola-server";
87
197
 
88
- - `string` - String with trim
89
- - `int` - Integer
90
- - `uint` - Unsigned integer
91
- - `float` - Float with 2 decimal places
92
- - `ufloat` - Unsigned float
93
- - `number` - Any number
94
- - `boolean` - Boolean
95
- - `password` - Encrypted password
96
- - `array` - Array type
97
- - `obj` - Object/Reference type
98
- - `file` - File upload
198
+ // CORS with allowed origins
199
+ app.use(plugins.holaCors({ origin: ["http://localhost:5173"] }));
99
200
 
100
- ### Field Options
201
+ // Request body parsing with size limit
202
+ app.use(plugins.holaBody({ limit: "10mb" }));
101
203
 
102
- - `required` - Field is required
103
- - `primary` - Primary key (unique)
104
- - `searchable` - Searchable in queries
105
- - `invisible` - Hidden from responses
106
- - `sys` - System field
107
- - `ref` - Reference to another entity
108
- - `delete` - Cascade behavior: `cascade` or `keep`
204
+ // JWT auth with access + refresh tokens
205
+ app.use(plugins.holaAuth({
206
+ secret: "your-secret",
207
+ accessExpiry: "15m", // default
208
+ refreshExpiry: "7d", // default
209
+ excludeUrls: ["/health", /^\/public/],
210
+ }));
109
211
 
110
- ### CRUD Modes
212
+ // Auth routes (POST /auth/refresh, POST /auth/logout)
213
+ app.use(plugins.holaAuthRoutes());
111
214
 
112
- - `c` - Create
113
- - `r` - Read
114
- - `u` - Update
115
- - `d` - Delete
116
- - `o` - Clone
117
- - `crud` - All operations
215
+ // Global error handling
216
+ app.use(plugins.holaError());
217
+ ```
118
218
 
119
- ### Role Configuration
219
+ ## 🗄 Database
120
220
 
121
- Format: `role_name:mode:view`
221
+ ```typescript
222
+ import { db } from "hola-server";
122
223
 
123
- Example:
224
+ // Direct MongoDB access
225
+ const database = await db.get_db();
226
+ const users = await database.collection("user").find({}).toArray();
124
227
 
125
- ```javascript
126
- roles: [
127
- "admin:crud:*", // Admin can do all operations on all views
128
- "user:r:public", // User can only read public view
129
- "editor:cru:edit", // Editor can create/read/update edit view
130
- ];
228
+ // Entity-level operations
229
+ const entity = new db.Entity("user");
230
+ await entity.create_entity(data, "*");
231
+ await entity.read_entity(id, "*", "*");
232
+ await entity.update_entity(id, updates, "*");
233
+ await entity.delete_entity([id]);
234
+ await entity.list_entity(queryParams, query, searchParams, role);
235
+ ```
236
+
237
+ ## 📁 File Storage (GridFS)
238
+
239
+ ```typescript
240
+ import { db } from "hola-server";
241
+
242
+ // Save file from buffer (FormData uploads)
243
+ await db.save_file_from_buffer("avatars", "user_123.png", buffer);
244
+
245
+ // Read file
246
+ const fileBuffer = await db.read_file("avatars", "user_123.png");
247
+
248
+ // Stream file
249
+ const stream = await db.read_file_stream("avatars", "user_123.png");
250
+
251
+ // Delete file
252
+ await db.delete_file("avatars", "user_123.png");
131
253
  ```
132
254
 
133
- ## API Endpoints
255
+ ## 🔍 Vector Search
134
256
 
135
- For an entity with `mode: 'crud'`:
257
+ SQLite-based vector similarity search for AI/embedding applications:
136
258
 
137
- - `POST /{entity}/create` - Create entity
138
- - `POST /{entity}/query` - Query entities
139
- - `POST /{entity}/count` - Count entities
140
- - `POST /{entity}/update` - Update entity
141
- - `POST /{entity}/delete` - Delete entity
142
- - `POST /{entity}/clone` - Clone entity (if cloneable)
259
+ ```typescript
260
+ import { VectorStore, initVectorStore } from "hola-server";
143
261
 
144
- ## Core Utilities
262
+ const store = await initVectorStore({
263
+ dbPath: "./data/vectors.db",
264
+ dimensions: 1536,
265
+ tableName: "embeddings",
266
+ });
145
267
 
146
- ### Database
268
+ // Insert vectors with metadata
269
+ await store.insert("doc_1", embedding, { category: "article" });
270
+ await store.insertBatch(records);
147
271
 
148
- ```javascript
149
- const { get_db } = require("hola-server");
272
+ // Search similar vectors
273
+ const results = await store.search(queryEmbedding, 10, { category: "article" });
274
+ // => [{ id, distance, score, metadata }]
150
275
 
151
- const db = await get_db();
152
- const users = await db.collection("user").find({});
276
+ // Manage vectors
277
+ await store.delete("doc_1");
278
+ await store.deleteByFilter({ category: "old" });
279
+ const count = await store.count();
153
280
  ```
154
281
 
155
- ### Entity Operations
282
+ ## ⚙️ Configuration
283
+
284
+ ### Environment-Based Config
156
285
 
157
- ```javascript
158
- const { Entity } = require("hola-server");
286
+ ```typescript
287
+ import { config } from "hola-server";
159
288
 
160
- const entity = new Entity(meta);
161
- await entity.create_entity(data);
162
- await entity.find_entity(query);
163
- await entity.update_entity(id, updates);
164
- await entity.delete_entity(id);
289
+ const appConfig = await config.load_config(__dirname + "/config");
290
+ // Loads config/dev.ts, config/test.ts, or config/prod.ts based on NODE_ENV
165
291
  ```
166
292
 
167
- ### Logging
293
+ ### Settings
168
294
 
169
- ```javascript
170
- const { log_info, log_error } = require("hola-server");
295
+ ```typescript
296
+ import { init_settings } from "hola-server";
171
297
 
172
- log_info("user", "User logged in", { user_id });
173
- log_error("auth", "Login failed", { email });
298
+ init_settings({
299
+ mongo: { url: "mongodb://localhost:27017/myapp", pool: 10 },
300
+ encrypt: { key: "my-encryption-key" },
301
+ roles: [{ name: "admin", root: true }, { name: "user" }],
302
+ server: {
303
+ service_port: 8088,
304
+ client_web_url: ["http://localhost:5173"],
305
+ keep_session: true,
306
+ check_user: true,
307
+ exclude_urls: ["/"],
308
+ session: { cookie_max_age: 86400000, secret: "session-secret" },
309
+ threshold: { max_download_size: 5000, body_limit: "10mb", default_list_limit: 1000 },
310
+ routes: ["router"],
311
+ },
312
+ axios: { retry: 3, retry_delay: 1000, proxy: null },
313
+ log: { col_log: "log", log_level: 0, save_db: true },
314
+ });
174
315
  ```
175
316
 
176
- ## Testing
317
+ ## 🧰 Core Utilities
318
+
319
+ ```typescript
320
+ import { array, date, validate, encrypt, random, bash, file, obj, number, lhs } from "hola-server";
321
+
322
+ // Array utilities
323
+ array.unique([1, 2, 2, 3]); // [1, 2, 3]
324
+
325
+ // Encryption
326
+ const hashed = encrypt_pwd("password");
327
+ const encrypted = encrypt_secret("api-key");
328
+ const decrypted = decrypt_secret(encrypted);
329
+
330
+ // And more: date formatting, validation, bash execution, file I/O, etc.
331
+ ```
332
+
333
+ ## 🧪 Testing
177
334
 
178
335
  ```bash
179
- npm test
336
+ bun test
180
337
  ```
181
338
 
182
- ## Project Structure
339
+ 25 test files covering:
340
+ - **Core**: array, chart, date, encrypt, file, lhs, meta, number, obj, random, thread, type, validate
341
+ - **Database**: connection, operations, entity class, GridFS
342
+ - **Entity CRUD**: create, read, update, delete, clone, ref-filter
343
+
344
+ ## 📂 Project Structure
183
345
 
184
346
  ```
185
347
  hola-server/
186
- ├── core/ # Core utilities (array, date, validate, etc.)
187
- ├── db/ # Database layer (connection, entity, gridfs)
188
- ├── http/ # HTTP layer (express, router, middleware)
189
- ├── router/ # CRUD route handlers
190
- ├── test/ # Test suite
191
- └── tool/ # Tools (i18n generation)
348
+ ├── src/
349
+ ├── index.ts # Public API entry point
350
+ ├── setting.ts # Application settings
351
+ ├── config/ # Environment-based config loader
352
+ ├── core/ # Core utilities (16 modules)
353
+ │ │ ├── meta.ts # Entity metadata & lifecycle hooks
354
+ │ │ ├── type.ts # Type system & validation
355
+ │ │ ├── role.ts # Role-based access control
356
+ │ │ ├── encrypt.ts # Password hashing & AES encryption
357
+ │ │ └── ... # array, bash, chart, date, file, etc.
358
+ │ ├── db/ # Database layer
359
+ │ │ ├── db.ts # MongoDB connection & helpers
360
+ │ │ ├── entity.ts # Entity CRUD operations
361
+ │ │ └── gridfs.ts # GridFS file storage
362
+ │ ├── meta/ # Meta programming
363
+ │ │ ├── router.ts # Auto RESTful route generation
364
+ │ │ └── schema.ts # TypeBox schema generation
365
+ │ ├── plugins/ # Elysia plugins
366
+ │ │ ├── auth.ts # JWT authentication
367
+ │ │ ├── cors.ts # CORS configuration
368
+ │ │ ├── body.ts # Body parser
369
+ │ │ └── error.ts # Error handling
370
+ │ ├── errors/ # Error classes (auth, http, validation)
371
+ │ ├── http/ # HTTP status codes
372
+ │ └── tool/ # Tools
373
+ │ ├── gen_i18n.ts # i18n key generation
374
+ │ └── vector_store.ts # Vector similarity search
375
+ ├── test/ # Test suite (25 test files)
376
+ ├── package.json
377
+ └── tsconfig.json
192
378
  ```
193
379
 
194
- ## License
380
+ ## 📄 License
195
381
 
196
382
  ISC
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Configuration loader for environment-based settings.
3
+ * @module config
4
+ */
5
+ export interface AppConfig {
6
+ /** Server port */
7
+ port: number;
8
+ /** JWT configuration */
9
+ jwt: {
10
+ secret: string;
11
+ accessExpiry: string;
12
+ refreshExpiry: string;
13
+ };
14
+ /** CORS configuration */
15
+ cors: {
16
+ origin: string[] | true;
17
+ credentials?: boolean;
18
+ };
19
+ /** Body parser configuration */
20
+ body: {
21
+ limit: string | number;
22
+ };
23
+ /** Database configuration */
24
+ db: {
25
+ url: string;
26
+ };
27
+ }
28
+ /**
29
+ * Load configuration from environment-specific file.
30
+ * Looks for config files in the specified directory based on NODE_ENV.
31
+ *
32
+ * @param config_dir Directory containing config files (dev.ts, test.ts, prod.ts)
33
+ * @returns Promise resolving to AppConfig
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const config = await load_config(__dirname + '/config');
38
+ * ```
39
+ */
40
+ export declare const load_config: (config_dir: string) => Promise<AppConfig>;
41
+ /**
42
+ * Create a base config with common defaults.
43
+ * Use this as a starting point for environment configs.
44
+ */
45
+ export declare const create_base_config: (overrides?: Partial<AppConfig>) => AppConfig;
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACtB,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,GAAG,EAAE;QACD,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,yBAAyB;IACzB,IAAI,EAAE;QACF,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACxB,WAAW,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,gCAAgC;IAChC,IAAI,EAAE;QACF,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;KAC1B,CAAC;IACF,6BAA6B;IAC7B,EAAE,EAAE;QACA,GAAG,EAAE,MAAM,CAAC;KACf,CAAC;CACL;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,SAAS,CAUvE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,YAAW,OAAO,CAAC,SAAS,CAAM,KAAG,SAsBtE,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Configuration loader for environment-based settings.
3
+ * @module config
4
+ */
5
+ /**
6
+ * Load configuration from environment-specific file.
7
+ * Looks for config files in the specified directory based on NODE_ENV.
8
+ *
9
+ * @param config_dir Directory containing config files (dev.ts, test.ts, prod.ts)
10
+ * @returns Promise resolving to AppConfig
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const config = await load_config(__dirname + '/config');
15
+ * ```
16
+ */
17
+ export const load_config = async (config_dir) => {
18
+ const env = process.env.NODE_ENV || 'dev';
19
+ const config_path = `${config_dir}/${env}`;
20
+ try {
21
+ const module = await import(config_path);
22
+ return module.default;
23
+ }
24
+ catch (error) {
25
+ throw new Error(`Failed to load config for environment '${env}' from ${config_path}: ${error}`);
26
+ }
27
+ };
28
+ /**
29
+ * Create a base config with common defaults.
30
+ * Use this as a starting point for environment configs.
31
+ */
32
+ export const create_base_config = (overrides = {}) => ({
33
+ port: 3000,
34
+ jwt: {
35
+ secret: 'change-this-in-production',
36
+ accessExpiry: '15m',
37
+ refreshExpiry: '7d',
38
+ ...overrides.jwt
39
+ },
40
+ cors: {
41
+ origin: ['http://localhost:5173'],
42
+ credentials: true,
43
+ ...overrides.cors
44
+ },
45
+ body: {
46
+ limit: '10mb',
47
+ ...overrides.body
48
+ },
49
+ db: {
50
+ url: 'mongodb://localhost:27017/hola',
51
+ ...overrides.db
52
+ },
53
+ ...overrides
54
+ });
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0BH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,UAAkB,EAAsB,EAAE;IACxE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC1C,MAAM,WAAW,GAAG,GAAG,UAAU,IAAI,GAAG,EAAE,CAAC;IAE3C,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QACzC,OAAO,MAAM,CAAC,OAAoB,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0CAA0C,GAAG,UAAU,WAAW,KAAK,KAAK,EAAE,CAAC,CAAC;IACpG,CAAC;AACL,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,YAAgC,EAAE,EAAa,EAAE,CAAC,CAAC;IAClF,IAAI,EAAE,IAAI;IACV,GAAG,EAAE;QACD,MAAM,EAAE,2BAA2B;QACnC,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,IAAI;QACnB,GAAG,SAAS,CAAC,GAAG;KACnB;IACD,IAAI,EAAE;QACF,MAAM,EAAE,CAAC,uBAAuB,CAAC;QACjC,WAAW,EAAE,IAAI;QACjB,GAAG,SAAS,CAAC,IAAI;KACpB;IACD,IAAI,EAAE;QACF,KAAK,EAAE,MAAM;QACb,GAAG,SAAS,CAAC,IAAI;KACpB;IACD,EAAE,EAAE;QACA,GAAG,EAAE,gCAAgC;QACrC,GAAG,SAAS,CAAC,EAAE;KAClB;IACD,GAAG,SAAS;CACf,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Array manipulation utility functions.
3
+ * @module core/array
4
+ */
5
+ /** Shuffle array elements randomly in place. */
6
+ export declare const shuffle: <T>(arr: T[]) => void;
7
+ /** Remove elements from array by matching field value. */
8
+ export declare const remove_element: <T, K extends keyof T>(array: T[], field: K, value: T[K]) => void;
9
+ export declare const pop_n: <T>(array: T[], n: number) => T[] | undefined;
10
+ export declare const shift_n: <T>(array: T[], n: number) => T[] | undefined;
11
+ /** Calculate sum of number array. */
12
+ export declare const sum: (arr: number[]) => number;
13
+ /** Calculate average of number array. */
14
+ export declare const avg: (arr: number[]) => number;
15
+ /** Convert array of objects to key-value object. */
16
+ export declare const map_array_to_obj: <T, K extends keyof T, V extends keyof T>(arr: T[], key_attr: K, value_attr: V) => Record<string, T[V]>;
17
+ /** Sort array of objects by attribute. */
18
+ export declare const sort_by_attr: <T extends Record<string, number>>(arr: T[], attr: keyof T, desc?: boolean) => T[];
19
+ export declare const sort_desc: <T extends Record<string, number>>(arr: T[], attr: keyof T) => T[];
20
+ export declare const sort_asc: <T extends Record<string, number>>(arr: T[], attr: keyof T) => T[];
21
+ /** Sort array by predefined key sequence. */
22
+ export declare const sort_by_key_seq: <T, K extends keyof T>(arr: T[], attr: K, keys: T[K][]) => T[];
23
+ /** Create cartesian product of two object arrays. */
24
+ export declare const combine: <T extends object, U extends object>(arr1: T[], arr2: U[]) => (T & U)[];
25
+ /** Remove duplicate elements from array. */
26
+ export declare const unique: <T>(array: T[]) => T[];
27
+ //# sourceMappingURL=array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/core/array.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,gDAAgD;AAChD,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,KAAK,CAAC,EAAE,KAAG,IAKrC,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAG,IAIxF,CAAC;AAaF,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,MAAM,KAAG,CAAC,EAAE,GAAG,SAAuC,CAAC;AAC/F,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,MAAM,KAAG,CAAC,EAAE,GAAG,SAAyC,CAAC;AAEnG,qCAAqC;AACrC,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,KAAG,MAAiE,CAAC;AAEtG,yCAAyC;AACzC,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,KAAG,MAAuE,CAAC;AAE5G,oDAAoD;AACpD,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAEnI,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,MAAM,CAAC,EAAE,OAAM,OAAe,KAAG,CAAC,EAGhH,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,MAAM,CAAC,KAAG,CAAC,EAAmC,CAAC;AAC3H,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,MAAM,CAAC,KAAG,CAAC,EAAoC,CAAC;AAE3H,6CAA6C;AAC7C,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAG,CAAC,EAGxF,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,KAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAEzF,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,KAAG,CAAC,EAMvC,CAAC"}