@grainulation/silo 1.0.0 → 1.0.1

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/lib/packs.js CHANGED
@@ -6,12 +6,12 @@
6
6
  * a manifest, claims, and optional templates.
7
7
  */
8
8
 
9
- const fs = require('node:fs');
10
- const path = require('node:path');
11
- const crypto = require('node:crypto');
12
- const { Store } = require('./store.js');
9
+ const fs = require("node:fs");
10
+ const path = require("node:path");
11
+ const crypto = require("node:crypto");
12
+ const { Store } = require("./store.js");
13
13
 
14
- const BUILT_IN_PACKS_DIR = path.join(__dirname, '..', 'packs');
14
+ const BUILT_IN_PACKS_DIR = path.join(__dirname, "..", "packs");
15
15
 
16
16
  class Packs {
17
17
  constructor(store) {
@@ -25,18 +25,18 @@ class Packs {
25
25
  // Built-in packs
26
26
  if (fs.existsSync(BUILT_IN_PACKS_DIR)) {
27
27
  for (const file of fs.readdirSync(BUILT_IN_PACKS_DIR)) {
28
- if (!file.endsWith('.json')) continue;
28
+ if (!file.endsWith(".json")) continue;
29
29
  try {
30
30
  const data = JSON.parse(
31
- fs.readFileSync(path.join(BUILT_IN_PACKS_DIR, file), 'utf-8'),
31
+ fs.readFileSync(path.join(BUILT_IN_PACKS_DIR, file), "utf-8"),
32
32
  );
33
33
  packs.push({
34
- id: file.replace('.json', ''),
34
+ id: file.replace(".json", ""),
35
35
  name: data.name,
36
36
  description: data.description,
37
37
  claimCount: (data.claims || []).length,
38
- version: data.version || '1.0.0',
39
- source: 'built-in',
38
+ version: data.version || "1.0.0",
39
+ source: "built-in",
40
40
  });
41
41
  } catch {
42
42
  // skip malformed
@@ -48,18 +48,18 @@ class Packs {
48
48
  const localDir = this.store.packsDir;
49
49
  if (fs.existsSync(localDir)) {
50
50
  for (const file of fs.readdirSync(localDir)) {
51
- if (!file.endsWith('.json')) continue;
51
+ if (!file.endsWith(".json")) continue;
52
52
  try {
53
53
  const data = JSON.parse(
54
- fs.readFileSync(path.join(localDir, file), 'utf-8'),
54
+ fs.readFileSync(path.join(localDir, file), "utf-8"),
55
55
  );
56
56
  packs.push({
57
- id: file.replace('.json', ''),
57
+ id: file.replace(".json", ""),
58
58
  name: data.name,
59
59
  description: data.description,
60
60
  claimCount: (data.claims || []).length,
61
- version: data.version || '1.0.0',
62
- source: 'local',
61
+ version: data.version || "1.0.0",
62
+ source: "local",
63
63
  });
64
64
  } catch {
65
65
  // skip malformed
@@ -74,12 +74,12 @@ class Packs {
74
74
  get(id) {
75
75
  const builtIn = path.join(BUILT_IN_PACKS_DIR, `${id}.json`);
76
76
  if (fs.existsSync(builtIn)) {
77
- return JSON.parse(fs.readFileSync(builtIn, 'utf-8'));
77
+ return JSON.parse(fs.readFileSync(builtIn, "utf-8"));
78
78
  }
79
79
 
80
80
  const local = path.join(this.store.packsDir, `${id}.json`);
81
81
  if (fs.existsSync(local)) {
82
- return JSON.parse(fs.readFileSync(local, 'utf-8'));
82
+ return JSON.parse(fs.readFileSync(local, "utf-8"));
83
83
  }
84
84
 
85
85
  return null;
@@ -105,19 +105,25 @@ class Packs {
105
105
 
106
106
  const pack = {
107
107
  name,
108
- description: meta.description || '',
109
- version: meta.version || '1.0.0',
110
- author: meta.author || '',
108
+ description: meta.description || "",
109
+ version: meta.version || "1.0.0",
110
+ author: meta.author || "",
111
111
  createdAt: new Date().toISOString(),
112
- hash: crypto.createHash('sha256').update(JSON.stringify(allClaims)).digest('hex'),
112
+ hash: crypto
113
+ .createHash("sha256")
114
+ .update(JSON.stringify(allClaims))
115
+ .digest("hex"),
113
116
  sources: collectionIds,
114
117
  claims: allClaims,
115
118
  };
116
119
 
117
- const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
120
+ const slug = name
121
+ .toLowerCase()
122
+ .replace(/[^a-z0-9]+/g, "-")
123
+ .replace(/^-|-$/g, "");
118
124
  const packPath = path.join(this.store.packsDir, `${slug}.json`);
119
- const tmp = packPath + '.tmp.' + process.pid;
120
- fs.writeFileSync(tmp, JSON.stringify(pack, null, 2) + '\n', 'utf-8');
125
+ const tmp = packPath + ".tmp." + process.pid;
126
+ fs.writeFileSync(tmp, JSON.stringify(pack, null, 2) + "\n", "utf-8");
121
127
  fs.renameSync(tmp, packPath);
122
128
 
123
129
  return { id: slug, path: packPath, claimCount: allClaims.length };
@@ -133,19 +139,24 @@ class Packs {
133
139
  throw new Error(`Pack file not found: ${filePath}`);
134
140
  }
135
141
 
136
- const pack = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
137
- const slug = (pack.name || path.basename(filePath, '.json'))
142
+ const pack = JSON.parse(fs.readFileSync(filePath, "utf-8"));
143
+ const slug = (pack.name || path.basename(filePath, ".json"))
138
144
  .toLowerCase()
139
- .replace(/[^a-z0-9]+/g, '-')
140
- .replace(/^-|-$/g, '');
145
+ .replace(/[^a-z0-9]+/g, "-")
146
+ .replace(/^-|-$/g, "");
141
147
 
142
148
  // Verify pack integrity if hash present
143
149
  if (pack.hash && pack.claims) {
144
- const actual = crypto.createHash('sha256').update(JSON.stringify(pack.claims)).digest('hex');
150
+ const actual = crypto
151
+ .createHash("sha256")
152
+ .update(JSON.stringify(pack.claims))
153
+ .digest("hex");
145
154
  // Support both old 12-char and new 64-char hashes
146
155
  if (pack.hash !== actual && !actual.startsWith(pack.hash)) {
147
156
  if (!options.force) {
148
- throw new Error(`Pack integrity check failed: hash mismatch. Use --force to install anyway.`);
157
+ throw new Error(
158
+ `Pack integrity check failed: hash mismatch. Use --force to install anyway.`,
159
+ );
149
160
  }
150
161
  }
151
162
  }
@@ -155,13 +166,26 @@ class Packs {
155
166
 
156
167
  // Version comparison if pack already exists
157
168
  if (fs.existsSync(dest) && !options.force) {
158
- const existing = JSON.parse(fs.readFileSync(dest, 'utf-8'));
159
- const cmp = _compareSemver(pack.version || '0.0.0', existing.version || '0.0.0');
169
+ const existing = JSON.parse(fs.readFileSync(dest, "utf-8"));
170
+ const cmp = _compareSemver(
171
+ pack.version || "0.0.0",
172
+ existing.version || "0.0.0",
173
+ );
160
174
  if (cmp === 0) {
161
- return { id: slug, claimCount: (pack.claims || []).length, skipped: true, reason: 'same version' };
175
+ return {
176
+ id: slug,
177
+ claimCount: (pack.claims || []).length,
178
+ skipped: true,
179
+ reason: "same version",
180
+ };
162
181
  }
163
182
  if (cmp < 0) {
164
- return { id: slug, claimCount: (pack.claims || []).length, skipped: true, reason: `downgrade (${existing.version} → ${pack.version}). Use --force to override.` };
183
+ return {
184
+ id: slug,
185
+ claimCount: (pack.claims || []).length,
186
+ skipped: true,
187
+ reason: `downgrade (${existing.version} → ${pack.version}). Use --force to override.`,
188
+ };
165
189
  }
166
190
  }
167
191
 
@@ -172,8 +196,8 @@ class Packs {
172
196
 
173
197
  /** Compare two semver strings. Returns -1, 0, or 1. */
174
198
  function _compareSemver(a, b) {
175
- const pa = (a || '0.0.0').split('.').map(Number);
176
- const pb = (b || '0.0.0').split('.').map(Number);
199
+ const pa = (a || "0.0.0").split(".").map(Number);
200
+ const pb = (b || "0.0.0").split(".").map(Number);
177
201
  for (let i = 0; i < 3; i++) {
178
202
  if ((pa[i] || 0) > (pb[i] || 0)) return 1;
179
203
  if ((pa[i] || 0) < (pb[i] || 0)) return -1;
package/lib/search.js CHANGED
@@ -5,9 +5,9 @@
5
5
  * rank by term frequency. No external deps.
6
6
  */
7
7
 
8
- const fs = require('node:fs');
9
- const path = require('node:path');
10
- const { Store } = require('./store.js');
8
+ const fs = require("node:fs");
9
+ const path = require("node:path");
10
+ const { Store } = require("./store.js");
11
11
 
12
12
  class Search {
13
13
  constructor(store) {
@@ -35,18 +35,18 @@ class Search {
35
35
 
36
36
  if (!fs.existsSync(claimsDir)) return [];
37
37
 
38
- const files = fs.readdirSync(claimsDir).filter((f) => f.endsWith('.json'));
38
+ const files = fs.readdirSync(claimsDir).filter((f) => f.endsWith(".json"));
39
39
 
40
40
  for (const file of files) {
41
41
  const filePath = path.join(claimsDir, file);
42
42
  let data;
43
43
  try {
44
- data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
44
+ data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
45
45
  } catch {
46
46
  continue;
47
47
  }
48
48
 
49
- const collectionName = data.meta?.name || file.replace('.json', '');
49
+ const collectionName = data.meta?.name || file.replace(".json", "");
50
50
  const claims = data.claims || [];
51
51
 
52
52
  for (const claim of claims) {
@@ -56,17 +56,20 @@ class Search {
56
56
  if (evidenceFilter && claimEvidence !== evidenceFilter) continue;
57
57
 
58
58
  // Score by token matches across searchable fields (support both content and legacy text)
59
- const sourceStr = typeof claim.source === 'object'
60
- ? [claim.source?.origin, claim.source?.artifact].filter(Boolean).join(' ')
61
- : (claim.source || '');
59
+ const sourceStr =
60
+ typeof claim.source === "object"
61
+ ? [claim.source?.origin, claim.source?.artifact]
62
+ .filter(Boolean)
63
+ .join(" ")
64
+ : claim.source || "";
62
65
  const searchable = [
63
- claim.content || claim.text || '',
64
- claim.type || '',
65
- claim.tags?.join(' ') || '',
66
+ claim.content || claim.text || "",
67
+ claim.type || "",
68
+ claim.tags?.join(" ") || "",
66
69
  sourceStr,
67
70
  collectionName,
68
71
  ]
69
- .join(' ')
72
+ .join(" ")
70
73
  .toLowerCase();
71
74
 
72
75
  let score = 0;
@@ -75,7 +78,10 @@ class Search {
75
78
  if (idx !== -1) {
76
79
  score += 1;
77
80
  // Bonus for exact word match
78
- if (searchable.includes(` ${token} `) || searchable.startsWith(`${token} `)) {
81
+ if (
82
+ searchable.includes(` ${token} `) ||
83
+ searchable.startsWith(`${token} `)
84
+ ) {
79
85
  score += 0.5;
80
86
  }
81
87
  }
@@ -101,10 +107,12 @@ class Search {
101
107
  const claimsDir = this.store.claimsDir;
102
108
  if (!fs.existsSync(claimsDir)) return [];
103
109
 
104
- const files = fs.readdirSync(claimsDir).filter((f) => f.endsWith('.json'));
110
+ const files = fs.readdirSync(claimsDir).filter((f) => f.endsWith(".json"));
105
111
  for (const file of files) {
106
112
  try {
107
- const data = JSON.parse(fs.readFileSync(path.join(claimsDir, file), 'utf-8'));
113
+ const data = JSON.parse(
114
+ fs.readFileSync(path.join(claimsDir, file), "utf-8"),
115
+ );
108
116
  for (const claim of data.claims || []) {
109
117
  for (const tag of claim.tags || []) {
110
118
  tagSet.add(tag);