@snowtop/ent 0.1.0-alpha3 → 0.1.0-alpha34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/action/action.d.ts +28 -24
  2. package/action/executor.d.ts +4 -4
  3. package/action/executor.js +2 -2
  4. package/action/experimental_action.d.ts +21 -20
  5. package/action/experimental_action.js +11 -5
  6. package/action/orchestrator.d.ts +31 -16
  7. package/action/orchestrator.js +164 -43
  8. package/action/privacy.d.ts +2 -2
  9. package/core/base.d.ts +25 -21
  10. package/core/base.js +16 -0
  11. package/core/clause.d.ts +24 -3
  12. package/core/clause.js +246 -5
  13. package/core/config.d.ts +23 -0
  14. package/core/config.js +17 -0
  15. package/core/context.d.ts +2 -2
  16. package/core/convert.d.ts +1 -1
  17. package/core/db.d.ts +3 -3
  18. package/core/db.js +2 -0
  19. package/core/ent.d.ts +19 -21
  20. package/core/ent.js +76 -25
  21. package/core/loaders/assoc_count_loader.d.ts +2 -2
  22. package/core/loaders/assoc_edge_loader.d.ts +3 -3
  23. package/core/loaders/assoc_edge_loader.js +5 -4
  24. package/core/loaders/index_loader.js +1 -0
  25. package/core/loaders/object_loader.d.ts +10 -5
  26. package/core/loaders/object_loader.js +59 -4
  27. package/core/loaders/query_loader.d.ts +2 -2
  28. package/core/loaders/raw_count_loader.d.ts +2 -2
  29. package/core/privacy.d.ts +25 -25
  30. package/core/privacy.js +3 -0
  31. package/core/query/assoc_query.d.ts +6 -6
  32. package/core/query/custom_query.d.ts +5 -5
  33. package/core/query/query.d.ts +1 -1
  34. package/core/viewer.d.ts +4 -3
  35. package/core/viewer.js +4 -0
  36. package/graphql/builtins/connection.js +3 -3
  37. package/graphql/builtins/edge.js +2 -2
  38. package/graphql/builtins/node.js +1 -1
  39. package/graphql/graphql.d.ts +3 -2
  40. package/graphql/graphql.js +24 -23
  41. package/graphql/index.d.ts +1 -0
  42. package/graphql/index.js +3 -1
  43. package/graphql/mutations/union.d.ts +2 -0
  44. package/graphql/mutations/union.js +35 -0
  45. package/graphql/node_resolver.d.ts +0 -1
  46. package/graphql/query/connection_type.js +6 -6
  47. package/graphql/query/edge_connection.d.ts +9 -9
  48. package/graphql/query/page_info.d.ts +1 -1
  49. package/graphql/query/page_info.js +4 -4
  50. package/graphql/query/shared_assoc_test.js +2 -2
  51. package/graphql/scalars/time.d.ts +1 -1
  52. package/index.d.ts +16 -1
  53. package/index.js +18 -5
  54. package/package.json +3 -3
  55. package/parse_schema/parse.d.ts +18 -5
  56. package/parse_schema/parse.js +55 -6
  57. package/schema/base_schema.d.ts +36 -1
  58. package/schema/base_schema.js +51 -2
  59. package/schema/field.d.ts +1 -1
  60. package/schema/field.js +1 -1
  61. package/schema/index.d.ts +2 -2
  62. package/schema/index.js +8 -1
  63. package/schema/schema.d.ts +61 -1
  64. package/schema/schema.js +126 -5
  65. package/schema/union_field.d.ts +2 -1
  66. package/schema/union_field.js +32 -14
  67. package/scripts/custom_graphql.js +122 -15
  68. package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
  69. package/scripts/migrate_v0.1.js +36 -0
  70. package/scripts/read_schema.js +15 -1
  71. package/testutils/builder.d.ts +31 -21
  72. package/testutils/builder.js +98 -13
  73. package/testutils/context/test_context.d.ts +2 -2
  74. package/testutils/context/test_context.js +7 -1
  75. package/testutils/db/test_db.d.ts +2 -1
  76. package/testutils/db/test_db.js +13 -4
  77. package/testutils/ent-graphql-tests/index.d.ts +2 -0
  78. package/testutils/ent-graphql-tests/index.js +26 -17
  79. package/testutils/fake_data/fake_contact.d.ts +4 -8
  80. package/testutils/fake_data/fake_contact.js +15 -19
  81. package/testutils/fake_data/fake_event.d.ts +4 -8
  82. package/testutils/fake_data/fake_event.js +21 -25
  83. package/testutils/fake_data/fake_user.d.ts +5 -9
  84. package/testutils/fake_data/fake_user.js +23 -27
  85. package/testutils/fake_data/test_helpers.js +1 -1
  86. package/testutils/fake_data/user_query.d.ts +2 -2
  87. package/testutils/fake_log.d.ts +3 -3
  88. package/tsc/ast.d.ts +43 -0
  89. package/tsc/ast.js +264 -0
  90. package/tsc/compilerOptions.d.ts +6 -0
  91. package/tsc/compilerOptions.js +40 -1
  92. package/tsc/move_generated.d.ts +1 -0
  93. package/tsc/move_generated.js +160 -0
  94. package/tsc/transform.d.ts +21 -0
  95. package/tsc/transform.js +167 -0
  96. package/tsc/transform_action.d.ts +20 -0
  97. package/tsc/transform_action.js +169 -0
  98. package/tsc/transform_ent.d.ts +17 -0
  99. package/tsc/transform_ent.js +59 -0
  100. package/tsc/transform_schema.d.ts +27 -0
  101. package/tsc/transform_schema.js +354 -0
  102. package/scripts/transform_schema.js +0 -288
@@ -1,14 +1,16 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
4
7
  const ent_1 = require("../core/ent");
5
8
  const schema_1 = require("../schema/schema");
6
9
  const action_1 = require("../action");
7
- const snake_case_1 = require("snake-case");
8
- const camel_case_1 = require("camel-case");
9
10
  const privacy_1 = require("../core/privacy");
10
11
  const executor_1 = require("./executor");
11
12
  const logger_1 = require("../core/logger");
13
+ const memoizee_1 = __importDefault(require("memoizee"));
12
14
  var edgeDirection;
13
15
  (function (edgeDirection) {
14
16
  edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
@@ -62,6 +64,9 @@ class Orchestrator {
62
64
  this.defaultFieldsByFieldName = {};
63
65
  this.defaultFieldsByTSName = {};
64
66
  this.viewer = options.viewer;
67
+ this.actualOperation = this.options.operation;
68
+ this.existingEnt = this.options.builder.existingEnt;
69
+ this.memoizedGetFields = (0, memoizee_1.default)(this.getFieldsInfo.bind(this));
65
70
  }
66
71
  addEdge(edge, op) {
67
72
  this.edgeSet.add(edge.edgeType);
@@ -80,6 +85,9 @@ class Orchestrator {
80
85
  m1.set(op, m2);
81
86
  this.edges.set(edge.edgeType, m1);
82
87
  }
88
+ setDisableTransformations(val) {
89
+ this.disableTransformations = val;
90
+ }
83
91
  addInboundEdge(id1, edgeType, nodeType, options) {
84
92
  this.addEdge(new edgeInputData({
85
93
  id: id1,
@@ -135,24 +143,27 @@ class Orchestrator {
135
143
  }
136
144
  buildMainOp() {
137
145
  // this assumes we have validated fields
138
- switch (this.options.operation) {
146
+ switch (this.actualOperation) {
139
147
  case action_1.WriteOperation.Delete:
140
- return new ent_1.DeleteNodeOperation(this.options.builder.existingEnt.id, {
148
+ return new ent_1.DeleteNodeOperation(this.existingEnt.id, {
141
149
  tableName: this.options.tableName,
142
150
  });
143
151
  default:
152
+ if (this.actualOperation === action_1.WriteOperation.Edit && !this.existingEnt) {
153
+ throw new Error(`existing ent required with operation ${this.actualOperation}`);
154
+ }
144
155
  const opts = {
145
156
  fields: this.validatedFields,
146
157
  tableName: this.options.tableName,
147
158
  fieldsToResolve: this.fieldsToResolve,
148
159
  key: this.options.key,
149
- ent: this.options.loaderOptions.ent,
160
+ loadEntOptions: this.options.loaderOptions,
150
161
  placeholderID: this.options.builder.placeholderID,
151
162
  };
152
163
  if (this.logValues) {
153
164
  opts.fieldsToLog = this.logValues;
154
165
  }
155
- this.mainOp = new ent_1.EditNodeOperation(opts, this.options.builder.existingEnt);
166
+ this.mainOp = new ent_1.EditNodeOperation(opts, this.existingEnt);
156
167
  return this.mainOp;
157
168
  }
158
169
  }
@@ -219,35 +230,82 @@ class Orchestrator {
219
230
  if (!privacyPolicy || !action) {
220
231
  throw new Error(`shouldn't get here if no privacyPolicy for action`);
221
232
  }
222
- if (this.options.operation === action_1.WriteOperation.Insert) {
233
+ if (this.actualOperation === action_1.WriteOperation.Insert) {
223
234
  return new EntCannotCreateEntError(privacyPolicy, action);
224
235
  }
225
- else if (this.options.operation === action_1.WriteOperation.Edit) {
226
- return new EntCannotEditEntError(privacyPolicy, action, this.options.builder.existingEnt);
236
+ else if (this.actualOperation === action_1.WriteOperation.Edit) {
237
+ return new EntCannotEditEntError(privacyPolicy, action, this.existingEnt);
227
238
  }
228
- return new EntCannotDeleteEntError(privacyPolicy, action, this.options.builder.existingEnt);
239
+ return new EntCannotDeleteEntError(privacyPolicy, action, this.existingEnt);
229
240
  }
230
- getEntForPrivacyPolicy(editedData) {
231
- if (this.options.operation !== action_1.WriteOperation.Insert) {
232
- return this.options.builder.existingEnt;
241
+ getEntForPrivacyPolicyImpl(editedData) {
242
+ if (this.actualOperation !== action_1.WriteOperation.Insert) {
243
+ return this.existingEnt;
233
244
  }
234
245
  // we create an unsafe ent to be used for privacy policies
235
246
  return new this.options.builder.ent(this.options.builder.viewer, editedData);
236
247
  }
248
+ getSQLStatementOperation() {
249
+ switch (this.actualOperation) {
250
+ case action_1.WriteOperation.Edit:
251
+ return schema_1.SQLStatementOperation.Update;
252
+ case action_1.WriteOperation.Insert:
253
+ return schema_1.SQLStatementOperation.Insert;
254
+ case action_1.WriteOperation.Delete:
255
+ return schema_1.SQLStatementOperation.Delete;
256
+ }
257
+ }
258
+ getWriteOpForSQLStamentOp(op) {
259
+ switch (op) {
260
+ case schema_1.SQLStatementOperation.Update:
261
+ return action_1.WriteOperation.Edit;
262
+ case schema_1.SQLStatementOperation.Insert:
263
+ return action_1.WriteOperation.Insert;
264
+ case schema_1.SQLStatementOperation.Update:
265
+ return action_1.WriteOperation.Delete;
266
+ default:
267
+ throw new Error("invalid path");
268
+ }
269
+ }
270
+ // if you're doing custom privacy within an action and want to
271
+ // get either the unsafe ent or the existing ent that's being edited
272
+ async getPossibleUnsafeEntForPrivacy() {
273
+ if (this.actualOperation !== action_1.WriteOperation.Insert) {
274
+ return this.existingEnt;
275
+ }
276
+ const { editedData } = await this.memoizedGetFields();
277
+ return this.getEntForPrivacyPolicyImpl(editedData);
278
+ }
279
+ // this gets the fields that were explicitly set plus any default or transformed values
280
+ // mainly exists to get default fields e.g. default id to be used in triggers
281
+ // NOTE: this API may change in the future
282
+ // doesn't work to get ids for autoincrement keys
283
+ async getEditedData() {
284
+ const { editedData } = await this.memoizedGetFields();
285
+ return editedData;
286
+ }
287
+ // Note: this is memoized. call memoizedGetFields instead
288
+ async getFieldsInfo() {
289
+ const action = this.options.action;
290
+ const builder = this.options.builder;
291
+ // future optimization: can get schemaFields to memoize based on different values
292
+ const schemaFields = (0, schema_1.getFields)(this.options.schema);
293
+ const editedFields = await this.options.editedFields();
294
+ let editedData = await this.getFieldsWithDefaultValues(builder, schemaFields, editedFields, action);
295
+ return { editedData, editedFields, schemaFields };
296
+ }
237
297
  async validate() {
238
298
  // existing ent required for edit or delete operations
239
- switch (this.options.operation) {
299
+ switch (this.actualOperation) {
240
300
  case action_1.WriteOperation.Delete:
241
301
  case action_1.WriteOperation.Edit:
242
- if (!this.options.builder.existingEnt) {
243
- throw new Error("existing ent required with operation");
302
+ if (!this.existingEnt) {
303
+ throw new Error(`existing ent required with operation ${this.actualOperation}`);
244
304
  }
245
305
  }
306
+ const { schemaFields, editedData } = await this.memoizedGetFields();
246
307
  const action = this.options.action;
247
308
  const builder = this.options.builder;
248
- // future optimization: can get schemaFields to memoize based on different values
249
- const schemaFields = (0, schema_1.getFields)(this.options.schema);
250
- let editedData = this.getFieldsWithDefaultValues(builder, schemaFields, action);
251
309
  // this runs in following phases:
252
310
  // * set default fields and pass to builder so the value can be checked by triggers/observers/validators
253
311
  // * privacy policy (use unsafe ent if we have it)
@@ -255,17 +313,22 @@ class Orchestrator {
255
313
  // * validators
256
314
  let privacyPolicy = action?.getPrivacyPolicy();
257
315
  if (privacyPolicy) {
258
- await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicy(editedData), this.throwError.bind(this));
316
+ await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
259
317
  }
260
318
  // have to run triggers which update fields first before field and other validators
261
319
  // so running this first to build things up
262
- let triggers = action?.triggers;
263
- if (triggers) {
264
- await this.triggers(action, builder, triggers);
320
+ if (action?.getTriggers) {
321
+ await this.triggers(action, builder, action.getTriggers());
322
+ }
323
+ let validators = [];
324
+ if (action?.getValidators) {
325
+ validators = action.getValidators();
265
326
  }
266
- let validators = action?.validators || [];
327
+ // not ideal we're calling this twice. fix...
328
+ // needed for now. may need to rewrite some of this?
329
+ const editedFields2 = await this.options.editedFields();
267
330
  await Promise.all([
268
- this.formatAndValidateFields(schemaFields),
331
+ this.formatAndValidateFields(schemaFields, editedFields2),
269
332
  this.validators(validators, action, builder),
270
333
  ]);
271
334
  }
@@ -300,18 +363,79 @@ class Orchestrator {
300
363
  isBuilder(val) {
301
364
  return val.placeholderID !== undefined;
302
365
  }
303
- getFieldsWithDefaultValues(builder, schemaFields, action) {
304
- const editedFields = this.options.editedFields();
366
+ getInputKey(k) {
367
+ return this.options.fieldInfo[k].inputKey;
368
+ }
369
+ getStorageKey(k) {
370
+ return this.options.fieldInfo[k].dbCol;
371
+ }
372
+ async getFieldsWithDefaultValues(builder, schemaFields, editedFields, action) {
305
373
  let data = {};
306
374
  let defaultData = {};
307
375
  let input = action?.getInput() || {};
308
376
  let updateInput = false;
377
+ // transformations
378
+ // if action transformations. always do it
379
+ // if disable transformations set, don't do schema transform and just do the right thing
380
+ // else apply schema tranformation if it exists
381
+ let transformed = null;
382
+ const sqlOp = this.getSQLStatementOperation();
383
+ if (action?.transformWrite) {
384
+ transformed = await action.transformWrite({
385
+ builder,
386
+ input,
387
+ op: sqlOp,
388
+ data: editedFields,
389
+ });
390
+ }
391
+ else if (!this.disableTransformations) {
392
+ transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
393
+ builder,
394
+ input,
395
+ op: sqlOp,
396
+ data: editedFields,
397
+ });
398
+ }
399
+ if (transformed) {
400
+ if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
401
+ if (!transformed.existingEnt) {
402
+ throw new Error(`cannot transform an insert operation without providing an existing ent`);
403
+ }
404
+ }
405
+ if (transformed.data) {
406
+ updateInput = true;
407
+ for (const k in transformed.data) {
408
+ let field = schemaFields.get(k);
409
+ if (!field) {
410
+ throw new Error(`tried to transform field with unknown field ${k}`);
411
+ }
412
+ let val = transformed.data[k];
413
+ if (field.format) {
414
+ val = field.format(transformed.data[k]);
415
+ }
416
+ data[this.getStorageKey(k)] = val;
417
+ this.defaultFieldsByTSName[this.getInputKey(k)] = val;
418
+ // hmm do we need this?
419
+ // TODO how to do this for local tests?
420
+ // this.defaultFieldsByFieldName[k] = val;
421
+ }
422
+ }
423
+ this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
424
+ if (transformed.existingEnt) {
425
+ // @ts-ignore
426
+ this.existingEnt = transformed.existingEnt;
427
+ // modify existing ent in builder. it's readonly in generated ents but doesn't apply here
428
+ builder.existingEnt = transformed.existingEnt;
429
+ }
430
+ }
431
+ // transforming before doing default fields so that we don't create a new id
432
+ // and anything that depends on the type of operations knows what it is
309
433
  for (const [fieldName, field] of schemaFields) {
310
434
  let value = editedFields.get(fieldName);
311
435
  let defaultValue = undefined;
312
- let dbKey = field.storageKey || (0, snake_case_1.snakeCase)(fieldName);
436
+ let dbKey = this.getStorageKey(fieldName);
313
437
  if (value === undefined) {
314
- if (this.options.operation === action_1.WriteOperation.Insert) {
438
+ if (this.actualOperation === action_1.WriteOperation.Insert) {
315
439
  if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
316
440
  throw new Error(`cannot set both defaultToViewerOnCreate and defaultValueOnCreate`);
317
441
  }
@@ -326,7 +450,7 @@ class Orchestrator {
326
450
  }
327
451
  }
328
452
  if (field.defaultValueOnEdit &&
329
- this.options.operation === action_1.WriteOperation.Edit) {
453
+ this.actualOperation === action_1.WriteOperation.Edit) {
330
454
  defaultValue = field.defaultValueOnEdit(builder, input);
331
455
  }
332
456
  }
@@ -337,8 +461,7 @@ class Orchestrator {
337
461
  updateInput = true;
338
462
  defaultData[dbKey] = defaultValue;
339
463
  this.defaultFieldsByFieldName[fieldName] = defaultValue;
340
- // TODO related to #510. we need this logic to be consistent so do this all in TypeScript or get it from go somehow
341
- this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(fieldName)] = defaultValue;
464
+ this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
342
465
  }
343
466
  }
344
467
  // if there's data changing, add data
@@ -374,13 +497,13 @@ class Orchestrator {
374
497
  // not setting server default as we're depending on the database handling that.
375
498
  // server default allowed
376
499
  field.serverDefault === undefined &&
377
- this.options.operation === action_1.WriteOperation.Insert) {
500
+ this.actualOperation === action_1.WriteOperation.Insert) {
378
501
  throw new Error(`required field ${fieldName} not set`);
379
502
  }
380
503
  }
381
504
  else if (this.isBuilder(value)) {
382
505
  if (field.valid) {
383
- const valid = await Promise.resolve(field.valid(value));
506
+ const valid = await field.valid(value);
384
507
  if (!valid) {
385
508
  throw new Error(`invalid field ${fieldName} with value ${value}`);
386
509
  }
@@ -393,24 +516,22 @@ class Orchestrator {
393
516
  else {
394
517
  if (field.valid) {
395
518
  // TODO this could be async. handle this better
396
- const valid = await Promise.resolve(field.valid(value));
519
+ const valid = await field.valid(value);
397
520
  if (!valid) {
398
521
  throw new Error(`invalid field ${fieldName} with value ${value}`);
399
522
  }
400
523
  }
401
524
  if (field.format) {
402
- // TODO this could be async e.g. password. handle this better
403
- value = await Promise.resolve(field.format(value));
525
+ value = await field.format(value);
404
526
  }
405
527
  }
406
528
  return value;
407
529
  }
408
- async formatAndValidateFields(schemaFields) {
409
- const op = this.options.operation;
530
+ async formatAndValidateFields(schemaFields, editedFields) {
531
+ const op = this.actualOperation;
410
532
  if (op === action_1.WriteOperation.Delete) {
411
533
  return;
412
534
  }
413
- const editedFields = this.options.editedFields();
414
535
  // build up data to be saved...
415
536
  let data = {};
416
537
  let logValues = {};
@@ -420,7 +541,7 @@ class Orchestrator {
420
541
  // null allowed
421
542
  value = this.defaultFieldsByFieldName[fieldName];
422
543
  }
423
- let dbKey = field.storageKey || (0, snake_case_1.snakeCase)(fieldName);
544
+ let dbKey = this.getStorageKey(fieldName);
424
545
  value = await this.transformFieldValue(fieldName, field, dbKey, value);
425
546
  if (value !== undefined) {
426
547
  data[dbKey] = value;
@@ -433,7 +554,7 @@ class Orchestrator {
433
554
  for (const fieldName in this.defaultFieldsByFieldName) {
434
555
  const defaultValue = this.defaultFieldsByFieldName[fieldName];
435
556
  let field = schemaFields.get(fieldName);
436
- let dbKey = field.storageKey || (0, snake_case_1.snakeCase)(fieldName);
557
+ let dbKey = this.getStorageKey(fieldName);
437
558
  // no value, let's just default
438
559
  if (data[dbKey] === undefined) {
439
560
  const value = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
@@ -494,7 +615,7 @@ class Orchestrator {
494
615
  const viewer = await this.viewerForEntLoad(row);
495
616
  const ent = await (0, ent_1.applyPrivacyPolicyForRow)(viewer, this.options.loaderOptions, row);
496
617
  if (!ent) {
497
- if (this.options.operation == action_1.WriteOperation.Insert) {
618
+ if (this.actualOperation == action_1.WriteOperation.Insert) {
498
619
  throw new Error(`was able to create ent but not load it`);
499
620
  }
500
621
  else {
@@ -2,11 +2,11 @@ import { Builder } from "./action";
2
2
  import { Viewer, ID, Ent, PrivacyResult, PrivacyPolicyRule } from "../core/base";
3
3
  export declare class DenyIfBuilder implements PrivacyPolicyRule {
4
4
  private id?;
5
- constructor(id?: ID | Builder<Ent> | undefined);
5
+ constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
6
6
  apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
7
7
  }
8
8
  export declare class AllowIfBuilder implements PrivacyPolicyRule {
9
9
  private id?;
10
- constructor(id?: ID | Builder<Ent> | undefined);
10
+ constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
11
11
  apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
12
12
  }
package/core/base.d.ts CHANGED
@@ -30,27 +30,27 @@ interface queryOptions {
30
30
  clause: clause.Clause;
31
31
  orderby?: string;
32
32
  }
33
- export interface Context {
34
- getViewer(): Viewer;
33
+ export interface Context<TViewer extends Viewer = Viewer> {
34
+ getViewer(): TViewer;
35
35
  cache?: cache;
36
36
  }
37
- export interface Viewer {
38
- viewerID: ID | null;
39
- viewer: () => Promise<Ent | null>;
37
+ export interface Viewer<TEnt extends any = Ent<any> | null, TID extends any = ID | null> {
38
+ viewerID: TID;
39
+ viewer: () => Promise<TEnt>;
40
40
  instanceKey: () => string;
41
- context?: Context;
41
+ context?: Context<any>;
42
42
  }
43
- export interface Ent {
43
+ export interface Ent<TViewer extends Viewer = Viewer> {
44
44
  id: ID;
45
- viewer: Viewer;
46
- privacyPolicy: PrivacyPolicy<this>;
45
+ viewer: TViewer;
46
+ getPrivacyPolicy(): PrivacyPolicy<this, TViewer>;
47
47
  nodeType: string;
48
48
  }
49
49
  export declare type Data = {
50
50
  [key: string]: any;
51
51
  };
52
- export interface EntConstructor<T extends Ent> {
53
- new (viewer: Viewer, data: Data): T;
52
+ export interface EntConstructor<TEnt extends Ent, TViewer extends Viewer = Viewer> {
53
+ new (viewer: TViewer, data: Data): TEnt;
54
54
  }
55
55
  export declare type ID = string | number;
56
56
  export interface DataOptions {
@@ -62,6 +62,7 @@ export interface SelectBaseDataOptions extends DataOptions {
62
62
  }
63
63
  export interface SelectDataOptions extends SelectBaseDataOptions {
64
64
  key: string;
65
+ clause?: clause.Clause | (() => clause.Clause | undefined);
65
66
  }
66
67
  export interface QueryableDataOptions extends SelectBaseDataOptions, QueryDataOptions {
67
68
  }
@@ -83,14 +84,16 @@ export interface CreateRowOptions extends DataOptions {
83
84
  export interface EditRowOptions extends CreateRowOptions {
84
85
  key: string;
85
86
  }
86
- interface LoadableEntOptions<T extends Ent> {
87
+ interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> {
87
88
  loaderFactory: LoaderFactory<any, Data | null>;
88
- ent: EntConstructor<T>;
89
+ ent: EntConstructor<TEnt, TViewer>;
89
90
  }
90
- export interface LoadEntOptions<T extends Ent> extends LoadableEntOptions<T>, SelectBaseDataOptions {
91
+ export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends LoadableEntOptions<TEnt, TViewer>, SelectBaseDataOptions {
92
+ fieldPrivacy?: Map<string, PrivacyPolicy>;
91
93
  }
92
- export interface LoadCustomEntOptions<T extends Ent> extends SelectBaseDataOptions {
93
- ent: EntConstructor<T>;
94
+ export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends SelectBaseDataOptions {
95
+ ent: EntConstructor<TEnt, TViewer>;
96
+ fieldPrivacy?: Map<string, PrivacyPolicy>;
94
97
  }
95
98
  export interface LoaderInfo {
96
99
  tableName: string;
@@ -108,6 +111,7 @@ declare enum privacyResult {
108
111
  export interface PrivacyResult {
109
112
  result: privacyResult;
110
113
  error?: PrivacyError;
114
+ getError?(policy: PrivacyPolicy, rule: PrivacyPolicyRule, ent?: Ent): PrivacyError;
111
115
  }
112
116
  export interface PrivacyError extends Error {
113
117
  privacyPolicy: PrivacyPolicy<Ent>;
@@ -116,11 +120,11 @@ export interface PrivacyError extends Error {
116
120
  export declare function Allow(): PrivacyResult;
117
121
  export declare function Skip(): PrivacyResult;
118
122
  export declare function Deny(): PrivacyResult;
119
- export declare function DenyWithReason(e: PrivacyError): PrivacyResult;
120
- export interface PrivacyPolicyRule<TEnt extends Ent = Ent> {
121
- apply(v: Viewer, ent?: TEnt): Promise<PrivacyResult>;
123
+ export declare function DenyWithReason(e: PrivacyError | string): PrivacyResult;
124
+ export interface PrivacyPolicyRule<TEnt extends Ent = Ent, TViewer = Viewer> {
125
+ apply(v: TViewer, ent?: TEnt): Promise<PrivacyResult>;
122
126
  }
123
- export interface PrivacyPolicy<TEnt extends Ent = Ent> {
124
- rules: PrivacyPolicyRule<TEnt>[];
127
+ export interface PrivacyPolicy<TEnt extends Ent = Ent, TViewer = Viewer> {
128
+ rules: PrivacyPolicyRule<TEnt, TViewer>[];
125
129
  }
126
130
  export {};
package/core/base.js CHANGED
@@ -30,7 +30,23 @@ function Deny() {
30
30
  return deny;
31
31
  }
32
32
  exports.Deny = Deny;
33
+ class DenyWithReasonError extends Error {
34
+ constructor(privacyPolicy, rule, msg, ent) {
35
+ super(msg);
36
+ this.privacyPolicy = privacyPolicy;
37
+ this.privacyRule = rule;
38
+ this.ent = ent;
39
+ }
40
+ }
33
41
  function DenyWithReason(e) {
42
+ if (typeof e === "string") {
43
+ return {
44
+ result: privacyResult.Deny,
45
+ getError(policy, rule, ent) {
46
+ return new DenyWithReasonError(policy, rule, e, ent);
47
+ },
48
+ };
49
+ }
34
50
  return {
35
51
  result: privacyResult.Deny,
36
52
  error: e,
package/core/clause.d.ts CHANGED
@@ -12,8 +12,10 @@ declare class simpleClause implements Clause {
12
12
  protected col: string;
13
13
  private value;
14
14
  private op;
15
- constructor(col: string, value: any, op: string);
15
+ private handleSqliteNull?;
16
+ constructor(col: string, value: any, op: string, handleSqliteNull?: Clause | undefined);
16
17
  clause(idx: number): string;
18
+ private sqliteNull;
17
19
  values(): any[];
18
20
  logValues(): any[];
19
21
  instanceKey(): string;
@@ -27,14 +29,33 @@ declare class compositeClause implements Clause {
27
29
  logValues(): any[];
28
30
  instanceKey(): string;
29
31
  }
30
- export declare function Eq(col: string, value: any): simpleClause;
31
- export declare function NotEq(col: string, value: any): simpleClause;
32
+ export declare function ArrayEq(col: string, value: any): Clause;
33
+ export declare function ArrayNotEq(col: string, value: any): Clause;
34
+ export declare function ArrayGreater(col: string, value: any): Clause;
35
+ export declare function ArrayLess(col: string, value: any): Clause;
36
+ export declare function ArrayGreaterEq(col: string, value: any): Clause;
37
+ export declare function ArrayLessEq(col: string, value: any): Clause;
38
+ export declare function Eq(col: string, value: any): Clause;
39
+ export declare function NotEq(col: string, value: any): Clause;
32
40
  export declare function Greater(col: string, value: any): simpleClause;
33
41
  export declare function Less(col: string, value: any): simpleClause;
34
42
  export declare function GreaterEq(col: string, value: any): simpleClause;
35
43
  export declare function LessEq(col: string, value: any): simpleClause;
36
44
  export declare function And(...args: Clause[]): compositeClause;
45
+ export declare function AndOptional(...args: (Clause | undefined)[]): Clause;
37
46
  export declare function Or(...args: Clause[]): compositeClause;
38
47
  export declare function In(col: string, ...values: any): Clause;
48
+ interface TsQuery {
49
+ language: "english" | "french" | "german" | "simple";
50
+ value: string;
51
+ }
52
+ export declare function TsQuery(col: string, val: string | TsQuery): Clause;
53
+ export declare function PlainToTsQuery(col: string, val: string | TsQuery): Clause;
54
+ export declare function PhraseToTsQuery(col: string, val: string | TsQuery): Clause;
55
+ export declare function WebsearchToTsQuery(col: string, val: string | TsQuery): Clause;
56
+ export declare function TsVectorColTsQuery(col: string, val: string | TsQuery): Clause;
57
+ export declare function TsVectorPlainToTsQuery(col: string, val: string | TsQuery): Clause;
58
+ export declare function TsVectorPhraseToTsQuery(col: string, val: string | TsQuery): Clause;
59
+ export declare function TsVectorWebsearchToTsQuery(col: string, val: string | TsQuery): Clause;
39
60
  export declare function sensitiveValue(val: any): SensitiveValue;
40
61
  export {};