@r_masseater/msch 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { $ as isSatisfiesExpression, A as API, B as isCallExpression, C as MANIFEST_FILE, D as workspaceDependencies, E as readManifest, F as getLeadingCommentRanges, G as isInterfaceDeclaration, H as isElementAccessExpression, I as getNameOfDeclaration, J as isNewExpression, K as isModuleBlock, L as getTextOfJSDocComment, M as InternalSymbolName, N as SyntaxKind, O as RepositoryFiles, P as getJSDocTags, Q as isPropertyAssignment, R as isArrayLiteralExpression, S as uniqueByReference, T as dependencyEntries, U as isEnumDeclaration, V as isClassDeclaration, W as isIdentifier, X as isObjectLiteralExpression, Y as isNumericLiteral, Z as isPropertyAccessExpression, _ as lineOf, a as problemAt, at as isTypeLiteralNode, b as resolvedSymbol, c as childrenOf, ct as isVariableDeclaration, d as declarationsOf, et as isSignatureDeclaration, f as descendantsOf, g as isTypeDeclaration, h as isGenericDeclaration, i as formatProblem, it as isTypeAliasDeclaration, j as IndexKind, k as nodeRepositoryFiles, l as columnOf, lt as isVariableDeclarationList, m as isExternalDeclaration, n as ProblemList, nt as isSpreadElement, o as sortProblems, ot as isTypeReferenceNode, p as importedModulesOf, q as isModuleDeclaration, rt as isStringLiteral, s as NODE_MODULES_SEGMENT, st as isUnionTypeNode, tt as isSourceFile, u as contains, ut as isVariableStatement, v as propertyNamesOf, w as NODE_MODULES, x as symbolAt, y as referencedSymbols, z as isBinaryExpression } from "./cli-contract-CkYucdU4.mjs";
3
- import { Array, Cause, Effect, FileSystem, Function, Layer, Number as Number$1, Option, Order, Path, Record, Schema, Stdio, Stream, String as String$1, pipe } from "effect";
4
- import { layer } from "@effect/platform-node/NodeServices";
2
+ import { $ as isObjectLiteralExpression, A as nodeServicesLayer, B as isBinaryExpression, C as MANIFEST_FILE, D as workspaceDependencies, E as readManifest, F as getJSDocTags, G as isExportDeclaration, H as isClassDeclaration, I as getLeadingCommentRanges, J as isInterfaceDeclaration, K as isIdentifier, L as getNameOfDeclaration, M as IndexKind, N as InternalSymbolName, O as RepositoryFiles, P as SyntaxKind, Q as isNumericLiteral, R as getTextOfJSDocComment, S as uniqueByReference, T as dependencyEntries, U as isElementAccessExpression, V as isCallExpression, W as isEnumDeclaration, X as isModuleDeclaration, Y as isModuleBlock, Z as isNewExpression, _ as lineOf, a as problemAt, at as isSpreadElement, b as resolvedSymbol, c as childrenOf$1, ct as isTypeLiteralNode, d as declarationsOf, dt as isVariableDeclaration, et as isPropertyAccessExpression, f as descendantsOf, ft as isVariableDeclarationList, g as isTypeDeclaration, h as isGenericDeclaration, i as formatProblem, it as isSourceFile, j as API, k as nodeRepositoryFiles, l as columnOf, lt as isTypeReferenceNode, m as isExternalDeclaration, n as ProblemList, nt as isSatisfiesExpression, o as sortProblems, ot as isStringLiteral, p as importedModulesOf, pt as isVariableStatement, q as isImportDeclaration, rt as isSignatureDeclaration, s as NODE_MODULES_SEGMENT, st as isTypeAliasDeclaration, tt as isPropertyAssignment, u as contains, ut as isUnionTypeNode, v as propertyNamesOf, w as NODE_MODULES, x as symbolAt, y as referencedSymbols, z as isArrayLiteralExpression } from "./cli-contract-DpQurUwC.mjs";
3
+ import { Array, Cause, Effect, FileSystem, Function, HashMap, HashSet, Layer, Number as Number$1, Option, Order, Path, Predicate, Record, Result, Schema, Stdio, Stream, String as String$1, pipe } from "effect";
5
4
  import { ChildProcess } from "effect/process";
6
5
  import { resolveConfigFromFile } from "vite-plus";
7
6
  import { LineCounter, isMap, isScalar, parseAllDocuments, parseDocument } from "yaml";
8
- import { runMain } from "@effect/platform-node/NodeRuntime";
7
+ import { runMain } from "@effect/platform-node-shared/NodeRuntime";
9
8
  import { FetchHttpClient } from "effect/http";
10
9
  import { Otlp, OtlpSerialization } from "effect/observability";
11
10
  // src/slots.ts
@@ -87,7 +86,7 @@ const valueText = (value) => {
87
86
  const formatValues = (values) => pipe(values, Array.map(valueText), Array.join(" | "));
88
87
  // src/flows.ts
89
88
  const SCOPE_PREFIX$1 = "@";
90
- const SCOPED_SEGMENTS = 2;
89
+ const SCOPED_SEGMENTS$1 = 2;
91
90
  const UNSCOPED_SEGMENTS = 1;
92
91
  const MODULE_PREFIX = /^".*?"\./u;
93
92
  const packageOf$1 = (fileName) => {
@@ -96,7 +95,7 @@ const packageOf$1 = (fileName) => {
96
95
  onSome: (index) => String$1.slice(index + NODE_MODULES_SEGMENT.length)(fileName)
97
96
  }));
98
97
  const segments = String$1.split("/")(afterModules);
99
- if (String$1.startsWith(SCOPE_PREFIX$1)(afterModules)) return pipe(Array.take(segments, SCOPED_SEGMENTS), Array.join("/"));
98
+ if (String$1.startsWith(SCOPE_PREFIX$1)(afterModules)) return pipe(Array.take(segments, SCOPED_SEGMENTS$1), Array.join("/"));
100
99
  return pipe(Array.take(segments, UNSCOPED_SEGMENTS), Array.join("/"));
101
100
  };
102
101
  const externalDeclarationOf = (context) => (symbol) => Array.findFirst(declarationsOf(symbol), (declaration) => isExternalDeclaration(context.program)(declaration));
@@ -457,7 +456,7 @@ const derivationScopeOf = (node) => {
457
456
  };
458
457
  const sourcesOf = (checker) => (node) => pipe(referencedSymbols(checker)(derivationScopeOf(node)), Array.filter((symbol) => Option.isSome(namedValuesOf(checker)(symbol))));
459
458
  const sitesUnder = (checker) => (node) => {
460
- const nested = pipe(childrenOf(node), Array.flatMap((child) => sitesUnder(checker)(child)));
459
+ const nested = pipe(childrenOf$1(node), Array.flatMap((child) => sitesUnder(checker)(child)));
461
460
  if (Array.isReadonlyArrayNonEmpty(nested)) return nested;
462
461
  return siteValuesOf(checker)(node).pipe(Option.map((values) => ({
463
462
  node,
@@ -796,10 +795,10 @@ const checkRepository = Effect.fn("checkRepository")(function* checkRepository(r
796
795
  ]);
797
796
  }, Effect.scoped);
798
797
  // src/text-position.ts
799
- const START$1 = 0;
798
+ const START$2 = 0;
800
799
  const FIRST_COLUMN$1 = 1;
801
800
  const positionOf = (source) => (index) => {
802
- const before = pipe(source, String$1.slice(START$1, index), String$1.split("\n"));
801
+ const before = pipe(source, String$1.slice(START$2, index), String$1.split("\n"));
803
802
  return {
804
803
  line: before.length,
805
804
  column: pipe(Array.last(before), Option.match({
@@ -811,17 +810,17 @@ const positionOf = (source) => (index) => {
811
810
  // src/hidden-unicode.ts
812
811
  const HIDDEN = /[\u00AD\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]|[\u{E0000}-\u{E007F}]/gu;
813
812
  const NUL = "\0";
814
- const START = 0;
813
+ const START$1 = 0;
815
814
  const HEX = 16;
816
815
  const CODE_POINT_WIDTH = 4;
817
- const READ_CONCURRENCY$2 = 64;
816
+ const READ_CONCURRENCY$3 = 64;
818
817
  const RULE$1 = "hidden-unicode";
819
- const hexOf = (character) => pipe(String$1.codePointAt(START)(character), Option.getOrElse(() => START), (code) => code.toString(HEX), String$1.toUpperCase, String$1.padStart(CODE_POINT_WIDTH, "0"));
818
+ const hexOf = (character) => pipe(String$1.codePointAt(START$1)(character), Option.getOrElse(() => START$1), (code) => code.toString(HEX), String$1.toUpperCase, String$1.padStart(CODE_POINT_WIDTH, "0"));
820
819
  const problemOf = (file, source) => (found) => {
821
- const hex = hexOf(found[START]);
820
+ const hex = hexOf(found[START$1]);
822
821
  return {
823
822
  file,
824
- ...positionOf(source)(found.index ?? START),
823
+ ...positionOf(source)(found.index ?? START$1),
825
824
  rule: RULE$1,
826
825
  message: `U+${hex} is invisible in editors and reviews and can change what the text means. Remove it, or write it as the escape \\u${hex}.`
827
826
  };
@@ -840,32 +839,447 @@ const checkHiddenUnicode = Effect.fn("checkHiddenUnicode")(function* checkHidden
840
839
  onNone: () => [],
841
840
  onSome: (source) => problemsIn(file, source)
842
841
  })));
843
- }, { concurrency: READ_CONCURRENCY$2 });
842
+ }, { concurrency: READ_CONCURRENCY$3 });
844
843
  return Array.flatten(found);
845
844
  });
846
- // src/nested-docs.ts
847
- const RULE = "nested-docs";
848
- const SEPARATOR = "/";
845
+ // src/import-graph.ts
846
+ const RELATIVE = /^\.\.?(?:\/|$)/u;
847
+ const SUBPATH_IMPORT = "#";
848
+ const WILDCARD = "*";
849
+ const SCOPE = "@";
850
+ const SEPARATOR$3 = "/";
851
+ const SOURCE_SUFFIXES = [
852
+ "",
853
+ ".ts",
854
+ ".tsx",
855
+ "/index.ts"
856
+ ];
857
+ const SCOPED_SEGMENTS = 2;
858
+ const PLAIN_SEGMENTS = 1;
859
+ const ImportsField = Schema.Struct({ imports: Schema.optional(Schema.Record(Schema.String, Schema.String)) });
860
+ const decodeImports = Schema.decodeOption(Schema.fromJsonString(ImportsField));
861
+ const packageNameOf = (specifier) => {
862
+ const segments = String$1.split(specifier, SEPARATOR$3);
863
+ if (String$1.startsWith(SCOPE)(specifier)) return pipe(segments, Array.take(SCOPED_SEGMENTS), Array.join(SEPARATOR$3));
864
+ return pipe(segments, Array.take(PLAIN_SEGMENTS), Array.join(SEPARATOR$3));
865
+ };
866
+ const literalText = (specifier) => pipe(Option.liftPredicate(specifier, isStringLiteral), Option.map((literal) => literal.text));
867
+ const runtimeSpecifierOf = (statement) => {
868
+ if (isImportDeclaration(statement)) {
869
+ const typeOnly = pipe(Option.fromUndefinedOr(statement.importClause), Option.exists((clause) => clause.phaseModifier === SyntaxKind.TypeKeyword));
870
+ return Option.filter(literalText(statement.moduleSpecifier), () => !typeOnly);
871
+ }
872
+ if (isExportDeclaration(statement) && !statement.isTypeOnly) return Option.flatMap(Option.fromUndefinedOr(statement.moduleSpecifier), literalText);
873
+ return Option.none();
874
+ };
875
+ const specifiersIn = (api, file, text) => {
876
+ const retained = api.createSourceFile(file, text);
877
+ const found = pipe(retained.sourceFile.statements, Array.fromIterable, Array.map(runtimeSpecifierOf));
878
+ retained.dispose();
879
+ return Array.getSomes(found);
880
+ };
881
+ const subpathTarget = (imports, specifier) => pipe(Record.toEntries(imports), Array.findFirst(([pattern, target]) => {
882
+ if (!String$1.endsWith(WILDCARD)(pattern)) return Option.liftPredicate(target, () => pattern === specifier);
883
+ const prefix = String$1.replace(WILDCARD, "")(pattern);
884
+ return Option.liftPredicate(String$1.replace(WILDCARD, String$1.replace(prefix, "")(specifier))(target), () => String$1.startsWith(prefix)(specifier));
885
+ }));
886
+ const existingSource = Effect.fn("existingSource")(function* existingSource(base) {
887
+ const files = yield* RepositoryFiles;
888
+ const found = yield* pipe(SOURCE_SUFFIXES, Effect.forEach((suffix) => {
889
+ const candidate = `${base}${suffix}`;
890
+ return files.isDirectory(candidate).pipe(Effect.map((directory) => Option.liftPredicate(candidate, () => Option.exists(directory, (isDirectory) => !isDirectory))));
891
+ }));
892
+ return Array.head(Array.getSomes(found));
893
+ });
894
+ const localFile = Effect.fn("localFile")(function* localFile({ directory, imports }, from, specifier) {
895
+ const files = yield* RepositoryFiles;
896
+ if (RELATIVE.test(specifier)) return yield* existingSource(files.resolve(files.dirname(from), specifier));
897
+ const target = subpathTarget(imports, specifier);
898
+ if (Option.isNone(target)) return Option.none();
899
+ return yield* existingSource(files.resolve(directory, target.value));
900
+ });
901
+ const referenceOf = (graph, from, specifier) => {
902
+ if (RELATIVE.test(specifier) || String$1.startsWith(SUBPATH_IMPORT)(specifier)) return localFile(graph, from, specifier).pipe(Effect.map(Option.map(Result.succeed)));
903
+ const name = packageNameOf(specifier);
904
+ return Effect.succeedSome(Result.fail(name));
905
+ };
906
+ const referencesIn = Effect.fn("referencesIn")(function* referencesIn(graph, file) {
907
+ const text = yield* (yield* RepositoryFiles).readFile(file);
908
+ const specifiers = Option.match(text, {
909
+ onNone: () => [],
910
+ onSome: (source) => specifiersIn(graph.api, file, source)
911
+ });
912
+ const references = yield* pipe(specifiers, Effect.forEach((specifier) => referenceOf(graph, file, specifier)));
913
+ const found = Array.getSomes(references);
914
+ return {
915
+ packages: Array.getFailures(found),
916
+ sources: Array.getSuccesses(found)
917
+ };
918
+ });
919
+ const packagesFrom = (graph, walk) => Array.match(walk.pending, {
920
+ onEmpty: () => Effect.succeed(walk.packages),
921
+ onNonEmpty: ([next, ...rest]) => {
922
+ if (HashSet.has(walk.visited, next)) return packagesFrom(graph, {
923
+ ...walk,
924
+ pending: rest
925
+ });
926
+ return referencesIn(graph, next).pipe(Effect.flatMap(({ packages, sources }) => packagesFrom(graph, {
927
+ pending: [...rest, ...sources],
928
+ visited: HashSet.add(walk.visited, next),
929
+ packages: HashSet.union(walk.packages, HashSet.fromIterable(packages))
930
+ })));
931
+ }
932
+ });
933
+ /** @internal */
934
+ const packagesByEntry = Effect.fn("packagesByEntry")(function* packagesByEntry(api, directory, entries) {
935
+ const files = yield* RepositoryFiles;
936
+ const manifest = yield* files.readFile(files.join(directory, MANIFEST_FILE));
937
+ const graph = {
938
+ api,
939
+ directory,
940
+ imports: pipe(manifest, Option.flatMap(decodeImports), Option.flatMap((field) => Option.fromUndefinedOr(field.imports)), Option.getOrElse(() => ({})))
941
+ };
942
+ return yield* pipe(entries, Effect.forEach((entry) => packagesFrom(graph, {
943
+ pending: [entry],
944
+ visited: HashSet.empty(),
945
+ packages: HashSet.empty()
946
+ }).pipe(Effect.map((packages) => [entry, packages]))));
947
+ });
948
+ // src/installed-closure.ts
949
+ const EMPTY = 0;
950
+ const DependencyMap = Schema.optional(Schema.Record(Schema.String, Schema.String));
951
+ const PeerMetaEntry = Schema.Struct({ optional: Schema.optional(Schema.Boolean) });
952
+ const PeerMeta = Schema.optional(Schema.Record(Schema.String, PeerMetaEntry));
953
+ const InstalledManifest = Schema.Struct({
954
+ dependencies: DependencyMap,
955
+ optionalDependencies: DependencyMap,
956
+ peerDependencies: DependencyMap,
957
+ peerDependenciesMeta: PeerMeta
958
+ });
959
+ const decodeManifest = Schema.decodeOption(Schema.fromJsonString(InstalledManifest));
960
+ const keysOf$1 = (map) => pipe(Option.fromUndefinedOr(map), Option.map(Record.keys), Option.getOrElse(() => []));
961
+ const isOptionalPeer = (manifest, name) => pipe(Option.fromUndefinedOr(manifest.peerDependenciesMeta), Option.flatMap(Record.get(name)), Option.exists((meta) => meta.optional === true));
962
+ const installedNames = (manifest) => [
963
+ ...keysOf$1(manifest.dependencies),
964
+ ...keysOf$1(manifest.optionalDependencies),
965
+ ...pipe(keysOf$1(manifest.peerDependencies), Array.filter((name) => !isOptionalPeer(manifest, name)))
966
+ ];
967
+ const childrenOf = Effect.fn("childrenOf")(function* childrenOf(directory) {
968
+ const files = yield* RepositoryFiles;
969
+ const text = yield* files.readFile(files.join(directory, MANIFEST_FILE));
970
+ return pipe(text, Option.flatMap(decodeManifest), Option.map(installedNames), Option.getOrElse(() => []), Array.map((child) => [directory, child]));
971
+ });
972
+ const resolveFrom = (from, name) => Effect.gen(function* resolvePackage() {
973
+ const files = yield* RepositoryFiles;
974
+ const candidate = files.join(from, NODE_MODULES, name);
975
+ if (yield* files.exists(files.join(candidate, "package.json"))) {
976
+ const real = yield* files.realPath(candidate);
977
+ return Option.some(real);
978
+ }
979
+ const parent = files.dirname(from);
980
+ if (parent === from) return Option.none();
981
+ return yield* resolveFrom(parent, name);
982
+ });
983
+ const unseen = (from, name, seen) => resolveFrom(from, name).pipe(Effect.map(Option.filter((directory) => !HashSet.has(seen, directory))));
984
+ const closureOf = (provided, collect) => Array.match(collect.pending, {
985
+ onEmpty: () => Effect.succeed(collect.seen),
986
+ onNonEmpty: ([[from, name], ...rest]) => {
987
+ if (HashSet.has(provided, name)) return closureOf(provided, {
988
+ ...collect,
989
+ pending: rest
990
+ });
991
+ return unseen(from, name, collect.seen).pipe(Effect.flatMap((resolved) => {
992
+ if (Option.isNone(resolved)) return closureOf(provided, {
993
+ ...collect,
994
+ pending: rest
995
+ });
996
+ return childrenOf(resolved.value).pipe(Effect.flatMap((children) => closureOf(provided, {
997
+ pending: [...rest, ...children],
998
+ seen: HashSet.add(collect.seen, resolved.value)
999
+ })));
1000
+ }));
1001
+ }
1002
+ });
1003
+ const directorySize = (directory) => Effect.gen(function* sizeOfDirectory() {
1004
+ const files = yield* RepositoryFiles;
1005
+ const names = yield* files.readDirectory(directory);
1006
+ const entries = pipe(names, Array.filter((name) => name !== NODE_MODULES), Array.map((name) => files.join(directory, name)));
1007
+ const sizes = yield* pipe(entries, Effect.forEach((entry) => files.isDirectory(entry).pipe(Effect.flatMap((kind) => {
1008
+ if (Option.isNone(kind)) return Effect.succeed(EMPTY);
1009
+ if (kind.value) return directorySize(entry);
1010
+ return files.fileSize(entry);
1011
+ }))));
1012
+ return Number$1.sumAll(sizes);
1013
+ });
1014
+ const installedClosure = ({ provided, from, name }) => closureOf(provided, {
1015
+ pending: [[from, name]],
1016
+ seen: HashSet.empty()
1017
+ });
1018
+ const installedSize = (directory) => {
1019
+ if (String$1.includes(`/node_modules/`)(directory)) return directorySize(directory);
1020
+ return Effect.succeed(EMPTY);
1021
+ };
1022
+ // src/public-entries.ts
1023
+ const SOURCE = /\.[cm]?tsx?$/u;
1024
+ const BUILT = /\.(?:d\.)?[cm]?[jt]s$/u;
1025
+ const DECLARATION = ".d.";
1026
+ const SEPARATOR$2 = "/";
1027
+ const ROOT_SUBPATH = ".";
1028
+ const BIN_LABEL = "bin";
1029
+ const targetsIn = (held) => {
1030
+ if (Predicate.isString(held)) return [held];
1031
+ if (!Predicate.isObject(held)) return [];
1032
+ return pipe(Record.values(held), Array.flatMap(targetsIn));
1033
+ };
1034
+ const subpathsOf = (exportsField) => {
1035
+ if (!Predicate.isObject(exportsField)) return [[ROOT_SUBPATH, exportsField]];
1036
+ const entries = Record.toEntries(exportsField);
1037
+ if (pipe(entries, Array.some(([key]) => String$1.startsWith(ROOT_SUBPATH)(key)))) return entries;
1038
+ return [[ROOT_SUBPATH, exportsField]];
1039
+ };
1040
+ const binsOf = (manifest) => {
1041
+ if (Predicate.isString(manifest.bin)) {
1042
+ const name = Option.getOrElse(Option.fromUndefinedOr(manifest.name), () => "");
1043
+ return [[`${BIN_LABEL} ${name}`, manifest.bin]];
1044
+ }
1045
+ if (!Predicate.isObject(manifest.bin)) return [];
1046
+ return pipe(Record.toEntries(manifest.bin), Array.map(([name, target]) => [`${BIN_LABEL} ${name}`, target]));
1047
+ };
1048
+ const baseNameOf = (target) => pipe(String$1.split(target, SEPARATOR$2), Array.last, Option.getOrElse(() => target), String$1.replace(BUILT, ""));
1049
+ const sourceOf = ({ join, packEntries }) => (target) => {
1050
+ if (SOURCE.test(target) && !String$1.includes(DECLARATION)(target)) return Option.some(join(target));
1051
+ const name = baseNameOf(target);
1052
+ return pipe(packEntries, Array.findFirst((entry) => entry.name === name), Option.map((entry) => entry.source));
1053
+ };
1054
+ const publicEntriesOf = (lookup) => pipe([...subpathsOf(lookup.manifest.exports), ...binsOf(lookup.manifest)], Array.flatMap(([label, held]) => pipe(targetsIn(held), Array.map(sourceOf(lookup)), Array.getSomes, Array.map((source) => ({
1055
+ label,
1056
+ source
1057
+ })))), Array.dedupeWith((left, right) => left.label === right.label && left.source === right.source));
1058
+ // src/published.ts
849
1059
  const CONFIG_FILE = "vite.config.ts";
850
- const FIRST = 1;
851
- const READ_CONCURRENCY$1 = 64;
852
- const AGENT_DOCS = /^(?:AGENTS|CLAUDE)(?:\.[^/]+)?\.md$/iu;
853
- const README = /^README(?:\.[^/]+)?$/iu;
854
- const Published = Schema.Struct({ publish: Schema.Literal(true) });
855
- const WorkspaceConfig = Schema.Struct({ msch: Published });
1060
+ const SEPARATOR$1 = "/";
1061
+ const READ_CONCURRENCY$2 = 16;
1062
+ const SOURCE_EXTENSION = /\.[cm]?tsx?$/u;
1063
+ const UNIT_BYTES = [
1064
+ ["B", 1],
1065
+ ["kB", 1e3],
1066
+ ["MB", 1e6]
1067
+ ];
1068
+ const SizeText = Schema.TemplateLiteralParser([
1069
+ Schema.Finite,
1070
+ " ",
1071
+ Schema.Literals([
1072
+ "B",
1073
+ "kB",
1074
+ "MB"
1075
+ ])
1076
+ ]);
1077
+ const PackEntry = Schema.Union([
1078
+ Schema.String,
1079
+ Schema.Array(Schema.String),
1080
+ Schema.Record(Schema.String, Schema.String)
1081
+ ]);
1082
+ const PackSettings = Schema.Struct({ entry: Schema.optional(PackEntry) });
1083
+ const PackList = Schema.Union([PackSettings, Schema.Array(PackSettings)]);
1084
+ const isEntryList = Schema.is(Schema.Array(Schema.String));
1085
+ const WorkspaceConfig = Schema.Struct({
1086
+ msch: Schema.Struct({ publish: Schema.Struct({ installSize: SizeText }) }),
1087
+ pack: Schema.optional(PackList)
1088
+ });
1089
+ const PublicManifest = Schema.Struct({ private: Schema.optional(Schema.Literal(false)) });
856
1090
  const taggedError$2 = Schema.TaggedError;
857
1091
  var WorkspaceConfigUnreadable = class extends taggedError$2()("WorkspaceConfigUnreadable", { file: Schema.String }) {};
858
- const isNested = (name) => String$1.includes(SEPARATOR)(name);
859
- const isPublished = Effect.fn("isPublished")(function* isPublished(root, directory) {
860
- const files = yield* RepositoryFiles;
861
- const file = files.join(root, directory, CONFIG_FILE);
862
- if (!(yield* files.exists(file))) return false;
1092
+ const bytesPer = (unit) => pipe(UNIT_BYTES, Array.findFirst(([name]) => name === unit), Option.map(([, bytes]) => bytes), Option.getOrElse(() => NaN));
1093
+ const nameOf = (source) => pipe(String$1.split(source, SEPARATOR$1), Array.last, Option.getOrElse(() => source), String$1.replace(SOURCE_EXTENSION, ""));
1094
+ const entriesOf = (entry) => {
1095
+ if (Predicate.isString(entry)) return [{
1096
+ name: nameOf(entry),
1097
+ source: entry
1098
+ }];
1099
+ if (isEntryList(entry)) return pipe(entry, Array.map((source) => ({
1100
+ name: nameOf(source),
1101
+ source
1102
+ })));
1103
+ return pipe(Record.toEntries(entry), Array.map(([name, source]) => ({
1104
+ name,
1105
+ source
1106
+ })));
1107
+ };
1108
+ const packEntriesOf = (pack) => pipe(Option.fromUndefinedOr(pack), Option.map(Array.ensure), Option.getOrElse(() => []), Array.flatMap((setting) => pipe(Option.fromUndefinedOr(setting.entry), Option.match({
1109
+ onNone: () => [],
1110
+ onSome: entriesOf
1111
+ }))));
1112
+ const settingsIn = Effect.fn("settingsIn")(function* settingsIn(file) {
863
1113
  const config = yield* Effect.tryPromise({
864
1114
  try: () => resolveConfigFromFile(file),
865
1115
  catch: () => WorkspaceConfigUnreadable.make({ file })
866
1116
  }).pipe(Effect.option);
867
- return pipe(config, Option.flatMap(Schema.decodeUnknownOption(WorkspaceConfig)), Option.isSome);
1117
+ return pipe(config, Option.flatMap(Schema.decodeUnknownOption(WorkspaceConfig)));
1118
+ });
1119
+ const isPublicPackage = Effect.fn("isPublicPackage")(function* isPublicPackage(directory) {
1120
+ const files = yield* RepositoryFiles;
1121
+ const text = yield* files.readFile(files.join(directory, MANIFEST_FILE));
1122
+ return Option.exists(text, (found) => Option.isSome(Schema.decodeOption(Schema.fromJsonString(PublicManifest))(found)));
1123
+ });
1124
+ const workspaceOf = (directory, configFile, join) => ({ msch, pack }) => {
1125
+ const [amount, , unit] = msch.publish.installSize;
1126
+ return {
1127
+ directory,
1128
+ configFile,
1129
+ installSize: `${amount} ${unit}`,
1130
+ installBudget: amount * bytesPer(unit),
1131
+ packEntries: pipe(packEntriesOf(pack), Array.map((entry) => ({
1132
+ name: entry.name,
1133
+ source: join(entry.source)
1134
+ })))
1135
+ };
1136
+ };
1137
+ const publishedIn = Effect.fn("publishedIn")(function* publishedIn(root, configName) {
1138
+ const files = yield* RepositoryFiles;
1139
+ const configFile = files.join(root, configName);
1140
+ const directory = files.dirname(configFile);
1141
+ if (!(yield* isPublicPackage(directory))) return Option.none();
1142
+ const settings = yield* settingsIn(configFile);
1143
+ return pipe(settings, Option.map(workspaceOf(directory, configFile, (source) => files.join(directory, source))));
1144
+ });
1145
+ /** @internal */
1146
+ const publishedWorkspaces = Effect.fn("publishedWorkspaces")(function* publishedWorkspaces(root) {
1147
+ const files = yield* RepositoryFiles;
1148
+ const names = yield* files.listedFiles(root);
1149
+ const configs = pipe(names, Array.filter((name) => files.basename(name) === CONFIG_FILE && String$1.includes(SEPARATOR$1)(name)));
1150
+ const found = yield* Effect.forEach(configs, (name) => publishedIn(root, name), { concurrency: READ_CONCURRENCY$2 });
1151
+ return Array.getSomes(found);
1152
+ });
1153
+ // src/install-size.ts
1154
+ const INSTALL_SIZE = "install-size";
1155
+ const STACK_DEPENDENCY = "stack-dependency";
1156
+ const STACK_CATALOG = "catalog:stack";
1157
+ const BUDGET_KEY = "installSize";
1158
+ const DEPENDENCIES_KEY = "\"dependencies\"";
1159
+ const LISTED_LARGEST = 5;
1160
+ const SIZE_CONCURRENCY = 32;
1161
+ const MEGABYTE = 1e6;
1162
+ const MEGABYTE_DIGITS = 1;
1163
+ const START = 0;
1164
+ const SINGLE_ENTRY = 1;
1165
+ const megabytes = (bytes) => `${(bytes / MEGABYTE).toFixed(MEGABYTE_DIGITS)} MB`;
1166
+ const usageHint = (dependency, entryCount) => {
1167
+ if (!(entryCount > SINGLE_ENTRY && dependency.entries.length > START && dependency.entries.length < entryCount)) return "";
1168
+ return ` (imported only from ${Array.join(dependency.entries, ", ")})`;
1169
+ };
1170
+ const listed = ({ largest, entryCount }) => pipe(largest, Array.map((dependency) => `${dependency.name} ${megabytes(dependency.size)}${usageHint(dependency, entryCount)}`), Array.join(", "));
1171
+ const budgetProblem = Effect.fn("budgetProblem")(function* budgetProblem(overrun) {
1172
+ const files = yield* RepositoryFiles;
1173
+ const { workspace, total } = overrun;
1174
+ const config = yield* files.readFile(workspace.configFile);
1175
+ const source = Option.getOrElse(config, () => "");
1176
+ const index = Option.getOrElse(String$1.indexOf(BUDGET_KEY)(source), () => START);
1177
+ return {
1178
+ file: workspace.configFile,
1179
+ ...positionOf(source)(index),
1180
+ rule: INSTALL_SIZE,
1181
+ message: `installing this package adds ${megabytes(total)} of dependencies besides its peers, over the ${workspace.installSize} in msch.publish.installSize. Largest: ${listed(overrun)}. Make a dependency imported only from some entries an optional peer, move one that the installing project already has to peerDependencies, or replace a heavy one. Raise installSize only when none of these applies.`
1182
+ };
1183
+ });
1184
+ const measure = Effect.fn("measure")(function* measure({ workspace, provided }, direct) {
1185
+ const closures = yield* pipe(direct, Effect.forEach((name) => installedClosure({
1186
+ provided,
1187
+ from: workspace.directory,
1188
+ name
1189
+ }).pipe(Effect.map((packages) => [name, packages]))));
1190
+ const everything = pipe(closures, Array.flatMap(([, packages]) => Array.fromIterable(packages)), HashSet.fromIterable);
1191
+ const measured = yield* Effect.forEach(everything, (found) => installedSize(found).pipe(Effect.map((size) => [found, size])), { concurrency: SIZE_CONCURRENCY });
1192
+ const sizes = HashMap.fromIterable(measured);
1193
+ const sizeOf = (packages) => Number$1.sumAll(pipe(Array.fromIterable(packages), Array.map((found) => Option.getOrElse(HashMap.get(sizes, found), () => START))));
1194
+ return {
1195
+ closures,
1196
+ sizeOf,
1197
+ total: sizeOf(everything)
1198
+ };
1199
+ });
1200
+ const importersOf = Effect.fn("importersOf")(function* importersOf({ workspace, entries }) {
1201
+ const compiler = yield* typeScriptCompiler;
1202
+ const sources = pipe(entries, Array.map((entry) => entry.source), Array.dedupe);
1203
+ const byEntry = yield* packagesByEntry(compiler.api, workspace.directory, sources);
1204
+ const reached = HashMap.fromIterable(byEntry);
1205
+ return (name) => pipe(entries, Array.filter((entry) => pipe(HashMap.get(reached, entry.source), Option.exists(HashSet.has(name)))), Array.map((entry) => entry.label), Array.dedupe);
1206
+ }, Effect.scoped);
1207
+ const bySizeDescending = Order.mapInput(Order.flip(Order.Number), (dependency) => dependency.size);
1208
+ const sizeProblems = Effect.fn("sizeProblems")(function* sizeProblems(published, direct) {
1209
+ const { closures, sizeOf, total } = yield* measure(published, direct);
1210
+ if (total <= published.workspace.installBudget) return [];
1211
+ const importers = yield* importersOf(published);
1212
+ const largest = pipe(closures, Array.map(([name, packages]) => ({
1213
+ name,
1214
+ size: sizeOf(packages),
1215
+ entries: importers(name)
1216
+ })), Array.sortBy(bySizeDescending), Array.take(LISTED_LARGEST));
1217
+ const entryCount = pipe(published.entries, Array.map((entry) => entry.label), Array.dedupe).length;
1218
+ return [yield* budgetProblem({
1219
+ workspace: published.workspace,
1220
+ total,
1221
+ largest,
1222
+ entryCount
1223
+ })];
1224
+ });
1225
+ const stackProblems = (file, source, dependencies) => {
1226
+ const section = Option.getOrElse(String$1.indexOf(DEPENDENCIES_KEY)(source), () => START);
1227
+ const rest = String$1.slice(section)(source);
1228
+ return pipe(Record.toEntries(dependencies), Array.filter(([, specifier]) => specifier === STACK_CATALOG), Array.map(([name]) => ({
1229
+ file,
1230
+ ...positionOf(source)(section + Option.getOrElse(String$1.indexOf(`"${name}"`)(rest), () => START)),
1231
+ rule: STACK_DEPENDENCY,
1232
+ message: `${name} comes from the stack catalog, so every project that installs this package already has it. Move it to peerDependencies and keep it in devDependencies, so the installing project shares one copy.`
1233
+ })));
1234
+ };
1235
+ const PublishedManifest = Schema.Struct({
1236
+ name: Schema.optional(Schema.String),
1237
+ dependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
1238
+ peerDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
1239
+ exports: Schema.optional(Schema.Unknown),
1240
+ bin: Schema.optional(Schema.Unknown)
868
1241
  });
1242
+ const problemsOf = Effect.fn("problemsOf")(function* problemsOf(workspace, file, source) {
1243
+ const files = yield* RepositoryFiles;
1244
+ const manifest = Schema.decodeOption(Schema.fromJsonString(PublishedManifest))(source);
1245
+ if (Option.isNone(manifest)) return [];
1246
+ const dependencies = Option.getOrElse(Option.fromUndefinedOr(manifest.value.dependencies), () => ({}));
1247
+ const entries = publicEntriesOf({
1248
+ manifest: manifest.value,
1249
+ join: (target) => files.join(workspace.directory, target),
1250
+ packEntries: workspace.packEntries
1251
+ });
1252
+ const provided = HashSet.fromIterable(keysOf$1(manifest.value.peerDependencies));
1253
+ const sized = yield* sizeProblems({
1254
+ workspace,
1255
+ entries,
1256
+ provided
1257
+ }, Record.keys(dependencies));
1258
+ return [...stackProblems(file, source, dependencies), ...sized];
1259
+ });
1260
+ const workspaceProblems = Effect.fn("workspaceProblems")(function* workspaceProblems(workspace) {
1261
+ const files = yield* RepositoryFiles;
1262
+ const file = files.join(workspace.directory, MANIFEST_FILE);
1263
+ const source = yield* files.readFile(file);
1264
+ return yield* Option.match(source, {
1265
+ onNone: () => Effect.succeed([]),
1266
+ onSome: (text) => problemsOf(workspace, file, text)
1267
+ });
1268
+ });
1269
+ /** @internal */
1270
+ const checkPublishedPackages = Effect.fn("checkPublishedPackages")(function* checkPublishedPackages(root) {
1271
+ const workspaces = yield* publishedWorkspaces(root);
1272
+ const found = yield* pipe(workspaces, Effect.forEach(workspaceProblems));
1273
+ return Array.flatten(found);
1274
+ });
1275
+ // src/nested-docs.ts
1276
+ const RULE = "nested-docs";
1277
+ const SEPARATOR = "/";
1278
+ const FIRST = 1;
1279
+ const READ_CONCURRENCY$1 = 64;
1280
+ const AGENT_DOCS = /^(?:AGENTS|CLAUDE)(?:\.[^/]+)?\.md$/iu;
1281
+ const README = /^README(?:\.[^/]+)?$/iu;
1282
+ const isNested = (name) => String$1.includes(SEPARATOR)(name);
869
1283
  const problemFor = (file, message) => ({
870
1284
  file,
871
1285
  line: FIRST,
@@ -874,21 +1288,23 @@ const problemFor = (file, message) => ({
874
1288
  message
875
1289
  });
876
1290
  const AGENT_DOCS_MESSAGE = "belongs only at the repository root. Move what agents need into the root AGENTS.md and delete this file.";
877
- const README_MESSAGE = "is allowed below the root only in a workspace published to npm, marked by msch: { publish: true } in its vite.config.ts. Set it there if the workspace is published, or move the content into the root README.md and delete this file.";
878
- const problemIn = Effect.fn("problemIn")(function* problemIn(root, name) {
1291
+ const README_MESSAGE = "is allowed below the root only in a workspace published to npm, marked by msch: { publish: { installSize } } in its vite.config.ts. Set it there if the workspace is published, or move the content into the root README.md and delete this file.";
1292
+ const problemIn = Effect.fn("problemIn")(function* problemIn(root, published, name) {
879
1293
  const files = yield* RepositoryFiles;
880
1294
  const base = files.basename(name);
881
1295
  const file = files.join(root, name);
882
1296
  if (AGENT_DOCS.test(base)) return Option.some(problemFor(file, `${base} ${AGENT_DOCS_MESSAGE}`));
883
1297
  if (!README.test(base)) return Option.none();
884
- if (yield* isPublished(root, files.dirname(name))) return Option.none();
1298
+ if (HashSet.has(published, files.dirname(file))) return Option.none();
885
1299
  return Option.some(problemFor(file, `${base} ${README_MESSAGE}`));
886
1300
  });
887
1301
  /** @internal */
888
1302
  const checkNestedDocs = Effect.fn("checkNestedDocs")(function* checkNestedDocs(root) {
889
1303
  const names = yield* (yield* RepositoryFiles).listedFiles(root);
890
1304
  const nested = pipe(names, Array.filter(isNested));
891
- const found = yield* Effect.forEach(nested, (name) => problemIn(root, name), { concurrency: READ_CONCURRENCY$1 });
1305
+ const workspaces = yield* publishedWorkspaces(root);
1306
+ const published = pipe(workspaces, Array.map((workspace) => workspace.directory), HashSet.fromIterable);
1307
+ const found = yield* Effect.forEach(nested, (name) => problemIn(root, published, name), { concurrency: READ_CONCURRENCY$1 });
892
1308
  return Array.getSomes(found);
893
1309
  });
894
1310
  // src/override-findings.ts
@@ -1251,7 +1667,8 @@ const checkAll = Effect.fn("checkAll")(function* checkAll(root) {
1251
1667
  checkOverrides(root),
1252
1668
  checkHiddenUnicode(root),
1253
1669
  checkPython(root),
1254
- checkNestedDocs(root)
1670
+ checkNestedDocs(root),
1671
+ checkPublishedPackages(root)
1255
1672
  ], { concurrency: "unbounded" });
1256
1673
  return Array.flatten(found);
1257
1674
  });
@@ -1271,7 +1688,7 @@ const reportFailure = (cause) => Effect.gen(function* report() {
1271
1688
  });
1272
1689
  const tracing = Otlp.layerFromConfig({ resource: { serviceName: "msch" } }).pipe(Layer.provide(OtlpSerialization.layerJson), Layer.provide(FetchHttpClient.layer));
1273
1690
  const runCli = (program) => {
1274
- const services = Layer.mergeAll(nodeRepositoryFiles, tracing).pipe(Layer.provideMerge(layer));
1691
+ const services = Layer.mergeAll(nodeRepositoryFiles, tracing).pipe(Layer.provideMerge(nodeServicesLayer));
1275
1692
  const main = Layer.build(services).pipe(Effect.flatMap((context) => Effect.provideContext(program.pipe(Effect.tapCause(reportFailure)), context)), Effect.scoped);
1276
1693
  runMain(main, { disableErrorReporting: true });
1277
1694
  };