@snowtop/ent 0.1.0-alpha8 → 0.1.0-alpha80

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 (112) hide show
  1. package/action/action.d.ts +27 -25
  2. package/action/executor.d.ts +3 -3
  3. package/action/executor.js +2 -2
  4. package/action/experimental_action.d.ts +29 -22
  5. package/action/experimental_action.js +29 -6
  6. package/action/orchestrator.d.ts +37 -16
  7. package/action/orchestrator.js +184 -54
  8. package/action/privacy.d.ts +2 -2
  9. package/core/base.d.ts +38 -22
  10. package/core/clause.d.ts +65 -7
  11. package/core/clause.js +261 -54
  12. package/core/config.d.ts +8 -0
  13. package/core/context.d.ts +5 -3
  14. package/core/context.js +7 -2
  15. package/core/convert.d.ts +1 -1
  16. package/core/db.d.ts +1 -2
  17. package/core/db.js +1 -1
  18. package/core/ent.d.ts +76 -24
  19. package/core/ent.js +459 -143
  20. package/core/loaders/assoc_count_loader.d.ts +2 -2
  21. package/core/loaders/assoc_count_loader.js +6 -1
  22. package/core/loaders/assoc_edge_loader.d.ts +2 -2
  23. package/core/loaders/loader.js +5 -5
  24. package/core/loaders/object_loader.d.ts +4 -3
  25. package/core/loaders/object_loader.js +22 -5
  26. package/core/loaders/query_loader.d.ts +2 -2
  27. package/core/loaders/raw_count_loader.d.ts +2 -2
  28. package/core/logger.d.ts +1 -1
  29. package/core/logger.js +1 -0
  30. package/core/privacy.d.ts +26 -25
  31. package/core/privacy.js +21 -25
  32. package/core/query/assoc_query.d.ts +6 -6
  33. package/core/query/custom_query.d.ts +5 -5
  34. package/core/query/query.d.ts +1 -1
  35. package/core/query/shared_assoc_test.d.ts +1 -1
  36. package/core/query/shared_assoc_test.js +17 -5
  37. package/core/query/shared_test.d.ts +3 -0
  38. package/core/query/shared_test.js +95 -17
  39. package/core/viewer.d.ts +3 -3
  40. package/core/viewer.js +1 -1
  41. package/graphql/builtins/connection.js +3 -3
  42. package/graphql/builtins/edge.js +2 -2
  43. package/graphql/builtins/node.js +1 -1
  44. package/graphql/graphql.js +8 -2
  45. package/graphql/query/connection_type.js +6 -6
  46. package/graphql/query/edge_connection.d.ts +9 -9
  47. package/graphql/query/page_info.d.ts +1 -1
  48. package/graphql/query/page_info.js +4 -4
  49. package/graphql/query/shared_assoc_test.js +2 -2
  50. package/graphql/scalars/time.d.ts +1 -1
  51. package/index.d.ts +10 -5
  52. package/index.js +13 -6
  53. package/package.json +2 -2
  54. package/parse_schema/parse.d.ts +14 -2
  55. package/parse_schema/parse.js +40 -2
  56. package/schema/base_schema.d.ts +1 -1
  57. package/schema/base_schema.js +3 -0
  58. package/schema/field.d.ts +36 -7
  59. package/schema/field.js +70 -2
  60. package/schema/index.d.ts +2 -2
  61. package/schema/json_field.d.ts +13 -1
  62. package/schema/json_field.js +28 -1
  63. package/schema/schema.d.ts +61 -10
  64. package/schema/schema.js +18 -4
  65. package/schema/struct_field.d.ts +11 -1
  66. package/schema/struct_field.js +43 -4
  67. package/scripts/custom_graphql.js +5 -1
  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 +25 -2
  71. package/testutils/builder.d.ts +31 -21
  72. package/testutils/builder.js +82 -29
  73. package/testutils/db/fixture.d.ts +10 -0
  74. package/testutils/db/fixture.js +26 -0
  75. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +15 -3
  76. package/testutils/db/{test_db.js → temp_db.js} +63 -15
  77. package/testutils/db/value.d.ts +6 -0
  78. package/testutils/db/value.js +251 -0
  79. package/testutils/db_time_zone.d.ts +4 -0
  80. package/testutils/db_time_zone.js +41 -0
  81. package/testutils/ent-graphql-tests/index.js +19 -12
  82. package/testutils/fake_data/fake_contact.d.ts +3 -3
  83. package/testutils/fake_data/fake_contact.js +9 -6
  84. package/testutils/fake_data/fake_event.d.ts +3 -3
  85. package/testutils/fake_data/fake_event.js +8 -5
  86. package/testutils/fake_data/fake_user.d.ts +4 -4
  87. package/testutils/fake_data/fake_user.js +16 -13
  88. package/testutils/fake_data/test_helpers.d.ts +2 -2
  89. package/testutils/fake_data/test_helpers.js +5 -5
  90. package/testutils/fake_data/user_query.d.ts +2 -2
  91. package/testutils/fake_log.d.ts +3 -3
  92. package/testutils/parse_sql.d.ts +6 -0
  93. package/testutils/parse_sql.js +16 -2
  94. package/testutils/test_edge_global_schema.d.ts +15 -0
  95. package/testutils/test_edge_global_schema.js +58 -0
  96. package/testutils/write.d.ts +2 -2
  97. package/testutils/write.js +29 -7
  98. package/tsc/ast.d.ts +44 -0
  99. package/tsc/ast.js +267 -0
  100. package/tsc/compilerOptions.d.ts +6 -0
  101. package/tsc/compilerOptions.js +40 -1
  102. package/tsc/move_generated.d.ts +1 -0
  103. package/tsc/move_generated.js +160 -0
  104. package/tsc/transform.d.ts +21 -0
  105. package/tsc/transform.js +167 -0
  106. package/tsc/transform_action.d.ts +22 -0
  107. package/tsc/transform_action.js +179 -0
  108. package/tsc/transform_ent.d.ts +17 -0
  109. package/tsc/transform_ent.js +59 -0
  110. package/tsc/transform_schema.d.ts +27 -0
  111. package/tsc/transform_schema.js +379 -0
  112. package/scripts/transform_schema.js +0 -437
@@ -1,13 +1,36 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
2
24
  Object.defineProperty(exports, "__esModule", { value: true });
3
25
  exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
4
26
  const ent_1 = require("../core/ent");
5
27
  const schema_1 = require("../schema/schema");
6
28
  const action_1 = require("../action");
7
- const camel_case_1 = require("camel-case");
8
29
  const privacy_1 = require("../core/privacy");
9
30
  const executor_1 = require("./executor");
10
31
  const logger_1 = require("../core/logger");
32
+ const memoizee_1 = __importDefault(require("memoizee"));
33
+ const clause = __importStar(require("../core/clause"));
11
34
  var edgeDirection;
12
35
  (function (edgeDirection) {
13
36
  edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
@@ -63,6 +86,7 @@ class Orchestrator {
63
86
  this.viewer = options.viewer;
64
87
  this.actualOperation = this.options.operation;
65
88
  this.existingEnt = this.options.builder.existingEnt;
89
+ this.memoizedGetFields = (0, memoizee_1.default)(this.getFieldsInfo.bind(this));
66
90
  }
67
91
  addEdge(edge, op) {
68
92
  this.edgeSet.add(edge.edgeType);
@@ -155,6 +179,7 @@ class Orchestrator {
155
179
  key: this.options.key,
156
180
  loadEntOptions: this.options.loaderOptions,
157
181
  placeholderID: this.options.builder.placeholderID,
182
+ whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
158
183
  };
159
184
  if (this.logValues) {
160
185
  opts.fieldsToLog = this.logValues;
@@ -269,9 +294,28 @@ class Orchestrator {
269
294
  if (this.actualOperation !== action_1.WriteOperation.Insert) {
270
295
  return this.existingEnt;
271
296
  }
272
- const { editedData } = await this.getFieldsInfo();
297
+ const { editedData } = await this.memoizedGetFields();
273
298
  return this.getEntForPrivacyPolicyImpl(editedData);
274
299
  }
300
+ // this gets the fields that were explicitly set plus any default or transformed values
301
+ // mainly exists to get default fields e.g. default id to be used in triggers
302
+ // NOTE: this API may change in the future
303
+ // doesn't work to get ids for autoincrement keys
304
+ async getEditedData() {
305
+ const { editedData } = await this.memoizedGetFields();
306
+ return editedData;
307
+ }
308
+ /**
309
+ * @returns validated and formatted fields that would be written to the db
310
+ * throws an error if called before valid() or validX() has been called
311
+ */
312
+ getValidatedFields() {
313
+ if (this.validatedFields === null) {
314
+ throw new Error(`trying to call getValidatedFields before validating fields`);
315
+ }
316
+ return this.validatedFields;
317
+ }
318
+ // Note: this is memoized. call memoizedGetFields instead
275
319
  async getFieldsInfo() {
276
320
  const action = this.options.action;
277
321
  const builder = this.options.builder;
@@ -290,7 +334,7 @@ class Orchestrator {
290
334
  throw new Error(`existing ent required with operation ${this.actualOperation}`);
291
335
  }
292
336
  }
293
- const { schemaFields, editedData } = await this.getFieldsInfo();
337
+ const { schemaFields, editedData } = await this.memoizedGetFields();
294
338
  const action = this.options.action;
295
339
  const builder = this.options.builder;
296
340
  // this runs in following phases:
@@ -299,43 +343,81 @@ class Orchestrator {
299
343
  // * triggers
300
344
  // * validators
301
345
  let privacyPolicy = action?.getPrivacyPolicy();
346
+ let privacyError = null;
302
347
  if (privacyPolicy) {
303
- await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
348
+ try {
349
+ await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
350
+ }
351
+ catch (err) {
352
+ privacyError = err;
353
+ }
304
354
  }
305
355
  // have to run triggers which update fields first before field and other validators
306
356
  // so running this first to build things up
307
- let triggers = action?.triggers;
308
- if (triggers) {
309
- await this.triggers(action, builder, triggers);
357
+ if (action?.getTriggers) {
358
+ await this.triggers(action, builder, action.getTriggers());
359
+ }
360
+ let validators = [];
361
+ if (action?.getValidators) {
362
+ validators = action.getValidators();
310
363
  }
311
- let validators = action?.validators || [];
312
364
  // not ideal we're calling this twice. fix...
313
365
  // needed for now. may need to rewrite some of this?
314
366
  const editedFields2 = await this.options.editedFields();
315
- await Promise.all([
367
+ const [errors, _] = await Promise.all([
316
368
  this.formatAndValidateFields(schemaFields, editedFields2),
317
369
  this.validators(validators, action, builder),
318
370
  ]);
371
+ if (privacyError !== null) {
372
+ errors.unshift(privacyError);
373
+ }
374
+ return errors;
319
375
  }
320
376
  async triggers(action, builder, triggers) {
321
- await Promise.all(triggers.map(async (trigger) => {
322
- let ret = await trigger.changeset(builder, action.getInput());
323
- if (Array.isArray(ret)) {
324
- ret = await Promise.all(ret);
325
- }
326
- if (Array.isArray(ret)) {
327
- for (const v of ret) {
328
- if (typeof v === "object") {
329
- this.changesets.push(v);
330
- }
377
+ let groups = [];
378
+ let lastArray = 0;
379
+ let prevWasArray = false;
380
+ for (let i = 0; i < triggers.length; i++) {
381
+ let t = triggers[i];
382
+ if (Array.isArray(t)) {
383
+ if (!prevWasArray) {
384
+ // @ts-ignore
385
+ groups.push(triggers.slice(lastArray, i));
331
386
  }
387
+ groups.push(t);
388
+ prevWasArray = true;
389
+ lastArray++;
332
390
  }
333
- else if (ret) {
334
- this.changesets.push(ret);
391
+ else {
392
+ if (i === triggers.length - 1) {
393
+ // @ts-ignore
394
+ groups.push(triggers.slice(lastArray, i + 1));
395
+ }
396
+ prevWasArray = false;
335
397
  }
336
- }));
398
+ }
399
+ for (const triggers of groups) {
400
+ await Promise.all(triggers.map(async (trigger) => {
401
+ let ret = await trigger.changeset(builder, action.getInput());
402
+ if (Array.isArray(ret)) {
403
+ ret = await Promise.all(ret);
404
+ }
405
+ if (Array.isArray(ret)) {
406
+ for (const v of ret) {
407
+ if (typeof v === "object") {
408
+ this.changesets.push(v);
409
+ }
410
+ }
411
+ }
412
+ else if (ret) {
413
+ this.changesets.push(ret);
414
+ }
415
+ }));
416
+ }
337
417
  }
338
418
  async validators(validators, action, builder) {
419
+ // TODO need to catch errors and return it...
420
+ // don't need it initially since what we need this for doesn't have the errors
339
421
  let promises = [];
340
422
  validators.forEach((validator) => {
341
423
  let res = validator.validate(builder, action.getInput());
@@ -348,6 +430,12 @@ class Orchestrator {
348
430
  isBuilder(val) {
349
431
  return val.placeholderID !== undefined;
350
432
  }
433
+ getInputKey(k) {
434
+ return this.options.fieldInfo[k].inputKey;
435
+ }
436
+ getStorageKey(k) {
437
+ return this.options.fieldInfo[k].dbCol;
438
+ }
351
439
  async getFieldsWithDefaultValues(builder, schemaFields, editedFields, action) {
352
440
  let data = {};
353
441
  let defaultData = {};
@@ -357,24 +445,30 @@ class Orchestrator {
357
445
  // if action transformations. always do it
358
446
  // if disable transformations set, don't do schema transform and just do the right thing
359
447
  // else apply schema tranformation if it exists
360
- let transformed;
448
+ let transformed = null;
449
+ const sqlOp = this.getSQLStatementOperation();
361
450
  if (action?.transformWrite) {
362
451
  transformed = await action.transformWrite({
363
- viewer: builder.viewer,
364
- op: this.getSQLStatementOperation(),
452
+ builder,
453
+ input,
454
+ op: sqlOp,
365
455
  data: editedFields,
366
- existingEnt: this.existingEnt,
367
456
  });
368
457
  }
369
458
  else if (!this.disableTransformations) {
370
459
  transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
371
- viewer: builder.viewer,
372
- op: this.getSQLStatementOperation(),
460
+ builder,
461
+ input,
462
+ op: sqlOp,
373
463
  data: editedFields,
374
- existingEnt: this.existingEnt,
375
464
  });
376
465
  }
377
466
  if (transformed) {
467
+ if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
468
+ if (!transformed.existingEnt) {
469
+ throw new Error(`cannot transform an insert operation without providing an existing ent`);
470
+ }
471
+ }
378
472
  if (transformed.data) {
379
473
  updateInput = true;
380
474
  for (const k in transformed.data) {
@@ -386,17 +480,23 @@ class Orchestrator {
386
480
  if (field.format) {
387
481
  val = field.format(transformed.data[k]);
388
482
  }
389
- let dbKey = (0, schema_1.getStorageKey)(field, k);
390
- data[dbKey] = val;
391
- this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(k)] = val;
483
+ data[this.getStorageKey(k)] = val;
484
+ this.defaultFieldsByTSName[this.getInputKey(k)] = val;
392
485
  // hmm do we need this?
393
486
  // TODO how to do this for local tests?
394
487
  // this.defaultFieldsByFieldName[k] = val;
395
488
  }
396
489
  }
490
+ if (transformed.changeset) {
491
+ const ct = await transformed.changeset();
492
+ this.changesets.push(ct);
493
+ }
397
494
  this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
398
495
  if (transformed.existingEnt) {
496
+ // @ts-ignore
399
497
  this.existingEnt = transformed.existingEnt;
498
+ // modify existing ent in builder. it's readonly in generated ents but doesn't apply here
499
+ builder.existingEnt = transformed.existingEnt;
400
500
  }
401
501
  }
402
502
  // transforming before doing default fields so that we don't create a new id
@@ -404,7 +504,7 @@ class Orchestrator {
404
504
  for (const [fieldName, field] of schemaFields) {
405
505
  let value = editedFields.get(fieldName);
406
506
  let defaultValue = undefined;
407
- let dbKey = (0, schema_1.getStorageKey)(field, fieldName);
507
+ let dbKey = this.getStorageKey(fieldName);
408
508
  if (value === undefined) {
409
509
  if (this.actualOperation === action_1.WriteOperation.Insert) {
410
510
  if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
@@ -432,8 +532,7 @@ class Orchestrator {
432
532
  updateInput = true;
433
533
  defaultData[dbKey] = defaultValue;
434
534
  this.defaultFieldsByFieldName[fieldName] = defaultValue;
435
- // TODO related to #510. we need this logic to be consistent so do this all in TypeScript or get it from go somehow
436
- this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(fieldName)] = defaultValue;
535
+ this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
437
536
  }
438
537
  }
439
538
  // if there's data changing, add data
@@ -459,7 +558,7 @@ class Orchestrator {
459
558
  // now format and validate...
460
559
  if (value === null) {
461
560
  if (!field.nullable) {
462
- throw new Error(`field ${fieldName} set to null for non-nullable field`);
561
+ return new Error(`field ${fieldName} set to null for non-nullable field`);
463
562
  }
464
563
  }
465
564
  else if (value === undefined) {
@@ -470,14 +569,14 @@ class Orchestrator {
470
569
  // server default allowed
471
570
  field.serverDefault === undefined &&
472
571
  this.actualOperation === action_1.WriteOperation.Insert) {
473
- throw new Error(`required field ${fieldName} not set`);
572
+ return new Error(`required field ${fieldName} not set`);
474
573
  }
475
574
  }
476
575
  else if (this.isBuilder(value)) {
477
576
  if (field.valid) {
478
577
  const valid = await field.valid(value);
479
578
  if (!valid) {
480
- throw new Error(`invalid field ${fieldName} with value ${value}`);
579
+ return new Error(`invalid field ${fieldName} with value ${value}`);
481
580
  }
482
581
  }
483
582
  // keep track of dependencies to resolve
@@ -490,20 +589,20 @@ class Orchestrator {
490
589
  // TODO this could be async. handle this better
491
590
  const valid = await field.valid(value);
492
591
  if (!valid) {
493
- throw new Error(`invalid field ${fieldName} with value ${value}`);
592
+ return new Error(`invalid field ${fieldName} with value ${value}`);
494
593
  }
495
594
  }
496
595
  if (field.format) {
497
- // TODO this could be async e.g. password. handle this better
498
- value = await Promise.resolve(field.format(value));
596
+ value = await field.format(value);
499
597
  }
500
598
  }
501
599
  return value;
502
600
  }
503
601
  async formatAndValidateFields(schemaFields, editedFields) {
602
+ const errors = [];
504
603
  const op = this.actualOperation;
505
604
  if (op === action_1.WriteOperation.Delete) {
506
- return;
605
+ return [];
507
606
  }
508
607
  // build up data to be saved...
509
608
  let data = {};
@@ -514,8 +613,14 @@ class Orchestrator {
514
613
  // null allowed
515
614
  value = this.defaultFieldsByFieldName[fieldName];
516
615
  }
517
- let dbKey = (0, schema_1.getStorageKey)(field, fieldName);
518
- value = await this.transformFieldValue(fieldName, field, dbKey, value);
616
+ let dbKey = this.getStorageKey(fieldName);
617
+ let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
618
+ if (ret instanceof Error) {
619
+ errors.push(ret);
620
+ }
621
+ else {
622
+ value = ret;
623
+ }
519
624
  if (value !== undefined) {
520
625
  data[dbKey] = value;
521
626
  logValues[dbKey] = field.logValue(value);
@@ -527,29 +632,48 @@ class Orchestrator {
527
632
  for (const fieldName in this.defaultFieldsByFieldName) {
528
633
  const defaultValue = this.defaultFieldsByFieldName[fieldName];
529
634
  let field = schemaFields.get(fieldName);
530
- let dbKey = (0, schema_1.getStorageKey)(field, fieldName);
635
+ let dbKey = this.getStorageKey(fieldName);
531
636
  // no value, let's just default
532
637
  if (data[dbKey] === undefined) {
533
- const value = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
534
- data[dbKey] = value;
535
- logValues[dbKey] = field.logValue(value);
638
+ const ret = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
639
+ if (ret instanceof Error) {
640
+ errors.push(ret);
641
+ }
642
+ else {
643
+ data[dbKey] = ret;
644
+ logValues[dbKey] = field.logValue(ret);
645
+ }
536
646
  }
537
647
  }
538
648
  }
539
649
  this.validatedFields = data;
540
650
  this.logValues = logValues;
651
+ return errors;
541
652
  }
542
653
  async valid() {
543
- try {
544
- await this.validate();
545
- }
546
- catch (e) {
547
- (0, logger_1.log)("error", e);
654
+ const errors = await this.validate();
655
+ if (errors.length) {
656
+ errors.map((err) => (0, logger_1.log)("error", err));
548
657
  return false;
549
658
  }
550
659
  return true;
551
660
  }
552
661
  async validX() {
662
+ const errors = await this.validate();
663
+ if (errors.length) {
664
+ // just throw the first one...
665
+ // TODO we should ideally throw all of them
666
+ throw errors[0];
667
+ }
668
+ }
669
+ /**
670
+ * @experimental API that's not guaranteed to remain in the future which returns
671
+ * a list of errors encountered
672
+ * 0 errors indicates valid
673
+ * NOTE that this currently doesn't catch errors returned by validators().
674
+ * If those throws, this still throws and doesn't return them
675
+ */
676
+ async validWithErrors() {
553
677
  return this.validate();
554
678
  }
555
679
  async build() {
@@ -564,7 +688,7 @@ class Orchestrator {
564
688
  if (!action || !action.viewerForEntLoad) {
565
689
  return this.options.viewer;
566
690
  }
567
- return action.viewerForEntLoad(data);
691
+ return action.viewerForEntLoad(data, action.builder.viewer.context);
568
692
  }
569
693
  async returnedRow() {
570
694
  if (this.mainOp && this.mainOp.returnedRow) {
@@ -599,6 +723,9 @@ class Orchestrator {
599
723
  }
600
724
  }
601
725
  exports.Orchestrator = Orchestrator;
726
+ function randomNum() {
727
+ return Math.random().toString(10).substring(2);
728
+ }
602
729
  class EntChangeset {
603
730
  constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
604
731
  this.viewer = viewer;
@@ -609,6 +736,9 @@ class EntChangeset {
609
736
  this.changesets = changesets;
610
737
  this.options = options;
611
738
  }
739
+ static changesetFrom(builder, ops) {
740
+ return new EntChangeset(builder.viewer, `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, builder.ent, ops);
741
+ }
612
742
  executor() {
613
743
  if (this._executor) {
614
744
  return this._executor;
@@ -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
@@ -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[];
@@ -30,27 +38,27 @@ interface queryOptions {
30
38
  clause: clause.Clause;
31
39
  orderby?: string;
32
40
  }
33
- export interface Context {
34
- getViewer(): Viewer;
41
+ export interface Context<TViewer extends Viewer = Viewer> {
42
+ getViewer(): TViewer;
35
43
  cache?: cache;
36
44
  }
37
- export interface Viewer {
38
- viewerID: ID | null;
39
- viewer: () => Promise<Ent | null>;
45
+ export interface Viewer<TEnt extends any = Ent<any> | null, TID extends any = ID | null> {
46
+ viewerID: TID;
47
+ viewer: () => Promise<TEnt>;
40
48
  instanceKey: () => string;
41
- context?: Context;
49
+ context?: Context<any>;
42
50
  }
43
- export interface Ent {
51
+ export interface Ent<TViewer extends Viewer = Viewer> {
44
52
  id: ID;
45
- viewer: Viewer;
46
- privacyPolicy: PrivacyPolicy<this>;
53
+ viewer: TViewer;
54
+ getPrivacyPolicy(): PrivacyPolicy<this, TViewer>;
47
55
  nodeType: string;
48
56
  }
49
57
  export declare type Data = {
50
58
  [key: string]: any;
51
59
  };
52
- export interface EntConstructor<T extends Ent> {
53
- new (viewer: Viewer, data: Data): T;
60
+ export interface EntConstructor<TEnt extends Ent, TViewer extends Viewer = Viewer> {
61
+ new (viewer: TViewer, data: Data): TEnt;
54
62
  }
55
63
  export declare type ID = string | number;
56
64
  export interface DataOptions {
@@ -72,6 +80,7 @@ export interface QueryDataOptions {
72
80
  orderby?: string;
73
81
  groupby?: string;
74
82
  limit?: number;
83
+ disableTransformations?: boolean;
75
84
  }
76
85
  export interface LoadRowOptions extends QueryableDataOptions {
77
86
  }
@@ -82,17 +91,24 @@ export interface CreateRowOptions extends DataOptions {
82
91
  fieldsToLog?: Data;
83
92
  }
84
93
  export interface EditRowOptions extends CreateRowOptions {
85
- key: string;
94
+ whereClause: clause.Clause;
86
95
  }
87
- interface LoadableEntOptions<T extends Ent> {
88
- loaderFactory: LoaderFactory<any, Data | null>;
89
- ent: EntConstructor<T>;
96
+ interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> {
97
+ loaderFactory: LoaderFactoryWithOptions;
98
+ ent: EntConstructor<TEnt, TViewer>;
99
+ }
100
+ interface LoaderFactoryWithOptions extends LoaderFactoryWithLoaderMany<any, Data | null> {
101
+ options?: SelectDataOptions;
90
102
  }
91
- export interface LoadEntOptions<T extends Ent> extends LoadableEntOptions<T>, SelectBaseDataOptions {
103
+ export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends LoadableEntOptions<TEnt, TViewer>, SelectBaseDataOptions {
92
104
  fieldPrivacy?: Map<string, PrivacyPolicy>;
93
105
  }
94
- export interface LoadCustomEntOptions<T extends Ent> extends SelectBaseDataOptions {
95
- ent: EntConstructor<T>;
106
+ export interface SelectCustomDataOptions extends SelectBaseDataOptions {
107
+ loaderFactory: LoaderFactoryWithOptions;
108
+ prime?: boolean;
109
+ }
110
+ export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends SelectCustomDataOptions {
111
+ ent: EntConstructor<TEnt, TViewer>;
96
112
  fieldPrivacy?: Map<string, PrivacyPolicy>;
97
113
  }
98
114
  export interface LoaderInfo {
@@ -121,10 +137,10 @@ export declare function Allow(): PrivacyResult;
121
137
  export declare function Skip(): PrivacyResult;
122
138
  export declare function Deny(): PrivacyResult;
123
139
  export declare function DenyWithReason(e: PrivacyError | string): PrivacyResult;
124
- export interface PrivacyPolicyRule<TEnt extends Ent = Ent> {
125
- apply(v: Viewer, ent?: TEnt): Promise<PrivacyResult>;
140
+ export interface PrivacyPolicyRule<TEnt extends Ent = Ent, TViewer = Viewer> {
141
+ apply(v: TViewer, ent?: TEnt): Promise<PrivacyResult>;
126
142
  }
127
- export interface PrivacyPolicy<TEnt extends Ent = Ent> {
128
- rules: PrivacyPolicyRule<TEnt>[];
143
+ export interface PrivacyPolicy<TEnt extends Ent = Ent, TViewer = Viewer> {
144
+ rules: PrivacyPolicyRule<TEnt, TViewer>[];
129
145
  }
130
146
  export {};