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