@snowtop/ent 0.1.0-alpha126 → 0.1.0-alpha128

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/core/base.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as clause from "./clause";
2
+ import { ObjectLoaderFactory } from "./loaders";
2
3
  export interface Loader<K, V> {
3
4
  context?: Context;
4
5
  load(key: K): Promise<V>;
@@ -95,21 +96,21 @@ export interface EditRowOptions extends CreateRowOptions {
95
96
  whereClause: clause.Clause;
96
97
  expressions?: Map<string, clause.Clause>;
97
98
  }
98
- interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> {
99
- loaderFactory: LoaderFactoryWithOptions;
99
+ interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer, TData extends Data = Data> {
100
+ loaderFactory: ObjectLoaderFactory<TData>;
100
101
  ent: EntConstructor<TEnt, TViewer>;
101
102
  }
102
- export interface LoaderFactoryWithOptions extends LoaderFactoryWithLoaderMany<any, Data | null> {
103
+ export interface LoaderFactoryWithOptions<T extends Data = Data> extends LoaderFactoryWithLoaderMany<any, T | null> {
103
104
  options?: SelectDataOptions;
104
105
  }
105
- export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends LoadableEntOptions<TEnt, TViewer>, SelectBaseDataOptions {
106
+ export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer, TData extends Data = Data> extends LoadableEntOptions<TEnt, TViewer, TData>, SelectBaseDataOptions {
106
107
  fieldPrivacy?: Map<string, PrivacyPolicy>;
107
108
  }
108
- export interface SelectCustomDataOptions extends SelectBaseDataOptions {
109
- loaderFactory: LoaderFactoryWithOptions;
109
+ export interface SelectCustomDataOptions<T extends Data = Data> extends SelectBaseDataOptions {
110
+ loaderFactory: ObjectLoaderFactory<T>;
110
111
  prime?: boolean;
111
112
  }
112
- export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends SelectCustomDataOptions {
113
+ export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer, TData extends Data = Data> extends SelectCustomDataOptions<TData> {
113
114
  ent: EntConstructor<TEnt, TViewer>;
114
115
  fieldPrivacy?: Map<string, PrivacyPolicy>;
115
116
  }
package/core/clause.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Data } from "./base";
1
+ import { Data, SelectDataOptions } from "./base";
2
2
  export interface Clause<T extends Data = Data, K = keyof T> {
3
3
  clause(idx: number): string;
4
4
  columns(): K[];
@@ -102,4 +102,5 @@ export declare function Subtract<T extends Data, K = keyof T>(col: K, value: any
102
102
  export declare function Multiply<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
103
103
  export declare function Divide<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
104
104
  export declare function Modulo<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
105
+ export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options: Omit<SelectDataOptions, "key">, cls: Clause<V, K>): Clause<V, K>;
105
106
  export {};
package/core/clause.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsSubQuery = exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.OrOptional = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.inClause = void 0;
26
+ exports.getCombinedClause = exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsSubQuery = exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.OrOptional = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.inClause = void 0;
27
27
  const db_1 = __importStar(require("./db"));
28
28
  function isSensitive(val) {
29
29
  return (val !== null &&
@@ -731,3 +731,20 @@ function Modulo(col, value) {
731
731
  return new simpleClause(col, value, "%", new isNullClause(col));
732
732
  }
733
733
  exports.Modulo = Modulo;
734
+ function getCombinedClause(options, cls) {
735
+ if (options.clause) {
736
+ let optionClause;
737
+ if (typeof options.clause === "function") {
738
+ optionClause = options.clause();
739
+ }
740
+ else {
741
+ optionClause = options.clause;
742
+ }
743
+ if (optionClause) {
744
+ // @ts-expect-error different types
745
+ cls = And(cls, optionClause);
746
+ }
747
+ }
748
+ return cls;
749
+ }
750
+ exports.getCombinedClause = getCombinedClause;
package/core/ent.d.ts CHANGED
@@ -4,7 +4,6 @@ import { Executor } from "../action/action";
4
4
  import * as clause from "./clause";
5
5
  import { Builder } from "../action";
6
6
  import DataLoader from "dataloader";
7
- import { GlobalSchema } from "../schema/";
8
7
  export declare function getEntKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): string;
9
8
  export declare function loadEnt<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
10
9
  export declare function loadEntViaKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, key: any, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
@@ -24,13 +23,13 @@ export declare function loadEntsList<TEnt extends Ent<TViewer>, TViewer extends
24
23
  * @deperecated use loadCustomEnts
25
24
  */
26
25
  export declare function loadEntsFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, clause: clause.Clause, options: LoadEntOptions<TEnt, TViewer>): Promise<Map<ID, TEnt>>;
27
- export declare function loadCustomEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer, TQueryData extends Data = Data, TResultData extends Data = TQueryData, TKey = keyof TQueryData>(viewer: TViewer, options: LoadCustomEntOptions<TEnt, TViewer>, query: CustomQuery<TQueryData, TKey>): Promise<TEnt[]>;
26
+ export declare function loadCustomEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer, TQueryData extends Data = Data, TResultData extends Data = TQueryData, TKey = keyof TQueryData>(viewer: TViewer, options: LoadCustomEntOptions<TEnt, TViewer, TResultData>, query: CustomQuery<TQueryData, TKey>): Promise<TEnt[]>;
28
27
  interface parameterizedQueryOptions {
29
28
  query: string;
30
29
  values?: any[];
31
30
  logValues?: any[];
32
31
  }
33
- export type CustomQuery<T extends Data = Data, K = keyof T> = string | parameterizedQueryOptions | clause.Clause<T, K> | QueryDataOptions;
32
+ export type CustomQuery<T extends Data = Data, K = keyof T> = string | parameterizedQueryOptions | clause.Clause<T, K> | QueryDataOptions<T, K>;
34
33
  /**
35
34
  * Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
36
35
  * either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
@@ -54,11 +53,15 @@ export type CustomQuery<T extends Data = Data, K = keyof T> = string | parameter
54
53
  * orderby: 'time',
55
54
  * disableTransformations: false
56
55
  * }) // doesn't change the query
56
+ *
57
+ * For queries that pass in a clause, we batch them with an underlying dataloader so that multiple queries with the same clause
58
+ * or parallel queries with the same clause are batched together.
59
+ *
60
+ * If a raw or parameterized query is passed in, we don't attempt to batch them together and they're executed as is.
61
+ * If you end up with a scenario where you may need to coalesce or batch (non-clause) queries here, you should use some kind of memoization here.
57
62
  */
58
- export declare function loadCustomData<TQueryData extends Data = Data, TResultData extends Data = TQueryData, K = keyof TQueryData>(options: SelectCustomDataOptions, query: CustomQuery<TQueryData, K>, context: Context | undefined): Promise<TResultData[]>;
59
- interface CustomCountOptions extends DataOptions {
60
- }
61
- export declare function loadCustomCount<T extends Data = Data, K = keyof T>(options: CustomCountOptions, query: CustomQuery<T, K>, context: Context | undefined): Promise<number>;
63
+ export declare function loadCustomData<TQueryData extends Data = Data, TResultData extends Data = TQueryData, K = keyof TQueryData>(options: SelectCustomDataOptions<TResultData>, query: CustomQuery<TQueryData, K>, context: Context | undefined): Promise<TResultData[]>;
64
+ export declare function loadCustomCount<T extends Data = Data, K = keyof T>(options: SelectCustomDataOptions<T>, query: CustomQuery<T, K>, context: Context | undefined): Promise<number>;
62
65
  export declare function loadDerivedEnt<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, data: Data, loader: new (viewer: TViewer, data: Data) => TEnt): Promise<TEnt | null>;
63
66
  export declare function loadDerivedEntX<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, data: Data, loader: new (viewer: TViewer, data: Data) => TEnt): Promise<TEnt>;
64
67
  export declare function logQuery(query: string, logValues: any[]): void;
@@ -114,9 +117,6 @@ export declare class EditNodeOperation<T extends Ent> implements DataOperation {
114
117
  returnedRow(): Data | null;
115
118
  createdEnt(viewer: Viewer): T | null;
116
119
  }
117
- export declare function setGlobalSchema(val: GlobalSchema): void;
118
- export declare function clearGlobalSchema(): void;
119
- export declare function __hasGlobalSchema(): boolean;
120
120
  export declare class EdgeOperation implements DataOperation {
121
121
  private builder;
122
122
  edgeInput: AssocEdgeInput;
package/core/ent.js CHANGED
@@ -26,15 +26,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.__hasGlobalSchema = exports.clearGlobalSchema = exports.setGlobalSchema = exports.EditNodeOperation = exports.RawQueryOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.___setLogQueryErrorWithError = exports.loadRow = exports.loadRowX = exports.logQuery = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomCount = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = exports.getEntKey = void 0;
30
- exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = void 0;
29
+ exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.EditNodeOperation = exports.RawQueryOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.___setLogQueryErrorWithError = exports.loadRow = exports.loadRowX = exports.logQuery = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomCount = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = exports.getEntKey = void 0;
30
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = void 0;
31
31
  const db_1 = __importStar(require("./db"));
32
32
  const privacy_1 = require("./privacy");
33
33
  const clause = __importStar(require("./clause"));
34
34
  const action_1 = require("../action");
35
35
  const logger_1 = require("./logger");
36
36
  const dataloader_1 = __importDefault(require("dataloader"));
37
- const schema_1 = require("../schema/");
37
+ const schema_1 = require("../schema/schema");
38
+ const global_schema_1 = require("./global_schema");
38
39
  // TODO kill this and createDataLoader
39
40
  class cacheMap {
40
41
  constructor(options) {
@@ -396,6 +397,12 @@ function isParameterizedQuery(opts) {
396
397
  * orderby: 'time',
397
398
  * disableTransformations: false
398
399
  * }) // doesn't change the query
400
+ *
401
+ * For queries that pass in a clause, we batch them with an underlying dataloader so that multiple queries with the same clause
402
+ * or parallel queries with the same clause are batched together.
403
+ *
404
+ * If a raw or parameterized query is passed in, we don't attempt to batch them together and they're executed as is.
405
+ * If you end up with a scenario where you may need to coalesce or batch (non-clause) queries here, you should use some kind of memoization here.
399
406
  */
400
407
  async function loadCustomData(options, query, context) {
401
408
  const rows = await loadCustomDataImpl(options, query, context);
@@ -414,7 +421,10 @@ exports.loadCustomData = loadCustomData;
414
421
  // NOTE: if you use a raw query or paramterized query with this,
415
422
  // you should use `SELECT count(*) as count...`
416
423
  async function loadCustomCount(options, query, context) {
417
- // TODO also need to loaderify this in case we're querying for this a lot...
424
+ // if clause, we'll use the loader and strong typing/coalescing it provides
425
+ if (typeof query !== "string" && isClause(query)) {
426
+ return options.loaderFactory.createCountLoader(context).load(query);
427
+ }
418
428
  const rows = await loadCustomDataImpl({
419
429
  ...options,
420
430
  fields: ["count(1) as count"],
@@ -429,50 +439,31 @@ function isPrimableLoader(loader) {
429
439
  return loader != undefined;
430
440
  }
431
441
  async function loadCustomDataImpl(options, query, context) {
432
- function getClause(cls) {
433
- let optClause = options.loaderFactory?.options?.clause;
434
- if (typeof optClause === "function") {
435
- optClause = optClause();
436
- }
437
- if (!optClause) {
438
- return cls;
439
- }
440
- // @ts-expect-error string|ID mismatch
441
- return clause.And(cls, optClause);
442
- }
443
442
  if (typeof query === "string") {
444
443
  // no caching, perform raw query
445
444
  return performRawQuery(query, [], []);
446
- // @ts-ignore
447
445
  }
448
446
  else if (isClause(query)) {
449
- // if a Clause is passed in and we have a default clause
450
- // associated with the query, pass that in
451
- // if we want to disableTransformations, need to indicate that with
452
- // disableTransformations option
453
- // this will have rudimentary caching but nothing crazy
454
- return loadRows({
455
- ...options,
456
- // @ts-ignore
457
- clause: getClause(query),
458
- context: context,
459
- });
447
+ const r = await options.loaderFactory
448
+ .createTypedLoader(context)
449
+ .load(query);
450
+ return r;
460
451
  }
461
452
  else if (isParameterizedQuery(query)) {
462
453
  // no caching, perform raw query
463
454
  return performRawQuery(query.query, query.values || [], query.logValues);
464
455
  }
465
456
  else {
457
+ // this will have rudimentary caching but nothing crazy
466
458
  let cls = query.clause;
467
459
  if (!query.disableTransformations) {
468
- // @ts-ignore
469
- cls = getClause(cls);
460
+ cls = clause.getCombinedClause(options.loaderFactory.options, query.clause);
470
461
  }
471
- // this will have rudimentary caching but nothing crazy
472
462
  return loadRows({
473
463
  ...query,
474
464
  ...options,
475
465
  context: context,
466
+ // @ts-expect-error
476
467
  clause: cls,
477
468
  });
478
469
  }
@@ -609,7 +600,7 @@ async function performRawQuery(query, values, logValues) {
609
600
  catch (e) {
610
601
  if (_logQueryWithError) {
611
602
  const msg = e.message;
612
- throw new Error(`error \`${msg}\` running query: \`${query}\``);
603
+ throw new Error(`error \`${msg}\` running query: \`${query}\` with values: \`${logValues}\``);
613
604
  }
614
605
  throw e;
615
606
  }
@@ -816,20 +807,6 @@ class EditNodeOperation {
816
807
  }
817
808
  }
818
809
  exports.EditNodeOperation = EditNodeOperation;
819
- let globalSchema;
820
- function setGlobalSchema(val) {
821
- globalSchema = val;
822
- }
823
- exports.setGlobalSchema = setGlobalSchema;
824
- function clearGlobalSchema() {
825
- globalSchema = undefined;
826
- }
827
- exports.clearGlobalSchema = clearGlobalSchema;
828
- // used by tests. no guarantee will always exist
829
- function __hasGlobalSchema() {
830
- return globalSchema !== undefined;
831
- }
832
- exports.__hasGlobalSchema = __hasGlobalSchema;
833
810
  class EdgeOperation {
834
811
  constructor(builder, edgeInput, options) {
835
812
  this.builder = builder;
@@ -872,8 +849,9 @@ class EdgeOperation {
872
849
  let op = schema_1.SQLStatementOperation.Delete;
873
850
  let updateData = null;
874
851
  // TODO respect disableTransformations
875
- if (globalSchema?.transformEdgeWrite) {
876
- transformed = globalSchema.transformEdgeWrite({
852
+ const transformedEdgeWrite = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeWrite;
853
+ if (transformedEdgeWrite) {
854
+ transformed = transformedEdgeWrite({
877
855
  op: schema_1.SQLStatementOperation.Delete,
878
856
  edge,
879
857
  });
@@ -951,9 +929,10 @@ class EdgeOperation {
951
929
  fields["time"] = new Date().toISOString();
952
930
  }
953
931
  const onConflictFields = ["data"];
954
- if (globalSchema?.extraEdgeFields) {
955
- for (const name in globalSchema.extraEdgeFields) {
956
- const f = globalSchema.extraEdgeFields[name];
932
+ const extraEdgeFields = (0, global_schema_1.__getGlobalSchema)()?.extraEdgeFields;
933
+ if (extraEdgeFields) {
934
+ for (const name in extraEdgeFields) {
935
+ const f = extraEdgeFields[name];
957
936
  if (f.defaultValueOnCreate) {
958
937
  const storageKey = (0, schema_1.getStorageKey)(f, name);
959
938
  fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
@@ -965,8 +944,9 @@ class EdgeOperation {
965
944
  }
966
945
  // TODO respect disableTransformations
967
946
  let transformed = null;
968
- if (globalSchema?.transformEdgeWrite) {
969
- transformed = globalSchema.transformEdgeWrite({
947
+ const transformEdgeWrite = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeWrite;
948
+ if (transformEdgeWrite) {
949
+ transformed = transformEdgeWrite({
970
950
  op: schema_1.SQLStatementOperation.Insert,
971
951
  edge,
972
952
  });
@@ -1483,8 +1463,9 @@ async function loadEdges(options) {
1483
1463
  exports.loadEdges = loadEdges;
1484
1464
  function getEdgeClauseAndFields(cls, options) {
1485
1465
  let fields = edgeFields;
1486
- if (globalSchema?.transformEdgeRead) {
1487
- const transformClause = globalSchema.transformEdgeRead();
1466
+ const transformEdgeRead = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeRead;
1467
+ if (transformEdgeRead) {
1468
+ const transformClause = transformEdgeRead();
1488
1469
  if (!options.disableTransformations) {
1489
1470
  cls = clause.And(cls, transformClause);
1490
1471
  }
@@ -0,0 +1,7 @@
1
+ import { Field, GlobalSchema } from "../schema/schema";
2
+ export declare function setGlobalSchema(val: GlobalSchema): void;
3
+ export declare function clearGlobalSchema(): void;
4
+ export declare function __hasGlobalSchema(): boolean;
5
+ export declare function __getGlobalSchema(): GlobalSchema | undefined;
6
+ export declare function __getGlobalSchemaFields(): Map<string, Field>;
7
+ export declare function __getGlobalSchemaField(type: string): Field | undefined;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.__getGlobalSchemaField = exports.__getGlobalSchemaFields = exports.__getGlobalSchema = exports.__hasGlobalSchema = exports.clearGlobalSchema = exports.setGlobalSchema = void 0;
4
+ const schema_1 = require("../schema/schema");
5
+ let globalSchema;
6
+ let globalSchemaFields = new Map();
7
+ function isGlobalSchemaField(f) {
8
+ switch (f.type.dbType) {
9
+ case schema_1.DBType.Enum:
10
+ case schema_1.DBType.StringEnum:
11
+ case schema_1.DBType.IntEnum:
12
+ case schema_1.DBType.JSON:
13
+ case schema_1.DBType.JSONB:
14
+ return true;
15
+ }
16
+ return false;
17
+ }
18
+ function setGlobalSchema(val) {
19
+ globalSchema = val;
20
+ if (val.fields) {
21
+ for (const [k, v] of Object.entries(val.fields)) {
22
+ if (isGlobalSchemaField(v) && v.type.type) {
23
+ globalSchemaFields.set(v.type.type, v);
24
+ }
25
+ }
26
+ }
27
+ }
28
+ exports.setGlobalSchema = setGlobalSchema;
29
+ function clearGlobalSchema() {
30
+ globalSchema = undefined;
31
+ globalSchemaFields.clear();
32
+ }
33
+ exports.clearGlobalSchema = clearGlobalSchema;
34
+ // used by tests. no guarantee will always exist
35
+ function __hasGlobalSchema() {
36
+ return globalSchema !== undefined;
37
+ }
38
+ exports.__hasGlobalSchema = __hasGlobalSchema;
39
+ // used by tests. no guarantee will always exist
40
+ function __getGlobalSchema() {
41
+ return globalSchema;
42
+ }
43
+ exports.__getGlobalSchema = __getGlobalSchema;
44
+ function __getGlobalSchemaFields() {
45
+ return globalSchemaFields;
46
+ }
47
+ exports.__getGlobalSchemaFields = __getGlobalSchemaFields;
48
+ function __getGlobalSchemaField(type) {
49
+ return globalSchemaFields.get(type);
50
+ }
51
+ exports.__getGlobalSchemaField = __getGlobalSchemaField;
@@ -21,7 +21,7 @@ export declare class AssocDirectEdgeLoader<T extends AssocEdge> implements Loade
21
21
  private edgeCtr;
22
22
  private options?;
23
23
  context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
24
- constructor(edgeType: string, edgeCtr: AssocEdgeConstructor<T>, options?: Partial<Pick<import("../base").QueryableDataOptions, "limit" | "orderby" | "clause">> | undefined, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
24
+ constructor(edgeType: string, edgeCtr: AssocEdgeConstructor<T>, options?: Partial<Pick<import("../base").QueryableDataOptions, "clause" | "limit" | "orderby">> | undefined, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
25
25
  load(id: ID): Promise<T[]>;
26
26
  loadEdgeForID2(id: ID, id2: ID): Promise<T | undefined>;
27
27
  clearAll(): void;
@@ -1,29 +1,49 @@
1
1
  import { ID, Data, SelectDataOptions, Context, Loader, LoaderFactory } from "../base";
2
- export declare class ObjectLoader<V = Data> implements Loader<ID, V | null> {
2
+ import * as clause from "../clause";
3
+ export declare class ObjectLoader<TQueryData extends Data = Data, TResultData extends Data = TQueryData, K = keyof TQueryData> implements Loader<ID, TResultData | null>, Loader<clause.Clause<TQueryData, K>, TResultData[] | null> {
3
4
  private options;
4
5
  context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
5
6
  private toPrime?;
6
- private loader;
7
+ private idLoader;
8
+ private clauseLoader;
7
9
  private primedLoaders;
8
10
  private memoizedInitPrime;
9
- constructor(options: SelectDataOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined, toPrime?: ObjectLoaderFactory<V>[] | undefined);
11
+ constructor(options: SelectDataOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined, toPrime?: ObjectLoaderFactory<TResultData>[] | undefined);
10
12
  getOptions(): SelectDataOptions;
11
13
  private initPrime;
12
- load(key: ID): Promise<V | null>;
14
+ load(key: ID): Promise<TResultData | null>;
15
+ load(key: clause.Clause<TQueryData, K>): Promise<TResultData[] | null>;
16
+ private loadID;
17
+ private loadClause;
18
+ clearAll(): void;
19
+ loadMany(keys: ID[]): Promise<Array<TResultData | null>>;
20
+ loadMany(keys: clause.Clause<TQueryData, K>[]): Promise<Array<TResultData[] | null>>;
21
+ private loadIDMany;
22
+ private loadClauseMany;
23
+ prime(data: TResultData): void;
24
+ primeAll(data: TResultData): void;
25
+ }
26
+ export declare class ObjectCountLoader<V extends Data = Data, K = keyof V> implements Loader<clause.Clause<V, K>, number> {
27
+ private options;
28
+ context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
29
+ private loader;
30
+ constructor(options: SelectDataOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
31
+ getOptions(): SelectDataOptions;
32
+ load(key: clause.Clause<V, K>): Promise<number>;
13
33
  clearAll(): void;
14
- loadMany(keys: ID[]): Promise<Array<V | null>>;
15
- prime(data: V): void;
16
- primeAll(data: V): void;
34
+ loadMany(keys: clause.Clause<V, K>[]): Promise<Array<number>>;
17
35
  }
18
36
  interface ObjectLoaderOptions extends SelectDataOptions {
19
37
  instanceKey?: string;
20
38
  }
21
- export declare class ObjectLoaderFactory<V = Data> implements LoaderFactory<ID, V | null> {
39
+ export declare class ObjectLoaderFactory<V extends Data = Data> implements LoaderFactory<ID, V | null>, LoaderFactory<clause.Clause<V>, V[] | null> {
22
40
  options: ObjectLoaderOptions;
23
41
  name: string;
24
42
  private toPrime;
25
43
  constructor(options: ObjectLoaderOptions);
26
44
  createLoader(context?: Context): ObjectLoader<V>;
45
+ createTypedLoader<TQueryData extends Data = Data, TResultData extends Data = Data, K = keyof TQueryData>(context?: Context): ObjectLoader<TQueryData, TResultData, K>;
46
+ createCountLoader<K = keyof V>(context?: Context): ObjectCountLoader<V, K>;
27
47
  addToPrime(factory: ObjectLoaderFactory<V>): this;
28
48
  }
29
49
  export {};
@@ -26,29 +26,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.ObjectLoaderFactory = exports.ObjectLoader = void 0;
29
+ exports.ObjectLoaderFactory = exports.ObjectCountLoader = exports.ObjectLoader = void 0;
30
30
  const dataloader_1 = __importDefault(require("dataloader"));
31
31
  const ent_1 = require("../ent");
32
32
  const clause = __importStar(require("../clause"));
33
33
  const logger_1 = require("../logger");
34
+ const clause_1 = require("../clause");
34
35
  const loader_1 = require("./loader");
35
36
  const memoizee_1 = __importDefault(require("memoizee"));
36
- async function loadRowsForLoader(options, ids, context) {
37
+ async function loadRowsForIDLoader(options, ids, context) {
37
38
  let col = options.key;
38
- let cls = clause.In(col, ...ids);
39
- if (options.clause) {
40
- let optionClause;
41
- if (typeof options.clause === "function") {
42
- optionClause = options.clause();
43
- }
44
- else {
45
- optionClause = options.clause;
46
- }
47
- if (optionClause) {
48
- // @ts-expect-error id/string mismatch
49
- cls = clause.And(cls, optionClause);
50
- }
51
- }
39
+ const cls = (0, clause_1.getCombinedClause)(options, clause.In(col, ...ids));
52
40
  const rowOptions = {
53
41
  ...options,
54
42
  clause: cls,
@@ -75,6 +63,29 @@ async function loadRowsForLoader(options, ids, context) {
75
63
  }
76
64
  return result;
77
65
  }
66
+ async function loadRowsForClauseLoader(options, clause) {
67
+ const rowOptions = {
68
+ ...options,
69
+ // @ts-expect-error clause in LoadRowOptions doesn't take templatized version of Clause
70
+ clause: (0, clause_1.getCombinedClause)(options, clause),
71
+ };
72
+ return (await (0, ent_1.loadRows)(rowOptions));
73
+ }
74
+ async function loadCountForClauseLoader(options, clause) {
75
+ const rowOptions = {
76
+ ...options,
77
+ // @ts-expect-error clause in LoadRowOptions doesn't take templatized version of Clause
78
+ clause: (0, clause_1.getCombinedClause)(options, clause),
79
+ };
80
+ const row = await (0, ent_1.loadRow)({
81
+ ...rowOptions,
82
+ fields: ["count(*) as count"],
83
+ });
84
+ if (!row) {
85
+ return 0;
86
+ }
87
+ return parseInt(row.count, 10);
88
+ }
78
89
  // optional clause...
79
90
  // so ObjectLoaderFactory and createDataLoader need to take a new optional field which is a clause that's always added here
80
91
  // and we need a disableTransform which skips loader completely and uses loadRow...
@@ -89,9 +100,66 @@ function createDataLoader(options) {
89
100
  return [];
90
101
  }
91
102
  // context not needed because we're creating a loader which has its own cache which is being used here
92
- return loadRowsForLoader(options, ids);
103
+ return loadRowsForIDLoader(options, ids);
93
104
  }, loaderOptions);
94
105
  }
106
+ class clauseCacheMap {
107
+ constructor(options, count) {
108
+ this.options = options;
109
+ this.count = count;
110
+ this.m = new Map();
111
+ }
112
+ get(key) {
113
+ const key2 = key.instanceKey();
114
+ const ret = this.m.get(key2);
115
+ if (ret) {
116
+ (0, logger_1.log)("cache", {
117
+ "dataloader-cache-hit": key2 + (this.count ? ":count" : ""),
118
+ "tableName": this.options.tableName,
119
+ });
120
+ }
121
+ return ret;
122
+ }
123
+ set(key, value) {
124
+ return this.m.set(key.instanceKey(), value);
125
+ }
126
+ delete(key) {
127
+ return this.m.delete(key.instanceKey());
128
+ }
129
+ clear() {
130
+ return this.m.clear();
131
+ }
132
+ }
133
+ function createClauseDataLoder(options) {
134
+ return new dataloader_1.default(async (clauses) => {
135
+ if (!clauses.length) {
136
+ return [];
137
+ }
138
+ const ret = [];
139
+ for await (const clause of clauses) {
140
+ const data = await loadRowsForClauseLoader(options, clause);
141
+ ret.push(data);
142
+ }
143
+ return ret;
144
+ }, {
145
+ cacheMap: new clauseCacheMap(options),
146
+ });
147
+ }
148
+ function createClauseCountDataLoader(options) {
149
+ return new dataloader_1.default(async (clauses) => {
150
+ if (!clauses.length) {
151
+ return [];
152
+ }
153
+ const ret = [];
154
+ for await (const clause of clauses) {
155
+ const data = await loadCountForClauseLoader(options, clause);
156
+ ret.push(data);
157
+ }
158
+ return ret;
159
+ }, {
160
+ cacheMap: new clauseCacheMap(options, true),
161
+ });
162
+ }
95
163
  class ObjectLoader {
96
164
  constructor(options, context, toPrime) {
97
165
  this.options = options;
@@ -101,7 +169,8 @@ class ObjectLoader {
101
169
  console.trace();
102
170
  }
103
171
  if (context) {
104
- this.loader = createDataLoader(options);
172
+ this.idLoader = createDataLoader(options);
173
+ this.clauseLoader = createClauseDataLoder(options);
105
174
  }
106
175
  this.memoizedInitPrime = (0, memoizee_1.default)(this.initPrime.bind(this));
107
176
  }
@@ -123,11 +192,17 @@ class ObjectLoader {
123
192
  this.primedLoaders = primedLoaders;
124
193
  }
125
194
  async load(key) {
195
+ if (typeof key === "string" || typeof key === "number") {
196
+ return this.loadID(key);
197
+ }
198
+ return this.loadClause(key);
199
+ }
200
+ async loadID(key) {
126
201
  // simple case. we get parallelization etc
127
- if (this.loader) {
202
+ if (this.idLoader) {
128
203
  this.memoizedInitPrime();
129
204
  // prime the result if we got primable loaders
130
- const result = await this.loader.load(key);
205
+ const result = await this.idLoader.load(key);
131
206
  if (result && this.primedLoaders) {
132
207
  for (const [key, loader] of this.primedLoaders) {
133
208
  const value = result[key];
@@ -138,19 +213,7 @@ class ObjectLoader {
138
213
  }
139
214
  return result;
140
215
  }
141
- let cls = clause.Eq(this.options.key, key);
142
- if (this.options.clause) {
143
- let optionClause;
144
- if (typeof this.options.clause === "function") {
145
- optionClause = this.options.clause();
146
- }
147
- else {
148
- optionClause = this.options.clause;
149
- }
150
- if (optionClause) {
151
- cls = clause.And(cls, optionClause);
152
- }
153
- }
216
+ const cls = (0, clause_1.getCombinedClause)(this.options, clause.Eq(this.options.key, key));
154
217
  const rowOptions = {
155
218
  ...this.options,
156
219
  clause: cls,
@@ -158,22 +221,50 @@ class ObjectLoader {
158
221
  };
159
222
  return (0, ent_1.loadRow)(rowOptions);
160
223
  }
224
+ async loadClause(key) {
225
+ if (this.clauseLoader) {
226
+ return this.clauseLoader.load(key);
227
+ }
228
+ return loadRowsForClauseLoader(this.options, key);
229
+ }
161
230
  clearAll() {
162
- this.loader && this.loader.clearAll();
231
+ this.idLoader && this.idLoader.clearAll();
232
+ this.clauseLoader && this.clauseLoader.clearAll();
163
233
  }
164
234
  async loadMany(keys) {
165
- if (this.loader) {
235
+ if (!keys.length) {
236
+ return [];
237
+ }
238
+ if (typeof keys[0] === "string" || typeof keys[0] === "number") {
239
+ return this.loadIDMany(keys);
240
+ }
241
+ return this.loadClauseMany(keys);
242
+ }
243
+ loadIDMany(keys) {
244
+ if (this.idLoader) {
166
245
  // @ts-expect-error TODO?
167
- return this.loader.loadMany(keys);
246
+ return this.idLoader.loadMany(keys);
247
+ }
248
+ return loadRowsForIDLoader(this.options, keys, this.context);
249
+ }
250
+ async loadClauseMany(keys) {
251
+ if (this.clauseLoader) {
252
+ // @ts-expect-error TODO?
253
+ return this.clauseLoader.loadMany(keys);
254
+ }
255
+ const res = [];
256
+ for await (const key of keys) {
257
+ const rows = await loadRowsForClauseLoader(this.options, key);
258
+ res.push(rows);
168
259
  }
169
- return loadRowsForLoader(this.options, keys, this.context);
260
+ return res;
170
261
  }
171
262
  prime(data) {
172
263
  // we have this data from somewhere else, prime it in the c
173
- if (this.loader) {
264
+ if (this.idLoader) {
174
265
  const col = this.options.key;
175
266
  const key = data[col];
176
- this.loader.prime(key, data);
267
+ this.idLoader.prime(key, data);
177
268
  }
178
269
  }
179
270
  // prime this loader and any other loaders it's aware of
@@ -190,6 +281,43 @@ class ObjectLoader {
190
281
  }
191
282
  }
192
283
  exports.ObjectLoader = ObjectLoader;
284
+ class ObjectCountLoader {
285
+ constructor(options, context) {
286
+ this.options = options;
287
+ this.context = context;
288
+ if (context) {
289
+ this.loader = createClauseCountDataLoader(options);
290
+ }
291
+ }
292
+ getOptions() {
293
+ return this.options;
294
+ }
295
+ async load(key) {
296
+ if (this.loader) {
297
+ return this.loader.load(key);
298
+ }
299
+ return loadCountForClauseLoader(this.options, key);
300
+ }
301
+ clearAll() {
302
+ this.loader && this.loader.clearAll();
303
+ }
304
+ async loadMany(keys) {
305
+ if (!keys.length) {
306
+ return [];
307
+ }
308
+ if (this.loader) {
309
+ // @ts-expect-error
310
+ return this.loader.loadMany(keys);
311
+ }
312
+ const res = [];
313
+ for await (const key of keys) {
314
+ const r = await loadCountForClauseLoader(this.options, key);
315
+ res.push(r);
316
+ }
317
+ return res;
318
+ }
319
+ }
320
+ exports.ObjectCountLoader = ObjectCountLoader;
193
321
  // NOTE: if not querying for all columns
194
322
  // have to query for the id field as one of the fields
195
323
  // because it's used to maintain sort order of the queried ids
@@ -213,6 +341,15 @@ class ObjectLoaderFactory {
213
341
  return new ObjectLoader(this.options, context, this.toPrime);
214
342
  }, context);
215
343
  }
344
+ createTypedLoader(context) {
345
+ const loader = this.createLoader(context);
346
+ return loader;
347
+ }
348
+ createCountLoader(context) {
349
+ return (0, loader_1.getCustomLoader)(`${this.name}:count_loader`, () => {
350
+ return new ObjectCountLoader(this.options, context);
351
+ }, context);
352
+ }
216
353
  // keep track of loaders to prime. needs to be done not in the constructor
217
354
  // because there's usually self references here
218
355
  addToPrime(factory) {
@@ -8,7 +8,7 @@ declare class QueryDirectLoader<K extends any> implements Loader<K, Data[]> {
8
8
  context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
9
9
  private memoizedInitPrime;
10
10
  private primedLoaders;
11
- constructor(options: QueryOptions, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "limit" | "orderby" | "clause">> | undefined, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
11
+ constructor(options: QueryOptions, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "clause" | "limit" | "orderby">> | undefined, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
12
12
  private initPrime;
13
13
  load(id: K): Promise<Data[]>;
14
14
  clearAll(): void;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.commonTests = void 0;
4
4
  const ent_1 = require("../ent");
5
+ const global_schema_1 = require("../global_schema");
5
6
  const viewer_1 = require("../viewer");
6
7
  const index_1 = require("../../testutils/fake_data/index");
7
8
  const test_helpers_1 = require("../../testutils/fake_data/test_helpers");
@@ -289,7 +290,7 @@ const commonTests = (opts) => {
289
290
  return { verify, getCursor };
290
291
  }
291
292
  if (opts.globalSchema) {
292
- (0, ent_1.setGlobalSchema)(test_edge_global_schema_1.testEdgeGlobalSchema);
293
+ (0, global_schema_1.setGlobalSchema)(test_edge_global_schema_1.testEdgeGlobalSchema);
293
294
  }
294
295
  let tdb;
295
296
  if (opts.sqlite) {
@@ -40,7 +40,7 @@ export interface gqlObjectOptions {
40
40
  type gqlMutationOptions = Omit<gqlFieldOptions, "nullable" | "type"> & {
41
41
  type?: gqlFieldOptionsBase["type"];
42
42
  };
43
- type gqlQueryOptions = Omit<gqlFieldOptions, "nullable">;
43
+ type gqlQueryOptions = gqlFieldOptions;
44
44
  export declare enum CustomFieldType {
45
45
  Accessor = "ACCESSOR",
46
46
  Field = "FIELD",
@@ -24,6 +24,7 @@ exports.knownAllowedNames = new Map([
24
24
  ["Float", "number"],
25
25
  ["ID", "ID"],
26
26
  ["JSON", "any"],
27
+ ["Node", "Ent"],
27
28
  ]);
28
29
  exports.knownDisAllowedNames = new Map([
29
30
  ["Function", true],
package/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./core/base";
2
- export { loadEnt, loadCustomData, loadCustomEnts, loadCustomCount, loadEntX, loadEnts, CustomQuery, loadDerivedEnt, loadDerivedEntX, loadEntViaKey, loadEntXViaKey, performRawQuery, loadRowX, loadRow, loadRows, DataOperation, EditNodeOptions, EditNodeOperation, RawQueryOperation, EdgeOperation, DeleteNodeOperation, AssocEdge, AssocEdgeInputOptions, AssocEdgeInput, AssocEdgeData, loadEdgeData, loadEdgeDatas, loadEdges, loadUniqueEdge, loadUniqueNode, loadRawEdgeCountX, loadEdgeForID2, loadNodesByEdge, getEdgeTypeInGroup, setGlobalSchema, } from "./core/ent";
2
+ export { loadEnt, loadCustomData, loadCustomEnts, loadCustomCount, loadEntX, loadEnts, CustomQuery, loadDerivedEnt, loadDerivedEntX, loadEntViaKey, loadEntXViaKey, performRawQuery, loadRowX, loadRow, loadRows, DataOperation, EditNodeOptions, EditNodeOperation, RawQueryOperation, EdgeOperation, DeleteNodeOperation, AssocEdge, AssocEdgeInputOptions, AssocEdgeInput, AssocEdgeData, loadEdgeData, loadEdgeDatas, loadEdges, loadUniqueEdge, loadUniqueNode, loadRawEdgeCountX, loadEdgeForID2, loadNodesByEdge, getEdgeTypeInGroup, } from "./core/ent";
3
+ export { setGlobalSchema } from "./core/global_schema";
3
4
  import DB from "./core/db";
4
5
  export * from "./core/loaders";
5
6
  export { DB };
package/index.js CHANGED
@@ -63,7 +63,8 @@ Object.defineProperty(exports, "loadRawEdgeCountX", { enumerable: true, get: fun
63
63
  Object.defineProperty(exports, "loadEdgeForID2", { enumerable: true, get: function () { return ent_1.loadEdgeForID2; } });
64
64
  Object.defineProperty(exports, "loadNodesByEdge", { enumerable: true, get: function () { return ent_1.loadNodesByEdge; } });
65
65
  Object.defineProperty(exports, "getEdgeTypeInGroup", { enumerable: true, get: function () { return ent_1.getEdgeTypeInGroup; } });
66
- Object.defineProperty(exports, "setGlobalSchema", { enumerable: true, get: function () { return ent_1.setGlobalSchema; } });
66
+ var global_schema_1 = require("./core/global_schema");
67
+ Object.defineProperty(exports, "setGlobalSchema", { enumerable: true, get: function () { return global_schema_1.setGlobalSchema; } });
67
68
  const db_1 = __importDefault(require("./core/db"));
68
69
  exports.DB = db_1.default;
69
70
  __exportStar(require("./core/loaders"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snowtop/ent",
3
- "version": "0.1.0-alpha126",
3
+ "version": "0.1.0-alpha128",
4
4
  "description": "snowtop ent framework",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -83,6 +83,7 @@ interface RomeConfig {
83
83
  interface ProcessedGlobalSchema {
84
84
  globalEdges: ProcessedAssocEdge[];
85
85
  extraEdgeFields: ProcessedField[];
86
- initForEdges?: boolean;
86
+ init?: boolean;
87
+ globalFields?: ProcessedField[];
87
88
  }
88
89
  export {};
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseSchema = void 0;
4
4
  const cosmiconfig_1 = require("cosmiconfig");
5
5
  const const_1 = require("../core/const");
6
+ const global_schema_1 = require("../core/global_schema");
6
7
  async function processFields(src, patternName) {
7
8
  const ret = [];
8
9
  let m = {};
@@ -21,7 +22,7 @@ async function processFields(src, patternName) {
21
22
  for (const name in m) {
22
23
  const field = m[name];
23
24
  //@ts-ignore type and other changed fields with different type in ProcessedField vs Field
24
- let f = { name, ...field };
25
+ let f = { ...field, name };
25
26
  f.hasDefaultValueOnCreate = field.defaultValueOnCreate != undefined;
26
27
  f.hasDefaultValueOnEdit = field.defaultValueOnEdit != undefined;
27
28
  f.hasFieldPrivacy = field.privacyPolicy !== undefined;
@@ -236,6 +237,9 @@ async function parseSchema(potentialSchemas, globalSchema) {
236
237
  let parsedGlobalSchema;
237
238
  if (globalSchema) {
238
239
  parsedGlobalSchema = await parseGlobalSchema(globalSchema);
240
+ // set this so that we can use it, if we're trying to process server default or anything
241
+ // that ends up parsing,validating and formatting fields
242
+ (0, global_schema_1.setGlobalSchema)(globalSchema);
239
243
  }
240
244
  for (const key in potentialSchemas) {
241
245
  const value = potentialSchemas[key];
@@ -354,9 +358,10 @@ async function parseGlobalSchema(s) {
354
358
  const ret = {
355
359
  globalEdges: [],
356
360
  extraEdgeFields: [],
357
- initForEdges: !!s.extraEdgeFields ||
361
+ init: !!s.extraEdgeFields ||
358
362
  s.transformEdgeRead !== undefined ||
359
- s.transformEdgeWrite !== undefined,
363
+ s.transformEdgeWrite !== undefined ||
364
+ s.fields !== undefined,
360
365
  };
361
366
  if (s.extraEdgeFields) {
362
367
  ret.extraEdgeFields = await processFields(s.extraEdgeFields);
@@ -364,5 +369,8 @@ async function parseGlobalSchema(s) {
364
369
  if (s.edges) {
365
370
  ret.globalEdges = processEdges(s.edges);
366
371
  }
372
+ if (s.fields) {
373
+ ret.globalFields = await processFields(s.fields);
374
+ }
367
375
  return ret;
368
376
  }
package/schema/field.d.ts CHANGED
@@ -149,6 +149,7 @@ export interface EnumOptions extends FieldOptions {
149
149
  graphQLType?: string;
150
150
  createEnumType?: boolean;
151
151
  disableUnknownType?: boolean;
152
+ globalType?: string;
152
153
  }
153
154
  /**
154
155
  * @deprecated Use StringEnumField
@@ -158,7 +159,7 @@ export declare class EnumField extends BaseField implements Field {
158
159
  private values?;
159
160
  private map?;
160
161
  constructor(options: StringEnumOptions);
161
- valid(val: any): boolean;
162
+ valid(val: any): Promise<boolean>;
162
163
  format(val: any): any;
163
164
  }
164
165
  export declare class StringEnumField extends EnumField {
@@ -173,17 +174,18 @@ declare type IntEnumMap = {
173
174
  [key: string]: number;
174
175
  };
175
176
  export interface IntegerEnumOptions extends FieldOptions {
176
- map: IntEnumMap;
177
+ map?: IntEnumMap;
177
178
  deprecated?: IntEnumMap;
178
179
  tsType?: string;
179
180
  graphQLType?: string;
180
181
  disableUnknownType?: boolean;
182
+ globalType?: string;
181
183
  }
182
184
  export declare class IntegerEnumField extends BaseField implements Field {
183
185
  type: Type;
184
186
  private map;
185
187
  constructor(options: IntegerEnumOptions);
186
- valid(val: any): boolean;
188
+ valid(val: any): Promise<boolean>;
187
189
  format(val: any): any;
188
190
  }
189
191
  export declare function IntegerEnumType(options: IntegerEnumOptions): IntegerEnumField;
package/schema/field.js CHANGED
@@ -31,6 +31,8 @@ const uuid_1 = require("uuid");
31
31
  const base_1 = require("../core/base");
32
32
  const db_1 = __importStar(require("../core/db"));
33
33
  const schema_1 = require("./schema");
34
+ const global_schema_1 = require("../core/global_schema");
35
+ const logger_1 = require("../core/logger");
34
36
  class BaseField {
35
37
  logValue(val) {
36
38
  if (this.sensitive) {
@@ -525,10 +527,11 @@ class EnumField extends BaseField {
525
527
  type: options.tsType,
526
528
  graphQLType: options.graphQLType,
527
529
  disableUnknownType: options.disableUnknownType,
530
+ globalType: options.globalType,
528
531
  };
529
532
  if (!options.foreignKey) {
530
- if (!options.values && !options.map) {
531
- throw new Error("values or map required if not look up table enum. Look-up table enum indicated by foreignKey field");
533
+ if (!options.values && !options.map && !options.globalType) {
534
+ throw new Error("values, map or globalType required if not look up table enum. Look-up table enum indicated by foreignKey field");
532
535
  }
533
536
  if (options.values) {
534
537
  if (!options.values.length) {
@@ -547,8 +550,8 @@ class EnumField extends BaseField {
547
550
  }
548
551
  }
549
552
  else {
550
- if (options.values || options.map) {
551
- throw new Error("cannot specify values or map and foreign key for lookup table enum type");
553
+ if (options.values || options.map || options.globalType) {
554
+ throw new Error("cannot specify values, map or globalType and foreign key for lookup table enum type");
552
555
  }
553
556
  if (options.createEnumType) {
554
557
  throw new Error("cannot specify createEnumType without specifying values");
@@ -563,7 +566,20 @@ class EnumField extends BaseField {
563
566
  this.values = options.values;
564
567
  this.map = options.map;
565
568
  }
566
- valid(val) {
569
+ async valid(val) {
570
+ if (this.type.globalType) {
571
+ const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
572
+ if (f) {
573
+ if (f.valid) {
574
+ return f.valid(val);
575
+ }
576
+ return true;
577
+ }
578
+ else {
579
+ (0, logger_1.log)("error", `globalType ${this.type.globalType} not found in global schema`);
580
+ return false;
581
+ }
582
+ }
567
583
  // lookup table enum and indicated via presence of foreignKey
568
584
  if (!this.values && !this.map) {
569
585
  return true;
@@ -581,6 +597,13 @@ class EnumField extends BaseField {
581
597
  return false;
582
598
  }
583
599
  format(val) {
600
+ if (this.type.globalType) {
601
+ const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
602
+ if (f && f.format) {
603
+ return f.format(val);
604
+ }
605
+ return val;
606
+ }
584
607
  return val;
585
608
  }
586
609
  }
@@ -603,21 +626,46 @@ class IntegerEnumField extends BaseField {
603
626
  graphQLType: options.graphQLType,
604
627
  deprecatedIntEnumMap: options.deprecated,
605
628
  disableUnknownType: options.disableUnknownType,
629
+ globalType: options.globalType,
606
630
  };
607
- let count = 0;
608
- for (const _ in options.map) {
609
- count++;
610
- break;
611
- }
612
- if (!count) {
613
- throw new Error("need at least one entry in enum map");
614
- }
615
631
  if (options.foreignKey) {
616
632
  throw new Error(`foreignKey on intEnum not supported`);
617
633
  }
618
- this.map = options.map;
634
+ if (options.globalType) {
635
+ if (options.map) {
636
+ throw new Error(`cannot specify map and globalType`);
637
+ }
638
+ this.map = {};
639
+ }
640
+ else {
641
+ let count = 0;
642
+ for (const _ in options.map) {
643
+ count++;
644
+ break;
645
+ }
646
+ if (!count) {
647
+ throw new Error("need at least one entry in enum map");
648
+ }
649
+ if (!options.map) {
650
+ throw new Error("map required if not globalType");
651
+ }
652
+ this.map = options.map;
653
+ }
619
654
  }
620
- valid(val) {
655
+ async valid(val) {
656
+ if (this.type?.globalType) {
657
+ const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
658
+ if (f) {
659
+ if (f.valid) {
660
+ return f.valid(val);
661
+ }
662
+ return true;
663
+ }
664
+ else {
665
+ (0, logger_1.log)("error", `globalType ${this.type.globalType} not found in global schema`);
666
+ return false;
667
+ }
668
+ }
621
669
  // lookup table enum and indicated via presence of foreignKey
622
670
  for (const k in this.map) {
623
671
  const v = this.map[k];
@@ -628,6 +676,12 @@ class IntegerEnumField extends BaseField {
628
676
  return false;
629
677
  }
630
678
  format(val) {
679
+ if (this.type.globalType) {
680
+ const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
681
+ if (f && f.format) {
682
+ return f.format(val);
683
+ }
684
+ }
631
685
  return parseInt(val);
632
686
  }
633
687
  }
@@ -17,6 +17,7 @@ export interface GlobalSchema {
17
17
  extraEdgeFields?: FieldMap;
18
18
  transformEdgeRead?: () => Clause;
19
19
  transformEdgeWrite?: (stmt: EdgeUpdateOperation) => TransformedEdgeUpdateOperation | null;
20
+ fields?: FieldMap;
20
21
  }
21
22
  type FieldOverride = Pick<FieldOptions, "nullable" | "storageKey" | "serverDefault" | "unique" | "hideFromGraphQL" | "graphqlName" | "index">;
22
23
  export type FieldOverrideMap = {
@@ -171,6 +172,7 @@ export interface Type {
171
172
  intEnumMap?: IntEnumMap;
172
173
  deprecatedIntEnumMap?: IntEnumMap;
173
174
  disableUnknownType?: boolean;
175
+ globalType?: string;
174
176
  importType?: DeprecatedImportType;
175
177
  subFields?: FieldMap;
176
178
  unionFields?: FieldMap;
@@ -1,20 +1,22 @@
1
1
  import { BaseField, ListField } from "./field";
2
2
  import { FieldOptions, Field, Type, FieldMap } from "./schema";
3
- export interface StructOptions extends FieldOptions {
3
+ interface structFieldOptions extends FieldOptions {
4
4
  tsType: string;
5
5
  fields: FieldMap;
6
6
  graphQLType?: string;
7
7
  jsonNotJSONB?: boolean;
8
8
  }
9
- interface allStructOptions extends StructOptions {
10
- jsonAsList?: boolean;
9
+ interface GlobalStructOptions extends FieldOptions {
10
+ globalType: string;
11
11
  }
12
+ export type StructOptions = structFieldOptions | GlobalStructOptions;
12
13
  export declare class StructField extends BaseField implements Field {
13
14
  private options;
15
+ private jsonAsList?;
14
16
  type: Type;
15
- constructor(options: allStructOptions);
17
+ constructor(options: StructOptions, jsonAsList?: boolean | undefined);
16
18
  formatImpl(obj: any, nested?: boolean): string | Object;
17
- format(obj: any, nested?: boolean): string | Object;
19
+ format(obj: any, nested?: boolean): any;
18
20
  private validImpl;
19
21
  valid(obj: any): Promise<boolean>;
20
22
  }
@@ -23,5 +25,5 @@ export declare function StructType(options: StructOptions): StructField & Struct
23
25
  * @deprecated use StructTypeAsList
24
26
  */
25
27
  export declare function StructListType(options: StructOptions): ListField;
26
- export declare function StructTypeAsList(options: allStructOptions): StructField & allStructOptions;
28
+ export declare function StructTypeAsList(options: StructOptions): StructField & StructOptions;
27
29
  export {};
@@ -4,20 +4,24 @@ exports.StructTypeAsList = exports.StructListType = exports.StructType = exports
4
4
  const camel_case_1 = require("camel-case");
5
5
  const field_1 = require("./field");
6
6
  const schema_1 = require("./schema");
7
+ const global_schema_1 = require("../core/global_schema");
8
+ const logger_1 = require("../core/logger");
7
9
  class StructField extends field_1.BaseField {
8
- constructor(options) {
10
+ constructor(options, jsonAsList) {
9
11
  super();
10
12
  this.options = options;
13
+ this.jsonAsList = jsonAsList;
11
14
  this.type = {
12
15
  dbType: schema_1.DBType.JSONB,
13
16
  };
14
17
  this.type.subFields = options.fields;
15
18
  this.type.type = options.tsType;
16
19
  this.type.graphQLType = options.graphQLType || options.tsType;
20
+ this.type.globalType = this.options.globalType;
17
21
  if (options.jsonNotJSONB) {
18
22
  this.type.dbType = schema_1.DBType.JSON;
19
23
  }
20
- if (options?.jsonAsList) {
24
+ if (jsonAsList) {
21
25
  this.type.listElemType = {
22
26
  dbType: schema_1.DBType.JSONB,
23
27
  };
@@ -57,7 +61,37 @@ class StructField extends field_1.BaseField {
57
61
  return JSON.stringify(ret);
58
62
  }
59
63
  format(obj, nested) {
60
- if (Array.isArray(obj) && this.options.jsonAsList) {
64
+ if (this.type.globalType) {
65
+ const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
66
+ if (f && f.format) {
67
+ if (JSON.stringify(this.type.listElemType) !==
68
+ JSON.stringify(f?.type.listElemType)) {
69
+ if (this.jsonAsList) {
70
+ // handle as nested
71
+ // @ts-ignore
72
+ const formatted = obj.map((v) => f.format(v, true));
73
+ if (nested) {
74
+ return formatted;
75
+ }
76
+ else {
77
+ return JSON.stringify(formatted);
78
+ }
79
+ }
80
+ else {
81
+ const formatted = f.format([obj], true);
82
+ if (nested) {
83
+ return formatted[0];
84
+ }
85
+ else {
86
+ return JSON.stringify(formatted[0]);
87
+ }
88
+ }
89
+ }
90
+ // TODO handle format code
91
+ return f.format(obj);
92
+ }
93
+ }
94
+ if (Array.isArray(obj) && this.jsonAsList) {
61
95
  const ret = obj.map((v) => this.formatImpl(v, true));
62
96
  if (nested) {
63
97
  return ret;
@@ -102,7 +136,35 @@ class StructField extends field_1.BaseField {
102
136
  return ret.every((v) => v);
103
137
  }
104
138
  async valid(obj) {
105
- if (this.options.jsonAsList) {
139
+ if (this.type.globalType) {
140
+ const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
141
+ // list and global type is not valid.
142
+ if (f) {
143
+ if (f.valid) {
144
+ if (JSON.stringify(this.type.listElemType) !==
145
+ JSON.stringify(f?.type.listElemType)) {
146
+ if (this.jsonAsList) {
147
+ if (!Array.isArray(obj)) {
148
+ return false;
149
+ }
150
+ // @ts-ignore
151
+ const valid = await Promise.all(obj.map((v) => f.valid(v)));
152
+ return valid.every((b) => b);
153
+ }
154
+ else {
155
+ return f.valid([obj]);
156
+ }
157
+ }
158
+ return f.valid(obj);
159
+ }
160
+ return true;
161
+ }
162
+ else {
163
+ (0, logger_1.log)("error", `globalType ${this.type.globalType} not found in global schema`);
164
+ return false;
165
+ }
166
+ }
167
+ if (this.jsonAsList) {
106
168
  if (!Array.isArray(obj)) {
107
169
  return false;
108
170
  }
@@ -129,10 +191,7 @@ function StructListType(options) {
129
191
  }
130
192
  exports.StructListType = StructListType;
131
193
  function StructTypeAsList(options) {
132
- let result = new StructField({
133
- ...options,
134
- jsonAsList: true,
135
- });
194
+ let result = new StructField(options, true);
136
195
  return Object.assign(result, options);
137
196
  }
138
197
  exports.StructTypeAsList = StructTypeAsList;
@@ -15,7 +15,7 @@ export declare class UnionField extends BaseField implements FieldOptions {
15
15
  type: Type;
16
16
  m: Map<Object, string>;
17
17
  constructor(options: UnionOptions);
18
- format(obj: any): string | Object;
18
+ format(obj: any): any;
19
19
  private validField;
20
20
  valid(obj: any): Promise<boolean>;
21
21
  }