jinaga-server 2.3.1 → 2.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinaga-server",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "Application-agnostic back end for web applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "express": "^4.18.1",
46
- "jinaga": "^5.3.0",
46
+ "jinaga": "^5.3.1",
47
47
  "node-forge": "^1.3.1",
48
48
  "passport": "^0.6.0",
49
49
  "pg": "^8.7.1"
@@ -6,13 +6,13 @@ export class AuthenticationDevice implements Authentication {
6
6
  private authorizationEngine: AuthorizationEngine | null;
7
7
 
8
8
  constructor(
9
- inner: Storage,
9
+ private store: Storage,
10
10
  private keystore: Keystore,
11
11
  authorizationRules: AuthorizationRules | null,
12
12
  private localDeviceIdentity: UserIdentity
13
13
  ) {
14
14
  this.authorizationEngine = authorizationRules &&
15
- new AuthorizationEngine(authorizationRules, inner);
15
+ new AuthorizationEngine(authorizationRules, store);
16
16
  }
17
17
 
18
18
  async login(): Promise<LoginResponse> {
@@ -20,7 +20,15 @@ export class AuthenticationDevice implements Authentication {
20
20
  }
21
21
 
22
22
  async local(): Promise<FactRecord> {
23
- return await this.keystore.getOrCreateDeviceFact(this.localDeviceIdentity);
23
+ const deviceFact = await this.keystore.getOrCreateDeviceFact(this.localDeviceIdentity);
24
+ const envelopes = [
25
+ <FactEnvelope>{
26
+ fact: deviceFact,
27
+ signatures: []
28
+ }
29
+ ];
30
+ await this.store.save(envelopes);
31
+ return deviceFact;
24
32
  }
25
33
 
26
34
  async authorize(envelopes: FactEnvelope[]): Promise<FactEnvelope[]> {
@@ -12,6 +12,7 @@ import {
12
12
  LoadMessage,
13
13
  LoadResponse,
14
14
  ProfileMessage,
15
+ ProjectedResult,
15
16
  QueryMessage,
16
17
  QueryResponse,
17
18
  SaveMessage,
@@ -274,7 +275,7 @@ export class HttpRouter {
274
275
 
275
276
  const userIdentity = serializeUserIdentity(user);
276
277
  const results = await this.authorization.read(userIdentity, start, specification);
277
- return results;
278
+ return extractResults(results);
278
279
  }
279
280
 
280
281
  private async write(user: RequestUser, input: string): Promise<void> {
@@ -383,3 +384,19 @@ function urlSafeHash(feed: Feed): string {
383
384
  const base64 = computeObjectHash(feed);
384
385
  return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
385
386
  }
387
+
388
+ function extractResults(obj: any): any {
389
+ if (Array.isArray(obj)) {
390
+ const projectedResults: ProjectedResult[] = obj;
391
+ return projectedResults.map(r => extractResults(r.result));
392
+ }
393
+ else if (typeof obj === "object") {
394
+ return Object.keys(obj).reduce((acc, key) => ({
395
+ ...acc,
396
+ [key]: extractResults(obj[key])
397
+ }), {});
398
+ }
399
+ else {
400
+ return obj;
401
+ }
402
+ }
@@ -16,6 +16,7 @@ import {
16
16
  getAllRolesFromFeed,
17
17
  Join,
18
18
  PredecessorCollection,
19
+ ProjectedResult,
19
20
  PropertyCondition,
20
21
  Query,
21
22
  Specification,
@@ -51,7 +52,7 @@ import {
51
52
  PublicKeyMap,
52
53
  RoleMap
53
54
  } from "./maps";
54
- import { ResultSetTree, resultSqlFromSpecification, SqlQueryTree } from "./specification-result-sql";
55
+ import { ResultSetFact, ResultSetRow, ResultSetTree, resultSqlFromSpecification, SqlQueryTree } from "./specification-result-sql";
55
56
  import { sqlFromFeed } from "./specification-sql";
56
57
  import { sqlFromSteps } from "./sql";
57
58
 
@@ -232,7 +233,7 @@ export class PostgresStore implements Storage {
232
233
  }
233
234
  }
234
235
 
235
- async read(start: FactReference[], specification: Specification): Promise<any[]> {
236
+ async read(start: FactReference[], specification: Specification): Promise<ProjectedResult[]> {
236
237
  const factTypes = await this.loadFactTypesFromSpecification(specification);
237
238
  const roleMap = await this.loadRolesFromSpecification(specification, factTypes);
238
239
 
@@ -250,7 +251,14 @@ export class PostgresStore implements Storage {
250
251
  const resultSets = await this.connectionFactory.with(async (connection) => {
251
252
  return await executeQueryTree(sqlQueryTree, connection);
252
253
  });
253
- return composer.compose(resultSets);
254
+
255
+ // Find the references for fact projections
256
+ const factReferences: FactReference[] = composer.findFactReferences(resultSets);
257
+
258
+ // Load the references into a fact tree
259
+ const factRecords = await this.load(factReferences);
260
+
261
+ return composer.compose(resultSets, factRecords);
254
262
  }
255
263
 
256
264
  async feed(feed: Feed, bookmark: string): Promise<FactFeed> {
@@ -478,17 +486,31 @@ export class PostgresStore implements Storage {
478
486
  }
479
487
 
480
488
  getMruDate(specificationHash: string): Promise<Date | null> {
481
- throw new Error("Method not implemented.");
489
+ return Promise.resolve(null);
482
490
  }
483
491
 
484
492
  setMruDate(specificationHash: string, mruDate: Date): Promise<void> {
485
- throw new Error("Method not implemented.");
493
+ return Promise.resolve();
486
494
  }
487
495
  }
488
496
 
489
497
  async function executeQueryTree(sqlQueryTree: SqlQueryTree, connection: PoolClient): Promise<ResultSetTree> {
490
498
  const sqlQuery = sqlQueryTree.sqlQuery;
491
- const { rows } = await connection.query(sqlQuery.sql, sqlQuery.parameters);
499
+ const { rows: dataRows } = await connection.query(sqlQuery.sql, sqlQuery.parameters);
500
+ const rows: ResultSetRow[] = dataRows.map(dataRow => {
501
+ const row = sqlQuery.labels.reduce((acc, label) => {
502
+ const fact: ResultSetFact = {
503
+ hash: dataRow[`hash${label.index}`],
504
+ factId: dataRow[`id${label.index}`],
505
+ data: dataRow[`data${label.index}`]
506
+ };
507
+ return {
508
+ ...acc,
509
+ [label.index]: fact
510
+ };
511
+ }, {} as ResultSetRow);
512
+ return row;
513
+ });
492
514
  const resultSets: ResultSetTree = {
493
515
  resultSet: rows,
494
516
  childResultSets: []
@@ -1,13 +1,18 @@
1
1
  import {
2
2
  ComponentProjection,
3
3
  FactProjection,
4
+ FactRecord,
4
5
  FactReference,
5
6
  FieldProjection,
6
7
  HashProjection,
8
+ hydrateFromTree,
7
9
  Label,
8
10
  Match,
9
11
  PathCondition,
12
+ PredecessorCollection,
13
+ ProjectedResult,
10
14
  Projection,
15
+ ReferencesByName,
11
16
  SingularProjection,
12
17
  Specification,
13
18
  SpecificationProjection
@@ -18,6 +23,7 @@ import { ensureGetFactTypeId, FactTypeMap, getFactTypeId, getRoleId, RoleMap } f
18
23
  interface SpecificationLabel {
19
24
  name: string;
20
25
  index: number;
26
+ type: string;
21
27
  }
22
28
  interface FactDescription {
23
29
  type: string;
@@ -43,6 +49,7 @@ interface SpecificationSqlQuery {
43
49
  };
44
50
  interface InputDescription {
45
51
  label: string;
52
+ type: string;
46
53
  factIndex: number;
47
54
  factTypeParameter: number;
48
55
  factHashParameter: number;
@@ -100,6 +107,7 @@ class QueryDescription {
100
107
  ]
101
108
  const input: InputDescription = {
102
109
  label: label.name,
110
+ type: label.type,
103
111
  factIndex,
104
112
  factTypeParameter,
105
113
  factHashParameter
@@ -218,8 +226,9 @@ class QueryDescription {
218
226
  }
219
227
 
220
228
  generateResultSqlQuery(schema: string): SpecificationSqlQuery {
221
- const columns = this.outputs
222
- .map(output => `f${output.factIndex}.hash as hash${output.factIndex}, f${output.factIndex}.fact_id as id${output.factIndex}, f${output.factIndex}.data as data${output.factIndex}`)
229
+ const allLabels = [ ...this.inputs, ...this.outputs ];
230
+ const columns = allLabels
231
+ .map(label => `f${label.factIndex}.hash as hash${label.factIndex}, f${label.factIndex}.fact_id as id${label.factIndex}, f${label.factIndex}.data as data${label.factIndex}`)
223
232
  .join(", ");
224
233
  const firstEdge = this.edges[0];
225
234
  const predecessorFact = this.inputs.find(i => i.factIndex === firstEdge.predecessorFactIndex);
@@ -240,10 +249,10 @@ class QueryDescription {
240
249
  return {
241
250
  sql,
242
251
  parameters: this.parameters,
243
- labels: this.outputs.map(output => ({
244
- name: output.label,
245
- type: output.type,
246
- index: output.factIndex
252
+ labels: allLabels.map(label => ({
253
+ name: label.label,
254
+ type: label.type,
255
+ index: label.factIndex
247
256
  })),
248
257
  bookmark: "[]"
249
258
  };
@@ -420,6 +429,7 @@ interface ResultDescription {
420
429
  queryDescription: QueryDescription;
421
430
  resultProjection: Projection;
422
431
  childResultDescriptions: NamedResultDescription[];
432
+ givenTuple: ReferencesByName;
423
433
  }
424
434
 
425
435
  interface NamedResultDescription extends ResultDescription {
@@ -428,12 +438,13 @@ interface NamedResultDescription extends ResultDescription {
428
438
 
429
439
  interface IdentifiedResults {
430
440
  factIds: number[];
441
+ tuple: ReferencesByName;
431
442
  result: any;
432
443
  }
433
444
 
434
445
  interface ChildResults {
435
446
  parentFactIds: number[];
436
- results: any[];
447
+ results: ProjectedResult[];
437
448
  }
438
449
 
439
450
  export interface SqlQueryTree {
@@ -445,8 +456,23 @@ interface NamedSqlQueryTree extends SqlQueryTree {
445
456
  name: string;
446
457
  }
447
458
 
459
+ export interface ResultSetData {
460
+ fields: { [field: string]: any };
461
+ predecessors: PredecessorCollection;
462
+ }
463
+
464
+ export interface ResultSetFact {
465
+ hash: string;
466
+ factId: number;
467
+ data: ResultSetData;
468
+ }
469
+
470
+ export interface ResultSetRow {
471
+ [factIndex: number]: ResultSetFact;
472
+ }
473
+
448
474
  export interface ResultSetTree {
449
- resultSet: any[];
475
+ resultSet: ResultSetRow[];
450
476
  childResultSets: NamedResultSetTree[];
451
477
  }
452
478
 
@@ -459,6 +485,7 @@ export class ResultComposer {
459
485
  private readonly sqlQuery: SpecificationSqlQuery,
460
486
  private readonly resultProjection: Projection,
461
487
  private readonly parentFactIdLength: number,
488
+ private readonly givenTuple: ReferencesByName,
462
489
  private readonly childResultComposers: NamedResultComposer[]
463
490
  ) { }
464
491
 
@@ -476,10 +503,56 @@ export class ResultComposer {
476
503
  };
477
504
  }
478
505
 
479
- public compose(
506
+ public findFactReferences(
480
507
  resultSets: ResultSetTree
481
- ): any[] {
482
- const childResults = this.composeInternal(resultSets);
508
+ ): FactReference[] {
509
+ let factReferences: FactReference[] = [];
510
+
511
+ // Add the fact references for selected facts.
512
+ const projection = this.resultProjection;
513
+ if (projection.type === 'fact') {
514
+ const projectionFactReferences = this.factReferencesForProjection(projection, resultSets);
515
+ factReferences = factReferences.concat(projectionFactReferences);
516
+ }
517
+
518
+ // Add the fact references for composites.
519
+ if (projection.type === 'composite') {
520
+ for (const component of projection.components) {
521
+ if (component.type === 'fact') {
522
+ const projectionFactReferences = this.factReferencesForProjection(component, resultSets);
523
+ factReferences = factReferences.concat(projectionFactReferences);
524
+ }
525
+ }
526
+ }
527
+
528
+ // Recursively add the fact references for child result sets.
529
+ for (const childResultSet of resultSets.childResultSets) {
530
+ const childResultComposer = this.childResultComposers.find(c =>
531
+ c.name === childResultSet.name);
532
+ if (childResultComposer) {
533
+ const childFactReferences = childResultComposer.resultComposer
534
+ .findFactReferences(childResultSet);
535
+ factReferences = factReferences.concat(childFactReferences);
536
+ }
537
+ }
538
+
539
+ return factReferences;
540
+ }
541
+
542
+ private factReferencesForProjection(projection: FactProjection, resultSets: ResultSetTree) {
543
+ const label = this.getLabel(projection.label);
544
+ const factReferences = resultSets.resultSet.map(row => ({
545
+ type: label.type,
546
+ hash: row[label.index].hash
547
+ }));
548
+ return factReferences;
549
+ }
550
+
551
+ public compose(
552
+ resultSets: ResultSetTree,
553
+ factRecords: FactRecord[]
554
+ ): ProjectedResult[] {
555
+ const childResults = this.composeInternal(resultSets, factRecords);
483
556
  if (childResults.length === 0) {
484
557
  return [];
485
558
  }
@@ -489,7 +562,8 @@ export class ResultComposer {
489
562
  }
490
563
 
491
564
  private composeInternal(
492
- resultSets: ResultSetTree
565
+ resultSets: ResultSetTree,
566
+ factRecords: FactRecord[]
493
567
  ): ChildResults[] {
494
568
  const rows = resultSets.resultSet;
495
569
  if (rows.length === 0) {
@@ -499,7 +573,8 @@ export class ResultComposer {
499
573
  // Project all rows and their identifiers
500
574
  const identifiedResults: IdentifiedResults[] = rows.map(row => ({
501
575
  factIds: this.identifierOf(row),
502
- result: this.projectionOf(row)
576
+ tuple: this.tupleOf(row),
577
+ result: this.projectionOf(row, factRecords)
503
578
  }));
504
579
 
505
580
  // Compose child results
@@ -510,7 +585,7 @@ export class ResultComposer {
510
585
  const availableNames = resultSets.childResultSets.map(childResultSet => childResultSet.name);
511
586
  throw new Error(`Child result set ${childResultComposer.name} not found in (${availableNames.join(", ")})`);
512
587
  }
513
- const composedResults = childResultComposer.resultComposer.composeInternal(childResultSet);
588
+ const composedResults = childResultComposer.resultComposer.composeInternal(childResultSet, factRecords);
514
589
 
515
590
  // Add the child results
516
591
  let index = 0;
@@ -530,11 +605,17 @@ export class ResultComposer {
530
605
  // Group the results by their parent identifiers
531
606
  const childResults: ChildResults[] = [];
532
607
  let parentFactIds: number[] = identifiedResults[0].factIds.slice(0, this.parentFactIdLength);
533
- let results: any[] = [ identifiedResults[0].result ];
608
+ let results: ProjectedResult[] = [{
609
+ tuple: identifiedResults[0].tuple,
610
+ result: identifiedResults[0].result
611
+ }];
534
612
  for (const identifiedResult of identifiedResults.slice(1)) {
535
613
  const nextParentFactIds = identifiedResult.factIds.slice(0, this.parentFactIdLength);
536
614
  if (idsEqual(nextParentFactIds, parentFactIds)) {
537
- results.push(identifiedResult.result);
615
+ results.push({
616
+ tuple: identifiedResult.tuple,
617
+ result: identifiedResult.result
618
+ });
538
619
  }
539
620
  else {
540
621
  childResults.push({
@@ -542,7 +623,10 @@ export class ResultComposer {
542
623
  results
543
624
  });
544
625
  parentFactIds = nextParentFactIds;
545
- results = [ identifiedResult.result ];
626
+ results = [{
627
+ tuple: identifiedResult.tuple,
628
+ result: identifiedResult.result
629
+ }];
546
630
  }
547
631
  }
548
632
  childResults.push({
@@ -552,11 +636,22 @@ export class ResultComposer {
552
636
  return childResults;
553
637
  }
554
638
 
555
- private identifierOf(row: any): number[] {
556
- return this.sqlQuery.labels.map(label => row[`id${label.index}`]);
639
+ private identifierOf(row: ResultSetRow): number[] {
640
+ return this.sqlQuery.labels.map(label => row[label.index].factId);
557
641
  }
558
642
 
559
- private projectionOf(row: any): any {
643
+ private tupleOf(row: ResultSetRow): ReferencesByName {
644
+ const tuple = this.sqlQuery.labels.reduce((acc, label) => ({
645
+ ...acc,
646
+ [label.name]: {
647
+ type: label.type,
648
+ hash: row[label.index].hash,
649
+ }
650
+ }), this.givenTuple);
651
+ return tuple;
652
+ }
653
+
654
+ private projectionOf(row: ResultSetRow, factRecords: FactRecord[]): any {
560
655
  if (this.resultProjection.type === "field") {
561
656
  return this.fieldValue(this.resultProjection, row);
562
657
  }
@@ -568,13 +663,13 @@ export class ResultComposer {
568
663
  .slice(this.parentFactIdLength)
569
664
  .reduce((acc, label) => ({
570
665
  ...acc,
571
- [label.name]: row[`data${label.index}`].fields
666
+ [label.name]: row[label.index].data.fields
572
667
  }), {})
573
668
  }
574
669
  else {
575
670
  return this.resultProjection.components.reduce((acc, component) => ({
576
671
  ...acc,
577
- [component.name]: this.elementValue(component, row)
672
+ [component.name]: this.elementValue(component, row, factRecords)
578
673
  }), {});
579
674
  }
580
675
  }
@@ -582,7 +677,7 @@ export class ResultComposer {
582
677
  return this.hashValue(this.resultProjection, row);
583
678
  }
584
679
  else if (this.resultProjection.type === "fact") {
585
- return this.factValue(this.resultProjection, row);
680
+ return this.factValue(this.resultProjection, row, factRecords);
586
681
  }
587
682
  else {
588
683
  const _exhaustiveCheck: never = this.resultProjection;
@@ -590,18 +685,23 @@ export class ResultComposer {
590
685
  }
591
686
  }
592
687
 
593
- private elementValue(projection: ComponentProjection, row: any): any {
688
+ private elementValue(projection: ComponentProjection, row: ResultSetRow, factRecords: FactRecord[]): any {
594
689
  if (projection.type === "field") {
595
690
  const label = this.getLabel(projection.label);
596
- return row[`data${label.index}`].fields[projection.field];
691
+ return row[label.index].data.fields[projection.field];
597
692
  }
598
693
  else if (projection.type === "hash") {
599
694
  const label = this.getLabel(projection.label);
600
- return row[`hash${label.index}`];
695
+ return row[label.index].hash;
601
696
  }
602
697
  else if (projection.type === "fact") {
603
698
  const label = this.getLabel(projection.label);
604
- return row[`data${label.index}`].fields;
699
+ const factReference = {
700
+ type: label.type,
701
+ hash: row[label.index].hash,
702
+ };
703
+ const [fact] = hydrateFromTree([factReference], factRecords);
704
+ return fact;
605
705
  }
606
706
  else if (projection.type === "specification") {
607
707
  // This should have already been taken care of
@@ -613,19 +713,24 @@ export class ResultComposer {
613
713
  }
614
714
  }
615
715
 
616
- private fieldValue(projection: FieldProjection, row: any): any {
716
+ private fieldValue(projection: FieldProjection, row: ResultSetRow): any {
617
717
  const label = this.getLabel(projection.label);
618
- return row[`data${label.index}`].fields[projection.field];
718
+ return row[label.index].data.fields[projection.field];
619
719
  }
620
720
 
621
- private hashValue(projection: HashProjection, row: any): any {
721
+ private hashValue(projection: HashProjection, row: ResultSetRow): any {
622
722
  const label = this.getLabel(projection.label);
623
- return row[`hash${label.index}`];
723
+ return row[label.index].hash;
624
724
  }
625
725
 
626
- private factValue(projection: FactProjection, row: any): any {
726
+ private factValue(projection: FactProjection, row: ResultSetRow, factRecords: FactRecord[]): any {
627
727
  const label = this.getLabel(projection.label);
628
- return row[`data${label.index}`].fields;
728
+ const factReference: FactReference = {
729
+ type: label.type,
730
+ hash: row[label.index].hash
731
+ };
732
+ const [fact] = hydrateFromTree([factReference], factRecords);
733
+ return fact;
629
734
  }
630
735
 
631
736
  private getLabel(name: string) {
@@ -669,6 +774,10 @@ class ResultDescriptionBuilder {
669
774
  }
670
775
 
671
776
  private createResultDescription(queryDescription: QueryDescription, given: Label[], start: FactReference[], matches: Match[], projection: Projection, knownFacts: FactByLabel, path: number[]): ResultDescription {
777
+ const givenTuple = given.reduce((acc, label, index) => ({
778
+ ...acc,
779
+ [label.name]: start[index]
780
+ }), {} as ReferencesByName);
672
781
  ({ queryDescription, knownFacts } = this.addEdges(queryDescription, given, start, knownFacts, path, matches));
673
782
  if (!queryDescription.isSatisfiable()) {
674
783
  // Abort the branch if the query is not satisfiable
@@ -678,7 +787,8 @@ class ResultDescriptionBuilder {
678
787
  type: "composite",
679
788
  components: []
680
789
  },
681
- childResultDescriptions: []
790
+ childResultDescriptions: [],
791
+ givenTuple
682
792
  }
683
793
  }
684
794
  const childResultDescriptions: NamedResultDescription[] = [];
@@ -700,14 +810,16 @@ class ResultDescriptionBuilder {
700
810
  type: "composite",
701
811
  components: singularProjections
702
812
  },
703
- childResultDescriptions
813
+ childResultDescriptions,
814
+ givenTuple
704
815
  };
705
816
  }
706
817
  else {
707
818
  return {
708
819
  queryDescription,
709
820
  resultProjection: projection,
710
- childResultDescriptions: []
821
+ childResultDescriptions: [],
822
+ givenTuple
711
823
  }
712
824
  }
713
825
  }
@@ -887,5 +999,5 @@ function createResultComposer(description: ResultDescription, parentFactIdLength
887
999
  name: child.name,
888
1000
  resultComposer: createResultComposer(child, description.queryDescription.outputLength(), schema)
889
1001
  }));
890
- return new ResultComposer(sqlQuery, resultProjection, parentFactIdLength, childResultComposers);
1002
+ return new ResultComposer(sqlQuery, resultProjection, parentFactIdLength, description.givenTuple, childResultComposers);
891
1003
  }
@@ -25,8 +25,6 @@ interface SpecificationSqlQuery {
25
25
 
26
26
  interface InputDescription {
27
27
  factIndex: number;
28
- factTypeId: number;
29
- factHash: string;
30
28
  factTypeParameter: number;
31
29
  factHashParameter: number;
32
30
  }
@@ -42,7 +40,6 @@ interface EdgeDescription {
42
40
  }
43
41
  interface NotExistsConditionDescription {
44
42
  edges: EdgeDescription[];
45
- notExistsConditions: NotExistsConditionDescription[];
46
43
  }
47
44
  class QueryDescription {
48
45
  constructor(
@@ -284,9 +281,7 @@ class DescriptionBuilder {
284
281
  const factHashParameter = addParameter(input.factHash);
285
282
  return {
286
283
  factIndex: input.factIndex,
287
- factTypeId,
288
284
  factTypeParameter,
289
- factHash: input.factHash,
290
285
  factHashParameter
291
286
  };
292
287
  }
@@ -332,13 +327,9 @@ class DescriptionBuilder {
332
327
  const edges = condition.edges.map(edge =>
333
328
  this.buildEdgeDescription(feed, edge, addParameter)
334
329
  );
335
- const notExistsConditions = condition.notExistsConditions.map(condition =>
336
- this.buildNotExistsConditionDescription(feed, condition, addParameter)
337
- );
338
330
 
339
331
  return {
340
- edges,
341
- notExistsConditions
332
+ edges
342
333
  }
343
334
  }
344
335
  }