@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.
Files changed (73) hide show
  1. package/action/executor.js +4 -4
  2. package/action/operations.js +3 -0
  3. package/action/orchestrator.js +10 -12
  4. package/action/topological_sort.d.ts +9 -0
  5. package/action/topological_sort.js +46 -0
  6. package/core/async_utils.d.ts +1 -0
  7. package/core/async_utils.js +29 -0
  8. package/core/base.d.ts +12 -5
  9. package/core/clause.d.ts +3 -5
  10. package/core/clause.js +32 -0
  11. package/core/config.d.ts +28 -2
  12. package/core/config.js +14 -1
  13. package/core/context.d.ts +3 -1
  14. package/core/context.js +90 -26
  15. package/core/db.d.ts +12 -2
  16. package/core/db.js +102 -7
  17. package/core/dev_schema.d.ts +9 -0
  18. package/core/dev_schema.js +306 -0
  19. package/core/ent.d.ts +5 -7
  20. package/core/ent.js +33 -48
  21. package/core/extensions.d.ts +25 -0
  22. package/core/extensions.js +220 -0
  23. package/core/loaders/assoc_count_loader.js +3 -6
  24. package/core/loaders/assoc_edge_loader.d.ts +3 -0
  25. package/core/loaders/assoc_edge_loader.js +48 -19
  26. package/core/loaders/index.d.ts +2 -1
  27. package/core/loaders/index.js +5 -1
  28. package/core/loaders/loader.d.ts +31 -0
  29. package/core/loaders/loader.js +141 -2
  30. package/core/loaders/object_loader.d.ts +2 -2
  31. package/core/loaders/object_loader.js +39 -57
  32. package/core/loaders/query_loader.d.ts +2 -5
  33. package/core/loaders/query_loader.js +45 -24
  34. package/core/loaders/raw_count_loader.d.ts +2 -2
  35. package/core/loaders/raw_count_loader.js +12 -14
  36. package/core/memoize.d.ts +1 -0
  37. package/core/memoize.js +15 -0
  38. package/core/metrics.d.ts +22 -0
  39. package/core/metrics.js +31 -0
  40. package/core/query/custom_clause_query.js +5 -1
  41. package/core/query/query.d.ts +1 -1
  42. package/core/query/query.js +10 -7
  43. package/core/query_expression.d.ts +6 -0
  44. package/core/query_expression.js +2 -0
  45. package/core/query_impl.d.ts +19 -3
  46. package/core/query_impl.js +148 -35
  47. package/index.d.ts +7 -2
  48. package/index.js +12 -2
  49. package/package.json +1 -7
  50. package/parse_schema/parse.d.ts +2 -12
  51. package/parse_schema/parse.js +22 -41
  52. package/schema/index.d.ts +1 -1
  53. package/schema/schema.d.ts +20 -1
  54. package/scripts/custom_graphql.js +12 -5
  55. package/scripts/fix_action_exports.js +1 -1
  56. package/scripts/migrate_v0.1.js +2 -5
  57. package/scripts/move_types.js +1 -1
  58. package/scripts/read_schema.js +2 -5
  59. package/testutils/builder.js +1 -2
  60. package/testutils/parse_sql.js +1 -1
  61. package/tsc/compilerOptions.d.ts +2 -2
  62. package/tsc/compilerOptions.js +12 -18
  63. package/tsc/move_generated.js +2 -2
  64. package/tsc/transform.d.ts +1 -1
  65. package/tsc/transform.js +16 -2
  66. package/tsc/transform_action.d.ts +1 -1
  67. package/tsc/transform_action.js +1 -1
  68. package/tsc/transform_ent.d.ts +1 -1
  69. package/tsc/transform_ent.js +1 -1
  70. package/tsc/transform_schema.d.ts +1 -1
  71. package/tsc/transform_schema.js +2 -2
  72. /package/core/{loaders/cache_utils.d.ts → cache_utils.d.ts} +0 -0
  73. /package/core/{loaders/cache_utils.js → cache_utils.js} +0 -0
@@ -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 graph_data_structure_1.Graph();
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(), 1);
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 = (0, graph_data_structure_1.topologicalSort)(graph);
159
+ const sorted = graph.topologicalSort();
160
160
  sorted.forEach((node) => {
161
161
  let c = changesetMap.get(node);
162
162
  if (!c) {
@@ -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;
@@ -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, memoizee_1.default)(this.getFieldsInfo.bind(this));
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 val = transformed.data[inputKey];
643
- if (val === undefined) {
644
- val = transformed.data[storageKey];
639
+ let inputVal = transformed.data[inputKey];
640
+ if (inputVal === undefined) {
641
+ inputVal = transformed.data[storageKey];
645
642
  }
646
- if (val === undefined) {
643
+ if (inputVal === undefined) {
647
644
  continue;
648
645
  }
646
+ let dbVal = inputVal;
649
647
  if (field.format) {
650
- val = field.format(transformed.data[k], true);
648
+ dbVal = field.format(inputVal, true);
651
649
  }
652
- data[this.getStorageKey(k)] = val;
650
+ data[this.getStorageKey(k)] = dbVal;
653
651
  if (!field.immutable) {
654
- this.defaultFieldsByTSName[this.getInputKey(k)] = val;
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,9 @@
1
+ type NodeID = string;
2
+ export declare class TopologicalGraph {
3
+ private nodes;
4
+ private edges;
5
+ addNode(node: NodeID): void;
6
+ addEdge(from: NodeID, to: NodeID): void;
7
+ topologicalSort(): NodeID[];
8
+ }
9
+ export {};
@@ -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: (string | {
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
- export interface Clause<T extends Data = Data, K = keyof T> {
3
- clause(idx: number, alias?: string): string;
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 || cfg.dbFile || cfg.db) {
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 = this.getkey(options);
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
- "tableName": options.tableName,
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 = this.getkey(options);
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
- "tableName": options.tableName,
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(this.getkey(options), rows);
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(this.getkey(options), rows);
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
- constructor(pool: Pool);
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
- constructor(client: PoolClient);
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>;