js-bao 0.2.10 → 0.2.12
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/README.md +174 -0
- package/dist/BaseModel-5YQCROYE.js +17 -0
- package/dist/BaseModel-5YQCROYE.js.map +1 -0
- package/dist/BaseModel-FCNWDJBH.js +17 -0
- package/dist/BaseModel-FCNWDJBH.js.map +1 -0
- package/dist/BrowserDatabaseFactory-PXOTK2DQ.js +119 -0
- package/dist/BrowserDatabaseFactory-PXOTK2DQ.js.map +1 -0
- package/dist/BrowserDatabaseFactory-WD4VX2VZ.js +119 -0
- package/dist/BrowserDatabaseFactory-WD4VX2VZ.js.map +1 -0
- package/dist/IncludeResolver-RCKQGNPZ.js +385 -0
- package/dist/IncludeResolver-RCKQGNPZ.js.map +1 -0
- package/dist/IncludeResolver-WGSQDMS7.js +385 -0
- package/dist/IncludeResolver-WGSQDMS7.js.map +1 -0
- package/dist/NodeDatabaseFactory-J4Z36UF3.js +165 -0
- package/dist/NodeDatabaseFactory-J4Z36UF3.js.map +1 -0
- package/dist/NodeDatabaseFactory-QIEKAXBM.js +10 -0
- package/dist/NodeDatabaseFactory-QIEKAXBM.js.map +1 -0
- package/dist/NodeSqliteEngine-HJSAYE4E.js +383 -0
- package/dist/NodeSqliteEngine-HJSAYE4E.js.map +1 -0
- package/dist/NodeSqliteEngine-I5SLWLME.js +383 -0
- package/dist/NodeSqliteEngine-I5SLWLME.js.map +1 -0
- package/dist/browser.cjs +3779 -3370
- package/dist/browser.d.cts +18 -1
- package/dist/browser.d.ts +18 -1
- package/dist/browser.js +3750 -3341
- package/dist/chunk-3PZWHUZO.js +4153 -0
- package/dist/chunk-3PZWHUZO.js.map +1 -0
- package/dist/chunk-53MS4MN7.js +373 -0
- package/dist/chunk-53MS4MN7.js.map +1 -0
- package/dist/chunk-65G2P4GL.js +709 -0
- package/dist/chunk-65G2P4GL.js.map +1 -0
- package/dist/chunk-6UX3YSCW.js +4151 -0
- package/dist/chunk-6UX3YSCW.js.map +1 -0
- package/dist/chunk-DANSD6BE.js +709 -0
- package/dist/chunk-DANSD6BE.js.map +1 -0
- package/dist/chunk-DF3JEQXA.js +373 -0
- package/dist/chunk-DF3JEQXA.js.map +1 -0
- package/dist/chunk-GO3APTPX.js +61 -0
- package/dist/chunk-GO3APTPX.js.map +1 -0
- package/dist/chunk-ID4U6IQC.js +53 -0
- package/dist/chunk-ID4U6IQC.js.map +1 -0
- package/dist/chunk-RQVS3LVL.js +165 -0
- package/dist/chunk-RQVS3LVL.js.map +1 -0
- package/dist/client.cjs +837 -0
- package/dist/client.d.cts +1101 -0
- package/dist/client.d.ts +1101 -0
- package/dist/client.js +806 -0
- package/dist/cloudflare-do.cjs +3637 -0
- package/dist/cloudflare-do.d.cts +1366 -0
- package/dist/cloudflare-do.d.ts +1366 -0
- package/dist/cloudflare-do.js +3614 -0
- package/dist/cloudflare.cjs +1048 -0
- package/dist/cloudflare.d.cts +1381 -0
- package/dist/cloudflare.d.ts +1381 -0
- package/dist/cloudflare.js +1017 -0
- package/dist/codegen.cjs +259 -18
- package/dist/environment-TOTQICSE.js +17 -0
- package/dist/environment-TOTQICSE.js.map +1 -0
- package/dist/index.cjs +1906 -1493
- package/dist/index.d.cts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +1871 -1458
- package/dist/node.cjs +4779 -4366
- package/dist/node.d.cts +18 -1
- package/dist/node.d.ts +18 -1
- package/dist/node.js +4602 -4189
- package/package.json +41 -12
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseModel,
|
|
3
|
+
Logger
|
|
4
|
+
} from "./chunk-6UX3YSCW.js";
|
|
5
|
+
|
|
6
|
+
// src/models/relationshipManager.ts
|
|
7
|
+
function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
8
|
+
return async function() {
|
|
9
|
+
const relatedId = this[config.relatedIdField];
|
|
10
|
+
if (relatedId === null || typeof relatedId === "undefined") return null;
|
|
11
|
+
return targetModelClass.find(relatedId);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbEngine) {
|
|
15
|
+
return async function(paginationArgs) {
|
|
16
|
+
const {
|
|
17
|
+
limit,
|
|
18
|
+
afterCursor,
|
|
19
|
+
beforeCursor,
|
|
20
|
+
direction = "forward"
|
|
21
|
+
} = paginationArgs || {};
|
|
22
|
+
const {
|
|
23
|
+
relatedIdField,
|
|
24
|
+
orderByField = "id",
|
|
25
|
+
orderDirection = "ASC"
|
|
26
|
+
} = config;
|
|
27
|
+
const targetSchema = targetModelClass.getSchema();
|
|
28
|
+
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
29
|
+
Logger.error(
|
|
30
|
+
`[RelationshipManager] Target model ${targetModelClass.name} for hasMany relationship has no valid schema name.`
|
|
31
|
+
);
|
|
32
|
+
return { data: [], nextCursor: null, prevCursor: null };
|
|
33
|
+
}
|
|
34
|
+
const filter = { [relatedIdField]: this.id };
|
|
35
|
+
const queryOptions = {
|
|
36
|
+
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 }
|
|
37
|
+
};
|
|
38
|
+
if (limit !== void 0) {
|
|
39
|
+
queryOptions.limit = limit + 1;
|
|
40
|
+
}
|
|
41
|
+
if (direction === "forward" && afterCursor) {
|
|
42
|
+
filter[orderByField] = {
|
|
43
|
+
[orderDirection === "ASC" ? "$gt" : "$lt"]: afterCursor
|
|
44
|
+
};
|
|
45
|
+
} else if (direction === "backward") {
|
|
46
|
+
if (beforeCursor) {
|
|
47
|
+
filter[orderByField] = {
|
|
48
|
+
[orderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
queryOptions.sort = { [orderByField]: orderDirection === "ASC" ? -1 : 1 };
|
|
52
|
+
}
|
|
53
|
+
const result = await targetModelClass.query(filter, queryOptions);
|
|
54
|
+
const results = result.data;
|
|
55
|
+
let nextCursor = null;
|
|
56
|
+
let prevCursor = null;
|
|
57
|
+
const isFirstPage = direction === "forward" && !afterCursor;
|
|
58
|
+
if (limit !== void 0) {
|
|
59
|
+
if (direction === "forward") {
|
|
60
|
+
if (results.length > limit) {
|
|
61
|
+
nextCursor = results[limit - 1][orderByField];
|
|
62
|
+
results.pop();
|
|
63
|
+
}
|
|
64
|
+
if (results.length > 0 && !isFirstPage) {
|
|
65
|
+
prevCursor = results[0][orderByField];
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
if (results.length > limit) {
|
|
69
|
+
prevCursor = results[limit - 1][orderByField];
|
|
70
|
+
results.pop();
|
|
71
|
+
}
|
|
72
|
+
results.reverse();
|
|
73
|
+
if (results.length > 0 && beforeCursor) {
|
|
74
|
+
nextCursor = results[results.length - 1][orderByField];
|
|
75
|
+
}
|
|
76
|
+
if (results.length > 0 && !beforeCursor) {
|
|
77
|
+
if (results.length === limit) {
|
|
78
|
+
prevCursor = results[0][orderByField];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { data: results, nextCursor, prevCursor };
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClass, joinModelClass, _dbEngine) {
|
|
87
|
+
return async function(paginationArgs) {
|
|
88
|
+
const {
|
|
89
|
+
limit,
|
|
90
|
+
afterCursor,
|
|
91
|
+
beforeCursor,
|
|
92
|
+
direction = "forward"
|
|
93
|
+
} = paginationArgs || {};
|
|
94
|
+
const joinSchema = joinModelClass.getSchema();
|
|
95
|
+
const targetSchema = targetModelClass.getSchema();
|
|
96
|
+
if (!joinSchema || !joinSchema.options || !joinSchema.options.name) {
|
|
97
|
+
Logger.error(
|
|
98
|
+
`[RelationshipManager] Join model ${joinModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
99
|
+
);
|
|
100
|
+
return { data: [], nextCursor: null, prevCursor: null };
|
|
101
|
+
}
|
|
102
|
+
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
103
|
+
Logger.error(
|
|
104
|
+
`[RelationshipManager] Target model ${targetModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
105
|
+
);
|
|
106
|
+
return { data: [], nextCursor: null, prevCursor: null };
|
|
107
|
+
}
|
|
108
|
+
const {
|
|
109
|
+
joinModelLocalField,
|
|
110
|
+
joinModelRelatedField,
|
|
111
|
+
joinModelOrderByField = "id",
|
|
112
|
+
joinModelOrderDirection = "ASC"
|
|
113
|
+
} = config;
|
|
114
|
+
const joinFilter = { [joinModelLocalField]: this.id };
|
|
115
|
+
const joinQueryOptions = {
|
|
116
|
+
sort: {
|
|
117
|
+
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? 1 : -1
|
|
118
|
+
},
|
|
119
|
+
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 }
|
|
120
|
+
};
|
|
121
|
+
if (limit !== void 0) {
|
|
122
|
+
joinQueryOptions.limit = limit + 1;
|
|
123
|
+
}
|
|
124
|
+
if (direction === "forward" && afterCursor) {
|
|
125
|
+
joinFilter[joinModelOrderByField] = {
|
|
126
|
+
[joinModelOrderDirection === "ASC" ? "$gt" : "$lt"]: afterCursor
|
|
127
|
+
};
|
|
128
|
+
} else if (direction === "backward") {
|
|
129
|
+
if (beforeCursor) {
|
|
130
|
+
joinFilter[joinModelOrderByField] = {
|
|
131
|
+
[joinModelOrderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
joinQueryOptions.sort = {
|
|
135
|
+
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? -1 : 1
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const joinResult = await joinModelClass.query(
|
|
139
|
+
joinFilter,
|
|
140
|
+
joinQueryOptions
|
|
141
|
+
);
|
|
142
|
+
const joinResults = joinResult.data;
|
|
143
|
+
let nextCursorFromJoin = null;
|
|
144
|
+
let prevCursorFromJoin = null;
|
|
145
|
+
const isFirstPage = direction === "forward" && !afterCursor;
|
|
146
|
+
if (limit !== void 0) {
|
|
147
|
+
if (direction === "forward") {
|
|
148
|
+
if (joinResults.length > limit) {
|
|
149
|
+
nextCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
150
|
+
joinResults.pop();
|
|
151
|
+
}
|
|
152
|
+
if (joinResults.length > 0 && !isFirstPage) {
|
|
153
|
+
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
if (joinResults.length > limit) {
|
|
157
|
+
prevCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
158
|
+
joinResults.pop();
|
|
159
|
+
}
|
|
160
|
+
joinResults.reverse();
|
|
161
|
+
if (joinResults.length > 0 && beforeCursor) {
|
|
162
|
+
nextCursorFromJoin = joinResults[joinResults.length - 1][joinModelOrderByField];
|
|
163
|
+
}
|
|
164
|
+
if (joinResults.length > 0 && !beforeCursor) {
|
|
165
|
+
if (joinResults.length === limit) {
|
|
166
|
+
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const relatedIds = joinResults.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
172
|
+
if (relatedIds.length === 0)
|
|
173
|
+
return { data: [], nextCursor: null, prevCursor: null };
|
|
174
|
+
const targetResult = await targetModelClass.query({
|
|
175
|
+
id: { $in: relatedIds }
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
data: targetResult.data,
|
|
179
|
+
nextCursor: nextCursorFromJoin,
|
|
180
|
+
prevCursor: prevCursorFromJoin
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function generateHasManyThroughAddMethod(_sourceModelClass, config, _targetModelClass, joinModelClass) {
|
|
185
|
+
return async function(targetInstanceOrId) {
|
|
186
|
+
const targetId = typeof targetInstanceOrId === "string" ? targetInstanceOrId : targetInstanceOrId.id;
|
|
187
|
+
if (!targetId) {
|
|
188
|
+
Logger.error("[RelationshipManager.add] Target ID is missing.");
|
|
189
|
+
throw new Error("Target ID is missing for add operation.");
|
|
190
|
+
}
|
|
191
|
+
const joinData = {};
|
|
192
|
+
joinData[config.joinModelLocalField] = this.id;
|
|
193
|
+
joinData[config.joinModelRelatedField] = targetId;
|
|
194
|
+
let yDoc = null;
|
|
195
|
+
let targetDocId = null;
|
|
196
|
+
const sourceModelConstructor = this.constructor;
|
|
197
|
+
const connectedDocuments = sourceModelConstructor.connectedDocuments;
|
|
198
|
+
if (this._metaDocId && connectedDocuments) {
|
|
199
|
+
targetDocId = this._metaDocId;
|
|
200
|
+
if (targetDocId) {
|
|
201
|
+
const documentInfo = connectedDocuments.get(targetDocId);
|
|
202
|
+
if (documentInfo) {
|
|
203
|
+
yDoc = documentInfo.yDoc;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (!yDoc) {
|
|
208
|
+
const legacyDocId = "__legacy_default__";
|
|
209
|
+
const legacyDocInfo = joinModelClass.connectedDocuments?.get(
|
|
210
|
+
legacyDocId
|
|
211
|
+
);
|
|
212
|
+
if (legacyDocInfo) {
|
|
213
|
+
yDoc = legacyDocInfo.yDoc;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!yDoc) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`[${joinModelClass.name}] No Y.Doc is connected for this join model. Connect a document via initJsBao(...).connectDocument or call initializeForDocument(yDoc, db, docId, permissionHint) before managing relationships.`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
await yDoc.transact(async () => {
|
|
222
|
+
const newJoinInstance = new joinModelClass(joinData);
|
|
223
|
+
if (targetDocId) {
|
|
224
|
+
await newJoinInstance.save({ targetDocument: targetDocId });
|
|
225
|
+
} else {
|
|
226
|
+
await newJoinInstance.save();
|
|
227
|
+
}
|
|
228
|
+
Logger.debug(
|
|
229
|
+
`[RelationshipManager.add] Created join entry in ${config.joinModel}:`,
|
|
230
|
+
newJoinInstance.toJSON()
|
|
231
|
+
);
|
|
232
|
+
}, `add-join-${config.joinModel}-${this.id}-${targetId}`);
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function generateHasManyThroughRemoveMethod(_sourceModelClass, config, _targetModelClass, joinModelClass) {
|
|
236
|
+
return async function(targetInstanceOrId) {
|
|
237
|
+
const targetId = typeof targetInstanceOrId === "string" ? targetInstanceOrId : targetInstanceOrId.id;
|
|
238
|
+
if (!targetId) {
|
|
239
|
+
Logger.error("[RelationshipManager.remove] Target ID is missing.");
|
|
240
|
+
throw new Error("Target ID is missing for remove operation.");
|
|
241
|
+
}
|
|
242
|
+
let yDoc = null;
|
|
243
|
+
let targetDocId = null;
|
|
244
|
+
const sourceModelConstructor = this.constructor;
|
|
245
|
+
const connectedDocuments = sourceModelConstructor.connectedDocuments;
|
|
246
|
+
if (this._metaDocId && connectedDocuments) {
|
|
247
|
+
targetDocId = this._metaDocId;
|
|
248
|
+
if (targetDocId) {
|
|
249
|
+
const documentInfo = connectedDocuments.get(targetDocId);
|
|
250
|
+
if (documentInfo) {
|
|
251
|
+
yDoc = documentInfo.yDoc;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!yDoc) {
|
|
256
|
+
const legacyDocId = "__legacy_default__";
|
|
257
|
+
const legacyDocInfo = joinModelClass.connectedDocuments?.get(
|
|
258
|
+
legacyDocId
|
|
259
|
+
);
|
|
260
|
+
if (legacyDocInfo) {
|
|
261
|
+
yDoc = legacyDocInfo.yDoc;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (!yDoc) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
`[${joinModelClass.name}] No Y.Doc is connected for this join model. Connect a document via initJsBao(...).connectDocument or call initializeForDocument(yDoc, db, docId, permissionHint) before managing relationships.`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
await yDoc.transact(async () => {
|
|
270
|
+
const joinRecordsResult = await joinModelClass.query(
|
|
271
|
+
{
|
|
272
|
+
[config.joinModelLocalField]: this.id,
|
|
273
|
+
[config.joinModelRelatedField]: targetId
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
limit: 1,
|
|
277
|
+
projection: { id: 1 }
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
if (joinRecordsResult.data.length > 0) {
|
|
281
|
+
for (const record of joinRecordsResult.data) {
|
|
282
|
+
const instanceToDelete = await joinModelClass.find(
|
|
283
|
+
record.id
|
|
284
|
+
);
|
|
285
|
+
if (instanceToDelete) {
|
|
286
|
+
await instanceToDelete.delete();
|
|
287
|
+
Logger.debug(
|
|
288
|
+
`[RelationshipManager.remove] Deleted join entry from ${config.joinModel} with ID: ${record.id}`
|
|
289
|
+
);
|
|
290
|
+
} else {
|
|
291
|
+
Logger.warn(
|
|
292
|
+
`[RelationshipManager.remove] Join entry with ID ${record.id} found by query but not in YMap for deletion.`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
Logger.debug(
|
|
298
|
+
`[RelationshipManager.remove] No join entry found in ${config.joinModel} for localId: ${this.id}, relatedId: ${targetId}. No action taken.`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}, `remove-join-${config.joinModel}-${this.id}-${targetId}`);
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/models/ModelRegistry.ts
|
|
306
|
+
var ModelRegistry = class _ModelRegistry {
|
|
307
|
+
static instance;
|
|
308
|
+
// Stores globally registered model classes by their name (from @Model decorator)
|
|
309
|
+
models = /* @__PURE__ */ new Map();
|
|
310
|
+
// Stores options for globally registered models (from @Model decorator)
|
|
311
|
+
modelOptions = /* @__PURE__ */ new Map();
|
|
312
|
+
// Stores fields for globally registered models (from @Model decorator, or a static getter on class)
|
|
313
|
+
// For simplicity, let's assume fields can be derived or are less critical for this registry part
|
|
314
|
+
// private modelFields: Map<string, Map<string, FieldOptions>> = new Map();
|
|
315
|
+
// Holds the subset of models explicitly set for the current initialization session
|
|
316
|
+
activeSessionModels = null;
|
|
317
|
+
dbEngine = null;
|
|
318
|
+
// Store dbEngine for relationship methods
|
|
319
|
+
constructor() {
|
|
320
|
+
}
|
|
321
|
+
static getInstance() {
|
|
322
|
+
if (!_ModelRegistry.instance) {
|
|
323
|
+
_ModelRegistry.instance = new _ModelRegistry();
|
|
324
|
+
}
|
|
325
|
+
return _ModelRegistry.instance;
|
|
326
|
+
}
|
|
327
|
+
registerModel(modelClass, options, fields) {
|
|
328
|
+
if (!options.name) {
|
|
329
|
+
throw new Error(
|
|
330
|
+
`[ModelRegistry] Model class is missing a name in its @Model options. Ensure the @Model decorator includes a 'name' property.`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
if (this.models.has(options.name)) {
|
|
334
|
+
console.warn(
|
|
335
|
+
`[ModelRegistry] Model "${options.name}" is already registered. Overwriting.`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
this.models.set(options.name, modelClass);
|
|
339
|
+
if (fields) {
|
|
340
|
+
BaseModel.attachFieldAccessors(modelClass, fields);
|
|
341
|
+
}
|
|
342
|
+
this.modelOptions.set(options.name, options);
|
|
343
|
+
console.log(
|
|
344
|
+
`[ModelRegistry] Model "${options.name}" registered successfully.`
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
// Gets the class for a globally registered model
|
|
348
|
+
getModelClass(name) {
|
|
349
|
+
return this.models.get(name);
|
|
350
|
+
}
|
|
351
|
+
getModelOptions(name) {
|
|
352
|
+
return this.modelOptions.get(name);
|
|
353
|
+
}
|
|
354
|
+
// This method might need to be adapted based on how fields are ultimately managed
|
|
355
|
+
getModelInfo(modelName) {
|
|
356
|
+
const modelClass = this.models.get(modelName);
|
|
357
|
+
const options = this.modelOptions.get(modelName);
|
|
358
|
+
if (modelClass && options) {
|
|
359
|
+
const fields = modelClass.getSchema ? modelClass.getSchema().fields : /* @__PURE__ */ new Map();
|
|
360
|
+
return { class: modelClass, options, fields };
|
|
361
|
+
}
|
|
362
|
+
return void 0;
|
|
363
|
+
}
|
|
364
|
+
getAllRegisteredModelsInfo() {
|
|
365
|
+
const infos = [];
|
|
366
|
+
for (const modelName of this.models.keys()) {
|
|
367
|
+
const info = this.getModelInfo(modelName);
|
|
368
|
+
if (info) {
|
|
369
|
+
infos.push(info);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return infos;
|
|
373
|
+
}
|
|
374
|
+
// Helper to get model name from class (assuming static property from @Model decorator)
|
|
375
|
+
getModelNameFromClass(modelClass) {
|
|
376
|
+
return modelClass.modelName;
|
|
377
|
+
}
|
|
378
|
+
setExplicitModelsForSession(modelClasses) {
|
|
379
|
+
if (modelClasses && modelClasses.length > 0) {
|
|
380
|
+
this.activeSessionModels = /* @__PURE__ */ new Map();
|
|
381
|
+
modelClasses.forEach((modelClass) => {
|
|
382
|
+
const modelName = this.getModelNameFromClass(modelClass);
|
|
383
|
+
if (!modelName) {
|
|
384
|
+
console.warn(
|
|
385
|
+
`[ModelRegistry] A model class provided to setExplicitModelsForSession does not have a static 'modelName' property or it's undefined. It will be ignored. Ensure @Model decorator sets this.`
|
|
386
|
+
);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (!this.models.has(modelName) || this.models.get(modelName) !== modelClass) {
|
|
390
|
+
console.warn(
|
|
391
|
+
`[ModelRegistry] Model class with name ${modelName} provided to setExplicitModelsForSession was not found in the global decorator-based registry or does not match. It will be ignored. Ensure the model file is imported and the @Model decorator has run correctly.`
|
|
392
|
+
);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (this.activeSessionModels) {
|
|
396
|
+
this.activeSessionModels.set(modelName, modelClass);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
let sessionModelNamesMessage = "none (no models were successfully added or session map is null)";
|
|
400
|
+
if (this.activeSessionModels) {
|
|
401
|
+
const currentSessionModelKeys = Array.from(
|
|
402
|
+
this.activeSessionModels.keys()
|
|
403
|
+
);
|
|
404
|
+
if (currentSessionModelKeys.length > 0) {
|
|
405
|
+
sessionModelNamesMessage = currentSessionModelKeys.join(", ");
|
|
406
|
+
} else {
|
|
407
|
+
sessionModelNamesMessage = "none (session map initialized but no models added)";
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
Logger.debug(
|
|
411
|
+
`[ModelRegistry] Explicit models set for session: ${sessionModelNamesMessage}`
|
|
412
|
+
);
|
|
413
|
+
} else if (modelClasses && modelClasses.length === 0) {
|
|
414
|
+
this.activeSessionModels = /* @__PURE__ */ new Map();
|
|
415
|
+
Logger.debug(
|
|
416
|
+
"[ModelRegistry] Explicitly no models for session (empty array passed)."
|
|
417
|
+
);
|
|
418
|
+
} else {
|
|
419
|
+
this.activeSessionModels = null;
|
|
420
|
+
console.log(
|
|
421
|
+
"[ModelRegistry] No explicit models specified for session (undefined), will use all decorator-registered models."
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
this.validateSessionModels();
|
|
425
|
+
}
|
|
426
|
+
async initializeAll(yDoc, dbEngine) {
|
|
427
|
+
Logger.debug("[ModelRegistry] Attempting to initialize models...");
|
|
428
|
+
this.dbEngine = dbEngine;
|
|
429
|
+
const modelsToInitialize = this.activeSessionModels || this.models;
|
|
430
|
+
if (modelsToInitialize.size === 0) {
|
|
431
|
+
console.warn(
|
|
432
|
+
"[ModelRegistry] No models to initialize (either no explicit models set for session or no models globally registered via @Model decorator)."
|
|
433
|
+
);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
console.log(
|
|
437
|
+
`[ModelRegistry] Initializing models: ${Array.from(
|
|
438
|
+
modelsToInitialize.keys()
|
|
439
|
+
).join(", ")}`
|
|
440
|
+
);
|
|
441
|
+
for (const [modelName, modelClass] of modelsToInitialize.entries()) {
|
|
442
|
+
if (modelClass && typeof modelClass.initialize === "function") {
|
|
443
|
+
Logger.debug(`[ModelRegistry] Initializing model: ${modelName}`);
|
|
444
|
+
await modelClass.initialize(yDoc, dbEngine);
|
|
445
|
+
} else {
|
|
446
|
+
const staticModelName = modelClass?.modelName || "unknown";
|
|
447
|
+
console.warn(
|
|
448
|
+
`[ModelRegistry] Model ${staticModelName} (class name: ${modelClass?.name}) does not have a static initialize method or class is not defined.`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
Logger.debug("[ModelRegistry] Model initialization phase complete.");
|
|
453
|
+
}
|
|
454
|
+
// New method to initialize models for a specific document
|
|
455
|
+
async initializeAllForDocument(yDoc, dbEngine, docId, permissionHint) {
|
|
456
|
+
Logger.debug(
|
|
457
|
+
`[ModelRegistry] Initializing models for document ${docId}...`
|
|
458
|
+
);
|
|
459
|
+
this.dbEngine = dbEngine;
|
|
460
|
+
const modelsToInitialize = this.activeSessionModels || this.models;
|
|
461
|
+
if (modelsToInitialize.size === 0) {
|
|
462
|
+
Logger.warn(
|
|
463
|
+
"[ModelRegistry] No models to initialize for document (either no explicit models set for session or no models globally registered via @Model decorator)."
|
|
464
|
+
);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
Logger.debug(
|
|
468
|
+
`[ModelRegistry] Initializing models for document ${docId}: ${Array.from(
|
|
469
|
+
modelsToInitialize.keys()
|
|
470
|
+
).join(", ")}`
|
|
471
|
+
);
|
|
472
|
+
for (const [modelName, modelClass] of modelsToInitialize.entries()) {
|
|
473
|
+
if (modelClass && typeof modelClass.initializeForDocument === "function") {
|
|
474
|
+
Logger.debug(
|
|
475
|
+
`[ModelRegistry] Initializing model ${modelName} for document ${docId}`
|
|
476
|
+
);
|
|
477
|
+
await modelClass.initializeForDocument(
|
|
478
|
+
yDoc,
|
|
479
|
+
dbEngine,
|
|
480
|
+
docId,
|
|
481
|
+
permissionHint
|
|
482
|
+
);
|
|
483
|
+
} else {
|
|
484
|
+
const staticModelName = modelClass?.modelName || "unknown";
|
|
485
|
+
console.warn(
|
|
486
|
+
`[ModelRegistry] Model ${staticModelName} (class name: ${modelClass?.name}) does not have a static initializeForDocument method or class is not defined.`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
await this.initializeRelationships();
|
|
491
|
+
Logger.debug(
|
|
492
|
+
`[ModelRegistry] Model initialization complete for document ${docId}`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
// New method to remove data from a disconnected document
|
|
496
|
+
async removeDocumentData(docId, dbEngine) {
|
|
497
|
+
Logger.debug(`[ModelRegistry] Removing data for document ${docId}...`);
|
|
498
|
+
const modelsToCleanup = this.activeSessionModels || this.models;
|
|
499
|
+
if (modelsToCleanup.size === 0) {
|
|
500
|
+
console.warn(
|
|
501
|
+
"[ModelRegistry] No models to cleanup for document (either no explicit models set for session or no models globally registered via @Model decorator)."
|
|
502
|
+
);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
for (const [modelName, modelClass] of modelsToCleanup.entries()) {
|
|
506
|
+
try {
|
|
507
|
+
Logger.debug(
|
|
508
|
+
`[ModelRegistry] Removing ${modelName} data for document ${docId}`
|
|
509
|
+
);
|
|
510
|
+
await dbEngine.deleteByDocumentId(modelName, docId);
|
|
511
|
+
if (modelClass && typeof modelClass.cleanupDocumentData === "function") {
|
|
512
|
+
await modelClass.cleanupDocumentData(docId);
|
|
513
|
+
}
|
|
514
|
+
} catch (error) {
|
|
515
|
+
console.error(
|
|
516
|
+
`[ModelRegistry] Error removing ${modelName} data for document ${docId}:`,
|
|
517
|
+
error
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
Logger.debug(`[ModelRegistry] Data removal complete for document ${docId}`);
|
|
522
|
+
}
|
|
523
|
+
// New method to initialize relationships
|
|
524
|
+
async initializeRelationships() {
|
|
525
|
+
if (!this.dbEngine) {
|
|
526
|
+
console.error(
|
|
527
|
+
"[ModelRegistry] DB engine not available for initializing relationships. Ensure initializeAll has been called."
|
|
528
|
+
);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const dbEngine = this.dbEngine;
|
|
532
|
+
Logger.debug(
|
|
533
|
+
"[ModelRegistry] Initializing relationships for active models..."
|
|
534
|
+
);
|
|
535
|
+
const activeModels = this.getActiveModels();
|
|
536
|
+
for (const [modelName, modelClass] of activeModels.entries()) {
|
|
537
|
+
const modelSchema = modelClass.getSchema ? modelClass.getSchema() : null;
|
|
538
|
+
const relationshipsConfig = modelSchema?.options?.relationships;
|
|
539
|
+
if (relationshipsConfig) {
|
|
540
|
+
Logger.debug(
|
|
541
|
+
`[ModelRegistry] Setting up relationships for ${modelName}`
|
|
542
|
+
);
|
|
543
|
+
for (const relName in relationshipsConfig) {
|
|
544
|
+
const config = relationshipsConfig[relName];
|
|
545
|
+
if (!config.model) {
|
|
546
|
+
throw new Error(
|
|
547
|
+
`[ModelRegistry] Relationship "${relName}" on model "${modelName}" is missing a target model name.`
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
const targetModelClass = this.getModelClass(config.model);
|
|
551
|
+
if (!targetModelClass) {
|
|
552
|
+
throw new Error(
|
|
553
|
+
`[ModelRegistry] Relationship "${relName}" on model "${modelName}" refers to an unknown model "${config.model}". Ensure "${config.model}" is registered and included in the session.`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
if (typeof targetModelClass.getSchema !== "function") {
|
|
557
|
+
throw new Error(
|
|
558
|
+
`[ModelRegistry] Target model "${config.model}" for relationship "${relName}" on "${modelName}" does not have a getSchema method.`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
switch (config.type) {
|
|
562
|
+
case "refersTo":
|
|
563
|
+
Logger.debug(
|
|
564
|
+
` - Adding '${relName}' (refersTo ${config.model}) to ${modelName}`
|
|
565
|
+
);
|
|
566
|
+
const refersToMethod = generateRefersToMethod(
|
|
567
|
+
modelClass,
|
|
568
|
+
config,
|
|
569
|
+
targetModelClass
|
|
570
|
+
);
|
|
571
|
+
this.addPrototypeMethod(modelClass, relName, refersToMethod);
|
|
572
|
+
break;
|
|
573
|
+
case "hasMany":
|
|
574
|
+
Logger.debug(
|
|
575
|
+
` - Adding '${relName}' (hasMany ${config.model}) to ${modelName}`
|
|
576
|
+
);
|
|
577
|
+
const hasManyMethod = generateHasManyMethod(
|
|
578
|
+
modelClass,
|
|
579
|
+
config,
|
|
580
|
+
targetModelClass,
|
|
581
|
+
dbEngine
|
|
582
|
+
);
|
|
583
|
+
this.addPrototypeMethod(modelClass, relName, hasManyMethod);
|
|
584
|
+
break;
|
|
585
|
+
case "hasManyThrough":
|
|
586
|
+
const joinModelName = config.joinModel;
|
|
587
|
+
const joinModelClass = this.getModelClass(joinModelName);
|
|
588
|
+
if (!joinModelClass) {
|
|
589
|
+
throw new Error(
|
|
590
|
+
`[ModelRegistry] Join model "${joinModelName}" for relationship "${relName}" on model "${modelName}" not found. Ensure it is registered and included in the session.`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
if (typeof joinModelClass.getSchema !== "function") {
|
|
594
|
+
throw new Error(
|
|
595
|
+
`[ModelRegistry] Join model "${joinModelName}" for relationship "${relName}" on "${modelName}" does not have a getSchema method.`
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
Logger.debug(
|
|
599
|
+
` - Adding '${relName}' (hasManyThrough ${config.model} via ${joinModelName}) to ${modelName}`
|
|
600
|
+
);
|
|
601
|
+
const hasManyThroughFetchMethod = generateHasManyThroughMethod(
|
|
602
|
+
modelClass,
|
|
603
|
+
config,
|
|
604
|
+
targetModelClass,
|
|
605
|
+
joinModelClass,
|
|
606
|
+
dbEngine
|
|
607
|
+
);
|
|
608
|
+
this.addPrototypeMethod(
|
|
609
|
+
modelClass,
|
|
610
|
+
relName,
|
|
611
|
+
hasManyThroughFetchMethod
|
|
612
|
+
);
|
|
613
|
+
const relNameSingular = config.model.endsWith("s") ? config.model.slice(0, -1) : config.model;
|
|
614
|
+
const capitalizedSingularRelName = relNameSingular.charAt(0).toUpperCase() + relNameSingular.slice(1);
|
|
615
|
+
const addMethodName = `add${capitalizedSingularRelName}`;
|
|
616
|
+
const addMethodLogic = generateHasManyThroughAddMethod(
|
|
617
|
+
modelClass,
|
|
618
|
+
config,
|
|
619
|
+
targetModelClass,
|
|
620
|
+
joinModelClass
|
|
621
|
+
);
|
|
622
|
+
this.addPrototypeMethod(
|
|
623
|
+
modelClass,
|
|
624
|
+
addMethodName,
|
|
625
|
+
addMethodLogic
|
|
626
|
+
);
|
|
627
|
+
Logger.debug(
|
|
628
|
+
` - Adding '${addMethodName}' helper to ${modelName} (for relationship ${relName})`
|
|
629
|
+
);
|
|
630
|
+
const removeMethodName = `remove${capitalizedSingularRelName}`;
|
|
631
|
+
const removeMethodLogic = generateHasManyThroughRemoveMethod(
|
|
632
|
+
modelClass,
|
|
633
|
+
config,
|
|
634
|
+
targetModelClass,
|
|
635
|
+
joinModelClass
|
|
636
|
+
);
|
|
637
|
+
this.addPrototypeMethod(
|
|
638
|
+
modelClass,
|
|
639
|
+
removeMethodName,
|
|
640
|
+
removeMethodLogic
|
|
641
|
+
);
|
|
642
|
+
Logger.debug(
|
|
643
|
+
` - Adding '${removeMethodName}' helper to ${modelName} (for relationship ${relName})`
|
|
644
|
+
);
|
|
645
|
+
break;
|
|
646
|
+
default:
|
|
647
|
+
console.warn(
|
|
648
|
+
`[ModelRegistry] Unknown relationship type "${config.type}" for ${relName} on ${modelName}. Skipping.`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
Logger.debug("[ModelRegistry] Relationship initialization phase complete.");
|
|
655
|
+
}
|
|
656
|
+
// Helper to add methods to prototype
|
|
657
|
+
addPrototypeMethod(modelClass, methodName, methodLogic) {
|
|
658
|
+
Object.defineProperty(modelClass.prototype, methodName, {
|
|
659
|
+
value: methodLogic,
|
|
660
|
+
writable: true,
|
|
661
|
+
enumerable: false,
|
|
662
|
+
// Keep it clean, like other prototype methods
|
|
663
|
+
configurable: true
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
clearSessionState() {
|
|
667
|
+
this.activeSessionModels = null;
|
|
668
|
+
Logger.debug(
|
|
669
|
+
"[ModelRegistry] Session state cleared (activeSessionModels reset)."
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
getActiveModels() {
|
|
673
|
+
return this.activeSessionModels || this.models;
|
|
674
|
+
}
|
|
675
|
+
validateSessionModels() {
|
|
676
|
+
const activeModels = this.getActiveModels();
|
|
677
|
+
if (!activeModels || activeModels.size === 0) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
for (const [modelName, modelClass] of activeModels.entries()) {
|
|
681
|
+
const modelSchema = modelClass.getSchema ? modelClass.getSchema() : null;
|
|
682
|
+
const relationships = modelSchema?.options?.relationships;
|
|
683
|
+
if (relationships) {
|
|
684
|
+
for (const relName in relationships) {
|
|
685
|
+
const config = relationships[relName];
|
|
686
|
+
const targetModelName = config.model;
|
|
687
|
+
if (!activeModels.has(targetModelName)) {
|
|
688
|
+
throw new Error(
|
|
689
|
+
`[ModelRegistry] Validation Error: Model "${modelName}" has a relationship "${relName}" that refers to model "${targetModelName}", but "${targetModelName}" is not active in the current session. Please include it in the 'models' array during initialization.`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if (config.type === "hasManyThrough") {
|
|
693
|
+
const joinModelName = config.joinModel;
|
|
694
|
+
if (!activeModels.has(joinModelName)) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
`[ModelRegistry] Validation Error: Model "${modelName}" has a relationship "${relName}" that uses join model "${joinModelName}", but "${joinModelName}" is not active in the current session. Please include it in the 'models' array during initialization.`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
export {
|
|
707
|
+
ModelRegistry
|
|
708
|
+
};
|
|
709
|
+
//# sourceMappingURL=chunk-DANSD6BE.js.map
|