outlet-orm 6.5.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -2
- package/bin/init.js +122 -0
- package/bin/mcp.js +78 -0
- package/bin/migrate.js +25 -0
- package/docs/skills/outlet-orm/ADVANCED.md +575 -0
- package/docs/skills/outlet-orm/AI.md +220 -0
- package/docs/skills/outlet-orm/API.md +522 -0
- package/docs/skills/outlet-orm/BACKUP.md +150 -0
- package/docs/skills/outlet-orm/MIGRATIONS.md +605 -0
- package/docs/skills/outlet-orm/MODELS.md +427 -0
- package/docs/skills/outlet-orm/QUERIES.md +345 -0
- package/docs/skills/outlet-orm/RELATIONS.md +555 -0
- package/docs/skills/outlet-orm/SECURITY.md +386 -0
- package/docs/skills/outlet-orm/SEEDS.md +98 -0
- package/docs/skills/outlet-orm/SKILL.md +205 -0
- package/docs/skills/outlet-orm/TYPESCRIPT.md +480 -0
- package/package.json +7 -3
- package/src/AI/AIPromptEnhancer.js +170 -0
- package/src/AI/AIQueryBuilder.js +234 -0
- package/src/AI/AIQueryOptimizer.js +185 -0
- package/src/AI/AISafetyGuardrails.js +146 -0
- package/src/AI/AISeeder.js +181 -0
- package/src/AI/AiBridgeManager.js +287 -0
- package/src/AI/Builders/TextBuilder.js +170 -0
- package/src/AI/Contracts/AudioProviderContract.js +29 -0
- package/src/AI/Contracts/ChatProviderContract.js +38 -0
- package/src/AI/Contracts/EmbeddingsProviderContract.js +19 -0
- package/src/AI/Contracts/ImageProviderContract.js +19 -0
- package/src/AI/Contracts/ModelsProviderContract.js +26 -0
- package/src/AI/Contracts/ToolContract.js +25 -0
- package/src/AI/Facades/AiBridge.js +79 -0
- package/src/AI/MCPServer.js +798 -0
- package/src/AI/PromptGenerator.js +318 -0
- package/src/AI/Providers/ClaudeProvider.js +64 -0
- package/src/AI/Providers/CustomOpenAIProvider.js +238 -0
- package/src/AI/Providers/GeminiProvider.js +68 -0
- package/src/AI/Providers/GrokProvider.js +46 -0
- package/src/AI/Providers/MistralProvider.js +21 -0
- package/src/AI/Providers/OllamaProvider.js +249 -0
- package/src/AI/Providers/OllamaTurboProvider.js +32 -0
- package/src/AI/Providers/OnnProvider.js +46 -0
- package/src/AI/Providers/OpenAIProvider.js +471 -0
- package/src/AI/Support/AudioNormalizer.js +37 -0
- package/src/AI/Support/ChatNormalizer.js +42 -0
- package/src/AI/Support/Document.js +77 -0
- package/src/AI/Support/DocumentAttachmentMapper.js +101 -0
- package/src/AI/Support/EmbeddingsNormalizer.js +30 -0
- package/src/AI/Support/Exceptions/ProviderError.js +22 -0
- package/src/AI/Support/FileSecurity.js +56 -0
- package/src/AI/Support/ImageNormalizer.js +62 -0
- package/src/AI/Support/JsonSchemaValidator.js +73 -0
- package/src/AI/Support/Message.js +40 -0
- package/src/AI/Support/StreamChunk.js +45 -0
- package/src/AI/Support/ToolChatRunner.js +160 -0
- package/src/AI/Support/ToolRegistry.js +62 -0
- package/src/AI/Tools/SystemInfoTool.js +25 -0
- package/src/index.js +77 -1
- package/types/index.d.ts +432 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# Outlet ORM - Models & CRUD
|
|
2
|
+
|
|
3
|
+
[← Back to Index](SKILL.md) | [Next: Queries →](QUERIES.md)
|
|
4
|
+
|
|
5
|
+
> 📘 **TypeScript** : Use`Model<TAttributes>`for typed attributes. See [TYPESCRIPT.md](TYPESCRIPT.md#generic-model-v400)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Recommended Project Structure (Layered Architecture)
|
|
10
|
+
|
|
11
|
+
> 🔐 **Security**: Use`hidden`for sensitive fields,`fillable`for mass assignment protection.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
my-project/
|
|
15
|
+
├── .env # ⚠️ NEVER commit
|
|
16
|
+
├── src/
|
|
17
|
+
│ ├── controllers/ # 🎮 HTTP handling only
|
|
18
|
+
│ ├── services/ # ⚙️ Business logic
|
|
19
|
+
│ ├── repositories/ # 📦 Data access layer
|
|
20
|
+
│ ├── models/ # 📊 Your Model classes
|
|
21
|
+
│ │ ├── User.js # hidden: ['password']
|
|
22
|
+
│ │ └── Post.js
|
|
23
|
+
│ ├── middlewares/ # 🔒 Auth, validation
|
|
24
|
+
│ ├── config/ # 🔒 Configuration
|
|
25
|
+
│ └── utils/ # 🔒 Hash, tokens
|
|
26
|
+
├── database/
|
|
27
|
+
│ └── migrations/
|
|
28
|
+
├── public/ # ✅ Only public folder
|
|
29
|
+
└── tests/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Model Definition
|
|
35
|
+
|
|
36
|
+
### Complete Model Example
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const { Model } = require('outlet-orm');
|
|
40
|
+
|
|
41
|
+
// Define related models first
|
|
42
|
+
class Post extends Model {
|
|
43
|
+
static table = 'posts';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class Profile extends Model {
|
|
47
|
+
static table = 'profiles';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class User extends Model {
|
|
51
|
+
// Required: Table name
|
|
52
|
+
static table = 'users';
|
|
53
|
+
|
|
54
|
+
// Optional: Primary key (default: 'id')
|
|
55
|
+
static primaryKey = 'id';
|
|
56
|
+
|
|
57
|
+
// Auto-manage created_at/updated_at
|
|
58
|
+
static timestamps = true;
|
|
59
|
+
|
|
60
|
+
// Enable soft deletes (deleted_at)
|
|
61
|
+
static softDeletes = true;
|
|
62
|
+
|
|
63
|
+
// Mass assignable fields
|
|
64
|
+
static fillable = ['name', 'email', 'password', 'role'];
|
|
65
|
+
|
|
66
|
+
// Hidden from JSON output
|
|
67
|
+
static hidden = ['password', 'remember_token'];
|
|
68
|
+
|
|
69
|
+
// Auto type casting
|
|
70
|
+
static casts = {
|
|
71
|
+
id: 'int',
|
|
72
|
+
email_verified: 'boolean',
|
|
73
|
+
preferences: 'json',
|
|
74
|
+
birthday: 'date',
|
|
75
|
+
balance: 'float'
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Validation rules
|
|
79
|
+
static rules = {
|
|
80
|
+
name: 'required|string|min:2|max:100',
|
|
81
|
+
email: 'required|email',
|
|
82
|
+
password: 'required|min:8',
|
|
83
|
+
role: 'in:admin,user,guest'
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Relationships
|
|
87
|
+
posts() {
|
|
88
|
+
return this.hasMany(Post, 'user_id');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
profile() {
|
|
92
|
+
return this.hasOne(Profile, 'user_id');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Static Properties
|
|
100
|
+
|
|
101
|
+
| Property | Type | Default | Description |
|
|
102
|
+
|----------|------|---------|-------------|
|
|
103
|
+
|`table`| string | **required** | Table name |
|
|
104
|
+
|`primaryKey`| string |`'id'`| Primary key column |
|
|
105
|
+
|`timestamps`| boolean |`true`| Auto-manage created_at/updated_at |
|
|
106
|
+
|`softDeletes`| boolean |`false`| Enable soft delete |
|
|
107
|
+
|`DELETED_AT`| string |`'deleted_at'`| Soft delete column name |
|
|
108
|
+
|`fillable`| array |`[]`| Mass assignable fields |
|
|
109
|
+
|`hidden`| array |`[]`| Hidden from JSON |
|
|
110
|
+
|`casts`| object |`{}`| Type casting definitions |
|
|
111
|
+
|`rules`| object |`{}`| Validation rules |
|
|
112
|
+
|`connection`| object |`null`| Custom DB connection |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Type Casting
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
class User extends Model {
|
|
120
|
+
static casts = {
|
|
121
|
+
id: 'int', // or 'integer'
|
|
122
|
+
age: 'integer',
|
|
123
|
+
balance: 'float', // or 'double'
|
|
124
|
+
is_active: 'boolean', // or 'bool'
|
|
125
|
+
metadata: 'json', // Parse as JSON object
|
|
126
|
+
settings: 'array', // Parse as JSON array
|
|
127
|
+
birthday: 'date' // Convert to Date
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const user = await User.find(1);
|
|
132
|
+
console.log(typeof user.getAttribute('age')); // 'number'
|
|
133
|
+
console.log(typeof user.getAttribute('is_active')); // 'boolean'
|
|
134
|
+
console.log(user.getAttribute('metadata')); // Object
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Cast Types
|
|
138
|
+
|
|
139
|
+
| Type | Description |
|
|
140
|
+
|------|-------------|
|
|
141
|
+
|`int`/`integer`| Integer number |
|
|
142
|
+
|`float`/`double`| Floating point number |
|
|
143
|
+
|`boolean`/`bool`| Boolean value |
|
|
144
|
+
|`json`| Parse JSON to object |
|
|
145
|
+
|`array`| Parse JSON to array |
|
|
146
|
+
|`date`| Convert to Date object |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## CRUD Operations
|
|
151
|
+
|
|
152
|
+
### Create
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// Method 1: create() - Create and save
|
|
156
|
+
const user = await User.create({
|
|
157
|
+
name: 'John Doe',
|
|
158
|
+
email: 'john@example.com',
|
|
159
|
+
password: 'secret123'
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Method 2: new + save()
|
|
163
|
+
const user = new User({
|
|
164
|
+
name: 'Jane Doe',
|
|
165
|
+
email: 'jane@example.com'
|
|
166
|
+
});
|
|
167
|
+
user.setAttribute('password', 'secret456');
|
|
168
|
+
await user.save();
|
|
169
|
+
|
|
170
|
+
// Method 3: Raw insert (no model instance returned)
|
|
171
|
+
await User.insert({ name: 'Bob', email: 'bob@example.com' });
|
|
172
|
+
|
|
173
|
+
// Insert multiple
|
|
174
|
+
await User.insert([
|
|
175
|
+
{ name: 'User 1', email: 'user1@example.com' },
|
|
176
|
+
{ name: 'User 2', email: 'user2@example.com' }
|
|
177
|
+
]);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Read
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
// All records
|
|
184
|
+
const users = await User.all();
|
|
185
|
+
|
|
186
|
+
// Find by ID
|
|
187
|
+
const user = await User.find(1);
|
|
188
|
+
const user = await User.findOrFail(1); // Throws if not found
|
|
189
|
+
|
|
190
|
+
// First result
|
|
191
|
+
const firstUser = await User.first();
|
|
192
|
+
|
|
193
|
+
// With conditions
|
|
194
|
+
const activeUsers = await User
|
|
195
|
+
.where('status', 'active')
|
|
196
|
+
.where('age', '>', 18)
|
|
197
|
+
.get();
|
|
198
|
+
|
|
199
|
+
// With relationships (Eager Loading)
|
|
200
|
+
const usersWithPosts = await User
|
|
201
|
+
.with('posts', 'profile')
|
|
202
|
+
.get();
|
|
203
|
+
|
|
204
|
+
// Order and limit
|
|
205
|
+
const recentUsers = await User
|
|
206
|
+
.orderBy('created_at', 'desc')
|
|
207
|
+
.limit(10)
|
|
208
|
+
.get();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Update
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
// Instance update
|
|
215
|
+
const user = await User.find(1);
|
|
216
|
+
user.setAttribute('name', 'Updated Name');
|
|
217
|
+
await user.save();
|
|
218
|
+
|
|
219
|
+
// Bulk update
|
|
220
|
+
await User
|
|
221
|
+
.where('status', 'pending')
|
|
222
|
+
.update({ status: 'active' });
|
|
223
|
+
|
|
224
|
+
// Update and fetch (like Prisma)
|
|
225
|
+
const updated = await User
|
|
226
|
+
.where('id', 1)
|
|
227
|
+
.updateAndFetch({ name: 'Neo' }, ['profile', 'posts']);
|
|
228
|
+
|
|
229
|
+
// Helpers by ID
|
|
230
|
+
const user = await User.updateAndFetchById(1, { name: 'Trinity' }, ['profile']);
|
|
231
|
+
await User.updateById(2, { status: 'active' });
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Delete
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
// Instance delete
|
|
238
|
+
const user = await User.find(1);
|
|
239
|
+
await user.destroy(); // Soft delete if enabled
|
|
240
|
+
|
|
241
|
+
// Bulk delete
|
|
242
|
+
await User
|
|
243
|
+
.where('status', 'banned')
|
|
244
|
+
.delete();
|
|
245
|
+
|
|
246
|
+
// Force delete (permanent, even with soft deletes)
|
|
247
|
+
await user.forceDelete();
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Attribute Methods
|
|
253
|
+
|
|
254
|
+
### Getting Attributes
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
const user = await User.find(1);
|
|
258
|
+
|
|
259
|
+
// Get single attribute
|
|
260
|
+
const name = user.getAttribute('name');
|
|
261
|
+
|
|
262
|
+
// Get all attributes as object
|
|
263
|
+
const attrs = user.toJSON();
|
|
264
|
+
|
|
265
|
+
// Check if modified
|
|
266
|
+
const isDirty = user.isDirty();
|
|
267
|
+
const dirty = user.getDirty(); // Get modified attributes
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Setting Attributes
|
|
271
|
+
|
|
272
|
+
```javascript
|
|
273
|
+
const user = new User();
|
|
274
|
+
|
|
275
|
+
// Set single attribute
|
|
276
|
+
user.setAttribute('name', 'John');
|
|
277
|
+
|
|
278
|
+
// Fill multiple attributes
|
|
279
|
+
user.fill({
|
|
280
|
+
name: 'John',
|
|
281
|
+
email: 'john@example.com'
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Hidden Attributes
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
class User extends Model {
|
|
291
|
+
static hidden = ['password', 'secret_token'];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Normal query - hidden fields excluded
|
|
295
|
+
const user = await User.find(1);
|
|
296
|
+
console.log(user.toJSON()); // password excluded
|
|
297
|
+
|
|
298
|
+
// Include hidden fields
|
|
299
|
+
const user = await User.withHidden().where('email', email).first();
|
|
300
|
+
console.log(user.toJSON()); // password included
|
|
301
|
+
|
|
302
|
+
// Control with boolean
|
|
303
|
+
const user = await User.withoutHidden(true).first(); // true = show
|
|
304
|
+
const user = await User.withoutHidden(false).first(); // false = hide
|
|
305
|
+
|
|
306
|
+
// Use case: Authentication
|
|
307
|
+
const user = await User.withHidden().where('email', email).first();
|
|
308
|
+
if (user && await bcrypt.compare(password, user.getAttribute('password'))) {
|
|
309
|
+
// Authentication successful
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Timestamps
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
// Enabled by default
|
|
319
|
+
class User extends Model {
|
|
320
|
+
static timestamps = true; // created_at, updated_at
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Disable timestamps
|
|
324
|
+
class Log extends Model {
|
|
325
|
+
static timestamps = false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Auto-managed on create/update
|
|
329
|
+
const user = await User.create({ name: 'John' });
|
|
330
|
+
console.log(user.getAttribute('created_at')); // Current date
|
|
331
|
+
|
|
332
|
+
user.setAttribute('name', 'Jane');
|
|
333
|
+
await user.save();
|
|
334
|
+
console.log(user.getAttribute('updated_at')); // Updated automatically
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Mass Assignment Protection
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
class User extends Model {
|
|
343
|
+
static fillable = ['name', 'email', 'age'];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// OK - all fields are in fillable
|
|
347
|
+
const user = await User.create({
|
|
348
|
+
name: 'John',
|
|
349
|
+
email: 'john@example.com',
|
|
350
|
+
age: 30
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// 'role' will be IGNORED (not in fillable)
|
|
354
|
+
const user2 = await User.create({
|
|
355
|
+
name: 'Jane',
|
|
356
|
+
role: 'admin' // Ignored!
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Multiple Connections
|
|
363
|
+
|
|
364
|
+
```javascript
|
|
365
|
+
const { DatabaseConnection, Model } = require('outlet-orm');
|
|
366
|
+
|
|
367
|
+
// Create connections
|
|
368
|
+
const mysqlDb = new DatabaseConnection({
|
|
369
|
+
driver: 'mysql',
|
|
370
|
+
host: 'localhost',
|
|
371
|
+
database: 'app_db'
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const postgresDb = new DatabaseConnection({
|
|
375
|
+
driver: 'postgres',
|
|
376
|
+
host: 'localhost',
|
|
377
|
+
database: 'analytics_db'
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Assign to models
|
|
381
|
+
class User extends Model {
|
|
382
|
+
static table = 'users';
|
|
383
|
+
static connection = mysqlDb;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
class Analytics extends Model {
|
|
387
|
+
static table = 'events';
|
|
388
|
+
static connection = postgresDb;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Close when done
|
|
392
|
+
await mysqlDb.close();
|
|
393
|
+
await postgresDb.close();
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Environment Variables
|
|
399
|
+
|
|
400
|
+
Configure via`.env`file (auto-loaded):
|
|
401
|
+
|
|
402
|
+
```env
|
|
403
|
+
DB_DRIVER=mysql
|
|
404
|
+
DB_HOST=localhost
|
|
405
|
+
DB_PORT=3306
|
|
406
|
+
DB_DATABASE=myapp
|
|
407
|
+
DB_USER=root
|
|
408
|
+
DB_PASSWORD=secret
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
| Variable | Description | Default |
|
|
412
|
+
|----------|-------------|---------|
|
|
413
|
+
|`DB_DRIVER`|`mysql`,`postgres`,`sqlite`|`mysql`|
|
|
414
|
+
|`DB_HOST`| Database host |`localhost`|
|
|
415
|
+
|`DB_PORT`| Connection port | Driver default |
|
|
416
|
+
|`DB_USER`/`DB_USERNAME`| Username | - |
|
|
417
|
+
|`DB_PASSWORD`| Password | - |
|
|
418
|
+
|`DB_DATABASE`/`DB_NAME`| Database name | - |
|
|
419
|
+
|`DB_FILE`/`SQLITE_DB`| SQLite file path |`:memory:`|
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Next Steps
|
|
424
|
+
|
|
425
|
+
- [Query Builder →](QUERIES.md)
|
|
426
|
+
- [Relationships →](RELATIONS.md)
|
|
427
|
+
- [Advanced Features →](ADVANCED.md)
|