keepset 0.0.4 → 0.1.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/index.d.ts CHANGED
@@ -1 +1,106 @@
1
- export {}
1
+ import { z } from "zod";
2
+ //#region src/targets/graph-v1.d.ts
3
+ /** A JSON scalar: the only values graph/v1 metadata may hold, so `meta` stays a flat record any consumer can tabulate. `z.number()` already rejects NaN and the infinities, which JSON cannot carry. */
4
+ export declare const JsonScalarSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>;
5
+ type JsonScalar = z.infer<typeof JsonScalarSchema>;
6
+ /** A node: an id, an optional type, and whatever else the dataset carries. The target fixes only what every graph consumer needs, so extra fields pass through untouched. */
7
+ export declare const GraphNodeSchema: z.ZodObject<{
8
+ id: z.ZodString;
9
+ type: z.ZodOptional<z.ZodString>;
10
+ }, z.core.$loose>;
11
+ type GraphNode = z.infer<typeof GraphNodeSchema>;
12
+ /** An edge: source and target node ids, an optional type, and whatever else the dataset carries. Direction is the dataset's own: graph/v1 records it in `meta`, never by duplicating edges. */
13
+ export declare const GraphEdgeSchema: z.ZodObject<{
14
+ source: z.ZodString;
15
+ target: z.ZodString;
16
+ type: z.ZodOptional<z.ZodString>;
17
+ }, z.core.$loose>;
18
+ type GraphEdge = z.infer<typeof GraphEdgeSchema>;
19
+ /**
20
+ * The graph/v1 normalisation target: `{ meta, nodes, edges }`, the shape the thesis pipeline's benchmark cache already uses. It describes the result `fetchDataset` resolves a graph entry to, not what a source file may contain. Beyond the field shapes it checks the two properties every consumer relies on: node ids are unique, and every edge endpoint names a node.
21
+ */
22
+ export declare const GraphV1Schema: z.ZodObject<{
23
+ meta: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
24
+ nodes: z.ZodArray<z.ZodObject<{
25
+ id: z.ZodString;
26
+ type: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$loose>>;
28
+ edges: z.ZodArray<z.ZodObject<{
29
+ source: z.ZodString;
30
+ target: z.ZodString;
31
+ type: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$loose>>;
33
+ }, z.core.$strict> & {
34
+ is: (value: unknown) => value is {
35
+ meta: Record<string, string | number | boolean | null>;
36
+ nodes: {
37
+ [x: string]: unknown;
38
+ id: string;
39
+ type?: string | undefined;
40
+ }[];
41
+ edges: {
42
+ [x: string]: unknown;
43
+ source: string;
44
+ target: string;
45
+ type?: string | undefined;
46
+ }[];
47
+ };
48
+ };
49
+ type GraphV1 = z.infer<typeof GraphV1Schema>;
50
+ //#endregion
51
+ //#region src/categories.d.ts
52
+ /** Every category id the registry knows. A category id is `<kind>/v<version>`: the kind of data, and the version of its normalisation target, so a breaking change to a target is a new category rather than an edit to an old one. */
53
+ export declare const CategoryIdSchema: z.ZodEnum<{
54
+ "graph/v1": "graph/v1";
55
+ }>;
56
+ type CategoryId = z.infer<typeof CategoryIdSchema>;
57
+ /** The normalisation target each category resolves to, keyed by category id. */
58
+ interface CategoryTargets {
59
+ readonly "graph/v1": GraphV1;
60
+ }
61
+ /** The normalised shape of any category: what `fetchDataset` can resolve to. */
62
+ type CategoryTarget = CategoryTargets[CategoryId];
63
+ /** One category: its id, what it holds, and the schema of its normalisation target. */
64
+ interface Category<Id extends CategoryId = CategoryId> {
65
+ readonly id: Id;
66
+ readonly description: string;
67
+ readonly target: z.ZodType<CategoryTargets[Id]>;
68
+ }
69
+ /**
70
+ * Looks up a category by id.
71
+ * @param id - Any string; category ids usually come from entry documents, so the argument is untrusted.
72
+ * @returns The category.
73
+ * @throws UnknownCategoryError when no category has that id.
74
+ */
75
+ export declare function getCategory<Id extends CategoryId>(id: Id): Category<Id>;
76
+ export declare function getCategory(id: string): Category;
77
+ /**
78
+ * Returns the schema of a category's normalisation target.
79
+ * @param id - Any string, as for `getCategory`.
80
+ * @returns The target schema.
81
+ * @throws UnknownCategoryError when no category has that id.
82
+ */
83
+ export declare function getTarget<Id extends CategoryId>(id: Id): z.ZodType<CategoryTargets[Id]>;
84
+ export declare function getTarget(id: string): z.ZodType<CategoryTarget>;
85
+ /**
86
+ * The kind of data a category holds: the part of its id before the version.
87
+ * @param id - A category id.
88
+ * @returns The kind, such as `graph`.
89
+ */
90
+ export declare function kindOf(id: CategoryId): string;
91
+ /** @returns Every registered category, in id order. */
92
+ export declare function listCategories(): readonly Category[];
93
+ //#endregion
94
+ //#region src/errors.d.ts
95
+ /** The root of this package's error taxonomy: every error keepset throws is a `KeepsetError`, so callers can catch the family with one check. */
96
+ export declare class KeepsetError extends Error {
97
+ constructor(message: string);
98
+ }
99
+ /** Thrown when a category id names no category in the registry. Carries the ids that do exist, so the message says what would have been accepted. */
100
+ export declare class UnknownCategoryError extends KeepsetError {
101
+ readonly categoryId: string;
102
+ readonly knownIds: readonly string[];
103
+ constructor(categoryId: string, knownIds: readonly string[]);
104
+ }
105
+ //#endregion
106
+ export type { Category, CategoryId, CategoryTarget, CategoryTargets, GraphEdge, GraphNode, GraphV1, JsonScalar };
package/dist/index.js CHANGED
@@ -0,0 +1,114 @@
1
+ import { z } from "zod";
2
+ //#region src/errors.ts
3
+ /** The root of this package's error taxonomy: every error keepset throws is a `KeepsetError`, so callers can catch the family with one check. */
4
+ var KeepsetError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = new.target.name;
8
+ }
9
+ };
10
+ /** Thrown when a category id names no category in the registry. Carries the ids that do exist, so the message says what would have been accepted. */
11
+ var UnknownCategoryError = class extends KeepsetError {
12
+ categoryId;
13
+ knownIds;
14
+ constructor(categoryId, knownIds) {
15
+ super(`unknown category ${JSON.stringify(categoryId)}: known categories are ${knownIds.join(", ")}`);
16
+ this.categoryId = categoryId;
17
+ this.knownIds = knownIds;
18
+ }
19
+ };
20
+ //#endregion
21
+ //#region src/define.ts
22
+ /**
23
+ * Attaches a type guard to a Zod schema (the schema-attached guard pattern: validation, inference and narrowing share one definition, so the three cannot drift). This module is isolated because it is the one place in the package that augments an object in place with Object.assign, which the house lint rule that eslint.config.ts turns off for this file alone would otherwise reject: object spread is no alternative, as it copies own properties only and drops the Zod prototype's parse/refine methods.
24
+ * @param schema - The schema to augment.
25
+ * @returns The same schema object, now also carrying `is`.
26
+ */
27
+ function define(schema) {
28
+ return Object.assign(schema, { is(value) {
29
+ return schema.safeParse(value).success;
30
+ } });
31
+ }
32
+ //#endregion
33
+ //#region src/targets/graph-v1.ts
34
+ /** A JSON scalar: the only values graph/v1 metadata may hold, so `meta` stays a flat record any consumer can tabulate. `z.number()` already rejects NaN and the infinities, which JSON cannot carry. */
35
+ const JsonScalarSchema = z.union([
36
+ z.string(),
37
+ z.number(),
38
+ z.boolean(),
39
+ z.null()
40
+ ]);
41
+ /** A node: an id, an optional type, and whatever else the dataset carries. The target fixes only what every graph consumer needs, so extra fields pass through untouched. */
42
+ const GraphNodeSchema = z.looseObject({
43
+ id: z.string().min(1),
44
+ type: z.string().min(1).optional()
45
+ });
46
+ /** An edge: source and target node ids, an optional type, and whatever else the dataset carries. Direction is the dataset's own: graph/v1 records it in `meta`, never by duplicating edges. */
47
+ const GraphEdgeSchema = z.looseObject({
48
+ source: z.string().min(1),
49
+ target: z.string().min(1),
50
+ type: z.string().min(1).optional()
51
+ });
52
+ /**
53
+ * The graph/v1 normalisation target: `{ meta, nodes, edges }`, the shape the thesis pipeline's benchmark cache already uses. It describes the result `fetchDataset` resolves a graph entry to, not what a source file may contain. Beyond the field shapes it checks the two properties every consumer relies on: node ids are unique, and every edge endpoint names a node.
54
+ */
55
+ const GraphV1Schema = define(z.object({
56
+ meta: z.record(z.string(), JsonScalarSchema),
57
+ nodes: z.array(GraphNodeSchema),
58
+ edges: z.array(GraphEdgeSchema)
59
+ }).strict().superRefine((graph, ctx) => {
60
+ const ids = /* @__PURE__ */ new Set();
61
+ for (const [index, node] of graph.nodes.entries()) {
62
+ if (ids.has(node.id)) ctx.addIssue({
63
+ code: "custom",
64
+ path: [
65
+ "nodes",
66
+ index,
67
+ "id"
68
+ ],
69
+ message: `duplicate node id ${JSON.stringify(node.id)}`
70
+ });
71
+ ids.add(node.id);
72
+ }
73
+ for (const [index, edge] of graph.edges.entries()) for (const end of ["source", "target"]) if (!ids.has(edge[end])) ctx.addIssue({
74
+ code: "custom",
75
+ path: [
76
+ "edges",
77
+ index,
78
+ end
79
+ ],
80
+ message: `edge ${end} ${JSON.stringify(edge[end])} names no node`
81
+ });
82
+ }));
83
+ //#endregion
84
+ //#region src/categories.ts
85
+ /** Every category id the registry knows. A category id is `<kind>/v<version>`: the kind of data, and the version of its normalisation target, so a breaking change to a target is a new category rather than an edit to an old one. */
86
+ const CategoryIdSchema = z.enum(["graph/v1"]);
87
+ /** The registry. Adding a category is adding its id to `CategoryIdSchema` and its entry here, with its target schema beside graph/v1's in src/targets/: the mapped type makes the compiler reject a registry that misses an id or has one too many. */
88
+ const registry = { "graph/v1": {
89
+ id: "graph/v1",
90
+ description: "A graph as { meta, nodes, edges }: a flat record of scalar metadata, nodes carrying at least an id, and edges carrying at least source and target node ids, with optional node and edge types.",
91
+ target: GraphV1Schema
92
+ } };
93
+ function getCategory(id) {
94
+ const parsed = CategoryIdSchema.safeParse(id);
95
+ if (!parsed.success) throw new UnknownCategoryError(id, CategoryIdSchema.options);
96
+ return registry[parsed.data];
97
+ }
98
+ function getTarget(id) {
99
+ return getCategory(id).target;
100
+ }
101
+ /**
102
+ * The kind of data a category holds: the part of its id before the version.
103
+ * @param id - A category id.
104
+ * @returns The kind, such as `graph`.
105
+ */
106
+ function kindOf(id) {
107
+ return id.slice(0, id.indexOf("/"));
108
+ }
109
+ /** @returns Every registered category, in id order. */
110
+ function listCategories() {
111
+ return CategoryIdSchema.options.map((id) => getCategory(id));
112
+ }
113
+ //#endregion
114
+ export { CategoryIdSchema, GraphEdgeSchema, GraphNodeSchema, GraphV1Schema, JsonScalarSchema, KeepsetError, UnknownCategoryError, getCategory, getTarget, kindOf, listCategories };
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.0.4",
4
+ "version": "0.1.0",
5
5
  "dependencies": {
6
6
  "zod": "4.6.6"
7
7
  },
@@ -71,7 +71,7 @@
71
71
  "prepare": "husky",
72
72
  "prepublishOnly": "pnpm run lint:check && pnpm run typecheck && pnpm run test && pnpm run build && publint && attw --pack --profile esm-only",
73
73
  "test": "vitest run",
74
- "typecheck": "tsc -p tsconfig.json"
74
+ "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.node.json"
75
75
  },
76
76
  "sideEffects": false,
77
77
  "type": "module",