@codemcp/agentskills-core 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +19 -0
  2. package/dist/__tests__/package-config.test.d.ts +2 -0
  3. package/dist/__tests__/package-config.test.d.ts.map +1 -0
  4. package/dist/__tests__/package-config.test.js +251 -0
  5. package/dist/__tests__/package-config.test.js.map +1 -0
  6. package/dist/__tests__/parser.test.d.ts +2 -0
  7. package/dist/__tests__/parser.test.d.ts.map +1 -0
  8. package/dist/__tests__/parser.test.js +613 -0
  9. package/dist/__tests__/parser.test.js.map +1 -0
  10. package/dist/__tests__/registry.test.d.ts +2 -0
  11. package/dist/__tests__/registry.test.d.ts.map +1 -0
  12. package/dist/__tests__/registry.test.js +415 -0
  13. package/dist/__tests__/registry.test.js.map +1 -0
  14. package/dist/__tests__/skill-installer.test.d.ts +2 -0
  15. package/dist/__tests__/skill-installer.test.d.ts.map +1 -0
  16. package/dist/__tests__/skill-installer.test.js +229 -0
  17. package/dist/__tests__/skill-installer.test.js.map +1 -0
  18. package/dist/__tests__/validator.test.d.ts +2 -0
  19. package/dist/__tests__/validator.test.d.ts.map +1 -0
  20. package/dist/__tests__/validator.test.js +284 -0
  21. package/dist/__tests__/validator.test.js.map +1 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +6 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/installer.d.ts +89 -0
  27. package/dist/installer.d.ts.map +1 -0
  28. package/dist/installer.js +469 -0
  29. package/dist/installer.js.map +1 -0
  30. package/dist/package-config.d.ts +52 -0
  31. package/dist/package-config.d.ts.map +1 -0
  32. package/dist/package-config.js +267 -0
  33. package/dist/package-config.js.map +1 -0
  34. package/dist/parser.d.ts +59 -0
  35. package/dist/parser.d.ts.map +1 -0
  36. package/dist/parser.js +154 -0
  37. package/dist/parser.js.map +1 -0
  38. package/dist/registry.d.ts +72 -0
  39. package/dist/registry.d.ts.map +1 -0
  40. package/dist/registry.js +180 -0
  41. package/dist/registry.js.map +1 -0
  42. package/dist/types.d.ts +202 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +8 -0
  45. package/dist/types.js.map +1 -0
  46. package/dist/validator.d.ts +27 -0
  47. package/dist/validator.d.ts.map +1 -0
  48. package/dist/validator.js +165 -0
  49. package/dist/validator.js.map +1 -0
  50. package/package.json +56 -0
@@ -0,0 +1,267 @@
1
+ import { promises as fs } from "fs";
2
+ import { join } from "path";
3
+ /**
4
+ * PackageConfigManager - Manages package.json configuration for agent skills
5
+ *
6
+ * Responsibilities:
7
+ * - Read package.json from a directory
8
+ * - Parse `agentskills` field (skill dependencies)
9
+ * - Parse `agentskillsConfig` field (configuration settings)
10
+ * - Provide defaults when package.json doesn't exist
11
+ * - Provide defaults when fields are missing
12
+ * - Validate configuration structure
13
+ * - Save/update skills in package.json
14
+ */
15
+ export class PackageConfigManager {
16
+ projectRoot;
17
+ packageJsonPath;
18
+ constructor(projectRoot) {
19
+ if (!projectRoot) {
20
+ throw new Error("Project root directory is required");
21
+ }
22
+ this.projectRoot = projectRoot;
23
+ this.packageJsonPath = join(projectRoot, "package.json");
24
+ }
25
+ /**
26
+ * Get default configuration with empty skills and standard defaults
27
+ */
28
+ getDefaultConfig() {
29
+ return {
30
+ skills: {},
31
+ config: {
32
+ skillsDirectory: ".agentskills/skills",
33
+ autoDiscover: [".claude/skills"],
34
+ maxSkillSize: 5000,
35
+ logLevel: "info"
36
+ },
37
+ source: {
38
+ type: "defaults"
39
+ }
40
+ };
41
+ }
42
+ /**
43
+ * Load configuration from package.json
44
+ * Returns defaults if package.json doesn't exist
45
+ */
46
+ async loadConfig() {
47
+ try {
48
+ const content = await fs.readFile(this.packageJsonPath, "utf-8");
49
+ let packageJson;
50
+ try {
51
+ packageJson = JSON.parse(content);
52
+ }
53
+ catch (error) {
54
+ throw new Error(`Failed to parse package.json: ${error instanceof Error ? error.message : String(error)}`);
55
+ }
56
+ // Validate and extract agentskills
57
+ const skills = this.validateAndExtractSkills(packageJson);
58
+ // Validate and extract agentskillsConfig
59
+ const config = this.validateAndExtractConfig(packageJson);
60
+ return {
61
+ skills,
62
+ config,
63
+ source: {
64
+ type: "file",
65
+ path: this.packageJsonPath
66
+ }
67
+ };
68
+ }
69
+ catch (error) {
70
+ // Return defaults if file doesn't exist
71
+ if (error.code === "ENOENT") {
72
+ return this.getDefaultConfig();
73
+ }
74
+ // Handle permission errors
75
+ if (error.code === "EACCES") {
76
+ throw new Error(`Permission denied reading package.json at ${this.packageJsonPath}`);
77
+ }
78
+ // Re-throw other errors (like parse errors)
79
+ throw error;
80
+ }
81
+ }
82
+ /**
83
+ * Validate and extract skills from package.json
84
+ */
85
+ validateAndExtractSkills(packageJson) {
86
+ if (!packageJson.agentskills) {
87
+ return {};
88
+ }
89
+ const agentskills = packageJson.agentskills;
90
+ // Validate agentskills is an object
91
+ if (typeof agentskills !== "object" ||
92
+ agentskills === null ||
93
+ Array.isArray(agentskills)) {
94
+ throw new Error("agentskills must be an object");
95
+ }
96
+ // Validate all values are strings
97
+ for (const value of Object.values(agentskills)) {
98
+ if (typeof value !== "string") {
99
+ throw new Error("agentskills values must be strings");
100
+ }
101
+ }
102
+ return agentskills;
103
+ }
104
+ /**
105
+ * Validate and extract config from package.json
106
+ */
107
+ validateAndExtractConfig(packageJson) {
108
+ const defaultConfig = this.getDefaultConfig().config;
109
+ if (!packageJson.agentskillsConfig) {
110
+ return defaultConfig;
111
+ }
112
+ const agentskillsConfig = packageJson.agentskillsConfig;
113
+ // Validate agentskillsConfig is an object
114
+ if (typeof agentskillsConfig !== "object" ||
115
+ agentskillsConfig === null ||
116
+ Array.isArray(agentskillsConfig)) {
117
+ throw new Error("agentskillsConfig must be an object");
118
+ }
119
+ // Type assertion after validation
120
+ const configObj = agentskillsConfig;
121
+ // Start with defaults and merge
122
+ const config = { ...defaultConfig };
123
+ // Validate and merge skillsDirectory
124
+ if (configObj.skillsDirectory !== undefined) {
125
+ if (typeof configObj.skillsDirectory !== "string") {
126
+ throw new Error("skillsDirectory must be a string");
127
+ }
128
+ if (configObj.skillsDirectory === "") {
129
+ throw new Error("skillsDirectory cannot be empty");
130
+ }
131
+ config.skillsDirectory = configObj.skillsDirectory;
132
+ }
133
+ // Validate and merge autoDiscover
134
+ if (configObj.autoDiscover !== undefined) {
135
+ if (!Array.isArray(configObj.autoDiscover)) {
136
+ throw new Error("autoDiscover must be an array");
137
+ }
138
+ for (const item of configObj.autoDiscover) {
139
+ if (typeof item !== "string") {
140
+ throw new Error("autoDiscover must contain only strings");
141
+ }
142
+ }
143
+ config.autoDiscover = configObj.autoDiscover;
144
+ }
145
+ // Validate and merge maxSkillSize
146
+ if (configObj.maxSkillSize !== undefined) {
147
+ if (typeof configObj.maxSkillSize !== "number") {
148
+ throw new Error("maxSkillSize must be a number");
149
+ }
150
+ if (configObj.maxSkillSize <= 0) {
151
+ throw new Error("maxSkillSize must be a positive number");
152
+ }
153
+ config.maxSkillSize = configObj.maxSkillSize;
154
+ }
155
+ // Validate and merge logLevel
156
+ if (configObj.logLevel !== undefined) {
157
+ if (typeof configObj.logLevel !== "string") {
158
+ throw new Error("logLevel must be a string");
159
+ }
160
+ const validLogLevels = ["error", "warn", "info", "debug"];
161
+ if (!validLogLevels.includes(configObj.logLevel)) {
162
+ throw new Error(`Invalid logLevel '${configObj.logLevel}'. Must be one of: error, warn, info, debug`);
163
+ }
164
+ config.logLevel = configObj.logLevel;
165
+ }
166
+ return config;
167
+ }
168
+ /**
169
+ * Save skills to package.json
170
+ * Creates package.json if it doesn't exist
171
+ * Preserves other fields
172
+ */
173
+ async saveSkills(skills) {
174
+ let packageJson;
175
+ try {
176
+ const content = await fs.readFile(this.packageJsonPath, "utf-8");
177
+ packageJson = JSON.parse(content);
178
+ }
179
+ catch (error) {
180
+ if (error &&
181
+ typeof error === "object" &&
182
+ "code" in error &&
183
+ error.code === "ENOENT") {
184
+ // Create minimal package.json
185
+ packageJson = {
186
+ name: "agentskills-project"
187
+ };
188
+ }
189
+ else {
190
+ throw error;
191
+ }
192
+ }
193
+ // Update agentskills field
194
+ packageJson.agentskills = skills;
195
+ // Write back to file with proper formatting
196
+ await fs.writeFile(this.packageJsonPath, JSON.stringify(packageJson, null, 2), "utf-8");
197
+ }
198
+ /**
199
+ * Add a single skill to package.json
200
+ * Updates existing skill if name already exists
201
+ */
202
+ async addSkill(name, spec) {
203
+ if (!name) {
204
+ throw new Error("Skill name cannot be empty");
205
+ }
206
+ if (!spec) {
207
+ throw new Error("Skill spec cannot be empty");
208
+ }
209
+ let packageJson;
210
+ try {
211
+ const content = await fs.readFile(this.packageJsonPath, "utf-8");
212
+ packageJson = JSON.parse(content);
213
+ }
214
+ catch (error) {
215
+ if (error &&
216
+ typeof error === "object" &&
217
+ "code" in error &&
218
+ error.code === "ENOENT") {
219
+ // Create minimal package.json
220
+ packageJson = {
221
+ name: "agentskills-project"
222
+ };
223
+ }
224
+ else {
225
+ throw error;
226
+ }
227
+ }
228
+ // Initialize agentskills if it doesn't exist
229
+ if (!packageJson.agentskills) {
230
+ packageJson.agentskills = {};
231
+ }
232
+ // Add or update skill - use type assertion after validation
233
+ const agentskills = packageJson.agentskills;
234
+ agentskills[name] = spec;
235
+ // Write back to file with proper formatting
236
+ await fs.writeFile(this.packageJsonPath, JSON.stringify(packageJson, null, 2), "utf-8");
237
+ }
238
+ /**
239
+ * Remove a skill from package.json
240
+ * Does not error if skill doesn't exist or file doesn't exist
241
+ */
242
+ async removeSkill(name) {
243
+ if (!name) {
244
+ throw new Error("Skill name cannot be empty");
245
+ }
246
+ try {
247
+ const content = await fs.readFile(this.packageJsonPath, "utf-8");
248
+ const packageJson = JSON.parse(content);
249
+ // If agentskills doesn't exist, nothing to remove
250
+ if (!packageJson.agentskills) {
251
+ return;
252
+ }
253
+ // Remove skill
254
+ delete packageJson.agentskills[name];
255
+ // Write back to file with proper formatting
256
+ await fs.writeFile(this.packageJsonPath, JSON.stringify(packageJson, null, 2), "utf-8");
257
+ }
258
+ catch (error) {
259
+ // If file doesn't exist, nothing to remove
260
+ if (error.code === "ENOENT") {
261
+ return;
262
+ }
263
+ throw error;
264
+ }
265
+ }
266
+ }
267
+ //# sourceMappingURL=package-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-config.js","sourceRoot":"","sources":["../src/package-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * SkillParser Component
3
+ *
4
+ * Responsibility: Parse SKILL.md files into structured Skill objects
5
+ * following the Agent Skills standard and Claude Code extensions.
6
+ *
7
+ * This module provides two main functions:
8
+ * - parseSkillContent: Parse skill content string directly
9
+ * - parseSkill: Read and parse a skill file from the filesystem
10
+ */
11
+ import type { ParseResult } from "./types.js";
12
+ /**
13
+ * Parse skill content from a string
14
+ *
15
+ * Extracts YAML frontmatter and Markdown body, validates required fields,
16
+ * and returns a structured Skill object.
17
+ *
18
+ * @param content - Raw skill file content (YAML frontmatter + Markdown body)
19
+ * @returns ParseResult with either success (Skill) or failure (ParseError)
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const content = `---
24
+ * name: example-skill
25
+ * description: An example skill
26
+ * ---
27
+ * # Example Skill
28
+ *
29
+ * This is the skill body.
30
+ * `;
31
+ *
32
+ * const result = parseSkillContent(content);
33
+ * if (result.success) {
34
+ * console.log(result.skill.metadata.name); // "example-skill"
35
+ * }
36
+ * ```
37
+ */
38
+ export declare function parseSkillContent(content: string): ParseResult;
39
+ /**
40
+ * Read and parse a skill file from the filesystem
41
+ *
42
+ * Reads the file at the given path, then delegates to parseSkillContent
43
+ * for parsing. Handles file system errors gracefully.
44
+ *
45
+ * @param filePath - Absolute path to SKILL.md file
46
+ * @returns ParseResult with either success (Skill) or failure (ParseError)
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const result = await parseSkill('/path/to/SKILL.md');
51
+ * if (result.success) {
52
+ * console.log(result.skill.metadata.name);
53
+ * } else {
54
+ * console.error(result.error.message);
55
+ * }
56
+ * ```
57
+ */
58
+ export declare function parseSkill(filePath: string): Promise<ParseResult>;
59
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EACV,WAAW,EAKZ,MAAM,YAAY,CAAC;AAsDpB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAiD9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA0BvE"}
package/dist/parser.js ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+ * SkillParser Component
3
+ *
4
+ * Responsibility: Parse SKILL.md files into structured Skill objects
5
+ * following the Agent Skills standard and Claude Code extensions.
6
+ *
7
+ * This module provides two main functions:
8
+ * - parseSkillContent: Parse skill content string directly
9
+ * - parseSkill: Read and parse a skill file from the filesystem
10
+ */
11
+ import matter from "gray-matter";
12
+ import { promises as fs } from "fs";
13
+ /**
14
+ * Field name mapping from kebab-case (YAML) to camelCase (TypeScript)
15
+ */
16
+ const FIELD_MAP = {
17
+ name: "name",
18
+ description: "description",
19
+ license: "license",
20
+ compatibility: "compatibility",
21
+ metadata: "metadata",
22
+ "allowed-tools": "allowedTools",
23
+ "disable-model-invocation": "disableModelInvocation",
24
+ "user-invocable": "userInvocable",
25
+ "argument-hint": "argumentHint",
26
+ context: "context",
27
+ agent: "agent",
28
+ model: "model",
29
+ hooks: "hooks"
30
+ };
31
+ /**
32
+ * Required fields that must be present in skill metadata
33
+ */
34
+ const REQUIRED_FIELDS = ["name", "description"];
35
+ /**
36
+ * Helper function to create error result
37
+ */
38
+ function createError(code, message, field) {
39
+ return {
40
+ success: false,
41
+ error: { code, message, ...(field && { field }) }
42
+ };
43
+ }
44
+ /**
45
+ * Map YAML field names (kebab-case) to TypeScript field names (camelCase)
46
+ */
47
+ function mapFieldNames(data) {
48
+ const metadata = {};
49
+ for (const [key, value] of Object.entries(data)) {
50
+ const mappedKey = FIELD_MAP[key] || key;
51
+ metadata[mappedKey] = value;
52
+ }
53
+ return metadata;
54
+ }
55
+ /**
56
+ * Parse skill content from a string
57
+ *
58
+ * Extracts YAML frontmatter and Markdown body, validates required fields,
59
+ * and returns a structured Skill object.
60
+ *
61
+ * @param content - Raw skill file content (YAML frontmatter + Markdown body)
62
+ * @returns ParseResult with either success (Skill) or failure (ParseError)
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const content = `---
67
+ * name: example-skill
68
+ * description: An example skill
69
+ * ---
70
+ * # Example Skill
71
+ *
72
+ * This is the skill body.
73
+ * `;
74
+ *
75
+ * const result = parseSkillContent(content);
76
+ * if (result.success) {
77
+ * console.log(result.skill.metadata.name); // "example-skill"
78
+ * }
79
+ * ```
80
+ */
81
+ export function parseSkillContent(content) {
82
+ // Check for empty file
83
+ if (!content || content.trim().length === 0) {
84
+ return createError("EMPTY_FILE", "Skill file is empty");
85
+ }
86
+ // Parse frontmatter using gray-matter
87
+ let parsed;
88
+ try {
89
+ parsed = matter(content);
90
+ }
91
+ catch (error) {
92
+ return createError("INVALID_YAML", `Failed to parse YAML frontmatter: ${error.message}`);
93
+ }
94
+ // Check if frontmatter exists
95
+ if (!parsed.data || Object.keys(parsed.data).length === 0) {
96
+ return createError("MISSING_FRONTMATTER", "Skill file must contain YAML frontmatter");
97
+ }
98
+ // Validate required fields
99
+ for (const field of REQUIRED_FIELDS) {
100
+ if (!(field in parsed.data)) {
101
+ return createError("MISSING_REQUIRED_FIELD", `required field '${field}' is missing from skill metadata`, field);
102
+ }
103
+ }
104
+ // Map field names from kebab-case to camelCase
105
+ const metadata = mapFieldNames(parsed.data);
106
+ // Create Skill object
107
+ const skill = Object.freeze({
108
+ metadata: Object.freeze(metadata),
109
+ body: parsed.content
110
+ });
111
+ return {
112
+ success: true,
113
+ skill
114
+ };
115
+ }
116
+ /**
117
+ * Read and parse a skill file from the filesystem
118
+ *
119
+ * Reads the file at the given path, then delegates to parseSkillContent
120
+ * for parsing. Handles file system errors gracefully.
121
+ *
122
+ * @param filePath - Absolute path to SKILL.md file
123
+ * @returns ParseResult with either success (Skill) or failure (ParseError)
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const result = await parseSkill('/path/to/SKILL.md');
128
+ * if (result.success) {
129
+ * console.log(result.skill.metadata.name);
130
+ * } else {
131
+ * console.error(result.error.message);
132
+ * }
133
+ * ```
134
+ */
135
+ export async function parseSkill(filePath) {
136
+ try {
137
+ const content = await fs.readFile(filePath, "utf-8");
138
+ return parseSkillContent(content);
139
+ }
140
+ catch (error) {
141
+ const nodeError = error;
142
+ // File not found error
143
+ if (nodeError.code === "ENOENT") {
144
+ return createError("FILE_NOT_FOUND", `File not found: ${filePath}`);
145
+ }
146
+ // Permission or other read errors
147
+ if (nodeError.code === "EACCES" || nodeError.code === "EISDIR") {
148
+ return createError("FILE_READ_ERROR", `Failed to read file: ${nodeError.message}`);
149
+ }
150
+ // Other errors
151
+ return createError("FILE_READ_ERROR", `Failed to read file: ${nodeError.message}`);
152
+ }
153
+ }
154
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * SkillRegistry Component
3
+ *
4
+ * Responsibility: In-memory skill storage with Map-based O(1) lookups.
5
+ * Load skills from a single directory with strict fail-fast behavior.
6
+ *
7
+ * Expected structure: <skillsDir>/<skill-name>/SKILL.md (exactly 2 levels deep)
8
+ * Throws errors on any misconfiguration (no partial failures).
9
+ */
10
+ import type { Skill, SkillMetadata, LoadResult, RegistryState } from "./types.js";
11
+ /**
12
+ * In-memory registry for managing agent skills
13
+ *
14
+ * Features:
15
+ * - O(1) skill lookups using Map
16
+ * - Load skills from single directory (strict fail-fast)
17
+ * - Expected structure: <skillsDir>/<skill-name>/SKILL.md
18
+ * - Validates directory name matches skill name
19
+ * - Immutable skill objects
20
+ */
21
+ export declare class SkillRegistry {
22
+ private skills;
23
+ private skillsDir;
24
+ private lastLoaded?;
25
+ /**
26
+ * Load skills from a single directory with strict error handling
27
+ *
28
+ * Expected structure: <skillsDir>/<skill-name>/SKILL.md (exactly 2 levels deep)
29
+ * - Throws on any error (fail fast)
30
+ * - Ignores hidden directories (.git/, etc.)
31
+ * - Ignores non-directory files
32
+ * - Validates directory name matches skill name in SKILL.md
33
+ *
34
+ * @param skillsDir - Directory containing skill subdirectories
35
+ * @returns Load result with count, directory, and timestamp
36
+ * @throws Error if directory doesn't exist, isn't a directory, or any skill is invalid
37
+ */
38
+ loadSkills(skillsDir: string): Promise<LoadResult>;
39
+ /**
40
+ * Get a skill by name
41
+ *
42
+ * @param name - The skill name
43
+ * @returns The skill or undefined if not found
44
+ */
45
+ getSkill(name: string): Skill | undefined;
46
+ /**
47
+ * Get all loaded skills
48
+ *
49
+ * @returns Array of all skills
50
+ */
51
+ getAllSkills(): Skill[];
52
+ /**
53
+ * Get skill metadata without body content
54
+ *
55
+ * @param name - The skill name
56
+ * @returns The skill metadata or undefined if not found
57
+ */
58
+ getSkillMetadata(name: string): SkillMetadata | undefined;
59
+ /**
60
+ * Get all skill metadata without body content
61
+ *
62
+ * @returns Array of all skill metadata
63
+ */
64
+ getAllMetadata(): SkillMetadata[];
65
+ /**
66
+ * Get current registry state
67
+ *
68
+ * @returns Current state with counts and source info
69
+ */
70
+ getState(): RegistryState;
71
+ }
72
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EACV,KAAK,EACL,aAAa,EACb,UAAU,EACV,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;GASG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAgE;IAC9E,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,UAAU,CAAC,CAAO;IAE1B;;;;;;;;;;;;OAYG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgGxD;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAazC;;;;OAIG;IACH,YAAY,IAAI,KAAK,EAAE;IAOvB;;;;;OAKG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IASzD;;;;OAIG;IACH,cAAc,IAAI,aAAa,EAAE;IAMjC;;;;OAIG;IACH,QAAQ,IAAI,aAAa;CAO1B"}