@revojs/storage 0.2.64 → 0.3.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.ts CHANGED
@@ -12,6 +12,9 @@ export type Definition = CheckConstraint | ForeignKeyConstraint | Index | Primar
12
12
  export interface Base {
13
13
  toSql: () => Sql;
14
14
  }
15
+ export interface RunResult {
16
+ changes: number;
17
+ }
15
18
  export interface Query<T = unknown> {
16
19
  sql: string;
17
20
  parameters: Array<Primitive>;
@@ -180,6 +183,7 @@ export declare class SelectStatement<TSelection = Table | Record<string, Column>
180
183
  protected $limit: Sql | undefined;
181
184
  protected $offset: Sql | undefined;
182
185
  constructor(session: Session, value: TSelection);
186
+ distinct(): this & Record<"distinct", never>;
183
187
  from(table: Table): this & Record<"from", never>;
184
188
  leftJoin<TTable extends Table>(table: TTable, on: Chunk): SelectStatement<AppendToSelection<TSelection, TTable, TMode>, TMode extends "partial" ? "partial" : "multiple", AppendToNullabilityMap<TNullability, TTable, "left">>;
185
189
  rightJoin<TTable extends Table>(table: TTable, on: Chunk): SelectStatement<AppendToSelection<TSelection, TTable, TMode>, TMode extends "partial" ? "partial" : "multiple", AppendToNullabilityMap<TNullability, TTable, "right">>;
@@ -199,6 +203,11 @@ export declare class InsertStatement<T extends Table = Table> extends Statement<
199
203
  constructor(session: Session, table: T);
200
204
  values<This extends InsertStatement>(this: This, value: InferInsertModel<T> | Array<InferInsertModel<T>>): This & Record<"values", never>;
201
205
  onConflictDoNothing<This extends InsertStatement>(this: This): This & Record<"onConflictDoNothing", never>;
206
+ onConflictDoUpdate<This extends InsertStatement>(this: This, options: {
207
+ target: Column | Array<Column>;
208
+ set: Partial<InferInsertModel<T>>;
209
+ }): This & Record<"onConflictDoNothing" | "onConflictDoUpdate", never>;
210
+ protected toConflictUpdate(targets: Array<Column>, updates: Array<Chunk>): Sql<unknown>;
202
211
  returning<This extends InsertStatement>(this: This): This & Record<"returning", never>;
203
212
  }
204
213
  export declare class UpdateStatement<T extends Table = Table> extends Statement<Array<InferSelectModel<T>>> {
@@ -225,7 +234,7 @@ export declare abstract class PreparedQuery {
225
234
  }
226
235
  export declare abstract class Session {
227
236
  transaction<T>(run: (session: Session) => Awaitable<T>): Promise<T>;
228
- batch(queries: Array<Query>): Promise<void>;
237
+ batch(queries: Array<Query>): Promise<Array<RunResult>>;
229
238
  run(query: Query): unknown;
230
239
  all(query: Query): unknown;
231
240
  get(query: Query): unknown;
@@ -277,7 +286,7 @@ export declare abstract class Database<T extends Record<string, Table> = Record<
277
286
  abstract update<TValue extends Table>(table: TValue): UpdateStatement<TValue>;
278
287
  abstract delete<TValue extends Table>(table: TValue): DeleteStatement<TValue>;
279
288
  abstract transaction<TResult>(run: (database: Database<T>) => Awaitable<TResult>): Promise<TResult>;
280
- batch(statements: Array<InsertStatement | UpdateStatement | DeleteStatement>): Promise<void>;
289
+ batch(statements: Array<InsertStatement | UpdateStatement | DeleteStatement | Sql>): Promise<RunResult[]>;
281
290
  }
282
291
  export declare abstract class Table<TName extends string = string, TColumns extends Record<string, Column> = Record<string, Column>> {
283
292
  readonly $name: TName;
@@ -312,6 +321,15 @@ export declare abstract class Column<TDataType = unknown> {
312
321
  toMeta(): ColumnMeta;
313
322
  abstract toSqlType(): string;
314
323
  }
324
+ export declare class Expression<TDataType = unknown> extends Column<TDataType> {
325
+ $expression: Sql;
326
+ $source: Column | undefined;
327
+ constructor(expression: Sql, source?: Column);
328
+ fromDriverValue(value: unknown): TDataType;
329
+ toSql(): Sql<unknown>;
330
+ toSqlName(): Sql<unknown>;
331
+ toSqlType(): string;
332
+ }
315
333
  export declare class UniqueConstraint {
316
334
  $columns: Array<Column>;
317
335
  constructor(...columns: Array<Column>);
@@ -369,6 +387,15 @@ export declare function and<T>(...chunks: Array<Chunk>): Sql<T>;
369
387
  export declare function or<T>(...chunks: Array<Chunk>): Sql<T>;
370
388
  export declare function asc<T>(chunk: Chunk): Sql<T>;
371
389
  export declare function desc<T>(chunk: Chunk): Sql<T>;
390
+ export declare function expression<T>(value: Sql, source?: Column): Expression<T>;
391
+ export declare function count(chunk?: Chunk): Expression<number> & Record<"notNull", never>;
392
+ export declare function countDistinct(column: Column): Expression<number> & Record<"notNull", never>;
393
+ export declare function max<TColumn extends Column>(column: TColumn): Expression<Data<TColumn>>;
394
+ export declare function min<TColumn extends Column>(column: TColumn): Expression<Data<TColumn>>;
395
+ export declare function sum(column: Column): Expression<number>;
396
+ export declare function coalesce<TColumn extends Column>(column: TColumn, fallback: Data<TColumn>): Expression<Data<TColumn>> & Record<"notNull", never>;
397
+ export declare function chunkValues<T>(values: Array<T>, size: number): T[][];
398
+ export declare function toRunResult(value: unknown): RunResult;
372
399
  export declare function mergeTable<TTable extends Table, TColumns extends Record<string, Column>>(table: TTable, columns: TColumns, definitions?: (columns: TTable["$columns"] & TColumns) => Array<Definition>): TTable & TColumns & {
373
400
  readonly $columns: TTable["$columns"] & TColumns;
374
401
  };
package/dist/index.js CHANGED
@@ -86,6 +86,10 @@ var SelectStatement = class extends Statement {
86
86
  this.$selection = combine(this.$fields.map(([, column]) => column), ", ");
87
87
  this.push(sql`SELECT ${this.$selection}`);
88
88
  }
89
+ distinct() {
90
+ this.$chunks[0] = sql`SELECT DISTINCT ${this.$selection}`;
91
+ return this;
92
+ }
89
93
  from(table) {
90
94
  this.push(sql`FROM ${table}`);
91
95
  return this;
@@ -210,6 +214,17 @@ var InsertStatement = class extends Statement {
210
214
  this.push(sql`ON CONFLICT DO NOTHING`);
211
215
  return this;
212
216
  }
217
+ onConflictDoUpdate(options) {
218
+ const columns = this.$table.$columns;
219
+ const targets = Array.isArray(options.target) ? options.target : [options.target];
220
+ const updates = new Array();
221
+ for (const [key, value] of Object.entries(options.set)) updates.push(sql`${columns[key].toSqlName()} = ${toParameter(columns[key], value ?? null)}`);
222
+ this.push(this.toConflictUpdate(targets, updates));
223
+ return this;
224
+ }
225
+ toConflictUpdate(targets, updates) {
226
+ return sql`ON CONFLICT (${combine(targets.map((column) => column.toSqlName()), ", ")}) DO UPDATE SET ${combine(updates, ", ")}`;
227
+ }
213
228
  returning() {
214
229
  this.push(sql`RETURNING ${combine(this.$fields.map(([, column]) => column.toSqlName()), ", ")}`);
215
230
  return this;
@@ -264,8 +279,10 @@ var Session = class {
264
279
  throw new Error("This storage provider does not support interactive transactions.");
265
280
  }
266
281
  async batch(queries) {
267
- await this.transaction(async (session) => {
268
- for (const query of queries) await session.run(query);
282
+ return await this.transaction(async (session) => {
283
+ const results = new Array();
284
+ for (const query of queries) results.push(toRunResult(await session.run(query)));
285
+ return results;
269
286
  });
270
287
  }
271
288
  run(query) {
@@ -598,7 +615,10 @@ var Database = class {
598
615
  };
599
616
  }
600
617
  async batch(statements) {
601
- if (statements.length > 0) await this.$session.batch(statements.map((statement) => statement.toQuery()));
618
+ if (statements.length === 0) return [];
619
+ const queries = new Array();
620
+ for (const statement of statements) queries.push(statement.toQuery());
621
+ return await this.$session.batch(queries);
602
622
  }
603
623
  };
604
624
  var Table = class {
@@ -700,6 +720,28 @@ var Column = class {
700
720
  };
701
721
  }
702
722
  };
723
+ var Expression = class extends Column {
724
+ $expression;
725
+ $source;
726
+ constructor(expression, source) {
727
+ super("");
728
+ this.$expression = expression;
729
+ this.$source = source;
730
+ }
731
+ fromDriverValue(value) {
732
+ if (this.$source) return this.$source.fromDriverValue(value);
733
+ return value;
734
+ }
735
+ toSql() {
736
+ return this.$expression;
737
+ }
738
+ toSqlName() {
739
+ return this.$expression;
740
+ }
741
+ toSqlType() {
742
+ return "";
743
+ }
744
+ };
703
745
  var UniqueConstraint = class {
704
746
  $columns;
705
747
  constructor(...columns) {
@@ -874,6 +916,39 @@ function asc(chunk) {
874
916
  function desc(chunk) {
875
917
  return sql`${chunk} DESC`;
876
918
  }
919
+ function expression(value, source) {
920
+ return new Expression(value, source);
921
+ }
922
+ function count(chunk) {
923
+ return new Expression(sql`COUNT(${chunk ?? new Sql("*")})`).notNull();
924
+ }
925
+ function countDistinct(column) {
926
+ return new Expression(sql`COUNT(DISTINCT ${column})`).notNull();
927
+ }
928
+ function max(column) {
929
+ return new Expression(sql`MAX(${column})`, column);
930
+ }
931
+ function min(column) {
932
+ return new Expression(sql`MIN(${column})`, column);
933
+ }
934
+ function sum(column) {
935
+ return new Expression(sql`SUM(${column})`);
936
+ }
937
+ function coalesce(column, fallback) {
938
+ return new Expression(sql`COALESCE(${column}, ${toParameter(column, fallback)})`, column).notNull();
939
+ }
940
+ function chunkValues(values, size) {
941
+ const chunks = new Array();
942
+ for (let index = 0; index < values.length; index += size) chunks.push(values.slice(index, index + size));
943
+ return chunks;
944
+ }
945
+ function toRunResult(value) {
946
+ if (typeof value === "object" && value !== null) {
947
+ if ("changes" in value && typeof value.changes === "number") return { changes: value.changes };
948
+ if ("meta" in value && typeof value.meta === "object" && value.meta !== null && "changes" in value.meta && typeof value.meta.changes === "number") return { changes: value.meta.changes };
949
+ }
950
+ return { changes: 0 };
951
+ }
877
952
  function mergeTable(table, columns, definitions) {
878
953
  const mergedColumns = {};
879
954
  const names = /* @__PURE__ */ new Set();
@@ -945,4 +1020,4 @@ function useStorage(scope, scopeName) {
945
1020
  }
946
1021
  var STORAGE_CONTEXT = defineContext("STORAGE_CONTEXT");
947
1022
  //#endregion
948
- export { CheckConstraint, Column, Database, DeleteStatement, ForeignKeyConstraint, Index, InsertStatement, Migration, Parameter, PreparedQuery, PrimaryKeyConstraint, STORAGE_CONTEXT, SelectStatement, Session, Sql, Statement, Table, UniqueConstraint, UniqueIndex, UpdateStatement, and, asc, between, combine, contains, desc, equals, exists, greater, greaterEquals, like, lower, lowerEquals, mergeTable, not, nullish, or, sql, toParameter, useReferenceStorage, useStorage, useStorageContext, useStorageProvider };
1023
+ export { CheckConstraint, Column, Database, DeleteStatement, Expression, ForeignKeyConstraint, Index, InsertStatement, Migration, Parameter, PreparedQuery, PrimaryKeyConstraint, STORAGE_CONTEXT, SelectStatement, Session, Sql, Statement, Table, UniqueConstraint, UniqueIndex, UpdateStatement, and, asc, between, chunkValues, coalesce, combine, contains, count, countDistinct, desc, equals, exists, expression, greater, greaterEquals, like, lower, lowerEquals, max, mergeTable, min, not, nullish, or, sql, sum, toParameter, toRunResult, useReferenceStorage, useStorage, useStorageContext, useStorageProvider };
@@ -1,5 +1,5 @@
1
1
  import type { Awaitable } from "revojs/shared";
2
- import { CheckConstraint, Column, Database, DeleteStatement, ForeignKeyConstraint, Index, InsertStatement, Migration, PrimaryKeyConstraint, SelectStatement, Sql, Table, UniqueConstraint, UniqueIndex, UpdateStatement, type Definition, type DeleteConstraintChange, type DeleteIndexChange, type Session, type UpdateColumnChange } from "../index";
2
+ import { CheckConstraint, Column, Database, DeleteStatement, ForeignKeyConstraint, Index, InsertStatement, Migration, PrimaryKeyConstraint, SelectStatement, Sql, Table, UniqueConstraint, UniqueIndex, UpdateStatement, type Chunk, type Definition, type DeleteConstraintChange, type DeleteIndexChange, type Session, type UpdateColumnChange } from "../index";
3
3
  export interface CharOptions {
4
4
  length?: number;
5
5
  }
@@ -122,6 +122,7 @@ export declare class MysqlUniqueIndex extends UniqueIndex {
122
122
  export declare class MysqlSelectStatement<T extends Table | Record<string, Column>> extends SelectStatement<T> {
123
123
  }
124
124
  export declare class MysqlInsertStatement<T extends Table> extends InsertStatement<T> {
125
+ protected toConflictUpdate(_targets: Array<Column>, updates: Array<Chunk>): Sql<unknown>;
125
126
  }
126
127
  export declare class MysqlUpdateStatement<T extends Table> extends UpdateStatement<T> {
127
128
  }
@@ -1,4 +1,4 @@
1
- import { CheckConstraint, Column, Database, DeleteStatement, ForeignKeyConstraint, Index, InsertStatement, Migration, PrimaryKeyConstraint, SelectStatement, Sql, Table, UniqueConstraint, UniqueIndex, UpdateStatement } from "../index.js";
1
+ import { CheckConstraint, Column, Database, DeleteStatement, ForeignKeyConstraint, Index, InsertStatement, Migration, PrimaryKeyConstraint, SelectStatement, Sql, Table, UniqueConstraint, UniqueIndex, UpdateStatement, combine, sql } from "../index.js";
2
2
  //#region src/protocols/mysql.ts
3
3
  var MysqlColumn = class extends Column {
4
4
  autoincrement() {
@@ -189,7 +189,11 @@ var MysqlUniqueIndex = class extends UniqueIndex {
189
189
  }
190
190
  };
191
191
  var MysqlSelectStatement = class extends SelectStatement {};
192
- var MysqlInsertStatement = class extends InsertStatement {};
192
+ var MysqlInsertStatement = class extends InsertStatement {
193
+ toConflictUpdate(_targets, updates) {
194
+ return sql`ON DUPLICATE KEY UPDATE ${combine(updates, ", ")}`;
195
+ }
196
+ };
193
197
  var MysqlUpdateStatement = class extends UpdateStatement {};
194
198
  var MysqlDeleteStatement = class extends DeleteStatement {};
195
199
  var MysqlTable = class extends Table {
@@ -1,6 +1,6 @@
1
1
  import type { D1Database, D1DatabaseSession, D1PreparedStatement } from "@cloudflare/workers-types";
2
2
  import type { Awaitable, Primitive } from "revojs/shared";
3
- import { PreparedQuery, Session, type Query } from "../index";
3
+ import { PreparedQuery, Session, type Query, type RunResult } from "../index";
4
4
  import { SqliteDatabase, type SqliteTable } from "../protocols/sqlite";
5
5
  export declare class D1PreparedQuery extends PreparedQuery {
6
6
  protected $statement: D1PreparedStatement;
@@ -14,7 +14,9 @@ export declare class D1Session extends Session {
14
14
  protected $client: D1Database | D1DatabaseSession;
15
15
  constructor(client: D1Database | D1DatabaseSession);
16
16
  transaction<T>(run: (session: Session) => Awaitable<T>): Promise<T>;
17
- batch(queries: Array<Query>): Promise<void>;
17
+ batch(queries: Array<Query>): Promise<RunResult[]>;
18
18
  prepareQuery(query: Query): D1PreparedQuery;
19
19
  }
20
+ export declare function checkParameters(query: Query): void;
20
21
  export declare function d1<T extends Record<string, SqliteTable>>(client: D1Database | D1DatabaseSession, schema: T): SqliteDatabase<T>;
22
+ export declare const D1_MAX_PARAMETERS = 100;
@@ -1,4 +1,4 @@
1
- import { PreparedQuery, Session } from "../index.js";
1
+ import { PreparedQuery, Session, toRunResult } from "../index.js";
2
2
  import { SqliteDatabase } from "../protocols/sqlite.js";
3
3
  //#region src/providers/d1.ts
4
4
  var D1PreparedQuery = class extends PreparedQuery {
@@ -30,14 +30,26 @@ var D1Session = class extends Session {
30
30
  throw new Error("Cloudflare D1 does not support interactive transactions; use its atomic batch API instead.");
31
31
  }
32
32
  async batch(queries) {
33
- await this.$client.batch(queries.map((query) => this.$client.prepare(query.sql).bind(...query.parameters)));
33
+ const statements = new Array();
34
+ for (const query of queries) {
35
+ checkParameters(query);
36
+ statements.push(this.$client.prepare(query.sql).bind(...query.parameters));
37
+ }
38
+ const results = new Array();
39
+ for (const result of await this.$client.batch(statements)) results.push(toRunResult(result));
40
+ return results;
34
41
  }
35
42
  prepareQuery(query) {
43
+ checkParameters(query);
36
44
  return new D1PreparedQuery(query, this.$client.prepare(query.sql));
37
45
  }
38
46
  };
47
+ function checkParameters(query) {
48
+ if (query.parameters.length > 100) throw new RangeError(`Cloudflare D1 binds at most 100 parameters per statement, this one binds ${query.parameters.length}. Split the values with chunkValues().`);
49
+ }
39
50
  function d1(client, schema) {
40
51
  return new SqliteDatabase(new D1Session(client), schema);
41
52
  }
53
+ var D1_MAX_PARAMETERS = 100;
42
54
  //#endregion
43
- export { D1PreparedQuery, D1Session, d1 };
55
+ export { D1PreparedQuery, D1Session, D1_MAX_PARAMETERS, checkParameters, d1 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revojs/storage",
3
- "version": "0.2.64",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,10 +37,10 @@
37
37
  "watch": "vite build --watch"
38
38
  },
39
39
  "dependencies": {
40
- "revojs": "*"
40
+ "revojs": "0.3.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@revojs/tsconfig": "*"
43
+ "@revojs/tsconfig": "0.3.0"
44
44
  },
45
45
  "optionalDependencies": {
46
46
  "@cloudflare/workers-types": "^5.20261009.1",