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.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -344,21 +350,36 @@ var init_CursorManager = __esm({
|
|
|
344
350
|
return fields;
|
|
345
351
|
}
|
|
346
352
|
/**
|
|
347
|
-
* Build ORDER BY clause from sort specification
|
|
353
|
+
* Build ORDER BY clause from sort specification.
|
|
354
|
+
*
|
|
355
|
+
* `direction` is the pagination direction (1 = forward, -1 = backward).
|
|
356
|
+
* For backward pagination each field's effective ORDER BY direction is
|
|
357
|
+
* reversed so the cursor's flipped inequality (built by
|
|
358
|
+
* `buildPaginationConditions`) returns the page adjacent to the cursor,
|
|
359
|
+
* not the globally smallest/largest window. Without this,
|
|
360
|
+
* `field < cursor ... ORDER BY field ASC LIMIT n` returns the n smallest
|
|
361
|
+
* rows overall instead of the n rows just below the cursor.
|
|
362
|
+
*
|
|
363
|
+
* The returned `directions` array carries the *declared* per-field sort
|
|
364
|
+
* directions (unaffected by `direction`) — `buildPaginationConditions`
|
|
365
|
+
* needs the declared directions to pick each field's comparison operator.
|
|
348
366
|
*/
|
|
349
|
-
static buildOrderClause(sort, fieldFormatter = (field) => field) {
|
|
367
|
+
static buildOrderClause(sort, fieldFormatter = (field) => field, direction = 1) {
|
|
350
368
|
const fields = this.extractSortFields(sort);
|
|
351
369
|
const clauses = [];
|
|
352
370
|
const directions = [];
|
|
371
|
+
const sqlDir = (declared) => {
|
|
372
|
+
const effective = direction === -1 ? declared === 1 ? -1 : 1 : declared;
|
|
373
|
+
return effective === 1 ? "ASC" : "DESC";
|
|
374
|
+
};
|
|
353
375
|
if (sort && Object.keys(sort).length > 0) {
|
|
354
376
|
for (const [field, dir] of Object.entries(sort)) {
|
|
355
|
-
|
|
356
|
-
clauses.push(`${fieldFormatter(field)} ${sqlDirection}`);
|
|
377
|
+
clauses.push(`${fieldFormatter(field)} ${sqlDir(dir)}`);
|
|
357
378
|
directions.push(dir);
|
|
358
379
|
}
|
|
359
380
|
}
|
|
360
381
|
if (!sort || !sort.hasOwnProperty("id")) {
|
|
361
|
-
clauses.push(`${fieldFormatter("id")}
|
|
382
|
+
clauses.push(`${fieldFormatter("id")} ${sqlDir(1)}`);
|
|
362
383
|
directions.push(1);
|
|
363
384
|
} else if (sort && sort.hasOwnProperty("id")) {
|
|
364
385
|
directions.push(sort["id"]);
|
|
@@ -381,18 +402,27 @@ var init_CursorManager = __esm({
|
|
|
381
402
|
/**
|
|
382
403
|
* Generate next and previous cursors from result set
|
|
383
404
|
*/
|
|
384
|
-
static generateResultCursors(results, sortFields,
|
|
405
|
+
static generateResultCursors(results, sortFields, requestedDirection, hasMore, isFirstPage = false) {
|
|
385
406
|
if (results.length === 0) {
|
|
386
407
|
return {};
|
|
387
408
|
}
|
|
388
409
|
const cursors = {};
|
|
389
410
|
if (hasMore && results.length > 0) {
|
|
390
411
|
const lastResult = results[results.length - 1];
|
|
391
|
-
cursors.nextCursor = this.generateCursor(
|
|
412
|
+
cursors.nextCursor = this.generateCursor(
|
|
413
|
+
lastResult,
|
|
414
|
+
sortFields,
|
|
415
|
+
requestedDirection
|
|
416
|
+
);
|
|
392
417
|
}
|
|
393
418
|
if (results.length > 0 && !isFirstPage) {
|
|
394
419
|
const firstResult = results[0];
|
|
395
|
-
|
|
420
|
+
const opposite = requestedDirection === 1 ? -1 : 1;
|
|
421
|
+
cursors.prevCursor = this.generateCursor(
|
|
422
|
+
firstResult,
|
|
423
|
+
sortFields,
|
|
424
|
+
opposite
|
|
425
|
+
);
|
|
396
426
|
}
|
|
397
427
|
return cursors;
|
|
398
428
|
}
|
|
@@ -525,7 +555,8 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
525
555
|
const whereClause = this.translateFilter(filter);
|
|
526
556
|
const orderClause = CursorManager.buildOrderClause(
|
|
527
557
|
options?.sort,
|
|
528
|
-
(field) => this.getQuotedField(field)
|
|
558
|
+
(field) => this.getQuotedField(field),
|
|
559
|
+
options?.direction || 1
|
|
529
560
|
);
|
|
530
561
|
const limitClause = this.buildLimitClause(options);
|
|
531
562
|
const paginationClause = this.buildPaginationClause(
|
|
@@ -1197,8 +1228,71 @@ var init_yjsValues = __esm({
|
|
|
1197
1228
|
}
|
|
1198
1229
|
});
|
|
1199
1230
|
|
|
1200
|
-
// src/models/
|
|
1231
|
+
// src/models/writeLifecycle.ts
|
|
1201
1232
|
import * as Y2 from "yjs";
|
|
1233
|
+
import { ulid } from "ulid";
|
|
1234
|
+
function ensureStringSetYMap(rm, fieldName) {
|
|
1235
|
+
const nested = rm.get(fieldName);
|
|
1236
|
+
if (nested instanceof Y2.Map) return nested;
|
|
1237
|
+
const fresh = new Y2.Map();
|
|
1238
|
+
if (nested && typeof nested === "object") {
|
|
1239
|
+
for (const [member, marker] of Object.entries(
|
|
1240
|
+
nested
|
|
1241
|
+
)) {
|
|
1242
|
+
if (Boolean(marker)) fresh.set(member, true);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
rm.set(fieldName, fresh);
|
|
1246
|
+
return fresh;
|
|
1247
|
+
}
|
|
1248
|
+
function applyStringSetDelta(target, delta) {
|
|
1249
|
+
if (delta.clear) {
|
|
1250
|
+
for (const member of Array.from(target.keys())) target.delete(member);
|
|
1251
|
+
}
|
|
1252
|
+
if (delta.remove) {
|
|
1253
|
+
for (const member of delta.remove) target.delete(String(member));
|
|
1254
|
+
}
|
|
1255
|
+
if (delta.add) {
|
|
1256
|
+
for (const member of delta.add) {
|
|
1257
|
+
const key = String(member);
|
|
1258
|
+
if (target.get(key) !== true) target.set(key, true);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function applyStringSetChange(recordYMap, fieldName, change) {
|
|
1263
|
+
const target = ensureStringSetYMap(recordYMap, fieldName);
|
|
1264
|
+
applyStringSetDelta(target, {
|
|
1265
|
+
add: change.additions,
|
|
1266
|
+
remove: change.removals
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
function registerFunctionDefaultGenerator(name, fn) {
|
|
1270
|
+
FUNCTION_DEFAULT_GENERATORS.set(name, fn);
|
|
1271
|
+
}
|
|
1272
|
+
function resolveLiteralOrFunctionDefault(rawDefault) {
|
|
1273
|
+
return typeof rawDefault === "function" ? rawDefault() : rawDefault;
|
|
1274
|
+
}
|
|
1275
|
+
var KNOWN_FIELD_TYPE_MAP, KNOWN_FIELD_TYPES, FUNCTION_DEFAULT_GENERATORS, UNKNOWN_FUNCTION_DEFAULT;
|
|
1276
|
+
var init_writeLifecycle = __esm({
|
|
1277
|
+
"src/models/writeLifecycle.ts"() {
|
|
1278
|
+
"use strict";
|
|
1279
|
+
KNOWN_FIELD_TYPE_MAP = {
|
|
1280
|
+
string: true,
|
|
1281
|
+
number: true,
|
|
1282
|
+
boolean: true,
|
|
1283
|
+
date: true,
|
|
1284
|
+
id: true,
|
|
1285
|
+
stringset: true
|
|
1286
|
+
};
|
|
1287
|
+
KNOWN_FIELD_TYPES = new Set(Object.keys(KNOWN_FIELD_TYPE_MAP));
|
|
1288
|
+
FUNCTION_DEFAULT_GENERATORS = /* @__PURE__ */ new Map();
|
|
1289
|
+
registerFunctionDefaultGenerator("generate_ulid", () => ulid());
|
|
1290
|
+
UNKNOWN_FUNCTION_DEFAULT = "$unknown_function";
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
// src/models/metaSync.ts
|
|
1295
|
+
import * as Y3 from "yjs";
|
|
1202
1296
|
function inferFieldType(value) {
|
|
1203
1297
|
if (isStringSetYMap(value)) return "stringset";
|
|
1204
1298
|
switch (typeof value) {
|
|
@@ -1219,7 +1313,10 @@ function encodeDefault(value) {
|
|
|
1219
1313
|
if (value === void 0 || value === null) return void 0;
|
|
1220
1314
|
if (typeof value === "function") {
|
|
1221
1315
|
const name = KNOWN_FUNCTION_DEFAULTS.get(value);
|
|
1222
|
-
return name ? `$${name}` :
|
|
1316
|
+
return name ? `$${name}` : UNKNOWN_FUNCTION_DEFAULT;
|
|
1317
|
+
}
|
|
1318
|
+
if (typeof value === "string" && value.startsWith("$")) {
|
|
1319
|
+
return `$${value}`;
|
|
1223
1320
|
}
|
|
1224
1321
|
return value;
|
|
1225
1322
|
}
|
|
@@ -1245,7 +1342,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1245
1342
|
if (compoundConstraints.length > 0) {
|
|
1246
1343
|
let constraints = meta.get("_constraints");
|
|
1247
1344
|
if (!constraints) {
|
|
1248
|
-
constraints = new
|
|
1345
|
+
constraints = new Y3.Map();
|
|
1249
1346
|
meta.set("_constraints", constraints);
|
|
1250
1347
|
}
|
|
1251
1348
|
for (const constraint of compoundConstraints) {
|
|
@@ -1256,7 +1353,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1256
1353
|
if (relationships && Object.keys(relationships).length > 0) {
|
|
1257
1354
|
let rels = meta.get("_relationships");
|
|
1258
1355
|
if (!rels) {
|
|
1259
|
-
rels = new
|
|
1356
|
+
rels = new Y3.Map();
|
|
1260
1357
|
meta.set("_relationships", rels);
|
|
1261
1358
|
}
|
|
1262
1359
|
for (const [relName, relConfig] of Object.entries(relationships)) {
|
|
@@ -1268,7 +1365,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1268
1365
|
function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
1269
1366
|
let fieldMeta = metaMap.get(fieldName);
|
|
1270
1367
|
if (!fieldMeta) {
|
|
1271
|
-
fieldMeta = new
|
|
1368
|
+
fieldMeta = new Y3.Map();
|
|
1272
1369
|
metaMap.set(fieldName, fieldMeta);
|
|
1273
1370
|
}
|
|
1274
1371
|
setIfChanged(fieldMeta, "type", fieldOpts.type);
|
|
@@ -1285,7 +1382,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1285
1382
|
function syncConstraintMeta(constraintsMap, constraint) {
|
|
1286
1383
|
let cMeta = constraintsMap.get(constraint.name);
|
|
1287
1384
|
if (!cMeta) {
|
|
1288
|
-
cMeta = new
|
|
1385
|
+
cMeta = new Y3.Map();
|
|
1289
1386
|
constraintsMap.set(constraint.name, cMeta);
|
|
1290
1387
|
}
|
|
1291
1388
|
setIfChanged(cMeta, "type", "unique");
|
|
@@ -1295,7 +1392,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
|
|
|
1295
1392
|
function syncRelationshipMeta(relsMap, relName, relConfig) {
|
|
1296
1393
|
let relMeta = relsMap.get(relName);
|
|
1297
1394
|
if (!relMeta) {
|
|
1298
|
-
relMeta = new
|
|
1395
|
+
relMeta = new Y3.Map();
|
|
1299
1396
|
relsMap.set(relName, relMeta);
|
|
1300
1397
|
}
|
|
1301
1398
|
for (const [key, value] of Object.entries(relConfig)) {
|
|
@@ -1310,7 +1407,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
|
|
|
1310
1407
|
if (fieldName.startsWith("_")) continue;
|
|
1311
1408
|
let fieldMeta = meta.get(fieldName);
|
|
1312
1409
|
if (!fieldMeta) {
|
|
1313
|
-
fieldMeta = new
|
|
1410
|
+
fieldMeta = new Y3.Map();
|
|
1314
1411
|
meta.set(fieldName, fieldMeta);
|
|
1315
1412
|
}
|
|
1316
1413
|
if (!fieldMeta.has("type")) {
|
|
@@ -1331,12 +1428,51 @@ var init_metaSync = __esm({
|
|
|
1331
1428
|
"src/models/metaSync.ts"() {
|
|
1332
1429
|
"use strict";
|
|
1333
1430
|
init_yjsValues();
|
|
1431
|
+
init_writeLifecycle();
|
|
1334
1432
|
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
1335
1433
|
_syncedCache = /* @__PURE__ */ new WeakMap();
|
|
1336
1434
|
}
|
|
1337
1435
|
});
|
|
1338
1436
|
|
|
1437
|
+
// src/utils/uniqueIndex.ts
|
|
1438
|
+
import * as Y4 from "yjs";
|
|
1439
|
+
function buildUniqueKey(recordData, fields) {
|
|
1440
|
+
const keyParts = [];
|
|
1441
|
+
for (const field of fields) {
|
|
1442
|
+
const value = recordData instanceof Y4.Map ? recordData.get(field) : recordData[field];
|
|
1443
|
+
if (value === null || value === void 0) {
|
|
1444
|
+
return null;
|
|
1445
|
+
}
|
|
1446
|
+
keyParts.push(String(value));
|
|
1447
|
+
}
|
|
1448
|
+
return keyParts.join("|");
|
|
1449
|
+
}
|
|
1450
|
+
function uniqueIndexMapName(modelName, constraintName) {
|
|
1451
|
+
return `_uniqueIdx_${modelName}_${constraintName}`;
|
|
1452
|
+
}
|
|
1453
|
+
function fieldUniqueConstraintName(modelName, fieldName) {
|
|
1454
|
+
return `${modelName}_${fieldName}_unique`;
|
|
1455
|
+
}
|
|
1456
|
+
var init_uniqueIndex = __esm({
|
|
1457
|
+
"src/utils/uniqueIndex.ts"() {
|
|
1458
|
+
"use strict";
|
|
1459
|
+
}
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1339
1462
|
// src/models/relationshipManager.ts
|
|
1463
|
+
function warnIfOrderFieldNullable(modelClass, orderByField, relationshipDescription) {
|
|
1464
|
+
if (ALWAYS_PRESENT_ORDER_FIELDS.has(orderByField)) return;
|
|
1465
|
+
const schema = modelClass.getSchema?.();
|
|
1466
|
+
const fields = schema?.fields;
|
|
1467
|
+
if (!fields || typeof fields.get !== "function") return;
|
|
1468
|
+
const fieldOptions = fields.get(orderByField);
|
|
1469
|
+
if (!fieldOptions) return;
|
|
1470
|
+
if (fieldOptions.required !== true) {
|
|
1471
|
+
Logger.warn(
|
|
1472
|
+
`[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.)`
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1340
1476
|
function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
1341
1477
|
return async function() {
|
|
1342
1478
|
const relatedId = this[config.relatedIdField];
|
|
@@ -1345,6 +1481,16 @@ function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
|
1345
1481
|
};
|
|
1346
1482
|
}
|
|
1347
1483
|
function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbEngine) {
|
|
1484
|
+
const {
|
|
1485
|
+
relatedIdField,
|
|
1486
|
+
orderByField = "id",
|
|
1487
|
+
orderDirection = "ASC"
|
|
1488
|
+
} = config;
|
|
1489
|
+
warnIfOrderFieldNullable(
|
|
1490
|
+
targetModelClass,
|
|
1491
|
+
orderByField,
|
|
1492
|
+
`hasMany(${targetModelClass.name})`
|
|
1493
|
+
);
|
|
1348
1494
|
return async function(paginationArgs) {
|
|
1349
1495
|
const {
|
|
1350
1496
|
limit,
|
|
@@ -1352,71 +1498,39 @@ function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbE
|
|
|
1352
1498
|
beforeCursor,
|
|
1353
1499
|
direction = "forward"
|
|
1354
1500
|
} = paginationArgs || {};
|
|
1355
|
-
const {
|
|
1356
|
-
relatedIdField,
|
|
1357
|
-
orderByField = "id",
|
|
1358
|
-
orderDirection = "ASC"
|
|
1359
|
-
} = config;
|
|
1360
1501
|
const targetSchema = targetModelClass.getSchema();
|
|
1361
1502
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
1362
1503
|
Logger.error(
|
|
1363
1504
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasMany relationship has no valid schema name.`
|
|
1364
1505
|
);
|
|
1365
|
-
return { data: [],
|
|
1506
|
+
return { data: [], hasMore: false };
|
|
1366
1507
|
}
|
|
1367
|
-
const
|
|
1508
|
+
const forward = direction !== "backward";
|
|
1509
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
1368
1510
|
const queryOptions = {
|
|
1369
|
-
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 }
|
|
1511
|
+
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 },
|
|
1512
|
+
direction: forward ? 1 : -1
|
|
1370
1513
|
};
|
|
1371
|
-
if (limit !== void 0)
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
};
|
|
1378
|
-
} else if (direction === "backward") {
|
|
1379
|
-
if (beforeCursor) {
|
|
1380
|
-
filter[orderByField] = {
|
|
1381
|
-
[orderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
1382
|
-
};
|
|
1383
|
-
}
|
|
1384
|
-
queryOptions.sort = { [orderByField]: orderDirection === "ASC" ? -1 : 1 };
|
|
1385
|
-
}
|
|
1386
|
-
const result = await targetModelClass.query(filter, queryOptions);
|
|
1387
|
-
const results = result.data;
|
|
1388
|
-
let nextCursor = null;
|
|
1389
|
-
let prevCursor = null;
|
|
1390
|
-
const isFirstPage = direction === "forward" && !afterCursor;
|
|
1391
|
-
if (limit !== void 0) {
|
|
1392
|
-
if (direction === "forward") {
|
|
1393
|
-
if (results.length > limit) {
|
|
1394
|
-
nextCursor = results[limit - 1][orderByField];
|
|
1395
|
-
results.pop();
|
|
1396
|
-
}
|
|
1397
|
-
if (results.length > 0 && !isFirstPage) {
|
|
1398
|
-
prevCursor = results[0][orderByField];
|
|
1399
|
-
}
|
|
1400
|
-
} else {
|
|
1401
|
-
if (results.length > limit) {
|
|
1402
|
-
prevCursor = results[limit - 1][orderByField];
|
|
1403
|
-
results.pop();
|
|
1404
|
-
}
|
|
1405
|
-
results.reverse();
|
|
1406
|
-
if (results.length > 0 && beforeCursor) {
|
|
1407
|
-
nextCursor = results[results.length - 1][orderByField];
|
|
1408
|
-
}
|
|
1409
|
-
if (results.length > 0 && !beforeCursor) {
|
|
1410
|
-
if (results.length === limit) {
|
|
1411
|
-
prevCursor = results[0][orderByField];
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
return { data: results, nextCursor, prevCursor };
|
|
1514
|
+
if (limit !== void 0) queryOptions.limit = limit;
|
|
1515
|
+
if (cursor != null) queryOptions.uniqueStartKey = cursor;
|
|
1516
|
+
return targetModelClass.query(
|
|
1517
|
+
{ [relatedIdField]: this.id },
|
|
1518
|
+
queryOptions
|
|
1519
|
+
);
|
|
1417
1520
|
};
|
|
1418
1521
|
}
|
|
1419
1522
|
function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClass, joinModelClass, _dbEngine) {
|
|
1523
|
+
const {
|
|
1524
|
+
joinModelLocalField,
|
|
1525
|
+
joinModelRelatedField,
|
|
1526
|
+
joinModelOrderByField = "id",
|
|
1527
|
+
joinModelOrderDirection = "ASC"
|
|
1528
|
+
} = config;
|
|
1529
|
+
warnIfOrderFieldNullable(
|
|
1530
|
+
joinModelClass,
|
|
1531
|
+
joinModelOrderByField,
|
|
1532
|
+
`hasManyThrough(${targetModelClass.name} via ${joinModelClass.name})`
|
|
1533
|
+
);
|
|
1420
1534
|
return async function(paginationArgs) {
|
|
1421
1535
|
const {
|
|
1422
1536
|
limit,
|
|
@@ -1430,87 +1544,42 @@ function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClas
|
|
|
1430
1544
|
Logger.error(
|
|
1431
1545
|
`[RelationshipManager] Join model ${joinModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
1432
1546
|
);
|
|
1433
|
-
return { data: [],
|
|
1547
|
+
return { data: [], hasMore: false };
|
|
1434
1548
|
}
|
|
1435
1549
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
1436
1550
|
Logger.error(
|
|
1437
1551
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
1438
1552
|
);
|
|
1439
|
-
return { data: [],
|
|
1553
|
+
return { data: [], hasMore: false };
|
|
1440
1554
|
}
|
|
1441
|
-
const
|
|
1442
|
-
|
|
1443
|
-
joinModelRelatedField,
|
|
1444
|
-
joinModelOrderByField = "id",
|
|
1445
|
-
joinModelOrderDirection = "ASC"
|
|
1446
|
-
} = config;
|
|
1447
|
-
const joinFilter = { [joinModelLocalField]: this.id };
|
|
1555
|
+
const forward = direction !== "backward";
|
|
1556
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
1448
1557
|
const joinQueryOptions = {
|
|
1449
1558
|
sort: {
|
|
1450
1559
|
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? 1 : -1
|
|
1451
1560
|
},
|
|
1452
|
-
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 }
|
|
1561
|
+
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 },
|
|
1562
|
+
direction: forward ? 1 : -1
|
|
1453
1563
|
};
|
|
1454
|
-
if (limit !== void 0)
|
|
1455
|
-
|
|
1456
|
-
}
|
|
1457
|
-
if (direction === "forward" && afterCursor) {
|
|
1458
|
-
joinFilter[joinModelOrderByField] = {
|
|
1459
|
-
[joinModelOrderDirection === "ASC" ? "$gt" : "$lt"]: afterCursor
|
|
1460
|
-
};
|
|
1461
|
-
} else if (direction === "backward") {
|
|
1462
|
-
if (beforeCursor) {
|
|
1463
|
-
joinFilter[joinModelOrderByField] = {
|
|
1464
|
-
[joinModelOrderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
1465
|
-
};
|
|
1466
|
-
}
|
|
1467
|
-
joinQueryOptions.sort = {
|
|
1468
|
-
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? -1 : 1
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1564
|
+
if (limit !== void 0) joinQueryOptions.limit = limit;
|
|
1565
|
+
if (cursor != null) joinQueryOptions.uniqueStartKey = cursor;
|
|
1471
1566
|
const joinResult = await joinModelClass.query(
|
|
1472
|
-
|
|
1567
|
+
{ [joinModelLocalField]: this.id },
|
|
1473
1568
|
joinQueryOptions
|
|
1474
1569
|
);
|
|
1475
|
-
const
|
|
1476
|
-
let
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
nextCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
1483
|
-
joinResults.pop();
|
|
1484
|
-
}
|
|
1485
|
-
if (joinResults.length > 0 && !isFirstPage) {
|
|
1486
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
1487
|
-
}
|
|
1488
|
-
} else {
|
|
1489
|
-
if (joinResults.length > limit) {
|
|
1490
|
-
prevCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
1491
|
-
joinResults.pop();
|
|
1492
|
-
}
|
|
1493
|
-
joinResults.reverse();
|
|
1494
|
-
if (joinResults.length > 0 && beforeCursor) {
|
|
1495
|
-
nextCursorFromJoin = joinResults[joinResults.length - 1][joinModelOrderByField];
|
|
1496
|
-
}
|
|
1497
|
-
if (joinResults.length > 0 && !beforeCursor) {
|
|
1498
|
-
if (joinResults.length === limit) {
|
|
1499
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1570
|
+
const relatedIds = joinResult.data.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
1571
|
+
let targetData = [];
|
|
1572
|
+
if (relatedIds.length > 0) {
|
|
1573
|
+
const targetResult = await targetModelClass.query({
|
|
1574
|
+
id: { $in: relatedIds }
|
|
1575
|
+
});
|
|
1576
|
+
targetData = targetResult.data;
|
|
1503
1577
|
}
|
|
1504
|
-
const relatedIds = joinResults.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
1505
|
-
if (relatedIds.length === 0)
|
|
1506
|
-
return { data: [], nextCursor: null, prevCursor: null };
|
|
1507
|
-
const targetResult = await targetModelClass.query({
|
|
1508
|
-
id: { $in: relatedIds }
|
|
1509
|
-
});
|
|
1510
1578
|
return {
|
|
1511
|
-
data:
|
|
1512
|
-
nextCursor:
|
|
1513
|
-
prevCursor:
|
|
1579
|
+
data: targetData,
|
|
1580
|
+
nextCursor: joinResult.nextCursor,
|
|
1581
|
+
prevCursor: joinResult.prevCursor,
|
|
1582
|
+
hasMore: joinResult.hasMore
|
|
1514
1583
|
};
|
|
1515
1584
|
};
|
|
1516
1585
|
}
|
|
@@ -1634,10 +1703,12 @@ function generateHasManyThroughRemoveMethod(_sourceModelClass, config, _targetMo
|
|
|
1634
1703
|
}, `remove-join-${config.joinModel}-${this.id}-${targetId}`);
|
|
1635
1704
|
};
|
|
1636
1705
|
}
|
|
1706
|
+
var ALWAYS_PRESENT_ORDER_FIELDS;
|
|
1637
1707
|
var init_relationshipManager = __esm({
|
|
1638
1708
|
"src/models/relationshipManager.ts"() {
|
|
1639
1709
|
"use strict";
|
|
1640
1710
|
init_BaseModel();
|
|
1711
|
+
ALWAYS_PRESENT_ORDER_FIELDS = /* @__PURE__ */ new Set(["id", "type"]);
|
|
1641
1712
|
}
|
|
1642
1713
|
});
|
|
1643
1714
|
|
|
@@ -2408,7 +2479,13 @@ var init_IncludeResolver = __esm({
|
|
|
2408
2479
|
}
|
|
2409
2480
|
return null;
|
|
2410
2481
|
}
|
|
2411
|
-
/**
|
|
2482
|
+
/**
|
|
2483
|
+
* Apply a projection to a single record. Assumes the projection was already
|
|
2484
|
+
* validated upstream (mixed inclusion+exclusion rejected by the translator):
|
|
2485
|
+
* `isInclusion` treats "any value === 1" as inclusion mode. `id` and `type`
|
|
2486
|
+
* are system-identity fields and are always kept in BOTH modes (mirrors the
|
|
2487
|
+
* DatabaseDO `_applyProjection`); `_meta` stays excludable.
|
|
2488
|
+
*/
|
|
2412
2489
|
_applyProjection(record, projection) {
|
|
2413
2490
|
const fields = Object.entries(projection);
|
|
2414
2491
|
if (fields.length === 0) return record;
|
|
@@ -2427,7 +2504,7 @@ var init_IncludeResolver = __esm({
|
|
|
2427
2504
|
} else {
|
|
2428
2505
|
const result = { ...record };
|
|
2429
2506
|
for (const [f, v] of fields) {
|
|
2430
|
-
if (v === 0) delete result[f];
|
|
2507
|
+
if (v === 0 && f !== "id" && f !== "type") delete result[f];
|
|
2431
2508
|
}
|
|
2432
2509
|
return result;
|
|
2433
2510
|
}
|
|
@@ -2446,10 +2523,10 @@ __export(BaseModel_exports, {
|
|
|
2446
2523
|
UniqueConstraintViolationError: () => UniqueConstraintViolationError,
|
|
2447
2524
|
generateULID: () => generateULID
|
|
2448
2525
|
});
|
|
2449
|
-
import * as
|
|
2450
|
-
import { ulid } from "ulid";
|
|
2526
|
+
import * as Y5 from "yjs";
|
|
2527
|
+
import { ulid as ulid2 } from "ulid";
|
|
2451
2528
|
function generateULID() {
|
|
2452
|
-
return
|
|
2529
|
+
return ulid2();
|
|
2453
2530
|
}
|
|
2454
2531
|
var LogLevel, Logger, UniqueConstraintViolationError, RecordNotFoundError, SCHEMA_ACCESSORS_KEY, BaseModel2;
|
|
2455
2532
|
var init_BaseModel = __esm({
|
|
@@ -2461,7 +2538,9 @@ var init_BaseModel = __esm({
|
|
|
2461
2538
|
init_CursorManager();
|
|
2462
2539
|
init_sql();
|
|
2463
2540
|
init_metaSync();
|
|
2541
|
+
init_writeLifecycle();
|
|
2464
2542
|
init_yjsValues();
|
|
2543
|
+
init_uniqueIndex();
|
|
2465
2544
|
LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
2466
2545
|
LogLevel2[LogLevel2["SILENT"] = 0] = "SILENT";
|
|
2467
2546
|
LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
|
|
@@ -2846,7 +2925,9 @@ var init_BaseModel = __esm({
|
|
|
2846
2925
|
this.setValue(fieldKey, data[fieldKey]);
|
|
2847
2926
|
this._constructorProvidedFields.add(fieldKey);
|
|
2848
2927
|
} else if (fieldOptions.default !== void 0) {
|
|
2849
|
-
const defaultValue =
|
|
2928
|
+
const defaultValue = resolveLiteralOrFunctionDefault(
|
|
2929
|
+
fieldOptions.default
|
|
2930
|
+
);
|
|
2850
2931
|
this.setValue(fieldKey, defaultValue);
|
|
2851
2932
|
}
|
|
2852
2933
|
}
|
|
@@ -3138,7 +3219,7 @@ var init_BaseModel = __esm({
|
|
|
3138
3219
|
}
|
|
3139
3220
|
getStringSetFromYjs(fieldName) {
|
|
3140
3221
|
const yjsData = this.getFromYjs(fieldName);
|
|
3141
|
-
if (yjsData instanceof
|
|
3222
|
+
if (yjsData instanceof Y5.Map) {
|
|
3142
3223
|
return Array.from(yjsData.keys());
|
|
3143
3224
|
}
|
|
3144
3225
|
if (yjsData && typeof yjsData === "object") {
|
|
@@ -3246,7 +3327,7 @@ var init_BaseModel = __esm({
|
|
|
3246
3327
|
}
|
|
3247
3328
|
const extractStringSetValues = (value) => {
|
|
3248
3329
|
if (!value) return [];
|
|
3249
|
-
if (value instanceof
|
|
3330
|
+
if (value instanceof Y5.Map) {
|
|
3250
3331
|
return Array.from(value.entries()).filter(([, isMember]) => Boolean(isMember)).map(([key]) => key);
|
|
3251
3332
|
}
|
|
3252
3333
|
if (Array.isArray(value)) {
|
|
@@ -3263,7 +3344,7 @@ var init_BaseModel = __esm({
|
|
|
3263
3344
|
if (!recordId) continue;
|
|
3264
3345
|
let itemData;
|
|
3265
3346
|
const stringSetValuesByField = {};
|
|
3266
|
-
if (recordData instanceof
|
|
3347
|
+
if (recordData instanceof Y5.Map) {
|
|
3267
3348
|
itemData = {};
|
|
3268
3349
|
const unknownFields = [];
|
|
3269
3350
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -3379,20 +3460,10 @@ var init_BaseModel = __esm({
|
|
|
3379
3460
|
Logger.verbose(
|
|
3380
3461
|
`[${this.name}] Setting up YMap observer for document ${docId}/${modelName}...`
|
|
3381
3462
|
);
|
|
3382
|
-
const
|
|
3383
|
-
const keyParts = [];
|
|
3384
|
-
for (const field of fields) {
|
|
3385
|
-
const value = recordData instanceof Y3.Map ? recordData.get(field) : recordData[field];
|
|
3386
|
-
if (value === null || value === void 0) {
|
|
3387
|
-
return null;
|
|
3388
|
-
}
|
|
3389
|
-
keyParts.push(String(value));
|
|
3390
|
-
}
|
|
3391
|
-
return keyParts.join("|");
|
|
3392
|
-
};
|
|
3463
|
+
const buildUniqueKey2 = (recordData, fields) => buildUniqueKey(recordData, fields);
|
|
3393
3464
|
const extractItemData = (key, recordData) => {
|
|
3394
3465
|
let itemData;
|
|
3395
|
-
if (recordData instanceof
|
|
3466
|
+
if (recordData instanceof Y5.Map) {
|
|
3396
3467
|
itemData = {};
|
|
3397
3468
|
const unknownFields = [];
|
|
3398
3469
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -3476,7 +3547,7 @@ var init_BaseModel = __esm({
|
|
|
3476
3547
|
const recordsByUniqueKey = /* @__PURE__ */ new Map();
|
|
3477
3548
|
for (const [recordId, recordData] of allRecords.entries()) {
|
|
3478
3549
|
if (recordIdsToDiscard.has(recordId)) continue;
|
|
3479
|
-
const uniqueKey =
|
|
3550
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
3480
3551
|
if (uniqueKey === null) continue;
|
|
3481
3552
|
if (!recordsByUniqueKey.has(uniqueKey)) {
|
|
3482
3553
|
recordsByUniqueKey.set(uniqueKey, []);
|
|
@@ -3522,7 +3593,7 @@ var init_BaseModel = __esm({
|
|
|
3522
3593
|
if (!recordData || !key) continue;
|
|
3523
3594
|
const itemData = extractItemData(key, recordData);
|
|
3524
3595
|
if (!itemData) continue;
|
|
3525
|
-
if (change.action === "add" && recordData instanceof
|
|
3596
|
+
if (change.action === "add" && recordData instanceof Y5.Map) {
|
|
3526
3597
|
Logger.verbose(
|
|
3527
3598
|
`[${this.name}] Setting up observer on newly added nested YMap for record ${key} in document ${docId}`
|
|
3528
3599
|
);
|
|
@@ -3620,9 +3691,12 @@ var init_BaseModel = __esm({
|
|
|
3620
3691
|
const recordData = documentYMap.get(idToDiscard);
|
|
3621
3692
|
if (!recordData) continue;
|
|
3622
3693
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
3623
|
-
const uniqueKey =
|
|
3694
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
3624
3695
|
if (uniqueKey === null) continue;
|
|
3625
|
-
const constraintMapName =
|
|
3696
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3697
|
+
modelName,
|
|
3698
|
+
constraint.name
|
|
3699
|
+
);
|
|
3626
3700
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
3627
3701
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
3628
3702
|
if (currentIndexValue === idToDiscard) {
|
|
@@ -3640,11 +3714,14 @@ var init_BaseModel = __esm({
|
|
|
3640
3714
|
}, `conflict-resolution-${modelName}-${docId}`);
|
|
3641
3715
|
yDoc.transact(() => {
|
|
3642
3716
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
3643
|
-
const constraintMapName =
|
|
3717
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3718
|
+
modelName,
|
|
3719
|
+
constraint.name
|
|
3720
|
+
);
|
|
3644
3721
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
3645
3722
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
3646
3723
|
if (!recordData) continue;
|
|
3647
|
-
const uniqueKey =
|
|
3724
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
3648
3725
|
if (uniqueKey === null) continue;
|
|
3649
3726
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
3650
3727
|
if (currentIndexValue !== recordId) {
|
|
@@ -3667,7 +3744,7 @@ var init_BaseModel = __esm({
|
|
|
3667
3744
|
`[${this.name}] Setting up observers on existing nested YMaps for ${modelName}/${docId}...`
|
|
3668
3745
|
);
|
|
3669
3746
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
3670
|
-
if (recordData instanceof
|
|
3747
|
+
if (recordData instanceof Y5.Map) {
|
|
3671
3748
|
Logger.verbose(
|
|
3672
3749
|
`[${this.name}] Setting up observer on existing nested YMap for record ${recordId} in document ${docId}`
|
|
3673
3750
|
);
|
|
@@ -3893,26 +3970,7 @@ var init_BaseModel = __esm({
|
|
|
3893
3970
|
* if the field hasn't been touched since the wire-format change in #561.
|
|
3894
3971
|
*/
|
|
3895
3972
|
applyStringSetChangeToYMap(recordYMap, fieldName, change) {
|
|
3896
|
-
|
|
3897
|
-
if (!(nested instanceof Y3.Map)) {
|
|
3898
|
-
const migrated = new Y3.Map();
|
|
3899
|
-
if (nested && typeof nested === "object") {
|
|
3900
|
-
for (const [member, marker] of Object.entries(
|
|
3901
|
-
nested
|
|
3902
|
-
)) {
|
|
3903
|
-
if (Boolean(marker)) migrated.set(member, true);
|
|
3904
|
-
}
|
|
3905
|
-
}
|
|
3906
|
-
recordYMap.set(fieldName, migrated);
|
|
3907
|
-
nested = migrated;
|
|
3908
|
-
}
|
|
3909
|
-
const target = nested;
|
|
3910
|
-
for (const member of change.additions) {
|
|
3911
|
-
target.set(member, true);
|
|
3912
|
-
}
|
|
3913
|
-
for (const member of change.removals) {
|
|
3914
|
-
target.delete(member);
|
|
3915
|
-
}
|
|
3973
|
+
applyStringSetChange(recordYMap, fieldName, change);
|
|
3916
3974
|
}
|
|
3917
3975
|
/**
|
|
3918
3976
|
* Deep equality check for comparing field values
|
|
@@ -4114,6 +4172,34 @@ var init_BaseModel = __esm({
|
|
|
4114
4172
|
`[${modelName}.save] getCurrentJSState() returned:`,
|
|
4115
4173
|
dataToSave
|
|
4116
4174
|
);
|
|
4175
|
+
const uniqueMissConflicts = /* @__PURE__ */ new Map();
|
|
4176
|
+
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
4177
|
+
const newUniqueKey = this._buildUniqueKey(
|
|
4178
|
+
constraint.fields,
|
|
4179
|
+
dataToSave,
|
|
4180
|
+
modelName,
|
|
4181
|
+
constraint.name
|
|
4182
|
+
);
|
|
4183
|
+
if (newUniqueKey === null) {
|
|
4184
|
+
continue;
|
|
4185
|
+
}
|
|
4186
|
+
const constraintMap = yDoc.getMap(
|
|
4187
|
+
uniqueIndexMapName(modelName, constraint.name)
|
|
4188
|
+
);
|
|
4189
|
+
if (!constraintMap.get(newUniqueKey)) {
|
|
4190
|
+
const constraintKeyValues = constraint.fields.map(
|
|
4191
|
+
(field) => dataToSave[field]
|
|
4192
|
+
);
|
|
4193
|
+
const conflictingRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4194
|
+
constraint,
|
|
4195
|
+
constraintKeyValues,
|
|
4196
|
+
{ excludeId: this.id }
|
|
4197
|
+
);
|
|
4198
|
+
if (conflictingRecord) {
|
|
4199
|
+
uniqueMissConflicts.set(constraint.name, conflictingRecord.id);
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4117
4203
|
await yDoc.transact(async () => {
|
|
4118
4204
|
if (upsertOnConstraint && upsertOnKey) {
|
|
4119
4205
|
const constraintMapName = `_uniqueIdx_${modelName}_${upsertOnConstraint.name}`;
|
|
@@ -4155,7 +4241,7 @@ var init_BaseModel = __esm({
|
|
|
4155
4241
|
Logger.debug(`[${modelName}.save] isUpdate: ${isUpdate}`);
|
|
4156
4242
|
Logger.debug(`[${modelName}.save] recordYMap exists: ${!!recordYMap}`);
|
|
4157
4243
|
if (!recordYMap) {
|
|
4158
|
-
recordYMap = new
|
|
4244
|
+
recordYMap = new Y5.Map();
|
|
4159
4245
|
Logger.verbose(
|
|
4160
4246
|
`[${modelName}] Creating new nested YMap for record ${this.id} in document ${targetDocId || "legacy"}`
|
|
4161
4247
|
);
|
|
@@ -4207,7 +4293,10 @@ var init_BaseModel = __esm({
|
|
|
4207
4293
|
);
|
|
4208
4294
|
continue;
|
|
4209
4295
|
}
|
|
4210
|
-
const constraintMapName =
|
|
4296
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4297
|
+
modelName,
|
|
4298
|
+
constraint.name
|
|
4299
|
+
);
|
|
4211
4300
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
4212
4301
|
Logger.verbose(
|
|
4213
4302
|
`[${modelName}] Save Check (Item '${this.id}'): GET from '${constraintMapName}' with Key '${newUniqueKey.substring(
|
|
@@ -4236,6 +4325,26 @@ var init_BaseModel = __esm({
|
|
|
4236
4325
|
this.id,
|
|
4237
4326
|
existingRecordIdWithNewKey
|
|
4238
4327
|
);
|
|
4328
|
+
} else if (!existingRecordIdWithNewKey) {
|
|
4329
|
+
const conflictingRecordId = uniqueMissConflicts.get(constraint.name);
|
|
4330
|
+
if (conflictingRecordId && conflictingRecordId !== this.id) {
|
|
4331
|
+
Logger.verbose(
|
|
4332
|
+
`[${modelName}] Save Check (Item '${this.id}'): index miss for '${constraint.name}', but engine fallback found conflicting index-less record ${conflictingRecordId}.`
|
|
4333
|
+
);
|
|
4334
|
+
throw new UniqueConstraintViolationError(
|
|
4335
|
+
`Unique constraint '${constraint.name}' violated for model '${modelName}' on fields [${constraint.fields.join(
|
|
4336
|
+
", "
|
|
4337
|
+
)}]. Attempted ID: ${this.id}. Value(s) starting with '${newUniqueKey.substring(
|
|
4338
|
+
0,
|
|
4339
|
+
50
|
|
4340
|
+
)}...' already exist for index-less record ID ${conflictingRecordId}.`,
|
|
4341
|
+
modelName,
|
|
4342
|
+
constraint.name,
|
|
4343
|
+
constraint.fields,
|
|
4344
|
+
this.id,
|
|
4345
|
+
conflictingRecordId
|
|
4346
|
+
);
|
|
4347
|
+
}
|
|
4239
4348
|
}
|
|
4240
4349
|
}
|
|
4241
4350
|
if (isUpdate && oldData) {
|
|
@@ -4579,34 +4688,46 @@ var init_BaseModel = __esm({
|
|
|
4579
4688
|
const modelName = schema.options.name;
|
|
4580
4689
|
Logger.verbose(`[${modelName}] Executing query:`, { filter, options });
|
|
4581
4690
|
const hasIncludes = options?.include && options.include.length > 0;
|
|
4582
|
-
const
|
|
4691
|
+
const requestedLimit = options?.limit;
|
|
4692
|
+
const overFetch = typeof requestedLimit === "number" && requestedLimit > 0;
|
|
4693
|
+
let translatorOptions = hasIncludes && options?.projection ? { ...options, projection: void 0 } : options;
|
|
4694
|
+
if (overFetch) {
|
|
4695
|
+
translatorOptions = {
|
|
4696
|
+
...translatorOptions || {},
|
|
4697
|
+
limit: requestedLimit + 1
|
|
4698
|
+
};
|
|
4699
|
+
}
|
|
4583
4700
|
const translator = new DocumentQueryTranslator(modelName, schema.fields);
|
|
4584
4701
|
const translatedQuery = translator.translateFind(filter, translatorOptions);
|
|
4585
4702
|
Logger.verbose(`[${modelName}] Translated SQL:`, translatedQuery.sql);
|
|
4586
4703
|
Logger.verbose(`[${modelName}] SQL params:`, translatedQuery.params);
|
|
4587
|
-
|
|
4704
|
+
let results = await _BaseModel.dbInstance.query(
|
|
4588
4705
|
translatedQuery.sql,
|
|
4589
4706
|
translatedQuery.params
|
|
4590
4707
|
);
|
|
4591
4708
|
Logger.verbose(`[${modelName}] Query returned ${results.length} results`);
|
|
4709
|
+
let hasMore = false;
|
|
4710
|
+
if (overFetch && results.length > requestedLimit) {
|
|
4711
|
+
hasMore = true;
|
|
4712
|
+
results = results.slice(0, requestedLimit);
|
|
4713
|
+
}
|
|
4592
4714
|
if (options?.include && options.include.length > 0) {
|
|
4593
4715
|
const { IncludeResolver: IncludeResolver2 } = await Promise.resolve().then(() => (init_IncludeResolver(), IncludeResolver_exports));
|
|
4594
4716
|
const resolver = new IncludeResolver2(_BaseModel.dbInstance);
|
|
4595
4717
|
await resolver.resolve(results, options.include, 0, modelName);
|
|
4596
4718
|
}
|
|
4597
|
-
const requestedLimit = options?.limit;
|
|
4598
|
-
const hasMore = CursorManager.hasMoreResults(
|
|
4599
|
-
requestedLimit,
|
|
4600
|
-
results.length
|
|
4601
|
-
);
|
|
4602
4719
|
const isFirstPage = !options?.uniqueStartKey;
|
|
4720
|
+
const direction = options?.direction || 1;
|
|
4603
4721
|
const cursors = CursorManager.generateResultCursors(
|
|
4604
4722
|
results,
|
|
4605
4723
|
translatedQuery.sortFields,
|
|
4606
|
-
|
|
4724
|
+
direction,
|
|
4607
4725
|
hasMore,
|
|
4608
4726
|
isFirstPage
|
|
4609
4727
|
);
|
|
4728
|
+
if (direction === -1) {
|
|
4729
|
+
results.reverse();
|
|
4730
|
+
}
|
|
4610
4731
|
let transformedData;
|
|
4611
4732
|
if (options?.projection && Object.keys(options.projection).length > 0) {
|
|
4612
4733
|
transformedData = results.map((row) => {
|
|
@@ -5182,7 +5303,7 @@ var init_BaseModel = __esm({
|
|
|
5182
5303
|
);
|
|
5183
5304
|
}
|
|
5184
5305
|
for (const [recordId, recordYMap] of documentYMap.entries()) {
|
|
5185
|
-
if (recordYMap instanceof
|
|
5306
|
+
if (recordYMap instanceof Y5.Map) {
|
|
5186
5307
|
const instance = new this({ id: recordId });
|
|
5187
5308
|
instance._metaDocId = docId;
|
|
5188
5309
|
const connectedDoc = modelConstructor.connectedDocuments.get(docId);
|
|
@@ -5207,6 +5328,50 @@ var init_BaseModel = __esm({
|
|
|
5207
5328
|
}
|
|
5208
5329
|
return allInstances;
|
|
5209
5330
|
}
|
|
5331
|
+
/**
|
|
5332
|
+
* Resolve a record by its unique-constraint field values via an engine query.
|
|
5333
|
+
* Used as a fallback when the `_uniqueIdx_<model>_<constraint>` map has no
|
|
5334
|
+
* entry for a key.
|
|
5335
|
+
*
|
|
5336
|
+
* The index map is a cache over the SQLite projection, not the source of
|
|
5337
|
+
* truth: every record is loaded into the projection on `initializeForDocument`
|
|
5338
|
+
* regardless of index presence, but a record written by a client predating
|
|
5339
|
+
* index maintenance (legacy data) is missing from the index. Such a record
|
|
5340
|
+
* silently escapes both `upsertByUnique` lookups and the `save()` uniqueness
|
|
5341
|
+
* check. A query over the constraint fields finds it reliably.
|
|
5342
|
+
*
|
|
5343
|
+
* Contract: RESOLVE ONLY. Returns the owning record (with `_metaDocId` set to
|
|
5344
|
+
* the document that holds it, via `queryOne`) or `null`. Callers decide
|
|
5345
|
+
* whether to heal the missing index entry (write paths) and do their own
|
|
5346
|
+
* logging — this method never mutates the index, so read paths stay
|
|
5347
|
+
* read-only. Mirrors `buildUniqueKey`: a null/undefined value in any
|
|
5348
|
+
* constraint field means the record does not participate in the constraint,
|
|
5349
|
+
* so no query runs.
|
|
5350
|
+
*/
|
|
5351
|
+
static async _resolveByUniqueFallback(constraint, keyValues, options) {
|
|
5352
|
+
if (!_BaseModel.dbInstance) {
|
|
5353
|
+
return null;
|
|
5354
|
+
}
|
|
5355
|
+
const filter = {};
|
|
5356
|
+
for (let i = 0; i < constraint.fields.length; i++) {
|
|
5357
|
+
const value = keyValues[i];
|
|
5358
|
+
if (value === null || value === void 0) {
|
|
5359
|
+
return null;
|
|
5360
|
+
}
|
|
5361
|
+
filter[constraint.fields[i]] = value;
|
|
5362
|
+
}
|
|
5363
|
+
if (options?.excludeId) {
|
|
5364
|
+
if ("id" in filter) {
|
|
5365
|
+
if (filter.id === options.excludeId) {
|
|
5366
|
+
return null;
|
|
5367
|
+
}
|
|
5368
|
+
} else {
|
|
5369
|
+
filter.id = { $ne: options.excludeId };
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
const found = await this.queryOne(filter);
|
|
5373
|
+
return found ?? null;
|
|
5374
|
+
}
|
|
5210
5375
|
static async findByUnique(constraintName, value) {
|
|
5211
5376
|
const modelConstructor = this;
|
|
5212
5377
|
const schema = modelConstructor.getSchema();
|
|
@@ -5245,7 +5410,10 @@ var init_BaseModel = __esm({
|
|
|
5245
5410
|
const documentInfo = modelConstructor.connectedDocuments.get(docId);
|
|
5246
5411
|
if (!documentInfo) continue;
|
|
5247
5412
|
const yDoc = documentInfo.yDoc;
|
|
5248
|
-
const constraintMapName2 =
|
|
5413
|
+
const constraintMapName2 = uniqueIndexMapName(
|
|
5414
|
+
currentModelName,
|
|
5415
|
+
constraint.name
|
|
5416
|
+
);
|
|
5249
5417
|
const constraintMap2 = yDoc.getMap(constraintMapName2);
|
|
5250
5418
|
const recordId2 = constraintMap2.get(uniqueKeyString);
|
|
5251
5419
|
if (recordId2) {
|
|
@@ -5253,7 +5421,7 @@ var init_BaseModel = __esm({
|
|
|
5253
5421
|
const documentYMap = modelConstructor.documentYMaps.get(documentYMapKey);
|
|
5254
5422
|
if (documentYMap) {
|
|
5255
5423
|
const recordYMap2 = documentYMap.get(recordId2);
|
|
5256
|
-
if (recordYMap2 && recordYMap2 instanceof
|
|
5424
|
+
if (recordYMap2 && recordYMap2 instanceof Y5.Map) {
|
|
5257
5425
|
const instance = new modelConstructor({
|
|
5258
5426
|
id: recordId2
|
|
5259
5427
|
});
|
|
@@ -5267,6 +5435,16 @@ var init_BaseModel = __esm({
|
|
|
5267
5435
|
}
|
|
5268
5436
|
}
|
|
5269
5437
|
}
|
|
5438
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
5439
|
+
constraint,
|
|
5440
|
+
keyValues
|
|
5441
|
+
);
|
|
5442
|
+
if (fallbackRecord) {
|
|
5443
|
+
Logger.verbose(
|
|
5444
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
5445
|
+
);
|
|
5446
|
+
return fallbackRecord;
|
|
5447
|
+
}
|
|
5270
5448
|
Logger.verbose(
|
|
5271
5449
|
`[${currentModelName}.findByUnique] Record not found in any connected document`
|
|
5272
5450
|
);
|
|
@@ -5280,17 +5458,30 @@ var init_BaseModel = __esm({
|
|
|
5280
5458
|
`[${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.`
|
|
5281
5459
|
);
|
|
5282
5460
|
}
|
|
5283
|
-
const constraintMapName =
|
|
5461
|
+
const constraintMapName = uniqueIndexMapName(
|
|
5462
|
+
currentModelName,
|
|
5463
|
+
constraint.name
|
|
5464
|
+
);
|
|
5284
5465
|
const constraintMap = legacyDocInfo.yDoc.getMap(constraintMapName);
|
|
5285
5466
|
const recordId = constraintMap.get(uniqueKeyString);
|
|
5286
5467
|
if (!recordId) {
|
|
5468
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
5469
|
+
constraint,
|
|
5470
|
+
keyValues
|
|
5471
|
+
);
|
|
5472
|
+
if (fallbackRecord) {
|
|
5473
|
+
Logger.verbose(
|
|
5474
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
5475
|
+
);
|
|
5476
|
+
return fallbackRecord;
|
|
5477
|
+
}
|
|
5287
5478
|
return null;
|
|
5288
5479
|
}
|
|
5289
5480
|
const recordYMap = legacyDocumentMap.get(recordId);
|
|
5290
5481
|
if (!recordYMap) {
|
|
5291
5482
|
return null;
|
|
5292
5483
|
}
|
|
5293
|
-
if (recordYMap instanceof
|
|
5484
|
+
if (recordYMap instanceof Y5.Map) {
|
|
5294
5485
|
const instance = new modelConstructor({
|
|
5295
5486
|
id: recordId
|
|
5296
5487
|
});
|
|
@@ -5348,7 +5539,10 @@ var init_BaseModel = __esm({
|
|
|
5348
5539
|
`[${currentModelName}] upsertByUnique for '${constraintName}': unique key cannot be built due to null/undefined values. Upsert cannot proceed reliably.`
|
|
5349
5540
|
);
|
|
5350
5541
|
}
|
|
5351
|
-
const constraintMapName =
|
|
5542
|
+
const constraintMapName = uniqueIndexMapName(
|
|
5543
|
+
currentModelName,
|
|
5544
|
+
constraint.name
|
|
5545
|
+
);
|
|
5352
5546
|
let existingRecord = null;
|
|
5353
5547
|
if (connectedDocuments && connectedDocuments.size > 0) {
|
|
5354
5548
|
for (const docId of connectedDocuments.keys()) {
|
|
@@ -5366,6 +5560,33 @@ var init_BaseModel = __esm({
|
|
|
5366
5560
|
}
|
|
5367
5561
|
} else {
|
|
5368
5562
|
}
|
|
5563
|
+
if (!existingRecord) {
|
|
5564
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
5565
|
+
constraint,
|
|
5566
|
+
keyValuesForLookup
|
|
5567
|
+
);
|
|
5568
|
+
if (fallbackRecord) {
|
|
5569
|
+
existingRecord = fallbackRecord;
|
|
5570
|
+
const healDocId = fallbackRecord._metaDocId;
|
|
5571
|
+
const healDocInfo = healDocId ? connectedDocuments.get(healDocId) : void 0;
|
|
5572
|
+
const canHeal = !!healDocInfo && healDocInfo.permissionHint !== "read";
|
|
5573
|
+
if (healDocInfo && canHeal) {
|
|
5574
|
+
healDocInfo.yDoc.transact(() => {
|
|
5575
|
+
const healMap = healDocInfo.yDoc.getMap(constraintMapName);
|
|
5576
|
+
if (healMap.get(uniqueKeyString) !== fallbackRecord.id) {
|
|
5577
|
+
healMap.set(uniqueKeyString, fallbackRecord.id);
|
|
5578
|
+
}
|
|
5579
|
+
}, `heal-unique-index-${currentModelName}-${healDocId}`);
|
|
5580
|
+
Logger.verbose(
|
|
5581
|
+
`[${currentModelName}] upsertByUnique: index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id}; healed missing index entry (legacy index-less record).`
|
|
5582
|
+
);
|
|
5583
|
+
} else {
|
|
5584
|
+
Logger.verbose(
|
|
5585
|
+
`[${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).`
|
|
5586
|
+
);
|
|
5587
|
+
}
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5369
5590
|
if (options?.objectMustExist) {
|
|
5370
5591
|
if (!existingRecord) {
|
|
5371
5592
|
throw new RecordNotFoundError(
|
|
@@ -5522,7 +5743,7 @@ var init_BaseModel = __esm({
|
|
|
5522
5743
|
for (const [fieldKey, fieldOptions] of schema.fields) {
|
|
5523
5744
|
if (fieldOptions?.type !== "stringset") continue;
|
|
5524
5745
|
const value = recordYMap.get(fieldKey);
|
|
5525
|
-
if (value instanceof
|
|
5746
|
+
if (value instanceof Y5.Map) {
|
|
5526
5747
|
this.observeStringSetMapOnce(value);
|
|
5527
5748
|
}
|
|
5528
5749
|
}
|
|
@@ -5754,7 +5975,143 @@ init_BaseModel();
|
|
|
5754
5975
|
init_sql();
|
|
5755
5976
|
import initSqlJs from "sql.js";
|
|
5756
5977
|
import { Mutex } from "async-mutex";
|
|
5978
|
+
|
|
5979
|
+
// src/utils/environment.ts
|
|
5980
|
+
function detectEnvironment() {
|
|
5981
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
5982
|
+
return "node";
|
|
5983
|
+
}
|
|
5984
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
5985
|
+
return "browser";
|
|
5986
|
+
}
|
|
5987
|
+
if (typeof self !== "undefined" && typeof self.importScripts === "function") {
|
|
5988
|
+
return "browser";
|
|
5989
|
+
}
|
|
5990
|
+
return "unknown";
|
|
5991
|
+
}
|
|
5992
|
+
function isNode() {
|
|
5993
|
+
return detectEnvironment() === "node";
|
|
5994
|
+
}
|
|
5995
|
+
function isBrowser() {
|
|
5996
|
+
return detectEnvironment() === "browser";
|
|
5997
|
+
}
|
|
5998
|
+
async function isNodeModuleAvailable(moduleName) {
|
|
5999
|
+
if (!isNode()) {
|
|
6000
|
+
return false;
|
|
6001
|
+
}
|
|
6002
|
+
try {
|
|
6003
|
+
await import(
|
|
6004
|
+
/* @vite-ignore */
|
|
6005
|
+
moduleName
|
|
6006
|
+
);
|
|
6007
|
+
return true;
|
|
6008
|
+
} catch {
|
|
6009
|
+
return false;
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
var features = {
|
|
6013
|
+
get hasWebWorkers() {
|
|
6014
|
+
return isBrowser() && typeof Worker !== "undefined";
|
|
6015
|
+
},
|
|
6016
|
+
get hasFileSystem() {
|
|
6017
|
+
return isNode();
|
|
6018
|
+
},
|
|
6019
|
+
get hasWebAssembly() {
|
|
6020
|
+
return typeof WebAssembly !== "undefined";
|
|
6021
|
+
},
|
|
6022
|
+
async hasBetterSqlite3() {
|
|
6023
|
+
return await isNodeModuleAvailable("better-sqlite3");
|
|
6024
|
+
}
|
|
6025
|
+
};
|
|
6026
|
+
|
|
6027
|
+
// src/engines/SqljsEngine.ts
|
|
5757
6028
|
var SQL_WASM_URL = "/sql-wasm.wasm";
|
|
6029
|
+
var SQL_WASM_FILENAME = "sql-wasm.wasm";
|
|
6030
|
+
async function resolveNodeWasmPath() {
|
|
6031
|
+
if (!isNode()) {
|
|
6032
|
+
return null;
|
|
6033
|
+
}
|
|
6034
|
+
try {
|
|
6035
|
+
const cjsRequire = typeof __require !== "undefined" ? __require : void 0;
|
|
6036
|
+
if (cjsRequire) {
|
|
6037
|
+
return cjsRequire.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6038
|
+
}
|
|
6039
|
+
} catch {
|
|
6040
|
+
}
|
|
6041
|
+
try {
|
|
6042
|
+
const metaUrl = import.meta.url;
|
|
6043
|
+
if (metaUrl) {
|
|
6044
|
+
const { createRequire } = await import(
|
|
6045
|
+
/* @vite-ignore */
|
|
6046
|
+
"module"
|
|
6047
|
+
);
|
|
6048
|
+
const req = createRequire(metaUrl);
|
|
6049
|
+
return req.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6050
|
+
}
|
|
6051
|
+
} catch {
|
|
6052
|
+
}
|
|
6053
|
+
return null;
|
|
6054
|
+
}
|
|
6055
|
+
async function preflightNodeWasm(wasmPath) {
|
|
6056
|
+
if (!isNode()) {
|
|
6057
|
+
return;
|
|
6058
|
+
}
|
|
6059
|
+
if (/^[a-z]+:\/\//i.test(wasmPath)) {
|
|
6060
|
+
return;
|
|
6061
|
+
}
|
|
6062
|
+
const { existsSync } = await import(
|
|
6063
|
+
/* @vite-ignore */
|
|
6064
|
+
"fs"
|
|
6065
|
+
);
|
|
6066
|
+
if (!existsSync(wasmPath)) {
|
|
6067
|
+
throw new Error(
|
|
6068
|
+
`[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()).`
|
|
6069
|
+
);
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
async function loadSqlJsModule(locateFile) {
|
|
6073
|
+
let abortReject;
|
|
6074
|
+
const toError = (reason) => reason instanceof Error ? reason : new Error(
|
|
6075
|
+
`[SqljsEngine] sql.js aborted during initialization: ${String(
|
|
6076
|
+
reason
|
|
6077
|
+
)}`
|
|
6078
|
+
);
|
|
6079
|
+
const nodeProcess = isNode() && typeof process !== "undefined" ? process : void 0;
|
|
6080
|
+
const onUnhandled = (reason) => {
|
|
6081
|
+
const err = toError(reason);
|
|
6082
|
+
if (/Aborted/.test(err.message) || /Aborted/.test(String(reason))) {
|
|
6083
|
+
abortReject?.(err);
|
|
6084
|
+
return;
|
|
6085
|
+
}
|
|
6086
|
+
if (nodeProcess && nodeProcess.listenerCount("unhandledRejection") === 1) {
|
|
6087
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6088
|
+
void Promise.reject(reason);
|
|
6089
|
+
}
|
|
6090
|
+
};
|
|
6091
|
+
if (nodeProcess) {
|
|
6092
|
+
nodeProcess.on("unhandledRejection", onUnhandled);
|
|
6093
|
+
}
|
|
6094
|
+
const abortGuard = new Promise((_resolve, reject) => {
|
|
6095
|
+
abortReject = reject;
|
|
6096
|
+
});
|
|
6097
|
+
abortGuard.catch(() => {
|
|
6098
|
+
});
|
|
6099
|
+
try {
|
|
6100
|
+
const initPromise = initSqlJs({
|
|
6101
|
+
locateFile,
|
|
6102
|
+
// Layer 1: emscripten onAbort.
|
|
6103
|
+
onAbort: (reason) => abortReject?.(toError(reason))
|
|
6104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6105
|
+
});
|
|
6106
|
+
return await Promise.race([initPromise, abortGuard]);
|
|
6107
|
+
} finally {
|
|
6108
|
+
if (nodeProcess) {
|
|
6109
|
+
setTimeout(() => {
|
|
6110
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6111
|
+
}, 0);
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
5758
6115
|
var TransactionalSQLJSOperations = class {
|
|
5759
6116
|
db;
|
|
5760
6117
|
engine;
|
|
@@ -5820,22 +6177,37 @@ var SqljsEngine = class _SqljsEngine {
|
|
|
5820
6177
|
}
|
|
5821
6178
|
async initialize(engineOptions) {
|
|
5822
6179
|
Logger.info("[SqljsEngine] Initializing SQL.js engine...");
|
|
6180
|
+
const loadingModuleThisCall = !_SqljsEngine.SQL;
|
|
5823
6181
|
try {
|
|
5824
6182
|
if (!_SqljsEngine.SQL) {
|
|
5825
|
-
|
|
6183
|
+
let locateFileConfig;
|
|
6184
|
+
if (engineOptions?.locateFile) {
|
|
6185
|
+
locateFileConfig = engineOptions.locateFile;
|
|
6186
|
+
} else if (engineOptions?.wasmURL) {
|
|
6187
|
+
const url = engineOptions.wasmURL;
|
|
6188
|
+
locateFileConfig = (_file) => url;
|
|
6189
|
+
} else {
|
|
6190
|
+
const nodeWasmPath = await resolveNodeWasmPath();
|
|
6191
|
+
const resolved = nodeWasmPath ?? SQL_WASM_URL;
|
|
6192
|
+
locateFileConfig = (_file) => resolved;
|
|
6193
|
+
}
|
|
6194
|
+
const wasmPath = locateFileConfig(SQL_WASM_FILENAME);
|
|
5826
6195
|
Logger.debug(
|
|
5827
6196
|
"[SqljsEngine] Attempting to load SQL.js WASM from:",
|
|
5828
|
-
|
|
6197
|
+
wasmPath
|
|
5829
6198
|
);
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
6199
|
+
await preflightNodeWasm(wasmPath);
|
|
6200
|
+
const SQL = await loadSqlJsModule(locateFileConfig);
|
|
6201
|
+
_SqljsEngine.SQL = SQL;
|
|
5833
6202
|
Logger.info("[SqljsEngine] SQL.js WASM loaded successfully.");
|
|
5834
6203
|
}
|
|
5835
6204
|
this.db = new _SqljsEngine.SQL.Database();
|
|
5836
6205
|
Logger.info("[SqljsEngine] SQL.js Database initialized in memory.");
|
|
5837
6206
|
this.updateTableNames();
|
|
5838
6207
|
} catch (error) {
|
|
6208
|
+
if (loadingModuleThisCall) {
|
|
6209
|
+
_SqljsEngine.SQL = null;
|
|
6210
|
+
}
|
|
5839
6211
|
Logger.error("[SqljsEngine] Error during SQL.js initialization:", error);
|
|
5840
6212
|
throw error;
|
|
5841
6213
|
}
|
|
@@ -6121,54 +6493,6 @@ var SqljsEngine = class _SqljsEngine {
|
|
|
6121
6493
|
}
|
|
6122
6494
|
};
|
|
6123
6495
|
|
|
6124
|
-
// src/utils/environment.ts
|
|
6125
|
-
function detectEnvironment() {
|
|
6126
|
-
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
6127
|
-
return "node";
|
|
6128
|
-
}
|
|
6129
|
-
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
6130
|
-
return "browser";
|
|
6131
|
-
}
|
|
6132
|
-
if (typeof self !== "undefined" && typeof self.importScripts === "function") {
|
|
6133
|
-
return "browser";
|
|
6134
|
-
}
|
|
6135
|
-
return "unknown";
|
|
6136
|
-
}
|
|
6137
|
-
function isNode() {
|
|
6138
|
-
return detectEnvironment() === "node";
|
|
6139
|
-
}
|
|
6140
|
-
function isBrowser() {
|
|
6141
|
-
return detectEnvironment() === "browser";
|
|
6142
|
-
}
|
|
6143
|
-
async function isNodeModuleAvailable(moduleName) {
|
|
6144
|
-
if (!isNode()) {
|
|
6145
|
-
return false;
|
|
6146
|
-
}
|
|
6147
|
-
try {
|
|
6148
|
-
await import(
|
|
6149
|
-
/* @vite-ignore */
|
|
6150
|
-
moduleName
|
|
6151
|
-
);
|
|
6152
|
-
return true;
|
|
6153
|
-
} catch {
|
|
6154
|
-
return false;
|
|
6155
|
-
}
|
|
6156
|
-
}
|
|
6157
|
-
var features = {
|
|
6158
|
-
get hasWebWorkers() {
|
|
6159
|
-
return isBrowser() && typeof Worker !== "undefined";
|
|
6160
|
-
},
|
|
6161
|
-
get hasFileSystem() {
|
|
6162
|
-
return isNode();
|
|
6163
|
-
},
|
|
6164
|
-
get hasWebAssembly() {
|
|
6165
|
-
return typeof WebAssembly !== "undefined";
|
|
6166
|
-
},
|
|
6167
|
-
async hasBetterSqlite3() {
|
|
6168
|
-
return await isNodeModuleAvailable("better-sqlite3");
|
|
6169
|
-
}
|
|
6170
|
-
};
|
|
6171
|
-
|
|
6172
6496
|
// src/engines/BrowserDatabaseFactory.ts
|
|
6173
6497
|
init_BaseModel();
|
|
6174
6498
|
var BrowserDatabaseFactory = class {
|
|
@@ -6479,6 +6803,7 @@ init_StringSet();
|
|
|
6479
6803
|
// src/models/schema.ts
|
|
6480
6804
|
init_BaseModel();
|
|
6481
6805
|
init_ModelRegistry();
|
|
6806
|
+
init_uniqueIndex();
|
|
6482
6807
|
function defineModelSchema(input) {
|
|
6483
6808
|
const { name, fields } = input;
|
|
6484
6809
|
const options = {
|
|
@@ -6551,7 +6876,7 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
|
|
|
6551
6876
|
for (const [fieldName, options] of fields.entries()) {
|
|
6552
6877
|
if (options.unique) {
|
|
6553
6878
|
resolved.push({
|
|
6554
|
-
name:
|
|
6879
|
+
name: fieldUniqueConstraintName(modelName, fieldName),
|
|
6555
6880
|
fields: [fieldName]
|
|
6556
6881
|
});
|
|
6557
6882
|
}
|
|
@@ -6616,7 +6941,7 @@ function attachAndRegisterModel(modelClass, schema) {
|
|
|
6616
6941
|
|
|
6617
6942
|
// src/utils/yDocSchema.ts
|
|
6618
6943
|
init_yjsValues();
|
|
6619
|
-
import * as
|
|
6944
|
+
import * as Y6 from "yjs";
|
|
6620
6945
|
function discoverSchema(yDoc) {
|
|
6621
6946
|
const models = {};
|
|
6622
6947
|
const metaNames = /* @__PURE__ */ new Set();
|
|
@@ -6650,7 +6975,7 @@ function discoverModelNames(yDoc) {
|
|
|
6650
6975
|
function materializeMap(yDoc, key) {
|
|
6651
6976
|
try {
|
|
6652
6977
|
const map = yDoc.getMap(key);
|
|
6653
|
-
return map instanceof
|
|
6978
|
+
return map instanceof Y6.Map ? map : null;
|
|
6654
6979
|
} catch {
|
|
6655
6980
|
return null;
|
|
6656
6981
|
}
|
|
@@ -6660,11 +6985,11 @@ function readModelMeta(metaMap) {
|
|
|
6660
6985
|
let constraints;
|
|
6661
6986
|
let relationships;
|
|
6662
6987
|
for (const [key, value] of metaMap.entries()) {
|
|
6663
|
-
if (key === "_constraints" && value instanceof
|
|
6988
|
+
if (key === "_constraints" && value instanceof Y6.Map) {
|
|
6664
6989
|
constraints = readConstraints(value);
|
|
6665
|
-
} else if (key === "_relationships" && value instanceof
|
|
6990
|
+
} else if (key === "_relationships" && value instanceof Y6.Map) {
|
|
6666
6991
|
relationships = readRelationships(value);
|
|
6667
|
-
} else if (value instanceof
|
|
6992
|
+
} else if (value instanceof Y6.Map) {
|
|
6668
6993
|
fields[key] = readFieldMeta(value);
|
|
6669
6994
|
}
|
|
6670
6995
|
}
|
|
@@ -6699,7 +7024,7 @@ function inferModelFromData(dataMap) {
|
|
|
6699
7024
|
const fields = {};
|
|
6700
7025
|
let sampled = 0;
|
|
6701
7026
|
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
6702
|
-
if (!(recordValue instanceof
|
|
7027
|
+
if (!(recordValue instanceof Y6.Map)) continue;
|
|
6703
7028
|
if (++sampled > 5) break;
|
|
6704
7029
|
for (const [fieldName, value] of recordValue.entries()) {
|
|
6705
7030
|
if (fieldName.startsWith("_")) continue;
|
|
@@ -6727,7 +7052,7 @@ function inferTypeFromValue(value) {
|
|
|
6727
7052
|
function readConstraints(constraintsMap) {
|
|
6728
7053
|
const out = {};
|
|
6729
7054
|
for (const [name, value] of constraintsMap.entries()) {
|
|
6730
|
-
if (!(value instanceof
|
|
7055
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
6731
7056
|
let fields = [];
|
|
6732
7057
|
const rawFields = value.get("fields");
|
|
6733
7058
|
if (typeof rawFields === "string") {
|
|
@@ -6746,7 +7071,7 @@ function readConstraints(constraintsMap) {
|
|
|
6746
7071
|
function readRelationships(relsMap) {
|
|
6747
7072
|
const out = {};
|
|
6748
7073
|
for (const [name, value] of relsMap.entries()) {
|
|
6749
|
-
if (!(value instanceof
|
|
7074
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
6750
7075
|
const rel = {};
|
|
6751
7076
|
for (const [k, v] of value.entries()) {
|
|
6752
7077
|
rel[k] = v;
|