js-bao 0.3.1 → 0.4.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.js CHANGED
@@ -1612,11 +1612,11 @@ var init_ModelRegistry = __esm({
1612
1612
  init_relationshipManager();
1613
1613
  ModelRegistry = class _ModelRegistry {
1614
1614
  static instance;
1615
- // Stores globally registered model classes by their name (from @Model decorator)
1615
+ // Stores globally registered model classes by their name
1616
1616
  models = /* @__PURE__ */ new Map();
1617
- // Stores options for globally registered models (from @Model decorator)
1617
+ // Stores options for globally registered models
1618
1618
  modelOptions = /* @__PURE__ */ new Map();
1619
- // Stores fields for globally registered models (from @Model decorator, or a static getter on class)
1619
+ // Stores fields for globally registered models (or a static getter on class)
1620
1620
  // For simplicity, let's assume fields can be derived or are less critical for this registry part
1621
1621
  // private modelFields: Map<string, Map<string, FieldOptions>> = new Map();
1622
1622
  // Holds the subset of models explicitly set for the current initialization session
@@ -1634,7 +1634,7 @@ var init_ModelRegistry = __esm({
1634
1634
  registerModel(modelClass, options, fields) {
1635
1635
  if (!options.name) {
1636
1636
  throw new Error(
1637
- `[ModelRegistry] Model class is missing a name in its @Model options. Ensure the @Model decorator includes a 'name' property.`
1637
+ `[ModelRegistry] Model class is missing a name in its options. Ensure the schema passed to defineModelSchema/createModelClass/attachAndRegisterModel includes a 'name' property.`
1638
1638
  );
1639
1639
  }
1640
1640
  if (this.models.has(options.name)) {
@@ -1678,7 +1678,7 @@ var init_ModelRegistry = __esm({
1678
1678
  }
1679
1679
  return infos;
1680
1680
  }
1681
- // Helper to get model name from class (assuming static property from @Model decorator)
1681
+ // Helper to get model name from class (set by attachAndRegisterModel/createModelClass)
1682
1682
  getModelNameFromClass(modelClass) {
1683
1683
  return modelClass.modelName;
1684
1684
  }
@@ -1689,13 +1689,13 @@ var init_ModelRegistry = __esm({
1689
1689
  const modelName = this.getModelNameFromClass(modelClass);
1690
1690
  if (!modelName) {
1691
1691
  console.warn(
1692
- `[ModelRegistry] A model class provided to setExplicitModelsForSession does not have a static 'modelName' property or it's undefined. It will be ignored. Ensure @Model decorator sets this.`
1692
+ `[ModelRegistry] A model class provided to setExplicitModelsForSession does not have a static 'modelName' property or it's undefined. It will be ignored. Ensure attachAndRegisterModel (or createModelClass) was called for this model.`
1693
1693
  );
1694
1694
  return;
1695
1695
  }
1696
1696
  if (!this.models.has(modelName) || this.models.get(modelName) !== modelClass) {
1697
1697
  console.warn(
1698
- `[ModelRegistry] Model class with name ${modelName} provided to setExplicitModelsForSession was not found in the global decorator-based registry or does not match. It will be ignored. Ensure the model file is imported and the @Model decorator has run correctly.`
1698
+ `[ModelRegistry] Model class with name ${modelName} provided to setExplicitModelsForSession was not found in the global registry or does not match. It will be ignored. Ensure the model file is imported and attachAndRegisterModel (or createModelClass) has run.`
1699
1699
  );
1700
1700
  return;
1701
1701
  }
@@ -1725,7 +1725,7 @@ var init_ModelRegistry = __esm({
1725
1725
  } else {
1726
1726
  this.activeSessionModels = null;
1727
1727
  console.log(
1728
- "[ModelRegistry] No explicit models specified for session (undefined), will use all decorator-registered models."
1728
+ "[ModelRegistry] No explicit models specified for session (undefined), will use all globally registered models."
1729
1729
  );
1730
1730
  }
1731
1731
  this.validateSessionModels();
@@ -1736,7 +1736,7 @@ var init_ModelRegistry = __esm({
1736
1736
  const modelsToInitialize = this.activeSessionModels || this.models;
1737
1737
  if (modelsToInitialize.size === 0) {
1738
1738
  console.warn(
1739
- "[ModelRegistry] No models to initialize (either no explicit models set for session or no models globally registered via @Model decorator)."
1739
+ "[ModelRegistry] No models to initialize (either no explicit models set for session or no models globally registered via attachAndRegisterModel)."
1740
1740
  );
1741
1741
  return;
1742
1742
  }
@@ -1767,7 +1767,7 @@ var init_ModelRegistry = __esm({
1767
1767
  const modelsToInitialize = this.activeSessionModels || this.models;
1768
1768
  if (modelsToInitialize.size === 0) {
1769
1769
  Logger.warn(
1770
- "[ModelRegistry] No models to initialize for document (either no explicit models set for session or no models globally registered via @Model decorator)."
1770
+ "[ModelRegistry] No models to initialize for document (either no explicit models set for session or no models globally registered via attachAndRegisterModel)."
1771
1771
  );
1772
1772
  return;
1773
1773
  }
@@ -1805,7 +1805,7 @@ var init_ModelRegistry = __esm({
1805
1805
  const modelsToCleanup = this.activeSessionModels || this.models;
1806
1806
  if (modelsToCleanup.size === 0) {
1807
1807
  console.warn(
1808
- "[ModelRegistry] No models to cleanup for document (either no explicit models set for session or no models globally registered via @Model decorator)."
1808
+ "[ModelRegistry] No models to cleanup for document (either no explicit models set for session or no models globally registered via attachAndRegisterModel)."
1809
1809
  );
1810
1810
  return;
1811
1811
  }
@@ -2569,7 +2569,7 @@ var init_BaseModel = __esm({
2569
2569
  getDocumentId() {
2570
2570
  return this._metaDocId;
2571
2571
  }
2572
- // id is now a plain property. Subclasses will define it with @Field.
2572
+ // id is a plain property. Subclasses define it via defineModelSchema.
2573
2573
  id;
2574
2574
  type;
2575
2575
  // This should be the modelName from ModelOptions
@@ -3116,7 +3116,7 @@ var init_BaseModel = __esm({
3116
3116
  const verboseEnabled = Logger.getLogLevel() >= 5 /* VERBOSE */;
3117
3117
  if (!schema || !schema.options || !schema.options.name || !schema.resolvedUniqueConstraints) {
3118
3118
  throw new Error(
3119
- `[${this.name}] Model schema is not registered, missing options, or missing resolvedUniqueConstraints. Did you forget to use the @Model decorator or has the schema structure changed?`
3119
+ `[${this.name}] Model schema is not registered, missing options, or missing resolvedUniqueConstraints. Did you forget to call attachAndRegisterModel (or autoRegisterModel) for this model?`
3120
3120
  );
3121
3121
  }
3122
3122
  const modelName = schema.options.name;
@@ -5514,131 +5514,6 @@ var init_BaseModel = __esm({
5514
5514
  init_BaseModel();
5515
5515
  init_ModelRegistry();
5516
5516
 
5517
- // src/models/decorators.ts
5518
- init_ModelRegistry();
5519
- init_BaseModel();
5520
- init_sql();
5521
- var SCHEMA_FIELDS_PROPERTY = "_jsbaoSchemaFields";
5522
- function Field(options) {
5523
- return function(target, propertyKey) {
5524
- const fieldName = String(propertyKey);
5525
- const modelConstructor = target.constructor || target;
5526
- assertValidIdentifier(
5527
- fieldName,
5528
- `[Field Decorator] Invalid field name on ${modelConstructor?.name || "unknown"}`
5529
- );
5530
- if (!modelConstructor) {
5531
- console.error(
5532
- "[Field Decorator] Could not determine model constructor for field:",
5533
- fieldName,
5534
- "Target:",
5535
- target
5536
- );
5537
- throw new Error(
5538
- `Failed to resolve model constructor for field '${fieldName}'.`
5539
- );
5540
- }
5541
- if (!Object.prototype.hasOwnProperty.call(
5542
- modelConstructor,
5543
- SCHEMA_FIELDS_PROPERTY
5544
- )) {
5545
- modelConstructor[SCHEMA_FIELDS_PROPERTY] = /* @__PURE__ */ new Map();
5546
- }
5547
- modelConstructor[SCHEMA_FIELDS_PROPERTY].set(
5548
- fieldName,
5549
- options
5550
- );
5551
- Logger.debug(
5552
- `[Field Decorator] Staged field "${fieldName}" for class ${modelConstructor.name}:`,
5553
- options
5554
- );
5555
- };
5556
- }
5557
- function Model(options) {
5558
- return function(targetClass) {
5559
- Logger.debug(
5560
- `[Model Decorator] Registering model:`,
5561
- targetClass.name,
5562
- options
5563
- );
5564
- assertValidIdentifier(
5565
- options.name,
5566
- `[Model Decorator] Invalid model name for ${targetClass.name}`
5567
- );
5568
- const registry = ModelRegistry.getInstance();
5569
- targetClass.modelName = options.name;
5570
- targetClass.getSchema = () => {
5571
- const combinedFields = /* @__PURE__ */ new Map();
5572
- const hierarchyStack = [];
5573
- let currentEvalClass = targetClass;
5574
- while (currentEvalClass && currentEvalClass.prototype) {
5575
- hierarchyStack.push(currentEvalClass);
5576
- const parentPrototype = Object.getPrototypeOf(
5577
- currentEvalClass.prototype
5578
- );
5579
- currentEvalClass = parentPrototype ? parentPrototype.constructor : null;
5580
- if (currentEvalClass === Object) break;
5581
- }
5582
- for (let i = hierarchyStack.length - 1; i >= 0; i--) {
5583
- const cls = hierarchyStack[i];
5584
- if (Object.prototype.hasOwnProperty.call(cls, SCHEMA_FIELDS_PROPERTY)) {
5585
- const fieldsForClass = cls[SCHEMA_FIELDS_PROPERTY];
5586
- fieldsForClass.forEach((fieldOpts, fieldName) => {
5587
- combinedFields.set(fieldName, fieldOpts);
5588
- });
5589
- }
5590
- }
5591
- const resolvedUniqueConstraints = [];
5592
- combinedFields.forEach((fieldOpts, fieldName) => {
5593
- if (fieldOpts.unique) {
5594
- const constraintName = `${options.name}_${fieldName}_unique`;
5595
- resolvedUniqueConstraints.push({
5596
- name: constraintName,
5597
- fields: [fieldName]
5598
- });
5599
- }
5600
- });
5601
- if (options.uniqueConstraints) {
5602
- options.uniqueConstraints.forEach(
5603
- (constraintConfig) => {
5604
- const allFieldsExist = constraintConfig.fields.every(
5605
- (f) => combinedFields.has(f)
5606
- );
5607
- if (!allFieldsExist) {
5608
- console.warn(
5609
- `[Model Decorator] Unique constraint "${constraintConfig.name}" for model "${options.name}" specifies non-existent fields. Skipping.`
5610
- );
5611
- return;
5612
- }
5613
- if (resolvedUniqueConstraints.some(
5614
- (rc) => rc.name === constraintConfig.name
5615
- )) {
5616
- console.warn(
5617
- `[Model Decorator] Duplicate unique constraint name "${constraintConfig.name}" in model "${options.name}".`
5618
- );
5619
- }
5620
- resolvedUniqueConstraints.push({
5621
- name: constraintConfig.name,
5622
- fields: constraintConfig.fields
5623
- });
5624
- }
5625
- );
5626
- }
5627
- return {
5628
- class: targetClass,
5629
- options,
5630
- fields: combinedFields,
5631
- resolvedUniqueConstraints
5632
- };
5633
- };
5634
- const directFieldsForThisClass = Object.prototype.hasOwnProperty.call(
5635
- targetClass,
5636
- SCHEMA_FIELDS_PROPERTY
5637
- ) ? targetClass[SCHEMA_FIELDS_PROPERTY] : /* @__PURE__ */ new Map();
5638
- registry.registerModel(targetClass, options, directFieldsForThisClass);
5639
- };
5640
- }
5641
-
5642
5517
  // src/engines/DatabaseEngine.ts
5643
5518
  var DatabaseEngine = class {
5644
5519
  createTable(_modelName, _schema, _options) {
@@ -6411,6 +6286,7 @@ function defineModelSchema(input) {
6411
6286
  const { name, fields } = input;
6412
6287
  const options = {
6413
6288
  name,
6289
+ className: input.options?.className,
6414
6290
  uniqueConstraints: input.options?.uniqueConstraints ? [...input.options.uniqueConstraints] : void 0,
6415
6291
  relationships: input.options?.relationships
6416
6292
  };
@@ -6510,18 +6386,13 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
6510
6386
  function attachSchemaToClass(modelClass, schema) {
6511
6387
  modelClass.schema = schema;
6512
6388
  modelClass.modelName = schema.options.name;
6513
- if (!modelClass.getSchema) {
6514
- Object.defineProperty(modelClass, "getSchema", {
6515
- value: function() {
6516
- return schema.buildRuntimeShape(modelClass);
6517
- },
6518
- writable: false
6519
- });
6520
- } else {
6521
- modelClass.getSchema = function() {
6389
+ Object.defineProperty(modelClass, "getSchema", {
6390
+ value: function() {
6522
6391
  return schema.buildRuntimeShape(modelClass);
6523
- };
6524
- }
6392
+ },
6393
+ writable: false,
6394
+ configurable: true
6395
+ });
6525
6396
  const runtimeShape = schema.buildRuntimeShape(modelClass);
6526
6397
  BaseModel2.attachFieldAccessors(modelClass, runtimeShape.fields);
6527
6398
  return runtimeShape;
@@ -6754,12 +6625,52 @@ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
6754
6625
  "id",
6755
6626
  "stringset"
6756
6627
  ]);
6757
- function parseFieldOptions(raw) {
6628
+ var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
6629
+ "type",
6630
+ "indexed",
6631
+ "unique",
6632
+ "required",
6633
+ "auto_assign",
6634
+ "max_length",
6635
+ "max_count",
6636
+ "default"
6637
+ ]);
6638
+ var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
6639
+ "fields",
6640
+ "relationships",
6641
+ "unique_constraints",
6642
+ "class_name"
6643
+ ]);
6644
+ var KNOWN_RELATIONSHIP_KEYS = /* @__PURE__ */ new Set([
6645
+ "type",
6646
+ "model",
6647
+ "related_id_field",
6648
+ "join_model",
6649
+ "join_model_local_field",
6650
+ "join_model_related_field",
6651
+ "order_by_field",
6652
+ "order_direction",
6653
+ "join_model_order_by_field",
6654
+ "join_model_order_direction"
6655
+ ]);
6656
+ var KNOWN_UNIQUE_CONSTRAINT_KEYS = /* @__PURE__ */ new Set(["name", "fields"]);
6657
+ function checkUnknownKeys(raw, known, context, strict) {
6658
+ if (!strict) return;
6659
+ for (const key of Object.keys(raw)) {
6660
+ if (!known.has(key)) {
6661
+ throw new Error(
6662
+ `${context}: unknown key "${key}". Allowed: ${[...known].join(", ")}`
6663
+ );
6664
+ }
6665
+ }
6666
+ }
6667
+ function parseFieldOptions(raw, context, strict) {
6758
6668
  if (!raw.type || !VALID_FIELD_TYPES.has(raw.type)) {
6759
6669
  throw new Error(
6760
6670
  `Invalid field type "${raw.type}". Must be one of: ${[...VALID_FIELD_TYPES].join(", ")}`
6761
6671
  );
6762
6672
  }
6673
+ checkUnknownKeys(raw, KNOWN_FIELD_KEYS, context, strict);
6763
6674
  const opts = { type: raw.type };
6764
6675
  if (raw.indexed === true) opts.indexed = true;
6765
6676
  if (raw.unique === true) opts.unique = true;
@@ -6775,8 +6686,9 @@ function requireField(raw, field, context) {
6775
6686
  throw new Error(`Relationship ${context}: missing required field "${field}"`);
6776
6687
  }
6777
6688
  }
6778
- function parseRelationship(raw) {
6689
+ function parseRelationship(raw, context, strict) {
6779
6690
  const type = raw.type;
6691
+ checkUnknownKeys(raw, KNOWN_RELATIONSHIP_KEYS, context, strict);
6780
6692
  if (type === "refersTo") {
6781
6693
  requireField(raw, "model", "refersTo");
6782
6694
  requireField(raw, "related_id_field", "refersTo");
@@ -6818,7 +6730,8 @@ function parseRelationship(raw) {
6818
6730
  }
6819
6731
  throw new Error(`Unknown relationship type: ${type}`);
6820
6732
  }
6821
- function loadSchemaFromTomlString(tomlString) {
6733
+ function loadSchemaFromTomlString(tomlString, options = {}) {
6734
+ const strict = options.strict !== false;
6822
6735
  const parsed = parseToml(tomlString);
6823
6736
  const models = parsed.models;
6824
6737
  if (!models || typeof models !== "object") {
@@ -6826,10 +6739,20 @@ function loadSchemaFromTomlString(tomlString) {
6826
6739
  }
6827
6740
  const schemas = [];
6828
6741
  for (const [modelName, modelDef] of Object.entries(models)) {
6742
+ checkUnknownKeys(
6743
+ modelDef,
6744
+ KNOWN_MODEL_KEYS,
6745
+ `[models.${modelName}]`,
6746
+ strict
6747
+ );
6829
6748
  const fields = {};
6830
6749
  if (modelDef.fields) {
6831
6750
  for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
6832
- fields[fieldName] = parseFieldOptions(fieldDef);
6751
+ fields[fieldName] = parseFieldOptions(
6752
+ fieldDef,
6753
+ `[models.${modelName}.fields.${fieldName}]`,
6754
+ strict
6755
+ );
6833
6756
  }
6834
6757
  }
6835
6758
  let relationships;
@@ -6838,24 +6761,36 @@ function loadSchemaFromTomlString(tomlString) {
6838
6761
  for (const [relName, relDef] of Object.entries(
6839
6762
  modelDef.relationships
6840
6763
  )) {
6841
- relationships[relName] = parseRelationship(relDef);
6764
+ relationships[relName] = parseRelationship(
6765
+ relDef,
6766
+ `[models.${modelName}.relationships.${relName}]`,
6767
+ strict
6768
+ );
6842
6769
  }
6843
6770
  }
6844
6771
  let uniqueConstraints;
6845
6772
  if (modelDef.unique_constraints) {
6846
6773
  uniqueConstraints = [];
6847
6774
  for (const raw of modelDef.unique_constraints) {
6775
+ checkUnknownKeys(
6776
+ raw,
6777
+ KNOWN_UNIQUE_CONSTRAINT_KEYS,
6778
+ `[[models.${modelName}.unique_constraints]]`,
6779
+ strict
6780
+ );
6848
6781
  uniqueConstraints.push({
6849
6782
  name: raw.name,
6850
6783
  fields: [...raw.fields]
6851
6784
  });
6852
6785
  }
6853
6786
  }
6787
+ const className = typeof modelDef.class_name === "string" ? modelDef.class_name : void 0;
6854
6788
  schemas.push(
6855
6789
  defineModelSchema({
6856
6790
  name: modelName,
6857
6791
  fields,
6858
6792
  options: {
6793
+ className,
6859
6794
  uniqueConstraints,
6860
6795
  relationships
6861
6796
  }
@@ -6871,10 +6806,8 @@ export {
6871
6806
  BaseModel2 as BaseModel,
6872
6807
  DatabaseEngine,
6873
6808
  BrowserDatabaseFactory as DatabaseFactory,
6874
- Field,
6875
6809
  LogLevel,
6876
6810
  Logger,
6877
- Model,
6878
6811
  ModelRegistry,
6879
6812
  RecordNotFoundError,
6880
6813
  SqljsEngine,
package/dist/client.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
  }
package/dist/client.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
  }
@@ -4266,6 +4266,7 @@ function defineModelSchema(input) {
4266
4266
  const { name, fields } = input;
4267
4267
  const options = {
4268
4268
  name,
4269
+ className: input.options?.className,
4269
4270
  uniqueConstraints: input.options?.uniqueConstraints ? [...input.options.uniqueConstraints] : void 0,
4270
4271
  relationships: input.options?.relationships
4271
4272
  };
@@ -4342,12 +4343,52 @@ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
4342
4343
  "id",
4343
4344
  "stringset"
4344
4345
  ]);
4345
- function parseFieldOptions(raw) {
4346
+ var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
4347
+ "type",
4348
+ "indexed",
4349
+ "unique",
4350
+ "required",
4351
+ "auto_assign",
4352
+ "max_length",
4353
+ "max_count",
4354
+ "default"
4355
+ ]);
4356
+ var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
4357
+ "fields",
4358
+ "relationships",
4359
+ "unique_constraints",
4360
+ "class_name"
4361
+ ]);
4362
+ var KNOWN_RELATIONSHIP_KEYS = /* @__PURE__ */ new Set([
4363
+ "type",
4364
+ "model",
4365
+ "related_id_field",
4366
+ "join_model",
4367
+ "join_model_local_field",
4368
+ "join_model_related_field",
4369
+ "order_by_field",
4370
+ "order_direction",
4371
+ "join_model_order_by_field",
4372
+ "join_model_order_direction"
4373
+ ]);
4374
+ var KNOWN_UNIQUE_CONSTRAINT_KEYS = /* @__PURE__ */ new Set(["name", "fields"]);
4375
+ function checkUnknownKeys(raw, known, context, strict) {
4376
+ if (!strict) return;
4377
+ for (const key of Object.keys(raw)) {
4378
+ if (!known.has(key)) {
4379
+ throw new Error(
4380
+ `${context}: unknown key "${key}". Allowed: ${[...known].join(", ")}`
4381
+ );
4382
+ }
4383
+ }
4384
+ }
4385
+ function parseFieldOptions(raw, context, strict) {
4346
4386
  if (!raw.type || !VALID_FIELD_TYPES.has(raw.type)) {
4347
4387
  throw new Error(
4348
4388
  `Invalid field type "${raw.type}". Must be one of: ${[...VALID_FIELD_TYPES].join(", ")}`
4349
4389
  );
4350
4390
  }
4391
+ checkUnknownKeys(raw, KNOWN_FIELD_KEYS, context, strict);
4351
4392
  const opts = { type: raw.type };
4352
4393
  if (raw.indexed === true) opts.indexed = true;
4353
4394
  if (raw.unique === true) opts.unique = true;
@@ -4363,8 +4404,9 @@ function requireField(raw, field, context) {
4363
4404
  throw new Error(`Relationship ${context}: missing required field "${field}"`);
4364
4405
  }
4365
4406
  }
4366
- function parseRelationship(raw) {
4407
+ function parseRelationship(raw, context, strict) {
4367
4408
  const type = raw.type;
4409
+ checkUnknownKeys(raw, KNOWN_RELATIONSHIP_KEYS, context, strict);
4368
4410
  if (type === "refersTo") {
4369
4411
  requireField(raw, "model", "refersTo");
4370
4412
  requireField(raw, "related_id_field", "refersTo");
@@ -4406,7 +4448,8 @@ function parseRelationship(raw) {
4406
4448
  }
4407
4449
  throw new Error(`Unknown relationship type: ${type}`);
4408
4450
  }
4409
- function loadSchemaFromTomlString(tomlString) {
4451
+ function loadSchemaFromTomlString(tomlString, options = {}) {
4452
+ const strict = options.strict !== false;
4410
4453
  const parsed = (0, import_smol_toml.parse)(tomlString);
4411
4454
  const models = parsed.models;
4412
4455
  if (!models || typeof models !== "object") {
@@ -4414,10 +4457,20 @@ function loadSchemaFromTomlString(tomlString) {
4414
4457
  }
4415
4458
  const schemas = [];
4416
4459
  for (const [modelName, modelDef] of Object.entries(models)) {
4460
+ checkUnknownKeys(
4461
+ modelDef,
4462
+ KNOWN_MODEL_KEYS,
4463
+ `[models.${modelName}]`,
4464
+ strict
4465
+ );
4417
4466
  const fields = {};
4418
4467
  if (modelDef.fields) {
4419
4468
  for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
4420
- fields[fieldName] = parseFieldOptions(fieldDef);
4469
+ fields[fieldName] = parseFieldOptions(
4470
+ fieldDef,
4471
+ `[models.${modelName}.fields.${fieldName}]`,
4472
+ strict
4473
+ );
4421
4474
  }
4422
4475
  }
4423
4476
  let relationships;
@@ -4426,24 +4479,36 @@ function loadSchemaFromTomlString(tomlString) {
4426
4479
  for (const [relName, relDef] of Object.entries(
4427
4480
  modelDef.relationships
4428
4481
  )) {
4429
- relationships[relName] = parseRelationship(relDef);
4482
+ relationships[relName] = parseRelationship(
4483
+ relDef,
4484
+ `[models.${modelName}.relationships.${relName}]`,
4485
+ strict
4486
+ );
4430
4487
  }
4431
4488
  }
4432
4489
  let uniqueConstraints;
4433
4490
  if (modelDef.unique_constraints) {
4434
4491
  uniqueConstraints = [];
4435
4492
  for (const raw of modelDef.unique_constraints) {
4493
+ checkUnknownKeys(
4494
+ raw,
4495
+ KNOWN_UNIQUE_CONSTRAINT_KEYS,
4496
+ `[[models.${modelName}.unique_constraints]]`,
4497
+ strict
4498
+ );
4436
4499
  uniqueConstraints.push({
4437
4500
  name: raw.name,
4438
4501
  fields: [...raw.fields]
4439
4502
  });
4440
4503
  }
4441
4504
  }
4505
+ const className = typeof modelDef.class_name === "string" ? modelDef.class_name : void 0;
4442
4506
  schemas.push(
4443
4507
  defineModelSchema({
4444
4508
  name: modelName,
4445
4509
  fields,
4446
4510
  options: {
4511
+ className,
4447
4512
  uniqueConstraints,
4448
4513
  relationships
4449
4514
  }
@@ -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
  }
@@ -1221,8 +1228,8 @@ declare function createDatabaseDO(config?: DatabaseDOConfig): {
1221
1228
  _initialized: boolean;
1222
1229
  /** @internal — cache for $contains misuse check: "model:field" keys known to be in data_json */
1223
1230
  _containsMisuseCache: Set<string>;
1224
- get state(): DurableObjectState;
1225
- get engine(): DurableObjectEngine;
1231
+ readonly state: DurableObjectState;
1232
+ readonly engine: DurableObjectEngine;
1226
1233
  /** @internal */
1227
1234
  _ensureInitialized(): Promise<void>;
1228
1235
  fetch(request: Request): Promise<Response>;
@@ -1845,10 +1852,22 @@ interface DefinedModelSchema<TFields extends Record<string, FieldOptions> = Reco
1845
1852
  * - Compound unique constraints are [[models.*.unique_constraints]]
1846
1853
  */
1847
1854
 
1855
+ interface LoadSchemaOptions {
1856
+ /**
1857
+ * When true (default), throw on unknown keys at the model, field,
1858
+ * relationship, and unique-constraint level. When false, unknown
1859
+ * keys are silently ignored (legacy behavior).
1860
+ */
1861
+ strict?: boolean;
1862
+ }
1848
1863
  /**
1849
1864
  * Parse a TOML string and return an array of DefinedModelSchema objects.
1865
+ *
1866
+ * By default operates in strict mode: unknown keys at the model, field,
1867
+ * relationship, or unique-constraint level cause an error. Pass
1868
+ * `{ strict: false }` to silently ignore unknown keys (legacy behavior).
1850
1869
  */
1851
- declare function loadSchemaFromTomlString(tomlString: string): DefinedModelSchema[];
1870
+ declare function loadSchemaFromTomlString(tomlString: string, options?: LoadSchemaOptions): DefinedModelSchema[];
1852
1871
 
1853
1872
  /**
1854
1873
  * Meta Sync — writes _meta_* YMaps into a YDoc.
@@ -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
  }
@@ -1221,8 +1228,8 @@ declare function createDatabaseDO(config?: DatabaseDOConfig): {
1221
1228
  _initialized: boolean;
1222
1229
  /** @internal — cache for $contains misuse check: "model:field" keys known to be in data_json */
1223
1230
  _containsMisuseCache: Set<string>;
1224
- get state(): DurableObjectState;
1225
- get engine(): DurableObjectEngine;
1231
+ readonly state: DurableObjectState;
1232
+ readonly engine: DurableObjectEngine;
1226
1233
  /** @internal */
1227
1234
  _ensureInitialized(): Promise<void>;
1228
1235
  fetch(request: Request): Promise<Response>;
@@ -1845,10 +1852,22 @@ interface DefinedModelSchema<TFields extends Record<string, FieldOptions> = Reco
1845
1852
  * - Compound unique constraints are [[models.*.unique_constraints]]
1846
1853
  */
1847
1854
 
1855
+ interface LoadSchemaOptions {
1856
+ /**
1857
+ * When true (default), throw on unknown keys at the model, field,
1858
+ * relationship, and unique-constraint level. When false, unknown
1859
+ * keys are silently ignored (legacy behavior).
1860
+ */
1861
+ strict?: boolean;
1862
+ }
1848
1863
  /**
1849
1864
  * Parse a TOML string and return an array of DefinedModelSchema objects.
1865
+ *
1866
+ * By default operates in strict mode: unknown keys at the model, field,
1867
+ * relationship, or unique-constraint level cause an error. Pass
1868
+ * `{ strict: false }` to silently ignore unknown keys (legacy behavior).
1850
1869
  */
1851
- declare function loadSchemaFromTomlString(tomlString: string): DefinedModelSchema[];
1870
+ declare function loadSchemaFromTomlString(tomlString: string, options?: LoadSchemaOptions): DefinedModelSchema[];
1852
1871
 
1853
1872
  /**
1854
1873
  * Meta Sync — writes _meta_* YMaps into a YDoc.