@shwfed/config 2.3.8 → 2.3.10

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/mcp.mjs CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import process$2 from "node:process";
3
+ import { createServer } from "node:http";
4
+ import { stat, readFile } from "node:fs/promises";
5
+ import { resolve as resolve$1, sep } from "node:path";
6
+ import { fileURLToPath } from "node:url";
3
7
  var util$2;
4
8
  (function(util2) {
5
9
  util2.assertEqual = (_) => {
@@ -3721,7 +3725,7 @@ function slugify(input) {
3721
3725
  }
3722
3726
  const captureStackTrace = "captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => {
3723
3727
  };
3724
- function isObject$3(data) {
3728
+ function isObject$4(data) {
3725
3729
  return typeof data === "object" && data !== null && !Array.isArray(data);
3726
3730
  }
3727
3731
  const allowsEval = /* @__PURE__ */ cached$1(() => {
@@ -3740,7 +3744,7 @@ const allowsEval = /* @__PURE__ */ cached$1(() => {
3740
3744
  }
3741
3745
  });
3742
3746
  function isPlainObject$1(o) {
3743
- if (isObject$3(o) === false)
3747
+ if (isObject$4(o) === false)
3744
3748
  return false;
3745
3749
  const ctor = o.constructor;
3746
3750
  if (ctor === void 0)
@@ -3748,7 +3752,7 @@ function isPlainObject$1(o) {
3748
3752
  if (typeof ctor !== "function")
3749
3753
  return true;
3750
3754
  const prot = ctor.prototype;
3751
- if (isObject$3(prot) === false)
3755
+ if (isObject$4(prot) === false)
3752
3756
  return false;
3753
3757
  if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
3754
3758
  return false;
@@ -5311,7 +5315,7 @@ const $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
5311
5315
  }
5312
5316
  return propValues;
5313
5317
  });
5314
- const isObject2 = isObject$3;
5318
+ const isObject2 = isObject$4;
5315
5319
  const catchall = def.catchall;
5316
5320
  let value;
5317
5321
  inst._zod.parse = (payload, ctx) => {
@@ -5444,7 +5448,7 @@ const $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def)
5444
5448
  return (payload, ctx) => fn(shape, payload, ctx);
5445
5449
  };
5446
5450
  let fastpass;
5447
- const isObject2 = isObject$3;
5451
+ const isObject2 = isObject$4;
5448
5452
  const jit = !globalConfig.jitless;
5449
5453
  const allowsEval$1 = allowsEval;
5450
5454
  const fastEnabled = jit && allowsEval$1.value;
@@ -5576,7 +5580,7 @@ const $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUn
5576
5580
  });
5577
5581
  inst._zod.parse = (payload, ctx) => {
5578
5582
  const input = payload.value;
5579
- if (!isObject$3(input)) {
5583
+ if (!isObject$4(input)) {
5580
5584
  payload.issues.push({
5581
5585
  code: "invalid_type",
5582
5586
  expected: "object",
@@ -15398,7 +15402,7 @@ function requireCompile() {
15398
15402
  }
15399
15403
  }
15400
15404
  compile$1.compileSchema = compileSchema;
15401
- function resolveRef(root, baseId, ref2) {
15405
+ function resolveRef2(root, baseId, ref2) {
15402
15406
  var _a2;
15403
15407
  ref2 = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref2);
15404
15408
  const schOrFunc = root.refs[ref2];
@@ -15415,7 +15419,7 @@ function requireCompile() {
15415
15419
  return;
15416
15420
  return root.refs[ref2] = inlineOrCompile.call(this, _sch);
15417
15421
  }
15418
- compile$1.resolveRef = resolveRef;
15422
+ compile$1.resolveRef = resolveRef2;
15419
15423
  function inlineOrCompile(sch) {
15420
15424
  if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs))
15421
15425
  return sch.schema;
@@ -20717,6 +20721,111 @@ class StdioServerTransport {
20717
20721
  });
20718
20722
  }
20719
20723
  }
20724
+ const ASSETS_DIR = fileURLToPath(new URL("./preview/", import.meta.url));
20725
+ const MIME = {
20726
+ ".html": "text/html; charset=utf-8",
20727
+ ".js": "application/javascript; charset=utf-8",
20728
+ ".mjs": "application/javascript; charset=utf-8",
20729
+ ".css": "text/css; charset=utf-8",
20730
+ ".json": "application/json; charset=utf-8",
20731
+ ".svg": "image/svg+xml",
20732
+ ".png": "image/png",
20733
+ ".jpg": "image/jpeg",
20734
+ ".jpeg": "image/jpeg",
20735
+ ".gif": "image/gif",
20736
+ ".ico": "image/x-icon",
20737
+ ".woff": "font/woff",
20738
+ ".woff2": "font/woff2",
20739
+ ".map": "application/json; charset=utf-8"
20740
+ };
20741
+ function mimeFor(path) {
20742
+ const dot = path.lastIndexOf(".");
20743
+ if (dot === -1) return "application/octet-stream";
20744
+ return MIME[path.slice(dot).toLowerCase()] ?? "application/octet-stream";
20745
+ }
20746
+ let server$1 = null;
20747
+ let serverUrl = null;
20748
+ let currentConfig = null;
20749
+ async function assetsAvailable() {
20750
+ try {
20751
+ const s = await stat(resolve$1(ASSETS_DIR, "index.html"));
20752
+ return s.isFile();
20753
+ } catch {
20754
+ return false;
20755
+ }
20756
+ }
20757
+ function injectConfig(html, config2) {
20758
+ const json = JSON.stringify(config2).replace(/</g, "\\u003c");
20759
+ const script = `<script>window.__SHWFED_PREVIEW_CONFIG__ = ${json};<\/script>`;
20760
+ const marker = '<script type="module"';
20761
+ const idx = html.indexOf(marker);
20762
+ if (idx === -1) return html.replace("</head>", `${script}</head>`);
20763
+ return `${html.slice(0, idx)}${script}${html.slice(idx)}`;
20764
+ }
20765
+ async function handleRequest(url) {
20766
+ const cleanPath = url.split("?")[0].split("#")[0];
20767
+ const route = cleanPath === "/" || cleanPath === "" ? "/index.html" : cleanPath;
20768
+ const absolute = resolve$1(ASSETS_DIR, `.${route}`);
20769
+ const root = ASSETS_DIR.endsWith(sep) ? ASSETS_DIR : `${ASSETS_DIR}${sep}`;
20770
+ if (absolute !== ASSETS_DIR.replace(/\/$/, "") && !absolute.startsWith(root)) {
20771
+ return { status: 403, headers: { "content-type": "text/plain" }, body: "forbidden" };
20772
+ }
20773
+ try {
20774
+ const file = await readFile(absolute);
20775
+ if (route === "/index.html") {
20776
+ const injected = injectConfig(file.toString("utf8"), currentConfig);
20777
+ return {
20778
+ status: 200,
20779
+ headers: { "content-type": MIME[".html"], "cache-control": "no-store" },
20780
+ body: injected
20781
+ };
20782
+ }
20783
+ return {
20784
+ status: 200,
20785
+ headers: { "content-type": mimeFor(route), "cache-control": "no-store" },
20786
+ body: file
20787
+ };
20788
+ } catch {
20789
+ return { status: 404, headers: { "content-type": "text/plain" }, body: "not found" };
20790
+ }
20791
+ }
20792
+ async function startPreview(config2) {
20793
+ if (!await assetsAvailable()) {
20794
+ throw new Error(
20795
+ `Preview assets not found at ${ASSETS_DIR}. The package's preview bundle is missing — reinstall @shwfed/config or run \`vite build --config vite.preview.config.ts\` if working from source.`
20796
+ );
20797
+ }
20798
+ currentConfig = config2;
20799
+ if (server$1 && serverUrl) return { url: serverUrl };
20800
+ const next = createServer((req, res) => {
20801
+ handleRequest(req.url ?? "/").then((result) => {
20802
+ res.writeHead(result.status, result.headers);
20803
+ res.end(result.body);
20804
+ }).catch((err) => {
20805
+ res.writeHead(500, { "content-type": "text/plain" });
20806
+ res.end(err instanceof Error ? err.message : String(err));
20807
+ });
20808
+ });
20809
+ await new Promise((resolve2, reject) => {
20810
+ next.once("error", reject);
20811
+ next.listen(0, "127.0.0.1", () => resolve2());
20812
+ });
20813
+ next.unref();
20814
+ const addr = next.address();
20815
+ const port = typeof addr === "object" && addr ? addr.port : 0;
20816
+ server$1 = next;
20817
+ serverUrl = `http://127.0.0.1:${port}/`;
20818
+ return { url: serverUrl };
20819
+ }
20820
+ async function stopPreview() {
20821
+ if (!server$1) return { stopped: false };
20822
+ const closing = server$1;
20823
+ server$1 = null;
20824
+ serverUrl = null;
20825
+ currentConfig = null;
20826
+ await new Promise((resolve2) => closing.close(() => resolve2()));
20827
+ return { stopped: true };
20828
+ }
20720
20829
  const isFunction$2 = (input) => typeof input === "function";
20721
20830
  const dual = function(arity, body) {
20722
20831
  if (typeof arity === "function") {
@@ -20854,8 +20963,8 @@ const isNotUndefined = (input) => input !== void 0;
20854
20963
  const isNotNull = (input) => input !== null;
20855
20964
  const isNever = (_) => false;
20856
20965
  const isRecordOrArray = (input) => typeof input === "object" && input !== null;
20857
- const isObject$2 = (input) => isRecordOrArray(input) || isFunction$1(input);
20858
- const hasProperty = /* @__PURE__ */ dual(2, (self2, property) => isObject$2(self2) && property in self2);
20966
+ const isObject$3 = (input) => isRecordOrArray(input) || isFunction$1(input);
20967
+ const hasProperty = /* @__PURE__ */ dual(2, (self2, property) => isObject$3(self2) && property in self2);
20859
20968
  const isTagged = /* @__PURE__ */ dual(2, (self2, tag2) => hasProperty(self2, "_tag") && self2["_tag"] === tag2);
20860
20969
  const isNullable = (input) => input === null || input === void 0;
20861
20970
  const isNotNullable = (input) => input !== null && input !== void 0;
@@ -21292,7 +21401,7 @@ function formatUnknown(input, options) {
21292
21401
  seen.add(v);
21293
21402
  return `${v.constructor.name}(${go2(Array.from(v), d)})`;
21294
21403
  }
21295
- if (isObject$2(v)) {
21404
+ if (isObject$3(v)) {
21296
21405
  if (seen.has(v)) return CIRCULAR;
21297
21406
  seen.add(v);
21298
21407
  const keys2 = ownKeys(v);
@@ -21837,7 +21946,7 @@ const dedupeWith = /* @__PURE__ */ dual(2, (self2, isEquivalent) => {
21837
21946
  return [];
21838
21947
  });
21839
21948
  const dedupe = (self2) => dedupeWith(self2, equivalence());
21840
- const join$2 = /* @__PURE__ */ dual(2, (self2, sep) => fromIterable$6(self2).join(sep));
21949
+ const join$2 = /* @__PURE__ */ dual(2, (self2, sep2) => fromIterable$6(self2).join(sep2));
21841
21950
  const getKeysForIndexSignature = (input, parameter) => {
21842
21951
  switch (parameter._tag) {
21843
21952
  case "StringKeyword":
@@ -26336,7 +26445,7 @@ const capture = (obj, span2) => {
26336
26445
  }
26337
26446
  return obj;
26338
26447
  };
26339
- const die$2 = (defect) => isObject$2(defect) && !(spanSymbol in defect) ? withFiberRuntime$1((fiber) => failCause$5(die$3(capture(defect, currentSpanFromFiber(fiber))))) : failCause$5(die$3(defect));
26448
+ const die$2 = (defect) => isObject$3(defect) && !(spanSymbol in defect) ? withFiberRuntime$1((fiber) => failCause$5(die$3(capture(defect, currentSpanFromFiber(fiber))))) : failCause$5(die$3(defect));
26340
26449
  const dieMessage$1 = (message2) => failCauseSync$1(() => die$3(new RuntimeException$1(message2)));
26341
26450
  const either$1 = (self2) => matchEffect(self2, {
26342
26451
  onFailure: (e) => succeed$8(left(e)),
@@ -26346,7 +26455,7 @@ const exit$1 = (self2) => matchCause$1(self2, {
26346
26455
  onFailure: exitFailCause$1,
26347
26456
  onSuccess: exitSucceed$1
26348
26457
  });
26349
- const fail$5 = (error2) => isObject$2(error2) && !(spanSymbol in error2) ? withFiberRuntime$1((fiber) => failCause$5(fail$6(capture(error2, currentSpanFromFiber(fiber))))) : failCause$5(fail$6(error2));
26458
+ const fail$5 = (error2) => isObject$3(error2) && !(spanSymbol in error2) ? withFiberRuntime$1((fiber) => failCause$5(fail$6(capture(error2, currentSpanFromFiber(fiber))))) : failCause$5(fail$6(error2));
26350
26459
  const failSync = (evaluate2) => flatMap$5(sync$2(evaluate2), fail$5);
26351
26460
  const failCause$5 = (cause) => {
26352
26461
  const effect2 = new EffectPrimitiveFailure(OP_FAILURE);
@@ -26481,7 +26590,7 @@ const sync$2 = (thunk) => {
26481
26590
  effect2.effect_instruction_i0 = thunk;
26482
26591
  return effect2;
26483
26592
  };
26484
- const tap$1 = /* @__PURE__ */ dual((args2) => args2.length === 3 || args2.length === 2 && !(isObject$2(args2[1]) && "onlyEffect" in args2[1]), (self2, f) => flatMap$5(self2, (a) => {
26593
+ const tap$1 = /* @__PURE__ */ dual((args2) => args2.length === 3 || args2.length === 2 && !(isObject$3(args2[1]) && "onlyEffect" in args2[1]), (self2, f) => flatMap$5(self2, (a) => {
26485
26594
  const b = typeof f === "function" ? f(a) : f;
26486
26595
  if (isEffect$1(b)) {
26487
26596
  return as$1(b, a);
@@ -34947,7 +35056,7 @@ const go$1 = (ast, isDecoding) => {
34947
35056
  case "SymbolKeyword":
34948
35057
  return fromRefinement(ast, isSymbol);
34949
35058
  case "ObjectKeyword":
34950
- return fromRefinement(ast, isObject$2);
35059
+ return fromRefinement(ast, isObject$3);
34951
35060
  case "Enums":
34952
35061
  return fromRefinement(ast, (u) => ast.enums.some(([_, value]) => value === u));
34953
35062
  case "TemplateLiteral": {
@@ -36414,7 +36523,7 @@ function compactUnion(members) {
36414
36523
  }
36415
36524
  return out;
36416
36525
  }
36417
- const pick = /* @__PURE__ */ dual((args2) => isObject$2(args2[0]), (s, ...keys2) => {
36526
+ const pick = /* @__PURE__ */ dual((args2) => isObject$3(args2[0]), (s, ...keys2) => {
36418
36527
  const out = {};
36419
36528
  for (const k of keys2) {
36420
36529
  if (k in s) {
@@ -36423,7 +36532,7 @@ const pick = /* @__PURE__ */ dual((args2) => isObject$2(args2[0]), (s, ...keys2)
36423
36532
  }
36424
36533
  return out;
36425
36534
  });
36426
- const omit = /* @__PURE__ */ dual((args2) => isObject$2(args2[0]), (s, ...keys2) => {
36535
+ const omit = /* @__PURE__ */ dual((args2) => isObject$3(args2[0]), (s, ...keys2) => {
36427
36536
  const out = {
36428
36537
  ...s
36429
36538
  };
@@ -36507,7 +36616,7 @@ const decodeUnknownEither = (schema2, options) => {
36507
36616
  const decodeUnknownEither2 = decodeUnknownEither$1(schema2, options);
36508
36617
  return (u, overrideOptions) => mapLeft(decodeUnknownEither2(u, overrideOptions), parseError$1);
36509
36618
  };
36510
- const isSchema = (u) => hasProperty(u, TypeId$3) && isObject$2(u[TypeId$3]);
36619
+ const isSchema = (u) => hasProperty(u, TypeId$3) && isObject$3(u[TypeId$3]);
36511
36620
  function getDefaultLiteralAST(literals) {
36512
36621
  return isMembers(literals) ? Union$1.make(mapMembers(literals, (literal2) => new Literal$1(literal2))) : new Literal$1(literals[0]);
36513
36622
  }
@@ -40962,14 +41071,14 @@ function registerFunctions(registry2) {
40962
41071
  throw evaluationError("invalid_regular_expression", `Invalid regular expression: ${b}`);
40963
41072
  }
40964
41073
  });
40965
- functionOverload("string.split(string): list<string>", (s, sep) => s.split(sep));
40966
- functionOverload("string.split(string, number): list<string>", (s, sep, l) => {
41074
+ functionOverload("string.split(string): list<string>", (s, sep2) => s.split(sep2));
41075
+ functionOverload("string.split(string, number): list<string>", (s, sep2, l) => {
40967
41076
  const limit2 = toNum(l);
40968
41077
  if (limit2 === 0) return [];
40969
- const parts2 = s.split(sep);
41078
+ const parts2 = s.split(sep2);
40970
41079
  if (limit2 < 0 || parts2.length <= limit2) return parts2;
40971
41080
  const limited = parts2.slice(0, limit2 - 1);
40972
- limited.push(parts2.slice(limit2 - 1).join(sep));
41081
+ limited.push(parts2.slice(limit2 - 1).join(sep2));
40973
41082
  return limited;
40974
41083
  });
40975
41084
  functionOverload("list<string>.join(): string", (v) => {
@@ -40984,7 +41093,7 @@ function registerFunctions(registry2) {
40984
41093
  }
40985
41094
  return arr.join("");
40986
41095
  });
40987
- functionOverload("list<string>.join(string): string", (v, sep) => {
41096
+ functionOverload("list<string>.join(string): string", (v, sep2) => {
40988
41097
  const arr = v;
40989
41098
  for (let i = 0; i < arr.length; i++) {
40990
41099
  if (typeof arr[i] !== "string") {
@@ -40994,7 +41103,7 @@ function registerFunctions(registry2) {
40994
41103
  );
40995
41104
  }
40996
41105
  }
40997
- return arr.join(sep);
41106
+ return arr.join(sep2);
40998
41107
  });
40999
41108
  const textEncoder = new TextEncoder();
41000
41109
  const textDecoder = new TextDecoder("utf8");
@@ -81787,7 +81896,9 @@ function schema$H(_configure) {
81787
81896
  // This button's entire behaviour: the op-requests it bubbles up the
81788
81897
  // component tree on click. Stored on the button itself — there is no
81789
81898
  // host-level trigger field any more.
81790
- triggers: optional(Triggers)
81899
+ triggers: optional(Triggers.annotations({
81900
+ description: '按列表顺序执行;关闭弹窗类操作的 target 是承载该弹窗的按钮实例(如行操作里的"模态窗口"按钮),而非弹窗本身'
81901
+ }))
81791
81902
  });
81792
81903
  }
81793
81904
  const __vite_glob_0_5$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -87711,7 +87822,7 @@ function _class(obj) {
87711
87822
  function isString(obj) {
87712
87823
  return _class(obj) === "[object String]";
87713
87824
  }
87714
- function isObject$1(obj) {
87825
+ function isObject$2(obj) {
87715
87826
  return _class(obj) === "[object Object]";
87716
87827
  }
87717
87828
  function isRegExp(obj) {
@@ -87838,7 +87949,7 @@ function compile(self2) {
87838
87949
  }
87839
87950
  const compiled = { validate: null, link: null };
87840
87951
  self2.__compiled__[name] = compiled;
87841
- if (isObject$1(val)) {
87952
+ if (isObject$2(val)) {
87842
87953
  if (isRegExp(val.validate)) {
87843
87954
  compiled.validate = createValidator(val.validate);
87844
87955
  } else if (isFunction(val.validate)) {
@@ -91215,7 +91326,7 @@ const __vite_glob_0_7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
91215
91326
  toColumnDef: toColumnDef$5,
91216
91327
  type: type$c
91217
91328
  }, Symbol.toStringTag, { value: "Module" }));
91218
- const isObject = (value) => {
91329
+ const isObject$1 = (value) => {
91219
91330
  const type2 = typeof value;
91220
91331
  return value !== null && (type2 === "object" || type2 === "function");
91221
91332
  };
@@ -91393,7 +91504,7 @@ function normalizePath(path) {
91393
91504
  return [];
91394
91505
  }
91395
91506
  function getProperty(object2, path, value) {
91396
- if (!isObject(object2) || typeof path !== "string" && !Array.isArray(path)) {
91507
+ if (!isObject$1(object2) || typeof path !== "string" && !Array.isArray(path)) {
91397
91508
  return value === void 0 ? object2 : value;
91398
91509
  }
91399
91510
  const pathArray = normalizePath(path);
@@ -92971,17 +93082,163 @@ function validateConfig(input) {
92971
93082
  function getSchema() {
92972
93083
  return make$2(pageSchema);
92973
93084
  }
93085
+ const isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
93086
+ const branchesOf = (node) => {
93087
+ const list2 = node.anyOf ?? node.oneOf;
93088
+ if (!Array.isArray(list2)) return void 0;
93089
+ return list2.filter(isObject);
93090
+ };
93091
+ const typeEnumOf = (branch) => {
93092
+ const props = isObject(branch.properties) ? branch.properties : void 0;
93093
+ const typeProp = props && isObject(props.type) ? props.type : void 0;
93094
+ const en = typeProp?.enum;
93095
+ return Array.isArray(en) ? en.filter((v) => typeof v === "string") : [];
93096
+ };
93097
+ const compatDateOf = (branch) => {
93098
+ const props = isObject(branch.properties) ? branch.properties : void 0;
93099
+ const cd = props && isObject(props.compatibilityDate) ? props.compatibilityDate : void 0;
93100
+ const en = cd?.enum;
93101
+ if (Array.isArray(en) && typeof en[0] === "string") return en[0];
93102
+ return void 0;
93103
+ };
93104
+ const resolveRef = (node, root) => {
93105
+ if (!isObject(node) || typeof node.$ref !== "string") return node;
93106
+ const ref2 = node.$ref;
93107
+ if (!ref2.startsWith("#/")) return node;
93108
+ const parts2 = ref2.slice(2).split("/");
93109
+ let cur = root;
93110
+ for (const raw of parts2) {
93111
+ const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
93112
+ if (!isObject(cur)) return node;
93113
+ cur = cur[key];
93114
+ }
93115
+ return cur;
93116
+ };
93117
+ const parseSegment = (raw) => {
93118
+ let s = raw;
93119
+ const array2 = s.endsWith("[]");
93120
+ if (array2) s = s.slice(0, -2);
93121
+ const at = s.indexOf("@");
93122
+ if (at >= 0) return { name: s.slice(0, at), pin: s.slice(at + 1), array: array2 };
93123
+ return { name: s, array: array2 };
93124
+ };
93125
+ const describeOptions = (node) => {
93126
+ const branches = branchesOf(node);
93127
+ if (branches) {
93128
+ const grouped = /* @__PURE__ */ new Map();
93129
+ for (const b of branches) {
93130
+ for (const t of typeEnumOf(b)) {
93131
+ const dates = grouped.get(t) ?? [];
93132
+ const d = compatDateOf(b);
93133
+ if (d) dates.push(d);
93134
+ grouped.set(t, dates);
93135
+ }
93136
+ }
93137
+ if (grouped.size === 0) return "<unnamed union branches>";
93138
+ return [...grouped.entries()].map(([t, ds]) => ds.length > 1 ? `${t} (${ds.sort().join(", ")})` : t).join(", ");
93139
+ }
93140
+ if (isObject(node.properties)) {
93141
+ const keys2 = Object.keys(node.properties);
93142
+ return keys2.length ? keys2.join(", ") : "<no properties>";
93143
+ }
93144
+ if (node.type === "array") return "[]";
93145
+ return `<${node.type ?? "unknown"}>`;
93146
+ };
93147
+ function narrowSchema(root, path) {
93148
+ const segments = path.split("/").map((s) => s.trim()).filter(Boolean);
93149
+ let node = root;
93150
+ const walked = [];
93151
+ for (const raw of segments) {
93152
+ node = resolveRef(node, root);
93153
+ if (!isObject(node)) {
93154
+ return {
93155
+ ok: false,
93156
+ error: `can't descend into non-object at '${walked.join("/")}'`,
93157
+ atPath: walked.join("/"),
93158
+ options: ""
93159
+ };
93160
+ }
93161
+ const { name, pin, array: array2 } = parseSegment(raw);
93162
+ const branches = branchesOf(node);
93163
+ if (branches) {
93164
+ const matches = branches.filter((b) => typeEnumOf(b).includes(name));
93165
+ if (matches.length === 0) {
93166
+ return {
93167
+ ok: false,
93168
+ error: `no '${name}' branch at '${walked.join("/") || "<root>"}'`,
93169
+ atPath: walked.join("/"),
93170
+ options: describeOptions(node)
93171
+ };
93172
+ }
93173
+ let picked;
93174
+ if (pin) {
93175
+ const pinned = matches.find((b) => compatDateOf(b) === pin);
93176
+ if (!pinned) {
93177
+ const dates = matches.map(compatDateOf).filter(Boolean).join(", ");
93178
+ return {
93179
+ ok: false,
93180
+ error: `no '${name}@${pin}' — available compatibilityDates: ${dates || "<none>"}`,
93181
+ atPath: walked.join("/"),
93182
+ options: dates
93183
+ };
93184
+ }
93185
+ picked = pinned;
93186
+ } else {
93187
+ picked = matches.reduce((best, cur) => {
93188
+ const a = compatDateOf(best) ?? "";
93189
+ const b = compatDateOf(cur) ?? "";
93190
+ return b > a ? cur : best;
93191
+ });
93192
+ }
93193
+ const d = compatDateOf(picked);
93194
+ walked.push(d ? `${name}@${d}` : name);
93195
+ node = picked;
93196
+ } else if (isObject(node.properties) && name in node.properties) {
93197
+ walked.push(raw.replace(/\[\]$/, ""));
93198
+ node = node.properties[name];
93199
+ } else {
93200
+ return {
93201
+ ok: false,
93202
+ error: `can't resolve '${name}' at '${walked.join("/") || "<root>"}'`,
93203
+ atPath: walked.join("/"),
93204
+ options: describeOptions(node)
93205
+ };
93206
+ }
93207
+ if (array2) {
93208
+ node = resolveRef(node, root);
93209
+ if (!isObject(node) || node.type !== "array" || node.items === void 0) {
93210
+ return {
93211
+ ok: false,
93212
+ error: `'${name}' is not an array`,
93213
+ atPath: walked.join("/"),
93214
+ options: ""
93215
+ };
93216
+ }
93217
+ walked[walked.length - 1] = `${walked[walked.length - 1]}[]`;
93218
+ node = node.items;
93219
+ }
93220
+ }
93221
+ node = resolveRef(node, root);
93222
+ return { ok: true, schema: node, resolvedPath: walked.join("/") };
93223
+ }
92974
93224
  const server = new McpServer({
92975
93225
  name: "@shwfed/config",
92976
93226
  version: "0.1.0"
92977
93227
  }, {
92978
- instructions: "Tools for authoring ShwfedConfig JSON. Use `get_schema` once to learn the shape, then iterate with `validate_config` until it returns `{ valid: true }`."
93228
+ instructions: [
93229
+ "Tools for authoring ShwfedConfig JSON. Use `get_schema` once to learn the shape, then iterate with `validate_config` until it returns `{ valid: true }`. Optionally call `preview_config` to render the result in the user's browser via a localhost-only playground; `stop_preview` shuts it down.",
93230
+ "",
93231
+ "Versioned types (`type` + `compatibilityDate`): the same `type` may appear at multiple `compatibilityDate`s with DIFFERENT contracts — they are not interchangeable. Rules:",
93232
+ "1. When creating a new entry, ALWAYS pick the LATEST `compatibilityDate` available for that `type` in the schema. Do not select an older one because it looks simpler or familiar.",
93233
+ "2. When editing an existing config that uses an older `compatibilityDate`, prefer migrating it to the latest version (translate its fields into the newer contract) and tell the user you did so, instead of preserving the old version silently.",
93234
+ "3. Never mix versions of the same `type` across a single config when the latest covers the use case."
93235
+ ].join("\n")
92979
93236
  });
92980
93237
  server.registerTool("validate_config", {
92981
93238
  title: "Validate ShwfedConfig JSON",
92982
93239
  description: "Validates a candidate ShwfedConfig JSON value against the Effect Schema. Returns `{ valid: true }` on success, or `{ valid: false, message, issues: [{ path, message, tag }] }` on failure. CEL expressions are accepted as strings without evaluation.",
92983
93240
  inputSchema: {
92984
- config: unknown().describe("The candidate ShwfedConfig JSON value")
93241
+ config: looseObject({}).describe("The candidate ShwfedConfig JSON value")
92985
93242
  }
92986
93243
  }, async ({ config: config2 }) => {
92987
93244
  const result = validateConfig(config2);
@@ -92993,12 +93250,81 @@ server.registerTool("validate_config", {
92993
93250
  });
92994
93251
  server.registerTool("get_schema", {
92995
93252
  title: "Get ShwfedConfig JSON Schema",
92996
- description: "Returns the JSON Schema (draft-07) describing the full ShwfedConfig shape, including every registered page block, form field, action button, and table column type."
92997
- }, async () => {
93253
+ description: [
93254
+ "Returns the JSON Schema (draft-07) describing the ShwfedConfig shape. The same `type` may be published at multiple `compatibilityDate` values with incompatible contracts; for new entries always select the latest `compatibilityDate` for that `type`, and prefer migrating older entries to it.",
93255
+ "",
93256
+ "Pass `path` to narrow into a sub-schema and avoid pulling the full schema into context. Path syntax (`/`-separated because type names contain dots):",
93257
+ " - `foo` step into `properties.foo`",
93258
+ " - `foo[]` step into an array's `items` after resolving `foo`",
93259
+ " - `<type>` on a union, pick the branch whose `type` discriminator matches; if multiple `compatibilityDate` versions exist, picks the latest",
93260
+ " - `<type>@<date>` pin to a specific compatibilityDate",
93261
+ "Example: `blocks[]/com.shwfed.block.table/columns[]/com.shwfed.table.column.switch`. On a bad segment the error lists the legal next steps."
93262
+ ].join("\n"),
93263
+ inputSchema: {
93264
+ path: string$1().optional().describe("Optional `/`-separated path to narrow into. Omit to return the full schema.")
93265
+ }
93266
+ }, async ({ path }) => {
92998
93267
  const schema2 = getSchema();
93268
+ if (!path) {
93269
+ return {
93270
+ content: [{ type: "text", text: JSON.stringify(schema2, null, 2) }],
93271
+ structuredContent: schema2
93272
+ };
93273
+ }
93274
+ const result = narrowSchema(schema2, path);
93275
+ if (!result.ok) {
93276
+ const payload2 = { error: result.error, atPath: result.atPath, options: result.options };
93277
+ return {
93278
+ content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }],
93279
+ structuredContent: payload2,
93280
+ isError: true
93281
+ };
93282
+ }
93283
+ const payload = { path: result.resolvedPath, schema: result.schema };
92999
93284
  return {
93000
- content: [{ type: "text", text: JSON.stringify(schema2, null, 2) }],
93001
- structuredContent: schema2
93285
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
93286
+ structuredContent: payload
93287
+ };
93288
+ });
93289
+ server.registerTool("preview_config", {
93290
+ title: "Preview ShwfedConfig in browser",
93291
+ description: "Boots a localhost-only preview server (127.0.0.1) that renders the given ShwfedConfig in a real browser using the package's shipped components. Returns the URL for the user to open. The config is validated first; invalid configs are rejected without starting the server. Subsequent calls hot-swap the rendered config and return the same URL — the user reloads the tab to see updates. The preview only knows about components shipped by this package; user-defined extensions in their host app are not visible here. CEL variables (e.g. `token`) are user-managed in the page itself — the agent never sees their values.",
93292
+ inputSchema: {
93293
+ config: looseObject({}).describe("The ShwfedConfig JSON value to render")
93294
+ }
93295
+ }, async ({ config: config2 }) => {
93296
+ const validation2 = validateConfig(config2);
93297
+ if (!validation2.valid) {
93298
+ return {
93299
+ content: [{ type: "text", text: JSON.stringify(validation2, null, 2) }],
93300
+ structuredContent: validation2,
93301
+ isError: true
93302
+ };
93303
+ }
93304
+ try {
93305
+ const { url } = await startPreview(config2);
93306
+ const payload = { url, hint: "Open the URL in a browser. Reload the tab after each preview_config call to pick up changes." };
93307
+ return {
93308
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
93309
+ structuredContent: payload
93310
+ };
93311
+ } catch (err) {
93312
+ const message2 = err instanceof Error ? err.message : String(err);
93313
+ return {
93314
+ content: [{ type: "text", text: message2 }],
93315
+ structuredContent: { error: message2 },
93316
+ isError: true
93317
+ };
93318
+ }
93319
+ });
93320
+ server.registerTool("stop_preview", {
93321
+ title: "Stop the ShwfedConfig preview server",
93322
+ description: "Shuts down the localhost preview server if one is running. Safe to call when nothing is running — returns `{ stopped: false }` in that case."
93323
+ }, async () => {
93324
+ const result = await stopPreview();
93325
+ return {
93326
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
93327
+ structuredContent: result
93002
93328
  };
93003
93329
  });
93004
93330
  const transport = new StdioServerTransport();
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shwfed",
3
3
  "configKey": "shwfed",
4
- "version": "2.3.8",
4
+ "version": "2.3.10",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -0,0 +1 @@
1
+ import{d as y,b as R,ab as U,o as V,c as z,f as x,g as t,w as n,u as s,h as i,j as u,k as c,l as d,_ as p,ac as g,i as f,t as _,F as D,v as F,T as $,x as C,y as H,$ as S}from"./index-OUd02U3g.js";const L={class:"flex flex-col gap-3"},N={class:"grid grid-cols-2 gap-3"},B={class:"flex items-start gap-2"},M={class:"grid grid-cols-2 gap-3"},I=y({name:"ShwfedHttpDownloadActionConfig",__name:"config",props:{modelValue:{required:!0},modelModifiers:{}},emits:["update:modelValue"],setup(b){const l=R(b,"modelValue"),E=U(()=>{}),m=a=>C(E,a)??a,r=a=>H(E,a),j=["onSuccess","onWarning","onError","onInfo"],h=S({get:()=>l.value.template?.request??"",set:a=>{l.value={...l.value,template:{...l.value.template,request:a}}}}),w=S({get:()=>l.value.template?.download??"",set:a=>{const e=l.value.template??{request:""};if(a.length===0){const{download:o,...v}=e;l.value={...l.value,template:v}}else l.value={...l.value,template:{...e,download:a}}}});function q(a){const e={...l.value};a.length===0?delete e.messageExpression:e.messageExpression=a,l.value=e}function T(a){const e={...l.value};a.length===0?delete e.resultExpression:e.resultExpression=a,l.value=e}function k(a,e){const o={...l.value};e.length===0?Reflect.deleteProperty(o,a):o[a]=e,l.value=o}return(a,e)=>(V(),z("div",L,[x("div",N,[t(s(p),{orientation:"vertical"},{default:n(()=>[t(s(i),{class:"text-xs text-zinc-500"},{tooltip:n(()=>[t(s(c),{source:r("template")??"返回 `HttpRequest` 的 CEL 表达式;未配置「下载请求」时直接下载其响应",block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),default:n(()=>[e[2]||(e[2]=u(" 请求 ",-1))]),_:1}),t(s(d),{"model-value":h.value,multiline:"",placeholder:"例:http.get('https://api.example.com/files/123')","result-type":"HttpRequest",class:"min-h-16","onUpdate:modelValue":e[0]||(e[0]=o=>h.value=o)},null,8,["model-value"])]),_:1}),t(s(p),{orientation:"vertical"},{default:n(()=>[t(s(i),{class:"text-xs text-zinc-500"},{tooltip:n(()=>[t(s(c),{source:"可选的第二步:用第一步响应 `json` 中的凭据构造真正的下载请求,例如 `json.data.key`",block:"",class:"prose prose-sm prose-zinc"})]),default:n(()=>[e[3]||(e[3]=u(" 下载请求 ",-1))]),_:1}),t(s(d),{"model-value":w.value,multiline:"",placeholder:"例:http.get('https://api.example.com/download').query('key', json.data.key)","result-type":"HttpRequest","extra-vars":{json:s(g)},class:"min-h-16","onUpdate:modelValue":e[1]||(e[1]=o=>w.value=o)},null,8,["model-value","extra-vars"])]),_:1})]),x("div",B,[t(s(p),{orientation:"vertical",class:"flex-1 basis-0 min-w-0"},{default:n(()=>[t(s(i),{class:"text-xs text-zinc-500"},f({default:n(()=>[u(" "+_(m("messageExpression")),1)]),_:2},[r("messageExpression")?{name:"tooltip",fn:n(()=>[t(s(c),{source:r("messageExpression"),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),t(s(d),{"model-value":l.value.messageExpression??"",placeholder:"例:string(json.message)","result-type":"string","extra-vars":{json:s(g)},"onUpdate:modelValue":q},null,8,["model-value","extra-vars"])]),_:1}),t(s(p),{orientation:"vertical",class:"flex-1 basis-0 min-w-0"},{default:n(()=>[t(s(i),{class:"text-xs text-zinc-500"},f({default:n(()=>[u(" "+_(m("resultExpression")),1)]),_:2},[r("resultExpression")?{name:"tooltip",fn:n(()=>[t(s(c),{source:r("resultExpression"),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),t(s(d),{"model-value":l.value.resultExpression??"",placeholder:"例:json.code == 0 ? 'success' : 'error'","result-type":"string","extra-vars":{json:s(g)},"onUpdate:modelValue":T},null,8,["model-value","extra-vars"])]),_:1})]),x("div",M,[(V(),z(D,null,F(j,o=>t(s(p),{key:o,orientation:"vertical",class:"min-w-0"},{default:n(()=>[t(s(i),{class:"text-xs text-zinc-500"},f({default:n(()=>[u(" "+_(m(o)),1)]),_:2},[r(o)?{name:"tooltip",fn:n(()=>[t(s(c),{source:r(o),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),t($,{triggers:l.value[o]??[],"onUpdate:triggers":v=>k(o,v)},null,8,["triggers","onUpdate:triggers"])]),_:2},1024)),64))])]))}});export{I as default};
@@ -0,0 +1 @@
1
+ import{d as V,b as w,z as F,c as v,g as e,w as n,u as s,f as _,F as U,v as j,o as E,h as c,i as u,j as d,t as p,k as m,l as f,_ as x,T as k,x as N,y as R}from"./index-OUd02U3g.js";const D={class:"flex flex-col gap-3"},H={class:"flex items-start gap-2"},$={class:"grid grid-cols-2 gap-3"},B=V({name:"ShwfedHttpRequestActionConfig",__name:"config",props:{modelValue:{required:!0},modelModifiers:{}},emits:["update:modelValue"],setup(h){const o=w(h,"modelValue"),g=F(()=>{}),i=t=>N(g,t)??t,r=t=>R(g,t),S=["onSuccess","onWarning","onError","onInfo"];function T(t){const l={...o.value};t.length===0?delete l.messageExpression:l.messageExpression=t,o.value=l}function b(t){const l={...o.value};t.length===0?delete l.resultExpression:l.resultExpression=t,o.value=l}function y(t,l){const a={...o.value};l.length===0?Reflect.deleteProperty(a,t):a[t]=l,o.value=a}return(t,l)=>(E(),v("div",D,[e(s(x),{orientation:"vertical"},{default:n(()=>[e(s(c),{class:"text-xs text-zinc-500"},u({default:n(()=>[d(" "+p(i("expression")),1)]),_:2},[r("expression")?{name:"tooltip",fn:n(()=>[e(s(m),{source:r("expression"),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),e(s(f),{"model-value":o.value.expression,multiline:"",placeholder:"例:http.post('/api/foo').body({ id: row.id })","result-type":"HttpRequest",class:"min-h-16","onUpdate:modelValue":l[0]||(l[0]=a=>o.value={...o.value,expression:a})},null,8,["model-value"])]),_:1}),_("div",H,[e(s(x),{orientation:"vertical",class:"flex-1 basis-0 min-w-0"},{default:n(()=>[e(s(c),{class:"text-xs text-zinc-500"},u({default:n(()=>[d(" "+p(i("messageExpression")),1)]),_:2},[r("messageExpression")?{name:"tooltip",fn:n(()=>[e(s(m),{source:r("messageExpression"),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),e(s(f),{"model-value":o.value.messageExpression??"",placeholder:"例:string(json.message)","result-type":"string","extra-vars":{json:{type:"dyn",label:"HTTP 响应体(已解析 JSON)"}},"onUpdate:modelValue":T},null,8,["model-value"])]),_:1}),e(s(x),{orientation:"vertical",class:"flex-1 basis-0 min-w-0"},{default:n(()=>[e(s(c),{class:"text-xs text-zinc-500"},u({default:n(()=>[d(" "+p(i("resultExpression")),1)]),_:2},[r("resultExpression")?{name:"tooltip",fn:n(()=>[e(s(m),{source:r("resultExpression"),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),e(s(f),{"model-value":o.value.resultExpression??"",placeholder:"例:json.code == 0 ? 'success' : 'error'","result-type":"string","extra-vars":{json:{type:"dyn",label:"HTTP 响应体(已解析 JSON)"}},"onUpdate:modelValue":b},null,8,["model-value"])]),_:1})]),_("div",$,[(E(),v(U,null,j(S,a=>e(s(x),{key:a,orientation:"vertical",class:"min-w-0"},{default:n(()=>[e(s(c),{class:"text-xs text-zinc-500"},u({default:n(()=>[d(" "+p(i(a)),1)]),_:2},[r(a)?{name:"tooltip",fn:n(()=>[e(s(m),{source:r(a),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),e(k,{triggers:o.value[a]??[],"onUpdate:triggers":z=>y(a,z)},null,8,["triggers","onUpdate:triggers"])]),_:2},1024)),64))])]))}});export{B as default};
@@ -0,0 +1 @@
1
+ import{d as _,b as y,B as x,A as b,c,f as i,g as n,w as o,u as a,o as l,C as v,I as u,D as g,E as D,F as h,v as w,e as B,G as C,t as p,H as S}from"./index-OUd02U3g.js";const $={class:"flex flex-col gap-3"},k={class:"flex-1"},E={class:"ml-auto text-xs tabular-nums text-zinc-400"},I=_({name:"ShwfedPrototypeActionConfig",__name:"config",props:{modelValue:{required:!0},modelModifiers:{}},emits:["update:modelValue"],setup(m){const r=y(m,"modelValue"),f=(()=>{const s=new Map;for(const e of x){if(e.type===b||e.deprecated)continue;const t=s.get(e.type);(!t||e.compatibilityDate>t.compatibilityDate)&&s.set(e.type,{type:e.type,compatibilityDate:e.compatibilityDate,name:e.metadata.name,icon:e.metadata.icon})}return[...s.values()].sort((e,t)=>e.name.localeCompare(t.name,"zh"))})();function d(s,e){r.value={type:s,compatibilityDate:e}}return(s,e)=>(l(),c("div",$,[e[0]||(e[0]=i("p",{class:"text-xs text-zinc-500"}," 原型按钮点击后无任何副作用,用于占位。选择下方任意类型可将其转换为正式按钮 —— 此转换仅能进行一次。 ",-1)),n(a(S),null,{default:o(()=>[n(a(v),{align:"inline-start"},{default:o(()=>[n(a(u),{icon:"fluent:arrow-swap-20-regular"})]),_:1}),n(a(g),null,{default:o(()=>[n(a(D),null,{default:o(()=>[(l(!0),c(h,null,w(a(f),t=>(l(),B(a(C),{key:t.type,value:t.type,onSelect:V=>d(t.type,t.compatibilityDate)},{default:o(()=>[n(a(u),{icon:t.icon,class:"size-4 shrink-0"},null,8,["icon"]),i("span",k,p(t.name),1),i("span",E,p(t.compatibilityDate),1)]),_:2},1032,["value","onSelect"]))),128))]),_:1})]),_:1})]),_:1})]))}});export{I as default};
@@ -0,0 +1 @@
1
+ import{d as x,b as k,aG as g,c as S,g as e,w as o,u as t,o as V,h as r,i as m,j as d,t as f,k as p,a5 as w,_,m as $,x as h,y}from"./index-OUd02U3g.js";const z={class:"flex flex-col gap-4"},C=x({name:"ShwfedMarkdownItemConfig",__name:"config",props:{modelValue:{required:!0},modelModifiers:{}},emits:["update:modelValue"],setup(v){const a=k(v,"modelValue"),c=g(),u=l=>h(c,l)??l,s=l=>y(c,l);return(l,n)=>(V(),S("div",z,[e(t(_),{orientation:"vertical"},{default:o(()=>[e(t(r),{class:"text-xs text-zinc-500"},m({default:o(()=>[d(" "+f(u("name")),1)]),_:2},[s("name")?{name:"tooltip",fn:o(()=>[e(t(p),{source:s("name"),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),e(t(w),{"model-value":a.value.name,placeholder:"仅用于编辑器内识别","onUpdate:modelValue":n[0]||(n[0]=i=>a.value={...a.value,name:String(i)})},null,8,["model-value"])]),_:1}),e(t(_),{orientation:"vertical"},{default:o(()=>[e(t(r),{class:"text-xs text-zinc-500"},m({default:o(()=>[d(" "+f(u("content")),1)]),_:2},[s("content")?{name:"tooltip",fn:o(()=>[e(t(p),{source:s("content"),block:"",class:"prose prose-sm prose-zinc"},null,8,["source"])]),key:"0"}:void 0]),1024),e(t($),{markdown:"",multiline:"","model-value":a.value.content,"onUpdate:modelValue":n[1]||(n[1]=i=>a.value={...a.value,content:i})},null,8,["model-value"])]),_:1})]))}});export{C as default};