@stonyx/orm 0.2.1-beta.9 → 0.2.1-beta.90

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 (171) hide show
  1. package/README.md +64 -6
  2. package/config/environment.js +37 -1
  3. package/dist/aggregates.d.ts +21 -0
  4. package/dist/aggregates.js +93 -0
  5. package/dist/attr.d.ts +2 -0
  6. package/dist/attr.js +22 -0
  7. package/dist/belongs-to.d.ts +11 -0
  8. package/dist/belongs-to.js +59 -0
  9. package/dist/cli.d.ts +22 -0
  10. package/dist/cli.js +148 -0
  11. package/dist/commands.d.ts +7 -0
  12. package/dist/commands.js +146 -0
  13. package/dist/db.d.ts +21 -0
  14. package/dist/db.js +180 -0
  15. package/dist/exports/db.d.ts +7 -0
  16. package/{src → dist}/exports/db.js +2 -4
  17. package/dist/has-many.d.ts +11 -0
  18. package/dist/has-many.js +58 -0
  19. package/dist/hooks.d.ts +62 -0
  20. package/dist/hooks.js +110 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.js +34 -0
  23. package/dist/main.d.ts +46 -0
  24. package/dist/main.js +181 -0
  25. package/dist/manage-record.d.ts +13 -0
  26. package/dist/manage-record.js +123 -0
  27. package/dist/meta-request.d.ts +6 -0
  28. package/dist/meta-request.js +52 -0
  29. package/dist/migrate.d.ts +2 -0
  30. package/dist/migrate.js +57 -0
  31. package/dist/model-property.d.ts +9 -0
  32. package/dist/model-property.js +29 -0
  33. package/dist/model.d.ts +15 -0
  34. package/dist/model.js +18 -0
  35. package/dist/mysql/connection.d.ts +14 -0
  36. package/dist/mysql/connection.js +24 -0
  37. package/dist/mysql/migration-generator.d.ts +45 -0
  38. package/dist/mysql/migration-generator.js +254 -0
  39. package/dist/mysql/migration-runner.d.ts +12 -0
  40. package/dist/mysql/migration-runner.js +88 -0
  41. package/dist/mysql/mysql-db.d.ts +100 -0
  42. package/dist/mysql/mysql-db.js +425 -0
  43. package/dist/mysql/query-builder.d.ts +10 -0
  44. package/dist/mysql/query-builder.js +44 -0
  45. package/dist/mysql/schema-introspector.d.ts +19 -0
  46. package/dist/mysql/schema-introspector.js +291 -0
  47. package/dist/mysql/type-map.d.ts +21 -0
  48. package/dist/mysql/type-map.js +36 -0
  49. package/dist/orm-request.d.ts +38 -0
  50. package/dist/orm-request.js +474 -0
  51. package/dist/plural-registry.d.ts +4 -0
  52. package/dist/plural-registry.js +9 -0
  53. package/dist/postgres/connection.d.ts +15 -0
  54. package/dist/postgres/connection.js +32 -0
  55. package/dist/postgres/migration-generator.d.ts +45 -0
  56. package/dist/postgres/migration-generator.js +261 -0
  57. package/dist/postgres/migration-runner.d.ts +10 -0
  58. package/dist/postgres/migration-runner.js +87 -0
  59. package/dist/postgres/postgres-db.d.ts +119 -0
  60. package/dist/postgres/postgres-db.js +477 -0
  61. package/dist/postgres/query-builder.d.ts +27 -0
  62. package/dist/postgres/query-builder.js +98 -0
  63. package/dist/postgres/schema-introspector.d.ts +29 -0
  64. package/dist/postgres/schema-introspector.js +314 -0
  65. package/dist/postgres/type-map.d.ts +23 -0
  66. package/dist/postgres/type-map.js +56 -0
  67. package/dist/record.d.ts +75 -0
  68. package/dist/record.js +129 -0
  69. package/dist/relationships.d.ts +10 -0
  70. package/dist/relationships.js +41 -0
  71. package/dist/serializer.d.ts +17 -0
  72. package/dist/serializer.js +136 -0
  73. package/dist/setup-rest-server.d.ts +1 -0
  74. package/dist/setup-rest-server.js +52 -0
  75. package/dist/standalone-db.d.ts +58 -0
  76. package/dist/standalone-db.js +142 -0
  77. package/dist/store.d.ts +62 -0
  78. package/dist/store.js +286 -0
  79. package/dist/timescale/query-builder.d.ts +43 -0
  80. package/dist/timescale/query-builder.js +115 -0
  81. package/dist/timescale/timescale-db.d.ts +45 -0
  82. package/dist/timescale/timescale-db.js +84 -0
  83. package/dist/transforms.d.ts +2 -0
  84. package/dist/transforms.js +17 -0
  85. package/dist/types/orm-types.d.ts +142 -0
  86. package/dist/types/orm-types.js +1 -0
  87. package/dist/utils.d.ts +7 -0
  88. package/dist/utils.js +17 -0
  89. package/dist/view-resolver.d.ts +8 -0
  90. package/dist/view-resolver.js +171 -0
  91. package/dist/view.d.ts +11 -0
  92. package/dist/view.js +18 -0
  93. package/package.json +57 -15
  94. package/src/aggregates.ts +109 -0
  95. package/src/{attr.js → attr.ts} +2 -2
  96. package/src/belongs-to.ts +90 -0
  97. package/src/cli.ts +183 -0
  98. package/src/{commands.js → commands.ts} +179 -170
  99. package/src/{db.js → db.ts} +55 -29
  100. package/src/exports/db.ts +7 -0
  101. package/src/has-many.ts +92 -0
  102. package/src/{hooks.js → hooks.ts} +41 -27
  103. package/src/{index.js → index.ts} +11 -2
  104. package/src/main.ts +229 -0
  105. package/src/manage-record.ts +161 -0
  106. package/src/{meta-request.js → meta-request.ts} +17 -14
  107. package/src/{migrate.js → migrate.ts} +9 -9
  108. package/src/model-property.ts +35 -0
  109. package/src/model.ts +21 -0
  110. package/src/mysql/{connection.js → connection.ts} +43 -28
  111. package/src/mysql/migration-generator.ts +337 -0
  112. package/src/mysql/{migration-runner.js → migration-runner.ts} +121 -110
  113. package/src/mysql/mysql-db.ts +543 -0
  114. package/src/mysql/{query-builder.js → query-builder.ts} +69 -64
  115. package/src/mysql/schema-introspector.ts +358 -0
  116. package/src/mysql/{type-map.js → type-map.ts} +42 -37
  117. package/src/{orm-request.js → orm-request.ts} +186 -108
  118. package/src/plural-registry.ts +12 -0
  119. package/src/postgres/connection.ts +48 -0
  120. package/src/postgres/migration-generator.ts +348 -0
  121. package/src/postgres/migration-runner.ts +115 -0
  122. package/src/postgres/postgres-db.ts +616 -0
  123. package/src/postgres/query-builder.ts +148 -0
  124. package/src/postgres/schema-introspector.ts +386 -0
  125. package/src/postgres/type-map.ts +61 -0
  126. package/src/record.ts +186 -0
  127. package/src/relationships.ts +54 -0
  128. package/src/serializer.ts +161 -0
  129. package/src/{setup-rest-server.js → setup-rest-server.ts} +18 -16
  130. package/src/standalone-db.ts +185 -0
  131. package/src/store.ts +373 -0
  132. package/src/timescale/query-builder.ts +174 -0
  133. package/src/timescale/timescale-db.ts +119 -0
  134. package/src/transforms.ts +20 -0
  135. package/src/types/mysql2.d.ts +49 -0
  136. package/src/types/orm-types.ts +146 -0
  137. package/src/types/pg.d.ts +32 -0
  138. package/src/types/stonyx-cron.d.ts +5 -0
  139. package/src/types/stonyx-events.d.ts +4 -0
  140. package/src/types/stonyx-rest-server.d.ts +16 -0
  141. package/src/types/stonyx-utils.d.ts +33 -0
  142. package/src/types/stonyx.d.ts +21 -0
  143. package/src/utils.ts +22 -0
  144. package/src/view-resolver.ts +211 -0
  145. package/src/view.ts +22 -0
  146. package/.claude/code-style-rules.md +0 -44
  147. package/.claude/hooks.md +0 -250
  148. package/.claude/index.md +0 -279
  149. package/.claude/usage-patterns.md +0 -217
  150. package/.github/workflows/ci.yml +0 -16
  151. package/.github/workflows/publish.yml +0 -51
  152. package/improvements.md +0 -139
  153. package/project-structure.md +0 -343
  154. package/src/belongs-to.js +0 -63
  155. package/src/has-many.js +0 -61
  156. package/src/main.js +0 -148
  157. package/src/manage-record.js +0 -118
  158. package/src/model-property.js +0 -29
  159. package/src/model.js +0 -9
  160. package/src/mysql/migration-generator.js +0 -188
  161. package/src/mysql/mysql-db.js +0 -320
  162. package/src/mysql/schema-introspector.js +0 -158
  163. package/src/record.js +0 -127
  164. package/src/relationships.js +0 -43
  165. package/src/serializer.js +0 -138
  166. package/src/store.js +0 -211
  167. package/src/transforms.js +0 -20
  168. package/src/utils.js +0 -12
  169. package/test-events-setup.js +0 -41
  170. package/test-hooks-manual.js +0 -54
  171. package/test-hooks-with-logging.js +0 -52
@@ -1,158 +0,0 @@
1
- import Orm from '@stonyx/orm';
2
- import { getMysqlType } from './type-map.js';
3
- import { camelCaseToKebabCase } from '@stonyx/utils/string';
4
- import { pluralize } from '../utils.js';
5
- import { dbKey } from '../db.js';
6
-
7
- function getRelationshipInfo(property) {
8
- if (typeof property !== 'function') return null;
9
- const fnStr = property.toString();
10
-
11
- if (fnStr.includes(`getRelationships('belongsTo',`)) return 'belongsTo';
12
- if (fnStr.includes(`getRelationships('hasMany',`)) return 'hasMany';
13
-
14
- return null;
15
- }
16
-
17
- export function introspectModels() {
18
- const { models } = Orm.instance;
19
- const schemas = {};
20
-
21
- for (const [modelKey, modelClass] of Object.entries(models)) {
22
- const name = camelCaseToKebabCase(modelKey.slice(0, -5));
23
-
24
- if (name === dbKey) continue;
25
-
26
- const model = new modelClass(modelKey);
27
- const columns = {};
28
- const foreignKeys = {};
29
- const relationships = { belongsTo: {}, hasMany: {} };
30
- let idType = 'number';
31
-
32
- const transforms = Orm.instance.transforms;
33
-
34
- for (const [key, property] of Object.entries(model)) {
35
- if (key.startsWith('__')) continue;
36
-
37
- const relType = getRelationshipInfo(property);
38
-
39
- if (relType === 'belongsTo') {
40
- relationships.belongsTo[key] = true;
41
- } else if (relType === 'hasMany') {
42
- relationships.hasMany[key] = true;
43
- } else if (property?.constructor?.name === 'ModelProperty') {
44
- if (key === 'id') {
45
- idType = property.type;
46
- } else {
47
- columns[key] = getMysqlType(property.type, transforms[property.type]);
48
- }
49
- }
50
- }
51
-
52
- // Build foreign keys from belongsTo relationships
53
- for (const relName of Object.keys(relationships.belongsTo)) {
54
- const fkColumn = `${relName}_id`;
55
- foreignKeys[fkColumn] = {
56
- references: pluralize(relName),
57
- column: 'id',
58
- };
59
- }
60
-
61
- schemas[name] = {
62
- table: pluralize(name),
63
- idType,
64
- columns,
65
- foreignKeys,
66
- relationships,
67
- };
68
- }
69
-
70
- return schemas;
71
- }
72
-
73
- export function buildTableDDL(name, schema, allSchemas = {}) {
74
- const { table, idType, columns, foreignKeys } = schema;
75
- const lines = [];
76
-
77
- // Primary key
78
- if (idType === 'string') {
79
- lines.push(' `id` VARCHAR(255) PRIMARY KEY');
80
- } else {
81
- lines.push(' `id` INT AUTO_INCREMENT PRIMARY KEY');
82
- }
83
-
84
- // Attribute columns
85
- for (const [col, mysqlType] of Object.entries(columns)) {
86
- lines.push(` \`${col}\` ${mysqlType}`);
87
- }
88
-
89
- // Foreign key columns
90
- for (const [fkCol, fkDef] of Object.entries(foreignKeys)) {
91
- const refIdType = getReferencedIdType(fkDef.references, allSchemas);
92
- lines.push(` \`${fkCol}\` ${refIdType}`);
93
- }
94
-
95
- // Timestamps
96
- lines.push(' `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP');
97
- lines.push(' `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
98
-
99
- // Foreign key constraints
100
- for (const [fkCol, fkDef] of Object.entries(foreignKeys)) {
101
- lines.push(` FOREIGN KEY (\`${fkCol}\`) REFERENCES \`${fkDef.references}\`(\`${fkDef.column}\`) ON DELETE SET NULL`);
102
- }
103
-
104
- return `CREATE TABLE IF NOT EXISTS \`${table}\` (\n${lines.join(',\n')}\n)`;
105
- }
106
-
107
- function getReferencedIdType(tableName, allSchemas) {
108
- // Look up the referenced table's PK type from schemas
109
- for (const schema of Object.values(allSchemas)) {
110
- if (schema.table === tableName) {
111
- return schema.idType === 'string' ? 'VARCHAR(255)' : 'INT';
112
- }
113
- }
114
-
115
- // Default to INT if referenced table not found in schemas
116
- return 'INT';
117
- }
118
-
119
- export function getTopologicalOrder(schemas) {
120
- const visited = new Set();
121
- const order = [];
122
-
123
- function visit(name) {
124
- if (visited.has(name)) return;
125
- visited.add(name);
126
-
127
- const schema = schemas[name];
128
- if (!schema) return;
129
-
130
- // Visit dependencies (belongsTo targets) first
131
- for (const relName of Object.keys(schema.relationships.belongsTo)) {
132
- visit(relName);
133
- }
134
-
135
- order.push(name);
136
- }
137
-
138
- for (const name of Object.keys(schemas)) {
139
- visit(name);
140
- }
141
-
142
- return order;
143
- }
144
-
145
- export function schemasToSnapshot(schemas) {
146
- const snapshot = {};
147
-
148
- for (const [name, schema] of Object.entries(schemas)) {
149
- snapshot[name] = {
150
- table: schema.table,
151
- idType: schema.idType,
152
- columns: { ...schema.columns },
153
- foreignKeys: { ...schema.foreignKeys },
154
- };
155
- }
156
-
157
- return snapshot;
158
- }
package/src/record.js DELETED
@@ -1,127 +0,0 @@
1
- import { store } from './index.js';
2
- import { getComputedProperties } from "./serializer.js";
3
- import { camelCaseToKebabCase } from '@stonyx/utils/string';
4
- import { pluralize } from './utils.js';
5
- export default class Record {
6
- __data = {};
7
- __relationships = {};
8
- __serialized = false;
9
-
10
- constructor(model, serializer) {
11
- this.__model = model;
12
- this.__serializer = serializer;
13
-
14
- }
15
-
16
- serialize(rawData, options={}) {
17
- const { __data:data } = this;
18
-
19
- if (this.__serialized && !options.update) {
20
- const relatedIds = {};
21
-
22
- for (const [ key, childRecord ] of Object.entries(this.__relationships)) {
23
- relatedIds[key] = Array.isArray(childRecord)
24
- ? childRecord.map(r => r.id)
25
- : childRecord?.id ?? null;
26
- }
27
-
28
- return { ...data, ...relatedIds };
29
- }
30
-
31
- const normalizedData = this.__serializer.normalize(rawData);
32
- this.__serializer.setProperties(normalizedData, this, options);
33
-
34
- return data;
35
- }
36
-
37
- // Similar to serialize, but preserves top level relationship records
38
- format() {
39
- if (!this.__serialized) throw new Error('Record must be serialized before being converted to JSON');
40
-
41
- const { __data:data } = this;
42
- const records = {};
43
-
44
- for (const [ key, childRecord ] of Object.entries(this.__relationships)) {
45
- records[key] = Array.isArray(childRecord)
46
- ? childRecord.map(r => r.serialize())
47
- : childRecord?.serialize() ?? null;
48
- }
49
-
50
- return { ...data, ...records };
51
- }
52
-
53
- // Formats record for JSON API output
54
- toJSON(options = {}) {
55
- if (!this.__serialized) throw new Error('Record must be serialized before being converted to JSON');
56
-
57
- const { fields, baseUrl } = options;
58
- const { __data:data } = this;
59
- const modelName = this.__model.__name;
60
- const pluralizedModelName = pluralize(modelName);
61
- const recordId = data.id;
62
- const relationships = {};
63
- const attributes = {};
64
-
65
- for (const [key, value] of Object.entries(data)) {
66
- if (key === 'id') continue;
67
- if (fields && !fields.has(key)) continue;
68
- attributes[key] = value;
69
- }
70
-
71
- for (const [key, getter] of getComputedProperties(this.__model)) {
72
- if (fields && !fields.has(key)) continue;
73
- attributes[key] = getter.call(this);
74
- }
75
-
76
- for (const [key, childRecord] of Object.entries(this.__relationships)) {
77
- if (fields && !fields.has(key)) continue;
78
-
79
- const relationshipData = Array.isArray(childRecord)
80
- ? childRecord.map(r => ({ type: r.__model.__name, id: r.id }))
81
- : childRecord ? { type: childRecord.__model.__name, id: childRecord.id } : null;
82
-
83
- // Dasherize the key for URL paths (e.g., accessLinks -> access-links)
84
- const dasherizedKey = camelCaseToKebabCase(key);
85
-
86
- relationships[dasherizedKey] = { data: relationshipData };
87
-
88
- // Add links to relationship if baseUrl provided
89
- if (baseUrl) {
90
- relationships[dasherizedKey].links = {
91
- self: `${baseUrl}/${pluralizedModelName}/${recordId}/relationships/${dasherizedKey}`,
92
- related: `${baseUrl}/${pluralizedModelName}/${recordId}/${dasherizedKey}`
93
- };
94
- }
95
- }
96
-
97
- const result = {
98
- attributes,
99
- relationships,
100
- id: recordId,
101
- type: modelName,
102
- };
103
-
104
- // Add resource links if baseUrl provided
105
- if (baseUrl) {
106
- result.links = {
107
- self: `${baseUrl}/${pluralizedModelName}/${recordId}`
108
- };
109
- }
110
-
111
- return result;
112
- }
113
-
114
- unload(options={}) {
115
- store.unloadRecord(this.__model.__name, this.id, options);
116
- }
117
-
118
- clean() {
119
- try {
120
- for (const key of Object.keys(this)) {
121
- delete this[key];
122
- }
123
- } catch {
124
- // Ignore errors during cleanup, as some keys may not be deletable
125
- }
126
- }
127
- }
@@ -1,43 +0,0 @@
1
- import { relationships } from "@stonyx/orm";
2
-
3
- export default class Relationships {
4
- constructor() {
5
- if (Relationships.instance) return Relationships.instance;
6
- Relationships.instance = this;
7
-
8
- this.data = new Map();
9
- }
10
-
11
- get(key) {
12
- return this.data.get(key);
13
- }
14
-
15
- set(key, value) {
16
- this.data.set(key, value);
17
- }
18
- }
19
-
20
- // TODO: Refactor mapping to remove a level of iteration
21
- export function getRelationships(type, sourceModel, targetModel, relationshipId) {
22
- const allRelationships = relationships.get(type);
23
-
24
- // create relationship map for this type of it doesn't already exist
25
- if (!allRelationships.has(sourceModel)) allRelationships.set(sourceModel, new Map());
26
-
27
- const modelRelationship = allRelationships.get(sourceModel);
28
-
29
- if (!modelRelationship.has(targetModel)) modelRelationship.set(targetModel, new Map());
30
-
31
- const relationship = modelRelationship.get(targetModel);
32
-
33
- // TODO: Determine whether already having id should be handled differently
34
- //if (relationship.has(relationshipId)) return;
35
-
36
- return relationship;
37
- }
38
-
39
- export function getHasManyRelationships(sourceModel, targetModel) {
40
- return relationships.get('hasMany').get(sourceModel)?.get(targetModel);
41
- }
42
-
43
- export const TYPES = ['global', 'hasMany', 'belongsTo', 'pending'];
package/src/serializer.js DELETED
@@ -1,138 +0,0 @@
1
- import config from 'stonyx/config';
2
- import { get, makeArray } from '@stonyx/utils/object';
3
-
4
- const RESERVED_KEYS = ['__name'];
5
-
6
- function searchQuery(query, array, key) {
7
- const result = makeArray(array).find(item => {
8
- for (const [ prop, value ] of Object.entries(query)) {
9
- if (item[prop] !== value) return false;
10
-
11
- return true;
12
- }
13
- });
14
-
15
- if (!result) return null;
16
- if (key) return result[key];
17
-
18
- return result;
19
- }
20
-
21
- function query(rawData, pathPrefix, subPath) {
22
- if (!rawData) return null;
23
-
24
- const [ path, getter, pointer ] = makeArray(subPath);
25
- const fullPath = `${pathPrefix}${path}`;
26
- const value = get(rawData, fullPath);
27
-
28
- if (getter === undefined || getter === null) return value;
29
-
30
- try {
31
- switch(typeof getter) {
32
- case 'object':
33
- return searchQuery(getter, value, pointer);
34
-
35
- case 'function':
36
- return getter(value);
37
-
38
- case 'number':
39
- const element = value[getter];
40
- return pointer ? element[pointer] : element;
41
-
42
- default:
43
- return value[getter];
44
- }
45
- } catch (error) {
46
- if (config.debug) console.error(`Cannot parse value for ${fullPath}.`, { getter, query }, error);
47
- }
48
- }
49
-
50
- export default class Serializer {
51
- map = {};
52
- path = '';
53
-
54
- constructor(model) {
55
- this.model = model;
56
- }
57
-
58
- /**
59
- * This method populates the record's instance with instances of
60
- * the ModelProperty object, while setting parsed values to the record's
61
- * __data property, which represents the serialized version of the data
62
- */
63
- setProperties(rawData, record, options) {
64
- const { path, model } = this;
65
- const keys = Object.keys(model).filter(key => !RESERVED_KEYS.includes(key));
66
- const pathPrefix = path ? `${path}.` : '';
67
- const { __data:parsedData, __relationships:relatedRecords } = record;
68
-
69
- for (const key of keys) {
70
- const subPath = options.serialize ? (this.map[key] || key) : key;
71
- const handler = model[key];
72
- const data = query(rawData, pathPrefix, subPath);
73
-
74
- // Ignore null/undefined values on updates (TODO: What if we want it set to null?)
75
- if ((data === null || data === undefined) && options.update) continue;
76
-
77
- // Relationship handling
78
- if (typeof handler === 'function') {
79
- // Pass relationship key name to handler for pending fulfillment
80
- const handlerOptions = { ...options, _relationshipKey: key };
81
- const childRecord = handler(record, data, handlerOptions);
82
-
83
- record[key] = childRecord
84
- relatedRecords[key] = childRecord;
85
-
86
- continue;
87
- }
88
-
89
- // Direct assignment handling
90
- if (handler?.constructor?.name !== 'ModelProperty') {
91
- parsedData[key] = handler;
92
- record[key] = handler;
93
- continue;
94
- }
95
-
96
- Object.defineProperty(record, key, {
97
- enumerable: true,
98
- configurable: true,
99
- get: () => handler.value,
100
- set(newValue) {
101
- handler.ignoreFirstTransform = !options.transform;
102
- handler.value = newValue;
103
- parsedData[key] = handler.value;
104
- }
105
- });
106
-
107
- record[key] = data;
108
- }
109
-
110
- if (options.update) return;
111
-
112
- // Serialize computed properties
113
- for (const [key, getter] of getComputedProperties(this.model)) {
114
- Object.defineProperty(record, key, {
115
- enumerable: true,
116
- get: () => getter.call(record)
117
- });
118
- }
119
-
120
- record.__serialized = true;
121
- }
122
-
123
- /**
124
- * OVERRIDE: This hook allows for data manipulation prior to serialization logic
125
- */
126
- normalize(data) {
127
- return data;
128
- }
129
- }
130
-
131
- export function getComputedProperties(classInstance) {
132
- const proto = Object.getPrototypeOf(classInstance);
133
- if (!proto || proto === Object.prototype) return [];
134
-
135
- return Object.entries(Object.getOwnPropertyDescriptors(proto))
136
- .filter(([key, descriptor]) => key !== 'constructor' && descriptor.get)
137
- .map(([key, descriptor]) => [key, descriptor.get]);
138
- }
package/src/store.js DELETED
@@ -1,211 +0,0 @@
1
- import { relationships } from '@stonyx/orm';
2
- import { TYPES } from './relationships.js';
3
-
4
- export default class Store {
5
- constructor() {
6
- if (Store.instance) return Store.instance;
7
- Store.instance = this;
8
-
9
- this.data = new Map();
10
- }
11
-
12
- get(key, id) {
13
- if (!id) return this.data.get(key);
14
-
15
- return this.data.get(key)?.get(id);
16
- }
17
-
18
- set(key, value) {
19
- this.data.set(key, value);
20
- }
21
-
22
- remove(key, id) {
23
- if (id) return this.unloadRecord(key, id);
24
-
25
- this.unloadAllRecords(key);
26
- }
27
-
28
- unloadRecord(model, id, options={}) {
29
- const modelStore = this.data.get(model);
30
-
31
- if (!modelStore) {
32
- console.warn(`[Store] Cannot unload record: model "${model}" not found in store`);
33
- return;
34
- }
35
-
36
- const record = modelStore.get(id);
37
-
38
- if (!record) {
39
- console.warn(`[Store] Cannot unload record: ${model}:${id} not found in store`);
40
- return;
41
- }
42
-
43
- const { toUnload, visited } = options.includeChildren
44
- ? this._buildUnloadQueue(record, options)
45
- : { toUnload: [{ record, modelName: model, recordId: id }], visited: new Set([`${model}:${id}`]) };
46
-
47
- for (const item of toUnload.reverse()) {
48
- const { record: recordToUnload, modelName, recordId } = item;
49
-
50
- this._removeFromHasManyArrays(modelName, recordId, visited);
51
- this._nullifyBelongsToReferences(modelName, recordId, visited);
52
- this._cleanupRelationshipRegistries(modelName, recordId);
53
- recordToUnload.clean();
54
-
55
- this.data.get(modelName).delete(recordId);
56
- }
57
- }
58
-
59
- unloadAllRecords(model, options={}) {
60
- const modelStore = this.data.get(model);
61
-
62
- if (!modelStore) {
63
- console.warn(`[Store] Cannot unload all records: model "${model}" not found in store`);
64
- return;
65
- }
66
-
67
- const recordIds = Array.from(modelStore.keys());
68
-
69
- for (const id of recordIds) {
70
- if (modelStore.has(id)) {
71
- this.unloadRecord(model, id, options);
72
- }
73
- }
74
-
75
- for (const relationshipType of TYPES) relationships.get(relationshipType).delete(model);
76
- }
77
-
78
- _removeFromHasManyArrays(modelName, recordId, visited) {
79
- const hasManyRegistry = relationships.get('hasMany');
80
-
81
- for (const [sourceModel, targetModels] of hasManyRegistry) {
82
- const targetModelMap = targetModels.get(modelName);
83
- if (!targetModelMap) continue;
84
-
85
- for (const [sourceRecordId, hasManyArray] of targetModelMap) {
86
- const sourceKey = `${sourceModel}:${sourceRecordId}`;
87
-
88
- // Don't modify arrays of records being deleted
89
- if (visited.has(sourceKey)) continue;
90
-
91
- const index = hasManyArray.findIndex(r => r && r.id === recordId);
92
- if (index !== -1) hasManyArray.splice(index, 1);
93
- }
94
- }
95
- }
96
-
97
- _nullifyBelongsToReferences(modelName, recordId, visited) {
98
- const belongsToRegistry = relationships.get('belongsTo');
99
-
100
- for (const [sourceModel, targetModels] of belongsToRegistry) {
101
- const targetModelMap = targetModels.get(modelName);
102
- if (!targetModelMap) continue;
103
-
104
- for (const [sourceRecordId, belongsToRecord] of targetModelMap) {
105
- if (belongsToRecord && belongsToRecord.id === recordId) {
106
- const sourceKey = `${sourceModel}:${sourceRecordId}`;
107
-
108
- if (visited.has(sourceKey)) continue;
109
- targetModelMap.set(sourceRecordId, null);
110
-
111
- const sourceRecord = this.get(sourceModel, sourceRecordId);
112
- if (sourceRecord && sourceRecord.__relationships) {
113
- for (const [key, value] of Object.entries(sourceRecord.__relationships)) {
114
- if (value && value.id === recordId) {
115
- sourceRecord.__relationships[key] = null;
116
- }
117
- }
118
- }
119
- }
120
- }
121
- }
122
- }
123
-
124
- _cleanupRelationshipRegistries(modelName, recordId) {
125
- const hasManyMap = relationships.get('hasMany').get(modelName);
126
- if (hasManyMap) {
127
- for (const [, recordMap] of hasManyMap) recordMap.delete(recordId);
128
- }
129
-
130
- const belongsToMap = relationships.get('belongsTo').get(modelName);
131
- if (belongsToMap) {
132
- for (const [, recordMap] of belongsToMap) recordMap.delete(recordId);
133
- }
134
-
135
- const pendingMap = relationships.get('pending').get(modelName);
136
- if (pendingMap) pendingMap.delete(recordId);
137
- }
138
-
139
- /**
140
- * Extracts hasMany and non-bidirectional belongsTo children from a record
141
- * @private
142
- */
143
- _getChildren(record) {
144
- const children = [];
145
-
146
- if (!record.__relationships) return children;
147
-
148
- for (const [key, value] of Object.entries(record.__relationships)) {
149
- // hasMany children - always include
150
- if (Array.isArray(value)) {
151
- for (const childRecord of value) {
152
- if (childRecord) children.push({ childRecord, relationshipKey: key, type: 'hasMany' });
153
- }
154
- } else if (value && !this._isBidirectionalRelationship(
155
- record.__model.__name,
156
- value.__model.__name
157
- )) {
158
- children.push({ childRecord: value, relationshipKey: key, type: 'belongsTo' });
159
- }
160
- }
161
-
162
- return children;
163
- }
164
-
165
- _isBidirectionalRelationship(sourceModel, targetModel) {
166
- const hasManyRegistry = relationships.get('hasMany');
167
- const inverseMap = hasManyRegistry.get(targetModel)?.get(sourceModel);
168
-
169
- return inverseMap && inverseMap.size > 0;
170
- }
171
-
172
- _buildUnloadQueue(record, options) {
173
- const visited = new Set();
174
- const toUnload = [];
175
- const queue = [{
176
- record,
177
- modelName: record.__model.__name,
178
- recordId: record.id,
179
- isRoot: true,
180
- depth: 0
181
- }];
182
-
183
- while (queue.length > 0) {
184
- const item = queue.shift();
185
- const key = `${item.modelName}:${item.recordId}`;
186
-
187
- if (visited.has(key)) continue;
188
- visited.add(key);
189
-
190
- toUnload.push(item);
191
-
192
- // Add children to queue if includeChildren is enabled
193
- if (options.includeChildren) {
194
- const children = this._getChildren(item.record);
195
- for (const { childRecord } of children) {
196
- if (childRecord) {
197
- queue.push({
198
- record: childRecord,
199
- modelName: childRecord.__model.__name,
200
- recordId: childRecord.id,
201
- isRoot: false,
202
- depth: item.depth + 1
203
- });
204
- }
205
- }
206
- }
207
- }
208
-
209
- return { toUnload, visited };
210
- }
211
- }
package/src/transforms.js DELETED
@@ -1,20 +0,0 @@
1
- import { getTimestamp } from "@stonyx/utils/date";
2
-
3
- const transforms = {
4
- boolean: value => typeof value === 'string' ? value.trim().toLowerCase() === 'true' : !!value,
5
- date: value => value ? new Date(value) : null,
6
- float: value => parseFloat(value),
7
- number: value => parseInt(value),
8
- passthrough: value => value,
9
- string: value => String(value),
10
- timestamp: value => getTimestamp(value),
11
- trim: value => value?.trim(),
12
- uppercase: value => value?.toUpperCase(),
13
- };
14
-
15
- // Math Proxies
16
- ['ceil', 'floor', 'round'].forEach(method => {
17
- transforms[method] = value => Math[method](value);
18
- });
19
-
20
- export default transforms;
package/src/utils.js DELETED
@@ -1,12 +0,0 @@
1
- import { pluralize as basePluralize } from '@stonyx/utils/string';
2
-
3
- // Wrapper to handle dasherized model names (e.g., "access-link" → "access-links")
4
- export function pluralize(word) {
5
- if (word.includes('-')) {
6
- const parts = word.split('-');
7
- const pluralizedLast = basePluralize(parts.pop());
8
- return [...parts, pluralizedLast].join('-');
9
- }
10
-
11
- return basePluralize(word);
12
- }