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/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -344,21 +350,36 @@ var init_CursorManager = __esm({
|
|
|
344
350
|
return fields;
|
|
345
351
|
}
|
|
346
352
|
/**
|
|
347
|
-
* Build ORDER BY clause from sort specification
|
|
353
|
+
* Build ORDER BY clause from sort specification.
|
|
354
|
+
*
|
|
355
|
+
* `direction` is the pagination direction (1 = forward, -1 = backward).
|
|
356
|
+
* For backward pagination each field's effective ORDER BY direction is
|
|
357
|
+
* reversed so the cursor's flipped inequality (built by
|
|
358
|
+
* `buildPaginationConditions`) returns the page adjacent to the cursor,
|
|
359
|
+
* not the globally smallest/largest window. Without this,
|
|
360
|
+
* `field < cursor ... ORDER BY field ASC LIMIT n` returns the n smallest
|
|
361
|
+
* rows overall instead of the n rows just below the cursor.
|
|
362
|
+
*
|
|
363
|
+
* The returned `directions` array carries the *declared* per-field sort
|
|
364
|
+
* directions (unaffected by `direction`) — `buildPaginationConditions`
|
|
365
|
+
* needs the declared directions to pick each field's comparison operator.
|
|
348
366
|
*/
|
|
349
|
-
static buildOrderClause(sort, fieldFormatter = (field) => field) {
|
|
367
|
+
static buildOrderClause(sort, fieldFormatter = (field) => field, direction = 1) {
|
|
350
368
|
const fields = this.extractSortFields(sort);
|
|
351
369
|
const clauses = [];
|
|
352
370
|
const directions = [];
|
|
371
|
+
const sqlDir = (declared) => {
|
|
372
|
+
const effective = direction === -1 ? declared === 1 ? -1 : 1 : declared;
|
|
373
|
+
return effective === 1 ? "ASC" : "DESC";
|
|
374
|
+
};
|
|
353
375
|
if (sort && Object.keys(sort).length > 0) {
|
|
354
376
|
for (const [field, dir] of Object.entries(sort)) {
|
|
355
|
-
|
|
356
|
-
clauses.push(`${fieldFormatter(field)} ${sqlDirection}`);
|
|
377
|
+
clauses.push(`${fieldFormatter(field)} ${sqlDir(dir)}`);
|
|
357
378
|
directions.push(dir);
|
|
358
379
|
}
|
|
359
380
|
}
|
|
360
381
|
if (!sort || !sort.hasOwnProperty("id")) {
|
|
361
|
-
clauses.push(`${fieldFormatter("id")}
|
|
382
|
+
clauses.push(`${fieldFormatter("id")} ${sqlDir(1)}`);
|
|
362
383
|
directions.push(1);
|
|
363
384
|
} else if (sort && sort.hasOwnProperty("id")) {
|
|
364
385
|
directions.push(sort["id"]);
|
|
@@ -381,18 +402,27 @@ var init_CursorManager = __esm({
|
|
|
381
402
|
/**
|
|
382
403
|
* Generate next and previous cursors from result set
|
|
383
404
|
*/
|
|
384
|
-
static generateResultCursors(results, sortFields,
|
|
405
|
+
static generateResultCursors(results, sortFields, requestedDirection, hasMore, isFirstPage = false) {
|
|
385
406
|
if (results.length === 0) {
|
|
386
407
|
return {};
|
|
387
408
|
}
|
|
388
409
|
const cursors = {};
|
|
389
410
|
if (hasMore && results.length > 0) {
|
|
390
411
|
const lastResult = results[results.length - 1];
|
|
391
|
-
cursors.nextCursor = this.generateCursor(
|
|
412
|
+
cursors.nextCursor = this.generateCursor(
|
|
413
|
+
lastResult,
|
|
414
|
+
sortFields,
|
|
415
|
+
requestedDirection
|
|
416
|
+
);
|
|
392
417
|
}
|
|
393
418
|
if (results.length > 0 && !isFirstPage) {
|
|
394
419
|
const firstResult = results[0];
|
|
395
|
-
|
|
420
|
+
const opposite = requestedDirection === 1 ? -1 : 1;
|
|
421
|
+
cursors.prevCursor = this.generateCursor(
|
|
422
|
+
firstResult,
|
|
423
|
+
sortFields,
|
|
424
|
+
opposite
|
|
425
|
+
);
|
|
396
426
|
}
|
|
397
427
|
return cursors;
|
|
398
428
|
}
|
|
@@ -525,7 +555,8 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
525
555
|
const whereClause = this.translateFilter(filter);
|
|
526
556
|
const orderClause = CursorManager.buildOrderClause(
|
|
527
557
|
options?.sort,
|
|
528
|
-
(field) => this.getQuotedField(field)
|
|
558
|
+
(field) => this.getQuotedField(field),
|
|
559
|
+
options?.direction || 1
|
|
529
560
|
);
|
|
530
561
|
const limitClause = this.buildLimitClause(options);
|
|
531
562
|
const paginationClause = this.buildPaginationClause(
|
|
@@ -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,11 +1428,37 @@ 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/query/IncludeResolver.ts
|
|
1340
1463
|
var IncludeResolver_exports = {};
|
|
1341
1464
|
__export(IncludeResolver_exports, {
|
|
@@ -1694,7 +1817,13 @@ var init_IncludeResolver = __esm({
|
|
|
1694
1817
|
}
|
|
1695
1818
|
return null;
|
|
1696
1819
|
}
|
|
1697
|
-
/**
|
|
1820
|
+
/**
|
|
1821
|
+
* Apply a projection to a single record. Assumes the projection was already
|
|
1822
|
+
* validated upstream (mixed inclusion+exclusion rejected by the translator):
|
|
1823
|
+
* `isInclusion` treats "any value === 1" as inclusion mode. `id` and `type`
|
|
1824
|
+
* are system-identity fields and are always kept in BOTH modes (mirrors the
|
|
1825
|
+
* DatabaseDO `_applyProjection`); `_meta` stays excludable.
|
|
1826
|
+
*/
|
|
1698
1827
|
_applyProjection(record, projection) {
|
|
1699
1828
|
const fields = Object.entries(projection);
|
|
1700
1829
|
if (fields.length === 0) return record;
|
|
@@ -1713,7 +1842,7 @@ var init_IncludeResolver = __esm({
|
|
|
1713
1842
|
} else {
|
|
1714
1843
|
const result = { ...record };
|
|
1715
1844
|
for (const [f, v] of fields) {
|
|
1716
|
-
if (v === 0) delete result[f];
|
|
1845
|
+
if (v === 0 && f !== "id" && f !== "type") delete result[f];
|
|
1717
1846
|
}
|
|
1718
1847
|
return result;
|
|
1719
1848
|
}
|
|
@@ -1732,10 +1861,10 @@ __export(BaseModel_exports, {
|
|
|
1732
1861
|
UniqueConstraintViolationError: () => UniqueConstraintViolationError,
|
|
1733
1862
|
generateULID: () => generateULID
|
|
1734
1863
|
});
|
|
1735
|
-
import * as
|
|
1736
|
-
import { ulid } from "ulid";
|
|
1864
|
+
import * as Y5 from "yjs";
|
|
1865
|
+
import { ulid as ulid2 } from "ulid";
|
|
1737
1866
|
function generateULID() {
|
|
1738
|
-
return
|
|
1867
|
+
return ulid2();
|
|
1739
1868
|
}
|
|
1740
1869
|
var LogLevel, Logger, UniqueConstraintViolationError, RecordNotFoundError, SCHEMA_ACCESSORS_KEY, BaseModel;
|
|
1741
1870
|
var init_BaseModel = __esm({
|
|
@@ -1747,7 +1876,9 @@ var init_BaseModel = __esm({
|
|
|
1747
1876
|
init_CursorManager();
|
|
1748
1877
|
init_sql();
|
|
1749
1878
|
init_metaSync();
|
|
1879
|
+
init_writeLifecycle();
|
|
1750
1880
|
init_yjsValues();
|
|
1881
|
+
init_uniqueIndex();
|
|
1751
1882
|
LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
1752
1883
|
LogLevel2[LogLevel2["SILENT"] = 0] = "SILENT";
|
|
1753
1884
|
LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
|
|
@@ -2132,7 +2263,9 @@ var init_BaseModel = __esm({
|
|
|
2132
2263
|
this.setValue(fieldKey, data[fieldKey]);
|
|
2133
2264
|
this._constructorProvidedFields.add(fieldKey);
|
|
2134
2265
|
} else if (fieldOptions.default !== void 0) {
|
|
2135
|
-
const defaultValue =
|
|
2266
|
+
const defaultValue = resolveLiteralOrFunctionDefault(
|
|
2267
|
+
fieldOptions.default
|
|
2268
|
+
);
|
|
2136
2269
|
this.setValue(fieldKey, defaultValue);
|
|
2137
2270
|
}
|
|
2138
2271
|
}
|
|
@@ -2424,7 +2557,7 @@ var init_BaseModel = __esm({
|
|
|
2424
2557
|
}
|
|
2425
2558
|
getStringSetFromYjs(fieldName) {
|
|
2426
2559
|
const yjsData = this.getFromYjs(fieldName);
|
|
2427
|
-
if (yjsData instanceof
|
|
2560
|
+
if (yjsData instanceof Y5.Map) {
|
|
2428
2561
|
return Array.from(yjsData.keys());
|
|
2429
2562
|
}
|
|
2430
2563
|
if (yjsData && typeof yjsData === "object") {
|
|
@@ -2532,7 +2665,7 @@ var init_BaseModel = __esm({
|
|
|
2532
2665
|
}
|
|
2533
2666
|
const extractStringSetValues = (value) => {
|
|
2534
2667
|
if (!value) return [];
|
|
2535
|
-
if (value instanceof
|
|
2668
|
+
if (value instanceof Y5.Map) {
|
|
2536
2669
|
return Array.from(value.entries()).filter(([, isMember]) => Boolean(isMember)).map(([key]) => key);
|
|
2537
2670
|
}
|
|
2538
2671
|
if (Array.isArray(value)) {
|
|
@@ -2549,7 +2682,7 @@ var init_BaseModel = __esm({
|
|
|
2549
2682
|
if (!recordId) continue;
|
|
2550
2683
|
let itemData;
|
|
2551
2684
|
const stringSetValuesByField = {};
|
|
2552
|
-
if (recordData instanceof
|
|
2685
|
+
if (recordData instanceof Y5.Map) {
|
|
2553
2686
|
itemData = {};
|
|
2554
2687
|
const unknownFields = [];
|
|
2555
2688
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -2665,20 +2798,10 @@ var init_BaseModel = __esm({
|
|
|
2665
2798
|
Logger.verbose(
|
|
2666
2799
|
`[${this.name}] Setting up YMap observer for document ${docId}/${modelName}...`
|
|
2667
2800
|
);
|
|
2668
|
-
const
|
|
2669
|
-
const keyParts = [];
|
|
2670
|
-
for (const field of fields) {
|
|
2671
|
-
const value = recordData instanceof Y3.Map ? recordData.get(field) : recordData[field];
|
|
2672
|
-
if (value === null || value === void 0) {
|
|
2673
|
-
return null;
|
|
2674
|
-
}
|
|
2675
|
-
keyParts.push(String(value));
|
|
2676
|
-
}
|
|
2677
|
-
return keyParts.join("|");
|
|
2678
|
-
};
|
|
2801
|
+
const buildUniqueKey2 = (recordData, fields) => buildUniqueKey(recordData, fields);
|
|
2679
2802
|
const extractItemData = (key, recordData) => {
|
|
2680
2803
|
let itemData;
|
|
2681
|
-
if (recordData instanceof
|
|
2804
|
+
if (recordData instanceof Y5.Map) {
|
|
2682
2805
|
itemData = {};
|
|
2683
2806
|
const unknownFields = [];
|
|
2684
2807
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -2762,7 +2885,7 @@ var init_BaseModel = __esm({
|
|
|
2762
2885
|
const recordsByUniqueKey = /* @__PURE__ */ new Map();
|
|
2763
2886
|
for (const [recordId, recordData] of allRecords.entries()) {
|
|
2764
2887
|
if (recordIdsToDiscard.has(recordId)) continue;
|
|
2765
|
-
const uniqueKey =
|
|
2888
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2766
2889
|
if (uniqueKey === null) continue;
|
|
2767
2890
|
if (!recordsByUniqueKey.has(uniqueKey)) {
|
|
2768
2891
|
recordsByUniqueKey.set(uniqueKey, []);
|
|
@@ -2808,7 +2931,7 @@ var init_BaseModel = __esm({
|
|
|
2808
2931
|
if (!recordData || !key) continue;
|
|
2809
2932
|
const itemData = extractItemData(key, recordData);
|
|
2810
2933
|
if (!itemData) continue;
|
|
2811
|
-
if (change.action === "add" && recordData instanceof
|
|
2934
|
+
if (change.action === "add" && recordData instanceof Y5.Map) {
|
|
2812
2935
|
Logger.verbose(
|
|
2813
2936
|
`[${this.name}] Setting up observer on newly added nested YMap for record ${key} in document ${docId}`
|
|
2814
2937
|
);
|
|
@@ -2906,9 +3029,12 @@ var init_BaseModel = __esm({
|
|
|
2906
3029
|
const recordData = documentYMap.get(idToDiscard);
|
|
2907
3030
|
if (!recordData) continue;
|
|
2908
3031
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
2909
|
-
const uniqueKey =
|
|
3032
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2910
3033
|
if (uniqueKey === null) continue;
|
|
2911
|
-
const constraintMapName =
|
|
3034
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3035
|
+
modelName,
|
|
3036
|
+
constraint.name
|
|
3037
|
+
);
|
|
2912
3038
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
2913
3039
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
2914
3040
|
if (currentIndexValue === idToDiscard) {
|
|
@@ -2926,11 +3052,14 @@ var init_BaseModel = __esm({
|
|
|
2926
3052
|
}, `conflict-resolution-${modelName}-${docId}`);
|
|
2927
3053
|
yDoc.transact(() => {
|
|
2928
3054
|
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
2929
|
-
const constraintMapName =
|
|
3055
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3056
|
+
modelName,
|
|
3057
|
+
constraint.name
|
|
3058
|
+
);
|
|
2930
3059
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
2931
3060
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
2932
3061
|
if (!recordData) continue;
|
|
2933
|
-
const uniqueKey =
|
|
3062
|
+
const uniqueKey = buildUniqueKey2(recordData, constraint.fields);
|
|
2934
3063
|
if (uniqueKey === null) continue;
|
|
2935
3064
|
const currentIndexValue = constraintMap.get(uniqueKey);
|
|
2936
3065
|
if (currentIndexValue !== recordId) {
|
|
@@ -2953,7 +3082,7 @@ var init_BaseModel = __esm({
|
|
|
2953
3082
|
`[${this.name}] Setting up observers on existing nested YMaps for ${modelName}/${docId}...`
|
|
2954
3083
|
);
|
|
2955
3084
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
2956
|
-
if (recordData instanceof
|
|
3085
|
+
if (recordData instanceof Y5.Map) {
|
|
2957
3086
|
Logger.verbose(
|
|
2958
3087
|
`[${this.name}] Setting up observer on existing nested YMap for record ${recordId} in document ${docId}`
|
|
2959
3088
|
);
|
|
@@ -3179,26 +3308,7 @@ var init_BaseModel = __esm({
|
|
|
3179
3308
|
* if the field hasn't been touched since the wire-format change in #561.
|
|
3180
3309
|
*/
|
|
3181
3310
|
applyStringSetChangeToYMap(recordYMap, fieldName, change) {
|
|
3182
|
-
|
|
3183
|
-
if (!(nested instanceof Y3.Map)) {
|
|
3184
|
-
const migrated = new Y3.Map();
|
|
3185
|
-
if (nested && typeof nested === "object") {
|
|
3186
|
-
for (const [member, marker] of Object.entries(
|
|
3187
|
-
nested
|
|
3188
|
-
)) {
|
|
3189
|
-
if (Boolean(marker)) migrated.set(member, true);
|
|
3190
|
-
}
|
|
3191
|
-
}
|
|
3192
|
-
recordYMap.set(fieldName, migrated);
|
|
3193
|
-
nested = migrated;
|
|
3194
|
-
}
|
|
3195
|
-
const target = nested;
|
|
3196
|
-
for (const member of change.additions) {
|
|
3197
|
-
target.set(member, true);
|
|
3198
|
-
}
|
|
3199
|
-
for (const member of change.removals) {
|
|
3200
|
-
target.delete(member);
|
|
3201
|
-
}
|
|
3311
|
+
applyStringSetChange(recordYMap, fieldName, change);
|
|
3202
3312
|
}
|
|
3203
3313
|
/**
|
|
3204
3314
|
* Deep equality check for comparing field values
|
|
@@ -3400,6 +3510,34 @@ var init_BaseModel = __esm({
|
|
|
3400
3510
|
`[${modelName}.save] getCurrentJSState() returned:`,
|
|
3401
3511
|
dataToSave
|
|
3402
3512
|
);
|
|
3513
|
+
const uniqueMissConflicts = /* @__PURE__ */ new Map();
|
|
3514
|
+
for (const constraint of schema.resolvedUniqueConstraints) {
|
|
3515
|
+
const newUniqueKey = this._buildUniqueKey(
|
|
3516
|
+
constraint.fields,
|
|
3517
|
+
dataToSave,
|
|
3518
|
+
modelName,
|
|
3519
|
+
constraint.name
|
|
3520
|
+
);
|
|
3521
|
+
if (newUniqueKey === null) {
|
|
3522
|
+
continue;
|
|
3523
|
+
}
|
|
3524
|
+
const constraintMap = yDoc.getMap(
|
|
3525
|
+
uniqueIndexMapName(modelName, constraint.name)
|
|
3526
|
+
);
|
|
3527
|
+
if (!constraintMap.get(newUniqueKey)) {
|
|
3528
|
+
const constraintKeyValues = constraint.fields.map(
|
|
3529
|
+
(field) => dataToSave[field]
|
|
3530
|
+
);
|
|
3531
|
+
const conflictingRecord = await modelConstructor._resolveByUniqueFallback(
|
|
3532
|
+
constraint,
|
|
3533
|
+
constraintKeyValues,
|
|
3534
|
+
{ excludeId: this.id }
|
|
3535
|
+
);
|
|
3536
|
+
if (conflictingRecord) {
|
|
3537
|
+
uniqueMissConflicts.set(constraint.name, conflictingRecord.id);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3403
3541
|
await yDoc.transact(async () => {
|
|
3404
3542
|
if (upsertOnConstraint && upsertOnKey) {
|
|
3405
3543
|
const constraintMapName = `_uniqueIdx_${modelName}_${upsertOnConstraint.name}`;
|
|
@@ -3441,7 +3579,7 @@ var init_BaseModel = __esm({
|
|
|
3441
3579
|
Logger.debug(`[${modelName}.save] isUpdate: ${isUpdate}`);
|
|
3442
3580
|
Logger.debug(`[${modelName}.save] recordYMap exists: ${!!recordYMap}`);
|
|
3443
3581
|
if (!recordYMap) {
|
|
3444
|
-
recordYMap = new
|
|
3582
|
+
recordYMap = new Y5.Map();
|
|
3445
3583
|
Logger.verbose(
|
|
3446
3584
|
`[${modelName}] Creating new nested YMap for record ${this.id} in document ${targetDocId || "legacy"}`
|
|
3447
3585
|
);
|
|
@@ -3493,7 +3631,10 @@ var init_BaseModel = __esm({
|
|
|
3493
3631
|
);
|
|
3494
3632
|
continue;
|
|
3495
3633
|
}
|
|
3496
|
-
const constraintMapName =
|
|
3634
|
+
const constraintMapName = uniqueIndexMapName(
|
|
3635
|
+
modelName,
|
|
3636
|
+
constraint.name
|
|
3637
|
+
);
|
|
3497
3638
|
const constraintMap = yDoc.getMap(constraintMapName);
|
|
3498
3639
|
Logger.verbose(
|
|
3499
3640
|
`[${modelName}] Save Check (Item '${this.id}'): GET from '${constraintMapName}' with Key '${newUniqueKey.substring(
|
|
@@ -3522,6 +3663,26 @@ var init_BaseModel = __esm({
|
|
|
3522
3663
|
this.id,
|
|
3523
3664
|
existingRecordIdWithNewKey
|
|
3524
3665
|
);
|
|
3666
|
+
} else if (!existingRecordIdWithNewKey) {
|
|
3667
|
+
const conflictingRecordId = uniqueMissConflicts.get(constraint.name);
|
|
3668
|
+
if (conflictingRecordId && conflictingRecordId !== this.id) {
|
|
3669
|
+
Logger.verbose(
|
|
3670
|
+
`[${modelName}] Save Check (Item '${this.id}'): index miss for '${constraint.name}', but engine fallback found conflicting index-less record ${conflictingRecordId}.`
|
|
3671
|
+
);
|
|
3672
|
+
throw new UniqueConstraintViolationError(
|
|
3673
|
+
`Unique constraint '${constraint.name}' violated for model '${modelName}' on fields [${constraint.fields.join(
|
|
3674
|
+
", "
|
|
3675
|
+
)}]. Attempted ID: ${this.id}. Value(s) starting with '${newUniqueKey.substring(
|
|
3676
|
+
0,
|
|
3677
|
+
50
|
|
3678
|
+
)}...' already exist for index-less record ID ${conflictingRecordId}.`,
|
|
3679
|
+
modelName,
|
|
3680
|
+
constraint.name,
|
|
3681
|
+
constraint.fields,
|
|
3682
|
+
this.id,
|
|
3683
|
+
conflictingRecordId
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3525
3686
|
}
|
|
3526
3687
|
}
|
|
3527
3688
|
if (isUpdate && oldData) {
|
|
@@ -3865,34 +4026,46 @@ var init_BaseModel = __esm({
|
|
|
3865
4026
|
const modelName = schema.options.name;
|
|
3866
4027
|
Logger.verbose(`[${modelName}] Executing query:`, { filter, options });
|
|
3867
4028
|
const hasIncludes = options?.include && options.include.length > 0;
|
|
3868
|
-
const
|
|
4029
|
+
const requestedLimit = options?.limit;
|
|
4030
|
+
const overFetch = typeof requestedLimit === "number" && requestedLimit > 0;
|
|
4031
|
+
let translatorOptions = hasIncludes && options?.projection ? { ...options, projection: void 0 } : options;
|
|
4032
|
+
if (overFetch) {
|
|
4033
|
+
translatorOptions = {
|
|
4034
|
+
...translatorOptions || {},
|
|
4035
|
+
limit: requestedLimit + 1
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
3869
4038
|
const translator = new DocumentQueryTranslator(modelName, schema.fields);
|
|
3870
4039
|
const translatedQuery = translator.translateFind(filter, translatorOptions);
|
|
3871
4040
|
Logger.verbose(`[${modelName}] Translated SQL:`, translatedQuery.sql);
|
|
3872
4041
|
Logger.verbose(`[${modelName}] SQL params:`, translatedQuery.params);
|
|
3873
|
-
|
|
4042
|
+
let results = await _BaseModel.dbInstance.query(
|
|
3874
4043
|
translatedQuery.sql,
|
|
3875
4044
|
translatedQuery.params
|
|
3876
4045
|
);
|
|
3877
4046
|
Logger.verbose(`[${modelName}] Query returned ${results.length} results`);
|
|
4047
|
+
let hasMore = false;
|
|
4048
|
+
if (overFetch && results.length > requestedLimit) {
|
|
4049
|
+
hasMore = true;
|
|
4050
|
+
results = results.slice(0, requestedLimit);
|
|
4051
|
+
}
|
|
3878
4052
|
if (options?.include && options.include.length > 0) {
|
|
3879
4053
|
const { IncludeResolver: IncludeResolver2 } = await Promise.resolve().then(() => (init_IncludeResolver(), IncludeResolver_exports));
|
|
3880
4054
|
const resolver = new IncludeResolver2(_BaseModel.dbInstance);
|
|
3881
4055
|
await resolver.resolve(results, options.include, 0, modelName);
|
|
3882
4056
|
}
|
|
3883
|
-
const requestedLimit = options?.limit;
|
|
3884
|
-
const hasMore = CursorManager.hasMoreResults(
|
|
3885
|
-
requestedLimit,
|
|
3886
|
-
results.length
|
|
3887
|
-
);
|
|
3888
4057
|
const isFirstPage = !options?.uniqueStartKey;
|
|
4058
|
+
const direction = options?.direction || 1;
|
|
3889
4059
|
const cursors = CursorManager.generateResultCursors(
|
|
3890
4060
|
results,
|
|
3891
4061
|
translatedQuery.sortFields,
|
|
3892
|
-
|
|
4062
|
+
direction,
|
|
3893
4063
|
hasMore,
|
|
3894
4064
|
isFirstPage
|
|
3895
4065
|
);
|
|
4066
|
+
if (direction === -1) {
|
|
4067
|
+
results.reverse();
|
|
4068
|
+
}
|
|
3896
4069
|
let transformedData;
|
|
3897
4070
|
if (options?.projection && Object.keys(options.projection).length > 0) {
|
|
3898
4071
|
transformedData = results.map((row) => {
|
|
@@ -4468,7 +4641,7 @@ var init_BaseModel = __esm({
|
|
|
4468
4641
|
);
|
|
4469
4642
|
}
|
|
4470
4643
|
for (const [recordId, recordYMap] of documentYMap.entries()) {
|
|
4471
|
-
if (recordYMap instanceof
|
|
4644
|
+
if (recordYMap instanceof Y5.Map) {
|
|
4472
4645
|
const instance = new this({ id: recordId });
|
|
4473
4646
|
instance._metaDocId = docId;
|
|
4474
4647
|
const connectedDoc = modelConstructor.connectedDocuments.get(docId);
|
|
@@ -4493,6 +4666,50 @@ var init_BaseModel = __esm({
|
|
|
4493
4666
|
}
|
|
4494
4667
|
return allInstances;
|
|
4495
4668
|
}
|
|
4669
|
+
/**
|
|
4670
|
+
* Resolve a record by its unique-constraint field values via an engine query.
|
|
4671
|
+
* Used as a fallback when the `_uniqueIdx_<model>_<constraint>` map has no
|
|
4672
|
+
* entry for a key.
|
|
4673
|
+
*
|
|
4674
|
+
* The index map is a cache over the SQLite projection, not the source of
|
|
4675
|
+
* truth: every record is loaded into the projection on `initializeForDocument`
|
|
4676
|
+
* regardless of index presence, but a record written by a client predating
|
|
4677
|
+
* index maintenance (legacy data) is missing from the index. Such a record
|
|
4678
|
+
* silently escapes both `upsertByUnique` lookups and the `save()` uniqueness
|
|
4679
|
+
* check. A query over the constraint fields finds it reliably.
|
|
4680
|
+
*
|
|
4681
|
+
* Contract: RESOLVE ONLY. Returns the owning record (with `_metaDocId` set to
|
|
4682
|
+
* the document that holds it, via `queryOne`) or `null`. Callers decide
|
|
4683
|
+
* whether to heal the missing index entry (write paths) and do their own
|
|
4684
|
+
* logging — this method never mutates the index, so read paths stay
|
|
4685
|
+
* read-only. Mirrors `buildUniqueKey`: a null/undefined value in any
|
|
4686
|
+
* constraint field means the record does not participate in the constraint,
|
|
4687
|
+
* so no query runs.
|
|
4688
|
+
*/
|
|
4689
|
+
static async _resolveByUniqueFallback(constraint, keyValues, options) {
|
|
4690
|
+
if (!_BaseModel.dbInstance) {
|
|
4691
|
+
return null;
|
|
4692
|
+
}
|
|
4693
|
+
const filter = {};
|
|
4694
|
+
for (let i = 0; i < constraint.fields.length; i++) {
|
|
4695
|
+
const value = keyValues[i];
|
|
4696
|
+
if (value === null || value === void 0) {
|
|
4697
|
+
return null;
|
|
4698
|
+
}
|
|
4699
|
+
filter[constraint.fields[i]] = value;
|
|
4700
|
+
}
|
|
4701
|
+
if (options?.excludeId) {
|
|
4702
|
+
if ("id" in filter) {
|
|
4703
|
+
if (filter.id === options.excludeId) {
|
|
4704
|
+
return null;
|
|
4705
|
+
}
|
|
4706
|
+
} else {
|
|
4707
|
+
filter.id = { $ne: options.excludeId };
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
const found = await this.queryOne(filter);
|
|
4711
|
+
return found ?? null;
|
|
4712
|
+
}
|
|
4496
4713
|
static async findByUnique(constraintName, value) {
|
|
4497
4714
|
const modelConstructor = this;
|
|
4498
4715
|
const schema = modelConstructor.getSchema();
|
|
@@ -4531,7 +4748,10 @@ var init_BaseModel = __esm({
|
|
|
4531
4748
|
const documentInfo = modelConstructor.connectedDocuments.get(docId);
|
|
4532
4749
|
if (!documentInfo) continue;
|
|
4533
4750
|
const yDoc = documentInfo.yDoc;
|
|
4534
|
-
const constraintMapName2 =
|
|
4751
|
+
const constraintMapName2 = uniqueIndexMapName(
|
|
4752
|
+
currentModelName,
|
|
4753
|
+
constraint.name
|
|
4754
|
+
);
|
|
4535
4755
|
const constraintMap2 = yDoc.getMap(constraintMapName2);
|
|
4536
4756
|
const recordId2 = constraintMap2.get(uniqueKeyString);
|
|
4537
4757
|
if (recordId2) {
|
|
@@ -4539,7 +4759,7 @@ var init_BaseModel = __esm({
|
|
|
4539
4759
|
const documentYMap = modelConstructor.documentYMaps.get(documentYMapKey);
|
|
4540
4760
|
if (documentYMap) {
|
|
4541
4761
|
const recordYMap2 = documentYMap.get(recordId2);
|
|
4542
|
-
if (recordYMap2 && recordYMap2 instanceof
|
|
4762
|
+
if (recordYMap2 && recordYMap2 instanceof Y5.Map) {
|
|
4543
4763
|
const instance = new modelConstructor({
|
|
4544
4764
|
id: recordId2
|
|
4545
4765
|
});
|
|
@@ -4553,6 +4773,16 @@ var init_BaseModel = __esm({
|
|
|
4553
4773
|
}
|
|
4554
4774
|
}
|
|
4555
4775
|
}
|
|
4776
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4777
|
+
constraint,
|
|
4778
|
+
keyValues
|
|
4779
|
+
);
|
|
4780
|
+
if (fallbackRecord) {
|
|
4781
|
+
Logger.verbose(
|
|
4782
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
4783
|
+
);
|
|
4784
|
+
return fallbackRecord;
|
|
4785
|
+
}
|
|
4556
4786
|
Logger.verbose(
|
|
4557
4787
|
`[${currentModelName}.findByUnique] Record not found in any connected document`
|
|
4558
4788
|
);
|
|
@@ -4566,17 +4796,30 @@ var init_BaseModel = __esm({
|
|
|
4566
4796
|
`[${currentModelName}] No Y.Doc is connected for this model. Connect a document via initJsBao(...).connectDocument or call initializeForDocument(yDoc, db, docId, permissionHint) before using legacy findByUnique.`
|
|
4567
4797
|
);
|
|
4568
4798
|
}
|
|
4569
|
-
const constraintMapName =
|
|
4799
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4800
|
+
currentModelName,
|
|
4801
|
+
constraint.name
|
|
4802
|
+
);
|
|
4570
4803
|
const constraintMap = legacyDocInfo.yDoc.getMap(constraintMapName);
|
|
4571
4804
|
const recordId = constraintMap.get(uniqueKeyString);
|
|
4572
4805
|
if (!recordId) {
|
|
4806
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4807
|
+
constraint,
|
|
4808
|
+
keyValues
|
|
4809
|
+
);
|
|
4810
|
+
if (fallbackRecord) {
|
|
4811
|
+
Logger.verbose(
|
|
4812
|
+
`[${currentModelName}.findByUnique] Index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id} (legacy index-less record; index left unchanged on read path).`
|
|
4813
|
+
);
|
|
4814
|
+
return fallbackRecord;
|
|
4815
|
+
}
|
|
4573
4816
|
return null;
|
|
4574
4817
|
}
|
|
4575
4818
|
const recordYMap = legacyDocumentMap.get(recordId);
|
|
4576
4819
|
if (!recordYMap) {
|
|
4577
4820
|
return null;
|
|
4578
4821
|
}
|
|
4579
|
-
if (recordYMap instanceof
|
|
4822
|
+
if (recordYMap instanceof Y5.Map) {
|
|
4580
4823
|
const instance = new modelConstructor({
|
|
4581
4824
|
id: recordId
|
|
4582
4825
|
});
|
|
@@ -4634,7 +4877,10 @@ var init_BaseModel = __esm({
|
|
|
4634
4877
|
`[${currentModelName}] upsertByUnique for '${constraintName}': unique key cannot be built due to null/undefined values. Upsert cannot proceed reliably.`
|
|
4635
4878
|
);
|
|
4636
4879
|
}
|
|
4637
|
-
const constraintMapName =
|
|
4880
|
+
const constraintMapName = uniqueIndexMapName(
|
|
4881
|
+
currentModelName,
|
|
4882
|
+
constraint.name
|
|
4883
|
+
);
|
|
4638
4884
|
let existingRecord = null;
|
|
4639
4885
|
if (connectedDocuments && connectedDocuments.size > 0) {
|
|
4640
4886
|
for (const docId of connectedDocuments.keys()) {
|
|
@@ -4652,6 +4898,33 @@ var init_BaseModel = __esm({
|
|
|
4652
4898
|
}
|
|
4653
4899
|
} else {
|
|
4654
4900
|
}
|
|
4901
|
+
if (!existingRecord) {
|
|
4902
|
+
const fallbackRecord = await modelConstructor._resolveByUniqueFallback(
|
|
4903
|
+
constraint,
|
|
4904
|
+
keyValuesForLookup
|
|
4905
|
+
);
|
|
4906
|
+
if (fallbackRecord) {
|
|
4907
|
+
existingRecord = fallbackRecord;
|
|
4908
|
+
const healDocId = fallbackRecord._metaDocId;
|
|
4909
|
+
const healDocInfo = healDocId ? connectedDocuments.get(healDocId) : void 0;
|
|
4910
|
+
const canHeal = !!healDocInfo && healDocInfo.permissionHint !== "read";
|
|
4911
|
+
if (healDocInfo && canHeal) {
|
|
4912
|
+
healDocInfo.yDoc.transact(() => {
|
|
4913
|
+
const healMap = healDocInfo.yDoc.getMap(constraintMapName);
|
|
4914
|
+
if (healMap.get(uniqueKeyString) !== fallbackRecord.id) {
|
|
4915
|
+
healMap.set(uniqueKeyString, fallbackRecord.id);
|
|
4916
|
+
}
|
|
4917
|
+
}, `heal-unique-index-${currentModelName}-${healDocId}`);
|
|
4918
|
+
Logger.verbose(
|
|
4919
|
+
`[${currentModelName}] upsertByUnique: index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id}; healed missing index entry (legacy index-less record).`
|
|
4920
|
+
);
|
|
4921
|
+
} else {
|
|
4922
|
+
Logger.verbose(
|
|
4923
|
+
`[${currentModelName}] upsertByUnique: index miss for '${constraintName}' resolved via engine fallback to record ${fallbackRecord.id}; skipped index heal (owning document is read-only or not connected).`
|
|
4924
|
+
);
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4655
4928
|
if (options?.objectMustExist) {
|
|
4656
4929
|
if (!existingRecord) {
|
|
4657
4930
|
throw new RecordNotFoundError(
|
|
@@ -4808,7 +5081,7 @@ var init_BaseModel = __esm({
|
|
|
4808
5081
|
for (const [fieldKey, fieldOptions] of schema.fields) {
|
|
4809
5082
|
if (fieldOptions?.type !== "stringset") continue;
|
|
4810
5083
|
const value = recordYMap.get(fieldKey);
|
|
4811
|
-
if (value instanceof
|
|
5084
|
+
if (value instanceof Y5.Map) {
|
|
4812
5085
|
this.observeStringSetMapOnce(value);
|
|
4813
5086
|
}
|
|
4814
5087
|
}
|
|
@@ -4994,6 +5267,19 @@ var init_BaseModel = __esm({
|
|
|
4994
5267
|
});
|
|
4995
5268
|
|
|
4996
5269
|
// src/models/relationshipManager.ts
|
|
5270
|
+
function warnIfOrderFieldNullable(modelClass, orderByField, relationshipDescription) {
|
|
5271
|
+
if (ALWAYS_PRESENT_ORDER_FIELDS.has(orderByField)) return;
|
|
5272
|
+
const schema = modelClass.getSchema?.();
|
|
5273
|
+
const fields = schema?.fields;
|
|
5274
|
+
if (!fields || typeof fields.get !== "function") return;
|
|
5275
|
+
const fieldOptions = fields.get(orderByField);
|
|
5276
|
+
if (!fieldOptions) return;
|
|
5277
|
+
if (fieldOptions.required !== true) {
|
|
5278
|
+
Logger.warn(
|
|
5279
|
+
`[RelationshipManager] ${relationshipDescription} orders by '${orderByField}', which is not marked required on ${modelClass.name}. The composite (${orderByField}, id) cursor paginates deterministically, but a row whose '${orderByField}' is actually NULL has no stable cursor position and may drop from the tail. Mark '${orderByField}' as required, or order by a required field such as 'id', to remove the risk. (Full NULL-ordering support is tracked in #1316.)`
|
|
5280
|
+
);
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
4997
5283
|
function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
4998
5284
|
return async function() {
|
|
4999
5285
|
const relatedId = this[config.relatedIdField];
|
|
@@ -5002,6 +5288,16 @@ function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
|
|
|
5002
5288
|
};
|
|
5003
5289
|
}
|
|
5004
5290
|
function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbEngine) {
|
|
5291
|
+
const {
|
|
5292
|
+
relatedIdField,
|
|
5293
|
+
orderByField = "id",
|
|
5294
|
+
orderDirection = "ASC"
|
|
5295
|
+
} = config;
|
|
5296
|
+
warnIfOrderFieldNullable(
|
|
5297
|
+
targetModelClass,
|
|
5298
|
+
orderByField,
|
|
5299
|
+
`hasMany(${targetModelClass.name})`
|
|
5300
|
+
);
|
|
5005
5301
|
return async function(paginationArgs) {
|
|
5006
5302
|
const {
|
|
5007
5303
|
limit,
|
|
@@ -5009,71 +5305,39 @@ function generateHasManyMethod(_sourceModelClass, config, targetModelClass, _dbE
|
|
|
5009
5305
|
beforeCursor,
|
|
5010
5306
|
direction = "forward"
|
|
5011
5307
|
} = paginationArgs || {};
|
|
5012
|
-
const {
|
|
5013
|
-
relatedIdField,
|
|
5014
|
-
orderByField = "id",
|
|
5015
|
-
orderDirection = "ASC"
|
|
5016
|
-
} = config;
|
|
5017
5308
|
const targetSchema = targetModelClass.getSchema();
|
|
5018
5309
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
5019
5310
|
Logger.error(
|
|
5020
5311
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasMany relationship has no valid schema name.`
|
|
5021
5312
|
);
|
|
5022
|
-
return { data: [],
|
|
5313
|
+
return { data: [], hasMore: false };
|
|
5023
5314
|
}
|
|
5024
|
-
const
|
|
5315
|
+
const forward = direction !== "backward";
|
|
5316
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
5025
5317
|
const queryOptions = {
|
|
5026
|
-
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 }
|
|
5318
|
+
sort: { [orderByField]: orderDirection === "ASC" ? 1 : -1 },
|
|
5319
|
+
direction: forward ? 1 : -1
|
|
5027
5320
|
};
|
|
5028
|
-
if (limit !== void 0)
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
};
|
|
5035
|
-
} else if (direction === "backward") {
|
|
5036
|
-
if (beforeCursor) {
|
|
5037
|
-
filter[orderByField] = {
|
|
5038
|
-
[orderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
5039
|
-
};
|
|
5040
|
-
}
|
|
5041
|
-
queryOptions.sort = { [orderByField]: orderDirection === "ASC" ? -1 : 1 };
|
|
5042
|
-
}
|
|
5043
|
-
const result = await targetModelClass.query(filter, queryOptions);
|
|
5044
|
-
const results = result.data;
|
|
5045
|
-
let nextCursor = null;
|
|
5046
|
-
let prevCursor = null;
|
|
5047
|
-
const isFirstPage = direction === "forward" && !afterCursor;
|
|
5048
|
-
if (limit !== void 0) {
|
|
5049
|
-
if (direction === "forward") {
|
|
5050
|
-
if (results.length > limit) {
|
|
5051
|
-
nextCursor = results[limit - 1][orderByField];
|
|
5052
|
-
results.pop();
|
|
5053
|
-
}
|
|
5054
|
-
if (results.length > 0 && !isFirstPage) {
|
|
5055
|
-
prevCursor = results[0][orderByField];
|
|
5056
|
-
}
|
|
5057
|
-
} else {
|
|
5058
|
-
if (results.length > limit) {
|
|
5059
|
-
prevCursor = results[limit - 1][orderByField];
|
|
5060
|
-
results.pop();
|
|
5061
|
-
}
|
|
5062
|
-
results.reverse();
|
|
5063
|
-
if (results.length > 0 && beforeCursor) {
|
|
5064
|
-
nextCursor = results[results.length - 1][orderByField];
|
|
5065
|
-
}
|
|
5066
|
-
if (results.length > 0 && !beforeCursor) {
|
|
5067
|
-
if (results.length === limit) {
|
|
5068
|
-
prevCursor = results[0][orderByField];
|
|
5069
|
-
}
|
|
5070
|
-
}
|
|
5071
|
-
}
|
|
5072
|
-
}
|
|
5073
|
-
return { data: results, nextCursor, prevCursor };
|
|
5321
|
+
if (limit !== void 0) queryOptions.limit = limit;
|
|
5322
|
+
if (cursor != null) queryOptions.uniqueStartKey = cursor;
|
|
5323
|
+
return targetModelClass.query(
|
|
5324
|
+
{ [relatedIdField]: this.id },
|
|
5325
|
+
queryOptions
|
|
5326
|
+
);
|
|
5074
5327
|
};
|
|
5075
5328
|
}
|
|
5076
5329
|
function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClass, joinModelClass, _dbEngine) {
|
|
5330
|
+
const {
|
|
5331
|
+
joinModelLocalField,
|
|
5332
|
+
joinModelRelatedField,
|
|
5333
|
+
joinModelOrderByField = "id",
|
|
5334
|
+
joinModelOrderDirection = "ASC"
|
|
5335
|
+
} = config;
|
|
5336
|
+
warnIfOrderFieldNullable(
|
|
5337
|
+
joinModelClass,
|
|
5338
|
+
joinModelOrderByField,
|
|
5339
|
+
`hasManyThrough(${targetModelClass.name} via ${joinModelClass.name})`
|
|
5340
|
+
);
|
|
5077
5341
|
return async function(paginationArgs) {
|
|
5078
5342
|
const {
|
|
5079
5343
|
limit,
|
|
@@ -5087,87 +5351,42 @@ function generateHasManyThroughMethod(_sourceModelClass, config, targetModelClas
|
|
|
5087
5351
|
Logger.error(
|
|
5088
5352
|
`[RelationshipManager] Join model ${joinModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
5089
5353
|
);
|
|
5090
|
-
return { data: [],
|
|
5354
|
+
return { data: [], hasMore: false };
|
|
5091
5355
|
}
|
|
5092
5356
|
if (!targetSchema || !targetSchema.options || !targetSchema.options.name) {
|
|
5093
5357
|
Logger.error(
|
|
5094
5358
|
`[RelationshipManager] Target model ${targetModelClass.name} for hasManyThrough relationship has no valid schema name.`
|
|
5095
5359
|
);
|
|
5096
|
-
return { data: [],
|
|
5360
|
+
return { data: [], hasMore: false };
|
|
5097
5361
|
}
|
|
5098
|
-
const
|
|
5099
|
-
|
|
5100
|
-
joinModelRelatedField,
|
|
5101
|
-
joinModelOrderByField = "id",
|
|
5102
|
-
joinModelOrderDirection = "ASC"
|
|
5103
|
-
} = config;
|
|
5104
|
-
const joinFilter = { [joinModelLocalField]: this.id };
|
|
5362
|
+
const forward = direction !== "backward";
|
|
5363
|
+
const cursor = forward ? afterCursor : beforeCursor;
|
|
5105
5364
|
const joinQueryOptions = {
|
|
5106
5365
|
sort: {
|
|
5107
5366
|
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? 1 : -1
|
|
5108
5367
|
},
|
|
5109
|
-
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 }
|
|
5368
|
+
projection: { [joinModelRelatedField]: 1, [joinModelOrderByField]: 1 },
|
|
5369
|
+
direction: forward ? 1 : -1
|
|
5110
5370
|
};
|
|
5111
|
-
if (limit !== void 0)
|
|
5112
|
-
|
|
5113
|
-
}
|
|
5114
|
-
if (direction === "forward" && afterCursor) {
|
|
5115
|
-
joinFilter[joinModelOrderByField] = {
|
|
5116
|
-
[joinModelOrderDirection === "ASC" ? "$gt" : "$lt"]: afterCursor
|
|
5117
|
-
};
|
|
5118
|
-
} else if (direction === "backward") {
|
|
5119
|
-
if (beforeCursor) {
|
|
5120
|
-
joinFilter[joinModelOrderByField] = {
|
|
5121
|
-
[joinModelOrderDirection === "ASC" ? "$lt" : "$gt"]: beforeCursor
|
|
5122
|
-
};
|
|
5123
|
-
}
|
|
5124
|
-
joinQueryOptions.sort = {
|
|
5125
|
-
[joinModelOrderByField]: joinModelOrderDirection === "ASC" ? -1 : 1
|
|
5126
|
-
};
|
|
5127
|
-
}
|
|
5371
|
+
if (limit !== void 0) joinQueryOptions.limit = limit;
|
|
5372
|
+
if (cursor != null) joinQueryOptions.uniqueStartKey = cursor;
|
|
5128
5373
|
const joinResult = await joinModelClass.query(
|
|
5129
|
-
|
|
5374
|
+
{ [joinModelLocalField]: this.id },
|
|
5130
5375
|
joinQueryOptions
|
|
5131
5376
|
);
|
|
5132
|
-
const
|
|
5133
|
-
let
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
nextCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
5140
|
-
joinResults.pop();
|
|
5141
|
-
}
|
|
5142
|
-
if (joinResults.length > 0 && !isFirstPage) {
|
|
5143
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
5144
|
-
}
|
|
5145
|
-
} else {
|
|
5146
|
-
if (joinResults.length > limit) {
|
|
5147
|
-
prevCursorFromJoin = joinResults[limit - 1][joinModelOrderByField];
|
|
5148
|
-
joinResults.pop();
|
|
5149
|
-
}
|
|
5150
|
-
joinResults.reverse();
|
|
5151
|
-
if (joinResults.length > 0 && beforeCursor) {
|
|
5152
|
-
nextCursorFromJoin = joinResults[joinResults.length - 1][joinModelOrderByField];
|
|
5153
|
-
}
|
|
5154
|
-
if (joinResults.length > 0 && !beforeCursor) {
|
|
5155
|
-
if (joinResults.length === limit) {
|
|
5156
|
-
prevCursorFromJoin = joinResults[0][joinModelOrderByField];
|
|
5157
|
-
}
|
|
5158
|
-
}
|
|
5159
|
-
}
|
|
5377
|
+
const relatedIds = joinResult.data.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
5378
|
+
let targetData = [];
|
|
5379
|
+
if (relatedIds.length > 0) {
|
|
5380
|
+
const targetResult = await targetModelClass.query({
|
|
5381
|
+
id: { $in: relatedIds }
|
|
5382
|
+
});
|
|
5383
|
+
targetData = targetResult.data;
|
|
5160
5384
|
}
|
|
5161
|
-
const relatedIds = joinResults.map((r) => r[joinModelRelatedField]).filter((id) => id != null && typeof id === "string");
|
|
5162
|
-
if (relatedIds.length === 0)
|
|
5163
|
-
return { data: [], nextCursor: null, prevCursor: null };
|
|
5164
|
-
const targetResult = await targetModelClass.query({
|
|
5165
|
-
id: { $in: relatedIds }
|
|
5166
|
-
});
|
|
5167
5385
|
return {
|
|
5168
|
-
data:
|
|
5169
|
-
nextCursor:
|
|
5170
|
-
prevCursor:
|
|
5386
|
+
data: targetData,
|
|
5387
|
+
nextCursor: joinResult.nextCursor,
|
|
5388
|
+
prevCursor: joinResult.prevCursor,
|
|
5389
|
+
hasMore: joinResult.hasMore
|
|
5171
5390
|
};
|
|
5172
5391
|
};
|
|
5173
5392
|
}
|
|
@@ -5291,10 +5510,12 @@ function generateHasManyThroughRemoveMethod(_sourceModelClass, config, _targetMo
|
|
|
5291
5510
|
}, `remove-join-${config.joinModel}-${this.id}-${targetId}`);
|
|
5292
5511
|
};
|
|
5293
5512
|
}
|
|
5513
|
+
var ALWAYS_PRESENT_ORDER_FIELDS;
|
|
5294
5514
|
var init_relationshipManager = __esm({
|
|
5295
5515
|
"src/models/relationshipManager.ts"() {
|
|
5296
5516
|
"use strict";
|
|
5297
5517
|
init_BaseModel();
|
|
5518
|
+
ALWAYS_PRESENT_ORDER_FIELDS = /* @__PURE__ */ new Set(["id", "type"]);
|
|
5298
5519
|
}
|
|
5299
5520
|
});
|
|
5300
5521
|
|
|
@@ -5764,13 +5985,100 @@ var init_environment = __esm({
|
|
|
5764
5985
|
// src/engines/SqljsEngine.ts
|
|
5765
5986
|
import initSqlJs from "sql.js";
|
|
5766
5987
|
import { Mutex } from "async-mutex";
|
|
5767
|
-
|
|
5988
|
+
async function resolveNodeWasmPath() {
|
|
5989
|
+
if (!isNode()) {
|
|
5990
|
+
return null;
|
|
5991
|
+
}
|
|
5992
|
+
try {
|
|
5993
|
+
const cjsRequire = typeof __require !== "undefined" ? __require : void 0;
|
|
5994
|
+
if (cjsRequire) {
|
|
5995
|
+
return cjsRequire.resolve("sql.js/dist/sql-wasm.wasm");
|
|
5996
|
+
}
|
|
5997
|
+
} catch {
|
|
5998
|
+
}
|
|
5999
|
+
try {
|
|
6000
|
+
const metaUrl = import.meta.url;
|
|
6001
|
+
if (metaUrl) {
|
|
6002
|
+
const { createRequire } = await import(
|
|
6003
|
+
/* @vite-ignore */
|
|
6004
|
+
"module"
|
|
6005
|
+
);
|
|
6006
|
+
const req = createRequire(metaUrl);
|
|
6007
|
+
return req.resolve("sql.js/dist/sql-wasm.wasm");
|
|
6008
|
+
}
|
|
6009
|
+
} catch {
|
|
6010
|
+
}
|
|
6011
|
+
return null;
|
|
6012
|
+
}
|
|
6013
|
+
async function preflightNodeWasm(wasmPath) {
|
|
6014
|
+
if (!isNode()) {
|
|
6015
|
+
return;
|
|
6016
|
+
}
|
|
6017
|
+
if (/^[a-z]+:\/\//i.test(wasmPath)) {
|
|
6018
|
+
return;
|
|
6019
|
+
}
|
|
6020
|
+
const { existsSync } = await import(
|
|
6021
|
+
/* @vite-ignore */
|
|
6022
|
+
"fs"
|
|
6023
|
+
);
|
|
6024
|
+
if (!existsSync(wasmPath)) {
|
|
6025
|
+
throw new Error(
|
|
6026
|
+
`[SqljsEngine] sql.js wasm not found at "${wasmPath}". Pass \`locateFile\`/\`wasmURL\` pointing at sql-wasm.wasm, or install \`better-sqlite3\` and use the \`node-sqlite\` engine (see getRecommendedNodeEngine()).`
|
|
6027
|
+
);
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
async function loadSqlJsModule(locateFile) {
|
|
6031
|
+
let abortReject;
|
|
6032
|
+
const toError = (reason) => reason instanceof Error ? reason : new Error(
|
|
6033
|
+
`[SqljsEngine] sql.js aborted during initialization: ${String(
|
|
6034
|
+
reason
|
|
6035
|
+
)}`
|
|
6036
|
+
);
|
|
6037
|
+
const nodeProcess = isNode() && typeof process !== "undefined" ? process : void 0;
|
|
6038
|
+
const onUnhandled = (reason) => {
|
|
6039
|
+
const err = toError(reason);
|
|
6040
|
+
if (/Aborted/.test(err.message) || /Aborted/.test(String(reason))) {
|
|
6041
|
+
abortReject?.(err);
|
|
6042
|
+
return;
|
|
6043
|
+
}
|
|
6044
|
+
if (nodeProcess && nodeProcess.listenerCount("unhandledRejection") === 1) {
|
|
6045
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6046
|
+
void Promise.reject(reason);
|
|
6047
|
+
}
|
|
6048
|
+
};
|
|
6049
|
+
if (nodeProcess) {
|
|
6050
|
+
nodeProcess.on("unhandledRejection", onUnhandled);
|
|
6051
|
+
}
|
|
6052
|
+
const abortGuard = new Promise((_resolve, reject) => {
|
|
6053
|
+
abortReject = reject;
|
|
6054
|
+
});
|
|
6055
|
+
abortGuard.catch(() => {
|
|
6056
|
+
});
|
|
6057
|
+
try {
|
|
6058
|
+
const initPromise = initSqlJs({
|
|
6059
|
+
locateFile,
|
|
6060
|
+
// Layer 1: emscripten onAbort.
|
|
6061
|
+
onAbort: (reason) => abortReject?.(toError(reason))
|
|
6062
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6063
|
+
});
|
|
6064
|
+
return await Promise.race([initPromise, abortGuard]);
|
|
6065
|
+
} finally {
|
|
6066
|
+
if (nodeProcess) {
|
|
6067
|
+
setTimeout(() => {
|
|
6068
|
+
nodeProcess.removeListener("unhandledRejection", onUnhandled);
|
|
6069
|
+
}, 0);
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
var SQL_WASM_URL, SQL_WASM_FILENAME, TransactionalSQLJSOperations, SqljsEngine;
|
|
5768
6074
|
var init_SqljsEngine = __esm({
|
|
5769
6075
|
"src/engines/SqljsEngine.ts"() {
|
|
5770
6076
|
"use strict";
|
|
5771
6077
|
init_BaseModel();
|
|
5772
6078
|
init_sql();
|
|
6079
|
+
init_environment();
|
|
5773
6080
|
SQL_WASM_URL = "/sql-wasm.wasm";
|
|
6081
|
+
SQL_WASM_FILENAME = "sql-wasm.wasm";
|
|
5774
6082
|
TransactionalSQLJSOperations = class {
|
|
5775
6083
|
db;
|
|
5776
6084
|
engine;
|
|
@@ -5836,22 +6144,37 @@ var init_SqljsEngine = __esm({
|
|
|
5836
6144
|
}
|
|
5837
6145
|
async initialize(engineOptions) {
|
|
5838
6146
|
Logger.info("[SqljsEngine] Initializing SQL.js engine...");
|
|
6147
|
+
const loadingModuleThisCall = !_SqljsEngine.SQL;
|
|
5839
6148
|
try {
|
|
5840
6149
|
if (!_SqljsEngine.SQL) {
|
|
5841
|
-
|
|
6150
|
+
let locateFileConfig;
|
|
6151
|
+
if (engineOptions?.locateFile) {
|
|
6152
|
+
locateFileConfig = engineOptions.locateFile;
|
|
6153
|
+
} else if (engineOptions?.wasmURL) {
|
|
6154
|
+
const url = engineOptions.wasmURL;
|
|
6155
|
+
locateFileConfig = (_file) => url;
|
|
6156
|
+
} else {
|
|
6157
|
+
const nodeWasmPath = await resolveNodeWasmPath();
|
|
6158
|
+
const resolved = nodeWasmPath ?? SQL_WASM_URL;
|
|
6159
|
+
locateFileConfig = (_file) => resolved;
|
|
6160
|
+
}
|
|
6161
|
+
const wasmPath = locateFileConfig(SQL_WASM_FILENAME);
|
|
5842
6162
|
Logger.debug(
|
|
5843
6163
|
"[SqljsEngine] Attempting to load SQL.js WASM from:",
|
|
5844
|
-
|
|
6164
|
+
wasmPath
|
|
5845
6165
|
);
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
6166
|
+
await preflightNodeWasm(wasmPath);
|
|
6167
|
+
const SQL = await loadSqlJsModule(locateFileConfig);
|
|
6168
|
+
_SqljsEngine.SQL = SQL;
|
|
5849
6169
|
Logger.info("[SqljsEngine] SQL.js WASM loaded successfully.");
|
|
5850
6170
|
}
|
|
5851
6171
|
this.db = new _SqljsEngine.SQL.Database();
|
|
5852
6172
|
Logger.info("[SqljsEngine] SQL.js Database initialized in memory.");
|
|
5853
6173
|
this.updateTableNames();
|
|
5854
6174
|
} catch (error) {
|
|
6175
|
+
if (loadingModuleThisCall) {
|
|
6176
|
+
_SqljsEngine.SQL = null;
|
|
6177
|
+
}
|
|
5855
6178
|
Logger.error("[SqljsEngine] Error during SQL.js initialization:", error);
|
|
5856
6179
|
throw error;
|
|
5857
6180
|
}
|
|
@@ -7036,6 +7359,7 @@ init_StringSet();
|
|
|
7036
7359
|
// src/models/schema.ts
|
|
7037
7360
|
init_BaseModel();
|
|
7038
7361
|
init_ModelRegistry();
|
|
7362
|
+
init_uniqueIndex();
|
|
7039
7363
|
function defineModelSchema(input) {
|
|
7040
7364
|
const { name, fields } = input;
|
|
7041
7365
|
const options = {
|
|
@@ -7108,7 +7432,7 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
|
|
|
7108
7432
|
for (const [fieldName, options] of fields.entries()) {
|
|
7109
7433
|
if (options.unique) {
|
|
7110
7434
|
resolved.push({
|
|
7111
|
-
name:
|
|
7435
|
+
name: fieldUniqueConstraintName(modelName, fieldName),
|
|
7112
7436
|
fields: [fieldName]
|
|
7113
7437
|
});
|
|
7114
7438
|
}
|
|
@@ -7436,7 +7760,7 @@ async function loadSchemaFromToml(filePath, options = {}) {
|
|
|
7436
7760
|
|
|
7437
7761
|
// src/utils/yDocSchema.ts
|
|
7438
7762
|
init_yjsValues();
|
|
7439
|
-
import * as
|
|
7763
|
+
import * as Y6 from "yjs";
|
|
7440
7764
|
function discoverSchema(yDoc) {
|
|
7441
7765
|
const models = {};
|
|
7442
7766
|
const metaNames = /* @__PURE__ */ new Set();
|
|
@@ -7470,7 +7794,7 @@ function discoverModelNames(yDoc) {
|
|
|
7470
7794
|
function materializeMap(yDoc, key) {
|
|
7471
7795
|
try {
|
|
7472
7796
|
const map = yDoc.getMap(key);
|
|
7473
|
-
return map instanceof
|
|
7797
|
+
return map instanceof Y6.Map ? map : null;
|
|
7474
7798
|
} catch {
|
|
7475
7799
|
return null;
|
|
7476
7800
|
}
|
|
@@ -7480,11 +7804,11 @@ function readModelMeta(metaMap) {
|
|
|
7480
7804
|
let constraints;
|
|
7481
7805
|
let relationships;
|
|
7482
7806
|
for (const [key, value] of metaMap.entries()) {
|
|
7483
|
-
if (key === "_constraints" && value instanceof
|
|
7807
|
+
if (key === "_constraints" && value instanceof Y6.Map) {
|
|
7484
7808
|
constraints = readConstraints(value);
|
|
7485
|
-
} else if (key === "_relationships" && value instanceof
|
|
7809
|
+
} else if (key === "_relationships" && value instanceof Y6.Map) {
|
|
7486
7810
|
relationships = readRelationships(value);
|
|
7487
|
-
} else if (value instanceof
|
|
7811
|
+
} else if (value instanceof Y6.Map) {
|
|
7488
7812
|
fields[key] = readFieldMeta(value);
|
|
7489
7813
|
}
|
|
7490
7814
|
}
|
|
@@ -7519,7 +7843,7 @@ function inferModelFromData(dataMap) {
|
|
|
7519
7843
|
const fields = {};
|
|
7520
7844
|
let sampled = 0;
|
|
7521
7845
|
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
7522
|
-
if (!(recordValue instanceof
|
|
7846
|
+
if (!(recordValue instanceof Y6.Map)) continue;
|
|
7523
7847
|
if (++sampled > 5) break;
|
|
7524
7848
|
for (const [fieldName, value] of recordValue.entries()) {
|
|
7525
7849
|
if (fieldName.startsWith("_")) continue;
|
|
@@ -7547,7 +7871,7 @@ function inferTypeFromValue(value) {
|
|
|
7547
7871
|
function readConstraints(constraintsMap) {
|
|
7548
7872
|
const out = {};
|
|
7549
7873
|
for (const [name, value] of constraintsMap.entries()) {
|
|
7550
|
-
if (!(value instanceof
|
|
7874
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
7551
7875
|
let fields = [];
|
|
7552
7876
|
const rawFields = value.get("fields");
|
|
7553
7877
|
if (typeof rawFields === "string") {
|
|
@@ -7566,7 +7890,7 @@ function readConstraints(constraintsMap) {
|
|
|
7566
7890
|
function readRelationships(relsMap) {
|
|
7567
7891
|
const out = {};
|
|
7568
7892
|
for (const [name, value] of relsMap.entries()) {
|
|
7569
|
-
if (!(value instanceof
|
|
7893
|
+
if (!(value instanceof Y6.Map)) continue;
|
|
7570
7894
|
const rel = {};
|
|
7571
7895
|
for (const [k, v] of value.entries()) {
|
|
7572
7896
|
rel[k] = v;
|
|
@@ -7640,19 +7964,20 @@ function schemaToToml(schema) {
|
|
|
7640
7964
|
}
|
|
7641
7965
|
|
|
7642
7966
|
// src/index.ts
|
|
7967
|
+
init_uniqueIndex();
|
|
7643
7968
|
init_BaseModel();
|
|
7644
7969
|
|
|
7645
7970
|
// src/utils/yDocDump.ts
|
|
7646
|
-
import * as
|
|
7971
|
+
import * as Y7 from "yjs";
|
|
7647
7972
|
function toPlain(value) {
|
|
7648
|
-
if (value instanceof
|
|
7973
|
+
if (value instanceof Y7.Map) {
|
|
7649
7974
|
const obj = {};
|
|
7650
7975
|
value.forEach((v, k) => {
|
|
7651
7976
|
obj[k] = toPlain(v);
|
|
7652
7977
|
});
|
|
7653
7978
|
return obj;
|
|
7654
7979
|
}
|
|
7655
|
-
if (value instanceof
|
|
7980
|
+
if (value instanceof Y7.Array) {
|
|
7656
7981
|
return value.toArray().map((item) => toPlain(item));
|
|
7657
7982
|
}
|
|
7658
7983
|
return value;
|
|
@@ -7661,7 +7986,7 @@ function dumpYDocToPlain(yDoc, options = {}) {
|
|
|
7661
7986
|
const result = {};
|
|
7662
7987
|
const includeIndexes = !!options.includeIndexes;
|
|
7663
7988
|
yDoc.share.forEach((type, name) => {
|
|
7664
|
-
if (!(type instanceof
|
|
7989
|
+
if (!(type instanceof Y7.Map)) return;
|
|
7665
7990
|
if (!includeIndexes && name.startsWith("_uniqueIdx_")) return;
|
|
7666
7991
|
const records = {};
|
|
7667
7992
|
type.forEach((recordValue, recordId) => {
|
|
@@ -7709,12 +8034,14 @@ export {
|
|
|
7709
8034
|
attachAndRegisterModel,
|
|
7710
8035
|
attachSchemaToClass,
|
|
7711
8036
|
autoRegisterModel,
|
|
8037
|
+
buildUniqueKey,
|
|
7712
8038
|
clearMetaSyncCache,
|
|
7713
8039
|
createModelClass,
|
|
7714
8040
|
defineModelSchema,
|
|
7715
8041
|
discoverModelNames,
|
|
7716
8042
|
discoverSchema,
|
|
7717
8043
|
dumpYDocToPlain,
|
|
8044
|
+
fieldUniqueConstraintName,
|
|
7718
8045
|
generateULID,
|
|
7719
8046
|
inferFieldType,
|
|
7720
8047
|
initJsBao,
|
|
@@ -7725,5 +8052,6 @@ export {
|
|
|
7725
8052
|
schemaToToml,
|
|
7726
8053
|
summarizePlainYDoc,
|
|
7727
8054
|
syncInferredMeta,
|
|
7728
|
-
syncModelMeta
|
|
8055
|
+
syncModelMeta,
|
|
8056
|
+
uniqueIndexMapName
|
|
7729
8057
|
};
|