@cascivo/registry 0.1.4 → 0.1.5
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/dist/index.d.mts +125 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +159 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,46 @@ interface RegistryFile {
|
|
|
3
3
|
url: string;
|
|
4
4
|
target?: string;
|
|
5
5
|
}
|
|
6
|
-
type RegistryItemType = 'component' | 'layout' | 'block' | 'chart' | 'section' | 'theme' | 'style';
|
|
6
|
+
type RegistryItemType = 'component' | 'layout' | 'block' | 'chart' | 'section' | 'theme' | 'style' | 'template';
|
|
7
|
+
/** Light/dark preview images for a template, shown in the marketplace gallery. */
|
|
8
|
+
interface TemplateScreenshot {
|
|
9
|
+
/** URL of the light-theme screenshot. */
|
|
10
|
+
light: string;
|
|
11
|
+
/** URL of the dark-theme screenshot (optional). */
|
|
12
|
+
dark?: string;
|
|
13
|
+
/** Alt text — required for accessibility. */
|
|
14
|
+
alt: string;
|
|
15
|
+
}
|
|
16
|
+
/** The role a template's own file plays once installed. Keyed by `files[].target`. */
|
|
17
|
+
type TemplateFileRole = 'page' | 'fixture' | 'asset' | 'config';
|
|
18
|
+
/**
|
|
19
|
+
* Gallery + install metadata carried in a template item's `meta` field.
|
|
20
|
+
*
|
|
21
|
+
* For a `type: 'template'` RegistryItem: the bundled components are listed in
|
|
22
|
+
* `registryDependencies`, the template's own novel source (page/fixtures/assets)
|
|
23
|
+
* is listed in `files` (each with a `target`), and this object describes how to
|
|
24
|
+
* present and install it. Templates ride the existing registry transport — there
|
|
25
|
+
* is no separate manifest format.
|
|
26
|
+
*/
|
|
27
|
+
interface TemplateMeta {
|
|
28
|
+
/** One-line description of what the template builds (e.g. "Analytics dashboard"). */
|
|
29
|
+
intent: string;
|
|
30
|
+
/** The framework the template's page source targets. */
|
|
31
|
+
framework: 'react-vite' | 'react-next';
|
|
32
|
+
/** Discovery category (e.g. "dashboard", "marketing", "auth"). */
|
|
33
|
+
category: string;
|
|
34
|
+
/** Preview images for the gallery — at least one required. */
|
|
35
|
+
screenshots: TemplateScreenshot[];
|
|
36
|
+
/** URL of a live demo (typically the author's GitHub Pages deploy). */
|
|
37
|
+
demoUrl?: string;
|
|
38
|
+
/** Maps each `files[].target` to the role it plays once installed. */
|
|
39
|
+
fileRoles: Record<string, TemplateFileRole>;
|
|
40
|
+
/** Named pages the template installs, for previews and routing hints. */
|
|
41
|
+
pages?: {
|
|
42
|
+
name: string;
|
|
43
|
+
target: string;
|
|
44
|
+
}[];
|
|
45
|
+
}
|
|
7
46
|
interface RegistryAdvisory {
|
|
8
47
|
id: string;
|
|
9
48
|
severity: 'low' | 'moderate' | 'high' | 'critical';
|
|
@@ -12,6 +51,13 @@ interface RegistryAdvisory {
|
|
|
12
51
|
summary: string;
|
|
13
52
|
refs?: string[];
|
|
14
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* A single registry item.
|
|
56
|
+
*
|
|
57
|
+
* For `type: 'template'`: `registryDependencies` lists the components the
|
|
58
|
+
* template composes, `files` are the template's *own* source (page/fixtures/
|
|
59
|
+
* assets — not component source), and `meta` is a {@link TemplateMeta}.
|
|
60
|
+
*/
|
|
15
61
|
interface RegistryItem {
|
|
16
62
|
schemaVersion: 2;
|
|
17
63
|
name: string;
|
|
@@ -68,6 +114,25 @@ declare function validateItem(raw: unknown): ValidationResult;
|
|
|
68
114
|
declare function validateIndex(raw: unknown): ValidationResult;
|
|
69
115
|
declare function parseLegacyRegistry(raw: unknown): RegistryIndex;
|
|
70
116
|
//#endregion
|
|
117
|
+
//#region src/template.d.ts
|
|
118
|
+
/** True when an item is a template. */
|
|
119
|
+
declare function isTemplateItem(item: RegistryItem): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Narrow an item's `meta` to a {@link TemplateMeta}, throwing on shape mismatch.
|
|
122
|
+
* Use after {@link validateTemplate} has passed.
|
|
123
|
+
*/
|
|
124
|
+
declare function asTemplateMeta(meta: unknown): TemplateMeta;
|
|
125
|
+
/**
|
|
126
|
+
* Validate a template item beyond the base item schema. Returns a list of
|
|
127
|
+
* errors (empty when valid). Requires:
|
|
128
|
+
* - `type === 'template'`
|
|
129
|
+
* - a valid {@link TemplateMeta}
|
|
130
|
+
* - at least one component (`registryDependencies`) or one `page` file
|
|
131
|
+
* - a non-empty `license`
|
|
132
|
+
* - every `files[].target` present in `meta.fileRoles`
|
|
133
|
+
*/
|
|
134
|
+
declare function validateTemplate(item: RegistryItem): string[];
|
|
135
|
+
//#endregion
|
|
71
136
|
//#region src/build.d.ts
|
|
72
137
|
declare function buildRegistry(index: RegistryIndex, outDir: string): Promise<void>;
|
|
73
138
|
//#endregion
|
|
@@ -107,6 +172,8 @@ declare function writeShadcnRegistry(index: RegistryIndex, outDir: string, opts?
|
|
|
107
172
|
declare function matchAdvisories(item: Pick<RegistryItem, 'advisories'>, lockedVersion: string): RegistryAdvisory[];
|
|
108
173
|
//#endregion
|
|
109
174
|
//#region src/directory.d.ts
|
|
175
|
+
/** What kinds of items a directory-listed registry publishes. */
|
|
176
|
+
type DirectoryProvides = 'component' | 'template' | 'theme';
|
|
110
177
|
interface DirectoryEntry {
|
|
111
178
|
namespace: string;
|
|
112
179
|
name: string;
|
|
@@ -115,6 +182,8 @@ interface DirectoryEntry {
|
|
|
115
182
|
registryUrl: string;
|
|
116
183
|
tags: string[];
|
|
117
184
|
verified: boolean;
|
|
185
|
+
/** Item kinds this registry provides. Absent ⇒ component-only. */
|
|
186
|
+
provides?: DirectoryProvides[];
|
|
118
187
|
}
|
|
119
188
|
interface RegistryDirectory {
|
|
120
189
|
schemaVersion: 1;
|
|
@@ -127,5 +196,59 @@ interface DirectoryValidationResult {
|
|
|
127
196
|
}
|
|
128
197
|
declare function validateDirectory(raw: unknown): DirectoryValidationResult;
|
|
129
198
|
//#endregion
|
|
130
|
-
|
|
199
|
+
//#region src/marketplace.d.ts
|
|
200
|
+
/** A template projected into a flat, gallery-ready record. */
|
|
201
|
+
interface MarketplaceTemplate {
|
|
202
|
+
name: string;
|
|
203
|
+
namespace: string;
|
|
204
|
+
description: string;
|
|
205
|
+
category: string;
|
|
206
|
+
framework: string;
|
|
207
|
+
tags: string[];
|
|
208
|
+
/** Component names the template composes (from `registryDependencies`). */
|
|
209
|
+
components: string[];
|
|
210
|
+
screenshots: TemplateScreenshot[];
|
|
211
|
+
demoUrl?: string;
|
|
212
|
+
version: string;
|
|
213
|
+
license: string;
|
|
214
|
+
verified: boolean;
|
|
215
|
+
homepage?: string;
|
|
216
|
+
/** The spec a user passes to `cascivo add` (e.g. "@acme/dashboard"). */
|
|
217
|
+
installSpec: string;
|
|
218
|
+
/** GitHub stars, baked at CI build time (omitted when unknown). */
|
|
219
|
+
stars?: number;
|
|
220
|
+
}
|
|
221
|
+
/** The static marketplace catalog written to `apps/site/public/marketplace.json`. */
|
|
222
|
+
interface MarketplaceCatalog {
|
|
223
|
+
generatedAt: string;
|
|
224
|
+
templates: MarketplaceTemplate[];
|
|
225
|
+
facets: {
|
|
226
|
+
categories: string[];
|
|
227
|
+
frameworks: string[];
|
|
228
|
+
tags: string[];
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/** One registry's resolved index + the directory metadata that referenced it. */
|
|
232
|
+
interface MarketplaceSource {
|
|
233
|
+
entry: {
|
|
234
|
+
namespace: string;
|
|
235
|
+
verified?: boolean;
|
|
236
|
+
homepage?: string;
|
|
237
|
+
stars?: number;
|
|
238
|
+
};
|
|
239
|
+
items: RegistryItem[];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Project a template item into a gallery record, or `null` if the item is not a
|
|
243
|
+
* valid template. Pure — no I/O.
|
|
244
|
+
*/
|
|
245
|
+
declare function projectTemplate(item: RegistryItem, source: MarketplaceSource['entry']): MarketplaceTemplate | null;
|
|
246
|
+
/**
|
|
247
|
+
* Build the marketplace catalog from a set of resolved registry sources. Pure —
|
|
248
|
+
* the caller does the fetching + star lookups and supplies the timestamp.
|
|
249
|
+
* Templates are sorted verified-first, then by stars (desc), then by name.
|
|
250
|
+
*/
|
|
251
|
+
declare function buildCatalog(sources: MarketplaceSource[], generatedAt: string): MarketplaceCatalog;
|
|
252
|
+
//#endregion
|
|
253
|
+
export { type DirectoryEntry, type DirectoryProvides, type DirectoryValidationResult, type LegacyRegistry, type LegacyRegistryEntry, type MarketplaceCatalog, type MarketplaceSource, type MarketplaceTemplate, type RegistryAdvisory, type RegistryDirectory, type RegistryFile, type RegistryIndex, type RegistryItem, type RegistryItemType, type ShadcnItemType, type ShadcnRegistryFile, type ShadcnRegistryItem, type TemplateFileRole, type TemplateMeta, type TemplateScreenshot, type ToShadcnOptions, type ValidationResult, asTemplateMeta, buildCatalog, buildRegistry, isTemplateItem, matchAdvisories, parseLegacyRegistry, projectTemplate, shadcnName, toShadcnItem, validateDirectory, validateIndex, validateItem, validateTemplate, writeShadcnRegistry };
|
|
131
254
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/validate.ts","../src/build.ts","../src/shadcn.ts","../src/advisories.ts","../src/directory.ts"],"mappings":";UAAiB,YAAA;EACf,GAAA;EACA,MAAM;AAAA;AAAA,KAGI,gBAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/validate.ts","../src/template.ts","../src/build.ts","../src/shadcn.ts","../src/advisories.ts","../src/directory.ts","../src/marketplace.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,8 @@ const VALID_TYPES = new Set([
|
|
|
8
8
|
"chart",
|
|
9
9
|
"section",
|
|
10
10
|
"theme",
|
|
11
|
-
"style"
|
|
11
|
+
"style",
|
|
12
|
+
"template"
|
|
12
13
|
]);
|
|
13
14
|
const VALID_SEVERITIES = new Set([
|
|
14
15
|
"low",
|
|
@@ -130,6 +131,86 @@ function parseLegacyRegistry(raw) {
|
|
|
130
131
|
};
|
|
131
132
|
}
|
|
132
133
|
//#endregion
|
|
134
|
+
//#region src/template.ts
|
|
135
|
+
const VALID_FRAMEWORKS = new Set(["react-vite", "react-next"]);
|
|
136
|
+
const VALID_FILE_ROLES = new Set([
|
|
137
|
+
"page",
|
|
138
|
+
"fixture",
|
|
139
|
+
"asset",
|
|
140
|
+
"config"
|
|
141
|
+
]);
|
|
142
|
+
/** True when an item is a template. */
|
|
143
|
+
function isTemplateItem(item) {
|
|
144
|
+
return item.type === "template";
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Narrow an item's `meta` to a {@link TemplateMeta}, throwing on shape mismatch.
|
|
148
|
+
* Use after {@link validateTemplate} has passed.
|
|
149
|
+
*/
|
|
150
|
+
function asTemplateMeta(meta) {
|
|
151
|
+
const errors = validateTemplateMeta(meta);
|
|
152
|
+
if (errors.length > 0) throw new Error(`Invalid TemplateMeta: ${errors.join("; ")}`);
|
|
153
|
+
return meta;
|
|
154
|
+
}
|
|
155
|
+
function validateTemplateMeta(meta) {
|
|
156
|
+
const errors = [];
|
|
157
|
+
if (!meta || typeof meta !== "object") return ["meta must be a TemplateMeta object"];
|
|
158
|
+
const m = meta;
|
|
159
|
+
if (typeof m["intent"] !== "string" || !m["intent"]) errors.push("meta.intent is required and must be a non-empty string");
|
|
160
|
+
if (!VALID_FRAMEWORKS.has(m["framework"])) errors.push(`meta.framework must be one of ${[...VALID_FRAMEWORKS].join("|")}`);
|
|
161
|
+
if (typeof m["category"] !== "string" || !m["category"]) errors.push("meta.category is required and must be a non-empty string");
|
|
162
|
+
if (!Array.isArray(m["screenshots"]) || m["screenshots"].length === 0) errors.push("meta.screenshots is required and must have at least one entry");
|
|
163
|
+
else for (let i = 0; i < m["screenshots"].length; i++) {
|
|
164
|
+
const shot = m["screenshots"][i];
|
|
165
|
+
if (!shot || typeof shot !== "object") {
|
|
166
|
+
errors.push(`meta.screenshots[${i}] must be an object`);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (typeof shot["light"] !== "string" || !shot["light"]) errors.push(`meta.screenshots[${i}].light is required`);
|
|
170
|
+
if (typeof shot["alt"] !== "string" || !shot["alt"]) errors.push(`meta.screenshots[${i}].alt is required`);
|
|
171
|
+
}
|
|
172
|
+
if (m["demoUrl"] !== void 0 && typeof m["demoUrl"] !== "string") errors.push("meta.demoUrl must be a string when present");
|
|
173
|
+
if (!m["fileRoles"] || typeof m["fileRoles"] !== "object") errors.push("meta.fileRoles is required and must be an object");
|
|
174
|
+
else for (const [target, role] of Object.entries(m["fileRoles"])) if (!VALID_FILE_ROLES.has(role)) errors.push(`meta.fileRoles["${target}"] must be one of ${[...VALID_FILE_ROLES].join("|")}`);
|
|
175
|
+
if (m["pages"] !== void 0) if (!Array.isArray(m["pages"])) errors.push("meta.pages must be an array when present");
|
|
176
|
+
else for (let i = 0; i < m["pages"].length; i++) {
|
|
177
|
+
const page = m["pages"][i];
|
|
178
|
+
if (!page || typeof page !== "object") {
|
|
179
|
+
errors.push(`meta.pages[${i}] must be an object`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (typeof page["name"] !== "string") errors.push(`meta.pages[${i}].name is required`);
|
|
183
|
+
if (typeof page["target"] !== "string") errors.push(`meta.pages[${i}].target is required`);
|
|
184
|
+
}
|
|
185
|
+
return errors;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Validate a template item beyond the base item schema. Returns a list of
|
|
189
|
+
* errors (empty when valid). Requires:
|
|
190
|
+
* - `type === 'template'`
|
|
191
|
+
* - a valid {@link TemplateMeta}
|
|
192
|
+
* - at least one component (`registryDependencies`) or one `page` file
|
|
193
|
+
* - a non-empty `license`
|
|
194
|
+
* - every `files[].target` present in `meta.fileRoles`
|
|
195
|
+
*/
|
|
196
|
+
function validateTemplate(item) {
|
|
197
|
+
const errors = [];
|
|
198
|
+
if (item.type !== "template") {
|
|
199
|
+
errors.push(`type must be 'template', got ${JSON.stringify(item.type)}`);
|
|
200
|
+
return errors;
|
|
201
|
+
}
|
|
202
|
+
const metaErrors = validateTemplateMeta(item.meta);
|
|
203
|
+
errors.push(...metaErrors);
|
|
204
|
+
const fileRoles = (item.meta ?? {}).fileRoles ?? {};
|
|
205
|
+
const hasComponents = Array.isArray(item.registryDependencies) ? item.registryDependencies.length > 0 : false;
|
|
206
|
+
const hasPageFile = Object.values(fileRoles).includes("page");
|
|
207
|
+
if (!hasComponents && !hasPageFile) errors.push("a template must list at least one component in registryDependencies or one page file");
|
|
208
|
+
if (typeof item.license !== "string" || !item.license) errors.push("license is required for a template");
|
|
209
|
+
for (const file of item.files ?? []) if (!file.target) errors.push(`template file ${JSON.stringify(file.url)} must declare a target`);
|
|
210
|
+
else if (metaErrors.length === 0 && !(file.target in fileRoles)) errors.push(`file target "${file.target}" is missing from meta.fileRoles`);
|
|
211
|
+
return errors;
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
133
214
|
//#region src/build.ts
|
|
134
215
|
function flatName(name) {
|
|
135
216
|
return name.replace(/\//g, "-");
|
|
@@ -185,7 +266,8 @@ const TYPE_MAP = {
|
|
|
185
266
|
section: "registry:block",
|
|
186
267
|
chart: "registry:component",
|
|
187
268
|
theme: "registry:theme",
|
|
188
|
-
style: "registry:style"
|
|
269
|
+
style: "registry:style",
|
|
270
|
+
template: "registry:block"
|
|
189
271
|
};
|
|
190
272
|
/** Flatten a possibly-prefixed cascivo name ("block/x") into a shadcn slug. */
|
|
191
273
|
function shadcnName(name) {
|
|
@@ -297,6 +379,11 @@ function matchAdvisories(item, lockedVersion) {
|
|
|
297
379
|
}
|
|
298
380
|
//#endregion
|
|
299
381
|
//#region src/directory.ts
|
|
382
|
+
const VALID_PROVIDES = new Set([
|
|
383
|
+
"component",
|
|
384
|
+
"template",
|
|
385
|
+
"theme"
|
|
386
|
+
]);
|
|
300
387
|
function levenshtein(a, b) {
|
|
301
388
|
const m = a.length;
|
|
302
389
|
const n = b.length;
|
|
@@ -338,6 +425,10 @@ function validateDirectory(raw) {
|
|
|
338
425
|
else if (!entry["registryUrl"].includes("{name}")) errors.push(`registries[${i}].registryUrl must contain {name} placeholder`);
|
|
339
426
|
if (!Array.isArray(entry["tags"])) errors.push(`registries[${i}].tags must be an array`);
|
|
340
427
|
if (typeof entry["verified"] !== "boolean") errors.push(`registries[${i}].verified must be a boolean`);
|
|
428
|
+
if (entry["provides"] !== void 0) {
|
|
429
|
+
if (!Array.isArray(entry["provides"])) errors.push(`registries[${i}].provides must be an array when present`);
|
|
430
|
+
else for (const p of entry["provides"]) if (!VALID_PROVIDES.has(p)) errors.push(`registries[${i}].provides has invalid value ${JSON.stringify(p)} (allowed: ${[...VALID_PROVIDES].join("|")})`);
|
|
431
|
+
}
|
|
341
432
|
}
|
|
342
433
|
for (let i = 0; i < namespaces.length; i++) for (let j = i + 1; j < namespaces.length; j++) {
|
|
343
434
|
const a = namespaces[i];
|
|
@@ -351,6 +442,71 @@ function validateDirectory(raw) {
|
|
|
351
442
|
};
|
|
352
443
|
}
|
|
353
444
|
//#endregion
|
|
354
|
-
|
|
445
|
+
//#region src/marketplace.ts
|
|
446
|
+
/**
|
|
447
|
+
* Project a template item into a gallery record, or `null` if the item is not a
|
|
448
|
+
* valid template. Pure — no I/O.
|
|
449
|
+
*/
|
|
450
|
+
function projectTemplate(item, source) {
|
|
451
|
+
if (!isTemplateItem(item)) return null;
|
|
452
|
+
let meta;
|
|
453
|
+
try {
|
|
454
|
+
meta = asTemplateMeta(item.meta);
|
|
455
|
+
} catch {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
const record = {
|
|
459
|
+
name: item.name,
|
|
460
|
+
namespace: source.namespace,
|
|
461
|
+
description: item.description,
|
|
462
|
+
category: meta.category,
|
|
463
|
+
framework: meta.framework,
|
|
464
|
+
tags: item.tags ?? [],
|
|
465
|
+
components: item.registryDependencies ?? [],
|
|
466
|
+
screenshots: meta.screenshots,
|
|
467
|
+
version: item.version,
|
|
468
|
+
license: item.license ?? "",
|
|
469
|
+
verified: source.verified ?? false,
|
|
470
|
+
installSpec: `${source.namespace}/${item.name}`
|
|
471
|
+
};
|
|
472
|
+
if (meta.demoUrl) record.demoUrl = meta.demoUrl;
|
|
473
|
+
const homepage = item.homepage ?? source.homepage;
|
|
474
|
+
if (homepage) record.homepage = homepage;
|
|
475
|
+
if (typeof source.stars === "number") record.stars = source.stars;
|
|
476
|
+
return record;
|
|
477
|
+
}
|
|
478
|
+
function uniqueSorted(values) {
|
|
479
|
+
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Build the marketplace catalog from a set of resolved registry sources. Pure —
|
|
483
|
+
* the caller does the fetching + star lookups and supplies the timestamp.
|
|
484
|
+
* Templates are sorted verified-first, then by stars (desc), then by name.
|
|
485
|
+
*/
|
|
486
|
+
function buildCatalog(sources, generatedAt) {
|
|
487
|
+
const templates = [];
|
|
488
|
+
for (const src of sources) for (const item of src.items) {
|
|
489
|
+
const record = projectTemplate(item, src.entry);
|
|
490
|
+
if (record) templates.push(record);
|
|
491
|
+
}
|
|
492
|
+
templates.sort((a, b) => {
|
|
493
|
+
if (a.verified !== b.verified) return a.verified ? -1 : 1;
|
|
494
|
+
const sa = a.stars ?? 0;
|
|
495
|
+
const sb = b.stars ?? 0;
|
|
496
|
+
if (sa !== sb) return sb - sa;
|
|
497
|
+
return a.name.localeCompare(b.name);
|
|
498
|
+
});
|
|
499
|
+
return {
|
|
500
|
+
generatedAt,
|
|
501
|
+
templates,
|
|
502
|
+
facets: {
|
|
503
|
+
categories: uniqueSorted(templates.map((t) => t.category)),
|
|
504
|
+
frameworks: uniqueSorted(templates.map((t) => t.framework)),
|
|
505
|
+
tags: uniqueSorted(templates.flatMap((t) => t.tags))
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
//#endregion
|
|
510
|
+
export { asTemplateMeta, buildCatalog, buildRegistry, isTemplateItem, matchAdvisories, parseLegacyRegistry, projectTemplate, shadcnName, toShadcnItem, validateDirectory, validateIndex, validateItem, validateTemplate, writeShadcnRegistry };
|
|
355
511
|
|
|
356
512
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/validate.ts","../src/build.ts","../src/shadcn.ts","../src/advisories.ts","../src/directory.ts"],"sourcesContent":["import type { RegistryIndex, RegistryItem } from './types.ts'\n\nexport interface ValidationResult {\n ok: boolean\n errors: string[]\n warnings: string[]\n}\n\nconst VALID_TYPES = new Set(['component', 'layout', 'block', 'chart', 'section', 'theme', 'style'])\nconst VALID_SEVERITIES = new Set(['low', 'moderate', 'high', 'critical'])\n\nfunction ok(warnings: string[] = []): ValidationResult {\n return { ok: true, errors: [], warnings }\n}\n\nfunction fail(errors: string[], warnings: string[] = []): ValidationResult {\n return { ok: false, errors, warnings }\n}\n\nexport function validateItem(raw: unknown): ValidationResult {\n if (!raw || typeof raw !== 'object') {\n return fail(['Item must be an object'])\n }\n const obj = raw as Record<string, unknown>\n const errors: string[] = []\n const warnings: string[] = []\n\n if (obj['schemaVersion'] !== 2) {\n errors.push(`schemaVersion must be 2, got ${JSON.stringify(obj['schemaVersion'])}`)\n }\n if (typeof obj['name'] !== 'string' || !obj['name']) {\n errors.push('name is required and must be a non-empty string')\n }\n if (!VALID_TYPES.has(obj['type'] as string)) {\n errors.push(\n `type must be one of ${[...VALID_TYPES].join('|')}, got ${JSON.stringify(obj['type'])}`,\n )\n }\n if (typeof obj['description'] !== 'string') {\n errors.push('description is required and must be a string')\n }\n if (typeof obj['version'] !== 'string' || !obj['version']) {\n errors.push('version is required and must be a non-empty string')\n }\n if (!Array.isArray(obj['files'])) {\n errors.push('files is required and must be an array')\n }\n if (!Array.isArray(obj['dependencies'])) {\n errors.push('dependencies is required and must be an array')\n }\n if (!Array.isArray(obj['tags'])) {\n errors.push('tags is required and must be an array')\n }\n\n if (obj['advisories'] !== undefined) {\n if (!Array.isArray(obj['advisories'])) {\n errors.push('advisories must be an array')\n } else {\n const ids = new Set<string>()\n for (const adv of obj['advisories'] as unknown[]) {\n if (!adv || typeof adv !== 'object') {\n errors.push('each advisory must be an object')\n continue\n }\n const a = adv as Record<string, unknown>\n if (typeof a['id'] !== 'string') errors.push('advisory.id must be a string')\n else if (ids.has(a['id'] as string)) errors.push(`advisory id \"${a['id']}\" is not unique`)\n else ids.add(a['id'] as string)\n if (!VALID_SEVERITIES.has(a['severity'] as string))\n errors.push(`advisory.severity must be one of ${[...VALID_SEVERITIES].join('|')}`)\n if (typeof a['affectedVersions'] !== 'string')\n errors.push('advisory.affectedVersions must be a string')\n if (typeof a['summary'] !== 'string') errors.push('advisory.summary must be a string')\n }\n }\n }\n\n const knownKeys = new Set([\n 'schemaVersion',\n 'name',\n 'type',\n 'description',\n 'category',\n 'version',\n 'changelog',\n 'author',\n 'license',\n 'homepage',\n 'files',\n 'install',\n 'dependencies',\n 'registryDependencies',\n 'tags',\n 'meta',\n 'advisories',\n ])\n for (const key of Object.keys(obj)) {\n if (!knownKeys.has(key)) {\n warnings.push(`Unknown field \"${key}\" — ignored (forward compat)`)\n }\n }\n\n return errors.length > 0 ? fail(errors, warnings) : ok(warnings)\n}\n\nexport function validateIndex(raw: unknown): ValidationResult {\n if (!raw || typeof raw !== 'object') {\n return fail(['Index must be an object'])\n }\n const obj = raw as Record<string, unknown>\n const errors: string[] = []\n const warnings: string[] = []\n\n if (obj['schemaVersion'] !== 2) {\n errors.push(`schemaVersion must be 2, got ${JSON.stringify(obj['schemaVersion'])}`)\n }\n if (typeof obj['name'] !== 'string' || !obj['name']) {\n errors.push('name is required and must be a non-empty string')\n }\n if (!Array.isArray(obj['items'])) {\n errors.push('items is required and must be an array')\n } else {\n for (let i = 0; i < obj['items'].length; i++) {\n const itemResult = validateItem(obj['items'][i])\n for (const e of itemResult.errors) errors.push(`items[${i}]: ${e}`)\n for (const w of itemResult.warnings) warnings.push(`items[${i}]: ${w}`)\n }\n }\n\n const knownKeys = new Set(['schemaVersion', 'name', 'homepage', 'items'])\n for (const key of Object.keys(obj)) {\n if (!knownKeys.has(key)) {\n warnings.push(`Unknown field \"${key}\" — ignored (forward compat)`)\n }\n }\n\n return errors.length > 0 ? fail(errors, warnings) : ok(warnings)\n}\n\nexport function parseLegacyRegistry(raw: unknown): RegistryIndex {\n if (!raw || typeof raw !== 'object') throw new Error('Legacy registry must be an object')\n const obj = raw as Record<string, unknown>\n const topVersion = typeof obj['version'] === 'string' ? obj['version'] : '0.0.0'\n const components = Array.isArray(obj['components']) ? obj['components'] : []\n\n return {\n schemaVersion: 2,\n name: 'cascivo',\n items: (components as Record<string, unknown>[]).map((c) => {\n const item: RegistryItem = {\n schemaVersion: 2,\n name: typeof c['name'] === 'string' ? c['name'] : '',\n type: (c['type'] as RegistryItem['type']) ?? 'component',\n description: typeof c['description'] === 'string' ? c['description'] : '',\n version: topVersion,\n files: Array.isArray(c['files']) ? (c['files'] as string[]).map((url) => ({ url })) : [],\n dependencies: Array.isArray(c['dependencies']) ? (c['dependencies'] as string[]) : [],\n tags: Array.isArray(c['tags']) ? (c['tags'] as string[]) : [],\n }\n if (typeof c['category'] === 'string') item.category = c['category']\n if (typeof c['install'] === 'string') item.install = c['install']\n if (c['meta']) item.meta = c['meta'] as RegistryItem['meta']\n return item\n }),\n }\n}\n","import { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { RegistryIndex, RegistryItem } from './types.ts'\n\nfunction flatName(name: string): string {\n return name.replace(/\\//g, '-')\n}\n\nfunction itemWithoutExamples(item: RegistryItem): RegistryItem {\n if (!item.meta || typeof item.meta !== 'object') return item\n const meta = item.meta as Record<string, unknown>\n if (!Array.isArray(meta['examples']) || meta['examples'].length === 0) return item\n return { ...item, meta: { ...meta, examples: [] } }\n}\n\nexport async function buildRegistry(index: RegistryIndex, outDir: string): Promise<void> {\n await mkdir(outDir, { recursive: true })\n\n const sortedItems = [...index.items].sort((a, b) => a.name.localeCompare(b.name))\n\n const indexOut: RegistryIndex = {\n schemaVersion: 2,\n name: index.name,\n ...(index.homepage ? { homepage: index.homepage } : {}),\n items: sortedItems.map(itemWithoutExamples),\n }\n await writeFile(join(outDir, 'registry.json'), `${JSON.stringify(indexOut, null, 2)}\\n`, 'utf8')\n\n for (const item of sortedItems) {\n const flat = flatName(item.name)\n await writeFile(join(outDir, `${flat}.json`), `${JSON.stringify(item, null, 2)}\\n`, 'utf8')\n await writeFile(\n join(outDir, `${flat}@${item.version}.json`),\n `${JSON.stringify(item, null, 2)}\\n`,\n 'utf8',\n )\n }\n}\n","/**\n * shadcn-registry-compatible projection of the cascivo registry.\n *\n * This is purely ADDITIVE interop: it maps cascivo's own `RegistryItem`s into\n * the shadcn `registry:*` item schema so the shadcn CLI can install a cascivo\n * component (`npx shadcn add <host>/r/shadcn/<name>.json`). cascivo's own\n * `registry.json` schema is untouched.\n *\n * cascivo is CSS-native: components ship `.tsx` + `.module.css` and rely on a\n * `@cascivo/themes` import plus the `@cascivo/core` peer — there is no Tailwind\n * config, so no `cssVars`/`tailwind` block is emitted.\n */\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { RegistryIndex, RegistryItem, RegistryItemType } from './types.ts'\n\nconst SHADCN_ITEM_SCHEMA = 'https://ui.shadcn.com/schema/registry-item.json'\nconst SHADCN_REGISTRY_SCHEMA = 'https://ui.shadcn.com/schema/registry.json'\n\nexport type ShadcnItemType =\n | 'registry:component'\n | 'registry:block'\n | 'registry:lib'\n | 'registry:ui'\n | 'registry:hook'\n | 'registry:theme'\n | 'registry:style'\n | 'registry:file'\n\nexport interface ShadcnRegistryFile {\n path: string\n type: ShadcnItemType\n content?: string\n target?: string\n}\n\nexport interface ShadcnRegistryItem {\n $schema: string\n name: string\n type: ShadcnItemType\n description?: string\n dependencies?: string[]\n registryDependencies?: string[]\n files: ShadcnRegistryFile[]\n categories?: string[]\n meta?: Record<string, unknown>\n}\n\nexport interface ToShadcnOptions {\n /** Resolve a cascivo file URL to its source text, to inline as `content`. */\n resolveContent?: (fileUrl: string) => string | undefined\n}\n\nconst TYPE_MAP: Record<RegistryItemType, ShadcnItemType> = {\n component: 'registry:component',\n layout: 'registry:block',\n block: 'registry:block',\n section: 'registry:block',\n chart: 'registry:component',\n theme: 'registry:theme',\n style: 'registry:style',\n}\n\n/** Flatten a possibly-prefixed cascivo name (\"block/x\") into a shadcn slug. */\nexport function shadcnName(name: string): string {\n return name.replace(/\\//g, '-')\n}\n\nfunction filePath(fileUrl: string, slug: string): string {\n const base = fileUrl.split('/').pop() ?? fileUrl\n return `${slug}/${base}`\n}\n\nexport function toShadcnItem(item: RegistryItem, opts: ToShadcnOptions = {}): ShadcnRegistryItem {\n const slug = shadcnName(item.name)\n const type = TYPE_MAP[item.type] ?? 'registry:component'\n\n const files: ShadcnRegistryFile[] = item.files.map((f) => {\n const file: ShadcnRegistryFile = { path: filePath(f.url, slug), type }\n const content = opts.resolveContent?.(f.url)\n if (content !== undefined) file.content = content\n return file\n })\n\n const out: ShadcnRegistryItem = {\n $schema: SHADCN_ITEM_SCHEMA,\n name: slug,\n type,\n files,\n }\n if (item.description) out.description = item.description\n if (item.dependencies.length > 0) out.dependencies = [...item.dependencies]\n if (item.registryDependencies && item.registryDependencies.length > 0) {\n out.registryDependencies = [...item.registryDependencies]\n }\n if (item.category) out.categories = [item.category]\n out.meta = { cascivo: { type: item.type, version: item.version } }\n return out\n}\n\n/**\n * Write one shadcn `r/<name>.json` per item (with inlined file content when a\n * resolver is supplied) plus a lean `registry.json` index (content omitted).\n */\nexport async function writeShadcnRegistry(\n index: RegistryIndex,\n outDir: string,\n opts: ToShadcnOptions = {},\n): Promise<void> {\n await mkdir(outDir, { recursive: true })\n\n const sorted = [...index.items].sort((a, b) => a.name.localeCompare(b.name))\n const items = sorted.map((it) => toShadcnItem(it, opts))\n\n for (const item of items) {\n await writeFile(join(outDir, `${item.name}.json`), `${JSON.stringify(item, null, 2)}\\n`, 'utf8')\n }\n\n const indexItems = items.map((it) => ({\n ...it,\n files: it.files.map((f) => ({\n path: f.path,\n type: f.type,\n ...(f.target ? { target: f.target } : {}),\n })),\n }))\n const registry = {\n $schema: SHADCN_REGISTRY_SCHEMA,\n name: index.name,\n ...(index.homepage ? { homepage: index.homepage } : {}),\n items: indexItems,\n }\n await writeFile(join(outDir, 'registry.json'), `${JSON.stringify(registry, null, 2)}\\n`, 'utf8')\n}\n","import type { RegistryAdvisory, RegistryItem } from './types.ts'\n\nfunction parseVersionParts(v: string): number[] {\n return v\n .replace(/^[^0-9]*/, '')\n .split('.')\n .map(Number)\n}\n\nfunction compareVersions(a: string, b: string): number {\n const pa = parseVersionParts(a)\n const pb = parseVersionParts(b)\n for (let i = 0; i < Math.max(pa.length, pb.length); i++) {\n const na = pa[i] ?? 0\n const nb = pb[i] ?? 0\n if (na !== nb) return na - nb\n }\n return 0\n}\n\nfunction matchesRange(version: string, range: string): boolean {\n try {\n const trimmed = range.trim()\n if (trimmed.startsWith('>=')) {\n return compareVersions(version, trimmed.slice(2).trim()) >= 0\n }\n if (trimmed.startsWith('>')) {\n return compareVersions(version, trimmed.slice(1).trim()) > 0\n }\n if (trimmed.startsWith('<=')) {\n return compareVersions(version, trimmed.slice(2).trim()) <= 0\n }\n if (trimmed.startsWith('<')) {\n return compareVersions(version, trimmed.slice(1).trim()) < 0\n }\n if (trimmed.startsWith('^')) {\n const base = trimmed.slice(1).trim()\n const baseParts = parseVersionParts(base)\n const major = baseParts[0] ?? 0\n const vParts = parseVersionParts(version)\n const vMajor = vParts[0] ?? 0\n return vMajor === major && compareVersions(version, base) >= 0\n }\n if (trimmed.startsWith('~')) {\n const base = trimmed.slice(1).trim()\n const baseParts = parseVersionParts(base)\n const major = baseParts[0] ?? 0\n const minor = baseParts[1] ?? 0\n const vParts = parseVersionParts(version)\n const vMajor = vParts[0] ?? 0\n const vMinor = vParts[1] ?? 0\n return vMajor === major && vMinor === minor && compareVersions(version, base) >= 0\n }\n return version === trimmed\n } catch {\n return false\n }\n}\n\nexport function matchAdvisories(\n item: Pick<RegistryItem, 'advisories'>,\n lockedVersion: string,\n): RegistryAdvisory[] {\n if (!item.advisories) return []\n const matched: RegistryAdvisory[] = []\n for (const adv of item.advisories) {\n try {\n if (matchesRange(lockedVersion, adv.affectedVersions)) {\n matched.push(adv)\n }\n } catch {\n // malformed range — skip silently, never crash an audit\n }\n }\n return matched\n}\n","export interface DirectoryEntry {\n namespace: string\n name: string\n description: string\n homepage: string\n registryUrl: string\n tags: string[]\n verified: boolean\n}\n\nexport interface RegistryDirectory {\n schemaVersion: 1\n registries: DirectoryEntry[]\n}\n\nexport interface DirectoryValidationResult {\n ok: boolean\n errors: string[]\n warnings: string[]\n}\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length\n const n = b.length\n const dp: number[][] = Array.from({ length: m + 1 }, (_, i) =>\n Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)),\n )\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i]![j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1]![j - 1]!\n : 1 + Math.min(dp[i - 1]![j]!, dp[i]![j - 1]!, dp[i - 1]![j - 1]!)\n }\n }\n return dp[m]![n]!\n}\n\nexport function validateDirectory(raw: unknown): DirectoryValidationResult {\n if (!raw || typeof raw !== 'object') {\n return { ok: false, errors: ['Directory must be an object'], warnings: [] }\n }\n const obj = raw as Record<string, unknown>\n const errors: string[] = []\n const warnings: string[] = []\n\n if (obj['schemaVersion'] !== 1) {\n errors.push(`schemaVersion must be 1, got ${JSON.stringify(obj['schemaVersion'])}`)\n }\n if (!Array.isArray(obj['registries'])) {\n errors.push('registries must be an array')\n return { ok: false, errors, warnings }\n }\n\n const namespaces: string[] = []\n for (let i = 0; i < obj['registries'].length; i++) {\n const entry = obj['registries'][i] as Record<string, unknown>\n const ns = entry['namespace']\n if (typeof ns !== 'string' || !ns) errors.push(`registries[${i}].namespace is required`)\n else {\n if (namespaces.includes(ns)) errors.push(`Duplicate namespace \"${ns}\" at index ${i}`)\n namespaces.push(ns)\n }\n if (typeof entry['name'] !== 'string') errors.push(`registries[${i}].name is required`)\n if (typeof entry['description'] !== 'string')\n errors.push(`registries[${i}].description is required`)\n if (typeof entry['homepage'] !== 'string') errors.push(`registries[${i}].homepage is required`)\n if (typeof entry['registryUrl'] !== 'string')\n errors.push(`registries[${i}].registryUrl is required`)\n else if (!(entry['registryUrl'] as string).includes('{name}'))\n errors.push(`registries[${i}].registryUrl must contain {name} placeholder`)\n if (!Array.isArray(entry['tags'])) errors.push(`registries[${i}].tags must be an array`)\n if (typeof entry['verified'] !== 'boolean')\n errors.push(`registries[${i}].verified must be a boolean`)\n }\n\n for (let i = 0; i < namespaces.length; i++) {\n for (let j = i + 1; j < namespaces.length; j++) {\n const a = namespaces[i]!\n const b = namespaces[j]!\n if (levenshtein(a, b) <= 2) {\n warnings.push(\n `Namespaces \"${a}\" and \"${b}\" are suspiciously similar (Levenshtein distance ≤ 2)`,\n )\n }\n }\n }\n\n return { ok: errors.length === 0, errors, warnings }\n}\n"],"mappings":";;;AAQA,MAAM,cAAc,IAAI,IAAI;CAAC;CAAa;CAAU;CAAS;CAAS;CAAW;CAAS;AAAO,CAAC;AAClG,MAAM,mBAAmB,IAAI,IAAI;CAAC;CAAO;CAAY;CAAQ;AAAU,CAAC;AAExE,SAAS,GAAG,WAAqB,CAAC,GAAqB;CACrD,OAAO;EAAE,IAAI;EAAM,QAAQ,CAAC;EAAG;CAAS;AAC1C;AAEA,SAAS,KAAK,QAAkB,WAAqB,CAAC,GAAqB;CACzE,OAAO;EAAE,IAAI;EAAO;EAAQ;CAAS;AACvC;AAEA,SAAgB,aAAa,KAAgC;CAC3D,IAAI,CAAC,OAAO,OAAO,QAAQ,UACzB,OAAO,KAAK,CAAC,wBAAwB,CAAC;CAExC,MAAM,MAAM;CACZ,MAAM,SAAmB,CAAC;CAC1B,MAAM,WAAqB,CAAC;CAE5B,IAAI,IAAI,qBAAqB,GAC3B,OAAO,KAAK,gCAAgC,KAAK,UAAU,IAAI,gBAAgB,GAAG;CAEpF,IAAI,OAAO,IAAI,YAAY,YAAY,CAAC,IAAI,SAC1C,OAAO,KAAK,iDAAiD;CAE/D,IAAI,CAAC,YAAY,IAAI,IAAI,OAAiB,GACxC,OAAO,KACL,uBAAuB,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,GAAG,EAAE,QAAQ,KAAK,UAAU,IAAI,OAAO,GACtF;CAEF,IAAI,OAAO,IAAI,mBAAmB,UAChC,OAAO,KAAK,8CAA8C;CAE5D,IAAI,OAAO,IAAI,eAAe,YAAY,CAAC,IAAI,YAC7C,OAAO,KAAK,oDAAoD;CAElE,IAAI,CAAC,MAAM,QAAQ,IAAI,QAAQ,GAC7B,OAAO,KAAK,wCAAwC;CAEtD,IAAI,CAAC,MAAM,QAAQ,IAAI,eAAe,GACpC,OAAO,KAAK,+CAA+C;CAE7D,IAAI,CAAC,MAAM,QAAQ,IAAI,OAAO,GAC5B,OAAO,KAAK,uCAAuC;CAGrD,IAAI,IAAI,kBAAkB,KAAA,GACxB,IAAI,CAAC,MAAM,QAAQ,IAAI,aAAa,GAClC,OAAO,KAAK,6BAA6B;MACpC;EACL,MAAM,sBAAM,IAAI,IAAY;EAC5B,KAAK,MAAM,OAAO,IAAI,eAA4B;GAChD,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;IACnC,OAAO,KAAK,iCAAiC;IAC7C;GACF;GACA,MAAM,IAAI;GACV,IAAI,OAAO,EAAE,UAAU,UAAU,OAAO,KAAK,8BAA8B;QACtE,IAAI,IAAI,IAAI,EAAE,KAAe,GAAG,OAAO,KAAK,gBAAgB,EAAE,MAAM,gBAAgB;QACpF,IAAI,IAAI,EAAE,KAAe;GAC9B,IAAI,CAAC,iBAAiB,IAAI,EAAE,WAAqB,GAC/C,OAAO,KAAK,oCAAoC,CAAC,GAAG,gBAAgB,CAAC,CAAC,KAAK,GAAG,GAAG;GACnF,IAAI,OAAO,EAAE,wBAAwB,UACnC,OAAO,KAAK,4CAA4C;GAC1D,IAAI,OAAO,EAAE,eAAe,UAAU,OAAO,KAAK,mCAAmC;EACvF;CACF;CAGF,MAAM,YAAY,IAAI,IAAI;EACxB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CACD,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,GAC/B,IAAI,CAAC,UAAU,IAAI,GAAG,GACpB,SAAS,KAAK,kBAAkB,IAAI,6BAA6B;CAIrE,OAAO,OAAO,SAAS,IAAI,KAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ;AACjE;AAEA,SAAgB,cAAc,KAAgC;CAC5D,IAAI,CAAC,OAAO,OAAO,QAAQ,UACzB,OAAO,KAAK,CAAC,yBAAyB,CAAC;CAEzC,MAAM,MAAM;CACZ,MAAM,SAAmB,CAAC;CAC1B,MAAM,WAAqB,CAAC;CAE5B,IAAI,IAAI,qBAAqB,GAC3B,OAAO,KAAK,gCAAgC,KAAK,UAAU,IAAI,gBAAgB,GAAG;CAEpF,IAAI,OAAO,IAAI,YAAY,YAAY,CAAC,IAAI,SAC1C,OAAO,KAAK,iDAAiD;CAE/D,IAAI,CAAC,MAAM,QAAQ,IAAI,QAAQ,GAC7B,OAAO,KAAK,wCAAwC;MAEpD,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,CAAC,QAAQ,KAAK;EAC5C,MAAM,aAAa,aAAa,IAAI,QAAQ,CAAC,EAAE;EAC/C,KAAK,MAAM,KAAK,WAAW,QAAQ,OAAO,KAAK,SAAS,EAAE,KAAK,GAAG;EAClE,KAAK,MAAM,KAAK,WAAW,UAAU,SAAS,KAAK,SAAS,EAAE,KAAK,GAAG;CACxE;CAGF,MAAM,YAAY,IAAI,IAAI;EAAC;EAAiB;EAAQ;EAAY;CAAO,CAAC;CACxE,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,GAC/B,IAAI,CAAC,UAAU,IAAI,GAAG,GACpB,SAAS,KAAK,kBAAkB,IAAI,6BAA6B;CAIrE,OAAO,OAAO,SAAS,IAAI,KAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ;AACjE;AAEA,SAAgB,oBAAoB,KAA6B;CAC/D,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,MAAM,IAAI,MAAM,mCAAmC;CACxF,MAAM,MAAM;CACZ,MAAM,aAAa,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;CAGzE,OAAO;EACL,eAAe;EACf,MAAM;EACN,QALiB,MAAM,QAAQ,IAAI,aAAa,IAAI,IAAI,gBAAgB,CAAC,EAAA,CAKxB,KAAK,MAAM;GAC1D,MAAM,OAAqB;IACzB,eAAe;IACf,MAAM,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;IAClD,MAAO,EAAE,WAAoC;IAC7C,aAAa,OAAO,EAAE,mBAAmB,WAAW,EAAE,iBAAiB;IACvE,SAAS;IACT,OAAO,MAAM,QAAQ,EAAE,QAAQ,IAAK,EAAE,QAAQ,CAAc,KAAK,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC;IACvF,cAAc,MAAM,QAAQ,EAAE,eAAe,IAAK,EAAE,kBAA+B,CAAC;IACpF,MAAM,MAAM,QAAQ,EAAE,OAAO,IAAK,EAAE,UAAuB,CAAC;GAC9D;GACA,IAAI,OAAO,EAAE,gBAAgB,UAAU,KAAK,WAAW,EAAE;GACzD,IAAI,OAAO,EAAE,eAAe,UAAU,KAAK,UAAU,EAAE;GACvD,IAAI,EAAE,SAAS,KAAK,OAAO,EAAE;GAC7B,OAAO;EACT,CAAC;CACH;AACF;;;ACjKA,SAAS,SAAS,MAAsB;CACtC,OAAO,KAAK,QAAQ,OAAO,GAAG;AAChC;AAEA,SAAS,oBAAoB,MAAkC;CAC7D,IAAI,CAAC,KAAK,QAAQ,OAAO,KAAK,SAAS,UAAU,OAAO;CACxD,MAAM,OAAO,KAAK;CAClB,IAAI,CAAC,MAAM,QAAQ,KAAK,WAAW,KAAK,KAAK,WAAW,CAAC,WAAW,GAAG,OAAO;CAC9E,OAAO;EAAE,GAAG;EAAM,MAAM;GAAE,GAAG;GAAM,UAAU,CAAC;EAAE;CAAE;AACpD;AAEA,eAAsB,cAAc,OAAsB,QAA+B;CACvF,MAAM,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;CAEvC,MAAM,cAAc,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;CAEhF,MAAM,WAA0B;EAC9B,eAAe;EACf,MAAM,MAAM;EACZ,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;EACrD,OAAO,YAAY,IAAI,mBAAmB;CAC5C;CACA,MAAM,UAAU,KAAK,QAAQ,eAAe,GAAG,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,KAAK,MAAM;CAE/F,KAAK,MAAM,QAAQ,aAAa;EAC9B,MAAM,OAAO,SAAS,KAAK,IAAI;EAC/B,MAAM,UAAU,KAAK,QAAQ,GAAG,KAAK,MAAM,GAAG,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,KAAK,MAAM;EAC1F,MAAM,UACJ,KAAK,QAAQ,GAAG,KAAK,GAAG,KAAK,QAAQ,MAAM,GAC3C,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,KACjC,MACF;CACF;AACF;;;;;;;;;;;;;;;ACrBA,MAAM,qBAAqB;AAC3B,MAAM,yBAAyB;AAoC/B,MAAM,WAAqD;CACzD,WAAW;CACX,QAAQ;CACR,OAAO;CACP,SAAS;CACT,OAAO;CACP,OAAO;CACP,OAAO;AACT;;AAGA,SAAgB,WAAW,MAAsB;CAC/C,OAAO,KAAK,QAAQ,OAAO,GAAG;AAChC;AAEA,SAAS,SAAS,SAAiB,MAAsB;CAEvD,OAAO,GAAG,KAAK,GADF,QAAQ,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;AAE3C;AAEA,SAAgB,aAAa,MAAoB,OAAwB,CAAC,GAAuB;CAC/F,MAAM,OAAO,WAAW,KAAK,IAAI;CACjC,MAAM,OAAO,SAAS,KAAK,SAAS;CASpC,MAAM,MAA0B;EAC9B,SAAS;EACT,MAAM;EACN;EACA,OAXkC,KAAK,MAAM,KAAK,MAAM;GACxD,MAAM,OAA2B;IAAE,MAAM,SAAS,EAAE,KAAK,IAAI;IAAG;GAAK;GACrE,MAAM,UAAU,KAAK,iBAAiB,EAAE,GAAG;GAC3C,IAAI,YAAY,KAAA,GAAW,KAAK,UAAU;GAC1C,OAAO;EACT,CAMM;CACN;CACA,IAAI,KAAK,aAAa,IAAI,cAAc,KAAK;CAC7C,IAAI,KAAK,aAAa,SAAS,GAAG,IAAI,eAAe,CAAC,GAAG,KAAK,YAAY;CAC1E,IAAI,KAAK,wBAAwB,KAAK,qBAAqB,SAAS,GAClE,IAAI,uBAAuB,CAAC,GAAG,KAAK,oBAAoB;CAE1D,IAAI,KAAK,UAAU,IAAI,aAAa,CAAC,KAAK,QAAQ;CAClD,IAAI,OAAO,EAAE,SAAS;EAAE,MAAM,KAAK;EAAM,SAAS,KAAK;CAAQ,EAAE;CACjE,OAAO;AACT;;;;;AAMA,eAAsB,oBACpB,OACA,QACA,OAAwB,CAAC,GACV;CACf,MAAM,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;CAGvC,MAAM,QADS,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CACvD,CAAC,CAAC,KAAK,OAAO,aAAa,IAAI,IAAI,CAAC;CAEvD,KAAK,MAAM,QAAQ,OACjB,MAAM,UAAU,KAAK,QAAQ,GAAG,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,KAAK,MAAM;CAGjG,MAAM,aAAa,MAAM,KAAK,QAAQ;EACpC,GAAG;EACH,OAAO,GAAG,MAAM,KAAK,OAAO;GAC1B,MAAM,EAAE;GACR,MAAM,EAAE;GACR,GAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,IAAI,CAAC;EACzC,EAAE;CACJ,EAAE;CACF,MAAM,WAAW;EACf,SAAS;EACT,MAAM,MAAM;EACZ,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;EACrD,OAAO;CACT;CACA,MAAM,UAAU,KAAK,QAAQ,eAAe,GAAG,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,KAAK,MAAM;AACjG;;;ACnIA,SAAS,kBAAkB,GAAqB;CAC9C,OAAO,EACJ,QAAQ,YAAY,EAAE,CAAC,CACvB,MAAM,GAAG,CAAC,CACV,IAAI,MAAM;AACf;AAEA,SAAS,gBAAgB,GAAW,GAAmB;CACrD,MAAM,KAAK,kBAAkB,CAAC;CAC9B,MAAM,KAAK,kBAAkB,CAAC;CAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK;EACvD,MAAM,KAAK,GAAG,MAAM;EACpB,MAAM,KAAK,GAAG,MAAM;EACpB,IAAI,OAAO,IAAI,OAAO,KAAK;CAC7B;CACA,OAAO;AACT;AAEA,SAAS,aAAa,SAAiB,OAAwB;CAC7D,IAAI;EACF,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,QAAQ,WAAW,IAAI,GACzB,OAAO,gBAAgB,SAAS,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK;EAE9D,IAAI,QAAQ,WAAW,GAAG,GACxB,OAAO,gBAAgB,SAAS,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI;EAE7D,IAAI,QAAQ,WAAW,IAAI,GACzB,OAAO,gBAAgB,SAAS,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK;EAE9D,IAAI,QAAQ,WAAW,GAAG,GACxB,OAAO,gBAAgB,SAAS,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI;EAE7D,IAAI,QAAQ,WAAW,GAAG,GAAG;GAC3B,MAAM,OAAO,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK;GAEnC,MAAM,QADY,kBAAkB,IACd,CAAC,CAAC,MAAM;GAG9B,QAFe,kBAAkB,OACb,CAAC,CAAC,MAAM,OACV,SAAS,gBAAgB,SAAS,IAAI,KAAK;EAC/D;EACA,IAAI,QAAQ,WAAW,GAAG,GAAG;GAC3B,MAAM,OAAO,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK;GACnC,MAAM,YAAY,kBAAkB,IAAI;GACxC,MAAM,QAAQ,UAAU,MAAM;GAC9B,MAAM,QAAQ,UAAU,MAAM;GAC9B,MAAM,SAAS,kBAAkB,OAAO;GACxC,MAAM,SAAS,OAAO,MAAM;GAC5B,MAAM,SAAS,OAAO,MAAM;GAC5B,OAAO,WAAW,SAAS,WAAW,SAAS,gBAAgB,SAAS,IAAI,KAAK;EACnF;EACA,OAAO,YAAY;CACrB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,gBACd,MACA,eACoB;CACpB,IAAI,CAAC,KAAK,YAAY,OAAO,CAAC;CAC9B,MAAM,UAA8B,CAAC;CACrC,KAAK,MAAM,OAAO,KAAK,YACrB,IAAI;EACF,IAAI,aAAa,eAAe,IAAI,gBAAgB,GAClD,QAAQ,KAAK,GAAG;CAEpB,QAAQ,CAER;CAEF,OAAO;AACT;;;ACtDA,SAAS,YAAY,GAAW,GAAmB;CACjD,MAAM,IAAI,EAAE;CACZ,MAAM,IAAI,EAAE;CACZ,MAAM,KAAiB,MAAM,KAAK,EAAE,QAAQ,IAAI,EAAE,IAAI,GAAG,MACvD,MAAM,KAAK,EAAE,QAAQ,IAAI,EAAE,IAAI,GAAG,MAAO,MAAM,IAAI,IAAI,MAAM,IAAI,IAAI,CAAE,CACzE;CACA,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KACtB,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KACtB,GAAG,EAAE,CAAE,KACL,EAAE,IAAI,OAAO,EAAE,IAAI,KACf,GAAG,IAAI,EAAE,CAAE,IAAI,KACf,IAAI,KAAK,IAAI,GAAG,IAAI,EAAE,CAAE,IAAK,GAAG,EAAE,CAAE,IAAI,IAAK,GAAG,IAAI,EAAE,CAAE,IAAI,EAAG;CAGzE,OAAO,GAAG,EAAE,CAAE;AAChB;AAEA,SAAgB,kBAAkB,KAAyC;CACzE,IAAI,CAAC,OAAO,OAAO,QAAQ,UACzB,OAAO;EAAE,IAAI;EAAO,QAAQ,CAAC,6BAA6B;EAAG,UAAU,CAAC;CAAE;CAE5E,MAAM,MAAM;CACZ,MAAM,SAAmB,CAAC;CAC1B,MAAM,WAAqB,CAAC;CAE5B,IAAI,IAAI,qBAAqB,GAC3B,OAAO,KAAK,gCAAgC,KAAK,UAAU,IAAI,gBAAgB,GAAG;CAEpF,IAAI,CAAC,MAAM,QAAQ,IAAI,aAAa,GAAG;EACrC,OAAO,KAAK,6BAA6B;EACzC,OAAO;GAAE,IAAI;GAAO;GAAQ;EAAS;CACvC;CAEA,MAAM,aAAuB,CAAC;CAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,aAAa,CAAC,QAAQ,KAAK;EACjD,MAAM,QAAQ,IAAI,aAAa,CAAC;EAChC,MAAM,KAAK,MAAM;EACjB,IAAI,OAAO,OAAO,YAAY,CAAC,IAAI,OAAO,KAAK,cAAc,EAAE,wBAAwB;OAClF;GACH,IAAI,WAAW,SAAS,EAAE,GAAG,OAAO,KAAK,wBAAwB,GAAG,aAAa,GAAG;GACpF,WAAW,KAAK,EAAE;EACpB;EACA,IAAI,OAAO,MAAM,YAAY,UAAU,OAAO,KAAK,cAAc,EAAE,mBAAmB;EACtF,IAAI,OAAO,MAAM,mBAAmB,UAClC,OAAO,KAAK,cAAc,EAAE,0BAA0B;EACxD,IAAI,OAAO,MAAM,gBAAgB,UAAU,OAAO,KAAK,cAAc,EAAE,uBAAuB;EAC9F,IAAI,OAAO,MAAM,mBAAmB,UAClC,OAAO,KAAK,cAAc,EAAE,0BAA0B;OACnD,IAAI,CAAE,MAAM,cAAc,CAAY,SAAS,QAAQ,GAC1D,OAAO,KAAK,cAAc,EAAE,8CAA8C;EAC5E,IAAI,CAAC,MAAM,QAAQ,MAAM,OAAO,GAAG,OAAO,KAAK,cAAc,EAAE,wBAAwB;EACvF,IAAI,OAAO,MAAM,gBAAgB,WAC/B,OAAO,KAAK,cAAc,EAAE,6BAA6B;CAC7D;CAEA,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KACrC,KAAK,IAAI,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;EAC9C,MAAM,IAAI,WAAW;EACrB,MAAM,IAAI,WAAW;EACrB,IAAI,YAAY,GAAG,CAAC,KAAK,GACvB,SAAS,KACP,eAAe,EAAE,SAAS,EAAE,sDAC9B;CAEJ;CAGF,OAAO;EAAE,IAAI,OAAO,WAAW;EAAG;EAAQ;CAAS;AACrD"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/validate.ts","../src/template.ts","../src/build.ts","../src/shadcn.ts","../src/advisories.ts","../src/directory.ts","../src/marketplace.ts"],"sourcesContent":["import type { RegistryIndex, RegistryItem } from './types.ts'\n\nexport interface ValidationResult {\n ok: boolean\n errors: string[]\n warnings: string[]\n}\n\nconst VALID_TYPES = new Set([\n 'component',\n 'layout',\n 'block',\n 'chart',\n 'section',\n 'theme',\n 'style',\n 'template',\n])\nconst VALID_SEVERITIES = new Set(['low', 'moderate', 'high', 'critical'])\n\nfunction ok(warnings: string[] = []): ValidationResult {\n return { ok: true, errors: [], warnings }\n}\n\nfunction fail(errors: string[], warnings: string[] = []): ValidationResult {\n return { ok: false, errors, warnings }\n}\n\nexport function validateItem(raw: unknown): ValidationResult {\n if (!raw || typeof raw !== 'object') {\n return fail(['Item must be an object'])\n }\n const obj = raw as Record<string, unknown>\n const errors: string[] = []\n const warnings: string[] = []\n\n if (obj['schemaVersion'] !== 2) {\n errors.push(`schemaVersion must be 2, got ${JSON.stringify(obj['schemaVersion'])}`)\n }\n if (typeof obj['name'] !== 'string' || !obj['name']) {\n errors.push('name is required and must be a non-empty string')\n }\n if (!VALID_TYPES.has(obj['type'] as string)) {\n errors.push(\n `type must be one of ${[...VALID_TYPES].join('|')}, got ${JSON.stringify(obj['type'])}`,\n )\n }\n if (typeof obj['description'] !== 'string') {\n errors.push('description is required and must be a string')\n }\n if (typeof obj['version'] !== 'string' || !obj['version']) {\n errors.push('version is required and must be a non-empty string')\n }\n if (!Array.isArray(obj['files'])) {\n errors.push('files is required and must be an array')\n }\n if (!Array.isArray(obj['dependencies'])) {\n errors.push('dependencies is required and must be an array')\n }\n if (!Array.isArray(obj['tags'])) {\n errors.push('tags is required and must be an array')\n }\n\n if (obj['advisories'] !== undefined) {\n if (!Array.isArray(obj['advisories'])) {\n errors.push('advisories must be an array')\n } else {\n const ids = new Set<string>()\n for (const adv of obj['advisories'] as unknown[]) {\n if (!adv || typeof adv !== 'object') {\n errors.push('each advisory must be an object')\n continue\n }\n const a = adv as Record<string, unknown>\n if (typeof a['id'] !== 'string') errors.push('advisory.id must be a string')\n else if (ids.has(a['id'] as string)) errors.push(`advisory id \"${a['id']}\" is not unique`)\n else ids.add(a['id'] as string)\n if (!VALID_SEVERITIES.has(a['severity'] as string))\n errors.push(`advisory.severity must be one of ${[...VALID_SEVERITIES].join('|')}`)\n if (typeof a['affectedVersions'] !== 'string')\n errors.push('advisory.affectedVersions must be a string')\n if (typeof a['summary'] !== 'string') errors.push('advisory.summary must be a string')\n }\n }\n }\n\n const knownKeys = new Set([\n 'schemaVersion',\n 'name',\n 'type',\n 'description',\n 'category',\n 'version',\n 'changelog',\n 'author',\n 'license',\n 'homepage',\n 'files',\n 'install',\n 'dependencies',\n 'registryDependencies',\n 'tags',\n 'meta',\n 'advisories',\n ])\n for (const key of Object.keys(obj)) {\n if (!knownKeys.has(key)) {\n warnings.push(`Unknown field \"${key}\" — ignored (forward compat)`)\n }\n }\n\n return errors.length > 0 ? fail(errors, warnings) : ok(warnings)\n}\n\nexport function validateIndex(raw: unknown): ValidationResult {\n if (!raw || typeof raw !== 'object') {\n return fail(['Index must be an object'])\n }\n const obj = raw as Record<string, unknown>\n const errors: string[] = []\n const warnings: string[] = []\n\n if (obj['schemaVersion'] !== 2) {\n errors.push(`schemaVersion must be 2, got ${JSON.stringify(obj['schemaVersion'])}`)\n }\n if (typeof obj['name'] !== 'string' || !obj['name']) {\n errors.push('name is required and must be a non-empty string')\n }\n if (!Array.isArray(obj['items'])) {\n errors.push('items is required and must be an array')\n } else {\n for (let i = 0; i < obj['items'].length; i++) {\n const itemResult = validateItem(obj['items'][i])\n for (const e of itemResult.errors) errors.push(`items[${i}]: ${e}`)\n for (const w of itemResult.warnings) warnings.push(`items[${i}]: ${w}`)\n }\n }\n\n const knownKeys = new Set(['schemaVersion', 'name', 'homepage', 'items'])\n for (const key of Object.keys(obj)) {\n if (!knownKeys.has(key)) {\n warnings.push(`Unknown field \"${key}\" — ignored (forward compat)`)\n }\n }\n\n return errors.length > 0 ? fail(errors, warnings) : ok(warnings)\n}\n\nexport function parseLegacyRegistry(raw: unknown): RegistryIndex {\n if (!raw || typeof raw !== 'object') throw new Error('Legacy registry must be an object')\n const obj = raw as Record<string, unknown>\n const topVersion = typeof obj['version'] === 'string' ? obj['version'] : '0.0.0'\n const components = Array.isArray(obj['components']) ? obj['components'] : []\n\n return {\n schemaVersion: 2,\n name: 'cascivo',\n items: (components as Record<string, unknown>[]).map((c) => {\n const item: RegistryItem = {\n schemaVersion: 2,\n name: typeof c['name'] === 'string' ? c['name'] : '',\n type: (c['type'] as RegistryItem['type']) ?? 'component',\n description: typeof c['description'] === 'string' ? c['description'] : '',\n version: topVersion,\n files: Array.isArray(c['files']) ? (c['files'] as string[]).map((url) => ({ url })) : [],\n dependencies: Array.isArray(c['dependencies']) ? (c['dependencies'] as string[]) : [],\n tags: Array.isArray(c['tags']) ? (c['tags'] as string[]) : [],\n }\n if (typeof c['category'] === 'string') item.category = c['category']\n if (typeof c['install'] === 'string') item.install = c['install']\n if (c['meta']) item.meta = c['meta'] as RegistryItem['meta']\n return item\n }),\n }\n}\n","import type { RegistryItem, TemplateMeta, TemplateFileRole } from './types.ts'\n\nconst VALID_FRAMEWORKS = new Set(['react-vite', 'react-next'])\nconst VALID_FILE_ROLES = new Set<TemplateFileRole>(['page', 'fixture', 'asset', 'config'])\n\n/** True when an item is a template. */\nexport function isTemplateItem(item: RegistryItem): boolean {\n return item.type === 'template'\n}\n\n/**\n * Narrow an item's `meta` to a {@link TemplateMeta}, throwing on shape mismatch.\n * Use after {@link validateTemplate} has passed.\n */\nexport function asTemplateMeta(meta: unknown): TemplateMeta {\n const errors = validateTemplateMeta(meta)\n if (errors.length > 0) {\n throw new Error(`Invalid TemplateMeta: ${errors.join('; ')}`)\n }\n return meta as TemplateMeta\n}\n\nfunction validateTemplateMeta(meta: unknown): string[] {\n const errors: string[] = []\n if (!meta || typeof meta !== 'object') {\n return ['meta must be a TemplateMeta object']\n }\n const m = meta as Record<string, unknown>\n\n if (typeof m['intent'] !== 'string' || !m['intent']) {\n errors.push('meta.intent is required and must be a non-empty string')\n }\n if (!VALID_FRAMEWORKS.has(m['framework'] as string)) {\n errors.push(`meta.framework must be one of ${[...VALID_FRAMEWORKS].join('|')}`)\n }\n if (typeof m['category'] !== 'string' || !m['category']) {\n errors.push('meta.category is required and must be a non-empty string')\n }\n\n if (!Array.isArray(m['screenshots']) || m['screenshots'].length === 0) {\n errors.push('meta.screenshots is required and must have at least one entry')\n } else {\n for (let i = 0; i < m['screenshots'].length; i++) {\n const shot = m['screenshots'][i] as Record<string, unknown>\n if (!shot || typeof shot !== 'object') {\n errors.push(`meta.screenshots[${i}] must be an object`)\n continue\n }\n if (typeof shot['light'] !== 'string' || !shot['light'])\n errors.push(`meta.screenshots[${i}].light is required`)\n if (typeof shot['alt'] !== 'string' || !shot['alt'])\n errors.push(`meta.screenshots[${i}].alt is required`)\n }\n }\n\n if (m['demoUrl'] !== undefined && typeof m['demoUrl'] !== 'string') {\n errors.push('meta.demoUrl must be a string when present')\n }\n\n if (!m['fileRoles'] || typeof m['fileRoles'] !== 'object') {\n errors.push('meta.fileRoles is required and must be an object')\n } else {\n for (const [target, role] of Object.entries(m['fileRoles'] as Record<string, unknown>)) {\n if (!VALID_FILE_ROLES.has(role as TemplateFileRole)) {\n errors.push(`meta.fileRoles[\"${target}\"] must be one of ${[...VALID_FILE_ROLES].join('|')}`)\n }\n }\n }\n\n if (m['pages'] !== undefined) {\n if (!Array.isArray(m['pages'])) {\n errors.push('meta.pages must be an array when present')\n } else {\n for (let i = 0; i < m['pages'].length; i++) {\n const page = m['pages'][i] as Record<string, unknown>\n if (!page || typeof page !== 'object') {\n errors.push(`meta.pages[${i}] must be an object`)\n continue\n }\n if (typeof page['name'] !== 'string') errors.push(`meta.pages[${i}].name is required`)\n if (typeof page['target'] !== 'string') errors.push(`meta.pages[${i}].target is required`)\n }\n }\n }\n\n return errors\n}\n\n/**\n * Validate a template item beyond the base item schema. Returns a list of\n * errors (empty when valid). Requires:\n * - `type === 'template'`\n * - a valid {@link TemplateMeta}\n * - at least one component (`registryDependencies`) or one `page` file\n * - a non-empty `license`\n * - every `files[].target` present in `meta.fileRoles`\n */\nexport function validateTemplate(item: RegistryItem): string[] {\n const errors: string[] = []\n\n if (item.type !== 'template') {\n errors.push(`type must be 'template', got ${JSON.stringify(item.type)}`)\n return errors\n }\n\n const metaErrors = validateTemplateMeta(item.meta)\n errors.push(...metaErrors)\n\n const meta = (item.meta ?? {}) as Partial<TemplateMeta>\n const fileRoles = (meta.fileRoles ?? {}) as Record<string, TemplateFileRole>\n\n const hasComponents = Array.isArray(item.registryDependencies)\n ? item.registryDependencies.length > 0\n : false\n const hasPageFile = Object.values(fileRoles).includes('page')\n if (!hasComponents && !hasPageFile) {\n errors.push(\n 'a template must list at least one component in registryDependencies or one page file',\n )\n }\n\n if (typeof item.license !== 'string' || !item.license) {\n errors.push('license is required for a template')\n }\n\n for (const file of item.files ?? []) {\n if (!file.target) {\n errors.push(`template file ${JSON.stringify(file.url)} must declare a target`)\n } else if (metaErrors.length === 0 && !(file.target in fileRoles)) {\n errors.push(`file target \"${file.target}\" is missing from meta.fileRoles`)\n }\n }\n\n return errors\n}\n","import { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { RegistryIndex, RegistryItem } from './types.ts'\n\nfunction flatName(name: string): string {\n return name.replace(/\\//g, '-')\n}\n\nfunction itemWithoutExamples(item: RegistryItem): RegistryItem {\n if (!item.meta || typeof item.meta !== 'object') return item\n const meta = item.meta as Record<string, unknown>\n if (!Array.isArray(meta['examples']) || meta['examples'].length === 0) return item\n return { ...item, meta: { ...meta, examples: [] } }\n}\n\nexport async function buildRegistry(index: RegistryIndex, outDir: string): Promise<void> {\n await mkdir(outDir, { recursive: true })\n\n const sortedItems = [...index.items].sort((a, b) => a.name.localeCompare(b.name))\n\n const indexOut: RegistryIndex = {\n schemaVersion: 2,\n name: index.name,\n ...(index.homepage ? { homepage: index.homepage } : {}),\n items: sortedItems.map(itemWithoutExamples),\n }\n await writeFile(join(outDir, 'registry.json'), `${JSON.stringify(indexOut, null, 2)}\\n`, 'utf8')\n\n for (const item of sortedItems) {\n const flat = flatName(item.name)\n await writeFile(join(outDir, `${flat}.json`), `${JSON.stringify(item, null, 2)}\\n`, 'utf8')\n await writeFile(\n join(outDir, `${flat}@${item.version}.json`),\n `${JSON.stringify(item, null, 2)}\\n`,\n 'utf8',\n )\n }\n}\n","/**\n * shadcn-registry-compatible projection of the cascivo registry.\n *\n * This is purely ADDITIVE interop: it maps cascivo's own `RegistryItem`s into\n * the shadcn `registry:*` item schema so the shadcn CLI can install a cascivo\n * component (`npx shadcn add <host>/r/shadcn/<name>.json`). cascivo's own\n * `registry.json` schema is untouched.\n *\n * cascivo is CSS-native: components ship `.tsx` + `.module.css` and rely on a\n * `@cascivo/themes` import plus the `@cascivo/core` peer — there is no Tailwind\n * config, so no `cssVars`/`tailwind` block is emitted.\n */\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { RegistryIndex, RegistryItem, RegistryItemType } from './types.ts'\n\nconst SHADCN_ITEM_SCHEMA = 'https://ui.shadcn.com/schema/registry-item.json'\nconst SHADCN_REGISTRY_SCHEMA = 'https://ui.shadcn.com/schema/registry.json'\n\nexport type ShadcnItemType =\n | 'registry:component'\n | 'registry:block'\n | 'registry:lib'\n | 'registry:ui'\n | 'registry:hook'\n | 'registry:theme'\n | 'registry:style'\n | 'registry:file'\n\nexport interface ShadcnRegistryFile {\n path: string\n type: ShadcnItemType\n content?: string\n target?: string\n}\n\nexport interface ShadcnRegistryItem {\n $schema: string\n name: string\n type: ShadcnItemType\n description?: string\n dependencies?: string[]\n registryDependencies?: string[]\n files: ShadcnRegistryFile[]\n categories?: string[]\n meta?: Record<string, unknown>\n}\n\nexport interface ToShadcnOptions {\n /** Resolve a cascivo file URL to its source text, to inline as `content`. */\n resolveContent?: (fileUrl: string) => string | undefined\n}\n\nconst TYPE_MAP: Record<RegistryItemType, ShadcnItemType> = {\n component: 'registry:component',\n layout: 'registry:block',\n block: 'registry:block',\n section: 'registry:block',\n chart: 'registry:component',\n theme: 'registry:theme',\n style: 'registry:style',\n template: 'registry:block',\n}\n\n/** Flatten a possibly-prefixed cascivo name (\"block/x\") into a shadcn slug. */\nexport function shadcnName(name: string): string {\n return name.replace(/\\//g, '-')\n}\n\nfunction filePath(fileUrl: string, slug: string): string {\n const base = fileUrl.split('/').pop() ?? fileUrl\n return `${slug}/${base}`\n}\n\nexport function toShadcnItem(item: RegistryItem, opts: ToShadcnOptions = {}): ShadcnRegistryItem {\n const slug = shadcnName(item.name)\n const type = TYPE_MAP[item.type] ?? 'registry:component'\n\n const files: ShadcnRegistryFile[] = item.files.map((f) => {\n const file: ShadcnRegistryFile = { path: filePath(f.url, slug), type }\n const content = opts.resolveContent?.(f.url)\n if (content !== undefined) file.content = content\n return file\n })\n\n const out: ShadcnRegistryItem = {\n $schema: SHADCN_ITEM_SCHEMA,\n name: slug,\n type,\n files,\n }\n if (item.description) out.description = item.description\n if (item.dependencies.length > 0) out.dependencies = [...item.dependencies]\n if (item.registryDependencies && item.registryDependencies.length > 0) {\n out.registryDependencies = [...item.registryDependencies]\n }\n if (item.category) out.categories = [item.category]\n out.meta = { cascivo: { type: item.type, version: item.version } }\n return out\n}\n\n/**\n * Write one shadcn `r/<name>.json` per item (with inlined file content when a\n * resolver is supplied) plus a lean `registry.json` index (content omitted).\n */\nexport async function writeShadcnRegistry(\n index: RegistryIndex,\n outDir: string,\n opts: ToShadcnOptions = {},\n): Promise<void> {\n await mkdir(outDir, { recursive: true })\n\n const sorted = [...index.items].sort((a, b) => a.name.localeCompare(b.name))\n const items = sorted.map((it) => toShadcnItem(it, opts))\n\n for (const item of items) {\n await writeFile(join(outDir, `${item.name}.json`), `${JSON.stringify(item, null, 2)}\\n`, 'utf8')\n }\n\n const indexItems = items.map((it) => ({\n ...it,\n files: it.files.map((f) => ({\n path: f.path,\n type: f.type,\n ...(f.target ? { target: f.target } : {}),\n })),\n }))\n const registry = {\n $schema: SHADCN_REGISTRY_SCHEMA,\n name: index.name,\n ...(index.homepage ? { homepage: index.homepage } : {}),\n items: indexItems,\n }\n await writeFile(join(outDir, 'registry.json'), `${JSON.stringify(registry, null, 2)}\\n`, 'utf8')\n}\n","import type { RegistryAdvisory, RegistryItem } from './types.ts'\n\nfunction parseVersionParts(v: string): number[] {\n return v\n .replace(/^[^0-9]*/, '')\n .split('.')\n .map(Number)\n}\n\nfunction compareVersions(a: string, b: string): number {\n const pa = parseVersionParts(a)\n const pb = parseVersionParts(b)\n for (let i = 0; i < Math.max(pa.length, pb.length); i++) {\n const na = pa[i] ?? 0\n const nb = pb[i] ?? 0\n if (na !== nb) return na - nb\n }\n return 0\n}\n\nfunction matchesRange(version: string, range: string): boolean {\n try {\n const trimmed = range.trim()\n if (trimmed.startsWith('>=')) {\n return compareVersions(version, trimmed.slice(2).trim()) >= 0\n }\n if (trimmed.startsWith('>')) {\n return compareVersions(version, trimmed.slice(1).trim()) > 0\n }\n if (trimmed.startsWith('<=')) {\n return compareVersions(version, trimmed.slice(2).trim()) <= 0\n }\n if (trimmed.startsWith('<')) {\n return compareVersions(version, trimmed.slice(1).trim()) < 0\n }\n if (trimmed.startsWith('^')) {\n const base = trimmed.slice(1).trim()\n const baseParts = parseVersionParts(base)\n const major = baseParts[0] ?? 0\n const vParts = parseVersionParts(version)\n const vMajor = vParts[0] ?? 0\n return vMajor === major && compareVersions(version, base) >= 0\n }\n if (trimmed.startsWith('~')) {\n const base = trimmed.slice(1).trim()\n const baseParts = parseVersionParts(base)\n const major = baseParts[0] ?? 0\n const minor = baseParts[1] ?? 0\n const vParts = parseVersionParts(version)\n const vMajor = vParts[0] ?? 0\n const vMinor = vParts[1] ?? 0\n return vMajor === major && vMinor === minor && compareVersions(version, base) >= 0\n }\n return version === trimmed\n } catch {\n return false\n }\n}\n\nexport function matchAdvisories(\n item: Pick<RegistryItem, 'advisories'>,\n lockedVersion: string,\n): RegistryAdvisory[] {\n if (!item.advisories) return []\n const matched: RegistryAdvisory[] = []\n for (const adv of item.advisories) {\n try {\n if (matchesRange(lockedVersion, adv.affectedVersions)) {\n matched.push(adv)\n }\n } catch {\n // malformed range — skip silently, never crash an audit\n }\n }\n return matched\n}\n","/** What kinds of items a directory-listed registry publishes. */\nexport type DirectoryProvides = 'component' | 'template' | 'theme'\n\nexport interface DirectoryEntry {\n namespace: string\n name: string\n description: string\n homepage: string\n registryUrl: string\n tags: string[]\n verified: boolean\n /** Item kinds this registry provides. Absent ⇒ component-only. */\n provides?: DirectoryProvides[]\n}\n\nconst VALID_PROVIDES = new Set<DirectoryProvides>(['component', 'template', 'theme'])\n\nexport interface RegistryDirectory {\n schemaVersion: 1\n registries: DirectoryEntry[]\n}\n\nexport interface DirectoryValidationResult {\n ok: boolean\n errors: string[]\n warnings: string[]\n}\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length\n const n = b.length\n const dp: number[][] = Array.from({ length: m + 1 }, (_, i) =>\n Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)),\n )\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i]![j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1]![j - 1]!\n : 1 + Math.min(dp[i - 1]![j]!, dp[i]![j - 1]!, dp[i - 1]![j - 1]!)\n }\n }\n return dp[m]![n]!\n}\n\nexport function validateDirectory(raw: unknown): DirectoryValidationResult {\n if (!raw || typeof raw !== 'object') {\n return { ok: false, errors: ['Directory must be an object'], warnings: [] }\n }\n const obj = raw as Record<string, unknown>\n const errors: string[] = []\n const warnings: string[] = []\n\n if (obj['schemaVersion'] !== 1) {\n errors.push(`schemaVersion must be 1, got ${JSON.stringify(obj['schemaVersion'])}`)\n }\n if (!Array.isArray(obj['registries'])) {\n errors.push('registries must be an array')\n return { ok: false, errors, warnings }\n }\n\n const namespaces: string[] = []\n for (let i = 0; i < obj['registries'].length; i++) {\n const entry = obj['registries'][i] as Record<string, unknown>\n const ns = entry['namespace']\n if (typeof ns !== 'string' || !ns) errors.push(`registries[${i}].namespace is required`)\n else {\n if (namespaces.includes(ns)) errors.push(`Duplicate namespace \"${ns}\" at index ${i}`)\n namespaces.push(ns)\n }\n if (typeof entry['name'] !== 'string') errors.push(`registries[${i}].name is required`)\n if (typeof entry['description'] !== 'string')\n errors.push(`registries[${i}].description is required`)\n if (typeof entry['homepage'] !== 'string') errors.push(`registries[${i}].homepage is required`)\n if (typeof entry['registryUrl'] !== 'string')\n errors.push(`registries[${i}].registryUrl is required`)\n else if (!(entry['registryUrl'] as string).includes('{name}'))\n errors.push(`registries[${i}].registryUrl must contain {name} placeholder`)\n if (!Array.isArray(entry['tags'])) errors.push(`registries[${i}].tags must be an array`)\n if (typeof entry['verified'] !== 'boolean')\n errors.push(`registries[${i}].verified must be a boolean`)\n if (entry['provides'] !== undefined) {\n if (!Array.isArray(entry['provides'])) {\n errors.push(`registries[${i}].provides must be an array when present`)\n } else {\n for (const p of entry['provides'] as unknown[]) {\n if (!VALID_PROVIDES.has(p as DirectoryProvides))\n errors.push(\n `registries[${i}].provides has invalid value ${JSON.stringify(p)} (allowed: ${[...VALID_PROVIDES].join('|')})`,\n )\n }\n }\n }\n }\n\n for (let i = 0; i < namespaces.length; i++) {\n for (let j = i + 1; j < namespaces.length; j++) {\n const a = namespaces[i]!\n const b = namespaces[j]!\n if (levenshtein(a, b) <= 2) {\n warnings.push(\n `Namespaces \"${a}\" and \"${b}\" are suspiciously similar (Levenshtein distance ≤ 2)`,\n )\n }\n }\n }\n\n return { ok: errors.length === 0, errors, warnings }\n}\n","import type { RegistryItem, TemplateScreenshot } from './types.ts'\nimport { isTemplateItem, asTemplateMeta } from './template.ts'\n\n/** A template projected into a flat, gallery-ready record. */\nexport interface MarketplaceTemplate {\n name: string\n namespace: string\n description: string\n category: string\n framework: string\n tags: string[]\n /** Component names the template composes (from `registryDependencies`). */\n components: string[]\n screenshots: TemplateScreenshot[]\n demoUrl?: string\n version: string\n license: string\n verified: boolean\n homepage?: string\n /** The spec a user passes to `cascivo add` (e.g. \"@acme/dashboard\"). */\n installSpec: string\n /** GitHub stars, baked at CI build time (omitted when unknown). */\n stars?: number\n}\n\n/** The static marketplace catalog written to `apps/site/public/marketplace.json`. */\nexport interface MarketplaceCatalog {\n generatedAt: string\n templates: MarketplaceTemplate[]\n facets: { categories: string[]; frameworks: string[]; tags: string[] }\n}\n\n/** One registry's resolved index + the directory metadata that referenced it. */\nexport interface MarketplaceSource {\n entry: { namespace: string; verified?: boolean; homepage?: string; stars?: number }\n items: RegistryItem[]\n}\n\n/**\n * Project a template item into a gallery record, or `null` if the item is not a\n * valid template. Pure — no I/O.\n */\nexport function projectTemplate(\n item: RegistryItem,\n source: MarketplaceSource['entry'],\n): MarketplaceTemplate | null {\n if (!isTemplateItem(item)) return null\n let meta\n try {\n meta = asTemplateMeta(item.meta)\n } catch {\n return null\n }\n const record: MarketplaceTemplate = {\n name: item.name,\n namespace: source.namespace,\n description: item.description,\n category: meta.category,\n framework: meta.framework,\n tags: item.tags ?? [],\n components: item.registryDependencies ?? [],\n screenshots: meta.screenshots,\n version: item.version,\n license: item.license ?? '',\n verified: source.verified ?? false,\n installSpec: `${source.namespace}/${item.name}`,\n }\n if (meta.demoUrl) record.demoUrl = meta.demoUrl\n const homepage = item.homepage ?? source.homepage\n if (homepage) record.homepage = homepage\n if (typeof source.stars === 'number') record.stars = source.stars\n return record\n}\n\nfunction uniqueSorted(values: string[]): string[] {\n return [...new Set(values)].sort((a, b) => a.localeCompare(b))\n}\n\n/**\n * Build the marketplace catalog from a set of resolved registry sources. Pure —\n * the caller does the fetching + star lookups and supplies the timestamp.\n * Templates are sorted verified-first, then by stars (desc), then by name.\n */\nexport function buildCatalog(\n sources: MarketplaceSource[],\n generatedAt: string,\n): MarketplaceCatalog {\n const templates: MarketplaceTemplate[] = []\n for (const src of sources) {\n for (const item of src.items) {\n const record = projectTemplate(item, src.entry)\n if (record) templates.push(record)\n }\n }\n\n templates.sort((a, b) => {\n if (a.verified !== b.verified) return a.verified ? -1 : 1\n const sa = a.stars ?? 0\n const sb = b.stars ?? 0\n if (sa !== sb) return sb - sa\n return a.name.localeCompare(b.name)\n })\n\n return {\n generatedAt,\n templates,\n facets: {\n categories: uniqueSorted(templates.map((t) => t.category)),\n frameworks: uniqueSorted(templates.map((t) => t.framework)),\n tags: uniqueSorted(templates.flatMap((t) => t.tags)),\n },\n }\n}\n"],"mappings":"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"}
|