@savvy-web/silk-effects 3.2.1 → 3.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/silk-effects",
3
- "version": "3.2.1",
3
+ "version": "3.2.2",
4
4
  "private": false,
5
5
  "description": "Shared Effect library for Silk Suite conventions",
6
6
  "homepage": "https://github.com/savvy-web/systems/tree/main/packages/silk-effects",
@@ -1,4 +1,5 @@
1
1
  import { ChangesetConfigFile, SilkChangesetConfigFile, VersioningStrategyResult } from "./VersioningSchemas.js";
2
+ import { trimTrailingSlashes } from "../utils/TrailingSlash.js";
2
3
  import { TagStrategyType } from "./TagStrategySchemas.js";
3
4
  import { Equal, Function, Hash, Option, Pretty, Schema } from "effect";
4
5
  import { PublishConfig, PublishTarget } from "workspaces-effect";
@@ -39,7 +40,7 @@ const KNOWN_REGISTRIES = {
39
40
  * both sides so `hasTarget`/`targetFor` match regardless of which form a target
40
41
  * carries (binding-driven, placeholder, or access-branch fallback).
41
42
  */
42
- const sameRegistry = (a, b) => a.replace(/\/+$/, "") === b.replace(/\/+$/, "");
43
+ const sameRegistry = (a, b) => trimTrailingSlashes(a) === trimTrailingSlashes(b);
43
44
  const WorkspaceVersion = Schema.Struct({ current: Schema.String });
44
45
  /**
45
46
  * A fully analyzed workspace with publish targets, versioning status,
@@ -1,4 +1,5 @@
1
1
  import { PublishTargetBindingError } from "../errors/PublishTargetBindingError.js";
2
+ import { trimTrailingSlashes } from "../utils/TrailingSlash.js";
2
3
  import { ChangesetConfig } from "./ChangesetConfig.js";
3
4
  import { Effect, Layer } from "effect";
4
5
  import { isAbsolute, join, relative } from "node:path";
@@ -186,18 +187,16 @@ var SilkPublishability = class {
186
187
  * normalizes to `.`, matching how a root-directory target is recorded.
187
188
  *
188
189
  * @remarks
189
- * Trailing slashes are trimmed with an index scan rather than `/\/+$/`. That
190
- * regex is unanchored at the start, so the engine retries the match from every
191
- * position and degrades to O(n²) on a path of many slashes (CodeQL
192
- * `js/polynomial-redos`). `dir` reaches here from `dist/prod/targets.json`, which
193
- * the bundler writes but a consumer repo could hand-edit.
190
+ * Trailing-slash trimming is delegated to the shared `trimTrailingSlashes`
191
+ * index-scan helper rather than `/\/+$/`: that regex is unanchored at the
192
+ * start, so the engine retries the match from every position and degrades to
193
+ * O(n²) on a path of many slashes (CodeQL `js/polynomial-redos`). `dir` reaches
194
+ * here from `dist/prod/targets.json`, which the bundler writes but a consumer
195
+ * repo could hand-edit.
194
196
  */
195
197
  const normalizeDir = (dir) => {
196
198
  const slashed = dir.replaceAll("\\", "/");
197
- const withoutPrefix = slashed.startsWith("./") ? slashed.slice(2) : slashed;
198
- let end = withoutPrefix.length;
199
- while (end > 0 && withoutPrefix[end - 1] === "/") end -= 1;
200
- const normalized = withoutPrefix.slice(0, end);
199
+ const normalized = trimTrailingSlashes(slashed.startsWith("./") ? slashed.slice(2) : slashed);
201
200
  return normalized === "" ? "." : normalized;
202
201
  };
203
202
  /** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
@@ -0,0 +1,18 @@
1
+ //#region src/utils/TrailingSlash.ts
2
+ /**
3
+ * Trim trailing slashes from a string.
4
+ *
5
+ * @remarks
6
+ * Trims trailing slashes with an index scan rather than `/\/+$/`. That regex is
7
+ * unanchored at the start, so the engine retries the match from every position
8
+ * and degrades to O(n²) on a string of many slashes (CodeQL `js/polynomial-redos`).
9
+ * Only a trailing run of slashes is removed; interior slash runs are untouched.
10
+ */
11
+ const trimTrailingSlashes = (s) => {
12
+ let end = s.length;
13
+ while (end > 0 && s[end - 1] === "/") end -= 1;
14
+ return s.slice(0, end);
15
+ };
16
+
17
+ //#endregion
18
+ export { trimTrailingSlashes };