@aws-amplify/datastore 3.12.11 → 3.12.13-unstable.1

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 (106) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/aws-amplify-datastore.js +1853 -964
  3. package/dist/aws-amplify-datastore.js.map +1 -1
  4. package/dist/aws-amplify-datastore.min.js +7 -7
  5. package/dist/aws-amplify-datastore.min.js.map +1 -1
  6. package/lib/datastore/datastore.d.ts +13 -16
  7. package/lib/datastore/datastore.js +130 -63
  8. package/lib/datastore/datastore.js.map +1 -1
  9. package/lib/index.d.ts +3 -19
  10. package/lib/predicates/index.d.ts +3 -2
  11. package/lib/predicates/index.js +12 -2
  12. package/lib/predicates/index.js.map +1 -1
  13. package/lib/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
  14. package/lib/storage/adapter/AsyncStorageAdapter.js +354 -203
  15. package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  16. package/lib/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
  17. package/lib/storage/adapter/AsyncStorageDatabase.js +65 -28
  18. package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  19. package/lib/storage/adapter/IndexedDBAdapter.d.ts +5 -4
  20. package/lib/storage/adapter/IndexedDBAdapter.js +389 -267
  21. package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
  22. package/lib/storage/adapter/index.d.ts +1 -1
  23. package/lib/storage/storage.d.ts +1 -1
  24. package/lib/storage/storage.js +92 -27
  25. package/lib/storage/storage.js.map +1 -1
  26. package/lib/sync/index.d.ts +21 -4
  27. package/lib/sync/index.js +13 -11
  28. package/lib/sync/index.js.map +1 -1
  29. package/lib/sync/merger.d.ts +3 -3
  30. package/lib/sync/merger.js +7 -6
  31. package/lib/sync/merger.js.map +1 -1
  32. package/lib/sync/outbox.d.ts +2 -2
  33. package/lib/sync/outbox.js +11 -9
  34. package/lib/sync/outbox.js.map +1 -1
  35. package/lib/sync/processors/mutation.js +60 -42
  36. package/lib/sync/processors/mutation.js.map +1 -1
  37. package/lib/sync/processors/subscription.js.map +1 -1
  38. package/lib/sync/processors/sync.js.map +1 -1
  39. package/lib/sync/utils.d.ts +3 -2
  40. package/lib/sync/utils.js +61 -8
  41. package/lib/sync/utils.js.map +1 -1
  42. package/lib/types.d.ts +64 -25
  43. package/lib/types.js +10 -1
  44. package/lib/types.js.map +1 -1
  45. package/lib/util.d.ts +56 -24
  46. package/lib/util.js +334 -170
  47. package/lib/util.js.map +1 -1
  48. package/lib-esm/datastore/datastore.d.ts +13 -16
  49. package/lib-esm/datastore/datastore.js +132 -65
  50. package/lib-esm/datastore/datastore.js.map +1 -1
  51. package/lib-esm/index.d.ts +3 -19
  52. package/lib-esm/predicates/index.d.ts +3 -2
  53. package/lib-esm/predicates/index.js +13 -3
  54. package/lib-esm/predicates/index.js.map +1 -1
  55. package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
  56. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +355 -204
  57. package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  58. package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
  59. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +66 -29
  60. package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  61. package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +5 -4
  62. package/lib-esm/storage/adapter/IndexedDBAdapter.js +390 -268
  63. package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
  64. package/lib-esm/storage/adapter/index.d.ts +1 -1
  65. package/lib-esm/storage/storage.d.ts +1 -1
  66. package/lib-esm/storage/storage.js +92 -27
  67. package/lib-esm/storage/storage.js.map +1 -1
  68. package/lib-esm/sync/index.d.ts +21 -4
  69. package/lib-esm/sync/index.js +15 -13
  70. package/lib-esm/sync/index.js.map +1 -1
  71. package/lib-esm/sync/merger.d.ts +3 -3
  72. package/lib-esm/sync/merger.js +7 -6
  73. package/lib-esm/sync/merger.js.map +1 -1
  74. package/lib-esm/sync/outbox.d.ts +2 -2
  75. package/lib-esm/sync/outbox.js +12 -10
  76. package/lib-esm/sync/outbox.js.map +1 -1
  77. package/lib-esm/sync/processors/mutation.js +61 -43
  78. package/lib-esm/sync/processors/mutation.js.map +1 -1
  79. package/lib-esm/sync/processors/subscription.js.map +1 -1
  80. package/lib-esm/sync/processors/sync.js.map +1 -1
  81. package/lib-esm/sync/utils.d.ts +3 -2
  82. package/lib-esm/sync/utils.js +62 -10
  83. package/lib-esm/sync/utils.js.map +1 -1
  84. package/lib-esm/types.d.ts +64 -25
  85. package/lib-esm/types.js +9 -2
  86. package/lib-esm/types.js.map +1 -1
  87. package/lib-esm/util.d.ts +56 -24
  88. package/lib-esm/util.js +334 -170
  89. package/lib-esm/util.js.map +1 -1
  90. package/package.json +7 -7
  91. package/src/datastore/datastore.ts +253 -113
  92. package/src/predicates/index.ts +32 -10
  93. package/src/storage/adapter/AsyncStorageAdapter.ts +309 -93
  94. package/src/storage/adapter/AsyncStorageDatabase.ts +74 -26
  95. package/src/storage/adapter/IndexedDBAdapter.ts +319 -136
  96. package/src/storage/adapter/index.ts +1 -1
  97. package/src/storage/storage.ts +68 -21
  98. package/src/sync/index.ts +41 -26
  99. package/src/sync/merger.ts +14 -4
  100. package/src/sync/outbox.ts +21 -8
  101. package/src/sync/processors/mutation.ts +49 -45
  102. package/src/sync/processors/subscription.ts +0 -1
  103. package/src/sync/processors/sync.ts +1 -3
  104. package/src/sync/utils.ts +69 -12
  105. package/src/types.ts +181 -29
  106. package/src/util.ts +415 -176
package/src/sync/utils.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  isGraphQLScalarType,
12
12
  isPredicateObj,
13
13
  isSchemaModel,
14
+ isSchemaModelWithAttributes,
14
15
  isTargetNameAssociation,
15
16
  isNonModelFieldType,
16
17
  ModelFields,
@@ -27,7 +28,12 @@ import {
27
28
  InternalSchema,
28
29
  AuthModeStrategy,
29
30
  } from '../types';
30
- import { exhaustiveCheck } from '../util';
31
+ import {
32
+ exhaustiveCheck,
33
+ extractPrimaryKeyFieldNames,
34
+ establishRelationAndKeys,
35
+ IDENTIFIER_KEY_SEPARATOR,
36
+ } from '../util';
31
37
  import { MutationEvent } from './';
32
38
 
33
39
  const logger = new Logger('DataStore');
@@ -47,7 +53,7 @@ export enum TransformerMutationType {
47
53
  GET = 'Get',
48
54
  }
49
55
 
50
- const dummyMetadata: Omit<ModelInstanceMetadata, 'id'> = {
56
+ const dummyMetadata: ModelInstanceMetadata = {
51
57
  _version: undefined,
52
58
  _lastChangedAt: undefined,
53
59
  _deleted: undefined,
@@ -79,7 +85,7 @@ export function generateSelectionSet(
79
85
  if (isSchemaModel(modelDefinition)) {
80
86
  scalarAndMetadataFields = scalarAndMetadataFields
81
87
  .concat(getMetadataFields())
82
- .concat(getConnectionFields(modelDefinition));
88
+ .concat(getConnectionFields(modelDefinition, namespace));
83
89
  }
84
90
 
85
91
  const result = scalarAndMetadataFields.join('\n');
@@ -103,7 +109,7 @@ function getOwnerFields(
103
109
  modelDefinition: SchemaModel | SchemaNonModel
104
110
  ): string[] {
105
111
  const ownerFields: string[] = [];
106
- if (isSchemaModel(modelDefinition) && modelDefinition.attributes) {
112
+ if (isSchemaModelWithAttributes(modelDefinition)) {
107
113
  modelDefinition.attributes.forEach(attr => {
108
114
  if (attr.properties && attr.properties.rules) {
109
115
  const rule = attr.properties.rules.find(rule => rule.allow === 'owner');
@@ -138,8 +144,12 @@ function getScalarFields(
138
144
  return result;
139
145
  }
140
146
 
141
- function getConnectionFields(modelDefinition: SchemaModel): string[] {
142
- const result = [];
147
+ // Used for generating the selection set for queries and mutations
148
+ function getConnectionFields(
149
+ modelDefinition: SchemaModel,
150
+ namespace: SchemaNamespace
151
+ ): string[] {
152
+ const result: string[] = [];
143
153
 
144
154
  Object.values(modelDefinition.fields)
145
155
  .filter(({ association }) => association && Object.keys(association).length)
@@ -153,7 +163,26 @@ function getConnectionFields(modelDefinition: SchemaModel): string[] {
153
163
  break;
154
164
  case 'BELONGS_TO':
155
165
  if (isTargetNameAssociation(association)) {
156
- result.push(`${name} { id _deleted }`);
166
+ // New codegen (CPK)
167
+ if (association.targetNames && association.targetNames.length > 0) {
168
+ // Need to retrieve relations in order to get connected model keys
169
+ const [relations] = establishRelationAndKeys(namespace);
170
+
171
+ const connectedModelName =
172
+ modelDefinition.fields[name].type['model'];
173
+
174
+ const byPkIndex = relations[connectedModelName].indexes.find(
175
+ ([name]) => name === 'byPk'
176
+ );
177
+ const keyFields = byPkIndex && byPkIndex[1];
178
+ const keyFieldSelectionSet = keyFields?.join(' ');
179
+
180
+ // We rely on `_deleted` when we process the sync query (e.g. in batchSave in the adapters)
181
+ result.push(`${name} { ${keyFieldSelectionSet} _deleted }`);
182
+ } else {
183
+ // backwards-compatability for schema generated prior to custom primary key support
184
+ result.push(`${name} { id _deleted }`);
185
+ }
157
186
  }
158
187
  break;
159
188
  default:
@@ -412,10 +441,13 @@ export function createMutationInstanceFromModelOperation<
412
441
  return v;
413
442
  };
414
443
 
444
+ const modelId = getIdentifierValue(modelDefinition, element);
445
+ const optionalId = OpType.INSERT && id ? { id } : {};
446
+
415
447
  const mutationEvent = modelInstanceCreator(MutationEventConstructor, {
416
- ...(id ? { id } : {}),
448
+ ...optionalId,
417
449
  data: JSON.stringify(element, replacer),
418
- modelId: element.id,
450
+ modelId,
419
451
  model: model.name,
420
452
  operation,
421
453
  condition: JSON.stringify(condition),
@@ -425,7 +457,8 @@ export function createMutationInstanceFromModelOperation<
425
457
  }
426
458
 
427
459
  export function predicateToGraphQLCondition(
428
- predicate: PredicatesGroup<any>
460
+ predicate: PredicatesGroup<any>,
461
+ modelDefinition: SchemaModel
429
462
  ): GraphQLCondition {
430
463
  const result = {};
431
464
 
@@ -433,17 +466,27 @@ export function predicateToGraphQLCondition(
433
466
  return result;
434
467
  }
435
468
 
469
+ const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
470
+
436
471
  predicate.predicates.forEach(p => {
437
472
  if (isPredicateObj(p)) {
438
473
  const { field, operator, operand } = p;
439
474
 
440
- if (field === 'id') {
475
+ // This is compatible with how the GQL Transform currently generates the Condition Input,
476
+ // i.e. any PK and SK fields are omitted and can't be used as conditions.
477
+ // However, I think this limits usability.
478
+ // What if we want to delete all records where SK > some value
479
+ // Or all records where PK = some value but SKs are different values
480
+
481
+ // TODO: if the Transform gets updated ^ we'll need to modify this logic to only omit
482
+ // key fields from the predicate/condition when ALL of the keyFields are present and using `eq` operators
483
+ if (keyFields.includes(field as string)) {
441
484
  return;
442
485
  }
443
486
 
444
487
  result[field] = { [operator]: operand };
445
488
  } else {
446
- result[p.type] = predicateToGraphQLCondition(p);
489
+ result[p.type] = predicateToGraphQLCondition(p, modelDefinition);
447
490
  }
448
491
  });
449
492
 
@@ -610,3 +653,17 @@ export async function getTokenForCustomAuth(
610
653
  }
611
654
  }
612
655
  }
656
+
657
+ // Util that takes a modelDefinition and model and returns either the id value(s) or the custom primary key value(s)
658
+ export function getIdentifierValue(
659
+ modelDefinition: SchemaModel,
660
+ model: ModelInstanceMetadata | PersistentModel
661
+ ): string {
662
+ const pkFieldNames = extractPrimaryKeyFieldNames(modelDefinition);
663
+
664
+ const idOrPk = pkFieldNames
665
+ .map(f => model[f])
666
+ .join(IDENTIFIER_KEY_SEPARATOR);
667
+
668
+ return idOrPk;
669
+ }
package/src/types.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  isAWSURL,
11
11
  isAWSPhone,
12
12
  isAWSIPAddress,
13
+ extractPrimaryKeyFieldNames,
13
14
  } from './util';
14
15
  import { PredicateAll } from './predicates';
15
16
  import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api-graphql';
@@ -46,9 +47,17 @@ export type SchemaModel = {
46
47
  fields: ModelFields;
47
48
  syncable?: boolean;
48
49
  };
50
+
49
51
  export function isSchemaModel(obj: any): obj is SchemaModel {
50
52
  return obj && (<SchemaModel>obj).pluralName !== undefined;
51
53
  }
54
+
55
+ export function isSchemaModelWithAttributes(
56
+ m: SchemaModel | SchemaNonModel
57
+ ): m is SchemaModel {
58
+ return isSchemaModel(m) && (m as SchemaModel).attributes !== undefined;
59
+ }
60
+
52
61
  export type SchemaNonModels = Record<string, SchemaNonModel>;
53
62
  export type SchemaNonModel = {
54
63
  name: string;
@@ -63,25 +72,29 @@ type SchemaEnum = {
63
72
  export type ModelAssociation = AssociatedWith | TargetNameAssociation;
64
73
  type AssociatedWith = {
65
74
  connectionType: 'HAS_MANY' | 'HAS_ONE';
66
- associatedWith: string;
75
+ associatedWith: string | string[];
67
76
  targetName?: string;
77
+ targetNames?: string[];
68
78
  };
79
+
69
80
  export function isAssociatedWith(obj: any): obj is AssociatedWith {
70
81
  return obj && obj.associatedWith;
71
82
  }
72
83
 
73
84
  type TargetNameAssociation = {
74
85
  connectionType: 'BELONGS_TO';
75
- targetName: string;
86
+ targetName?: string;
87
+ targetNames?: string[];
76
88
  };
89
+
77
90
  export function isTargetNameAssociation(
78
91
  obj: any
79
92
  ): obj is TargetNameAssociation {
80
- return obj && obj.targetName;
93
+ return obj?.targetName || obj?.targetNames;
81
94
  }
82
95
 
83
96
  export type ModelAttributes = ModelAttribute[];
84
- type ModelAttribute = { type: string; properties?: Record<string, any> };
97
+ export type ModelAttribute = { type: string; properties?: Record<string, any> };
85
98
 
86
99
  export type ModelAuthRule = {
87
100
  allow: string;
@@ -123,6 +136,7 @@ type ModelAttributeKey = {
123
136
  type ModelAttributePrimaryKey = {
124
137
  type: 'key';
125
138
  properties: {
139
+ name: never;
126
140
  fields: string[];
127
141
  };
128
142
  };
@@ -336,33 +350,138 @@ export type NonModelTypeConstructor<T> = {
336
350
  };
337
351
 
338
352
  // Class for model
339
- export type PersistentModelConstructor<
340
- T extends PersistentModel,
341
- K extends PersistentModelMetaData = {
342
- readOnlyFields: 'createdAt' | 'updatedAt';
343
- }
344
- > = {
345
- new (init: ModelInit<T, K>): T;
346
- copyOf(src: T, mutator: (draft: MutableModel<T, K>) => void): T;
353
+ export type PersistentModelConstructor<T extends PersistentModel> = {
354
+ new (init: ModelInit<T, PersistentModelMetaData<T>>): T;
355
+ copyOf(
356
+ src: T,
357
+ mutator: (draft: MutableModel<T, PersistentModelMetaData<T>>) => void
358
+ ): T;
347
359
  };
348
360
 
349
361
  export type TypeConstructorMap = Record<
350
362
  string,
351
- PersistentModelConstructor<any> | NonModelTypeConstructor<any>
363
+ PersistentModelConstructor<any> | NonModelTypeConstructor<unknown>
364
+ >;
365
+
366
+ /**
367
+ * Each identifier type is represented using nominal types, see:
368
+ * https://basarat.gitbook.io/typescript/main-1/nominaltyping
369
+ */
370
+ export declare const __identifierBrand__: unique symbol;
371
+ export type IdentifierBrand<T, K> = T & { [__identifierBrand__]: K };
372
+
373
+ // datastore generates a uuid for you
374
+ export type ManagedIdentifier<T, F extends keyof T> = IdentifierBrand<
375
+ { field: F extends string ? F : never; type: T },
376
+ 'ManagedIdentifier'
377
+ >;
378
+
379
+ // you can provide a value, if not, datastore generates a uuid for you
380
+ export type OptionallyManagedIdentifier<T, F extends keyof T> = IdentifierBrand<
381
+ { field: F extends string ? F : never; type: T },
382
+ 'OptionallyManagedIdentifier'
352
383
  >;
353
384
 
385
+ // You provide the values
386
+ export type CompositeIdentifier<T, K extends Array<keyof T>> = IdentifierBrand<
387
+ { fields: K; type: T },
388
+ 'CompositeIdentifier'
389
+ >;
390
+
391
+ // You provide the value
392
+ export type CustomIdentifier<T, K extends keyof T> = CompositeIdentifier<
393
+ T,
394
+ [K]
395
+ >;
396
+
397
+ export type Identifier<T> =
398
+ | ManagedIdentifier<T, any>
399
+ | OptionallyManagedIdentifier<T, any>
400
+ | CompositeIdentifier<T, any>
401
+ | CustomIdentifier<T, any>;
402
+
403
+ export type IdentifierFields<
404
+ T extends PersistentModel,
405
+ M extends PersistentModelMetaData<T> = never
406
+ > = (MetadataOrDefault<T, M>['identifier'] extends
407
+ | ManagedIdentifier<any, any>
408
+ | OptionallyManagedIdentifier<any, any>
409
+ ? MetadataOrDefault<T, M>['identifier']['field']
410
+ : MetadataOrDefault<T, M>['identifier'] extends CompositeIdentifier<
411
+ T,
412
+ infer B
413
+ >
414
+ ? B[number] // B[number]
415
+ : MetadataOrDefault<T, M>['identifier']['field']) &
416
+ string;
417
+
418
+ export type IdentifierFieldsForInit<
419
+ T extends PersistentModel,
420
+ M extends PersistentModelMetaData<T>
421
+ > = MetadataOrDefault<T, M>['identifier'] extends
422
+ | DefaultPersistentModelMetaData
423
+ | ManagedIdentifier<T, any>
424
+ ? never
425
+ : MetadataOrDefault<T, M>['identifier'] extends OptionallyManagedIdentifier<
426
+ T,
427
+ any
428
+ >
429
+ ? IdentifierFields<T, M>
430
+ : MetadataOrDefault<T, M>['identifier'] extends CompositeIdentifier<T, any>
431
+ ? IdentifierFields<T, M>
432
+ : never;
433
+
354
434
  // Instance of model
355
- export type PersistentModelMetaData = {
356
- readOnlyFields: string;
435
+ export declare const __modelMeta__: unique symbol;
436
+
437
+ export type PersistentModelMetaData<T> = {
438
+ identifier?: Identifier<T>;
439
+ readOnlyFields?: string;
357
440
  };
358
441
 
359
- export type PersistentModel = Readonly<{ id: string } & Record<string, any>>;
442
+ export type DefaultPersistentModelMetaData = {
443
+ identifier: ManagedIdentifier<{ id: string }, 'id'>;
444
+ readOnlyFields: never;
445
+ };
446
+
447
+ export type MetadataOrDefault<
448
+ T extends PersistentModel,
449
+ _ extends PersistentModelMetaData<T> = never
450
+ > = T extends {
451
+ [__modelMeta__]: PersistentModelMetaData<T>;
452
+ }
453
+ ? T[typeof __modelMeta__]
454
+ : DefaultPersistentModelMetaData;
455
+
456
+ export type PersistentModel = Readonly<Record<string, any>>;
457
+
458
+ export type MetadataReadOnlyFields<
459
+ T extends PersistentModel,
460
+ M extends PersistentModelMetaData<T>
461
+ > = Extract<
462
+ MetadataOrDefault<T, M>['readOnlyFields'] | M['readOnlyFields'],
463
+ keyof T
464
+ >;
465
+
466
+ // This type omits the metadata field in the constructor init object
467
+ // This type omits identifier fields in the constructor init object
468
+ // This type omits readOnlyFields in the constructor init object
469
+ // This type requires some identifiers in the constructor init object (e.g. CustomIdentifier)
470
+ // This type makes optional some identifiers in the constructor init object (e.g. OptionallyManagedIdentifier)
360
471
  export type ModelInit<
472
+ T extends PersistentModel,
473
+ M extends PersistentModelMetaData<T> = {}
474
+ > = Omit<
361
475
  T,
362
- K extends PersistentModelMetaData = {
363
- readOnlyFields: 'createdAt' | 'updatedAt';
364
- }
365
- > = Omit<T, 'id' | K['readOnlyFields']>;
476
+ typeof __modelMeta__ | IdentifierFields<T, M> | MetadataReadOnlyFields<T, M>
477
+ > &
478
+ (MetadataOrDefault<T, M>['identifier'] extends OptionallyManagedIdentifier<
479
+ T,
480
+ any
481
+ >
482
+ ? Partial<Pick<T, IdentifierFieldsForInit<T, M>>>
483
+ : Required<Pick<T, IdentifierFieldsForInit<T, M>>>);
484
+
366
485
  type DeepWritable<T> = {
367
486
  -readonly [P in keyof T]: T[P] extends TypeName<T[P]>
368
487
  ? T[P]
@@ -370,22 +489,45 @@ type DeepWritable<T> = {
370
489
  };
371
490
 
372
491
  export type MutableModel<
373
- T extends Record<string, any>,
374
- K extends PersistentModelMetaData = {
375
- readOnlyFields: 'createdAt' | 'updatedAt';
376
- }
492
+ T extends PersistentModel,
493
+ M extends PersistentModelMetaData<T> = {}
377
494
  // This provides Intellisense with ALL of the properties, regardless of read-only
378
495
  // but will throw a linting error if trying to overwrite a read-only property
379
- > = DeepWritable<Omit<T, 'id' | K['readOnlyFields']>> &
380
- Readonly<Pick<T, 'id' | K['readOnlyFields']>>;
496
+ > = DeepWritable<
497
+ Omit<T, IdentifierFields<T, M> | MetadataReadOnlyFields<T, M>>
498
+ > &
499
+ Readonly<Pick<T, IdentifierFields<T, M> | MetadataReadOnlyFields<T, M>>>;
381
500
 
382
501
  export type ModelInstanceMetadata = {
383
- id: string;
384
502
  _version: number;
385
503
  _lastChangedAt: number;
386
504
  _deleted: boolean;
387
505
  };
388
506
 
507
+ export type IdentifierFieldValue<
508
+ T extends PersistentModel,
509
+ M extends PersistentModelMetaData<T>
510
+ > = MetadataOrDefault<T, M>['identifier'] extends CompositeIdentifier<T, any>
511
+ ? MetadataOrDefault<T, M>['identifier']['fields'] extends [any]
512
+ ? T[MetadataOrDefault<T, M>['identifier']['fields'][0]]
513
+ : never
514
+ : T[MetadataOrDefault<T, M>['identifier']['field']];
515
+
516
+ export type IdentifierFieldOrIdentifierObject<
517
+ T extends PersistentModel,
518
+ M extends PersistentModelMetaData<T>
519
+ > = Pick<T, IdentifierFields<T, M>> | IdentifierFieldValue<T, M>;
520
+
521
+ export function isIdentifierObject<T extends PersistentModel>(
522
+ obj: any,
523
+ modelDefinition: SchemaModel
524
+ ): obj is IdentifierFields<T extends PersistentModel ? T : never, any> {
525
+ const keys = extractPrimaryKeyFieldNames(modelDefinition);
526
+
527
+ return (
528
+ typeof obj === 'object' && obj && keys.every(k => obj[k] !== undefined)
529
+ );
530
+ }
389
531
  //#endregion
390
532
 
391
533
  //#region Subscription messages
@@ -635,11 +777,21 @@ export type RelationType = {
635
777
  modelName: string;
636
778
  relationType: 'HAS_ONE' | 'HAS_MANY' | 'BELONGS_TO';
637
779
  targetName?: string;
638
- associatedWith?: string;
780
+ targetNames?: string[];
781
+ associatedWith?: string | string[];
782
+ };
783
+
784
+ type IndexOptions = {
785
+ unique?: boolean;
639
786
  };
640
787
 
788
+ export type IndexesType = Array<[string, string[], IndexOptions?]>;
789
+
641
790
  export type RelationshipType = {
642
- [modelName: string]: { indexes: string[]; relationTypes: RelationType[] };
791
+ [modelName: string]: {
792
+ indexes: IndexesType;
793
+ relationTypes: RelationType[];
794
+ };
643
795
  };
644
796
 
645
797
  //#endregion