@stackbit/cms-contentful 0.4.49-staging.1 → 0.4.49

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.
@@ -39,11 +39,17 @@ interface PlainApiClientOptions {
39
39
  accessToken: string;
40
40
  spaceId: string;
41
41
  environment: string;
42
+ managementHost?: string;
43
+ uploadHost?: string;
42
44
  }
43
45
 
44
- export function createPlainApiClient({ accessToken, spaceId, environment }: PlainApiClientOptions) {
46
+ export function createPlainApiClient({ accessToken, spaceId, environment, managementHost, uploadHost }: PlainApiClientOptions) {
45
47
  return createClient(
46
- { accessToken: accessToken },
48
+ {
49
+ accessToken: accessToken,
50
+ ...(managementHost ? { host: managementHost } : null),
51
+ ...(uploadHost ? { hostUpload: uploadHost } : null)
52
+ },
47
53
  {
48
54
  type: 'plain',
49
55
  defaults: {
@@ -178,7 +184,7 @@ export async function fetchEntriesUpdatedAfter(client: PlainClientAPI, date: str
178
184
  return fetchAllItems<EntryProps>({
179
185
  getMany: client.entry.getMany,
180
186
  logger,
181
- params: {
187
+ query: {
182
188
  'sys.updatedAt[gt]': date
183
189
  }
184
190
  });
@@ -192,7 +198,7 @@ export async function fetchAssetsUpdatedAfter(client: PlainClientAPI, date: stri
192
198
  return fetchAllItems<AssetProps>({
193
199
  getMany: client.asset.getMany,
194
200
  logger,
195
- params: {
201
+ query: {
196
202
  'sys.updatedAt[gt]': date
197
203
  }
198
204
  });
@@ -210,11 +216,23 @@ export async function fetchAllContentTypes(client: PlainClientAPI, logger?: Logg
210
216
  return fetchAllItems<ContentTypeProps>({ getMany: client.contentType.getMany, logger });
211
217
  }
212
218
 
213
- export async function fetchAllContentTypesAfter(client: PlainClientAPI, date: string, logger?: Logger) {
219
+ export async function hasContentTypesUpdatedAfter(client: PlainClientAPI, date: string, logger?: Logger): Promise<boolean> {
220
+ const updatedContentTypes = await fetchAllItems<ContentTypeProps>({
221
+ getMany: client.contentType.getMany,
222
+ logger,
223
+ query: {
224
+ 'sys.updatedAt[gt]': date,
225
+ limit: 1
226
+ }
227
+ });
228
+ return updatedContentTypes.length > 0;
229
+ }
230
+
231
+ export async function fetchContentTypesUpdatedAfter(client: PlainClientAPI, date: string, logger?: Logger) {
214
232
  return fetchAllItems<ContentTypeProps>({
215
233
  getMany: client.contentType.getMany,
216
234
  logger,
217
- params: {
235
+ query: {
218
236
  'sys.updatedAt[gt]': date
219
237
  }
220
238
  });
@@ -366,11 +384,11 @@ export async function getScheduledAction(client: PlainClientAPI, scheduledAction
366
384
  export async function fetchAllItems<T>({
367
385
  getMany,
368
386
  logger,
369
- params
387
+ query
370
388
  }: {
371
389
  getMany: (params: QueryParams) => Promise<CollectionProp<T>>;
372
390
  logger?: Logger;
373
- params?: Record<string, any>;
391
+ query?: QueryOptions;
374
392
  }): Promise<T[]> {
375
393
  let limit = 1000;
376
394
  let items: T[] = [];
@@ -382,7 +400,7 @@ export async function fetchAllItems<T>({
382
400
  query: {
383
401
  skip: items.length,
384
402
  limit: limit,
385
- ...params
403
+ ...query
386
404
  }
387
405
  });
388
406
  items = items.concat(result.items);
@@ -76,10 +76,10 @@ import {
76
76
  convertDocumentVersions
77
77
  } from './contentful-entries-converter';
78
78
  import { convertAndFilterScheduledActions } from './contentful-scheduled-actions-converter';
79
- import { ContentPoller, SyncResult } from './content-poller';
79
+ import { ContentPoller, ContentPollerSyncResult, ContentPollerSyncContext } from './content-poller';
80
80
  import { Readable } from 'stream';
81
81
  import { CONTENTFUL_BUILT_IN_IMAGE_SOURCES, CONTENTFUL_BYNDER_APP, CONTENTFUL_CLOUDINARY_APP } from './contentful-consts';
82
- import { getLastUpdatedEntityDate, SyncContext } from './utils';
82
+ import { getLastUpdatedEntityDate } from './utils';
83
83
 
84
84
  export interface ContentSourceOptions {
85
85
  /** Contentful Space ID */
@@ -103,13 +103,50 @@ export interface ContentSourceOptions {
103
103
  * Use webhook for content updates.
104
104
  */
105
105
  useWebhookForContentUpdates?: boolean;
106
+ /**
107
+ * Use accessToken instead of userContext.accessToken for write operations.
108
+ */
109
+ useAccessTokenForUpdates?: boolean;
110
+ /**
111
+ * Switches the ContentfulContentSource to work with EU data regions.
112
+ * When set to true, the following updated accordingly:
113
+ * previewHost: 'preview.eu.contentful.com'
114
+ * manageHost: 'api.eu.contentful.com'
115
+ * uploadHost: 'upload.eu.contentful.com'
116
+ */
117
+ useEURegion?: boolean;
118
+ /**
119
+ * The host of the Contentful's preview API.
120
+ * By default, this value is set to 'preview.contentful.com'.
121
+ * If `useEURegion` is set, uses 'preview.eu.contentful.com'.
122
+ */
123
+ previewHost?: string;
124
+ /**
125
+ * The host of the Contentful's management API.
126
+ * By default, this value is `undefined` which makes the contentful-management
127
+ * use the 'api.contentful.com'.
128
+ * If `useEURegion` is set, this value is set to 'api.eu.contentful.com'.
129
+ */
130
+ managementHost?: string;
131
+ /**
132
+ * The host used to upload assets to Contentful.
133
+ * By default, this value is `undefined` which makes the contentful-management
134
+ * use the 'upload.contentful.com'.
135
+ * If `useEURegion` is set, this value is set to 'upload.eu.contentful.com'.
136
+ */
137
+ uploadHost?: string;
106
138
  }
107
139
 
108
140
  type UserContext = {
141
+ // The user's accessToken is provided by the "Netlify Create" Contentful
142
+ // OAuth Application when users connect their Netlify Create and Contentful
143
+ // accounts via OAuth flow.
109
144
  accessToken: string;
110
145
  };
111
146
 
112
- export type SchemaContext = unknown;
147
+ export type SchemaContext = {
148
+ lastUpdatedContentTypeDate?: string;
149
+ };
113
150
 
114
151
  type EntityError = {
115
152
  name: string;
@@ -129,6 +166,11 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
129
166
  private localDev!: boolean;
130
167
  private contentPoller: ContentPoller | null = null;
131
168
  private useWebhookForContentUpdates: boolean;
169
+ private useAccessTokenForUpdates: boolean;
170
+ private useEURegion: boolean;
171
+ private previewHost?: string;
172
+ private managementHost?: string;
173
+ private uploadHost?: string;
132
174
  private locales: LocaleProps[] = [];
133
175
  private defaultLocale?: LocaleProps;
134
176
  private userMap: Record<string, UserProps> = {};
@@ -138,7 +180,6 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
138
180
  private webhookUrl?: string;
139
181
  private devAppRestartNeeded?: () => void;
140
182
  private cache!: ContentSourceTypes.Cache<SchemaContext, DocumentContext, AssetContext>;
141
- private syncContext!: SyncContext;
142
183
  private taskQueue: TaskQueue;
143
184
 
144
185
  constructor(options: ContentSourceOptions) {
@@ -148,6 +189,11 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
148
189
  this.previewToken = options.previewToken ?? process.env.CONTENTFUL_PREVIEW_TOKEN;
149
190
  this.useLocalizedAssetFields = options.useLocalizedAssetFields ?? false;
150
191
  this.useWebhookForContentUpdates = options.useWebhookForContentUpdates ?? false;
192
+ this.useAccessTokenForUpdates = options.useAccessTokenForUpdates ?? false;
193
+ this.useEURegion = options.useEURegion ?? false;
194
+ this.previewHost = options.previewHost ?? (this.useEURegion ? 'preview.eu.contentful.com' : undefined);
195
+ this.managementHost = options.managementHost ?? (this.useEURegion ? 'api.eu.contentful.com' : undefined);
196
+ this.uploadHost = options.uploadHost ?? (this.useEURegion ? 'upload.eu.contentful.com' : undefined);
151
197
  // Max active bulk actions per space is limited to 5.
152
198
  // Max of 200 items per bulk action.
153
199
  this.taskQueue = new TaskQueue({ limit: 5 });
@@ -158,7 +204,7 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
158
204
  }
159
205
 
160
206
  getContentSourceType(): string {
161
- return 'contentful';
207
+ return this.useEURegion ? 'contentful-eu' : 'contentful';
162
208
  }
163
209
 
164
210
  getProjectId(): string {
@@ -197,12 +243,12 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
197
243
  this.userLogger.info('Using webhook for content updates');
198
244
  }
199
245
 
200
- this.syncContext = ((await this.cache.get('syncContext')) as SyncContext) ?? {};
201
-
202
246
  this.plainClient = createPlainApiClient({
203
247
  spaceId: this.spaceId,
204
248
  accessToken: this.accessToken,
205
- environment: this.environment
249
+ environment: this.environment,
250
+ managementHost: this.managementHost,
251
+ uploadHost: this.uploadHost
206
252
  });
207
253
 
208
254
  await this.validateConfig();
@@ -217,15 +263,6 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
217
263
  const appInstallations: AppInstallationProps[] = await fetchAllAppInstallations(this.plainClient);
218
264
  await this.fetchUsers();
219
265
 
220
- // The contentPoller updates its syncContext property internally when
221
- // it gets an updated content. But, the actual cached data is not
222
- // updated. Therefore, when content source module is restarted, we need
223
- // to reset the contentPoller's internal syncContext, so it will be
224
- // able to fetch all the updated content from its cached state.
225
- if (this.contentPoller && this.contentPoller.pollType === 'date') {
226
- this.contentPoller.setSyncContext(this.syncContext);
227
- }
228
-
229
266
  // replace all data at once in atomic action
230
267
  this.locales = locales.filter((locale) => {
231
268
  // filter out disabled locales
@@ -338,12 +375,15 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
338
375
  environment: this.environment,
339
376
  previewToken: this.previewToken,
340
377
  managementToken: this.accessToken,
378
+ previewHost: this.previewHost,
379
+ managementHost: this.managementHost,
380
+ uploadHost: this.uploadHost,
341
381
  pollType: 'date',
342
- syncContext: this.syncContext,
382
+ syncContext: this.getSyncContextForContentPollerFromCache(),
343
383
  notificationCallback: async (syncResult) => {
344
384
  if (syncResult.contentTypes.length) {
345
385
  this.logger.debug('content type was changed, invalidate schema');
346
- this.cache.invalidateSchema();
386
+ await this.cache.invalidateSchema();
347
387
  } else {
348
388
  const result = await this.convertSyncResult(syncResult);
349
389
  await this.cache.updateContent(result);
@@ -367,7 +407,32 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
367
407
  this.userMap = _.keyBy(users, 'sys.id');
368
408
  }
369
409
 
370
- private async convertSyncResult(syncResult: SyncResult) {
410
+ private async fetchUsersIfNeeded(entities: (EntryProps | AssetProps)[]) {
411
+ const entityHasNoCachedAuthor = entities.some((entity) => !this.userMap[entity.sys.updatedBy?.sys.id ?? '']);
412
+ if (entityHasNoCachedAuthor) {
413
+ await this.fetchUsers();
414
+ }
415
+ }
416
+
417
+ private updateContentPollerSyncContext(syncContext: ContentPollerSyncContext) {
418
+ if (this.contentPoller && this.contentPoller.pollType === 'date') {
419
+ this.contentPoller.setSyncContext({
420
+ ...this.getSyncContextForContentPollerFromCache(),
421
+ ...syncContext
422
+ });
423
+ }
424
+ }
425
+
426
+ private getSyncContextForContentPollerFromCache() {
427
+ const cacheSyncContext = this.cache.getSyncContext();
428
+ return {
429
+ lastUpdatedEntryDate: cacheSyncContext.documentsSyncContext as string | undefined,
430
+ lastUpdatedAssetDate: cacheSyncContext.assetsSyncContext as string | undefined,
431
+ lastUpdatedContentTypeDate: this.cache.getSchema().context?.lastUpdatedContentTypeDate
432
+ };
433
+ }
434
+
435
+ private async convertSyncResult(syncResult: ContentPollerSyncResult) {
371
436
  // remove deleted entries and assets from fieldData
372
437
  // generally, the "sync" method of the preview API never notifies of deleted objects, therefore we rely on
373
438
  // the deleteObject method to notify the user that restart is needed. Then, once user restarts the SSG, it
@@ -380,11 +445,7 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
380
445
  deletedAssets: syncResult.deletedAssets.length
381
446
  });
382
447
 
383
- const resultHasNoCachedAuthor = [...syncResult.entries, ...syncResult.assets].some((data) => !this.userMap[data.sys.updatedBy?.sys.id ?? '']);
384
-
385
- if (resultHasNoCachedAuthor) {
386
- await this.fetchUsers();
387
- }
448
+ await this.fetchUsersIfNeeded([...syncResult.entries, ...syncResult.assets]);
388
449
 
389
450
  // convert updated/created entries and assets
390
451
  const documents = this.convertEntries(syncResult.entries, this.cache.getModelByName);
@@ -413,94 +474,111 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
413
474
  bynderImagesAsList: this.bynderImagesAsList
414
475
  });
415
476
 
477
+ const prevLastUpdatedContentTypeDate = this.cache.getSchema().context?.lastUpdatedContentTypeDate;
478
+
416
479
  // Check if one of the content types was changed from the last time the
417
480
  // content types were fetched, in which case remove all cached content.
418
481
  const lastUpdatedContentTypeDate = getLastUpdatedEntityDate(contentTypes);
419
- if (this.syncContext.lastUpdatedContentTypeDate !== lastUpdatedContentTypeDate) {
482
+ if (prevLastUpdatedContentTypeDate !== lastUpdatedContentTypeDate) {
420
483
  this.logger.debug(
421
- `last updated content type date '${lastUpdatedContentTypeDate}' is different from cached ` +
422
- `syncContext.lastUpdatedContentTypeDate '${this.syncContext.lastUpdatedContentTypeDate}', clearing cache`
484
+ `last updated content type date '${lastUpdatedContentTypeDate}' is different ` +
485
+ `from the cached date '${prevLastUpdatedContentTypeDate}', clearing cache`
423
486
  );
424
- await this.cache.remove('entries');
425
- await this.cache.remove('assets');
426
- this.syncContext = { lastUpdatedContentTypeDate };
427
- await this.cache.set('syncContext', this.syncContext);
487
+ await this.cache.clearSyncContext({
488
+ clearDocumentsSyncContext: true,
489
+ clearAssetsSyncContext: false
490
+ });
428
491
  }
429
492
 
493
+ this.updateContentPollerSyncContext({ lastUpdatedContentTypeDate });
494
+
430
495
  return {
431
496
  models,
432
497
  locales: this.locales.map((locale) => ({
433
498
  code: locale.code,
434
499
  default: locale.default
435
500
  })),
436
- context: {}
501
+ context: {
502
+ lastUpdatedContentTypeDate: lastUpdatedContentTypeDate
503
+ }
437
504
  };
438
505
  }
439
506
 
440
- async getDocuments(): Promise<ContextualDocument[]> {
507
+ async getDocuments(options?: { syncContext?: string }): Promise<ContextualDocument[] | { documents: ContextualDocument[]; syncContext?: string }> {
441
508
  this.logger.debug('getDocuments');
509
+ let lastUpdatedEntryDate = options?.syncContext;
442
510
  let entries: EntryProps[];
443
- const cachedEntries = ((await this.cache.get('entries')) as EntryProps[]) ?? [];
444
511
  try {
445
- if (this.syncContext.lastUpdatedEntryDate && !_.isEmpty(cachedEntries)) {
446
- this.logger.debug(`got ${cachedEntries.length} entries from cache, fetching entries updated after ${this.syncContext.lastUpdatedEntryDate}`);
447
- const updatedEntries = await fetchEntriesUpdatedAfter(this.plainClient, this.syncContext.lastUpdatedEntryDate, this.userLogger);
448
- this.logger.debug(`got ${updatedEntries.length} updated/created entries after ${this.syncContext.lastUpdatedEntryDate}`);
449
- if (updatedEntries.length) {
450
- this.syncContext.lastUpdatedEntryDate = getLastUpdatedEntityDate(updatedEntries);
512
+ if (lastUpdatedEntryDate) {
513
+ this.logger.debug(`fetching entries updated after ${lastUpdatedEntryDate}`);
514
+ entries = await fetchEntriesUpdatedAfter(this.plainClient, lastUpdatedEntryDate, this.userLogger);
515
+ this.logger.debug(`got ${entries.length} updated/created entries after ${lastUpdatedEntryDate}`);
516
+ if (entries.length) {
517
+ lastUpdatedEntryDate = getLastUpdatedEntityDate(entries);
451
518
  }
452
- entries = _.unionBy(updatedEntries, cachedEntries, 'sys.id');
453
519
  } else {
454
520
  entries = await fetchAllEntries(this.plainClient, this.userLogger);
455
- this.syncContext.lastUpdatedEntryDate = getLastUpdatedEntityDate(entries);
521
+ lastUpdatedEntryDate = getLastUpdatedEntityDate(entries);
456
522
  }
457
523
  } catch (error: any) {
458
524
  // Stackbit won't be able to work properly even if one of the entries was not fetched.
459
525
  // All fetch methods use Contentful's API client that handles errors and retries automatically.
460
526
  this.logger.error(`Failed fetching documents from Contentful, error: ${error.message}`);
461
- return [];
527
+ // By returning the original syncContext we are ensuring that next
528
+ // time the getDocuments is called, it will try to get the documents
529
+ // using the same syncContext
530
+ return { documents: [], syncContext: lastUpdatedEntryDate };
462
531
  }
463
- await this.cache.set('entries', entries);
464
- await this.cache.set('syncContext', this.syncContext);
532
+
533
+ this.updateContentPollerSyncContext({ lastUpdatedEntryDate });
534
+
465
535
  this.logger.debug(
466
- `got ${entries.length} entries from space ${this.spaceId}, environment ${this.environment}, lastUpdatedEntryDate: ${this.syncContext.lastUpdatedEntryDate}`
536
+ `got ${entries.length} entries from space ${this.spaceId}, environment ${this.environment}, lastUpdatedEntryDate: ${lastUpdatedEntryDate}`
467
537
  );
468
- return this.convertEntries(entries, this.cache.getModelByName);
538
+ await this.fetchUsersIfNeeded(entries);
539
+
540
+ const documents = this.convertEntries(entries, this.cache.getModelByName);
541
+
542
+ return { documents, syncContext: lastUpdatedEntryDate };
469
543
  }
470
544
 
471
- async getAssets() {
545
+ async getAssets(options?: { syncContext?: string }): Promise<ContextualAsset[] | { assets: ContextualAsset[]; syncContext?: string }> {
472
546
  this.logger.debug('getAssets');
473
- let assets: AssetProps[];
474
- const cachedAssets = ((await this.cache.get('assets')) as AssetProps[]) ?? [];
547
+ let lastUpdatedAssetDate = options?.syncContext;
548
+ let ctflAssets: AssetProps[];
475
549
  try {
476
- if (this.syncContext.lastUpdatedAssetDate && !_.isEmpty(cachedAssets)) {
477
- this.logger.debug(`got ${cachedAssets.length} assets from cache, fetching assets updated after ${this.syncContext.lastUpdatedAssetDate}`);
478
- const updatedAssets = await fetchAssetsUpdatedAfter(this.plainClient, this.syncContext.lastUpdatedAssetDate, this.userLogger);
479
- this.logger.debug(`got ${updatedAssets.length} updated/created assets after ${this.syncContext.lastUpdatedAssetDate}`);
480
- if (updatedAssets.length) {
481
- this.syncContext.lastUpdatedAssetDate = getLastUpdatedEntityDate(updatedAssets);
550
+ if (lastUpdatedAssetDate) {
551
+ this.logger.debug(`fetching assets updated after ${lastUpdatedAssetDate}`);
552
+ ctflAssets = await fetchAssetsUpdatedAfter(this.plainClient, lastUpdatedAssetDate, this.userLogger);
553
+ this.logger.debug(`got ${ctflAssets.length} updated/created assets after ${lastUpdatedAssetDate}`);
554
+ if (ctflAssets.length) {
555
+ lastUpdatedAssetDate = getLastUpdatedEntityDate(ctflAssets);
482
556
  }
483
- assets = _.unionBy(updatedAssets, cachedAssets, 'sys.id');
484
557
  } else {
485
- assets = await fetchAllAssets(this.plainClient, this.userLogger);
486
- this.syncContext.lastUpdatedAssetDate = getLastUpdatedEntityDate(assets);
558
+ ctflAssets = await fetchAllAssets(this.plainClient, this.userLogger);
559
+ lastUpdatedAssetDate = getLastUpdatedEntityDate(ctflAssets);
487
560
  }
488
561
  } catch (error: any) {
489
562
  // Stackbit won't be able to work properly even if one of the entries or the assets was not fetched.
490
563
  // All fetch methods use Contentful's API client that handles errors and retries automatically.
491
564
  this.logger.error(`Failed fetching assets from Contentful, error: ${error.message}`);
492
- return [];
565
+ return { assets: [], syncContext: lastUpdatedAssetDate };
493
566
  }
494
- await this.cache.set('assets', assets);
495
- await this.cache.set('syncContext', this.syncContext);
567
+
568
+ this.updateContentPollerSyncContext({ lastUpdatedAssetDate });
569
+
496
570
  this.logger.debug(
497
- `got ${assets.length} assets from space ${this.spaceId}, environment ${this.environment}, lastUpdatedAssetDate: ${this.syncContext.lastUpdatedAssetDate}`
571
+ `got ${ctflAssets.length} assets from space ${this.spaceId}, environment ${this.environment}, lastUpdatedAssetDate: ${lastUpdatedAssetDate}`
498
572
  );
499
- return this.convertAssets(assets);
573
+ await this.fetchUsersIfNeeded(ctflAssets);
574
+
575
+ const assets = this.convertAssets(ctflAssets);
576
+
577
+ return { assets, syncContext: lastUpdatedAssetDate };
500
578
  }
501
579
 
502
580
  async hasAccess({ userContext }: { userContext?: UserContext }): Promise<{ hasConnection: boolean; hasPermissions: boolean }> {
503
- if (!this.localDev && !userContext?.accessToken) {
581
+ if (!this.localDev && !this.useAccessTokenForUpdates && !userContext?.accessToken) {
504
582
  return {
505
583
  hasConnection: false,
506
584
  hasPermissions: false
@@ -1011,7 +1089,7 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
1011
1089
  }
1012
1090
 
1013
1091
  private getPlainApiClientForUser({ userContext }: { userContext?: UserContext }): PlainClientAPI {
1014
- if (this.localDev) {
1092
+ if (this.localDev || this.useAccessTokenForUpdates) {
1015
1093
  return this.plainClient;
1016
1094
  }
1017
1095
  const userAccessToken = userContext?.accessToken;
@@ -1021,7 +1099,9 @@ export class ContentfulContentSource implements ContentSourceTypes.ContentSource
1021
1099
  return createPlainApiClient({
1022
1100
  spaceId: this.spaceId,
1023
1101
  accessToken: userAccessToken,
1024
- environment: this.environment
1102
+ environment: this.environment,
1103
+ managementHost: this.managementHost,
1104
+ uploadHost: this.uploadHost
1025
1105
  });
1026
1106
  }
1027
1107
 
package/src/utils.ts CHANGED
@@ -1,11 +1,5 @@
1
1
  import { BasicMetaSysProps } from 'contentful-management';
2
2
 
3
- export type SyncContext = {
4
- lastUpdatedEntryDate?: string;
5
- lastUpdatedAssetDate?: string;
6
- lastUpdatedContentTypeDate?: string;
7
- };
8
-
9
3
  export function getLastUpdatedEntityDate(entities: { sys: BasicMetaSysProps }[]): string | undefined {
10
4
  let lastUpdatedDate: string = '';
11
5
  for (const entity of entities) {