@deterministic-code/datasources 0.1.12 → 0.1.14
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/BulkCRUDRepository.js +2 -0
- package/dist/CRUDRepository.js +20 -9
- 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/CreateTableStatementTextRenderer.d.ts +2 -1
- package/package.json +1 -1
|
@@ -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.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))
|
|
@@ -56,9 +56,11 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
56
56
|
}
|
|
57
57
|
addEnriched(enrichment, via, strategy) {
|
|
58
58
|
super.addEnriched(enrichment, via, strategy);
|
|
59
|
+
const stored = lastEnrichment(this);
|
|
60
|
+
stored.via = via;
|
|
59
61
|
const writer = writerOf(via);
|
|
60
62
|
if (writer)
|
|
61
|
-
|
|
63
|
+
stored.persist = writer;
|
|
62
64
|
return this;
|
|
63
65
|
}
|
|
64
66
|
addLinked(enrichment, via, link, strategy) {
|
|
@@ -73,9 +75,12 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
73
75
|
update = (item, byWhere) => this.inTransaction(() => this.updateCore(item, byWhere));
|
|
74
76
|
delete = (item, byWhere) => this.inTransaction(() => this.deleteCore(item, byWhere));
|
|
75
77
|
inTransaction = async (write) => {
|
|
78
|
+
const connection = this.command().connection;
|
|
79
|
+
if (!connection.isOpen)
|
|
80
|
+
await connection.open();
|
|
76
81
|
if (!this.useTransaction)
|
|
77
82
|
return write();
|
|
78
|
-
const work = new UnitOfWorkTransaction(
|
|
83
|
+
const work = new UnitOfWorkTransaction(connection);
|
|
79
84
|
await work.begin();
|
|
80
85
|
try {
|
|
81
86
|
const result = await write();
|
|
@@ -88,6 +93,7 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
88
93
|
}
|
|
89
94
|
};
|
|
90
95
|
createCore = async (item) => {
|
|
96
|
+
await this.write.resolveLookups?.(this.tree, [item]);
|
|
91
97
|
await this.insert(item);
|
|
92
98
|
if (!this.enriched)
|
|
93
99
|
return item;
|
|
@@ -98,6 +104,7 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
98
104
|
return id === undefined ? item : (await this.readSingleById(id)) ?? item;
|
|
99
105
|
};
|
|
100
106
|
updateCore = async (item, byWhere) => {
|
|
107
|
+
await this.write.resolveLookups?.(this.tree, [item]);
|
|
101
108
|
const response = await this.save(item, byWhere);
|
|
102
109
|
if (this.enriched)
|
|
103
110
|
await this.write.persist(this.tree, [item], "update");
|
|
@@ -110,8 +117,9 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
110
117
|
};
|
|
111
118
|
readSingleById = async (id) => this.readSingle(this.byId(id));
|
|
112
119
|
insert = async (item) => {
|
|
120
|
+
const extras = lookupKeys(item);
|
|
113
121
|
const record = this.reverseConvert(item);
|
|
114
|
-
const fields = fieldsOf(record, (col) => listed(this.createProperties, col, !this.ids.includes(col) || assigned(record[col] ?? null)), this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column);
|
|
122
|
+
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
123
|
const statement = this.crudStatements.insert(fields);
|
|
116
124
|
const response = await execute(this.command(), statement, statement.fields);
|
|
117
125
|
if (this.ids.length === 1 && !fields.some((field) => field.name === this.ids[0]))
|
|
@@ -145,8 +153,9 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
145
153
|
};
|
|
146
154
|
idValue = (item) => scalarOf(item[this.ids[0]]);
|
|
147
155
|
save = async (item, byWhere) => {
|
|
156
|
+
const extras = lookupKeys(item);
|
|
148
157
|
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);
|
|
158
|
+
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
159
|
if (fields.length === 0)
|
|
151
160
|
throw new DatasourceError("Update requires at least one non-Id field");
|
|
152
161
|
const statement = this.crudStatements.update(fields, byWhere ?? this.idWhere(record), (name, value) => this.cell(name, value, byWhere === undefined));
|
|
@@ -159,7 +168,7 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
159
168
|
const parameters = listed.length
|
|
160
169
|
? listed
|
|
161
170
|
: DefaultStatementTextRenderer.instance.render(statement).includes("$")
|
|
162
|
-
? fieldsOf(record, () => true, this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column)
|
|
171
|
+
? fieldsOf(record, () => true, new Set(), this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column)
|
|
163
172
|
: [];
|
|
164
173
|
return execute(this.command(), statement, parameters);
|
|
165
174
|
};
|
|
@@ -174,7 +183,9 @@ export class CRUDRepository extends ReadonlyRepository {
|
|
|
174
183
|
};
|
|
175
184
|
reverseConvert = (item) => {
|
|
176
185
|
const row = this.objectConverter.reverseConvert(item);
|
|
177
|
-
|
|
186
|
+
const converted = this.rowConverter ? this.rowConverter.reverseConvert(row) : row;
|
|
187
|
+
mergeLookups(item, converted);
|
|
188
|
+
return converted;
|
|
178
189
|
};
|
|
179
190
|
idWhere = (record) => {
|
|
180
191
|
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)})$`)),
|
|
@@ -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;
|