js-bao 0.5.0 → 0.5.2
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 +124 -30
- package/dist/browser.cjs +759 -273
- package/dist/browser.d.cts +62 -0
- package/dist/browser.d.ts +62 -0
- package/dist/browser.js +760 -271
- package/dist/client.cjs +53 -6
- package/dist/client.d.cts +149 -0
- package/dist/client.d.ts +149 -0
- package/dist/client.js +49 -4
- package/dist/cloudflare-do.cjs +643 -231
- package/dist/cloudflare-do.d.cts +237 -1
- package/dist/cloudflare-do.d.ts +237 -1
- package/dist/cloudflare-do.js +639 -229
- package/dist/cloudflare.cjs +330 -26
- package/dist/cloudflare.d.cts +360 -1
- package/dist/cloudflare.d.ts +360 -1
- package/dist/cloudflare.js +326 -24
- package/dist/codegen-v2.cjs +139 -21
- package/dist/codegen.cjs +7 -1
- package/dist/index.cjs +724 -232
- package/dist/index.d.cts +98 -1
- package/dist/index.d.ts +98 -1
- package/dist/index.js +721 -229
- package/dist/node.cjs +948 -299
- package/dist/node.d.cts +220 -1
- package/dist/node.d.ts +220 -1
- package/dist/node.js +934 -296
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -344,21 +350,36 @@ var init_CursorManager = __esm({
|
|
|
344
350
|
return fields;
|
|
345
351
|
}
|
|
346
352
|
/**
|
|
347
|
-
* Build ORDER BY clause from sort specification
|
|
353
|
+
* Build ORDER BY clause from sort specification.
|
|
354
|
+
*
|
|
355
|
+
* `direction` is the pagination direction (1 = forward, -1 = backward).
|
|
356
|
+
* For backward pagination each field's effective ORDER BY direction is
|
|
357
|
+
* reversed so the cursor's flipped inequality (built by
|
|
358
|
+
* `buildPaginationConditions`) returns the page adjacent to the cursor,
|
|
359
|
+
* not the globally smallest/largest window. Without this,
|
|
360
|
+
* `field < cursor ... ORDER BY field ASC LIMIT n` returns the n smallest
|
|
361
|
+
* rows overall instead of the n rows just below the cursor.
|
|
362
|
+
*
|
|
363
|
+
* The returned `directions` array carries the *declared* per-field sort
|
|
364
|
+
* directions (unaffected by `direction`) — `buildPaginationConditions`
|
|
365
|
+
* needs the declared directions to pick each field's comparison operator.
|
|
348
366
|
*/
|
|
349
|
-
static buildOrderClause(sort, fieldFormatter = (field) => field) {
|
|
367
|
+
static buildOrderClause(sort, fieldFormatter = (field) => field, direction = 1) {
|
|
350
368
|
const fields = this.extractSortFields(sort);
|
|
351
369
|
const clauses = [];
|
|
352
370
|
const directions = [];
|
|
371
|
+
const sqlDir = (declared) => {
|
|
372
|
+
const effective = direction === -1 ? declared === 1 ? -1 : 1 : declared;
|
|
373
|
+
return effective === 1 ? "ASC" : "DESC";
|
|
374
|
+
};
|
|
353
375
|
if (sort && Object.keys(sort).length > 0) {
|
|
354
376
|
for (const [field, dir] of Object.entries(sort)) {
|
|
355
|
-
|
|
356
|
-
clauses.push(`${fieldFormatter(field)} ${sqlDirection}`);
|
|
377
|
+
clauses.push(`${fieldFormatter(field)} ${sqlDir(dir)}`);
|
|
357
378
|
directions.push(dir);
|
|
358
379
|
}
|
|
359
380
|
}
|
|
360
381
|
if (!sort || !sort.hasOwnProperty("id")) {
|
|
361
|
-
clauses.push(`${fieldFormatter("id")}
|
|
382
|
+
clauses.push(`${fieldFormatter("id")} ${sqlDir(1)}`);
|
|
362
383
|
directions.push(1);
|
|
363
384
|
} else if (sort && sort.hasOwnProperty("id")) {
|
|
364
385
|
directions.push(sort["id"]);
|
|
@@ -381,18 +402,27 @@ var init_CursorManager = __esm({
|
|
|
381
402
|
/**
|
|
382
403
|
* Generate next and previous cursors from result set
|
|
383
404
|
*/
|
|
384
|
-
static generateResultCursors(results, sortFields,
|
|
405
|
+
static generateResultCursors(results, sortFields, requestedDirection, hasMore, isFirstPage = false) {
|
|
385
406
|
if (results.length === 0) {
|
|
386
407
|
return {};
|
|
387
408
|
}
|
|
388
409
|
const cursors = {};
|
|
389
410
|
if (hasMore && results.length > 0) {
|
|
390
411
|
const lastResult = results[results.length - 1];
|
|
391
|
-
cursors.nextCursor = this.generateCursor(
|
|
412
|
+
cursors.nextCursor = this.generateCursor(
|
|
413
|
+
lastResult,
|
|
414
|
+
sortFields,
|
|
415
|
+
requestedDirection
|
|
416
|
+
);
|
|
392
417
|
}
|
|
393
418
|
if (results.length > 0 && !isFirstPage) {
|
|
394
419
|
const firstResult = results[0];
|
|
395
|
-
|
|
420
|
+
const opposite = requestedDirection === 1 ? -1 : 1;
|
|
421
|
+
cursors.prevCursor = this.generateCursor(
|
|
422
|
+
firstResult,
|
|
423
|
+
sortFields,
|
|
424
|
+
opposite
|
|
425
|
+
);
|
|
396
426
|
}
|
|
397
427
|
return cursors;
|
|
398
428
|
}
|
|
@@ -525,7 +555,8 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
525
555
|
const whereClause = this.translateFilter(filter);
|
|
526
556
|
const orderClause = CursorManager.buildOrderClause(
|
|
527
557
|
options?.sort,
|
|
528
|
-
(field) => this.getQuotedField(field)
|
|
558
|
+
(field) => this.getQuotedField(field),
|
|
559
|
+
options?.direction || 1
|
|
529
560
|
);
|
|
530
561
|
const limitClause = this.buildLimitClause(options);
|
|
531
562
|
const paginationClause = this.buildPaginationClause(
|
|
@@ -1161,10 +1192,109 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
1161
1192
|
}
|
|
1162
1193
|
});
|
|
1163
1194
|
|
|
1164
|
-
// src/
|
|
1195
|
+
// src/utils/yjsValues.ts
|
|
1165
1196
|
import * as Y from "yjs";
|
|
1197
|
+
function isYjsComposite(value) {
|
|
1198
|
+
return value instanceof Y.Map || value instanceof Y.Array || value instanceof Y.Text;
|
|
1199
|
+
}
|
|
1200
|
+
function isStringSetYMap(value) {
|
|
1201
|
+
if (!(value instanceof Y.Map)) return false;
|
|
1202
|
+
for (const v of value.values()) {
|
|
1203
|
+
if (v !== true) return false;
|
|
1204
|
+
}
|
|
1205
|
+
return true;
|
|
1206
|
+
}
|
|
1207
|
+
function normalizeYjsValue(value) {
|
|
1208
|
+
if (value instanceof Y.Text) {
|
|
1209
|
+
return value.toString();
|
|
1210
|
+
}
|
|
1211
|
+
if (value instanceof Y.Map || value instanceof Y.Array) {
|
|
1212
|
+
return value.toJSON();
|
|
1213
|
+
}
|
|
1214
|
+
return value;
|
|
1215
|
+
}
|
|
1216
|
+
function normalizeYjsValueForStorage(value) {
|
|
1217
|
+
if (value instanceof Y.Text) {
|
|
1218
|
+
return value.toString();
|
|
1219
|
+
}
|
|
1220
|
+
if (value instanceof Y.Map || value instanceof Y.Array) {
|
|
1221
|
+
return JSON.stringify(value.toJSON());
|
|
1222
|
+
}
|
|
1223
|
+
return value;
|
|
1224
|
+
}
|
|
1225
|
+
var init_yjsValues = __esm({
|
|
1226
|
+
"src/utils/yjsValues.ts"() {
|
|
1227
|
+
"use strict";
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
// src/models/writeLifecycle.ts
|
|
1232
|
+
import * as Y2 from "yjs";
|
|
1233
|
+
import { ulid } from "ulid";
|
|
1234
|
+
function ensureStringSetYMap(rm, fieldName) {
|
|
1235
|
+
const nested = rm.get(fieldName);
|
|
1236
|
+
if (nested instanceof Y2.Map) return nested;
|
|
1237
|
+
const fresh = new Y2.Map();
|
|
1238
|
+
if (nested && typeof nested === "object") {
|
|
1239
|
+
for (const [member, marker] of Object.entries(
|
|
1240
|
+
nested
|
|
1241
|
+
)) {
|
|
1242
|
+
if (Boolean(marker)) fresh.set(member, true);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
rm.set(fieldName, fresh);
|
|
1246
|
+
return fresh;
|
|
1247
|
+
}
|
|
1248
|
+
function applyStringSetDelta(target, delta) {
|
|
1249
|
+
if (delta.clear) {
|
|
1250
|
+
for (const member of Array.from(target.keys())) target.delete(member);
|
|
1251
|
+
}
|
|
1252
|
+
if (delta.remove) {
|
|
1253
|
+
for (const member of delta.remove) target.delete(String(member));
|
|
1254
|
+
}
|
|
1255
|
+
if (delta.add) {
|
|
1256
|
+
for (const member of delta.add) {
|
|
1257
|
+
const key = String(member);
|
|
1258
|
+
if (target.get(key) !== true) target.set(key, true);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function applyStringSetChange(recordYMap, fieldName, change) {
|
|
1263
|
+
const target = ensureStringSetYMap(recordYMap, fieldName);
|
|
1264
|
+
applyStringSetDelta(target, {
|
|
1265
|
+
add: change.additions,
|
|
1266
|
+
remove: change.removals
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
function registerFunctionDefaultGenerator(name, fn) {
|
|
1270
|
+
FUNCTION_DEFAULT_GENERATORS.set(name, fn);
|
|
1271
|
+
}
|
|
1272
|
+
function resolveLiteralOrFunctionDefault(rawDefault) {
|
|
1273
|
+
return typeof rawDefault === "function" ? rawDefault() : rawDefault;
|
|
1274
|
+
}
|
|
1275
|
+
var KNOWN_FIELD_TYPE_MAP, KNOWN_FIELD_TYPES, FUNCTION_DEFAULT_GENERATORS, UNKNOWN_FUNCTION_DEFAULT;
|
|
1276
|
+
var init_writeLifecycle = __esm({
|
|
1277
|
+
"src/models/writeLifecycle.ts"() {
|
|
1278
|
+
"use strict";
|
|
1279
|
+
KNOWN_FIELD_TYPE_MAP = {
|
|
1280
|
+
string: true,
|
|
1281
|
+
number: true,
|
|
1282
|
+
boolean: true,
|
|
1283
|
+
date: true,
|
|
1284
|
+
id: true,
|
|
1285
|
+
stringset: true
|
|
1286
|
+
};
|
|
1287
|
+
KNOWN_FIELD_TYPES = new Set(Object.keys(KNOWN_FIELD_TYPE_MAP));
|
|
1288
|
+
FUNCTION_DEFAULT_GENERATORS = /* @__PURE__ */ new Map();
|
|
1289
|
+
registerFunctionDefaultGenerator("generate_ulid", () => ulid());
|
|
1290
|
+
UNKNOWN_FUNCTION_DEFAULT = "$unknown_function";
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
// src/models/metaSync.ts
|
|
1295
|
+
import * as Y3 from "yjs";
|
|
1166
1296
|
function inferFieldType(value) {
|
|
1167
|
-
if (value
|
|
1297
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
1168
1298
|
switch (typeof value) {
|
|
1169
1299
|
case "string":
|
|
1170
1300
|
return "string";
|
|
@@ -1183,7 +1313,10 @@ function encodeDefault(value) {
|
|
|
1183
1313
|
if (value === void 0 || value === null) return void 0;
|
|
1184
1314
|
if (typeof value === "function") {
|
|
1185
1315
|
const name = KNOWN_FUNCTION_DEFAULTS.get(value);
|
|
1186
|
-
return name ? `$${name}` :
|
|
1316
|
+
return name ? `$${name}` : UNKNOWN_FUNCTION_DEFAULT;
|
|
1317
|
+
}
|
|
1318
|
+
if (typeof value === "string" && value.startsWith("$")) {
|
|
1319
|
+
return `$${value}`;
|
|
1187
1320
|
}
|
|
1188
1321
|
return value;
|
|
1189
1322
|
}
|
|
@@ -1209,7 +1342,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1209
1342
|
if (compoundConstraints.length > 0) {
|
|
1210
1343
|
let constraints = meta.get("_constraints");
|
|
1211
1344
|
if (!constraints) {
|
|
1212
|
-
constraints = new
|
|
1345
|
+
constraints = new Y3.Map();
|
|
1213
1346
|
meta.set("_constraints", constraints);
|
|
1214
1347
|
}
|
|
1215
1348
|
for (const constraint of compoundConstraints) {
|
|
@@ -1220,7 +1353,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1220
1353
|
if (relationships && Object.keys(relationships).length > 0) {
|
|
1221
1354
|
let rels = meta.get("_relationships");
|
|
1222
1355
|
if (!rels) {
|
|
1223
|
-
rels = new
|
|
1356
|
+
rels = new Y3.Map();
|
|
1224
1357
|
meta.set("_relationships", rels);
|
|
1225
1358
|
}
|
|
1226
1359
|
for (const [relName, relConfig] of Object.entries(relationships)) {
|
|
@@ -1232,7 +1365,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1232
1365
|
function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
1233
1366
|
let fieldMeta = metaMap.get(fieldName);
|
|
1234
1367
|
if (!fieldMeta) {
|
|
1235
|
-
fieldMeta = new
|
|
1368
|
+
fieldMeta = new Y3.Map();
|
|
1236
1369
|
metaMap.set(fieldName, fieldMeta);
|
|
1237
1370
|
}
|
|
1238
1371
|
setIfChanged(fieldMeta, "type", fieldOpts.type);
|
|
@@ -1240,6 +1373,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1240
1373
|
if (fieldOpts.unique) setIfChanged(fieldMeta, "unique", true);
|
|
1241
1374
|
if (fieldOpts.required) setIfChanged(fieldMeta, "required", true);
|
|
1242
1375
|
if (fieldOpts.autoAssign) setIfChanged(fieldMeta, "autoAssign", true);
|
|
1376
|
+
if (fieldOpts.autoStamp) setIfChanged(fieldMeta, "autoStamp", fieldOpts.autoStamp);
|
|
1243
1377
|
if (fieldOpts.maxLength !== void 0) setIfChanged(fieldMeta, "maxLength", fieldOpts.maxLength);
|
|
1244
1378
|
if (fieldOpts.maxCount !== void 0) setIfChanged(fieldMeta, "maxCount", fieldOpts.maxCount);
|
|
1245
1379
|
const encoded = encodeDefault(fieldOpts.default);
|
|
@@ -1248,7 +1382,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1248
1382
|
function syncConstraintMeta(constraintsMap, constraint) {
|
|
1249
1383
|
let cMeta = constraintsMap.get(constraint.name);
|
|
1250
1384
|
if (!cMeta) {
|
|
1251
|
-
cMeta = new
|
|
1385
|
+
cMeta = new Y3.Map();
|
|
1252
1386
|
constraintsMap.set(constraint.name, cMeta);
|
|
1253
1387
|
}
|
|
1254
1388
|
setIfChanged(cMeta, "type", "unique");
|
|
@@ -1258,7 +1392,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
|
|
|
1258
1392
|
function syncRelationshipMeta(relsMap, relName, relConfig) {
|
|
1259
1393
|
let relMeta = relsMap.get(relName);
|
|
1260
1394
|
if (!relMeta) {
|
|
1261
|
-
relMeta = new
|
|
1395
|
+
relMeta = new Y3.Map();
|
|
1262
1396
|
relsMap.set(relName, relMeta);
|
|
1263
1397
|
}
|
|
1264
1398
|
for (const [key, value] of Object.entries(relConfig)) {
|
|
@@ -1273,7 +1407,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
|
|
|
1273
1407
|
if (fieldName.startsWith("_")) continue;
|
|
1274
1408
|
let fieldMeta = meta.get(fieldName);
|
|
1275
1409
|
if (!fieldMeta) {
|
|
1276
|
-
fieldMeta = new
|
|
1410
|
+
fieldMeta = new Y3.Map();
|
|
1277
1411
|
meta.set(fieldName, fieldMeta);
|
|
1278
1412
|
}
|
|
1279
1413
|
if (!fieldMeta.has("type")) {
|
|
@@ -1293,11 +1427,38 @@ var KNOWN_FUNCTION_DEFAULTS, _syncedCache;
|
|
|
1293
1427
|
var init_metaSync = __esm({
|
|
1294
1428
|
"src/models/metaSync.ts"() {
|
|
1295
1429
|
"use strict";
|
|
1430
|
+
init_yjsValues();
|
|
1431
|
+
init_writeLifecycle();
|
|
1296
1432
|
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
1297
1433
|
_syncedCache = /* @__PURE__ */ new WeakMap();
|
|
1298
1434
|
}
|
|
1299
1435
|
});
|
|
1300
1436
|
|
|
1437
|
+
// src/utils/uniqueIndex.ts
|
|
1438
|
+
import * as Y4 from "yjs";
|
|
1439
|
+
function buildUniqueKey(recordData, fields) {
|
|
1440
|
+
const keyParts = [];
|
|
1441
|
+
for (const field of fields) {
|
|
1442
|
+
const value = recordData instanceof Y4.Map ? recordData.get(field) : recordData[field];
|
|
1443
|
+
if (value === null || value === void 0) {
|
|
1444
|
+
return null;
|
|
1445
|
+
}
|
|
1446
|
+
keyParts.push(String(value));
|
|
1447
|
+
}
|
|
1448
|
+
return keyParts.join("|");
|
|
1449
|
+
}
|
|
1450
|
+
function uniqueIndexMapName(modelName, constraintName) {
|
|
1451
|
+
return `_uniqueIdx_${modelName}_${constraintName}`;
|
|
1452
|
+
}
|
|
1453
|
+
function fieldUniqueConstraintName(modelName, fieldName) {
|
|
1454
|
+
return `${modelName}_${fieldName}_unique`;
|
|
1455
|
+
}
|
|
1456
|
+
var init_uniqueIndex = __esm({
|
|
1457
|
+
"src/utils/uniqueIndex.ts"() {
|
|
1458
|
+
"use strict";
|
|
1459
|
+
}
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1301
1462
|
// src/query/IncludeResolver.ts
|
|
1302
1463
|
var IncludeResolver_exports = {};
|
|
1303
1464
|
__export(IncludeResolver_exports, {
|
|
@@ -1656,7 +1817,13 @@ var init_IncludeResolver = __esm({
|
|
|
1656
1817
|
}
|
|
1657
1818
|
return null;
|
|
1658
1819
|
}
|
|
1659
|
-
/**
|
|
1820
|
+
/**
|
|
1821
|
+
* Apply a projection to a single record. Assumes the projection was already
|
|
1822
|
+
* validated upstream (mixed inclusion+exclusion rejected by the translator):
|
|
1823
|
+
* `isInclusion` treats "any value === 1" as inclusion mode. `id` and `type`
|
|
1824
|
+
* are system-identity fields and are always kept in BOTH modes (mirrors the
|
|
1825
|
+
* DatabaseDO `_applyProjection`); `_meta` stays excludable.
|
|
1826
|
+
*/
|
|
1660
1827
|
_applyProjection(record, projection) {
|
|
1661
1828
|
const fields = Object.entries(projection);
|
|
1662
1829
|
if (fields.length === 0) return record;
|
|
@@ -1675,7 +1842,7 @@ var init_IncludeResolver = __esm({
|
|
|
1675
1842
|
} else {
|
|
1676
1843
|
const result = { ...record };
|
|
1677
1844
|
for (const [f, v] of fields) {
|
|
1678
|
-
if (v === 0) delete result[f];
|
|
1845
|
+
if (v === 0 && f !== "id" && f !== "type") delete result[f];
|
|
1679
1846
|
}
|
|
1680
1847
|
return result;
|
|
1681
1848
|
}
|
|
@@ -1694,10 +1861,10 @@ __export(BaseModel_exports, {
|
|
|
1694
1861
|
UniqueConstraintViolationError: () => UniqueConstraintViolationError,
|
|
1695
1862
|
generateULID: () => generateULID
|
|
1696
1863
|
});
|
|
1697
|
-
import * as
|
|
1698
|
-
import { ulid } from "ulid";
|
|
1864
|
+
import * as Y5 from "yjs";
|
|
1865
|
+
import { ulid as ulid2 } from "ulid";
|
|
1699
1866
|
function generateULID() {
|
|
1700
|
-
return
|
|
1867
|
+
return ulid2();
|
|
1701
1868
|
}
|
|
1702
1869
|
var LogLevel, Logger, UniqueConstraintViolationError, RecordNotFoundError, SCHEMA_ACCESSORS_KEY, BaseModel;
|
|
1703
1870
|
var init_BaseModel = __esm({
|
|
@@ -1709,6 +1876,9 @@ var init_BaseModel = __esm({
|
|
|
1709
1876
|
init_CursorManager();
|
|
1710
1877
|
init_sql();
|
|
1711
1878
|
init_metaSync();
|
|
1879
|
+
init_writeLifecycle();
|
|
1880
|
+
init_yjsValues();
|
|
1881
|
+
init_uniqueIndex();
|
|
1712
1882
|
LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
1713
1883
|
LogLevel2[LogLevel2["SILENT"] = 0] = "SILENT";
|
|
1714
1884
|
LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
|
|
@@ -2093,7 +2263,9 @@ var init_BaseModel = __esm({
|
|
|
2093
2263
|
this.setValue(fieldKey, data[fieldKey]);
|
|
2094
2264
|
this._constructorProvidedFields.add(fieldKey);
|
|
2095
2265
|
} else if (fieldOptions.default !== void 0) {
|
|
2096
|
-
const defaultValue =
|
|
2266
|
+
const defaultValue = resolveLiteralOrFunctionDefault(
|
|
2267
|
+
fieldOptions.default
|
|
2268
|
+
);
|
|
2097
2269
|
this.setValue(fieldKey, defaultValue);
|
|
2098
2270
|
}
|
|
2099
2271
|
}
|
|
@@ -2217,7 +2389,30 @@ var init_BaseModel = __esm({
|
|
|
2217
2389
|
if (verboseEnabled) {
|
|
2218
2390
|
Logger.verbose(`[getFromYjs] Returning field value: ${fieldValue}`);
|
|
2219
2391
|
}
|
|
2220
|
-
return fieldValue;
|
|
2392
|
+
return this.normalizeNonStringSetValue(fieldKey, fieldValue, schema, modelName);
|
|
2393
|
+
}
|
|
2394
|
+
/**
|
|
2395
|
+
* Defensive normalization for the read/sync boundary (issue #625).
|
|
2396
|
+
*
|
|
2397
|
+
* Stringset fields are owned by getStringSetFromYjs (which expects the
|
|
2398
|
+
* raw Y.Map / legacy plain-object shape — #561 / #601), so they pass
|
|
2399
|
+
* through untouched. For any other field, a composite Yjs primitive
|
|
2400
|
+
* (Y.Map / Y.Array / Y.Text) must never reach user code or the DB layer
|
|
2401
|
+
* raw: normalize it to its plain-JS projection (toJSON / toString) and
|
|
2402
|
+
* warn, rather than throwing. Scalars pass through unchanged.
|
|
2403
|
+
*/
|
|
2404
|
+
normalizeNonStringSetValue(fieldKey, value, schema, modelName) {
|
|
2405
|
+
const fieldOptions = schema?.fields?.get(fieldKey);
|
|
2406
|
+
if (fieldOptions?.type === "stringset") {
|
|
2407
|
+
return value;
|
|
2408
|
+
}
|
|
2409
|
+
if (isYjsComposite(value)) {
|
|
2410
|
+
Logger.warn(
|
|
2411
|
+
`[${modelName ?? this.constructor.name}] Field '${fieldKey}' holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions?.type ?? "unknown"}'. Normalizing to plain JS \u2014 the writer disagreed with the schema.`
|
|
2412
|
+
);
|
|
2413
|
+
return normalizeYjsValue(value);
|
|
2414
|
+
}
|
|
2415
|
+
return value;
|
|
2221
2416
|
}
|
|
2222
2417
|
getValue(fieldKey) {
|
|
2223
2418
|
const verboseEnabled = Logger.getLogLevel() >= 5 /* VERBOSE */;
|
|
@@ -2362,7 +2557,7 @@ var init_BaseModel = __esm({
|
|
|
2362
2557
|
}
|
|
2363
2558
|
getStringSetFromYjs(fieldName) {
|
|
2364
2559
|
const yjsData = this.getFromYjs(fieldName);
|
|
2365
|
-
if (yjsData instanceof
|
|
2560
|
+
if (yjsData instanceof Y5.Map) {
|
|
2366
2561
|
return Array.from(yjsData.keys());
|
|
2367
2562
|
}
|
|
2368
2563
|
if (yjsData && typeof yjsData === "object") {
|
|
@@ -2470,7 +2665,7 @@ var init_BaseModel = __esm({
|
|
|
2470
2665
|
}
|
|
2471
2666
|
const extractStringSetValues = (value) => {
|
|
2472
2667
|
if (!value) return [];
|
|
2473
|
-
if (value instanceof
|
|
2668
|
+
if (value instanceof Y5.Map) {
|
|
2474
2669
|
return Array.from(value.entries()).filter(([, isMember]) => Boolean(isMember)).map(([key]) => key);
|
|
2475
2670
|
}
|
|
2476
2671
|
if (Array.isArray(value)) {
|
|
@@ -2487,7 +2682,7 @@ var init_BaseModel = __esm({
|
|
|
2487
2682
|
if (!recordId) continue;
|
|
2488
2683
|
let itemData;
|
|
2489
2684
|
const stringSetValuesByField = {};
|
|
2490
|
-
if (recordData instanceof
|
|
2685
|
+
if (recordData instanceof Y5.Map) {
|
|
2491
2686
|
itemData = {};
|
|
2492
2687
|
const unknownFields = [];
|
|
2493
2688
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -2603,20 +2798,10 @@ var init_BaseModel = __esm({
|
|
|
2603
2798
|
Logger.verbose(
|
|
2604
2799
|
`[${this.name}] Setting up YMap observer for document ${docId}/${modelName}...`
|
|
2605
2800
|
);
|
|
2606
|
-
const
|
|
2607
|
-
const keyParts = [];
|
|
2608
|
-
for (const field of fields) {
|
|
2609
|
-
const value = recordData instanceof Y2.Map ? recordData.get(field) : recordData[field];
|
|
2610
|
-
if (value === null || value === void 0) {
|
|
2611
|
-
return null;
|
|
2612
|
-
}
|
|
2613
|
-
keyParts.push(String(value));
|
|
2614
|
-
}
|
|
2615
|
-
return keyParts.join("|");
|
|
2616
|
-
};
|
|
2801
|
+
const buildUniqueKey2 = (recordData, fields) => buildUniqueKey(recordData, fields);
|
|
2617
2802
|
const extractItemData = (key, recordData) => {
|
|
2618
2803
|
let itemData;
|
|
2619
|
-
if (recordData instanceof
|
|
2804
|
+
if (recordData instanceof Y5.Map) {
|
|
2620
2805
|
itemData = {};
|
|
2621
2806
|
const unknownFields = [];
|
|
2622
2807
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -2629,7 +2814,14 @@ var init_BaseModel = __esm({
|
|
|
2629
2814
|
continue;
|
|
2630
2815
|
}
|
|
2631
2816
|
if (value !== void 0) {
|
|
2632
|
-
|
|
2817
|
+
if (isYjsComposite(value)) {
|
|
2818
|
+
Logger.warn(
|
|
2819
|
+
`[${this.name}] Field '${fieldKey}' on record ${key} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
2820
|
+
);
|
|
2821
|
+
itemData[fieldKey] = normalizeYjsValueForStorage(value);
|
|
2822
|
+
} else {
|
|
2823
|
+
itemData[fieldKey] = value;
|
|
2824
|
+
}
|
|
2633
2825
|
}
|
|
2634
2826
|
}
|
|
2635
2827
|
if (unknownFields.length > 0) {
|
|
@@ -2652,7 +2844,14 @@ var init_BaseModel = __esm({
|
|
|
2652
2844
|
continue;
|
|
2653
2845
|
}
|
|
2654
2846
|
if (value !== void 0) {
|
|
2655
|
-
|
|
2847
|
+
if (isYjsComposite(value)) {
|
|
2848
|
+
Logger.warn(
|
|
2849
|
+
`[${this.name}] Field '${fieldKey}' on legacy record ${key} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
2850
|
+
);
|
|
2851
|
+
filteredData[fieldKey] = normalizeYjsValueForStorage(value);
|
|
2852
|
+
} else {
|
|
2853
|
+
filteredData[fieldKey] = value;
|
|
2854
|
+
}
|
|
2656
2855
|
}
|
|
2657
2856
|
}
|
|
2658
2857
|
if (unknownFields.length > 0) {
|
|
@@ -2686,7 +2885,7 @@ var init_BaseModel = __esm({
|
|
|
2686
2885
|
const recordsByUniqueKey = /* @__PURE__ */ new Map();
|
|
2687
2886
|
for (const [recordId, recordData] of allRecords.entries()) {
|
|
2688
2887
|
if (recordIdsToDiscard.has(recordId)) continue;
|
|
2689
|
-
const uniqueKey =
|
|
2888
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2690
2889
|
if (uniqueKey === null) continue;
|
|
2691
2890
|
if (!recordsByUniqueKey.has(uniqueKey)) {
|
|
2692
2891
|
recordsByUniqueKey.set(uniqueKey, []);
|
|
@@ -2732,7 +2931,7 @@ var init_BaseModel = __esm({
|
|
|
2732
2931
|
if (!recordData || !key) continue;
|
|
2733
2932
|
const itemData = extractItemData(key, recordData);
|
|
2734
2933
|
if (!itemData) continue;
|
|
2735
|
-
if (change.action === "add" && recordData instanceof
|
|
2934
|
+
if (change.action === "add" && recordData instanceof Y5.Map) {
|
|
2736
2935
|
Logger.verbose(
|
|
2737
2936
|
`[${this.name}] Setting up observer on newly added nested YMap for record ${key} in document ${docId}`
|
|
2738
2937
|
);
|
|
@@ -2830,9 +3029,12 @@ var init_BaseModel = __esm({
|
|
|
2830
3029
|
const recordData = documentYMap.get(idToDiscard);
|
|
2831
3030
|
if (!recordData) continue;
|
|
2832
3031
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
2833
|
-
const uniqueKey =
|
|
3032
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2834
3033
|
if (uniqueKey === null) continue;
|
|
2835
|
-
const constraintMapName =
|
|
3034
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3035
|
+
modelName,
|
|
3036
|
+
constraint.name
|
|
3037
|
+
);
|
|
2836
3038
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
2837
3039
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
2838
3040
|
if (currentIndexValue === idToDiscard) {
|
|
@@ -2850,11 +3052,14 @@ var init_BaseModel = __esm({
|
|
|
2850
3052
|
}, `conflict-resolution-${modelName}-${docId}`);
|
|
2851
3053
|
yDoc.transact(() => {
|
|
2852
3054
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
2853
|
-
const constraintMapName =
|
|
3055
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3056
|
+
modelName,
|
|
3057
|
+
constraint.name
|
|
3058
|
+
);
|
|
2854
3059
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
2855
3060
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
2856
3061
|
if (!recordData) continue;
|
|
2857
|
-
const uniqueKey =
|
|
3062
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2858
3063
|
if (uniqueKey === null) continue;
|
|
2859
3064
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
2860
3065
|
if (currentIndexValue !== recordId) {
|
|
@@ -2877,7 +3082,7 @@ var init_BaseModel = __esm({
|
|
|
2877
3082
|
`[${this.name}] Setting up observers on existing nested YMaps for ${modelName}/${docId}...`
|
|
2878
3083
|
);
|
|
2879
3084
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
2880
|
-
if (recordData instanceof
|
|
3085
|
+
if (recordData instanceof Y5.Map) {
|
|
2881
3086
|
Logger.verbose(
|
|
2882
3087
|
`[${this.name}] Setting up observer on existing nested YMap for record ${recordId} in document ${docId}`
|
|
2883
3088
|
);
|
|
@@ -3062,6 +3267,11 @@ var init_BaseModel = __esm({
|
|
|
3062
3267
|
Logger.debug(
|
|
3063
3268
|
`[_diffWithYjsData] Field '${key}' - Y.js value: ${yjsValue}, local value: ${localValue}`
|
|
3064
3269
|
);
|
|
3270
|
+
if (isYjsComposite(yjsValue)) {
|
|
3271
|
+
Logger.warn(
|
|
3272
|
+
`[${modelConstructor.name}] Field '${key}' holds a composite Yjs value (${yjsValue?.constructor?.name}) in a non-stringset slot; the local scalar change will overwrite it (last-writer-wins).`
|
|
3273
|
+
);
|
|
3274
|
+
}
|
|
3065
3275
|
if (yjsValue === void 0) {
|
|
3066
3276
|
Logger.debug(
|
|
3067
3277
|
`[_diffWithYjsData] Field '${key}' not in Y.js, adding to 'added'`
|
|
@@ -3098,26 +3308,7 @@ var init_BaseModel = __esm({
|
|
|
3098
3308
|
* if the field hasn't been touched since the wire-format change in #561.
|
|
3099
3309
|
*/
|
|
3100
3310
|
applyStringSetChangeToYMap(recordYMap, fieldName, change) {
|
|
3101
|
-
|
|
3102
|
-
if (!(nested instanceof Y2.Map)) {
|
|
3103
|
-
const migrated = new Y2.Map();
|
|
3104
|
-
if (nested && typeof nested === "object") {
|
|
3105
|
-
for (const [member, marker] of Object.entries(
|
|
3106
|
-
nested
|
|
3107
|
-
)) {
|
|
3108
|
-
if (Boolean(marker)) migrated.set(member, true);
|
|
3109
|
-
}
|
|
3110
|
-
}
|
|
3111
|
-
recordYMap.set(fieldName, migrated);
|
|
3112
|
-
nested = migrated;
|
|
3113
|
-
}
|
|
3114
|
-
const target = nested;
|
|
3115
|
-
for (const member of change.additions) {
|
|
3116
|
-
target.set(member, true);
|
|
3117
|
-
}
|
|
3118
|
-
for (const member of change.removals) {
|
|
3119
|
-
target.delete(member);
|
|
3120
|
-
}
|
|
3311
|
+
applyStringSetChange(recordYMap, fieldName, change);
|
|
3121
3312
|
}
|
|
3122
3313
|
/**
|
|
3123
3314
|
* Deep equality check for comparing field values
|
|
@@ -3287,6 +3478,29 @@ var init_BaseModel = __esm({
|
|
|
3287
3478
|
);
|
|
3288
3479
|
throw new Error("Cannot save item without an id. Ensure id is set.");
|
|
3289
3480
|
}
|
|
3481
|
+
const preTransactCreateStamps = /* @__PURE__ */ new Set();
|
|
3482
|
+
{
|
|
3483
|
+
const existingRecord = targetYMap.get(this.id);
|
|
3484
|
+
const isNew = !existingRecord;
|
|
3485
|
+
for (const [fieldKey, fieldOptions] of schema.fields.entries()) {
|
|
3486
|
+
const stamp = fieldOptions.autoStamp;
|
|
3487
|
+
if (!stamp) continue;
|
|
3488
|
+
const callerExplicit = this.hasLocalChange(fieldKey);
|
|
3489
|
+
if (callerExplicit) {
|
|
3490
|
+
const localValue = this._localChanges[fieldKey];
|
|
3491
|
+
if (localValue !== null && localValue !== void 0) continue;
|
|
3492
|
+
}
|
|
3493
|
+
if (stamp === "create") {
|
|
3494
|
+
if (!isNew) continue;
|
|
3495
|
+
const persisted = fieldKey === "id" ? this.id : this.getValue(fieldKey);
|
|
3496
|
+
if (persisted !== null && persisted !== void 0) continue;
|
|
3497
|
+
}
|
|
3498
|
+
this[fieldKey] = Date.now();
|
|
3499
|
+
if (stamp === "create") {
|
|
3500
|
+
preTransactCreateStamps.add(fieldKey);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3290
3504
|
this.validateBeforeSave();
|
|
3291
3505
|
Logger.debug(`[${modelName}.save] About to get current JS state`);
|
|
3292
3506
|
Logger.debug(`[${modelName}.save] _localChanges:`, this._localChanges);
|
|
@@ -3296,6 +3510,34 @@ var init_BaseModel = __esm({
|
|
|
3296
3510
|
`[${modelName}.save] getCurrentJSState() returned:`,
|
|
3297
3511
|
dataToSave
|
|
3298
3512
|
);
|
|
3513
|
+
const uniqueMissConflicts = /* @__PURE__ */ new Map();
|
|
3514
|
+
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
3515
|
+
const newUniqueKey = this._buildUniqueKey(
|
|
3516
|
+
constraint.fields,
|
|
3517
|
+
dataToSave,
|
|
3518
|
+
modelName,
|
|
3519
|
+
constraint.name
|
|
3520
|
+
);
|
|
3521
|
+
if (newUniqueKey === null) {
|
|
3522
|
+
continue;
|
|
3523
|
+
}
|
|
3524
|
+
const constraintMap = yDoc.getMap(
|
|
3525
|
+
uniqueIndexMapName(modelName, constraint.name)
|
|
3526
|
+
);
|
|
3527
|
+
if (!constraintMap.get(newUniqueKey)) {
|
|
3528
|
+
const constraintKeyValues = constraint.fields.map(
|
|
3529
|
+
(field) => dataToSave[field]
|
|
3530
|
+
);
|
|
3531
|
+
const conflictingRecord = await modelConstructor._resolveByUniqueFallback(
|
|
3532
|
+
constraint,
|
|
3533
|
+
constraintKeyValues,
|
|
3534
|
+
{ excludeId: this.id }
|
|
3535
|
+
);
|
|
3536
|
+
if (conflictingRecord) {
|
|
3537
|
+
uniqueMissConflicts.set(constraint.name, conflictingRecord.id);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3299
3541
|
await yDoc.transact(async () => {
|
|
3300
3542
|
if (upsertOnConstraint && upsertOnKey) {
|
|
3301
3543
|
const constraintMapName = `_uniqueIdx_${modelName}_${upsertOnConstraint.name}`;
|
|
@@ -3317,6 +3559,14 @@ var init_BaseModel = __esm({
|
|
|
3317
3559
|
}
|
|
3318
3560
|
}
|
|
3319
3561
|
}
|
|
3562
|
+
for (const fieldKey of preTransactCreateStamps) {
|
|
3563
|
+
if (this._localChanges && fieldKey in this._localChanges) {
|
|
3564
|
+
delete this._localChanges[fieldKey];
|
|
3565
|
+
}
|
|
3566
|
+
if (fieldKey in dataToSave) {
|
|
3567
|
+
delete dataToSave[fieldKey];
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3320
3570
|
}
|
|
3321
3571
|
}
|
|
3322
3572
|
if (!targetYMap) {
|
|
@@ -3329,7 +3579,7 @@ var init_BaseModel = __esm({
|
|
|
3329
3579
|
Logger.debug(`[${modelName}.save] isUpdate: ${isUpdate}`);
|
|
3330
3580
|
Logger.debug(`[${modelName}.save] recordYMap exists: ${!!recordYMap}`);
|
|
3331
3581
|
if (!recordYMap) {
|
|
3332
|
-
recordYMap = new
|
|
3582
|
+
recordYMap = new Y5.Map();
|
|
3333
3583
|
Logger.verbose(
|
|
3334
3584
|
`[${modelName}] Creating new nested YMap for record ${this.id} in document ${targetDocId || "legacy"}`
|
|
3335
3585
|
);
|
|
@@ -3381,7 +3631,10 @@ var init_BaseModel = __esm({
|
|
|
3381
3631
|
);
|
|
3382
3632
|
continue;
|
|
3383
3633
|
}
|
|
3384
|
-
const constraintMapName =
|
|
3634
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3635
|
+
modelName,
|
|
3636
|
+
constraint.name
|
|
3637
|
+
);
|
|
3385
3638
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
3386
3639
|
Logger.verbose(
|
|
3387
3640
|
`[${modelName}] Save Check (Item '${this.id}'): GET from '${constraintMapName}' with Key '${newUniqueKey.substring(
|
|
@@ -3410,6 +3663,26 @@ var init_BaseModel = __esm({
|
|
|
3410
3663
|
this.id,
|
|
3411
3664
|
existingRecordIdWithNewKey
|
|
3412
3665
|
);
|
|
3666
|
+
} else if (!existingRecordIdWithNewKey) {
|
|
3667
|
+
const conflictingRecordId = uniqueMissConflicts.get(constraint.name);
|
|
3668
|
+
if (conflictingRecordId && conflictingRecordId !== this.id) {
|
|
3669
|
+
Logger.verbose(
|
|
3670
|
+
`[${modelName}] Save Check (Item '${this.id}'): index miss for '${constraint.name}', but engine fallback found conflicting index-less record ${conflictingRecordId}.`
|
|
3671
|
+
);
|
|
3672
|
+
throw new UniqueConstraintViolationError(
|
|
3673
|
+
`Unique constraint '${constraint.name}' violated for model '${modelName}' on fields [${constraint.fields.join(
|
|
3674
|
+
", "
|
|
3675
|
+
)}]. Attempted ID: ${this.id}. Value(s) starting with '${newUniqueKey.substring(
|
|
3676
|
+
0,
|
|
3677
|
+
50
|
|
3678
|
+
)}...' already exist for index-less record ID ${conflictingRecordId}.`,
|
|
3679
|
+
modelName,
|
|
3680
|
+
constraint.name,
|
|
3681
|
+
constraint.fields,
|
|
3682
|
+
this.id,
|
|
3683
|
+
conflictingRecordId
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3413
3686
|
}
|
|
3414
3687
|
}
|
|
3415
3688
|
if (isUpdate && oldData) {
|
|
@@ -3753,34 +4026,46 @@ var init_BaseModel = __esm({
|
|
|
3753
4026
|
const modelName = schema.options.name;
|
|
3754
4027
|
Logger.verbose(`[${modelName}] Executing query:`, { filter, options });
|
|
3755
4028
|
const hasIncludes = options?.include && options.include.length > 0;
|
|
3756
|
-
const
|
|
4029
|
+
const requestedLimit = options?.limit;
|
|
4030
|
+
const overFetch = typeof requestedLimit === "number" && requestedLimit > 0;
|
|
4031
|
+
let translatorOptions = hasIncludes && options?.projection ? { ...options, projection: void 0 } : options;
|
|
4032
|
+
if (overFetch) {
|
|
4033
|
+
translatorOptions = {
|
|
4034
|
+
...translatorOptions || {},
|
|
4035
|
+
limit: requestedLimit + 1
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
3757
4038
|
const translator = new DocumentQueryTranslator(modelName, schema.fields);
|
|
3758
4039
|
const translatedQuery = translator.translateFind(filter, translatorOptions);
|
|
3759
4040
|
Logger.verbose(`[${modelName}] Translated SQL:`, translatedQuery.sql);
|
|
3760
4041
|
Logger.verbose(`[${modelName}] SQL params:`, translatedQuery.params);
|
|
3761
|
-
|
|
4042
|
+
let results = await _BaseModel.dbInstance.query(
|
|
3762
4043
|
translatedQuery.sql,
|
|
3763
4044
|
translatedQuery.params
|
|
3764
4045
|
);
|
|
3765
4046
|
Logger.verbose(`[${modelName}] Query returned ${results.length} results`);
|
|
4047
|
+
let hasMore = false;
|
|
4048
|
+
if (overFetch && results.length > requestedLimit) {
|
|
4049
|
+
hasMore = true;
|
|
4050
|
+
results = results.slice(0, requestedLimit);
|
|
4051
|
+
}
|
|
3766
4052
|
if (options?.include && options.include.length > 0) {
|
|
3767
4053
|
const { IncludeResolver: IncludeResolver2 } = await Promise.resolve().then(() => (init_IncludeResolver(), IncludeResolver_exports));
|
|
3768
4054
|
const resolver = new IncludeResolver2(_BaseModel.dbInstance);
|
|
3769
4055
|
await resolver.resolve(results, options.include, 0, modelName);
|
|
3770
4056
|
}
|
|
3771
|
-
const requestedLimit = options?.limit;
|
|
3772
|
-
const hasMore = CursorManager.hasMoreResults(
|
|
3773
|
-
requestedLimit,
|
|
3774
|
-
results.length
|
|
3775
|
-
);
|
|
3776
4057
|
const isFirstPage = !options?.uniqueStartKey;
|
|
4058
|
+
const direction = options?.direction || 1;
|
|
3777
4059
|
const cursors = CursorManager.generateResultCursors(
|
|
3778
4060
|
results,
|
|
3779
4061
|
translatedQuery.sortFields,
|
|
3780
|
-
|
|
4062
|
+
direction,
|
|
3781
4063
|
hasMore,
|
|
3782
4064
|
isFirstPage
|
|
3783
4065
|
);
|
|
4066
|
+
if (direction === -1) {
|
|
4067
|
+
results.reverse();
|
|
4068
|
+
}
|
|
3784
4069
|
let transformedData;
|
|
3785
4070
|
if (options?.projection && Object.keys(options.projection).length > 0) {
|
|
3786
4071
|
transformedData = results.map((row) => {
|
|
@@ -4356,7 +4641,7 @@ var init_BaseModel = __esm({
|
|
|
4356
4641
|
);
|
|
4357
4642
|
}
|
|
4358
4643
|
for (const [recordId, recordYMap] of documentYMap.entries()) {
|
|
4359
|
-
if (recordYMap instanceof
|
|
4644
|
+
if (recordYMap instanceof Y5.Map) {
|
|
4360
4645
|
const instance = new this({ id: recordId });
|
|
4361
4646
|
instance._metaDocId = docId;
|
|
4362
4647
|
const connectedDoc = modelConstructor.connectedDocuments.get(docId);
|
|
@@ -4381,6 +4666,50 @@ var init_BaseModel = __esm({
|
|
|
4381
4666
|
}
|
|
4382
4667
|
return allInstances;
|
|
4383
4668
|
}
|
|
4669
|
+
/**
|
|
4670
|
+
* Resolve a record by its unique-constraint field values via an engine query.
|
|
4671
|
+
* Used as a fallback when the `_uniqueIdx_<model>_<constraint>` map has no
|
|
4672
|
+
* entry for a key.
|
|
4673
|
+
*
|
|
4674
|
+
* The index map is a cache over the SQLite projection, not the source of
|
|
4675
|
+
* truth: every record is loaded into the projection on `initializeForDocument`
|
|
4676
|
+
* regardless of index presence, but a record written by a client predating
|
|
4677
|
+
* index maintenance (legacy data) is missing from the index. Such a record
|
|
4678
|
+
* silently escapes both `upsertByUnique` lookups and the `save()` uniqueness
|
|
4679
|
+
* check. A query over the constraint fields finds it reliably.
|
|
4680
|
+
*
|
|
4681
|
+
* Contract: RESOLVE ONLY. Returns the owning record (with `_metaDocId` set to
|
|
4682
|
+
* the document that holds it, via `queryOne`) or `null`. Callers decide
|
|
4683
|
+
* whether to heal the missing index entry (write paths) and do their own
|
|
4684
|
+
* logging — this method never mutates the index, so read paths stay
|
|
4685
|
+
* read-only. Mirrors `buildUniqueKey`: a null/undefined value in any
|
|
4686
|
+
* constraint field means the record does not participate in the constraint,
|
|
4687
|
+
* so no query runs.
|
|
4688
|
+
*/
|
|
4689
|
+
static async _resolveByUniqueFallback(constraint, keyValues, options) {
|
|
4690
|
+
if (!_BaseModel.dbInstance) {
|
|
4691
|
+
return null;
|
|
4692
|
+
}
|
|
4693
|
+
const filter = {};
|
|
4694
|
+
for (let i = 0; i < constraint.fields.length; i++) {
|
|
4695
|
+
const value = keyValues[i];
|
|
4696
|
+
if (value === null || value === void 0) {
|
|
4697
|
+
return null;
|
|
4698
|
+
}
|
|
4699
|
+
filter[constraint.fields[i]] = value;
|
|
4700
|
+
}
|
|
4701
|
+
if (options?.excludeId) {
|
|
4702
|
+
if ("id" in filter) {
|
|
4703
|
+
if (filter.id === options.excludeId) {
|
|
4704
|
+
return null;
|
|
4705
|
+
}
|
|
4706
|
+
} else {
|
|
4707
|
+
filter.id = { $ne: options.excludeId };
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
const found = await this.queryOne(filter);
|
|
4711
|
+
return found ?? null;
|
|
4712
|
+
}
|
|
4384
4713
|
static async findByUnique(constraintName, value) {
|
|
4385
4714
|
const modelConstructor = this;
|
|
4386
4715
|
const schema = modelConstructor.getSchema();
|
|
@@ -4419,7 +4748,10 @@ var init_BaseModel = __esm({
|
|
|
4419
4748
|
const documentInfo = modelConstructor.connectedDocuments.get(docId);
|
|
4420
4749
|
if (!documentInfo) continue;
|
|
4421
4750
|
const yDoc = documentInfo.yDoc;
|
|
4422
|
-
const constraintMapName2 =
|
|
4751
|
+
const constraintMapName2 = uniqueIndexMapName(
|
|
4752
|
+
currentModelName,
|
|
4753
|
+
constraint.name
|
|
4754
|
+
);
|
|
4423
4755
|
const constraintMap2 = yDoc.getMap(constraintMapName2);
|
|
4424
4756
|
const recordId2 = constraintMap2.get(uniqueKeyString);
|
|
4425
4757
|
if (recordId2) {
|
|
@@ -4427,7 +4759,7 @@ var init_BaseModel = __esm({
|
|
|
4427
4759
|
const documentYMap = modelConstructor.documentYMaps.get(documentYMapKey);
|
|
4428
4760
|
if (documentYMap) {
|
|
4429
4761
|
const recordYMap2 = documentYMap.get(recordId2);
|
|
4430
|
-
if (recordYMap2 && recordYMap2 instanceof
|
|
4762
|
+
if (recordYMap2 && recordYMap2 instanceof Y5.Map) {
|
|
4431
4763
|
const instance = new modelConstructor({
|
|
4432
4764
|
id: recordId2
|
|
4433
4765
|
});
|
|
@@ -4441,6 +4773,16 @@ var init_BaseModel = __esm({
|
|
|
4441
4773
|
}
|
|
4442
4774
|
}
|
|
4443
4775
|
}
|
|
4776
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4777
|
+
constraint,
|
|
4778
|
+
keyValues
|
|
4779
|
+
);
|
|
4780
|
+
if (fallbackRecord) {
|
|
4781
|
+
Logger.verbose(
|
|
4782
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
4783
|
+
);
|
|
4784
|
+
return fallbackRecord;
|
|
4785
|
+
}
|
|
4444
4786
|
Logger.verbose(
|
|
4445
4787
|
`[${currentModelName}.findByUnique] Record not found in any connected document`
|
|
4446
4788
|
);
|
|
@@ -4454,17 +4796,30 @@ var init_BaseModel = __esm({
|
|
|
4454
4796
|
`[${currentModelName}] No Y.Doc is connected for this model. Connect a document via initJsBao(...).connectDocument or call initializeForDocument(yDoc, db, docId, permissionHint) before using legacy findByUnique.`
|
|
4455
4797
|
);
|
|
4456
4798
|
}
|
|
4457
|
-
const constraintMapName =
|
|
4799
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4800
|
+
currentModelName,
|
|
4801
|
+
constraint.name
|
|
4802
|
+
);
|
|
4458
4803
|
const constraintMap = legacyDocInfo.yDoc.getMap(constraintMapName);
|
|
4459
4804
|
const recordId = constraintMap.get(uniqueKeyString);
|
|
4460
4805
|
if (!recordId) {
|
|
4806
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4807
|
+
constraint,
|
|
4808
|
+
keyValues
|
|
4809
|
+
);
|
|
4810
|
+
if (fallbackRecord) {
|
|
4811
|
+
Logger.verbose(
|
|
4812
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
4813
|
+
);
|
|
4814
|
+
return fallbackRecord;
|
|
4815
|
+
}
|
|
4461
4816
|
return null;
|
|
4462
4817
|
}
|
|
4463
4818
|
const recordYMap = legacyDocumentMap.get(recordId);
|
|
4464
4819
|
if (!recordYMap) {
|
|
4465
4820
|
return null;
|
|
4466
4821
|
}
|
|
4467
|
-
if (recordYMap instanceof
|
|
4822
|
+
if (recordYMap instanceof Y5.Map) {
|
|
4468
4823
|
const instance = new modelConstructor({
|
|
4469
4824
|
id: recordId
|
|
4470
4825
|
});
|
|
@@ -4522,7 +4877,10 @@ var init_BaseModel = __esm({
|
|
|
4522
4877
|
`[${currentModelName}] upsertByUnique for '${constraintName}': unique key cannot be built due to null/undefined values. Upsert cannot proceed reliably.`
|
|
4523
4878
|
);
|
|
4524
4879
|
}
|
|
4525
|
-
const constraintMapName =
|
|
4880
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4881
|
+
currentModelName,
|
|
4882
|
+
constraint.name
|
|
4883
|
+
);
|
|
4526
4884
|
let existingRecord = null;
|
|
4527
4885
|
if (connectedDocuments && connectedDocuments.size > 0) {
|
|
4528
4886
|
for (const docId of connectedDocuments.keys()) {
|
|
@@ -4540,6 +4898,33 @@ var init_BaseModel = __esm({
|
|
|
4540
4898
|
}
|
|
4541
4899
|
} else {
|
|
4542
4900
|
}
|
|
4901
|
+
if (!existingRecord) {
|
|
4902
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4903
|
+
constraint,
|
|
4904
|
+
keyValuesForLookup
|
|
4905
|
+
);
|
|
4906
|
+
if (fallbackRecord) {
|
|
4907
|
+
existingRecord = fallbackRecord;
|
|
4908
|
+
const healDocId = fallbackRecord._metaDocId;
|
|
4909
|
+
const healDocInfo = healDocId ? connectedDocuments.get(healDocId) : void 0;
|
|
4910
|
+
const canHeal = !!healDocInfo && healDocInfo.permissionHint !== "read";
|
|
4911
|
+
if (healDocInfo && canHeal) {
|
|
4912
|
+
healDocInfo.yDoc.transact(() => {
|
|
4913
|
+
const healMap = healDocInfo.yDoc.getMap(constraintMapName);
|
|
4914
|
+
if (healMap.get(uniqueKeyString) !== fallbackRecord.id) {
|
|
4915
|
+
healMap.set(uniqueKeyString, fallbackRecord.id);
|
|
4916
|
+
}
|
|
4917
|
+
}, `heal-unique-index-${currentModelName}-${healDocId}`);
|
|
4918
|
+
Logger.verbose(
|
|
4919
|
+
`[${currentModelName}] upsertByUnique: index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id}; healed missing index entry (legacy index-less record).`
|
|
4920
|
+
);
|
|
4921
|
+
} else {
|
|
4922
|
+
Logger.verbose(
|
|
4923
|
+
`[${currentModelName}] upsertByUnique: index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id}; skipped index heal (owning document is read-only or not connected).`
|
|
4924
|
+
);
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4543
4928
|
if (options?.objectMustExist) {
|
|
4544
4929
|
if (!existingRecord) {
|
|
4545
4930
|
throw new RecordNotFoundError(
|
|
@@ -4696,7 +5081,7 @@ var init_BaseModel = __esm({
|
|
|
4696
5081
|
for (const [fieldKey, fieldOptions] of schema.fields) {
|
|
4697
5082
|
if (fieldOptions?.type !== "stringset") continue;
|
|
4698
5083
|
const value = recordYMap.get(fieldKey);
|
|
4699
|
-
if (value instanceof
|
|
5084
|
+
if (value instanceof Y5.Map) {
|
|
4700
5085
|
this.observeStringSetMapOnce(value);
|
|
4701
5086
|
}
|
|
4702
5087
|
}
|
|
@@ -4743,7 +5128,14 @@ var init_BaseModel = __esm({
|
|
|
4743
5128
|
continue;
|
|
4744
5129
|
}
|
|
4745
5130
|
if (value !== void 0) {
|
|
4746
|
-
|
|
5131
|
+
if (isYjsComposite(value)) {
|
|
5132
|
+
Logger.warn(
|
|
5133
|
+
`[${modelName}] Field '${key}' on record ${recordId} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
5134
|
+
);
|
|
5135
|
+
updatedData[key] = normalizeYjsValueForStorage(value);
|
|
5136
|
+
} else {
|
|
5137
|
+
updatedData[key] = value;
|
|
5138
|
+
}
|
|
4747
5139
|
}
|
|
4748
5140
|
}
|
|
4749
5141
|
if (unknownFields.length > 0) {
|
|
@@ -4823,7 +5215,14 @@ var init_BaseModel = __esm({
|
|
|
4823
5215
|
continue;
|
|
4824
5216
|
}
|
|
4825
5217
|
if (value !== void 0) {
|
|
4826
|
-
|
|
5218
|
+
if (isYjsComposite(value)) {
|
|
5219
|
+
Logger.warn(
|
|
5220
|
+
`[${modelName}] Field '${key}' on record ${recordId} in document ${docId} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
5221
|
+
);
|
|
5222
|
+
updatedData[key] = normalizeYjsValueForStorage(value);
|
|
5223
|
+
} else {
|
|
5224
|
+
updatedData[key] = value;
|
|
5225
|
+
}
|
|
4827
5226
|
}
|
|
4828
5227
|
}
|
|
4829
5228
|
if (unknownFields.length > 0) {
|
|
@@ -4868,6 +5267,19 @@ var init_BaseModel = __esm({
|
|
|
4868
5267
|
});
|
|
4869
5268
|
|
|
4870
5269
|
// src/models/relationshipManager.ts
|
|
5270
|
+
function warnIfOrderFieldNullable(modelClass, orderByField, relationshipDescription) {
|
|
5271
|
+
if (ALWAYS_PRESENT_ORDER_FIELDS.has(orderByField)) return;
|
|
5272
|
+
const schema = modelClass.getSchema?.();
|
|
5273
|
+
const fields = schema?.fields;
|
|
5274
|
+
if (!fields || typeof fields.get !== "function") return;
|
|
5275
|
+
const fieldOptions = fields.get(orderByField);
|
|
5276
|
+
if (!fieldOptions) return;
|
|
5277
|
+
if (fieldOptions.required !== true) {
|
|
5278
|
+
Logger.warn(
|
|
5279
|
+
`[RelationshipManager] ${relationshipDescription} orders by '${orderByField}', which is not marked required on ${modelClass.name}. The composite (${orderByField}, id) cursor paginates deterministically, but a row whose '${orderByField}' is actually NULL has no stable cursor position and may drop from the tail. Mark '${orderByField}' as required, or order by a required field such as 'id', to remove the risk. (Full NULL-ordering support is tracked in #1316.)`
|
|
5280
|
+
);
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
4871
5283
|
function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
4872
5284
|
return async function() {
|
|
4873
5285
|
const relatedId = this[config.relatedIdField];
|
|
@@ -4876,6 +5288,16 @@ function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
|
4876
5288
|
};
|
|
4877
5289
|
}
|
|
4878
5290
|
function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbEngine) {
|
|
5291
|
+
const {
|
|
5292
|
+
relatedIdField,
|
|
5293
|
+
orderByField = "id",
|
|
5294
|
+
orderDirection = "ASC"
|
|
5295
|
+
} = config;
|
|
5296
|
+
warnIfOrderFieldNullable(
|
|
5297
|
+
targetModelClass,
|
|
5298
|
+
orderByField,
|
|
5299
|
+
`hasMany(${targetModelClass.name})`
|
|
5300
|
+
);
|
|
4879
5301
|
return async function(paginationArgs) {
|
|
4880
5302
|
const {
|
|
4881
5303
|
limit,
|
|
@@ -4883,71 +5305,39 @@ function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbE
|
|
|
4883
5305
|
beforeCursor,
|
|
4884
5306
|
direction = "forward"
|
|
4885
5307
|
} = paginationArgs || {};
|
|
4886
|
-
const {
|
|
4887
|
-
relatedIdField,
|
|
4888
|
-
orderByField = "id",
|
|
4889
|
-
orderDirection = "ASC"
|
|
4890
|
-
} = config;
|
|
4891
5308
|
const targetSchema = targetModelClass.getSchema();
|
|
4892
5309
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
4893
5310
|
Logger.error(
|
|
4894
5311
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasMany relationship has no valid schema name.`
|
|
4895
5312
|
);
|
|
4896
|
-
return { data: [],
|
|
5313
|
+
return { data: [], hasMore: false };
|
|
4897
5314
|
}
|
|
4898
|
-
const
|
|
5315
|
+
const forward = direction !== "backward";
|
|
5316
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
4899
5317
|
const queryOptions = {
|
|
4900
|
-
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 }
|
|
5318
|
+
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 },
|
|
5319
|
+
direction: forward ? 1 : -1
|
|
4901
5320
|
};
|
|
4902
|
-
if (limit !== void 0)
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
};
|
|
4909
|
-
} else if (direction === "backward") {
|
|
4910
|
-
if (beforeCursor) {
|
|
4911
|
-
filter[orderByField] = {
|
|
4912
|
-
[orderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
4913
|
-
};
|
|
4914
|
-
}
|
|
4915
|
-
queryOptions.sort = { [orderByField]: orderDirection === "ASC" ? -1 : 1 };
|
|
4916
|
-
}
|
|
4917
|
-
const result = await targetModelClass.query(filter, queryOptions);
|
|
4918
|
-
const results = result.data;
|
|
4919
|
-
let nextCursor = null;
|
|
4920
|
-
let prevCursor = null;
|
|
4921
|
-
const isFirstPage = direction === "forward" && !afterCursor;
|
|
4922
|
-
if (limit !== void 0) {
|
|
4923
|
-
if (direction === "forward") {
|
|
4924
|
-
if (results.length > limit) {
|
|
4925
|
-
nextCursor = results[limit - 1][orderByField];
|
|
4926
|
-
results.pop();
|
|
4927
|
-
}
|
|
4928
|
-
if (results.length > 0 && !isFirstPage) {
|
|
4929
|
-
prevCursor = results[0][orderByField];
|
|
4930
|
-
}
|
|
4931
|
-
} else {
|
|
4932
|
-
if (results.length > limit) {
|
|
4933
|
-
prevCursor = results[limit - 1][orderByField];
|
|
4934
|
-
results.pop();
|
|
4935
|
-
}
|
|
4936
|
-
results.reverse();
|
|
4937
|
-
if (results.length > 0 && beforeCursor) {
|
|
4938
|
-
nextCursor = results[results.length - 1][orderByField];
|
|
4939
|
-
}
|
|
4940
|
-
if (results.length > 0 && !beforeCursor) {
|
|
4941
|
-
if (results.length === limit) {
|
|
4942
|
-
prevCursor = results[0][orderByField];
|
|
4943
|
-
}
|
|
4944
|
-
}
|
|
4945
|
-
}
|
|
4946
|
-
}
|
|
4947
|
-
return { data: results, nextCursor, prevCursor };
|
|
5321
|
+
if (limit !== void 0) queryOptions.limit = limit;
|
|
5322
|
+
if (cursor != null) queryOptions.uniqueStartKey = cursor;
|
|
5323
|
+
return targetModelClass.query(
|
|
5324
|
+
{ [relatedIdField]: this.id },
|
|
5325
|
+
queryOptions
|
|
5326
|
+
);
|
|
4948
5327
|
};
|
|
4949
5328
|
}
|
|
4950
5329
|
function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClass, joinModelClass, _dbEngine) {
|
|
5330
|
+
const {
|
|
5331
|
+
joinModelLocalField,
|
|
5332
|
+
joinModelRelatedField,
|
|
5333
|
+
joinModelOrderByField = "id",
|
|
5334
|
+
joinModelOrderDirection = "ASC"
|
|
5335
|
+
} = config;
|
|
5336
|
+
warnIfOrderFieldNullable(
|
|
5337
|
+
joinModelClass,
|
|
5338
|
+
joinModelOrderByField,
|
|
5339
|
+
`hasManyThrough(${targetModelClass.name} via ${joinModelClass.name})`
|
|
5340
|
+
);
|
|
4951
5341
|
return async function(paginationArgs) {
|
|
4952
5342
|
const {
|
|
4953
5343
|
limit,
|
|
@@ -4961,87 +5351,42 @@ function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClas
|
|
|
4961
5351
|
Logger.error(
|
|
4962
5352
|
`[RelationshipManager] Join model ${joinModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
4963
5353
|
);
|
|
4964
|
-
return { data: [],
|
|
5354
|
+
return { data: [], hasMore: false };
|
|
4965
5355
|
}
|
|
4966
5356
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
4967
5357
|
Logger.error(
|
|
4968
5358
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
4969
5359
|
);
|
|
4970
|
-
return { data: [],
|
|
5360
|
+
return { data: [], hasMore: false };
|
|
4971
5361
|
}
|
|
4972
|
-
const
|
|
4973
|
-
|
|
4974
|
-
joinModelRelatedField,
|
|
4975
|
-
joinModelOrderByField = "id",
|
|
4976
|
-
joinModelOrderDirection = "ASC"
|
|
4977
|
-
} = config;
|
|
4978
|
-
const joinFilter = { [joinModelLocalField]: this.id };
|
|
5362
|
+
const forward = direction !== "backward";
|
|
5363
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
4979
5364
|
const joinQueryOptions = {
|
|
4980
5365
|
sort: {
|
|
4981
5366
|
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? 1 : -1
|
|
4982
5367
|
},
|
|
4983
|
-
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 }
|
|
5368
|
+
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 },
|
|
5369
|
+
direction: forward ? 1 : -1
|
|
4984
5370
|
};
|
|
4985
|
-
if (limit !== void 0)
|
|
4986
|
-
|
|
4987
|
-
}
|
|
4988
|
-
if (direction === "forward" && afterCursor) {
|
|
4989
|
-
joinFilter[joinModelOrderByField] = {
|
|
4990
|
-
[joinModelOrderDirection === "ASC" ? "$gt" : "$lt"]: afterCursor
|
|
4991
|
-
};
|
|
4992
|
-
} else if (direction === "backward") {
|
|
4993
|
-
if (beforeCursor) {
|
|
4994
|
-
joinFilter[joinModelOrderByField] = {
|
|
4995
|
-
[joinModelOrderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
4996
|
-
};
|
|
4997
|
-
}
|
|
4998
|
-
joinQueryOptions.sort = {
|
|
4999
|
-
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? -1 : 1
|
|
5000
|
-
};
|
|
5001
|
-
}
|
|
5371
|
+
if (limit !== void 0) joinQueryOptions.limit = limit;
|
|
5372
|
+
if (cursor != null) joinQueryOptions.uniqueStartKey = cursor;
|
|
5002
5373
|
const joinResult = await joinModelClass.query(
|
|
5003
|
-
|
|
5374
|
+
{ [joinModelLocalField]: this.id },
|
|
5004
5375
|
joinQueryOptions
|
|
5005
5376
|
);
|
|
5006
|
-
const
|
|
5007
|
-
let
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
nextCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
5014
|
-
joinResults.pop();
|
|
5015
|
-
}
|
|
5016
|
-
if (joinResults.length > 0 && !isFirstPage) {
|
|
5017
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
5018
|
-
}
|
|
5019
|
-
} else {
|
|
5020
|
-
if (joinResults.length > limit) {
|
|
5021
|
-
prevCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
5022
|
-
joinResults.pop();
|
|
5023
|
-
}
|
|
5024
|
-
joinResults.reverse();
|
|
5025
|
-
if (joinResults.length > 0 && beforeCursor) {
|
|
5026
|
-
nextCursorFromJoin = joinResults[joinResults.length - 1][joinModelOrderByField];
|
|
5027
|
-
}
|
|
5028
|
-
if (joinResults.length > 0 && !beforeCursor) {
|
|
5029
|
-
if (joinResults.length === limit) {
|
|
5030
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
5031
|
-
}
|
|
5032
|
-
}
|
|
5033
|
-
}
|
|
5377
|
+
const relatedIds = joinResult.data.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
5378
|
+
let targetData = [];
|
|
5379
|
+
if (relatedIds.length > 0) {
|
|
5380
|
+
const targetResult = await targetModelClass.query({
|
|
5381
|
+
id: { $in: relatedIds }
|
|
5382
|
+
});
|
|
5383
|
+
targetData = targetResult.data;
|
|
5034
5384
|
}
|
|
5035
|
-
const relatedIds = joinResults.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
5036
|
-
if (relatedIds.length === 0)
|
|
5037
|
-
return { data: [], nextCursor: null, prevCursor: null };
|
|
5038
|
-
const targetResult = await targetModelClass.query({
|
|
5039
|
-
id: { $in: relatedIds }
|
|
5040
|
-
});
|
|
5041
5385
|
return {
|
|
5042
|
-
data:
|
|
5043
|
-
nextCursor:
|
|
5044
|
-
prevCursor:
|
|
5386
|
+
data: targetData,
|
|
5387
|
+
nextCursor: joinResult.nextCursor,
|
|
5388
|
+
prevCursor: joinResult.prevCursor,
|
|
5389
|
+
hasMore: joinResult.hasMore
|
|
5045
5390
|
};
|
|
5046
5391
|
};
|
|
5047
5392
|
}
|
|
@@ -5165,10 +5510,12 @@ function generateHasManyThroughRemoveMethod(_sourceModelClass, config, _targetMo
|
|
|
5165
5510
|
}, `remove-join-${config.joinModel}-${this.id}-${targetId}`);
|
|
5166
5511
|
};
|
|
5167
5512
|
}
|
|
5513
|
+
var ALWAYS_PRESENT_ORDER_FIELDS;
|
|
5168
5514
|
var init_relationshipManager = __esm({
|
|
5169
5515
|
"src/models/relationshipManager.ts"() {
|
|
5170
5516
|
"use strict";
|
|
5171
5517
|
init_BaseModel();
|
|
5518
|
+
ALWAYS_PRESENT_ORDER_FIELDS = /* @__PURE__ */ new Set(["id", "type"]);
|
|
5172
5519
|
}
|
|
5173
5520
|
});
|
|
5174
5521
|
|
|
@@ -5638,13 +5985,100 @@ var init_environment = __esm({
|
|
|
5638
5985
|
// src/engines/SqljsEngine.ts
|
|
5639
5986
|
import initSqlJs from "sql.js";
|
|
5640
5987
|
import { Mutex } from "async-mutex";
|
|
5641
|
-
|
|
5988
|
+
async function resolveNodeWasmPath() {
|
|
5989
|
+
if (!isNode()) {
|
|
5990
|
+
return null;
|
|
5991
|
+
}
|
|
5992
|
+
try {
|
|
5993
|
+
const cjsRequire = typeof __require !== "undefined" ? __require : void 0;
|
|
5994
|
+
if (cjsRequire) {
|
|
5995
|
+
return cjsRequire.resolve("sql.js/dist/sql-wasm.wasm");
|
|
5996
|
+
}
|
|
5997
|
+
} catch {
|
|
5998
|
+
}
|
|
5999
|
+
try {
|
|
6000
|
+
const metaUrl = import.meta.url;
|
|
6001
|
+
if (metaUrl) {
|
|
6002
|
+
const { createRequire } = await import(
|
|
6003
|
+
/* @vite-ignore */
|
|
6004
|
+
"module"
|
|
6005
|
+
);
|
|
6006
|
+
const req = createRequire(metaUrl);
|
|
6007
|
+
return req.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6008
|
+
}
|
|
6009
|
+
} catch {
|
|
6010
|
+
}
|
|
6011
|
+
return null;
|
|
6012
|
+
}
|
|
6013
|
+
async function preflightNodeWasm(wasmPath) {
|
|
6014
|
+
if (!isNode()) {
|
|
6015
|
+
return;
|
|
6016
|
+
}
|
|
6017
|
+
if (/^[a-z]+:\/\//i.test(wasmPath)) {
|
|
6018
|
+
return;
|
|
6019
|
+
}
|
|
6020
|
+
const { existsSync } = await import(
|
|
6021
|
+
/* @vite-ignore */
|
|
6022
|
+
"fs"
|
|
6023
|
+
);
|
|
6024
|
+
if (!existsSync(wasmPath)) {
|
|
6025
|
+
throw new Error(
|
|
6026
|
+
`[SqljsEngine] sql.js wasm not found at "${wasmPath}". Pass \`locateFile\`/\`wasmURL\` pointing at sql-wasm.wasm, or install \`better-sqlite3\` and use the \`node-sqlite\` engine (see getRecommendedNodeEngine()).`
|
|
6027
|
+
);
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
async function loadSqlJsModule(locateFile) {
|
|
6031
|
+
let abortReject;
|
|
6032
|
+
const toError = (reason) => reason instanceof Error ? reason : new Error(
|
|
6033
|
+
`[SqljsEngine] sql.js aborted during initialization: ${String(
|
|
6034
|
+
reason
|
|
6035
|
+
)}`
|
|
6036
|
+
);
|
|
6037
|
+
const nodeProcess = isNode() && typeof process !== "undefined" ? process : void 0;
|
|
6038
|
+
const onUnhandled = (reason) => {
|
|
6039
|
+
const err = toError(reason);
|
|
6040
|
+
if (/Aborted/.test(err.message) || /Aborted/.test(String(reason))) {
|
|
6041
|
+
abortReject?.(err);
|
|
6042
|
+
return;
|
|
6043
|
+
}
|
|
6044
|
+
if (nodeProcess && nodeProcess.listenerCount("unhandledRejection") === 1) {
|
|
6045
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6046
|
+
void Promise.reject(reason);
|
|
6047
|
+
}
|
|
6048
|
+
};
|
|
6049
|
+
if (nodeProcess) {
|
|
6050
|
+
nodeProcess.on("unhandledRejection", onUnhandled);
|
|
6051
|
+
}
|
|
6052
|
+
const abortGuard = new Promise((_resolve, reject) => {
|
|
6053
|
+
abortReject = reject;
|
|
6054
|
+
});
|
|
6055
|
+
abortGuard.catch(() => {
|
|
6056
|
+
});
|
|
6057
|
+
try {
|
|
6058
|
+
const initPromise = initSqlJs({
|
|
6059
|
+
locateFile,
|
|
6060
|
+
// Layer 1: emscripten onAbort.
|
|
6061
|
+
onAbort: (reason) => abortReject?.(toError(reason))
|
|
6062
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6063
|
+
});
|
|
6064
|
+
return await Promise.race([initPromise, abortGuard]);
|
|
6065
|
+
} finally {
|
|
6066
|
+
if (nodeProcess) {
|
|
6067
|
+
setTimeout(() => {
|
|
6068
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6069
|
+
}, 0);
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
var SQL_WASM_URL, SQL_WASM_FILENAME, TransactionalSQLJSOperations, SqljsEngine;
|
|
5642
6074
|
var init_SqljsEngine = __esm({
|
|
5643
6075
|
"src/engines/SqljsEngine.ts"() {
|
|
5644
6076
|
"use strict";
|
|
5645
6077
|
init_BaseModel();
|
|
5646
6078
|
init_sql();
|
|
6079
|
+
init_environment();
|
|
5647
6080
|
SQL_WASM_URL = "/sql-wasm.wasm";
|
|
6081
|
+
SQL_WASM_FILENAME = "sql-wasm.wasm";
|
|
5648
6082
|
TransactionalSQLJSOperations = class {
|
|
5649
6083
|
db;
|
|
5650
6084
|
engine;
|
|
@@ -5710,22 +6144,37 @@ var init_SqljsEngine = __esm({
|
|
|
5710
6144
|
}
|
|
5711
6145
|
async initialize(engineOptions) {
|
|
5712
6146
|
Logger.info("[SqljsEngine] Initializing SQL.js engine...");
|
|
6147
|
+
const loadingModuleThisCall = !_SqljsEngine.SQL;
|
|
5713
6148
|
try {
|
|
5714
6149
|
if (!_SqljsEngine.SQL) {
|
|
5715
|
-
|
|
6150
|
+
let locateFileConfig;
|
|
6151
|
+
if (engineOptions?.locateFile) {
|
|
6152
|
+
locateFileConfig = engineOptions.locateFile;
|
|
6153
|
+
} else if (engineOptions?.wasmURL) {
|
|
6154
|
+
const url = engineOptions.wasmURL;
|
|
6155
|
+
locateFileConfig = (_file) => url;
|
|
6156
|
+
} else {
|
|
6157
|
+
const nodeWasmPath = await resolveNodeWasmPath();
|
|
6158
|
+
const resolved = nodeWasmPath ?? SQL_WASM_URL;
|
|
6159
|
+
locateFileConfig = (_file) => resolved;
|
|
6160
|
+
}
|
|
6161
|
+
const wasmPath = locateFileConfig(SQL_WASM_FILENAME);
|
|
5716
6162
|
Logger.debug(
|
|
5717
6163
|
"[SqljsEngine] Attempting to load SQL.js WASM from:",
|
|
5718
|
-
|
|
6164
|
+
wasmPath
|
|
5719
6165
|
);
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
6166
|
+
await preflightNodeWasm(wasmPath);
|
|
6167
|
+
const SQL = await loadSqlJsModule(locateFileConfig);
|
|
6168
|
+
_SqljsEngine.SQL = SQL;
|
|
5723
6169
|
Logger.info("[SqljsEngine] SQL.js WASM loaded successfully.");
|
|
5724
6170
|
}
|
|
5725
6171
|
this.db = new _SqljsEngine.SQL.Database();
|
|
5726
6172
|
Logger.info("[SqljsEngine] SQL.js Database initialized in memory.");
|
|
5727
6173
|
this.updateTableNames();
|
|
5728
6174
|
} catch (error) {
|
|
6175
|
+
if (loadingModuleThisCall) {
|
|
6176
|
+
_SqljsEngine.SQL = null;
|
|
6177
|
+
}
|
|
5729
6178
|
Logger.error("[SqljsEngine] Error during SQL.js initialization:", error);
|
|
5730
6179
|
throw error;
|
|
5731
6180
|
}
|
|
@@ -6910,6 +7359,7 @@ init_StringSet();
|
|
|
6910
7359
|
// src/models/schema.ts
|
|
6911
7360
|
init_BaseModel();
|
|
6912
7361
|
init_ModelRegistry();
|
|
7362
|
+
init_uniqueIndex();
|
|
6913
7363
|
function defineModelSchema(input) {
|
|
6914
7364
|
const { name, fields } = input;
|
|
6915
7365
|
const options = {
|
|
@@ -6982,7 +7432,7 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
|
|
|
6982
7432
|
for (const [fieldName, options] of fields.entries()) {
|
|
6983
7433
|
if (options.unique) {
|
|
6984
7434
|
resolved.push({
|
|
6985
|
-
name:
|
|
7435
|
+
name: fieldUniqueConstraintName(modelName, fieldName),
|
|
6986
7436
|
fields: [fieldName]
|
|
6987
7437
|
});
|
|
6988
7438
|
}
|
|
@@ -7103,10 +7553,13 @@ var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
|
7103
7553
|
"unique",
|
|
7104
7554
|
"required",
|
|
7105
7555
|
"auto_assign",
|
|
7556
|
+
"auto_stamp",
|
|
7106
7557
|
"max_length",
|
|
7107
7558
|
"max_count",
|
|
7108
|
-
"default"
|
|
7559
|
+
"default",
|
|
7560
|
+
"enum"
|
|
7109
7561
|
]);
|
|
7562
|
+
var VALID_AUTO_STAMP_VALUES = /* @__PURE__ */ new Set(["create", "update", "both"]);
|
|
7110
7563
|
var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
|
|
7111
7564
|
"fields",
|
|
7112
7565
|
"relationships",
|
|
@@ -7148,9 +7601,37 @@ function parseFieldOptions(raw, context, strict) {
|
|
|
7148
7601
|
if (raw.unique === true) opts.unique = true;
|
|
7149
7602
|
if (raw.required === true) opts.required = true;
|
|
7150
7603
|
if (raw.auto_assign === true) opts.autoAssign = true;
|
|
7604
|
+
if (raw.auto_stamp !== void 0) {
|
|
7605
|
+
if (typeof raw.auto_stamp !== "string" || !VALID_AUTO_STAMP_VALUES.has(raw.auto_stamp)) {
|
|
7606
|
+
throw new Error(
|
|
7607
|
+
`${context}: invalid auto_stamp value "${raw.auto_stamp}". Must be one of: ${[
|
|
7608
|
+
...VALID_AUTO_STAMP_VALUES
|
|
7609
|
+
].join(", ")}`
|
|
7610
|
+
);
|
|
7611
|
+
}
|
|
7612
|
+
opts.autoStamp = raw.auto_stamp;
|
|
7613
|
+
}
|
|
7151
7614
|
if (raw.max_length !== void 0) opts.maxLength = raw.max_length;
|
|
7152
7615
|
if (raw.max_count !== void 0) opts.maxCount = raw.max_count;
|
|
7153
7616
|
if (raw.default !== void 0) opts.default = raw.default;
|
|
7617
|
+
if (raw.enum !== void 0) {
|
|
7618
|
+
if (raw.type !== "string") {
|
|
7619
|
+
throw new Error(
|
|
7620
|
+
`${context}: \`enum\` is only valid on a "string" field, not "${raw.type}".`
|
|
7621
|
+
);
|
|
7622
|
+
}
|
|
7623
|
+
if (!Array.isArray(raw.enum) || raw.enum.length === 0) {
|
|
7624
|
+
throw new Error(
|
|
7625
|
+
`${context}: \`enum\` must be a non-empty array of strings.`
|
|
7626
|
+
);
|
|
7627
|
+
}
|
|
7628
|
+
if (!raw.enum.every((v) => typeof v === "string")) {
|
|
7629
|
+
throw new Error(
|
|
7630
|
+
`${context}: \`enum\` values must all be strings.`
|
|
7631
|
+
);
|
|
7632
|
+
}
|
|
7633
|
+
opts.enum = [...raw.enum];
|
|
7634
|
+
}
|
|
7154
7635
|
return opts;
|
|
7155
7636
|
}
|
|
7156
7637
|
function requireField(raw, field, context) {
|
|
@@ -7278,7 +7759,8 @@ async function loadSchemaFromToml(filePath, options = {}) {
|
|
|
7278
7759
|
}
|
|
7279
7760
|
|
|
7280
7761
|
// src/utils/yDocSchema.ts
|
|
7281
|
-
|
|
7762
|
+
init_yjsValues();
|
|
7763
|
+
import * as Y6 from "yjs";
|
|
7282
7764
|
function discoverSchema(yDoc) {
|
|
7283
7765
|
const models = {};
|
|
7284
7766
|
const metaNames = /* @__PURE__ */ new Set();
|
|
@@ -7312,7 +7794,7 @@ function discoverModelNames(yDoc) {
|
|
|
7312
7794
|
function materializeMap(yDoc, key) {
|
|
7313
7795
|
try {
|
|
7314
7796
|
const map = yDoc.getMap(key);
|
|
7315
|
-
return map instanceof
|
|
7797
|
+
return map instanceof Y6.Map ? map : null;
|
|
7316
7798
|
} catch {
|
|
7317
7799
|
return null;
|
|
7318
7800
|
}
|
|
@@ -7322,11 +7804,11 @@ function readModelMeta(metaMap) {
|
|
|
7322
7804
|
let constraints;
|
|
7323
7805
|
let relationships;
|
|
7324
7806
|
for (const [key, value] of metaMap.entries()) {
|
|
7325
|
-
if (key === "_constraints" && value instanceof
|
|
7807
|
+
if (key === "_constraints" && value instanceof Y6.Map) {
|
|
7326
7808
|
constraints = readConstraints(value);
|
|
7327
|
-
} else if (key === "_relationships" && value instanceof
|
|
7809
|
+
} else if (key === "_relationships" && value instanceof Y6.Map) {
|
|
7328
7810
|
relationships = readRelationships(value);
|
|
7329
|
-
} else if (value instanceof
|
|
7811
|
+
} else if (value instanceof Y6.Map) {
|
|
7330
7812
|
fields[key] = readFieldMeta(value);
|
|
7331
7813
|
}
|
|
7332
7814
|
}
|
|
@@ -7345,6 +7827,10 @@ function readFieldMeta(fieldMap) {
|
|
|
7345
7827
|
if (fieldMap.get("unique") === true) field.unique = true;
|
|
7346
7828
|
if (fieldMap.get("required") === true) field.required = true;
|
|
7347
7829
|
if (fieldMap.get("autoAssign") === true) field.autoAssign = true;
|
|
7830
|
+
const autoStamp = fieldMap.get("autoStamp");
|
|
7831
|
+
if (typeof autoStamp === "string" && (autoStamp === "create" || autoStamp === "update" || autoStamp === "both")) {
|
|
7832
|
+
field.autoStamp = autoStamp;
|
|
7833
|
+
}
|
|
7348
7834
|
const def = fieldMap.get("default");
|
|
7349
7835
|
if (def !== void 0) field.default = def;
|
|
7350
7836
|
const maxLength = fieldMap.get("maxLength");
|
|
@@ -7357,7 +7843,7 @@ function inferModelFromData(dataMap) {
|
|
|
7357
7843
|
const fields = {};
|
|
7358
7844
|
let sampled = 0;
|
|
7359
7845
|
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
7360
|
-
if (!(recordValue instanceof
|
|
7846
|
+
if (!(recordValue instanceof Y6.Map)) continue;
|
|
7361
7847
|
if (++sampled > 5) break;
|
|
7362
7848
|
for (const [fieldName, value] of recordValue.entries()) {
|
|
7363
7849
|
if (fieldName.startsWith("_")) continue;
|
|
@@ -7370,7 +7856,7 @@ function inferModelFromData(dataMap) {
|
|
|
7370
7856
|
return { fields };
|
|
7371
7857
|
}
|
|
7372
7858
|
function inferTypeFromValue(value) {
|
|
7373
|
-
if (value
|
|
7859
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
7374
7860
|
switch (typeof value) {
|
|
7375
7861
|
case "string":
|
|
7376
7862
|
return "string";
|
|
@@ -7385,7 +7871,7 @@ function inferTypeFromValue(value) {
|
|
|
7385
7871
|
function readConstraints(constraintsMap) {
|
|
7386
7872
|
const out = {};
|
|
7387
7873
|
for (const [name, value] of constraintsMap.entries()) {
|
|
7388
|
-
if (!(value instanceof
|
|
7874
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
7389
7875
|
let fields = [];
|
|
7390
7876
|
const rawFields = value.get("fields");
|
|
7391
7877
|
if (typeof rawFields === "string") {
|
|
@@ -7404,7 +7890,7 @@ function readConstraints(constraintsMap) {
|
|
|
7404
7890
|
function readRelationships(relsMap) {
|
|
7405
7891
|
const out = {};
|
|
7406
7892
|
for (const [name, value] of relsMap.entries()) {
|
|
7407
|
-
if (!(value instanceof
|
|
7893
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
7408
7894
|
const rel = {};
|
|
7409
7895
|
for (const [k, v] of value.entries()) {
|
|
7410
7896
|
rel[k] = v;
|
|
@@ -7415,6 +7901,7 @@ function readRelationships(relsMap) {
|
|
|
7415
7901
|
}
|
|
7416
7902
|
var CAMEL_TO_SNAKE = {
|
|
7417
7903
|
autoAssign: "auto_assign",
|
|
7904
|
+
autoStamp: "auto_stamp",
|
|
7418
7905
|
maxLength: "max_length",
|
|
7419
7906
|
maxCount: "max_count",
|
|
7420
7907
|
relatedIdField: "related_id_field",
|
|
@@ -7445,6 +7932,7 @@ function schemaToToml(schema) {
|
|
|
7445
7932
|
lines.push(`[models.${modelName}.fields.${fieldName}]`);
|
|
7446
7933
|
lines.push(`type = ${tomlValue(field.type)}`);
|
|
7447
7934
|
if (field.autoAssign) lines.push("auto_assign = true");
|
|
7935
|
+
if (field.autoStamp) lines.push(`auto_stamp = ${tomlValue(field.autoStamp)}`);
|
|
7448
7936
|
if (field.indexed) lines.push("indexed = true");
|
|
7449
7937
|
if (field.unique) lines.push("unique = true");
|
|
7450
7938
|
if (field.required) lines.push("required = true");
|
|
@@ -7476,19 +7964,20 @@ function schemaToToml(schema) {
|
|
|
7476
7964
|
}
|
|
7477
7965
|
|
|
7478
7966
|
// src/index.ts
|
|
7967
|
+
init_uniqueIndex();
|
|
7479
7968
|
init_BaseModel();
|
|
7480
7969
|
|
|
7481
7970
|
// src/utils/yDocDump.ts
|
|
7482
|
-
import * as
|
|
7971
|
+
import * as Y7 from "yjs";
|
|
7483
7972
|
function toPlain(value) {
|
|
7484
|
-
if (value instanceof
|
|
7973
|
+
if (value instanceof Y7.Map) {
|
|
7485
7974
|
const obj = {};
|
|
7486
7975
|
value.forEach((v, k) => {
|
|
7487
7976
|
obj[k] = toPlain(v);
|
|
7488
7977
|
});
|
|
7489
7978
|
return obj;
|
|
7490
7979
|
}
|
|
7491
|
-
if (value instanceof
|
|
7980
|
+
if (value instanceof Y7.Array) {
|
|
7492
7981
|
return value.toArray().map((item) => toPlain(item));
|
|
7493
7982
|
}
|
|
7494
7983
|
return value;
|
|
@@ -7497,7 +7986,7 @@ function dumpYDocToPlain(yDoc, options = {}) {
|
|
|
7497
7986
|
const result = {};
|
|
7498
7987
|
const includeIndexes = !!options.includeIndexes;
|
|
7499
7988
|
yDoc.share.forEach((type, name) => {
|
|
7500
|
-
if (!(type instanceof
|
|
7989
|
+
if (!(type instanceof Y7.Map)) return;
|
|
7501
7990
|
if (!includeIndexes && name.startsWith("_uniqueIdx_")) return;
|
|
7502
7991
|
const records = {};
|
|
7503
7992
|
type.forEach((recordValue, recordId) => {
|
|
@@ -7545,12 +8034,14 @@ export {
|
|
|
7545
8034
|
attachAndRegisterModel,
|
|
7546
8035
|
attachSchemaToClass,
|
|
7547
8036
|
autoRegisterModel,
|
|
8037
|
+
buildUniqueKey,
|
|
7548
8038
|
clearMetaSyncCache,
|
|
7549
8039
|
createModelClass,
|
|
7550
8040
|
defineModelSchema,
|
|
7551
8041
|
discoverModelNames,
|
|
7552
8042
|
discoverSchema,
|
|
7553
8043
|
dumpYDocToPlain,
|
|
8044
|
+
fieldUniqueConstraintName,
|
|
7554
8045
|
generateULID,
|
|
7555
8046
|
inferFieldType,
|
|
7556
8047
|
initJsBao,
|
|
@@ -7561,5 +8052,6 @@ export {
|
|
|
7561
8052
|
schemaToToml,
|
|
7562
8053
|
summarizePlainYDoc,
|
|
7563
8054
|
syncInferredMeta,
|
|
7564
|
-
syncModelMeta
|
|
8055
|
+
syncModelMeta,
|
|
8056
|
+
uniqueIndexMapName
|
|
7565
8057
|
};
|