@fabricorg/experiments-datasets 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/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # @fabricorg/experiments-datasets
2
+
3
+ Versioned, tenant-aware JSON datasets for Fabric Experiments AI Quality suites.
4
+
5
+ ```ts
6
+ import { InMemoryDatasetStore, parseJsonl } from '@fabricorg/experiments-datasets';
7
+ ```
8
+
9
+ The package provides schemas, deterministic JSONL import, content hashing, a
10
+ storage contract, and an in-memory reference store. Frozen versions own their
11
+ values and cannot be changed by mutating caller or returned objects.
12
+
13
+ See [experiments.fabric.pro/docs/ai-quality/overview](https://experiments.fabric.pro/docs/ai-quality/overview).
package/dist/index.cjs ADDED
@@ -0,0 +1,198 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var crypto = require('crypto');
5
+
6
+ // src/schema.ts
7
+ var JsonValue = zod.z.lazy(
8
+ () => zod.z.union([
9
+ zod.z.null(),
10
+ zod.z.boolean(),
11
+ zod.z.number().finite(),
12
+ zod.z.string(),
13
+ zod.z.array(JsonValue),
14
+ zod.z.record(JsonValue)
15
+ ])
16
+ );
17
+ var DatasetId = zod.z.string().min(2).max(64).regex(/^[a-z][a-z0-9-]*$/, "lowercase kebab-case, must start with a letter");
18
+ var DatasetSplit = zod.z.enum(["train", "test", "validation"]);
19
+ var DatasetExample = zod.z.object({
20
+ exampleId: zod.z.string().uuid(),
21
+ input: JsonValue,
22
+ /** Reference/expected output; optional — judges without ground truth ignore it. */
23
+ expectedOutput: JsonValue.optional(),
24
+ metadata: zod.z.record(JsonValue).default({}),
25
+ split: DatasetSplit.default("test")
26
+ }).strict();
27
+ var DatasetSpec = zod.z.object({
28
+ id: DatasetId,
29
+ name: zod.z.string().min(1).max(120),
30
+ description: zod.z.string().max(2e3).optional()
31
+ });
32
+ var DatasetVersion = zod.z.object({
33
+ datasetId: DatasetId,
34
+ version: zod.z.number().int().min(1),
35
+ exampleCount: zod.z.number().int().min(0),
36
+ /** SHA-256 over the canonical JSON of sorted example ids + example hashes. */
37
+ contentHash: zod.z.string().regex(/^[a-f0-9]{64}$/),
38
+ createdAtIso: zod.z.string().datetime(),
39
+ note: zod.z.string().max(500).optional()
40
+ });
41
+ var Dataset = DatasetSpec.extend({
42
+ tenantId: zod.z.string().min(1),
43
+ latestVersion: zod.z.number().int().min(0),
44
+ exampleCount: zod.z.number().int().min(0),
45
+ createdAtIso: zod.z.string().datetime(),
46
+ updatedAtIso: zod.z.string().datetime()
47
+ });
48
+ function canonicalJson(value) {
49
+ return JSON.stringify(sortValue(value));
50
+ }
51
+ function sortValue(value) {
52
+ if (Array.isArray(value)) return value.map(sortValue);
53
+ if (value !== null && typeof value === "object") {
54
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
55
+ return Object.fromEntries(entries.map(([k, v]) => [k, sortValue(v)]));
56
+ }
57
+ return value;
58
+ }
59
+ function sha256Hex(input) {
60
+ return crypto.createHash("sha256").update(input, "utf8").digest("hex");
61
+ }
62
+
63
+ // src/memory.ts
64
+ var InMemoryDatasetStore = class {
65
+ byTenant = /* @__PURE__ */ new Map();
66
+ tenant(tenantId) {
67
+ let m = this.byTenant.get(tenantId);
68
+ if (!m) {
69
+ m = /* @__PURE__ */ new Map();
70
+ this.byTenant.set(tenantId, m);
71
+ }
72
+ return m;
73
+ }
74
+ async create(tenantId, spec, nowIso) {
75
+ const m = this.tenant(tenantId);
76
+ if (m.has(spec.id)) throw new Error(`dataset already exists: ${spec.id}`);
77
+ const dataset = {
78
+ ...spec,
79
+ tenantId,
80
+ latestVersion: 0,
81
+ exampleCount: 0,
82
+ createdAtIso: nowIso,
83
+ updatedAtIso: nowIso
84
+ };
85
+ m.set(spec.id, { dataset, examples: [], versions: [] });
86
+ return clone(dataset);
87
+ }
88
+ async get(tenantId, datasetId) {
89
+ const dataset = this.tenant(tenantId).get(datasetId)?.dataset;
90
+ return dataset ? clone(dataset) : null;
91
+ }
92
+ async list(tenantId) {
93
+ return [...this.tenant(tenantId).values()].map((s) => clone(s.dataset));
94
+ }
95
+ async addExamples(tenantId, datasetId, examples, nowIso) {
96
+ const stored = this.require(tenantId, datasetId);
97
+ const validated = examples.map((example) => DatasetExample.parse(example));
98
+ const ids = new Set(stored.examples.map((e) => e.exampleId));
99
+ for (const e of validated) {
100
+ if (ids.has(e.exampleId)) throw new Error(`duplicate exampleId: ${e.exampleId}`);
101
+ ids.add(e.exampleId);
102
+ }
103
+ stored.examples.push(...validated.map(clone));
104
+ stored.dataset = {
105
+ ...stored.dataset,
106
+ exampleCount: stored.examples.length,
107
+ updatedAtIso: nowIso
108
+ };
109
+ return validated.map(clone);
110
+ }
111
+ async listExamples(query) {
112
+ const stored = this.require(query.tenantId, query.datasetId);
113
+ let pool = stored.examples;
114
+ if (query.version !== void 0) {
115
+ const v = stored.versions.find((x) => x.version === query.version);
116
+ if (!v) throw new Error(`unknown version ${query.version} for dataset ${query.datasetId}`);
117
+ pool = pool.slice(0, v.frozenCount);
118
+ }
119
+ if (query.split) pool = pool.filter((e) => e.split === query.split);
120
+ const offset = query.offset ?? 0;
121
+ const limit = query.limit ?? 100;
122
+ return { examples: pool.slice(offset, offset + limit).map(clone), total: pool.length };
123
+ }
124
+ async createVersion(tenantId, datasetId, nowIso, note) {
125
+ const stored = this.require(tenantId, datasetId);
126
+ const frozenCount = stored.examples.length;
127
+ const contentHash = hashExamples(stored.examples);
128
+ const version = {
129
+ datasetId,
130
+ version: stored.dataset.latestVersion + 1,
131
+ exampleCount: frozenCount,
132
+ contentHash,
133
+ createdAtIso: nowIso,
134
+ ...note === void 0 ? {} : { note },
135
+ frozenCount
136
+ };
137
+ stored.versions.push(version);
138
+ stored.dataset = { ...stored.dataset, latestVersion: version.version, updatedAtIso: nowIso };
139
+ const { frozenCount: _drop, ...pub } = version;
140
+ return clone(pub);
141
+ }
142
+ async listVersions(tenantId, datasetId) {
143
+ return this.require(tenantId, datasetId).versions.map(
144
+ ({ frozenCount: _drop, ...v }) => clone(v)
145
+ );
146
+ }
147
+ require(tenantId, datasetId) {
148
+ const stored = this.tenant(tenantId).get(datasetId);
149
+ if (!stored) throw new Error(`unknown dataset: ${datasetId}`);
150
+ return stored;
151
+ }
152
+ };
153
+ function hashExamples(examples) {
154
+ const entries = examples.map((e) => [e.exampleId, sha256Hex(canonicalJson(e))]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
155
+ return sha256Hex(canonicalJson(entries));
156
+ }
157
+ function clone(value) {
158
+ return structuredClone(value);
159
+ }
160
+
161
+ // src/import.ts
162
+ function parseJsonl(text, makeId) {
163
+ const examples = [];
164
+ const errors = [];
165
+ const lines = text.split("\n");
166
+ for (let i = 0; i < lines.length; i++) {
167
+ const raw = lines[i].trim();
168
+ if (raw === "") continue;
169
+ let parsed;
170
+ try {
171
+ parsed = JSON.parse(raw);
172
+ } catch (e) {
173
+ errors.push({ line: i + 1, message: `invalid JSON: ${e.message}` });
174
+ continue;
175
+ }
176
+ const candidate = parsed !== null && typeof parsed === "object" && !("exampleId" in parsed) ? { ...parsed, exampleId: makeId(i + 1) } : parsed;
177
+ const result = DatasetExample.safeParse(candidate);
178
+ if (result.success) examples.push(result.data);
179
+ else
180
+ errors.push({ line: i + 1, message: result.error.issues[0]?.message ?? "invalid example" });
181
+ }
182
+ return { examples, errors };
183
+ }
184
+
185
+ exports.Dataset = Dataset;
186
+ exports.DatasetExample = DatasetExample;
187
+ exports.DatasetId = DatasetId;
188
+ exports.DatasetSpec = DatasetSpec;
189
+ exports.DatasetSplit = DatasetSplit;
190
+ exports.DatasetVersion = DatasetVersion;
191
+ exports.InMemoryDatasetStore = InMemoryDatasetStore;
192
+ exports.JsonValue = JsonValue;
193
+ exports.canonicalJson = canonicalJson;
194
+ exports.hashExamples = hashExamples;
195
+ exports.parseJsonl = parseJsonl;
196
+ exports.sha256Hex = sha256Hex;
197
+ //# sourceMappingURL=index.cjs.map
198
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts","../src/hash.ts","../src/memory.ts","../src/import.ts"],"names":["z","createHash"],"mappings":";;;;;;AAiBO,IAAM,YAAkCA,KAAA,CAAE,IAAA;AAAA,EAAK,MACpDA,MAAE,KAAA,CAAM;AAAA,IACNA,MAAE,IAAA,EAAK;AAAA,IACPA,MAAE,OAAA,EAAQ;AAAA,IACVA,KAAA,CAAE,MAAA,EAAO,CAAE,MAAA,EAAO;AAAA,IAClBA,MAAE,MAAA,EAAO;AAAA,IACTA,KAAA,CAAE,MAAM,SAAS,CAAA;AAAA,IACjBA,KAAA,CAAE,OAAO,SAAS;AAAA,GACnB;AACH;AAEO,IAAM,SAAA,GAAYA,KAAA,CACtB,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,GAAA,CAAI,EAAE,CAAA,CACN,KAAA,CAAM,mBAAA,EAAqB,gDAAgD;AAGvE,IAAM,eAAeA,KAAA,CAAE,IAAA,CAAK,CAAC,OAAA,EAAS,MAAA,EAAQ,YAAY,CAAC;AAS3D,IAAM,cAAA,GAAiBA,MAC3B,MAAA,CAAO;AAAA,EACN,SAAA,EAAWA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAA,EAAK;AAAA,EAC3B,KAAA,EAAO,SAAA;AAAA;AAAA,EAEP,cAAA,EAAgB,UAAU,QAAA,EAAS;AAAA,EACnC,UAAUA,KAAA,CAAE,MAAA,CAAO,SAAS,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACxC,KAAA,EAAO,YAAA,CAAa,OAAA,CAAQ,MAAM;AACpC,CAAC,EACA,MAAA;AAGI,IAAM,WAAA,GAAcA,MAAE,MAAA,CAAO;AAAA,EAClC,EAAA,EAAI,SAAA;AAAA,EACJ,IAAA,EAAMA,MAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,IAAI,GAAG,CAAA;AAAA,EAC/B,aAAaA,KAAA,CAAE,MAAA,GAAS,GAAA,CAAI,GAAI,EAAE,QAAA;AACpC,CAAC;AASM,IAAM,cAAA,GAAiBA,MAAE,MAAA,CAAO;AAAA,EACrC,SAAA,EAAW,SAAA;AAAA,EACX,SAASA,KAAA,CAAE,MAAA,GAAS,GAAA,EAAI,CAAE,IAAI,CAAC,CAAA;AAAA,EAC/B,cAAcA,KAAA,CAAE,MAAA,GAAS,GAAA,EAAI,CAAE,IAAI,CAAC,CAAA;AAAA;AAAA,EAEpC,WAAA,EAAaA,KAAA,CAAE,MAAA,EAAO,CAAE,MAAM,gBAAgB,CAAA;AAAA,EAC9C,YAAA,EAAcA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAClC,MAAMA,KAAA,CAAE,MAAA,GAAS,GAAA,CAAI,GAAG,EAAE,QAAA;AAC5B,CAAC;AAGM,IAAM,OAAA,GAAU,YAAY,MAAA,CAAO;AAAA,EACxC,QAAA,EAAUA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC1B,eAAeA,KAAA,CAAE,MAAA,GAAS,GAAA,EAAI,CAAE,IAAI,CAAC,CAAA;AAAA,EACrC,cAAcA,KAAA,CAAE,MAAA,GAAS,GAAA,EAAI,CAAE,IAAI,CAAC,CAAA;AAAA,EACpC,YAAA,EAAcA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAClC,YAAA,EAAcA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAC3B,CAAC;ACnFM,SAAS,cAAc,KAAA,EAAwB;AACpD,EAAA,OAAO,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,KAAK,CAAC,CAAA;AACxC;AAEA,SAAS,UAAU,KAAA,EAAyB;AAC1C,EAAA,IAAI,MAAM,OAAA,CAAQ,KAAK,GAAG,OAAO,KAAA,CAAM,IAAI,SAAS,CAAA;AACpD,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,EAAU;AAC/C,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,KAAgC,CAAA,CAC5D,MAAA,CAAO,CAAC,GAAG,CAAC,CAAA,KAAM,CAAA,KAAM,MAAS,CAAA,CACjC,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA,EAAG,CAAC,CAAC,CAAA,KAAO,CAAA,GAAI,CAAA,GAAI,EAAA,GAAK,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAE,CAAA;AAClD,IAAA,OAAO,MAAA,CAAO,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,CAAC,CAAA,EAAG,SAAA,CAAU,CAAC,CAAC,CAAC,CAAC,CAAA;AAAA,EACtE;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,UAAU,KAAA,EAAuB;AAC/C,EAAA,OAAOC,iBAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,OAAO,MAAM,CAAA,CAAE,OAAO,KAAK,CAAA;AAChE;;;ACRO,IAAM,uBAAN,MAAmD;AAAA,EAChD,QAAA,uBAAe,GAAA,EAAwC;AAAA,EAEvD,OAAO,QAAA,EAA8C;AAC3D,IAAA,IAAI,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,QAAQ,CAAA;AAClC,IAAA,IAAI,CAAC,CAAA,EAAG;AACN,MAAA,CAAA,uBAAQ,GAAA,EAAI;AACZ,MAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,QAAA,EAAU,CAAC,CAAA;AAAA,IAC/B;AACA,IAAA,OAAO,CAAA;AAAA,EACT;AAAA,EAEA,MAAM,MAAA,CAAO,QAAA,EAAkB,IAAA,EAAmB,MAAA,EAAkC;AAClF,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA;AAC9B,IAAA,IAAI,CAAA,CAAE,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,IAAA,CAAK,EAAE,CAAA,CAAE,CAAA;AACxE,IAAA,MAAM,OAAA,GAAmB;AAAA,MACvB,GAAG,IAAA;AAAA,MACH,QAAA;AAAA,MACA,aAAA,EAAe,CAAA;AAAA,MACf,YAAA,EAAc,CAAA;AAAA,MACd,YAAA,EAAc,MAAA;AAAA,MACd,YAAA,EAAc;AAAA,KAChB;AACA,IAAA,CAAA,CAAE,GAAA,CAAI,IAAA,CAAK,EAAA,EAAI,EAAE,OAAA,EAAS,QAAA,EAAU,EAAC,EAAG,QAAA,EAAU,EAAC,EAAG,CAAA;AACtD,IAAA,OAAO,MAAM,OAAO,CAAA;AAAA,EACtB;AAAA,EAEA,MAAM,GAAA,CAAI,QAAA,EAAkB,SAAA,EAA4C;AACtE,IAAA,MAAM,UAAU,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA,CAAE,GAAA,CAAI,SAAS,CAAA,EAAG,OAAA;AACtD,IAAA,OAAO,OAAA,GAAU,KAAA,CAAM,OAAO,CAAA,GAAI,IAAA;AAAA,EACpC;AAAA,EAEA,MAAM,KAAK,QAAA,EAAsC;AAC/C,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA,CAAE,MAAA,EAAQ,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,KAAA,CAAM,CAAA,CAAE,OAAO,CAAC,CAAA;AAAA,EACxE;AAAA,EAEA,MAAM,WAAA,CACJ,QAAA,EACA,SAAA,EACA,UACA,MAAA,EAC2B;AAC3B,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,SAAS,CAAA;AAC/C,IAAA,MAAM,SAAA,GAAY,SAAS,GAAA,CAAI,CAAC,YAAY,cAAA,CAAe,KAAA,CAAM,OAAO,CAAC,CAAA;AACzE,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,MAAA,CAAO,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,CAAC,CAAA;AAC3D,IAAA,KAAA,MAAW,KAAK,SAAA,EAAW;AACzB,MAAA,IAAI,GAAA,CAAI,GAAA,CAAI,CAAA,CAAE,SAAS,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,CAAA,qBAAA,EAAwB,CAAA,CAAE,SAAS,CAAA,CAAE,CAAA;AAC/E,MAAA,GAAA,CAAI,GAAA,CAAI,EAAE,SAAS,CAAA;AAAA,IACrB;AAGA,IAAA,MAAA,CAAO,SAAS,IAAA,CAAK,GAAG,SAAA,CAAU,GAAA,CAAI,KAAK,CAAC,CAAA;AAC5C,IAAA,MAAA,CAAO,OAAA,GAAU;AAAA,MACf,GAAG,MAAA,CAAO,OAAA;AAAA,MACV,YAAA,EAAc,OAAO,QAAA,CAAS,MAAA;AAAA,MAC9B,YAAA,EAAc;AAAA,KAChB;AACA,IAAA,OAAO,SAAA,CAAU,IAAI,KAAK,CAAA;AAAA,EAC5B;AAAA,EAEA,MAAM,aAAa,KAAA,EAAgD;AACjE,IAAA,MAAM,SAAS,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,QAAA,EAAU,MAAM,SAAS,CAAA;AAC3D,IAAA,IAAI,OAAO,MAAA,CAAO,QAAA;AAClB,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,EAAW;AAC/B,MAAA,MAAM,CAAA,GAAI,OAAO,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,KAAA,CAAM,OAAO,CAAA;AACjE,MAAA,IAAI,CAAC,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,CAAA,gBAAA,EAAmB,KAAA,CAAM,OAAO,CAAA,aAAA,EAAgB,KAAA,CAAM,SAAS,CAAA,CAAE,CAAA;AACzF,MAAA,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAA,CAAE,WAAW,CAAA;AAAA,IACpC;AACA,IAAA,IAAI,KAAA,CAAM,KAAA,EAAO,IAAA,GAAO,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,KAAA,KAAU,KAAA,CAAM,KAAK,CAAA;AAClE,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,CAAA;AAC/B,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,IAAS,GAAA;AAC7B,IAAA,OAAO,EAAE,QAAA,EAAU,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA,CAAE,GAAA,CAAI,KAAK,CAAA,EAAG,KAAA,EAAO,KAAK,MAAA,EAAO;AAAA,EACvF;AAAA,EAEA,MAAM,aAAA,CACJ,QAAA,EACA,SAAA,EACA,QACA,IAAA,EACyB;AACzB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,SAAS,CAAA;AAC/C,IAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,MAAA;AACpC,IAAA,MAAM,WAAA,GAAc,YAAA,CAAa,MAAA,CAAO,QAAQ,CAAA;AAChD,IAAA,MAAM,OAAA,GAAoD;AAAA,MACxD,SAAA;AAAA,MACA,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,aAAA,GAAgB,CAAA;AAAA,MACxC,YAAA,EAAc,WAAA;AAAA,MACd,WAAA;AAAA,MACA,YAAA,EAAc,MAAA;AAAA,MACd,GAAI,IAAA,KAAS,MAAA,GAAY,EAAC,GAAI,EAAE,IAAA,EAAK;AAAA,MACrC;AAAA,KACF;AACA,IAAA,MAAA,CAAO,QAAA,CAAS,KAAK,OAAO,CAAA;AAC5B,IAAA,MAAA,CAAO,OAAA,GAAU,EAAE,GAAG,MAAA,CAAO,SAAS,aAAA,EAAe,OAAA,CAAQ,OAAA,EAAS,YAAA,EAAc,MAAA,EAAO;AAC3F,IAAA,MAAM,EAAE,WAAA,EAAa,KAAA,EAAO,GAAG,KAAI,GAAI,OAAA;AACvC,IAAA,OAAO,MAAM,GAAG,CAAA;AAAA,EAClB;AAAA,EAEA,MAAM,YAAA,CAAa,QAAA,EAAkB,SAAA,EAA8C;AACjF,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,SAAS,EAAE,QAAA,CAAS,GAAA;AAAA,MAAI,CAAC,EAAE,WAAA,EAAa,KAAA,EAAO,GAAG,CAAA,EAAE,KAChF,MAAM,CAAC;AAAA,KACT;AAAA,EACF;AAAA,EAEQ,OAAA,CAAQ,UAAkB,SAAA,EAAkC;AAClE,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAI,SAAS,CAAA;AAClD,IAAA,IAAI,CAAC,MAAA,EAAQ,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAE,CAAA;AAC5D,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAGO,SAAS,aAAa,QAAA,EAA6C;AACxE,EAAA,MAAM,OAAA,GAAU,QAAA,CACb,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,SAAA,EAAW,SAAA,CAAU,aAAA,CAAc,CAAC,CAAC,CAAC,CAAU,CAAA,CAC9D,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA,EAAG,CAAC,CAAC,CAAA,KAAO,CAAA,GAAI,CAAA,GAAI,EAAA,GAAK,CAAA,GAAI,CAAA,GAAI,IAAI,CAAE,CAAA;AAClD,EAAA,OAAO,SAAA,CAAU,aAAA,CAAc,OAAO,CAAC,CAAA;AACzC;AAEA,SAAS,MAAS,KAAA,EAAa;AAC7B,EAAA,OAAO,gBAAgB,KAAK,CAAA;AAC9B;;;ACxHO,SAAS,UAAA,CAAW,MAAc,MAAA,EAAgD;AACvF,EAAA,MAAM,WAA6B,EAAC;AACpC,EAAA,MAAM,SAAiC,EAAC;AACxC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC7B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,CAAC,CAAA,CAAG,IAAA,EAAK;AAC3B,IAAA,IAAI,QAAQ,EAAA,EAAI;AAChB,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,IACzB,SAAS,CAAA,EAAG;AACV,MAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAM,CAAA,GAAI,CAAA,EAAG,SAAS,CAAA,cAAA,EAAkB,CAAA,CAAY,OAAO,CAAA,CAAA,EAAI,CAAA;AAC7E,MAAA;AAAA,IACF;AACA,IAAA,MAAM,YACJ,MAAA,KAAW,IAAA,IAAQ,OAAO,MAAA,KAAW,YAAY,EAAE,WAAA,IAAgB,MAAA,CAAA,GAC/D,EAAE,GAAI,MAAA,EAAmB,SAAA,EAAW,OAAO,CAAA,GAAI,CAAC,GAAE,GAClD,MAAA;AACN,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,SAAA,CAAU,SAAS,CAAA;AACjD,IAAA,IAAI,MAAA,CAAO,OAAA,EAAS,QAAA,CAAS,IAAA,CAAK,OAAO,IAAI,CAAA;AAAA;AAE3C,MAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAM,CAAA,GAAI,CAAA,EAAG,OAAA,EAAS,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,EAAG,OAAA,IAAW,mBAAmB,CAAA;AAAA,EAC9F;AACA,EAAA,OAAO,EAAE,UAAU,MAAA,EAAO;AAC5B","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/** JSON-compatible values accepted by public dataset and prompt surfaces. */\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/**\n * `z.unknown()` accepts `undefined`, which makes an object field effectively\n * optional and also admits values that cannot survive JSONL/Delta storage.\n * Datasets are durable evidence, so their public shape is deliberately the\n * portable JSON subset.\n */\nexport const JsonValue: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.null(),\n z.boolean(),\n z.number().finite(),\n z.string(),\n z.array(JsonValue),\n z.record(JsonValue),\n ]),\n);\n\nexport const DatasetId = z\n .string()\n .min(2)\n .max(64)\n .regex(/^[a-z][a-z0-9-]*$/, 'lowercase kebab-case, must start with a letter');\nexport type DatasetId = z.infer<typeof DatasetId>;\n\nexport const DatasetSplit = z.enum(['train', 'test', 'validation']);\nexport type DatasetSplit = z.infer<typeof DatasetSplit>;\n\n/**\n * A single evaluation example. `input`/`output` are open JSON values so the\n * same shape carries chat transcripts, RAG tuples, or plain string pairs —\n * mirroring OpenInference conventions so trace-derived examples (M7) import\n * without transformation.\n */\nexport const DatasetExample = z\n .object({\n exampleId: z.string().uuid(),\n input: JsonValue,\n /** Reference/expected output; optional — judges without ground truth ignore it. */\n expectedOutput: JsonValue.optional(),\n metadata: z.record(JsonValue).default({}),\n split: DatasetSplit.default('test'),\n })\n .strict();\nexport type DatasetExample = z.infer<typeof DatasetExample>;\n\nexport const DatasetSpec = z.object({\n id: DatasetId,\n name: z.string().min(1).max(120),\n description: z.string().max(2000).optional(),\n});\nexport type DatasetSpec = z.infer<typeof DatasetSpec>;\n\n/**\n * Immutable version pointer. Examples are append-only; a version freezes the\n * set of example ids visible at creation time and records a content hash so\n * two versions with identical membership are provably identical (same\n * content-addressing discipline as the edge manifest).\n */\nexport const DatasetVersion = z.object({\n datasetId: DatasetId,\n version: z.number().int().min(1),\n exampleCount: z.number().int().min(0),\n /** SHA-256 over the canonical JSON of sorted example ids + example hashes. */\n contentHash: z.string().regex(/^[a-f0-9]{64}$/),\n createdAtIso: z.string().datetime(),\n note: z.string().max(500).optional(),\n});\nexport type DatasetVersion = z.infer<typeof DatasetVersion>;\n\nexport const Dataset = DatasetSpec.extend({\n tenantId: z.string().min(1),\n latestVersion: z.number().int().min(0),\n exampleCount: z.number().int().min(0),\n createdAtIso: z.string().datetime(),\n updatedAtIso: z.string().datetime(),\n});\nexport type Dataset = z.infer<typeof Dataset>;\n","import { createHash } from 'node:crypto';\n\n/** Deterministic JSON: object keys sorted recursively, arrays kept in order. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortValue);\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return Object.fromEntries(entries.map(([k, v]) => [k, sortValue(v)]));\n }\n return value;\n}\n\nexport function sha256Hex(input: string): string {\n return createHash('sha256').update(input, 'utf8').digest('hex');\n}\n","import { canonicalJson, sha256Hex } from './hash.js';\nimport { type Dataset, DatasetExample, type DatasetSpec, type DatasetVersion } from './schema.js';\nimport type { DatasetStore, ExamplePage, ListExamplesQuery } from './store.js';\n\ninterface StoredDataset {\n dataset: Dataset;\n /** Append-only example log; versions freeze a prefix of this log. */\n examples: DatasetExample[];\n versions: Array<DatasetVersion & { frozenCount: number }>;\n}\n\n/** Reference implementation: unit tests, `fx dev`, and harness fixtures. */\nexport class InMemoryDatasetStore implements DatasetStore {\n private byTenant = new Map<string, Map<string, StoredDataset>>();\n\n private tenant(tenantId: string): Map<string, StoredDataset> {\n let m = this.byTenant.get(tenantId);\n if (!m) {\n m = new Map();\n this.byTenant.set(tenantId, m);\n }\n return m;\n }\n\n async create(tenantId: string, spec: DatasetSpec, nowIso: string): Promise<Dataset> {\n const m = this.tenant(tenantId);\n if (m.has(spec.id)) throw new Error(`dataset already exists: ${spec.id}`);\n const dataset: Dataset = {\n ...spec,\n tenantId,\n latestVersion: 0,\n exampleCount: 0,\n createdAtIso: nowIso,\n updatedAtIso: nowIso,\n };\n m.set(spec.id, { dataset, examples: [], versions: [] });\n return clone(dataset);\n }\n\n async get(tenantId: string, datasetId: string): Promise<Dataset | null> {\n const dataset = this.tenant(tenantId).get(datasetId)?.dataset;\n return dataset ? clone(dataset) : null;\n }\n\n async list(tenantId: string): Promise<Dataset[]> {\n return [...this.tenant(tenantId).values()].map((s) => clone(s.dataset));\n }\n\n async addExamples(\n tenantId: string,\n datasetId: string,\n examples: readonly DatasetExample[],\n nowIso: string,\n ): Promise<DatasetExample[]> {\n const stored = this.require(tenantId, datasetId);\n const validated = examples.map((example) => DatasetExample.parse(example));\n const ids = new Set(stored.examples.map((e) => e.exampleId));\n for (const e of validated) {\n if (ids.has(e.exampleId)) throw new Error(`duplicate exampleId: ${e.exampleId}`);\n ids.add(e.exampleId);\n }\n // Keep an owned copy. Callers may mutate the object they passed after this\n // method returns; a frozen version must never change as a side effect.\n stored.examples.push(...validated.map(clone));\n stored.dataset = {\n ...stored.dataset,\n exampleCount: stored.examples.length,\n updatedAtIso: nowIso,\n };\n return validated.map(clone);\n }\n\n async listExamples(query: ListExamplesQuery): Promise<ExamplePage> {\n const stored = this.require(query.tenantId, query.datasetId);\n let pool = stored.examples;\n if (query.version !== undefined) {\n const v = stored.versions.find((x) => x.version === query.version);\n if (!v) throw new Error(`unknown version ${query.version} for dataset ${query.datasetId}`);\n pool = pool.slice(0, v.frozenCount);\n }\n if (query.split) pool = pool.filter((e) => e.split === query.split);\n const offset = query.offset ?? 0;\n const limit = query.limit ?? 100;\n return { examples: pool.slice(offset, offset + limit).map(clone), total: pool.length };\n }\n\n async createVersion(\n tenantId: string,\n datasetId: string,\n nowIso: string,\n note?: string,\n ): Promise<DatasetVersion> {\n const stored = this.require(tenantId, datasetId);\n const frozenCount = stored.examples.length;\n const contentHash = hashExamples(stored.examples);\n const version: DatasetVersion & { frozenCount: number } = {\n datasetId,\n version: stored.dataset.latestVersion + 1,\n exampleCount: frozenCount,\n contentHash,\n createdAtIso: nowIso,\n ...(note === undefined ? {} : { note }),\n frozenCount,\n };\n stored.versions.push(version);\n stored.dataset = { ...stored.dataset, latestVersion: version.version, updatedAtIso: nowIso };\n const { frozenCount: _drop, ...pub } = version;\n return clone(pub);\n }\n\n async listVersions(tenantId: string, datasetId: string): Promise<DatasetVersion[]> {\n return this.require(tenantId, datasetId).versions.map(({ frozenCount: _drop, ...v }) =>\n clone(v),\n );\n }\n\n private require(tenantId: string, datasetId: string): StoredDataset {\n const stored = this.tenant(tenantId).get(datasetId);\n if (!stored) throw new Error(`unknown dataset: ${datasetId}`);\n return stored;\n }\n}\n\n/** Content hash over sorted example ids + per-example canonical hashes. */\nexport function hashExamples(examples: readonly DatasetExample[]): string {\n const entries = examples\n .map((e) => [e.exampleId, sha256Hex(canonicalJson(e))] as const)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return sha256Hex(canonicalJson(entries));\n}\n\nfunction clone<T>(value: T): T {\n return structuredClone(value);\n}\n","import { DatasetExample } from './schema.js';\n\nexport interface ImportResult {\n examples: DatasetExample[];\n errors: Array<{ line: number; message: string }>;\n}\n\n/**\n * Parse JSONL into examples. Accepts either full example objects (with\n * `exampleId`) or bare `{input, expectedOutput?, metadata?, split?}` rows;\n * bare rows get ids from `makeId` (injected — no ambient randomness, matching\n * the deterministic-by-default rule the Harness agents follow).\n */\nexport function parseJsonl(text: string, makeId: (line: number) => string): ImportResult {\n const examples: DatasetExample[] = [];\n const errors: ImportResult['errors'] = [];\n const lines = text.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i]!.trim();\n if (raw === '') continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n errors.push({ line: i + 1, message: `invalid JSON: ${(e as Error).message}` });\n continue;\n }\n const candidate =\n parsed !== null && typeof parsed === 'object' && !('exampleId' in (parsed as object))\n ? { ...(parsed as object), exampleId: makeId(i + 1) }\n : parsed;\n const result = DatasetExample.safeParse(candidate);\n if (result.success) examples.push(result.data);\n else\n errors.push({ line: i + 1, message: result.error.issues[0]?.message ?? 'invalid example' });\n }\n return { examples, errors };\n}\n"]}
@@ -0,0 +1,186 @@
1
+ import { z } from 'zod';
2
+
3
+ /** JSON-compatible values accepted by public dataset and prompt surfaces. */
4
+ type JsonValue = null | boolean | number | string | JsonValue[] | {
5
+ [key: string]: JsonValue;
6
+ };
7
+ /**
8
+ * `z.unknown()` accepts `undefined`, which makes an object field effectively
9
+ * optional and also admits values that cannot survive JSONL/Delta storage.
10
+ * Datasets are durable evidence, so their public shape is deliberately the
11
+ * portable JSON subset.
12
+ */
13
+ declare const JsonValue: z.ZodType<JsonValue>;
14
+ declare const DatasetId: z.ZodString;
15
+ type DatasetId = z.infer<typeof DatasetId>;
16
+ declare const DatasetSplit: z.ZodEnum<["train", "test", "validation"]>;
17
+ type DatasetSplit = z.infer<typeof DatasetSplit>;
18
+ /**
19
+ * A single evaluation example. `input`/`output` are open JSON values so the
20
+ * same shape carries chat transcripts, RAG tuples, or plain string pairs —
21
+ * mirroring OpenInference conventions so trace-derived examples (M7) import
22
+ * without transformation.
23
+ */
24
+ declare const DatasetExample: z.ZodObject<{
25
+ exampleId: z.ZodString;
26
+ input: z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>;
27
+ /** Reference/expected output; optional — judges without ground truth ignore it. */
28
+ expectedOutput: z.ZodOptional<z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>>;
29
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>>>;
30
+ split: z.ZodDefault<z.ZodEnum<["train", "test", "validation"]>>;
31
+ }, "strict", z.ZodTypeAny, {
32
+ exampleId: string;
33
+ input: JsonValue;
34
+ metadata: Record<string, JsonValue>;
35
+ split: "validation" | "train" | "test";
36
+ expectedOutput?: JsonValue | undefined;
37
+ }, {
38
+ exampleId: string;
39
+ input: JsonValue;
40
+ expectedOutput?: JsonValue | undefined;
41
+ metadata?: Record<string, JsonValue> | undefined;
42
+ split?: "validation" | "train" | "test" | undefined;
43
+ }>;
44
+ type DatasetExample = z.infer<typeof DatasetExample>;
45
+ declare const DatasetSpec: z.ZodObject<{
46
+ id: z.ZodString;
47
+ name: z.ZodString;
48
+ description: z.ZodOptional<z.ZodString>;
49
+ }, "strip", z.ZodTypeAny, {
50
+ id: string;
51
+ name: string;
52
+ description?: string | undefined;
53
+ }, {
54
+ id: string;
55
+ name: string;
56
+ description?: string | undefined;
57
+ }>;
58
+ type DatasetSpec = z.infer<typeof DatasetSpec>;
59
+ /**
60
+ * Immutable version pointer. Examples are append-only; a version freezes the
61
+ * set of example ids visible at creation time and records a content hash so
62
+ * two versions with identical membership are provably identical (same
63
+ * content-addressing discipline as the edge manifest).
64
+ */
65
+ declare const DatasetVersion: z.ZodObject<{
66
+ datasetId: z.ZodString;
67
+ version: z.ZodNumber;
68
+ exampleCount: z.ZodNumber;
69
+ /** SHA-256 over the canonical JSON of sorted example ids + example hashes. */
70
+ contentHash: z.ZodString;
71
+ createdAtIso: z.ZodString;
72
+ note: z.ZodOptional<z.ZodString>;
73
+ }, "strip", z.ZodTypeAny, {
74
+ datasetId: string;
75
+ version: number;
76
+ exampleCount: number;
77
+ contentHash: string;
78
+ createdAtIso: string;
79
+ note?: string | undefined;
80
+ }, {
81
+ datasetId: string;
82
+ version: number;
83
+ exampleCount: number;
84
+ contentHash: string;
85
+ createdAtIso: string;
86
+ note?: string | undefined;
87
+ }>;
88
+ type DatasetVersion = z.infer<typeof DatasetVersion>;
89
+ declare const Dataset: z.ZodObject<{
90
+ id: z.ZodString;
91
+ name: z.ZodString;
92
+ description: z.ZodOptional<z.ZodString>;
93
+ } & {
94
+ tenantId: z.ZodString;
95
+ latestVersion: z.ZodNumber;
96
+ exampleCount: z.ZodNumber;
97
+ createdAtIso: z.ZodString;
98
+ updatedAtIso: z.ZodString;
99
+ }, "strip", z.ZodTypeAny, {
100
+ id: string;
101
+ name: string;
102
+ exampleCount: number;
103
+ createdAtIso: string;
104
+ tenantId: string;
105
+ latestVersion: number;
106
+ updatedAtIso: string;
107
+ description?: string | undefined;
108
+ }, {
109
+ id: string;
110
+ name: string;
111
+ exampleCount: number;
112
+ createdAtIso: string;
113
+ tenantId: string;
114
+ latestVersion: number;
115
+ updatedAtIso: string;
116
+ description?: string | undefined;
117
+ }>;
118
+ type Dataset = z.infer<typeof Dataset>;
119
+
120
+ interface ListExamplesQuery {
121
+ tenantId: string;
122
+ datasetId: string;
123
+ /** Pin to a frozen version; latest mutable view when omitted. */
124
+ version?: number;
125
+ split?: DatasetExample['split'];
126
+ limit?: number;
127
+ offset?: number;
128
+ }
129
+ interface ExamplePage {
130
+ examples: DatasetExample[];
131
+ total: number;
132
+ }
133
+ /**
134
+ * Storage contract for datasets. Implementations live where the drivers live
135
+ * (warehouse package: DuckDB local, Databricks Delta) — this package stays
136
+ * domain-only. `InMemoryDatasetStore` is the reference implementation used by
137
+ * unit tests and `fx dev`.
138
+ */
139
+ interface DatasetStore {
140
+ create(tenantId: string, spec: DatasetSpec, nowIso: string): Promise<Dataset>;
141
+ get(tenantId: string, datasetId: string): Promise<Dataset | null>;
142
+ list(tenantId: string): Promise<Dataset[]>;
143
+ /** Append examples to the mutable head. Returns the stored examples. */
144
+ addExamples(tenantId: string, datasetId: string, examples: readonly DatasetExample[], nowIso: string): Promise<DatasetExample[]>;
145
+ listExamples(query: ListExamplesQuery): Promise<ExamplePage>;
146
+ /** Freeze the current head into an immutable numbered version. */
147
+ createVersion(tenantId: string, datasetId: string, nowIso: string, note?: string): Promise<DatasetVersion>;
148
+ listVersions(tenantId: string, datasetId: string): Promise<DatasetVersion[]>;
149
+ }
150
+
151
+ /** Reference implementation: unit tests, `fx dev`, and harness fixtures. */
152
+ declare class InMemoryDatasetStore implements DatasetStore {
153
+ private byTenant;
154
+ private tenant;
155
+ create(tenantId: string, spec: DatasetSpec, nowIso: string): Promise<Dataset>;
156
+ get(tenantId: string, datasetId: string): Promise<Dataset | null>;
157
+ list(tenantId: string): Promise<Dataset[]>;
158
+ addExamples(tenantId: string, datasetId: string, examples: readonly DatasetExample[], nowIso: string): Promise<DatasetExample[]>;
159
+ listExamples(query: ListExamplesQuery): Promise<ExamplePage>;
160
+ createVersion(tenantId: string, datasetId: string, nowIso: string, note?: string): Promise<DatasetVersion>;
161
+ listVersions(tenantId: string, datasetId: string): Promise<DatasetVersion[]>;
162
+ private require;
163
+ }
164
+ /** Content hash over sorted example ids + per-example canonical hashes. */
165
+ declare function hashExamples(examples: readonly DatasetExample[]): string;
166
+
167
+ interface ImportResult {
168
+ examples: DatasetExample[];
169
+ errors: Array<{
170
+ line: number;
171
+ message: string;
172
+ }>;
173
+ }
174
+ /**
175
+ * Parse JSONL into examples. Accepts either full example objects (with
176
+ * `exampleId`) or bare `{input, expectedOutput?, metadata?, split?}` rows;
177
+ * bare rows get ids from `makeId` (injected — no ambient randomness, matching
178
+ * the deterministic-by-default rule the Harness agents follow).
179
+ */
180
+ declare function parseJsonl(text: string, makeId: (line: number) => string): ImportResult;
181
+
182
+ /** Deterministic JSON: object keys sorted recursively, arrays kept in order. */
183
+ declare function canonicalJson(value: unknown): string;
184
+ declare function sha256Hex(input: string): string;
185
+
186
+ export { Dataset, DatasetExample, DatasetId, DatasetSpec, DatasetSplit, type DatasetStore, DatasetVersion, type ExamplePage, type ImportResult, InMemoryDatasetStore, JsonValue, type ListExamplesQuery, canonicalJson, hashExamples, parseJsonl, sha256Hex };
@@ -0,0 +1,186 @@
1
+ import { z } from 'zod';
2
+
3
+ /** JSON-compatible values accepted by public dataset and prompt surfaces. */
4
+ type JsonValue = null | boolean | number | string | JsonValue[] | {
5
+ [key: string]: JsonValue;
6
+ };
7
+ /**
8
+ * `z.unknown()` accepts `undefined`, which makes an object field effectively
9
+ * optional and also admits values that cannot survive JSONL/Delta storage.
10
+ * Datasets are durable evidence, so their public shape is deliberately the
11
+ * portable JSON subset.
12
+ */
13
+ declare const JsonValue: z.ZodType<JsonValue>;
14
+ declare const DatasetId: z.ZodString;
15
+ type DatasetId = z.infer<typeof DatasetId>;
16
+ declare const DatasetSplit: z.ZodEnum<["train", "test", "validation"]>;
17
+ type DatasetSplit = z.infer<typeof DatasetSplit>;
18
+ /**
19
+ * A single evaluation example. `input`/`output` are open JSON values so the
20
+ * same shape carries chat transcripts, RAG tuples, or plain string pairs —
21
+ * mirroring OpenInference conventions so trace-derived examples (M7) import
22
+ * without transformation.
23
+ */
24
+ declare const DatasetExample: z.ZodObject<{
25
+ exampleId: z.ZodString;
26
+ input: z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>;
27
+ /** Reference/expected output; optional — judges without ground truth ignore it. */
28
+ expectedOutput: z.ZodOptional<z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>>;
29
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>>>;
30
+ split: z.ZodDefault<z.ZodEnum<["train", "test", "validation"]>>;
31
+ }, "strict", z.ZodTypeAny, {
32
+ exampleId: string;
33
+ input: JsonValue;
34
+ metadata: Record<string, JsonValue>;
35
+ split: "validation" | "train" | "test";
36
+ expectedOutput?: JsonValue | undefined;
37
+ }, {
38
+ exampleId: string;
39
+ input: JsonValue;
40
+ expectedOutput?: JsonValue | undefined;
41
+ metadata?: Record<string, JsonValue> | undefined;
42
+ split?: "validation" | "train" | "test" | undefined;
43
+ }>;
44
+ type DatasetExample = z.infer<typeof DatasetExample>;
45
+ declare const DatasetSpec: z.ZodObject<{
46
+ id: z.ZodString;
47
+ name: z.ZodString;
48
+ description: z.ZodOptional<z.ZodString>;
49
+ }, "strip", z.ZodTypeAny, {
50
+ id: string;
51
+ name: string;
52
+ description?: string | undefined;
53
+ }, {
54
+ id: string;
55
+ name: string;
56
+ description?: string | undefined;
57
+ }>;
58
+ type DatasetSpec = z.infer<typeof DatasetSpec>;
59
+ /**
60
+ * Immutable version pointer. Examples are append-only; a version freezes the
61
+ * set of example ids visible at creation time and records a content hash so
62
+ * two versions with identical membership are provably identical (same
63
+ * content-addressing discipline as the edge manifest).
64
+ */
65
+ declare const DatasetVersion: z.ZodObject<{
66
+ datasetId: z.ZodString;
67
+ version: z.ZodNumber;
68
+ exampleCount: z.ZodNumber;
69
+ /** SHA-256 over the canonical JSON of sorted example ids + example hashes. */
70
+ contentHash: z.ZodString;
71
+ createdAtIso: z.ZodString;
72
+ note: z.ZodOptional<z.ZodString>;
73
+ }, "strip", z.ZodTypeAny, {
74
+ datasetId: string;
75
+ version: number;
76
+ exampleCount: number;
77
+ contentHash: string;
78
+ createdAtIso: string;
79
+ note?: string | undefined;
80
+ }, {
81
+ datasetId: string;
82
+ version: number;
83
+ exampleCount: number;
84
+ contentHash: string;
85
+ createdAtIso: string;
86
+ note?: string | undefined;
87
+ }>;
88
+ type DatasetVersion = z.infer<typeof DatasetVersion>;
89
+ declare const Dataset: z.ZodObject<{
90
+ id: z.ZodString;
91
+ name: z.ZodString;
92
+ description: z.ZodOptional<z.ZodString>;
93
+ } & {
94
+ tenantId: z.ZodString;
95
+ latestVersion: z.ZodNumber;
96
+ exampleCount: z.ZodNumber;
97
+ createdAtIso: z.ZodString;
98
+ updatedAtIso: z.ZodString;
99
+ }, "strip", z.ZodTypeAny, {
100
+ id: string;
101
+ name: string;
102
+ exampleCount: number;
103
+ createdAtIso: string;
104
+ tenantId: string;
105
+ latestVersion: number;
106
+ updatedAtIso: string;
107
+ description?: string | undefined;
108
+ }, {
109
+ id: string;
110
+ name: string;
111
+ exampleCount: number;
112
+ createdAtIso: string;
113
+ tenantId: string;
114
+ latestVersion: number;
115
+ updatedAtIso: string;
116
+ description?: string | undefined;
117
+ }>;
118
+ type Dataset = z.infer<typeof Dataset>;
119
+
120
+ interface ListExamplesQuery {
121
+ tenantId: string;
122
+ datasetId: string;
123
+ /** Pin to a frozen version; latest mutable view when omitted. */
124
+ version?: number;
125
+ split?: DatasetExample['split'];
126
+ limit?: number;
127
+ offset?: number;
128
+ }
129
+ interface ExamplePage {
130
+ examples: DatasetExample[];
131
+ total: number;
132
+ }
133
+ /**
134
+ * Storage contract for datasets. Implementations live where the drivers live
135
+ * (warehouse package: DuckDB local, Databricks Delta) — this package stays
136
+ * domain-only. `InMemoryDatasetStore` is the reference implementation used by
137
+ * unit tests and `fx dev`.
138
+ */
139
+ interface DatasetStore {
140
+ create(tenantId: string, spec: DatasetSpec, nowIso: string): Promise<Dataset>;
141
+ get(tenantId: string, datasetId: string): Promise<Dataset | null>;
142
+ list(tenantId: string): Promise<Dataset[]>;
143
+ /** Append examples to the mutable head. Returns the stored examples. */
144
+ addExamples(tenantId: string, datasetId: string, examples: readonly DatasetExample[], nowIso: string): Promise<DatasetExample[]>;
145
+ listExamples(query: ListExamplesQuery): Promise<ExamplePage>;
146
+ /** Freeze the current head into an immutable numbered version. */
147
+ createVersion(tenantId: string, datasetId: string, nowIso: string, note?: string): Promise<DatasetVersion>;
148
+ listVersions(tenantId: string, datasetId: string): Promise<DatasetVersion[]>;
149
+ }
150
+
151
+ /** Reference implementation: unit tests, `fx dev`, and harness fixtures. */
152
+ declare class InMemoryDatasetStore implements DatasetStore {
153
+ private byTenant;
154
+ private tenant;
155
+ create(tenantId: string, spec: DatasetSpec, nowIso: string): Promise<Dataset>;
156
+ get(tenantId: string, datasetId: string): Promise<Dataset | null>;
157
+ list(tenantId: string): Promise<Dataset[]>;
158
+ addExamples(tenantId: string, datasetId: string, examples: readonly DatasetExample[], nowIso: string): Promise<DatasetExample[]>;
159
+ listExamples(query: ListExamplesQuery): Promise<ExamplePage>;
160
+ createVersion(tenantId: string, datasetId: string, nowIso: string, note?: string): Promise<DatasetVersion>;
161
+ listVersions(tenantId: string, datasetId: string): Promise<DatasetVersion[]>;
162
+ private require;
163
+ }
164
+ /** Content hash over sorted example ids + per-example canonical hashes. */
165
+ declare function hashExamples(examples: readonly DatasetExample[]): string;
166
+
167
+ interface ImportResult {
168
+ examples: DatasetExample[];
169
+ errors: Array<{
170
+ line: number;
171
+ message: string;
172
+ }>;
173
+ }
174
+ /**
175
+ * Parse JSONL into examples. Accepts either full example objects (with
176
+ * `exampleId`) or bare `{input, expectedOutput?, metadata?, split?}` rows;
177
+ * bare rows get ids from `makeId` (injected — no ambient randomness, matching
178
+ * the deterministic-by-default rule the Harness agents follow).
179
+ */
180
+ declare function parseJsonl(text: string, makeId: (line: number) => string): ImportResult;
181
+
182
+ /** Deterministic JSON: object keys sorted recursively, arrays kept in order. */
183
+ declare function canonicalJson(value: unknown): string;
184
+ declare function sha256Hex(input: string): string;
185
+
186
+ export { Dataset, DatasetExample, DatasetId, DatasetSpec, DatasetSplit, type DatasetStore, DatasetVersion, type ExamplePage, type ImportResult, InMemoryDatasetStore, JsonValue, type ListExamplesQuery, canonicalJson, hashExamples, parseJsonl, sha256Hex };
package/dist/index.js ADDED
@@ -0,0 +1,185 @@
1
+ import { z } from 'zod';
2
+ import { createHash } from 'crypto';
3
+
4
+ // src/schema.ts
5
+ var JsonValue = z.lazy(
6
+ () => z.union([
7
+ z.null(),
8
+ z.boolean(),
9
+ z.number().finite(),
10
+ z.string(),
11
+ z.array(JsonValue),
12
+ z.record(JsonValue)
13
+ ])
14
+ );
15
+ var DatasetId = z.string().min(2).max(64).regex(/^[a-z][a-z0-9-]*$/, "lowercase kebab-case, must start with a letter");
16
+ var DatasetSplit = z.enum(["train", "test", "validation"]);
17
+ var DatasetExample = z.object({
18
+ exampleId: z.string().uuid(),
19
+ input: JsonValue,
20
+ /** Reference/expected output; optional — judges without ground truth ignore it. */
21
+ expectedOutput: JsonValue.optional(),
22
+ metadata: z.record(JsonValue).default({}),
23
+ split: DatasetSplit.default("test")
24
+ }).strict();
25
+ var DatasetSpec = z.object({
26
+ id: DatasetId,
27
+ name: z.string().min(1).max(120),
28
+ description: z.string().max(2e3).optional()
29
+ });
30
+ var DatasetVersion = z.object({
31
+ datasetId: DatasetId,
32
+ version: z.number().int().min(1),
33
+ exampleCount: z.number().int().min(0),
34
+ /** SHA-256 over the canonical JSON of sorted example ids + example hashes. */
35
+ contentHash: z.string().regex(/^[a-f0-9]{64}$/),
36
+ createdAtIso: z.string().datetime(),
37
+ note: z.string().max(500).optional()
38
+ });
39
+ var Dataset = DatasetSpec.extend({
40
+ tenantId: z.string().min(1),
41
+ latestVersion: z.number().int().min(0),
42
+ exampleCount: z.number().int().min(0),
43
+ createdAtIso: z.string().datetime(),
44
+ updatedAtIso: z.string().datetime()
45
+ });
46
+ function canonicalJson(value) {
47
+ return JSON.stringify(sortValue(value));
48
+ }
49
+ function sortValue(value) {
50
+ if (Array.isArray(value)) return value.map(sortValue);
51
+ if (value !== null && typeof value === "object") {
52
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
53
+ return Object.fromEntries(entries.map(([k, v]) => [k, sortValue(v)]));
54
+ }
55
+ return value;
56
+ }
57
+ function sha256Hex(input) {
58
+ return createHash("sha256").update(input, "utf8").digest("hex");
59
+ }
60
+
61
+ // src/memory.ts
62
+ var InMemoryDatasetStore = class {
63
+ byTenant = /* @__PURE__ */ new Map();
64
+ tenant(tenantId) {
65
+ let m = this.byTenant.get(tenantId);
66
+ if (!m) {
67
+ m = /* @__PURE__ */ new Map();
68
+ this.byTenant.set(tenantId, m);
69
+ }
70
+ return m;
71
+ }
72
+ async create(tenantId, spec, nowIso) {
73
+ const m = this.tenant(tenantId);
74
+ if (m.has(spec.id)) throw new Error(`dataset already exists: ${spec.id}`);
75
+ const dataset = {
76
+ ...spec,
77
+ tenantId,
78
+ latestVersion: 0,
79
+ exampleCount: 0,
80
+ createdAtIso: nowIso,
81
+ updatedAtIso: nowIso
82
+ };
83
+ m.set(spec.id, { dataset, examples: [], versions: [] });
84
+ return clone(dataset);
85
+ }
86
+ async get(tenantId, datasetId) {
87
+ const dataset = this.tenant(tenantId).get(datasetId)?.dataset;
88
+ return dataset ? clone(dataset) : null;
89
+ }
90
+ async list(tenantId) {
91
+ return [...this.tenant(tenantId).values()].map((s) => clone(s.dataset));
92
+ }
93
+ async addExamples(tenantId, datasetId, examples, nowIso) {
94
+ const stored = this.require(tenantId, datasetId);
95
+ const validated = examples.map((example) => DatasetExample.parse(example));
96
+ const ids = new Set(stored.examples.map((e) => e.exampleId));
97
+ for (const e of validated) {
98
+ if (ids.has(e.exampleId)) throw new Error(`duplicate exampleId: ${e.exampleId}`);
99
+ ids.add(e.exampleId);
100
+ }
101
+ stored.examples.push(...validated.map(clone));
102
+ stored.dataset = {
103
+ ...stored.dataset,
104
+ exampleCount: stored.examples.length,
105
+ updatedAtIso: nowIso
106
+ };
107
+ return validated.map(clone);
108
+ }
109
+ async listExamples(query) {
110
+ const stored = this.require(query.tenantId, query.datasetId);
111
+ let pool = stored.examples;
112
+ if (query.version !== void 0) {
113
+ const v = stored.versions.find((x) => x.version === query.version);
114
+ if (!v) throw new Error(`unknown version ${query.version} for dataset ${query.datasetId}`);
115
+ pool = pool.slice(0, v.frozenCount);
116
+ }
117
+ if (query.split) pool = pool.filter((e) => e.split === query.split);
118
+ const offset = query.offset ?? 0;
119
+ const limit = query.limit ?? 100;
120
+ return { examples: pool.slice(offset, offset + limit).map(clone), total: pool.length };
121
+ }
122
+ async createVersion(tenantId, datasetId, nowIso, note) {
123
+ const stored = this.require(tenantId, datasetId);
124
+ const frozenCount = stored.examples.length;
125
+ const contentHash = hashExamples(stored.examples);
126
+ const version = {
127
+ datasetId,
128
+ version: stored.dataset.latestVersion + 1,
129
+ exampleCount: frozenCount,
130
+ contentHash,
131
+ createdAtIso: nowIso,
132
+ ...note === void 0 ? {} : { note },
133
+ frozenCount
134
+ };
135
+ stored.versions.push(version);
136
+ stored.dataset = { ...stored.dataset, latestVersion: version.version, updatedAtIso: nowIso };
137
+ const { frozenCount: _drop, ...pub } = version;
138
+ return clone(pub);
139
+ }
140
+ async listVersions(tenantId, datasetId) {
141
+ return this.require(tenantId, datasetId).versions.map(
142
+ ({ frozenCount: _drop, ...v }) => clone(v)
143
+ );
144
+ }
145
+ require(tenantId, datasetId) {
146
+ const stored = this.tenant(tenantId).get(datasetId);
147
+ if (!stored) throw new Error(`unknown dataset: ${datasetId}`);
148
+ return stored;
149
+ }
150
+ };
151
+ function hashExamples(examples) {
152
+ const entries = examples.map((e) => [e.exampleId, sha256Hex(canonicalJson(e))]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
153
+ return sha256Hex(canonicalJson(entries));
154
+ }
155
+ function clone(value) {
156
+ return structuredClone(value);
157
+ }
158
+
159
+ // src/import.ts
160
+ function parseJsonl(text, makeId) {
161
+ const examples = [];
162
+ const errors = [];
163
+ const lines = text.split("\n");
164
+ for (let i = 0; i < lines.length; i++) {
165
+ const raw = lines[i].trim();
166
+ if (raw === "") continue;
167
+ let parsed;
168
+ try {
169
+ parsed = JSON.parse(raw);
170
+ } catch (e) {
171
+ errors.push({ line: i + 1, message: `invalid JSON: ${e.message}` });
172
+ continue;
173
+ }
174
+ const candidate = parsed !== null && typeof parsed === "object" && !("exampleId" in parsed) ? { ...parsed, exampleId: makeId(i + 1) } : parsed;
175
+ const result = DatasetExample.safeParse(candidate);
176
+ if (result.success) examples.push(result.data);
177
+ else
178
+ errors.push({ line: i + 1, message: result.error.issues[0]?.message ?? "invalid example" });
179
+ }
180
+ return { examples, errors };
181
+ }
182
+
183
+ export { Dataset, DatasetExample, DatasetId, DatasetSpec, DatasetSplit, DatasetVersion, InMemoryDatasetStore, JsonValue, canonicalJson, hashExamples, parseJsonl, sha256Hex };
184
+ //# sourceMappingURL=index.js.map
185
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts","../src/hash.ts","../src/memory.ts","../src/import.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/** JSON-compatible values accepted by public dataset and prompt surfaces. */\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/**\n * `z.unknown()` accepts `undefined`, which makes an object field effectively\n * optional and also admits values that cannot survive JSONL/Delta storage.\n * Datasets are durable evidence, so their public shape is deliberately the\n * portable JSON subset.\n */\nexport const JsonValue: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.null(),\n z.boolean(),\n z.number().finite(),\n z.string(),\n z.array(JsonValue),\n z.record(JsonValue),\n ]),\n);\n\nexport const DatasetId = z\n .string()\n .min(2)\n .max(64)\n .regex(/^[a-z][a-z0-9-]*$/, 'lowercase kebab-case, must start with a letter');\nexport type DatasetId = z.infer<typeof DatasetId>;\n\nexport const DatasetSplit = z.enum(['train', 'test', 'validation']);\nexport type DatasetSplit = z.infer<typeof DatasetSplit>;\n\n/**\n * A single evaluation example. `input`/`output` are open JSON values so the\n * same shape carries chat transcripts, RAG tuples, or plain string pairs —\n * mirroring OpenInference conventions so trace-derived examples (M7) import\n * without transformation.\n */\nexport const DatasetExample = z\n .object({\n exampleId: z.string().uuid(),\n input: JsonValue,\n /** Reference/expected output; optional — judges without ground truth ignore it. */\n expectedOutput: JsonValue.optional(),\n metadata: z.record(JsonValue).default({}),\n split: DatasetSplit.default('test'),\n })\n .strict();\nexport type DatasetExample = z.infer<typeof DatasetExample>;\n\nexport const DatasetSpec = z.object({\n id: DatasetId,\n name: z.string().min(1).max(120),\n description: z.string().max(2000).optional(),\n});\nexport type DatasetSpec = z.infer<typeof DatasetSpec>;\n\n/**\n * Immutable version pointer. Examples are append-only; a version freezes the\n * set of example ids visible at creation time and records a content hash so\n * two versions with identical membership are provably identical (same\n * content-addressing discipline as the edge manifest).\n */\nexport const DatasetVersion = z.object({\n datasetId: DatasetId,\n version: z.number().int().min(1),\n exampleCount: z.number().int().min(0),\n /** SHA-256 over the canonical JSON of sorted example ids + example hashes. */\n contentHash: z.string().regex(/^[a-f0-9]{64}$/),\n createdAtIso: z.string().datetime(),\n note: z.string().max(500).optional(),\n});\nexport type DatasetVersion = z.infer<typeof DatasetVersion>;\n\nexport const Dataset = DatasetSpec.extend({\n tenantId: z.string().min(1),\n latestVersion: z.number().int().min(0),\n exampleCount: z.number().int().min(0),\n createdAtIso: z.string().datetime(),\n updatedAtIso: z.string().datetime(),\n});\nexport type Dataset = z.infer<typeof Dataset>;\n","import { createHash } from 'node:crypto';\n\n/** Deterministic JSON: object keys sorted recursively, arrays kept in order. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortValue);\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return Object.fromEntries(entries.map(([k, v]) => [k, sortValue(v)]));\n }\n return value;\n}\n\nexport function sha256Hex(input: string): string {\n return createHash('sha256').update(input, 'utf8').digest('hex');\n}\n","import { canonicalJson, sha256Hex } from './hash.js';\nimport { type Dataset, DatasetExample, type DatasetSpec, type DatasetVersion } from './schema.js';\nimport type { DatasetStore, ExamplePage, ListExamplesQuery } from './store.js';\n\ninterface StoredDataset {\n dataset: Dataset;\n /** Append-only example log; versions freeze a prefix of this log. */\n examples: DatasetExample[];\n versions: Array<DatasetVersion & { frozenCount: number }>;\n}\n\n/** Reference implementation: unit tests, `fx dev`, and harness fixtures. */\nexport class InMemoryDatasetStore implements DatasetStore {\n private byTenant = new Map<string, Map<string, StoredDataset>>();\n\n private tenant(tenantId: string): Map<string, StoredDataset> {\n let m = this.byTenant.get(tenantId);\n if (!m) {\n m = new Map();\n this.byTenant.set(tenantId, m);\n }\n return m;\n }\n\n async create(tenantId: string, spec: DatasetSpec, nowIso: string): Promise<Dataset> {\n const m = this.tenant(tenantId);\n if (m.has(spec.id)) throw new Error(`dataset already exists: ${spec.id}`);\n const dataset: Dataset = {\n ...spec,\n tenantId,\n latestVersion: 0,\n exampleCount: 0,\n createdAtIso: nowIso,\n updatedAtIso: nowIso,\n };\n m.set(spec.id, { dataset, examples: [], versions: [] });\n return clone(dataset);\n }\n\n async get(tenantId: string, datasetId: string): Promise<Dataset | null> {\n const dataset = this.tenant(tenantId).get(datasetId)?.dataset;\n return dataset ? clone(dataset) : null;\n }\n\n async list(tenantId: string): Promise<Dataset[]> {\n return [...this.tenant(tenantId).values()].map((s) => clone(s.dataset));\n }\n\n async addExamples(\n tenantId: string,\n datasetId: string,\n examples: readonly DatasetExample[],\n nowIso: string,\n ): Promise<DatasetExample[]> {\n const stored = this.require(tenantId, datasetId);\n const validated = examples.map((example) => DatasetExample.parse(example));\n const ids = new Set(stored.examples.map((e) => e.exampleId));\n for (const e of validated) {\n if (ids.has(e.exampleId)) throw new Error(`duplicate exampleId: ${e.exampleId}`);\n ids.add(e.exampleId);\n }\n // Keep an owned copy. Callers may mutate the object they passed after this\n // method returns; a frozen version must never change as a side effect.\n stored.examples.push(...validated.map(clone));\n stored.dataset = {\n ...stored.dataset,\n exampleCount: stored.examples.length,\n updatedAtIso: nowIso,\n };\n return validated.map(clone);\n }\n\n async listExamples(query: ListExamplesQuery): Promise<ExamplePage> {\n const stored = this.require(query.tenantId, query.datasetId);\n let pool = stored.examples;\n if (query.version !== undefined) {\n const v = stored.versions.find((x) => x.version === query.version);\n if (!v) throw new Error(`unknown version ${query.version} for dataset ${query.datasetId}`);\n pool = pool.slice(0, v.frozenCount);\n }\n if (query.split) pool = pool.filter((e) => e.split === query.split);\n const offset = query.offset ?? 0;\n const limit = query.limit ?? 100;\n return { examples: pool.slice(offset, offset + limit).map(clone), total: pool.length };\n }\n\n async createVersion(\n tenantId: string,\n datasetId: string,\n nowIso: string,\n note?: string,\n ): Promise<DatasetVersion> {\n const stored = this.require(tenantId, datasetId);\n const frozenCount = stored.examples.length;\n const contentHash = hashExamples(stored.examples);\n const version: DatasetVersion & { frozenCount: number } = {\n datasetId,\n version: stored.dataset.latestVersion + 1,\n exampleCount: frozenCount,\n contentHash,\n createdAtIso: nowIso,\n ...(note === undefined ? {} : { note }),\n frozenCount,\n };\n stored.versions.push(version);\n stored.dataset = { ...stored.dataset, latestVersion: version.version, updatedAtIso: nowIso };\n const { frozenCount: _drop, ...pub } = version;\n return clone(pub);\n }\n\n async listVersions(tenantId: string, datasetId: string): Promise<DatasetVersion[]> {\n return this.require(tenantId, datasetId).versions.map(({ frozenCount: _drop, ...v }) =>\n clone(v),\n );\n }\n\n private require(tenantId: string, datasetId: string): StoredDataset {\n const stored = this.tenant(tenantId).get(datasetId);\n if (!stored) throw new Error(`unknown dataset: ${datasetId}`);\n return stored;\n }\n}\n\n/** Content hash over sorted example ids + per-example canonical hashes. */\nexport function hashExamples(examples: readonly DatasetExample[]): string {\n const entries = examples\n .map((e) => [e.exampleId, sha256Hex(canonicalJson(e))] as const)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return sha256Hex(canonicalJson(entries));\n}\n\nfunction clone<T>(value: T): T {\n return structuredClone(value);\n}\n","import { DatasetExample } from './schema.js';\n\nexport interface ImportResult {\n examples: DatasetExample[];\n errors: Array<{ line: number; message: string }>;\n}\n\n/**\n * Parse JSONL into examples. Accepts either full example objects (with\n * `exampleId`) or bare `{input, expectedOutput?, metadata?, split?}` rows;\n * bare rows get ids from `makeId` (injected — no ambient randomness, matching\n * the deterministic-by-default rule the Harness agents follow).\n */\nexport function parseJsonl(text: string, makeId: (line: number) => string): ImportResult {\n const examples: DatasetExample[] = [];\n const errors: ImportResult['errors'] = [];\n const lines = text.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i]!.trim();\n if (raw === '') continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n errors.push({ line: i + 1, message: `invalid JSON: ${(e as Error).message}` });\n continue;\n }\n const candidate =\n parsed !== null && typeof parsed === 'object' && !('exampleId' in (parsed as object))\n ? { ...(parsed as object), exampleId: makeId(i + 1) }\n : parsed;\n const result = DatasetExample.safeParse(candidate);\n if (result.success) examples.push(result.data);\n else\n errors.push({ line: i + 1, message: result.error.issues[0]?.message ?? 'invalid example' });\n }\n return { examples, errors };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@fabricorg/experiments-datasets",
3
+ "version": "0.1.0",
4
+ "description": "Versioned evaluation datasets for Fabric Experiments: schemas, store contract, JSONL import.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.cjs",
9
+ "types": "./dist/index.d.ts",
10
+ "module": "./dist/index.js",
11
+ "exports": {
12
+ ".": {
13
+ "import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
14
+ "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
15
+ }
16
+ },
17
+ "files": ["dist"],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "type-check": "tsc --noEmit",
21
+ "test": "vitest run",
22
+ "clean": "rm -rf dist"
23
+ },
24
+ "dependencies": {
25
+ "zod": "^3.23.8"
26
+ },
27
+ "devDependencies": {
28
+ "tsup": "^8.3.5",
29
+ "typescript": "^5.8.3",
30
+ "vitest": "^3.2.7"
31
+ }
32
+ }