@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.
@@ -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);
@@ -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
- lastEnrichment(this).persist = writer;
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(this.command().connection);
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
- return this.rowConverter ? this.rowConverter.reverseConvert(row) : row;
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
- collectChildren<TChild extends object>(writer: ICRUDRepository<TChild>, enrichment: Enrichment, parent: T): TChild[];
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
- collectChildren(writer, enrichment, parent) {
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
- stampCell = (related, by, key) => {
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
- Object.assign(related, { [by]: key });
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
  };
@@ -1,5 +1,5 @@
1
1
  import type { IDriver } from "./IDriver.js";
2
2
  import type { ProviderCatalog } from "./ProviderCatalog.js";
3
3
  export declare class DriverLoader {
4
- static load: (catalog: ProviderCatalog) => IDriver;
4
+ static load(catalog: ProviderCatalog): IDriver;
5
5
  }
@@ -1,118 +1,15 @@
1
- import { readFileSync } from "node:fs";
2
- import { createRequire, registerHooks } from "node:module";
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
- const require = createRequire(import.meta.url);
8
- const requirers = () => [
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
- compilingRoot = dirname(req.resolve(catalog.location));
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
- if (typeof Type !== "function")
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
- last = error;
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 extends object = object> = (parents: T[], sources?: (Row | undefined)[]) => Promise<void>;
5
- export declare class StoredEnrichment<T extends object = object> {
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<object>;
9
- link?: ICRUDRepository<object>;
10
- constructor(enrichment: Enrichment, apply: ApplyEnrichment<T>, persist?: ICRUDRepository<object>, link?: ICRUDRepository<object>);
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
  }
@@ -3,10 +3,12 @@ export class StoredEnrichment {
3
3
  apply;
4
4
  persist;
5
5
  link;
6
- constructor(enrichment, apply, persist, link) {
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[1] ?? "";
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, quoted, quotedReverse),
153
- Numeric: token(spec.string.String, quoted, quotedReverse),
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, quoted, quotedReverse),
159
- Numeric: token(spec.character.String, quoted, quotedReverse),
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,4 +6,6 @@ export type DefaultValueOptions = {
6
6
  booleanOff: string;
7
7
  hex: (arg: string) => string;
8
8
  hexReverse: RegExp;
9
+ quote?: (arg: string) => string;
10
+ quoteReverse?: RegExp;
9
11
  };
@@ -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
- private notNullSql;
15
+ protected notNullSql(value?: NotNull): string;
15
16
  private foreignKey;
16
17
  private quoted;
17
18
  private actionSql;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deterministic-code/datasources",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",