keepset 0.23.0 → 0.24.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.
- package/README.md +1 -1
- package/dist/{bundled-BKl0C7kF.js → bundled-BWINQKLQ.js} +261 -26
- package/dist/{categories-BGjPWXpS.d.ts → categories-BfqmrAvf.d.ts} +162 -83
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node/fetch-dataset.d.ts +4 -1
- package/dist/node/fetch-dataset.js +179 -21
- package/dist-cli/keepset.mjs +1 -1
- package/package.json +1 -1
- package/schemas/entry.schema.json +1 -1
- package/schemas/manifest.schema.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ The catalogue itself is a manifest (`catalogue/manifest.json`: id, version, repo
|
|
|
63
63
|
|
|
64
64
|
## Categories and normalisation targets
|
|
65
65
|
|
|
66
|
-
A category names a normalisation target: the shape every entry in that category resolves to, whatever its native shape. `graph/v1` is `{ meta, nodes, edges }` with unique node ids and edges whose endpoints name nodes; its entries are graph datasets taken from upstream collections such as SuiteSparse, Netzschleuder, SNAP, KONECT and OGB. `or/v1` is `{ meta, nodes }`, an operations-research instance as the nodes it offers to visit, each flat with optional coordinates, prize, prize probability, duration, travel time, skill, territory, lifecycle status and times, and deadline, read from tabular sources by the `csv-table` parser (a delimited file, a member of an archive, a sheet of an Excel workbook, or an open data portal read a page at a time) and from ArcGIS feature layers by the `geojson` parser. Its entries come from a survey of open operations-research datasets, imported with a curation that records, for each source, what its download gives and how its columns map onto the node; a dataset whose source has no usable table is imported without a recipe and says why. A new category starts by defining its target schema in `src/targets/` and registering it in the category registry; an entry cannot name a category that does not exist. The statically generated site renders every entry with schema.org Dataset JSON-LD, so the catalogue surfaces in the dataset discovery layers.
|
|
66
|
+
A category names a normalisation target: the shape every entry in that category resolves to, whatever its native shape. `graph/v1` is `{ meta, nodes, edges }` with unique node ids and edges whose endpoints name nodes; its entries are graph datasets taken from upstream collections such as SuiteSparse, Netzschleuder, SNAP, KONECT and OGB. `or/v1` is `{ meta, nodes }`, an operations-research instance as the nodes it offers to visit, each flat with optional coordinates, prize, prize probability, duration, travel time, skill, territory, lifecycle status and times, and deadline, read from tabular sources by the `csv-table` parser (a delimited file, a member of an archive, a sheet of an Excel workbook, or an open data portal read a page at a time) and from ArcGIS feature layers by the `geojson` parser. Its entries come from a survey of open operations-research datasets, imported with a curation that records, for each source, what its download gives and how its columns map onto the node; a dataset whose source has no usable table is imported without a recipe and says why. `or-set/v1` is `{ meta, instances }`, a collection of operations-research instances such as a benchmark suite, each instance an `or/v1` document under an id of its own (for a collection read from an archive, the path of the member it came from): a recipe selects every matching archive member with `members`, the parser reads each one, and a member that does not parse fails the fetch and is named. A new category starts by defining its target schema in `src/targets/` and registering it in the category registry; an entry cannot name a category that does not exist. The statically generated site renders every entry with schema.org Dataset JSON-LD, so the catalogue surfaces in the dataset discovery layers.
|
|
67
67
|
|
|
68
68
|
## Licensing
|
|
69
69
|
|
|
@@ -113,14 +113,16 @@ const JsonScalarSchema = z.union([
|
|
|
113
113
|
* Reports each node whose id an earlier node already has, as an issue on that node's id.
|
|
114
114
|
* @param nodes - The nodes of a target document, in order.
|
|
115
115
|
* @param ctx - The refinement context to report to.
|
|
116
|
+
* @param prefix - The path of the object holding the `nodes` array, for a document that nests nodes inside another.
|
|
116
117
|
* @returns The set of ids the nodes carry.
|
|
117
118
|
*/
|
|
118
|
-
function addDuplicateIdIssues(nodes, ctx) {
|
|
119
|
+
function addDuplicateIdIssues(nodes, ctx, prefix = []) {
|
|
119
120
|
const ids = /* @__PURE__ */ new Set();
|
|
120
121
|
for (const [index, node] of nodes.entries()) {
|
|
121
122
|
if (ids.has(node.id)) ctx.addIssue({
|
|
122
123
|
code: "custom",
|
|
123
124
|
path: [
|
|
125
|
+
...prefix,
|
|
124
126
|
"nodes",
|
|
125
127
|
index,
|
|
126
128
|
"id"
|
|
@@ -202,22 +204,65 @@ const OrV1Schema = define(z.object({
|
|
|
202
204
|
addDuplicateIdIssues(instance.nodes, ctx);
|
|
203
205
|
}));
|
|
204
206
|
//#endregion
|
|
207
|
+
//#region src/targets/or-set-v1.ts
|
|
208
|
+
/** One instance of a set: an or/v1 instance, `{ meta, nodes }`, with the id that names it within the set (for a collection read from an archive, the member's path). */
|
|
209
|
+
const OrInstanceSchema = z.object({
|
|
210
|
+
id: z.string().min(1),
|
|
211
|
+
meta: recordOf(z.string().min(1), JsonScalarSchema),
|
|
212
|
+
nodes: z.array(OrNodeSchema)
|
|
213
|
+
}).strict();
|
|
214
|
+
/**
|
|
215
|
+
* The or-set/v1 normalisation target: `{ meta, instances }`, a collection of operations-research instances such as a benchmark suite, each instance an or/v1 document (`{ meta, nodes }`) under an id of its own. Beyond the shapes it checks that instance ids are unique in the set and node ids unique within each instance. A node id need not be unique across instances, because every instance numbers its own nodes.
|
|
216
|
+
*/
|
|
217
|
+
const OrSetV1Schema = define(z.object({
|
|
218
|
+
meta: recordOf(z.string().min(1), JsonScalarSchema),
|
|
219
|
+
instances: z.array(OrInstanceSchema)
|
|
220
|
+
}).strict().superRefine((set, ctx) => {
|
|
221
|
+
const ids = /* @__PURE__ */ new Set();
|
|
222
|
+
for (const [index, instance] of set.instances.entries()) {
|
|
223
|
+
if (ids.has(instance.id)) ctx.addIssue({
|
|
224
|
+
code: "custom",
|
|
225
|
+
path: [
|
|
226
|
+
"instances",
|
|
227
|
+
index,
|
|
228
|
+
"id"
|
|
229
|
+
],
|
|
230
|
+
message: `duplicate instance id ${JSON.stringify(instance.id)}`
|
|
231
|
+
});
|
|
232
|
+
ids.add(instance.id);
|
|
233
|
+
addDuplicateIdIssues(instance.nodes, ctx, ["instances", index]);
|
|
234
|
+
}
|
|
235
|
+
}));
|
|
236
|
+
//#endregion
|
|
205
237
|
//#region src/categories.ts
|
|
206
238
|
/** Every category id the registry knows. A category id is `<kind>/v<version>`: the kind of data, and the version of its normalisation target, so a breaking change to a target is a new category rather than an edit to an old one. */
|
|
207
|
-
const CategoryIdSchema = z.enum([
|
|
239
|
+
const CategoryIdSchema = z.enum([
|
|
240
|
+
"graph/v1",
|
|
241
|
+
"or/v1",
|
|
242
|
+
"or-set/v1"
|
|
243
|
+
]);
|
|
208
244
|
/** The registry. Adding a category is adding its id to `CategoryIdSchema` and its entry here, with its target schema beside graph/v1's in src/targets/: the mapped type makes the compiler reject a registry that misses an id or has one too many. */
|
|
209
245
|
const registry = {
|
|
210
246
|
"graph/v1": {
|
|
211
247
|
id: "graph/v1",
|
|
212
248
|
description: "A graph as { meta, nodes, edges }: a flat record of scalar metadata, nodes carrying at least an id, and edges carrying at least source and target node ids, with optional node and edge types.",
|
|
249
|
+
shape: "document",
|
|
213
250
|
target: GraphV1Schema,
|
|
214
251
|
counted: ["nodes", "edges"]
|
|
215
252
|
},
|
|
216
253
|
"or/v1": {
|
|
217
254
|
id: "or/v1",
|
|
218
255
|
description: "An operations-research instance as { meta, nodes }: a flat record of scalar metadata and nodes carrying at least an id, with optional coordinates, prize, prize probability, duration, travel time, skill, territory, lifecycle status and times, and deadline.",
|
|
256
|
+
shape: "document",
|
|
219
257
|
target: OrV1Schema,
|
|
220
258
|
counted: ["nodes"]
|
|
259
|
+
},
|
|
260
|
+
"or-set/v1": {
|
|
261
|
+
id: "or-set/v1",
|
|
262
|
+
description: "A collection of operations-research instances as { meta, instances }: a flat record of scalar metadata and instances, each an or/v1 document ({ meta, nodes }) under an id of its own, read from every matching member of an archive.",
|
|
263
|
+
shape: "collection",
|
|
264
|
+
target: OrSetV1Schema,
|
|
265
|
+
counted: ["instances"]
|
|
221
266
|
}
|
|
222
267
|
};
|
|
223
268
|
function getCategory(id) {
|
|
@@ -410,22 +455,26 @@ const CitationSchema = z.object({
|
|
|
410
455
|
const RelativePathSchema = z.string().refine((path) => path.split("/").every((segment) => segment !== "" && segment !== "." && segment !== ".."), { message: "must be a relative path of non-empty segments with no '.' or '..'" });
|
|
411
456
|
/** Which file of an archive to read: a member path, matched against a member's whole path or its trailing segments (so `net.gml` finds `net/net.gml`), or the member whose name ends with one of a list of extensions (a format can have several, such as Pajek's `.net` and `.paj`). Either must match exactly one member when the recipe runs; macOS resource-fork members (`__MACOSX/` and `._` files) never match. */
|
|
412
457
|
const ArchiveMemberSchema = z.union([z.object({ path: RelativePathSchema }).strict(), z.object({ extensions: z.array(z.string().regex(/^\.[A-Za-z0-9][A-Za-z0-9.]*$/, { message: "must be a file extension such as .mtx" })).min(1) }).strict()]);
|
|
413
|
-
/**
|
|
414
|
-
const
|
|
458
|
+
/** The archive kinds that hold several files, from which a recipe selects one (`member`) or every one a selector matches (`members`). */
|
|
459
|
+
const FileArchiveKindSchema = z.enum([
|
|
460
|
+
"zip",
|
|
461
|
+
"tar",
|
|
462
|
+
"tar.gz"
|
|
463
|
+
]);
|
|
464
|
+
/** An archive a collection is read from: every file a selector matches, at least one. */
|
|
465
|
+
const CollectionArchiveSchema = z.object({
|
|
466
|
+
kind: FileArchiveKindSchema,
|
|
467
|
+
members: ArchiveMemberSchema
|
|
468
|
+
}).strict();
|
|
469
|
+
/** How the downloaded bytes unpack into the file the parser reads, or for a collection the files it reads one by one. A `members` selector gives every member it matches, at least one, in path order. A workbook (`xlsx`) is read as the CSV of its named sheet, with dates as ISO 8601 text, for the `csv-table` parser. */
|
|
470
|
+
const ArchiveSchema = z.union([
|
|
415
471
|
z.object({ kind: z.literal("none") }).strict(),
|
|
416
472
|
z.object({ kind: z.literal("gzip") }).strict(),
|
|
417
473
|
z.object({
|
|
418
|
-
kind:
|
|
419
|
-
member: ArchiveMemberSchema
|
|
420
|
-
}).strict(),
|
|
421
|
-
z.object({
|
|
422
|
-
kind: z.literal("tar"),
|
|
423
|
-
member: ArchiveMemberSchema
|
|
424
|
-
}).strict(),
|
|
425
|
-
z.object({
|
|
426
|
-
kind: z.literal("tar.gz"),
|
|
474
|
+
kind: FileArchiveKindSchema,
|
|
427
475
|
member: ArchiveMemberSchema
|
|
428
476
|
}).strict(),
|
|
477
|
+
CollectionArchiveSchema,
|
|
429
478
|
z.object({
|
|
430
479
|
kind: z.literal("xlsx"),
|
|
431
480
|
sheet: z.string().min(1)
|
|
@@ -468,6 +517,10 @@ const ParserSchema = z.discriminatedUnion("format", [
|
|
|
468
517
|
format: z.literal("pajek"),
|
|
469
518
|
encoding: EncodingSchema
|
|
470
519
|
}).strict(),
|
|
520
|
+
z.object({
|
|
521
|
+
format: z.literal("solomon"),
|
|
522
|
+
encoding: EncodingSchema
|
|
523
|
+
}).strict(),
|
|
471
524
|
z.object({
|
|
472
525
|
format: z.literal("csv-table"),
|
|
473
526
|
encoding: EncodingSchema,
|
|
@@ -587,6 +640,24 @@ const EntrySchema = define(z.object({
|
|
|
587
640
|
path: ["recipe", "source"],
|
|
588
641
|
message: `names source ${String(entry.recipe.source)} but the entry has ${String(entry.sources.length)}`
|
|
589
642
|
});
|
|
643
|
+
if (entry.recipe.status === "fetchable") {
|
|
644
|
+
const selectsMembers = "members" in entry.recipe.archive;
|
|
645
|
+
const { shape } = getCategory(entry.category);
|
|
646
|
+
if (shape === "collection" && !selectsMembers) ctx.addIssue({
|
|
647
|
+
code: "custom",
|
|
648
|
+
path: ["recipe", "archive"],
|
|
649
|
+
message: `${entry.category} is a collection, so the archive must select its members with \`members\``
|
|
650
|
+
});
|
|
651
|
+
if (shape === "document" && selectsMembers) ctx.addIssue({
|
|
652
|
+
code: "custom",
|
|
653
|
+
path: [
|
|
654
|
+
"recipe",
|
|
655
|
+
"archive",
|
|
656
|
+
"members"
|
|
657
|
+
],
|
|
658
|
+
message: `${entry.category} is one document, so the archive selects one file with \`member\``
|
|
659
|
+
});
|
|
660
|
+
}
|
|
590
661
|
if ("target" in entry.crosswalk && entry.crosswalk.target !== entry.category) ctx.addIssue({
|
|
591
662
|
code: "custom",
|
|
592
663
|
path: ["crosswalk", "target"],
|
|
@@ -909,7 +980,7 @@ var catalogue_default = {
|
|
|
909
980
|
"id": "keepset",
|
|
910
981
|
"name": "keepset",
|
|
911
982
|
"description": "The keepset catalogue: datasets with machine-readable recipes that fetch each one and normalise it into its category's target shape.",
|
|
912
|
-
"version": "1.
|
|
983
|
+
"version": "1.12.0",
|
|
913
984
|
"repository": "https://github.com/Mearman/keepset",
|
|
914
985
|
"namespaceAliases": {
|
|
915
986
|
"networkrepository": ["nr"],
|
|
@@ -7105,7 +7176,7 @@ var catalogue_default = {
|
|
|
7105
7176
|
"duration",
|
|
7106
7177
|
"travelTime"
|
|
7107
7178
|
],
|
|
7108
|
-
"category": "or/v1",
|
|
7179
|
+
"category": "or-set/v1",
|
|
7109
7180
|
"sources": [{
|
|
7110
7181
|
"url": "https://github.com/psxjpc/MOVRPTW-Generator",
|
|
7111
7182
|
"retrieved": "2026-10-19",
|
|
@@ -7121,12 +7192,95 @@ var catalogue_default = {
|
|
|
7121
7192
|
},
|
|
7122
7193
|
"citations": [],
|
|
7123
7194
|
"recipe": {
|
|
7124
|
-
"status": "
|
|
7125
|
-
"
|
|
7195
|
+
"status": "fetchable",
|
|
7196
|
+
"source": 1,
|
|
7197
|
+
"archive": {
|
|
7198
|
+
"kind": "zip",
|
|
7199
|
+
"members": { "extensions": [
|
|
7200
|
+
".tw0Specs.dat",
|
|
7201
|
+
".tw1Specs.dat",
|
|
7202
|
+
".tw2Specs.dat",
|
|
7203
|
+
".tw3Specs.dat",
|
|
7204
|
+
".tw4Specs.dat"
|
|
7205
|
+
] }
|
|
7206
|
+
},
|
|
7207
|
+
"parser": {
|
|
7208
|
+
"format": "solomon",
|
|
7209
|
+
"encoding": "utf-8"
|
|
7210
|
+
}
|
|
7126
7211
|
},
|
|
7127
|
-
"crosswalk": {
|
|
7128
|
-
|
|
7129
|
-
|
|
7212
|
+
"crosswalk": {
|
|
7213
|
+
"source": "solomon",
|
|
7214
|
+
"target": "or-set/v1",
|
|
7215
|
+
"rules": [
|
|
7216
|
+
{
|
|
7217
|
+
"op": "move",
|
|
7218
|
+
"from": "meta",
|
|
7219
|
+
"to": "meta"
|
|
7220
|
+
},
|
|
7221
|
+
{
|
|
7222
|
+
"op": "move",
|
|
7223
|
+
"from": "members[].id",
|
|
7224
|
+
"to": "instances[].id"
|
|
7225
|
+
},
|
|
7226
|
+
{
|
|
7227
|
+
"op": "move",
|
|
7228
|
+
"from": "members[].meta.name",
|
|
7229
|
+
"to": "instances[].meta.name"
|
|
7230
|
+
},
|
|
7231
|
+
{
|
|
7232
|
+
"op": "move",
|
|
7233
|
+
"from": "members[].meta.vehicles",
|
|
7234
|
+
"to": "instances[].meta.vehicles"
|
|
7235
|
+
},
|
|
7236
|
+
{
|
|
7237
|
+
"op": "move",
|
|
7238
|
+
"from": "members[].meta.capacity",
|
|
7239
|
+
"to": "instances[].meta.capacity"
|
|
7240
|
+
},
|
|
7241
|
+
{
|
|
7242
|
+
"op": "move",
|
|
7243
|
+
"from": "members[].nodes[].id",
|
|
7244
|
+
"to": "instances[].nodes[].id"
|
|
7245
|
+
},
|
|
7246
|
+
{
|
|
7247
|
+
"op": "move",
|
|
7248
|
+
"from": "members[].nodes[].due",
|
|
7249
|
+
"to": "instances[].nodes[].deadline"
|
|
7250
|
+
},
|
|
7251
|
+
{
|
|
7252
|
+
"op": "move",
|
|
7253
|
+
"from": "members[].nodes[].service",
|
|
7254
|
+
"to": "instances[].nodes[].duration"
|
|
7255
|
+
},
|
|
7256
|
+
{
|
|
7257
|
+
"op": "move",
|
|
7258
|
+
"from": "members[].nodes[].x",
|
|
7259
|
+
"to": "instances[].nodes[].lat"
|
|
7260
|
+
},
|
|
7261
|
+
{
|
|
7262
|
+
"op": "move",
|
|
7263
|
+
"from": "members[].nodes[].y",
|
|
7264
|
+
"to": "instances[].nodes[].lon"
|
|
7265
|
+
},
|
|
7266
|
+
{
|
|
7267
|
+
"op": "move",
|
|
7268
|
+
"from": "members[].nodes[].demand",
|
|
7269
|
+
"to": "instances[].nodes[].demand"
|
|
7270
|
+
},
|
|
7271
|
+
{
|
|
7272
|
+
"op": "move",
|
|
7273
|
+
"from": "members[].nodes[].ready",
|
|
7274
|
+
"to": "instances[].nodes[].ready"
|
|
7275
|
+
}
|
|
7276
|
+
],
|
|
7277
|
+
"oneWay": []
|
|
7278
|
+
},
|
|
7279
|
+
"expectedCounts": { "instances": 45 },
|
|
7280
|
+
"properties": {
|
|
7281
|
+
"surveyId": "or-9",
|
|
7282
|
+
"timeUnit": "seconds"
|
|
7283
|
+
}
|
|
7130
7284
|
},
|
|
7131
7285
|
{
|
|
7132
7286
|
"id": "github-zhangchenhaoseu-jd-cold-chain-nodes",
|
|
@@ -8586,21 +8740,102 @@ var catalogue_default = {
|
|
|
8586
8740
|
"duration",
|
|
8587
8741
|
"travelTime"
|
|
8588
8742
|
],
|
|
8589
|
-
"category": "or/v1",
|
|
8743
|
+
"category": "or-set/v1",
|
|
8590
8744
|
"sources": [{
|
|
8591
8745
|
"url": "https://neo.lcc.uma.es/vrp/vrp-instances/capacitated-vrp-with-time-windows-instances/",
|
|
8592
8746
|
"retrieved": "2026-10-19",
|
|
8593
8747
|
"status": "live"
|
|
8748
|
+
}, {
|
|
8749
|
+
"url": "https://neo.lcc.uma.es/vrp/wp-content/data/instances/solomon/solomon_100.zip",
|
|
8750
|
+
"retrieved": "2026-10-19",
|
|
8751
|
+
"status": "live"
|
|
8594
8752
|
}],
|
|
8595
8753
|
"licence": { "kind": "unrecorded" },
|
|
8596
8754
|
"citations": [],
|
|
8597
8755
|
"recipe": {
|
|
8598
|
-
"status": "
|
|
8599
|
-
"
|
|
8756
|
+
"status": "fetchable",
|
|
8757
|
+
"source": 1,
|
|
8758
|
+
"archive": {
|
|
8759
|
+
"kind": "zip",
|
|
8760
|
+
"members": { "extensions": [".txt"] }
|
|
8761
|
+
},
|
|
8762
|
+
"parser": {
|
|
8763
|
+
"format": "solomon",
|
|
8764
|
+
"encoding": "utf-8"
|
|
8765
|
+
}
|
|
8600
8766
|
},
|
|
8601
|
-
"crosswalk": {
|
|
8602
|
-
|
|
8603
|
-
|
|
8767
|
+
"crosswalk": {
|
|
8768
|
+
"source": "solomon",
|
|
8769
|
+
"target": "or-set/v1",
|
|
8770
|
+
"rules": [
|
|
8771
|
+
{
|
|
8772
|
+
"op": "move",
|
|
8773
|
+
"from": "meta",
|
|
8774
|
+
"to": "meta"
|
|
8775
|
+
},
|
|
8776
|
+
{
|
|
8777
|
+
"op": "move",
|
|
8778
|
+
"from": "members[].id",
|
|
8779
|
+
"to": "instances[].id"
|
|
8780
|
+
},
|
|
8781
|
+
{
|
|
8782
|
+
"op": "move",
|
|
8783
|
+
"from": "members[].meta.name",
|
|
8784
|
+
"to": "instances[].meta.name"
|
|
8785
|
+
},
|
|
8786
|
+
{
|
|
8787
|
+
"op": "move",
|
|
8788
|
+
"from": "members[].meta.vehicles",
|
|
8789
|
+
"to": "instances[].meta.vehicles"
|
|
8790
|
+
},
|
|
8791
|
+
{
|
|
8792
|
+
"op": "move",
|
|
8793
|
+
"from": "members[].meta.capacity",
|
|
8794
|
+
"to": "instances[].meta.capacity"
|
|
8795
|
+
},
|
|
8796
|
+
{
|
|
8797
|
+
"op": "move",
|
|
8798
|
+
"from": "members[].nodes[].id",
|
|
8799
|
+
"to": "instances[].nodes[].id"
|
|
8800
|
+
},
|
|
8801
|
+
{
|
|
8802
|
+
"op": "move",
|
|
8803
|
+
"from": "members[].nodes[].due",
|
|
8804
|
+
"to": "instances[].nodes[].deadline"
|
|
8805
|
+
},
|
|
8806
|
+
{
|
|
8807
|
+
"op": "move",
|
|
8808
|
+
"from": "members[].nodes[].x",
|
|
8809
|
+
"to": "instances[].nodes[].x"
|
|
8810
|
+
},
|
|
8811
|
+
{
|
|
8812
|
+
"op": "move",
|
|
8813
|
+
"from": "members[].nodes[].y",
|
|
8814
|
+
"to": "instances[].nodes[].y"
|
|
8815
|
+
},
|
|
8816
|
+
{
|
|
8817
|
+
"op": "move",
|
|
8818
|
+
"from": "members[].nodes[].demand",
|
|
8819
|
+
"to": "instances[].nodes[].demand"
|
|
8820
|
+
},
|
|
8821
|
+
{
|
|
8822
|
+
"op": "move",
|
|
8823
|
+
"from": "members[].nodes[].ready",
|
|
8824
|
+
"to": "instances[].nodes[].ready"
|
|
8825
|
+
},
|
|
8826
|
+
{
|
|
8827
|
+
"op": "move",
|
|
8828
|
+
"from": "members[].nodes[].service",
|
|
8829
|
+
"to": "instances[].nodes[].service"
|
|
8830
|
+
}
|
|
8831
|
+
],
|
|
8832
|
+
"oneWay": []
|
|
8833
|
+
},
|
|
8834
|
+
"expectedCounts": { "instances": 56 },
|
|
8835
|
+
"properties": {
|
|
8836
|
+
"surveyId": "or-5",
|
|
8837
|
+
"timeUnit": "abstract units"
|
|
8838
|
+
}
|
|
8604
8839
|
},
|
|
8605
8840
|
{
|
|
8606
8841
|
"id": "leeds-fixed-penalty-notices",
|
|
@@ -70,28 +70,29 @@ declare const ArchiveMemberSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
70
70
|
extensions: z.ZodArray<z.ZodString>;
|
|
71
71
|
}, z.core.$strict>]>;
|
|
72
72
|
type ArchiveMember = z.infer<typeof ArchiveMemberSchema>;
|
|
73
|
-
/** How the downloaded bytes unpack into the
|
|
74
|
-
declare const ArchiveSchema: z.
|
|
73
|
+
/** How the downloaded bytes unpack into the file the parser reads, or for a collection the files it reads one by one. A `members` selector gives every member it matches, at least one, in path order. A workbook (`xlsx`) is read as the CSV of its named sheet, with dates as ISO 8601 text, for the `csv-table` parser. */
|
|
74
|
+
declare const ArchiveSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
75
75
|
kind: z.ZodLiteral<"none">;
|
|
76
76
|
}, z.core.$strict>, z.ZodObject<{
|
|
77
77
|
kind: z.ZodLiteral<"gzip">;
|
|
78
78
|
}, z.core.$strict>, z.ZodObject<{
|
|
79
|
-
kind: z.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}, z.core.$strict>]>;
|
|
85
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
86
|
-
kind: z.ZodLiteral<"tar">;
|
|
79
|
+
kind: z.ZodEnum<{
|
|
80
|
+
zip: "zip";
|
|
81
|
+
tar: "tar";
|
|
82
|
+
"tar.gz": "tar.gz";
|
|
83
|
+
}>;
|
|
87
84
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
88
85
|
path: z.ZodString;
|
|
89
86
|
}, z.core.$strict>, z.ZodObject<{
|
|
90
87
|
extensions: z.ZodArray<z.ZodString>;
|
|
91
88
|
}, z.core.$strict>]>;
|
|
92
89
|
}, z.core.$strict>, z.ZodObject<{
|
|
93
|
-
kind: z.
|
|
94
|
-
|
|
90
|
+
kind: z.ZodEnum<{
|
|
91
|
+
zip: "zip";
|
|
92
|
+
tar: "tar";
|
|
93
|
+
"tar.gz": "tar.gz";
|
|
94
|
+
}>;
|
|
95
|
+
members: z.ZodUnion<readonly [z.ZodObject<{
|
|
95
96
|
path: z.ZodString;
|
|
96
97
|
}, z.core.$strict>, z.ZodObject<{
|
|
97
98
|
extensions: z.ZodArray<z.ZodString>;
|
|
@@ -99,7 +100,7 @@ declare const ArchiveSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
99
100
|
}, z.core.$strict>, z.ZodObject<{
|
|
100
101
|
kind: z.ZodLiteral<"xlsx">;
|
|
101
102
|
sheet: z.ZodString;
|
|
102
|
-
}, z.core.$strict>]
|
|
103
|
+
}, z.core.$strict>]>;
|
|
103
104
|
type Archive = z.infer<typeof ArchiveSchema>;
|
|
104
105
|
/** A column of a table as the table parser names it: the header with every run of characters outside letters, digits and underscore replaced by one underscore, so it is a valid segment of a twindex path. */
|
|
105
106
|
declare const ColumnNameSchema: z.ZodString;
|
|
@@ -139,6 +140,12 @@ declare const ParserSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
139
140
|
"utf-8": "utf-8";
|
|
140
141
|
latin1: "latin1";
|
|
141
142
|
}>;
|
|
143
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
144
|
+
format: z.ZodLiteral<"solomon">;
|
|
145
|
+
encoding: z.ZodEnum<{
|
|
146
|
+
"utf-8": "utf-8";
|
|
147
|
+
latin1: "latin1";
|
|
148
|
+
}>;
|
|
142
149
|
}, z.core.$strict>, z.ZodObject<{
|
|
143
150
|
format: z.ZodLiteral<"csv-table">;
|
|
144
151
|
encoding: z.ZodEnum<{
|
|
@@ -187,27 +194,28 @@ declare const RecipeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
187
194
|
kind: z.ZodLiteral<"openfema">;
|
|
188
195
|
pageSize: z.ZodInt;
|
|
189
196
|
}, z.core.$strict>], "kind">>;
|
|
190
|
-
archive: z.
|
|
197
|
+
archive: z.ZodUnion<readonly [z.ZodObject<{
|
|
191
198
|
kind: z.ZodLiteral<"none">;
|
|
192
199
|
}, z.core.$strict>, z.ZodObject<{
|
|
193
200
|
kind: z.ZodLiteral<"gzip">;
|
|
194
201
|
}, z.core.$strict>, z.ZodObject<{
|
|
195
|
-
kind: z.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}, z.core.$strict>]>;
|
|
201
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
202
|
-
kind: z.ZodLiteral<"tar">;
|
|
202
|
+
kind: z.ZodEnum<{
|
|
203
|
+
zip: "zip";
|
|
204
|
+
tar: "tar";
|
|
205
|
+
"tar.gz": "tar.gz";
|
|
206
|
+
}>;
|
|
203
207
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
204
208
|
path: z.ZodString;
|
|
205
209
|
}, z.core.$strict>, z.ZodObject<{
|
|
206
210
|
extensions: z.ZodArray<z.ZodString>;
|
|
207
211
|
}, z.core.$strict>]>;
|
|
208
212
|
}, z.core.$strict>, z.ZodObject<{
|
|
209
|
-
kind: z.
|
|
210
|
-
|
|
213
|
+
kind: z.ZodEnum<{
|
|
214
|
+
zip: "zip";
|
|
215
|
+
tar: "tar";
|
|
216
|
+
"tar.gz": "tar.gz";
|
|
217
|
+
}>;
|
|
218
|
+
members: z.ZodUnion<readonly [z.ZodObject<{
|
|
211
219
|
path: z.ZodString;
|
|
212
220
|
}, z.core.$strict>, z.ZodObject<{
|
|
213
221
|
extensions: z.ZodArray<z.ZodString>;
|
|
@@ -215,7 +223,7 @@ declare const RecipeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
215
223
|
}, z.core.$strict>, z.ZodObject<{
|
|
216
224
|
kind: z.ZodLiteral<"xlsx">;
|
|
217
225
|
sheet: z.ZodString;
|
|
218
|
-
}, z.core.$strict>]
|
|
226
|
+
}, z.core.$strict>]>;
|
|
219
227
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
220
228
|
format: z.ZodLiteral<"edge-list">;
|
|
221
229
|
encoding: z.ZodEnum<{
|
|
@@ -251,6 +259,12 @@ declare const RecipeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
251
259
|
"utf-8": "utf-8";
|
|
252
260
|
latin1: "latin1";
|
|
253
261
|
}>;
|
|
262
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
263
|
+
format: z.ZodLiteral<"solomon">;
|
|
264
|
+
encoding: z.ZodEnum<{
|
|
265
|
+
"utf-8": "utf-8";
|
|
266
|
+
latin1: "latin1";
|
|
267
|
+
}>;
|
|
254
268
|
}, z.core.$strict>, z.ZodObject<{
|
|
255
269
|
format: z.ZodLiteral<"csv-table">;
|
|
256
270
|
encoding: z.ZodEnum<{
|
|
@@ -309,6 +323,7 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
309
323
|
category: z.ZodEnum<{
|
|
310
324
|
"graph/v1": "graph/v1";
|
|
311
325
|
"or/v1": "or/v1";
|
|
326
|
+
"or-set/v1": "or-set/v1";
|
|
312
327
|
}>;
|
|
313
328
|
sources: z.ZodArray<z.ZodObject<{
|
|
314
329
|
url: z.ZodURL;
|
|
@@ -353,27 +368,28 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
353
368
|
kind: z.ZodLiteral<"openfema">;
|
|
354
369
|
pageSize: z.ZodInt;
|
|
355
370
|
}, z.core.$strict>], "kind">>;
|
|
356
|
-
archive: z.
|
|
371
|
+
archive: z.ZodUnion<readonly [z.ZodObject<{
|
|
357
372
|
kind: z.ZodLiteral<"none">;
|
|
358
373
|
}, z.core.$strict>, z.ZodObject<{
|
|
359
374
|
kind: z.ZodLiteral<"gzip">;
|
|
360
375
|
}, z.core.$strict>, z.ZodObject<{
|
|
361
|
-
kind: z.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}, z.core.$strict>]>;
|
|
367
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
368
|
-
kind: z.ZodLiteral<"tar">;
|
|
376
|
+
kind: z.ZodEnum<{
|
|
377
|
+
zip: "zip";
|
|
378
|
+
tar: "tar";
|
|
379
|
+
"tar.gz": "tar.gz";
|
|
380
|
+
}>;
|
|
369
381
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
370
382
|
path: z.ZodString;
|
|
371
383
|
}, z.core.$strict>, z.ZodObject<{
|
|
372
384
|
extensions: z.ZodArray<z.ZodString>;
|
|
373
385
|
}, z.core.$strict>]>;
|
|
374
386
|
}, z.core.$strict>, z.ZodObject<{
|
|
375
|
-
kind: z.
|
|
376
|
-
|
|
387
|
+
kind: z.ZodEnum<{
|
|
388
|
+
zip: "zip";
|
|
389
|
+
tar: "tar";
|
|
390
|
+
"tar.gz": "tar.gz";
|
|
391
|
+
}>;
|
|
392
|
+
members: z.ZodUnion<readonly [z.ZodObject<{
|
|
377
393
|
path: z.ZodString;
|
|
378
394
|
}, z.core.$strict>, z.ZodObject<{
|
|
379
395
|
extensions: z.ZodArray<z.ZodString>;
|
|
@@ -381,7 +397,7 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
381
397
|
}, z.core.$strict>, z.ZodObject<{
|
|
382
398
|
kind: z.ZodLiteral<"xlsx">;
|
|
383
399
|
sheet: z.ZodString;
|
|
384
|
-
}, z.core.$strict>]
|
|
400
|
+
}, z.core.$strict>]>;
|
|
385
401
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
386
402
|
format: z.ZodLiteral<"edge-list">;
|
|
387
403
|
encoding: z.ZodEnum<{
|
|
@@ -417,6 +433,12 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
417
433
|
"utf-8": "utf-8";
|
|
418
434
|
latin1: "latin1";
|
|
419
435
|
}>;
|
|
436
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
437
|
+
format: z.ZodLiteral<"solomon">;
|
|
438
|
+
encoding: z.ZodEnum<{
|
|
439
|
+
"utf-8": "utf-8";
|
|
440
|
+
latin1: "latin1";
|
|
441
|
+
}>;
|
|
420
442
|
}, z.core.$strict>, z.ZodObject<{
|
|
421
443
|
format: z.ZodLiteral<"csv-table">;
|
|
422
444
|
encoding: z.ZodEnum<{
|
|
@@ -463,7 +485,7 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
463
485
|
id: string;
|
|
464
486
|
name: string;
|
|
465
487
|
tags: string[];
|
|
466
|
-
category: "graph/v1" | "or/v1";
|
|
488
|
+
category: "graph/v1" | "or/v1" | "or-set/v1";
|
|
467
489
|
sources: {
|
|
468
490
|
url: string;
|
|
469
491
|
retrieved: string;
|
|
@@ -496,22 +518,15 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
496
518
|
} | {
|
|
497
519
|
kind: "gzip";
|
|
498
520
|
} | {
|
|
499
|
-
kind: "zip";
|
|
500
|
-
member: {
|
|
501
|
-
path: string;
|
|
502
|
-
} | {
|
|
503
|
-
extensions: string[];
|
|
504
|
-
};
|
|
505
|
-
} | {
|
|
506
|
-
kind: "tar";
|
|
521
|
+
kind: "zip" | "tar" | "tar.gz";
|
|
507
522
|
member: {
|
|
508
523
|
path: string;
|
|
509
524
|
} | {
|
|
510
525
|
extensions: string[];
|
|
511
526
|
};
|
|
512
527
|
} | {
|
|
513
|
-
kind: "tar.gz";
|
|
514
|
-
|
|
528
|
+
kind: "zip" | "tar" | "tar.gz";
|
|
529
|
+
members: {
|
|
515
530
|
path: string;
|
|
516
531
|
} | {
|
|
517
532
|
extensions: string[];
|
|
@@ -559,6 +574,9 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
559
574
|
} | {
|
|
560
575
|
format: "pajek";
|
|
561
576
|
encoding: "utf-8" | "latin1";
|
|
577
|
+
} | {
|
|
578
|
+
format: "solomon";
|
|
579
|
+
encoding: "utf-8" | "latin1";
|
|
562
580
|
};
|
|
563
581
|
paging?: {
|
|
564
582
|
kind: "arcgis-query";
|
|
@@ -692,6 +710,7 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
692
710
|
category: z.ZodEnum<{
|
|
693
711
|
"graph/v1": "graph/v1";
|
|
694
712
|
"or/v1": "or/v1";
|
|
713
|
+
"or-set/v1": "or-set/v1";
|
|
695
714
|
}>;
|
|
696
715
|
sources: z.ZodArray<z.ZodObject<{
|
|
697
716
|
url: z.ZodURL;
|
|
@@ -736,27 +755,28 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
736
755
|
kind: z.ZodLiteral<"openfema">;
|
|
737
756
|
pageSize: z.ZodInt;
|
|
738
757
|
}, z.core.$strict>], "kind">>;
|
|
739
|
-
archive: z.
|
|
758
|
+
archive: z.ZodUnion<readonly [z.ZodObject<{
|
|
740
759
|
kind: z.ZodLiteral<"none">;
|
|
741
760
|
}, z.core.$strict>, z.ZodObject<{
|
|
742
761
|
kind: z.ZodLiteral<"gzip">;
|
|
743
762
|
}, z.core.$strict>, z.ZodObject<{
|
|
744
|
-
kind: z.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}, z.core.$strict>]>;
|
|
750
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
751
|
-
kind: z.ZodLiteral<"tar">;
|
|
763
|
+
kind: z.ZodEnum<{
|
|
764
|
+
zip: "zip";
|
|
765
|
+
tar: "tar";
|
|
766
|
+
"tar.gz": "tar.gz";
|
|
767
|
+
}>;
|
|
752
768
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
753
769
|
path: z.ZodString;
|
|
754
770
|
}, z.core.$strict>, z.ZodObject<{
|
|
755
771
|
extensions: z.ZodArray<z.ZodString>;
|
|
756
772
|
}, z.core.$strict>]>;
|
|
757
773
|
}, z.core.$strict>, z.ZodObject<{
|
|
758
|
-
kind: z.
|
|
759
|
-
|
|
774
|
+
kind: z.ZodEnum<{
|
|
775
|
+
zip: "zip";
|
|
776
|
+
tar: "tar";
|
|
777
|
+
"tar.gz": "tar.gz";
|
|
778
|
+
}>;
|
|
779
|
+
members: z.ZodUnion<readonly [z.ZodObject<{
|
|
760
780
|
path: z.ZodString;
|
|
761
781
|
}, z.core.$strict>, z.ZodObject<{
|
|
762
782
|
extensions: z.ZodArray<z.ZodString>;
|
|
@@ -764,7 +784,7 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
764
784
|
}, z.core.$strict>, z.ZodObject<{
|
|
765
785
|
kind: z.ZodLiteral<"xlsx">;
|
|
766
786
|
sheet: z.ZodString;
|
|
767
|
-
}, z.core.$strict>]
|
|
787
|
+
}, z.core.$strict>]>;
|
|
768
788
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
769
789
|
format: z.ZodLiteral<"edge-list">;
|
|
770
790
|
encoding: z.ZodEnum<{
|
|
@@ -800,6 +820,12 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
800
820
|
"utf-8": "utf-8";
|
|
801
821
|
latin1: "latin1";
|
|
802
822
|
}>;
|
|
823
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
824
|
+
format: z.ZodLiteral<"solomon">;
|
|
825
|
+
encoding: z.ZodEnum<{
|
|
826
|
+
"utf-8": "utf-8";
|
|
827
|
+
latin1: "latin1";
|
|
828
|
+
}>;
|
|
803
829
|
}, z.core.$strict>, z.ZodObject<{
|
|
804
830
|
format: z.ZodLiteral<"csv-table">;
|
|
805
831
|
encoding: z.ZodEnum<{
|
|
@@ -846,7 +872,7 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
846
872
|
id: string;
|
|
847
873
|
name: string;
|
|
848
874
|
tags: string[];
|
|
849
|
-
category: "graph/v1" | "or/v1";
|
|
875
|
+
category: "graph/v1" | "or/v1" | "or-set/v1";
|
|
850
876
|
sources: {
|
|
851
877
|
url: string;
|
|
852
878
|
retrieved: string;
|
|
@@ -879,22 +905,15 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
879
905
|
} | {
|
|
880
906
|
kind: "gzip";
|
|
881
907
|
} | {
|
|
882
|
-
kind: "zip";
|
|
908
|
+
kind: "zip" | "tar" | "tar.gz";
|
|
883
909
|
member: {
|
|
884
910
|
path: string;
|
|
885
911
|
} | {
|
|
886
912
|
extensions: string[];
|
|
887
913
|
};
|
|
888
914
|
} | {
|
|
889
|
-
kind: "tar";
|
|
890
|
-
|
|
891
|
-
path: string;
|
|
892
|
-
} | {
|
|
893
|
-
extensions: string[];
|
|
894
|
-
};
|
|
895
|
-
} | {
|
|
896
|
-
kind: "tar.gz";
|
|
897
|
-
member: {
|
|
915
|
+
kind: "zip" | "tar" | "tar.gz";
|
|
916
|
+
members: {
|
|
898
917
|
path: string;
|
|
899
918
|
} | {
|
|
900
919
|
extensions: string[];
|
|
@@ -942,6 +961,9 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
942
961
|
} | {
|
|
943
962
|
format: "pajek";
|
|
944
963
|
encoding: "utf-8" | "latin1";
|
|
964
|
+
} | {
|
|
965
|
+
format: "solomon";
|
|
966
|
+
encoding: "utf-8" | "latin1";
|
|
945
967
|
};
|
|
946
968
|
paging?: {
|
|
947
969
|
kind: "arcgis-query";
|
|
@@ -991,7 +1013,7 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
991
1013
|
id: string;
|
|
992
1014
|
name: string;
|
|
993
1015
|
tags: string[];
|
|
994
|
-
category: "graph/v1" | "or/v1";
|
|
1016
|
+
category: "graph/v1" | "or/v1" | "or-set/v1";
|
|
995
1017
|
sources: {
|
|
996
1018
|
url: string;
|
|
997
1019
|
retrieved: string;
|
|
@@ -1024,22 +1046,15 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
1024
1046
|
} | {
|
|
1025
1047
|
kind: "gzip";
|
|
1026
1048
|
} | {
|
|
1027
|
-
kind: "zip";
|
|
1049
|
+
kind: "zip" | "tar" | "tar.gz";
|
|
1028
1050
|
member: {
|
|
1029
1051
|
path: string;
|
|
1030
1052
|
} | {
|
|
1031
1053
|
extensions: string[];
|
|
1032
1054
|
};
|
|
1033
1055
|
} | {
|
|
1034
|
-
kind: "tar";
|
|
1035
|
-
|
|
1036
|
-
path: string;
|
|
1037
|
-
} | {
|
|
1038
|
-
extensions: string[];
|
|
1039
|
-
};
|
|
1040
|
-
} | {
|
|
1041
|
-
kind: "tar.gz";
|
|
1042
|
-
member: {
|
|
1056
|
+
kind: "zip" | "tar" | "tar.gz";
|
|
1057
|
+
members: {
|
|
1043
1058
|
path: string;
|
|
1044
1059
|
} | {
|
|
1045
1060
|
extensions: string[];
|
|
@@ -1087,6 +1102,9 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
1087
1102
|
} | {
|
|
1088
1103
|
format: "pajek";
|
|
1089
1104
|
encoding: "utf-8" | "latin1";
|
|
1105
|
+
} | {
|
|
1106
|
+
format: "solomon";
|
|
1107
|
+
encoding: "utf-8" | "latin1";
|
|
1090
1108
|
};
|
|
1091
1109
|
paging?: {
|
|
1092
1110
|
kind: "arcgis-query";
|
|
@@ -1222,6 +1240,62 @@ declare const GraphV1Schema: z.ZodObject<{
|
|
|
1222
1240
|
};
|
|
1223
1241
|
type GraphV1 = z.infer<typeof GraphV1Schema>;
|
|
1224
1242
|
//#endregion
|
|
1243
|
+
//#region src/targets/or-set-v1.d.ts
|
|
1244
|
+
/**
|
|
1245
|
+
* The or-set/v1 normalisation target: `{ meta, instances }`, a collection of operations-research instances such as a benchmark suite, each instance an or/v1 document (`{ meta, nodes }`) under an id of its own. Beyond the shapes it checks that instance ids are unique in the set and node ids unique within each instance. A node id need not be unique across instances, because every instance numbers its own nodes.
|
|
1246
|
+
*/
|
|
1247
|
+
declare const OrSetV1Schema: z.ZodObject<{
|
|
1248
|
+
meta: z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>, unknown>;
|
|
1249
|
+
instances: z.ZodArray<z.ZodObject<{
|
|
1250
|
+
id: z.ZodString;
|
|
1251
|
+
meta: z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>, unknown>;
|
|
1252
|
+
nodes: z.ZodArray<z.ZodObject<{
|
|
1253
|
+
id: z.ZodString;
|
|
1254
|
+
lat: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1255
|
+
lon: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1256
|
+
x: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1257
|
+
y: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1258
|
+
prize: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1259
|
+
prizeProbability: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1260
|
+
duration: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1261
|
+
travelTime: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1262
|
+
skill: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1263
|
+
territory: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1264
|
+
status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1265
|
+
opened: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1266
|
+
closed: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1267
|
+
deadline: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
|
|
1268
|
+
}, z.core.$loose>>;
|
|
1269
|
+
}, z.core.$strict>>;
|
|
1270
|
+
}, z.core.$strict> & {
|
|
1271
|
+
is: (value: unknown) => value is {
|
|
1272
|
+
meta: Record<string, string | number | boolean | null>;
|
|
1273
|
+
instances: {
|
|
1274
|
+
id: string;
|
|
1275
|
+
meta: Record<string, string | number | boolean | null>;
|
|
1276
|
+
nodes: {
|
|
1277
|
+
[x: string]: unknown;
|
|
1278
|
+
id: string;
|
|
1279
|
+
lat?: number | null | undefined;
|
|
1280
|
+
lon?: number | null | undefined;
|
|
1281
|
+
x?: number | null | undefined;
|
|
1282
|
+
y?: number | null | undefined;
|
|
1283
|
+
prize?: number | null | undefined;
|
|
1284
|
+
prizeProbability?: number | null | undefined;
|
|
1285
|
+
duration?: number | null | undefined;
|
|
1286
|
+
travelTime?: number | null | undefined;
|
|
1287
|
+
skill?: string | null | undefined;
|
|
1288
|
+
territory?: string | null | undefined;
|
|
1289
|
+
status?: string | null | undefined;
|
|
1290
|
+
opened?: string | null | undefined;
|
|
1291
|
+
closed?: string | null | undefined;
|
|
1292
|
+
deadline?: string | number | null | undefined;
|
|
1293
|
+
}[];
|
|
1294
|
+
}[];
|
|
1295
|
+
};
|
|
1296
|
+
};
|
|
1297
|
+
type OrSetV1 = z.infer<typeof OrSetV1Schema>;
|
|
1298
|
+
//#endregion
|
|
1225
1299
|
//#region src/targets/or-v1.d.ts
|
|
1226
1300
|
/**
|
|
1227
1301
|
* A node of an operations-research instance: a place or case a route may visit, flat so a twindex crosswalk can map a table's columns onto it (a wildcard path must keep the same shape on both sides). The target fixes only what the datasets share and lets a crosswalk map any further column through under its own name, as graph/v1 does. `lat` and `lon` are WGS84 degrees and `x` and `y` are planar coordinates in the dataset's own system; `duration` and `travelTime` are seconds, `prize` is the dataset's own reward measure, and `prizeProbability` is the chance the reward is collected. `skill` and `territory` name what is needed to serve the node and where it sits, `status` is where it is in its lifecycle, and `opened`, `closed` and `deadline` are its lifecycle times as the source writes them, a deadline being a number where the source gives a time of day.
|
|
@@ -1296,21 +1370,26 @@ type OrV1 = z.infer<typeof OrV1Schema>;
|
|
|
1296
1370
|
declare const CategoryIdSchema: z.ZodEnum<{
|
|
1297
1371
|
"graph/v1": "graph/v1";
|
|
1298
1372
|
"or/v1": "or/v1";
|
|
1373
|
+
"or-set/v1": "or-set/v1";
|
|
1299
1374
|
}>;
|
|
1300
1375
|
type CategoryId = z.infer<typeof CategoryIdSchema>;
|
|
1301
1376
|
/** The normalisation target each category resolves to, keyed by category id. */
|
|
1302
1377
|
interface CategoryTargets {
|
|
1303
1378
|
readonly "graph/v1": GraphV1;
|
|
1304
1379
|
readonly "or/v1": OrV1;
|
|
1380
|
+
readonly "or-set/v1": OrSetV1;
|
|
1305
1381
|
}
|
|
1306
1382
|
/** The normalised shape of any category: what `fetchDataset` can resolve to. */
|
|
1307
1383
|
type CategoryTarget = CategoryTargets[CategoryId];
|
|
1308
1384
|
/** The keys of a target whose values are arrays: the collections an entry's `expectedCounts` may name. */
|
|
1309
1385
|
type CountedKey<Target> = { readonly [Key in keyof Target]: Target[Key] extends readonly unknown[] ? Key : never; }[keyof Target] & string;
|
|
1310
|
-
/**
|
|
1386
|
+
/** What one fetch of an entry in a category reads: a single document, or a set of documents, one per member of the archive the source unpacks to. */
|
|
1387
|
+
type CategoryShape = "document" | "collection";
|
|
1388
|
+
/** One category: its id, what it holds, whether an entry is one document or a collection of them, the schema of its normalisation target, and which of the target's arrays an entry may record expected counts for. */
|
|
1311
1389
|
interface CategoryOf<Id extends CategoryId> {
|
|
1312
1390
|
readonly id: Id;
|
|
1313
1391
|
readonly description: string;
|
|
1392
|
+
readonly shape: CategoryShape;
|
|
1314
1393
|
readonly target: z.ZodType<CategoryTargets[Id]>;
|
|
1315
1394
|
readonly counted: readonly CountedKey<CategoryTargets[Id]>[];
|
|
1316
1395
|
}
|
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-
|
|
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";
|
|
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-
|
|
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";
|
|
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-
|
|
1
|
+
import { G as Entry, T as Dataset, at as Snapshot, i as CategoryTarget, st as Source } from "../categories-BfqmrAvf.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: {
|
|
@@ -24,7 +24,10 @@ export interface Provenance {
|
|
|
24
24
|
readonly retrievedAt: string;
|
|
25
25
|
readonly sha256: string;
|
|
26
26
|
readonly byteLength: number;
|
|
27
|
+
/** The archive member a document was read from. */
|
|
27
28
|
readonly member?: string;
|
|
29
|
+
/** The archive members a collection was read from, in the order they were read. */
|
|
30
|
+
readonly members?: readonly string[];
|
|
28
31
|
}
|
|
29
32
|
/** A fetched dataset: the resolved category target, the provenance of the bytes it came from, and any warnings, such as a count that differs from the one the entry records. */
|
|
30
33
|
export interface FetchedDataset {
|
|
@@ -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-
|
|
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";
|
|
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 = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?/y;
|
|
107
|
+
const NUMBER$1 = /[+-]?(?:\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, text, position) : void 0;
|
|
158
|
+
const number = key === void 0 ? matchAt(NUMBER$1, 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({
|
|
@@ -832,6 +832,89 @@ function parsePajek(text) {
|
|
|
832
832
|
};
|
|
833
833
|
}
|
|
834
834
|
//#endregion
|
|
835
|
+
//#region src/parsers/solomon.ts
|
|
836
|
+
const FORMAT = "solomon";
|
|
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+)?$/;
|
|
839
|
+
/**
|
|
840
|
+
* Reads a token as a finite number.
|
|
841
|
+
* @param token - The token.
|
|
842
|
+
* @param line - The 1-based line, for the error.
|
|
843
|
+
* @param what - What the token should be, for the error.
|
|
844
|
+
* @returns The number.
|
|
845
|
+
* @throws NativeParseError when the token is not a decimal number.
|
|
846
|
+
*/
|
|
847
|
+
function numberOf(token, line, what) {
|
|
848
|
+
if (!NUMBER.test(token)) throw new NativeParseError(FORMAT, line, `expected ${what}, found ${JSON.stringify(token)}`);
|
|
849
|
+
return Number(token);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Parses a Solomon style vehicle routing instance (R. M. Solomon, 1987): a name on the first line, a `VEHICLE` section holding the number of vehicles and their capacity, and a `CUSTOMER` section of one row per node, the depot first, with its id, position, demand, ready time, due date and service time. Columns are separated by any run of spaces or tabs, so the original files and the tab separated Tenerife set read alike, and blank or whitespace only lines are skipped. The header lines (`NUMBER CAPACITY` and the column titles) are not read: a row is a line of exactly seven numbers after the `CUSTOMER` keyword, and anything else after it is an error. Node ids are kept as the file writes them, so a file whose ids are not consecutive keeps its own. The time quantities are in the unit the source uses, which the format does not record.
|
|
853
|
+
* @param text - The file text.
|
|
854
|
+
* @returns The instance: its name, vehicle count and capacity as `meta`, and its nodes in file order.
|
|
855
|
+
* @throws NativeParseError naming the line for a missing name, `VEHICLE` or `CUSTOMER` section, a vehicle line that is not two numbers, a customer row that is not seven numbers, a repeated node id, or a file with no customer.
|
|
856
|
+
*/
|
|
857
|
+
function parseSolomon(text) {
|
|
858
|
+
const all = lines(text);
|
|
859
|
+
const present = (index) => (all[index] ?? "").trim();
|
|
860
|
+
const name = present(0);
|
|
861
|
+
if (name === "") throw new NativeParseError(FORMAT, 1, "expected the instance name on the first line");
|
|
862
|
+
const meta = { name };
|
|
863
|
+
const nodes = [];
|
|
864
|
+
const seen = /* @__PURE__ */ new Set();
|
|
865
|
+
let section = "preamble";
|
|
866
|
+
let awaitingHeader = false;
|
|
867
|
+
for (const [index, raw] of all.entries()) {
|
|
868
|
+
const line = index + 1;
|
|
869
|
+
const trimmed = raw.trim();
|
|
870
|
+
if (index === 0 || trimmed === "") continue;
|
|
871
|
+
const keyword = trimmed.toUpperCase();
|
|
872
|
+
if (keyword === "VEHICLE" || keyword === "CUSTOMER") {
|
|
873
|
+
section = keyword === "VEHICLE" ? "vehicle" : "customer";
|
|
874
|
+
awaitingHeader = true;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (section === "preamble") throw new NativeParseError(FORMAT, line, `expected VEHICLE or CUSTOMER, found ${JSON.stringify(trimmed)}`);
|
|
878
|
+
if (awaitingHeader) {
|
|
879
|
+
awaitingHeader = false;
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
const tokens = trimmed.split(/\s+/);
|
|
883
|
+
if (section === "vehicle") {
|
|
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");
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
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`);
|
|
894
|
+
seen.add(id);
|
|
895
|
+
nodes.push({
|
|
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")
|
|
903
|
+
});
|
|
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");
|
|
908
|
+
return {
|
|
909
|
+
meta: {
|
|
910
|
+
name: meta.name,
|
|
911
|
+
vehicles: meta.vehicles,
|
|
912
|
+
capacity: meta.capacity
|
|
913
|
+
},
|
|
914
|
+
nodes
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
//#endregion
|
|
835
918
|
//#region src/parsers/parse-native.ts
|
|
836
919
|
/** 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. */
|
|
837
920
|
const LATIN1_CHUNK = 32768;
|
|
@@ -887,6 +970,7 @@ function parseNative(bytes, parser) {
|
|
|
887
970
|
if (parser.format === "edge-list") return parseEdgeList(text, parser);
|
|
888
971
|
if (parser.format === "matrix-market") return parseMatrixMarket(text);
|
|
889
972
|
if (parser.format === "gml") return parseGml(text);
|
|
973
|
+
if (parser.format === "solomon") return parseSolomon(text);
|
|
890
974
|
return parsePajek(text);
|
|
891
975
|
}
|
|
892
976
|
/** The ustar header layout (POSIX.1-1988, pax(1) "ustar Interchange Format"): each field's byte offset and length. */
|
|
@@ -1285,6 +1369,18 @@ function selects(path, member) {
|
|
|
1285
1369
|
return member.extensions.some((extension) => path.toLowerCase().endsWith(extension.toLowerCase()));
|
|
1286
1370
|
}
|
|
1287
1371
|
/**
|
|
1372
|
+
* Picks every member a selector matches, in path order, so a collection reads in the same order wherever it is unpacked.
|
|
1373
|
+
* @param paths - Every member path.
|
|
1374
|
+
* @param member - The recipe's selector.
|
|
1375
|
+
* @returns The matching paths, sorted, at least one.
|
|
1376
|
+
* @throws ArchiveError when no member matches, listing the members.
|
|
1377
|
+
*/
|
|
1378
|
+
function pickAll(paths, member) {
|
|
1379
|
+
const matched = paths.filter((path) => selects(path, member)).sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
1380
|
+
if (matched.length === 0) throw new ArchiveError(`expected at least one ${"path" in member ? `member ${member.path}` : `member ending ${member.extensions.join(" or ")}`}, found none among: ${paths.join(", ")}`);
|
|
1381
|
+
return matched;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1288
1384
|
* Picks the one member a selector matches.
|
|
1289
1385
|
* @param paths - Every member path.
|
|
1290
1386
|
* @param member - The recipe's selector.
|
|
@@ -1338,6 +1434,42 @@ function unpack(bytes, archive) {
|
|
|
1338
1434
|
};
|
|
1339
1435
|
}
|
|
1340
1436
|
/**
|
|
1437
|
+
* Unpacks downloaded bytes into every file a collection's selector matches.
|
|
1438
|
+
* @param bytes - The downloaded bytes.
|
|
1439
|
+
* @param archive - The recipe's archive layout, which selects members with `members`.
|
|
1440
|
+
* @returns Each matching file, in path order.
|
|
1441
|
+
* @throws ArchiveError when the bytes are not a well-formed archive of the stated kind or the selector matches no member.
|
|
1442
|
+
*/
|
|
1443
|
+
function unpackMembers(bytes, archive) {
|
|
1444
|
+
if (archive.kind === "zip") {
|
|
1445
|
+
const names = [];
|
|
1446
|
+
unzip(bytes, (file) => {
|
|
1447
|
+
names.push(file.name);
|
|
1448
|
+
return false;
|
|
1449
|
+
});
|
|
1450
|
+
const paths = pickAll(names, archive.members);
|
|
1451
|
+
const chosen = new Set(paths);
|
|
1452
|
+
const files = unzip(bytes, (candidate) => chosen.has(candidate.name));
|
|
1453
|
+
return paths.map((path) => {
|
|
1454
|
+
const file = getOwn(files, path);
|
|
1455
|
+
if (file === void 0) throw new ArchiveError(`zip member ${path} could not be read`);
|
|
1456
|
+
return {
|
|
1457
|
+
path,
|
|
1458
|
+
bytes: file
|
|
1459
|
+
};
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
const members = readTar(archive.kind === "tar" ? bytes : gunzip(bytes));
|
|
1463
|
+
return pickAll(members.map((member) => member.path), archive.members).map((path) => {
|
|
1464
|
+
const found = members.find((member) => member.path === path);
|
|
1465
|
+
if (found === void 0) throw new ArchiveError(`tar member ${path} could not be read`);
|
|
1466
|
+
return {
|
|
1467
|
+
path,
|
|
1468
|
+
bytes: found.data
|
|
1469
|
+
};
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1341
1473
|
* Gunzips bytes, rethrowing zlib's failure in the package's taxonomy.
|
|
1342
1474
|
* @param bytes - Gzip data.
|
|
1343
1475
|
* @returns The decompressed bytes.
|
|
@@ -1808,28 +1940,54 @@ async function runToNative(dataset, options) {
|
|
|
1808
1940
|
if (!response.ok) throw new KeepsetError(`${fetchedUrl} answered ${String(response.status)} ${response.statusText}`);
|
|
1809
1941
|
return new Uint8Array(await response.arrayBuffer());
|
|
1810
1942
|
});
|
|
1811
|
-
const
|
|
1943
|
+
const provenanceOf = (extra) => ({
|
|
1944
|
+
entryId: entry.id,
|
|
1945
|
+
catalogue: dataset.catalogue,
|
|
1946
|
+
category: entry.category,
|
|
1947
|
+
source: {
|
|
1948
|
+
url: source.url,
|
|
1949
|
+
status: source.status,
|
|
1950
|
+
...source.snapshot === void 0 ? {} : { snapshot: source.snapshot }
|
|
1951
|
+
},
|
|
1952
|
+
fetchedUrl,
|
|
1953
|
+
retrievedAt,
|
|
1954
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
1955
|
+
byteLength: bytes.byteLength,
|
|
1956
|
+
...extra
|
|
1957
|
+
});
|
|
1958
|
+
const { archive } = recipe;
|
|
1959
|
+
if ("members" in archive) {
|
|
1960
|
+
const files = await step(entry.id, "extract", () => unpackMembers(bytes, archive));
|
|
1961
|
+
const members = await step(entry.id, "parse", () => files.map((file) => {
|
|
1962
|
+
try {
|
|
1963
|
+
return {
|
|
1964
|
+
id: file.path,
|
|
1965
|
+
meta: {},
|
|
1966
|
+
...parseNative(file.bytes, recipe.parser)
|
|
1967
|
+
};
|
|
1968
|
+
} catch (error) {
|
|
1969
|
+
throw new KeepsetError(`member ${file.path}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
1970
|
+
}
|
|
1971
|
+
}));
|
|
1972
|
+
return {
|
|
1973
|
+
native: {
|
|
1974
|
+
meta: metaOf(entry),
|
|
1975
|
+
members
|
|
1976
|
+
},
|
|
1977
|
+
provenance: provenanceOf({ members: files.map((file) => file.path) })
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
const unpacked = await step(entry.id, "extract", () => unpack(bytes, archive));
|
|
1812
1981
|
const parsed = await step(entry.id, "parse", () => parseNative(unpacked.bytes, recipe.parser));
|
|
1813
1982
|
return {
|
|
1814
1983
|
native: {
|
|
1815
|
-
|
|
1816
|
-
|
|
1984
|
+
...parsed,
|
|
1985
|
+
meta: {
|
|
1986
|
+
..."meta" in parsed ? parsed.meta : {},
|
|
1987
|
+
...metaOf(entry)
|
|
1988
|
+
}
|
|
1817
1989
|
},
|
|
1818
|
-
provenance: {
|
|
1819
|
-
entryId: entry.id,
|
|
1820
|
-
catalogue: dataset.catalogue,
|
|
1821
|
-
category: entry.category,
|
|
1822
|
-
source: {
|
|
1823
|
-
url: source.url,
|
|
1824
|
-
status: source.status,
|
|
1825
|
-
...source.snapshot === void 0 ? {} : { snapshot: source.snapshot }
|
|
1826
|
-
},
|
|
1827
|
-
fetchedUrl,
|
|
1828
|
-
retrievedAt,
|
|
1829
|
-
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
1830
|
-
byteLength: bytes.byteLength,
|
|
1831
|
-
...unpacked.member === void 0 ? {} : { member: unpacked.member }
|
|
1832
|
-
}
|
|
1990
|
+
provenance: provenanceOf(unpacked.member === void 0 ? {} : { member: unpacked.member })
|
|
1833
1991
|
};
|
|
1834
1992
|
}
|
|
1835
1993
|
/**
|
package/dist-cli/keepset.mjs
CHANGED
|
@@ -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.
|
|
7
|
+
var version = "0.24.0";
|
|
8
8
|
const usage = `Usage: keepset <command> [options]
|
|
9
9
|
|
|
10
10
|
Commands:
|
package/package.json
CHANGED
|
@@ -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"],"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":{"
|
|
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 +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.
|
|
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"}
|