@bensandee/tooling 0.84.4 → 0.85.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/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { accessSync, appendFileSync, constants, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
2
+ import { accessSync, appendFileSync, closeSync, constants, copyFileSync, existsSync, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
5
5
  import { parseArgs, parseEnv } from "node:util";
@@ -269,7 +269,7 @@ function loadEnvFileForBin(rawArgs, bin, io = processIo) {
269
269
  */
270
270
  const envFileLoad = loadEnvFileForBin(process.argv.slice(2), {
271
271
  name: "bst",
272
- version: "0.84.4"
272
+ version: "0.85.0"
273
273
  });
274
274
  //#endregion
275
275
  //#region ../cli-kit/src/logger.ts
@@ -1112,8 +1112,8 @@ const HealthConfigSchema = z.strictObject({
1112
1112
  directoryFileLimit: z.number().int().nonnegative().optional().meta({ description: "A directory is a crowdedDirectories offender at strictly more than this many measured files sitting directly in it (default 20). The metric counts over-limit directories rather than files, so splitting one crowded directory into two crowded directories makes it worse. 0 makes every directory holding a measured file an offender." }),
1113
1113
  fanInLimit: z.number().int().nonnegative().optional().meta({ description: "A module is a fan-in outlier above this many importers, and only when it also exceeds `moduleSizeLimit` source lines (defaults 10 and 200) -- popularity alone is not coupling. Only value edges are counted: a type-only import is erased before the graph is built and never moves this count, and a third-party import resolves outside the graph entirely. A cross-package import counts unless `crossPackage` is off (default on) -- see that setting. Never duplicate a type or fork a helper to stay under the limit -- prefer a justified `exempt` entry, which names the hub, over raising the limit, which hides it." }),
1114
1114
  fanOutLimit: z.number().int().nonnegative().optional().meta({ description: "A module is a fan-out outlier above this many first-party dependencies (default 10). Counted on the same value-edge basis as fanInLimit, `crossPackage` included, with the same consequence: an import you were about to inline or copy to protect this budget most likely costs nothing." }),
1115
- moduleSizeLimit: z.number().int().nonnegative().optional().meta({ description: "Source lines a module must exceed before its importer count can make it a fan-in outlier (default 200). A module with many importers and little surface is a leaf, not a junction: it has one job and no reason to change for any of its callers, so counting its importers reports popularity rather than coupling. Applies to fan-in only -- a small module with many dependencies is a barrel, which is a finding." }),
1116
- crossPackage: z.boolean().optional().meta({ description: "Count dependency edges that cross a workspace package boundary (default true). On by default because the alternative measures the wrong thing: with it off, moving a module into its own package removes its importers from every metric, so extracting a package hides a hub rather than fixing it. A monorepo adopting the default sees fan-in and fan-out rise once, and clears it with a one-time `health:check --update-baseline --reset`; a single-package repo has no workspace siblings to resolve, so no new edge enters its graph. Set it to false only where the package boundary is a curated API surface an edge across genuinely costs less, or to hold a baseline on the old measurement basis." }),
1115
+ moduleSizeLimit: z.number().int().nonnegative().optional().meta({ description: "Raw source lines a module must exceed before its importer count can make it a fan-in outlier (default 200) -- comments and blanks included, since the question is how big the module is to read, not how much code it holds. A module with many importers and little surface is a leaf, not a junction: it has one job and no reason to change for any of its callers, so counting its importers reports popularity rather than coupling. Applies to fan-in only -- a small module with many dependencies is a barrel, which is a finding." }),
1116
+ crossPackage: z.boolean().optional().meta({ description: "Count dependency edges that cross a workspace package boundary (default true). On by default because the alternative measures the wrong thing: with it off, moving a module into its own package removes its importers from every metric, so extracting a package hides a hub rather than fixing it. A monorepo adopting the default sees fan-in and fan-out rise once, and adopts that rise as a ceiling-only pull request -- a branch whose diff changes nothing but health-baseline.json and .tooling.json, which a person merges; a single-package repo has no workspace siblings to resolve, so no new edge enters its graph. Set it to false only where the package boundary is a curated API surface an edge across genuinely costs less, or to hold a baseline on the old measurement basis." }),
1117
1117
  history: z.enum(["orphan-branch", "none"]).optional().meta({ description: "How history is persisted. \"orphan-branch\" (default) has `health:record` append one JSON line per commit to `historyBranch` and has `repo:sync` emit the CI record and status steps; \"none\" turns both off. Does not disable the gate, since the baseline is a committed file. Ignored when `enabled` is false." }),
1118
1118
  historyBranch: z.string().optional().meta({ description: "The orphan branch `health:record` appends to and `health:status` reads (default \"bst/health-metrics\"). Never merges into the default branch and holds no source; separate from `coverage.historyBranch` and must not share a branch with it. `branch:prune`'s built-in never-touch list matches the default name exactly, so a custom name should be named in `branchPrune.exclude` as well." }),
1119
1119
  tolerance: z.number().int().nonnegative().optional().meta({ description: "Slack in offender counts before an increase is a regression: a metric fails when the current count exceeds baseline + tolerance (default 0, so any increase fails). The same allowance applies to every per-module ceiling in health-baseline.json's moduleCeilings, not only the headline metrics. A count, not a percentage -- coverage's percentage-point slack is `coverage.tolerancePp`. Raising it un-tracks every module beneath the new line at once, which is why a justified `exempt` entry is the better answer to one stubborn hub." }),
@@ -4030,6 +4030,10 @@ function tracedRunners(logger) {
4030
4030
  }
4031
4031
  };
4032
4032
  }
4033
+ /** The `code` field a Node filesystem error carries, or undefined for anything else. */
4034
+ function fileErrorCode(error) {
4035
+ if (error instanceof Error && "code" in error && typeof error.code === "string") return error.code;
4036
+ }
4033
4037
  /**
4034
4038
  * Create an executor that runs real commands, fetches, and reads the filesystem. Subprocess and
4035
4039
  * network calls are traced through `logger` (default: the {@link rootLogger}); see
@@ -4093,10 +4097,71 @@ function createRealExecutor$1(logger = rootLogger) {
4093
4097
  return null;
4094
4098
  }
4095
4099
  },
4100
+ readRegularFile(filePath, maxBytes) {
4101
+ let fd;
4102
+ try {
4103
+ fd = openSync(filePath, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
4104
+ } catch (error) {
4105
+ const code = fileErrorCode(error);
4106
+ if (code === "ENOENT") return { kind: "absent" };
4107
+ if (code === "ELOOP") return {
4108
+ kind: "refused",
4109
+ reason: "symlink"
4110
+ };
4111
+ return {
4112
+ kind: "refused",
4113
+ reason: "unreadable"
4114
+ };
4115
+ }
4116
+ try {
4117
+ const stat = fstatSync(fd);
4118
+ if (!stat.isFile()) return {
4119
+ kind: "refused",
4120
+ reason: "not-regular"
4121
+ };
4122
+ if (stat.size > maxBytes) return {
4123
+ kind: "refused",
4124
+ reason: "too-large"
4125
+ };
4126
+ const buffer = Buffer.alloc(maxBytes + 1);
4127
+ let bytesRead;
4128
+ try {
4129
+ bytesRead = readSync(fd, buffer, 0, buffer.length, 0);
4130
+ } catch {
4131
+ return {
4132
+ kind: "refused",
4133
+ reason: "unreadable"
4134
+ };
4135
+ }
4136
+ if (bytesRead > maxBytes) return {
4137
+ kind: "refused",
4138
+ reason: "too-large"
4139
+ };
4140
+ return {
4141
+ kind: "ok",
4142
+ content: buffer.toString("utf8", 0, bytesRead)
4143
+ };
4144
+ } catch {
4145
+ return {
4146
+ kind: "refused",
4147
+ reason: "unreadable"
4148
+ };
4149
+ } finally {
4150
+ closeSync(fd);
4151
+ }
4152
+ },
4096
4153
  writeFile(filePath, content) {
4097
4154
  mkdirSync(path.dirname(filePath), { recursive: true });
4098
4155
  writeFileSync(filePath, content);
4099
4156
  },
4157
+ replaceFile(filePath, content) {
4158
+ mkdirSync(path.dirname(filePath), { recursive: true });
4159
+ rmSync(filePath, {
4160
+ recursive: true,
4161
+ force: true
4162
+ });
4163
+ writeFileSync(filePath, content, { flag: "wx" });
4164
+ },
4100
4165
  writeFileBuffer(filePath, content) {
4101
4166
  mkdirSync(path.dirname(filePath), { recursive: true });
4102
4167
  writeFileSync(filePath, content);
@@ -5176,23 +5241,28 @@ function runDockerBuild(executor, config) {
5176
5241
  return { packages };
5177
5242
  }
5178
5243
  /**
5179
- * Tag a single package's image with all its tags and push them (unless dry-run).
5244
+ * Tag a single package's image with all its tags and publish them (unless dry-run).
5245
+ *
5246
+ * The record -- the exact `vX.Y.Z` ref -- is decided before any floating ref, because the floating
5247
+ * rule depends on the answer: with the record kept, this run's build is not what the version
5248
+ * publishes, so a floating ref that still moves is pointed at the registry's record instead.
5180
5249
  *
5181
5250
  * A prerelease publishes its record tag only: every floating ref is reported as skipped before it
5182
5251
  * is even tagged locally, in a dry run as in a live one, so a beta never displaces the `latest` a
5183
5252
  * deployment pulls.
5184
5253
  */
5185
- function tagAndPushPackage(executor, config, pkg, allTags, skipped) {
5254
+ function tagAndPushPackage(executor, config, pkg, outcome) {
5186
5255
  const version = pkg.version ?? "";
5187
5256
  const prerelease = isPrerelease(version);
5188
5257
  const tags = generateTags(version);
5189
5258
  const exactTag = exactImageTag(version);
5259
+ const exactRef = imageRef(config.registryNamespace, pkg.imageName, exactTag);
5190
5260
  log$1.info(`${pkg.dir} v${pkg.version} -> tags: ${tags.join(", ")}`);
5191
5261
  for (const tag of tags) {
5192
5262
  const ref = imageRef(config.registryNamespace, pkg.imageName, tag);
5193
5263
  if (prerelease && tag !== exactTag) {
5194
5264
  log$1.info(`Leaving ${ref} alone: ${version} is a prerelease`);
5195
- skipped.push(ref);
5265
+ outcome.skipped.push(ref);
5196
5266
  continue;
5197
5267
  }
5198
5268
  log$1.info(`Tagging ${pkg.imageName} -> ${ref}`);
@@ -5204,24 +5274,99 @@ function tagAndPushPackage(executor, config, pkg, allTags, skipped) {
5204
5274
  debugExec(config, `docker tag ${pkg.imageName} ${ref}`, tagResult);
5205
5275
  if (tagResult.exitCode !== 0) throw new FatalError(`docker tag failed: ${tagResult.stderr}`);
5206
5276
  if (config.dryRun) {
5207
- allTags.push(ref);
5277
+ outcome.tags.push(ref);
5208
5278
  log$1.info(`[dry-run] Skipping push for ${ref}`);
5209
- continue;
5210
5279
  }
5211
- if (tag !== exactTag) {
5212
- const current = readPublishedImageVersion(executor, config, ref);
5213
- if (current !== void 0 && compareReleaseVersions(version, current) < 0) {
5280
+ }
5281
+ if (config.dryRun) return;
5282
+ const recordKept = publishRecord(executor, config, exactRef, outcome);
5283
+ if (prerelease) return;
5284
+ for (const tag of tags) {
5285
+ if (tag === exactTag) continue;
5286
+ moveFloatingRef(executor, config, {
5287
+ version,
5288
+ ref: imageRef(config.registryNamespace, pkg.imageName, tag),
5289
+ exactRef,
5290
+ recordKept
5291
+ }, outcome);
5292
+ }
5293
+ }
5294
+ /**
5295
+ * Decide the exact ref: kept when the registry already has it, pushed from this build when it does
5296
+ * not. A kept record is the whole point -- the exact tag names one build forever -- so there is no
5297
+ * flag that pushes over it; new content under an unchanged version is a versioning mistake, and
5298
+ * the fix is to bump the version or the image's `version` override.
5299
+ */
5300
+ function publishRecord(executor, config, exactRef, outcome) {
5301
+ if (inspectImageManifest(executor, config, exactRef) !== void 0) {
5302
+ log$1.info(`Keeping ${exactRef}: the registry already has this record`);
5303
+ outcome.kept.push(exactRef);
5304
+ return true;
5305
+ }
5306
+ pushRef(executor, config, exactRef);
5307
+ outcome.tags.push(exactRef);
5308
+ return false;
5309
+ }
5310
+ /**
5311
+ * Decide one floating ref. With the record kept, a ref that needs to move is retagged from the
5312
+ * record, never pushed: this run's build is not the content the version publishes. "Needs to move"
5313
+ * is an older, unlabelled, unparsable or absent registry version -- an unknown fails open, because
5314
+ * `compareReleaseVersions` answers `0` for "no order established" and its module tells every
5315
+ * fail-open caller to check parsability itself. With the record pushed, the ref moves unless the
5316
+ * registry's version is strictly newer.
5317
+ */
5318
+ function moveFloatingRef(executor, config, refCase, outcome) {
5319
+ const { version, ref, exactRef, recordKept } = refCase;
5320
+ const current = readPublishedImageVersion(executor, config, ref);
5321
+ if (recordKept) {
5322
+ if (current !== void 0 && parseReleaseVersion(current) !== void 0) {
5323
+ const order = compareReleaseVersions(version, current);
5324
+ if (order === 0) {
5325
+ log$1.info(`Leaving ${ref} alone: already at ${version}`);
5326
+ outcome.skipped.push(ref);
5327
+ return;
5328
+ }
5329
+ if (order < 0) {
5214
5330
  log$1.info(`Leaving ${ref} at ${current} (newer than ${version})`);
5215
- skipped.push(ref);
5216
- continue;
5331
+ outcome.skipped.push(ref);
5332
+ return;
5217
5333
  }
5218
5334
  }
5219
- allTags.push(ref);
5220
- log$1.info(`Pushing ${ref}...`);
5221
- const pushResult = executor.exec("docker", ["push", ref]);
5222
- debugExec(config, `docker push ${ref}`, pushResult);
5223
- if (pushResult.exitCode !== 0) throw new FatalError(`docker push failed: ${pushResult.stderr}`);
5335
+ retagFromRecord(executor, config, ref, exactRef);
5336
+ outcome.tags.push(ref);
5337
+ return;
5338
+ }
5339
+ if (current !== void 0 && compareReleaseVersions(version, current) < 0) {
5340
+ log$1.info(`Leaving ${ref} at ${current} (newer than ${version})`);
5341
+ outcome.skipped.push(ref);
5342
+ return;
5224
5343
  }
5344
+ pushRef(executor, config, ref);
5345
+ outcome.tags.push(ref);
5346
+ }
5347
+ /** Push one ref. A failed push is fatal, as it is for every other ref in the run. */
5348
+ function pushRef(executor, config, ref) {
5349
+ log$1.info(`Pushing ${ref}...`);
5350
+ const pushResult = executor.exec("docker", ["push", ref]);
5351
+ debugExec(config, `docker push ${ref}`, pushResult);
5352
+ if (pushResult.exitCode !== 0) throw new FatalError(`docker push failed: ${pushResult.stderr}`);
5353
+ }
5354
+ /**
5355
+ * Point the floating ref at the record with `imagetools create`, which writes the tag server-side
5356
+ * from the manifest the registry already holds: no pull, and nothing from this run's build.
5357
+ */
5358
+ function retagFromRecord(executor, config, ref, exactRef) {
5359
+ log$1.info(`Retagging ${ref} from the record ${exactRef}...`);
5360
+ const result = executor.exec("docker", [
5361
+ "buildx",
5362
+ "imagetools",
5363
+ "create",
5364
+ "--tag",
5365
+ ref,
5366
+ exactRef
5367
+ ]);
5368
+ debugExec(config, `docker buildx imagetools create --tag ${ref} ${exactRef}`, result);
5369
+ if (result.exitCode !== 0) throw new FatalError(`imagetools create failed: ${result.stderr}`);
5225
5370
  }
5226
5371
  /**
5227
5372
  * One image descriptor's config labels, as `docker buildx imagetools inspect --format
@@ -5244,15 +5389,16 @@ function versionFromImageManifest(parsed) {
5244
5389
  }
5245
5390
  }
5246
5391
  /**
5247
- * The version the registry already has behind `ref`, or `undefined` when that cannot be
5248
- * established -- the tag is absent, the manifest carries no version label, or the read failed.
5392
+ * The raw manifest the registry holds behind `ref`, or `undefined` when it has none.
5249
5393
  *
5250
- * Reads the published manifest rather than pulling the image, and never throws: every failure
5251
- * route answers `undefined`, which the caller reads as "nothing newer is known, so move". That is
5252
- * the same fail-open rule the git floating tags use, and for the same reason -- a floating tag is
5253
- * a convenience pointer, and a read that cannot answer must not stall a release.
5394
+ * Exit code 0 is the whole existence test, and the read fails open: `imagetools inspect` reports a
5395
+ * missing tag, an unreachable registry and a missing `buildx` all as free text on stderr, so
5396
+ * parsing the reason would mean pattern-matching a version-dependent string. Everything that is
5397
+ * not a manifest answers "none", which is today's behaviour for a first publish and no worse for
5398
+ * the rest -- a registry that cannot answer an inspect usually fails the following push too, which
5399
+ * aborts the run. The window that leaves is recorded, not closed.
5254
5400
  */
5255
- function readPublishedImageVersion(executor, config, ref) {
5401
+ function inspectImageManifest(executor, config, ref) {
5256
5402
  const result = executor.exec("docker", [
5257
5403
  "buildx",
5258
5404
  "imagetools",
@@ -5263,9 +5409,23 @@ function readPublishedImageVersion(executor, config, ref) {
5263
5409
  ]);
5264
5410
  debugExec(config, `docker buildx imagetools inspect ${ref}`, result);
5265
5411
  if (result.exitCode !== 0) return;
5412
+ return result.stdout;
5413
+ }
5414
+ /**
5415
+ * The version the registry already has behind `ref`, or `undefined` when that cannot be
5416
+ * established -- the tag is absent, the manifest carries no version label, or the read failed.
5417
+ *
5418
+ * Reads the published manifest rather than pulling the image, and never throws: every failure
5419
+ * route answers `undefined`, which the caller reads as "nothing newer is known, so move". That is
5420
+ * the same fail-open rule the git floating tags use, and for the same reason -- a floating tag is
5421
+ * a convenience pointer, and a read that cannot answer must not stall a release.
5422
+ */
5423
+ function readPublishedImageVersion(executor, config, ref) {
5424
+ const stdout = inspectImageManifest(executor, config, ref);
5425
+ if (stdout === void 0) return;
5266
5426
  let raw;
5267
5427
  try {
5268
- raw = JSON.parse(result.stdout);
5428
+ raw = JSON.parse(stdout);
5269
5429
  } catch (error) {
5270
5430
  debug(config, `Could not parse the manifest for ${ref}: ${String(error)}`);
5271
5431
  return;
@@ -5280,15 +5440,21 @@ function readPublishedImageVersion(executor, config, ref) {
5280
5440
  * Run the full Docker publish pipeline:
5281
5441
  * 1. Build all images via runDockerBuild
5282
5442
  * 2. Login to registry
5283
- * 3. Tag each image with semver variants from its resolved version
5284
- * 4. Push all tags
5443
+ * 3. Tag each image locally with the semver variants of its resolved version
5444
+ * 4. Publish each image: its exact record, then its floating refs
5285
5445
  * 5. Logout from registry
5286
5446
  *
5287
- * A floating tag (`latest`, `vX`, `vX.Y`) moves forward only: its published manifest is read
5288
- * for the version label and left alone when that names a newer version than this run's. The
5289
- * accepted residual window is that a registry offers no compare-and-swap for a tag, so two
5290
- * overlapping runs that both read `latest` before either pushes can still finish in the wrong
5291
- * order -- seconds wide, where an unconditional push was open for a whole run.
5447
+ * The exact `vX.Y.Z` tag is the version's record: it is pushed only when the registry does not have
5448
+ * it, and a run that finds it there keeps it and publishes nothing from its own build under that
5449
+ * version. A floating tag (`latest`, `vX`, `vX.Y`) then moves to that record -- retagged
5450
+ * server-side from the manifest the registry holds, never pushed from this run's build -- whenever
5451
+ * the version behind it is older, unlabelled, unparsable, or absent. When the record was pushed,
5452
+ * the floating refs are pushed from the same build and move forward only: its published manifest is
5453
+ * read for the version label and left alone when that names a newer version than this run's.
5454
+ *
5455
+ * The accepted residual window is that a registry offers no compare-and-swap for a tag, so two
5456
+ * overlapping runs that both read a ref before either writes can still finish in the wrong order --
5457
+ * seconds wide, where an unconditional push was open for a whole run.
5292
5458
  */
5293
5459
  function runDockerPublish(executor, config) {
5294
5460
  const { packages } = runDockerBuild(executor, {
@@ -5300,13 +5466,15 @@ function runDockerPublish(executor, config) {
5300
5466
  if (packages.length === 0) return {
5301
5467
  packages: [],
5302
5468
  tags: [],
5303
- skipped: []
5469
+ skipped: [],
5470
+ kept: []
5304
5471
  };
5305
5472
  const publishablePackages = packages.filter((pkg) => pkg.publish);
5306
5473
  if (publishablePackages.length === 0) return {
5307
5474
  packages,
5308
5475
  tags: [],
5309
- skipped: []
5476
+ skipped: [],
5477
+ kept: []
5310
5478
  };
5311
5479
  for (const pkg of publishablePackages) {
5312
5480
  if (!pkg.version) throw new FatalError(`Docker image "${pkg.imageName}" (dockerfile ${pkg.docker.dockerfile}) has no version to publish. Checked: an explicit "version" override in .tooling.json#docker, the nearest enclosing packages/<name>/package.json walking up from the dockerfile, and the root package.json. Set an explicit version override for this image.`);
@@ -5325,22 +5493,25 @@ function runDockerPublish(executor, config) {
5325
5493
  debugExec(config, "docker login", loginResult);
5326
5494
  if (loginResult.exitCode !== 0) throw new FatalError(`Docker login failed: ${loginResult.stderr}`);
5327
5495
  } else log$1.info("[dry-run] Skipping docker login");
5328
- const allTags = [];
5329
- const skipped = [];
5496
+ const outcome = {
5497
+ tags: [],
5498
+ skipped: [],
5499
+ kept: []
5500
+ };
5330
5501
  try {
5331
- for (const pkg of publishablePackages) tagAndPushPackage(executor, config, pkg, allTags, skipped);
5502
+ for (const pkg of publishablePackages) tagAndPushPackage(executor, config, pkg, outcome);
5332
5503
  } finally {
5333
5504
  if (!config.dryRun) {
5334
5505
  log$1.info(`Logging out from ${config.registryHost}...`);
5335
5506
  executor.exec("docker", ["logout", config.registryHost]);
5336
5507
  }
5337
5508
  }
5338
- log$1.info(`Published ${String(allTags.length)} image tag(s)`);
5339
- if (skipped.length > 0) log$1.info(`Left ${String(skipped.length)} floating tag(s) alone: ${skipped.join(", ")}`);
5509
+ log$1.info(`Published ${String(outcome.tags.length)} image tag(s)`);
5510
+ if (outcome.skipped.length > 0) log$1.info(`Left ${String(outcome.skipped.length)} floating tag(s) alone: ${outcome.skipped.join(", ")}`);
5511
+ if (outcome.kept.length > 0) log$1.info(`Kept ${String(outcome.kept.length)} record(s) already in the registry: ${outcome.kept.join(", ")}`);
5340
5512
  return {
5341
5513
  packages,
5342
- tags: allTags,
5343
- skipped
5514
+ ...outcome
5344
5515
  };
5345
5516
  }
5346
5517
  //#endregion
@@ -6291,8 +6462,22 @@ function resolveFromOverrides(overrides, isMonorepo, stack) {
6291
6462
  function resolveHealthConfigFromContext(reader, opts) {
6292
6463
  return resolveFromOverrides(parseToolingConfigSlice(reader.read(".tooling.json"), ToolingShallowSchema$2, { block: "health" })?.health, opts.isMonorepo, reader.config.stack);
6293
6464
  }
6465
+ /**
6466
+ * The two facts about a tree that `resolveHealthConfig` detects but does not return: the stack and
6467
+ * whether it is a monorepo. Exported for `health:check`'s reference read, which must resolve the
6468
+ * default branch's `health` block with the *same* context the working tree's block was resolved
6469
+ * with -- and which therefore has to come out of this module rather than a fresh import, because
6470
+ * the command that needs it sits at its fan-out ceiling.
6471
+ */
6472
+ function detectHealthContext(targetDir) {
6473
+ return {
6474
+ stack: resolveStack(targetDir),
6475
+ isMonorepo: existsSync(path.join(targetDir, "packages"))
6476
+ };
6477
+ }
6294
6478
  /** Resolve from the real filesystem (used by CLI commands). */
6295
6479
  function resolveHealthConfig(targetDir) {
6480
+ const { stack, isMonorepo } = detectHealthContext(targetDir);
6296
6481
  return resolveHealthConfigFromContext({
6297
6482
  exists: (rel) => existsSync(path.join(targetDir, rel)),
6298
6483
  read: (rel) => {
@@ -6304,8 +6489,8 @@ function resolveHealthConfig(targetDir) {
6304
6489
  return;
6305
6490
  }
6306
6491
  },
6307
- config: { stack: resolveStack(targetDir) }
6308
- }, { isMonorepo: existsSync(path.join(targetDir, "packages")) });
6492
+ config: { stack }
6493
+ }, { isMonorepo });
6309
6494
  }
6310
6495
  //#endregion
6311
6496
  //#region src/utils/script-forwarding.ts
@@ -7163,7 +7348,7 @@ function buildManagedDevDeps(ctx) {
7163
7348
  const devDeps = { ...rootDevDeps(ctx.config) };
7164
7349
  if (!isMonorepo) Object.assign(devDeps, perPackageDevDeps(ctx.config));
7165
7350
  if (ctx.config.stack !== "none" || ctx.config.ci === "forgejo") devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.14.5";
7166
- if (!isWorkspacePackage(ctx, "@bensandee/tooling")) devDeps["@bensandee/tooling"] = "0.84.4";
7351
+ if (!isWorkspacePackage(ctx, "@bensandee/tooling")) devDeps["@bensandee/tooling"] = "0.85.0";
7167
7352
  if (ctx.config.useEslintPlugin && ctx.config.stack !== "none") devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.15.2";
7168
7353
  if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
7169
7354
  "@changesets/cli": "3.0.3",
@@ -10550,7 +10735,7 @@ function checkLegacyAllowKey(ctx) {
10550
10735
  };
10551
10736
  }
10552
10737
  async function generateClaudeHooks(ctx) {
10553
- const results = [writeIfChanged(ctx, HOOK_PATH, "/**\n * bst-hook -- Claude Code PreToolUse hook\n * @bensandee/claude-hooks\n * https://github.com/bensandee/tooling\n *\n * Auto-generated by @bensandee/tooling repo:sync -- do not edit manually.\n */\nimport * as fs from \"node:fs\";\nimport { existsSync, readFileSync, readdirSync, realpathSync, statSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execFileSync } from \"node:child_process\";\n\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/util.js\nfunction getEnumValues(entries) {\n const numericValues = Object.values(entries).filter((v) => typeof v === \"number\");\n return Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);\n}\nfunction joinValues(array, separator = \"|\") {\n return array.map((val) => stringifyPrimitive(val)).join(separator);\n}\nfunction jsonStringifyReplacer(_, value) {\n if (typeof value === \"bigint\") return value.toString();\n return value;\n}\nvar Cached = class {\n constructor(getter) {\n this._getter = getter;\n this._value = void 0;\n }\n get value() {\n const getter = this._getter;\n if (getter !== void 0) {\n this._value = getter();\n this._getter = void 0;\n }\n return this._value;\n }\n};\nfunction cached(getter) {\n return new Cached(getter);\n}\nfunction nullish(input) {\n return input === null || input === void 0;\n}\nfunction cleanRegex(source) {\n const start = source.startsWith(\"^\") ? 1 : 0;\n const end = source.endsWith(\"$\") ? source.length - 1 : source.length;\n return source.slice(start, end);\n}\nfunction assignProp(target, prop, value) {\n Object.defineProperty(target, prop, {\n value,\n writable: true,\n enumerable: true,\n configurable: true\n });\n}\n/**\n* Whichever object a def's `shape` currently answers from: the one the caller passed until the first read, the frozen copy after it.\n*\n* Its keys and descriptors read without invoking anything, which is what lets a discriminated union check its discriminator, and the cycle walk read a shape, without resolving a getter that references the schema being constructed. A def that answers `shape` from an accessor of its own has none.\n*/\nfunction rawShape(def) {\n const desc = Object.getOwnPropertyDescriptor(def, \"shape\");\n return desc?.get ? desc.get.raw : desc?.value;\n}\nfunction sourceShape(schema) {\n return rawShape(schema._zod.def) ?? schema._zod.def.shape;\n}\nfunction deferProp(target, key, getter) {\n Object.defineProperty(target, key, {\n get() {\n const value = getter();\n assignProp(this, key, value);\n return value;\n },\n enumerable: true,\n configurable: true\n });\n}\nfunction putProp(target, key, value) {\n if (key in target) assignProp(target, key, value);\n else target[key] = value;\n}\n/**\n* Copies `keys` of `source`'s shape onto `target`, each value passed through `wrap`.\n*\n* A key the source has resolved is copied through now, so the derived shape states it outright and nothing has to resolve it to learn what it holds. A key the source still defers stays deferred, and reads back through the source's own `shape`, so it resolves once and both shapes get that one schema.\n*/\nfunction mirrorShape(target, source, keys, wrap) {\n const raw = sourceShape(source);\n for (const key of keys) {\n const desc = Object.getOwnPropertyDescriptor(raw, key);\n if (!desc.enumerable) continue;\n if (desc.get) deferProp(target, key, () => {\n const value = source._zod.def.shape[key];\n return wrap ? wrap(value, key) : value;\n });\n else putProp(target, key, wrap ? wrap(desc.value, key) : desc.value);\n }\n}\nfunction mirrorProps(target, source) {\n for (const key of Reflect.ownKeys(source)) {\n const desc = Object.getOwnPropertyDescriptor(source, key);\n if (!desc.enumerable) continue;\n if (desc.get) deferProp(target, key, () => source[key]);\n else putProp(target, key, desc.value);\n }\n}\nfunction mergeDefs(...defs) {\n const mergedDescriptors = {};\n for (const def of defs) {\n const descriptors = Object.getOwnPropertyDescriptors(def);\n Object.assign(mergedDescriptors, descriptors);\n }\n return Object.defineProperties({}, mergedDescriptors);\n}\nfunction esc(str) {\n return JSON.stringify(str);\n}\nfunction slugify(input) {\n return input.toLowerCase().trim().replace(/[^\\w\\s-]/g, \"\").replace(/[\\s_-]+/g, \"-\").replace(/^-+|-+$/g, \"\");\n}\nconst captureStackTrace = \"captureStackTrace\" in Error ? Error.captureStackTrace : (..._args) => {};\nfunction isObject(data) {\n return typeof data === \"object\" && data !== null && !Array.isArray(data);\n}\nconst allowsEval = /* @__PURE__*/ cached(() => {\n if (globalConfig.jitless) return false;\n if (typeof navigator !== \"undefined\" && navigator?.userAgent?.includes(\"Cloudflare\")) return false;\n try {\n new Function(\"\");\n return true;\n } catch (_) {\n return false;\n }\n});\nfunction isPlainObject(o) {\n if (isObject(o) === false) return false;\n const ctor = o.constructor;\n if (ctor === void 0) return true;\n if (typeof ctor !== \"function\") return true;\n const prot = ctor.prototype;\n if (isObject(prot) === false) return false;\n if (Object.prototype.hasOwnProperty.call(prot, \"isPrototypeOf\") === false) return false;\n return true;\n}\nfunction shallowClone(o) {\n if (isPlainObject(o)) return { ...o };\n if (Array.isArray(o)) return [...o];\n if (o instanceof Map) return new Map(o);\n if (o instanceof Set) return new Set(o);\n return o;\n}\nconst propertyKeyTypes = /* @__PURE__*/ new Set([\n \"string\",\n \"number\",\n \"symbol\"\n]);\nfunction escapeRegex(str) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\nfunction clone(inst, def, params) {\n const cl = new inst._zod.constr(def ?? inst._zod.def);\n if (!def || params?.parent) cl._zod.parent = inst;\n return cl;\n}\nfunction normalizeParams(_params) {\n const params = _params;\n if (!params) return {};\n if (typeof params === \"string\") return { error: () => params };\n if (params?.message !== void 0) {\n if (params?.error !== void 0) throw new Error(\"Cannot specify both `message` and `error` params\");\n params.error = params.message;\n }\n delete params.message;\n if (typeof params.error === \"string\") return {\n ...params,\n error: () => params.error\n };\n return params;\n}\nfunction stringifyPrimitive(value) {\n if (typeof value === \"bigint\") return value.toString() + \"n\";\n if (typeof value === \"string\") return `\"${value}\"`;\n return `${value}`;\n}\nfunction optionalKeys(shape) {\n return Object.keys(shape).filter((k) => {\n return shape[k]._zod.optin !== void 0 && shape[k]._zod.optout === \"optional\";\n });\n}\nconst NUMBER_FORMAT_RANGES = /*@__PURE__*/ (() => ({\n safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],\n int32: [-2147483648, 2147483647],\n uint32: [0, 4294967295],\n float32: [-34028234663852886e22, 34028234663852886e22],\n float64: [-Number.MAX_VALUE, Number.MAX_VALUE]\n}))();\nconst BIGINT_FORMAT_RANGES = {\n int64: [/* @__PURE__*/ BigInt(\"-9223372036854775808\"), /* @__PURE__*/ BigInt(\"9223372036854775807\")],\n uint64: [/* @__PURE__*/ BigInt(0), /* @__PURE__*/ BigInt(\"18446744073709551615\")]\n};\nfunction pick(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n if (checks && checks.length > 0) throw new Error(\".pick() cannot be used on object schemas containing refinements\");\n const newShape = {};\n mirrorShape(newShape, schema, maskedKeys(schema, mask));\n return clone(schema, mergeDefs(currDef, {\n shape: newShape,\n checks: []\n }));\n}\nfunction maskedKeys(schema, mask) {\n const raw = sourceShape(schema);\n const keys = [];\n for (const key of Reflect.ownKeys(mask)) {\n if (!Object.getOwnPropertyDescriptor(raw, key)?.enumerable) throw new Error(`Unrecognized key: \"${String(key)}\"`);\n if (mask[key]) keys.push(key);\n }\n return keys;\n}\nfunction omit(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n if (checks && checks.length > 0) throw new Error(\".omit() cannot be used on object schemas containing refinements\");\n const omitted = new Set(maskedKeys(schema, mask));\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)).filter((key) => !omitted.has(key)));\n return clone(schema, mergeDefs(currDef, {\n shape: newShape,\n checks: []\n }));\n}\nfunction extend(schema, shape) {\n if (!isPlainObject(shape)) throw new Error(\"Invalid input to extend: expected a plain object\");\n const checks = schema._zod.def.checks;\n if (checks && checks.length > 0) {\n const existingShape = sourceShape(schema);\n for (const key of Reflect.ownKeys(shape)) if (Object.getOwnPropertyDescriptor(existingShape, key) !== void 0) throw new Error(\"Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.\");\n }\n return clone(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));\n}\nfunction extended(schema, shape) {\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)));\n mirrorProps(newShape, shape);\n return newShape;\n}\nfunction safeExtend(schema, shape) {\n if (!isPlainObject(shape)) throw new Error(\"Invalid input to safeExtend: expected a plain object\");\n return clone(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));\n}\nfunction merge(a, b) {\n if (!b?._zod?.def) throw new Error(\"Invalid input to merge: expected an object schema. To merge a plain shape, use `.extend()`.\");\n if (a._zod.def.checks?.length) throw new Error(\".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.\");\n const newShape = {};\n mirrorShape(newShape, a, Reflect.ownKeys(sourceShape(a)));\n mirrorShape(newShape, b, Reflect.ownKeys(sourceShape(b)));\n return clone(a, mergeDefs(a._zod.def, {\n shape: newShape,\n get catchall() {\n return b._zod.def.catchall;\n },\n checks: b._zod.def.checks ?? []\n }));\n}\nfunction partial(Class, schema, mask, name = \"partial\") {\n const checks = schema._zod.def.checks;\n if (checks && checks.length > 0) throw new Error(`.${name}() cannot be used on object schemas containing refinements`);\n const selected = mask ? new Set(maskedKeys(schema, mask)) : void 0;\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)), Class && ((value, key) => selected && !selected.has(key) ? value : new Class({\n type: \"optional\",\n innerType: value\n })));\n return clone(schema, mergeDefs(schema._zod.def, {\n shape: newShape,\n checks: []\n }));\n}\nfunction required(Class, schema, mask) {\n const selected = mask ? new Set(maskedKeys(schema, mask)) : void 0;\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)), (value, key) => selected && !selected.has(key) ? value : new Class({\n type: \"nonoptional\",\n innerType: value\n }));\n return clone(schema, mergeDefs(schema._zod.def, { shape: newShape }));\n}\nfunction aborted(x, startIndex = 0) {\n if (x.aborted === true) return true;\n for (let i = startIndex; i < x.issues.length; i++) if (x.issues[i]?.continue !== true) return true;\n return false;\n}\nfunction explicitlyAborted(x, startIndex = 0) {\n if (x.aborted === true) return true;\n for (let i = startIndex; i < x.issues.length; i++) if (x.issues[i]?.continue === false) return true;\n return false;\n}\nfunction prefixIssues(path, issues) {\n return issues.map((iss) => {\n var _a;\n (_a = iss).path ?? (_a.path = []);\n iss.path.unshift(path);\n return iss;\n });\n}\nfunction unwrapMessage(message) {\n return typeof message === \"string\" ? message : message?.message;\n}\nfunction attachSchema(issues, start, inst) {\n var _a;\n for (let i = start; i < issues.length; i++) (_a = issues[i]).schema ?? (_a.schema = inst);\n}\nfunction finalizeIssue(iss, ctx, config) {\n var _a;\n const traits = iss.inst?._zod?.traits;\n if (traits?.has(\"$ZodType\")) {\n if (traits.has(\"$ZodCheck\")) (_a = iss).schema ?? (_a.schema = iss.inst);\n else iss.schema = iss.inst;\n }\n const schemaError = iss.schema !== iss.inst ? iss.schema?._zod.def?.error : void 0;\n const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(schemaError?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config.customError?.(iss)) ?? unwrapMessage(config.localeError?.(iss)) ?? \"Invalid input\";\n const full = {};\n for (const k of Object.keys(iss)) {\n if (k === \"inst\" || k === \"schema\" || k === \"continue\" || k === \"input\" || k === \"__proto__\") continue;\n full[k] = iss[k];\n }\n full.path ?? (full.path = []);\n full.message = message;\n if (ctx?.reportInput) full.input = iss.input;\n return full;\n}\nconst highSurrogate = /[\\uD800-\\uDBFF]/;\nfunction codePointLength(str) {\n const units = str.length;\n if (!highSurrogate.test(str)) return units;\n let count = units;\n for (let i = 0; i < units - 1; i++) if ((str.charCodeAt(i) & 64512) === 55296 && (str.charCodeAt(i + 1) & 64512) === 56320) {\n count--;\n i++;\n }\n return count;\n}\nfunction getLengthableOrigin(input) {\n if (Array.isArray(input)) return \"array\";\n if (typeof input === \"string\") return \"string\";\n return \"unknown\";\n}\nfunction parsedType(data) {\n const t = typeof data;\n switch (t) {\n case \"number\": return Number.isNaN(data) ? \"nan\" : \"number\";\n case \"object\": {\n if (data === null) return \"null\";\n if (Array.isArray(data)) return \"array\";\n const obj = data;\n if (obj && Object.getPrototypeOf(obj) !== Object.prototype && \"constructor\" in obj && obj.constructor) return obj.constructor.name;\n }\n }\n return t;\n}\nfunction issue(...args) {\n const [iss, input, inst] = args;\n if (typeof iss === \"string\") return {\n message: iss,\n code: \"custom\",\n input,\n inst\n };\n return { ...iss };\n}\n/**\n* Installs a trait's members on its prototype. Each value builds that member for the instance on first read; the built value shadows the accessor as an own property, so a detached `const { parse } = schema` keeps working.\n*\n* Call this from a `proto` initializer, which runs once per prototype — never per instance.\n*/\nfunction members(proto, table) {\n for (const key in table) {\n const desc = Object.getOwnPropertyDescriptor(table, key);\n if (desc.get) Object.defineProperty(proto, key, {\n ...desc,\n enumerable: false\n });\n else defineBound(proto, key, desc.value);\n }\n}\n/** Shadows a prototype member with an own value, so a getter that builds from the instance runs once. */\nfunction own(inst, key, value, enumerable = true) {\n Object.defineProperty(inst, key, {\n configurable: true,\n writable: true,\n enumerable,\n value\n });\n return value;\n}\n/** Like {@link own}, for a member that was never an own data property and has to stay out of `Object.keys`. */\nfunction hide(inst, key, value) {\n return own(inst, key, value, false);\n}\n/** Adds members a table derives from the instance: each builds on first read and shadows as own data, and assignment shadows the same way, as when these were own properties. */\nfunction derived(computes, table) {\n for (const key in computes) {\n const compute = computes[key];\n Object.defineProperty(table, key, {\n configurable: true,\n enumerable: true,\n get() {\n return own(this, key, compute(this));\n },\n set(value) {\n own(this, key, value);\n }\n });\n }\n return table;\n}\nfunction defineBound(proto, key, fn) {\n Object.defineProperty(proto, key, {\n configurable: true,\n get() {\n return this == null ? fn : own(this, key, fn.bind(this));\n },\n set(value) {\n own(this, key, value);\n }\n });\n}\n/** Returns the prototype to install on, or `undefined` if this group is already installed on it. */\nfunction claim(inst, sentinel) {\n const proto = Object.getPrototypeOf(inst);\n return sentinel in proto ? void 0 : proto;\n}\nlet installing;\nlet broke = false;\nconst breaker = {\n configurable: true,\n get() {\n broke = true;\n }\n};\n/**\n* Installs a lazily-derived internal on the `_zod` prototype of `inst`'s\n* constructor, computed from the internals object itself and cached there on\n* first read. One accessor per constructor rather than one per instance.\n*/\nfunction defineLazyInternal(inst, key, compute) {\n const proto = Object.getPrototypeOf(inst._zod);\n if (key in proto && installing !== inst._zod) {\n installing = void 0;\n return;\n }\n installing = inst._zod;\n Object.defineProperty(proto, key, {\n configurable: true,\n get() {\n Object.defineProperty(this, key, breaker);\n const outer = broke;\n broke = false;\n try {\n const value = compute(this);\n if (broke) delete this[key];\n else Object.defineProperty(this, key, {\n configurable: true,\n writable: true,\n value\n });\n broke = broke || outer;\n return value;\n } catch (err) {\n delete this[key];\n broke = broke || outer;\n throw err;\n }\n },\n set(value) {\n Object.defineProperty(this, key, {\n configurable: true,\n writable: true,\n value\n });\n }\n });\n}\n/**\n* Installs `key` on `inst`'s prototype, computed by `make` on first read and cached there as an own\n* data property. One accessor per constructor rather than one per instance, because an own accessor\n* puts every instance after the first into v8 dictionary mode. The key doubles as the sentinel.\n*/\nfunction installLazyProp(inst, key, make, enumerable) {\n const proto = claim(inst, key);\n if (!proto) return;\n Object.defineProperty(proto, key, {\n configurable: true,\n get() {\n const desc = {\n configurable: true,\n writable: true,\n enumerable,\n value: void 0\n };\n Object.defineProperty(this, key, desc);\n desc.value = make(this);\n Object.defineProperty(this, key, desc);\n return desc.value;\n },\n set(value) {\n Object.defineProperty(this, key, {\n configurable: true,\n writable: true,\n enumerable,\n value\n });\n }\n });\n}\n/** Marks the thunk `_catch` synthesises for a constant catch value. `Function.length` cannot tell that thunk from a user callback — rest and defaulted parameters both report arity 0 — and a user callback reads `ctx.error`, whose issues only finalize correctly against the caller's per-parse error map. Provenance can say what arity cannot. A plain string key rather than `Symbol.for`, whose call at module scope no bundler can prove pure — the same shape that anchored `urlCanParse` into every build. */\nconst CONSTANT_CATCH = \"~constantCatch\";\n/** Wraps a constant catch value in a thunk tagged with {@link CONSTANT_CATCH}. */\nfunction constantCatch(value) {\n const fn = () => value;\n fn[CONSTANT_CATCH] = true;\n return fn;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/core.js\nvar _a$1;\nconst _zodDesc = {\n value: void 0,\n enumerable: false\n};\nlet _E = \"captureStackTrace\" in Error ? Error : null;\nfunction newError(Definition) {\n const E = _E;\n if (E) {\n const saved = E.stackTraceLimit;\n if (typeof saved === \"number\") {\n try {\n E.stackTraceLimit = 0;\n } catch {\n _E = null;\n return new Definition();\n }\n try {\n return new Definition();\n } finally {\n E.stackTraceLimit = saved;\n }\n }\n }\n return new Definition();\n}\nfunction $constructor(name, initializer, proto, params) {\n const zodProto = {};\n function Internals(def) {\n this.def = def;\n this.constr = _;\n this.traits = /* @__PURE__ */ new Set();\n }\n Internals.prototype = zodProto;\n const protoMembers = proto;\n const initialized = protoMembers && /* @__PURE__ */ new WeakSet();\n function init(inst, def) {\n if (!inst._zod) {\n _zodDesc.value = new Internals(def);\n try {\n Object.defineProperty(inst, \"_zod\", _zodDesc);\n } finally {\n _zodDesc.value = void 0;\n }\n } else if (inst._zod.traits.has(name)) return;\n inst._zod.traits.add(name);\n initializer(inst, def);\n if (initialized) {\n const own = Object.getPrototypeOf(inst);\n const ctorProto = inst._zod.constr.prototype;\n let up = own;\n while (up && up !== ctorProto) up = Object.getPrototypeOf(up);\n const target = up ?? own;\n if (!initialized.has(target)) {\n initialized.add(target);\n members(target, protoMembers);\n }\n }\n const proto = _.prototype;\n for (const k in proto) {\n if (!Object.prototype.hasOwnProperty.call(proto, k)) continue;\n if (!(k in inst)) inst[k] = proto[k].bind(inst);\n }\n }\n const Parent = params?.Parent ?? Object;\n class Definition extends Parent {}\n Object.defineProperty(Definition, \"name\", { value: name });\n function _(def) {\n const inst = params?.Parent ? newError(Definition) : this;\n init(inst, def);\n const deferred = inst._zod.deferred;\n if (deferred) {\n for (const fn of deferred) fn();\n inst._zod.deferred = void 0;\n }\n const pp = globalThis.__zod_globalConfig?.postProcessor;\n if (pp) pp(inst);\n return inst;\n }\n Object.defineProperty(_, \"init\", { value: init });\n Object.defineProperty(_, Symbol.hasInstance, { value: (inst) => {\n if (params?.Parent && inst instanceof params.Parent) return true;\n return inst?._zod?.traits?.has(name);\n } });\n Object.defineProperty(_, \"name\", { value: name });\n return _;\n}\nvar $ZodAsyncError = class extends Error {\n constructor() {\n super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);\n }\n};\nvar $ZodEncodeError = class extends Error {\n constructor(name) {\n super(`Encountered unidirectional transform during encode: ${name}`);\n this.name = \"ZodEncodeError\";\n }\n};\n(_a$1 = globalThis).__zod_globalConfig ?? (_a$1.__zod_globalConfig = {});\nconst globalConfig = globalThis.__zod_globalConfig;\nfunction config(newConfig) {\n if (newConfig) Object.assign(globalConfig, newConfig);\n return globalConfig;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/errors.js\nfunction _getMessage() {\n const internals = this._zod;\n internals.message ?? (internals.message = JSON.stringify(internals.def, jsonStringifyReplacer, 2));\n return internals.message;\n}\nfunction _setMessage(value) {\n this._zod.message = value;\n}\nconst _messageDesc = {\n get: _getMessage,\n set: _setMessage,\n enumerable: true,\n configurable: true\n};\nconst _issuesDesc = {\n value: void 0,\n enumerable: false\n};\nconst _installedToString = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);\nconst initializer$1 = (inst, def) => {\n inst.name = \"$ZodError\";\n _issuesDesc.value = def;\n Object.defineProperty(inst, \"issues\", _issuesDesc);\n _issuesDesc.value = void 0;\n Object.defineProperty(inst, \"message\", _messageDesc);\n const proto = Object.getPrototypeOf(inst);\n if (!_installedToString.has(proto)) {\n _installedToString.add(proto);\n Object.defineProperty(proto, \"toString\", {\n configurable: true,\n enumerable: false,\n get() {\n const value = () => this.message;\n Object.defineProperty(this, \"toString\", {\n value,\n configurable: true,\n writable: true\n });\n return value;\n },\n set(value) {\n Object.defineProperty(this, \"toString\", {\n value,\n configurable: true,\n writable: true\n });\n }\n });\n }\n};\nconst $ZodError = $constructor(\"$ZodError\", initializer$1);\nconst $ZodRealError = $constructor(\"$ZodError\", initializer$1, void 0, { Parent: Error });\n/** Get-or-create `obj[key]` as an own data property. A path segment naming an inherited member\n* (\"toString\", \"constructor\") would otherwise read through to the prototype, and assigning\n* \"__proto__\" would hit the setter instead of creating a key. */\nfunction node(obj, key, make) {\n if (!Object.prototype.hasOwnProperty.call(obj, key)) {\n if (key === \"__proto__\") Object.defineProperty(obj, key, {\n value: make(),\n writable: true,\n enumerable: true,\n configurable: true\n });\n else obj[key] = make();\n }\n return obj[key];\n}\nfunction flattenError(error, mapper = (issue) => issue.message) {\n const fieldErrors = {};\n const formErrors = [];\n for (const sub of error.issues) if (sub.path.length > 0) node(fieldErrors, sub.path[0], () => []).push(mapper(sub));\n else formErrors.push(mapper(sub));\n return {\n formErrors,\n fieldErrors\n };\n}\nfunction formatError(error, mapper = (issue) => issue.message) {\n const fieldErrors = { _errors: [] };\n const processError = (error, path = []) => {\n for (const issue of error.issues) if (issue.code === \"invalid_union\" && issue.errors.length) issue.errors.map((issues) => processError({ issues }, [...path, ...issue.path]));\n else if (issue.code === \"invalid_key\") processError({ issues: issue.issues }, [...path, ...issue.path]);\n else if (issue.code === \"invalid_element\") processError({ issues: issue.issues }, [...path, ...issue.path]);\n else {\n const fullpath = [...path, ...issue.path];\n if (fullpath.length === 0) fieldErrors._errors.push(mapper(issue));\n else {\n let curr = fieldErrors;\n let i = 0;\n while (i < fullpath.length) {\n const el = fullpath[i];\n const terminal = i === fullpath.length - 1;\n if (el === \"_errors\") {\n if (terminal) curr._errors.push(mapper(issue));\n i++;\n continue;\n }\n if (!Object.prototype.hasOwnProperty.call(curr, el)) Object.defineProperty(curr, el, {\n value: { _errors: [] },\n enumerable: true,\n writable: true,\n configurable: true\n });\n const node = curr[el];\n if (terminal) node._errors.push(mapper(issue));\n curr = node;\n i++;\n }\n }\n }\n };\n processError(error);\n return fieldErrors;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/parse.js\nfunction finalizeParams(callee, params) {\n return {\n callee: params?.callee ?? callee,\n Err: params?.Err\n };\n}\nconst _parse = (_Err) => {\n const fn = (schema, value, _ctx, _params) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: false\n } : { async: false };\n const result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) throw new $ZodAsyncError();\n if (result.issues.length) {\n const e = new ((_params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));\n captureStackTrace(e, _params?.callee ?? fn);\n throw e;\n }\n return result.value;\n };\n return fn;\n};\nconst _parseAsync = (_Err) => {\n const fn = async (schema, value, _ctx, params) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: true\n } : { async: true };\n let result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) result = await result;\n if (result.issues.length) {\n const e = new ((params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));\n captureStackTrace(e, params?.callee ?? fn);\n throw e;\n }\n return result.value;\n };\n return fn;\n};\nconst _safeParse = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: false\n } : { async: false };\n const result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) throw new $ZodAsyncError();\n return result.issues.length ? failure(_Err, result.issues, ctx) : {\n success: true,\n data: result.value\n };\n};\nfunction failure(Err, issues, ctx) {\n let error;\n return {\n success: false,\n get error() {\n if (!error) {\n error = new Err(issues.map((iss) => finalizeIssue(iss, ctx, config())));\n issues = void 0;\n ctx = void 0;\n }\n return error;\n },\n set error(e) {\n error = e;\n issues = void 0;\n ctx = void 0;\n }\n };\n}\nconst _safeParseAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: true\n } : { async: true };\n let result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) result = await result;\n return result.issues.length ? failure(_Err, result.issues, ctx) : {\n success: true,\n data: result.value\n };\n};\nconst COMPILE_INVALID = /* @__PURE__ */ Symbol.for(\"zod.compile.invalid\");\nconst COMPILE_FALLBACK = /* @__PURE__ */ Symbol.for(\"zod.compile.fallback\");\nconst validate = ((schema, value, _ctx) => {\n const validator = schema._zod.bag.validator;\n if (validator !== void 0) {\n if (validator(value) !== COMPILE_INVALID) return true;\n if (validator.definite === true && _ctx === void 0) return false;\n }\n return validateFallback(schema, value, _ctx);\n});\nfunction validateFallback(schema, value, _ctx) {\n const ctx = _ctx ? {\n ..._ctx,\n async: false,\n abortEarly: true\n } : {\n async: false,\n abortEarly: true\n };\n const fallbackRun = schema._zod.bag.fallbackRun;\n let result;\n if (fallbackRun) {\n ctx[COMPILE_FALLBACK] = true;\n result = fallbackRun({\n value,\n issues: []\n }, ctx);\n } else result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) throw new $ZodAsyncError();\n return result.issues.length === 0;\n}\nconst validateAsync$1 = async (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: true,\n abortEarly: true\n } : {\n async: true,\n abortEarly: true\n };\n let result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) result = await result;\n return result.issues.length === 0;\n};\nconst _encode = (_Err) => {\n const parse = _parse(_Err);\n const fn = (schema, value, _ctx, _params) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return parse(schema, value, ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _decode = (_Err) => {\n const parse = _parse(_Err);\n const fn = (schema, value, _ctx, _params) => {\n return parse(schema, value, _ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _encodeAsync = (_Err) => {\n const parseAsync = _parseAsync(_Err);\n const fn = async (schema, value, _ctx, _params) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return await parseAsync(schema, value, ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _decodeAsync = (_Err) => {\n const parseAsync = _parseAsync(_Err);\n const fn = async (schema, value, _ctx, _params) => {\n return await parseAsync(schema, value, _ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _safeEncode = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return _safeParse(_Err)(schema, value, ctx);\n};\nconst _safeDecode = (_Err) => (schema, value, _ctx) => {\n return _safeParse(_Err)(schema, value, _ctx);\n};\nconst _safeEncodeAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return _safeParseAsync(_Err)(schema, value, ctx);\n};\nconst _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {\n return _safeParseAsync(_Err)(schema, value, _ctx);\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/regexes.js\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link cuid2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\nconst cuid = /^[cC][0-9a-z]{6,}$/;\nconst cuid2 = /^[0-9a-z]+$/;\nconst ulid = /^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/;\nconst xid = /^[0-9a-vA-V]{20}$/;\nconst ksuid = /^[A-Za-z0-9]{27}$/;\nconst nanoid = /^[a-zA-Z0-9_-]{21}$/;\nfunction nanoidOfLength(length) {\n return new RegExp(`^[a-zA-Z0-9_-]{${length}}$`);\n}\n/** ISO 8601-1 duration regex. Does not support the 8601-2 extensions like negative durations or fractional/negative components. */\nconst duration = /^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$/;\n/** A regex for any UUID-like identifier: 8-4-4-4-12 hex pattern */\nconst guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;\n/** Returns a regex for validating an RFC 9562/4122 UUID.\n*\n* @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */\nconst uuid = (version) => {\n if (!version) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;\n return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);\n};\n/** Practical email validation */\nconst email = /^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$/;\nconst _emoji$1 = `^(?=[\\\\s\\\\S]*[\\\\p{Extended_Pictographic}\\\\p{Regional_Indicator}\\\\u20E3])[\\\\p{Extended_Pictographic}\\\\p{Emoji_Component}]+$`;\nfunction emoji() {\n return new RegExp(_emoji$1, \"u\");\n}\nconst ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;\nconst ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;\nconst cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\/([0-9]|[1-2][0-9]|3[0-2])$/;\nconst cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;\nconst base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;\nconst base64url = /^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-]{2,3})?$/;\nconst httpProtocol = /^https?$/;\nconst e164 = /^\\+[1-9]\\d{6,14}$/;\nconst dateSource = `(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))`;\n/** Anchors a pattern source. The interpolation lives here rather than at the call site because\n* esbuild will not drop a `@__PURE__` call whose own argument interpolates a variable, but it\n* will drop `anchor(dateSource)`. Keeping it inline pinned `date` into every bundle. */\nfunction anchor(source) {\n return new RegExp(`^${source}$`);\n}\nconst date = /*@__PURE__*/ anchor(dateSource);\nfunction timeSource(args) {\n const hhmm = `(?:[01]\\\\d|2[0-3]):[0-5]\\\\d`;\n return typeof args.precision === \"number\" ? args.precision === -1 ? `${hhmm}` : args.precision === 0 ? `${hhmm}:[0-5]\\\\d` : `${hhmm}:[0-5]\\\\d\\\\.\\\\d{${args.precision}}` : args.seconds ? `${hhmm}:[0-5]\\\\d(?:\\\\.\\\\d+)?` : `${hhmm}(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?`;\n}\nfunction time(args) {\n return new RegExp(`^${timeSource(args)}$`);\n}\nfunction datetime(args) {\n const opts = [\"Z\"];\n if (args.offset) opts.push(`([+-](?:[01]\\\\d|2[0-3]):[0-5]\\\\d)`);\n const qualified = `${timeSource({\n precision: args.precision,\n seconds: true\n })}(?:${opts.join(\"|\")})`;\n const timeRegex = args.local ? `${qualified}|${timeSource({ precision: args.precision })}` : qualified;\n return new RegExp(`^${dateSource}T(?:${timeRegex})$`);\n}\nconst anyString = /^[\\s\\S]{0,}$/;\nconst integer = /^-?\\d+$/;\nconst number = /^-?\\d+(?:\\.\\d+)?$/;\nconst lowercase = /^[^A-Z]*$/;\nconst uppercase = /^[^a-z]*$/;\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/checks.js\nconst $ZodCheck = /*@__PURE__*/ $constructor(\"$ZodCheck\", (inst, def) => {\n var _a;\n inst._zod ?? (inst._zod = {});\n inst._zod.def = def;\n (_a = inst._zod).onattach ?? (_a.onattach = []);\n});\n/** Default `when` for length-based checks: run only on non-nullish values with a `length`. */\nconst _whenHasLength = (payload) => {\n const val = payload.value;\n return !nullish(val) && val.length !== void 0;\n};\nconst $ZodCheckMaxLength = /*@__PURE__*/ $constructor(\"$ZodCheckMaxLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = _whenHasLength);\n inst._zod.check = (payload) => {\n const input = payload.value;\n const units = input.length;\n if ((typeof input === \"string\" && units > def.maximum ? codePointLength(input) : units) <= def.maximum) return;\n const origin = getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_big\",\n maximum: def.maximum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckMinLength = /*@__PURE__*/ $constructor(\"$ZodCheckMinLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = _whenHasLength);\n inst._zod.check = (payload) => {\n const input = payload.value;\n const units = input.length;\n if ((typeof input === \"string\" && units >= def.minimum && units < def.minimum * 2 ? codePointLength(input) : units) >= def.minimum) return;\n const origin = getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_small\",\n minimum: def.minimum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckLengthEquals = /*@__PURE__*/ $constructor(\"$ZodCheckLengthEquals\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = _whenHasLength);\n inst._zod.check = (payload) => {\n const input = payload.value;\n const units = input.length;\n const length = typeof input === \"string\" && units >= def.length && units <= def.length * 2 ? codePointLength(input) : units;\n if (length === def.length) return;\n const origin = getLengthableOrigin(input);\n const tooBig = length > def.length;\n payload.issues.push({\n origin,\n ...tooBig ? {\n code: \"too_big\",\n maximum: def.length\n } : {\n code: \"too_small\",\n minimum: def.length\n },\n inclusive: true,\n exact: true,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckStringFormat = /*@__PURE__*/ $constructor(\"$ZodCheckStringFormat\", (inst, def) => {\n var _a, _b;\n $ZodCheck.init(inst, def);\n if (def.pattern) (_a = inst._zod).check ?? (_a.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: def.format,\n input: payload.value,\n ...def.pattern ? { pattern: def.pattern.toString() } : {},\n inst,\n continue: !def.abort\n });\n });\n else (_b = inst._zod).check ?? (_b.check = () => {});\n});\nconst $ZodCheckRegex = /*@__PURE__*/ $constructor(\"$ZodCheckRegex\", (inst, def) => {\n $ZodCheckStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"regex\",\n input: payload.value,\n pattern: def.pattern.toString(),\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckLowerCase = /*@__PURE__*/ $constructor(\"$ZodCheckLowerCase\", (inst, def) => {\n def.pattern ?? (def.pattern = lowercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nconst $ZodCheckUpperCase = /*@__PURE__*/ $constructor(\"$ZodCheckUpperCase\", (inst, def) => {\n def.pattern ?? (def.pattern = uppercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nconst $ZodCheckIncludes = /*@__PURE__*/ $constructor(\"$ZodCheckIncludes\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const escapedRegex = escapeRegex(def.includes);\n def.pattern = new RegExp(typeof def.position === \"number\" ? `^.{${def.position},}${escapedRegex}` : escapedRegex);\n inst._zod.check = (payload) => {\n if (payload.value.includes(def.includes, def.position)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"includes\",\n includes: def.includes,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckStartsWith = /*@__PURE__*/ $constructor(\"$ZodCheckStartsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`^${escapeRegex(def.prefix)}.*`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.check = (payload) => {\n if (payload.value.startsWith(def.prefix)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"starts_with\",\n prefix: def.prefix,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckEndsWith = /*@__PURE__*/ $constructor(\"$ZodCheckEndsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`.*${escapeRegex(def.suffix)}$`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.check = (payload) => {\n if (payload.value.endsWith(def.suffix)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"ends_with\",\n suffix: def.suffix,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckOverwrite = /*@__PURE__*/ $constructor(\"$ZodCheckOverwrite\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.check = (payload) => {\n payload.value = def.tx(payload.value);\n };\n});\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/doc.js\nvar Doc = class {\n constructor(args = [], closed = {}) {\n this.content = [];\n this.indent = 0;\n this.args = args;\n this.closed = closed;\n }\n indented(fn) {\n this.indent += 1;\n try {\n fn(this);\n } finally {\n this.indent -= 1;\n }\n }\n write(arg) {\n if (typeof arg === \"function\") {\n arg(this, { execution: \"sync\" });\n arg(this, { execution: \"async\" });\n return;\n }\n const lines = arg.split(\"\\n\").filter((x) => x);\n const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));\n const dedented = lines.map((x) => x.slice(minIndent)).map((x) => \" \".repeat(this.indent * 2) + x);\n for (const line of dedented) this.content.push(line);\n }\n compile() {\n const F = Function;\n const content = this?.content ?? [``];\n return new F(...Object.keys(this.closed), `return function (${this.args.join(\", \")}) {\\n${content.join(\"\\n\")}\\n};`)(...Object.values(this.closed));\n }\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/versions.js\nconst version = {\n major: 4,\n minor: 6,\n patch: 5\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/schemas.js\nconst $ZodType = /*@__PURE__*/ $constructor(\"$ZodType\", (inst, def) => {\n var _a;\n inst ?? (inst = {});\n inst._zod.def = def;\n inst._zod.bag = inst._zod.bag || {};\n inst._zod.version = version;\n const defChecks = inst._zod.def.checks;\n const checks = inst._zod.traits.has(\"$ZodCheck\") ? [inst, ...defChecks ?? []] : defChecks?.length ? [...defChecks] : [];\n for (const ch of checks) for (const fn of ch._zod.onattach) fn(inst);\n if (checks.length === 0) {\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred?.push(() => {\n inst._zod.run = inst._zod.parse;\n });\n } else {\n const runChecks = (payload, checks, ctx) => {\n if (payload.memo) return payload;\n let isAborted = aborted(payload);\n let asyncResult;\n for (const ch of checks) {\n if (ch._zod.def.when) {\n if (explicitlyAborted(payload)) continue;\n if (!ch._zod.def.when(payload)) continue;\n } else if (isAborted) continue;\n const currLen = payload.issues.length;\n const _ = ch._zod.check(payload);\n if (_ instanceof Promise && ctx?.async === false) throw new $ZodAsyncError();\n if (asyncResult || _ instanceof Promise) asyncResult = (asyncResult ?? Promise.resolve()).then(async () => {\n await _;\n if (payload.issues.length === currLen) return;\n attachSchema(payload.issues, currLen, inst);\n if (!isAborted) isAborted = aborted(payload, currLen);\n });\n else {\n if (payload.issues.length === currLen) continue;\n attachSchema(payload.issues, currLen, inst);\n if (!isAborted) isAborted = aborted(payload, currLen);\n }\n }\n if (asyncResult) return asyncResult.then(() => {\n return payload;\n });\n return payload;\n };\n const handleCanaryResult = (canary, payload, ctx) => {\n if (aborted(canary)) {\n canary.aborted = true;\n return canary;\n }\n const checkResult = runChecks(payload, checks, ctx);\n if (checkResult instanceof Promise) {\n if (ctx.async === false) throw new $ZodAsyncError();\n return checkResult.then((checkResult) => inst._zod.parse(checkResult, ctx));\n }\n return inst._zod.parse(checkResult, ctx);\n };\n inst._zod.run = (payload, ctx) => {\n if (ctx.skipChecks) return inst._zod.parse(payload, ctx);\n if (ctx.direction === \"backward\") {\n const canary = inst._zod.parse({\n value: payload.value,\n issues: []\n }, {\n ...ctx,\n skipChecks: true\n });\n if (canary instanceof Promise) return canary.then((canary) => {\n return handleCanaryResult(canary, payload, ctx);\n });\n return handleCanaryResult(canary, payload, ctx);\n }\n const result = inst._zod.parse(payload, ctx);\n if (result instanceof Promise) {\n if (ctx.async === false) throw new $ZodAsyncError();\n return result.then((result) => runChecks(result, checks, ctx));\n }\n return runChecks(result, checks, ctx);\n };\n }\n}, {\n get \"~standard\"() {\n return hide(this, \"~standard\", standardProps(this));\n },\n set \"~standard\"(value) {\n own(this, \"~standard\", value);\n }\n});\n/** The Standard Schema surface for `inst`. Shared so wrappers can extend it without forcing it. */\nconst toStandardResult = (r, ctx) => r.issues.length ? { issues: r.issues.map((iss) => finalizeIssue(iss, ctx, config())) } : { value: r.value };\nasync function validateAsync(inst, value) {\n const ctx = { async: true };\n return toStandardResult(await inst._zod.run({\n value,\n issues: []\n }, ctx), ctx);\n}\nfunction standardProps(inst) {\n return {\n validate: (value) => {\n const ctx = { async: false };\n try {\n const r = inst._zod.run({\n value,\n issues: []\n }, ctx);\n if (!(r instanceof Promise)) return toStandardResult(r, ctx);\n } catch (_) {}\n return validateAsync(inst, value);\n },\n vendor: \"zod\",\n version: 1\n };\n}\nconst $ZodString = /*@__PURE__*/ $constructor(\"$ZodString\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = def.pattern ?? anyString;\n inst._zod.parse = (payload, _) => {\n if (def.coerce) try {\n payload.value = String(payload.value);\n } catch (_) {}\n if (typeof payload.value === \"string\") return payload;\n payload.issues.push({\n expected: \"string\",\n code: \"invalid_type\",\n input: payload.value,\n inst\n });\n return payload;\n };\n});\nconst $ZodStringFormat = /*@__PURE__*/ $constructor(\"$ZodStringFormat\", (inst, def) => {\n $ZodCheckStringFormat.init(inst, def);\n $ZodString.init(inst, def);\n});\nconst $ZodGUID = /*@__PURE__*/ $constructor(\"$ZodGUID\", (inst, def) => {\n def.pattern ?? (def.pattern = guid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodUUID = /*@__PURE__*/ $constructor(\"$ZodUUID\", (inst, def) => {\n if (def.version) {\n const v = {\n v1: 1,\n v2: 2,\n v3: 3,\n v4: 4,\n v5: 5,\n v6: 6,\n v7: 7,\n v8: 8\n }[def.version];\n if (v === void 0) throw new Error(`Invalid UUID version: \"${def.version}\"`);\n def.pattern ?? (def.pattern = uuid(v));\n } else def.pattern ?? (def.pattern = uuid());\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodEmail = /*@__PURE__*/ $constructor(\"$ZodEmail\", (inst, def) => {\n def.pattern ?? (def.pattern = email);\n $ZodStringFormat.init(inst, def);\n});\n/** The `://` guard rejected the input before the URL constructor saw it. */\nconst URL_BAD_FORMAT = 1;\n/** The URL parser rejected the input. */\nconst URL_UNPARSEABLE = 2;\nfunction canParseURL(input) {\n try {\n if (typeof URL !== \"undefined\" && typeof URL.canParse === \"function\") return URL.canParse(input);\n new URL(input);\n return true;\n } catch {\n return false;\n }\n}\nfunction validateURL(trimmed, def) {\n if (!(\"normalize\" in def) && !(\"hostname\" in def) && !(\"protocol\" in def)) return canParseURL(trimmed) || 2;\n return parseURLObject(trimmed, def);\n}\n/** Parses a URL while preserving the non-normalizing HTTP guard. */\nfunction parseURLObject(trimmed, def) {\n if (!def.normalize && def.protocol?.source === httpProtocol.source && !/^https?:\\/\\//i.test(trimmed)) return 1;\n try {\n if (typeof URL !== \"undefined\") {\n const URLStatic = URL;\n if (typeof URLStatic.parse === \"function\") return URLStatic.parse(trimmed) ?? 2;\n }\n return new URL(trimmed);\n } catch {\n return 2;\n }\n}\nconst asciiTabOrNewline = /[\\t\\n\\r]/g;\n/** The URL parser deletes every ASCII tab, LF and CR from its input before it parses, so `new URL(\"https://exa\\nmple.com\")` reports on `example.com`. Applying the same deletion to the returned value closes the half of that divergence which can move the host; the parser's other rewrite, stripping C0 controls at the edges, cannot. */\nfunction stripTabAndNewline(value) {\n return value.replace(asciiTabOrNewline, \"\");\n}\nfunction urlHostnameOk(url, hostname) {\n hostname.lastIndex = 0;\n return hostname.test(url.hostname);\n}\nfunction urlProtocolOk(url, protocol) {\n protocol.lastIndex = 0;\n return protocol.test(url.protocol.endsWith(\":\") ? url.protocol.slice(0, -1) : url.protocol);\n}\nconst $ZodURL = /*@__PURE__*/ $constructor(\"$ZodURL\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n try {\n const trimmed = payload.value.trim();\n const url = validateURL(trimmed, def);\n if (url === 1) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid URL format\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n return;\n }\n if (url === 2) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n return;\n }\n if (url === true) {\n payload.value = stripTabAndNewline(trimmed);\n return;\n }\n if (def.hostname && !urlHostnameOk(url, def.hostname)) payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid hostname\",\n pattern: def.hostname.source,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n if (def.protocol && !urlProtocolOk(url, def.protocol)) payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid protocol\",\n pattern: def.protocol.source,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n payload.value = def.normalize ? url.href : stripTabAndNewline(trimmed);\n return;\n } catch (_) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n }\n };\n});\nconst $ZodEmoji = /*@__PURE__*/ $constructor(\"$ZodEmoji\", (inst, def) => {\n def.pattern ?? (def.pattern = emoji());\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodNanoID = /*@__PURE__*/ $constructor(\"$ZodNanoID\", (inst, def) => {\n if (def.length !== void 0 && (!Number.isInteger(def.length) || def.length < 1)) throw new Error(`Invalid nanoid length: ${def.length}`);\n def.pattern ?? (def.pattern = def.length === void 0 ? nanoid : nanoidOfLength(def.length));\n $ZodStringFormat.init(inst, def);\n});\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link $ZodCUID2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\nconst $ZodCUID = /*@__PURE__*/ $constructor(\"$ZodCUID\", (inst, def) => {\n def.pattern ?? (def.pattern = cuid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodCUID2 = /*@__PURE__*/ $constructor(\"$ZodCUID2\", (inst, def) => {\n def.pattern ?? (def.pattern = cuid2);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodULID = /*@__PURE__*/ $constructor(\"$ZodULID\", (inst, def) => {\n def.pattern ?? (def.pattern = ulid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodXID = /*@__PURE__*/ $constructor(\"$ZodXID\", (inst, def) => {\n def.pattern ?? (def.pattern = xid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodKSUID = /*@__PURE__*/ $constructor(\"$ZodKSUID\", (inst, def) => {\n def.pattern ?? (def.pattern = ksuid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISODateTime = /*@__PURE__*/ $constructor(\"$ZodISODateTime\", (inst, def) => {\n def.pattern ?? (def.pattern = datetime(def));\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISODate = /*@__PURE__*/ $constructor(\"$ZodISODate\", (inst, def) => {\n def.pattern ?? (def.pattern = date);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISOTime = /*@__PURE__*/ $constructor(\"$ZodISOTime\", (inst, def) => {\n def.pattern ?? (def.pattern = time(def));\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISODuration = /*@__PURE__*/ $constructor(\"$ZodISODuration\", (inst, def) => {\n def.pattern ?? (def.pattern = duration);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodIPv4 = /*@__PURE__*/ $constructor(\"$ZodIPv4\", (inst, def) => {\n def.pattern ?? (def.pattern = ipv4);\n $ZodStringFormat.init(inst, def);\n});\n/** An IPv6 address is written with hex digits, colons and dots, and nothing else. The guard is what makes the check below an IPv6 check: `new URL(\"http://[...]\")` parses an authority, not an address, so `@` and `\\` re-delimit it and `\"::@1\\\\\"` validates against the host `0.0.0.1`. The URL parser also deletes ASCII tab, LF and CR rather than failing, which is how `\"::1\\n\"` validated as `::1`. */\nconst ipv6Alphabet = /^[0-9a-fA-F:.]+$/;\nfunction isValidIPv6(value) {\n if (!ipv6Alphabet.test(value)) return false;\n return canParseURL(`http://[${value}]`);\n}\nconst $ZodIPv6 = /*@__PURE__*/ $constructor(\"$ZodIPv6\", (inst, def) => {\n def.pattern ?? (def.pattern = ipv6);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (!isValidIPv6(payload.value)) payload.issues.push({\n code: \"invalid_format\",\n format: \"ipv6\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCIDRv4 = /*@__PURE__*/ $constructor(\"$ZodCIDRv4\", (inst, def) => {\n def.pattern ?? (def.pattern = cidrv4);\n $ZodStringFormat.init(inst, def);\n});\nfunction isValidCIDRv6(value) {\n const parts = value.split(\"/\");\n if (parts.length !== 2) return false;\n const [address, prefix] = parts;\n if (!prefix) return false;\n const prefixNum = Number(prefix);\n if (`${prefixNum}` !== prefix) return false;\n if (prefixNum < 0 || prefixNum > 128) return false;\n return isValidIPv6(address);\n}\nconst $ZodCIDRv6 = /*@__PURE__*/ $constructor(\"$ZodCIDRv6\", (inst, def) => {\n def.pattern ?? (def.pattern = cidrv6);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (!isValidCIDRv6(payload.value)) payload.issues.push({\n code: \"invalid_format\",\n format: \"cidrv6\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nfunction isValidBase64(data) {\n if (data === \"\") return true;\n if (/\\s/.test(data)) return false;\n if (data.length % 4 !== 0) return false;\n try {\n atob(data);\n return true;\n } catch {\n return false;\n }\n}\nconst base64Charset = /^[0-9a-zA-Z+/]*={0,2}$/;\nconst $ZodBase64 = /*@__PURE__*/ $constructor(\"$ZodBase64\", (inst, def) => {\n def.pattern ?? (def.pattern = base64Charset);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidBase64(payload.value)) return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst base64urlCharset = /^[A-Za-z0-9_-]*$/;\nfunction isValidBase64URL(data) {\n if (!base64urlCharset.test(data)) return false;\n const base64 = data.replace(/[-_]/g, (c) => c === \"-\" ? \"+\" : \"/\");\n return isValidBase64(base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\"));\n}\nconst $ZodBase64URL = /*@__PURE__*/ $constructor(\"$ZodBase64URL\", (inst, def) => {\n def.pattern ?? (def.pattern = base64urlCharset);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidBase64URL(payload.value)) return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64url\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodE164 = /*@__PURE__*/ $constructor(\"$ZodE164\", (inst, def) => {\n def.pattern ?? (def.pattern = e164);\n $ZodStringFormat.init(inst, def);\n});\nfunction isValidJWT(token, algorithm = null) {\n try {\n const tokensParts = token.split(\".\");\n if (tokensParts.length !== 3) return false;\n const [header] = tokensParts;\n if (!header) return false;\n const parsedHeader = JSON.parse(atob(header));\n if (\"typ\" in parsedHeader && parsedHeader?.typ !== \"JWT\") return false;\n if (!parsedHeader.alg) return false;\n if (algorithm && (!(\"alg\" in parsedHeader) || parsedHeader.alg !== algorithm)) return false;\n return true;\n } catch {\n return false;\n }\n}\nconst $ZodJWT = /*@__PURE__*/ $constructor(\"$ZodJWT\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidJWT(payload.value, def.alg)) return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"jwt\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodUnknown = /*@__PURE__*/ $constructor(\"$ZodUnknown\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload) => payload;\n});\nconst $ZodNever = /*@__PURE__*/ $constructor(\"$ZodNever\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n payload.issues.push({\n expected: \"never\",\n code: \"invalid_type\",\n input: payload.value,\n inst\n });\n return payload;\n };\n});\nfunction handleArrayResult(result, final, index) {\n if (result.issues.length) final.issues.push(...prefixIssues(index, result.issues));\n final.value[index] = result.value;\n}\nconst $ZodArray = /*@__PURE__*/ $constructor(\"$ZodArray\", (inst, def) => {\n $ZodType.init(inst, def);\n const memo = globalConfig.memoizer;\n memo?.attach(inst);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!Array.isArray(input)) {\n payload.issues.push({\n expected: \"array\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n payload.value = memo ? memo.alloc(inst, payload, Array(input.length), ctx) : Array(input.length);\n const proms = [];\n const abortEarly = ctx?.abortEarly;\n for (let i = 0; i < input.length; i++) {\n const item = input[i];\n const result = def.element._zod.run({\n value: item,\n issues: []\n }, ctx);\n if (result instanceof Promise) proms.push(result.then((result) => handleArrayResult(result, payload, i)));\n else {\n handleArrayResult(result, payload, i);\n if (abortEarly && result.issues.length !== 0 && aborted(result)) break;\n }\n }\n if (proms.length) return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handlePropertyResult(result, final, key, input, optin, optout) {\n const isPresent = key in input;\n const isOptionalOut = optout === \"optional\";\n if (!isPresent && isOptionalOut && optin === \"optional\") return;\n if (result.issues.length) {\n if (optin !== void 0 && isOptionalOut && !isPresent) return;\n final.issues.push(...prefixIssues(key, result.issues));\n }\n if (!isPresent && optin === void 0) {\n if (!result.issues.length) final.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: void 0,\n path: [key]\n });\n return;\n }\n if (result.value === void 0) {\n if (isPresent || optin === \"defaulted\" && !isOptionalOut) final.value[key] = void 0;\n } else final.value[key] = result.value;\n}\nconst NO_SYMBOL_KEYS = [];\nfunction normalizeDef(def) {\n const keys = Object.keys(def.shape);\n const ownSymbols = Object.getOwnPropertySymbols(def.shape);\n const symbolKeys = ownSymbols.length ? ownSymbols : NO_SYMBOL_KEYS;\n const allKeys = symbolKeys.length ? [...keys, ...symbolKeys] : keys;\n for (const k of allKeys) if (!def.shape?.[k]?._zod?.traits?.has(\"$ZodType\")) throw new Error(`Invalid element at key \"${String(k)}\": expected a Zod schema`);\n const okeys = optionalKeys(def.shape);\n return {\n ...def,\n allKeys,\n symbolKeys,\n keySet: new Set(keys),\n numKeys: keys.length,\n optionalKeys: new Set(okeys)\n };\n}\nfunction handleCatchall(proms, input, payload, ctx, def, inst, abortEarly) {\n const unrecognized = [];\n const keySet = def.keySet;\n const _catchall = def.catchall._zod;\n const t = _catchall.def.type;\n const optin = _catchall.optin;\n const optout = _catchall.optout;\n let seen = 0;\n for (const key in input) {\n if (abortEarly && payload.issues.length !== seen) {\n if (aborted(payload, seen)) break;\n seen = payload.issues.length;\n }\n if (keySet.has(key)) continue;\n if (key === \"__proto__\") {\n if (t === \"never\") unrecognized.push(key);\n continue;\n }\n if (t === \"never\") {\n unrecognized.push(key);\n continue;\n }\n const r = _catchall.run({\n value: input[key],\n issues: []\n }, ctx);\n if (r instanceof Promise) proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, optin, optout)));\n else handlePropertyResult(r, payload, key, input, optin, optout);\n }\n if (unrecognized.length) payload.issues.push({\n code: \"unrecognized_keys\",\n keys: unrecognized,\n input,\n inst,\n continue: true\n });\n if (!proms.length) return payload;\n return Promise.all(proms).then(() => {\n return payload;\n });\n}\nconst $ZodObject = /*@__PURE__*/ $constructor(\"$ZodObject\", (inst, def) => {\n $ZodType.init(inst, def);\n const desc = Object.getOwnPropertyDescriptor(def, \"shape\");\n const sh = desc?.get ? desc.get.raw : def.shape ?? {};\n if (sh) {\n const get = () => {\n const newSh = { ...sh };\n Object.defineProperty(def, \"shape\", { value: newSh });\n get.raw = newSh;\n return newSh;\n };\n get.raw = sh;\n Object.defineProperty(def, \"shape\", { get });\n }\n const _normalized = cached(() => normalizeDef(def));\n defineLazyInternal(inst, \"propValues\", (zod) => {\n const shape = zod.def.shape;\n const propValues = {};\n for (const key in shape) {\n const field = shape[key]._zod;\n if (field.values) {\n if (!Object.prototype.hasOwnProperty.call(propValues, key)) assignProp(propValues, key, /* @__PURE__ */ new Set());\n for (const v of field.values) propValues[key].add(v);\n if (field.optin !== void 0) propValues[key].add(void 0);\n }\n }\n return propValues;\n });\n const isObject$2 = isObject;\n const catchall = def.catchall;\n let value;\n const memo = globalConfig.memoizer;\n memo?.attach(inst);\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject$2(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};\n const proms = [];\n const shape = value.shape;\n const abortEarly = ctx?.abortEarly;\n let seen = payload.issues.length;\n for (const key of value.allKeys) {\n if (abortEarly && payload.issues.length !== seen) {\n if (aborted(payload, seen)) break;\n seen = payload.issues.length;\n }\n if (key === \"__proto__\") continue;\n const el = shape[key];\n const optin = el._zod.optin;\n const optout = el._zod.optout;\n const r = el._zod.run({\n value: input[key],\n issues: []\n }, ctx);\n if (r instanceof Promise) proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, optin, optout)));\n else handlePropertyResult(r, payload, key, input, optin, optout);\n }\n if (!catchall) return proms.length ? Promise.all(proms).then(() => payload) : payload;\n return handleCatchall(proms, input, payload, ctx, _normalized.value, inst, abortEarly === true);\n };\n});\nconst $ZodObjectJIT = /*@__PURE__*/ $constructor(\"$ZodObjectJIT\", (inst, def) => {\n $ZodObject.init(inst, def);\n const superParse = inst._zod.parse;\n const _normalized = cached(() => normalizeDef(def));\n const memo = globalConfig.memoizer;\n const generateFastpass = (shape) => {\n const normalized = _normalized.value;\n const syms = normalized.symbolKeys;\n const doc = new Doc([\"payload\", \"ctx\"], {\n shape,\n inst,\n memo,\n syms\n });\n const parseStr = (k) => `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;\n const prefixStr = (id, k) => `\n let ${id}_ab = false;\n for (let i = 0; i < ${id}.issues.length; i++) {\n const iss = ${id}.issues[i];\n iss.path = iss.path ? [${k}, ...iss.path] : [${k}];\n payload.issues.push(iss);\n if (iss.continue !== true) ${id}_ab = true;\n }\n if (${id}_ab && ctx && ctx.abortEarly) {\n payload.value = newResult;\n return payload;\n }`;\n doc.write(`const input = payload.value;`);\n const ids = Object.create(null);\n let counter = 0;\n for (const key of normalized.allKeys) ids[key] = `key_${counter++}`;\n doc.write(memo ? `const newResult = memo.alloc(inst, payload, {}, ctx);` : `const newResult = {};`);\n for (const key of normalized.allKeys) {\n if (key === \"__proto__\") continue;\n const id = ids[key];\n const k = typeof key === \"symbol\" ? `syms[${syms.indexOf(key)}]` : esc(key);\n const isPresent = `${k} in input`;\n const schema = shape[key];\n const optin = schema?._zod?.optin;\n const isOptionalIn = optin !== void 0;\n const isOptionalOut = schema?._zod?.optout === \"optional\";\n doc.write(`const ${id} = ${parseStr(k)};`);\n if (isOptionalIn && isOptionalOut) {\n const assign = optin === \"optional\" ? `${id}_present` : `${id}.value !== undefined || ${id}_present`;\n doc.write(`\n const ${id}_present = ${isPresent};\n if (!${id}.issues.length || ${id}_present) {\n if (${id}.issues.length) {${prefixStr(id, k)}\n }\n\n if (${assign}) {\n newResult[${k}] = ${id}.value;\n }\n }\n\n `);\n } else if (!isOptionalIn) doc.write(`\n const ${id}_present = ${isPresent};\n if (${id}.issues.length) {${prefixStr(id, k)}\n }\n if (!${id}_present && !${id}.issues.length) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: undefined,\n path: [${k}]\n });\n if (ctx && ctx.abortEarly) {\n payload.value = newResult;\n return payload;\n }\n }\n\n if (${id}_present) {\n newResult[${k}] = ${id}.value;\n }\n\n `);\n else {\n doc.write(`\n if (${id}.issues.length) {${prefixStr(id, k)}\n }\n `);\n if (optin === \"defaulted\") doc.write(`newResult[${k}] = ${id}.value;`);\n else doc.write(`\n if (${id}.value !== undefined || ${isPresent}) {\n newResult[${k}] = ${id}.value;\n }\n `);\n }\n }\n doc.write(`payload.value = newResult;`);\n doc.write(`return payload;`);\n return doc.compile();\n };\n let fastpass;\n const isObject$1 = isObject;\n const jit = !globalConfig.jitless;\n const allowsEval$1 = allowsEval;\n const fastEnabled = jit && allowsEval$1.value;\n const catchall = def.catchall;\n let value;\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject$1(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n if (jit && fastEnabled && ctx?.async === false && ctx.jitless !== true) {\n if (!fastpass) fastpass = generateFastpass(def.shape);\n payload = fastpass(payload, ctx);\n if (!catchall) return payload;\n return handleCatchall([], input, payload, ctx, value, inst, ctx?.abortEarly === true);\n }\n return superParse(payload, ctx);\n };\n});\nfunction handleUnionResults(results, final, inst, ctx) {\n for (const result of results) if (result.issues.length === 0) {\n final.value = result.value;\n return final;\n }\n const nonaborted = results.filter((r) => !aborted(r));\n if (nonaborted.length === 1) {\n final.value = nonaborted[0].value;\n return nonaborted[0];\n }\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config())))\n });\n return final;\n}\nconst $ZodUnion = /*@__PURE__*/ $constructor(\"$ZodUnion\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.options.some((o) => o._zod.optin === \"defaulted\") ? \"defaulted\" : zod.def.options.some((o) => o._zod.optin !== void 0) ? \"optional\" : void 0);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.options.some((o) => o._zod.optout === \"optional\") ? \"optional\" : void 0);\n defineLazyInternal(inst, \"values\", (zod) => {\n if (zod.def.options.every((o) => o._zod.values)) return new Set(zod.def.options.flatMap((option) => Array.from(option._zod.values)));\n });\n defineLazyInternal(inst, \"pattern\", (zod) => {\n if (zod.def.options.every((o) => o._zod.pattern)) {\n const patterns = zod.def.options.map((o) => o._zod.pattern);\n return new RegExp(`^(${patterns.map((p) => cleanRegex(p.source)).join(\"|\")})$`);\n }\n });\n const first = def.options.length === 1 ? def.options[0]._zod.run : null;\n inst._zod.parse = (payload, ctx) => {\n if (first) return first(payload, ctx);\n let async = false;\n const results = [];\n for (const option of def.options) {\n const result = option._zod.run({\n value: payload.value,\n issues: []\n }, ctx);\n if (result instanceof Promise) {\n results.push(result);\n async = true;\n } else {\n if (result.issues.length === 0) return result;\n results.push(result);\n }\n }\n if (!async) return handleUnionResults(results, payload, inst, ctx);\n return Promise.all(results).then((results) => {\n return handleUnionResults(results, payload, inst, ctx);\n });\n };\n});\nconst $ZodIntersection = /*@__PURE__*/ $constructor(\"$ZodIntersection\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n const left = def.left._zod.run({\n value: input,\n issues: []\n }, ctx);\n const right = def.right._zod.run({\n value: input,\n issues: []\n }, ctx);\n if (left instanceof Promise || right instanceof Promise) return Promise.all([left, right]).then(([left, right]) => {\n return handleIntersectionResults(payload, left, right);\n });\n return handleIntersectionResults(payload, left, right);\n };\n});\nfunction mergeValues(a, b) {\n if (a === b) return {\n valid: true,\n data: a\n };\n if (a instanceof Date && b instanceof Date && +a === +b) return {\n valid: true,\n data: a\n };\n if (isPlainObject(a) && isPlainObject(b)) {\n const bKeys = Object.keys(b);\n const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);\n const newObj = {\n ...a,\n ...b\n };\n if (Object.prototype.hasOwnProperty.call(newObj, \"__proto__\")) delete newObj.__proto__;\n for (const key of sharedKeys) {\n if (key === \"__proto__\") continue;\n const sharedValue = mergeValues(a[key], b[key]);\n if (!sharedValue.valid) return {\n valid: false,\n mergeErrorPath: [key, ...sharedValue.mergeErrorPath]\n };\n newObj[key] = sharedValue.data;\n }\n return {\n valid: true,\n data: newObj\n };\n }\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return {\n valid: false,\n mergeErrorPath: []\n };\n const newArray = [];\n for (let index = 0; index < a.length; index++) {\n const itemA = a[index];\n const itemB = b[index];\n const sharedValue = mergeValues(itemA, itemB);\n if (!sharedValue.valid) return {\n valid: false,\n mergeErrorPath: [index, ...sharedValue.mergeErrorPath]\n };\n newArray.push(sharedValue.data);\n }\n return {\n valid: true,\n data: newArray\n };\n }\n return {\n valid: false,\n mergeErrorPath: []\n };\n}\nfunction handleIntersectionResults(result, left, right) {\n const unrecKeys = /* @__PURE__ */ new Map();\n let unrecIssue;\n const keyIssues = /* @__PURE__ */ new Map();\n const collect = (iss, side) => {\n let keys;\n if (iss.code === \"unrecognized_keys\" && !iss.path?.length) {\n unrecIssue ?? (unrecIssue = iss);\n keys = iss.keys;\n } else if (iss.code === \"invalid_key\" && iss.origin === \"record\" && iss.path?.length === 1) {\n const k = String(iss.path[0]);\n if (!keyIssues.has(k)) keyIssues.set(k, iss);\n keys = [k];\n } else return false;\n for (const k of keys) {\n if (!unrecKeys.has(k)) unrecKeys.set(k, {});\n unrecKeys.get(k)[side] = true;\n }\n return true;\n };\n for (const iss of left.issues) if (!collect(iss, \"l\")) result.issues.push(iss);\n for (const iss of right.issues) if (!collect(iss, \"r\")) result.issues.push(iss);\n const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);\n if (bothKeys.length) {\n const aggregated = unrecIssue ? bothKeys.filter((k) => unrecIssue.keys.includes(k)) : [];\n if (aggregated.length) result.issues.push({\n ...unrecIssue,\n keys: aggregated\n });\n for (const k of bothKeys) if (!aggregated.includes(k) && keyIssues.has(k)) result.issues.push(keyIssues.get(k));\n }\n const merged = mergeValues(left.value, right.value);\n if (!merged.valid) {\n if (aborted(result)) return result;\n throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(merged.mergeErrorPath)}`);\n }\n result.value = merged.data;\n return result;\n}\nconst $ZodRecord = /*@__PURE__*/ $constructor(\"$ZodRecord\", (inst, def) => {\n $ZodType.init(inst, def);\n const memo = globalConfig.memoizer;\n memo?.attach(inst);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!isPlainObject(input)) {\n payload.issues.push({\n expected: \"record\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n const proms = [];\n const values = def.keyType._zod.values;\n if (values && !def.partial) {\n payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};\n const recordKeys = /* @__PURE__ */ new Set();\n for (const key of values) if (typeof key === \"string\" || typeof key === \"number\" || typeof key === \"symbol\") {\n recordKeys.add(typeof key === \"number\" ? key.toString() : key);\n if (key === \"__proto__\") continue;\n const keyResult = def.keyType._zod.run({\n value: key,\n issues: []\n }, ctx);\n if (keyResult instanceof Promise) throw new Error(\"Async schemas not supported in object keys currently\");\n if (keyResult.issues.length) {\n payload.issues.push({\n code: \"invalid_key\",\n origin: \"record\",\n issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),\n input: key,\n path: [key],\n inst\n });\n continue;\n }\n const outKey = keyResult.value;\n if (outKey === \"__proto__\") continue;\n const result = def.valueType._zod.run({\n value: input[key],\n issues: []\n }, ctx);\n if (result instanceof Promise) proms.push(result.then((result) => {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }));\n else {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }\n }\n let unrecognized;\n for (const key in input) if (!recordKeys.has(key)) {\n if (def.mode === \"loose\") {\n if (key === \"__proto__\") continue;\n payload.value[key] = input[key];\n } else {\n unrecognized = unrecognized ?? [];\n unrecognized.push(key);\n }\n }\n if (unrecognized && unrecognized.length > 0) payload.issues.push({\n code: \"unrecognized_keys\",\n input,\n inst,\n keys: unrecognized,\n continue: true\n });\n } else {\n payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};\n let unrecognized;\n for (const key of Reflect.ownKeys(input)) {\n if (key === \"__proto__\") continue;\n if (!Object.prototype.propertyIsEnumerable.call(input, key)) continue;\n let keyResult = def.keyType._zod.run({\n value: key,\n issues: []\n }, ctx);\n if (keyResult instanceof Promise) throw new Error(\"Async schemas not supported in object keys currently\");\n if (typeof key === \"string\" && number.test(key) && keyResult.issues.length) {\n const retryResult = def.keyType._zod.run({\n value: Number(key),\n issues: []\n }, ctx);\n if (retryResult instanceof Promise) throw new Error(\"Async schemas not supported in object keys currently\");\n if (retryResult.issues.length === 0) keyResult = retryResult;\n }\n if (keyResult.issues.length) {\n if (def.mode === \"loose\") payload.value[key] = input[key];\n else if (values) {\n unrecognized = unrecognized ?? [];\n unrecognized.push(key);\n } else payload.issues.push({\n code: \"invalid_key\",\n origin: \"record\",\n issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),\n input: key,\n path: [key],\n inst\n });\n continue;\n }\n const outKey = keyResult.value;\n if (outKey === \"__proto__\") continue;\n const result = def.valueType._zod.run({\n value: input[key],\n issues: []\n }, ctx);\n if (result instanceof Promise) proms.push(result.then((result) => {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }));\n else {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }\n }\n if (unrecognized && unrecognized.length > 0) payload.issues.push({\n code: \"unrecognized_keys\",\n input,\n inst,\n keys: unrecognized,\n continue: true\n });\n }\n if (proms.length) return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nconst $ZodEnum = /*@__PURE__*/ $constructor(\"$ZodEnum\", (inst, def) => {\n $ZodType.init(inst, def);\n const values = getEnumValues(def.entries);\n const valuesSet = new Set(values);\n inst._zod.values = valuesSet;\n defineLazyInternal(inst, \"pattern\", (zod) => {\n const patternValues = getEnumValues(zod.def.entries).filter((k) => propertyKeyTypes.has(typeof k));\n return new RegExp(patternValues.length ? `^(${patternValues.map((o) => escapeRegex(o.toString())).join(\"|\")})$` : \"^[^\\\\s\\\\S]$\");\n });\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (valuesSet.has(input)) return payload;\n payload.issues.push({\n code: \"invalid_value\",\n values,\n input,\n inst\n });\n return payload;\n };\n});\nconst $ZodTransform = /*@__PURE__*/ $constructor(\"$ZodTransform\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"optional\";\n globalConfig.memoizer?.guard(inst);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") throw new $ZodEncodeError(inst.constructor.name);\n const _out = def.transform(payload.value, payload);\n if (ctx.async) return (_out instanceof Promise ? _out : Promise.resolve(_out)).then((output) => {\n payload.value = output;\n return payload;\n });\n if (_out instanceof Promise) throw new $ZodAsyncError();\n payload.value = _out;\n return payload;\n };\n});\nfunction handleOptionalResult(payload, result) {\n payload.value = result.issues.length ? void 0 : result.value;\n return payload;\n}\nconst $ZodOptional = /*@__PURE__*/ $constructor(\"$ZodOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType._zod.optin === \"defaulted\" ? \"defaulted\" : \"optional\");\n inst._zod.optout = \"optional\";\n defineLazyInternal(inst, \"values\", (zod) => {\n const values = zod.def.innerType._zod.values;\n return values ? /* @__PURE__ */ new Set([...values, void 0]) : void 0;\n });\n defineLazyInternal(inst, \"pattern\", (zod) => {\n const pattern = zod.def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${cleanRegex(pattern.source)})?$`) : void 0;\n });\n inst._zod.parse = (payload, ctx) => {\n if (payload.value === void 0) {\n if (def.innerType._zod.optin !== \"defaulted\") return payload;\n const result = def.innerType._zod.run({\n value: payload.value,\n issues: []\n }, ctx);\n if (result instanceof Promise) return result.then((result) => handleOptionalResult(payload, result));\n return handleOptionalResult(payload, result);\n }\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodExactOptional = /*@__PURE__*/ $constructor(\"$ZodExactOptional\", (inst, def) => {\n $ZodOptional.init(inst, def);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n defineLazyInternal(inst, \"pattern\", (zod) => zod.def.innerType._zod.pattern);\n inst._zod.parse = (payload, ctx) => {\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodNullable = /*@__PURE__*/ $constructor(\"$ZodNullable\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType._zod.optin);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.innerType._zod.optout);\n defineLazyInternal(inst, \"pattern\", (zod) => {\n const pattern = zod.def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${cleanRegex(pattern.source)}|null)$`) : void 0;\n });\n defineLazyInternal(inst, \"values\", (zod) => {\n return zod.def.innerType._zod.values ? /* @__PURE__ */ new Set([...zod.def.innerType._zod.values, null]) : void 0;\n });\n inst._zod.parse = (payload, ctx) => {\n if (payload.value === null) return payload;\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodDefault = /*@__PURE__*/ $constructor(\"$ZodDefault\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"defaulted\";\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n if (payload.value === void 0) {\n payload.value = def.defaultValue;\n /**\n * $ZodDefault returns the default value immediately in forward direction.\n * It doesn't pass the default value into the validator (\"prefault\"). There's no reason to pass the default value through validation. The validity of the default is enforced by TypeScript statically. Otherwise, it's the responsibility of the user to ensure the default is valid. In the case of pipes with divergent in/out types, you can specify the default on the `in` schema of your ZodPipe to set a \"prefault\" for the pipe. */\n return payload;\n }\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) return result.then((result) => handleDefaultResult(result, def));\n return handleDefaultResult(result, def);\n };\n});\nfunction handleDefaultResult(payload, def) {\n if (payload.value === void 0) payload.value = def.defaultValue;\n return payload;\n}\nconst $ZodPrefault = /*@__PURE__*/ $constructor(\"$ZodPrefault\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"defaulted\";\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n if (payload.value === void 0) payload.value = def.defaultValue;\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodNonOptional = /*@__PURE__*/ $constructor(\"$ZodNonOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"values\", (zod) => {\n const v = zod.def.innerType._zod.values;\n return v ? new Set([...v].filter((x) => x !== void 0)) : void 0;\n });\n inst._zod.parse = (payload, ctx) => {\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) return result.then((result) => handleNonOptionalResult(result, inst));\n return handleNonOptionalResult(result, inst);\n };\n});\nfunction handleNonOptionalResult(payload, inst) {\n if (!payload.issues.length && payload.value === void 0) payload.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: payload.value,\n inst\n });\n return payload;\n}\nfunction handleCatchResult(payload, result, def, ctx) {\n if (!result.issues.length) {\n payload.value = result.value;\n if (result.memo) payload.memo = true;\n return payload;\n }\n payload.value = def.catchValue({\n ...result,\n value: payload.value,\n error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },\n input: payload.value\n });\n return payload;\n}\nconst $ZodCatch = /*@__PURE__*/ $constructor(\"$ZodCatch\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType._zod.optin === \"defaulted\" ? \"defaulted\" : \"optional\");\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.innerType._zod.optout);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n const result = def.innerType._zod.run({\n value: payload.value,\n issues: []\n }, ctx);\n if (result instanceof Promise) return result.then((result) => handleCatchResult(payload, result, def, ctx));\n return handleCatchResult(payload, result, def, ctx);\n };\n});\nconst $ZodPipe = /*@__PURE__*/ $constructor(\"$ZodPipe\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.in._zod.values);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.in._zod.optin);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.out._zod.optout);\n defineLazyInternal(inst, \"propValues\", (zod) => zod.def.in._zod.propValues);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n const right = def.out._zod.run(payload, ctx);\n if (right instanceof Promise) return right.then((right) => handlePipeResult(right, def.in, ctx));\n return handlePipeResult(right, def.in, ctx);\n }\n const left = def.in._zod.run(payload, ctx);\n if (left instanceof Promise) return left.then((left) => handlePipeResult(left, def.out, ctx));\n return handlePipeResult(left, def.out, ctx);\n };\n});\nfunction handlePipeResult(left, next, ctx) {\n if (left.issues.some((iss) => iss.code !== \"unrecognized_keys\")) {\n left.aborted = true;\n return left;\n }\n return next._zod.run({\n value: left.value,\n issues: left.issues\n }, ctx);\n}\nconst $ZodReadonly = /*@__PURE__*/ $constructor(\"$ZodReadonly\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"propValues\", (zod) => zod.def.innerType._zod.propValues);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType?._zod?.optin);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.innerType?._zod?.optout);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) return result.then(handleReadonlyResult);\n return handleReadonlyResult(result);\n };\n});\nfunction handleReadonlyResult(payload) {\n if (!payload.memo) payload.value = Object.freeze(payload.value);\n return payload;\n}\nconst $ZodCustom = /*@__PURE__*/ $constructor(\"$ZodCustom\", (inst, def) => {\n $ZodCheck.init(inst, def);\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _) => {\n return payload;\n };\n inst._zod.check = (payload) => {\n const input = payload.value;\n const r = def.fn(input);\n if (r instanceof Promise) return r.then((r) => handleRefineResult(r, payload, input, inst));\n handleRefineResult(r, payload, input, inst);\n };\n});\nfunction handleRefineResult(result, payload, input, inst) {\n if (!result) {\n const _iss = {\n code: \"custom\",\n input,\n inst,\n path: [...inst._zod.def.path ?? []],\n continue: !inst._zod.def.abort\n };\n if (inst._zod.def.params) _iss.params = inst._zod.def.params;\n payload.issues.push(issue(_iss));\n }\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/memoizer.js\nvar $ZodCyclicError = class extends Error {\n constructor() {\n super(`Cannot parse a reference cycle that closes through a transform`);\n this.name = \"ZodCyclicError\";\n }\n};\n/** Keyed off the context object every schema in one parse call already shares. */\nconst STATE = \"~memo\";\nconst NO_ISSUES = [];\nfunction isRef(value) {\n return value !== null && typeof value === \"object\";\n}\nfunction cloneIssues(issues) {\n return issues.map((iss) => iss.path ? {\n ...iss,\n path: iss.path.slice()\n } : { ...iss });\n}\nconst recursive = /*@__PURE__*/ new WeakMap();\n/** What the walk established, in order of certainty: ordered so the strongest answer among children wins. */\nconst NONE = 0;\nconst ASSUMED = 1;\nconst PROVEN = 2;\n/** Whether this schema's subtree contains a cycle, so one parse can re-enter it. */\nfunction isRecursive(inst, stack, resolve) {\n const cached = recursive.get(inst);\n if (cached !== void 0) return cached ? PROVEN : NONE;\n if (stack.has(inst)) return PROVEN;\n stack.add(inst);\n let result = NONE;\n const check = (child) => {\n if (result !== PROVEN && child?._zod) {\n const answer = isRecursive(child, stack, resolve);\n if (answer > result) result = answer;\n }\n };\n const shape = (sh, spread) => {\n let answer = NONE;\n for (const key of Reflect.ownKeys(sh)) {\n const desc = Object.getOwnPropertyDescriptor(sh, key);\n if (spread && !desc.enumerable) continue;\n const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve) : NONE;\n if (child > answer) answer = child;\n }\n return answer;\n };\n const merge = (answer) => {\n if (answer > result) result = answer;\n };\n const def = inst._zod.def;\n switch (def.type) {\n case \"object\": {\n const raw = rawShape(def);\n merge(raw ? shape(raw, true) : ASSUMED);\n check(def.catchall);\n break;\n }\n case \"array\":\n check(def.element);\n break;\n case \"tuple\":\n for (const el of def.items) check(el);\n check(def.rest);\n break;\n case \"record\":\n case \"map\":\n check(def.keyType);\n check(def.valueType);\n break;\n case \"set\":\n check(def.valueType);\n break;\n case \"union\":\n for (const el of def.options) check(el);\n break;\n case \"intersection\":\n check(def.left);\n check(def.right);\n break;\n case \"optional\":\n case \"nullable\":\n case \"default\":\n case \"prefault\":\n case \"catch\":\n case \"readonly\":\n case \"nonoptional\":\n case \"promise\":\n case \"success\":\n check(def.innerType);\n break;\n case \"pipe\":\n check(def.in);\n check(def.out);\n break;\n case \"function\":\n check(def.input);\n check(def.output);\n break;\n case \"lazy\": {\n const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : void 0);\n merge(inner ? isRecursive(inner, stack, false) : ASSUMED);\n break;\n }\n case \"template_literal\":\n case \"string\":\n case \"number\":\n case \"int\":\n case \"boolean\":\n case \"bigint\":\n case \"symbol\":\n case \"undefined\":\n case \"null\":\n case \"void\":\n case \"never\":\n case \"any\":\n case \"unknown\":\n case \"date\":\n case \"nan\":\n case \"enum\":\n case \"literal\":\n case \"file\":\n case \"transform\":\n case \"custom\": break;\n default: for (const key in def) {\n const desc = Object.getOwnPropertyDescriptor(def, key);\n if (!desc || desc.get) continue;\n const value = desc.value;\n if (!value || typeof value !== \"object\") continue;\n if (value._zod) check(value);\n else if (Array.isArray(value)) for (const el of value) check(el);\n }\n }\n stack.delete(inst);\n return settle(inst, result);\n}\n/** An assumed answer must not outlive the resolution that settles it, so only a certain one is cached. */\nfunction settle(inst, answer) {\n if (answer !== ASSUMED) recursive.set(inst, answer === PROVEN);\n return answer;\n}\nfunction bucketFor(state, inst) {\n let bucket = state.buckets.get(inst);\n if (!bucket) {\n bucket = /* @__PURE__ */ new WeakMap();\n state.buckets.set(inst, bucket);\n }\n return bucket;\n}\nlet handoff;\nconst open = [];\nconst memo = {\n alloc(_inst, payload, empty) {\n const bucket = handoff;\n if (!bucket) return empty;\n handoff = void 0;\n const entry = {\n value: empty,\n issues: null\n };\n bucket.set(payload.value, entry);\n open.push(entry);\n return empty;\n },\n guard(inst) {\n var _a;\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred.push(() => {\n const base = inst._zod.parse;\n const wrapped = (payload, ctx) => {\n if (ctx.direction !== \"backward\" && isBackEdge(ctx, payload.value)) throw new $ZodCyclicError();\n return base(payload, ctx);\n };\n inst._zod.parse = wrapped;\n if (inst._zod.run === base) inst._zod.run = wrapped;\n });\n },\n attach(inst) {\n var _a;\n let isRecursiveInst;\n let rechecked = false;\n let lastCtx;\n let lastBucket;\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred.push(() => {\n const base = inst._zod.parse;\n const wrapped = (payload, ctx) => {\n if (isRecursiveInst === void 0) {\n const walked = isRecursive(inst, /* @__PURE__ */ new Set(), false);\n if (walked === NONE) {\n inst._zod.parse = base;\n if (inst._zod.run === wrapped) inst._zod.run = base;\n return base(payload, ctx);\n }\n if (walked === PROVEN || rechecked) isRecursiveInst = true;\n else rechecked = true;\n }\n const input = payload.value;\n if (!isRef(input)) return base(payload, ctx);\n let state = ctx[STATE];\n if (!state) {\n state = {\n buckets: /* @__PURE__ */ new WeakMap(),\n backEdges: void 0\n };\n ctx[STATE] = state;\n }\n let bucket;\n if (lastCtx === ctx) bucket = lastBucket;\n else {\n bucket = bucketFor(state, inst);\n lastCtx = ctx;\n lastBucket = bucket;\n }\n const hit = bucket.get(input);\n if (hit) {\n payload.value = hit.value;\n if (hit.issues) {\n if (hit.issues.length) payload.issues.push(...cloneIssues(hit.issues));\n } else {\n payload.memo = true;\n state.backEdges ?? (state.backEdges = /* @__PURE__ */ new WeakSet());\n state.backEdges.add(hit.value);\n }\n return payload;\n }\n handoff = bucket;\n const depth = open.length;\n const result = base(payload, ctx);\n handoff = void 0;\n const entry = open.length > depth ? open.pop() : void 0;\n if (result instanceof Promise) return result.then((r) => {\n if (entry) entry.issues = r.issues.length ? cloneIssues(r.issues) : NO_ISSUES;\n return r;\n });\n if (entry) entry.issues = result.issues.length ? cloneIssues(result.issues) : NO_ISSUES;\n return result;\n };\n inst._zod.parse = wrapped;\n if (inst._zod.run === base) inst._zod.run = wrapped;\n });\n }\n};\n/** The memoizer that gives containers cycle support. `zod` installs it by default; `zod/mini` opts in with `config({ memoizer: memoizer() })`. */\nfunction memoizer() {\n return memo;\n}\n/** Whether this value is a node a back-edge resolved to before it finished. */\nfunction isBackEdge(ctx, value) {\n const backEdges = ctx[STATE]?.backEdges;\n return backEdges !== void 0 && isRef(value) && backEdges.has(value);\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/en.js\nconst error = () => {\n const Sizable = {\n string: {\n unit: \"characters\",\n verb: \"to have\"\n },\n file: {\n unit: \"bytes\",\n verb: \"to have\"\n },\n array: {\n unit: \"items\",\n verb: \"to have\"\n },\n set: {\n unit: \"items\",\n verb: \"to have\"\n },\n map: {\n unit: \"entries\",\n verb: \"to have\"\n }\n };\n function getSizing(origin) {\n return Sizable[origin] ?? null;\n }\n const FormatDictionary = {\n regex: \"input\",\n email: \"email address\",\n url: \"URL\",\n emoji: \"emoji\",\n uuid: \"UUID\",\n uuidv4: \"UUIDv4\",\n uuidv6: \"UUIDv6\",\n nanoid: \"nanoid\",\n guid: \"GUID\",\n cuid: \"cuid\",\n cuid2: \"cuid2\",\n ulid: \"ULID\",\n xid: \"XID\",\n ksuid: \"KSUID\",\n datetime: \"ISO datetime\",\n date: \"ISO date\",\n time: \"ISO time\",\n duration: \"ISO duration\",\n ipv4: \"IPv4 address\",\n ipv6: \"IPv6 address\",\n mac: \"MAC address\",\n cidrv4: \"IPv4 range\",\n cidrv6: \"IPv6 range\",\n base64: \"base64-encoded string\",\n base64url: \"base64url-encoded string\",\n json_string: \"JSON string\",\n e164: \"E.164 number\",\n currency_code: \"currency code\",\n credit_card: \"credit card number\",\n iban: \"IBAN\",\n jwt: \"JWT\",\n template_literal: \"input\"\n };\n const TypeDictionary = { nan: \"NaN\" };\n function getTypeName(type, input) {\n if (type === \"number\" && typeof input === \"number\" && !Number.isFinite(input)) return String(input);\n return TypeDictionary[type] ?? type;\n }\n return (issue) => {\n switch (issue.code) {\n case \"invalid_type\": return `Invalid input: expected ${getTypeName(issue.expected)}, received ${getTypeName(parsedType(issue.input), issue.input)}`;\n case \"invalid_value\":\n if (issue.values.length === 1) return `Invalid input: expected ${stringifyPrimitive(issue.values[0])}`;\n return `Invalid option: expected one of ${joinValues(issue.values, \"|\")}`;\n case \"too_big\": {\n const adj = issue.exact ? \"exactly \" : issue.inclusive ? \"<=\" : \"<\";\n const sizing = getSizing(issue.origin);\n if (sizing) return `Too big: expected ${issue.origin ?? \"value\"} to have ${adj}${issue.maximum.toString()} ${sizing.unit ?? \"elements\"}`;\n return `Too big: expected ${issue.origin ?? \"value\"} to be ${adj}${issue.maximum.toString()}`;\n }\n case \"too_small\": {\n const adj = issue.exact ? \"exactly \" : issue.inclusive ? \">=\" : \">\";\n const sizing = getSizing(issue.origin);\n if (sizing) return `Too small: expected ${issue.origin} to have ${adj}${issue.minimum.toString()} ${sizing.unit}`;\n return `Too small: expected ${issue.origin} to be ${adj}${issue.minimum.toString()}`;\n }\n case \"invalid_format\": {\n const _issue = issue;\n if (_issue.format === \"starts_with\") return `Invalid string: must start with \"${_issue.prefix}\"`;\n if (_issue.format === \"ends_with\") return `Invalid string: must end with \"${_issue.suffix}\"`;\n if (_issue.format === \"includes\") return `Invalid string: must include \"${_issue.includes}\"`;\n if (_issue.format === \"regex\") return `Invalid string: must match pattern ${_issue.pattern}`;\n return `Invalid ${FormatDictionary[_issue.format] ?? issue.format}`;\n }\n case \"not_multiple_of\": return `Invalid number: must be a multiple of ${issue.divisor}`;\n case \"unrecognized_keys\": return `Unrecognized key${issue.keys.length > 1 ? \"s\" : \"\"}: ${joinValues(issue.keys, \", \")}`;\n case \"invalid_key\": return `Invalid key in ${issue.origin}`;\n case \"invalid_union\":\n if (issue.options && Array.isArray(issue.options) && issue.options.length > 0) return `Invalid discriminator value. Expected ${issue.options.map((o) => `'${o}'`).join(\" | \")}`;\n if (issue.inclusive === false) return \"Invalid input: more than one option matched\";\n return \"Invalid input\";\n case \"invalid_element\": return `Invalid value in ${issue.origin}`;\n default: return `Invalid input`;\n }\n };\n};\nfunction en_default() {\n return { localeError: error() };\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/registries.js\nvar _a;\nvar $ZodRegistry = class {\n constructor() {\n this._map = /* @__PURE__ */ new WeakMap();\n this._idmap = /* @__PURE__ */ new Map();\n }\n add(schema, ..._meta) {\n const meta = _meta[0];\n this._map.set(schema, meta);\n if (meta && typeof meta === \"object\" && \"id\" in meta) this._idmap.set(meta.id, schema);\n return this;\n }\n clear() {\n this._map = /* @__PURE__ */ new WeakMap();\n this._idmap = /* @__PURE__ */ new Map();\n return this;\n }\n remove(schema) {\n const meta = this._map.get(schema);\n if (meta && typeof meta === \"object\" && \"id\" in meta) this._idmap.delete(meta.id);\n this._map.delete(schema);\n return this;\n }\n get(schema) {\n const p = schema._zod.parent;\n if (p) {\n const pm = { ...this.get(p) ?? {} };\n delete pm.id;\n const f = {\n ...pm,\n ...this._map.get(schema)\n };\n return Object.keys(f).length ? f : void 0;\n }\n return this._map.get(schema);\n }\n has(schema) {\n return this._map.has(schema);\n }\n};\nfunction registry() {\n return new $ZodRegistry();\n}\n(_a = globalThis).__zod_globalRegistry ?? (_a.__zod_globalRegistry = registry());\nconst globalRegistry = globalThis.__zod_globalRegistry;\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/api.js\nfunction snapshotChecks(def) {\n if (def.checks) def.checks = [...def.checks];\n return def;\n}\n// @__NO_SIDE_EFFECTS__\nfunction _string(Class, params) {\n return new Class(snapshotChecks({\n type: \"string\",\n ...normalizeParams(params)\n }));\n}\n// @__NO_SIDE_EFFECTS__\nfunction _email(Class, params) {\n return new Class({\n type: \"string\",\n format: \"email\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _guid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"guid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuidv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v4\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuidv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v6\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuidv7(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v7\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"url\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _emoji(Class, params) {\n return new Class({\n type: \"string\",\n format: \"emoji\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _nanoid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"nanoid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link _cuid2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\n// @__NO_SIDE_EFFECTS__\nfunction _cuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _cuid2(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid2\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ulid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ulid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _xid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"xid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ksuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ksuid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ipv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv4\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ipv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv6\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _cidrv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv4\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _cidrv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv6\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _base64(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _base64url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64url\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _e164(Class, params) {\n return new Class({\n type: \"string\",\n format: \"e164\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _jwt(Class, params) {\n return new Class({\n type: \"string\",\n format: \"jwt\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoDateTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"datetime\",\n check: \"string_format\",\n offset: false,\n local: false,\n precision: null,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoDate(Class, params) {\n return new Class({\n type: \"string\",\n format: \"date\",\n check: \"string_format\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"time\",\n check: \"string_format\",\n precision: null,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoDuration(Class, params) {\n return new Class({\n type: \"string\",\n format: \"duration\",\n check: \"string_format\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _unknown(Class) {\n return new Class({ type: \"unknown\" });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _never(Class, params) {\n return new Class({\n type: \"never\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _maxLength(maximum, params) {\n return new $ZodCheckMaxLength({\n check: \"max_length\",\n ...normalizeParams(params),\n maximum\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _minLength(minimum, params) {\n return new $ZodCheckMinLength({\n check: \"min_length\",\n ...normalizeParams(params),\n minimum\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _length(length, params) {\n return new $ZodCheckLengthEquals({\n check: \"length_equals\",\n ...normalizeParams(params),\n length\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _regex(pattern, params) {\n return new $ZodCheckRegex({\n check: \"string_format\",\n format: \"regex\",\n ...normalizeParams(params),\n pattern\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _lowercase(params) {\n return new $ZodCheckLowerCase({\n check: \"string_format\",\n format: \"lowercase\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uppercase(params) {\n return new $ZodCheckUpperCase({\n check: \"string_format\",\n format: \"uppercase\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _includes(includes, params) {\n return new $ZodCheckIncludes({\n check: \"string_format\",\n format: \"includes\",\n ...normalizeParams(params),\n includes\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _startsWith(prefix, params) {\n return new $ZodCheckStartsWith({\n check: \"string_format\",\n format: \"starts_with\",\n ...normalizeParams(params),\n prefix\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _endsWith(suffix, params) {\n return new $ZodCheckEndsWith({\n check: \"string_format\",\n format: \"ends_with\",\n ...normalizeParams(params),\n suffix\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _overwrite(tx) {\n return new $ZodCheckOverwrite({\n check: \"overwrite\",\n tx\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _normalize(form) {\n return /* @__PURE__ */ _overwrite((input) => input.normalize(form));\n}\n// @__NO_SIDE_EFFECTS__\nfunction _trim() {\n return /* @__PURE__ */ _overwrite((input) => input.trim());\n}\n// @__NO_SIDE_EFFECTS__\nfunction _toLowerCase() {\n return /* @__PURE__ */ _overwrite((input) => input.toLowerCase());\n}\n// @__NO_SIDE_EFFECTS__\nfunction _toUpperCase() {\n return /* @__PURE__ */ _overwrite((input) => input.toUpperCase());\n}\n// @__NO_SIDE_EFFECTS__\nfunction _slugify() {\n return /* @__PURE__ */ _overwrite((input) => slugify(input));\n}\n// @__NO_SIDE_EFFECTS__\nfunction _array(Class, element, params) {\n return new Class({\n type: \"array\",\n element,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _refine(Class, fn, _params) {\n return new Class({\n type: \"custom\",\n check: \"custom\",\n fn,\n ...normalizeParams(_params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _superRefine(fn, params) {\n const ch = /* @__PURE__ */ _check((payload) => {\n payload.addIssue = (issue$2) => {\n if (typeof issue$2 === \"string\") payload.issues.push(issue(issue$2, payload.value, ch._zod.def));\n else {\n const _issue = issue$2;\n if (_issue.fatal) _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n if (!(\"input\" in _issue)) _issue.input = payload.value;\n _issue.inst ?? (_issue.inst = ch);\n _issue.continue ?? (_issue.continue = !ch._zod.def.abort);\n payload.issues.push(issue(_issue));\n }\n };\n return fn(payload.value, payload);\n }, params);\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nfunction _check(fn, params) {\n const ch = new $ZodCheck({\n check: \"custom\",\n ...normalizeParams(params)\n });\n ch._zod.check = fn;\n return ch;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/to-json-schema.js\nfunction assignProps(target, ...sources) {\n for (const source of sources) for (const key of Reflect.ownKeys(source)) if (Object.prototype.propertyIsEnumerable.call(source, key)) assignProp(target, key, source[key]);\n return target;\n}\nfunction initializeContext(params) {\n let target = params?.target ?? \"draft-2020-12\";\n if (target === \"draft-4\") target = \"draft-04\";\n if (target === \"draft-7\") target = \"draft-07\";\n return {\n processors: params.processors ?? {},\n metadataRegistry: params?.metadata ?? globalRegistry,\n target,\n unrepresentable: params?.unrepresentable ?? \"throw\",\n override: params?.override ?? (() => {}),\n io: params?.io ?? \"output\",\n counter: 0,\n seen: /* @__PURE__ */ new Map(),\n sharedDefsExtractedFor: void 0,\n sharedEmitDoneFor: void 0,\n cycles: params?.cycles ?? \"ref\",\n reused: params?.reused ?? \"inline\",\n intersections: [],\n deferred: [],\n external: params?.external ?? void 0\n };\n}\n/**\n* Applies the `unrepresentable` setting at a site that has no JSON Schema equivalent. Throws\n* `message` unless the setting (or the handler's return value) says otherwise. Returns `true` if a\n* custom JSON Schema was written into `json`, in which case the caller must not write its own.\n*/\nfunction handleUnrepresentable(schema, ctx, json, params, message) {\n const result = typeof ctx.unrepresentable === \"function\" ? ctx.unrepresentable({\n zodSchema: schema,\n path: params.path,\n message\n }) : ctx.unrepresentable;\n if (result === \"any\") return false;\n if (result === void 0 || result === \"throw\") throw new Error(message);\n Object.assign(json, result);\n return true;\n}\nfunction processSchema(schema, ctx, _params = {\n path: [],\n schemaPath: []\n}) {\n var _a;\n const def = schema._zod.def;\n const seen = ctx.seen.get(schema);\n if (seen) {\n seen.count++;\n if (_params.schemaPath.includes(schema)) seen.cycle = _params.path;\n return seen.schema;\n }\n const result = {\n schema: {},\n count: 1,\n cycle: void 0,\n path: _params.path\n };\n ctx.seen.set(schema, result);\n ctx.sharedDefsExtractedFor = void 0;\n ctx.sharedEmitDoneFor = void 0;\n const overrideSchema = schema._zod.toJSONSchema?.();\n if (overrideSchema) result.schema = overrideSchema;\n else {\n const params = {\n ..._params,\n schemaPath: [..._params.schemaPath, schema],\n path: _params.path\n };\n if (schema._zod.processJSONSchema) schema._zod.processJSONSchema(ctx, result.schema, params);\n else {\n const _json = result.schema;\n const processor = ctx.processors[def.type];\n if (!processor) throw new Error(`[toJSONSchema]: Non-representable type encountered: ${def.type}`);\n processor(schema, ctx, _json, params);\n }\n const parent = schema._zod.parent;\n if (parent) {\n if (!result.ref) result.ref = parent;\n processSchema(parent, ctx, params);\n ctx.seen.get(parent).isParent = true;\n }\n }\n const meta = ctx.metadataRegistry.get(schema);\n if (meta) assignProps(result.schema, meta);\n if (ctx.io === \"input\" && isTransforming(schema)) {\n delete result.schema.examples;\n delete result.schema.default;\n }\n if (ctx.io === \"input\" && \"_prefault\" in result.schema) (_a = result.schema).default ?? (_a.default = result.schema._prefault);\n delete result.schema._prefault;\n return ctx.seen.get(schema).schema;\n}\nfunction encodeJSONPointerSegment(segment) {\n return segment.replace(/~/g, \"~0\").replace(/\\//g, \"~1\");\n}\nfunction extractDefs(ctx, schema) {\n const root = ctx.seen.get(schema);\n if (!root) throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n if (ctx.external && ctx.sharedDefsExtractedFor === ctx.external) return;\n const idToSchema = /* @__PURE__ */ new Map();\n for (const entry of ctx.seen.entries()) {\n const id = ctx.metadataRegistry.get(entry[0])?.id;\n if (id) {\n const existing = idToSchema.get(id);\n if (existing && existing !== entry[0]) throw new Error(`Duplicate schema id \"${id}\" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);\n idToSchema.set(id, entry[0]);\n }\n }\n const makeURI = (entry) => {\n const defsSegment = ctx.target === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n if (ctx.external) {\n const externalId = ctx.external.registry.get(entry[0])?.id;\n const uriGenerator = ctx.external.uri ?? ((id) => id);\n if (externalId) return { ref: uriGenerator(externalId) };\n const id = entry[1].defId ?? entry[1].schema.id ?? `schema${ctx.counter++}`;\n entry[1].defId = id;\n return {\n defId: id,\n ref: `${uriGenerator(\"__shared\")}#/${defsSegment}/${encodeJSONPointerSegment(id)}`\n };\n }\n const uriPrefix = `#`;\n const defUriPrefix = `${uriPrefix}/${defsSegment}/`;\n if (entry[1] === root && !entry[1].schema.id) return { ref: uriPrefix };\n const defId = entry[1].schema.id ?? `__schema${ctx.counter++}`;\n return {\n defId,\n ref: defUriPrefix + encodeJSONPointerSegment(defId)\n };\n };\n const extractToDef = (entry) => {\n if (entry[1].schema.$ref) return;\n const seen = entry[1];\n const { ref, defId } = makeURI(entry);\n seen.def = { ...seen.schema };\n if (defId) seen.defId = defId;\n const schema = seen.schema;\n for (const key in schema) delete schema[key];\n schema.$ref = ref;\n };\n if (ctx.cycles === \"throw\") for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.cycle) throw new Error(`Cycle detected: #/${seen.cycle?.join(\"/\")}/<root>\n\nSet the \\`cycles\\` parameter to \\`\"ref\"\\` to resolve cyclical schemas with defs.`);\n }\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (schema === entry[0]) {\n extractToDef(entry);\n continue;\n }\n if (ctx.external) {\n const ext = ctx.external.registry.get(entry[0])?.id;\n if (schema !== entry[0] && ext) {\n extractToDef(entry);\n continue;\n }\n }\n if (ctx.metadataRegistry.get(entry[0])?.id) {\n extractToDef(entry);\n continue;\n }\n if (seen.cycle) {\n extractToDef(entry);\n continue;\n }\n if (seen.count > 1) {\n if (ctx.reused === \"ref\") extractToDef(entry);\n }\n }\n if (ctx.external) ctx.sharedDefsExtractedFor = ctx.external;\n}\n/** Rewrites `anyOf: [{type: \"a\"}, {type: \"b\"}]` to `type: [\"a\", \"b\"]`, which every JSON Schema draft treats as equivalent and most consumers render far better for the nullable case. Only branches that are a bare type assertion qualify — anything carrying a constraint, `$ref`, `const` or metadata is left alone. Runs after `flattenRef`, so a branch an override decorated or `$defs` extraction turned into a `$ref` is no longer bare and correctly stays in `anyOf`. `oneOf` is excluded: `integer` and `number` overlap, so \"exactly one\" and \"at least one\" are not the same there. OpenAPI 3.0 is excluded: its `type` must be a single string. */\nfunction compactTypeUnion(schema) {\n const options = schema.anyOf;\n if (!Array.isArray(options) || options.length === 0 || schema.type !== void 0) return;\n const types = [];\n for (const option of options) {\n if (!option || typeof option !== \"object\") return;\n compactTypeUnion(option);\n const keys = Object.keys(option);\n if (keys.length !== 1 || keys[0] !== \"type\") return;\n const type = option.type;\n for (const member of Array.isArray(type) ? type : [type]) {\n if (typeof member !== \"string\") return;\n if (!types.includes(member)) types.push(member);\n }\n }\n delete schema.anyOf;\n schema.type = types.length === 1 ? types[0] : types;\n}\n/** Keywords `foldIntersection` knows how to combine. Anything else — `$ref`, `patternProperties`,\n* an annotation like `description` — makes a member unfoldable, so a constraint this does not\n* understand leaves the `allOf` alone instead of being silently dropped or misattributed. */\nconst FOLDABLE_KEYS = /* @__PURE__ */ new Set([\n \"type\",\n \"properties\",\n \"required\",\n \"additionalProperties\"\n]);\nconst UNION_KEYS = [\"oneOf\", \"anyOf\"];\n/** A member's constraint on a key it does not declare itself. A `catchall` states one; `false`, an absent `additionalProperties`, and the empty schema a loose object emits state nothing. */\nfunction undeclaredConstraint(member) {\n const extra = member.additionalProperties;\n if (extra === void 0 || extra === false || typeof extra !== \"object\" || extra === null) return null;\n return Object.keys(extra).length ? extra : null;\n}\n/** Combines object members into the single object they describe together, or returns `null` if any of them carries a keyword outside {@link FOLDABLE_KEYS}. */\nfunction foldObjects(members) {\n const objects = [];\n for (const member of members) {\n if (typeof member !== \"object\" || member.type !== \"object\") return null;\n for (const key in member) if (!FOLDABLE_KEYS.has(key)) return null;\n objects.push(member);\n }\n const properties = {};\n const required = /* @__PURE__ */ new Set();\n for (const object of objects) {\n for (const key in object.properties) {\n if (Object.prototype.hasOwnProperty.call(properties, key)) continue;\n const parts = [];\n for (const other of objects) {\n const part = other.properties?.[key] ?? undeclaredConstraint(other);\n if (part === null || part === void 0) continue;\n if (!parts.some((seen) => JSON.stringify(seen) === JSON.stringify(part))) parts.push(part);\n }\n const merged = parts.length === 1 ? parts[0] : foldObjects(parts) ?? { allOf: parts };\n assignProp(properties, key, merged);\n }\n for (const key of object.required ?? []) required.add(key);\n }\n const folded = {\n type: \"object\",\n properties\n };\n if (required.size) folded.required = [...required];\n if (objects.every((object) => object.additionalProperties === false)) folded.additionalProperties = false;\n else {\n const constraints = [];\n for (const object of objects) {\n const constraint = undeclaredConstraint(object);\n if (constraint && !constraints.some((seen) => JSON.stringify(seen) === JSON.stringify(constraint))) constraints.push(constraint);\n }\n if (constraints.length === 1) folded.additionalProperties = constraints[0];\n else if (constraints.length > 1) folded.additionalProperties = { allOf: constraints };\n }\n return folded;\n}\n/** `additionalProperties` in an `allOf` member sees only that member's own `properties`, so two\n* closed object members reject each other's keys and the schema validates nothing. Zod's parser\n* pools the key sets instead — `handleIntersectionResults` reports a key as unrecognized only when\n* *every* side rejects it — so the emitted schema has to pool them too, and folding the members\n* into one object is the encoding that says so on every target.\n*\n* This runs from `finalize`, after `extractDefs`, which is what keeps it clear of the `$ref`\n* machinery: a member extracted into `$defs` is already a `$ref` by now and declines to fold, so it\n* keeps its reference and its own closedness rather than being inlined as a stale copy. */\nfunction foldIntersection(json) {\n const allOf = json.allOf;\n if (!Array.isArray(allOf) || allOf.length < 2) return;\n for (const key of FOLDABLE_KEYS) if (key in json) return;\n const unions = allOf.filter((m) => UNION_KEYS.some((k) => Array.isArray(m[k])));\n let folded = null;\n if (!unions.length) folded = foldObjects(allOf);\n else {\n const union = unions[0];\n const keyword = UNION_KEYS.find((k) => Array.isArray(union[k]));\n if (Object.keys(union).length !== 1) return;\n const rest = allOf.filter((m) => m !== union);\n const branches = union[keyword].map((branch) => foldObjects([...rest, branch]));\n if (branches.some((b) => !b)) return;\n folded = { [keyword]: branches };\n }\n if (!folded) return;\n delete json.allOf;\n assignProps(json, folded);\n}\nfunction finalize(ctx, schema) {\n const root = ctx.seen.get(schema);\n if (!root) throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n const flattenRef = (zodSchema) => {\n const seen = ctx.seen.get(zodSchema);\n if (seen.ref === null) return;\n const schema = seen.def ?? seen.schema;\n const _cached = { ...schema };\n const ref = seen.ref;\n seen.ref = null;\n if (ref) {\n flattenRef(ref);\n const refSeen = ctx.seen.get(ref);\n const refSchema = refSeen.schema;\n if (refSchema.$ref && (ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\")) {\n schema.allOf = schema.allOf ?? [];\n schema.allOf.push(refSchema);\n } else assignProps(schema, refSchema);\n assignProps(schema, _cached);\n if (zodSchema._zod.parent === ref) for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\") continue;\n if (!(key in _cached)) delete schema[key];\n }\n if (refSchema.$ref && refSeen.def) for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\") continue;\n if (key in refSeen.def && JSON.stringify(schema[key]) === JSON.stringify(refSeen.def[key])) delete schema[key];\n }\n }\n const parent = zodSchema._zod.parent;\n if (parent && parent !== ref) {\n flattenRef(parent);\n const parentSeen = ctx.seen.get(parent);\n if (parentSeen?.schema.$ref) {\n schema.$ref = parentSeen.schema.$ref;\n if (parentSeen.def) for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\") continue;\n if (key in parentSeen.def && JSON.stringify(schema[key]) === JSON.stringify(parentSeen.def[key])) delete schema[key];\n }\n }\n }\n ctx.override({\n zodSchema,\n jsonSchema: schema,\n path: seen.path ?? []\n });\n };\n if (!ctx.external || ctx.sharedEmitDoneFor !== ctx.external) {\n for (const entry of [...ctx.seen.entries()].reverse()) flattenRef(entry[0]);\n if (ctx.target !== \"openapi-3.0\") for (const entry of ctx.seen.entries()) compactTypeUnion(entry[1].def ?? entry[1].schema);\n for (const rewrite of ctx.deferred) rewrite();\n if (ctx.intersections.length) {\n const carriers = /* @__PURE__ */ new Map();\n for (const seen of ctx.seen.values()) for (const json of [seen.schema, seen.def]) {\n const allOf = json?.allOf;\n if (!Array.isArray(allOf)) continue;\n const existing = carriers.get(allOf);\n if (existing) existing.push(json);\n else carriers.set(allOf, [json]);\n }\n for (const allOf of ctx.intersections) for (const json of carriers.get(allOf) ?? []) foldIntersection(json);\n }\n }\n const result = {};\n if (ctx.target === \"draft-2020-12\") result.$schema = \"https://json-schema.org/draft/2020-12/schema\";\n else if (ctx.target === \"draft-07\") result.$schema = \"http://json-schema.org/draft-07/schema#\";\n else if (ctx.target === \"draft-04\") result.$schema = \"http://json-schema.org/draft-04/schema#\";\n else if (ctx.target === \"openapi-3.0\") {}\n if (ctx.external?.uri) {\n const id = ctx.external.registry.get(schema)?.id;\n if (!id) throw new Error(\"Schema is missing an `id` property\");\n result.$id = ctx.external.uri(id);\n }\n assignProps(result, root.defId ? root.schema : root.def ?? root.schema);\n const rootMetaId = ctx.metadataRegistry.get(schema)?.id;\n if (rootMetaId !== void 0 && result.id === rootMetaId) delete result.id;\n const defs = ctx.external?.defs ?? {};\n if (!ctx.external || ctx.sharedEmitDoneFor !== ctx.external) for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.def && seen.defId) {\n if (seen.def.id === seen.defId) delete seen.def.id;\n assignProp(defs, seen.defId, seen.def);\n }\n }\n if (ctx.external) ctx.sharedEmitDoneFor = ctx.external;\n if (ctx.external) {} else if (Object.keys(defs).length > 0) {\n if (ctx.target === \"draft-2020-12\") result.$defs = defs;\n else result.definitions = defs;\n }\n try {\n const finalized = JSON.parse(JSON.stringify(result));\n Object.defineProperty(finalized, \"~standard\", {\n value: {\n ...schema[\"~standard\"],\n jsonSchema: {\n input: createStandardJSONSchemaMethod(schema, \"input\", ctx.processors),\n output: createStandardJSONSchemaMethod(schema, \"output\", ctx.processors)\n }\n },\n enumerable: false,\n writable: false\n });\n return finalized;\n } catch (_err) {\n throw new Error(\"Error converting schema to JSON.\");\n }\n}\nfunction isTransforming(_schema, _ctx) {\n const ctx = _ctx ?? { seen: /* @__PURE__ */ new Set() };\n if (ctx.seen.has(_schema)) return false;\n ctx.seen.add(_schema);\n const def = _schema._zod.def;\n if (def.type === \"transform\") return true;\n if (def.type === \"array\") return isTransforming(def.element, ctx);\n if (def.type === \"set\") return isTransforming(def.valueType, ctx);\n if (def.type === \"lazy\") return isTransforming(def.getter(), ctx);\n if (def.type === \"promise\" || def.type === \"optional\" || def.type === \"nonoptional\" || def.type === \"nullable\" || def.type === \"readonly\" || def.type === \"default\" || def.type === \"prefault\" || def.type === \"catch\") return isTransforming(def.innerType, ctx);\n if (def.type === \"intersection\") return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);\n if (def.type === \"record\" || def.type === \"map\") return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);\n if (def.type === \"pipe\") {\n if (_schema._zod.traits.has(\"$ZodCodec\")) return true;\n return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);\n }\n if (def.type === \"object\") {\n for (const key in def.shape) if (isTransforming(def.shape[key], ctx)) return true;\n return false;\n }\n if (def.type === \"union\") {\n for (const option of def.options) if (isTransforming(option, ctx)) return true;\n return false;\n }\n if (def.type === \"tuple\") {\n for (const item of def.items) if (isTransforming(item, ctx)) return true;\n if (def.rest && isTransforming(def.rest, ctx)) return true;\n return false;\n }\n return false;\n}\n/**\n* Creates a toJSONSchema method for a schema instance.\n* This encapsulates the logic of initializing context, processing, extracting defs, and finalizing.\n*/\nconst createToJSONSchemaMethod = (schema, processors = {}) => (params) => {\n const ctx = initializeContext({\n ...params,\n processors\n });\n processSchema(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\nconst createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {\n const { libraryOptions, target } = params ?? {};\n const ctx = initializeContext({\n ...libraryOptions ?? {},\n target,\n io,\n processors\n });\n processSchema(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/json-schema-processors.js\nconst narrowMin = (agg, key, value) => {\n if (agg[key] === void 0 || value > agg[key]) agg[key] = value;\n};\nconst narrowMax = (agg, key, value) => {\n if (agg[key] === void 0 || value < agg[key]) agg[key] = value;\n};\nconst narrowBoth = (agg, value) => {\n narrowMin(agg, \"minimum\", value);\n narrowMax(agg, \"maximum\", value);\n};\nconst addDivisor = (agg, value) => {\n agg.multipleOf ?? (agg.multipleOf = []);\n if (!agg.multipleOf.includes(value)) agg.multipleOf.push(value);\n};\nconst addPattern = (agg, pattern) => {\n agg.patterns ?? (agg.patterns = /* @__PURE__ */ new Set());\n agg.patterns.add(pattern);\n};\nconst intersectMime = (agg, mime) => {\n agg.mime = agg.mime ? agg.mime.filter((m) => mime.includes(m)) : [...mime];\n};\nconst setFormat = (agg, format) => {\n agg.format = format;\n if (format.includes(\"int\")) agg.isInt = true;\n};\nconst minContributor = (agg, def) => narrowMin(agg, \"minimum\", def.minimum);\nconst maxContributor = (agg, def) => narrowMax(agg, \"maximum\", def.maximum);\nconst formatContributor = (ranges) => (agg, def) => {\n setFormat(agg, def.format);\n const [minimum, maximum] = ranges[def.format];\n narrowMin(agg, \"minimum\", minimum);\n narrowMax(agg, \"maximum\", maximum);\n};\nconst contributors = {\n greater_than: (agg, def) => narrowMin(agg, def.inclusive ? \"minimum\" : \"exclusiveMinimum\", def.value),\n less_than: (agg, def) => narrowMax(agg, def.inclusive ? \"maximum\" : \"exclusiveMaximum\", def.value),\n multiple_of: (agg, def) => addDivisor(agg, def.value),\n number_format: formatContributor(NUMBER_FORMAT_RANGES),\n bigint_format: formatContributor(BIGINT_FORMAT_RANGES),\n min_length: minContributor,\n max_length: maxContributor,\n length_equals: (agg, def) => narrowBoth(agg, def.length),\n min_size: minContributor,\n max_size: maxContributor,\n size_equals: (agg, def) => narrowBoth(agg, def.size),\n string_format: (agg, def) => {\n setFormat(agg, def.format);\n if (def.pattern) addPattern(agg, def.pattern);\n if (def.format === \"base64\" || def.format === \"base64url\") agg.contentEncoding = def.format;\n if (def.local || def.precision === -1) agg.laxFormat = true;\n },\n mime_type: (agg, def) => intersectMime(agg, def.mime)\n};\nfunction aggregateChecks(schema) {\n const agg = {};\n const def = schema._zod.def;\n const list = schema._zod.traits.has(\"$ZodCheck\") ? [schema, ...def.checks ?? []] : def.checks ?? [];\n for (const ch of list) contributors[ch._zod.def.check]?.(agg, ch._zod.def);\n const bag = schema._zod.bag;\n if (bag.minimum !== void 0) narrowMin(agg, \"minimum\", bag.minimum);\n if (bag.exclusiveMinimum !== void 0) narrowMin(agg, \"exclusiveMinimum\", bag.exclusiveMinimum);\n if (bag.maximum !== void 0) narrowMax(agg, \"maximum\", bag.maximum);\n if (bag.exclusiveMaximum !== void 0) narrowMax(agg, \"exclusiveMaximum\", bag.exclusiveMaximum);\n if (bag.multipleOf !== void 0) addDivisor(agg, bag.multipleOf);\n if (bag.format !== void 0) {\n agg.format ?? (agg.format = bag.format);\n if (bag.format.includes(\"int\")) agg.isInt = true;\n }\n if (bag.mime) intersectMime(agg, bag.mime);\n for (const pattern of bag.patterns ?? []) addPattern(agg, pattern);\n return agg;\n}\nconst formatMap = {\n guid: \"uuid\",\n url: \"uri\",\n datetime: \"date-time\",\n json_string: \"json-string\",\n regex: \"\"\n};\nconst exactPatterns = /* @__PURE__ */ new Map([[base64Charset, base64], [base64urlCharset, base64url]]);\nconst exactPattern = (p) => exactPatterns.get(p) ?? p;\nconst stringProcessor = (schema, ctx, _json, _params) => {\n const json = _json;\n json.type = \"string\";\n const { minimum, maximum, format, patterns, contentEncoding, laxFormat } = aggregateChecks(schema);\n if (typeof minimum === \"number\") json.minLength = minimum;\n if (typeof maximum === \"number\") json.maxLength = maximum;\n if (format) {\n json.format = formatMap[format] ?? format;\n if (json.format === \"\") delete json.format;\n if (format === \"time\" || laxFormat) delete json.format;\n }\n if (contentEncoding) json.contentEncoding = contentEncoding;\n if (patterns && patterns.size > 0) {\n const patternList = [...patterns].map(exactPattern);\n if (patternList.length === 1) json.pattern = patternList[0].source;\n else if (patternList.length > 1) json.allOf = [...patternList.map((regex) => ({\n ...ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\" ? { type: \"string\" } : {},\n pattern: regex.source\n }))];\n }\n};\nconst neverProcessor = (_schema, _ctx, json, _params) => {\n json.not = {};\n};\nconst unknownProcessor = (_schema, _ctx, _json, _params) => {};\nconst enumProcessor = (schema, _ctx, json, _params) => {\n const def = schema._zod.def;\n const values = getEnumValues(def.entries);\n if (values.length === 0) {\n json.not = {};\n return;\n }\n if (values.every((v) => typeof v === \"number\")) json.type = \"number\";\n if (values.every((v) => typeof v === \"string\")) json.type = \"string\";\n json.enum = values;\n};\nconst customProcessor = (schema, ctx, json, params) => {\n handleUnrepresentable(schema, ctx, json, params, \"Custom types cannot be represented in JSON Schema\");\n};\nconst transformProcessor = (schema, ctx, json, params) => {\n handleUnrepresentable(schema, ctx, json, params, \"Transforms cannot be represented in JSON Schema\");\n};\nconst arrayProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n const { minimum, maximum } = aggregateChecks(schema);\n if (typeof minimum === \"number\") json.minItems = minimum;\n if (typeof maximum === \"number\") json.maxItems = maximum;\n json.type = \"array\";\n json.items = processSchema(def.element, ctx, {\n ...params,\n path: [...params.path, \"items\"]\n });\n};\nfunction inputOptin(schema) {\n const def = schema._zod.def;\n if (def.type === \"pipe\" && def.in._zod.traits.has(\"$ZodTransform\")) return inputOptin(def.out);\n if (def.type === \"catch\") return inputOptin(def.innerType);\n return schema._zod.optin;\n}\nconst objectProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n const shape = def.shape;\n if (Object.getOwnPropertySymbols(shape).length && handleUnrepresentable(schema, ctx, json, params, \"Symbol keys cannot be represented in JSON Schema\")) return;\n json.type = \"object\";\n json.properties = {};\n for (const key in shape) assignProp(json.properties, key, processSchema(shape[key], ctx, {\n ...params,\n path: [\n ...params.path,\n \"properties\",\n key\n ]\n }));\n const requiredKeys = [];\n for (const key of Object.keys(shape)) {\n const field = def.shape[key];\n if (ctx.io === \"input\" ? inputOptin(field) === void 0 : field._zod.optout === void 0) requiredKeys.push(key);\n }\n if (requiredKeys.length > 0) json.required = requiredKeys;\n if (def.catchall?._zod.def.type === \"never\") json.additionalProperties = false;\n else if (!def.catchall) {\n if (ctx.io === \"output\") json.additionalProperties = false;\n } else if (def.catchall) json.additionalProperties = processSchema(def.catchall, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"]\n });\n};\nconst unionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const isExclusive = def.inclusive === false;\n const options = def.options.map((x, i) => processSchema(x, ctx, {\n ...params,\n path: [\n ...params.path,\n isExclusive ? \"oneOf\" : \"anyOf\",\n i\n ]\n }));\n if (isExclusive) json.oneOf = options;\n else json.anyOf = options;\n};\nconst intersectionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const a = processSchema(def.left, ctx, {\n ...params,\n path: [\n ...params.path,\n \"allOf\",\n 0\n ]\n });\n const b = processSchema(def.right, ctx, {\n ...params,\n path: [\n ...params.path,\n \"allOf\",\n 1\n ]\n });\n const isSimpleIntersection = (val) => \"allOf\" in val && Object.keys(val).length === 1;\n const allOf = [...isSimpleIntersection(a) ? a.allOf : [a], ...isSimpleIntersection(b) ? b.allOf : [b]];\n json.allOf = allOf;\n ctx.intersections.push(allOf);\n};\n/** JSON object keys are always strings, so a numeric record key schema is re-expressed over the\n* numeric-string form the record parser matches. Deferred to `finalize`, after the flatten: a key\n* behind a wrapper only carries its own `type` before then, and a union key only has its branches.\n*\n* A numeric bound cannot apply to a property name, so `minimum` and its siblings are dropped rather\n* than carried over: keeping them beside `type: \"string\"` reproduces the match-nothing schema this\n* exists to fix. A key that carries one therefore emits wider than the record parses — `z.record(z.number().min(5), V)`\n* accepts `\"3\"` — which is the deliberate trade, since throwing on it would reject an ordinary schema\n* outright. */\nfunction stringifyKeyNames(bySchema, json, visited) {\n if (json.$ref) {\n if (visited.has(json)) return json;\n visited.add(json);\n const def = bySchema.get(json)?.def;\n if (!def) return json;\n const inlined = stringifyKeyNames(bySchema, def, visited);\n return inlined === def ? json : inlined;\n }\n for (const keyword of [\"anyOf\", \"oneOf\"]) {\n const branches = json[keyword];\n if (!Array.isArray(branches)) continue;\n const mapped = branches.map((branch) => stringifyKeyNames(bySchema, branch, visited));\n if (mapped.some((branch, i) => branch !== branches[i])) json = {\n ...json,\n [keyword]: mapped\n };\n }\n const types = Array.isArray(json.type) ? json.type : [json.type];\n const numericType = !types.includes(\"string\") && types.some((t) => t === \"number\" || t === \"integer\");\n const values = json.enum ?? (json.const !== void 0 ? [json.const] : void 0);\n if (!numericType && !values?.some((v) => typeof v === \"number\")) return json;\n const { minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, format, id, ...rest } = json;\n if (rest.enum) rest.enum = rest.enum.map((v) => typeof v === \"number\" ? String(v) : v);\n else if (typeof rest.const === \"number\") rest.const = String(rest.const);\n if (!numericType) return rest;\n rest.type = \"string\";\n if (!values) rest.pattern = (types.includes(\"number\") ? number : integer).source;\n return rest;\n}\n/** Every record of one conversion, so the carriers are found in a single pass rather than once per record. */\nconst pendingRecords = /* @__PURE__ */ new WeakMap();\nfunction rewriteKeyNames(ctx) {\n const bySchema = /* @__PURE__ */ new Map();\n for (const entry of ctx.seen.values()) if (entry.def && !bySchema.has(entry.schema)) bySchema.set(entry.schema, entry);\n const rewrites = /* @__PURE__ */ new Map();\n for (const record of pendingRecords.get(ctx) ?? []) {\n const seen = ctx.seen.get(record);\n const names = (seen?.def ?? seen?.schema)?.propertyNames;\n if (!names || names === true || rewrites.has(names)) continue;\n const rewritten = stringifyKeyNames(bySchema, names, /* @__PURE__ */ new Set());\n if (rewritten !== names) rewrites.set(names, rewritten);\n }\n if (!rewrites.size) return;\n for (const entry of ctx.seen.values()) for (const carrier of [entry.schema, entry.def]) {\n const rewritten = carrier && rewrites.get(carrier.propertyNames);\n if (rewritten) carrier.propertyNames = rewritten;\n }\n}\nconst recordProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"object\";\n const keyType = def.keyType;\n const patterns = aggregateChecks(keyType).patterns;\n if (def.mode === \"loose\" && patterns && patterns.size > 0) {\n const valueSchema = processSchema(def.valueType, ctx, {\n ...params,\n path: [\n ...params.path,\n \"patternProperties\",\n \"*\"\n ]\n });\n json.patternProperties = {};\n for (const pattern of patterns) assignProp(json.patternProperties, exactPattern(pattern).source, valueSchema);\n } else {\n if (ctx.target === \"draft-07\" || ctx.target === \"draft-2020-12\") {\n json.propertyNames = processSchema(def.keyType, ctx, {\n ...params,\n path: [...params.path, \"propertyNames\"]\n });\n let pending = pendingRecords.get(ctx);\n if (!pending) {\n pending = [];\n pendingRecords.set(ctx, pending);\n ctx.deferred.push(() => rewriteKeyNames(ctx));\n }\n pending.push(schema);\n }\n json.additionalProperties = processSchema(def.valueType, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"]\n });\n }\n const keyValues = keyType._zod.values;\n const omittableOnInput = ctx.io === \"input\" && inputOptin(def.valueType) !== void 0;\n if (keyValues && !def.partial && !omittableOnInput) {\n const validKeyValues = [...keyValues].filter((v) => typeof v === \"string\" || typeof v === \"number\");\n if (validKeyValues.length > 0) json.required = validKeyValues.map(String);\n }\n};\nconst nullableProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const inner = processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n if (ctx.target === \"openapi-3.0\") {\n seen.ref = def.innerType;\n json.nullable = true;\n } else json.anyOf = [inner, { type: \"null\" }];\n};\nconst nonoptionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\n/** Round-trips a default value through JSON so the emitted schema is guaranteed to be valid JSON.\n* A BigInt has no reliable encoding, so it goes through `unrepresentable` like any other\n* unrepresentable value. Returns a sentinel when the caller must not write a default of its own. */\nconst UNREPRESENTABLE_DEFAULT = Symbol();\nfunction serializeDefaultValue(value, schema, ctx, json, params) {\n let unrepresentable = false;\n const serialized = JSON.stringify(value, (_, val) => {\n if (typeof val !== \"bigint\") return val;\n unrepresentable = true;\n return null;\n });\n if (!unrepresentable) return JSON.parse(serialized);\n handleUnrepresentable(schema, ctx, json, params, \"BigInt defaults cannot be represented in JSON Schema\");\n return UNREPRESENTABLE_DEFAULT;\n}\nconst defaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n const value = serializeDefaultValue(def.defaultValue, schema, ctx, json, params);\n if (value !== UNREPRESENTABLE_DEFAULT) json.default = value;\n};\nconst prefaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n if (ctx.io !== \"input\") return;\n const value = serializeDefaultValue(def.defaultValue, schema, ctx, json, params);\n if (value !== UNREPRESENTABLE_DEFAULT) json._prefault = value;\n};\nconst catchProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n let catchValue;\n try {\n catchValue = def.catchValue(void 0);\n } catch {\n handleUnrepresentable(schema, ctx, json, params, \"Dynamic catch values are not supported in JSON Schema\");\n return;\n }\n json.default = catchValue;\n};\nconst pipeProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n const inIsTransform = def.in._zod.traits.has(\"$ZodTransform\");\n const innerType = ctx.io === \"input\" ? inIsTransform ? def.out : def.in : def.out;\n processSchema(innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = innerType;\n};\nconst readonlyProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n json.readOnly = true;\n};\nconst optionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/errors.js\nconst _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);\nfunction _lazyMethod(proto, key, make) {\n Object.defineProperty(proto, key, {\n configurable: true,\n enumerable: false,\n get() {\n const value = make(this);\n Object.defineProperty(this, key, {\n value,\n configurable: true,\n writable: true\n });\n return value;\n },\n set(value) {\n Object.defineProperty(this, key, {\n value,\n configurable: true,\n writable: true\n });\n }\n });\n}\nconst initializer = (inst, issues) => {\n $ZodError.init(inst, issues);\n inst.name = \"ZodError\";\n const proto = Object.getPrototypeOf(inst);\n if (_installedErrorProtos.has(proto)) return;\n _installedErrorProtos.add(proto);\n _lazyMethod(proto, \"format\", (self) => (mapper) => formatError(self, mapper));\n _lazyMethod(proto, \"flatten\", (self) => (mapper) => flattenError(self, mapper));\n _lazyMethod(proto, \"addIssue\", (self) => (issue) => {\n self.issues.push(issue);\n self.message = JSON.stringify(self.issues, jsonStringifyReplacer, 2);\n });\n _lazyMethod(proto, \"addIssues\", (self) => (issues) => {\n self.issues.push(...issues);\n self.message = JSON.stringify(self.issues, jsonStringifyReplacer, 2);\n });\n Object.defineProperty(proto, \"isEmpty\", {\n configurable: true,\n enumerable: false,\n get() {\n return this.issues.length === 0;\n }\n });\n};\nconst ZodRealError = /*@__PURE__*/ $constructor(\"ZodError\", initializer, void 0, { Parent: Error });\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/parse.js\nconst parse = /* @__PURE__ */ _parse(ZodRealError);\nconst parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);\nconst safeParse = /* @__PURE__ */ _safeParse(ZodRealError);\nconst safeParseAsync = /* @__PURE__ */ _safeParseAsync(ZodRealError);\nconst encode = /* @__PURE__ */ _encode(ZodRealError);\nconst decode = /* @__PURE__ */ _decode(ZodRealError);\nconst encodeAsync = /* @__PURE__ */ _encodeAsync(ZodRealError);\nconst decodeAsync = /* @__PURE__ */ _decodeAsync(ZodRealError);\nconst safeEncode = /* @__PURE__ */ _safeEncode(ZodRealError);\nconst safeDecode = /* @__PURE__ */ _safeDecode(ZodRealError);\nconst safeEncodeAsync = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);\nconst safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/schemas.js\nfunction _ensureDefaultLocale() {\n if (!globalConfig.localeError) config(en_default());\n}\nfunction _ensureDefaultMemoizer() {\n if (!globalConfig.memoizer) config({ memoizer: memoizer() });\n}\nconst ZodType = /*@__PURE__*/ $constructor(\"ZodType\", (inst, def) => {\n _ensureDefaultLocale();\n $ZodType.init(inst, def);\n inst.def = def;\n inst.type = def.type;\n return inst;\n}, {\n check(...chks) {\n const def = this.def;\n return this.clone(mergeDefs(def, { checks: [...def.checks ?? [], ...chks.map((ch) => typeof ch === \"function\" ? { _zod: {\n check: ch,\n def: { check: \"custom\" },\n onattach: []\n } } : ch)] }), { parent: true });\n },\n with(...chks) {\n return this.check(...chks);\n },\n clone(def, params) {\n return clone(this, def, params);\n },\n brand() {\n return this;\n },\n register(reg, meta) {\n reg.add(this, meta);\n return this;\n },\n refine(check, params) {\n return this.check(refine(check, params));\n },\n superRefine(refinement, params) {\n return this.check(superRefine(refinement, params));\n },\n overwrite(fn) {\n return this.check(_overwrite(fn));\n },\n optional() {\n return optional(this);\n },\n exactOptional() {\n return exactOptional(this);\n },\n nullable() {\n return nullable(this);\n },\n nullish() {\n return optional(nullable(this));\n },\n nonoptional(params) {\n return nonoptional(this, params);\n },\n array() {\n return array(this);\n },\n or(arg) {\n return union([this, arg]);\n },\n and(arg) {\n return intersection(this, arg);\n },\n transform(tx) {\n return pipe(this, transform(tx));\n },\n default(d) {\n return _default(this, d);\n },\n prefault(d) {\n return prefault(this, d);\n },\n catch(params) {\n return _catch(this, params);\n },\n pipe(target) {\n return pipe(this, target);\n },\n readonly() {\n return readonly(this);\n },\n describe(description) {\n const cl = this.clone();\n globalRegistry.add(cl, { description });\n return cl;\n },\n meta(...args) {\n if (args.length === 0) return globalRegistry.get(this);\n const cl = this.clone();\n globalRegistry.add(cl, args[0]);\n return cl;\n },\n isOptional() {\n return this.safeParse(void 0).success;\n },\n isNullable() {\n return this.safeParse(null).success;\n },\n apply(fn, ...args) {\n return args.length === 0 ? fn(this) : fn(this, ...args);\n },\n get \"~standard\"() {\n return hide(this, \"~standard\", {\n ...standardProps(this),\n jsonSchema: {\n input: createStandardJSONSchemaMethod(this, \"input\"),\n output: createStandardJSONSchemaMethod(this, \"output\")\n }\n });\n },\n set \"~standard\"(value) {\n own(this, \"~standard\", value);\n },\n parse: function _parse(data, params) {\n return parse(this, data, params, { callee: _parse });\n },\n parseAsync: async function _parseAsync(data, params) {\n return await parseAsync(this, data, params, { callee: _parseAsync });\n },\n safeParse(data, params) {\n return safeParse(this, data, params);\n },\n async safeParseAsync(data, params) {\n return safeParseAsync(this, data, params);\n },\n get spa() {\n return this?.safeParseAsync;\n },\n set spa(value) {\n own(this, \"spa\", value);\n },\n validate(data, params) {\n return validate(this, data, params);\n },\n validateAsync(data, params) {\n return validateAsync$1(this, data, params);\n },\n encode: function _encode(data, params) {\n return encode(this, data, params, { callee: _encode });\n },\n decode: function _decode(data, params) {\n return decode(this, data, params, { callee: _decode });\n },\n encodeAsync: async function _encodeAsync(data, params) {\n return await encodeAsync(this, data, params, { callee: _encodeAsync });\n },\n decodeAsync: async function _decodeAsync(data, params) {\n return await decodeAsync(this, data, params, { callee: _decodeAsync });\n },\n safeEncode(data, params) {\n return safeEncode(this, data, params);\n },\n safeDecode(data, params) {\n return safeDecode(this, data, params);\n },\n async safeEncodeAsync(data, params) {\n return safeEncodeAsync(this, data, params);\n },\n async safeDecodeAsync(data, params) {\n return safeDecodeAsync(this, data, params);\n },\n toJSONSchema(params) {\n return createToJSONSchemaMethod(this, {})(params);\n },\n get description() {\n return globalRegistry.get(this)?.description;\n },\n get _def() {\n return this._zod.def;\n }\n});\n/** @internal */\nconst _ZodString = /*@__PURE__*/ $constructor(\"_ZodString\", (inst, def) => {\n $ZodString.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => stringProcessor(inst, ctx, json, params);\n}, /*@__PURE__*/ derived({\n format: (inst) => aggregateChecks(inst).format ?? null,\n minLength: (inst) => aggregateChecks(inst).minimum ?? null,\n maxLength: (inst) => aggregateChecks(inst).maximum ?? null\n}, {\n regex(...args) {\n return this.check(_regex(...args));\n },\n includes(...args) {\n return this.check(_includes(...args));\n },\n startsWith(...args) {\n return this.check(_startsWith(...args));\n },\n endsWith(...args) {\n return this.check(_endsWith(...args));\n },\n min(...args) {\n return this.check(_minLength(...args));\n },\n max(...args) {\n return this.check(_maxLength(...args));\n },\n length(...args) {\n return this.check(_length(...args));\n },\n nonempty(...args) {\n return this.check(_minLength(1, ...args));\n },\n lowercase(params) {\n return this.check(_lowercase(params));\n },\n uppercase(params) {\n return this.check(_uppercase(params));\n },\n trim() {\n return this.check(_trim());\n },\n normalize(...args) {\n return this.check(_normalize(...args));\n },\n toLowerCase() {\n return this.check(_toLowerCase());\n },\n toUpperCase() {\n return this.check(_toUpperCase());\n },\n slugify() {\n return this.check(_slugify());\n }\n}));\nconst ZodString = /*@__PURE__*/ $constructor(\"ZodString\", (inst, def) => {\n $ZodString.init(inst, def);\n _ZodString.init(inst, def);\n}, {\n email(params) {\n return this.check(_email(ZodEmail, params));\n },\n url(params) {\n return this.check(_url(ZodURL, params));\n },\n jwt(params) {\n return this.check(_jwt(ZodJWT, params));\n },\n emoji(params) {\n return this.check(_emoji(ZodEmoji, params));\n },\n guid(params) {\n return this.check(_guid(ZodGUID, params));\n },\n uuid(params) {\n return this.check(_uuid(ZodUUID, params));\n },\n uuidv4(params) {\n return this.check(_uuidv4(ZodUUID, params));\n },\n uuidv6(params) {\n return this.check(_uuidv6(ZodUUID, params));\n },\n uuidv7(params) {\n return this.check(_uuidv7(ZodUUID, params));\n },\n nanoid(params) {\n return this.check(_nanoid(ZodNanoID, params));\n },\n cuid(params) {\n return this.check(_cuid(ZodCUID, params));\n },\n cuid2(params) {\n return this.check(_cuid2(ZodCUID2, params));\n },\n ulid(params) {\n return this.check(_ulid(ZodULID, params));\n },\n base64(params) {\n return this.check(_base64(ZodBase64, params));\n },\n base64url(params) {\n return this.check(_base64url(ZodBase64URL, params));\n },\n xid(params) {\n return this.check(_xid(ZodXID, params));\n },\n ksuid(params) {\n return this.check(_ksuid(ZodKSUID, params));\n },\n ipv4(params) {\n return this.check(_ipv4(ZodIPv4, params));\n },\n ipv6(params) {\n return this.check(_ipv6(ZodIPv6, params));\n },\n cidrv4(params) {\n return this.check(_cidrv4(ZodCIDRv4, params));\n },\n cidrv6(params) {\n return this.check(_cidrv6(ZodCIDRv6, params));\n },\n e164(params) {\n return this.check(_e164(ZodE164, params));\n },\n datetime(params) {\n return this.check(_isoDateTime(ZodISODateTime, params));\n },\n date(params) {\n return this.check(_isoDate(ZodISODate, params));\n },\n time(params) {\n return this.check(_isoTime(ZodISOTime, params));\n },\n duration(params) {\n return this.check(_isoDuration(ZodISODuration, params));\n }\n});\nfunction string(params) {\n return _string(ZodString, params);\n}\nconst ZodStringFormat = /*@__PURE__*/ $constructor(\"ZodStringFormat\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n _ZodString.init(inst, def);\n});\nconst ZodISODateTime = /*@__PURE__*/ $constructor(\"ZodISODateTime\", (inst, def) => {\n $ZodISODateTime.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodISODate = /*@__PURE__*/ $constructor(\"ZodISODate\", (inst, def) => {\n $ZodISODate.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodISOTime = /*@__PURE__*/ $constructor(\"ZodISOTime\", (inst, def) => {\n $ZodISOTime.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodISODuration = /*@__PURE__*/ $constructor(\"ZodISODuration\", (inst, def) => {\n $ZodISODuration.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodEmail = /*@__PURE__*/ $constructor(\"ZodEmail\", (inst, def) => {\n $ZodEmail.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodGUID = /*@__PURE__*/ $constructor(\"ZodGUID\", (inst, def) => {\n $ZodGUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodUUID = /*@__PURE__*/ $constructor(\"ZodUUID\", (inst, def) => {\n $ZodUUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodURL = /*@__PURE__*/ $constructor(\"ZodURL\", (inst, def) => {\n $ZodURL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodEmoji = /*@__PURE__*/ $constructor(\"ZodEmoji\", (inst, def) => {\n $ZodEmoji.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodNanoID = /*@__PURE__*/ $constructor(\"ZodNanoID\", (inst, def) => {\n $ZodNanoID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link ZodCUID2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\nconst ZodCUID = /*@__PURE__*/ $constructor(\"ZodCUID\", (inst, def) => {\n $ZodCUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodCUID2 = /*@__PURE__*/ $constructor(\"ZodCUID2\", (inst, def) => {\n $ZodCUID2.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodULID = /*@__PURE__*/ $constructor(\"ZodULID\", (inst, def) => {\n $ZodULID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodXID = /*@__PURE__*/ $constructor(\"ZodXID\", (inst, def) => {\n $ZodXID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodKSUID = /*@__PURE__*/ $constructor(\"ZodKSUID\", (inst, def) => {\n $ZodKSUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodIPv4 = /*@__PURE__*/ $constructor(\"ZodIPv4\", (inst, def) => {\n $ZodIPv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodIPv6 = /*@__PURE__*/ $constructor(\"ZodIPv6\", (inst, def) => {\n $ZodIPv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodCIDRv4 = /*@__PURE__*/ $constructor(\"ZodCIDRv4\", (inst, def) => {\n $ZodCIDRv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodCIDRv6 = /*@__PURE__*/ $constructor(\"ZodCIDRv6\", (inst, def) => {\n $ZodCIDRv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodBase64 = /*@__PURE__*/ $constructor(\"ZodBase64\", (inst, def) => {\n $ZodBase64.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodBase64URL = /*@__PURE__*/ $constructor(\"ZodBase64URL\", (inst, def) => {\n $ZodBase64URL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodE164 = /*@__PURE__*/ $constructor(\"ZodE164\", (inst, def) => {\n $ZodE164.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodJWT = /*@__PURE__*/ $constructor(\"ZodJWT\", (inst, def) => {\n $ZodJWT.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodUnknown = /*@__PURE__*/ $constructor(\"ZodUnknown\", (inst, def) => {\n $ZodUnknown.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => unknownProcessor(inst, ctx, json, params);\n});\nfunction unknown() {\n return _unknown(ZodUnknown);\n}\nconst ZodNever = /*@__PURE__*/ $constructor(\"ZodNever\", (inst, def) => {\n $ZodNever.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => neverProcessor(inst, ctx, json, params);\n});\nfunction never(params) {\n return _never(ZodNever, params);\n}\nconst ZodArray = /*@__PURE__*/ $constructor(\"ZodArray\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodArray.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => arrayProcessor(inst, ctx, json, params);\n inst.element = def.element;\n}, {\n min(n, params) {\n return this.check(_minLength(n, params));\n },\n nonempty(params) {\n return this.check(_minLength(1, params));\n },\n max(n, params) {\n return this.check(_maxLength(n, params));\n },\n length(n, params) {\n return this.check(_length(n, params));\n },\n unwrap() {\n return this.element;\n }\n});\nfunction array(element, params) {\n return _array(ZodArray, element, params);\n}\nconst ZodObject = /*@__PURE__*/ $constructor(\"ZodObject\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodObjectJIT.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => objectProcessor(inst, ctx, json, params);\n installLazyProp(inst, \"shape\", (self) => self._zod.def.shape, false);\n}, {\n keyof() {\n return _enum(Object.keys(this._zod.def.shape));\n },\n catchall(catchall) {\n return this.clone(mergeDefs(this._zod.def, { catchall }));\n },\n passthrough() {\n return this.clone(mergeDefs(this._zod.def, { catchall: unknown() }));\n },\n loose() {\n return this.clone(mergeDefs(this._zod.def, { catchall: unknown() }));\n },\n strict() {\n return this.clone(mergeDefs(this._zod.def, { catchall: never() }));\n },\n strip() {\n return this.clone(mergeDefs(this._zod.def, { catchall: void 0 }));\n },\n extend(incoming) {\n return extend(this, incoming);\n },\n safeExtend(incoming) {\n return safeExtend(this, incoming);\n },\n merge(other) {\n return merge(this, other);\n },\n pick(mask) {\n return pick(this, mask);\n },\n omit(mask) {\n return omit(this, mask);\n },\n partial(...args) {\n return partial(ZodOptional, this, args[0]);\n },\n exactPartial(...args) {\n return partial(ZodExactOptional, this, args[0], \"exactPartial\");\n },\n required(...args) {\n return required(ZodNonOptional, this, args[0]);\n }\n});\nfunction object(shape, params) {\n const def = {\n type: \"object\",\n shape: shape ?? {},\n ...normalizeParams(params)\n };\n return new ZodObject(def);\n}\nconst ZodUnion = /*@__PURE__*/ $constructor(\"ZodUnion\", (inst, def) => {\n $ZodUnion.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => unionProcessor(inst, ctx, json, params);\n inst.options = def.options;\n});\nfunction union(options, params) {\n return new ZodUnion({\n type: \"union\",\n options,\n ...normalizeParams(params)\n });\n}\nconst ZodIntersection = /*@__PURE__*/ $constructor(\"ZodIntersection\", (inst, def) => {\n $ZodIntersection.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => intersectionProcessor(inst, ctx, json, params);\n});\nfunction intersection(left, right) {\n return new ZodIntersection({\n type: \"intersection\",\n left,\n right\n });\n}\nconst ZodRecord = /*@__PURE__*/ $constructor(\"ZodRecord\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodRecord.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => recordProcessor(inst, ctx, json, params);\n inst.keyType = def.keyType;\n inst.valueType = def.valueType;\n});\nfunction record(keyType, valueType, params) {\n if (!valueType || !valueType._zod) return new ZodRecord({\n type: \"record\",\n keyType: string(),\n valueType: keyType,\n ...normalizeParams(valueType)\n });\n return new ZodRecord({\n type: \"record\",\n keyType,\n valueType,\n ...normalizeParams(params)\n });\n}\nconst ZodEnum = /*@__PURE__*/ $constructor(\"ZodEnum\", (inst, def) => {\n $ZodEnum.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => enumProcessor(inst, ctx, json, params);\n inst.enum = def.entries;\n inst.options = [...inst._zod.values];\n const keys = new Set(Object.keys(def.entries));\n inst.extract = (values, params) => {\n const newEntries = {};\n for (const value of values) if (keys.has(value)) newEntries[value] = def.entries[value];\n else throw new Error(`Key ${value} not found in enum`);\n return new ZodEnum({\n ...def,\n checks: [],\n ...normalizeParams(params),\n entries: newEntries\n });\n };\n inst.exclude = (values, params) => {\n const newEntries = { ...def.entries };\n for (const value of values) if (keys.has(value)) delete newEntries[value];\n else throw new Error(`Key ${value} not found in enum`);\n return new ZodEnum({\n ...def,\n checks: [],\n ...normalizeParams(params),\n entries: newEntries\n });\n };\n});\nfunction _enum(values, params) {\n const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;\n return new ZodEnum({\n type: \"enum\",\n entries,\n ...normalizeParams(params)\n });\n}\nconst ZodTransform = /*@__PURE__*/ $constructor(\"ZodTransform\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodTransform.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => transformProcessor(inst, ctx, json, params);\n inst._zod.parse = (payload, _ctx) => {\n if (_ctx.direction === \"backward\") throw new $ZodEncodeError(inst.constructor.name);\n payload.addIssue = (issue$1) => {\n if (typeof issue$1 === \"string\") payload.issues.push(issue(issue$1, payload.value, def));\n else {\n const _issue = issue$1;\n if (_issue.fatal) _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n if (!(\"input\" in _issue)) _issue.input = payload.value;\n _issue.inst ?? (_issue.inst = inst);\n payload.issues.push(issue(_issue));\n }\n };\n const output = def.transform(payload.value, payload);\n if (output instanceof Promise) return output.then((output) => {\n payload.value = output;\n return payload;\n });\n payload.value = output;\n return payload;\n };\n});\nfunction transform(fn) {\n return new ZodTransform({\n type: \"transform\",\n transform: fn\n });\n}\nconst ZodOptional = /*@__PURE__*/ $constructor(\"ZodOptional\", (inst, def) => {\n $ZodOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction optional(innerType) {\n return new ZodOptional({\n type: \"optional\",\n innerType\n });\n}\nconst ZodExactOptional = /*@__PURE__*/ $constructor(\"ZodExactOptional\", (inst, def) => {\n $ZodExactOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction exactOptional(innerType) {\n return new ZodExactOptional({\n type: \"optional\",\n innerType\n });\n}\nconst ZodNullable = /*@__PURE__*/ $constructor(\"ZodNullable\", (inst, def) => {\n $ZodNullable.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => nullableProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction nullable(innerType) {\n return new ZodNullable({\n type: \"nullable\",\n innerType\n });\n}\nconst ZodDefault = /*@__PURE__*/ $constructor(\"ZodDefault\", (inst, def) => {\n $ZodDefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => defaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeDefault = inst.unwrap;\n});\nfunction _default(innerType, defaultValue) {\n return new ZodDefault({\n type: \"default\",\n innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : shallowClone(defaultValue);\n }\n });\n}\nconst ZodPrefault = /*@__PURE__*/ $constructor(\"ZodPrefault\", (inst, def) => {\n $ZodPrefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => prefaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction prefault(innerType, defaultValue) {\n return new ZodPrefault({\n type: \"prefault\",\n innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : shallowClone(defaultValue);\n }\n });\n}\nconst ZodNonOptional = /*@__PURE__*/ $constructor(\"ZodNonOptional\", (inst, def) => {\n $ZodNonOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => nonoptionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction nonoptional(innerType, params) {\n return new ZodNonOptional({\n type: \"nonoptional\",\n innerType,\n ...normalizeParams(params)\n });\n}\nconst ZodCatch = /*@__PURE__*/ $constructor(\"ZodCatch\", (inst, def) => {\n $ZodCatch.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => catchProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeCatch = inst.unwrap;\n});\nfunction _catch(innerType, catchValue) {\n return new ZodCatch({\n type: \"catch\",\n innerType,\n catchValue: typeof catchValue === \"function\" ? catchValue : constantCatch(catchValue)\n });\n}\nconst ZodPipe = /*@__PURE__*/ $constructor(\"ZodPipe\", (inst, def) => {\n $ZodPipe.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => pipeProcessor(inst, ctx, json, params);\n inst.in = def.in;\n inst.out = def.out;\n});\nfunction pipe(in_, out) {\n return new ZodPipe({\n type: \"pipe\",\n in: in_,\n out\n });\n}\nconst ZodReadonly = /*@__PURE__*/ $constructor(\"ZodReadonly\", (inst, def) => {\n $ZodReadonly.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => readonlyProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction readonly(innerType) {\n return new ZodReadonly({\n type: \"readonly\",\n innerType\n });\n}\nconst ZodCustom = /*@__PURE__*/ $constructor(\"ZodCustom\", (inst, def) => {\n $ZodCustom.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);\n});\nfunction refine(fn, _params = {}) {\n return _refine(ZodCustom, fn, _params);\n}\nfunction superRefine(fn, params) {\n return _superRefine(fn, params);\n}\n\n//#endregion\n//#region src/lib/hook-io.ts\nconst HookInputSchema = object({\n session_id: string(),\n transcript_path: string(),\n cwd: string(),\n permission_mode: string(),\n hook_event_name: string(),\n tool_name: string(),\n tool_input: record(string(), unknown())\n});\n/**\n* Read and parse the full stdin as JSON.\n* Returns a result with the validated HookInput, or the raw data and error on failure.\n*/\nasync function readHookInput() {\n const chunks = [];\n for await (const chunk of process.stdin) chunks.push(typeof chunk === \"string\" ? chunk : String(chunk));\n let parsed;\n try {\n parsed = JSON.parse(chunks.join(\"\"));\n } catch (_error) {\n return {\n ok: false,\n raw: chunks.join(\"\").slice(0, 200),\n error: \"invalid JSON\"\n };\n }\n const result = HookInputSchema.safeParse(parsed);\n if (!result.success) return {\n ok: false,\n raw: parsed,\n error: result.error.message\n };\n return {\n ok: true,\n data: result.data\n };\n}\nconst BashInputSchema = object({\n command: string(),\n description: optional(string())\n});\nconst FilePathInputSchema = object({ file_path: string() });\nconst SearchInputSchema = object({\n pattern: string(),\n path: optional(string())\n});\n/** Parse tool_input as Bash input. Returns null if invalid. */\nfunction parseBashInput(toolInput) {\n const result = BashInputSchema.safeParse(toolInput);\n return result.success ? result.data : null;\n}\n/** Parse tool_input as file-path input (Read, Write, Edit). Returns null if invalid. */\nfunction parseFilePathInput(toolInput) {\n const result = FilePathInputSchema.safeParse(toolInput);\n return result.success ? result.data : null;\n}\n/** Parse tool_input as search input (Glob, Grep). Returns null if invalid. */\nfunction parseSearchInput(toolInput) {\n const result = SearchInputSchema.safeParse(toolInput);\n return result.success ? result.data : null;\n}\n/**\n* Write a deny decision to stdout as JSON, with `reason` emitted as\n* `permissionDecisionReason` when present.\n*/\nfunction writeDeny(reason) {\n const decision = { hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n ...reason ? { permissionDecisionReason: reason } : {}\n } };\n process.stdout.write(JSON.stringify(decision));\n}\n/**\n* Write a non-blocking advisory to stdout as `additionalContext`, with no\n* `permissionDecision` at all. Claude Code still shows a PreToolUse hook's\n* `additionalContext` to the model when the output carries no permission\n* decision (checked by hand against a live session), so a `none` result can\n* still carry advice.\n*/\nfunction writeAdvice(advice) {\n const decision = { hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n additionalContext: advice\n } };\n process.stdout.write(JSON.stringify(decision));\n}\n\n//#endregion\n//#region src/lib/audit.ts\nconst MAX_LOG_SIZE = 1048576;\n/**\n* Resolve the audit log path relative to the hook's own location.\n* Co-located at `.claude/hooks/bst-hook.audit.local.jsonl`.\n*/\nfunction getLogPath() {\n const hookDir = path.dirname(fileURLToPath(import.meta.url));\n return path.join(hookDir, \"bst-hook.audit.local.jsonl\");\n}\n/**\n* Rotate the log file if it exceeds MAX_LOG_SIZE.\n* Renames current log to `.1` and starts fresh.\n* Safe against concurrent rotation -- ENOENT on rename is expected and ignored.\n*/\nfunction rotateIfNeeded(logPath) {\n try {\n if (fs.statSync(logPath).size > MAX_LOG_SIZE) {\n const rotatedPath = `${logPath}.1`;\n fs.renameSync(logPath, rotatedPath);\n }\n } catch (_error) {}\n}\n/** Whether rotation has been checked this session */\nlet rotationChecked = false;\n/**\n* Append an audit entry to the JSONL log.\n* Never throws -- audit failures log to stderr and continue.\n*/\nfunction audit(entry) {\n try {\n const logPath = getLogPath();\n if (!rotationChecked) {\n rotateIfNeeded(logPath);\n rotationChecked = true;\n }\n const record = {\n timestamp: (/* @__PURE__ */ new Date()).toISOString(),\n ...entry\n };\n fs.appendFileSync(logPath, JSON.stringify(record) + \"\\n\");\n } catch (err) {\n process.stderr.write(`[bst-hook] audit write failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n}\n\n//#endregion\n//#region src/lib/shell-tokenizer.ts\n/**\n* Scan a double-quoted string starting at `start` (the char after the opening\n* quote). Returns the unescaped content and the index just past the closing\n* quote, or `null` if the quote is never closed.\n*/\nfunction scanDoubleQuoted(input, start) {\n let content = \"\";\n let i = start;\n while (i < input.length && input[i] !== \"\\\"\") {\n if (input[i] === \"\\\\\" && i + 1 < input.length) {\n const next = input[i + 1];\n if (next === \"\\\"\" || next === \"\\\\\" || next === \"$\" || next === \"`\") {\n content += next;\n i += 2;\n continue;\n }\n }\n content += input[i];\n i++;\n }\n if (i >= input.length) return null;\n return {\n content,\n next: i + 1\n };\n}\nfunction tokenize(input) {\n const tokens = [];\n let current = \"\";\n let i = 0;\n let inToken = false;\n while (i < input.length) {\n const ch = input[i];\n if (ch === \"'\") {\n inToken = true;\n i++;\n while (i < input.length && input[i] !== \"'\") {\n current += input[i];\n i++;\n }\n if (i >= input.length) return {\n ok: false,\n error: \"unterminated single quote\"\n };\n i++;\n } else if (ch === \"\\\"\") {\n inToken = true;\n const scanned = scanDoubleQuoted(input, i + 1);\n if (!scanned) return {\n ok: false,\n error: \"unterminated double quote\"\n };\n current += scanned.content;\n i = scanned.next;\n } else if (ch === \"\\\\\" && i + 1 < input.length) {\n inToken = true;\n current += input[i + 1];\n i += 2;\n } else if (ch === \" \" || ch === \" \") {\n if (inToken) {\n tokens.push(current);\n current = \"\";\n inToken = false;\n }\n i++;\n } else {\n inToken = true;\n current += ch;\n i++;\n }\n }\n if (inToken) tokens.push(current);\n return {\n ok: true,\n tokens\n };\n}\n/**\n* Characters safe to leave unquoted when re-emitting a token. Anything outside\n* this set could be interpreted by the shell-splitter or tokenizer on a\n* subsequent pass (operators, whitespace, quotes, globs, vars, etc.).\n*/\nconst SAFE_UNQUOTED_RE = /^[A-Za-z0-9_\\-./=:@%+,]+$/;\n/** Single-quote-wrap a token unless it only contains safe characters. */\nfunction quoteToken(token) {\n if (token.length > 0 && SAFE_UNQUOTED_RE.test(token)) return token;\n return \"'\" + token.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n/**\n* Inverse of `tokenize`: join tokens into a command string that survives\n* round-tripping through `splitCommand` and `tokenize`.\n*\n* Naive `tokens.join(\" \")` is lossy: any token that was originally quoted\n* loses its quote wrapper and may be mis-parsed when split/tokenized again\n* (e.g. `--testPathPattern=a|b` would split on the embedded `|`).\n*/\nfunction joinTokens(tokens) {\n return tokens.map(quoteToken).join(\" \");\n}\n\n//#endregion\n//#region src/lib/shell-splitter.ts\n/**\n* Split a raw command string into segments on unquoted &&, ||, ;, and | operators.\n* Quote-aware: operators inside single or double quotes are not split points.\n*\n* Returns segments with the operator that follows each one (null for the last segment).\n*/\nfunction splitCommand(input) {\n if (!input || !input.trim()) return [];\n const segments = [];\n let current = \"\";\n let i = 0;\n let inSingleQuote = false;\n let inDoubleQuote = false;\n while (i < input.length) {\n const ch = input[i];\n if (ch === \"'\" && !inDoubleQuote) {\n inSingleQuote = !inSingleQuote;\n current += ch;\n i++;\n continue;\n }\n if (ch === \"\\\"\" && !inSingleQuote) {\n inDoubleQuote = !inDoubleQuote;\n current += ch;\n i++;\n continue;\n }\n if (ch === \"\\\\\" && !inSingleQuote && i + 1 < input.length) {\n current += ch + input[i + 1];\n i += 2;\n continue;\n }\n if (!inSingleQuote && !inDoubleQuote) {\n if (ch === \"&\" && i + 1 < input.length && input[i + 1] === \"&\") {\n pushSegment(segments, current, \"&&\");\n current = \"\";\n i += 2;\n continue;\n }\n if (ch === \"&\" && !(i + 1 < input.length && input[i + 1] === \">\") && !(i > 0 && (input[i - 1] === \">\" || input[i - 1] === \"<\"))) {\n pushSegment(segments, current, \"&\");\n current = \"\";\n i++;\n continue;\n }\n if (ch === \"|\" && i + 1 < input.length && input[i + 1] === \"|\") {\n pushSegment(segments, current, \"||\");\n current = \"\";\n i += 2;\n continue;\n }\n if (ch === \"|\") {\n pushSegment(segments, current, \"|\");\n current = \"\";\n i++;\n continue;\n }\n if (ch === \";\") {\n pushSegment(segments, current, \";\");\n current = \"\";\n i++;\n continue;\n }\n }\n current += ch;\n i++;\n }\n const trimmed = current.trim();\n if (trimmed) segments.push({\n command: trimmed,\n operator: null\n });\n return segments;\n}\nfunction pushSegment(segments, text, operator) {\n const trimmed = text.trim();\n if (trimmed) segments.push({\n command: trimmed,\n operator\n });\n}\n\n//#endregion\n//#region src/lib/injection-guard.ts\n/**\n* Bash brace expansion: a `{...}` group containing a comma (list form,\n* `{a,b}`/`{a,}`/`{,a}`) or `..` (range form, `{1..3}`/`{a..z}`) is expanded\n* into multiple words *before* the command runs -- so `--for{c,}e` reaches the\n* program as argv `--force --fore` and `r{m,}m` as `rmm rm`. Braces without a\n* comma or `..` (`a{b}`, `HEAD@{1}`, `{}`) are left literal by bash and must\n* not be flagged.\n*\n* Bash brace expansion is *recursive*: a comma/range list expands even when a\n* branch of it contains its own nested `{...}`, so `--for{c,{}}e` still yields\n* `--force`. A regex cannot express \"a `,` or `..` at balanced brace depth\"\n* (the earlier `/\\{[^{}]*(?:,|\\.\\.)[^{}]*\\}/` required the separator to be\n* flanked by brace-free text out to the enclosing braces, which nested braces\n* defeat while bash still expands). So this is a depth-aware scan: increment on\n* `{`, decrement on `}` (never below 0), and flag the token if a `,` or `..`\n* appears while depth >= 1 -- i.e. anywhere inside any brace group, no matter\n* how deeply nested. Fail-closed: unbalanced braces are flagged too.\n*\n* As with `$`-expansion, the tokenizer has already stripped single quotes by\n* the time `checkInjection` runs, so a genuinely-quoted `'{a,b}'` or\n* `'{\"a\":1,\"b\":2}'` is indistinguishable from an unquoted brace-expansion here\n* and is flagged too -- an accepted fail-closed false positive (a `none`\n* result just defers to Claude Code's own classifier).\n*/\nfunction hasBraceExpansion(token) {\n let depth = 0;\n for (let i = 0; i < token.length; i++) {\n const ch = token[i];\n if (ch === \"{\") depth++;\n else if (ch === \"}\") {\n if (depth > 0) depth--;\n } else if (depth >= 1 && (ch === \",\" || ch === \".\" && token[i + 1] === \".\")) return true;\n }\n return false;\n}\n/**\n* Detect dangerous patterns in tokenized command segments.\n*\n* Checks for:\n* - Command substitution (`$(...)`, backticks)\n* - Any other unresolved `$`-expansion (`${...}`, `$'...'`, `$\"...\"`, `$VAR`, `$1`, ...)\n* - Brace expansion (`{a,b}` list form, `{1..3}` range form)\n* - Process substitution (`<(...)`, `>(...)`)\n* - Newlines in raw segment (injection via line splitting)\n*\n* Redirect targets (`>`, `>>`, `<`) are deliberately not checked here any\n* more -- with no allow tier, a bare redirect is no longer a reason to\n* withhold a decision on its own, so `evaluateSegment` judges a redirect's\n* *target* itself (the node-modules-write and env-file-write deny rules)\n* instead of asking this guard to flag the operator generically. See\n* `evaluateSegment`'s own comment for why that check has to run ahead of this\n* one.\n*\n* The tokenizer (`shell-tokenizer.ts`) resolves single-quoted and\n* backslash-escaped content into plain characters before this function ever\n* sees it -- so a token's surviving `$` is indistinguishable between \"bash\n* will expand this\" (`$(...)`, `${...}`, `$'...'`, `$\"...\"`, `$VAR`, `$1`,\n* `$@`, `$?`, ...) and \"this `$` was always a literal character\" (e.g. from\n* `'cost $5'` or `\\$5`). Rather than trying to reconstruct quote context\n* after the fact, any token still containing a `$` is treated as unsafe.\n* This is deliberately fail-closed: it routes the segment to `none` (Claude\n* Code's own classifier judges it from there) rather than risking an\n* unresolved expansion silently being denied or not based on the wrong argv.\n*\n* ## Expansions this guard knowingly does NOT model\n*\n* Two weaker members of the same \"unmodeled expansion\" family are left open. Both were\n* surfaced by the adversarial review of the issue-attachments hardening and verified\n* empirically against real bash plus this evaluator. **Decision (2026-07-13): accept as\n* documented limitations.** They are recorded here, not as a todo, because the next\n* person to touch this function is the person who needs to know.\n*\n* **Glob reconstruction.** `git push --forc[e] origin feat/x` (also `--forc?`, `--for*`)\n* expands to a real `--force` argv, yet this returns `safe` and the `git-force-push` rule\n* never sees a `--force` token -- but *only when a file literally named `--force` already\n* exists in the working directory*. Creating that file is a separate command, so it is a\n* two-step, precondition-gated bypass: strictly weaker than the unconditional `$`/brace\n* bypasses that were fixed. The same class defeats a command-name deny rule too (e.g.\n* `s[u]do`), again only with a pre-existing matching file. Such a command gets no decision\n* from the hook, and auto mode's classifier judges it.\n*\n* *Why not fixed here:* this guard can only withhold a decision. Routing every token with an\n* unquoted glob metacharacter (`*`, `?`, `[`) to `none` would add no deny; it would hide the\n* deny rules from nearly every globbing command an agent runs (`ls *.md`, `cp dist/* ...`).\n* Closing the gap takes a deny rule instead -- the candidate is to deny a glob metachar\n* **only in a token that also begins with `-`** (a flag position, which is where the\n* reconstruction lives), leaving bare-argument globs like `*.md` alone.\n*\n* **Tilde expansion.** `~` is unmodeled. Bash only expands it to a home *path* at word\n* start, so it cannot reconstruct a flag or a command name -- it is a path-scope concern,\n* not the flag/command-reconstruction vector this guard defends against. Lowest priority;\n* noted for completeness.\n*\n* If either is ever closed, pin it with regression fixtures the way the nested-brace cases\n* were pinned in `tests/fixtures.ts`.\n*/\nfunction checkInjection(tokens, rawSegment) {\n if (/[\\n\\r]/.test(rawSegment)) return {\n safe: false,\n reason: \"unquoted newline in command\"\n };\n for (const token of tokens) {\n if (token === \"`\") return {\n safe: false,\n reason: \"backtick command substitution\"\n };\n if (token.includes(\"`\")) return {\n safe: false,\n reason: \"backtick command substitution\"\n };\n if (token.includes(\"$\")) return {\n safe: false,\n reason: \"unresolved dollar-expansion\"\n };\n if (hasBraceExpansion(token)) return {\n safe: false,\n reason: \"brace expansion\"\n };\n if (token.includes(\"<(\")) return {\n safe: false,\n reason: \"process substitution\"\n };\n if (token.includes(\">(\")) return {\n safe: false,\n reason: \"process substitution\"\n };\n }\n return { safe: true };\n}\n\n//#endregion\n//#region src/lib/env-file.ts\n/**\n* File extensions considered safe documentation templates.\n* Files ending with these suffixes are NOT treated as secret .env files.\n*/\nconst ENV_SAFE_SUFFIXES = [\n \".sample\",\n \".example\",\n \".template\"\n];\n/**\n* Env files owned by a bst command rather than by the developer's secrets.\n*\n* `docker-compose.check.env` is the file `bst docker:check` auto-detects and\n* passes to compose as `--env-file`. It is committed, and it holds placeholder\n* values for a throwaway check stack -- yet it is also the *only* way to feed\n* compose interpolation, so denying it left an agent unable to create or read\n* the one file the check needs. Two bst features blocking each other is worse\n* than the marginal secret risk of a file whose purpose is to hold non-secrets.\n*/\nconst ENV_TOOL_OWNED_SUFFIXES = [\".check.env\"];\n/**\n* Pattern matching .env files: `.env`, `.env.local`, `.env.production`, etc.\n* Does NOT match `.env.sample`, `.env.example`, `.env.template`.\n*/\nconst ENV_FILE_PATTERN = /(^|[/\\\\])(\\.env(\\..*)?|.*\\.env(\\..*)?|\\.env)$/;\n/** Check if a filename is a safe .env documentation template, or a bst-owned env file. */\nfunction isEnvSafeSuffix(filename) {\n return [...ENV_SAFE_SUFFIXES, ...ENV_TOOL_OWNED_SUFFIXES].some((suffix) => filename.endsWith(suffix));\n}\n/**\n* Check if a path targets a .env file (excluding safe templates).\n* Works with both bare filenames and full paths.\n*/\nfunction isEnvFile(filePath) {\n if (isEnvSafeSuffix(path.basename(filePath))) return false;\n return ENV_FILE_PATTERN.test(filePath);\n}\n\n//#endregion\n//#region src/lib/git-state.ts\n/**\n* Return the current branch name, or null when:\n* - cwd is not a git repository,\n* - HEAD is detached,\n* - any git invocation fails.\n*\n* Null means \"unknown\", and an unknown branch is not a protected one: no\n* rule denies because the branch could not be read.\n*/\nfunction getCurrentBranch(cwd) {\n try {\n const out = execFileSync(\"git\", [\n \"rev-parse\",\n \"--abbrev-ref\",\n \"HEAD\"\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ]\n }).toString().trim();\n if (!out || out === \"HEAD\") return null;\n return out;\n } catch {\n return null;\n }\n}\n/**\n* Return the remote default branch (from refs/remotes/origin/HEAD), stripped\n* of the \"origin/\" prefix. Returns null when the symbolic ref isn't set\n* (common for fresh clones with no remote).\n*/\nfunction getRemoteDefaultBranch(cwd) {\n try {\n const out = execFileSync(\"git\", [\n \"symbolic-ref\",\n \"--short\",\n \"refs/remotes/origin/HEAD\"\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ]\n }).toString().trim();\n if (!out) return null;\n return out.startsWith(\"origin/\") ? out.slice(7) : out;\n } catch {\n return null;\n }\n}\nconst ALWAYS_PROTECTED = [\"main\", \"master\"];\n/**\n* Compute the full protected set: hard-coded {main, master}, plus the remote\n* default branch when available, plus any caller-supplied extras (e.g. from\n* .claude/hooks/bst-hook.settings.json#protectedBranches).\n*\n* Known limitation: if a repo's real default branch is neither \"main\" nor\n* \"master\" AND refs/remotes/origin/HEAD is unset (getRemoteDefaultBranch()\n* returns null -- e.g. a shallow or partial clone that never fetched the\n* symbolic ref), that default branch is absent from this set. A push whose\n* refspec destination is that branch would then pass the protected-branch\n* check in movesProtectedRef. This is not treated as a reason to block pushes\n* whenever origin/HEAD is unset, since that would break the far more common\n* case (default branch already covered by {main, master} or by\n* protectedBranches); repos with a non-standard default branch name should\n* set it explicitly via .claude/hooks/bst-hook.settings.json#protectedBranches.\n*/\nfunction getProtectedBranches(cwd, extra) {\n const set = new Set(ALWAYS_PROTECTED);\n const remote = getRemoteDefaultBranch(cwd);\n if (remote) set.add(remote);\n for (const b of extra) set.add(b);\n return set;\n}\n/**\n* The upstream of `branch` in short form (`origin/main`), or null when it has\n* none, cwd is not a repository, or git fails for any other reason.\n*/\nfunction getUpstream(cwd, branch) {\n try {\n return execFileSync(\"git\", [\n \"rev-parse\",\n \"--abbrev-ref\",\n `${branch}@{upstream}`\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ]\n }).toString().trim() || null;\n } catch {\n return null;\n }\n}\n/**\n* The commit id `rev` names, or null when it names no commit (a path, a\n* typo) or git fails. `rev` must not start with `-`: callers pass only\n* positional arguments.\n*/\nfunction resolveCommit(cwd, rev) {\n try {\n return execFileSync(\"git\", [\n \"rev-parse\",\n \"--verify\",\n \"--quiet\",\n `${rev}^{commit}`\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ]\n }).toString().trim() || null;\n } catch {\n return null;\n }\n}\n/**\n* True when `cwd` is the repository's *primary* worktree (not a linked one\n* created by `git worktree add`). In the primary worktree the git dir and the\n* common git dir are the same directory; in a linked worktree the git dir is\n* `.../.git/worktrees/<name>` while the common dir is the shared `.../.git`.\n*\n* Fail-safe: any git/filesystem error returns `false` (treated as \"not primary\",\n* i.e. no advisory) so a hiccup never disturbs the decision.\n*/\nfunction isPrimaryWorktree(cwd) {\n try {\n const gitDir = execFileSync(\"git\", [\"rev-parse\", \"--absolute-git-dir\"], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ]\n }).toString().trim();\n const commonRaw = execFileSync(\"git\", [\"rev-parse\", \"--git-common-dir\"], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ]\n }).toString().trim();\n if (!gitDir || !commonRaw) return false;\n const commonDir = resolve(cwd, commonRaw);\n return realpathSync(gitDir) === realpathSync(commonDir);\n } catch {\n return false;\n }\n}\n\n//#endregion\n//#region src/lib/git-short-options.ts\n/** Per-subcommand required-value short option letters. Deliberately excludes\n* `-t` (`--track`): see the note below the value-taking sets.\n*/\nconst VALUE_TAKING_LETTERS = {\n switch: /* @__PURE__ */ new Set([\"c\", \"C\"]),\n checkout: /* @__PURE__ */ new Set([\"b\", \"B\"]),\n branch: /* @__PURE__ */ new Set([\"u\"]),\n fetch: /* @__PURE__ */ new Set([\"o\", \"j\"])\n};\n/**\n* Decompose a short-option cluster token for `sub`. Returns `undefined` for\n* anything that is not a short-option cluster: a token that does not start\n* with `-`, the bare `-`, and anything starting with `--`. Every caller keeps\n* handling those tokens exactly as it does today.\n*\n* Otherwise walks the characters after the leading `-` left to right,\n* collecting each into `letters`, and stops at the first character that is a\n* required-value option letter for `sub` -- that letter is the last one\n* collected, and the remainder of the token (possibly empty) becomes\n* `attachedValue`, never inspected as flags again.\n*\n* Worked cases:\n* - `-fc` (switch or checkout) -> letters `[\"f\", \"c\"]`, no attached value --\n* `f` never stops the scan, so `c`/`C` is just collected as another letter\n* unless the subcommand's own value-taking set includes it earlier.\n* - `-cf` (switch) -> letters `[\"c\"]`, attached value `\"f\"`.\n* - `-cfeat/x` (switch) -> letters `[\"c\"]`, attached value `\"feat/x\"`.\n* - `-c` (switch) -> letters `[\"c\"]`, attached value `\"\"` (git reads the\n* branch name from the next argv element).\n*/\nfunction splitShortOptionCluster(token, sub) {\n if (!token.startsWith(\"-\") || token.startsWith(\"--\") || token.length <= 1) return;\n const valueLetters = VALUE_TAKING_LETTERS[sub];\n const letters = [];\n const chars = token.slice(1);\n for (let i = 0; i < chars.length; i++) {\n const ch = chars[i];\n letters.push(ch);\n if (valueLetters.has(ch)) return {\n letters,\n attachedValue: chars.slice(i + 1)\n };\n }\n return { letters };\n}\n\n//#endregion\n//#region src/lib/path-scope.ts\n/**\n* Path-containment helpers that are correct across operating systems.\n*\n* The hook receives a `cwd` from its host. On Windows the same location can be\n* spelled with either drive-letter case (`D:\\proj` vs `d:\\proj`) -- the IDE is\n* inconsistent -- and NTFS treats them as identical. Plain `===` / `startsWith`\n* comparisons on the resolved path therefore misfire whenever the drive-letter\n* case differs, falling through to none (reads) or a false containment\n* result for a path-scoped write rule -- git-dir-write, node-modules-write,\n* hooks-folder-write (writes).\n*\n* The fix: pick the path flavor (win32 vs posix) from the *trusted* base path --\n* `cwd` -- and run every resolve/compare through that flavor. win32's\n* `relative()` is case-insensitive and separator-aware, so drive-letter case no\n* longer matters; posix stays case-sensitive, as that filesystem demands.\n* Deriving the flavor from the base (not the incoming target) means a crafted\n* target can't flip the comparison mode.\n*/\n/** Select the path flavor implied by a base path (a leading drive letter => win32). */\nfunction pathFlavorFor(base) {\n return /^[a-zA-Z]:[\\\\/]/.test(base) ? path.win32 : path.posix;\n}\n/** Resolve `target` against `base` using the flavor implied by `base`. */\nfunction resolveIn(base, target) {\n return pathFlavorFor(base).resolve(base, target);\n}\n/**\n* Whether `target` is `dir` itself or nested beneath it, compared with `dir`'s\n* flavor. `target` is expected to be already resolved (absolute); pass it through\n* `resolveIn` first when it may be relative or contain `..`.\n*/\nfunction isWithin(dir, target) {\n const pp = pathFlavorFor(dir);\n const rel = pp.relative(dir, target);\n if (rel === \"\") return true;\n if (rel === \"..\" || rel.startsWith(\"..\" + pp.sep)) return false;\n return !pp.isAbsolute(rel);\n}\n/** Whether two paths refer to the same location, compared with `a`'s flavor. */\nfunction samePath(a, b) {\n return pathFlavorFor(a).relative(a, b) === \"\";\n}\n/**\n* Whether `filePath` (resolved against `cwd`) is a `.git` directory\n* anywhere under `cwd` -- top-level or nested inside a subdirectory repo --\n* or anything beneath one. Covers both the directory in a normal clone and\n* the gitdir-pointer *file* git writes at the root of a worktree checkout\n* (both cases have a path segment exactly equal to `.git`).\n*\n* A nested repo's git dir (`<cwd>/sub/.git/config`) is just as sensitive as\n* the top-level one: an unguarded write there can add a malicious remote or\n* rewrite push config that a later `cd sub && git push` trusts. So this\n* checks every path segment of `filePath` resolved against `cwd`, not just\n* `<cwd>/.git` -- matching `.git` at *any* depth, as long as the resolved\n* path stays within `cwd`. A `.git` outside `cwd` is not this function's\n* concern and reaches `none` -- there is no longer a blanket outside-cwd\n* deny to catch it either.\n*\n* Matches on a resolved path *segment* exactly equal to `.git`, not a\n* substring, so it does not mistake `.gitignore`, `.gitkeep`, `.github/`,\n* `src/foo.git.ts`, or a directory merely named `mygit`/`git` for a `.git/`\n* target.\n*/\nfunction targetsGitDir(filePath, cwd) {\n const pp = pathFlavorFor(cwd);\n const resolved = resolveIn(cwd, filePath);\n if (!isWithin(cwd, resolved)) return false;\n const rel = pp.relative(cwd, resolved);\n if (rel === \"\") return false;\n return rel.split(pp.sep).includes(\".git\");\n}\n/**\n* Whether `filePath` (resolved against `cwd`) has a path segment exactly equal\n* to `node_modules` -- i.e. it names a location inside *any* installed package\n* tree, at any depth, including pnpm's virtual store\n* (`node_modules/.pnpm/<pkg>/node_modules/<pkg>/...`).\n*\n* **Deliberately not scoped to `cwd`**, unlike `targetsGitDir`. pnpm populates\n* `node_modules` with *hardlinks into the global content-addressable store*, so\n* a single in-place overwrite of an installed file (a plain `cp`, a `>`\n* redirect -- anything that opens the destination `O_WRONLY|O_TRUNC` rather than\n* unlinking it first) writes *through* the link and mutates the store's own\n* content-addressed file. That corrupts the package for the global store, every\n* other project on the machine that shares the inode, and every future install\n* or `pnpm dlx` that hardlinks it -- with no error at the scene, because the\n* directory that did the writing ends up perfectly consistent.\n*\n* This is exactly how a local `@bensandee/tooling` build landed on top of the\n* published 0.64.2 in the store on 2026-07-13: `cp -r <local>/dist/*` over a\n* scratch consumer's `node_modules/@bensandee/tooling/dist/`. The destination\n* was outside `cwd` -- so a cwd-scoped check would not have seen it -- and the\n* store stayed broken until a consumer's `pnpm dlx` failed days later with\n* `ERR_MODULE_NOT_FOUND` for a chunk that no published tarball has ever\n* contained.\n*\n* The lesson encoded here: **cwd-containment is not a safety boundary for\n* `node_modules`.** A path inside it is not \"in the project\" in any meaningful\n* sense -- it is a shared, machine-global resource wearing a local path. Hence\n* every segment of the *resolved* path is checked, wherever it lands.\n*\n* Matches on a path *segment*, not a substring, so `src/node_modules_helper.ts`\n* and `docs/node_modules.md` are not mistaken for the real thing.\n*/\nfunction targetsNodeModules(filePath, cwd) {\n return resolveIn(cwd, filePath).split(/[/\\\\]/).includes(\"node_modules\");\n}\n\n//#endregion\n//#region src/rules.ts\n/** Git global flags that consume the next token as an argument. */\nconst GIT_FLAGS_WITH_ARG = /* @__PURE__ */ new Set([\n \"-C\",\n \"-c\",\n \"--git-dir\",\n \"--work-tree\"\n]);\n/** Extract the git subcommand (first non-flag token after \"git\"), skipping flag arguments. */\nfunction getGitSubcommand(tokens) {\n const args = tokens.slice(1);\n for (let i = 0; i < args.length; i++) {\n const t = args[i];\n if (GIT_FLAGS_WITH_ARG.has(t)) {\n i++;\n continue;\n }\n if (t.startsWith(\"-\")) continue;\n return t;\n }\n}\nconst WORKTREE_ADVICE = \"You're creating a feature branch in the primary worktree. An isolated worktree keeps this work independent of other windows, and with no commits on this branch yet, converting is cheap -- `git worktree add ../<dir> -b <branch>` (a sibling outside the repo, with its own dependency install). Claude Code keeps this session inside its own working tree, so the work continues in a new session opened in the sibling worktree. If you cannot open a session there, stay in the primary checkout on this branch and check `git branch --show-current` before each commit.\";\n/**\n* True when the command creates a branch: `git checkout -b/-B` or\n* `git switch -c/-C`, including those letters carried in a short-option\n* cluster with other letters or an attached value (`-bfix-bug`, `-qc\n* feat/x`). A plain `git switch <branch>` is not creation. Long spellings\n* (`--create`, `--force-create`) are deliberately not recognized as creation\n* here -- only the short grammar is in scope.\n*/\nfunction isGitBranchCreation(tokens) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"checkout\" && sub !== \"switch\") return false;\n const after = tokens.slice(tokens.indexOf(sub) + 1);\n const creationLetters = sub === \"checkout\" ? [\"b\", \"B\"] : [\"c\", \"C\"];\n return after.some((t) => {\n const cluster = splitShortOptionCluster(t, sub);\n return cluster !== void 0 && cluster.letters.some((l) => creationLetters.includes(l));\n });\n}\n/**\n* Non-blocking advisory for a `git` command that reaches the end of rule\n* matching with no decision either way. There is no allow tier any more to\n* carry it, so `evaluateSegment` calls this directly and attaches the result\n* to the none result it returns for a `git` command, rather than routing it\n* through a `Rule`'s own `when`/advise pair the way the old `git-safe-write`\n* allow rule did.\n*/\nfunction worktreeAdvice(tokens, ctx) {\n return isGitBranchCreation(tokens) && isPrimaryWorktree(ctx.cwd) ? WORKTREE_ADVICE : void 0;\n}\n/** Force-create letter: `C` on switch, `B` on checkout and `worktree add`. */\nconst GIT_FORCE_CREATE_LETTER = {\n switch: \"C\",\n checkout: \"B\"\n};\n/**\n* The long force-create spelling (switch only). Every unambiguous prefix down\n* to `--force-` (the shortest that is not the distinct `--force` option)\n* matches, because git's parse-options accepts one and a prefix missed here\n* would let a force-create onto a protected name through.\n*/\nconst GIT_FORCE_CREATE_LONG = {\n flag: \"--force-create\",\n shortest: \"--force-\"\n};\n/**\n* Every branch name a force-create spelling in `after` (the tokens following\n* the subcommand -- and past `add`, for `worktree add`) targets. Usually zero\n* or one, but git's last-occurrence-wins on a repeated creation flag is not\n* modeled: every occurrence contributes its target, so a repeated flag is\n* handled conservatively rather than resolving which one wins.\n*\n* Grammar, reusing `splitShortOptionCluster`: a cluster whose letters include\n* the force letter contributes its attached value when non-empty, else the\n* next argv token; the long spelling contributes the next argv token, or the\n* name attached to it with `=`. A creation flag with nothing after it (a\n* valueless trailing `-C`, or `--force-create` as the last token) resolves no\n* target for that occurrence -- git errors on the command anyway.\n*/\nfunction gitForceCreateTargets(after, letterSub) {\n const letter = GIT_FORCE_CREATE_LETTER[letterSub];\n const longFlag = GIT_FORCE_CREATE_LONG;\n const targets = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === void 0) continue;\n if (letterSub === \"switch\") {\n const [flag, ...rest] = t.split(\"=\");\n if (flag !== void 0 && flag.length >= longFlag.shortest.length && longFlag.flag.startsWith(flag)) {\n const target = rest.length > 0 ? rest.join(\"=\") : after[i + 1];\n if (target !== void 0) targets.push(target);\n continue;\n }\n }\n const cluster = splitShortOptionCluster(t, letterSub);\n if (cluster === void 0 || !cluster.letters.includes(letter)) continue;\n if (cluster.attachedValue !== void 0 && cluster.attachedValue !== \"\") {\n targets.push(cluster.attachedValue);\n continue;\n }\n const next = after[i + 1];\n if (next !== void 0) targets.push(next);\n }\n return targets;\n}\n/**\n* Every branch name a force-create spelling targets in a full `git ...`\n* command: `switch -C`/`--force-create`, `checkout -B`, or `worktree add -B`.\n* Any other subcommand -- including `worktree list`/`remove`/`move` -- always\n* resolves no target. `worktree add` reuses checkout's `-B` grammar.\n*/\nfunction gitCommandForceCreateTargets(tokens) {\n const sub = getGitSubcommand(tokens);\n if (sub === \"switch\" || sub === \"checkout\") return gitForceCreateTargets(tokens.slice(tokens.indexOf(sub) + 1), sub);\n if (sub === \"worktree\") {\n const worktreeIdx = tokens.indexOf(\"worktree\");\n if (tokens[worktreeIdx + 1] !== \"add\") return [];\n return gitForceCreateTargets(tokens.slice(worktreeIdx + 2), \"checkout\");\n }\n return [];\n}\n/**\n* True when a `switch`/`checkout`/`worktree add` command force-creates a\n* branch under a name in the protected set. Force-create resets an existing\n* branch to the start point (HEAD by default) rather than merely creating it,\n* so this is a claim about the TOKENS, not the current branch: it never calls\n* getCurrentBranch and is valid even outside a repository, where main/master\n* are protected unconditionally.\n*/\nfunction isGitProtectedBranchForceCreate(tokens, ctx) {\n const targets = gitCommandForceCreateTargets(tokens);\n if (targets.length === 0) return false;\n const protectedSet = getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []);\n return targets.some((t) => protectedSet.has(t));\n}\n/**\n* True when `git branch` destroys or overwrites a protected ref: a delete\n* (`-d`/`-D`/`--delete`) naming one, a non-force rename (`-m`/`-M`/`--move`)\n* moving one away by name, or any force spelling (`-f`/`--force`, or the\n* force letters `-M`/`-C`) writing onto one. A claim about the TOKENS, never\n* calling getCurrentBranch and valid outside a repository, like the\n* force-create check above. It carries no pre-subcommand flag guard, so\n* `git -c core.hooksPath=/tmp/evil branch -D main` still denies.\n*\n* The three checks are independent, not mutually exclusive branches of one\n* mode -- `git branch -M junk main` is a force rename whose *source* (`junk`)\n* is not protected, so the rename check alone would miss it; the force check\n* catches it via the *destination* (`main`).\n*\n* The accepted residual: `git branch -m junk` while sitting on a protected\n* branch renames it away with no victim name in the tokens at all, so this\n* returns false and the command gets no decision.\n*/\nfunction isGitProtectedBranchRefWrite(tokens, ctx) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"branch\") return false;\n const { flags, positionals } = splitGitBranchArgs(tokens.slice(tokens.indexOf(sub) + 1));\n if (positionals.length === 0) return false;\n let deleteMode = false;\n let renameMode = false;\n let copyMode = false;\n let forceMode = false;\n for (const flag of flags) {\n if (flag.letters !== void 0) {\n if (flag.letters.includes(\"d\") || flag.letters.includes(\"D\")) deleteMode = true;\n if (flag.letters.includes(\"m\") || flag.letters.includes(\"M\")) renameMode = true;\n if (flag.letters.includes(\"c\") || flag.letters.includes(\"C\")) copyMode = true;\n if (flag.letters.includes(\"f\") || flag.letters.includes(\"M\") || flag.letters.includes(\"C\")) forceMode = true;\n continue;\n }\n if (flag.name === \"delete\") deleteMode = true;\n if (flag.name === \"move\") renameMode = true;\n if (flag.name === \"copy\") copyMode = true;\n if (flag.name === \"force\") forceMode = true;\n }\n const protectedSet = getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []);\n if (deleteMode && positionals.some((p) => protectedSet.has(p))) return true;\n if (renameMode && positionals.length >= 2 && protectedSet.has(positionals[0])) return true;\n if (forceMode) {\n const destination = renameMode || copyMode ? positionals[positionals.length - 1] : positionals[0];\n if (destination !== void 0 && protectedSet.has(destination)) return true;\n }\n return false;\n}\n/**\n* The ref a refspec writes to. `src:dst` targets dst; a bare `ref` targets the\n* same name on the remote. Strips the leading \"+\" (force) and any\n* `refs/heads/` prefix.\n*/\nfunction refspecDestination(refspec) {\n const spec = refspec.startsWith(\"+\") ? refspec.slice(1) : refspec;\n const colon = spec.lastIndexOf(\":\");\n return (colon === -1 ? spec : spec.slice(colon + 1)).replace(/^refs\\/heads\\//, \"\");\n}\n/**\n* Long `git branch` flags whose value is the next token when spelled without\n* `=`. `--abbrev`, `--color` and `--column` are deliberately absent: measured,\n* they consume nothing (`git branch --color zz` creates `zz`), so treating\n* them as value-consuming would swallow a branch name. `-u`/`--set-upstream-to`\n* also consume the next token, but `-u` is\n* handled by `splitShortOptionCluster`'s own branch table, not by name here.\n*/\nconst GIT_BRANCH_VALUE_FLAG_NAMES = /* @__PURE__ */ new Set([\n \"contains\",\n \"no-contains\",\n \"merged\",\n \"no-merged\",\n \"points-at\",\n \"sort\",\n \"format\",\n \"set-upstream-to\"\n]);\n/**\n* Split the tokens after `git branch` into its flags and positionals, for\n* `isGitProtectedBranchRefWrite`. A token not starting with `-` is a\n* positional. `--` is recorded as a flag with no name. A `--flag`\n* token is recorded by its name (the part before `=`) and consumes the\n* following token when it has no `=` and its name is in\n* `GIT_BRANCH_VALUE_FLAG_NAMES`. A short token is decomposed with\n* `splitShortOptionCluster(t, \"branch\")`, recording its letters and\n* consuming the following token when its attached value is the empty string\n* (a trailing `-u`).\n*/\nfunction splitGitBranchArgs(after) {\n const flags = [];\n const positionals = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === void 0) continue;\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n if (t.startsWith(\"--\") && t !== \"--\") {\n const eq = t.indexOf(\"=\");\n const name = eq > 0 ? t.slice(2, eq) : t.slice(2);\n flags.push({\n raw: t,\n name\n });\n if (eq < 0 && GIT_BRANCH_VALUE_FLAG_NAMES.has(name)) i++;\n continue;\n }\n const cluster = splitShortOptionCluster(t, \"branch\");\n if (cluster === void 0) {\n flags.push({ raw: t });\n continue;\n }\n flags.push({\n raw: t,\n letters: cluster.letters\n });\n if (cluster.attachedValue === \"\") i++;\n }\n return {\n flags,\n positionals\n };\n}\n/**\n* Long `git fetch` flags whose value is the next token when spelled without\n* `=`. `--recurse-submodules` is deliberately absent: it takes its optional\n* value only with `=`, so `git fetch --recurse-submodules origin` reads\n* `origin` as the remote, not as the flag's value. The three names that are on\n* no allowlist (`--upload-pack`, `--submodule-prefix`,\n* `--recurse-submodules-default`) are listed here so a swallowed value cannot\n* be misread as a refspec.\n*/\nconst GIT_FETCH_VALUE_FLAG_NAMES = /* @__PURE__ */ new Set([\n \"depth\",\n \"deepen\",\n \"shallow-since\",\n \"shallow-exclude\",\n \"negotiation-tip\",\n \"filter\",\n \"jobs\",\n \"server-option\",\n \"upload-pack\",\n \"refmap\",\n \"submodule-prefix\",\n \"recurse-submodules-default\"\n]);\n/**\n* Split the tokens after `git fetch` into flags, positionals and `--refmap`\n* values, for `isGitProtectedBranchFetchWrite`, in the shape of\n* `splitGitBranchArgs` (whose interfaces are deliberately not reused: the two\n* splitters have different value-flag tables and must stay independent). A\n* token not starting with `-` is a positional. `--` is recorded as a flag with\n* no name and no letters; later tokens are not treated specially. A `--flag` token is recorded\n* by name and consumes the following token when it has no `=` and its name is\n* in `GIT_FETCH_VALUE_FLAG_NAMES`. A short token is decomposed with\n* `splitShortOptionCluster(t, \"fetch\")`, recording its letters and consuming\n* the following token when its attached value is the empty string (a trailing\n* `-j` or `-o`).\n*/\nfunction splitGitFetchArgs(after) {\n const flags = [];\n const positionals = [];\n const refmaps = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === void 0) continue;\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n if (t === \"--\") {\n flags.push({ raw: t });\n continue;\n }\n if (t.startsWith(\"--\")) {\n const eq = t.indexOf(\"=\");\n const name = eq > 0 ? t.slice(2, eq) : t.slice(2);\n flags.push({\n raw: t,\n name\n });\n if (name === \"refmap\") {\n if (eq > 0) refmaps.push(t.slice(eq + 1));\n else if (after[i + 1] !== void 0) refmaps.push(after[i + 1]);\n }\n if (eq < 0 && GIT_FETCH_VALUE_FLAG_NAMES.has(name)) i++;\n continue;\n }\n const cluster = splitShortOptionCluster(t, \"fetch\");\n if (cluster === void 0) {\n flags.push({ raw: t });\n continue;\n }\n flags.push({\n raw: t,\n letters: cluster.letters\n });\n if (cluster.attachedValue === \"\") i++;\n }\n return {\n flags,\n positionals,\n refmaps\n };\n}\n/**\n* Classify one fetch refspec's destination. Unlike a push's use of\n* `refspecDestination`, a token with **no colon is not a destination at all**:\n* a source-only fetch refspec writes `FETCH_HEAD` plus whatever the remote's\n* refmap says, never a named local branch, so this function must not consult\n* the shared helper first -- for push, a bare `main` means \"the same name on\n* the remote\" and the helper hands it back as a destination.\n*\n* With a colon the destination goes through `refspecDestination`, which already\n* strips `refs/heads/` and leaves every other `refs/` prefix intact. An empty\n* destination (`main:`) writes nothing. `refs/remotes/` is a remote-tracking\n* cache; any other `refs/` namespace (tags included) is `other`; anything else\n* is a branch name, which git resolves to `refs/heads/<name>` when no such ref\n* exists. Note that `:main` reaches `branch` with name `main` -- the fetch\n* reading, where an empty source means HEAD, not push's delete reading.\n*/\nfunction fetchRefspecTarget(spec) {\n const body = spec.startsWith(\"+\") ? spec.slice(1) : spec;\n if (body.startsWith(\"^\")) return { kind: \"none\" };\n if (!body.includes(\":\")) return { kind: \"none\" };\n const destination = refspecDestination(body);\n if (destination === \"\") return { kind: \"none\" };\n if (destination.startsWith(\"refs/remotes/\")) return { kind: \"tracking\" };\n if (destination.startsWith(\"refs/\")) return { kind: \"other\" };\n return {\n kind: \"branch\",\n name: destination\n };\n}\n/**\n* Escape a glob for regex use, then turn each escaped `*` into \"any run of\n* characters\". Escaping first is what keeps a name containing regex syntax\n* (`feat/x+`, say) from compiling into a pattern that matches something the\n* name does not.\n*/\nfunction globToAnchoredRegex(glob) {\n const escaped = glob.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`^${escaped.replace(/\\\\\\*/g, \".*\")}$`);\n}\n/**\n* True when a refspec destination names a protected branch, or is a glob that\n* covers one (`refs/heads/*:refs/heads/*` covers `main`,\n* `refs/heads/*:refs/heads/copy/*` does not).\n*/\nfunction isProtectedFetchBranch(name, protectedSet) {\n if (protectedSet.has(name)) return true;\n if (!name.includes(\"*\")) return false;\n const pattern = globToAnchoredRegex(name);\n for (const member of protectedSet) if (pattern.test(member)) return true;\n return false;\n}\n/**\n* True when a `git fetch` writes a protected local branch: some refspec lands\n* on a protected name (or a glob covering one), forced or not -- an unforced\n* write still fast-forwards the branch. A claim about the TOKENS --\n* it never calls `getCurrentBranch` and never asks whether the ref exists, so it\n* is valid outside a repository, where `main`/`master` are protected\n* unconditionally. Like `isGitProtectedBranchRefWrite`, it carries no\n* pre-subcommand flag guard, so `git -c core.hooksPath=/tmp/evil fetch .\n* +HEAD:main` still denies.\n*\n* Two deliberate widenings, both in the deny direction. **Every** positional\n* is scanned, the remote slot included: if the value-consuming flag list were\n* ever over-inclusive, a swallowed remote would shift the sole refspec into the\n* first slot, and the deny must not be blind there -- the cost is a deny on an\n* scp-style URL whose path is literally a protected name. And every\n* `--refmap` value is classified, because measured,\n* `--refmap=+refs/heads/*:refs/heads/*` beside a source-only positional\n* force-writes `main`.\n*\n* The accepted residual: `--stdin` carries its refspecs on a stream, so a\n* `HEAD:main` arriving that way cannot be seen and gets no decision.\n*/\nfunction isGitProtectedBranchFetchWrite(tokens, ctx) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"fetch\") return false;\n const { positionals, refmaps } = splitGitFetchArgs(tokens.slice(tokens.indexOf(sub) + 1));\n const protectedSet = getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []);\n return [...positionals, ...refmaps].some((spec) => {\n const target = fetchRefspecTarget(spec);\n if (target.kind !== \"branch\" || target.name === void 0) return false;\n return isProtectedFetchBranch(target.name, protectedSet);\n });\n}\n/**\n* True when `git update-ref` writes or deletes (`-d`) a protected branch: its\n* ref positional, with any `refs/heads/` prefix stripped, is protected. `-m`\n* takes the next token as its reason. `--stdin` carries its commands on a\n* stream, so a protected write arriving that way cannot be seen and gets no\n* decision.\n*/\nfunction isGitProtectedUpdateRef(tokens, ctx) {\n const after = tokens.slice(tokens.indexOf(\"update-ref\") + 1);\n let ref;\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === \"-m\") {\n i++;\n continue;\n }\n if (t.startsWith(\"-\")) continue;\n ref = t;\n break;\n }\n if (ref === void 0) return false;\n return getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []).has(ref.replace(/^refs\\/heads\\//, \"\"));\n}\n/**\n* The directory git runs in: `ctx.cwd` (already the effective directory after\n* `cd <dir> &&` propagation) with every global `-C <dir>` applied in order, the\n* way git composes them. `--git-dir`/`--work-tree` are not modelled: a command\n* carrying one reads its branch from `ctx.cwd`, an accepted residual.\n*/\nfunction gitEffectiveCwd(tokens, cwd) {\n let dir = cwd;\n const args = tokens.slice(1);\n for (let i = 0; i < args.length; i++) {\n const t = args[i];\n if (t === \"-C\") {\n const next = args[i + 1];\n if (next !== void 0) dir = path.resolve(dir, next);\n i++;\n continue;\n }\n if (GIT_FLAGS_WITH_ARG.has(t)) {\n i++;\n continue;\n }\n if (!t.startsWith(\"-\")) break;\n }\n return dir;\n}\n/** Flags that do not change which branch a switch or checkout lands on. */\nconst GIT_SWITCH_NEUTRAL_FLAGS = /* @__PURE__ */ new Set([\n \"-q\",\n \"--quiet\",\n \"-f\",\n \"--force\",\n \"--discard-changes\",\n \"-m\",\n \"--merge\",\n \"--no-track\",\n \"--progress\",\n \"--no-progress\",\n \"--guess\",\n \"--no-guess\",\n \"--ignore-other-worktrees\",\n \"--recurse-submodules\",\n \"--no-recurse-submodules\"\n]);\n/** Long flags whose value names the branch a switch or checkout creates and lands on. */\nconst GIT_SWITCH_CREATE_FLAGS = /* @__PURE__ */ new Set([\n \"--create\",\n \"--force-create\",\n \"--orphan\"\n]);\n/**\n* `git checkout` flags that only work on paths, so the branch stays where it\n* is. Matched on the part before any `=`.\n*/\nconst GIT_CHECKOUT_PATH_FLAGS = /* @__PURE__ */ new Set([\n \"--ours\",\n \"--theirs\",\n \"-p\",\n \"--patch\",\n \"--conflict\",\n \"--pathspec-from-file\",\n \"--pathspec-file-nul\",\n \"--overlay\",\n \"--no-overlay\"\n]);\n/** Short letters that are neutral on both subcommands: `-q`, `-f`, `-m`. */\nconst GIT_SWITCH_NEUTRAL_LETTERS = /* @__PURE__ */ new Set([\n \"q\",\n \"f\",\n \"m\"\n]);\n/**\n* The branch a `git switch` / `git checkout` lands on: the created name for a\n* create form, else its one positional. Undefined when it switches nothing (a\n* checkout of paths, a bare checkout, `HEAD`); null when it lands somewhere\n* the hook cannot name (`-`, `--detach`, `--track`, any flag not listed\n* here). A checkout's one positional can be a path rather than a branch, so\n* it counts only when `isCheckoutBranch` says so.\n*/\nfunction switchTarget(after, sub, isCheckoutBranch) {\n if (sub === \"checkout\" && after.includes(\"--\")) return void 0;\n const createLetters = sub === \"checkout\" ? [\"b\", \"B\"] : [\"c\", \"C\"];\n const known = (l) => GIT_SWITCH_NEUTRAL_LETTERS.has(l) || createLetters.includes(l);\n const positionals = [];\n let created;\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n if (GIT_SWITCH_NEUTRAL_FLAGS.has(t)) continue;\n const eq = t.indexOf(\"=\");\n if (sub === \"checkout\" && GIT_CHECKOUT_PATH_FLAGS.has(eq === -1 ? t : t.slice(0, eq))) return;\n if (GIT_SWITCH_CREATE_FLAGS.has(eq === -1 ? t : t.slice(0, eq))) {\n created = eq === -1 ? after[++i] : t.slice(eq + 1);\n continue;\n }\n const cluster = splitShortOptionCluster(t, sub);\n if (cluster === void 0 || !cluster.letters.every(known)) return null;\n if (cluster.letters.some((l) => createLetters.includes(l))) created = cluster.attachedValue ? cluster.attachedValue : after[++i];\n }\n if (created !== void 0) return created;\n if (positionals.length !== 1) return void 0;\n const target = positionals[0];\n if (target === \"HEAD\" || target === \"@\") return void 0;\n if (sub === \"checkout\" && !isCheckoutBranch(target)) return void 0;\n return target;\n}\n/**\n* The branch a `git switch` / `git checkout` segment leaves checked out, for\n* `evaluateCommand` to carry to the later segments of an `&&` chain. Undefined\n* when the segment is not a switch or switches nothing. `resolved` is false\n* when the segment carries an expansion the tokenizer cannot resolve; the\n* target is then unknown, never guessed.\n*\n* A checkout target that is neither protected nor a commit the repo can\n* resolve is read as a checkout of paths, so the branch stays as it was.\n*/\nfunction branchSwitch(tokens, ctx, resolved) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"switch\" && sub !== \"checkout\") return void 0;\n const dir = gitEffectiveCwd(tokens, ctx.cwd);\n const isCheckoutBranch = (name) => !resolved || getProtectedBranches(dir, ctx.repoProtectedBranches ?? []).has(name) || resolveCommit(dir, name) !== null;\n const target = switchTarget(tokens.slice(tokens.indexOf(sub) + 1), sub, isCheckoutBranch);\n if (!resolved) return target === void 0 ? void 0 : {\n dir,\n branch: null\n };\n return target === void 0 ? void 0 : {\n dir,\n branch: target\n };\n}\n/** Subcommands that move the checked-out branch's ref when they succeed. */\nconst GIT_CHECKED_OUT_WRITES = /* @__PURE__ */ new Set([\n \"commit\",\n \"merge\",\n \"rebase\",\n \"cherry-pick\",\n \"revert\",\n \"am\",\n \"pull\"\n]);\n/**\n* Split the tokens after `sub` into flag-shaped and positional tokens. A flag\n* in `valueFlags` also consumes the next token as its value.\n*/\nfunction subcommandArgs(tokens, sub, valueFlags = /* @__PURE__ */ new Set()) {\n const after = tokens.slice(tokens.indexOf(sub) + 1);\n const flags = [];\n const positionals = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n flags.push(t);\n if (valueFlags.has(t)) i++;\n }\n return {\n flags,\n positionals\n };\n}\n/**\n* `git push` flags whose value is the next token when spelled without `=`.\n* Left unskipped, the value reads as the remote and shifts the real remote\n* into the refspec slot: `git push -o ci.skip origin` would look like a push\n* of the refspec `origin` instead of a push with no refspec.\n*/\nconst GIT_PUSH_VALUE_FLAGS = /* @__PURE__ */ new Set([\n \"-o\",\n \"--push-option\",\n \"--repo\",\n \"--receive-pack\",\n \"--exec\"\n]);\n/**\n* True for `merge --ff-only <ref>` where `<ref>` is the branch's own upstream,\n* spelled out (`origin/main`) or as `@{u}`/`@{upstream}`. Fast-forwarding\n* `main` from its upstream is how a human keeps it current.\n*/\nfunction isMergeFastForwardFromUpstream(tokens, upstream) {\n const { flags, positionals } = subcommandArgs(tokens, \"merge\");\n if (!flags.includes(\"--ff-only\") || positionals.length !== 1) return false;\n const ref = positionals[0];\n return ref === \"@{u}\" || ref === \"@{upstream}\" || upstream !== null && ref === upstream;\n}\n/** `--rebase=<value>` spellings git reads as false, so the pull does not rebase. */\nconst GIT_PULL_NO_REBASE = /* @__PURE__ */ new Set([\n \"--rebase=false\",\n \"--rebase=no\",\n \"--rebase=off\",\n \"--rebase=0\"\n]);\n/**\n* True for a `pull` that can only fast-forward or merge the branch's own\n* upstream: no rebase or `--no-ff`, and either no positional or exactly\n* `<remote> <branch>` naming the upstream. `--rebase` is matched as a prefix\n* (`--rebase=merges`), and `-r` inside any short cluster, so neither spelling\n* slips through; `--rebase=false` and git's other false spellings turn\n* rebasing off rather than on.\n*/\nfunction isPullFromUpstream(tokens, upstream) {\n const { flags, positionals } = subcommandArgs(tokens, \"pull\");\n if (flags.some((t) => t.startsWith(\"--rebase\") && !GIT_PULL_NO_REBASE.has(t) || t === \"--no-ff\" || !t.startsWith(\"--\") && t.slice(1).includes(\"r\"))) return false;\n if (positionals.length === 0) return true;\n if (positionals.length !== 2 || upstream === null) return false;\n const [remote, branch] = positionals;\n return `${remote}/${branch.replace(/^refs\\/heads\\//, \"\")}` === upstream;\n}\n/**\n* True for a `reset` that moves the ref: no `--`, exactly one positional, and\n* that positional resolves to a commit other than `head`'s (`HEAD`, or the\n* branch a switch earlier in the chain checked out). `git reset`,\n* `git reset <path>` and `git reset --hard` unstage or discard work but leave\n* the ref where it is, so they are false.\n*/\nfunction isRefMovingReset(tokens, cwd, head) {\n if (tokens.slice(tokens.indexOf(\"reset\") + 1).includes(\"--\")) return false;\n const { positionals } = subcommandArgs(tokens, \"reset\");\n if (positionals.length !== 1) return false;\n if (head !== \"HEAD\" && /^(HEAD|@)/.test(positionals[0])) return false;\n const target = resolveCommit(cwd, positionals[0]);\n if (target === null) return false;\n return target !== resolveCommit(cwd, head);\n}\n/**\n* The branch names a `git push` writes on the remote. A refspec's destination\n* comes from `refspecDestination`; `HEAD` and `@` stand for the checked-out\n* branch. The first positional is the remote. Returns null when the command\n* names no refspec, so the destination comes from push config.\n*/\nfunction pushDestinations(tokens, current) {\n const { positionals } = subcommandArgs(tokens, \"push\", GIT_PUSH_VALUE_FLAGS);\n const refspecs = positionals.slice(1);\n if (refspecs.length === 0) return null;\n const names = [];\n for (const spec of refspecs) {\n const dst = refspecDestination(spec);\n if (dst === \"HEAD\" || dst === \"@\") {\n if (current !== null) names.push(current);\n continue;\n }\n names.push(dst);\n }\n return names;\n}\n/** `git push` flags that push every local branch rather than named refspecs. */\nconst GIT_PUSH_EVERY_BRANCH = /* @__PURE__ */ new Set([\n \"--all\",\n \"--branches\",\n \"--mirror\"\n]);\n/**\n* True for a checked-out write that cannot move the ref forward: `--abort`\n* and `--quit` leave an operation in progress (a plain `git pull` can leave a\n* conflicted merge that has to be abandoned), and `commit --dry-run` only\n* reports what would be committed. `--continue` and `--skip` are not here,\n* because they do commit.\n*/\nfunction leavesRefInPlace(tokens, sub) {\n const { flags } = subcommandArgs(tokens, sub);\n if (flags.includes(\"--abort\") || flags.includes(\"--quit\")) return true;\n return sub === \"commit\" && flags.includes(\"--dry-run\");\n}\n/**\n* True when a git command would move a protected branch's ref:\n*\n* - With a protected branch checked out (in the effective directory; the\n* branch an earlier `git switch`/`git checkout` in the `&&` chain lands on\n* there, when one did, else the branch on disk): a\n* subcommand in `GIT_CHECKED_OUT_WRITES`, except a fast-forward from the\n* branch's own upstream (`isMergeFastForwardFromUpstream`,\n* `isPullFromUpstream`) and a command that leaves the ref in place\n* (`leavesRefInPlace`); or a `reset` that moves the ref (`isRefMovingReset`).\n* - From any branch: a push of every branch (`--all`, `--branches`,\n* `--mirror`), a push whose refspec destination is protected, or a push\n* with no refspec while a protected branch is checked out, unless it pushes\n* only tags (`--tags`).\n* - From any branch: `switch -C`/`checkout -B`/`worktree add -B`, a\n* `git branch` delete/rename/force, a `fetch` refspec, or `update-ref`, onto\n* a protected name.\n*\n* An unknown branch -- a directory that does not exist yet, a detached HEAD,\n* git failing -- is not a protected one, so the checked-out cases are false\n* there. The subcommand is checked before any git process is spawned.\n*/\nfunction movesProtectedRef(tokens, ctx) {\n const sub = getGitSubcommand(tokens);\n if (sub === void 0) return false;\n if (sub === \"switch\" || sub === \"checkout\" || sub === \"worktree\") return isGitProtectedBranchForceCreate(tokens, ctx);\n if (sub === \"branch\") return isGitProtectedBranchRefWrite(tokens, ctx);\n if (sub === \"fetch\") return isGitProtectedBranchFetchWrite(tokens, ctx);\n if (sub === \"update-ref\") return isGitProtectedUpdateRef(tokens, ctx);\n if (sub !== \"push\" && sub !== \"reset\" && !GIT_CHECKED_OUT_WRITES.has(sub)) return false;\n const cwd = gitEffectiveCwd(tokens, ctx.cwd);\n const pending = ctx.pendingBranches?.[cwd];\n const current = pending ? pending.branch : getCurrentBranch(cwd);\n const protectedSet = getProtectedBranches(cwd, ctx.repoProtectedBranches ?? []);\n const onProtected = current !== null && protectedSet.has(current);\n if (sub === \"push\") {\n const { flags } = subcommandArgs(tokens, \"push\", GIT_PUSH_VALUE_FLAGS);\n if (flags.some((f) => GIT_PUSH_EVERY_BRANCH.has(f))) return true;\n const destinations = pushDestinations(tokens, current);\n if (destinations === null) {\n if (flags.includes(\"--tags\")) return false;\n return onProtected;\n }\n return destinations.some((d) => protectedSet.has(d));\n }\n if (!onProtected) return false;\n if (leavesRefInPlace(tokens, sub)) return false;\n if (sub === \"reset\") return isRefMovingReset(tokens, cwd, pending ? current : \"HEAD\");\n if (sub === \"merge\" || sub === \"pull\") {\n const upstream = getUpstream(cwd, current);\n if (sub === \"merge\" && isMergeFastForwardFromUpstream(tokens, upstream)) return false;\n if (sub === \"pull\" && isPullFromUpstream(tokens, upstream)) return false;\n }\n return true;\n}\n/** pnpm built-in subcommands (not user-defined scripts) */\nconst PNPM_BUILTINS = /* @__PURE__ */ new Set([\n \"install\",\n \"i\",\n \"add\",\n \"remove\",\n \"rm\",\n \"uninstall\",\n \"un\",\n \"update\",\n \"up\",\n \"upgrade\",\n \"list\",\n \"ls\",\n \"ll\",\n \"la\",\n \"why\",\n \"view\",\n \"info\",\n \"show\",\n \"outdated\",\n \"audit\",\n \"licenses\",\n \"init\",\n \"create\",\n \"link\",\n \"unlink\",\n \"pack\",\n \"publish\",\n \"config\",\n \"set\",\n \"get\",\n \"store\",\n \"cache\",\n \"rebuild\",\n \"rb\",\n \"prune\",\n \"dedupe\",\n \"dlx\",\n \"exec\",\n \"run\",\n \"patch\",\n \"patch-commit\",\n \"recursive\",\n \"-r\"\n]);\nconst NPM_READ_OPS = /* @__PURE__ */ new Set([\n \"view\",\n \"info\",\n \"show\"\n]);\n/** True for a read-only npm query (`view`/`info`/`show`) -- everything else denies with a redirect. */\nfunction isNpmReadOp(tokens) {\n const sub = tokens[1];\n return sub !== void 0 && NPM_READ_OPS.has(sub);\n}\n/** Map npm subcommand to a pnpm redirect message */\nfunction getNpmRedirectReason(tokens) {\n const sub = tokens[1];\n const BASE = \"This project uses pnpm.\";\n if (!sub) return `${BASE} Use \\`pnpm\\` instead of npm.`;\n if (sub === \"install\" || sub === \"i\" || sub === \"ci\") {\n if (tokens.slice(2).some((t) => !t.startsWith(\"-\"))) return `${BASE} Use \\`pnpm add <pkg>\\` instead of \\`npm install <pkg>\\`.`;\n return `${BASE} Use \\`pnpm install\\` instead of npm.`;\n }\n if (sub === \"add\") return `${BASE} Use \\`pnpm add <pkg>\\` instead of npm.`;\n if (sub === \"uninstall\" || sub === \"remove\" || sub === \"rm\" || sub === \"un\" || sub === \"r\") return `${BASE} Use \\`pnpm remove <pkg>\\` instead of npm.`;\n if (sub === \"run\" || sub === \"run-script\") return `${BASE} Use \\`pnpm run <script>\\` or \\`pnpm <script>\\` instead of npm.`;\n if (sub === \"test\" || sub === \"t\" || sub === \"tst\") return `${BASE} Use \\`pnpm test\\` instead of npm.`;\n if (sub === \"start\") return `${BASE} Use \\`pnpm start\\` instead of npm.`;\n if (sub === \"stop\") return `${BASE} Use \\`pnpm stop\\` instead of npm.`;\n if (sub === \"restart\") return `${BASE} Use \\`pnpm restart\\` instead of npm.`;\n if (sub === \"exec\") return `${BASE} Use \\`pnpm exec <cmd>\\` instead of npm.`;\n if (sub === \"list\" || sub === \"ls\" || sub === \"ll\" || sub === \"la\") return `${BASE} Use \\`pnpm list\\` instead of npm.`;\n if (sub === \"why\") return `${BASE} Use \\`pnpm why\\` instead of npm.`;\n if (sub === \"outdated\") return `${BASE} Use \\`pnpm outdated\\` instead of npm.`;\n if (sub === \"audit\") return `${BASE} Use \\`pnpm audit\\` instead of npm.`;\n if (sub === \"update\" || sub === \"up\" || sub === \"upgrade\") return `${BASE} Use \\`pnpm update\\` instead of npm.`;\n if (sub === \"init\") return `${BASE} Use \\`pnpm init\\` instead of npm.`;\n if (sub === \"publish\") return `${BASE} Use \\`pnpm publish\\` instead of npm.`;\n if (sub === \"link\") return `${BASE} Use \\`pnpm link\\` instead of npm.`;\n if (sub === \"unlink\") return `${BASE} Use \\`pnpm unlink\\` instead of npm.`;\n if (sub === \"pack\") return `${BASE} Use \\`pnpm pack\\` instead of npm.`;\n if (sub === \"cache\") return `${BASE} Use \\`pnpm store\\` instead of \\`npm cache\\`.`;\n if (sub === \"config\" || sub === \"set\" || sub === \"get\") return `${BASE} Use \\`pnpm config\\` instead of npm.`;\n return `${BASE} Use the equivalent pnpm command instead of npm.`;\n}\n/**\n* Commands whose write destination is the *last* positional argument\n* (`cp src dst`, `mv src dst`, `rsync -a src/ dst/`, `install -m 755 src dst`,\n* `ln -f src dst`).\n*/\nconst LAST_POSITIONAL_DEST = /* @__PURE__ */ new Set([\n \"cp\",\n \"mv\",\n \"rsync\",\n \"install\",\n \"ln\"\n]);\n/** Commands where *every* positional is written to (`tee a b`, `truncate -s 0 f`). */\nconst ALL_POSITIONAL_DEST = /* @__PURE__ */ new Set([\"tee\", \"truncate\"]);\n/** `-t DIR` / `--target-directory[=DIR]` -- an explicit destination directory. */\nfunction explicitTargetDirectory(rest) {\n for (const [i, t] of rest.entries()) {\n if (t === \"-t\" || t === \"--target-directory\") return rest[i + 1] ?? null;\n if (t.startsWith(\"--target-directory=\")) return t.slice(19);\n }\n return null;\n}\n/** An output operator: `>` or `>>`, optionally after a file descriptor number or `&`. */\nconst OUTPUT_REDIRECT = /^(?:\\d*|&)>{1,2}$/;\nconst OUTPUT_REDIRECT_ATTACHED = /^(?:\\d*|&)>{1,2}(.+)$/;\n/**\n* An input operator: a single `<`, optionally after a file descriptor number.\n* `<<` and `<<<` feed inline text rather than a file, so they are not matched.\n*/\nconst INPUT_REDIRECT = /^\\d*<$/;\nconst INPUT_REDIRECT_ATTACHED = /^\\d*<([^<].*)$/;\n/** The direction of a redirect operator written apart from its target, or null. */\nfunction spacedRedirectDirection(token) {\n if (OUTPUT_REDIRECT.test(token)) return \"out\";\n if (INPUT_REDIRECT.test(token)) return \"in\";\n return null;\n}\n/**\n* Split a command's tokens into its file redirects and every other token, in\n* both the spaced (`> f`, `2> f`, `&> f`, `< f`) and attached (`>f`, `2>>f`,\n* `<f`) spellings. A trailing operator with no target is dropped.\n*\n* Checked for *every* command, not just known write tools: the shell splitter\n* leaves redirect tokens inside the segment, so without this an unrecognized\n* command (`some-generator > node_modules/pkg/index.js`) would sail past a\n* command-keyed rule. Over-capture here is harmless -- a bogus target like\n* `&1` from `2>&1` names neither node_modules nor an env file.\n*/\nfunction splitRedirects(tokens) {\n const redirects = [];\n const rest = [];\n for (let i = 0; i < tokens.length; i++) {\n const t = tokens[i];\n const spaced = spacedRedirectDirection(t);\n if (spaced !== null) {\n const next = tokens[i + 1];\n if (next !== void 0) redirects.push({\n direction: spaced,\n target: next\n });\n i++;\n continue;\n }\n const out = OUTPUT_REDIRECT_ATTACHED.exec(t);\n if (out) {\n redirects.push({\n direction: \"out\",\n target: out[1]\n });\n continue;\n }\n const input = INPUT_REDIRECT_ATTACHED.exec(t);\n if (input) {\n redirects.push({\n direction: \"in\",\n target: input[1]\n });\n continue;\n }\n rest.push(t);\n }\n return {\n redirects,\n rest\n };\n}\n/** The files a command's redirects write to (`direction` \"out\") or read from (\"in\"). */\nfunction redirectTargets(tokens, direction) {\n return splitRedirects(tokens).redirects.filter((r) => r.direction === direction).map((r) => r.target);\n}\n/**\n* Every path this command would *write to*. Sources are deliberately excluded:\n* reading out of `node_modules` (`cat node_modules/pkg/package.json`,\n* `cp node_modules/pkg/README.md docs/`) is routine and stays allowed. Only the\n* destination side can corrupt anything.\n*/\nfunction writeDestinations(tokens) {\n const cmd = path.basename(tokens[0] ?? \"\");\n const rest = tokens.slice(1);\n const dests = redirectTargets(tokens, \"out\");\n if (cmd === \"dd\") dests.push(...rest.filter((t) => t.startsWith(\"of=\")).map((t) => t.slice(3)));\n else if (cmd === \"sed\") {\n if (rest.some((t) => t === \"--in-place\" || /^-[a-zA-Z]*i/.test(t))) dests.push(...rest.filter((t) => !t.startsWith(\"-\")));\n } else if (ALL_POSITIONAL_DEST.has(cmd)) dests.push(...rest.filter((t) => !t.startsWith(\"-\")));\n else if (LAST_POSITIONAL_DEST.has(cmd)) {\n if (cmd === \"cp\" && rest.includes(\"--remove-destination\")) return dests;\n const targetDir = explicitTargetDirectory(rest);\n if (targetDir !== null) dests.push(targetDir);\n else {\n const last = rest.findLast((t) => !t.startsWith(\"-\"));\n if (last !== void 0) dests.push(last);\n }\n }\n return dests;\n}\n/**\n* True when the command writes anything into a `node_modules/` tree.\n*\n* See `targetsNodeModules` for why this is a hard deny rather than a prompt:\n* `node_modules` is hardlinked into pnpm's global content-addressable store, so\n* an in-place overwrite silently corrupts that package for every project on the\n* machine -- and the directory doing the writing looks perfectly fine afterward.\n* The 2026-07-13 store poisoning was exactly this, and it was *human-approved*\n* at the prompt, which is why a `none` result would not have stopped it.\n*/\nfunction writesIntoNodeModules(tokens, ctx) {\n return writeDestinations(tokens).some((d) => targetsNodeModules(d, ctx.cwd));\n}\n/** Shared by the `node-modules-write` deny rule and the early check in `evaluate.ts`. */\nconst NODE_MODULES_WRITE_REASON = \"Don't write into node_modules/. Its files are hardlinks into pnpm's global store, so overwriting one in place (plain `cp`, `>` redirect, `sed -i`, ...) writes through the link and corrupts that package for every project on this machine -- silently, since the directory you wrote into ends up fine. To test a local build as an installed dependency, run it directly (`node packages/<pkg>/dist/bin.mjs <cmd>`), or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`). If you must replace a file in place, `cp --remove-destination` is safe -- it unlinks the target first. Run `pnpm store status` to check whether the store is already damaged.\";\nfunction isChmod777(tokens) {\n return tokens.some((t) => t === \"777\" || t === \"0777\");\n}\nfunction isGitForcePush(tokens) {\n const pushIdx = tokens.indexOf(\"push\");\n if (pushIdx === -1) return false;\n return tokens.slice(pushIdx + 1).some((t) => t === \"--force\" || t === \"-f\");\n}\n/**\n* Commands whose bare positional arguments are files to read verbatim -- no\n* argument among them is a search pattern the way grep's is.\n*/\nconst ENV_READ_FILE_COMMANDS = /* @__PURE__ */ new Set([\n \"cat\",\n \"head\",\n \"tail\",\n \"less\",\n \"more\",\n \"source\",\n \".\"\n]);\n/** grep-family commands: their first positional is normally a search pattern, not a file. */\nconst GREP_FAMILY_COMMANDS = /* @__PURE__ */ new Set([\n \"grep\",\n \"rg\",\n \"ag\"\n]);\n/**\n* The file operands of a grep-family (`grep`/`rg`/`ag`) invocation, given its\n* tokens after the command name with redirects already stripped.\n*\n* This walks the tokens instead of filtering positionals and asking \"was a\n* pattern flag present anywhere\" as a single yes/no, because the two pattern\n* flags disagree about what to do with their *own* value once one is found:\n*\n* - `-e`/`--regexp` (bare, or attached as `-eX`/`--regexp=X`) supply the\n* search pattern directly, so that value is a pattern, never a file, and is\n* dropped -- `grep -e process.env src/` and `grep -rn -e process.env src/`\n* must not deny just because the pattern happens to look like a filename,\n* the same false positive the no-flag case below exists to avoid.\n* - `-f`/`--file` (bare, or attached as `-fX`/`--file=X`) name a *file of\n* patterns*, which is a real file read, so that value is kept as an\n* operand -- `grep -f pats.txt .env.local` must still deny on `.env.local`.\n*\n* Absent either flag, the first bare positional is the search pattern and\n* every one after it is a file -- `grep -n \"process.env\" src/x.ts` is the\n* false positive that motivated all of this: the pattern string\n* \"process.env\" matches the `.env` filename pattern just as well as a real\n* path does, so it must never be checked as a file operand.\n*\n* A `--` ends option parsing (see `optionsEnded` below): every token after it\n* is a literal positional even when it looks like `-e` or `-f`, following the\n* same \"first one is the pattern, absent an earlier flag\" rule -- `grep --\n* -e .env` passes `-e` as the pattern and `.env` as the file, and `grep --\n* .env src/x.ts` passes `.env` itself as the pattern (not a file) and\n* `src/x.ts` as the file.\n*/\nfunction grepFileOperands(rest) {\n const files = [];\n let patternAccountedFor = false;\n let optionsEnded = false;\n for (let i = 0; i < rest.length; i++) {\n const t = rest[i];\n if (!optionsEnded && t === \"--\") {\n optionsEnded = true;\n continue;\n }\n if (!optionsEnded) {\n if (t === \"-f\" || t === \"--file\") {\n const value = rest[i + 1];\n if (value !== void 0) {\n files.push(value);\n i++;\n }\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"--file=\")) {\n files.push(t.slice(7));\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"-f\") && t !== \"-f\" && !t.startsWith(\"--\")) {\n files.push(t.slice(2));\n patternAccountedFor = true;\n continue;\n }\n if (t === \"-e\" || t === \"--regexp\") {\n i++;\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"--regexp=\") || t.startsWith(\"-e\") && t !== \"-e\" && !t.startsWith(\"--\")) {\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"-\")) continue;\n }\n if (!patternAccountedFor) {\n patternAccountedFor = true;\n continue;\n }\n files.push(t);\n }\n return files;\n}\n/**\n* Every file a command reads: each input redirect target, on any command,\n* plus the file operands of a `cat`/`head`/`tail`/`less`/`more`/`source`/`.`/\n* `grep`/`rg`/`ag` command. Backs the `env-file-read` rule. grep/rg/ag\n* delegate to `grepFileOperands` (above); every other command named here has\n* no search pattern to exclude, so every bare positional is a file operand.\n* Output redirects are left out: they write, and `env-file-write` owns them.\n*/\nfunction envFileOperands(tokens) {\n const cmd = path.basename(tokens[0] ?? \"\");\n const { redirects, rest } = splitRedirects(tokens.slice(1));\n const operands = redirects.filter((r) => r.direction === \"in\").map((r) => r.target);\n if (GREP_FAMILY_COMMANDS.has(cmd)) operands.push(...grepFileOperands(rest));\n else if (ENV_READ_FILE_COMMANDS.has(cmd)) operands.push(...rest.filter((t) => !t.startsWith(\"-\")));\n return operands;\n}\nconst denyRules = [\n {\n name: \"sudo\",\n match: /^sudo$/,\n action: \"deny\",\n reason: \"Don't use sudo. If you need elevated permissions, ask me to do it manually.\"\n },\n {\n name: \"git-force-push\",\n match: /^git$/,\n action: \"deny\",\n when: (tokens) => isGitForcePush(tokens),\n reason: \"Use `git push --force-with-lease` instead of `--force`.\"\n },\n {\n name: \"git-protected-branch-write\",\n match: /^git$/,\n action: \"deny\",\n when: (tokens, ctx) => movesProtectedRef(tokens, ctx),\n reason: \"This would move a protected branch (main, master, the remote's default branch, or one listed in .claude/hooks/bst-hook.settings.json#protectedBranches). Work on a feature branch in a worktree: `git worktree add ../<repo>-<topic> -b feat/<topic>`, then commit and push there. To bring `main` up to date, use `git pull --ff-only`.\"\n },\n {\n name: \"chmod-777\",\n match: /^chmod$/,\n action: \"deny\",\n when: (tokens) => isChmod777(tokens),\n reason: \"Don't use 777 permissions. Use more restrictive permissions.\"\n },\n {\n name: \"node-modules-write\",\n match: /./,\n action: \"deny\",\n when: (tokens, ctx) => writesIntoNodeModules(tokens, ctx),\n reason: NODE_MODULES_WRITE_REASON\n },\n {\n name: \"env-file-read\",\n match: /./,\n action: \"deny\",\n when: (tokens) => envFileOperands(tokens).some(isEnvFile),\n reason: \"Don't read .env files. These contain secrets that should not appear in the conversation.\"\n },\n {\n name: \"env-file-write\",\n match: /./,\n action: \"deny\",\n when: (tokens) => redirectTargets(tokens, \"out\").some(isEnvFile),\n reason: \"Don't write to .env files. These contain secrets.\"\n },\n {\n name: \"npx-redirect\",\n match: /^npx$/,\n action: \"deny\",\n reason: \"This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\"\n },\n {\n name: \"mise-self-update\",\n match: /^mise$/,\n action: \"deny\",\n when: (tokens) => tokens[1] === \"self-update\",\n reason: \"Don't self-update mise. The version is Renovate-managed; ask a human to bump the pin.\"\n }\n];\n\n//#endregion\n//#region src/lib/script-resolver.ts\nconst PackageJsonSchema = object({ scripts: record(string(), unknown()) });\n/**\n* Resolve a pnpm script name to its actual command(s) by reading package.json.\n*\n* Returns null if the script cannot be resolved (missing package.json,\n* missing script name, parse error).\n*/\nfunction resolveScript(scriptName, cwd) {\n const scripts = readScripts(cwd);\n if (!scripts) return null;\n const main = scripts[scriptName];\n if (typeof main !== \"string\") return null;\n const pre = scripts[`pre${scriptName}`];\n const post = scripts[`post${scriptName}`];\n return {\n pre: typeof pre === \"string\" ? pre : null,\n main,\n post: typeof post === \"string\" ? post : null\n };\n}\n/**\n* Resolve a script in a specific workspace package identified by --filter.\n*\n* Reads pnpm-workspace.yaml, scans workspace directories to match filterPkg\n* against package.json#name fields, then resolves the script in that package's directory.\n*\n* Returns null if the package or script cannot be resolved.\n*/\nfunction resolveFilteredScript(scriptName, filterPkg, cwd) {\n const pkgDir = findWorkspacePackageDir(filterPkg, cwd);\n if (!pkgDir) return null;\n const script = resolveScript(scriptName, pkgDir);\n if (!script) return null;\n return {\n ...script,\n pkgDir\n };\n}\n/**\n* Resolve a script name across all workspace packages (for `pnpm -r <script>`).\n*\n* Returns an array of resolved scripts (one per workspace package that defines the script),\n* each with a `pkgDir` field. Returns null if workspace patterns can't be read.\n* Packages that don't define the script are silently skipped.\n*/\nfunction resolveRecursiveScripts(scriptName, rootDir) {\n const pkgDirs = listWorkspacePackageDirs(rootDir);\n if (!pkgDirs) return null;\n const results = [];\n for (const pkgDir of pkgDirs) {\n const resolved = resolveScript(scriptName, pkgDir);\n if (resolved) results.push({\n ...resolved,\n pkgDir\n });\n }\n return results;\n}\n/**\n* List all workspace package directories.\n*/\nfunction listWorkspacePackageDirs(rootDir) {\n const patterns = readWorkspacePatterns(rootDir);\n if (!patterns) return null;\n const dirs = [];\n for (const pattern of patterns) {\n if (!pattern.endsWith(\"/*\")) continue;\n const baseDir = pattern.slice(0, -2);\n const parentDir = resolve(rootDir, baseDir);\n let entries;\n try {\n entries = readdirSync(parentDir);\n } catch {\n continue;\n }\n for (const entry of entries) {\n const entryDir = join(parentDir, entry);\n try {\n if (!statSync(entryDir).isDirectory()) continue;\n } catch {\n continue;\n }\n dirs.push(entryDir);\n }\n }\n return dirs.length > 0 ? dirs : null;\n}\n/**\n* Find the directory of a workspace package by name.\n* Reads pnpm-workspace.yaml for glob patterns, then scans matching directories.\n*/\nfunction findWorkspacePackageDir(pkgName, rootDir) {\n const pkgDirs = listWorkspacePackageDirs(rootDir);\n if (!pkgDirs) return null;\n for (const entryDir of pkgDirs) {\n const name = readPackageName(entryDir);\n if (name === pkgName || name === `@${pkgName}`) return entryDir;\n if (entryDir.split(\"/\").pop() === pkgName) return entryDir;\n if (name) {\n if ((name.startsWith(\"@\") ? name.split(\"/\")[1] : name) === pkgName) return entryDir;\n }\n }\n return null;\n}\n/**\n* Read workspace glob patterns from pnpm-workspace.yaml.\n*/\nfunction readWorkspacePatterns(rootDir) {\n try {\n const lines = readFileSync(join(rootDir, \"pnpm-workspace.yaml\"), \"utf-8\").split(\"\\n\");\n const packagesIdx = lines.findIndex((l) => /^packages\\s*:/.test(l));\n if (packagesIdx === -1) return null;\n const patterns = [];\n for (let i = packagesIdx + 1; i < lines.length; i++) {\n const line = lines[i];\n const match = /^\\s+-\\s+(.+)$/.exec(line);\n if (!match) break;\n patterns.push(match[1].trim().replace(/['\"`]/g, \"\"));\n }\n return patterns.length > 0 ? patterns : null;\n } catch {\n return null;\n }\n}\nconst PackageNameSchema = object({ name: string() });\n/**\n* Read the name field from package.json in the given directory.\n*/\nfunction readPackageName(dir) {\n try {\n const raw = readFileSync(join(dir, \"package.json\"), \"utf-8\");\n const result = PackageNameSchema.safeParse(JSON.parse(raw));\n if (!result.success) return null;\n return result.data.name;\n } catch {\n return null;\n }\n}\n/**\n* Read the scripts field from package.json in the given directory.\n* Returns null on any failure (missing file, parse error, no scripts field).\n*/\nfunction readScripts(cwd) {\n try {\n const raw = readFileSync(join(cwd, \"package.json\"), \"utf-8\");\n const result = PackageJsonSchema.safeParse(JSON.parse(raw));\n if (!result.success) return null;\n return result.data.scripts;\n } catch {\n return null;\n }\n}\n\n//#endregion\n//#region src/lib/workspace-root.ts\n/**\n* Walk up from `cwd` to the directory holding `pnpm-workspace.yaml`.\n*\n* Its own module rather than a `script-resolver.ts` export because\n* `settings.ts` needs it and `script-resolver.ts` needs `settings.ts`,\n* which is a cycle the health gate reports.\n*/\nfunction findWorkspaceRoot(cwd) {\n let dir = resolve(cwd);\n while (true) {\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n//#endregion\n//#region src/evaluate.ts\nconst MAX_EVAL_DEPTH = 5;\n/**\n* The body of a command that is wholly one subshell -- its first `(` closes at\n* its last character -- or null. `(a) && (b)` is not one subshell: its first\n* `(` closes early. Parentheses inside single or double quotes, or escaped\n* with a backslash, are not counted.\n*/\nfunction subshellBody(command) {\n if (!command.startsWith(\"(\") || !command.endsWith(\")\")) return null;\n let depth = 0;\n let quote = null;\n for (let i = 0; i < command.length; i++) {\n const ch = command[i];\n if (quote !== null) {\n if (ch === quote) quote = null;\n else if (ch === \"\\\\\" && quote === \"\\\"\") i++;\n continue;\n }\n if (ch === \"\\\\\") i++;\n else if (ch === \"'\" || ch === \"\\\"\") quote = ch;\n else if (ch === \"(\") depth++;\n else if (ch === \")\") {\n depth--;\n if (depth === 0) return i === command.length - 1 ? command.slice(1, -1) : null;\n }\n }\n return null;\n}\n/**\n* Extract the target directory from a `cd <dir>` segment.\n* Returns the resolved absolute path, or null if the segment isn't a simple `cd`.\n*/\nfunction extractCdDir(segment, ctx) {\n const tokenResult = tokenize(segment);\n if (!tokenResult.ok || tokenResult.tokens.length !== 2) return null;\n const tokens = tokenResult.tokens;\n if (tokens[0] !== \"cd\") return null;\n const dir = tokens[1];\n if (path.isAbsolute(dir)) return dir;\n return path.resolve(ctx.cwd, dir);\n}\n/**\n* The branch a `git switch` / `git checkout` segment leaves checked out, or\n* undefined when the segment is not one (see `branchSwitch`).\n*/\nfunction extractBranchSwitch(segment, ctx) {\n const tokenResult = tokenize(segment);\n if (!tokenResult.ok) return void 0;\n let tokens = tokenResult.tokens;\n let envSkipped = 0;\n while (envSkipped < tokens.length && /^\\w+=/.test(tokens[envSkipped])) envSkipped++;\n tokens = tokens.slice(envSkipped);\n if (tokens.length === 0 || path.basename(tokens[0]) !== \"git\") return void 0;\n return branchSwitch(tokens, ctx, checkInjection(tokens, segment).safe);\n}\n/**\n* Evaluate a full command string. Splits on operators and evaluates each segment.\n*\n* This is the single reentrant entry point. Called at depth 0 for top-level\n* commands and at depth 1+ for derived commands (resolved scripts, exec tails, etc.).\n*/\nfunction evaluateCommand(command, ctx) {\n if (ctx.depth >= MAX_EVAL_DEPTH) return {\n action: \"none\",\n rule: null,\n reason: \"max evaluation depth exceeded\"\n };\n const trimmed = command.trim();\n if (!trimmed) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n const body = subshellBody(trimmed);\n if (body !== null) return evaluateCommand(body, {\n ...ctx,\n depth: ctx.depth + 1\n });\n const segments = splitCommand(trimmed);\n if (segments.length === 0) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n let firstNone = null;\n const currentCtx = { ...ctx };\n let unknownAfterNext;\n for (const segment of segments) {\n if (segment.operator === \"&&\") {\n const cdDir = extractCdDir(segment.command, currentCtx);\n if (cdDir) currentCtx.cwd = cdDir;\n }\n const result = evaluateSegment(segment.command, currentCtx);\n if (result.action === \"deny\") return result;\n if (!firstNone) firstNone = result;\n if (unknownAfterNext) {\n currentCtx.pendingBranches = {\n ...currentCtx.pendingBranches,\n [unknownAfterNext.dir]: unknownAfterNext\n };\n unknownAfterNext = void 0;\n }\n const switched = extractBranchSwitch(segment.command, currentCtx);\n if (switched) {\n const unknown = {\n dir: switched.dir,\n branch: null\n };\n if (segment.operator === \"&&\") currentCtx.pendingBranches = {\n ...currentCtx.pendingBranches,\n [switched.dir]: switched\n };\n else if (segment.operator === \"||\") unknownAfterNext = unknown;\n else currentCtx.pendingBranches = {\n ...currentCtx.pendingBranches,\n [switched.dir]: unknown\n };\n }\n }\n return firstNone ?? {\n action: \"none\",\n rule: null,\n reason: null\n };\n}\n/**\n* Helper to evaluate a resolved script with its pre and post hooks.\n*\n* There is no allow tier, so there is nothing to \"trust\" a clean package.json\n* into skipping: every resolved script -- clean or not -- is evaluated for\n* denies the same way a top-level command is. A deny can only get stronger by\n* running the check, never weaker, so there is no safety reason to skip it.\n*/\nfunction evaluateResolvedScript(resolved, ctx) {\n if (resolved.pre) {\n const preResult = evaluateCommand(resolved.pre, ctx);\n if (preResult.action === \"deny\") return preResult;\n }\n const mainResult = evaluateCommand(resolved.main, ctx);\n if (mainResult.action === \"deny\") return mainResult;\n if (resolved.post) {\n const postResult = evaluateCommand(resolved.post, ctx);\n if (postResult.action === \"deny\") return postResult;\n }\n return {\n action: \"none\",\n rule: null,\n reason: null\n };\n}\n/**\n* Evaluate a single command segment (no operators).\n* May recurse into evaluateCommand() for indirection constructs.\n*/\nfunction evaluateSegment(rawSegment, ctx) {\n const trimmed = rawSegment.trim();\n const body = subshellBody(trimmed);\n if (body !== null) return evaluateCommand(body, {\n ...ctx,\n depth: ctx.depth + 1\n });\n if (trimmed.startsWith(\"(\") && trimmed.endsWith(\")\")) return {\n action: \"none\",\n rule: null,\n reason: \"subshell\"\n };\n const tokenResult = tokenize(rawSegment);\n if (!tokenResult.ok) return {\n action: \"none\",\n rule: null,\n reason: tokenResult.error\n };\n let tokens = tokenResult.tokens;\n if (tokens.length === 0) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n let envSkipped = 0;\n while (envSkipped < tokens.length && /^\\w+=/.test(tokens[envSkipped])) envSkipped++;\n if (envSkipped >= tokens.length) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n tokens = tokens.slice(envSkipped);\n if (writesIntoNodeModules(tokens, ctx)) return {\n action: \"deny\",\n rule: \"node-modules-write\",\n reason: NODE_MODULES_WRITE_REASON\n };\n const injection = checkInjection(tokens, rawSegment);\n if (!injection.safe) return {\n action: \"none\",\n rule: null,\n reason: injection.reason\n };\n const SAFE_REDIRECT_TOKENS = /* @__PURE__ */ new Set([\n \"2>&1\",\n \"2>/dev/null\",\n \">/dev/null\",\n \"1>/dev/null\"\n ]);\n tokens = tokens.filter((t, i) => {\n if (SAFE_REDIRECT_TOKENS.has(t)) return false;\n if ((t === \">\" || t === \">>\") && tokens[i + 1] === \"/dev/null\") return false;\n if (t === \"/dev/null\" && i > 0 && (tokens[i - 1] === \">\" || tokens[i - 1] === \">>\")) return false;\n return true;\n });\n const cmdName = path.basename(tokens[0]);\n if (cmdName === \"command\" && tokens.length > 1) return evaluateCommand(joinTokens(tokens.slice(1)), {\n ...ctx,\n depth: ctx.depth + 1\n });\n if (cmdName === \"env\") {\n let envIdx = 1;\n while (envIdx < tokens.length && /^\\w+=/.test(tokens[envIdx])) envIdx++;\n if (envIdx >= tokens.length) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n return evaluateCommand(joinTokens(tokens.slice(envIdx)), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n if (cmdName === \"pnpm\") return evaluatePnpm(tokens, ctx);\n if (cmdName === \"npm\") return evaluateNpm(tokens);\n if (cmdName === \"mise\" && tokens[1] === \"exec\") {\n const inner = miseExecInner(tokens);\n if (!inner) return {\n action: \"none\",\n rule: null,\n reason: \"bare mise exec\"\n };\n return evaluateCommand(joinTokens(inner), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n if (cmdName === \"bash\" || cmdName === \"sh\" || cmdName === \"zsh\") return evaluateShellC(tokens, ctx);\n if (cmdName === \"xargs\") return evaluateXargs(tokens, ctx);\n if (cmdName === \"node\" && (tokens.includes(\"-e\") || tokens.includes(\"-p\") || tokens.includes(\"--eval\") || tokens.includes(\"--print\"))) return {\n action: \"none\",\n rule: null,\n reason: \"node eval\"\n };\n for (const rule of denyRules) if (rule.match.test(cmdName)) {\n if (!rule.when || rule.when(tokens, ctx)) return {\n action: \"deny\",\n rule: rule.name,\n reason: rule.reason ?? null\n };\n }\n const advice = cmdName === \"git\" ? worktreeAdvice(tokens, ctx) : void 0;\n return {\n action: \"none\",\n rule: null,\n reason: `no rule matched: ${cmdName}`,\n ...advice ? { advice } : {}\n };\n}\n/**\n* The inner command of `mise exec [tool@version...] -- <cmd...>`: everything after the `--`\n* separator. `undefined` when there is no `--` (a bare `mise exec` with nothing to unwrap) or\n* nothing follows it.\n*/\nfunction miseExecInner(tokens) {\n const dashIdx = tokens.indexOf(\"--\");\n if (dashIdx === -1) return void 0;\n const inner = tokens.slice(dashIdx + 1);\n return inner.length > 0 ? inner : void 0;\n}\n/**\n* Scan pnpm subTokens starting at `from`, skipping and accumulating flags\n* (--filter / -F, -r, -w, -C, and generic --flags). Returns the first non-flag\n* token as `name`, or `undefined` if none found. Returns `\"filter\"` as a\n* sentinel when --filter is encountered (caller must handle).\n*/\nfunction scanPnpmFlags(subTokens, from, state) {\n for (let i = from; i < subTokens.length; i++) {\n const t = subTokens[i];\n if (t === \"--filter\" || t.startsWith(\"--filter=\") || t === \"-F\" || t.startsWith(\"-F=\")) return {\n name: void 0,\n filterIdx: i\n };\n if (t === \"-r\" || t === \"--recursive\") {\n state.isRecursive = true;\n continue;\n }\n if (t === \"-w\" || t === \"--workspace-root\") {\n state.isWorkspaceRoot = true;\n continue;\n }\n if (t === \"-C\" || t === \"--dir\") {\n state.dirOverride = subTokens[++i];\n continue;\n }\n if (t.startsWith(\"-C=\") || t.startsWith(\"--dir=\")) {\n state.dirOverride = t.split(\"=\", 2)[1];\n continue;\n }\n if (t.startsWith(\"-\") && !PNPM_BUILTINS.has(t)) continue;\n return { name: t };\n }\n return { name: void 0 };\n}\n/**\n* Evaluate a pnpm command. Classifies the subcommand as built-in, exec, or script.\n* Script commands are resolved via package.json and recursively evaluated.\n*/\nfunction evaluatePnpm(tokens, ctx) {\n const subTokens = tokens.slice(1);\n const state = {\n isRecursive: false,\n isWorkspaceRoot: false,\n dirOverride: void 0\n };\n const scan = scanPnpmFlags(subTokens, 0, state);\n if (scan.filterIdx !== void 0) return evaluatePnpmFilter(tokens, subTokens, scan.filterIdx, ctx);\n const subcommand = scan.name;\n const subcommandIdx = subcommand ? subTokens.indexOf(subcommand) : -1;\n if (!subcommand) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n if (subcommand === \"dlx\") return {\n action: \"none\",\n rule: null,\n reason: \"pnpm dlx\"\n };\n if (subcommand === \"add\") return {\n action: \"none\",\n rule: null,\n reason: \"pnpm add\"\n };\n if (subcommand === \"exec\") {\n const execArgs = subTokens.slice(subcommandIdx + 1);\n if (execArgs.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"bare pnpm exec\"\n };\n return evaluateCommand(joinTokens(execArgs), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n let scriptName;\n let { isRecursive, isWorkspaceRoot, dirOverride } = state;\n if (subcommand === \"run\") {\n const runScan = scanPnpmFlags(subTokens, subcommandIdx + 1, state);\n ({isRecursive, isWorkspaceRoot, dirOverride} = state);\n if (runScan.filterIdx !== void 0) return evaluatePnpmFilter(tokens, subTokens, runScan.filterIdx, ctx);\n if (!runScan.name) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm run without script name\"\n };\n scriptName = runScan.name;\n } else if (!PNPM_BUILTINS.has(subcommand)) scriptName = subcommand;\n else return {\n action: \"none\",\n rule: null,\n reason: null\n };\n if (isRecursive) return evaluatePnpmRecursive(scriptName, ctx);\n if (isWorkspaceRoot) {\n const root = findWorkspaceRoot(ctx.cwd);\n if (!root) return {\n action: \"none\",\n rule: null,\n reason: \"workspace root not found\"\n };\n return evaluatePnpmScript(scriptName, {\n ...ctx,\n cwd: root\n });\n }\n if (dirOverride) {\n const resolved = path.resolve(ctx.cwd, dirOverride);\n return evaluatePnpmScript(scriptName, {\n ...ctx,\n cwd: resolved\n });\n }\n return evaluatePnpmScript(scriptName, ctx);\n}\n/**\n* Resolve a pnpm script name via package.json and recursively evaluate\n* the resolved command(s) including pre/post scripts.\n*/\nfunction evaluatePnpmScript(scriptName, ctx) {\n const resolved = resolveScript(scriptName, ctx.cwd);\n if (!resolved) return {\n action: \"none\",\n rule: null,\n reason: \"script resolution failed\"\n };\n const res = evaluateResolvedScript(resolved, {\n ...ctx,\n depth: ctx.depth + 1\n });\n if (res.action === \"deny\") return res;\n return {\n action: \"none\",\n rule: `pnpm-script:${scriptName}`,\n reason: null\n };\n}\n/**\n* Evaluate `pnpm -r <script>` by resolving the script in each workspace package.\n*\n* Unlike `evaluatePnpmScript` (which resolves from the root package.json and would\n* cycle back to `pnpm -r <script>`), this resolves against each workspace package's\n* own package.json, breaking the recursion.\n*/\nfunction evaluatePnpmRecursive(scriptName, ctx) {\n const resolved = resolveRecursiveScripts(scriptName, ctx.cwd);\n if (!resolved || resolved.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm recursive\"\n };\n for (const pkg of resolved) {\n const res = evaluateResolvedScript(pkg, {\n ...ctx,\n depth: ctx.depth + 1,\n cwd: pkg.pkgDir ?? ctx.cwd\n });\n if (res.action === \"deny\") return res;\n }\n return {\n action: \"none\",\n rule: `pnpm-recursive:${scriptName}`,\n reason: null\n };\n}\n/**\n* Evaluate an npm command. Read-only ops (view/info/show) pass through;\n* everything else denies with a redirect message to the pnpm equivalent.\n*/\nfunction evaluateNpm(tokens) {\n if (isNpmReadOp(tokens)) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n return {\n action: \"deny\",\n rule: \"npm-redirect\",\n reason: getNpmRedirectReason(tokens)\n };\n}\n/**\n* Evaluate `pnpm --filter <pkg> [--filter <pkg> ...] <subcommand>`.\n*\n* Collects every `--filter` / `-F` flag (long, equals, and shorthand forms),\n* resolves each against pnpm-workspace.yaml, then classifies the subcommand as\n* built-in or script. Script commands are resolved and evaluated in each\n* filtered package -- all must allow.\n*\n* Falls back to none if workspace resolution fails.\n*/\nfunction evaluatePnpmFilter(_tokens, subTokens, filterIdx, ctx) {\n const filterPkgs = [];\n let cursor = filterIdx;\n while (cursor < subTokens.length) {\n const t = subTokens[cursor];\n if (t === \"--filter\" || t === \"-F\") {\n const pkg = subTokens[cursor + 1];\n if (!pkg || pkg.startsWith(\"-\")) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without package\"\n };\n filterPkgs.push(pkg);\n cursor += 2;\n } else if (t.startsWith(\"--filter=\") || t.startsWith(\"-F=\")) {\n const pkg = t.startsWith(\"-F=\") ? t.slice(3) : t.slice(9);\n if (!pkg) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without package\"\n };\n filterPkgs.push(pkg);\n cursor += 1;\n } else break;\n }\n if (filterPkgs.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without package\"\n };\n let filterSubcommand;\n let subcmdIdx = -1;\n for (let i = cursor; i < subTokens.length; i++) {\n const t = subTokens[i];\n if (t === \"--filter\" || t === \"-F\") {\n i += 1;\n continue;\n }\n if (t.startsWith(\"--filter=\") || t.startsWith(\"-F=\")) continue;\n if (t.startsWith(\"-\") && !PNPM_BUILTINS.has(t)) continue;\n filterSubcommand = t;\n subcmdIdx = i;\n break;\n }\n if (!filterSubcommand) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without subcommand\"\n };\n if (PNPM_BUILTINS.has(filterSubcommand) && filterSubcommand !== \"run\" && filterSubcommand !== \"exec\") return {\n action: \"none\",\n rule: null,\n reason: null\n };\n if (filterSubcommand === \"exec\") {\n const execArgs = subTokens.slice(subcmdIdx + 1);\n if (execArgs.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"bare pnpm exec\"\n };\n return evaluateCommand(joinTokens(execArgs), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n const scriptName = filterSubcommand === \"run\" ? subTokens[subcmdIdx + 1] : filterSubcommand;\n if (!scriptName || scriptName.startsWith(\"-\")) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter run without script name\"\n };\n if (PNPM_BUILTINS.has(scriptName) && scriptName !== \"run\") return {\n action: \"none\",\n rule: null,\n reason: null\n };\n for (const pkg of filterPkgs) {\n const resolved = resolveFilteredScript(scriptName, pkg, ctx.cwd);\n if (!resolved) return {\n action: \"none\",\n rule: null,\n reason: \"filtered script resolution failed\"\n };\n const res = evaluateResolvedScript(resolved, {\n ...ctx,\n depth: ctx.depth + 1,\n cwd: resolved.pkgDir ?? ctx.cwd\n });\n if (res.action === \"deny\") return res;\n }\n return {\n action: \"none\",\n rule: `pnpm-filter:${filterPkgs.join(\",\")}:${scriptName}`,\n reason: null\n };\n}\n/** A login flag: the shell reads its profile first, then runs `-c` as usual. */\nconst SHELL_LOGIN_FLAGS = /* @__PURE__ */ new Set([\"-l\", \"--login\"]);\n/**\n* Evaluate `bash -c` / `sh -c` / `zsh -c` commands.\n*\n* Unwraps only `<shell> [-l|--login]... -c <command-string>`, with `-c` also\n* accepted inside a cluster of `l` and `c` (`-lc`, `-cl`). A login shell reads\n* its profile before the command, which can change what runs but never makes\n* a denied command safe. Any other flag, a positional after the command\n* string (it sets `$0`, `$1`, ...), or a missing `-c` -> none.\n*/\nfunction evaluateShellC(tokens, ctx) {\n let i = 1;\n while (i < tokens.length && SHELL_LOGIN_FLAGS.has(tokens[i])) i++;\n const flag = tokens[i];\n if (flag === void 0 || !/^-[lc]+$/.test(flag) || !flag.includes(\"c\")) return {\n action: \"none\",\n rule: null,\n reason: \"shell without a plain -c\"\n };\n const cmdArg = tokens[i + 1];\n if (!cmdArg) return {\n action: \"none\",\n rule: null,\n reason: \"shell -c without command\"\n };\n if (tokens.length > i + 2) return {\n action: \"none\",\n rule: null,\n reason: \"shell -c with positional args\"\n };\n return evaluateCommand(cmdArg, {\n ...ctx,\n depth: ctx.depth + 1\n });\n}\n/** xargs flags that consume the next token as an argument */\nconst XARGS_ARG_FLAGS = /* @__PURE__ */ new Set([\n \"-I\",\n \"-d\",\n \"-n\",\n \"-P\",\n \"-s\",\n \"-L\",\n \"-E\",\n \"-a\",\n \"--max-args\",\n \"--max-procs\",\n \"--delimiter\",\n \"--replace\",\n \"--max-lines\",\n \"--max-chars\",\n \"--arg-file\",\n \"--process-slot-var\"\n]);\n/** xargs flags that are boolean (no argument) */\nconst XARGS_BOOL_FLAGS = /* @__PURE__ */ new Set([\n \"-0\",\n \"--null\",\n \"-t\",\n \"--verbose\",\n \"-r\",\n \"--no-run-if-empty\",\n \"-p\",\n \"--interactive\",\n \"-x\",\n \"--exit\",\n \"--show-limits\"\n]);\n/**\n* Evaluate `xargs <cmd>` commands.\n*\n* Skips xargs flags to find the inner command, then recursively evaluates it.\n* None if no command follows the flags or if unrecognised flags are found.\n*/\nfunction evaluateXargs(tokens, ctx) {\n let i = 1;\n while (i < tokens.length) {\n const t = tokens[i];\n if (!t.startsWith(\"-\")) break;\n if (XARGS_ARG_FLAGS.has(t)) {\n i += 2;\n continue;\n }\n const eqIdx = t.indexOf(\"=\");\n if (eqIdx !== -1) {\n const flagName = t.slice(0, eqIdx);\n if (XARGS_ARG_FLAGS.has(flagName)) {\n i++;\n continue;\n }\n }\n if (t.length > 2 && XARGS_ARG_FLAGS.has(t.slice(0, 2))) {\n i++;\n continue;\n }\n if (XARGS_BOOL_FLAGS.has(t)) {\n i++;\n continue;\n }\n return {\n action: \"none\",\n rule: null,\n reason: \"xargs with unrecognised flags\"\n };\n }\n if (i >= tokens.length) return {\n action: \"none\",\n rule: null,\n reason: \"xargs without command\"\n };\n return evaluateCommand(joinTokens(tokens.slice(i)), {\n ...ctx,\n depth: ctx.depth + 1\n });\n}\n\n//#endregion\n//#region src/file-rules.ts\nconst READ_TOOLS = /* @__PURE__ */ new Set([\n \"Read\",\n \"Glob\",\n \"Grep\"\n]);\nconst WRITE_TOOLS = /* @__PURE__ */ new Set([\"Write\", \"Edit\"]);\nconst fileDenyRules = [\n {\n name: \"env-file-read\",\n tools: READ_TOOLS,\n action: \"deny\",\n when: (filePath) => isEnvFile(filePath),\n reason: \"Don't read .env files. These contain secrets that should not appear in the conversation.\"\n },\n {\n name: \"env-file-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath) => isEnvFile(filePath),\n reason: \"Don't write to .env files. These contain secrets.\"\n },\n {\n name: \"hooks-folder-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => {\n const resolved = resolveIn(ctx.cwd, filePath);\n const hooksDir = path.join(ctx.cwd, \".claude\", \"hooks\");\n return isWithin(hooksDir, resolved);\n },\n reason: \"Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.\"\n },\n {\n name: \"git-dir-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => targetsGitDir(filePath, ctx.cwd),\n reason: \"Don't write into .git/ directly. Its internals (config, hooks, refs) are not plain project files -- a config edit can redirect remotes or push destinations that other rules trust, and a hooks/ script runs automatically on the next commit/push. Use git subcommands instead, or ask the user.\"\n },\n {\n name: \"node-modules-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => targetsNodeModules(filePath, ctx.cwd),\n reason: \"Don't write into node_modules/. Its files are hardlinks into pnpm's global store -- editing one in place corrupts that package for every project on this machine. Edit the source package and rebuild, or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`).\"\n },\n {\n name: \"claude-settings-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => {\n const resolved = resolveIn(ctx.cwd, filePath);\n return samePath(resolved, path.join(ctx.cwd, \".claude\", \"settings.json\"));\n },\n reason: \"Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.\"\n }\n];\n\n//#endregion\n//#region src/evaluate-tool.ts\n/** Tools whose input contains a `file_path` field. */\nconst FILE_PATH_TOOLS = /* @__PURE__ */ new Set([\n \"Read\",\n \"Write\",\n \"Edit\"\n]);\n/** Tools whose input contains a `path` field (search root, optional). */\nconst SEARCH_TOOLS = /* @__PURE__ */ new Set([\"Glob\", \"Grep\"]);\n/**\n* Extract the relevant file path from tool input based on tool name.\n* Returns null if the input shape doesn't match expectations.\n*/\nfunction extractPath(toolName, toolInput, cwd) {\n if (FILE_PATH_TOOLS.has(toolName)) return parseFilePathInput(toolInput)?.file_path ?? null;\n if (SEARCH_TOOLS.has(toolName)) return parseSearchInput(toolInput)?.path ?? cwd;\n return null;\n}\n/**\n* Evaluate a non-Bash tool invocation against file-tool rules.\n* Returns deny or none.\n*/\nfunction evaluateToolUse(toolName, toolInput, ctx) {\n const filePath = extractPath(toolName, toolInput, ctx.cwd);\n if (filePath === null) return {\n action: \"none\",\n rule: null,\n reason: \"unknown tool input shape\"\n };\n for (const rule of fileDenyRules) if (rule.tools.has(toolName) && rule.when(filePath, ctx)) return {\n action: \"deny\",\n rule: rule.name,\n reason: rule.reason ?? null\n };\n return {\n action: \"none\",\n rule: null,\n reason: \"no file rule matched\"\n };\n}\n\n//#endregion\n//#region src/lib/settings.ts\nconst SETTINGS_PATH = \".claude/hooks/bst-hook.settings.json\";\nconst BstHookSettingsSchema = object({ protectedBranches: array(string().min(1)).default([]) });\n/**\n* Check whether a file at `relativePath` inside `cwd` is tracked and\n* unmodified in git (no staged or unstaged changes vs HEAD).\n*\n* Returns false on any error -- safe default is to not trust the file.\n*/\nfunction isFileClean(cwd, relativePath) {\n try {\n execFileSync(\"git\", [\n \"ls-files\",\n \"--error-unmatch\",\n relativePath\n ], {\n cwd,\n stdio: \"ignore\"\n });\n execFileSync(\"git\", [\n \"diff\",\n \"--quiet\",\n \"HEAD\",\n \"--\",\n relativePath\n ], {\n cwd,\n stdio: \"ignore\"\n });\n return true;\n } catch {\n return false;\n }\n}\n/**\n* Load repo-level settings from `.claude/hooks/bst-hook.settings.json`.\n*\n* Walks up from `cwd` to find the workspace root (or uses `cwd` directly),\n* reads the settings file, validates it, and checks that the file is\n* git-tracked and unmodified before trusting it.\n*\n* Returns `{ protectedBranches: [] }` if the file is missing, invalid, or\n* untrusted.\n*/\nfunction loadRepoSettings(cwd) {\n const root = findWorkspaceRoot(cwd) ?? cwd;\n const settingsFile = join(root, SETTINGS_PATH);\n const empty = { protectedBranches: [] };\n if (!existsSync(settingsFile)) return empty;\n if (!isFileClean(root, SETTINGS_PATH)) return empty;\n try {\n const raw = readFileSync(settingsFile, \"utf-8\");\n const parsed = JSON.parse(raw);\n const result = BstHookSettingsSchema.safeParse(parsed);\n if (!result.success) return empty;\n return { protectedBranches: result.data.protectedBranches };\n } catch {\n return empty;\n }\n}\n\n//#endregion\n//#region src/index.ts\nconst inputResult = await readHookInput();\nif (!inputResult.ok) {\n process.stderr.write(`[bst-hook] invalid hook input: ${inputResult.error}\\n`);\n audit({\n sessionId: \"unknown\",\n cwd: \"unknown\",\n command: `<invalid input: ${inputResult.error}>`,\n decision: \"none\",\n rule: null,\n reason: `validation failed: ${inputResult.error}`,\n permissionMode: void 0\n });\n process.exit(0);\n}\nconst input = inputResult.data;\nif (input.hook_event_name !== \"PreToolUse\") process.exit(0);\nif (!(/* @__PURE__ */ new Set([\n \"Bash\",\n \"Read\",\n \"Write\",\n \"Edit\",\n \"Glob\",\n \"Grep\"\n])).has(input.tool_name)) process.exit(0);\nconst repoSettings = loadRepoSettings(input.cwd);\nlet decision;\nlet auditCommand;\nif (input.tool_name === \"Bash\") {\n const bashInput = parseBashInput(input.tool_input);\n if (!bashInput) {\n audit({\n sessionId: input.session_id,\n cwd: input.cwd,\n toolName: input.tool_name,\n command: `<invalid Bash input: ${JSON.stringify(input.tool_input).slice(0, 200)}>`,\n decision: \"none\",\n rule: null,\n reason: \"Bash tool_input missing or invalid 'command' field\",\n permissionMode: input.permission_mode\n });\n process.exit(0);\n }\n decision = evaluateCommand(bashInput.command, {\n cwd: input.cwd,\n depth: 0,\n repoProtectedBranches: repoSettings.protectedBranches\n });\n auditCommand = bashInput.command;\n} else {\n const projectDir = findWorkspaceRoot(input.cwd) ?? process.env[\"CLAUDE_PROJECT_DIR\"] ?? input.cwd;\n decision = evaluateToolUse(input.tool_name, input.tool_input, {\n cwd: projectDir,\n sessionId: input.session_id\n });\n const filePath = String(input.tool_input[\"file_path\"] ?? input.tool_input[\"path\"] ?? \"\");\n auditCommand = `${input.tool_name} ${filePath}`;\n}\naudit({\n sessionId: input.session_id,\n cwd: input.cwd,\n toolName: input.tool_name,\n command: auditCommand,\n decision: decision.action,\n rule: decision.rule,\n reason: decision.reason,\n permissionMode: input.permission_mode\n});\nif (decision.action === \"deny\") writeDeny(decision.reason);\nelse if (decision.advice) writeAdvice(decision.advice);\n\n//#endregion\nexport { };", "bst-hook.mjs"), writeIfChanged(ctx, GUIDE_PATH, "# bst-hook Permission Guide\n\nThis project uses a PreToolUse hook (`bst-hook`) that checks each Bash command\nand file tool call before it runs. The hook only ever blocks a command; it does\nnot approve anything on its own. A command that matches a rule below is denied,\nwith a message that says what to do instead.\n\n## Denied commands\n\nThese are blocked. The hook tells you what to do instead.\n\n### Bash\n\n- **sudo**: Don't use sudo. If you need elevated permissions, ask me to do it manually.\n- **git-force-push**: Use `git push --force-with-lease` instead of `--force`.\n- **git-protected-branch-write**: This would move a protected branch (main, master, the remote's default branch, or one listed in .claude/hooks/bst-hook.settings.json#protectedBranches). Work on a feature branch in a worktree: `git worktree add ../<repo>-<topic> -b feat/<topic>`, then commit and push there. To bring `main` up to date, use `git pull --ff-only`.\n- **chmod-777**: Don't use 777 permissions. Use more restrictive permissions.\n- **node-modules-write**: Don't write into node_modules/. Its files are hardlinks into pnpm's global store, so overwriting one in place (plain `cp`, `>` redirect, `sed -i`, ...) writes through the link and corrupts that package for every project on this machine -- silently, since the directory you wrote into ends up fine. To test a local build as an installed dependency, run it directly (`node packages/<pkg>/dist/bin.mjs <cmd>`), or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`). If you must replace a file in place, `cp --remove-destination` is safe -- it unlinks the target first. Run `pnpm store status` to check whether the store is already damaged.\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **env-file-write**: Don't write to .env files. These contain secrets.\n- **npx-redirect**: This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\n- **mise-self-update**: Don't self-update mise. The version is Renovate-managed; ask a human to bump the pin.\n\n### File tools (Read, Write, Edit, Glob, Grep)\n\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **env-file-write**: Don't write to .env files. These contain secrets.\n- **hooks-folder-write**: Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.\n- **git-dir-write**: Don't write into .git/ directly. Its internals (config, hooks, refs) are not plain project files -- a config edit can redirect remotes or push destinations that other rules trust, and a hooks/ script runs automatically on the next commit/push. Use git subcommands instead, or ask the user.\n- **node-modules-write**: Don't write into node_modules/. Its files are hardlinks into pnpm's global store -- editing one in place corrupts that package for every project on this machine. Edit the source package and rebuild, or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`).\n- **claude-settings-write**: Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.\n\n## How commands are evaluated\n\n- **Pipelines and chains**: every segment is checked; a deny on any segment blocks the whole command.\n- **pnpm scripts**: `pnpm <script>` resolves against `package.json` (including pre/post hooks and `--filter` workspaces) and evaluates the resolved command recursively.\n- **cd propagation**: `cd dir && cmd` evaluates `cmd` relative to the new directory.\n- **Branch switch propagation**: `git switch <branch> && cmd` (or `git checkout <branch>`) evaluates a git write in `cmd` against the branch it switched to, not the one checked out before. This holds only across `&&` and only in the same directory.\n- **Audit log**: all decisions are logged to `.claude/hooks/bst-hook.audit.local.jsonl`.\n\n## Everything else\n\nA command that matches none of the rules above is judged by Claude Code's own\nauto mode, not by this hook.\n", "bst-hook-guide.md")];
10738
+ const results = [writeIfChanged(ctx, HOOK_PATH, "/**\n * bst-hook -- Claude Code PreToolUse hook\n * @bensandee/claude-hooks\n * https://github.com/bensandee/tooling\n *\n * Auto-generated by @bensandee/tooling repo:sync -- do not edit manually.\n */\nimport * as fs from \"node:fs\";\nimport { existsSync, readFileSync, readdirSync, realpathSync, statSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execFileSync } from \"node:child_process\";\n\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/util.js\nfunction getEnumValues(entries) {\n const numericValues = Object.values(entries).filter((v) => typeof v === \"number\");\n return Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);\n}\nfunction joinValues(array, separator = \"|\") {\n return array.map((val) => stringifyPrimitive(val)).join(separator);\n}\nfunction jsonStringifyReplacer(_, value) {\n if (typeof value === \"bigint\") return value.toString();\n return value;\n}\nvar Cached = class {\n constructor(getter) {\n this._getter = getter;\n this._value = void 0;\n }\n get value() {\n const getter = this._getter;\n if (getter !== void 0) {\n this._value = getter();\n this._getter = void 0;\n }\n return this._value;\n }\n};\nfunction cached(getter) {\n return new Cached(getter);\n}\nfunction nullish(input) {\n return input === null || input === void 0;\n}\nfunction cleanRegex(source) {\n const start = source.startsWith(\"^\") ? 1 : 0;\n const end = source.endsWith(\"$\") ? source.length - 1 : source.length;\n return source.slice(start, end);\n}\nfunction assignProp(target, prop, value) {\n Object.defineProperty(target, prop, {\n value,\n writable: true,\n enumerable: true,\n configurable: true\n });\n}\n/**\n* Whichever object a def's `shape` currently answers from: the one the caller passed until the first read, the frozen copy after it.\n*\n* Its keys and descriptors read without invoking anything, which is what lets a discriminated union check its discriminator, and the cycle walk read a shape, without resolving a getter that references the schema being constructed. A def that answers `shape` from an accessor of its own has none.\n*/\nfunction rawShape(def) {\n const desc = Object.getOwnPropertyDescriptor(def, \"shape\");\n return desc?.get ? desc.get.raw : desc?.value;\n}\nfunction sourceShape(schema) {\n return rawShape(schema._zod.def) ?? schema._zod.def.shape;\n}\nfunction deferProp(target, key, getter) {\n Object.defineProperty(target, key, {\n get() {\n const value = getter();\n assignProp(this, key, value);\n return value;\n },\n enumerable: true,\n configurable: true\n });\n}\nfunction putProp(target, key, value) {\n if (key in target) assignProp(target, key, value);\n else target[key] = value;\n}\n/**\n* Copies `keys` of `source`'s shape onto `target`, each value passed through `wrap`.\n*\n* A key the source has resolved is copied through now, so the derived shape states it outright and nothing has to resolve it to learn what it holds. A key the source still defers stays deferred, and reads back through the source's own `shape`, so it resolves once and both shapes get that one schema.\n*/\nfunction mirrorShape(target, source, keys, wrap) {\n const raw = sourceShape(source);\n for (const key of keys) {\n const desc = Object.getOwnPropertyDescriptor(raw, key);\n if (!desc.enumerable) continue;\n if (desc.get) deferProp(target, key, () => {\n const value = source._zod.def.shape[key];\n return wrap ? wrap(value, key) : value;\n });\n else putProp(target, key, wrap ? wrap(desc.value, key) : desc.value);\n }\n}\nfunction mirrorProps(target, source) {\n for (const key of Reflect.ownKeys(source)) {\n const desc = Object.getOwnPropertyDescriptor(source, key);\n if (!desc.enumerable) continue;\n if (desc.get) deferProp(target, key, () => source[key]);\n else putProp(target, key, desc.value);\n }\n}\nfunction mergeDefs(...defs) {\n const mergedDescriptors = {};\n for (const def of defs) {\n const descriptors = Object.getOwnPropertyDescriptors(def);\n Object.assign(mergedDescriptors, descriptors);\n }\n return Object.defineProperties({}, mergedDescriptors);\n}\nfunction esc(str) {\n return JSON.stringify(str);\n}\nfunction slugify(input) {\n return input.toLowerCase().trim().replace(/[^\\w\\s-]/g, \"\").replace(/[\\s_-]+/g, \"-\").replace(/^-+|-+$/g, \"\");\n}\nconst captureStackTrace = \"captureStackTrace\" in Error ? Error.captureStackTrace : (..._args) => {};\nfunction isObject(data) {\n return typeof data === \"object\" && data !== null && !Array.isArray(data);\n}\nconst allowsEval = /* @__PURE__*/ cached(() => {\n if (globalConfig.jitless) return false;\n if (typeof navigator !== \"undefined\" && navigator?.userAgent?.includes(\"Cloudflare\")) return false;\n try {\n new Function(\"\");\n return true;\n } catch (_) {\n return false;\n }\n});\nfunction isPlainObject(o) {\n if (isObject(o) === false) return false;\n const ctor = o.constructor;\n if (ctor === void 0) return true;\n if (typeof ctor !== \"function\") return true;\n const prot = ctor.prototype;\n if (isObject(prot) === false) return false;\n if (Object.prototype.hasOwnProperty.call(prot, \"isPrototypeOf\") === false) return false;\n return true;\n}\nfunction shallowClone(o) {\n if (isPlainObject(o)) return { ...o };\n if (Array.isArray(o)) return [...o];\n if (o instanceof Map) return new Map(o);\n if (o instanceof Set) return new Set(o);\n return o;\n}\nconst propertyKeyTypes = /* @__PURE__*/ new Set([\n \"string\",\n \"number\",\n \"symbol\"\n]);\nfunction escapeRegex(str) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\nfunction clone(inst, def, params) {\n const cl = new inst._zod.constr(def ?? inst._zod.def);\n if (!def || params?.parent) cl._zod.parent = inst;\n return cl;\n}\nfunction normalizeParams(_params) {\n const params = _params;\n if (!params) return {};\n if (typeof params === \"string\") return { error: () => params };\n if (params?.message !== void 0) {\n if (params?.error !== void 0) throw new Error(\"Cannot specify both `message` and `error` params\");\n params.error = params.message;\n }\n delete params.message;\n if (typeof params.error === \"string\") return {\n ...params,\n error: () => params.error\n };\n return params;\n}\nfunction stringifyPrimitive(value) {\n if (typeof value === \"bigint\") return value.toString() + \"n\";\n if (typeof value === \"string\") return `\"${value}\"`;\n return `${value}`;\n}\nfunction optionalKeys(shape) {\n return Object.keys(shape).filter((k) => {\n return shape[k]._zod.optin !== void 0 && shape[k]._zod.optout === \"optional\";\n });\n}\nconst NUMBER_FORMAT_RANGES = /*@__PURE__*/ (() => ({\n safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],\n int32: [-2147483648, 2147483647],\n uint32: [0, 4294967295],\n float32: [-34028234663852886e22, 34028234663852886e22],\n float64: [-Number.MAX_VALUE, Number.MAX_VALUE]\n}))();\nconst BIGINT_FORMAT_RANGES = {\n int64: [/* @__PURE__*/ BigInt(\"-9223372036854775808\"), /* @__PURE__*/ BigInt(\"9223372036854775807\")],\n uint64: [/* @__PURE__*/ BigInt(0), /* @__PURE__*/ BigInt(\"18446744073709551615\")]\n};\nfunction pick(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n if (checks && checks.length > 0) throw new Error(\".pick() cannot be used on object schemas containing refinements\");\n const newShape = {};\n mirrorShape(newShape, schema, maskedKeys(schema, mask));\n return clone(schema, mergeDefs(currDef, {\n shape: newShape,\n checks: []\n }));\n}\nfunction maskedKeys(schema, mask) {\n const raw = sourceShape(schema);\n const keys = [];\n for (const key of Reflect.ownKeys(mask)) {\n if (!Object.getOwnPropertyDescriptor(raw, key)?.enumerable) throw new Error(`Unrecognized key: \"${String(key)}\"`);\n if (mask[key]) keys.push(key);\n }\n return keys;\n}\nfunction omit(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n if (checks && checks.length > 0) throw new Error(\".omit() cannot be used on object schemas containing refinements\");\n const omitted = new Set(maskedKeys(schema, mask));\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)).filter((key) => !omitted.has(key)));\n return clone(schema, mergeDefs(currDef, {\n shape: newShape,\n checks: []\n }));\n}\nfunction extend(schema, shape) {\n if (!isPlainObject(shape)) throw new Error(\"Invalid input to extend: expected a plain object\");\n const checks = schema._zod.def.checks;\n if (checks && checks.length > 0) {\n const existingShape = sourceShape(schema);\n for (const key of Reflect.ownKeys(shape)) if (Object.getOwnPropertyDescriptor(existingShape, key) !== void 0) throw new Error(\"Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.\");\n }\n return clone(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));\n}\nfunction extended(schema, shape) {\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)));\n mirrorProps(newShape, shape);\n return newShape;\n}\nfunction safeExtend(schema, shape) {\n if (!isPlainObject(shape)) throw new Error(\"Invalid input to safeExtend: expected a plain object\");\n return clone(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));\n}\nfunction merge(a, b) {\n if (!b?._zod?.def) throw new Error(\"Invalid input to merge: expected an object schema. To merge a plain shape, use `.extend()`.\");\n if (a._zod.def.checks?.length) throw new Error(\".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.\");\n const newShape = {};\n mirrorShape(newShape, a, Reflect.ownKeys(sourceShape(a)));\n mirrorShape(newShape, b, Reflect.ownKeys(sourceShape(b)));\n return clone(a, mergeDefs(a._zod.def, {\n shape: newShape,\n get catchall() {\n return b._zod.def.catchall;\n },\n checks: b._zod.def.checks ?? []\n }));\n}\nfunction partial(Class, schema, mask, name = \"partial\") {\n const checks = schema._zod.def.checks;\n if (checks && checks.length > 0) throw new Error(`.${name}() cannot be used on object schemas containing refinements`);\n const selected = mask ? new Set(maskedKeys(schema, mask)) : void 0;\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)), Class && ((value, key) => selected && !selected.has(key) ? value : new Class({\n type: \"optional\",\n innerType: value\n })));\n return clone(schema, mergeDefs(schema._zod.def, {\n shape: newShape,\n checks: []\n }));\n}\nfunction required(Class, schema, mask) {\n const selected = mask ? new Set(maskedKeys(schema, mask)) : void 0;\n const newShape = {};\n mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)), (value, key) => selected && !selected.has(key) ? value : new Class({\n type: \"nonoptional\",\n innerType: value\n }));\n return clone(schema, mergeDefs(schema._zod.def, { shape: newShape }));\n}\nfunction aborted(x, startIndex = 0) {\n if (x.aborted === true) return true;\n for (let i = startIndex; i < x.issues.length; i++) if (x.issues[i]?.continue !== true) return true;\n return false;\n}\nfunction explicitlyAborted(x, startIndex = 0) {\n if (x.aborted === true) return true;\n for (let i = startIndex; i < x.issues.length; i++) if (x.issues[i]?.continue === false) return true;\n return false;\n}\nfunction prefixIssues(path, issues) {\n return issues.map((iss) => {\n var _a;\n (_a = iss).path ?? (_a.path = []);\n iss.path.unshift(path);\n return iss;\n });\n}\nfunction unwrapMessage(message) {\n return typeof message === \"string\" ? message : message?.message;\n}\nfunction attachSchema(issues, start, inst) {\n var _a;\n for (let i = start; i < issues.length; i++) (_a = issues[i]).schema ?? (_a.schema = inst);\n}\nfunction finalizeIssue(iss, ctx, config) {\n var _a;\n const traits = iss.inst?._zod?.traits;\n if (traits?.has(\"$ZodType\")) {\n if (traits.has(\"$ZodCheck\")) (_a = iss).schema ?? (_a.schema = iss.inst);\n else iss.schema = iss.inst;\n }\n const schemaError = iss.schema !== iss.inst ? iss.schema?._zod.def?.error : void 0;\n const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(schemaError?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config.customError?.(iss)) ?? unwrapMessage(config.localeError?.(iss)) ?? \"Invalid input\";\n const full = {};\n for (const k of Object.keys(iss)) {\n if (k === \"inst\" || k === \"schema\" || k === \"continue\" || k === \"input\" || k === \"__proto__\") continue;\n full[k] = iss[k];\n }\n full.path ?? (full.path = []);\n full.message = message;\n if (ctx?.reportInput) full.input = iss.input;\n return full;\n}\nconst highSurrogate = /[\\uD800-\\uDBFF]/;\nfunction codePointLength(str) {\n const units = str.length;\n if (!highSurrogate.test(str)) return units;\n let count = units;\n for (let i = 0; i < units - 1; i++) if ((str.charCodeAt(i) & 64512) === 55296 && (str.charCodeAt(i + 1) & 64512) === 56320) {\n count--;\n i++;\n }\n return count;\n}\nfunction getLengthableOrigin(input) {\n if (Array.isArray(input)) return \"array\";\n if (typeof input === \"string\") return \"string\";\n return \"unknown\";\n}\nfunction parsedType(data) {\n const t = typeof data;\n switch (t) {\n case \"number\": return Number.isNaN(data) ? \"nan\" : \"number\";\n case \"object\": {\n if (data === null) return \"null\";\n if (Array.isArray(data)) return \"array\";\n const obj = data;\n if (obj && Object.getPrototypeOf(obj) !== Object.prototype && \"constructor\" in obj && obj.constructor) return obj.constructor.name;\n }\n }\n return t;\n}\nfunction issue(...args) {\n const [iss, input, inst] = args;\n if (typeof iss === \"string\") return {\n message: iss,\n code: \"custom\",\n input,\n inst\n };\n return { ...iss };\n}\n/**\n* Installs a trait's members on its prototype. Each value builds that member for the instance on first read; the built value shadows the accessor as an own property, so a detached `const { parse } = schema` keeps working.\n*\n* Call this from a `proto` initializer, which runs once per prototype — never per instance.\n*/\nfunction members(proto, table) {\n for (const key in table) {\n const desc = Object.getOwnPropertyDescriptor(table, key);\n if (desc.get) Object.defineProperty(proto, key, {\n ...desc,\n enumerable: false\n });\n else defineBound(proto, key, desc.value);\n }\n}\n/** Shadows a prototype member with an own value, so a getter that builds from the instance runs once. */\nfunction own(inst, key, value, enumerable = true) {\n Object.defineProperty(inst, key, {\n configurable: true,\n writable: true,\n enumerable,\n value\n });\n return value;\n}\n/** Like {@link own}, for a member that was never an own data property and has to stay out of `Object.keys`. */\nfunction hide(inst, key, value) {\n return own(inst, key, value, false);\n}\n/** Adds members a table derives from the instance: each builds on first read and shadows as own data, and assignment shadows the same way, as when these were own properties. */\nfunction derived(computes, table) {\n for (const key in computes) {\n const compute = computes[key];\n Object.defineProperty(table, key, {\n configurable: true,\n enumerable: true,\n get() {\n return own(this, key, compute(this));\n },\n set(value) {\n own(this, key, value);\n }\n });\n }\n return table;\n}\nfunction defineBound(proto, key, fn) {\n Object.defineProperty(proto, key, {\n configurable: true,\n get() {\n return this == null ? fn : own(this, key, fn.bind(this));\n },\n set(value) {\n own(this, key, value);\n }\n });\n}\n/** Returns the prototype to install on, or `undefined` if this group is already installed on it. */\nfunction claim(inst, sentinel) {\n const proto = Object.getPrototypeOf(inst);\n return sentinel in proto ? void 0 : proto;\n}\nlet installing;\nlet broke = false;\nconst breaker = {\n configurable: true,\n get() {\n broke = true;\n }\n};\n/**\n* Installs a lazily-derived internal on the `_zod` prototype of `inst`'s\n* constructor, computed from the internals object itself and cached there on\n* first read. One accessor per constructor rather than one per instance.\n*/\nfunction defineLazyInternal(inst, key, compute) {\n const proto = Object.getPrototypeOf(inst._zod);\n if (key in proto && installing !== inst._zod) {\n installing = void 0;\n return;\n }\n installing = inst._zod;\n Object.defineProperty(proto, key, {\n configurable: true,\n get() {\n Object.defineProperty(this, key, breaker);\n const outer = broke;\n broke = false;\n try {\n const value = compute(this);\n if (broke) delete this[key];\n else Object.defineProperty(this, key, {\n configurable: true,\n writable: true,\n value\n });\n broke = broke || outer;\n return value;\n } catch (err) {\n delete this[key];\n broke = broke || outer;\n throw err;\n }\n },\n set(value) {\n Object.defineProperty(this, key, {\n configurable: true,\n writable: true,\n value\n });\n }\n });\n}\n/**\n* Installs `key` on `inst`'s prototype, computed by `make` on first read and cached there as an own\n* data property. One accessor per constructor rather than one per instance, because an own accessor\n* puts every instance after the first into v8 dictionary mode. The key doubles as the sentinel.\n*/\nfunction installLazyProp(inst, key, make, enumerable) {\n const proto = claim(inst, key);\n if (!proto) return;\n Object.defineProperty(proto, key, {\n configurable: true,\n get() {\n const desc = {\n configurable: true,\n writable: true,\n enumerable,\n value: void 0\n };\n Object.defineProperty(this, key, desc);\n desc.value = make(this);\n Object.defineProperty(this, key, desc);\n return desc.value;\n },\n set(value) {\n Object.defineProperty(this, key, {\n configurable: true,\n writable: true,\n enumerable,\n value\n });\n }\n });\n}\n/** Marks the thunk `_catch` synthesises for a constant catch value. `Function.length` cannot tell that thunk from a user callback — rest and defaulted parameters both report arity 0 — and a user callback reads `ctx.error`, whose issues only finalize correctly against the caller's per-parse error map. Provenance can say what arity cannot. A plain string key rather than `Symbol.for`, whose call at module scope no bundler can prove pure — the same shape that anchored `urlCanParse` into every build. */\nconst CONSTANT_CATCH = \"~constantCatch\";\n/** Wraps a constant catch value in a thunk tagged with {@link CONSTANT_CATCH}. */\nfunction constantCatch(value) {\n const fn = () => value;\n fn[CONSTANT_CATCH] = true;\n return fn;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/core.js\nvar _a$1;\nconst _zodDesc = {\n value: void 0,\n enumerable: false\n};\nlet _E = \"captureStackTrace\" in Error ? Error : null;\nfunction newError(Definition) {\n const E = _E;\n if (E) {\n const saved = E.stackTraceLimit;\n if (typeof saved === \"number\") {\n try {\n E.stackTraceLimit = 0;\n } catch {\n _E = null;\n return new Definition();\n }\n try {\n return new Definition();\n } finally {\n E.stackTraceLimit = saved;\n }\n }\n }\n return new Definition();\n}\nfunction $constructor(name, initializer, proto, params) {\n const zodProto = {};\n function Internals(def) {\n this.def = def;\n this.constr = _;\n this.traits = /* @__PURE__ */ new Set();\n }\n Internals.prototype = zodProto;\n const protoMembers = proto;\n const initialized = protoMembers && /* @__PURE__ */ new WeakSet();\n function init(inst, def) {\n if (!inst._zod) {\n _zodDesc.value = new Internals(def);\n try {\n Object.defineProperty(inst, \"_zod\", _zodDesc);\n } finally {\n _zodDesc.value = void 0;\n }\n } else if (inst._zod.traits.has(name)) return;\n inst._zod.traits.add(name);\n initializer(inst, def);\n if (initialized) {\n const own = Object.getPrototypeOf(inst);\n const ctorProto = inst._zod.constr.prototype;\n let up = own;\n while (up && up !== ctorProto) up = Object.getPrototypeOf(up);\n const target = up ?? own;\n if (!initialized.has(target)) {\n initialized.add(target);\n members(target, protoMembers);\n }\n }\n const proto = _.prototype;\n for (const k in proto) {\n if (!Object.prototype.hasOwnProperty.call(proto, k)) continue;\n if (!(k in inst)) inst[k] = proto[k].bind(inst);\n }\n }\n const Parent = params?.Parent ?? Object;\n class Definition extends Parent {}\n Object.defineProperty(Definition, \"name\", { value: name });\n function _(def) {\n const inst = params?.Parent ? newError(Definition) : this;\n init(inst, def);\n const deferred = inst._zod.deferred;\n if (deferred) {\n for (const fn of deferred) fn();\n inst._zod.deferred = void 0;\n }\n const pp = globalThis.__zod_globalConfig?.postProcessor;\n if (pp) pp(inst);\n return inst;\n }\n Object.defineProperty(_, \"init\", { value: init });\n Object.defineProperty(_, Symbol.hasInstance, { value: (inst) => {\n if (params?.Parent && inst instanceof params.Parent) return true;\n return inst?._zod?.traits?.has(name);\n } });\n Object.defineProperty(_, \"name\", { value: name });\n return _;\n}\nvar $ZodAsyncError = class extends Error {\n constructor() {\n super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);\n }\n};\nvar $ZodEncodeError = class extends Error {\n constructor(name) {\n super(`Encountered unidirectional transform during encode: ${name}`);\n this.name = \"ZodEncodeError\";\n }\n};\n(_a$1 = globalThis).__zod_globalConfig ?? (_a$1.__zod_globalConfig = {});\nconst globalConfig = globalThis.__zod_globalConfig;\nfunction config(newConfig) {\n if (newConfig) Object.assign(globalConfig, newConfig);\n return globalConfig;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/errors.js\nfunction _getMessage() {\n const internals = this._zod;\n internals.message ?? (internals.message = JSON.stringify(internals.def, jsonStringifyReplacer, 2));\n return internals.message;\n}\nfunction _setMessage(value) {\n this._zod.message = value;\n}\nconst _messageDesc = {\n get: _getMessage,\n set: _setMessage,\n enumerable: true,\n configurable: true\n};\nconst _issuesDesc = {\n value: void 0,\n enumerable: false\n};\nconst _installedToString = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);\nconst initializer$1 = (inst, def) => {\n inst.name = \"$ZodError\";\n _issuesDesc.value = def;\n Object.defineProperty(inst, \"issues\", _issuesDesc);\n _issuesDesc.value = void 0;\n Object.defineProperty(inst, \"message\", _messageDesc);\n const proto = Object.getPrototypeOf(inst);\n if (!_installedToString.has(proto)) {\n _installedToString.add(proto);\n Object.defineProperty(proto, \"toString\", {\n configurable: true,\n enumerable: false,\n get() {\n const value = () => this.message;\n Object.defineProperty(this, \"toString\", {\n value,\n configurable: true,\n writable: true\n });\n return value;\n },\n set(value) {\n Object.defineProperty(this, \"toString\", {\n value,\n configurable: true,\n writable: true\n });\n }\n });\n }\n};\nconst $ZodError = $constructor(\"$ZodError\", initializer$1);\nconst $ZodRealError = $constructor(\"$ZodError\", initializer$1, void 0, { Parent: Error });\n/** Get-or-create `obj[key]` as an own data property. A path segment naming an inherited member\n* (\"toString\", \"constructor\") would otherwise read through to the prototype, and assigning\n* \"__proto__\" would hit the setter instead of creating a key. */\nfunction node(obj, key, make) {\n if (!Object.prototype.hasOwnProperty.call(obj, key)) {\n if (key === \"__proto__\") Object.defineProperty(obj, key, {\n value: make(),\n writable: true,\n enumerable: true,\n configurable: true\n });\n else obj[key] = make();\n }\n return obj[key];\n}\nfunction flattenError(error, mapper = (issue) => issue.message) {\n const fieldErrors = {};\n const formErrors = [];\n for (const sub of error.issues) if (sub.path.length > 0) node(fieldErrors, sub.path[0], () => []).push(mapper(sub));\n else formErrors.push(mapper(sub));\n return {\n formErrors,\n fieldErrors\n };\n}\nfunction formatError(error, mapper = (issue) => issue.message) {\n const fieldErrors = { _errors: [] };\n const processError = (error, path = []) => {\n for (const issue of error.issues) if (issue.code === \"invalid_union\" && issue.errors.length) issue.errors.map((issues) => processError({ issues }, [...path, ...issue.path]));\n else if (issue.code === \"invalid_key\") processError({ issues: issue.issues }, [...path, ...issue.path]);\n else if (issue.code === \"invalid_element\") processError({ issues: issue.issues }, [...path, ...issue.path]);\n else {\n const fullpath = [...path, ...issue.path];\n if (fullpath.length === 0) fieldErrors._errors.push(mapper(issue));\n else {\n let curr = fieldErrors;\n let i = 0;\n while (i < fullpath.length) {\n const el = fullpath[i];\n const terminal = i === fullpath.length - 1;\n if (el === \"_errors\") {\n if (terminal) curr._errors.push(mapper(issue));\n i++;\n continue;\n }\n if (!Object.prototype.hasOwnProperty.call(curr, el)) Object.defineProperty(curr, el, {\n value: { _errors: [] },\n enumerable: true,\n writable: true,\n configurable: true\n });\n const node = curr[el];\n if (terminal) node._errors.push(mapper(issue));\n curr = node;\n i++;\n }\n }\n }\n };\n processError(error);\n return fieldErrors;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/parse.js\nfunction finalizeParams(callee, params) {\n return {\n callee: params?.callee ?? callee,\n Err: params?.Err\n };\n}\nconst _parse = (_Err) => {\n const fn = (schema, value, _ctx, _params) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: false\n } : { async: false };\n const result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) throw new $ZodAsyncError();\n if (result.issues.length) {\n const e = new ((_params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));\n captureStackTrace(e, _params?.callee ?? fn);\n throw e;\n }\n return result.value;\n };\n return fn;\n};\nconst _parseAsync = (_Err) => {\n const fn = async (schema, value, _ctx, params) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: true\n } : { async: true };\n let result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) result = await result;\n if (result.issues.length) {\n const e = new ((params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));\n captureStackTrace(e, params?.callee ?? fn);\n throw e;\n }\n return result.value;\n };\n return fn;\n};\nconst _safeParse = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: false\n } : { async: false };\n const result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) throw new $ZodAsyncError();\n return result.issues.length ? failure(_Err, result.issues, ctx) : {\n success: true,\n data: result.value\n };\n};\nfunction failure(Err, issues, ctx) {\n let error;\n return {\n success: false,\n get error() {\n if (!error) {\n error = new Err(issues.map((iss) => finalizeIssue(iss, ctx, config())));\n issues = void 0;\n ctx = void 0;\n }\n return error;\n },\n set error(e) {\n error = e;\n issues = void 0;\n ctx = void 0;\n }\n };\n}\nconst _safeParseAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: true\n } : { async: true };\n let result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) result = await result;\n return result.issues.length ? failure(_Err, result.issues, ctx) : {\n success: true,\n data: result.value\n };\n};\nconst COMPILE_INVALID = /* @__PURE__ */ Symbol.for(\"zod.compile.invalid\");\nconst COMPILE_FALLBACK = /* @__PURE__ */ Symbol.for(\"zod.compile.fallback\");\nconst validate = ((schema, value, _ctx) => {\n const validator = schema._zod.bag.validator;\n if (validator !== void 0) {\n if (validator(value) !== COMPILE_INVALID) return true;\n if (validator.definite === true && _ctx === void 0) return false;\n }\n return validateFallback(schema, value, _ctx);\n});\nfunction validateFallback(schema, value, _ctx) {\n const ctx = _ctx ? {\n ..._ctx,\n async: false,\n abortEarly: true\n } : {\n async: false,\n abortEarly: true\n };\n const fallbackRun = schema._zod.bag.fallbackRun;\n let result;\n if (fallbackRun) {\n ctx[COMPILE_FALLBACK] = true;\n result = fallbackRun({\n value,\n issues: []\n }, ctx);\n } else result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) throw new $ZodAsyncError();\n return result.issues.length === 0;\n}\nconst validateAsync$1 = async (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n async: true,\n abortEarly: true\n } : {\n async: true,\n abortEarly: true\n };\n let result = schema._zod.run({\n value,\n issues: []\n }, ctx);\n if (result instanceof Promise) result = await result;\n return result.issues.length === 0;\n};\nconst _encode = (_Err) => {\n const parse = _parse(_Err);\n const fn = (schema, value, _ctx, _params) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return parse(schema, value, ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _decode = (_Err) => {\n const parse = _parse(_Err);\n const fn = (schema, value, _ctx, _params) => {\n return parse(schema, value, _ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _encodeAsync = (_Err) => {\n const parseAsync = _parseAsync(_Err);\n const fn = async (schema, value, _ctx, _params) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return await parseAsync(schema, value, ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _decodeAsync = (_Err) => {\n const parseAsync = _parseAsync(_Err);\n const fn = async (schema, value, _ctx, _params) => {\n return await parseAsync(schema, value, _ctx, finalizeParams(fn, _params));\n };\n return fn;\n};\nconst _safeEncode = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return _safeParse(_Err)(schema, value, ctx);\n};\nconst _safeDecode = (_Err) => (schema, value, _ctx) => {\n return _safeParse(_Err)(schema, value, _ctx);\n};\nconst _safeEncodeAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? {\n ..._ctx,\n direction: \"backward\"\n } : { direction: \"backward\" };\n return _safeParseAsync(_Err)(schema, value, ctx);\n};\nconst _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {\n return _safeParseAsync(_Err)(schema, value, _ctx);\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/regexes.js\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link cuid2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\nconst cuid = /^[cC][0-9a-z]{6,}$/;\nconst cuid2 = /^[0-9a-z]+$/;\nconst ulid = /^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/;\nconst xid = /^[0-9a-vA-V]{20}$/;\nconst ksuid = /^[A-Za-z0-9]{27}$/;\nconst nanoid = /^[a-zA-Z0-9_-]{21}$/;\nfunction nanoidOfLength(length) {\n return new RegExp(`^[a-zA-Z0-9_-]{${length}}$`);\n}\n/** ISO 8601-1 duration regex. Does not support the 8601-2 extensions like negative durations or fractional/negative components. */\nconst duration = /^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$/;\n/** A regex for any UUID-like identifier: 8-4-4-4-12 hex pattern */\nconst guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;\n/** Returns a regex for validating an RFC 9562/4122 UUID.\n*\n* @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */\nconst uuid = (version) => {\n if (!version) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;\n return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);\n};\n/** Practical email validation */\nconst email = /^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$/;\nconst _emoji$1 = `^(?=[\\\\s\\\\S]*[\\\\p{Extended_Pictographic}\\\\p{Regional_Indicator}\\\\u20E3])[\\\\p{Extended_Pictographic}\\\\p{Emoji_Component}]+$`;\nfunction emoji() {\n return new RegExp(_emoji$1, \"u\");\n}\nconst ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;\nconst ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;\nconst cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\/([0-9]|[1-2][0-9]|3[0-2])$/;\nconst cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;\nconst base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;\nconst base64url = /^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-]{2,3})?$/;\nconst httpProtocol = /^https?$/;\nconst e164 = /^\\+[1-9]\\d{6,14}$/;\nconst dateSource = `(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))`;\n/** Anchors a pattern source. The interpolation lives here rather than at the call site because\n* esbuild will not drop a `@__PURE__` call whose own argument interpolates a variable, but it\n* will drop `anchor(dateSource)`. Keeping it inline pinned `date` into every bundle. */\nfunction anchor(source) {\n return new RegExp(`^${source}$`);\n}\nconst date = /*@__PURE__*/ anchor(dateSource);\nfunction timeSource(args) {\n const hhmm = `(?:[01]\\\\d|2[0-3]):[0-5]\\\\d`;\n return typeof args.precision === \"number\" ? args.precision === -1 ? `${hhmm}` : args.precision === 0 ? `${hhmm}:[0-5]\\\\d` : `${hhmm}:[0-5]\\\\d\\\\.\\\\d{${args.precision}}` : args.seconds ? `${hhmm}:[0-5]\\\\d(?:\\\\.\\\\d+)?` : `${hhmm}(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?`;\n}\nfunction time(args) {\n return new RegExp(`^${timeSource(args)}$`);\n}\nfunction datetime(args) {\n const opts = [\"Z\"];\n if (args.offset) opts.push(`([+-](?:[01]\\\\d|2[0-3]):[0-5]\\\\d)`);\n const qualified = `${timeSource({\n precision: args.precision,\n seconds: true\n })}(?:${opts.join(\"|\")})`;\n const timeRegex = args.local ? `${qualified}|${timeSource({ precision: args.precision })}` : qualified;\n return new RegExp(`^${dateSource}T(?:${timeRegex})$`);\n}\nconst anyString = /^[\\s\\S]{0,}$/;\nconst integer = /^-?\\d+$/;\nconst number = /^-?\\d+(?:\\.\\d+)?$/;\nconst lowercase = /^[^A-Z]*$/;\nconst uppercase = /^[^a-z]*$/;\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/checks.js\nconst $ZodCheck = /*@__PURE__*/ $constructor(\"$ZodCheck\", (inst, def) => {\n var _a;\n inst._zod ?? (inst._zod = {});\n inst._zod.def = def;\n (_a = inst._zod).onattach ?? (_a.onattach = []);\n});\n/** Default `when` for length-based checks: run only on non-nullish values with a `length`. */\nconst _whenHasLength = (payload) => {\n const val = payload.value;\n return !nullish(val) && val.length !== void 0;\n};\nconst $ZodCheckMaxLength = /*@__PURE__*/ $constructor(\"$ZodCheckMaxLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = _whenHasLength);\n inst._zod.check = (payload) => {\n const input = payload.value;\n const units = input.length;\n if ((typeof input === \"string\" && units > def.maximum ? codePointLength(input) : units) <= def.maximum) return;\n const origin = getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_big\",\n maximum: def.maximum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckMinLength = /*@__PURE__*/ $constructor(\"$ZodCheckMinLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = _whenHasLength);\n inst._zod.check = (payload) => {\n const input = payload.value;\n const units = input.length;\n if ((typeof input === \"string\" && units >= def.minimum && units < def.minimum * 2 ? codePointLength(input) : units) >= def.minimum) return;\n const origin = getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_small\",\n minimum: def.minimum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckLengthEquals = /*@__PURE__*/ $constructor(\"$ZodCheckLengthEquals\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = _whenHasLength);\n inst._zod.check = (payload) => {\n const input = payload.value;\n const units = input.length;\n const length = typeof input === \"string\" && units >= def.length && units <= def.length * 2 ? codePointLength(input) : units;\n if (length === def.length) return;\n const origin = getLengthableOrigin(input);\n const tooBig = length > def.length;\n payload.issues.push({\n origin,\n ...tooBig ? {\n code: \"too_big\",\n maximum: def.length\n } : {\n code: \"too_small\",\n minimum: def.length\n },\n inclusive: true,\n exact: true,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckStringFormat = /*@__PURE__*/ $constructor(\"$ZodCheckStringFormat\", (inst, def) => {\n var _a, _b;\n $ZodCheck.init(inst, def);\n if (def.pattern) (_a = inst._zod).check ?? (_a.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: def.format,\n input: payload.value,\n ...def.pattern ? { pattern: def.pattern.toString() } : {},\n inst,\n continue: !def.abort\n });\n });\n else (_b = inst._zod).check ?? (_b.check = () => {});\n});\nconst $ZodCheckRegex = /*@__PURE__*/ $constructor(\"$ZodCheckRegex\", (inst, def) => {\n $ZodCheckStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"regex\",\n input: payload.value,\n pattern: def.pattern.toString(),\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckLowerCase = /*@__PURE__*/ $constructor(\"$ZodCheckLowerCase\", (inst, def) => {\n def.pattern ?? (def.pattern = lowercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nconst $ZodCheckUpperCase = /*@__PURE__*/ $constructor(\"$ZodCheckUpperCase\", (inst, def) => {\n def.pattern ?? (def.pattern = uppercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nconst $ZodCheckIncludes = /*@__PURE__*/ $constructor(\"$ZodCheckIncludes\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const escapedRegex = escapeRegex(def.includes);\n def.pattern = new RegExp(typeof def.position === \"number\" ? `^.{${def.position},}${escapedRegex}` : escapedRegex);\n inst._zod.check = (payload) => {\n if (payload.value.includes(def.includes, def.position)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"includes\",\n includes: def.includes,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckStartsWith = /*@__PURE__*/ $constructor(\"$ZodCheckStartsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`^${escapeRegex(def.prefix)}.*`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.check = (payload) => {\n if (payload.value.startsWith(def.prefix)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"starts_with\",\n prefix: def.prefix,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckEndsWith = /*@__PURE__*/ $constructor(\"$ZodCheckEndsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`.*${escapeRegex(def.suffix)}$`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.check = (payload) => {\n if (payload.value.endsWith(def.suffix)) return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"ends_with\",\n suffix: def.suffix,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCheckOverwrite = /*@__PURE__*/ $constructor(\"$ZodCheckOverwrite\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.check = (payload) => {\n payload.value = def.tx(payload.value);\n };\n});\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/doc.js\nvar Doc = class {\n constructor(args = [], closed = {}) {\n this.content = [];\n this.indent = 0;\n this.args = args;\n this.closed = closed;\n }\n indented(fn) {\n this.indent += 1;\n try {\n fn(this);\n } finally {\n this.indent -= 1;\n }\n }\n write(arg) {\n if (typeof arg === \"function\") {\n arg(this, { execution: \"sync\" });\n arg(this, { execution: \"async\" });\n return;\n }\n const lines = arg.split(\"\\n\").filter((x) => x);\n const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));\n const dedented = lines.map((x) => x.slice(minIndent)).map((x) => \" \".repeat(this.indent * 2) + x);\n for (const line of dedented) this.content.push(line);\n }\n compile() {\n const F = Function;\n const content = this?.content ?? [``];\n return new F(...Object.keys(this.closed), `return function (${this.args.join(\", \")}) {\\n${content.join(\"\\n\")}\\n};`)(...Object.values(this.closed));\n }\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/versions.js\nconst version = {\n major: 4,\n minor: 6,\n patch: 5\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/schemas.js\nconst $ZodType = /*@__PURE__*/ $constructor(\"$ZodType\", (inst, def) => {\n var _a;\n inst ?? (inst = {});\n inst._zod.def = def;\n inst._zod.bag = inst._zod.bag || {};\n inst._zod.version = version;\n const defChecks = inst._zod.def.checks;\n const checks = inst._zod.traits.has(\"$ZodCheck\") ? [inst, ...defChecks ?? []] : defChecks?.length ? [...defChecks] : [];\n for (const ch of checks) for (const fn of ch._zod.onattach) fn(inst);\n if (checks.length === 0) {\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred?.push(() => {\n inst._zod.run = inst._zod.parse;\n });\n } else {\n const runChecks = (payload, checks, ctx) => {\n if (payload.memo) return payload;\n let isAborted = aborted(payload);\n let asyncResult;\n for (const ch of checks) {\n if (ch._zod.def.when) {\n if (explicitlyAborted(payload)) continue;\n if (!ch._zod.def.when(payload)) continue;\n } else if (isAborted) continue;\n const currLen = payload.issues.length;\n const _ = ch._zod.check(payload);\n if (_ instanceof Promise && ctx?.async === false) throw new $ZodAsyncError();\n if (asyncResult || _ instanceof Promise) asyncResult = (asyncResult ?? Promise.resolve()).then(async () => {\n await _;\n if (payload.issues.length === currLen) return;\n attachSchema(payload.issues, currLen, inst);\n if (!isAborted) isAborted = aborted(payload, currLen);\n });\n else {\n if (payload.issues.length === currLen) continue;\n attachSchema(payload.issues, currLen, inst);\n if (!isAborted) isAborted = aborted(payload, currLen);\n }\n }\n if (asyncResult) return asyncResult.then(() => {\n return payload;\n });\n return payload;\n };\n const handleCanaryResult = (canary, payload, ctx) => {\n if (aborted(canary)) {\n canary.aborted = true;\n return canary;\n }\n const checkResult = runChecks(payload, checks, ctx);\n if (checkResult instanceof Promise) {\n if (ctx.async === false) throw new $ZodAsyncError();\n return checkResult.then((checkResult) => inst._zod.parse(checkResult, ctx));\n }\n return inst._zod.parse(checkResult, ctx);\n };\n inst._zod.run = (payload, ctx) => {\n if (ctx.skipChecks) return inst._zod.parse(payload, ctx);\n if (ctx.direction === \"backward\") {\n const canary = inst._zod.parse({\n value: payload.value,\n issues: []\n }, {\n ...ctx,\n skipChecks: true\n });\n if (canary instanceof Promise) return canary.then((canary) => {\n return handleCanaryResult(canary, payload, ctx);\n });\n return handleCanaryResult(canary, payload, ctx);\n }\n const result = inst._zod.parse(payload, ctx);\n if (result instanceof Promise) {\n if (ctx.async === false) throw new $ZodAsyncError();\n return result.then((result) => runChecks(result, checks, ctx));\n }\n return runChecks(result, checks, ctx);\n };\n }\n}, {\n get \"~standard\"() {\n return hide(this, \"~standard\", standardProps(this));\n },\n set \"~standard\"(value) {\n own(this, \"~standard\", value);\n }\n});\n/** The Standard Schema surface for `inst`. Shared so wrappers can extend it without forcing it. */\nconst toStandardResult = (r, ctx) => r.issues.length ? { issues: r.issues.map((iss) => finalizeIssue(iss, ctx, config())) } : { value: r.value };\nasync function validateAsync(inst, value) {\n const ctx = { async: true };\n return toStandardResult(await inst._zod.run({\n value,\n issues: []\n }, ctx), ctx);\n}\nfunction standardProps(inst) {\n return {\n validate: (value) => {\n const ctx = { async: false };\n try {\n const r = inst._zod.run({\n value,\n issues: []\n }, ctx);\n if (!(r instanceof Promise)) return toStandardResult(r, ctx);\n } catch (_) {}\n return validateAsync(inst, value);\n },\n vendor: \"zod\",\n version: 1\n };\n}\nconst $ZodString = /*@__PURE__*/ $constructor(\"$ZodString\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = def.pattern ?? anyString;\n inst._zod.parse = (payload, _) => {\n if (def.coerce) try {\n payload.value = String(payload.value);\n } catch (_) {}\n if (typeof payload.value === \"string\") return payload;\n payload.issues.push({\n expected: \"string\",\n code: \"invalid_type\",\n input: payload.value,\n inst\n });\n return payload;\n };\n});\nconst $ZodStringFormat = /*@__PURE__*/ $constructor(\"$ZodStringFormat\", (inst, def) => {\n $ZodCheckStringFormat.init(inst, def);\n $ZodString.init(inst, def);\n});\nconst $ZodGUID = /*@__PURE__*/ $constructor(\"$ZodGUID\", (inst, def) => {\n def.pattern ?? (def.pattern = guid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodUUID = /*@__PURE__*/ $constructor(\"$ZodUUID\", (inst, def) => {\n if (def.version) {\n const v = {\n v1: 1,\n v2: 2,\n v3: 3,\n v4: 4,\n v5: 5,\n v6: 6,\n v7: 7,\n v8: 8\n }[def.version];\n if (v === void 0) throw new Error(`Invalid UUID version: \"${def.version}\"`);\n def.pattern ?? (def.pattern = uuid(v));\n } else def.pattern ?? (def.pattern = uuid());\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodEmail = /*@__PURE__*/ $constructor(\"$ZodEmail\", (inst, def) => {\n def.pattern ?? (def.pattern = email);\n $ZodStringFormat.init(inst, def);\n});\n/** The `://` guard rejected the input before the URL constructor saw it. */\nconst URL_BAD_FORMAT = 1;\n/** The URL parser rejected the input. */\nconst URL_UNPARSEABLE = 2;\nfunction canParseURL(input) {\n try {\n if (typeof URL !== \"undefined\" && typeof URL.canParse === \"function\") return URL.canParse(input);\n new URL(input);\n return true;\n } catch {\n return false;\n }\n}\nfunction validateURL(trimmed, def) {\n if (!(\"normalize\" in def) && !(\"hostname\" in def) && !(\"protocol\" in def)) return canParseURL(trimmed) || 2;\n return parseURLObject(trimmed, def);\n}\n/** Parses a URL while preserving the non-normalizing HTTP guard. */\nfunction parseURLObject(trimmed, def) {\n if (!def.normalize && def.protocol?.source === httpProtocol.source && !/^https?:\\/\\//i.test(trimmed)) return 1;\n try {\n if (typeof URL !== \"undefined\") {\n const URLStatic = URL;\n if (typeof URLStatic.parse === \"function\") return URLStatic.parse(trimmed) ?? 2;\n }\n return new URL(trimmed);\n } catch {\n return 2;\n }\n}\nconst asciiTabOrNewline = /[\\t\\n\\r]/g;\n/** The URL parser deletes every ASCII tab, LF and CR from its input before it parses, so `new URL(\"https://exa\\nmple.com\")` reports on `example.com`. Applying the same deletion to the returned value closes the half of that divergence which can move the host; the parser's other rewrite, stripping C0 controls at the edges, cannot. */\nfunction stripTabAndNewline(value) {\n return value.replace(asciiTabOrNewline, \"\");\n}\nfunction urlHostnameOk(url, hostname) {\n hostname.lastIndex = 0;\n return hostname.test(url.hostname);\n}\nfunction urlProtocolOk(url, protocol) {\n protocol.lastIndex = 0;\n return protocol.test(url.protocol.endsWith(\":\") ? url.protocol.slice(0, -1) : url.protocol);\n}\nconst $ZodURL = /*@__PURE__*/ $constructor(\"$ZodURL\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n try {\n const trimmed = payload.value.trim();\n const url = validateURL(trimmed, def);\n if (url === 1) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid URL format\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n return;\n }\n if (url === 2) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n return;\n }\n if (url === true) {\n payload.value = stripTabAndNewline(trimmed);\n return;\n }\n if (def.hostname && !urlHostnameOk(url, def.hostname)) payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid hostname\",\n pattern: def.hostname.source,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n if (def.protocol && !urlProtocolOk(url, def.protocol)) payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid protocol\",\n pattern: def.protocol.source,\n input: payload.value,\n inst,\n continue: !def.abort\n });\n payload.value = def.normalize ? url.href : stripTabAndNewline(trimmed);\n return;\n } catch (_) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n }\n };\n});\nconst $ZodEmoji = /*@__PURE__*/ $constructor(\"$ZodEmoji\", (inst, def) => {\n def.pattern ?? (def.pattern = emoji());\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodNanoID = /*@__PURE__*/ $constructor(\"$ZodNanoID\", (inst, def) => {\n if (def.length !== void 0 && (!Number.isInteger(def.length) || def.length < 1)) throw new Error(`Invalid nanoid length: ${def.length}`);\n def.pattern ?? (def.pattern = def.length === void 0 ? nanoid : nanoidOfLength(def.length));\n $ZodStringFormat.init(inst, def);\n});\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link $ZodCUID2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\nconst $ZodCUID = /*@__PURE__*/ $constructor(\"$ZodCUID\", (inst, def) => {\n def.pattern ?? (def.pattern = cuid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodCUID2 = /*@__PURE__*/ $constructor(\"$ZodCUID2\", (inst, def) => {\n def.pattern ?? (def.pattern = cuid2);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodULID = /*@__PURE__*/ $constructor(\"$ZodULID\", (inst, def) => {\n def.pattern ?? (def.pattern = ulid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodXID = /*@__PURE__*/ $constructor(\"$ZodXID\", (inst, def) => {\n def.pattern ?? (def.pattern = xid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodKSUID = /*@__PURE__*/ $constructor(\"$ZodKSUID\", (inst, def) => {\n def.pattern ?? (def.pattern = ksuid);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISODateTime = /*@__PURE__*/ $constructor(\"$ZodISODateTime\", (inst, def) => {\n def.pattern ?? (def.pattern = datetime(def));\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISODate = /*@__PURE__*/ $constructor(\"$ZodISODate\", (inst, def) => {\n def.pattern ?? (def.pattern = date);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISOTime = /*@__PURE__*/ $constructor(\"$ZodISOTime\", (inst, def) => {\n def.pattern ?? (def.pattern = time(def));\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodISODuration = /*@__PURE__*/ $constructor(\"$ZodISODuration\", (inst, def) => {\n def.pattern ?? (def.pattern = duration);\n $ZodStringFormat.init(inst, def);\n});\nconst $ZodIPv4 = /*@__PURE__*/ $constructor(\"$ZodIPv4\", (inst, def) => {\n def.pattern ?? (def.pattern = ipv4);\n $ZodStringFormat.init(inst, def);\n});\n/** An IPv6 address is written with hex digits, colons and dots, and nothing else. The guard is what makes the check below an IPv6 check: `new URL(\"http://[...]\")` parses an authority, not an address, so `@` and `\\` re-delimit it and `\"::@1\\\\\"` validates against the host `0.0.0.1`. The URL parser also deletes ASCII tab, LF and CR rather than failing, which is how `\"::1\\n\"` validated as `::1`. */\nconst ipv6Alphabet = /^[0-9a-fA-F:.]+$/;\nfunction isValidIPv6(value) {\n if (!ipv6Alphabet.test(value)) return false;\n return canParseURL(`http://[${value}]`);\n}\nconst $ZodIPv6 = /*@__PURE__*/ $constructor(\"$ZodIPv6\", (inst, def) => {\n def.pattern ?? (def.pattern = ipv6);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (!isValidIPv6(payload.value)) payload.issues.push({\n code: \"invalid_format\",\n format: \"ipv6\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodCIDRv4 = /*@__PURE__*/ $constructor(\"$ZodCIDRv4\", (inst, def) => {\n def.pattern ?? (def.pattern = cidrv4);\n $ZodStringFormat.init(inst, def);\n});\nfunction isValidCIDRv6(value) {\n const parts = value.split(\"/\");\n if (parts.length !== 2) return false;\n const [address, prefix] = parts;\n if (!prefix) return false;\n const prefixNum = Number(prefix);\n if (`${prefixNum}` !== prefix) return false;\n if (prefixNum < 0 || prefixNum > 128) return false;\n return isValidIPv6(address);\n}\nconst $ZodCIDRv6 = /*@__PURE__*/ $constructor(\"$ZodCIDRv6\", (inst, def) => {\n def.pattern ?? (def.pattern = cidrv6);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (!isValidCIDRv6(payload.value)) payload.issues.push({\n code: \"invalid_format\",\n format: \"cidrv6\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nfunction isValidBase64(data) {\n if (data === \"\") return true;\n if (/\\s/.test(data)) return false;\n if (data.length % 4 !== 0) return false;\n try {\n atob(data);\n return true;\n } catch {\n return false;\n }\n}\nconst base64Charset = /^[0-9a-zA-Z+/]*={0,2}$/;\nconst $ZodBase64 = /*@__PURE__*/ $constructor(\"$ZodBase64\", (inst, def) => {\n def.pattern ?? (def.pattern = base64Charset);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidBase64(payload.value)) return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst base64urlCharset = /^[A-Za-z0-9_-]*$/;\nfunction isValidBase64URL(data) {\n if (!base64urlCharset.test(data)) return false;\n const base64 = data.replace(/[-_]/g, (c) => c === \"-\" ? \"+\" : \"/\");\n return isValidBase64(base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\"));\n}\nconst $ZodBase64URL = /*@__PURE__*/ $constructor(\"$ZodBase64URL\", (inst, def) => {\n def.pattern ?? (def.pattern = base64urlCharset);\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidBase64URL(payload.value)) return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64url\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodE164 = /*@__PURE__*/ $constructor(\"$ZodE164\", (inst, def) => {\n def.pattern ?? (def.pattern = e164);\n $ZodStringFormat.init(inst, def);\n});\nfunction isValidJWT(token, algorithm = null) {\n try {\n const tokensParts = token.split(\".\");\n if (tokensParts.length !== 3) return false;\n const [header] = tokensParts;\n if (!header) return false;\n const parsedHeader = JSON.parse(atob(header));\n if (\"typ\" in parsedHeader && parsedHeader?.typ !== \"JWT\") return false;\n if (!parsedHeader.alg) return false;\n if (algorithm && (!(\"alg\" in parsedHeader) || parsedHeader.alg !== algorithm)) return false;\n return true;\n } catch {\n return false;\n }\n}\nconst $ZodJWT = /*@__PURE__*/ $constructor(\"$ZodJWT\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidJWT(payload.value, def.alg)) return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"jwt\",\n input: payload.value,\n inst,\n continue: !def.abort\n });\n };\n});\nconst $ZodUnknown = /*@__PURE__*/ $constructor(\"$ZodUnknown\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload) => payload;\n});\nconst $ZodNever = /*@__PURE__*/ $constructor(\"$ZodNever\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n payload.issues.push({\n expected: \"never\",\n code: \"invalid_type\",\n input: payload.value,\n inst\n });\n return payload;\n };\n});\nfunction handleArrayResult(result, final, index) {\n if (result.issues.length) final.issues.push(...prefixIssues(index, result.issues));\n final.value[index] = result.value;\n}\nconst $ZodArray = /*@__PURE__*/ $constructor(\"$ZodArray\", (inst, def) => {\n $ZodType.init(inst, def);\n const memo = globalConfig.memoizer;\n memo?.attach(inst);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!Array.isArray(input)) {\n payload.issues.push({\n expected: \"array\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n payload.value = memo ? memo.alloc(inst, payload, Array(input.length), ctx) : Array(input.length);\n const proms = [];\n const abortEarly = ctx?.abortEarly;\n for (let i = 0; i < input.length; i++) {\n const item = input[i];\n const result = def.element._zod.run({\n value: item,\n issues: []\n }, ctx);\n if (result instanceof Promise) proms.push(result.then((result) => handleArrayResult(result, payload, i)));\n else {\n handleArrayResult(result, payload, i);\n if (abortEarly && result.issues.length !== 0 && aborted(result)) break;\n }\n }\n if (proms.length) return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handlePropertyResult(result, final, key, input, optin, optout) {\n const isPresent = key in input;\n const isOptionalOut = optout === \"optional\";\n if (!isPresent && isOptionalOut && optin === \"optional\") return;\n if (result.issues.length) {\n if (optin !== void 0 && isOptionalOut && !isPresent) return;\n final.issues.push(...prefixIssues(key, result.issues));\n }\n if (!isPresent && optin === void 0) {\n if (!result.issues.length) final.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: void 0,\n path: [key]\n });\n return;\n }\n if (result.value === void 0) {\n if (isPresent || optin === \"defaulted\" && !isOptionalOut) final.value[key] = void 0;\n } else final.value[key] = result.value;\n}\nconst NO_SYMBOL_KEYS = [];\nfunction normalizeDef(def) {\n const keys = Object.keys(def.shape);\n const ownSymbols = Object.getOwnPropertySymbols(def.shape);\n const symbolKeys = ownSymbols.length ? ownSymbols : NO_SYMBOL_KEYS;\n const allKeys = symbolKeys.length ? [...keys, ...symbolKeys] : keys;\n for (const k of allKeys) if (!def.shape?.[k]?._zod?.traits?.has(\"$ZodType\")) throw new Error(`Invalid element at key \"${String(k)}\": expected a Zod schema`);\n const okeys = optionalKeys(def.shape);\n return {\n ...def,\n allKeys,\n symbolKeys,\n keySet: new Set(keys),\n numKeys: keys.length,\n optionalKeys: new Set(okeys)\n };\n}\nfunction handleCatchall(proms, input, payload, ctx, def, inst, abortEarly) {\n const unrecognized = [];\n const keySet = def.keySet;\n const _catchall = def.catchall._zod;\n const t = _catchall.def.type;\n const optin = _catchall.optin;\n const optout = _catchall.optout;\n let seen = 0;\n for (const key in input) {\n if (abortEarly && payload.issues.length !== seen) {\n if (aborted(payload, seen)) break;\n seen = payload.issues.length;\n }\n if (keySet.has(key)) continue;\n if (key === \"__proto__\") {\n if (t === \"never\") unrecognized.push(key);\n continue;\n }\n if (t === \"never\") {\n unrecognized.push(key);\n continue;\n }\n const r = _catchall.run({\n value: input[key],\n issues: []\n }, ctx);\n if (r instanceof Promise) proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, optin, optout)));\n else handlePropertyResult(r, payload, key, input, optin, optout);\n }\n if (unrecognized.length) payload.issues.push({\n code: \"unrecognized_keys\",\n keys: unrecognized,\n input,\n inst,\n continue: true\n });\n if (!proms.length) return payload;\n return Promise.all(proms).then(() => {\n return payload;\n });\n}\nconst $ZodObject = /*@__PURE__*/ $constructor(\"$ZodObject\", (inst, def) => {\n $ZodType.init(inst, def);\n const desc = Object.getOwnPropertyDescriptor(def, \"shape\");\n const sh = desc?.get ? desc.get.raw : def.shape ?? {};\n if (sh) {\n const get = () => {\n const newSh = { ...sh };\n Object.defineProperty(def, \"shape\", { value: newSh });\n get.raw = newSh;\n return newSh;\n };\n get.raw = sh;\n Object.defineProperty(def, \"shape\", { get });\n }\n const _normalized = cached(() => normalizeDef(def));\n defineLazyInternal(inst, \"propValues\", (zod) => {\n const shape = zod.def.shape;\n const propValues = {};\n for (const key in shape) {\n const field = shape[key]._zod;\n if (field.values) {\n if (!Object.prototype.hasOwnProperty.call(propValues, key)) assignProp(propValues, key, /* @__PURE__ */ new Set());\n for (const v of field.values) propValues[key].add(v);\n if (field.optin !== void 0) propValues[key].add(void 0);\n }\n }\n return propValues;\n });\n const isObject$2 = isObject;\n const catchall = def.catchall;\n let value;\n const memo = globalConfig.memoizer;\n memo?.attach(inst);\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject$2(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};\n const proms = [];\n const shape = value.shape;\n const abortEarly = ctx?.abortEarly;\n let seen = payload.issues.length;\n for (const key of value.allKeys) {\n if (abortEarly && payload.issues.length !== seen) {\n if (aborted(payload, seen)) break;\n seen = payload.issues.length;\n }\n if (key === \"__proto__\") continue;\n const el = shape[key];\n const optin = el._zod.optin;\n const optout = el._zod.optout;\n const r = el._zod.run({\n value: input[key],\n issues: []\n }, ctx);\n if (r instanceof Promise) proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, optin, optout)));\n else handlePropertyResult(r, payload, key, input, optin, optout);\n }\n if (!catchall) return proms.length ? Promise.all(proms).then(() => payload) : payload;\n return handleCatchall(proms, input, payload, ctx, _normalized.value, inst, abortEarly === true);\n };\n});\nconst $ZodObjectJIT = /*@__PURE__*/ $constructor(\"$ZodObjectJIT\", (inst, def) => {\n $ZodObject.init(inst, def);\n const superParse = inst._zod.parse;\n const _normalized = cached(() => normalizeDef(def));\n const memo = globalConfig.memoizer;\n const generateFastpass = (shape) => {\n const normalized = _normalized.value;\n const syms = normalized.symbolKeys;\n const doc = new Doc([\"payload\", \"ctx\"], {\n shape,\n inst,\n memo,\n syms\n });\n const parseStr = (k) => `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;\n const prefixStr = (id, k) => `\n let ${id}_ab = false;\n for (let i = 0; i < ${id}.issues.length; i++) {\n const iss = ${id}.issues[i];\n iss.path = iss.path ? [${k}, ...iss.path] : [${k}];\n payload.issues.push(iss);\n if (iss.continue !== true) ${id}_ab = true;\n }\n if (${id}_ab && ctx && ctx.abortEarly) {\n payload.value = newResult;\n return payload;\n }`;\n doc.write(`const input = payload.value;`);\n const ids = Object.create(null);\n let counter = 0;\n for (const key of normalized.allKeys) ids[key] = `key_${counter++}`;\n doc.write(memo ? `const newResult = memo.alloc(inst, payload, {}, ctx);` : `const newResult = {};`);\n for (const key of normalized.allKeys) {\n if (key === \"__proto__\") continue;\n const id = ids[key];\n const k = typeof key === \"symbol\" ? `syms[${syms.indexOf(key)}]` : esc(key);\n const isPresent = `${k} in input`;\n const schema = shape[key];\n const optin = schema?._zod?.optin;\n const isOptionalIn = optin !== void 0;\n const isOptionalOut = schema?._zod?.optout === \"optional\";\n doc.write(`const ${id} = ${parseStr(k)};`);\n if (isOptionalIn && isOptionalOut) {\n const assign = optin === \"optional\" ? `${id}_present` : `${id}.value !== undefined || ${id}_present`;\n doc.write(`\n const ${id}_present = ${isPresent};\n if (!${id}.issues.length || ${id}_present) {\n if (${id}.issues.length) {${prefixStr(id, k)}\n }\n\n if (${assign}) {\n newResult[${k}] = ${id}.value;\n }\n }\n\n `);\n } else if (!isOptionalIn) doc.write(`\n const ${id}_present = ${isPresent};\n if (${id}.issues.length) {${prefixStr(id, k)}\n }\n if (!${id}_present && !${id}.issues.length) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: undefined,\n path: [${k}]\n });\n if (ctx && ctx.abortEarly) {\n payload.value = newResult;\n return payload;\n }\n }\n\n if (${id}_present) {\n newResult[${k}] = ${id}.value;\n }\n\n `);\n else {\n doc.write(`\n if (${id}.issues.length) {${prefixStr(id, k)}\n }\n `);\n if (optin === \"defaulted\") doc.write(`newResult[${k}] = ${id}.value;`);\n else doc.write(`\n if (${id}.value !== undefined || ${isPresent}) {\n newResult[${k}] = ${id}.value;\n }\n `);\n }\n }\n doc.write(`payload.value = newResult;`);\n doc.write(`return payload;`);\n return doc.compile();\n };\n let fastpass;\n const isObject$1 = isObject;\n const jit = !globalConfig.jitless;\n const allowsEval$1 = allowsEval;\n const fastEnabled = jit && allowsEval$1.value;\n const catchall = def.catchall;\n let value;\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject$1(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n if (jit && fastEnabled && ctx?.async === false && ctx.jitless !== true) {\n if (!fastpass) fastpass = generateFastpass(def.shape);\n payload = fastpass(payload, ctx);\n if (!catchall) return payload;\n return handleCatchall([], input, payload, ctx, value, inst, ctx?.abortEarly === true);\n }\n return superParse(payload, ctx);\n };\n});\nfunction handleUnionResults(results, final, inst, ctx) {\n for (const result of results) if (result.issues.length === 0) {\n final.value = result.value;\n return final;\n }\n const nonaborted = results.filter((r) => !aborted(r));\n if (nonaborted.length === 1) {\n final.value = nonaborted[0].value;\n return nonaborted[0];\n }\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config())))\n });\n return final;\n}\nconst $ZodUnion = /*@__PURE__*/ $constructor(\"$ZodUnion\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.options.some((o) => o._zod.optin === \"defaulted\") ? \"defaulted\" : zod.def.options.some((o) => o._zod.optin !== void 0) ? \"optional\" : void 0);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.options.some((o) => o._zod.optout === \"optional\") ? \"optional\" : void 0);\n defineLazyInternal(inst, \"values\", (zod) => {\n if (zod.def.options.every((o) => o._zod.values)) return new Set(zod.def.options.flatMap((option) => Array.from(option._zod.values)));\n });\n defineLazyInternal(inst, \"pattern\", (zod) => {\n if (zod.def.options.every((o) => o._zod.pattern)) {\n const patterns = zod.def.options.map((o) => o._zod.pattern);\n return new RegExp(`^(${patterns.map((p) => cleanRegex(p.source)).join(\"|\")})$`);\n }\n });\n const first = def.options.length === 1 ? def.options[0]._zod.run : null;\n inst._zod.parse = (payload, ctx) => {\n if (first) return first(payload, ctx);\n let async = false;\n const results = [];\n for (const option of def.options) {\n const result = option._zod.run({\n value: payload.value,\n issues: []\n }, ctx);\n if (result instanceof Promise) {\n results.push(result);\n async = true;\n } else {\n if (result.issues.length === 0) return result;\n results.push(result);\n }\n }\n if (!async) return handleUnionResults(results, payload, inst, ctx);\n return Promise.all(results).then((results) => {\n return handleUnionResults(results, payload, inst, ctx);\n });\n };\n});\nconst $ZodIntersection = /*@__PURE__*/ $constructor(\"$ZodIntersection\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n const left = def.left._zod.run({\n value: input,\n issues: []\n }, ctx);\n const right = def.right._zod.run({\n value: input,\n issues: []\n }, ctx);\n if (left instanceof Promise || right instanceof Promise) return Promise.all([left, right]).then(([left, right]) => {\n return handleIntersectionResults(payload, left, right);\n });\n return handleIntersectionResults(payload, left, right);\n };\n});\nfunction mergeValues(a, b) {\n if (a === b) return {\n valid: true,\n data: a\n };\n if (a instanceof Date && b instanceof Date && +a === +b) return {\n valid: true,\n data: a\n };\n if (isPlainObject(a) && isPlainObject(b)) {\n const bKeys = Object.keys(b);\n const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);\n const newObj = {\n ...a,\n ...b\n };\n if (Object.prototype.hasOwnProperty.call(newObj, \"__proto__\")) delete newObj.__proto__;\n for (const key of sharedKeys) {\n if (key === \"__proto__\") continue;\n const sharedValue = mergeValues(a[key], b[key]);\n if (!sharedValue.valid) return {\n valid: false,\n mergeErrorPath: [key, ...sharedValue.mergeErrorPath]\n };\n newObj[key] = sharedValue.data;\n }\n return {\n valid: true,\n data: newObj\n };\n }\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return {\n valid: false,\n mergeErrorPath: []\n };\n const newArray = [];\n for (let index = 0; index < a.length; index++) {\n const itemA = a[index];\n const itemB = b[index];\n const sharedValue = mergeValues(itemA, itemB);\n if (!sharedValue.valid) return {\n valid: false,\n mergeErrorPath: [index, ...sharedValue.mergeErrorPath]\n };\n newArray.push(sharedValue.data);\n }\n return {\n valid: true,\n data: newArray\n };\n }\n return {\n valid: false,\n mergeErrorPath: []\n };\n}\nfunction handleIntersectionResults(result, left, right) {\n const unrecKeys = /* @__PURE__ */ new Map();\n let unrecIssue;\n const keyIssues = /* @__PURE__ */ new Map();\n const collect = (iss, side) => {\n let keys;\n if (iss.code === \"unrecognized_keys\" && !iss.path?.length) {\n unrecIssue ?? (unrecIssue = iss);\n keys = iss.keys;\n } else if (iss.code === \"invalid_key\" && iss.origin === \"record\" && iss.path?.length === 1) {\n const k = String(iss.path[0]);\n if (!keyIssues.has(k)) keyIssues.set(k, iss);\n keys = [k];\n } else return false;\n for (const k of keys) {\n if (!unrecKeys.has(k)) unrecKeys.set(k, {});\n unrecKeys.get(k)[side] = true;\n }\n return true;\n };\n for (const iss of left.issues) if (!collect(iss, \"l\")) result.issues.push(iss);\n for (const iss of right.issues) if (!collect(iss, \"r\")) result.issues.push(iss);\n const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);\n if (bothKeys.length) {\n const aggregated = unrecIssue ? bothKeys.filter((k) => unrecIssue.keys.includes(k)) : [];\n if (aggregated.length) result.issues.push({\n ...unrecIssue,\n keys: aggregated\n });\n for (const k of bothKeys) if (!aggregated.includes(k) && keyIssues.has(k)) result.issues.push(keyIssues.get(k));\n }\n const merged = mergeValues(left.value, right.value);\n if (!merged.valid) {\n if (aborted(result)) return result;\n throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(merged.mergeErrorPath)}`);\n }\n result.value = merged.data;\n return result;\n}\nconst $ZodRecord = /*@__PURE__*/ $constructor(\"$ZodRecord\", (inst, def) => {\n $ZodType.init(inst, def);\n const memo = globalConfig.memoizer;\n memo?.attach(inst);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!isPlainObject(input)) {\n payload.issues.push({\n expected: \"record\",\n code: \"invalid_type\",\n input,\n inst\n });\n return payload;\n }\n const proms = [];\n const values = def.keyType._zod.values;\n if (values && !def.partial) {\n payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};\n const recordKeys = /* @__PURE__ */ new Set();\n for (const key of values) if (typeof key === \"string\" || typeof key === \"number\" || typeof key === \"symbol\") {\n recordKeys.add(typeof key === \"number\" ? key.toString() : key);\n if (key === \"__proto__\") continue;\n const keyResult = def.keyType._zod.run({\n value: key,\n issues: []\n }, ctx);\n if (keyResult instanceof Promise) throw new Error(\"Async schemas not supported in object keys currently\");\n if (keyResult.issues.length) {\n payload.issues.push({\n code: \"invalid_key\",\n origin: \"record\",\n issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),\n input: key,\n path: [key],\n inst\n });\n continue;\n }\n const outKey = keyResult.value;\n if (outKey === \"__proto__\") continue;\n const result = def.valueType._zod.run({\n value: input[key],\n issues: []\n }, ctx);\n if (result instanceof Promise) proms.push(result.then((result) => {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }));\n else {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }\n }\n let unrecognized;\n for (const key in input) if (!recordKeys.has(key)) {\n if (def.mode === \"loose\") {\n if (key === \"__proto__\") continue;\n payload.value[key] = input[key];\n } else {\n unrecognized = unrecognized ?? [];\n unrecognized.push(key);\n }\n }\n if (unrecognized && unrecognized.length > 0) payload.issues.push({\n code: \"unrecognized_keys\",\n input,\n inst,\n keys: unrecognized,\n continue: true\n });\n } else {\n payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};\n let unrecognized;\n for (const key of Reflect.ownKeys(input)) {\n if (key === \"__proto__\") continue;\n if (!Object.prototype.propertyIsEnumerable.call(input, key)) continue;\n let keyResult = def.keyType._zod.run({\n value: key,\n issues: []\n }, ctx);\n if (keyResult instanceof Promise) throw new Error(\"Async schemas not supported in object keys currently\");\n if (typeof key === \"string\" && number.test(key) && keyResult.issues.length) {\n const retryResult = def.keyType._zod.run({\n value: Number(key),\n issues: []\n }, ctx);\n if (retryResult instanceof Promise) throw new Error(\"Async schemas not supported in object keys currently\");\n if (retryResult.issues.length === 0) keyResult = retryResult;\n }\n if (keyResult.issues.length) {\n if (def.mode === \"loose\") payload.value[key] = input[key];\n else if (values) {\n unrecognized = unrecognized ?? [];\n unrecognized.push(key);\n } else payload.issues.push({\n code: \"invalid_key\",\n origin: \"record\",\n issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),\n input: key,\n path: [key],\n inst\n });\n continue;\n }\n const outKey = keyResult.value;\n if (outKey === \"__proto__\") continue;\n const result = def.valueType._zod.run({\n value: input[key],\n issues: []\n }, ctx);\n if (result instanceof Promise) proms.push(result.then((result) => {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }));\n else {\n if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));\n payload.value[outKey] = result.value;\n }\n }\n if (unrecognized && unrecognized.length > 0) payload.issues.push({\n code: \"unrecognized_keys\",\n input,\n inst,\n keys: unrecognized,\n continue: true\n });\n }\n if (proms.length) return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nconst $ZodEnum = /*@__PURE__*/ $constructor(\"$ZodEnum\", (inst, def) => {\n $ZodType.init(inst, def);\n const values = getEnumValues(def.entries);\n const valuesSet = new Set(values);\n inst._zod.values = valuesSet;\n defineLazyInternal(inst, \"pattern\", (zod) => {\n const patternValues = getEnumValues(zod.def.entries).filter((k) => propertyKeyTypes.has(typeof k));\n return new RegExp(patternValues.length ? `^(${patternValues.map((o) => escapeRegex(o.toString())).join(\"|\")})$` : \"^[^\\\\s\\\\S]$\");\n });\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (valuesSet.has(input)) return payload;\n payload.issues.push({\n code: \"invalid_value\",\n values,\n input,\n inst\n });\n return payload;\n };\n});\nconst $ZodTransform = /*@__PURE__*/ $constructor(\"$ZodTransform\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"optional\";\n globalConfig.memoizer?.guard(inst);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") throw new $ZodEncodeError(inst.constructor.name);\n const _out = def.transform(payload.value, payload);\n if (ctx.async) return (_out instanceof Promise ? _out : Promise.resolve(_out)).then((output) => {\n payload.value = output;\n return payload;\n });\n if (_out instanceof Promise) throw new $ZodAsyncError();\n payload.value = _out;\n return payload;\n };\n});\nfunction handleOptionalResult(payload, result) {\n payload.value = result.issues.length ? void 0 : result.value;\n return payload;\n}\nconst $ZodOptional = /*@__PURE__*/ $constructor(\"$ZodOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType._zod.optin === \"defaulted\" ? \"defaulted\" : \"optional\");\n inst._zod.optout = \"optional\";\n defineLazyInternal(inst, \"values\", (zod) => {\n const values = zod.def.innerType._zod.values;\n return values ? /* @__PURE__ */ new Set([...values, void 0]) : void 0;\n });\n defineLazyInternal(inst, \"pattern\", (zod) => {\n const pattern = zod.def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${cleanRegex(pattern.source)})?$`) : void 0;\n });\n inst._zod.parse = (payload, ctx) => {\n if (payload.value === void 0) {\n if (def.innerType._zod.optin !== \"defaulted\") return payload;\n const result = def.innerType._zod.run({\n value: payload.value,\n issues: []\n }, ctx);\n if (result instanceof Promise) return result.then((result) => handleOptionalResult(payload, result));\n return handleOptionalResult(payload, result);\n }\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodExactOptional = /*@__PURE__*/ $constructor(\"$ZodExactOptional\", (inst, def) => {\n $ZodOptional.init(inst, def);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n defineLazyInternal(inst, \"pattern\", (zod) => zod.def.innerType._zod.pattern);\n inst._zod.parse = (payload, ctx) => {\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodNullable = /*@__PURE__*/ $constructor(\"$ZodNullable\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType._zod.optin);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.innerType._zod.optout);\n defineLazyInternal(inst, \"pattern\", (zod) => {\n const pattern = zod.def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${cleanRegex(pattern.source)}|null)$`) : void 0;\n });\n defineLazyInternal(inst, \"values\", (zod) => {\n return zod.def.innerType._zod.values ? /* @__PURE__ */ new Set([...zod.def.innerType._zod.values, null]) : void 0;\n });\n inst._zod.parse = (payload, ctx) => {\n if (payload.value === null) return payload;\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodDefault = /*@__PURE__*/ $constructor(\"$ZodDefault\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"defaulted\";\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n if (payload.value === void 0) {\n payload.value = def.defaultValue;\n /**\n * $ZodDefault returns the default value immediately in forward direction.\n * It doesn't pass the default value into the validator (\"prefault\"). There's no reason to pass the default value through validation. The validity of the default is enforced by TypeScript statically. Otherwise, it's the responsibility of the user to ensure the default is valid. In the case of pipes with divergent in/out types, you can specify the default on the `in` schema of your ZodPipe to set a \"prefault\" for the pipe. */\n return payload;\n }\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) return result.then((result) => handleDefaultResult(result, def));\n return handleDefaultResult(result, def);\n };\n});\nfunction handleDefaultResult(payload, def) {\n if (payload.value === void 0) payload.value = def.defaultValue;\n return payload;\n}\nconst $ZodPrefault = /*@__PURE__*/ $constructor(\"$ZodPrefault\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"defaulted\";\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n if (payload.value === void 0) payload.value = def.defaultValue;\n return def.innerType._zod.run(payload, ctx);\n };\n});\nconst $ZodNonOptional = /*@__PURE__*/ $constructor(\"$ZodNonOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"values\", (zod) => {\n const v = zod.def.innerType._zod.values;\n return v ? new Set([...v].filter((x) => x !== void 0)) : void 0;\n });\n inst._zod.parse = (payload, ctx) => {\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) return result.then((result) => handleNonOptionalResult(result, inst));\n return handleNonOptionalResult(result, inst);\n };\n});\nfunction handleNonOptionalResult(payload, inst) {\n if (!payload.issues.length && payload.value === void 0) payload.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: payload.value,\n inst\n });\n return payload;\n}\nfunction handleCatchResult(payload, result, def, ctx) {\n if (!result.issues.length) {\n payload.value = result.value;\n if (result.memo) payload.memo = true;\n return payload;\n }\n payload.value = def.catchValue({\n ...result,\n value: payload.value,\n error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },\n input: payload.value\n });\n return payload;\n}\nconst $ZodCatch = /*@__PURE__*/ $constructor(\"$ZodCatch\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType._zod.optin === \"defaulted\" ? \"defaulted\" : \"optional\");\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.innerType._zod.optout);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n const result = def.innerType._zod.run({\n value: payload.value,\n issues: []\n }, ctx);\n if (result instanceof Promise) return result.then((result) => handleCatchResult(payload, result, def, ctx));\n return handleCatchResult(payload, result, def, ctx);\n };\n});\nconst $ZodPipe = /*@__PURE__*/ $constructor(\"$ZodPipe\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.in._zod.values);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.in._zod.optin);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.out._zod.optout);\n defineLazyInternal(inst, \"propValues\", (zod) => zod.def.in._zod.propValues);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n const right = def.out._zod.run(payload, ctx);\n if (right instanceof Promise) return right.then((right) => handlePipeResult(right, def.in, ctx));\n return handlePipeResult(right, def.in, ctx);\n }\n const left = def.in._zod.run(payload, ctx);\n if (left instanceof Promise) return left.then((left) => handlePipeResult(left, def.out, ctx));\n return handlePipeResult(left, def.out, ctx);\n };\n});\nfunction handlePipeResult(left, next, ctx) {\n if (left.issues.some((iss) => iss.code !== \"unrecognized_keys\")) {\n left.aborted = true;\n return left;\n }\n return next._zod.run({\n value: left.value,\n issues: left.issues\n }, ctx);\n}\nconst $ZodReadonly = /*@__PURE__*/ $constructor(\"$ZodReadonly\", (inst, def) => {\n $ZodType.init(inst, def);\n defineLazyInternal(inst, \"propValues\", (zod) => zod.def.innerType._zod.propValues);\n defineLazyInternal(inst, \"values\", (zod) => zod.def.innerType._zod.values);\n defineLazyInternal(inst, \"optin\", (zod) => zod.def.innerType?._zod?.optin);\n defineLazyInternal(inst, \"optout\", (zod) => zod.def.innerType?._zod?.optout);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") return def.innerType._zod.run(payload, ctx);\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) return result.then(handleReadonlyResult);\n return handleReadonlyResult(result);\n };\n});\nfunction handleReadonlyResult(payload) {\n if (!payload.memo) payload.value = Object.freeze(payload.value);\n return payload;\n}\nconst $ZodCustom = /*@__PURE__*/ $constructor(\"$ZodCustom\", (inst, def) => {\n $ZodCheck.init(inst, def);\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _) => {\n return payload;\n };\n inst._zod.check = (payload) => {\n const input = payload.value;\n const r = def.fn(input);\n if (r instanceof Promise) return r.then((r) => handleRefineResult(r, payload, input, inst));\n handleRefineResult(r, payload, input, inst);\n };\n});\nfunction handleRefineResult(result, payload, input, inst) {\n if (!result) {\n const _iss = {\n code: \"custom\",\n input,\n inst,\n path: [...inst._zod.def.path ?? []],\n continue: !inst._zod.def.abort\n };\n if (inst._zod.def.params) _iss.params = inst._zod.def.params;\n payload.issues.push(issue(_iss));\n }\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/memoizer.js\nvar $ZodCyclicError = class extends Error {\n constructor() {\n super(`Cannot parse a reference cycle that closes through a transform`);\n this.name = \"ZodCyclicError\";\n }\n};\n/** Keyed off the context object every schema in one parse call already shares. */\nconst STATE = \"~memo\";\nconst NO_ISSUES = [];\nfunction isRef(value) {\n return value !== null && typeof value === \"object\";\n}\nfunction cloneIssues(issues) {\n return issues.map((iss) => iss.path ? {\n ...iss,\n path: iss.path.slice()\n } : { ...iss });\n}\nconst recursive = /*@__PURE__*/ new WeakMap();\n/** What the walk established, in order of certainty: ordered so the strongest answer among children wins. */\nconst NONE = 0;\nconst ASSUMED = 1;\nconst PROVEN = 2;\n/** Whether this schema's subtree contains a cycle, so one parse can re-enter it. */\nfunction isRecursive(inst, stack, resolve) {\n const cached = recursive.get(inst);\n if (cached !== void 0) return cached ? PROVEN : NONE;\n if (stack.has(inst)) return PROVEN;\n stack.add(inst);\n let result = NONE;\n const check = (child) => {\n if (result !== PROVEN && child?._zod) {\n const answer = isRecursive(child, stack, resolve);\n if (answer > result) result = answer;\n }\n };\n const shape = (sh, spread) => {\n let answer = NONE;\n for (const key of Reflect.ownKeys(sh)) {\n const desc = Object.getOwnPropertyDescriptor(sh, key);\n if (spread && !desc.enumerable) continue;\n const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve) : NONE;\n if (child > answer) answer = child;\n }\n return answer;\n };\n const merge = (answer) => {\n if (answer > result) result = answer;\n };\n const def = inst._zod.def;\n switch (def.type) {\n case \"object\": {\n const raw = rawShape(def);\n merge(raw ? shape(raw, true) : ASSUMED);\n check(def.catchall);\n break;\n }\n case \"array\":\n check(def.element);\n break;\n case \"tuple\":\n for (const el of def.items) check(el);\n check(def.rest);\n break;\n case \"record\":\n case \"map\":\n check(def.keyType);\n check(def.valueType);\n break;\n case \"set\":\n check(def.valueType);\n break;\n case \"union\":\n for (const el of def.options) check(el);\n break;\n case \"intersection\":\n check(def.left);\n check(def.right);\n break;\n case \"optional\":\n case \"nullable\":\n case \"default\":\n case \"prefault\":\n case \"catch\":\n case \"readonly\":\n case \"nonoptional\":\n case \"promise\":\n case \"success\":\n check(def.innerType);\n break;\n case \"pipe\":\n check(def.in);\n check(def.out);\n break;\n case \"function\":\n check(def.input);\n check(def.output);\n break;\n case \"lazy\": {\n const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : void 0);\n merge(inner ? isRecursive(inner, stack, false) : ASSUMED);\n break;\n }\n case \"template_literal\":\n case \"string\":\n case \"number\":\n case \"int\":\n case \"boolean\":\n case \"bigint\":\n case \"symbol\":\n case \"undefined\":\n case \"null\":\n case \"void\":\n case \"never\":\n case \"any\":\n case \"unknown\":\n case \"date\":\n case \"nan\":\n case \"enum\":\n case \"literal\":\n case \"file\":\n case \"transform\":\n case \"custom\": break;\n default: for (const key in def) {\n const desc = Object.getOwnPropertyDescriptor(def, key);\n if (!desc || desc.get) continue;\n const value = desc.value;\n if (!value || typeof value !== \"object\") continue;\n if (value._zod) check(value);\n else if (Array.isArray(value)) for (const el of value) check(el);\n }\n }\n stack.delete(inst);\n return settle(inst, result);\n}\n/** An assumed answer must not outlive the resolution that settles it, so only a certain one is cached. */\nfunction settle(inst, answer) {\n if (answer !== ASSUMED) recursive.set(inst, answer === PROVEN);\n return answer;\n}\nfunction bucketFor(state, inst) {\n let bucket = state.buckets.get(inst);\n if (!bucket) {\n bucket = /* @__PURE__ */ new WeakMap();\n state.buckets.set(inst, bucket);\n }\n return bucket;\n}\nlet handoff;\nconst open = [];\nconst memo = {\n alloc(_inst, payload, empty) {\n const bucket = handoff;\n if (!bucket) return empty;\n handoff = void 0;\n const entry = {\n value: empty,\n issues: null\n };\n bucket.set(payload.value, entry);\n open.push(entry);\n return empty;\n },\n guard(inst) {\n var _a;\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred.push(() => {\n const base = inst._zod.parse;\n const wrapped = (payload, ctx) => {\n if (ctx.direction !== \"backward\" && isBackEdge(ctx, payload.value)) throw new $ZodCyclicError();\n return base(payload, ctx);\n };\n inst._zod.parse = wrapped;\n if (inst._zod.run === base) inst._zod.run = wrapped;\n });\n },\n attach(inst) {\n var _a;\n let isRecursiveInst;\n let rechecked = false;\n let lastCtx;\n let lastBucket;\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred.push(() => {\n const base = inst._zod.parse;\n const wrapped = (payload, ctx) => {\n if (isRecursiveInst === void 0) {\n const walked = isRecursive(inst, /* @__PURE__ */ new Set(), false);\n if (walked === NONE) {\n inst._zod.parse = base;\n if (inst._zod.run === wrapped) inst._zod.run = base;\n return base(payload, ctx);\n }\n if (walked === PROVEN || rechecked) isRecursiveInst = true;\n else rechecked = true;\n }\n const input = payload.value;\n if (!isRef(input)) return base(payload, ctx);\n let state = ctx[STATE];\n if (!state) {\n state = {\n buckets: /* @__PURE__ */ new WeakMap(),\n backEdges: void 0\n };\n ctx[STATE] = state;\n }\n let bucket;\n if (lastCtx === ctx) bucket = lastBucket;\n else {\n bucket = bucketFor(state, inst);\n lastCtx = ctx;\n lastBucket = bucket;\n }\n const hit = bucket.get(input);\n if (hit) {\n payload.value = hit.value;\n if (hit.issues) {\n if (hit.issues.length) payload.issues.push(...cloneIssues(hit.issues));\n } else {\n payload.memo = true;\n state.backEdges ?? (state.backEdges = /* @__PURE__ */ new WeakSet());\n state.backEdges.add(hit.value);\n }\n return payload;\n }\n handoff = bucket;\n const depth = open.length;\n const result = base(payload, ctx);\n handoff = void 0;\n const entry = open.length > depth ? open.pop() : void 0;\n if (result instanceof Promise) return result.then((r) => {\n if (entry) entry.issues = r.issues.length ? cloneIssues(r.issues) : NO_ISSUES;\n return r;\n });\n if (entry) entry.issues = result.issues.length ? cloneIssues(result.issues) : NO_ISSUES;\n return result;\n };\n inst._zod.parse = wrapped;\n if (inst._zod.run === base) inst._zod.run = wrapped;\n });\n }\n};\n/** The memoizer that gives containers cycle support. `zod` installs it by default; `zod/mini` opts in with `config({ memoizer: memoizer() })`. */\nfunction memoizer() {\n return memo;\n}\n/** Whether this value is a node a back-edge resolved to before it finished. */\nfunction isBackEdge(ctx, value) {\n const backEdges = ctx[STATE]?.backEdges;\n return backEdges !== void 0 && isRef(value) && backEdges.has(value);\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/en.js\nconst error = () => {\n const Sizable = {\n string: {\n unit: \"characters\",\n verb: \"to have\"\n },\n file: {\n unit: \"bytes\",\n verb: \"to have\"\n },\n array: {\n unit: \"items\",\n verb: \"to have\"\n },\n set: {\n unit: \"items\",\n verb: \"to have\"\n },\n map: {\n unit: \"entries\",\n verb: \"to have\"\n }\n };\n function getSizing(origin) {\n return Sizable[origin] ?? null;\n }\n const FormatDictionary = {\n regex: \"input\",\n email: \"email address\",\n url: \"URL\",\n emoji: \"emoji\",\n uuid: \"UUID\",\n uuidv4: \"UUIDv4\",\n uuidv6: \"UUIDv6\",\n nanoid: \"nanoid\",\n guid: \"GUID\",\n cuid: \"cuid\",\n cuid2: \"cuid2\",\n ulid: \"ULID\",\n xid: \"XID\",\n ksuid: \"KSUID\",\n datetime: \"ISO datetime\",\n date: \"ISO date\",\n time: \"ISO time\",\n duration: \"ISO duration\",\n ipv4: \"IPv4 address\",\n ipv6: \"IPv6 address\",\n mac: \"MAC address\",\n cidrv4: \"IPv4 range\",\n cidrv6: \"IPv6 range\",\n base64: \"base64-encoded string\",\n base64url: \"base64url-encoded string\",\n json_string: \"JSON string\",\n e164: \"E.164 number\",\n currency_code: \"currency code\",\n credit_card: \"credit card number\",\n iban: \"IBAN\",\n jwt: \"JWT\",\n template_literal: \"input\"\n };\n const TypeDictionary = { nan: \"NaN\" };\n function getTypeName(type, input) {\n if (type === \"number\" && typeof input === \"number\" && !Number.isFinite(input)) return String(input);\n return TypeDictionary[type] ?? type;\n }\n return (issue) => {\n switch (issue.code) {\n case \"invalid_type\": return `Invalid input: expected ${getTypeName(issue.expected)}, received ${getTypeName(parsedType(issue.input), issue.input)}`;\n case \"invalid_value\":\n if (issue.values.length === 1) return `Invalid input: expected ${stringifyPrimitive(issue.values[0])}`;\n return `Invalid option: expected one of ${joinValues(issue.values, \"|\")}`;\n case \"too_big\": {\n const adj = issue.exact ? \"exactly \" : issue.inclusive ? \"<=\" : \"<\";\n const sizing = getSizing(issue.origin);\n if (sizing) return `Too big: expected ${issue.origin ?? \"value\"} to have ${adj}${issue.maximum.toString()} ${sizing.unit ?? \"elements\"}`;\n return `Too big: expected ${issue.origin ?? \"value\"} to be ${adj}${issue.maximum.toString()}`;\n }\n case \"too_small\": {\n const adj = issue.exact ? \"exactly \" : issue.inclusive ? \">=\" : \">\";\n const sizing = getSizing(issue.origin);\n if (sizing) return `Too small: expected ${issue.origin} to have ${adj}${issue.minimum.toString()} ${sizing.unit}`;\n return `Too small: expected ${issue.origin} to be ${adj}${issue.minimum.toString()}`;\n }\n case \"invalid_format\": {\n const _issue = issue;\n if (_issue.format === \"starts_with\") return `Invalid string: must start with \"${_issue.prefix}\"`;\n if (_issue.format === \"ends_with\") return `Invalid string: must end with \"${_issue.suffix}\"`;\n if (_issue.format === \"includes\") return `Invalid string: must include \"${_issue.includes}\"`;\n if (_issue.format === \"regex\") return `Invalid string: must match pattern ${_issue.pattern}`;\n return `Invalid ${FormatDictionary[_issue.format] ?? issue.format}`;\n }\n case \"not_multiple_of\": return `Invalid number: must be a multiple of ${issue.divisor}`;\n case \"unrecognized_keys\": return `Unrecognized key${issue.keys.length > 1 ? \"s\" : \"\"}: ${joinValues(issue.keys, \", \")}`;\n case \"invalid_key\": return `Invalid key in ${issue.origin}`;\n case \"invalid_union\":\n if (issue.options && Array.isArray(issue.options) && issue.options.length > 0) return `Invalid discriminator value. Expected ${issue.options.map((o) => `'${o}'`).join(\" | \")}`;\n if (issue.inclusive === false) return \"Invalid input: more than one option matched\";\n return \"Invalid input\";\n case \"invalid_element\": return `Invalid value in ${issue.origin}`;\n default: return `Invalid input`;\n }\n };\n};\nfunction en_default() {\n return { localeError: error() };\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/registries.js\nvar _a;\nvar $ZodRegistry = class {\n constructor() {\n this._map = /* @__PURE__ */ new WeakMap();\n this._idmap = /* @__PURE__ */ new Map();\n }\n add(schema, ..._meta) {\n const meta = _meta[0];\n this._map.set(schema, meta);\n if (meta && typeof meta === \"object\" && \"id\" in meta) this._idmap.set(meta.id, schema);\n return this;\n }\n clear() {\n this._map = /* @__PURE__ */ new WeakMap();\n this._idmap = /* @__PURE__ */ new Map();\n return this;\n }\n remove(schema) {\n const meta = this._map.get(schema);\n if (meta && typeof meta === \"object\" && \"id\" in meta) this._idmap.delete(meta.id);\n this._map.delete(schema);\n return this;\n }\n get(schema) {\n const p = schema._zod.parent;\n if (p) {\n const pm = { ...this.get(p) ?? {} };\n delete pm.id;\n const f = {\n ...pm,\n ...this._map.get(schema)\n };\n return Object.keys(f).length ? f : void 0;\n }\n return this._map.get(schema);\n }\n has(schema) {\n return this._map.has(schema);\n }\n};\nfunction registry() {\n return new $ZodRegistry();\n}\n(_a = globalThis).__zod_globalRegistry ?? (_a.__zod_globalRegistry = registry());\nconst globalRegistry = globalThis.__zod_globalRegistry;\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/api.js\nfunction snapshotChecks(def) {\n if (def.checks) def.checks = [...def.checks];\n return def;\n}\n// @__NO_SIDE_EFFECTS__\nfunction _string(Class, params) {\n return new Class(snapshotChecks({\n type: \"string\",\n ...normalizeParams(params)\n }));\n}\n// @__NO_SIDE_EFFECTS__\nfunction _email(Class, params) {\n return new Class({\n type: \"string\",\n format: \"email\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _guid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"guid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuidv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v4\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuidv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v6\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uuidv7(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v7\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"url\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _emoji(Class, params) {\n return new Class({\n type: \"string\",\n format: \"emoji\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _nanoid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"nanoid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link _cuid2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\n// @__NO_SIDE_EFFECTS__\nfunction _cuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _cuid2(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid2\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ulid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ulid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _xid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"xid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ksuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ksuid\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ipv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv4\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _ipv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv6\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _cidrv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv4\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _cidrv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv6\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _base64(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _base64url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64url\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _e164(Class, params) {\n return new Class({\n type: \"string\",\n format: \"e164\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _jwt(Class, params) {\n return new Class({\n type: \"string\",\n format: \"jwt\",\n check: \"string_format\",\n abort: false,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoDateTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"datetime\",\n check: \"string_format\",\n offset: false,\n local: false,\n precision: null,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoDate(Class, params) {\n return new Class({\n type: \"string\",\n format: \"date\",\n check: \"string_format\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"time\",\n check: \"string_format\",\n precision: null,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _isoDuration(Class, params) {\n return new Class({\n type: \"string\",\n format: \"duration\",\n check: \"string_format\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _unknown(Class) {\n return new Class({ type: \"unknown\" });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _never(Class, params) {\n return new Class({\n type: \"never\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _maxLength(maximum, params) {\n return new $ZodCheckMaxLength({\n check: \"max_length\",\n ...normalizeParams(params),\n maximum\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _minLength(minimum, params) {\n return new $ZodCheckMinLength({\n check: \"min_length\",\n ...normalizeParams(params),\n minimum\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _length(length, params) {\n return new $ZodCheckLengthEquals({\n check: \"length_equals\",\n ...normalizeParams(params),\n length\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _regex(pattern, params) {\n return new $ZodCheckRegex({\n check: \"string_format\",\n format: \"regex\",\n ...normalizeParams(params),\n pattern\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _lowercase(params) {\n return new $ZodCheckLowerCase({\n check: \"string_format\",\n format: \"lowercase\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _uppercase(params) {\n return new $ZodCheckUpperCase({\n check: \"string_format\",\n format: \"uppercase\",\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _includes(includes, params) {\n return new $ZodCheckIncludes({\n check: \"string_format\",\n format: \"includes\",\n ...normalizeParams(params),\n includes\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _startsWith(prefix, params) {\n return new $ZodCheckStartsWith({\n check: \"string_format\",\n format: \"starts_with\",\n ...normalizeParams(params),\n prefix\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _endsWith(suffix, params) {\n return new $ZodCheckEndsWith({\n check: \"string_format\",\n format: \"ends_with\",\n ...normalizeParams(params),\n suffix\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _overwrite(tx) {\n return new $ZodCheckOverwrite({\n check: \"overwrite\",\n tx\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _normalize(form) {\n return /* @__PURE__ */ _overwrite((input) => input.normalize(form));\n}\n// @__NO_SIDE_EFFECTS__\nfunction _trim() {\n return /* @__PURE__ */ _overwrite((input) => input.trim());\n}\n// @__NO_SIDE_EFFECTS__\nfunction _toLowerCase() {\n return /* @__PURE__ */ _overwrite((input) => input.toLowerCase());\n}\n// @__NO_SIDE_EFFECTS__\nfunction _toUpperCase() {\n return /* @__PURE__ */ _overwrite((input) => input.toUpperCase());\n}\n// @__NO_SIDE_EFFECTS__\nfunction _slugify() {\n return /* @__PURE__ */ _overwrite((input) => slugify(input));\n}\n// @__NO_SIDE_EFFECTS__\nfunction _array(Class, element, params) {\n return new Class({\n type: \"array\",\n element,\n ...normalizeParams(params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _refine(Class, fn, _params) {\n return new Class({\n type: \"custom\",\n check: \"custom\",\n fn,\n ...normalizeParams(_params)\n });\n}\n// @__NO_SIDE_EFFECTS__\nfunction _superRefine(fn, params) {\n const ch = /* @__PURE__ */ _check((payload) => {\n payload.addIssue = (issue$2) => {\n if (typeof issue$2 === \"string\") payload.issues.push(issue(issue$2, payload.value, ch._zod.def));\n else {\n const _issue = issue$2;\n if (_issue.fatal) _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n if (!(\"input\" in _issue)) _issue.input = payload.value;\n _issue.inst ?? (_issue.inst = ch);\n _issue.continue ?? (_issue.continue = !ch._zod.def.abort);\n payload.issues.push(issue(_issue));\n }\n };\n return fn(payload.value, payload);\n }, params);\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nfunction _check(fn, params) {\n const ch = new $ZodCheck({\n check: \"custom\",\n ...normalizeParams(params)\n });\n ch._zod.check = fn;\n return ch;\n}\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/to-json-schema.js\nfunction assignProps(target, ...sources) {\n for (const source of sources) for (const key of Reflect.ownKeys(source)) if (Object.prototype.propertyIsEnumerable.call(source, key)) assignProp(target, key, source[key]);\n return target;\n}\nfunction initializeContext(params) {\n let target = params?.target ?? \"draft-2020-12\";\n if (target === \"draft-4\") target = \"draft-04\";\n if (target === \"draft-7\") target = \"draft-07\";\n return {\n processors: params.processors ?? {},\n metadataRegistry: params?.metadata ?? globalRegistry,\n target,\n unrepresentable: params?.unrepresentable ?? \"throw\",\n override: params?.override ?? (() => {}),\n io: params?.io ?? \"output\",\n counter: 0,\n seen: /* @__PURE__ */ new Map(),\n sharedDefsExtractedFor: void 0,\n sharedEmitDoneFor: void 0,\n cycles: params?.cycles ?? \"ref\",\n reused: params?.reused ?? \"inline\",\n intersections: [],\n deferred: [],\n external: params?.external ?? void 0\n };\n}\n/**\n* Applies the `unrepresentable` setting at a site that has no JSON Schema equivalent. Throws\n* `message` unless the setting (or the handler's return value) says otherwise. Returns `true` if a\n* custom JSON Schema was written into `json`, in which case the caller must not write its own.\n*/\nfunction handleUnrepresentable(schema, ctx, json, params, message) {\n const result = typeof ctx.unrepresentable === \"function\" ? ctx.unrepresentable({\n zodSchema: schema,\n path: params.path,\n message\n }) : ctx.unrepresentable;\n if (result === \"any\") return false;\n if (result === void 0 || result === \"throw\") throw new Error(message);\n Object.assign(json, result);\n return true;\n}\nfunction processSchema(schema, ctx, _params = {\n path: [],\n schemaPath: []\n}) {\n var _a;\n const def = schema._zod.def;\n const seen = ctx.seen.get(schema);\n if (seen) {\n seen.count++;\n if (_params.schemaPath.includes(schema)) seen.cycle = _params.path;\n return seen.schema;\n }\n const result = {\n schema: {},\n count: 1,\n cycle: void 0,\n path: _params.path\n };\n ctx.seen.set(schema, result);\n ctx.sharedDefsExtractedFor = void 0;\n ctx.sharedEmitDoneFor = void 0;\n const overrideSchema = schema._zod.toJSONSchema?.();\n if (overrideSchema) result.schema = overrideSchema;\n else {\n const params = {\n ..._params,\n schemaPath: [..._params.schemaPath, schema],\n path: _params.path\n };\n if (schema._zod.processJSONSchema) schema._zod.processJSONSchema(ctx, result.schema, params);\n else {\n const _json = result.schema;\n const processor = ctx.processors[def.type];\n if (!processor) throw new Error(`[toJSONSchema]: Non-representable type encountered: ${def.type}`);\n processor(schema, ctx, _json, params);\n }\n const parent = schema._zod.parent;\n if (parent) {\n if (!result.ref) result.ref = parent;\n processSchema(parent, ctx, params);\n ctx.seen.get(parent).isParent = true;\n }\n }\n const meta = ctx.metadataRegistry.get(schema);\n if (meta) assignProps(result.schema, meta);\n if (ctx.io === \"input\" && isTransforming(schema)) {\n delete result.schema.examples;\n delete result.schema.default;\n }\n if (ctx.io === \"input\" && \"_prefault\" in result.schema) (_a = result.schema).default ?? (_a.default = result.schema._prefault);\n delete result.schema._prefault;\n return ctx.seen.get(schema).schema;\n}\nfunction encodeJSONPointerSegment(segment) {\n return segment.replace(/~/g, \"~0\").replace(/\\//g, \"~1\");\n}\nfunction extractDefs(ctx, schema) {\n const root = ctx.seen.get(schema);\n if (!root) throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n if (ctx.external && ctx.sharedDefsExtractedFor === ctx.external) return;\n const idToSchema = /* @__PURE__ */ new Map();\n for (const entry of ctx.seen.entries()) {\n const id = ctx.metadataRegistry.get(entry[0])?.id;\n if (id) {\n const existing = idToSchema.get(id);\n if (existing && existing !== entry[0]) throw new Error(`Duplicate schema id \"${id}\" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);\n idToSchema.set(id, entry[0]);\n }\n }\n const makeURI = (entry) => {\n const defsSegment = ctx.target === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n if (ctx.external) {\n const externalId = ctx.external.registry.get(entry[0])?.id;\n const uriGenerator = ctx.external.uri ?? ((id) => id);\n if (externalId) return { ref: uriGenerator(externalId) };\n const id = entry[1].defId ?? entry[1].schema.id ?? `schema${ctx.counter++}`;\n entry[1].defId = id;\n return {\n defId: id,\n ref: `${uriGenerator(\"__shared\")}#/${defsSegment}/${encodeJSONPointerSegment(id)}`\n };\n }\n const uriPrefix = `#`;\n const defUriPrefix = `${uriPrefix}/${defsSegment}/`;\n if (entry[1] === root && !entry[1].schema.id) return { ref: uriPrefix };\n const defId = entry[1].schema.id ?? `__schema${ctx.counter++}`;\n return {\n defId,\n ref: defUriPrefix + encodeJSONPointerSegment(defId)\n };\n };\n const extractToDef = (entry) => {\n if (entry[1].schema.$ref) return;\n const seen = entry[1];\n const { ref, defId } = makeURI(entry);\n seen.def = { ...seen.schema };\n if (defId) seen.defId = defId;\n const schema = seen.schema;\n for (const key in schema) delete schema[key];\n schema.$ref = ref;\n };\n if (ctx.cycles === \"throw\") for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.cycle) throw new Error(`Cycle detected: #/${seen.cycle?.join(\"/\")}/<root>\n\nSet the \\`cycles\\` parameter to \\`\"ref\"\\` to resolve cyclical schemas with defs.`);\n }\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (schema === entry[0]) {\n extractToDef(entry);\n continue;\n }\n if (ctx.external) {\n const ext = ctx.external.registry.get(entry[0])?.id;\n if (schema !== entry[0] && ext) {\n extractToDef(entry);\n continue;\n }\n }\n if (ctx.metadataRegistry.get(entry[0])?.id) {\n extractToDef(entry);\n continue;\n }\n if (seen.cycle) {\n extractToDef(entry);\n continue;\n }\n if (seen.count > 1) {\n if (ctx.reused === \"ref\") extractToDef(entry);\n }\n }\n if (ctx.external) ctx.sharedDefsExtractedFor = ctx.external;\n}\n/** Rewrites `anyOf: [{type: \"a\"}, {type: \"b\"}]` to `type: [\"a\", \"b\"]`, which every JSON Schema draft treats as equivalent and most consumers render far better for the nullable case. Only branches that are a bare type assertion qualify — anything carrying a constraint, `$ref`, `const` or metadata is left alone. Runs after `flattenRef`, so a branch an override decorated or `$defs` extraction turned into a `$ref` is no longer bare and correctly stays in `anyOf`. `oneOf` is excluded: `integer` and `number` overlap, so \"exactly one\" and \"at least one\" are not the same there. OpenAPI 3.0 is excluded: its `type` must be a single string. */\nfunction compactTypeUnion(schema) {\n const options = schema.anyOf;\n if (!Array.isArray(options) || options.length === 0 || schema.type !== void 0) return;\n const types = [];\n for (const option of options) {\n if (!option || typeof option !== \"object\") return;\n compactTypeUnion(option);\n const keys = Object.keys(option);\n if (keys.length !== 1 || keys[0] !== \"type\") return;\n const type = option.type;\n for (const member of Array.isArray(type) ? type : [type]) {\n if (typeof member !== \"string\") return;\n if (!types.includes(member)) types.push(member);\n }\n }\n delete schema.anyOf;\n schema.type = types.length === 1 ? types[0] : types;\n}\n/** Keywords `foldIntersection` knows how to combine. Anything else — `$ref`, `patternProperties`,\n* an annotation like `description` — makes a member unfoldable, so a constraint this does not\n* understand leaves the `allOf` alone instead of being silently dropped or misattributed. */\nconst FOLDABLE_KEYS = /* @__PURE__ */ new Set([\n \"type\",\n \"properties\",\n \"required\",\n \"additionalProperties\"\n]);\nconst UNION_KEYS = [\"oneOf\", \"anyOf\"];\n/** A member's constraint on a key it does not declare itself. A `catchall` states one; `false`, an absent `additionalProperties`, and the empty schema a loose object emits state nothing. */\nfunction undeclaredConstraint(member) {\n const extra = member.additionalProperties;\n if (extra === void 0 || extra === false || typeof extra !== \"object\" || extra === null) return null;\n return Object.keys(extra).length ? extra : null;\n}\n/** Combines object members into the single object they describe together, or returns `null` if any of them carries a keyword outside {@link FOLDABLE_KEYS}. */\nfunction foldObjects(members) {\n const objects = [];\n for (const member of members) {\n if (typeof member !== \"object\" || member.type !== \"object\") return null;\n for (const key in member) if (!FOLDABLE_KEYS.has(key)) return null;\n objects.push(member);\n }\n const properties = {};\n const required = /* @__PURE__ */ new Set();\n for (const object of objects) {\n for (const key in object.properties) {\n if (Object.prototype.hasOwnProperty.call(properties, key)) continue;\n const parts = [];\n for (const other of objects) {\n const part = other.properties?.[key] ?? undeclaredConstraint(other);\n if (part === null || part === void 0) continue;\n if (!parts.some((seen) => JSON.stringify(seen) === JSON.stringify(part))) parts.push(part);\n }\n const merged = parts.length === 1 ? parts[0] : foldObjects(parts) ?? { allOf: parts };\n assignProp(properties, key, merged);\n }\n for (const key of object.required ?? []) required.add(key);\n }\n const folded = {\n type: \"object\",\n properties\n };\n if (required.size) folded.required = [...required];\n if (objects.every((object) => object.additionalProperties === false)) folded.additionalProperties = false;\n else {\n const constraints = [];\n for (const object of objects) {\n const constraint = undeclaredConstraint(object);\n if (constraint && !constraints.some((seen) => JSON.stringify(seen) === JSON.stringify(constraint))) constraints.push(constraint);\n }\n if (constraints.length === 1) folded.additionalProperties = constraints[0];\n else if (constraints.length > 1) folded.additionalProperties = { allOf: constraints };\n }\n return folded;\n}\n/** `additionalProperties` in an `allOf` member sees only that member's own `properties`, so two\n* closed object members reject each other's keys and the schema validates nothing. Zod's parser\n* pools the key sets instead — `handleIntersectionResults` reports a key as unrecognized only when\n* *every* side rejects it — so the emitted schema has to pool them too, and folding the members\n* into one object is the encoding that says so on every target.\n*\n* This runs from `finalize`, after `extractDefs`, which is what keeps it clear of the `$ref`\n* machinery: a member extracted into `$defs` is already a `$ref` by now and declines to fold, so it\n* keeps its reference and its own closedness rather than being inlined as a stale copy. */\nfunction foldIntersection(json) {\n const allOf = json.allOf;\n if (!Array.isArray(allOf) || allOf.length < 2) return;\n for (const key of FOLDABLE_KEYS) if (key in json) return;\n const unions = allOf.filter((m) => UNION_KEYS.some((k) => Array.isArray(m[k])));\n let folded = null;\n if (!unions.length) folded = foldObjects(allOf);\n else {\n const union = unions[0];\n const keyword = UNION_KEYS.find((k) => Array.isArray(union[k]));\n if (Object.keys(union).length !== 1) return;\n const rest = allOf.filter((m) => m !== union);\n const branches = union[keyword].map((branch) => foldObjects([...rest, branch]));\n if (branches.some((b) => !b)) return;\n folded = { [keyword]: branches };\n }\n if (!folded) return;\n delete json.allOf;\n assignProps(json, folded);\n}\nfunction finalize(ctx, schema) {\n const root = ctx.seen.get(schema);\n if (!root) throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n const flattenRef = (zodSchema) => {\n const seen = ctx.seen.get(zodSchema);\n if (seen.ref === null) return;\n const schema = seen.def ?? seen.schema;\n const _cached = { ...schema };\n const ref = seen.ref;\n seen.ref = null;\n if (ref) {\n flattenRef(ref);\n const refSeen = ctx.seen.get(ref);\n const refSchema = refSeen.schema;\n if (refSchema.$ref && (ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\")) {\n schema.allOf = schema.allOf ?? [];\n schema.allOf.push(refSchema);\n } else assignProps(schema, refSchema);\n assignProps(schema, _cached);\n if (zodSchema._zod.parent === ref) for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\") continue;\n if (!(key in _cached)) delete schema[key];\n }\n if (refSchema.$ref && refSeen.def) for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\") continue;\n if (key in refSeen.def && JSON.stringify(schema[key]) === JSON.stringify(refSeen.def[key])) delete schema[key];\n }\n }\n const parent = zodSchema._zod.parent;\n if (parent && parent !== ref) {\n flattenRef(parent);\n const parentSeen = ctx.seen.get(parent);\n if (parentSeen?.schema.$ref) {\n schema.$ref = parentSeen.schema.$ref;\n if (parentSeen.def) for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\") continue;\n if (key in parentSeen.def && JSON.stringify(schema[key]) === JSON.stringify(parentSeen.def[key])) delete schema[key];\n }\n }\n }\n ctx.override({\n zodSchema,\n jsonSchema: schema,\n path: seen.path ?? []\n });\n };\n if (!ctx.external || ctx.sharedEmitDoneFor !== ctx.external) {\n for (const entry of [...ctx.seen.entries()].reverse()) flattenRef(entry[0]);\n if (ctx.target !== \"openapi-3.0\") for (const entry of ctx.seen.entries()) compactTypeUnion(entry[1].def ?? entry[1].schema);\n for (const rewrite of ctx.deferred) rewrite();\n if (ctx.intersections.length) {\n const carriers = /* @__PURE__ */ new Map();\n for (const seen of ctx.seen.values()) for (const json of [seen.schema, seen.def]) {\n const allOf = json?.allOf;\n if (!Array.isArray(allOf)) continue;\n const existing = carriers.get(allOf);\n if (existing) existing.push(json);\n else carriers.set(allOf, [json]);\n }\n for (const allOf of ctx.intersections) for (const json of carriers.get(allOf) ?? []) foldIntersection(json);\n }\n }\n const result = {};\n if (ctx.target === \"draft-2020-12\") result.$schema = \"https://json-schema.org/draft/2020-12/schema\";\n else if (ctx.target === \"draft-07\") result.$schema = \"http://json-schema.org/draft-07/schema#\";\n else if (ctx.target === \"draft-04\") result.$schema = \"http://json-schema.org/draft-04/schema#\";\n else if (ctx.target === \"openapi-3.0\") {}\n if (ctx.external?.uri) {\n const id = ctx.external.registry.get(schema)?.id;\n if (!id) throw new Error(\"Schema is missing an `id` property\");\n result.$id = ctx.external.uri(id);\n }\n assignProps(result, root.defId ? root.schema : root.def ?? root.schema);\n const rootMetaId = ctx.metadataRegistry.get(schema)?.id;\n if (rootMetaId !== void 0 && result.id === rootMetaId) delete result.id;\n const defs = ctx.external?.defs ?? {};\n if (!ctx.external || ctx.sharedEmitDoneFor !== ctx.external) for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.def && seen.defId) {\n if (seen.def.id === seen.defId) delete seen.def.id;\n assignProp(defs, seen.defId, seen.def);\n }\n }\n if (ctx.external) ctx.sharedEmitDoneFor = ctx.external;\n if (ctx.external) {} else if (Object.keys(defs).length > 0) {\n if (ctx.target === \"draft-2020-12\") result.$defs = defs;\n else result.definitions = defs;\n }\n try {\n const finalized = JSON.parse(JSON.stringify(result));\n Object.defineProperty(finalized, \"~standard\", {\n value: {\n ...schema[\"~standard\"],\n jsonSchema: {\n input: createStandardJSONSchemaMethod(schema, \"input\", ctx.processors),\n output: createStandardJSONSchemaMethod(schema, \"output\", ctx.processors)\n }\n },\n enumerable: false,\n writable: false\n });\n return finalized;\n } catch (_err) {\n throw new Error(\"Error converting schema to JSON.\");\n }\n}\nfunction isTransforming(_schema, _ctx) {\n const ctx = _ctx ?? { seen: /* @__PURE__ */ new Set() };\n if (ctx.seen.has(_schema)) return false;\n ctx.seen.add(_schema);\n const def = _schema._zod.def;\n if (def.type === \"transform\") return true;\n if (def.type === \"array\") return isTransforming(def.element, ctx);\n if (def.type === \"set\") return isTransforming(def.valueType, ctx);\n if (def.type === \"lazy\") return isTransforming(def.getter(), ctx);\n if (def.type === \"promise\" || def.type === \"optional\" || def.type === \"nonoptional\" || def.type === \"nullable\" || def.type === \"readonly\" || def.type === \"default\" || def.type === \"prefault\" || def.type === \"catch\") return isTransforming(def.innerType, ctx);\n if (def.type === \"intersection\") return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);\n if (def.type === \"record\" || def.type === \"map\") return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);\n if (def.type === \"pipe\") {\n if (_schema._zod.traits.has(\"$ZodCodec\")) return true;\n return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);\n }\n if (def.type === \"object\") {\n for (const key in def.shape) if (isTransforming(def.shape[key], ctx)) return true;\n return false;\n }\n if (def.type === \"union\") {\n for (const option of def.options) if (isTransforming(option, ctx)) return true;\n return false;\n }\n if (def.type === \"tuple\") {\n for (const item of def.items) if (isTransforming(item, ctx)) return true;\n if (def.rest && isTransforming(def.rest, ctx)) return true;\n return false;\n }\n return false;\n}\n/**\n* Creates a toJSONSchema method for a schema instance.\n* This encapsulates the logic of initializing context, processing, extracting defs, and finalizing.\n*/\nconst createToJSONSchemaMethod = (schema, processors = {}) => (params) => {\n const ctx = initializeContext({\n ...params,\n processors\n });\n processSchema(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\nconst createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {\n const { libraryOptions, target } = params ?? {};\n const ctx = initializeContext({\n ...libraryOptions ?? {},\n target,\n io,\n processors\n });\n processSchema(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/json-schema-processors.js\nconst narrowMin = (agg, key, value) => {\n if (agg[key] === void 0 || value > agg[key]) agg[key] = value;\n};\nconst narrowMax = (agg, key, value) => {\n if (agg[key] === void 0 || value < agg[key]) agg[key] = value;\n};\nconst narrowBoth = (agg, value) => {\n narrowMin(agg, \"minimum\", value);\n narrowMax(agg, \"maximum\", value);\n};\nconst addDivisor = (agg, value) => {\n agg.multipleOf ?? (agg.multipleOf = []);\n if (!agg.multipleOf.includes(value)) agg.multipleOf.push(value);\n};\nconst addPattern = (agg, pattern) => {\n agg.patterns ?? (agg.patterns = /* @__PURE__ */ new Set());\n agg.patterns.add(pattern);\n};\nconst intersectMime = (agg, mime) => {\n agg.mime = agg.mime ? agg.mime.filter((m) => mime.includes(m)) : [...mime];\n};\nconst setFormat = (agg, format) => {\n agg.format = format;\n if (format.includes(\"int\")) agg.isInt = true;\n};\nconst minContributor = (agg, def) => narrowMin(agg, \"minimum\", def.minimum);\nconst maxContributor = (agg, def) => narrowMax(agg, \"maximum\", def.maximum);\nconst formatContributor = (ranges) => (agg, def) => {\n setFormat(agg, def.format);\n const [minimum, maximum] = ranges[def.format];\n narrowMin(agg, \"minimum\", minimum);\n narrowMax(agg, \"maximum\", maximum);\n};\nconst contributors = {\n greater_than: (agg, def) => narrowMin(agg, def.inclusive ? \"minimum\" : \"exclusiveMinimum\", def.value),\n less_than: (agg, def) => narrowMax(agg, def.inclusive ? \"maximum\" : \"exclusiveMaximum\", def.value),\n multiple_of: (agg, def) => addDivisor(agg, def.value),\n number_format: formatContributor(NUMBER_FORMAT_RANGES),\n bigint_format: formatContributor(BIGINT_FORMAT_RANGES),\n min_length: minContributor,\n max_length: maxContributor,\n length_equals: (agg, def) => narrowBoth(agg, def.length),\n min_size: minContributor,\n max_size: maxContributor,\n size_equals: (agg, def) => narrowBoth(agg, def.size),\n string_format: (agg, def) => {\n setFormat(agg, def.format);\n if (def.pattern) addPattern(agg, def.pattern);\n if (def.format === \"base64\" || def.format === \"base64url\") agg.contentEncoding = def.format;\n if (def.local || def.precision === -1) agg.laxFormat = true;\n },\n mime_type: (agg, def) => intersectMime(agg, def.mime)\n};\nfunction aggregateChecks(schema) {\n const agg = {};\n const def = schema._zod.def;\n const list = schema._zod.traits.has(\"$ZodCheck\") ? [schema, ...def.checks ?? []] : def.checks ?? [];\n for (const ch of list) contributors[ch._zod.def.check]?.(agg, ch._zod.def);\n const bag = schema._zod.bag;\n if (bag.minimum !== void 0) narrowMin(agg, \"minimum\", bag.minimum);\n if (bag.exclusiveMinimum !== void 0) narrowMin(agg, \"exclusiveMinimum\", bag.exclusiveMinimum);\n if (bag.maximum !== void 0) narrowMax(agg, \"maximum\", bag.maximum);\n if (bag.exclusiveMaximum !== void 0) narrowMax(agg, \"exclusiveMaximum\", bag.exclusiveMaximum);\n if (bag.multipleOf !== void 0) addDivisor(agg, bag.multipleOf);\n if (bag.format !== void 0) {\n agg.format ?? (agg.format = bag.format);\n if (bag.format.includes(\"int\")) agg.isInt = true;\n }\n if (bag.mime) intersectMime(agg, bag.mime);\n for (const pattern of bag.patterns ?? []) addPattern(agg, pattern);\n return agg;\n}\nconst formatMap = {\n guid: \"uuid\",\n url: \"uri\",\n datetime: \"date-time\",\n json_string: \"json-string\",\n regex: \"\"\n};\nconst exactPatterns = /* @__PURE__ */ new Map([[base64Charset, base64], [base64urlCharset, base64url]]);\nconst exactPattern = (p) => exactPatterns.get(p) ?? p;\nconst stringProcessor = (schema, ctx, _json, _params) => {\n const json = _json;\n json.type = \"string\";\n const { minimum, maximum, format, patterns, contentEncoding, laxFormat } = aggregateChecks(schema);\n if (typeof minimum === \"number\") json.minLength = minimum;\n if (typeof maximum === \"number\") json.maxLength = maximum;\n if (format) {\n json.format = formatMap[format] ?? format;\n if (json.format === \"\") delete json.format;\n if (format === \"time\" || laxFormat) delete json.format;\n }\n if (contentEncoding) json.contentEncoding = contentEncoding;\n if (patterns && patterns.size > 0) {\n const patternList = [...patterns].map(exactPattern);\n if (patternList.length === 1) json.pattern = patternList[0].source;\n else if (patternList.length > 1) json.allOf = [...patternList.map((regex) => ({\n ...ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\" ? { type: \"string\" } : {},\n pattern: regex.source\n }))];\n }\n};\nconst neverProcessor = (_schema, _ctx, json, _params) => {\n json.not = {};\n};\nconst unknownProcessor = (_schema, _ctx, _json, _params) => {};\nconst enumProcessor = (schema, _ctx, json, _params) => {\n const def = schema._zod.def;\n const values = getEnumValues(def.entries);\n if (values.length === 0) {\n json.not = {};\n return;\n }\n if (values.every((v) => typeof v === \"number\")) json.type = \"number\";\n if (values.every((v) => typeof v === \"string\")) json.type = \"string\";\n json.enum = values;\n};\nconst customProcessor = (schema, ctx, json, params) => {\n handleUnrepresentable(schema, ctx, json, params, \"Custom types cannot be represented in JSON Schema\");\n};\nconst transformProcessor = (schema, ctx, json, params) => {\n handleUnrepresentable(schema, ctx, json, params, \"Transforms cannot be represented in JSON Schema\");\n};\nconst arrayProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n const { minimum, maximum } = aggregateChecks(schema);\n if (typeof minimum === \"number\") json.minItems = minimum;\n if (typeof maximum === \"number\") json.maxItems = maximum;\n json.type = \"array\";\n json.items = processSchema(def.element, ctx, {\n ...params,\n path: [...params.path, \"items\"]\n });\n};\nfunction inputOptin(schema) {\n const def = schema._zod.def;\n if (def.type === \"pipe\" && def.in._zod.traits.has(\"$ZodTransform\")) return inputOptin(def.out);\n if (def.type === \"catch\") return inputOptin(def.innerType);\n return schema._zod.optin;\n}\nconst objectProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n const shape = def.shape;\n if (Object.getOwnPropertySymbols(shape).length && handleUnrepresentable(schema, ctx, json, params, \"Symbol keys cannot be represented in JSON Schema\")) return;\n json.type = \"object\";\n json.properties = {};\n for (const key in shape) assignProp(json.properties, key, processSchema(shape[key], ctx, {\n ...params,\n path: [\n ...params.path,\n \"properties\",\n key\n ]\n }));\n const requiredKeys = [];\n for (const key of Object.keys(shape)) {\n const field = def.shape[key];\n if (ctx.io === \"input\" ? inputOptin(field) === void 0 : field._zod.optout === void 0) requiredKeys.push(key);\n }\n if (requiredKeys.length > 0) json.required = requiredKeys;\n if (def.catchall?._zod.def.type === \"never\") json.additionalProperties = false;\n else if (!def.catchall) {\n if (ctx.io === \"output\") json.additionalProperties = false;\n } else if (def.catchall) json.additionalProperties = processSchema(def.catchall, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"]\n });\n};\nconst unionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const isExclusive = def.inclusive === false;\n const options = def.options.map((x, i) => processSchema(x, ctx, {\n ...params,\n path: [\n ...params.path,\n isExclusive ? \"oneOf\" : \"anyOf\",\n i\n ]\n }));\n if (isExclusive) json.oneOf = options;\n else json.anyOf = options;\n};\nconst intersectionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const a = processSchema(def.left, ctx, {\n ...params,\n path: [\n ...params.path,\n \"allOf\",\n 0\n ]\n });\n const b = processSchema(def.right, ctx, {\n ...params,\n path: [\n ...params.path,\n \"allOf\",\n 1\n ]\n });\n const isSimpleIntersection = (val) => \"allOf\" in val && Object.keys(val).length === 1;\n const allOf = [...isSimpleIntersection(a) ? a.allOf : [a], ...isSimpleIntersection(b) ? b.allOf : [b]];\n json.allOf = allOf;\n ctx.intersections.push(allOf);\n};\n/** JSON object keys are always strings, so a numeric record key schema is re-expressed over the\n* numeric-string form the record parser matches. Deferred to `finalize`, after the flatten: a key\n* behind a wrapper only carries its own `type` before then, and a union key only has its branches.\n*\n* A numeric bound cannot apply to a property name, so `minimum` and its siblings are dropped rather\n* than carried over: keeping them beside `type: \"string\"` reproduces the match-nothing schema this\n* exists to fix. A key that carries one therefore emits wider than the record parses — `z.record(z.number().min(5), V)`\n* accepts `\"3\"` — which is the deliberate trade, since throwing on it would reject an ordinary schema\n* outright. */\nfunction stringifyKeyNames(bySchema, json, visited) {\n if (json.$ref) {\n if (visited.has(json)) return json;\n visited.add(json);\n const def = bySchema.get(json)?.def;\n if (!def) return json;\n const inlined = stringifyKeyNames(bySchema, def, visited);\n return inlined === def ? json : inlined;\n }\n for (const keyword of [\"anyOf\", \"oneOf\"]) {\n const branches = json[keyword];\n if (!Array.isArray(branches)) continue;\n const mapped = branches.map((branch) => stringifyKeyNames(bySchema, branch, visited));\n if (mapped.some((branch, i) => branch !== branches[i])) json = {\n ...json,\n [keyword]: mapped\n };\n }\n const types = Array.isArray(json.type) ? json.type : [json.type];\n const numericType = !types.includes(\"string\") && types.some((t) => t === \"number\" || t === \"integer\");\n const values = json.enum ?? (json.const !== void 0 ? [json.const] : void 0);\n if (!numericType && !values?.some((v) => typeof v === \"number\")) return json;\n const { minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, format, id, ...rest } = json;\n if (rest.enum) rest.enum = rest.enum.map((v) => typeof v === \"number\" ? String(v) : v);\n else if (typeof rest.const === \"number\") rest.const = String(rest.const);\n if (!numericType) return rest;\n rest.type = \"string\";\n if (!values) rest.pattern = (types.includes(\"number\") ? number : integer).source;\n return rest;\n}\n/** Every record of one conversion, so the carriers are found in a single pass rather than once per record. */\nconst pendingRecords = /* @__PURE__ */ new WeakMap();\nfunction rewriteKeyNames(ctx) {\n const bySchema = /* @__PURE__ */ new Map();\n for (const entry of ctx.seen.values()) if (entry.def && !bySchema.has(entry.schema)) bySchema.set(entry.schema, entry);\n const rewrites = /* @__PURE__ */ new Map();\n for (const record of pendingRecords.get(ctx) ?? []) {\n const seen = ctx.seen.get(record);\n const names = (seen?.def ?? seen?.schema)?.propertyNames;\n if (!names || names === true || rewrites.has(names)) continue;\n const rewritten = stringifyKeyNames(bySchema, names, /* @__PURE__ */ new Set());\n if (rewritten !== names) rewrites.set(names, rewritten);\n }\n if (!rewrites.size) return;\n for (const entry of ctx.seen.values()) for (const carrier of [entry.schema, entry.def]) {\n const rewritten = carrier && rewrites.get(carrier.propertyNames);\n if (rewritten) carrier.propertyNames = rewritten;\n }\n}\nconst recordProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"object\";\n const keyType = def.keyType;\n const patterns = aggregateChecks(keyType).patterns;\n if (def.mode === \"loose\" && patterns && patterns.size > 0) {\n const valueSchema = processSchema(def.valueType, ctx, {\n ...params,\n path: [\n ...params.path,\n \"patternProperties\",\n \"*\"\n ]\n });\n json.patternProperties = {};\n for (const pattern of patterns) assignProp(json.patternProperties, exactPattern(pattern).source, valueSchema);\n } else {\n if (ctx.target === \"draft-07\" || ctx.target === \"draft-2020-12\") {\n json.propertyNames = processSchema(def.keyType, ctx, {\n ...params,\n path: [...params.path, \"propertyNames\"]\n });\n let pending = pendingRecords.get(ctx);\n if (!pending) {\n pending = [];\n pendingRecords.set(ctx, pending);\n ctx.deferred.push(() => rewriteKeyNames(ctx));\n }\n pending.push(schema);\n }\n json.additionalProperties = processSchema(def.valueType, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"]\n });\n }\n const keyValues = keyType._zod.values;\n const omittableOnInput = ctx.io === \"input\" && inputOptin(def.valueType) !== void 0;\n if (keyValues && !def.partial && !omittableOnInput) {\n const validKeyValues = [...keyValues].filter((v) => typeof v === \"string\" || typeof v === \"number\");\n if (validKeyValues.length > 0) json.required = validKeyValues.map(String);\n }\n};\nconst nullableProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const inner = processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n if (ctx.target === \"openapi-3.0\") {\n seen.ref = def.innerType;\n json.nullable = true;\n } else json.anyOf = [inner, { type: \"null\" }];\n};\nconst nonoptionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\n/** Round-trips a default value through JSON so the emitted schema is guaranteed to be valid JSON.\n* A BigInt has no reliable encoding, so it goes through `unrepresentable` like any other\n* unrepresentable value. Returns a sentinel when the caller must not write a default of its own. */\nconst UNREPRESENTABLE_DEFAULT = Symbol();\nfunction serializeDefaultValue(value, schema, ctx, json, params) {\n let unrepresentable = false;\n const serialized = JSON.stringify(value, (_, val) => {\n if (typeof val !== \"bigint\") return val;\n unrepresentable = true;\n return null;\n });\n if (!unrepresentable) return JSON.parse(serialized);\n handleUnrepresentable(schema, ctx, json, params, \"BigInt defaults cannot be represented in JSON Schema\");\n return UNREPRESENTABLE_DEFAULT;\n}\nconst defaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n const value = serializeDefaultValue(def.defaultValue, schema, ctx, json, params);\n if (value !== UNREPRESENTABLE_DEFAULT) json.default = value;\n};\nconst prefaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n if (ctx.io !== \"input\") return;\n const value = serializeDefaultValue(def.defaultValue, schema, ctx, json, params);\n if (value !== UNREPRESENTABLE_DEFAULT) json._prefault = value;\n};\nconst catchProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n let catchValue;\n try {\n catchValue = def.catchValue(void 0);\n } catch {\n handleUnrepresentable(schema, ctx, json, params, \"Dynamic catch values are not supported in JSON Schema\");\n return;\n }\n json.default = catchValue;\n};\nconst pipeProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n const inIsTransform = def.in._zod.traits.has(\"$ZodTransform\");\n const innerType = ctx.io === \"input\" ? inIsTransform ? def.out : def.in : def.out;\n processSchema(innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = innerType;\n};\nconst readonlyProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n json.readOnly = true;\n};\nconst optionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n processSchema(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/errors.js\nconst _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);\nfunction _lazyMethod(proto, key, make) {\n Object.defineProperty(proto, key, {\n configurable: true,\n enumerable: false,\n get() {\n const value = make(this);\n Object.defineProperty(this, key, {\n value,\n configurable: true,\n writable: true\n });\n return value;\n },\n set(value) {\n Object.defineProperty(this, key, {\n value,\n configurable: true,\n writable: true\n });\n }\n });\n}\nconst initializer = (inst, issues) => {\n $ZodError.init(inst, issues);\n inst.name = \"ZodError\";\n const proto = Object.getPrototypeOf(inst);\n if (_installedErrorProtos.has(proto)) return;\n _installedErrorProtos.add(proto);\n _lazyMethod(proto, \"format\", (self) => (mapper) => formatError(self, mapper));\n _lazyMethod(proto, \"flatten\", (self) => (mapper) => flattenError(self, mapper));\n _lazyMethod(proto, \"addIssue\", (self) => (issue) => {\n self.issues.push(issue);\n self.message = JSON.stringify(self.issues, jsonStringifyReplacer, 2);\n });\n _lazyMethod(proto, \"addIssues\", (self) => (issues) => {\n self.issues.push(...issues);\n self.message = JSON.stringify(self.issues, jsonStringifyReplacer, 2);\n });\n Object.defineProperty(proto, \"isEmpty\", {\n configurable: true,\n enumerable: false,\n get() {\n return this.issues.length === 0;\n }\n });\n};\nconst ZodRealError = /*@__PURE__*/ $constructor(\"ZodError\", initializer, void 0, { Parent: Error });\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/parse.js\nconst parse = /* @__PURE__ */ _parse(ZodRealError);\nconst parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);\nconst safeParse = /* @__PURE__ */ _safeParse(ZodRealError);\nconst safeParseAsync = /* @__PURE__ */ _safeParseAsync(ZodRealError);\nconst encode = /* @__PURE__ */ _encode(ZodRealError);\nconst decode = /* @__PURE__ */ _decode(ZodRealError);\nconst encodeAsync = /* @__PURE__ */ _encodeAsync(ZodRealError);\nconst decodeAsync = /* @__PURE__ */ _decodeAsync(ZodRealError);\nconst safeEncode = /* @__PURE__ */ _safeEncode(ZodRealError);\nconst safeDecode = /* @__PURE__ */ _safeDecode(ZodRealError);\nconst safeEncodeAsync = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);\nconst safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);\n\n//#endregion\n//#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/schemas.js\nfunction _ensureDefaultLocale() {\n if (!globalConfig.localeError) config(en_default());\n}\nfunction _ensureDefaultMemoizer() {\n if (!globalConfig.memoizer) config({ memoizer: memoizer() });\n}\nconst ZodType = /*@__PURE__*/ $constructor(\"ZodType\", (inst, def) => {\n _ensureDefaultLocale();\n $ZodType.init(inst, def);\n inst.def = def;\n inst.type = def.type;\n return inst;\n}, {\n check(...chks) {\n const def = this.def;\n return this.clone(mergeDefs(def, { checks: [...def.checks ?? [], ...chks.map((ch) => typeof ch === \"function\" ? { _zod: {\n check: ch,\n def: { check: \"custom\" },\n onattach: []\n } } : ch)] }), { parent: true });\n },\n with(...chks) {\n return this.check(...chks);\n },\n clone(def, params) {\n return clone(this, def, params);\n },\n brand() {\n return this;\n },\n register(reg, meta) {\n reg.add(this, meta);\n return this;\n },\n refine(check, params) {\n return this.check(refine(check, params));\n },\n superRefine(refinement, params) {\n return this.check(superRefine(refinement, params));\n },\n overwrite(fn) {\n return this.check(_overwrite(fn));\n },\n optional() {\n return optional(this);\n },\n exactOptional() {\n return exactOptional(this);\n },\n nullable() {\n return nullable(this);\n },\n nullish() {\n return optional(nullable(this));\n },\n nonoptional(params) {\n return nonoptional(this, params);\n },\n array() {\n return array(this);\n },\n or(arg) {\n return union([this, arg]);\n },\n and(arg) {\n return intersection(this, arg);\n },\n transform(tx) {\n return pipe(this, transform(tx));\n },\n default(d) {\n return _default(this, d);\n },\n prefault(d) {\n return prefault(this, d);\n },\n catch(params) {\n return _catch(this, params);\n },\n pipe(target) {\n return pipe(this, target);\n },\n readonly() {\n return readonly(this);\n },\n describe(description) {\n const cl = this.clone();\n globalRegistry.add(cl, { description });\n return cl;\n },\n meta(...args) {\n if (args.length === 0) return globalRegistry.get(this);\n const cl = this.clone();\n globalRegistry.add(cl, args[0]);\n return cl;\n },\n isOptional() {\n return this.safeParse(void 0).success;\n },\n isNullable() {\n return this.safeParse(null).success;\n },\n apply(fn, ...args) {\n return args.length === 0 ? fn(this) : fn(this, ...args);\n },\n get \"~standard\"() {\n return hide(this, \"~standard\", {\n ...standardProps(this),\n jsonSchema: {\n input: createStandardJSONSchemaMethod(this, \"input\"),\n output: createStandardJSONSchemaMethod(this, \"output\")\n }\n });\n },\n set \"~standard\"(value) {\n own(this, \"~standard\", value);\n },\n parse: function _parse(data, params) {\n return parse(this, data, params, { callee: _parse });\n },\n parseAsync: async function _parseAsync(data, params) {\n return await parseAsync(this, data, params, { callee: _parseAsync });\n },\n safeParse(data, params) {\n return safeParse(this, data, params);\n },\n async safeParseAsync(data, params) {\n return safeParseAsync(this, data, params);\n },\n get spa() {\n return this?.safeParseAsync;\n },\n set spa(value) {\n own(this, \"spa\", value);\n },\n validate(data, params) {\n return validate(this, data, params);\n },\n validateAsync(data, params) {\n return validateAsync$1(this, data, params);\n },\n encode: function _encode(data, params) {\n return encode(this, data, params, { callee: _encode });\n },\n decode: function _decode(data, params) {\n return decode(this, data, params, { callee: _decode });\n },\n encodeAsync: async function _encodeAsync(data, params) {\n return await encodeAsync(this, data, params, { callee: _encodeAsync });\n },\n decodeAsync: async function _decodeAsync(data, params) {\n return await decodeAsync(this, data, params, { callee: _decodeAsync });\n },\n safeEncode(data, params) {\n return safeEncode(this, data, params);\n },\n safeDecode(data, params) {\n return safeDecode(this, data, params);\n },\n async safeEncodeAsync(data, params) {\n return safeEncodeAsync(this, data, params);\n },\n async safeDecodeAsync(data, params) {\n return safeDecodeAsync(this, data, params);\n },\n toJSONSchema(params) {\n return createToJSONSchemaMethod(this, {})(params);\n },\n get description() {\n return globalRegistry.get(this)?.description;\n },\n get _def() {\n return this._zod.def;\n }\n});\n/** @internal */\nconst _ZodString = /*@__PURE__*/ $constructor(\"_ZodString\", (inst, def) => {\n $ZodString.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => stringProcessor(inst, ctx, json, params);\n}, /*@__PURE__*/ derived({\n format: (inst) => aggregateChecks(inst).format ?? null,\n minLength: (inst) => aggregateChecks(inst).minimum ?? null,\n maxLength: (inst) => aggregateChecks(inst).maximum ?? null\n}, {\n regex(...args) {\n return this.check(_regex(...args));\n },\n includes(...args) {\n return this.check(_includes(...args));\n },\n startsWith(...args) {\n return this.check(_startsWith(...args));\n },\n endsWith(...args) {\n return this.check(_endsWith(...args));\n },\n min(...args) {\n return this.check(_minLength(...args));\n },\n max(...args) {\n return this.check(_maxLength(...args));\n },\n length(...args) {\n return this.check(_length(...args));\n },\n nonempty(...args) {\n return this.check(_minLength(1, ...args));\n },\n lowercase(params) {\n return this.check(_lowercase(params));\n },\n uppercase(params) {\n return this.check(_uppercase(params));\n },\n trim() {\n return this.check(_trim());\n },\n normalize(...args) {\n return this.check(_normalize(...args));\n },\n toLowerCase() {\n return this.check(_toLowerCase());\n },\n toUpperCase() {\n return this.check(_toUpperCase());\n },\n slugify() {\n return this.check(_slugify());\n }\n}));\nconst ZodString = /*@__PURE__*/ $constructor(\"ZodString\", (inst, def) => {\n $ZodString.init(inst, def);\n _ZodString.init(inst, def);\n}, {\n email(params) {\n return this.check(_email(ZodEmail, params));\n },\n url(params) {\n return this.check(_url(ZodURL, params));\n },\n jwt(params) {\n return this.check(_jwt(ZodJWT, params));\n },\n emoji(params) {\n return this.check(_emoji(ZodEmoji, params));\n },\n guid(params) {\n return this.check(_guid(ZodGUID, params));\n },\n uuid(params) {\n return this.check(_uuid(ZodUUID, params));\n },\n uuidv4(params) {\n return this.check(_uuidv4(ZodUUID, params));\n },\n uuidv6(params) {\n return this.check(_uuidv6(ZodUUID, params));\n },\n uuidv7(params) {\n return this.check(_uuidv7(ZodUUID, params));\n },\n nanoid(params) {\n return this.check(_nanoid(ZodNanoID, params));\n },\n cuid(params) {\n return this.check(_cuid(ZodCUID, params));\n },\n cuid2(params) {\n return this.check(_cuid2(ZodCUID2, params));\n },\n ulid(params) {\n return this.check(_ulid(ZodULID, params));\n },\n base64(params) {\n return this.check(_base64(ZodBase64, params));\n },\n base64url(params) {\n return this.check(_base64url(ZodBase64URL, params));\n },\n xid(params) {\n return this.check(_xid(ZodXID, params));\n },\n ksuid(params) {\n return this.check(_ksuid(ZodKSUID, params));\n },\n ipv4(params) {\n return this.check(_ipv4(ZodIPv4, params));\n },\n ipv6(params) {\n return this.check(_ipv6(ZodIPv6, params));\n },\n cidrv4(params) {\n return this.check(_cidrv4(ZodCIDRv4, params));\n },\n cidrv6(params) {\n return this.check(_cidrv6(ZodCIDRv6, params));\n },\n e164(params) {\n return this.check(_e164(ZodE164, params));\n },\n datetime(params) {\n return this.check(_isoDateTime(ZodISODateTime, params));\n },\n date(params) {\n return this.check(_isoDate(ZodISODate, params));\n },\n time(params) {\n return this.check(_isoTime(ZodISOTime, params));\n },\n duration(params) {\n return this.check(_isoDuration(ZodISODuration, params));\n }\n});\nfunction string(params) {\n return _string(ZodString, params);\n}\nconst ZodStringFormat = /*@__PURE__*/ $constructor(\"ZodStringFormat\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n _ZodString.init(inst, def);\n});\nconst ZodISODateTime = /*@__PURE__*/ $constructor(\"ZodISODateTime\", (inst, def) => {\n $ZodISODateTime.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodISODate = /*@__PURE__*/ $constructor(\"ZodISODate\", (inst, def) => {\n $ZodISODate.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodISOTime = /*@__PURE__*/ $constructor(\"ZodISOTime\", (inst, def) => {\n $ZodISOTime.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodISODuration = /*@__PURE__*/ $constructor(\"ZodISODuration\", (inst, def) => {\n $ZodISODuration.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodEmail = /*@__PURE__*/ $constructor(\"ZodEmail\", (inst, def) => {\n $ZodEmail.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodGUID = /*@__PURE__*/ $constructor(\"ZodGUID\", (inst, def) => {\n $ZodGUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodUUID = /*@__PURE__*/ $constructor(\"ZodUUID\", (inst, def) => {\n $ZodUUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodURL = /*@__PURE__*/ $constructor(\"ZodURL\", (inst, def) => {\n $ZodURL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodEmoji = /*@__PURE__*/ $constructor(\"ZodEmoji\", (inst, def) => {\n $ZodEmoji.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodNanoID = /*@__PURE__*/ $constructor(\"ZodNanoID\", (inst, def) => {\n $ZodNanoID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\n/**\n* @deprecated CUID v1 is deprecated by its authors due to information leakage\n* (timestamps embedded in the id). Use {@link ZodCUID2} instead.\n* See https://github.com/paralleldrive/cuid.\n*/\nconst ZodCUID = /*@__PURE__*/ $constructor(\"ZodCUID\", (inst, def) => {\n $ZodCUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodCUID2 = /*@__PURE__*/ $constructor(\"ZodCUID2\", (inst, def) => {\n $ZodCUID2.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodULID = /*@__PURE__*/ $constructor(\"ZodULID\", (inst, def) => {\n $ZodULID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodXID = /*@__PURE__*/ $constructor(\"ZodXID\", (inst, def) => {\n $ZodXID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodKSUID = /*@__PURE__*/ $constructor(\"ZodKSUID\", (inst, def) => {\n $ZodKSUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodIPv4 = /*@__PURE__*/ $constructor(\"ZodIPv4\", (inst, def) => {\n $ZodIPv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodIPv6 = /*@__PURE__*/ $constructor(\"ZodIPv6\", (inst, def) => {\n $ZodIPv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodCIDRv4 = /*@__PURE__*/ $constructor(\"ZodCIDRv4\", (inst, def) => {\n $ZodCIDRv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodCIDRv6 = /*@__PURE__*/ $constructor(\"ZodCIDRv6\", (inst, def) => {\n $ZodCIDRv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodBase64 = /*@__PURE__*/ $constructor(\"ZodBase64\", (inst, def) => {\n $ZodBase64.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodBase64URL = /*@__PURE__*/ $constructor(\"ZodBase64URL\", (inst, def) => {\n $ZodBase64URL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodE164 = /*@__PURE__*/ $constructor(\"ZodE164\", (inst, def) => {\n $ZodE164.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodJWT = /*@__PURE__*/ $constructor(\"ZodJWT\", (inst, def) => {\n $ZodJWT.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nconst ZodUnknown = /*@__PURE__*/ $constructor(\"ZodUnknown\", (inst, def) => {\n $ZodUnknown.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => unknownProcessor(inst, ctx, json, params);\n});\nfunction unknown() {\n return _unknown(ZodUnknown);\n}\nconst ZodNever = /*@__PURE__*/ $constructor(\"ZodNever\", (inst, def) => {\n $ZodNever.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => neverProcessor(inst, ctx, json, params);\n});\nfunction never(params) {\n return _never(ZodNever, params);\n}\nconst ZodArray = /*@__PURE__*/ $constructor(\"ZodArray\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodArray.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => arrayProcessor(inst, ctx, json, params);\n inst.element = def.element;\n}, {\n min(n, params) {\n return this.check(_minLength(n, params));\n },\n nonempty(params) {\n return this.check(_minLength(1, params));\n },\n max(n, params) {\n return this.check(_maxLength(n, params));\n },\n length(n, params) {\n return this.check(_length(n, params));\n },\n unwrap() {\n return this.element;\n }\n});\nfunction array(element, params) {\n return _array(ZodArray, element, params);\n}\nconst ZodObject = /*@__PURE__*/ $constructor(\"ZodObject\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodObjectJIT.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => objectProcessor(inst, ctx, json, params);\n installLazyProp(inst, \"shape\", (self) => self._zod.def.shape, false);\n}, {\n keyof() {\n return _enum(Object.keys(this._zod.def.shape));\n },\n catchall(catchall) {\n return this.clone(mergeDefs(this._zod.def, { catchall }));\n },\n passthrough() {\n return this.clone(mergeDefs(this._zod.def, { catchall: unknown() }));\n },\n loose() {\n return this.clone(mergeDefs(this._zod.def, { catchall: unknown() }));\n },\n strict() {\n return this.clone(mergeDefs(this._zod.def, { catchall: never() }));\n },\n strip() {\n return this.clone(mergeDefs(this._zod.def, { catchall: void 0 }));\n },\n extend(incoming) {\n return extend(this, incoming);\n },\n safeExtend(incoming) {\n return safeExtend(this, incoming);\n },\n merge(other) {\n return merge(this, other);\n },\n pick(mask) {\n return pick(this, mask);\n },\n omit(mask) {\n return omit(this, mask);\n },\n partial(...args) {\n return partial(ZodOptional, this, args[0]);\n },\n exactPartial(...args) {\n return partial(ZodExactOptional, this, args[0], \"exactPartial\");\n },\n required(...args) {\n return required(ZodNonOptional, this, args[0]);\n }\n});\nfunction object(shape, params) {\n const def = {\n type: \"object\",\n shape: shape ?? {},\n ...normalizeParams(params)\n };\n return new ZodObject(def);\n}\nconst ZodUnion = /*@__PURE__*/ $constructor(\"ZodUnion\", (inst, def) => {\n $ZodUnion.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => unionProcessor(inst, ctx, json, params);\n inst.options = def.options;\n});\nfunction union(options, params) {\n return new ZodUnion({\n type: \"union\",\n options,\n ...normalizeParams(params)\n });\n}\nconst ZodIntersection = /*@__PURE__*/ $constructor(\"ZodIntersection\", (inst, def) => {\n $ZodIntersection.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => intersectionProcessor(inst, ctx, json, params);\n});\nfunction intersection(left, right) {\n return new ZodIntersection({\n type: \"intersection\",\n left,\n right\n });\n}\nconst ZodRecord = /*@__PURE__*/ $constructor(\"ZodRecord\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodRecord.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => recordProcessor(inst, ctx, json, params);\n inst.keyType = def.keyType;\n inst.valueType = def.valueType;\n});\nfunction record(keyType, valueType, params) {\n if (!valueType || !valueType._zod) return new ZodRecord({\n type: \"record\",\n keyType: string(),\n valueType: keyType,\n ...normalizeParams(valueType)\n });\n return new ZodRecord({\n type: \"record\",\n keyType,\n valueType,\n ...normalizeParams(params)\n });\n}\nconst ZodEnum = /*@__PURE__*/ $constructor(\"ZodEnum\", (inst, def) => {\n $ZodEnum.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => enumProcessor(inst, ctx, json, params);\n inst.enum = def.entries;\n inst.options = [...inst._zod.values];\n const keys = new Set(Object.keys(def.entries));\n inst.extract = (values, params) => {\n const newEntries = {};\n for (const value of values) if (keys.has(value)) newEntries[value] = def.entries[value];\n else throw new Error(`Key ${value} not found in enum`);\n return new ZodEnum({\n ...def,\n checks: [],\n ...normalizeParams(params),\n entries: newEntries\n });\n };\n inst.exclude = (values, params) => {\n const newEntries = { ...def.entries };\n for (const value of values) if (keys.has(value)) delete newEntries[value];\n else throw new Error(`Key ${value} not found in enum`);\n return new ZodEnum({\n ...def,\n checks: [],\n ...normalizeParams(params),\n entries: newEntries\n });\n };\n});\nfunction _enum(values, params) {\n const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;\n return new ZodEnum({\n type: \"enum\",\n entries,\n ...normalizeParams(params)\n });\n}\nconst ZodTransform = /*@__PURE__*/ $constructor(\"ZodTransform\", (inst, def) => {\n _ensureDefaultMemoizer();\n $ZodTransform.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => transformProcessor(inst, ctx, json, params);\n inst._zod.parse = (payload, _ctx) => {\n if (_ctx.direction === \"backward\") throw new $ZodEncodeError(inst.constructor.name);\n payload.addIssue = (issue$1) => {\n if (typeof issue$1 === \"string\") payload.issues.push(issue(issue$1, payload.value, def));\n else {\n const _issue = issue$1;\n if (_issue.fatal) _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n if (!(\"input\" in _issue)) _issue.input = payload.value;\n _issue.inst ?? (_issue.inst = inst);\n payload.issues.push(issue(_issue));\n }\n };\n const output = def.transform(payload.value, payload);\n if (output instanceof Promise) return output.then((output) => {\n payload.value = output;\n return payload;\n });\n payload.value = output;\n return payload;\n };\n});\nfunction transform(fn) {\n return new ZodTransform({\n type: \"transform\",\n transform: fn\n });\n}\nconst ZodOptional = /*@__PURE__*/ $constructor(\"ZodOptional\", (inst, def) => {\n $ZodOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction optional(innerType) {\n return new ZodOptional({\n type: \"optional\",\n innerType\n });\n}\nconst ZodExactOptional = /*@__PURE__*/ $constructor(\"ZodExactOptional\", (inst, def) => {\n $ZodExactOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction exactOptional(innerType) {\n return new ZodExactOptional({\n type: \"optional\",\n innerType\n });\n}\nconst ZodNullable = /*@__PURE__*/ $constructor(\"ZodNullable\", (inst, def) => {\n $ZodNullable.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => nullableProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction nullable(innerType) {\n return new ZodNullable({\n type: \"nullable\",\n innerType\n });\n}\nconst ZodDefault = /*@__PURE__*/ $constructor(\"ZodDefault\", (inst, def) => {\n $ZodDefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => defaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeDefault = inst.unwrap;\n});\nfunction _default(innerType, defaultValue) {\n return new ZodDefault({\n type: \"default\",\n innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : shallowClone(defaultValue);\n }\n });\n}\nconst ZodPrefault = /*@__PURE__*/ $constructor(\"ZodPrefault\", (inst, def) => {\n $ZodPrefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => prefaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction prefault(innerType, defaultValue) {\n return new ZodPrefault({\n type: \"prefault\",\n innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : shallowClone(defaultValue);\n }\n });\n}\nconst ZodNonOptional = /*@__PURE__*/ $constructor(\"ZodNonOptional\", (inst, def) => {\n $ZodNonOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => nonoptionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction nonoptional(innerType, params) {\n return new ZodNonOptional({\n type: \"nonoptional\",\n innerType,\n ...normalizeParams(params)\n });\n}\nconst ZodCatch = /*@__PURE__*/ $constructor(\"ZodCatch\", (inst, def) => {\n $ZodCatch.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => catchProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeCatch = inst.unwrap;\n});\nfunction _catch(innerType, catchValue) {\n return new ZodCatch({\n type: \"catch\",\n innerType,\n catchValue: typeof catchValue === \"function\" ? catchValue : constantCatch(catchValue)\n });\n}\nconst ZodPipe = /*@__PURE__*/ $constructor(\"ZodPipe\", (inst, def) => {\n $ZodPipe.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => pipeProcessor(inst, ctx, json, params);\n inst.in = def.in;\n inst.out = def.out;\n});\nfunction pipe(in_, out) {\n return new ZodPipe({\n type: \"pipe\",\n in: in_,\n out\n });\n}\nconst ZodReadonly = /*@__PURE__*/ $constructor(\"ZodReadonly\", (inst, def) => {\n $ZodReadonly.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => readonlyProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nfunction readonly(innerType) {\n return new ZodReadonly({\n type: \"readonly\",\n innerType\n });\n}\nconst ZodCustom = /*@__PURE__*/ $constructor(\"ZodCustom\", (inst, def) => {\n $ZodCustom.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);\n});\nfunction refine(fn, _params = {}) {\n return _refine(ZodCustom, fn, _params);\n}\nfunction superRefine(fn, params) {\n return _superRefine(fn, params);\n}\n\n//#endregion\n//#region src/lib/hook-io.ts\nconst HookInputSchema = object({\n session_id: string(),\n transcript_path: string(),\n cwd: string(),\n permission_mode: string(),\n hook_event_name: string(),\n tool_name: string(),\n tool_input: record(string(), unknown())\n});\n/**\n* Read and parse the full stdin as JSON.\n* Returns a result with the validated HookInput, or the raw data and error on failure.\n*/\nasync function readHookInput() {\n const chunks = [];\n for await (const chunk of process.stdin) chunks.push(typeof chunk === \"string\" ? chunk : String(chunk));\n let parsed;\n try {\n parsed = JSON.parse(chunks.join(\"\"));\n } catch (_error) {\n return {\n ok: false,\n raw: chunks.join(\"\").slice(0, 200),\n error: \"invalid JSON\"\n };\n }\n const result = HookInputSchema.safeParse(parsed);\n if (!result.success) return {\n ok: false,\n raw: parsed,\n error: result.error.message\n };\n return {\n ok: true,\n data: result.data\n };\n}\nconst BashInputSchema = object({\n command: string(),\n description: optional(string())\n});\nconst FilePathInputSchema = object({ file_path: string() });\nconst SearchInputSchema = object({\n pattern: string(),\n path: optional(string())\n});\n/** Parse tool_input as Bash input. Returns null if invalid. */\nfunction parseBashInput(toolInput) {\n const result = BashInputSchema.safeParse(toolInput);\n return result.success ? result.data : null;\n}\n/** Parse tool_input as file-path input (Read, Write, Edit). Returns null if invalid. */\nfunction parseFilePathInput(toolInput) {\n const result = FilePathInputSchema.safeParse(toolInput);\n return result.success ? result.data : null;\n}\n/** Parse tool_input as search input (Glob, Grep). Returns null if invalid. */\nfunction parseSearchInput(toolInput) {\n const result = SearchInputSchema.safeParse(toolInput);\n return result.success ? result.data : null;\n}\n/**\n* Write a deny decision to stdout as JSON, with `reason` emitted as\n* `permissionDecisionReason` when present.\n*/\nfunction writeDeny(reason) {\n const decision = { hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n ...reason ? { permissionDecisionReason: reason } : {}\n } };\n process.stdout.write(JSON.stringify(decision));\n}\n/**\n* Write a non-blocking advisory to stdout as `additionalContext`, with no\n* `permissionDecision` at all. Claude Code still shows a PreToolUse hook's\n* `additionalContext` to the model when the output carries no permission\n* decision (checked by hand against a live session), so a `none` result can\n* still carry advice.\n*/\nfunction writeAdvice(advice) {\n const decision = { hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n additionalContext: advice\n } };\n process.stdout.write(JSON.stringify(decision));\n}\n\n//#endregion\n//#region src/lib/audit.ts\nconst MAX_LOG_SIZE = 1048576;\n/**\n* Resolve the audit log path relative to the hook's own location.\n* Co-located at `.claude/hooks/bst-hook.audit.local.jsonl`.\n*/\nfunction getLogPath() {\n const hookDir = path.dirname(fileURLToPath(import.meta.url));\n return path.join(hookDir, \"bst-hook.audit.local.jsonl\");\n}\n/**\n* Rotate the log file if it exceeds MAX_LOG_SIZE.\n* Renames current log to `.1` and starts fresh.\n* Safe against concurrent rotation -- ENOENT on rename is expected and ignored.\n*/\nfunction rotateIfNeeded(logPath) {\n try {\n if (fs.statSync(logPath).size > MAX_LOG_SIZE) {\n const rotatedPath = `${logPath}.1`;\n fs.renameSync(logPath, rotatedPath);\n }\n } catch (_error) {}\n}\n/** Whether rotation has been checked this session */\nlet rotationChecked = false;\n/**\n* Append an audit entry to the JSONL log.\n* Never throws -- audit failures log to stderr and continue.\n*/\nfunction audit(entry) {\n try {\n const logPath = getLogPath();\n if (!rotationChecked) {\n rotateIfNeeded(logPath);\n rotationChecked = true;\n }\n const record = {\n timestamp: (/* @__PURE__ */ new Date()).toISOString(),\n ...entry\n };\n fs.appendFileSync(logPath, JSON.stringify(record) + \"\\n\");\n } catch (err) {\n process.stderr.write(`[bst-hook] audit write failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n}\n\n//#endregion\n//#region src/lib/shell-tokenizer.ts\n/**\n* Scan a double-quoted string starting at `start` (the char after the opening\n* quote). Returns the unescaped content and the index just past the closing\n* quote, or `null` if the quote is never closed.\n*/\nfunction scanDoubleQuoted(input, start) {\n let content = \"\";\n let i = start;\n while (i < input.length && input[i] !== \"\\\"\") {\n if (input[i] === \"\\\\\" && i + 1 < input.length) {\n const next = input[i + 1];\n if (next === \"\\\"\" || next === \"\\\\\" || next === \"$\" || next === \"`\") {\n content += next;\n i += 2;\n continue;\n }\n }\n content += input[i];\n i++;\n }\n if (i >= input.length) return null;\n return {\n content,\n next: i + 1\n };\n}\nfunction tokenize(input) {\n const tokens = [];\n let current = \"\";\n let i = 0;\n let inToken = false;\n while (i < input.length) {\n const ch = input[i];\n if (ch === \"'\") {\n inToken = true;\n i++;\n while (i < input.length && input[i] !== \"'\") {\n current += input[i];\n i++;\n }\n if (i >= input.length) return {\n ok: false,\n error: \"unterminated single quote\"\n };\n i++;\n } else if (ch === \"\\\"\") {\n inToken = true;\n const scanned = scanDoubleQuoted(input, i + 1);\n if (!scanned) return {\n ok: false,\n error: \"unterminated double quote\"\n };\n current += scanned.content;\n i = scanned.next;\n } else if (ch === \"\\\\\" && i + 1 < input.length) {\n inToken = true;\n current += input[i + 1];\n i += 2;\n } else if (ch === \" \" || ch === \" \") {\n if (inToken) {\n tokens.push(current);\n current = \"\";\n inToken = false;\n }\n i++;\n } else {\n inToken = true;\n current += ch;\n i++;\n }\n }\n if (inToken) tokens.push(current);\n return {\n ok: true,\n tokens\n };\n}\n/**\n* Characters safe to leave unquoted when re-emitting a token. Anything outside\n* this set could be interpreted by the shell-splitter or tokenizer on a\n* subsequent pass (operators, whitespace, quotes, globs, vars, etc.).\n*/\nconst SAFE_UNQUOTED_RE = /^[A-Za-z0-9_\\-./=:@%+,]+$/;\n/** Single-quote-wrap a token unless it only contains safe characters. */\nfunction quoteToken(token) {\n if (token.length > 0 && SAFE_UNQUOTED_RE.test(token)) return token;\n return \"'\" + token.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n/**\n* Inverse of `tokenize`: join tokens into a command string that survives\n* round-tripping through `splitCommand` and `tokenize`.\n*\n* Naive `tokens.join(\" \")` is lossy: any token that was originally quoted\n* loses its quote wrapper and may be mis-parsed when split/tokenized again\n* (e.g. `--testPathPattern=a|b` would split on the embedded `|`).\n*/\nfunction joinTokens(tokens) {\n return tokens.map(quoteToken).join(\" \");\n}\n\n//#endregion\n//#region src/lib/shell-splitter.ts\n/**\n* Split a raw command string into segments on unquoted &&, ||, ;, and | operators.\n* Quote-aware: operators inside single or double quotes are not split points.\n*\n* Returns segments with the operator that follows each one (null for the last segment).\n*/\nfunction splitCommand(input) {\n if (!input || !input.trim()) return [];\n const segments = [];\n let current = \"\";\n let i = 0;\n let inSingleQuote = false;\n let inDoubleQuote = false;\n while (i < input.length) {\n const ch = input[i];\n if (ch === \"'\" && !inDoubleQuote) {\n inSingleQuote = !inSingleQuote;\n current += ch;\n i++;\n continue;\n }\n if (ch === \"\\\"\" && !inSingleQuote) {\n inDoubleQuote = !inDoubleQuote;\n current += ch;\n i++;\n continue;\n }\n if (ch === \"\\\\\" && !inSingleQuote && i + 1 < input.length) {\n current += ch + input[i + 1];\n i += 2;\n continue;\n }\n if (!inSingleQuote && !inDoubleQuote) {\n if (ch === \"&\" && i + 1 < input.length && input[i + 1] === \"&\") {\n pushSegment(segments, current, \"&&\");\n current = \"\";\n i += 2;\n continue;\n }\n if (ch === \"&\" && !(i + 1 < input.length && input[i + 1] === \">\") && !(i > 0 && (input[i - 1] === \">\" || input[i - 1] === \"<\"))) {\n pushSegment(segments, current, \"&\");\n current = \"\";\n i++;\n continue;\n }\n if (ch === \"|\" && i + 1 < input.length && input[i + 1] === \"|\") {\n pushSegment(segments, current, \"||\");\n current = \"\";\n i += 2;\n continue;\n }\n if (ch === \"|\") {\n pushSegment(segments, current, \"|\");\n current = \"\";\n i++;\n continue;\n }\n if (ch === \";\") {\n pushSegment(segments, current, \";\");\n current = \"\";\n i++;\n continue;\n }\n }\n current += ch;\n i++;\n }\n const trimmed = current.trim();\n if (trimmed) segments.push({\n command: trimmed,\n operator: null\n });\n return segments;\n}\nfunction pushSegment(segments, text, operator) {\n const trimmed = text.trim();\n if (trimmed) segments.push({\n command: trimmed,\n operator\n });\n}\n\n//#endregion\n//#region src/lib/injection-guard.ts\n/**\n* Bash brace expansion: a `{...}` group containing a comma (list form,\n* `{a,b}`/`{a,}`/`{,a}`) or `..` (range form, `{1..3}`/`{a..z}`) is expanded\n* into multiple words *before* the command runs -- so `--for{c,}e` reaches the\n* program as argv `--force --fore` and `r{m,}m` as `rmm rm`. Braces without a\n* comma or `..` (`a{b}`, `HEAD@{1}`, `{}`) are left literal by bash and must\n* not be flagged.\n*\n* Bash brace expansion is *recursive*: a comma/range list expands even when a\n* branch of it contains its own nested `{...}`, so `--for{c,{}}e` still yields\n* `--force`. A regex cannot express \"a `,` or `..` at balanced brace depth\"\n* (the earlier `/\\{[^{}]*(?:,|\\.\\.)[^{}]*\\}/` required the separator to be\n* flanked by brace-free text out to the enclosing braces, which nested braces\n* defeat while bash still expands). So this is a depth-aware scan: increment on\n* `{`, decrement on `}` (never below 0), and flag the token if a `,` or `..`\n* appears while depth >= 1 -- i.e. anywhere inside any brace group, no matter\n* how deeply nested. Fail-closed: unbalanced braces are flagged too.\n*\n* As with `$`-expansion, the tokenizer has already stripped single quotes by\n* the time `checkInjection` runs, so a genuinely-quoted `'{a,b}'` or\n* `'{\"a\":1,\"b\":2}'` is indistinguishable from an unquoted brace-expansion here\n* and is flagged too -- an accepted fail-closed false positive (a `none`\n* result just defers to Claude Code's own classifier).\n*/\nfunction hasBraceExpansion(token) {\n let depth = 0;\n for (let i = 0; i < token.length; i++) {\n const ch = token[i];\n if (ch === \"{\") depth++;\n else if (ch === \"}\") {\n if (depth > 0) depth--;\n } else if (depth >= 1 && (ch === \",\" || ch === \".\" && token[i + 1] === \".\")) return true;\n }\n return false;\n}\n/**\n* Detect dangerous patterns in tokenized command segments.\n*\n* Checks for:\n* - Command substitution (`$(...)`, backticks)\n* - Any other unresolved `$`-expansion (`${...}`, `$'...'`, `$\"...\"`, `$VAR`, `$1`, ...)\n* - Brace expansion (`{a,b}` list form, `{1..3}` range form)\n* - Process substitution (`<(...)`, `>(...)`)\n* - Newlines in raw segment (injection via line splitting)\n*\n* Redirect targets (`>`, `>>`, `<`) are deliberately not checked here any\n* more -- with no allow tier, a bare redirect is no longer a reason to\n* withhold a decision on its own, so `evaluateSegment` judges a redirect's\n* *target* itself (the node-modules-write and env-file-write deny rules)\n* instead of asking this guard to flag the operator generically. See\n* `evaluateSegment`'s own comment for why that check has to run ahead of this\n* one.\n*\n* The tokenizer (`shell-tokenizer.ts`) resolves single-quoted and\n* backslash-escaped content into plain characters before this function ever\n* sees it -- so a token's surviving `$` is indistinguishable between \"bash\n* will expand this\" (`$(...)`, `${...}`, `$'...'`, `$\"...\"`, `$VAR`, `$1`,\n* `$@`, `$?`, ...) and \"this `$` was always a literal character\" (e.g. from\n* `'cost $5'` or `\\$5`). Rather than trying to reconstruct quote context\n* after the fact, any token still containing a `$` is treated as unsafe.\n* This is deliberately fail-closed: it routes the segment to `none` (Claude\n* Code's own classifier judges it from there) rather than risking an\n* unresolved expansion silently being denied or not based on the wrong argv.\n*\n* ## Expansions this guard knowingly does NOT model\n*\n* Two weaker members of the same \"unmodeled expansion\" family are left open. Both were\n* surfaced by the adversarial review of the issue-attachments hardening and verified\n* empirically against real bash plus this evaluator. **Decision (2026-07-13): accept as\n* documented limitations.** They are recorded here, not as a todo, because the next\n* person to touch this function is the person who needs to know.\n*\n* **Glob reconstruction.** `git push --forc[e] origin feat/x` (also `--forc?`, `--for*`)\n* expands to a real `--force` argv, yet this returns `safe` and the `git-force-push` rule\n* never sees a `--force` token -- but *only when a file literally named `--force` already\n* exists in the working directory*. Creating that file is a separate command, so it is a\n* two-step, precondition-gated bypass: strictly weaker than the unconditional `$`/brace\n* bypasses that were fixed. The same class defeats a command-name deny rule too (e.g.\n* `s[u]do`), again only with a pre-existing matching file. Such a command gets no decision\n* from the hook, and auto mode's classifier judges it.\n*\n* *Why not fixed here:* this guard can only withhold a decision. Routing every token with an\n* unquoted glob metacharacter (`*`, `?`, `[`) to `none` would add no deny; it would hide the\n* deny rules from nearly every globbing command an agent runs (`ls *.md`, `cp dist/* ...`).\n* Closing the gap takes a deny rule instead -- the candidate is to deny a glob metachar\n* **only in a token that also begins with `-`** (a flag position, which is where the\n* reconstruction lives), leaving bare-argument globs like `*.md` alone.\n*\n* **Tilde expansion.** `~` is unmodeled. Bash only expands it to a home *path* at word\n* start, so it cannot reconstruct a flag or a command name -- it is a path-scope concern,\n* not the flag/command-reconstruction vector this guard defends against. Lowest priority;\n* noted for completeness.\n*\n* If either is ever closed, pin it with regression fixtures the way the nested-brace cases\n* were pinned in `tests/fixtures.ts`.\n*/\nfunction checkInjection(tokens, rawSegment) {\n if (/[\\n\\r]/.test(rawSegment)) return {\n safe: false,\n reason: \"unquoted newline in command\"\n };\n for (const token of tokens) {\n if (token === \"`\") return {\n safe: false,\n reason: \"backtick command substitution\"\n };\n if (token.includes(\"`\")) return {\n safe: false,\n reason: \"backtick command substitution\"\n };\n if (token.includes(\"$\")) return {\n safe: false,\n reason: \"unresolved dollar-expansion\"\n };\n if (hasBraceExpansion(token)) return {\n safe: false,\n reason: \"brace expansion\"\n };\n if (token.includes(\"<(\")) return {\n safe: false,\n reason: \"process substitution\"\n };\n if (token.includes(\">(\")) return {\n safe: false,\n reason: \"process substitution\"\n };\n }\n return { safe: true };\n}\n\n//#endregion\n//#region src/lib/env-file.ts\n/**\n* File extensions considered safe documentation templates.\n* Files ending with these suffixes are NOT treated as secret .env files.\n*/\nconst ENV_SAFE_SUFFIXES = [\n \".sample\",\n \".example\",\n \".template\"\n];\n/**\n* Env files owned by a bst command rather than by the developer's secrets.\n*\n* `docker-compose.check.env` is the file `bst docker:check` auto-detects and\n* passes to compose as `--env-file`. It is committed, and it holds placeholder\n* values for a throwaway check stack -- yet it is also the *only* way to feed\n* compose interpolation, so denying it left an agent unable to create or read\n* the one file the check needs. Two bst features blocking each other is worse\n* than the marginal secret risk of a file whose purpose is to hold non-secrets.\n*/\nconst ENV_TOOL_OWNED_SUFFIXES = [\".check.env\"];\n/**\n* Pattern matching .env files: `.env`, `.env.local`, `.env.production`, etc.\n* Does NOT match `.env.sample`, `.env.example`, `.env.template`.\n*/\nconst ENV_FILE_PATTERN = /(^|[/\\\\])(\\.env(\\..*)?|.*\\.env(\\..*)?|\\.env)$/;\n/** Check if a filename is a safe .env documentation template, or a bst-owned env file. */\nfunction isEnvSafeSuffix(filename) {\n return [...ENV_SAFE_SUFFIXES, ...ENV_TOOL_OWNED_SUFFIXES].some((suffix) => filename.endsWith(suffix));\n}\n/**\n* Check if a path targets a .env file (excluding safe templates).\n* Works with both bare filenames and full paths.\n*/\nfunction isEnvFile(filePath) {\n if (isEnvSafeSuffix(path.basename(filePath))) return false;\n return ENV_FILE_PATTERN.test(filePath);\n}\n\n//#endregion\n//#region src/lib/git-state.ts\n/**\n* Every git call here is a hook's, made while a tool call waits, and several of them run from\n* whatever tree the session is sitting in. `execFileSync` has no timeout of its own, so a wedged\n* repository (an index.lock held by a hung process, a filesystem that stopped answering) would\n* hang the session rather than answer. On a timeout git is killed and the call throws, which\n* every function below already reads as \"unknown\" -- a bound, not a new answer.\n*/\nconst GIT_TIMEOUT_MS = 5e3;\n/**\n* Return the current branch name, or null when:\n* - cwd is not a git repository,\n* - HEAD is detached,\n* - any git invocation fails.\n*\n* Null means \"unknown\", and an unknown branch is not a protected one: no\n* rule denies because the branch could not be read.\n*/\nfunction getCurrentBranch(cwd) {\n try {\n const out = execFileSync(\"git\", [\n \"rev-parse\",\n \"--abbrev-ref\",\n \"HEAD\"\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim();\n if (!out || out === \"HEAD\") return null;\n return out;\n } catch {\n return null;\n }\n}\n/**\n* The repository's top-level working tree for `cwd`, or null when cwd is not in one.\n*\n* This is what a file-targeting rule needs, not `cwd`: a session started in a package\n* subdirectory has a `cwd` that is not the repository root, and rules that name a repository\n* file (`health-baseline.json` above all) must resolve it against the tree that git reads, not\n* against wherever the session happened to start.\n*/\nfunction getRepoTopLevel(cwd) {\n try {\n const out = execFileSync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim();\n return out === \"\" ? null : out;\n } catch {\n return null;\n }\n}\n/**\n* Return the remote default branch (from refs/remotes/origin/HEAD), stripped\n* of the \"origin/\" prefix. Returns null when the symbolic ref isn't set\n* (common for fresh clones with no remote).\n*/\nfunction getRemoteDefaultBranch(cwd) {\n try {\n const out = execFileSync(\"git\", [\n \"symbolic-ref\",\n \"--short\",\n \"refs/remotes/origin/HEAD\"\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim();\n if (!out) return null;\n return out.startsWith(\"origin/\") ? out.slice(7) : out;\n } catch {\n return null;\n }\n}\n/** `.tooling.json#defaultBranch`, read at the repository top level; null when absent or unreadable. */\nconst DefaultBranchSchema = object({ defaultBranch: string().min(1).optional() });\n/**\n* The repo's own answer to \"which branch is the default one\", from `.tooling.json#defaultBranch`\n* at the top level. `bst health:check` honours this override through `resolveDefaultBranch`;\n* without it here, the hook's ceiling-only test would compare a non-standard-default repo against\n* a `main` that does not exist and answer \"unknown\" -- no deny -- on exactly the branch the rule\n* exists to catch.\n*/\nfunction getConfiguredDefaultBranch(topLevel) {\n let parsed;\n try {\n parsed = JSON.parse(readFileSync(join(topLevel, \".tooling.json\"), \"utf-8\"));\n } catch {\n return null;\n }\n const result = DefaultBranchSchema.safeParse(parsed);\n return result.success ? result.data.defaultBranch ?? null : null;\n}\nconst ALWAYS_PROTECTED = [\"main\", \"master\"];\n/**\n* Compute the full protected set: hard-coded {main, master}, plus the remote\n* default branch when available, plus any caller-supplied extras (e.g. from\n* .claude/hooks/bst-hook.settings.json#protectedBranches).\n*\n* Known limitation: if a repo's real default branch is neither \"main\" nor\n* \"master\" AND refs/remotes/origin/HEAD is unset (getRemoteDefaultBranch()\n* returns null -- e.g. a shallow or partial clone that never fetched the\n* symbolic ref), that default branch is absent from this set. A push whose\n* refspec destination is that branch would then pass the protected-branch\n* check in movesProtectedRef. This is not treated as a reason to block pushes\n* whenever origin/HEAD is unset, since that would break the far more common\n* case (default branch already covered by {main, master} or by\n* protectedBranches); repos with a non-standard default branch name should\n* set it explicitly via .claude/hooks/bst-hook.settings.json#protectedBranches.\n*/\nfunction getProtectedBranches(cwd, extra) {\n const set = new Set(ALWAYS_PROTECTED);\n const remote = getRemoteDefaultBranch(cwd);\n if (remote) set.add(remote);\n for (const b of extra) set.add(b);\n return set;\n}\n/**\n* The upstream of `branch` in short form (`origin/main`), or null when it has\n* none, cwd is not a repository, or git fails for any other reason.\n*/\nfunction getUpstream(cwd, branch) {\n try {\n return execFileSync(\"git\", [\n \"rev-parse\",\n \"--abbrev-ref\",\n `${branch}@{upstream}`\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim() || null;\n } catch {\n return null;\n }\n}\n/**\n* The commit id `rev` names, or null when it names no commit (a path, a\n* typo) or git fails. `rev` must not start with `-`: callers pass only\n* positional arguments.\n*/\nfunction resolveCommit(cwd, rev) {\n try {\n return execFileSync(\"git\", [\n \"rev-parse\",\n \"--verify\",\n \"--quiet\",\n `${rev}^{commit}`\n ], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim() || null;\n } catch {\n return null;\n }\n}\n/**\n* Whether the branch at `cwd` carries any file outside `allowed`, measured against the\n* merge base with the default branch -- the same comparison `bst health:check` makes when it\n* refuses a ceiling raise that travels with code.\n*\n* Returns `true` when some changed or untracked path is outside `allowed`, `false` when every\n* one of them is inside it, and `null` whenever git cannot answer (no repository, no\n* `origin/<default>` to compare against, any failed invocation). Per this file's standing\n* rule, `null` is not a deny: an unknown branch is not a branch that carries code.\n*\n* Untracked files count. A new module nobody has `git add`ed yet is part of what the branch\n* carries, and a ceiling raise smuggled in beside it is the case this rule exists for.\n*\n* Every git call runs from `git rev-parse --show-toplevel`, not from `cwd`. Git names paths\n* relative to the session's working directory by default, so a session started in a package\n* subdirectory would see the root's `health-baseline.json` named as `../health-baseline.json`\n* and read as a file outside `allowed` -- a false deny, which is the one answer this function\n* must never give. From the top level the names are the ones the ceiling-only pull request\n* itself names.\n*\n* The default branch is `.tooling.json#defaultBranch` first and `refs/remotes/origin/HEAD`\n* second, the order `bst health:check`'s own `resolveDefaultBranch` uses; a repo whose default\n* branch has another name and no override here compares against a `main` that is not there and\n* answers `null`, which is no deny.\n*/\nfunction branchCarriesOtherFiles(cwd, allowed) {\n const toplevel = getRepoTopLevel(cwd);\n if (toplevel === null) return null;\n try {\n const defaultBranch = getConfiguredDefaultBranch(toplevel) ?? getRemoteDefaultBranch(toplevel) ?? \"main\";\n const mergeBase = execFileSync(\"git\", [\n \"merge-base\",\n \"HEAD\",\n `origin/${defaultBranch}`\n ], {\n cwd: toplevel,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim();\n if (!mergeBase) return null;\n const names = (args) => execFileSync(\"git\", args, {\n cwd: toplevel,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().split(\"\\n\").filter((line) => line.length > 0);\n return [...names([\n \"diff\",\n \"--name-only\",\n mergeBase\n ]), ...names([\n \"ls-files\",\n \"--others\",\n \"--exclude-standard\"\n ])].some((name) => !allowed.includes(name));\n } catch {\n return null;\n }\n}\n/**\n* True when `cwd` is the repository's *primary* worktree (not a linked one\n* created by `git worktree add`). In the primary worktree the git dir and the\n* common git dir are the same directory; in a linked worktree the git dir is\n* `.../.git/worktrees/<name>` while the common dir is the shared `.../.git`.\n*\n* Fail-safe: any git/filesystem error returns `false` (treated as \"not primary\",\n* i.e. no advisory) so a hiccup never disturbs the decision.\n*/\nfunction isPrimaryWorktree(cwd) {\n try {\n const gitDir = execFileSync(\"git\", [\"rev-parse\", \"--absolute-git-dir\"], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim();\n const commonRaw = execFileSync(\"git\", [\"rev-parse\", \"--git-common-dir\"], {\n cwd,\n stdio: [\n \"ignore\",\n \"pipe\",\n \"ignore\"\n ],\n timeout: GIT_TIMEOUT_MS\n }).toString().trim();\n if (!gitDir || !commonRaw) return false;\n const commonDir = resolve(cwd, commonRaw);\n return realpathSync(gitDir) === realpathSync(commonDir);\n } catch {\n return false;\n }\n}\n\n//#endregion\n//#region src/lib/git-short-options.ts\n/** Per-subcommand required-value short option letters. Deliberately excludes\n* `-t` (`--track`): see the note below the value-taking sets.\n*/\nconst VALUE_TAKING_LETTERS = {\n switch: /* @__PURE__ */ new Set([\"c\", \"C\"]),\n checkout: /* @__PURE__ */ new Set([\"b\", \"B\"]),\n branch: /* @__PURE__ */ new Set([\"u\"]),\n fetch: /* @__PURE__ */ new Set([\"o\", \"j\"])\n};\n/**\n* Decompose a short-option cluster token for `sub`. Returns `undefined` for\n* anything that is not a short-option cluster: a token that does not start\n* with `-`, the bare `-`, and anything starting with `--`. Every caller keeps\n* handling those tokens exactly as it does today.\n*\n* Otherwise walks the characters after the leading `-` left to right,\n* collecting each into `letters`, and stops at the first character that is a\n* required-value option letter for `sub` -- that letter is the last one\n* collected, and the remainder of the token (possibly empty) becomes\n* `attachedValue`, never inspected as flags again.\n*\n* Worked cases:\n* - `-fc` (switch or checkout) -> letters `[\"f\", \"c\"]`, no attached value --\n* `f` never stops the scan, so `c`/`C` is just collected as another letter\n* unless the subcommand's own value-taking set includes it earlier.\n* - `-cf` (switch) -> letters `[\"c\"]`, attached value `\"f\"`.\n* - `-cfeat/x` (switch) -> letters `[\"c\"]`, attached value `\"feat/x\"`.\n* - `-c` (switch) -> letters `[\"c\"]`, attached value `\"\"` (git reads the\n* branch name from the next argv element).\n*/\nfunction splitShortOptionCluster(token, sub) {\n if (!token.startsWith(\"-\") || token.startsWith(\"--\") || token.length <= 1) return;\n const valueLetters = VALUE_TAKING_LETTERS[sub];\n const letters = [];\n const chars = token.slice(1);\n for (let i = 0; i < chars.length; i++) {\n const ch = chars[i];\n letters.push(ch);\n if (valueLetters.has(ch)) return {\n letters,\n attachedValue: chars.slice(i + 1)\n };\n }\n return { letters };\n}\n\n//#endregion\n//#region src/lib/path-scope.ts\n/**\n* Path-containment helpers that are correct across operating systems.\n*\n* The hook receives a `cwd` from its host. On Windows the same location can be\n* spelled with either drive-letter case (`D:\\proj` vs `d:\\proj`) -- the IDE is\n* inconsistent -- and NTFS treats them as identical. Plain `===` / `startsWith`\n* comparisons on the resolved path therefore misfire whenever the drive-letter\n* case differs, falling through to none (reads) or a false containment\n* result for a path-scoped write rule -- git-dir-write, node-modules-write,\n* hooks-folder-write (writes).\n*\n* The fix: pick the path flavor (win32 vs posix) from the *trusted* base path --\n* `cwd` -- and run every resolve/compare through that flavor. win32's\n* `relative()` is case-insensitive and separator-aware, so drive-letter case no\n* longer matters; posix stays case-sensitive, as that filesystem demands.\n* Deriving the flavor from the base (not the incoming target) means a crafted\n* target can't flip the comparison mode.\n*/\n/** Select the path flavor implied by a base path (a leading drive letter => win32). */\nfunction pathFlavorFor(base) {\n return /^[a-zA-Z]:[\\\\/]/.test(base) ? path.win32 : path.posix;\n}\n/** Resolve `target` against `base` using the flavor implied by `base`. */\nfunction resolveIn(base, target) {\n return pathFlavorFor(base).resolve(base, target);\n}\n/**\n* Whether `target` is `dir` itself or nested beneath it, compared with `dir`'s\n* flavor. `target` is expected to be already resolved (absolute); pass it through\n* `resolveIn` first when it may be relative or contain `..`.\n*/\nfunction isWithin(dir, target) {\n const pp = pathFlavorFor(dir);\n const rel = pp.relative(dir, target);\n if (rel === \"\") return true;\n if (rel === \"..\" || rel.startsWith(\"..\" + pp.sep)) return false;\n return !pp.isAbsolute(rel);\n}\n/** Whether two paths refer to the same location, compared with `a`'s flavor. */\nfunction samePath(a, b) {\n return pathFlavorFor(a).relative(a, b) === \"\";\n}\n/**\n* Whether `filePath` (resolved against `cwd`) is a `.git` directory\n* anywhere under `cwd` -- top-level or nested inside a subdirectory repo --\n* or anything beneath one. Covers both the directory in a normal clone and\n* the gitdir-pointer *file* git writes at the root of a worktree checkout\n* (both cases have a path segment exactly equal to `.git`).\n*\n* A nested repo's git dir (`<cwd>/sub/.git/config`) is just as sensitive as\n* the top-level one: an unguarded write there can add a malicious remote or\n* rewrite push config that a later `cd sub && git push` trusts. So this\n* checks every path segment of `filePath` resolved against `cwd`, not just\n* `<cwd>/.git` -- matching `.git` at *any* depth, as long as the resolved\n* path stays within `cwd`. A `.git` outside `cwd` is not this function's\n* concern and reaches `none` -- there is no longer a blanket outside-cwd\n* deny to catch it either.\n*\n* Matches on a resolved path *segment* exactly equal to `.git`, not a\n* substring, so it does not mistake `.gitignore`, `.gitkeep`, `.github/`,\n* `src/foo.git.ts`, or a directory merely named `mygit`/`git` for a `.git/`\n* target.\n*/\nfunction targetsGitDir(filePath, cwd) {\n const pp = pathFlavorFor(cwd);\n const resolved = resolveIn(cwd, filePath);\n if (!isWithin(cwd, resolved)) return false;\n const rel = pp.relative(cwd, resolved);\n if (rel === \"\") return false;\n return rel.split(pp.sep).includes(\".git\");\n}\n/**\n* Whether `filePath` (resolved against `cwd`) has a path segment exactly equal\n* to `node_modules` -- i.e. it names a location inside *any* installed package\n* tree, at any depth, including pnpm's virtual store\n* (`node_modules/.pnpm/<pkg>/node_modules/<pkg>/...`).\n*\n* **Deliberately not scoped to `cwd`**, unlike `targetsGitDir`. pnpm populates\n* `node_modules` with *hardlinks into the global content-addressable store*, so\n* a single in-place overwrite of an installed file (a plain `cp`, a `>`\n* redirect -- anything that opens the destination `O_WRONLY|O_TRUNC` rather than\n* unlinking it first) writes *through* the link and mutates the store's own\n* content-addressed file. That corrupts the package for the global store, every\n* other project on the machine that shares the inode, and every future install\n* or `pnpm dlx` that hardlinks it -- with no error at the scene, because the\n* directory that did the writing ends up perfectly consistent.\n*\n* This is exactly how a local `@bensandee/tooling` build landed on top of the\n* published 0.64.2 in the store on 2026-07-13: `cp -r <local>/dist/*` over a\n* scratch consumer's `node_modules/@bensandee/tooling/dist/`. The destination\n* was outside `cwd` -- so a cwd-scoped check would not have seen it -- and the\n* store stayed broken until a consumer's `pnpm dlx` failed days later with\n* `ERR_MODULE_NOT_FOUND` for a chunk that no published tarball has ever\n* contained.\n*\n* The lesson encoded here: **cwd-containment is not a safety boundary for\n* `node_modules`.** A path inside it is not \"in the project\" in any meaningful\n* sense -- it is a shared, machine-global resource wearing a local path. Hence\n* every segment of the *resolved* path is checked, wherever it lands.\n*\n* Matches on a path *segment*, not a substring, so `src/node_modules_helper.ts`\n* and `docs/node_modules.md` are not mistaken for the real thing.\n*/\nfunction targetsNodeModules(filePath, cwd) {\n return resolveIn(cwd, filePath).split(/[/\\\\]/).includes(\"node_modules\");\n}\n\n//#endregion\n//#region src/rules.ts\n/** Git global flags that consume the next token as an argument. */\nconst GIT_FLAGS_WITH_ARG = /* @__PURE__ */ new Set([\n \"-C\",\n \"-c\",\n \"--git-dir\",\n \"--work-tree\"\n]);\n/** Extract the git subcommand (first non-flag token after \"git\"), skipping flag arguments. */\nfunction getGitSubcommand(tokens) {\n const args = tokens.slice(1);\n for (let i = 0; i < args.length; i++) {\n const t = args[i];\n if (GIT_FLAGS_WITH_ARG.has(t)) {\n i++;\n continue;\n }\n if (t.startsWith(\"-\")) continue;\n return t;\n }\n}\nconst WORKTREE_ADVICE = \"You're creating a feature branch in the primary worktree. An isolated worktree keeps this work independent of other windows, and with no commits on this branch yet, converting is cheap -- `git worktree add ../<dir> -b <branch>` (a sibling outside the repo, with its own dependency install). Claude Code keeps this session inside its own working tree, so the work continues in a new session opened in the sibling worktree. If you cannot open a session there, stay in the primary checkout on this branch and check `git branch --show-current` before each commit.\";\n/**\n* True when the command creates a branch: `git checkout -b/-B` or\n* `git switch -c/-C`, including those letters carried in a short-option\n* cluster with other letters or an attached value (`-bfix-bug`, `-qc\n* feat/x`). A plain `git switch <branch>` is not creation. Long spellings\n* (`--create`, `--force-create`) are deliberately not recognized as creation\n* here -- only the short grammar is in scope.\n*/\nfunction isGitBranchCreation(tokens) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"checkout\" && sub !== \"switch\") return false;\n const after = tokens.slice(tokens.indexOf(sub) + 1);\n const creationLetters = sub === \"checkout\" ? [\"b\", \"B\"] : [\"c\", \"C\"];\n return after.some((t) => {\n const cluster = splitShortOptionCluster(t, sub);\n return cluster !== void 0 && cluster.letters.some((l) => creationLetters.includes(l));\n });\n}\n/**\n* Non-blocking advisory for a `git` command that reaches the end of rule\n* matching with no decision either way. There is no allow tier any more to\n* carry it, so `evaluateSegment` calls this directly and attaches the result\n* to the none result it returns for a `git` command, rather than routing it\n* through a `Rule`'s own `when`/advise pair the way the old `git-safe-write`\n* allow rule did.\n*/\nfunction worktreeAdvice(tokens, ctx) {\n return isGitBranchCreation(tokens) && isPrimaryWorktree(ctx.cwd) ? WORKTREE_ADVICE : void 0;\n}\n/** Force-create letter: `C` on switch, `B` on checkout and `worktree add`. */\nconst GIT_FORCE_CREATE_LETTER = {\n switch: \"C\",\n checkout: \"B\"\n};\n/**\n* The long force-create spelling (switch only). Every unambiguous prefix down\n* to `--force-` (the shortest that is not the distinct `--force` option)\n* matches, because git's parse-options accepts one and a prefix missed here\n* would let a force-create onto a protected name through.\n*/\nconst GIT_FORCE_CREATE_LONG = {\n flag: \"--force-create\",\n shortest: \"--force-\"\n};\n/**\n* Every branch name a force-create spelling in `after` (the tokens following\n* the subcommand -- and past `add`, for `worktree add`) targets. Usually zero\n* or one, but git's last-occurrence-wins on a repeated creation flag is not\n* modeled: every occurrence contributes its target, so a repeated flag is\n* handled conservatively rather than resolving which one wins.\n*\n* Grammar, reusing `splitShortOptionCluster`: a cluster whose letters include\n* the force letter contributes its attached value when non-empty, else the\n* next argv token; the long spelling contributes the next argv token, or the\n* name attached to it with `=`. A creation flag with nothing after it (a\n* valueless trailing `-C`, or `--force-create` as the last token) resolves no\n* target for that occurrence -- git errors on the command anyway.\n*/\nfunction gitForceCreateTargets(after, letterSub) {\n const letter = GIT_FORCE_CREATE_LETTER[letterSub];\n const longFlag = GIT_FORCE_CREATE_LONG;\n const targets = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === void 0) continue;\n if (letterSub === \"switch\") {\n const [flag, ...rest] = t.split(\"=\");\n if (flag !== void 0 && flag.length >= longFlag.shortest.length && longFlag.flag.startsWith(flag)) {\n const target = rest.length > 0 ? rest.join(\"=\") : after[i + 1];\n if (target !== void 0) targets.push(target);\n continue;\n }\n }\n const cluster = splitShortOptionCluster(t, letterSub);\n if (cluster === void 0 || !cluster.letters.includes(letter)) continue;\n if (cluster.attachedValue !== void 0 && cluster.attachedValue !== \"\") {\n targets.push(cluster.attachedValue);\n continue;\n }\n const next = after[i + 1];\n if (next !== void 0) targets.push(next);\n }\n return targets;\n}\n/**\n* Every branch name a force-create spelling targets in a full `git ...`\n* command: `switch -C`/`--force-create`, `checkout -B`, or `worktree add -B`.\n* Any other subcommand -- including `worktree list`/`remove`/`move` -- always\n* resolves no target. `worktree add` reuses checkout's `-B` grammar.\n*/\nfunction gitCommandForceCreateTargets(tokens) {\n const sub = getGitSubcommand(tokens);\n if (sub === \"switch\" || sub === \"checkout\") return gitForceCreateTargets(tokens.slice(tokens.indexOf(sub) + 1), sub);\n if (sub === \"worktree\") {\n const worktreeIdx = tokens.indexOf(\"worktree\");\n if (tokens[worktreeIdx + 1] !== \"add\") return [];\n return gitForceCreateTargets(tokens.slice(worktreeIdx + 2), \"checkout\");\n }\n return [];\n}\n/**\n* True when a `switch`/`checkout`/`worktree add` command force-creates a\n* branch under a name in the protected set. Force-create resets an existing\n* branch to the start point (HEAD by default) rather than merely creating it,\n* so this is a claim about the TOKENS, not the current branch: it never calls\n* getCurrentBranch and is valid even outside a repository, where main/master\n* are protected unconditionally.\n*/\nfunction isGitProtectedBranchForceCreate(tokens, ctx) {\n const targets = gitCommandForceCreateTargets(tokens);\n if (targets.length === 0) return false;\n const protectedSet = getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []);\n return targets.some((t) => protectedSet.has(t));\n}\n/**\n* True when `git branch` destroys or overwrites a protected ref: a delete\n* (`-d`/`-D`/`--delete`) naming one, a non-force rename (`-m`/`-M`/`--move`)\n* moving one away by name, or any force spelling (`-f`/`--force`, or the\n* force letters `-M`/`-C`) writing onto one. A claim about the TOKENS, never\n* calling getCurrentBranch and valid outside a repository, like the\n* force-create check above. It carries no pre-subcommand flag guard, so\n* `git -c core.hooksPath=/tmp/evil branch -D main` still denies.\n*\n* The three checks are independent, not mutually exclusive branches of one\n* mode -- `git branch -M junk main` is a force rename whose *source* (`junk`)\n* is not protected, so the rename check alone would miss it; the force check\n* catches it via the *destination* (`main`).\n*\n* The accepted residual: `git branch -m junk` while sitting on a protected\n* branch renames it away with no victim name in the tokens at all, so this\n* returns false and the command gets no decision.\n*/\nfunction isGitProtectedBranchRefWrite(tokens, ctx) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"branch\") return false;\n const { flags, positionals } = splitGitBranchArgs(tokens.slice(tokens.indexOf(sub) + 1));\n if (positionals.length === 0) return false;\n let deleteMode = false;\n let renameMode = false;\n let copyMode = false;\n let forceMode = false;\n for (const flag of flags) {\n if (flag.letters !== void 0) {\n if (flag.letters.includes(\"d\") || flag.letters.includes(\"D\")) deleteMode = true;\n if (flag.letters.includes(\"m\") || flag.letters.includes(\"M\")) renameMode = true;\n if (flag.letters.includes(\"c\") || flag.letters.includes(\"C\")) copyMode = true;\n if (flag.letters.includes(\"f\") || flag.letters.includes(\"M\") || flag.letters.includes(\"C\")) forceMode = true;\n continue;\n }\n if (flag.name === \"delete\") deleteMode = true;\n if (flag.name === \"move\") renameMode = true;\n if (flag.name === \"copy\") copyMode = true;\n if (flag.name === \"force\") forceMode = true;\n }\n const protectedSet = getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []);\n if (deleteMode && positionals.some((p) => protectedSet.has(p))) return true;\n if (renameMode && positionals.length >= 2 && protectedSet.has(positionals[0])) return true;\n if (forceMode) {\n const destination = renameMode || copyMode ? positionals[positionals.length - 1] : positionals[0];\n if (destination !== void 0 && protectedSet.has(destination)) return true;\n }\n return false;\n}\n/**\n* The ref a refspec writes to. `src:dst` targets dst; a bare `ref` targets the\n* same name on the remote. Strips the leading \"+\" (force) and any\n* `refs/heads/` prefix.\n*/\nfunction refspecDestination(refspec) {\n const spec = refspec.startsWith(\"+\") ? refspec.slice(1) : refspec;\n const colon = spec.lastIndexOf(\":\");\n return (colon === -1 ? spec : spec.slice(colon + 1)).replace(/^refs\\/heads\\//, \"\");\n}\n/**\n* Long `git branch` flags whose value is the next token when spelled without\n* `=`. `--abbrev`, `--color` and `--column` are deliberately absent: measured,\n* they consume nothing (`git branch --color zz` creates `zz`), so treating\n* them as value-consuming would swallow a branch name. `-u`/`--set-upstream-to`\n* also consume the next token, but `-u` is\n* handled by `splitShortOptionCluster`'s own branch table, not by name here.\n*/\nconst GIT_BRANCH_VALUE_FLAG_NAMES = /* @__PURE__ */ new Set([\n \"contains\",\n \"no-contains\",\n \"merged\",\n \"no-merged\",\n \"points-at\",\n \"sort\",\n \"format\",\n \"set-upstream-to\"\n]);\n/**\n* Split the tokens after `git branch` into its flags and positionals, for\n* `isGitProtectedBranchRefWrite`. A token not starting with `-` is a\n* positional. `--` is recorded as a flag with no name. A `--flag`\n* token is recorded by its name (the part before `=`) and consumes the\n* following token when it has no `=` and its name is in\n* `GIT_BRANCH_VALUE_FLAG_NAMES`. A short token is decomposed with\n* `splitShortOptionCluster(t, \"branch\")`, recording its letters and\n* consuming the following token when its attached value is the empty string\n* (a trailing `-u`).\n*/\nfunction splitGitBranchArgs(after) {\n const flags = [];\n const positionals = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === void 0) continue;\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n if (t.startsWith(\"--\") && t !== \"--\") {\n const eq = t.indexOf(\"=\");\n const name = eq > 0 ? t.slice(2, eq) : t.slice(2);\n flags.push({\n raw: t,\n name\n });\n if (eq < 0 && GIT_BRANCH_VALUE_FLAG_NAMES.has(name)) i++;\n continue;\n }\n const cluster = splitShortOptionCluster(t, \"branch\");\n if (cluster === void 0) {\n flags.push({ raw: t });\n continue;\n }\n flags.push({\n raw: t,\n letters: cluster.letters\n });\n if (cluster.attachedValue === \"\") i++;\n }\n return {\n flags,\n positionals\n };\n}\n/**\n* Long `git fetch` flags whose value is the next token when spelled without\n* `=`. `--recurse-submodules` is deliberately absent: it takes its optional\n* value only with `=`, so `git fetch --recurse-submodules origin` reads\n* `origin` as the remote, not as the flag's value. The three names that are on\n* no allowlist (`--upload-pack`, `--submodule-prefix`,\n* `--recurse-submodules-default`) are listed here so a swallowed value cannot\n* be misread as a refspec.\n*/\nconst GIT_FETCH_VALUE_FLAG_NAMES = /* @__PURE__ */ new Set([\n \"depth\",\n \"deepen\",\n \"shallow-since\",\n \"shallow-exclude\",\n \"negotiation-tip\",\n \"filter\",\n \"jobs\",\n \"server-option\",\n \"upload-pack\",\n \"refmap\",\n \"submodule-prefix\",\n \"recurse-submodules-default\"\n]);\n/**\n* Split the tokens after `git fetch` into flags, positionals and `--refmap`\n* values, for `isGitProtectedBranchFetchWrite`, in the shape of\n* `splitGitBranchArgs` (whose interfaces are deliberately not reused: the two\n* splitters have different value-flag tables and must stay independent). A\n* token not starting with `-` is a positional. `--` is recorded as a flag with\n* no name and no letters; later tokens are not treated specially. A `--flag` token is recorded\n* by name and consumes the following token when it has no `=` and its name is\n* in `GIT_FETCH_VALUE_FLAG_NAMES`. A short token is decomposed with\n* `splitShortOptionCluster(t, \"fetch\")`, recording its letters and consuming\n* the following token when its attached value is the empty string (a trailing\n* `-j` or `-o`).\n*/\nfunction splitGitFetchArgs(after) {\n const flags = [];\n const positionals = [];\n const refmaps = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === void 0) continue;\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n if (t === \"--\") {\n flags.push({ raw: t });\n continue;\n }\n if (t.startsWith(\"--\")) {\n const eq = t.indexOf(\"=\");\n const name = eq > 0 ? t.slice(2, eq) : t.slice(2);\n flags.push({\n raw: t,\n name\n });\n if (name === \"refmap\") {\n if (eq > 0) refmaps.push(t.slice(eq + 1));\n else if (after[i + 1] !== void 0) refmaps.push(after[i + 1]);\n }\n if (eq < 0 && GIT_FETCH_VALUE_FLAG_NAMES.has(name)) i++;\n continue;\n }\n const cluster = splitShortOptionCluster(t, \"fetch\");\n if (cluster === void 0) {\n flags.push({ raw: t });\n continue;\n }\n flags.push({\n raw: t,\n letters: cluster.letters\n });\n if (cluster.attachedValue === \"\") i++;\n }\n return {\n flags,\n positionals,\n refmaps\n };\n}\n/**\n* Classify one fetch refspec's destination. Unlike a push's use of\n* `refspecDestination`, a token with **no colon is not a destination at all**:\n* a source-only fetch refspec writes `FETCH_HEAD` plus whatever the remote's\n* refmap says, never a named local branch, so this function must not consult\n* the shared helper first -- for push, a bare `main` means \"the same name on\n* the remote\" and the helper hands it back as a destination.\n*\n* With a colon the destination goes through `refspecDestination`, which already\n* strips `refs/heads/` and leaves every other `refs/` prefix intact. An empty\n* destination (`main:`) writes nothing. `refs/remotes/` is a remote-tracking\n* cache; any other `refs/` namespace (tags included) is `other`; anything else\n* is a branch name, which git resolves to `refs/heads/<name>` when no such ref\n* exists. Note that `:main` reaches `branch` with name `main` -- the fetch\n* reading, where an empty source means HEAD, not push's delete reading.\n*/\nfunction fetchRefspecTarget(spec) {\n const body = spec.startsWith(\"+\") ? spec.slice(1) : spec;\n if (body.startsWith(\"^\")) return { kind: \"none\" };\n if (!body.includes(\":\")) return { kind: \"none\" };\n const destination = refspecDestination(body);\n if (destination === \"\") return { kind: \"none\" };\n if (destination.startsWith(\"refs/remotes/\")) return { kind: \"tracking\" };\n if (destination.startsWith(\"refs/\")) return { kind: \"other\" };\n return {\n kind: \"branch\",\n name: destination\n };\n}\n/**\n* Escape a glob for regex use, then turn each escaped `*` into \"any run of\n* characters\". Escaping first is what keeps a name containing regex syntax\n* (`feat/x+`, say) from compiling into a pattern that matches something the\n* name does not.\n*/\nfunction globToAnchoredRegex(glob) {\n const escaped = glob.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`^${escaped.replace(/\\\\\\*/g, \".*\")}$`);\n}\n/**\n* True when a refspec destination names a protected branch, or is a glob that\n* covers one (`refs/heads/*:refs/heads/*` covers `main`,\n* `refs/heads/*:refs/heads/copy/*` does not).\n*/\nfunction isProtectedFetchBranch(name, protectedSet) {\n if (protectedSet.has(name)) return true;\n if (!name.includes(\"*\")) return false;\n const pattern = globToAnchoredRegex(name);\n for (const member of protectedSet) if (pattern.test(member)) return true;\n return false;\n}\n/**\n* True when a `git fetch` writes a protected local branch: some refspec lands\n* on a protected name (or a glob covering one), forced or not -- an unforced\n* write still fast-forwards the branch. A claim about the TOKENS --\n* it never calls `getCurrentBranch` and never asks whether the ref exists, so it\n* is valid outside a repository, where `main`/`master` are protected\n* unconditionally. Like `isGitProtectedBranchRefWrite`, it carries no\n* pre-subcommand flag guard, so `git -c core.hooksPath=/tmp/evil fetch .\n* +HEAD:main` still denies.\n*\n* Two deliberate widenings, both in the deny direction. **Every** positional\n* is scanned, the remote slot included: if the value-consuming flag list were\n* ever over-inclusive, a swallowed remote would shift the sole refspec into the\n* first slot, and the deny must not be blind there -- the cost is a deny on an\n* scp-style URL whose path is literally a protected name. And every\n* `--refmap` value is classified, because measured,\n* `--refmap=+refs/heads/*:refs/heads/*` beside a source-only positional\n* force-writes `main`.\n*\n* The accepted residual: `--stdin` carries its refspecs on a stream, so a\n* `HEAD:main` arriving that way cannot be seen and gets no decision.\n*/\nfunction isGitProtectedBranchFetchWrite(tokens, ctx) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"fetch\") return false;\n const { positionals, refmaps } = splitGitFetchArgs(tokens.slice(tokens.indexOf(sub) + 1));\n const protectedSet = getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []);\n return [...positionals, ...refmaps].some((spec) => {\n const target = fetchRefspecTarget(spec);\n if (target.kind !== \"branch\" || target.name === void 0) return false;\n return isProtectedFetchBranch(target.name, protectedSet);\n });\n}\n/**\n* True when `git update-ref` writes or deletes (`-d`) a protected branch: its\n* ref positional, with any `refs/heads/` prefix stripped, is protected. `-m`\n* takes the next token as its reason. `--stdin` carries its commands on a\n* stream, so a protected write arriving that way cannot be seen and gets no\n* decision.\n*/\nfunction isGitProtectedUpdateRef(tokens, ctx) {\n const after = tokens.slice(tokens.indexOf(\"update-ref\") + 1);\n let ref;\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (t === \"-m\") {\n i++;\n continue;\n }\n if (t.startsWith(\"-\")) continue;\n ref = t;\n break;\n }\n if (ref === void 0) return false;\n return getProtectedBranches(ctx.cwd, ctx.repoProtectedBranches ?? []).has(ref.replace(/^refs\\/heads\\//, \"\"));\n}\n/**\n* The directory git runs in: `ctx.cwd` (already the effective directory after\n* `cd <dir> &&` propagation) with every global `-C <dir>` applied in order, the\n* way git composes them. `--git-dir`/`--work-tree` are not modelled: a command\n* carrying one reads its branch from `ctx.cwd`, an accepted residual.\n*/\nfunction gitEffectiveCwd(tokens, cwd) {\n let dir = cwd;\n const args = tokens.slice(1);\n for (let i = 0; i < args.length; i++) {\n const t = args[i];\n if (t === \"-C\") {\n const next = args[i + 1];\n if (next !== void 0) dir = path.resolve(dir, next);\n i++;\n continue;\n }\n if (GIT_FLAGS_WITH_ARG.has(t)) {\n i++;\n continue;\n }\n if (!t.startsWith(\"-\")) break;\n }\n return dir;\n}\n/** Flags that do not change which branch a switch or checkout lands on. */\nconst GIT_SWITCH_NEUTRAL_FLAGS = /* @__PURE__ */ new Set([\n \"-q\",\n \"--quiet\",\n \"-f\",\n \"--force\",\n \"--discard-changes\",\n \"-m\",\n \"--merge\",\n \"--no-track\",\n \"--progress\",\n \"--no-progress\",\n \"--guess\",\n \"--no-guess\",\n \"--ignore-other-worktrees\",\n \"--recurse-submodules\",\n \"--no-recurse-submodules\"\n]);\n/** Long flags whose value names the branch a switch or checkout creates and lands on. */\nconst GIT_SWITCH_CREATE_FLAGS = /* @__PURE__ */ new Set([\n \"--create\",\n \"--force-create\",\n \"--orphan\"\n]);\n/**\n* `git checkout` flags that only work on paths, so the branch stays where it\n* is. Matched on the part before any `=`.\n*/\nconst GIT_CHECKOUT_PATH_FLAGS = /* @__PURE__ */ new Set([\n \"--ours\",\n \"--theirs\",\n \"-p\",\n \"--patch\",\n \"--conflict\",\n \"--pathspec-from-file\",\n \"--pathspec-file-nul\",\n \"--overlay\",\n \"--no-overlay\"\n]);\n/** Short letters that are neutral on both subcommands: `-q`, `-f`, `-m`. */\nconst GIT_SWITCH_NEUTRAL_LETTERS = /* @__PURE__ */ new Set([\n \"q\",\n \"f\",\n \"m\"\n]);\n/**\n* The branch a `git switch` / `git checkout` lands on: the created name for a\n* create form, else its one positional. Undefined when it switches nothing (a\n* checkout of paths, a bare checkout, `HEAD`); null when it lands somewhere\n* the hook cannot name (`-`, `--detach`, `--track`, any flag not listed\n* here). A checkout's one positional can be a path rather than a branch, so\n* it counts only when `isCheckoutBranch` says so.\n*/\nfunction switchTarget(after, sub, isCheckoutBranch) {\n if (sub === \"checkout\" && after.includes(\"--\")) return void 0;\n const createLetters = sub === \"checkout\" ? [\"b\", \"B\"] : [\"c\", \"C\"];\n const known = (l) => GIT_SWITCH_NEUTRAL_LETTERS.has(l) || createLetters.includes(l);\n const positionals = [];\n let created;\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n if (GIT_SWITCH_NEUTRAL_FLAGS.has(t)) continue;\n const eq = t.indexOf(\"=\");\n if (sub === \"checkout\" && GIT_CHECKOUT_PATH_FLAGS.has(eq === -1 ? t : t.slice(0, eq))) return;\n if (GIT_SWITCH_CREATE_FLAGS.has(eq === -1 ? t : t.slice(0, eq))) {\n created = eq === -1 ? after[++i] : t.slice(eq + 1);\n continue;\n }\n const cluster = splitShortOptionCluster(t, sub);\n if (cluster === void 0 || !cluster.letters.every(known)) return null;\n if (cluster.letters.some((l) => createLetters.includes(l))) created = cluster.attachedValue ? cluster.attachedValue : after[++i];\n }\n if (created !== void 0) return created;\n if (positionals.length !== 1) return void 0;\n const target = positionals[0];\n if (target === \"HEAD\" || target === \"@\") return void 0;\n if (sub === \"checkout\" && !isCheckoutBranch(target)) return void 0;\n return target;\n}\n/**\n* The branch a `git switch` / `git checkout` segment leaves checked out, for\n* `evaluateCommand` to carry to the later segments of an `&&` chain. Undefined\n* when the segment is not a switch or switches nothing. `resolved` is false\n* when the segment carries an expansion the tokenizer cannot resolve; the\n* target is then unknown, never guessed.\n*\n* A checkout target that is neither protected nor a commit the repo can\n* resolve is read as a checkout of paths, so the branch stays as it was.\n*/\nfunction branchSwitch(tokens, ctx, resolved) {\n const sub = getGitSubcommand(tokens);\n if (sub !== \"switch\" && sub !== \"checkout\") return void 0;\n const dir = gitEffectiveCwd(tokens, ctx.cwd);\n const isCheckoutBranch = (name) => !resolved || getProtectedBranches(dir, ctx.repoProtectedBranches ?? []).has(name) || resolveCommit(dir, name) !== null;\n const target = switchTarget(tokens.slice(tokens.indexOf(sub) + 1), sub, isCheckoutBranch);\n if (!resolved) return target === void 0 ? void 0 : {\n dir,\n branch: null\n };\n return target === void 0 ? void 0 : {\n dir,\n branch: target\n };\n}\n/** Subcommands that move the checked-out branch's ref when they succeed. */\nconst GIT_CHECKED_OUT_WRITES = /* @__PURE__ */ new Set([\n \"commit\",\n \"merge\",\n \"rebase\",\n \"cherry-pick\",\n \"revert\",\n \"am\",\n \"pull\"\n]);\n/**\n* Split the tokens after `sub` into flag-shaped and positional tokens. A flag\n* in `valueFlags` also consumes the next token as its value.\n*/\nfunction subcommandArgs(tokens, sub, valueFlags = /* @__PURE__ */ new Set()) {\n const after = tokens.slice(tokens.indexOf(sub) + 1);\n const flags = [];\n const positionals = [];\n for (let i = 0; i < after.length; i++) {\n const t = after[i];\n if (!t.startsWith(\"-\")) {\n positionals.push(t);\n continue;\n }\n flags.push(t);\n if (valueFlags.has(t)) i++;\n }\n return {\n flags,\n positionals\n };\n}\n/**\n* `git push` flags whose value is the next token when spelled without `=`.\n* Left unskipped, the value reads as the remote and shifts the real remote\n* into the refspec slot: `git push -o ci.skip origin` would look like a push\n* of the refspec `origin` instead of a push with no refspec.\n*/\nconst GIT_PUSH_VALUE_FLAGS = /* @__PURE__ */ new Set([\n \"-o\",\n \"--push-option\",\n \"--repo\",\n \"--receive-pack\",\n \"--exec\"\n]);\n/**\n* True for `merge --ff-only <ref>` where `<ref>` is the branch's own upstream,\n* spelled out (`origin/main`) or as `@{u}`/`@{upstream}`. Fast-forwarding\n* `main` from its upstream is how a human keeps it current.\n*/\nfunction isMergeFastForwardFromUpstream(tokens, upstream) {\n const { flags, positionals } = subcommandArgs(tokens, \"merge\");\n if (!flags.includes(\"--ff-only\") || positionals.length !== 1) return false;\n const ref = positionals[0];\n return ref === \"@{u}\" || ref === \"@{upstream}\" || upstream !== null && ref === upstream;\n}\n/** `--rebase=<value>` spellings git reads as false, so the pull does not rebase. */\nconst GIT_PULL_NO_REBASE = /* @__PURE__ */ new Set([\n \"--rebase=false\",\n \"--rebase=no\",\n \"--rebase=off\",\n \"--rebase=0\"\n]);\n/**\n* True for a `pull` that can only fast-forward or merge the branch's own\n* upstream: no rebase or `--no-ff`, and either no positional or exactly\n* `<remote> <branch>` naming the upstream. `--rebase` is matched as a prefix\n* (`--rebase=merges`), and `-r` inside any short cluster, so neither spelling\n* slips through; `--rebase=false` and git's other false spellings turn\n* rebasing off rather than on.\n*/\nfunction isPullFromUpstream(tokens, upstream) {\n const { flags, positionals } = subcommandArgs(tokens, \"pull\");\n if (flags.some((t) => t.startsWith(\"--rebase\") && !GIT_PULL_NO_REBASE.has(t) || t === \"--no-ff\" || !t.startsWith(\"--\") && t.slice(1).includes(\"r\"))) return false;\n if (positionals.length === 0) return true;\n if (positionals.length !== 2 || upstream === null) return false;\n const [remote, branch] = positionals;\n return `${remote}/${branch.replace(/^refs\\/heads\\//, \"\")}` === upstream;\n}\n/**\n* True for a `reset` that moves the ref: no `--`, exactly one positional, and\n* that positional resolves to a commit other than `head`'s (`HEAD`, or the\n* branch a switch earlier in the chain checked out). `git reset`,\n* `git reset <path>` and `git reset --hard` unstage or discard work but leave\n* the ref where it is, so they are false.\n*/\nfunction isRefMovingReset(tokens, cwd, head) {\n if (tokens.slice(tokens.indexOf(\"reset\") + 1).includes(\"--\")) return false;\n const { positionals } = subcommandArgs(tokens, \"reset\");\n if (positionals.length !== 1) return false;\n if (head !== \"HEAD\" && /^(HEAD|@)/.test(positionals[0])) return false;\n const target = resolveCommit(cwd, positionals[0]);\n if (target === null) return false;\n return target !== resolveCommit(cwd, head);\n}\n/**\n* The branch names a `git push` writes on the remote. A refspec's destination\n* comes from `refspecDestination`; `HEAD` and `@` stand for the checked-out\n* branch. The first positional is the remote. Returns null when the command\n* names no refspec, so the destination comes from push config.\n*/\nfunction pushDestinations(tokens, current) {\n const { positionals } = subcommandArgs(tokens, \"push\", GIT_PUSH_VALUE_FLAGS);\n const refspecs = positionals.slice(1);\n if (refspecs.length === 0) return null;\n const names = [];\n for (const spec of refspecs) {\n const dst = refspecDestination(spec);\n if (dst === \"HEAD\" || dst === \"@\") {\n if (current !== null) names.push(current);\n continue;\n }\n names.push(dst);\n }\n return names;\n}\n/** `git push` flags that push every local branch rather than named refspecs. */\nconst GIT_PUSH_EVERY_BRANCH = /* @__PURE__ */ new Set([\n \"--all\",\n \"--branches\",\n \"--mirror\"\n]);\n/**\n* True for a checked-out write that cannot move the ref forward: `--abort`\n* and `--quit` leave an operation in progress (a plain `git pull` can leave a\n* conflicted merge that has to be abandoned), and `commit --dry-run` only\n* reports what would be committed. `--continue` and `--skip` are not here,\n* because they do commit.\n*/\nfunction leavesRefInPlace(tokens, sub) {\n const { flags } = subcommandArgs(tokens, sub);\n if (flags.includes(\"--abort\") || flags.includes(\"--quit\")) return true;\n return sub === \"commit\" && flags.includes(\"--dry-run\");\n}\n/**\n* True when a git command would move a protected branch's ref:\n*\n* - With a protected branch checked out (in the effective directory; the\n* branch an earlier `git switch`/`git checkout` in the `&&` chain lands on\n* there, when one did, else the branch on disk): a\n* subcommand in `GIT_CHECKED_OUT_WRITES`, except a fast-forward from the\n* branch's own upstream (`isMergeFastForwardFromUpstream`,\n* `isPullFromUpstream`) and a command that leaves the ref in place\n* (`leavesRefInPlace`); or a `reset` that moves the ref (`isRefMovingReset`).\n* - From any branch: a push of every branch (`--all`, `--branches`,\n* `--mirror`), a push whose refspec destination is protected, or a push\n* with no refspec while a protected branch is checked out, unless it pushes\n* only tags (`--tags`).\n* - From any branch: `switch -C`/`checkout -B`/`worktree add -B`, a\n* `git branch` delete/rename/force, a `fetch` refspec, or `update-ref`, onto\n* a protected name.\n*\n* An unknown branch -- a directory that does not exist yet, a detached HEAD,\n* git failing -- is not a protected one, so the checked-out cases are false\n* there. The subcommand is checked before any git process is spawned.\n*/\nfunction movesProtectedRef(tokens, ctx) {\n const sub = getGitSubcommand(tokens);\n if (sub === void 0) return false;\n if (sub === \"switch\" || sub === \"checkout\" || sub === \"worktree\") return isGitProtectedBranchForceCreate(tokens, ctx);\n if (sub === \"branch\") return isGitProtectedBranchRefWrite(tokens, ctx);\n if (sub === \"fetch\") return isGitProtectedBranchFetchWrite(tokens, ctx);\n if (sub === \"update-ref\") return isGitProtectedUpdateRef(tokens, ctx);\n if (sub !== \"push\" && sub !== \"reset\" && !GIT_CHECKED_OUT_WRITES.has(sub)) return false;\n const cwd = gitEffectiveCwd(tokens, ctx.cwd);\n const pending = ctx.pendingBranches?.[cwd];\n const current = pending ? pending.branch : getCurrentBranch(cwd);\n const protectedSet = getProtectedBranches(cwd, ctx.repoProtectedBranches ?? []);\n const onProtected = current !== null && protectedSet.has(current);\n if (sub === \"push\") {\n const { flags } = subcommandArgs(tokens, \"push\", GIT_PUSH_VALUE_FLAGS);\n if (flags.some((f) => GIT_PUSH_EVERY_BRANCH.has(f))) return true;\n const destinations = pushDestinations(tokens, current);\n if (destinations === null) {\n if (flags.includes(\"--tags\")) return false;\n return onProtected;\n }\n return destinations.some((d) => protectedSet.has(d));\n }\n if (!onProtected) return false;\n if (leavesRefInPlace(tokens, sub)) return false;\n if (sub === \"reset\") return isRefMovingReset(tokens, cwd, pending ? current : \"HEAD\");\n if (sub === \"merge\" || sub === \"pull\") {\n const upstream = getUpstream(cwd, current);\n if (sub === \"merge\" && isMergeFastForwardFromUpstream(tokens, upstream)) return false;\n if (sub === \"pull\" && isPullFromUpstream(tokens, upstream)) return false;\n }\n return true;\n}\n/** pnpm built-in subcommands (not user-defined scripts) */\nconst PNPM_BUILTINS = /* @__PURE__ */ new Set([\n \"install\",\n \"i\",\n \"add\",\n \"remove\",\n \"rm\",\n \"uninstall\",\n \"un\",\n \"update\",\n \"up\",\n \"upgrade\",\n \"list\",\n \"ls\",\n \"ll\",\n \"la\",\n \"why\",\n \"view\",\n \"info\",\n \"show\",\n \"outdated\",\n \"audit\",\n \"licenses\",\n \"init\",\n \"create\",\n \"link\",\n \"unlink\",\n \"pack\",\n \"publish\",\n \"config\",\n \"set\",\n \"get\",\n \"store\",\n \"cache\",\n \"rebuild\",\n \"rb\",\n \"prune\",\n \"dedupe\",\n \"dlx\",\n \"exec\",\n \"run\",\n \"patch\",\n \"patch-commit\",\n \"recursive\",\n \"-r\"\n]);\nconst NPM_READ_OPS = /* @__PURE__ */ new Set([\n \"view\",\n \"info\",\n \"show\"\n]);\n/** True for a read-only npm query (`view`/`info`/`show`) -- everything else denies with a redirect. */\nfunction isNpmReadOp(tokens) {\n const sub = tokens[1];\n return sub !== void 0 && NPM_READ_OPS.has(sub);\n}\n/** Map npm subcommand to a pnpm redirect message */\nfunction getNpmRedirectReason(tokens) {\n const sub = tokens[1];\n const BASE = \"This project uses pnpm.\";\n if (!sub) return `${BASE} Use \\`pnpm\\` instead of npm.`;\n if (sub === \"install\" || sub === \"i\" || sub === \"ci\") {\n if (tokens.slice(2).some((t) => !t.startsWith(\"-\"))) return `${BASE} Use \\`pnpm add <pkg>\\` instead of \\`npm install <pkg>\\`.`;\n return `${BASE} Use \\`pnpm install\\` instead of npm.`;\n }\n if (sub === \"add\") return `${BASE} Use \\`pnpm add <pkg>\\` instead of npm.`;\n if (sub === \"uninstall\" || sub === \"remove\" || sub === \"rm\" || sub === \"un\" || sub === \"r\") return `${BASE} Use \\`pnpm remove <pkg>\\` instead of npm.`;\n if (sub === \"run\" || sub === \"run-script\") return `${BASE} Use \\`pnpm run <script>\\` or \\`pnpm <script>\\` instead of npm.`;\n if (sub === \"test\" || sub === \"t\" || sub === \"tst\") return `${BASE} Use \\`pnpm test\\` instead of npm.`;\n if (sub === \"start\") return `${BASE} Use \\`pnpm start\\` instead of npm.`;\n if (sub === \"stop\") return `${BASE} Use \\`pnpm stop\\` instead of npm.`;\n if (sub === \"restart\") return `${BASE} Use \\`pnpm restart\\` instead of npm.`;\n if (sub === \"exec\") return `${BASE} Use \\`pnpm exec <cmd>\\` instead of npm.`;\n if (sub === \"list\" || sub === \"ls\" || sub === \"ll\" || sub === \"la\") return `${BASE} Use \\`pnpm list\\` instead of npm.`;\n if (sub === \"why\") return `${BASE} Use \\`pnpm why\\` instead of npm.`;\n if (sub === \"outdated\") return `${BASE} Use \\`pnpm outdated\\` instead of npm.`;\n if (sub === \"audit\") return `${BASE} Use \\`pnpm audit\\` instead of npm.`;\n if (sub === \"update\" || sub === \"up\" || sub === \"upgrade\") return `${BASE} Use \\`pnpm update\\` instead of npm.`;\n if (sub === \"init\") return `${BASE} Use \\`pnpm init\\` instead of npm.`;\n if (sub === \"publish\") return `${BASE} Use \\`pnpm publish\\` instead of npm.`;\n if (sub === \"link\") return `${BASE} Use \\`pnpm link\\` instead of npm.`;\n if (sub === \"unlink\") return `${BASE} Use \\`pnpm unlink\\` instead of npm.`;\n if (sub === \"pack\") return `${BASE} Use \\`pnpm pack\\` instead of npm.`;\n if (sub === \"cache\") return `${BASE} Use \\`pnpm store\\` instead of \\`npm cache\\`.`;\n if (sub === \"config\" || sub === \"set\" || sub === \"get\") return `${BASE} Use \\`pnpm config\\` instead of npm.`;\n return `${BASE} Use the equivalent pnpm command instead of npm.`;\n}\n/**\n* Commands whose write destination is the *last* positional argument\n* (`cp src dst`, `mv src dst`, `rsync -a src/ dst/`, `install -m 755 src dst`,\n* `ln -f src dst`).\n*/\nconst LAST_POSITIONAL_DEST = /* @__PURE__ */ new Set([\n \"cp\",\n \"mv\",\n \"rsync\",\n \"install\",\n \"ln\"\n]);\n/** Commands where *every* positional is written to (`tee a b`, `truncate -s 0 f`). */\nconst ALL_POSITIONAL_DEST = /* @__PURE__ */ new Set([\"tee\", \"truncate\"]);\n/** `-t DIR` / `--target-directory[=DIR]` -- an explicit destination directory. */\nfunction explicitTargetDirectory(rest) {\n for (const [i, t] of rest.entries()) {\n if (t === \"-t\" || t === \"--target-directory\") return rest[i + 1] ?? null;\n if (t.startsWith(\"--target-directory=\")) return t.slice(19);\n }\n return null;\n}\n/** An output operator: `>` or `>>`, optionally after a file descriptor number or `&`. */\nconst OUTPUT_REDIRECT = /^(?:\\d*|&)>{1,2}$/;\nconst OUTPUT_REDIRECT_ATTACHED = /^(?:\\d*|&)>{1,2}(.+)$/;\n/**\n* An input operator: a single `<`, optionally after a file descriptor number.\n* `<<` and `<<<` feed inline text rather than a file, so they are not matched.\n*/\nconst INPUT_REDIRECT = /^\\d*<$/;\nconst INPUT_REDIRECT_ATTACHED = /^\\d*<([^<].*)$/;\n/** The direction of a redirect operator written apart from its target, or null. */\nfunction spacedRedirectDirection(token) {\n if (OUTPUT_REDIRECT.test(token)) return \"out\";\n if (INPUT_REDIRECT.test(token)) return \"in\";\n return null;\n}\n/**\n* Split a command's tokens into its file redirects and every other token, in\n* both the spaced (`> f`, `2> f`, `&> f`, `< f`) and attached (`>f`, `2>>f`,\n* `<f`) spellings. A trailing operator with no target is dropped.\n*\n* Checked for *every* command, not just known write tools: the shell splitter\n* leaves redirect tokens inside the segment, so without this an unrecognized\n* command (`some-generator > node_modules/pkg/index.js`) would sail past a\n* command-keyed rule. Over-capture here is harmless -- a bogus target like\n* `&1` from `2>&1` names neither node_modules nor an env file.\n*/\nfunction splitRedirects(tokens) {\n const redirects = [];\n const rest = [];\n for (let i = 0; i < tokens.length; i++) {\n const t = tokens[i];\n const spaced = spacedRedirectDirection(t);\n if (spaced !== null) {\n const next = tokens[i + 1];\n if (next !== void 0) redirects.push({\n direction: spaced,\n target: next\n });\n i++;\n continue;\n }\n const out = OUTPUT_REDIRECT_ATTACHED.exec(t);\n if (out) {\n redirects.push({\n direction: \"out\",\n target: out[1]\n });\n continue;\n }\n const input = INPUT_REDIRECT_ATTACHED.exec(t);\n if (input) {\n redirects.push({\n direction: \"in\",\n target: input[1]\n });\n continue;\n }\n rest.push(t);\n }\n return {\n redirects,\n rest\n };\n}\n/** The files a command's redirects write to (`direction` \"out\") or read from (\"in\"). */\nfunction redirectTargets(tokens, direction) {\n return splitRedirects(tokens).redirects.filter((r) => r.direction === direction).map((r) => r.target);\n}\n/**\n* Every path this command would *write to*. Sources are deliberately excluded:\n* reading out of `node_modules` (`cat node_modules/pkg/package.json`,\n* `cp node_modules/pkg/README.md docs/`) is routine and stays allowed. Only the\n* destination side can corrupt anything.\n*/\nfunction writeDestinations(tokens) {\n const cmd = path.basename(tokens[0] ?? \"\");\n const rest = tokens.slice(1);\n const dests = redirectTargets(tokens, \"out\");\n if (cmd === \"dd\") dests.push(...rest.filter((t) => t.startsWith(\"of=\")).map((t) => t.slice(3)));\n else if (cmd === \"sed\") {\n if (rest.some((t) => t === \"--in-place\" || /^-[a-zA-Z]*i/.test(t))) dests.push(...rest.filter((t) => !t.startsWith(\"-\")));\n } else if (ALL_POSITIONAL_DEST.has(cmd)) dests.push(...rest.filter((t) => !t.startsWith(\"-\")));\n else if (LAST_POSITIONAL_DEST.has(cmd)) {\n if (cmd === \"cp\" && rest.includes(\"--remove-destination\")) return dests;\n const targetDir = explicitTargetDirectory(rest);\n if (targetDir !== null) dests.push(targetDir);\n else {\n const last = rest.findLast((t) => !t.startsWith(\"-\"));\n if (last !== void 0) dests.push(last);\n }\n }\n return dests;\n}\n/**\n* True when the command writes anything into a `node_modules/` tree.\n*\n* See `targetsNodeModules` for why this is a hard deny rather than a prompt:\n* `node_modules` is hardlinked into pnpm's global content-addressable store, so\n* an in-place overwrite silently corrupts that package for every project on the\n* machine -- and the directory doing the writing looks perfectly fine afterward.\n* The 2026-07-13 store poisoning was exactly this, and it was *human-approved*\n* at the prompt, which is why a `none` result would not have stopped it.\n*/\nfunction writesIntoNodeModules(tokens, ctx) {\n return writeDestinations(tokens).some((d) => targetsNodeModules(d, ctx.cwd));\n}\n/** Shared by the `node-modules-write` deny rule and the early check in `evaluate.ts`. */\nconst NODE_MODULES_WRITE_REASON = \"Don't write into node_modules/. Its files are hardlinks into pnpm's global store, so overwriting one in place (plain `cp`, `>` redirect, `sed -i`, ...) writes through the link and corrupts that package for every project on this machine -- silently, since the directory you wrote into ends up fine. To test a local build as an installed dependency, run it directly (`node packages/<pkg>/dist/bin.mjs <cmd>`), or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`). If you must replace a file in place, `cp --remove-destination` is safe -- it unlinks the target first. Run `pnpm store status` to check whether the store is already damaged.\";\nfunction isChmod777(tokens) {\n return tokens.some((t) => t === \"777\" || t === \"0777\");\n}\nfunction isGitForcePush(tokens) {\n const pushIdx = tokens.indexOf(\"push\");\n if (pushIdx === -1) return false;\n return tokens.slice(pushIdx + 1).some((t) => t === \"--force\" || t === \"-f\");\n}\n/**\n* Commands whose bare positional arguments are files to read verbatim -- no\n* argument among them is a search pattern the way grep's is.\n*/\nconst ENV_READ_FILE_COMMANDS = /* @__PURE__ */ new Set([\n \"cat\",\n \"head\",\n \"tail\",\n \"less\",\n \"more\",\n \"source\",\n \".\"\n]);\n/** grep-family commands: their first positional is normally a search pattern, not a file. */\nconst GREP_FAMILY_COMMANDS = /* @__PURE__ */ new Set([\n \"grep\",\n \"rg\",\n \"ag\"\n]);\n/**\n* The file operands of a grep-family (`grep`/`rg`/`ag`) invocation, given its\n* tokens after the command name with redirects already stripped.\n*\n* This walks the tokens instead of filtering positionals and asking \"was a\n* pattern flag present anywhere\" as a single yes/no, because the two pattern\n* flags disagree about what to do with their *own* value once one is found:\n*\n* - `-e`/`--regexp` (bare, or attached as `-eX`/`--regexp=X`) supply the\n* search pattern directly, so that value is a pattern, never a file, and is\n* dropped -- `grep -e process.env src/` and `grep -rn -e process.env src/`\n* must not deny just because the pattern happens to look like a filename,\n* the same false positive the no-flag case below exists to avoid.\n* - `-f`/`--file` (bare, or attached as `-fX`/`--file=X`) name a *file of\n* patterns*, which is a real file read, so that value is kept as an\n* operand -- `grep -f pats.txt .env.local` must still deny on `.env.local`.\n*\n* Absent either flag, the first bare positional is the search pattern and\n* every one after it is a file -- `grep -n \"process.env\" src/x.ts` is the\n* false positive that motivated all of this: the pattern string\n* \"process.env\" matches the `.env` filename pattern just as well as a real\n* path does, so it must never be checked as a file operand.\n*\n* A `--` ends option parsing (see `optionsEnded` below): every token after it\n* is a literal positional even when it looks like `-e` or `-f`, following the\n* same \"first one is the pattern, absent an earlier flag\" rule -- `grep --\n* -e .env` passes `-e` as the pattern and `.env` as the file, and `grep --\n* .env src/x.ts` passes `.env` itself as the pattern (not a file) and\n* `src/x.ts` as the file.\n*/\nfunction grepFileOperands(rest) {\n const files = [];\n let patternAccountedFor = false;\n let optionsEnded = false;\n for (let i = 0; i < rest.length; i++) {\n const t = rest[i];\n if (!optionsEnded && t === \"--\") {\n optionsEnded = true;\n continue;\n }\n if (!optionsEnded) {\n if (t === \"-f\" || t === \"--file\") {\n const value = rest[i + 1];\n if (value !== void 0) {\n files.push(value);\n i++;\n }\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"--file=\")) {\n files.push(t.slice(7));\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"-f\") && t !== \"-f\" && !t.startsWith(\"--\")) {\n files.push(t.slice(2));\n patternAccountedFor = true;\n continue;\n }\n if (t === \"-e\" || t === \"--regexp\") {\n i++;\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"--regexp=\") || t.startsWith(\"-e\") && t !== \"-e\" && !t.startsWith(\"--\")) {\n patternAccountedFor = true;\n continue;\n }\n if (t.startsWith(\"-\")) continue;\n }\n if (!patternAccountedFor) {\n patternAccountedFor = true;\n continue;\n }\n files.push(t);\n }\n return files;\n}\n/**\n* Every file a command reads: each input redirect target, on any command,\n* plus the file operands of a `cat`/`head`/`tail`/`less`/`more`/`source`/`.`/\n* `grep`/`rg`/`ag` command. Backs the `env-file-read` rule. grep/rg/ag\n* delegate to `grepFileOperands` (above); every other command named here has\n* no search pattern to exclude, so every bare positional is a file operand.\n* Output redirects are left out: they write, and `env-file-write` owns them.\n*/\nfunction envFileOperands(tokens) {\n const cmd = path.basename(tokens[0] ?? \"\");\n const { redirects, rest } = splitRedirects(tokens.slice(1));\n const operands = redirects.filter((r) => r.direction === \"in\").map((r) => r.target);\n if (GREP_FAMILY_COMMANDS.has(cmd)) operands.push(...grepFileOperands(rest));\n else if (ENV_READ_FILE_COMMANDS.has(cmd)) operands.push(...rest.filter((t) => !t.startsWith(\"-\")));\n return operands;\n}\n/**\n* The two files a health ceiling raise may travel in. This mirrors\n* `HEALTH_CEILING_FILES` in tooling's `codebase-health/reference.ts`: the hook cannot import\n* tooling (tooling embeds the hook's own `dist/` at build time, so the edge would cycle), which\n* leaves a second copy of the two names here. `bst health:check` stays the wall -- this rule\n* only front-runs its refusal, at the tool call that would have written the file.\n*/\nconst HEALTH_CEILING_FILES = [\"health-baseline.json\", \".tooling.json\"];\n/** The one file of the two a session ever has a reason to write by hand. */\nconst HEALTH_BASELINE_FILE = \"health-baseline.json\";\n/** Shared by the two bash rules and the file rule: a deny reason that is a redirect. */\nconst HEALTH_RAISE_REASON = \"A health ceiling raise travels alone: open a ceiling-only pull request -- only health-baseline.json and .tooling.json changed -- for a human to merge, or split the module. `bst health:check` refuses the raise beside code either way.\";\n/**\n* True when the command writes the repository's `health-baseline.json` -- the file at the\n* **repository's** top level, matched exactly. A destination deeper in the tree is some other file\n* that happens to share the name (a test fixture is the common one), and a deny there would\n* be a false positive, which no rule here is allowed to create.\n*\n* The top level, not `cwd`: a session started in `packages/x` has a `cwd` of its own, and\n* matching against it would deny a write to `packages/x/health-baseline.json` -- a file that is\n* not the gate's baseline and not this rule's business.\n*/\nfunction writesHealthBaseline(tokens, cwd) {\n const destinations = writeDestinations(tokens);\n if (!destinations.some((dest) => baseName(dest) === \"health-baseline.json\")) return false;\n const root = getRepoTopLevel(cwd) ?? cwd;\n return destinations.some((dest) => samePath(resolveIn(cwd, dest), path.join(root, HEALTH_BASELINE_FILE)));\n}\n/** The last path segment of a write destination, on either separator. */\nfunction baseName(target) {\n const segments = target.split(/[/\\\\]/);\n return segments[segments.length - 1] ?? \"\";\n}\n/** `bst health:check --update-baseline --reset` -- the verb that rewrites the whole baseline. */\nfunction isHealthBaselineReset(tokens) {\n return tokens.includes(\"health:check\") && tokens.includes(\"--update-baseline\") && tokens.includes(\"--reset\");\n}\n/**\n* The branch condition, tested only after a rule has seen a write to the baseline: a rule that\n* matches every command must not spawn git for every command. `null` (git could not answer)\n* denies nothing.\n*/\nfunction raiseTravelsWithCode(cwd) {\n return branchCarriesOtherFiles(cwd, HEALTH_CEILING_FILES) === true;\n}\nconst denyRules = [\n {\n name: \"sudo\",\n match: /^sudo$/,\n action: \"deny\",\n reason: \"Don't use sudo. If you need elevated permissions, ask me to do it manually.\"\n },\n {\n name: \"git-force-push\",\n match: /^git$/,\n action: \"deny\",\n when: (tokens) => isGitForcePush(tokens),\n reason: \"Use `git push --force-with-lease` instead of `--force`.\"\n },\n {\n name: \"git-protected-branch-write\",\n match: /^git$/,\n action: \"deny\",\n when: (tokens, ctx) => movesProtectedRef(tokens, ctx),\n reason: \"This would move a protected branch (main, master, the remote's default branch, or one listed in .claude/hooks/bst-hook.settings.json#protectedBranches). Work on a feature branch in a worktree: `git worktree add ../<repo>-<topic> -b feat/<topic>`, then commit and push there. To bring `main` up to date, use `git pull --ff-only`.\"\n },\n {\n name: \"chmod-777\",\n match: /^chmod$/,\n action: \"deny\",\n when: (tokens) => isChmod777(tokens),\n reason: \"Don't use 777 permissions. Use more restrictive permissions.\"\n },\n {\n name: \"node-modules-write\",\n match: /./,\n action: \"deny\",\n when: (tokens, ctx) => writesIntoNodeModules(tokens, ctx),\n reason: NODE_MODULES_WRITE_REASON\n },\n {\n name: \"env-file-read\",\n match: /./,\n action: \"deny\",\n when: (tokens) => envFileOperands(tokens).some(isEnvFile),\n reason: \"Don't read .env files. These contain secrets that should not appear in the conversation.\"\n },\n {\n name: \"env-file-write\",\n match: /./,\n action: \"deny\",\n when: (tokens) => redirectTargets(tokens, \"out\").some(isEnvFile),\n reason: \"Don't write to .env files. These contain secrets.\"\n },\n {\n name: \"health-baseline-write\",\n match: /./,\n action: \"deny\",\n when: (tokens, ctx) => writesHealthBaseline(tokens, ctx.cwd) && raiseTravelsWithCode(ctx.cwd),\n reason: HEALTH_RAISE_REASON\n },\n {\n name: \"health-baseline-reset\",\n match: /^bst$/,\n action: \"deny\",\n when: (tokens, ctx) => isHealthBaselineReset(tokens) && raiseTravelsWithCode(ctx.cwd),\n reason: HEALTH_RAISE_REASON\n },\n {\n name: \"npx-redirect\",\n match: /^npx$/,\n action: \"deny\",\n reason: \"This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\"\n },\n {\n name: \"mise-self-update\",\n match: /^mise$/,\n action: \"deny\",\n when: (tokens) => tokens[1] === \"self-update\",\n reason: \"Don't self-update mise. The version is Renovate-managed; ask a human to bump the pin.\"\n }\n];\n\n//#endregion\n//#region src/lib/script-resolver.ts\nconst PackageJsonSchema = object({ scripts: record(string(), unknown()) });\n/**\n* Resolve a pnpm script name to its actual command(s) by reading package.json.\n*\n* Returns null if the script cannot be resolved (missing package.json,\n* missing script name, parse error).\n*/\nfunction resolveScript(scriptName, cwd) {\n const scripts = readScripts(cwd);\n if (!scripts) return null;\n const main = scripts[scriptName];\n if (typeof main !== \"string\") return null;\n const pre = scripts[`pre${scriptName}`];\n const post = scripts[`post${scriptName}`];\n return {\n pre: typeof pre === \"string\" ? pre : null,\n main,\n post: typeof post === \"string\" ? post : null\n };\n}\n/**\n* Resolve a script in a specific workspace package identified by --filter.\n*\n* Reads pnpm-workspace.yaml, scans workspace directories to match filterPkg\n* against package.json#name fields, then resolves the script in that package's directory.\n*\n* Returns null if the package or script cannot be resolved.\n*/\nfunction resolveFilteredScript(scriptName, filterPkg, cwd) {\n const pkgDir = findWorkspacePackageDir(filterPkg, cwd);\n if (!pkgDir) return null;\n const script = resolveScript(scriptName, pkgDir);\n if (!script) return null;\n return {\n ...script,\n pkgDir\n };\n}\n/**\n* Resolve a script name across all workspace packages (for `pnpm -r <script>`).\n*\n* Returns an array of resolved scripts (one per workspace package that defines the script),\n* each with a `pkgDir` field. Returns null if workspace patterns can't be read.\n* Packages that don't define the script are silently skipped.\n*/\nfunction resolveRecursiveScripts(scriptName, rootDir) {\n const pkgDirs = listWorkspacePackageDirs(rootDir);\n if (!pkgDirs) return null;\n const results = [];\n for (const pkgDir of pkgDirs) {\n const resolved = resolveScript(scriptName, pkgDir);\n if (resolved) results.push({\n ...resolved,\n pkgDir\n });\n }\n return results;\n}\n/**\n* List all workspace package directories.\n*/\nfunction listWorkspacePackageDirs(rootDir) {\n const patterns = readWorkspacePatterns(rootDir);\n if (!patterns) return null;\n const dirs = [];\n for (const pattern of patterns) {\n if (!pattern.endsWith(\"/*\")) continue;\n const baseDir = pattern.slice(0, -2);\n const parentDir = resolve(rootDir, baseDir);\n let entries;\n try {\n entries = readdirSync(parentDir);\n } catch {\n continue;\n }\n for (const entry of entries) {\n const entryDir = join(parentDir, entry);\n try {\n if (!statSync(entryDir).isDirectory()) continue;\n } catch {\n continue;\n }\n dirs.push(entryDir);\n }\n }\n return dirs.length > 0 ? dirs : null;\n}\n/**\n* Find the directory of a workspace package by name.\n* Reads pnpm-workspace.yaml for glob patterns, then scans matching directories.\n*/\nfunction findWorkspacePackageDir(pkgName, rootDir) {\n const pkgDirs = listWorkspacePackageDirs(rootDir);\n if (!pkgDirs) return null;\n for (const entryDir of pkgDirs) {\n const name = readPackageName(entryDir);\n if (name === pkgName || name === `@${pkgName}`) return entryDir;\n if (entryDir.split(\"/\").pop() === pkgName) return entryDir;\n if (name) {\n if ((name.startsWith(\"@\") ? name.split(\"/\")[1] : name) === pkgName) return entryDir;\n }\n }\n return null;\n}\n/**\n* Read workspace glob patterns from pnpm-workspace.yaml.\n*/\nfunction readWorkspacePatterns(rootDir) {\n try {\n const lines = readFileSync(join(rootDir, \"pnpm-workspace.yaml\"), \"utf-8\").split(\"\\n\");\n const packagesIdx = lines.findIndex((l) => /^packages\\s*:/.test(l));\n if (packagesIdx === -1) return null;\n const patterns = [];\n for (let i = packagesIdx + 1; i < lines.length; i++) {\n const line = lines[i];\n const match = /^\\s+-\\s+(.+)$/.exec(line);\n if (!match) break;\n patterns.push(match[1].trim().replace(/['\"`]/g, \"\"));\n }\n return patterns.length > 0 ? patterns : null;\n } catch {\n return null;\n }\n}\nconst PackageNameSchema = object({ name: string() });\n/**\n* Read the name field from package.json in the given directory.\n*/\nfunction readPackageName(dir) {\n try {\n const raw = readFileSync(join(dir, \"package.json\"), \"utf-8\");\n const result = PackageNameSchema.safeParse(JSON.parse(raw));\n if (!result.success) return null;\n return result.data.name;\n } catch {\n return null;\n }\n}\n/**\n* Read the scripts field from package.json in the given directory.\n* Returns null on any failure (missing file, parse error, no scripts field).\n*/\nfunction readScripts(cwd) {\n try {\n const raw = readFileSync(join(cwd, \"package.json\"), \"utf-8\");\n const result = PackageJsonSchema.safeParse(JSON.parse(raw));\n if (!result.success) return null;\n return result.data.scripts;\n } catch {\n return null;\n }\n}\n\n//#endregion\n//#region src/lib/workspace-root.ts\n/**\n* Walk up from `cwd` to the directory holding `pnpm-workspace.yaml`.\n*\n* Its own module rather than a `script-resolver.ts` export because\n* `settings.ts` needs it and `script-resolver.ts` needs `settings.ts`,\n* which is a cycle the health gate reports.\n*/\nfunction findWorkspaceRoot(cwd) {\n let dir = resolve(cwd);\n while (true) {\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n//#endregion\n//#region src/evaluate.ts\nconst MAX_EVAL_DEPTH = 5;\n/**\n* The body of a command that is wholly one subshell -- its first `(` closes at\n* its last character -- or null. `(a) && (b)` is not one subshell: its first\n* `(` closes early. Parentheses inside single or double quotes, or escaped\n* with a backslash, are not counted.\n*/\nfunction subshellBody(command) {\n if (!command.startsWith(\"(\") || !command.endsWith(\")\")) return null;\n let depth = 0;\n let quote = null;\n for (let i = 0; i < command.length; i++) {\n const ch = command[i];\n if (quote !== null) {\n if (ch === quote) quote = null;\n else if (ch === \"\\\\\" && quote === \"\\\"\") i++;\n continue;\n }\n if (ch === \"\\\\\") i++;\n else if (ch === \"'\" || ch === \"\\\"\") quote = ch;\n else if (ch === \"(\") depth++;\n else if (ch === \")\") {\n depth--;\n if (depth === 0) return i === command.length - 1 ? command.slice(1, -1) : null;\n }\n }\n return null;\n}\n/**\n* Extract the target directory from a `cd <dir>` segment.\n* Returns the resolved absolute path, or null if the segment isn't a simple `cd`.\n*/\nfunction extractCdDir(segment, ctx) {\n const tokenResult = tokenize(segment);\n if (!tokenResult.ok || tokenResult.tokens.length !== 2) return null;\n const tokens = tokenResult.tokens;\n if (tokens[0] !== \"cd\") return null;\n const dir = tokens[1];\n if (path.isAbsolute(dir)) return dir;\n return path.resolve(ctx.cwd, dir);\n}\n/**\n* The branch a `git switch` / `git checkout` segment leaves checked out, or\n* undefined when the segment is not one (see `branchSwitch`).\n*/\nfunction extractBranchSwitch(segment, ctx) {\n const tokenResult = tokenize(segment);\n if (!tokenResult.ok) return void 0;\n let tokens = tokenResult.tokens;\n let envSkipped = 0;\n while (envSkipped < tokens.length && /^\\w+=/.test(tokens[envSkipped])) envSkipped++;\n tokens = tokens.slice(envSkipped);\n if (tokens.length === 0 || path.basename(tokens[0]) !== \"git\") return void 0;\n return branchSwitch(tokens, ctx, checkInjection(tokens, segment).safe);\n}\n/**\n* Evaluate a full command string. Splits on operators and evaluates each segment.\n*\n* This is the single reentrant entry point. Called at depth 0 for top-level\n* commands and at depth 1+ for derived commands (resolved scripts, exec tails, etc.).\n*/\nfunction evaluateCommand(command, ctx) {\n if (ctx.depth >= MAX_EVAL_DEPTH) return {\n action: \"none\",\n rule: null,\n reason: \"max evaluation depth exceeded\"\n };\n const trimmed = command.trim();\n if (!trimmed) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n const body = subshellBody(trimmed);\n if (body !== null) return evaluateCommand(body, {\n ...ctx,\n depth: ctx.depth + 1\n });\n const segments = splitCommand(trimmed);\n if (segments.length === 0) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n let firstNone = null;\n const currentCtx = { ...ctx };\n let unknownAfterNext;\n for (const segment of segments) {\n if (segment.operator === \"&&\") {\n const cdDir = extractCdDir(segment.command, currentCtx);\n if (cdDir) currentCtx.cwd = cdDir;\n }\n const result = evaluateSegment(segment.command, currentCtx);\n if (result.action === \"deny\") return result;\n if (!firstNone) firstNone = result;\n if (unknownAfterNext) {\n currentCtx.pendingBranches = {\n ...currentCtx.pendingBranches,\n [unknownAfterNext.dir]: unknownAfterNext\n };\n unknownAfterNext = void 0;\n }\n const switched = extractBranchSwitch(segment.command, currentCtx);\n if (switched) {\n const unknown = {\n dir: switched.dir,\n branch: null\n };\n if (segment.operator === \"&&\") currentCtx.pendingBranches = {\n ...currentCtx.pendingBranches,\n [switched.dir]: switched\n };\n else if (segment.operator === \"||\") unknownAfterNext = unknown;\n else currentCtx.pendingBranches = {\n ...currentCtx.pendingBranches,\n [switched.dir]: unknown\n };\n }\n }\n return firstNone ?? {\n action: \"none\",\n rule: null,\n reason: null\n };\n}\n/**\n* Helper to evaluate a resolved script with its pre and post hooks.\n*\n* There is no allow tier, so there is nothing to \"trust\" a clean package.json\n* into skipping: every resolved script -- clean or not -- is evaluated for\n* denies the same way a top-level command is. A deny can only get stronger by\n* running the check, never weaker, so there is no safety reason to skip it.\n*/\nfunction evaluateResolvedScript(resolved, ctx) {\n if (resolved.pre) {\n const preResult = evaluateCommand(resolved.pre, ctx);\n if (preResult.action === \"deny\") return preResult;\n }\n const mainResult = evaluateCommand(resolved.main, ctx);\n if (mainResult.action === \"deny\") return mainResult;\n if (resolved.post) {\n const postResult = evaluateCommand(resolved.post, ctx);\n if (postResult.action === \"deny\") return postResult;\n }\n return {\n action: \"none\",\n rule: null,\n reason: null\n };\n}\n/**\n* Evaluate a single command segment (no operators).\n* May recurse into evaluateCommand() for indirection constructs.\n*/\nfunction evaluateSegment(rawSegment, ctx) {\n const trimmed = rawSegment.trim();\n const body = subshellBody(trimmed);\n if (body !== null) return evaluateCommand(body, {\n ...ctx,\n depth: ctx.depth + 1\n });\n if (trimmed.startsWith(\"(\") && trimmed.endsWith(\")\")) return {\n action: \"none\",\n rule: null,\n reason: \"subshell\"\n };\n const tokenResult = tokenize(rawSegment);\n if (!tokenResult.ok) return {\n action: \"none\",\n rule: null,\n reason: tokenResult.error\n };\n let tokens = tokenResult.tokens;\n if (tokens.length === 0) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n let envSkipped = 0;\n while (envSkipped < tokens.length && /^\\w+=/.test(tokens[envSkipped])) envSkipped++;\n if (envSkipped >= tokens.length) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n tokens = tokens.slice(envSkipped);\n if (writesIntoNodeModules(tokens, ctx)) return {\n action: \"deny\",\n rule: \"node-modules-write\",\n reason: NODE_MODULES_WRITE_REASON\n };\n const injection = checkInjection(tokens, rawSegment);\n if (!injection.safe) return {\n action: \"none\",\n rule: null,\n reason: injection.reason\n };\n const SAFE_REDIRECT_TOKENS = /* @__PURE__ */ new Set([\n \"2>&1\",\n \"2>/dev/null\",\n \">/dev/null\",\n \"1>/dev/null\"\n ]);\n tokens = tokens.filter((t, i) => {\n if (SAFE_REDIRECT_TOKENS.has(t)) return false;\n if ((t === \">\" || t === \">>\") && tokens[i + 1] === \"/dev/null\") return false;\n if (t === \"/dev/null\" && i > 0 && (tokens[i - 1] === \">\" || tokens[i - 1] === \">>\")) return false;\n return true;\n });\n const cmdName = path.basename(tokens[0]);\n if (cmdName === \"command\" && tokens.length > 1) return evaluateCommand(joinTokens(tokens.slice(1)), {\n ...ctx,\n depth: ctx.depth + 1\n });\n if (cmdName === \"env\") {\n let envIdx = 1;\n while (envIdx < tokens.length && /^\\w+=/.test(tokens[envIdx])) envIdx++;\n if (envIdx >= tokens.length) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n return evaluateCommand(joinTokens(tokens.slice(envIdx)), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n if (cmdName === \"pnpm\") return evaluatePnpm(tokens, ctx);\n if (cmdName === \"npm\") return evaluateNpm(tokens);\n if (cmdName === \"mise\" && tokens[1] === \"exec\") {\n const inner = miseExecInner(tokens);\n if (!inner) return {\n action: \"none\",\n rule: null,\n reason: \"bare mise exec\"\n };\n return evaluateCommand(joinTokens(inner), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n if (cmdName === \"bash\" || cmdName === \"sh\" || cmdName === \"zsh\") return evaluateShellC(tokens, ctx);\n if (cmdName === \"xargs\") return evaluateXargs(tokens, ctx);\n if (cmdName === \"node\" && (tokens.includes(\"-e\") || tokens.includes(\"-p\") || tokens.includes(\"--eval\") || tokens.includes(\"--print\"))) return {\n action: \"none\",\n rule: null,\n reason: \"node eval\"\n };\n for (const rule of denyRules) if (rule.match.test(cmdName)) {\n if (!rule.when || rule.when(tokens, ctx)) return {\n action: \"deny\",\n rule: rule.name,\n reason: rule.reason ?? null\n };\n }\n const advice = cmdName === \"git\" ? worktreeAdvice(tokens, ctx) : void 0;\n return {\n action: \"none\",\n rule: null,\n reason: `no rule matched: ${cmdName}`,\n ...advice ? { advice } : {}\n };\n}\n/**\n* The inner command of `mise exec [tool@version...] -- <cmd...>`: everything after the `--`\n* separator. `undefined` when there is no `--` (a bare `mise exec` with nothing to unwrap) or\n* nothing follows it.\n*/\nfunction miseExecInner(tokens) {\n const dashIdx = tokens.indexOf(\"--\");\n if (dashIdx === -1) return void 0;\n const inner = tokens.slice(dashIdx + 1);\n return inner.length > 0 ? inner : void 0;\n}\n/**\n* Scan pnpm subTokens starting at `from`, skipping and accumulating flags\n* (--filter / -F, -r, -w, -C, and generic --flags). Returns the first non-flag\n* token as `name`, or `undefined` if none found. Returns `\"filter\"` as a\n* sentinel when --filter is encountered (caller must handle).\n*/\nfunction scanPnpmFlags(subTokens, from, state) {\n for (let i = from; i < subTokens.length; i++) {\n const t = subTokens[i];\n if (t === \"--filter\" || t.startsWith(\"--filter=\") || t === \"-F\" || t.startsWith(\"-F=\")) return {\n name: void 0,\n filterIdx: i\n };\n if (t === \"-r\" || t === \"--recursive\") {\n state.isRecursive = true;\n continue;\n }\n if (t === \"-w\" || t === \"--workspace-root\") {\n state.isWorkspaceRoot = true;\n continue;\n }\n if (t === \"-C\" || t === \"--dir\") {\n state.dirOverride = subTokens[++i];\n continue;\n }\n if (t.startsWith(\"-C=\") || t.startsWith(\"--dir=\")) {\n state.dirOverride = t.split(\"=\", 2)[1];\n continue;\n }\n if (t.startsWith(\"-\") && !PNPM_BUILTINS.has(t)) continue;\n return { name: t };\n }\n return { name: void 0 };\n}\n/**\n* Evaluate a pnpm command. Classifies the subcommand as built-in, exec, or script.\n* Script commands are resolved via package.json and recursively evaluated.\n*/\nfunction evaluatePnpm(tokens, ctx) {\n const subTokens = tokens.slice(1);\n const state = {\n isRecursive: false,\n isWorkspaceRoot: false,\n dirOverride: void 0\n };\n const scan = scanPnpmFlags(subTokens, 0, state);\n if (scan.filterIdx !== void 0) return evaluatePnpmFilter(tokens, subTokens, scan.filterIdx, ctx);\n const subcommand = scan.name;\n const subcommandIdx = subcommand ? subTokens.indexOf(subcommand) : -1;\n if (!subcommand) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n if (subcommand === \"dlx\") return {\n action: \"none\",\n rule: null,\n reason: \"pnpm dlx\"\n };\n if (subcommand === \"add\") return {\n action: \"none\",\n rule: null,\n reason: \"pnpm add\"\n };\n if (subcommand === \"exec\") {\n const execArgs = subTokens.slice(subcommandIdx + 1);\n if (execArgs.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"bare pnpm exec\"\n };\n return evaluateCommand(joinTokens(execArgs), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n let scriptName;\n let { isRecursive, isWorkspaceRoot, dirOverride } = state;\n if (subcommand === \"run\") {\n const runScan = scanPnpmFlags(subTokens, subcommandIdx + 1, state);\n ({isRecursive, isWorkspaceRoot, dirOverride} = state);\n if (runScan.filterIdx !== void 0) return evaluatePnpmFilter(tokens, subTokens, runScan.filterIdx, ctx);\n if (!runScan.name) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm run without script name\"\n };\n scriptName = runScan.name;\n } else if (!PNPM_BUILTINS.has(subcommand)) scriptName = subcommand;\n else return {\n action: \"none\",\n rule: null,\n reason: null\n };\n if (isRecursive) return evaluatePnpmRecursive(scriptName, ctx);\n if (isWorkspaceRoot) {\n const root = findWorkspaceRoot(ctx.cwd);\n if (!root) return {\n action: \"none\",\n rule: null,\n reason: \"workspace root not found\"\n };\n return evaluatePnpmScript(scriptName, {\n ...ctx,\n cwd: root\n });\n }\n if (dirOverride) {\n const resolved = path.resolve(ctx.cwd, dirOverride);\n return evaluatePnpmScript(scriptName, {\n ...ctx,\n cwd: resolved\n });\n }\n return evaluatePnpmScript(scriptName, ctx);\n}\n/**\n* Resolve a pnpm script name via package.json and recursively evaluate\n* the resolved command(s) including pre/post scripts.\n*/\nfunction evaluatePnpmScript(scriptName, ctx) {\n const resolved = resolveScript(scriptName, ctx.cwd);\n if (!resolved) return {\n action: \"none\",\n rule: null,\n reason: \"script resolution failed\"\n };\n const res = evaluateResolvedScript(resolved, {\n ...ctx,\n depth: ctx.depth + 1\n });\n if (res.action === \"deny\") return res;\n return {\n action: \"none\",\n rule: `pnpm-script:${scriptName}`,\n reason: null\n };\n}\n/**\n* Evaluate `pnpm -r <script>` by resolving the script in each workspace package.\n*\n* Unlike `evaluatePnpmScript` (which resolves from the root package.json and would\n* cycle back to `pnpm -r <script>`), this resolves against each workspace package's\n* own package.json, breaking the recursion.\n*/\nfunction evaluatePnpmRecursive(scriptName, ctx) {\n const resolved = resolveRecursiveScripts(scriptName, ctx.cwd);\n if (!resolved || resolved.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm recursive\"\n };\n for (const pkg of resolved) {\n const res = evaluateResolvedScript(pkg, {\n ...ctx,\n depth: ctx.depth + 1,\n cwd: pkg.pkgDir ?? ctx.cwd\n });\n if (res.action === \"deny\") return res;\n }\n return {\n action: \"none\",\n rule: `pnpm-recursive:${scriptName}`,\n reason: null\n };\n}\n/**\n* Evaluate an npm command. Read-only ops (view/info/show) pass through;\n* everything else denies with a redirect message to the pnpm equivalent.\n*/\nfunction evaluateNpm(tokens) {\n if (isNpmReadOp(tokens)) return {\n action: \"none\",\n rule: null,\n reason: null\n };\n return {\n action: \"deny\",\n rule: \"npm-redirect\",\n reason: getNpmRedirectReason(tokens)\n };\n}\n/**\n* Evaluate `pnpm --filter <pkg> [--filter <pkg> ...] <subcommand>`.\n*\n* Collects every `--filter` / `-F` flag (long, equals, and shorthand forms),\n* resolves each against pnpm-workspace.yaml, then classifies the subcommand as\n* built-in or script. Script commands are resolved and evaluated in each\n* filtered package -- all must allow.\n*\n* Falls back to none if workspace resolution fails.\n*/\nfunction evaluatePnpmFilter(_tokens, subTokens, filterIdx, ctx) {\n const filterPkgs = [];\n let cursor = filterIdx;\n while (cursor < subTokens.length) {\n const t = subTokens[cursor];\n if (t === \"--filter\" || t === \"-F\") {\n const pkg = subTokens[cursor + 1];\n if (!pkg || pkg.startsWith(\"-\")) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without package\"\n };\n filterPkgs.push(pkg);\n cursor += 2;\n } else if (t.startsWith(\"--filter=\") || t.startsWith(\"-F=\")) {\n const pkg = t.startsWith(\"-F=\") ? t.slice(3) : t.slice(9);\n if (!pkg) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without package\"\n };\n filterPkgs.push(pkg);\n cursor += 1;\n } else break;\n }\n if (filterPkgs.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without package\"\n };\n let filterSubcommand;\n let subcmdIdx = -1;\n for (let i = cursor; i < subTokens.length; i++) {\n const t = subTokens[i];\n if (t === \"--filter\" || t === \"-F\") {\n i += 1;\n continue;\n }\n if (t.startsWith(\"--filter=\") || t.startsWith(\"-F=\")) continue;\n if (t.startsWith(\"-\") && !PNPM_BUILTINS.has(t)) continue;\n filterSubcommand = t;\n subcmdIdx = i;\n break;\n }\n if (!filterSubcommand) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter without subcommand\"\n };\n if (PNPM_BUILTINS.has(filterSubcommand) && filterSubcommand !== \"run\" && filterSubcommand !== \"exec\") return {\n action: \"none\",\n rule: null,\n reason: null\n };\n if (filterSubcommand === \"exec\") {\n const execArgs = subTokens.slice(subcmdIdx + 1);\n if (execArgs.length === 0) return {\n action: \"none\",\n rule: null,\n reason: \"bare pnpm exec\"\n };\n return evaluateCommand(joinTokens(execArgs), {\n ...ctx,\n depth: ctx.depth + 1\n });\n }\n const scriptName = filterSubcommand === \"run\" ? subTokens[subcmdIdx + 1] : filterSubcommand;\n if (!scriptName || scriptName.startsWith(\"-\")) return {\n action: \"none\",\n rule: null,\n reason: \"pnpm --filter run without script name\"\n };\n if (PNPM_BUILTINS.has(scriptName) && scriptName !== \"run\") return {\n action: \"none\",\n rule: null,\n reason: null\n };\n for (const pkg of filterPkgs) {\n const resolved = resolveFilteredScript(scriptName, pkg, ctx.cwd);\n if (!resolved) return {\n action: \"none\",\n rule: null,\n reason: \"filtered script resolution failed\"\n };\n const res = evaluateResolvedScript(resolved, {\n ...ctx,\n depth: ctx.depth + 1,\n cwd: resolved.pkgDir ?? ctx.cwd\n });\n if (res.action === \"deny\") return res;\n }\n return {\n action: \"none\",\n rule: `pnpm-filter:${filterPkgs.join(\",\")}:${scriptName}`,\n reason: null\n };\n}\n/** A login flag: the shell reads its profile first, then runs `-c` as usual. */\nconst SHELL_LOGIN_FLAGS = /* @__PURE__ */ new Set([\"-l\", \"--login\"]);\n/**\n* Evaluate `bash -c` / `sh -c` / `zsh -c` commands.\n*\n* Unwraps only `<shell> [-l|--login]... -c <command-string>`, with `-c` also\n* accepted inside a cluster of `l` and `c` (`-lc`, `-cl`). A login shell reads\n* its profile before the command, which can change what runs but never makes\n* a denied command safe. Any other flag, a positional after the command\n* string (it sets `$0`, `$1`, ...), or a missing `-c` -> none.\n*/\nfunction evaluateShellC(tokens, ctx) {\n let i = 1;\n while (i < tokens.length && SHELL_LOGIN_FLAGS.has(tokens[i])) i++;\n const flag = tokens[i];\n if (flag === void 0 || !/^-[lc]+$/.test(flag) || !flag.includes(\"c\")) return {\n action: \"none\",\n rule: null,\n reason: \"shell without a plain -c\"\n };\n const cmdArg = tokens[i + 1];\n if (!cmdArg) return {\n action: \"none\",\n rule: null,\n reason: \"shell -c without command\"\n };\n if (tokens.length > i + 2) return {\n action: \"none\",\n rule: null,\n reason: \"shell -c with positional args\"\n };\n return evaluateCommand(cmdArg, {\n ...ctx,\n depth: ctx.depth + 1\n });\n}\n/** xargs flags that consume the next token as an argument */\nconst XARGS_ARG_FLAGS = /* @__PURE__ */ new Set([\n \"-I\",\n \"-d\",\n \"-n\",\n \"-P\",\n \"-s\",\n \"-L\",\n \"-E\",\n \"-a\",\n \"--max-args\",\n \"--max-procs\",\n \"--delimiter\",\n \"--replace\",\n \"--max-lines\",\n \"--max-chars\",\n \"--arg-file\",\n \"--process-slot-var\"\n]);\n/** xargs flags that are boolean (no argument) */\nconst XARGS_BOOL_FLAGS = /* @__PURE__ */ new Set([\n \"-0\",\n \"--null\",\n \"-t\",\n \"--verbose\",\n \"-r\",\n \"--no-run-if-empty\",\n \"-p\",\n \"--interactive\",\n \"-x\",\n \"--exit\",\n \"--show-limits\"\n]);\n/**\n* Evaluate `xargs <cmd>` commands.\n*\n* Skips xargs flags to find the inner command, then recursively evaluates it.\n* None if no command follows the flags or if unrecognised flags are found.\n*/\nfunction evaluateXargs(tokens, ctx) {\n let i = 1;\n while (i < tokens.length) {\n const t = tokens[i];\n if (!t.startsWith(\"-\")) break;\n if (XARGS_ARG_FLAGS.has(t)) {\n i += 2;\n continue;\n }\n const eqIdx = t.indexOf(\"=\");\n if (eqIdx !== -1) {\n const flagName = t.slice(0, eqIdx);\n if (XARGS_ARG_FLAGS.has(flagName)) {\n i++;\n continue;\n }\n }\n if (t.length > 2 && XARGS_ARG_FLAGS.has(t.slice(0, 2))) {\n i++;\n continue;\n }\n if (XARGS_BOOL_FLAGS.has(t)) {\n i++;\n continue;\n }\n return {\n action: \"none\",\n rule: null,\n reason: \"xargs with unrecognised flags\"\n };\n }\n if (i >= tokens.length) return {\n action: \"none\",\n rule: null,\n reason: \"xargs without command\"\n };\n return evaluateCommand(joinTokens(tokens.slice(i)), {\n ...ctx,\n depth: ctx.depth + 1\n });\n}\n\n//#endregion\n//#region src/file-rules.ts\nconst READ_TOOLS = /* @__PURE__ */ new Set([\n \"Read\",\n \"Glob\",\n \"Grep\"\n]);\nconst WRITE_TOOLS = /* @__PURE__ */ new Set([\"Write\", \"Edit\"]);\nconst fileDenyRules = [\n {\n name: \"env-file-read\",\n tools: READ_TOOLS,\n action: \"deny\",\n when: (filePath) => isEnvFile(filePath),\n reason: \"Don't read .env files. These contain secrets that should not appear in the conversation.\"\n },\n {\n name: \"env-file-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath) => isEnvFile(filePath),\n reason: \"Don't write to .env files. These contain secrets.\"\n },\n {\n name: \"hooks-folder-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => {\n const resolved = resolveIn(ctx.cwd, filePath);\n const hooksDir = path.join(ctx.cwd, \".claude\", \"hooks\");\n return isWithin(hooksDir, resolved);\n },\n reason: \"Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.\"\n },\n {\n name: \"git-dir-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => targetsGitDir(filePath, ctx.cwd),\n reason: \"Don't write into .git/ directly. Its internals (config, hooks, refs) are not plain project files -- a config edit can redirect remotes or push destinations that other rules trust, and a hooks/ script runs automatically on the next commit/push. Use git subcommands instead, or ask the user.\"\n },\n {\n name: \"node-modules-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => targetsNodeModules(filePath, ctx.cwd),\n reason: \"Don't write into node_modules/. Its files are hardlinks into pnpm's global store -- editing one in place corrupts that package for every project on this machine. Edit the source package and rebuild, or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`).\"\n },\n {\n name: \"claude-settings-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => {\n const resolved = resolveIn(ctx.cwd, filePath);\n return samePath(resolved, path.join(ctx.cwd, \".claude\", \"settings.json\"));\n },\n reason: \"Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.\"\n },\n {\n name: \"health-baseline-write\",\n tools: WRITE_TOOLS,\n action: \"deny\",\n when: (filePath, ctx) => {\n const resolved = resolveIn(ctx.cwd, filePath);\n if (path.basename(resolved) !== \"health-baseline.json\") return false;\n const root = getRepoTopLevel(ctx.cwd) ?? ctx.cwd;\n if (!samePath(resolved, path.join(root, \"health-baseline.json\"))) return false;\n return branchCarriesOtherFiles(ctx.cwd, HEALTH_CEILING_FILES) === true;\n },\n reason: HEALTH_RAISE_REASON\n }\n];\n\n//#endregion\n//#region src/evaluate-tool.ts\n/** Tools whose input contains a `file_path` field. */\nconst FILE_PATH_TOOLS = /* @__PURE__ */ new Set([\n \"Read\",\n \"Write\",\n \"Edit\"\n]);\n/** Tools whose input contains a `path` field (search root, optional). */\nconst SEARCH_TOOLS = /* @__PURE__ */ new Set([\"Glob\", \"Grep\"]);\n/**\n* Extract the relevant file path from tool input based on tool name.\n* Returns null if the input shape doesn't match expectations.\n*/\nfunction extractPath(toolName, toolInput, cwd) {\n if (FILE_PATH_TOOLS.has(toolName)) return parseFilePathInput(toolInput)?.file_path ?? null;\n if (SEARCH_TOOLS.has(toolName)) return parseSearchInput(toolInput)?.path ?? cwd;\n return null;\n}\n/**\n* Evaluate a non-Bash tool invocation against file-tool rules.\n* Returns deny or none.\n*/\nfunction evaluateToolUse(toolName, toolInput, ctx) {\n const filePath = extractPath(toolName, toolInput, ctx.cwd);\n if (filePath === null) return {\n action: \"none\",\n rule: null,\n reason: \"unknown tool input shape\"\n };\n for (const rule of fileDenyRules) if (rule.tools.has(toolName) && rule.when(filePath, ctx)) return {\n action: \"deny\",\n rule: rule.name,\n reason: rule.reason ?? null\n };\n return {\n action: \"none\",\n rule: null,\n reason: \"no file rule matched\"\n };\n}\n\n//#endregion\n//#region src/lib/settings.ts\nconst SETTINGS_PATH = \".claude/hooks/bst-hook.settings.json\";\nconst BstHookSettingsSchema = object({ protectedBranches: array(string().min(1)).default([]) });\n/**\n* Check whether a file at `relativePath` inside `cwd` is tracked and\n* unmodified in git (no staged or unstaged changes vs HEAD).\n*\n* Returns false on any error -- safe default is to not trust the file.\n*/\nfunction isFileClean(cwd, relativePath) {\n try {\n execFileSync(\"git\", [\n \"ls-files\",\n \"--error-unmatch\",\n relativePath\n ], {\n cwd,\n stdio: \"ignore\"\n });\n execFileSync(\"git\", [\n \"diff\",\n \"--quiet\",\n \"HEAD\",\n \"--\",\n relativePath\n ], {\n cwd,\n stdio: \"ignore\"\n });\n return true;\n } catch {\n return false;\n }\n}\n/**\n* Load repo-level settings from `.claude/hooks/bst-hook.settings.json`.\n*\n* Walks up from `cwd` to find the workspace root (or uses `cwd` directly),\n* reads the settings file, validates it, and checks that the file is\n* git-tracked and unmodified before trusting it.\n*\n* Returns `{ protectedBranches: [] }` if the file is missing, invalid, or\n* untrusted.\n*/\nfunction loadRepoSettings(cwd) {\n const root = findWorkspaceRoot(cwd) ?? cwd;\n const settingsFile = join(root, SETTINGS_PATH);\n const empty = { protectedBranches: [] };\n if (!existsSync(settingsFile)) return empty;\n if (!isFileClean(root, SETTINGS_PATH)) return empty;\n try {\n const raw = readFileSync(settingsFile, \"utf-8\");\n const parsed = JSON.parse(raw);\n const result = BstHookSettingsSchema.safeParse(parsed);\n if (!result.success) return empty;\n return { protectedBranches: result.data.protectedBranches };\n } catch {\n return empty;\n }\n}\n\n//#endregion\n//#region src/index.ts\nconst inputResult = await readHookInput();\nif (!inputResult.ok) {\n process.stderr.write(`[bst-hook] invalid hook input: ${inputResult.error}\\n`);\n audit({\n sessionId: \"unknown\",\n cwd: \"unknown\",\n command: `<invalid input: ${inputResult.error}>`,\n decision: \"none\",\n rule: null,\n reason: `validation failed: ${inputResult.error}`,\n permissionMode: void 0\n });\n process.exit(0);\n}\nconst input = inputResult.data;\nif (input.hook_event_name !== \"PreToolUse\") process.exit(0);\nif (!(/* @__PURE__ */ new Set([\n \"Bash\",\n \"Read\",\n \"Write\",\n \"Edit\",\n \"Glob\",\n \"Grep\"\n])).has(input.tool_name)) process.exit(0);\nconst repoSettings = loadRepoSettings(input.cwd);\nlet decision;\nlet auditCommand;\nif (input.tool_name === \"Bash\") {\n const bashInput = parseBashInput(input.tool_input);\n if (!bashInput) {\n audit({\n sessionId: input.session_id,\n cwd: input.cwd,\n toolName: input.tool_name,\n command: `<invalid Bash input: ${JSON.stringify(input.tool_input).slice(0, 200)}>`,\n decision: \"none\",\n rule: null,\n reason: \"Bash tool_input missing or invalid 'command' field\",\n permissionMode: input.permission_mode\n });\n process.exit(0);\n }\n decision = evaluateCommand(bashInput.command, {\n cwd: input.cwd,\n depth: 0,\n repoProtectedBranches: repoSettings.protectedBranches\n });\n auditCommand = bashInput.command;\n} else {\n const projectDir = findWorkspaceRoot(input.cwd) ?? process.env[\"CLAUDE_PROJECT_DIR\"] ?? input.cwd;\n decision = evaluateToolUse(input.tool_name, input.tool_input, {\n cwd: projectDir,\n sessionId: input.session_id\n });\n const filePath = String(input.tool_input[\"file_path\"] ?? input.tool_input[\"path\"] ?? \"\");\n auditCommand = `${input.tool_name} ${filePath}`;\n}\naudit({\n sessionId: input.session_id,\n cwd: input.cwd,\n toolName: input.tool_name,\n command: auditCommand,\n decision: decision.action,\n rule: decision.rule,\n reason: decision.reason,\n permissionMode: input.permission_mode\n});\nif (decision.action === \"deny\") writeDeny(decision.reason);\nelse if (decision.advice) writeAdvice(decision.advice);\n\n//#endregion\nexport { };", "bst-hook.mjs"), writeIfChanged(ctx, GUIDE_PATH, "# bst-hook Permission Guide\n\nThis project uses a PreToolUse hook (`bst-hook`) that checks each Bash command\nand file tool call before it runs. The hook only ever blocks a command; it does\nnot approve anything on its own. A command that matches a rule below is denied,\nwith a message that says what to do instead.\n\n## Denied commands\n\nThese are blocked. The hook tells you what to do instead.\n\n### Bash\n\n- **sudo**: Don't use sudo. If you need elevated permissions, ask me to do it manually.\n- **git-force-push**: Use `git push --force-with-lease` instead of `--force`.\n- **git-protected-branch-write**: This would move a protected branch (main, master, the remote's default branch, or one listed in .claude/hooks/bst-hook.settings.json#protectedBranches). Work on a feature branch in a worktree: `git worktree add ../<repo>-<topic> -b feat/<topic>`, then commit and push there. To bring `main` up to date, use `git pull --ff-only`.\n- **chmod-777**: Don't use 777 permissions. Use more restrictive permissions.\n- **node-modules-write**: Don't write into node_modules/. Its files are hardlinks into pnpm's global store, so overwriting one in place (plain `cp`, `>` redirect, `sed -i`, ...) writes through the link and corrupts that package for every project on this machine -- silently, since the directory you wrote into ends up fine. To test a local build as an installed dependency, run it directly (`node packages/<pkg>/dist/bin.mjs <cmd>`), or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`). If you must replace a file in place, `cp --remove-destination` is safe -- it unlinks the target first. Run `pnpm store status` to check whether the store is already damaged.\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **env-file-write**: Don't write to .env files. These contain secrets.\n- **health-baseline-write**: A health ceiling raise travels alone: open a ceiling-only pull request -- only health-baseline.json and .tooling.json changed -- for a human to merge, or split the module. `bst health:check` refuses the raise beside code either way.\n- **health-baseline-reset**: A health ceiling raise travels alone: open a ceiling-only pull request -- only health-baseline.json and .tooling.json changed -- for a human to merge, or split the module. `bst health:check` refuses the raise beside code either way.\n- **npx-redirect**: This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\n- **mise-self-update**: Don't self-update mise. The version is Renovate-managed; ask a human to bump the pin.\n\n### File tools (Read, Write, Edit, Glob, Grep)\n\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **env-file-write**: Don't write to .env files. These contain secrets.\n- **hooks-folder-write**: Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.\n- **git-dir-write**: Don't write into .git/ directly. Its internals (config, hooks, refs) are not plain project files -- a config edit can redirect remotes or push destinations that other rules trust, and a hooks/ script runs automatically on the next commit/push. Use git subcommands instead, or ask the user.\n- **node-modules-write**: Don't write into node_modules/. Its files are hardlinks into pnpm's global store -- editing one in place corrupts that package for every project on this machine. Edit the source package and rebuild, or install into a scratch tree with an isolated store (`pnpm add --store-dir <scratch> <pkg>`).\n- **claude-settings-write**: Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.\n- **health-baseline-write**: A health ceiling raise travels alone: open a ceiling-only pull request -- only health-baseline.json and .tooling.json changed -- for a human to merge, or split the module. `bst health:check` refuses the raise beside code either way.\n\n## How commands are evaluated\n\n- **Pipelines and chains**: every segment is checked; a deny on any segment blocks the whole command.\n- **pnpm scripts**: `pnpm <script>` resolves against `package.json` (including pre/post hooks and `--filter` workspaces) and evaluates the resolved command recursively.\n- **cd propagation**: `cd dir && cmd` evaluates `cmd` relative to the new directory.\n- **Branch switch propagation**: `git switch <branch> && cmd` (or `git checkout <branch>`) evaluates a git write in `cmd` against the branch it switched to, not the one checked out before. This holds only across `&&` and only in the same directory.\n- **Audit log**: all decisions are logged to `.claude/hooks/bst-hook.audit.local.jsonl`.\n\n## Everything else\n\nA command that matches none of the rules above is judged by Claude Code's own\nauto mode, not by this hook.\n", "bst-hook-guide.md")];
10554
10739
  const legacyAllowResult = checkLegacyAllowKey(ctx);
10555
10740
  if (legacyAllowResult) results.push(legacyAllowResult);
10556
10741
  return results;
@@ -12239,9 +12424,14 @@ function checkCadenceBody(config) {
12239
12424
  * The tail keeps a per-module ceiling apart from that shared limit because an exemption
12240
12425
  * suppresses reporting, not measurement: `ceilingsFromDetails` in `codebase-health/collect.ts`
12241
12426
  * records a ceiling for an exempt module too, so the gate still fails when such a hub gains an
12242
- * edge. A reader who reads "exempt" as "unmeasured" files the resulting raise as an
12243
- * unexplained regression, and a remedy list of only split-or-exempt gives that reader nowhere
12244
- * to go.
12427
+ * edge. That is also why adding an exemption is named as a raise here rather than as an escape
12428
+ * from the gate.
12429
+ *
12430
+ * The remedy is one rule, not a list. What it replaces had three branches, and one of them
12431
+ * told the session to adopt the growth with `--update-baseline --reset` in the very pull
12432
+ * request that added the edge -- the move `codebase-health/reference.ts` now refuses, because a
12433
+ * raise that travels with code is a raise nobody reviewed. Splitting stays open on a branch
12434
+ * that carries code; the raise itself waits for a pull request of its own and a human's merge.
12245
12435
  */
12246
12436
  const HEALTH_RULE_HEAD = [
12247
12437
  "# Codebase health",
@@ -12255,13 +12445,9 @@ const HEALTH_RULE_TAIL = [
12255
12445
  "",
12256
12446
  "So never duplicate a type, or fork a helper, to protect a fan budget -- import it. Nor move a module out of its home directory to dodge crowding. The converse holds: turning a type-only import into a value import adds an edge, so a module that already imports another for its types gains a real edge the first time it imports a value from it.",
12257
12447
  "",
12258
- "**An exemption drops a module from one outlier list and nothing else.** Every module over a fan limit keeps a per-module ceiling in `health-baseline.json`, exempt or not, and growing past that ceiling fails the gate.",
12448
+ "**An exemption drops a module from one outlier list and nothing else.** Every module over a fan limit keeps a per-module ceiling in `health-baseline.json`, exempt or not, and growing past that ceiling fails the gate. Adding one is a raise too.",
12259
12449
  "",
12260
- "When a real value edge trips the gate, the remedy depends on the module:",
12261
- "",
12262
- "- No `health.exempt` entry: split the module, or record a justified entry. The justification names what the hub wires by construction, which is what a copy would hide.",
12263
- "- An exempt hub gaining an edge inside its justification's scope: adopt the growth with `bst health:check --update-baseline --reset` in the pull request that adds the edge, and say why in that PR's plan or commit. The justification is the durable record of what the hub may wire; the raise needs no second record when the new edge is already named there.",
12264
- "- An exempt hub gaining an edge outside that scope: split the module, or widen the justification in `.tooling.json` as a reviewed edit of its own. A ceiling raised without either is the thing a reviewer should question.",
12450
+ "**A raise travels alone.** A raise is a higher count or ceiling, a ceiling for a module that has none, a new exemption, or a higher limit. It lands only as a ceiling-only pull request -- one whose diff changes nothing but `health-baseline.json` and `.tooling.json` -- which a human merges. When `bst health:check` refuses, split the module or open that pull request, and stop. Never run `bst health:check --update-baseline --reset` on a branch that carries code.",
12265
12451
  "",
12266
12452
  "Prefer an exemption to raising `fanInLimit` or `fanOutLimit`: an exemption names the hub in `bst health:summary --explain`, while a higher limit silently un-tracks every module beneath it. A per-module ceiling has no such shadow -- it ratchets one module at one count and fails again one edge later."
12267
12453
  ];
@@ -12696,7 +12882,7 @@ function reportFailures(failed, command) {
12696
12882
  function generateMigratePrompt(results, config, detected) {
12697
12883
  const sections = [];
12698
12884
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
12699
- sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.84.4 repo:sync\` on ${timestamp}_\n`);
12885
+ sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.85.0 repo:sync\` on ${timestamp}_\n`);
12700
12886
  const changed = results.filter((r) => r.action === "created" || r.action === "updated" || r.action === "archived");
12701
12887
  if (changed.length > 0) {
12702
12888
  const items = changed.map((r) => {
@@ -21557,7 +21743,7 @@ function serializeBaseline(baseline) {
21557
21743
  //#endregion
21558
21744
  //#region src/commands/coverage-check.ts
21559
21745
  const COVERAGE_SUMMARY_FILE$1 = "coverage/coverage-summary.json";
21560
- const BASELINE_FILE$1 = "coverage-baseline.json";
21746
+ const BASELINE_FILE$2 = "coverage-baseline.json";
21561
21747
  /**
21562
21748
  * A baseline written before coverage.include scoping landed has no `scope` marker. Its
21563
21749
  * totals were computed over only the files a test happened to import, so a drop against
@@ -21565,7 +21751,7 @@ const BASELINE_FILE$1 = "coverage-baseline.json";
21565
21751
  * files -- not necessarily a real regression. Softened to advisory until the one-shot
21566
21752
  * --reset adopts a marked, honest baseline; once marked, normal gating resumes.
21567
21753
  */
21568
- const MIGRATION_NOTICE = `${BASELINE_FILE$1} predates coverage.include scoping (no scope marker). Totals may have shifted because previously-untested files entered the denominator for the first time -- that is not necessarily a regression. This comparison is advisory only until the baseline is migrated: run \`pnpm test:coverage && bst coverage:check --update-baseline --reset\` once to adopt the honest, marked baseline. Normal gating resumes after that.`;
21754
+ const MIGRATION_NOTICE = `${BASELINE_FILE$2} predates coverage.include scoping (no scope marker). Totals may have shifted because previously-untested files entered the denominator for the first time -- that is not necessarily a regression. This comparison is advisory only until the baseline is migrated: run \`pnpm test:coverage && bst coverage:check --update-baseline --reset\` once to adopt the honest, marked baseline. Normal gating resumes after that.`;
21569
21755
  /**
21570
21756
  * Applies `--update-baseline` (and its `--reset` modifier) to an existing baseline
21571
21757
  * comparison. Extracted from `run()` to keep it under the statement-count lint limit.
@@ -21574,7 +21760,7 @@ function applyBaselineUpdate$1(out, baselinePath, baseline, current, options) {
21574
21760
  const { reset, floorPp } = options;
21575
21761
  if (reset) {
21576
21762
  writeFileSync(baselinePath, serializeBaseline(current));
21577
- out.notice(`Updated ${BASELINE_FILE$1} (--reset: unconditional).`);
21763
+ out.notice(`Updated ${BASELINE_FILE$2} (--reset: unconditional).`);
21578
21764
  } else if (floorPp !== void 0) {
21579
21765
  const merged = mergeTotalsWithFloor(baseline.totals, current.totals, floorPp);
21580
21766
  writeFileSync(baselinePath, serializeBaseline({
@@ -21582,10 +21768,10 @@ function applyBaselineUpdate$1(out, baselinePath, baseline, current, options) {
21582
21768
  totals: merged
21583
21769
  }));
21584
21770
  const held = TOTAL_KEYS.filter((key) => merged[key] !== current.totals[key]);
21585
- out.notice(held.length === 0 ? `Updated ${BASELINE_FILE$1} (floor ${String(floorPp)}%).` : `Updated ${BASELINE_FILE$1} (floor ${String(floorPp)}%; held at baseline below the floor: ${held.join(", ")}).`);
21771
+ out.notice(held.length === 0 ? `Updated ${BASELINE_FILE$2} (floor ${String(floorPp)}%).` : `Updated ${BASELINE_FILE$2} (floor ${String(floorPp)}%; held at baseline below the floor: ${held.join(", ")}).`);
21586
21772
  } else if (isFlatOrUp(baseline.totals, current.totals)) {
21587
21773
  writeFileSync(baselinePath, serializeBaseline(current));
21588
- out.notice(`Updated ${BASELINE_FILE$1} (all four totals flat-or-up).`);
21774
+ out.notice(`Updated ${BASELINE_FILE$2} (all four totals flat-or-up).`);
21589
21775
  } else out.notice(`Skipped baseline update: at least one total dropped below baseline. Add tests or adjust coverage.exclude before updating.`);
21590
21776
  }
21591
21777
  const coverageCheckCommand = defineCommand({
@@ -21631,20 +21817,20 @@ const coverageCheckCommand = defineCommand({
21631
21817
  return;
21632
21818
  }
21633
21819
  const summaryPath = path.join(targetDir, COVERAGE_SUMMARY_FILE$1);
21634
- const baselinePath = path.join(targetDir, BASELINE_FILE$1);
21820
+ const baselinePath = path.join(targetDir, BASELINE_FILE$2);
21635
21821
  if (!existsSync(summaryPath)) throw new FatalError(missingSummaryMessage(targetDir));
21636
21822
  const current = deriveBaseline(parseV8Summary(readFileSync(summaryPath, "utf-8")), { rootDir: targetDir });
21637
21823
  if (!existsSync(baselinePath)) {
21638
21824
  if (args["update-baseline"] === true) {
21639
21825
  writeFileSync(baselinePath, serializeBaseline(current));
21640
- out.notice(`Created ${BASELINE_FILE$1} (no prior baseline).`);
21826
+ out.notice(`Created ${BASELINE_FILE$2} (no prior baseline).`);
21641
21827
  out.json({
21642
21828
  status: "baseline-created",
21643
21829
  totals: current.totals
21644
21830
  });
21645
21831
  return;
21646
21832
  }
21647
- const message = `${BASELINE_FILE$1} not found. Run with --update-baseline to create one from the current run.`;
21833
+ const message = `${BASELINE_FILE$2} not found. Run with --update-baseline to create one from the current run.`;
21648
21834
  if (args["fail-on-missing-baseline"] === false) {
21649
21835
  out.notice(message);
21650
21836
  out.json({
@@ -21850,6 +22036,7 @@ function createExcludeMatcher(patterns) {
21850
22036
  //#endregion
21851
22037
  //#region src/codebase-health/depcruise.ts
21852
22038
  const DependencySchema = z.object({
22039
+ module: z.string(),
21853
22040
  resolved: z.string(),
21854
22041
  circular: z.boolean().optional(),
21855
22042
  dependencyTypes: z.array(z.string()).optional(),
@@ -21861,6 +22048,27 @@ const ModuleSchema = z.object({
21861
22048
  });
21862
22049
  const DepCruiseSchema = z.object({ modules: z.array(ModuleSchema) });
21863
22050
  /**
22051
+ * True when a specifier names our own code rather than a published package or a Node builtin:
22052
+ * a relative path, a package `imports` alias (`#src/x`), or a bare specifier that is (or sits
22053
+ * under) one of the workspace's own package names.
22054
+ *
22055
+ * The third branch needs the names because the shape cannot tell them apart: `@fx/a/thing` and
22056
+ * `left-pad/thing` are written identically, and only `pnpm-workspace.yaml` says which one is
22057
+ * ours. This is the classification the cruise's own resolver cannot do for us -- by the time an
22058
+ * edge is `couldNotResolve` there is no resolved path left to inspect.
22059
+ *
22060
+ * A rooted path is deliberately absent. `/vite.svg` is a working import in a Vite app -- the
22061
+ * dev server serves it out of `public/`, and the stock template ships one -- and the resolver
22062
+ * cannot follow it, so counting it would fail `health:check` over an import that works. The
22063
+ * same is true of a bundler alias resolved only in the build config; none of those are ours to
22064
+ * judge, and a false failure here is a wall a session cannot get past without editing code.
22065
+ */
22066
+ function isFirstPartySpecifier(specifier, workspacePackages) {
22067
+ if (specifier.startsWith(".") || specifier.startsWith("#")) return true;
22068
+ for (const name of workspacePackages) if (specifier === name || specifier.startsWith(`${name}/`)) return true;
22069
+ return false;
22070
+ }
22071
+ /**
21864
22072
  * Partition an outlier list into what stays a finding and what a configured
21865
22073
  * exemption suppresses. This runs AFTER the full outlier list is built from
21866
22074
  * the complete graph, which is the load-bearing ordering: an exempt module
@@ -21922,9 +22130,23 @@ function isLocalDep(dep, crossPackage) {
21922
22130
  if (types.includes("local")) return true;
21923
22131
  return crossPackage;
21924
22132
  }
22133
+ /**
22134
+ * Record that `importer` imports `target`, creating the importer set on first sight.
22135
+ *
22136
+ * Extracted because `parseGraphDetails` is over the statement budget with the unresolved-edge
22137
+ * check in it, and this is the one piece of it that is pure bookkeeping.
22138
+ */
22139
+ function addImporter(fanIn, target, importer) {
22140
+ let importers = fanIn.get(target);
22141
+ if (importers === void 0) {
22142
+ importers = /* @__PURE__ */ new Set();
22143
+ fanIn.set(target, importers);
22144
+ }
22145
+ importers.add(importer);
22146
+ }
21925
22147
  /** Parse a dependency-cruiser JSON blob into the offending items behind each graph metric. */
21926
22148
  function parseGraphDetails(raw, opts) {
21927
- const { limits, exempt = {}, isExcluded = () => false, sourceLines = /* @__PURE__ */ new Map(), crossPackage } = opts;
22149
+ const { limits, exempt = {}, isExcluded = () => false, sourceLines = /* @__PURE__ */ new Map(), crossPackage, workspacePackages = /* @__PURE__ */ new Set() } = opts;
21928
22150
  let json;
21929
22151
  try {
21930
22152
  json = JSON.parse(raw);
@@ -21938,12 +22160,17 @@ function parseGraphDetails(raw, opts) {
21938
22160
  const fanOutEdges = /* @__PURE__ */ new Map();
21939
22161
  const fanIn = /* @__PURE__ */ new Map();
21940
22162
  const graphModules = /* @__PURE__ */ new Set();
22163
+ const unresolvedFirstParty = [];
21941
22164
  for (const mod of parsed.data.modules) {
21942
22165
  if (isExcluded(mod.source)) continue;
21943
22166
  graphModules.add(mod.source);
21944
22167
  let localDeps = 0;
21945
22168
  let edges;
21946
22169
  for (const dep of mod.dependencies) {
22170
+ if (dep.couldNotResolve === true && isFirstPartySpecifier(dep.module, workspacePackages)) unresolvedFirstParty.push({
22171
+ importer: mod.source,
22172
+ specifier: dep.module
22173
+ });
21947
22174
  if (!isLocalDep(dep, crossPackage)) continue;
21948
22175
  if (isExcluded(dep.resolved)) continue;
21949
22176
  if (dep.circular === true) cycles.push({
@@ -21953,12 +22180,7 @@ function parseGraphDetails(raw, opts) {
21953
22180
  localDeps++;
21954
22181
  if (edges === void 0) edges = [];
21955
22182
  edges.push(dep.resolved);
21956
- let importers = fanIn.get(dep.resolved);
21957
- if (importers === void 0) {
21958
- importers = /* @__PURE__ */ new Set();
21959
- fanIn.set(dep.resolved, importers);
21960
- }
21961
- importers.add(mod.source);
22183
+ addImporter(fanIn, dep.resolved, mod.source);
21962
22184
  }
21963
22185
  if (localDeps > 0) {
21964
22186
  fanOutCount.set(mod.source, localDeps);
@@ -21986,7 +22208,8 @@ function parseGraphDetails(raw, opts) {
21986
22208
  fanOutExempt: fanOutSplit.exempt,
21987
22209
  fanOutEdges,
21988
22210
  fanInEdges,
21989
- graphModules
22211
+ graphModules,
22212
+ unresolvedFirstParty
21990
22213
  };
21991
22214
  }
21992
22215
  /**
@@ -22149,7 +22372,7 @@ function runDepcruise(executor, config) {
22149
22372
  }
22150
22373
  }
22151
22374
  /** Invoke dependency-cruiser and compute the offending items behind each graph metric. */
22152
- function collectGraphDetails(executor, config, isExcluded = () => false, sourceLines = /* @__PURE__ */ new Map()) {
22375
+ function collectGraphDetails(executor, config, isExcluded = () => false, sourceLines = /* @__PURE__ */ new Map(), workspacePackages = /* @__PURE__ */ new Set()) {
22153
22376
  const { stdout, diagnostic } = runDepcruise(executor, config);
22154
22377
  if (stdout === void 0) return {
22155
22378
  details: void 0,
@@ -22164,10 +22387,142 @@ function collectGraphDetails(executor, config, isExcluded = () => false, sourceL
22164
22387
  exempt: config.exempt,
22165
22388
  isExcluded,
22166
22389
  sourceLines,
22167
- crossPackage: config.crossPackage
22390
+ crossPackage: config.crossPackage,
22391
+ workspacePackages
22168
22392
  }) };
22169
22393
  }
22170
22394
  //#endregion
22395
+ //#region src/codebase-health/workspace-packages.ts
22396
+ /**
22397
+ * The workspace's own package names, so an unresolved bare specifier can be told apart from a
22398
+ * published package. It exists for one caller -- `health:check` reports an unresolved
22399
+ * first-party import as a failure, and `@fx/a/missing` is only distinguishable from
22400
+ * `left-pad/missing` by consulting the workspace definition -- and it is a measurement helper,
22401
+ * not a gate: every unreadable or malformed piece of the workspace description is skipped here
22402
+ * rather than thrown, because failing a repo's health gate because this reader could not parse
22403
+ * a manifest it does not need would be worse than the blindness it cures.
22404
+ */
22405
+ /** `pnpm-workspace.yaml`, narrowed to the one key that lists member globs. */
22406
+ const PnpmWorkspaceSchema = z.object({ packages: z.array(z.string()).optional() });
22407
+ /** The root `package.json`, narrowed to `workspaces` in either of its two published forms. */
22408
+ const RootManifestSchema = z.object({ workspaces: z.union([z.array(z.string()), z.object({ packages: z.array(z.string()) })]).optional() });
22409
+ const PackageNameSchema = z.object({ name: z.string() });
22410
+ /** The glob list in `pnpm-workspace.yaml#packages`; empty when absent or malformed. */
22411
+ function pnpmWorkspaceGlobs(executor) {
22412
+ const raw = executor.readFile("pnpm-workspace.yaml");
22413
+ if (raw === void 0 || raw.trim() === "") return [];
22414
+ let parsed;
22415
+ try {
22416
+ parsed = parse$1(raw);
22417
+ } catch (_error) {
22418
+ return [];
22419
+ }
22420
+ const result = PnpmWorkspaceSchema.safeParse(parsed);
22421
+ return result.success ? result.data.packages ?? [] : [];
22422
+ }
22423
+ /** The glob list in the root `package.json#workspaces`; empty when absent or malformed. */
22424
+ function rootWorkspaceGlobs(executor) {
22425
+ const raw = executor.readFile("package.json");
22426
+ if (raw === void 0 || raw.trim() === "") return [];
22427
+ let parsed;
22428
+ try {
22429
+ parsed = JSON.parse(raw);
22430
+ } catch (_error) {
22431
+ return [];
22432
+ }
22433
+ const result = RootManifestSchema.safeParse(parsed);
22434
+ if (!result.success || result.data.workspaces === void 0) return [];
22435
+ const workspaces = result.data.workspaces;
22436
+ return Array.isArray(workspaces) ? workspaces : workspaces.packages;
22437
+ }
22438
+ /** The directories a `**` walk never descends into -- the same list `readDirRecursive` skips in
22439
+ * `executor.ts`. Without it, a workspace glob of `**` would walk every installed package. */
22440
+ const GLOB_SKIP_DIRS = /* @__PURE__ */ new Set([
22441
+ "node_modules",
22442
+ ".git",
22443
+ "dist"
22444
+ ]);
22445
+ /** The relative path of a child directory; `base` is "" for the project root. */
22446
+ function joinRel(base, name) {
22447
+ return base === "" ? name : `${base}/${name}`;
22448
+ }
22449
+ /** Escape the characters a glob segment may carry that a RegExp would read as syntax. */
22450
+ function escapeRegExp(text) {
22451
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22452
+ }
22453
+ /**
22454
+ * Whether one directory name matches one glob segment. A `*` stands for anything inside a
22455
+ * single segment, so `pkg-*` is a real pattern and not a directory named `pkg-`.
22456
+ */
22457
+ function segmentMatcher(segment) {
22458
+ if (!segment.includes("*")) return (name) => name === segment;
22459
+ const source = segment.split("*").map(escapeRegExp).join("[^/]*");
22460
+ const pattern = new RegExp(`^${source}$`);
22461
+ return (name) => pattern.test(name);
22462
+ }
22463
+ /** Every directory the remaining `segments` name, starting under `base`, added to `matched`. */
22464
+ function walkSegments(executor, segments, base, matched) {
22465
+ const [segment, ...rest] = segments;
22466
+ if (segment === void 0) {
22467
+ if (base !== "" && executor.readDir(base) !== void 0) matched.add(base);
22468
+ return;
22469
+ }
22470
+ const children = executor.readDir(base) ?? [];
22471
+ if (segment === "**") {
22472
+ walkSegments(executor, rest, base, matched);
22473
+ for (const name of children) {
22474
+ if (GLOB_SKIP_DIRS.has(name)) continue;
22475
+ walkSegments(executor, ["**", ...rest], joinRel(base, name), matched);
22476
+ }
22477
+ return;
22478
+ }
22479
+ const matches = segmentMatcher(segment);
22480
+ for (const name of children) if (matches(name)) walkSegments(executor, rest, joinRel(base, name), matched);
22481
+ }
22482
+ /**
22483
+ * The directories one workspace glob names, each as a path from the project root. A `*` matches
22484
+ * inside one path segment (so `packages/*` and `packages/pkg-*` both work); `**` matches zero or
22485
+ * more whole segments (so `packages/**` reaches a package nested below `packages`). A pattern
22486
+ * with no star is a directory named directly, probed once.
22487
+ *
22488
+ * `**` descends everywhere except {@link GLOB_SKIP_DIRS}, which is what keeps a `**` pattern from
22489
+ * walking an installed tree to find nothing.
22490
+ */
22491
+ function expandGlob(executor, pattern) {
22492
+ const trimmed = pattern.replace(/^\.\//, "").replace(/\/+$/, "");
22493
+ if (trimmed === "") return [];
22494
+ if (!trimmed.includes("*")) return executor.readDir(trimmed) === void 0 ? [] : [trimmed];
22495
+ const matched = /* @__PURE__ */ new Set();
22496
+ walkSegments(executor, trimmed.split("/"), "", matched);
22497
+ return [...matched];
22498
+ }
22499
+ /** The `name` a member directory's own manifest declares; undefined when unreadable. */
22500
+ function memberName(executor, dir) {
22501
+ const raw = executor.readFile(`${dir}/package.json`);
22502
+ if (raw === void 0 || raw.trim() === "") return void 0;
22503
+ let parsed;
22504
+ try {
22505
+ parsed = JSON.parse(raw);
22506
+ } catch (_error) {
22507
+ return;
22508
+ }
22509
+ const result = PackageNameSchema.safeParse(parsed);
22510
+ return result.success ? result.data.name : void 0;
22511
+ }
22512
+ /**
22513
+ * Every package name the workspace declares, from `pnpm-workspace.yaml` and the root
22514
+ * `package.json#workspaces`. Duplicates across the two sources collapse into one entry.
22515
+ */
22516
+ function listWorkspacePackageNames(executor) {
22517
+ const globs = [...pnpmWorkspaceGlobs(executor), ...rootWorkspaceGlobs(executor)];
22518
+ const names = /* @__PURE__ */ new Set();
22519
+ for (const glob of globs) for (const dir of expandGlob(executor, glob)) {
22520
+ const name = memberName(executor, dir);
22521
+ if (name !== void 0) names.add(name);
22522
+ }
22523
+ return names;
22524
+ }
22525
+ //#endregion
22171
22526
  //#region src/codebase-health/collect.ts
22172
22527
  /** Build a `CollectConfig` from a resolved health config. The one place `exclude` cannot be omitted. */
22173
22528
  function buildCollectConfig(config, opts) {
@@ -22361,18 +22716,42 @@ function listCrowdedDirectories(files, config) {
22361
22716
  })).toSorted((a, b) => b.count - a.count);
22362
22717
  }
22363
22718
  /**
22364
- * Source lines per file, keyed by repo-relative path. An unreadable file (readFile returns
22365
- * undefined) is left out of the map entirely, the same tolerance readFile's other callers
22719
+ * Raw line count: every line, comments and blanks included, after the same CRLF handling.
22720
+ *
22721
+ * A trailing newline terminates the last line; it is not a line of its own, and `split("\n")`
22722
+ * counts one for it. A file ending in a newline therefore has one fewer line than the split's
22723
+ * length, and an empty file has none -- both of which an uncorrected count inflates by one, and
22724
+ * one is exactly the error that tips a module of exactly `moduleSizeLimit` lines over the limit.
22725
+ */
22726
+ function countRawLines(content) {
22727
+ if (content === "") return 0;
22728
+ const lines = content.split("\n").length;
22729
+ return content.endsWith("\n") ? lines - 1 : lines;
22730
+ }
22731
+ /**
22732
+ * Both line counts per file, keyed by repo-relative path. An unreadable file (readFile returns
22733
+ * undefined) is left out of both maps entirely, the same tolerance readFile's other callers
22366
22734
  * already have.
22735
+ *
22736
+ * Two counts, not one, because two consumers need opposite things. Lines of code is a trend
22737
+ * over the code in the tree, so it counts stripped lines. The fan-in size condition asks
22738
+ * whether a module has surface a reader has to hold, and a comment-heavy hub does -- 300 lines
22739
+ * of documentation above a thin API is a big file to open, and stripping it away measures a
22740
+ * module that does not exist.
22367
22741
  */
22368
22742
  function countLinesByModule(executor, files) {
22369
- const perFile = /* @__PURE__ */ new Map();
22743
+ const stripped = /* @__PURE__ */ new Map();
22744
+ const raw = /* @__PURE__ */ new Map();
22370
22745
  for (const rel of files) {
22371
22746
  const content = executor.readFile(rel);
22372
22747
  if (content === void 0) continue;
22373
- perFile.set(rel, countSourceLines(content));
22748
+ stripped.set(rel, countSourceLines(content));
22749
+ raw.set(rel, countRawLines(content));
22374
22750
  }
22375
- return perFile;
22751
+ return {
22752
+ stripped,
22753
+ raw
22754
+ };
22376
22755
  }
22377
22756
  /**
22378
22757
  * Sum source lines per directory over the per-file counts, highest first. Zero-count
@@ -22405,12 +22784,14 @@ function collectDetails(executor, config) {
22405
22784
  }
22406
22785
  };
22407
22786
  if (config.extensions.length > 0) {
22408
- const perFile = countLinesByModule(executor, files);
22409
- details.sourceLinesByModule = perFile;
22410
- details.linesOfCodeByDirectory = rollUpByDirectory(perFile);
22787
+ const { stripped, raw } = countLinesByModule(executor, files);
22788
+ details.sourceLinesByModule = stripped;
22789
+ details.rawLinesByModule = raw;
22790
+ details.linesOfCodeByDirectory = rollUpByDirectory(stripped);
22411
22791
  }
22412
22792
  if (config.includeGraph) {
22413
- const graph = collectGraphDetails(executor, config, isExcluded, details.sourceLinesByModule);
22793
+ const workspacePackages = listWorkspacePackageNames(executor);
22794
+ const graph = collectGraphDetails(executor, config, isExcluded, details.rawLinesByModule, workspacePackages);
22414
22795
  if (graph.details) {
22415
22796
  details.circularDependencies = graph.details.cycles;
22416
22797
  details.fanInOutliers = graph.details.fanIn;
@@ -22420,6 +22801,7 @@ function collectDetails(executor, config) {
22420
22801
  details.fanOutEdges = graph.details.fanOutEdges;
22421
22802
  details.fanInEdges = graph.details.fanInEdges;
22422
22803
  details.graphModules = graph.details.graphModules;
22804
+ details.unresolvedFirstParty = graph.details.unresolvedFirstParty;
22423
22805
  } else if (graph.diagnostic) details.graphDiagnostic = graph.diagnostic;
22424
22806
  }
22425
22807
  return details;
@@ -22629,6 +23011,223 @@ function createRealHealthExecutor(rootDir) {
22629
23011
  };
22630
23012
  }
22631
23013
  //#endregion
23014
+ //#region src/codebase-health/reference.ts
23015
+ /**
23016
+ * The files a health ceiling raise may travel in, and nothing else. A branch whose diff against
23017
+ * the default branch names only these two carries a raise on its own; a raise riding alongside
23018
+ * code is the gate being widened in the same breath as the code that needed widening, which is
23019
+ * the leak this module exists to close. Exported here rather than repeated at each reader so the
23020
+ * CLI's refusal, the agent's landing hold and the hook's deny cannot drift apart.
23021
+ */
23022
+ const HEALTH_CEILING_FILES = ["health-baseline.json", ".tooling.json"];
23023
+ const BASELINE_FILE$1 = "health-baseline.json";
23024
+ const TOOLING_FILE = ".tooling.json";
23025
+ /**
23026
+ * The target directory's path from the repository root, with its trailing slash; `""` at the root
23027
+ * or whenever git will not answer (`rev-parse` outside a repository), which is the root's own
23028
+ * spelling and the one `readReference`'s merge-base read fails closed on anyway.
23029
+ */
23030
+ function repoPrefix(executor, cwd) {
23031
+ const result = executor.exec("git", ["rev-parse", "--show-prefix"], { cwd });
23032
+ if (result.exitCode !== 0) return "";
23033
+ return result.stdout.trim();
23034
+ }
23035
+ /** The two health files as git names them, for a run whose target directory sits at `prefix`. */
23036
+ function ceilingFileNamesAt(prefix) {
23037
+ return HEALTH_CEILING_FILES.map((name) => `${prefix}${name}`);
23038
+ }
23039
+ /**
23040
+ * `.tooling.json#defaultBranch` read on its own. `utils/tooling-config.ts` is the normal door for
23041
+ * this file and is deliberately not imported here: it sits at its fan-in ceiling, and one optional
23042
+ * string does not warrant an edge onto a hub. An unparsable file is no override -- `repo:sync`
23043
+ * reports that shape, and a reference read must not be the thing that fails on it.
23044
+ */
23045
+ const DefaultBranchSchema = z.object({ defaultBranch: z.string().optional() });
23046
+ function readDefaultBranchOverride(executor) {
23047
+ const raw = executor.readFile(TOOLING_FILE);
23048
+ if (raw === void 0 || raw.trim() === "") return void 0;
23049
+ let json;
23050
+ try {
23051
+ json = JSON.parse(raw);
23052
+ } catch (_error) {
23053
+ return;
23054
+ }
23055
+ const result = DefaultBranchSchema.safeParse(json);
23056
+ return result.success ? result.data.defaultBranch : void 0;
23057
+ }
23058
+ /** `GitRunner` over the health executor, so a mocked executor's `execRules` drive git too. */
23059
+ function gitRunner(executor) {
23060
+ return (args, cwd) => {
23061
+ const result = executor.exec("git", args, { cwd });
23062
+ if (result.exitCode !== 0) return void 0;
23063
+ const trimmed = result.stdout.trim();
23064
+ return trimmed === "" ? void 0 : trimmed;
23065
+ };
23066
+ }
23067
+ /** `git show <sha>:<file>`; undefined when the file is not in that commit or git refused. */
23068
+ function showAt(executor, cwd, sha, file) {
23069
+ const result = executor.exec("git", ["show", `${sha}:${file}`], { cwd });
23070
+ if (result.exitCode !== 0) return void 0;
23071
+ return result.stdout;
23072
+ }
23073
+ /**
23074
+ * Read the two health files at the merge base of `HEAD` with the default branch.
23075
+ *
23076
+ * The merge base, not the previous commit: a branch that has merged `main` in already carries the
23077
+ * reference's own raises, and comparing against its parent would read them as this branch's.
23078
+ *
23079
+ * Fails closed. A missing reference is the wall switched off, and CI is exactly where a shallow
23080
+ * clone or a missing remote-tracking ref can make it missing, so an unreadable reference is an
23081
+ * error naming the fetch that would fix it, never a notice.
23082
+ */
23083
+ function readReference(executor, opts) {
23084
+ const branch = resolveDefaultBranch({
23085
+ cwd: opts.cwd,
23086
+ configOverride: readDefaultBranchOverride(executor),
23087
+ git: gitRunner(executor)
23088
+ });
23089
+ const mergeBase = executor.exec("git", [
23090
+ "merge-base",
23091
+ "HEAD",
23092
+ `origin/${branch}`
23093
+ ], { cwd: opts.cwd });
23094
+ const sha = mergeBase.stdout.trim();
23095
+ if (mergeBase.exitCode !== 0 || sha === "") return {
23096
+ kind: "unavailable",
23097
+ reason: `health: cannot read the reference at the merge base with origin/${branch}. Run \`git fetch origin ${branch}\` so the default branch is present (a shallow clone or a missing remote-tracking ref makes it absent), or set .tooling.json#defaultBranch if this repo's default branch has another name. health:check fails closed without a reference.`
23098
+ };
23099
+ const prefix = repoPrefix(executor, opts.cwd);
23100
+ const baselineText = showAt(executor, opts.cwd, sha, `${prefix}${BASELINE_FILE$1}`);
23101
+ const toolingText = showAt(executor, opts.cwd, sha, `${prefix}${TOOLING_FILE}`);
23102
+ const health = resolveHealthConfigFromContext({
23103
+ exists: (rel) => rel === TOOLING_FILE && toolingText !== void 0,
23104
+ read: (rel) => rel === TOOLING_FILE ? toolingText : void 0,
23105
+ config: { stack: opts.stack }
23106
+ }, { isMonorepo: opts.isMonorepo });
23107
+ return {
23108
+ kind: "read",
23109
+ reference: {
23110
+ mergeBase: sha,
23111
+ prefix,
23112
+ baseline: baselineText === void 0 ? void 0 : parseHealthBaseline(baselineText),
23113
+ health
23114
+ }
23115
+ };
23116
+ }
23117
+ /** Each limit is a raise by another name: widening it widens every module it governs. */
23118
+ const RAISABLE_LIMITS = [
23119
+ "fanInLimit",
23120
+ "fanOutLimit",
23121
+ "directoryFileLimit",
23122
+ "moduleSizeLimit",
23123
+ "tolerance"
23124
+ ];
23125
+ /**
23126
+ * Every way the branch's health files are higher than the reference's, and nothing else.
23127
+ *
23128
+ * Deliberately one-directional: a lower count, a dropped ceiling, a removed exemption or a lower
23129
+ * limit is never a raise, because `--update-baseline` adopts those and the gate must stay quiet
23130
+ * while the numbers fall. Deleting the baseline entirely *is* a raise -- it is the gate switched
23131
+ * off, which costs the same as widening it and is easier to slip into a code-only branch.
23132
+ */
23133
+ function findRaises(reference, current) {
23134
+ const raises = [];
23135
+ for (const key of METRIC_KEYS) {
23136
+ const base = reference.baseline?.[key];
23137
+ const now = current.baseline?.[key];
23138
+ if (base !== void 0 && now !== void 0 && now > base) raises.push({
23139
+ leaf: key,
23140
+ reference: String(base),
23141
+ current: String(now)
23142
+ });
23143
+ }
23144
+ for (const key of RATCHETED_METRIC_KEYS) {
23145
+ const recorded = current.baseline?.moduleCeilings?.[key];
23146
+ if (recorded === void 0) continue;
23147
+ for (const [module, value] of Object.entries(recorded)) {
23148
+ const leaf = `moduleCeilings.${key}.${module}`;
23149
+ const base = reference.baseline?.moduleCeilings?.[key]?.[module];
23150
+ if (base === void 0) raises.push({
23151
+ leaf,
23152
+ reference: "absent",
23153
+ current: String(value)
23154
+ });
23155
+ else if (value > base) raises.push({
23156
+ leaf,
23157
+ reference: String(base),
23158
+ current: String(value)
23159
+ });
23160
+ }
23161
+ }
23162
+ for (const key of EXEMPTABLE_METRIC_KEYS) {
23163
+ const exemptions = current.health.exempt[key];
23164
+ if (exemptions === void 0) continue;
23165
+ for (const module of Object.keys(exemptions)) if (reference.health.exempt[key]?.[module] === void 0) raises.push({
23166
+ leaf: `health.exempt.${key}.${module}`,
23167
+ reference: "absent",
23168
+ current: "present"
23169
+ });
23170
+ }
23171
+ for (const name of RAISABLE_LIMITS) {
23172
+ const base = reference.health[name];
23173
+ const now = current.health[name];
23174
+ if (now > base) raises.push({
23175
+ leaf: `health.${name}`,
23176
+ reference: String(base),
23177
+ current: String(now)
23178
+ });
23179
+ }
23180
+ if (reference.baseline !== void 0 && current.baseline === void 0) raises.push({
23181
+ leaf: BASELINE_FILE$1,
23182
+ reference: "present",
23183
+ current: "absent"
23184
+ });
23185
+ return raises;
23186
+ }
23187
+ /**
23188
+ * The files this branch carries, each named from the repository root: everything changed against
23189
+ * the merge base, plus the untracked files, which are part of what the branch adds and are
23190
+ * invisible to `git diff` until staged. Deduplicated and sorted so the list is stable for both
23191
+ * the refusal's text and the comparison.
23192
+ *
23193
+ * `--full-name` is load-bearing, not cosmetic: `git diff --name-only` already answers from the
23194
+ * repository root while `git ls-files` answers from its working directory, so a subdirectory run
23195
+ * without it would mix two spellings of the same tree -- and the ceiling-only comparison below
23196
+ * reads these names against root-relative ones.
23197
+ */
23198
+ function branchDiffNames(executor, cwd, mergeBase) {
23199
+ const tracked = executor.exec("git", [
23200
+ "diff",
23201
+ "--name-only",
23202
+ mergeBase
23203
+ ], { cwd });
23204
+ const untracked = executor.exec("git", [
23205
+ "ls-files",
23206
+ "--others",
23207
+ "--exclude-standard",
23208
+ "--full-name"
23209
+ ], { cwd });
23210
+ const names = /* @__PURE__ */ new Set();
23211
+ for (const output of [tracked, untracked]) for (const line of output.stdout.split("\n")) {
23212
+ const name = line.trim();
23213
+ if (name !== "") names.add(name);
23214
+ }
23215
+ return [...names].toSorted();
23216
+ }
23217
+ /**
23218
+ * True when the branch's diff is non-empty and names nothing but the two health files.
23219
+ *
23220
+ * `prefix` is {@link HealthReference.prefix}: the names arrive from the repository root, so a
23221
+ * `health:check packages/x` run compares them against `packages/x/health-baseline.json` and
23222
+ * `packages/x/.tooling.json`, not the bare names. The default is the root, where the two
23223
+ * spellings are the same.
23224
+ */
23225
+ function isCeilingOnly(names, prefix = "") {
23226
+ if (names.length === 0) return false;
23227
+ const files = ceilingFileNamesAt(prefix);
23228
+ return names.every((name) => files.includes(name));
23229
+ }
23230
+ //#endregion
22632
23231
  //#region src/codebase-health/render.ts
22633
23232
  /** The three graph-derived metric keys, in `METRIC_KEYS` order. */
22634
23233
  const GRAPH_METRIC_KEYS = METRIC_KEYS.filter((key) => key !== "crowdedDirectories");
@@ -22842,6 +23441,42 @@ function formatSourceDiagnosis(sources, graphMetricsEnabled) {
22842
23441
  ].filter((part) => part.length > 0).join(" ")
22843
23442
  };
22844
23443
  }
23444
+ /**
23445
+ * The lines naming every first-party import dependency-cruiser could not resolve.
23446
+ *
23447
+ * Reported as a failure and not as a smaller set of counts because an unresolved edge is not
23448
+ * measured at all: it leaves the graph, so `fanOutOutliers` and `fanInOutliers` both drop, and a
23449
+ * repo can "improve" its health numbers by breaking an import. The one sentence says which of
23450
+ * the two fixes is wanted -- the statement is wrong, or the resolver that was asked to follow it
23451
+ * is configured wrong -- because the bare list reads as a licence to delete the line.
23452
+ */
23453
+ function formatUnresolvedFirstParty(entries) {
23454
+ const lines = entries.map((e) => ` ${e.importer} -> ${e.specifier}`);
23455
+ lines.push(` An unresolved first-party import leaves the dependency graph, improving every health`, ` metric. Fix the import, or the resolve options that were supposed to follow it.`);
23456
+ return lines.join("\n");
23457
+ }
23458
+ /** How many of the branch's other files the refusal lists before a "+N more" line. */
23459
+ const REFUSAL_FILE_CAP = 8;
23460
+ /**
23461
+ * The refusal `health:check` prints when a raise travels beside code.
23462
+ *
23463
+ * It names each raised leaf with both values, because "which number moved" is the whole question
23464
+ * a session asks, and then the remedy -- split the module, or move the raise into a pull request
23465
+ * that carries nothing but the two health files. The two file names are written into the sentence
23466
+ * rather than composed from `HEALTH_CEILING_FILES`: this is the prose a human reads, while the
23467
+ * rule that reads the list lives in `reference.ts`.
23468
+ */
23469
+ function formatRaiseRefusal(raises, otherFiles) {
23470
+ const lines = [`health: refused -- a health ceiling raise cannot travel with code.`, ...raises.map((r) => ` ${r.leaf}: ${r.reference} -> ${r.current}`)];
23471
+ const shown = otherFiles.slice(0, REFUSAL_FILE_CAP);
23472
+ if (shown.length > 0) {
23473
+ const more = otherFiles.length - shown.length;
23474
+ const suffix = more > 0 ? ` (+${String(more)} more)` : "";
23475
+ lines.push(` this branch also changes: ${shown.join(", ")}${suffix}`);
23476
+ }
23477
+ lines.push(` Split the module so the graph does not grow, or open a ceiling-only pull request --`, ` only health-baseline.json and .tooling.json changed -- for a human to merge.`);
23478
+ return lines.join("\n");
23479
+ }
22845
23480
  //#endregion
22846
23481
  //#region src/codebase-health/staleness.ts
22847
23482
  /**
@@ -22899,6 +23534,75 @@ function findStaleEntries(baseline, details, exempt) {
22899
23534
  //#region src/commands/health-check.ts
22900
23535
  const BASELINE_FILE = "health-baseline.json";
22901
23536
  /**
23537
+ * The reference comparison: read the two health files at the merge base with the default branch,
23538
+ * list every way the working tree's are higher, and refuse the run when the branch carries code
23539
+ * beside them. Fails closed on an unreadable reference -- see `reference.ts`.
23540
+ *
23541
+ * The refusal judges the baseline **as this run found it**, before any write it makes, so
23542
+ * `--update-baseline --reset` still rewrites and is judged on the next run.
23543
+ */
23544
+ function assessRaises(executor, targetDir, config, treeBaseline, out) {
23545
+ const context = detectHealthContext(targetDir);
23546
+ const read = readReference(executor, {
23547
+ cwd: targetDir,
23548
+ stack: context.stack,
23549
+ isMonorepo: context.isMonorepo
23550
+ });
23551
+ if (read.kind === "unavailable") throw new FatalError(read.reason);
23552
+ const reference = read.reference;
23553
+ if (reference.baseline === void 0) {
23554
+ out.notice(`${BASELINE_FILE} has no counterpart on the default branch yet, so there is nothing to raise against.`);
23555
+ return {
23556
+ reference: {
23557
+ status: "no-baseline",
23558
+ mergeBase: reference.mergeBase
23559
+ },
23560
+ raises: [],
23561
+ raiseRefused: false
23562
+ };
23563
+ }
23564
+ const raises = findRaises(reference, {
23565
+ baseline: treeBaseline,
23566
+ health: config
23567
+ });
23568
+ if (raises.length === 0) return {
23569
+ reference: {
23570
+ status: "read",
23571
+ mergeBase: reference.mergeBase
23572
+ },
23573
+ raises,
23574
+ raiseRefused: false
23575
+ };
23576
+ const names = branchDiffNames(executor, targetDir, reference.mergeBase);
23577
+ const raiseRefused = !isCeilingOnly(names, reference.prefix);
23578
+ if (raiseRefused) {
23579
+ const ceilingFiles = ceilingFileNamesAt(reference.prefix);
23580
+ const otherFiles = names.filter((name) => !ceilingFiles.includes(name));
23581
+ out.text(formatRaiseRefusal(raises, otherFiles));
23582
+ process.exitCode = 1;
23583
+ }
23584
+ return {
23585
+ reference: {
23586
+ status: "read",
23587
+ mergeBase: reference.mergeBase
23588
+ },
23589
+ raises,
23590
+ raiseRefused
23591
+ };
23592
+ }
23593
+ /**
23594
+ * Refuse a run whose cruise could not follow an import of the repo's own code.
23595
+ *
23596
+ * Ahead of the source diagnosis and of every baseline read or write: an unresolved first-party
23597
+ * import leaves the graph, so every graph count this run measured is smaller than the truth and
23598
+ * there is nothing to compare, create, or update a baseline against. Thrown rather than noticed
23599
+ * because `--update-baseline --reset` would otherwise adopt that shrunken graph as the new floor.
23600
+ */
23601
+ function refuseUnresolvedImports(details) {
23602
+ const entries = details.unresolvedFirstParty;
23603
+ if (entries !== void 0 && entries.length > 0) throw new FatalError(formatUnresolvedFirstParty(entries));
23604
+ }
23605
+ /**
22902
23606
  * `run`'s branch for a repo with no baseline yet: create one under `--update-baseline`, else
22903
23607
  * notice or fail per `--fail-on-missing-baseline`. Every path here returns or throws, so `run`
22904
23608
  * calls this and returns right after. Extracted to keep `run` under the lint's statement budget.
@@ -22910,6 +23614,7 @@ function handleMissingBaseline(state, updateBaseline, failOnMissingBaseline) {
22910
23614
  out.notice(`Skipped baseline creation: graph metrics are unavailable this run. Fix the cruise before creating ${BASELINE_FILE}.`);
22911
23615
  out.json({
22912
23616
  status: "graph-unavailable",
23617
+ ...state.referenceFields,
22913
23618
  metrics: current,
22914
23619
  sources: details.sources
22915
23620
  });
@@ -22920,6 +23625,7 @@ function handleMissingBaseline(state, updateBaseline, failOnMissingBaseline) {
22920
23625
  out.notice(`Created ${BASELINE_FILE} (no prior baseline).`);
22921
23626
  out.json({
22922
23627
  status: "baseline-created",
23628
+ ...state.referenceFields,
22923
23629
  metrics: current,
22924
23630
  sources: details.sources
22925
23631
  });
@@ -22930,6 +23636,7 @@ function handleMissingBaseline(state, updateBaseline, failOnMissingBaseline) {
22930
23636
  out.notice(message);
22931
23637
  out.json({
22932
23638
  status: "missing-baseline",
23639
+ ...state.referenceFields,
22933
23640
  metrics: current,
22934
23641
  sources: details.sources
22935
23642
  });
@@ -22987,7 +23694,7 @@ const healthCheckCommand = defineCommand({
22987
23694
  },
22988
23695
  json: {
22989
23696
  type: "boolean",
22990
- description: "Emit one JSON document: status (compared | disabled | baseline-created | missing-baseline | graph-unavailable). disabled carries nothing else. baseline-created, missing-baseline and graph-unavailable carry metrics ({crowdedDirectories, circularDependencies?, fanInOutliers?, fanOutOutliers?}) and sources. compared carries pass, tolerance, metrics (a list: [{key (crowdedDirectories | circularDependencies | fanInOutliers | fanOutOutliers), baseline?, current?, delta?, pass}]), ceilings ([{key (fanInOutliers | fanOutOutliers | crowdedDirectories), module, baseline, current, pass}]), graphMetricsEnabled, graphMetricsMissing, staleEntries ([{kind (ceiling | exemption), metric, path, count?}]), regressions ({<failing metric key>: [{module, count} | {from, to}]}) and sources. sources is {entries ([{pattern, roots, status (resolved | unmatched | unrepresentable)}]), roots, fileCount}."
23697
+ description: "Emit one JSON document: status (compared | disabled | baseline-created | missing-baseline | graph-unavailable). disabled carries nothing else. Every other status carries reference ({status (read | no-baseline), mergeBase}), raises ([{leaf, reference, current}]) and raiseRefused (whether the branch carries anything besides the two health files beside a raise; it is the refusal, and a raise with raiseRefused true still reports the comparison below). baseline-created, missing-baseline and graph-unavailable carry metrics ({crowdedDirectories, circularDependencies?, fanInOutliers?, fanOutOutliers?}) and sources. compared carries pass, tolerance, metrics (a list: [{key (crowdedDirectories | circularDependencies | fanInOutliers | fanOutOutliers), baseline?, current?, delta?, pass}]), ceilings ([{key (fanInOutliers | fanOutOutliers | crowdedDirectories), module, baseline, current, pass}]), graphMetricsEnabled, graphMetricsMissing, staleEntries ([{kind (ceiling | exemption), metric, path, count?}]), regressions ({<failing metric key>: [{module, count} | {from, to}]}) and sources. sources is {entries ([{pattern, roots, status (resolved | unmatched | unrepresentable)}]), roots, fileCount}."
22991
23698
  }
22992
23699
  },
22993
23700
  run({ args }) {
@@ -23000,27 +23707,32 @@ const healthCheckCommand = defineCommand({
23000
23707
  return;
23001
23708
  }
23002
23709
  if (!config.graphMetricsEnabled) out.notice("health: graph metrics disabled (no JS/TS source to cruise) -- crowdedDirectories only.");
23003
- const details = collectDetails(createRealHealthExecutor(targetDir), buildCollectConfig(config, { tsConfig: existsSync(path.join(targetDir, "tsconfig.json")) ? "tsconfig.json" : void 0 }));
23710
+ const executor = createRealHealthExecutor(targetDir);
23711
+ const details = collectDetails(executor, buildCollectConfig(config, { tsConfig: existsSync(path.join(targetDir, "tsconfig.json")) ? "tsconfig.json" : void 0 }));
23004
23712
  const current = metricsFromDetails(details);
23005
23713
  const ceilings = ceilingsFromDetails(details);
23714
+ refuseUnresolvedImports(details);
23006
23715
  const { warnings, emptyScope } = formatSourceDiagnosis(details.sources, config.graphMetricsEnabled);
23007
23716
  for (const warning of warnings) out.notice(warning);
23008
23717
  if (emptyScope !== void 0 && args["fail-on-empty-src"] !== false) throw new FatalError(emptyScope);
23009
23718
  if (emptyScope !== void 0) out.notice(emptyScope);
23010
23719
  const baselinePath = path.join(targetDir, BASELINE_FILE);
23720
+ const treeBaseline = existsSync(baselinePath) ? parseHealthBaseline(readFileSync(baselinePath, "utf-8")) : void 0;
23721
+ const referenceFields = assessRaises(executor, targetDir, config, treeBaseline, out);
23011
23722
  const state = {
23012
23723
  baselinePath,
23013
23724
  config,
23014
23725
  details,
23015
23726
  current,
23016
23727
  ceilings,
23017
- out
23728
+ out,
23729
+ referenceFields
23018
23730
  };
23019
- if (!existsSync(baselinePath)) {
23731
+ if (treeBaseline === void 0) {
23020
23732
  handleMissingBaseline(state, args["update-baseline"], args["fail-on-missing-baseline"]);
23021
23733
  return;
23022
23734
  }
23023
- const baseline = parseHealthBaseline(readFileSync(baselinePath, "utf-8"));
23735
+ const baseline = treeBaseline;
23024
23736
  const result = compareMetrics(baseline, current, config.tolerance, ceilings);
23025
23737
  out.text(formatCompareDiff(result, details));
23026
23738
  const regressions = {};
@@ -23031,6 +23743,7 @@ const healthCheckCommand = defineCommand({
23031
23743
  for (const warning of formatStaleEntries(stale)) out.notice(warning);
23032
23744
  out.json({
23033
23745
  status: "compared",
23746
+ ...referenceFields,
23034
23747
  ...result,
23035
23748
  graphMetricsEnabled: config.graphMetricsEnabled,
23036
23749
  graphMetricsMissing: graphUnavailable !== void 0,
@@ -23180,7 +23893,7 @@ const healthSummaryCommand = defineCommand({
23180
23893
  },
23181
23894
  json: {
23182
23895
  type: "boolean",
23183
- description: "Emit one JSON document: crowdedDirectories, circularDependencies?, fanInOutliers?, fanOutOutliers?, sources ({entries, roots, fileCount}), linesOfCode?, graphMetricsEnabled, graphMetricsMissing. The four metrics are counts; with --explain they are offender lists instead (fanInOutliers as [{module, count, lines?}], crowdedDirectories and fanOutOutliers as [{module, count}], circularDependencies as [{from, to}]), joined by fanInExempt? and fanOutExempt? ([{module, count, reason}]), linesOfCodeByDirectory? ([{module, count}]) and, with --module, modules ([{module, status, fanOut?, fanIn?, ceilings?, exempt?}]). When health is disabled the document is only enabled (false)."
23896
+ description: "Emit one JSON document: crowdedDirectories, circularDependencies?, fanInOutliers?, fanOutOutliers?, sources ({entries, roots, fileCount}), linesOfCode?, graphMetricsEnabled, graphMetricsMissing. The four metrics are counts; with --explain they are offender lists instead (fanInOutliers as [{module, count, lines?}], crowdedDirectories and fanOutOutliers as [{module, count}], circularDependencies as [{from, to}]), joined by fanInExempt? and fanOutExempt? ([{module, count, reason}]), linesOfCodeByDirectory? ([{module, count}]), unresolvedFirstParty? ([{importer, specifier}], the first-party imports this run's cruise could not resolve) and, with --module, modules ([{module, status, fanOut?, fanIn?, ceilings?, exempt?}]). When health is disabled the document is only enabled (false)."
23184
23897
  },
23185
23898
  explain: {
23186
23899
  type: "boolean",
@@ -23203,6 +23916,7 @@ const healthSummaryCommand = defineCommand({
23203
23916
  return;
23204
23917
  }
23205
23918
  const details = collectDetails(createRealHealthExecutor(targetDir), buildCollectConfig(config, { tsConfig: existsSync(path.join(targetDir, "tsconfig.json")) ? "tsconfig.json" : void 0 }));
23919
+ if (details.unresolvedFirstParty !== void 0 && details.unresolvedFirstParty.length > 0) out.notice(formatUnresolvedFirstParty(details.unresolvedFirstParty));
23206
23920
  const { warnings, emptyScope } = formatSourceDiagnosis(details.sources, config.graphMetricsEnabled);
23207
23921
  for (const warning of warnings) out.notice(warning);
23208
23922
  if (emptyScope !== void 0) out.notice(emptyScope);
@@ -23221,7 +23935,7 @@ const healthSummaryCommand = defineCommand({
23221
23935
  probe: createFsProbe(targetDir)
23222
23936
  });
23223
23937
  if (args.json === true) {
23224
- const { sourceLinesByModule: _sourceLinesByModule, fanOutEdges: _fanOutEdges, fanInEdges: _fanInEdges, graphModules: _graphModules, directories: _directories, graphDiagnostic: _graphDiagnostic, ...explainable } = details;
23938
+ const { sourceLinesByModule: _sourceLinesByModule, rawLinesByModule: _rawLinesByModule, fanOutEdges: _fanOutEdges, fanInEdges: _fanInEdges, graphModules: _graphModules, directories: _directories, graphDiagnostic: _graphDiagnostic, ...explainable } = details;
23225
23939
  out.json({
23226
23940
  ...args.explain === true ? {
23227
23941
  ...explainable,
@@ -31269,7 +31983,7 @@ setForgejoLogger(log$1);
31269
31983
  * `local <sha>[-dirty]` otherwise -- so an artifact built from the working tree
31270
31984
  * can't pass itself off as the published release whose version it carries.
31271
31985
  */
31272
- const displayVersion = "0.84.4";
31986
+ const displayVersion = "0.85.0";
31273
31987
  /**
31274
31988
  * The registered `bst` subcommand map, exported so both the JSON entry path below (resolving a
31275
31989
  * command name before citty parses anything) and the coverage test (enumerating every