js-bao 0.4.2 → 0.5.1
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/dist/browser.cjs +206 -41
- package/dist/browser.d.cts +41 -0
- package/dist/browser.d.ts +41 -0
- package/dist/browser.js +203 -39
- package/dist/client.cjs +15 -4
- package/dist/client.d.cts +78 -0
- package/dist/client.d.ts +78 -0
- package/dist/client.js +12 -2
- package/dist/cloudflare-do.cjs +191 -26
- package/dist/cloudflare-do.d.cts +127 -1
- package/dist/cloudflare-do.d.ts +127 -1
- package/dist/cloudflare-do.js +188 -24
- package/dist/cloudflare.cjs +78 -21
- package/dist/cloudflare.d.cts +84 -0
- package/dist/cloudflare.d.ts +84 -0
- package/dist/cloudflare.js +75 -19
- package/dist/codegen-v2.cjs +80 -9
- package/dist/codegen.cjs +6 -1
- package/dist/index.cjs +210 -45
- package/dist/index.d.cts +41 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +207 -43
- package/dist/node.cjs +210 -45
- package/dist/node.d.cts +41 -0
- package/dist/node.d.ts +41 -0
- package/dist/node.js +207 -43
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -31,8 +31,32 @@ interface FieldOptions {
|
|
|
31
31
|
unique?: boolean;
|
|
32
32
|
default?: any | (() => any);
|
|
33
33
|
autoAssign?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Auto-timestamp this field with `Date.now()` (milliseconds) on save.
|
|
36
|
+
*
|
|
37
|
+
* - `'create'` — stamp only on the first save (when `isNew === true`).
|
|
38
|
+
* - `'update'` — stamp on every save (create AND update).
|
|
39
|
+
* - `'both'` — stamp on every save (create AND update).
|
|
40
|
+
*
|
|
41
|
+
* If the caller passes an explicit value for the field in `data`, the
|
|
42
|
+
* explicit value wins and the stamp is skipped. The stamp is applied
|
|
43
|
+
* in `beforeSave` BEFORE any user-defined hooks, so user hooks may
|
|
44
|
+
* still overwrite the value if they wish (last writer wins).
|
|
45
|
+
*/
|
|
46
|
+
autoStamp?: "create" | "update" | "both";
|
|
34
47
|
maxLength?: number;
|
|
35
48
|
maxCount?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Allowed-value set for a `string` field. When present, the codegen
|
|
51
|
+
* generators (database-type codegen + doc-model v2 codegen) emit a
|
|
52
|
+
* TypeScript string-literal union (`"a" | "b" | "c"`) instead of a bare
|
|
53
|
+
* `string` for this field.
|
|
54
|
+
*
|
|
55
|
+
* Advisory / codegen-only: this is a TS-emission hint. The runtime and the
|
|
56
|
+
* server do NOT enforce enum membership on write (see #843). Only valid on
|
|
57
|
+
* `string` fields, and must be a non-empty array of strings.
|
|
58
|
+
*/
|
|
59
|
+
enum?: string[];
|
|
36
60
|
}
|
|
37
61
|
interface UniqueConstraintConfig {
|
|
38
62
|
name: string;
|
|
@@ -495,6 +519,17 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
495
519
|
private ensureLocalChanges;
|
|
496
520
|
private hasLocalChange;
|
|
497
521
|
private getFromYjs;
|
|
522
|
+
/**
|
|
523
|
+
* Defensive normalization for the read/sync boundary (issue #625).
|
|
524
|
+
*
|
|
525
|
+
* Stringset fields are owned by getStringSetFromYjs (which expects the
|
|
526
|
+
* raw Y.Map / legacy plain-object shape — #561 / #601), so they pass
|
|
527
|
+
* through untouched. For any other field, a composite Yjs primitive
|
|
528
|
+
* (Y.Map / Y.Array / Y.Text) must never reach user code or the DB layer
|
|
529
|
+
* raw: normalize it to its plain-JS projection (toJSON / toString) and
|
|
530
|
+
* warn, rather than throwing. Scalars pass through unchanged.
|
|
531
|
+
*/
|
|
532
|
+
private normalizeNonStringSetValue;
|
|
498
533
|
private getValue;
|
|
499
534
|
private setValue;
|
|
500
535
|
get isDirty(): boolean;
|
|
@@ -922,6 +957,7 @@ interface DiscoveredField {
|
|
|
922
957
|
required?: boolean;
|
|
923
958
|
default?: string | number | boolean;
|
|
924
959
|
autoAssign?: boolean;
|
|
960
|
+
autoStamp?: "create" | "update" | "both";
|
|
925
961
|
maxLength?: number;
|
|
926
962
|
maxCount?: number;
|
|
927
963
|
}
|
|
@@ -1010,6 +1046,11 @@ declare function loadSchemaFromTomlString(tomlString: string, options?: LoadSche
|
|
|
1010
1046
|
|
|
1011
1047
|
/**
|
|
1012
1048
|
* Infer a _meta_ type string from a JS runtime value.
|
|
1049
|
+
*
|
|
1050
|
+
* Only an all-`true` Y.Map (the stringset wire shape) is tagged as
|
|
1051
|
+
* `stringset` (issue #625) — a Y.Map carrying a composite payload, or a
|
|
1052
|
+
* Y.Array / Y.Text, is not a stringset and stays untyped (`null`) rather
|
|
1053
|
+
* than being mis-tagged.
|
|
1013
1054
|
*/
|
|
1014
1055
|
declare function inferFieldType(value: unknown): "string" | "number" | "boolean" | "stringset" | null;
|
|
1015
1056
|
/**
|
package/dist/browser.js
CHANGED
|
@@ -1161,10 +1161,46 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
1161
1161
|
}
|
|
1162
1162
|
});
|
|
1163
1163
|
|
|
1164
|
-
// src/
|
|
1164
|
+
// src/utils/yjsValues.ts
|
|
1165
1165
|
import * as Y from "yjs";
|
|
1166
|
+
function isYjsComposite(value) {
|
|
1167
|
+
return value instanceof Y.Map || value instanceof Y.Array || value instanceof Y.Text;
|
|
1168
|
+
}
|
|
1169
|
+
function isStringSetYMap(value) {
|
|
1170
|
+
if (!(value instanceof Y.Map)) return false;
|
|
1171
|
+
for (const v of value.values()) {
|
|
1172
|
+
if (v !== true) return false;
|
|
1173
|
+
}
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
function normalizeYjsValue(value) {
|
|
1177
|
+
if (value instanceof Y.Text) {
|
|
1178
|
+
return value.toString();
|
|
1179
|
+
}
|
|
1180
|
+
if (value instanceof Y.Map || value instanceof Y.Array) {
|
|
1181
|
+
return value.toJSON();
|
|
1182
|
+
}
|
|
1183
|
+
return value;
|
|
1184
|
+
}
|
|
1185
|
+
function normalizeYjsValueForStorage(value) {
|
|
1186
|
+
if (value instanceof Y.Text) {
|
|
1187
|
+
return value.toString();
|
|
1188
|
+
}
|
|
1189
|
+
if (value instanceof Y.Map || value instanceof Y.Array) {
|
|
1190
|
+
return JSON.stringify(value.toJSON());
|
|
1191
|
+
}
|
|
1192
|
+
return value;
|
|
1193
|
+
}
|
|
1194
|
+
var init_yjsValues = __esm({
|
|
1195
|
+
"src/utils/yjsValues.ts"() {
|
|
1196
|
+
"use strict";
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
// src/models/metaSync.ts
|
|
1201
|
+
import * as Y2 from "yjs";
|
|
1166
1202
|
function inferFieldType(value) {
|
|
1167
|
-
if (value
|
|
1203
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
1168
1204
|
switch (typeof value) {
|
|
1169
1205
|
case "string":
|
|
1170
1206
|
return "string";
|
|
@@ -1209,7 +1245,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1209
1245
|
if (compoundConstraints.length > 0) {
|
|
1210
1246
|
let constraints = meta.get("_constraints");
|
|
1211
1247
|
if (!constraints) {
|
|
1212
|
-
constraints = new
|
|
1248
|
+
constraints = new Y2.Map();
|
|
1213
1249
|
meta.set("_constraints", constraints);
|
|
1214
1250
|
}
|
|
1215
1251
|
for (const constraint of compoundConstraints) {
|
|
@@ -1220,7 +1256,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1220
1256
|
if (relationships && Object.keys(relationships).length > 0) {
|
|
1221
1257
|
let rels = meta.get("_relationships");
|
|
1222
1258
|
if (!rels) {
|
|
1223
|
-
rels = new
|
|
1259
|
+
rels = new Y2.Map();
|
|
1224
1260
|
meta.set("_relationships", rels);
|
|
1225
1261
|
}
|
|
1226
1262
|
for (const [relName, relConfig] of Object.entries(relationships)) {
|
|
@@ -1232,7 +1268,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
1232
1268
|
function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
1233
1269
|
let fieldMeta = metaMap.get(fieldName);
|
|
1234
1270
|
if (!fieldMeta) {
|
|
1235
|
-
fieldMeta = new
|
|
1271
|
+
fieldMeta = new Y2.Map();
|
|
1236
1272
|
metaMap.set(fieldName, fieldMeta);
|
|
1237
1273
|
}
|
|
1238
1274
|
setIfChanged(fieldMeta, "type", fieldOpts.type);
|
|
@@ -1240,6 +1276,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1240
1276
|
if (fieldOpts.unique) setIfChanged(fieldMeta, "unique", true);
|
|
1241
1277
|
if (fieldOpts.required) setIfChanged(fieldMeta, "required", true);
|
|
1242
1278
|
if (fieldOpts.autoAssign) setIfChanged(fieldMeta, "autoAssign", true);
|
|
1279
|
+
if (fieldOpts.autoStamp) setIfChanged(fieldMeta, "autoStamp", fieldOpts.autoStamp);
|
|
1243
1280
|
if (fieldOpts.maxLength !== void 0) setIfChanged(fieldMeta, "maxLength", fieldOpts.maxLength);
|
|
1244
1281
|
if (fieldOpts.maxCount !== void 0) setIfChanged(fieldMeta, "maxCount", fieldOpts.maxCount);
|
|
1245
1282
|
const encoded = encodeDefault(fieldOpts.default);
|
|
@@ -1248,7 +1285,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
1248
1285
|
function syncConstraintMeta(constraintsMap, constraint) {
|
|
1249
1286
|
let cMeta = constraintsMap.get(constraint.name);
|
|
1250
1287
|
if (!cMeta) {
|
|
1251
|
-
cMeta = new
|
|
1288
|
+
cMeta = new Y2.Map();
|
|
1252
1289
|
constraintsMap.set(constraint.name, cMeta);
|
|
1253
1290
|
}
|
|
1254
1291
|
setIfChanged(cMeta, "type", "unique");
|
|
@@ -1258,7 +1295,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
|
|
|
1258
1295
|
function syncRelationshipMeta(relsMap, relName, relConfig) {
|
|
1259
1296
|
let relMeta = relsMap.get(relName);
|
|
1260
1297
|
if (!relMeta) {
|
|
1261
|
-
relMeta = new
|
|
1298
|
+
relMeta = new Y2.Map();
|
|
1262
1299
|
relsMap.set(relName, relMeta);
|
|
1263
1300
|
}
|
|
1264
1301
|
for (const [key, value] of Object.entries(relConfig)) {
|
|
@@ -1273,7 +1310,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
|
|
|
1273
1310
|
if (fieldName.startsWith("_")) continue;
|
|
1274
1311
|
let fieldMeta = meta.get(fieldName);
|
|
1275
1312
|
if (!fieldMeta) {
|
|
1276
|
-
fieldMeta = new
|
|
1313
|
+
fieldMeta = new Y2.Map();
|
|
1277
1314
|
meta.set(fieldName, fieldMeta);
|
|
1278
1315
|
}
|
|
1279
1316
|
if (!fieldMeta.has("type")) {
|
|
@@ -1293,6 +1330,7 @@ var KNOWN_FUNCTION_DEFAULTS, _syncedCache;
|
|
|
1293
1330
|
var init_metaSync = __esm({
|
|
1294
1331
|
"src/models/metaSync.ts"() {
|
|
1295
1332
|
"use strict";
|
|
1333
|
+
init_yjsValues();
|
|
1296
1334
|
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
1297
1335
|
_syncedCache = /* @__PURE__ */ new WeakMap();
|
|
1298
1336
|
}
|
|
@@ -2408,7 +2446,7 @@ __export(BaseModel_exports, {
|
|
|
2408
2446
|
UniqueConstraintViolationError: () => UniqueConstraintViolationError,
|
|
2409
2447
|
generateULID: () => generateULID
|
|
2410
2448
|
});
|
|
2411
|
-
import * as
|
|
2449
|
+
import * as Y3 from "yjs";
|
|
2412
2450
|
import { ulid } from "ulid";
|
|
2413
2451
|
function generateULID() {
|
|
2414
2452
|
return ulid();
|
|
@@ -2423,6 +2461,7 @@ var init_BaseModel = __esm({
|
|
|
2423
2461
|
init_CursorManager();
|
|
2424
2462
|
init_sql();
|
|
2425
2463
|
init_metaSync();
|
|
2464
|
+
init_yjsValues();
|
|
2426
2465
|
LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
2427
2466
|
LogLevel2[LogLevel2["SILENT"] = 0] = "SILENT";
|
|
2428
2467
|
LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
|
|
@@ -2931,7 +2970,30 @@ var init_BaseModel = __esm({
|
|
|
2931
2970
|
if (verboseEnabled) {
|
|
2932
2971
|
Logger.verbose(`[getFromYjs] Returning field value: ${fieldValue}`);
|
|
2933
2972
|
}
|
|
2934
|
-
return fieldValue;
|
|
2973
|
+
return this.normalizeNonStringSetValue(fieldKey, fieldValue, schema, modelName);
|
|
2974
|
+
}
|
|
2975
|
+
/**
|
|
2976
|
+
* Defensive normalization for the read/sync boundary (issue #625).
|
|
2977
|
+
*
|
|
2978
|
+
* Stringset fields are owned by getStringSetFromYjs (which expects the
|
|
2979
|
+
* raw Y.Map / legacy plain-object shape — #561 / #601), so they pass
|
|
2980
|
+
* through untouched. For any other field, a composite Yjs primitive
|
|
2981
|
+
* (Y.Map / Y.Array / Y.Text) must never reach user code or the DB layer
|
|
2982
|
+
* raw: normalize it to its plain-JS projection (toJSON / toString) and
|
|
2983
|
+
* warn, rather than throwing. Scalars pass through unchanged.
|
|
2984
|
+
*/
|
|
2985
|
+
normalizeNonStringSetValue(fieldKey, value, schema, modelName) {
|
|
2986
|
+
const fieldOptions = schema?.fields?.get(fieldKey);
|
|
2987
|
+
if (fieldOptions?.type === "stringset") {
|
|
2988
|
+
return value;
|
|
2989
|
+
}
|
|
2990
|
+
if (isYjsComposite(value)) {
|
|
2991
|
+
Logger.warn(
|
|
2992
|
+
`[${modelName ?? this.constructor.name}] Field '${fieldKey}' holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions?.type ?? "unknown"}'. Normalizing to plain JS \u2014 the writer disagreed with the schema.`
|
|
2993
|
+
);
|
|
2994
|
+
return normalizeYjsValue(value);
|
|
2995
|
+
}
|
|
2996
|
+
return value;
|
|
2935
2997
|
}
|
|
2936
2998
|
getValue(fieldKey) {
|
|
2937
2999
|
const verboseEnabled = Logger.getLogLevel() >= 5 /* VERBOSE */;
|
|
@@ -3012,7 +3074,7 @@ var init_BaseModel = __esm({
|
|
|
3012
3074
|
validateBeforeSave() {
|
|
3013
3075
|
const schema = this.constructor.getSchema();
|
|
3014
3076
|
for (const [fieldKey, fieldOptions] of schema.fields.entries()) {
|
|
3015
|
-
const currentValue = this.getValue(fieldKey);
|
|
3077
|
+
const currentValue = fieldKey === "id" ? this.id : this.getValue(fieldKey);
|
|
3016
3078
|
if (fieldOptions.required && (currentValue === null || currentValue === void 0)) {
|
|
3017
3079
|
throw new Error(`Field ${fieldKey} is required before save`);
|
|
3018
3080
|
}
|
|
@@ -3076,7 +3138,7 @@ var init_BaseModel = __esm({
|
|
|
3076
3138
|
}
|
|
3077
3139
|
getStringSetFromYjs(fieldName) {
|
|
3078
3140
|
const yjsData = this.getFromYjs(fieldName);
|
|
3079
|
-
if (yjsData instanceof
|
|
3141
|
+
if (yjsData instanceof Y3.Map) {
|
|
3080
3142
|
return Array.from(yjsData.keys());
|
|
3081
3143
|
}
|
|
3082
3144
|
if (yjsData && typeof yjsData === "object") {
|
|
@@ -3184,7 +3246,7 @@ var init_BaseModel = __esm({
|
|
|
3184
3246
|
}
|
|
3185
3247
|
const extractStringSetValues = (value) => {
|
|
3186
3248
|
if (!value) return [];
|
|
3187
|
-
if (value instanceof
|
|
3249
|
+
if (value instanceof Y3.Map) {
|
|
3188
3250
|
return Array.from(value.entries()).filter(([, isMember]) => Boolean(isMember)).map(([key]) => key);
|
|
3189
3251
|
}
|
|
3190
3252
|
if (Array.isArray(value)) {
|
|
@@ -3201,7 +3263,7 @@ var init_BaseModel = __esm({
|
|
|
3201
3263
|
if (!recordId) continue;
|
|
3202
3264
|
let itemData;
|
|
3203
3265
|
const stringSetValuesByField = {};
|
|
3204
|
-
if (recordData instanceof
|
|
3266
|
+
if (recordData instanceof Y3.Map) {
|
|
3205
3267
|
itemData = {};
|
|
3206
3268
|
const unknownFields = [];
|
|
3207
3269
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -3320,7 +3382,7 @@ var init_BaseModel = __esm({
|
|
|
3320
3382
|
const buildUniqueKey = (recordData, fields) => {
|
|
3321
3383
|
const keyParts = [];
|
|
3322
3384
|
for (const field of fields) {
|
|
3323
|
-
const value = recordData instanceof
|
|
3385
|
+
const value = recordData instanceof Y3.Map ? recordData.get(field) : recordData[field];
|
|
3324
3386
|
if (value === null || value === void 0) {
|
|
3325
3387
|
return null;
|
|
3326
3388
|
}
|
|
@@ -3330,7 +3392,7 @@ var init_BaseModel = __esm({
|
|
|
3330
3392
|
};
|
|
3331
3393
|
const extractItemData = (key, recordData) => {
|
|
3332
3394
|
let itemData;
|
|
3333
|
-
if (recordData instanceof
|
|
3395
|
+
if (recordData instanceof Y3.Map) {
|
|
3334
3396
|
itemData = {};
|
|
3335
3397
|
const unknownFields = [];
|
|
3336
3398
|
for (const [fieldKey, value] of recordData.entries()) {
|
|
@@ -3343,7 +3405,14 @@ var init_BaseModel = __esm({
|
|
|
3343
3405
|
continue;
|
|
3344
3406
|
}
|
|
3345
3407
|
if (value !== void 0) {
|
|
3346
|
-
|
|
3408
|
+
if (isYjsComposite(value)) {
|
|
3409
|
+
Logger.warn(
|
|
3410
|
+
`[${this.name}] Field '${fieldKey}' on record ${key} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
3411
|
+
);
|
|
3412
|
+
itemData[fieldKey] = normalizeYjsValueForStorage(value);
|
|
3413
|
+
} else {
|
|
3414
|
+
itemData[fieldKey] = value;
|
|
3415
|
+
}
|
|
3347
3416
|
}
|
|
3348
3417
|
}
|
|
3349
3418
|
if (unknownFields.length > 0) {
|
|
@@ -3366,7 +3435,14 @@ var init_BaseModel = __esm({
|
|
|
3366
3435
|
continue;
|
|
3367
3436
|
}
|
|
3368
3437
|
if (value !== void 0) {
|
|
3369
|
-
|
|
3438
|
+
if (isYjsComposite(value)) {
|
|
3439
|
+
Logger.warn(
|
|
3440
|
+
`[${this.name}] Field '${fieldKey}' on legacy record ${key} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
3441
|
+
);
|
|
3442
|
+
filteredData[fieldKey] = normalizeYjsValueForStorage(value);
|
|
3443
|
+
} else {
|
|
3444
|
+
filteredData[fieldKey] = value;
|
|
3445
|
+
}
|
|
3370
3446
|
}
|
|
3371
3447
|
}
|
|
3372
3448
|
if (unknownFields.length > 0) {
|
|
@@ -3446,7 +3522,7 @@ var init_BaseModel = __esm({
|
|
|
3446
3522
|
if (!recordData || !key) continue;
|
|
3447
3523
|
const itemData = extractItemData(key, recordData);
|
|
3448
3524
|
if (!itemData) continue;
|
|
3449
|
-
if (change.action === "add" && recordData instanceof
|
|
3525
|
+
if (change.action === "add" && recordData instanceof Y3.Map) {
|
|
3450
3526
|
Logger.verbose(
|
|
3451
3527
|
`[${this.name}] Setting up observer on newly added nested YMap for record ${key} in document ${docId}`
|
|
3452
3528
|
);
|
|
@@ -3591,7 +3667,7 @@ var init_BaseModel = __esm({
|
|
|
3591
3667
|
`[${this.name}] Setting up observers on existing nested YMaps for ${modelName}/${docId}...`
|
|
3592
3668
|
);
|
|
3593
3669
|
for (const [recordId, recordData] of documentYMap.entries()) {
|
|
3594
|
-
if (recordData instanceof
|
|
3670
|
+
if (recordData instanceof Y3.Map) {
|
|
3595
3671
|
Logger.verbose(
|
|
3596
3672
|
`[${this.name}] Setting up observer on existing nested YMap for record ${recordId} in document ${docId}`
|
|
3597
3673
|
);
|
|
@@ -3776,6 +3852,11 @@ var init_BaseModel = __esm({
|
|
|
3776
3852
|
Logger.debug(
|
|
3777
3853
|
`[_diffWithYjsData] Field '${key}' - Y.js value: ${yjsValue}, local value: ${localValue}`
|
|
3778
3854
|
);
|
|
3855
|
+
if (isYjsComposite(yjsValue)) {
|
|
3856
|
+
Logger.warn(
|
|
3857
|
+
`[${modelConstructor.name}] Field '${key}' holds a composite Yjs value (${yjsValue?.constructor?.name}) in a non-stringset slot; the local scalar change will overwrite it (last-writer-wins).`
|
|
3858
|
+
);
|
|
3859
|
+
}
|
|
3779
3860
|
if (yjsValue === void 0) {
|
|
3780
3861
|
Logger.debug(
|
|
3781
3862
|
`[_diffWithYjsData] Field '${key}' not in Y.js, adding to 'added'`
|
|
@@ -3813,8 +3894,8 @@ var init_BaseModel = __esm({
|
|
|
3813
3894
|
*/
|
|
3814
3895
|
applyStringSetChangeToYMap(recordYMap, fieldName, change) {
|
|
3815
3896
|
let nested = recordYMap.get(fieldName);
|
|
3816
|
-
if (!(nested instanceof
|
|
3817
|
-
const migrated = new
|
|
3897
|
+
if (!(nested instanceof Y3.Map)) {
|
|
3898
|
+
const migrated = new Y3.Map();
|
|
3818
3899
|
if (nested && typeof nested === "object") {
|
|
3819
3900
|
for (const [member, marker] of Object.entries(
|
|
3820
3901
|
nested
|
|
@@ -4001,6 +4082,29 @@ var init_BaseModel = __esm({
|
|
|
4001
4082
|
);
|
|
4002
4083
|
throw new Error("Cannot save item without an id. Ensure id is set.");
|
|
4003
4084
|
}
|
|
4085
|
+
const preTransactCreateStamps = /* @__PURE__ */ new Set();
|
|
4086
|
+
{
|
|
4087
|
+
const existingRecord = targetYMap.get(this.id);
|
|
4088
|
+
const isNew = !existingRecord;
|
|
4089
|
+
for (const [fieldKey, fieldOptions] of schema.fields.entries()) {
|
|
4090
|
+
const stamp = fieldOptions.autoStamp;
|
|
4091
|
+
if (!stamp) continue;
|
|
4092
|
+
const callerExplicit = this.hasLocalChange(fieldKey);
|
|
4093
|
+
if (callerExplicit) {
|
|
4094
|
+
const localValue = this._localChanges[fieldKey];
|
|
4095
|
+
if (localValue !== null && localValue !== void 0) continue;
|
|
4096
|
+
}
|
|
4097
|
+
if (stamp === "create") {
|
|
4098
|
+
if (!isNew) continue;
|
|
4099
|
+
const persisted = fieldKey === "id" ? this.id : this.getValue(fieldKey);
|
|
4100
|
+
if (persisted !== null && persisted !== void 0) continue;
|
|
4101
|
+
}
|
|
4102
|
+
this[fieldKey] = Date.now();
|
|
4103
|
+
if (stamp === "create") {
|
|
4104
|
+
preTransactCreateStamps.add(fieldKey);
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4004
4108
|
this.validateBeforeSave();
|
|
4005
4109
|
Logger.debug(`[${modelName}.save] About to get current JS state`);
|
|
4006
4110
|
Logger.debug(`[${modelName}.save] _localChanges:`, this._localChanges);
|
|
@@ -4031,6 +4135,14 @@ var init_BaseModel = __esm({
|
|
|
4031
4135
|
}
|
|
4032
4136
|
}
|
|
4033
4137
|
}
|
|
4138
|
+
for (const fieldKey of preTransactCreateStamps) {
|
|
4139
|
+
if (this._localChanges && fieldKey in this._localChanges) {
|
|
4140
|
+
delete this._localChanges[fieldKey];
|
|
4141
|
+
}
|
|
4142
|
+
if (fieldKey in dataToSave) {
|
|
4143
|
+
delete dataToSave[fieldKey];
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4034
4146
|
}
|
|
4035
4147
|
}
|
|
4036
4148
|
if (!targetYMap) {
|
|
@@ -4043,7 +4155,7 @@ var init_BaseModel = __esm({
|
|
|
4043
4155
|
Logger.debug(`[${modelName}.save] isUpdate: ${isUpdate}`);
|
|
4044
4156
|
Logger.debug(`[${modelName}.save] recordYMap exists: ${!!recordYMap}`);
|
|
4045
4157
|
if (!recordYMap) {
|
|
4046
|
-
recordYMap = new
|
|
4158
|
+
recordYMap = new Y3.Map();
|
|
4047
4159
|
Logger.verbose(
|
|
4048
4160
|
`[${modelName}] Creating new nested YMap for record ${this.id} in document ${targetDocId || "legacy"}`
|
|
4049
4161
|
);
|
|
@@ -5070,7 +5182,7 @@ var init_BaseModel = __esm({
|
|
|
5070
5182
|
);
|
|
5071
5183
|
}
|
|
5072
5184
|
for (const [recordId, recordYMap] of documentYMap.entries()) {
|
|
5073
|
-
if (recordYMap instanceof
|
|
5185
|
+
if (recordYMap instanceof Y3.Map) {
|
|
5074
5186
|
const instance = new this({ id: recordId });
|
|
5075
5187
|
instance._metaDocId = docId;
|
|
5076
5188
|
const connectedDoc = modelConstructor.connectedDocuments.get(docId);
|
|
@@ -5141,7 +5253,7 @@ var init_BaseModel = __esm({
|
|
|
5141
5253
|
const documentYMap = modelConstructor.documentYMaps.get(documentYMapKey);
|
|
5142
5254
|
if (documentYMap) {
|
|
5143
5255
|
const recordYMap2 = documentYMap.get(recordId2);
|
|
5144
|
-
if (recordYMap2 && recordYMap2 instanceof
|
|
5256
|
+
if (recordYMap2 && recordYMap2 instanceof Y3.Map) {
|
|
5145
5257
|
const instance = new modelConstructor({
|
|
5146
5258
|
id: recordId2
|
|
5147
5259
|
});
|
|
@@ -5178,7 +5290,7 @@ var init_BaseModel = __esm({
|
|
|
5178
5290
|
if (!recordYMap) {
|
|
5179
5291
|
return null;
|
|
5180
5292
|
}
|
|
5181
|
-
if (recordYMap instanceof
|
|
5293
|
+
if (recordYMap instanceof Y3.Map) {
|
|
5182
5294
|
const instance = new modelConstructor({
|
|
5183
5295
|
id: recordId
|
|
5184
5296
|
});
|
|
@@ -5410,7 +5522,7 @@ var init_BaseModel = __esm({
|
|
|
5410
5522
|
for (const [fieldKey, fieldOptions] of schema.fields) {
|
|
5411
5523
|
if (fieldOptions?.type !== "stringset") continue;
|
|
5412
5524
|
const value = recordYMap.get(fieldKey);
|
|
5413
|
-
if (value instanceof
|
|
5525
|
+
if (value instanceof Y3.Map) {
|
|
5414
5526
|
this.observeStringSetMapOnce(value);
|
|
5415
5527
|
}
|
|
5416
5528
|
}
|
|
@@ -5457,7 +5569,14 @@ var init_BaseModel = __esm({
|
|
|
5457
5569
|
continue;
|
|
5458
5570
|
}
|
|
5459
5571
|
if (value !== void 0) {
|
|
5460
|
-
|
|
5572
|
+
if (isYjsComposite(value)) {
|
|
5573
|
+
Logger.warn(
|
|
5574
|
+
`[${modelName}] Field '${key}' on record ${recordId} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
5575
|
+
);
|
|
5576
|
+
updatedData[key] = normalizeYjsValueForStorage(value);
|
|
5577
|
+
} else {
|
|
5578
|
+
updatedData[key] = value;
|
|
5579
|
+
}
|
|
5461
5580
|
}
|
|
5462
5581
|
}
|
|
5463
5582
|
if (unknownFields.length > 0) {
|
|
@@ -5537,7 +5656,14 @@ var init_BaseModel = __esm({
|
|
|
5537
5656
|
continue;
|
|
5538
5657
|
}
|
|
5539
5658
|
if (value !== void 0) {
|
|
5540
|
-
|
|
5659
|
+
if (isYjsComposite(value)) {
|
|
5660
|
+
Logger.warn(
|
|
5661
|
+
`[${modelName}] Field '${key}' on record ${recordId} in document ${docId} holds a composite Yjs value (${value?.constructor?.name}) but its declared type is '${fieldOptions.type}'. Normalizing to plain JS before persisting to SQLite.`
|
|
5662
|
+
);
|
|
5663
|
+
updatedData[key] = normalizeYjsValueForStorage(value);
|
|
5664
|
+
} else {
|
|
5665
|
+
updatedData[key] = value;
|
|
5666
|
+
}
|
|
5541
5667
|
}
|
|
5542
5668
|
}
|
|
5543
5669
|
if (unknownFields.length > 0) {
|
|
@@ -6489,7 +6615,8 @@ function attachAndRegisterModel(modelClass, schema) {
|
|
|
6489
6615
|
}
|
|
6490
6616
|
|
|
6491
6617
|
// src/utils/yDocSchema.ts
|
|
6492
|
-
|
|
6618
|
+
init_yjsValues();
|
|
6619
|
+
import * as Y4 from "yjs";
|
|
6493
6620
|
function discoverSchema(yDoc) {
|
|
6494
6621
|
const models = {};
|
|
6495
6622
|
const metaNames = /* @__PURE__ */ new Set();
|
|
@@ -6523,7 +6650,7 @@ function discoverModelNames(yDoc) {
|
|
|
6523
6650
|
function materializeMap(yDoc, key) {
|
|
6524
6651
|
try {
|
|
6525
6652
|
const map = yDoc.getMap(key);
|
|
6526
|
-
return map instanceof
|
|
6653
|
+
return map instanceof Y4.Map ? map : null;
|
|
6527
6654
|
} catch {
|
|
6528
6655
|
return null;
|
|
6529
6656
|
}
|
|
@@ -6533,11 +6660,11 @@ function readModelMeta(metaMap) {
|
|
|
6533
6660
|
let constraints;
|
|
6534
6661
|
let relationships;
|
|
6535
6662
|
for (const [key, value] of metaMap.entries()) {
|
|
6536
|
-
if (key === "_constraints" && value instanceof
|
|
6663
|
+
if (key === "_constraints" && value instanceof Y4.Map) {
|
|
6537
6664
|
constraints = readConstraints(value);
|
|
6538
|
-
} else if (key === "_relationships" && value instanceof
|
|
6665
|
+
} else if (key === "_relationships" && value instanceof Y4.Map) {
|
|
6539
6666
|
relationships = readRelationships(value);
|
|
6540
|
-
} else if (value instanceof
|
|
6667
|
+
} else if (value instanceof Y4.Map) {
|
|
6541
6668
|
fields[key] = readFieldMeta(value);
|
|
6542
6669
|
}
|
|
6543
6670
|
}
|
|
@@ -6556,6 +6683,10 @@ function readFieldMeta(fieldMap) {
|
|
|
6556
6683
|
if (fieldMap.get("unique") === true) field.unique = true;
|
|
6557
6684
|
if (fieldMap.get("required") === true) field.required = true;
|
|
6558
6685
|
if (fieldMap.get("autoAssign") === true) field.autoAssign = true;
|
|
6686
|
+
const autoStamp = fieldMap.get("autoStamp");
|
|
6687
|
+
if (typeof autoStamp === "string" && (autoStamp === "create" || autoStamp === "update" || autoStamp === "both")) {
|
|
6688
|
+
field.autoStamp = autoStamp;
|
|
6689
|
+
}
|
|
6559
6690
|
const def = fieldMap.get("default");
|
|
6560
6691
|
if (def !== void 0) field.default = def;
|
|
6561
6692
|
const maxLength = fieldMap.get("maxLength");
|
|
@@ -6568,7 +6699,7 @@ function inferModelFromData(dataMap) {
|
|
|
6568
6699
|
const fields = {};
|
|
6569
6700
|
let sampled = 0;
|
|
6570
6701
|
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
6571
|
-
if (!(recordValue instanceof
|
|
6702
|
+
if (!(recordValue instanceof Y4.Map)) continue;
|
|
6572
6703
|
if (++sampled > 5) break;
|
|
6573
6704
|
for (const [fieldName, value] of recordValue.entries()) {
|
|
6574
6705
|
if (fieldName.startsWith("_")) continue;
|
|
@@ -6581,7 +6712,7 @@ function inferModelFromData(dataMap) {
|
|
|
6581
6712
|
return { fields };
|
|
6582
6713
|
}
|
|
6583
6714
|
function inferTypeFromValue(value) {
|
|
6584
|
-
if (value
|
|
6715
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
6585
6716
|
switch (typeof value) {
|
|
6586
6717
|
case "string":
|
|
6587
6718
|
return "string";
|
|
@@ -6596,7 +6727,7 @@ function inferTypeFromValue(value) {
|
|
|
6596
6727
|
function readConstraints(constraintsMap) {
|
|
6597
6728
|
const out = {};
|
|
6598
6729
|
for (const [name, value] of constraintsMap.entries()) {
|
|
6599
|
-
if (!(value instanceof
|
|
6730
|
+
if (!(value instanceof Y4.Map)) continue;
|
|
6600
6731
|
let fields = [];
|
|
6601
6732
|
const rawFields = value.get("fields");
|
|
6602
6733
|
if (typeof rawFields === "string") {
|
|
@@ -6615,7 +6746,7 @@ function readConstraints(constraintsMap) {
|
|
|
6615
6746
|
function readRelationships(relsMap) {
|
|
6616
6747
|
const out = {};
|
|
6617
6748
|
for (const [name, value] of relsMap.entries()) {
|
|
6618
|
-
if (!(value instanceof
|
|
6749
|
+
if (!(value instanceof Y4.Map)) continue;
|
|
6619
6750
|
const rel = {};
|
|
6620
6751
|
for (const [k, v] of value.entries()) {
|
|
6621
6752
|
rel[k] = v;
|
|
@@ -6626,6 +6757,7 @@ function readRelationships(relsMap) {
|
|
|
6626
6757
|
}
|
|
6627
6758
|
var CAMEL_TO_SNAKE = {
|
|
6628
6759
|
autoAssign: "auto_assign",
|
|
6760
|
+
autoStamp: "auto_stamp",
|
|
6629
6761
|
maxLength: "max_length",
|
|
6630
6762
|
maxCount: "max_count",
|
|
6631
6763
|
relatedIdField: "related_id_field",
|
|
@@ -6656,6 +6788,7 @@ function schemaToToml(schema) {
|
|
|
6656
6788
|
lines.push(`[models.${modelName}.fields.${fieldName}]`);
|
|
6657
6789
|
lines.push(`type = ${tomlValue(field.type)}`);
|
|
6658
6790
|
if (field.autoAssign) lines.push("auto_assign = true");
|
|
6791
|
+
if (field.autoStamp) lines.push(`auto_stamp = ${tomlValue(field.autoStamp)}`);
|
|
6659
6792
|
if (field.indexed) lines.push("indexed = true");
|
|
6660
6793
|
if (field.unique) lines.push("unique = true");
|
|
6661
6794
|
if (field.required) lines.push("required = true");
|
|
@@ -6702,10 +6835,13 @@ var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
|
6702
6835
|
"unique",
|
|
6703
6836
|
"required",
|
|
6704
6837
|
"auto_assign",
|
|
6838
|
+
"auto_stamp",
|
|
6705
6839
|
"max_length",
|
|
6706
6840
|
"max_count",
|
|
6707
|
-
"default"
|
|
6841
|
+
"default",
|
|
6842
|
+
"enum"
|
|
6708
6843
|
]);
|
|
6844
|
+
var VALID_AUTO_STAMP_VALUES = /* @__PURE__ */ new Set(["create", "update", "both"]);
|
|
6709
6845
|
var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
|
|
6710
6846
|
"fields",
|
|
6711
6847
|
"relationships",
|
|
@@ -6747,9 +6883,37 @@ function parseFieldOptions(raw, context, strict) {
|
|
|
6747
6883
|
if (raw.unique === true) opts.unique = true;
|
|
6748
6884
|
if (raw.required === true) opts.required = true;
|
|
6749
6885
|
if (raw.auto_assign === true) opts.autoAssign = true;
|
|
6886
|
+
if (raw.auto_stamp !== void 0) {
|
|
6887
|
+
if (typeof raw.auto_stamp !== "string" || !VALID_AUTO_STAMP_VALUES.has(raw.auto_stamp)) {
|
|
6888
|
+
throw new Error(
|
|
6889
|
+
`${context}: invalid auto_stamp value "${raw.auto_stamp}". Must be one of: ${[
|
|
6890
|
+
...VALID_AUTO_STAMP_VALUES
|
|
6891
|
+
].join(", ")}`
|
|
6892
|
+
);
|
|
6893
|
+
}
|
|
6894
|
+
opts.autoStamp = raw.auto_stamp;
|
|
6895
|
+
}
|
|
6750
6896
|
if (raw.max_length !== void 0) opts.maxLength = raw.max_length;
|
|
6751
6897
|
if (raw.max_count !== void 0) opts.maxCount = raw.max_count;
|
|
6752
6898
|
if (raw.default !== void 0) opts.default = raw.default;
|
|
6899
|
+
if (raw.enum !== void 0) {
|
|
6900
|
+
if (raw.type !== "string") {
|
|
6901
|
+
throw new Error(
|
|
6902
|
+
`${context}: \`enum\` is only valid on a "string" field, not "${raw.type}".`
|
|
6903
|
+
);
|
|
6904
|
+
}
|
|
6905
|
+
if (!Array.isArray(raw.enum) || raw.enum.length === 0) {
|
|
6906
|
+
throw new Error(
|
|
6907
|
+
`${context}: \`enum\` must be a non-empty array of strings.`
|
|
6908
|
+
);
|
|
6909
|
+
}
|
|
6910
|
+
if (!raw.enum.every((v) => typeof v === "string")) {
|
|
6911
|
+
throw new Error(
|
|
6912
|
+
`${context}: \`enum\` values must all be strings.`
|
|
6913
|
+
);
|
|
6914
|
+
}
|
|
6915
|
+
opts.enum = [...raw.enum];
|
|
6916
|
+
}
|
|
6753
6917
|
return opts;
|
|
6754
6918
|
}
|
|
6755
6919
|
function requireField(raw, field, context) {
|
package/dist/client.cjs
CHANGED
|
@@ -84,15 +84,25 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
// src/utils/yjsValues.ts
|
|
88
|
+
var Y;
|
|
89
|
+
var init_yjsValues = __esm({
|
|
90
|
+
"src/utils/yjsValues.ts"() {
|
|
91
|
+
"use strict";
|
|
92
|
+
Y = __toESM(require("yjs"), 1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
87
96
|
// src/models/metaSync.ts
|
|
88
97
|
function registerFunctionDefault(fn, name) {
|
|
89
98
|
KNOWN_FUNCTION_DEFAULTS.set(fn, name);
|
|
90
99
|
}
|
|
91
|
-
var
|
|
100
|
+
var Y2, KNOWN_FUNCTION_DEFAULTS;
|
|
92
101
|
var init_metaSync = __esm({
|
|
93
102
|
"src/models/metaSync.ts"() {
|
|
94
103
|
"use strict";
|
|
95
|
-
|
|
104
|
+
Y2 = __toESM(require("yjs"), 1);
|
|
105
|
+
init_yjsValues();
|
|
96
106
|
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
97
107
|
}
|
|
98
108
|
});
|
|
@@ -101,11 +111,11 @@ var init_metaSync = __esm({
|
|
|
101
111
|
function generateULID() {
|
|
102
112
|
return (0, import_ulid.ulid)();
|
|
103
113
|
}
|
|
104
|
-
var
|
|
114
|
+
var Y3, import_ulid, Logger;
|
|
105
115
|
var init_BaseModel = __esm({
|
|
106
116
|
"src/models/BaseModel.ts"() {
|
|
107
117
|
"use strict";
|
|
108
|
-
|
|
118
|
+
Y3 = __toESM(require("yjs"), 1);
|
|
109
119
|
import_ulid = require("ulid");
|
|
110
120
|
init_StringSet();
|
|
111
121
|
init_documentTypes();
|
|
@@ -113,6 +123,7 @@ var init_BaseModel = __esm({
|
|
|
113
123
|
init_CursorManager();
|
|
114
124
|
init_sql();
|
|
115
125
|
init_metaSync();
|
|
126
|
+
init_yjsValues();
|
|
116
127
|
Logger = class {
|
|
117
128
|
static _logLevel = 1 /* ERROR */;
|
|
118
129
|
static _logCallback = null;
|