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.cjs
CHANGED
|
@@ -366,21 +366,36 @@ var init_CursorManager = __esm({
|
|
|
366
366
|
return fields;
|
|
367
367
|
}
|
|
368
368
|
/**
|
|
369
|
-
* Build ORDER BY clause from sort specification
|
|
369
|
+
* Build ORDER BY clause from sort specification.
|
|
370
|
+
*
|
|
371
|
+
* `direction` is the pagination direction (1 = forward, -1 = backward).
|
|
372
|
+
* For backward pagination each field's effective ORDER BY direction is
|
|
373
|
+
* reversed so the cursor's flipped inequality (built by
|
|
374
|
+
* `buildPaginationConditions`) returns the page adjacent to the cursor,
|
|
375
|
+
* not the globally smallest/largest window. Without this,
|
|
376
|
+
* `field < cursor ... ORDER BY field ASC LIMIT n` returns the n smallest
|
|
377
|
+
* rows overall instead of the n rows just below the cursor.
|
|
378
|
+
*
|
|
379
|
+
* The returned `directions` array carries the *declared* per-field sort
|
|
380
|
+
* directions (unaffected by `direction`) — `buildPaginationConditions`
|
|
381
|
+
* needs the declared directions to pick each field's comparison operator.
|
|
370
382
|
*/
|
|
371
|
-
static buildOrderClause(sort, fieldFormatter = (field) => field) {
|
|
383
|
+
static buildOrderClause(sort, fieldFormatter = (field) => field, direction = 1) {
|
|
372
384
|
const fields = this.extractSortFields(sort);
|
|
373
385
|
const clauses = [];
|
|
374
386
|
const directions = [];
|
|
387
|
+
const sqlDir = (declared) => {
|
|
388
|
+
const effective = direction === -1 ? declared === 1 ? -1 : 1 : declared;
|
|
389
|
+
return effective === 1 ? "ASC" : "DESC";
|
|
390
|
+
};
|
|
375
391
|
if (sort && Object.keys(sort).length > 0) {
|
|
376
392
|
for (const [field, dir] of Object.entries(sort)) {
|
|
377
|
-
|
|
378
|
-
clauses.push(`${fieldFormatter(field)} ${sqlDirection}`);
|
|
393
|
+
clauses.push(`${fieldFormatter(field)} ${sqlDir(dir)}`);
|
|
379
394
|
directions.push(dir);
|
|
380
395
|
}
|
|
381
396
|
}
|
|
382
397
|
if (!sort || !sort.hasOwnProperty("id")) {
|
|
383
|
-
clauses.push(`${fieldFormatter("id")}
|
|
398
|
+
clauses.push(`${fieldFormatter("id")} ${sqlDir(1)}`);
|
|
384
399
|
directions.push(1);
|
|
385
400
|
} else if (sort && sort.hasOwnProperty("id")) {
|
|
386
401
|
directions.push(sort["id"]);
|
|
@@ -403,18 +418,27 @@ var init_CursorManager = __esm({
|
|
|
403
418
|
/**
|
|
404
419
|
* Generate next and previous cursors from result set
|
|
405
420
|
*/
|
|
406
|
-
static generateResultCursors(results, sortFields,
|
|
421
|
+
static generateResultCursors(results, sortFields, requestedDirection, hasMore, isFirstPage = false) {
|
|
407
422
|
if (results.length === 0) {
|
|
408
423
|
return {};
|
|
409
424
|
}
|
|
410
425
|
const cursors = {};
|
|
411
426
|
if (hasMore && results.length > 0) {
|
|
412
427
|
const lastResult = results[results.length - 1];
|
|
413
|
-
cursors.nextCursor = this.generateCursor(
|
|
428
|
+
cursors.nextCursor = this.generateCursor(
|
|
429
|
+
lastResult,
|
|
430
|
+
sortFields,
|
|
431
|
+
requestedDirection
|
|
432
|
+
);
|
|
414
433
|
}
|
|
415
434
|
if (results.length > 0 && !isFirstPage) {
|
|
416
435
|
const firstResult = results[0];
|
|
417
|
-
|
|
436
|
+
const opposite = requestedDirection === 1 ? -1 : 1;
|
|
437
|
+
cursors.prevCursor = this.generateCursor(
|
|
438
|
+
firstResult,
|
|
439
|
+
sortFields,
|
|
440
|
+
opposite
|
|
441
|
+
);
|
|
418
442
|
}
|
|
419
443
|
return cursors;
|
|
420
444
|
}
|
|
@@ -547,7 +571,8 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
547
571
|
const whereClause = this.translateFilter(filter);
|
|
548
572
|
const orderClause = CursorManager.buildOrderClause(
|
|
549
573
|
options?.sort,
|
|
550
|
-
(field) => this.getQuotedField(field)
|
|
574
|
+
(field) => this.getQuotedField(field),
|
|
575
|
+
options?.direction || 1
|
|
551
576
|
);
|
|
552
577
|
const limitClause = this.buildLimitClause(options);
|
|
553
578
|
const paginationClause = this.buildPaginationClause(
|
|
@@ -1183,9 +1208,109 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
1183
1208
|
}
|
|
1184
1209
|
});
|
|
1185
1210
|
|
|
1211
|
+
// src/utils/yjsValues.ts
|
|
1212
|
+
function isYjsComposite(value) {
|
|
1213
|
+
return value instanceof Y.Map || value instanceof Y.Array || value instanceof Y.Text;
|
|
1214
|
+
}
|
|
1215
|
+
function isStringSetYMap(value) {
|
|
1216
|
+
if (!(value instanceof Y.Map)) return false;
|
|
1217
|
+
for (const v of value.values()) {
|
|
1218
|
+
if (v !== true) return false;
|
|
1219
|
+
}
|
|
1220
|
+
return true;
|
|
1221
|
+
}
|
|
1222
|
+
function normalizeYjsValue(value) {
|
|
1223
|
+
if (value instanceof Y.Text) {
|
|
1224
|
+
return value.toString();
|
|
1225
|
+
}
|
|
1226
|
+
if (value instanceof Y.Map || value instanceof Y.Array) {
|
|
1227
|
+
return value.toJSON();
|
|
1228
|
+
}
|
|
1229
|
+
return value;
|
|
1230
|
+
}
|
|
1231
|
+
function normalizeYjsValueForStorage(value) {
|
|
1232
|
+
if (value instanceof Y.Text) {
|
|
1233
|
+
return value.toString();
|
|
1234
|
+
}
|
|
1235
|
+
if (value instanceof Y.Map || value instanceof Y.Array) {
|
|
1236
|
+
return JSON.stringify(value.toJSON());
|
|
1237
|
+
}
|
|
1238
|
+
return value;
|
|
1239
|
+
}
|
|
1240
|
+
var Y;
|
|
1241
|
+
var init_yjsValues = __esm({
|
|
1242
|
+
"src/utils/yjsValues.ts"() {
|
|
1243
|
+
"use strict";
|
|
1244
|
+
Y = __toESM(require("yjs"), 1);
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
// src/models/writeLifecycle.ts
|
|
1249
|
+
function ensureStringSetYMap(rm, fieldName) {
|
|
1250
|
+
const nested = rm.get(fieldName);
|
|
1251
|
+
if (nested instanceof Y2.Map) return nested;
|
|
1252
|
+
const fresh = new Y2.Map();
|
|
1253
|
+
if (nested && typeof nested === "object") {
|
|
1254
|
+
for (const [member, marker] of Object.entries(
|
|
1255
|
+
nested
|
|
1256
|
+
)) {
|
|
1257
|
+
if (Boolean(marker)) fresh.set(member, true);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
rm.set(fieldName, fresh);
|
|
1261
|
+
return fresh;
|
|
1262
|
+
}
|
|
1263
|
+
function applyStringSetDelta(target, delta) {
|
|
1264
|
+
if (delta.clear) {
|
|
1265
|
+
for (const member of Array.from(target.keys())) target.delete(member);
|
|
1266
|
+
}
|
|
1267
|
+
if (delta.remove) {
|
|
1268
|
+
for (const member of delta.remove) target.delete(String(member));
|
|
1269
|
+
}
|
|
1270
|
+
if (delta.add) {
|
|
1271
|
+
for (const member of delta.add) {
|
|
1272
|
+
const key = String(member);
|
|
1273
|
+
if (target.get(key) !== true) target.set(key, true);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function applyStringSetChange(recordYMap, fieldName, change) {
|
|
1278
|
+
const target = ensureStringSetYMap(recordYMap, fieldName);
|
|
1279
|
+
applyStringSetDelta(target, {
|
|
1280
|
+
add: change.additions,
|
|
1281
|
+
remove: change.removals
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
function registerFunctionDefaultGenerator(name, fn) {
|
|
1285
|
+
FUNCTION_DEFAULT_GENERATORS.set(name, fn);
|
|
1286
|
+
}
|
|
1287
|
+
function resolveLiteralOrFunctionDefault(rawDefault) {
|
|
1288
|
+
return typeof rawDefault === "function" ? rawDefault() : rawDefault;
|
|
1289
|
+
}
|
|
1290
|
+
var Y2, import_ulid, KNOWN_FIELD_TYPE_MAP, KNOWN_FIELD_TYPES, FUNCTION_DEFAULT_GENERATORS, UNKNOWN_FUNCTION_DEFAULT;
|
|
1291
|
+
var init_writeLifecycle = __esm({
|
|
1292
|
+
"src/models/writeLifecycle.ts"() {
|
|
1293
|
+
"use strict";
|
|
1294
|
+
Y2 = __toESM(require("yjs"), 1);
|
|
1295
|
+
import_ulid = require("ulid");
|
|
1296
|
+
KNOWN_FIELD_TYPE_MAP = {
|
|
1297
|
+
string: true,
|
|
1298
|
+
number: true,
|
|
1299
|
+
boolean: true,
|
|
1300
|
+
date: true,
|
|
1301
|
+
id: true,
|
|
1302
|
+
stringset: true
|
|
1303
|
+
};
|
|
1304
|
+
KNOWN_FIELD_TYPES = new Set(Object.keys(KNOWN_FIELD_TYPE_MAP));
|
|
1305
|
+
FUNCTION_DEFAULT_GENERATORS = /* @__PURE__ */ new Map();
|
|
1306
|
+
registerFunctionDefaultGenerator("generate_ulid", () => (0, import_ulid.ulid)());
|
|
1307
|
+
UNKNOWN_FUNCTION_DEFAULT = "$unknown_function";
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1186
1311
|
// src/models/metaSync.ts
|
|
1187
1312
|
function inferFieldType(value) {
|
|
1188
|
-
if (value
|
|
1313
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
1189
1314
|
switch (typeof value) {
|
|
1190
1315
|
case "string":
|
|
1191
1316
|
return "string";
|
|
@@ -1204,7 +1329,10 @@ function encodeDefault(value) {
|
|
|
1204
1329
|
if (value === void 0 || value === null) return void 0;
|
|
1205
1330
|
if (typeof value === "function") {
|
|
1206
1331
|
const name = KNOWN_FUNCTION_DEFAULTS.get(value);
|
|
1207
|
-
return name ? `$${name}` :
|
|
1332
|
+
return name ? `$${name}` : UNKNOWN_FUNCTION_DEFAULT;
|
|
1333
|
+
}
|
|
1334
|
+
if (typeof value === "string" && value.startsWith("$")) {
|
|
1335
|
+
return `$${value}`;
|
|
1208
1336
|
}
|
|
1209
1337
|
return value;
|
|
1210
1338
|
}
|
|
@@ -1230,7 +1358,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1230
1358
|
if (compoundConstraints.length > 0) {
|
|
1231
1359
|
let constraints = meta.get("_constraints");
|
|
1232
1360
|
if (!constraints) {
|
|
1233
|
-
constraints = new
|
|
1361
|
+
constraints = new Y3.Map();
|
|
1234
1362
|
meta.set("_constraints", constraints);
|
|
1235
1363
|
}
|
|
1236
1364
|
for (const constraint of compoundConstraints) {
|
|
@@ -1241,7 +1369,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1241
1369
|
if (relationships && Object.keys(relationships).length > 0) {
|
|
1242
1370
|
let rels = meta.get("_relationships");
|
|
1243
1371
|
if (!rels) {
|
|
1244
|
-
rels = new
|
|
1372
|
+
rels = new Y3.Map();
|
|
1245
1373
|
meta.set("_relationships", rels);
|
|
1246
1374
|
}
|
|
1247
1375
|
for (const [relName, relConfig] of Object.entries(relationships)) {
|
|
@@ -1253,7 +1381,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1253
1381
|
function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
1254
1382
|
let fieldMeta = metaMap.get(fieldName);
|
|
1255
1383
|
if (!fieldMeta) {
|
|
1256
|
-
fieldMeta = new
|
|
1384
|
+
fieldMeta = new Y3.Map();
|
|
1257
1385
|
metaMap.set(fieldName, fieldMeta);
|
|
1258
1386
|
}
|
|
1259
1387
|
setIfChanged(fieldMeta, "type", fieldOpts.type);
|
|
@@ -1261,6 +1389,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1261
1389
|
if (fieldOpts.unique) setIfChanged(fieldMeta, "unique", true);
|
|
1262
1390
|
if (fieldOpts.required) setIfChanged(fieldMeta, "required", true);
|
|
1263
1391
|
if (fieldOpts.autoAssign) setIfChanged(fieldMeta, "autoAssign", true);
|
|
1392
|
+
if (fieldOpts.autoStamp) setIfChanged(fieldMeta, "autoStamp", fieldOpts.autoStamp);
|
|
1264
1393
|
if (fieldOpts.maxLength !== void 0) setIfChanged(fieldMeta, "maxLength", fieldOpts.maxLength);
|
|
1265
1394
|
if (fieldOpts.maxCount !== void 0) setIfChanged(fieldMeta, "maxCount", fieldOpts.maxCount);
|
|
1266
1395
|
const encoded = encodeDefault(fieldOpts.default);
|
|
@@ -1269,7 +1398,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1269
1398
|
function syncConstraintMeta(constraintsMap, constraint) {
|
|
1270
1399
|
let cMeta = constraintsMap.get(constraint.name);
|
|
1271
1400
|
if (!cMeta) {
|
|
1272
|
-
cMeta = new
|
|
1401
|
+
cMeta = new Y3.Map();
|
|
1273
1402
|
constraintsMap.set(constraint.name, cMeta);
|
|
1274
1403
|
}
|
|
1275
1404
|
setIfChanged(cMeta, "type", "unique");
|
|
@@ -1279,7 +1408,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
|
|
|
1279
1408
|
function syncRelationshipMeta(relsMap, relName, relConfig) {
|
|
1280
1409
|
let relMeta = relsMap.get(relName);
|
|
1281
1410
|
if (!relMeta) {
|
|
1282
|
-
relMeta = new
|
|
1411
|
+
relMeta = new Y3.Map();
|
|
1283
1412
|
relsMap.set(relName, relMeta);
|
|
1284
1413
|
}
|
|
1285
1414
|
for (const [key, value] of Object.entries(relConfig)) {
|
|
@@ -1294,7 +1423,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
|
|
|
1294
1423
|
if (fieldName.startsWith("_")) continue;
|
|
1295
1424
|
let fieldMeta = meta.get(fieldName);
|
|
1296
1425
|
if (!fieldMeta) {
|
|
1297
|
-
fieldMeta = new
|
|
1426
|
+
fieldMeta = new Y3.Map();
|
|
1298
1427
|
meta.set(fieldName, fieldMeta);
|
|
1299
1428
|
}
|
|
1300
1429
|
if (!fieldMeta.has("type")) {
|
|
@@ -1310,16 +1439,44 @@ function setIfChanged(map, key, value) {
|
|
|
1310
1439
|
map.set(key, value);
|
|
1311
1440
|
}
|
|
1312
1441
|
}
|
|
1313
|
-
var
|
|
1442
|
+
var Y3, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
|
|
1314
1443
|
var init_metaSync = __esm({
|
|
1315
1444
|
"src/models/metaSync.ts"() {
|
|
1316
1445
|
"use strict";
|
|
1317
|
-
|
|
1446
|
+
Y3 = __toESM(require("yjs"), 1);
|
|
1447
|
+
init_yjsValues();
|
|
1448
|
+
init_writeLifecycle();
|
|
1318
1449
|
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
1319
1450
|
_syncedCache = /* @__PURE__ */ new WeakMap();
|
|
1320
1451
|
}
|
|
1321
1452
|
});
|
|
1322
1453
|
|
|
1454
|
+
// src/utils/uniqueIndex.ts
|
|
1455
|
+
function buildUniqueKey(recordData, fields) {
|
|
1456
|
+
const keyParts = [];
|
|
1457
|
+
for (const field of fields) {
|
|
1458
|
+
const value = recordData instanceof Y4.Map ? recordData.get(field) : recordData[field];
|
|
1459
|
+
if (value === null || value === void 0) {
|
|
1460
|
+
return null;
|
|
1461
|
+
}
|
|
1462
|
+
keyParts.push(String(value));
|
|
1463
|
+
}
|
|
1464
|
+
return keyParts.join("|");
|
|
1465
|
+
}
|
|
1466
|
+
function uniqueIndexMapName(modelName, constraintName) {
|
|
1467
|
+
return `_uniqueIdx_${modelName}_${constraintName}`;
|
|
1468
|
+
}
|
|
1469
|
+
function fieldUniqueConstraintName(modelName, fieldName) {
|
|
1470
|
+
return `${modelName}_${fieldName}_unique`;
|
|
1471
|
+
}
|
|
1472
|
+
var Y4;
|
|
1473
|
+
var init_uniqueIndex = __esm({
|
|
1474
|
+
"src/utils/uniqueIndex.ts"() {
|
|
1475
|
+
"use strict";
|
|
1476
|
+
Y4 = __toESM(require("yjs"), 1);
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1323
1480
|
// src/query/IncludeResolver.ts
|
|
1324
1481
|
var IncludeResolver_exports = {};
|
|
1325
1482
|
__export(IncludeResolver_exports, {
|
|
@@ -1678,7 +1835,13 @@ var init_IncludeResolver = __esm({
|
|
|
1678
1835
|
}
|
|
1679
1836
|
return null;
|
|
1680
1837
|
}
|
|
1681
|
-
/**
|
|
1838
|
+
/**
|
|
1839
|
+
* Apply a projection to a single record. Assumes the projection was already
|
|
1840
|
+
* validated upstream (mixed inclusion+exclusion rejected by the translator):
|
|
1841
|
+
* `isInclusion` treats "any value === 1" as inclusion mode. `id` and `type`
|
|
1842
|
+
* are system-identity fields and are always kept in BOTH modes (mirrors the
|
|
1843
|
+
* DatabaseDO `_applyProjection`); `_meta` stays excludable.
|
|
1844
|
+
*/
|
|
1682
1845
|
_applyProjection(record, projection) {
|
|
1683
1846
|
const fields = Object.entries(projection);
|
|
1684
1847
|
if (fields.length === 0) return record;
|
|
@@ -1697,7 +1860,7 @@ var init_IncludeResolver = __esm({
|
|
|
1697
1860
|
} else {
|
|
1698
1861
|
const result = { ...record };
|
|
1699
1862
|
for (const [f, v] of fields) {
|
|
1700
|
-
if (v === 0) delete result[f];
|
|
1863
|
+
if (v === 0 && f !== "id" && f !== "type") delete result[f];
|
|
1701
1864
|
}
|
|
1702
1865
|
return result;
|
|
1703
1866
|
}
|
|
@@ -1717,20 +1880,23 @@ __export(BaseModel_exports, {
|
|
|
1717
1880
|
generateULID: () => generateULID
|
|
1718
1881
|
});
|
|
1719
1882
|
function generateULID() {
|
|
1720
|
-
return (0,
|
|
1883
|
+
return (0, import_ulid2.ulid)();
|
|
1721
1884
|
}
|
|
1722
|
-
var
|
|
1885
|
+
var Y5, import_ulid2, LogLevel, Logger, UniqueConstraintViolationError, RecordNotFoundError, SCHEMA_ACCESSORS_KEY, BaseModel;
|
|
1723
1886
|
var init_BaseModel = __esm({
|
|
1724
1887
|
"src/models/BaseModel.ts"() {
|
|
1725
1888
|
"use strict";
|
|
1726
|
-
|
|
1727
|
-
|
|
1889
|
+
Y5 = __toESM(require("yjs"), 1);
|
|
1890
|
+
import_ulid2 = require("ulid");
|
|
1728
1891
|
init_StringSet();
|
|
1729
1892
|
init_documentTypes();
|
|
1730
1893
|
init_DocumentQueryTranslator();
|
|
1731
1894
|
init_CursorManager();
|
|
1732
1895
|
init_sql();
|
|
1733
1896
|
init_metaSync();
|
|
1897
|
+
init_writeLifecycle();
|
|
1898
|
+
init_yjsValues();
|
|
1899
|
+
init_uniqueIndex();
|
|
1734
1900
|
LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
1735
1901
|
LogLevel2[LogLevel2["SILENT"] = 0] = "SILENT";
|
|
1736
1902
|
LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
|
|
@@ -2115,7 +2281,9 @@ var init_BaseModel = __esm({
|
|
|
2115
2281
|
this.setValue(fieldKey, data[fieldKey]);
|
|
2116
2282
|
this._constructorProvidedFields.add(fieldKey);
|
|
2117
2283
|
} else if (fieldOptions.default !== void 0) {
|
|
2118
|
-
const defaultValue =
|
|
2284
|
+
const defaultValue = resolveLiteralOrFunctionDefault(
|
|
2285
|
+
fieldOptions.default
|
|
2286
|
+
);
|
|
2119
2287
|
this.setValue(fieldKey, defaultValue);
|
|
2120
2288
|
}
|
|
2121
2289
|
}
|
|
@@ -2239,7 +2407,30 @@ var init_BaseModel = __esm({
|
|
|
2239
2407
|
if (verboseEnabled) {
|
|
2240
2408
|
Logger.verbose(`[getFromYjs] Returning field value: ${fieldValue}`);
|
|
2241
2409
|
}
|
|
2242
|
-
return fieldValue;
|
|
2410
|
+
return this.normalizeNonStringSetValue(fieldKey, fieldValue, schema, modelName);
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Defensive normalization for the read/sync boundary (issue #625).
|
|
2414
|
+
*
|
|
2415
|
+
* Stringset fields are owned by getStringSetFromYjs (which expects the
|
|
2416
|
+
* raw Y.Map / legacy plain-object shape — #561 / #601), so they pass
|
|
2417
|
+
* through untouched. For any other field, a composite Yjs primitive
|
|
2418
|
+
* (Y.Map / Y.Array / Y.Text) must never reach user code or the DB layer
|
|
2419
|
+
* raw: normalize it to its plain-JS projection (toJSON / toString) and
|
|
2420
|
+
* warn, rather than throwing. Scalars pass through unchanged.
|
|
2421
|
+
*/
|
|
2422
|
+
normalizeNonStringSetValue(fieldKey, value, schema, modelName) {
|
|
2423
|
+
const fieldOptions = schema?.fields?.get(fieldKey);
|
|
2424
|
+
if (fieldOptions?.type === "stringset") {
|
|
2425
|
+
return value;
|
|
2426
|
+
}
|
|
2427
|
+
if (isYjsComposite(value)) {
|
|
2428
|
+
Logger.warn(
|
|
2429
|
+
`[${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.`
|
|
2430
|
+
);
|
|
2431
|
+
return normalizeYjsValue(value);
|
|
2432
|
+
}
|
|
2433
|
+
return value;
|
|
2243
2434
|
}
|
|
2244
2435
|
getValue(fieldKey) {
|
|
2245
2436
|
const verboseEnabled = Logger.getLogLevel() >= 5 /* VERBOSE */;
|
|
@@ -2384,7 +2575,7 @@ var init_BaseModel = __esm({
|
|
|
2384
2575
|
}
|
|
2385
2576
|
getStringSetFromYjs(fieldName) {
|
|
2386
2577
|
const yjsData = this.getFromYjs(fieldName);
|
|
2387
|
-
if (yjsData instanceof
|
|
2578
|
+
if (yjsData instanceof Y5.Map) {
|
|
2388
2579
|
return Array.from(yjsData.keys());
|
|
2389
2580
|
}
|
|
2390
2581
|
if (yjsData && typeof yjsData === "object") {
|
|
@@ -2492,7 +2683,7 @@ var init_BaseModel = __esm({
|
|
|
2492
2683
|
}
|
|
2493
2684
|
const extractStringSetValues = (value) => {
|
|
2494
2685
|
if (!value) return [];
|
|
2495
|
-
if (value instanceof
|
|
2686
|
+
if (value instanceof Y5.Map) {
|
|
2496
2687
|
return Array.from(value.entries()).filter(([, isMember]) => Boolean(isMember)).map(([key]) => key);
|
|
2497
2688
|
}
|
|
2498
2689
|
if (Array.isArray(value)) {
|
|
@@ -2509,7 +2700,7 @@ var init_BaseModel = __esm({
|
|
|
2509
2700
|
if (!recordId) continue;
|
|
2510
2701
|
let itemData;
|
|
2511
2702
|
const stringSetValuesByField = {};
|
|
2512
|
-
if (recordData instanceof
|
|
2703
|
+
if (recordData instanceof Y5.Map) {
|
|
2513
2704
|
itemData = {};
|
|
2514
2705
|
const unknownFields = [];
|
|
2515
2706
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -2625,20 +2816,10 @@ var init_BaseModel = __esm({
|
|
|
2625
2816
|
Logger.verbose(
|
|
2626
2817
|
`[${this.name}] Setting up YMap observer for document ${docId}/${modelName}...`
|
|
2627
2818
|
);
|
|
2628
|
-
const
|
|
2629
|
-
const keyParts = [];
|
|
2630
|
-
for (const field of fields) {
|
|
2631
|
-
const value = recordData instanceof Y2.Map ? recordData.get(field) : recordData[field];
|
|
2632
|
-
if (value === null || value === void 0) {
|
|
2633
|
-
return null;
|
|
2634
|
-
}
|
|
2635
|
-
keyParts.push(String(value));
|
|
2636
|
-
}
|
|
2637
|
-
return keyParts.join("|");
|
|
2638
|
-
};
|
|
2819
|
+
const buildUniqueKey2 = (recordData, fields) => buildUniqueKey(recordData, fields);
|
|
2639
2820
|
const extractItemData = (key, recordData) => {
|
|
2640
2821
|
let itemData;
|
|
2641
|
-
if (recordData instanceof
|
|
2822
|
+
if (recordData instanceof Y5.Map) {
|
|
2642
2823
|
itemData = {};
|
|
2643
2824
|
const unknownFields = [];
|
|
2644
2825
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -2651,7 +2832,14 @@ var init_BaseModel = __esm({
|
|
|
2651
2832
|
continue;
|
|
2652
2833
|
}
|
|
2653
2834
|
if (value !== void 0) {
|
|
2654
|
-
|
|
2835
|
+
if (isYjsComposite(value)) {
|
|
2836
|
+
Logger.warn(
|
|
2837
|
+
`[${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.`
|
|
2838
|
+
);
|
|
2839
|
+
itemData[fieldKey] = normalizeYjsValueForStorage(value);
|
|
2840
|
+
} else {
|
|
2841
|
+
itemData[fieldKey] = value;
|
|
2842
|
+
}
|
|
2655
2843
|
}
|
|
2656
2844
|
}
|
|
2657
2845
|
if (unknownFields.length > 0) {
|
|
@@ -2674,7 +2862,14 @@ var init_BaseModel = __esm({
|
|
|
2674
2862
|
continue;
|
|
2675
2863
|
}
|
|
2676
2864
|
if (value !== void 0) {
|
|
2677
|
-
|
|
2865
|
+
if (isYjsComposite(value)) {
|
|
2866
|
+
Logger.warn(
|
|
2867
|
+
`[${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.`
|
|
2868
|
+
);
|
|
2869
|
+
filteredData[fieldKey] = normalizeYjsValueForStorage(value);
|
|
2870
|
+
} else {
|
|
2871
|
+
filteredData[fieldKey] = value;
|
|
2872
|
+
}
|
|
2678
2873
|
}
|
|
2679
2874
|
}
|
|
2680
2875
|
if (unknownFields.length > 0) {
|
|
@@ -2708,7 +2903,7 @@ var init_BaseModel = __esm({
|
|
|
2708
2903
|
const recordsByUniqueKey = /* @__PURE__ */ new Map();
|
|
2709
2904
|
for (const [recordId, recordData] of allRecords.entries()) {
|
|
2710
2905
|
if (recordIdsToDiscard.has(recordId)) continue;
|
|
2711
|
-
const uniqueKey =
|
|
2906
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2712
2907
|
if (uniqueKey === null) continue;
|
|
2713
2908
|
if (!recordsByUniqueKey.has(uniqueKey)) {
|
|
2714
2909
|
recordsByUniqueKey.set(uniqueKey, []);
|
|
@@ -2754,7 +2949,7 @@ var init_BaseModel = __esm({
|
|
|
2754
2949
|
if (!recordData || !key) continue;
|
|
2755
2950
|
const itemData = extractItemData(key, recordData);
|
|
2756
2951
|
if (!itemData) continue;
|
|
2757
|
-
if (change.action === "add" && recordData instanceof
|
|
2952
|
+
if (change.action === "add" && recordData instanceof Y5.Map) {
|
|
2758
2953
|
Logger.verbose(
|
|
2759
2954
|
`[${this.name}] Setting up observer on newly added nested YMap for record ${key} in document ${docId}`
|
|
2760
2955
|
);
|
|
@@ -2852,9 +3047,12 @@ var init_BaseModel = __esm({
|
|
|
2852
3047
|
const recordData = documentYMap.get(idToDiscard);
|
|
2853
3048
|
if (!recordData) continue;
|
|
2854
3049
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
2855
|
-
const uniqueKey =
|
|
3050
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2856
3051
|
if (uniqueKey === null) continue;
|
|
2857
|
-
const constraintMapName =
|
|
3052
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3053
|
+
modelName,
|
|
3054
|
+
constraint.name
|
|
3055
|
+
);
|
|
2858
3056
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
2859
3057
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
2860
3058
|
if (currentIndexValue === idToDiscard) {
|
|
@@ -2872,11 +3070,14 @@ var init_BaseModel = __esm({
|
|
|
2872
3070
|
}, `conflict-resolution-${modelName}-${docId}`);
|
|
2873
3071
|
yDoc.transact(() => {
|
|
2874
3072
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
2875
|
-
const constraintMapName =
|
|
3073
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3074
|
+
modelName,
|
|
3075
|
+
constraint.name
|
|
3076
|
+
);
|
|
2876
3077
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
2877
3078
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
2878
3079
|
if (!recordData) continue;
|
|
2879
|
-
const uniqueKey =
|
|
3080
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2880
3081
|
if (uniqueKey === null) continue;
|
|
2881
3082
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
2882
3083
|
if (currentIndexValue !== recordId) {
|
|
@@ -2899,7 +3100,7 @@ var init_BaseModel = __esm({
|
|
|
2899
3100
|
`[${this.name}] Setting up observers on existing nested YMaps for ${modelName}/${docId}...`
|
|
2900
3101
|
);
|
|
2901
3102
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
2902
|
-
if (recordData instanceof
|
|
3103
|
+
if (recordData instanceof Y5.Map) {
|
|
2903
3104
|
Logger.verbose(
|
|
2904
3105
|
`[${this.name}] Setting up observer on existing nested YMap for record ${recordId} in document ${docId}`
|
|
2905
3106
|
);
|
|
@@ -3084,6 +3285,11 @@ var init_BaseModel = __esm({
|
|
|
3084
3285
|
Logger.debug(
|
|
3085
3286
|
`[_diffWithYjsData] Field '${key}' - Y.js value: ${yjsValue}, local value: ${localValue}`
|
|
3086
3287
|
);
|
|
3288
|
+
if (isYjsComposite(yjsValue)) {
|
|
3289
|
+
Logger.warn(
|
|
3290
|
+
`[${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).`
|
|
3291
|
+
);
|
|
3292
|
+
}
|
|
3087
3293
|
if (yjsValue === void 0) {
|
|
3088
3294
|
Logger.debug(
|
|
3089
3295
|
`[_diffWithYjsData] Field '${key}' not in Y.js, adding to 'added'`
|
|
@@ -3120,26 +3326,7 @@ var init_BaseModel = __esm({
|
|
|
3120
3326
|
* if the field hasn't been touched since the wire-format change in #561.
|
|
3121
3327
|
*/
|
|
3122
3328
|
applyStringSetChangeToYMap(recordYMap, fieldName, change) {
|
|
3123
|
-
|
|
3124
|
-
if (!(nested instanceof Y2.Map)) {
|
|
3125
|
-
const migrated = new Y2.Map();
|
|
3126
|
-
if (nested && typeof nested === "object") {
|
|
3127
|
-
for (const [member, marker] of Object.entries(
|
|
3128
|
-
nested
|
|
3129
|
-
)) {
|
|
3130
|
-
if (Boolean(marker)) migrated.set(member, true);
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
recordYMap.set(fieldName, migrated);
|
|
3134
|
-
nested = migrated;
|
|
3135
|
-
}
|
|
3136
|
-
const target = nested;
|
|
3137
|
-
for (const member of change.additions) {
|
|
3138
|
-
target.set(member, true);
|
|
3139
|
-
}
|
|
3140
|
-
for (const member of change.removals) {
|
|
3141
|
-
target.delete(member);
|
|
3142
|
-
}
|
|
3329
|
+
applyStringSetChange(recordYMap, fieldName, change);
|
|
3143
3330
|
}
|
|
3144
3331
|
/**
|
|
3145
3332
|
* Deep equality check for comparing field values
|
|
@@ -3309,6 +3496,29 @@ var init_BaseModel = __esm({
|
|
|
3309
3496
|
);
|
|
3310
3497
|
throw new Error("Cannot save item without an id. Ensure id is set.");
|
|
3311
3498
|
}
|
|
3499
|
+
const preTransactCreateStamps = /* @__PURE__ */ new Set();
|
|
3500
|
+
{
|
|
3501
|
+
const existingRecord = targetYMap.get(this.id);
|
|
3502
|
+
const isNew = !existingRecord;
|
|
3503
|
+
for (const [fieldKey, fieldOptions] of schema.fields.entries()) {
|
|
3504
|
+
const stamp = fieldOptions.autoStamp;
|
|
3505
|
+
if (!stamp) continue;
|
|
3506
|
+
const callerExplicit = this.hasLocalChange(fieldKey);
|
|
3507
|
+
if (callerExplicit) {
|
|
3508
|
+
const localValue = this._localChanges[fieldKey];
|
|
3509
|
+
if (localValue !== null && localValue !== void 0) continue;
|
|
3510
|
+
}
|
|
3511
|
+
if (stamp === "create") {
|
|
3512
|
+
if (!isNew) continue;
|
|
3513
|
+
const persisted = fieldKey === "id" ? this.id : this.getValue(fieldKey);
|
|
3514
|
+
if (persisted !== null && persisted !== void 0) continue;
|
|
3515
|
+
}
|
|
3516
|
+
this[fieldKey] = Date.now();
|
|
3517
|
+
if (stamp === "create") {
|
|
3518
|
+
preTransactCreateStamps.add(fieldKey);
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3312
3522
|
this.validateBeforeSave();
|
|
3313
3523
|
Logger.debug(`[${modelName}.save] About to get current JS state`);
|
|
3314
3524
|
Logger.debug(`[${modelName}.save] _localChanges:`, this._localChanges);
|
|
@@ -3318,6 +3528,34 @@ var init_BaseModel = __esm({
|
|
|
3318
3528
|
`[${modelName}.save] getCurrentJSState() returned:`,
|
|
3319
3529
|
dataToSave
|
|
3320
3530
|
);
|
|
3531
|
+
const uniqueMissConflicts = /* @__PURE__ */ new Map();
|
|
3532
|
+
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
3533
|
+
const newUniqueKey = this._buildUniqueKey(
|
|
3534
|
+
constraint.fields,
|
|
3535
|
+
dataToSave,
|
|
3536
|
+
modelName,
|
|
3537
|
+
constraint.name
|
|
3538
|
+
);
|
|
3539
|
+
if (newUniqueKey === null) {
|
|
3540
|
+
continue;
|
|
3541
|
+
}
|
|
3542
|
+
const constraintMap = yDoc.getMap(
|
|
3543
|
+
uniqueIndexMapName(modelName, constraint.name)
|
|
3544
|
+
);
|
|
3545
|
+
if (!constraintMap.get(newUniqueKey)) {
|
|
3546
|
+
const constraintKeyValues = constraint.fields.map(
|
|
3547
|
+
(field) => dataToSave[field]
|
|
3548
|
+
);
|
|
3549
|
+
const conflictingRecord = await modelConstructor._resolveByUniqueFallback(
|
|
3550
|
+
constraint,
|
|
3551
|
+
constraintKeyValues,
|
|
3552
|
+
{ excludeId: this.id }
|
|
3553
|
+
);
|
|
3554
|
+
if (conflictingRecord) {
|
|
3555
|
+
uniqueMissConflicts.set(constraint.name, conflictingRecord.id);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3321
3559
|
await yDoc.transact(async () => {
|
|
3322
3560
|
if (upsertOnConstraint && upsertOnKey) {
|
|
3323
3561
|
const constraintMapName = `_uniqueIdx_${modelName}_${upsertOnConstraint.name}`;
|
|
@@ -3339,6 +3577,14 @@ var init_BaseModel = __esm({
|
|
|
3339
3577
|
}
|
|
3340
3578
|
}
|
|
3341
3579
|
}
|
|
3580
|
+
for (const fieldKey of preTransactCreateStamps) {
|
|
3581
|
+
if (this._localChanges && fieldKey in this._localChanges) {
|
|
3582
|
+
delete this._localChanges[fieldKey];
|
|
3583
|
+
}
|
|
3584
|
+
if (fieldKey in dataToSave) {
|
|
3585
|
+
delete dataToSave[fieldKey];
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3342
3588
|
}
|
|
3343
3589
|
}
|
|
3344
3590
|
if (!targetYMap) {
|
|
@@ -3351,7 +3597,7 @@ var init_BaseModel = __esm({
|
|
|
3351
3597
|
Logger.debug(`[${modelName}.save] isUpdate: ${isUpdate}`);
|
|
3352
3598
|
Logger.debug(`[${modelName}.save] recordYMap exists: ${!!recordYMap}`);
|
|
3353
3599
|
if (!recordYMap) {
|
|
3354
|
-
recordYMap = new
|
|
3600
|
+
recordYMap = new Y5.Map();
|
|
3355
3601
|
Logger.verbose(
|
|
3356
3602
|
`[${modelName}] Creating new nested YMap for record ${this.id} in document ${targetDocId || "legacy"}`
|
|
3357
3603
|
);
|
|
@@ -3403,7 +3649,10 @@ var init_BaseModel = __esm({
|
|
|
3403
3649
|
);
|
|
3404
3650
|
continue;
|
|
3405
3651
|
}
|
|
3406
|
-
const constraintMapName =
|
|
3652
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3653
|
+
modelName,
|
|
3654
|
+
constraint.name
|
|
3655
|
+
);
|
|
3407
3656
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
3408
3657
|
Logger.verbose(
|
|
3409
3658
|
`[${modelName}] Save Check (Item '${this.id}'): GET from '${constraintMapName}' with Key '${newUniqueKey.substring(
|
|
@@ -3432,6 +3681,26 @@ var init_BaseModel = __esm({
|
|
|
3432
3681
|
this.id,
|
|
3433
3682
|
existingRecordIdWithNewKey
|
|
3434
3683
|
);
|
|
3684
|
+
} else if (!existingRecordIdWithNewKey) {
|
|
3685
|
+
const conflictingRecordId = uniqueMissConflicts.get(constraint.name);
|
|
3686
|
+
if (conflictingRecordId && conflictingRecordId !== this.id) {
|
|
3687
|
+
Logger.verbose(
|
|
3688
|
+
`[${modelName}] Save Check (Item '${this.id}'): index miss for '${constraint.name}', but engine fallback found conflicting index-less record ${conflictingRecordId}.`
|
|
3689
|
+
);
|
|
3690
|
+
throw new UniqueConstraintViolationError(
|
|
3691
|
+
`Unique constraint '${constraint.name}' violated for model '${modelName}' on fields [${constraint.fields.join(
|
|
3692
|
+
", "
|
|
3693
|
+
)}]. Attempted ID: ${this.id}. Value(s) starting with '${newUniqueKey.substring(
|
|
3694
|
+
0,
|
|
3695
|
+
50
|
|
3696
|
+
)}...' already exist for index-less record ID ${conflictingRecordId}.`,
|
|
3697
|
+
modelName,
|
|
3698
|
+
constraint.name,
|
|
3699
|
+
constraint.fields,
|
|
3700
|
+
this.id,
|
|
3701
|
+
conflictingRecordId
|
|
3702
|
+
);
|
|
3703
|
+
}
|
|
3435
3704
|
}
|
|
3436
3705
|
}
|
|
3437
3706
|
if (isUpdate && oldData) {
|
|
@@ -3775,34 +4044,46 @@ var init_BaseModel = __esm({
|
|
|
3775
4044
|
const modelName = schema.options.name;
|
|
3776
4045
|
Logger.verbose(`[${modelName}] Executing query:`, { filter, options });
|
|
3777
4046
|
const hasIncludes = options?.include && options.include.length > 0;
|
|
3778
|
-
const
|
|
4047
|
+
const requestedLimit = options?.limit;
|
|
4048
|
+
const overFetch = typeof requestedLimit === "number" && requestedLimit > 0;
|
|
4049
|
+
let translatorOptions = hasIncludes && options?.projection ? { ...options, projection: void 0 } : options;
|
|
4050
|
+
if (overFetch) {
|
|
4051
|
+
translatorOptions = {
|
|
4052
|
+
...translatorOptions || {},
|
|
4053
|
+
limit: requestedLimit + 1
|
|
4054
|
+
};
|
|
4055
|
+
}
|
|
3779
4056
|
const translator = new DocumentQueryTranslator(modelName, schema.fields);
|
|
3780
4057
|
const translatedQuery = translator.translateFind(filter, translatorOptions);
|
|
3781
4058
|
Logger.verbose(`[${modelName}] Translated SQL:`, translatedQuery.sql);
|
|
3782
4059
|
Logger.verbose(`[${modelName}] SQL params:`, translatedQuery.params);
|
|
3783
|
-
|
|
4060
|
+
let results = await _BaseModel.dbInstance.query(
|
|
3784
4061
|
translatedQuery.sql,
|
|
3785
4062
|
translatedQuery.params
|
|
3786
4063
|
);
|
|
3787
4064
|
Logger.verbose(`[${modelName}] Query returned ${results.length} results`);
|
|
4065
|
+
let hasMore = false;
|
|
4066
|
+
if (overFetch && results.length > requestedLimit) {
|
|
4067
|
+
hasMore = true;
|
|
4068
|
+
results = results.slice(0, requestedLimit);
|
|
4069
|
+
}
|
|
3788
4070
|
if (options?.include && options.include.length > 0) {
|
|
3789
4071
|
const { IncludeResolver: IncludeResolver2 } = await Promise.resolve().then(() => (init_IncludeResolver(), IncludeResolver_exports));
|
|
3790
4072
|
const resolver = new IncludeResolver2(_BaseModel.dbInstance);
|
|
3791
4073
|
await resolver.resolve(results, options.include, 0, modelName);
|
|
3792
4074
|
}
|
|
3793
|
-
const requestedLimit = options?.limit;
|
|
3794
|
-
const hasMore = CursorManager.hasMoreResults(
|
|
3795
|
-
requestedLimit,
|
|
3796
|
-
results.length
|
|
3797
|
-
);
|
|
3798
4075
|
const isFirstPage = !options?.uniqueStartKey;
|
|
4076
|
+
const direction = options?.direction || 1;
|
|
3799
4077
|
const cursors = CursorManager.generateResultCursors(
|
|
3800
4078
|
results,
|
|
3801
4079
|
translatedQuery.sortFields,
|
|
3802
|
-
|
|
4080
|
+
direction,
|
|
3803
4081
|
hasMore,
|
|
3804
4082
|
isFirstPage
|
|
3805
4083
|
);
|
|
4084
|
+
if (direction === -1) {
|
|
4085
|
+
results.reverse();
|
|
4086
|
+
}
|
|
3806
4087
|
let transformedData;
|
|
3807
4088
|
if (options?.projection && Object.keys(options.projection).length > 0) {
|
|
3808
4089
|
transformedData = results.map((row) => {
|
|
@@ -4378,7 +4659,7 @@ var init_BaseModel = __esm({
|
|
|
4378
4659
|
);
|
|
4379
4660
|
}
|
|
4380
4661
|
for (const [recordId, recordYMap] of documentYMap.entries()) {
|
|
4381
|
-
if (recordYMap instanceof
|
|
4662
|
+
if (recordYMap instanceof Y5.Map) {
|
|
4382
4663
|
const instance = new this({ id: recordId });
|
|
4383
4664
|
instance._metaDocId = docId;
|
|
4384
4665
|
const connectedDoc = modelConstructor.connectedDocuments.get(docId);
|
|
@@ -4403,6 +4684,50 @@ var init_BaseModel = __esm({
|
|
|
4403
4684
|
}
|
|
4404
4685
|
return allInstances;
|
|
4405
4686
|
}
|
|
4687
|
+
/**
|
|
4688
|
+
* Resolve a record by its unique-constraint field values via an engine query.
|
|
4689
|
+
* Used as a fallback when the `_uniqueIdx_<model>_<constraint>` map has no
|
|
4690
|
+
* entry for a key.
|
|
4691
|
+
*
|
|
4692
|
+
* The index map is a cache over the SQLite projection, not the source of
|
|
4693
|
+
* truth: every record is loaded into the projection on `initializeForDocument`
|
|
4694
|
+
* regardless of index presence, but a record written by a client predating
|
|
4695
|
+
* index maintenance (legacy data) is missing from the index. Such a record
|
|
4696
|
+
* silently escapes both `upsertByUnique` lookups and the `save()` uniqueness
|
|
4697
|
+
* check. A query over the constraint fields finds it reliably.
|
|
4698
|
+
*
|
|
4699
|
+
* Contract: RESOLVE ONLY. Returns the owning record (with `_metaDocId` set to
|
|
4700
|
+
* the document that holds it, via `queryOne`) or `null`. Callers decide
|
|
4701
|
+
* whether to heal the missing index entry (write paths) and do their own
|
|
4702
|
+
* logging — this method never mutates the index, so read paths stay
|
|
4703
|
+
* read-only. Mirrors `buildUniqueKey`: a null/undefined value in any
|
|
4704
|
+
* constraint field means the record does not participate in the constraint,
|
|
4705
|
+
* so no query runs.
|
|
4706
|
+
*/
|
|
4707
|
+
static async _resolveByUniqueFallback(constraint, keyValues, options) {
|
|
4708
|
+
if (!_BaseModel.dbInstance) {
|
|
4709
|
+
return null;
|
|
4710
|
+
}
|
|
4711
|
+
const filter = {};
|
|
4712
|
+
for (let i = 0; i < constraint.fields.length; i++) {
|
|
4713
|
+
const value = keyValues[i];
|
|
4714
|
+
if (value === null || value === void 0) {
|
|
4715
|
+
return null;
|
|
4716
|
+
}
|
|
4717
|
+
filter[constraint.fields[i]] = value;
|
|
4718
|
+
}
|
|
4719
|
+
if (options?.excludeId) {
|
|
4720
|
+
if ("id" in filter) {
|
|
4721
|
+
if (filter.id === options.excludeId) {
|
|
4722
|
+
return null;
|
|
4723
|
+
}
|
|
4724
|
+
} else {
|
|
4725
|
+
filter.id = { $ne: options.excludeId };
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
const found = await this.queryOne(filter);
|
|
4729
|
+
return found ?? null;
|
|
4730
|
+
}
|
|
4406
4731
|
static async findByUnique(constraintName, value) {
|
|
4407
4732
|
const modelConstructor = this;
|
|
4408
4733
|
const schema = modelConstructor.getSchema();
|
|
@@ -4441,7 +4766,10 @@ var init_BaseModel = __esm({
|
|
|
4441
4766
|
const documentInfo = modelConstructor.connectedDocuments.get(docId);
|
|
4442
4767
|
if (!documentInfo) continue;
|
|
4443
4768
|
const yDoc = documentInfo.yDoc;
|
|
4444
|
-
const constraintMapName2 =
|
|
4769
|
+
const constraintMapName2 = uniqueIndexMapName(
|
|
4770
|
+
currentModelName,
|
|
4771
|
+
constraint.name
|
|
4772
|
+
);
|
|
4445
4773
|
const constraintMap2 = yDoc.getMap(constraintMapName2);
|
|
4446
4774
|
const recordId2 = constraintMap2.get(uniqueKeyString);
|
|
4447
4775
|
if (recordId2) {
|
|
@@ -4449,7 +4777,7 @@ var init_BaseModel = __esm({
|
|
|
4449
4777
|
const documentYMap = modelConstructor.documentYMaps.get(documentYMapKey);
|
|
4450
4778
|
if (documentYMap) {
|
|
4451
4779
|
const recordYMap2 = documentYMap.get(recordId2);
|
|
4452
|
-
if (recordYMap2 && recordYMap2 instanceof
|
|
4780
|
+
if (recordYMap2 && recordYMap2 instanceof Y5.Map) {
|
|
4453
4781
|
const instance = new modelConstructor({
|
|
4454
4782
|
id: recordId2
|
|
4455
4783
|
});
|
|
@@ -4463,6 +4791,16 @@ var init_BaseModel = __esm({
|
|
|
4463
4791
|
}
|
|
4464
4792
|
}
|
|
4465
4793
|
}
|
|
4794
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4795
|
+
constraint,
|
|
4796
|
+
keyValues
|
|
4797
|
+
);
|
|
4798
|
+
if (fallbackRecord) {
|
|
4799
|
+
Logger.verbose(
|
|
4800
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
4801
|
+
);
|
|
4802
|
+
return fallbackRecord;
|
|
4803
|
+
}
|
|
4466
4804
|
Logger.verbose(
|
|
4467
4805
|
`[${currentModelName}.findByUnique] Record not found in any connected document`
|
|
4468
4806
|
);
|
|
@@ -4476,17 +4814,30 @@ var init_BaseModel = __esm({
|
|
|
4476
4814
|
`[${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.`
|
|
4477
4815
|
);
|
|
4478
4816
|
}
|
|
4479
|
-
const constraintMapName =
|
|
4817
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4818
|
+
currentModelName,
|
|
4819
|
+
constraint.name
|
|
4820
|
+
);
|
|
4480
4821
|
const constraintMap = legacyDocInfo.yDoc.getMap(constraintMapName);
|
|
4481
4822
|
const recordId = constraintMap.get(uniqueKeyString);
|
|
4482
4823
|
if (!recordId) {
|
|
4824
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4825
|
+
constraint,
|
|
4826
|
+
keyValues
|
|
4827
|
+
);
|
|
4828
|
+
if (fallbackRecord) {
|
|
4829
|
+
Logger.verbose(
|
|
4830
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
4831
|
+
);
|
|
4832
|
+
return fallbackRecord;
|
|
4833
|
+
}
|
|
4483
4834
|
return null;
|
|
4484
4835
|
}
|
|
4485
4836
|
const recordYMap = legacyDocumentMap.get(recordId);
|
|
4486
4837
|
if (!recordYMap) {
|
|
4487
4838
|
return null;
|
|
4488
4839
|
}
|
|
4489
|
-
if (recordYMap instanceof
|
|
4840
|
+
if (recordYMap instanceof Y5.Map) {
|
|
4490
4841
|
const instance = new modelConstructor({
|
|
4491
4842
|
id: recordId
|
|
4492
4843
|
});
|
|
@@ -4544,7 +4895,10 @@ var init_BaseModel = __esm({
|
|
|
4544
4895
|
`[${currentModelName}] upsertByUnique for '${constraintName}': unique key cannot be built due to null/undefined values. Upsert cannot proceed reliably.`
|
|
4545
4896
|
);
|
|
4546
4897
|
}
|
|
4547
|
-
const constraintMapName =
|
|
4898
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4899
|
+
currentModelName,
|
|
4900
|
+
constraint.name
|
|
4901
|
+
);
|
|
4548
4902
|
let existingRecord = null;
|
|
4549
4903
|
if (connectedDocuments && connectedDocuments.size > 0) {
|
|
4550
4904
|
for (const docId of connectedDocuments.keys()) {
|
|
@@ -4562,6 +4916,33 @@ var init_BaseModel = __esm({
|
|
|
4562
4916
|
}
|
|
4563
4917
|
} else {
|
|
4564
4918
|
}
|
|
4919
|
+
if (!existingRecord) {
|
|
4920
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4921
|
+
constraint,
|
|
4922
|
+
keyValuesForLookup
|
|
4923
|
+
);
|
|
4924
|
+
if (fallbackRecord) {
|
|
4925
|
+
existingRecord = fallbackRecord;
|
|
4926
|
+
const healDocId = fallbackRecord._metaDocId;
|
|
4927
|
+
const healDocInfo = healDocId ? connectedDocuments.get(healDocId) : void 0;
|
|
4928
|
+
const canHeal = !!healDocInfo && healDocInfo.permissionHint !== "read";
|
|
4929
|
+
if (healDocInfo && canHeal) {
|
|
4930
|
+
healDocInfo.yDoc.transact(() => {
|
|
4931
|
+
const healMap = healDocInfo.yDoc.getMap(constraintMapName);
|
|
4932
|
+
if (healMap.get(uniqueKeyString) !== fallbackRecord.id) {
|
|
4933
|
+
healMap.set(uniqueKeyString, fallbackRecord.id);
|
|
4934
|
+
}
|
|
4935
|
+
}, `heal-unique-index-${currentModelName}-${healDocId}`);
|
|
4936
|
+
Logger.verbose(
|
|
4937
|
+
`[${currentModelName}] upsertByUnique: index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id}; healed missing index entry (legacy index-less record).`
|
|
4938
|
+
);
|
|
4939
|
+
} else {
|
|
4940
|
+
Logger.verbose(
|
|
4941
|
+
`[${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).`
|
|
4942
|
+
);
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
}
|
|
4565
4946
|
if (options?.objectMustExist) {
|
|
4566
4947
|
if (!existingRecord) {
|
|
4567
4948
|
throw new RecordNotFoundError(
|
|
@@ -4718,7 +5099,7 @@ var init_BaseModel = __esm({
|
|
|
4718
5099
|
for (const [fieldKey, fieldOptions] of schema.fields) {
|
|
4719
5100
|
if (fieldOptions?.type !== "stringset") continue;
|
|
4720
5101
|
const value = recordYMap.get(fieldKey);
|
|
4721
|
-
if (value instanceof
|
|
5102
|
+
if (value instanceof Y5.Map) {
|
|
4722
5103
|
this.observeStringSetMapOnce(value);
|
|
4723
5104
|
}
|
|
4724
5105
|
}
|
|
@@ -4765,7 +5146,14 @@ var init_BaseModel = __esm({
|
|
|
4765
5146
|
continue;
|
|
4766
5147
|
}
|
|
4767
5148
|
if (value !== void 0) {
|
|
4768
|
-
|
|
5149
|
+
if (isYjsComposite(value)) {
|
|
5150
|
+
Logger.warn(
|
|
5151
|
+
`[${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.`
|
|
5152
|
+
);
|
|
5153
|
+
updatedData[key] = normalizeYjsValueForStorage(value);
|
|
5154
|
+
} else {
|
|
5155
|
+
updatedData[key] = value;
|
|
5156
|
+
}
|
|
4769
5157
|
}
|
|
4770
5158
|
}
|
|
4771
5159
|
if (unknownFields.length > 0) {
|
|
@@ -4845,7 +5233,14 @@ var init_BaseModel = __esm({
|
|
|
4845
5233
|
continue;
|
|
4846
5234
|
}
|
|
4847
5235
|
if (value !== void 0) {
|
|
4848
|
-
|
|
5236
|
+
if (isYjsComposite(value)) {
|
|
5237
|
+
Logger.warn(
|
|
5238
|
+
`[${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.`
|
|
5239
|
+
);
|
|
5240
|
+
updatedData[key] = normalizeYjsValueForStorage(value);
|
|
5241
|
+
} else {
|
|
5242
|
+
updatedData[key] = value;
|
|
5243
|
+
}
|
|
4849
5244
|
}
|
|
4850
5245
|
}
|
|
4851
5246
|
if (unknownFields.length > 0) {
|
|
@@ -4890,6 +5285,19 @@ var init_BaseModel = __esm({
|
|
|
4890
5285
|
});
|
|
4891
5286
|
|
|
4892
5287
|
// src/models/relationshipManager.ts
|
|
5288
|
+
function warnIfOrderFieldNullable(modelClass, orderByField, relationshipDescription) {
|
|
5289
|
+
if (ALWAYS_PRESENT_ORDER_FIELDS.has(orderByField)) return;
|
|
5290
|
+
const schema = modelClass.getSchema?.();
|
|
5291
|
+
const fields = schema?.fields;
|
|
5292
|
+
if (!fields || typeof fields.get !== "function") return;
|
|
5293
|
+
const fieldOptions = fields.get(orderByField);
|
|
5294
|
+
if (!fieldOptions) return;
|
|
5295
|
+
if (fieldOptions.required !== true) {
|
|
5296
|
+
Logger.warn(
|
|
5297
|
+
`[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.)`
|
|
5298
|
+
);
|
|
5299
|
+
}
|
|
5300
|
+
}
|
|
4893
5301
|
function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
4894
5302
|
return async function() {
|
|
4895
5303
|
const relatedId = this[config.relatedIdField];
|
|
@@ -4898,6 +5306,16 @@ function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
|
4898
5306
|
};
|
|
4899
5307
|
}
|
|
4900
5308
|
function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbEngine) {
|
|
5309
|
+
const {
|
|
5310
|
+
relatedIdField,
|
|
5311
|
+
orderByField = "id",
|
|
5312
|
+
orderDirection = "ASC"
|
|
5313
|
+
} = config;
|
|
5314
|
+
warnIfOrderFieldNullable(
|
|
5315
|
+
targetModelClass,
|
|
5316
|
+
orderByField,
|
|
5317
|
+
`hasMany(${targetModelClass.name})`
|
|
5318
|
+
);
|
|
4901
5319
|
return async function(paginationArgs) {
|
|
4902
5320
|
const {
|
|
4903
5321
|
limit,
|
|
@@ -4905,71 +5323,39 @@ function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbE
|
|
|
4905
5323
|
beforeCursor,
|
|
4906
5324
|
direction = "forward"
|
|
4907
5325
|
} = paginationArgs || {};
|
|
4908
|
-
const {
|
|
4909
|
-
relatedIdField,
|
|
4910
|
-
orderByField = "id",
|
|
4911
|
-
orderDirection = "ASC"
|
|
4912
|
-
} = config;
|
|
4913
5326
|
const targetSchema = targetModelClass.getSchema();
|
|
4914
5327
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
4915
5328
|
Logger.error(
|
|
4916
5329
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasMany relationship has no valid schema name.`
|
|
4917
5330
|
);
|
|
4918
|
-
return { data: [],
|
|
5331
|
+
return { data: [], hasMore: false };
|
|
4919
5332
|
}
|
|
4920
|
-
const
|
|
5333
|
+
const forward = direction !== "backward";
|
|
5334
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
4921
5335
|
const queryOptions = {
|
|
4922
|
-
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 }
|
|
5336
|
+
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 },
|
|
5337
|
+
direction: forward ? 1 : -1
|
|
4923
5338
|
};
|
|
4924
|
-
if (limit !== void 0)
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
};
|
|
4931
|
-
} else if (direction === "backward") {
|
|
4932
|
-
if (beforeCursor) {
|
|
4933
|
-
filter[orderByField] = {
|
|
4934
|
-
[orderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
4935
|
-
};
|
|
4936
|
-
}
|
|
4937
|
-
queryOptions.sort = { [orderByField]: orderDirection === "ASC" ? -1 : 1 };
|
|
4938
|
-
}
|
|
4939
|
-
const result = await targetModelClass.query(filter, queryOptions);
|
|
4940
|
-
const results = result.data;
|
|
4941
|
-
let nextCursor = null;
|
|
4942
|
-
let prevCursor = null;
|
|
4943
|
-
const isFirstPage = direction === "forward" && !afterCursor;
|
|
4944
|
-
if (limit !== void 0) {
|
|
4945
|
-
if (direction === "forward") {
|
|
4946
|
-
if (results.length > limit) {
|
|
4947
|
-
nextCursor = results[limit - 1][orderByField];
|
|
4948
|
-
results.pop();
|
|
4949
|
-
}
|
|
4950
|
-
if (results.length > 0 && !isFirstPage) {
|
|
4951
|
-
prevCursor = results[0][orderByField];
|
|
4952
|
-
}
|
|
4953
|
-
} else {
|
|
4954
|
-
if (results.length > limit) {
|
|
4955
|
-
prevCursor = results[limit - 1][orderByField];
|
|
4956
|
-
results.pop();
|
|
4957
|
-
}
|
|
4958
|
-
results.reverse();
|
|
4959
|
-
if (results.length > 0 && beforeCursor) {
|
|
4960
|
-
nextCursor = results[results.length - 1][orderByField];
|
|
4961
|
-
}
|
|
4962
|
-
if (results.length > 0 && !beforeCursor) {
|
|
4963
|
-
if (results.length === limit) {
|
|
4964
|
-
prevCursor = results[0][orderByField];
|
|
4965
|
-
}
|
|
4966
|
-
}
|
|
4967
|
-
}
|
|
4968
|
-
}
|
|
4969
|
-
return { data: results, nextCursor, prevCursor };
|
|
5339
|
+
if (limit !== void 0) queryOptions.limit = limit;
|
|
5340
|
+
if (cursor != null) queryOptions.uniqueStartKey = cursor;
|
|
5341
|
+
return targetModelClass.query(
|
|
5342
|
+
{ [relatedIdField]: this.id },
|
|
5343
|
+
queryOptions
|
|
5344
|
+
);
|
|
4970
5345
|
};
|
|
4971
5346
|
}
|
|
4972
5347
|
function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClass, joinModelClass, _dbEngine) {
|
|
5348
|
+
const {
|
|
5349
|
+
joinModelLocalField,
|
|
5350
|
+
joinModelRelatedField,
|
|
5351
|
+
joinModelOrderByField = "id",
|
|
5352
|
+
joinModelOrderDirection = "ASC"
|
|
5353
|
+
} = config;
|
|
5354
|
+
warnIfOrderFieldNullable(
|
|
5355
|
+
joinModelClass,
|
|
5356
|
+
joinModelOrderByField,
|
|
5357
|
+
`hasManyThrough(${targetModelClass.name} via ${joinModelClass.name})`
|
|
5358
|
+
);
|
|
4973
5359
|
return async function(paginationArgs) {
|
|
4974
5360
|
const {
|
|
4975
5361
|
limit,
|
|
@@ -4983,87 +5369,42 @@ function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClas
|
|
|
4983
5369
|
Logger.error(
|
|
4984
5370
|
`[RelationshipManager] Join model ${joinModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
4985
5371
|
);
|
|
4986
|
-
return { data: [],
|
|
5372
|
+
return { data: [], hasMore: false };
|
|
4987
5373
|
}
|
|
4988
5374
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
4989
5375
|
Logger.error(
|
|
4990
5376
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
4991
5377
|
);
|
|
4992
|
-
return { data: [],
|
|
5378
|
+
return { data: [], hasMore: false };
|
|
4993
5379
|
}
|
|
4994
|
-
const
|
|
4995
|
-
|
|
4996
|
-
joinModelRelatedField,
|
|
4997
|
-
joinModelOrderByField = "id",
|
|
4998
|
-
joinModelOrderDirection = "ASC"
|
|
4999
|
-
} = config;
|
|
5000
|
-
const joinFilter = { [joinModelLocalField]: this.id };
|
|
5380
|
+
const forward = direction !== "backward";
|
|
5381
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
5001
5382
|
const joinQueryOptions = {
|
|
5002
5383
|
sort: {
|
|
5003
5384
|
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? 1 : -1
|
|
5004
5385
|
},
|
|
5005
|
-
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 }
|
|
5386
|
+
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 },
|
|
5387
|
+
direction: forward ? 1 : -1
|
|
5006
5388
|
};
|
|
5007
|
-
if (limit !== void 0)
|
|
5008
|
-
|
|
5009
|
-
}
|
|
5010
|
-
if (direction === "forward" && afterCursor) {
|
|
5011
|
-
joinFilter[joinModelOrderByField] = {
|
|
5012
|
-
[joinModelOrderDirection === "ASC" ? "$gt" : "$lt"]: afterCursor
|
|
5013
|
-
};
|
|
5014
|
-
} else if (direction === "backward") {
|
|
5015
|
-
if (beforeCursor) {
|
|
5016
|
-
joinFilter[joinModelOrderByField] = {
|
|
5017
|
-
[joinModelOrderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
5018
|
-
};
|
|
5019
|
-
}
|
|
5020
|
-
joinQueryOptions.sort = {
|
|
5021
|
-
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? -1 : 1
|
|
5022
|
-
};
|
|
5023
|
-
}
|
|
5389
|
+
if (limit !== void 0) joinQueryOptions.limit = limit;
|
|
5390
|
+
if (cursor != null) joinQueryOptions.uniqueStartKey = cursor;
|
|
5024
5391
|
const joinResult = await joinModelClass.query(
|
|
5025
|
-
|
|
5392
|
+
{ [joinModelLocalField]: this.id },
|
|
5026
5393
|
joinQueryOptions
|
|
5027
5394
|
);
|
|
5028
|
-
const
|
|
5029
|
-
let
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
nextCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
5036
|
-
joinResults.pop();
|
|
5037
|
-
}
|
|
5038
|
-
if (joinResults.length > 0 && !isFirstPage) {
|
|
5039
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
5040
|
-
}
|
|
5041
|
-
} else {
|
|
5042
|
-
if (joinResults.length > limit) {
|
|
5043
|
-
prevCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
5044
|
-
joinResults.pop();
|
|
5045
|
-
}
|
|
5046
|
-
joinResults.reverse();
|
|
5047
|
-
if (joinResults.length > 0 && beforeCursor) {
|
|
5048
|
-
nextCursorFromJoin = joinResults[joinResults.length - 1][joinModelOrderByField];
|
|
5049
|
-
}
|
|
5050
|
-
if (joinResults.length > 0 && !beforeCursor) {
|
|
5051
|
-
if (joinResults.length === limit) {
|
|
5052
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
5053
|
-
}
|
|
5054
|
-
}
|
|
5055
|
-
}
|
|
5395
|
+
const relatedIds = joinResult.data.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
5396
|
+
let targetData = [];
|
|
5397
|
+
if (relatedIds.length > 0) {
|
|
5398
|
+
const targetResult = await targetModelClass.query({
|
|
5399
|
+
id: { $in: relatedIds }
|
|
5400
|
+
});
|
|
5401
|
+
targetData = targetResult.data;
|
|
5056
5402
|
}
|
|
5057
|
-
const relatedIds = joinResults.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
5058
|
-
if (relatedIds.length === 0)
|
|
5059
|
-
return { data: [], nextCursor: null, prevCursor: null };
|
|
5060
|
-
const targetResult = await targetModelClass.query({
|
|
5061
|
-
id: { $in: relatedIds }
|
|
5062
|
-
});
|
|
5063
5403
|
return {
|
|
5064
|
-
data:
|
|
5065
|
-
nextCursor:
|
|
5066
|
-
prevCursor:
|
|
5404
|
+
data: targetData,
|
|
5405
|
+
nextCursor: joinResult.nextCursor,
|
|
5406
|
+
prevCursor: joinResult.prevCursor,
|
|
5407
|
+
hasMore: joinResult.hasMore
|
|
5067
5408
|
};
|
|
5068
5409
|
};
|
|
5069
5410
|
}
|
|
@@ -5187,10 +5528,12 @@ function generateHasManyThroughRemoveMethod(_sourceModelClass, config, _targetMo
|
|
|
5187
5528
|
}, `remove-join-${config.joinModel}-${this.id}-${targetId}`);
|
|
5188
5529
|
};
|
|
5189
5530
|
}
|
|
5531
|
+
var ALWAYS_PRESENT_ORDER_FIELDS;
|
|
5190
5532
|
var init_relationshipManager = __esm({
|
|
5191
5533
|
"src/models/relationshipManager.ts"() {
|
|
5192
5534
|
"use strict";
|
|
5193
5535
|
init_BaseModel();
|
|
5536
|
+
ALWAYS_PRESENT_ORDER_FIELDS = /* @__PURE__ */ new Set(["id", "type"]);
|
|
5194
5537
|
}
|
|
5195
5538
|
});
|
|
5196
5539
|
|
|
@@ -5658,7 +6001,92 @@ var init_environment = __esm({
|
|
|
5658
6001
|
});
|
|
5659
6002
|
|
|
5660
6003
|
// src/engines/SqljsEngine.ts
|
|
5661
|
-
|
|
6004
|
+
async function resolveNodeWasmPath() {
|
|
6005
|
+
if (!isNode()) {
|
|
6006
|
+
return null;
|
|
6007
|
+
}
|
|
6008
|
+
try {
|
|
6009
|
+
const cjsRequire = typeof require !== "undefined" ? require : void 0;
|
|
6010
|
+
if (cjsRequire) {
|
|
6011
|
+
return cjsRequire.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6012
|
+
}
|
|
6013
|
+
} catch {
|
|
6014
|
+
}
|
|
6015
|
+
try {
|
|
6016
|
+
const metaUrl = import_meta.url;
|
|
6017
|
+
if (metaUrl) {
|
|
6018
|
+
const { createRequire } = await import(
|
|
6019
|
+
/* @vite-ignore */
|
|
6020
|
+
"module"
|
|
6021
|
+
);
|
|
6022
|
+
const req = createRequire(metaUrl);
|
|
6023
|
+
return req.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6024
|
+
}
|
|
6025
|
+
} catch {
|
|
6026
|
+
}
|
|
6027
|
+
return null;
|
|
6028
|
+
}
|
|
6029
|
+
async function preflightNodeWasm(wasmPath) {
|
|
6030
|
+
if (!isNode()) {
|
|
6031
|
+
return;
|
|
6032
|
+
}
|
|
6033
|
+
if (/^[a-z]+:\/\//i.test(wasmPath)) {
|
|
6034
|
+
return;
|
|
6035
|
+
}
|
|
6036
|
+
const { existsSync } = await import(
|
|
6037
|
+
/* @vite-ignore */
|
|
6038
|
+
"fs"
|
|
6039
|
+
);
|
|
6040
|
+
if (!existsSync(wasmPath)) {
|
|
6041
|
+
throw new Error(
|
|
6042
|
+
`[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()).`
|
|
6043
|
+
);
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
6046
|
+
async function loadSqlJsModule(locateFile) {
|
|
6047
|
+
let abortReject;
|
|
6048
|
+
const toError = (reason) => reason instanceof Error ? reason : new Error(
|
|
6049
|
+
`[SqljsEngine] sql.js aborted during initialization: ${String(
|
|
6050
|
+
reason
|
|
6051
|
+
)}`
|
|
6052
|
+
);
|
|
6053
|
+
const nodeProcess = isNode() && typeof process !== "undefined" ? process : void 0;
|
|
6054
|
+
const onUnhandled = (reason) => {
|
|
6055
|
+
const err = toError(reason);
|
|
6056
|
+
if (/Aborted/.test(err.message) || /Aborted/.test(String(reason))) {
|
|
6057
|
+
abortReject?.(err);
|
|
6058
|
+
return;
|
|
6059
|
+
}
|
|
6060
|
+
if (nodeProcess && nodeProcess.listenerCount("unhandledRejection") === 1) {
|
|
6061
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6062
|
+
void Promise.reject(reason);
|
|
6063
|
+
}
|
|
6064
|
+
};
|
|
6065
|
+
if (nodeProcess) {
|
|
6066
|
+
nodeProcess.on("unhandledRejection", onUnhandled);
|
|
6067
|
+
}
|
|
6068
|
+
const abortGuard = new Promise((_resolve, reject) => {
|
|
6069
|
+
abortReject = reject;
|
|
6070
|
+
});
|
|
6071
|
+
abortGuard.catch(() => {
|
|
6072
|
+
});
|
|
6073
|
+
try {
|
|
6074
|
+
const initPromise = (0, import_sql4.default)({
|
|
6075
|
+
locateFile,
|
|
6076
|
+
// Layer 1: emscripten onAbort.
|
|
6077
|
+
onAbort: (reason) => abortReject?.(toError(reason))
|
|
6078
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6079
|
+
});
|
|
6080
|
+
return await Promise.race([initPromise, abortGuard]);
|
|
6081
|
+
} finally {
|
|
6082
|
+
if (nodeProcess) {
|
|
6083
|
+
setTimeout(() => {
|
|
6084
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6085
|
+
}, 0);
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
var import_sql4, import_async_mutex, import_meta, SQL_WASM_URL, SQL_WASM_FILENAME, TransactionalSQLJSOperations, SqljsEngine;
|
|
5662
6090
|
var init_SqljsEngine = __esm({
|
|
5663
6091
|
"src/engines/SqljsEngine.ts"() {
|
|
5664
6092
|
"use strict";
|
|
@@ -5666,7 +6094,10 @@ var init_SqljsEngine = __esm({
|
|
|
5666
6094
|
init_BaseModel();
|
|
5667
6095
|
import_async_mutex = require("async-mutex");
|
|
5668
6096
|
init_sql();
|
|
6097
|
+
init_environment();
|
|
6098
|
+
import_meta = {};
|
|
5669
6099
|
SQL_WASM_URL = "/sql-wasm.wasm";
|
|
6100
|
+
SQL_WASM_FILENAME = "sql-wasm.wasm";
|
|
5670
6101
|
TransactionalSQLJSOperations = class {
|
|
5671
6102
|
db;
|
|
5672
6103
|
engine;
|
|
@@ -5732,22 +6163,37 @@ var init_SqljsEngine = __esm({
|
|
|
5732
6163
|
}
|
|
5733
6164
|
async initialize(engineOptions) {
|
|
5734
6165
|
Logger.info("[SqljsEngine] Initializing SQL.js engine...");
|
|
6166
|
+
const loadingModuleThisCall = !_SqljsEngine.SQL;
|
|
5735
6167
|
try {
|
|
5736
6168
|
if (!_SqljsEngine.SQL) {
|
|
5737
|
-
|
|
6169
|
+
let locateFileConfig;
|
|
6170
|
+
if (engineOptions?.locateFile) {
|
|
6171
|
+
locateFileConfig = engineOptions.locateFile;
|
|
6172
|
+
} else if (engineOptions?.wasmURL) {
|
|
6173
|
+
const url = engineOptions.wasmURL;
|
|
6174
|
+
locateFileConfig = (_file) => url;
|
|
6175
|
+
} else {
|
|
6176
|
+
const nodeWasmPath = await resolveNodeWasmPath();
|
|
6177
|
+
const resolved = nodeWasmPath ?? SQL_WASM_URL;
|
|
6178
|
+
locateFileConfig = (_file) => resolved;
|
|
6179
|
+
}
|
|
6180
|
+
const wasmPath = locateFileConfig(SQL_WASM_FILENAME);
|
|
5738
6181
|
Logger.debug(
|
|
5739
6182
|
"[SqljsEngine] Attempting to load SQL.js WASM from:",
|
|
5740
|
-
|
|
6183
|
+
wasmPath
|
|
5741
6184
|
);
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
6185
|
+
await preflightNodeWasm(wasmPath);
|
|
6186
|
+
const SQL = await loadSqlJsModule(locateFileConfig);
|
|
6187
|
+
_SqljsEngine.SQL = SQL;
|
|
5745
6188
|
Logger.info("[SqljsEngine] SQL.js WASM loaded successfully.");
|
|
5746
6189
|
}
|
|
5747
6190
|
this.db = new _SqljsEngine.SQL.Database();
|
|
5748
6191
|
Logger.info("[SqljsEngine] SQL.js Database initialized in memory.");
|
|
5749
6192
|
this.updateTableNames();
|
|
5750
6193
|
} catch (error) {
|
|
6194
|
+
if (loadingModuleThisCall) {
|
|
6195
|
+
_SqljsEngine.SQL = null;
|
|
6196
|
+
}
|
|
5751
6197
|
Logger.error("[SqljsEngine] Error during SQL.js initialization:", error);
|
|
5752
6198
|
throw error;
|
|
5753
6199
|
}
|
|
@@ -6729,12 +7175,14 @@ __export(index_exports, {
|
|
|
6729
7175
|
attachAndRegisterModel: () => attachAndRegisterModel,
|
|
6730
7176
|
attachSchemaToClass: () => attachSchemaToClass,
|
|
6731
7177
|
autoRegisterModel: () => autoRegisterModel,
|
|
7178
|
+
buildUniqueKey: () => buildUniqueKey,
|
|
6732
7179
|
clearMetaSyncCache: () => clearMetaSyncCache,
|
|
6733
7180
|
createModelClass: () => createModelClass,
|
|
6734
7181
|
defineModelSchema: () => defineModelSchema,
|
|
6735
7182
|
discoverModelNames: () => discoverModelNames,
|
|
6736
7183
|
discoverSchema: () => discoverSchema,
|
|
6737
7184
|
dumpYDocToPlain: () => dumpYDocToPlain,
|
|
7185
|
+
fieldUniqueConstraintName: () => fieldUniqueConstraintName,
|
|
6738
7186
|
generateULID: () => generateULID,
|
|
6739
7187
|
inferFieldType: () => inferFieldType,
|
|
6740
7188
|
initJsBao: () => initJsBao,
|
|
@@ -6745,7 +7193,8 @@ __export(index_exports, {
|
|
|
6745
7193
|
schemaToToml: () => schemaToToml,
|
|
6746
7194
|
summarizePlainYDoc: () => summarizePlainYDoc,
|
|
6747
7195
|
syncInferredMeta: () => syncInferredMeta,
|
|
6748
|
-
syncModelMeta: () => syncModelMeta
|
|
7196
|
+
syncModelMeta: () => syncModelMeta,
|
|
7197
|
+
uniqueIndexMapName: () => uniqueIndexMapName
|
|
6749
7198
|
});
|
|
6750
7199
|
module.exports = __toCommonJS(index_exports);
|
|
6751
7200
|
|
|
@@ -6968,6 +7417,7 @@ init_StringSet();
|
|
|
6968
7417
|
// src/models/schema.ts
|
|
6969
7418
|
init_BaseModel();
|
|
6970
7419
|
init_ModelRegistry();
|
|
7420
|
+
init_uniqueIndex();
|
|
6971
7421
|
function defineModelSchema(input) {
|
|
6972
7422
|
const { name, fields } = input;
|
|
6973
7423
|
const options = {
|
|
@@ -7040,7 +7490,7 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
|
|
|
7040
7490
|
for (const [fieldName, options] of fields.entries()) {
|
|
7041
7491
|
if (options.unique) {
|
|
7042
7492
|
resolved.push({
|
|
7043
|
-
name:
|
|
7493
|
+
name: fieldUniqueConstraintName(modelName, fieldName),
|
|
7044
7494
|
fields: [fieldName]
|
|
7045
7495
|
});
|
|
7046
7496
|
}
|
|
@@ -7161,10 +7611,13 @@ var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
|
7161
7611
|
"unique",
|
|
7162
7612
|
"required",
|
|
7163
7613
|
"auto_assign",
|
|
7614
|
+
"auto_stamp",
|
|
7164
7615
|
"max_length",
|
|
7165
7616
|
"max_count",
|
|
7166
|
-
"default"
|
|
7617
|
+
"default",
|
|
7618
|
+
"enum"
|
|
7167
7619
|
]);
|
|
7620
|
+
var VALID_AUTO_STAMP_VALUES = /* @__PURE__ */ new Set(["create", "update", "both"]);
|
|
7168
7621
|
var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
|
|
7169
7622
|
"fields",
|
|
7170
7623
|
"relationships",
|
|
@@ -7206,9 +7659,37 @@ function parseFieldOptions(raw, context, strict) {
|
|
|
7206
7659
|
if (raw.unique === true) opts.unique = true;
|
|
7207
7660
|
if (raw.required === true) opts.required = true;
|
|
7208
7661
|
if (raw.auto_assign === true) opts.autoAssign = true;
|
|
7662
|
+
if (raw.auto_stamp !== void 0) {
|
|
7663
|
+
if (typeof raw.auto_stamp !== "string" || !VALID_AUTO_STAMP_VALUES.has(raw.auto_stamp)) {
|
|
7664
|
+
throw new Error(
|
|
7665
|
+
`${context}: invalid auto_stamp value "${raw.auto_stamp}". Must be one of: ${[
|
|
7666
|
+
...VALID_AUTO_STAMP_VALUES
|
|
7667
|
+
].join(", ")}`
|
|
7668
|
+
);
|
|
7669
|
+
}
|
|
7670
|
+
opts.autoStamp = raw.auto_stamp;
|
|
7671
|
+
}
|
|
7209
7672
|
if (raw.max_length !== void 0) opts.maxLength = raw.max_length;
|
|
7210
7673
|
if (raw.max_count !== void 0) opts.maxCount = raw.max_count;
|
|
7211
7674
|
if (raw.default !== void 0) opts.default = raw.default;
|
|
7675
|
+
if (raw.enum !== void 0) {
|
|
7676
|
+
if (raw.type !== "string") {
|
|
7677
|
+
throw new Error(
|
|
7678
|
+
`${context}: \`enum\` is only valid on a "string" field, not "${raw.type}".`
|
|
7679
|
+
);
|
|
7680
|
+
}
|
|
7681
|
+
if (!Array.isArray(raw.enum) || raw.enum.length === 0) {
|
|
7682
|
+
throw new Error(
|
|
7683
|
+
`${context}: \`enum\` must be a non-empty array of strings.`
|
|
7684
|
+
);
|
|
7685
|
+
}
|
|
7686
|
+
if (!raw.enum.every((v) => typeof v === "string")) {
|
|
7687
|
+
throw new Error(
|
|
7688
|
+
`${context}: \`enum\` values must all be strings.`
|
|
7689
|
+
);
|
|
7690
|
+
}
|
|
7691
|
+
opts.enum = [...raw.enum];
|
|
7692
|
+
}
|
|
7212
7693
|
return opts;
|
|
7213
7694
|
}
|
|
7214
7695
|
function requireField(raw, field, context) {
|
|
@@ -7336,7 +7817,8 @@ async function loadSchemaFromToml(filePath, options = {}) {
|
|
|
7336
7817
|
}
|
|
7337
7818
|
|
|
7338
7819
|
// src/utils/yDocSchema.ts
|
|
7339
|
-
var
|
|
7820
|
+
var Y6 = __toESM(require("yjs"), 1);
|
|
7821
|
+
init_yjsValues();
|
|
7340
7822
|
function discoverSchema(yDoc) {
|
|
7341
7823
|
const models = {};
|
|
7342
7824
|
const metaNames = /* @__PURE__ */ new Set();
|
|
@@ -7370,7 +7852,7 @@ function discoverModelNames(yDoc) {
|
|
|
7370
7852
|
function materializeMap(yDoc, key) {
|
|
7371
7853
|
try {
|
|
7372
7854
|
const map = yDoc.getMap(key);
|
|
7373
|
-
return map instanceof
|
|
7855
|
+
return map instanceof Y6.Map ? map : null;
|
|
7374
7856
|
} catch {
|
|
7375
7857
|
return null;
|
|
7376
7858
|
}
|
|
@@ -7380,11 +7862,11 @@ function readModelMeta(metaMap) {
|
|
|
7380
7862
|
let constraints;
|
|
7381
7863
|
let relationships;
|
|
7382
7864
|
for (const [key, value] of metaMap.entries()) {
|
|
7383
|
-
if (key === "_constraints" && value instanceof
|
|
7865
|
+
if (key === "_constraints" && value instanceof Y6.Map) {
|
|
7384
7866
|
constraints = readConstraints(value);
|
|
7385
|
-
} else if (key === "_relationships" && value instanceof
|
|
7867
|
+
} else if (key === "_relationships" && value instanceof Y6.Map) {
|
|
7386
7868
|
relationships = readRelationships(value);
|
|
7387
|
-
} else if (value instanceof
|
|
7869
|
+
} else if (value instanceof Y6.Map) {
|
|
7388
7870
|
fields[key] = readFieldMeta(value);
|
|
7389
7871
|
}
|
|
7390
7872
|
}
|
|
@@ -7403,6 +7885,10 @@ function readFieldMeta(fieldMap) {
|
|
|
7403
7885
|
if (fieldMap.get("unique") === true) field.unique = true;
|
|
7404
7886
|
if (fieldMap.get("required") === true) field.required = true;
|
|
7405
7887
|
if (fieldMap.get("autoAssign") === true) field.autoAssign = true;
|
|
7888
|
+
const autoStamp = fieldMap.get("autoStamp");
|
|
7889
|
+
if (typeof autoStamp === "string" && (autoStamp === "create" || autoStamp === "update" || autoStamp === "both")) {
|
|
7890
|
+
field.autoStamp = autoStamp;
|
|
7891
|
+
}
|
|
7406
7892
|
const def = fieldMap.get("default");
|
|
7407
7893
|
if (def !== void 0) field.default = def;
|
|
7408
7894
|
const maxLength = fieldMap.get("maxLength");
|
|
@@ -7415,7 +7901,7 @@ function inferModelFromData(dataMap) {
|
|
|
7415
7901
|
const fields = {};
|
|
7416
7902
|
let sampled = 0;
|
|
7417
7903
|
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
7418
|
-
if (!(recordValue instanceof
|
|
7904
|
+
if (!(recordValue instanceof Y6.Map)) continue;
|
|
7419
7905
|
if (++sampled > 5) break;
|
|
7420
7906
|
for (const [fieldName, value] of recordValue.entries()) {
|
|
7421
7907
|
if (fieldName.startsWith("_")) continue;
|
|
@@ -7428,7 +7914,7 @@ function inferModelFromData(dataMap) {
|
|
|
7428
7914
|
return { fields };
|
|
7429
7915
|
}
|
|
7430
7916
|
function inferTypeFromValue(value) {
|
|
7431
|
-
if (value
|
|
7917
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
7432
7918
|
switch (typeof value) {
|
|
7433
7919
|
case "string":
|
|
7434
7920
|
return "string";
|
|
@@ -7443,7 +7929,7 @@ function inferTypeFromValue(value) {
|
|
|
7443
7929
|
function readConstraints(constraintsMap) {
|
|
7444
7930
|
const out = {};
|
|
7445
7931
|
for (const [name, value] of constraintsMap.entries()) {
|
|
7446
|
-
if (!(value instanceof
|
|
7932
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
7447
7933
|
let fields = [];
|
|
7448
7934
|
const rawFields = value.get("fields");
|
|
7449
7935
|
if (typeof rawFields === "string") {
|
|
@@ -7462,7 +7948,7 @@ function readConstraints(constraintsMap) {
|
|
|
7462
7948
|
function readRelationships(relsMap) {
|
|
7463
7949
|
const out = {};
|
|
7464
7950
|
for (const [name, value] of relsMap.entries()) {
|
|
7465
|
-
if (!(value instanceof
|
|
7951
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
7466
7952
|
const rel = {};
|
|
7467
7953
|
for (const [k, v] of value.entries()) {
|
|
7468
7954
|
rel[k] = v;
|
|
@@ -7473,6 +7959,7 @@ function readRelationships(relsMap) {
|
|
|
7473
7959
|
}
|
|
7474
7960
|
var CAMEL_TO_SNAKE = {
|
|
7475
7961
|
autoAssign: "auto_assign",
|
|
7962
|
+
autoStamp: "auto_stamp",
|
|
7476
7963
|
maxLength: "max_length",
|
|
7477
7964
|
maxCount: "max_count",
|
|
7478
7965
|
relatedIdField: "related_id_field",
|
|
@@ -7503,6 +7990,7 @@ function schemaToToml(schema) {
|
|
|
7503
7990
|
lines.push(`[models.${modelName}.fields.${fieldName}]`);
|
|
7504
7991
|
lines.push(`type = ${tomlValue(field.type)}`);
|
|
7505
7992
|
if (field.autoAssign) lines.push("auto_assign = true");
|
|
7993
|
+
if (field.autoStamp) lines.push(`auto_stamp = ${tomlValue(field.autoStamp)}`);
|
|
7506
7994
|
if (field.indexed) lines.push("indexed = true");
|
|
7507
7995
|
if (field.unique) lines.push("unique = true");
|
|
7508
7996
|
if (field.required) lines.push("required = true");
|
|
@@ -7534,19 +8022,20 @@ function schemaToToml(schema) {
|
|
|
7534
8022
|
}
|
|
7535
8023
|
|
|
7536
8024
|
// src/index.ts
|
|
8025
|
+
init_uniqueIndex();
|
|
7537
8026
|
init_BaseModel();
|
|
7538
8027
|
|
|
7539
8028
|
// src/utils/yDocDump.ts
|
|
7540
|
-
var
|
|
8029
|
+
var Y7 = __toESM(require("yjs"), 1);
|
|
7541
8030
|
function toPlain(value) {
|
|
7542
|
-
if (value instanceof
|
|
8031
|
+
if (value instanceof Y7.Map) {
|
|
7543
8032
|
const obj = {};
|
|
7544
8033
|
value.forEach((v, k) => {
|
|
7545
8034
|
obj[k] = toPlain(v);
|
|
7546
8035
|
});
|
|
7547
8036
|
return obj;
|
|
7548
8037
|
}
|
|
7549
|
-
if (value instanceof
|
|
8038
|
+
if (value instanceof Y7.Array) {
|
|
7550
8039
|
return value.toArray().map((item) => toPlain(item));
|
|
7551
8040
|
}
|
|
7552
8041
|
return value;
|
|
@@ -7555,7 +8044,7 @@ function dumpYDocToPlain(yDoc, options = {}) {
|
|
|
7555
8044
|
const result = {};
|
|
7556
8045
|
const includeIndexes = !!options.includeIndexes;
|
|
7557
8046
|
yDoc.share.forEach((type, name) => {
|
|
7558
|
-
if (!(type instanceof
|
|
8047
|
+
if (!(type instanceof Y7.Map)) return;
|
|
7559
8048
|
if (!includeIndexes && name.startsWith("_uniqueIdx_")) return;
|
|
7560
8049
|
const records = {};
|
|
7561
8050
|
type.forEach((recordValue, recordId) => {
|
|
@@ -7604,12 +8093,14 @@ function summarizePlainYDoc(dump) {
|
|
|
7604
8093
|
attachAndRegisterModel,
|
|
7605
8094
|
attachSchemaToClass,
|
|
7606
8095
|
autoRegisterModel,
|
|
8096
|
+
buildUniqueKey,
|
|
7607
8097
|
clearMetaSyncCache,
|
|
7608
8098
|
createModelClass,
|
|
7609
8099
|
defineModelSchema,
|
|
7610
8100
|
discoverModelNames,
|
|
7611
8101
|
discoverSchema,
|
|
7612
8102
|
dumpYDocToPlain,
|
|
8103
|
+
fieldUniqueConstraintName,
|
|
7613
8104
|
generateULID,
|
|
7614
8105
|
inferFieldType,
|
|
7615
8106
|
initJsBao,
|
|
@@ -7620,5 +8111,6 @@ function summarizePlainYDoc(dump) {
|
|
|
7620
8111
|
schemaToToml,
|
|
7621
8112
|
summarizePlainYDoc,
|
|
7622
8113
|
syncInferredMeta,
|
|
7623
|
-
syncModelMeta
|
|
8114
|
+
syncModelMeta,
|
|
8115
|
+
uniqueIndexMapName
|
|
7624
8116
|
});
|