js-bao 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.cjs +179 -48
- package/dist/browser.d.cts +45 -1
- package/dist/browser.d.ts +45 -1
- package/dist/browser.js +179 -48
- package/dist/client.d.cts +32 -0
- package/dist/client.d.ts +32 -0
- package/dist/cloudflare-do.cjs +70 -5
- package/dist/cloudflare-do.d.cts +47 -3
- package/dist/cloudflare-do.d.ts +47 -3
- package/dist/cloudflare-do.js +70 -5
- package/dist/cloudflare.cjs +70 -5
- package/dist/cloudflare.d.cts +45 -1
- package/dist/cloudflare.d.ts +45 -1
- package/dist/cloudflare.js +70 -5
- package/dist/codegen-v2.cjs +1766 -0
- package/dist/codegen-v2.d.cts +1 -0
- package/dist/codegen.cjs +7 -10
- package/dist/index.cjs +181 -50
- package/dist/index.d.cts +46 -2
- package/dist/index.d.ts +46 -2
- package/dist/index.js +181 -50
- package/dist/node.cjs +181 -50
- package/dist/node.d.cts +46 -2
- package/dist/node.d.ts +46 -2
- package/dist/node.js +181 -50
- package/package.json +7 -10
package/dist/node.cjs
CHANGED
|
@@ -2572,6 +2572,9 @@ var init_BaseModel = __esm({
|
|
|
2572
2572
|
static connectedDocuments = /* @__PURE__ */ new Map();
|
|
2573
2573
|
static documentYMaps = /* @__PURE__ */ new Map();
|
|
2574
2574
|
// Maps docId to YMap for that document
|
|
2575
|
+
// Tracks nested-Y.Map stringset fields we've already attached observers to,
|
|
2576
|
+
// so attachStringSetObserversToRecord() is idempotent across re-entry.
|
|
2577
|
+
static _observedStringSetMaps = /* @__PURE__ */ new WeakSet();
|
|
2575
2578
|
// Copy-on-write state management
|
|
2576
2579
|
_localChanges = null;
|
|
2577
2580
|
_isDirty = false;
|
|
@@ -3091,16 +3094,15 @@ var init_BaseModel = __esm({
|
|
|
3091
3094
|
this._isDirty = true;
|
|
3092
3095
|
}
|
|
3093
3096
|
getStringSetCurrentValues(fieldName) {
|
|
3094
|
-
|
|
3095
|
-
if (yjsData && typeof yjsData === "object") {
|
|
3096
|
-
return Object.keys(yjsData);
|
|
3097
|
-
}
|
|
3098
|
-
return [];
|
|
3097
|
+
return this.getStringSetFromYjs(fieldName);
|
|
3099
3098
|
}
|
|
3100
3099
|
getStringSetFromYjs(fieldName) {
|
|
3101
3100
|
const yjsData = this.getFromYjs(fieldName);
|
|
3101
|
+
if (yjsData instanceof Y2.Map) {
|
|
3102
|
+
return Array.from(yjsData.keys());
|
|
3103
|
+
}
|
|
3102
3104
|
if (yjsData && typeof yjsData === "object") {
|
|
3103
|
-
return Object.keys(yjsData);
|
|
3105
|
+
return Object.keys(yjsData).filter((k) => Boolean(yjsData[k]));
|
|
3104
3106
|
}
|
|
3105
3107
|
return [];
|
|
3106
3108
|
}
|
|
@@ -3711,7 +3713,7 @@ var init_BaseModel = __esm({
|
|
|
3711
3713
|
Logger.debug(
|
|
3712
3714
|
`[_diffWithYjsData] No unsaved changes, returning empty diff`
|
|
3713
3715
|
);
|
|
3714
|
-
return { added: {}, modified: {}, removed: [] };
|
|
3716
|
+
return { added: {}, modified: {}, removed: [], stringSetChanges: {} };
|
|
3715
3717
|
}
|
|
3716
3718
|
const modelConstructor = this.constructor;
|
|
3717
3719
|
const schema = modelConstructor.getSchema();
|
|
@@ -3742,6 +3744,7 @@ var init_BaseModel = __esm({
|
|
|
3742
3744
|
const added = {};
|
|
3743
3745
|
const modified = {};
|
|
3744
3746
|
const removed = [];
|
|
3747
|
+
const stringSetChanges = {};
|
|
3745
3748
|
if (!recordYMap) {
|
|
3746
3749
|
Logger.debug(
|
|
3747
3750
|
`[_diffWithYjsData] No existing recordYMap, treating all local changes as 'added'`
|
|
@@ -3750,11 +3753,12 @@ var init_BaseModel = __esm({
|
|
|
3750
3753
|
for (const [key, value] of Object.entries(this._localChanges)) {
|
|
3751
3754
|
Logger.debug(`[_diffWithYjsData] Adding field '${key}': ${value}`);
|
|
3752
3755
|
if (value && value.type === "stringset") {
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
+
if (value.additions.size > 0 || value.removals.size > 0) {
|
|
3757
|
+
stringSetChanges[key] = {
|
|
3758
|
+
additions: value.additions,
|
|
3759
|
+
removals: value.removals
|
|
3760
|
+
};
|
|
3756
3761
|
}
|
|
3757
|
-
added[key] = stringSetData;
|
|
3758
3762
|
} else {
|
|
3759
3763
|
added[key] = value;
|
|
3760
3764
|
}
|
|
@@ -3763,9 +3767,10 @@ var init_BaseModel = __esm({
|
|
|
3763
3767
|
Logger.debug(`[_diffWithYjsData] Final diff for new record:`, {
|
|
3764
3768
|
added,
|
|
3765
3769
|
modified: {},
|
|
3766
|
-
removed: []
|
|
3770
|
+
removed: [],
|
|
3771
|
+
stringSetChanges
|
|
3767
3772
|
});
|
|
3768
|
-
return { added, modified, removed: [] };
|
|
3773
|
+
return { added, modified, removed: [], stringSetChanges };
|
|
3769
3774
|
}
|
|
3770
3775
|
Logger.debug(
|
|
3771
3776
|
`[_diffWithYjsData] Existing record found, comparing local changes with Y.js data`
|
|
@@ -3782,21 +3787,11 @@ var init_BaseModel = __esm({
|
|
|
3782
3787
|
`[_diffWithYjsData] Processing field '${key}' with local value: ${localValue}`
|
|
3783
3788
|
);
|
|
3784
3789
|
if (localValue && localValue.type === "stringset") {
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
newStringSetData[addition] = true;
|
|
3791
|
-
}
|
|
3792
|
-
for (const removal of localValue.removals) {
|
|
3793
|
-
delete newStringSetData[removal];
|
|
3794
|
-
}
|
|
3795
|
-
const yjsValue = recordYMap.get(key);
|
|
3796
|
-
if (yjsValue === void 0) {
|
|
3797
|
-
added[key] = newStringSetData;
|
|
3798
|
-
} else if (!this._deepEqual(yjsValue, newStringSetData)) {
|
|
3799
|
-
modified[key] = newStringSetData;
|
|
3790
|
+
if (localValue.additions.size > 0 || localValue.removals.size > 0) {
|
|
3791
|
+
stringSetChanges[key] = {
|
|
3792
|
+
additions: localValue.additions,
|
|
3793
|
+
removals: localValue.removals
|
|
3794
|
+
};
|
|
3800
3795
|
}
|
|
3801
3796
|
} else {
|
|
3802
3797
|
const yjsValue = recordYMap.get(key);
|
|
@@ -3822,14 +3817,43 @@ var init_BaseModel = __esm({
|
|
|
3822
3817
|
Logger.debug(`[_diffWithYjsData] Final diff result:`, {
|
|
3823
3818
|
added,
|
|
3824
3819
|
modified,
|
|
3825
|
-
removed
|
|
3820
|
+
removed,
|
|
3821
|
+
stringSetChanges
|
|
3826
3822
|
});
|
|
3827
3823
|
Logger.verbose(`[${modelConstructor.name}] Diff for ${this.id}:`, {
|
|
3828
3824
|
added: Object.keys(added),
|
|
3829
3825
|
modified: Object.keys(modified),
|
|
3830
|
-
removed
|
|
3826
|
+
removed,
|
|
3827
|
+
stringSetChanges: Object.keys(stringSetChanges)
|
|
3831
3828
|
});
|
|
3832
|
-
return { added, modified, removed };
|
|
3829
|
+
return { added, modified, removed, stringSetChanges };
|
|
3830
|
+
}
|
|
3831
|
+
/**
|
|
3832
|
+
* Apply a stringset change set to the parent record's Y.Map by mutating a
|
|
3833
|
+
* nested Y.Map keyed by member. Migrates from a legacy plain-object value
|
|
3834
|
+
* if the field hasn't been touched since the wire-format change in #561.
|
|
3835
|
+
*/
|
|
3836
|
+
applyStringSetChangeToYMap(recordYMap, fieldName, change) {
|
|
3837
|
+
let nested = recordYMap.get(fieldName);
|
|
3838
|
+
if (!(nested instanceof Y2.Map)) {
|
|
3839
|
+
const migrated = new Y2.Map();
|
|
3840
|
+
if (nested && typeof nested === "object") {
|
|
3841
|
+
for (const [member, marker] of Object.entries(
|
|
3842
|
+
nested
|
|
3843
|
+
)) {
|
|
3844
|
+
if (Boolean(marker)) migrated.set(member, true);
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
recordYMap.set(fieldName, migrated);
|
|
3848
|
+
nested = migrated;
|
|
3849
|
+
}
|
|
3850
|
+
const target = nested;
|
|
3851
|
+
for (const member of change.additions) {
|
|
3852
|
+
target.set(member, true);
|
|
3853
|
+
}
|
|
3854
|
+
for (const member of change.removals) {
|
|
3855
|
+
target.delete(member);
|
|
3856
|
+
}
|
|
3833
3857
|
}
|
|
3834
3858
|
/**
|
|
3835
3859
|
* Deep equality check for comparing field values
|
|
@@ -4068,7 +4092,7 @@ var init_BaseModel = __esm({
|
|
|
4068
4092
|
Logger.debug(`[${modelName}.save] About to calculate diff`);
|
|
4069
4093
|
const diff = this._diffWithYjsData();
|
|
4070
4094
|
Logger.debug(`[${modelName}.save] Diff result:`, diff);
|
|
4071
|
-
const hasChanges = Object.keys(diff.added).length > 0 || Object.keys(diff.modified).length > 0 || diff.removed.length > 0;
|
|
4095
|
+
const hasChanges = Object.keys(diff.added).length > 0 || Object.keys(diff.modified).length > 0 || diff.removed.length > 0 || Object.keys(diff.stringSetChanges).length > 0;
|
|
4072
4096
|
Logger.debug(`[${modelName}.save] hasChanges: ${hasChanges}`);
|
|
4073
4097
|
if (!hasChanges && isUpdate) {
|
|
4074
4098
|
Logger.verbose(
|
|
@@ -4186,6 +4210,16 @@ var init_BaseModel = __esm({
|
|
|
4186
4210
|
Logger.debug(`[${modelName}.save] Removing field '${key}'`);
|
|
4187
4211
|
recordYMap.delete(key);
|
|
4188
4212
|
}
|
|
4213
|
+
for (const [fieldName, change] of Object.entries(diff.stringSetChanges)) {
|
|
4214
|
+
Logger.debug(
|
|
4215
|
+
`[${modelName}.save] Applying stringset change to field '${fieldName}':`,
|
|
4216
|
+
{
|
|
4217
|
+
additions: Array.from(change.additions),
|
|
4218
|
+
removals: Array.from(change.removals)
|
|
4219
|
+
}
|
|
4220
|
+
);
|
|
4221
|
+
this.applyStringSetChangeToYMap(recordYMap, fieldName, change);
|
|
4222
|
+
}
|
|
4189
4223
|
Logger.debug(
|
|
4190
4224
|
`[${modelName}.save] After applying changes, recordYMap contents:`,
|
|
4191
4225
|
Object.fromEntries(recordYMap.entries())
|
|
@@ -5370,6 +5404,39 @@ var init_BaseModel = __esm({
|
|
|
5370
5404
|
_BaseModel.prototype.setValue = originalSetValue;
|
|
5371
5405
|
}
|
|
5372
5406
|
}
|
|
5407
|
+
/**
|
|
5408
|
+
* Attach a one-shot observer to a nested-Y.Map stringset field. Calls
|
|
5409
|
+
* notifyListeners() when the nested map's keys change (i.e. when a remote
|
|
5410
|
+
* member arrives via Y.applyUpdate, or a local per-member set/delete).
|
|
5411
|
+
* Idempotent — re-calling on the same Y.Map is a no-op.
|
|
5412
|
+
*/
|
|
5413
|
+
static observeStringSetMapOnce(nestedMap) {
|
|
5414
|
+
if (_BaseModel._observedStringSetMaps.has(nestedMap)) return;
|
|
5415
|
+
_BaseModel._observedStringSetMaps.add(nestedMap);
|
|
5416
|
+
const modelConstructor = this;
|
|
5417
|
+
nestedMap.observe(() => {
|
|
5418
|
+
Logger.verbose(
|
|
5419
|
+
`[${modelConstructor.name}] Nested stringset Y.Map changed; notifying listeners`
|
|
5420
|
+
);
|
|
5421
|
+
modelConstructor.notifyListeners();
|
|
5422
|
+
});
|
|
5423
|
+
}
|
|
5424
|
+
/**
|
|
5425
|
+
* Walk the schema's stringset fields on `recordYMap` and observe any nested
|
|
5426
|
+
* Y.Map values. Called from observer setup and from the parent observer body
|
|
5427
|
+
* so newly-arrived stringset Y.Maps (local migration or remote create) get
|
|
5428
|
+
* an observer too.
|
|
5429
|
+
*/
|
|
5430
|
+
static attachStringSetObserversToRecord(recordYMap, schema) {
|
|
5431
|
+
if (!schema?.fields) return;
|
|
5432
|
+
for (const [fieldKey, fieldOptions] of schema.fields) {
|
|
5433
|
+
if (fieldOptions?.type !== "stringset") continue;
|
|
5434
|
+
const value = recordYMap.get(fieldKey);
|
|
5435
|
+
if (value instanceof Y2.Map) {
|
|
5436
|
+
this.observeStringSetMapOnce(value);
|
|
5437
|
+
}
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
5373
5440
|
/**
|
|
5374
5441
|
* Sets up deep observation on a nested YMap to sync field-level changes to the database
|
|
5375
5442
|
*/
|
|
@@ -5386,11 +5453,13 @@ var init_BaseModel = __esm({
|
|
|
5386
5453
|
Logger.verbose(
|
|
5387
5454
|
`[${modelName}] Setting up nested YMap observer for record ${recordId}`
|
|
5388
5455
|
);
|
|
5456
|
+
modelConstructor.attachStringSetObserversToRecord(recordYMap, schema);
|
|
5389
5457
|
recordYMap.observe(async (event) => {
|
|
5390
5458
|
Logger.verbose(
|
|
5391
5459
|
`[${modelName}] Nested YMap change detected for record ${recordId}:`,
|
|
5392
5460
|
event
|
|
5393
5461
|
);
|
|
5462
|
+
modelConstructor.attachStringSetObserversToRecord(recordYMap, schema);
|
|
5394
5463
|
const currentDbInstance = _BaseModel.dbInstance;
|
|
5395
5464
|
if (!currentDbInstance) {
|
|
5396
5465
|
Logger.error(
|
|
@@ -5464,11 +5533,13 @@ var init_BaseModel = __esm({
|
|
|
5464
5533
|
Logger.verbose(
|
|
5465
5534
|
`[${modelName}] Setting up nested YMap observer for record ${recordId} in document ${docId}`
|
|
5466
5535
|
);
|
|
5536
|
+
modelConstructor.attachStringSetObserversToRecord(recordYMap, schema);
|
|
5467
5537
|
recordYMap.observe(async (event) => {
|
|
5468
5538
|
Logger.verbose(
|
|
5469
5539
|
`[${modelName}] Nested YMap change detected for record ${recordId} in document ${docId}:`,
|
|
5470
5540
|
event
|
|
5471
5541
|
);
|
|
5542
|
+
modelConstructor.attachStringSetObserversToRecord(recordYMap, schema);
|
|
5472
5543
|
const currentDbInstance = _BaseModel.dbInstance;
|
|
5473
5544
|
if (!currentDbInstance) {
|
|
5474
5545
|
Logger.error(
|
|
@@ -6965,6 +7036,7 @@ function defineModelSchema(input) {
|
|
|
6965
7036
|
const { name, fields } = input;
|
|
6966
7037
|
const options = {
|
|
6967
7038
|
name,
|
|
7039
|
+
className: input.options?.className,
|
|
6968
7040
|
uniqueConstraints: input.options?.uniqueConstraints ? [...input.options.uniqueConstraints] : void 0,
|
|
6969
7041
|
relationships: input.options?.relationships
|
|
6970
7042
|
};
|
|
@@ -7064,18 +7136,13 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
|
|
|
7064
7136
|
function attachSchemaToClass(modelClass, schema) {
|
|
7065
7137
|
modelClass.schema = schema;
|
|
7066
7138
|
modelClass.modelName = schema.options.name;
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
value: function() {
|
|
7070
|
-
return schema.buildRuntimeShape(modelClass);
|
|
7071
|
-
},
|
|
7072
|
-
writable: false
|
|
7073
|
-
});
|
|
7074
|
-
} else {
|
|
7075
|
-
modelClass.getSchema = function() {
|
|
7139
|
+
Object.defineProperty(modelClass, "getSchema", {
|
|
7140
|
+
value: function() {
|
|
7076
7141
|
return schema.buildRuntimeShape(modelClass);
|
|
7077
|
-
}
|
|
7078
|
-
|
|
7142
|
+
},
|
|
7143
|
+
writable: false,
|
|
7144
|
+
configurable: true
|
|
7145
|
+
});
|
|
7079
7146
|
const runtimeShape = schema.buildRuntimeShape(modelClass);
|
|
7080
7147
|
BaseModel2.attachFieldAccessors(modelClass, runtimeShape.fields);
|
|
7081
7148
|
return runtimeShape;
|
|
@@ -7308,12 +7375,52 @@ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
7308
7375
|
"id",
|
|
7309
7376
|
"stringset"
|
|
7310
7377
|
]);
|
|
7311
|
-
|
|
7378
|
+
var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
7379
|
+
"type",
|
|
7380
|
+
"indexed",
|
|
7381
|
+
"unique",
|
|
7382
|
+
"required",
|
|
7383
|
+
"auto_assign",
|
|
7384
|
+
"max_length",
|
|
7385
|
+
"max_count",
|
|
7386
|
+
"default"
|
|
7387
|
+
]);
|
|
7388
|
+
var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
|
|
7389
|
+
"fields",
|
|
7390
|
+
"relationships",
|
|
7391
|
+
"unique_constraints",
|
|
7392
|
+
"class_name"
|
|
7393
|
+
]);
|
|
7394
|
+
var KNOWN_RELATIONSHIP_KEYS = /* @__PURE__ */ new Set([
|
|
7395
|
+
"type",
|
|
7396
|
+
"model",
|
|
7397
|
+
"related_id_field",
|
|
7398
|
+
"join_model",
|
|
7399
|
+
"join_model_local_field",
|
|
7400
|
+
"join_model_related_field",
|
|
7401
|
+
"order_by_field",
|
|
7402
|
+
"order_direction",
|
|
7403
|
+
"join_model_order_by_field",
|
|
7404
|
+
"join_model_order_direction"
|
|
7405
|
+
]);
|
|
7406
|
+
var KNOWN_UNIQUE_CONSTRAINT_KEYS = /* @__PURE__ */ new Set(["name", "fields"]);
|
|
7407
|
+
function checkUnknownKeys(raw, known, context, strict) {
|
|
7408
|
+
if (!strict) return;
|
|
7409
|
+
for (const key of Object.keys(raw)) {
|
|
7410
|
+
if (!known.has(key)) {
|
|
7411
|
+
throw new Error(
|
|
7412
|
+
`${context}: unknown key "${key}". Allowed: ${[...known].join(", ")}`
|
|
7413
|
+
);
|
|
7414
|
+
}
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
function parseFieldOptions(raw, context, strict) {
|
|
7312
7418
|
if (!raw.type || !VALID_FIELD_TYPES.has(raw.type)) {
|
|
7313
7419
|
throw new Error(
|
|
7314
7420
|
`Invalid field type "${raw.type}". Must be one of: ${[...VALID_FIELD_TYPES].join(", ")}`
|
|
7315
7421
|
);
|
|
7316
7422
|
}
|
|
7423
|
+
checkUnknownKeys(raw, KNOWN_FIELD_KEYS, context, strict);
|
|
7317
7424
|
const opts = { type: raw.type };
|
|
7318
7425
|
if (raw.indexed === true) opts.indexed = true;
|
|
7319
7426
|
if (raw.unique === true) opts.unique = true;
|
|
@@ -7329,8 +7436,9 @@ function requireField(raw, field, context) {
|
|
|
7329
7436
|
throw new Error(`Relationship ${context}: missing required field "${field}"`);
|
|
7330
7437
|
}
|
|
7331
7438
|
}
|
|
7332
|
-
function parseRelationship(raw) {
|
|
7439
|
+
function parseRelationship(raw, context, strict) {
|
|
7333
7440
|
const type = raw.type;
|
|
7441
|
+
checkUnknownKeys(raw, KNOWN_RELATIONSHIP_KEYS, context, strict);
|
|
7334
7442
|
if (type === "refersTo") {
|
|
7335
7443
|
requireField(raw, "model", "refersTo");
|
|
7336
7444
|
requireField(raw, "related_id_field", "refersTo");
|
|
@@ -7372,7 +7480,8 @@ function parseRelationship(raw) {
|
|
|
7372
7480
|
}
|
|
7373
7481
|
throw new Error(`Unknown relationship type: ${type}`);
|
|
7374
7482
|
}
|
|
7375
|
-
function loadSchemaFromTomlString(tomlString) {
|
|
7483
|
+
function loadSchemaFromTomlString(tomlString, options = {}) {
|
|
7484
|
+
const strict = options.strict !== false;
|
|
7376
7485
|
const parsed = (0, import_smol_toml.parse)(tomlString);
|
|
7377
7486
|
const models = parsed.models;
|
|
7378
7487
|
if (!models || typeof models !== "object") {
|
|
@@ -7380,10 +7489,20 @@ function loadSchemaFromTomlString(tomlString) {
|
|
|
7380
7489
|
}
|
|
7381
7490
|
const schemas = [];
|
|
7382
7491
|
for (const [modelName, modelDef] of Object.entries(models)) {
|
|
7492
|
+
checkUnknownKeys(
|
|
7493
|
+
modelDef,
|
|
7494
|
+
KNOWN_MODEL_KEYS,
|
|
7495
|
+
`[models.${modelName}]`,
|
|
7496
|
+
strict
|
|
7497
|
+
);
|
|
7383
7498
|
const fields = {};
|
|
7384
7499
|
if (modelDef.fields) {
|
|
7385
7500
|
for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
|
|
7386
|
-
fields[fieldName] = parseFieldOptions(
|
|
7501
|
+
fields[fieldName] = parseFieldOptions(
|
|
7502
|
+
fieldDef,
|
|
7503
|
+
`[models.${modelName}.fields.${fieldName}]`,
|
|
7504
|
+
strict
|
|
7505
|
+
);
|
|
7387
7506
|
}
|
|
7388
7507
|
}
|
|
7389
7508
|
let relationships;
|
|
@@ -7392,24 +7511,36 @@ function loadSchemaFromTomlString(tomlString) {
|
|
|
7392
7511
|
for (const [relName, relDef] of Object.entries(
|
|
7393
7512
|
modelDef.relationships
|
|
7394
7513
|
)) {
|
|
7395
|
-
relationships[relName] = parseRelationship(
|
|
7514
|
+
relationships[relName] = parseRelationship(
|
|
7515
|
+
relDef,
|
|
7516
|
+
`[models.${modelName}.relationships.${relName}]`,
|
|
7517
|
+
strict
|
|
7518
|
+
);
|
|
7396
7519
|
}
|
|
7397
7520
|
}
|
|
7398
7521
|
let uniqueConstraints;
|
|
7399
7522
|
if (modelDef.unique_constraints) {
|
|
7400
7523
|
uniqueConstraints = [];
|
|
7401
7524
|
for (const raw of modelDef.unique_constraints) {
|
|
7525
|
+
checkUnknownKeys(
|
|
7526
|
+
raw,
|
|
7527
|
+
KNOWN_UNIQUE_CONSTRAINT_KEYS,
|
|
7528
|
+
`[[models.${modelName}.unique_constraints]]`,
|
|
7529
|
+
strict
|
|
7530
|
+
);
|
|
7402
7531
|
uniqueConstraints.push({
|
|
7403
7532
|
name: raw.name,
|
|
7404
7533
|
fields: [...raw.fields]
|
|
7405
7534
|
});
|
|
7406
7535
|
}
|
|
7407
7536
|
}
|
|
7537
|
+
const className = typeof modelDef.class_name === "string" ? modelDef.class_name : void 0;
|
|
7408
7538
|
schemas.push(
|
|
7409
7539
|
defineModelSchema({
|
|
7410
7540
|
name: modelName,
|
|
7411
7541
|
fields,
|
|
7412
7542
|
options: {
|
|
7543
|
+
className,
|
|
7413
7544
|
uniqueConstraints,
|
|
7414
7545
|
relationships
|
|
7415
7546
|
}
|
|
@@ -7418,10 +7549,10 @@ function loadSchemaFromTomlString(tomlString) {
|
|
|
7418
7549
|
}
|
|
7419
7550
|
return schemas;
|
|
7420
7551
|
}
|
|
7421
|
-
async function loadSchemaFromToml(filePath) {
|
|
7552
|
+
async function loadSchemaFromToml(filePath, options = {}) {
|
|
7422
7553
|
const { readFile } = await import("fs/promises");
|
|
7423
7554
|
const content = await readFile(filePath, "utf-8");
|
|
7424
|
-
return loadSchemaFromTomlString(content);
|
|
7555
|
+
return loadSchemaFromTomlString(content, options);
|
|
7425
7556
|
}
|
|
7426
7557
|
|
|
7427
7558
|
// src/node.ts
|
package/dist/node.d.cts
CHANGED
|
@@ -40,6 +40,13 @@ interface UniqueConstraintConfig {
|
|
|
40
40
|
}
|
|
41
41
|
interface ModelOptions {
|
|
42
42
|
name: string;
|
|
43
|
+
/**
|
|
44
|
+
* Optional PascalCase class name. Used by the v2 codegen to drive
|
|
45
|
+
* generated TypeScript class names (and relationship method names that
|
|
46
|
+
* derive from a target's class name). When absent, the v2 codegen
|
|
47
|
+
* falls back to suffix-based singularization of `name`.
|
|
48
|
+
*/
|
|
49
|
+
className?: string;
|
|
43
50
|
uniqueConstraints?: UniqueConstraintConfig[];
|
|
44
51
|
relationships?: Record<string, RelationshipConfig>;
|
|
45
52
|
}
|
|
@@ -447,6 +454,7 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
447
454
|
permissionHint: DocumentPermissionHint;
|
|
448
455
|
}>;
|
|
449
456
|
protected static documentYMaps: Map<string, Y.Map<any>>;
|
|
457
|
+
private static _observedStringSetMaps;
|
|
450
458
|
private _localChanges;
|
|
451
459
|
private _isDirty;
|
|
452
460
|
private _isLoadingFromYjs;
|
|
@@ -521,7 +529,17 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
521
529
|
added: Record<string, any>;
|
|
522
530
|
modified: Record<string, any>;
|
|
523
531
|
removed: string[];
|
|
532
|
+
stringSetChanges: Record<string, {
|
|
533
|
+
additions: Set<string>;
|
|
534
|
+
removals: Set<string>;
|
|
535
|
+
}>;
|
|
524
536
|
};
|
|
537
|
+
/**
|
|
538
|
+
* Apply a stringset change set to the parent record's Y.Map by mutating a
|
|
539
|
+
* nested Y.Map keyed by member. Migrates from a legacy plain-object value
|
|
540
|
+
* if the field hasn't been touched since the wire-format change in #561.
|
|
541
|
+
*/
|
|
542
|
+
private applyStringSetChangeToYMap;
|
|
525
543
|
/**
|
|
526
544
|
* Deep equality check for comparing field values
|
|
527
545
|
*/
|
|
@@ -623,6 +641,20 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
623
641
|
* Execute a callback with automatic transaction handling for all modified models
|
|
624
642
|
*/
|
|
625
643
|
static withTransaction<T>(callback: () => Promise<T> | T): Promise<T>;
|
|
644
|
+
/**
|
|
645
|
+
* Attach a one-shot observer to a nested-Y.Map stringset field. Calls
|
|
646
|
+
* notifyListeners() when the nested map's keys change (i.e. when a remote
|
|
647
|
+
* member arrives via Y.applyUpdate, or a local per-member set/delete).
|
|
648
|
+
* Idempotent — re-calling on the same Y.Map is a no-op.
|
|
649
|
+
*/
|
|
650
|
+
protected static observeStringSetMapOnce(nestedMap: Y.Map<any>): void;
|
|
651
|
+
/**
|
|
652
|
+
* Walk the schema's stringset fields on `recordYMap` and observe any nested
|
|
653
|
+
* Y.Map values. Called from observer setup and from the parent observer body
|
|
654
|
+
* so newly-arrived stringset Y.Maps (local migration or remote create) get
|
|
655
|
+
* an observer too.
|
|
656
|
+
*/
|
|
657
|
+
protected static attachStringSetObserversToRecord(recordYMap: Y.Map<any>, schema: any): void;
|
|
626
658
|
/**
|
|
627
659
|
* Sets up deep observation on a nested YMap to sync field-level changes to the database
|
|
628
660
|
*/
|
|
@@ -974,15 +1006,27 @@ declare function schemaToToml(schema: DiscoveredSchema): string;
|
|
|
974
1006
|
* - Compound unique constraints are [[models.*.unique_constraints]]
|
|
975
1007
|
*/
|
|
976
1008
|
|
|
1009
|
+
interface LoadSchemaOptions {
|
|
1010
|
+
/**
|
|
1011
|
+
* When true (default), throw on unknown keys at the model, field,
|
|
1012
|
+
* relationship, and unique-constraint level. When false, unknown
|
|
1013
|
+
* keys are silently ignored (legacy behavior).
|
|
1014
|
+
*/
|
|
1015
|
+
strict?: boolean;
|
|
1016
|
+
}
|
|
977
1017
|
/**
|
|
978
1018
|
* Parse a TOML string and return an array of DefinedModelSchema objects.
|
|
1019
|
+
*
|
|
1020
|
+
* By default operates in strict mode: unknown keys at the model, field,
|
|
1021
|
+
* relationship, or unique-constraint level cause an error. Pass
|
|
1022
|
+
* `{ strict: false }` to silently ignore unknown keys (legacy behavior).
|
|
979
1023
|
*/
|
|
980
|
-
declare function loadSchemaFromTomlString(tomlString: string): DefinedModelSchema[];
|
|
1024
|
+
declare function loadSchemaFromTomlString(tomlString: string, options?: LoadSchemaOptions): DefinedModelSchema[];
|
|
981
1025
|
/**
|
|
982
1026
|
* Read a TOML file from disk and return an array of DefinedModelSchema objects.
|
|
983
1027
|
* Only available in Node.js environments.
|
|
984
1028
|
*/
|
|
985
|
-
declare function loadSchemaFromToml(filePath: string): Promise<DefinedModelSchema[]>;
|
|
1029
|
+
declare function loadSchemaFromToml(filePath: string, options?: LoadSchemaOptions): Promise<DefinedModelSchema[]>;
|
|
986
1030
|
|
|
987
1031
|
/**
|
|
988
1032
|
* Meta Sync — writes _meta_* YMaps into a YDoc.
|
package/dist/node.d.ts
CHANGED
|
@@ -40,6 +40,13 @@ interface UniqueConstraintConfig {
|
|
|
40
40
|
}
|
|
41
41
|
interface ModelOptions {
|
|
42
42
|
name: string;
|
|
43
|
+
/**
|
|
44
|
+
* Optional PascalCase class name. Used by the v2 codegen to drive
|
|
45
|
+
* generated TypeScript class names (and relationship method names that
|
|
46
|
+
* derive from a target's class name). When absent, the v2 codegen
|
|
47
|
+
* falls back to suffix-based singularization of `name`.
|
|
48
|
+
*/
|
|
49
|
+
className?: string;
|
|
43
50
|
uniqueConstraints?: UniqueConstraintConfig[];
|
|
44
51
|
relationships?: Record<string, RelationshipConfig>;
|
|
45
52
|
}
|
|
@@ -447,6 +454,7 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
447
454
|
permissionHint: DocumentPermissionHint;
|
|
448
455
|
}>;
|
|
449
456
|
protected static documentYMaps: Map<string, Y.Map<any>>;
|
|
457
|
+
private static _observedStringSetMaps;
|
|
450
458
|
private _localChanges;
|
|
451
459
|
private _isDirty;
|
|
452
460
|
private _isLoadingFromYjs;
|
|
@@ -521,7 +529,17 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
521
529
|
added: Record<string, any>;
|
|
522
530
|
modified: Record<string, any>;
|
|
523
531
|
removed: string[];
|
|
532
|
+
stringSetChanges: Record<string, {
|
|
533
|
+
additions: Set<string>;
|
|
534
|
+
removals: Set<string>;
|
|
535
|
+
}>;
|
|
524
536
|
};
|
|
537
|
+
/**
|
|
538
|
+
* Apply a stringset change set to the parent record's Y.Map by mutating a
|
|
539
|
+
* nested Y.Map keyed by member. Migrates from a legacy plain-object value
|
|
540
|
+
* if the field hasn't been touched since the wire-format change in #561.
|
|
541
|
+
*/
|
|
542
|
+
private applyStringSetChangeToYMap;
|
|
525
543
|
/**
|
|
526
544
|
* Deep equality check for comparing field values
|
|
527
545
|
*/
|
|
@@ -623,6 +641,20 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
623
641
|
* Execute a callback with automatic transaction handling for all modified models
|
|
624
642
|
*/
|
|
625
643
|
static withTransaction<T>(callback: () => Promise<T> | T): Promise<T>;
|
|
644
|
+
/**
|
|
645
|
+
* Attach a one-shot observer to a nested-Y.Map stringset field. Calls
|
|
646
|
+
* notifyListeners() when the nested map's keys change (i.e. when a remote
|
|
647
|
+
* member arrives via Y.applyUpdate, or a local per-member set/delete).
|
|
648
|
+
* Idempotent — re-calling on the same Y.Map is a no-op.
|
|
649
|
+
*/
|
|
650
|
+
protected static observeStringSetMapOnce(nestedMap: Y.Map<any>): void;
|
|
651
|
+
/**
|
|
652
|
+
* Walk the schema's stringset fields on `recordYMap` and observe any nested
|
|
653
|
+
* Y.Map values. Called from observer setup and from the parent observer body
|
|
654
|
+
* so newly-arrived stringset Y.Maps (local migration or remote create) get
|
|
655
|
+
* an observer too.
|
|
656
|
+
*/
|
|
657
|
+
protected static attachStringSetObserversToRecord(recordYMap: Y.Map<any>, schema: any): void;
|
|
626
658
|
/**
|
|
627
659
|
* Sets up deep observation on a nested YMap to sync field-level changes to the database
|
|
628
660
|
*/
|
|
@@ -974,15 +1006,27 @@ declare function schemaToToml(schema: DiscoveredSchema): string;
|
|
|
974
1006
|
* - Compound unique constraints are [[models.*.unique_constraints]]
|
|
975
1007
|
*/
|
|
976
1008
|
|
|
1009
|
+
interface LoadSchemaOptions {
|
|
1010
|
+
/**
|
|
1011
|
+
* When true (default), throw on unknown keys at the model, field,
|
|
1012
|
+
* relationship, and unique-constraint level. When false, unknown
|
|
1013
|
+
* keys are silently ignored (legacy behavior).
|
|
1014
|
+
*/
|
|
1015
|
+
strict?: boolean;
|
|
1016
|
+
}
|
|
977
1017
|
/**
|
|
978
1018
|
* Parse a TOML string and return an array of DefinedModelSchema objects.
|
|
1019
|
+
*
|
|
1020
|
+
* By default operates in strict mode: unknown keys at the model, field,
|
|
1021
|
+
* relationship, or unique-constraint level cause an error. Pass
|
|
1022
|
+
* `{ strict: false }` to silently ignore unknown keys (legacy behavior).
|
|
979
1023
|
*/
|
|
980
|
-
declare function loadSchemaFromTomlString(tomlString: string): DefinedModelSchema[];
|
|
1024
|
+
declare function loadSchemaFromTomlString(tomlString: string, options?: LoadSchemaOptions): DefinedModelSchema[];
|
|
981
1025
|
/**
|
|
982
1026
|
* Read a TOML file from disk and return an array of DefinedModelSchema objects.
|
|
983
1027
|
* Only available in Node.js environments.
|
|
984
1028
|
*/
|
|
985
|
-
declare function loadSchemaFromToml(filePath: string): Promise<DefinedModelSchema[]>;
|
|
1029
|
+
declare function loadSchemaFromToml(filePath: string, options?: LoadSchemaOptions): Promise<DefinedModelSchema[]>;
|
|
986
1030
|
|
|
987
1031
|
/**
|
|
988
1032
|
* Meta Sync — writes _meta_* YMaps into a YDoc.
|