@radiantearth/stac-migrate 2.0.2 → 2.1.0

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 (5) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +81 -41
  3. package/bin/cli.js +20 -20
  4. package/migrate.js +244 -190
  5. package/package.json +10 -7
package/migrate.js CHANGED
@@ -7,23 +7,27 @@ var compareVersions = require('compare-versions');
7
7
  const LATEST_VERSION = '1.1.0';
8
8
  const DONE = true; // This is used to verify in code coverage whether something has been used or not
9
9
  const SCHEMAS = {
10
- 'classification': 'https://stac-extensions.github.io/classification/v2.0.0/schema.json',
11
- 'datacube': 'https://stac-extensions.github.io/datacube/v2.2.0/schema.json',
12
- 'eo': 'https://stac-extensions.github.io/eo/v2.0.0/schema.json',
13
- 'file': 'https://stac-extensions.github.io/file/v2.1.0/schema.json',
10
+ cf: 'https://stac-extensions.github.io/cf/v1.0.0/schema.json',
11
+ classification: 'https://stac-extensions.github.io/classification/v2.0.0/schema.json',
12
+ datacube: 'https://stac-extensions.github.io/datacube/v2.3.0/schema.json',
13
+ eo: 'https://stac-extensions.github.io/eo/v2.0.0/schema.json',
14
+ file: 'https://stac-extensions.github.io/file/v2.1.0/schema.json',
15
+ instruments: 'https://stac-extensions.github.io/instruments/v0.1.0/schema.json',
14
16
  'item-assets': 'https://stac-extensions.github.io/item-assets/v1.0.0/schema.json',
15
- 'label': 'https://stac-extensions.github.io/label/v1.0.1/schema.json',
16
- 'pointcloud': 'https://stac-extensions.github.io/pointcloud/v1.0.0/schema.json',
17
- 'processing': 'https://stac-extensions.github.io/processing/v1.2.0/schema.json',
18
- 'projection': 'https://stac-extensions.github.io/projection/v2.0.0/schema.json',
19
- 'raster': 'https://stac-extensions.github.io/raster/v2.0.0/schema.json',
20
- 'sar': 'https://stac-extensions.github.io/sar/v1.0.0/schema.json',
21
- 'sat': 'https://stac-extensions.github.io/sat/v1.0.0/schema.json',
22
- 'scientific': 'https://stac-extensions.github.io/scientific/v1.0.0/schema.json',
23
- 'table': 'https://stac-extensions.github.io/table/v1.2.0/schema.json',
24
- 'timestamps': 'https://stac-extensions.github.io/timestamps/v1.1.0/schema.json',
25
- 'version': 'https://stac-extensions.github.io/version/v1.2.0/schema.json',
26
- 'view': 'https://stac-extensions.github.io/view/v1.0.0/schema.json'
17
+ label: 'https://stac-extensions.github.io/label/v1.0.1/schema.json',
18
+ order: 'https://stac-extensions.github.io/order/v1.1.0/schema.json',
19
+ pointcloud: 'https://stac-extensions.github.io/pointcloud/v2.0.0/schema.json',
20
+ processing: 'https://stac-extensions.github.io/processing/v1.2.0/schema.json',
21
+ product: 'https://stac-extensions.github.io/product/v1.0.0/schema.json',
22
+ projection: 'https://stac-extensions.github.io/projection/v2.0.0/schema.json',
23
+ raster: 'https://stac-extensions.github.io/raster/v2.0.0/schema.json',
24
+ sar: 'https://stac-extensions.github.io/sar/v1.3.2/schema.json',
25
+ sat: 'https://stac-extensions.github.io/sat/v1.2.0/schema.json',
26
+ scientific: 'https://stac-extensions.github.io/scientific/v1.0.0/schema.json',
27
+ table: 'https://stac-extensions.github.io/table/v1.2.0/schema.json',
28
+ timestamps: 'https://stac-extensions.github.io/timestamps/v1.1.0/schema.json',
29
+ version: 'https://stac-extensions.github.io/version/v1.2.0/schema.json',
30
+ view: 'https://stac-extensions.github.io/view/v1.1.0/schema.json',
27
31
  };
28
32
  const EXTENSIONS = {
29
33
  // Add a : at the end to indicate it has a prefix, otherwise list all fields separately (see version extension for example).
@@ -43,11 +47,11 @@ const EXTENSIONS = {
43
47
  'sci:': SCHEMAS.scientific,
44
48
  'view:': SCHEMAS.view,
45
49
  // without prefix
46
- 'version': SCHEMAS.version,
47
- 'deprecated': SCHEMAS.version,
48
- 'published': SCHEMAS.timestamps,
49
- 'expires': SCHEMAS.timestamps,
50
- 'unpublished': SCHEMAS.timestamps
50
+ version: SCHEMAS.version,
51
+ deprecated: SCHEMAS.version,
52
+ published: SCHEMAS.timestamps,
53
+ expires: SCHEMAS.timestamps,
54
+ unpublished: SCHEMAS.timestamps,
51
55
  },
52
56
  catalog: {
53
57
  // None yet
@@ -65,11 +69,11 @@ EXTENSIONS.item = Object.assign(EXTENSIONS.item, EXTENSIONS.itemAndCollection);
65
69
  var Ext = {
66
70
  parseExtension(url) {
67
71
  // Try to match name and version from official extensions
68
- let match = url.match(/^https?:\/\/stac-extensions.github.io\/([^\/]+)\/v([^\/]+)\/[^.]+.json$/i);
72
+ let match = url.match(/^https?:\/\/stac-extensions.github.io\/([^/]+)\/v([^/]+)\/[^.]+.json$/i);
69
73
  if (match) {
70
74
  return {
71
75
  id: match[1],
72
- version: match[2]
76
+ version: match[2],
73
77
  };
74
78
  }
75
79
  // Try to match version from URIs
@@ -77,17 +81,17 @@ var Ext = {
77
81
  if (match2) {
78
82
  return {
79
83
  id: url,
80
- version: match2[1]
84
+ version: match2[1],
81
85
  };
82
86
  }
83
87
  // Handle schortnames
84
88
  if (url in SCHEMAS) {
85
89
  return {
86
90
  id: url,
87
- version: '0.0.0'
91
+ version: '0.0.0',
88
92
  };
89
93
  }
90
- }
94
+ },
91
95
  };
92
96
 
93
97
  var V = {
@@ -97,8 +101,7 @@ var V = {
97
101
  set(stac) {
98
102
  if (typeof stac.stac_version !== 'string') {
99
103
  V.version = '0.6.0'; // Assume the worst case, it doesn't seem there's a clear indicator for 0.7.0
100
- }
101
- else {
104
+ } else {
102
105
  V.version = stac.stac_version;
103
106
  }
104
107
 
@@ -120,23 +123,19 @@ var V = {
120
123
  let compareTo = ext ? V.extensions[ext] : V.version;
121
124
  if (typeof compareTo === 'undefined') {
122
125
  return false;
123
- }
124
- else {
126
+ } else {
125
127
  return compareVersions.compare(compareTo, version, comparator);
126
128
  }
127
- }
128
-
129
+ },
129
130
  };
130
131
 
131
132
  var _ = {
132
-
133
133
  type(val) {
134
134
  let type = typeof val;
135
135
  if (type === 'object') {
136
136
  if (val === null) {
137
137
  return 'null';
138
- }
139
- else if (Array.isArray(val)) {
138
+ } else if (Array.isArray(val)) {
140
139
  return 'array';
141
140
  }
142
141
  }
@@ -146,18 +145,17 @@ var _ = {
146
145
  is(val, type) {
147
146
  if (Array.isArray(type)) {
148
147
  return type.includes(_.type(val));
149
- }
150
- else {
148
+ } else {
151
149
  return _.type(val) === type;
152
150
  }
153
151
  },
154
152
 
155
153
  isDefined(val) {
156
- return (typeof val !== 'undefined');
154
+ return typeof val !== 'undefined';
157
155
  },
158
156
 
159
157
  isObject(obj) {
160
- return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj));
158
+ return typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj);
161
159
  },
162
160
 
163
161
  rename(obj, oldKey, newKey) {
@@ -212,8 +210,7 @@ var _ = {
212
210
  if (obj[key].length === 1) {
213
211
  obj[key] = obj[key][0];
214
212
  return true;
215
- }
216
- else {
213
+ } else {
217
214
  return false; // It's still an array and we don't know which element to choose
218
215
  }
219
216
  }
@@ -235,8 +232,7 @@ var _ = {
235
232
  if (Array.isArray(obj[key]) && obj[key].length > 0) {
236
233
  obj[key] = obj[key][0];
237
234
  return true;
238
- }
239
- else {
235
+ } else {
240
236
  delete obj[key];
241
237
  return false;
242
238
  }
@@ -251,22 +247,21 @@ var _ = {
251
247
 
252
248
  upgradeExtension(context, extension) {
253
249
  let { id, version } = Ext.parseExtension(extension);
254
- let index = context.stac_extensions.findIndex(url => {
250
+ let index = context.stac_extensions.findIndex((url) => {
255
251
  let old = Ext.parseExtension(url);
256
- return (old && old.id === id && compareVersions.compare(old.version, version, '<'));
252
+ return old && old.id === id && compareVersions.compare(old.version, version, '<');
257
253
  });
258
254
  if (index !== -1) {
259
255
  context.stac_extensions[index] = extension;
260
256
  return true;
261
- }
262
- else {
257
+ } else {
263
258
  return false;
264
259
  }
265
260
  },
266
261
 
267
262
  addExtension(context, newExtension) {
268
263
  let { id, version } = Ext.parseExtension(newExtension);
269
- let index = context.stac_extensions.findIndex(url => {
264
+ let index = context.stac_extensions.findIndex((url) => {
270
265
  if (url === newExtension) {
271
266
  return true;
272
267
  }
@@ -278,8 +273,7 @@ var _ = {
278
273
  });
279
274
  if (index === -1) {
280
275
  context.stac_extensions.push(newExtension);
281
- }
282
- else {
276
+ } else {
283
277
  context.stac_extensions[index] = newExtension;
284
278
  }
285
279
 
@@ -317,8 +311,8 @@ var _ = {
317
311
  objectsToCheck.push(context.links);
318
312
 
319
313
  let obj;
320
- while(obj = objectsToCheck.pop()) {
321
- Object.keys(obj).forEach(key => {
314
+ while ((obj = objectsToCheck.pop())) {
315
+ Object.keys(obj).forEach((key) => {
322
316
  // Add additional objects to check
323
317
  if (Array.isArray(obj.bands)) {
324
318
  objectsToCheck = objectsToCheck.concat(obj.bands);
@@ -335,7 +329,7 @@ var _ = {
335
329
  },
336
330
 
337
331
  mapValues(obj, key, oldValues, newValues) {
338
- let fn = value => {
332
+ let fn = (value) => {
339
333
  let index = oldValues.indexOf(value);
340
334
  if (index >= 0) {
341
335
  return newValues[index];
@@ -344,8 +338,7 @@ var _ = {
344
338
  };
345
339
  if (Array.isArray(obj[key])) {
346
340
  obj[key] = obj[key].map(fn);
347
- }
348
- else if (typeof obj[key] !== 'undefined') {
341
+ } else if (typeof obj[key] !== 'undefined') {
349
342
  obj[key] = fn(obj[key]);
350
343
  }
351
344
  return true;
@@ -361,14 +354,12 @@ var _ = {
361
354
  let condition;
362
355
  if (fromSummary) {
363
356
  if (mergedSummary) {
364
- condition = val => Array.isArray(val);
365
- }
366
- else {
367
- condition = val => Array.isArray(val) && val.length === 1;
357
+ condition = (val) => Array.isArray(val);
358
+ } else {
359
+ condition = (val) => Array.isArray(val) && val.length === 1;
368
360
  }
369
- }
370
- else {
371
- condition = _.isDefined
361
+ } else {
362
+ condition = _.isDefined;
372
363
  }
373
364
  if (condition(obj[key])) {
374
365
  context[key] = fromSummary && !mergedSummary ? obj[key][0] : obj[key];
@@ -391,7 +382,7 @@ var _ = {
391
382
  try {
392
383
  obj[key] = this.toISOString(obj[key]);
393
384
  return true;
394
- } catch (error) { }
385
+ } catch (error) {}
395
386
  }
396
387
  delete obj[key];
397
388
  return false;
@@ -401,11 +392,11 @@ var _ = {
401
392
  if (!(date instanceof Date)) {
402
393
  date = new Date(date);
403
394
  }
404
- return date.toISOString().replace(/\.0+(?=[-\+Z])/, ''); // Don't export milliseconds if not needed
395
+ return date.toISOString().replace(/\.0+(?=[-+Z])/, ''); // Don't export milliseconds if not needed
405
396
  },
406
397
 
407
398
  formatString(obj, key, format) {
408
- const formatter = value => {
399
+ const formatter = (value) => {
409
400
  if (_.is(value, ['string', 'number'])) {
410
401
  return format.replaceAll('{}', value);
411
402
  }
@@ -413,47 +404,61 @@ var _ = {
413
404
  };
414
405
  if (Array.isArray(obj[key])) {
415
406
  obj[key] = obj[key].map(formatter);
416
- }
417
- else {
407
+ } else {
418
408
  obj[key] = formatter(obj[key]);
419
409
  }
420
- }
421
-
410
+ },
422
411
  };
423
412
 
424
413
  var Checksum = {
414
+ // Multicodec codes, see https://github.com/multiformats/multicodec/
415
+ codes: {
416
+ md5: 0xd5,
417
+ sha1: 0x11,
418
+ 'sha2-256': 0x12,
419
+ 'sha3-256': 0x16,
420
+ },
425
421
 
426
- multihash: null,
427
-
428
- hexToUint8(hexString) {
429
- if (hexString.length === 0 || hexString.length % 2 !== 0) {
430
- throw new Error(`The string "${hexString}" is not valid hex.`)
422
+ // Unsigned varint as a hex string.
423
+ encodeVarint(value) {
424
+ let hex = '';
425
+ while (value >= 0x80) {
426
+ hex += ((value & 0x7f) | 0x80).toString(16).padStart(2, '0');
427
+ value >>>= 7;
431
428
  }
432
- return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
429
+ hex += value.toString(16).padStart(2, '0');
430
+ return hex;
433
431
  },
434
432
 
435
- uint8ToHex(bytes) {
436
- return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
433
+ // Prefix a hex digest with its varint-encoded multicodec code and length.
434
+ encodeMultihash(digest, algo) {
435
+ if (!(algo in Checksum.codes)) {
436
+ throw new Error(`Unsupported hash function: ${algo}`);
437
+ }
438
+ if (digest.length === 0 || !/^[0-9a-f]+$/i.test(digest) || digest.length % 2 !== 0) {
439
+ throw new Error(`The string "${digest}" is not valid hex.`);
440
+ }
441
+ const code = Checksum.encodeVarint(Checksum.codes[algo]);
442
+ // Hex string, so byte length is half the string length.
443
+ const length = Checksum.encodeVarint(digest.length / 2);
444
+ return code + length + digest;
437
445
  },
438
446
 
439
447
  toMultihash(obj, key, algo) {
440
- if (!Checksum.multihash || !_.is(obj[key], 'string')) {
448
+ if (!_.is(obj[key], 'string')) {
441
449
  return false;
442
450
  }
443
451
  try {
444
- const encoded = Checksum.multihash.encode(Checksum.hexToUint8(obj[key]), algo);
445
- obj[key] = Checksum.uint8ToHex(encoded);
452
+ obj[key] = Checksum.encodeMultihash(obj[key], algo);
446
453
  return true;
447
454
  } catch (error) {
448
455
  console.warn(error);
449
456
  return false;
450
457
  }
451
- }
452
-
458
+ },
453
459
  };
454
460
 
455
461
  var Catalog = {
456
-
457
462
  migrate(catalog, updateVersionNumber = true) {
458
463
  V.set(catalog);
459
464
  if (updateVersionNumber) {
@@ -470,15 +475,18 @@ var Catalog = {
470
475
 
471
476
  _.runAll(Catalog, catalog, catalog);
472
477
 
478
+ // Migrate extension fields that live directly on the Catalog/Collection root
479
+ // (e.g. order, timestamps, version). Assets, Item properties, bands and Collection
480
+ // summaries are migrated through their own Fields.migrate calls.
481
+ Fields.migrate(catalog, catalog);
482
+
473
483
  V.before('0.8.0') && _.populateExtensions(catalog, 'catalog') && DONE;
474
484
 
475
485
  return catalog;
476
486
  },
477
-
478
487
  };
479
488
 
480
489
  var Collection = {
481
-
482
490
  migrate(collection, updateVersionNumber = true) {
483
491
  Catalog.migrate(collection, updateVersionNumber); // Migrates stac_version, stac_extensions, id, title, description, links
484
492
  collection.type = 'Collection';
@@ -488,11 +496,11 @@ var Collection = {
488
496
  _.ensure(collection, 'license', 'other') && DONE;
489
497
  _.ensure(collection, 'extent', {
490
498
  spatial: {
491
- bbox: []
499
+ bbox: [],
492
500
  },
493
501
  temporal: {
494
- interval: []
495
- }
502
+ interval: [],
503
+ },
496
504
  }) && DONE;
497
505
 
498
506
  _.runAll(Collection, collection, collection);
@@ -510,31 +518,27 @@ var Collection = {
510
518
  },
511
519
 
512
520
  extent(collection) {
513
- _.ensure(collection, "extent", {});
521
+ _.ensure(collection, 'extent', {});
514
522
 
515
523
  if (V.before('0.8.0')) {
516
524
  // Restructure spatial extent
517
525
  if (Array.isArray(collection.extent.spatial)) {
518
526
  collection.extent.spatial = {
519
- bbox: [
520
- collection.extent.spatial
521
- ]
527
+ bbox: [collection.extent.spatial],
522
528
  };
523
529
  }
524
530
  // Restructure temporal extent
525
531
  if (Array.isArray(collection.extent.temporal)) {
526
532
  collection.extent.temporal = {
527
- interval: [
528
- collection.extent.temporal
529
- ]
533
+ interval: [collection.extent.temporal],
530
534
  };
531
535
  }
532
536
  }
533
537
 
534
- _.ensure(collection.extent, "spatial", {});
535
- _.ensure(collection.extent.spatial, "bbox", []);
536
- _.ensure(collection.extent, "temporal", {});
537
- _.ensure(collection.extent.temporal, "interval", []);
538
+ _.ensure(collection.extent, 'spatial', {});
539
+ _.ensure(collection.extent.spatial, 'bbox', []);
540
+ _.ensure(collection.extent, 'temporal', {});
541
+ _.ensure(collection.extent.temporal, 'interval', []);
538
542
 
539
543
  if (V.before('1.0.0-rc.3')) {
540
544
  // The first extent in a Collection is always the overall extent, followed by more specific extents.
@@ -543,37 +547,35 @@ var Collection = {
543
547
  for (let interval of collection.extent.temporal.interval) {
544
548
  if (interval[0] === null) {
545
549
  min = null;
546
- }
547
- else if (typeof interval[0] === 'string' && min !== null) {
550
+ } else if (typeof interval[0] === 'string' && min !== null) {
548
551
  try {
549
552
  let start = new Date(interval[0]);
550
553
  if (typeof min === 'undefined' || start < min) {
551
554
  min = start;
552
555
  }
553
- } catch (error) { }
556
+ } catch (error) {}
554
557
  }
555
558
 
556
559
  if (interval[1] === null) {
557
560
  max = null;
558
- }
559
- else if (typeof interval[1] === 'string' && max !== null) {
561
+ } else if (typeof interval[1] === 'string' && max !== null) {
560
562
  try {
561
563
  let end = new Date(interval[1]);
562
564
  if (typeof max === 'undefined' || end > max) {
563
565
  max = end;
564
566
  }
565
- } catch (error) { }
567
+ } catch (error) {}
566
568
  }
567
569
  }
568
- collection.extent.temporal.interval.unshift([
569
- min ? _.toISOString(min) : null,
570
- max ? _.toISOString(max) : null
571
- ]);
570
+ collection.extent.temporal.interval.unshift([min ? _.toISOString(min) : null, max ? _.toISOString(max) : null]);
572
571
  }
573
572
  if (collection.extent.spatial.bbox.length > 1) {
574
- let count = collection.extent.spatial.bbox.reduce((val, bbox) => Array.isArray(bbox) ? Math.max(bbox.length, val) : val, 4);
573
+ let count = collection.extent.spatial.bbox.reduce(
574
+ (val, bbox) => (Array.isArray(bbox) ? Math.max(bbox.length, val) : val),
575
+ 4,
576
+ );
575
577
  if (count >= 4) {
576
- let union = new Array(count).fill(null);
578
+ let union = Array.from({ length: count }).fill(null);
577
579
  let middle = count / 2;
578
580
  for (let bbox of collection.extent.spatial.bbox) {
579
581
  if (!Array.isArray(bbox) || bbox.length < 4) {
@@ -583,17 +585,14 @@ var Collection = {
583
585
  let c = bbox[i];
584
586
  if (union[i] === null) {
585
587
  union[i] = c;
586
- }
587
- else if (i < middle) {
588
+ } else if (i < middle) {
588
589
  union[i] = Math.min(c, union[i]);
589
- }
590
- else {
590
+ } else {
591
591
  union[i] = Math.max(c, union[i]);
592
592
  }
593
-
594
593
  }
595
594
  }
596
- if (union.findIndex(c => c === null) === -1) {
595
+ if (union.findIndex((c) => c === null) === -1) {
597
596
  collection.extent.spatial.bbox.unshift(union);
598
597
  }
599
598
  }
@@ -628,13 +627,11 @@ var Collection = {
628
627
  minimum: prop.extent[0],
629
628
  maximum: prop.extent[1],
630
629
  };
631
- }
632
- else if (Array.isArray(prop.values)) {
633
- if (prop.values.filter(v => Array.isArray(v)).length === prop.values.length) {
630
+ } else if (Array.isArray(prop.values)) {
631
+ if (prop.values.filter((v) => Array.isArray(v)).length === prop.values.length) {
634
632
  // If it is an array of arrays, merge the arrays
635
633
  collection.summaries[key] = prop.values.reduce((a, b) => a.concat(b), []);
636
- }
637
- else {
634
+ } else {
638
635
  collection.summaries[key] = prop.values;
639
636
  }
640
637
  }
@@ -645,7 +642,11 @@ var Collection = {
645
642
  // Migrate Commons extension - part 2
646
643
  // Move properties to (single element) summaries if the Collection is standalone
647
644
  // see also https://github.com/stac-utils/stac-migrate/issues/3
648
- if (V.before('1.0.0-beta.1') && _.isObject(collection.properties) && !collection.links.find(link => ['child', 'item'].includes(link.rel))) {
645
+ if (
646
+ V.before('1.0.0-beta.1') &&
647
+ _.isObject(collection.properties) &&
648
+ !collection.links.find((link) => ['child', 'item'].includes(link.rel))
649
+ ) {
649
650
  for (let key in collection.properties) {
650
651
  let value = collection.properties[key];
651
652
  if (!Array.isArray(value)) {
@@ -656,7 +657,7 @@ var Collection = {
656
657
  }
657
658
 
658
659
  if (V.before('1.0.0-rc.1')) {
659
- _.mapObject(collection.summaries, val => {
660
+ _.mapObject(collection.summaries, (val) => {
660
661
  _.rename(val, 'min', 'minimum') && DONE;
661
662
  _.rename(val, 'max', 'maximum') && DONE;
662
663
  return val;
@@ -667,21 +668,27 @@ var Collection = {
667
668
  Fields.migrate(collection.summaries, collection, true);
668
669
 
669
670
  // Some fields should usually be on root-level if there's only one element
670
- _.moveTo(collection.summaries, 'sci:doi', collection, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
671
- _.moveTo(collection.summaries, 'sci:publications', collection, true, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
672
- _.moveTo(collection.summaries, 'sci:citation', collection, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
673
- _.moveTo(collection.summaries, 'cube:dimensions', collection, true) && _.addExtension(collection, SCHEMAS.datacube) && DONE;
671
+ _.moveTo(collection.summaries, 'sci:doi', collection, true) &&
672
+ _.addExtension(collection, SCHEMAS.scientific) &&
673
+ DONE;
674
+ _.moveTo(collection.summaries, 'sci:publications', collection, true, true) &&
675
+ _.addExtension(collection, SCHEMAS.scientific) &&
676
+ DONE;
677
+ _.moveTo(collection.summaries, 'sci:citation', collection, true) &&
678
+ _.addExtension(collection, SCHEMAS.scientific) &&
679
+ DONE;
680
+ _.moveTo(collection.summaries, 'cube:dimensions', collection, true) &&
681
+ _.addExtension(collection, SCHEMAS.datacube) &&
682
+ DONE;
674
683
 
675
684
  // Remove summary field if empty
676
685
  if (Object.keys(collection.summaries).length === 0) {
677
686
  delete collection.summaries;
678
687
  }
679
- }
680
-
688
+ },
681
689
  };
682
690
 
683
691
  var Item = {
684
-
685
692
  migrate(item, collection = null, updateVersionNumber = true) {
686
693
  V.set(item);
687
694
  if (updateVersionNumber) {
@@ -721,27 +728,23 @@ var Item = {
721
728
  (V.before('0.8.0') || commons) && _.populateExtensions(item, 'item') && DONE;
722
729
 
723
730
  return item;
724
- }
725
-
731
+ },
726
732
  };
727
733
 
728
734
  var CollectionCollection = {
729
-
730
735
  migrate(object, updateVersionNumber = true) {
731
736
  _.ensure(object, 'collections', []) && DONE;
732
737
  _.ensure(object, 'links', []) && DONE;
733
738
 
734
739
  _.runAll(CollectionCollection, object, object);
735
740
 
736
- object.collections = object.collections.map(collection => Collection.migrate(collection, updateVersionNumber));
741
+ object.collections = object.collections.map((collection) => Collection.migrate(collection, updateVersionNumber));
737
742
 
738
743
  return object;
739
744
  },
740
-
741
745
  };
742
746
 
743
747
  var ItemCollection = {
744
-
745
748
  migrate(itemCollection, updateVersionNumber = true) {
746
749
  _.ensure(itemCollection, 'type', 'FeatureCollection') && DONE;
747
750
  _.ensure(itemCollection, 'features', []) && DONE;
@@ -749,15 +752,15 @@ var ItemCollection = {
749
752
 
750
753
  _.runAll(ItemCollection, itemCollection, itemCollection);
751
754
 
752
- itemCollection.features = itemCollection.features.map(feature => Item.migrate(feature, null, updateVersionNumber));
755
+ itemCollection.features = itemCollection.features.map((feature) =>
756
+ Item.migrate(feature, null, updateVersionNumber),
757
+ );
753
758
 
754
759
  return itemCollection;
755
760
  },
756
-
757
761
  };
758
762
 
759
763
  var Asset = {
760
-
761
764
  migrateAll(context, field = 'assets') {
762
765
  for (let key in context[field]) {
763
766
  Asset.migrate(context[field][key], context);
@@ -773,21 +776,22 @@ var Asset = {
773
776
  },
774
777
 
775
778
  mediaTypes(asset) {
776
- _.is(asset.type, 'string') && _.mapValues(
777
- asset, 'type',
778
- ['image/vnd.stac.geotiff', 'image/vnd.stac.geotiff; cloud-optimized=true'],
779
- ['image/tiff; application=geotiff', 'image/tiff; application=geotiff; profile=cloud-optimized']
780
- );
781
- }
782
-
779
+ _.is(asset.type, 'string') &&
780
+ _.mapValues(
781
+ asset,
782
+ 'type',
783
+ ['image/vnd.stac.geotiff', 'image/vnd.stac.geotiff; cloud-optimized=true'],
784
+ ['image/tiff; application=geotiff', 'image/tiff; application=geotiff; profile=cloud-optimized'],
785
+ );
786
+ },
783
787
  };
784
788
 
785
789
  var Band = {
786
-
787
790
  migrateAll(obj, context) {
788
-
789
- if (V.before('1.0.0')) { // Not sure when the index-based bands were removed
790
- const bands = _.isObject(context.properties) && Array.isArray(context.properties.bands) ? context.properties.bands : [];
791
+ if (V.before('1.0.0')) {
792
+ // Not sure when the index-based bands were removed
793
+ const bands =
794
+ _.isObject(context.properties) && Array.isArray(context.properties.bands) ? context.properties.bands : [];
791
795
  if (Array.isArray(obj['eo:bands'])) {
792
796
  for (let i in obj['eo:bands']) {
793
797
  let band = obj['eo:bands'][i];
@@ -802,11 +806,11 @@ var Band = {
802
806
  }
803
807
  }
804
808
 
805
- if (V.before("1.1.0-beta.1") && (Array.isArray(obj["raster:bands"]) || Array.isArray(obj["eo:bands"]))) {
806
- _.ensure(obj, "bands", []);
809
+ if (V.before('1.1.0-beta.1') && (Array.isArray(obj['raster:bands']) || Array.isArray(obj['eo:bands']))) {
810
+ _.ensure(obj, 'bands', []);
807
811
 
808
- const raster = obj["raster:bands"] || [];
809
- const eo = obj["eo:bands"] || [];
812
+ const raster = obj['raster:bands'] || [];
813
+ const eo = obj['eo:bands'] || [];
810
814
  const length = Math.max(raster.length, eo.length);
811
815
  for (let i = 0; i < length; i++) {
812
816
  _.ensure(obj.bands, i, {});
@@ -814,10 +818,9 @@ var Band = {
814
818
  obj.bands[i] = Band.migrate(obj.bands[i], context);
815
819
  }
816
820
 
817
- delete obj["raster:bands"];
818
- delete obj["eo:bands"];
821
+ delete obj['raster:bands'];
822
+ delete obj['eo:bands'];
819
823
  }
820
-
821
824
  },
822
825
 
823
826
  migrate(band, context) {
@@ -846,12 +849,10 @@ var Band = {
846
849
  _.rename(band, 'offset', 'raster:offset') && DONE;
847
850
  _.rename(band, 'histogram', 'raster:histogram') && DONE;
848
851
  }
849
- }
850
-
852
+ },
851
853
  };
852
854
 
853
855
  var Fields = {
854
-
855
856
  migrate(obj, context, summaries = false) {
856
857
  _.runAll(Fields, obj, context, summaries);
857
858
 
@@ -885,31 +886,48 @@ var Fields = {
885
886
  },
886
887
 
887
888
  checksum(obj, context) {
888
- if (V.before('0.9.0') && Checksum.multihash) {
889
- _.rename(obj, 'checksum:md5', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'md5') && DONE;
890
- _.rename(obj, 'checksum:sha1', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha1') && DONE;
889
+ if (V.before('0.9.0')) {
890
+ _.rename(obj, 'checksum:md5', 'checksum:multihash') &&
891
+ Checksum.toMultihash(obj, 'checksum:multihash', 'md5') &&
892
+ DONE;
893
+ _.rename(obj, 'checksum:sha1', 'checksum:multihash') &&
894
+ Checksum.toMultihash(obj, 'checksum:multihash', 'sha1') &&
895
+ DONE;
891
896
  // We assume sha2/3-256 although that may fail in some cases and other lengths are chosen
892
897
  // Never seen this implemtned in the wild, so let's try this until another use case comes up
893
- _.rename(obj, 'checksum:sha2', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha2-256') && DONE;
894
- _.rename(obj, 'checksum:sha3', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha3-256') && DONE;
898
+ _.rename(obj, 'checksum:sha2', 'checksum:multihash') &&
899
+ Checksum.toMultihash(obj, 'checksum:multihash', 'sha2-256') &&
900
+ DONE;
901
+ _.rename(obj, 'checksum:sha3', 'checksum:multihash') &&
902
+ Checksum.toMultihash(obj, 'checksum:multihash', 'sha3-256') &&
903
+ DONE;
895
904
  }
896
905
 
897
- V.before('1.0.0-rc.1') && _.rename(obj, 'checksum:multihash', 'file:checksum') && _.addExtension(context, SCHEMAS.file) && DONE;
906
+ V.before('1.0.0-rc.1') &&
907
+ _.rename(obj, 'checksum:multihash', 'file:checksum') &&
908
+ _.addExtension(context, SCHEMAS.file) &&
909
+ DONE;
898
910
 
899
911
  _.removeExtension(context, 'checksum');
900
912
  },
901
913
 
902
914
  classification(obj, context) {
903
915
  if (V.before('1.1.0', 'classification')) {
904
- _.forAll(obj, 'classification:classes', o => _.rename(o, 'color-hint', 'color_hint')) && DONE;
916
+ _.forAll(obj, 'classification:classes', (o) => _.rename(o, 'color-hint', 'color_hint')) && DONE;
905
917
  }
906
918
  if (V.before('2.0.0', 'classification')) {
907
- _.forAll(obj, 'classification:classes', o => _.ensure(o, 'name', o.description)) && DONE;
919
+ _.forAll(obj, 'classification:classes', (o) => _.ensure(o, 'name', o.description)) && DONE;
908
920
  }
909
921
 
910
922
  _.upgradeExtension(context, SCHEMAS.classification);
911
923
  },
912
924
 
925
+ cf(obj, context) {
926
+ _.rename(obj, 'cf:parameter', 'cf:standard_name') && DONE;
927
+
928
+ _.upgradeExtension(context, SCHEMAS.cf);
929
+ },
930
+
913
931
  cube(obj, context) {
914
932
  // We'd need to convert proj strings to something else for v1.0 -> v2.0, but that's unfeasible here.
915
933
  // Nothing else to do here.
@@ -970,9 +988,21 @@ var Fields = {
970
988
  _.upgradeExtension(context, SCHEMAS.label);
971
989
  },
972
990
 
991
+ order(obj, context) {
992
+ if (_.rename(obj, 'order:expiration_date', 'expires')) {
993
+ _.toUTC(obj, 'expires');
994
+ _.addExtension(context, SCHEMAS.timestamps) && DONE;
995
+ }
996
+
997
+ _.upgradeExtension(context, SCHEMAS.order);
998
+ },
999
+
973
1000
  pc(obj, context) {
974
1001
  V.before('0.8.0') && _.rename(obj, 'pc:schema', 'pc:schemas') && DONE;
975
1002
 
1003
+ // pc:encoding was removed in Point Cloud 2.0.0 (superseded by the asset's media type),
1004
+ // but it cannot be reliably reconstructed, so it is left untouched.
1005
+
976
1006
  _.upgradeExtension(context, SCHEMAS.pointcloud);
977
1007
  },
978
1008
 
@@ -982,6 +1012,12 @@ var Fields = {
982
1012
  _.upgradeExtension(context, SCHEMAS.processing);
983
1013
  },
984
1014
 
1015
+ product(obj, context) {
1016
+ // Nothing to do
1017
+
1018
+ _.upgradeExtension(context, SCHEMAS.product);
1019
+ },
1020
+
985
1021
  proj(obj, context) {
986
1022
  _.rename(obj, 'proj:epsg', 'proj:code') && _.formatString(obj, 'proj:code', 'EPSG:{}') && DONE;
987
1023
 
@@ -995,12 +1031,21 @@ var Fields = {
995
1031
  sar(obj, context, summary) {
996
1032
  // Which version have they been (re)moved?
997
1033
  _.rename(obj, 'sar:incidence_angle', 'view:incidence_angle') && _.addExtension(context, SCHEMAS.view) && DONE;
998
- _.rename(obj, 'sar:pass_direction', 'sat:orbit_state') && _.mapValues(obj, 'sat:orbit_state', [null], ['geostationary']) && _.addExtension(context, SCHEMAS.sat) && DONE;
1034
+ _.rename(obj, 'sar:pass_direction', 'sat:orbit_state') &&
1035
+ _.mapValues(obj, 'sat:orbit_state', [null], ['geostationary']) &&
1036
+ _.addExtension(context, SCHEMAS.sat) &&
1037
+ DONE;
999
1038
 
1000
1039
  if (V.before('0.7.0')) {
1001
1040
  _.flattenArray(obj, 'sar:resolution', ['sar:resolution_range', 'sar:resolution_azimuth'], summary) && DONE;
1002
- _.flattenArray(obj, 'sar:pixel_spacing', ['sar:pixel_spacing_range', 'sar:pixel_spacing_azimuth'], summary) && DONE;
1003
- _.flattenArray(obj, 'sar:looks', ['sar:looks_range', 'sar:looks_azimuth', 'sar:looks_equivalent_number'], summary) && DONE;
1041
+ _.flattenArray(obj, 'sar:pixel_spacing', ['sar:pixel_spacing_range', 'sar:pixel_spacing_azimuth'], summary) &&
1042
+ DONE;
1043
+ _.flattenArray(
1044
+ obj,
1045
+ 'sar:looks',
1046
+ ['sar:looks_range', 'sar:looks_azimuth', 'sar:looks_equivalent_number'],
1047
+ summary,
1048
+ ) && DONE;
1004
1049
  _.rename(obj, 'sar:off_nadir', 'view:off_nadir') && _.addExtension(context, SCHEMAS.view) && DONE;
1005
1050
  }
1006
1051
 
@@ -1010,8 +1055,21 @@ var Fields = {
1010
1055
  _.rename(obj, 'sar:constellation', 'constellation') && DONE;
1011
1056
  _.rename(obj, 'sar:type', 'sar:product_type') && DONE;
1012
1057
  _.rename(obj, 'sar:polarization', 'sar:polarizations') && DONE;
1013
- _.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) && _.rename(obj, 'sar:absolute_orbit', 'sat:absolute_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
1014
- _.flattenOneElementArray(obj, 'sar:relative_orbit', summary) && _.rename(obj, 'sar:relative_orbit', 'sat:relative_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
1058
+ _.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) &&
1059
+ _.rename(obj, 'sar:absolute_orbit', 'sat:absolute_orbit') &&
1060
+ _.addExtension(context, SCHEMAS.sat) &&
1061
+ DONE;
1062
+ _.flattenOneElementArray(obj, 'sar:relative_orbit', summary) &&
1063
+ _.rename(obj, 'sar:relative_orbit', 'sat:relative_orbit') &&
1064
+ _.addExtension(context, SCHEMAS.sat) &&
1065
+ DONE;
1066
+ }
1067
+
1068
+ _.rename(obj, 'sar:product_type', 'product:type') && _.addExtension(context, SCHEMAS.product) && DONE;
1069
+
1070
+ if (_.rename(obj, 'sar:instrument_mode', 'instrument_modes')) {
1071
+ !summary && _.toArray(obj, 'instrument_modes');
1072
+ _.addExtension(context, SCHEMAS.instruments) && DONE;
1015
1073
  }
1016
1074
 
1017
1075
  _.upgradeExtension(context, SCHEMAS.sar);
@@ -1035,7 +1093,8 @@ var Fields = {
1035
1093
  _.upgradeExtension(context, SCHEMAS.scientific);
1036
1094
  },
1037
1095
 
1038
- item(obj) { // Single Item
1096
+ item(obj) {
1097
+ // Single Item
1039
1098
  if (V.before('0.8.0')) {
1040
1099
  _.rename(obj, 'item:license', 'license') && DONE;
1041
1100
  _.rename(obj, 'item:providers', 'providers') && DONE;
@@ -1053,12 +1112,10 @@ var Fields = {
1053
1112
  // Nothing to do
1054
1113
 
1055
1114
  _.upgradeExtension(context, SCHEMAS.view);
1056
- }
1057
-
1115
+ },
1058
1116
  };
1059
1117
 
1060
1118
  var Migrate = {
1061
-
1062
1119
  item(item, collection = null, updateVersionNumber = true) {
1063
1120
  return Item.migrate(item, collection, updateVersionNumber);
1064
1121
  },
@@ -1082,25 +1139,22 @@ var Migrate = {
1082
1139
  stac(object, updateVersionNumber = true) {
1083
1140
  if (object.type === 'Feature') {
1084
1141
  return Migrate.item(object, null, updateVersionNumber);
1085
- }
1086
- else if (object.type === 'FeatureCollection') {
1142
+ } else if (object.type === 'FeatureCollection') {
1087
1143
  return Migrate.itemCollection(object, updateVersionNumber);
1088
- }
1089
- else if (object.type === 'Collection' || (!object.type && _.isDefined(object.extent) && _.isDefined(object.license))) {
1144
+ } else if (
1145
+ object.type === 'Collection' ||
1146
+ (!object.type && _.isDefined(object.extent) && _.isDefined(object.license))
1147
+ ) {
1090
1148
  return Migrate.collection(object, updateVersionNumber);
1091
- }
1092
- else if (!object.type && Array.isArray(object.collections)) {
1149
+ } else if (!object.type && Array.isArray(object.collections)) {
1093
1150
  return Migrate.collectionCollection(object, updateVersionNumber);
1094
- }
1095
- else {
1151
+ } else {
1096
1152
  return Migrate.catalog(object, updateVersionNumber);
1097
1153
  }
1098
1154
  },
1099
1155
 
1100
- enableMultihash(multihash) {
1101
- Checksum.multihash = multihash;
1102
- }
1103
-
1156
+ // Deprecated no-op; multihash conversion is always on.
1157
+ enableMultihash() {},
1104
1158
  };
1105
1159
 
1106
1160
  module.exports = Migrate;