@snowtop/ent 0.1.0-alpha74 → 0.1.0-alpha75

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
@@ -5,16 +5,23 @@ export interface Loader<T, V> {
5
5
  loadMany?(keys: T[]): Promise<(V | null)[]>;
6
6
  clearAll(): any;
7
7
  }
8
+ interface LoaderWithLoadMany<T, V> extends Loader<T, V> {
9
+ loadMany(keys: T[]): Promise<(V | null)[]>;
10
+ }
8
11
  export interface LoaderFactory<T, V> {
9
12
  name: string;
10
13
  createLoader(context?: Context): Loader<T, V>;
11
14
  }
15
+ interface LoaderFactoryWithLoaderMany<T, V> extends LoaderFactory<T, V> {
16
+ createLoader(context?: Context): LoaderWithLoadMany<T, V>;
17
+ }
12
18
  export interface ConfigurableLoaderFactory<T, V> extends LoaderFactory<T, V> {
13
19
  createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context): Loader<T, V>;
14
20
  }
15
21
  export declare type EdgeQueryableDataOptions = Partial<Pick<QueryableDataOptions, "limit" | "orderby" | "clause">>;
16
22
  export interface PrimableLoader<T, V> extends Loader<T, V> {
17
23
  prime(d: Data): void;
24
+ primeAll?(d: Data): void;
18
25
  }
19
26
  interface cache {
20
27
  getLoader<T, V>(name: string, create: () => Loader<T, V>): Loader<T, V>;
@@ -23,6 +30,7 @@ interface cache {
23
30
  primeCache(options: queryOptions, rows: Data[]): void;
24
31
  primeCache(options: queryOptions, rows: Data): void;
25
32
  clearCache(): void;
33
+ getEntCache(): Map<string, Ent | Error | null>;
26
34
  }
27
35
  interface queryOptions {
28
36
  fields: string[];
@@ -89,15 +97,15 @@ interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer>
89
97
  loaderFactory: LoaderFactoryWithOptions;
90
98
  ent: EntConstructor<TEnt, TViewer>;
91
99
  }
92
- interface LoaderFactoryWithOptions extends LoaderFactory<any, Data | null> {
100
+ interface LoaderFactoryWithOptions extends LoaderFactoryWithLoaderMany<any, Data | null> {
93
101
  options?: SelectDataOptions;
94
102
  }
95
103
  export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends LoadableEntOptions<TEnt, TViewer>, SelectBaseDataOptions {
96
104
  fieldPrivacy?: Map<string, PrivacyPolicy>;
97
105
  }
98
106
  export interface SelectCustomDataOptions extends SelectBaseDataOptions {
99
- clause?: clause.Clause;
100
- loaderFactory?: LoaderFactoryWithOptions;
107
+ loaderFactory: LoaderFactoryWithOptions;
108
+ prime?: boolean;
101
109
  }
102
110
  export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends SelectCustomDataOptions {
103
111
  ent: EntConstructor<TEnt, TViewer>;
package/core/clause.d.ts CHANGED
@@ -22,6 +22,18 @@ declare class simpleClause implements Clause {
22
22
  logValues(): any[];
23
23
  instanceKey(): string;
24
24
  }
25
+ export declare class inClause implements Clause {
26
+ private col;
27
+ private value;
28
+ private type;
29
+ static getPostgresInClauseValuesThreshold(): number;
30
+ constructor(col: string, value: any[], type?: string);
31
+ clause(idx: number): string;
32
+ columns(): string[];
33
+ values(): any[];
34
+ logValues(): any[];
35
+ instanceKey(): string;
36
+ }
25
37
  declare class compositeClause implements Clause {
26
38
  private clauses;
27
39
  private sep;
@@ -86,6 +98,7 @@ export declare function And(...args: Clause[]): compositeClause;
86
98
  export declare function AndOptional(...args: (Clause | undefined)[]): Clause;
87
99
  export declare function Or(...args: Clause[]): compositeClause;
88
100
  export declare function In(col: string, ...values: any): Clause;
101
+ export declare function In(col: string, values: any[], type?: string): Clause;
89
102
  interface TsQuery {
90
103
  language: "english" | "french" | "german" | "simple";
91
104
  value: string;
package/core/clause.js CHANGED
@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
19
19
  return result;
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- 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.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 = void 0;
22
+ 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.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;
23
23
  const db_1 = __importStar(require("./db"));
24
24
  function isSensitive(val) {
25
25
  return (val !== null &&
@@ -213,17 +213,37 @@ class postgresArrayOperatorList extends postgresArrayOperator {
213
213
  }
214
214
  }
215
215
  class inClause {
216
- constructor(col, value) {
216
+ constructor(col, value, type = "uuid") {
217
217
  this.col = col;
218
218
  this.value = value;
219
+ this.type = type;
220
+ }
221
+ static getPostgresInClauseValuesThreshold() {
222
+ return 70;
219
223
  }
220
224
  clause(idx) {
221
- const dialect = db_1.default.getDialect();
225
+ // do a simple = when only one item
226
+ if (this.value.length === 1) {
227
+ return new simpleClause(this.col, this.value[0], "=").clause(idx);
228
+ }
229
+ const postgres = db_1.default.getDialect() === db_1.Dialect.Postgres;
230
+ const postgresValuesList = postgres &&
231
+ this.value.length >= inClause.getPostgresInClauseValuesThreshold();
222
232
  let indices;
223
- if (dialect === db_1.Dialect.Postgres) {
233
+ if (postgres) {
224
234
  indices = [];
225
235
  for (let i = 0; i < this.value.length; i++) {
226
- indices.push(`$${idx}`);
236
+ if (postgresValuesList) {
237
+ if (i === 0) {
238
+ indices.push(`($${idx}::${this.type})`);
239
+ }
240
+ else {
241
+ indices.push(`($${idx})`);
242
+ }
243
+ }
244
+ else {
245
+ indices.push(`$${idx}`);
246
+ }
227
247
  idx++;
228
248
  }
229
249
  }
@@ -231,7 +251,11 @@ class inClause {
231
251
  indices = new Array(this.value.length);
232
252
  indices.fill("?", 0);
233
253
  }
234
- const inValue = indices.join(", ");
254
+ let inValue = indices.join(", ");
255
+ // wrap in VALUES list for postgres...
256
+ if (postgresValuesList) {
257
+ inValue = `VALUES${inValue}`;
258
+ }
235
259
  return `${this.col} IN (${inValue})`;
236
260
  // TODO we need to return idx at end to query builder...
237
261
  // or anything that's doing a composite query so next clause knows where to start
@@ -243,25 +267,15 @@ class inClause {
243
267
  }
244
268
  values() {
245
269
  const result = [];
246
- for (const value of this.value) {
247
- if (isSensitive(value)) {
248
- result.push(value.value());
249
- }
250
- else {
251
- result.push(value);
252
- }
270
+ for (let value of this.value) {
271
+ result.push(rawValue(value));
253
272
  }
254
273
  return result;
255
274
  }
256
275
  logValues() {
257
276
  const result = [];
258
- for (const value of this.value) {
259
- if (isSensitive(value)) {
260
- result.push(value.logValue());
261
- }
262
- else {
263
- result.push(value);
264
- }
277
+ for (let value of this.value) {
278
+ result.push(isSensitive(value) ? value.logValue() : value);
265
279
  }
266
280
  return result;
267
281
  }
@@ -269,6 +283,7 @@ class inClause {
269
283
  return `in:${this.col}:${this.values().join(",")}`;
270
284
  }
271
285
  }
286
+ exports.inClause = inClause;
272
287
  class compositeClause {
273
288
  constructor(clauses, sep) {
274
289
  this.clauses = clauses;
@@ -486,9 +501,15 @@ function Or(...args) {
486
501
  return new compositeClause(args, " OR ");
487
502
  }
488
503
  exports.Or = Or;
489
- // TODO this breaks if values.length ===1 and array. todo fix
490
- function In(col, ...values) {
491
- return new inClause(col, values);
504
+ function In(...args) {
505
+ if (args.length < 2) {
506
+ throw new Error(`invalid args passed to In`);
507
+ }
508
+ // 2nd overload
509
+ if (Array.isArray(args[1])) {
510
+ return new inClause(args[0], args[1], args[2]);
511
+ }
512
+ return new inClause(args[0], args.slice(1));
492
513
  }
493
514
  exports.In = In;
494
515
  // if string defaults to english
package/core/context.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { Viewer, Data, Loader } from "./base";
2
+ import { Viewer, Data, Loader, Ent } from "./base";
3
3
  import { IncomingMessage, ServerResponse } from "http";
4
4
  import * as clause from "./clause";
5
5
  import { Context } from "./base";
@@ -14,12 +14,14 @@ export declare class ContextCache {
14
14
  getLoader<T, V>(name: string, create: () => Loader<T, V>): Loader<T, V>;
15
15
  private itemMap;
16
16
  private listMap;
17
+ private entCache;
17
18
  private getkey;
18
19
  getCachedRows(options: queryOptions): Data[] | null;
19
20
  getCachedRow(options: queryOptions): Data | null;
20
21
  primeCache(options: queryOptions, rows: Data[]): void;
21
22
  primeCache(options: queryOptions, rows: Data): void;
22
23
  clearCache(): void;
24
+ getEntCache(): Map<string, Error | Ent<any> | null>;
23
25
  }
24
26
  interface queryOptions {
25
27
  fields: string[];
package/core/context.js CHANGED
@@ -8,6 +8,7 @@ class ContextCache {
8
8
  // we have a per-table map to make it easier to purge and have less things to compare with
9
9
  this.itemMap = new Map();
10
10
  this.listMap = new Map();
11
+ this.entCache = new Map();
11
12
  }
12
13
  getLoader(name, create) {
13
14
  let l = this.loaders.get(name);
@@ -82,6 +83,10 @@ class ContextCache {
82
83
  this.loaders.clear();
83
84
  this.itemMap.clear();
84
85
  this.listMap.clear();
86
+ this.entCache.clear();
87
+ }
88
+ getEntCache() {
89
+ return this.entCache;
85
90
  }
86
91
  }
87
92
  exports.ContextCache = ContextCache;
package/core/ent.d.ts CHANGED
@@ -5,14 +5,24 @@ import * as clause from "./clause";
5
5
  import { Builder } from "../action";
6
6
  import DataLoader from "dataloader";
7
7
  import { GlobalSchema } from "../schema/";
8
+ export declare function getEntKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): string;
8
9
  export declare function loadEnt<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
9
10
  export declare function loadEntViaKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, key: any, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
10
11
  export declare function loadEntX<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt>;
11
12
  export declare function loadEntXViaKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, key: any, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt>;
13
+ /**
14
+ * @deprecated use loadCustomEnts
15
+ */
12
16
  export declare function loadEntFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, clause: clause.Clause): Promise<TEnt | null>;
17
+ /**
18
+ * @deprecated use loadCustomEnts
19
+ */
13
20
  export declare function loadEntXFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, clause: clause.Clause): Promise<TEnt>;
14
21
  export declare function loadEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, ...ids: ID[]): Promise<Map<ID, TEnt>>;
15
22
  export declare function loadEntsList<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, ...ids: ID[]): Promise<TEnt[]>;
23
+ /**
24
+ * @deperecated use loadCustomEnts
25
+ */
16
26
  export declare function loadEntsFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, clause: clause.Clause, options: LoadEntOptions<TEnt, TViewer>): Promise<Map<ID, TEnt>>;
17
27
  export declare function loadCustomEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadCustomEntOptions<TEnt, TViewer>, query: CustomQuery): Promise<TEnt[]>;
18
28
  interface parameterizedQueryOptions {
@@ -210,7 +220,6 @@ interface loadEdgeForIDOptions<T extends AssocEdge> extends loadCustomEdgesOptio
210
220
  export declare function loadEdgeForID2<T extends AssocEdge>(options: loadEdgeForIDOptions<T>): Promise<T | undefined>;
211
221
  export declare function loadNodesByEdge<T extends Ent>(viewer: Viewer, id1: ID, edgeType: string, options: LoadEntOptions<T>): Promise<T[]>;
212
222
  export declare function applyPrivacyPolicyForRow<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, row: Data | null): Promise<TEnt | null>;
213
- export declare function applyPrivacyPolicyForRowX<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, row: Data): Promise<TEnt>;
214
223
  export declare function applyPrivacyPolicyForRows<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, rows: Data[], options: LoadEntOptions<TEnt, TViewer>): Promise<Map<ID, TEnt>>;
215
224
  export declare function getEdgeTypeInGroup<T extends string>(viewer: Viewer, id1: ID, id2: ID, m: Map<T, string>): Promise<[T, AssocEdge] | undefined>;
216
225
  export {};
package/core/ent.js CHANGED
@@ -22,8 +22,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.defaultEdgeQueryOptions = 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.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
26
- exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = void 0;
25
+ exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.defaultEdgeQueryOptions = 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.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = 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;
26
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = void 0;
27
27
  const db_1 = __importStar(require("./db"));
28
28
  const privacy_1 = require("./privacy");
29
29
  const clause = __importStar(require("./clause"));
@@ -87,10 +87,58 @@ function createDataLoader(options) {
87
87
  return result;
88
88
  }, loaderOptions);
89
89
  }
90
+ function getEntKey(viewer, id, options) {
91
+ return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
92
+ }
93
+ exports.getEntKey = getEntKey;
94
+ // fetches the ent from cache if cache exists.
95
+ function entFromCacheMaybe(viewer, id, options) {
96
+ const cache = viewer.context?.cache?.getEntCache();
97
+ if (!cache) {
98
+ return {};
99
+ }
100
+ const key = getEntKey(viewer, id, options);
101
+ const r = cache.get(key);
102
+ if (r !== undefined) {
103
+ (0, logger_1.log)("cache", {
104
+ "ent-cache-hit": key,
105
+ });
106
+ }
107
+ return {
108
+ key,
109
+ ent: r instanceof Error ? undefined : r,
110
+ error: r instanceof Error ? r : undefined,
111
+ cache,
112
+ };
113
+ }
90
114
  // Ent accessors
115
+ async function applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info) {
116
+ const ent = await applyPrivacyPolicyForRow(viewer, options, row);
117
+ if (info.cache && info.key) {
118
+ info.cache.set(info.key, ent);
119
+ }
120
+ return ent instanceof Error ? null : ent;
121
+ }
122
+ async function applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info) {
123
+ const ent = await applyPrivacyPolicyForRowImpl(viewer, options, row);
124
+ if (info.cache && info.key) {
125
+ info.cache.set(info.key, ent);
126
+ }
127
+ if (ent instanceof Error) {
128
+ throw ent;
129
+ }
130
+ if (ent === null) {
131
+ throw new Error(`TODO`);
132
+ }
133
+ return ent;
134
+ }
91
135
  async function loadEnt(viewer, id, options) {
136
+ const info = entFromCacheMaybe(viewer, id, options);
137
+ if (info.ent !== undefined) {
138
+ return info.ent;
139
+ }
92
140
  const row = await options.loaderFactory.createLoader(viewer.context).load(id);
93
- return await applyPrivacyPolicyForRow(viewer, options, row);
141
+ return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
94
142
  }
95
143
  exports.loadEnt = loadEnt;
96
144
  // this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
@@ -99,18 +147,36 @@ async function loadEntViaKey(viewer, key, options) {
99
147
  const row = await options.loaderFactory
100
148
  .createLoader(viewer.context)
101
149
  .load(key);
102
- return await applyPrivacyPolicyForRow(viewer, options, row);
150
+ if (!row) {
151
+ return null;
152
+ }
153
+ // TODO every row.id needs to be audited...
154
+ const info = entFromCacheMaybe(viewer, row.id, options);
155
+ if (info.ent !== undefined) {
156
+ return info.ent;
157
+ }
158
+ return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
103
159
  }
104
160
  exports.loadEntViaKey = loadEntViaKey;
161
+ // need a cached error...
105
162
  async function loadEntX(viewer, id, options) {
163
+ const info = entFromCacheMaybe(viewer, id, options);
164
+ if (info.error !== undefined) {
165
+ throw info.error;
166
+ }
167
+ if (info.ent !== undefined && info.ent !== null) {
168
+ return info.ent;
169
+ }
106
170
  const row = await options.loaderFactory.createLoader(viewer.context).load(id);
107
171
  if (!row) {
108
172
  // todo make this better
109
173
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${id}`);
110
174
  }
111
- return await applyPrivacyPolicyForRowX(viewer, options, row);
175
+ return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
112
176
  }
113
177
  exports.loadEntX = loadEntX;
178
+ // TODO test this and loadEntViaKey
179
+ // replace loadEntViaClause??
114
180
  async function loadEntXViaKey(viewer, key, options) {
115
181
  const row = await options.loaderFactory
116
182
  .createLoader(viewer.context)
@@ -119,9 +185,19 @@ async function loadEntXViaKey(viewer, key, options) {
119
185
  // todo make this better
120
186
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
121
187
  }
122
- return await applyPrivacyPolicyForRowX(viewer, options, row);
188
+ const info = entFromCacheMaybe(viewer, row.id, options);
189
+ if (info.error !== undefined) {
190
+ throw info.error;
191
+ }
192
+ if (info.ent !== undefined && info.ent !== null) {
193
+ return info.ent;
194
+ }
195
+ return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
123
196
  }
124
197
  exports.loadEntXViaKey = loadEntXViaKey;
198
+ /**
199
+ * @deprecated use loadCustomEnts
200
+ */
125
201
  async function loadEntFromClause(viewer, options, clause) {
126
202
  const rowOptions = {
127
203
  ...options,
@@ -129,12 +205,15 @@ async function loadEntFromClause(viewer, options, clause) {
129
205
  context: viewer.context,
130
206
  };
131
207
  const row = await loadRow(rowOptions);
132
- return await applyPrivacyPolicyForRow(viewer, options, row);
208
+ return applyPrivacyPolicyForRow(viewer, options, row);
133
209
  }
134
210
  exports.loadEntFromClause = loadEntFromClause;
135
211
  // same as loadEntFromClause
136
212
  // only works for ents where primary key is "id"
137
213
  // use loadEnt with a loaderFactory if different
214
+ /**
215
+ * @deprecated use loadCustomEnts
216
+ */
138
217
  async function loadEntXFromClause(viewer, options, clause) {
139
218
  const rowOptions = {
140
219
  ...options,
@@ -149,33 +228,62 @@ async function loadEnts(viewer, options, ...ids) {
149
228
  if (!ids.length) {
150
229
  return new Map();
151
230
  }
152
- let loaded = false;
153
- let rows = [];
154
- // TODO loadMany everywhere
155
- const l = options.loaderFactory.createLoader(viewer.context);
156
- if (l.loadMany) {
157
- loaded = true;
158
- rows = await l.loadMany(ids);
159
- }
160
- // TODO rewrite all of this
231
+ // result
161
232
  let m = new Map();
162
- if (loaded) {
163
- let rows2 = [];
164
- for (const row of rows) {
165
- if (!row) {
166
- continue;
233
+ const cache = viewer.context?.cache?.getEntCache();
234
+ let toFetch = [];
235
+ if (cache) {
236
+ for (const id of ids) {
237
+ const key = getEntKey(viewer, id, options);
238
+ const ent = cache.get(key);
239
+ if (ent !== undefined) {
240
+ (0, logger_1.log)("cache", {
241
+ "ent-cache-hit": key,
242
+ });
243
+ if (ent === null) {
244
+ // TODO this should return null if not loadable...
245
+ continue;
246
+ }
247
+ // @ts-ignore
248
+ m.set(id, ent);
167
249
  }
168
- if (row instanceof Error) {
169
- throw row;
250
+ else {
251
+ toFetch.push(id);
170
252
  }
171
- rows2.push(row);
172
253
  }
173
- m = await applyPrivacyPolicyForRows(viewer, rows2, options);
174
254
  }
175
255
  else {
176
- m = await loadEntsFromClause(viewer,
177
- // this is always "id" if not using an ObjectLoaderFactory
178
- clause.In("id", ...ids), options);
256
+ toFetch = ids;
257
+ }
258
+ // all in ent cache!
259
+ if (!toFetch.length) {
260
+ return m;
261
+ }
262
+ const l = options.loaderFactory.createLoader(viewer.context);
263
+ const rows = await l.loadMany(toFetch);
264
+ let rows2 = [];
265
+ for (const row of rows) {
266
+ if (!row) {
267
+ continue;
268
+ }
269
+ if (row instanceof Error) {
270
+ throw row;
271
+ }
272
+ rows2.push(row);
273
+ }
274
+ const m2 = await applyPrivacyPolicyForRows(viewer, rows2, options);
275
+ for (const row of rows2) {
276
+ const id = row[options.loaderFactory.options?.key || "id"];
277
+ const ent = m2.get(id);
278
+ if (cache) {
279
+ // put back in cache...
280
+ // store null for rows that can't be seen
281
+ cache.set(getEntKey(viewer, id, options), ent ?? null);
282
+ }
283
+ if (ent !== undefined) {
284
+ // TODO this should return null if not loadable...?
285
+ m.set(id, ent);
286
+ }
179
287
  }
180
288
  return m;
181
289
  // TODO do we want to change this to be a map not a list so that it's easy to check for existence?
@@ -198,6 +306,9 @@ async function loadEntsList(viewer, options, ...ids) {
198
306
  exports.loadEntsList = loadEntsList;
199
307
  // we return a map here so that any sorting for queries that exist
200
308
  // can be done in O(N) time
309
+ /**
310
+ * @deperecated use loadCustomEnts
311
+ */
201
312
  async function loadEntsFromClause(viewer, clause, options) {
202
313
  const rowOptions = {
203
314
  ...options,
@@ -212,8 +323,18 @@ async function loadCustomEnts(viewer, options, query) {
212
323
  const rows = await loadCustomData(options, query, viewer.context);
213
324
  const result = new Array(rows.length);
214
325
  await Promise.all(rows.map(async (row, idx) => {
215
- const ent = new options.ent(viewer, row);
216
- let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent, row, options);
326
+ // TODO what if key is different
327
+ const info = entFromCacheMaybe(viewer, row.id, options);
328
+ if (info.ent !== undefined) {
329
+ if (info.ent === null) {
330
+ // we're done here
331
+ return;
332
+ }
333
+ // @ts-ignore
334
+ result[idx] = info.ent;
335
+ return;
336
+ }
337
+ const privacyEnt = await applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
217
338
  if (privacyEnt) {
218
339
  result[idx] = privacyEnt;
219
340
  }
@@ -254,11 +375,25 @@ function isParameterizedQuery(opts) {
254
375
  * }) // doesn't change the query
255
376
  */
256
377
  async function loadCustomData(options, query, context) {
257
- function getClause(cls) {
258
- if (options.clause && options.loaderFactory?.options?.clause) {
259
- throw new Error(`cannot pass both options.clause && optsions.loaderFactory.options.clause`);
378
+ const rows = await loadCustomDataImpl(options, query, context);
379
+ // prime the data so that subsequent fetches of the row with this id are a cache hit.
380
+ if (options.prime) {
381
+ const loader = options.loaderFactory.createLoader(context);
382
+ if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
383
+ for (const row of rows) {
384
+ loader.primeAll(row);
385
+ }
260
386
  }
261
- let optClause = options.clause || options.loaderFactory?.options?.clause;
387
+ }
388
+ return rows;
389
+ }
390
+ exports.loadCustomData = loadCustomData;
391
+ function isPrimableLoader(loader) {
392
+ return loader != undefined;
393
+ }
394
+ async function loadCustomDataImpl(options, query, context) {
395
+ function getClause(cls) {
396
+ let optClause = options.loaderFactory?.options?.clause;
262
397
  if (typeof optClause === "function") {
263
398
  optClause = optClause();
264
399
  }
@@ -269,7 +404,7 @@ async function loadCustomData(options, query, context) {
269
404
  }
270
405
  if (typeof query === "string") {
271
406
  // no caching, perform raw query
272
- return await performRawQuery(query, [], []);
407
+ return performRawQuery(query, [], []);
273
408
  }
274
409
  else if (isClause(query)) {
275
410
  // if a Clause is passed in and we have a default clause
@@ -277,7 +412,7 @@ async function loadCustomData(options, query, context) {
277
412
  // if we want to disableTransformations, need to indicate that with
278
413
  // disableTransformations option
279
414
  // this will have rudimentary caching but nothing crazy
280
- return await loadRows({
415
+ return loadRows({
281
416
  ...options,
282
417
  clause: getClause(query),
283
418
  context: context,
@@ -285,7 +420,7 @@ async function loadCustomData(options, query, context) {
285
420
  }
286
421
  else if (isParameterizedQuery(query)) {
287
422
  // no caching, perform raw query
288
- return await performRawQuery(query.query, query.values || [], query.logValues);
423
+ return performRawQuery(query.query, query.values || [], query.logValues);
289
424
  }
290
425
  else {
291
426
  let cls = query.clause;
@@ -293,7 +428,7 @@ async function loadCustomData(options, query, context) {
293
428
  cls = getClause(cls);
294
429
  }
295
430
  // this will have rudimentary caching but nothing crazy
296
- return await loadRows({
431
+ return loadRows({
297
432
  ...query,
298
433
  ...options,
299
434
  context: context,
@@ -301,15 +436,20 @@ async function loadCustomData(options, query, context) {
301
436
  });
302
437
  }
303
438
  }
304
- exports.loadCustomData = loadCustomData;
305
439
  // Derived ents
440
+ // no ent caching
306
441
  async function loadDerivedEnt(viewer, data, loader) {
307
442
  const ent = new loader(viewer, data);
308
- return await applyPrivacyPolicyForEnt(viewer, ent, data, {
443
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
309
444
  ent: loader,
310
445
  });
446
+ if (r instanceof Error) {
447
+ return null;
448
+ }
449
+ return r;
311
450
  }
312
451
  exports.loadDerivedEnt = loadDerivedEnt;
452
+ // won't have caching yet either
313
453
  async function loadDerivedEntX(viewer, data, loader) {
314
454
  const ent = new loader(viewer, data);
315
455
  return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
@@ -319,18 +459,23 @@ exports.loadDerivedEntX = loadDerivedEntX;
319
459
  // TODO is there a smarter way to not instantiate two objects here?
320
460
  async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
321
461
  if (ent) {
322
- const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.getPrivacyPolicy(), ent);
323
- if (!visible) {
324
- return null;
462
+ const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
463
+ if (error === null) {
464
+ return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
325
465
  }
326
- return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
466
+ return error;
327
467
  }
328
468
  return null;
329
469
  }
330
470
  async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
331
- // this will throw
332
- await (0, privacy_1.applyPrivacyPolicyX)(viewer, ent.getPrivacyPolicy(), ent);
333
- return doFieldPrivacy(viewer, ent, data, options);
471
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
472
+ if (r instanceof Error) {
473
+ throw r;
474
+ }
475
+ if (r === null) {
476
+ throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
477
+ }
478
+ return r;
334
479
  }
335
480
  async function doFieldPrivacy(viewer, ent, data, options) {
336
481
  if (!options.fieldPrivacy) {
@@ -339,12 +484,12 @@ async function doFieldPrivacy(viewer, ent, data, options) {
339
484
  const promises = [];
340
485
  let somethingChanged = false;
341
486
  for (const [k, policy] of options.fieldPrivacy) {
487
+ const curr = data[k];
488
+ if (curr === null || curr === undefined) {
489
+ continue;
490
+ }
342
491
  promises.push((async () => {
343
492
  // don't do anything if key is null or for some reason missing
344
- const curr = data[k];
345
- if (curr === null || curr === undefined) {
346
- return;
347
- }
348
493
  const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
349
494
  if (!r) {
350
495
  data[k] = null;
@@ -547,7 +692,7 @@ class EditNodeOperation {
547
692
  optionClause = opts.clause;
548
693
  }
549
694
  if (optionClause) {
550
- cls = clause.And(optionClause, cls);
695
+ cls = clause.And(cls, optionClause);
551
696
  }
552
697
  }
553
698
  const query = buildQuery({
@@ -1062,6 +1207,9 @@ function buildUpdateQuery(options, suffix) {
1062
1207
  let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
1063
1208
  query = query + options.whereClause.clause(idx);
1064
1209
  values.push(...options.whereClause.values());
1210
+ if (options.fieldsToLog) {
1211
+ logValues.push(...options.whereClause.logValues());
1212
+ }
1065
1213
  if (suffix) {
1066
1214
  query = query + " " + suffix;
1067
1215
  }
@@ -1352,18 +1500,22 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
1352
1500
  }
1353
1501
  exports.loadNodesByEdge = loadNodesByEdge;
1354
1502
  async function applyPrivacyPolicyForRow(viewer, options, row) {
1503
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
1504
+ return r instanceof Error ? null : r;
1505
+ }
1506
+ exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1507
+ async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
1355
1508
  if (!row) {
1356
1509
  return null;
1357
1510
  }
1358
1511
  const ent = new options.ent(viewer, row);
1359
- return await applyPrivacyPolicyForEnt(viewer, ent, row, options);
1512
+ return applyPrivacyPolicyForEnt(viewer, ent, row, options);
1360
1513
  }
1361
- exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1362
1514
  async function applyPrivacyPolicyForRowX(viewer, options, row) {
1363
1515
  const ent = new options.ent(viewer, row);
1364
1516
  return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
1365
1517
  }
1366
- exports.applyPrivacyPolicyForRowX = applyPrivacyPolicyForRowX;
1518
+ // TODO this needs to be changed to use ent cache as needed...
1367
1519
  async function applyPrivacyPolicyForRows(viewer, rows, options) {
1368
1520
  let m = new Map();
1369
1521
  // apply privacy logic
@@ -13,6 +13,7 @@ export declare class ObjectLoader<T> implements Loader<T, Data | null> {
13
13
  clearAll(): void;
14
14
  loadMany(keys: T[]): Promise<Data[]>;
15
15
  prime(data: Data): void;
16
+ primeAll(data: Data): void;
16
17
  }
17
18
  interface ObjectLoaderOptions extends SelectDataOptions {
18
19
  instanceKey?: string;
@@ -53,7 +53,7 @@ function createDataLoader(options) {
53
53
  optionClause = options.clause;
54
54
  }
55
55
  if (optionClause) {
56
- cls = clause.And(optionClause, cls);
56
+ cls = clause.And(cls, optionClause);
57
57
  }
58
58
  }
59
59
  const rowOptions = {
@@ -136,7 +136,7 @@ class ObjectLoader {
136
136
  optionClause = this.options.clause;
137
137
  }
138
138
  if (optionClause) {
139
- cls = clause.And(optionClause, cls);
139
+ cls = clause.And(cls, optionClause);
140
140
  }
141
141
  }
142
142
  const rowOptions = {
@@ -163,7 +163,7 @@ class ObjectLoader {
163
163
  optionClause = this.options.clause;
164
164
  }
165
165
  if (optionClause) {
166
- cls = clause.And(optionClause, cls);
166
+ cls = clause.And(cls, optionClause);
167
167
  }
168
168
  }
169
169
  const rowOptions = {
@@ -181,6 +181,18 @@ class ObjectLoader {
181
181
  this.loader.prime(key, data);
182
182
  }
183
183
  }
184
+ // prime this loader and any other loaders it's aware of
185
+ primeAll(data) {
186
+ this.prime(data);
187
+ if (this.primedLoaders) {
188
+ for (const [key, loader] of this.primedLoaders) {
189
+ const value = data[key];
190
+ if (value !== undefined) {
191
+ loader.prime(data);
192
+ }
193
+ }
194
+ }
195
+ }
184
196
  }
185
197
  exports.ObjectLoader = ObjectLoader;
186
198
  class ObjectLoaderFactory {
package/core/privacy.d.ts CHANGED
@@ -183,6 +183,7 @@ export declare class AllowIfSubPolicyAllowsRule implements PrivacyPolicyRule {
183
183
  }
184
184
  export declare function applyPrivacyPolicy(v: Viewer, policy: PrivacyPolicy, ent: Ent | undefined): Promise<boolean>;
185
185
  export declare function applyPrivacyPolicyX(v: Viewer, policy: PrivacyPolicy, ent: Ent | undefined, throwErr?: () => Error): Promise<boolean>;
186
+ export declare function applyPrivacyPolicyImpl(v: Viewer, policy: PrivacyPolicy, ent: Ent | undefined, throwErr?: () => Error): Promise<Error | null>;
186
187
  export declare const AlwaysAllowPrivacyPolicy: PrivacyPolicy;
187
188
  export declare const AlwaysDenyPrivacyPolicy: PrivacyPolicy;
188
189
  export declare const AllowIfViewerPrivacyPolicy: PrivacyPolicy;
package/core/privacy.js CHANGED
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.DelayedResultRule = exports.AllowIfConditionAppliesRule = exports.DenyIfViewerOutboundEdgeDoesNotExistRule = exports.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = exports.DenyIfViewerOutboundEdgeExistsRule = exports.DenyIfViewerInboundEdgeExistsRule = exports.DenyIfEdgeExistsRule = exports.AllowIfViewerOutboundEdgeExistsRule = exports.AllowIfViewerInboundEdgeExistsRule = exports.AllowIfEdgeExistsRule = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = exports.DenyIfEntPropertyIsRule = exports.AllowIfEntPropertyIsRule = exports.AllowIfViewerIsEntPropertyRule = exports.AllowIfViewerIsRule = exports.DenyIfFuncRule = exports.AllowIfFuncRule = exports.DenyIfViewerEqualsRule = exports.AllowIfViewerEqualsRule = exports.AllowIfViewerRule = exports.AllowIfHasIdentity = exports.DenyIfLoggedInRule = exports.DenyIfLoggedOutRule = exports.AlwaysDenyRule = exports.AlwaysAllowRule = exports.EntPrivacyError = void 0;
3
+ exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyImpl = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.DelayedResultRule = exports.AllowIfConditionAppliesRule = exports.DenyIfViewerOutboundEdgeDoesNotExistRule = exports.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = exports.DenyIfViewerOutboundEdgeExistsRule = exports.DenyIfViewerInboundEdgeExistsRule = exports.DenyIfEdgeExistsRule = exports.AllowIfViewerOutboundEdgeExistsRule = exports.AllowIfViewerInboundEdgeExistsRule = exports.AllowIfEdgeExistsRule = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = exports.DenyIfEntPropertyIsRule = exports.AllowIfEntPropertyIsRule = exports.AllowIfViewerIsEntPropertyRule = exports.AllowIfViewerIsRule = exports.DenyIfFuncRule = exports.AllowIfFuncRule = exports.DenyIfViewerEqualsRule = exports.AllowIfViewerEqualsRule = exports.AllowIfViewerRule = exports.AllowIfHasIdentity = exports.DenyIfLoggedInRule = exports.DenyIfLoggedOutRule = exports.AlwaysDenyRule = exports.AlwaysAllowRule = exports.EntPrivacyError = void 0;
4
4
  const base_1 = require("./base");
5
5
  const ent_1 = require("./ent");
6
- const logger_1 = require("./logger");
7
6
  // copied from ./base
8
7
  var privacyResult;
9
8
  (function (privacyResult) {
@@ -437,42 +436,42 @@ class AllowIfSubPolicyAllowsRule {
437
436
  }
438
437
  exports.AllowIfSubPolicyAllowsRule = AllowIfSubPolicyAllowsRule;
439
438
  async function applyPrivacyPolicy(v, policy, ent) {
440
- try {
441
- return await applyPrivacyPolicyX(v, policy, ent);
442
- }
443
- catch (e) {
444
- // TODO privacy errors should not throw
445
- // but other expected errors should throw...
446
- // we shouldn't just hide them
447
- (0, logger_1.log)("debug", e);
448
- return false;
449
- }
439
+ const err = await applyPrivacyPolicyImpl(v, policy, ent);
440
+ return err === null;
450
441
  }
451
442
  exports.applyPrivacyPolicy = applyPrivacyPolicy;
452
- // this will throw an exception if fails or return error | null?
453
443
  async function applyPrivacyPolicyX(v, policy, ent, throwErr) {
444
+ const err = await applyPrivacyPolicyImpl(v, policy, ent, throwErr);
445
+ if (err !== null) {
446
+ throw err;
447
+ }
448
+ return true;
449
+ }
450
+ exports.applyPrivacyPolicyX = applyPrivacyPolicyX;
451
+ // this will throw an exception if fails or return error | null?
452
+ async function applyPrivacyPolicyImpl(v, policy, ent, throwErr) {
454
453
  for (const rule of policy.rules) {
455
454
  const res = await rule.apply(v, ent);
456
455
  if (res.result == privacyResult.Allow) {
457
- return true;
456
+ return null;
458
457
  }
459
458
  else if (res.result == privacyResult.Deny) {
460
459
  // specific error throw that
461
460
  if (res.error) {
462
- throw res.error;
461
+ return res.error;
463
462
  }
464
463
  if (res.getError) {
465
- throw res.getError(policy, rule, ent);
464
+ return res.getError(policy, rule, ent);
466
465
  }
467
466
  if (throwErr) {
468
- throw throwErr();
467
+ return throwErr();
469
468
  }
470
- throw new EntPrivacyError(policy, rule, ent);
469
+ return new EntPrivacyError(policy, rule, ent);
471
470
  }
472
471
  }
473
- throw new EntInvalidPrivacyPolicyError(policy, ent);
472
+ return new EntInvalidPrivacyPolicyError(policy, ent);
474
473
  }
475
- exports.applyPrivacyPolicyX = applyPrivacyPolicyX;
474
+ exports.applyPrivacyPolicyImpl = applyPrivacyPolicyImpl;
476
475
  exports.AlwaysAllowPrivacyPolicy = {
477
476
  rules: [exports.AlwaysAllowRule],
478
477
  };
package/core/viewer.js CHANGED
@@ -35,7 +35,7 @@ class IDViewer {
35
35
  return this.ent;
36
36
  }
37
37
  instanceKey() {
38
- return `idViewer: ${this.viewerID}`;
38
+ return `idViewer:${this.viewerID}`;
39
39
  }
40
40
  }
41
41
  exports.IDViewer = IDViewer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snowtop/ent",
3
- "version": "0.1.0-alpha74",
3
+ "version": "0.1.0-alpha75",
4
4
  "description": "snowtop ent framework",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -205,6 +205,7 @@ export interface FieldOptions {
205
205
  privacyPolicy?: PrivacyPolicy | (() => PrivacyPolicy);
206
206
  getDerivedFields?(name: string): FieldMap;
207
207
  convert?: ConvertType;
208
+ fetchOnDemand?: boolean;
208
209
  [x: string]: any;
209
210
  }
210
211
  export interface PolymorphicOptions {