@deterministic-code/datasources 0.1.14 → 0.1.16

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.
@@ -83,7 +83,7 @@ export class BatchCommandIBulkWriteStrategy {
83
83
  return { result: null, rowsAffected };
84
84
  };
85
85
  mappedInsert = (item) => {
86
- const record = this.reverse(item);
86
+ const record = this.repo.fillCreateZeros(this.reverse(item));
87
87
  const statement = this.statements().insert(this.fields(record, (col) => listed(this.options.createProperties, col, !this.ids.includes(col) || assigned(record[col] ?? null))));
88
88
  return { table: statement.table, fields: statement.fields };
89
89
  };
@@ -14,6 +14,7 @@ export declare class CRUDRepository<T = Cells> extends ReadonlyRepository<T> imp
14
14
  readonly ids: readonly string[];
15
15
  private readonly createProperties?;
16
16
  private readonly updateProperties?;
17
+ protected createZeros?: Cells;
17
18
  private readonly crudStatements;
18
19
  private readonly tree;
19
20
  private readonly write;
@@ -36,6 +37,7 @@ export declare class CRUDRepository<T = Cells> extends ReadonlyRepository<T> imp
36
37
  private save;
37
38
  private remove;
38
39
  private byId;
40
+ fillCreateZeros: (record: Cells) => Cells;
39
41
  private reverseConvert;
40
42
  private idWhere;
41
43
  protected enrichmentInto: (name: string) => boolean;
@@ -37,6 +37,7 @@ export class CRUDRepository extends ReadonlyRepository {
37
37
  ids;
38
38
  createProperties;
39
39
  updateProperties;
40
+ createZeros;
40
41
  crudStatements;
41
42
  tree;
42
43
  write;
@@ -118,7 +119,7 @@ export class CRUDRepository extends ReadonlyRepository {
118
119
  readSingleById = async (id) => this.readSingle(this.byId(id));
119
120
  insert = async (item) => {
120
121
  const extras = lookupKeys(item);
121
- const record = this.reverseConvert(item);
122
+ const record = this.fillCreateZeros(this.reverseConvert(item));
122
123
  const fields = fieldsOf(record, (col) => extras.has(col) || listed(this.createProperties, col, !this.ids.includes(col) || assigned(record[col] ?? null)), extras, this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column);
123
124
  const statement = this.crudStatements.insert(fields);
124
125
  const response = await execute(this.command(), statement, statement.fields);
@@ -181,6 +182,7 @@ export class CRUDRepository extends ReadonlyRepository {
181
182
  throw new DatasourceError("Composite id requires a row keyed by each Id column");
182
183
  return this.idWhere({ [this.ids[0]]: id });
183
184
  };
185
+ fillCreateZeros = (record) => this.createZeros === undefined ? record : { ...this.createZeros, ...record };
184
186
  reverseConvert = (item) => {
185
187
  const row = this.objectConverter.reverseConvert(item);
186
188
  const converted = this.rowConverter ? this.rowConverter.reverseConvert(row) : row;
@@ -4,6 +4,7 @@ import { ReadonlyRepository } from "./ReadonlyRepository.js";
4
4
  import { RepositoryFactory } from "./RepositoryFactory.js";
5
5
  import { RepositoryNotRegisteredError } from "./RepositoryNotRegisteredError.js";
6
6
  import { RowTypeConverter } from "./converters/RowTypeConverter.js";
7
+ import { Schema } from "./Schema.js";
7
8
  import { createCRUDRepositoryOptions } from "./createCRUDRepositoryOptions.js";
8
9
  import { createReadonlyRepositoryMappingOptions } from "./createReadonlyRepositoryMappingOptions.js";
9
10
  export class Datasource {
@@ -41,10 +42,12 @@ export class Datasource {
41
42
  applyCrud = (type, options) => {
42
43
  const built = createCRUDRepositoryOptions(type, this.driver.rowTypeConverters);
43
44
  const types = this.driver.rowTypeConverters;
44
- const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, columnsOf(type));
45
+ const schema = options?.schema ?? new Schema({});
46
+ const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.columns);
45
47
  return {
46
48
  ...(options ?? built),
47
49
  entity: options?.entity ?? built.entity,
50
+ schema,
48
51
  rowConverter: rows,
49
52
  rowTypeConverters: types,
50
53
  type,
@@ -53,10 +56,12 @@ export class Datasource {
53
56
  applyReadonly = (type, options) => {
54
57
  const built = createReadonlyRepositoryMappingOptions(type, this.driver.rowTypeConverters);
55
58
  const types = this.driver.rowTypeConverters;
56
- const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, columnsOf(type));
59
+ const schema = options?.schema ?? new Schema({});
60
+ const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.columns);
57
61
  return {
58
62
  ...(options ?? built),
59
63
  entity: options?.entity ?? built.entity,
64
+ schema,
60
65
  rowConverter: rows,
61
66
  rowTypeConverters: types,
62
67
  type,
@@ -77,22 +82,3 @@ export class Datasource {
77
82
  return Datasource.create();
78
83
  }
79
84
  }
80
- const columnsOf = (type) => {
81
- const instance = new type();
82
- const columns = {};
83
- for (const [name, value] of Object.entries(instance)) {
84
- if (Array.isArray(value))
85
- continue;
86
- const canonical = canonicalOf(value);
87
- if (canonical)
88
- columns[name] = canonical;
89
- }
90
- return columns;
91
- };
92
- const canonicalOf = (value) => typeof value === "boolean"
93
- ? "bool"
94
- : value instanceof Date
95
- ? "datetime"
96
- : value instanceof Uint8Array
97
- ? "binary"
98
- : undefined;
@@ -6,6 +6,7 @@ import type { IWhere } from "./statements/IWhere.js";
6
6
  import type { ReadonlyRepositoryMappingOptions } from "./ReadonlyRepositoryMappingOptions.js";
7
7
  export type ReadQuery = ISelectStatement | IWhere | IExecuteStoredProcedureStatement;
8
8
  export interface IReadonlyRepository<T = unknown> extends IRepository<T> {
9
+ readonly isSchemaRequired: boolean;
9
10
  readSingle(query?: ReadQuery, parameters?: Parameter[], options?: ReadonlyRepositoryMappingOptions<T>): Promise<T | undefined>;
10
11
  read(query?: ReadQuery, parameters?: Parameter[], options?: ReadonlyRepositoryMappingOptions<T>): AsyncIterable<T>;
11
12
  }
@@ -23,6 +23,7 @@ export declare const isBuilt: <T>(value: IConnection | BuiltRepository<T>) => va
23
23
  export declare const builtOf: <T>(typeOrConnectionOrBuilt: Constructable<T> | IConnection | BuiltRepository<T>, connectionOrOptions?: IConnection | ReadonlyRepositoryMappingOptions<T>) => BuiltRepository<T> | undefined;
24
24
  export declare class ReadonlyRepository<T> implements IReadonlyRepository<T>, IEnriched<T> {
25
25
  readonly type: Constructable<T>;
26
+ readonly isSchemaRequired = false;
26
27
  private connection?;
27
28
  protected rowConverter?: TypeConverter<Cells, Cells>;
28
29
  protected objectConverter?: TypeConverter<T, Cells>;
@@ -22,6 +22,7 @@ export const builtOf = (typeOrConnectionOrBuilt, connectionOrOptions) => typeof
22
22
  : new ReadonlyRepositoryBuilder().parts(typeOrConnectionOrBuilt, connectionOrOptions);
23
23
  export class ReadonlyRepository {
24
24
  type;
25
+ isSchemaRequired = false;
25
26
  connection;
26
27
  rowConverter;
27
28
  objectConverter;
@@ -2,6 +2,7 @@ import type { Cell, Cells } from "./converters/Cell.js";
2
2
  import type { Constructable } from "./converters/Constructable.js";
3
3
  import type { TypeConverter } from "./converters/TypeConverter.js";
4
4
  import type { IMapper } from "./mappers/IMapper.js";
5
+ import type { Schema } from "./Schema.js";
5
6
  export type ReadonlyRepositoryMappingOptions<T = Cells> = {
6
7
  entity?: string;
7
8
  entityMapper?: IMapper<string, string>;
@@ -11,4 +12,5 @@ export type ReadonlyRepositoryMappingOptions<T = Cells> = {
11
12
  type?: Constructable<T>;
12
13
  rowTypeConverters?: Record<string, TypeConverter<Cell, Cell>>;
13
14
  readProperties?: string[];
15
+ schema?: Schema;
14
16
  };
@@ -0,0 +1,4 @@
1
+ export declare class Schema {
2
+ readonly columns: Readonly<Record<string, string>>;
3
+ constructor(columns: Readonly<Record<string, string>>);
4
+ }
package/dist/Schema.js ADDED
@@ -0,0 +1,6 @@
1
+ export class Schema {
2
+ columns;
3
+ constructor(columns) {
4
+ this.columns = columns;
5
+ }
6
+ }
package/dist/index.d.ts CHANGED
@@ -118,6 +118,8 @@ export type { TypeMapGroup } from "./mappers/TypeMapGroup.js";
118
118
  export { FlatToNestedMapper } from "./mappers/FlatToNestedMapper.js";
119
119
  export { ChangeCasingMapper } from "./mappers/ChangeCasingMapper.js";
120
120
  export { DictionaryMapper } from "./mappers/DictionaryMapper.js";
121
+ export { DeterministicTypeMapper } from "./mappers/DeterministicTypeMapper.js";
122
+ export { Schema } from "./Schema.js";
121
123
  export { IdentityMapper } from "./mappers/IdentityMapper.js";
122
124
  export { PluralizerMapper } from "./mappers/PluralizerMapper.js";
123
125
  export { TransformMapper } from "./mappers/TransformMapper.js";
package/dist/index.js CHANGED
@@ -72,6 +72,8 @@ export { ReplaceConverter } from "./converters/ReplaceConverter.js";
72
72
  export { FlatToNestedMapper } from "./mappers/FlatToNestedMapper.js";
73
73
  export { ChangeCasingMapper } from "./mappers/ChangeCasingMapper.js";
74
74
  export { DictionaryMapper } from "./mappers/DictionaryMapper.js";
75
+ export { DeterministicTypeMapper } from "./mappers/DeterministicTypeMapper.js";
76
+ export { Schema } from "./Schema.js";
75
77
  export { IdentityMapper } from "./mappers/IdentityMapper.js";
76
78
  export { PluralizerMapper } from "./mappers/PluralizerMapper.js";
77
79
  export { TransformMapper } from "./mappers/TransformMapper.js";
@@ -0,0 +1,5 @@
1
+ import type { IMapper } from "./IMapper.js";
2
+ export declare class DeterministicTypeMapper implements IMapper<string, string> {
3
+ map: (value: string) => string;
4
+ reverseMap: (value: string) => string;
5
+ }
@@ -0,0 +1,21 @@
1
+ const forward = {
2
+ boolean: "boolean",
3
+ Boolean: "boolean",
4
+ number: "integer",
5
+ Number: "integer",
6
+ bigint: "biginteger",
7
+ BigInt: "biginteger",
8
+ Date: "datetime",
9
+ Uint8Array: "binary",
10
+ };
11
+ const reverse = {
12
+ boolean: "boolean",
13
+ integer: "number",
14
+ biginteger: "bigint",
15
+ datetime: "Date",
16
+ binary: "Uint8Array",
17
+ };
18
+ export class DeterministicTypeMapper {
19
+ map = (value) => forward[value] ?? "";
20
+ reverseMap = (value) => reverse[value] ?? "";
21
+ }
@@ -1,6 +1,8 @@
1
+ import { brand } from "./brand.js";
1
2
  export class ArrayExpression {
2
3
  elements;
3
4
  constructor(elements) {
4
5
  this.elements = elements;
5
6
  }
6
7
  }
8
+ brand(ArrayExpression);
@@ -1,4 +1,6 @@
1
+ import { is } from "./brand.js";
1
2
  import { Where } from "./Where.js";
3
+ import { WhereText } from "./WhereText.js";
2
4
  export class ConditionRenderer {
3
5
  expressions;
4
6
  constructor(expressions) {
@@ -6,6 +8,12 @@ export class ConditionRenderer {
6
8
  }
7
9
  render = (condition) => condition.accept(this);
8
10
  visit(where) {
9
- return where instanceof Where ? this.expressions.render(where.condition) : where.rawText;
11
+ return is(where, Where)
12
+ ? this.expressions.render(where.condition)
13
+ : is(where, WhereText)
14
+ ? where.rawText
15
+ : (() => {
16
+ throw new Error("empty expression");
17
+ })();
10
18
  }
11
19
  }
@@ -1,4 +1,5 @@
1
1
  import { ArrayExpression } from "./ArrayExpression.js";
2
+ import { is } from "./brand.js";
2
3
  import { Identifier } from "./Identifier.js";
3
4
  import { Literal } from "./Literal.js";
4
5
  import { Operator } from "./Operator.js";
@@ -10,63 +11,55 @@ export class ExpressionRenderer {
10
11
  this.text = text;
11
12
  }
12
13
  render = (expression) => {
13
- if (expression instanceof Where)
14
+ if (is(expression, Where))
14
15
  return this.render(expression.condition);
15
- if (expression instanceof Identifier)
16
+ if (is(expression, Identifier))
16
17
  return expression.name;
17
- if (expression instanceof Literal)
18
+ if (is(expression, Literal))
18
19
  return inline(expression.value);
19
- if (expression instanceof ArrayExpression)
20
+ if (is(expression, ArrayExpression))
20
21
  return expression.elements.map((e) => this.render(e)).join(", ");
21
- if (expression instanceof Operator.Not)
22
+ if (is(expression, Operator.Not))
22
23
  return `NOT (${this.render(expression.argument)})`;
23
- if (expression instanceof Operator.IsNull)
24
+ if (is(expression, Operator.IsNull))
24
25
  return `${this.render(expression.argument)} IS NULL`;
25
- if (expression instanceof Operator.IsNotNull)
26
+ if (is(expression, Operator.IsNotNull))
26
27
  return `${this.render(expression.argument)} IS NOT NULL`;
27
- if (expression instanceof Operator.Exists)
28
+ if (is(expression, Operator.Exists))
28
29
  return `EXISTS ${this.render(expression.argument)}`;
29
- if (expression instanceof Subquery)
30
+ if (is(expression, Subquery))
30
31
  return `(${this.text.render(expression.query)})`;
31
- if (expression instanceof Operator.In &&
32
- expression.left instanceof Identifier &&
33
- expression.right instanceof ArrayExpression)
32
+ if (is(expression, Operator.In) && is(expression.left, Identifier) && is(expression.right, ArrayExpression))
34
33
  return expression.right.elements.length === 0
35
34
  ? "1 = 0"
36
35
  : `${expression.left.name} IN (${slots(expression.left.name, expression.right.elements.length)})`;
37
- if (expression instanceof Operator.In &&
38
- expression.left instanceof Identifier &&
39
- expression.right instanceof Subquery)
36
+ if (is(expression, Operator.In) && is(expression.left, Identifier) && is(expression.right, Subquery))
40
37
  return `${expression.left.name} IN ${this.render(expression.right)}`;
41
- if (expression instanceof Operator.NotIn &&
42
- expression.left instanceof Identifier &&
43
- expression.right instanceof ArrayExpression)
38
+ if (is(expression, Operator.NotIn) && is(expression.left, Identifier) && is(expression.right, ArrayExpression))
44
39
  return expression.right.elements.length === 0
45
40
  ? "1 = 1"
46
41
  : `${expression.left.name} NOT IN (${slots(expression.left.name, expression.right.elements.length)})`;
47
- if (expression instanceof Operator.NotIn &&
48
- expression.left instanceof Identifier &&
49
- expression.right instanceof Subquery)
42
+ if (is(expression, Operator.NotIn) && is(expression.left, Identifier) && is(expression.right, Subquery))
50
43
  return `${expression.left.name} NOT IN ${this.render(expression.right)}`;
51
- if ((expression instanceof Operator.Between || expression instanceof Operator.NotBetween) &&
52
- expression.value instanceof Identifier &&
53
- expression.low instanceof Literal &&
54
- expression.high instanceof Literal) {
55
- const word = expression instanceof Operator.NotBetween ? "NOT BETWEEN" : "BETWEEN";
44
+ if ((is(expression, Operator.Between) || is(expression, Operator.NotBetween)) &&
45
+ is(expression.value, Identifier) &&
46
+ is(expression.low, Literal) &&
47
+ is(expression.high, Literal)) {
48
+ const word = is(expression, Operator.NotBetween) ? "NOT BETWEEN" : "BETWEEN";
56
49
  return `${expression.value.name} ${word} $${parameterName(expression.value.name)}_0 AND $${parameterName(expression.value.name)}_1`;
57
50
  }
58
- if (expression instanceof Operator.Between)
51
+ if (is(expression, Operator.Between))
59
52
  return `${this.render(expression.value)} BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
60
- if (expression instanceof Operator.NotBetween)
53
+ if (is(expression, Operator.NotBetween))
61
54
  return `${this.render(expression.value)} NOT BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
62
- if (expression instanceof Operator.And)
55
+ if (is(expression, Operator.And))
63
56
  return `${this.wrap(expression.left, true)} AND ${this.wrap(expression.right, true)}`;
64
- if (expression instanceof Operator.Or)
57
+ if (is(expression, Operator.Or))
65
58
  return `${this.wrap(expression.left, false)} OR ${this.wrap(expression.right, false)}`;
66
- const operands = expression instanceof Operator ? pair(expression) : undefined;
67
- if (operands && operands.left instanceof Identifier && operands.right instanceof Literal)
59
+ const operands = pair(expression);
60
+ if (operands && is(operands.left, Identifier) && is(operands.right, Literal))
68
61
  return `${operands.left.name} ${symbol(expression)} $${parameterName(operands.left.name)}`;
69
- if (operands && operands.left instanceof Literal && operands.right instanceof Literal)
62
+ if (operands && is(operands.left, Literal) && is(operands.right, Literal))
70
63
  return `${inline(operands.left.value)} ${symbol(expression)} ${inline(operands.right.value)}`;
71
64
  if (operands)
72
65
  return `${this.render(operands.left)} ${symbol(expression)} ${this.render(operands.right)}`;
@@ -74,23 +67,27 @@ export class ExpressionRenderer {
74
67
  };
75
68
  wrap = (child, and) => needsParens(child, and) ? `(${this.render(child)})` : this.render(child);
76
69
  }
77
- const symbol = (op) => op instanceof Operator.Equals
78
- ? "="
79
- : op instanceof Operator.NotEquals
80
- ? "<>"
81
- : op instanceof Operator.GreaterThan
82
- ? ">"
83
- : op instanceof Operator.LessThan
84
- ? "<"
85
- : op instanceof Operator.GreaterThanOrEqual
86
- ? ">="
87
- : op instanceof Operator.LessThanOrEqual
88
- ? "<="
89
- : op instanceof Operator.Like
90
- ? "LIKE"
91
- : op instanceof Operator.NotLike
92
- ? "NOT LIKE"
93
- : "IN";
70
+ const symbol = (op) => {
71
+ if (is(op, Operator.Equals))
72
+ return "=";
73
+ if (is(op, Operator.NotEquals))
74
+ return "<>";
75
+ if (is(op, Operator.GreaterThan))
76
+ return ">";
77
+ if (is(op, Operator.LessThan))
78
+ return "<";
79
+ if (is(op, Operator.GreaterThanOrEqual))
80
+ return ">=";
81
+ if (is(op, Operator.LessThanOrEqual))
82
+ return "<=";
83
+ if (is(op, Operator.Like))
84
+ return "LIKE";
85
+ if (is(op, Operator.NotLike))
86
+ return "NOT LIKE";
87
+ if (is(op, Operator.In))
88
+ return "IN";
89
+ throw new Error("empty expression");
90
+ };
94
91
  const inline = (value) => value === null
95
92
  ? "null"
96
93
  : typeof value === "boolean"
@@ -98,24 +95,25 @@ const inline = (value) => value === null
98
95
  : typeof value === "string"
99
96
  ? `'${value.replace(/'/g, "''")}'`
100
97
  : String(value);
101
- const needsParens = (child, and) => child instanceof Where
98
+ const needsParens = (child, and) => is(child, Where)
102
99
  ? needsParens(child.condition, and)
103
- : child instanceof Operator.Not ||
104
- child instanceof Operator.In ||
105
- child instanceof Operator.NotIn ||
106
- (child instanceof Operator.And && !and) ||
107
- (child instanceof Operator.Or && and);
108
- const pair = (expression) => expression instanceof Operator.Equals ||
109
- expression instanceof Operator.NotEquals ||
110
- expression instanceof Operator.GreaterThan ||
111
- expression instanceof Operator.LessThan ||
112
- expression instanceof Operator.GreaterThanOrEqual ||
113
- expression instanceof Operator.LessThanOrEqual ||
114
- expression instanceof Operator.And ||
115
- expression instanceof Operator.Or ||
116
- expression instanceof Operator.In ||
117
- expression instanceof Operator.Like ||
118
- expression instanceof Operator.NotLike
100
+ : is(child, Operator.Not) ||
101
+ is(child, Operator.In) ||
102
+ is(child, Operator.NotIn) ||
103
+ (is(child, Operator.And) && !and) ||
104
+ (is(child, Operator.Or) && and);
105
+ const pair = (expression) => is(expression, Operator.Equals) ||
106
+ is(expression, Operator.NotEquals) ||
107
+ is(expression, Operator.GreaterThan) ||
108
+ is(expression, Operator.LessThan) ||
109
+ is(expression, Operator.GreaterThanOrEqual) ||
110
+ is(expression, Operator.LessThanOrEqual) ||
111
+ is(expression, Operator.And) ||
112
+ is(expression, Operator.Or) ||
113
+ is(expression, Operator.In) ||
114
+ is(expression, Operator.Like) ||
115
+ is(expression, Operator.NotLike) ||
116
+ is(expression, Operator.NotIn)
119
117
  ? expression
120
118
  : undefined;
121
119
  const parameterName = (name) => parts(name)
@@ -1,6 +1,8 @@
1
+ import { brand } from "./brand.js";
1
2
  export class Identifier {
2
3
  name;
3
4
  constructor(name) {
4
5
  this.name = name;
5
6
  }
6
7
  }
8
+ brand(Identifier);
@@ -1,6 +1,8 @@
1
+ import { brand } from "./brand.js";
1
2
  export class Literal {
2
3
  value;
3
4
  constructor(value) {
4
5
  this.value = value;
5
6
  }
6
7
  }
8
+ brand(Literal);
@@ -2,6 +2,7 @@ import { ArrayExpression } from "./ArrayExpression.js";
2
2
  import { Condition } from "./Condition.js";
3
3
  import { Identifier } from "./Identifier.js";
4
4
  import { Literal } from "./Literal.js";
5
+ import { brand } from "./brand.js";
5
6
  const pair = (left, right) => typeof left === "string"
6
7
  ? [new Identifier(left), new Literal(right)]
7
8
  : [left, right];
@@ -205,3 +206,21 @@ export class Operator extends Condition {
205
206
  }
206
207
  Operator.Exists = Exists;
207
208
  })(Operator || (Operator = {}));
209
+ brand(Operator.Equals);
210
+ brand(Operator.NotEquals);
211
+ brand(Operator.GreaterThan);
212
+ brand(Operator.LessThan);
213
+ brand(Operator.GreaterThanOrEqual);
214
+ brand(Operator.LessThanOrEqual);
215
+ brand(Operator.And);
216
+ brand(Operator.Or);
217
+ brand(Operator.In);
218
+ brand(Operator.Not);
219
+ brand(Operator.IsNull);
220
+ brand(Operator.IsNotNull);
221
+ brand(Operator.Like);
222
+ brand(Operator.NotLike);
223
+ brand(Operator.Between);
224
+ brand(Operator.NotBetween);
225
+ brand(Operator.NotIn);
226
+ brand(Operator.Exists);
@@ -1,3 +1,4 @@
1
+ import { is } from "./brand.js";
1
2
  import { ConditionRenderer } from "./ConditionRenderer.js";
2
3
  import { ExpressionRenderer } from "./ExpressionRenderer.js";
3
4
  import { StatementRenderer } from "./StatementRenderer.js";
@@ -12,16 +13,8 @@ export class StatementTextRenderer {
12
13
  this.conditions = conditions?.(expressionRenderer) ?? new ConditionRenderer(expressionRenderer);
13
14
  }
14
15
  render(target) {
15
- if (target.constructor.name === "Where" || target.constructor.name === "WhereText") {
16
- return target.accept(this.conditions);
17
- }
18
- if (target.constructor.name.endsWith("Statement")) {
19
- return target.accept(this.statements);
20
- }
21
- if (target instanceof Where || target instanceof WhereText)
22
- return target.accept(this.conditions);
23
- return "where" in target || "fields" in target || "joins" in target || "column" in target
24
- ? target.accept(this.statements)
25
- : target.accept(this.conditions);
16
+ return is(target, Where) || is(target, WhereText)
17
+ ? target.accept(this.conditions)
18
+ : target.accept(this.statements);
26
19
  }
27
20
  }
@@ -1,6 +1,8 @@
1
+ import { brand } from "./brand.js";
1
2
  export class Subquery {
2
3
  query;
3
4
  constructor(query) {
4
5
  this.query = query;
5
6
  }
6
7
  }
8
+ brand(Subquery);
@@ -5,6 +5,7 @@ import { Operator } from "./Operator.js";
5
5
  import { eq } from "./Cmp.js";
6
6
  import { SelectStatement } from "./SelectStatement.js";
7
7
  import { Subquery } from "./Subquery.js";
8
+ import { brand } from "./brand.js";
8
9
  const pair = (expression) => expression instanceof Operator.Equals ||
9
10
  expression instanceof Operator.NotEquals ||
10
11
  expression instanceof Operator.GreaterThan ||
@@ -181,3 +182,4 @@ export class Where {
181
182
  return collect(this.condition);
182
183
  }
183
184
  }
185
+ brand(Where);
@@ -1,3 +1,4 @@
1
+ import { brand } from "./brand.js";
1
2
  export class WhereText {
2
3
  text;
3
4
  constructor(text) {
@@ -8,3 +9,4 @@ export class WhereText {
8
9
  }
9
10
  accept = (visitor) => visitor.visit(this);
10
11
  }
12
+ brand(WhereText);
@@ -0,0 +1,5 @@
1
+ export declare const brand: (type: {
2
+ name: string;
3
+ prototype: object;
4
+ }) => void;
5
+ export declare const is: <T extends object>(value: object, type: abstract new (...args: never[]) => T) => value is T;
@@ -0,0 +1,5 @@
1
+ const key = (name) => Symbol.for(`@deterministic-code/datasources/${name}`);
2
+ export const brand = (type) => {
3
+ Object.defineProperty(type.prototype, key(type.name), { value: true });
4
+ };
5
+ export const is = (value, type) => value instanceof type || Boolean(value[key(type.name)]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deterministic-code/datasources",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",