keepset 0.3.0 → 0.5.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.
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env node
2
+ import { writeFileSync } from "node:fs";
3
+ import { KeepsetError, countTarget, getDataset, listDatasets, validateDataset } from "keepset";
4
+ import { fetchDataset, roundTripDataset } from "keepset/node";
5
+ import { parseArgs } from "node:util";
6
+ //#region package.json
7
+ var version = "0.5.0";
8
+ const usage = `Usage: keepset <command> [options]
9
+
10
+ Commands:
11
+ search [text] [--kind <kind>] [--domain <domain>] [--category <id>] [--json]
12
+ list the datasets whose id, name, description, domain or
13
+ tags contain the text, filtered by kind, domain or category
14
+ show <id> [--json] print a dataset's metadata, recipe and crosswalk
15
+ fetch <id> [--out <file>]
16
+ run the dataset's recipe and write the normalised result
17
+ to <file> (default <id>.json), with its provenance beside
18
+ it in <file without .json>.provenance.json
19
+ validate <id> check the entry against the entry schema and its
20
+ crosswalk against twindex's validation
21
+ roundtrip <id> fetch the dataset and check that decoding the encoded
22
+ native document gives it back
23
+
24
+ Options:
25
+ --help, -h print this help
26
+ --version, -v print the version
27
+
28
+ Exit status: 0 for a result, a valid entry or a round trip that holds; 1 for an
29
+ invalid entry or a round trip that does not hold; 2 when there is no answer:
30
+ bad usage, an unknown dataset or a recipe that failed.
31
+ `;
32
+ function parseCommandLine(args) {
33
+ return parseArgs({
34
+ args: [...args],
35
+ allowPositionals: true,
36
+ strict: true,
37
+ options: {
38
+ kind: { type: "string" },
39
+ domain: { type: "string" },
40
+ category: { type: "string" },
41
+ json: { type: "boolean" },
42
+ out: { type: "string" },
43
+ help: {
44
+ type: "boolean",
45
+ short: "h"
46
+ },
47
+ version: {
48
+ type: "boolean",
49
+ short: "v"
50
+ }
51
+ }
52
+ });
53
+ }
54
+ /**
55
+ * JSON as the command prints it: two-space indented, with a trailing newline.
56
+ * @param value - The value.
57
+ * @returns The text.
58
+ */
59
+ function json(value) {
60
+ return `${JSON.stringify(value, void 0, 2)}\n`;
61
+ }
62
+ /**
63
+ * One line per dataset for `search`: id, name, category and whether its recipe can run, tab-separated so the output pipes into other tools.
64
+ * @param datasets - The datasets.
65
+ * @returns The text.
66
+ */
67
+ function formatSearch(datasets) {
68
+ return datasets.map(({ entry }) => `${entry.id}\t${entry.name}\t${entry.category}\t${entry.recipe.status}\n`).join("");
69
+ }
70
+ /**
71
+ * The human-readable form of `show`: the descriptor, sources, licence, citations, recipe and crosswalk rules, one labelled section after another.
72
+ * @param dataset - The dataset.
73
+ * @returns The text.
74
+ */
75
+ function formatShow(dataset) {
76
+ const { entry, crosswalk } = dataset;
77
+ const lines = [`${entry.name} (${entry.id})`];
78
+ if (entry.description !== void 0) lines.push("", entry.description);
79
+ lines.push("", `category: ${entry.category} (kind ${dataset.kind})`, `catalogue: ${dataset.catalogue.id} ${dataset.catalogue.version}`);
80
+ if (entry.domain !== void 0) lines.push(`domain: ${entry.domain}`);
81
+ if (entry.tags.length > 0) lines.push(`tags: ${entry.tags.join(", ")}`);
82
+ const { licence } = entry;
83
+ lines.push(`licence: ${licence.kind === "spdx" ? licence.id : licence.kind === "custom" ? `${licence.name} <${licence.url}>` : "not recorded by the source"}`);
84
+ lines.push("", "sources:");
85
+ for (const [index, source] of entry.sources.entries()) {
86
+ const snapshot = source.snapshot === void 0 ? "" : `, snapshot ${source.snapshot.timestamp}`;
87
+ lines.push(` [${String(index)}] ${source.url} (${source.status}, retrieved ${source.retrieved}${snapshot})`);
88
+ }
89
+ if (entry.citations.length > 0) {
90
+ lines.push("", "citations:");
91
+ for (const citation of entry.citations) {
92
+ const link = citation.doi === void 0 ? citation.url === void 0 ? "" : ` <${citation.url}>` : ` doi:${citation.doi}`;
93
+ lines.push(` ${citation.text}${link}`);
94
+ }
95
+ }
96
+ const { recipe } = entry;
97
+ lines.push("", "recipe:");
98
+ if (recipe.status === "not-fetchable") lines.push(` not fetchable: ${recipe.reason}`);
99
+ else {
100
+ const { member } = "member" in recipe.archive ? recipe.archive : { member: void 0 };
101
+ const memberText = member === void 0 ? "" : "path" in member ? `, member ${member.path}` : `, member ending ${member.extensions.join(" or ")}`;
102
+ const { format, ...options } = recipe.parser;
103
+ lines.push(` source: [${String(recipe.source)}]`, ` archive: ${recipe.archive.kind}${memberText}`, ` parser: ${format} ${JSON.stringify(options)}`);
104
+ }
105
+ lines.push("", `crosswalk: ${crosswalk.source} -> ${crosswalk.target}`);
106
+ for (const rule of crosswalk.rules) lines.push(` ${rule.op} ${rule.from} -> ${rule.to}`);
107
+ for (const rule of crosswalk.oneWay) lines.push(rule.op === "drop" ? ` drop ${rule.from} (encode)` : ` default ${rule.to} = ${JSON.stringify(rule.value)} (${rule.direction})`);
108
+ const counts = Object.entries(entry.expectedCounts);
109
+ if (counts.length > 0) lines.push("", `expected counts: ${counts.map(([key, count]) => `${key} ${String(count)}`).join(", ")}`);
110
+ const properties = Object.entries(entry.properties);
111
+ if (properties.length > 0) lines.push("", "properties:", ...properties.map(([key, value]) => ` ${key}: ${JSON.stringify(value)}`));
112
+ return `${lines.join("\n")}\n`;
113
+ }
114
+ /**
115
+ * Where `fetch` writes provenance: beside the result, `.json` replaced by `.provenance.json` (or the suffix appended to a path without one).
116
+ * @param out - The result path.
117
+ * @returns The provenance path.
118
+ */
119
+ function provenancePath(out) {
120
+ return out.endsWith(".json") ? `${out.slice(0, -5)}.provenance.json` : `${out}.provenance.json`;
121
+ }
122
+ /**
123
+ * Formats a list of validation issues, one per line.
124
+ * @param issues - The issues.
125
+ * @returns The text.
126
+ */
127
+ function formatIssues(issues) {
128
+ return issues.map((issue) => `${issue.path}: ${issue.message}\n`).join("");
129
+ }
130
+ /**
131
+ * Runs the command. A KeepsetError from the library (an unknown dataset, a failing recipe) is the command's failure to answer and becomes exit status 2 with its message; anything else is a bug and propagates.
132
+ * @param args - The arguments after the command name.
133
+ * @param library - The library calls.
134
+ * @param io - Output.
135
+ * @returns The exit status.
136
+ */
137
+ async function run(args, library, io) {
138
+ let parsed;
139
+ try {
140
+ parsed = parseCommandLine(args);
141
+ } catch (error) {
142
+ io.writeStderr(`${error instanceof Error ? error.message : String(error)}\n\n${usage}`);
143
+ return 2;
144
+ }
145
+ const { values, positionals } = parsed;
146
+ if (values.version === true) {
147
+ io.writeStdout(`${io.version}\n`);
148
+ return 0;
149
+ }
150
+ if (values.help === true) {
151
+ io.writeStdout(usage);
152
+ return 0;
153
+ }
154
+ const [command, ...rest] = positionals;
155
+ const reject = (message) => {
156
+ io.writeStderr(`${message}\n\n${usage}`);
157
+ return 2;
158
+ };
159
+ try {
160
+ if (command === "search") {
161
+ if (rest.length > 1) return reject("search takes at most one text argument; quote a phrase");
162
+ const [text] = rest;
163
+ const query = {
164
+ ...text === void 0 ? {} : { text },
165
+ ...values.kind === void 0 ? {} : { kind: values.kind },
166
+ ...values.domain === void 0 ? {} : { domain: values.domain },
167
+ ...values.category === void 0 ? {} : { category: values.category }
168
+ };
169
+ const datasets = library.listDatasets(query);
170
+ io.writeStdout(values.json === true ? json(datasets.map((dataset) => dataset.entry)) : formatSearch(datasets));
171
+ return 0;
172
+ }
173
+ const [id, ...extra] = rest;
174
+ if (command === void 0) return reject("expected a command");
175
+ if (![
176
+ "show",
177
+ "fetch",
178
+ "validate",
179
+ "roundtrip"
180
+ ].includes(command)) return reject(`unknown command ${command}`);
181
+ if (id === void 0 || extra.length > 0) return reject(`${command} takes exactly one dataset id`);
182
+ if (command === "show") {
183
+ const dataset = library.getDataset(id);
184
+ io.writeStdout(values.json === true ? json(dataset) : formatShow(dataset));
185
+ return 0;
186
+ }
187
+ if (command === "validate") {
188
+ const issues = library.validateDataset(library.getDataset(id));
189
+ if (issues.length > 0) {
190
+ io.writeStdout(formatIssues(issues));
191
+ return 1;
192
+ }
193
+ io.writeStdout(`${id} is valid\n`);
194
+ return 0;
195
+ }
196
+ if (command === "fetch") {
197
+ const fetched = await library.fetchDataset(id);
198
+ const out = values.out ?? `${id}.json`;
199
+ io.writeFile(out, json(fetched.data));
200
+ io.writeFile(provenancePath(out), json(fetched.provenance));
201
+ for (const warning of fetched.warnings) io.writeStderr(`warning: ${warning}\n`);
202
+ const counts = [...library.countTarget(fetched)].map(([key, count]) => `${key} ${String(count)}`).join(", ");
203
+ io.writeStdout(`wrote ${out} (${counts}) and ${provenancePath(out)}\n`);
204
+ return 0;
205
+ }
206
+ const report = await library.roundTripDataset(id);
207
+ if (report.holds) {
208
+ io.writeStdout(`${id}: the round trip holds\n`);
209
+ return 0;
210
+ }
211
+ io.writeStdout(`${id}: the round trip does not hold at ${report.path === "" ? "(root)" : report.path}: ${report.message}\n`);
212
+ return 1;
213
+ } catch (error) {
214
+ if (error instanceof KeepsetError) {
215
+ io.writeStderr(`${error.message}\n`);
216
+ return 2;
217
+ }
218
+ throw error;
219
+ }
220
+ }
221
+ //#endregion
222
+ //#region cli/keepset.ts
223
+ process.stdout.on("error", (error) => {
224
+ if (error.code !== "EPIPE") throw error;
225
+ });
226
+ process.exitCode = await run(process.argv.slice(2), {
227
+ listDatasets,
228
+ getDataset,
229
+ validateDataset,
230
+ fetchDataset: async (id) => fetchDataset(id),
231
+ roundTripDataset: async (id) => roundTripDataset(id),
232
+ countTarget: (fetched) => countTarget(fetched.dataset.entry.category, fetched.data)
233
+ }, {
234
+ writeStdout: (text) => {
235
+ process.stdout.write(text);
236
+ },
237
+ writeStderr: (text) => {
238
+ process.stderr.write(text);
239
+ },
240
+ writeFile: (path, text) => {
241
+ writeFileSync(path, text, "utf8");
242
+ },
243
+ version
244
+ });
245
+ //#endregion
246
+ export {};
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "keepset",
3
3
  "description": "Library, CLI and statically generated catalogue for dataset fetch-and-normalise recipes",
4
- "version": "0.3.0",
4
+ "version": "0.5.0",
5
+ "bin": {
6
+ "keepset": "./dist-cli/keepset.mjs"
7
+ },
5
8
  "dependencies": {
6
9
  "fflate": "0.8.3",
7
10
  "twindex": "0.1.1",
@@ -49,6 +52,7 @@
49
52
  },
50
53
  "files": [
51
54
  "dist",
55
+ "dist-cli",
52
56
  "schemas"
53
57
  ],
54
58
  "keywords": [
@@ -79,6 +83,8 @@
79
83
  "lint:check": "eslint . --cache --max-warnings 0",
80
84
  "prepare": "husky",
81
85
  "prepublishOnly": "pnpm run lint:check && pnpm run build && pnpm run typecheck && pnpm run test && publint && attw --pack --profile esm-only",
86
+ "seed:check": "tsx scripts/seed/import-phd-catalogue.ts --pipeline scripts/seed/sources/pipeline-catalog.json --graphbox scripts/seed/sources/graphbox-catalog.json --check",
87
+ "seed:import": "tsx scripts/seed/import-phd-catalogue.ts",
82
88
  "test": "vitest run",
83
89
  "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.node.json"
84
90
  },
@@ -1 +1 @@
1
- {"$defs":{"Crosswalk":{"$ref":"https://cdn.jsdelivr.net/npm/twindex@0.1.1/schemas/twindex.schema.json"},"Entry":{"additionalProperties":false,"properties":{"category":{"enum":["graph/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"},"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":{"extension":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"}},"required":["extension"],"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":{"extension":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"}},"required":["extension"],"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":{"extension":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"}},"required":["extension"],"type":"object"}]}},"required":["kind","member"],"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"}]},"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.3.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.1/schemas/twindex.schema.json"},"Entry":{"additionalProperties":false,"properties":{"category":{"enum":["graph/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"},"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"}]},"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"}]},"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.5.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"},"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.3.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"},"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.5.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}