@grove-dev/core 0.3.1 → 0.3.4
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 +16 -142
- package/dist/build-data.d.ts.map +1 -1
- package/dist/build-data.js +13 -4
- package/dist/build-data.js.map +1 -1
- package/dist/contributors.d.ts +3 -0
- package/dist/contributors.d.ts.map +1 -1
- package/dist/contributors.js +46 -36
- package/dist/contributors.js.map +1 -1
- package/dist/directory-client.d.ts +11 -0
- package/dist/directory-client.d.ts.map +1 -0
- package/dist/directory-client.js +11 -0
- package/dist/directory-client.js.map +1 -0
- package/dist/directory-display.d.ts +27 -0
- package/dist/directory-display.d.ts.map +1 -0
- package/dist/directory-display.js +104 -0
- package/dist/directory-display.js.map +1 -0
- package/dist/directory-format.d.ts +29 -0
- package/dist/directory-format.d.ts.map +1 -0
- package/dist/directory-format.js +72 -0
- package/dist/directory-format.js.map +1 -0
- package/dist/directory-lenses.d.ts +59 -0
- package/dist/directory-lenses.d.ts.map +1 -0
- package/dist/directory-lenses.js +199 -0
- package/dist/directory-lenses.js.map +1 -0
- package/dist/directory-repo.d.ts +20 -0
- package/dist/directory-repo.d.ts.map +1 -0
- package/dist/directory-repo.js +31 -0
- package/dist/directory-repo.js.map +1 -0
- package/dist/directory-scores.d.ts +37 -0
- package/dist/directory-scores.d.ts.map +1 -0
- package/dist/directory-scores.js +76 -0
- package/dist/directory-scores.js.map +1 -0
- package/dist/directory-search.d.ts +119 -0
- package/dist/directory-search.d.ts.map +1 -0
- package/dist/directory-search.js +398 -0
- package/dist/directory-search.js.map +1 -0
- package/dist/directory-taxonomy.d.ts +19 -0
- package/dist/directory-taxonomy.d.ts.map +1 -0
- package/dist/directory-taxonomy.js +36 -0
- package/dist/directory-taxonomy.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -6
- package/dist/index.js.map +1 -1
- package/dist/llms.js +1 -1
- package/dist/llms.js.map +1 -1
- package/dist/prepare.d.ts +20 -0
- package/dist/prepare.d.ts.map +1 -0
- package/dist/prepare.js +75 -0
- package/dist/prepare.js.map +1 -0
- package/dist/schema.d.ts +56 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +38 -0
- package/dist/schema.js.map +1 -1
- package/dist/site-artifacts.d.ts +15 -0
- package/dist/site-artifacts.d.ts.map +1 -0
- package/dist/site-artifacts.js +122 -0
- package/dist/site-artifacts.js.map +1 -0
- package/dist/validate.js +4 -2
- package/dist/validate.js.map +1 -1
- package/package.json +9 -2
- package/dist/build-data.test.d.ts +0 -2
- package/dist/build-data.test.d.ts.map +0 -1
- package/dist/build-data.test.js +0 -196
- package/dist/build-data.test.js.map +0 -1
- package/dist/contributors.test.d.ts +0 -2
- package/dist/contributors.test.d.ts.map +0 -1
- package/dist/contributors.test.js +0 -79
- package/dist/contributors.test.js.map +0 -1
- package/dist/decisions.test.d.ts +0 -2
- package/dist/decisions.test.d.ts.map +0 -1
- package/dist/decisions.test.js +0 -205
- package/dist/decisions.test.js.map +0 -1
- package/dist/enrich.test.d.ts +0 -2
- package/dist/enrich.test.d.ts.map +0 -1
- package/dist/enrich.test.js +0 -105
- package/dist/enrich.test.js.map +0 -1
- package/dist/github-client.test.d.ts +0 -2
- package/dist/github-client.test.d.ts.map +0 -1
- package/dist/github-client.test.js +0 -176
- package/dist/github-client.test.js.map +0 -1
- package/dist/github.test.d.ts +0 -2
- package/dist/github.test.d.ts.map +0 -1
- package/dist/github.test.js +0 -73
- package/dist/github.test.js.map +0 -1
- package/dist/health.test.d.ts +0 -2
- package/dist/health.test.d.ts.map +0 -1
- package/dist/health.test.js +0 -183
- package/dist/health.test.js.map +0 -1
- package/dist/importer.test.d.ts +0 -2
- package/dist/importer.test.d.ts.map +0 -1
- package/dist/importer.test.js +0 -173
- package/dist/importer.test.js.map +0 -1
- package/dist/llms.test.d.ts +0 -2
- package/dist/llms.test.d.ts.map +0 -1
- package/dist/llms.test.js +0 -39
- package/dist/llms.test.js.map +0 -1
- package/dist/public-api.test.d.ts +0 -2
- package/dist/public-api.test.d.ts.map +0 -1
- package/dist/public-api.test.js +0 -13
- package/dist/public-api.test.js.map +0 -1
- package/dist/sitemap.test.d.ts +0 -2
- package/dist/sitemap.test.d.ts.map +0 -1
- package/dist/sitemap.test.js +0 -35
- package/dist/sitemap.test.js.map +0 -1
- package/dist/slug.test.d.ts +0 -2
- package/dist/slug.test.d.ts.map +0 -1
- package/dist/slug.test.js +0 -101
- package/dist/slug.test.js.map +0 -1
- package/dist/validate.test.d.ts +0 -2
- package/dist/validate.test.d.ts.map +0 -1
- package/dist/validate.test.js +0 -272
- package/dist/validate.test.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grove-dev/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Headless engine for Grove: resource schema, config, importers, validators, taxonomy, sitemap, llms.txt, and the data build pipeline.",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.12.0"
|
|
9
|
+
},
|
|
7
10
|
"keywords": [
|
|
8
11
|
"grove",
|
|
9
12
|
"directory",
|
|
@@ -31,6 +34,10 @@
|
|
|
31
34
|
".": {
|
|
32
35
|
"types": "./dist/index.d.ts",
|
|
33
36
|
"import": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./directory": {
|
|
39
|
+
"types": "./dist/directory-client.d.ts",
|
|
40
|
+
"import": "./dist/directory-client.js"
|
|
34
41
|
}
|
|
35
42
|
},
|
|
36
43
|
"files": [
|
|
@@ -43,7 +50,7 @@
|
|
|
43
50
|
"zod": "^4.4.3"
|
|
44
51
|
},
|
|
45
52
|
"scripts": {
|
|
46
|
-
"build": "tsc -p tsconfig.json",
|
|
53
|
+
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
47
54
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
48
55
|
"test": "cd ../.. && pnpm exec vitest run --project unit packages/core"
|
|
49
56
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-data.test.d.ts","sourceRoot":"","sources":["../src/build-data.test.ts"],"names":[],"mappings":""}
|
package/dist/build-data.test.js
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @grove-dev/core — build-data.ts unit tests.
|
|
3
|
-
*
|
|
4
|
-
* The brief calls out `applyDecision` as a load-bearing function:
|
|
5
|
-
* - When a project record has no existing health block but a
|
|
6
|
-
* decisions.yml override exists, the function fabricates an
|
|
7
|
-
* "unknown" health block (via `classifyHealth(slug)`) and
|
|
8
|
-
* writes the decision's visibility into it. This is the
|
|
9
|
-
* preserved (vs. removed) decision from the audit, and the
|
|
10
|
-
* test pins the chosen behavior.
|
|
11
|
-
* - For resource-hub / ecosystem-map records (no `health`
|
|
12
|
-
* block), the decision visibility goes onto the record's
|
|
13
|
-
* top-level `visibility` field.
|
|
14
|
-
*
|
|
15
|
-
* `generate` itself is tested via filesystem round-trip with a
|
|
16
|
-
* real tmpdir.
|
|
17
|
-
*/
|
|
18
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
19
|
-
import { mkdtemp, mkdir, writeFile, readFile, rm } from "node:fs/promises";
|
|
20
|
-
import { tmpdir } from "node:os";
|
|
21
|
-
import { join } from "node:path";
|
|
22
|
-
import { generate } from "./build-data.js";
|
|
23
|
-
import { toIndexRecord } from "./schema.js";
|
|
24
|
-
function makeProject(overrides = {}) {
|
|
25
|
-
return {
|
|
26
|
-
kind: "project",
|
|
27
|
-
slug: "demo",
|
|
28
|
-
name: "Demo",
|
|
29
|
-
description: "A demo",
|
|
30
|
-
category: "tools",
|
|
31
|
-
tags: [],
|
|
32
|
-
links: {},
|
|
33
|
-
curation: { reviewed: false, labels: [], lenses: [] },
|
|
34
|
-
scores: {},
|
|
35
|
-
visibility: "keep",
|
|
36
|
-
...overrides,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
describe("toIndexRecord — fabrication behaviour (applyDecision shape)", () => {
|
|
40
|
-
it("a project record with no health block + a decision.decision.visibility of 'hide' fabricates a health block carrying that visibility", async () => {
|
|
41
|
-
// This test pins the PRESERVED audit decision: when a
|
|
42
|
-
// project record has no health block but a decisions.yml
|
|
43
|
-
// override exists, applyDecision (called inside generate)
|
|
44
|
-
// fabricates a health block via classifyHealth(slug) (the
|
|
45
|
-
// canonical unknown shape) and overwrites visibility.
|
|
46
|
-
// The fabrication must keep the unknown status / maturity /
|
|
47
|
-
// tier, and only visibility changes.
|
|
48
|
-
//
|
|
49
|
-
// We exercise the public `toIndexRecord` path, which is what
|
|
50
|
-
// generate ultimately calls, with a record that has been
|
|
51
|
-
// through the applyDecision path (simulate that here by
|
|
52
|
-
// hand).
|
|
53
|
-
const project = makeProject();
|
|
54
|
-
// The "fabricated" record is what applyDecision produces:
|
|
55
|
-
const fabricated = {
|
|
56
|
-
...project,
|
|
57
|
-
health: {
|
|
58
|
-
status: "unknown",
|
|
59
|
-
maturity: "unknown",
|
|
60
|
-
tier: "experimental",
|
|
61
|
-
visibility: "hide",
|
|
62
|
-
cleanupCandidate: false,
|
|
63
|
-
staleReason: null,
|
|
64
|
-
confidence: "low",
|
|
65
|
-
reasons: ["No GitHub metadata available"],
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
const idx = toIndexRecord(fabricated);
|
|
69
|
-
// The index record's `visibility` (read by the index
|
|
70
|
-
// filter) must reflect the decision override, not the
|
|
71
|
-
// fabricated default of "keep".
|
|
72
|
-
expect(idx.visibility).toBe("hide");
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
describe("generate — filesystem round-trip", () => {
|
|
76
|
-
let cwd;
|
|
77
|
-
beforeEach(async () => {
|
|
78
|
-
cwd = await mkdtemp(join(tmpdir(), "grove-build-test-"));
|
|
79
|
-
await mkdir(join(cwd, "data", "records"), { recursive: true });
|
|
80
|
-
await mkdir(join(cwd, "data", "generated"), { recursive: true });
|
|
81
|
-
// grove.config.ts stub so loadConfig() doesn't throw.
|
|
82
|
-
await writeFile(join(cwd, "grove.config.ts"), [
|
|
83
|
-
"export default {",
|
|
84
|
-
" site: { name: 'test', tagline: 'test' },",
|
|
85
|
-
" paths: { recordsDir: 'data/records', generatedDir: 'data/generated', health: 'data/health.yml', decisions: 'data/decisions.yml' },",
|
|
86
|
-
" blueprint: 'project-directory',",
|
|
87
|
-
" nav: [],",
|
|
88
|
-
" theme: {},",
|
|
89
|
-
" integrations: {},",
|
|
90
|
-
"};",
|
|
91
|
-
].join("\n"));
|
|
92
|
-
});
|
|
93
|
-
afterEach(async () => {
|
|
94
|
-
await rm(cwd, { recursive: true, force: true });
|
|
95
|
-
});
|
|
96
|
-
it("generates records.full.json, records.index.json, and records.json (alias) from a real records dir", async () => {
|
|
97
|
-
await writeFile(join(cwd, "data", "records", "demo.yml"), [
|
|
98
|
-
"kind: project",
|
|
99
|
-
"slug: demo",
|
|
100
|
-
"name: Demo",
|
|
101
|
-
"description: a demo",
|
|
102
|
-
"category: tools",
|
|
103
|
-
"links: {}",
|
|
104
|
-
"curation: { reviewed: false, labels: [], lenses: [] }",
|
|
105
|
-
"scores: {}",
|
|
106
|
-
].join("\n"));
|
|
107
|
-
const r = await generate(cwd);
|
|
108
|
-
expect(r.totalRecords).toBe(1);
|
|
109
|
-
expect(r.visibleRecords).toBe(1);
|
|
110
|
-
// The three files were all written.
|
|
111
|
-
const full = JSON.parse(await readFile(r.fullPath, "utf8"));
|
|
112
|
-
const idx = JSON.parse(await readFile(r.indexPath, "utf8"));
|
|
113
|
-
const alias = JSON.parse(await readFile(r.aliasPath, "utf8"));
|
|
114
|
-
expect(full.totalRecords).toBe(1);
|
|
115
|
-
expect(idx.totalRecords).toBe(1);
|
|
116
|
-
// Alias == full payload.
|
|
117
|
-
expect(alias).toEqual(full);
|
|
118
|
-
});
|
|
119
|
-
it("applies a decisions.yml override of visibility='hide' to a project record without a health block", async () => {
|
|
120
|
-
// The brief's load-bearing test: a project record that has
|
|
121
|
-
// no health block, plus a decisions.yml that says
|
|
122
|
-
// "hide" for that slug, must produce a hidden record in the
|
|
123
|
-
// full payload AND exclude it from the index payload.
|
|
124
|
-
await writeFile(join(cwd, "data", "records", "hidden.yml"), [
|
|
125
|
-
"kind: project",
|
|
126
|
-
"slug: hidden",
|
|
127
|
-
"name: Hidden",
|
|
128
|
-
"description: should be hidden by decisions.yml",
|
|
129
|
-
"category: tools",
|
|
130
|
-
"links: {}",
|
|
131
|
-
"curation: { reviewed: false, labels: [], lenses: [] }",
|
|
132
|
-
"scores: {}",
|
|
133
|
-
].join("\n"));
|
|
134
|
-
await writeFile(join(cwd, "data", "decisions.yml"), [
|
|
135
|
-
"decisions:",
|
|
136
|
-
" - id: hidden",
|
|
137
|
-
" decision:",
|
|
138
|
-
" visibility: hide",
|
|
139
|
-
" reason: tested override",
|
|
140
|
-
].join("\n"));
|
|
141
|
-
const r = await generate(cwd);
|
|
142
|
-
expect(r.totalRecords).toBe(1); // full payload still includes the record
|
|
143
|
-
expect(r.visibleRecords).toBe(0); // but the index payload omits it
|
|
144
|
-
const full = JSON.parse(await readFile(r.fullPath, "utf8"));
|
|
145
|
-
const hidden = full.records.find((rec) => rec.slug === "hidden");
|
|
146
|
-
expect(hidden).toBeDefined();
|
|
147
|
-
// The fabrication: a health block exists, and its visibility
|
|
148
|
-
// is "hide" (the decision override), not "keep" (the
|
|
149
|
-
// fabricated default).
|
|
150
|
-
expect(hidden?.health?.visibility).toBe("hide");
|
|
151
|
-
});
|
|
152
|
-
it("writes site-config.json with stats derived from the records", async () => {
|
|
153
|
-
await writeFile(join(cwd, "data", "records", "coolify.yml"), [
|
|
154
|
-
"kind: project",
|
|
155
|
-
"slug: coolify",
|
|
156
|
-
"name: Coolify",
|
|
157
|
-
"description: self-hosting",
|
|
158
|
-
"category: tools",
|
|
159
|
-
"stacks: [typescript]",
|
|
160
|
-
"links: {}",
|
|
161
|
-
"curation: { reviewed: false, labels: [], lenses: [] }",
|
|
162
|
-
"scores: {}",
|
|
163
|
-
].join("\n"));
|
|
164
|
-
const r = await generate(cwd);
|
|
165
|
-
const sitePath = join(cwd, "data", "generated", "site-config.json");
|
|
166
|
-
const site = JSON.parse(await readFile(sitePath, "utf8"));
|
|
167
|
-
expect(site.stats.totalRecords).toBe(1);
|
|
168
|
-
expect(site.stats.totalApps).toBe(1);
|
|
169
|
-
});
|
|
170
|
-
it("writes configured taxonomy names to site-config.json", async () => {
|
|
171
|
-
await mkdir(join(cwd, "data", "taxonomy"), { recursive: true });
|
|
172
|
-
await writeFile(join(cwd, "data", "taxonomy", "categories.yml"), [
|
|
173
|
-
"- id: news",
|
|
174
|
-
" name: News and Magazine",
|
|
175
|
-
"- id: tools",
|
|
176
|
-
" name: Developer Tools",
|
|
177
|
-
].join("\n"));
|
|
178
|
-
await writeFile(join(cwd, "data", "records", "reader.yml"), [
|
|
179
|
-
"kind: project",
|
|
180
|
-
"slug: reader",
|
|
181
|
-
"name: Reader",
|
|
182
|
-
"description: news reader",
|
|
183
|
-
"category: news",
|
|
184
|
-
"links: {}",
|
|
185
|
-
"curation: { reviewed: false, labels: [], lenses: [] }",
|
|
186
|
-
"scores: {}",
|
|
187
|
-
].join("\n"));
|
|
188
|
-
await generate(cwd);
|
|
189
|
-
const site = JSON.parse(await readFile(join(cwd, "data", "generated", "site-config.json"), "utf8"));
|
|
190
|
-
expect(site.taxonomy?.categories).toEqual([
|
|
191
|
-
{ id: "news", name: "News and Magazine" },
|
|
192
|
-
{ id: "tools", name: "Developer Tools" },
|
|
193
|
-
]);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
//# sourceMappingURL=build-data.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-data.test.js","sourceRoot":"","sources":["../src/build-data.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contributors.test.d.ts","sourceRoot":"","sources":["../src/contributors.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { syncContributors } from "./contributors.js";
|
|
6
|
-
describe("syncContributors", () => {
|
|
7
|
-
let cwd;
|
|
8
|
-
beforeEach(async () => {
|
|
9
|
-
cwd = await mkdtemp(join(tmpdir(), "grove-contributors-"));
|
|
10
|
-
await mkdir(join(cwd, "data", "generated"), { recursive: true });
|
|
11
|
-
await writeFile(join(cwd, "data", "generated", "records.index.json"), JSON.stringify({
|
|
12
|
-
records: [
|
|
13
|
-
{
|
|
14
|
-
kind: "project",
|
|
15
|
-
slug: "one",
|
|
16
|
-
repoUrl: "https://github.com/acme/one",
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
kind: "project",
|
|
20
|
-
slug: "two",
|
|
21
|
-
github: { fullName: "acme/two" },
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
}));
|
|
25
|
-
});
|
|
26
|
-
afterEach(async () => {
|
|
27
|
-
await rm(cwd, { recursive: true, force: true });
|
|
28
|
-
});
|
|
29
|
-
it("aggregates contributors across repositories and writes generated JSON", async () => {
|
|
30
|
-
const fetchImpl = async (input) => {
|
|
31
|
-
const url = String(input);
|
|
32
|
-
const contributors = url.includes("/one/")
|
|
33
|
-
? [
|
|
34
|
-
{
|
|
35
|
-
login: "alice",
|
|
36
|
-
avatar_url: "https://avatars.example/alice",
|
|
37
|
-
html_url: "https://github.com/alice",
|
|
38
|
-
contributions: 3,
|
|
39
|
-
},
|
|
40
|
-
]
|
|
41
|
-
: [
|
|
42
|
-
{
|
|
43
|
-
login: "alice",
|
|
44
|
-
avatar_url: "https://avatars.example/alice",
|
|
45
|
-
html_url: "https://github.com/alice",
|
|
46
|
-
contributions: 4,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
login: "bob",
|
|
50
|
-
avatar_url: "https://avatars.example/bob",
|
|
51
|
-
html_url: "https://github.com/bob",
|
|
52
|
-
contributions: 2,
|
|
53
|
-
},
|
|
54
|
-
];
|
|
55
|
-
return new Response(JSON.stringify(contributors), { status: 200 });
|
|
56
|
-
};
|
|
57
|
-
const result = await syncContributors({ cwd, fetchImpl, generatedAt: "2026-06-25T00:00:00.000Z" });
|
|
58
|
-
const output = JSON.parse(await readFile(result.outputPath, "utf8"));
|
|
59
|
-
expect(result.repositories).toBe(2);
|
|
60
|
-
expect(output).toEqual({
|
|
61
|
-
generatedAt: "2026-06-25T00:00:00.000Z",
|
|
62
|
-
contributors: [
|
|
63
|
-
{
|
|
64
|
-
username: "alice",
|
|
65
|
-
avatarUrl: "https://avatars.example/alice",
|
|
66
|
-
profileUrl: "https://github.com/alice",
|
|
67
|
-
contributions: 7,
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
username: "bob",
|
|
71
|
-
avatarUrl: "https://avatars.example/bob",
|
|
72
|
-
profileUrl: "https://github.com/bob",
|
|
73
|
-
contributions: 2,
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
//# sourceMappingURL=contributors.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contributors.test.js","sourceRoot":"","sources":["../src/contributors.test.ts"],"names":[],"mappings":"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"}
|
package/dist/decisions.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decisions.test.d.ts","sourceRoot":"","sources":["../src/decisions.test.ts"],"names":[],"mappings":""}
|
package/dist/decisions.test.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @grove-dev/core — decisions + cleanup-stale unit tests.
|
|
3
|
-
*
|
|
4
|
-
* `pickCleanupCandidates` is a pure predicate and trivially testable
|
|
5
|
-
* without any I/O. `cleanupStale` reads from the filesystem and
|
|
6
|
-
* writes `data/generated/cleanup-report.json` — the tests below use
|
|
7
|
-
* `tmpdir()` (per the brief — no fs mocks) to spin up a real
|
|
8
|
-
* project tree and exercise both the happy path and the missing-
|
|
9
|
-
* files-dir / invalid-YAML silent-fallback paths.
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
12
|
-
import { mkdir, writeFile, readFile, rm, mkdtemp } from "node:fs/promises";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
|
-
import { join } from "node:path";
|
|
15
|
-
import { pickCleanupCandidates, cleanupStale } from "./decisions.js";
|
|
16
|
-
function makeProjectResource(overrides = {}) {
|
|
17
|
-
return {
|
|
18
|
-
kind: "project",
|
|
19
|
-
slug: "demo",
|
|
20
|
-
name: "Demo",
|
|
21
|
-
description: "A demo project",
|
|
22
|
-
category: "tools",
|
|
23
|
-
tags: [],
|
|
24
|
-
links: { github: "https://github.com/owner/repo" },
|
|
25
|
-
curation: { reviewed: false, labels: [], lenses: [] },
|
|
26
|
-
scores: {},
|
|
27
|
-
visibility: "keep",
|
|
28
|
-
...overrides,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
describe("pickCleanupCandidates — pure predicate", () => {
|
|
32
|
-
it("returns records whose health.cleanupCandidate is true", () => {
|
|
33
|
-
const r = makeProjectResource({
|
|
34
|
-
health: {
|
|
35
|
-
status: "stale",
|
|
36
|
-
maturity: "useful",
|
|
37
|
-
tier: "listed",
|
|
38
|
-
visibility: "keep",
|
|
39
|
-
cleanupCandidate: true,
|
|
40
|
-
staleReason: "no_commits_365_days",
|
|
41
|
-
confidence: "high",
|
|
42
|
-
reasons: [],
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
expect(pickCleanupCandidates([r])).toEqual([r]);
|
|
46
|
-
});
|
|
47
|
-
it("returns records whose health.status is 'unknown' or 'needs_review'", () => {
|
|
48
|
-
const a = makeProjectResource({ slug: "a", health: { status: "unknown", maturity: "unknown", tier: "experimental", visibility: "keep", cleanupCandidate: false, staleReason: null, confidence: "low", reasons: [] } });
|
|
49
|
-
const b = makeProjectResource({ slug: "b", health: { status: "needs_review", maturity: "useful", tier: "listed", visibility: "keep", cleanupCandidate: false, staleReason: null, confidence: "medium", reasons: [] } });
|
|
50
|
-
const c = makeProjectResource({ slug: "c", health: { status: "active", maturity: "mature", tier: "curated", visibility: "keep", cleanupCandidate: false, staleReason: null, confidence: "high", reasons: [] } });
|
|
51
|
-
const out = pickCleanupCandidates([a, b, c]);
|
|
52
|
-
expect(out.map((r) => r.slug).sort()).toEqual(["a", "b"]);
|
|
53
|
-
});
|
|
54
|
-
it("returns an empty array when no records need cleanup", () => {
|
|
55
|
-
const a = makeProjectResource({ health: { status: "active", maturity: "mature", tier: "curated", visibility: "keep", cleanupCandidate: false, staleReason: null, confidence: "high", reasons: [] } });
|
|
56
|
-
const b = makeProjectResource({ slug: "b", health: { status: "mature", maturity: "mature", tier: "curated", visibility: "keep", cleanupCandidate: false, staleReason: null, confidence: "high", reasons: [] } });
|
|
57
|
-
expect(pickCleanupCandidates([a, b])).toEqual([]);
|
|
58
|
-
});
|
|
59
|
-
it("treats a record with no health block as not-a-candidate", () => {
|
|
60
|
-
const r = makeProjectResource(); // no health
|
|
61
|
-
expect(pickCleanupCandidates([r])).toEqual([]);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
describe("cleanupStale — filesystem integration (real tmpdir)", () => {
|
|
65
|
-
let cwd;
|
|
66
|
-
beforeEach(async () => {
|
|
67
|
-
cwd = await mkdtemp(join(tmpdir(), "grove-cleanup-test-"));
|
|
68
|
-
await mkdir(join(cwd, "data", "records"), { recursive: true });
|
|
69
|
-
await mkdir(join(cwd, "data", "generated"), { recursive: true });
|
|
70
|
-
// cleanupStale → loadConfig → jiti-resolves grove.config.ts.
|
|
71
|
-
// Write a minimal valid stub so the config loader doesn't throw.
|
|
72
|
-
await writeFile(join(cwd, "grove.config.ts"), [
|
|
73
|
-
"export default {",
|
|
74
|
-
" site: { name: 'test', tagline: 'test' },",
|
|
75
|
-
" paths: { recordsDir: 'data/records', generatedDir: 'data/generated', health: 'data/health.yml', decisions: 'data/decisions.yml' },",
|
|
76
|
-
" blueprint: 'project-directory',",
|
|
77
|
-
" nav: [],",
|
|
78
|
-
" theme: {},",
|
|
79
|
-
" integrations: {},",
|
|
80
|
-
"};",
|
|
81
|
-
].join("\n"));
|
|
82
|
-
});
|
|
83
|
-
afterEach(async () => {
|
|
84
|
-
await rm(cwd, { recursive: true, force: true });
|
|
85
|
-
});
|
|
86
|
-
it("reads records, applies pickCleanupCandidates, writes cleanup-report.json", async () => {
|
|
87
|
-
// One record whose health status is 'unknown' (cleanup candidate)
|
|
88
|
-
// and one whose status is 'active' (not a candidate). The slug
|
|
89
|
-
// field is in the YAML (matches the schema), and the file name
|
|
90
|
-
// also carries it (cleanupStale overwrites normalized.slug
|
|
91
|
-
// with the filename at line 88 — pinning that contract).
|
|
92
|
-
await writeFile(join(cwd, "data", "records", "stale-one.yml"), [
|
|
93
|
-
"kind: project",
|
|
94
|
-
"slug: stale-one",
|
|
95
|
-
"name: Stale One",
|
|
96
|
-
"description: an old project",
|
|
97
|
-
"category: tools",
|
|
98
|
-
"links: { github: 'https://github.com/owner/repo' }",
|
|
99
|
-
"curation: { reviewed: false, labels: [], lenses: [] }",
|
|
100
|
-
"scores: {}",
|
|
101
|
-
"health:",
|
|
102
|
-
" status: unknown",
|
|
103
|
-
" maturity: unknown",
|
|
104
|
-
" tier: experimental",
|
|
105
|
-
" visibility: keep",
|
|
106
|
-
" cleanupCandidate: false",
|
|
107
|
-
" staleReason: null",
|
|
108
|
-
" confidence: low",
|
|
109
|
-
" reasons: []",
|
|
110
|
-
].join("\n"));
|
|
111
|
-
await writeFile(join(cwd, "data", "records", "fresh.yml"), [
|
|
112
|
-
"kind: project",
|
|
113
|
-
"slug: fresh",
|
|
114
|
-
"name: Fresh",
|
|
115
|
-
"description: an active project",
|
|
116
|
-
"category: tools",
|
|
117
|
-
"links: { github: 'https://github.com/owner/fresh' }",
|
|
118
|
-
"curation: { reviewed: false, labels: [], lenses: [] }",
|
|
119
|
-
"scores: {}",
|
|
120
|
-
"health:",
|
|
121
|
-
" status: active",
|
|
122
|
-
" maturity: mature",
|
|
123
|
-
" tier: curated",
|
|
124
|
-
" visibility: keep",
|
|
125
|
-
" cleanupCandidate: false",
|
|
126
|
-
" staleReason: null",
|
|
127
|
-
" confidence: high",
|
|
128
|
-
" reasons: []",
|
|
129
|
-
].join("\n"));
|
|
130
|
-
const { report, path } = await cleanupStale(cwd);
|
|
131
|
-
expect(report.totalCandidates).toBe(1);
|
|
132
|
-
expect(report.candidates[0]?.slug).toBe("stale-one");
|
|
133
|
-
expect(report.candidates[0]?.status).toBe("unknown");
|
|
134
|
-
expect(report.blueprint).toBe("project-directory");
|
|
135
|
-
// Verify the file was actually written.
|
|
136
|
-
const written = JSON.parse(await readFile(path, "utf8"));
|
|
137
|
-
expect(written.totalCandidates).toBe(1);
|
|
138
|
-
expect(written.candidates[0]?.slug).toBe("stale-one");
|
|
139
|
-
});
|
|
140
|
-
it("skips records whose parsed YAML fails Zod (try/catch on recordsFileSchema.parse)", async () => {
|
|
141
|
-
// The brief calls out the silent try/catch around
|
|
142
|
-
// `recordsFileSchema.parse(raw)` in decisions.ts as audit
|
|
143
|
-
// concern #1. Pin the *current* behaviour (skip, do not
|
|
144
|
-
// throw) so a future change to throw is visible.
|
|
145
|
-
//
|
|
146
|
-
// Note: `parseYaml` itself is NOT in a try/catch — a totally
|
|
147
|
-
// malformed YAML would crash the loop. The silent path the
|
|
148
|
-
// audit flagged is the Zod parse step, not the YAML parse
|
|
149
|
-
// step. So this test uses a YAML file that *parses* but
|
|
150
|
-
// produces a value the Zod schema rejects (e.g. an array
|
|
151
|
-
// where an object is required).
|
|
152
|
-
await writeFile(join(cwd, "data", "records", "broken.yml"),
|
|
153
|
-
// YAML that parses to an array — fails Zod object validation.
|
|
154
|
-
"- this\n- is\n- an\n- array\n");
|
|
155
|
-
// A second, valid record that IS a cleanup candidate.
|
|
156
|
-
await writeFile(join(cwd, "data", "records", "ok.yml"), [
|
|
157
|
-
"kind: project",
|
|
158
|
-
"slug: ok",
|
|
159
|
-
"name: Ok",
|
|
160
|
-
"description: candidate",
|
|
161
|
-
"category: tools",
|
|
162
|
-
"links: {}",
|
|
163
|
-
"curation: { reviewed: false, labels: [], lenses: [] }",
|
|
164
|
-
"scores: {}",
|
|
165
|
-
"health:",
|
|
166
|
-
" status: needs_review",
|
|
167
|
-
" maturity: useful",
|
|
168
|
-
" tier: listed",
|
|
169
|
-
" visibility: keep",
|
|
170
|
-
" cleanupCandidate: false",
|
|
171
|
-
" staleReason: null",
|
|
172
|
-
" confidence: medium",
|
|
173
|
-
" reasons: []",
|
|
174
|
-
].join("\n"));
|
|
175
|
-
const { report } = await cleanupStale(cwd);
|
|
176
|
-
// The broken record is dropped; only "ok" is reported.
|
|
177
|
-
expect(report.totalCandidates).toBe(1);
|
|
178
|
-
expect(report.candidates[0]?.slug).toBe("ok");
|
|
179
|
-
});
|
|
180
|
-
it("returns an empty report when the records directory does not exist", async () => {
|
|
181
|
-
// The readdir call at decisions.ts:77 uses .catch(() => []),
|
|
182
|
-
// so a missing dir is silently treated as zero records. Pin
|
|
183
|
-
// that behaviour — the function should NOT throw, the report
|
|
184
|
-
// should be empty, and the JSON file should still be written
|
|
185
|
-
// (so downstream tooling can rely on its presence). We test
|
|
186
|
-
// by pointing config.paths.recordsDir at a path that doesn't
|
|
187
|
-
// exist, rather than rm-ing the records dir after beforeEach
|
|
188
|
-
// created the config (which would also remove the config).
|
|
189
|
-
const noRecordsConfig = {
|
|
190
|
-
site: { name: "test", tagline: "test" },
|
|
191
|
-
paths: { recordsDir: "data/does-not-exist", generatedDir: "data/generated", health: "data/health.yml", decisions: "data/decisions.yml" },
|
|
192
|
-
blueprint: "project-directory",
|
|
193
|
-
nav: [],
|
|
194
|
-
theme: {},
|
|
195
|
-
integrations: {},
|
|
196
|
-
};
|
|
197
|
-
const { report, path } = await cleanupStale(cwd, noRecordsConfig);
|
|
198
|
-
expect(report.totalCandidates).toBe(0);
|
|
199
|
-
expect(report.candidates).toEqual([]);
|
|
200
|
-
// The file should still exist for downstream consumers.
|
|
201
|
-
const written = JSON.parse(await readFile(path, "utf8"));
|
|
202
|
-
expect(written.totalCandidates).toBe(0);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
//# sourceMappingURL=decisions.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decisions.test.js","sourceRoot":"","sources":["../src/decisions.test.ts"],"names":[],"mappings":"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"}
|
package/dist/enrich.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"enrich.test.d.ts","sourceRoot":"","sources":["../src/enrich.test.ts"],"names":[],"mappings":""}
|