keepset 0.19.0 → 0.20.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-8oHQPxcd.js → bundled-BmqpHjOH.js} +76 -5
- package/dist/{categories-tH53cHmu.d.ts → categories-BVROO4tM.d.ts} +22 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node/fetch-dataset.d.ts +1 -1
- package/dist/node/fetch-dataset.js +262 -9
- package/dist-cli/keepset.mjs +1 -1
- package/package.json +1 -1
- package/schemas/entry.schema.json +1 -1
- package/schemas/manifest.schema.json +1 -1
|
@@ -410,7 +410,7 @@ const CitationSchema = z.object({
|
|
|
410
410
|
const RelativePathSchema = z.string().refine((path) => path.split("/").every((segment) => segment !== "" && segment !== "." && segment !== ".."), { message: "must be a relative path of non-empty segments with no '.' or '..'" });
|
|
411
411
|
/** 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. */
|
|
412
412
|
const ArchiveMemberSchema = z.union([z.object({ path: RelativePathSchema }).strict(), z.object({ extensions: z.array(z.string().regex(/^\.[A-Za-z0-9][A-Za-z0-9.]*$/, { message: "must be a file extension such as .mtx" })).min(1) }).strict()]);
|
|
413
|
-
/** How the downloaded bytes unpack into the one file the parser reads. */
|
|
413
|
+
/** How the downloaded bytes unpack into the one file the parser reads. A workbook (`xlsx`) is read as the CSV of its named sheet, with dates as ISO 8601 text, for the `csv-table` parser. */
|
|
414
414
|
const ArchiveSchema = z.discriminatedUnion("kind", [
|
|
415
415
|
z.object({ kind: z.literal("none") }).strict(),
|
|
416
416
|
z.object({ kind: z.literal("gzip") }).strict(),
|
|
@@ -425,6 +425,10 @@ const ArchiveSchema = z.discriminatedUnion("kind", [
|
|
|
425
425
|
z.object({
|
|
426
426
|
kind: z.literal("tar.gz"),
|
|
427
427
|
member: ArchiveMemberSchema
|
|
428
|
+
}).strict(),
|
|
429
|
+
z.object({
|
|
430
|
+
kind: z.literal("xlsx"),
|
|
431
|
+
sheet: z.string().min(1)
|
|
428
432
|
}).strict()
|
|
429
433
|
]);
|
|
430
434
|
const ColumnSchema = z.int().min(0);
|
|
@@ -889,7 +893,7 @@ var catalogue_default = {
|
|
|
889
893
|
"id": "keepset",
|
|
890
894
|
"name": "keepset",
|
|
891
895
|
"description": "The keepset catalogue: datasets with machine-readable recipes that fetch each one and normalise it into its category's target shape.",
|
|
892
|
-
"version": "1.
|
|
896
|
+
"version": "1.8.0",
|
|
893
897
|
"repository": "https://github.com/Mearman/keepset",
|
|
894
898
|
"namespaceAliases": {
|
|
895
899
|
"networkrepository": ["nr"],
|
|
@@ -5191,6 +5195,10 @@ var catalogue_default = {
|
|
|
5191
5195
|
"url": "https://environment.data.gov.uk/dataset/9c7728fc-68e2-46e1-addb-63790b6b5313",
|
|
5192
5196
|
"retrieved": "2026-10-19",
|
|
5193
5197
|
"status": "live"
|
|
5198
|
+
}, {
|
|
5199
|
+
"url": "https://environment.data.gov.uk/api/file/download?fileDataSetId=e06036f5-7f41-46a3-93ff-fbd0fa1a83cc&fileName=Environment_Agency_Prosecutions.xlsx",
|
|
5200
|
+
"retrieved": "2026-10-19",
|
|
5201
|
+
"status": "live"
|
|
5194
5202
|
}],
|
|
5195
5203
|
"licence": {
|
|
5196
5204
|
"kind": "spdx",
|
|
@@ -5198,10 +5206,73 @@ var catalogue_default = {
|
|
|
5198
5206
|
},
|
|
5199
5207
|
"citations": [],
|
|
5200
5208
|
"recipe": {
|
|
5201
|
-
"status": "
|
|
5202
|
-
"
|
|
5209
|
+
"status": "fetchable",
|
|
5210
|
+
"source": 1,
|
|
5211
|
+
"archive": {
|
|
5212
|
+
"kind": "xlsx",
|
|
5213
|
+
"sheet": "Environment Agency Prosecutions"
|
|
5214
|
+
},
|
|
5215
|
+
"parser": {
|
|
5216
|
+
"format": "csv-table",
|
|
5217
|
+
"encoding": "utf-8",
|
|
5218
|
+
"delimiter": "comma",
|
|
5219
|
+
"skipLines": 0,
|
|
5220
|
+
"idColumn": null,
|
|
5221
|
+
"numericColumns": [{
|
|
5222
|
+
"column": "Fine",
|
|
5223
|
+
"factor": 1
|
|
5224
|
+
}],
|
|
5225
|
+
"columns": [
|
|
5226
|
+
"Date_of_Action",
|
|
5227
|
+
"Fine",
|
|
5228
|
+
"Incident_Type",
|
|
5229
|
+
"Verdict",
|
|
5230
|
+
"Incident_Area"
|
|
5231
|
+
]
|
|
5232
|
+
}
|
|
5233
|
+
},
|
|
5234
|
+
"crosswalk": {
|
|
5235
|
+
"source": "csv-table",
|
|
5236
|
+
"target": "or/v1",
|
|
5237
|
+
"rules": [
|
|
5238
|
+
{
|
|
5239
|
+
"op": "move",
|
|
5240
|
+
"from": "meta",
|
|
5241
|
+
"to": "meta"
|
|
5242
|
+
},
|
|
5243
|
+
{
|
|
5244
|
+
"op": "move",
|
|
5245
|
+
"from": "nodes[].id",
|
|
5246
|
+
"to": "nodes[].id"
|
|
5247
|
+
},
|
|
5248
|
+
{
|
|
5249
|
+
"op": "move",
|
|
5250
|
+
"from": "nodes[].Date_of_Action",
|
|
5251
|
+
"to": "nodes[].closed"
|
|
5252
|
+
},
|
|
5253
|
+
{
|
|
5254
|
+
"op": "move",
|
|
5255
|
+
"from": "nodes[].Fine",
|
|
5256
|
+
"to": "nodes[].prize"
|
|
5257
|
+
},
|
|
5258
|
+
{
|
|
5259
|
+
"op": "move",
|
|
5260
|
+
"from": "nodes[].Incident_Type",
|
|
5261
|
+
"to": "nodes[].skill"
|
|
5262
|
+
},
|
|
5263
|
+
{
|
|
5264
|
+
"op": "move",
|
|
5265
|
+
"from": "nodes[].Verdict",
|
|
5266
|
+
"to": "nodes[].status"
|
|
5267
|
+
},
|
|
5268
|
+
{
|
|
5269
|
+
"op": "move",
|
|
5270
|
+
"from": "nodes[].Incident_Area",
|
|
5271
|
+
"to": "nodes[].territory"
|
|
5272
|
+
}
|
|
5273
|
+
],
|
|
5274
|
+
"oneWay": []
|
|
5203
5275
|
},
|
|
5204
|
-
"crosswalk": { "file": "crosswalks/or-v1/nodes.json" },
|
|
5205
5276
|
"expectedCounts": {},
|
|
5206
5277
|
"properties": { "surveyId": "or-50" }
|
|
5207
5278
|
},
|
|
@@ -70,7 +70,7 @@ declare const ArchiveMemberSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
70
70
|
extensions: z.ZodArray<z.ZodString>;
|
|
71
71
|
}, z.core.$strict>]>;
|
|
72
72
|
type ArchiveMember = z.infer<typeof ArchiveMemberSchema>;
|
|
73
|
-
/** How the downloaded bytes unpack into the one file the parser reads. */
|
|
73
|
+
/** How the downloaded bytes unpack into the one file the parser reads. A workbook (`xlsx`) is read as the CSV of its named sheet, with dates as ISO 8601 text, for the `csv-table` parser. */
|
|
74
74
|
declare const ArchiveSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
75
75
|
kind: z.ZodLiteral<"none">;
|
|
76
76
|
}, z.core.$strict>, z.ZodObject<{
|
|
@@ -96,6 +96,9 @@ declare const ArchiveSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
96
96
|
}, z.core.$strict>, z.ZodObject<{
|
|
97
97
|
extensions: z.ZodArray<z.ZodString>;
|
|
98
98
|
}, z.core.$strict>]>;
|
|
99
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
100
|
+
kind: z.ZodLiteral<"xlsx">;
|
|
101
|
+
sheet: z.ZodString;
|
|
99
102
|
}, z.core.$strict>], "kind">;
|
|
100
103
|
type Archive = z.infer<typeof ArchiveSchema>;
|
|
101
104
|
/** A column of a table as the table parser names it: the header with every run of characters outside letters, digits and underscore replaced by one underscore, so it is a valid segment of a twindex path. */
|
|
@@ -199,6 +202,9 @@ declare const RecipeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
199
202
|
}, z.core.$strict>, z.ZodObject<{
|
|
200
203
|
extensions: z.ZodArray<z.ZodString>;
|
|
201
204
|
}, z.core.$strict>]>;
|
|
205
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
206
|
+
kind: z.ZodLiteral<"xlsx">;
|
|
207
|
+
sheet: z.ZodString;
|
|
202
208
|
}, z.core.$strict>], "kind">;
|
|
203
209
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
204
210
|
format: z.ZodLiteral<"edge-list">;
|
|
@@ -352,6 +358,9 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
352
358
|
}, z.core.$strict>, z.ZodObject<{
|
|
353
359
|
extensions: z.ZodArray<z.ZodString>;
|
|
354
360
|
}, z.core.$strict>]>;
|
|
361
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
362
|
+
kind: z.ZodLiteral<"xlsx">;
|
|
363
|
+
sheet: z.ZodString;
|
|
355
364
|
}, z.core.$strict>], "kind">;
|
|
356
365
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
357
366
|
format: z.ZodLiteral<"edge-list">;
|
|
@@ -486,6 +495,9 @@ declare const EntrySchema: z.ZodObject<{
|
|
|
486
495
|
} | {
|
|
487
496
|
extensions: string[];
|
|
488
497
|
};
|
|
498
|
+
} | {
|
|
499
|
+
kind: "xlsx";
|
|
500
|
+
sheet: string;
|
|
489
501
|
};
|
|
490
502
|
parser: {
|
|
491
503
|
format: "edge-list";
|
|
@@ -709,6 +721,9 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
709
721
|
}, z.core.$strict>, z.ZodObject<{
|
|
710
722
|
extensions: z.ZodArray<z.ZodString>;
|
|
711
723
|
}, z.core.$strict>]>;
|
|
724
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
725
|
+
kind: z.ZodLiteral<"xlsx">;
|
|
726
|
+
sheet: z.ZodString;
|
|
712
727
|
}, z.core.$strict>], "kind">;
|
|
713
728
|
parser: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
714
729
|
format: z.ZodLiteral<"edge-list">;
|
|
@@ -843,6 +858,9 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
843
858
|
} | {
|
|
844
859
|
extensions: string[];
|
|
845
860
|
};
|
|
861
|
+
} | {
|
|
862
|
+
kind: "xlsx";
|
|
863
|
+
sheet: string;
|
|
846
864
|
};
|
|
847
865
|
parser: {
|
|
848
866
|
format: "edge-list";
|
|
@@ -975,6 +993,9 @@ declare const CatalogueIndexSchema: z.ZodObject<{
|
|
|
975
993
|
} | {
|
|
976
994
|
extensions: string[];
|
|
977
995
|
};
|
|
996
|
+
} | {
|
|
997
|
+
kind: "xlsx";
|
|
998
|
+
sheet: string;
|
|
978
999
|
};
|
|
979
1000
|
parser: {
|
|
980
1001
|
format: "edge-list";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as Parser, A as LineageSchema, B as Citation, C as CatalogueIndex, D as NamespaceNode, E as DatasetQuery, F as AliasSchema, G as Entry, H as ColumnNameSchema, I as Archive, J as IdSchema, K as EntrySchema, L as ArchiveMember, M as ManifestSchema, N as SemVerSchema, O as createCatalogue, P as parseManifest, Q as NamespaceSchema, R as ArchiveMemberSchema, S as Catalogue, T as Dataset, U as CrosswalkReference, V as CitationSchema, W as CrosswalkReferenceSchema, X as LicenceSchema, Y as Licence, Z as Namespace, _ as GraphEdgeSchema, a as CategoryTargets, at as Snapshot, b as GraphV1, c as getCategory, ct as SourceSchema, d as listCategories, dt as waybackUrl, et as ParserSchema, f as OrNode, g as GraphEdge, h as OrV1Schema, i as CategoryTarget, it as RelativePathSchema, j as Manifest, k as Lineage, l as getTarget, lt as WaybackTimestampSchema, m as OrV1, n as CategoryId, nt as Recipe, o as CountedKey, ot as SnapshotSchema, p as OrNodeSchema, q as HttpUrlSchema, r as CategoryIdSchema, rt as RecipeSchema, s as countTarget, st as Source, t as Category, tt as RESERVED_META_KEYS, u as kindOf, ut as parseEntry, v as GraphNode, w as CatalogueIndexSchema, x as GraphV1Schema, y as GraphNodeSchema, z as ArchiveSchema } from "./categories-
|
|
1
|
+
import { $ as Parser, A as LineageSchema, B as Citation, C as CatalogueIndex, D as NamespaceNode, E as DatasetQuery, F as AliasSchema, G as Entry, H as ColumnNameSchema, I as Archive, J as IdSchema, K as EntrySchema, L as ArchiveMember, M as ManifestSchema, N as SemVerSchema, O as createCatalogue, P as parseManifest, Q as NamespaceSchema, R as ArchiveMemberSchema, S as Catalogue, T as Dataset, U as CrosswalkReference, V as CitationSchema, W as CrosswalkReferenceSchema, X as LicenceSchema, Y as Licence, Z as Namespace, _ as GraphEdgeSchema, a as CategoryTargets, at as Snapshot, b as GraphV1, c as getCategory, ct as SourceSchema, d as listCategories, dt as waybackUrl, et as ParserSchema, f as OrNode, g as GraphEdge, h as OrV1Schema, i as CategoryTarget, it as RelativePathSchema, j as Manifest, k as Lineage, l as getTarget, lt as WaybackTimestampSchema, m as OrV1, n as CategoryId, nt as Recipe, o as CountedKey, ot as SnapshotSchema, p as OrNodeSchema, q as HttpUrlSchema, r as CategoryIdSchema, rt as RecipeSchema, s as countTarget, st as Source, t as Category, tt as RESERVED_META_KEYS, u as kindOf, ut as parseEntry, v as GraphNode, w as CatalogueIndexSchema, x as GraphV1Schema, y as GraphNodeSchema, z as ArchiveSchema } from "./categories-BVROO4tM.js";
|
|
2
2
|
import { Crosswalk } from "twindex";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/errors.d.ts
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as NativeParseError, A as toIssues, B as kindOf, C as RESERVED_META_KEYS, D as SourceSchema, E as SnapshotSchema, G as GraphNodeSchema, H as OrNodeSchema, I as CategoryIdSchema, J as ArchiveError, K as GraphV1Schema, L as countTarget, M as NAME_SEPARATOR, N as groupsOf, O as WaybackTimestampSchema, P as namesOf, Q as KeepsetError, R as getCategory, S as ParserSchema, T as RelativePathSchema, U as OrV1Schema, V as listCategories, W as GraphEdgeSchema, X as DatasetAmbiguousError, Y as CatalogueSyntaxError, Z as DatasetNotFoundError, _ as EntrySchema, a as CatalogueIndexSchema, b as LicenceSchema, c as ManifestSchema, d as AliasSchema, et as RecipeError, f as ArchiveMemberSchema, g as CrosswalkReferenceSchema, h as ColumnNameSchema, i as listNamespaces, j as waybackUrl, k as parseEntry, l as SemVerSchema, m as CitationSchema, n as getManifest, o as createCatalogue, p as ArchiveSchema, q as JsonScalarSchema, r as listDatasets, s as LineageSchema, t as getDataset, tt as UnknownCategoryError, u as parseManifest, v as HttpUrlSchema, w as RecipeSchema, x as NamespaceSchema, y as IdSchema, z as getTarget } from "./bundled-
|
|
1
|
+
import { $ as NativeParseError, A as toIssues, B as kindOf, C as RESERVED_META_KEYS, D as SourceSchema, E as SnapshotSchema, G as GraphNodeSchema, H as OrNodeSchema, I as CategoryIdSchema, J as ArchiveError, K as GraphV1Schema, L as countTarget, M as NAME_SEPARATOR, N as groupsOf, O as WaybackTimestampSchema, P as namesOf, Q as KeepsetError, R as getCategory, S as ParserSchema, T as RelativePathSchema, U as OrV1Schema, V as listCategories, W as GraphEdgeSchema, X as DatasetAmbiguousError, Y as CatalogueSyntaxError, Z as DatasetNotFoundError, _ as EntrySchema, a as CatalogueIndexSchema, b as LicenceSchema, c as ManifestSchema, d as AliasSchema, et as RecipeError, f as ArchiveMemberSchema, g as CrosswalkReferenceSchema, h as ColumnNameSchema, i as listNamespaces, j as waybackUrl, k as parseEntry, l as SemVerSchema, m as CitationSchema, n as getManifest, o as createCatalogue, p as ArchiveSchema, q as JsonScalarSchema, r as listDatasets, s as LineageSchema, t as getDataset, tt as UnknownCategoryError, u as parseManifest, v as HttpUrlSchema, w as RecipeSchema, x as NamespaceSchema, y as IdSchema, z as getTarget } from "./bundled-BmqpHjOH.js";
|
|
2
2
|
import { validateCrosswalk } from "twindex";
|
|
3
3
|
//#region src/serialise.ts
|
|
4
4
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as Entry, T as Dataset, at as Snapshot, i as CategoryTarget, st as Source } from "../categories-
|
|
1
|
+
import { G as Entry, T as Dataset, at as Snapshot, i as CategoryTarget, st as Source } from "../categories-BVROO4tM.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 { $ as NativeParseError, F as getOwn, J as ArchiveError, L as countTarget, Q as KeepsetError, et as RecipeError, t as getDataset, z as getTarget } from "../bundled-
|
|
1
|
+
import { $ as NativeParseError, F as getOwn, J as ArchiveError, L as countTarget, Q as KeepsetError, et as RecipeError, t as getDataset, z as getTarget } from "../bundled-BmqpHjOH.js";
|
|
2
2
|
import { RoundTripMismatchError, assertRoundTrip, encode } from "twindex";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
@@ -356,7 +356,7 @@ const DELIMITERS = {
|
|
|
356
356
|
semicolon: ";"
|
|
357
357
|
};
|
|
358
358
|
/** A number as a decimal literal, with an optional sign and exponent. `Number` alone would also accept hex, binary, `Infinity` and the empty string. */
|
|
359
|
-
const DECIMAL = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
359
|
+
const DECIMAL$1 = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
360
360
|
/** The byte order mark some exporters put before the first header. */
|
|
361
361
|
const BYTE_ORDER_MARK = "";
|
|
362
362
|
/**
|
|
@@ -559,7 +559,7 @@ function* withoutByteOrderMark(text) {
|
|
|
559
559
|
*/
|
|
560
560
|
function numeric$1(cell, factor, column, line) {
|
|
561
561
|
if (cell === "") return null;
|
|
562
|
-
if (!DECIMAL.test(cell)) throw new NativeParseError("csv-table", line, `column ${column} is declared numeric but holds ${JSON.stringify(cell)}`);
|
|
562
|
+
if (!DECIMAL$1.test(cell)) throw new NativeParseError("csv-table", line, `column ${column} is declared numeric but holds ${JSON.stringify(cell)}`);
|
|
563
563
|
return Number(cell) * factor;
|
|
564
564
|
}
|
|
565
565
|
/**
|
|
@@ -933,7 +933,7 @@ const BYTE_RADIX = 256;
|
|
|
933
933
|
* @param length - Field length.
|
|
934
934
|
* @returns The field text.
|
|
935
935
|
*/
|
|
936
|
-
function field(bytes, offset, length) {
|
|
936
|
+
function field$1(bytes, offset, length) {
|
|
937
937
|
const slice = bytes.subarray(offset, offset + length);
|
|
938
938
|
const end = slice.indexOf(0);
|
|
939
939
|
return new TextDecoder().decode(end === -1 ? slice : slice.subarray(0, end));
|
|
@@ -952,7 +952,7 @@ function numeric(bytes, offset, length) {
|
|
|
952
952
|
for (const byte of bytes.subarray(offset + 1, offset + length)) value = value * BYTE_RADIX + byte;
|
|
953
953
|
return value;
|
|
954
954
|
}
|
|
955
|
-
const text = field(bytes, offset, length).trim();
|
|
955
|
+
const text = field$1(bytes, offset, length).trim();
|
|
956
956
|
if (!/^[0-7]*$/.test(text)) throw new ArchiveError(`tar header field is not octal: ${JSON.stringify(text)}`);
|
|
957
957
|
return text === "" ? 0 : Number.parseInt(text, 8);
|
|
958
958
|
}
|
|
@@ -988,21 +988,21 @@ function readTar(bytes) {
|
|
|
988
988
|
for (const [index, byte] of header.entries()) sum += index >= checksum.offset && index < checksum.offset + checksum.length ? 32 : byte;
|
|
989
989
|
if (sum !== numeric(header, checksum.offset, checksum.length)) throw new ArchiveError(`tar header at byte ${String(offset)} fails its checksum`);
|
|
990
990
|
const size = numeric(header, HEADER.size.offset, HEADER.size.length);
|
|
991
|
-
const type = field(header, HEADER.type.offset, HEADER.type.length);
|
|
991
|
+
const type = field$1(header, HEADER.type.offset, HEADER.type.length);
|
|
992
992
|
const dataStart = offset + 512;
|
|
993
993
|
if (dataStart + size > bytes.length) throw new ArchiveError(`tar member at byte ${String(offset)} runs past the end of the archive`);
|
|
994
994
|
const data = bytes.subarray(dataStart, dataStart + size);
|
|
995
995
|
offset = dataStart + Math.ceil(size / 512) * 512;
|
|
996
996
|
if (type === "L") {
|
|
997
|
-
longName = field(data, 0, data.length);
|
|
997
|
+
longName = field$1(data, 0, data.length);
|
|
998
998
|
continue;
|
|
999
999
|
}
|
|
1000
1000
|
if (type === "x") {
|
|
1001
1001
|
longName = paxPath(data);
|
|
1002
1002
|
continue;
|
|
1003
1003
|
}
|
|
1004
|
-
const name = field(header, HEADER.name.offset, HEADER.name.length);
|
|
1005
|
-
const prefix = field(header, HEADER.magic.offset, HEADER.magic.length).startsWith("ustar") ? field(header, HEADER.prefix.offset, HEADER.prefix.length) : "";
|
|
1004
|
+
const name = field$1(header, HEADER.name.offset, HEADER.name.length);
|
|
1005
|
+
const prefix = field$1(header, HEADER.magic.offset, HEADER.magic.length).startsWith("ustar") ? field$1(header, HEADER.prefix.offset, HEADER.prefix.length) : "";
|
|
1006
1006
|
const path = longName ?? (prefix === "" ? name : `${prefix}/${name}`);
|
|
1007
1007
|
longName = void 0;
|
|
1008
1008
|
if (type === "0" || type === "" || type === "7") members.push({
|
|
@@ -1013,6 +1013,255 @@ function readTar(bytes) {
|
|
|
1013
1013
|
return members;
|
|
1014
1014
|
}
|
|
1015
1015
|
//#endregion
|
|
1016
|
+
//#region src/node/xlsx.ts
|
|
1017
|
+
const HEXADECIMAL = 16;
|
|
1018
|
+
const DECIMAL = 10;
|
|
1019
|
+
const LETTERS = 26;
|
|
1020
|
+
const FIRST_LETTER = "A".charCodeAt(0);
|
|
1021
|
+
const SECONDS_PER_DAY = 86400;
|
|
1022
|
+
const MILLISECONDS = 1e3;
|
|
1023
|
+
/** The serial number Excel gives its 29th of February 1900, a day that never existed: serials above it count from 30 December 1899 and serials below it from 31 December 1899. */
|
|
1024
|
+
const LEAP_BUG_SERIAL = 60;
|
|
1025
|
+
/** The day a serial number counts from: 1 January 1904 in a workbook that says so, otherwise 31 December 1899 for the serials before Excel's false 29th of February 1900 and 30 December 1899 for those after it. */
|
|
1026
|
+
const EPOCH_1904 = "1904-01-01T00:00:00Z";
|
|
1027
|
+
const EPOCH_BEFORE_LEAP_BUG = "1899-12-31T00:00:00Z";
|
|
1028
|
+
const EPOCH_AFTER_LEAP_BUG = "1899-12-30T00:00:00Z";
|
|
1029
|
+
const DATE_LENGTH = 10;
|
|
1030
|
+
const DATE_TIME_LENGTH = 19;
|
|
1031
|
+
const SECONDS_PER_MINUTE = 60;
|
|
1032
|
+
const MINUTES_PER_HOUR = 60;
|
|
1033
|
+
/**
|
|
1034
|
+
* Replaces the XML entities and character references of a text.
|
|
1035
|
+
* @param text - Escaped text.
|
|
1036
|
+
* @returns The text.
|
|
1037
|
+
*/
|
|
1038
|
+
function unescape(text) {
|
|
1039
|
+
return text.replaceAll(/&(#x[0-9A-Fa-f]+|#[0-9]+|amp|lt|gt|quot|apos);/g, (_whole, entity) => {
|
|
1040
|
+
if (entity.startsWith("#x")) return String.fromCodePoint(Number.parseInt(entity.slice(2), HEXADECIMAL));
|
|
1041
|
+
if (entity.startsWith("#")) return String.fromCodePoint(Number.parseInt(entity.slice(1), DECIMAL));
|
|
1042
|
+
return {
|
|
1043
|
+
amp: "&",
|
|
1044
|
+
lt: "<",
|
|
1045
|
+
gt: ">",
|
|
1046
|
+
quot: "\"",
|
|
1047
|
+
apos: "'"
|
|
1048
|
+
}[entity] ?? "";
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* The attributes of a start tag's text.
|
|
1053
|
+
* @param tag - The text between `<` and `>`, name included.
|
|
1054
|
+
* @returns The attributes, unescaped.
|
|
1055
|
+
*/
|
|
1056
|
+
function attributesOf(tag) {
|
|
1057
|
+
const attributes = {};
|
|
1058
|
+
for (const [, name, value] of tag.matchAll(/([\w:.-]+)\s*=\s*"([^"]*)"/g)) if (name !== void 0 && value !== void 0) attributes[name] = unescape(value);
|
|
1059
|
+
return attributes;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* The start tags of one element name in an XML part, in order.
|
|
1063
|
+
* @param xml - The part's text.
|
|
1064
|
+
* @param name - The element name.
|
|
1065
|
+
* @returns The attributes of each start tag.
|
|
1066
|
+
*/
|
|
1067
|
+
function elements(xml, name) {
|
|
1068
|
+
return [...xml.matchAll(new RegExp(`<${name}(?=[\\s/>])([^>]*)>`, "g"))].map((match) => attributesOf(match[1] ?? ""));
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* The strings of a workbook's shared string table, in index order: each item's text runs joined, the phonetic runs of East Asian text left out.
|
|
1072
|
+
* @param xml - The `sharedStrings.xml` text.
|
|
1073
|
+
* @returns The strings.
|
|
1074
|
+
*/
|
|
1075
|
+
function sharedStrings(xml) {
|
|
1076
|
+
return [...xml.matchAll(/<si(?:\s[^>]*)?>([\s\S]*?)<\/si>/g)].map((item) => {
|
|
1077
|
+
return [...(item[1] ?? "").replaceAll(/<rPh[\s\S]*?<\/rPh>/g, "").matchAll(/<t(?:\s[^>]*)?>([\s\S]*?)<\/t>/g)].map((text) => unescape(text[1] ?? "")).join("");
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
/** The built-in number format ids that show a date or a time, as inclusive ranges (ECMA-376, 18.8.30): the short and long dates and times, the locale dates, and the elapsed and minute times. */
|
|
1081
|
+
const BUILT_IN_DATE_FORMATS = [
|
|
1082
|
+
{
|
|
1083
|
+
first: 14,
|
|
1084
|
+
last: 22
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
first: 27,
|
|
1088
|
+
last: 36
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
first: 45,
|
|
1092
|
+
last: 47
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
first: 50,
|
|
1096
|
+
last: 58
|
|
1097
|
+
}
|
|
1098
|
+
];
|
|
1099
|
+
/**
|
|
1100
|
+
* Whether a custom number format shows a date or a time: it names a year, month, day, hour or second once its quoted text, escaped characters and bracketed conditions and colours are removed.
|
|
1101
|
+
* @param code - The format code.
|
|
1102
|
+
* @returns True for a date or time format.
|
|
1103
|
+
*/
|
|
1104
|
+
function isDateFormat(code) {
|
|
1105
|
+
return /[ymdhs]/i.test(code.replaceAll(/"[^"]*"|\\.|\[[^\]]*\]/g, ""));
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Which cell styles show dates, by the style's index.
|
|
1109
|
+
* @param xml - The `styles.xml` text, or undefined when the workbook has none.
|
|
1110
|
+
* @returns The style indexes whose number format is a date or a time.
|
|
1111
|
+
*/
|
|
1112
|
+
function dateStyles(xml) {
|
|
1113
|
+
if (xml === void 0) return /* @__PURE__ */ new Set();
|
|
1114
|
+
const custom = /* @__PURE__ */ new Map();
|
|
1115
|
+
for (const format of elements(xml, "numFmt")) if (format["numFmtId"] !== void 0 && format["formatCode"] !== void 0) custom.set(Number(format["numFmtId"]), format["formatCode"]);
|
|
1116
|
+
const cellFormats = /<cellXfs(?:\s[^>]*)?>([\s\S]*?)<\/cellXfs>/.exec(xml)?.[1] ?? "";
|
|
1117
|
+
const styles = /* @__PURE__ */ new Set();
|
|
1118
|
+
for (const [index, format] of elements(cellFormats, "xf").entries()) {
|
|
1119
|
+
const id = Number(format["numFmtId"] ?? "0");
|
|
1120
|
+
const code = custom.get(id);
|
|
1121
|
+
if (code === void 0 ? BUILT_IN_DATE_FORMATS.some((range) => id >= range.first && id <= range.last) : isDateFormat(code)) styles.add(index);
|
|
1122
|
+
}
|
|
1123
|
+
return styles;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* A date serial number as ISO 8601 text: a date when it has no time of day, a date and time when it has, and a time alone for a serial below one day in a 1900 workbook.
|
|
1127
|
+
* @param serial - The serial number, with the time of day as its fraction.
|
|
1128
|
+
* @param date1904 - Whether the workbook counts days from 1 January 1904.
|
|
1129
|
+
* @returns The text.
|
|
1130
|
+
* @throws ArchiveError for the serial of the 29th of February 1900, which is not a date.
|
|
1131
|
+
*/
|
|
1132
|
+
function isoOf(serial, date1904) {
|
|
1133
|
+
const whole = Math.floor(serial);
|
|
1134
|
+
const seconds = Math.round((serial - whole) * SECONDS_PER_DAY);
|
|
1135
|
+
if (!date1904 && whole === 0) return [
|
|
1136
|
+
Math.floor(seconds / 3600),
|
|
1137
|
+
Math.floor(seconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR,
|
|
1138
|
+
seconds % SECONDS_PER_MINUTE
|
|
1139
|
+
].map((part) => String(part).padStart(2, "0")).join(":");
|
|
1140
|
+
if (!date1904 && whole === LEAP_BUG_SERIAL) throw new ArchiveError("a cell holds the serial of 29 February 1900, which is not a date");
|
|
1141
|
+
return new Date(Date.parse(date1904 ? EPOCH_1904 : whole < LEAP_BUG_SERIAL ? EPOCH_BEFORE_LEAP_BUG : EPOCH_AFTER_LEAP_BUG) + (whole * SECONDS_PER_DAY + seconds) * MILLISECONDS).toISOString().slice(0, seconds === 0 ? DATE_LENGTH : DATE_TIME_LENGTH);
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* The 0-based column of a cell reference such as `AB12`.
|
|
1145
|
+
* @param reference - The reference.
|
|
1146
|
+
* @returns The column index.
|
|
1147
|
+
*/
|
|
1148
|
+
function columnOf(reference) {
|
|
1149
|
+
let index = 0;
|
|
1150
|
+
for (const letter of reference.replaceAll(/[^A-Za-z]/g, "")) index = index * LETTERS + (letter.toUpperCase().charCodeAt(0) - FIRST_LETTER + 1);
|
|
1151
|
+
return index - 1;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* One CSV field: quoted when it holds a comma, a quote or a line break.
|
|
1155
|
+
* @param text - The cell text.
|
|
1156
|
+
* @returns The field.
|
|
1157
|
+
*/
|
|
1158
|
+
function field(text) {
|
|
1159
|
+
return /[",\r\n]/.test(text) ? `"${text.replaceAll("\"", "\"\"")}"` : text;
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Finds the part that holds a named sheet.
|
|
1163
|
+
* @param workbook - The `workbook.xml` text.
|
|
1164
|
+
* @param rels - The workbook's relationships text.
|
|
1165
|
+
* @param name - The sheet's name.
|
|
1166
|
+
* @returns The part's path inside the archive.
|
|
1167
|
+
* @throws ArchiveError when the workbook has no such sheet or its relationship is missing.
|
|
1168
|
+
*/
|
|
1169
|
+
function sheetPath(workbook, rels, name) {
|
|
1170
|
+
const sheets = elements(workbook, "sheet");
|
|
1171
|
+
const wanted = sheets.find((sheet) => sheet["name"] === name);
|
|
1172
|
+
if (wanted === void 0) throw new ArchiveError(`the workbook has no sheet ${name}; it has ${sheets.map((sheet) => sheet["name"] ?? "").join(", ")}`);
|
|
1173
|
+
const target = elements(rels, "Relationship").find((relation) => relation["Id"] === wanted["r:id"])?.["Target"];
|
|
1174
|
+
if (target === void 0) throw new ArchiveError(`the workbook has no relationship for the sheet ${name}`);
|
|
1175
|
+
return target.startsWith("/") ? target.slice(1) : `xl/${target}`;
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Reads a worksheet's cells into text by row and column.
|
|
1179
|
+
* @param xml - The worksheet's text.
|
|
1180
|
+
* @param strings - The shared strings.
|
|
1181
|
+
* @param dates - The date styles.
|
|
1182
|
+
* @param date1904 - Whether the workbook counts days from 1 January 1904.
|
|
1183
|
+
* @returns The rows by 1-based row number, each a sparse list of the cells' text, and the widest row's column count.
|
|
1184
|
+
* @throws ArchiveError when a cell names a shared string the workbook lacks or a date cell holds no number.
|
|
1185
|
+
*/
|
|
1186
|
+
function readRows(xml, strings, dates, date1904) {
|
|
1187
|
+
const rows = /* @__PURE__ */ new Map();
|
|
1188
|
+
let width = 0;
|
|
1189
|
+
let next = 1;
|
|
1190
|
+
for (const row of xml.matchAll(/<row(?=[\s>])([^>]*?)(?:\/>|>([\s\S]*?)<\/row>)/g)) {
|
|
1191
|
+
const number = Number(attributesOf(row[1] ?? "")["r"] ?? next);
|
|
1192
|
+
next = number + 1;
|
|
1193
|
+
const cells = [];
|
|
1194
|
+
for (const cell of (row[2] ?? "").matchAll(/<c(?=[\s>])([^>]*?)(?:\/>|>([\s\S]*?)<\/c>)/g)) {
|
|
1195
|
+
const attributes = attributesOf(cell[1] ?? "");
|
|
1196
|
+
const column = attributes["r"] === void 0 ? cells.length : columnOf(attributes["r"]);
|
|
1197
|
+
const body = cell[2] ?? "";
|
|
1198
|
+
const value = /<v(?:\s[^>]*)?>([\s\S]*?)<\/v>/.exec(body)?.[1];
|
|
1199
|
+
let text;
|
|
1200
|
+
if (attributes["t"] === "inlineStr") text = [...body.matchAll(/<t(?:\s[^>]*)?>([\s\S]*?)<\/t>/g)].map((run) => unescape(run[1] ?? "")).join("");
|
|
1201
|
+
else if (value === void 0) text = void 0;
|
|
1202
|
+
else if (attributes["t"] === "s") {
|
|
1203
|
+
text = strings[Number(value)];
|
|
1204
|
+
if (text === void 0) throw new ArchiveError(`row ${String(number)} names the shared string ${value}, which the workbook does not have`);
|
|
1205
|
+
} else if (attributes["t"] === "b") text = value === "0" ? "FALSE" : "TRUE";
|
|
1206
|
+
else if (attributes["t"] === "str" || attributes["t"] === "e") text = unescape(value);
|
|
1207
|
+
else if (dates.has(Number(attributes["s"] ?? "0"))) {
|
|
1208
|
+
const serial = Number(value);
|
|
1209
|
+
if (!Number.isFinite(serial) || serial < 0) throw new ArchiveError(`row ${String(number)} has a date cell holding ${value}`);
|
|
1210
|
+
text = isoOf(serial, date1904);
|
|
1211
|
+
} else text = unescape(value);
|
|
1212
|
+
if (text !== void 0 && text !== "") {
|
|
1213
|
+
cells[column] = text;
|
|
1214
|
+
width = Math.max(width, column + 1);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
rows.set(number, cells);
|
|
1218
|
+
}
|
|
1219
|
+
return {
|
|
1220
|
+
rows,
|
|
1221
|
+
width
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Writes one sheet of an xlsx workbook as CSV text, so a recipe reads it with the table parser: one line for every row number from the first to the last (a row the sheet leaves out is an empty line, so the lines above the header are the rows an editor shows), every line the width of the sheet's widest row, a date cell as ISO 8601 text, a boolean as TRUE or FALSE, a number as the text the workbook stores, and a formula as its stored result.
|
|
1226
|
+
* @param bytes - The workbook.
|
|
1227
|
+
* @param sheet - The sheet's name.
|
|
1228
|
+
* @returns The CSV, in UTF-8.
|
|
1229
|
+
* @throws ArchiveError when the bytes are not a workbook, the sheet is missing, or a cell cannot be read.
|
|
1230
|
+
*/
|
|
1231
|
+
function sheetToCsv(bytes, sheet) {
|
|
1232
|
+
let parts;
|
|
1233
|
+
try {
|
|
1234
|
+
const wanted = /* @__PURE__ */ new Set([
|
|
1235
|
+
"xl/workbook.xml",
|
|
1236
|
+
"xl/_rels/workbook.xml.rels",
|
|
1237
|
+
"xl/sharedStrings.xml",
|
|
1238
|
+
"xl/styles.xml"
|
|
1239
|
+
]);
|
|
1240
|
+
parts = unzipSync(bytes, { filter: (file) => wanted.has(file.name) });
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
throw new ArchiveError(`not a valid workbook: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
1243
|
+
}
|
|
1244
|
+
const text = (path) => {
|
|
1245
|
+
const part = getOwn(parts, path);
|
|
1246
|
+
return part === void 0 ? void 0 : new TextDecoder("utf-8").decode(part);
|
|
1247
|
+
};
|
|
1248
|
+
const workbook = text("xl/workbook.xml");
|
|
1249
|
+
const rels = text("xl/_rels/workbook.xml.rels");
|
|
1250
|
+
if (workbook === void 0 || rels === void 0) throw new ArchiveError("the file is not an xlsx workbook: it has no xl/workbook.xml with its relationships");
|
|
1251
|
+
const path = sheetPath(workbook, rels, sheet);
|
|
1252
|
+
const sheetBytes = getOwn(unzipSync(bytes, { filter: (file) => file.name === path }), path);
|
|
1253
|
+
if (sheetBytes === void 0) throw new ArchiveError(`the workbook names the sheet ${sheet} in ${path}, which the file does not have`);
|
|
1254
|
+
const strings = text("xl/sharedStrings.xml");
|
|
1255
|
+
const { rows, width } = readRows(new TextDecoder("utf-8").decode(sheetBytes), strings === void 0 ? [] : sharedStrings(strings), dateStyles(text("xl/styles.xml")), /<workbookPr(?=[\s/>])[^>]*date1904="(?:1|true)"/.test(workbook));
|
|
1256
|
+
const last = Math.max(0, ...rows.keys());
|
|
1257
|
+
const lines = [];
|
|
1258
|
+
for (let number = 1; number <= last; number += 1) {
|
|
1259
|
+
const cells = rows.get(number) ?? [];
|
|
1260
|
+
lines.push(Array.from({ length: width }, (_empty, column) => field(cells[column] ?? "")).join(","));
|
|
1261
|
+
}
|
|
1262
|
+
return new TextEncoder().encode(lines.length === 0 ? "" : `${lines.join("\n")}\n`);
|
|
1263
|
+
}
|
|
1264
|
+
//#endregion
|
|
1016
1265
|
//#region src/node/archive.ts
|
|
1017
1266
|
/**
|
|
1018
1267
|
* Whether an archive member is a macOS resource fork (`__MACOSX/` or an AppleDouble `._` file), which carries Finder metadata rather than data and never matches a selector.
|
|
@@ -1059,6 +1308,10 @@ function pick(paths, member) {
|
|
|
1059
1308
|
function unpack(bytes, archive) {
|
|
1060
1309
|
if (archive.kind === "none") return { bytes };
|
|
1061
1310
|
if (archive.kind === "gzip") return { bytes: gunzip(bytes) };
|
|
1311
|
+
if (archive.kind === "xlsx") return {
|
|
1312
|
+
bytes: sheetToCsv(bytes, archive.sheet),
|
|
1313
|
+
member: archive.sheet
|
|
1314
|
+
};
|
|
1062
1315
|
if (archive.kind === "zip") {
|
|
1063
1316
|
const names = [];
|
|
1064
1317
|
unzip(bytes, (file) => {
|
package/dist-cli/keepset.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { KeepsetError, NAME_SEPARATOR, countTarget, getDataset, listDatasets, li
|
|
|
4
4
|
import { fetchDataset, roundTripDataset } from "keepset/node";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
//#region package.json
|
|
7
|
-
var version = "0.
|
|
7
|
+
var version = "0.20.0";
|
|
8
8
|
const usage = `Usage: keepset <command> [options]
|
|
9
9
|
|
|
10
10
|
Commands:
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$defs":{"Crosswalk":{"$ref":"https://cdn.jsdelivr.net/npm/twindex@0.1.4/schemas/twindex.schema.json"},"Entry":{"additionalProperties":false,"properties":{"aliases":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*(?:\\/[A-Za-z0-9][A-Za-z0-9._-]*)*$","type":"string"},"minItems":1,"type":"array"},"category":{"enum":["graph/v1","or/v1"],"type":"string"},"citations":{"items":{"additionalProperties":false,"properties":{"doi":{"pattern":"^10\\.\\d{4,9}\\/\\S+$","type":"string"},"text":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["text"],"type":"object"},"type":"array"},"crosswalk":{"anyOf":[{"$ref":"#/$defs/Crosswalk"},{"additionalProperties":false,"properties":{"file":{"type":"string"}},"required":["file"],"type":"object"}]},"description":{"minLength":1,"type":"string"},"domain":{"minLength":1,"type":"string"},"expectedCounts":{"additionalProperties":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"licence":{"oneOf":[{"additionalProperties":false,"properties":{"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","type":"string"},"kind":{"const":"spdx","type":"string"}},"required":["kind","id"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"custom","type":"string"},"name":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["kind","name","url"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"unrecorded","type":"string"}},"required":["kind"],"type":"object"}]},"name":{"minLength":1,"type":"string"},"namespace":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"properties":{"additionalProperties":{"type":["string","number","boolean","null"]},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"recipe":{"oneOf":[{"additionalProperties":false,"properties":{"archive":{"oneOf":[{"additionalProperties":false,"properties":{"kind":{"const":"none","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"gzip","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"zip","type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"tar","type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"tar.gz","type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"}]},"paging":{"oneOf":[{"additionalProperties":false,"properties":{"kind":{"const":"arcgis-query","type":"string"},"orderBy":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","type":"string"}},"required":["kind","orderBy"],"type":"object"}]},"parser":{"oneOf":[{"additionalProperties":false,"properties":{"commentPrefixes":{"items":{"minLength":1,"type":"string"},"type":"array"},"delimiter":{"enum":["detect","whitespace","tab","comma","space"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"edge-list","type":"string"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"sourceColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"targetColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","commentPrefixes","sourceColumn","targetColumn"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"matrix-market","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"gml","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"pajek","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"delimiter":{"enum":["comma","tab","semicolon"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"csv-table","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","idColumn","numericColumns"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"minItems":1,"type":"array"},"dateColumns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"encoding":{"const":"utf-8","type":"string"},"format":{"const":"geojson","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"}},"required":["format","encoding","idColumn","numericColumns","dateColumns","columns"],"type":"object"}]},"source":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"status":{"const":"fetchable","type":"string"}},"required":["status","source","archive","parser"],"type":"object"},{"additionalProperties":false,"properties":{"reason":{"minLength":1,"type":"string"},"status":{"const":"not-fetchable","type":"string"}},"required":["status","reason"],"type":"object"}]},"sources":{"items":{"additionalProperties":false,"properties":{"retrieved":{"format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","type":"string"},"snapshot":{"additionalProperties":false,"properties":{"timestamp":{"pattern":"^\\d{8}(?:\\d{6})?$","type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","timestamp"],"type":"object"},"status":{"enum":["live","dead"],"type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","retrieved","status"],"type":"object"},"minItems":1,"type":"array"},"tags":{"items":{"minLength":1,"type":"string"},"type":"array"}},"required":["id","name","tags","category","sources","licence","citations","recipe","crosswalk","expectedCounts","properties"],"type":"object"}},"$id":"https://cdn.jsdelivr.net/npm/keepset@0.
|
|
1
|
+
{"$defs":{"Crosswalk":{"$ref":"https://cdn.jsdelivr.net/npm/twindex@0.1.4/schemas/twindex.schema.json"},"Entry":{"additionalProperties":false,"properties":{"aliases":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*(?:\\/[A-Za-z0-9][A-Za-z0-9._-]*)*$","type":"string"},"minItems":1,"type":"array"},"category":{"enum":["graph/v1","or/v1"],"type":"string"},"citations":{"items":{"additionalProperties":false,"properties":{"doi":{"pattern":"^10\\.\\d{4,9}\\/\\S+$","type":"string"},"text":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["text"],"type":"object"},"type":"array"},"crosswalk":{"anyOf":[{"$ref":"#/$defs/Crosswalk"},{"additionalProperties":false,"properties":{"file":{"type":"string"}},"required":["file"],"type":"object"}]},"description":{"minLength":1,"type":"string"},"domain":{"minLength":1,"type":"string"},"expectedCounts":{"additionalProperties":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"licence":{"oneOf":[{"additionalProperties":false,"properties":{"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","type":"string"},"kind":{"const":"spdx","type":"string"}},"required":["kind","id"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"custom","type":"string"},"name":{"minLength":1,"type":"string"},"url":{"format":"uri","type":"string"}},"required":["kind","name","url"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"unrecorded","type":"string"}},"required":["kind"],"type":"object"}]},"name":{"minLength":1,"type":"string"},"namespace":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"properties":{"additionalProperties":{"type":["string","number","boolean","null"]},"propertyNames":{"pattern":"^[A-Za-z][A-Za-z0-9]*$","type":"string"},"type":"object"},"recipe":{"oneOf":[{"additionalProperties":false,"properties":{"archive":{"oneOf":[{"additionalProperties":false,"properties":{"kind":{"const":"none","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"gzip","type":"string"}},"required":["kind"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"zip","type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"tar","type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"tar.gz","type":"string"},"member":{"anyOf":[{"additionalProperties":false,"properties":{"path":{"type":"string"}},"required":["path"],"type":"object"},{"additionalProperties":false,"properties":{"extensions":{"items":{"pattern":"^\\.[A-Za-z0-9][A-Za-z0-9.]*$","type":"string"},"minItems":1,"type":"array"}},"required":["extensions"],"type":"object"}]}},"required":["kind","member"],"type":"object"},{"additionalProperties":false,"properties":{"kind":{"const":"xlsx","type":"string"},"sheet":{"minLength":1,"type":"string"}},"required":["kind","sheet"],"type":"object"}]},"paging":{"oneOf":[{"additionalProperties":false,"properties":{"kind":{"const":"arcgis-query","type":"string"},"orderBy":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","type":"string"}},"required":["kind","orderBy"],"type":"object"}]},"parser":{"oneOf":[{"additionalProperties":false,"properties":{"commentPrefixes":{"items":{"minLength":1,"type":"string"},"type":"array"},"delimiter":{"enum":["detect","whitespace","tab","comma","space"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"edge-list","type":"string"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"sourceColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"targetColumn":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","commentPrefixes","sourceColumn","targetColumn"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"matrix-market","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"gml","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"pajek","type":"string"}},"required":["format","encoding"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"delimiter":{"enum":["comma","tab","semicolon"],"type":"string"},"encoding":{"enum":["utf-8","latin1"],"type":"string"},"format":{"const":"csv-table","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"},"skipLines":{"maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["format","encoding","delimiter","skipLines","idColumn","numericColumns"],"type":"object"},{"additionalProperties":false,"properties":{"columns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"minItems":1,"type":"array"},"dateColumns":{"items":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"type":"array"},"encoding":{"const":"utf-8","type":"string"},"format":{"const":"geojson","type":"string"},"idColumn":{"anyOf":[{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},{"type":"null"}]},"numericColumns":{"items":{"additionalProperties":false,"properties":{"column":{"pattern":"^[A-Za-z](?:[A-Za-z0-9_]*[A-Za-z0-9])?$","type":"string"},"factor":{"exclusiveMinimum":0,"type":"number"}},"required":["column","factor"],"type":"object"},"type":"array"}},"required":["format","encoding","idColumn","numericColumns","dateColumns","columns"],"type":"object"}]},"source":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"status":{"const":"fetchable","type":"string"}},"required":["status","source","archive","parser"],"type":"object"},{"additionalProperties":false,"properties":{"reason":{"minLength":1,"type":"string"},"status":{"const":"not-fetchable","type":"string"}},"required":["status","reason"],"type":"object"}]},"sources":{"items":{"additionalProperties":false,"properties":{"retrieved":{"format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","type":"string"},"snapshot":{"additionalProperties":false,"properties":{"timestamp":{"pattern":"^\\d{8}(?:\\d{6})?$","type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","timestamp"],"type":"object"},"status":{"enum":["live","dead"],"type":"string"},"url":{"format":"uri","type":"string"}},"required":["url","retrieved","status"],"type":"object"},"minItems":1,"type":"array"},"tags":{"items":{"minLength":1,"type":"string"},"type":"array"}},"required":["id","name","tags","category","sources","licence","citations","recipe","crosswalk","expectedCounts","properties"],"type":"object"}},"$id":"https://cdn.jsdelivr.net/npm/keepset@0.20.0/schemas/entry.schema.json","$ref":"#/$defs/Entry","$schema":"https://json-schema.org/draft/2020-12/schema"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$defs":{"Manifest":{"additionalProperties":false,"properties":{"description":{"minLength":1,"type":"string"},"id":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"lineage":{"additionalProperties":false,"properties":{"catalogue":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"repository":{"format":"uri","type":"string"},"version":{"pattern":"^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$","type":"string"}},"required":["catalogue","version","repository"],"type":"object"},"name":{"minLength":1,"type":"string"},"namespaceAliases":{"additionalProperties":{"items":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"minItems":1,"type":"array"},"propertyNames":{"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","type":"string"},"type":"object"},"repository":{"format":"uri","type":"string"},"version":{"pattern":"^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$","type":"string"}},"required":["id","name","description","version","repository"],"type":"object"}},"$id":"https://cdn.jsdelivr.net/npm/keepset@0.
|
|
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.20.0/schemas/manifest.schema.json","$ref":"#/$defs/Manifest","$schema":"https://json-schema.org/draft/2020-12/schema"}
|