rado 1.0.0 → 1.0.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/README.md CHANGED
@@ -20,7 +20,7 @@ Conditions can be created by accessing the fields of the table:
20
20
 
21
21
  ```ts
22
22
  import {eq} from 'rado'
23
- eq(User.id.is, 1) // Compare with a value
23
+ eq(User.id, 1) // Compare with a value
24
24
  eq(User.id, Post.userId) // Compare to fields on other tables
25
25
  ```
26
26
 
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getQuery,
4
4
  getSql,
5
- hasSql
5
+ hasQuery
6
6
  } from "./Internal.js";
7
7
  var Dialect = class {
8
8
  #createEmitter;
@@ -10,13 +10,13 @@ var Dialect = class {
10
10
  this.#createEmitter = createEmitter;
11
11
  }
12
12
  emit = (input) => {
13
- const sql = hasSql(input) ? getSql(input) : getQuery(input);
13
+ const sql = hasQuery(input) ? getQuery(input) : getSql(input);
14
14
  const emitter = new this.#createEmitter();
15
15
  sql.emitTo(emitter);
16
16
  return emitter;
17
17
  };
18
18
  inline = (input) => {
19
- const sql = hasSql(input) ? getSql(input) : getQuery(input);
19
+ const sql = hasQuery(input) ? getQuery(input) : getSql(input);
20
20
  const emitter = new this.#createEmitter();
21
21
  sql.inlineValues().emitTo(emitter);
22
22
  return emitter.sql;
@@ -1,6 +1,8 @@
1
1
  import type { ColumnData } from './Column.js';
2
2
  import { type HasQuery, type HasTarget } from './Internal.js';
3
+ import type { Runtime } from './MetaData.js';
3
4
  import { type Param } from './Param.js';
5
+ import { type Sql } from './Sql.js';
4
6
  import type { TableApi } from './Table.js';
5
7
  import type { FieldData } from './expr/Field.js';
6
8
  import type { IncludeData } from './expr/Include.js';
@@ -15,16 +17,12 @@ export declare abstract class Emitter {
15
17
  get hasParams(): boolean;
16
18
  bind(inputs?: Record<string, unknown>): Array<unknown>;
17
19
  processValue(value: unknown): unknown;
18
- abstract jsonArrayFn: string;
19
- abstract jsonGroupFn: string;
20
+ abstract runtime: Runtime;
20
21
  abstract emitIdentifier(value: string): void;
21
22
  abstract emitValue(value: unknown): void;
22
23
  abstract emitInline(value: unknown): void;
23
24
  abstract emitJsonPath(value: Array<number | string>): void;
24
25
  abstract emitPlaceholder(value: string): void;
25
- abstract emitDefaultValue(): void;
26
- abstract emitLastInsertId(): void;
27
- abstract emitIdColumn(): void;
28
26
  emitUnsafe(value: string): void;
29
27
  emitField(field: FieldData): void;
30
28
  emitCreateTable(tableApi: TableApi): void;
@@ -37,4 +35,5 @@ export declare abstract class Emitter {
37
35
  emitUpdate(update: Update<unknown>): void;
38
36
  emitWith(cte: Array<HasQuery & HasTarget>): void;
39
37
  emitInclude(data: IncludeData): void;
38
+ emitUniversal(runtimes: Partial<Record<Runtime | 'default', Sql>>): void;
40
39
  }
@@ -7,6 +7,8 @@ import {
7
7
  } from "./Internal.js";
8
8
  import { ValueParam } from "./Param.js";
9
9
  import { sql } from "./Sql.js";
10
+ import { callFunction } from "./expr/Functions.js";
11
+ import { jsonAggregateArray, jsonArray } from "./expr/Json.js";
10
12
  var Emitter = class {
11
13
  sql = "";
12
14
  params = [];
@@ -54,13 +56,10 @@ var Emitter = class {
54
56
  ]).emitTo(this);
55
57
  }
56
58
  emitReferences(fields) {
57
- sql.join([
59
+ callFunction(
58
60
  sql.identifier(fields[0].targetName),
59
- sql`(${sql.join(
60
- fields.map((field) => sql.identifier(field.fieldName)),
61
- sql`, `
62
- )})`
63
- ]).emitTo(this);
61
+ fields.map((field) => sql.identifier(field.fieldName))
62
+ ).emitTo(this);
64
63
  }
65
64
  emitDelete(deleteOp) {
66
65
  const { cte, from, where, returning } = getData(deleteOp);
@@ -148,13 +147,16 @@ var Emitter = class {
148
147
  if (!data.select.selection)
149
148
  throw new Error("No selection defined");
150
149
  const fields = data.select.selection.fieldNames();
151
- let subject = sql`${sql.unsafe(this.jsonArrayFn)}(${sql.join(
152
- fields.map((name) => sql`_.${sql.identifier(name)}`),
153
- sql`, `
154
- )})`;
155
- if (!data.first)
156
- subject = sql`${sql.unsafe(this.jsonGroupFn)}(${subject})`;
157
- sql`(select ${subject} from (${inner}) as _)`.emitTo(this);
150
+ const subject = jsonArray(
151
+ ...fields.map((name) => sql`_.${sql.identifier(name)}`)
152
+ );
153
+ sql`(select ${data.first ? subject : jsonAggregateArray(subject)} from (${inner}) as _)`.emitTo(this);
154
+ }
155
+ emitUniversal(runtimes) {
156
+ const sql2 = runtimes[this.runtime] ?? runtimes.default;
157
+ if (!sql2)
158
+ throw new Error("Unsupported runtime");
159
+ sql2.emitTo(this);
158
160
  }
159
161
  };
160
162
  export {
@@ -1,5 +1,6 @@
1
1
  export type QueryMode = 'sync' | 'async' | undefined;
2
- export type QueryDialect = 'universal' | 'sqlite' | 'mysql' | 'postgres';
2
+ export type Runtime = 'sqlite' | 'mysql' | 'postgres';
3
+ export type QueryDialect = 'universal' | Runtime;
3
4
  export type Deliver<Meta extends QueryMeta, Result> = Meta extends Either ? Result | Promise<Result> : Meta extends Sync ? Result : Promise<Result>;
4
5
  export interface QueryMeta {
5
6
  mode: QueryMode;
@@ -1,4 +1,4 @@
1
- import { type HasQuery, type HasResolver, type HasSql, type HasTarget, internalData, internalQuery } from './Internal.js';
1
+ import { internalData, internalQuery, type HasQuery, type HasResolver, type HasSql, type HasTarget } from './Internal.js';
2
2
  import type { Deliver, QueryMeta } from './MetaData.js';
3
3
  import type { PreparedStatement, Resolver } from './Resolver.js';
4
4
  import type { Sql } from './Sql.js';
@@ -26,7 +26,7 @@ export declare abstract class Query<Result, Meta extends QueryMeta> extends Exec
26
26
  abstract [internalQuery]: Sql;
27
27
  constructor(data: QueryData<Meta>);
28
28
  all(db?: HasResolver): Deliver<Meta, Array<Result>>;
29
- get(db?: HasResolver): Deliver<Meta, Result>;
29
+ get(db?: HasResolver): Deliver<Meta, Result | null>;
30
30
  run(db?: HasResolver): Deliver<Meta, void>;
31
31
  prepare<Inputs extends Record<string, unknown>>(name?: string): PreparedQuery<Result, Inputs, Meta>;
32
32
  toSQL(): {
@@ -60,9 +60,9 @@ var Query = class extends Executable {
60
60
  try {
61
61
  const result = prepared[resultType]();
62
62
  if (result instanceof Promise)
63
- return result.finally(prepared.free.bind(prepared));
63
+ return result.then((res) => res ?? null).finally(prepared.free.bind(prepared));
64
64
  prepared.free();
65
- return result;
65
+ return result ?? null;
66
66
  } catch (error) {
67
67
  prepared.free();
68
68
  throw error;
@@ -1,6 +1,7 @@
1
1
  import type { DriverSpecs } from './Driver.js';
2
2
  import type { Emitter } from './Emitter.js';
3
- import { internalSql, type HasSql } from './Internal.js';
3
+ import { type HasSql, internalSql } from './Internal.js';
4
+ import type { Runtime } from './MetaData.js';
4
5
  import type { FieldData } from './expr/Field.js';
5
6
  type EmitMethods = {
6
7
  [K in keyof Emitter as K extends `emit${string}` ? K : never]: Emitter[K];
@@ -45,6 +46,7 @@ export declare namespace sql {
45
46
  export function identifier<T>(identifier: string): Sql<T>;
46
47
  export function field<T>(field: FieldData): Sql<T>;
47
48
  export function chunk<Type extends keyof EmitMethods>(type: Type, inner: Parameters<EmitMethods[Type]>[0]): Sql;
49
+ export function universal(runtimes: Partial<Record<Runtime | 'default', Sql>>): Sql;
48
50
  type QueryChunk = HasSql | undefined;
49
51
  export function query(ast: Record<string, boolean | QueryChunk | Array<QueryChunk>>): Sql;
50
52
  export function join<T>(items: Array<Sql | HasSql | undefined | false>, separator?: Sql): Sql<T>;
package/dist/core/Sql.js CHANGED
@@ -136,6 +136,10 @@ function sql(strings, ...inner) {
136
136
  return empty().chunk(type, inner);
137
137
  }
138
138
  sql2.chunk = chunk;
139
+ function universal(runtimes) {
140
+ return chunk("emitUniversal", runtimes);
141
+ }
142
+ sql2.universal = universal;
139
143
  function query(ast) {
140
144
  return join(
141
145
  Object.entries(ast).map(([key, value2]) => {
@@ -1,5 +1,6 @@
1
1
  import { type Sql } from '../Sql.js';
2
2
  import { type Input } from './Input.js';
3
+ export declare function callFunction<Result>(name: Sql, args: Array<Input<unknown>>): Sql<Result>;
3
4
  export declare const Functions: Functions;
4
5
  export type Functions = {
5
6
  [key: string]: (...args: Array<Input<any>>) => Sql<any>;
@@ -1,12 +1,16 @@
1
1
  // src/core/expr/Functions.ts
2
2
  import { sql } from "../Sql.js";
3
3
  import { input } from "./Input.js";
4
+ function callFunction(name, args) {
5
+ return sql`${name}(${sql.join(args.map(input), sql`, `)})`;
6
+ }
4
7
  function get(target, method) {
5
8
  return target[method] ??= (...args) => {
6
- return sql`${sql.identifier(method)}(${sql.join(args.map(input), sql`, `)})`;
9
+ return callFunction(sql.identifier(method), args);
7
10
  };
8
11
  }
9
12
  var Functions = new Proxy(/* @__PURE__ */ Object.create(null), { get });
10
13
  export {
11
- Functions
14
+ Functions,
15
+ callFunction
12
16
  };
@@ -1,4 +1,5 @@
1
1
  import type { HasSql } from '../Internal.js';
2
+ import type { Input } from './Input.js';
2
3
  export interface JsonArrayHasSql<Value> extends HasSql<Array<Value>> {
3
4
  [index: number]: JsonExpr<Value>;
4
5
  }
@@ -10,4 +11,6 @@ type NullableEach<T> = {
10
11
  };
11
12
  export type JsonExpr<Value> = [NonNullable<Value>] extends [Array<infer V>] ? JsonArrayHasSql<null extends Value ? V | null : V> : [NonNullable<Value>] extends [object] ? JsonRecordHasSql<null extends Value ? NullableEach<Value> : Value> : HasSql<Value>;
12
13
  export declare function jsonExpr<Value>(e: HasSql<Value>): JsonExpr<Value>;
14
+ export declare function jsonAggregateArray(...args: Array<Input<unknown>>): import("../Sql.ts").Sql<unknown>;
15
+ export declare function jsonArray(...args: Array<Input<unknown>>): import("../Sql.ts").Sql<unknown>;
13
16
  export {};
@@ -1,5 +1,6 @@
1
1
  // src/core/expr/Json.ts
2
2
  import { sql } from "../Sql.js";
3
+ import { callFunction } from "./Functions.js";
3
4
  var INDEX_PROPERTY = /^\d+$/;
4
5
  function jsonExpr(e) {
5
6
  return new Proxy(e, {
@@ -11,6 +12,27 @@ function jsonExpr(e) {
11
12
  }
12
13
  });
13
14
  }
15
+ function jsonAggregateArray(...args) {
16
+ return callFunction(
17
+ sql.universal({
18
+ sqlite: sql`json_group_array`,
19
+ postgres: sql`json_agg`,
20
+ mysql: sql`json_arrayagg`
21
+ }),
22
+ args
23
+ );
24
+ }
25
+ function jsonArray(...args) {
26
+ return callFunction(
27
+ sql.universal({
28
+ postgres: sql`json_build_array`,
29
+ default: sql`json_array`
30
+ }),
31
+ args
32
+ );
33
+ }
14
34
  export {
35
+ jsonAggregateArray,
36
+ jsonArray,
15
37
  jsonExpr
16
38
  };
@@ -66,6 +66,10 @@ var InsertCanConflict = class extends InsertCanReturn {
66
66
  });
67
67
  }
68
68
  };
69
+ var defaultKeyword = sql.universal({
70
+ sqlite: sql`null`,
71
+ default: sql`default`
72
+ });
69
73
  var InsertInto = class {
70
74
  [internalData];
71
75
  constructor(data) {
@@ -85,7 +89,7 @@ var InsertInto = class {
85
89
  return input(mapToDriverValue?.(value) ?? value);
86
90
  if (defaultValue)
87
91
  return defaultValue();
88
- return sql.chunk("emitDefaultValue", void 0);
92
+ return defaultKeyword;
89
93
  }),
90
94
  sql`, `
91
95
  )})`;
@@ -1,4 +1,4 @@
1
- import { type HasSelection, type HasSql, type HasTable, type HasTarget, internalData, internalQuery, internalSelection } from '../Internal.js';
1
+ import { internalData, internalQuery, internalSelection, internalSql, type HasSelection, type HasSql, type HasTable, type HasTarget } from '../Internal.js';
2
2
  import type { QueryMeta } from '../MetaData.js';
3
3
  import type { Query } from '../Query.js';
4
4
  import { type IsNullable, type MakeNullable, type Selection, type SelectionRecord, type SelectionRow } from '../Selection.js';
@@ -43,10 +43,11 @@ export declare class Select<Input, Meta extends QueryMeta = QueryMeta> extends U
43
43
  offset(offset: UserInput<number>): Select<Input, Meta>;
44
44
  get [internalSelection](): Selection;
45
45
  get [internalQuery](): Sql;
46
+ get [internalSql](): Sql<SelectionRow<Input>>;
46
47
  }
47
48
  export type SubQuery<Input> = Input & HasTarget;
48
- export interface SelectBase<Input, Meta extends QueryMeta = QueryMeta> extends UnionBase<Input, Meta>, HasSelection {
49
- where(where: HasSql<boolean>): Select<Input, Meta>;
49
+ export interface SelectBase<Input, Meta extends QueryMeta = QueryMeta> extends UnionBase<Input, Meta>, HasSelection, HasSql<SelectionRow<Input>> {
50
+ where(...where: Array<HasSql<boolean> | undefined>): Select<Input, Meta>;
50
51
  groupBy(...exprs: Array<HasSql>): Select<Input, Meta>;
51
52
  having(having: HasSql<boolean>): Select<Input, Meta>;
52
53
  orderBy(...exprs: Array<HasSql>): Select<Input, Meta>;
@@ -58,7 +59,7 @@ export interface WithoutSelection<Meta extends QueryMeta> {
58
59
  from<Definition extends TableDefinition, Name extends string>(from: Table<Definition, Name>): AllFrom<TableFields<Definition>, Meta, Record<Name, TableFields<Definition>>>;
59
60
  from<Input>(from: SubQuery<Input>): SelectionFrom<Input, Meta>;
60
61
  }
61
- export interface WithSelection<Input, Meta extends QueryMeta> extends Query<SelectionRow<Input>, Meta> {
62
+ export interface WithSelection<Input, Meta extends QueryMeta> extends Query<SelectionRow<Input>, Meta>, HasSql<SelectionRow<Input>> {
62
63
  from<Definition extends TableDefinition, Name extends string>(from: Table<Definition, Name>): SelectionFrom<Input, Meta>;
63
64
  from(from: SubQuery<unknown>): SelectionFrom<Input, Meta>;
64
65
  from(target: HasSql): Select<Input, Meta>;
@@ -11,6 +11,7 @@ import {
11
11
  internalData,
12
12
  internalQuery,
13
13
  internalSelection,
14
+ internalSql,
14
15
  internalTarget
15
16
  } from "../Internal.js";
16
17
  import {
@@ -132,6 +133,9 @@ var Select = class _Select extends UnionBase {
132
133
  get [internalQuery]() {
133
134
  return sql.chunk("emitSelect", getData(this));
134
135
  }
136
+ get [internalSql]() {
137
+ return sql`(${getQuery(this)})`;
138
+ }
135
139
  };
136
140
  export {
137
141
  Select
@@ -10,9 +10,8 @@ var ESCAPE_SINGLE_QUOTE = "''";
10
10
  var MATCH_SINGLE_QUOTE = /'/g;
11
11
  var mysqlDialect = new Dialect(
12
12
  class extends Emitter {
13
+ runtime = "mysql";
13
14
  paramIndex = 0;
14
- jsonArrayFn = "json_array";
15
- jsonGroupFn = "json_arrayagg";
16
15
  emitValue(value) {
17
16
  this.sql += "?";
18
17
  this.params.push(new ValueParam(value));
@@ -41,15 +40,6 @@ var mysqlDialect = new Dialect(
41
40
  emitIdentifier(identifier) {
42
41
  this.sql += BACKTICK + identifier.replace(MATCH_BACKTICK, ESCAPE_BACKTICK) + BACKTICK;
43
42
  }
44
- emitDefaultValue() {
45
- this.sql += "default";
46
- }
47
- emitIdColumn() {
48
- this.sql += "int not null auto_increment";
49
- }
50
- emitLastInsertId() {
51
- this.sql += "last_insert_id()";
52
- }
53
43
  }
54
44
  );
55
45
  export {
@@ -10,9 +10,8 @@ var ESCAPE_SINGLE_QUOTE = "''";
10
10
  var MATCH_SINGLE_QUOTE = /'/g;
11
11
  var postgresDialect = new Dialect(
12
12
  class extends Emitter {
13
+ runtime = "postgres";
13
14
  paramIndex = 0;
14
- jsonArrayFn = "json_build_array";
15
- jsonGroupFn = "json_agg";
16
15
  emitValue(value) {
17
16
  this.sql += `$${++this.paramIndex}`;
18
17
  this.params.push(new ValueParam(value));
@@ -49,15 +48,6 @@ var postgresDialect = new Dialect(
49
48
  emitIdentifier(identifier) {
50
49
  this.sql += DOUBLE_QUOTE + identifier.replace(MATCH_DOUBLE_QUOTE, ESCAPE_DOUBLE_QUOTE) + DOUBLE_QUOTE;
51
50
  }
52
- emitDefaultValue() {
53
- this.sql += "default";
54
- }
55
- emitIdColumn() {
56
- this.sql += "integer generated always as identity";
57
- }
58
- emitLastInsertId() {
59
- this.sql += "lastval()";
60
- }
61
51
  }
62
52
  );
63
53
  export {
@@ -10,8 +10,7 @@ var ESCAPE_SINGLE_QUOTE = "''";
10
10
  var MATCH_SINGLE_QUOTE = /'/g;
11
11
  var sqliteDialect = new Dialect(
12
12
  class extends Emitter {
13
- jsonArrayFn = "json_array";
14
- jsonGroupFn = "json_group_array";
13
+ runtime = "sqlite";
15
14
  processValue(value) {
16
15
  return typeof value === "boolean" ? value ? 1 : 0 : value;
17
16
  }
@@ -42,18 +41,9 @@ var sqliteDialect = new Dialect(
42
41
  emitIdentifier(identifier) {
43
42
  this.sql += DOUBLE_QUOTE + identifier.replace(MATCH_DOUBLE_QUOTE, ESCAPE_DOUBLE_QUOTE) + DOUBLE_QUOTE;
44
43
  }
45
- emitDefaultValue() {
46
- this.sql += "null";
47
- }
48
44
  quoteString(input) {
49
45
  return SINGLE_QUOTE + input.replace(MATCH_SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE) + SINGLE_QUOTE;
50
46
  }
51
- emitIdColumn() {
52
- this.sql += "integer";
53
- }
54
- emitLastInsertId() {
55
- this.sql += "last_insert_rowid()";
56
- }
57
47
  }
58
48
  );
59
49
  export {
@@ -142,7 +142,7 @@ var sqliteDiff = (hasTable) => {
142
142
  );
143
143
  const selection = { ...hasTable };
144
144
  for (const name of missingColumns) {
145
- selection[name] = getData(tableApi.columns[name]).defaultValue?.() ?? sql.chunk("emitDefaultValue", void 0);
145
+ selection[name] = getData(tableApi.columns[name]).defaultValue?.() ?? sql`null`;
146
146
  }
147
147
  return [
148
148
  // Create a new temporary table with the new definition
@@ -1,4 +1,4 @@
1
- import { JsonColumn, type Column } from '../core/Column.js';
1
+ import { type Column, JsonColumn } from '../core/Column.js';
2
2
  export declare function id(name?: string): Column<number>;
3
3
  export declare function text(name?: string): Column<string | null>;
4
4
  export declare function integer(name?: string): Column<number | null>;
@@ -1,10 +1,15 @@
1
1
  // src/universal/columns.ts
2
2
  import { JsonColumn, column } from "../core/Column.js";
3
3
  import { sql } from "../core/Sql.js";
4
+ var idType = sql.universal({
5
+ sqlite: sql`integer`,
6
+ postgres: sql`integer generated always as identity`,
7
+ mysql: sql`int not null auto_increment`
8
+ });
4
9
  function id(name) {
5
10
  return column({
6
11
  name,
7
- type: sql.chunk("emitIdColumn", void 0),
12
+ type: idType,
8
13
  primary: true
9
14
  });
10
15
  }
@@ -1,4 +1,4 @@
1
1
  import { type Sql } from '../core/Sql.js';
2
- import { type Input } from '../core/expr/Input.js';
2
+ import type { Input } from '../core/expr/Input.js';
3
3
  export declare function lastInsertId(): Sql<number>;
4
4
  export declare function concat(...slices: Array<Input<string | null>>): Sql<string>;
@@ -1,11 +1,16 @@
1
1
  // src/universal/functions.ts
2
2
  import { sql } from "../core/Sql.js";
3
- import { input } from "../core/expr/Input.js";
3
+ import { callFunction } from "../core/expr/Functions.js";
4
+ var insertId = sql.universal({
5
+ sqlite: sql`last_insert_rowid()`,
6
+ postgres: sql`lastval()`,
7
+ mysql: sql`last_insert_id()`
8
+ }).mapWith(Number);
4
9
  function lastInsertId() {
5
- return sql.chunk("emitLastInsertId", void 0).mapWith(Number);
10
+ return insertId;
6
11
  }
7
12
  function concat(...slices) {
8
- return sql`concat(${sql.join(slices.map(input), sql`, `)})`;
13
+ return callFunction(sql`concat`, slices);
9
14
  }
10
15
  export {
11
16
  concat,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rado",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {