outlet-orm 9.0.2 → 11.1.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 (44) hide show
  1. package/README.md +103 -5
  2. package/bin/reverse.js +0 -1
  3. package/package.json +1 -1
  4. package/skills/outlet-orm/ADVANCED.md +29 -0
  5. package/skills/outlet-orm/AI.md +23 -23
  6. package/skills/outlet-orm/API.md +33 -3
  7. package/skills/outlet-orm/MODELS.md +144 -2
  8. package/skills/outlet-orm/QUERIES.md +136 -3
  9. package/skills/outlet-orm/RELATIONS.md +44 -0
  10. package/skills/outlet-orm/SEEDS.md +2 -2
  11. package/skills/outlet-orm/SKILL.md +8 -6
  12. package/skills/outlet-orm/TYPESCRIPT.md +98 -0
  13. package/src/AI/{AiBridgeManager.js → AIManager.js} +61 -61
  14. package/src/AI/AIPromptEnhancer.js +1 -1
  15. package/src/AI/AIQueryBuilder.js +4 -4
  16. package/src/AI/AIQueryOptimizer.js +3 -3
  17. package/src/AI/AISeeder.js +1 -1
  18. package/src/AI/Builders/TextBuilder.js +2 -2
  19. package/src/AI/Contracts/AudioProviderContract.js +2 -2
  20. package/src/AI/Contracts/ChatProviderContract.js +3 -2
  21. package/src/AI/Contracts/EmbeddingsProviderContract.js +1 -1
  22. package/src/AI/Contracts/ImageProviderContract.js +1 -1
  23. package/src/AI/Contracts/ModelsProviderContract.js +1 -1
  24. package/src/AI/Contracts/ToolContract.js +1 -1
  25. package/src/AI/Facades/{AiBridge.js → AI.js} +11 -11
  26. package/src/AI/MCPServer.js +16 -16
  27. package/src/AI/Providers/CustomOpenAIProvider.js +0 -2
  28. package/src/AI/Providers/GeminiProvider.js +2 -2
  29. package/src/AI/Providers/OpenAIProvider.js +0 -5
  30. package/src/AI/Support/DocumentAttachmentMapper.js +37 -37
  31. package/src/AI/Support/FileSecurity.js +1 -1
  32. package/src/AI/Support/ToolChatRunner.js +1 -1
  33. package/src/Backup/BackupManager.js +6 -6
  34. package/src/Backup/BackupScheduler.js +1 -1
  35. package/src/Backup/BackupSocketServer.js +2 -2
  36. package/src/DatabaseConnection.js +51 -0
  37. package/src/Model.js +245 -5
  38. package/src/QueryBuilder.js +191 -0
  39. package/src/Relations/HasOneRelation.js +114 -114
  40. package/src/Relations/HasOneThroughRelation.js +105 -105
  41. package/src/Relations/MorphOneRelation.js +4 -2
  42. package/src/Relations/Relation.js +35 -0
  43. package/src/index.js +6 -6
  44. package/types/index.d.ts +78 -12
@@ -1,114 +1,114 @@
1
- const Relation = require('./Relation');
2
-
3
- /**
4
- * Has One Relation
5
- * Represents a one-to-one relationship
6
- */
7
- class HasOneRelation extends Relation {
8
- /**
9
- * Get the related model
10
- * @returns {Promise<Model|null>}
11
- */
12
- async get() {
13
- const result = await this.related
14
- .where(this.foreignKey, this.parent.getAttribute(this.localKey))
15
- .first();
16
-
17
- return result;
18
- }
19
-
20
- /**
21
- * Eager load the relationship for a collection of parent models
22
- * @param {Array<Model>} models
23
- * @param {string} relationName
24
- * @returns {Promise<void>}
25
- */
26
- async eagerLoad(models, relationName, constraint) {
27
- const keys = models
28
- .map(model => model.getAttribute(this.localKey))
29
- .filter(key => key !== null && key !== undefined);
30
-
31
- if (keys.length === 0) return;
32
-
33
- const qb = this.related.whereIn(this.foreignKey, keys);
34
- if (typeof constraint === 'function') constraint(qb);
35
- const relatedModels = await qb.get();
36
-
37
- const relatedMap = {};
38
- relatedModels.forEach(model => {
39
- const foreignKeyValue = model.getAttribute(this.foreignKey);
40
- relatedMap[foreignKeyValue] = model;
41
- });
42
-
43
- models.forEach(model => {
44
- const localKeyValue = model.getAttribute(this.localKey);
45
- model.relations[relationName] = relatedMap[localKeyValue] || null;
46
- });
47
- }
48
-
49
- /**
50
- * Add a where clause to the relation query
51
- * @param {string} column
52
- * @param {string|any} operator
53
- * @param {any} value
54
- * @returns {QueryBuilder}
55
- */
56
- where(column, operator, value) {
57
- return this.related
58
- .where(this.foreignKey, this.parent.getAttribute(this.localKey))
59
- .where(column, operator, value);
60
- }
61
-
62
- /**
63
- * Create a new related model and associate it
64
- * @param {Object} attributes
65
- * @returns {Promise<Model>}
66
- */
67
- async create(attributes = {}) {
68
- const model = new this.related.model(attributes);
69
- model.setAttribute(this.foreignKey, this.parent.getAttribute(this.localKey));
70
- await model.save();
71
- return model;
72
- }
73
-
74
- /**
75
- * Save an existing model and associate it
76
- * @param {Model} model
77
- * @returns {Promise<Model>}
78
- */
79
- async save(model) {
80
- model.setAttribute(this.foreignKey, this.parent.getAttribute(this.localKey));
81
- await model.save();
82
- return model;
83
- }
84
-
85
- /**
86
- * Create multiple related models and associate them
87
- * @param {Array<Object>} attributesArray
88
- * @returns {Promise<Array<Model>>}
89
- */
90
- async createMany(attributesArray) {
91
- const models = [];
92
- for (const attributes of attributesArray) {
93
- const model = await this.create(attributes);
94
- models.push(model);
95
- }
96
- return models;
97
- }
98
-
99
- /**
100
- * Save multiple existing models and associate them
101
- * @param {Array<Model>} models
102
- * @returns {Promise<Array<Model>>}
103
- */
104
- async saveMany(models) {
105
- const savedModels = [];
106
- for (const model of models) {
107
- const saved = await this.save(model);
108
- savedModels.push(saved);
109
- }
110
- return savedModels;
111
- }
112
- }
113
-
114
- module.exports = HasOneRelation;
1
+ const Relation = require('./Relation');
2
+
3
+ /**
4
+ * Has One Relation
5
+ * Represents a one-to-one relationship
6
+ */
7
+ class HasOneRelation extends Relation {
8
+ /**
9
+ * Get the related model
10
+ * @returns {Promise<Model|null>}
11
+ */
12
+ async get() {
13
+ const result = await this.related
14
+ .where(this.foreignKey, this.parent.getAttribute(this.localKey))
15
+ .first();
16
+
17
+ return result || this._buildDefault();
18
+ }
19
+
20
+ /**
21
+ * Eager load the relationship for a collection of parent models
22
+ * @param {Array<Model>} models
23
+ * @param {string} relationName
24
+ * @returns {Promise<void>}
25
+ */
26
+ async eagerLoad(models, relationName, constraint) {
27
+ const keys = models
28
+ .map(model => model.getAttribute(this.localKey))
29
+ .filter(key => key !== null && key !== undefined);
30
+
31
+ if (keys.length === 0) return;
32
+
33
+ const qb = this.related.whereIn(this.foreignKey, keys);
34
+ if (typeof constraint === 'function') constraint(qb);
35
+ const relatedModels = await qb.get();
36
+
37
+ const relatedMap = {};
38
+ relatedModels.forEach(model => {
39
+ const foreignKeyValue = model.getAttribute(this.foreignKey);
40
+ relatedMap[foreignKeyValue] = model;
41
+ });
42
+
43
+ models.forEach(model => {
44
+ const localKeyValue = model.getAttribute(this.localKey);
45
+ model.relations[relationName] = relatedMap[localKeyValue] || this._buildDefault();
46
+ });
47
+ }
48
+
49
+ /**
50
+ * Add a where clause to the relation query
51
+ * @param {string} column
52
+ * @param {string|any} operator
53
+ * @param {any} value
54
+ * @returns {QueryBuilder}
55
+ */
56
+ where(column, operator, value) {
57
+ return this.related
58
+ .where(this.foreignKey, this.parent.getAttribute(this.localKey))
59
+ .where(column, operator, value);
60
+ }
61
+
62
+ /**
63
+ * Create a new related model and associate it
64
+ * @param {Object} attributes
65
+ * @returns {Promise<Model>}
66
+ */
67
+ async create(attributes = {}) {
68
+ const model = new this.related.model(attributes);
69
+ model.setAttribute(this.foreignKey, this.parent.getAttribute(this.localKey));
70
+ await model.save();
71
+ return model;
72
+ }
73
+
74
+ /**
75
+ * Save an existing model and associate it
76
+ * @param {Model} model
77
+ * @returns {Promise<Model>}
78
+ */
79
+ async save(model) {
80
+ model.setAttribute(this.foreignKey, this.parent.getAttribute(this.localKey));
81
+ await model.save();
82
+ return model;
83
+ }
84
+
85
+ /**
86
+ * Create multiple related models and associate them
87
+ * @param {Array<Object>} attributesArray
88
+ * @returns {Promise<Array<Model>>}
89
+ */
90
+ async createMany(attributesArray) {
91
+ const models = [];
92
+ for (const attributes of attributesArray) {
93
+ const model = await this.create(attributes);
94
+ models.push(model);
95
+ }
96
+ return models;
97
+ }
98
+
99
+ /**
100
+ * Save multiple existing models and associate them
101
+ * @param {Array<Model>} models
102
+ * @returns {Promise<Array<Model>>}
103
+ */
104
+ async saveMany(models) {
105
+ const savedModels = [];
106
+ for (const model of models) {
107
+ const saved = await this.save(model);
108
+ savedModels.push(saved);
109
+ }
110
+ return savedModels;
111
+ }
112
+ }
113
+
114
+ module.exports = HasOneRelation;
@@ -1,105 +1,105 @@
1
- const Relation = require('./Relation');
2
-
3
- /**
4
- * Has One Through Relation
5
- * parent -> through -> related (final) - returns single model
6
- */
7
- class HasOneThroughRelation extends Relation {
8
- /**
9
- * @param {import('../Model')} parent
10
- * @param {typeof import('../Model')} relatedFinal
11
- * @param {typeof import('../Model')} through
12
- * @param {string} [foreignKeyOnThrough] - FK on through referencing parent
13
- * @param {string} [throughKeyOnFinal] - FK on final referencing through
14
- * @param {string} [localKey] - PK on parent
15
- * @param {string} [throughLocalKey] - PK on through
16
- */
17
- constructor(parent, relatedFinal, through, foreignKeyOnThrough, throughKeyOnFinal, localKey, throughLocalKey) {
18
- super(parent, relatedFinal, foreignKeyOnThrough, localKey);
19
- this.through = through;
20
- // Defaults based on naming conventions
21
- this.localKey = localKey || parent.constructor.primaryKey || 'id';
22
- this.throughLocalKey = throughLocalKey || through.primaryKey || 'id';
23
- this.foreignKeyOnThrough = foreignKeyOnThrough || `${parent.constructor.table.slice(0, -1)}_id`;
24
- this.throughKeyOnFinal = throughKeyOnFinal || `${through.table.slice(0, -1)}_id`;
25
- }
26
-
27
- /**
28
- * Get final related model (single)
29
- * @returns {Promise<import('../Model')|null>}
30
- */
31
- async get() {
32
- const parentKeyValue = this.parent.getAttribute(this.localKey);
33
- if (parentKeyValue === undefined || parentKeyValue === null) return null;
34
-
35
- const throughRow = await this.through
36
- .where(this.foreignKeyOnThrough, parentKeyValue)
37
- .first();
38
-
39
- if (!throughRow) return null;
40
-
41
- const throughId = throughRow.getAttribute(this.throughLocalKey);
42
- const result = await this.related
43
- .where(this.throughKeyOnFinal, throughId)
44
- .first();
45
- return result;
46
- }
47
-
48
- /**
49
- * Eager load hasOneThrough for a batch of parents
50
- * @param {Array} models
51
- * @param {string} relationName
52
- * @param {(qb: any) => void} [constraint]
53
- */
54
- async eagerLoad(models, relationName, constraint) {
55
- const parentKeys = models
56
- .map(m => m.getAttribute(this.localKey))
57
- .filter(v => v !== undefined && v !== null);
58
-
59
- if (parentKeys.length === 0) {
60
- models.forEach(m => { m.relations[relationName] = null; });
61
- return;
62
- }
63
-
64
- // Fetch through rows for all parent keys
65
- const throughRows = await this.through
66
- .whereIn(this.foreignKeyOnThrough, parentKeys)
67
- .get();
68
-
69
- if (throughRows.length === 0) {
70
- models.forEach(m => { m.relations[relationName] = null; });
71
- return;
72
- }
73
-
74
- // Map parentKey -> throughId (assuming one through per parent)
75
- const parentToThroughId = {};
76
- for (const row of throughRows) {
77
- const pKey = row.getAttribute(this.foreignKeyOnThrough);
78
- const tId = row.getAttribute(this.throughLocalKey);
79
- parentToThroughId[pKey] = tId; // overwrite if multiple, take last
80
- }
81
-
82
- const allThroughIds = Object.values(parentToThroughId);
83
-
84
- // Fetch finals in one query (with optional constraint)
85
- const qb = this.related.whereIn(this.throughKeyOnFinal, allThroughIds);
86
- if (typeof constraint === 'function') constraint(qb);
87
- const finals = await qb.get();
88
-
89
- // Map through id to final
90
- const finalsByThrough = {};
91
- for (const f of finals) {
92
- const key = f.getAttribute(this.throughKeyOnFinal);
93
- finalsByThrough[key] = f; // assume one per through
94
- }
95
-
96
- // Assign to each parent
97
- for (const m of models) {
98
- const pKey = m.getAttribute(this.localKey);
99
- const tId = parentToThroughId[pKey];
100
- m.relations[relationName] = tId ? finalsByThrough[tId] || null : null;
101
- }
102
- }
103
- }
104
-
105
- module.exports = HasOneThroughRelation;
1
+ const Relation = require('./Relation');
2
+
3
+ /**
4
+ * Has One Through Relation
5
+ * parent -> through -> related (final) - returns single model
6
+ */
7
+ class HasOneThroughRelation extends Relation {
8
+ /**
9
+ * @param {import('../Model')} parent
10
+ * @param {typeof import('../Model')} relatedFinal
11
+ * @param {typeof import('../Model')} through
12
+ * @param {string} [foreignKeyOnThrough] - FK on through referencing parent
13
+ * @param {string} [throughKeyOnFinal] - FK on final referencing through
14
+ * @param {string} [localKey] - PK on parent
15
+ * @param {string} [throughLocalKey] - PK on through
16
+ */
17
+ constructor(parent, relatedFinal, through, foreignKeyOnThrough, throughKeyOnFinal, localKey, throughLocalKey) {
18
+ super(parent, relatedFinal, foreignKeyOnThrough, localKey);
19
+ this.through = through;
20
+ // Defaults based on naming conventions
21
+ this.localKey = localKey || parent.constructor.primaryKey || 'id';
22
+ this.throughLocalKey = throughLocalKey || through.primaryKey || 'id';
23
+ this.foreignKeyOnThrough = foreignKeyOnThrough || `${parent.constructor.table.slice(0, -1)}_id`;
24
+ this.throughKeyOnFinal = throughKeyOnFinal || `${through.table.slice(0, -1)}_id`;
25
+ }
26
+
27
+ /**
28
+ * Get final related model (single)
29
+ * @returns {Promise<import('../Model')|null>}
30
+ */
31
+ async get() {
32
+ const parentKeyValue = this.parent.getAttribute(this.localKey);
33
+ if (parentKeyValue === undefined || parentKeyValue === null) return this._buildDefault();
34
+
35
+ const throughRow = await this.through
36
+ .where(this.foreignKeyOnThrough, parentKeyValue)
37
+ .first();
38
+
39
+ if (!throughRow) return this._buildDefault();
40
+
41
+ const throughId = throughRow.getAttribute(this.throughLocalKey);
42
+ const result = await this.related
43
+ .where(this.throughKeyOnFinal, throughId)
44
+ .first();
45
+ return result || this._buildDefault();
46
+ }
47
+
48
+ /**
49
+ * Eager load hasOneThrough for a batch of parents
50
+ * @param {Array} models
51
+ * @param {string} relationName
52
+ * @param {(qb: any) => void} [constraint]
53
+ */
54
+ async eagerLoad(models, relationName, constraint) {
55
+ const parentKeys = models
56
+ .map(m => m.getAttribute(this.localKey))
57
+ .filter(v => v !== undefined && v !== null);
58
+
59
+ if (parentKeys.length === 0) {
60
+ models.forEach(m => { m.relations[relationName] = null; });
61
+ return;
62
+ }
63
+
64
+ // Fetch through rows for all parent keys
65
+ const throughRows = await this.through
66
+ .whereIn(this.foreignKeyOnThrough, parentKeys)
67
+ .get();
68
+
69
+ if (throughRows.length === 0) {
70
+ models.forEach(m => { m.relations[relationName] = null; });
71
+ return;
72
+ }
73
+
74
+ // Map parentKey -> throughId (assuming one through per parent)
75
+ const parentToThroughId = {};
76
+ for (const row of throughRows) {
77
+ const pKey = row.getAttribute(this.foreignKeyOnThrough);
78
+ const tId = row.getAttribute(this.throughLocalKey);
79
+ parentToThroughId[pKey] = tId; // overwrite if multiple, take last
80
+ }
81
+
82
+ const allThroughIds = Object.values(parentToThroughId);
83
+
84
+ // Fetch finals in one query (with optional constraint)
85
+ const qb = this.related.whereIn(this.throughKeyOnFinal, allThroughIds);
86
+ if (typeof constraint === 'function') constraint(qb);
87
+ const finals = await qb.get();
88
+
89
+ // Map through id to final
90
+ const finalsByThrough = {};
91
+ for (const f of finals) {
92
+ const key = f.getAttribute(this.throughKeyOnFinal);
93
+ finalsByThrough[key] = f; // assume one per through
94
+ }
95
+
96
+ // Assign to each parent
97
+ for (const m of models) {
98
+ const pKey = m.getAttribute(this.localKey);
99
+ const tId = parentToThroughId[pKey];
100
+ m.relations[relationName] = tId ? finalsByThrough[tId] || null : null;
101
+ }
102
+ }
103
+ }
104
+
105
+ module.exports = HasOneThroughRelation;
@@ -16,10 +16,12 @@ class MorphOneRelation extends Relation {
16
16
  * @returns {Promise<Model|null>}
17
17
  */
18
18
  async get() {
19
- return this.related
19
+ const result = await this.related
20
20
  .where(this.foreignKey, this.parent.getAttribute(this.localKey))
21
21
  .where(`${this.morphType}_type`, this.parent.constructor.table)
22
22
  .first();
23
+
24
+ return result || this._buildDefault();
23
25
  }
24
26
 
25
27
  /**
@@ -46,7 +48,7 @@ class MorphOneRelation extends Relation {
46
48
 
47
49
  for (const model of models) {
48
50
  const key = model.getAttribute(this.localKey);
49
- model.relations[relationName] = relatedMap[key] || null;
51
+ model.relations[relationName] = relatedMap[key] || this._buildDefault();
50
52
  }
51
53
  }
52
54
 
@@ -7,6 +7,41 @@ class Relation {
7
7
  this.related = related;
8
8
  this.foreignKey = foreignKey;
9
9
  this.localKey = localKey;
10
+ this._defaultValue = undefined;
11
+ }
12
+
13
+ /**
14
+ * Set a default value/model to return when the relation is empty
15
+ * @param {Object|Function|boolean} [value=true] - Default attributes, factory fn, or true for empty model
16
+ * @returns {this}
17
+ */
18
+ withDefault(value = true) {
19
+ this._defaultValue = value;
20
+ return this;
21
+ }
22
+
23
+ /**
24
+ * Build the default model when the relation result is null
25
+ * @returns {Model|null}
26
+ * @protected
27
+ */
28
+ _buildDefault() {
29
+ if (this._defaultValue === undefined) return null;
30
+ if (typeof this._defaultValue === 'function') return this._defaultValue();
31
+ if (this._defaultValue === true) {
32
+ // Return an empty instance of the related model
33
+ const RelatedModel = typeof this.related === 'function' && this.related.prototype
34
+ ? this.related
35
+ : this.related.constructor;
36
+ return new RelatedModel();
37
+ }
38
+ if (typeof this._defaultValue === 'object' && this._defaultValue !== null) {
39
+ const RelatedModel = typeof this.related === 'function' && this.related.prototype
40
+ ? this.related
41
+ : this.related.constructor;
42
+ return new RelatedModel(this._defaultValue);
43
+ }
44
+ return null;
10
45
  }
11
46
 
12
47
  /**
package/src/index.js CHANGED
@@ -33,9 +33,9 @@ const MCPServer = require('./AI/MCPServer');
33
33
  const AISafetyGuardrails = require('./AI/AISafetyGuardrails');
34
34
  const PromptGenerator = require('./AI/PromptGenerator');
35
35
 
36
- // AI Bridge (v8.0.0) — Multi-provider LLM abstraction
37
- const AiBridgeManager = require('./AI/AiBridgeManager');
38
- const AiBridgeFacade = require('./AI/Facades/AiBridge');
36
+ // AI (v8.0.0) — Multi-provider LLM abstraction
37
+ const AIManager = require('./AI/AIManager');
38
+ const AIFacade = require('./AI/Facades/AI');
39
39
  const TextBuilder = require('./AI/Builders/TextBuilder');
40
40
  const ChatProviderContract = require('./AI/Contracts/ChatProviderContract');
41
41
  const EmbeddingsProviderContract = require('./AI/Contracts/EmbeddingsProviderContract');
@@ -110,9 +110,9 @@ module.exports = {
110
110
  AISafetyGuardrails,
111
111
  PromptGenerator,
112
112
 
113
- // AI Bridge (v8.0.0)
114
- AiBridgeManager,
115
- AiBridgeFacade,
113
+ // AI (v8.0.0)
114
+ AIManager,
115
+ AIFacade,
116
116
  TextBuilder,
117
117
  ChatProviderContract,
118
118
  EmbeddingsProviderContract,