@teamix-evo/registry 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +123 -0
- package/dist/index.cjs +384 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +796 -24
- package/dist/index.d.ts +796 -24
- package/dist/index.js +366 -12
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Teamix Evo Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# @teamix-evo/registry
|
|
2
|
+
|
|
3
|
+
> Teamix Evo 协议包 — 提供 Schema、类型、加载器、校验器和策略决策引擎。
|
|
4
|
+
|
|
5
|
+
## 定位
|
|
6
|
+
|
|
7
|
+
registry 是整个 Teamix Evo 架构的**协议层**,负责定义:
|
|
8
|
+
|
|
9
|
+
- 资源清单(VariantManifest)的结构约束
|
|
10
|
+
- 项目配置(ProjectConfig)的 Schema
|
|
11
|
+
- 安装状态(InstalledManifest)的追踪格式
|
|
12
|
+
- 三种更新策略(frozen / regenerable / managed)的决策逻辑
|
|
13
|
+
- Managed Regions 标记的解析与替换
|
|
14
|
+
|
|
15
|
+
CLI 包和 design 包都依赖本包的协议定义工作。
|
|
16
|
+
|
|
17
|
+
## 目录结构
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
packages/registry/
|
|
21
|
+
├── src/
|
|
22
|
+
│ ├── schema/
|
|
23
|
+
│ │ ├── index.ts # Schema 统一导出
|
|
24
|
+
│ │ ├── manifest.ts # VariantManifest Zod Schema
|
|
25
|
+
│ │ ├── config.ts # ProjectConfig Zod Schema
|
|
26
|
+
│ │ └── installed.ts # InstalledManifest Zod Schema
|
|
27
|
+
│ ├── types.ts # 从 Schema 推导的 TypeScript 类型
|
|
28
|
+
│ ├── loader.ts # 从文件系统加载 manifest
|
|
29
|
+
│ ├── validator.ts # 校验封装 + 友好错误消息
|
|
30
|
+
│ ├── strategy.ts # updateStrategy 决策函数
|
|
31
|
+
│ ├── managed-regions.ts # HTML 标记区解析与替换
|
|
32
|
+
│ ├── index.ts # 公共导出入口
|
|
33
|
+
│ └── __tests__/ # 单元测试
|
|
34
|
+
├── tsup.config.ts # 构建配置(ESM + CJS + DTS)
|
|
35
|
+
├── tsconfig.json
|
|
36
|
+
└── package.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 研发流程
|
|
40
|
+
|
|
41
|
+
### 1. 环境准备
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 在仓库根目录
|
|
45
|
+
pnpm install
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. 开发
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# 监听模式构建(修改即时编译)
|
|
52
|
+
pnpm --filter @teamix-evo/registry dev
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. 新增 / 修改 Schema
|
|
56
|
+
|
|
57
|
+
1. 在 `src/schema/` 中使用 Zod 定义或修改 Schema
|
|
58
|
+
2. 在 `src/types.ts` 中通过 `z.infer<typeof XxxSchema>` 导出对应类型
|
|
59
|
+
3. 在 `src/index.ts` 中确保新增的 Schema 和类型被导出
|
|
60
|
+
4. 如需新增校验函数,在 `src/validator.ts` 中添加
|
|
61
|
+
|
|
62
|
+
### 4. 修改策略逻辑
|
|
63
|
+
|
|
64
|
+
- `src/strategy.ts` — 修改 `shouldUpdate` 或 `getUpdateAction`
|
|
65
|
+
- `src/managed-regions.ts` — 修改标记解析或替换逻辑
|
|
66
|
+
|
|
67
|
+
### 5. 测试
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 运行全部测试
|
|
71
|
+
pnpm --filter @teamix-evo/registry test
|
|
72
|
+
|
|
73
|
+
# 监听模式
|
|
74
|
+
pnpm --filter @teamix-evo/registry test:watch
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
测试文件位于 `src/__tests__/`,覆盖:
|
|
78
|
+
|
|
79
|
+
- `manifest.test.ts` — VariantManifest Schema 校验
|
|
80
|
+
- `config.test.ts` — ProjectConfig Schema 校验
|
|
81
|
+
- `installed.test.ts` — InstalledManifest Schema 校验
|
|
82
|
+
- `validator.test.ts` — 友好错误消息格式化
|
|
83
|
+
- `strategy.test.ts` — 三种策略决策逻辑
|
|
84
|
+
- `managed-regions.test.ts` — 标记解析、替换、错误检测
|
|
85
|
+
|
|
86
|
+
### 6. 类型检查
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pnpm --filter @teamix-evo/registry typecheck
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 7. 构建
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pnpm --filter @teamix-evo/registry build
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
产物输出到 `dist/`,包含:
|
|
99
|
+
|
|
100
|
+
- `index.js` — ESM 格式
|
|
101
|
+
- `index.cjs` — CommonJS 格式
|
|
102
|
+
- `index.d.ts` — TypeScript 声明
|
|
103
|
+
|
|
104
|
+
### 8. 发布前检查
|
|
105
|
+
|
|
106
|
+
- 确保所有测试通过
|
|
107
|
+
- 确保类型检查通过
|
|
108
|
+
- 本包与 `teamix-evo`(CLI)使用 **linked** 版本策略,需一起发布
|
|
109
|
+
|
|
110
|
+
## 关键约定
|
|
111
|
+
|
|
112
|
+
- 所有 Schema 使用 [Zod](https://zod.dev/) 定义,运行时校验 + 编译时类型推导一体
|
|
113
|
+
- `Result<T>` 类型用于校验返回,避免 throw(除 loader 和 managed-regions 外)
|
|
114
|
+
- managed regions 标记格式:`<!-- teamix-evo:managed:start id="xxx" -->...<!-- teamix-evo:managed:end id="xxx" -->`
|
|
115
|
+
- 同一文件中 region id 不得重复
|
|
116
|
+
|
|
117
|
+
## 依赖关系
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
本包无内部包依赖
|
|
121
|
+
CLI 包 → 依赖本包
|
|
122
|
+
design 包 → devDependencies 引用本包(验证脚本)
|
|
123
|
+
```
|
package/dist/index.cjs
CHANGED
|
@@ -37,17 +37,34 @@ __export(index_exports, {
|
|
|
37
37
|
ProjectConfigSchema: () => ProjectConfigSchema,
|
|
38
38
|
ResourceSchema: () => ResourceSchema,
|
|
39
39
|
ResourceTypeSchema: () => ResourceTypeSchema,
|
|
40
|
+
SkillEntrySchema: () => SkillEntrySchema,
|
|
41
|
+
SkillIdeSchema: () => SkillIdeSchema,
|
|
42
|
+
SkillScopeSchema: () => SkillScopeSchema,
|
|
43
|
+
SkillsPackageManifestSchema: () => SkillsPackageManifestSchema,
|
|
44
|
+
TailwindVersionSchema: () => TailwindVersionSchema,
|
|
45
|
+
UiAliasSchema: () => UiAliasSchema,
|
|
46
|
+
UiAliasesSchema: () => UiAliasesSchema,
|
|
47
|
+
UiEntryFileSchema: () => UiEntryFileSchema,
|
|
48
|
+
UiEntrySchema: () => UiEntrySchema,
|
|
49
|
+
UiEntryTypeSchema: () => UiEntryTypeSchema,
|
|
50
|
+
UiPackageManifestSchema: () => UiPackageManifestSchema,
|
|
40
51
|
UpdateStrategySchema: () => UpdateStrategySchema,
|
|
41
52
|
VariantManifestSchema: () => VariantManifestSchema,
|
|
42
53
|
getUpdateAction: () => getUpdateAction,
|
|
43
54
|
hasManagedRegion: () => hasManagedRegion,
|
|
55
|
+
loadSkillsPackageManifest: () => loadSkillsPackageManifest,
|
|
56
|
+
loadUiPackageManifest: () => loadUiPackageManifest,
|
|
44
57
|
loadVariantManifest: () => loadVariantManifest,
|
|
45
58
|
parseManagedRegions: () => parseManagedRegions,
|
|
46
59
|
replaceManagedRegion: () => replaceManagedRegion,
|
|
60
|
+
resolveUiEntryOrder: () => resolveUiEntryOrder,
|
|
47
61
|
shouldUpdate: () => shouldUpdate,
|
|
48
62
|
validateConfig: () => validateConfig,
|
|
49
63
|
validateInstalled: () => validateInstalled,
|
|
50
|
-
validateManifest: () => validateManifest
|
|
64
|
+
validateManifest: () => validateManifest,
|
|
65
|
+
validateSkillsPackage: () => validateSkillsPackage,
|
|
66
|
+
validateUiDependencyGraph: () => validateUiDependencyGraph,
|
|
67
|
+
validateUiPackage: () => validateUiPackage
|
|
51
68
|
});
|
|
52
69
|
module.exports = __toCommonJS(index_exports);
|
|
53
70
|
|
|
@@ -62,8 +79,11 @@ var ResourceTypeSchema = import_zod.z.enum([
|
|
|
62
79
|
"doc",
|
|
63
80
|
"tokens",
|
|
64
81
|
"ai-rules",
|
|
65
|
-
"config"
|
|
82
|
+
"config",
|
|
83
|
+
"skill"
|
|
66
84
|
]);
|
|
85
|
+
var SkillIdeSchema = import_zod.z.enum(["qoder", "claude"]);
|
|
86
|
+
var SkillScopeSchema = import_zod.z.enum(["project", "global"]);
|
|
67
87
|
var ResourceSchema = import_zod.z.object({
|
|
68
88
|
/** Unique identifier for the resource within the manifest */
|
|
69
89
|
id: import_zod.z.string().min(1),
|
|
@@ -71,7 +91,10 @@ var ResourceSchema = import_zod.z.object({
|
|
|
71
91
|
type: ResourceTypeSchema,
|
|
72
92
|
/** Source path (relative to variant root), typically a template */
|
|
73
93
|
source: import_zod.z.string().min(1),
|
|
74
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Target path (relative to project root) where the resource is installed.
|
|
96
|
+
* For type="skill" this is ignored: the path is computed from skill name + ide + scope.
|
|
97
|
+
*/
|
|
75
98
|
target: import_zod.z.string().min(1),
|
|
76
99
|
/** How the resource is updated */
|
|
77
100
|
updateStrategy: UpdateStrategySchema,
|
|
@@ -80,7 +103,11 @@ var ResourceSchema = import_zod.z.object({
|
|
|
80
103
|
/** IDs of managed regions (only relevant when updateStrategy is "managed") */
|
|
81
104
|
managedRegions: import_zod.z.array(import_zod.z.string()).optional(),
|
|
82
105
|
/** Whether to recursively process directory contents */
|
|
83
|
-
recursive: import_zod.z.boolean().optional()
|
|
106
|
+
recursive: import_zod.z.boolean().optional(),
|
|
107
|
+
/** Supported IDEs (only meaningful when type="skill"; defaults to all when omitted) */
|
|
108
|
+
ides: import_zod.z.array(SkillIdeSchema).optional(),
|
|
109
|
+
/** Default install scope (only meaningful when type="skill"; user can override at install time) */
|
|
110
|
+
scope: SkillScopeSchema.optional()
|
|
84
111
|
});
|
|
85
112
|
var VariantManifestSchema = import_zod.z.object({
|
|
86
113
|
$schema: import_zod.z.string().optional(),
|
|
@@ -104,14 +131,160 @@ var VariantManifestSchema = import_zod.z.object({
|
|
|
104
131
|
/** List of resources provided by this variant */
|
|
105
132
|
resources: import_zod.z.array(ResourceSchema)
|
|
106
133
|
});
|
|
134
|
+
var SkillEntrySchema = import_zod.z.object({
|
|
135
|
+
/** Skill identifier (matches name/folder) */
|
|
136
|
+
id: import_zod.z.string().min(1),
|
|
137
|
+
/** Skill name — must match the directory name and frontmatter `name` */
|
|
138
|
+
name: import_zod.z.string().min(1).regex(
|
|
139
|
+
/^[a-z0-9][a-z0-9-]*$/,
|
|
140
|
+
"Skill name must be lowercase letters, digits, hyphens (no leading hyphen)"
|
|
141
|
+
),
|
|
142
|
+
/** One-line description for skill discovery */
|
|
143
|
+
description: import_zod.z.string().min(1),
|
|
144
|
+
/** Semver version */
|
|
145
|
+
version: import_zod.z.string().regex(/^\d+\.\d+\.\d+/, "Invalid semver version"),
|
|
146
|
+
/** Source path relative to package root — file or directory */
|
|
147
|
+
source: import_zod.z.string().min(1),
|
|
148
|
+
/** Supported IDEs (defaults to both when omitted) */
|
|
149
|
+
ides: import_zod.z.array(SkillIdeSchema).default(["qoder", "claude"]),
|
|
150
|
+
/** Update strategy (defaults to managed) */
|
|
151
|
+
updateStrategy: UpdateStrategySchema.default("managed"),
|
|
152
|
+
/** Managed region IDs to maintain on update */
|
|
153
|
+
managedRegions: import_zod.z.array(import_zod.z.string()).optional(),
|
|
154
|
+
/** Whether the source is a Handlebars template */
|
|
155
|
+
template: import_zod.z.boolean().optional()
|
|
156
|
+
});
|
|
157
|
+
var SkillsPackageManifestSchema = import_zod.z.object({
|
|
158
|
+
$schema: import_zod.z.string().optional(),
|
|
159
|
+
schemaVersion: import_zod.z.literal(1),
|
|
160
|
+
/** Always "skills" for this package */
|
|
161
|
+
package: import_zod.z.literal("skills"),
|
|
162
|
+
/** Semver version of the skills package */
|
|
163
|
+
version: import_zod.z.string().regex(/^\d+\.\d+\.\d+/, "Invalid semver version"),
|
|
164
|
+
/** Engine compatibility */
|
|
165
|
+
engines: import_zod.z.object({
|
|
166
|
+
"teamix-evo": import_zod.z.string().min(1)
|
|
167
|
+
}),
|
|
168
|
+
/** Flat list of skills shipped by this package */
|
|
169
|
+
skills: import_zod.z.array(SkillEntrySchema)
|
|
170
|
+
});
|
|
171
|
+
var UiEntryTypeSchema = import_zod.z.enum([
|
|
172
|
+
"component",
|
|
173
|
+
"hook",
|
|
174
|
+
"util",
|
|
175
|
+
"block"
|
|
176
|
+
]);
|
|
177
|
+
var UiAliasSchema = import_zod.z.enum(["components", "hooks", "utils", "lib"]);
|
|
178
|
+
var UiEntryStatusSchema = import_zod.z.enum([
|
|
179
|
+
"stable",
|
|
180
|
+
"experimental",
|
|
181
|
+
"deprecated"
|
|
182
|
+
]);
|
|
183
|
+
var UiEntryFileSchema = import_zod.z.object({
|
|
184
|
+
/** Source path relative to the ui package root (e.g. "src/components/button/button.tsx") */
|
|
185
|
+
source: import_zod.z.string().min(1),
|
|
186
|
+
/** Which alias this file resolves under in the consumer project */
|
|
187
|
+
targetAlias: UiAliasSchema,
|
|
188
|
+
/** Filename written under the alias directory (e.g. "button.tsx") */
|
|
189
|
+
targetName: import_zod.z.string().min(1)
|
|
190
|
+
});
|
|
191
|
+
var UiEntrySchema = import_zod.z.object({
|
|
192
|
+
/** Unique entry identifier within the ui package (e.g. "button") */
|
|
193
|
+
id: import_zod.z.string().min(1).regex(
|
|
194
|
+
/^[a-z0-9][a-z0-9-]*$/,
|
|
195
|
+
"UI entry id must be lowercase letters, digits, hyphens (no leading hyphen)"
|
|
196
|
+
),
|
|
197
|
+
/** Display name (e.g. "Button") */
|
|
198
|
+
name: import_zod.z.string().min(1),
|
|
199
|
+
/** Entry type */
|
|
200
|
+
type: UiEntryTypeSchema,
|
|
201
|
+
/** One-line description for entry discovery and AI guidance */
|
|
202
|
+
description: import_zod.z.string().min(1),
|
|
203
|
+
/** Files this entry ships (typically 1) */
|
|
204
|
+
files: import_zod.z.array(UiEntryFileSchema).min(1),
|
|
205
|
+
/**
|
|
206
|
+
* Optional path to an AI-readable meta document (frontmatter + Markdown).
|
|
207
|
+
* When set, CLI install drops it at `.teamix-evo/design/components/<id>.meta.md`
|
|
208
|
+
* for design's ai-rules to consume.
|
|
209
|
+
*/
|
|
210
|
+
meta: import_zod.z.string().optional(),
|
|
211
|
+
/** Other UI entries this one depends on (e.g. "button" depends on "cn") */
|
|
212
|
+
registryDependencies: import_zod.z.array(import_zod.z.string()).optional(),
|
|
213
|
+
/** npm dependencies required by this entry (name → semver range) */
|
|
214
|
+
dependencies: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
215
|
+
/**
|
|
216
|
+
* How CLI handles this entry on `ui upgrade`.
|
|
217
|
+
* Defaults to `frozen` — user-owned source code, untouched after first install.
|
|
218
|
+
* Upgrade is handled by AI + skill flow (no CLI rewrite). See PLAN §10.9.
|
|
219
|
+
*/
|
|
220
|
+
updateStrategy: UpdateStrategySchema.default("frozen"),
|
|
221
|
+
/**
|
|
222
|
+
* Whether the source file should be passed through Handlebars before write.
|
|
223
|
+
* Most entries don't need templating (use import-rewrite transformer instead).
|
|
224
|
+
*/
|
|
225
|
+
template: import_zod.z.boolean().optional(),
|
|
226
|
+
/**
|
|
227
|
+
* Maturity / lifecycle status of this entry. Defaults to `stable`.
|
|
228
|
+
* See {@link UiEntryStatusSchema} for semantics.
|
|
229
|
+
*/
|
|
230
|
+
status: UiEntryStatusSchema.default("stable"),
|
|
231
|
+
/**
|
|
232
|
+
* Free-text rationale shown to consumers when `status` is `deprecated`.
|
|
233
|
+
* Should explain why the entry is going away and what replaces it.
|
|
234
|
+
*/
|
|
235
|
+
deprecatedReason: import_zod.z.string().optional(),
|
|
236
|
+
/**
|
|
237
|
+
* If this entry is `deprecated`, the id of the entry that supersedes it.
|
|
238
|
+
* Consumers (and AI) should migrate to `replacedBy` rather than this entry.
|
|
239
|
+
*/
|
|
240
|
+
replacedBy: import_zod.z.string().optional()
|
|
241
|
+
});
|
|
242
|
+
var UiPackageManifestSchema = import_zod.z.object({
|
|
243
|
+
$schema: import_zod.z.string().optional(),
|
|
244
|
+
schemaVersion: import_zod.z.literal(1),
|
|
245
|
+
/** Always "ui" for this package */
|
|
246
|
+
package: import_zod.z.literal("ui"),
|
|
247
|
+
/** Semver version of the ui package */
|
|
248
|
+
version: import_zod.z.string().regex(/^\d+\.\d+\.\d+/, "Invalid semver version"),
|
|
249
|
+
/** Engine compatibility */
|
|
250
|
+
engines: import_zod.z.object({
|
|
251
|
+
"teamix-evo": import_zod.z.string().min(1)
|
|
252
|
+
}),
|
|
253
|
+
/** Flat list of entries shipped by this package */
|
|
254
|
+
entries: import_zod.z.array(UiEntrySchema)
|
|
255
|
+
});
|
|
107
256
|
|
|
108
257
|
// src/schema/config.ts
|
|
109
258
|
var import_zod2 = require("zod");
|
|
259
|
+
var TailwindVersionSchema = import_zod2.z.enum(["v3", "v4"]);
|
|
260
|
+
var UiAliasesSchema = import_zod2.z.object({
|
|
261
|
+
components: import_zod2.z.string().min(1),
|
|
262
|
+
hooks: import_zod2.z.string().min(1),
|
|
263
|
+
utils: import_zod2.z.string().min(1),
|
|
264
|
+
lib: import_zod2.z.string().min(1)
|
|
265
|
+
});
|
|
110
266
|
var PackageEntrySchema = import_zod2.z.object({
|
|
111
|
-
/** Variant identifier (e.g. "opentrek") */
|
|
267
|
+
/** Variant identifier (e.g. "opentrek"; use "_flat" for skills/ui) */
|
|
112
268
|
variant: import_zod2.z.string().min(1),
|
|
113
269
|
/** Semver version string */
|
|
114
|
-
version: import_zod2.z.string().min(1)
|
|
270
|
+
version: import_zod2.z.string().min(1),
|
|
271
|
+
/** Tailwind CSS version this project uses (only meaningful for design package). */
|
|
272
|
+
tailwind: TailwindVersionSchema.optional(),
|
|
273
|
+
/** IDEs this package was installed for (only meaningful for skills package). */
|
|
274
|
+
ides: import_zod2.z.array(SkillIdeSchema).optional(),
|
|
275
|
+
/** Install scope (only meaningful for skills package). */
|
|
276
|
+
scope: SkillScopeSchema.optional(),
|
|
277
|
+
/** Path aliases for ui entry installation (only meaningful for ui package). */
|
|
278
|
+
aliases: UiAliasesSchema.optional(),
|
|
279
|
+
/**
|
|
280
|
+
* Default icon library declared by the project (only meaningful for ui).
|
|
281
|
+
* Declarative only — does NOT trigger code rewrites; ui entries hardcode imports.
|
|
282
|
+
*/
|
|
283
|
+
iconLibrary: import_zod2.z.string().min(1).optional(),
|
|
284
|
+
/** Whether the project uses TSX (true) or JSX (false). ui-specific. */
|
|
285
|
+
tsx: import_zod2.z.boolean().optional(),
|
|
286
|
+
/** Whether to emit React Server Components markers (`"use client"`). ui-specific. */
|
|
287
|
+
rsc: import_zod2.z.boolean().optional()
|
|
115
288
|
});
|
|
116
289
|
var ProjectConfigSchema = import_zod2.z.object({
|
|
117
290
|
$schema: import_zod2.z.string().optional(),
|
|
@@ -127,17 +300,21 @@ var import_zod3 = require("zod");
|
|
|
127
300
|
var InstalledResourceSchema = import_zod3.z.object({
|
|
128
301
|
/** Resource identifier matching the variant manifest */
|
|
129
302
|
id: import_zod3.z.string().min(1),
|
|
130
|
-
/** Target path where the resource was installed */
|
|
303
|
+
/** Target path where the resource was installed (absolute or project-relative) */
|
|
131
304
|
target: import_zod3.z.string().min(1),
|
|
132
305
|
/** Content hash for change detection (e.g. "sha256:...") */
|
|
133
306
|
hash: import_zod3.z.string().min(1),
|
|
134
307
|
/** Update strategy that was applied */
|
|
135
|
-
strategy: import_zod3.z.enum(["frozen", "regenerable", "managed"])
|
|
308
|
+
strategy: import_zod3.z.enum(["frozen", "regenerable", "managed"]),
|
|
309
|
+
/** IDE this resource was installed for (skill resources only) */
|
|
310
|
+
ide: SkillIdeSchema.optional(),
|
|
311
|
+
/** Install scope (skill resources only) */
|
|
312
|
+
scope: SkillScopeSchema.optional()
|
|
136
313
|
});
|
|
137
314
|
var InstalledPackageSchema = import_zod3.z.object({
|
|
138
315
|
/** Full package name (e.g. "@teamix-evo/design") */
|
|
139
316
|
package: import_zod3.z.string().min(1),
|
|
140
|
-
/** Variant identifier */
|
|
317
|
+
/** Variant identifier (use "_flat" for non-variant packages such as skills) */
|
|
141
318
|
variant: import_zod3.z.string().min(1),
|
|
142
319
|
/** Installed version */
|
|
143
320
|
version: import_zod3.z.string().min(1),
|
|
@@ -196,6 +373,115 @@ function validateInstalled(data) {
|
|
|
196
373
|
${formatZodError(result.error)}`
|
|
197
374
|
};
|
|
198
375
|
}
|
|
376
|
+
function validateSkillsPackage(data) {
|
|
377
|
+
const result = SkillsPackageManifestSchema.safeParse(data);
|
|
378
|
+
if (result.success) {
|
|
379
|
+
return { success: true, data: result.data };
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
success: false,
|
|
383
|
+
error: `Invalid skills package manifest:
|
|
384
|
+
${formatZodError(result.error)}`
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function validateUiPackage(data) {
|
|
388
|
+
const parsed = UiPackageManifestSchema.safeParse(data);
|
|
389
|
+
if (!parsed.success) {
|
|
390
|
+
return {
|
|
391
|
+
success: false,
|
|
392
|
+
error: `Invalid ui package manifest:
|
|
393
|
+
${formatZodError(parsed.error)}`
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
const graphIssues = validateUiDependencyGraph(parsed.data.entries);
|
|
397
|
+
if (graphIssues.length > 0) {
|
|
398
|
+
return {
|
|
399
|
+
success: false,
|
|
400
|
+
error: `Invalid ui package manifest dependency graph:
|
|
401
|
+
${graphIssues.map((i) => ` - ${i}`).join("\n")}`
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
return { success: true, data: parsed.data };
|
|
405
|
+
}
|
|
406
|
+
function validateUiDependencyGraph(entries) {
|
|
407
|
+
const issues = [];
|
|
408
|
+
const idToEntry = /* @__PURE__ */ new Map();
|
|
409
|
+
for (const entry of entries) {
|
|
410
|
+
if (idToEntry.has(entry.id)) {
|
|
411
|
+
issues.push(`duplicate entry id: "${entry.id}"`);
|
|
412
|
+
} else {
|
|
413
|
+
idToEntry.set(entry.id, entry);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
for (const entry of entries) {
|
|
417
|
+
const deps = entry.registryDependencies ?? [];
|
|
418
|
+
for (const dep of deps) {
|
|
419
|
+
if (!idToEntry.has(dep)) {
|
|
420
|
+
issues.push(
|
|
421
|
+
`entry "${entry.id}" depends on unknown entry "${dep}"`
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const WHITE = 0;
|
|
427
|
+
const GRAY = 1;
|
|
428
|
+
const BLACK = 2;
|
|
429
|
+
const color = /* @__PURE__ */ new Map();
|
|
430
|
+
for (const id of idToEntry.keys()) color.set(id, WHITE);
|
|
431
|
+
const cycles = [];
|
|
432
|
+
function dfs(id, path2) {
|
|
433
|
+
color.set(id, GRAY);
|
|
434
|
+
path2.push(id);
|
|
435
|
+
const entry = idToEntry.get(id);
|
|
436
|
+
const deps = entry?.registryDependencies ?? [];
|
|
437
|
+
for (const dep of deps) {
|
|
438
|
+
if (!idToEntry.has(dep)) continue;
|
|
439
|
+
const c = color.get(dep);
|
|
440
|
+
if (c === GRAY) {
|
|
441
|
+
const startIdx = path2.indexOf(dep);
|
|
442
|
+
cycles.push([...path2.slice(startIdx), dep]);
|
|
443
|
+
} else if (c === WHITE) {
|
|
444
|
+
dfs(dep, path2);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
path2.pop();
|
|
448
|
+
color.set(id, BLACK);
|
|
449
|
+
}
|
|
450
|
+
for (const id of idToEntry.keys()) {
|
|
451
|
+
if (color.get(id) === WHITE) dfs(id, []);
|
|
452
|
+
}
|
|
453
|
+
for (const cycle of cycles) {
|
|
454
|
+
issues.push(`cyclic registryDependencies: ${cycle.join(" \u2192 ")}`);
|
|
455
|
+
}
|
|
456
|
+
return issues;
|
|
457
|
+
}
|
|
458
|
+
function resolveUiEntryOrder(entries, requested) {
|
|
459
|
+
const idToEntry = new Map(entries.map((e) => [e.id, e]));
|
|
460
|
+
for (const id of requested) {
|
|
461
|
+
if (!idToEntry.has(id)) {
|
|
462
|
+
throw new Error(`Unknown ui entry: "${id}"`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const graphIssues = validateUiDependencyGraph(entries);
|
|
466
|
+
if (graphIssues.length > 0) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
`UI dependency graph has issues:
|
|
469
|
+
${graphIssues.map((i) => ` - ${i}`).join("\n")}`
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
const visited = /* @__PURE__ */ new Set();
|
|
473
|
+
const order = [];
|
|
474
|
+
function visit(id) {
|
|
475
|
+
if (visited.has(id)) return;
|
|
476
|
+
visited.add(id);
|
|
477
|
+
const entry = idToEntry.get(id);
|
|
478
|
+
const deps = entry?.registryDependencies ?? [];
|
|
479
|
+
for (const dep of deps) visit(dep);
|
|
480
|
+
order.push(id);
|
|
481
|
+
}
|
|
482
|
+
for (const id of requested) visit(id);
|
|
483
|
+
return order;
|
|
484
|
+
}
|
|
199
485
|
|
|
200
486
|
// src/loader.ts
|
|
201
487
|
async function loadVariantManifest(packageDir) {
|
|
@@ -225,6 +511,60 @@ async function loadVariantManifest(packageDir) {
|
|
|
225
511
|
}
|
|
226
512
|
return result.data;
|
|
227
513
|
}
|
|
514
|
+
async function loadSkillsPackageManifest(packageDir) {
|
|
515
|
+
const manifestPath = path.join(packageDir, "manifest.json");
|
|
516
|
+
let raw;
|
|
517
|
+
try {
|
|
518
|
+
raw = await fs.readFile(manifestPath, "utf-8");
|
|
519
|
+
} catch (err) {
|
|
520
|
+
const code = err.code;
|
|
521
|
+
if (code === "ENOENT") {
|
|
522
|
+
throw new Error(`Skills manifest not found: ${manifestPath}`);
|
|
523
|
+
}
|
|
524
|
+
throw new Error(
|
|
525
|
+
`Failed to read skills manifest at ${manifestPath}: ${err.message}`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
let data;
|
|
529
|
+
try {
|
|
530
|
+
data = JSON.parse(raw);
|
|
531
|
+
} catch {
|
|
532
|
+
throw new Error(`Invalid JSON in skills manifest at ${manifestPath}`);
|
|
533
|
+
}
|
|
534
|
+
const result = validateSkillsPackage(data);
|
|
535
|
+
if (!result.success) {
|
|
536
|
+
throw new Error(`${result.error}
|
|
537
|
+
File: ${manifestPath}`);
|
|
538
|
+
}
|
|
539
|
+
return result.data;
|
|
540
|
+
}
|
|
541
|
+
async function loadUiPackageManifest(packageDir) {
|
|
542
|
+
const manifestPath = path.join(packageDir, "manifest.json");
|
|
543
|
+
let raw;
|
|
544
|
+
try {
|
|
545
|
+
raw = await fs.readFile(manifestPath, "utf-8");
|
|
546
|
+
} catch (err) {
|
|
547
|
+
const code = err.code;
|
|
548
|
+
if (code === "ENOENT") {
|
|
549
|
+
throw new Error(`UI manifest not found: ${manifestPath}`);
|
|
550
|
+
}
|
|
551
|
+
throw new Error(
|
|
552
|
+
`Failed to read ui manifest at ${manifestPath}: ${err.message}`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
let data;
|
|
556
|
+
try {
|
|
557
|
+
data = JSON.parse(raw);
|
|
558
|
+
} catch {
|
|
559
|
+
throw new Error(`Invalid JSON in ui manifest at ${manifestPath}`);
|
|
560
|
+
}
|
|
561
|
+
const result = validateUiPackage(data);
|
|
562
|
+
if (!result.success) {
|
|
563
|
+
throw new Error(`${result.error}
|
|
564
|
+
File: ${manifestPath}`);
|
|
565
|
+
}
|
|
566
|
+
return result.data;
|
|
567
|
+
}
|
|
228
568
|
|
|
229
569
|
// src/managed-regions.ts
|
|
230
570
|
var REGION_PATTERN = /<!-- teamix-evo:managed:start id="([^"]+)" -->([\s\S]*?)<!-- teamix-evo:managed:end id="\1" -->/g;
|
|
@@ -233,15 +573,25 @@ var END_MARKER_PATTERN = /<!-- teamix-evo:managed:end id="([^"]+)" -->/g;
|
|
|
233
573
|
function parseManagedRegions(content) {
|
|
234
574
|
const startIds = /* @__PURE__ */ new Set();
|
|
235
575
|
const endIds = /* @__PURE__ */ new Set();
|
|
576
|
+
const startCounts = /* @__PURE__ */ new Map();
|
|
236
577
|
let match;
|
|
237
578
|
const startRe = new RegExp(START_MARKER_PATTERN.source, "g");
|
|
238
579
|
while ((match = startRe.exec(content)) !== null) {
|
|
239
|
-
|
|
580
|
+
const id = match[1];
|
|
581
|
+
startIds.add(id);
|
|
582
|
+
startCounts.set(id, (startCounts.get(id) ?? 0) + 1);
|
|
240
583
|
}
|
|
241
584
|
const endRe = new RegExp(END_MARKER_PATTERN.source, "g");
|
|
242
585
|
while ((match = endRe.exec(content)) !== null) {
|
|
243
586
|
endIds.add(match[1]);
|
|
244
587
|
}
|
|
588
|
+
for (const [id, count] of startCounts) {
|
|
589
|
+
if (count > 1) {
|
|
590
|
+
throw new Error(
|
|
591
|
+
`Duplicate managed region: found ${count} start markers for "${id}". Each id must be unique within a file.`
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
245
595
|
for (const id of startIds) {
|
|
246
596
|
if (!endIds.has(id)) {
|
|
247
597
|
throw new Error(
|
|
@@ -274,7 +624,9 @@ function replaceManagedRegion(content, id, newContent) {
|
|
|
274
624
|
const startMarker = `<!-- teamix-evo:managed:start id="${id}" -->`;
|
|
275
625
|
const endMarker = `<!-- teamix-evo:managed:end id="${id}" -->`;
|
|
276
626
|
const pattern = new RegExp(
|
|
277
|
-
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
627
|
+
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
628
|
+
id
|
|
629
|
+
)}" -->[\\s\\S]*?<!-- teamix-evo:managed:end id="${escapeRegExp(id)}" -->`,
|
|
278
630
|
"g"
|
|
279
631
|
);
|
|
280
632
|
if (!pattern.test(content)) {
|
|
@@ -290,7 +642,9 @@ ${endMarker}`
|
|
|
290
642
|
}
|
|
291
643
|
function hasManagedRegion(content, id) {
|
|
292
644
|
const pattern = new RegExp(
|
|
293
|
-
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
645
|
+
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
646
|
+
id
|
|
647
|
+
)}" -->[\\s\\S]*?<!-- teamix-evo:managed:end id="${escapeRegExp(id)}" -->`
|
|
294
648
|
);
|
|
295
649
|
return pattern.test(content);
|
|
296
650
|
}
|
|
@@ -341,16 +695,33 @@ function getUpdateAction(strategy, options) {
|
|
|
341
695
|
ProjectConfigSchema,
|
|
342
696
|
ResourceSchema,
|
|
343
697
|
ResourceTypeSchema,
|
|
698
|
+
SkillEntrySchema,
|
|
699
|
+
SkillIdeSchema,
|
|
700
|
+
SkillScopeSchema,
|
|
701
|
+
SkillsPackageManifestSchema,
|
|
702
|
+
TailwindVersionSchema,
|
|
703
|
+
UiAliasSchema,
|
|
704
|
+
UiAliasesSchema,
|
|
705
|
+
UiEntryFileSchema,
|
|
706
|
+
UiEntrySchema,
|
|
707
|
+
UiEntryTypeSchema,
|
|
708
|
+
UiPackageManifestSchema,
|
|
344
709
|
UpdateStrategySchema,
|
|
345
710
|
VariantManifestSchema,
|
|
346
711
|
getUpdateAction,
|
|
347
712
|
hasManagedRegion,
|
|
713
|
+
loadSkillsPackageManifest,
|
|
714
|
+
loadUiPackageManifest,
|
|
348
715
|
loadVariantManifest,
|
|
349
716
|
parseManagedRegions,
|
|
350
717
|
replaceManagedRegion,
|
|
718
|
+
resolveUiEntryOrder,
|
|
351
719
|
shouldUpdate,
|
|
352
720
|
validateConfig,
|
|
353
721
|
validateInstalled,
|
|
354
|
-
validateManifest
|
|
722
|
+
validateManifest,
|
|
723
|
+
validateSkillsPackage,
|
|
724
|
+
validateUiDependencyGraph,
|
|
725
|
+
validateUiPackage
|
|
355
726
|
});
|
|
356
727
|
//# sourceMappingURL=index.cjs.map
|