@objectstack/driver-sql 7.9.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -85,6 +85,7 @@ declare class SqlDriver implements IDataDriver {
85
85
  jsonFields: boolean;
86
86
  arrayFields: boolean;
87
87
  vectorSearch: boolean;
88
+ autonumber: boolean;
88
89
  schemaSync: boolean;
89
90
  batchSchemaSync: boolean;
90
91
  migrations: boolean;
@@ -419,6 +420,15 @@ declare class SqlDriver implements IDataDriver {
419
420
  */
420
421
  protected coerceFilterValue(table: string | null, field: string, value: any): any;
421
422
  protected applyFilters(builder: Knex.QueryBuilder, filters: any): void;
423
+ /**
424
+ * Apply a `contains` substring match as a parameterized `LIKE '%…%'`, escaping
425
+ * the LIKE metacharacters `%` / `_` (and the escape char `\`) in the user value
426
+ * so they match literally instead of acting as wildcards — otherwise a value of
427
+ * `%` matches every row (a filter-bypass, P0). Binds an explicit `ESCAPE '\'`
428
+ * because SQLite does not honour a default escape character (MySQL/Postgres do,
429
+ * but the explicit clause is correct for all three).
430
+ */
431
+ private applyContainsLike;
422
432
  protected applyFilterCondition(builder: Knex.QueryBuilder, condition: any, logicalOp?: 'and' | 'or', tableHint?: string | null): void;
423
433
  protected mapSortField(field: string): string;
424
434
  protected mapAggregateFunc(func: string): string;
package/dist/index.d.ts CHANGED
@@ -85,6 +85,7 @@ declare class SqlDriver implements IDataDriver {
85
85
  jsonFields: boolean;
86
86
  arrayFields: boolean;
87
87
  vectorSearch: boolean;
88
+ autonumber: boolean;
88
89
  schemaSync: boolean;
89
90
  batchSchemaSync: boolean;
90
91
  migrations: boolean;
@@ -419,6 +420,15 @@ declare class SqlDriver implements IDataDriver {
419
420
  */
420
421
  protected coerceFilterValue(table: string | null, field: string, value: any): any;
421
422
  protected applyFilters(builder: Knex.QueryBuilder, filters: any): void;
423
+ /**
424
+ * Apply a `contains` substring match as a parameterized `LIKE '%…%'`, escaping
425
+ * the LIKE metacharacters `%` / `_` (and the escape char `\`) in the user value
426
+ * so they match literally instead of acting as wildcards — otherwise a value of
427
+ * `%` matches every row (a filter-bypass, P0). Binds an explicit `ESCAPE '\'`
428
+ * because SQLite does not honour a default escape character (MySQL/Postgres do,
429
+ * but the explicit clause is correct for all three).
430
+ */
431
+ private applyContainsLike;
422
432
  protected applyFilterCondition(builder: Knex.QueryBuilder, condition: any, logicalOp?: 'and' | 'or', tableHint?: string | null): void;
423
433
  protected mapSortField(field: string): string;
424
434
  protected mapAggregateFunc(func: string): string;
package/dist/index.js CHANGED
@@ -139,6 +139,10 @@ var SqlDriver = class {
139
139
  jsonFields: true,
140
140
  arrayFields: true,
141
141
  vectorSearch: false,
142
+ // Persistent, atomic autonumber sequences via `_objectstack_sequences`
143
+ // (see fillAutoNumberFields / getNextSequenceValue). The engine defers
144
+ // autonumber generation to this driver — it is the single source of truth.
145
+ autonumber: true,
142
146
  // Schema Management
143
147
  schemaSync: true,
144
148
  batchSchemaSync: false,
@@ -546,7 +550,10 @@ var SqlDriver = class {
546
550
  async bulkCreate(object, data, options) {
547
551
  this.auditMissingTenant(object, "bulkCreate", options);
548
552
  for (const row of data) {
549
- if (row && typeof row === "object") this.injectTenantOnInsert(object, row, options);
553
+ if (row && typeof row === "object") {
554
+ this.injectTenantOnInsert(object, row, options);
555
+ await this.fillAutoNumberFields(object, row, options);
556
+ }
550
557
  }
551
558
  const builder = this.getBuilder(object, options);
552
559
  return await builder.insert(data).returning("*");
@@ -841,7 +848,8 @@ var SqlDriver = class {
841
848
  ((_b = this.datetimeFields)[tableName] ?? (_b[tableName] = /* @__PURE__ */ new Set())).add(name);
842
849
  }
843
850
  if (type === "auto_number" || type === "autonumber") {
844
- const fmt = typeof field.format === "string" && field.format ? field.format : "{0000}";
851
+ const rawFmt = typeof field.autonumberFormat === "string" && field.autonumberFormat ? field.autonumberFormat : typeof field.format === "string" && field.format ? field.format : "";
852
+ const fmt = rawFmt || "{0000}";
845
853
  const m = fmt.match(/\{(0+)\}/);
846
854
  const padWidth = m ? m[1].length : 4;
847
855
  const prefix = m ? fmt.slice(0, m.index ?? 0) : fmt;
@@ -1229,7 +1237,7 @@ var SqlDriver = class {
1229
1237
  const methodIn = nextJoin === "or" ? "orWhereIn" : "whereIn";
1230
1238
  const methodNotIn = nextJoin === "or" ? "orWhereNotIn" : "whereNotIn";
1231
1239
  if (op === "contains") {
1232
- b[method](field, "like", `%${value}%`);
1240
+ this.applyContainsLike(b, method, field, value);
1233
1241
  return;
1234
1242
  }
1235
1243
  switch (op) {
@@ -1260,6 +1268,19 @@ var SqlDriver = class {
1260
1268
  }
1261
1269
  }
1262
1270
  }
1271
+ /**
1272
+ * Apply a `contains` substring match as a parameterized `LIKE '%…%'`, escaping
1273
+ * the LIKE metacharacters `%` / `_` (and the escape char `\`) in the user value
1274
+ * so they match literally instead of acting as wildcards — otherwise a value of
1275
+ * `%` matches every row (a filter-bypass, P0). Binds an explicit `ESCAPE '\'`
1276
+ * because SQLite does not honour a default escape character (MySQL/Postgres do,
1277
+ * but the explicit clause is correct for all three).
1278
+ */
1279
+ applyContainsLike(builder, method, field, value) {
1280
+ const escaped = String(value).replace(/[\\%_]/g, "\\$&");
1281
+ const rawMethod = method.startsWith("or") ? "orWhereRaw" : "whereRaw";
1282
+ builder[rawMethod]("?? LIKE ? ESCAPE ?", [field, `%${escaped}%`, "\\"]);
1283
+ }
1263
1284
  applyFilterCondition(builder, condition, logicalOp = "and", tableHint) {
1264
1285
  if (!condition || typeof condition !== "object") return;
1265
1286
  const table = tableHint ?? this.tableNameForBuilder(builder);
@@ -1316,7 +1337,7 @@ var SqlDriver = class {
1316
1337
  break;
1317
1338
  }
1318
1339
  case "$contains":
1319
- builder[method](field, "like", `%${opValue}%`);
1340
+ this.applyContainsLike(builder, method, field, opValue);
1320
1341
  break;
1321
1342
  default:
1322
1343
  builder[method](field, coerced);