openspecui 0.4.0 → 0.6.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/cli.mjs +12 -12
- package/dist/index.mjs +1 -1
- package/dist/{open-Cw_l4kh9.mjs → open-BTGRjBxH.mjs} +1 -1
- package/dist/{src-BS3LYQ6T.mjs → src-IryZbu1Z.mjs} +2341 -1422
- package/package.json +6 -3
- package/web/assets/index-CQhLNlQr.js +369 -0
- package/web/assets/index-D-0Lh1u9.css +1 -0
- package/web/index.html +2 -2
- package/web/assets/index-DU-ty3pA.css +0 -1
- package/web/assets/index-noSlrtA-.js +0 -337
|
@@ -9,9 +9,11 @@ import { join } from "path";
|
|
|
9
9
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
10
|
import { readFile as readFile$1, readdir, stat } from "node:fs/promises";
|
|
11
11
|
import { dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
|
|
12
|
-
import { existsSync, readFileSync, statSync,
|
|
13
|
-
import { watch
|
|
14
|
-
import { spawn } from "child_process";
|
|
12
|
+
import { existsSync, readFileSync, realpathSync, statSync, utimesSync } from "node:fs";
|
|
13
|
+
import { watch } from "fs";
|
|
14
|
+
import { exec, spawn } from "child_process";
|
|
15
|
+
import { promisify } from "util";
|
|
16
|
+
import { homedir } from "node:os";
|
|
15
17
|
import { createServer as createServer$1 } from "node:net";
|
|
16
18
|
import { fileURLToPath } from "node:url";
|
|
17
19
|
|
|
@@ -46,1011 +48,573 @@ var __toESM$1 = (mod, isNodeMode, target) => (target = mod != null ? __create$1(
|
|
|
46
48
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
47
49
|
|
|
48
50
|
//#endregion
|
|
49
|
-
//#region ../../node_modules/.pnpm
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (e instanceof RequestError) return e;
|
|
58
|
-
return new RequestError(e.message, { cause: e });
|
|
59
|
-
};
|
|
60
|
-
var GlobalRequest = global.Request;
|
|
61
|
-
var Request$1 = class extends GlobalRequest {
|
|
62
|
-
constructor(input, options) {
|
|
63
|
-
if (typeof input === "object" && getRequestCache in input) input = input[getRequestCache]();
|
|
64
|
-
if (typeof options?.body?.getReader !== "undefined") options.duplex ??= "half";
|
|
65
|
-
super(input, options);
|
|
51
|
+
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
|
|
52
|
+
var util;
|
|
53
|
+
(function(util$1) {
|
|
54
|
+
util$1.assertEqual = (_) => {};
|
|
55
|
+
function assertIs(_arg) {}
|
|
56
|
+
util$1.assertIs = assertIs;
|
|
57
|
+
function assertNever(_x) {
|
|
58
|
+
throw new Error();
|
|
66
59
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
util$1.assertNever = assertNever;
|
|
61
|
+
util$1.arrayToEnum = (items) => {
|
|
62
|
+
const obj = {};
|
|
63
|
+
for (const item of items) obj[item] = item;
|
|
64
|
+
return obj;
|
|
65
|
+
};
|
|
66
|
+
util$1.getValidEnumValues = (obj) => {
|
|
67
|
+
const validKeys = util$1.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number");
|
|
68
|
+
const filtered = {};
|
|
69
|
+
for (const k of validKeys) filtered[k] = obj[k];
|
|
70
|
+
return util$1.objectValues(filtered);
|
|
71
|
+
};
|
|
72
|
+
util$1.objectValues = (obj) => {
|
|
73
|
+
return util$1.objectKeys(obj).map(function(e) {
|
|
74
|
+
return obj[e];
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
util$1.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => {
|
|
78
|
+
const keys = [];
|
|
79
|
+
for (const key in object) if (Object.prototype.hasOwnProperty.call(object, key)) keys.push(key);
|
|
80
|
+
return keys;
|
|
81
|
+
};
|
|
82
|
+
util$1.find = (arr, checker) => {
|
|
83
|
+
for (const item of arr) if (checker(item)) return item;
|
|
84
|
+
};
|
|
85
|
+
util$1.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val;
|
|
86
|
+
function joinValues(array, separator = " | ") {
|
|
87
|
+
return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator);
|
|
74
88
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const init = {
|
|
80
|
-
method,
|
|
81
|
-
headers,
|
|
82
|
-
signal: abortController.signal
|
|
89
|
+
util$1.joinValues = joinValues;
|
|
90
|
+
util$1.jsonStringifyReplacer = (_, value) => {
|
|
91
|
+
if (typeof value === "bigint") return value.toString();
|
|
92
|
+
return value;
|
|
83
93
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
})(util || (util = {}));
|
|
95
|
+
var objectUtil;
|
|
96
|
+
(function(objectUtil$1) {
|
|
97
|
+
objectUtil$1.mergeShapes = (first, second) => {
|
|
98
|
+
return {
|
|
99
|
+
...first,
|
|
100
|
+
...second
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
})(objectUtil || (objectUtil = {}));
|
|
104
|
+
const ZodParsedType = util.arrayToEnum([
|
|
105
|
+
"string",
|
|
106
|
+
"nan",
|
|
107
|
+
"number",
|
|
108
|
+
"integer",
|
|
109
|
+
"float",
|
|
110
|
+
"boolean",
|
|
111
|
+
"date",
|
|
112
|
+
"bigint",
|
|
113
|
+
"symbol",
|
|
114
|
+
"function",
|
|
115
|
+
"undefined",
|
|
116
|
+
"null",
|
|
117
|
+
"array",
|
|
118
|
+
"object",
|
|
119
|
+
"unknown",
|
|
120
|
+
"promise",
|
|
121
|
+
"void",
|
|
122
|
+
"never",
|
|
123
|
+
"map",
|
|
124
|
+
"set"
|
|
125
|
+
]);
|
|
126
|
+
const getParsedType = (data) => {
|
|
127
|
+
switch (typeof data) {
|
|
128
|
+
case "undefined": return ZodParsedType.undefined;
|
|
129
|
+
case "string": return ZodParsedType.string;
|
|
130
|
+
case "number": return Number.isNaN(data) ? ZodParsedType.nan : ZodParsedType.number;
|
|
131
|
+
case "boolean": return ZodParsedType.boolean;
|
|
132
|
+
case "function": return ZodParsedType.function;
|
|
133
|
+
case "bigint": return ZodParsedType.bigint;
|
|
134
|
+
case "symbol": return ZodParsedType.symbol;
|
|
135
|
+
case "object":
|
|
136
|
+
if (Array.isArray(data)) return ZodParsedType.array;
|
|
137
|
+
if (data === null) return ZodParsedType.null;
|
|
138
|
+
if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") return ZodParsedType.promise;
|
|
139
|
+
if (typeof Map !== "undefined" && data instanceof Map) return ZodParsedType.map;
|
|
140
|
+
if (typeof Set !== "undefined" && data instanceof Set) return ZodParsedType.set;
|
|
141
|
+
if (typeof Date !== "undefined" && data instanceof Date) return ZodParsedType.date;
|
|
142
|
+
return ZodParsedType.object;
|
|
143
|
+
default: return ZodParsedType.unknown;
|
|
91
144
|
}
|
|
92
|
-
if (!(method === "GET" || method === "HEAD")) if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) {
|
|
93
|
-
controller.enqueue(incoming.rawBody);
|
|
94
|
-
controller.close();
|
|
95
|
-
} });
|
|
96
|
-
else if (incoming[wrapBodyStream]) {
|
|
97
|
-
let reader;
|
|
98
|
-
init.body = new ReadableStream({ async pull(controller) {
|
|
99
|
-
try {
|
|
100
|
-
reader ||= Readable.toWeb(incoming).getReader();
|
|
101
|
-
const { done, value } = await reader.read();
|
|
102
|
-
if (done) controller.close();
|
|
103
|
-
else controller.enqueue(value);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
controller.error(error);
|
|
106
|
-
}
|
|
107
|
-
} });
|
|
108
|
-
} else init.body = Readable.toWeb(incoming);
|
|
109
|
-
return new Request$1(url, init);
|
|
110
145
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js
|
|
149
|
+
const ZodIssueCode = util.arrayToEnum([
|
|
150
|
+
"invalid_type",
|
|
151
|
+
"invalid_literal",
|
|
152
|
+
"custom",
|
|
153
|
+
"invalid_union",
|
|
154
|
+
"invalid_union_discriminator",
|
|
155
|
+
"invalid_enum_value",
|
|
156
|
+
"unrecognized_keys",
|
|
157
|
+
"invalid_arguments",
|
|
158
|
+
"invalid_return_type",
|
|
159
|
+
"invalid_date",
|
|
160
|
+
"invalid_string",
|
|
161
|
+
"too_small",
|
|
162
|
+
"too_big",
|
|
163
|
+
"invalid_intersection_types",
|
|
164
|
+
"not_multiple_of",
|
|
165
|
+
"not_finite"
|
|
166
|
+
]);
|
|
167
|
+
var ZodError = class ZodError extends Error {
|
|
168
|
+
get errors() {
|
|
169
|
+
return this.issues;
|
|
134
170
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
].forEach((k) => {
|
|
150
|
-
Object.defineProperty(requestPrototype, k, { get() {
|
|
151
|
-
return this[getRequestCache]()[k];
|
|
152
|
-
} });
|
|
153
|
-
});
|
|
154
|
-
[
|
|
155
|
-
"arrayBuffer",
|
|
156
|
-
"blob",
|
|
157
|
-
"clone",
|
|
158
|
-
"formData",
|
|
159
|
-
"json",
|
|
160
|
-
"text"
|
|
161
|
-
].forEach((k) => {
|
|
162
|
-
Object.defineProperty(requestPrototype, k, { value: function() {
|
|
163
|
-
return this[getRequestCache]()[k]();
|
|
164
|
-
} });
|
|
165
|
-
});
|
|
166
|
-
Object.setPrototypeOf(requestPrototype, Request$1.prototype);
|
|
167
|
-
var newRequest = (incoming, defaultHostname) => {
|
|
168
|
-
const req = Object.create(requestPrototype);
|
|
169
|
-
req[incomingKey] = incoming;
|
|
170
|
-
const incomingUrl = incoming.url || "";
|
|
171
|
-
if (incomingUrl[0] !== "/" && (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) {
|
|
172
|
-
if (incoming instanceof Http2ServerRequest) throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
173
|
-
try {
|
|
174
|
-
req[urlKey] = new URL(incomingUrl).href;
|
|
175
|
-
} catch (e) {
|
|
176
|
-
throw new RequestError("Invalid absolute URL", { cause: e });
|
|
177
|
-
}
|
|
178
|
-
return req;
|
|
171
|
+
constructor(issues) {
|
|
172
|
+
super();
|
|
173
|
+
this.issues = [];
|
|
174
|
+
this.addIssue = (sub) => {
|
|
175
|
+
this.issues = [...this.issues, sub];
|
|
176
|
+
};
|
|
177
|
+
this.addIssues = (subs = []) => {
|
|
178
|
+
this.issues = [...this.issues, ...subs];
|
|
179
|
+
};
|
|
180
|
+
const actualProto = new.target.prototype;
|
|
181
|
+
if (Object.setPrototypeOf) Object.setPrototypeOf(this, actualProto);
|
|
182
|
+
else this.__proto__ = actualProto;
|
|
183
|
+
this.name = "ZodError";
|
|
184
|
+
this.issues = issues;
|
|
179
185
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
constructor(body, init) {
|
|
204
|
-
let headers;
|
|
205
|
-
this.#body = body;
|
|
206
|
-
if (init instanceof _Response) {
|
|
207
|
-
const cachedGlobalResponse = init[responseCache];
|
|
208
|
-
if (cachedGlobalResponse) {
|
|
209
|
-
this.#init = cachedGlobalResponse;
|
|
210
|
-
this[getResponseCache]();
|
|
211
|
-
return;
|
|
212
|
-
} else {
|
|
213
|
-
this.#init = init.#init;
|
|
214
|
-
headers = new Headers(init.#init.headers);
|
|
186
|
+
format(_mapper) {
|
|
187
|
+
const mapper = _mapper || function(issue) {
|
|
188
|
+
return issue.message;
|
|
189
|
+
};
|
|
190
|
+
const fieldErrors = { _errors: [] };
|
|
191
|
+
const processError = (error) => {
|
|
192
|
+
for (const issue of error.issues) if (issue.code === "invalid_union") issue.unionErrors.map(processError);
|
|
193
|
+
else if (issue.code === "invalid_return_type") processError(issue.returnTypeError);
|
|
194
|
+
else if (issue.code === "invalid_arguments") processError(issue.argumentsError);
|
|
195
|
+
else if (issue.path.length === 0) fieldErrors._errors.push(mapper(issue));
|
|
196
|
+
else {
|
|
197
|
+
let curr = fieldErrors;
|
|
198
|
+
let i = 0;
|
|
199
|
+
while (i < issue.path.length) {
|
|
200
|
+
const el = issue.path[i];
|
|
201
|
+
if (!(i === issue.path.length - 1)) curr[el] = curr[el] || { _errors: [] };
|
|
202
|
+
else {
|
|
203
|
+
curr[el] = curr[el] || { _errors: [] };
|
|
204
|
+
curr[el]._errors.push(mapper(issue));
|
|
205
|
+
}
|
|
206
|
+
curr = curr[el];
|
|
207
|
+
i++;
|
|
208
|
+
}
|
|
215
209
|
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
this[cacheKey] = [
|
|
220
|
-
init?.status || 200,
|
|
221
|
-
body,
|
|
222
|
-
headers
|
|
223
|
-
];
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
get headers() {
|
|
227
|
-
const cache = this[cacheKey];
|
|
228
|
-
if (cache) {
|
|
229
|
-
if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2]);
|
|
230
|
-
return cache[2];
|
|
231
|
-
}
|
|
232
|
-
return this[getResponseCache]().headers;
|
|
210
|
+
};
|
|
211
|
+
processError(this);
|
|
212
|
+
return fieldErrors;
|
|
233
213
|
}
|
|
234
|
-
|
|
235
|
-
|
|
214
|
+
static assert(value) {
|
|
215
|
+
if (!(value instanceof ZodError)) throw new Error(`Not a ZodError: ${value}`);
|
|
236
216
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return status >= 200 && status < 300;
|
|
217
|
+
toString() {
|
|
218
|
+
return this.message;
|
|
240
219
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
"body",
|
|
244
|
-
"bodyUsed",
|
|
245
|
-
"redirected",
|
|
246
|
-
"statusText",
|
|
247
|
-
"trailers",
|
|
248
|
-
"type",
|
|
249
|
-
"url"
|
|
250
|
-
].forEach((k) => {
|
|
251
|
-
Object.defineProperty(Response2.prototype, k, { get() {
|
|
252
|
-
return this[getResponseCache]()[k];
|
|
253
|
-
} });
|
|
254
|
-
});
|
|
255
|
-
[
|
|
256
|
-
"arrayBuffer",
|
|
257
|
-
"blob",
|
|
258
|
-
"clone",
|
|
259
|
-
"formData",
|
|
260
|
-
"json",
|
|
261
|
-
"text"
|
|
262
|
-
].forEach((k) => {
|
|
263
|
-
Object.defineProperty(Response2.prototype, k, { value: function() {
|
|
264
|
-
return this[getResponseCache]()[k]();
|
|
265
|
-
} });
|
|
266
|
-
});
|
|
267
|
-
Object.setPrototypeOf(Response2, GlobalResponse);
|
|
268
|
-
Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
|
|
269
|
-
async function readWithoutBlocking(readPromise) {
|
|
270
|
-
return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]);
|
|
271
|
-
}
|
|
272
|
-
function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {
|
|
273
|
-
const cancel = (error) => {
|
|
274
|
-
reader.cancel(error).catch(() => {});
|
|
275
|
-
};
|
|
276
|
-
writable.on("close", cancel);
|
|
277
|
-
writable.on("error", cancel);
|
|
278
|
-
(currentReadPromise ?? reader.read()).then(flow, handleStreamError);
|
|
279
|
-
return reader.closed.finally(() => {
|
|
280
|
-
writable.off("close", cancel);
|
|
281
|
-
writable.off("error", cancel);
|
|
282
|
-
});
|
|
283
|
-
function handleStreamError(error) {
|
|
284
|
-
if (error) writable.destroy(error);
|
|
220
|
+
get message() {
|
|
221
|
+
return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2);
|
|
285
222
|
}
|
|
286
|
-
|
|
287
|
-
|
|
223
|
+
get isEmpty() {
|
|
224
|
+
return this.issues.length === 0;
|
|
288
225
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
226
|
+
flatten(mapper = (issue) => issue.message) {
|
|
227
|
+
const fieldErrors = {};
|
|
228
|
+
const formErrors = [];
|
|
229
|
+
for (const sub of this.issues) if (sub.path.length > 0) {
|
|
230
|
+
const firstEl = sub.path[0];
|
|
231
|
+
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
|
|
232
|
+
fieldErrors[firstEl].push(mapper(sub));
|
|
233
|
+
} else formErrors.push(mapper(sub));
|
|
234
|
+
return {
|
|
235
|
+
formErrors,
|
|
236
|
+
fieldErrors
|
|
237
|
+
};
|
|
297
238
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (stream.locked) throw new TypeError("ReadableStream is locked.");
|
|
301
|
-
else if (writable.destroyed) return;
|
|
302
|
-
return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
|
|
303
|
-
}
|
|
304
|
-
var buildOutgoingHttpHeaders = (headers) => {
|
|
305
|
-
const res = {};
|
|
306
|
-
if (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0);
|
|
307
|
-
const cookies = [];
|
|
308
|
-
for (const [k, v] of headers) if (k === "set-cookie") cookies.push(v);
|
|
309
|
-
else res[k] = v;
|
|
310
|
-
if (cookies.length > 0) res["set-cookie"] = cookies;
|
|
311
|
-
res["content-type"] ??= "text/plain; charset=UTF-8";
|
|
312
|
-
return res;
|
|
313
|
-
};
|
|
314
|
-
var X_ALREADY_SENT = "x-hono-already-sent";
|
|
315
|
-
var webFetch = global.fetch;
|
|
316
|
-
if (typeof global.crypto === "undefined") global.crypto = crypto;
|
|
317
|
-
global.fetch = (info, init) => {
|
|
318
|
-
init = {
|
|
319
|
-
compress: false,
|
|
320
|
-
...init
|
|
321
|
-
};
|
|
322
|
-
return webFetch(info, init);
|
|
323
|
-
};
|
|
324
|
-
var outgoingEnded = Symbol("outgoingEnded");
|
|
325
|
-
var handleRequestError = () => new Response(null, { status: 400 });
|
|
326
|
-
var handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 });
|
|
327
|
-
var handleResponseError = (e, outgoing) => {
|
|
328
|
-
const err = e instanceof Error ? e : new Error("unknown error", { cause: e });
|
|
329
|
-
if (err.code === "ERR_STREAM_PREMATURE_CLOSE") console.info("The user aborted a request.");
|
|
330
|
-
else {
|
|
331
|
-
console.error(e);
|
|
332
|
-
if (!outgoing.headersSent) outgoing.writeHead(500, { "Content-Type": "text/plain" });
|
|
333
|
-
outgoing.end(`Error: ${err.message}`);
|
|
334
|
-
outgoing.destroy(err);
|
|
239
|
+
get formErrors() {
|
|
240
|
+
return this.flatten();
|
|
335
241
|
}
|
|
336
242
|
};
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
};
|
|
340
|
-
var responseViaCache = async (res, outgoing) => {
|
|
341
|
-
let [status, body, header] = res[cacheKey];
|
|
342
|
-
if (header instanceof Headers) header = buildOutgoingHttpHeaders(header);
|
|
343
|
-
if (typeof body === "string") header["Content-Length"] = Buffer.byteLength(body);
|
|
344
|
-
else if (body instanceof Uint8Array) header["Content-Length"] = body.byteLength;
|
|
345
|
-
else if (body instanceof Blob) header["Content-Length"] = body.size;
|
|
346
|
-
outgoing.writeHead(status, header);
|
|
347
|
-
if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body);
|
|
348
|
-
else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
349
|
-
else {
|
|
350
|
-
flushHeaders(outgoing);
|
|
351
|
-
await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
|
|
352
|
-
}
|
|
353
|
-
outgoing[outgoingEnded]?.();
|
|
243
|
+
ZodError.create = (issues) => {
|
|
244
|
+
return new ZodError(issues);
|
|
354
245
|
};
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js
|
|
249
|
+
const errorMap = (issue, _ctx) => {
|
|
250
|
+
let message;
|
|
251
|
+
switch (issue.code) {
|
|
252
|
+
case ZodIssueCode.invalid_type:
|
|
253
|
+
if (issue.received === ZodParsedType.undefined) message = "Required";
|
|
254
|
+
else message = `Expected ${issue.expected}, received ${issue.received}`;
|
|
255
|
+
break;
|
|
256
|
+
case ZodIssueCode.invalid_literal:
|
|
257
|
+
message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`;
|
|
258
|
+
break;
|
|
259
|
+
case ZodIssueCode.unrecognized_keys:
|
|
260
|
+
message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`;
|
|
261
|
+
break;
|
|
262
|
+
case ZodIssueCode.invalid_union:
|
|
263
|
+
message = `Invalid input`;
|
|
264
|
+
break;
|
|
265
|
+
case ZodIssueCode.invalid_union_discriminator:
|
|
266
|
+
message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`;
|
|
267
|
+
break;
|
|
268
|
+
case ZodIssueCode.invalid_enum_value:
|
|
269
|
+
message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`;
|
|
270
|
+
break;
|
|
271
|
+
case ZodIssueCode.invalid_arguments:
|
|
272
|
+
message = `Invalid function arguments`;
|
|
273
|
+
break;
|
|
274
|
+
case ZodIssueCode.invalid_return_type:
|
|
275
|
+
message = `Invalid function return type`;
|
|
276
|
+
break;
|
|
277
|
+
case ZodIssueCode.invalid_date:
|
|
278
|
+
message = `Invalid date`;
|
|
279
|
+
break;
|
|
280
|
+
case ZodIssueCode.invalid_string:
|
|
281
|
+
if (typeof issue.validation === "object") if ("includes" in issue.validation) {
|
|
282
|
+
message = `Invalid input: must include "${issue.validation.includes}"`;
|
|
283
|
+
if (typeof issue.validation.position === "number") message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;
|
|
284
|
+
} else if ("startsWith" in issue.validation) message = `Invalid input: must start with "${issue.validation.startsWith}"`;
|
|
285
|
+
else if ("endsWith" in issue.validation) message = `Invalid input: must end with "${issue.validation.endsWith}"`;
|
|
286
|
+
else util.assertNever(issue.validation);
|
|
287
|
+
else if (issue.validation !== "regex") message = `Invalid ${issue.validation}`;
|
|
288
|
+
else message = "Invalid";
|
|
289
|
+
break;
|
|
290
|
+
case ZodIssueCode.too_small:
|
|
291
|
+
if (issue.type === "array") message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
|
|
292
|
+
else if (issue.type === "string") message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
|
|
293
|
+
else if (issue.type === "number") message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`;
|
|
294
|
+
else if (issue.type === "bigint") message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`;
|
|
295
|
+
else if (issue.type === "date") message = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`;
|
|
296
|
+
else message = "Invalid input";
|
|
297
|
+
break;
|
|
298
|
+
case ZodIssueCode.too_big:
|
|
299
|
+
if (issue.type === "array") message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
|
|
300
|
+
else if (issue.type === "string") message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
|
|
301
|
+
else if (issue.type === "number") message = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
|
|
302
|
+
else if (issue.type === "bigint") message = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
|
|
303
|
+
else if (issue.type === "date") message = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`;
|
|
304
|
+
else message = "Invalid input";
|
|
305
|
+
break;
|
|
306
|
+
case ZodIssueCode.custom:
|
|
307
|
+
message = `Invalid input`;
|
|
308
|
+
break;
|
|
309
|
+
case ZodIssueCode.invalid_intersection_types:
|
|
310
|
+
message = `Intersection results could not be merged`;
|
|
311
|
+
break;
|
|
312
|
+
case ZodIssueCode.not_multiple_of:
|
|
313
|
+
message = `Number must be a multiple of ${issue.multipleOf}`;
|
|
314
|
+
break;
|
|
315
|
+
case ZodIssueCode.not_finite:
|
|
316
|
+
message = "Number must be finite";
|
|
317
|
+
break;
|
|
318
|
+
default:
|
|
319
|
+
message = _ctx.defaultError;
|
|
320
|
+
util.assertNever(issue);
|
|
417
321
|
}
|
|
418
|
-
return
|
|
419
|
-
let res, req;
|
|
420
|
-
try {
|
|
421
|
-
req = newRequest(incoming, options.hostname);
|
|
422
|
-
let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
|
|
423
|
-
if (!incomingEnded) {
|
|
424
|
-
incoming[wrapBodyStream] = true;
|
|
425
|
-
incoming.on("end", () => {
|
|
426
|
-
incomingEnded = true;
|
|
427
|
-
});
|
|
428
|
-
if (incoming instanceof Http2ServerRequest) outgoing[outgoingEnded] = () => {
|
|
429
|
-
if (!incomingEnded) setTimeout(() => {
|
|
430
|
-
if (!incomingEnded) setTimeout(() => {
|
|
431
|
-
incoming.destroy();
|
|
432
|
-
outgoing.destroy();
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
outgoing.on("close", () => {
|
|
438
|
-
if (req[abortControllerKey]) {
|
|
439
|
-
if (incoming.errored) req[abortControllerKey].abort(incoming.errored.toString());
|
|
440
|
-
else if (!outgoing.writableFinished) req[abortControllerKey].abort("Client connection prematurely closed.");
|
|
441
|
-
}
|
|
442
|
-
if (!incomingEnded) setTimeout(() => {
|
|
443
|
-
if (!incomingEnded) setTimeout(() => {
|
|
444
|
-
incoming.destroy();
|
|
445
|
-
});
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
res = fetchCallback(req, {
|
|
449
|
-
incoming,
|
|
450
|
-
outgoing
|
|
451
|
-
});
|
|
452
|
-
if (cacheKey in res) return responseViaCache(res, outgoing);
|
|
453
|
-
} catch (e) {
|
|
454
|
-
if (!res) if (options.errorHandler) {
|
|
455
|
-
res = await options.errorHandler(req ? e : toRequestError(e));
|
|
456
|
-
if (!res) return;
|
|
457
|
-
} else if (!req) res = handleRequestError();
|
|
458
|
-
else res = handleFetchError(e);
|
|
459
|
-
else return handleResponseError(e, outgoing);
|
|
460
|
-
}
|
|
461
|
-
try {
|
|
462
|
-
return await responseViaResponseObject(res, outgoing, options);
|
|
463
|
-
} catch (e) {
|
|
464
|
-
return handleResponseError(e, outgoing);
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
};
|
|
468
|
-
var createAdaptorServer = (options) => {
|
|
469
|
-
const fetchCallback = options.fetch;
|
|
470
|
-
const requestListener = getRequestListener(fetchCallback, {
|
|
471
|
-
hostname: options.hostname,
|
|
472
|
-
overrideGlobalObjects: options.overrideGlobalObjects,
|
|
473
|
-
autoCleanupIncoming: options.autoCleanupIncoming
|
|
474
|
-
});
|
|
475
|
-
return (options.createServer || createServer)(options.serverOptions || {}, requestListener);
|
|
476
|
-
};
|
|
477
|
-
var serve = (options, listeningListener) => {
|
|
478
|
-
const server = createAdaptorServer(options);
|
|
479
|
-
server.listen(options?.port ?? 3e3, options.hostname, () => {
|
|
480
|
-
const serverInfo = server.address();
|
|
481
|
-
listeningListener && listeningListener(serverInfo);
|
|
482
|
-
});
|
|
483
|
-
return server;
|
|
322
|
+
return { message };
|
|
484
323
|
};
|
|
324
|
+
var en_default = errorMap;
|
|
485
325
|
|
|
486
326
|
//#endregion
|
|
487
|
-
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
};
|
|
502
|
-
util$1.getValidEnumValues = (obj) => {
|
|
503
|
-
const validKeys = util$1.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number");
|
|
504
|
-
const filtered = {};
|
|
505
|
-
for (const k of validKeys) filtered[k] = obj[k];
|
|
506
|
-
return util$1.objectValues(filtered);
|
|
507
|
-
};
|
|
508
|
-
util$1.objectValues = (obj) => {
|
|
509
|
-
return util$1.objectKeys(obj).map(function(e) {
|
|
510
|
-
return obj[e];
|
|
511
|
-
});
|
|
327
|
+
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js
|
|
328
|
+
let overrideErrorMap = en_default;
|
|
329
|
+
function getErrorMap() {
|
|
330
|
+
return overrideErrorMap;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
//#endregion
|
|
334
|
+
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
335
|
+
const makeIssue = (params) => {
|
|
336
|
+
const { data, path: path$1, errorMaps, issueData } = params;
|
|
337
|
+
const fullPath = [...path$1, ...issueData.path || []];
|
|
338
|
+
const fullIssue = {
|
|
339
|
+
...issueData,
|
|
340
|
+
path: fullPath
|
|
512
341
|
};
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
342
|
+
if (issueData.message !== void 0) return {
|
|
343
|
+
...issueData,
|
|
344
|
+
path: fullPath,
|
|
345
|
+
message: issueData.message
|
|
517
346
|
};
|
|
518
|
-
|
|
519
|
-
|
|
347
|
+
let errorMessage = "";
|
|
348
|
+
const maps = errorMaps.filter((m) => !!m).slice().reverse();
|
|
349
|
+
for (const map of maps) errorMessage = map(fullIssue, {
|
|
350
|
+
data,
|
|
351
|
+
defaultError: errorMessage
|
|
352
|
+
}).message;
|
|
353
|
+
return {
|
|
354
|
+
...issueData,
|
|
355
|
+
path: fullPath,
|
|
356
|
+
message: errorMessage
|
|
520
357
|
};
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
358
|
+
};
|
|
359
|
+
function addIssueToContext(ctx, issueData) {
|
|
360
|
+
const overrideMap = getErrorMap();
|
|
361
|
+
const issue = makeIssue({
|
|
362
|
+
issueData,
|
|
363
|
+
data: ctx.data,
|
|
364
|
+
path: ctx.path,
|
|
365
|
+
errorMaps: [
|
|
366
|
+
ctx.common.contextualErrorMap,
|
|
367
|
+
ctx.schemaErrorMap,
|
|
368
|
+
overrideMap,
|
|
369
|
+
overrideMap === en_default ? void 0 : en_default
|
|
370
|
+
].filter((x) => !!x)
|
|
371
|
+
});
|
|
372
|
+
ctx.common.issues.push(issue);
|
|
373
|
+
}
|
|
374
|
+
var ParseStatus = class ParseStatus {
|
|
375
|
+
constructor() {
|
|
376
|
+
this.value = "valid";
|
|
524
377
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
case "bigint": return ZodParsedType.bigint;
|
|
570
|
-
case "symbol": return ZodParsedType.symbol;
|
|
571
|
-
case "object":
|
|
572
|
-
if (Array.isArray(data)) return ZodParsedType.array;
|
|
573
|
-
if (data === null) return ZodParsedType.null;
|
|
574
|
-
if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") return ZodParsedType.promise;
|
|
575
|
-
if (typeof Map !== "undefined" && data instanceof Map) return ZodParsedType.map;
|
|
576
|
-
if (typeof Set !== "undefined" && data instanceof Set) return ZodParsedType.set;
|
|
577
|
-
if (typeof Date !== "undefined" && data instanceof Date) return ZodParsedType.date;
|
|
578
|
-
return ZodParsedType.object;
|
|
579
|
-
default: return ZodParsedType.unknown;
|
|
378
|
+
dirty() {
|
|
379
|
+
if (this.value === "valid") this.value = "dirty";
|
|
380
|
+
}
|
|
381
|
+
abort() {
|
|
382
|
+
if (this.value !== "aborted") this.value = "aborted";
|
|
383
|
+
}
|
|
384
|
+
static mergeArray(status, results) {
|
|
385
|
+
const arrayValue = [];
|
|
386
|
+
for (const s of results) {
|
|
387
|
+
if (s.status === "aborted") return INVALID;
|
|
388
|
+
if (s.status === "dirty") status.dirty();
|
|
389
|
+
arrayValue.push(s.value);
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
status: status.value,
|
|
393
|
+
value: arrayValue
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
static async mergeObjectAsync(status, pairs) {
|
|
397
|
+
const syncPairs = [];
|
|
398
|
+
for (const pair of pairs) {
|
|
399
|
+
const key = await pair.key;
|
|
400
|
+
const value = await pair.value;
|
|
401
|
+
syncPairs.push({
|
|
402
|
+
key,
|
|
403
|
+
value
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
return ParseStatus.mergeObjectSync(status, syncPairs);
|
|
407
|
+
}
|
|
408
|
+
static mergeObjectSync(status, pairs) {
|
|
409
|
+
const finalObject = {};
|
|
410
|
+
for (const pair of pairs) {
|
|
411
|
+
const { key, value } = pair;
|
|
412
|
+
if (key.status === "aborted") return INVALID;
|
|
413
|
+
if (value.status === "aborted") return INVALID;
|
|
414
|
+
if (key.status === "dirty") status.dirty();
|
|
415
|
+
if (value.status === "dirty") status.dirty();
|
|
416
|
+
if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) finalObject[key.value] = value.value;
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
status: status.value,
|
|
420
|
+
value: finalObject
|
|
421
|
+
};
|
|
580
422
|
}
|
|
581
423
|
};
|
|
424
|
+
const INVALID = Object.freeze({ status: "aborted" });
|
|
425
|
+
const DIRTY = (value) => ({
|
|
426
|
+
status: "dirty",
|
|
427
|
+
value
|
|
428
|
+
});
|
|
429
|
+
const OK = (value) => ({
|
|
430
|
+
status: "valid",
|
|
431
|
+
value
|
|
432
|
+
});
|
|
433
|
+
const isAborted = (x) => x.status === "aborted";
|
|
434
|
+
const isDirty = (x) => x.status === "dirty";
|
|
435
|
+
const isValid = (x) => x.status === "valid";
|
|
436
|
+
const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
|
|
582
437
|
|
|
583
438
|
//#endregion
|
|
584
|
-
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
"
|
|
588
|
-
"
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
"not_multiple_of",
|
|
601
|
-
"not_finite"
|
|
602
|
-
]);
|
|
603
|
-
var ZodError = class ZodError extends Error {
|
|
604
|
-
get errors() {
|
|
605
|
-
return this.issues;
|
|
439
|
+
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
|
|
440
|
+
var errorUtil;
|
|
441
|
+
(function(errorUtil$1) {
|
|
442
|
+
errorUtil$1.errToObj = (message) => typeof message === "string" ? { message } : message || {};
|
|
443
|
+
errorUtil$1.toString = (message) => typeof message === "string" ? message : message?.message;
|
|
444
|
+
})(errorUtil || (errorUtil = {}));
|
|
445
|
+
|
|
446
|
+
//#endregion
|
|
447
|
+
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
448
|
+
var ParseInputLazyPath = class {
|
|
449
|
+
constructor(parent, value, path$1, key) {
|
|
450
|
+
this._cachedPath = [];
|
|
451
|
+
this.parent = parent;
|
|
452
|
+
this.data = value;
|
|
453
|
+
this._path = path$1;
|
|
454
|
+
this._key = key;
|
|
606
455
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
this.
|
|
610
|
-
this.
|
|
611
|
-
this.issues = [...this.issues, sub];
|
|
612
|
-
};
|
|
613
|
-
this.addIssues = (subs = []) => {
|
|
614
|
-
this.issues = [...this.issues, ...subs];
|
|
615
|
-
};
|
|
616
|
-
const actualProto = new.target.prototype;
|
|
617
|
-
if (Object.setPrototypeOf) Object.setPrototypeOf(this, actualProto);
|
|
618
|
-
else this.__proto__ = actualProto;
|
|
619
|
-
this.name = "ZodError";
|
|
620
|
-
this.issues = issues;
|
|
456
|
+
get path() {
|
|
457
|
+
if (!this._cachedPath.length) if (Array.isArray(this._key)) this._cachedPath.push(...this._path, ...this._key);
|
|
458
|
+
else this._cachedPath.push(...this._path, this._key);
|
|
459
|
+
return this._cachedPath;
|
|
621
460
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const el = issue.path[i];
|
|
637
|
-
if (!(i === issue.path.length - 1)) curr[el] = curr[el] || { _errors: [] };
|
|
638
|
-
else {
|
|
639
|
-
curr[el] = curr[el] || { _errors: [] };
|
|
640
|
-
curr[el]._errors.push(mapper(issue));
|
|
641
|
-
}
|
|
642
|
-
curr = curr[el];
|
|
643
|
-
i++;
|
|
644
|
-
}
|
|
461
|
+
};
|
|
462
|
+
const handleResult = (ctx, result) => {
|
|
463
|
+
if (isValid(result)) return {
|
|
464
|
+
success: true,
|
|
465
|
+
data: result.value
|
|
466
|
+
};
|
|
467
|
+
else {
|
|
468
|
+
if (!ctx.common.issues.length) throw new Error("Validation failed but no issues detected.");
|
|
469
|
+
return {
|
|
470
|
+
success: false,
|
|
471
|
+
get error() {
|
|
472
|
+
if (this._error) return this._error;
|
|
473
|
+
this._error = new ZodError(ctx.common.issues);
|
|
474
|
+
return this._error;
|
|
645
475
|
}
|
|
646
476
|
};
|
|
647
|
-
processError(this);
|
|
648
|
-
return fieldErrors;
|
|
649
477
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
|
|
478
|
+
};
|
|
479
|
+
function processCreateParams(params) {
|
|
480
|
+
if (!params) return {};
|
|
481
|
+
const { errorMap: errorMap$1, invalid_type_error, required_error, description } = params;
|
|
482
|
+
if (errorMap$1 && (invalid_type_error || required_error)) throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);
|
|
483
|
+
if (errorMap$1) return {
|
|
484
|
+
errorMap: errorMap$1,
|
|
485
|
+
description
|
|
486
|
+
};
|
|
487
|
+
const customMap = (iss, ctx) => {
|
|
488
|
+
const { message } = params;
|
|
489
|
+
if (iss.code === "invalid_enum_value") return { message: message ?? ctx.defaultError };
|
|
490
|
+
if (typeof ctx.data === "undefined") return { message: message ?? required_error ?? ctx.defaultError };
|
|
491
|
+
if (iss.code !== "invalid_type") return { message: ctx.defaultError };
|
|
492
|
+
return { message: message ?? invalid_type_error ?? ctx.defaultError };
|
|
493
|
+
};
|
|
494
|
+
return {
|
|
495
|
+
errorMap: customMap,
|
|
496
|
+
description
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
var ZodType = class {
|
|
500
|
+
get description() {
|
|
501
|
+
return this._def.description;
|
|
655
502
|
}
|
|
656
|
-
|
|
657
|
-
return
|
|
503
|
+
_getType(input) {
|
|
504
|
+
return getParsedType(input.data);
|
|
658
505
|
}
|
|
659
|
-
|
|
660
|
-
return
|
|
506
|
+
_getOrReturnCtx(input, ctx) {
|
|
507
|
+
return ctx || {
|
|
508
|
+
common: input.parent.common,
|
|
509
|
+
data: input.data,
|
|
510
|
+
parsedType: getParsedType(input.data),
|
|
511
|
+
schemaErrorMap: this._def.errorMap,
|
|
512
|
+
path: input.path,
|
|
513
|
+
parent: input.parent
|
|
514
|
+
};
|
|
661
515
|
}
|
|
662
|
-
|
|
663
|
-
const fieldErrors = {};
|
|
664
|
-
const formErrors = [];
|
|
665
|
-
for (const sub of this.issues) if (sub.path.length > 0) {
|
|
666
|
-
const firstEl = sub.path[0];
|
|
667
|
-
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
|
|
668
|
-
fieldErrors[firstEl].push(mapper(sub));
|
|
669
|
-
} else formErrors.push(mapper(sub));
|
|
516
|
+
_processInputParams(input) {
|
|
670
517
|
return {
|
|
671
|
-
|
|
672
|
-
|
|
518
|
+
status: new ParseStatus(),
|
|
519
|
+
ctx: {
|
|
520
|
+
common: input.parent.common,
|
|
521
|
+
data: input.data,
|
|
522
|
+
parsedType: getParsedType(input.data),
|
|
523
|
+
schemaErrorMap: this._def.errorMap,
|
|
524
|
+
path: input.path,
|
|
525
|
+
parent: input.parent
|
|
526
|
+
}
|
|
673
527
|
};
|
|
674
528
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
ZodError.create = (issues) => {
|
|
680
|
-
return new ZodError(issues);
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
//#endregion
|
|
684
|
-
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js
|
|
685
|
-
const errorMap = (issue, _ctx) => {
|
|
686
|
-
let message;
|
|
687
|
-
switch (issue.code) {
|
|
688
|
-
case ZodIssueCode.invalid_type:
|
|
689
|
-
if (issue.received === ZodParsedType.undefined) message = "Required";
|
|
690
|
-
else message = `Expected ${issue.expected}, received ${issue.received}`;
|
|
691
|
-
break;
|
|
692
|
-
case ZodIssueCode.invalid_literal:
|
|
693
|
-
message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`;
|
|
694
|
-
break;
|
|
695
|
-
case ZodIssueCode.unrecognized_keys:
|
|
696
|
-
message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`;
|
|
697
|
-
break;
|
|
698
|
-
case ZodIssueCode.invalid_union:
|
|
699
|
-
message = `Invalid input`;
|
|
700
|
-
break;
|
|
701
|
-
case ZodIssueCode.invalid_union_discriminator:
|
|
702
|
-
message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`;
|
|
703
|
-
break;
|
|
704
|
-
case ZodIssueCode.invalid_enum_value:
|
|
705
|
-
message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`;
|
|
706
|
-
break;
|
|
707
|
-
case ZodIssueCode.invalid_arguments:
|
|
708
|
-
message = `Invalid function arguments`;
|
|
709
|
-
break;
|
|
710
|
-
case ZodIssueCode.invalid_return_type:
|
|
711
|
-
message = `Invalid function return type`;
|
|
712
|
-
break;
|
|
713
|
-
case ZodIssueCode.invalid_date:
|
|
714
|
-
message = `Invalid date`;
|
|
715
|
-
break;
|
|
716
|
-
case ZodIssueCode.invalid_string:
|
|
717
|
-
if (typeof issue.validation === "object") if ("includes" in issue.validation) {
|
|
718
|
-
message = `Invalid input: must include "${issue.validation.includes}"`;
|
|
719
|
-
if (typeof issue.validation.position === "number") message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;
|
|
720
|
-
} else if ("startsWith" in issue.validation) message = `Invalid input: must start with "${issue.validation.startsWith}"`;
|
|
721
|
-
else if ("endsWith" in issue.validation) message = `Invalid input: must end with "${issue.validation.endsWith}"`;
|
|
722
|
-
else util.assertNever(issue.validation);
|
|
723
|
-
else if (issue.validation !== "regex") message = `Invalid ${issue.validation}`;
|
|
724
|
-
else message = "Invalid";
|
|
725
|
-
break;
|
|
726
|
-
case ZodIssueCode.too_small:
|
|
727
|
-
if (issue.type === "array") message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
|
|
728
|
-
else if (issue.type === "string") message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
|
|
729
|
-
else if (issue.type === "number") message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`;
|
|
730
|
-
else if (issue.type === "bigint") message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`;
|
|
731
|
-
else if (issue.type === "date") message = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`;
|
|
732
|
-
else message = "Invalid input";
|
|
733
|
-
break;
|
|
734
|
-
case ZodIssueCode.too_big:
|
|
735
|
-
if (issue.type === "array") message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
|
|
736
|
-
else if (issue.type === "string") message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
|
|
737
|
-
else if (issue.type === "number") message = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
|
|
738
|
-
else if (issue.type === "bigint") message = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
|
|
739
|
-
else if (issue.type === "date") message = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`;
|
|
740
|
-
else message = "Invalid input";
|
|
741
|
-
break;
|
|
742
|
-
case ZodIssueCode.custom:
|
|
743
|
-
message = `Invalid input`;
|
|
744
|
-
break;
|
|
745
|
-
case ZodIssueCode.invalid_intersection_types:
|
|
746
|
-
message = `Intersection results could not be merged`;
|
|
747
|
-
break;
|
|
748
|
-
case ZodIssueCode.not_multiple_of:
|
|
749
|
-
message = `Number must be a multiple of ${issue.multipleOf}`;
|
|
750
|
-
break;
|
|
751
|
-
case ZodIssueCode.not_finite:
|
|
752
|
-
message = "Number must be finite";
|
|
753
|
-
break;
|
|
754
|
-
default:
|
|
755
|
-
message = _ctx.defaultError;
|
|
756
|
-
util.assertNever(issue);
|
|
757
|
-
}
|
|
758
|
-
return { message };
|
|
759
|
-
};
|
|
760
|
-
var en_default = errorMap;
|
|
761
|
-
|
|
762
|
-
//#endregion
|
|
763
|
-
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js
|
|
764
|
-
let overrideErrorMap = en_default;
|
|
765
|
-
function getErrorMap() {
|
|
766
|
-
return overrideErrorMap;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
//#endregion
|
|
770
|
-
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
771
|
-
const makeIssue = (params) => {
|
|
772
|
-
const { data, path: path$1, errorMaps, issueData } = params;
|
|
773
|
-
const fullPath = [...path$1, ...issueData.path || []];
|
|
774
|
-
const fullIssue = {
|
|
775
|
-
...issueData,
|
|
776
|
-
path: fullPath
|
|
777
|
-
};
|
|
778
|
-
if (issueData.message !== void 0) return {
|
|
779
|
-
...issueData,
|
|
780
|
-
path: fullPath,
|
|
781
|
-
message: issueData.message
|
|
782
|
-
};
|
|
783
|
-
let errorMessage = "";
|
|
784
|
-
const maps = errorMaps.filter((m) => !!m).slice().reverse();
|
|
785
|
-
for (const map of maps) errorMessage = map(fullIssue, {
|
|
786
|
-
data,
|
|
787
|
-
defaultError: errorMessage
|
|
788
|
-
}).message;
|
|
789
|
-
return {
|
|
790
|
-
...issueData,
|
|
791
|
-
path: fullPath,
|
|
792
|
-
message: errorMessage
|
|
793
|
-
};
|
|
794
|
-
};
|
|
795
|
-
function addIssueToContext(ctx, issueData) {
|
|
796
|
-
const overrideMap = getErrorMap();
|
|
797
|
-
const issue = makeIssue({
|
|
798
|
-
issueData,
|
|
799
|
-
data: ctx.data,
|
|
800
|
-
path: ctx.path,
|
|
801
|
-
errorMaps: [
|
|
802
|
-
ctx.common.contextualErrorMap,
|
|
803
|
-
ctx.schemaErrorMap,
|
|
804
|
-
overrideMap,
|
|
805
|
-
overrideMap === en_default ? void 0 : en_default
|
|
806
|
-
].filter((x) => !!x)
|
|
807
|
-
});
|
|
808
|
-
ctx.common.issues.push(issue);
|
|
809
|
-
}
|
|
810
|
-
var ParseStatus = class ParseStatus {
|
|
811
|
-
constructor() {
|
|
812
|
-
this.value = "valid";
|
|
529
|
+
_parseSync(input) {
|
|
530
|
+
const result = this._parse(input);
|
|
531
|
+
if (isAsync(result)) throw new Error("Synchronous parse encountered promise.");
|
|
532
|
+
return result;
|
|
813
533
|
}
|
|
814
|
-
|
|
815
|
-
|
|
534
|
+
_parseAsync(input) {
|
|
535
|
+
const result = this._parse(input);
|
|
536
|
+
return Promise.resolve(result);
|
|
816
537
|
}
|
|
817
|
-
|
|
818
|
-
|
|
538
|
+
parse(data, params) {
|
|
539
|
+
const result = this.safeParse(data, params);
|
|
540
|
+
if (result.success) return result.data;
|
|
541
|
+
throw result.error;
|
|
819
542
|
}
|
|
820
|
-
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
543
|
+
safeParse(data, params) {
|
|
544
|
+
const ctx = {
|
|
545
|
+
common: {
|
|
546
|
+
issues: [],
|
|
547
|
+
async: params?.async ?? false,
|
|
548
|
+
contextualErrorMap: params?.errorMap
|
|
549
|
+
},
|
|
550
|
+
path: params?.path || [],
|
|
551
|
+
schemaErrorMap: this._def.errorMap,
|
|
552
|
+
parent: null,
|
|
553
|
+
data,
|
|
554
|
+
parsedType: getParsedType(data)
|
|
830
555
|
};
|
|
556
|
+
return handleResult(ctx, this._parseSync({
|
|
557
|
+
data,
|
|
558
|
+
path: ctx.path,
|
|
559
|
+
parent: ctx
|
|
560
|
+
}));
|
|
831
561
|
}
|
|
832
|
-
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
562
|
+
"~validate"(data) {
|
|
563
|
+
const ctx = {
|
|
564
|
+
common: {
|
|
565
|
+
issues: [],
|
|
566
|
+
async: !!this["~standard"].async
|
|
567
|
+
},
|
|
568
|
+
path: [],
|
|
569
|
+
schemaErrorMap: this._def.errorMap,
|
|
570
|
+
parent: null,
|
|
571
|
+
data,
|
|
572
|
+
parsedType: getParsedType(data)
|
|
573
|
+
};
|
|
574
|
+
if (!this["~standard"].async) try {
|
|
575
|
+
const result = this._parseSync({
|
|
576
|
+
data,
|
|
577
|
+
path: [],
|
|
578
|
+
parent: ctx
|
|
840
579
|
});
|
|
580
|
+
return isValid(result) ? { value: result.value } : { issues: ctx.common.issues };
|
|
581
|
+
} catch (err) {
|
|
582
|
+
if (err?.message?.toLowerCase()?.includes("encountered")) this["~standard"].async = true;
|
|
583
|
+
ctx.common = {
|
|
584
|
+
issues: [],
|
|
585
|
+
async: true
|
|
586
|
+
};
|
|
841
587
|
}
|
|
842
|
-
return
|
|
588
|
+
return this._parseAsync({
|
|
589
|
+
data,
|
|
590
|
+
path: [],
|
|
591
|
+
parent: ctx
|
|
592
|
+
}).then((result) => isValid(result) ? { value: result.value } : { issues: ctx.common.issues });
|
|
843
593
|
}
|
|
844
|
-
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
594
|
+
async parseAsync(data, params) {
|
|
595
|
+
const result = await this.safeParseAsync(data, params);
|
|
596
|
+
if (result.success) return result.data;
|
|
597
|
+
throw result.error;
|
|
598
|
+
}
|
|
599
|
+
async safeParseAsync(data, params) {
|
|
600
|
+
const ctx = {
|
|
601
|
+
common: {
|
|
602
|
+
issues: [],
|
|
603
|
+
contextualErrorMap: params?.errorMap,
|
|
604
|
+
async: true
|
|
605
|
+
},
|
|
606
|
+
path: params?.path || [],
|
|
607
|
+
schemaErrorMap: this._def.errorMap,
|
|
608
|
+
parent: null,
|
|
609
|
+
data,
|
|
610
|
+
parsedType: getParsedType(data)
|
|
857
611
|
};
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
});
|
|
865
|
-
const OK = (value) => ({
|
|
866
|
-
status: "valid",
|
|
867
|
-
value
|
|
868
|
-
});
|
|
869
|
-
const isAborted = (x) => x.status === "aborted";
|
|
870
|
-
const isDirty = (x) => x.status === "dirty";
|
|
871
|
-
const isValid = (x) => x.status === "valid";
|
|
872
|
-
const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
|
|
873
|
-
|
|
874
|
-
//#endregion
|
|
875
|
-
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
|
|
876
|
-
var errorUtil;
|
|
877
|
-
(function(errorUtil$1) {
|
|
878
|
-
errorUtil$1.errToObj = (message) => typeof message === "string" ? { message } : message || {};
|
|
879
|
-
errorUtil$1.toString = (message) => typeof message === "string" ? message : message?.message;
|
|
880
|
-
})(errorUtil || (errorUtil = {}));
|
|
881
|
-
|
|
882
|
-
//#endregion
|
|
883
|
-
//#region ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
884
|
-
var ParseInputLazyPath = class {
|
|
885
|
-
constructor(parent, value, path$1, key) {
|
|
886
|
-
this._cachedPath = [];
|
|
887
|
-
this.parent = parent;
|
|
888
|
-
this.data = value;
|
|
889
|
-
this._path = path$1;
|
|
890
|
-
this._key = key;
|
|
891
|
-
}
|
|
892
|
-
get path() {
|
|
893
|
-
if (!this._cachedPath.length) if (Array.isArray(this._key)) this._cachedPath.push(...this._path, ...this._key);
|
|
894
|
-
else this._cachedPath.push(...this._path, this._key);
|
|
895
|
-
return this._cachedPath;
|
|
896
|
-
}
|
|
897
|
-
};
|
|
898
|
-
const handleResult = (ctx, result) => {
|
|
899
|
-
if (isValid(result)) return {
|
|
900
|
-
success: true,
|
|
901
|
-
data: result.value
|
|
902
|
-
};
|
|
903
|
-
else {
|
|
904
|
-
if (!ctx.common.issues.length) throw new Error("Validation failed but no issues detected.");
|
|
905
|
-
return {
|
|
906
|
-
success: false,
|
|
907
|
-
get error() {
|
|
908
|
-
if (this._error) return this._error;
|
|
909
|
-
this._error = new ZodError(ctx.common.issues);
|
|
910
|
-
return this._error;
|
|
911
|
-
}
|
|
912
|
-
};
|
|
913
|
-
}
|
|
914
|
-
};
|
|
915
|
-
function processCreateParams(params) {
|
|
916
|
-
if (!params) return {};
|
|
917
|
-
const { errorMap: errorMap$1, invalid_type_error, required_error, description } = params;
|
|
918
|
-
if (errorMap$1 && (invalid_type_error || required_error)) throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);
|
|
919
|
-
if (errorMap$1) return {
|
|
920
|
-
errorMap: errorMap$1,
|
|
921
|
-
description
|
|
922
|
-
};
|
|
923
|
-
const customMap = (iss, ctx) => {
|
|
924
|
-
const { message } = params;
|
|
925
|
-
if (iss.code === "invalid_enum_value") return { message: message ?? ctx.defaultError };
|
|
926
|
-
if (typeof ctx.data === "undefined") return { message: message ?? required_error ?? ctx.defaultError };
|
|
927
|
-
if (iss.code !== "invalid_type") return { message: ctx.defaultError };
|
|
928
|
-
return { message: message ?? invalid_type_error ?? ctx.defaultError };
|
|
929
|
-
};
|
|
930
|
-
return {
|
|
931
|
-
errorMap: customMap,
|
|
932
|
-
description
|
|
933
|
-
};
|
|
934
|
-
}
|
|
935
|
-
var ZodType = class {
|
|
936
|
-
get description() {
|
|
937
|
-
return this._def.description;
|
|
938
|
-
}
|
|
939
|
-
_getType(input) {
|
|
940
|
-
return getParsedType(input.data);
|
|
941
|
-
}
|
|
942
|
-
_getOrReturnCtx(input, ctx) {
|
|
943
|
-
return ctx || {
|
|
944
|
-
common: input.parent.common,
|
|
945
|
-
data: input.data,
|
|
946
|
-
parsedType: getParsedType(input.data),
|
|
947
|
-
schemaErrorMap: this._def.errorMap,
|
|
948
|
-
path: input.path,
|
|
949
|
-
parent: input.parent
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
_processInputParams(input) {
|
|
953
|
-
return {
|
|
954
|
-
status: new ParseStatus(),
|
|
955
|
-
ctx: {
|
|
956
|
-
common: input.parent.common,
|
|
957
|
-
data: input.data,
|
|
958
|
-
parsedType: getParsedType(input.data),
|
|
959
|
-
schemaErrorMap: this._def.errorMap,
|
|
960
|
-
path: input.path,
|
|
961
|
-
parent: input.parent
|
|
962
|
-
}
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
_parseSync(input) {
|
|
966
|
-
const result = this._parse(input);
|
|
967
|
-
if (isAsync(result)) throw new Error("Synchronous parse encountered promise.");
|
|
968
|
-
return result;
|
|
969
|
-
}
|
|
970
|
-
_parseAsync(input) {
|
|
971
|
-
const result = this._parse(input);
|
|
972
|
-
return Promise.resolve(result);
|
|
973
|
-
}
|
|
974
|
-
parse(data, params) {
|
|
975
|
-
const result = this.safeParse(data, params);
|
|
976
|
-
if (result.success) return result.data;
|
|
977
|
-
throw result.error;
|
|
978
|
-
}
|
|
979
|
-
safeParse(data, params) {
|
|
980
|
-
const ctx = {
|
|
981
|
-
common: {
|
|
982
|
-
issues: [],
|
|
983
|
-
async: params?.async ?? false,
|
|
984
|
-
contextualErrorMap: params?.errorMap
|
|
985
|
-
},
|
|
986
|
-
path: params?.path || [],
|
|
987
|
-
schemaErrorMap: this._def.errorMap,
|
|
988
|
-
parent: null,
|
|
989
|
-
data,
|
|
990
|
-
parsedType: getParsedType(data)
|
|
991
|
-
};
|
|
992
|
-
return handleResult(ctx, this._parseSync({
|
|
993
|
-
data,
|
|
994
|
-
path: ctx.path,
|
|
995
|
-
parent: ctx
|
|
996
|
-
}));
|
|
997
|
-
}
|
|
998
|
-
"~validate"(data) {
|
|
999
|
-
const ctx = {
|
|
1000
|
-
common: {
|
|
1001
|
-
issues: [],
|
|
1002
|
-
async: !!this["~standard"].async
|
|
1003
|
-
},
|
|
1004
|
-
path: [],
|
|
1005
|
-
schemaErrorMap: this._def.errorMap,
|
|
1006
|
-
parent: null,
|
|
1007
|
-
data,
|
|
1008
|
-
parsedType: getParsedType(data)
|
|
1009
|
-
};
|
|
1010
|
-
if (!this["~standard"].async) try {
|
|
1011
|
-
const result = this._parseSync({
|
|
1012
|
-
data,
|
|
1013
|
-
path: [],
|
|
1014
|
-
parent: ctx
|
|
1015
|
-
});
|
|
1016
|
-
return isValid(result) ? { value: result.value } : { issues: ctx.common.issues };
|
|
1017
|
-
} catch (err) {
|
|
1018
|
-
if (err?.message?.toLowerCase()?.includes("encountered")) this["~standard"].async = true;
|
|
1019
|
-
ctx.common = {
|
|
1020
|
-
issues: [],
|
|
1021
|
-
async: true
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
return this._parseAsync({
|
|
1025
|
-
data,
|
|
1026
|
-
path: [],
|
|
1027
|
-
parent: ctx
|
|
1028
|
-
}).then((result) => isValid(result) ? { value: result.value } : { issues: ctx.common.issues });
|
|
1029
|
-
}
|
|
1030
|
-
async parseAsync(data, params) {
|
|
1031
|
-
const result = await this.safeParseAsync(data, params);
|
|
1032
|
-
if (result.success) return result.data;
|
|
1033
|
-
throw result.error;
|
|
1034
|
-
}
|
|
1035
|
-
async safeParseAsync(data, params) {
|
|
1036
|
-
const ctx = {
|
|
1037
|
-
common: {
|
|
1038
|
-
issues: [],
|
|
1039
|
-
contextualErrorMap: params?.errorMap,
|
|
1040
|
-
async: true
|
|
1041
|
-
},
|
|
1042
|
-
path: params?.path || [],
|
|
1043
|
-
schemaErrorMap: this._def.errorMap,
|
|
1044
|
-
parent: null,
|
|
1045
|
-
data,
|
|
1046
|
-
parsedType: getParsedType(data)
|
|
1047
|
-
};
|
|
1048
|
-
const maybeAsyncResult = this._parse({
|
|
1049
|
-
data,
|
|
1050
|
-
path: ctx.path,
|
|
1051
|
-
parent: ctx
|
|
1052
|
-
});
|
|
1053
|
-
return handleResult(ctx, await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult)));
|
|
612
|
+
const maybeAsyncResult = this._parse({
|
|
613
|
+
data,
|
|
614
|
+
path: ctx.path,
|
|
615
|
+
parent: ctx
|
|
616
|
+
});
|
|
617
|
+
return handleResult(ctx, await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult)));
|
|
1054
618
|
}
|
|
1055
619
|
refine(check, message) {
|
|
1056
620
|
const getIssueProperties = (val) => {
|
|
@@ -4165,186 +3729,624 @@ var APIProvider = class {
|
|
|
4165
3729
|
reader.releaseLock();
|
|
4166
3730
|
}
|
|
4167
3731
|
}
|
|
4168
|
-
async isAvailable() {
|
|
4169
|
-
try {
|
|
4170
|
-
return (await fetch(`${this.config.baseUrl}/models`, { headers: {
|
|
4171
|
-
...this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` },
|
|
4172
|
-
...this.config.headers
|
|
4173
|
-
} })).ok;
|
|
4174
|
-
} catch {
|
|
4175
|
-
return false;
|
|
3732
|
+
async isAvailable() {
|
|
3733
|
+
try {
|
|
3734
|
+
return (await fetch(`${this.config.baseUrl}/models`, { headers: {
|
|
3735
|
+
...this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` },
|
|
3736
|
+
...this.config.headers
|
|
3737
|
+
} })).ok;
|
|
3738
|
+
} catch {
|
|
3739
|
+
return false;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
};
|
|
3743
|
+
|
|
3744
|
+
//#endregion
|
|
3745
|
+
//#region ../ai-provider/src/acp-provider.ts
|
|
3746
|
+
/**
|
|
3747
|
+
* ACP-based AI provider (Agent Client Protocol)
|
|
3748
|
+
* Communicates with coding agents via standardized protocol
|
|
3749
|
+
*
|
|
3750
|
+
* Note: ACP support is experimental and not yet fully implemented.
|
|
3751
|
+
* The ACP SDK API is still evolving.
|
|
3752
|
+
*
|
|
3753
|
+
* @see https://agentclientprotocol.com/
|
|
3754
|
+
*/
|
|
3755
|
+
var ACPProvider = class {
|
|
3756
|
+
name;
|
|
3757
|
+
type = "acp";
|
|
3758
|
+
constructor(config) {
|
|
3759
|
+
this.config = config;
|
|
3760
|
+
this.name = config.name;
|
|
3761
|
+
}
|
|
3762
|
+
async complete(_options) {
|
|
3763
|
+
throw new Error(`ACP provider "${this.name}" is not yet implemented. Command: ${this.config.command} ${this.config.args.join(" ")}`);
|
|
3764
|
+
}
|
|
3765
|
+
async isAvailable() {
|
|
3766
|
+
return false;
|
|
3767
|
+
}
|
|
3768
|
+
async dispose() {}
|
|
3769
|
+
};
|
|
3770
|
+
/**
|
|
3771
|
+
* Predefined ACP agent configurations
|
|
3772
|
+
*
|
|
3773
|
+
* Native ACP support:
|
|
3774
|
+
* - iFlow: `iflow --experimental-acp`
|
|
3775
|
+
* - Gemini: `gemini --experimental-acp`
|
|
3776
|
+
*
|
|
3777
|
+
* ACP adapters (by Zed team):
|
|
3778
|
+
* - Claude Code: https://github.com/zed-industries/claude-code-acp
|
|
3779
|
+
* - Codex: https://github.com/zed-industries/codex-acp
|
|
3780
|
+
*/
|
|
3781
|
+
const ACPAgents = {
|
|
3782
|
+
iflow: {
|
|
3783
|
+
type: "acp",
|
|
3784
|
+
name: "iFlow",
|
|
3785
|
+
command: "iflow",
|
|
3786
|
+
args: ["--experimental-acp"]
|
|
3787
|
+
},
|
|
3788
|
+
gemini: {
|
|
3789
|
+
type: "acp",
|
|
3790
|
+
name: "Gemini",
|
|
3791
|
+
command: "gemini",
|
|
3792
|
+
args: ["--experimental-acp"]
|
|
3793
|
+
},
|
|
3794
|
+
claude: {
|
|
3795
|
+
type: "acp",
|
|
3796
|
+
name: "Claude Code",
|
|
3797
|
+
command: "claude-code-acp",
|
|
3798
|
+
args: []
|
|
3799
|
+
},
|
|
3800
|
+
codex: {
|
|
3801
|
+
type: "acp",
|
|
3802
|
+
name: "Codex",
|
|
3803
|
+
command: "codex-acp",
|
|
3804
|
+
args: []
|
|
3805
|
+
}
|
|
3806
|
+
};
|
|
3807
|
+
|
|
3808
|
+
//#endregion
|
|
3809
|
+
//#region ../ai-provider/src/manager.ts
|
|
3810
|
+
/**
|
|
3811
|
+
* Factory function to create a provider from configuration
|
|
3812
|
+
*/
|
|
3813
|
+
function createProvider(config) {
|
|
3814
|
+
switch (config.type) {
|
|
3815
|
+
case "api": return new APIProvider(config);
|
|
3816
|
+
case "acp": return new ACPProvider(config);
|
|
3817
|
+
default: throw new Error(`Unknown provider type: ${config.type}`);
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
/**
|
|
3821
|
+
* Provider manager for handling multiple AI providers
|
|
3822
|
+
*/
|
|
3823
|
+
var ProviderManager = class {
|
|
3824
|
+
providers = /* @__PURE__ */ new Map();
|
|
3825
|
+
defaultApiName = null;
|
|
3826
|
+
defaultAcpName = null;
|
|
3827
|
+
constructor(registry) {
|
|
3828
|
+
if (registry) this.loadRegistry(registry);
|
|
3829
|
+
}
|
|
3830
|
+
/**
|
|
3831
|
+
* Load providers from a registry configuration
|
|
3832
|
+
*/
|
|
3833
|
+
loadRegistry(registry) {
|
|
3834
|
+
for (const [name, config] of Object.entries(registry.providers)) {
|
|
3835
|
+
const provider = createProvider(config);
|
|
3836
|
+
this.providers.set(name, provider);
|
|
3837
|
+
}
|
|
3838
|
+
if (registry.defaultApi) this.defaultApiName = registry.defaultApi;
|
|
3839
|
+
if (registry.defaultAcp) this.defaultAcpName = registry.defaultAcp;
|
|
3840
|
+
}
|
|
3841
|
+
/**
|
|
3842
|
+
* Register a provider
|
|
3843
|
+
*/
|
|
3844
|
+
register(name, provider) {
|
|
3845
|
+
this.providers.set(name, provider);
|
|
3846
|
+
}
|
|
3847
|
+
/**
|
|
3848
|
+
* Get a provider by name
|
|
3849
|
+
*/
|
|
3850
|
+
get(name) {
|
|
3851
|
+
return this.providers.get(name);
|
|
3852
|
+
}
|
|
3853
|
+
/**
|
|
3854
|
+
* Get the default API provider
|
|
3855
|
+
*/
|
|
3856
|
+
getDefaultApi() {
|
|
3857
|
+
if (!this.defaultApiName) return void 0;
|
|
3858
|
+
return this.providers.get(this.defaultApiName);
|
|
3859
|
+
}
|
|
3860
|
+
/**
|
|
3861
|
+
* Get the default ACP provider
|
|
3862
|
+
*/
|
|
3863
|
+
getDefaultAcp() {
|
|
3864
|
+
if (!this.defaultAcpName) return void 0;
|
|
3865
|
+
return this.providers.get(this.defaultAcpName);
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Set the default API provider
|
|
3869
|
+
*/
|
|
3870
|
+
setDefaultApi(name) {
|
|
3871
|
+
const provider = this.providers.get(name);
|
|
3872
|
+
if (!provider) throw new Error(`Provider '${name}' not found`);
|
|
3873
|
+
if (provider.type !== "api") throw new Error(`Provider '${name}' is not an API provider`);
|
|
3874
|
+
this.defaultApiName = name;
|
|
3875
|
+
}
|
|
3876
|
+
/**
|
|
3877
|
+
* Set the default ACP provider
|
|
3878
|
+
*/
|
|
3879
|
+
setDefaultAcp(name) {
|
|
3880
|
+
const provider = this.providers.get(name);
|
|
3881
|
+
if (!provider) throw new Error(`Provider '${name}' not found`);
|
|
3882
|
+
if (provider.type !== "acp") throw new Error(`Provider '${name}' is not an ACP provider`);
|
|
3883
|
+
this.defaultAcpName = name;
|
|
3884
|
+
}
|
|
3885
|
+
/**
|
|
3886
|
+
* List all registered providers
|
|
3887
|
+
*/
|
|
3888
|
+
list() {
|
|
3889
|
+
return Array.from(this.providers.entries()).map(([name, provider]) => ({
|
|
3890
|
+
name,
|
|
3891
|
+
type: provider.type
|
|
3892
|
+
}));
|
|
3893
|
+
}
|
|
3894
|
+
/**
|
|
3895
|
+
* Check availability of all providers
|
|
3896
|
+
*/
|
|
3897
|
+
async checkAvailability() {
|
|
3898
|
+
const results = /* @__PURE__ */ new Map();
|
|
3899
|
+
await Promise.all(Array.from(this.providers.entries()).map(async ([name, provider]) => {
|
|
3900
|
+
const available = await provider.isAvailable();
|
|
3901
|
+
results.set(name, available);
|
|
3902
|
+
}));
|
|
3903
|
+
return results;
|
|
3904
|
+
}
|
|
3905
|
+
/**
|
|
3906
|
+
* Dispose all providers
|
|
3907
|
+
*/
|
|
3908
|
+
async dispose() {
|
|
3909
|
+
await Promise.all(Array.from(this.providers.values()).map((provider) => provider.dispose?.()));
|
|
3910
|
+
this.providers.clear();
|
|
3911
|
+
}
|
|
3912
|
+
};
|
|
3913
|
+
|
|
3914
|
+
//#endregion
|
|
3915
|
+
//#region ../../node_modules/.pnpm/@hono+node-server@1.19.6_hono@4.10.6/node_modules/@hono/node-server/dist/index.mjs
|
|
3916
|
+
var RequestError = class extends Error {
|
|
3917
|
+
constructor(message, options) {
|
|
3918
|
+
super(message, options);
|
|
3919
|
+
this.name = "RequestError";
|
|
3920
|
+
}
|
|
3921
|
+
};
|
|
3922
|
+
var toRequestError = (e) => {
|
|
3923
|
+
if (e instanceof RequestError) return e;
|
|
3924
|
+
return new RequestError(e.message, { cause: e });
|
|
3925
|
+
};
|
|
3926
|
+
var GlobalRequest = global.Request;
|
|
3927
|
+
var Request$1 = class extends GlobalRequest {
|
|
3928
|
+
constructor(input, options) {
|
|
3929
|
+
if (typeof input === "object" && getRequestCache in input) input = input[getRequestCache]();
|
|
3930
|
+
if (typeof options?.body?.getReader !== "undefined") options.duplex ??= "half";
|
|
3931
|
+
super(input, options);
|
|
3932
|
+
}
|
|
3933
|
+
};
|
|
3934
|
+
var newHeadersFromIncoming = (incoming) => {
|
|
3935
|
+
const headerRecord = [];
|
|
3936
|
+
const rawHeaders = incoming.rawHeaders;
|
|
3937
|
+
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
3938
|
+
const { [i]: key, [i + 1]: value } = rawHeaders;
|
|
3939
|
+
if (key.charCodeAt(0) !== 58) headerRecord.push([key, value]);
|
|
3940
|
+
}
|
|
3941
|
+
return new Headers(headerRecord);
|
|
3942
|
+
};
|
|
3943
|
+
var wrapBodyStream = Symbol("wrapBodyStream");
|
|
3944
|
+
var newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
|
|
3945
|
+
const init = {
|
|
3946
|
+
method,
|
|
3947
|
+
headers,
|
|
3948
|
+
signal: abortController.signal
|
|
3949
|
+
};
|
|
3950
|
+
if (method === "TRACE") {
|
|
3951
|
+
init.method = "GET";
|
|
3952
|
+
const req = new Request$1(url, init);
|
|
3953
|
+
Object.defineProperty(req, "method", { get() {
|
|
3954
|
+
return "TRACE";
|
|
3955
|
+
} });
|
|
3956
|
+
return req;
|
|
3957
|
+
}
|
|
3958
|
+
if (!(method === "GET" || method === "HEAD")) if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) {
|
|
3959
|
+
controller.enqueue(incoming.rawBody);
|
|
3960
|
+
controller.close();
|
|
3961
|
+
} });
|
|
3962
|
+
else if (incoming[wrapBodyStream]) {
|
|
3963
|
+
let reader;
|
|
3964
|
+
init.body = new ReadableStream({ async pull(controller) {
|
|
3965
|
+
try {
|
|
3966
|
+
reader ||= Readable.toWeb(incoming).getReader();
|
|
3967
|
+
const { done, value } = await reader.read();
|
|
3968
|
+
if (done) controller.close();
|
|
3969
|
+
else controller.enqueue(value);
|
|
3970
|
+
} catch (error) {
|
|
3971
|
+
controller.error(error);
|
|
3972
|
+
}
|
|
3973
|
+
} });
|
|
3974
|
+
} else init.body = Readable.toWeb(incoming);
|
|
3975
|
+
return new Request$1(url, init);
|
|
3976
|
+
};
|
|
3977
|
+
var getRequestCache = Symbol("getRequestCache");
|
|
3978
|
+
var requestCache = Symbol("requestCache");
|
|
3979
|
+
var incomingKey = Symbol("incomingKey");
|
|
3980
|
+
var urlKey = Symbol("urlKey");
|
|
3981
|
+
var headersKey = Symbol("headersKey");
|
|
3982
|
+
var abortControllerKey = Symbol("abortControllerKey");
|
|
3983
|
+
var requestPrototype = {
|
|
3984
|
+
get method() {
|
|
3985
|
+
return this[incomingKey].method || "GET";
|
|
3986
|
+
},
|
|
3987
|
+
get url() {
|
|
3988
|
+
return this[urlKey];
|
|
3989
|
+
},
|
|
3990
|
+
get headers() {
|
|
3991
|
+
return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
|
|
3992
|
+
},
|
|
3993
|
+
[Symbol("getAbortController")]() {
|
|
3994
|
+
this[getRequestCache]();
|
|
3995
|
+
return this[abortControllerKey];
|
|
3996
|
+
},
|
|
3997
|
+
[getRequestCache]() {
|
|
3998
|
+
this[abortControllerKey] ||= new AbortController();
|
|
3999
|
+
return this[requestCache] ||= newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], this[abortControllerKey]);
|
|
4000
|
+
}
|
|
4001
|
+
};
|
|
4002
|
+
[
|
|
4003
|
+
"body",
|
|
4004
|
+
"bodyUsed",
|
|
4005
|
+
"cache",
|
|
4006
|
+
"credentials",
|
|
4007
|
+
"destination",
|
|
4008
|
+
"integrity",
|
|
4009
|
+
"mode",
|
|
4010
|
+
"redirect",
|
|
4011
|
+
"referrer",
|
|
4012
|
+
"referrerPolicy",
|
|
4013
|
+
"signal",
|
|
4014
|
+
"keepalive"
|
|
4015
|
+
].forEach((k) => {
|
|
4016
|
+
Object.defineProperty(requestPrototype, k, { get() {
|
|
4017
|
+
return this[getRequestCache]()[k];
|
|
4018
|
+
} });
|
|
4019
|
+
});
|
|
4020
|
+
[
|
|
4021
|
+
"arrayBuffer",
|
|
4022
|
+
"blob",
|
|
4023
|
+
"clone",
|
|
4024
|
+
"formData",
|
|
4025
|
+
"json",
|
|
4026
|
+
"text"
|
|
4027
|
+
].forEach((k) => {
|
|
4028
|
+
Object.defineProperty(requestPrototype, k, { value: function() {
|
|
4029
|
+
return this[getRequestCache]()[k]();
|
|
4030
|
+
} });
|
|
4031
|
+
});
|
|
4032
|
+
Object.setPrototypeOf(requestPrototype, Request$1.prototype);
|
|
4033
|
+
var newRequest = (incoming, defaultHostname) => {
|
|
4034
|
+
const req = Object.create(requestPrototype);
|
|
4035
|
+
req[incomingKey] = incoming;
|
|
4036
|
+
const incomingUrl = incoming.url || "";
|
|
4037
|
+
if (incomingUrl[0] !== "/" && (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) {
|
|
4038
|
+
if (incoming instanceof Http2ServerRequest) throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
4039
|
+
try {
|
|
4040
|
+
req[urlKey] = new URL(incomingUrl).href;
|
|
4041
|
+
} catch (e) {
|
|
4042
|
+
throw new RequestError("Invalid absolute URL", { cause: e });
|
|
4043
|
+
}
|
|
4044
|
+
return req;
|
|
4045
|
+
}
|
|
4046
|
+
const host = (incoming instanceof Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
|
|
4047
|
+
if (!host) throw new RequestError("Missing host header");
|
|
4048
|
+
let scheme;
|
|
4049
|
+
if (incoming instanceof Http2ServerRequest) {
|
|
4050
|
+
scheme = incoming.scheme;
|
|
4051
|
+
if (!(scheme === "http" || scheme === "https")) throw new RequestError("Unsupported scheme");
|
|
4052
|
+
} else scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
4053
|
+
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
4054
|
+
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) throw new RequestError("Invalid host header");
|
|
4055
|
+
req[urlKey] = url.href;
|
|
4056
|
+
return req;
|
|
4057
|
+
};
|
|
4058
|
+
var responseCache = Symbol("responseCache");
|
|
4059
|
+
var getResponseCache = Symbol("getResponseCache");
|
|
4060
|
+
var cacheKey = Symbol("cache");
|
|
4061
|
+
var GlobalResponse = global.Response;
|
|
4062
|
+
var Response2 = class _Response {
|
|
4063
|
+
#body;
|
|
4064
|
+
#init;
|
|
4065
|
+
[getResponseCache]() {
|
|
4066
|
+
delete this[cacheKey];
|
|
4067
|
+
return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
|
|
4068
|
+
}
|
|
4069
|
+
constructor(body, init) {
|
|
4070
|
+
let headers;
|
|
4071
|
+
this.#body = body;
|
|
4072
|
+
if (init instanceof _Response) {
|
|
4073
|
+
const cachedGlobalResponse = init[responseCache];
|
|
4074
|
+
if (cachedGlobalResponse) {
|
|
4075
|
+
this.#init = cachedGlobalResponse;
|
|
4076
|
+
this[getResponseCache]();
|
|
4077
|
+
return;
|
|
4078
|
+
} else {
|
|
4079
|
+
this.#init = init.#init;
|
|
4080
|
+
headers = new Headers(init.#init.headers);
|
|
4081
|
+
}
|
|
4082
|
+
} else this.#init = init;
|
|
4083
|
+
if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) {
|
|
4084
|
+
headers ||= init?.headers || { "content-type": "text/plain; charset=UTF-8" };
|
|
4085
|
+
this[cacheKey] = [
|
|
4086
|
+
init?.status || 200,
|
|
4087
|
+
body,
|
|
4088
|
+
headers
|
|
4089
|
+
];
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
get headers() {
|
|
4093
|
+
const cache = this[cacheKey];
|
|
4094
|
+
if (cache) {
|
|
4095
|
+
if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2]);
|
|
4096
|
+
return cache[2];
|
|
4176
4097
|
}
|
|
4098
|
+
return this[getResponseCache]().headers;
|
|
4177
4099
|
}
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
//#endregion
|
|
4181
|
-
//#region ../ai-provider/src/acp-provider.ts
|
|
4182
|
-
/**
|
|
4183
|
-
* ACP-based AI provider (Agent Client Protocol)
|
|
4184
|
-
* Communicates with coding agents via standardized protocol
|
|
4185
|
-
*
|
|
4186
|
-
* Note: ACP support is experimental and not yet fully implemented.
|
|
4187
|
-
* The ACP SDK API is still evolving.
|
|
4188
|
-
*
|
|
4189
|
-
* @see https://agentclientprotocol.com/
|
|
4190
|
-
*/
|
|
4191
|
-
var ACPProvider = class {
|
|
4192
|
-
name;
|
|
4193
|
-
type = "acp";
|
|
4194
|
-
constructor(config) {
|
|
4195
|
-
this.config = config;
|
|
4196
|
-
this.name = config.name;
|
|
4197
|
-
}
|
|
4198
|
-
async complete(_options) {
|
|
4199
|
-
throw new Error(`ACP provider "${this.name}" is not yet implemented. Command: ${this.config.command} ${this.config.args.join(" ")}`);
|
|
4200
|
-
}
|
|
4201
|
-
async isAvailable() {
|
|
4202
|
-
return false;
|
|
4100
|
+
get status() {
|
|
4101
|
+
return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
|
|
4203
4102
|
}
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
* Predefined ACP agent configurations
|
|
4208
|
-
*
|
|
4209
|
-
* Native ACP support:
|
|
4210
|
-
* - iFlow: `iflow --experimental-acp`
|
|
4211
|
-
* - Gemini: `gemini --experimental-acp`
|
|
4212
|
-
*
|
|
4213
|
-
* ACP adapters (by Zed team):
|
|
4214
|
-
* - Claude Code: https://github.com/zed-industries/claude-code-acp
|
|
4215
|
-
* - Codex: https://github.com/zed-industries/codex-acp
|
|
4216
|
-
*/
|
|
4217
|
-
const ACPAgents = {
|
|
4218
|
-
iflow: {
|
|
4219
|
-
type: "acp",
|
|
4220
|
-
name: "iFlow",
|
|
4221
|
-
command: "iflow",
|
|
4222
|
-
args: ["--experimental-acp"]
|
|
4223
|
-
},
|
|
4224
|
-
gemini: {
|
|
4225
|
-
type: "acp",
|
|
4226
|
-
name: "Gemini",
|
|
4227
|
-
command: "gemini",
|
|
4228
|
-
args: ["--experimental-acp"]
|
|
4229
|
-
},
|
|
4230
|
-
claude: {
|
|
4231
|
-
type: "acp",
|
|
4232
|
-
name: "Claude Code",
|
|
4233
|
-
command: "claude-code-acp",
|
|
4234
|
-
args: []
|
|
4235
|
-
},
|
|
4236
|
-
codex: {
|
|
4237
|
-
type: "acp",
|
|
4238
|
-
name: "Codex",
|
|
4239
|
-
command: "codex-acp",
|
|
4240
|
-
args: []
|
|
4103
|
+
get ok() {
|
|
4104
|
+
const status = this.status;
|
|
4105
|
+
return status >= 200 && status < 300;
|
|
4241
4106
|
}
|
|
4242
4107
|
};
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
}
|
|
4108
|
+
[
|
|
4109
|
+
"body",
|
|
4110
|
+
"bodyUsed",
|
|
4111
|
+
"redirected",
|
|
4112
|
+
"statusText",
|
|
4113
|
+
"trailers",
|
|
4114
|
+
"type",
|
|
4115
|
+
"url"
|
|
4116
|
+
].forEach((k) => {
|
|
4117
|
+
Object.defineProperty(Response2.prototype, k, { get() {
|
|
4118
|
+
return this[getResponseCache]()[k];
|
|
4119
|
+
} });
|
|
4120
|
+
});
|
|
4121
|
+
[
|
|
4122
|
+
"arrayBuffer",
|
|
4123
|
+
"blob",
|
|
4124
|
+
"clone",
|
|
4125
|
+
"formData",
|
|
4126
|
+
"json",
|
|
4127
|
+
"text"
|
|
4128
|
+
].forEach((k) => {
|
|
4129
|
+
Object.defineProperty(Response2.prototype, k, { value: function() {
|
|
4130
|
+
return this[getResponseCache]()[k]();
|
|
4131
|
+
} });
|
|
4132
|
+
});
|
|
4133
|
+
Object.setPrototypeOf(Response2, GlobalResponse);
|
|
4134
|
+
Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
|
|
4135
|
+
async function readWithoutBlocking(readPromise) {
|
|
4136
|
+
return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]);
|
|
4255
4137
|
}
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
loadRegistry(registry) {
|
|
4270
|
-
for (const [name, config] of Object.entries(registry.providers)) {
|
|
4271
|
-
const provider = createProvider(config);
|
|
4272
|
-
this.providers.set(name, provider);
|
|
4273
|
-
}
|
|
4274
|
-
if (registry.defaultApi) this.defaultApiName = registry.defaultApi;
|
|
4275
|
-
if (registry.defaultAcp) this.defaultAcpName = registry.defaultAcp;
|
|
4276
|
-
}
|
|
4277
|
-
/**
|
|
4278
|
-
* Register a provider
|
|
4279
|
-
*/
|
|
4280
|
-
register(name, provider) {
|
|
4281
|
-
this.providers.set(name, provider);
|
|
4282
|
-
}
|
|
4283
|
-
/**
|
|
4284
|
-
* Get a provider by name
|
|
4285
|
-
*/
|
|
4286
|
-
get(name) {
|
|
4287
|
-
return this.providers.get(name);
|
|
4138
|
+
function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {
|
|
4139
|
+
const cancel = (error) => {
|
|
4140
|
+
reader.cancel(error).catch(() => {});
|
|
4141
|
+
};
|
|
4142
|
+
writable.on("close", cancel);
|
|
4143
|
+
writable.on("error", cancel);
|
|
4144
|
+
(currentReadPromise ?? reader.read()).then(flow, handleStreamError);
|
|
4145
|
+
return reader.closed.finally(() => {
|
|
4146
|
+
writable.off("close", cancel);
|
|
4147
|
+
writable.off("error", cancel);
|
|
4148
|
+
});
|
|
4149
|
+
function handleStreamError(error) {
|
|
4150
|
+
if (error) writable.destroy(error);
|
|
4288
4151
|
}
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
*/
|
|
4292
|
-
getDefaultApi() {
|
|
4293
|
-
if (!this.defaultApiName) return void 0;
|
|
4294
|
-
return this.providers.get(this.defaultApiName);
|
|
4152
|
+
function onDrain() {
|
|
4153
|
+
reader.read().then(flow, handleStreamError);
|
|
4295
4154
|
}
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4155
|
+
function flow({ done, value }) {
|
|
4156
|
+
try {
|
|
4157
|
+
if (done) writable.end();
|
|
4158
|
+
else if (!writable.write(value)) writable.once("drain", onDrain);
|
|
4159
|
+
else return reader.read().then(flow, handleStreamError);
|
|
4160
|
+
} catch (e) {
|
|
4161
|
+
handleStreamError(e);
|
|
4162
|
+
}
|
|
4302
4163
|
}
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4164
|
+
}
|
|
4165
|
+
function writeFromReadableStream(stream, writable) {
|
|
4166
|
+
if (stream.locked) throw new TypeError("ReadableStream is locked.");
|
|
4167
|
+
else if (writable.destroyed) return;
|
|
4168
|
+
return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
|
|
4169
|
+
}
|
|
4170
|
+
var buildOutgoingHttpHeaders = (headers) => {
|
|
4171
|
+
const res = {};
|
|
4172
|
+
if (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0);
|
|
4173
|
+
const cookies = [];
|
|
4174
|
+
for (const [k, v] of headers) if (k === "set-cookie") cookies.push(v);
|
|
4175
|
+
else res[k] = v;
|
|
4176
|
+
if (cookies.length > 0) res["set-cookie"] = cookies;
|
|
4177
|
+
res["content-type"] ??= "text/plain; charset=UTF-8";
|
|
4178
|
+
return res;
|
|
4179
|
+
};
|
|
4180
|
+
var X_ALREADY_SENT = "x-hono-already-sent";
|
|
4181
|
+
var webFetch = global.fetch;
|
|
4182
|
+
if (typeof global.crypto === "undefined") global.crypto = crypto;
|
|
4183
|
+
global.fetch = (info, init) => {
|
|
4184
|
+
init = {
|
|
4185
|
+
compress: false,
|
|
4186
|
+
...init
|
|
4187
|
+
};
|
|
4188
|
+
return webFetch(info, init);
|
|
4189
|
+
};
|
|
4190
|
+
var outgoingEnded = Symbol("outgoingEnded");
|
|
4191
|
+
var handleRequestError = () => new Response(null, { status: 400 });
|
|
4192
|
+
var handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 });
|
|
4193
|
+
var handleResponseError = (e, outgoing) => {
|
|
4194
|
+
const err = e instanceof Error ? e : new Error("unknown error", { cause: e });
|
|
4195
|
+
if (err.code === "ERR_STREAM_PREMATURE_CLOSE") console.info("The user aborted a request.");
|
|
4196
|
+
else {
|
|
4197
|
+
console.error(e);
|
|
4198
|
+
if (!outgoing.headersSent) outgoing.writeHead(500, { "Content-Type": "text/plain" });
|
|
4199
|
+
outgoing.end(`Error: ${err.message}`);
|
|
4200
|
+
outgoing.destroy(err);
|
|
4311
4201
|
}
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4202
|
+
};
|
|
4203
|
+
var flushHeaders = (outgoing) => {
|
|
4204
|
+
if ("flushHeaders" in outgoing && outgoing.writable) outgoing.flushHeaders();
|
|
4205
|
+
};
|
|
4206
|
+
var responseViaCache = async (res, outgoing) => {
|
|
4207
|
+
let [status, body, header] = res[cacheKey];
|
|
4208
|
+
if (header instanceof Headers) header = buildOutgoingHttpHeaders(header);
|
|
4209
|
+
if (typeof body === "string") header["Content-Length"] = Buffer.byteLength(body);
|
|
4210
|
+
else if (body instanceof Uint8Array) header["Content-Length"] = body.byteLength;
|
|
4211
|
+
else if (body instanceof Blob) header["Content-Length"] = body.size;
|
|
4212
|
+
outgoing.writeHead(status, header);
|
|
4213
|
+
if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body);
|
|
4214
|
+
else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
4215
|
+
else {
|
|
4216
|
+
flushHeaders(outgoing);
|
|
4217
|
+
await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
|
|
4320
4218
|
}
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4219
|
+
outgoing[outgoingEnded]?.();
|
|
4220
|
+
};
|
|
4221
|
+
var isPromise$1 = (res) => typeof res.then === "function";
|
|
4222
|
+
var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
4223
|
+
if (isPromise$1(res)) if (options.errorHandler) try {
|
|
4224
|
+
res = await res;
|
|
4225
|
+
} catch (err) {
|
|
4226
|
+
const errRes = await options.errorHandler(err);
|
|
4227
|
+
if (!errRes) return;
|
|
4228
|
+
res = errRes;
|
|
4329
4229
|
}
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
const
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4230
|
+
else res = await res.catch(handleFetchError);
|
|
4231
|
+
if (cacheKey in res) return responseViaCache(res, outgoing);
|
|
4232
|
+
const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
|
|
4233
|
+
if (res.body) {
|
|
4234
|
+
const reader = res.body.getReader();
|
|
4235
|
+
const values = [];
|
|
4236
|
+
let done = false;
|
|
4237
|
+
let currentReadPromise = void 0;
|
|
4238
|
+
if (resHeaderRecord["transfer-encoding"] !== "chunked") {
|
|
4239
|
+
let maxReadCount = 2;
|
|
4240
|
+
for (let i = 0; i < maxReadCount; i++) {
|
|
4241
|
+
currentReadPromise ||= reader.read();
|
|
4242
|
+
const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => {
|
|
4243
|
+
console.error(e);
|
|
4244
|
+
done = true;
|
|
4245
|
+
});
|
|
4246
|
+
if (!chunk) {
|
|
4247
|
+
if (i === 1) {
|
|
4248
|
+
await new Promise((resolve$2) => setTimeout(resolve$2));
|
|
4249
|
+
maxReadCount = 3;
|
|
4250
|
+
continue;
|
|
4251
|
+
}
|
|
4252
|
+
break;
|
|
4253
|
+
}
|
|
4254
|
+
currentReadPromise = void 0;
|
|
4255
|
+
if (chunk.value) values.push(chunk.value);
|
|
4256
|
+
if (chunk.done) {
|
|
4257
|
+
done = true;
|
|
4258
|
+
break;
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
if (done && !("content-length" in resHeaderRecord)) resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0);
|
|
4262
|
+
}
|
|
4263
|
+
outgoing.writeHead(res.status, resHeaderRecord);
|
|
4264
|
+
values.forEach((value) => {
|
|
4265
|
+
outgoing.write(value);
|
|
4266
|
+
});
|
|
4267
|
+
if (done) outgoing.end();
|
|
4268
|
+
else {
|
|
4269
|
+
if (values.length === 0) flushHeaders(outgoing);
|
|
4270
|
+
await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise);
|
|
4271
|
+
}
|
|
4272
|
+
} else if (resHeaderRecord[X_ALREADY_SENT]) {} else {
|
|
4273
|
+
outgoing.writeHead(res.status, resHeaderRecord);
|
|
4274
|
+
outgoing.end();
|
|
4340
4275
|
}
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4276
|
+
outgoing[outgoingEnded]?.();
|
|
4277
|
+
};
|
|
4278
|
+
var getRequestListener = (fetchCallback, options = {}) => {
|
|
4279
|
+
const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
|
|
4280
|
+
if (options.overrideGlobalObjects !== false && global.Request !== Request$1) {
|
|
4281
|
+
Object.defineProperty(global, "Request", { value: Request$1 });
|
|
4282
|
+
Object.defineProperty(global, "Response", { value: Response2 });
|
|
4347
4283
|
}
|
|
4284
|
+
return async (incoming, outgoing) => {
|
|
4285
|
+
let res, req;
|
|
4286
|
+
try {
|
|
4287
|
+
req = newRequest(incoming, options.hostname);
|
|
4288
|
+
let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
|
|
4289
|
+
if (!incomingEnded) {
|
|
4290
|
+
incoming[wrapBodyStream] = true;
|
|
4291
|
+
incoming.on("end", () => {
|
|
4292
|
+
incomingEnded = true;
|
|
4293
|
+
});
|
|
4294
|
+
if (incoming instanceof Http2ServerRequest) outgoing[outgoingEnded] = () => {
|
|
4295
|
+
if (!incomingEnded) setTimeout(() => {
|
|
4296
|
+
if (!incomingEnded) setTimeout(() => {
|
|
4297
|
+
incoming.destroy();
|
|
4298
|
+
outgoing.destroy();
|
|
4299
|
+
});
|
|
4300
|
+
});
|
|
4301
|
+
};
|
|
4302
|
+
}
|
|
4303
|
+
outgoing.on("close", () => {
|
|
4304
|
+
if (req[abortControllerKey]) {
|
|
4305
|
+
if (incoming.errored) req[abortControllerKey].abort(incoming.errored.toString());
|
|
4306
|
+
else if (!outgoing.writableFinished) req[abortControllerKey].abort("Client connection prematurely closed.");
|
|
4307
|
+
}
|
|
4308
|
+
if (!incomingEnded) setTimeout(() => {
|
|
4309
|
+
if (!incomingEnded) setTimeout(() => {
|
|
4310
|
+
incoming.destroy();
|
|
4311
|
+
});
|
|
4312
|
+
});
|
|
4313
|
+
});
|
|
4314
|
+
res = fetchCallback(req, {
|
|
4315
|
+
incoming,
|
|
4316
|
+
outgoing
|
|
4317
|
+
});
|
|
4318
|
+
if (cacheKey in res) return responseViaCache(res, outgoing);
|
|
4319
|
+
} catch (e) {
|
|
4320
|
+
if (!res) if (options.errorHandler) {
|
|
4321
|
+
res = await options.errorHandler(req ? e : toRequestError(e));
|
|
4322
|
+
if (!res) return;
|
|
4323
|
+
} else if (!req) res = handleRequestError();
|
|
4324
|
+
else res = handleFetchError(e);
|
|
4325
|
+
else return handleResponseError(e, outgoing);
|
|
4326
|
+
}
|
|
4327
|
+
try {
|
|
4328
|
+
return await responseViaResponseObject(res, outgoing, options);
|
|
4329
|
+
} catch (e) {
|
|
4330
|
+
return handleResponseError(e, outgoing);
|
|
4331
|
+
}
|
|
4332
|
+
};
|
|
4333
|
+
};
|
|
4334
|
+
var createAdaptorServer = (options) => {
|
|
4335
|
+
const fetchCallback = options.fetch;
|
|
4336
|
+
const requestListener = getRequestListener(fetchCallback, {
|
|
4337
|
+
hostname: options.hostname,
|
|
4338
|
+
overrideGlobalObjects: options.overrideGlobalObjects,
|
|
4339
|
+
autoCleanupIncoming: options.autoCleanupIncoming
|
|
4340
|
+
});
|
|
4341
|
+
return (options.createServer || createServer)(options.serverOptions || {}, requestListener);
|
|
4342
|
+
};
|
|
4343
|
+
var serve = (options, listeningListener) => {
|
|
4344
|
+
const server = createAdaptorServer(options);
|
|
4345
|
+
server.listen(options?.port ?? 3e3, options.hostname, () => {
|
|
4346
|
+
const serverInfo = server.address();
|
|
4347
|
+
listeningListener && listeningListener(serverInfo);
|
|
4348
|
+
});
|
|
4349
|
+
return server;
|
|
4348
4350
|
};
|
|
4349
4351
|
|
|
4350
4352
|
//#endregion
|
|
@@ -10938,7 +10940,7 @@ var require_extension = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm
|
|
|
10938
10940
|
* @return {String} A string representing the given object
|
|
10939
10941
|
* @public
|
|
10940
10942
|
*/
|
|
10941
|
-
function format$
|
|
10943
|
+
function format$2(extensions) {
|
|
10942
10944
|
return Object.keys(extensions).map((extension$1) => {
|
|
10943
10945
|
let configurations = extensions[extension$1];
|
|
10944
10946
|
if (!Array.isArray(configurations)) configurations = [configurations];
|
|
@@ -10952,7 +10954,7 @@ var require_extension = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm
|
|
|
10952
10954
|
}).join(", ");
|
|
10953
10955
|
}
|
|
10954
10956
|
module.exports = {
|
|
10955
|
-
format: format$
|
|
10957
|
+
format: format$2,
|
|
10956
10958
|
parse: parse$2
|
|
10957
10959
|
};
|
|
10958
10960
|
}) });
|
|
@@ -10974,7 +10976,7 @@ var require_websocket = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm
|
|
|
10974
10976
|
const { isBlob } = require_validation();
|
|
10975
10977
|
const { BINARY_TYPES, EMPTY_BUFFER, GUID: GUID$1, kForOnEventAttribute, kListener, kStatusCode, kWebSocket: kWebSocket$1, NOOP } = require_constants();
|
|
10976
10978
|
const { EventTarget: { addEventListener: addEventListener$1, removeEventListener } } = require_event_target();
|
|
10977
|
-
const { format, parse: parse$1 } = require_extension();
|
|
10979
|
+
const { format: format$1, parse: parse$1 } = require_extension();
|
|
10978
10980
|
const { toBuffer } = require_buffer_util();
|
|
10979
10981
|
const closeTimeout = 30 * 1e3;
|
|
10980
10982
|
const kAborted = Symbol("kAborted");
|
|
@@ -11535,7 +11537,7 @@ var require_websocket = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm
|
|
|
11535
11537
|
opts.timeout = opts.handshakeTimeout;
|
|
11536
11538
|
if (opts.perMessageDeflate) {
|
|
11537
11539
|
perMessageDeflate = new PerMessageDeflate$1(opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, false, opts.maxPayload);
|
|
11538
|
-
opts.headers["Sec-WebSocket-Extensions"] = format({ [PerMessageDeflate$1.extensionName]: perMessageDeflate.offer() });
|
|
11540
|
+
opts.headers["Sec-WebSocket-Extensions"] = format$1({ [PerMessageDeflate$1.extensionName]: perMessageDeflate.offer() });
|
|
11539
11541
|
}
|
|
11540
11542
|
if (protocols.length) {
|
|
11541
11543
|
for (const protocol of protocols) {
|
|
@@ -12810,93 +12812,423 @@ var ReactiveContext = class {
|
|
|
12810
12812
|
/** 是否已销毁 */
|
|
12811
12813
|
destroyed = false;
|
|
12812
12814
|
/**
|
|
12813
|
-
* 追踪依赖
|
|
12814
|
-
* 由 ReactiveState.get() 调用
|
|
12815
|
+
* 追踪依赖
|
|
12816
|
+
* 由 ReactiveState.get() 调用
|
|
12817
|
+
*/
|
|
12818
|
+
track(state) {
|
|
12819
|
+
if (!this.destroyed) this.dependencies.add(state);
|
|
12820
|
+
}
|
|
12821
|
+
/**
|
|
12822
|
+
* 通知变更
|
|
12823
|
+
* 由 ReactiveState.set() 调用
|
|
12824
|
+
*/
|
|
12825
|
+
notifyChange() {
|
|
12826
|
+
if (!this.destroyed && this.changePromise) this.changePromise.resolve();
|
|
12827
|
+
}
|
|
12828
|
+
/**
|
|
12829
|
+
* 运行响应式任务流
|
|
12830
|
+
* 每次依赖变更时重新执行任务并 yield 结果
|
|
12831
|
+
*
|
|
12832
|
+
* @param task 要执行的异步任务
|
|
12833
|
+
* @param signal 用于取消的 AbortSignal
|
|
12834
|
+
*/
|
|
12835
|
+
async *stream(task, signal) {
|
|
12836
|
+
try {
|
|
12837
|
+
while (!signal?.aborted && !this.destroyed) {
|
|
12838
|
+
this.clearDependencies();
|
|
12839
|
+
this.changePromise = createPromiseWithResolvers();
|
|
12840
|
+
yield await contextStorage.run(this, task);
|
|
12841
|
+
if (this.dependencies.size === 0) break;
|
|
12842
|
+
await Promise.race([this.changePromise.promise, signal ? this.waitForAbort(signal) : new Promise(() => {})]);
|
|
12843
|
+
if (signal?.aborted) break;
|
|
12844
|
+
}
|
|
12845
|
+
} finally {
|
|
12846
|
+
this.destroy();
|
|
12847
|
+
}
|
|
12848
|
+
}
|
|
12849
|
+
/**
|
|
12850
|
+
* 执行一次任务(非响应式)
|
|
12851
|
+
* 用于初始数据获取
|
|
12852
|
+
*/
|
|
12853
|
+
async runOnce(task) {
|
|
12854
|
+
return contextStorage.run(this, task);
|
|
12855
|
+
}
|
|
12856
|
+
/**
|
|
12857
|
+
* 清理依赖
|
|
12858
|
+
*/
|
|
12859
|
+
clearDependencies() {
|
|
12860
|
+
for (const state of this.dependencies) state.unsubscribe(this);
|
|
12861
|
+
this.dependencies.clear();
|
|
12862
|
+
}
|
|
12863
|
+
/**
|
|
12864
|
+
* 销毁上下文
|
|
12865
|
+
* @param reason 可选的销毁原因,如果提供则 reject changePromise
|
|
12866
|
+
*/
|
|
12867
|
+
destroy(reason) {
|
|
12868
|
+
this.destroyed = true;
|
|
12869
|
+
this.clearDependencies();
|
|
12870
|
+
if (reason && this.changePromise) this.changePromise.reject(reason);
|
|
12871
|
+
this.changePromise = void 0;
|
|
12872
|
+
}
|
|
12873
|
+
/**
|
|
12874
|
+
* 等待 AbortSignal
|
|
12875
|
+
*/
|
|
12876
|
+
waitForAbort(signal) {
|
|
12877
|
+
return new Promise((_, reject) => {
|
|
12878
|
+
if (signal.aborted) {
|
|
12879
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
12880
|
+
return;
|
|
12881
|
+
}
|
|
12882
|
+
signal.addEventListener("abort", () => {
|
|
12883
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
12884
|
+
});
|
|
12885
|
+
});
|
|
12886
|
+
}
|
|
12887
|
+
};
|
|
12888
|
+
|
|
12889
|
+
//#endregion
|
|
12890
|
+
//#region ../core/src/reactive-fs/project-watcher.ts
|
|
12891
|
+
/**
|
|
12892
|
+
* 获取路径的真实路径(解析符号链接)
|
|
12893
|
+
* 在 macOS 上,/var 是 /private/var 的符号链接
|
|
12894
|
+
*/
|
|
12895
|
+
function getRealPath$1(path$1) {
|
|
12896
|
+
try {
|
|
12897
|
+
return realpathSync(resolve$1(path$1));
|
|
12898
|
+
} catch {
|
|
12899
|
+
return resolve$1(path$1);
|
|
12900
|
+
}
|
|
12901
|
+
}
|
|
12902
|
+
/** 默认防抖时间 (ms) */
|
|
12903
|
+
const DEBOUNCE_MS$1 = 50;
|
|
12904
|
+
/** 默认忽略模式 */
|
|
12905
|
+
const DEFAULT_IGNORE = [
|
|
12906
|
+
"node_modules",
|
|
12907
|
+
".git",
|
|
12908
|
+
"**/.DS_Store"
|
|
12909
|
+
];
|
|
12910
|
+
/** 健康检查间隔 (ms) - 3秒 */
|
|
12911
|
+
const HEALTH_CHECK_INTERVAL_MS = 3e3;
|
|
12912
|
+
/**
|
|
12913
|
+
* 项目监听器
|
|
12914
|
+
*
|
|
12915
|
+
* 使用 @parcel/watcher 监听项目根目录,
|
|
12916
|
+
* 然后通过路径前缀匹配分发事件给订阅者。
|
|
12917
|
+
*
|
|
12918
|
+
* 特性:
|
|
12919
|
+
* - 单个 watcher 监听整个项目
|
|
12920
|
+
* - 自动处理新创建的目录
|
|
12921
|
+
* - 内置防抖机制
|
|
12922
|
+
* - 高性能原生实现
|
|
12923
|
+
*/
|
|
12924
|
+
var ProjectWatcher = class {
|
|
12925
|
+
projectDir;
|
|
12926
|
+
subscription = null;
|
|
12927
|
+
pathSubscriptions = /* @__PURE__ */ new Map();
|
|
12928
|
+
pendingEvents = [];
|
|
12929
|
+
debounceTimer = null;
|
|
12930
|
+
debounceMs;
|
|
12931
|
+
ignore;
|
|
12932
|
+
initialized = false;
|
|
12933
|
+
initPromise = null;
|
|
12934
|
+
healthCheckTimer = null;
|
|
12935
|
+
lastEventTime = 0;
|
|
12936
|
+
healthCheckPending = false;
|
|
12937
|
+
enableHealthCheck;
|
|
12938
|
+
constructor(projectDir, options = {}) {
|
|
12939
|
+
this.projectDir = getRealPath$1(projectDir);
|
|
12940
|
+
this.debounceMs = options.debounceMs ?? DEBOUNCE_MS$1;
|
|
12941
|
+
this.ignore = options.ignore ?? DEFAULT_IGNORE;
|
|
12942
|
+
this.enableHealthCheck = options.enableHealthCheck ?? true;
|
|
12943
|
+
}
|
|
12944
|
+
/**
|
|
12945
|
+
* 初始化 watcher
|
|
12946
|
+
* 懒加载,首次订阅时自动调用
|
|
12947
|
+
*/
|
|
12948
|
+
async init() {
|
|
12949
|
+
if (this.initialized) return;
|
|
12950
|
+
if (this.initPromise) return this.initPromise;
|
|
12951
|
+
this.initPromise = this.doInit();
|
|
12952
|
+
await this.initPromise;
|
|
12953
|
+
}
|
|
12954
|
+
async doInit() {
|
|
12955
|
+
this.subscription = await (await import("@parcel/watcher")).subscribe(this.projectDir, (err, events) => {
|
|
12956
|
+
if (err) {
|
|
12957
|
+
console.error("[ProjectWatcher] Error:", err);
|
|
12958
|
+
return;
|
|
12959
|
+
}
|
|
12960
|
+
this.handleEvents(events);
|
|
12961
|
+
}, { ignore: this.ignore });
|
|
12962
|
+
this.initialized = true;
|
|
12963
|
+
this.lastEventTime = Date.now();
|
|
12964
|
+
if (this.enableHealthCheck) this.startHealthCheck();
|
|
12965
|
+
}
|
|
12966
|
+
/**
|
|
12967
|
+
* 处理原始事件
|
|
12968
|
+
*/
|
|
12969
|
+
handleEvents(events) {
|
|
12970
|
+
this.lastEventTime = Date.now();
|
|
12971
|
+
this.healthCheckPending = false;
|
|
12972
|
+
const watchEvents = events.map((e) => ({
|
|
12973
|
+
type: e.type,
|
|
12974
|
+
path: e.path
|
|
12975
|
+
}));
|
|
12976
|
+
this.pendingEvents.push(...watchEvents);
|
|
12977
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
12978
|
+
this.debounceTimer = setTimeout(() => {
|
|
12979
|
+
this.flushEvents();
|
|
12980
|
+
}, this.debounceMs);
|
|
12981
|
+
}
|
|
12982
|
+
/**
|
|
12983
|
+
* 分发事件给订阅者
|
|
12815
12984
|
*/
|
|
12816
|
-
|
|
12817
|
-
|
|
12985
|
+
flushEvents() {
|
|
12986
|
+
const events = this.pendingEvents;
|
|
12987
|
+
this.pendingEvents = [];
|
|
12988
|
+
this.debounceTimer = null;
|
|
12989
|
+
if (events.length === 0) return;
|
|
12990
|
+
for (const sub of this.pathSubscriptions.values()) {
|
|
12991
|
+
const matchedEvents = events.filter((e) => this.matchPath(e, sub));
|
|
12992
|
+
if (matchedEvents.length > 0) try {
|
|
12993
|
+
sub.callback(matchedEvents);
|
|
12994
|
+
} catch (err) {
|
|
12995
|
+
console.error(`[ProjectWatcher] Callback error for ${sub.path}:`, err);
|
|
12996
|
+
}
|
|
12997
|
+
}
|
|
12818
12998
|
}
|
|
12819
12999
|
/**
|
|
12820
|
-
*
|
|
12821
|
-
* 由 ReactiveState.set() 调用
|
|
13000
|
+
* 检查事件是否匹配订阅
|
|
12822
13001
|
*/
|
|
12823
|
-
|
|
12824
|
-
|
|
13002
|
+
matchPath(event, sub) {
|
|
13003
|
+
const eventPath = event.path;
|
|
13004
|
+
if (sub.watchChildren) return eventPath.startsWith(sub.path + "/") || eventPath === sub.path;
|
|
13005
|
+
else {
|
|
13006
|
+
const eventDir = dirname$1(eventPath);
|
|
13007
|
+
return eventPath === sub.path || eventDir === sub.path;
|
|
13008
|
+
}
|
|
12825
13009
|
}
|
|
12826
13010
|
/**
|
|
12827
|
-
*
|
|
12828
|
-
* 每次依赖变更时重新执行任务并 yield 结果
|
|
13011
|
+
* 同步订阅路径变更(watcher 必须已初始化)
|
|
12829
13012
|
*
|
|
12830
|
-
*
|
|
12831
|
-
*
|
|
13013
|
+
* 这是同步版本,用于在 watcher 已初始化后快速注册订阅。
|
|
13014
|
+
* 如果 watcher 未初始化,抛出错误。
|
|
13015
|
+
*
|
|
13016
|
+
* @param path 要监听的路径
|
|
13017
|
+
* @param callback 变更回调
|
|
13018
|
+
* @param options 订阅选项
|
|
13019
|
+
* @returns 取消订阅函数
|
|
13020
|
+
*/
|
|
13021
|
+
subscribeSync(path$1, callback, options = {}) {
|
|
13022
|
+
if (!this.initialized) throw new Error("ProjectWatcher not initialized. Call init() first.");
|
|
13023
|
+
const normalizedPath = getRealPath$1(path$1);
|
|
13024
|
+
const id = Symbol();
|
|
13025
|
+
this.pathSubscriptions.set(id, {
|
|
13026
|
+
path: normalizedPath,
|
|
13027
|
+
watchChildren: options.watchChildren ?? false,
|
|
13028
|
+
callback
|
|
13029
|
+
});
|
|
13030
|
+
return () => {
|
|
13031
|
+
this.pathSubscriptions.delete(id);
|
|
13032
|
+
};
|
|
13033
|
+
}
|
|
13034
|
+
/**
|
|
13035
|
+
* 订阅路径变更(异步版本,自动初始化)
|
|
13036
|
+
*
|
|
13037
|
+
* @param path 要监听的路径
|
|
13038
|
+
* @param callback 变更回调
|
|
13039
|
+
* @param options 订阅选项
|
|
13040
|
+
* @returns 取消订阅函数
|
|
12832
13041
|
*/
|
|
12833
|
-
async
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
this.clearDependencies();
|
|
12837
|
-
this.changePromise = createPromiseWithResolvers();
|
|
12838
|
-
yield await contextStorage.run(this, task);
|
|
12839
|
-
if (this.dependencies.size === 0) break;
|
|
12840
|
-
await Promise.race([this.changePromise.promise, signal ? this.waitForAbort(signal) : new Promise(() => {})]);
|
|
12841
|
-
if (signal?.aborted) break;
|
|
12842
|
-
}
|
|
12843
|
-
} finally {
|
|
12844
|
-
this.destroy();
|
|
12845
|
-
}
|
|
13042
|
+
async subscribe(path$1, callback, options = {}) {
|
|
13043
|
+
await this.init();
|
|
13044
|
+
return this.subscribeSync(path$1, callback, options);
|
|
12846
13045
|
}
|
|
12847
13046
|
/**
|
|
12848
|
-
*
|
|
12849
|
-
* 用于初始数据获取
|
|
13047
|
+
* 获取当前订阅数量(用于调试)
|
|
12850
13048
|
*/
|
|
12851
|
-
|
|
12852
|
-
return
|
|
13049
|
+
get subscriptionCount() {
|
|
13050
|
+
return this.pathSubscriptions.size;
|
|
12853
13051
|
}
|
|
12854
13052
|
/**
|
|
12855
|
-
*
|
|
13053
|
+
* 检查是否已初始化
|
|
12856
13054
|
*/
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
this.dependencies.clear();
|
|
13055
|
+
get isInitialized() {
|
|
13056
|
+
return this.initialized;
|
|
12860
13057
|
}
|
|
12861
13058
|
/**
|
|
12862
|
-
*
|
|
12863
|
-
* @param reason 可选的销毁原因,如果提供则 reject changePromise
|
|
13059
|
+
* 启动健康检查定时器
|
|
12864
13060
|
*/
|
|
12865
|
-
|
|
12866
|
-
this.
|
|
12867
|
-
this.
|
|
12868
|
-
|
|
12869
|
-
|
|
13061
|
+
startHealthCheck() {
|
|
13062
|
+
this.stopHealthCheck();
|
|
13063
|
+
this.healthCheckTimer = setInterval(() => {
|
|
13064
|
+
this.performHealthCheck();
|
|
13065
|
+
}, HEALTH_CHECK_INTERVAL_MS);
|
|
13066
|
+
this.healthCheckTimer.unref();
|
|
12870
13067
|
}
|
|
12871
13068
|
/**
|
|
12872
|
-
*
|
|
13069
|
+
* 停止健康检查定时器
|
|
12873
13070
|
*/
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
|
|
13071
|
+
stopHealthCheck() {
|
|
13072
|
+
if (this.healthCheckTimer) {
|
|
13073
|
+
clearInterval(this.healthCheckTimer);
|
|
13074
|
+
this.healthCheckTimer = null;
|
|
13075
|
+
}
|
|
13076
|
+
this.healthCheckPending = false;
|
|
13077
|
+
}
|
|
13078
|
+
/**
|
|
13079
|
+
* 执行健康检查
|
|
13080
|
+
*
|
|
13081
|
+
* 工作流程:
|
|
13082
|
+
* 1. 如果最近有事件,无需检查
|
|
13083
|
+
* 2. 如果上次探测还在等待中,说明 watcher 可能失效,尝试重建
|
|
13084
|
+
* 3. 否则,创建临时文件触发事件,等待下次检查验证
|
|
13085
|
+
*/
|
|
13086
|
+
async performHealthCheck() {
|
|
13087
|
+
if (Date.now() - this.lastEventTime < HEALTH_CHECK_INTERVAL_MS) {
|
|
13088
|
+
this.healthCheckPending = false;
|
|
13089
|
+
return;
|
|
13090
|
+
}
|
|
13091
|
+
if (this.healthCheckPending) {
|
|
13092
|
+
console.warn("[ProjectWatcher] Health check failed, watcher appears stale. Reinitializing...");
|
|
13093
|
+
await this.reinitialize();
|
|
13094
|
+
return;
|
|
13095
|
+
}
|
|
13096
|
+
this.healthCheckPending = true;
|
|
13097
|
+
this.sendProbe();
|
|
13098
|
+
}
|
|
13099
|
+
/**
|
|
13100
|
+
* 发送探测:通过 utimesSync 修改项目目录的时间戳来触发 watcher 事件
|
|
13101
|
+
*/
|
|
13102
|
+
sendProbe() {
|
|
13103
|
+
try {
|
|
13104
|
+
const now = /* @__PURE__ */ new Date();
|
|
13105
|
+
utimesSync(this.projectDir, now, now);
|
|
13106
|
+
} catch {}
|
|
13107
|
+
}
|
|
13108
|
+
/**
|
|
13109
|
+
* 重新初始化 watcher
|
|
13110
|
+
*/
|
|
13111
|
+
async reinitialize() {
|
|
13112
|
+
this.stopHealthCheck();
|
|
13113
|
+
if (this.subscription) {
|
|
13114
|
+
try {
|
|
13115
|
+
await this.subscription.unsubscribe();
|
|
13116
|
+
} catch {}
|
|
13117
|
+
this.subscription = null;
|
|
13118
|
+
}
|
|
13119
|
+
this.initialized = false;
|
|
13120
|
+
this.initPromise = null;
|
|
13121
|
+
this.healthCheckPending = false;
|
|
13122
|
+
if (!existsSync(this.projectDir)) {
|
|
13123
|
+
console.warn("[ProjectWatcher] Project directory does not exist, waiting for it to be created...");
|
|
13124
|
+
this.waitForProjectDir();
|
|
13125
|
+
return;
|
|
13126
|
+
}
|
|
13127
|
+
try {
|
|
13128
|
+
await this.init();
|
|
13129
|
+
console.log("[ProjectWatcher] Reinitialized successfully");
|
|
13130
|
+
} catch (err) {
|
|
13131
|
+
console.error("[ProjectWatcher] Failed to reinitialize:", err);
|
|
13132
|
+
setTimeout(() => this.reinitialize(), HEALTH_CHECK_INTERVAL_MS);
|
|
13133
|
+
}
|
|
13134
|
+
}
|
|
13135
|
+
/**
|
|
13136
|
+
* 等待项目目录被创建
|
|
13137
|
+
*/
|
|
13138
|
+
waitForProjectDir() {
|
|
13139
|
+
const checkInterval = setInterval(() => {
|
|
13140
|
+
if (existsSync(this.projectDir)) {
|
|
13141
|
+
clearInterval(checkInterval);
|
|
13142
|
+
console.log("[ProjectWatcher] Project directory created, reinitializing...");
|
|
13143
|
+
this.reinitialize();
|
|
12879
13144
|
}
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
13145
|
+
}, HEALTH_CHECK_INTERVAL_MS);
|
|
13146
|
+
checkInterval.unref();
|
|
13147
|
+
}
|
|
13148
|
+
/**
|
|
13149
|
+
* 关闭 watcher
|
|
13150
|
+
*/
|
|
13151
|
+
async close() {
|
|
13152
|
+
this.stopHealthCheck();
|
|
13153
|
+
if (this.debounceTimer) {
|
|
13154
|
+
clearTimeout(this.debounceTimer);
|
|
13155
|
+
this.debounceTimer = null;
|
|
13156
|
+
}
|
|
13157
|
+
if (this.subscription) {
|
|
13158
|
+
await this.subscription.unsubscribe();
|
|
13159
|
+
this.subscription = null;
|
|
13160
|
+
}
|
|
13161
|
+
this.pathSubscriptions.clear();
|
|
13162
|
+
this.pendingEvents = [];
|
|
13163
|
+
this.initialized = false;
|
|
13164
|
+
this.initPromise = null;
|
|
12884
13165
|
}
|
|
12885
13166
|
};
|
|
13167
|
+
/**
|
|
13168
|
+
* 全局 ProjectWatcher 实例缓存
|
|
13169
|
+
* key: 项目目录路径
|
|
13170
|
+
*/
|
|
13171
|
+
const watcherCache = /* @__PURE__ */ new Map();
|
|
13172
|
+
/**
|
|
13173
|
+
* 获取或创建项目监听器
|
|
13174
|
+
*/
|
|
13175
|
+
function getProjectWatcher(projectDir, options) {
|
|
13176
|
+
const normalizedDir = getRealPath$1(projectDir);
|
|
13177
|
+
let watcher = watcherCache.get(normalizedDir);
|
|
13178
|
+
if (!watcher) {
|
|
13179
|
+
watcher = new ProjectWatcher(normalizedDir, options);
|
|
13180
|
+
watcherCache.set(normalizedDir, watcher);
|
|
13181
|
+
}
|
|
13182
|
+
return watcher;
|
|
13183
|
+
}
|
|
12886
13184
|
|
|
12887
13185
|
//#endregion
|
|
12888
13186
|
//#region ../core/src/reactive-fs/watcher-pool.ts
|
|
12889
|
-
/**
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
13187
|
+
/**
|
|
13188
|
+
* 获取路径的真实路径(解析符号链接)
|
|
13189
|
+
*/
|
|
13190
|
+
function getRealPath(path$1) {
|
|
13191
|
+
try {
|
|
13192
|
+
return realpathSync(resolve$1(path$1));
|
|
13193
|
+
} catch {
|
|
13194
|
+
return resolve$1(path$1);
|
|
13195
|
+
}
|
|
13196
|
+
}
|
|
13197
|
+
/**
|
|
13198
|
+
* 全局 ProjectWatcher 实例
|
|
13199
|
+
* 通过 initWatcherPool 初始化
|
|
13200
|
+
*/
|
|
13201
|
+
let globalProjectWatcher = null;
|
|
13202
|
+
let globalProjectDir = null;
|
|
12893
13203
|
/** 默认防抖时间 (ms) */
|
|
12894
13204
|
const DEBOUNCE_MS = 100;
|
|
13205
|
+
/** 路径订阅缓存 */
|
|
13206
|
+
const subscriptionCache = /* @__PURE__ */ new Map();
|
|
13207
|
+
/** 防抖定时器 */
|
|
13208
|
+
const debounceTimers = /* @__PURE__ */ new Map();
|
|
13209
|
+
/**
|
|
13210
|
+
* 初始化 watcher pool
|
|
13211
|
+
*
|
|
13212
|
+
* 必须在使用 acquireWatcher 之前调用。
|
|
13213
|
+
* 通常由 server 在启动时调用。
|
|
13214
|
+
*
|
|
13215
|
+
* @param projectDir 项目根目录
|
|
13216
|
+
*/
|
|
13217
|
+
async function initWatcherPool(projectDir) {
|
|
13218
|
+
const normalizedDir = getRealPath(projectDir);
|
|
13219
|
+
if (globalProjectWatcher && globalProjectDir === normalizedDir) return;
|
|
13220
|
+
if (globalProjectWatcher) await globalProjectWatcher.close();
|
|
13221
|
+
globalProjectDir = normalizedDir;
|
|
13222
|
+
globalProjectWatcher = getProjectWatcher(normalizedDir);
|
|
13223
|
+
await globalProjectWatcher.init();
|
|
13224
|
+
}
|
|
12895
13225
|
/**
|
|
12896
13226
|
* 获取或创建文件/目录监听器
|
|
12897
13227
|
*
|
|
12898
13228
|
* 特性:
|
|
12899
|
-
* -
|
|
13229
|
+
* - 使用 @parcel/watcher 监听项目根目录
|
|
13230
|
+
* - 自动处理新创建的目录(解决 init 后无法监听的问题)
|
|
13231
|
+
* - 同一路径共享订阅
|
|
12900
13232
|
* - 引用计数管理生命周期
|
|
12901
13233
|
* - 内置防抖机制
|
|
12902
13234
|
*
|
|
@@ -12906,51 +13238,50 @@ const DEBOUNCE_MS = 100;
|
|
|
12906
13238
|
* @returns 释放函数,调用后取消订阅
|
|
12907
13239
|
*/
|
|
12908
13240
|
function acquireWatcher(path$1, onChange, options = {}) {
|
|
12909
|
-
|
|
13241
|
+
if (!globalProjectWatcher || !globalProjectWatcher.isInitialized) {
|
|
13242
|
+
console.warn("[watcher-pool] ProjectWatcher not initialized. Call initWatcherPool first.");
|
|
13243
|
+
return () => {};
|
|
13244
|
+
}
|
|
13245
|
+
const normalizedPath = getRealPath(path$1);
|
|
12910
13246
|
const debounceMs = options.debounceMs ?? DEBOUNCE_MS;
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
const existingTimer = debounceTimers.get(normalizedPath);
|
|
13247
|
+
const isRecursive = options.recursive ?? false;
|
|
13248
|
+
const cacheKey$1 = `${normalizedPath}:${isRecursive}`;
|
|
13249
|
+
let subscription = subscriptionCache.get(cacheKey$1);
|
|
13250
|
+
if (!subscription) {
|
|
13251
|
+
const unsubscribe = globalProjectWatcher.subscribeSync(normalizedPath, () => {
|
|
13252
|
+
const existingTimer = debounceTimers.get(cacheKey$1);
|
|
12918
13253
|
if (existingTimer) clearTimeout(existingTimer);
|
|
12919
13254
|
const timer = setTimeout(() => {
|
|
12920
|
-
debounceTimers.delete(
|
|
12921
|
-
const
|
|
12922
|
-
if (
|
|
13255
|
+
debounceTimers.delete(cacheKey$1);
|
|
13256
|
+
const currentSub = subscriptionCache.get(cacheKey$1);
|
|
13257
|
+
if (currentSub) for (const cb of currentSub.callbacks) try {
|
|
12923
13258
|
cb();
|
|
12924
13259
|
} catch (err) {
|
|
12925
|
-
console.error(`
|
|
13260
|
+
console.error(`[watcher-pool] Callback error for ${normalizedPath}:`, err);
|
|
12926
13261
|
}
|
|
12927
13262
|
}, debounceMs);
|
|
12928
|
-
debounceTimers.set(
|
|
12929
|
-
});
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
refCount: 0,
|
|
12936
|
-
callbacks: /* @__PURE__ */ new Set()
|
|
13263
|
+
debounceTimers.set(cacheKey$1, timer);
|
|
13264
|
+
}, { watchChildren: isRecursive });
|
|
13265
|
+
subscription = {
|
|
13266
|
+
path: normalizedPath,
|
|
13267
|
+
callbacks: /* @__PURE__ */ new Set(),
|
|
13268
|
+
unsubscribe,
|
|
13269
|
+
onError: options.onError
|
|
12937
13270
|
};
|
|
12938
|
-
|
|
13271
|
+
subscriptionCache.set(cacheKey$1, subscription);
|
|
12939
13272
|
}
|
|
12940
|
-
|
|
12941
|
-
entry.callbacks.add(onChange);
|
|
13273
|
+
subscription.callbacks.add(onChange);
|
|
12942
13274
|
return () => {
|
|
12943
|
-
const
|
|
12944
|
-
if (!
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
const timer = debounceTimers.get(normalizedPath);
|
|
13275
|
+
const currentSub = subscriptionCache.get(cacheKey$1);
|
|
13276
|
+
if (!currentSub) return;
|
|
13277
|
+
currentSub.callbacks.delete(onChange);
|
|
13278
|
+
if (currentSub.callbacks.size === 0) {
|
|
13279
|
+
currentSub.unsubscribe();
|
|
13280
|
+
subscriptionCache.delete(cacheKey$1);
|
|
13281
|
+
const timer = debounceTimers.get(cacheKey$1);
|
|
12951
13282
|
if (timer) {
|
|
12952
13283
|
clearTimeout(timer);
|
|
12953
|
-
debounceTimers.delete(
|
|
13284
|
+
debounceTimers.delete(cacheKey$1);
|
|
12954
13285
|
}
|
|
12955
13286
|
}
|
|
12956
13287
|
};
|
|
@@ -12959,9 +13290,9 @@ function acquireWatcher(path$1, onChange, options = {}) {
|
|
|
12959
13290
|
//#endregion
|
|
12960
13291
|
//#region ../core/src/reactive-fs/reactive-fs.ts
|
|
12961
13292
|
/** 状态缓存:路径 -> ReactiveState */
|
|
12962
|
-
const stateCache = /* @__PURE__ */ new Map();
|
|
13293
|
+
const stateCache$1 = /* @__PURE__ */ new Map();
|
|
12963
13294
|
/** 监听器释放函数缓存 */
|
|
12964
|
-
const releaseCache = /* @__PURE__ */ new Map();
|
|
13295
|
+
const releaseCache$1 = /* @__PURE__ */ new Map();
|
|
12965
13296
|
/**
|
|
12966
13297
|
* 响应式读取文件内容
|
|
12967
13298
|
*
|
|
@@ -12969,6 +13300,7 @@ const releaseCache = /* @__PURE__ */ new Map();
|
|
|
12969
13300
|
* - 自动注册文件监听
|
|
12970
13301
|
* - 文件变更时自动更新状态
|
|
12971
13302
|
* - 在 ReactiveContext 中调用时自动追踪依赖
|
|
13303
|
+
* - 支持监听尚未创建的文件(通过 @parcel/watcher)
|
|
12972
13304
|
*
|
|
12973
13305
|
* @param filepath 文件路径
|
|
12974
13306
|
* @returns 文件内容,文件不存在时返回 null
|
|
@@ -12983,15 +13315,18 @@ async function reactiveReadFile(filepath) {
|
|
|
12983
13315
|
return null;
|
|
12984
13316
|
}
|
|
12985
13317
|
};
|
|
12986
|
-
let state = stateCache.get(key);
|
|
13318
|
+
let state = stateCache$1.get(key);
|
|
12987
13319
|
if (!state) {
|
|
12988
13320
|
state = new ReactiveState(await getValue());
|
|
12989
|
-
stateCache.set(key, state);
|
|
13321
|
+
stateCache$1.set(key, state);
|
|
12990
13322
|
const release = acquireWatcher(dirname$1(normalizedPath), async () => {
|
|
12991
13323
|
const newValue = await getValue();
|
|
12992
13324
|
state.set(newValue);
|
|
12993
|
-
})
|
|
12994
|
-
|
|
13325
|
+
}, { onError: () => {
|
|
13326
|
+
stateCache$1.delete(key);
|
|
13327
|
+
releaseCache$1.delete(key);
|
|
13328
|
+
} });
|
|
13329
|
+
releaseCache$1.set(key, release);
|
|
12995
13330
|
}
|
|
12996
13331
|
return state.get();
|
|
12997
13332
|
}
|
|
@@ -13002,6 +13337,7 @@ async function reactiveReadFile(filepath) {
|
|
|
13002
13337
|
* - 自动注册目录监听
|
|
13003
13338
|
* - 目录变更时自动更新状态
|
|
13004
13339
|
* - 在 ReactiveContext 中调用时自动追踪依赖
|
|
13340
|
+
* - 支持监听尚未创建的目录(通过 @parcel/watcher)
|
|
13005
13341
|
*
|
|
13006
13342
|
* @param dirpath 目录路径
|
|
13007
13343
|
* @param options 选项
|
|
@@ -13023,44 +13359,21 @@ async function reactiveReadDir(dirpath, options = {}) {
|
|
|
13023
13359
|
return [];
|
|
13024
13360
|
}
|
|
13025
13361
|
};
|
|
13026
|
-
let state = stateCache.get(key);
|
|
13362
|
+
let state = stateCache$1.get(key);
|
|
13027
13363
|
if (!state) {
|
|
13028
13364
|
state = new ReactiveState(await getValue(), { equals: (a, b) => a.length === b.length && a.every((v, i) => v === b[i]) });
|
|
13029
|
-
stateCache.set(key, state);
|
|
13365
|
+
stateCache$1.set(key, state);
|
|
13030
13366
|
const release = acquireWatcher(normalizedPath, async () => {
|
|
13031
13367
|
const newValue = await getValue();
|
|
13032
13368
|
state.set(newValue);
|
|
13033
|
-
}, {
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
* 响应式检查路径是否存在
|
|
13040
|
-
*
|
|
13041
|
-
* @param path 路径
|
|
13042
|
-
* @returns 是否存在
|
|
13043
|
-
*/
|
|
13044
|
-
async function reactiveExists(path$1) {
|
|
13045
|
-
const normalizedPath = resolve$1(path$1);
|
|
13046
|
-
const key = `exists:${normalizedPath}`;
|
|
13047
|
-
const getValue = async () => {
|
|
13048
|
-
try {
|
|
13049
|
-
await stat(normalizedPath);
|
|
13050
|
-
return true;
|
|
13051
|
-
} catch {
|
|
13052
|
-
return false;
|
|
13053
|
-
}
|
|
13054
|
-
};
|
|
13055
|
-
let state = stateCache.get(key);
|
|
13056
|
-
if (!state) {
|
|
13057
|
-
state = new ReactiveState(await getValue());
|
|
13058
|
-
stateCache.set(key, state);
|
|
13059
|
-
const release = acquireWatcher(dirname$1(normalizedPath), async () => {
|
|
13060
|
-
const newValue = await getValue();
|
|
13061
|
-
state.set(newValue);
|
|
13369
|
+
}, {
|
|
13370
|
+
recursive: true,
|
|
13371
|
+
onError: () => {
|
|
13372
|
+
stateCache$1.delete(key);
|
|
13373
|
+
releaseCache$1.delete(key);
|
|
13374
|
+
}
|
|
13062
13375
|
});
|
|
13063
|
-
releaseCache.set(key, release);
|
|
13376
|
+
releaseCache$1.set(key, release);
|
|
13064
13377
|
}
|
|
13065
13378
|
return state.get();
|
|
13066
13379
|
}
|
|
@@ -13086,19 +13399,22 @@ async function reactiveStat(path$1) {
|
|
|
13086
13399
|
return null;
|
|
13087
13400
|
}
|
|
13088
13401
|
};
|
|
13089
|
-
let state = stateCache.get(key);
|
|
13402
|
+
let state = stateCache$1.get(key);
|
|
13090
13403
|
if (!state) {
|
|
13091
13404
|
state = new ReactiveState(await getValue(), { equals: (a, b) => {
|
|
13092
13405
|
if (a === null && b === null) return true;
|
|
13093
13406
|
if (a === null || b === null) return false;
|
|
13094
13407
|
return a.isDirectory === b.isDirectory && a.isFile === b.isFile && a.mtime === b.mtime && a.birthtime === b.birthtime;
|
|
13095
13408
|
} });
|
|
13096
|
-
stateCache.set(key, state);
|
|
13409
|
+
stateCache$1.set(key, state);
|
|
13097
13410
|
const release = acquireWatcher(dirname$1(normalizedPath), async () => {
|
|
13098
13411
|
const newValue = await getValue();
|
|
13099
13412
|
state.set(newValue);
|
|
13100
|
-
})
|
|
13101
|
-
|
|
13413
|
+
}, { onError: () => {
|
|
13414
|
+
stateCache$1.delete(key);
|
|
13415
|
+
releaseCache$1.delete(key);
|
|
13416
|
+
} });
|
|
13417
|
+
releaseCache$1.set(key, release);
|
|
13102
13418
|
}
|
|
13103
13419
|
return state.get();
|
|
13104
13420
|
}
|
|
@@ -13607,7 +13923,7 @@ var OpenSpecWatcher = class extends EventEmitter {
|
|
|
13607
13923
|
*/
|
|
13608
13924
|
watchDir(dir, callback) {
|
|
13609
13925
|
try {
|
|
13610
|
-
const watcher = watch
|
|
13926
|
+
const watcher = watch(dir, { recursive: true }, (eventType, filename) => {
|
|
13611
13927
|
if (filename) callback(filename, eventType);
|
|
13612
13928
|
});
|
|
13613
13929
|
watcher.on("error", (error) => {
|
|
@@ -13634,22 +13950,143 @@ var OpenSpecWatcher = class extends EventEmitter {
|
|
|
13634
13950
|
|
|
13635
13951
|
//#endregion
|
|
13636
13952
|
//#region ../core/src/config.ts
|
|
13953
|
+
const execAsync = promisify(exec);
|
|
13954
|
+
/** 默认的 fallback CLI 命令(数组形式) */
|
|
13955
|
+
const FALLBACK_CLI_COMMAND = ["npx", "@fission-ai/openspec"];
|
|
13956
|
+
/** 全局 openspec 命令(数组形式) */
|
|
13957
|
+
const GLOBAL_CLI_COMMAND = ["openspec"];
|
|
13958
|
+
/** 缓存检测到的 CLI 命令 */
|
|
13959
|
+
let detectedCliCommand = null;
|
|
13960
|
+
/**
|
|
13961
|
+
* 解析 CLI 命令字符串为数组
|
|
13962
|
+
*
|
|
13963
|
+
* 支持两种格式:
|
|
13964
|
+
* 1. JSON 数组:以 `[` 开头,如 `["npx", "@fission-ai/openspec"]`
|
|
13965
|
+
* 2. 简单字符串:用空格分割,如 `npx @fission-ai/openspec`
|
|
13966
|
+
*
|
|
13967
|
+
* 注意:简单字符串解析不支持带引号的参数,如需复杂命令请使用 JSON 数组格式
|
|
13968
|
+
*/
|
|
13969
|
+
function parseCliCommand(command) {
|
|
13970
|
+
const trimmed = command.trim();
|
|
13971
|
+
if (trimmed.startsWith("[")) try {
|
|
13972
|
+
const parsed = JSON.parse(trimmed);
|
|
13973
|
+
if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) return parsed;
|
|
13974
|
+
throw new Error("Invalid JSON array: expected array of strings");
|
|
13975
|
+
} catch (err) {
|
|
13976
|
+
throw new Error(`Failed to parse CLI command as JSON array: ${err instanceof Error ? err.message : err}`);
|
|
13977
|
+
}
|
|
13978
|
+
return trimmed.split(/\s+/).filter(Boolean);
|
|
13979
|
+
}
|
|
13980
|
+
/**
|
|
13981
|
+
* 比较两个语义化版本号
|
|
13982
|
+
* @returns 正数表示 a > b,负数表示 a < b,0 表示相等
|
|
13983
|
+
*/
|
|
13984
|
+
function compareVersions(a, b) {
|
|
13985
|
+
const parseVersion = (v) => {
|
|
13986
|
+
return v.replace(/^v/, "").split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
13987
|
+
};
|
|
13988
|
+
const aParts = parseVersion(a);
|
|
13989
|
+
const bParts = parseVersion(b);
|
|
13990
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
13991
|
+
const aVal = aParts[i] ?? 0;
|
|
13992
|
+
const bVal = bParts[i] ?? 0;
|
|
13993
|
+
if (aVal !== bVal) return aVal - bVal;
|
|
13994
|
+
}
|
|
13995
|
+
return 0;
|
|
13996
|
+
}
|
|
13997
|
+
/**
|
|
13998
|
+
* 获取 npx 可用的最新版本
|
|
13999
|
+
*
|
|
14000
|
+
* 使用 `npx @fission-ai/openspec --version` 获取最新版本
|
|
14001
|
+
* 这会下载并执行最新版本,所以超时时间较长
|
|
14002
|
+
*/
|
|
14003
|
+
async function fetchLatestVersion() {
|
|
14004
|
+
try {
|
|
14005
|
+
const { stdout } = await execAsync("npx @fission-ai/openspec --version", { timeout: 6e4 });
|
|
14006
|
+
return stdout.trim();
|
|
14007
|
+
} catch {
|
|
14008
|
+
return;
|
|
14009
|
+
}
|
|
14010
|
+
}
|
|
14011
|
+
/**
|
|
14012
|
+
* 嗅探全局 openspec 命令(无缓存)
|
|
14013
|
+
*
|
|
14014
|
+
* 使用 `openspec --version` 检测是否有全局命令可用。
|
|
14015
|
+
* 同时检查 npm registry 上的最新版本。
|
|
14016
|
+
* 每次调用都会重新检测,不使用缓存。
|
|
14017
|
+
*/
|
|
14018
|
+
async function sniffGlobalCli() {
|
|
14019
|
+
const [localResult, latestVersion] = await Promise.all([execAsync("openspec --version", { timeout: 1e4 }).catch((err) => ({ error: err })), fetchLatestVersion()]);
|
|
14020
|
+
if ("error" in localResult) {
|
|
14021
|
+
const error = localResult.error instanceof Error ? localResult.error.message : String(localResult.error);
|
|
14022
|
+
if (error.includes("not found") || error.includes("ENOENT") || error.includes("not recognized")) return {
|
|
14023
|
+
hasGlobal: false,
|
|
14024
|
+
latestVersion,
|
|
14025
|
+
hasUpdate: !!latestVersion
|
|
14026
|
+
};
|
|
14027
|
+
return {
|
|
14028
|
+
hasGlobal: false,
|
|
14029
|
+
latestVersion,
|
|
14030
|
+
hasUpdate: !!latestVersion,
|
|
14031
|
+
error
|
|
14032
|
+
};
|
|
14033
|
+
}
|
|
14034
|
+
const version = localResult.stdout.trim();
|
|
14035
|
+
detectedCliCommand = GLOBAL_CLI_COMMAND;
|
|
14036
|
+
return {
|
|
14037
|
+
hasGlobal: true,
|
|
14038
|
+
version,
|
|
14039
|
+
latestVersion,
|
|
14040
|
+
hasUpdate: latestVersion ? compareVersions(latestVersion, version) > 0 : false
|
|
14041
|
+
};
|
|
14042
|
+
}
|
|
14043
|
+
/**
|
|
14044
|
+
* 检测全局安装的 openspec 命令
|
|
14045
|
+
* 优先使用全局命令,fallback 到 npx
|
|
14046
|
+
*
|
|
14047
|
+
* @returns CLI 命令数组
|
|
14048
|
+
*/
|
|
14049
|
+
async function detectCliCommand() {
|
|
14050
|
+
if (detectedCliCommand !== null) return detectedCliCommand;
|
|
14051
|
+
try {
|
|
14052
|
+
await execAsync(`${process.platform === "win32" ? "where" : "which"} openspec`);
|
|
14053
|
+
detectedCliCommand = GLOBAL_CLI_COMMAND;
|
|
14054
|
+
return detectedCliCommand;
|
|
14055
|
+
} catch {
|
|
14056
|
+
detectedCliCommand = FALLBACK_CLI_COMMAND;
|
|
14057
|
+
return detectedCliCommand;
|
|
14058
|
+
}
|
|
14059
|
+
}
|
|
14060
|
+
/**
|
|
14061
|
+
* 获取默认 CLI 命令(异步,带检测)
|
|
14062
|
+
*
|
|
14063
|
+
* @returns CLI 命令数组,如 `['openspec']` 或 `['npx', '@fission-ai/openspec']`
|
|
14064
|
+
*/
|
|
14065
|
+
async function getDefaultCliCommand() {
|
|
14066
|
+
return detectCliCommand();
|
|
14067
|
+
}
|
|
14068
|
+
/**
|
|
14069
|
+
* 获取默认 CLI 命令的字符串形式(用于 UI 显示)
|
|
14070
|
+
*/
|
|
14071
|
+
async function getDefaultCliCommandString() {
|
|
14072
|
+
return (await detectCliCommand()).join(" ");
|
|
14073
|
+
}
|
|
13637
14074
|
/**
|
|
13638
14075
|
* OpenSpecUI 配置 Schema
|
|
13639
14076
|
*
|
|
13640
14077
|
* 存储在 openspec/.openspecui.json 中,利用文件监听实现响应式更新
|
|
13641
14078
|
*/
|
|
13642
14079
|
const OpenSpecUIConfigSchema = objectType({
|
|
13643
|
-
cli: objectType({ command: stringType().
|
|
14080
|
+
cli: objectType({ command: stringType().optional() }).default({}),
|
|
13644
14081
|
ui: objectType({ theme: enumType([
|
|
13645
14082
|
"light",
|
|
13646
14083
|
"dark",
|
|
13647
14084
|
"system"
|
|
13648
14085
|
]).default("system") }).default({})
|
|
13649
14086
|
});
|
|
13650
|
-
/**
|
|
14087
|
+
/** 默认配置(静态,用于测试和类型) */
|
|
13651
14088
|
const DEFAULT_CONFIG = {
|
|
13652
|
-
cli: {
|
|
14089
|
+
cli: {},
|
|
13653
14090
|
ui: { theme: "system" }
|
|
13654
14091
|
};
|
|
13655
14092
|
/**
|
|
@@ -13705,10 +14142,22 @@ var ConfigManager = class {
|
|
|
13705
14142
|
await writeFile(this.configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
13706
14143
|
}
|
|
13707
14144
|
/**
|
|
13708
|
-
* 获取 CLI
|
|
14145
|
+
* 获取 CLI 命令(数组形式)
|
|
14146
|
+
*
|
|
14147
|
+
* 优先级:配置文件 > 全局 openspec 命令 > npx fallback
|
|
14148
|
+
*
|
|
14149
|
+
* @returns CLI 命令数组,如 `['openspec']` 或 `['npx', '@fission-ai/openspec']`
|
|
13709
14150
|
*/
|
|
13710
14151
|
async getCliCommand() {
|
|
13711
|
-
|
|
14152
|
+
const config = await this.readConfig();
|
|
14153
|
+
if (config.cli.command) return parseCliCommand(config.cli.command);
|
|
14154
|
+
return getDefaultCliCommand();
|
|
14155
|
+
}
|
|
14156
|
+
/**
|
|
14157
|
+
* 获取 CLI 命令的字符串形式(用于 UI 显示)
|
|
14158
|
+
*/
|
|
14159
|
+
async getCliCommandString() {
|
|
14160
|
+
return (await this.getCliCommand()).join(" ");
|
|
13712
14161
|
}
|
|
13713
14162
|
/**
|
|
13714
14163
|
* 设置 CLI 命令
|
|
@@ -13725,10 +14174,11 @@ var ConfigManager = class {
|
|
|
13725
14174
|
*
|
|
13726
14175
|
* 负责调用外部 openspec CLI 命令。
|
|
13727
14176
|
* 命令前缀从 ConfigManager 获取,支持:
|
|
13728
|
-
* - npx @fission-ai/openspec (默认)
|
|
13729
|
-
* -
|
|
13730
|
-
* -
|
|
13731
|
-
*
|
|
14177
|
+
* - ['npx', '@fission-ai/openspec'] (默认)
|
|
14178
|
+
* - ['openspec'] (全局安装)
|
|
14179
|
+
* - 自定义数组或字符串
|
|
14180
|
+
*
|
|
14181
|
+
* 注意:所有命令都使用 shell: false 执行,避免 shell 注入风险
|
|
13732
14182
|
*/
|
|
13733
14183
|
var CliExecutor = class {
|
|
13734
14184
|
constructor(configManager, projectDir) {
|
|
@@ -13745,19 +14195,26 @@ var CliExecutor = class {
|
|
|
13745
14195
|
return env;
|
|
13746
14196
|
}
|
|
13747
14197
|
/**
|
|
14198
|
+
* 构建完整命令数组
|
|
14199
|
+
*
|
|
14200
|
+
* @param args CLI 参数,如 ['init'] 或 ['archive', 'change-id']
|
|
14201
|
+
* @returns [command, ...commandArgs, ...args]
|
|
14202
|
+
*/
|
|
14203
|
+
async buildCommandArray(args) {
|
|
14204
|
+
return [...await this.configManager.getCliCommand(), ...args];
|
|
14205
|
+
}
|
|
14206
|
+
/**
|
|
13748
14207
|
* 执行 CLI 命令
|
|
13749
14208
|
*
|
|
13750
14209
|
* @param args CLI 参数,如 ['init'] 或 ['archive', 'change-id']
|
|
13751
14210
|
* @returns 执行结果
|
|
13752
14211
|
*/
|
|
13753
14212
|
async execute(args) {
|
|
13754
|
-
const
|
|
13755
|
-
const cmd = parts[0];
|
|
13756
|
-
const cmdArgs = [...parts.slice(1), ...args];
|
|
14213
|
+
const [cmd, ...cmdArgs] = await this.buildCommandArray(args);
|
|
13757
14214
|
return new Promise((resolve$2) => {
|
|
13758
14215
|
const child = spawn(cmd, cmdArgs, {
|
|
13759
14216
|
cwd: this.projectDir,
|
|
13760
|
-
shell:
|
|
14217
|
+
shell: false,
|
|
13761
14218
|
env: this.getCleanEnv()
|
|
13762
14219
|
});
|
|
13763
14220
|
let stdout = "";
|
|
@@ -13793,7 +14250,11 @@ var CliExecutor = class {
|
|
|
13793
14250
|
*/
|
|
13794
14251
|
async init(tools = "all") {
|
|
13795
14252
|
const toolsArg = Array.isArray(tools) ? tools.join(",") : tools;
|
|
13796
|
-
return this.execute([
|
|
14253
|
+
return this.execute([
|
|
14254
|
+
"init",
|
|
14255
|
+
"--tools",
|
|
14256
|
+
toolsArg
|
|
14257
|
+
]);
|
|
13797
14258
|
}
|
|
13798
14259
|
/**
|
|
13799
14260
|
* 执行 openspec archive <changeId>(非交互式)
|
|
@@ -13821,25 +14282,154 @@ var CliExecutor = class {
|
|
|
13821
14282
|
return this.execute(args);
|
|
13822
14283
|
}
|
|
13823
14284
|
/**
|
|
13824
|
-
* 检查 CLI 是否可用
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
|
|
13836
|
-
|
|
13837
|
-
|
|
13838
|
-
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
|
|
13842
|
-
|
|
14285
|
+
* 检查 CLI 是否可用
|
|
14286
|
+
* @param timeout 超时时间(毫秒),默认 10 秒
|
|
14287
|
+
*/
|
|
14288
|
+
async checkAvailability(timeout = 1e4) {
|
|
14289
|
+
try {
|
|
14290
|
+
const result = await Promise.race([this.execute(["--version"]), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("CLI check timed out")), timeout))]);
|
|
14291
|
+
if (result.success) return {
|
|
14292
|
+
available: true,
|
|
14293
|
+
version: result.stdout.trim()
|
|
14294
|
+
};
|
|
14295
|
+
return {
|
|
14296
|
+
available: false,
|
|
14297
|
+
error: result.stderr || "Unknown error"
|
|
14298
|
+
};
|
|
14299
|
+
} catch (err) {
|
|
14300
|
+
return {
|
|
14301
|
+
available: false,
|
|
14302
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
14303
|
+
};
|
|
14304
|
+
}
|
|
14305
|
+
}
|
|
14306
|
+
/**
|
|
14307
|
+
* 流式执行 CLI 命令
|
|
14308
|
+
*
|
|
14309
|
+
* @param args CLI 参数
|
|
14310
|
+
* @param onEvent 事件回调
|
|
14311
|
+
* @returns 取消函数
|
|
14312
|
+
*/
|
|
14313
|
+
async executeStream(args, onEvent) {
|
|
14314
|
+
const fullCommand = await this.buildCommandArray(args);
|
|
14315
|
+
const [cmd, ...cmdArgs] = fullCommand;
|
|
14316
|
+
onEvent({
|
|
14317
|
+
type: "command",
|
|
14318
|
+
data: fullCommand.join(" ")
|
|
14319
|
+
});
|
|
14320
|
+
const child = spawn(cmd, cmdArgs, {
|
|
14321
|
+
cwd: this.projectDir,
|
|
14322
|
+
shell: false,
|
|
14323
|
+
env: this.getCleanEnv()
|
|
14324
|
+
});
|
|
14325
|
+
child.stdout?.on("data", (data) => {
|
|
14326
|
+
onEvent({
|
|
14327
|
+
type: "stdout",
|
|
14328
|
+
data: data.toString()
|
|
14329
|
+
});
|
|
14330
|
+
});
|
|
14331
|
+
child.stderr?.on("data", (data) => {
|
|
14332
|
+
onEvent({
|
|
14333
|
+
type: "stderr",
|
|
14334
|
+
data: data.toString()
|
|
14335
|
+
});
|
|
14336
|
+
});
|
|
14337
|
+
child.on("close", (exitCode) => {
|
|
14338
|
+
onEvent({
|
|
14339
|
+
type: "exit",
|
|
14340
|
+
exitCode
|
|
14341
|
+
});
|
|
14342
|
+
});
|
|
14343
|
+
child.on("error", (err) => {
|
|
14344
|
+
onEvent({
|
|
14345
|
+
type: "stderr",
|
|
14346
|
+
data: err.message
|
|
14347
|
+
});
|
|
14348
|
+
onEvent({
|
|
14349
|
+
type: "exit",
|
|
14350
|
+
exitCode: null
|
|
14351
|
+
});
|
|
14352
|
+
});
|
|
14353
|
+
return () => {
|
|
14354
|
+
child.kill();
|
|
14355
|
+
};
|
|
14356
|
+
}
|
|
14357
|
+
/**
|
|
14358
|
+
* 流式执行 openspec init
|
|
14359
|
+
*/
|
|
14360
|
+
initStream(tools, onEvent) {
|
|
14361
|
+
const toolsArg = Array.isArray(tools) ? tools.join(",") : tools;
|
|
14362
|
+
return this.executeStream([
|
|
14363
|
+
"init",
|
|
14364
|
+
"--tools",
|
|
14365
|
+
toolsArg
|
|
14366
|
+
], onEvent);
|
|
14367
|
+
}
|
|
14368
|
+
/**
|
|
14369
|
+
* 流式执行 openspec archive
|
|
14370
|
+
*/
|
|
14371
|
+
archiveStream(changeId, options, onEvent) {
|
|
14372
|
+
const args = [
|
|
14373
|
+
"archive",
|
|
14374
|
+
"-y",
|
|
14375
|
+
changeId
|
|
14376
|
+
];
|
|
14377
|
+
if (options.skipSpecs) args.push("--skip-specs");
|
|
14378
|
+
if (options.noValidate) args.push("--no-validate");
|
|
14379
|
+
return this.executeStream(args, onEvent);
|
|
14380
|
+
}
|
|
14381
|
+
/**
|
|
14382
|
+
* 流式执行任意命令(数组形式)
|
|
14383
|
+
*
|
|
14384
|
+
* 用于执行不需要 openspec CLI 前缀的命令,如 npm install。
|
|
14385
|
+
* 使用 shell: false 避免 shell 注入风险。
|
|
14386
|
+
*
|
|
14387
|
+
* @param command 命令数组,如 ['npm', 'install', '-g', '@fission-ai/openspec']
|
|
14388
|
+
* @param onEvent 事件回调
|
|
14389
|
+
* @returns 取消函数
|
|
14390
|
+
*/
|
|
14391
|
+
executeCommandStream(command, onEvent) {
|
|
14392
|
+
const [cmd, ...cmdArgs] = command;
|
|
14393
|
+
onEvent({
|
|
14394
|
+
type: "command",
|
|
14395
|
+
data: command.join(" ")
|
|
14396
|
+
});
|
|
14397
|
+
const child = spawn(cmd, cmdArgs, {
|
|
14398
|
+
cwd: this.projectDir,
|
|
14399
|
+
shell: false,
|
|
14400
|
+
env: this.getCleanEnv()
|
|
14401
|
+
});
|
|
14402
|
+
child.stdout?.on("data", (data) => {
|
|
14403
|
+
onEvent({
|
|
14404
|
+
type: "stdout",
|
|
14405
|
+
data: data.toString()
|
|
14406
|
+
});
|
|
14407
|
+
});
|
|
14408
|
+
child.stderr?.on("data", (data) => {
|
|
14409
|
+
onEvent({
|
|
14410
|
+
type: "stderr",
|
|
14411
|
+
data: data.toString()
|
|
14412
|
+
});
|
|
14413
|
+
});
|
|
14414
|
+
child.on("close", (exitCode) => {
|
|
14415
|
+
onEvent({
|
|
14416
|
+
type: "exit",
|
|
14417
|
+
exitCode
|
|
14418
|
+
});
|
|
14419
|
+
});
|
|
14420
|
+
child.on("error", (err) => {
|
|
14421
|
+
onEvent({
|
|
14422
|
+
type: "stderr",
|
|
14423
|
+
data: err.message
|
|
14424
|
+
});
|
|
14425
|
+
onEvent({
|
|
14426
|
+
type: "exit",
|
|
14427
|
+
exitCode: null
|
|
14428
|
+
});
|
|
14429
|
+
});
|
|
14430
|
+
return () => {
|
|
14431
|
+
child.kill();
|
|
14432
|
+
};
|
|
13843
14433
|
}
|
|
13844
14434
|
};
|
|
13845
14435
|
|
|
@@ -13848,121 +14438,312 @@ var CliExecutor = class {
|
|
|
13848
14438
|
/**
|
|
13849
14439
|
* 工具配置检测模块
|
|
13850
14440
|
*
|
|
13851
|
-
*
|
|
14441
|
+
* 完全对齐 @fission-ai/openspec 的官方实现
|
|
13852
14442
|
* 用于检测项目中已配置的 AI 工具
|
|
13853
14443
|
*
|
|
13854
|
-
*
|
|
14444
|
+
* 重要:使用响应式文件系统实现,监听配置目录,
|
|
13855
14445
|
* 当配置文件变化时会自动触发更新。
|
|
13856
14446
|
*
|
|
14447
|
+
* @see references/openspec/src/core/config.ts (AI_TOOLS)
|
|
13857
14448
|
* @see references/openspec/src/core/configurators/slash/
|
|
14449
|
+
* @see references/openspec/src/core/init.ts (isToolConfigured)
|
|
13858
14450
|
*/
|
|
13859
14451
|
/**
|
|
13860
|
-
*
|
|
14452
|
+
* 获取 Codex 全局 prompts 目录
|
|
14453
|
+
* 优先使用 $CODEX_HOME 环境变量,否则使用 ~/.codex
|
|
14454
|
+
* @see references/openspec/src/core/configurators/slash/codex.ts
|
|
14455
|
+
*/
|
|
14456
|
+
function getCodexGlobalPromptsDir() {
|
|
14457
|
+
return join$1(process.env.CODEX_HOME?.trim() || join$1(homedir(), ".codex"), "prompts");
|
|
14458
|
+
}
|
|
14459
|
+
/**
|
|
14460
|
+
* 所有支持的 AI 工具配置
|
|
14461
|
+
*
|
|
14462
|
+
* 完全对齐官方 OpenSpec CLI 的 AI_TOOLS
|
|
14463
|
+
* 按字母顺序排序(与官方一致)
|
|
13861
14464
|
*
|
|
13862
|
-
*
|
|
13863
|
-
*
|
|
14465
|
+
* @see references/openspec/src/core/config.ts
|
|
14466
|
+
* @see references/openspec/src/core/configurators/slash/registry.ts
|
|
13864
14467
|
*/
|
|
13865
|
-
const
|
|
14468
|
+
const AI_TOOLS = [
|
|
13866
14469
|
{
|
|
13867
|
-
|
|
13868
|
-
|
|
14470
|
+
name: "Amazon Q Developer",
|
|
14471
|
+
value: "amazon-q",
|
|
14472
|
+
available: true,
|
|
14473
|
+
successLabel: "Amazon Q Developer",
|
|
14474
|
+
scope: "project",
|
|
14475
|
+
detectionPath: ".amazonq/prompts/openspec-proposal.md"
|
|
13869
14476
|
},
|
|
13870
14477
|
{
|
|
13871
|
-
|
|
13872
|
-
|
|
14478
|
+
name: "Antigravity",
|
|
14479
|
+
value: "antigravity",
|
|
14480
|
+
available: true,
|
|
14481
|
+
successLabel: "Antigravity",
|
|
14482
|
+
scope: "project",
|
|
14483
|
+
detectionPath: ".agent/workflows/openspec-proposal.md"
|
|
13873
14484
|
},
|
|
13874
14485
|
{
|
|
13875
|
-
|
|
13876
|
-
|
|
14486
|
+
name: "Auggie (Augment CLI)",
|
|
14487
|
+
value: "auggie",
|
|
14488
|
+
available: true,
|
|
14489
|
+
successLabel: "Auggie",
|
|
14490
|
+
scope: "project",
|
|
14491
|
+
detectionPath: ".augment/commands/openspec-proposal.md"
|
|
13877
14492
|
},
|
|
13878
14493
|
{
|
|
13879
|
-
|
|
13880
|
-
|
|
14494
|
+
name: "Claude Code",
|
|
14495
|
+
value: "claude",
|
|
14496
|
+
available: true,
|
|
14497
|
+
successLabel: "Claude Code",
|
|
14498
|
+
scope: "project",
|
|
14499
|
+
detectionPath: ".claude/commands/openspec/proposal.md"
|
|
13881
14500
|
},
|
|
13882
14501
|
{
|
|
13883
|
-
|
|
13884
|
-
|
|
14502
|
+
name: "Cline",
|
|
14503
|
+
value: "cline",
|
|
14504
|
+
available: true,
|
|
14505
|
+
successLabel: "Cline",
|
|
14506
|
+
scope: "project",
|
|
14507
|
+
detectionPath: ".clinerules/workflows/openspec-proposal.md"
|
|
13885
14508
|
},
|
|
13886
14509
|
{
|
|
13887
|
-
|
|
13888
|
-
|
|
14510
|
+
name: "Codex",
|
|
14511
|
+
value: "codex",
|
|
14512
|
+
available: true,
|
|
14513
|
+
successLabel: "Codex",
|
|
14514
|
+
scope: "global",
|
|
14515
|
+
detectionPath: () => join$1(getCodexGlobalPromptsDir(), "openspec-proposal.md")
|
|
13889
14516
|
},
|
|
13890
14517
|
{
|
|
13891
|
-
|
|
13892
|
-
|
|
14518
|
+
name: "CodeBuddy Code (CLI)",
|
|
14519
|
+
value: "codebuddy",
|
|
14520
|
+
available: true,
|
|
14521
|
+
successLabel: "CodeBuddy Code",
|
|
14522
|
+
scope: "project",
|
|
14523
|
+
detectionPath: ".codebuddy/commands/openspec/proposal.md"
|
|
13893
14524
|
},
|
|
13894
14525
|
{
|
|
13895
|
-
|
|
13896
|
-
|
|
14526
|
+
name: "CoStrict",
|
|
14527
|
+
value: "costrict",
|
|
14528
|
+
available: true,
|
|
14529
|
+
successLabel: "CoStrict",
|
|
14530
|
+
scope: "project",
|
|
14531
|
+
detectionPath: ".cospec/openspec/commands/openspec-proposal.md"
|
|
13897
14532
|
},
|
|
13898
14533
|
{
|
|
13899
|
-
|
|
13900
|
-
|
|
14534
|
+
name: "Crush",
|
|
14535
|
+
value: "crush",
|
|
14536
|
+
available: true,
|
|
14537
|
+
successLabel: "Crush",
|
|
14538
|
+
scope: "project",
|
|
14539
|
+
detectionPath: ".crush/commands/openspec/proposal.md"
|
|
13901
14540
|
},
|
|
13902
14541
|
{
|
|
13903
|
-
|
|
13904
|
-
|
|
14542
|
+
name: "Cursor",
|
|
14543
|
+
value: "cursor",
|
|
14544
|
+
available: true,
|
|
14545
|
+
successLabel: "Cursor",
|
|
14546
|
+
scope: "project",
|
|
14547
|
+
detectionPath: ".cursor/commands/openspec-proposal.md"
|
|
13905
14548
|
},
|
|
13906
14549
|
{
|
|
13907
|
-
|
|
13908
|
-
|
|
14550
|
+
name: "Factory Droid",
|
|
14551
|
+
value: "factory",
|
|
14552
|
+
available: true,
|
|
14553
|
+
successLabel: "Factory Droid",
|
|
14554
|
+
scope: "project",
|
|
14555
|
+
detectionPath: ".factory/commands/openspec-proposal.md"
|
|
13909
14556
|
},
|
|
13910
14557
|
{
|
|
13911
|
-
|
|
13912
|
-
|
|
14558
|
+
name: "Gemini CLI",
|
|
14559
|
+
value: "gemini",
|
|
14560
|
+
available: true,
|
|
14561
|
+
successLabel: "Gemini CLI",
|
|
14562
|
+
scope: "project",
|
|
14563
|
+
detectionPath: ".gemini/commands/openspec/proposal.toml"
|
|
13913
14564
|
},
|
|
13914
14565
|
{
|
|
13915
|
-
|
|
13916
|
-
|
|
14566
|
+
name: "GitHub Copilot",
|
|
14567
|
+
value: "github-copilot",
|
|
14568
|
+
available: true,
|
|
14569
|
+
successLabel: "GitHub Copilot",
|
|
14570
|
+
scope: "project",
|
|
14571
|
+
detectionPath: ".github/prompts/openspec-proposal.prompt.md"
|
|
13917
14572
|
},
|
|
13918
14573
|
{
|
|
13919
|
-
|
|
13920
|
-
|
|
14574
|
+
name: "iFlow",
|
|
14575
|
+
value: "iflow",
|
|
14576
|
+
available: true,
|
|
14577
|
+
successLabel: "iFlow",
|
|
14578
|
+
scope: "project",
|
|
14579
|
+
detectionPath: ".iflow/commands/openspec-proposal.md"
|
|
13921
14580
|
},
|
|
13922
14581
|
{
|
|
13923
|
-
|
|
13924
|
-
|
|
14582
|
+
name: "Kilo Code",
|
|
14583
|
+
value: "kilocode",
|
|
14584
|
+
available: true,
|
|
14585
|
+
successLabel: "Kilo Code",
|
|
14586
|
+
scope: "project",
|
|
14587
|
+
detectionPath: ".kilocode/workflows/openspec-proposal.md"
|
|
13925
14588
|
},
|
|
13926
14589
|
{
|
|
13927
|
-
|
|
13928
|
-
|
|
14590
|
+
name: "OpenCode",
|
|
14591
|
+
value: "opencode",
|
|
14592
|
+
available: true,
|
|
14593
|
+
successLabel: "OpenCode",
|
|
14594
|
+
scope: "project",
|
|
14595
|
+
detectionPath: ".opencode/command/openspec-proposal.md"
|
|
13929
14596
|
},
|
|
13930
14597
|
{
|
|
13931
|
-
|
|
13932
|
-
|
|
14598
|
+
name: "Qoder (CLI)",
|
|
14599
|
+
value: "qoder",
|
|
14600
|
+
available: true,
|
|
14601
|
+
successLabel: "Qoder",
|
|
14602
|
+
scope: "project",
|
|
14603
|
+
detectionPath: ".qoder/commands/openspec/proposal.md"
|
|
13933
14604
|
},
|
|
13934
14605
|
{
|
|
13935
|
-
|
|
14606
|
+
name: "Qwen Code",
|
|
14607
|
+
value: "qwen",
|
|
14608
|
+
available: true,
|
|
14609
|
+
successLabel: "Qwen Code",
|
|
14610
|
+
scope: "project",
|
|
13936
14611
|
detectionPath: ".qwen/commands/openspec-proposal.toml"
|
|
13937
14612
|
},
|
|
13938
14613
|
{
|
|
13939
|
-
|
|
13940
|
-
|
|
14614
|
+
name: "RooCode",
|
|
14615
|
+
value: "roocode",
|
|
14616
|
+
available: true,
|
|
14617
|
+
successLabel: "RooCode",
|
|
14618
|
+
scope: "project",
|
|
14619
|
+
detectionPath: ".roo/commands/openspec-proposal.md"
|
|
13941
14620
|
},
|
|
13942
14621
|
{
|
|
13943
|
-
|
|
13944
|
-
|
|
14622
|
+
name: "Windsurf",
|
|
14623
|
+
value: "windsurf",
|
|
14624
|
+
available: true,
|
|
14625
|
+
successLabel: "Windsurf",
|
|
14626
|
+
scope: "project",
|
|
14627
|
+
detectionPath: ".windsurf/workflows/openspec-proposal.md"
|
|
14628
|
+
},
|
|
14629
|
+
{
|
|
14630
|
+
name: "AGENTS.md (works with Amp, VS Code, …)",
|
|
14631
|
+
value: "agents",
|
|
14632
|
+
available: false,
|
|
14633
|
+
successLabel: "your AGENTS.md-compatible assistant",
|
|
14634
|
+
scope: "project",
|
|
14635
|
+
detectionPath: "AGENTS.md"
|
|
13945
14636
|
}
|
|
13946
14637
|
];
|
|
13947
14638
|
/**
|
|
13948
|
-
*
|
|
14639
|
+
* 获取所有可用的工具(available: true)
|
|
14640
|
+
*/
|
|
14641
|
+
function getAvailableTools() {
|
|
14642
|
+
return AI_TOOLS.filter((tool) => tool.available);
|
|
14643
|
+
}
|
|
14644
|
+
/**
|
|
14645
|
+
* 获取所有工具(包括 available: false 的)
|
|
14646
|
+
*/
|
|
14647
|
+
function getAllTools() {
|
|
14648
|
+
return AI_TOOLS;
|
|
14649
|
+
}
|
|
14650
|
+
/** 状态缓存:projectDir -> ReactiveState */
|
|
14651
|
+
const stateCache = /* @__PURE__ */ new Map();
|
|
14652
|
+
/** 监听器释放函数缓存 */
|
|
14653
|
+
const releaseCache = /* @__PURE__ */ new Map();
|
|
14654
|
+
/**
|
|
14655
|
+
* 检查文件是否存在
|
|
14656
|
+
*/
|
|
14657
|
+
async function fileExists(filePath) {
|
|
14658
|
+
try {
|
|
14659
|
+
await stat(filePath);
|
|
14660
|
+
return true;
|
|
14661
|
+
} catch {
|
|
14662
|
+
return false;
|
|
14663
|
+
}
|
|
14664
|
+
}
|
|
14665
|
+
/**
|
|
14666
|
+
* 解析工具的检测路径
|
|
14667
|
+
* @param config 工具配置
|
|
14668
|
+
* @param projectDir 项目根目录
|
|
14669
|
+
* @returns 绝对路径,如果无检测路径则返回 undefined
|
|
14670
|
+
*/
|
|
14671
|
+
function resolveDetectionPath(config, projectDir) {
|
|
14672
|
+
if (config.scope === "none" || !config.detectionPath) return;
|
|
14673
|
+
if (config.scope === "global") return config.detectionPath();
|
|
14674
|
+
return join$1(projectDir, config.detectionPath);
|
|
14675
|
+
}
|
|
14676
|
+
/**
|
|
14677
|
+
* 扫描已配置的工具(并行检查)
|
|
14678
|
+
*/
|
|
14679
|
+
async function scanConfiguredTools(projectDir) {
|
|
14680
|
+
return (await Promise.all(AI_TOOLS.map(async (config) => {
|
|
14681
|
+
const filePath = resolveDetectionPath(config, projectDir);
|
|
14682
|
+
if (!filePath) return null;
|
|
14683
|
+
return await fileExists(filePath) ? config.value : null;
|
|
14684
|
+
}))).filter((id) => id !== null);
|
|
14685
|
+
}
|
|
14686
|
+
/**
|
|
14687
|
+
* 获取需要监听的项目级目录列表
|
|
14688
|
+
* 只监听包含工具配置的一级隐藏目录
|
|
14689
|
+
*/
|
|
14690
|
+
function getProjectWatchDirs(projectDir) {
|
|
14691
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
14692
|
+
for (const config of AI_TOOLS) if (config.scope === "project" && config.detectionPath) {
|
|
14693
|
+
const firstDir = config.detectionPath.split("/")[0];
|
|
14694
|
+
if (firstDir) dirs.add(join$1(projectDir, firstDir));
|
|
14695
|
+
}
|
|
14696
|
+
return Array.from(dirs);
|
|
14697
|
+
}
|
|
14698
|
+
/**
|
|
14699
|
+
* 获取需要监听的全局目录列表
|
|
14700
|
+
* 如 Codex 的 ~/.codex/prompts/
|
|
13949
14701
|
*/
|
|
13950
|
-
function
|
|
13951
|
-
|
|
14702
|
+
function getGlobalWatchDirs() {
|
|
14703
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
14704
|
+
for (const config of AI_TOOLS) if (config.scope === "global" && config.detectionPath) {
|
|
14705
|
+
const filePath = config.detectionPath();
|
|
14706
|
+
dirs.add(dirname$1(filePath));
|
|
14707
|
+
}
|
|
14708
|
+
return Array.from(dirs);
|
|
13952
14709
|
}
|
|
13953
14710
|
/**
|
|
13954
14711
|
* 检测项目中已配置的工具(响应式)
|
|
13955
14712
|
*
|
|
13956
|
-
*
|
|
13957
|
-
*
|
|
14713
|
+
* 监听两类目录:
|
|
14714
|
+
* 1. 项目级配置目录(如 .claude, .cursor 等)
|
|
14715
|
+
* 2. 全局配置目录(如 ~/.codex/prompts/)
|
|
13958
14716
|
*
|
|
13959
14717
|
* @param projectDir 项目根目录
|
|
13960
14718
|
* @returns 已配置的工具 ID 列表
|
|
13961
14719
|
*/
|
|
13962
14720
|
async function getConfiguredTools(projectDir) {
|
|
13963
|
-
const
|
|
13964
|
-
|
|
13965
|
-
|
|
14721
|
+
const normalizedPath = resolve$1(projectDir);
|
|
14722
|
+
const key = `tools:${normalizedPath}`;
|
|
14723
|
+
let state = stateCache.get(key);
|
|
14724
|
+
if (!state) {
|
|
14725
|
+
state = new ReactiveState(await scanConfiguredTools(normalizedPath), { equals: (a, b) => a.length === b.length && a.every((v, i) => v === b[i]) });
|
|
14726
|
+
stateCache.set(key, state);
|
|
14727
|
+
const releases = [];
|
|
14728
|
+
const onUpdate = async () => {
|
|
14729
|
+
const newValue = await scanConfiguredTools(normalizedPath);
|
|
14730
|
+
state.set(newValue);
|
|
14731
|
+
};
|
|
14732
|
+
const projectWatchDirs = getProjectWatchDirs(normalizedPath);
|
|
14733
|
+
for (const dir of projectWatchDirs) {
|
|
14734
|
+
const release = acquireWatcher(dir, onUpdate, { recursive: true });
|
|
14735
|
+
releases.push(release);
|
|
14736
|
+
}
|
|
14737
|
+
const globalWatchDirs = getGlobalWatchDirs();
|
|
14738
|
+
for (const dir of globalWatchDirs) {
|
|
14739
|
+
const release = acquireWatcher(dir, onUpdate, { recursive: false });
|
|
14740
|
+
releases.push(release);
|
|
14741
|
+
}
|
|
14742
|
+
const rootRelease = acquireWatcher(normalizedPath, onUpdate, { recursive: false });
|
|
14743
|
+
releases.push(rootRelease);
|
|
14744
|
+
releaseCache.set(key, () => releases.forEach((r) => r()));
|
|
14745
|
+
}
|
|
14746
|
+
return state.get();
|
|
13966
14747
|
}
|
|
13967
14748
|
|
|
13968
14749
|
//#endregion
|
|
@@ -14023,6 +14804,63 @@ function createReactiveSubscriptionWithInput(task) {
|
|
|
14023
14804
|
};
|
|
14024
14805
|
}
|
|
14025
14806
|
|
|
14807
|
+
//#endregion
|
|
14808
|
+
//#region ../server/src/cli-stream-observable.ts
|
|
14809
|
+
/**
|
|
14810
|
+
* 创建安全的 CLI 流式 observable
|
|
14811
|
+
*
|
|
14812
|
+
* 解决的问题:
|
|
14813
|
+
* 1. 防止在 emit.complete() 之后调用 emit.next()(会导致 "Controller is already closed" 错误)
|
|
14814
|
+
* 2. 统一的错误处理,防止未捕获的异常导致服务器崩溃
|
|
14815
|
+
* 3. 确保取消时正确清理资源
|
|
14816
|
+
*
|
|
14817
|
+
* @param startStream 启动流的函数,接收 onEvent 回调,返回取消函数的 Promise
|
|
14818
|
+
*/
|
|
14819
|
+
function createCliStreamObservable(startStream) {
|
|
14820
|
+
return observable((emit) => {
|
|
14821
|
+
let cancel;
|
|
14822
|
+
let completed = false;
|
|
14823
|
+
/**
|
|
14824
|
+
* 安全的事件处理器
|
|
14825
|
+
* - 检查是否已完成,防止重复调用
|
|
14826
|
+
* - 使用 try-catch 防止异常导致服务器崩溃
|
|
14827
|
+
*/
|
|
14828
|
+
const safeEventHandler = (event) => {
|
|
14829
|
+
if (completed) return;
|
|
14830
|
+
try {
|
|
14831
|
+
emit.next(event);
|
|
14832
|
+
if (event.type === "exit") {
|
|
14833
|
+
completed = true;
|
|
14834
|
+
emit.complete();
|
|
14835
|
+
}
|
|
14836
|
+
} catch (err) {
|
|
14837
|
+
console.error("[CLI Stream] Error emitting event:", err);
|
|
14838
|
+
if (!completed) {
|
|
14839
|
+
completed = true;
|
|
14840
|
+
try {
|
|
14841
|
+
emit.error(err instanceof Error ? err : new Error(String(err)));
|
|
14842
|
+
} catch {}
|
|
14843
|
+
}
|
|
14844
|
+
}
|
|
14845
|
+
};
|
|
14846
|
+
startStream(safeEventHandler).then((cancelFn) => {
|
|
14847
|
+
cancel = cancelFn;
|
|
14848
|
+
}).catch((err) => {
|
|
14849
|
+
console.error("[CLI Stream] Error starting stream:", err);
|
|
14850
|
+
if (!completed) {
|
|
14851
|
+
completed = true;
|
|
14852
|
+
try {
|
|
14853
|
+
emit.error(err instanceof Error ? err : new Error(String(err)));
|
|
14854
|
+
} catch {}
|
|
14855
|
+
}
|
|
14856
|
+
});
|
|
14857
|
+
return () => {
|
|
14858
|
+
completed = true;
|
|
14859
|
+
cancel?.();
|
|
14860
|
+
};
|
|
14861
|
+
});
|
|
14862
|
+
}
|
|
14863
|
+
|
|
14026
14864
|
//#endregion
|
|
14027
14865
|
//#region ../server/src/router.ts
|
|
14028
14866
|
const t = initTRPC.context().create();
|
|
@@ -14335,6 +15173,12 @@ const configRouter = router({
|
|
|
14335
15173
|
get: publicProcedure.query(async ({ ctx }) => {
|
|
14336
15174
|
return ctx.configManager.readConfig();
|
|
14337
15175
|
}),
|
|
15176
|
+
getEffectiveCliCommand: publicProcedure.query(async ({ ctx }) => {
|
|
15177
|
+
return ctx.configManager.getCliCommandString();
|
|
15178
|
+
}),
|
|
15179
|
+
getDefaultCliCommand: publicProcedure.query(async () => {
|
|
15180
|
+
return getDefaultCliCommandString();
|
|
15181
|
+
}),
|
|
14338
15182
|
update: publicProcedure.input(objectType({
|
|
14339
15183
|
cli: objectType({ command: stringType() }).optional(),
|
|
14340
15184
|
ui: objectType({ theme: enumType([
|
|
@@ -14361,8 +15205,40 @@ const cliRouter = router({
|
|
|
14361
15205
|
checkAvailability: publicProcedure.query(async ({ ctx }) => {
|
|
14362
15206
|
return ctx.cliExecutor.checkAvailability();
|
|
14363
15207
|
}),
|
|
15208
|
+
sniffGlobalCli: publicProcedure.query(async () => {
|
|
15209
|
+
return sniffGlobalCli();
|
|
15210
|
+
}),
|
|
15211
|
+
installGlobalCliStream: publicProcedure.subscription(({ ctx }) => {
|
|
15212
|
+
return observable((emit) => {
|
|
15213
|
+
const cancel = ctx.cliExecutor.executeCommandStream([
|
|
15214
|
+
"npm",
|
|
15215
|
+
"install",
|
|
15216
|
+
"-g",
|
|
15217
|
+
"@fission-ai/openspec"
|
|
15218
|
+
], (event) => {
|
|
15219
|
+
emit.next(event);
|
|
15220
|
+
if (event.type === "exit") emit.complete();
|
|
15221
|
+
});
|
|
15222
|
+
return () => {
|
|
15223
|
+
cancel();
|
|
15224
|
+
};
|
|
15225
|
+
});
|
|
15226
|
+
}),
|
|
14364
15227
|
getAvailableTools: publicProcedure.query(() => {
|
|
14365
|
-
return
|
|
15228
|
+
return getAvailableTools().map((tool) => ({
|
|
15229
|
+
name: tool.name,
|
|
15230
|
+
value: tool.value,
|
|
15231
|
+
available: tool.available,
|
|
15232
|
+
successLabel: tool.successLabel
|
|
15233
|
+
}));
|
|
15234
|
+
}),
|
|
15235
|
+
getAllTools: publicProcedure.query(() => {
|
|
15236
|
+
return getAllTools().map((tool) => ({
|
|
15237
|
+
name: tool.name,
|
|
15238
|
+
value: tool.value,
|
|
15239
|
+
available: tool.available,
|
|
15240
|
+
successLabel: tool.successLabel
|
|
15241
|
+
}));
|
|
14366
15242
|
}),
|
|
14367
15243
|
getConfiguredTools: publicProcedure.query(async ({ ctx }) => {
|
|
14368
15244
|
return getConfiguredTools(ctx.projectDir);
|
|
@@ -14395,6 +15271,23 @@ const cliRouter = router({
|
|
|
14395
15271
|
}),
|
|
14396
15272
|
execute: publicProcedure.input(objectType({ args: arrayType(stringType()) })).mutation(async ({ ctx, input }) => {
|
|
14397
15273
|
return ctx.cliExecutor.execute(input.args);
|
|
15274
|
+
}),
|
|
15275
|
+
initStream: publicProcedure.input(objectType({ tools: unionType([
|
|
15276
|
+
arrayType(stringType()),
|
|
15277
|
+
literalType("all"),
|
|
15278
|
+
literalType("none")
|
|
15279
|
+
]).optional() }).optional()).subscription(({ ctx, input }) => {
|
|
15280
|
+
return createCliStreamObservable((onEvent) => ctx.cliExecutor.initStream(input?.tools ?? "all", onEvent));
|
|
15281
|
+
}),
|
|
15282
|
+
archiveStream: publicProcedure.input(objectType({
|
|
15283
|
+
changeId: stringType(),
|
|
15284
|
+
skipSpecs: booleanType().optional(),
|
|
15285
|
+
noValidate: booleanType().optional()
|
|
15286
|
+
})).subscription(({ ctx, input }) => {
|
|
15287
|
+
return createCliStreamObservable((onEvent) => ctx.cliExecutor.archiveStream(input.changeId, {
|
|
15288
|
+
skipSpecs: input.skipSpecs,
|
|
15289
|
+
noValidate: input.noValidate
|
|
15290
|
+
}, onEvent));
|
|
14398
15291
|
})
|
|
14399
15292
|
});
|
|
14400
15293
|
/**
|
|
@@ -14413,6 +15306,41 @@ const appRouter = router({
|
|
|
14413
15306
|
cli: cliRouter
|
|
14414
15307
|
});
|
|
14415
15308
|
|
|
15309
|
+
//#endregion
|
|
15310
|
+
//#region ../server/src/port-utils.ts
|
|
15311
|
+
/**
|
|
15312
|
+
* Check if a port is available by trying to listen on it.
|
|
15313
|
+
* Uses default binding (both IPv4 and IPv6) to detect conflicts.
|
|
15314
|
+
*/
|
|
15315
|
+
function isPortAvailable(port) {
|
|
15316
|
+
return new Promise((resolve$2) => {
|
|
15317
|
+
const server = createServer$1();
|
|
15318
|
+
server.once("error", () => {
|
|
15319
|
+
resolve$2(false);
|
|
15320
|
+
});
|
|
15321
|
+
server.once("listening", () => {
|
|
15322
|
+
server.close(() => resolve$2(true));
|
|
15323
|
+
});
|
|
15324
|
+
server.listen(port);
|
|
15325
|
+
});
|
|
15326
|
+
}
|
|
15327
|
+
/**
|
|
15328
|
+
* Find an available port starting from the given port.
|
|
15329
|
+
* Will try up to maxAttempts ports sequentially.
|
|
15330
|
+
*
|
|
15331
|
+
* @param startPort - The preferred port to start checking from
|
|
15332
|
+
* @param maxAttempts - Maximum number of ports to try (default: 10)
|
|
15333
|
+
* @returns The first available port found
|
|
15334
|
+
* @throws Error if no available port is found in the range
|
|
15335
|
+
*/
|
|
15336
|
+
async function findAvailablePort(startPort, maxAttempts = 10) {
|
|
15337
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
15338
|
+
const port = startPort + i;
|
|
15339
|
+
if (await isPortAvailable(port)) return port;
|
|
15340
|
+
}
|
|
15341
|
+
throw new Error(`No available port found in range ${startPort}-${startPort + maxAttempts - 1}`);
|
|
15342
|
+
}
|
|
15343
|
+
|
|
14416
15344
|
//#endregion
|
|
14417
15345
|
//#region ../server/src/server.ts
|
|
14418
15346
|
/**
|
|
@@ -14486,7 +15414,8 @@ function createServer$2(config) {
|
|
|
14486
15414
|
/**
|
|
14487
15415
|
* Create WebSocket server for tRPC subscriptions
|
|
14488
15416
|
*/
|
|
14489
|
-
function createWebSocketServer(server, httpServer) {
|
|
15417
|
+
async function createWebSocketServer(server, httpServer, config) {
|
|
15418
|
+
await initWatcherPool(config.projectDir);
|
|
14490
15419
|
const wss = new import_websocket_server.default({ noServer: true });
|
|
14491
15420
|
const handler = applyWSSHandler({
|
|
14492
15421
|
wss,
|
|
@@ -14510,45 +15439,42 @@ function createWebSocketServer(server, httpServer) {
|
|
|
14510
15439
|
}
|
|
14511
15440
|
};
|
|
14512
15441
|
}
|
|
14513
|
-
|
|
14514
|
-
//#endregion
|
|
14515
|
-
//#region src/index.ts
|
|
14516
|
-
const __dirname = dirname$1(fileURLToPath(import.meta.url));
|
|
14517
15442
|
/**
|
|
14518
|
-
*
|
|
14519
|
-
*
|
|
15443
|
+
* Start the OpenSpec UI server with WebSocket support.
|
|
15444
|
+
* Automatically finds an available port if the preferred port is occupied.
|
|
15445
|
+
*
|
|
15446
|
+
* @param config - Server configuration
|
|
15447
|
+
* @param setupApp - Optional callback to configure the Hono app before starting (e.g., add static file middleware)
|
|
15448
|
+
* @returns Running server instance with actual port and close function
|
|
14520
15449
|
*/
|
|
14521
|
-
function
|
|
14522
|
-
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
server1.close(() => {
|
|
14529
|
-
const server2 = createServer$1();
|
|
14530
|
-
server2.once("error", () => {
|
|
14531
|
-
resolve$2(false);
|
|
14532
|
-
});
|
|
14533
|
-
server2.once("listening", () => {
|
|
14534
|
-
server2.close(() => resolve$2(true));
|
|
14535
|
-
});
|
|
14536
|
-
server2.listen(port, "0.0.0.0");
|
|
14537
|
-
});
|
|
14538
|
-
});
|
|
14539
|
-
server1.listen(port, "127.0.0.1");
|
|
15450
|
+
async function startServer(config, setupApp) {
|
|
15451
|
+
const preferredPort = config.port ?? 3100;
|
|
15452
|
+
const port = await findAvailablePort(preferredPort);
|
|
15453
|
+
console.log("QAQ", preferredPort, port);
|
|
15454
|
+
const server = createServer$2({
|
|
15455
|
+
...config,
|
|
15456
|
+
port
|
|
14540
15457
|
});
|
|
15458
|
+
if (setupApp) setupApp(server.app);
|
|
15459
|
+
const httpServer = serve({
|
|
15460
|
+
fetch: server.app.fetch,
|
|
15461
|
+
port
|
|
15462
|
+
});
|
|
15463
|
+
const wsServer = await createWebSocketServer(server, httpServer, { projectDir: config.projectDir });
|
|
15464
|
+
return {
|
|
15465
|
+
url: `http://localhost:${port}`,
|
|
15466
|
+
port,
|
|
15467
|
+
preferredPort,
|
|
15468
|
+
close: async () => {
|
|
15469
|
+
wsServer.close();
|
|
15470
|
+
httpServer.close();
|
|
15471
|
+
}
|
|
15472
|
+
};
|
|
14541
15473
|
}
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
14547
|
-
const port = startPort + i;
|
|
14548
|
-
if (await isPortAvailable(port)) return port;
|
|
14549
|
-
}
|
|
14550
|
-
throw new Error(`No available port found in range ${startPort}-${startPort + maxAttempts - 1}`);
|
|
14551
|
-
}
|
|
15474
|
+
|
|
15475
|
+
//#endregion
|
|
15476
|
+
//#region src/index.ts
|
|
15477
|
+
const __dirname = dirname$1(fileURLToPath(import.meta.url));
|
|
14552
15478
|
/**
|
|
14553
15479
|
* Get the path to the web assets directory
|
|
14554
15480
|
*/
|
|
@@ -14589,39 +15515,32 @@ function createDefaultProviders() {
|
|
|
14589
15515
|
};
|
|
14590
15516
|
}
|
|
14591
15517
|
/**
|
|
14592
|
-
*
|
|
15518
|
+
* Setup static file serving middleware for the Hono app
|
|
14593
15519
|
*/
|
|
14594
|
-
|
|
14595
|
-
const { projectDir = process.cwd(), port: preferredPort = 3100, providers = createDefaultProviders(), enableWatcher = true } = options;
|
|
14596
|
-
const port = await findAvailablePort(preferredPort);
|
|
14597
|
-
const server = createServer$2({
|
|
14598
|
-
projectDir,
|
|
14599
|
-
port,
|
|
14600
|
-
providers,
|
|
14601
|
-
enableWatcher
|
|
14602
|
-
});
|
|
15520
|
+
function setupStaticFiles(app) {
|
|
14603
15521
|
const webDir = getWebAssetsDir();
|
|
14604
|
-
|
|
15522
|
+
const mimeTypes = {
|
|
15523
|
+
html: "text/html",
|
|
15524
|
+
js: "application/javascript",
|
|
15525
|
+
css: "text/css",
|
|
15526
|
+
json: "application/json",
|
|
15527
|
+
png: "image/png",
|
|
15528
|
+
jpg: "image/jpeg",
|
|
15529
|
+
jpeg: "image/jpeg",
|
|
15530
|
+
gif: "image/gif",
|
|
15531
|
+
svg: "image/svg+xml",
|
|
15532
|
+
ico: "image/x-icon",
|
|
15533
|
+
woff: "font/woff",
|
|
15534
|
+
woff2: "font/woff2",
|
|
15535
|
+
ttf: "font/ttf"
|
|
15536
|
+
};
|
|
15537
|
+
app.use("/*", async (c, next) => {
|
|
14605
15538
|
const path$1 = c.req.path === "/" ? "/index.html" : c.req.path;
|
|
14606
15539
|
if (path$1.startsWith("/trpc")) return next();
|
|
14607
15540
|
const filePath = join$1(webDir, path$1);
|
|
14608
15541
|
if (existsSync(filePath) && statSync(filePath).isFile()) {
|
|
14609
15542
|
const content = readFileSync(filePath);
|
|
14610
|
-
const contentType =
|
|
14611
|
-
html: "text/html",
|
|
14612
|
-
js: "application/javascript",
|
|
14613
|
-
css: "text/css",
|
|
14614
|
-
json: "application/json",
|
|
14615
|
-
png: "image/png",
|
|
14616
|
-
jpg: "image/jpeg",
|
|
14617
|
-
jpeg: "image/jpeg",
|
|
14618
|
-
gif: "image/gif",
|
|
14619
|
-
svg: "image/svg+xml",
|
|
14620
|
-
ico: "image/x-icon",
|
|
14621
|
-
woff: "font/woff",
|
|
14622
|
-
woff2: "font/woff2",
|
|
14623
|
-
ttf: "font/ttf"
|
|
14624
|
-
}[path$1.split(".").pop() || ""] || "application/octet-stream";
|
|
15543
|
+
const contentType = mimeTypes[path$1.split(".").pop() || ""] || "application/octet-stream";
|
|
14625
15544
|
return c.body(content, 200, { "Content-Type": contentType });
|
|
14626
15545
|
}
|
|
14627
15546
|
if (!path$1.includes(".")) {
|
|
@@ -14633,20 +15552,20 @@ async function startServer(options = {}) {
|
|
|
14633
15552
|
}
|
|
14634
15553
|
return c.notFound();
|
|
14635
15554
|
});
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
|
|
14639
|
-
|
|
14640
|
-
|
|
14641
|
-
|
|
14642
|
-
|
|
15555
|
+
}
|
|
15556
|
+
/**
|
|
15557
|
+
* Start the OpenSpec UI server with WebSocket support for realtime updates.
|
|
15558
|
+
* Includes static file serving for the web UI.
|
|
15559
|
+
*/
|
|
15560
|
+
async function startServer$1(options = {}) {
|
|
15561
|
+
const { projectDir = process.cwd(), port = 3100, providers = createDefaultProviders(), enableWatcher = true } = options;
|
|
15562
|
+
return await startServer({
|
|
15563
|
+
projectDir,
|
|
14643
15564
|
port,
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
}
|
|
14648
|
-
};
|
|
15565
|
+
providers,
|
|
15566
|
+
enableWatcher
|
|
15567
|
+
}, setupStaticFiles);
|
|
14649
15568
|
}
|
|
14650
15569
|
|
|
14651
15570
|
//#endregion
|
|
14652
|
-
export { __commonJS$1 as a, ACPAgents as i, createServer$2 as n, __toESM$1 as o, ProviderManager as r, startServer as t };
|
|
15571
|
+
export { __commonJS$1 as a, ACPAgents as i, createServer$2 as n, __toESM$1 as o, ProviderManager as r, startServer$1 as t };
|