@aalis/schema-config 0.10.0 → 0.12.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/dist/index.d.ts +55 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +224 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,16 @@ export interface SchemaField {
|
|
|
39
39
|
label: string;
|
|
40
40
|
value: string | number;
|
|
41
41
|
}>;
|
|
42
|
+
/** number:数值下限(含),validateConfig 强制。无 default 的 number 表单不预填(留空=走服务端默认),带 min 仍建议声明 default 以免长期处于缺失告警 */
|
|
43
|
+
min?: number;
|
|
44
|
+
/** number:数值上限(含),validateConfig 强制 */
|
|
45
|
+
max?: number;
|
|
46
|
+
/** number:仅接受整数,validateConfig 强制 */
|
|
47
|
+
integer?: boolean;
|
|
48
|
+
/** number:步进——纯 UI 提示(浮点取模不可靠),validateConfig 不校验 */
|
|
49
|
+
step?: number;
|
|
50
|
+
/** string / textarea:正则约束(RegExp 源文本,test() 子串语义——要整串匹配请显式 ^…$),validateConfig 强制;模式本身非法则跳过该检查 */
|
|
51
|
+
pattern?: string;
|
|
42
52
|
}
|
|
43
53
|
export interface SchemaGroup {
|
|
44
54
|
label?: string;
|
|
@@ -77,8 +87,52 @@ export declare const CORE_CONFIG_SCHEMA: ConfigSchema;
|
|
|
77
87
|
* - SchemaGroup:递归 `fields`,总是产出嵌套对象(即使子字段全无默认值)
|
|
78
88
|
*/
|
|
79
89
|
export declare function defaultsFrom(schema: ConfigSchema | undefined): Record<string, unknown>;
|
|
90
|
+
/**
|
|
91
|
+
* validateConfig 发现的单条问题。path 以点号/下标定位(如 `server.port`、`hosts[2]`)。
|
|
92
|
+
* kind 区分两类性质不同的问题,供调用方分级处置:
|
|
93
|
+
* - `missing`:必填字段未配置——"没配全",半成品配置的正常中间态;
|
|
94
|
+
* - `invalid`:值与声明的类型/形状不符——"配错了",任何时候都不该写入。
|
|
95
|
+
*/
|
|
96
|
+
export interface SchemaIssue {
|
|
97
|
+
path: string;
|
|
98
|
+
message: string;
|
|
99
|
+
kind: 'missing' | 'invalid';
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 按 ConfigSchema 对配置做只读结构校验,返回问题清单(空数组 = 无问题)。
|
|
103
|
+
*
|
|
104
|
+
* 定位与边界:
|
|
105
|
+
* - **只读**:不转换、不回填、不裁剪——回填是 defaultsFrom,裁剪是 removeExtraFields。
|
|
106
|
+
* 校验器出错的最大代价必须始终是"少一条警告",因此它绝不参与配置取值链路。
|
|
107
|
+
* - **只解释中立词汇**:type/required 之外的语义(含宿主经 declaration merging
|
|
108
|
+
* 注入的呈现属性,如 secret/allowCustom)一概不解读。
|
|
109
|
+
* - **options 不作为取值白名单**:中立契约里 options 只是"静态选项"(呈现语义),
|
|
110
|
+
* 是否允许选项外的值由宿主属性(allowCustom)决定,而本包看不见宿主属性,
|
|
111
|
+
* 故 select/multiselect 只查原始类型不查成员资格。
|
|
112
|
+
* - **约束键**:number 强制 min/max(含边界)与 integer,string/textarea 强制
|
|
113
|
+
* pattern(模式非法则跳过该检查);step 是纯 UI 提示不校验。约束只在类型
|
|
114
|
+
* 检查通过后评估——类型都不对时报类型错,不叠报约束错。
|
|
115
|
+
* - **宽容缺失**:undefined 与 null(YAML 裸键)视为"未配置",仅在 required 且
|
|
116
|
+
* 未声明 default 时报缺——顶层/分组的默认值在调用点已合并,数组元素的默认值
|
|
117
|
+
* 不参与合并(defaultsFrom 把 SchemaArray 当叶子),靠 default 声明本身放行。
|
|
118
|
+
*
|
|
119
|
+
* 政策留给调用方:config-sync 对启用插件打 warn(不拒载,存量脏值不打死实例;
|
|
120
|
+
* 禁用插件的配置是休眠数据,不告警),webui 的 PUT 只拦**新增**的 `invalid`
|
|
121
|
+
* (存量问题与 `missing` 放行——拦新不追旧,半成品配置允许落盘)。
|
|
122
|
+
*/
|
|
123
|
+
export declare function validateConfig(schema: ConfigSchema | undefined, config: Record<string, unknown>): SchemaIssue[];
|
|
124
|
+
/**
|
|
125
|
+
* 按 schema 键集裁掉未知字段。configSchema 是插件配置的**唯一声明来源**(默认值也从它派生),
|
|
126
|
+
* 所以它的键集就是完整的白名单:不在 schema 里的字段,要么是用户手写的错别字,
|
|
127
|
+
* 要么是已废弃的旧字段,裁掉即归位。无 schema 的插件不裁(见调用方守卫)。
|
|
128
|
+
*
|
|
129
|
+
* 被裁的键写入 `removed`(带点号前缀)——静默裁剪会让「字段被吃掉」与「用户没配」不可分辨。
|
|
130
|
+
* `type: 'array'` 的值整段保留(数组元素结构由 validateConfig 解释,不在这里拆)。
|
|
131
|
+
*/
|
|
132
|
+
export declare function removeExtraFields(config: Record<string, unknown>, schema: Record<string, unknown>, removed?: string[], prefix?: string): Record<string, unknown>;
|
|
133
|
+
export declare function cloneConfigObject(obj: Record<string, unknown>): Record<string, unknown>;
|
|
80
134
|
declare module '@aalis/core' {
|
|
81
|
-
interface
|
|
135
|
+
interface PluginMeta {
|
|
82
136
|
/** 配置表单 Schema:插件配置的唯一声明来源(默认值经 defaultsFrom 派生)。 */
|
|
83
137
|
configSchema?: ConfigSchema;
|
|
84
138
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// @aalis/schema-config —
|
|
2
|
+
// @aalis/schema-config — 配置 Schema 词汇契约 + 词汇的中立解释
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// 类型部分描述"配置如何呈现为表单"(label / options / textarea …);
|
|
5
|
+
// 函数部分是对这套词汇的**中立解释**:默认值派生(defaultsFrom)、只读
|
|
6
|
+
// 结构校验(validateConfig)、按 schema 键集裁未知字段(removeExtraFields)。
|
|
7
|
+
// 三者都不属于 @aalis/core——core 只把 `PluginMeta.configSchema` 当作
|
|
8
|
+
// opaque 数据透传,不解释任何字段。
|
|
7
9
|
//
|
|
8
10
|
// 消费方:
|
|
9
|
-
// -
|
|
11
|
+
// - 插件:在 `definePlugin({ configSchema })` 的定义对象里声明
|
|
10
12
|
// - 渲染宿主(webui-server/client 等):读取并渲染表单
|
|
11
|
-
// - 宿主政策(@aalis/runtime 的 config-sync):按 schema
|
|
13
|
+
// - 宿主政策(@aalis/runtime 的 config-sync):按 schema 裁剪未知字段、校验告警
|
|
12
14
|
//
|
|
13
15
|
// 本包零运行时依赖:对 @aalis/core 仅有 type-only 锚点 import(编译后擦除)。
|
|
14
16
|
// ============================================================
|
|
@@ -51,12 +53,227 @@ export function defaultsFrom(schema) {
|
|
|
51
53
|
for (const [key, entry] of Object.entries(schema ?? {})) {
|
|
52
54
|
if (!entry || typeof entry !== 'object')
|
|
53
55
|
continue;
|
|
56
|
+
if (isUnsafeConfigKey(key))
|
|
57
|
+
continue;
|
|
54
58
|
if ('fields' in entry) {
|
|
55
59
|
out[key] = defaultsFrom(entry.fields);
|
|
56
60
|
continue;
|
|
57
61
|
}
|
|
58
62
|
if ('default' in entry)
|
|
59
|
-
out[key] = entry.default;
|
|
63
|
+
out[key] = cloneConfigValue(entry.default);
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
// 校验器在运行时需要一份"本包内置类型"名单:SchemaFieldTypes 是纯类型层的
|
|
68
|
+
// merging 注册表,运行时不存在。外来类型(如 'llm-ref')落在名单之外,
|
|
69
|
+
// 校验器一律跳过放行——开放词汇表要求校验器同样开放。
|
|
70
|
+
const NEUTRAL_FIELD_TYPES = new Set([
|
|
71
|
+
'string',
|
|
72
|
+
'number',
|
|
73
|
+
'boolean',
|
|
74
|
+
'select',
|
|
75
|
+
'multiselect',
|
|
76
|
+
'textarea',
|
|
77
|
+
]);
|
|
78
|
+
/**
|
|
79
|
+
* 按 ConfigSchema 对配置做只读结构校验,返回问题清单(空数组 = 无问题)。
|
|
80
|
+
*
|
|
81
|
+
* 定位与边界:
|
|
82
|
+
* - **只读**:不转换、不回填、不裁剪——回填是 defaultsFrom,裁剪是 removeExtraFields。
|
|
83
|
+
* 校验器出错的最大代价必须始终是"少一条警告",因此它绝不参与配置取值链路。
|
|
84
|
+
* - **只解释中立词汇**:type/required 之外的语义(含宿主经 declaration merging
|
|
85
|
+
* 注入的呈现属性,如 secret/allowCustom)一概不解读。
|
|
86
|
+
* - **options 不作为取值白名单**:中立契约里 options 只是"静态选项"(呈现语义),
|
|
87
|
+
* 是否允许选项外的值由宿主属性(allowCustom)决定,而本包看不见宿主属性,
|
|
88
|
+
* 故 select/multiselect 只查原始类型不查成员资格。
|
|
89
|
+
* - **约束键**:number 强制 min/max(含边界)与 integer,string/textarea 强制
|
|
90
|
+
* pattern(模式非法则跳过该检查);step 是纯 UI 提示不校验。约束只在类型
|
|
91
|
+
* 检查通过后评估——类型都不对时报类型错,不叠报约束错。
|
|
92
|
+
* - **宽容缺失**:undefined 与 null(YAML 裸键)视为"未配置",仅在 required 且
|
|
93
|
+
* 未声明 default 时报缺——顶层/分组的默认值在调用点已合并,数组元素的默认值
|
|
94
|
+
* 不参与合并(defaultsFrom 把 SchemaArray 当叶子),靠 default 声明本身放行。
|
|
95
|
+
*
|
|
96
|
+
* 政策留给调用方:config-sync 对启用插件打 warn(不拒载,存量脏值不打死实例;
|
|
97
|
+
* 禁用插件的配置是休眠数据,不告警),webui 的 PUT 只拦**新增**的 `invalid`
|
|
98
|
+
* (存量问题与 `missing` 放行——拦新不追旧,半成品配置允许落盘)。
|
|
99
|
+
*/
|
|
100
|
+
export function validateConfig(schema, config) {
|
|
101
|
+
const issues = [];
|
|
102
|
+
validateFields(schema ?? {}, config, '', issues);
|
|
103
|
+
return issues;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 按 schema 键集裁掉未知字段。configSchema 是插件配置的**唯一声明来源**(默认值也从它派生),
|
|
107
|
+
* 所以它的键集就是完整的白名单:不在 schema 里的字段,要么是用户手写的错别字,
|
|
108
|
+
* 要么是已废弃的旧字段,裁掉即归位。无 schema 的插件不裁(见调用方守卫)。
|
|
109
|
+
*
|
|
110
|
+
* 被裁的键写入 `removed`(带点号前缀)——静默裁剪会让「字段被吃掉」与「用户没配」不可分辨。
|
|
111
|
+
* `type: 'array'` 的值整段保留(数组元素结构由 validateConfig 解释,不在这里拆)。
|
|
112
|
+
*/
|
|
113
|
+
export function removeExtraFields(config, schema, removed, prefix = '') {
|
|
114
|
+
const result = {};
|
|
115
|
+
for (const [key, value] of Object.entries(config)) {
|
|
116
|
+
if (!(key in schema)) {
|
|
117
|
+
removed?.push(prefix + key);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const schemaDef = schema[key];
|
|
121
|
+
if (schemaDef.type === 'array') {
|
|
122
|
+
result[key] = value;
|
|
123
|
+
}
|
|
124
|
+
else if (schemaDef.fields &&
|
|
125
|
+
typeof schemaDef.fields === 'object' &&
|
|
126
|
+
value !== null &&
|
|
127
|
+
typeof value === 'object' &&
|
|
128
|
+
!Array.isArray(value)) {
|
|
129
|
+
result[key] = removeExtraFields(value, schemaDef.fields, removed, `${prefix + key}.`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
result[key] = value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
function validateFields(schema, config, prefix, issues) {
|
|
138
|
+
// `?? {}` 与 defaultsFrom 同款兜底:畸形 schema(array 缺 items / fields 为 null)
|
|
139
|
+
// 不许把"最多少一条警告"升级成 TypeError——校验器自身永远不能成为故障源。
|
|
140
|
+
for (const [key, entry] of Object.entries(schema ?? {})) {
|
|
141
|
+
if (!entry || typeof entry !== 'object')
|
|
142
|
+
continue;
|
|
143
|
+
const path = prefix + key;
|
|
144
|
+
const value = config[key];
|
|
145
|
+
if ('fields' in entry) {
|
|
146
|
+
if (value === undefined || value === null)
|
|
147
|
+
continue;
|
|
148
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
149
|
+
issues.push({ path, message: `期望对象(分组),得到 ${describeType(value)}`, kind: 'invalid' });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
validateFields(entry.fields, value, `${path}.`, issues);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (entry.type === 'array') {
|
|
156
|
+
if (value === undefined || value === null)
|
|
157
|
+
continue;
|
|
158
|
+
if (!Array.isArray(value)) {
|
|
159
|
+
issues.push({ path, message: `期望数组,得到 ${describeType(value)}`, kind: 'invalid' });
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
value.forEach((item, i) => {
|
|
163
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
164
|
+
issues.push({ path: `${path}[${i}]`, message: `期望对象元素,得到 ${describeType(item)}`, kind: 'invalid' });
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
validateFields(entry.items, item, `${path}[${i}].`, issues);
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (value === undefined || value === null) {
|
|
172
|
+
// 声明了 default 的字段永远不算缺失:顶层/分组的默认值在调用点已合并(此判恒假),
|
|
173
|
+
// 数组元素的默认值不参与任何合并(defaultsFrom 把 SchemaArray 当叶子),
|
|
174
|
+
// 全靠这里放行——否则 required+default 的 item 字段会误报(scheduler jobs[].platform 型)。
|
|
175
|
+
if (entry.required && !('default' in entry))
|
|
176
|
+
issues.push({ path, message: '必填字段缺失', kind: 'missing' });
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (!NEUTRAL_FIELD_TYPES.has(entry.type))
|
|
180
|
+
continue;
|
|
181
|
+
switch (entry.type) {
|
|
182
|
+
case 'string':
|
|
183
|
+
case 'textarea':
|
|
184
|
+
if (typeof value !== 'string') {
|
|
185
|
+
issues.push({ path, message: `期望 string,得到 ${describeType(value)}`, kind: 'invalid' });
|
|
186
|
+
}
|
|
187
|
+
else if (entry.pattern !== undefined) {
|
|
188
|
+
// 模式本身非法(作者的 schema bug)时跳过该检查——校验器不得因 schema
|
|
189
|
+
// 缺陷抛错或误伤值;作者侧问题由类型检查与测试兜。
|
|
190
|
+
try {
|
|
191
|
+
if (!new RegExp(entry.pattern).test(value)) {
|
|
192
|
+
issues.push({ path, message: `不匹配模式 ${entry.pattern}`, kind: 'invalid' });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
/* 非法 pattern:跳过 */
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
case 'number':
|
|
201
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
202
|
+
issues.push({ path, message: `期望有限数值,得到 ${describeType(value)}`, kind: 'invalid' });
|
|
203
|
+
}
|
|
204
|
+
else if (entry.integer && !Number.isInteger(value)) {
|
|
205
|
+
issues.push({ path, message: '期望整数', kind: 'invalid' });
|
|
206
|
+
}
|
|
207
|
+
else if (entry.min !== undefined && value < entry.min) {
|
|
208
|
+
issues.push({ path, message: `小于下限 ${entry.min}`, kind: 'invalid' });
|
|
209
|
+
}
|
|
210
|
+
else if (entry.max !== undefined && value > entry.max) {
|
|
211
|
+
issues.push({ path, message: `大于上限 ${entry.max}`, kind: 'invalid' });
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
214
|
+
case 'boolean':
|
|
215
|
+
if (typeof value !== 'boolean')
|
|
216
|
+
issues.push({ path, message: `期望 boolean,得到 ${describeType(value)}`, kind: 'invalid' });
|
|
217
|
+
break;
|
|
218
|
+
case 'select':
|
|
219
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
220
|
+
issues.push({ path, message: `期望 string 或 number,得到 ${describeType(value)}`, kind: 'invalid' });
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
case 'multiselect':
|
|
224
|
+
if (!Array.isArray(value)) {
|
|
225
|
+
issues.push({ path, message: `期望数组,得到 ${describeType(value)}`, kind: 'invalid' });
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
value.forEach((item, i) => {
|
|
229
|
+
if (typeof item !== 'string' && typeof item !== 'number') {
|
|
230
|
+
issues.push({
|
|
231
|
+
path: `${path}[${i}]`,
|
|
232
|
+
message: `期望 string 或 number 元素,得到 ${describeType(item)}`,
|
|
233
|
+
kind: 'invalid',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function describeType(value) {
|
|
243
|
+
if (value === null)
|
|
244
|
+
return 'null';
|
|
245
|
+
if (Array.isArray(value))
|
|
246
|
+
return 'array';
|
|
247
|
+
if (typeof value === 'number' && !Number.isFinite(value))
|
|
248
|
+
return String(value);
|
|
249
|
+
return typeof value;
|
|
250
|
+
}
|
|
251
|
+
// 与 packages/core/src/context/safe-keys.ts 同一规则(本包零运行时依赖,不能 import core)。
|
|
252
|
+
// 防漂移:test/architecture/config-copy-parity.test.ts
|
|
253
|
+
const UNSAFE_CONFIG_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
254
|
+
function isUnsafeConfigKey(key) {
|
|
255
|
+
return UNSAFE_CONFIG_KEYS.has(key);
|
|
256
|
+
}
|
|
257
|
+
function isPlainConfigObject(value) {
|
|
258
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
259
|
+
return false;
|
|
260
|
+
const proto = Object.getPrototypeOf(value);
|
|
261
|
+
return proto === Object.prototype || proto === null;
|
|
262
|
+
}
|
|
263
|
+
function cloneConfigValue(value) {
|
|
264
|
+
if (Array.isArray(value)) {
|
|
265
|
+
return value.map(item => (isPlainConfigObject(item) ? cloneConfigObject(item) : item));
|
|
266
|
+
}
|
|
267
|
+
if (isPlainConfigObject(value))
|
|
268
|
+
return cloneConfigObject(value);
|
|
269
|
+
return value;
|
|
270
|
+
}
|
|
271
|
+
export function cloneConfigObject(obj) {
|
|
272
|
+
const out = {};
|
|
273
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
274
|
+
if (isUnsafeConfigKey(key))
|
|
275
|
+
continue;
|
|
276
|
+
out[key] = cloneConfigValue(value);
|
|
60
277
|
}
|
|
61
278
|
return out;
|
|
62
279
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aalis/schema-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
],
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"typescript": "^5.7.0",
|
|
23
|
-
"@aalis/core": "0.
|
|
23
|
+
"@aalis/core": "0.17.0"
|
|
24
24
|
},
|
|
25
25
|
"aalis": {
|
|
26
26
|
"types": true
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"aalis-schema"
|
|
31
31
|
],
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@aalis/core": ">=0.
|
|
33
|
+
"@aalis/core": ">=0.17.0 <1.0.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsc",
|