keepset 0.22.1 → 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.
@@ -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-CAsUNCDb.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-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.
@@ -1682,6 +1814,56 @@ async function downloadCkanDatastore(dump, pageSize, port, signal) {
1682
1814
  signal
1683
1815
  });
1684
1816
  }
1817
+ const OpenFemaCountSchema = z.object({ metadata: z.object({ count: z.int().min(0) }) });
1818
+ /**
1819
+ * Downloads a whole OpenFEMA entity, or the rows of it the source URL selects, as one CSV: the row count is read from the API's own metadata, then pages of `pageSize` rows in the order of the entity's id, each checked to hold the rows it should, and joined under the first page's header.
1820
+ * @param entity - The entity's URL with any `$select` or `$filter` but no `$top`, `$skip`, `$orderby` or `$format`, such as `https://www.fema.gov/api/open/v3/NfipClaims?$select=id,state`.
1821
+ * @param pageSize - How many rows a page holds.
1822
+ * @param port - The network port.
1823
+ * @param signal - Aborts the download.
1824
+ * @returns The bytes of one CSV, which is what the parser reads and what the provenance hashes.
1825
+ * @throws KeepsetError when the source URL sets a paging parameter, the entity does not report a count, or a page is not what it should be.
1826
+ */
1827
+ async function downloadOpenFema(entity, pageSize, port, signal) {
1828
+ const base = new URL(entity);
1829
+ for (const name of [
1830
+ "$top",
1831
+ "$skip",
1832
+ "$orderby",
1833
+ "$format",
1834
+ "$inlinecount"
1835
+ ]) if (base.searchParams.has(name)) throw new KeepsetError(`${entity} sets ${name}, which the download sets itself`);
1836
+ const withParameters = (parameters) => {
1837
+ const url = new URL(entity);
1838
+ for (const [name, value] of Object.entries(parameters)) url.searchParams.set(name, value);
1839
+ return url.href;
1840
+ };
1841
+ const countUrl = withParameters({
1842
+ $inlinecount: "allpages",
1843
+ $top: "1"
1844
+ });
1845
+ let body;
1846
+ try {
1847
+ body = JSON.parse(new TextDecoder("utf-8").decode(await withAttempts(async () => getBytes(port, countUrl, signal), signal)));
1848
+ } catch (error) {
1849
+ throw new KeepsetError(`${countUrl} did not answer JSON (${error instanceof Error ? error.message : String(error)})`);
1850
+ }
1851
+ const count = OpenFemaCountSchema.safeParse(body);
1852
+ if (!count.success) throw new KeepsetError(`${entity} did not report a row count`);
1853
+ return downloadPages({
1854
+ resource: entity,
1855
+ pageUrl: (offset) => withParameters({
1856
+ $format: "csv",
1857
+ $orderby: "id",
1858
+ $top: String(pageSize),
1859
+ $skip: String(offset)
1860
+ }),
1861
+ pageSize,
1862
+ count: count.data.metadata.count,
1863
+ port,
1864
+ signal
1865
+ });
1866
+ }
1685
1867
  /**
1686
1868
  * Downloads a paged source as the one file its pages make, in the way its recipe's paging says.
1687
1869
  * @param url - The source's URL.
@@ -1693,7 +1875,8 @@ async function downloadCkanDatastore(dump, pageSize, port, signal) {
1693
1875
  */
1694
1876
  async function downloadPaged(url, paging, port, signal) {
1695
1877
  if (paging.kind === "arcgis-query") return downloadArcgisLayer(url, paging.orderBy, port, signal);
1696
- return paging.kind === "socrata-resource" ? downloadSocrataResource(url, paging.pageSize, port, signal) : downloadCkanDatastore(url, paging.pageSize, port, signal);
1878
+ if (paging.kind === "socrata-resource") return downloadSocrataResource(url, paging.pageSize, port, signal);
1879
+ return paging.kind === "ckan-datastore" ? downloadCkanDatastore(url, paging.pageSize, port, signal) : downloadOpenFema(url, paging.pageSize, port, signal);
1697
1880
  }
1698
1881
  //#endregion
1699
1882
  //#region src/node/fetch-dataset.ts
@@ -1757,28 +1940,54 @@ async function runToNative(dataset, options) {
1757
1940
  if (!response.ok) throw new KeepsetError(`${fetchedUrl} answered ${String(response.status)} ${response.statusText}`);
1758
1941
  return new Uint8Array(await response.arrayBuffer());
1759
1942
  });
1760
- const unpacked = await step(entry.id, "extract", () => unpack(bytes, recipe.archive));
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));
1761
1981
  const parsed = await step(entry.id, "parse", () => parseNative(unpacked.bytes, recipe.parser));
1762
1982
  return {
1763
1983
  native: {
1764
- meta: metaOf(entry),
1765
- ...parsed
1984
+ ...parsed,
1985
+ meta: {
1986
+ ..."meta" in parsed ? parsed.meta : {},
1987
+ ...metaOf(entry)
1988
+ }
1766
1989
  },
1767
- provenance: {
1768
- entryId: entry.id,
1769
- catalogue: dataset.catalogue,
1770
- category: entry.category,
1771
- source: {
1772
- url: source.url,
1773
- status: source.status,
1774
- ...source.snapshot === void 0 ? {} : { snapshot: source.snapshot }
1775
- },
1776
- fetchedUrl,
1777
- retrievedAt,
1778
- sha256: createHash("sha256").update(bytes).digest("hex"),
1779
- byteLength: bytes.byteLength,
1780
- ...unpacked.member === void 0 ? {} : { member: unpacked.member }
1781
- }
1990
+ provenance: provenanceOf(unpacked.member === void 0 ? {} : { member: unpacked.member })
1782
1991
  };
1783
1992
  }
1784
1993
  /**
@@ -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.22.1";
7
+ var version = "0.24.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.22.1",
4
+ "version": "0.24.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"}]},"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.22.1/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":{"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.22.1/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.24.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}