hostctl 0.1.55 → 0.1.58

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.
@@ -2929,9 +2929,13 @@ function task(runFn4, options) {
2929
2929
  options?.outputSchema
2930
2930
  );
2931
2931
  const taskFnObject = function(params) {
2932
- return function(parentInvocation) {
2933
- return parentInvocation.invokeChildTask(taskFnObject, params ?? {});
2932
+ const normalizedParams = params ?? {};
2933
+ const taskPartialFn = function(parentInvocation) {
2934
+ return parentInvocation.invokeChildTask(taskFnObject, normalizedParams);
2934
2935
  };
2936
+ taskPartialFn.taskFn = taskFnObject;
2937
+ taskPartialFn.params = normalizedParams;
2938
+ return taskPartialFn;
2935
2939
  };
2936
2940
  Object.assign(taskFnObject, { task: taskInstance });
2937
2941
  return taskFnObject;
@@ -3897,6 +3901,9 @@ import AdmZip from "adm-zip";
3897
3901
 
3898
3902
  // src/hash.ts
3899
3903
  import { createHash } from "crypto";
3904
+ function sha256(str) {
3905
+ return createHash("sha256").update(str).digest("hex");
3906
+ }
3900
3907
 
3901
3908
  // src/param-map.ts
3902
3909
  import { match as match2 } from "ts-pattern";
@@ -3981,7 +3988,7 @@ var ParamMap = class _ParamMap {
3981
3988
  import * as z from "zod";
3982
3989
 
3983
3990
  // src/version.ts
3984
- var version = "0.1.55";
3991
+ var version = "0.1.58";
3985
3992
 
3986
3993
  // src/app.ts
3987
3994
  import { retryUntilDefined } from "ts-retry";
@@ -6731,58 +6738,64 @@ async function detectRockyLinux(exec) {
6731
6738
  }
6732
6739
  var os_default = task(
6733
6740
  async function run27(context) {
6734
- try {
6735
- const { exec } = context;
6736
- const {
6737
- success: ostypeSuccess,
6738
- stdout: ostypeOutput,
6739
- stderr: ostypeStderr
6740
- } = await exec(["bash", "-c", "echo $OSTYPE"]);
6741
- if (!ostypeSuccess) {
6742
- throw new Error(`Failed to get OSTYPE: ${ostypeStderr}`);
6743
- }
6744
- const family = await match5(ostypeOutput.trim().toLowerCase()).with(P3.string.startsWith("solaris"), () => "solaris").with(P3.string.startsWith("darwin"), () => "darwin").with(P3.string.startsWith("linux"), () => "linux").with(P3.string.startsWith("bsd"), () => "bsd").with(P3.string.startsWith("freebsd"), () => "bsd").with(P3.string.startsWith("msys"), () => "windows").with(P3.string.startsWith("cygwin"), () => "windows").with(P3.string.startsWith("mingw"), () => "windows").otherwise(async () => {
6745
- const { stdout: unameOutput } = await exec(["uname"]);
6746
- const unameFamily = match5(unameOutput.trim().toLowerCase()).with(P3.string.startsWith("sunos"), () => "solaris").with(P3.string.startsWith("darwin"), () => "darwin").with(P3.string.startsWith("linux"), () => "linux").with(P3.string.startsWith("freebsd"), () => "bsd").with(P3.string.startsWith("openbsd"), () => "bsd").with(P3.string.startsWith("netbsd"), () => "bsd").otherwise(() => "unknown");
6747
- return unameFamily;
6748
- });
6749
- const [osIdLike, osId, osVersion] = await match5(family).with("bsd", async () => {
6750
- const { stdout: unameROutput } = await exec(["uname", "-r"]);
6751
- return [family, family, unameROutput.trim()];
6752
- }).with("darwin", async () => {
6753
- const { stdout: swVersOutput } = await exec(["sw_vers", "-productVersion"]);
6754
- return [family, family, swVersOutput.trim()];
6755
- }).with("linux", async () => {
6756
- const { idLike, id, version: version2 } = await getOsReleaseInfo(exec);
6757
- return [idLike, id, version2];
6758
- }).with("solaris", async () => {
6759
- const { stdout: unameROutput } = await exec(["uname", "-r"]);
6760
- return ["solaris", "solaris", unameROutput.trim()];
6761
- }).with("windows", () => ["windows", "windows", "unknown"]).otherwise(() => ["unknown", "unknown", "unknown"]);
6762
- let variant = osId.toLowerCase();
6763
- if (family === "linux" && !variant.includes("rocky") && (osIdLike.toLowerCase().includes("rocky") || osId.toLowerCase().includes("rhel"))) {
6764
- const isRocky = await detectRockyLinux(exec);
6765
- if (isRocky) {
6766
- variant = "rocky";
6741
+ return await context.memoize(
6742
+ "core.host.os:v1",
6743
+ async () => {
6744
+ try {
6745
+ const { exec } = context;
6746
+ const {
6747
+ success: ostypeSuccess,
6748
+ stdout: ostypeOutput,
6749
+ stderr: ostypeStderr
6750
+ } = await exec(["bash", "-c", "echo $OSTYPE"]);
6751
+ if (!ostypeSuccess) {
6752
+ throw new Error(`Failed to get OSTYPE: ${ostypeStderr}`);
6753
+ }
6754
+ const family = await match5(ostypeOutput.trim().toLowerCase()).with(P3.string.startsWith("solaris"), () => "solaris").with(P3.string.startsWith("darwin"), () => "darwin").with(P3.string.startsWith("linux"), () => "linux").with(P3.string.startsWith("bsd"), () => "bsd").with(P3.string.startsWith("freebsd"), () => "bsd").with(P3.string.startsWith("msys"), () => "windows").with(P3.string.startsWith("cygwin"), () => "windows").with(P3.string.startsWith("mingw"), () => "windows").otherwise(async () => {
6755
+ const { stdout: unameOutput } = await exec(["uname"]);
6756
+ const unameFamily = match5(unameOutput.trim().toLowerCase()).with(P3.string.startsWith("sunos"), () => "solaris").with(P3.string.startsWith("darwin"), () => "darwin").with(P3.string.startsWith("linux"), () => "linux").with(P3.string.startsWith("freebsd"), () => "bsd").with(P3.string.startsWith("openbsd"), () => "bsd").with(P3.string.startsWith("netbsd"), () => "bsd").otherwise(() => "unknown");
6757
+ return unameFamily;
6758
+ });
6759
+ const [osIdLike, osId, osVersion] = await match5(family).with("bsd", async () => {
6760
+ const { stdout: unameROutput } = await exec(["uname", "-r"]);
6761
+ return [family, family, unameROutput.trim()];
6762
+ }).with("darwin", async () => {
6763
+ const { stdout: swVersOutput } = await exec(["sw_vers", "-productVersion"]);
6764
+ return [family, family, swVersOutput.trim()];
6765
+ }).with("linux", async () => {
6766
+ const { idLike, id, version: version2 } = await getOsReleaseInfo(exec);
6767
+ return [idLike, id, version2];
6768
+ }).with("solaris", async () => {
6769
+ const { stdout: unameROutput } = await exec(["uname", "-r"]);
6770
+ return ["solaris", "solaris", unameROutput.trim()];
6771
+ }).with("windows", () => ["windows", "windows", "unknown"]).otherwise(() => ["unknown", "unknown", "unknown"]);
6772
+ let variant = osId.toLowerCase();
6773
+ if (family === "linux" && !variant.includes("rocky") && (osIdLike.toLowerCase().includes("rocky") || osId.toLowerCase().includes("rhel"))) {
6774
+ const isRocky = await detectRockyLinux(exec);
6775
+ if (isRocky) {
6776
+ variant = "rocky";
6777
+ }
6778
+ }
6779
+ return {
6780
+ success: true,
6781
+ family,
6782
+ os: osIdLike.toLowerCase(),
6783
+ variant,
6784
+ version: osVersion
6785
+ };
6786
+ } catch (error) {
6787
+ return {
6788
+ success: false,
6789
+ error: error.message,
6790
+ family: "unknown",
6791
+ os: "unknown",
6792
+ variant: "unknown",
6793
+ version: "unknown"
6794
+ };
6767
6795
  }
6768
- }
6769
- return {
6770
- success: true,
6771
- family,
6772
- os: osIdLike.toLowerCase(),
6773
- variant,
6774
- version: osVersion
6775
- };
6776
- } catch (error) {
6777
- return {
6778
- success: false,
6779
- error: error.message,
6780
- family: "unknown",
6781
- os: "unknown",
6782
- variant: "unknown",
6783
- version: "unknown"
6784
- };
6785
- }
6796
+ },
6797
+ { scope: "host" }
6798
+ );
6786
6799
  },
6787
6800
  {
6788
6801
  name: "os",
@@ -31791,6 +31804,154 @@ function registrySize(registry2) {
31791
31804
  return registry2.tasks().length;
31792
31805
  }
31793
31806
 
31807
+ // src/task-cache.ts
31808
+ var DEFAULT_CACHE_MODE = "use";
31809
+ var LOCAL_HOST_SCOPE_KEY = "host:local";
31810
+ function normalizeValue(value) {
31811
+ if (value === void 0) {
31812
+ return void 0;
31813
+ }
31814
+ if (value === null) {
31815
+ return null;
31816
+ }
31817
+ if (typeof value === "bigint") {
31818
+ return value.toString();
31819
+ }
31820
+ if (value instanceof Date) {
31821
+ return value.toISOString();
31822
+ }
31823
+ if (Array.isArray(value)) {
31824
+ return value.map((item) => {
31825
+ const normalized = normalizeValue(item);
31826
+ return normalized === void 0 ? null : normalized;
31827
+ });
31828
+ }
31829
+ if (typeof value === "object") {
31830
+ const obj = value;
31831
+ const sortedKeys = Object.keys(obj).sort();
31832
+ const result = {};
31833
+ for (const key of sortedKeys) {
31834
+ const normalized = normalizeValue(obj[key]);
31835
+ if (normalized !== void 0) {
31836
+ result[key] = normalized;
31837
+ }
31838
+ }
31839
+ return result;
31840
+ }
31841
+ return value;
31842
+ }
31843
+ function stableJson(value) {
31844
+ const normalized = normalizeValue(value);
31845
+ return JSON.stringify(normalized ?? null);
31846
+ }
31847
+ function normalizeCacheConfig(cache) {
31848
+ if (!cache) {
31849
+ return { enabled: false, mode: DEFAULT_CACHE_MODE };
31850
+ }
31851
+ if (cache === true) {
31852
+ return { enabled: true, mode: DEFAULT_CACHE_MODE };
31853
+ }
31854
+ if (typeof cache === "string") {
31855
+ return { enabled: true, scope: cache, mode: DEFAULT_CACHE_MODE };
31856
+ }
31857
+ return {
31858
+ enabled: true,
31859
+ scope: cache.scope,
31860
+ key: cache.key,
31861
+ ttlMs: cache.ttlMs,
31862
+ mode: cache.mode ?? DEFAULT_CACHE_MODE
31863
+ };
31864
+ }
31865
+ function buildTaskCacheKey(taskIdentity, params) {
31866
+ const taskName = taskIdentity?.task?.name;
31867
+ const modulePath = taskIdentity?.task?.taskModuleAbsolutePath;
31868
+ const identity2 = taskName ?? (modulePath ? Path.new(modulePath).absolute().toString() : "unknown-task");
31869
+ const paramsHash = sha256(stableJson(params ?? {}));
31870
+ return `${identity2}:${paramsHash}`;
31871
+ }
31872
+ function buildHostScopeKey(host, configRef) {
31873
+ if (!host) {
31874
+ return LOCAL_HOST_SCOPE_KEY;
31875
+ }
31876
+ const identity2 = {
31877
+ alias: host.alias ?? "",
31878
+ hostname: host.hostname ?? "",
31879
+ user: host.user ?? "",
31880
+ port: host.port ?? 22,
31881
+ config: configRef ?? ""
31882
+ };
31883
+ return `host:${sha256(stableJson(identity2))}`;
31884
+ }
31885
+ function buildInvocationScopeKey(rootInvocationId) {
31886
+ return `invocation:${rootInvocationId}`;
31887
+ }
31888
+ var TaskCacheStore = class {
31889
+ entries = /* @__PURE__ */ new Map();
31890
+ runId;
31891
+ constructor(runId) {
31892
+ this.runId = runId ?? crypto.randomUUID();
31893
+ }
31894
+ globalScopeKey() {
31895
+ return `global:${this.runId}`;
31896
+ }
31897
+ get(scopeKey, cacheKey2) {
31898
+ const scope = this.entries.get(scopeKey);
31899
+ if (!scope) {
31900
+ return void 0;
31901
+ }
31902
+ const entry = scope.get(cacheKey2);
31903
+ if (!entry) {
31904
+ return void 0;
31905
+ }
31906
+ if (entry.expiresAt > 0 && Date.now() > entry.expiresAt) {
31907
+ scope.delete(cacheKey2);
31908
+ if (scope.size === 0) {
31909
+ this.entries.delete(scopeKey);
31910
+ }
31911
+ return void 0;
31912
+ }
31913
+ return entry;
31914
+ }
31915
+ set(scopeKey, cacheKey2, value, ttlMs) {
31916
+ const scope = this.entries.get(scopeKey) ?? /* @__PURE__ */ new Map();
31917
+ const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : 0;
31918
+ const entry = { value, expiresAt };
31919
+ scope.set(cacheKey2, entry);
31920
+ this.entries.set(scopeKey, scope);
31921
+ return entry;
31922
+ }
31923
+ delete(scopeKey, cacheKey2) {
31924
+ const scope = this.entries.get(scopeKey);
31925
+ if (!scope) {
31926
+ return;
31927
+ }
31928
+ scope.delete(cacheKey2);
31929
+ if (scope.size === 0) {
31930
+ this.entries.delete(scopeKey);
31931
+ }
31932
+ }
31933
+ async resolve(scopeKey, cacheKey2, compute, options = {}) {
31934
+ const mode = options.mode ?? DEFAULT_CACHE_MODE;
31935
+ if (mode === "bypass") {
31936
+ return await compute();
31937
+ }
31938
+ if (mode === "use") {
31939
+ const cached = this.get(scopeKey, cacheKey2);
31940
+ if (cached) {
31941
+ return await cached.value;
31942
+ }
31943
+ }
31944
+ const promise = Promise.resolve().then(compute);
31945
+ this.set(scopeKey, cacheKey2, promise, options.ttlMs);
31946
+ try {
31947
+ return await promise;
31948
+ } catch (error) {
31949
+ this.delete(scopeKey, cacheKey2);
31950
+ throw error;
31951
+ }
31952
+ }
31953
+ };
31954
+
31794
31955
  // src/app.ts
31795
31956
  var TaskTree = class {
31796
31957
  // private taskEventBus: Emittery<{ newTask: NewTaskEvent; taskComplete: TaskCompleteEvent }>;
@@ -31887,6 +32048,7 @@ var App3 = class _App {
31887
32048
  outputStyle;
31888
32049
  _tmpDir;
31889
32050
  tmpFileRegistry;
32051
+ taskCache;
31890
32052
  taskTree;
31891
32053
  verbosity = Verbosity.ERROR;
31892
32054
  passwordProvider;
@@ -31896,6 +32058,7 @@ var App3 = class _App {
31896
32058
  this.taskTree = new TaskTree();
31897
32059
  this.outputStyle = "plain";
31898
32060
  this.tmpFileRegistry = new TmpFileRegistry(this.hostctlTmpDir());
32061
+ this.taskCache = new TaskCacheStore();
31899
32062
  this.configRef = void 0;
31900
32063
  this.hostSelector = void 0;
31901
32064
  process3.on("exit", (code) => this.appExitCallback());
@@ -32211,6 +32374,27 @@ ${cmdRes.stderr.trim()}`));
32211
32374
  }
32212
32375
  taskContextForRunFn(invocation, params, hostForContext) {
32213
32376
  const effectiveHost = hostForContext || invocation.host;
32377
+ const rootInvocationId = (() => {
32378
+ let current = invocation;
32379
+ while (current.parent) {
32380
+ current = current.parent;
32381
+ }
32382
+ return current.id;
32383
+ })();
32384
+ const defaultScope = () => effectiveHost ? "host" : "global";
32385
+ const scopeKeyFor = (scope) => {
32386
+ switch (scope) {
32387
+ case "global":
32388
+ return this.taskCache.globalScopeKey();
32389
+ case "host":
32390
+ return buildHostScopeKey(effectiveHost, this.configRef);
32391
+ case "invocation":
32392
+ return buildInvocationScopeKey(rootInvocationId);
32393
+ }
32394
+ };
32395
+ const isTaskFn = (candidate) => {
32396
+ return typeof candidate === "function" && !!candidate.task;
32397
+ };
32214
32398
  return {
32215
32399
  // Properties from TaskContext
32216
32400
  params,
@@ -32240,8 +32424,58 @@ ${cmdRes.stderr.trim()}`));
32240
32424
  ssh: async (tags, remoteTaskFn) => {
32241
32425
  return await invocation.ssh(tags, remoteTaskFn);
32242
32426
  },
32243
- run: async (taskPartialFn) => {
32244
- return await invocation.run(taskPartialFn);
32427
+ run: async (...args) => {
32428
+ const [firstArg, secondArg, thirdArg] = args;
32429
+ let taskPartialFn;
32430
+ let taskIdentity;
32431
+ let paramsForKey;
32432
+ let options;
32433
+ if (isTaskFn(firstArg)) {
32434
+ taskIdentity = firstArg;
32435
+ paramsForKey = secondArg ?? {};
32436
+ options = thirdArg;
32437
+ taskPartialFn = taskIdentity(paramsForKey);
32438
+ } else {
32439
+ taskPartialFn = firstArg;
32440
+ options = secondArg;
32441
+ const meta = taskPartialFn;
32442
+ taskIdentity = meta.taskFn;
32443
+ paramsForKey = meta.params;
32444
+ }
32445
+ const cacheDecision = normalizeCacheConfig(options?.cache);
32446
+ if (!cacheDecision.enabled || cacheDecision.mode === "bypass") {
32447
+ return await invocation.run(taskPartialFn);
32448
+ }
32449
+ const scope = cacheDecision.scope ?? defaultScope();
32450
+ const cacheKey2 = cacheDecision.key ?? buildTaskCacheKey(taskIdentity, paramsForKey ?? {});
32451
+ const scopeKey = scopeKeyFor(scope);
32452
+ return await this.taskCache.resolve(scopeKey, cacheKey2, () => invocation.run(taskPartialFn), {
32453
+ ttlMs: cacheDecision.ttlMs,
32454
+ mode: cacheDecision.mode
32455
+ });
32456
+ },
32457
+ memoize: async (key, valueOrFactory, options) => {
32458
+ const cacheDecision = normalizeCacheConfig({
32459
+ scope: options?.scope,
32460
+ ttlMs: options?.ttlMs,
32461
+ mode: options?.mode,
32462
+ key
32463
+ });
32464
+ const scope = cacheDecision.scope ?? defaultScope();
32465
+ const scopeKey = scopeKeyFor(scope);
32466
+ const compute = async () => {
32467
+ if (typeof valueOrFactory === "function") {
32468
+ return await valueOrFactory();
32469
+ }
32470
+ return await valueOrFactory;
32471
+ };
32472
+ if (cacheDecision.mode === "bypass") {
32473
+ return await compute();
32474
+ }
32475
+ return await this.taskCache.resolve(scopeKey, key, compute, {
32476
+ ttlMs: cacheDecision.ttlMs,
32477
+ mode: cacheDecision.mode
32478
+ });
32245
32479
  },
32246
32480
  getPassword: async () => {
32247
32481
  return await invocation.getPassword();
@@ -33906,8 +34140,8 @@ function renderTaskRows(rows, {
33906
34140
  name: r.name,
33907
34141
  description: r.description,
33908
34142
  module: r.module,
33909
- inputSchema: Boolean(r.inputSchema),
33910
- outputSchema: Boolean(r.outputSchema),
34143
+ inputSchema: schemaToDescriptor(r.inputSchema),
34144
+ outputSchema: schemaToDescriptor(r.outputSchema),
33911
34145
  schemaSource: r.schemaSource
33912
34146
  }));
33913
34147
  console.log(JSON.stringify(sanitized, null, 2));
@@ -33948,9 +34182,12 @@ function summarizeType(val) {
33948
34182
  case "optional":
33949
34183
  case "ZodNullable":
33950
34184
  case "nullable":
34185
+ case "ZodDefault":
34186
+ case "default":
33951
34187
  return summarizeType(def.innerType || def.type);
33952
34188
  case "ZodEffects":
33953
- return summarizeType(def.schema);
34189
+ case "pipe":
34190
+ return summarizeType(def.schema || def.in);
33954
34191
  case "ZodString":
33955
34192
  case "string":
33956
34193
  return { type: "string", desc };
@@ -33973,8 +34210,11 @@ function summarizeType(val) {
33973
34210
  return types.length ? { type: types.join(" | "), desc } : {};
33974
34211
  }
33975
34212
  case "ZodLiteral":
33976
- case "literal":
33977
- return { type: JSON.stringify(def.value), desc };
34213
+ case "literal": {
34214
+ const values3 = Array.isArray(def?.values) ? def.values : def?.value !== void 0 ? [def.value] : [];
34215
+ const value = values3.length ? values3[0] : void 0;
34216
+ return { type: value !== void 0 ? JSON.stringify(value) : void 0, desc };
34217
+ }
33978
34218
  case "ZodObject":
33979
34219
  case "object":
33980
34220
  return { type: "object", desc };
@@ -33982,6 +34222,140 @@ function summarizeType(val) {
33982
34222
  return { type: String(tn).replace(/^Zod/, "").toLowerCase(), desc };
33983
34223
  }
33984
34224
  }
34225
+ function schemaToDescriptor(schema) {
34226
+ if (!schema) return null;
34227
+ const { schema: base, nullable } = unwrapSchema(schema);
34228
+ const def = base?._def ?? base?.def;
34229
+ const typeName = def?.typeName || def?.type;
34230
+ const description = def?.description || base?.description;
34231
+ let descriptor = {};
34232
+ switch (typeName) {
34233
+ case "ZodString":
34234
+ case "string":
34235
+ descriptor.type = "string";
34236
+ break;
34237
+ case "ZodNumber":
34238
+ case "number":
34239
+ descriptor.type = "number";
34240
+ break;
34241
+ case "ZodBoolean":
34242
+ case "boolean":
34243
+ descriptor.type = "boolean";
34244
+ break;
34245
+ case "ZodArray":
34246
+ case "array": {
34247
+ const inner = def?.element ?? def?.type;
34248
+ descriptor.type = "array";
34249
+ descriptor.items = schemaToDescriptor(inner);
34250
+ break;
34251
+ }
34252
+ case "ZodObject":
34253
+ case "object": {
34254
+ const shapeSource = def?.shape || def?.shape_;
34255
+ const shape = shapeSource && typeof shapeSource === "function" ? shapeSource.call(def) : shapeSource;
34256
+ const properties = {};
34257
+ const required = [];
34258
+ if (shape && typeof shape === "object") {
34259
+ for (const [key, value] of Object.entries(shape)) {
34260
+ const { schema: fieldSchema, optional, nullable: fieldNullable } = unwrapSchema(value);
34261
+ const fieldDescriptor = schemaToDescriptor(fieldSchema) ?? {};
34262
+ if (fieldNullable) {
34263
+ fieldDescriptor.nullable = true;
34264
+ }
34265
+ properties[key] = fieldDescriptor;
34266
+ if (!optional) {
34267
+ required.push(key);
34268
+ }
34269
+ }
34270
+ }
34271
+ descriptor.type = "object";
34272
+ descriptor.properties = properties;
34273
+ descriptor.required = required.length ? required : void 0;
34274
+ break;
34275
+ }
34276
+ case "ZodUnion":
34277
+ case "union": {
34278
+ const options = def?.options ?? [];
34279
+ descriptor.anyOf = options.map((option) => schemaToDescriptor(option)).filter(Boolean);
34280
+ break;
34281
+ }
34282
+ case "ZodLiteral":
34283
+ case "literal": {
34284
+ const values3 = Array.isArray(def?.values) ? def.values : def?.value !== void 0 ? [def.value] : [];
34285
+ const sample = values3.length ? values3[0] : void 0;
34286
+ descriptor.type = sample === null ? "null" : typeof sample;
34287
+ descriptor.enum = values3;
34288
+ break;
34289
+ }
34290
+ case "ZodEnum":
34291
+ case "enum":
34292
+ const enumValues = Array.isArray(def?.values) ? def.values : Object.values(def?.entries ?? {});
34293
+ descriptor.enum = enumValues;
34294
+ if (enumValues.length) {
34295
+ const hasNumber = enumValues.some((value) => typeof value === "number");
34296
+ const hasString = enumValues.some((value) => typeof value === "string");
34297
+ descriptor.type = hasNumber && !hasString ? "number" : "string";
34298
+ } else {
34299
+ descriptor.type = "string";
34300
+ }
34301
+ break;
34302
+ case "ZodNativeEnum":
34303
+ case "nativeenum": {
34304
+ const values3 = Array.isArray(def?.values) ? def.values : Object.values(def?.entries ?? def?.values ?? {});
34305
+ descriptor.type = values3.every((value) => typeof value === "number") ? "number" : "string";
34306
+ descriptor.enum = values3;
34307
+ break;
34308
+ }
34309
+ default:
34310
+ descriptor.type = typeName ? String(typeName).replace(/^Zod/, "").toLowerCase() : "unknown";
34311
+ break;
34312
+ }
34313
+ if (description) {
34314
+ descriptor.description = description;
34315
+ }
34316
+ if (nullable) {
34317
+ descriptor.nullable = true;
34318
+ }
34319
+ return descriptor;
34320
+ }
34321
+ function unwrapSchema(schema) {
34322
+ let current = schema;
34323
+ let optional = false;
34324
+ let nullable = false;
34325
+ while (current) {
34326
+ const def = current?._def ?? current?.def;
34327
+ const typeName = def?.typeName || def?.type;
34328
+ if (typeName === "ZodOptional" || typeName === "optional") {
34329
+ optional = true;
34330
+ current = def?.innerType ?? def?.type;
34331
+ continue;
34332
+ }
34333
+ if (typeName === "ZodNullable" || typeName === "nullable") {
34334
+ nullable = true;
34335
+ current = def?.innerType ?? def?.type;
34336
+ continue;
34337
+ }
34338
+ if (typeName === "ZodDefault" || typeName === "default") {
34339
+ optional = true;
34340
+ current = def?.innerType ?? def?.type;
34341
+ continue;
34342
+ }
34343
+ if (typeName === "ZodEffects" || typeName === "effects") {
34344
+ current = def?.schema;
34345
+ continue;
34346
+ }
34347
+ if (typeName === "pipe") {
34348
+ current = def?.in ?? def?.schema;
34349
+ continue;
34350
+ }
34351
+ if (typeName === "transform") {
34352
+ current = def?.schema ?? def?.in;
34353
+ continue;
34354
+ }
34355
+ break;
34356
+ }
34357
+ return { schema: current, optional, nullable };
34358
+ }
33985
34359
  function formatSchemaLines(schema) {
33986
34360
  if (!schema) return ["none"];
33987
34361
  const def = schema._def ?? schema.def;
@@ -33994,7 +34368,7 @@ function formatSchemaLines(schema) {
33994
34368
  const tn = anyVal?._def?.typeName || anyVal?.def?.type;
33995
34369
  const typeString = summary2.type || (tn ? String(tn).replace(/^Zod/, "").toLowerCase() : void 0);
33996
34370
  const desc = summary2.desc;
33997
- const optional = tn === "ZodOptional" || tn === "ZodNullable";
34371
+ const optional = ["ZodOptional", "optional", "ZodNullable", "nullable", "ZodDefault", "default"].includes(tn);
33998
34372
  const typePart = typeString ? `: ${typeString}` : "";
33999
34373
  const descPart = desc ? ` - ${desc}` : "";
34000
34374
  return `- ${key}${optional ? "?" : ""}${typePart}${descPart}`;