js-bao 0.3.0-alpha.5 → 0.3.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.
Files changed (66) hide show
  1. package/dist/browser.cjs +554 -17
  2. package/dist/browser.d.cts +129 -1
  3. package/dist/browser.d.ts +129 -1
  4. package/dist/browser.js +553 -16
  5. package/dist/client.cjs +36 -13
  6. package/dist/client.d.cts +10 -3
  7. package/dist/client.d.ts +10 -3
  8. package/dist/client.js +35 -12
  9. package/dist/cloudflare-do.cjs +1144 -324
  10. package/dist/cloudflare-do.d.cts +551 -17
  11. package/dist/cloudflare-do.d.ts +551 -17
  12. package/dist/cloudflare-do.js +1135 -324
  13. package/dist/cloudflare.cjs +592 -19
  14. package/dist/cloudflare.d.cts +154 -4
  15. package/dist/cloudflare.d.ts +154 -4
  16. package/dist/cloudflare.js +591 -18
  17. package/dist/codegen.cjs +11 -10
  18. package/dist/index.cjs +585 -26
  19. package/dist/index.d.cts +134 -1
  20. package/dist/index.d.ts +134 -1
  21. package/dist/index.js +573 -24
  22. package/dist/node.cjs +639 -22
  23. package/dist/node.d.cts +170 -1
  24. package/dist/node.d.ts +170 -1
  25. package/dist/node.js +625 -20
  26. package/package.json +11 -10
  27. package/dist/BaseModel-5YQCROYE.js +0 -17
  28. package/dist/BaseModel-5YQCROYE.js.map +0 -1
  29. package/dist/BaseModel-FCNWDJBH.js +0 -17
  30. package/dist/BaseModel-FCNWDJBH.js.map +0 -1
  31. package/dist/BrowserDatabaseFactory-PXOTK2DQ.js +0 -119
  32. package/dist/BrowserDatabaseFactory-PXOTK2DQ.js.map +0 -1
  33. package/dist/BrowserDatabaseFactory-WD4VX2VZ.js +0 -119
  34. package/dist/BrowserDatabaseFactory-WD4VX2VZ.js.map +0 -1
  35. package/dist/IncludeResolver-RCKQGNPZ.js +0 -385
  36. package/dist/IncludeResolver-RCKQGNPZ.js.map +0 -1
  37. package/dist/IncludeResolver-WGSQDMS7.js +0 -385
  38. package/dist/IncludeResolver-WGSQDMS7.js.map +0 -1
  39. package/dist/NodeDatabaseFactory-J4Z36UF3.js +0 -165
  40. package/dist/NodeDatabaseFactory-J4Z36UF3.js.map +0 -1
  41. package/dist/NodeDatabaseFactory-QIEKAXBM.js +0 -10
  42. package/dist/NodeDatabaseFactory-QIEKAXBM.js.map +0 -1
  43. package/dist/NodeSqliteEngine-HJSAYE4E.js +0 -383
  44. package/dist/NodeSqliteEngine-HJSAYE4E.js.map +0 -1
  45. package/dist/NodeSqliteEngine-I5SLWLME.js +0 -383
  46. package/dist/NodeSqliteEngine-I5SLWLME.js.map +0 -1
  47. package/dist/chunk-3PZWHUZO.js +0 -4153
  48. package/dist/chunk-3PZWHUZO.js.map +0 -1
  49. package/dist/chunk-53MS4MN7.js +0 -373
  50. package/dist/chunk-53MS4MN7.js.map +0 -1
  51. package/dist/chunk-65G2P4GL.js +0 -709
  52. package/dist/chunk-65G2P4GL.js.map +0 -1
  53. package/dist/chunk-6UX3YSCW.js +0 -4151
  54. package/dist/chunk-6UX3YSCW.js.map +0 -1
  55. package/dist/chunk-DANSD6BE.js +0 -709
  56. package/dist/chunk-DANSD6BE.js.map +0 -1
  57. package/dist/chunk-DF3JEQXA.js +0 -373
  58. package/dist/chunk-DF3JEQXA.js.map +0 -1
  59. package/dist/chunk-GO3APTPX.js +0 -61
  60. package/dist/chunk-GO3APTPX.js.map +0 -1
  61. package/dist/chunk-ID4U6IQC.js +0 -53
  62. package/dist/chunk-ID4U6IQC.js.map +0 -1
  63. package/dist/chunk-RQVS3LVL.js +0 -165
  64. package/dist/chunk-RQVS3LVL.js.map +0 -1
  65. package/dist/environment-TOTQICSE.js +0 -17
  66. package/dist/environment-TOTQICSE.js.map +0 -1
package/dist/browser.cjs CHANGED
@@ -1183,6 +1183,143 @@ var init_DocumentQueryTranslator = __esm({
1183
1183
  }
1184
1184
  });
1185
1185
 
1186
+ // src/models/metaSync.ts
1187
+ function inferFieldType(value) {
1188
+ if (value instanceof Y.Map) return "stringset";
1189
+ switch (typeof value) {
1190
+ case "string":
1191
+ return "string";
1192
+ case "number":
1193
+ return "number";
1194
+ case "boolean":
1195
+ return "boolean";
1196
+ default:
1197
+ return null;
1198
+ }
1199
+ }
1200
+ function registerFunctionDefault(fn, name) {
1201
+ KNOWN_FUNCTION_DEFAULTS.set(fn, name);
1202
+ }
1203
+ function encodeDefault(value) {
1204
+ if (value === void 0 || value === null) return void 0;
1205
+ if (typeof value === "function") {
1206
+ const name = KNOWN_FUNCTION_DEFAULTS.get(value);
1207
+ return name ? `$${name}` : "$unknown_function";
1208
+ }
1209
+ return value;
1210
+ }
1211
+ function clearMetaSyncCache(yDoc) {
1212
+ if (yDoc) {
1213
+ _syncedCache.delete(yDoc);
1214
+ }
1215
+ }
1216
+ function syncModelMeta(yDoc, modelName, schema) {
1217
+ let synced = _syncedCache.get(yDoc);
1218
+ if (synced?.has(modelName)) return;
1219
+ if (!synced) {
1220
+ synced = /* @__PURE__ */ new Set();
1221
+ _syncedCache.set(yDoc, synced);
1222
+ }
1223
+ const meta = yDoc.getMap(`_meta_${modelName}`);
1224
+ for (const [fieldName, fieldOpts] of schema.fields.entries()) {
1225
+ syncFieldMeta(meta, fieldName, fieldOpts);
1226
+ }
1227
+ const compoundConstraints = schema.resolvedUniqueConstraints.filter(
1228
+ (c) => c.fields.length > 1
1229
+ );
1230
+ if (compoundConstraints.length > 0) {
1231
+ let constraints = meta.get("_constraints");
1232
+ if (!constraints) {
1233
+ constraints = new Y.Map();
1234
+ meta.set("_constraints", constraints);
1235
+ }
1236
+ for (const constraint of compoundConstraints) {
1237
+ syncConstraintMeta(constraints, constraint);
1238
+ }
1239
+ }
1240
+ const relationships = schema.options.relationships;
1241
+ if (relationships && Object.keys(relationships).length > 0) {
1242
+ let rels = meta.get("_relationships");
1243
+ if (!rels) {
1244
+ rels = new Y.Map();
1245
+ meta.set("_relationships", rels);
1246
+ }
1247
+ for (const [relName, relConfig] of Object.entries(relationships)) {
1248
+ syncRelationshipMeta(rels, relName, relConfig);
1249
+ }
1250
+ }
1251
+ synced.add(modelName);
1252
+ }
1253
+ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
1254
+ let fieldMeta = metaMap.get(fieldName);
1255
+ if (!fieldMeta) {
1256
+ fieldMeta = new Y.Map();
1257
+ metaMap.set(fieldName, fieldMeta);
1258
+ }
1259
+ setIfChanged(fieldMeta, "type", fieldOpts.type);
1260
+ if (fieldOpts.indexed) setIfChanged(fieldMeta, "indexed", true);
1261
+ if (fieldOpts.unique) setIfChanged(fieldMeta, "unique", true);
1262
+ if (fieldOpts.required) setIfChanged(fieldMeta, "required", true);
1263
+ if (fieldOpts.autoAssign) setIfChanged(fieldMeta, "autoAssign", true);
1264
+ if (fieldOpts.maxLength !== void 0) setIfChanged(fieldMeta, "maxLength", fieldOpts.maxLength);
1265
+ if (fieldOpts.maxCount !== void 0) setIfChanged(fieldMeta, "maxCount", fieldOpts.maxCount);
1266
+ const encoded = encodeDefault(fieldOpts.default);
1267
+ if (encoded !== void 0) setIfChanged(fieldMeta, "default", encoded);
1268
+ }
1269
+ function syncConstraintMeta(constraintsMap, constraint) {
1270
+ let cMeta = constraintsMap.get(constraint.name);
1271
+ if (!cMeta) {
1272
+ cMeta = new Y.Map();
1273
+ constraintsMap.set(constraint.name, cMeta);
1274
+ }
1275
+ setIfChanged(cMeta, "type", "unique");
1276
+ const fieldsJson = JSON.stringify(constraint.fields);
1277
+ setIfChanged(cMeta, "fields", fieldsJson);
1278
+ }
1279
+ function syncRelationshipMeta(relsMap, relName, relConfig) {
1280
+ let relMeta = relsMap.get(relName);
1281
+ if (!relMeta) {
1282
+ relMeta = new Y.Map();
1283
+ relsMap.set(relName, relMeta);
1284
+ }
1285
+ for (const [key, value] of Object.entries(relConfig)) {
1286
+ if (value !== void 0) {
1287
+ setIfChanged(relMeta, key, value);
1288
+ }
1289
+ }
1290
+ }
1291
+ function syncInferredMeta(yDoc, modelName, recordData) {
1292
+ const meta = yDoc.getMap(`_meta_${modelName}`);
1293
+ for (const [fieldName, value] of Object.entries(recordData)) {
1294
+ if (fieldName.startsWith("_")) continue;
1295
+ let fieldMeta = meta.get(fieldName);
1296
+ if (!fieldMeta) {
1297
+ fieldMeta = new Y.Map();
1298
+ meta.set(fieldName, fieldMeta);
1299
+ }
1300
+ if (!fieldMeta.has("type")) {
1301
+ const inferredType = inferFieldType(value);
1302
+ if (inferredType) {
1303
+ fieldMeta.set("type", inferredType);
1304
+ }
1305
+ }
1306
+ }
1307
+ }
1308
+ function setIfChanged(map, key, value) {
1309
+ if (map.get(key) !== value) {
1310
+ map.set(key, value);
1311
+ }
1312
+ }
1313
+ var Y, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
1314
+ var init_metaSync = __esm({
1315
+ "src/models/metaSync.ts"() {
1316
+ "use strict";
1317
+ Y = __toESM(require("yjs"), 1);
1318
+ KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
1319
+ _syncedCache = /* @__PURE__ */ new WeakMap();
1320
+ }
1321
+ });
1322
+
1186
1323
  // src/models/relationshipManager.ts
1187
1324
  function generateRefersToMethod(_sourceModelClass, config, targetModelClass) {
1188
1325
  return async function() {
@@ -2296,17 +2433,18 @@ __export(BaseModel_exports, {
2296
2433
  function generateULID() {
2297
2434
  return (0, import_ulid.ulid)();
2298
2435
  }
2299
- var Y, import_ulid, LogLevel, Logger, UniqueConstraintViolationError, RecordNotFoundError, SCHEMA_ACCESSORS_KEY, BaseModel2;
2436
+ var Y2, import_ulid, LogLevel, Logger, UniqueConstraintViolationError, RecordNotFoundError, SCHEMA_ACCESSORS_KEY, BaseModel2;
2300
2437
  var init_BaseModel = __esm({
2301
2438
  "src/models/BaseModel.ts"() {
2302
2439
  "use strict";
2303
- Y = __toESM(require("yjs"), 1);
2440
+ Y2 = __toESM(require("yjs"), 1);
2304
2441
  import_ulid = require("ulid");
2305
2442
  init_StringSet();
2306
2443
  init_documentTypes();
2307
2444
  init_DocumentQueryTranslator();
2308
2445
  init_CursorManager();
2309
2446
  init_sql();
2447
+ init_metaSync();
2310
2448
  LogLevel = /* @__PURE__ */ ((LogLevel2) => {
2311
2449
  LogLevel2[LogLevel2["SILENT"] = 0] = "SILENT";
2312
2450
  LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
@@ -2410,6 +2548,7 @@ var init_BaseModel = __esm({
2410
2548
  Object.setPrototypeOf(this, _RecordNotFoundError.prototype);
2411
2549
  }
2412
2550
  };
2551
+ registerFunctionDefault(generateULID, "generate_ulid");
2413
2552
  SCHEMA_ACCESSORS_KEY = /* @__PURE__ */ Symbol("jsBaoSchemaAccessors");
2414
2553
  BaseModel2 = class _BaseModel {
2415
2554
  static modelName;
@@ -2440,6 +2579,9 @@ var init_BaseModel = __esm({
2440
2579
  // Flag to distinguish loading vs user changes
2441
2580
  // StringSet field caching
2442
2581
  _stringSetFields = /* @__PURE__ */ new Map();
2582
+ // upsertOn: track what the caller explicitly provided in the constructor
2583
+ _constructorProvidedId = false;
2584
+ _constructorProvidedFields = /* @__PURE__ */ new Set();
2443
2585
  // Multi-document instance metadata
2444
2586
  _metaDocId = null;
2445
2587
  _metaPermissionHint = null;
@@ -2601,6 +2743,7 @@ var init_BaseModel = __esm({
2601
2743
  const idFieldOptions = schema.fields.get("id");
2602
2744
  if (data.id) {
2603
2745
  this.id = data.id;
2746
+ this._constructorProvidedId = true;
2604
2747
  } else if (idFieldOptions?.autoAssign) {
2605
2748
  this.id = generateULID();
2606
2749
  } else if (idFieldOptions?.default) {
@@ -2681,6 +2824,7 @@ var init_BaseModel = __esm({
2681
2824
  if (fieldKey === "id") continue;
2682
2825
  if (data.hasOwnProperty(fieldKey)) {
2683
2826
  this.setValue(fieldKey, data[fieldKey]);
2827
+ this._constructorProvidedFields.add(fieldKey);
2684
2828
  } else if (fieldOptions.default !== void 0) {
2685
2829
  const defaultValue = typeof fieldOptions.default === "function" ? fieldOptions.default() : fieldOptions.default;
2686
2830
  this.setValue(fieldKey, defaultValue);
@@ -3060,7 +3204,7 @@ var init_BaseModel = __esm({
3060
3204
  }
3061
3205
  const extractStringSetValues = (value) => {
3062
3206
  if (!value) return [];
3063
- if (value instanceof Y.Map) {
3207
+ if (value instanceof Y2.Map) {
3064
3208
  return Array.from(value.entries()).filter(([, isMember]) => Boolean(isMember)).map(([key]) => key);
3065
3209
  }
3066
3210
  if (Array.isArray(value)) {
@@ -3077,7 +3221,7 @@ var init_BaseModel = __esm({
3077
3221
  if (!recordId) continue;
3078
3222
  let itemData;
3079
3223
  const stringSetValuesByField = {};
3080
- if (recordData instanceof Y.Map) {
3224
+ if (recordData instanceof Y2.Map) {
3081
3225
  itemData = {};
3082
3226
  const unknownFields = [];
3083
3227
  for (const [fieldKey, value] of recordData.entries()) {
@@ -3196,7 +3340,7 @@ var init_BaseModel = __esm({
3196
3340
  const buildUniqueKey = (recordData, fields) => {
3197
3341
  const keyParts = [];
3198
3342
  for (const field of fields) {
3199
- const value = recordData instanceof Y.Map ? recordData.get(field) : recordData[field];
3343
+ const value = recordData instanceof Y2.Map ? recordData.get(field) : recordData[field];
3200
3344
  if (value === null || value === void 0) {
3201
3345
  return null;
3202
3346
  }
@@ -3206,7 +3350,7 @@ var init_BaseModel = __esm({
3206
3350
  };
3207
3351
  const extractItemData = (key, recordData) => {
3208
3352
  let itemData;
3209
- if (recordData instanceof Y.Map) {
3353
+ if (recordData instanceof Y2.Map) {
3210
3354
  itemData = {};
3211
3355
  const unknownFields = [];
3212
3356
  for (const [fieldKey, value] of recordData.entries()) {
@@ -3322,7 +3466,7 @@ var init_BaseModel = __esm({
3322
3466
  if (!recordData || !key) continue;
3323
3467
  const itemData = extractItemData(key, recordData);
3324
3468
  if (!itemData) continue;
3325
- if (change.action === "add" && recordData instanceof Y.Map) {
3469
+ if (change.action === "add" && recordData instanceof Y2.Map) {
3326
3470
  Logger.verbose(
3327
3471
  `[${this.name}] Setting up observer on newly added nested YMap for record ${key} in document ${docId}`
3328
3472
  );
@@ -3467,7 +3611,7 @@ var init_BaseModel = __esm({
3467
3611
  `[${this.name}] Setting up observers on existing nested YMaps for ${modelName}/${docId}...`
3468
3612
  );
3469
3613
  for (const [recordId, recordData] of documentYMap.entries()) {
3470
- if (recordData instanceof Y.Map) {
3614
+ if (recordData instanceof Y2.Map) {
3471
3615
  Logger.verbose(
3472
3616
  `[${this.name}] Setting up observer on existing nested YMap for record ${recordId} in document ${docId}`
3473
3617
  );
@@ -3815,6 +3959,39 @@ var init_BaseModel = __esm({
3815
3959
  );
3816
3960
  }
3817
3961
  const yDoc = targetYDoc;
3962
+ let upsertOnConstraint = null;
3963
+ let upsertOnKey = null;
3964
+ if (options?.upsertOn) {
3965
+ const upsertField = options.upsertOn;
3966
+ assertValidIdentifier(upsertField, "upsertOn field");
3967
+ const upsertValue = this[upsertField];
3968
+ if (upsertValue === null || upsertValue === void 0 || upsertValue === "") {
3969
+ throw new Error(
3970
+ `[${modelName}] upsertOn field '${upsertField}' must be present in data and not null/empty`
3971
+ );
3972
+ }
3973
+ const constraint = schema.resolvedUniqueConstraints.find(
3974
+ (c) => c.fields.length === 1 && c.fields[0] === upsertField
3975
+ );
3976
+ if (!constraint) {
3977
+ throw new Error(
3978
+ `[${modelName}] upsertOn field '${upsertField}' does not have a registered unique index`
3979
+ );
3980
+ }
3981
+ const uniqueKey = modelConstructor._buildKeyFromValues(
3982
+ constraint.fields,
3983
+ [upsertValue],
3984
+ modelName,
3985
+ constraint.name
3986
+ );
3987
+ if (uniqueKey === null) {
3988
+ throw new Error(
3989
+ `[${modelName}] upsertOn: unable to build unique key for field '${upsertField}'`
3990
+ );
3991
+ }
3992
+ upsertOnConstraint = constraint;
3993
+ upsertOnKey = uniqueKey;
3994
+ }
3818
3995
  if (!this.id) {
3819
3996
  Logger.error(
3820
3997
  "[BaseModel.save()] ID is undefined before saving! Instance dump:",
@@ -3832,6 +4009,28 @@ var init_BaseModel = __esm({
3832
4009
  dataToSave
3833
4010
  );
3834
4011
  await yDoc.transact(async () => {
4012
+ if (upsertOnConstraint && upsertOnKey) {
4013
+ const constraintMapName = `_uniqueIdx_${modelName}_${upsertOnConstraint.name}`;
4014
+ const constraintMap = yDoc.getMap(constraintMapName);
4015
+ const existingId = constraintMap.get(upsertOnKey);
4016
+ if (existingId) {
4017
+ if (this._constructorProvidedId && this.id !== existingId) {
4018
+ throw new Error(
4019
+ `[${modelName}] upsertOn conflict: caller id '${this.id}' does not match existing record '${existingId}'`
4020
+ );
4021
+ }
4022
+ this.id = existingId;
4023
+ this._metaDocId = targetDocId;
4024
+ this._metaPermissionHint = permissionHint;
4025
+ if (this._localChanges) {
4026
+ for (const key of Object.keys(this._localChanges)) {
4027
+ if (!this._constructorProvidedFields.has(key)) {
4028
+ delete this._localChanges[key];
4029
+ }
4030
+ }
4031
+ }
4032
+ }
4033
+ }
3835
4034
  if (!targetYMap) {
3836
4035
  throw new Error(
3837
4036
  `[${modelName}] Target YMap not found for save operation`
@@ -3842,7 +4041,7 @@ var init_BaseModel = __esm({
3842
4041
  Logger.debug(`[${modelName}.save] isUpdate: ${isUpdate}`);
3843
4042
  Logger.debug(`[${modelName}.save] recordYMap exists: ${!!recordYMap}`);
3844
4043
  if (!recordYMap) {
3845
- recordYMap = new Y.Map();
4044
+ recordYMap = new Y2.Map();
3846
4045
  Logger.verbose(
3847
4046
  `[${modelName}] Creating new nested YMap for record ${this.id} in document ${targetDocId || "legacy"}`
3848
4047
  );
@@ -4014,6 +4213,7 @@ var init_BaseModel = __esm({
4014
4213
  }
4015
4214
  }
4016
4215
  Logger.verbose(`[${modelName}] Save completed for ${this.id}`);
4216
+ syncModelMeta(yDoc, modelName, schema);
4017
4217
  if (_BaseModel.dbInstance) {
4018
4218
  Logger.verbose(
4019
4219
  `[${modelName}] Syncing StringSet changes to database for ${this.id}`
@@ -4858,7 +5058,7 @@ var init_BaseModel = __esm({
4858
5058
  );
4859
5059
  }
4860
5060
  for (const [recordId, recordYMap] of documentYMap.entries()) {
4861
- if (recordYMap instanceof Y.Map) {
5061
+ if (recordYMap instanceof Y2.Map) {
4862
5062
  const instance = new this({ id: recordId });
4863
5063
  instance._metaDocId = docId;
4864
5064
  const connectedDoc = modelConstructor.connectedDocuments.get(docId);
@@ -4929,7 +5129,7 @@ var init_BaseModel = __esm({
4929
5129
  const documentYMap = modelConstructor.documentYMaps.get(documentYMapKey);
4930
5130
  if (documentYMap) {
4931
5131
  const recordYMap2 = documentYMap.get(recordId2);
4932
- if (recordYMap2 && recordYMap2 instanceof Y.Map) {
5132
+ if (recordYMap2 && recordYMap2 instanceof Y2.Map) {
4933
5133
  const instance = new modelConstructor({
4934
5134
  id: recordId2
4935
5135
  });
@@ -4966,7 +5166,7 @@ var init_BaseModel = __esm({
4966
5166
  if (!recordYMap) {
4967
5167
  return null;
4968
5168
  }
4969
- if (recordYMap instanceof Y.Map) {
5169
+ if (recordYMap instanceof Y2.Map) {
4970
5170
  const instance = new modelConstructor({
4971
5171
  id: recordId
4972
5172
  });
@@ -5350,15 +5550,24 @@ __export(browser_exports, {
5350
5550
  attachAndRegisterModel: () => attachAndRegisterModel,
5351
5551
  attachSchemaToClass: () => attachSchemaToClass,
5352
5552
  autoRegisterModel: () => autoRegisterModel,
5553
+ clearMetaSyncCache: () => clearMetaSyncCache,
5353
5554
  createModelClass: () => createModelClass,
5354
5555
  defineModelSchema: () => defineModelSchema,
5355
5556
  detectEnvironment: () => detectEnvironment,
5557
+ discoverModelNames: () => discoverModelNames,
5558
+ discoverSchema: () => discoverSchema,
5356
5559
  features: () => features,
5357
5560
  generateULID: () => generateULID,
5561
+ inferFieldType: () => inferFieldType,
5358
5562
  initJsBao: () => initJsBao,
5359
5563
  isBrowser: () => isBrowser,
5360
5564
  isNode: () => isNode,
5361
- resetJsBao: () => resetJsBao
5565
+ loadSchemaFromTomlString: () => loadSchemaFromTomlString,
5566
+ registerFunctionDefault: () => registerFunctionDefault,
5567
+ resetJsBao: () => resetJsBao,
5568
+ schemaToToml: () => schemaToToml,
5569
+ syncInferredMeta: () => syncInferredMeta,
5570
+ syncModelMeta: () => syncModelMeta
5362
5571
  });
5363
5572
  module.exports = __toCommonJS(browser_exports);
5364
5573
  init_BaseModel();
@@ -5549,9 +5758,12 @@ var TransactionalSQLJSOperations = class {
5549
5758
  const quotedColumns = columns.map((column) => quoteIdentifier(column));
5550
5759
  const placeholders = columns.map(() => "?").join(", ");
5551
5760
  const values = Object.values(data);
5552
- const insertSQL = `INSERT OR REPLACE INTO ${quotedTableName} (${quotedColumns.join(
5761
+ const nonIdColumns = columns.filter((c) => c !== "id");
5762
+ const quotedNonIdColumns = nonIdColumns.map((c) => quoteIdentifier(c));
5763
+ const updateClause = nonIdColumns.length > 0 ? `ON CONFLICT(${quoteIdentifier("id")}) DO UPDATE SET ${quotedNonIdColumns.map((c) => `${c}=excluded.${c}`).join(", ")}` : `ON CONFLICT(${quoteIdentifier("id")}) DO NOTHING`;
5764
+ const insertSQL = `INSERT INTO ${quotedTableName} (${quotedColumns.join(
5553
5765
  ", "
5554
- )}) VALUES (${placeholders});`;
5766
+ )}) VALUES (${placeholders}) ${updateClause};`;
5555
5767
  this.db.run(insertSQL, values);
5556
5768
  }
5557
5769
  async delete(modelName, id) {
@@ -5762,9 +5974,12 @@ var SqljsEngine = class _SqljsEngine {
5762
5974
  const quotedColumns = columns.map((column) => quoteIdentifier(column));
5763
5975
  const placeholders = columns.map(() => "?").join(", ");
5764
5976
  const values = Object.values(data);
5765
- const insertSQL = `INSERT OR REPLACE INTO ${quotedTableName} (${quotedColumns.join(
5977
+ const nonIdColumns = columns.filter((c) => c !== "id");
5978
+ const quotedNonIdColumns = nonIdColumns.map((c) => quoteIdentifier(c));
5979
+ const updateClause = nonIdColumns.length > 0 ? `ON CONFLICT(${quoteIdentifier("id")}) DO UPDATE SET ${quotedNonIdColumns.map((c) => `${c}=excluded.${c}`).join(", ")}` : `ON CONFLICT(${quoteIdentifier("id")}) DO NOTHING`;
5980
+ const insertSQL = `INSERT INTO ${quotedTableName} (${quotedColumns.join(
5766
5981
  ", "
5767
- )}) VALUES (${placeholders});`;
5982
+ )}) VALUES (${placeholders}) ${updateClause};`;
5768
5983
  this.db.run(insertSQL, values);
5769
5984
  }
5770
5985
  async delete(modelName, id) {
@@ -6389,3 +6604,325 @@ function attachAndRegisterModel(modelClass, schema) {
6389
6604
  const runtimeShape = attachSchemaToClass(modelClass, schema);
6390
6605
  autoRegisterModel(modelClass, runtimeShape);
6391
6606
  }
6607
+
6608
+ // src/utils/yDocSchema.ts
6609
+ var Y3 = __toESM(require("yjs"), 1);
6610
+ function discoverSchema(yDoc) {
6611
+ const models = {};
6612
+ const metaNames = /* @__PURE__ */ new Set();
6613
+ for (const key of yDoc.share.keys()) {
6614
+ if (!key.startsWith("_meta_")) continue;
6615
+ const map = materializeMap(yDoc, key);
6616
+ if (!map) continue;
6617
+ const modelName = key.slice("_meta_".length);
6618
+ metaNames.add(modelName);
6619
+ models[modelName] = readModelMeta(map);
6620
+ }
6621
+ for (const key of yDoc.share.keys()) {
6622
+ if (key.startsWith("_")) continue;
6623
+ if (metaNames.has(key)) continue;
6624
+ const map = materializeMap(yDoc, key);
6625
+ if (!map || map.size === 0) continue;
6626
+ const inferred = inferModelFromData(map);
6627
+ if (inferred) models[key] = inferred;
6628
+ }
6629
+ return { models };
6630
+ }
6631
+ function discoverModelNames(yDoc) {
6632
+ const names = [];
6633
+ for (const key of yDoc.share.keys()) {
6634
+ if (key.startsWith("_")) continue;
6635
+ const map = materializeMap(yDoc, key);
6636
+ if (map) names.push(key);
6637
+ }
6638
+ return names.sort();
6639
+ }
6640
+ function materializeMap(yDoc, key) {
6641
+ try {
6642
+ const map = yDoc.getMap(key);
6643
+ return map instanceof Y3.Map ? map : null;
6644
+ } catch {
6645
+ return null;
6646
+ }
6647
+ }
6648
+ function readModelMeta(metaMap) {
6649
+ const fields = {};
6650
+ let constraints;
6651
+ let relationships;
6652
+ for (const [key, value] of metaMap.entries()) {
6653
+ if (key === "_constraints" && value instanceof Y3.Map) {
6654
+ constraints = readConstraints(value);
6655
+ } else if (key === "_relationships" && value instanceof Y3.Map) {
6656
+ relationships = readRelationships(value);
6657
+ } else if (value instanceof Y3.Map) {
6658
+ fields[key] = readFieldMeta(value);
6659
+ }
6660
+ }
6661
+ const model = { fields };
6662
+ if (constraints && Object.keys(constraints).length > 0) {
6663
+ model.constraints = constraints;
6664
+ }
6665
+ if (relationships && Object.keys(relationships).length > 0) {
6666
+ model.relationships = relationships;
6667
+ }
6668
+ return model;
6669
+ }
6670
+ function readFieldMeta(fieldMap) {
6671
+ const field = { type: fieldMap.get("type") ?? "unknown" };
6672
+ if (fieldMap.get("indexed") === true) field.indexed = true;
6673
+ if (fieldMap.get("unique") === true) field.unique = true;
6674
+ if (fieldMap.get("required") === true) field.required = true;
6675
+ if (fieldMap.get("autoAssign") === true) field.autoAssign = true;
6676
+ const def = fieldMap.get("default");
6677
+ if (def !== void 0) field.default = def;
6678
+ const maxLength = fieldMap.get("maxLength");
6679
+ if (maxLength !== void 0) field.maxLength = maxLength;
6680
+ const maxCount = fieldMap.get("maxCount");
6681
+ if (maxCount !== void 0) field.maxCount = maxCount;
6682
+ return field;
6683
+ }
6684
+ function inferModelFromData(dataMap) {
6685
+ const fields = {};
6686
+ let sampled = 0;
6687
+ for (const [_recordId, recordValue] of dataMap.entries()) {
6688
+ if (!(recordValue instanceof Y3.Map)) continue;
6689
+ if (++sampled > 5) break;
6690
+ for (const [fieldName, value] of recordValue.entries()) {
6691
+ if (fieldName.startsWith("_")) continue;
6692
+ if (fields[fieldName]) continue;
6693
+ const type = inferTypeFromValue(value);
6694
+ if (type) fields[fieldName] = { type };
6695
+ }
6696
+ }
6697
+ if (Object.keys(fields).length === 0) return null;
6698
+ return { fields };
6699
+ }
6700
+ function inferTypeFromValue(value) {
6701
+ if (value instanceof Y3.Map) return "stringset";
6702
+ switch (typeof value) {
6703
+ case "string":
6704
+ return "string";
6705
+ case "number":
6706
+ return "number";
6707
+ case "boolean":
6708
+ return "boolean";
6709
+ default:
6710
+ return null;
6711
+ }
6712
+ }
6713
+ function readConstraints(constraintsMap) {
6714
+ const out = {};
6715
+ for (const [name, value] of constraintsMap.entries()) {
6716
+ if (!(value instanceof Y3.Map)) continue;
6717
+ let fields = [];
6718
+ const rawFields = value.get("fields");
6719
+ if (typeof rawFields === "string") {
6720
+ try {
6721
+ fields = JSON.parse(rawFields);
6722
+ } catch {
6723
+ }
6724
+ }
6725
+ out[name] = {
6726
+ type: value.get("type") ?? "unknown",
6727
+ fields
6728
+ };
6729
+ }
6730
+ return out;
6731
+ }
6732
+ function readRelationships(relsMap) {
6733
+ const out = {};
6734
+ for (const [name, value] of relsMap.entries()) {
6735
+ if (!(value instanceof Y3.Map)) continue;
6736
+ const rel = {};
6737
+ for (const [k, v] of value.entries()) {
6738
+ rel[k] = v;
6739
+ }
6740
+ out[name] = rel;
6741
+ }
6742
+ return out;
6743
+ }
6744
+ var CAMEL_TO_SNAKE = {
6745
+ autoAssign: "auto_assign",
6746
+ maxLength: "max_length",
6747
+ maxCount: "max_count",
6748
+ relatedIdField: "related_id_field",
6749
+ joinModel: "join_model",
6750
+ joinModelLocalField: "join_model_local_field",
6751
+ joinModelRelatedField: "join_model_related_field",
6752
+ joinModelOrderByField: "join_model_order_by_field",
6753
+ joinModelOrderDirection: "join_model_order_direction",
6754
+ orderByField: "order_by_field",
6755
+ orderDirection: "order_direction"
6756
+ };
6757
+ function toSnake(key) {
6758
+ return CAMEL_TO_SNAKE[key] ?? key;
6759
+ }
6760
+ function tomlValue(v) {
6761
+ if (typeof v === "string") {
6762
+ return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
6763
+ }
6764
+ return String(v);
6765
+ }
6766
+ function schemaToToml(schema) {
6767
+ const lines = [];
6768
+ for (const [modelName, model] of Object.entries(schema.models)) {
6769
+ if (lines.length > 0) lines.push("", "");
6770
+ lines.push(`[models.${modelName}]`);
6771
+ for (const [fieldName, field] of Object.entries(model.fields)) {
6772
+ lines.push("");
6773
+ lines.push(`[models.${modelName}.fields.${fieldName}]`);
6774
+ lines.push(`type = ${tomlValue(field.type)}`);
6775
+ if (field.autoAssign) lines.push("auto_assign = true");
6776
+ if (field.indexed) lines.push("indexed = true");
6777
+ if (field.unique) lines.push("unique = true");
6778
+ if (field.required) lines.push("required = true");
6779
+ if (field.maxLength !== void 0) lines.push(`max_length = ${field.maxLength}`);
6780
+ if (field.maxCount !== void 0) lines.push(`max_count = ${field.maxCount}`);
6781
+ if (field.default !== void 0) lines.push(`default = ${tomlValue(field.default)}`);
6782
+ }
6783
+ if (model.relationships) {
6784
+ for (const [relName, rel] of Object.entries(model.relationships)) {
6785
+ lines.push("");
6786
+ lines.push(`[models.${modelName}.relationships.${relName}]`);
6787
+ for (const [k, v] of Object.entries(rel)) {
6788
+ if (v === void 0) continue;
6789
+ lines.push(`${toSnake(k)} = ${tomlValue(v)}`);
6790
+ }
6791
+ }
6792
+ }
6793
+ if (model.constraints) {
6794
+ for (const [cName, c] of Object.entries(model.constraints)) {
6795
+ lines.push("");
6796
+ lines.push(`[[models.${modelName}.unique_constraints]]`);
6797
+ lines.push(`name = ${tomlValue(cName)}`);
6798
+ lines.push(`fields = [${c.fields.map((f) => tomlValue(f)).join(", ")}]`);
6799
+ }
6800
+ }
6801
+ }
6802
+ lines.push("");
6803
+ return lines.join("\n");
6804
+ }
6805
+
6806
+ // src/models/tomlLoader.ts
6807
+ var import_smol_toml = require("smol-toml");
6808
+ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
6809
+ "string",
6810
+ "number",
6811
+ "boolean",
6812
+ "date",
6813
+ "id",
6814
+ "stringset"
6815
+ ]);
6816
+ function parseFieldOptions(raw) {
6817
+ if (!raw.type || !VALID_FIELD_TYPES.has(raw.type)) {
6818
+ throw new Error(
6819
+ `Invalid field type "${raw.type}". Must be one of: ${[...VALID_FIELD_TYPES].join(", ")}`
6820
+ );
6821
+ }
6822
+ const opts = { type: raw.type };
6823
+ if (raw.indexed === true) opts.indexed = true;
6824
+ if (raw.unique === true) opts.unique = true;
6825
+ if (raw.required === true) opts.required = true;
6826
+ if (raw.auto_assign === true) opts.autoAssign = true;
6827
+ if (raw.max_length !== void 0) opts.maxLength = raw.max_length;
6828
+ if (raw.max_count !== void 0) opts.maxCount = raw.max_count;
6829
+ if (raw.default !== void 0) opts.default = raw.default;
6830
+ return opts;
6831
+ }
6832
+ function requireField(raw, field, context) {
6833
+ if (!raw[field]) {
6834
+ throw new Error(`Relationship ${context}: missing required field "${field}"`);
6835
+ }
6836
+ }
6837
+ function parseRelationship(raw) {
6838
+ const type = raw.type;
6839
+ if (type === "refersTo") {
6840
+ requireField(raw, "model", "refersTo");
6841
+ requireField(raw, "related_id_field", "refersTo");
6842
+ return {
6843
+ type: "refersTo",
6844
+ model: raw.model,
6845
+ relatedIdField: raw.related_id_field
6846
+ };
6847
+ }
6848
+ if (type === "hasMany") {
6849
+ requireField(raw, "model", "hasMany");
6850
+ requireField(raw, "related_id_field", "hasMany");
6851
+ const rel = {
6852
+ type: "hasMany",
6853
+ model: raw.model,
6854
+ relatedIdField: raw.related_id_field
6855
+ };
6856
+ if (raw.order_by_field) rel.orderByField = raw.order_by_field;
6857
+ if (raw.order_direction) rel.orderDirection = raw.order_direction;
6858
+ return rel;
6859
+ }
6860
+ if (type === "hasManyThrough") {
6861
+ requireField(raw, "model", "hasManyThrough");
6862
+ requireField(raw, "join_model", "hasManyThrough");
6863
+ requireField(raw, "join_model_local_field", "hasManyThrough");
6864
+ requireField(raw, "join_model_related_field", "hasManyThrough");
6865
+ const rel = {
6866
+ type: "hasManyThrough",
6867
+ model: raw.model,
6868
+ joinModel: raw.join_model,
6869
+ joinModelLocalField: raw.join_model_local_field,
6870
+ joinModelRelatedField: raw.join_model_related_field
6871
+ };
6872
+ if (raw.join_model_order_by_field)
6873
+ rel.joinModelOrderByField = raw.join_model_order_by_field;
6874
+ if (raw.join_model_order_direction)
6875
+ rel.joinModelOrderDirection = raw.join_model_order_direction;
6876
+ return rel;
6877
+ }
6878
+ throw new Error(`Unknown relationship type: ${type}`);
6879
+ }
6880
+ function loadSchemaFromTomlString(tomlString) {
6881
+ const parsed = (0, import_smol_toml.parse)(tomlString);
6882
+ const models = parsed.models;
6883
+ if (!models || typeof models !== "object") {
6884
+ throw new Error("TOML schema must have a [models] section");
6885
+ }
6886
+ const schemas = [];
6887
+ for (const [modelName, modelDef] of Object.entries(models)) {
6888
+ const fields = {};
6889
+ if (modelDef.fields) {
6890
+ for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
6891
+ fields[fieldName] = parseFieldOptions(fieldDef);
6892
+ }
6893
+ }
6894
+ let relationships;
6895
+ if (modelDef.relationships) {
6896
+ relationships = {};
6897
+ for (const [relName, relDef] of Object.entries(
6898
+ modelDef.relationships
6899
+ )) {
6900
+ relationships[relName] = parseRelationship(relDef);
6901
+ }
6902
+ }
6903
+ let uniqueConstraints;
6904
+ if (modelDef.unique_constraints) {
6905
+ uniqueConstraints = [];
6906
+ for (const raw of modelDef.unique_constraints) {
6907
+ uniqueConstraints.push({
6908
+ name: raw.name,
6909
+ fields: [...raw.fields]
6910
+ });
6911
+ }
6912
+ }
6913
+ schemas.push(
6914
+ defineModelSchema({
6915
+ name: modelName,
6916
+ fields,
6917
+ options: {
6918
+ uniqueConstraints,
6919
+ relationships
6920
+ }
6921
+ })
6922
+ );
6923
+ }
6924
+ return schemas;
6925
+ }
6926
+
6927
+ // src/browser.ts
6928
+ init_metaSync();