js-bao 0.3.0 → 0.4.0

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.
@@ -85,15 +85,139 @@ var init_DocumentQueryTranslator = __esm({
85
85
  });
86
86
 
87
87
  // src/models/metaSync.ts
88
+ function inferFieldType(value) {
89
+ if (value instanceof Y.Map) return "stringset";
90
+ switch (typeof value) {
91
+ case "string":
92
+ return "string";
93
+ case "number":
94
+ return "number";
95
+ case "boolean":
96
+ return "boolean";
97
+ default:
98
+ return null;
99
+ }
100
+ }
88
101
  function registerFunctionDefault(fn, name) {
89
102
  KNOWN_FUNCTION_DEFAULTS.set(fn, name);
90
103
  }
91
- var Y, KNOWN_FUNCTION_DEFAULTS;
104
+ function encodeDefault(value) {
105
+ if (value === void 0 || value === null) return void 0;
106
+ if (typeof value === "function") {
107
+ const name = KNOWN_FUNCTION_DEFAULTS.get(value);
108
+ return name ? `$${name}` : "$unknown_function";
109
+ }
110
+ return value;
111
+ }
112
+ function clearMetaSyncCache(yDoc) {
113
+ if (yDoc) {
114
+ _syncedCache.delete(yDoc);
115
+ }
116
+ }
117
+ function syncModelMeta(yDoc, modelName, schema) {
118
+ let synced = _syncedCache.get(yDoc);
119
+ if (synced?.has(modelName)) return;
120
+ if (!synced) {
121
+ synced = /* @__PURE__ */ new Set();
122
+ _syncedCache.set(yDoc, synced);
123
+ }
124
+ const meta = yDoc.getMap(`_meta_${modelName}`);
125
+ for (const [fieldName, fieldOpts] of schema.fields.entries()) {
126
+ syncFieldMeta(meta, fieldName, fieldOpts);
127
+ }
128
+ const compoundConstraints = schema.resolvedUniqueConstraints.filter(
129
+ (c) => c.fields.length > 1
130
+ );
131
+ if (compoundConstraints.length > 0) {
132
+ let constraints = meta.get("_constraints");
133
+ if (!constraints) {
134
+ constraints = new Y.Map();
135
+ meta.set("_constraints", constraints);
136
+ }
137
+ for (const constraint of compoundConstraints) {
138
+ syncConstraintMeta(constraints, constraint);
139
+ }
140
+ }
141
+ const relationships = schema.options.relationships;
142
+ if (relationships && Object.keys(relationships).length > 0) {
143
+ let rels = meta.get("_relationships");
144
+ if (!rels) {
145
+ rels = new Y.Map();
146
+ meta.set("_relationships", rels);
147
+ }
148
+ for (const [relName, relConfig] of Object.entries(relationships)) {
149
+ syncRelationshipMeta(rels, relName, relConfig);
150
+ }
151
+ }
152
+ synced.add(modelName);
153
+ }
154
+ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
155
+ let fieldMeta = metaMap.get(fieldName);
156
+ if (!fieldMeta) {
157
+ fieldMeta = new Y.Map();
158
+ metaMap.set(fieldName, fieldMeta);
159
+ }
160
+ setIfChanged(fieldMeta, "type", fieldOpts.type);
161
+ if (fieldOpts.indexed) setIfChanged(fieldMeta, "indexed", true);
162
+ if (fieldOpts.unique) setIfChanged(fieldMeta, "unique", true);
163
+ if (fieldOpts.required) setIfChanged(fieldMeta, "required", true);
164
+ if (fieldOpts.autoAssign) setIfChanged(fieldMeta, "autoAssign", true);
165
+ if (fieldOpts.maxLength !== void 0) setIfChanged(fieldMeta, "maxLength", fieldOpts.maxLength);
166
+ if (fieldOpts.maxCount !== void 0) setIfChanged(fieldMeta, "maxCount", fieldOpts.maxCount);
167
+ const encoded = encodeDefault(fieldOpts.default);
168
+ if (encoded !== void 0) setIfChanged(fieldMeta, "default", encoded);
169
+ }
170
+ function syncConstraintMeta(constraintsMap, constraint) {
171
+ let cMeta = constraintsMap.get(constraint.name);
172
+ if (!cMeta) {
173
+ cMeta = new Y.Map();
174
+ constraintsMap.set(constraint.name, cMeta);
175
+ }
176
+ setIfChanged(cMeta, "type", "unique");
177
+ const fieldsJson = JSON.stringify(constraint.fields);
178
+ setIfChanged(cMeta, "fields", fieldsJson);
179
+ }
180
+ function syncRelationshipMeta(relsMap, relName, relConfig) {
181
+ let relMeta = relsMap.get(relName);
182
+ if (!relMeta) {
183
+ relMeta = new Y.Map();
184
+ relsMap.set(relName, relMeta);
185
+ }
186
+ for (const [key, value] of Object.entries(relConfig)) {
187
+ if (value !== void 0) {
188
+ setIfChanged(relMeta, key, value);
189
+ }
190
+ }
191
+ }
192
+ function syncInferredMeta(yDoc, modelName, recordData) {
193
+ const meta = yDoc.getMap(`_meta_${modelName}`);
194
+ for (const [fieldName, value] of Object.entries(recordData)) {
195
+ if (fieldName.startsWith("_")) continue;
196
+ let fieldMeta = meta.get(fieldName);
197
+ if (!fieldMeta) {
198
+ fieldMeta = new Y.Map();
199
+ meta.set(fieldName, fieldMeta);
200
+ }
201
+ if (!fieldMeta.has("type")) {
202
+ const inferredType = inferFieldType(value);
203
+ if (inferredType) {
204
+ fieldMeta.set("type", inferredType);
205
+ }
206
+ }
207
+ }
208
+ }
209
+ function setIfChanged(map, key, value) {
210
+ if (map.get(key) !== value) {
211
+ map.set(key, value);
212
+ }
213
+ }
214
+ var Y, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
92
215
  var init_metaSync = __esm({
93
216
  "src/models/metaSync.ts"() {
94
217
  "use strict";
95
218
  Y = __toESM(require("yjs"), 1);
96
219
  KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
220
+ _syncedCache = /* @__PURE__ */ new WeakMap();
97
221
  }
98
222
  });
99
223
 
@@ -185,14 +309,40 @@ var init_BaseModel = __esm({
185
309
  }
186
310
  });
187
311
 
312
+ // src/models/relationshipManager.ts
313
+ var init_relationshipManager = __esm({
314
+ "src/models/relationshipManager.ts"() {
315
+ "use strict";
316
+ init_BaseModel();
317
+ }
318
+ });
319
+
320
+ // src/models/ModelRegistry.ts
321
+ var init_ModelRegistry = __esm({
322
+ "src/models/ModelRegistry.ts"() {
323
+ "use strict";
324
+ init_BaseModel();
325
+ init_relationshipManager();
326
+ }
327
+ });
328
+
188
329
  // src/cloudflare.ts
189
330
  var cloudflare_exports = {};
190
331
  __export(cloudflare_exports, {
191
332
  DOClientEngine: () => DOClientEngine,
333
+ clearMetaSyncCache: () => clearMetaSyncCache,
192
334
  connectDoDb: () => connectDoDb,
335
+ discoverModelNames: () => discoverModelNames,
336
+ discoverSchema: () => discoverSchema,
193
337
  getActiveDOEngine: () => getActiveDOEngine,
338
+ inferFieldType: () => inferFieldType,
194
339
  initJsBaoDO: () => initJsBaoDO,
195
- resetJsBaoDO: () => resetJsBaoDO
340
+ loadSchemaFromTomlString: () => loadSchemaFromTomlString,
341
+ registerFunctionDefault: () => registerFunctionDefault,
342
+ resetJsBaoDO: () => resetJsBaoDO,
343
+ schemaToToml: () => schemaToToml,
344
+ syncInferredMeta: () => syncInferredMeta,
345
+ syncModelMeta: () => syncModelMeta
196
346
  });
197
347
  module.exports = __toCommonJS(cloudflare_exports);
198
348
 
@@ -340,7 +490,8 @@ var DOClientEngine = class extends DatabaseEngine {
340
490
  data,
341
491
  stringSets,
342
492
  ifNotExists: options?.ifNotExists,
343
- condition: options?.condition
493
+ condition: options?.condition,
494
+ upsertOn: options?.upsertOn
344
495
  };
345
496
  const response = await this.doFetch("/save", request);
346
497
  return response.id;
@@ -707,22 +858,24 @@ async function initJsBaoDO(options) {
707
858
  if (!docId) {
708
859
  throw new Error("Not connected to a document. Call connectDocument() first.");
709
860
  }
710
- if (!data.id) {
711
- throw new Error("Record must have an 'id' field");
712
- }
713
861
  let stringSets;
714
862
  let ifNotExists;
715
863
  let condition;
864
+ let upsertOn;
716
865
  if (options2 && typeof options2 === "object") {
717
- if (options2.ifNotExists !== void 0 || options2.stringSets !== void 0 || options2.condition !== void 0) {
866
+ if (options2.ifNotExists !== void 0 || options2.stringSets !== void 0 || options2.condition !== void 0 || options2.upsertOn !== void 0) {
718
867
  stringSets = options2.stringSets;
719
868
  ifNotExists = options2.ifNotExists;
720
869
  condition = options2.condition;
870
+ upsertOn = options2.upsertOn;
721
871
  } else {
722
872
  stringSets = options2;
723
873
  }
724
874
  }
725
- return engine.saveModel(resolveModelName(model), data.id, data, stringSets, { ifNotExists, condition });
875
+ if (!data.id && !upsertOn) {
876
+ throw new Error("Record must have an 'id' field (or use upsertOn)");
877
+ }
878
+ return engine.saveModel(resolveModelName(model), data.id, data, stringSets, { ifNotExists, condition, upsertOn });
726
879
  },
727
880
  patch: async (model, id, data, options2) => {
728
881
  const docId = engine.getCurrentDocument();
@@ -881,22 +1034,24 @@ function createModelAccessor(engine, modelName) {
881
1034
  return result.data.length > 0 ? result.data[0] : null;
882
1035
  },
883
1036
  save: (data, options) => {
884
- if (!data.id) {
885
- throw new Error("Record must have an 'id' field");
886
- }
887
1037
  let stringSets;
888
1038
  let ifNotExists;
889
1039
  let condition;
1040
+ let upsertOn;
890
1041
  if (options && typeof options === "object") {
891
- if (options.ifNotExists !== void 0 || options.stringSets !== void 0 || options.condition !== void 0) {
1042
+ if (options.ifNotExists !== void 0 || options.stringSets !== void 0 || options.condition !== void 0 || options.upsertOn !== void 0) {
892
1043
  stringSets = options.stringSets;
893
1044
  ifNotExists = options.ifNotExists;
894
1045
  condition = options.condition;
1046
+ upsertOn = options.upsertOn;
895
1047
  } else {
896
1048
  stringSets = options;
897
1049
  }
898
1050
  }
899
- return engine.saveModel(modelName, data.id, data, stringSets, { ifNotExists, condition });
1051
+ if (!data.id && !upsertOn) {
1052
+ throw new Error("Record must have an 'id' field (or use upsertOn)");
1053
+ }
1054
+ return engine.saveModel(modelName, data.id, data, stringSets, { ifNotExists, condition, upsertOn });
900
1055
  },
901
1056
  patch: (id, data, options) => {
902
1057
  let stringSets;
@@ -983,22 +1138,24 @@ function connectDoDb(options) {
983
1138
  return result.data.length > 0 ? result.data[0] : null;
984
1139
  },
985
1140
  save: (model, data, options2) => {
986
- if (!data.id) {
987
- throw new Error("Record must have an 'id' field");
988
- }
989
1141
  let stringSets;
990
1142
  let ifNotExists;
991
1143
  let condition;
1144
+ let upsertOn;
992
1145
  if (options2 && typeof options2 === "object") {
993
- if (options2.ifNotExists !== void 0 || options2.stringSets !== void 0 || options2.condition !== void 0) {
1146
+ if (options2.ifNotExists !== void 0 || options2.stringSets !== void 0 || options2.condition !== void 0 || options2.upsertOn !== void 0) {
994
1147
  stringSets = options2.stringSets;
995
1148
  ifNotExists = options2.ifNotExists;
996
1149
  condition = options2.condition;
1150
+ upsertOn = options2.upsertOn;
997
1151
  } else {
998
1152
  stringSets = options2;
999
1153
  }
1000
1154
  }
1001
- return engine.saveModel(resolveModelName(model), data.id, data, stringSets, { ifNotExists, condition });
1155
+ if (!data.id && !upsertOn) {
1156
+ throw new Error("Record must have an 'id' field (or use upsertOn)");
1157
+ }
1158
+ return engine.saveModel(resolveModelName(model), data.id, data, stringSets, { ifNotExists, condition, upsertOn });
1002
1159
  },
1003
1160
  patch: (model, id2, data, options2) => {
1004
1161
  let stringSets;
@@ -1064,3 +1221,401 @@ function connectDoDb(options) {
1064
1221
  Logger.info(`[connectDoDb] Connected to document: ${id} at ${endpoint}`);
1065
1222
  return db;
1066
1223
  }
1224
+
1225
+ // src/utils/yDocSchema.ts
1226
+ var Y3 = __toESM(require("yjs"), 1);
1227
+ function discoverSchema(yDoc) {
1228
+ const models = {};
1229
+ const metaNames = /* @__PURE__ */ new Set();
1230
+ for (const key of yDoc.share.keys()) {
1231
+ if (!key.startsWith("_meta_")) continue;
1232
+ const map = materializeMap(yDoc, key);
1233
+ if (!map) continue;
1234
+ const modelName = key.slice("_meta_".length);
1235
+ metaNames.add(modelName);
1236
+ models[modelName] = readModelMeta(map);
1237
+ }
1238
+ for (const key of yDoc.share.keys()) {
1239
+ if (key.startsWith("_")) continue;
1240
+ if (metaNames.has(key)) continue;
1241
+ const map = materializeMap(yDoc, key);
1242
+ if (!map || map.size === 0) continue;
1243
+ const inferred = inferModelFromData(map);
1244
+ if (inferred) models[key] = inferred;
1245
+ }
1246
+ return { models };
1247
+ }
1248
+ function discoverModelNames(yDoc) {
1249
+ const names = [];
1250
+ for (const key of yDoc.share.keys()) {
1251
+ if (key.startsWith("_")) continue;
1252
+ const map = materializeMap(yDoc, key);
1253
+ if (map) names.push(key);
1254
+ }
1255
+ return names.sort();
1256
+ }
1257
+ function materializeMap(yDoc, key) {
1258
+ try {
1259
+ const map = yDoc.getMap(key);
1260
+ return map instanceof Y3.Map ? map : null;
1261
+ } catch {
1262
+ return null;
1263
+ }
1264
+ }
1265
+ function readModelMeta(metaMap) {
1266
+ const fields = {};
1267
+ let constraints;
1268
+ let relationships;
1269
+ for (const [key, value] of metaMap.entries()) {
1270
+ if (key === "_constraints" && value instanceof Y3.Map) {
1271
+ constraints = readConstraints(value);
1272
+ } else if (key === "_relationships" && value instanceof Y3.Map) {
1273
+ relationships = readRelationships(value);
1274
+ } else if (value instanceof Y3.Map) {
1275
+ fields[key] = readFieldMeta(value);
1276
+ }
1277
+ }
1278
+ const model = { fields };
1279
+ if (constraints && Object.keys(constraints).length > 0) {
1280
+ model.constraints = constraints;
1281
+ }
1282
+ if (relationships && Object.keys(relationships).length > 0) {
1283
+ model.relationships = relationships;
1284
+ }
1285
+ return model;
1286
+ }
1287
+ function readFieldMeta(fieldMap) {
1288
+ const field = { type: fieldMap.get("type") ?? "unknown" };
1289
+ if (fieldMap.get("indexed") === true) field.indexed = true;
1290
+ if (fieldMap.get("unique") === true) field.unique = true;
1291
+ if (fieldMap.get("required") === true) field.required = true;
1292
+ if (fieldMap.get("autoAssign") === true) field.autoAssign = true;
1293
+ const def = fieldMap.get("default");
1294
+ if (def !== void 0) field.default = def;
1295
+ const maxLength = fieldMap.get("maxLength");
1296
+ if (maxLength !== void 0) field.maxLength = maxLength;
1297
+ const maxCount = fieldMap.get("maxCount");
1298
+ if (maxCount !== void 0) field.maxCount = maxCount;
1299
+ return field;
1300
+ }
1301
+ function inferModelFromData(dataMap) {
1302
+ const fields = {};
1303
+ let sampled = 0;
1304
+ for (const [_recordId, recordValue] of dataMap.entries()) {
1305
+ if (!(recordValue instanceof Y3.Map)) continue;
1306
+ if (++sampled > 5) break;
1307
+ for (const [fieldName, value] of recordValue.entries()) {
1308
+ if (fieldName.startsWith("_")) continue;
1309
+ if (fields[fieldName]) continue;
1310
+ const type = inferTypeFromValue(value);
1311
+ if (type) fields[fieldName] = { type };
1312
+ }
1313
+ }
1314
+ if (Object.keys(fields).length === 0) return null;
1315
+ return { fields };
1316
+ }
1317
+ function inferTypeFromValue(value) {
1318
+ if (value instanceof Y3.Map) return "stringset";
1319
+ switch (typeof value) {
1320
+ case "string":
1321
+ return "string";
1322
+ case "number":
1323
+ return "number";
1324
+ case "boolean":
1325
+ return "boolean";
1326
+ default:
1327
+ return null;
1328
+ }
1329
+ }
1330
+ function readConstraints(constraintsMap) {
1331
+ const out = {};
1332
+ for (const [name, value] of constraintsMap.entries()) {
1333
+ if (!(value instanceof Y3.Map)) continue;
1334
+ let fields = [];
1335
+ const rawFields = value.get("fields");
1336
+ if (typeof rawFields === "string") {
1337
+ try {
1338
+ fields = JSON.parse(rawFields);
1339
+ } catch {
1340
+ }
1341
+ }
1342
+ out[name] = {
1343
+ type: value.get("type") ?? "unknown",
1344
+ fields
1345
+ };
1346
+ }
1347
+ return out;
1348
+ }
1349
+ function readRelationships(relsMap) {
1350
+ const out = {};
1351
+ for (const [name, value] of relsMap.entries()) {
1352
+ if (!(value instanceof Y3.Map)) continue;
1353
+ const rel = {};
1354
+ for (const [k, v] of value.entries()) {
1355
+ rel[k] = v;
1356
+ }
1357
+ out[name] = rel;
1358
+ }
1359
+ return out;
1360
+ }
1361
+ var CAMEL_TO_SNAKE = {
1362
+ autoAssign: "auto_assign",
1363
+ maxLength: "max_length",
1364
+ maxCount: "max_count",
1365
+ relatedIdField: "related_id_field",
1366
+ joinModel: "join_model",
1367
+ joinModelLocalField: "join_model_local_field",
1368
+ joinModelRelatedField: "join_model_related_field",
1369
+ joinModelOrderByField: "join_model_order_by_field",
1370
+ joinModelOrderDirection: "join_model_order_direction",
1371
+ orderByField: "order_by_field",
1372
+ orderDirection: "order_direction"
1373
+ };
1374
+ function toSnake(key) {
1375
+ return CAMEL_TO_SNAKE[key] ?? key;
1376
+ }
1377
+ function tomlValue(v) {
1378
+ if (typeof v === "string") {
1379
+ return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
1380
+ }
1381
+ return String(v);
1382
+ }
1383
+ function schemaToToml(schema) {
1384
+ const lines = [];
1385
+ for (const [modelName, model] of Object.entries(schema.models)) {
1386
+ if (lines.length > 0) lines.push("", "");
1387
+ lines.push(`[models.${modelName}]`);
1388
+ for (const [fieldName, field] of Object.entries(model.fields)) {
1389
+ lines.push("");
1390
+ lines.push(`[models.${modelName}.fields.${fieldName}]`);
1391
+ lines.push(`type = ${tomlValue(field.type)}`);
1392
+ if (field.autoAssign) lines.push("auto_assign = true");
1393
+ if (field.indexed) lines.push("indexed = true");
1394
+ if (field.unique) lines.push("unique = true");
1395
+ if (field.required) lines.push("required = true");
1396
+ if (field.maxLength !== void 0) lines.push(`max_length = ${field.maxLength}`);
1397
+ if (field.maxCount !== void 0) lines.push(`max_count = ${field.maxCount}`);
1398
+ if (field.default !== void 0) lines.push(`default = ${tomlValue(field.default)}`);
1399
+ }
1400
+ if (model.relationships) {
1401
+ for (const [relName, rel] of Object.entries(model.relationships)) {
1402
+ lines.push("");
1403
+ lines.push(`[models.${modelName}.relationships.${relName}]`);
1404
+ for (const [k, v] of Object.entries(rel)) {
1405
+ if (v === void 0) continue;
1406
+ lines.push(`${toSnake(k)} = ${tomlValue(v)}`);
1407
+ }
1408
+ }
1409
+ }
1410
+ if (model.constraints) {
1411
+ for (const [cName, c] of Object.entries(model.constraints)) {
1412
+ lines.push("");
1413
+ lines.push(`[[models.${modelName}.unique_constraints]]`);
1414
+ lines.push(`name = ${tomlValue(cName)}`);
1415
+ lines.push(`fields = [${c.fields.map((f) => tomlValue(f)).join(", ")}]`);
1416
+ }
1417
+ }
1418
+ }
1419
+ lines.push("");
1420
+ return lines.join("\n");
1421
+ }
1422
+
1423
+ // src/models/tomlLoader.ts
1424
+ var import_smol_toml = require("smol-toml");
1425
+
1426
+ // src/models/schema.ts
1427
+ init_BaseModel();
1428
+ init_ModelRegistry();
1429
+ function defineModelSchema(input) {
1430
+ const { name, fields } = input;
1431
+ const options = {
1432
+ name,
1433
+ uniqueConstraints: input.options?.uniqueConstraints ? [...input.options.uniqueConstraints] : void 0,
1434
+ relationships: input.options?.relationships
1435
+ };
1436
+ const schema = {
1437
+ name,
1438
+ fields,
1439
+ options,
1440
+ runtimeShape: void 0,
1441
+ buildRuntimeShape(modelClass) {
1442
+ if (schema.runtimeShape && schema.runtimeShape.class === modelClass) {
1443
+ return schema.runtimeShape;
1444
+ }
1445
+ const fieldsMap = /* @__PURE__ */ new Map();
1446
+ for (const [fieldName, fieldOptions] of Object.entries(fields)) {
1447
+ fieldsMap.set(fieldName, { ...fieldOptions });
1448
+ }
1449
+ const resolvedUniqueConstraints = resolveUniqueConstraints(
1450
+ name,
1451
+ fieldsMap,
1452
+ options.uniqueConstraints
1453
+ );
1454
+ schema.runtimeShape = {
1455
+ class: modelClass,
1456
+ options,
1457
+ fields: fieldsMap,
1458
+ resolvedUniqueConstraints
1459
+ };
1460
+ return schema.runtimeShape;
1461
+ }
1462
+ };
1463
+ return schema;
1464
+ }
1465
+ function resolveUniqueConstraints(modelName, fields, customConstraints) {
1466
+ const resolved = [];
1467
+ for (const [fieldName, options] of fields.entries()) {
1468
+ if (options.unique) {
1469
+ resolved.push({
1470
+ name: `${modelName}_${fieldName}_unique`,
1471
+ fields: [fieldName]
1472
+ });
1473
+ }
1474
+ }
1475
+ if (customConstraints) {
1476
+ for (const constraint of customConstraints) {
1477
+ const missingField = constraint.fields.find(
1478
+ (field) => !fields.has(field)
1479
+ );
1480
+ if (missingField) {
1481
+ console.warn(
1482
+ `[defineModelSchema] Unique constraint "${constraint.name}" for model "${modelName}" references unknown field "${missingField}". Skipping.`
1483
+ );
1484
+ continue;
1485
+ }
1486
+ if (resolved.some((item) => item.name === constraint.name)) {
1487
+ console.warn(
1488
+ `[defineModelSchema] Duplicate unique constraint name "${constraint.name}" in model "${modelName}".`
1489
+ );
1490
+ }
1491
+ resolved.push({
1492
+ name: constraint.name,
1493
+ fields: [...constraint.fields]
1494
+ });
1495
+ }
1496
+ }
1497
+ return resolved;
1498
+ }
1499
+
1500
+ // src/models/tomlLoader.ts
1501
+ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
1502
+ "string",
1503
+ "number",
1504
+ "boolean",
1505
+ "date",
1506
+ "id",
1507
+ "stringset"
1508
+ ]);
1509
+ function parseFieldOptions(raw) {
1510
+ if (!raw.type || !VALID_FIELD_TYPES.has(raw.type)) {
1511
+ throw new Error(
1512
+ `Invalid field type "${raw.type}". Must be one of: ${[...VALID_FIELD_TYPES].join(", ")}`
1513
+ );
1514
+ }
1515
+ const opts = { type: raw.type };
1516
+ if (raw.indexed === true) opts.indexed = true;
1517
+ if (raw.unique === true) opts.unique = true;
1518
+ if (raw.required === true) opts.required = true;
1519
+ if (raw.auto_assign === true) opts.autoAssign = true;
1520
+ if (raw.max_length !== void 0) opts.maxLength = raw.max_length;
1521
+ if (raw.max_count !== void 0) opts.maxCount = raw.max_count;
1522
+ if (raw.default !== void 0) opts.default = raw.default;
1523
+ return opts;
1524
+ }
1525
+ function requireField(raw, field, context) {
1526
+ if (!raw[field]) {
1527
+ throw new Error(`Relationship ${context}: missing required field "${field}"`);
1528
+ }
1529
+ }
1530
+ function parseRelationship(raw) {
1531
+ const type = raw.type;
1532
+ if (type === "refersTo") {
1533
+ requireField(raw, "model", "refersTo");
1534
+ requireField(raw, "related_id_field", "refersTo");
1535
+ return {
1536
+ type: "refersTo",
1537
+ model: raw.model,
1538
+ relatedIdField: raw.related_id_field
1539
+ };
1540
+ }
1541
+ if (type === "hasMany") {
1542
+ requireField(raw, "model", "hasMany");
1543
+ requireField(raw, "related_id_field", "hasMany");
1544
+ const rel = {
1545
+ type: "hasMany",
1546
+ model: raw.model,
1547
+ relatedIdField: raw.related_id_field
1548
+ };
1549
+ if (raw.order_by_field) rel.orderByField = raw.order_by_field;
1550
+ if (raw.order_direction) rel.orderDirection = raw.order_direction;
1551
+ return rel;
1552
+ }
1553
+ if (type === "hasManyThrough") {
1554
+ requireField(raw, "model", "hasManyThrough");
1555
+ requireField(raw, "join_model", "hasManyThrough");
1556
+ requireField(raw, "join_model_local_field", "hasManyThrough");
1557
+ requireField(raw, "join_model_related_field", "hasManyThrough");
1558
+ const rel = {
1559
+ type: "hasManyThrough",
1560
+ model: raw.model,
1561
+ joinModel: raw.join_model,
1562
+ joinModelLocalField: raw.join_model_local_field,
1563
+ joinModelRelatedField: raw.join_model_related_field
1564
+ };
1565
+ if (raw.join_model_order_by_field)
1566
+ rel.joinModelOrderByField = raw.join_model_order_by_field;
1567
+ if (raw.join_model_order_direction)
1568
+ rel.joinModelOrderDirection = raw.join_model_order_direction;
1569
+ return rel;
1570
+ }
1571
+ throw new Error(`Unknown relationship type: ${type}`);
1572
+ }
1573
+ function loadSchemaFromTomlString(tomlString) {
1574
+ const parsed = (0, import_smol_toml.parse)(tomlString);
1575
+ const models = parsed.models;
1576
+ if (!models || typeof models !== "object") {
1577
+ throw new Error("TOML schema must have a [models] section");
1578
+ }
1579
+ const schemas = [];
1580
+ for (const [modelName, modelDef] of Object.entries(models)) {
1581
+ const fields = {};
1582
+ if (modelDef.fields) {
1583
+ for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
1584
+ fields[fieldName] = parseFieldOptions(fieldDef);
1585
+ }
1586
+ }
1587
+ let relationships;
1588
+ if (modelDef.relationships) {
1589
+ relationships = {};
1590
+ for (const [relName, relDef] of Object.entries(
1591
+ modelDef.relationships
1592
+ )) {
1593
+ relationships[relName] = parseRelationship(relDef);
1594
+ }
1595
+ }
1596
+ let uniqueConstraints;
1597
+ if (modelDef.unique_constraints) {
1598
+ uniqueConstraints = [];
1599
+ for (const raw of modelDef.unique_constraints) {
1600
+ uniqueConstraints.push({
1601
+ name: raw.name,
1602
+ fields: [...raw.fields]
1603
+ });
1604
+ }
1605
+ }
1606
+ schemas.push(
1607
+ defineModelSchema({
1608
+ name: modelName,
1609
+ fields,
1610
+ options: {
1611
+ uniqueConstraints,
1612
+ relationships
1613
+ }
1614
+ })
1615
+ );
1616
+ }
1617
+ return schemas;
1618
+ }
1619
+
1620
+ // src/cloudflare.ts
1621
+ init_metaSync();