@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,
@@ -287,6 +287,32 @@ const ChangesetConfigReaderLive = effect.Layer.effect(ChangesetConfigReader, eff
287
287
  return { read };
288
288
  }));
289
289
  //#endregion
290
+ //#region ../silk-effects/dist/dev/pkg/errors/PublishTargetBindingError.js
291
+ /**
292
+ * Raised when publishability detection selects a directory that the package's
293
+ * `dist/prod/targets.json` binding does not describe.
294
+ *
295
+ * @remarks
296
+ * The bundler's prod build writes `targets.json` naming every byte-group
297
+ * directory it produced. Once that binding exists it is authoritative: the only
298
+ * directories whose bytes may be published are the ones it lists. A detector
299
+ * that returns anything else — most often `publishConfig.directory` pointing at
300
+ * a **dev** build, because silk mode was misdetected — is about to pack an
301
+ * unresolved workspace manifest and ship it to a registry.
302
+ *
303
+ * That is the `yaml-effect@0.7.1` failure: detection picked `dist/dev/pkg`, the
304
+ * dev manifest still carried `catalog:` specifiers, and the published package
305
+ * was uninstallable (`EUNSUPPORTEDPROTOCOL`).
306
+ *
307
+ * @since 3.1.0
308
+ * @public
309
+ */
310
+ var PublishTargetBindingError = class extends effect.Data.TaggedError("PublishTargetBindingError") {
311
+ get message() {
312
+ 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.`;
313
+ }
314
+ };
315
+ //#endregion
290
316
  //#region ../silk-effects/dist/dev/pkg/services/ChangesetConfig.js
291
317
  /**
292
318
  * Accessor service over a workspace root's `.changeset/config.json`.
@@ -10779,6 +10805,17 @@ var SilkPublishability = class {
10779
10805
  * Resolve a package's publish targets via {@link SilkPublishability}, then drop any
10780
10806
  * whose built `directory` package.json is `private: true`. Returned targets keep the
10781
10807
  * detector's original (possibly package-relative) `directory`.
10808
+ *
10809
+ * @remarks
10810
+ * When the prod build has written `dist/prod/targets.json`, that binding is
10811
+ * authoritative: every surviving target's directory must be one of the group
10812
+ * directories it names. A directory outside the binding means detection did
10813
+ * not select the prod output — the `yaml-effect@0.7.1` shape, where a dev
10814
+ * manifest carrying `catalog:` specifiers was packed and published. Rather
10815
+ * than ship those bytes, fail with {@link PublishTargetBindingError}.
10816
+ *
10817
+ * Before the prod build runs there is no binding, and the detector's
10818
+ * placeholder directories are left alone.
10782
10819
  */
10783
10820
  static resolveTargets(pkg, root) {
10784
10821
  return effect.Effect.gen(function* () {
@@ -10790,6 +10827,18 @@ var SilkPublishability = class {
10790
10827
  const dir = (0, node_path.isAbsolute)(t.directory) ? t.directory : (0, node_path.join)(pkg.path, t.directory);
10791
10828
  if (!(yield* isTargetPrivate(fs, dir))) kept.push(t);
10792
10829
  }
10830
+ const binding = yield* readTargetsBinding(fs, pkg.path);
10831
+ if (binding === null) return kept;
10832
+ const boundDirectories = binding.groups.map((g) => normalizeDir(g.dir));
10833
+ const bound = new Set(boundDirectories);
10834
+ for (const t of kept) {
10835
+ const relativeDir = normalizeDir((0, node_path.isAbsolute)(t.directory) ? (0, node_path.relative)(pkg.path, t.directory) : t.directory);
10836
+ if (!bound.has(relativeDir)) return yield* effect.Effect.fail(new PublishTargetBindingError({
10837
+ pkg: pkg.name,
10838
+ directory: relativeDir,
10839
+ boundDirectories
10840
+ }));
10841
+ }
10793
10842
  return kept;
10794
10843
  });
10795
10844
  }
@@ -10816,6 +10865,26 @@ var SilkPublishability = class {
10816
10865
  });
10817
10866
  }
10818
10867
  };
10868
+ /**
10869
+ * Reduce a directory to a comparable package-relative POSIX path: backslashes to
10870
+ * forward slashes, no `./` prefix, no trailing slash. `""` (the package root)
10871
+ * normalizes to `.`, matching how a root-directory target is recorded.
10872
+ *
10873
+ * @remarks
10874
+ * Trailing slashes are trimmed with an index scan rather than `/\/+$/`. That
10875
+ * regex is unanchored at the start, so the engine retries the match from every
10876
+ * position and degrades to O(n²) on a path of many slashes (CodeQL
10877
+ * `js/polynomial-redos`). `dir` reaches here from `dist/prod/targets.json`, which
10878
+ * the bundler writes but a consumer repo could hand-edit.
10879
+ */
10880
+ const normalizeDir = (dir) => {
10881
+ const slashed = dir.replaceAll("\\", "/");
10882
+ const withoutPrefix = slashed.startsWith("./") ? slashed.slice(2) : slashed;
10883
+ let end = withoutPrefix.length;
10884
+ while (end > 0 && withoutPrefix[end - 1] === "/") end -= 1;
10885
+ const normalized = withoutPrefix.slice(0, end);
10886
+ return normalized === "" ? "." : normalized;
10887
+ };
10819
10888
  /** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
10820
10889
  const isTargetPrivate = (fs, targetDir) => fs.readFileString((0, node_path.join)(targetDir, "package.json")).pipe(effect.Effect.flatMap((content) => effect.Effect.try({
10821
10890
  try: () => JSON.parse(content).private === true,