@stonyx/orm 0.3.2-beta.76 → 0.3.2-beta.78
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 +4 -3
- package/dist/store.d.ts +10 -0
- package/dist/store.js +34 -0
- package/package.json +1 -1
- package/src/manage-record.ts +4 -2
- package/src/store.ts +38 -0
package/dist/manage-record.js
CHANGED
|
@@ -104,6 +104,9 @@ export function createRecord(modelName, rawData = {}, userOptions = {}) {
|
|
|
104
104
|
// Auto-persist to SQL — skip for DB loads (isDbRecord) and relationship resolution (_relationshipKey)
|
|
105
105
|
const shouldPersist = orm?.sqlDb && !options.isDbRecord && !userOptions._relationshipKey && !options._skipAutoPersist;
|
|
106
106
|
if (shouldPersist) {
|
|
107
|
+
// Capture ID before persist — SQL adapters re-key pending IDs to real DB IDs,
|
|
108
|
+
// but relationship registries were keyed with this original ID
|
|
109
|
+
const registryId = record.id;
|
|
107
110
|
const response = { data: { id: record.id } };
|
|
108
111
|
orm.sqlDb.persist('create', modelName, { rawData }, response)
|
|
109
112
|
.catch((err) => {
|
|
@@ -117,9 +120,7 @@ export function createRecord(modelName, rawData = {}, userOptions = {}) {
|
|
|
117
120
|
.finally(() => {
|
|
118
121
|
// Evict non-memory records after persist to prevent unbounded heap growth (stonyx#81)
|
|
119
122
|
if (store._memoryResolver && !store._memoryResolver(modelName)) {
|
|
120
|
-
|
|
121
|
-
if (ms)
|
|
122
|
-
ms.delete(record.id);
|
|
123
|
+
store.evictRecord(modelName, record.id, registryId);
|
|
123
124
|
}
|
|
124
125
|
});
|
|
125
126
|
}
|
package/dist/store.d.ts
CHANGED
|
@@ -46,6 +46,16 @@ export default class Store {
|
|
|
46
46
|
private _isMemoryModel;
|
|
47
47
|
set(key: string, value: Map<number | string, unknown>): void;
|
|
48
48
|
remove(key: string, id?: number | string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Evict a record from the store with full relationship registry cleanup,
|
|
51
|
+
* WITHOUT calling record.clean(). This preserves the caller's reference
|
|
52
|
+
* to the returned record (used by memory:false post-persist eviction).
|
|
53
|
+
*
|
|
54
|
+
* @param registryId - The ID used when the record's relationships were
|
|
55
|
+
* registered. For SQL models with pending IDs, this is the original
|
|
56
|
+
* negative pending ID (before the adapter re-keyed to the real DB ID).
|
|
57
|
+
*/
|
|
58
|
+
evictRecord(modelName: string, id: unknown, registryId?: unknown): void;
|
|
49
59
|
unloadRecord(model: string, id: unknown, options?: UnloadOptions): void;
|
|
50
60
|
unloadAllRecords(model: string, options?: UnloadOptions): void;
|
|
51
61
|
private _removeFromHasManyArrays;
|
package/dist/store.js
CHANGED
|
@@ -127,6 +127,40 @@ export default class Store {
|
|
|
127
127
|
return this.unloadRecord(key, id);
|
|
128
128
|
this.unloadAllRecords(key);
|
|
129
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Evict a record from the store with full relationship registry cleanup,
|
|
132
|
+
* WITHOUT calling record.clean(). This preserves the caller's reference
|
|
133
|
+
* to the returned record (used by memory:false post-persist eviction).
|
|
134
|
+
*
|
|
135
|
+
* @param registryId - The ID used when the record's relationships were
|
|
136
|
+
* registered. For SQL models with pending IDs, this is the original
|
|
137
|
+
* negative pending ID (before the adapter re-keyed to the real DB ID).
|
|
138
|
+
*/
|
|
139
|
+
evictRecord(modelName, id, registryId) {
|
|
140
|
+
const modelStore = this.data.get(modelName);
|
|
141
|
+
if (!modelStore)
|
|
142
|
+
return;
|
|
143
|
+
if (typeof id !== 'string' && typeof id !== 'number')
|
|
144
|
+
return;
|
|
145
|
+
const raw = modelStore.get(id);
|
|
146
|
+
if (!raw || !isStoreRecord(raw))
|
|
147
|
+
return;
|
|
148
|
+
const visited = new Set([`${modelName}:${id}`]);
|
|
149
|
+
// Remove from hasMany arrays and nullify belongsTo references using current ID
|
|
150
|
+
// (the adapter updates record.id, so value-based matches need the current ID)
|
|
151
|
+
this._removeFromHasManyArrays(modelName, id, visited);
|
|
152
|
+
this._nullifyBelongsToReferences(modelName, id, visited);
|
|
153
|
+
// Clean up relationship registry entries using the registry key
|
|
154
|
+
// (belongsTo/hasMany registries were keyed by the ID at registration time,
|
|
155
|
+
// which may differ from the current ID if SQL persist re-keyed the record)
|
|
156
|
+
const cleanupId = registryId ?? id;
|
|
157
|
+
this._cleanupRelationshipRegistries(modelName, cleanupId);
|
|
158
|
+
// If registryId differs from id, also clean with current id as safety net
|
|
159
|
+
if (registryId !== undefined && registryId !== id) {
|
|
160
|
+
this._cleanupRelationshipRegistries(modelName, id);
|
|
161
|
+
}
|
|
162
|
+
modelStore.delete(id);
|
|
163
|
+
}
|
|
130
164
|
unloadRecord(model, id, options = {}) {
|
|
131
165
|
const modelStore = this.data.get(model);
|
|
132
166
|
if (!modelStore) {
|
package/package.json
CHANGED
package/src/manage-record.ts
CHANGED
|
@@ -139,6 +139,9 @@ export function createRecord(modelName: string, rawData: { [key: string]: unknow
|
|
|
139
139
|
// Auto-persist to SQL — skip for DB loads (isDbRecord) and relationship resolution (_relationshipKey)
|
|
140
140
|
const shouldPersist = orm?.sqlDb && !options.isDbRecord && !userOptions._relationshipKey && !options._skipAutoPersist;
|
|
141
141
|
if (shouldPersist) {
|
|
142
|
+
// Capture ID before persist — SQL adapters re-key pending IDs to real DB IDs,
|
|
143
|
+
// but relationship registries were keyed with this original ID
|
|
144
|
+
const registryId = record.id;
|
|
142
145
|
const response = { data: { id: record.id } };
|
|
143
146
|
orm!.sqlDb!.persist('create', modelName, { rawData }, response)
|
|
144
147
|
.catch((err: unknown) => {
|
|
@@ -152,8 +155,7 @@ export function createRecord(modelName: string, rawData: { [key: string]: unknow
|
|
|
152
155
|
.finally(() => {
|
|
153
156
|
// Evict non-memory records after persist to prevent unbounded heap growth (stonyx#81)
|
|
154
157
|
if (store._memoryResolver && !store._memoryResolver(modelName)) {
|
|
155
|
-
|
|
156
|
-
if (ms) ms.delete(record.id as number | string);
|
|
158
|
+
store.evictRecord(modelName, record.id, registryId);
|
|
157
159
|
}
|
|
158
160
|
});
|
|
159
161
|
}
|
package/src/store.ts
CHANGED
|
@@ -193,6 +193,44 @@ export default class Store {
|
|
|
193
193
|
this.unloadAllRecords(key);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Evict a record from the store with full relationship registry cleanup,
|
|
198
|
+
* WITHOUT calling record.clean(). This preserves the caller's reference
|
|
199
|
+
* to the returned record (used by memory:false post-persist eviction).
|
|
200
|
+
*
|
|
201
|
+
* @param registryId - The ID used when the record's relationships were
|
|
202
|
+
* registered. For SQL models with pending IDs, this is the original
|
|
203
|
+
* negative pending ID (before the adapter re-keyed to the real DB ID).
|
|
204
|
+
*/
|
|
205
|
+
evictRecord(modelName: string, id: unknown, registryId?: unknown): void {
|
|
206
|
+
const modelStore = this.data.get(modelName);
|
|
207
|
+
if (!modelStore) return;
|
|
208
|
+
|
|
209
|
+
if (typeof id !== 'string' && typeof id !== 'number') return;
|
|
210
|
+
const raw = modelStore.get(id);
|
|
211
|
+
if (!raw || !isStoreRecord(raw)) return;
|
|
212
|
+
|
|
213
|
+
const visited = new Set([`${modelName}:${id}`]);
|
|
214
|
+
|
|
215
|
+
// Remove from hasMany arrays and nullify belongsTo references using current ID
|
|
216
|
+
// (the adapter updates record.id, so value-based matches need the current ID)
|
|
217
|
+
this._removeFromHasManyArrays(modelName, id, visited);
|
|
218
|
+
this._nullifyBelongsToReferences(modelName, id, visited);
|
|
219
|
+
|
|
220
|
+
// Clean up relationship registry entries using the registry key
|
|
221
|
+
// (belongsTo/hasMany registries were keyed by the ID at registration time,
|
|
222
|
+
// which may differ from the current ID if SQL persist re-keyed the record)
|
|
223
|
+
const cleanupId = registryId ?? id;
|
|
224
|
+
this._cleanupRelationshipRegistries(modelName, cleanupId);
|
|
225
|
+
|
|
226
|
+
// If registryId differs from id, also clean with current id as safety net
|
|
227
|
+
if (registryId !== undefined && registryId !== id) {
|
|
228
|
+
this._cleanupRelationshipRegistries(modelName, id);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
modelStore.delete(id);
|
|
232
|
+
}
|
|
233
|
+
|
|
196
234
|
unloadRecord(model: string, id: unknown, options: UnloadOptions = {}): void {
|
|
197
235
|
const modelStore = this.data.get(model);
|
|
198
236
|
|