@deterministic-code/datasources 0.1.12 → 0.1.15
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/BatchCommandIBulkWriteStrategy.js +1 -1
- package/dist/BulkCRUDRepository.js +2 -0
- package/dist/CRUDRepository.d.ts +2 -0
- package/dist/CRUDRepository.js +23 -10
- package/dist/CreateEnrichmentStrategy.d.ts +8 -1
- package/dist/CreateEnrichmentStrategy.js +48 -3
- package/dist/DefaultWriteEnrichmentStrategy.d.ts +1 -0
- package/dist/DefaultWriteEnrichmentStrategy.js +8 -2
- package/dist/DriverLoader.d.ts +1 -1
- package/dist/DriverLoader.js +7 -110
- package/dist/IWriteEnrichmentStrategy.d.ts +1 -0
- package/dist/ReadonlyRepository.d.ts +2 -1
- package/dist/ReadonlyRepository.js +9 -1
- package/dist/StoredEnrichment.d.ts +7 -5
- package/dist/StoredEnrichment.js +3 -1
- package/dist/converters/DefaultValueConverter.js +7 -5
- package/dist/converters/DefaultValueOptions.d.ts +2 -0
- package/dist/statements/ConditionRenderer.js +3 -1
- package/dist/statements/CreateTableStatementTextRenderer.d.ts +2 -1
- package/dist/statements/ExpressionRenderer.js +79 -67
- package/package.json +1 -1
|
@@ -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
|
};
|
|
@@ -10,6 +10,8 @@ const isBulkStrategy = (value) => "create" in value &&
|
|
|
10
10
|
!("statements" in value);
|
|
11
11
|
const transactionOf = (options) => options !== undefined && "write" in options ? options.write?.useTransaction ?? false : false;
|
|
12
12
|
const inTransaction = async (transactional, connection, write) => {
|
|
13
|
+
if (!connection.isOpen)
|
|
14
|
+
await connection.open();
|
|
13
15
|
if (!transactional)
|
|
14
16
|
return write();
|
|
15
17
|
const work = new UnitOfWorkTransaction(connection);
|
package/dist/CRUDRepository.d.ts
CHANGED
|
@@ -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;
|
package/dist/CRUDRepository.js
CHANGED
|
@@ -6,6 +6,7 @@ import { enrichmentsOf, ReadonlyRepository } from "./ReadonlyRepository.js";
|
|
|
6
6
|
import { DefaultReadonlyEnrichmentStrategy } from "./DefaultReadonlyEnrichmentStrategy.js";
|
|
7
7
|
import { execute, executeReader } from "./ICommand.js";
|
|
8
8
|
import { DatasourceError } from "./DatasourceError.js";
|
|
9
|
+
import { lookupKeys, mergeLookups } from "./CreateEnrichmentStrategy.js";
|
|
9
10
|
import { DefaultWriteEnrichmentStrategy } from "./DefaultWriteEnrichmentStrategy.js";
|
|
10
11
|
import { UnitOfWorkTransaction } from "./UnitOfWorkTransaction.js";
|
|
11
12
|
import { RepositoryTree } from "./RepositoryTree.js";
|
|
@@ -13,10 +14,9 @@ import { RepositoryTreeNode } from "./RepositoryTreeNode.js";
|
|
|
13
14
|
const assigned = (value) => value !== null && value !== "" && value !== 0 && value !== 0n;
|
|
14
15
|
const listed = (list, col, fallback) => list === undefined ? fallback : list.includes(col);
|
|
15
16
|
const writable = (key, columns, column) => columns.length === 0 || columns.includes(key) || columns.some((name) => column(name) === key);
|
|
16
|
-
const fieldsOf = (record, include, cell, enrichmentInto, columns, column) => Object.entries(record)
|
|
17
|
+
const fieldsOf = (record, include, extras, cell, enrichmentInto, columns, column) => Object.entries(record)
|
|
17
18
|
.filter(([key, value]) => include(key) &&
|
|
18
|
-
writable(key, columns, column) &&
|
|
19
|
-
!enrichmentInto(key) &&
|
|
19
|
+
(extras.has(key) || (writable(key, columns, column) && !enrichmentInto(key))) &&
|
|
20
20
|
value !== null &&
|
|
21
21
|
value !== undefined)
|
|
22
22
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
@@ -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;
|
|
@@ -56,9 +57,11 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
56
57
|
}
|
|
57
58
|
addEnriched(enrichment, via, strategy) {
|
|
58
59
|
super.addEnriched(enrichment, via, strategy);
|
|
60
|
+
const stored = lastEnrichment(this);
|
|
61
|
+
stored.via = via;
|
|
59
62
|
const writer = writerOf(via);
|
|
60
63
|
if (writer)
|
|
61
|
-
|
|
64
|
+
stored.persist = writer;
|
|
62
65
|
return this;
|
|
63
66
|
}
|
|
64
67
|
addLinked(enrichment, via, link, strategy) {
|
|
@@ -73,9 +76,12 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
73
76
|
update = (item, byWhere) => this.inTransaction(() => this.updateCore(item, byWhere));
|
|
74
77
|
delete = (item, byWhere) => this.inTransaction(() => this.deleteCore(item, byWhere));
|
|
75
78
|
inTransaction = async (write) => {
|
|
79
|
+
const connection = this.command().connection;
|
|
80
|
+
if (!connection.isOpen)
|
|
81
|
+
await connection.open();
|
|
76
82
|
if (!this.useTransaction)
|
|
77
83
|
return write();
|
|
78
|
-
const work = new UnitOfWorkTransaction(
|
|
84
|
+
const work = new UnitOfWorkTransaction(connection);
|
|
79
85
|
await work.begin();
|
|
80
86
|
try {
|
|
81
87
|
const result = await write();
|
|
@@ -88,6 +94,7 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
88
94
|
}
|
|
89
95
|
};
|
|
90
96
|
createCore = async (item) => {
|
|
97
|
+
await this.write.resolveLookups?.(this.tree, [item]);
|
|
91
98
|
await this.insert(item);
|
|
92
99
|
if (!this.enriched)
|
|
93
100
|
return item;
|
|
@@ -98,6 +105,7 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
98
105
|
return id === undefined ? item : (await this.readSingleById(id)) ?? item;
|
|
99
106
|
};
|
|
100
107
|
updateCore = async (item, byWhere) => {
|
|
108
|
+
await this.write.resolveLookups?.(this.tree, [item]);
|
|
101
109
|
const response = await this.save(item, byWhere);
|
|
102
110
|
if (this.enriched)
|
|
103
111
|
await this.write.persist(this.tree, [item], "update");
|
|
@@ -110,8 +118,9 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
110
118
|
};
|
|
111
119
|
readSingleById = async (id) => this.readSingle(this.byId(id));
|
|
112
120
|
insert = async (item) => {
|
|
113
|
-
const
|
|
114
|
-
const
|
|
121
|
+
const extras = lookupKeys(item);
|
|
122
|
+
const record = this.fillCreateZeros(this.reverseConvert(item));
|
|
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);
|
|
115
124
|
const statement = this.crudStatements.insert(fields);
|
|
116
125
|
const response = await execute(this.command(), statement, statement.fields);
|
|
117
126
|
if (this.ids.length === 1 && !fields.some((field) => field.name === this.ids[0]))
|
|
@@ -145,8 +154,9 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
145
154
|
};
|
|
146
155
|
idValue = (item) => scalarOf(item[this.ids[0]]);
|
|
147
156
|
save = async (item, byWhere) => {
|
|
157
|
+
const extras = lookupKeys(item);
|
|
148
158
|
const record = this.reverseConvert(item);
|
|
149
|
-
const fields = fieldsOf(record, (col) => listed(this.updateProperties, col, !this.ids.includes(col)), this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column);
|
|
159
|
+
const fields = fieldsOf(record, (col) => extras.has(col) || listed(this.updateProperties, col, !this.ids.includes(col)), extras, this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column);
|
|
150
160
|
if (fields.length === 0)
|
|
151
161
|
throw new DatasourceError("Update requires at least one non-Id field");
|
|
152
162
|
const statement = this.crudStatements.update(fields, byWhere ?? this.idWhere(record), (name, value) => this.cell(name, value, byWhere === undefined));
|
|
@@ -159,7 +169,7 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
159
169
|
const parameters = listed.length
|
|
160
170
|
? listed
|
|
161
171
|
: DefaultStatementTextRenderer.instance.render(statement).includes("$")
|
|
162
|
-
? fieldsOf(record, () => true, this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column)
|
|
172
|
+
? fieldsOf(record, () => true, new Set(), this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column)
|
|
163
173
|
: [];
|
|
164
174
|
return execute(this.command(), statement, parameters);
|
|
165
175
|
};
|
|
@@ -172,9 +182,12 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
172
182
|
throw new DatasourceError("Composite id requires a row keyed by each Id column");
|
|
173
183
|
return this.idWhere({ [this.ids[0]]: id });
|
|
174
184
|
};
|
|
185
|
+
fillCreateZeros = (record) => this.createZeros === undefined ? record : { ...this.createZeros, ...record };
|
|
175
186
|
reverseConvert = (item) => {
|
|
176
187
|
const row = this.objectConverter.reverseConvert(item);
|
|
177
|
-
|
|
188
|
+
const converted = this.rowConverter ? this.rowConverter.reverseConvert(row) : row;
|
|
189
|
+
mergeLookups(item, converted);
|
|
190
|
+
return converted;
|
|
178
191
|
};
|
|
179
192
|
idWhere = (record) => {
|
|
180
193
|
const parts = this.ids.map((col) => {
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import type { Enrichment } from "./Enrichment.js";
|
|
2
2
|
import type { ICRUDRepository } from "./ICRUDRepository.js";
|
|
3
3
|
import type { Scalar } from "./builders/Scalar.js";
|
|
4
|
+
import type { Cells } from "./converters/Cell.js";
|
|
5
|
+
import type { StoredEnrichment } from "./StoredEnrichment.js";
|
|
6
|
+
export declare const mergeLookups: (item: object, record: Cells) => void;
|
|
7
|
+
export declare const lookupKeys: (item: object) => Set<string>;
|
|
4
8
|
export declare abstract class CreateEnrichmentStrategy<T> {
|
|
5
9
|
abstract createChildren<TChild extends object>(writer: ICRUDRepository<TChild>, enrichment: Enrichment, parent: T): Promise<void>;
|
|
6
10
|
abstract createLinked<TChild extends object>(writer: ICRUDRepository<TChild>, enrichment: Enrichment, parent: T): Promise<void>;
|
|
7
|
-
|
|
11
|
+
resolveLookups(entries: readonly StoredEnrichment<T>[], items: T[]): Promise<void>;
|
|
12
|
+
collectChildren<TChild extends object>(_writer: ICRUDRepository<TChild>, enrichment: Enrichment, parent: T): TChild[];
|
|
13
|
+
protected keyOf: (value: Scalar | object | undefined) => Scalar | object | undefined;
|
|
8
14
|
protected getCell: <TItem>(item: TItem, name: string) => Scalar | object | undefined;
|
|
9
15
|
protected presentCell: (value: Scalar | object | undefined) => value is Scalar;
|
|
10
16
|
protected payloadOf: <TChild extends object, TParent>(parent: TParent, into: string, enrichment?: Enrichment) => TChild[];
|
|
17
|
+
protected stamp: <TItem extends object>(related: TItem, by: string, key: Scalar | object | undefined) => void;
|
|
11
18
|
protected stampCell: <TItem extends object>(related: TItem, by: string, key: Scalar | object | undefined) => void;
|
|
12
19
|
protected relatedIds: <TParent>(parent: TParent, enrichment: Enrichment) => Scalar[];
|
|
13
20
|
protected requiredKey: <TParent>(parent: TParent, enrichment: Enrichment) => Scalar;
|
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import { DatasourceError } from "./DatasourceError.js";
|
|
2
|
+
import { Operator, Where } from "./statements/index.js";
|
|
3
|
+
const lookups = new WeakMap();
|
|
4
|
+
export const mergeLookups = (item, record) => {
|
|
5
|
+
const extras = lookups.get(item);
|
|
6
|
+
if (extras)
|
|
7
|
+
Object.assign(record, extras);
|
|
8
|
+
};
|
|
9
|
+
export const lookupKeys = (item) => new Set(Object.keys(lookups.get(item) ?? {}));
|
|
2
10
|
export class CreateEnrichmentStrategy {
|
|
3
|
-
|
|
11
|
+
async resolveLookups(entries, items) {
|
|
12
|
+
for (const item of items) {
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const { enrichment } = entry;
|
|
15
|
+
if ((enrichment.type ?? "object") !== "object" || enrichment.association || !enrichment.relatedProperty)
|
|
16
|
+
continue;
|
|
17
|
+
const name = this.getCell(item, enrichment.destinationProperty);
|
|
18
|
+
if (!this.presentCell(name))
|
|
19
|
+
continue;
|
|
20
|
+
if (this.presentCell(this.keyOf(this.getCell(item, enrichment.sourceProperty))))
|
|
21
|
+
continue;
|
|
22
|
+
const via = entry.via ?? entry.persist;
|
|
23
|
+
if (!via)
|
|
24
|
+
continue;
|
|
25
|
+
const found = await via.readSingle(new Where(new Operator.Equals(enrichment.relatedProperty, name)));
|
|
26
|
+
if (!found)
|
|
27
|
+
throw new DatasourceError(`Unknown ${enrichment.destinationProperty} "${String(name)}"`);
|
|
28
|
+
const id = this.getCell(found, enrichment.relatedKey);
|
|
29
|
+
if (!this.presentCell(id))
|
|
30
|
+
throw new DatasourceError(`Unknown ${enrichment.destinationProperty} "${String(name)}"`);
|
|
31
|
+
this.stamp(item, enrichment.sourceProperty, id);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
collectChildren(_writer, enrichment, parent) {
|
|
4
36
|
const children = this.payloadOf(parent, enrichment.destinationProperty, enrichment);
|
|
5
37
|
if (children.length === 0)
|
|
6
38
|
return children;
|
|
@@ -9,10 +41,16 @@ export class CreateEnrichmentStrategy {
|
|
|
9
41
|
this.stampCell(child, enrichment.relatedKey, key);
|
|
10
42
|
return children;
|
|
11
43
|
}
|
|
44
|
+
keyOf = (value) => {
|
|
45
|
+
if (value === undefined || value === null || typeof value !== "object")
|
|
46
|
+
return value;
|
|
47
|
+
const id = value.Id ?? value.id;
|
|
48
|
+
return id === undefined || typeof id === "object" ? undefined : id;
|
|
49
|
+
};
|
|
12
50
|
getCell = (item, name) => item[name];
|
|
13
51
|
presentCell = (value) => value !== undefined && value !== null && typeof value !== "object" && value !== 0 && value !== 0n && value !== "";
|
|
14
52
|
payloadOf = (parent, into, enrichment) => mapPayload(this.getCell(parent, into), enrichment);
|
|
15
|
-
|
|
53
|
+
stamp = (related, by, key) => {
|
|
16
54
|
if (key === undefined || key === null || typeof key === "object")
|
|
17
55
|
return;
|
|
18
56
|
const current = this.getCell(related, by);
|
|
@@ -22,8 +60,15 @@ export class CreateEnrichmentStrategy {
|
|
|
22
60
|
target[id] = key;
|
|
23
61
|
return;
|
|
24
62
|
}
|
|
25
|
-
|
|
63
|
+
if (by in related) {
|
|
64
|
+
Object.assign(related, { [by]: key });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const extras = lookups.get(related) ?? {};
|
|
68
|
+
extras[by] = key;
|
|
69
|
+
lookups.set(related, extras);
|
|
26
70
|
};
|
|
71
|
+
stampCell = (related, by, key) => this.stamp(related, by, key);
|
|
27
72
|
relatedIds = (parent, enrichment) => {
|
|
28
73
|
const ids = [];
|
|
29
74
|
const seen = new Set();
|
|
@@ -11,6 +11,7 @@ export declare class DefaultWriteEnrichmentStrategy<T> implements IWriteEnrichme
|
|
|
11
11
|
private readonly deleteStrategy;
|
|
12
12
|
private readonly updateStrategy;
|
|
13
13
|
constructor(deleteStrategy?: DeleteEnrichmentStrategy<T>, updateStrategy?: UpdateEnrichmentStrategy<T>, createStrategy?: CreateEnrichmentStrategy<T>, useTransaction?: boolean);
|
|
14
|
+
resolveLookups: (tree: IRepositoryTree, items: T[]) => Promise<void>;
|
|
14
15
|
persist: (tree: IRepositoryTree, items: T[], mode: WriteMode) => Promise<void>;
|
|
15
16
|
persistBulk: (tree: IRepositoryTree, items: T[], mode: WriteMode) => Promise<void>;
|
|
16
17
|
private walk;
|
|
@@ -8,14 +8,15 @@ const unloaded = (parent, into) => {
|
|
|
8
8
|
const value = parent[into];
|
|
9
9
|
return value === undefined || value === null;
|
|
10
10
|
};
|
|
11
|
+
const asRelated = (writer) => writer;
|
|
11
12
|
const childWriter = (entry, into) => {
|
|
12
13
|
if (entry.persist)
|
|
13
|
-
return entry.persist;
|
|
14
|
+
return asRelated(entry.persist);
|
|
14
15
|
throw new DatasourceError(`Deep write requires a CRUD repository for "${into}"`);
|
|
15
16
|
};
|
|
16
17
|
const linkWriter = (entry, into) => {
|
|
17
18
|
if (entry.link)
|
|
18
|
-
return entry.link;
|
|
19
|
+
return asRelated(entry.link);
|
|
19
20
|
throw new DatasourceError(`Deep write requires a CRUD repository for "${into}"`);
|
|
20
21
|
};
|
|
21
22
|
const dispatch = (mode, runs) => (runs[mode] ??
|
|
@@ -34,6 +35,11 @@ export class DefaultWriteEnrichmentStrategy {
|
|
|
34
35
|
this.createStrategy = createStrategy;
|
|
35
36
|
this.useTransaction = useTransaction;
|
|
36
37
|
}
|
|
38
|
+
resolveLookups = async (tree, items) => {
|
|
39
|
+
const entries = enrichmentsOf(tree.parent.value);
|
|
40
|
+
if (entries)
|
|
41
|
+
await this.createStrategy.resolveLookups(entries, items);
|
|
42
|
+
};
|
|
37
43
|
persist = async (tree, items, mode) => {
|
|
38
44
|
await this.walk(tree.parent.value, items, mode);
|
|
39
45
|
};
|
package/dist/DriverLoader.d.ts
CHANGED
package/dist/DriverLoader.js
CHANGED
|
@@ -1,118 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { join } from "node:path";
|
|
6
3
|
import { ProviderNotInstalledError } from "./ProviderNotInstalledError.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
createRequire(join(process.cwd(), "package.json")),
|
|
10
|
-
...(process.argv[1] ? [createRequire(process.argv[1])] : []),
|
|
11
|
-
require,
|
|
12
|
-
];
|
|
13
|
-
const missingModule = (error) => error instanceof Error &&
|
|
14
|
-
"code" in error &&
|
|
15
|
-
(error.code === "MODULE_NOT_FOUND" ||
|
|
16
|
-
error.code === "ERR_MODULE_NOT_FOUND" ||
|
|
17
|
-
error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED");
|
|
18
|
-
const virtual = new Map();
|
|
19
|
-
const virtualBySource = new Map();
|
|
20
|
-
let typeScriptHook = false;
|
|
21
|
-
let compiling = false;
|
|
22
|
-
let compilingRoot = "";
|
|
23
|
-
let nextVirtual = 0;
|
|
24
|
-
/* v8 ignore start */
|
|
25
|
-
const underRoot = (url) => {
|
|
26
|
-
if (!url.startsWith("file:"))
|
|
27
|
-
return false;
|
|
28
|
-
const path = fileURLToPath(url);
|
|
29
|
-
return path === compilingRoot || path.startsWith(`${compilingRoot}/`);
|
|
30
|
-
};
|
|
31
|
-
/* v8 ignore stop */
|
|
32
|
-
const installTypeScriptHook = () => {
|
|
33
|
-
if (typeScriptHook)
|
|
34
|
-
return;
|
|
35
|
-
const typescript = require("typescript");
|
|
36
|
-
/* v8 ignore start */
|
|
37
|
-
registerHooks({
|
|
38
|
-
resolve: (specifier, context, nextResolve) => {
|
|
39
|
-
const parentURL = context.parentURL ? virtual.get(context.parentURL) ?? context.parentURL : undefined;
|
|
40
|
-
const resolved = nextResolve(specifier, parentURL ? { ...context, parentURL } : context);
|
|
41
|
-
/* v8 ignore next */
|
|
42
|
-
if (!compiling || !resolved.url.endsWith(".ts") || !underRoot(resolved.url))
|
|
43
|
-
return resolved;
|
|
44
|
-
const existing = virtualBySource.get(resolved.url);
|
|
45
|
-
/* v8 ignore next */
|
|
46
|
-
if (existing)
|
|
47
|
-
return { url: existing, shortCircuit: true };
|
|
48
|
-
const url = pathToFileURL(`${tmpdir()}/datasources-plugin-${nextVirtual++}.mjs`).href;
|
|
49
|
-
virtual.set(url, resolved.url);
|
|
50
|
-
virtualBySource.set(resolved.url, url);
|
|
51
|
-
return { url, shortCircuit: true };
|
|
52
|
-
},
|
|
53
|
-
load: (url, context, nextLoad) => {
|
|
54
|
-
const original = virtual.get(url) ?? (compiling && url.endsWith(".ts") && underRoot(url) ? url : undefined);
|
|
55
|
-
/* v8 ignore next */
|
|
56
|
-
if (!original)
|
|
57
|
-
return nextLoad(url, context);
|
|
58
|
-
const filename = fileURLToPath(original);
|
|
59
|
-
const source = typescript.transpileModule(readFileSync(filename, "utf8"), {
|
|
60
|
-
compilerOptions: {
|
|
61
|
-
module: typescript.ModuleKind.ESNext,
|
|
62
|
-
target: typescript.ScriptTarget.ES2022,
|
|
63
|
-
},
|
|
64
|
-
fileName: filename,
|
|
65
|
-
}).outputText;
|
|
66
|
-
return { format: "module", source, shortCircuit: true };
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
/* v8 ignore stop */
|
|
70
|
-
typeScriptHook = true;
|
|
71
|
-
};
|
|
72
|
-
const instantiate = (catalog) => {
|
|
73
|
-
compiling = true;
|
|
74
|
-
try {
|
|
75
|
-
return instantiateLoaded(catalog);
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
compiling = false;
|
|
79
|
-
compilingRoot = "";
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
const instantiateLoaded = (catalog) => {
|
|
83
|
-
let last;
|
|
84
|
-
for (const req of requirers()) {
|
|
4
|
+
export class DriverLoader {
|
|
5
|
+
static load(catalog) {
|
|
85
6
|
try {
|
|
86
|
-
|
|
87
|
-
const loaded = req(catalog.location);
|
|
7
|
+
const loaded = createRequire(join(process.cwd(), "package.json"))(catalog.location);
|
|
88
8
|
const Type = loaded[catalog.module];
|
|
89
|
-
|
|
90
|
-
throw new ProviderNotInstalledError(catalog.name);
|
|
91
|
-
return new Type();
|
|
9
|
+
return typeof Type === "function" ? new Type() : (() => { throw new ProviderNotInstalledError(catalog.name); })();
|
|
92
10
|
}
|
|
93
11
|
catch (error) {
|
|
94
|
-
|
|
95
|
-
if (error instanceof ProviderNotInstalledError || !missingModule(error))
|
|
96
|
-
throw error;
|
|
12
|
+
throw error instanceof ProviderNotInstalledError ? error : new ProviderNotInstalledError(catalog.name);
|
|
97
13
|
}
|
|
98
14
|
}
|
|
99
|
-
throw last instanceof ProviderNotInstalledError ? last : new ProviderNotInstalledError(catalog.name);
|
|
100
|
-
};
|
|
101
|
-
export class DriverLoader {
|
|
102
|
-
static load = (catalog) => {
|
|
103
|
-
try {
|
|
104
|
-
return instantiate(catalog);
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
if (error instanceof ProviderNotInstalledError)
|
|
108
|
-
throw error;
|
|
109
|
-
try {
|
|
110
|
-
installTypeScriptHook();
|
|
111
|
-
return instantiate(catalog);
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
throw new ProviderNotInstalledError(catalog.name);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
15
|
}
|
|
@@ -4,4 +4,5 @@ export interface IWriteEnrichmentStrategy<T> {
|
|
|
4
4
|
readonly useTransaction: boolean;
|
|
5
5
|
persist(tree: IRepositoryTree, items: T[], mode: WriteMode): Promise<void>;
|
|
6
6
|
persistBulk?(tree: IRepositoryTree, items: T[], mode: WriteMode): Promise<void>;
|
|
7
|
+
resolveLookups?(tree: IRepositoryTree, items: T[]): Promise<void>;
|
|
7
8
|
}
|
|
@@ -18,7 +18,7 @@ export type Sourced<T> = {
|
|
|
18
18
|
item: T;
|
|
19
19
|
source?: Row;
|
|
20
20
|
};
|
|
21
|
-
export declare const enrichmentsOf: (host: object) => readonly StoredEnrichment[] | undefined;
|
|
21
|
+
export declare const enrichmentsOf: <T>(host: object) => readonly StoredEnrichment<T>[] | undefined;
|
|
22
22
|
export declare const isBuilt: <T>(value: IConnection | BuiltRepository<T>) => value is BuiltRepository<T>;
|
|
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> {
|
|
@@ -42,6 +42,7 @@ export declare class ReadonlyRepository<T> implements IReadonlyRepository<T>, IE
|
|
|
42
42
|
readSingle(query?: ReadQuery, parameters?: Parameter[], options?: ReadonlyRepositoryMappingOptions<T>): Promise<T | undefined>;
|
|
43
43
|
protected get enriched(): boolean;
|
|
44
44
|
protected command: () => ICommand;
|
|
45
|
+
private ensureOpen;
|
|
45
46
|
protected column: (property: string) => string;
|
|
46
47
|
protected cell: (property: string, value: Cell, fieldsApplied: boolean, options?: ReadonlyRepositoryMappingOptions<T>) => Cell;
|
|
47
48
|
private entries;
|
|
@@ -56,7 +56,7 @@ export class ReadonlyRepository {
|
|
|
56
56
|
};
|
|
57
57
|
addEnriched(enrichment, via, strategy) {
|
|
58
58
|
const chosen = strategy ?? new DefaultReadonlyEnrichmentStrategy();
|
|
59
|
-
this.entries().push(new StoredEnrichment(enrichment, (parents, sources) => chosen.apply(via, enrichment, parents, sources)));
|
|
59
|
+
this.entries().push(new StoredEnrichment(enrichment, (parents, sources) => chosen.apply(via, enrichment, parents, sources), undefined, undefined, via));
|
|
60
60
|
return this;
|
|
61
61
|
}
|
|
62
62
|
async *read(query, parameters, options) {
|
|
@@ -72,6 +72,13 @@ export class ReadonlyRepository {
|
|
|
72
72
|
return this.entries().length > 0;
|
|
73
73
|
}
|
|
74
74
|
command = () => this.middlewares.reduce((command, middleware) => command.addMiddleware(middleware), this.connection.createCommand());
|
|
75
|
+
ensureOpen = async () => {
|
|
76
|
+
const connection = this.connection;
|
|
77
|
+
if (!connection)
|
|
78
|
+
throw new DatasourceError("Connection required");
|
|
79
|
+
if (!connection.isOpen)
|
|
80
|
+
await connection.open();
|
|
81
|
+
};
|
|
75
82
|
column = (property) => this.statementMappers.column(property);
|
|
76
83
|
cell = (property, value, fieldsApplied, options) => {
|
|
77
84
|
const objects = options?.objectConverter ?? this.objectConverter;
|
|
@@ -107,6 +114,7 @@ export class ReadonlyRepository {
|
|
|
107
114
|
: objects.convert(row);
|
|
108
115
|
async *readItems(query, parameters, mappers, rows, objects) {
|
|
109
116
|
const statement = query ?? this.readonlyStatements.select();
|
|
117
|
+
await this.ensureOpen();
|
|
110
118
|
const reader = await executeReader(this.command(), statement, parameters);
|
|
111
119
|
try {
|
|
112
120
|
while (await reader.next()) {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { Enrichment } from "./Enrichment.js";
|
|
2
2
|
import type { ICRUDRepository } from "./ICRUDRepository.js";
|
|
3
|
+
import type { IReadonlyRepository } from "./IReadonlyRepository.js";
|
|
3
4
|
import type { Row } from "./Row.js";
|
|
4
|
-
export type ApplyEnrichment<T
|
|
5
|
-
export declare class StoredEnrichment<T
|
|
5
|
+
export type ApplyEnrichment<T> = (parents: T[], sources?: (Row | undefined)[]) => Promise<void>;
|
|
6
|
+
export declare class StoredEnrichment<T, TRelated = T, TLink = TRelated> {
|
|
6
7
|
readonly enrichment: Enrichment;
|
|
7
8
|
apply: ApplyEnrichment<T>;
|
|
8
|
-
persist?: ICRUDRepository<
|
|
9
|
-
link?: ICRUDRepository<
|
|
10
|
-
|
|
9
|
+
persist?: ICRUDRepository<TRelated>;
|
|
10
|
+
link?: ICRUDRepository<TLink>;
|
|
11
|
+
via?: IReadonlyRepository<TRelated>;
|
|
12
|
+
constructor(enrichment: Enrichment, apply: ApplyEnrichment<T>, persist?: ICRUDRepository<TRelated>, link?: ICRUDRepository<TLink>, via?: IReadonlyRepository<TRelated>);
|
|
11
13
|
}
|
package/dist/StoredEnrichment.js
CHANGED
|
@@ -3,10 +3,12 @@ export class StoredEnrichment {
|
|
|
3
3
|
apply;
|
|
4
4
|
persist;
|
|
5
5
|
link;
|
|
6
|
-
|
|
6
|
+
via;
|
|
7
|
+
constructor(enrichment, apply, persist, link, via) {
|
|
7
8
|
this.enrichment = enrichment;
|
|
8
9
|
this.apply = apply;
|
|
9
10
|
this.persist = persist;
|
|
10
11
|
this.link = link;
|
|
12
|
+
this.via = via;
|
|
11
13
|
}
|
|
12
14
|
}
|
|
@@ -131,7 +131,7 @@ export class DefaultValueConverter {
|
|
|
131
131
|
const match = entry.reverse.exec(native);
|
|
132
132
|
if (match === null)
|
|
133
133
|
continue;
|
|
134
|
-
const arg = match
|
|
134
|
+
const arg = match.slice(1).find((group) => group !== undefined) ?? "";
|
|
135
135
|
return entry.specText === undefined ? name : entry.specText(arg);
|
|
136
136
|
}
|
|
137
137
|
throw new Error(`Native default '${native}' is not a valid ${specType} default`);
|
|
@@ -148,15 +148,17 @@ export class DefaultValueConverter {
|
|
|
148
148
|
String: token(numeric, (arg) => arg, numeric.spec),
|
|
149
149
|
};
|
|
150
150
|
};
|
|
151
|
+
const quote = opts.quote ?? quoted;
|
|
152
|
+
const quoteReverse = opts.quoteReverse ?? quotedReverse;
|
|
151
153
|
const quotedString = {
|
|
152
|
-
String: token(spec.string.String,
|
|
153
|
-
Numeric: token(spec.string.String,
|
|
154
|
+
String: token(spec.string.String, quote, quoteReverse),
|
|
155
|
+
Numeric: token(spec.string.String, quote, quoteReverse),
|
|
154
156
|
};
|
|
155
157
|
const dictionary = {
|
|
156
158
|
string: quotedString,
|
|
157
159
|
character: {
|
|
158
|
-
String: token(spec.character.String,
|
|
159
|
-
Numeric: token(spec.character.String,
|
|
160
|
+
String: token(spec.character.String, quote, quoteReverse),
|
|
161
|
+
Numeric: token(spec.character.String, quote, quoteReverse),
|
|
160
162
|
},
|
|
161
163
|
boolean: {
|
|
162
164
|
Boolean: token(spec.boolean.Boolean, (arg) => (arg === "true" ? opts.booleanOn : opts.booleanOff), new RegExp(`^(${escapeRe(opts.booleanOn)}|${escapeRe(opts.booleanOff)})$`)),
|
|
@@ -6,6 +6,8 @@ export class ConditionRenderer {
|
|
|
6
6
|
}
|
|
7
7
|
render = (condition) => condition.accept(this);
|
|
8
8
|
visit(where) {
|
|
9
|
-
return where instanceof Where
|
|
9
|
+
return where instanceof Where || where.constructor.name === "Where"
|
|
10
|
+
? this.expressions.render(where.condition)
|
|
11
|
+
: where.rawText;
|
|
10
12
|
}
|
|
11
13
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Column } from "./Column.js";
|
|
2
2
|
import type { CreateTableStatement } from "./CreateTableStatement.js";
|
|
3
3
|
import type { ITableConstraint } from "./ITableConstraint.js";
|
|
4
|
+
import type { NotNull } from "./NotNull.js";
|
|
4
5
|
export declare class CreateTableStatementTextRenderer {
|
|
5
6
|
render(statement: CreateTableStatement): string;
|
|
6
7
|
protected quote(name: string): string;
|
|
@@ -11,7 +12,7 @@ export declare class CreateTableStatementTextRenderer {
|
|
|
11
12
|
protected column(column: Column): string;
|
|
12
13
|
protected constraint(constraint: ITableConstraint): string;
|
|
13
14
|
private parts;
|
|
14
|
-
|
|
15
|
+
protected notNullSql(value?: NotNull): string;
|
|
15
16
|
private foreignKey;
|
|
16
17
|
private quoted;
|
|
17
18
|
private actionSql;
|
|
@@ -4,69 +4,70 @@ import { Literal } from "./Literal.js";
|
|
|
4
4
|
import { Operator } from "./Operator.js";
|
|
5
5
|
import { Subquery } from "./Subquery.js";
|
|
6
6
|
import { Where } from "./Where.js";
|
|
7
|
+
const is = (value, type, name) => value instanceof type || value.constructor.name === name;
|
|
7
8
|
export class ExpressionRenderer {
|
|
8
9
|
text;
|
|
9
10
|
constructor(text) {
|
|
10
11
|
this.text = text;
|
|
11
12
|
}
|
|
12
13
|
render = (expression) => {
|
|
13
|
-
if (expression
|
|
14
|
+
if (is(expression, Where, "Where"))
|
|
14
15
|
return this.render(expression.condition);
|
|
15
|
-
if (expression
|
|
16
|
+
if (is(expression, Identifier, "Identifier"))
|
|
16
17
|
return expression.name;
|
|
17
|
-
if (expression
|
|
18
|
+
if (is(expression, Literal, "Literal"))
|
|
18
19
|
return inline(expression.value);
|
|
19
|
-
if (expression
|
|
20
|
+
if (is(expression, ArrayExpression, "ArrayExpression"))
|
|
20
21
|
return expression.elements.map((e) => this.render(e)).join(", ");
|
|
21
|
-
if (expression
|
|
22
|
+
if (is(expression, Operator.Not, "Not"))
|
|
22
23
|
return `NOT (${this.render(expression.argument)})`;
|
|
23
|
-
if (expression
|
|
24
|
+
if (is(expression, Operator.IsNull, "IsNull"))
|
|
24
25
|
return `${this.render(expression.argument)} IS NULL`;
|
|
25
|
-
if (expression
|
|
26
|
+
if (is(expression, Operator.IsNotNull, "IsNotNull"))
|
|
26
27
|
return `${this.render(expression.argument)} IS NOT NULL`;
|
|
27
|
-
if (expression
|
|
28
|
+
if (is(expression, Operator.Exists, "Exists"))
|
|
28
29
|
return `EXISTS ${this.render(expression.argument)}`;
|
|
29
|
-
if (expression
|
|
30
|
+
if (is(expression, Subquery, "Subquery"))
|
|
30
31
|
return `(${this.text.render(expression.query)})`;
|
|
31
|
-
if (expression
|
|
32
|
-
expression.left
|
|
33
|
-
expression.right
|
|
32
|
+
if (is(expression, Operator.In, "In") &&
|
|
33
|
+
is(expression.left, Identifier, "Identifier") &&
|
|
34
|
+
is(expression.right, ArrayExpression, "ArrayExpression"))
|
|
34
35
|
return expression.right.elements.length === 0
|
|
35
36
|
? "1 = 0"
|
|
36
37
|
: `${expression.left.name} IN (${slots(expression.left.name, expression.right.elements.length)})`;
|
|
37
|
-
if (expression
|
|
38
|
-
expression.left
|
|
39
|
-
expression.right
|
|
38
|
+
if (is(expression, Operator.In, "In") &&
|
|
39
|
+
is(expression.left, Identifier, "Identifier") &&
|
|
40
|
+
is(expression.right, Subquery, "Subquery"))
|
|
40
41
|
return `${expression.left.name} IN ${this.render(expression.right)}`;
|
|
41
|
-
if (expression
|
|
42
|
-
expression.left
|
|
43
|
-
expression.right
|
|
42
|
+
if (is(expression, Operator.NotIn, "NotIn") &&
|
|
43
|
+
is(expression.left, Identifier, "Identifier") &&
|
|
44
|
+
is(expression.right, ArrayExpression, "ArrayExpression"))
|
|
44
45
|
return expression.right.elements.length === 0
|
|
45
46
|
? "1 = 1"
|
|
46
47
|
: `${expression.left.name} NOT IN (${slots(expression.left.name, expression.right.elements.length)})`;
|
|
47
|
-
if (expression
|
|
48
|
-
expression.left
|
|
49
|
-
expression.right
|
|
48
|
+
if (is(expression, Operator.NotIn, "NotIn") &&
|
|
49
|
+
is(expression.left, Identifier, "Identifier") &&
|
|
50
|
+
is(expression.right, Subquery, "Subquery"))
|
|
50
51
|
return `${expression.left.name} NOT IN ${this.render(expression.right)}`;
|
|
51
|
-
if ((expression
|
|
52
|
-
expression.value
|
|
53
|
-
expression.low
|
|
54
|
-
expression.high
|
|
55
|
-
const word = expression
|
|
52
|
+
if ((is(expression, Operator.Between, "Between") || is(expression, Operator.NotBetween, "NotBetween")) &&
|
|
53
|
+
is(expression.value, Identifier, "Identifier") &&
|
|
54
|
+
is(expression.low, Literal, "Literal") &&
|
|
55
|
+
is(expression.high, Literal, "Literal")) {
|
|
56
|
+
const word = is(expression, Operator.NotBetween, "NotBetween") ? "NOT BETWEEN" : "BETWEEN";
|
|
56
57
|
return `${expression.value.name} ${word} $${parameterName(expression.value.name)}_0 AND $${parameterName(expression.value.name)}_1`;
|
|
57
58
|
}
|
|
58
|
-
if (expression
|
|
59
|
+
if (is(expression, Operator.Between, "Between"))
|
|
59
60
|
return `${this.render(expression.value)} BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
|
|
60
|
-
if (expression
|
|
61
|
+
if (is(expression, Operator.NotBetween, "NotBetween"))
|
|
61
62
|
return `${this.render(expression.value)} NOT BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
|
|
62
|
-
if (expression
|
|
63
|
+
if (is(expression, Operator.And, "And"))
|
|
63
64
|
return `${this.wrap(expression.left, true)} AND ${this.wrap(expression.right, true)}`;
|
|
64
|
-
if (expression
|
|
65
|
+
if (is(expression, Operator.Or, "Or"))
|
|
65
66
|
return `${this.wrap(expression.left, false)} OR ${this.wrap(expression.right, false)}`;
|
|
66
|
-
const operands =
|
|
67
|
-
if (operands && operands.left
|
|
67
|
+
const operands = pair(expression);
|
|
68
|
+
if (operands && is(operands.left, Identifier, "Identifier") && is(operands.right, Literal, "Literal"))
|
|
68
69
|
return `${operands.left.name} ${symbol(expression)} $${parameterName(operands.left.name)}`;
|
|
69
|
-
if (operands && operands.left
|
|
70
|
+
if (operands && is(operands.left, Literal, "Literal") && is(operands.right, Literal, "Literal"))
|
|
70
71
|
return `${inline(operands.left.value)} ${symbol(expression)} ${inline(operands.right.value)}`;
|
|
71
72
|
if (operands)
|
|
72
73
|
return `${this.render(operands.left)} ${symbol(expression)} ${this.render(operands.right)}`;
|
|
@@ -74,23 +75,34 @@ export class ExpressionRenderer {
|
|
|
74
75
|
};
|
|
75
76
|
wrap = (child, and) => needsParens(child, and) ? `(${this.render(child)})` : this.render(child);
|
|
76
77
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
const symbols = {
|
|
79
|
+
Equals: "=",
|
|
80
|
+
NotEquals: "<>",
|
|
81
|
+
GreaterThan: ">",
|
|
82
|
+
LessThan: "<",
|
|
83
|
+
GreaterThanOrEqual: ">=",
|
|
84
|
+
LessThanOrEqual: "<=",
|
|
85
|
+
Like: "LIKE",
|
|
86
|
+
NotLike: "NOT LIKE",
|
|
87
|
+
};
|
|
88
|
+
const symbol = (op) => symbols[op.constructor.name] ??
|
|
89
|
+
(is(op, Operator.Equals, "Equals")
|
|
90
|
+
? "="
|
|
91
|
+
: is(op, Operator.NotEquals, "NotEquals")
|
|
92
|
+
? "<>"
|
|
93
|
+
: is(op, Operator.GreaterThan, "GreaterThan")
|
|
94
|
+
? ">"
|
|
95
|
+
: is(op, Operator.LessThan, "LessThan")
|
|
96
|
+
? "<"
|
|
97
|
+
: is(op, Operator.GreaterThanOrEqual, "GreaterThanOrEqual")
|
|
98
|
+
? ">="
|
|
99
|
+
: is(op, Operator.LessThanOrEqual, "LessThanOrEqual")
|
|
100
|
+
? "<="
|
|
101
|
+
: is(op, Operator.Like, "Like")
|
|
102
|
+
? "LIKE"
|
|
103
|
+
: is(op, Operator.NotLike, "NotLike")
|
|
104
|
+
? "NOT LIKE"
|
|
105
|
+
: "IN");
|
|
94
106
|
const inline = (value) => value === null
|
|
95
107
|
? "null"
|
|
96
108
|
: typeof value === "boolean"
|
|
@@ -98,24 +110,24 @@ const inline = (value) => value === null
|
|
|
98
110
|
: typeof value === "string"
|
|
99
111
|
? `'${value.replace(/'/g, "''")}'`
|
|
100
112
|
: String(value);
|
|
101
|
-
const needsParens = (child, and) => child
|
|
113
|
+
const needsParens = (child, and) => is(child, Where, "Where")
|
|
102
114
|
? needsParens(child.condition, and)
|
|
103
|
-
: child
|
|
104
|
-
child
|
|
105
|
-
child
|
|
106
|
-
(child
|
|
107
|
-
(child
|
|
108
|
-
const pair = (expression) => expression
|
|
109
|
-
expression
|
|
110
|
-
expression
|
|
111
|
-
expression
|
|
112
|
-
expression
|
|
113
|
-
expression
|
|
114
|
-
expression
|
|
115
|
-
expression
|
|
116
|
-
expression
|
|
117
|
-
expression
|
|
118
|
-
expression
|
|
115
|
+
: is(child, Operator.Not, "Not") ||
|
|
116
|
+
is(child, Operator.In, "In") ||
|
|
117
|
+
is(child, Operator.NotIn, "NotIn") ||
|
|
118
|
+
(is(child, Operator.And, "And") && !and) ||
|
|
119
|
+
(is(child, Operator.Or, "Or") && and);
|
|
120
|
+
const pair = (expression) => is(expression, Operator.Equals, "Equals") ||
|
|
121
|
+
is(expression, Operator.NotEquals, "NotEquals") ||
|
|
122
|
+
is(expression, Operator.GreaterThan, "GreaterThan") ||
|
|
123
|
+
is(expression, Operator.LessThan, "LessThan") ||
|
|
124
|
+
is(expression, Operator.GreaterThanOrEqual, "GreaterThanOrEqual") ||
|
|
125
|
+
is(expression, Operator.LessThanOrEqual, "LessThanOrEqual") ||
|
|
126
|
+
is(expression, Operator.And, "And") ||
|
|
127
|
+
is(expression, Operator.Or, "Or") ||
|
|
128
|
+
is(expression, Operator.In, "In") ||
|
|
129
|
+
is(expression, Operator.Like, "Like") ||
|
|
130
|
+
is(expression, Operator.NotLike, "NotLike")
|
|
119
131
|
? expression
|
|
120
132
|
: undefined;
|
|
121
133
|
const parameterName = (name) => parts(name)
|