keepset 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/{bundled-DaXJpk0C.js → bundled-DNLiwEcd.js} +190 -45
- package/dist/{categories-BSJj6C-y.d.ts → categories-DzjLQ_fp.d.ts} +25 -6
- package/dist/index.d.ts +23 -6
- package/dist/index.js +2 -2
- package/dist/node/fetch-dataset.d.ts +1 -1
- package/dist/node/fetch-dataset.js +1 -1
- package/dist-cli/keepset.mjs +29 -5
- package/package.json +1 -1
- package/schemas/entry.schema.json +1 -1
- package/schemas/manifest.schema.json +1 -1
package/README.md
CHANGED
|
@@ -29,13 +29,14 @@ provenance.sha256; // the hash of exactly those bytes
|
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
npx keepset search citatio --kind graph
|
|
32
|
+
npx keepset namespaces
|
|
32
33
|
npx keepset show cora
|
|
33
34
|
npx keepset fetch cora --out cora.json
|
|
34
35
|
npx keepset validate cora
|
|
35
36
|
npx keepset roundtrip cora
|
|
36
37
|
```
|
|
37
38
|
|
|
38
|
-
`fetch` writes the normalised result with its provenance beside it; `validate` checks the entry against the entry schema and its crosswalk against twindex's validation without the network; `roundtrip` fetches the dataset and checks that decoding the encoded native document gives it back. Exit status is 0 for an answer, 1 for a negative one (an invalid entry, a round trip that does not hold) and 2 for no answer (bad usage, an unknown or ambiguous name, a recipe that failed).
|
|
39
|
+
`namespaces [path]` lists the namespaces directly beneath a path (the outermost ones without one) with how many datasets each holds, and the site has a page for each, linked from every entry's namespace. `fetch` writes the normalised result with its provenance beside it; `validate` checks the entry against the entry schema and its crosswalk against twindex's validation without the network; `roundtrip` fetches the dataset and checks that decoding the encoded native document gives it back. Exit status is 0 for an answer, 1 for a negative one (an invalid entry, a round trip that does not hold) and 2 for no answer (bad usage, an unknown or ambiguous name or namespace, a recipe that failed).
|
|
39
40
|
|
|
40
41
|
## Names, aliases and namespaces
|
|
41
42
|
|
|
@@ -48,7 +49,11 @@ npx keepset fetch gset/g53
|
|
|
48
49
|
npx keepset fetch g53
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
A qualified name joins the namespace to a name with `/`, and any leading part of the namespace may be dropped while the name still means one dataset. An entry's own id always wins; otherwise a name that more than one entry answers to is refused with the ids it could mean (`DatasetAmbiguousError` in the library, exit status 2 in the CLI), and the way out is a longer qualified name or the id.
|
|
52
|
+
A qualified name joins the namespace to a name with `/`, and any leading part of the namespace may be dropped while the name still means one dataset (a middle part may not be). An entry's own id always wins; otherwise a name that more than one entry answers to is refused with the ids it could mean (`DatasetAmbiguousError` in the library, exit status 2 in the CLI), and the way out is a longer qualified name or the id. Everything a command writes or prints uses the entry's own id, never the name typed.
|
|
53
|
+
|
|
54
|
+
An alias that is a path, such as `foo/graphs/bar`, files the entry in a second group as well: it is a name in its own right, not prefixed by the entry's namespace, and answers to `bar`, `graphs/bar` and `foo/graphs/bar`. The namespace is where an entry comes from and path aliases are cross-listing, so an entry can sit in several groups. The catalogue manifest can also give a namespace segment aliases (`namespaceAliases`, such as `ss` for `suitesparse`), and a name then answers with the alias in the segment's place.
|
|
55
|
+
|
|
56
|
+
`search --namespace suitesparse` (or `listDatasets({ namespace })`) lists a group and every group beneath it, an entry once however many of those it is in. `listNamespaces(path)` lists the namespaces directly beneath a path, or the outermost ones, each with the number of entries at or beneath it.
|
|
52
57
|
|
|
53
58
|
## The entry format
|
|
54
59
|
|
|
@@ -191,10 +191,87 @@ function countTarget(id, target) {
|
|
|
191
191
|
return new Map(category.counted.map((key) => [key, target[key].length]));
|
|
192
192
|
}
|
|
193
193
|
//#endregion
|
|
194
|
+
//#region src/own.ts
|
|
195
|
+
/**
|
|
196
|
+
* Reads an own property of a record keyed by strings that come from data. A plain `record[key]` read follows the prototype chain, so a key such as `constructor` would find an inherited function instead of reporting absence.
|
|
197
|
+
* @param record - The record to read.
|
|
198
|
+
* @param key - Any string.
|
|
199
|
+
* @returns The own property's value, or `undefined` when the record has no such own property.
|
|
200
|
+
*/
|
|
201
|
+
function getOwn(record, key) {
|
|
202
|
+
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/names.ts
|
|
206
|
+
/** The character that joins a namespace's segments to each other and to a name. It is not in an id's alphabet, so a name containing it is always a path and never a literal id. */
|
|
207
|
+
const NAME_SEPARATOR = "/";
|
|
208
|
+
/**
|
|
209
|
+
* Splits a path of ids on the separator.
|
|
210
|
+
* @param path - A path such as `a/b`.
|
|
211
|
+
* @returns Its segments.
|
|
212
|
+
*/
|
|
213
|
+
function segmentsOf(path) {
|
|
214
|
+
return path.split("/");
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The addresses of an entry, each as its path of segments with the name last: the id and every bare alias sit in the entry's own namespace, and an alias that is a path is an address of its own, in the group its leading segments name.
|
|
218
|
+
* @param entry - The entry.
|
|
219
|
+
* @returns One path per id or alias, the id first.
|
|
220
|
+
*/
|
|
221
|
+
function addressesOf(entry) {
|
|
222
|
+
const { namespace = [], aliases = [] } = entry;
|
|
223
|
+
return [entry.id, ...aliases].map((name) => name.includes("/") ? segmentsOf(name) : [...namespace, name]);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* The groups an entry belongs to: the namespace its id and bare aliases sit in, and the group each alias that is a path names, each once, in order of first appearance. A group is a path of segments; an entry belongs to every group above it as well, which the caller reads off the path.
|
|
227
|
+
* @param entry - The entry.
|
|
228
|
+
* @returns The groups, empty for an entry with no namespace and no path aliases.
|
|
229
|
+
*/
|
|
230
|
+
function groupsOf(entry) {
|
|
231
|
+
const seen = /* @__PURE__ */ new Set();
|
|
232
|
+
return addressesOf(entry).map((address) => address.slice(0, -1)).filter((group) => {
|
|
233
|
+
const key = group.join("/");
|
|
234
|
+
if (group.length === 0 || seen.has(key)) return false;
|
|
235
|
+
seen.add(key);
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Every spelling of a path of segments when each segment may be replaced by one of its aliases.
|
|
241
|
+
* @param segments - The segments.
|
|
242
|
+
* @param aliases - The segment aliases.
|
|
243
|
+
* @returns Each combination, the unaliased path first.
|
|
244
|
+
*/
|
|
245
|
+
function spellings(segments, aliases) {
|
|
246
|
+
return segments.reduce((paths, segment) => paths.flatMap((path) => [segment, ...getOwn(aliases, segment) ?? []].map((spelling) => [...path, spelling])), [[]]);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Works out the names an entry answers to. Each address is a path of segments with the name last, and an address answers to every trailing run of its path, so id `c` in namespace `a/b` answers to `c`, `b/c` and `a/b/c`, and the alias `x/y/d` answers to `d`, `y/d` and `x/y/d`. Where the manifest gives a segment aliases, an address also answers with the alias in the segment's place.
|
|
250
|
+
* @param entry - The entry: only its id, namespace and aliases are read.
|
|
251
|
+
* @param segmentAliases - The manifest's aliases for namespace segments, if any.
|
|
252
|
+
* @returns The fully qualified names and every name that resolves to the entry.
|
|
253
|
+
*/
|
|
254
|
+
function namesOf(entry, segmentAliases = {}) {
|
|
255
|
+
const addresses = addressesOf(entry);
|
|
256
|
+
const names = addresses.flatMap((address) => {
|
|
257
|
+
const name = address.slice(-1);
|
|
258
|
+
return spellings(address.slice(0, -1), segmentAliases).flatMap((group) => {
|
|
259
|
+
const path = [...group, ...name];
|
|
260
|
+
return Array.from({ length: path.length }, (_unused, depth) => path.slice(path.length - 1 - depth).join("/"));
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
return {
|
|
264
|
+
full: addresses.map((address) => address.join("/")),
|
|
265
|
+
all: [...new Set(names)]
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
194
269
|
//#region src/entry.ts
|
|
195
270
|
/** A stable identifier: an entry id, a catalogue id. It starts with a letter or digit, so no id can be `__proto__` or any other name that begins with an underscore. Entry ids stay the same across forks, which is what lets two catalogues be compared entry by entry. */
|
|
196
271
|
const IdSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, { message: "must start with a letter or digit and contain only letters, digits, '.', '_' and '-'" });
|
|
197
272
|
/** A key of an entry's `properties` or `expectedCounts` record: an identifier that starts with a letter, so it can never be `__proto__` or another name Object.prototype reacts to on assignment. */
|
|
273
|
+
/** An alias: an id, or a path of ids joined by `/` such as `foo/graphs/bar`. A bare id is a name inside the entry's own namespace; a path is a name of its own, in the group its leading segments name. The segment pattern repeats the one in `IdSchema` so that each part of a path is an id. */
|
|
274
|
+
const AliasSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?:\/[A-Za-z0-9][A-Za-z0-9._-]*)*$/, { message: "must be an id, or ids joined by '/', each starting with a letter or digit and containing only letters, digits, '.', '_' and '-'" });
|
|
198
275
|
/** The path a dataset sits under in a collection, outermost segment first, such as `["suitesparse", "gset"]`. Each segment is an id, so a segment can never contain the `/` that joins a namespace to a name. */
|
|
199
276
|
const NamespaceSchema = z.array(IdSchema).min(1);
|
|
200
277
|
const RecordKeySchema = z.string().regex(/^[A-Za-z][A-Za-z0-9]*$/, { message: "must be a letter followed by letters or digits" });
|
|
@@ -334,7 +411,7 @@ const RESERVED_META_KEYS = ["id", "name"];
|
|
|
334
411
|
const EntrySchema = define(z.object({
|
|
335
412
|
id: IdSchema,
|
|
336
413
|
namespace: NamespaceSchema.optional(),
|
|
337
|
-
aliases: z.array(
|
|
414
|
+
aliases: z.array(AliasSchema).min(1).optional(),
|
|
338
415
|
name: z.string().min(1),
|
|
339
416
|
description: z.string().min(1).optional(),
|
|
340
417
|
domain: z.string().min(1).optional(),
|
|
@@ -354,6 +431,12 @@ const EntrySchema = define(z.object({
|
|
|
354
431
|
path: ["aliases"],
|
|
355
432
|
message: "aliases must be unique and differ from the entry's own id"
|
|
356
433
|
});
|
|
434
|
+
const { full } = namesOf(entry);
|
|
435
|
+
if (new Set(full).size !== full.length) ctx.addIssue({
|
|
436
|
+
code: "custom",
|
|
437
|
+
path: ["aliases"],
|
|
438
|
+
message: "the entry's id and aliases must not give the same fully qualified name twice"
|
|
439
|
+
});
|
|
357
440
|
if (entry.recipe.status === "fetchable" && entry.recipe.source >= entry.sources.length) ctx.addIssue({
|
|
358
441
|
code: "custom",
|
|
359
442
|
path: ["recipe", "source"],
|
|
@@ -409,7 +492,7 @@ const LineageSchema = z.object({
|
|
|
409
492
|
repository: HttpUrlSchema
|
|
410
493
|
}).strict();
|
|
411
494
|
/**
|
|
412
|
-
* A catalogue manifest: the catalogue's own id and version, where it lives, and, for a fork, its lineage. A catalogue is a self-contained set of entry documents beside its manifest, and a fork is its own catalogue with its own id, so lineage may never name the catalogue itself.
|
|
495
|
+
* A catalogue manifest: the catalogue's own id and version, where it lives, the other names its namespace segments go by, and, for a fork, its lineage. A catalogue is a self-contained set of entry documents beside its manifest, and a fork is its own catalogue with its own id, so lineage may never name the catalogue itself. A segment alias is a name for a whole segment wherever it appears in a namespace, so it may not repeat, name itself, or be the segment of another alias.
|
|
413
496
|
*/
|
|
414
497
|
const ManifestSchema = define(z.object({
|
|
415
498
|
id: IdSchema,
|
|
@@ -417,13 +500,35 @@ const ManifestSchema = define(z.object({
|
|
|
417
500
|
description: z.string().min(1),
|
|
418
501
|
version: SemVerSchema,
|
|
419
502
|
repository: HttpUrlSchema,
|
|
420
|
-
lineage: LineageSchema.optional()
|
|
503
|
+
lineage: LineageSchema.optional(),
|
|
504
|
+
namespaceAliases: recordOf(IdSchema, z.array(IdSchema).min(1)).optional()
|
|
421
505
|
}).strict().superRefine((manifest, ctx) => {
|
|
422
506
|
if (manifest.lineage?.catalogue === manifest.id) ctx.addIssue({
|
|
423
507
|
code: "custom",
|
|
424
508
|
path: ["lineage", "catalogue"],
|
|
425
509
|
message: "a fork is its own catalogue: lineage must name the catalogue it was forked from, not itself"
|
|
426
510
|
});
|
|
511
|
+
const aliasOf = /* @__PURE__ */ new Map();
|
|
512
|
+
const aliases = manifest.namespaceAliases ?? {};
|
|
513
|
+
for (const [segment, names] of Object.entries(aliases)) for (const alias of names) {
|
|
514
|
+
const earlier = aliasOf.get(alias);
|
|
515
|
+
if (alias === segment) ctx.addIssue({
|
|
516
|
+
code: "custom",
|
|
517
|
+
path: ["namespaceAliases", segment],
|
|
518
|
+
message: `${alias} cannot be an alias of itself`
|
|
519
|
+
});
|
|
520
|
+
else if (earlier !== void 0) ctx.addIssue({
|
|
521
|
+
code: "custom",
|
|
522
|
+
path: ["namespaceAliases", segment],
|
|
523
|
+
message: `${alias} is already an alias of ${earlier}`
|
|
524
|
+
});
|
|
525
|
+
else if (Object.hasOwn(aliases, alias)) ctx.addIssue({
|
|
526
|
+
code: "custom",
|
|
527
|
+
path: ["namespaceAliases", segment],
|
|
528
|
+
message: `${alias} is itself a segment with aliases`
|
|
529
|
+
});
|
|
530
|
+
aliasOf.set(alias, segment);
|
|
531
|
+
}
|
|
427
532
|
}));
|
|
428
533
|
/**
|
|
429
534
|
* Parses an unknown value as a catalogue manifest.
|
|
@@ -437,37 +542,9 @@ function parseManifest(doc) {
|
|
|
437
542
|
return result.data;
|
|
438
543
|
}
|
|
439
544
|
//#endregion
|
|
440
|
-
//#region src/names.ts
|
|
441
|
-
/** The character that joins a namespace's segments to each other and to a name. It is not in an id's alphabet, so a name containing it is always a path and never a literal id. */
|
|
442
|
-
const NAME_SEPARATOR = "/";
|
|
443
|
-
/**
|
|
444
|
-
* Works out the names an entry answers to. A leaf is the entry's id or one of its aliases; a name is a leaf with any trailing run of the entry's namespace segments before it, so an entry in namespace `a/b` with id `c` answers to `c`, `b/c` and `a/b/c`.
|
|
445
|
-
* @param entry - The entry: only its id, namespace and aliases are read.
|
|
446
|
-
* @returns The fully qualified names and every name that resolves to the entry.
|
|
447
|
-
*/
|
|
448
|
-
function namesOf(entry) {
|
|
449
|
-
const { namespace = [], aliases = [] } = entry;
|
|
450
|
-
const leaves = [entry.id, ...aliases];
|
|
451
|
-
return {
|
|
452
|
-
full: leaves.map((leaf) => [...namespace, leaf].join("/")),
|
|
453
|
-
all: leaves.flatMap((leaf) => Array.from({ length: namespace.length + 1 }, (_unused, depth) => [...namespace.slice(namespace.length - depth), leaf].join("/")))
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
//#endregion
|
|
457
|
-
//#region src/own.ts
|
|
458
|
-
/**
|
|
459
|
-
* Reads an own property of a record keyed by strings that come from data. A plain `record[key]` read follows the prototype chain, so a key such as `constructor` would find an inherited function instead of reporting absence.
|
|
460
|
-
* @param record - The record to read.
|
|
461
|
-
* @param key - Any string.
|
|
462
|
-
* @returns The own property's value, or `undefined` when the record has no such own property.
|
|
463
|
-
*/
|
|
464
|
-
function getOwn(record, key) {
|
|
465
|
-
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
466
|
-
}
|
|
467
|
-
//#endregion
|
|
468
545
|
//#region src/catalogue.ts
|
|
469
546
|
/**
|
|
470
|
-
* A catalogue index: the manifest, every entry, and every crosswalk file of the catalogue, keyed by its catalogue-relative path. The build assembles it from a catalogue directory; a consumer holding its own catalogue (a fork, say) can pass one to `createCatalogue`. Entry ids are unique, every fully qualified name (the whole
|
|
547
|
+
* A catalogue index: the manifest, every entry, and every crosswalk file of the catalogue, keyed by its catalogue-relative path. The build assembles it from a catalogue directory; a consumer holding its own catalogue (a fork, say) can pass one to `createCatalogue`. Entry ids are unique, every fully qualified name (the whole path of an id or alias) identifies one entry and is not another entry's id, every namespace segment the manifest gives aliases to is some entry's segment and no alias is itself one, every crosswalk passes twindex's structural validation, every crosswalk file reference resolves, and every crosswalk's target is the category of each entry that uses it.
|
|
471
548
|
*/
|
|
472
549
|
const CatalogueIndexSchema = define(z.object({
|
|
473
550
|
manifest: ManifestSchema,
|
|
@@ -479,6 +556,27 @@ const CatalogueIndexSchema = define(z.object({
|
|
|
479
556
|
path: ["crosswalks", file],
|
|
480
557
|
message: `rule ${String(issue.ruleIndex)}: ${issue.message}`
|
|
481
558
|
});
|
|
559
|
+
const segments = new Set(index.entries.flatMap((entry) => groupsOf(entry).flat()));
|
|
560
|
+
for (const [segment, aliases] of Object.entries(index.manifest.namespaceAliases ?? {})) {
|
|
561
|
+
if (!segments.has(segment)) ctx.addIssue({
|
|
562
|
+
code: "custom",
|
|
563
|
+
path: [
|
|
564
|
+
"manifest",
|
|
565
|
+
"namespaceAliases",
|
|
566
|
+
segment
|
|
567
|
+
],
|
|
568
|
+
message: `no entry has a namespace segment ${segment}, so it has nothing to be an alias of`
|
|
569
|
+
});
|
|
570
|
+
for (const alias of aliases.filter((name) => segments.has(name))) ctx.addIssue({
|
|
571
|
+
code: "custom",
|
|
572
|
+
path: [
|
|
573
|
+
"manifest",
|
|
574
|
+
"namespaceAliases",
|
|
575
|
+
segment
|
|
576
|
+
],
|
|
577
|
+
message: `${alias} is already a namespace segment of an entry, so it cannot also be an alias`
|
|
578
|
+
});
|
|
579
|
+
}
|
|
482
580
|
const seen = /* @__PURE__ */ new Set();
|
|
483
581
|
const ids = new Set(index.entries.map((entry) => entry.id));
|
|
484
582
|
const owners = /* @__PURE__ */ new Map();
|
|
@@ -553,32 +651,57 @@ const CatalogueIndexSchema = define(z.object({
|
|
|
553
651
|
* Whether a dataset matches a query.
|
|
554
652
|
* @param dataset - The dataset.
|
|
555
653
|
* @param query - The filters.
|
|
654
|
+
* @param groups - The dataset's groups, each as a path joined by the separator.
|
|
655
|
+
* @param namespace - The query's namespace with every segment alias replaced by its segment, when it has one.
|
|
556
656
|
* @returns True when every given filter matches.
|
|
557
657
|
*/
|
|
558
|
-
function matches(dataset, query) {
|
|
658
|
+
function matches(dataset, query, groups, namespace) {
|
|
559
659
|
const { entry } = dataset;
|
|
560
660
|
if (query.kind !== void 0 && dataset.kind !== query.kind) return false;
|
|
561
661
|
if (query.category !== void 0 && entry.category !== query.category) return false;
|
|
562
662
|
if (query.domain !== void 0 && entry.domain !== query.domain) return false;
|
|
563
|
-
if (
|
|
564
|
-
const path = (entry.namespace ?? []).join("/");
|
|
565
|
-
if (path !== query.namespace && !path.startsWith(`${query.namespace}/`)) return false;
|
|
566
|
-
}
|
|
663
|
+
if (namespace !== void 0 && !groups.some((group) => group === namespace || group.startsWith(`${namespace}/`))) return false;
|
|
567
664
|
if (query.text !== void 0) {
|
|
568
665
|
const needle = query.text.toLowerCase();
|
|
569
666
|
return [
|
|
570
667
|
entry.id,
|
|
571
668
|
...entry.aliases ?? [],
|
|
572
|
-
|
|
669
|
+
...groups,
|
|
573
670
|
entry.name,
|
|
574
671
|
entry.description,
|
|
575
672
|
entry.domain,
|
|
576
673
|
...entry.tags
|
|
577
|
-
].filter((field) => field !== void 0
|
|
674
|
+
].filter((field) => field !== void 0).some((field) => field.toLowerCase().includes(needle));
|
|
578
675
|
}
|
|
579
676
|
return true;
|
|
580
677
|
}
|
|
581
678
|
/**
|
|
679
|
+
* Builds the tree of namespaces: every group of every entry and each path above it, with the ids of the entries at or beneath each.
|
|
680
|
+
* @param groupsById - Each entry's groups, each as a path joined by the separator.
|
|
681
|
+
* @returns The child namespaces of each path, the outermost ones under the empty path, each list in path order.
|
|
682
|
+
*/
|
|
683
|
+
function namespaceTree(groupsById) {
|
|
684
|
+
const members = /* @__PURE__ */ new Map();
|
|
685
|
+
for (const [id, groups] of groupsById) for (const group of groups) {
|
|
686
|
+
const segments = group.split("/");
|
|
687
|
+
for (let length = 1; length <= segments.length; length += 1) {
|
|
688
|
+
const path = segments.slice(0, length).join("/");
|
|
689
|
+
members.set(path, (members.get(path) ?? /* @__PURE__ */ new Set()).add(id));
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const children = /* @__PURE__ */ new Map();
|
|
693
|
+
for (const [path, ids] of [...members].sort(([left], [right]) => left < right ? -1 : 1)) {
|
|
694
|
+
const cut = path.lastIndexOf("/");
|
|
695
|
+
const parent = cut === -1 ? "" : path.slice(0, cut);
|
|
696
|
+
children.set(parent, [...children.get(parent) ?? [], {
|
|
697
|
+
path,
|
|
698
|
+
name: path.slice(cut + 1),
|
|
699
|
+
count: ids.size
|
|
700
|
+
}]);
|
|
701
|
+
}
|
|
702
|
+
return children;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
582
705
|
* Builds a queryable catalogue from an index. Pure and synchronous: no filesystem, no network, so it runs the same in a browser as in Node.
|
|
583
706
|
* @param index - A candidate catalogue index.
|
|
584
707
|
* @returns The catalogue.
|
|
@@ -592,7 +715,11 @@ function createCatalogue(index) {
|
|
|
592
715
|
id: manifest.id,
|
|
593
716
|
version: manifest.version
|
|
594
717
|
};
|
|
718
|
+
const segmentAliases = manifest.namespaceAliases ?? {};
|
|
719
|
+
const segmentOf = new Map(Object.entries(segmentAliases).flatMap(([segment, aliases]) => aliases.map((alias) => [alias, segment])));
|
|
720
|
+
const canonical = (path) => path.split("/").map((segment) => segmentOf.get(segment) ?? segment).join("/");
|
|
595
721
|
const datasets = /* @__PURE__ */ new Map();
|
|
722
|
+
const groupsById = /* @__PURE__ */ new Map();
|
|
596
723
|
const names = /* @__PURE__ */ new Map();
|
|
597
724
|
for (const entry of [...entries].sort((left, right) => left.id < right.id ? -1 : 1)) {
|
|
598
725
|
const crosswalk = "file" in entry.crosswalk ? getOwn(crosswalks, entry.crosswalk.file) : entry.crosswalk;
|
|
@@ -606,11 +733,16 @@ function createCatalogue(index) {
|
|
|
606
733
|
crosswalk,
|
|
607
734
|
catalogue
|
|
608
735
|
});
|
|
609
|
-
|
|
736
|
+
groupsById.set(entry.id, groupsOf(entry).map((group) => group.join("/")));
|
|
737
|
+
for (const name of namesOf(entry, segmentAliases).all) names.set(name, [...names.get(name) ?? [], entry.id]);
|
|
610
738
|
}
|
|
739
|
+
const tree = namespaceTree(groupsById);
|
|
611
740
|
return {
|
|
612
741
|
manifest,
|
|
613
|
-
listDatasets: (query = {}) =>
|
|
742
|
+
listDatasets: (query = {}) => {
|
|
743
|
+
const namespace = query.namespace === void 0 ? void 0 : canonical(query.namespace);
|
|
744
|
+
return [...datasets.values()].filter((dataset) => matches(dataset, query, groupsById.get(dataset.entry.id) ?? [], namespace));
|
|
745
|
+
},
|
|
614
746
|
getDataset: (name) => {
|
|
615
747
|
const exact = datasets.get(name);
|
|
616
748
|
if (exact !== void 0) return exact;
|
|
@@ -621,7 +753,8 @@ function createCatalogue(index) {
|
|
|
621
753
|
const dataset = datasets.get(only);
|
|
622
754
|
if (dataset === void 0) throw new DatasetNotFoundError(name);
|
|
623
755
|
return dataset;
|
|
624
|
-
}
|
|
756
|
+
},
|
|
757
|
+
listNamespaces: (path) => tree.get(path === void 0 ? "" : canonical(path)) ?? []
|
|
625
758
|
};
|
|
626
759
|
}
|
|
627
760
|
//#endregion
|
|
@@ -631,8 +764,12 @@ var catalogue_default = {
|
|
|
631
764
|
"id": "keepset",
|
|
632
765
|
"name": "keepset",
|
|
633
766
|
"description": "The keepset catalogue: datasets with machine-readable recipes that fetch each one and normalise it into its category's target shape.",
|
|
634
|
-
"version": "1.
|
|
635
|
-
"repository": "https://github.com/Mearman/keepset"
|
|
767
|
+
"version": "1.2.0",
|
|
768
|
+
"repository": "https://github.com/Mearman/keepset",
|
|
769
|
+
"namespaceAliases": {
|
|
770
|
+
"networkrepository": ["nr"],
|
|
771
|
+
"suitesparse": ["ss"]
|
|
772
|
+
}
|
|
636
773
|
},
|
|
637
774
|
crosswalks: { "crosswalks/graph-v1/topology.json": {
|
|
638
775
|
"source": "keepset/topology",
|
|
@@ -47667,9 +47804,17 @@ function listDatasets(query) {
|
|
|
47667
47804
|
function getDataset(id) {
|
|
47668
47805
|
return catalogue().getDataset(id);
|
|
47669
47806
|
}
|
|
47807
|
+
/**
|
|
47808
|
+
* Lists the namespaces directly beneath a path of the bundled catalogue, or the outermost ones, with how many entries each holds. Browser-safe.
|
|
47809
|
+
* @param path - A group path whose children to list; absent for the outermost namespaces.
|
|
47810
|
+
* @returns The child namespaces in path order.
|
|
47811
|
+
*/
|
|
47812
|
+
function listNamespaces(path) {
|
|
47813
|
+
return catalogue().listNamespaces(path);
|
|
47814
|
+
}
|
|
47670
47815
|
/** @returns The bundled catalogue's manifest: its id, version and repository. */
|
|
47671
47816
|
function getManifest() {
|
|
47672
47817
|
return catalogue().manifest;
|
|
47673
47818
|
}
|
|
47674
47819
|
//#endregion
|
|
47675
|
-
export {
|
|
47820
|
+
export { waybackUrl as A, listCategories as B, RecipeSchema as C, WaybackTimestampSchema as D, SourceSchema as E, CategoryIdSchema as F, ArchiveError as G, GraphNodeSchema as H, countTarget as I, DatasetNotFoundError as J, CatalogueSyntaxError as K, getCategory as L, groupsOf as M, namesOf as N, parseEntry as O, getOwn as P, UnknownCategoryError as Q, getTarget as R, RESERVED_META_KEYS as S, SnapshotSchema as T, GraphV1Schema as U, GraphEdgeSchema as V, JsonScalarSchema as W, NativeParseError as X, KeepsetError as Y, RecipeError as Z, HttpUrlSchema as _, CatalogueIndexSchema as a, NamespaceSchema as b, ManifestSchema as c, AliasSchema as d, ArchiveMemberSchema as f, EntrySchema as g, CrosswalkReferenceSchema as h, listNamespaces as i, NAME_SEPARATOR as j, toIssues as k, SemVerSchema as l, CitationSchema as m, getManifest as n, createCatalogue as o, ArchiveSchema as p, DatasetAmbiguousError as q, listDatasets as r, LineageSchema as s, getDataset as t, parseManifest as u, IdSchema as v, RelativePathSchema as w, ParserSchema as x, LicenceSchema as y, kindOf as z };
|
|
@@ -4,6 +4,8 @@ import { z } from "zod";
|
|
|
4
4
|
/** A stable identifier: an entry id, a catalogue id. It starts with a letter or digit, so no id can be `__proto__` or any other name that begins with an underscore. Entry ids stay the same across forks, which is what lets two catalogues be compared entry by entry. */
|
|
5
5
|
declare const IdSchema: z.ZodString;
|
|
6
6
|
/** A key of an entry's `properties` or `expectedCounts` record: an identifier that starts with a letter, so it can never be `__proto__` or another name Object.prototype reacts to on assignment. */
|
|
7
|
+
/** An alias: an id, or a path of ids joined by `/` such as `foo/graphs/bar`. A bare id is a name inside the entry's own namespace; a path is a name of its own, in the group its leading segments name. The segment pattern repeats the one in `IdSchema` so that each part of a path is an id. */
|
|
8
|
+
declare const AliasSchema: z.ZodString;
|
|
7
9
|
/** The path a dataset sits under in a collection, outermost segment first, such as `["suitesparse", "gset"]`. Each segment is an id, so a segment can never contain the `/` that joins a namespace to a name. */
|
|
8
10
|
declare const NamespaceSchema: z.ZodArray<z.ZodString>;
|
|
9
11
|
type Namespace = z.infer<typeof NamespaceSchema>;
|
|
@@ -443,7 +445,7 @@ declare const LineageSchema: z.ZodObject<{
|
|
|
443
445
|
}, z.core.$strict>;
|
|
444
446
|
type Lineage = z.infer<typeof LineageSchema>;
|
|
445
447
|
/**
|
|
446
|
-
* A catalogue manifest: the catalogue's own id and version, where it lives, and, for a fork, its lineage. A catalogue is a self-contained set of entry documents beside its manifest, and a fork is its own catalogue with its own id, so lineage may never name the catalogue itself.
|
|
448
|
+
* A catalogue manifest: the catalogue's own id and version, where it lives, the other names its namespace segments go by, and, for a fork, its lineage. A catalogue is a self-contained set of entry documents beside its manifest, and a fork is its own catalogue with its own id, so lineage may never name the catalogue itself. A segment alias is a name for a whole segment wherever it appears in a namespace, so it may not repeat, name itself, or be the segment of another alias.
|
|
447
449
|
*/
|
|
448
450
|
declare const ManifestSchema: z.ZodObject<{
|
|
449
451
|
id: z.ZodString;
|
|
@@ -456,6 +458,7 @@ declare const ManifestSchema: z.ZodObject<{
|
|
|
456
458
|
version: z.ZodString;
|
|
457
459
|
repository: z.ZodURL;
|
|
458
460
|
}, z.core.$strict>>;
|
|
461
|
+
namespaceAliases: z.ZodOptional<z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>, unknown>>;
|
|
459
462
|
}, z.core.$strict> & {
|
|
460
463
|
is: (value: unknown) => value is {
|
|
461
464
|
id: string;
|
|
@@ -468,6 +471,7 @@ declare const ManifestSchema: z.ZodObject<{
|
|
|
468
471
|
version: string;
|
|
469
472
|
repository: string;
|
|
470
473
|
} | undefined;
|
|
474
|
+
namespaceAliases?: Record<string, string[]> | undefined;
|
|
471
475
|
};
|
|
472
476
|
};
|
|
473
477
|
type Manifest = z.infer<typeof ManifestSchema>;
|
|
@@ -481,7 +485,7 @@ declare function parseManifest(doc: unknown): Manifest;
|
|
|
481
485
|
//#endregion
|
|
482
486
|
//#region src/catalogue.d.ts
|
|
483
487
|
/**
|
|
484
|
-
* A catalogue index: the manifest, every entry, and every crosswalk file of the catalogue, keyed by its catalogue-relative path. The build assembles it from a catalogue directory; a consumer holding its own catalogue (a fork, say) can pass one to `createCatalogue`. Entry ids are unique, every fully qualified name (the whole
|
|
488
|
+
* A catalogue index: the manifest, every entry, and every crosswalk file of the catalogue, keyed by its catalogue-relative path. The build assembles it from a catalogue directory; a consumer holding its own catalogue (a fork, say) can pass one to `createCatalogue`. Entry ids are unique, every fully qualified name (the whole path of an id or alias) identifies one entry and is not another entry's id, every namespace segment the manifest gives aliases to is some entry's segment and no alias is itself one, every crosswalk passes twindex's structural validation, every crosswalk file reference resolves, and every crosswalk's target is the category of each entry that uses it.
|
|
485
489
|
*/
|
|
486
490
|
declare const CatalogueIndexSchema: z.ZodObject<{
|
|
487
491
|
manifest: z.ZodObject<{
|
|
@@ -495,6 +499,7 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
495
499
|
version: z.ZodString;
|
|
496
500
|
repository: z.ZodURL;
|
|
497
501
|
}, z.core.$strict>>;
|
|
502
|
+
namespaceAliases: z.ZodOptional<z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>, unknown>>;
|
|
498
503
|
}, z.core.$strict> & {
|
|
499
504
|
is: (value: unknown) => value is {
|
|
500
505
|
id: string;
|
|
@@ -507,6 +512,7 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
507
512
|
version: string;
|
|
508
513
|
repository: string;
|
|
509
514
|
} | undefined;
|
|
515
|
+
namespaceAliases?: Record<string, string[]> | undefined;
|
|
510
516
|
};
|
|
511
517
|
};
|
|
512
518
|
crosswalks: z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodType<Crosswalk, unknown, z.core.$ZodTypeInternals<Crosswalk, unknown>> & {
|
|
@@ -731,6 +737,7 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
731
737
|
version: string;
|
|
732
738
|
repository: string;
|
|
733
739
|
} | undefined;
|
|
740
|
+
namespaceAliases?: Record<string, string[]> | undefined;
|
|
734
741
|
};
|
|
735
742
|
crosswalks: Record<string, Crosswalk>;
|
|
736
743
|
entries: {
|
|
@@ -841,12 +848,18 @@ interface DatasetQuery {
|
|
|
841
848
|
readonly category?: string;
|
|
842
849
|
/** The entry's domain, matched exactly. */
|
|
843
850
|
readonly domain?: string;
|
|
844
|
-
/** A
|
|
851
|
+
/** A group path such as `suitesparse` or `suitesparse/gset`, in which any segment may be an alias the manifest gives it: matches the entries in that group, in any group beneath it, and the entries a path alias files there. */
|
|
845
852
|
readonly namespace?: string;
|
|
846
|
-
/** Text that must appear, ignoring case, in the entry's id, an alias, its
|
|
853
|
+
/** Text that must appear, ignoring case, in the entry's id, an alias, one of its groups, name, description, domain or one of its tags. */
|
|
847
854
|
readonly text?: string;
|
|
848
855
|
}
|
|
849
|
-
/**
|
|
856
|
+
/** One namespace in the tree of groups: its path, its last segment, and how many distinct entries sit in it or in any namespace beneath it. */
|
|
857
|
+
interface NamespaceNode {
|
|
858
|
+
readonly path: string;
|
|
859
|
+
readonly name: string;
|
|
860
|
+
readonly count: number;
|
|
861
|
+
}
|
|
862
|
+
/** A queryable catalogue: the surface `listDatasets`, `getDataset` and `listNamespaces` are bound to. */
|
|
850
863
|
interface Catalogue {
|
|
851
864
|
readonly manifest: Manifest;
|
|
852
865
|
/**
|
|
@@ -863,6 +876,12 @@ interface Catalogue {
|
|
|
863
876
|
* @throws DatasetNotFoundError when no entry answers to the name.
|
|
864
877
|
*/
|
|
865
878
|
readonly getDataset: (name: string) => Dataset;
|
|
879
|
+
/**
|
|
880
|
+
* Lists the namespaces directly beneath a path, or the outermost ones, with how many entries each holds. The tree is every group of every entry and each path above it, so an entry filed in `a/b` counts toward `a/b` and `a`.
|
|
881
|
+
* @param path - A group path whose children to list, in which any segment may be an alias the manifest gives it; absent for the outermost namespaces.
|
|
882
|
+
* @returns The child namespaces in path order; empty when the path has none, whether it is unknown or a leaf.
|
|
883
|
+
*/
|
|
884
|
+
readonly listNamespaces: (path?: string) => NamespaceNode[];
|
|
866
885
|
}
|
|
867
886
|
/**
|
|
868
887
|
* Builds a queryable catalogue from an index. Pure and synchronous: no filesystem, no network, so it runs the same in a browser as in Node.
|
|
@@ -974,4 +993,4 @@ declare function listCategories(): readonly Category[];
|
|
|
974
993
|
*/
|
|
975
994
|
declare function countTarget(id: CategoryId, target: CategoryTarget): ReadonlyMap<string, number>;
|
|
976
995
|
//#endregion
|
|
977
|
-
export {
|
|
996
|
+
export { Recipe as $, ManifestSchema as A, CrosswalkReference as B, Dataset as C, Lineage as D, createCatalogue as E, ArchiveMember as F, IdSchema as G, Entry as H, ArchiveMemberSchema as I, Namespace as J, Licence as K, ArchiveSchema as L, parseManifest as M, AliasSchema as N, LineageSchema as O, Archive as P, RESERVED_META_KEYS as Q, Citation as R, CatalogueIndexSchema as S, NamespaceNode as T, EntrySchema as U, CrosswalkReferenceSchema as V, HttpUrlSchema as W, Parser as X, NamespaceSchema as Y, ParserSchema as Z, GraphV1Schema as _, CategoryTargets as a, SourceSchema as at, Catalogue as b, getCategory as c, waybackUrl as ct, listCategories as d, RecipeSchema as et, GraphEdge as f, GraphV1 as g, GraphNodeSchema as h, CategoryTarget as i, Source as it, SemVerSchema as j, Manifest as k, getTarget as l, GraphNode as m, CategoryId as n, Snapshot as nt, CountedKey as o, WaybackTimestampSchema as ot, GraphEdgeSchema as p, LicenceSchema as q, CategoryIdSchema as r, SnapshotSchema as rt, countTarget as s, parseEntry as st, Category as t, RelativePathSchema as tt, kindOf as u, JsonScalar as v, DatasetQuery as w, CatalogueIndex as x, JsonScalarSchema as y, CitationSchema as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as Recipe, A as ManifestSchema, B as CrosswalkReference, C as Dataset, D as Lineage, E as createCatalogue, F as ArchiveMember, G as IdSchema, H as Entry, I as ArchiveMemberSchema, J as Namespace, K as Licence, L as ArchiveSchema, M as parseManifest, N as AliasSchema, O as LineageSchema, P as Archive, Q as RESERVED_META_KEYS, R as Citation, S as CatalogueIndexSchema, T as NamespaceNode, U as EntrySchema, V as CrosswalkReferenceSchema, W as HttpUrlSchema, X as Parser, Y as NamespaceSchema, Z as ParserSchema, _ as GraphV1Schema, a as CategoryTargets, at as SourceSchema, b as Catalogue, c as getCategory, ct as waybackUrl, d as listCategories, et as RecipeSchema, f as GraphEdge, g as GraphV1, h as GraphNodeSchema, i as CategoryTarget, it as Source, j as SemVerSchema, k as Manifest, l as getTarget, m as GraphNode, n as CategoryId, nt as Snapshot, o as CountedKey, ot as WaybackTimestampSchema, p as GraphEdgeSchema, q as LicenceSchema, r as CategoryIdSchema, rt as SnapshotSchema, s as countTarget, st as parseEntry, t as Category, tt as RelativePathSchema, u as kindOf, v as JsonScalar, w as DatasetQuery, x as CatalogueIndex, y as JsonScalarSchema, z as CitationSchema } from "./categories-DzjLQ_fp.js";
|
|
2
2
|
import { Crosswalk } from "twindex";
|
|
3
3
|
//#region src/errors.d.ts
|
|
4
4
|
/** The root of this package's error taxonomy: every error keepset throws is a `KeepsetError`, so callers can catch the family with one check. */
|
|
@@ -64,25 +64,42 @@ export declare function listDatasets(query?: DatasetQuery): Dataset[];
|
|
|
64
64
|
* @throws DatasetNotFoundError when no entry has that id.
|
|
65
65
|
*/
|
|
66
66
|
export declare function getDataset(id: string): Dataset;
|
|
67
|
+
/**
|
|
68
|
+
* Lists the namespaces directly beneath a path of the bundled catalogue, or the outermost ones, with how many entries each holds. Browser-safe.
|
|
69
|
+
* @param path - A group path whose children to list; absent for the outermost namespaces.
|
|
70
|
+
* @returns The child namespaces in path order.
|
|
71
|
+
*/
|
|
72
|
+
export declare function listNamespaces(path?: string): NamespaceNode[];
|
|
67
73
|
/** @returns The bundled catalogue's manifest: its id, version and repository. */
|
|
68
74
|
export declare function getManifest(): Manifest;
|
|
69
75
|
//#endregion
|
|
70
76
|
//#region src/names.d.ts
|
|
71
77
|
/** The character that joins a namespace's segments to each other and to a name. It is not in an id's alphabet, so a name containing it is always a path and never a literal id. */
|
|
72
78
|
export declare const NAME_SEPARATOR = "/";
|
|
79
|
+
/** The other names namespace segments go by, as a catalogue manifest declares them: a segment mapped to its aliases. */
|
|
80
|
+
type SegmentAliases = Readonly<Record<string, readonly string[]>>;
|
|
73
81
|
/** The names an entry answers to: its fully qualified names, which must each identify it alone, and every name that resolves to it, which also includes the shorter forms. */
|
|
74
82
|
interface EntryNames {
|
|
75
|
-
/**
|
|
83
|
+
/** Each address written out in full: the whole path of an address, with no segment alias substituted. */
|
|
76
84
|
readonly full: readonly string[];
|
|
77
|
-
/** Every
|
|
85
|
+
/** Every trailing run of each address's path, the whole path and the bare name among them, with every combination of segment aliases for the namespace part. */
|
|
78
86
|
readonly all: readonly string[];
|
|
79
87
|
}
|
|
88
|
+
/** The entry fields that give it addresses and groups. */
|
|
89
|
+
type Addressed = Pick<Entry, "id" | "namespace" | "aliases">;
|
|
90
|
+
/**
|
|
91
|
+
* The groups an entry belongs to: the namespace its id and bare aliases sit in, and the group each alias that is a path names, each once, in order of first appearance. A group is a path of segments; an entry belongs to every group above it as well, which the caller reads off the path.
|
|
92
|
+
* @param entry - The entry.
|
|
93
|
+
* @returns The groups, empty for an entry with no namespace and no path aliases.
|
|
94
|
+
*/
|
|
95
|
+
export declare function groupsOf(entry: Addressed): string[][];
|
|
80
96
|
/**
|
|
81
|
-
* Works out the names an entry answers to.
|
|
97
|
+
* Works out the names an entry answers to. Each address is a path of segments with the name last, and an address answers to every trailing run of its path, so id `c` in namespace `a/b` answers to `c`, `b/c` and `a/b/c`, and the alias `x/y/d` answers to `d`, `y/d` and `x/y/d`. Where the manifest gives a segment aliases, an address also answers with the alias in the segment's place.
|
|
82
98
|
* @param entry - The entry: only its id, namespace and aliases are read.
|
|
99
|
+
* @param segmentAliases - The manifest's aliases for namespace segments, if any.
|
|
83
100
|
* @returns The fully qualified names and every name that resolves to the entry.
|
|
84
101
|
*/
|
|
85
|
-
export declare function namesOf(entry:
|
|
102
|
+
export declare function namesOf(entry: Addressed, segmentAliases?: SegmentAliases): EntryNames;
|
|
86
103
|
//#endregion
|
|
87
104
|
//#region src/serialise.d.ts
|
|
88
105
|
/**
|
|
@@ -100,4 +117,4 @@ export declare function serialise(doc: Entry | Manifest | Crosswalk): string;
|
|
|
100
117
|
*/
|
|
101
118
|
export declare function validateDataset(dataset: Dataset): SyntaxIssue[];
|
|
102
119
|
//#endregion
|
|
103
|
-
export { type Archive, type ArchiveMember, ArchiveMemberSchema, ArchiveSchema, type Catalogue, type CatalogueIndex, CatalogueIndexSchema, type Category, type CategoryId, CategoryIdSchema, type CategoryTarget, type CategoryTargets, type Citation, CitationSchema, type CountedKey, type CrosswalkReference, CrosswalkReferenceSchema, type Dataset, type DatasetQuery, type Entry, type EntryNames, EntrySchema, type GraphEdge, GraphEdgeSchema, type GraphNode, GraphNodeSchema, type GraphV1, GraphV1Schema, HttpUrlSchema, IdSchema, type JsonScalar, JsonScalarSchema, type Licence, LicenceSchema, type Lineage, LineageSchema, type Manifest, ManifestSchema, type Namespace, NamespaceSchema, type Parser, ParserSchema, RESERVED_META_KEYS, type Recipe, RecipeSchema, type RecipeStep, RelativePathSchema, SemVerSchema, type Snapshot, SnapshotSchema, type Source, SourceSchema, type SyntaxIssue, WaybackTimestampSchema, countTarget, createCatalogue, getCategory, getTarget, kindOf, listCategories, parseEntry, parseManifest, waybackUrl };
|
|
120
|
+
export { AliasSchema, type Archive, type ArchiveMember, ArchiveMemberSchema, ArchiveSchema, type Catalogue, type CatalogueIndex, CatalogueIndexSchema, type Category, type CategoryId, CategoryIdSchema, type CategoryTarget, type CategoryTargets, type Citation, CitationSchema, type CountedKey, type CrosswalkReference, CrosswalkReferenceSchema, type Dataset, type DatasetQuery, type Entry, type EntryNames, EntrySchema, type GraphEdge, GraphEdgeSchema, type GraphNode, GraphNodeSchema, type GraphV1, GraphV1Schema, HttpUrlSchema, IdSchema, type JsonScalar, JsonScalarSchema, type Licence, LicenceSchema, type Lineage, LineageSchema, type Manifest, ManifestSchema, type Namespace, type NamespaceNode, NamespaceSchema, type Parser, ParserSchema, RESERVED_META_KEYS, type Recipe, RecipeSchema, type RecipeStep, RelativePathSchema, type SegmentAliases, SemVerSchema, type Snapshot, SnapshotSchema, type Source, SourceSchema, type SyntaxIssue, WaybackTimestampSchema, countTarget, createCatalogue, getCategory, getTarget, kindOf, listCategories, parseEntry, parseManifest, waybackUrl };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as waybackUrl, B as listCategories, C as RecipeSchema, D as WaybackTimestampSchema, E as SourceSchema, F as CategoryIdSchema, G as ArchiveError, H as GraphNodeSchema, I as countTarget, J as DatasetNotFoundError, K as CatalogueSyntaxError, L as getCategory, M as groupsOf, N as namesOf, O as parseEntry, Q as UnknownCategoryError, R as getTarget, S as RESERVED_META_KEYS, T as SnapshotSchema, U as GraphV1Schema, V as GraphEdgeSchema, W as JsonScalarSchema, X as NativeParseError, Y as KeepsetError, Z as RecipeError, _ as HttpUrlSchema, a as CatalogueIndexSchema, b as NamespaceSchema, c as ManifestSchema, d as AliasSchema, f as ArchiveMemberSchema, g as EntrySchema, h as CrosswalkReferenceSchema, i as listNamespaces, j as NAME_SEPARATOR, k as toIssues, l as SemVerSchema, m as CitationSchema, n as getManifest, o as createCatalogue, p as ArchiveSchema, q as DatasetAmbiguousError, r as listDatasets, s as LineageSchema, t as getDataset, u as parseManifest, v as IdSchema, w as RelativePathSchema, x as ParserSchema, y as LicenceSchema, z as kindOf } from "./bundled-DNLiwEcd.js";
|
|
2
2
|
import { validateCrosswalk } from "twindex";
|
|
3
3
|
//#region src/serialise.ts
|
|
4
4
|
/**
|
|
@@ -57,4 +57,4 @@ function validateDataset(dataset) {
|
|
|
57
57
|
return issues;
|
|
58
58
|
}
|
|
59
59
|
//#endregion
|
|
60
|
-
export { ArchiveError, ArchiveMemberSchema, ArchiveSchema, CatalogueIndexSchema, CatalogueSyntaxError, CategoryIdSchema, CitationSchema, CrosswalkReferenceSchema, DatasetAmbiguousError, DatasetNotFoundError, EntrySchema, GraphEdgeSchema, GraphNodeSchema, GraphV1Schema, HttpUrlSchema, IdSchema, JsonScalarSchema, KeepsetError, LicenceSchema, LineageSchema, ManifestSchema, NAME_SEPARATOR, NamespaceSchema, NativeParseError, ParserSchema, RESERVED_META_KEYS, RecipeError, RecipeSchema, RelativePathSchema, SemVerSchema, SnapshotSchema, SourceSchema, UnknownCategoryError, WaybackTimestampSchema, countTarget, createCatalogue, getCategory, getDataset, getManifest, getTarget, kindOf, listCategories, listDatasets, namesOf, parseEntry, parseManifest, serialise, validateDataset, waybackUrl };
|
|
60
|
+
export { AliasSchema, ArchiveError, ArchiveMemberSchema, ArchiveSchema, CatalogueIndexSchema, CatalogueSyntaxError, CategoryIdSchema, CitationSchema, CrosswalkReferenceSchema, DatasetAmbiguousError, DatasetNotFoundError, EntrySchema, GraphEdgeSchema, GraphNodeSchema, GraphV1Schema, HttpUrlSchema, IdSchema, JsonScalarSchema, KeepsetError, LicenceSchema, LineageSchema, ManifestSchema, NAME_SEPARATOR, NamespaceSchema, NativeParseError, ParserSchema, RESERVED_META_KEYS, RecipeError, RecipeSchema, RelativePathSchema, SemVerSchema, SnapshotSchema, SourceSchema, UnknownCategoryError, WaybackTimestampSchema, countTarget, createCatalogue, getCategory, getDataset, getManifest, getTarget, groupsOf, kindOf, listCategories, listDatasets, listNamespaces, namesOf, parseEntry, parseManifest, serialise, validateDataset, waybackUrl };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as Dataset, H as Entry, i as CategoryTarget, it as Source, nt as Snapshot } from "../categories-DzjLQ_fp.js";
|
|
2
2
|
//#region src/node/fetch-dataset.d.ts
|
|
3
3
|
/** The network port a recipe downloads through: `downloadRaw` by default, which returns the bytes exactly as served, or a stand-in under test. */
|
|
4
4
|
export type FetchPort = (url: string, init: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { G as ArchiveError, I as countTarget, P as getOwn, R as getTarget, X as NativeParseError, Y as KeepsetError, Z as RecipeError, t as getDataset } from "../bundled-DNLiwEcd.js";
|
|
2
2
|
import { RoundTripMismatchError, assertRoundTrip, encode } from "twindex";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { gunzipSync } from "node:zlib";
|
package/dist-cli/keepset.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { writeFileSync } from "node:fs";
|
|
3
|
-
import { KeepsetError, NAME_SEPARATOR, countTarget, getDataset, listDatasets, validateDataset } from "keepset";
|
|
3
|
+
import { KeepsetError, NAME_SEPARATOR, countTarget, getDataset, listDatasets, listNamespaces, validateDataset } from "keepset";
|
|
4
4
|
import { fetchDataset, roundTripDataset } from "keepset/node";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
//#region package.json
|
|
7
|
-
var version = "0.
|
|
7
|
+
var version = "0.13.0";
|
|
8
8
|
const usage = `Usage: keepset <command> [options]
|
|
9
9
|
|
|
10
10
|
Commands:
|
|
@@ -12,8 +12,12 @@ Commands:
|
|
|
12
12
|
[--category <id>] [--json]
|
|
13
13
|
list the datasets whose id, alias, namespace, name,
|
|
14
14
|
description, domain or tags contain the text, filtered by
|
|
15
|
-
kind, domain, namespace (and
|
|
16
|
-
category
|
|
15
|
+
kind, domain, namespace (and every namespace beneath it,
|
|
16
|
+
segment aliases allowed) or category
|
|
17
|
+
namespaces [path] [--json]
|
|
18
|
+
list the namespaces directly beneath a path, or the
|
|
19
|
+
outermost ones, each with how many datasets it holds,
|
|
20
|
+
tab-separated
|
|
17
21
|
show <dataset> [--json]
|
|
18
22
|
print a dataset's metadata, recipe and crosswalk
|
|
19
23
|
fetch <dataset> [--out <file>]
|
|
@@ -36,7 +40,7 @@ Options:
|
|
|
36
40
|
|
|
37
41
|
Exit status: 0 for a result, a valid entry or a round trip that holds; 1 for an
|
|
38
42
|
invalid entry or a round trip that does not hold; 2 when there is no answer:
|
|
39
|
-
bad usage, an unknown or ambiguous dataset or a recipe that failed.
|
|
43
|
+
bad usage, an unknown or ambiguous dataset or namespace or a recipe that failed.
|
|
40
44
|
`;
|
|
41
45
|
function parseCommandLine(args) {
|
|
42
46
|
return parseArgs({
|
|
@@ -78,6 +82,14 @@ function formatSearch(datasets) {
|
|
|
78
82
|
return datasets.map(({ entry }) => `${entry.id}\t${entry.name}\t${entry.category}\t${entry.recipe.status}\n`).join("");
|
|
79
83
|
}
|
|
80
84
|
/**
|
|
85
|
+
* One line per namespace for `namespaces`: path and the number of datasets it holds, tab-separated so the output pipes into other tools.
|
|
86
|
+
* @param nodes - The namespaces.
|
|
87
|
+
* @returns The text.
|
|
88
|
+
*/
|
|
89
|
+
function formatNamespaces(nodes) {
|
|
90
|
+
return nodes.map((node) => `${node.path}\t${String(node.count)}\n`).join("");
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
81
93
|
* The human-readable form of `show`: the descriptor, sources, licence, citations, recipe and crosswalk rules, one labelled section after another.
|
|
82
94
|
* @param dataset - The dataset.
|
|
83
95
|
* @returns The text.
|
|
@@ -183,6 +195,17 @@ async function run(args, library, io) {
|
|
|
183
195
|
io.writeStdout(values.json === true ? json(datasets.map((dataset) => dataset.entry)) : formatSearch(datasets));
|
|
184
196
|
return 0;
|
|
185
197
|
}
|
|
198
|
+
if (command === "namespaces") {
|
|
199
|
+
if (rest.length > 1) return reject("namespaces takes at most one path");
|
|
200
|
+
const [path] = rest;
|
|
201
|
+
if (path !== void 0 && library.listDatasets({ namespace: path }).length === 0) {
|
|
202
|
+
io.writeStderr(`no namespace ${JSON.stringify(path)} in the catalogue\n`);
|
|
203
|
+
return 2;
|
|
204
|
+
}
|
|
205
|
+
const nodes = library.listNamespaces(path);
|
|
206
|
+
io.writeStdout(values.json === true ? json(nodes) : formatNamespaces(nodes));
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
186
209
|
const [name, ...extra] = rest;
|
|
187
210
|
if (command === void 0) return reject("expected a command");
|
|
188
211
|
if (![
|
|
@@ -240,6 +263,7 @@ process.stdout.on("error", (error) => {
|
|
|
240
263
|
process.exitCode = await run(process.argv.slice(2), {
|
|
241
264
|
listDatasets,
|
|
242
265
|
getDataset,
|
|
266
|
+
listNamespaces,
|
|
243
267
|
validateDataset,
|
|
244
268
|
fetchDataset: async (id) => fetchDataset(id),
|
|
245
269
|
roundTripDataset: async (id) => roundTripDataset(id),
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$defs":{"Crosswalk":{"$ref":"https://cdn.jsdelivr.net/npm/twindex@0.1.1/schemas/twindex.schema.json"},"Entry":{"additionalProperties":false,"properties":{"aliases":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"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"},"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"}]},"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.
|
|
1
|
+
{"$defs":{"Crosswalk":{"$ref":"https://cdn.jsdelivr.net/npm/twindex@0.1.1/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"],"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"}]},"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.13.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.
|
|
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.13.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}
|