@stonyx/orm 0.3.2-alpha.14 → 0.3.2-alpha.15
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/dist/manage-record.js +12 -24
- package/dist/serializer.js +2 -27
- package/package.json +1 -1
- package/src/manage-record.ts +17 -31
- package/src/serializer.ts +2 -27
package/dist/manage-record.js
CHANGED
|
@@ -52,34 +52,13 @@ export function createRecord(modelName, rawData = {}, userOptions = {}) {
|
|
|
52
52
|
relationship.push(record);
|
|
53
53
|
pendingHasMany.splice(0);
|
|
54
54
|
}
|
|
55
|
-
// FK-based inverse hasMany wiring — when a child record is created with a
|
|
56
|
-
// foreign-key field (e.g. `owner: 'owner-1'` on an animal), find any parent
|
|
57
|
-
// whose hasMany registry targets this model and push the child into the
|
|
58
|
-
// parent's shared array. This covers edge cases where the child is created
|
|
59
|
-
// in a separate async frame without a belongsTo handler firing.
|
|
60
|
-
const hasManyReg = getHasManyRegistry();
|
|
61
|
-
if (hasManyReg) {
|
|
62
|
-
for (const [parentModelName, targetMap] of hasManyReg) {
|
|
63
|
-
const childArrayMap = targetMap.get(modelName);
|
|
64
|
-
if (!childArrayMap)
|
|
65
|
-
continue;
|
|
66
|
-
// Check if rawData contains a FK field matching the parent model name
|
|
67
|
-
const fkValue = rawData[parentModelName];
|
|
68
|
-
if (fkValue === undefined || fkValue === null)
|
|
69
|
-
continue;
|
|
70
|
-
const parentArray = childArrayMap.get(fkValue);
|
|
71
|
-
if (parentArray && !parentArray.includes(record)) {
|
|
72
|
-
parentArray.push(record);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
55
|
// Fulfill pending belongsTo relationships
|
|
77
56
|
const pendingBelongsToQueue = getPendingBelongsToRegistry();
|
|
78
57
|
const pendingBelongsToRaw = pendingBelongsToQueue.get(modelName)?.get(record.id);
|
|
79
58
|
const pendingBelongsTo = Array.isArray(pendingBelongsToRaw) ? pendingBelongsToRaw : undefined;
|
|
80
59
|
if (pendingBelongsTo) {
|
|
81
60
|
const belongsToReg = getBelongsToRegistry();
|
|
82
|
-
const
|
|
61
|
+
const hasManyReg = getHasManyRegistry();
|
|
83
62
|
for (const { sourceRecord, sourceModelName, relationshipKey, relationshipId } of pendingBelongsTo) {
|
|
84
63
|
// Update the belongsTo relationship on the source record
|
|
85
64
|
sourceRecord.__relationships[relationshipKey] = record;
|
|
@@ -93,7 +72,7 @@ export function createRecord(modelName, rawData = {}, userOptions = {}) {
|
|
|
93
72
|
}
|
|
94
73
|
}
|
|
95
74
|
// Wire inverse hasMany if it exists
|
|
96
|
-
const inverseHasMany =
|
|
75
|
+
const inverseHasMany = hasManyReg.get(modelName)?.get(sourceModelName)?.get(record.id);
|
|
97
76
|
if (inverseHasMany && !inverseHasMany.includes(sourceRecord)) {
|
|
98
77
|
inverseHasMany.push(sourceRecord);
|
|
99
78
|
}
|
|
@@ -105,13 +84,22 @@ export function createRecord(modelName, rawData = {}, userOptions = {}) {
|
|
|
105
84
|
const shouldPersist = orm?.sqlDb && !options.isDbRecord && !userOptions._relationshipKey && !options._skipAutoPersist;
|
|
106
85
|
if (shouldPersist) {
|
|
107
86
|
const response = { data: { id: record.id } };
|
|
108
|
-
orm.sqlDb.persist('create', modelName, { rawData }, response)
|
|
87
|
+
orm.sqlDb.persist('create', modelName, { rawData }, response)
|
|
88
|
+
.catch((err) => {
|
|
109
89
|
orm.emitPersistError({
|
|
110
90
|
operation: 'create',
|
|
111
91
|
modelName,
|
|
112
92
|
recordId: record.id,
|
|
113
93
|
error: err instanceof Error ? err : new Error(String(err)),
|
|
114
94
|
});
|
|
95
|
+
})
|
|
96
|
+
.finally(() => {
|
|
97
|
+
// Evict non-memory records after persist to prevent unbounded heap growth (stonyx#81)
|
|
98
|
+
if (store._memoryResolver && !store._memoryResolver(modelName)) {
|
|
99
|
+
const ms = store.get(modelName);
|
|
100
|
+
if (ms)
|
|
101
|
+
ms.delete(record.id);
|
|
102
|
+
}
|
|
115
103
|
});
|
|
116
104
|
}
|
|
117
105
|
return record;
|
package/dist/serializer.js
CHANGED
|
@@ -79,33 +79,8 @@ export default class Serializer {
|
|
|
79
79
|
// Pass relationship key name to handler for pending fulfillment
|
|
80
80
|
const handlerOptions = { ...options, _relationshipKey: key };
|
|
81
81
|
const childRecord = handler(record, data, handlerOptions);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// serialization time. This is critical when child records are created
|
|
85
|
-
// in a later async frame — the belongsTo inverse wiring pushes into
|
|
86
|
-
// the shared registry array, and the getter ensures the parent sees it.
|
|
87
|
-
const isHasMany = handler.__relationshipType === 'hasMany';
|
|
88
|
-
if (isHasMany) {
|
|
89
|
-
// `childRecord` IS the shared registry array — define a getter that
|
|
90
|
-
// always dereferences through the same array reference.
|
|
91
|
-
const registryArray = childRecord;
|
|
92
|
-
Object.defineProperty(rec, key, {
|
|
93
|
-
enumerable: true,
|
|
94
|
-
configurable: true,
|
|
95
|
-
get: () => registryArray,
|
|
96
|
-
set(v) { relatedRecords[key] = v; }
|
|
97
|
-
});
|
|
98
|
-
Object.defineProperty(relatedRecords, key, {
|
|
99
|
-
enumerable: true,
|
|
100
|
-
configurable: true,
|
|
101
|
-
get: () => registryArray,
|
|
102
|
-
set(v) { Object.defineProperty(relatedRecords, key, { value: v, writable: true, enumerable: true, configurable: true }); }
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
rec[key] = childRecord;
|
|
107
|
-
relatedRecords[key] = childRecord;
|
|
108
|
-
}
|
|
82
|
+
rec[key] = childRecord;
|
|
83
|
+
relatedRecords[key] = childRecord;
|
|
109
84
|
continue;
|
|
110
85
|
}
|
|
111
86
|
// Aggregate property handling — use the rawData value, not the aggregate descriptor
|
package/package.json
CHANGED
package/src/manage-record.ts
CHANGED
|
@@ -79,28 +79,6 @@ export function createRecord(modelName: string, rawData: { [key: string]: unknow
|
|
|
79
79
|
pendingHasMany.splice(0);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
// FK-based inverse hasMany wiring — when a child record is created with a
|
|
83
|
-
// foreign-key field (e.g. `owner: 'owner-1'` on an animal), find any parent
|
|
84
|
-
// whose hasMany registry targets this model and push the child into the
|
|
85
|
-
// parent's shared array. This covers edge cases where the child is created
|
|
86
|
-
// in a separate async frame without a belongsTo handler firing.
|
|
87
|
-
const hasManyReg = getHasManyRegistry();
|
|
88
|
-
if (hasManyReg) {
|
|
89
|
-
for (const [parentModelName, targetMap] of hasManyReg) {
|
|
90
|
-
const childArrayMap = targetMap.get(modelName);
|
|
91
|
-
if (!childArrayMap) continue;
|
|
92
|
-
|
|
93
|
-
// Check if rawData contains a FK field matching the parent model name
|
|
94
|
-
const fkValue = rawData[parentModelName];
|
|
95
|
-
if (fkValue === undefined || fkValue === null) continue;
|
|
96
|
-
|
|
97
|
-
const parentArray = childArrayMap.get(fkValue);
|
|
98
|
-
if (parentArray && !parentArray.includes(record)) {
|
|
99
|
-
parentArray.push(record);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
82
|
// Fulfill pending belongsTo relationships
|
|
105
83
|
const pendingBelongsToQueue = getPendingBelongsToRegistry();
|
|
106
84
|
const pendingBelongsToRaw = pendingBelongsToQueue.get(modelName)?.get(record.id);
|
|
@@ -108,7 +86,7 @@ export function createRecord(modelName: string, rawData: { [key: string]: unknow
|
|
|
108
86
|
|
|
109
87
|
if (pendingBelongsTo) {
|
|
110
88
|
const belongsToReg = getBelongsToRegistry();
|
|
111
|
-
const
|
|
89
|
+
const hasManyReg = getHasManyRegistry();
|
|
112
90
|
|
|
113
91
|
for (const { sourceRecord, sourceModelName, relationshipKey, relationshipId } of pendingBelongsTo) {
|
|
114
92
|
// Update the belongsTo relationship on the source record
|
|
@@ -125,7 +103,7 @@ export function createRecord(modelName: string, rawData: { [key: string]: unknow
|
|
|
125
103
|
}
|
|
126
104
|
|
|
127
105
|
// Wire inverse hasMany if it exists
|
|
128
|
-
const inverseHasMany =
|
|
106
|
+
const inverseHasMany = hasManyReg.get(modelName)?.get(sourceModelName)?.get(record.id);
|
|
129
107
|
|
|
130
108
|
if (inverseHasMany && !inverseHasMany.includes(sourceRecord)) {
|
|
131
109
|
inverseHasMany.push(sourceRecord);
|
|
@@ -140,14 +118,22 @@ export function createRecord(modelName: string, rawData: { [key: string]: unknow
|
|
|
140
118
|
const shouldPersist = orm?.sqlDb && !options.isDbRecord && !userOptions._relationshipKey && !options._skipAutoPersist;
|
|
141
119
|
if (shouldPersist) {
|
|
142
120
|
const response = { data: { id: record.id } };
|
|
143
|
-
orm!.sqlDb!.persist('create', modelName, { rawData }, response)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
121
|
+
orm!.sqlDb!.persist('create', modelName, { rawData }, response)
|
|
122
|
+
.catch((err: unknown) => {
|
|
123
|
+
orm!.emitPersistError({
|
|
124
|
+
operation: 'create',
|
|
125
|
+
modelName,
|
|
126
|
+
recordId: record.id,
|
|
127
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
128
|
+
});
|
|
129
|
+
})
|
|
130
|
+
.finally(() => {
|
|
131
|
+
// Evict non-memory records after persist to prevent unbounded heap growth (stonyx#81)
|
|
132
|
+
if (store._memoryResolver && !store._memoryResolver(modelName)) {
|
|
133
|
+
const ms = store.get(modelName);
|
|
134
|
+
if (ms) ms.delete(record.id as number | string);
|
|
135
|
+
}
|
|
149
136
|
});
|
|
150
|
-
});
|
|
151
137
|
}
|
|
152
138
|
|
|
153
139
|
return record;
|
package/src/serializer.ts
CHANGED
|
@@ -94,33 +94,8 @@ export default class Serializer {
|
|
|
94
94
|
const handlerOptions = { ...options, _relationshipKey: key };
|
|
95
95
|
const childRecord = handler(record, data, handlerOptions);
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// serialization time. This is critical when child records are created
|
|
100
|
-
// in a later async frame — the belongsTo inverse wiring pushes into
|
|
101
|
-
// the shared registry array, and the getter ensures the parent sees it.
|
|
102
|
-
const isHasMany = (handler as { __relationshipType?: string }).__relationshipType === 'hasMany';
|
|
103
|
-
|
|
104
|
-
if (isHasMany) {
|
|
105
|
-
// `childRecord` IS the shared registry array — define a getter that
|
|
106
|
-
// always dereferences through the same array reference.
|
|
107
|
-
const registryArray = childRecord as unknown[];
|
|
108
|
-
Object.defineProperty(rec, key, {
|
|
109
|
-
enumerable: true,
|
|
110
|
-
configurable: true,
|
|
111
|
-
get: () => registryArray,
|
|
112
|
-
set(v: unknown) { relatedRecords[key] = v; }
|
|
113
|
-
});
|
|
114
|
-
Object.defineProperty(relatedRecords, key, {
|
|
115
|
-
enumerable: true,
|
|
116
|
-
configurable: true,
|
|
117
|
-
get: () => registryArray,
|
|
118
|
-
set(v: unknown) { Object.defineProperty(relatedRecords, key, { value: v, writable: true, enumerable: true, configurable: true }); }
|
|
119
|
-
});
|
|
120
|
-
} else {
|
|
121
|
-
rec[key] = childRecord;
|
|
122
|
-
relatedRecords[key] = childRecord;
|
|
123
|
-
}
|
|
97
|
+
rec[key] = childRecord;
|
|
98
|
+
relatedRecords[key] = childRecord;
|
|
124
99
|
|
|
125
100
|
continue;
|
|
126
101
|
}
|