@savvy-web/silk 2.2.0 → 2.2.1

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.
@@ -295,6 +295,33 @@ const ChangesetConfigReaderLive = Layer.effect(ChangesetConfigReader, Effect.gen
295
295
  return { read };
296
296
  }));
297
297
 
298
+ //#endregion
299
+ //#region ../silk-effects/dist/dev/pkg/errors/PublishTargetBindingError.js
300
+ /**
301
+ * Raised when publishability detection selects a directory that the package's
302
+ * `dist/prod/targets.json` binding does not describe.
303
+ *
304
+ * @remarks
305
+ * The bundler's prod build writes `targets.json` naming every byte-group
306
+ * directory it produced. Once that binding exists it is authoritative: the only
307
+ * directories whose bytes may be published are the ones it lists. A detector
308
+ * that returns anything else — most often `publishConfig.directory` pointing at
309
+ * a **dev** build, because silk mode was misdetected — is about to pack an
310
+ * unresolved workspace manifest and ship it to a registry.
311
+ *
312
+ * That is the `yaml-effect@0.7.1` failure: detection picked `dist/dev/pkg`, the
313
+ * dev manifest still carried `catalog:` specifiers, and the published package
314
+ * was uninstallable (`EUNSUPPORTEDPROTOCOL`).
315
+ *
316
+ * @since 3.1.0
317
+ * @public
318
+ */
319
+ var PublishTargetBindingError = class extends Data.TaggedError("PublishTargetBindingError") {
320
+ get message() {
321
+ return `Package ${this.pkg} resolved publish directory "${this.directory}", which is not one of the directories bound by dist/prod/targets.json (${this.boundDirectories.join(", ")}). This means publishability detection did not select the prod build output — refusing to publish before packing an unresolved manifest.`;
322
+ }
323
+ };
324
+
298
325
  //#endregion
299
326
  //#region ../silk-effects/dist/dev/pkg/services/ChangesetConfig.js
300
327
  /**
@@ -10970,6 +10997,17 @@ var SilkPublishability = class {
10970
10997
  * Resolve a package's publish targets via {@link SilkPublishability}, then drop any
10971
10998
  * whose built `directory` package.json is `private: true`. Returned targets keep the
10972
10999
  * detector's original (possibly package-relative) `directory`.
11000
+ *
11001
+ * @remarks
11002
+ * When the prod build has written `dist/prod/targets.json`, that binding is
11003
+ * authoritative: every surviving target's directory must be one of the group
11004
+ * directories it names. A directory outside the binding means detection did
11005
+ * not select the prod output — the `yaml-effect@0.7.1` shape, where a dev
11006
+ * manifest carrying `catalog:` specifiers was packed and published. Rather
11007
+ * than ship those bytes, fail with {@link PublishTargetBindingError}.
11008
+ *
11009
+ * Before the prod build runs there is no binding, and the detector's
11010
+ * placeholder directories are left alone.
10973
11011
  */
10974
11012
  static resolveTargets(pkg, root) {
10975
11013
  return Effect.gen(function* () {
@@ -10981,6 +11019,18 @@ var SilkPublishability = class {
10981
11019
  const dir = isAbsolute(t.directory) ? t.directory : join(pkg.path, t.directory);
10982
11020
  if (!(yield* isTargetPrivate(fs, dir))) kept.push(t);
10983
11021
  }
11022
+ const binding = yield* readTargetsBinding(fs, pkg.path);
11023
+ if (binding === null) return kept;
11024
+ const boundDirectories = binding.groups.map((g) => normalizeDir(g.dir));
11025
+ const bound = new Set(boundDirectories);
11026
+ for (const t of kept) {
11027
+ const relativeDir = normalizeDir(isAbsolute(t.directory) ? relative(pkg.path, t.directory) : t.directory);
11028
+ if (!bound.has(relativeDir)) return yield* Effect.fail(new PublishTargetBindingError({
11029
+ pkg: pkg.name,
11030
+ directory: relativeDir,
11031
+ boundDirectories
11032
+ }));
11033
+ }
10984
11034
  return kept;
10985
11035
  });
10986
11036
  }
@@ -11007,6 +11057,26 @@ var SilkPublishability = class {
11007
11057
  });
11008
11058
  }
11009
11059
  };
11060
+ /**
11061
+ * Reduce a directory to a comparable package-relative POSIX path: backslashes to
11062
+ * forward slashes, no `./` prefix, no trailing slash. `""` (the package root)
11063
+ * normalizes to `.`, matching how a root-directory target is recorded.
11064
+ *
11065
+ * @remarks
11066
+ * Trailing slashes are trimmed with an index scan rather than `/\/+$/`. That
11067
+ * regex is unanchored at the start, so the engine retries the match from every
11068
+ * position and degrades to O(n²) on a path of many slashes (CodeQL
11069
+ * `js/polynomial-redos`). `dir` reaches here from `dist/prod/targets.json`, which
11070
+ * the bundler writes but a consumer repo could hand-edit.
11071
+ */
11072
+ const normalizeDir = (dir) => {
11073
+ const slashed = dir.replaceAll("\\", "/");
11074
+ const withoutPrefix = slashed.startsWith("./") ? slashed.slice(2) : slashed;
11075
+ let end = withoutPrefix.length;
11076
+ while (end > 0 && withoutPrefix[end - 1] === "/") end -= 1;
11077
+ const normalized = withoutPrefix.slice(0, end);
11078
+ return normalized === "" ? "." : normalized;
11079
+ };
11010
11080
  /** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
11011
11081
  const isTargetPrivate = (fs, targetDir) => fs.readFileString(join(targetDir, "package.json")).pipe(Effect.flatMap((content) => Effect.try({
11012
11082
  try: () => JSON.parse(content).private === true,
@@ -1,5 +1,4 @@
1
1
  import { Changesets } from "@savvy-web/silk-effects";
2
-
3
2
  //#region src/changesets/remark.d.ts
4
3
  declare const AggregateDependencyTablesPlugin: typeof Changesets.AggregateDependencyTablesPlugin;
5
4
  declare const ContributorFootnotesPlugin: typeof Changesets.ContributorFootnotesPlugin;
package/changesets.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Changesets } from "@savvy-web/silk-effects";
2
-
3
2
  //#region src/changesets/index.d.ts
4
3
  type LintMessage = Changesets.LintMessage;
5
4
  declare const Categories: typeof Changesets.Categories;
@@ -1,5 +1,4 @@
1
1
  import { Commitlint } from "@savvy-web/silk-effects";
2
-
3
2
  //#region src/commitlint/formatter.d.ts
4
3
  type FormatterResult = Commitlint.FormatterResult;
5
4
  type LintOutcome = Commitlint.LintOutcome;
@@ -1,5 +1,4 @@
1
1
  import { Commitlint } from "@savvy-web/silk-effects";
2
-
3
2
  //#region src/commitlint/prompt.d.ts
4
3
  type CommitType = Commitlint.CommitType;
5
4
  type PromptConfigOptions = Commitlint.PromptConfigOptions;
@@ -1,5 +1,4 @@
1
1
  import { Commitlint } from "@savvy-web/silk-effects";
2
-
3
2
  //#region src/commitlint/static.d.ts
4
3
  type CommitType = Commitlint.CommitType;
5
4
  type CommitTypeDefinition = Commitlint.CommitTypeDefinition;
package/commitlint.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Commitlint } from "@savvy-web/silk-effects";
2
-
3
2
  //#region src/commitlint/index.d.ts
4
3
  type CommitType = Commitlint.CommitType;
5
4
  type ReleaseFormat = Commitlint.ReleaseFormat;
package/lint.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { ConfigDiscovery, ConfigDiscoveryLive, ConfigLocation, Lint } from "@savvy-web/silk-effects";
2
-
3
2
  //#region src/lint/index.d.ts
4
3
  declare const Handler: typeof Lint.Handler;
5
4
  type Handler = typeof Lint.Handler;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/silk",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "private": false,
5
5
  "description": "The single Silk Suite dev-tooling package — changeset, commitlint, lint, and biome conventions",
6
6
  "homepage": "https://github.com/savvy-web/systems/tree/main/packages/silk",
@@ -67,9 +67,9 @@
67
67
  "dependencies": {
68
68
  "@effect/platform": "^0.96.2",
69
69
  "@savvy-web/changelog": "0.1.1",
70
- "@savvy-web/cli": "1.5.3",
71
- "@savvy-web/mcp": "1.7.0",
72
- "@savvy-web/silk-effects": "3.1.0",
70
+ "@savvy-web/cli": "1.5.4",
71
+ "@savvy-web/mcp": "1.7.1",
72
+ "@savvy-web/silk-effects": "3.2.0",
73
73
  "effect": "^3.21.4",
74
74
  "semver": "^7.8.5"
75
75
  },