keepset 0.24.0 → 0.26.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.
@@ -521,6 +521,18 @@ const ParserSchema = z.discriminatedUnion("format", [
521
521
  format: z.literal("solomon"),
522
522
  encoding: EncodingSchema
523
523
  }).strict(),
524
+ z.object({
525
+ format: z.literal("top"),
526
+ encoding: EncodingSchema
527
+ }).strict(),
528
+ z.object({
529
+ format: z.literal("toptw"),
530
+ encoding: EncodingSchema
531
+ }).strict(),
532
+ z.object({
533
+ format: z.literal("tsplib"),
534
+ encoding: EncodingSchema
535
+ }).strict(),
524
536
  z.object({
525
537
  format: z.literal("csv-table"),
526
538
  encoding: EncodingSchema,
@@ -980,7 +992,7 @@ var catalogue_default = {
980
992
  "id": "keepset",
981
993
  "name": "keepset",
982
994
  "description": "The keepset catalogue: datasets with machine-readable recipes that fetch each one and normalise it into its category's target shape.",
983
- "version": "1.12.0",
995
+ "version": "1.14.0",
984
996
  "repository": "https://github.com/Mearman/keepset",
985
997
  "namespaceAliases": {
986
998
  "networkrepository": ["nr"],
@@ -8635,7 +8647,7 @@ var catalogue_default = {
8635
8647
  "location",
8636
8648
  "travelTime"
8637
8649
  ],
8638
- "category": "or/v1",
8650
+ "category": "or-set/v1",
8639
8651
  "sources": [{
8640
8652
  "url": "https://www.mech.kuleuven.be/en/mim/op",
8641
8653
  "retrieved": "2026-10-19",
@@ -8648,11 +8660,75 @@ var catalogue_default = {
8648
8660
  "licence": { "kind": "unrecorded" },
8649
8661
  "citations": [],
8650
8662
  "recipe": {
8651
- "status": "not-fetchable",
8652
- "reason": "The download is a collection of 60 text instance files of one 100 node point set under different budgets (planar coordinates and a prize, with no time quantities), and a collection is more than one or/v1 document, so no recipe reads it."
8663
+ "status": "fetchable",
8664
+ "source": 1,
8665
+ "archive": {
8666
+ "kind": "zip",
8667
+ "members": { "extensions": [".txt"] }
8668
+ },
8669
+ "parser": {
8670
+ "format": "top",
8671
+ "encoding": "utf-8"
8672
+ }
8653
8673
  },
8654
- "crosswalk": { "file": "crosswalks/or-v1/nodes.json" },
8655
- "expectedCounts": {},
8674
+ "crosswalk": {
8675
+ "source": "top",
8676
+ "target": "or-set/v1",
8677
+ "rules": [
8678
+ {
8679
+ "op": "move",
8680
+ "from": "meta",
8681
+ "to": "meta"
8682
+ },
8683
+ {
8684
+ "op": "move",
8685
+ "from": "members[].id",
8686
+ "to": "instances[].id"
8687
+ },
8688
+ {
8689
+ "op": "move",
8690
+ "from": "members[].meta.points",
8691
+ "to": "instances[].meta.points"
8692
+ },
8693
+ {
8694
+ "op": "move",
8695
+ "from": "members[].meta.paths",
8696
+ "to": "instances[].meta.paths"
8697
+ },
8698
+ {
8699
+ "op": "move",
8700
+ "from": "members[].meta.budget",
8701
+ "to": "instances[].meta.budget"
8702
+ },
8703
+ {
8704
+ "op": "move",
8705
+ "from": "members[].nodes[].id",
8706
+ "to": "instances[].nodes[].id"
8707
+ },
8708
+ {
8709
+ "op": "move",
8710
+ "from": "members[].nodes[].score",
8711
+ "to": "instances[].nodes[].prize"
8712
+ },
8713
+ {
8714
+ "op": "move",
8715
+ "from": "members[].nodes[].x",
8716
+ "to": "instances[].nodes[].x"
8717
+ },
8718
+ {
8719
+ "op": "move",
8720
+ "from": "members[].nodes[].y",
8721
+ "to": "instances[].nodes[].y"
8722
+ },
8723
+ {
8724
+ "op": "move",
8725
+ "from": "members[].nodes[].role",
8726
+ "to": "instances[].nodes[].role"
8727
+ }
8728
+ ],
8729
+ "oneWay": []
8730
+ },
8731
+ "expectedCounts": { "instances": 60 },
8656
8732
  "properties": { "surveyId": "or-0" }
8657
8733
  },
8658
8734
  {
@@ -8669,7 +8745,7 @@ var catalogue_default = {
8669
8745
  "duration",
8670
8746
  "travelTime"
8671
8747
  ],
8672
- "category": "or/v1",
8748
+ "category": "or-set/v1",
8673
8749
  "sources": [{
8674
8750
  "url": "https://www.mech.kuleuven.be/en/mim/op",
8675
8751
  "retrieved": "2026-10-19",
@@ -8682,12 +8758,89 @@ var catalogue_default = {
8682
8758
  "licence": { "kind": "unrecorded" },
8683
8759
  "citations": [],
8684
8760
  "recipe": {
8685
- "status": "not-fetchable",
8686
- "reason": "The download is a collection of Solomon style time window instance files (planar coordinates, service durations, prizes and windows in abstract units, not seconds), and a collection is more than one or/v1 document, so no recipe reads it."
8761
+ "status": "fetchable",
8762
+ "source": 1,
8763
+ "archive": {
8764
+ "kind": "zip",
8765
+ "members": { "extensions": [".txt"] }
8766
+ },
8767
+ "parser": {
8768
+ "format": "toptw",
8769
+ "encoding": "utf-8"
8770
+ }
8687
8771
  },
8688
- "crosswalk": { "file": "crosswalks/or-v1/nodes.json" },
8689
- "expectedCounts": {},
8690
- "properties": { "surveyId": "or-1" }
8772
+ "crosswalk": {
8773
+ "source": "toptw",
8774
+ "target": "or-set/v1",
8775
+ "rules": [
8776
+ {
8777
+ "op": "move",
8778
+ "from": "meta",
8779
+ "to": "meta"
8780
+ },
8781
+ {
8782
+ "op": "move",
8783
+ "from": "members[].id",
8784
+ "to": "instances[].id"
8785
+ },
8786
+ {
8787
+ "op": "move",
8788
+ "from": "members[].meta.paths",
8789
+ "to": "instances[].meta.paths"
8790
+ },
8791
+ {
8792
+ "op": "move",
8793
+ "from": "members[].meta.customers",
8794
+ "to": "instances[].meta.customers"
8795
+ },
8796
+ {
8797
+ "op": "move",
8798
+ "from": "members[].meta.capacity",
8799
+ "to": "instances[].meta.capacity"
8800
+ },
8801
+ {
8802
+ "op": "move",
8803
+ "from": "members[].nodes[].id",
8804
+ "to": "instances[].nodes[].id"
8805
+ },
8806
+ {
8807
+ "op": "move",
8808
+ "from": "members[].nodes[].close",
8809
+ "to": "instances[].nodes[].deadline"
8810
+ },
8811
+ {
8812
+ "op": "move",
8813
+ "from": "members[].nodes[].profit",
8814
+ "to": "instances[].nodes[].prize"
8815
+ },
8816
+ {
8817
+ "op": "move",
8818
+ "from": "members[].nodes[].x",
8819
+ "to": "instances[].nodes[].x"
8820
+ },
8821
+ {
8822
+ "op": "move",
8823
+ "from": "members[].nodes[].y",
8824
+ "to": "instances[].nodes[].y"
8825
+ },
8826
+ {
8827
+ "op": "move",
8828
+ "from": "members[].nodes[].service",
8829
+ "to": "instances[].nodes[].service"
8830
+ },
8831
+ {
8832
+ "op": "move",
8833
+ "from": "members[].nodes[].open",
8834
+ "to": "instances[].nodes[].open"
8835
+ }
8836
+ ],
8837
+ "oneWay": []
8838
+ },
8839
+ "expectedCounts": { "instances": 29 },
8840
+ "properties": {
8841
+ "surveyId": "or-1",
8842
+ "timeUnit": "abstract units"
8843
+ }
8691
8844
  },
8692
8845
  {
8693
8846
  "id": "lastfm-hgb",
@@ -56160,20 +56313,119 @@ var catalogue_default = {
56160
56313
  "territory",
56161
56314
  "travelTime"
56162
56315
  ],
56163
- "category": "or/v1",
56316
+ "category": "or-set/v1",
56164
56317
  "sources": [{
56165
56318
  "url": "http://www.vrp-rep.org/datasets/download/angelelli-et-al-2014.zip",
56166
56319
  "retrieved": "2026-10-19",
56167
56320
  "status": "live"
56321
+ }, {
56322
+ "url": "https://www.vrp-rep.org/datasets/download/angelelli-et-al-2014.zip",
56323
+ "retrieved": "2026-10-19",
56324
+ "status": "live"
56168
56325
  }],
56169
56326
  "licence": { "kind": "unrecorded" },
56170
56327
  "citations": [],
56171
56328
  "recipe": {
56172
- "status": "not-fetchable",
56173
- "reason": "The download is a collection of TSPLIB style instance files whose prizes belong to overlapping clusters of nodes and not to nodes, and a collection is more than one or/v1 document, so no recipe reads it."
56329
+ "status": "fetchable",
56330
+ "source": 1,
56331
+ "archive": {
56332
+ "kind": "zip",
56333
+ "members": { "extensions": [".cop"] }
56334
+ },
56335
+ "parser": {
56336
+ "format": "tsplib",
56337
+ "encoding": "utf-8"
56338
+ }
56174
56339
  },
56175
- "crosswalk": { "file": "crosswalks/or-v1/nodes.json" },
56176
- "expectedCounts": {},
56340
+ "crosswalk": {
56341
+ "source": "tsplib",
56342
+ "target": "or-set/v1",
56343
+ "rules": [
56344
+ {
56345
+ "op": "move",
56346
+ "from": "meta",
56347
+ "to": "meta"
56348
+ },
56349
+ {
56350
+ "op": "move",
56351
+ "from": "members[].id",
56352
+ "to": "instances[].id"
56353
+ },
56354
+ {
56355
+ "op": "move",
56356
+ "from": "members[].meta.name",
56357
+ "to": "instances[].meta.name"
56358
+ },
56359
+ {
56360
+ "op": "move",
56361
+ "from": "members[].meta.type",
56362
+ "to": "instances[].meta.type"
56363
+ },
56364
+ {
56365
+ "op": "move",
56366
+ "from": "members[].meta.budget",
56367
+ "to": "instances[].meta.budget"
56368
+ },
56369
+ {
56370
+ "op": "move",
56371
+ "from": "members[].meta.edgeWeightType",
56372
+ "to": "instances[].meta.edgeWeightType"
56373
+ },
56374
+ {
56375
+ "op": "move",
56376
+ "from": "members[].meta.setPrizes",
56377
+ "to": "instances[].meta.setPrizes"
56378
+ },
56379
+ {
56380
+ "op": "move",
56381
+ "from": "members[].nodes[].id",
56382
+ "to": "instances[].nodes[].id"
56383
+ },
56384
+ {
56385
+ "op": "move",
56386
+ "from": "members[].nodes[].lat",
56387
+ "to": "instances[].nodes[].lat"
56388
+ },
56389
+ {
56390
+ "op": "move",
56391
+ "from": "members[].nodes[].lon",
56392
+ "to": "instances[].nodes[].lon"
56393
+ },
56394
+ {
56395
+ "op": "move",
56396
+ "from": "members[].nodes[].x",
56397
+ "to": "instances[].nodes[].x"
56398
+ },
56399
+ {
56400
+ "op": "move",
56401
+ "from": "members[].nodes[].y",
56402
+ "to": "instances[].nodes[].y"
56403
+ },
56404
+ {
56405
+ "op": "move",
56406
+ "from": "members[].nodes[].clusters",
56407
+ "to": "instances[].nodes[].clusters"
56408
+ }
56409
+ ],
56410
+ "oneWay": [
56411
+ {
56412
+ "op": "drop",
56413
+ "from": "members[].meta.totalPrize",
56414
+ "direction": "encode"
56415
+ },
56416
+ {
56417
+ "op": "drop",
56418
+ "from": "members[].nodes[].prize",
56419
+ "direction": "encode"
56420
+ },
56421
+ {
56422
+ "op": "drop",
56423
+ "from": "members[].nodes[].probability",
56424
+ "direction": "encode"
56425
+ }
56426
+ ]
56427
+ },
56428
+ "expectedCounts": { "instances": 924 },
56177
56429
  "properties": { "surveyId": "or-4" }
56178
56430
  },
56179
56431
  {
@@ -56216,20 +56468,116 @@ var catalogue_default = {
56216
56468
  "location",
56217
56469
  "travelTime"
56218
56470
  ],
56219
- "category": "or/v1",
56471
+ "category": "or-set/v1",
56220
56472
  "sources": [{
56221
56473
  "url": "http://www.vrp-rep.org/datasets/download/angelelli-et-al-2017.zip",
56222
56474
  "retrieved": "2026-10-19",
56223
56475
  "status": "live"
56476
+ }, {
56477
+ "url": "https://www.vrp-rep.org/datasets/download/angelelli-et-al-2017.zip",
56478
+ "retrieved": "2026-10-19",
56479
+ "status": "live"
56224
56480
  }],
56225
56481
  "licence": { "kind": "unrecorded" },
56226
56482
  "citations": [],
56227
56483
  "recipe": {
56228
- "status": "not-fetchable",
56229
- "reason": "The download is a collection of TSPLIB style instance files with a prize and probability section, half of which carry no coordinates and some of which use the degrees and minutes form, and a collection is more than one or/v1 document, so no recipe reads it."
56484
+ "status": "fetchable",
56485
+ "source": 1,
56486
+ "archive": {
56487
+ "kind": "zip",
56488
+ "members": { "extensions": [".pop"] }
56489
+ },
56490
+ "parser": {
56491
+ "format": "tsplib",
56492
+ "encoding": "utf-8"
56493
+ }
56230
56494
  },
56231
- "crosswalk": { "file": "crosswalks/or-v1/nodes.json" },
56232
- "expectedCounts": {},
56495
+ "crosswalk": {
56496
+ "source": "tsplib",
56497
+ "target": "or-set/v1",
56498
+ "rules": [
56499
+ {
56500
+ "op": "move",
56501
+ "from": "meta",
56502
+ "to": "meta"
56503
+ },
56504
+ {
56505
+ "op": "move",
56506
+ "from": "members[].id",
56507
+ "to": "instances[].id"
56508
+ },
56509
+ {
56510
+ "op": "move",
56511
+ "from": "members[].meta.name",
56512
+ "to": "instances[].meta.name"
56513
+ },
56514
+ {
56515
+ "op": "move",
56516
+ "from": "members[].meta.type",
56517
+ "to": "instances[].meta.type"
56518
+ },
56519
+ {
56520
+ "op": "move",
56521
+ "from": "members[].meta.budget",
56522
+ "to": "instances[].meta.budget"
56523
+ },
56524
+ {
56525
+ "op": "move",
56526
+ "from": "members[].meta.edgeWeightType",
56527
+ "to": "instances[].meta.edgeWeightType"
56528
+ },
56529
+ {
56530
+ "op": "move",
56531
+ "from": "members[].meta.totalPrize",
56532
+ "to": "instances[].meta.totalPrize"
56533
+ },
56534
+ {
56535
+ "op": "move",
56536
+ "from": "members[].nodes[].id",
56537
+ "to": "instances[].nodes[].id"
56538
+ },
56539
+ {
56540
+ "op": "move",
56541
+ "from": "members[].nodes[].lat",
56542
+ "to": "instances[].nodes[].lat"
56543
+ },
56544
+ {
56545
+ "op": "move",
56546
+ "from": "members[].nodes[].lon",
56547
+ "to": "instances[].nodes[].lon"
56548
+ },
56549
+ {
56550
+ "op": "move",
56551
+ "from": "members[].nodes[].prize",
56552
+ "to": "instances[].nodes[].prize"
56553
+ },
56554
+ {
56555
+ "op": "move",
56556
+ "from": "members[].nodes[].probability",
56557
+ "to": "instances[].nodes[].prizeProbability"
56558
+ },
56559
+ {
56560
+ "op": "move",
56561
+ "from": "members[].nodes[].x",
56562
+ "to": "instances[].nodes[].x"
56563
+ },
56564
+ {
56565
+ "op": "move",
56566
+ "from": "members[].nodes[].y",
56567
+ "to": "instances[].nodes[].y"
56568
+ }
56569
+ ],
56570
+ "oneWay": [{
56571
+ "op": "drop",
56572
+ "from": "members[].meta.setPrizes",
56573
+ "direction": "encode"
56574
+ }, {
56575
+ "op": "drop",
56576
+ "from": "members[].nodes[].clusters",
56577
+ "direction": "encode"
56578
+ }]
56579
+ },
56580
+ "expectedCounts": { "instances": 264 },
56233
56581
  "properties": { "surveyId": "or-3" }
56234
56582
  },
56235
56583
  {
@@ -146,6 +146,24 @@ declare const ParserSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
146
146
  "utf-8": "utf-8";
147
147
  latin1: "latin1";
148
148
  }>;
149
+ }, z.core.$strict>, z.ZodObject<{
150
+ format: z.ZodLiteral<"top">;
151
+ encoding: z.ZodEnum<{
152
+ "utf-8": "utf-8";
153
+ latin1: "latin1";
154
+ }>;
155
+ }, z.core.$strict>, z.ZodObject<{
156
+ format: z.ZodLiteral<"toptw">;
157
+ encoding: z.ZodEnum<{
158
+ "utf-8": "utf-8";
159
+ latin1: "latin1";
160
+ }>;
161
+ }, z.core.$strict>, z.ZodObject<{
162
+ format: z.ZodLiteral<"tsplib">;
163
+ encoding: z.ZodEnum<{
164
+ "utf-8": "utf-8";
165
+ latin1: "latin1";
166
+ }>;
149
167
  }, z.core.$strict>, z.ZodObject<{
150
168
  format: z.ZodLiteral<"csv-table">;
151
169
  encoding: z.ZodEnum<{
@@ -265,6 +283,24 @@ declare const RecipeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
265
283
  "utf-8": "utf-8";
266
284
  latin1: "latin1";
267
285
  }>;
286
+ }, z.core.$strict>, z.ZodObject<{
287
+ format: z.ZodLiteral<"top">;
288
+ encoding: z.ZodEnum<{
289
+ "utf-8": "utf-8";
290
+ latin1: "latin1";
291
+ }>;
292
+ }, z.core.$strict>, z.ZodObject<{
293
+ format: z.ZodLiteral<"toptw">;
294
+ encoding: z.ZodEnum<{
295
+ "utf-8": "utf-8";
296
+ latin1: "latin1";
297
+ }>;
298
+ }, z.core.$strict>, z.ZodObject<{
299
+ format: z.ZodLiteral<"tsplib">;
300
+ encoding: z.ZodEnum<{
301
+ "utf-8": "utf-8";
302
+ latin1: "latin1";
303
+ }>;
268
304
  }, z.core.$strict>, z.ZodObject<{
269
305
  format: z.ZodLiteral<"csv-table">;
270
306
  encoding: z.ZodEnum<{
@@ -439,6 +475,24 @@ declare const EntrySchema: z.ZodObject<{
439
475
  "utf-8": "utf-8";
440
476
  latin1: "latin1";
441
477
  }>;
478
+ }, z.core.$strict>, z.ZodObject<{
479
+ format: z.ZodLiteral<"top">;
480
+ encoding: z.ZodEnum<{
481
+ "utf-8": "utf-8";
482
+ latin1: "latin1";
483
+ }>;
484
+ }, z.core.$strict>, z.ZodObject<{
485
+ format: z.ZodLiteral<"toptw">;
486
+ encoding: z.ZodEnum<{
487
+ "utf-8": "utf-8";
488
+ latin1: "latin1";
489
+ }>;
490
+ }, z.core.$strict>, z.ZodObject<{
491
+ format: z.ZodLiteral<"tsplib">;
492
+ encoding: z.ZodEnum<{
493
+ "utf-8": "utf-8";
494
+ latin1: "latin1";
495
+ }>;
442
496
  }, z.core.$strict>, z.ZodObject<{
443
497
  format: z.ZodLiteral<"csv-table">;
444
498
  encoding: z.ZodEnum<{
@@ -514,19 +568,19 @@ declare const EntrySchema: z.ZodObject<{
514
568
  status: "fetchable";
515
569
  source: number;
516
570
  archive: {
517
- kind: "none";
518
- } | {
519
- kind: "gzip";
520
- } | {
521
571
  kind: "zip" | "tar" | "tar.gz";
522
- member: {
572
+ members: {
523
573
  path: string;
524
574
  } | {
525
575
  extensions: string[];
526
576
  };
577
+ } | {
578
+ kind: "none";
579
+ } | {
580
+ kind: "gzip";
527
581
  } | {
528
582
  kind: "zip" | "tar" | "tar.gz";
529
- members: {
583
+ member: {
530
584
  path: string;
531
585
  } | {
532
586
  extensions: string[];
@@ -577,6 +631,15 @@ declare const EntrySchema: z.ZodObject<{
577
631
  } | {
578
632
  format: "solomon";
579
633
  encoding: "utf-8" | "latin1";
634
+ } | {
635
+ format: "top";
636
+ encoding: "utf-8" | "latin1";
637
+ } | {
638
+ format: "toptw";
639
+ encoding: "utf-8" | "latin1";
640
+ } | {
641
+ format: "tsplib";
642
+ encoding: "utf-8" | "latin1";
580
643
  };
581
644
  paging?: {
582
645
  kind: "arcgis-query";
@@ -826,6 +889,24 @@ declare const CatalogueIndexSchema: z.ZodObject<{
826
889
  "utf-8": "utf-8";
827
890
  latin1: "latin1";
828
891
  }>;
892
+ }, z.core.$strict>, z.ZodObject<{
893
+ format: z.ZodLiteral<"top">;
894
+ encoding: z.ZodEnum<{
895
+ "utf-8": "utf-8";
896
+ latin1: "latin1";
897
+ }>;
898
+ }, z.core.$strict>, z.ZodObject<{
899
+ format: z.ZodLiteral<"toptw">;
900
+ encoding: z.ZodEnum<{
901
+ "utf-8": "utf-8";
902
+ latin1: "latin1";
903
+ }>;
904
+ }, z.core.$strict>, z.ZodObject<{
905
+ format: z.ZodLiteral<"tsplib">;
906
+ encoding: z.ZodEnum<{
907
+ "utf-8": "utf-8";
908
+ latin1: "latin1";
909
+ }>;
829
910
  }, z.core.$strict>, z.ZodObject<{
830
911
  format: z.ZodLiteral<"csv-table">;
831
912
  encoding: z.ZodEnum<{
@@ -901,19 +982,19 @@ declare const CatalogueIndexSchema: z.ZodObject<{
901
982
  status: "fetchable";
902
983
  source: number;
903
984
  archive: {
904
- kind: "none";
905
- } | {
906
- kind: "gzip";
907
- } | {
908
985
  kind: "zip" | "tar" | "tar.gz";
909
- member: {
986
+ members: {
910
987
  path: string;
911
988
  } | {
912
989
  extensions: string[];
913
990
  };
991
+ } | {
992
+ kind: "none";
993
+ } | {
994
+ kind: "gzip";
914
995
  } | {
915
996
  kind: "zip" | "tar" | "tar.gz";
916
- members: {
997
+ member: {
917
998
  path: string;
918
999
  } | {
919
1000
  extensions: string[];
@@ -964,6 +1045,15 @@ declare const CatalogueIndexSchema: z.ZodObject<{
964
1045
  } | {
965
1046
  format: "solomon";
966
1047
  encoding: "utf-8" | "latin1";
1048
+ } | {
1049
+ format: "top";
1050
+ encoding: "utf-8" | "latin1";
1051
+ } | {
1052
+ format: "toptw";
1053
+ encoding: "utf-8" | "latin1";
1054
+ } | {
1055
+ format: "tsplib";
1056
+ encoding: "utf-8" | "latin1";
967
1057
  };
968
1058
  paging?: {
969
1059
  kind: "arcgis-query";
@@ -1042,19 +1132,19 @@ declare const CatalogueIndexSchema: z.ZodObject<{
1042
1132
  status: "fetchable";
1043
1133
  source: number;
1044
1134
  archive: {
1045
- kind: "none";
1046
- } | {
1047
- kind: "gzip";
1048
- } | {
1049
1135
  kind: "zip" | "tar" | "tar.gz";
1050
- member: {
1136
+ members: {
1051
1137
  path: string;
1052
1138
  } | {
1053
1139
  extensions: string[];
1054
1140
  };
1141
+ } | {
1142
+ kind: "none";
1143
+ } | {
1144
+ kind: "gzip";
1055
1145
  } | {
1056
1146
  kind: "zip" | "tar" | "tar.gz";
1057
- members: {
1147
+ member: {
1058
1148
  path: string;
1059
1149
  } | {
1060
1150
  extensions: string[];
@@ -1105,6 +1195,15 @@ declare const CatalogueIndexSchema: z.ZodObject<{
1105
1195
  } | {
1106
1196
  format: "solomon";
1107
1197
  encoding: "utf-8" | "latin1";
1198
+ } | {
1199
+ format: "top";
1200
+ encoding: "utf-8" | "latin1";
1201
+ } | {
1202
+ format: "toptw";
1203
+ encoding: "utf-8" | "latin1";
1204
+ } | {
1205
+ format: "tsplib";
1206
+ encoding: "utf-8" | "latin1";
1108
1207
  };
1109
1208
  paging?: {
1110
1209
  kind: "arcgis-query";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as Parser, A as LineageSchema, B as Citation, C as CatalogueIndex, D as NamespaceNode, E as DatasetQuery, F as AliasSchema, G as Entry, H as ColumnNameSchema, I as Archive, J as IdSchema, K as EntrySchema, L as ArchiveMember, M as ManifestSchema, N as SemVerSchema, O as createCatalogue, P as parseManifest, Q as NamespaceSchema, R as ArchiveMemberSchema, S as Catalogue, T as Dataset, U as CrosswalkReference, V as CitationSchema, W as CrosswalkReferenceSchema, X as LicenceSchema, Y as Licence, Z as Namespace, _ as GraphEdgeSchema, a as CategoryTargets, at as Snapshot, b as GraphV1, c as getCategory, ct as SourceSchema, d as listCategories, dt as waybackUrl, et as ParserSchema, f as OrNode, g as GraphEdge, h as OrV1Schema, i as CategoryTarget, it as RelativePathSchema, j as Manifest, k as Lineage, l as getTarget, lt as WaybackTimestampSchema, m as OrV1, n as CategoryId, nt as Recipe, o as CountedKey, ot as SnapshotSchema, p as OrNodeSchema, q as HttpUrlSchema, r as CategoryIdSchema, rt as RecipeSchema, s as countTarget, st as Source, t as Category, tt as RESERVED_META_KEYS, u as kindOf, ut as parseEntry, v as GraphNode, w as CatalogueIndexSchema, x as GraphV1Schema, y as GraphNodeSchema, z as ArchiveSchema } from "./categories-BfqmrAvf.js";
1
+ import { $ as Parser, A as LineageSchema, B as Citation, C as CatalogueIndex, D as NamespaceNode, E as DatasetQuery, F as AliasSchema, G as Entry, H as ColumnNameSchema, I as Archive, J as IdSchema, K as EntrySchema, L as ArchiveMember, M as ManifestSchema, N as SemVerSchema, O as createCatalogue, P as parseManifest, Q as NamespaceSchema, R as ArchiveMemberSchema, S as Catalogue, T as Dataset, U as CrosswalkReference, V as CitationSchema, W as CrosswalkReferenceSchema, X as LicenceSchema, Y as Licence, Z as Namespace, _ as GraphEdgeSchema, a as CategoryTargets, at as Snapshot, b as GraphV1, c as getCategory, ct as SourceSchema, d as listCategories, dt as waybackUrl, et as ParserSchema, f as OrNode, g as GraphEdge, h as OrV1Schema, i as CategoryTarget, it as RelativePathSchema, j as Manifest, k as Lineage, l as getTarget, lt as WaybackTimestampSchema, m as OrV1, n as CategoryId, nt as Recipe, o as CountedKey, ot as SnapshotSchema, p as OrNodeSchema, q as HttpUrlSchema, r as CategoryIdSchema, rt as RecipeSchema, s as countTarget, st as Source, t as Category, tt as RESERVED_META_KEYS, u as kindOf, ut as parseEntry, v as GraphNode, w as CatalogueIndexSchema, x as GraphV1Schema, y as GraphNodeSchema, z as ArchiveSchema } from "./categories-HeWbVPhc.js";
2
2
  import { Crosswalk } from "twindex";
3
3
  import { z } from "zod";
4
4
  //#region src/errors.d.ts
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as NativeParseError, A as toIssues, B as kindOf, C as RESERVED_META_KEYS, D as SourceSchema, E as SnapshotSchema, G as GraphNodeSchema, H as OrNodeSchema, I as CategoryIdSchema, J as ArchiveError, K as GraphV1Schema, L as countTarget, M as NAME_SEPARATOR, N as groupsOf, O as WaybackTimestampSchema, P as namesOf, Q as KeepsetError, R as getCategory, S as ParserSchema, T as RelativePathSchema, U as OrV1Schema, V as listCategories, W as GraphEdgeSchema, X as DatasetAmbiguousError, Y as CatalogueSyntaxError, Z as DatasetNotFoundError, _ as EntrySchema, a as CatalogueIndexSchema, b as LicenceSchema, c as ManifestSchema, d as AliasSchema, et as RecipeError, f as ArchiveMemberSchema, g as CrosswalkReferenceSchema, h as ColumnNameSchema, i as listNamespaces, j as waybackUrl, k as parseEntry, l as SemVerSchema, m as CitationSchema, n as getManifest, o as createCatalogue, p as ArchiveSchema, q as JsonScalarSchema, r as listDatasets, s as LineageSchema, t as getDataset, tt as UnknownCategoryError, u as parseManifest, v as HttpUrlSchema, w as RecipeSchema, x as NamespaceSchema, y as IdSchema, z as getTarget } from "./bundled-BWINQKLQ.js";
1
+ import { $ as NativeParseError, A as toIssues, B as kindOf, C as RESERVED_META_KEYS, D as SourceSchema, E as SnapshotSchema, G as GraphNodeSchema, H as OrNodeSchema, I as CategoryIdSchema, J as ArchiveError, K as GraphV1Schema, L as countTarget, M as NAME_SEPARATOR, N as groupsOf, O as WaybackTimestampSchema, P as namesOf, Q as KeepsetError, R as getCategory, S as ParserSchema, T as RelativePathSchema, U as OrV1Schema, V as listCategories, W as GraphEdgeSchema, X as DatasetAmbiguousError, Y as CatalogueSyntaxError, Z as DatasetNotFoundError, _ as EntrySchema, a as CatalogueIndexSchema, b as LicenceSchema, c as ManifestSchema, d as AliasSchema, et as RecipeError, f as ArchiveMemberSchema, g as CrosswalkReferenceSchema, h as ColumnNameSchema, i as listNamespaces, j as waybackUrl, k as parseEntry, l as SemVerSchema, m as CitationSchema, n as getManifest, o as createCatalogue, p as ArchiveSchema, q as JsonScalarSchema, r as listDatasets, s as LineageSchema, t as getDataset, tt as UnknownCategoryError, u as parseManifest, v as HttpUrlSchema, w as RecipeSchema, x as NamespaceSchema, y as IdSchema, z as getTarget } from "./bundled-V1EJ0k9e.js";
2
2
  import { validateCrosswalk } from "twindex";
3
3
  //#region src/serialise.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { G as Entry, T as Dataset, at as Snapshot, i as CategoryTarget, st as Source } from "../categories-BfqmrAvf.js";
1
+ import { G as Entry, T as Dataset, at as Snapshot, i as CategoryTarget, st as Source } from "../categories-HeWbVPhc.js";
2
2
  //#region src/node/fetch-dataset.d.ts
3
3
  /** The network port a recipe downloads through: `downloadRaw` by default, which returns the bytes exactly as served, or a stand-in under test. */
4
4
  export type FetchPort = (url: string, init: {
@@ -1,4 +1,4 @@
1
- import { $ as NativeParseError, F as getOwn, J as ArchiveError, L as countTarget, Q as KeepsetError, et as RecipeError, t as getDataset, z as getTarget } from "../bundled-BWINQKLQ.js";
1
+ import { $ as NativeParseError, F as getOwn, J as ArchiveError, L as countTarget, Q as KeepsetError, et as RecipeError, t as getDataset, z as getTarget } from "../bundled-V1EJ0k9e.js";
2
2
  import { RoundTripMismatchError, assertRoundTrip, encode } from "twindex";
3
3
  import { z } from "zod";
4
4
  import { createHash } from "node:crypto";
@@ -104,7 +104,7 @@ function parseEdgeList(text, options) {
104
104
  //#endregion
105
105
  //#region src/parsers/gml.ts
106
106
  const KEY = /[A-Za-z_][A-Za-z0-9_]*/y;
107
- const NUMBER$1 = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?/y;
107
+ const NUMBER$4 = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?/y;
108
108
  /**
109
109
  * Matches a sticky pattern at a position.
110
110
  * @param pattern - A sticky regular expression.
@@ -155,7 +155,7 @@ function tokenise(text) {
155
155
  position = end + 1;
156
156
  } else {
157
157
  const key = matchAt(KEY, text, position);
158
- const number = key === void 0 ? matchAt(NUMBER$1, text, position) : void 0;
158
+ const number = key === void 0 ? matchAt(NUMBER$4, text, position) : void 0;
159
159
  const match = key ?? number;
160
160
  if (match === void 0) throw new NativeParseError("gml", line, `unexpected character ${JSON.stringify(char)}`);
161
161
  tokens.push({
@@ -833,9 +833,9 @@ function parsePajek(text) {
833
833
  }
834
834
  //#endregion
835
835
  //#region src/parsers/solomon.ts
836
- const FORMAT = "solomon";
836
+ const FORMAT$3 = "solomon";
837
837
  /** A line of integers or decimals separated by any run of spaces and tabs, as the files write rows (Solomon's with padded spaces, the Tenerife set with tabs). */
838
- const NUMBER = /^-?\d+(?:\.\d+)?$/;
838
+ const NUMBER$3 = /^-?\d+(?:\.\d+)?$/;
839
839
  /**
840
840
  * Reads a token as a finite number.
841
841
  * @param token - The token.
@@ -844,8 +844,8 @@ const NUMBER = /^-?\d+(?:\.\d+)?$/;
844
844
  * @returns The number.
845
845
  * @throws NativeParseError when the token is not a decimal number.
846
846
  */
847
- function numberOf(token, line, what) {
848
- if (!NUMBER.test(token)) throw new NativeParseError(FORMAT, line, `expected ${what}, found ${JSON.stringify(token)}`);
847
+ function numberOf$2(token, line, what) {
848
+ if (!NUMBER$3.test(token)) throw new NativeParseError(FORMAT$3, line, `expected ${what}, found ${JSON.stringify(token)}`);
849
849
  return Number(token);
850
850
  }
851
851
  /**
@@ -858,7 +858,7 @@ function parseSolomon(text) {
858
858
  const all = lines(text);
859
859
  const present = (index) => (all[index] ?? "").trim();
860
860
  const name = present(0);
861
- if (name === "") throw new NativeParseError(FORMAT, 1, "expected the instance name on the first line");
861
+ if (name === "") throw new NativeParseError(FORMAT$3, 1, "expected the instance name on the first line");
862
862
  const meta = { name };
863
863
  const nodes = [];
864
864
  const seen = /* @__PURE__ */ new Set();
@@ -874,7 +874,7 @@ function parseSolomon(text) {
874
874
  awaitingHeader = true;
875
875
  continue;
876
876
  }
877
- if (section === "preamble") throw new NativeParseError(FORMAT, line, `expected VEHICLE or CUSTOMER, found ${JSON.stringify(trimmed)}`);
877
+ if (section === "preamble") throw new NativeParseError(FORMAT$3, line, `expected VEHICLE or CUSTOMER, found ${JSON.stringify(trimmed)}`);
878
878
  if (awaitingHeader) {
879
879
  awaitingHeader = false;
880
880
  continue;
@@ -882,29 +882,29 @@ function parseSolomon(text) {
882
882
  const tokens = trimmed.split(/\s+/);
883
883
  if (section === "vehicle") {
884
884
  const [vehicles, capacity, ...extra] = tokens;
885
- if (vehicles === void 0 || capacity === void 0 || extra.length > 0) throw new NativeParseError(FORMAT, line, `expected the vehicle count and capacity, found ${JSON.stringify(trimmed)}`);
886
- meta.vehicles = numberOf(vehicles, line, "the vehicle count");
887
- meta.capacity = numberOf(capacity, line, "the vehicle capacity");
885
+ if (vehicles === void 0 || capacity === void 0 || extra.length > 0) throw new NativeParseError(FORMAT$3, line, `expected the vehicle count and capacity, found ${JSON.stringify(trimmed)}`);
886
+ meta.vehicles = numberOf$2(vehicles, line, "the vehicle count");
887
+ meta.capacity = numberOf$2(capacity, line, "the vehicle capacity");
888
888
  continue;
889
889
  }
890
890
  const [id, x, y, demand, ready, due, service, ...extra] = tokens;
891
- if (id === void 0 || x === void 0 || y === void 0 || demand === void 0 || ready === void 0 || due === void 0 || service === void 0 || extra.length > 0) throw new NativeParseError(FORMAT, line, `expected seven numbers (id, x, y, demand, ready, due, service), found ${JSON.stringify(trimmed)}`);
892
- if (!/^\d+$/.test(id)) throw new NativeParseError(FORMAT, line, `expected a whole number node id, found ${JSON.stringify(id)}`);
893
- if (seen.has(id)) throw new NativeParseError(FORMAT, line, `node ${id} appears twice`);
891
+ if (id === void 0 || x === void 0 || y === void 0 || demand === void 0 || ready === void 0 || due === void 0 || service === void 0 || extra.length > 0) throw new NativeParseError(FORMAT$3, line, `expected seven numbers (id, x, y, demand, ready, due, service), found ${JSON.stringify(trimmed)}`);
892
+ if (!/^\d+$/.test(id)) throw new NativeParseError(FORMAT$3, line, `expected a whole number node id, found ${JSON.stringify(id)}`);
893
+ if (seen.has(id)) throw new NativeParseError(FORMAT$3, line, `node ${id} appears twice`);
894
894
  seen.add(id);
895
895
  nodes.push({
896
896
  id,
897
- x: numberOf(x, line, "x"),
898
- y: numberOf(y, line, "y"),
899
- demand: numberOf(demand, line, "the demand"),
900
- ready: numberOf(ready, line, "the ready time"),
901
- due: numberOf(due, line, "the due date"),
902
- service: numberOf(service, line, "the service time")
897
+ x: numberOf$2(x, line, "x"),
898
+ y: numberOf$2(y, line, "y"),
899
+ demand: numberOf$2(demand, line, "the demand"),
900
+ ready: numberOf$2(ready, line, "the ready time"),
901
+ due: numberOf$2(due, line, "the due date"),
902
+ service: numberOf$2(service, line, "the service time")
903
903
  });
904
904
  }
905
- if (section !== "customer") throw new NativeParseError(FORMAT, all.length, "expected a CUSTOMER section");
906
- if (nodes.length === 0) throw new NativeParseError(FORMAT, all.length, "the CUSTOMER section has no rows");
907
- if (meta.vehicles === void 0 || meta.capacity === void 0) throw new NativeParseError(FORMAT, all.length, "expected a VEHICLE section with the vehicle count and capacity");
905
+ if (section !== "customer") throw new NativeParseError(FORMAT$3, all.length, "expected a CUSTOMER section");
906
+ if (nodes.length === 0) throw new NativeParseError(FORMAT$3, all.length, "the CUSTOMER section has no rows");
907
+ if (meta.vehicles === void 0 || meta.capacity === void 0) throw new NativeParseError(FORMAT$3, all.length, "expected a VEHICLE section with the vehicle count and capacity");
908
908
  return {
909
909
  meta: {
910
910
  name: meta.name,
@@ -915,6 +915,345 @@ function parseSolomon(text) {
915
915
  };
916
916
  }
917
917
  //#endregion
918
+ //#region src/parsers/top.ts
919
+ const FORMAT$2 = "top";
920
+ /** The header lines before the first point: `n`, `m` and `tmax`. */
921
+ const HEADER_LINES = 3;
922
+ const NUMBER$2 = /^-?\d+(?:\.\d+)?$/;
923
+ /**
924
+ * Reads the value of a header line `<key> <number>`.
925
+ * @param text - The line.
926
+ * @param key - The key it must start with.
927
+ * @param line - The 1-based line, for the error.
928
+ * @returns The number.
929
+ * @throws NativeParseError when the line is not the key and one number.
930
+ */
931
+ function headerOf(text, key, line) {
932
+ const [name, value, ...extra] = (text ?? "").trim().split(/\s+/);
933
+ if (name !== key || value === void 0 || extra.length > 0 || !NUMBER$2.test(value)) throw new NativeParseError(FORMAT$2, line, `expected "${key} <number>", found ${JSON.stringify((text ?? "").trim())}`);
934
+ return Number(value);
935
+ }
936
+ /**
937
+ * Parses a team orienteering problem instance (Chao, Golden and Wasil, as the KU Leuven pages describe it): `n <points>`, `m <paths>` and `tmax <budget>` on three lines, then one row of position and score for each point. The first point is where the paths start and the last is where they end, and the rest are to visit; the file has no ids, so a point's id is its zero-based row number. Columns are separated by any run of spaces or tabs, and blank or whitespace only lines are skipped. The positions and the budget are in the same planar unit, which the format does not name.
938
+ * @param text - The file text.
939
+ * @returns The instance: its point count, path count and per-path budget as `meta`, and its nodes in file order.
940
+ * @throws NativeParseError naming the line for a missing or misordered header line, a row that is not three numbers, or a row count that is not the point count.
941
+ */
942
+ function parseTop(text) {
943
+ const all = lines(text).map((raw, index) => ({
944
+ line: index + 1,
945
+ text: raw.trim()
946
+ })).filter((entry) => entry.text !== "");
947
+ const [first, second, third] = all;
948
+ const rows = all.slice(HEADER_LINES);
949
+ const points = headerOf(first?.text, "n", first?.line ?? 1);
950
+ const paths = headerOf(second?.text, "m", second?.line ?? 2);
951
+ const budget = headerOf(third?.text, "tmax", third?.line ?? HEADER_LINES);
952
+ if (!Number.isInteger(points) || points < 2) throw new NativeParseError(FORMAT$2, first?.line ?? 1, `expected at least a start and an end point, found n ${String(points)}`);
953
+ if (rows.length !== points) throw new NativeParseError(FORMAT$2, all[all.length - 1]?.line ?? 1, `the header gives ${String(points)} points, found ${String(rows.length)} rows`);
954
+ const nodes = rows.map((row, index) => {
955
+ const [x, y, score, ...extra] = row.text.split(/\s+/);
956
+ if (x === void 0 || y === void 0 || score === void 0 || extra.length > 0 || !NUMBER$2.test(x) || !NUMBER$2.test(y) || !NUMBER$2.test(score)) throw new NativeParseError(FORMAT$2, row.line, `expected three numbers (x, y, score), found ${JSON.stringify(row.text)}`);
957
+ return {
958
+ id: String(index),
959
+ x: Number(x),
960
+ y: Number(y),
961
+ score: Number(score),
962
+ role: index === 0 ? "start" : index === points - 1 ? "end" : "visit"
963
+ };
964
+ });
965
+ return {
966
+ meta: {
967
+ points,
968
+ paths,
969
+ budget
970
+ },
971
+ nodes
972
+ };
973
+ }
974
+ //#endregion
975
+ //#region src/parsers/toptw.ts
976
+ const FORMAT$1 = "toptw";
977
+ /** How many numbers the first header line holds (k, paths, customers, t) and the second (D, capacity). */
978
+ const FIRST_HEADER_NUMBERS = 4;
979
+ const SECOND_HEADER_NUMBERS = 2;
980
+ /** Where a node row's fields sit: the length of the unused list is the seventh token, the list follows, and the window's opening and closing come after it. A row without a list is the first token to the window's closing, so it holds this many numbers plus the list's length. */
981
+ const LIST_LENGTH_AT = 6;
982
+ const LIST_STARTS_AT = 7;
983
+ const ROW_NUMBERS_BESIDE_LIST = 9;
984
+ const NUMBER$1 = /^-?\d+(?:\.\d+)?$/;
985
+ /**
986
+ * Reads a token as a finite number.
987
+ * @param token - The token.
988
+ * @param line - The 1-based line, for the error.
989
+ * @param what - What the token should be, for the error.
990
+ * @returns The number.
991
+ * @throws NativeParseError when the token is not a decimal number.
992
+ */
993
+ function numberOf$1(token, line, what) {
994
+ if (token === void 0 || !NUMBER$1.test(token)) throw new NativeParseError(FORMAT$1, line, `expected ${what}, found ${JSON.stringify(token)}`);
995
+ return Number(token);
996
+ }
997
+ /**
998
+ * Reads a line of a given number of numbers.
999
+ * @param text - The line.
1000
+ * @param count - How many numbers it holds.
1001
+ * @param line - The 1-based line, for the error.
1002
+ * @param what - What the line should be, for the error.
1003
+ * @returns The numbers.
1004
+ * @throws NativeParseError when the line does not hold exactly that many numbers.
1005
+ */
1006
+ function numbersOf(text, count, line, what) {
1007
+ const tokens = text.trim().split(/\s+/);
1008
+ if (tokens.length !== count) throw new NativeParseError(FORMAT$1, line, `expected ${what}, found ${JSON.stringify(text.trim())}`);
1009
+ return tokens.map((token) => numberOf$1(token, line, what));
1010
+ }
1011
+ /**
1012
+ * Parses a team orienteering problem with time windows instance (TOPTW, in the layout of Righini and Salani, as the KU Leuven pages describe it). The first line holds four numbers, of which the second is the number of paths and the third the number of customers, the second line two, of which the second is the vehicle capacity, and then one row per node, the depot first: its id, position, service duration, profit, an unused number, the length of a list of unused numbers, that list, and the opening and closing of its time window. Columns are separated by any run of spaces, and blank or whitespace only lines are skipped. The row count must be the customer count plus one, which catches a truncated file. The time quantities are in the unit of the source's coordinate grid, which the format does not name.
1013
+ * @param text - The file text.
1014
+ * @returns The instance: its path count, customer count and capacity as `meta`, and its nodes in file order.
1015
+ * @throws NativeParseError naming the line for a header or row that is not made of the numbers the format has, a row whose length does not match its list, a repeated node id, or a row count that is not the customer count plus one.
1016
+ */
1017
+ function parseToptw(text) {
1018
+ const all = lines(text).map((raw, index) => ({
1019
+ line: index + 1,
1020
+ text: raw.trim()
1021
+ })).filter((entry) => entry.text !== "");
1022
+ const [first, second, ...rows] = all;
1023
+ if (first === void 0 || second === void 0) throw new NativeParseError(FORMAT$1, 1, "expected the two header lines");
1024
+ const [, paths, customers] = numbersOf(first.text, FIRST_HEADER_NUMBERS, first.line, "four numbers (k, paths, customers, t)");
1025
+ const [, capacity] = numbersOf(second.text, SECOND_HEADER_NUMBERS, second.line, "two numbers (D, capacity)");
1026
+ if (paths === void 0 || customers === void 0 || capacity === void 0) throw new NativeParseError(FORMAT$1, first.line, "the header lacks a number");
1027
+ const seen = /* @__PURE__ */ new Set();
1028
+ const nodes = rows.map((row) => {
1029
+ const tokens = row.text.split(/\s+/);
1030
+ const listLength = numberOf$1(tokens[LIST_LENGTH_AT], row.line, "the length of the list after the unused number");
1031
+ if (!Number.isInteger(listLength) || listLength < 0 || tokens.length !== ROW_NUMBERS_BESIDE_LIST + listLength) throw new NativeParseError(FORMAT$1, row.line, `expected ${String(ROW_NUMBERS_BESIDE_LIST + listLength)} numbers for a list of ${String(listLength)}, found ${String(tokens.length)}`);
1032
+ const [id] = tokens;
1033
+ if (id === void 0 || !/^\d+$/.test(id)) throw new NativeParseError(FORMAT$1, row.line, `expected a whole number node id, found ${JSON.stringify(id)}`);
1034
+ if (seen.has(id)) throw new NativeParseError(FORMAT$1, row.line, `node ${id} appears twice`);
1035
+ seen.add(id);
1036
+ return {
1037
+ id,
1038
+ x: numberOf$1(tokens[1], row.line, "x"),
1039
+ y: numberOf$1(tokens[2], row.line, "y"),
1040
+ service: numberOf$1(tokens[3], row.line, "the service duration"),
1041
+ profit: numberOf$1(tokens[4], row.line, "the profit"),
1042
+ open: numberOf$1(tokens[LIST_STARTS_AT + listLength], row.line, "the opening of the window"),
1043
+ close: numberOf$1(tokens[8 + listLength], row.line, "the closing of the window")
1044
+ };
1045
+ });
1046
+ if (nodes.length !== customers + 1) throw new NativeParseError(FORMAT$1, all.length === 0 ? 1 : all[all.length - 1]?.line ?? 1, `the header gives ${String(customers)} customers, so expected ${String(customers + 1)} rows with the depot, found ${String(nodes.length)}`);
1047
+ return {
1048
+ meta: {
1049
+ paths,
1050
+ customers,
1051
+ capacity
1052
+ },
1053
+ nodes
1054
+ };
1055
+ }
1056
+ //#endregion
1057
+ //#region src/parsers/tsplib.ts
1058
+ const FORMAT = "tsplib";
1059
+ /** The instance types the parser reads: the probabilistic and the clustered orienteering problem. */
1060
+ const TYPES = /* @__PURE__ */ new Set(["POP", "COP"]);
1061
+ /** The edge weight types a coordinate section is read under: planar ones keep their coordinates as `x` and `y`, `GEO` converts them to degrees, and `EXPLICIT` has no coordinates. */
1062
+ const PLANAR = /* @__PURE__ */ new Set([
1063
+ "EUC_2D",
1064
+ "CEIL_2D",
1065
+ "ATT"
1066
+ ]);
1067
+ const NUMBER = /^-?\d+(?:\.\d+)?$/;
1068
+ const WHOLE = /^\d+$/;
1069
+ const HEADER$1 = /^([A-Z][A-Z_]*)\s*:\s*(.*)$/;
1070
+ const SECTION = /^[A-Z][A-Z_]*$/;
1071
+ /** Minutes in a degree, which a `DDD.MM` coordinate counts in. */
1072
+ const MINUTES_PER_DEGREE = 60;
1073
+ /** `DDD.MM` has two digits of minutes, so the fraction is read in hundredths. */
1074
+ const MINUTE_SCALE = 100;
1075
+ /** How far a coordinate's hundredths may lie from a whole number of minutes: float error in the text's value, not a third digit. */
1076
+ const MINUTE_TOLERANCE = 1e-6;
1077
+ /**
1078
+ * Converts a TSPLIB `GEO` coordinate to decimal degrees. The value is `DDD.MM`, degrees then minutes in one number (`48.23` is 48 degrees 23 minutes), so the fraction is minutes out of a hundred, not a decimal fraction of a degree. The degrees are truncated, which is what makes the conversion agree with the optimal tour lengths the instances record where the specification's `nint` would round minutes of 50 or more up to the next degree.
1079
+ * @param value - The coordinate as the file writes it.
1080
+ * @param line - The 1-based line, for the error.
1081
+ * @returns Decimal degrees, signed like the input.
1082
+ * @throws NativeParseError when the minutes are not a whole number below sixty.
1083
+ */
1084
+ function degrees(value, line) {
1085
+ const magnitude = Math.abs(value);
1086
+ const whole = Math.trunc(magnitude);
1087
+ const hundredths = (magnitude - whole) * MINUTE_SCALE;
1088
+ const minutes = Math.round(hundredths);
1089
+ if (Math.abs(hundredths - minutes) > MINUTE_TOLERANCE || minutes >= MINUTES_PER_DEGREE) throw new NativeParseError(FORMAT, line, `expected a DDD.MM coordinate with whole minutes below 60, found ${String(value)}`);
1090
+ return Math.sign(value) * (whole + minutes / MINUTES_PER_DEGREE);
1091
+ }
1092
+ /**
1093
+ * Reads a token as a finite number.
1094
+ * @param token - The token.
1095
+ * @param line - The 1-based line, for the error.
1096
+ * @param what - What the token should be, for the error.
1097
+ * @returns The number.
1098
+ * @throws NativeParseError when the token is not a decimal number.
1099
+ */
1100
+ function numberOf(token, line, what) {
1101
+ if (token === void 0 || !NUMBER.test(token)) throw new NativeParseError(FORMAT, line, `expected ${what}, found ${JSON.stringify(token)}`);
1102
+ return Number(token);
1103
+ }
1104
+ /**
1105
+ * Reads a token as a whole number.
1106
+ * @param token - The token.
1107
+ * @param line - The 1-based line, for the error.
1108
+ * @param what - What the token should be, for the error.
1109
+ * @returns The number.
1110
+ * @throws NativeParseError when the token is not a whole number.
1111
+ */
1112
+ function wholeOf(token, line, what) {
1113
+ if (token === void 0 || !WHOLE.test(token)) throw new NativeParseError(FORMAT, line, `expected ${what}, found ${JSON.stringify(token)}`);
1114
+ return Number(token);
1115
+ }
1116
+ /**
1117
+ * Reads the header, the coordinate section, the prize and probability section and the set section of a file, skipping every other section.
1118
+ * @param text - The file text.
1119
+ * @returns What it holds.
1120
+ * @throws NativeParseError naming the line for a repeated key, a malformed row, a repeated node or set id, or a row outside any section.
1121
+ */
1122
+ function collect(text) {
1123
+ const collected = {
1124
+ header: /* @__PURE__ */ new Map(),
1125
+ coords: /* @__PURE__ */ new Map(),
1126
+ prizes: /* @__PURE__ */ new Map(),
1127
+ sets: []
1128
+ };
1129
+ let section;
1130
+ for (const [index, raw] of lines(text).entries()) {
1131
+ const line = index + 1;
1132
+ const trimmed = raw.trim();
1133
+ if (trimmed === "" || trimmed === "EOF") continue;
1134
+ const keyed = HEADER$1.exec(trimmed);
1135
+ const key = keyed?.[1];
1136
+ if (key === "CONSTRAINING_SET_SECTION") {
1137
+ section = "sets";
1138
+ continue;
1139
+ }
1140
+ if (keyed !== null && key !== void 0) {
1141
+ if (collected.header.has(key)) throw new NativeParseError(FORMAT, line, `${key} appears twice`);
1142
+ collected.header.set(key, (keyed[2] ?? "").trim());
1143
+ section = void 0;
1144
+ continue;
1145
+ }
1146
+ if (SECTION.test(trimmed)) {
1147
+ section = trimmed === "NODE_COORD_SECTION" ? "coords" : trimmed === "NODE_PRIZE_PROBABILITY_SECTION" ? "prizes" : "skipped";
1148
+ continue;
1149
+ }
1150
+ const tokens = trimmed.split(/\s+/);
1151
+ if (section === "coords") {
1152
+ const [id, x, y, ...extra] = tokens;
1153
+ if (extra.length > 0) throw new NativeParseError(FORMAT, line, `expected "id x y", found ${JSON.stringify(trimmed)}`);
1154
+ const node = wholeOf(id, line, "a node id");
1155
+ if (collected.coords.has(node)) throw new NativeParseError(FORMAT, line, `node ${String(node)} has two coordinate rows`);
1156
+ collected.coords.set(node, {
1157
+ x: numberOf(x, line, "x"),
1158
+ y: numberOf(y, line, "y"),
1159
+ line
1160
+ });
1161
+ } else if (section === "prizes") {
1162
+ const [id, prize, probability, ...extra] = tokens;
1163
+ if (extra.length > 0) throw new NativeParseError(FORMAT, line, `expected "id prize probability", found ${JSON.stringify(trimmed)}`);
1164
+ const node = wholeOf(id, line, "a node id");
1165
+ if (collected.prizes.has(node)) throw new NativeParseError(FORMAT, line, `node ${String(node)} has two prize rows`);
1166
+ collected.prizes.set(node, {
1167
+ prize: numberOf(prize, line, "the prize"),
1168
+ probability: numberOf(probability, line, "the probability")
1169
+ });
1170
+ } else if (section === "sets") {
1171
+ const [id, prize, ...members] = tokens;
1172
+ const setId = wholeOf(id, line, "a set id");
1173
+ if (collected.sets.some((set) => set.id === setId)) throw new NativeParseError(FORMAT, line, `set ${String(setId)} appears twice`);
1174
+ collected.sets.push({
1175
+ id: setId,
1176
+ prize: numberOf(prize, line, "the set prize"),
1177
+ members: members.map((member) => wholeOf(member, line, "a node id in a set"))
1178
+ });
1179
+ } else if (section === void 0) throw new NativeParseError(FORMAT, line, `expected a KEY : value line or a section, found ${JSON.stringify(trimmed)}`);
1180
+ }
1181
+ return collected;
1182
+ }
1183
+ /**
1184
+ * Reads a header value that must be there.
1185
+ * @param header - The header.
1186
+ * @param key - The key.
1187
+ * @returns Its text.
1188
+ * @throws NativeParseError when the file has no such key.
1189
+ */
1190
+ function required(header, key) {
1191
+ const value = header.get(key);
1192
+ if (value === void 0) throw new NativeParseError(FORMAT, 1, `expected a ${key} line`);
1193
+ return value;
1194
+ }
1195
+ /**
1196
+ * Parses a TSPLIB style orienteering instance of type `POP` (probabilistic orienteering, Angelelli and others) or `COP` (clustered orienteering), in the layout TSPLIB95 (Reinelt) defines: `KEY : value` header lines and bare keyword sections. The nodes are 1 to `DIMENSION`. The planar weight types keep their coordinates as `x` and `y` and `GEO` converts them to `lat` and `lon`; an `EXPLICIT` file has no coordinates, so its nodes have only the prize data. A `POP` file's `NODE_PRIZE_PROBABILITY_SECTION` gives each node its prize and probability. A `COP` file has no prize on a node: its prizes belong to sets of nodes that may overlap, so each node lists the sets it is in under `clusters` and the sets' prizes are the `setPrizes` metadata, and nothing is divided among the members. The weight matrix is not read, as or/v1 has no arcs.
1197
+ * @param text - The file text.
1198
+ * @returns The instance: its header values as `meta` and its nodes in id order.
1199
+ * @throws NativeParseError naming the line for an unsupported type or weight type, a missing key or section, a node id outside 1 to `DIMENSION` or missing from a section that must cover every node, or a set that names a node outside that range.
1200
+ */
1201
+ function parseTsplib(text) {
1202
+ const { header, coords, prizes, sets } = collect(text);
1203
+ const type = required(header, "TYPE");
1204
+ if (!TYPES.has(type)) throw new NativeParseError(FORMAT, 1, `expected TYPE POP or COP, found ${JSON.stringify(type)}`);
1205
+ const weightType = required(header, "EDGE_WEIGHT_TYPE");
1206
+ const geographic = weightType === "GEO";
1207
+ if (!geographic && !PLANAR.has(weightType) && weightType !== "EXPLICIT") throw new NativeParseError(FORMAT, 1, `expected EDGE_WEIGHT_TYPE EUC_2D, CEIL_2D, ATT, GEO or EXPLICIT, found ${JSON.stringify(weightType)}`);
1208
+ const dimension = wholeOf(required(header, "DIMENSION"), 1, "DIMENSION");
1209
+ const budget = numberOf(required(header, "TMAX"), 1, "TMAX");
1210
+ if (weightType === "EXPLICIT" ? coords.size > 0 : coords.size !== dimension) throw new NativeParseError(FORMAT, 1, weightType === "EXPLICIT" ? "an EXPLICIT file has no NODE_COORD_SECTION" : `expected ${String(dimension)} coordinate rows, found ${String(coords.size)}`);
1211
+ const inRange = (id) => id >= 1 && id <= dimension;
1212
+ for (const id of [
1213
+ ...coords.keys(),
1214
+ ...prizes.keys(),
1215
+ ...sets.flatMap((set) => set.members)
1216
+ ]) if (!inRange(id)) throw new NativeParseError(FORMAT, 1, `node ${String(id)} is outside 1 to ${String(dimension)}`);
1217
+ if (type === "POP" && prizes.size !== dimension) throw new NativeParseError(FORMAT, 1, `expected ${String(dimension)} prize rows, found ${String(prizes.size)}`);
1218
+ if (type === "COP") {
1219
+ const declared = wholeOf(required(header, "SETS"), 1, "SETS");
1220
+ if (sets.length !== declared) throw new NativeParseError(FORMAT, 1, `expected ${String(declared)} sets, found ${String(sets.length)}`);
1221
+ }
1222
+ const clustersOf = (node) => {
1223
+ const ids = sets.filter((set) => set.members.includes(node)).map((set) => String(set.id));
1224
+ return ids.length === 0 ? null : ids.join(",");
1225
+ };
1226
+ const nodes = Array.from({ length: dimension }, (_, index) => {
1227
+ const id = index + 1;
1228
+ const position = coords.get(id);
1229
+ const prize = prizes.get(id);
1230
+ const lat = geographic && position !== void 0 ? degrees(position.x, position.line) : null;
1231
+ const lon = geographic && position !== void 0 ? degrees(position.y, position.line) : null;
1232
+ return {
1233
+ id: String(id),
1234
+ x: geographic ? null : position?.x ?? null,
1235
+ y: geographic ? null : position?.y ?? null,
1236
+ lat,
1237
+ lon,
1238
+ prize: prize?.prize ?? null,
1239
+ probability: prize?.probability ?? null,
1240
+ clusters: clustersOf(id)
1241
+ };
1242
+ });
1243
+ const total = header.get("TPRIZE");
1244
+ return {
1245
+ meta: {
1246
+ name: header.get("NAME") ?? null,
1247
+ type,
1248
+ budget,
1249
+ edgeWeightType: weightType,
1250
+ totalPrize: total === void 0 ? null : numberOf(total, 1, "TPRIZE"),
1251
+ setPrizes: type === "COP" ? sets.map((set) => `${String(set.id)}:${String(set.prize)}`).join(",") : null
1252
+ },
1253
+ nodes
1254
+ };
1255
+ }
1256
+ //#endregion
918
1257
  //#region src/parsers/parse-native.ts
919
1258
  /** Bytes converted per String.fromCharCode call: spreading a whole file would exceed the engine's argument-count limit, and this many stays well inside every engine's. */
920
1259
  const LATIN1_CHUNK = 32768;
@@ -971,6 +1310,9 @@ function parseNative(bytes, parser) {
971
1310
  if (parser.format === "matrix-market") return parseMatrixMarket(text);
972
1311
  if (parser.format === "gml") return parseGml(text);
973
1312
  if (parser.format === "solomon") return parseSolomon(text);
1313
+ if (parser.format === "top") return parseTop(text);
1314
+ if (parser.format === "toptw") return parseToptw(text);
1315
+ if (parser.format === "tsplib") return parseTsplib(text);
974
1316
  return parsePajek(text);
975
1317
  }
976
1318
  /** The ustar header layout (POSIX.1-1988, pax(1) "ustar Interchange Format"): each field's byte offset and length. */
@@ -1507,6 +1849,8 @@ const REDIRECTS = new Set(Object.values({
1507
1849
  }));
1508
1850
  /** What the downloader says it is, so a server operator can tell where the traffic comes from. */
1509
1851
  const USER_AGENT = "keepset (+https://github.com/Mearman/keepset)";
1852
+ /** What the downloader accepts: anything, as the file is the recipe's to read. Node's HTTP client sends no `Accept` header of its own, and a server that content-negotiates a download (the VRP-REP host answers 406 to a request without one) refuses it; every other HTTP client sends this, and RFC 9110 reads an absent header as the same. */
1853
+ const ACCEPT = "*/*";
1510
1854
  /** The failure of a connection that sent nothing for the stall time. */
1511
1855
  var StalledError = class extends KeepsetError {};
1512
1856
  /**
@@ -1531,7 +1875,10 @@ async function get(url, signal, stall) {
1531
1875
  let answered;
1532
1876
  const request = send(url, {
1533
1877
  method: "GET",
1534
- headers: { "user-agent": USER_AGENT },
1878
+ headers: {
1879
+ accept: ACCEPT,
1880
+ "user-agent": USER_AGENT
1881
+ },
1535
1882
  ...signal === void 0 ? {} : { signal }
1536
1883
  }, (response) => {
1537
1884
  answered = response;
@@ -4,7 +4,7 @@ import { KeepsetError, NAME_SEPARATOR, countTarget, getDataset, listDatasets, li
4
4
  import { fetchDataset, roundTripDataset } from "keepset/node";
5
5
  import { parseArgs } from "node:util";
6
6
  //#region package.json
7
- var version = "0.24.0";
7
+ var version = "0.26.0";
8
8
  const usage = `Usage: keepset <command> [options]
9
9
 
10
10
  Commands:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "keepset",
3
3
  "description": "Library, CLI and statically generated catalogue for dataset fetch-and-normalise recipes",
4
- "version": "0.24.0",
4
+ "version": "0.26.0",
5
5
  "bin": {
6
6
  "keepset": "./dist-cli/keepset.mjs"
7
7
  },
@@ -1 +1 @@
1
- {"$defs":{"Crosswalk":{"$ref":"https://cdn.jsdelivr.net/npm/twindex@0.1.4/schemas/twindex.schema.json"},"Entry":{"additionalProperties":false,"properties":{"aliases":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*(?:\\/[A-Za-z0-9][A-Za-z0-9._-]*)*$","type":"string"},"minItems":1,"type":"array"},"category":{"enum":["graph/v1","or/v1","or-set/v1"],"type":"string"},"citations":{"items":{"additionalProperties":false,"properties":{"doi":{"pattern":"^10\\.\\d{4,9}\\/\\S+$","type":"string"},"text":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["text"],"type":"object"},"type":"array"},"crosswalk":{"anyOf":[{"$ref":"#/$defs/Crosswalk"},{"additionalProperties":false,"properties":{"file":{"type":"string"}},"required":["file"],"type":"object"}]},"description":{"minLength":1,"type":"string"},"domain":{"minLength":1,"type":"string"},"expectedCounts":{"additionalProperties":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"licence":{"oneOf":[{"additionalProperties":false,"properties":{"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","type":"string"},"kind":{"const":"spdx","type":"string"}},"required":["kind","id"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"custom","type":"string"},"name":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["kind","name","url"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"unrecorded","type":"string"}},"required":["kind"],"type":"object"}]},"name":{"minLength":1,"type":"string"},"namespace":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"properties":{"additionalProperties":{"type":["string","number","boolean","null"]},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"recipe":{"oneOf":[{"additionalProperties":false,"properties":{"archive":{"anyOf":[{"additionalProperties":false,"properties":{"kind":{"const":"none","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"gzip","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"enum":["zip","tar","tar.gz"],"type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"enum":["zip","tar","tar.gz"],"type":"string"},"members":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","members"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"xlsx","type":"string"},"sheet":{"minLength":1,"type":"string"}},"required":["kind","sheet"],"type":"object"}]},"paging":{"oneOf":[{"additionalProperties":false,"properties":{"kind":{"const":"arcgis-query","type":"string"},"orderBy":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","type":"string"}},"required":["kind","orderBy"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"socrata-resource","type":"string"},"pageSize":{"maximum":9007199254740991,"minimum":1,"type":"integer"}},"required":["kind","pageSize"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"ckan-datastore","type":"string"},"pageSize":{"maximum":9007199254740991,"minimum":1,"type":"integer"}},"required":["kind","pageSize"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"openfema","type":"string"},"pageSize":{"maximum":9007199254740991,"minimum":1,"type":"integer"}},"required":["kind","pageSize"],"type":"object"}]},"parser":{"oneOf":[{"additionalProperties":false,"properties":{"commentPrefixes":{"items":{"minLength":1,"type":"string"},"type":"array"},"delimiter":{"enum":["detect","whitespace","tab","comma","space"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"edge-list","type":"string"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"sourceColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"targetColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","commentPrefixes","sourceColumn","targetColumn"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"matrix-market","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"gml","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"pajek","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"solomon","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"delimiter":{"enum":["comma","tab","semicolon"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"csv-table","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"nullValues":{"items":{"minLength":1,"type":"string"},"type":"array"},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","idColumn","numericColumns"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"minItems":1,"type":"array"},"dateColumns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"encoding":{"const":"utf-8","type":"string"},"format":{"const":"geojson","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"}},"required":["format","encoding","idColumn","numericColumns","dateColumns","columns"],"type":"object"}]},"source":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"status":{"const":"fetchable","type":"string"}},"required":["status","source","archive","parser"],"type":"object"},{"additionalProperties":false,"properties":{"reason":{"minLength":1,"type":"string"},"status":{"const":"not-fetchable","type":"string"}},"required":["status","reason"],"type":"object"}]},"sources":{"items":{"additionalProperties":false,"properties":{"retrieved":{"format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","type":"string"},"snapshot":{"additionalProperties":false,"properties":{"timestamp":{"pattern":"^\\d{8}(?:\\d{6})?$","type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","timestamp"],"type":"object"},"status":{"enum":["live","dead"],"type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","retrieved","status"],"type":"object"},"minItems":1,"type":"array"},"tags":{"items":{"minLength":1,"type":"string"},"type":"array"}},"required":["id","name","tags","category","sources","licence","citations","recipe","crosswalk","expectedCounts","properties"],"type":"object"}},"$id":"https://cdn.jsdelivr.net/npm/keepset@0.24.0/schemas/entry.schema.json","$ref":"#/$defs/Entry","$schema":"https://json-schema.org/draft/2020-12/schema"}
1
+ {"$defs":{"Crosswalk":{"$ref":"https://cdn.jsdelivr.net/npm/twindex@0.1.4/schemas/twindex.schema.json"},"Entry":{"additionalProperties":false,"properties":{"aliases":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*(?:\\/[A-Za-z0-9][A-Za-z0-9._-]*)*$","type":"string"},"minItems":1,"type":"array"},"category":{"enum":["graph/v1","or/v1","or-set/v1"],"type":"string"},"citations":{"items":{"additionalProperties":false,"properties":{"doi":{"pattern":"^10\\.\\d{4,9}\\/\\S+$","type":"string"},"text":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["text"],"type":"object"},"type":"array"},"crosswalk":{"anyOf":[{"$ref":"#/$defs/Crosswalk"},{"additionalProperties":false,"properties":{"file":{"type":"string"}},"required":["file"],"type":"object"}]},"description":{"minLength":1,"type":"string"},"domain":{"minLength":1,"type":"string"},"expectedCounts":{"additionalProperties":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"licence":{"oneOf":[{"additionalProperties":false,"properties":{"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","type":"string"},"kind":{"const":"spdx","type":"string"}},"required":["kind","id"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"custom","type":"string"},"name":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["kind","name","url"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"unrecorded","type":"string"}},"required":["kind"],"type":"object"}]},"name":{"minLength":1,"type":"string"},"namespace":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"properties":{"additionalProperties":{"type":["string","number","boolean","null"]},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"recipe":{"oneOf":[{"additionalProperties":false,"properties":{"archive":{"anyOf":[{"additionalProperties":false,"properties":{"kind":{"const":"none","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"gzip","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"enum":["zip","tar","tar.gz"],"type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"enum":["zip","tar","tar.gz"],"type":"string"},"members":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","members"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"xlsx","type":"string"},"sheet":{"minLength":1,"type":"string"}},"required":["kind","sheet"],"type":"object"}]},"paging":{"oneOf":[{"additionalProperties":false,"properties":{"kind":{"const":"arcgis-query","type":"string"},"orderBy":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","type":"string"}},"required":["kind","orderBy"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"socrata-resource","type":"string"},"pageSize":{"maximum":9007199254740991,"minimum":1,"type":"integer"}},"required":["kind","pageSize"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"ckan-datastore","type":"string"},"pageSize":{"maximum":9007199254740991,"minimum":1,"type":"integer"}},"required":["kind","pageSize"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"openfema","type":"string"},"pageSize":{"maximum":9007199254740991,"minimum":1,"type":"integer"}},"required":["kind","pageSize"],"type":"object"}]},"parser":{"oneOf":[{"additionalProperties":false,"properties":{"commentPrefixes":{"items":{"minLength":1,"type":"string"},"type":"array"},"delimiter":{"enum":["detect","whitespace","tab","comma","space"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"edge-list","type":"string"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"sourceColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"targetColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","commentPrefixes","sourceColumn","targetColumn"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"matrix-market","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"gml","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"pajek","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"solomon","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"top","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"toptw","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"tsplib","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"delimiter":{"enum":["comma","tab","semicolon"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"csv-table","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"nullValues":{"items":{"minLength":1,"type":"string"},"type":"array"},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","idColumn","numericColumns"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"minItems":1,"type":"array"},"dateColumns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"encoding":{"const":"utf-8","type":"string"},"format":{"const":"geojson","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"}},"required":["format","encoding","idColumn","numericColumns","dateColumns","columns"],"type":"object"}]},"source":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"status":{"const":"fetchable","type":"string"}},"required":["status","source","archive","parser"],"type":"object"},{"additionalProperties":false,"properties":{"reason":{"minLength":1,"type":"string"},"status":{"const":"not-fetchable","type":"string"}},"required":["status","reason"],"type":"object"}]},"sources":{"items":{"additionalProperties":false,"properties":{"retrieved":{"format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","type":"string"},"snapshot":{"additionalProperties":false,"properties":{"timestamp":{"pattern":"^\\d{8}(?:\\d{6})?$","type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","timestamp"],"type":"object"},"status":{"enum":["live","dead"],"type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","retrieved","status"],"type":"object"},"minItems":1,"type":"array"},"tags":{"items":{"minLength":1,"type":"string"},"type":"array"}},"required":["id","name","tags","category","sources","licence","citations","recipe","crosswalk","expectedCounts","properties"],"type":"object"}},"$id":"https://cdn.jsdelivr.net/npm/keepset@0.26.0/schemas/entry.schema.json","$ref":"#/$defs/Entry","$schema":"https://json-schema.org/draft/2020-12/schema"}
@@ -1 +1 @@
1
- {"$defs":{"Manifest":{"additionalProperties":false,"properties":{"description":{"minLength":1,"type":"string"},"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"lineage":{"additionalProperties":false,"properties":{"catalogue":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"repository":{"format":"uri","type":"string"},"version":{"pattern":"^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$","type":"string"}},"required":["catalogue","version","repository"],"type":"object"},"name":{"minLength":1,"type":"string"},"namespaceAliases":{"additionalProperties":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"propertyNames":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"type":"object"},"repository":{"format":"uri","type":"string"},"version":{"pattern":"^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$","type":"string"}},"required":["id","name","description","version","repository"],"type":"object"}},"$id":"https://cdn.jsdelivr.net/npm/keepset@0.24.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}
1
+ {"$defs":{"Manifest":{"additionalProperties":false,"properties":{"description":{"minLength":1,"type":"string"},"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"lineage":{"additionalProperties":false,"properties":{"catalogue":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"repository":{"format":"uri","type":"string"},"version":{"pattern":"^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$","type":"string"}},"required":["catalogue","version","repository"],"type":"object"},"name":{"minLength":1,"type":"string"},"namespaceAliases":{"additionalProperties":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"propertyNames":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"type":"object"},"repository":{"format":"uri","type":"string"},"version":{"pattern":"^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$","type":"string"}},"required":["id","name","description","version","repository"],"type":"object"}},"$id":"https://cdn.jsdelivr.net/npm/keepset@0.26.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}