js-bao 0.3.0 → 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.
- package/dist/browser.cjs +367 -1
- package/dist/browser.d.cts +126 -1
- package/dist/browser.d.ts +126 -1
- package/dist/browser.js +367 -1
- package/dist/client.cjs +16 -11
- package/dist/client.d.cts +3 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.js +16 -11
- package/dist/cloudflare-do.cjs +937 -286
- package/dist/cloudflare-do.d.cts +517 -15
- package/dist/cloudflare-do.d.ts +517 -15
- package/dist/cloudflare-do.js +928 -286
- package/dist/cloudflare.cjs +573 -18
- package/dist/cloudflare.d.cts +147 -2
- package/dist/cloudflare.d.ts +147 -2
- package/dist/cloudflare.js +573 -18
- package/dist/codegen.cjs +6 -6
- package/dist/index.cjs +19 -10
- package/dist/index.js +19 -10
- package/dist/node.cjs +388 -6
- package/dist/node.d.cts +131 -1
- package/dist/node.d.ts +131 -1
- package/dist/node.js +377 -5
- package/package.json +6 -6
package/dist/cloudflare.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|