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/node.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;
@@ -6637,131 +6637,6 @@ var init_BrowserDatabaseFactory = __esm({
6637
6637
  init_BaseModel();
6638
6638
  init_ModelRegistry();
6639
6639
 
6640
- // src/models/decorators.ts
6641
- init_ModelRegistry();
6642
- init_BaseModel();
6643
- init_sql();
6644
- var SCHEMA_FIELDS_PROPERTY = "_jsbaoSchemaFields";
6645
- function Field(options) {
6646
- return function(target, propertyKey) {
6647
- const fieldName = String(propertyKey);
6648
- const modelConstructor = target.constructor || target;
6649
- assertValidIdentifier(
6650
- fieldName,
6651
- `[Field Decorator] Invalid field name on ${modelConstructor?.name || "unknown"}`
6652
- );
6653
- if (!modelConstructor) {
6654
- console.error(
6655
- "[Field Decorator] Could not determine model constructor for field:",
6656
- fieldName,
6657
- "Target:",
6658
- target
6659
- );
6660
- throw new Error(
6661
- `Failed to resolve model constructor for field '${fieldName}'.`
6662
- );
6663
- }
6664
- if (!Object.prototype.hasOwnProperty.call(
6665
- modelConstructor,
6666
- SCHEMA_FIELDS_PROPERTY
6667
- )) {
6668
- modelConstructor[SCHEMA_FIELDS_PROPERTY] = /* @__PURE__ */ new Map();
6669
- }
6670
- modelConstructor[SCHEMA_FIELDS_PROPERTY].set(
6671
- fieldName,
6672
- options
6673
- );
6674
- Logger.debug(
6675
- `[Field Decorator] Staged field "${fieldName}" for class ${modelConstructor.name}:`,
6676
- options
6677
- );
6678
- };
6679
- }
6680
- function Model(options) {
6681
- return function(targetClass) {
6682
- Logger.debug(
6683
- `[Model Decorator] Registering model:`,
6684
- targetClass.name,
6685
- options
6686
- );
6687
- assertValidIdentifier(
6688
- options.name,
6689
- `[Model Decorator] Invalid model name for ${targetClass.name}`
6690
- );
6691
- const registry = ModelRegistry.getInstance();
6692
- targetClass.modelName = options.name;
6693
- targetClass.getSchema = () => {
6694
- const combinedFields = /* @__PURE__ */ new Map();
6695
- const hierarchyStack = [];
6696
- let currentEvalClass = targetClass;
6697
- while (currentEvalClass && currentEvalClass.prototype) {
6698
- hierarchyStack.push(currentEvalClass);
6699
- const parentPrototype = Object.getPrototypeOf(
6700
- currentEvalClass.prototype
6701
- );
6702
- currentEvalClass = parentPrototype ? parentPrototype.constructor : null;
6703
- if (currentEvalClass === Object) break;
6704
- }
6705
- for (let i = hierarchyStack.length - 1; i >= 0; i--) {
6706
- const cls = hierarchyStack[i];
6707
- if (Object.prototype.hasOwnProperty.call(cls, SCHEMA_FIELDS_PROPERTY)) {
6708
- const fieldsForClass = cls[SCHEMA_FIELDS_PROPERTY];
6709
- fieldsForClass.forEach((fieldOpts, fieldName) => {
6710
- combinedFields.set(fieldName, fieldOpts);
6711
- });
6712
- }
6713
- }
6714
- const resolvedUniqueConstraints = [];
6715
- combinedFields.forEach((fieldOpts, fieldName) => {
6716
- if (fieldOpts.unique) {
6717
- const constraintName = `${options.name}_${fieldName}_unique`;
6718
- resolvedUniqueConstraints.push({
6719
- name: constraintName,
6720
- fields: [fieldName]
6721
- });
6722
- }
6723
- });
6724
- if (options.uniqueConstraints) {
6725
- options.uniqueConstraints.forEach(
6726
- (constraintConfig) => {
6727
- const allFieldsExist = constraintConfig.fields.every(
6728
- (f) => combinedFields.has(f)
6729
- );
6730
- if (!allFieldsExist) {
6731
- console.warn(
6732
- `[Model Decorator] Unique constraint "${constraintConfig.name}" for model "${options.name}" specifies non-existent fields. Skipping.`
6733
- );
6734
- return;
6735
- }
6736
- if (resolvedUniqueConstraints.some(
6737
- (rc) => rc.name === constraintConfig.name
6738
- )) {
6739
- console.warn(
6740
- `[Model Decorator] Duplicate unique constraint name "${constraintConfig.name}" in model "${options.name}".`
6741
- );
6742
- }
6743
- resolvedUniqueConstraints.push({
6744
- name: constraintConfig.name,
6745
- fields: constraintConfig.fields
6746
- });
6747
- }
6748
- );
6749
- }
6750
- return {
6751
- class: targetClass,
6752
- options,
6753
- fields: combinedFields,
6754
- resolvedUniqueConstraints
6755
- };
6756
- };
6757
- const directFieldsForThisClass = Object.prototype.hasOwnProperty.call(
6758
- targetClass,
6759
- SCHEMA_FIELDS_PROPERTY
6760
- ) ? targetClass[SCHEMA_FIELDS_PROPERTY] : /* @__PURE__ */ new Map();
6761
- registry.registerModel(targetClass, options, directFieldsForThisClass);
6762
- };
6763
- }
6764
-
6765
6640
  // src/engines/DatabaseEngine.ts
6766
6641
  var DatabaseEngine = class {
6767
6642
  createTable(_modelName, _schema, _options) {
@@ -7027,6 +6902,7 @@ function defineModelSchema(input) {
7027
6902
  const { name, fields } = input;
7028
6903
  const options = {
7029
6904
  name,
6905
+ className: input.options?.className,
7030
6906
  uniqueConstraints: input.options?.uniqueConstraints ? [...input.options.uniqueConstraints] : void 0,
7031
6907
  relationships: input.options?.relationships
7032
6908
  };
@@ -7126,18 +7002,13 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
7126
7002
  function attachSchemaToClass(modelClass, schema) {
7127
7003
  modelClass.schema = schema;
7128
7004
  modelClass.modelName = schema.options.name;
7129
- if (!modelClass.getSchema) {
7130
- Object.defineProperty(modelClass, "getSchema", {
7131
- value: function() {
7132
- return schema.buildRuntimeShape(modelClass);
7133
- },
7134
- writable: false
7135
- });
7136
- } else {
7137
- modelClass.getSchema = function() {
7005
+ Object.defineProperty(modelClass, "getSchema", {
7006
+ value: function() {
7138
7007
  return schema.buildRuntimeShape(modelClass);
7139
- };
7140
- }
7008
+ },
7009
+ writable: false,
7010
+ configurable: true
7011
+ });
7141
7012
  const runtimeShape = schema.buildRuntimeShape(modelClass);
7142
7013
  BaseModel2.attachFieldAccessors(modelClass, runtimeShape.fields);
7143
7014
  return runtimeShape;
@@ -7370,12 +7241,52 @@ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
7370
7241
  "id",
7371
7242
  "stringset"
7372
7243
  ]);
7373
- function parseFieldOptions(raw) {
7244
+ var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
7245
+ "type",
7246
+ "indexed",
7247
+ "unique",
7248
+ "required",
7249
+ "auto_assign",
7250
+ "max_length",
7251
+ "max_count",
7252
+ "default"
7253
+ ]);
7254
+ var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
7255
+ "fields",
7256
+ "relationships",
7257
+ "unique_constraints",
7258
+ "class_name"
7259
+ ]);
7260
+ var KNOWN_RELATIONSHIP_KEYS = /* @__PURE__ */ new Set([
7261
+ "type",
7262
+ "model",
7263
+ "related_id_field",
7264
+ "join_model",
7265
+ "join_model_local_field",
7266
+ "join_model_related_field",
7267
+ "order_by_field",
7268
+ "order_direction",
7269
+ "join_model_order_by_field",
7270
+ "join_model_order_direction"
7271
+ ]);
7272
+ var KNOWN_UNIQUE_CONSTRAINT_KEYS = /* @__PURE__ */ new Set(["name", "fields"]);
7273
+ function checkUnknownKeys(raw, known, context, strict) {
7274
+ if (!strict) return;
7275
+ for (const key of Object.keys(raw)) {
7276
+ if (!known.has(key)) {
7277
+ throw new Error(
7278
+ `${context}: unknown key "${key}". Allowed: ${[...known].join(", ")}`
7279
+ );
7280
+ }
7281
+ }
7282
+ }
7283
+ function parseFieldOptions(raw, context, strict) {
7374
7284
  if (!raw.type || !VALID_FIELD_TYPES.has(raw.type)) {
7375
7285
  throw new Error(
7376
7286
  `Invalid field type "${raw.type}". Must be one of: ${[...VALID_FIELD_TYPES].join(", ")}`
7377
7287
  );
7378
7288
  }
7289
+ checkUnknownKeys(raw, KNOWN_FIELD_KEYS, context, strict);
7379
7290
  const opts = { type: raw.type };
7380
7291
  if (raw.indexed === true) opts.indexed = true;
7381
7292
  if (raw.unique === true) opts.unique = true;
@@ -7391,8 +7302,9 @@ function requireField(raw, field, context) {
7391
7302
  throw new Error(`Relationship ${context}: missing required field "${field}"`);
7392
7303
  }
7393
7304
  }
7394
- function parseRelationship(raw) {
7305
+ function parseRelationship(raw, context, strict) {
7395
7306
  const type = raw.type;
7307
+ checkUnknownKeys(raw, KNOWN_RELATIONSHIP_KEYS, context, strict);
7396
7308
  if (type === "refersTo") {
7397
7309
  requireField(raw, "model", "refersTo");
7398
7310
  requireField(raw, "related_id_field", "refersTo");
@@ -7434,7 +7346,8 @@ function parseRelationship(raw) {
7434
7346
  }
7435
7347
  throw new Error(`Unknown relationship type: ${type}`);
7436
7348
  }
7437
- function loadSchemaFromTomlString(tomlString) {
7349
+ function loadSchemaFromTomlString(tomlString, options = {}) {
7350
+ const strict = options.strict !== false;
7438
7351
  const parsed = parseToml(tomlString);
7439
7352
  const models = parsed.models;
7440
7353
  if (!models || typeof models !== "object") {
@@ -7442,10 +7355,20 @@ function loadSchemaFromTomlString(tomlString) {
7442
7355
  }
7443
7356
  const schemas = [];
7444
7357
  for (const [modelName, modelDef] of Object.entries(models)) {
7358
+ checkUnknownKeys(
7359
+ modelDef,
7360
+ KNOWN_MODEL_KEYS,
7361
+ `[models.${modelName}]`,
7362
+ strict
7363
+ );
7445
7364
  const fields = {};
7446
7365
  if (modelDef.fields) {
7447
7366
  for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
7448
- fields[fieldName] = parseFieldOptions(fieldDef);
7367
+ fields[fieldName] = parseFieldOptions(
7368
+ fieldDef,
7369
+ `[models.${modelName}.fields.${fieldName}]`,
7370
+ strict
7371
+ );
7449
7372
  }
7450
7373
  }
7451
7374
  let relationships;
@@ -7454,24 +7377,36 @@ function loadSchemaFromTomlString(tomlString) {
7454
7377
  for (const [relName, relDef] of Object.entries(
7455
7378
  modelDef.relationships
7456
7379
  )) {
7457
- relationships[relName] = parseRelationship(relDef);
7380
+ relationships[relName] = parseRelationship(
7381
+ relDef,
7382
+ `[models.${modelName}.relationships.${relName}]`,
7383
+ strict
7384
+ );
7458
7385
  }
7459
7386
  }
7460
7387
  let uniqueConstraints;
7461
7388
  if (modelDef.unique_constraints) {
7462
7389
  uniqueConstraints = [];
7463
7390
  for (const raw of modelDef.unique_constraints) {
7391
+ checkUnknownKeys(
7392
+ raw,
7393
+ KNOWN_UNIQUE_CONSTRAINT_KEYS,
7394
+ `[[models.${modelName}.unique_constraints]]`,
7395
+ strict
7396
+ );
7464
7397
  uniqueConstraints.push({
7465
7398
  name: raw.name,
7466
7399
  fields: [...raw.fields]
7467
7400
  });
7468
7401
  }
7469
7402
  }
7403
+ const className = typeof modelDef.class_name === "string" ? modelDef.class_name : void 0;
7470
7404
  schemas.push(
7471
7405
  defineModelSchema({
7472
7406
  name: modelName,
7473
7407
  fields,
7474
7408
  options: {
7409
+ className,
7475
7410
  uniqueConstraints,
7476
7411
  relationships
7477
7412
  }
@@ -7480,10 +7415,10 @@ function loadSchemaFromTomlString(tomlString) {
7480
7415
  }
7481
7416
  return schemas;
7482
7417
  }
7483
- async function loadSchemaFromToml(filePath) {
7418
+ async function loadSchemaFromToml(filePath, options = {}) {
7484
7419
  const { readFile } = await import("fs/promises");
7485
7420
  const content = await readFile(filePath, "utf-8");
7486
- return loadSchemaFromTomlString(content);
7421
+ return loadSchemaFromTomlString(content, options);
7487
7422
  }
7488
7423
 
7489
7424
  // src/node.ts
@@ -7561,10 +7496,8 @@ export {
7561
7496
  BaseModel2 as BaseModel,
7562
7497
  DatabaseEngine,
7563
7498
  NodeDatabaseFactory as DatabaseFactory,
7564
- Field,
7565
7499
  LogLevel,
7566
7500
  Logger,
7567
- Model,
7568
7501
  ModelRegistry,
7569
7502
  RecordNotFoundError,
7570
7503
  SqljsEngine,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-bao",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "A library providing data modeling capabilities which support live updates and queries.",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -51,10 +51,12 @@
51
51
  },
52
52
  "bin": {
53
53
  "js-bao-codegen": "./dist/codegen.cjs",
54
- "jsbao-codegen": "./dist/codegen.cjs"
54
+ "jsbao-codegen": "./dist/codegen.cjs",
55
+ "js-bao-codegen-v2": "./dist/codegen-v2.cjs",
56
+ "jsbao-codegen-v2": "./dist/codegen-v2.cjs"
55
57
  },
56
58
  "scripts": {
57
- "build": "pnpm build:cli && pnpm codegen && rm -f dist/index.* dist/node.* dist/browser.* dist/cloudflare.* dist/cloudflare-do.* dist/client.* && pnpm build:main",
59
+ "build": "pnpm build:cli && pnpm build:cli-v2 && pnpm codegen && rm -f dist/index.* dist/node.* dist/browser.* dist/cloudflare.* dist/cloudflare-do.* dist/client.* && pnpm build:main",
58
60
  "build:main": "pnpm build:browser && pnpm build:node && pnpm build:universal && pnpm build:cloudflare && pnpm build:cloudflare-do && pnpm build:client",
59
61
  "build:browser": "tsup src/browser.ts --format esm,cjs --dts --platform browser --external better-sqlite3 --external sql.js --external async-mutex --external ulid --external yjs --external fs --no-splitting",
60
62
  "build:node": "tsup src/node.ts --format esm,cjs --dts --platform node --external better-sqlite3 --external sql.js --external async-mutex --external ulid --external yjs --no-splitting",
@@ -63,6 +65,7 @@
63
65
  "build:cloudflare-do": "tsup src/cloudflare-do.ts --format esm,cjs --dts --platform neutral --external better-sqlite3 --external sql.js --external async-mutex --external ulid --external yjs --external fs --no-splitting",
64
66
  "build:client": "tsup src/client.ts --format esm,cjs --dts --platform browser --external better-sqlite3 --external sql.js --external async-mutex --external ulid --external yjs --external fs --no-splitting",
65
67
  "build:cli": "tsup src/cli/codegen.ts --format cjs --dts --platform node --target node18 --external commander --external typescript --external fs --external path --external util --no-splitting",
68
+ "build:cli-v2": "tsup src/cli/codegen-v2.ts --format cjs --dts --platform node --target node18 --external commander --external typescript --external fs --external path --external util --no-splitting",
66
69
  "dev": "pnpm build:cli && concurrently \"pnpm codegen:watch\" \"pnpm dev:main\"",
67
70
  "dev:main": "concurrently \"pnpm build:browser:dev --watch\" \"pnpm build:node:dev --watch\" \"pnpm build:universal:dev --watch\"",
68
71
  "dev:simple": "pnpm build:cli && pnpm codegen && pnpm build:main --watch",
@@ -116,11 +119,5 @@
116
119
  "workers"
117
120
  ],
118
121
  "author": "Primitive LLC",
119
- "license": "UNLICENSED",
120
- "pnpm": {
121
- "onlyBuiltDependencies": [
122
- "better-sqlite3",
123
- "esbuild"
124
- ]
125
- }
122
+ "license": "UNLICENSED"
126
123
  }