@snowtop/ent 0.2.6 → 0.2.8
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/action/executor.js +4 -4
- package/action/operations.js +3 -0
- package/action/orchestrator.js +10 -12
- package/action/topological_sort.d.ts +9 -0
- package/action/topological_sort.js +46 -0
- package/core/async_utils.d.ts +1 -0
- package/core/async_utils.js +29 -0
- package/core/base.d.ts +12 -5
- package/core/clause.d.ts +3 -5
- package/core/clause.js +32 -0
- package/core/config.d.ts +28 -2
- package/core/config.js +14 -1
- package/core/context.d.ts +3 -1
- package/core/context.js +90 -26
- package/core/db.d.ts +12 -2
- package/core/db.js +102 -7
- package/core/dev_schema.d.ts +9 -0
- package/core/dev_schema.js +306 -0
- package/core/ent.d.ts +5 -7
- package/core/ent.js +33 -48
- package/core/extensions.d.ts +25 -0
- package/core/extensions.js +220 -0
- package/core/loaders/assoc_count_loader.js +3 -6
- package/core/loaders/assoc_edge_loader.d.ts +3 -0
- package/core/loaders/assoc_edge_loader.js +48 -19
- package/core/loaders/index.d.ts +2 -1
- package/core/loaders/index.js +5 -1
- package/core/loaders/loader.d.ts +31 -0
- package/core/loaders/loader.js +141 -2
- package/core/loaders/object_loader.d.ts +2 -2
- package/core/loaders/object_loader.js +39 -57
- package/core/loaders/query_loader.d.ts +2 -5
- package/core/loaders/query_loader.js +45 -24
- package/core/loaders/raw_count_loader.d.ts +2 -2
- package/core/loaders/raw_count_loader.js +12 -14
- package/core/memoize.d.ts +1 -0
- package/core/memoize.js +15 -0
- package/core/metrics.d.ts +22 -0
- package/core/metrics.js +31 -0
- package/core/query/custom_clause_query.js +5 -1
- package/core/query/query.d.ts +1 -1
- package/core/query/query.js +10 -7
- package/core/query_expression.d.ts +6 -0
- package/core/query_expression.js +2 -0
- package/core/query_impl.d.ts +19 -3
- package/core/query_impl.js +148 -35
- package/index.d.ts +7 -2
- package/index.js +12 -2
- package/package.json +1 -7
- package/parse_schema/parse.d.ts +2 -12
- package/parse_schema/parse.js +22 -41
- package/schema/index.d.ts +1 -1
- package/schema/schema.d.ts +20 -1
- package/scripts/custom_graphql.js +12 -5
- package/scripts/fix_action_exports.js +1 -1
- package/scripts/migrate_v0.1.js +2 -5
- package/scripts/move_types.js +1 -1
- package/scripts/read_schema.js +2 -5
- package/testutils/builder.js +1 -2
- package/testutils/parse_sql.js +1 -1
- package/tsc/compilerOptions.d.ts +2 -2
- package/tsc/compilerOptions.js +12 -18
- package/tsc/move_generated.js +2 -2
- package/tsc/transform.d.ts +1 -1
- package/tsc/transform.js +16 -2
- package/tsc/transform_action.d.ts +1 -1
- package/tsc/transform_action.js +1 -1
- package/tsc/transform_ent.d.ts +1 -1
- package/tsc/transform_ent.js +1 -1
- package/tsc/transform_schema.d.ts +1 -1
- package/tsc/transform_schema.js +2 -2
- /package/core/{loaders/cache_utils.d.ts → cache_utils.d.ts} +0 -0
- /package/core/{loaders/cache_utils.js → cache_utils.js} +0 -0
package/action/executor.js
CHANGED
|
@@ -5,10 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ComplexExecutor = exports.ListBasedExecutor = void 0;
|
|
7
7
|
exports.executeOperations = executeOperations;
|
|
8
|
-
const graph_data_structure_1 = require("graph-data-structure");
|
|
9
8
|
const ent_1 = require("../core/ent");
|
|
10
9
|
const db_1 = __importDefault(require("../core/db"));
|
|
11
10
|
const logger_1 = require("../core/logger");
|
|
11
|
+
const topological_sort_1 = require("./topological_sort");
|
|
12
12
|
const operations_1 = require("./operations");
|
|
13
13
|
// private to ent
|
|
14
14
|
class ListBasedExecutor {
|
|
@@ -123,7 +123,7 @@ class ComplexExecutor {
|
|
|
123
123
|
this.executors = [];
|
|
124
124
|
this.changedOps = new Map();
|
|
125
125
|
this.builder = options?.builder;
|
|
126
|
-
const graph = new
|
|
126
|
+
const graph = new topological_sort_1.TopologicalGraph();
|
|
127
127
|
const changesetMap = new Map();
|
|
128
128
|
const impl = (c) => {
|
|
129
129
|
changesetMap.set(c.placeholderID.toString(), c);
|
|
@@ -131,7 +131,7 @@ class ComplexExecutor {
|
|
|
131
131
|
if (c.dependencies) {
|
|
132
132
|
for (let [_, builder] of c.dependencies) {
|
|
133
133
|
// dependency should go first...
|
|
134
|
-
graph.addEdge(builder.placeholderID.toString(), c.placeholderID.toString()
|
|
134
|
+
graph.addEdge(builder.placeholderID.toString(), c.placeholderID.toString());
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
if (c.changesets) {
|
|
@@ -156,7 +156,7 @@ class ComplexExecutor {
|
|
|
156
156
|
// TODO: can this logic be rewritten to not have a set yet avoid duplicates?
|
|
157
157
|
let nodeOps = new Set();
|
|
158
158
|
let remainOps = new Set();
|
|
159
|
-
const sorted =
|
|
159
|
+
const sorted = graph.topologicalSort();
|
|
160
160
|
sorted.forEach((node) => {
|
|
161
161
|
let c = changesetMap.get(node);
|
|
162
162
|
if (!c) {
|
package/action/operations.js
CHANGED
|
@@ -237,6 +237,9 @@ class EditNodeOperation {
|
|
|
237
237
|
return `RETURNING ${this.options.loadEntOptions.fields
|
|
238
238
|
.map((f) => {
|
|
239
239
|
if (typeof f === "object") {
|
|
240
|
+
if ("expression" in f) {
|
|
241
|
+
throw new Error("RETURNING does not support computed select expressions");
|
|
242
|
+
}
|
|
240
243
|
return `${f.alias}.${f.column}`;
|
|
241
244
|
}
|
|
242
245
|
return f;
|
package/action/orchestrator.js
CHANGED
|
@@ -32,9 +32,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
|
|
40
37
|
const ent_1 = require("../core/ent");
|
|
@@ -43,8 +40,8 @@ const operations_1 = require("./operations");
|
|
|
43
40
|
const action_1 = require("../action");
|
|
44
41
|
const privacy_1 = require("../core/privacy");
|
|
45
42
|
const executor_1 = require("./executor");
|
|
43
|
+
const memoize_1 = require("../core/memoize");
|
|
46
44
|
const logger_1 = require("../core/logger");
|
|
47
|
-
const memoizee_1 = __importDefault(require("memoizee"));
|
|
48
45
|
const clause = __importStar(require("../core/clause"));
|
|
49
46
|
const types_1 = require("util/types");
|
|
50
47
|
const operations_2 = require("./operations");
|
|
@@ -112,7 +109,7 @@ class Orchestrator {
|
|
|
112
109
|
this.viewer = options.viewer;
|
|
113
110
|
this.actualOperation = this.options.operation;
|
|
114
111
|
this.existingEnt = this.options.builder.existingEnt;
|
|
115
|
-
this.memoizedGetFields = (0,
|
|
112
|
+
this.memoizedGetFields = (0, memoize_1.memoizeNoArgs)(this.getFieldsInfo.bind(this));
|
|
116
113
|
}
|
|
117
114
|
// don't type this because we don't care
|
|
118
115
|
__getOptions() {
|
|
@@ -639,19 +636,20 @@ class Orchestrator {
|
|
|
639
636
|
for (const [k, field] of schemaFields) {
|
|
640
637
|
const inputKey = this.getInputKey(k);
|
|
641
638
|
const storageKey = this.getStorageKey(k);
|
|
642
|
-
let
|
|
643
|
-
if (
|
|
644
|
-
|
|
639
|
+
let inputVal = transformed.data[inputKey];
|
|
640
|
+
if (inputVal === undefined) {
|
|
641
|
+
inputVal = transformed.data[storageKey];
|
|
645
642
|
}
|
|
646
|
-
if (
|
|
643
|
+
if (inputVal === undefined) {
|
|
647
644
|
continue;
|
|
648
645
|
}
|
|
646
|
+
let dbVal = inputVal;
|
|
649
647
|
if (field.format) {
|
|
650
|
-
|
|
648
|
+
dbVal = field.format(inputVal, true);
|
|
651
649
|
}
|
|
652
|
-
data[this.getStorageKey(k)] =
|
|
650
|
+
data[this.getStorageKey(k)] = dbVal;
|
|
653
651
|
if (!field.immutable) {
|
|
654
|
-
this.defaultFieldsByTSName[this.getInputKey(k)] =
|
|
652
|
+
this.defaultFieldsByTSName[this.getInputKey(k)] = inputVal;
|
|
655
653
|
}
|
|
656
654
|
// hmm do we need this?
|
|
657
655
|
// TODO how to do this for local tests?
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TopologicalGraph = void 0;
|
|
4
|
+
class TopologicalGraph {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.nodes = new Set();
|
|
7
|
+
this.edges = new Map();
|
|
8
|
+
}
|
|
9
|
+
addNode(node) {
|
|
10
|
+
this.nodes.add(node);
|
|
11
|
+
if (!this.edges.has(node)) {
|
|
12
|
+
this.edges.set(node, new Set());
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
addEdge(from, to) {
|
|
16
|
+
this.addNode(from);
|
|
17
|
+
this.addNode(to);
|
|
18
|
+
this.edges.get(from).add(to);
|
|
19
|
+
}
|
|
20
|
+
topologicalSort() {
|
|
21
|
+
const ordered = [];
|
|
22
|
+
const seen = new Map();
|
|
23
|
+
const visit = (node) => {
|
|
24
|
+
const state = seen.get(node);
|
|
25
|
+
if (state === 1) {
|
|
26
|
+
throw new Error("Cycle found");
|
|
27
|
+
}
|
|
28
|
+
if (state === 2) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
seen.set(node, 1);
|
|
32
|
+
for (const target of this.edges.get(node) || []) {
|
|
33
|
+
visit(target);
|
|
34
|
+
}
|
|
35
|
+
seen.set(node, 2);
|
|
36
|
+
ordered.push(node);
|
|
37
|
+
};
|
|
38
|
+
for (const node of this.nodes) {
|
|
39
|
+
if (seen.get(node) !== 2) {
|
|
40
|
+
visit(node);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return ordered.reverse();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.TopologicalGraph = TopologicalGraph;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function mapWithConcurrency<T, U>(items: T[], limit: number, mapper: (item: T, index: number) => Promise<U>): Promise<U[]>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapWithConcurrency = mapWithConcurrency;
|
|
4
|
+
async function mapWithConcurrency(items, limit, mapper) {
|
|
5
|
+
if (!items.length) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
const results = new Array(items.length);
|
|
9
|
+
const concurrency = limit === Infinity
|
|
10
|
+
? items.length
|
|
11
|
+
: Number.isFinite(limit) && limit > 0
|
|
12
|
+
? Math.max(1, Math.floor(limit))
|
|
13
|
+
: 1;
|
|
14
|
+
let nextIndex = 0;
|
|
15
|
+
const workers = new Array(Math.min(concurrency, items.length))
|
|
16
|
+
.fill(null)
|
|
17
|
+
.map(async () => {
|
|
18
|
+
while (true) {
|
|
19
|
+
const currentIndex = nextIndex;
|
|
20
|
+
if (currentIndex >= items.length) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
nextIndex += 1;
|
|
24
|
+
results[currentIndex] = await mapper(items[currentIndex], currentIndex);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
await Promise.all(workers);
|
|
28
|
+
return results;
|
|
29
|
+
}
|
package/core/base.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as clause from "./clause";
|
|
2
2
|
import { ObjectLoaderFactory } from "./loaders";
|
|
3
3
|
import { OrderBy } from "./query_impl";
|
|
4
|
+
import { QueryExpression } from "./query_expression";
|
|
4
5
|
export interface Loader<K, V> {
|
|
5
6
|
context?: Context;
|
|
6
7
|
load(key: K): Promise<V>;
|
|
@@ -26,7 +27,7 @@ export interface PrimableLoader<K, V> extends Loader<K, V> {
|
|
|
26
27
|
prime(d: V): void;
|
|
27
28
|
primeAll?(d: V): void;
|
|
28
29
|
}
|
|
29
|
-
export type QueryOptions = Required<Pick<LoadRowsOptions, "clause" | "fields" | "tableName">> & Pick<LoadRowsOptions, "orderby" | "join">;
|
|
30
|
+
export type QueryOptions = Required<Pick<LoadRowsOptions, "clause" | "fields" | "tableName">> & Pick<LoadRowsOptions, "distinct" | "alias" | "fieldsAlias" | "disableFieldsAlias" | "disableDefaultOrderByAlias" | "groupby" | "orderby" | "join" | "limit" | "offset">;
|
|
30
31
|
interface cache {
|
|
31
32
|
getLoader<K, V>(name: string, create: () => Loader<K, V>): Loader<K, V>;
|
|
32
33
|
getLoaderWithLoadMany<K, V>(name: string, create: () => LoaderWithLoadMany<K, V>): LoaderWithLoadMany<K, V>;
|
|
@@ -61,16 +62,22 @@ export interface EntConstructor<TEnt extends Ent, TViewer extends Viewer = Viewe
|
|
|
61
62
|
new (viewer: TViewer, data: Data): TEnt;
|
|
62
63
|
}
|
|
63
64
|
export type ID = string | number;
|
|
65
|
+
export interface SelectColumnField {
|
|
66
|
+
alias: string;
|
|
67
|
+
column: string;
|
|
68
|
+
}
|
|
69
|
+
export interface SelectExpressionField {
|
|
70
|
+
alias: string;
|
|
71
|
+
expression: QueryExpression;
|
|
72
|
+
}
|
|
73
|
+
export type SelectField = string | SelectColumnField | SelectExpressionField;
|
|
64
74
|
export interface DataOptions {
|
|
65
75
|
tableName: string;
|
|
66
76
|
alias?: string;
|
|
67
77
|
context?: Context;
|
|
68
78
|
}
|
|
69
79
|
export interface SelectBaseDataOptions extends DataOptions {
|
|
70
|
-
fields:
|
|
71
|
-
alias: string;
|
|
72
|
-
column: string;
|
|
73
|
-
})[];
|
|
80
|
+
fields: SelectField[];
|
|
74
81
|
fieldsAlias?: string;
|
|
75
82
|
disableFieldsAlias?: boolean;
|
|
76
83
|
disableDefaultOrderByAlias?: boolean;
|
package/core/clause.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { Data, ID, SelectDataOptions } from "./base";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { QueryExpression } from "./query_expression";
|
|
3
|
+
export interface Clause<T extends Data = Data, K = keyof T> extends QueryExpression {
|
|
4
4
|
columns(): K[];
|
|
5
|
-
values(): any[];
|
|
6
|
-
instanceKey(): string;
|
|
7
|
-
logValues(): any[];
|
|
8
5
|
compositeOp?: string;
|
|
9
6
|
}
|
|
10
7
|
export interface SensitiveValue {
|
|
@@ -180,4 +177,5 @@ export declare function Modulo<T extends Data, K = keyof T>(col: K, value: any,
|
|
|
180
177
|
export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options: Pick<SelectDataOptions, "clause">, cls: Clause<V, K>, checkIntersection?: boolean): Clause<V, K>;
|
|
181
178
|
export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options: Pick<SelectDataOptions, "clause">, cls: Clause<V, K> | undefined, checkIntersection?: boolean): Clause<V, K> | undefined;
|
|
182
179
|
export declare function Expression<T extends Data, K = keyof T>(expression: string): Clause<T, K>;
|
|
180
|
+
export declare function ParameterizedExpression(key: string, expression: (idx: number, alias?: string) => string, values: any[], logValues?: any[]): QueryExpression;
|
|
183
181
|
export {};
|
package/core/clause.js
CHANGED
|
@@ -92,6 +92,7 @@ exports.Divide = Divide;
|
|
|
92
92
|
exports.Modulo = Modulo;
|
|
93
93
|
exports.getCombinedClause = getCombinedClause;
|
|
94
94
|
exports.Expression = Expression;
|
|
95
|
+
exports.ParameterizedExpression = ParameterizedExpression;
|
|
95
96
|
const db_1 = __importStar(require("./db"));
|
|
96
97
|
const query_impl_1 = require("./query_impl");
|
|
97
98
|
function isSensitive(val) {
|
|
@@ -256,6 +257,34 @@ class simpleExpression {
|
|
|
256
257
|
return `${this.expression}`;
|
|
257
258
|
}
|
|
258
259
|
}
|
|
260
|
+
class parameterizedExpression {
|
|
261
|
+
constructor(key, expression, params, logParams) {
|
|
262
|
+
this.key = key;
|
|
263
|
+
this.expression = expression;
|
|
264
|
+
this.params = params;
|
|
265
|
+
this.logParams = logParams;
|
|
266
|
+
}
|
|
267
|
+
clause(idx, alias) {
|
|
268
|
+
return this.expression(idx, alias);
|
|
269
|
+
}
|
|
270
|
+
values() {
|
|
271
|
+
return this.params.map((value) => rawValue(value));
|
|
272
|
+
}
|
|
273
|
+
logValues() {
|
|
274
|
+
if (this.logParams) {
|
|
275
|
+
return this.logParams;
|
|
276
|
+
}
|
|
277
|
+
return this.params.map((value) => {
|
|
278
|
+
if (isSensitive(value)) {
|
|
279
|
+
return value.logValue();
|
|
280
|
+
}
|
|
281
|
+
return value;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
instanceKey() {
|
|
285
|
+
return this.key;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
259
288
|
class arraySimpleClause {
|
|
260
289
|
constructor(col, value, op, overrideAlias) {
|
|
261
290
|
this.col = col;
|
|
@@ -1107,3 +1136,6 @@ function getCombinedClause(options, cls, checkIntersection = false) {
|
|
|
1107
1136
|
function Expression(expression) {
|
|
1108
1137
|
return new simpleExpression(expression);
|
|
1109
1138
|
}
|
|
1139
|
+
function ParameterizedExpression(key, expression, values, logValues) {
|
|
1140
|
+
return new parameterizedExpression(key, expression, values, logValues);
|
|
1141
|
+
}
|
package/core/config.d.ts
CHANGED
|
@@ -12,6 +12,14 @@ declare enum fieldPrivacyEvaluated {
|
|
|
12
12
|
AT_ENT_LOAD = "at_ent_load",
|
|
13
13
|
ON_DEMAND = "on_demand"
|
|
14
14
|
}
|
|
15
|
+
export interface RuntimeDBExtension {
|
|
16
|
+
name: string;
|
|
17
|
+
provisionedBy?: "ent" | "external";
|
|
18
|
+
version?: string;
|
|
19
|
+
installSchema?: string;
|
|
20
|
+
runtimeSchemas?: string[];
|
|
21
|
+
dropCascade?: boolean;
|
|
22
|
+
}
|
|
15
23
|
export interface Config {
|
|
16
24
|
dbConnectionString?: string;
|
|
17
25
|
dbFile?: string;
|
|
@@ -20,18 +28,36 @@ export interface Config {
|
|
|
20
28
|
logQueryWithError?: boolean;
|
|
21
29
|
defaultConnectionLimit?: number;
|
|
22
30
|
loaderMaxBatchSize?: number;
|
|
31
|
+
clauseLoaderConcurrency?: number;
|
|
32
|
+
entLoaderPrivacyConcurrencyLimit?: number;
|
|
33
|
+
devSchema?: RuntimeDevSchemaConfig;
|
|
34
|
+
extensions?: RuntimeDBExtension[];
|
|
23
35
|
}
|
|
24
|
-
export interface ConfigWithCodegen extends Config {
|
|
36
|
+
export interface ConfigWithCodegen extends Omit<Config, "devSchema"> {
|
|
25
37
|
codegen?: CodegenConfig;
|
|
26
38
|
databaseMigration?: DatabaseMigrationConfig;
|
|
27
39
|
customGraphQLJSONPath?: string;
|
|
28
40
|
dynamicScriptCustomGraphQLJSONPath?: string;
|
|
29
41
|
globalSchemaPath?: string;
|
|
42
|
+
devSchema?: DevSchemaConfig;
|
|
30
43
|
}
|
|
31
44
|
interface DatabaseMigrationConfig {
|
|
32
45
|
custom_sql_include?: string[];
|
|
33
46
|
custom_sql_exclude?: string[];
|
|
34
47
|
}
|
|
48
|
+
export interface DevSchemaPruneConfig {
|
|
49
|
+
enabled?: boolean;
|
|
50
|
+
days?: number;
|
|
51
|
+
}
|
|
52
|
+
export interface RuntimeDevSchemaConfig {
|
|
53
|
+
enabled?: boolean;
|
|
54
|
+
schemaName?: string;
|
|
55
|
+
includePublic?: boolean;
|
|
56
|
+
ignoreBranches?: string[];
|
|
57
|
+
}
|
|
58
|
+
export interface DevSchemaConfig extends RuntimeDevSchemaConfig {
|
|
59
|
+
prune?: DevSchemaPruneConfig;
|
|
60
|
+
}
|
|
35
61
|
interface CodegenConfig {
|
|
36
62
|
defaultEntPolicy?: PrivacyConfig;
|
|
37
63
|
defaultActionPolicy?: PrivacyConfig;
|
|
@@ -68,5 +94,5 @@ interface importedObject {
|
|
|
68
94
|
name: string;
|
|
69
95
|
alias?: string;
|
|
70
96
|
}
|
|
71
|
-
export declare function loadConfig(file?: string | Buffer | Config): void;
|
|
97
|
+
export declare function loadConfig(file?: string | Buffer | Config | ConfigWithCodegen): void;
|
|
72
98
|
export {};
|
package/core/config.js
CHANGED
|
@@ -43,6 +43,7 @@ const db_1 = __importDefault(require("./db"));
|
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
44
|
const logger_1 = require("./logger");
|
|
45
45
|
const ent_1 = require("./ent");
|
|
46
|
+
const object_loader_1 = require("./loaders/object_loader");
|
|
46
47
|
const loader_1 = require("./loaders/loader");
|
|
47
48
|
// ent.config.ts eventually. for now ent.yml
|
|
48
49
|
// or ent.yml?
|
|
@@ -65,11 +66,17 @@ function setConfig(cfg) {
|
|
|
65
66
|
if (cfg.log) {
|
|
66
67
|
(0, logger_1.setLogLevels)(cfg.log);
|
|
67
68
|
}
|
|
68
|
-
if (cfg.dbConnectionString ||
|
|
69
|
+
if (cfg.dbConnectionString ||
|
|
70
|
+
cfg.dbFile ||
|
|
71
|
+
cfg.db ||
|
|
72
|
+
cfg.devSchema ||
|
|
73
|
+
cfg.extensions) {
|
|
69
74
|
db_1.default.initDB({
|
|
70
75
|
connectionString: cfg.dbConnectionString,
|
|
71
76
|
dbFile: cfg.dbFile,
|
|
72
77
|
db: cfg.db,
|
|
78
|
+
devSchema: cfg.devSchema,
|
|
79
|
+
extensions: cfg.extensions,
|
|
73
80
|
});
|
|
74
81
|
}
|
|
75
82
|
(0, ent_1.___setLogQueryErrorWithError)(cfg.logQueryWithError);
|
|
@@ -79,6 +86,12 @@ function setConfig(cfg) {
|
|
|
79
86
|
if (cfg.loaderMaxBatchSize !== undefined) {
|
|
80
87
|
(0, loader_1.setLoaderMaxBatchSize)(cfg.loaderMaxBatchSize);
|
|
81
88
|
}
|
|
89
|
+
if (cfg.clauseLoaderConcurrency !== undefined) {
|
|
90
|
+
(0, object_loader_1.setClauseLoaderConcurrency)(cfg.clauseLoaderConcurrency);
|
|
91
|
+
}
|
|
92
|
+
if (cfg.entLoaderPrivacyConcurrencyLimit !== undefined) {
|
|
93
|
+
(0, ent_1.setEntLoaderPrivacyConcurrencyLimit)(cfg.entLoaderPrivacyConcurrencyLimit);
|
|
94
|
+
}
|
|
82
95
|
}
|
|
83
96
|
function isBuffer(b) {
|
|
84
97
|
return b.write !== undefined;
|
package/core/context.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from "http";
|
|
2
2
|
import { Data, Loader, LoaderWithLoadMany, QueryOptions, Viewer } from "./base";
|
|
3
3
|
import { Context } from "./base";
|
|
4
|
+
export declare function getContextCacheMaxDiscardedLoaders(): number;
|
|
5
|
+
export declare function setContextCacheMaxDiscardedLoaders(size?: number | null): void;
|
|
4
6
|
export interface RequestContext<TViewer extends Viewer = Viewer> extends Context<TViewer> {
|
|
5
7
|
authViewer(viewer: TViewer): Promise<void>;
|
|
6
8
|
logout(): Promise<void>;
|
|
7
9
|
request: IncomingMessage;
|
|
8
10
|
response: ServerResponse;
|
|
9
11
|
}
|
|
12
|
+
export declare function getContextCacheKey(options: QueryOptions): string;
|
|
10
13
|
export declare class ContextCache {
|
|
11
14
|
loaders: Map<string, Loader<any, any>>;
|
|
12
15
|
loaderWithLoadMany: Map<string, LoaderWithLoadMany<any, any>>;
|
|
@@ -15,7 +18,6 @@ export declare class ContextCache {
|
|
|
15
18
|
getLoaderWithLoadMany<K, V>(name: string, create: () => LoaderWithLoadMany<K, V>): LoaderWithLoadMany<K, V>;
|
|
16
19
|
private itemMap;
|
|
17
20
|
private listMap;
|
|
18
|
-
private getkey;
|
|
19
21
|
getCachedRows(options: QueryOptions): Data[] | null;
|
|
20
22
|
getCachedRow(options: QueryOptions): Data | null;
|
|
21
23
|
primeCache(options: QueryOptions, rows: Data[]): void;
|
package/core/context.js
CHANGED
|
@@ -1,8 +1,71 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ContextCache = void 0;
|
|
4
|
+
exports.getContextCacheMaxDiscardedLoaders = getContextCacheMaxDiscardedLoaders;
|
|
5
|
+
exports.setContextCacheMaxDiscardedLoaders = setContextCacheMaxDiscardedLoaders;
|
|
6
|
+
exports.getContextCacheKey = getContextCacheKey;
|
|
4
7
|
const logger_1 = require("./logger");
|
|
8
|
+
const cache_utils_1 = require("./cache_utils");
|
|
9
|
+
const metrics_1 = require("./metrics");
|
|
5
10
|
const query_impl_1 = require("./query_impl");
|
|
11
|
+
const DEFAULT_MAX_DISCARDED_LOADERS = 1000;
|
|
12
|
+
let maxDiscardedLoaders = DEFAULT_MAX_DISCARDED_LOADERS;
|
|
13
|
+
function getContextCacheMaxDiscardedLoaders() {
|
|
14
|
+
return maxDiscardedLoaders;
|
|
15
|
+
}
|
|
16
|
+
function setContextCacheMaxDiscardedLoaders(size) {
|
|
17
|
+
if (size === undefined || size === null) {
|
|
18
|
+
maxDiscardedLoaders = DEFAULT_MAX_DISCARDED_LOADERS;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!Number.isFinite(size) || size < 0) {
|
|
22
|
+
throw new Error(`maxDiscardedLoaders must be a non-negative number`);
|
|
23
|
+
}
|
|
24
|
+
maxDiscardedLoaders = Math.floor(size);
|
|
25
|
+
}
|
|
26
|
+
function getContextCacheKey(options) {
|
|
27
|
+
const parts = [
|
|
28
|
+
`fields:${(0, query_impl_1.getSelectFieldsKey)(options.fields)}`,
|
|
29
|
+
`clause:${options.clause.instanceKey()}`,
|
|
30
|
+
];
|
|
31
|
+
if (options.distinct !== undefined) {
|
|
32
|
+
parts.push(`distinct:${options.distinct}`);
|
|
33
|
+
}
|
|
34
|
+
if (options.alias !== undefined) {
|
|
35
|
+
parts.push(`alias:${options.alias}`);
|
|
36
|
+
}
|
|
37
|
+
if (options.fieldsAlias !== undefined) {
|
|
38
|
+
parts.push(`fieldsAlias:${options.fieldsAlias}`);
|
|
39
|
+
}
|
|
40
|
+
if (options.disableFieldsAlias !== undefined) {
|
|
41
|
+
parts.push(`disableFieldsAlias:${options.disableFieldsAlias}`);
|
|
42
|
+
}
|
|
43
|
+
if (options.disableDefaultOrderByAlias !== undefined) {
|
|
44
|
+
parts.push(`disableDefaultOrderByAlias:${options.disableDefaultOrderByAlias}`);
|
|
45
|
+
}
|
|
46
|
+
if (options.groupby !== undefined) {
|
|
47
|
+
parts.push(`groupby:${options.groupby}`);
|
|
48
|
+
}
|
|
49
|
+
if (options.orderby) {
|
|
50
|
+
parts.push(`orderby:${(0, query_impl_1.getOrderByKey)(options.orderby)}`);
|
|
51
|
+
}
|
|
52
|
+
if (options.join) {
|
|
53
|
+
const joinKey = options.join.map((join) => ({
|
|
54
|
+
type: join.type ?? "inner",
|
|
55
|
+
tableName: join.tableName,
|
|
56
|
+
alias: join.alias,
|
|
57
|
+
clause: join.clause.instanceKey(),
|
|
58
|
+
}));
|
|
59
|
+
parts.push(`join:${(0, cache_utils_1.stableStringify)(joinKey)}`);
|
|
60
|
+
}
|
|
61
|
+
if (options.limit !== undefined) {
|
|
62
|
+
parts.push(`limit:${options.limit}`);
|
|
63
|
+
}
|
|
64
|
+
if (options.offset !== undefined) {
|
|
65
|
+
parts.push(`offset:${options.offset}`);
|
|
66
|
+
}
|
|
67
|
+
return parts.join(",");
|
|
68
|
+
}
|
|
6
69
|
class ContextCache {
|
|
7
70
|
constructor() {
|
|
8
71
|
this.loaders = new Map();
|
|
@@ -37,37 +100,24 @@ class ContextCache {
|
|
|
37
100
|
}
|
|
38
101
|
// tableName is ignored bcos already indexed on that
|
|
39
102
|
// maybe we just want to store sql queries???
|
|
40
|
-
getkey(options) {
|
|
41
|
-
let parts = [
|
|
42
|
-
options.fields
|
|
43
|
-
.map((f) => {
|
|
44
|
-
if (typeof f === "object") {
|
|
45
|
-
return `${f.alias}.${f.column}`;
|
|
46
|
-
}
|
|
47
|
-
return f;
|
|
48
|
-
})
|
|
49
|
-
.join(","),
|
|
50
|
-
options.clause.instanceKey(),
|
|
51
|
-
];
|
|
52
|
-
if (options.orderby) {
|
|
53
|
-
parts.push((0, query_impl_1.getOrderByPhrase)(options.orderby));
|
|
54
|
-
}
|
|
55
|
-
if (options.join) {
|
|
56
|
-
parts.push((0, query_impl_1.getJoinInfo)(options.join).phrase);
|
|
57
|
-
}
|
|
58
|
-
return parts.join(",");
|
|
59
|
-
}
|
|
60
103
|
getCachedRows(options) {
|
|
61
104
|
let m = this.listMap.get(options.tableName);
|
|
62
105
|
if (!m) {
|
|
63
106
|
return null;
|
|
64
107
|
}
|
|
65
|
-
const key =
|
|
108
|
+
const key = getContextCacheKey(options);
|
|
66
109
|
let rows = m.get(key);
|
|
67
110
|
if (rows) {
|
|
111
|
+
const hook = (0, metrics_1.getOnQueryCacheHit)();
|
|
112
|
+
if (hook) {
|
|
113
|
+
hook({
|
|
114
|
+
tableName: options.tableName,
|
|
115
|
+
key,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
68
118
|
(0, logger_1.log)("cache", {
|
|
69
119
|
"cache-hit": key,
|
|
70
|
-
|
|
120
|
+
tableName: options.tableName,
|
|
71
121
|
});
|
|
72
122
|
}
|
|
73
123
|
return rows || null;
|
|
@@ -77,12 +127,19 @@ class ContextCache {
|
|
|
77
127
|
if (!m) {
|
|
78
128
|
return null;
|
|
79
129
|
}
|
|
80
|
-
const key =
|
|
130
|
+
const key = getContextCacheKey(options);
|
|
81
131
|
let row = m.get(key);
|
|
82
132
|
if (row) {
|
|
133
|
+
const hook = (0, metrics_1.getOnQueryCacheHit)();
|
|
134
|
+
if (hook) {
|
|
135
|
+
hook({
|
|
136
|
+
tableName: options.tableName,
|
|
137
|
+
key,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
83
140
|
(0, logger_1.log)("cache", {
|
|
84
141
|
"cache-hit": key,
|
|
85
|
-
|
|
142
|
+
tableName: options.tableName,
|
|
86
143
|
});
|
|
87
144
|
}
|
|
88
145
|
return row || null;
|
|
@@ -90,12 +147,12 @@ class ContextCache {
|
|
|
90
147
|
primeCache(options, rows) {
|
|
91
148
|
if (Array.isArray(rows)) {
|
|
92
149
|
let m = this.listMap.get(options.tableName) || new Map();
|
|
93
|
-
m.set(
|
|
150
|
+
m.set(getContextCacheKey(options), rows);
|
|
94
151
|
this.listMap.set(options.tableName, m);
|
|
95
152
|
}
|
|
96
153
|
else {
|
|
97
154
|
let m = this.itemMap.get(options.tableName) || new Map();
|
|
98
|
-
m.set(
|
|
155
|
+
m.set(getContextCacheKey(options), rows);
|
|
99
156
|
this.itemMap.set(options.tableName, m);
|
|
100
157
|
}
|
|
101
158
|
}
|
|
@@ -113,6 +170,13 @@ class ContextCache {
|
|
|
113
170
|
this.loaderWithLoadMany.clear();
|
|
114
171
|
this.itemMap.clear();
|
|
115
172
|
this.listMap.clear();
|
|
173
|
+
if (maxDiscardedLoaders === 0) {
|
|
174
|
+
this.discardedLoaders = [];
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (this.discardedLoaders.length > maxDiscardedLoaders) {
|
|
178
|
+
this.discardedLoaders = this.discardedLoaders.slice(-maxDiscardedLoaders);
|
|
179
|
+
}
|
|
116
180
|
}
|
|
117
181
|
/**
|
|
118
182
|
* reset clears the cache and resets the discarded loaders
|
package/core/db.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Pool, PoolClient, PoolConfig } from "pg";
|
|
2
|
+
import type { RuntimeDBExtension, RuntimeDevSchemaConfig } from "./config";
|
|
2
3
|
export interface Database extends PoolConfig {
|
|
3
4
|
database?: string;
|
|
4
5
|
user?: string;
|
|
@@ -17,12 +18,16 @@ interface DatabaseInfo {
|
|
|
17
18
|
dialect: Dialect;
|
|
18
19
|
config: PoolConfig;
|
|
19
20
|
filePath?: string;
|
|
21
|
+
devSchema?: RuntimeDevSchemaConfig;
|
|
22
|
+
extensions?: RuntimeDBExtension[];
|
|
20
23
|
}
|
|
21
24
|
interface clientConfigArgs {
|
|
22
25
|
connectionString?: string;
|
|
23
26
|
dbFile?: string;
|
|
24
27
|
db?: Database | DBDict;
|
|
25
28
|
cfg?: PoolConfig;
|
|
29
|
+
devSchema?: RuntimeDevSchemaConfig;
|
|
30
|
+
extensions?: RuntimeDBExtension[];
|
|
26
31
|
}
|
|
27
32
|
export default class DB {
|
|
28
33
|
db: DatabaseInfo;
|
|
@@ -112,7 +117,10 @@ export declare class Sqlite implements Connection, SyncClient {
|
|
|
112
117
|
}
|
|
113
118
|
export declare class Postgres implements Connection {
|
|
114
119
|
private pool;
|
|
115
|
-
|
|
120
|
+
private ready?;
|
|
121
|
+
private closePromise?;
|
|
122
|
+
constructor(pool: Pool, ready?: Promise<void> | undefined);
|
|
123
|
+
private ensureReady;
|
|
116
124
|
self(): this;
|
|
117
125
|
newClient(): Promise<PostgresClient>;
|
|
118
126
|
query(query: string, values?: any[]): Promise<QueryResult<QueryResultRow>>;
|
|
@@ -122,7 +130,9 @@ export declare class Postgres implements Connection {
|
|
|
122
130
|
}
|
|
123
131
|
export declare class PostgresClient implements Client {
|
|
124
132
|
private client;
|
|
125
|
-
|
|
133
|
+
private ready?;
|
|
134
|
+
constructor(client: PoolClient, ready?: Promise<void> | undefined);
|
|
135
|
+
private ensureReady;
|
|
126
136
|
query(query: string, values?: any[]): Promise<QueryResult<QueryResultRow>>;
|
|
127
137
|
queryAll(query: string, values?: any[]): Promise<QueryResult<QueryResultRow>>;
|
|
128
138
|
exec(query: string, values?: any[]): Promise<ExecResult>;
|