js-bao 0.5.1 → 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 +586 -265
- package/dist/browser.d.cts +21 -0
- package/dist/browser.d.ts +21 -0
- package/dist/browser.js +588 -263
- package/dist/client.cjs +42 -6
- package/dist/client.d.cts +71 -0
- package/dist/client.d.ts +71 -0
- package/dist/client.js +39 -4
- package/dist/cloudflare-do.cjs +462 -215
- package/dist/cloudflare-do.d.cts +111 -1
- package/dist/cloudflare-do.d.ts +111 -1
- package/dist/cloudflare-do.js +459 -213
- package/dist/cloudflare.cjs +270 -23
- package/dist/cloudflare.d.cts +276 -1
- package/dist/cloudflare.d.ts +276 -1
- package/dist/cloudflare.js +267 -21
- package/dist/codegen-v2.cjs +77 -19
- package/dist/codegen.cjs +2 -1
- package/dist/index.cjs +551 -224
- package/dist/index.d.cts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +549 -221
- package/dist/node.cjs +775 -291
- package/dist/node.d.cts +179 -1
- package/dist/node.d.ts +179 -1
- package/dist/node.js +762 -288
- package/package.json +2 -1
package/dist/browser.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(
|
|
@@ -1220,6 +1245,69 @@ var init_yjsValues = __esm({
|
|
|
1220
1245
|
}
|
|
1221
1246
|
});
|
|
1222
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
|
+
|
|
1223
1311
|
// src/models/metaSync.ts
|
|
1224
1312
|
function inferFieldType(value) {
|
|
1225
1313
|
if (isStringSetYMap(value)) return "stringset";
|
|
@@ -1241,7 +1329,10 @@ function encodeDefault(value) {
|
|
|
1241
1329
|
if (value === void 0 || value === null) return void 0;
|
|
1242
1330
|
if (typeof value === "function") {
|
|
1243
1331
|
const name = KNOWN_FUNCTION_DEFAULTS.get(value);
|
|
1244
|
-
return name ? `$${name}` :
|
|
1332
|
+
return name ? `$${name}` : UNKNOWN_FUNCTION_DEFAULT;
|
|
1333
|
+
}
|
|
1334
|
+
if (typeof value === "string" && value.startsWith("$")) {
|
|
1335
|
+
return `$${value}`;
|
|
1245
1336
|
}
|
|
1246
1337
|
return value;
|
|
1247
1338
|
}
|
|
@@ -1267,7 +1358,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1267
1358
|
if (compoundConstraints.length > 0) {
|
|
1268
1359
|
let constraints = meta.get("_constraints");
|
|
1269
1360
|
if (!constraints) {
|
|
1270
|
-
constraints = new
|
|
1361
|
+
constraints = new Y3.Map();
|
|
1271
1362
|
meta.set("_constraints", constraints);
|
|
1272
1363
|
}
|
|
1273
1364
|
for (const constraint of compoundConstraints) {
|
|
@@ -1278,7 +1369,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1278
1369
|
if (relationships && Object.keys(relationships).length > 0) {
|
|
1279
1370
|
let rels = meta.get("_relationships");
|
|
1280
1371
|
if (!rels) {
|
|
1281
|
-
rels = new
|
|
1372
|
+
rels = new Y3.Map();
|
|
1282
1373
|
meta.set("_relationships", rels);
|
|
1283
1374
|
}
|
|
1284
1375
|
for (const [relName, relConfig] of Object.entries(relationships)) {
|
|
@@ -1290,7 +1381,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1290
1381
|
function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
1291
1382
|
let fieldMeta = metaMap.get(fieldName);
|
|
1292
1383
|
if (!fieldMeta) {
|
|
1293
|
-
fieldMeta = new
|
|
1384
|
+
fieldMeta = new Y3.Map();
|
|
1294
1385
|
metaMap.set(fieldName, fieldMeta);
|
|
1295
1386
|
}
|
|
1296
1387
|
setIfChanged(fieldMeta, "type", fieldOpts.type);
|
|
@@ -1307,7 +1398,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1307
1398
|
function syncConstraintMeta(constraintsMap, constraint) {
|
|
1308
1399
|
let cMeta = constraintsMap.get(constraint.name);
|
|
1309
1400
|
if (!cMeta) {
|
|
1310
|
-
cMeta = new
|
|
1401
|
+
cMeta = new Y3.Map();
|
|
1311
1402
|
constraintsMap.set(constraint.name, cMeta);
|
|
1312
1403
|
}
|
|
1313
1404
|
setIfChanged(cMeta, "type", "unique");
|
|
@@ -1317,7 +1408,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
|
|
|
1317
1408
|
function syncRelationshipMeta(relsMap, relName, relConfig) {
|
|
1318
1409
|
let relMeta = relsMap.get(relName);
|
|
1319
1410
|
if (!relMeta) {
|
|
1320
|
-
relMeta = new
|
|
1411
|
+
relMeta = new Y3.Map();
|
|
1321
1412
|
relsMap.set(relName, relMeta);
|
|
1322
1413
|
}
|
|
1323
1414
|
for (const [key, value] of Object.entries(relConfig)) {
|
|
@@ -1332,7 +1423,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
|
|
|
1332
1423
|
if (fieldName.startsWith("_")) continue;
|
|
1333
1424
|
let fieldMeta = meta.get(fieldName);
|
|
1334
1425
|
if (!fieldMeta) {
|
|
1335
|
-
fieldMeta = new
|
|
1426
|
+
fieldMeta = new Y3.Map();
|
|
1336
1427
|
meta.set(fieldName, fieldMeta);
|
|
1337
1428
|
}
|
|
1338
1429
|
if (!fieldMeta.has("type")) {
|
|
@@ -1348,18 +1439,58 @@ function setIfChanged(map, key, value) {
|
|
|
1348
1439
|
map.set(key, value);
|
|
1349
1440
|
}
|
|
1350
1441
|
}
|
|
1351
|
-
var
|
|
1442
|
+
var Y3, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
|
|
1352
1443
|
var init_metaSync = __esm({
|
|
1353
1444
|
"src/models/metaSync.ts"() {
|
|
1354
1445
|
"use strict";
|
|
1355
|
-
|
|
1446
|
+
Y3 = __toESM(require("yjs"), 1);
|
|
1356
1447
|
init_yjsValues();
|
|
1448
|
+
init_writeLifecycle();
|
|
1357
1449
|
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
1358
1450
|
_syncedCache = /* @__PURE__ */ new WeakMap();
|
|
1359
1451
|
}
|
|
1360
1452
|
});
|
|
1361
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
|
+
|
|
1362
1480
|
// src/models/relationshipManager.ts
|
|
1481
|
+
function warnIfOrderFieldNullable(modelClass, orderByField, relationshipDescription) {
|
|
1482
|
+
if (ALWAYS_PRESENT_ORDER_FIELDS.has(orderByField)) return;
|
|
1483
|
+
const schema = modelClass.getSchema?.();
|
|
1484
|
+
const fields = schema?.fields;
|
|
1485
|
+
if (!fields || typeof fields.get !== "function") return;
|
|
1486
|
+
const fieldOptions = fields.get(orderByField);
|
|
1487
|
+
if (!fieldOptions) return;
|
|
1488
|
+
if (fieldOptions.required !== true) {
|
|
1489
|
+
Logger.warn(
|
|
1490
|
+
`[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.)`
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1363
1494
|
function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
1364
1495
|
return async function() {
|
|
1365
1496
|
const relatedId = this[config.relatedIdField];
|
|
@@ -1368,6 +1499,16 @@ function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
|
1368
1499
|
};
|
|
1369
1500
|
}
|
|
1370
1501
|
function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbEngine) {
|
|
1502
|
+
const {
|
|
1503
|
+
relatedIdField,
|
|
1504
|
+
orderByField = "id",
|
|
1505
|
+
orderDirection = "ASC"
|
|
1506
|
+
} = config;
|
|
1507
|
+
warnIfOrderFieldNullable(
|
|
1508
|
+
targetModelClass,
|
|
1509
|
+
orderByField,
|
|
1510
|
+
`hasMany(${targetModelClass.name})`
|
|
1511
|
+
);
|
|
1371
1512
|
return async function(paginationArgs) {
|
|
1372
1513
|
const {
|
|
1373
1514
|
limit,
|
|
@@ -1375,71 +1516,39 @@ function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbE
|
|
|
1375
1516
|
beforeCursor,
|
|
1376
1517
|
direction = "forward"
|
|
1377
1518
|
} = paginationArgs || {};
|
|
1378
|
-
const {
|
|
1379
|
-
relatedIdField,
|
|
1380
|
-
orderByField = "id",
|
|
1381
|
-
orderDirection = "ASC"
|
|
1382
|
-
} = config;
|
|
1383
1519
|
const targetSchema = targetModelClass.getSchema();
|
|
1384
1520
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
1385
1521
|
Logger.error(
|
|
1386
1522
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasMany relationship has no valid schema name.`
|
|
1387
1523
|
);
|
|
1388
|
-
return { data: [],
|
|
1524
|
+
return { data: [], hasMore: false };
|
|
1389
1525
|
}
|
|
1390
|
-
const
|
|
1526
|
+
const forward = direction !== "backward";
|
|
1527
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
1391
1528
|
const queryOptions = {
|
|
1392
|
-
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 }
|
|
1529
|
+
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 },
|
|
1530
|
+
direction: forward ? 1 : -1
|
|
1393
1531
|
};
|
|
1394
|
-
if (limit !== void 0)
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
};
|
|
1401
|
-
} else if (direction === "backward") {
|
|
1402
|
-
if (beforeCursor) {
|
|
1403
|
-
filter[orderByField] = {
|
|
1404
|
-
[orderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
1405
|
-
};
|
|
1406
|
-
}
|
|
1407
|
-
queryOptions.sort = { [orderByField]: orderDirection === "ASC" ? -1 : 1 };
|
|
1408
|
-
}
|
|
1409
|
-
const result = await targetModelClass.query(filter, queryOptions);
|
|
1410
|
-
const results = result.data;
|
|
1411
|
-
let nextCursor = null;
|
|
1412
|
-
let prevCursor = null;
|
|
1413
|
-
const isFirstPage = direction === "forward" && !afterCursor;
|
|
1414
|
-
if (limit !== void 0) {
|
|
1415
|
-
if (direction === "forward") {
|
|
1416
|
-
if (results.length > limit) {
|
|
1417
|
-
nextCursor = results[limit - 1][orderByField];
|
|
1418
|
-
results.pop();
|
|
1419
|
-
}
|
|
1420
|
-
if (results.length > 0 && !isFirstPage) {
|
|
1421
|
-
prevCursor = results[0][orderByField];
|
|
1422
|
-
}
|
|
1423
|
-
} else {
|
|
1424
|
-
if (results.length > limit) {
|
|
1425
|
-
prevCursor = results[limit - 1][orderByField];
|
|
1426
|
-
results.pop();
|
|
1427
|
-
}
|
|
1428
|
-
results.reverse();
|
|
1429
|
-
if (results.length > 0 && beforeCursor) {
|
|
1430
|
-
nextCursor = results[results.length - 1][orderByField];
|
|
1431
|
-
}
|
|
1432
|
-
if (results.length > 0 && !beforeCursor) {
|
|
1433
|
-
if (results.length === limit) {
|
|
1434
|
-
prevCursor = results[0][orderByField];
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
return { data: results, nextCursor, prevCursor };
|
|
1532
|
+
if (limit !== void 0) queryOptions.limit = limit;
|
|
1533
|
+
if (cursor != null) queryOptions.uniqueStartKey = cursor;
|
|
1534
|
+
return targetModelClass.query(
|
|
1535
|
+
{ [relatedIdField]: this.id },
|
|
1536
|
+
queryOptions
|
|
1537
|
+
);
|
|
1440
1538
|
};
|
|
1441
1539
|
}
|
|
1442
1540
|
function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClass, joinModelClass, _dbEngine) {
|
|
1541
|
+
const {
|
|
1542
|
+
joinModelLocalField,
|
|
1543
|
+
joinModelRelatedField,
|
|
1544
|
+
joinModelOrderByField = "id",
|
|
1545
|
+
joinModelOrderDirection = "ASC"
|
|
1546
|
+
} = config;
|
|
1547
|
+
warnIfOrderFieldNullable(
|
|
1548
|
+
joinModelClass,
|
|
1549
|
+
joinModelOrderByField,
|
|
1550
|
+
`hasManyThrough(${targetModelClass.name} via ${joinModelClass.name})`
|
|
1551
|
+
);
|
|
1443
1552
|
return async function(paginationArgs) {
|
|
1444
1553
|
const {
|
|
1445
1554
|
limit,
|
|
@@ -1453,87 +1562,42 @@ function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClas
|
|
|
1453
1562
|
Logger.error(
|
|
1454
1563
|
`[RelationshipManager] Join model ${joinModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
1455
1564
|
);
|
|
1456
|
-
return { data: [],
|
|
1565
|
+
return { data: [], hasMore: false };
|
|
1457
1566
|
}
|
|
1458
1567
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
1459
1568
|
Logger.error(
|
|
1460
1569
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
1461
1570
|
);
|
|
1462
|
-
return { data: [],
|
|
1571
|
+
return { data: [], hasMore: false };
|
|
1463
1572
|
}
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1466
|
-
joinModelRelatedField,
|
|
1467
|
-
joinModelOrderByField = "id",
|
|
1468
|
-
joinModelOrderDirection = "ASC"
|
|
1469
|
-
} = config;
|
|
1470
|
-
const joinFilter = { [joinModelLocalField]: this.id };
|
|
1573
|
+
const forward = direction !== "backward";
|
|
1574
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
1471
1575
|
const joinQueryOptions = {
|
|
1472
1576
|
sort: {
|
|
1473
1577
|
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? 1 : -1
|
|
1474
1578
|
},
|
|
1475
|
-
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 }
|
|
1579
|
+
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 },
|
|
1580
|
+
direction: forward ? 1 : -1
|
|
1476
1581
|
};
|
|
1477
|
-
if (limit !== void 0)
|
|
1478
|
-
|
|
1479
|
-
}
|
|
1480
|
-
if (direction === "forward" && afterCursor) {
|
|
1481
|
-
joinFilter[joinModelOrderByField] = {
|
|
1482
|
-
[joinModelOrderDirection === "ASC" ? "$gt" : "$lt"]: afterCursor
|
|
1483
|
-
};
|
|
1484
|
-
} else if (direction === "backward") {
|
|
1485
|
-
if (beforeCursor) {
|
|
1486
|
-
joinFilter[joinModelOrderByField] = {
|
|
1487
|
-
[joinModelOrderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
joinQueryOptions.sort = {
|
|
1491
|
-
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? -1 : 1
|
|
1492
|
-
};
|
|
1493
|
-
}
|
|
1582
|
+
if (limit !== void 0) joinQueryOptions.limit = limit;
|
|
1583
|
+
if (cursor != null) joinQueryOptions.uniqueStartKey = cursor;
|
|
1494
1584
|
const joinResult = await joinModelClass.query(
|
|
1495
|
-
|
|
1585
|
+
{ [joinModelLocalField]: this.id },
|
|
1496
1586
|
joinQueryOptions
|
|
1497
1587
|
);
|
|
1498
|
-
const
|
|
1499
|
-
let
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
nextCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
1506
|
-
joinResults.pop();
|
|
1507
|
-
}
|
|
1508
|
-
if (joinResults.length > 0 && !isFirstPage) {
|
|
1509
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
1510
|
-
}
|
|
1511
|
-
} else {
|
|
1512
|
-
if (joinResults.length > limit) {
|
|
1513
|
-
prevCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
1514
|
-
joinResults.pop();
|
|
1515
|
-
}
|
|
1516
|
-
joinResults.reverse();
|
|
1517
|
-
if (joinResults.length > 0 && beforeCursor) {
|
|
1518
|
-
nextCursorFromJoin = joinResults[joinResults.length - 1][joinModelOrderByField];
|
|
1519
|
-
}
|
|
1520
|
-
if (joinResults.length > 0 && !beforeCursor) {
|
|
1521
|
-
if (joinResults.length === limit) {
|
|
1522
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1588
|
+
const relatedIds = joinResult.data.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
1589
|
+
let targetData = [];
|
|
1590
|
+
if (relatedIds.length > 0) {
|
|
1591
|
+
const targetResult = await targetModelClass.query({
|
|
1592
|
+
id: { $in: relatedIds }
|
|
1593
|
+
});
|
|
1594
|
+
targetData = targetResult.data;
|
|
1526
1595
|
}
|
|
1527
|
-
const relatedIds = joinResults.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
1528
|
-
if (relatedIds.length === 0)
|
|
1529
|
-
return { data: [], nextCursor: null, prevCursor: null };
|
|
1530
|
-
const targetResult = await targetModelClass.query({
|
|
1531
|
-
id: { $in: relatedIds }
|
|
1532
|
-
});
|
|
1533
1596
|
return {
|
|
1534
|
-
data:
|
|
1535
|
-
nextCursor:
|
|
1536
|
-
prevCursor:
|
|
1597
|
+
data: targetData,
|
|
1598
|
+
nextCursor: joinResult.nextCursor,
|
|
1599
|
+
prevCursor: joinResult.prevCursor,
|
|
1600
|
+
hasMore: joinResult.hasMore
|
|
1537
1601
|
};
|
|
1538
1602
|
};
|
|
1539
1603
|
}
|
|
@@ -1657,10 +1721,12 @@ function generateHasManyThroughRemoveMethod(_sourceModelClass, config, _targetMo
|
|
|
1657
1721
|
}, `remove-join-${config.joinModel}-${this.id}-${targetId}`);
|
|
1658
1722
|
};
|
|
1659
1723
|
}
|
|
1724
|
+
var ALWAYS_PRESENT_ORDER_FIELDS;
|
|
1660
1725
|
var init_relationshipManager = __esm({
|
|
1661
1726
|
"src/models/relationshipManager.ts"() {
|
|
1662
1727
|
"use strict";
|
|
1663
1728
|
init_BaseModel();
|
|
1729
|
+
ALWAYS_PRESENT_ORDER_FIELDS = /* @__PURE__ */ new Set(["id", "type"]);
|
|
1664
1730
|
}
|
|
1665
1731
|
});
|
|
1666
1732
|
|
|
@@ -2431,7 +2497,13 @@ var init_IncludeResolver = __esm({
|
|
|
2431
2497
|
}
|
|
2432
2498
|
return null;
|
|
2433
2499
|
}
|
|
2434
|
-
/**
|
|
2500
|
+
/**
|
|
2501
|
+
* Apply a projection to a single record. Assumes the projection was already
|
|
2502
|
+
* validated upstream (mixed inclusion+exclusion rejected by the translator):
|
|
2503
|
+
* `isInclusion` treats "any value === 1" as inclusion mode. `id` and `type`
|
|
2504
|
+
* are system-identity fields and are always kept in BOTH modes (mirrors the
|
|
2505
|
+
* DatabaseDO `_applyProjection`); `_meta` stays excludable.
|
|
2506
|
+
*/
|
|
2435
2507
|
_applyProjection(record, projection) {
|
|
2436
2508
|
const fields = Object.entries(projection);
|
|
2437
2509
|
if (fields.length === 0) return record;
|
|
@@ -2450,7 +2522,7 @@ var init_IncludeResolver = __esm({
|
|
|
2450
2522
|
} else {
|
|
2451
2523
|
const result = { ...record };
|
|
2452
2524
|
for (const [f, v] of fields) {
|
|
2453
|
-
if (v === 0) delete result[f];
|
|
2525
|
+
if (v === 0 && f !== "id" && f !== "type") delete result[f];
|
|
2454
2526
|
}
|
|
2455
2527
|
return result;
|
|
2456
2528
|
}
|
|
@@ -2470,21 +2542,23 @@ __export(BaseModel_exports, {
|
|
|
2470
2542
|
generateULID: () => generateULID
|
|
2471
2543
|
});
|
|
2472
2544
|
function generateULID() {
|
|
2473
|
-
return (0,
|
|
2545
|
+
return (0, import_ulid2.ulid)();
|
|
2474
2546
|
}
|
|
2475
|
-
var
|
|
2547
|
+
var Y5, import_ulid2, LogLevel, Logger, UniqueConstraintViolationError, RecordNotFoundError, SCHEMA_ACCESSORS_KEY, BaseModel2;
|
|
2476
2548
|
var init_BaseModel = __esm({
|
|
2477
2549
|
"src/models/BaseModel.ts"() {
|
|
2478
2550
|
"use strict";
|
|
2479
|
-
|
|
2480
|
-
|
|
2551
|
+
Y5 = __toESM(require("yjs"), 1);
|
|
2552
|
+
import_ulid2 = require("ulid");
|
|
2481
2553
|
init_StringSet();
|
|
2482
2554
|
init_documentTypes();
|
|
2483
2555
|
init_DocumentQueryTranslator();
|
|
2484
2556
|
init_CursorManager();
|
|
2485
2557
|
init_sql();
|
|
2486
2558
|
init_metaSync();
|
|
2559
|
+
init_writeLifecycle();
|
|
2487
2560
|
init_yjsValues();
|
|
2561
|
+
init_uniqueIndex();
|
|
2488
2562
|
LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
2489
2563
|
LogLevel2[LogLevel2["SILENT"] = 0] = "SILENT";
|
|
2490
2564
|
LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
|
|
@@ -2869,7 +2943,9 @@ var init_BaseModel = __esm({
|
|
|
2869
2943
|
this.setValue(fieldKey, data[fieldKey]);
|
|
2870
2944
|
this._constructorProvidedFields.add(fieldKey);
|
|
2871
2945
|
} else if (fieldOptions.default !== void 0) {
|
|
2872
|
-
const defaultValue =
|
|
2946
|
+
const defaultValue = resolveLiteralOrFunctionDefault(
|
|
2947
|
+
fieldOptions.default
|
|
2948
|
+
);
|
|
2873
2949
|
this.setValue(fieldKey, defaultValue);
|
|
2874
2950
|
}
|
|
2875
2951
|
}
|
|
@@ -3161,7 +3237,7 @@ var init_BaseModel = __esm({
|
|
|
3161
3237
|
}
|
|
3162
3238
|
getStringSetFromYjs(fieldName) {
|
|
3163
3239
|
const yjsData = this.getFromYjs(fieldName);
|
|
3164
|
-
if (yjsData instanceof
|
|
3240
|
+
if (yjsData instanceof Y5.Map) {
|
|
3165
3241
|
return Array.from(yjsData.keys());
|
|
3166
3242
|
}
|
|
3167
3243
|
if (yjsData && typeof yjsData === "object") {
|
|
@@ -3269,7 +3345,7 @@ var init_BaseModel = __esm({
|
|
|
3269
3345
|
}
|
|
3270
3346
|
const extractStringSetValues = (value) => {
|
|
3271
3347
|
if (!value) return [];
|
|
3272
|
-
if (value instanceof
|
|
3348
|
+
if (value instanceof Y5.Map) {
|
|
3273
3349
|
return Array.from(value.entries()).filter(([, isMember]) => Boolean(isMember)).map(([key]) => key);
|
|
3274
3350
|
}
|
|
3275
3351
|
if (Array.isArray(value)) {
|
|
@@ -3286,7 +3362,7 @@ var init_BaseModel = __esm({
|
|
|
3286
3362
|
if (!recordId) continue;
|
|
3287
3363
|
let itemData;
|
|
3288
3364
|
const stringSetValuesByField = {};
|
|
3289
|
-
if (recordData instanceof
|
|
3365
|
+
if (recordData instanceof Y5.Map) {
|
|
3290
3366
|
itemData = {};
|
|
3291
3367
|
const unknownFields = [];
|
|
3292
3368
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -3402,20 +3478,10 @@ var init_BaseModel = __esm({
|
|
|
3402
3478
|
Logger.verbose(
|
|
3403
3479
|
`[${this.name}] Setting up YMap observer for document ${docId}/${modelName}...`
|
|
3404
3480
|
);
|
|
3405
|
-
const
|
|
3406
|
-
const keyParts = [];
|
|
3407
|
-
for (const field of fields) {
|
|
3408
|
-
const value = recordData instanceof Y3.Map ? recordData.get(field) : recordData[field];
|
|
3409
|
-
if (value === null || value === void 0) {
|
|
3410
|
-
return null;
|
|
3411
|
-
}
|
|
3412
|
-
keyParts.push(String(value));
|
|
3413
|
-
}
|
|
3414
|
-
return keyParts.join("|");
|
|
3415
|
-
};
|
|
3481
|
+
const buildUniqueKey2 = (recordData, fields) => buildUniqueKey(recordData, fields);
|
|
3416
3482
|
const extractItemData = (key, recordData) => {
|
|
3417
3483
|
let itemData;
|
|
3418
|
-
if (recordData instanceof
|
|
3484
|
+
if (recordData instanceof Y5.Map) {
|
|
3419
3485
|
itemData = {};
|
|
3420
3486
|
const unknownFields = [];
|
|
3421
3487
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -3499,7 +3565,7 @@ var init_BaseModel = __esm({
|
|
|
3499
3565
|
const recordsByUniqueKey = /* @__PURE__ */ new Map();
|
|
3500
3566
|
for (const [recordId, recordData] of allRecords.entries()) {
|
|
3501
3567
|
if (recordIdsToDiscard.has(recordId)) continue;
|
|
3502
|
-
const uniqueKey =
|
|
3568
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
3503
3569
|
if (uniqueKey === null) continue;
|
|
3504
3570
|
if (!recordsByUniqueKey.has(uniqueKey)) {
|
|
3505
3571
|
recordsByUniqueKey.set(uniqueKey, []);
|
|
@@ -3545,7 +3611,7 @@ var init_BaseModel = __esm({
|
|
|
3545
3611
|
if (!recordData || !key) continue;
|
|
3546
3612
|
const itemData = extractItemData(key, recordData);
|
|
3547
3613
|
if (!itemData) continue;
|
|
3548
|
-
if (change.action === "add" && recordData instanceof
|
|
3614
|
+
if (change.action === "add" && recordData instanceof Y5.Map) {
|
|
3549
3615
|
Logger.verbose(
|
|
3550
3616
|
`[${this.name}] Setting up observer on newly added nested YMap for record ${key} in document ${docId}`
|
|
3551
3617
|
);
|
|
@@ -3643,9 +3709,12 @@ var init_BaseModel = __esm({
|
|
|
3643
3709
|
const recordData = documentYMap.get(idToDiscard);
|
|
3644
3710
|
if (!recordData) continue;
|
|
3645
3711
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
3646
|
-
const uniqueKey =
|
|
3712
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
3647
3713
|
if (uniqueKey === null) continue;
|
|
3648
|
-
const constraintMapName =
|
|
3714
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3715
|
+
modelName,
|
|
3716
|
+
constraint.name
|
|
3717
|
+
);
|
|
3649
3718
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
3650
3719
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
3651
3720
|
if (currentIndexValue === idToDiscard) {
|
|
@@ -3663,11 +3732,14 @@ var init_BaseModel = __esm({
|
|
|
3663
3732
|
}, `conflict-resolution-${modelName}-${docId}`);
|
|
3664
3733
|
yDoc.transact(() => {
|
|
3665
3734
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
3666
|
-
const constraintMapName =
|
|
3735
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3736
|
+
modelName,
|
|
3737
|
+
constraint.name
|
|
3738
|
+
);
|
|
3667
3739
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
3668
3740
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
3669
3741
|
if (!recordData) continue;
|
|
3670
|
-
const uniqueKey =
|
|
3742
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
3671
3743
|
if (uniqueKey === null) continue;
|
|
3672
3744
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
3673
3745
|
if (currentIndexValue !== recordId) {
|
|
@@ -3690,7 +3762,7 @@ var init_BaseModel = __esm({
|
|
|
3690
3762
|
`[${this.name}] Setting up observers on existing nested YMaps for ${modelName}/${docId}...`
|
|
3691
3763
|
);
|
|
3692
3764
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
3693
|
-
if (recordData instanceof
|
|
3765
|
+
if (recordData instanceof Y5.Map) {
|
|
3694
3766
|
Logger.verbose(
|
|
3695
3767
|
`[${this.name}] Setting up observer on existing nested YMap for record ${recordId} in document ${docId}`
|
|
3696
3768
|
);
|
|
@@ -3916,26 +3988,7 @@ var init_BaseModel = __esm({
|
|
|
3916
3988
|
* if the field hasn't been touched since the wire-format change in #561.
|
|
3917
3989
|
*/
|
|
3918
3990
|
applyStringSetChangeToYMap(recordYMap, fieldName, change) {
|
|
3919
|
-
|
|
3920
|
-
if (!(nested instanceof Y3.Map)) {
|
|
3921
|
-
const migrated = new Y3.Map();
|
|
3922
|
-
if (nested && typeof nested === "object") {
|
|
3923
|
-
for (const [member, marker] of Object.entries(
|
|
3924
|
-
nested
|
|
3925
|
-
)) {
|
|
3926
|
-
if (Boolean(marker)) migrated.set(member, true);
|
|
3927
|
-
}
|
|
3928
|
-
}
|
|
3929
|
-
recordYMap.set(fieldName, migrated);
|
|
3930
|
-
nested = migrated;
|
|
3931
|
-
}
|
|
3932
|
-
const target = nested;
|
|
3933
|
-
for (const member of change.additions) {
|
|
3934
|
-
target.set(member, true);
|
|
3935
|
-
}
|
|
3936
|
-
for (const member of change.removals) {
|
|
3937
|
-
target.delete(member);
|
|
3938
|
-
}
|
|
3991
|
+
applyStringSetChange(recordYMap, fieldName, change);
|
|
3939
3992
|
}
|
|
3940
3993
|
/**
|
|
3941
3994
|
* Deep equality check for comparing field values
|
|
@@ -4137,6 +4190,34 @@ var init_BaseModel = __esm({
|
|
|
4137
4190
|
`[${modelName}.save] getCurrentJSState() returned:`,
|
|
4138
4191
|
dataToSave
|
|
4139
4192
|
);
|
|
4193
|
+
const uniqueMissConflicts = /* @__PURE__ */ new Map();
|
|
4194
|
+
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
4195
|
+
const newUniqueKey = this._buildUniqueKey(
|
|
4196
|
+
constraint.fields,
|
|
4197
|
+
dataToSave,
|
|
4198
|
+
modelName,
|
|
4199
|
+
constraint.name
|
|
4200
|
+
);
|
|
4201
|
+
if (newUniqueKey === null) {
|
|
4202
|
+
continue;
|
|
4203
|
+
}
|
|
4204
|
+
const constraintMap = yDoc.getMap(
|
|
4205
|
+
uniqueIndexMapName(modelName, constraint.name)
|
|
4206
|
+
);
|
|
4207
|
+
if (!constraintMap.get(newUniqueKey)) {
|
|
4208
|
+
const constraintKeyValues = constraint.fields.map(
|
|
4209
|
+
(field) => dataToSave[field]
|
|
4210
|
+
);
|
|
4211
|
+
const conflictingRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4212
|
+
constraint,
|
|
4213
|
+
constraintKeyValues,
|
|
4214
|
+
{ excludeId: this.id }
|
|
4215
|
+
);
|
|
4216
|
+
if (conflictingRecord) {
|
|
4217
|
+
uniqueMissConflicts.set(constraint.name, conflictingRecord.id);
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4140
4221
|
await yDoc.transact(async () => {
|
|
4141
4222
|
if (upsertOnConstraint && upsertOnKey) {
|
|
4142
4223
|
const constraintMapName = `_uniqueIdx_${modelName}_${upsertOnConstraint.name}`;
|
|
@@ -4178,7 +4259,7 @@ var init_BaseModel = __esm({
|
|
|
4178
4259
|
Logger.debug(`[${modelName}.save] isUpdate: ${isUpdate}`);
|
|
4179
4260
|
Logger.debug(`[${modelName}.save] recordYMap exists: ${!!recordYMap}`);
|
|
4180
4261
|
if (!recordYMap) {
|
|
4181
|
-
recordYMap = new
|
|
4262
|
+
recordYMap = new Y5.Map();
|
|
4182
4263
|
Logger.verbose(
|
|
4183
4264
|
`[${modelName}] Creating new nested YMap for record ${this.id} in document ${targetDocId || "legacy"}`
|
|
4184
4265
|
);
|
|
@@ -4230,7 +4311,10 @@ var init_BaseModel = __esm({
|
|
|
4230
4311
|
);
|
|
4231
4312
|
continue;
|
|
4232
4313
|
}
|
|
4233
|
-
const constraintMapName =
|
|
4314
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4315
|
+
modelName,
|
|
4316
|
+
constraint.name
|
|
4317
|
+
);
|
|
4234
4318
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
4235
4319
|
Logger.verbose(
|
|
4236
4320
|
`[${modelName}] Save Check (Item '${this.id}'): GET from '${constraintMapName}' with Key '${newUniqueKey.substring(
|
|
@@ -4259,6 +4343,26 @@ var init_BaseModel = __esm({
|
|
|
4259
4343
|
this.id,
|
|
4260
4344
|
existingRecordIdWithNewKey
|
|
4261
4345
|
);
|
|
4346
|
+
} else if (!existingRecordIdWithNewKey) {
|
|
4347
|
+
const conflictingRecordId = uniqueMissConflicts.get(constraint.name);
|
|
4348
|
+
if (conflictingRecordId && conflictingRecordId !== this.id) {
|
|
4349
|
+
Logger.verbose(
|
|
4350
|
+
`[${modelName}] Save Check (Item '${this.id}'): index miss for '${constraint.name}', but engine fallback found conflicting index-less record ${conflictingRecordId}.`
|
|
4351
|
+
);
|
|
4352
|
+
throw new UniqueConstraintViolationError(
|
|
4353
|
+
`Unique constraint '${constraint.name}' violated for model '${modelName}' on fields [${constraint.fields.join(
|
|
4354
|
+
", "
|
|
4355
|
+
)}]. Attempted ID: ${this.id}. Value(s) starting with '${newUniqueKey.substring(
|
|
4356
|
+
0,
|
|
4357
|
+
50
|
|
4358
|
+
)}...' already exist for index-less record ID ${conflictingRecordId}.`,
|
|
4359
|
+
modelName,
|
|
4360
|
+
constraint.name,
|
|
4361
|
+
constraint.fields,
|
|
4362
|
+
this.id,
|
|
4363
|
+
conflictingRecordId
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4262
4366
|
}
|
|
4263
4367
|
}
|
|
4264
4368
|
if (isUpdate && oldData) {
|
|
@@ -4602,34 +4706,46 @@ var init_BaseModel = __esm({
|
|
|
4602
4706
|
const modelName = schema.options.name;
|
|
4603
4707
|
Logger.verbose(`[${modelName}] Executing query:`, { filter, options });
|
|
4604
4708
|
const hasIncludes = options?.include && options.include.length > 0;
|
|
4605
|
-
const
|
|
4709
|
+
const requestedLimit = options?.limit;
|
|
4710
|
+
const overFetch = typeof requestedLimit === "number" && requestedLimit > 0;
|
|
4711
|
+
let translatorOptions = hasIncludes && options?.projection ? { ...options, projection: void 0 } : options;
|
|
4712
|
+
if (overFetch) {
|
|
4713
|
+
translatorOptions = {
|
|
4714
|
+
...translatorOptions || {},
|
|
4715
|
+
limit: requestedLimit + 1
|
|
4716
|
+
};
|
|
4717
|
+
}
|
|
4606
4718
|
const translator = new DocumentQueryTranslator(modelName, schema.fields);
|
|
4607
4719
|
const translatedQuery = translator.translateFind(filter, translatorOptions);
|
|
4608
4720
|
Logger.verbose(`[${modelName}] Translated SQL:`, translatedQuery.sql);
|
|
4609
4721
|
Logger.verbose(`[${modelName}] SQL params:`, translatedQuery.params);
|
|
4610
|
-
|
|
4722
|
+
let results = await _BaseModel.dbInstance.query(
|
|
4611
4723
|
translatedQuery.sql,
|
|
4612
4724
|
translatedQuery.params
|
|
4613
4725
|
);
|
|
4614
4726
|
Logger.verbose(`[${modelName}] Query returned ${results.length} results`);
|
|
4727
|
+
let hasMore = false;
|
|
4728
|
+
if (overFetch && results.length > requestedLimit) {
|
|
4729
|
+
hasMore = true;
|
|
4730
|
+
results = results.slice(0, requestedLimit);
|
|
4731
|
+
}
|
|
4615
4732
|
if (options?.include && options.include.length > 0) {
|
|
4616
4733
|
const { IncludeResolver: IncludeResolver2 } = await Promise.resolve().then(() => (init_IncludeResolver(), IncludeResolver_exports));
|
|
4617
4734
|
const resolver = new IncludeResolver2(_BaseModel.dbInstance);
|
|
4618
4735
|
await resolver.resolve(results, options.include, 0, modelName);
|
|
4619
4736
|
}
|
|
4620
|
-
const requestedLimit = options?.limit;
|
|
4621
|
-
const hasMore = CursorManager.hasMoreResults(
|
|
4622
|
-
requestedLimit,
|
|
4623
|
-
results.length
|
|
4624
|
-
);
|
|
4625
4737
|
const isFirstPage = !options?.uniqueStartKey;
|
|
4738
|
+
const direction = options?.direction || 1;
|
|
4626
4739
|
const cursors = CursorManager.generateResultCursors(
|
|
4627
4740
|
results,
|
|
4628
4741
|
translatedQuery.sortFields,
|
|
4629
|
-
|
|
4742
|
+
direction,
|
|
4630
4743
|
hasMore,
|
|
4631
4744
|
isFirstPage
|
|
4632
4745
|
);
|
|
4746
|
+
if (direction === -1) {
|
|
4747
|
+
results.reverse();
|
|
4748
|
+
}
|
|
4633
4749
|
let transformedData;
|
|
4634
4750
|
if (options?.projection && Object.keys(options.projection).length > 0) {
|
|
4635
4751
|
transformedData = results.map((row) => {
|
|
@@ -5205,7 +5321,7 @@ var init_BaseModel = __esm({
|
|
|
5205
5321
|
);
|
|
5206
5322
|
}
|
|
5207
5323
|
for (const [recordId, recordYMap] of documentYMap.entries()) {
|
|
5208
|
-
if (recordYMap instanceof
|
|
5324
|
+
if (recordYMap instanceof Y5.Map) {
|
|
5209
5325
|
const instance = new this({ id: recordId });
|
|
5210
5326
|
instance._metaDocId = docId;
|
|
5211
5327
|
const connectedDoc = modelConstructor.connectedDocuments.get(docId);
|
|
@@ -5230,6 +5346,50 @@ var init_BaseModel = __esm({
|
|
|
5230
5346
|
}
|
|
5231
5347
|
return allInstances;
|
|
5232
5348
|
}
|
|
5349
|
+
/**
|
|
5350
|
+
* Resolve a record by its unique-constraint field values via an engine query.
|
|
5351
|
+
* Used as a fallback when the `_uniqueIdx_<model>_<constraint>` map has no
|
|
5352
|
+
* entry for a key.
|
|
5353
|
+
*
|
|
5354
|
+
* The index map is a cache over the SQLite projection, not the source of
|
|
5355
|
+
* truth: every record is loaded into the projection on `initializeForDocument`
|
|
5356
|
+
* regardless of index presence, but a record written by a client predating
|
|
5357
|
+
* index maintenance (legacy data) is missing from the index. Such a record
|
|
5358
|
+
* silently escapes both `upsertByUnique` lookups and the `save()` uniqueness
|
|
5359
|
+
* check. A query over the constraint fields finds it reliably.
|
|
5360
|
+
*
|
|
5361
|
+
* Contract: RESOLVE ONLY. Returns the owning record (with `_metaDocId` set to
|
|
5362
|
+
* the document that holds it, via `queryOne`) or `null`. Callers decide
|
|
5363
|
+
* whether to heal the missing index entry (write paths) and do their own
|
|
5364
|
+
* logging — this method never mutates the index, so read paths stay
|
|
5365
|
+
* read-only. Mirrors `buildUniqueKey`: a null/undefined value in any
|
|
5366
|
+
* constraint field means the record does not participate in the constraint,
|
|
5367
|
+
* so no query runs.
|
|
5368
|
+
*/
|
|
5369
|
+
static async _resolveByUniqueFallback(constraint, keyValues, options) {
|
|
5370
|
+
if (!_BaseModel.dbInstance) {
|
|
5371
|
+
return null;
|
|
5372
|
+
}
|
|
5373
|
+
const filter = {};
|
|
5374
|
+
for (let i = 0; i < constraint.fields.length; i++) {
|
|
5375
|
+
const value = keyValues[i];
|
|
5376
|
+
if (value === null || value === void 0) {
|
|
5377
|
+
return null;
|
|
5378
|
+
}
|
|
5379
|
+
filter[constraint.fields[i]] = value;
|
|
5380
|
+
}
|
|
5381
|
+
if (options?.excludeId) {
|
|
5382
|
+
if ("id" in filter) {
|
|
5383
|
+
if (filter.id === options.excludeId) {
|
|
5384
|
+
return null;
|
|
5385
|
+
}
|
|
5386
|
+
} else {
|
|
5387
|
+
filter.id = { $ne: options.excludeId };
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
const found = await this.queryOne(filter);
|
|
5391
|
+
return found ?? null;
|
|
5392
|
+
}
|
|
5233
5393
|
static async findByUnique(constraintName, value) {
|
|
5234
5394
|
const modelConstructor = this;
|
|
5235
5395
|
const schema = modelConstructor.getSchema();
|
|
@@ -5268,7 +5428,10 @@ var init_BaseModel = __esm({
|
|
|
5268
5428
|
const documentInfo = modelConstructor.connectedDocuments.get(docId);
|
|
5269
5429
|
if (!documentInfo) continue;
|
|
5270
5430
|
const yDoc = documentInfo.yDoc;
|
|
5271
|
-
const constraintMapName2 =
|
|
5431
|
+
const constraintMapName2 = uniqueIndexMapName(
|
|
5432
|
+
currentModelName,
|
|
5433
|
+
constraint.name
|
|
5434
|
+
);
|
|
5272
5435
|
const constraintMap2 = yDoc.getMap(constraintMapName2);
|
|
5273
5436
|
const recordId2 = constraintMap2.get(uniqueKeyString);
|
|
5274
5437
|
if (recordId2) {
|
|
@@ -5276,7 +5439,7 @@ var init_BaseModel = __esm({
|
|
|
5276
5439
|
const documentYMap = modelConstructor.documentYMaps.get(documentYMapKey);
|
|
5277
5440
|
if (documentYMap) {
|
|
5278
5441
|
const recordYMap2 = documentYMap.get(recordId2);
|
|
5279
|
-
if (recordYMap2 && recordYMap2 instanceof
|
|
5442
|
+
if (recordYMap2 && recordYMap2 instanceof Y5.Map) {
|
|
5280
5443
|
const instance = new modelConstructor({
|
|
5281
5444
|
id: recordId2
|
|
5282
5445
|
});
|
|
@@ -5290,6 +5453,16 @@ var init_BaseModel = __esm({
|
|
|
5290
5453
|
}
|
|
5291
5454
|
}
|
|
5292
5455
|
}
|
|
5456
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
5457
|
+
constraint,
|
|
5458
|
+
keyValues
|
|
5459
|
+
);
|
|
5460
|
+
if (fallbackRecord) {
|
|
5461
|
+
Logger.verbose(
|
|
5462
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
5463
|
+
);
|
|
5464
|
+
return fallbackRecord;
|
|
5465
|
+
}
|
|
5293
5466
|
Logger.verbose(
|
|
5294
5467
|
`[${currentModelName}.findByUnique] Record not found in any connected document`
|
|
5295
5468
|
);
|
|
@@ -5303,17 +5476,30 @@ var init_BaseModel = __esm({
|
|
|
5303
5476
|
`[${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.`
|
|
5304
5477
|
);
|
|
5305
5478
|
}
|
|
5306
|
-
const constraintMapName =
|
|
5479
|
+
const constraintMapName = uniqueIndexMapName(
|
|
5480
|
+
currentModelName,
|
|
5481
|
+
constraint.name
|
|
5482
|
+
);
|
|
5307
5483
|
const constraintMap = legacyDocInfo.yDoc.getMap(constraintMapName);
|
|
5308
5484
|
const recordId = constraintMap.get(uniqueKeyString);
|
|
5309
5485
|
if (!recordId) {
|
|
5486
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
5487
|
+
constraint,
|
|
5488
|
+
keyValues
|
|
5489
|
+
);
|
|
5490
|
+
if (fallbackRecord) {
|
|
5491
|
+
Logger.verbose(
|
|
5492
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
5493
|
+
);
|
|
5494
|
+
return fallbackRecord;
|
|
5495
|
+
}
|
|
5310
5496
|
return null;
|
|
5311
5497
|
}
|
|
5312
5498
|
const recordYMap = legacyDocumentMap.get(recordId);
|
|
5313
5499
|
if (!recordYMap) {
|
|
5314
5500
|
return null;
|
|
5315
5501
|
}
|
|
5316
|
-
if (recordYMap instanceof
|
|
5502
|
+
if (recordYMap instanceof Y5.Map) {
|
|
5317
5503
|
const instance = new modelConstructor({
|
|
5318
5504
|
id: recordId
|
|
5319
5505
|
});
|
|
@@ -5371,7 +5557,10 @@ var init_BaseModel = __esm({
|
|
|
5371
5557
|
`[${currentModelName}] upsertByUnique for '${constraintName}': unique key cannot be built due to null/undefined values. Upsert cannot proceed reliably.`
|
|
5372
5558
|
);
|
|
5373
5559
|
}
|
|
5374
|
-
const constraintMapName =
|
|
5560
|
+
const constraintMapName = uniqueIndexMapName(
|
|
5561
|
+
currentModelName,
|
|
5562
|
+
constraint.name
|
|
5563
|
+
);
|
|
5375
5564
|
let existingRecord = null;
|
|
5376
5565
|
if (connectedDocuments && connectedDocuments.size > 0) {
|
|
5377
5566
|
for (const docId of connectedDocuments.keys()) {
|
|
@@ -5389,6 +5578,33 @@ var init_BaseModel = __esm({
|
|
|
5389
5578
|
}
|
|
5390
5579
|
} else {
|
|
5391
5580
|
}
|
|
5581
|
+
if (!existingRecord) {
|
|
5582
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
5583
|
+
constraint,
|
|
5584
|
+
keyValuesForLookup
|
|
5585
|
+
);
|
|
5586
|
+
if (fallbackRecord) {
|
|
5587
|
+
existingRecord = fallbackRecord;
|
|
5588
|
+
const healDocId = fallbackRecord._metaDocId;
|
|
5589
|
+
const healDocInfo = healDocId ? connectedDocuments.get(healDocId) : void 0;
|
|
5590
|
+
const canHeal = !!healDocInfo && healDocInfo.permissionHint !== "read";
|
|
5591
|
+
if (healDocInfo && canHeal) {
|
|
5592
|
+
healDocInfo.yDoc.transact(() => {
|
|
5593
|
+
const healMap = healDocInfo.yDoc.getMap(constraintMapName);
|
|
5594
|
+
if (healMap.get(uniqueKeyString) !== fallbackRecord.id) {
|
|
5595
|
+
healMap.set(uniqueKeyString, fallbackRecord.id);
|
|
5596
|
+
}
|
|
5597
|
+
}, `heal-unique-index-${currentModelName}-${healDocId}`);
|
|
5598
|
+
Logger.verbose(
|
|
5599
|
+
`[${currentModelName}] upsertByUnique: index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id}; healed missing index entry (legacy index-less record).`
|
|
5600
|
+
);
|
|
5601
|
+
} else {
|
|
5602
|
+
Logger.verbose(
|
|
5603
|
+
`[${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).`
|
|
5604
|
+
);
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5392
5608
|
if (options?.objectMustExist) {
|
|
5393
5609
|
if (!existingRecord) {
|
|
5394
5610
|
throw new RecordNotFoundError(
|
|
@@ -5545,7 +5761,7 @@ var init_BaseModel = __esm({
|
|
|
5545
5761
|
for (const [fieldKey, fieldOptions] of schema.fields) {
|
|
5546
5762
|
if (fieldOptions?.type !== "stringset") continue;
|
|
5547
5763
|
const value = recordYMap.get(fieldKey);
|
|
5548
|
-
if (value instanceof
|
|
5764
|
+
if (value instanceof Y5.Map) {
|
|
5549
5765
|
this.observeStringSetMapOnce(value);
|
|
5550
5766
|
}
|
|
5551
5767
|
}
|
|
@@ -5812,7 +6028,144 @@ var import_sql4 = __toESM(require("sql.js"), 1);
|
|
|
5812
6028
|
init_BaseModel();
|
|
5813
6029
|
var import_async_mutex = require("async-mutex");
|
|
5814
6030
|
init_sql();
|
|
6031
|
+
|
|
6032
|
+
// src/utils/environment.ts
|
|
6033
|
+
function detectEnvironment() {
|
|
6034
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
6035
|
+
return "node";
|
|
6036
|
+
}
|
|
6037
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
6038
|
+
return "browser";
|
|
6039
|
+
}
|
|
6040
|
+
if (typeof self !== "undefined" && typeof self.importScripts === "function") {
|
|
6041
|
+
return "browser";
|
|
6042
|
+
}
|
|
6043
|
+
return "unknown";
|
|
6044
|
+
}
|
|
6045
|
+
function isNode() {
|
|
6046
|
+
return detectEnvironment() === "node";
|
|
6047
|
+
}
|
|
6048
|
+
function isBrowser() {
|
|
6049
|
+
return detectEnvironment() === "browser";
|
|
6050
|
+
}
|
|
6051
|
+
async function isNodeModuleAvailable(moduleName) {
|
|
6052
|
+
if (!isNode()) {
|
|
6053
|
+
return false;
|
|
6054
|
+
}
|
|
6055
|
+
try {
|
|
6056
|
+
await import(
|
|
6057
|
+
/* @vite-ignore */
|
|
6058
|
+
moduleName
|
|
6059
|
+
);
|
|
6060
|
+
return true;
|
|
6061
|
+
} catch {
|
|
6062
|
+
return false;
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
var features = {
|
|
6066
|
+
get hasWebWorkers() {
|
|
6067
|
+
return isBrowser() && typeof Worker !== "undefined";
|
|
6068
|
+
},
|
|
6069
|
+
get hasFileSystem() {
|
|
6070
|
+
return isNode();
|
|
6071
|
+
},
|
|
6072
|
+
get hasWebAssembly() {
|
|
6073
|
+
return typeof WebAssembly !== "undefined";
|
|
6074
|
+
},
|
|
6075
|
+
async hasBetterSqlite3() {
|
|
6076
|
+
return await isNodeModuleAvailable("better-sqlite3");
|
|
6077
|
+
}
|
|
6078
|
+
};
|
|
6079
|
+
|
|
6080
|
+
// src/engines/SqljsEngine.ts
|
|
6081
|
+
var import_meta = {};
|
|
5815
6082
|
var SQL_WASM_URL = "/sql-wasm.wasm";
|
|
6083
|
+
var SQL_WASM_FILENAME = "sql-wasm.wasm";
|
|
6084
|
+
async function resolveNodeWasmPath() {
|
|
6085
|
+
if (!isNode()) {
|
|
6086
|
+
return null;
|
|
6087
|
+
}
|
|
6088
|
+
try {
|
|
6089
|
+
const cjsRequire = typeof require !== "undefined" ? require : void 0;
|
|
6090
|
+
if (cjsRequire) {
|
|
6091
|
+
return cjsRequire.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6092
|
+
}
|
|
6093
|
+
} catch {
|
|
6094
|
+
}
|
|
6095
|
+
try {
|
|
6096
|
+
const metaUrl = import_meta.url;
|
|
6097
|
+
if (metaUrl) {
|
|
6098
|
+
const { createRequire } = await import(
|
|
6099
|
+
/* @vite-ignore */
|
|
6100
|
+
"module"
|
|
6101
|
+
);
|
|
6102
|
+
const req = createRequire(metaUrl);
|
|
6103
|
+
return req.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6104
|
+
}
|
|
6105
|
+
} catch {
|
|
6106
|
+
}
|
|
6107
|
+
return null;
|
|
6108
|
+
}
|
|
6109
|
+
async function preflightNodeWasm(wasmPath) {
|
|
6110
|
+
if (!isNode()) {
|
|
6111
|
+
return;
|
|
6112
|
+
}
|
|
6113
|
+
if (/^[a-z]+:\/\//i.test(wasmPath)) {
|
|
6114
|
+
return;
|
|
6115
|
+
}
|
|
6116
|
+
const { existsSync } = await import(
|
|
6117
|
+
/* @vite-ignore */
|
|
6118
|
+
"fs"
|
|
6119
|
+
);
|
|
6120
|
+
if (!existsSync(wasmPath)) {
|
|
6121
|
+
throw new Error(
|
|
6122
|
+
`[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()).`
|
|
6123
|
+
);
|
|
6124
|
+
}
|
|
6125
|
+
}
|
|
6126
|
+
async function loadSqlJsModule(locateFile) {
|
|
6127
|
+
let abortReject;
|
|
6128
|
+
const toError = (reason) => reason instanceof Error ? reason : new Error(
|
|
6129
|
+
`[SqljsEngine] sql.js aborted during initialization: ${String(
|
|
6130
|
+
reason
|
|
6131
|
+
)}`
|
|
6132
|
+
);
|
|
6133
|
+
const nodeProcess = isNode() && typeof process !== "undefined" ? process : void 0;
|
|
6134
|
+
const onUnhandled = (reason) => {
|
|
6135
|
+
const err = toError(reason);
|
|
6136
|
+
if (/Aborted/.test(err.message) || /Aborted/.test(String(reason))) {
|
|
6137
|
+
abortReject?.(err);
|
|
6138
|
+
return;
|
|
6139
|
+
}
|
|
6140
|
+
if (nodeProcess && nodeProcess.listenerCount("unhandledRejection") === 1) {
|
|
6141
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6142
|
+
void Promise.reject(reason);
|
|
6143
|
+
}
|
|
6144
|
+
};
|
|
6145
|
+
if (nodeProcess) {
|
|
6146
|
+
nodeProcess.on("unhandledRejection", onUnhandled);
|
|
6147
|
+
}
|
|
6148
|
+
const abortGuard = new Promise((_resolve, reject) => {
|
|
6149
|
+
abortReject = reject;
|
|
6150
|
+
});
|
|
6151
|
+
abortGuard.catch(() => {
|
|
6152
|
+
});
|
|
6153
|
+
try {
|
|
6154
|
+
const initPromise = (0, import_sql4.default)({
|
|
6155
|
+
locateFile,
|
|
6156
|
+
// Layer 1: emscripten onAbort.
|
|
6157
|
+
onAbort: (reason) => abortReject?.(toError(reason))
|
|
6158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6159
|
+
});
|
|
6160
|
+
return await Promise.race([initPromise, abortGuard]);
|
|
6161
|
+
} finally {
|
|
6162
|
+
if (nodeProcess) {
|
|
6163
|
+
setTimeout(() => {
|
|
6164
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6165
|
+
}, 0);
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
5816
6169
|
var TransactionalSQLJSOperations = class {
|
|
5817
6170
|
db;
|
|
5818
6171
|
engine;
|
|
@@ -5878,22 +6231,37 @@ var SqljsEngine = class _SqljsEngine {
|
|
|
5878
6231
|
}
|
|
5879
6232
|
async initialize(engineOptions) {
|
|
5880
6233
|
Logger.info("[SqljsEngine] Initializing SQL.js engine...");
|
|
6234
|
+
const loadingModuleThisCall = !_SqljsEngine.SQL;
|
|
5881
6235
|
try {
|
|
5882
6236
|
if (!_SqljsEngine.SQL) {
|
|
5883
|
-
|
|
6237
|
+
let locateFileConfig;
|
|
6238
|
+
if (engineOptions?.locateFile) {
|
|
6239
|
+
locateFileConfig = engineOptions.locateFile;
|
|
6240
|
+
} else if (engineOptions?.wasmURL) {
|
|
6241
|
+
const url = engineOptions.wasmURL;
|
|
6242
|
+
locateFileConfig = (_file) => url;
|
|
6243
|
+
} else {
|
|
6244
|
+
const nodeWasmPath = await resolveNodeWasmPath();
|
|
6245
|
+
const resolved = nodeWasmPath ?? SQL_WASM_URL;
|
|
6246
|
+
locateFileConfig = (_file) => resolved;
|
|
6247
|
+
}
|
|
6248
|
+
const wasmPath = locateFileConfig(SQL_WASM_FILENAME);
|
|
5884
6249
|
Logger.debug(
|
|
5885
6250
|
"[SqljsEngine] Attempting to load SQL.js WASM from:",
|
|
5886
|
-
|
|
6251
|
+
wasmPath
|
|
5887
6252
|
);
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
6253
|
+
await preflightNodeWasm(wasmPath);
|
|
6254
|
+
const SQL = await loadSqlJsModule(locateFileConfig);
|
|
6255
|
+
_SqljsEngine.SQL = SQL;
|
|
5891
6256
|
Logger.info("[SqljsEngine] SQL.js WASM loaded successfully.");
|
|
5892
6257
|
}
|
|
5893
6258
|
this.db = new _SqljsEngine.SQL.Database();
|
|
5894
6259
|
Logger.info("[SqljsEngine] SQL.js Database initialized in memory.");
|
|
5895
6260
|
this.updateTableNames();
|
|
5896
6261
|
} catch (error) {
|
|
6262
|
+
if (loadingModuleThisCall) {
|
|
6263
|
+
_SqljsEngine.SQL = null;
|
|
6264
|
+
}
|
|
5897
6265
|
Logger.error("[SqljsEngine] Error during SQL.js initialization:", error);
|
|
5898
6266
|
throw error;
|
|
5899
6267
|
}
|
|
@@ -6179,54 +6547,6 @@ var SqljsEngine = class _SqljsEngine {
|
|
|
6179
6547
|
}
|
|
6180
6548
|
};
|
|
6181
6549
|
|
|
6182
|
-
// src/utils/environment.ts
|
|
6183
|
-
function detectEnvironment() {
|
|
6184
|
-
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
6185
|
-
return "node";
|
|
6186
|
-
}
|
|
6187
|
-
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
6188
|
-
return "browser";
|
|
6189
|
-
}
|
|
6190
|
-
if (typeof self !== "undefined" && typeof self.importScripts === "function") {
|
|
6191
|
-
return "browser";
|
|
6192
|
-
}
|
|
6193
|
-
return "unknown";
|
|
6194
|
-
}
|
|
6195
|
-
function isNode() {
|
|
6196
|
-
return detectEnvironment() === "node";
|
|
6197
|
-
}
|
|
6198
|
-
function isBrowser() {
|
|
6199
|
-
return detectEnvironment() === "browser";
|
|
6200
|
-
}
|
|
6201
|
-
async function isNodeModuleAvailable(moduleName) {
|
|
6202
|
-
if (!isNode()) {
|
|
6203
|
-
return false;
|
|
6204
|
-
}
|
|
6205
|
-
try {
|
|
6206
|
-
await import(
|
|
6207
|
-
/* @vite-ignore */
|
|
6208
|
-
moduleName
|
|
6209
|
-
);
|
|
6210
|
-
return true;
|
|
6211
|
-
} catch {
|
|
6212
|
-
return false;
|
|
6213
|
-
}
|
|
6214
|
-
}
|
|
6215
|
-
var features = {
|
|
6216
|
-
get hasWebWorkers() {
|
|
6217
|
-
return isBrowser() && typeof Worker !== "undefined";
|
|
6218
|
-
},
|
|
6219
|
-
get hasFileSystem() {
|
|
6220
|
-
return isNode();
|
|
6221
|
-
},
|
|
6222
|
-
get hasWebAssembly() {
|
|
6223
|
-
return typeof WebAssembly !== "undefined";
|
|
6224
|
-
},
|
|
6225
|
-
async hasBetterSqlite3() {
|
|
6226
|
-
return await isNodeModuleAvailable("better-sqlite3");
|
|
6227
|
-
}
|
|
6228
|
-
};
|
|
6229
|
-
|
|
6230
6550
|
// src/engines/BrowserDatabaseFactory.ts
|
|
6231
6551
|
init_BaseModel();
|
|
6232
6552
|
var BrowserDatabaseFactory = class {
|
|
@@ -6537,6 +6857,7 @@ init_StringSet();
|
|
|
6537
6857
|
// src/models/schema.ts
|
|
6538
6858
|
init_BaseModel();
|
|
6539
6859
|
init_ModelRegistry();
|
|
6860
|
+
init_uniqueIndex();
|
|
6540
6861
|
function defineModelSchema(input) {
|
|
6541
6862
|
const { name, fields } = input;
|
|
6542
6863
|
const options = {
|
|
@@ -6609,7 +6930,7 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
|
|
|
6609
6930
|
for (const [fieldName, options] of fields.entries()) {
|
|
6610
6931
|
if (options.unique) {
|
|
6611
6932
|
resolved.push({
|
|
6612
|
-
name:
|
|
6933
|
+
name: fieldUniqueConstraintName(modelName, fieldName),
|
|
6613
6934
|
fields: [fieldName]
|
|
6614
6935
|
});
|
|
6615
6936
|
}
|
|
@@ -6673,7 +6994,7 @@ function attachAndRegisterModel(modelClass, schema) {
|
|
|
6673
6994
|
}
|
|
6674
6995
|
|
|
6675
6996
|
// src/utils/yDocSchema.ts
|
|
6676
|
-
var
|
|
6997
|
+
var Y6 = __toESM(require("yjs"), 1);
|
|
6677
6998
|
init_yjsValues();
|
|
6678
6999
|
function discoverSchema(yDoc) {
|
|
6679
7000
|
const models = {};
|
|
@@ -6708,7 +7029,7 @@ function discoverModelNames(yDoc) {
|
|
|
6708
7029
|
function materializeMap(yDoc, key) {
|
|
6709
7030
|
try {
|
|
6710
7031
|
const map = yDoc.getMap(key);
|
|
6711
|
-
return map instanceof
|
|
7032
|
+
return map instanceof Y6.Map ? map : null;
|
|
6712
7033
|
} catch {
|
|
6713
7034
|
return null;
|
|
6714
7035
|
}
|
|
@@ -6718,11 +7039,11 @@ function readModelMeta(metaMap) {
|
|
|
6718
7039
|
let constraints;
|
|
6719
7040
|
let relationships;
|
|
6720
7041
|
for (const [key, value] of metaMap.entries()) {
|
|
6721
|
-
if (key === "_constraints" && value instanceof
|
|
7042
|
+
if (key === "_constraints" && value instanceof Y6.Map) {
|
|
6722
7043
|
constraints = readConstraints(value);
|
|
6723
|
-
} else if (key === "_relationships" && value instanceof
|
|
7044
|
+
} else if (key === "_relationships" && value instanceof Y6.Map) {
|
|
6724
7045
|
relationships = readRelationships(value);
|
|
6725
|
-
} else if (value instanceof
|
|
7046
|
+
} else if (value instanceof Y6.Map) {
|
|
6726
7047
|
fields[key] = readFieldMeta(value);
|
|
6727
7048
|
}
|
|
6728
7049
|
}
|
|
@@ -6757,7 +7078,7 @@ function inferModelFromData(dataMap) {
|
|
|
6757
7078
|
const fields = {};
|
|
6758
7079
|
let sampled = 0;
|
|
6759
7080
|
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
6760
|
-
if (!(recordValue instanceof
|
|
7081
|
+
if (!(recordValue instanceof Y6.Map)) continue;
|
|
6761
7082
|
if (++sampled > 5) break;
|
|
6762
7083
|
for (const [fieldName, value] of recordValue.entries()) {
|
|
6763
7084
|
if (fieldName.startsWith("_")) continue;
|
|
@@ -6785,7 +7106,7 @@ function inferTypeFromValue(value) {
|
|
|
6785
7106
|
function readConstraints(constraintsMap) {
|
|
6786
7107
|
const out = {};
|
|
6787
7108
|
for (const [name, value] of constraintsMap.entries()) {
|
|
6788
|
-
if (!(value instanceof
|
|
7109
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
6789
7110
|
let fields = [];
|
|
6790
7111
|
const rawFields = value.get("fields");
|
|
6791
7112
|
if (typeof rawFields === "string") {
|
|
@@ -6804,7 +7125,7 @@ function readConstraints(constraintsMap) {
|
|
|
6804
7125
|
function readRelationships(relsMap) {
|
|
6805
7126
|
const out = {};
|
|
6806
7127
|
for (const [name, value] of relsMap.entries()) {
|
|
6807
|
-
if (!(value instanceof
|
|
7128
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
6808
7129
|
const rel = {};
|
|
6809
7130
|
for (const [k, v] of value.entries()) {
|
|
6810
7131
|
rel[k] = v;
|