@stonyx/orm 0.2.5-alpha.0 → 0.3.1

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 (166) hide show
  1. package/README.md +482 -15
  2. package/config/environment.js +63 -6
  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 +75 -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 +257 -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 +475 -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 +280 -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 +296 -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/schema-helpers.d.ts +20 -0
  72. package/dist/schema-helpers.js +48 -0
  73. package/dist/serializer.d.ts +17 -0
  74. package/dist/serializer.js +136 -0
  75. package/dist/setup-rest-server.d.ts +1 -0
  76. package/dist/setup-rest-server.js +52 -0
  77. package/dist/standalone-db.d.ts +58 -0
  78. package/dist/standalone-db.js +142 -0
  79. package/dist/store.d.ts +62 -0
  80. package/dist/store.js +286 -0
  81. package/dist/timescale/query-builder.d.ts +43 -0
  82. package/dist/timescale/query-builder.js +115 -0
  83. package/dist/timescale/timescale-db.d.ts +45 -0
  84. package/dist/timescale/timescale-db.js +84 -0
  85. package/dist/transforms.d.ts +2 -0
  86. package/dist/transforms.js +17 -0
  87. package/dist/types/orm-types.d.ts +153 -0
  88. package/dist/types/orm-types.js +1 -0
  89. package/dist/utils.d.ts +7 -0
  90. package/dist/utils.js +17 -0
  91. package/dist/view-resolver.d.ts +8 -0
  92. package/dist/view-resolver.js +171 -0
  93. package/dist/view.d.ts +11 -0
  94. package/dist/view.js +18 -0
  95. package/package.json +64 -11
  96. package/src/aggregates.ts +109 -0
  97. package/src/{attr.js → attr.ts} +2 -2
  98. package/src/belongs-to.ts +90 -0
  99. package/src/cli.ts +183 -0
  100. package/src/commands.ts +179 -0
  101. package/src/db.ts +232 -0
  102. package/src/exports/db.ts +7 -0
  103. package/src/has-many.ts +92 -0
  104. package/src/hooks.ts +151 -0
  105. package/src/{index.js → index.ts} +12 -2
  106. package/src/main.ts +229 -0
  107. package/src/manage-record.ts +161 -0
  108. package/src/{meta-request.js → meta-request.ts} +17 -14
  109. package/src/migrate.ts +72 -0
  110. package/src/model-property.ts +35 -0
  111. package/src/model.ts +21 -0
  112. package/src/mysql/connection.ts +43 -0
  113. package/src/mysql/migration-generator.ts +337 -0
  114. package/src/mysql/migration-runner.ts +121 -0
  115. package/src/mysql/mysql-db.ts +543 -0
  116. package/src/mysql/query-builder.ts +69 -0
  117. package/src/mysql/schema-introspector.ts +310 -0
  118. package/src/mysql/type-map.ts +42 -0
  119. package/src/orm-request.ts +582 -0
  120. package/src/plural-registry.ts +12 -0
  121. package/src/postgres/connection.ts +48 -0
  122. package/src/postgres/migration-generator.ts +370 -0
  123. package/src/postgres/migration-runner.ts +115 -0
  124. package/src/postgres/postgres-db.ts +616 -0
  125. package/src/postgres/query-builder.ts +148 -0
  126. package/src/postgres/schema-introspector.ts +360 -0
  127. package/src/postgres/type-map.ts +61 -0
  128. package/src/record.ts +186 -0
  129. package/src/relationships.ts +54 -0
  130. package/src/schema-helpers.ts +59 -0
  131. package/src/serializer.ts +161 -0
  132. package/src/setup-rest-server.ts +62 -0
  133. package/src/standalone-db.ts +185 -0
  134. package/src/store.ts +373 -0
  135. package/src/timescale/query-builder.ts +174 -0
  136. package/src/timescale/timescale-db.ts +119 -0
  137. package/src/transforms.ts +20 -0
  138. package/src/types/mysql2.d.ts +49 -0
  139. package/src/types/orm-types.ts +158 -0
  140. package/src/types/pg.d.ts +32 -0
  141. package/src/types/stonyx-cron.d.ts +5 -0
  142. package/src/types/stonyx-events.d.ts +4 -0
  143. package/src/types/stonyx-rest-server.d.ts +16 -0
  144. package/src/types/stonyx-utils.d.ts +33 -0
  145. package/src/types/stonyx.d.ts +21 -0
  146. package/src/utils.ts +22 -0
  147. package/src/view-resolver.ts +211 -0
  148. package/src/view.ts +22 -0
  149. package/.claude/project-structure.md +0 -578
  150. package/.github/workflows/ci.yml +0 -36
  151. package/.github/workflows/publish.yml +0 -143
  152. package/src/belongs-to.js +0 -63
  153. package/src/db.js +0 -80
  154. package/src/has-many.js +0 -61
  155. package/src/main.js +0 -119
  156. package/src/manage-record.js +0 -103
  157. package/src/model-property.js +0 -29
  158. package/src/model.js +0 -9
  159. package/src/orm-request.js +0 -249
  160. package/src/record.js +0 -100
  161. package/src/relationships.js +0 -43
  162. package/src/serializer.js +0 -138
  163. package/src/setup-rest-server.js +0 -57
  164. package/src/store.js +0 -211
  165. package/src/transforms.js +0 -20
  166. package/stonyx-bootstrap.cjs +0 -30
@@ -0,0 +1,211 @@
1
+ import Orm, { store } from '@stonyx/orm';
2
+ import { createRecord } from './manage-record.js';
3
+ import { AggregateProperty } from './aggregates.js';
4
+ import { get } from '@stonyx/utils/object';
5
+ import type { SourceRecord } from './types/orm-types.js';
6
+
7
+ interface ViewClass {
8
+ source?: string;
9
+ resolve?: Record<string, unknown>;
10
+ groupBy?: string;
11
+ new (viewName: string): Record<string, unknown>;
12
+ }
13
+
14
+ export default class ViewResolver {
15
+ viewName: string;
16
+
17
+ constructor(viewName: string) {
18
+ this.viewName = viewName;
19
+ }
20
+
21
+ async resolveAll(): Promise<unknown[]> {
22
+ const orm = Orm.instance;
23
+ const { modelClass: viewClass } = orm.getRecordClasses(this.viewName) as { modelClass: ViewClass | undefined; serializerClass: unknown };
24
+
25
+ if (!viewClass) return [];
26
+
27
+ const source = viewClass.source;
28
+ if (!source) return [];
29
+
30
+ const sourceRecords = await store.findAll(source) as SourceRecord[];
31
+ if (!sourceRecords || sourceRecords.length === 0) {
32
+ return [];
33
+ }
34
+
35
+ const resolveMap = viewClass.resolve || {};
36
+ const viewInstance = new viewClass(this.viewName);
37
+ const aggregateFields: Record<string, AggregateProperty> = {};
38
+ const regularFields: Record<string, unknown> = {};
39
+
40
+ // Categorize fields on the view instance
41
+ for (const [key, value] of Object.entries(viewInstance)) {
42
+ if (key.startsWith('__')) continue;
43
+ if (key === 'id') continue;
44
+
45
+ if (value instanceof AggregateProperty) {
46
+ aggregateFields[key] = value;
47
+ } else if (typeof value !== 'function') {
48
+ // Regular attr or direct value — not a relationship handler
49
+ regularFields[key] = value;
50
+ }
51
+ }
52
+
53
+ const groupByField = viewClass.groupBy;
54
+
55
+ if (groupByField) {
56
+ return this._resolveGroupBy(sourceRecords, groupByField, aggregateFields, regularFields, resolveMap, viewClass);
57
+ }
58
+
59
+ return this._resolvePerRecord(sourceRecords, aggregateFields, regularFields, resolveMap, viewClass);
60
+ }
61
+
62
+ private _resolvePerRecord(
63
+ sourceRecords: SourceRecord[],
64
+ aggregateFields: Record<string, AggregateProperty>,
65
+ regularFields: Record<string, unknown>,
66
+ resolveMap: Record<string, unknown>,
67
+ viewClass: ViewClass
68
+ ): unknown[] {
69
+ const results: unknown[] = [];
70
+
71
+ for (const sourceRecord of sourceRecords) {
72
+ const rawData: Record<string, unknown> = { id: sourceRecord.id };
73
+
74
+ // Compute aggregate fields from source record's relationships
75
+ for (const [key, aggProp] of Object.entries(aggregateFields)) {
76
+ if (!aggProp.relationship) continue;
77
+ const relatedRecords = sourceRecord.__relationships?.[aggProp.relationship]
78
+ || sourceRecord[aggProp.relationship];
79
+ const relArray = Array.isArray(relatedRecords) ? relatedRecords : [];
80
+ rawData[key] = aggProp.compute(relArray);
81
+ }
82
+
83
+ // Apply resolve map entries
84
+ for (const [key, resolver] of Object.entries(resolveMap)) {
85
+ if (typeof resolver === 'function') {
86
+ rawData[key] = resolver(sourceRecord);
87
+ } else if (typeof resolver === 'string') {
88
+ rawData[key] = get(sourceRecord.__data || sourceRecord, resolver)
89
+ ?? get(sourceRecord, resolver);
90
+ }
91
+ }
92
+
93
+ // Map regular attr fields from source record if not already set
94
+ for (const key of Object.keys(regularFields)) {
95
+ if (rawData[key] !== undefined) continue;
96
+
97
+ const sourceValue = sourceRecord.__data?.[key] ?? sourceRecord[key];
98
+ if (sourceValue !== undefined) {
99
+ rawData[key] = sourceValue;
100
+ }
101
+ }
102
+
103
+ // Set belongsTo source relationship
104
+ const viewInstanceForRel = new viewClass(this.viewName);
105
+ for (const [key, value] of Object.entries(viewInstanceForRel)) {
106
+ if (typeof value === 'function' && key !== 'id') {
107
+ // This is a relationship handler — pass the source record id
108
+ rawData[key] = sourceRecord.id;
109
+ }
110
+ }
111
+
112
+ // Clear existing record from store to allow re-resolution
113
+ const viewStore = store.get(this.viewName);
114
+ if (viewStore?.has(rawData.id as number | string)) {
115
+ viewStore.delete(rawData.id as number | string);
116
+ }
117
+
118
+ const record = createRecord(this.viewName, rawData, { isDbRecord: true });
119
+ results.push(record);
120
+ }
121
+
122
+ return results;
123
+ }
124
+
125
+ private _resolveGroupBy(
126
+ sourceRecords: SourceRecord[],
127
+ groupByField: string,
128
+ aggregateFields: Record<string, AggregateProperty>,
129
+ regularFields: Record<string, unknown>,
130
+ resolveMap: Record<string, unknown>,
131
+ viewClass: ViewClass
132
+ ): unknown[] {
133
+ // Group source records by the groupBy field value
134
+ const groups = new Map<unknown, SourceRecord[]>();
135
+ for (const record of sourceRecords) {
136
+ const key = record.__data?.[groupByField] ?? record[groupByField];
137
+ if (!groups.has(key)) {
138
+ groups.set(key, []);
139
+ }
140
+ const group = groups.get(key);
141
+ if (group) group.push(record);
142
+ }
143
+
144
+ const results: unknown[] = [];
145
+
146
+ for (const [groupKey, groupRecords] of groups) {
147
+ const rawData: Record<string, unknown> = { id: groupKey };
148
+
149
+ // Compute aggregate fields
150
+ for (const [key, aggProp] of Object.entries(aggregateFields)) {
151
+ if (aggProp.relationship === undefined) {
152
+ // Field-level aggregate — compute over group records directly
153
+ rawData[key] = aggProp.compute(groupRecords);
154
+ } else {
155
+ // Relationship aggregate — flatten related records across all group members
156
+ if (!aggProp.relationship) continue;
157
+ const allRelated: unknown[] = [];
158
+ for (const record of groupRecords) {
159
+ const relatedRecords = record.__relationships?.[aggProp.relationship!]
160
+ || record[aggProp.relationship!];
161
+ if (Array.isArray(relatedRecords)) {
162
+ allRelated.push(...relatedRecords);
163
+ }
164
+ }
165
+ rawData[key] = aggProp.compute(allRelated as { __data?: Record<string, unknown>; [key: string]: unknown }[]);
166
+ }
167
+ }
168
+
169
+ // Apply resolve map entries — functions receive the group array
170
+ for (const [key, resolver] of Object.entries(resolveMap)) {
171
+ if (typeof resolver === 'function') {
172
+ rawData[key] = resolver(groupRecords);
173
+ } else if (typeof resolver === 'string') {
174
+ // String path — take value from first record in group
175
+ const first = groupRecords[0];
176
+ rawData[key] = get(first.__data || first, resolver)
177
+ ?? get(first, resolver);
178
+ }
179
+ }
180
+
181
+ // Map regular attr fields from first record if not already set
182
+ for (const key of Object.keys(regularFields)) {
183
+ if (rawData[key] !== undefined) continue;
184
+ const first = groupRecords[0];
185
+ const sourceValue = first.__data?.[key] ?? first[key];
186
+ if (sourceValue !== undefined) {
187
+ rawData[key] = sourceValue;
188
+ }
189
+ }
190
+
191
+ // Clear existing record from store to allow re-resolution
192
+ const viewStore = store.get(this.viewName);
193
+ if (viewStore?.has(rawData.id as number | string)) {
194
+ viewStore.delete(rawData.id as number | string);
195
+ }
196
+
197
+ const record = createRecord(this.viewName, rawData, { isDbRecord: true });
198
+ results.push(record);
199
+ }
200
+
201
+ return results;
202
+ }
203
+
204
+ async resolveOne(id: unknown): Promise<unknown> {
205
+ const all = await this.resolveAll();
206
+ return all.find((record: unknown) => {
207
+ const r = record as SourceRecord;
208
+ return r.id === id || r.id == id;
209
+ });
210
+ }
211
+ }
package/src/view.ts ADDED
@@ -0,0 +1,22 @@
1
+ import attr from './attr.js';
2
+
3
+ export default class View {
4
+ static memory: boolean = false;
5
+ static readOnly: boolean = true;
6
+ static pluralName: string | undefined = undefined;
7
+ static source: string | undefined = undefined;
8
+ static groupBy: string | undefined = undefined;
9
+ static resolve: ((record: unknown) => unknown) | undefined = undefined;
10
+
11
+ id = attr('number');
12
+ __name: string;
13
+
14
+ constructor(name: string) {
15
+ this.__name = name;
16
+
17
+ // Enforce readOnly — cannot be overridden to false
18
+ if ((this.constructor as typeof View).readOnly !== true) {
19
+ throw new Error(`View '${name}' cannot override readOnly to false`);
20
+ }
21
+ }
22
+ }