keepset 0.23.0 → 0.25.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.
@@ -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-BKl0C7kF.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-Dkvod55t.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$3 = /[+-]?(?:\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$3, 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,228 @@ function parsePajek(text) {
832
832
  };
833
833
  }
834
834
  //#endregion
835
+ //#region src/parsers/solomon.ts
836
+ const FORMAT$2 = "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$2 = /^-?\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$1(token, line, what) {
848
+ if (!NUMBER$2.test(token)) throw new NativeParseError(FORMAT$2, 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$2, 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$2, 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$2, line, `expected the vehicle count and capacity, found ${JSON.stringify(trimmed)}`);
886
+ meta.vehicles = numberOf$1(vehicles, line, "the vehicle count");
887
+ meta.capacity = numberOf$1(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$2, line, `expected seven numbers (id, x, y, demand, ready, due, service), found ${JSON.stringify(trimmed)}`);
892
+ if (!/^\d+$/.test(id)) throw new NativeParseError(FORMAT$2, line, `expected a whole number node id, found ${JSON.stringify(id)}`);
893
+ if (seen.has(id)) throw new NativeParseError(FORMAT$2, line, `node ${id} appears twice`);
894
+ seen.add(id);
895
+ nodes.push({
896
+ id,
897
+ x: numberOf$1(x, line, "x"),
898
+ y: numberOf$1(y, line, "y"),
899
+ demand: numberOf$1(demand, line, "the demand"),
900
+ ready: numberOf$1(ready, line, "the ready time"),
901
+ due: numberOf$1(due, line, "the due date"),
902
+ service: numberOf$1(service, line, "the service time")
903
+ });
904
+ }
905
+ if (section !== "customer") throw new NativeParseError(FORMAT$2, all.length, "expected a CUSTOMER section");
906
+ if (nodes.length === 0) throw new NativeParseError(FORMAT$2, all.length, "the CUSTOMER section has no rows");
907
+ if (meta.vehicles === void 0 || meta.capacity === void 0) throw new NativeParseError(FORMAT$2, 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
918
+ //#region src/parsers/top.ts
919
+ const FORMAT$1 = "top";
920
+ /** The header lines before the first point: `n`, `m` and `tmax`. */
921
+ const HEADER_LINES = 3;
922
+ const NUMBER$1 = /^-?\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$1.test(value)) throw new NativeParseError(FORMAT$1, 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$1, 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$1, 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$1.test(x) || !NUMBER$1.test(y) || !NUMBER$1.test(score)) throw new NativeParseError(FORMAT$1, 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 = "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 = /^-?\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(token, line, what) {
994
+ if (token === void 0 || !NUMBER.test(token)) throw new NativeParseError(FORMAT, 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, line, `expected ${what}, found ${JSON.stringify(text.trim())}`);
1009
+ return tokens.map((token) => numberOf(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, "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, 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(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, 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, row.line, `expected a whole number node id, found ${JSON.stringify(id)}`);
1034
+ if (seen.has(id)) throw new NativeParseError(FORMAT, row.line, `node ${id} appears twice`);
1035
+ seen.add(id);
1036
+ return {
1037
+ id,
1038
+ x: numberOf(tokens[1], row.line, "x"),
1039
+ y: numberOf(tokens[2], row.line, "y"),
1040
+ service: numberOf(tokens[3], row.line, "the service duration"),
1041
+ profit: numberOf(tokens[4], row.line, "the profit"),
1042
+ open: numberOf(tokens[LIST_STARTS_AT + listLength], row.line, "the opening of the window"),
1043
+ close: numberOf(tokens[8 + listLength], row.line, "the closing of the window")
1044
+ };
1045
+ });
1046
+ if (nodes.length !== customers + 1) throw new NativeParseError(FORMAT, 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
835
1057
  //#region src/parsers/parse-native.ts
836
1058
  /** 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
1059
  const LATIN1_CHUNK = 32768;
@@ -887,6 +1109,9 @@ function parseNative(bytes, parser) {
887
1109
  if (parser.format === "edge-list") return parseEdgeList(text, parser);
888
1110
  if (parser.format === "matrix-market") return parseMatrixMarket(text);
889
1111
  if (parser.format === "gml") return parseGml(text);
1112
+ if (parser.format === "solomon") return parseSolomon(text);
1113
+ if (parser.format === "top") return parseTop(text);
1114
+ if (parser.format === "toptw") return parseToptw(text);
890
1115
  return parsePajek(text);
891
1116
  }
892
1117
  /** The ustar header layout (POSIX.1-1988, pax(1) "ustar Interchange Format"): each field's byte offset and length. */
@@ -1285,6 +1510,18 @@ function selects(path, member) {
1285
1510
  return member.extensions.some((extension) => path.toLowerCase().endsWith(extension.toLowerCase()));
1286
1511
  }
1287
1512
  /**
1513
+ * Picks every member a selector matches, in path order, so a collection reads in the same order wherever it is unpacked.
1514
+ * @param paths - Every member path.
1515
+ * @param member - The recipe's selector.
1516
+ * @returns The matching paths, sorted, at least one.
1517
+ * @throws ArchiveError when no member matches, listing the members.
1518
+ */
1519
+ function pickAll(paths, member) {
1520
+ const matched = paths.filter((path) => selects(path, member)).sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
1521
+ 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(", ")}`);
1522
+ return matched;
1523
+ }
1524
+ /**
1288
1525
  * Picks the one member a selector matches.
1289
1526
  * @param paths - Every member path.
1290
1527
  * @param member - The recipe's selector.
@@ -1338,6 +1575,42 @@ function unpack(bytes, archive) {
1338
1575
  };
1339
1576
  }
1340
1577
  /**
1578
+ * Unpacks downloaded bytes into every file a collection's selector matches.
1579
+ * @param bytes - The downloaded bytes.
1580
+ * @param archive - The recipe's archive layout, which selects members with `members`.
1581
+ * @returns Each matching file, in path order.
1582
+ * @throws ArchiveError when the bytes are not a well-formed archive of the stated kind or the selector matches no member.
1583
+ */
1584
+ function unpackMembers(bytes, archive) {
1585
+ if (archive.kind === "zip") {
1586
+ const names = [];
1587
+ unzip(bytes, (file) => {
1588
+ names.push(file.name);
1589
+ return false;
1590
+ });
1591
+ const paths = pickAll(names, archive.members);
1592
+ const chosen = new Set(paths);
1593
+ const files = unzip(bytes, (candidate) => chosen.has(candidate.name));
1594
+ return paths.map((path) => {
1595
+ const file = getOwn(files, path);
1596
+ if (file === void 0) throw new ArchiveError(`zip member ${path} could not be read`);
1597
+ return {
1598
+ path,
1599
+ bytes: file
1600
+ };
1601
+ });
1602
+ }
1603
+ const members = readTar(archive.kind === "tar" ? bytes : gunzip(bytes));
1604
+ return pickAll(members.map((member) => member.path), archive.members).map((path) => {
1605
+ const found = members.find((member) => member.path === path);
1606
+ if (found === void 0) throw new ArchiveError(`tar member ${path} could not be read`);
1607
+ return {
1608
+ path,
1609
+ bytes: found.data
1610
+ };
1611
+ });
1612
+ }
1613
+ /**
1341
1614
  * Gunzips bytes, rethrowing zlib's failure in the package's taxonomy.
1342
1615
  * @param bytes - Gzip data.
1343
1616
  * @returns The decompressed bytes.
@@ -1808,28 +2081,54 @@ async function runToNative(dataset, options) {
1808
2081
  if (!response.ok) throw new KeepsetError(`${fetchedUrl} answered ${String(response.status)} ${response.statusText}`);
1809
2082
  return new Uint8Array(await response.arrayBuffer());
1810
2083
  });
1811
- const unpacked = await step(entry.id, "extract", () => unpack(bytes, recipe.archive));
2084
+ const provenanceOf = (extra) => ({
2085
+ entryId: entry.id,
2086
+ catalogue: dataset.catalogue,
2087
+ category: entry.category,
2088
+ source: {
2089
+ url: source.url,
2090
+ status: source.status,
2091
+ ...source.snapshot === void 0 ? {} : { snapshot: source.snapshot }
2092
+ },
2093
+ fetchedUrl,
2094
+ retrievedAt,
2095
+ sha256: createHash("sha256").update(bytes).digest("hex"),
2096
+ byteLength: bytes.byteLength,
2097
+ ...extra
2098
+ });
2099
+ const { archive } = recipe;
2100
+ if ("members" in archive) {
2101
+ const files = await step(entry.id, "extract", () => unpackMembers(bytes, archive));
2102
+ const members = await step(entry.id, "parse", () => files.map((file) => {
2103
+ try {
2104
+ return {
2105
+ id: file.path,
2106
+ meta: {},
2107
+ ...parseNative(file.bytes, recipe.parser)
2108
+ };
2109
+ } catch (error) {
2110
+ throw new KeepsetError(`member ${file.path}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
2111
+ }
2112
+ }));
2113
+ return {
2114
+ native: {
2115
+ meta: metaOf(entry),
2116
+ members
2117
+ },
2118
+ provenance: provenanceOf({ members: files.map((file) => file.path) })
2119
+ };
2120
+ }
2121
+ const unpacked = await step(entry.id, "extract", () => unpack(bytes, archive));
1812
2122
  const parsed = await step(entry.id, "parse", () => parseNative(unpacked.bytes, recipe.parser));
1813
2123
  return {
1814
2124
  native: {
1815
- meta: metaOf(entry),
1816
- ...parsed
2125
+ ...parsed,
2126
+ meta: {
2127
+ ..."meta" in parsed ? parsed.meta : {},
2128
+ ...metaOf(entry)
2129
+ }
1817
2130
  },
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
- }
2131
+ provenance: provenanceOf(unpacked.member === void 0 ? {} : { member: unpacked.member })
1833
2132
  };
1834
2133
  }
1835
2134
  /**
@@ -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.23.0";
7
+ var version = "0.25.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.23.0",
4
+ "version": "0.25.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"],"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":{"oneOf":[{"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":{"const":"zip","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":{"const":"tar","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":{"const":"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":{"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":{"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.23.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":{"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.25.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.23.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.25.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}