@chartcoach/catalog 0.1.6 → 0.2.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.
Files changed (73) hide show
  1. package/LICENSE +201 -21
  2. package/README.md +324 -38
  3. package/dist/duckdb-wasm.d.ts +8 -0
  4. package/dist/duckdb-wasm.js +22 -0
  5. package/dist/duckdb.d.ts +8 -0
  6. package/dist/duckdb.js +9 -0
  7. package/dist/index-cache-Cx1gIK6N.js +280 -0
  8. package/dist/index.d.ts +40 -8
  9. package/dist/index.js +22 -7
  10. package/dist/json-CBWjz9b4.js +29 -0
  11. package/dist/model-De0MF-zg.d.ts +255 -0
  12. package/dist/node.d.ts +15 -0
  13. package/dist/node.js +218 -0
  14. package/dist/open-Nnzkr_bE.d.ts +15 -0
  15. package/dist/open-Uu3-r8kp.js +1244 -0
  16. package/dist/registration-CgQGMUIH.d.ts +6 -0
  17. package/dist/registration-DXhS7vgr.js +41 -0
  18. package/dist/tables-DcgKnXwF.js +389 -0
  19. package/package.json +48 -19
  20. package/src/catalog/artifacts.ts +309 -100
  21. package/src/catalog/description.ts +198 -0
  22. package/src/catalog/errors.ts +26 -1
  23. package/src/catalog/identity.ts +69 -0
  24. package/src/catalog/json.ts +32 -0
  25. package/src/catalog/labels.ts +8 -6
  26. package/src/catalog/manifest.ts +81 -20
  27. package/src/catalog/markdown.ts +2 -2
  28. package/src/catalog/model.ts +222 -37
  29. package/src/catalog/open.ts +428 -0
  30. package/src/catalog/parquet.ts +164 -0
  31. package/src/catalog/profile-layout.ts +86 -0
  32. package/src/catalog/profile.ts +345 -0
  33. package/src/catalog/query.ts +125 -0
  34. package/src/catalog/read.ts +101 -0
  35. package/src/catalog/references.ts +336 -0
  36. package/src/catalog/registration.ts +74 -0
  37. package/src/catalog/tables.ts +250 -0
  38. package/src/catalog/wire.ts +38 -65
  39. package/src/duckdb-wasm.ts +33 -0
  40. package/src/duckdb.ts +18 -0
  41. package/src/index.ts +51 -20
  42. package/src/node/index-cache.ts +432 -0
  43. package/src/node.ts +347 -0
  44. package/dist/catalog/artifacts.d.ts +0 -28
  45. package/dist/catalog/artifacts.d.ts.map +0 -1
  46. package/dist/catalog/artifacts.js +0 -89
  47. package/dist/catalog/chartcoach-defaults.d.ts +0 -17
  48. package/dist/catalog/chartcoach-defaults.d.ts.map +0 -1
  49. package/dist/catalog/chartcoach-defaults.js +0 -8
  50. package/dist/catalog/errors.d.ts +0 -4
  51. package/dist/catalog/errors.d.ts.map +0 -1
  52. package/dist/catalog/errors.js +0 -6
  53. package/dist/catalog/labels.d.ts +0 -9
  54. package/dist/catalog/labels.d.ts.map +0 -1
  55. package/dist/catalog/labels.js +0 -23
  56. package/dist/catalog/load-parquet-core.d.ts +0 -15
  57. package/dist/catalog/load-parquet-core.d.ts.map +0 -1
  58. package/dist/catalog/load-parquet-core.js +0 -45
  59. package/dist/catalog/manifest.d.ts +0 -15
  60. package/dist/catalog/manifest.d.ts.map +0 -1
  61. package/dist/catalog/manifest.js +0 -143
  62. package/dist/catalog/markdown.d.ts +0 -3
  63. package/dist/catalog/markdown.d.ts.map +0 -1
  64. package/dist/catalog/markdown.js +0 -13
  65. package/dist/catalog/model.d.ts +0 -33
  66. package/dist/catalog/model.d.ts.map +0 -1
  67. package/dist/catalog/model.js +0 -67
  68. package/dist/catalog/wire.d.ts +0 -18
  69. package/dist/catalog/wire.d.ts.map +0 -1
  70. package/dist/catalog/wire.js +0 -78
  71. package/dist/index.d.ts.map +0 -1
  72. package/src/catalog/chartcoach-defaults.ts +0 -17
  73. package/src/catalog/load-parquet-core.ts +0 -78
package/src/node.ts ADDED
@@ -0,0 +1,347 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { mkdir, open, rename, stat, unlink, writeFile } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { Readable } from "node:stream";
7
+ import { fileURLToPath, pathToFileURL } from "node:url";
8
+
9
+ import { CatalogError } from "./catalog/errors";
10
+ import { assertReleaseDigest, parseCatalogRelease, releaseArtifact } from "./catalog/artifacts";
11
+ import { parseJson } from "./catalog/json";
12
+ import { loadCatalogData } from "./catalog/parquet";
13
+ import {
14
+ openCatalog as openRemoteCatalog,
15
+ type FetchLike,
16
+ type OpenCatalogOptions,
17
+ } from "./catalog/open";
18
+ import type { ArtifactOptions, Catalog } from "./catalog/model";
19
+
20
+ type NodeContext = { cacheDirectory: string; fetch: FetchLike };
21
+
22
+ const contexts = new WeakMap<Catalog, NodeContext>();
23
+
24
+ export type NodeOpenCatalogOptions = OpenCatalogOptions & { cacheDirectory?: string };
25
+
26
+ export type IndexPathOptions = ArtifactOptions & { directory?: string };
27
+
28
+ /** Return a verified profile's extracted database directory for native LanceDB clients. */
29
+ export async function indexPath(
30
+ catalog: Catalog,
31
+ profile: string,
32
+ options: IndexPathOptions = {},
33
+ ): Promise<string> {
34
+ options.signal?.throwIfAborted();
35
+
36
+ if (!catalog.release)
37
+ throw new CatalogError("Index access requires a catalog release.", {
38
+ code: "unavailable_capability",
39
+ });
40
+ await catalog.describe({ profile, signal: options.signal });
41
+ const path = `profiles/${profile}/index.tar.gz`;
42
+ const artifact = releaseArtifact(catalog.release, path);
43
+ const { extractIndex } = await import("./node/index-cache");
44
+
45
+ return extractIndex({
46
+ archive: () => artifactPath(catalog, path, options),
47
+ digest: artifact.sha256,
48
+ cacheDirectory: contexts.get(catalog)?.cacheDirectory ?? defaultCacheDirectory(),
49
+ directory: options.directory,
50
+ signal: options.signal,
51
+ });
52
+ }
53
+
54
+ export async function openCatalog(
55
+ location?: string | URL,
56
+ options: NodeOpenCatalogOptions = {},
57
+ ): Promise<Catalog> {
58
+ const cacheDirectory = options.cacheDirectory ?? defaultCacheDirectory();
59
+ const fetch = options.fetch ?? globalThis.fetch;
60
+ const uri = location === undefined ? undefined : locationUrl(location);
61
+
62
+ if (uri?.protocol === "file:") {
63
+ const local = fileURLToPath(uri);
64
+
65
+ if ((await stat(local)).isDirectory()) {
66
+ const selection = await existingFile(join(local, "catalog.json"));
67
+ const release = await existingFile(join(local, "release.json"));
68
+
69
+ if (selection && release)
70
+ throw new CatalogError("Catalog directory contains both descriptors.");
71
+
72
+ if (selection || release)
73
+ return openCatalog(
74
+ pathToFileURL(join(local, selection ? "catalog.json" : "release.json")),
75
+ options,
76
+ );
77
+
78
+ const [entries, manifest] = await Promise.all([
79
+ readBytes(join(local, "entries.parquet"), 64 * 1024 ** 2, options.signal),
80
+ readBytes(join(local, "MANIFEST.md"), 64 * 1024 ** 2, options.signal),
81
+ ]);
82
+
83
+ return loadCatalogData({
84
+ entries,
85
+ manifestText: new TextDecoder("utf-8", { fatal: true }).decode(manifest),
86
+ });
87
+ }
88
+ }
89
+
90
+ const fetchResource: FetchLike = async (input, init) => {
91
+ const url = new URL(input);
92
+ init?.signal?.throwIfAborted();
93
+
94
+ if (url.protocol === "file:") {
95
+ // SAFETY: createReadStream emits Buffer chunks and toWeb preserves those byte chunks.
96
+ const body = Readable.toWeb(
97
+ createReadStream(fileURLToPath(url)),
98
+ ) as ReadableStream<Uint8Array>;
99
+
100
+ return new Response(body);
101
+ }
102
+
103
+ const parent = url.pathname.split("/").at(-2) ?? "";
104
+
105
+ if (url.pathname.endsWith("/release.json") && /^[a-f0-9]{64}$/.test(parent)) {
106
+ const cached = await readCached(
107
+ join(cacheDirectory, "descriptors", `${parent}.json`),
108
+ 1024 ** 2,
109
+ );
110
+
111
+ if (cached) {
112
+ try {
113
+ const release = parseCatalogRelease(parseJson(new TextDecoder().decode(cached)));
114
+ await assertReleaseDigest(release, parent);
115
+
116
+ return new Response(Uint8Array.from(cached));
117
+ } catch (error) {
118
+ if (!(error instanceof CatalogError) && !(error instanceof SyntaxError)) throw error;
119
+ }
120
+ }
121
+ }
122
+
123
+ return fetch(input, init);
124
+ };
125
+
126
+ const catalog = await openRemoteCatalog(uri, {
127
+ signal: options.signal,
128
+ cache: options.cache ?? {
129
+ get: async (digest, bytes) => readCached(join(cacheDirectory, "artifacts", digest), bytes),
130
+ put: async (digest, bytes) => atomicWrite(join(cacheDirectory, "artifacts", digest), bytes),
131
+ },
132
+ fetch: fetchResource,
133
+ });
134
+
135
+ if (catalog.release)
136
+ await atomicWrite(
137
+ join(cacheDirectory, "descriptors", `${catalog.release.digest}.json`),
138
+ new TextEncoder().encode(JSON.stringify(catalog.release)),
139
+ );
140
+ contexts.set(catalog, { cacheDirectory, fetch: fetchResource });
141
+
142
+ return catalog;
143
+ }
144
+
145
+ export async function artifactPath(
146
+ catalog: Catalog,
147
+ path: string,
148
+ options: ArtifactOptions = {},
149
+ ): Promise<string> {
150
+ if (!catalog.release)
151
+ throw new CatalogError("Artifact access requires a catalog release.", {
152
+ code: "unavailable_capability",
153
+ });
154
+ const artifact = releaseArtifact(catalog.release, path);
155
+
156
+ if (artifact.bytes > 1024 ** 3) throw new CatalogError("Catalog artifact exceeds 1 GiB.");
157
+ const context = contexts.get(catalog);
158
+
159
+ const target = join(
160
+ context?.cacheDirectory ?? defaultCacheDirectory(),
161
+ "artifacts",
162
+ artifact.sha256,
163
+ );
164
+
165
+ const timeout = AbortSignal.timeout(900_000);
166
+ const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
167
+ signal.throwIfAborted();
168
+
169
+ if (await verifiedFile(target, artifact.sha256, artifact.bytes, signal)) return target;
170
+
171
+ if (!context || !catalog.releaseUrl) {
172
+ await atomicWrite(target, await catalog.artifact(path, { signal }));
173
+ signal.throwIfAborted();
174
+
175
+ return target;
176
+ }
177
+
178
+ const response = await context.fetch(new URL(path, catalog.releaseUrl), { signal });
179
+
180
+ if (!response.ok) {
181
+ void response.body?.cancel().catch(() => {});
182
+ throw new CatalogError("Artifact could not be loaded.");
183
+ }
184
+
185
+ if (!response.body) {
186
+ if (artifact.bytes !== 0 || artifact.sha256 !== createHash("sha256").digest("hex"))
187
+ throw new CatalogError("Artifact integrity check failed.", { code: "integrity" });
188
+ signal.throwIfAborted();
189
+ await atomicWrite(target, new Uint8Array());
190
+
191
+ return target;
192
+ }
193
+
194
+ const temporary = `${target}.${randomUUID()}.tmp`;
195
+ const reader = response.body.getReader();
196
+
197
+ const abort = () => {
198
+ void reader.cancel(signal.reason).catch(() => {});
199
+ };
200
+
201
+ signal.addEventListener("abort", abort, { once: true });
202
+
203
+ if (signal.aborted) abort();
204
+
205
+ try {
206
+ await mkdir(dirname(target), { recursive: true });
207
+ const file = await open(temporary, "wx");
208
+ const hash = createHash("sha256");
209
+ let size = 0;
210
+
211
+ try {
212
+ while (true) {
213
+ const chunk = await reader.read();
214
+ signal.throwIfAborted();
215
+
216
+ if (chunk.done) break;
217
+ size += chunk.value.byteLength;
218
+
219
+ if (size > artifact.bytes)
220
+ throw new CatalogError("Artifact byte count mismatch.", { code: "integrity" });
221
+ hash.update(chunk.value);
222
+ await file.writeFile(chunk.value);
223
+ }
224
+ } finally {
225
+ await file.close();
226
+ }
227
+
228
+ if (size !== artifact.bytes || hash.digest("hex") !== artifact.sha256)
229
+ throw new CatalogError("Artifact integrity check failed.", { code: "integrity" });
230
+ signal.throwIfAborted();
231
+ await rename(temporary, target);
232
+
233
+ return target;
234
+ } finally {
235
+ signal.removeEventListener("abort", abort);
236
+ void reader.cancel().catch(() => {});
237
+ reader.releaseLock();
238
+ await removeTemporary(temporary);
239
+ }
240
+ }
241
+
242
+ async function verifiedFile(
243
+ path: string,
244
+ digest: string,
245
+ bytes: number,
246
+ signal: AbortSignal,
247
+ ): Promise<boolean> {
248
+ try {
249
+ if ((await stat(path)).size !== bytes) return false;
250
+ const hash = createHash("sha256");
251
+ let size = 0;
252
+
253
+ for await (const chunk of createReadStream(path, { signal })) {
254
+ size += chunk.length;
255
+
256
+ if (size > bytes) return false;
257
+ hash.update(chunk);
258
+ }
259
+
260
+ signal.throwIfAborted();
261
+
262
+ return size === bytes && hash.digest("hex") === digest;
263
+ } catch (error) {
264
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
265
+ throw error;
266
+ }
267
+ }
268
+
269
+ function locationUrl(location: string | URL): URL {
270
+ if (location instanceof URL) return location;
271
+
272
+ return /^[a-z][a-z0-9+.-]*:\/\//i.test(location)
273
+ ? new URL(location)
274
+ : pathToFileURL(resolve(location));
275
+ }
276
+
277
+ function defaultCacheDirectory(): string {
278
+ if (process.platform === "darwin") return join(homedir(), "Library", "Caches", "chartcoach");
279
+
280
+ if (process.platform === "win32")
281
+ return join(
282
+ process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"),
283
+ "chartcoach",
284
+ "Cache",
285
+ );
286
+
287
+ return join(process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache"), "chartcoach");
288
+ }
289
+
290
+ async function existingFile(path: string): Promise<boolean> {
291
+ try {
292
+ return (await stat(path)).isFile();
293
+ } catch (error) {
294
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
295
+ throw error;
296
+ }
297
+ }
298
+
299
+ async function readBytes(path: string, maximum: number, signal?: AbortSignal): Promise<Uint8Array> {
300
+ if ((await stat(path)).size > maximum) throw new CatalogError("Catalog file exceeds size limit.");
301
+ const chunks: Uint8Array[] = [];
302
+ let size = 0;
303
+
304
+ for await (const chunk of createReadStream(path, { signal })) {
305
+ size += chunk.length;
306
+
307
+ if (size > maximum) throw new CatalogError("Catalog file exceeds size limit.");
308
+ chunks.push(chunk);
309
+ }
310
+
311
+ signal?.throwIfAborted();
312
+
313
+ return Buffer.concat(chunks);
314
+ }
315
+
316
+ async function readCached(path: string, maximum: number): Promise<Uint8Array | undefined> {
317
+ try {
318
+ return await readBytes(path, maximum);
319
+ } catch (error) {
320
+ if (
321
+ error instanceof CatalogError ||
322
+ (error instanceof Error && "code" in error && error.code === "ENOENT")
323
+ )
324
+ return undefined;
325
+ throw error;
326
+ }
327
+ }
328
+
329
+ async function atomicWrite(path: string, bytes: Uint8Array): Promise<void> {
330
+ await mkdir(dirname(path), { recursive: true });
331
+ const temporary = `${path}.${randomUUID()}.tmp`;
332
+
333
+ try {
334
+ await writeFile(temporary, bytes, { flag: "wx" });
335
+ await rename(temporary, path);
336
+ } finally {
337
+ await removeTemporary(temporary);
338
+ }
339
+ }
340
+
341
+ async function removeTemporary(path: string): Promise<void> {
342
+ try {
343
+ await unlink(path);
344
+ } catch (error) {
345
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
346
+ }
347
+ }
@@ -1,28 +0,0 @@
1
- export { CHARTCOACH_DEFAULTS, type ChartCoachDefaults } from "./chartcoach-defaults.js";
2
- export declare const DEFAULT_CATALOG: {
3
- readonly version: "0.1.6";
4
- readonly digest: "7cfd43ee820be252b8ae9058c4c36109a9c8415c6b3a5ff8a9127117b4a10c19";
5
- readonly releaseRootUrl: string;
6
- readonly metadataUrl: string;
7
- readonly entriesUrl: string;
8
- readonly manifestUrl: string;
9
- };
10
- export type ArtifactKind = "manifest" | "entries" | "lancedb-index";
11
- export type ArtifactDescriptor = {
12
- kind: ArtifactKind;
13
- path: string;
14
- digest: string;
15
- bytes: number;
16
- format?: string;
17
- rows?: number;
18
- [key: string]: unknown;
19
- };
20
- export type CatalogReleaseMetadata = {
21
- version: string;
22
- digest: string;
23
- artifacts: ArtifactDescriptor[];
24
- };
25
- export declare function parseCatalogReleaseMetadata(value: unknown): CatalogReleaseMetadata;
26
- export declare function catalogArtifactUrl(baseUrl: string | URL, descriptor: ArtifactDescriptor): string;
27
- export declare function catalogArtifact(metadata: CatalogReleaseMetadata, kind: ArtifactKind): ArtifactDescriptor;
28
- //# sourceMappingURL=artifacts.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/catalog/artifacts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAOrF,eAAO,MAAM,eAAe;;;;;;;CAOlB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,eAAe,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,kBAAkB,EAAE,CAAC;CACjC,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CAgBlF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,EAAE,UAAU,EAAE,kBAAkB,GAAG,MAAM,CAEhG;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,sBAAsB,EAChC,IAAI,EAAE,YAAY,GACjB,kBAAkB,CAMpB"}
@@ -1,89 +0,0 @@
1
- import { CHARTCOACH_DEFAULTS } from "./chartcoach-defaults.js";
2
- import { CatalogError } from "./errors.js";
3
- export { CHARTCOACH_DEFAULTS } from "./chartcoach-defaults.js";
4
- const defaultCatalogReleaseRootUrl = new URL(`catalog/releases/${CHARTCOACH_DEFAULTS.catalogVersion}/${CHARTCOACH_DEFAULTS.catalogDigest}/`, `${CHARTCOACH_DEFAULTS.catalogArtifactBaseUrl}/`).toString();
5
- export const DEFAULT_CATALOG = {
6
- version: CHARTCOACH_DEFAULTS.catalogVersion,
7
- digest: CHARTCOACH_DEFAULTS.catalogDigest,
8
- releaseRootUrl: defaultCatalogReleaseRootUrl,
9
- metadataUrl: new URL("metadata.json", defaultCatalogReleaseRootUrl).toString(),
10
- entriesUrl: new URL("entries.parquet", defaultCatalogReleaseRootUrl).toString(),
11
- manifestUrl: new URL("MANIFEST.md", defaultCatalogReleaseRootUrl).toString(),
12
- };
13
- export function parseCatalogReleaseMetadata(value) {
14
- if (!isRecord(value)) {
15
- throw new CatalogError("Catalog release metadata must be an object.");
16
- }
17
- const artifacts = value.artifacts;
18
- if (!Array.isArray(artifacts)) {
19
- throw new CatalogError("Catalog release metadata artifacts must be a list.");
20
- }
21
- const metadata = {
22
- version: requiredString(value, "version"),
23
- digest: requiredString(value, "digest"),
24
- artifacts: artifacts.map(parseArtifactDescriptor),
25
- };
26
- catalogArtifact(metadata, "manifest");
27
- catalogArtifact(metadata, "entries");
28
- return metadata;
29
- }
30
- export function catalogArtifactUrl(baseUrl, descriptor) {
31
- return new URL(descriptor.path, baseUrl).toString();
32
- }
33
- export function catalogArtifact(metadata, kind) {
34
- const descriptor = metadata.artifacts.find((item) => item.kind === kind);
35
- if (!descriptor) {
36
- throw new CatalogError(`Catalog release metadata is missing a ${kind} artifact.`);
37
- }
38
- return descriptor;
39
- }
40
- function parseArtifactDescriptor(value) {
41
- if (!isRecord(value)) {
42
- throw new CatalogError("Catalog artifact descriptor must be an object.");
43
- }
44
- const kind = value.kind;
45
- if (kind !== "manifest" && kind !== "entries" && kind !== "lancedb-index") {
46
- throw new CatalogError(`Unsupported catalog artifact kind: ${String(kind)}`);
47
- }
48
- const path = requiredString(value, "path");
49
- validateRelativePath(path);
50
- const descriptor = {
51
- kind,
52
- path,
53
- digest: requiredString(value, "digest"),
54
- bytes: requiredInteger(value, "bytes"),
55
- };
56
- if (typeof value.format === "string")
57
- descriptor.format = value.format;
58
- if (typeof value.rows === "number" && Number.isInteger(value.rows)) {
59
- descriptor.rows = value.rows;
60
- }
61
- for (const [key, rawValue] of Object.entries(value)) {
62
- if (!["kind", "path", "digest", "bytes", "format", "rows"].includes(key)) {
63
- descriptor[key] = rawValue;
64
- }
65
- }
66
- return descriptor;
67
- }
68
- function validateRelativePath(path) {
69
- if (path.startsWith("/") || path.split("/").includes("..")) {
70
- throw new CatalogError(`Catalog artifact path must be relative: ${path}`);
71
- }
72
- }
73
- function requiredString(value, key) {
74
- const raw = value[key];
75
- if (typeof raw !== "string" || raw.length === 0) {
76
- throw new CatalogError(`Catalog release metadata ${key} must be a string.`);
77
- }
78
- return raw;
79
- }
80
- function requiredInteger(value, key) {
81
- const raw = value[key];
82
- if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 0) {
83
- throw new CatalogError(`Catalog release metadata ${key} must be a non-negative integer.`);
84
- }
85
- return raw;
86
- }
87
- function isRecord(value) {
88
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
89
- }
@@ -1,17 +0,0 @@
1
- export type ChartCoachDefaults = {
2
- catalogArtifactBaseUrl: string;
3
- catalogDigest: string;
4
- catalogVersion: string;
5
- guidelineUrlTemplate: string;
6
- indexTopK: number;
7
- lanceDocumentTable: string;
8
- };
9
- export declare const CHARTCOACH_DEFAULTS: {
10
- readonly catalogArtifactBaseUrl: "https://artifacts.chartcoach.dev";
11
- readonly catalogDigest: "7cfd43ee820be252b8ae9058c4c36109a9c8415c6b3a5ff8a9127117b4a10c19";
12
- readonly catalogVersion: "0.1.6";
13
- readonly guidelineUrlTemplate: "https://chartcoach.dev/guidelines/{id}";
14
- readonly indexTopK: 10;
15
- readonly lanceDocumentTable: "catalog_documents";
16
- };
17
- //# sourceMappingURL=chartcoach-defaults.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chartcoach-defaults.d.ts","sourceRoot":"","sources":["../../src/catalog/chartcoach-defaults.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;;;;;CAOO,CAAC"}
@@ -1,8 +0,0 @@
1
- export const CHARTCOACH_DEFAULTS = {
2
- catalogArtifactBaseUrl: "https://artifacts.chartcoach.dev",
3
- catalogDigest: "7cfd43ee820be252b8ae9058c4c36109a9c8415c6b3a5ff8a9127117b4a10c19",
4
- catalogVersion: "0.1.6",
5
- guidelineUrlTemplate: "https://chartcoach.dev/guidelines/{id}",
6
- indexTopK: 10,
7
- lanceDocumentTable: "catalog_documents",
8
- };
@@ -1,4 +0,0 @@
1
- export declare class CatalogError extends Error {
2
- constructor(message: string);
3
- }
4
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/catalog/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B"}
@@ -1,6 +0,0 @@
1
- export class CatalogError extends Error {
2
- constructor(message) {
3
- super(message);
4
- this.name = "CatalogError";
5
- }
6
- }
@@ -1,9 +0,0 @@
1
- export type CatalogLabel = {
2
- value: string;
3
- family: string;
4
- category: string;
5
- modifier?: string;
6
- };
7
- export declare function parseLabel(value: unknown, context?: string): CatalogLabel;
8
- export declare function normalizeLabel(value: unknown, context?: string): string;
9
- //# sourceMappingURL=labels.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/catalog/labels.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,SAAU,GAAG,YAAY,CAoB1E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,SAAU,GAAG,MAAM,CAExE"}
@@ -1,23 +0,0 @@
1
- import { CatalogError } from "./errors.js";
2
- export function parseLabel(value, context = "label") {
3
- if (typeof value !== "string") {
4
- throw new CatalogError(`${context} must be a string.`);
5
- }
6
- const parts = value
7
- .trim()
8
- .split(":")
9
- .map((part) => part.trim());
10
- if ((parts.length !== 2 && parts.length !== 3) || parts.some((part) => part.length === 0)) {
11
- throw new CatalogError(`${context} must use <family>:<category> or <family>:<category>:<modifier>.`);
12
- }
13
- const [family, category, modifier] = parts;
14
- return {
15
- value: modifier === undefined ? `${family}:${category}` : `${family}:${category}:${modifier}`,
16
- family,
17
- category,
18
- modifier,
19
- };
20
- }
21
- export function normalizeLabel(value, context = "label") {
22
- return parseLabel(value, context).value;
23
- }
@@ -1,15 +0,0 @@
1
- import { Catalog } from "./model.js";
2
- import type { CatalogManifest } from "./manifest.js";
3
- export type AsyncBuffer = {
4
- byteLength: number;
5
- slice(start: number, end?: number): ArrayBuffer | Promise<ArrayBuffer>;
6
- };
7
- export type ParquetBytes = ArrayBuffer | ArrayBufferView;
8
- export type CatalogBytes = ParquetBytes | AsyncBuffer;
9
- export type LoadCatalogInput = CatalogBytes | {
10
- entries: CatalogBytes;
11
- manifest?: CatalogManifest;
12
- manifestText?: string;
13
- };
14
- export declare function loadCatalog(input: LoadCatalogInput): Promise<Catalog>;
15
- //# sourceMappingURL=load-parquet-core.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"load-parquet-core.d.ts","sourceRoot":"","sources":["../../src/catalog/load-parquet-core.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAkB,MAAM,SAAS,CAAC;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACxE,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,eAAe,CAAC;AACzD,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,WAAW,CAAC;AACtD,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ;IACE,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAWN,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAiB3E"}
@@ -1,45 +0,0 @@
1
- import { parquetReadObjects } from "hyparquet";
2
- import { compressors } from "hyparquet-compressors";
3
- import { Catalog } from "./model.js";
4
- import { CatalogError } from "./errors.js";
5
- import { parseCatalogManifest } from "./manifest.js";
6
- import { requireGuidelineFromWire } from "./wire.js";
7
- function normalizeParquetBytes(bytes) {
8
- if (bytes instanceof ArrayBuffer)
9
- return bytes;
10
- // TypedArray/DataView may be a view into a larger ArrayBuffer (or SharedArrayBuffer).
11
- // Copy to a standalone ArrayBuffer covering exactly the view range.
12
- const u8 = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13
- return u8.slice().buffer;
14
- }
15
- export async function loadCatalog(input) {
16
- const { entries, manifest } = resolveLoadCatalogInput(input);
17
- const normalizedFile = entries instanceof ArrayBuffer || ArrayBuffer.isView(entries)
18
- ? normalizeParquetBytes(entries)
19
- : entries;
20
- const rows = (await parquetReadObjects({
21
- file: normalizedFile,
22
- compressors,
23
- }));
24
- const guidelines = rows.map((row, index) => requireGuidelineFromWire(row, `parquet row ${index}`));
25
- return new Catalog(guidelines, { manifest });
26
- }
27
- function resolveLoadCatalogInput(input) {
28
- if (isCatalogBytes(input))
29
- return { entries: input };
30
- if (input.manifest && input.manifestText !== undefined) {
31
- throw new CatalogError("Pass manifest or manifestText, not both.");
32
- }
33
- return {
34
- entries: input.entries,
35
- manifest: input.manifestText === undefined ? input.manifest : parseCatalogManifest(input.manifestText),
36
- };
37
- }
38
- function isCatalogBytes(value) {
39
- return (value instanceof ArrayBuffer ||
40
- ArrayBuffer.isView(value) ||
41
- (typeof value === "object" &&
42
- value !== null &&
43
- typeof value.byteLength === "number" &&
44
- typeof value.slice === "function"));
45
- }
@@ -1,15 +0,0 @@
1
- import type { Guideline } from "./model.js";
2
- export declare const REQUIRED_MANIFEST_HEADINGS: readonly ["Section Roles", "Label Families"];
3
- export type ManifestDefinition = {
4
- name: string;
5
- description: string;
6
- examples: string[];
7
- };
8
- export type CatalogManifest = {
9
- markdown: string;
10
- sectionRoles: Record<string, ManifestDefinition>;
11
- labelFamilies: Record<string, ManifestDefinition>;
12
- };
13
- export declare function parseCatalogManifest(markdown: string): CatalogManifest;
14
- export declare function validateManifestCoverage(guidelines: readonly Guideline[], manifest: CatalogManifest): void;
15
- //# sourceMappingURL=manifest.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/catalog/manifest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,eAAO,MAAM,0BAA0B,8CAA+C,CAAC;AAEvF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACjD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CACnD,CAAC;AAKF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CA6FtE;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,QAAQ,EAAE,eAAe,QAmC1B"}