@kb-labs/marketplace-registry-core 2.96.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,5 @@
1
+ # @kb-labs/marketplace-registry-core
2
+
3
+ > Part of [KB Labs](https://github.com/KirillBaranov/kb-labs) ecosystem.
4
+
5
+ Business logic for the marketplace registry plugin.
@@ -0,0 +1,63 @@
1
+ import { IStorage, ICache, ILogger } from '@kb-labs/core-platform';
2
+ import { PublishRequest, PublishResponse, PackageMeta, ShareToken, RegistryEntry, PackageStats, RegistryPackageSummary } from '@kb-labs/marketplace-registry-contracts';
3
+ import { EntitySignature } from '@kb-labs/core-discovery';
4
+
5
+ /**
6
+ * RegistryService — stores and retrieves packages using platform.storage + platform.cache.
7
+ * No direct filesystem or DB dependencies — swap adapters in kb.config.json.
8
+ */
9
+
10
+ interface RegistryServiceOptions {
11
+ storage: IStorage;
12
+ cache: ICache;
13
+ logger: ILogger;
14
+ /** Base URL of the registry (for share links and pageUrl in responses) */
15
+ baseUrl: string;
16
+ /** Ed25519 private key (PEM) for signing public packages. Absent = no signing. */
17
+ signingPrivateKey?: string;
18
+ /** Site URL for marketplace pages (e.g. https://kblabs.ru) */
19
+ siteUrl?: string;
20
+ }
21
+ declare class RegistryService {
22
+ private readonly storage;
23
+ private readonly cache;
24
+ private readonly log;
25
+ private readonly baseUrl;
26
+ private readonly signingPrivateKey?;
27
+ private readonly siteUrl?;
28
+ constructor(opts: RegistryServiceOptions);
29
+ publish(tarball: Buffer, req: PublishRequest, authorHandle: string, authorNamespaceId: string): Promise<PublishResponse>;
30
+ updateMeta(authorHandle: string, name: string, meta: Partial<PackageMeta>): Promise<void>;
31
+ yank(authorHandle: string, name: string, version: string, reason?: string): Promise<void>;
32
+ deprecate(authorHandle: string, name: string, message?: string): Promise<void>;
33
+ addToAllowlist(authorHandle: string, name: string, namespaceId: string): Promise<void>;
34
+ createShareToken(authorHandle: string, name: string, ttlMs?: number): Promise<ShareToken>;
35
+ resolveShareToken(token: string): Promise<{
36
+ pkg: string;
37
+ } | null>;
38
+ getTarball(authorHandle: string, name: string, version: string): Promise<{
39
+ tarball: Buffer;
40
+ signature?: EntitySignature;
41
+ }>;
42
+ canAccess(entry: RegistryEntry, callerNamespaceId: string | null, shareToken?: {
43
+ pkg: string;
44
+ } | null): boolean;
45
+ getEntry(authorHandle: string, name: string): Promise<RegistryEntry | null>;
46
+ getStats(authorHandle: string, name: string): Promise<PackageStats>;
47
+ incrementInstalls(authorHandle: string, name: string): Promise<void>;
48
+ listPublicPackages(): Promise<RegistryPackageSummary[]>;
49
+ searchPackages(query: string): Promise<RegistryPackageSummary[]>;
50
+ listByAuthor(authorNamespaceId: string): Promise<RegistryEntry[]>;
51
+ setFeatured(authorHandle: string, name: string, featured: boolean): Promise<void>;
52
+ private metaPath;
53
+ private tarballPath;
54
+ private sigPath;
55
+ private loadEntry;
56
+ private saveEntry;
57
+ private requireEntry;
58
+ private invalidateCaches;
59
+ private rebuildIndex;
60
+ private signTarball;
61
+ }
62
+
63
+ export { RegistryService, type RegistryServiceOptions };
package/dist/index.js ADDED
@@ -0,0 +1,346 @@
1
+ import { createHash, sign } from 'crypto';
2
+
3
+ // src/registry-service.ts
4
+ var STORAGE_PREFIX = "marketplace-registry";
5
+ var CACHE_TTL_META = 5 * 60 * 1e3;
6
+ var CACHE_TTL_SEARCH = 60 * 1e3;
7
+ var CACHE_TTL_SHARE_DEFAULT = 7 * 24 * 60 * 60 * 1e3;
8
+ var MAX_TARBALL_SIZE = 50 * 1024 * 1024;
9
+ var RegistryService = class {
10
+ storage;
11
+ cache;
12
+ log;
13
+ baseUrl;
14
+ signingPrivateKey;
15
+ siteUrl;
16
+ constructor(opts) {
17
+ this.storage = opts.storage;
18
+ this.cache = opts.cache;
19
+ this.log = opts.logger;
20
+ this.baseUrl = opts.baseUrl;
21
+ this.signingPrivateKey = opts.signingPrivateKey;
22
+ this.siteUrl = opts.siteUrl;
23
+ }
24
+ // ── Publish ────────────────────────────────────────────────────────────────
25
+ async publish(tarball, req, authorHandle, authorNamespaceId) {
26
+ const { meta, visibility, primaryKind } = req;
27
+ const { name, version } = meta;
28
+ if (tarball.length > MAX_TARBALL_SIZE) {
29
+ throw Object.assign(new Error("Tarball exceeds 50 MB limit"), { code: "TARBALL_TOO_LARGE" });
30
+ }
31
+ const existing = await this.loadEntry(authorHandle, name);
32
+ if (existing) {
33
+ if (existing.versions.some((v) => v.version === version)) {
34
+ throw Object.assign(
35
+ new Error(`Version ${version} already exists. Versions are immutable.`),
36
+ { code: "VERSION_ALREADY_EXISTS" }
37
+ );
38
+ }
39
+ }
40
+ const integrity = `sha256-${createHash("sha256").update(tarball).digest("base64")}`;
41
+ const tarballPath = this.tarballPath(authorHandle, name, version);
42
+ await this.storage.write(tarballPath, tarball);
43
+ let signature;
44
+ if (visibility === "public" && this.signingPrivateKey) {
45
+ signature = this.signTarball(tarball, integrity, authorHandle, name, version);
46
+ const sigPath = this.sigPath(authorHandle, name, version);
47
+ await this.storage.write(sigPath, Buffer.from(JSON.stringify(signature)));
48
+ }
49
+ const versionEntry = {
50
+ version,
51
+ publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
52
+ integrity,
53
+ signature
54
+ };
55
+ const now = (/* @__PURE__ */ new Date()).toISOString();
56
+ const entry = existing ? {
57
+ ...existing,
58
+ visibility,
59
+ primaryKind,
60
+ trust: visibility === "public" && signature ? "trusted" : existing.trust,
61
+ versions: [...existing.versions, versionEntry],
62
+ meta,
63
+ updatedAt: now
64
+ } : {
65
+ name,
66
+ authorHandle,
67
+ authorNamespaceId,
68
+ primaryKind,
69
+ visibility,
70
+ trust: visibility === "public" && signature ? "trusted" : "untrusted",
71
+ allowlist: [],
72
+ versions: [versionEntry],
73
+ deprecated: false,
74
+ featured: false,
75
+ badges: [],
76
+ tags: meta.keywords ?? [],
77
+ meta,
78
+ createdAt: now,
79
+ updatedAt: now
80
+ };
81
+ await this.saveEntry(authorHandle, name, entry);
82
+ await this.invalidateCaches(authorHandle, name);
83
+ if (visibility === "public") {
84
+ await this.rebuildIndex();
85
+ }
86
+ const fullName = `${authorHandle}/${name}`;
87
+ return {
88
+ handle: authorHandle,
89
+ name,
90
+ version,
91
+ visibility,
92
+ trust: entry.trust,
93
+ signature,
94
+ installCommand: `kb marketplace install kb:${fullName}`,
95
+ pageUrl: this.siteUrl ? `${this.siteUrl}/ru/marketplace/${fullName}` : void 0
96
+ };
97
+ }
98
+ // ── Update metadata only ───────────────────────────────────────────────────
99
+ async updateMeta(authorHandle, name, meta) {
100
+ const entry = await this.requireEntry(authorHandle, name);
101
+ const updated = {
102
+ ...entry,
103
+ meta: { ...entry.meta, ...meta },
104
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
105
+ };
106
+ await this.saveEntry(authorHandle, name, updated);
107
+ await this.invalidateCaches(authorHandle, name);
108
+ if (entry.visibility === "public") {
109
+ await this.rebuildIndex();
110
+ }
111
+ }
112
+ // ── Yank version ──────────────────────────────────────────────────────────
113
+ async yank(authorHandle, name, version, reason) {
114
+ const entry = await this.requireEntry(authorHandle, name);
115
+ const versions = entry.versions.map(
116
+ (v) => v.version === version ? { ...v, yanked: true, yankReason: reason } : v
117
+ );
118
+ if (versions.every((v, i) => v === entry.versions[i])) {
119
+ throw Object.assign(new Error(`Version ${version} not found`), { code: "VERSION_NOT_FOUND" });
120
+ }
121
+ await this.saveEntry(authorHandle, name, { ...entry, versions, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
122
+ await this.invalidateCaches(authorHandle, name);
123
+ }
124
+ // ── Deprecate package ─────────────────────────────────────────────────────
125
+ async deprecate(authorHandle, name, message) {
126
+ const entry = await this.requireEntry(authorHandle, name);
127
+ await this.saveEntry(authorHandle, name, {
128
+ ...entry,
129
+ deprecated: true,
130
+ deprecateMessage: message,
131
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
132
+ });
133
+ await this.invalidateCaches(authorHandle, name);
134
+ if (entry.visibility === "public") {
135
+ await this.rebuildIndex();
136
+ }
137
+ }
138
+ // ── Share ──────────────────────────────────────────────────────────────────
139
+ async addToAllowlist(authorHandle, name, namespaceId) {
140
+ const entry = await this.requireEntry(authorHandle, name);
141
+ if (entry.allowlist.includes(namespaceId)) {
142
+ return;
143
+ }
144
+ await this.saveEntry(authorHandle, name, {
145
+ ...entry,
146
+ allowlist: [...entry.allowlist, namespaceId],
147
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
148
+ });
149
+ await this.invalidateCaches(authorHandle, name);
150
+ }
151
+ async createShareToken(authorHandle, name, ttlMs = CACHE_TTL_SHARE_DEFAULT) {
152
+ await this.requireEntry(authorHandle, name);
153
+ const token = `kbrt_${createHash("sha256").update(`${Date.now()}${Math.random()}`).digest("hex").slice(0, 32)}`;
154
+ const expiresAt = new Date(Date.now() + ttlMs).toISOString();
155
+ const pkg = `${authorHandle}/${name}`;
156
+ await this.cache.set(`registry:sharetoken:${token}`, { pkg, expiresAt }, ttlMs);
157
+ return { token, pkg, expiresAt };
158
+ }
159
+ async resolveShareToken(token) {
160
+ return this.cache.get(`registry:sharetoken:${token}`);
161
+ }
162
+ // ── Download ───────────────────────────────────────────────────────────────
163
+ async getTarball(authorHandle, name, version) {
164
+ const entry = await this.requireEntry(authorHandle, name);
165
+ const vEntry = entry.versions.find((v) => v.version === version);
166
+ if (!vEntry) {
167
+ throw Object.assign(new Error(`Version ${version} not found`), { code: "VERSION_NOT_FOUND" });
168
+ }
169
+ if (vEntry.yanked) {
170
+ throw Object.assign(new Error(`Version ${version} has been yanked`), { code: "VERSION_YANKED" });
171
+ }
172
+ const tarball = await this.storage.read(this.tarballPath(authorHandle, name, version));
173
+ if (!tarball) {
174
+ throw Object.assign(new Error("Tarball not found in storage"), { code: "VERSION_NOT_FOUND" });
175
+ }
176
+ return { tarball, signature: vEntry.signature };
177
+ }
178
+ // ── Access check ──────────────────────────────────────────────────────────
179
+ canAccess(entry, callerNamespaceId, shareToken) {
180
+ if (entry.visibility === "public") {
181
+ return true;
182
+ }
183
+ if (!callerNamespaceId) {
184
+ return !!shareToken && shareToken.pkg === `${entry.authorHandle}/${entry.name}`;
185
+ }
186
+ if (entry.authorNamespaceId === callerNamespaceId) {
187
+ return true;
188
+ }
189
+ if (entry.allowlist.includes(callerNamespaceId)) {
190
+ return true;
191
+ }
192
+ return !!shareToken && shareToken.pkg === `${entry.authorHandle}/${entry.name}`;
193
+ }
194
+ // ── Metadata reads ─────────────────────────────────────────────────────────
195
+ async getEntry(authorHandle, name) {
196
+ const cacheKey = `registry:pkg:${authorHandle}/${name}`;
197
+ const cached = await this.cache.get(cacheKey);
198
+ if (cached) {
199
+ return cached;
200
+ }
201
+ const entry = await this.loadEntry(authorHandle, name);
202
+ if (entry) {
203
+ await this.cache.set(cacheKey, entry, CACHE_TTL_META);
204
+ }
205
+ return entry;
206
+ }
207
+ async getStats(authorHandle, name) {
208
+ const key = `registry:stats:${authorHandle}/${name}`;
209
+ const data = await this.cache.get(key);
210
+ return data ?? { installs: 0 };
211
+ }
212
+ async incrementInstalls(authorHandle, name) {
213
+ const key = `registry:stats:${authorHandle}/${name}`;
214
+ const current = await this.cache.get(key) ?? { installs: 0 };
215
+ await this.cache.set(key, { installs: current.installs + 1 });
216
+ }
217
+ async listPublicPackages() {
218
+ const cached = await this.cache.get("registry:search:__index");
219
+ if (cached) {
220
+ return cached;
221
+ }
222
+ return this.rebuildIndex();
223
+ }
224
+ async searchPackages(query) {
225
+ const q = query.toLowerCase().trim();
226
+ const cacheKey = `registry:search:${q}`;
227
+ const cached = await this.cache.get(cacheKey);
228
+ if (cached) {
229
+ return cached;
230
+ }
231
+ const all = await this.listPublicPackages();
232
+ const results = all.filter(
233
+ (p) => p.name.toLowerCase().includes(q) || p.fullName.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q) || p.keywords?.some((k) => k.toLowerCase().includes(q))
234
+ );
235
+ await this.cache.set(cacheKey, results, CACHE_TTL_SEARCH);
236
+ return results;
237
+ }
238
+ async listByAuthor(authorNamespaceId) {
239
+ const paths = await this.storage.list(`${STORAGE_PREFIX}/packages/`);
240
+ const results = [];
241
+ for (const path of paths.filter((p) => p.endsWith("/meta.json"))) {
242
+ const raw = await this.storage.read(path);
243
+ if (!raw) {
244
+ continue;
245
+ }
246
+ const entry = JSON.parse(raw.toString());
247
+ if (entry.authorNamespaceId === authorNamespaceId) {
248
+ results.push(entry);
249
+ }
250
+ }
251
+ return results;
252
+ }
253
+ async setFeatured(authorHandle, name, featured) {
254
+ const entry = await this.requireEntry(authorHandle, name);
255
+ await this.saveEntry(authorHandle, name, { ...entry, featured, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
256
+ await this.invalidateCaches(authorHandle, name);
257
+ if (entry.visibility === "public") {
258
+ await this.rebuildIndex();
259
+ }
260
+ }
261
+ // ── Private helpers ────────────────────────────────────────────────────────
262
+ metaPath(handle, name) {
263
+ return `${STORAGE_PREFIX}/packages/${handle}/${name}/meta.json`;
264
+ }
265
+ tarballPath(handle, name, version) {
266
+ return `${STORAGE_PREFIX}/packages/${handle}/${name}/${version}.tgz`;
267
+ }
268
+ sigPath(handle, name, version) {
269
+ return `${STORAGE_PREFIX}/packages/${handle}/${name}/${version}.sig`;
270
+ }
271
+ async loadEntry(handle, name) {
272
+ const raw = await this.storage.read(this.metaPath(handle, name));
273
+ if (!raw) {
274
+ return null;
275
+ }
276
+ return JSON.parse(raw.toString());
277
+ }
278
+ async saveEntry(handle, name, entry) {
279
+ await this.storage.write(this.metaPath(handle, name), Buffer.from(JSON.stringify(entry, null, 2)));
280
+ }
281
+ async requireEntry(handle, name) {
282
+ const entry = await this.loadEntry(handle, name);
283
+ if (!entry) {
284
+ throw Object.assign(new Error(`Package ${handle}/${name} not found`), { code: "PACKAGE_NOT_FOUND" });
285
+ }
286
+ return entry;
287
+ }
288
+ async invalidateCaches(handle, name) {
289
+ await this.cache.delete(`registry:pkg:${handle}/${name}`);
290
+ await this.cache.delete("registry:search:__index");
291
+ }
292
+ async rebuildIndex() {
293
+ const paths = await this.storage.list(`${STORAGE_PREFIX}/packages/`);
294
+ const summaries = [];
295
+ for (const path of paths.filter((p) => p.endsWith("/meta.json"))) {
296
+ const raw = await this.storage.read(path);
297
+ if (!raw) {
298
+ continue;
299
+ }
300
+ const entry = JSON.parse(raw.toString());
301
+ if (entry.visibility !== "public" || entry.deprecated) {
302
+ continue;
303
+ }
304
+ const latest = entry.versions.filter((v) => !v.yanked).at(-1);
305
+ if (!latest) {
306
+ continue;
307
+ }
308
+ const stats = await this.getStats(entry.authorHandle, entry.name);
309
+ summaries.push({
310
+ handle: entry.authorHandle,
311
+ name: entry.name,
312
+ fullName: `${entry.authorHandle}/${entry.name}`,
313
+ version: latest.version,
314
+ description: entry.meta.description,
315
+ author: entry.meta.author,
316
+ keywords: entry.meta.keywords,
317
+ primaryKind: entry.primaryKind ?? "plugin",
318
+ featured: entry.featured,
319
+ badges: entry.badges,
320
+ trust: entry.trust,
321
+ installs: stats.installs,
322
+ publishedAt: latest.publishedAt
323
+ });
324
+ }
325
+ summaries.sort((a, b) => (b.featured ? 1 : 0) - (a.featured ? 1 : 0) || b.installs - a.installs);
326
+ await this.cache.set("registry:search:__index", summaries, CACHE_TTL_SEARCH);
327
+ const indexPath = `${STORAGE_PREFIX}/index.json`;
328
+ await this.storage.write(indexPath, Buffer.from(JSON.stringify(summaries, null, 2)));
329
+ return summaries;
330
+ }
331
+ signTarball(tarball, integrity, handle, name, version) {
332
+ const payload = Buffer.from(JSON.stringify({ integrity, pkg: `${handle}/${name}@${version}` }));
333
+ const sig = sign(null, payload, { key: this.signingPrivateKey, format: "pem" });
334
+ return {
335
+ algorithm: "ed25519",
336
+ value: sig.toString("base64"),
337
+ signer: "kb-labs-platform",
338
+ signedAt: (/* @__PURE__ */ new Date()).toISOString(),
339
+ verifiedChecks: ["integrity"]
340
+ };
341
+ }
342
+ };
343
+
344
+ export { RegistryService };
345
+ //# sourceMappingURL=index.js.map
346
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry-service.ts"],"names":["cryptoSign"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * RegistryService — stores and retrieves packages using platform.storage + platform.cache.\n * No direct filesystem or DB dependencies — swap adapters in kb.config.json.\n */\n\nimport { createHash, sign as cryptoSign } from 'node:crypto';\nimport type { IStorage, ICache, ILogger } from '@kb-labs/core-platform';\nimport type {\n RegistryEntry,\n RegistryVersionEntry,\n PackageMeta,\n PublishRequest,\n PublishResponse,\n RegistryPackageSummary,\n PackageStats,\n ShareToken,\n} from '@kb-labs/marketplace-registry-contracts';\nimport type { EntitySignature } from '@kb-labs/core-discovery';\n\nconst STORAGE_PREFIX = 'marketplace-registry';\nconst CACHE_TTL_META = 5 * 60 * 1000;\nconst CACHE_TTL_SEARCH = 60 * 1000;\nconst CACHE_TTL_SHARE_DEFAULT = 7 * 24 * 60 * 60 * 1000; // 7 days\nconst MAX_TARBALL_SIZE = 50 * 1024 * 1024; // 50 MB\n\nexport interface RegistryServiceOptions {\n storage: IStorage;\n cache: ICache;\n logger: ILogger;\n /** Base URL of the registry (for share links and pageUrl in responses) */\n baseUrl: string;\n /** Ed25519 private key (PEM) for signing public packages. Absent = no signing. */\n signingPrivateKey?: string;\n /** Site URL for marketplace pages (e.g. https://kblabs.ru) */\n siteUrl?: string;\n}\n\nexport class RegistryService {\n private readonly storage: IStorage;\n private readonly cache: ICache;\n private readonly log: ILogger;\n private readonly baseUrl: string;\n private readonly signingPrivateKey?: string;\n private readonly siteUrl?: string;\n\n constructor(opts: RegistryServiceOptions) {\n this.storage = opts.storage;\n this.cache = opts.cache;\n this.log = opts.logger;\n this.baseUrl = opts.baseUrl;\n this.signingPrivateKey = opts.signingPrivateKey;\n this.siteUrl = opts.siteUrl;\n }\n\n // ── Publish ────────────────────────────────────────────────────────────────\n\n async publish(\n tarball: Buffer,\n req: PublishRequest,\n authorHandle: string,\n authorNamespaceId: string,\n ): Promise<PublishResponse> {\n const { meta, visibility, primaryKind } = req;\n const { name, version } = meta;\n\n if (tarball.length > MAX_TARBALL_SIZE) {\n throw Object.assign(new Error('Tarball exceeds 50 MB limit'), { code: 'TARBALL_TOO_LARGE' });\n }\n\n const existing = await this.loadEntry(authorHandle, name);\n\n if (existing) {\n if (existing.versions.some(v => v.version === version)) {\n throw Object.assign(\n new Error(`Version ${version} already exists. Versions are immutable.`),\n { code: 'VERSION_ALREADY_EXISTS' },\n );\n }\n }\n\n const integrity = `sha256-${createHash('sha256').update(tarball).digest('base64')}`;\n const tarballPath = this.tarballPath(authorHandle, name, version);\n await this.storage.write(tarballPath, tarball);\n\n let signature: EntitySignature | undefined;\n if (visibility === 'public' && this.signingPrivateKey) {\n signature = this.signTarball(tarball, integrity, authorHandle, name, version);\n const sigPath = this.sigPath(authorHandle, name, version);\n await this.storage.write(sigPath, Buffer.from(JSON.stringify(signature)));\n }\n\n const versionEntry: RegistryVersionEntry = {\n version,\n publishedAt: new Date().toISOString(),\n integrity,\n signature,\n };\n\n const now = new Date().toISOString();\n const entry: RegistryEntry = existing\n ? {\n ...existing,\n visibility,\n primaryKind,\n trust: visibility === 'public' && signature ? 'trusted' : existing.trust,\n versions: [...existing.versions, versionEntry],\n meta,\n updatedAt: now,\n }\n : {\n name,\n authorHandle,\n authorNamespaceId,\n primaryKind,\n visibility,\n trust: visibility === 'public' && signature ? 'trusted' : 'untrusted',\n allowlist: [],\n versions: [versionEntry],\n deprecated: false,\n featured: false,\n badges: [],\n tags: meta.keywords ?? [],\n meta,\n createdAt: now,\n updatedAt: now,\n };\n\n await this.saveEntry(authorHandle, name, entry);\n await this.invalidateCaches(authorHandle, name);\n\n if (visibility === 'public') {\n await this.rebuildIndex();\n }\n\n const fullName = `${authorHandle}/${name}`;\n return {\n handle: authorHandle,\n name,\n version,\n visibility,\n trust: entry.trust,\n signature,\n installCommand: `kb marketplace install kb:${fullName}`,\n pageUrl: this.siteUrl ? `${this.siteUrl}/ru/marketplace/${fullName}` : undefined,\n };\n }\n\n // ── Update metadata only ───────────────────────────────────────────────────\n\n async updateMeta(authorHandle: string, name: string, meta: Partial<PackageMeta>): Promise<void> {\n const entry = await this.requireEntry(authorHandle, name);\n const updated: RegistryEntry = {\n ...entry,\n meta: { ...entry.meta, ...meta },\n updatedAt: new Date().toISOString(),\n };\n await this.saveEntry(authorHandle, name, updated);\n await this.invalidateCaches(authorHandle, name);\n if (entry.visibility === 'public') { await this.rebuildIndex(); }\n }\n\n // ── Yank version ──────────────────────────────────────────────────────────\n\n async yank(authorHandle: string, name: string, version: string, reason?: string): Promise<void> {\n const entry = await this.requireEntry(authorHandle, name);\n const versions = entry.versions.map(v =>\n v.version === version ? { ...v, yanked: true, yankReason: reason } : v,\n );\n if (versions.every((v, i) => v === entry.versions[i])) {\n throw Object.assign(new Error(`Version ${version} not found`), { code: 'VERSION_NOT_FOUND' });\n }\n await this.saveEntry(authorHandle, name, { ...entry, versions, updatedAt: new Date().toISOString() });\n await this.invalidateCaches(authorHandle, name);\n }\n\n // ── Deprecate package ─────────────────────────────────────────────────────\n\n async deprecate(authorHandle: string, name: string, message?: string): Promise<void> {\n const entry = await this.requireEntry(authorHandle, name);\n await this.saveEntry(authorHandle, name, {\n ...entry,\n deprecated: true,\n deprecateMessage: message,\n updatedAt: new Date().toISOString(),\n });\n await this.invalidateCaches(authorHandle, name);\n if (entry.visibility === 'public') { await this.rebuildIndex(); }\n }\n\n // ── Share ──────────────────────────────────────────────────────────────────\n\n async addToAllowlist(authorHandle: string, name: string, namespaceId: string): Promise<void> {\n const entry = await this.requireEntry(authorHandle, name);\n if (entry.allowlist.includes(namespaceId)) { return; }\n await this.saveEntry(authorHandle, name, {\n ...entry,\n allowlist: [...entry.allowlist, namespaceId],\n updatedAt: new Date().toISOString(),\n });\n await this.invalidateCaches(authorHandle, name);\n }\n\n async createShareToken(\n authorHandle: string,\n name: string,\n ttlMs: number = CACHE_TTL_SHARE_DEFAULT,\n ): Promise<ShareToken> {\n await this.requireEntry(authorHandle, name);\n const token = `kbrt_${createHash('sha256').update(`${Date.now()}${Math.random()}`).digest('hex').slice(0, 32)}`;\n const expiresAt = new Date(Date.now() + ttlMs).toISOString();\n const pkg = `${authorHandle}/${name}`;\n await this.cache.set(`registry:sharetoken:${token}`, { pkg, expiresAt }, ttlMs);\n return { token, pkg, expiresAt };\n }\n\n async resolveShareToken(token: string): Promise<{ pkg: string } | null> {\n return this.cache.get<{ pkg: string; expiresAt: string }>(`registry:sharetoken:${token}`);\n }\n\n // ── Download ───────────────────────────────────────────────────────────────\n\n async getTarball(\n authorHandle: string,\n name: string,\n version: string,\n ): Promise<{ tarball: Buffer; signature?: EntitySignature }> {\n const entry = await this.requireEntry(authorHandle, name);\n const vEntry = entry.versions.find(v => v.version === version);\n if (!vEntry) {\n throw Object.assign(new Error(`Version ${version} not found`), { code: 'VERSION_NOT_FOUND' });\n }\n if (vEntry.yanked) {\n throw Object.assign(new Error(`Version ${version} has been yanked`), { code: 'VERSION_YANKED' });\n }\n\n const tarball = await this.storage.read(this.tarballPath(authorHandle, name, version));\n if (!tarball) {\n throw Object.assign(new Error('Tarball not found in storage'), { code: 'VERSION_NOT_FOUND' });\n }\n return { tarball, signature: vEntry.signature };\n }\n\n // ── Access check ──────────────────────────────────────────────────────────\n\n canAccess(\n entry: RegistryEntry,\n callerNamespaceId: string | null,\n shareToken?: { pkg: string } | null,\n ): boolean {\n if (entry.visibility === 'public') { return true; }\n if (!callerNamespaceId) {\n return !!shareToken && shareToken.pkg === `${entry.authorHandle}/${entry.name}`;\n }\n if (entry.authorNamespaceId === callerNamespaceId) { return true; }\n if (entry.allowlist.includes(callerNamespaceId)) { return true; }\n return !!shareToken && shareToken.pkg === `${entry.authorHandle}/${entry.name}`;\n }\n\n // ── Metadata reads ─────────────────────────────────────────────────────────\n\n async getEntry(authorHandle: string, name: string): Promise<RegistryEntry | null> {\n const cacheKey = `registry:pkg:${authorHandle}/${name}`;\n const cached = await this.cache.get<RegistryEntry>(cacheKey);\n if (cached) { return cached; }\n const entry = await this.loadEntry(authorHandle, name);\n if (entry) { await this.cache.set(cacheKey, entry, CACHE_TTL_META); }\n return entry;\n }\n\n async getStats(authorHandle: string, name: string): Promise<PackageStats> {\n const key = `registry:stats:${authorHandle}/${name}`;\n const data = await this.cache.get<PackageStats>(key);\n return data ?? { installs: 0 };\n }\n\n async incrementInstalls(authorHandle: string, name: string): Promise<void> {\n const key = `registry:stats:${authorHandle}/${name}`;\n const current = await this.cache.get<PackageStats>(key) ?? { installs: 0 };\n await this.cache.set(key, { installs: current.installs + 1 });\n }\n\n async listPublicPackages(): Promise<RegistryPackageSummary[]> {\n const cached = await this.cache.get<RegistryPackageSummary[]>('registry:search:__index');\n if (cached) { return cached; }\n return this.rebuildIndex();\n }\n\n async searchPackages(query: string): Promise<RegistryPackageSummary[]> {\n const q = query.toLowerCase().trim();\n const cacheKey = `registry:search:${q}`;\n const cached = await this.cache.get<RegistryPackageSummary[]>(cacheKey);\n if (cached) { return cached; }\n\n const all = await this.listPublicPackages();\n const results = all.filter(p =>\n p.name.toLowerCase().includes(q) ||\n p.fullName.toLowerCase().includes(q) ||\n p.description?.toLowerCase().includes(q) ||\n p.keywords?.some(k => k.toLowerCase().includes(q)),\n );\n\n await this.cache.set(cacheKey, results, CACHE_TTL_SEARCH);\n return results;\n }\n\n async listByAuthor(authorNamespaceId: string): Promise<RegistryEntry[]> {\n const paths = await this.storage.list(`${STORAGE_PREFIX}/packages/`);\n const results: RegistryEntry[] = [];\n for (const path of paths.filter(p => p.endsWith('/meta.json'))) {\n const raw = await this.storage.read(path);\n if (!raw) { continue; }\n const entry = JSON.parse(raw.toString()) as RegistryEntry;\n if (entry.authorNamespaceId === authorNamespaceId) { results.push(entry); }\n }\n return results;\n }\n\n async setFeatured(authorHandle: string, name: string, featured: boolean): Promise<void> {\n const entry = await this.requireEntry(authorHandle, name);\n await this.saveEntry(authorHandle, name, { ...entry, featured, updatedAt: new Date().toISOString() });\n await this.invalidateCaches(authorHandle, name);\n if (entry.visibility === 'public') { await this.rebuildIndex(); }\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private metaPath(handle: string, name: string): string {\n return `${STORAGE_PREFIX}/packages/${handle}/${name}/meta.json`;\n }\n\n private tarballPath(handle: string, name: string, version: string): string {\n return `${STORAGE_PREFIX}/packages/${handle}/${name}/${version}.tgz`;\n }\n\n private sigPath(handle: string, name: string, version: string): string {\n return `${STORAGE_PREFIX}/packages/${handle}/${name}/${version}.sig`;\n }\n\n private async loadEntry(handle: string, name: string): Promise<RegistryEntry | null> {\n const raw = await this.storage.read(this.metaPath(handle, name));\n if (!raw) { return null; }\n return JSON.parse(raw.toString()) as RegistryEntry;\n }\n\n private async saveEntry(handle: string, name: string, entry: RegistryEntry): Promise<void> {\n await this.storage.write(this.metaPath(handle, name), Buffer.from(JSON.stringify(entry, null, 2)));\n }\n\n private async requireEntry(handle: string, name: string): Promise<RegistryEntry> {\n const entry = await this.loadEntry(handle, name);\n if (!entry) {\n throw Object.assign(new Error(`Package ${handle}/${name} not found`), { code: 'PACKAGE_NOT_FOUND' });\n }\n return entry;\n }\n\n private async invalidateCaches(handle: string, name: string): Promise<void> {\n await this.cache.delete(`registry:pkg:${handle}/${name}`);\n await this.cache.delete('registry:search:__index');\n }\n\n private async rebuildIndex(): Promise<RegistryPackageSummary[]> {\n const paths = await this.storage.list(`${STORAGE_PREFIX}/packages/`);\n const summaries: RegistryPackageSummary[] = [];\n\n for (const path of paths.filter(p => p.endsWith('/meta.json'))) {\n const raw = await this.storage.read(path);\n if (!raw) { continue; }\n const entry = JSON.parse(raw.toString()) as RegistryEntry;\n if (entry.visibility !== 'public' || entry.deprecated) { continue; }\n\n const latest = entry.versions.filter(v => !v.yanked).at(-1);\n if (!latest) { continue; }\n\n const stats = await this.getStats(entry.authorHandle, entry.name);\n summaries.push({\n handle: entry.authorHandle,\n name: entry.name,\n fullName: `${entry.authorHandle}/${entry.name}`,\n version: latest.version,\n description: entry.meta.description,\n author: entry.meta.author,\n keywords: entry.meta.keywords,\n primaryKind: entry.primaryKind ?? 'plugin',\n featured: entry.featured,\n badges: entry.badges,\n trust: entry.trust,\n installs: stats.installs,\n publishedAt: latest.publishedAt,\n });\n }\n\n summaries.sort((a, b) => (b.featured ? 1 : 0) - (a.featured ? 1 : 0) || b.installs - a.installs);\n await this.cache.set('registry:search:__index', summaries, CACHE_TTL_SEARCH);\n\n const indexPath = `${STORAGE_PREFIX}/index.json`;\n await this.storage.write(indexPath, Buffer.from(JSON.stringify(summaries, null, 2)));\n\n return summaries;\n }\n\n private signTarball(\n tarball: Buffer,\n integrity: string,\n handle: string,\n name: string,\n version: string,\n ): EntitySignature {\n const payload = Buffer.from(JSON.stringify({ integrity, pkg: `${handle}/${name}@${version}` }));\n // Ed25519 uses its own internal hash — algorithm must be null\n const sig = cryptoSign(null, payload, { key: this.signingPrivateKey!, format: 'pem' });\n return {\n algorithm: 'ed25519',\n value: sig.toString('base64'),\n signer: 'kb-labs-platform',\n signedAt: new Date().toISOString(),\n verifiedChecks: ['integrity'],\n };\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@kb-labs/marketplace-registry-core",
3
+ "description": "Registry service for KB Labs Marketplace Registry",
4
+ "version": "2.96.0",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "@kb-labs/core-discovery": "2.96.0",
19
+ "@kb-labs/core-platform": "2.96.0",
20
+ "@kb-labs/marketplace-registry-contracts": "2.96.0"
21
+ },
22
+ "devDependencies": {
23
+ "tsup": "^8.5.0",
24
+ "typescript": "^5",
25
+ "vitest": "^3.2.6",
26
+ "@kb-labs/devkit": "2.96.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=20.0.0",
30
+ "pnpm": ">=9.0.0"
31
+ },
32
+ "sideEffects": false,
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "clean": "rimraf dist",
36
+ "dev": "tsup --watch",
37
+ "lint": "eslint src --ext .ts",
38
+ "lint:fix": "eslint src --ext .ts --fix",
39
+ "test": "vitest run --passWithNoTests -c ../../vitest.config.ts",
40
+ "test:watch": "vitest -c ../../vitest.config.ts",
41
+ "type-check": "tsc --noEmit"
42
+ }
43
+ }