keepset 0.3.0 → 0.4.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/dist/bundled-DN3sSHfu.js +46192 -0
- package/dist/{categories-BIZJUKUw.d.ts → categories-CUf1VecC.d.ts} +23 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -2
- package/dist/node/fetch-dataset.d.ts +3 -3
- package/dist/node/fetch-dataset.js +101 -11
- package/package.json +3 -1
- package/schemas/entry.schema.json +1 -1
- package/schemas/manifest.schema.json +1 -1
- package/dist/bundled--fOitQlP.js +0 -621
|
@@ -57,11 +57,11 @@ declare const CitationSchema: z.ZodObject<{
|
|
|
57
57
|
type Citation = z.infer<typeof CitationSchema>;
|
|
58
58
|
/** A relative path inside an archive or a catalogue: `/`-separated segments, none empty, `.` or `..`, so it can never climb out of the directory it is resolved against. */
|
|
59
59
|
declare const RelativePathSchema: z.ZodString;
|
|
60
|
-
/** Which file of an archive to read: a member path, matched against a member's whole path or its trailing segments (so `net.gml` finds `net/net.gml`), or the member whose name ends with
|
|
60
|
+
/** Which file of an archive to read: a member path, matched against a member's whole path or its trailing segments (so `net.gml` finds `net/net.gml`), or the member whose name ends with one of a list of extensions (a format can have several, such as Pajek's `.net` and `.paj`). Either must match exactly one member when the recipe runs; macOS resource-fork members (`__MACOSX/` and `._` files) never match. */
|
|
61
61
|
declare const ArchiveMemberSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
62
62
|
path: z.ZodString;
|
|
63
63
|
}, z.core.$strict>, z.ZodObject<{
|
|
64
|
-
|
|
64
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
65
65
|
}, z.core.$strict>]>;
|
|
66
66
|
type ArchiveMember = z.infer<typeof ArchiveMemberSchema>;
|
|
67
67
|
/** How the downloaded bytes unpack into the one file the parser reads. */
|
|
@@ -74,21 +74,21 @@ declare const ArchiveSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
74
74
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
75
75
|
path: z.ZodString;
|
|
76
76
|
}, z.core.$strict>, z.ZodObject<{
|
|
77
|
-
|
|
77
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
78
78
|
}, z.core.$strict>]>;
|
|
79
79
|
}, z.core.$strict>, z.ZodObject<{
|
|
80
80
|
kind: z.ZodLiteral<"tar">;
|
|
81
81
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
82
82
|
path: z.ZodString;
|
|
83
83
|
}, z.core.$strict>, z.ZodObject<{
|
|
84
|
-
|
|
84
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
85
85
|
}, z.core.$strict>]>;
|
|
86
86
|
}, z.core.$strict>, z.ZodObject<{
|
|
87
87
|
kind: z.ZodLiteral<"tar.gz">;
|
|
88
88
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
89
89
|
path: z.ZodString;
|
|
90
90
|
}, z.core.$strict>, z.ZodObject<{
|
|
91
|
-
|
|
91
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
92
92
|
}, z.core.$strict>]>;
|
|
93
93
|
}, z.core.$strict>], "kind">;
|
|
94
94
|
type Archive = z.infer<typeof ArchiveSchema>;
|
|
@@ -143,21 +143,21 @@ declare const RecipeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
143
143
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
144
144
|
path: z.ZodString;
|
|
145
145
|
}, z.core.$strict>, z.ZodObject<{
|
|
146
|
-
|
|
146
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
147
147
|
}, z.core.$strict>]>;
|
|
148
148
|
}, z.core.$strict>, z.ZodObject<{
|
|
149
149
|
kind: z.ZodLiteral<"tar">;
|
|
150
150
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
151
151
|
path: z.ZodString;
|
|
152
152
|
}, z.core.$strict>, z.ZodObject<{
|
|
153
|
-
|
|
153
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
154
154
|
}, z.core.$strict>]>;
|
|
155
155
|
}, z.core.$strict>, z.ZodObject<{
|
|
156
156
|
kind: z.ZodLiteral<"tar.gz">;
|
|
157
157
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
158
158
|
path: z.ZodString;
|
|
159
159
|
}, z.core.$strict>, z.ZodObject<{
|
|
160
|
-
|
|
160
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
161
161
|
}, z.core.$strict>]>;
|
|
162
162
|
}, z.core.$strict>], "kind">;
|
|
163
163
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -261,21 +261,21 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
261
261
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
262
262
|
path: z.ZodString;
|
|
263
263
|
}, z.core.$strict>, z.ZodObject<{
|
|
264
|
-
|
|
264
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
265
265
|
}, z.core.$strict>]>;
|
|
266
266
|
}, z.core.$strict>, z.ZodObject<{
|
|
267
267
|
kind: z.ZodLiteral<"tar">;
|
|
268
268
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
269
269
|
path: z.ZodString;
|
|
270
270
|
}, z.core.$strict>, z.ZodObject<{
|
|
271
|
-
|
|
271
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
272
272
|
}, z.core.$strict>]>;
|
|
273
273
|
}, z.core.$strict>, z.ZodObject<{
|
|
274
274
|
kind: z.ZodLiteral<"tar.gz">;
|
|
275
275
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
276
276
|
path: z.ZodString;
|
|
277
277
|
}, z.core.$strict>, z.ZodObject<{
|
|
278
|
-
|
|
278
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
279
279
|
}, z.core.$strict>]>;
|
|
280
280
|
}, z.core.$strict>], "kind">;
|
|
281
281
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -367,21 +367,21 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
367
367
|
member: {
|
|
368
368
|
path: string;
|
|
369
369
|
} | {
|
|
370
|
-
|
|
370
|
+
extensions: string[];
|
|
371
371
|
};
|
|
372
372
|
} | {
|
|
373
373
|
kind: "tar";
|
|
374
374
|
member: {
|
|
375
375
|
path: string;
|
|
376
376
|
} | {
|
|
377
|
-
|
|
377
|
+
extensions: string[];
|
|
378
378
|
};
|
|
379
379
|
} | {
|
|
380
380
|
kind: "tar.gz";
|
|
381
381
|
member: {
|
|
382
382
|
path: string;
|
|
383
383
|
} | {
|
|
384
|
-
|
|
384
|
+
extensions: string[];
|
|
385
385
|
};
|
|
386
386
|
};
|
|
387
387
|
parser: {
|
|
@@ -552,21 +552,21 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
552
552
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
553
553
|
path: z.ZodString;
|
|
554
554
|
}, z.core.$strict>, z.ZodObject<{
|
|
555
|
-
|
|
555
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
556
556
|
}, z.core.$strict>]>;
|
|
557
557
|
}, z.core.$strict>, z.ZodObject<{
|
|
558
558
|
kind: z.ZodLiteral<"tar">;
|
|
559
559
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
560
560
|
path: z.ZodString;
|
|
561
561
|
}, z.core.$strict>, z.ZodObject<{
|
|
562
|
-
|
|
562
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
563
563
|
}, z.core.$strict>]>;
|
|
564
564
|
}, z.core.$strict>, z.ZodObject<{
|
|
565
565
|
kind: z.ZodLiteral<"tar.gz">;
|
|
566
566
|
member: z.ZodUnion<readonly [z.ZodObject<{
|
|
567
567
|
path: z.ZodString;
|
|
568
568
|
}, z.core.$strict>, z.ZodObject<{
|
|
569
|
-
|
|
569
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
570
570
|
}, z.core.$strict>]>;
|
|
571
571
|
}, z.core.$strict>], "kind">;
|
|
572
572
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -658,21 +658,21 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
658
658
|
member: {
|
|
659
659
|
path: string;
|
|
660
660
|
} | {
|
|
661
|
-
|
|
661
|
+
extensions: string[];
|
|
662
662
|
};
|
|
663
663
|
} | {
|
|
664
664
|
kind: "tar";
|
|
665
665
|
member: {
|
|
666
666
|
path: string;
|
|
667
667
|
} | {
|
|
668
|
-
|
|
668
|
+
extensions: string[];
|
|
669
669
|
};
|
|
670
670
|
} | {
|
|
671
671
|
kind: "tar.gz";
|
|
672
672
|
member: {
|
|
673
673
|
path: string;
|
|
674
674
|
} | {
|
|
675
|
-
|
|
675
|
+
extensions: string[];
|
|
676
676
|
};
|
|
677
677
|
};
|
|
678
678
|
parser: {
|
|
@@ -762,21 +762,21 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
762
762
|
member: {
|
|
763
763
|
path: string;
|
|
764
764
|
} | {
|
|
765
|
-
|
|
765
|
+
extensions: string[];
|
|
766
766
|
};
|
|
767
767
|
} | {
|
|
768
768
|
kind: "tar";
|
|
769
769
|
member: {
|
|
770
770
|
path: string;
|
|
771
771
|
} | {
|
|
772
|
-
|
|
772
|
+
extensions: string[];
|
|
773
773
|
};
|
|
774
774
|
} | {
|
|
775
775
|
kind: "tar.gz";
|
|
776
776
|
member: {
|
|
777
777
|
path: string;
|
|
778
778
|
} | {
|
|
779
|
-
|
|
779
|
+
extensions: string[];
|
|
780
780
|
};
|
|
781
781
|
};
|
|
782
782
|
parser: {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as SnapshotSchema, A as SemVerSchema, B as Entry, C as Dataset, D as LineageSchema, E as Lineage, F as ArchiveSchema, G as LicenceSchema, H as HttpUrlSchema, I as Citation, J as RESERVED_META_KEYS, K as Parser, L as CitationSchema, M as Archive, N as ArchiveMember, O as Manifest, P as ArchiveMemberSchema, Q as Snapshot, R as CrosswalkReference, S as CatalogueIndexSchema, T as createCatalogue, U as IdSchema, V as EntrySchema, W as Licence, X as RecipeSchema, Y as Recipe, Z as RelativePathSchema, _ as GraphV1Schema, a as CategoryTargets, b as Catalogue, c as getCategory, d as listCategories, et as Source, f as GraphEdge, g as GraphV1, h as GraphNodeSchema, i as CategoryTarget, it as waybackUrl, j as parseManifest, k as ManifestSchema, l as getTarget, m as GraphNode, n as CategoryId, nt as WaybackTimestampSchema, o as CountedKey, p as GraphEdgeSchema, q as ParserSchema, r as CategoryIdSchema, rt as parseEntry, s as countTarget, t as Category, tt as SourceSchema, u as kindOf, v as JsonScalar, w as DatasetQuery, x as CatalogueIndex, y as JsonScalarSchema, z as CrosswalkReferenceSchema } from "./categories-
|
|
1
|
+
import { $ as SnapshotSchema, A as SemVerSchema, B as Entry, C as Dataset, D as LineageSchema, E as Lineage, F as ArchiveSchema, G as LicenceSchema, H as HttpUrlSchema, I as Citation, J as RESERVED_META_KEYS, K as Parser, L as CitationSchema, M as Archive, N as ArchiveMember, O as Manifest, P as ArchiveMemberSchema, Q as Snapshot, R as CrosswalkReference, S as CatalogueIndexSchema, T as createCatalogue, U as IdSchema, V as EntrySchema, W as Licence, X as RecipeSchema, Y as Recipe, Z as RelativePathSchema, _ as GraphV1Schema, a as CategoryTargets, b as Catalogue, c as getCategory, d as listCategories, et as Source, f as GraphEdge, g as GraphV1, h as GraphNodeSchema, i as CategoryTarget, it as waybackUrl, j as parseManifest, k as ManifestSchema, l as getTarget, m as GraphNode, n as CategoryId, nt as WaybackTimestampSchema, o as CountedKey, p as GraphEdgeSchema, q as ParserSchema, r as CategoryIdSchema, rt as parseEntry, s as countTarget, t as Category, tt as SourceSchema, u as kindOf, v as JsonScalar, w as DatasetQuery, x as CatalogueIndex, y as JsonScalarSchema, z as CrosswalkReferenceSchema } from "./categories-CUf1VecC.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. */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as getCategory, B as DatasetNotFoundError, C as SnapshotSchema, D as waybackUrl, E as parseEntry, F as GraphNodeSchema, H as NativeParseError, I as GraphV1Schema, L as JsonScalarSchema, M as kindOf, N as listCategories, O as CategoryIdSchema, P as GraphEdgeSchema, R as ArchiveError, S as RelativePathSchema, T as WaybackTimestampSchema, U as RecipeError, V as KeepsetError, W as UnknownCategoryError, _ as IdSchema, a as createCatalogue, b as RESERVED_META_KEYS, c as ManifestSchema, d as ArchiveMemberSchema, f as ArchiveSchema, g as HttpUrlSchema, h as EntrySchema, i as CatalogueIndexSchema, j as getTarget, k as countTarget, l as SemVerSchema, m as CrosswalkReferenceSchema, n as getManifest, p as CitationSchema, r as listDatasets, s as LineageSchema, t as getDataset, u as parseManifest, v as LicenceSchema, w as SourceSchema, x as RecipeSchema, y as ParserSchema, z as CatalogueSyntaxError } from "./bundled
|
|
1
|
+
import { A as getCategory, B as DatasetNotFoundError, C as SnapshotSchema, D as waybackUrl, E as parseEntry, F as GraphNodeSchema, H as NativeParseError, I as GraphV1Schema, L as JsonScalarSchema, M as kindOf, N as listCategories, O as CategoryIdSchema, P as GraphEdgeSchema, R as ArchiveError, S as RelativePathSchema, T as WaybackTimestampSchema, U as RecipeError, V as KeepsetError, W as UnknownCategoryError, _ as IdSchema, a as createCatalogue, b as RESERVED_META_KEYS, c as ManifestSchema, d as ArchiveMemberSchema, f as ArchiveSchema, g as HttpUrlSchema, h as EntrySchema, i as CatalogueIndexSchema, j as getTarget, k as countTarget, l as SemVerSchema, m as CrosswalkReferenceSchema, n as getManifest, p as CitationSchema, r as listDatasets, s as LineageSchema, t as getDataset, u as parseManifest, v as LicenceSchema, w as SourceSchema, x as RecipeSchema, y as ParserSchema, z as CatalogueSyntaxError } from "./bundled-DN3sSHfu.js";
|
|
2
2
|
//#region src/serialise.ts
|
|
3
3
|
/**
|
|
4
4
|
* Copies a JSON value with every object's keys in ascending code-unit order, the order the house JSON lint (`json/sort-keys`) requires of committed JSON. `Object.fromEntries` defines each key as an own data property, so a key such as `__proto__` stays a key.
|
|
@@ -11,12 +11,20 @@ function sortKeys(value) {
|
|
|
11
11
|
return value;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
+
* Serialises any JSON value in the committed form: two-space indented, keys in ascending order, with a trailing newline.
|
|
15
|
+
* @param value - A JSON value.
|
|
16
|
+
* @returns The file text.
|
|
17
|
+
*/
|
|
18
|
+
function canonicalJson(value) {
|
|
19
|
+
return `${JSON.stringify(sortKeys(value), void 0, 2)}\n`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
14
22
|
* Serialises a catalogue document (an entry, a manifest or a crosswalk file) as its file holds it: two-space indented JSON with keys in ascending order and a trailing newline. The form depends only on the document's content, so parsing a file written this way and serialising the result reproduces the file byte for byte.
|
|
15
23
|
* @param doc - A parsed entry, manifest or crosswalk.
|
|
16
24
|
* @returns The file text.
|
|
17
25
|
*/
|
|
18
26
|
function serialise(doc) {
|
|
19
|
-
return
|
|
27
|
+
return canonicalJson(doc);
|
|
20
28
|
}
|
|
21
29
|
//#endregion
|
|
22
30
|
export { ArchiveError, ArchiveMemberSchema, ArchiveSchema, CatalogueIndexSchema, CatalogueSyntaxError, CategoryIdSchema, CitationSchema, CrosswalkReferenceSchema, DatasetNotFoundError, EntrySchema, GraphEdgeSchema, GraphNodeSchema, GraphV1Schema, HttpUrlSchema, IdSchema, JsonScalarSchema, KeepsetError, LicenceSchema, LineageSchema, ManifestSchema, NativeParseError, ParserSchema, RESERVED_META_KEYS, RecipeError, RecipeSchema, RelativePathSchema, SemVerSchema, SnapshotSchema, SourceSchema, UnknownCategoryError, WaybackTimestampSchema, countTarget, createCatalogue, getCategory, getDataset, getManifest, getTarget, kindOf, listCategories, listDatasets, parseEntry, parseManifest, serialise, waybackUrl };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { B as Entry, C as Dataset, Q as Snapshot, et as Source, i as CategoryTarget } from "../categories-
|
|
1
|
+
import { B as Entry, C as Dataset, Q as Snapshot, et as Source, i as CategoryTarget } from "../categories-CUf1VecC.js";
|
|
2
2
|
//#region src/node/fetch-dataset.d.ts
|
|
3
|
-
/** The network port a recipe downloads through: the
|
|
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: {
|
|
5
5
|
readonly signal?: AbortSignal;
|
|
6
6
|
}) => Promise<Response>;
|
|
@@ -48,7 +48,7 @@ export declare function downloadUrl(source: Source): string;
|
|
|
48
48
|
*/
|
|
49
49
|
export declare function fetchEntry(dataset: Dataset, options?: FetchOptions): Promise<FetchedDataset>;
|
|
50
50
|
/**
|
|
51
|
-
* Fetches a dataset of the bundled catalogue by id and resolves it to its category's target shape, so `fetchDataset("cora")` yields graph/v1 `{ meta, nodes, edges }` directly. Node-only: it downloads with
|
|
51
|
+
* Fetches a dataset of the bundled catalogue by id and resolves it to its category's target shape, so `fetchDataset("cora")` yields graph/v1 `{ meta, nodes, edges }` directly. Node-only: it downloads with Node's http and https clients and unpacks with Node's zlib.
|
|
52
52
|
* @param id - The entry id.
|
|
53
53
|
* @param options - The network port, abort signal and clock.
|
|
54
54
|
* @returns The resolved target with its provenance and any count warnings.
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { H as NativeParseError, R as ArchiveError, U as RecipeError, V as KeepsetError, j as getTarget, k as countTarget, o as getOwn, t as getDataset } from "../bundled
|
|
1
|
+
import { H as NativeParseError, R as ArchiveError, U as RecipeError, V as KeepsetError, j as getTarget, k as countTarget, o as getOwn, t as getDataset } from "../bundled-DN3sSHfu.js";
|
|
2
2
|
import { encode } from "twindex";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { gunzipSync } from "node:zlib";
|
|
5
5
|
import { unzipSync } from "fflate";
|
|
6
|
+
import { request } from "node:http";
|
|
7
|
+
import { request as request$1 } from "node:https";
|
|
6
8
|
//#region src/parsers/native.ts
|
|
7
9
|
/**
|
|
8
10
|
* Collects node ids in order of first appearance, so a parser that learns its nodes from its edges emits them deterministically.
|
|
@@ -346,8 +348,9 @@ function parseMatrixMarket(text) {
|
|
|
346
348
|
}
|
|
347
349
|
//#endregion
|
|
348
350
|
//#region src/parsers/pajek.ts
|
|
349
|
-
/** Section keywords, lower-cased, mapped to how their lines are read. `*Partition` and `*Vector` hold node attributes this version does not carry, so their lines are skipped, as are the `*Vertices` lines inside them. */
|
|
351
|
+
/** Section keywords, lower-cased, mapped to how their lines are read. `*Network` names the network and starts no section. `*Partition` and `*Vector` hold node attributes this version does not carry, so their lines are skipped, as are the `*Vertices` lines inside them. */
|
|
350
352
|
const sections = /* @__PURE__ */ new Map([
|
|
353
|
+
["*network", "network"],
|
|
351
354
|
["*vertices", "vertices"],
|
|
352
355
|
["*arcs", "pairs"],
|
|
353
356
|
["*edges", "pairs"],
|
|
@@ -358,6 +361,18 @@ const sections = /* @__PURE__ */ new Map([
|
|
|
358
361
|
["*vector", "attributes"]
|
|
359
362
|
]);
|
|
360
363
|
/**
|
|
364
|
+
* Resolves a section keyword, which Pajek reads case-insensitively and accepts abbreviated (`*net` for `*Network`, `*vert` for `*Vertices`): an exact keyword first, otherwise the shortest keyword it abbreviates, so `*edge` is `*Edges` rather than `*Edgeslist`.
|
|
365
|
+
* @param keyword - The keyword as written.
|
|
366
|
+
* @returns How its section is read, or undefined for a keyword that abbreviates none.
|
|
367
|
+
*/
|
|
368
|
+
function sectionOf(keyword) {
|
|
369
|
+
const name = keyword.toLowerCase();
|
|
370
|
+
const exact = sections.get(name);
|
|
371
|
+
if (exact !== void 0) return exact;
|
|
372
|
+
const [shortest] = [...sections.keys()].filter((candidate) => candidate.startsWith(name)).sort((left, right) => left.length - right.length);
|
|
373
|
+
return shortest === void 0 ? void 0 : sections.get(shortest);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
361
376
|
* Reads a vertex number, which Pajek numbers from 1 to the declared vertex count.
|
|
362
377
|
* @param token - The token.
|
|
363
378
|
* @param count - The declared vertex count.
|
|
@@ -388,14 +403,13 @@ function parsePajek(text) {
|
|
|
388
403
|
const tokens = content.split(/\s+/);
|
|
389
404
|
const [keyword] = tokens;
|
|
390
405
|
if (keyword?.startsWith("*") === true) {
|
|
391
|
-
const
|
|
392
|
-
if (
|
|
406
|
+
const next = sectionOf(keyword);
|
|
407
|
+
if (next === void 0) throw new NativeParseError("pajek", lineNumber, `unknown section ${keyword}`);
|
|
408
|
+
if (next === "network") {
|
|
393
409
|
networks++;
|
|
394
410
|
if (networks > 1) throw new NativeParseError("pajek", lineNumber, "a project file holding more than one network is not supported");
|
|
395
411
|
continue;
|
|
396
412
|
}
|
|
397
|
-
const next = sections.get(name);
|
|
398
|
-
if (next === void 0) throw new NativeParseError("pajek", lineNumber, `unknown section ${keyword}`);
|
|
399
413
|
if (next === "vertices" && section !== "attributes") {
|
|
400
414
|
if (count !== void 0) throw new NativeParseError("pajek", lineNumber, "a network declares *Vertices twice");
|
|
401
415
|
const declared = tokens[1];
|
|
@@ -616,7 +630,7 @@ function isResourceFork(path) {
|
|
|
616
630
|
return path.startsWith("__MACOSX/") || path.split("/").some((segment) => segment.startsWith("._"));
|
|
617
631
|
}
|
|
618
632
|
/**
|
|
619
|
-
* Whether a member path matches a selector: a path selector matches the whole member path or its trailing segments, an
|
|
633
|
+
* Whether a member path matches a selector: a path selector matches the whole member path or its trailing segments, an extensions selector the end of the member's name, ignoring case.
|
|
620
634
|
* @param path - The member path.
|
|
621
635
|
* @param member - The recipe's selector.
|
|
622
636
|
* @returns True on a match.
|
|
@@ -624,7 +638,7 @@ function isResourceFork(path) {
|
|
|
624
638
|
function selects(path, member) {
|
|
625
639
|
if (isResourceFork(path) || path.endsWith("/")) return false;
|
|
626
640
|
if ("path" in member) return path === member.path || path.endsWith(`/${member.path}`);
|
|
627
|
-
return path.toLowerCase().endsWith(
|
|
641
|
+
return member.extensions.some((extension) => path.toLowerCase().endsWith(extension.toLowerCase()));
|
|
628
642
|
}
|
|
629
643
|
/**
|
|
630
644
|
* Picks the one member a selector matches.
|
|
@@ -637,7 +651,7 @@ function pick(paths, member) {
|
|
|
637
651
|
const matched = paths.filter((path) => selects(path, member));
|
|
638
652
|
const [only] = matched;
|
|
639
653
|
if (matched.length !== 1 || only === void 0) {
|
|
640
|
-
const wanted = "path" in member ? `member ${member.path}` : `one member ending ${member.
|
|
654
|
+
const wanted = "path" in member ? `member ${member.path}` : `one member ending ${member.extensions.join(" or ")}`;
|
|
641
655
|
throw new ArchiveError(`expected ${wanted}, found ${String(matched.length)} among: ${paths.join(", ")}`);
|
|
642
656
|
}
|
|
643
657
|
return only;
|
|
@@ -701,6 +715,82 @@ function unzip(bytes, filter) {
|
|
|
701
715
|
}
|
|
702
716
|
}
|
|
703
717
|
//#endregion
|
|
718
|
+
//#region src/node/download.ts
|
|
719
|
+
/** The most redirects one download follows: the Fetch Standard's own limit (https://fetch.spec.whatwg.org/#http-redirect-fetch, step 5). */
|
|
720
|
+
const MAX_REDIRECTS = 20;
|
|
721
|
+
const REDIRECTS = new Set(Object.values({
|
|
722
|
+
movedPermanently: 301,
|
|
723
|
+
found: 302,
|
|
724
|
+
seeOther: 303,
|
|
725
|
+
temporaryRedirect: 307,
|
|
726
|
+
permanentRedirect: 308
|
|
727
|
+
}));
|
|
728
|
+
/** What the downloader says it is, so a server operator can tell where the traffic comes from. */
|
|
729
|
+
const USER_AGENT = "keepset (+https://github.com/ExaDev/keepset)";
|
|
730
|
+
/**
|
|
731
|
+
* Sends one GET and resolves with the response head, its body still unread.
|
|
732
|
+
* @param url - The URL.
|
|
733
|
+
* @param signal - Aborts the request.
|
|
734
|
+
* @returns The response.
|
|
735
|
+
*/
|
|
736
|
+
async function get(url, signal) {
|
|
737
|
+
const send = url.protocol === "https:" ? request$1 : request;
|
|
738
|
+
return new Promise((resolve, reject) => {
|
|
739
|
+
const request = send(url, {
|
|
740
|
+
method: "GET",
|
|
741
|
+
headers: { "user-agent": USER_AGENT },
|
|
742
|
+
...signal === void 0 ? {} : { signal }
|
|
743
|
+
}, resolve);
|
|
744
|
+
request.on("error", reject);
|
|
745
|
+
request.end();
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Reads a response body to the end.
|
|
750
|
+
* @param response - The response.
|
|
751
|
+
* @returns The body's bytes.
|
|
752
|
+
*/
|
|
753
|
+
async function readBody(response) {
|
|
754
|
+
const chunks = [];
|
|
755
|
+
for await (const chunk of response) {
|
|
756
|
+
if (!(chunk instanceof Uint8Array)) throw new KeepsetError("the response body yielded a chunk that is not bytes");
|
|
757
|
+
chunks.push(chunk);
|
|
758
|
+
}
|
|
759
|
+
return Buffer.concat(chunks);
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Requests one URL, following its redirects by recursion.
|
|
763
|
+
* @param url - The URL to request.
|
|
764
|
+
* @param signal - Aborts the request.
|
|
765
|
+
* @param redirects - How many redirects led here.
|
|
766
|
+
* @returns The final response.
|
|
767
|
+
*/
|
|
768
|
+
async function follow(url, signal, redirects) {
|
|
769
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new KeepsetError(`${url.href} is not an http or https URL`);
|
|
770
|
+
const response = await get(url, signal);
|
|
771
|
+
const { statusCode: status, statusMessage } = response;
|
|
772
|
+
if (status === void 0 || statusMessage === void 0) throw new KeepsetError(`${url.href} answered without a status line`);
|
|
773
|
+
if (!REDIRECTS.has(status)) return new Response(await readBody(response), {
|
|
774
|
+
status,
|
|
775
|
+
statusText: statusMessage
|
|
776
|
+
});
|
|
777
|
+
response.resume();
|
|
778
|
+
const { location } = response.headers;
|
|
779
|
+
if (location === void 0) throw new KeepsetError(`${url.href} answered ${String(status)} without a location`);
|
|
780
|
+
if (redirects === MAX_REDIRECTS) throw new KeepsetError(`${url.href} redirected more than ${String(MAX_REDIRECTS)} times`);
|
|
781
|
+
return follow(new URL(location, url), signal, redirects + 1);
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Downloads a URL's bytes exactly as the server sends them, following redirects. Unlike the platform `fetch`, it never undoes a `Content-Encoding`: servers commonly label a `.gz` file `Content-Encoding: gzip`, and a recipe that unpacks gzip needs the file as published, which is also what its SHA-256 is recorded over. No `Accept-Encoding` is sent, so a server that compresses on the fly has no reason to.
|
|
785
|
+
* @param url - An http or https URL.
|
|
786
|
+
* @param init - The request options.
|
|
787
|
+
* @returns A Response holding the raw bytes, with the final status.
|
|
788
|
+
* @throws KeepsetError when a redirect has no location, leaves http and https, or passes the redirect limit.
|
|
789
|
+
*/
|
|
790
|
+
async function downloadRaw(url, init) {
|
|
791
|
+
return follow(new URL(url), init.signal, 0);
|
|
792
|
+
}
|
|
793
|
+
//#endregion
|
|
704
794
|
//#region src/node/fetch-dataset.ts
|
|
705
795
|
/**
|
|
706
796
|
* The URL a recipe downloads a source from. A dead source is fetched from its pinned snapshot in the Wayback Machine's `id_` form, which serves the archived bytes as they were captured rather than wrapped in the archive's page chrome.
|
|
@@ -750,7 +840,7 @@ function metaOf(entry) {
|
|
|
750
840
|
*/
|
|
751
841
|
async function fetchEntry(dataset, options = {}) {
|
|
752
842
|
const { entry, crosswalk } = dataset;
|
|
753
|
-
const { fetch: fetchPort =
|
|
843
|
+
const { fetch: fetchPort = downloadRaw, signal, now = () => /* @__PURE__ */ new Date() } = options;
|
|
754
844
|
const { recipe } = entry;
|
|
755
845
|
if (recipe.status === "not-fetchable") throw new RecipeError(entry.id, "recipe", `the entry has no fetchable recipe: ${recipe.reason}`);
|
|
756
846
|
const source = entry.sources[recipe.source];
|
|
@@ -798,7 +888,7 @@ async function fetchEntry(dataset, options = {}) {
|
|
|
798
888
|
};
|
|
799
889
|
}
|
|
800
890
|
/**
|
|
801
|
-
* Fetches a dataset of the bundled catalogue by id and resolves it to its category's target shape, so `fetchDataset("cora")` yields graph/v1 `{ meta, nodes, edges }` directly. Node-only: it downloads with
|
|
891
|
+
* Fetches a dataset of the bundled catalogue by id and resolves it to its category's target shape, so `fetchDataset("cora")` yields graph/v1 `{ meta, nodes, edges }` directly. Node-only: it downloads with Node's http and https clients and unpacks with Node's zlib.
|
|
802
892
|
* @param id - The entry id.
|
|
803
893
|
* @param options - The network port, abort signal and clock.
|
|
804
894
|
* @returns The resolved target with its provenance and any count warnings.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keepset",
|
|
3
3
|
"description": "Library, CLI and statically generated catalogue for dataset fetch-and-normalise recipes",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"fflate": "0.8.3",
|
|
7
7
|
"twindex": "0.1.1",
|
|
@@ -79,6 +79,8 @@
|
|
|
79
79
|
"lint:check": "eslint . --cache --max-warnings 0",
|
|
80
80
|
"prepare": "husky",
|
|
81
81
|
"prepublishOnly": "pnpm run lint:check && pnpm run build && pnpm run typecheck && pnpm run test && publint && attw --pack --profile esm-only",
|
|
82
|
+
"seed:check": "tsx scripts/seed/import-phd-catalogue.ts --pipeline scripts/seed/sources/pipeline-catalog.json --graphbox scripts/seed/sources/graphbox-catalog.json --check",
|
|
83
|
+
"seed:import": "tsx scripts/seed/import-phd-catalogue.ts",
|
|
82
84
|
"test": "vitest run",
|
|
83
85
|
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.node.json"
|
|
84
86
|
},
|
|
@@ -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":{"
|
|
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.4.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"},"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.4.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}
|