@mengyuly/dsh-ponytail 0.1.4 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -3,6 +3,77 @@
3
3
  All notable changes to `@mengyuly/dsh-ponytail` are documented here.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.1.6] - 2026-08-24
7
+
8
+ ### Fixed
9
+
10
+ - 移除发行产物中的动态代码执行:`new Function` 的调用点来自内联进 bundle 的
11
+ schemastery(其 schema DSL 会把字符串 `callback` 编译成函数)。已把
12
+ `@deepseek-ai/schemastery` 从 bundle **外置**为已发布的 peer(与 cordis
13
+ 同等对待),发行 `lib/index.js` 不再包含任何 `new Function` / `eval`
14
+ (CI 的 `check-bundle` 现在断言外链集合精确为
15
+ `cordis + schemastery`,且产物零动态执行)。该调用点在本插件运行时路径上
16
+ 本不可达(只构造、不解析),外置是消除扫描告警的根治,也是更诚实的依赖声明。
17
+
18
+ ### Changed
19
+
20
+ - 运行时依赖表述更新:bundle 现在 import `@deepseek-ai/cordis` +
21
+ `@deepseek-ai/schemastery`(两者都已发布);`dsh-llm` / `dsh-skill` 仍内联
22
+ (npm 无兼容版本)。
23
+ - `@deepseek-ai/schemastery` 加入 peerDependencies(宿主兼容声明)。
24
+ - `verify-dist` 的运行时导出检查改用链式可调用 stub 加载 bundle
25
+ (schemastery 在模块加载时被急切构建 schema,但本插件路径不解析它们)。
26
+ - 移除只做类型检查、职责与 `sync:dist` 重叠的 `scripts/build.sh`。
27
+
28
+ ### Tests
29
+
30
+ - Ubuntu + Windows(CI 矩阵):check-bundle(外链白名单 + 零动态执行)、
31
+ verify:dist、verify:pack、test:consumer、test:regressions。
32
+ - tarball 安装 smoke 现在显式安装 cordis + schemastery 两个运行时 peer。
33
+
34
+ ## [0.1.5] - 2026-08-24
35
+
36
+ ### Fixed
37
+
38
+ - Windows 下验证脚本无法启动 `npm.cmd`:`spawnSync('npm', …)` 在 Windows 上对
39
+ `.cmd` 无效(`status === null`)。现在统一走 `process.execPath + npm_execpath`
40
+ (npm script 环境内为 npm 自身 CLI,跨平台可靠),并在 `npm_execpath` 缺失或
41
+ 指向其他包管理器(pnpm/yarn/bun shim)时回退到 `npm`/`npm.cmd`。
42
+ - `spawnSync` 启动失败时错误信息丢失:统一 `formatSpawnFailure` 输出 command、
43
+ args、cwd、status、signal、`result.error.message`、stdout/stderr 尾部
44
+ (各 4KB 上限);`status === null` 明确判为启动失败或被信号终止,不再显示为
45
+ 普通退出码。
46
+ - `verify-dist` 不再有未定义 `existsSync` 的潜伏分支:改为**直接拒绝**任何
47
+ `sourceMappingURL`(与 v0.1.4 起 `declarationMap: false` 的发布策略一致),
48
+ 并新增回归测试证明声明文件重现 source map 时验证会明确失败。
49
+ - 删除 CI 中 checkout 后立即执行的无效 `git diff --exit-code -- lib` 步骤
50
+ (工作区天然干净,证明不了任何事);防漂移表述与真实能力对齐。
51
+
52
+ ### Changed
53
+
54
+ - `verify-pack` / `test-consumer` 共用 `scripts/lib/run-command.mjs` 跨平台进程
55
+ 工具,不再各自维护一套 `spawnSync('npm', …)`。
56
+ - `verify-pack` 如实报告实际安装的依赖(npm 会解析全部声明 peers,包括宿主
57
+ 契约 peers;bundle 运行时只 import `@deepseek-ai/cordis` 由独立外链检查证明)。
58
+ - CI 增加 Windows Runner(`windows-latest` + `ubuntu-latest` 矩阵,Node 24,
59
+ 不允许跳过或 continue-on-error)。
60
+ - `sync:dist` 生成 `dist-provenance.json`:`sourceCommit` 来自权威 checkout 的
61
+ `git rev-parse HEAD`(真实 SHA,不手工填写),工具链版本取自 checkout 的
62
+ node_modules,不含本机绝对路径;`verify:dist` 校验其格式。
63
+ - tarball 现在包含 `dist-provenance.json`。
64
+ - 产物一致性检查命名与真实能力一致:`verify:dist` 是静态一致性检查(src/d.ts
65
+ 导出、关键签名、主入口运行时导出、无 source map、provenance),不是与权威
66
+ 构建的字节级等价证明。
67
+
68
+ ### Tests
69
+
70
+ - Ubuntu:verify:dist / verify:pack / test:consumer / test:regressions 全部通过。
71
+ - Windows:CI 矩阵真实执行 verify:dist / verify:pack / test:consumer。
72
+ - npm tarball smoke(仅声明依赖安装后加载)。
73
+ - NodeNext + skipLibCheck:false consumer。
74
+ - 子进程启动失败诊断回归测试。
75
+ - declaration source map 回归测试。
76
+
6
77
  ## [0.1.4] - 2026-08-24
7
78
 
8
79
  ### Fixed
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # dsh-ponytail
2
2
 
3
3
  ![CI](https://github.com/MengYuil/dsh-ponytail/actions/workflows/ci.yml/badge.svg)
4
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
5
+ [![npm](https://img.shields.io/npm/v/@mengyuly/dsh-ponytail)](https://www.npmjs.com/package/@mengyuly/dsh-ponytail)
4
6
 
5
7
  把 [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)(「懒惰资深开发者」最少代码心智)移植成 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 原生插件。功能与效率与上游一致:7 级阶梯规则集每轮注入、强度切换、`/ponytail-*` 斜杠命令。
6
8
 
@@ -24,9 +26,9 @@ dsh plugin --profile web add @mengyuly/dsh-ponytail
24
26
 
25
27
  装完重启 profile 生效(`dsh web` / `dsh tui`)。装载完成后,会话技能目录里会出现 6 个 `ponytail*` 技能,发 `/ponytail-help` 立即验证。
26
28
 
27
- > `lib/index.js` 是自包含 bundle(已内联 `dsh-llm` / `dsh-skill`),运行时只依赖 `@deepseek-ai/cordis` peer(registry 有 4.0.1),所以 GitHub / tgz / npm 三种安装方式都不需要 dsh 源码树。
29
+ > `lib/index.js` 是自包含 bundle(已内联 `dsh-llm` / `dsh-skill`——npm 无兼容版本),运行时依赖两个已发布的 peer:`@deepseek-ai/cordis`(4.0.1)与 `@deepseek-ai/schemastery`(3.18.x)。`schemastery` 刻意保持外置而非内联:其 schema DSL 用 `new Function` 编译 `callback` 字符串,外置后**发行产物不含任何动态代码执行**(CI 有专门检查)。GitHub / tgz / npm 三种安装方式都不需要 dsh 源码树。
28
30
 
29
- > 说明:`src/` 是源码、`lib/` 是预构建产物(开箱即可加载,无需编译)。源码主仓在 deepseek-harness 的 `packages/community/ponytail`,改源码后回主仓重建,再把 `lib/` 同步回本仓库即可发版。本地改 `src/` 想快速验证编译,可用 `DSH_CHECKOUT=/path/to/deepseek-harness ./scripts/build.sh`(只做类型检查)。
31
+ > 说明:`src/` 是源码、`lib/` 是预构建产物(开箱即可加载,无需编译)。源码主仓在 deepseek-harness 的 `packages/community/ponytail`;改源码后用 `DSH_CHECKOUT=/path/to/deepseek-harness npm run sync:dist` 重建并同步完整 `lib/`(见下「发行维护」)。
30
32
 
31
33
  ## 功能
32
34
 
@@ -62,8 +64,8 @@ dsh plugin --profile web add @mengyuly/dsh-ponytail
62
64
 
63
65
  ## 测试环境与权威关系
64
66
 
65
- - Node.js **v24.16.0** 在本机 deepseek-harness checkout 上测试(`tsc -b` + `tsdown` host 构建通过)。与之精确匹配的已发布 DSH/Cordis 版本**待确认**——checkout 是预发布工作树,非发布 tag。
66
- - 权威源码在 deepseek-harness monorepo 的 `packages/community/ponytail`(`@deepseek-ai/dsh-ponytail`);本仓库(`@mengyuly/dsh-ponytail`)是**发行镜像**:随包附构建产物,`scripts/build.sh` 只做源码类型检查,不是独立真源。
67
+ - 本机(Linux,Node.js **v24.16.0**,deepseek-harness checkout 构建)与 CI 矩阵(**ubuntu-latest + windows-latest**,Node 24)上验证通过。与之精确匹配的已发布 DSH/Cordis 版本**待确认**——checkout 是预发布工作树,非发布 tag。
68
+ - 权威源码在 deepseek-harness monorepo 的 `packages/community/ponytail`(`@deepseek-ai/dsh-ponytail`);本仓库(`@mengyuly/dsh-ponytail`)是**发行镜像**:随包附构建产物,不是独立真源。
67
69
 
68
70
  ## 发行维护
69
71
 
@@ -72,14 +74,17 @@ dsh plugin --profile web add @mengyuly/dsh-ponytail
72
74
  ```bash
73
75
  DSH_CHECKOUT=/path/to/deepseek-harness npm run sync:dist
74
76
  ```
75
- 该命令在权威 checkout 中重建(`tsc` 生成声明 + `tsdown` 打包运行时),同步 `lib/index.js`、`lib/invariant.js`、`lib/types/*.d.ts`,并自动执行一致性校验;`lib/` 有变化时会提示提交。
76
- - **验证命令**:
77
+ 该命令在权威 checkout 中重建(`tsc` 生成声明 + `tsdown` 打包运行时),同步 `lib/index.js`、`lib/invariant.js`、`lib/types/*.d.ts`,生成 `dist-provenance.json`(记录权威 checkout 的真实 commit SHA 与工具链版本),并自动执行一致性校验;产物有变化时会提示提交。**完整构建一致性由本命令在发布流程中完成——发行镜像 CI 不会重新构建权威 monorepo。**
78
+ - **验证命令**(Linux / Windows 通用,跨平台进程调用见 `scripts/lib/run-command.mjs`):
77
79
  ```bash
78
- npm run verify:dist # 声明↔src↔运行时导出一致性、关键签名、无悬空 source map
79
- npm run verify:pack # tarball 内容、版本一致、仅 peer 安装后 smoke
80
+ npm run verify:dist # 静态一致性:src/d.ts 导出一致、关键签名、主入口运行时导出、无 source map、provenance 合法
81
+ npm run verify:pack # tarball 内容/版本、安装后 smoke(如实报告实际安装的依赖)
80
82
  npm run test:consumer # NodeNext + skipLibCheck:false 的声明消费测试(对打包产物)
83
+ npm run test:regressions # 验证工具自身的回归测试(source map 策略、provenance、spawn 诊断)
81
84
  ```
82
- - CI 对已提交产物执行上述全部校验;src 新增导出而 d.ts 未同步、或产物漂移都会失败。
85
+ - **CI 能力边界(如实)**:CI(ubuntu + windows 矩阵)执行上述静态验证与打包/消费测试,但**不重新构建权威 monorepo**;`verify:dist` 是导出表面/签名/运行时导出的一致性检查,**不是**与权威构建的字节级等价证明——后者由 `sync:dist` 在发布流程中保证。
86
+ - `dist-provenance.json` 随 npm 包发布,便于审计构建来源。
87
+ - 本机验证时若 `npm_execpath` 指向其他包管理器(如 pnpm/yarn shim),脚本会自动回退到 PATH 上的 `npm`;临时目录失败时保留需设 `PONYTAIL_VERIFY_KEEP_TEMP=1`。
83
88
 
84
89
  ## 许可
85
90
 
@@ -0,0 +1,10 @@
1
+ {
2
+ "sourceRepository": "https://github.com/deepseek-ai/deepseek-harness.git",
3
+ "sourceCommit": "b150a551b8d465e31e418e1b2eaf5e79bbb7d28e",
4
+ "sourcePackage": "packages/community/ponytail",
5
+ "generatedBy": {
6
+ "node": "v24.16.0",
7
+ "typescript": "6.0.3",
8
+ "tsdown": "0.22.2"
9
+ }
10
+ }
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { Service } from "@deepseek-ai/cordis";
3
+ import z from "@deepseek-ai/schemastery";
3
4
  import { mkdirSync, readFileSync, renameSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
4
5
  import { homedir } from "node:os";
5
6
  import { dirname, join } from "node:path";
@@ -93,798 +94,6 @@ function createUserMessage(input) {
93
94
  });
94
95
  }
95
96
  //#endregion
96
- //#region ../../../vendor/cosmokit/src/misc.ts
97
- /** Return true when a value is `null` or `undefined`. */
98
- function isNullable(value) {
99
- return value === null || value === void 0;
100
- }
101
- /** Return true for non-array object values. */
102
- function isPlainObject(data) {
103
- return data && typeof data === "object" && !Array.isArray(data);
104
- }
105
- /** Filter object entries and return a new object. */
106
- function filterKeys(object, filter) {
107
- return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
108
- }
109
- /** Map object values while preserving the original key set. */
110
- function mapValues(object, transform) {
111
- return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
112
- }
113
- /** Pick selected keys from an object, optionally including `undefined` values. */
114
- function pick(source, keys, forced) {
115
- if (!keys) return { ...source };
116
- const result = {};
117
- for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
118
- return result;
119
- }
120
- //#endregion
121
- //#region ../../../vendor/cosmokit/src/types.ts
122
- /** Test values using `instanceof` with a `toStringTag` fallback. */
123
- function is(type, value) {
124
- if (arguments.length === 1) return (value) => is(type, value);
125
- return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
126
- }
127
- function isArrayBufferLike(value) {
128
- return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
129
- }
130
- function isArrayBufferSource(value) {
131
- return isArrayBufferLike(value) || ArrayBuffer.isView(value);
132
- }
133
- let Binary;
134
- (function(_Binary) {
135
- _Binary.is = isArrayBufferLike;
136
- _Binary.isSource = isArrayBufferSource;
137
- function fromSource(source) {
138
- if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
139
- else return source;
140
- }
141
- _Binary.fromSource = fromSource;
142
- function toBase64(source) {
143
- source = fromSource(source);
144
- if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
145
- let binary = "";
146
- const bytes = new Uint8Array(source);
147
- for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
148
- return btoa(binary);
149
- }
150
- _Binary.toBase64 = toBase64;
151
- function fromBase64(source) {
152
- if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
153
- return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
154
- }
155
- _Binary.fromBase64 = fromBase64;
156
- function toHex(source) {
157
- source = fromSource(source);
158
- if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
159
- return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
160
- }
161
- _Binary.toHex = toHex;
162
- function fromHex(source) {
163
- if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
164
- const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
165
- const buffer = [];
166
- for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
167
- return Uint8Array.from(buffer).buffer;
168
- }
169
- _Binary.fromHex = fromHex;
170
- })(Binary || (Binary = {}));
171
- Binary.fromBase64;
172
- Binary.toBase64;
173
- Binary.fromHex;
174
- Binary.toHex;
175
- /** Deep-clone common JavaScript values while preserving prototypes and cycles. */
176
- function clone(source, refs = /* @__PURE__ */ new Map()) {
177
- if (!source || typeof source !== "object") return source;
178
- if (is("Date", source)) return new Date(source.valueOf());
179
- if (is("RegExp", source)) return new RegExp(source.source, source.flags);
180
- if (isArrayBufferLike(source)) return source.slice(0);
181
- if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
182
- const cached = refs.get(source);
183
- if (cached) return cached;
184
- if (Array.isArray(source)) {
185
- const result = [];
186
- refs.set(source, result);
187
- source.forEach((value, index) => {
188
- result[index] = Reflect.apply(clone, null, [value, refs]);
189
- });
190
- return result;
191
- }
192
- const result = Object.create(Object.getPrototypeOf(source));
193
- refs.set(source, result);
194
- for (const key of Reflect.ownKeys(source)) {
195
- const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
196
- if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
197
- Reflect.defineProperty(result, key, descriptor);
198
- }
199
- return result;
200
- }
201
- /** Deeply compare arrays, dates, regexps, buffers, and plain object fields. */
202
- function deepEqual(a, b, strict) {
203
- if (a === b) return true;
204
- if (!strict && isNullable(a) && isNullable(b)) return true;
205
- if (typeof a !== typeof b) return false;
206
- if (typeof a !== "object") return false;
207
- if (!a || !b) return false;
208
- function check(test, then) {
209
- return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
210
- }
211
- return check(Array.isArray, (a, b) => a.length === b.length && a.every((item, index) => deepEqual(item, b[index]))) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
212
- if (a.byteLength !== b.byteLength) return false;
213
- const viewA = new Uint8Array(a);
214
- const viewB = new Uint8Array(b);
215
- for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
216
- return true;
217
- }) ?? Object.keys({
218
- ...a,
219
- ...b
220
- }).every((key) => deepEqual(a[key], b[key], strict));
221
- }
222
- //#endregion
223
- //#region ../../../vendor/cosmokit/src/time.ts
224
- let Time;
225
- (function(_Time) {
226
- _Time.millisecond = 1;
227
- const second = _Time.second = 1e3;
228
- const minute = _Time.minute = second * 60;
229
- const hour = _Time.hour = minute * 60;
230
- const day = _Time.day = hour * 24;
231
- const week = _Time.week = day * 7;
232
- let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
233
- function setTimezoneOffset(offset) {
234
- timezoneOffset = offset;
235
- }
236
- _Time.setTimezoneOffset = setTimezoneOffset;
237
- function getTimezoneOffset() {
238
- return timezoneOffset;
239
- }
240
- _Time.getTimezoneOffset = getTimezoneOffset;
241
- function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
242
- if (typeof date === "number") date = new Date(date);
243
- if (offset === void 0) offset = timezoneOffset;
244
- return Math.floor((date.valueOf() / minute - offset) / 1440);
245
- }
246
- _Time.getDateNumber = getDateNumber;
247
- function fromDateNumber(value, offset) {
248
- const date = new Date(value * day);
249
- if (offset === void 0) offset = timezoneOffset;
250
- return new Date(+date + offset * minute);
251
- }
252
- _Time.fromDateNumber = fromDateNumber;
253
- const numeric = /\d+(?:\.\d+)?/.source;
254
- const timeRegExp = new RegExp(`^${[
255
- "w(?:eek(?:s)?)?",
256
- "d(?:ay(?:s)?)?",
257
- "h(?:our(?:s)?)?",
258
- "m(?:in(?:ute)?(?:s)?)?",
259
- "s(?:ec(?:ond)?(?:s)?)?"
260
- ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
261
- function parseTime(source) {
262
- const capture = timeRegExp.exec(source);
263
- if (!capture) return 0;
264
- return (parseFloat(capture[1]) * week || 0) + (parseFloat(capture[2]) * day || 0) + (parseFloat(capture[3]) * hour || 0) + (parseFloat(capture[4]) * minute || 0) + (parseFloat(capture[5]) * second || 0);
265
- }
266
- _Time.parseTime = parseTime;
267
- function parseDate(date) {
268
- const parsed = parseTime(date);
269
- if (parsed) date = Date.now() + parsed;
270
- else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
271
- else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
272
- return date ? new Date(date) : /* @__PURE__ */ new Date();
273
- }
274
- _Time.parseDate = parseDate;
275
- function format(ms) {
276
- const abs = Math.abs(ms);
277
- if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
278
- else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
279
- else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
280
- else if (abs >= second) return Math.round(ms / second) + "s";
281
- return ms + "ms";
282
- }
283
- _Time.format = format;
284
- function toDigits(source, length = 2) {
285
- return source.toString().padStart(length, "0");
286
- }
287
- _Time.toDigits = toDigits;
288
- function template(template, time = /* @__PURE__ */ new Date()) {
289
- return template.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
290
- }
291
- _Time.template = template;
292
- })(Time || (Time = {}));
293
- //#endregion
294
- //#region ../../../vendor/schemastery/src/index.ts
295
- const kSchema = Symbol.for("schemastery");
296
- const kValidationError = Symbol.for("ValidationError");
297
- globalThis.__schemastery_index__ ??= 0;
298
- globalThis.__schemastery_refs__ = void 0;
299
- var ValidationError = class extends TypeError {
300
- options;
301
- name = "ValidationError";
302
- constructor(message, options) {
303
- let prefix = "$";
304
- for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
305
- else if (typeof segment === "number") prefix += "[" + segment + "]";
306
- else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
307
- if (prefix.startsWith(".")) prefix = prefix.slice(1);
308
- super((prefix === "$" ? "" : `${prefix} `) + message);
309
- this.options = options;
310
- }
311
- static is(error) {
312
- return !!error?.[kValidationError];
313
- }
314
- };
315
- Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
316
- const Schema = function(options) {
317
- const schema = function(data, options = {}) {
318
- return Schema.resolve(data, schema, options)[0];
319
- };
320
- if (options.refs) {
321
- const refs = mapValues(options.refs, (options) => new Schema(options));
322
- const getRef = (uid) => refs[uid];
323
- for (const key in refs) {
324
- const options = refs[key];
325
- options.sKey = getRef(options.sKey);
326
- options.inner = getRef(options.inner);
327
- options.list = options.list && options.list.map(getRef);
328
- options.dict = options.dict && mapValues(options.dict, getRef);
329
- }
330
- return refs[options.uid];
331
- }
332
- Object.assign(schema, options);
333
- if (typeof schema.callback === "string") try {
334
- schema.callback = new Function("return " + schema.callback)();
335
- } catch {}
336
- Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
337
- Object.setPrototypeOf(schema, Schema.prototype);
338
- schema.meta ||= {};
339
- schema.toString = schema.toString.bind(schema);
340
- return schema;
341
- };
342
- Schema.prototype = Object.create(Function.prototype);
343
- Schema.prototype[kSchema] = true;
344
- Object.defineProperty(Schema.prototype, "~standard", { get() {
345
- return {
346
- version: 1,
347
- vendor: "schemastery",
348
- validate: (value) => {
349
- try {
350
- return { value: Schema.resolve(value, this, {})[0] };
351
- } catch (error) {
352
- if (ValidationError.is(error)) return { issues: [{
353
- message: error.message,
354
- path: error.options.path
355
- }] };
356
- throw error;
357
- }
358
- }
359
- };
360
- } });
361
- Schema.ValidationError = ValidationError;
362
- Schema.prototype.toJSON = function toJSON() {
363
- if (globalThis.__schemastery_refs__) {
364
- globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
365
- return this.uid;
366
- }
367
- globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
368
- globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
369
- const result = {
370
- uid: this.uid,
371
- refs: globalThis.__schemastery_refs__
372
- };
373
- globalThis.__schemastery_refs__ = void 0;
374
- return result;
375
- };
376
- Schema.prototype.set = function set(key, value) {
377
- this.dict[key] = value;
378
- return this;
379
- };
380
- Schema.prototype.push = function push(value) {
381
- this.list.push(value);
382
- return this;
383
- };
384
- function mergeDesc(original, messages) {
385
- const result = typeof original === "string" ? { "": original } : { ...original };
386
- for (const locale in messages) {
387
- const value = messages[locale];
388
- if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
389
- else if (typeof value === "string") result[locale] = value;
390
- }
391
- return result;
392
- }
393
- function getInner(value) {
394
- return value?.$value ?? value?.$inner;
395
- }
396
- function extractKeys(data) {
397
- return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
398
- }
399
- Schema.prototype.i18n = function i18n(messages) {
400
- const schema = Schema(this);
401
- const desc = mergeDesc(schema.meta.description, messages);
402
- if (Object.keys(desc).length) schema.meta.description = desc;
403
- if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
404
- return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
405
- });
406
- if (schema.list) schema.list = schema.list.map((inner, index) => {
407
- return inner.i18n(mapValues(messages, (data = {}) => {
408
- if (Array.isArray(getInner(data))) return getInner(data)[index];
409
- if (Array.isArray(data)) return data[index];
410
- return extractKeys(data);
411
- }));
412
- });
413
- if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
414
- if (getInner(data)) return getInner(data);
415
- return extractKeys(data);
416
- }));
417
- if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
418
- return schema;
419
- };
420
- Schema.prototype.extra = function extra(key, value) {
421
- const schema = Schema(this);
422
- schema.meta = {
423
- ...schema.meta,
424
- [key]: value
425
- };
426
- return schema;
427
- };
428
- for (const key of [
429
- "required",
430
- "disabled",
431
- "collapse",
432
- "hidden",
433
- "loose"
434
- ]) Object.assign(Schema.prototype, { [key](value = true) {
435
- const schema = Schema(this);
436
- schema.meta = {
437
- ...schema.meta,
438
- [key]: value
439
- };
440
- return schema;
441
- } });
442
- Schema.prototype.deprecated = function deprecated() {
443
- const schema = Schema(this);
444
- schema.meta.badges ||= [];
445
- schema.meta.badges.push({
446
- text: "deprecated",
447
- type: "danger"
448
- });
449
- return schema;
450
- };
451
- Schema.prototype.experimental = function experimental() {
452
- const schema = Schema(this);
453
- schema.meta.badges ||= [];
454
- schema.meta.badges.push({
455
- text: "experimental",
456
- type: "warning"
457
- });
458
- return schema;
459
- };
460
- Schema.prototype.pattern = function pattern(regexp) {
461
- const schema = Schema(this);
462
- const pattern = pick(regexp, ["source", "flags"]);
463
- schema.meta = {
464
- ...schema.meta,
465
- pattern
466
- };
467
- return schema;
468
- };
469
- Schema.prototype.simplify = function simplify(value) {
470
- if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
471
- if (isNullable(value)) return value;
472
- if (this.type === "object" || this.type === "dict") {
473
- const result = {};
474
- for (const key in value) {
475
- const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
476
- if (this.type === "dict" || !isNullable(item)) result[key] = item;
477
- }
478
- if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
479
- return result;
480
- } else if (this.type === "array" || this.type === "tuple") {
481
- const result = [];
482
- value.forEach((value, index) => {
483
- const schema = this.type === "array" ? this.inner : this.list[index];
484
- const item = schema ? schema.simplify(value) : value;
485
- result.push(item);
486
- });
487
- return result;
488
- } else if (this.type === "intersect") {
489
- const result = {};
490
- for (const item of this.list) Object.assign(result, item.simplify(value));
491
- return result;
492
- } else if (this.type === "union") for (const schema of this.list) try {
493
- Schema.resolve(value, schema, {});
494
- return schema.simplify(value);
495
- } catch {}
496
- return value;
497
- };
498
- Schema.prototype.toString = function toString(inline) {
499
- return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
500
- };
501
- Schema.prototype.role = function role(role, extra) {
502
- const schema = Schema(this);
503
- schema.meta = {
504
- ...schema.meta,
505
- role,
506
- extra
507
- };
508
- return schema;
509
- };
510
- for (const key of [
511
- "default",
512
- "link",
513
- "comment",
514
- "description",
515
- "max",
516
- "min",
517
- "step"
518
- ]) Object.assign(Schema.prototype, { [key](value) {
519
- const schema = Schema(this);
520
- schema.meta = {
521
- ...schema.meta,
522
- [key]: value
523
- };
524
- return schema;
525
- } });
526
- const resolvers = {};
527
- Schema.extend = function extend(type, resolve) {
528
- resolvers[type] = resolve;
529
- };
530
- Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
531
- if (!schema) return [data];
532
- if (options.ignore?.(data, schema)) return [data];
533
- if (isNullable(data) && schema.type !== "lazy") {
534
- if (schema.meta.required) throw new ValidationError(`missing required value`, options);
535
- let current = schema;
536
- let fallback = schema.meta.default;
537
- while (current?.type === "intersect" && isNullable(fallback)) {
538
- current = current.list[0];
539
- fallback = current?.meta.default;
540
- }
541
- if (isNullable(fallback)) return [data];
542
- data = clone(fallback);
543
- }
544
- const callback = resolvers[schema.type];
545
- if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
546
- try {
547
- return callback(data, schema, options, strict);
548
- } catch (error) {
549
- if (!schema.meta.loose) throw error;
550
- return [schema.meta.default];
551
- }
552
- };
553
- Schema.from = function from(source) {
554
- if (isNullable(source)) return Schema.any();
555
- else if ([
556
- "string",
557
- "number",
558
- "boolean"
559
- ].includes(typeof source)) return Schema.const(source).required();
560
- else if (source[kSchema]) return source;
561
- else if (typeof source === "function") switch (source) {
562
- case String: return Schema.string().required();
563
- case Number: return Schema.number().required();
564
- case Boolean: return Schema.boolean().required();
565
- case Function: return Schema.function().required();
566
- default: return Schema.is(source).required();
567
- }
568
- else throw new TypeError(`cannot infer schema from ${source}`);
569
- };
570
- Schema.lazy = function lazy(builder) {
571
- const toJSON = () => {
572
- if (!schema.inner[kSchema]) {
573
- schema.inner = schema.builder();
574
- schema.inner.meta = {
575
- ...schema.meta,
576
- ...schema.inner.meta
577
- };
578
- }
579
- return schema.inner.toJSON();
580
- };
581
- const schema = new Schema({
582
- type: "lazy",
583
- builder,
584
- inner: { toJSON }
585
- });
586
- return schema;
587
- };
588
- Schema.natural = function natural() {
589
- return Schema.number().step(1).min(0);
590
- };
591
- Schema.percent = function percent() {
592
- return Schema.number().step(.01).min(0).max(1).role("slider");
593
- };
594
- Schema.date = function date() {
595
- return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
596
- const date = new Date(value);
597
- if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
598
- return date;
599
- }, true)]);
600
- };
601
- Schema.regExp = function regExp(flag = "") {
602
- return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
603
- try {
604
- return new RegExp(value, flag);
605
- } catch (e) {
606
- throw new ValidationError(e.message, options);
607
- }
608
- }, true)]);
609
- };
610
- Schema.arrayBuffer = function arrayBuffer(encoding) {
611
- return Schema.union([
612
- Schema.is(ArrayBuffer),
613
- Schema.is(SharedArrayBuffer),
614
- Schema.transform(Schema.any(), (value, options) => {
615
- if (Binary.isSource(value)) return Binary.fromSource(value);
616
- throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
617
- }, true),
618
- ...encoding ? [Schema.transform(Schema.string(), (value, options) => {
619
- try {
620
- return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
621
- } catch (e) {
622
- throw new ValidationError(e.message, options);
623
- }
624
- }, true)] : []
625
- ]);
626
- };
627
- Schema.extend("lazy", (data, schema, options, strict) => {
628
- if (!schema.inner[kSchema]) {
629
- schema.inner = schema.builder();
630
- schema.inner.meta = {
631
- ...schema.meta,
632
- ...schema.inner.meta
633
- };
634
- }
635
- return Schema.resolve(data, schema.inner, options, strict);
636
- });
637
- Schema.extend("any", (data) => {
638
- return [data];
639
- });
640
- Schema.extend("never", (data, _, options) => {
641
- throw new ValidationError(`expected nullable but got ${data}`, options);
642
- });
643
- Schema.extend("const", (data, { value }, options) => {
644
- if (deepEqual(data, value)) return [value];
645
- throw new ValidationError(`expected ${value} but got ${data}`, options);
646
- });
647
- function checkWithinRange(data, meta, description, options, skipMin = false) {
648
- const { max = Infinity, min = -Infinity } = meta;
649
- if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
650
- if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
651
- }
652
- Schema.extend("string", (data, { meta }, options) => {
653
- if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
654
- if (meta.pattern) {
655
- const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
656
- if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
657
- }
658
- checkWithinRange(data.length, meta, "string length", options);
659
- return [data];
660
- });
661
- function decimalShift(data, digits) {
662
- const str = data.toString();
663
- if (str.includes("e")) return data * Math.pow(10, digits);
664
- const index = str.indexOf(".");
665
- if (index === -1) return data * Math.pow(10, digits);
666
- const frac = str.slice(index + 1);
667
- const integer = str.slice(0, index);
668
- if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
669
- return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
670
- }
671
- function isMultipleOf(data, min, step) {
672
- step = Math.abs(step);
673
- if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
674
- const index = step.toString().indexOf(".");
675
- const digits = step.toString().slice(index + 1).length;
676
- return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
677
- }
678
- Schema.extend("number", (data, { meta }, options) => {
679
- if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
680
- checkWithinRange(data, meta, "number", options);
681
- const { step } = meta;
682
- if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
683
- return [data];
684
- });
685
- Schema.extend("boolean", (data, _, options) => {
686
- if (typeof data === "boolean") return [data];
687
- throw new ValidationError(`expected boolean but got ${data}`, options);
688
- });
689
- Schema.extend("bitset", (data, { bits, meta }, options) => {
690
- let value = 0, keys = [];
691
- if (typeof data === "number") {
692
- value = data;
693
- for (const key in bits) if (data & bits[key]) keys.push(key);
694
- } else if (Array.isArray(data)) {
695
- keys = data;
696
- for (const key of keys) {
697
- if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
698
- if (key in bits) value |= bits[key];
699
- }
700
- } else throw new ValidationError(`expected number or array but got ${data}`, options);
701
- if (value === meta.default) return [value];
702
- return [value, keys];
703
- });
704
- Schema.extend("function", (data, _, options) => {
705
- if (typeof data === "function") return [data];
706
- throw new ValidationError(`expected function but got ${data}`, options);
707
- });
708
- Schema.extend("is", (data, { constructor }, options) => {
709
- if (typeof constructor === "function") {
710
- if (data instanceof constructor) return [data];
711
- throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
712
- } else {
713
- if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
714
- let prototype = Object.getPrototypeOf(data);
715
- while (prototype) {
716
- if (prototype.constructor?.name === constructor) return [data];
717
- prototype = Object.getPrototypeOf(prototype);
718
- }
719
- throw new ValidationError(`expected ${constructor} but got ${data}`, options);
720
- }
721
- });
722
- function property(data, key, schema, options) {
723
- try {
724
- const [value, adapted] = Schema.resolve(data[key], schema, {
725
- ...options,
726
- path: [...options.path || [], key]
727
- });
728
- if (adapted !== void 0) data[key] = adapted;
729
- return value;
730
- } catch (e) {
731
- if (!options?.autofix) throw e;
732
- delete data[key];
733
- return schema.meta.default;
734
- }
735
- }
736
- Schema.extend("array", (data, { inner, meta }, options) => {
737
- if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
738
- checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
739
- return [data.map((_, index) => property(data, index, inner, options))];
740
- });
741
- Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
742
- if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
743
- const result = {};
744
- for (const key in data) {
745
- let rKey;
746
- try {
747
- rKey = Schema.resolve(key, sKey, options)[0];
748
- } catch (error) {
749
- if (strict) continue;
750
- throw error;
751
- }
752
- result[rKey] = property(data, key, inner, options);
753
- data[rKey] = data[key];
754
- if (key !== rKey) delete data[key];
755
- }
756
- return [result];
757
- });
758
- Schema.extend("tuple", (data, { list }, options, strict) => {
759
- if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
760
- const result = list.map((inner, index) => property(data, index, inner, options));
761
- if (strict) return [result];
762
- result.push(...data.slice(list.length));
763
- return [result];
764
- });
765
- function merge(result, data) {
766
- for (const key in data) {
767
- if (key in result) continue;
768
- result[key] = data[key];
769
- }
770
- }
771
- Schema.extend("object", (data, { dict }, options, strict) => {
772
- if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
773
- const result = {};
774
- for (const key in dict) {
775
- const value = property(data, key, dict[key], options);
776
- if (!isNullable(value) || key in data) result[key] = value;
777
- }
778
- if (!strict) merge(result, data);
779
- return [result];
780
- });
781
- Schema.extend("union", (data, { list, toString }, options, strict) => {
782
- const messages = [];
783
- for (const inner of list) try {
784
- return Schema.resolve(data, inner, options, strict);
785
- } catch (error) {
786
- messages.push(error);
787
- }
788
- throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
789
- });
790
- Schema.extend("intersect", (data, { list, toString }, options, strict) => {
791
- if (!list.length) return [data];
792
- let result;
793
- for (const inner of list) {
794
- const value = Schema.resolve(data, inner, options, true)[0];
795
- if (isNullable(value)) continue;
796
- if (isNullable(result)) result = value;
797
- else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
798
- else if (typeof value === "object") merge(result ??= {}, value);
799
- else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
800
- }
801
- if (!strict && isPlainObject(data)) merge(result, data);
802
- return [result];
803
- });
804
- Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
805
- const [result, adapted = data] = Schema.resolve(data, inner, options, true);
806
- if (preserve) return [callback(result)];
807
- else return [callback(result), callback(adapted)];
808
- });
809
- const formatters = {};
810
- function defineMethod(name, keys, format) {
811
- formatters[name] = format;
812
- Object.assign(Schema, { [name](...args) {
813
- const schema = new Schema({ type: name });
814
- keys.forEach((key, index) => {
815
- switch (key) {
816
- case "sKey":
817
- schema.sKey = args[index] ?? Schema.string();
818
- break;
819
- case "inner":
820
- schema.inner = Schema.from(args[index]);
821
- break;
822
- case "list":
823
- schema.list = args[index].map(Schema.from);
824
- break;
825
- case "dict":
826
- schema.dict = mapValues(args[index], Schema.from);
827
- break;
828
- case "bits":
829
- schema.bits = {};
830
- for (const key in args[index]) {
831
- if (typeof args[index][key] !== "number") continue;
832
- schema.bits[key] = args[index][key];
833
- }
834
- break;
835
- case "callback": {
836
- const callback = schema.callback = args[index];
837
- callback["toJSON"] ||= () => callback.toString();
838
- break;
839
- }
840
- case "constructor": {
841
- const constructor = schema.constructor = args[index];
842
- if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
843
- break;
844
- }
845
- default: schema[key] = args[index];
846
- }
847
- });
848
- if (name === "object" || name === "dict") schema.meta.default = {};
849
- else if (name === "array" || name === "tuple") schema.meta.default = [];
850
- else if (name === "bitset") schema.meta.default = 0;
851
- return schema;
852
- } });
853
- }
854
- defineMethod("is", ["constructor"], ({ constructor }) => {
855
- if (typeof constructor === "function") return constructor.name;
856
- else return constructor;
857
- });
858
- defineMethod("any", [], () => "any");
859
- defineMethod("never", [], () => "never");
860
- defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
861
- defineMethod("string", [], () => "string");
862
- defineMethod("number", [], () => "number");
863
- defineMethod("boolean", [], () => "boolean");
864
- defineMethod("bitset", ["bits"], () => "bitset");
865
- defineMethod("function", [], () => "function");
866
- defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
867
- defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
868
- defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
869
- defineMethod("object", ["dict"], ({ dict }) => {
870
- if (Object.keys(dict).length === 0) return "{}";
871
- return `{ ${Object.entries(dict).map(([key, inner]) => {
872
- return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
873
- }).join(", ")} }`;
874
- });
875
- defineMethod("union", ["list"], ({ list }, inline) => {
876
- const result = list.map(({ toString: format }) => format()).join(" | ");
877
- return inline ? `(${result})` : result;
878
- });
879
- defineMethod("intersect", ["list"], ({ list }) => {
880
- return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
881
- });
882
- defineMethod("transform", [
883
- "inner",
884
- "callback",
885
- "preserve"
886
- ], ({ inner }, isInner) => inner.toString(isInner));
887
- //#endregion
888
97
  //#region ../../util/timeout/src/index.ts
889
98
  /** Largest delay Node schedules without clamping it to one millisecond. */
890
99
  const MAX_TIMER_DELAY_MS = 2147483647;
@@ -924,22 +133,22 @@ const DEFAULT_RETRYABLE_CODES = Object.freeze([
924
133
  "TIMEOUT",
925
134
  "TRANSPORT"
926
135
  ]);
927
- const backoffSchema = Schema.object({
928
- initialDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
929
- maxDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
930
- jitterRatio: Schema.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
136
+ const backoffSchema = z.object({
137
+ initialDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
138
+ maxDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
139
+ jitterRatio: z.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
931
140
  });
932
- const normalPolicySchema = Schema.object({
933
- mode: Schema.const("normal").required(),
934
- maxRetries: Schema.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
935
- retryableCodes: Schema.array(Schema.string()).default([...DEFAULT_RETRYABLE_CODES]),
141
+ const normalPolicySchema = z.object({
142
+ mode: z.const("normal").required(),
143
+ maxRetries: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
144
+ retryableCodes: z.array(z.string()).default([...DEFAULT_RETRYABLE_CODES]),
936
145
  backoff: backoffSchema
937
146
  });
938
- const alwaysPolicySchema = Schema.object({
939
- mode: Schema.const("always").required(),
147
+ const alwaysPolicySchema = z.object({
148
+ mode: z.const("always").required(),
940
149
  backoff: backoffSchema
941
150
  });
942
- Schema.union([normalPolicySchema, alwaysPolicySchema]);
151
+ z.union([normalPolicySchema, alwaysPolicySchema]);
943
152
  //#endregion
944
153
  //#region ../../llm/llm/src/attribution.ts
945
154
  /**
@@ -1261,7 +470,7 @@ var SkillLayer = class {
1261
470
  }
1262
471
  };
1263
472
  (class extends Service {
1264
- static Config = Schema.object({ collectCacheMaxEntries: Schema.number().default(DEFAULT_COLLECT_CACHE_ENTRIES) });
473
+ static Config = z.object({ collectCacheMaxEntries: z.number().default(DEFAULT_COLLECT_CACHE_ENTRIES) });
1265
474
  collectCacheMaxEntries;
1266
475
  layers = new ScopedLayers((scope) => new SkillLayer(scope), () => {
1267
476
  this.invalidateCache();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mengyuly/dsh-ponytail",
3
3
  "description": "Lazy senior dev mode for DeepSeek Harness: always-on minimal-code ruleset, intensity switching, and short review/audit/debt/gain/help skills",
4
- "version": "0.1.4",
4
+ "version": "0.1.6",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -25,6 +25,7 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "files": [
28
+ "dist-provenance.json",
28
29
  "CHANGELOG.md",
29
30
  "lib/index.js",
30
31
  "lib/invariant.js",
@@ -43,12 +44,14 @@
43
44
  "@deepseek-ai/dsh-invariants": ">=0.0.1-rc <2",
44
45
  "@deepseek-ai/dsh-llm": ">=0.0.1-rc <2",
45
46
  "@deepseek-ai/dsh-skill": ">=0.0.1-rc <2",
46
- "@deepseek-ai/dsh-system-prompt": ">=0.0.1-rc <2"
47
+ "@deepseek-ai/dsh-system-prompt": ">=0.0.1-rc <2",
48
+ "@deepseek-ai/schemastery": "^3.18.0"
47
49
  },
48
50
  "scripts": {
49
51
  "sync:dist": "node scripts/sync-dist.mjs",
50
52
  "verify:dist": "node scripts/verify-dist.mjs",
51
53
  "verify:pack": "node scripts/verify-pack.mjs",
52
- "test:consumer": "node scripts/test-consumer.mjs"
54
+ "test:consumer": "node scripts/test-consumer.mjs",
55
+ "test:regressions": "node scripts/test-regressions.mjs"
53
56
  }
54
57
  }