@savvy-web/silk 0.4.1 → 0.5.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.
@@ -0,0 +1,78 @@
1
+ const require_ChangesetConfigReader = require('./ChangesetConfigReader.cjs');
2
+ let effect = require("effect");
3
+
4
+ //#region ../silk-effects/dist/dev/pkg/services/ChangesetConfig.js
5
+ /**
6
+ * Accessor service over a workspace root's `.changeset/config.json`.
7
+ *
8
+ * @remarks
9
+ * Reads through {@link ChangesetConfigReader} (FileSystem-based) with a per-root cache.
10
+ * Every accessor is total (error channel `never`): a missing or unreadable config collapses
11
+ * to `mode: "none"` and empty/false defaults.
12
+ *
13
+ * @since 0.4.0
14
+ */
15
+ var ChangesetConfig = class extends effect.Context.Tag("@savvy-web/silk-effects/ChangesetConfig")() {
16
+ /**
17
+ * The one ignore matcher: exact name match, or `@scope/*` wildcard.
18
+ *
19
+ * `"@scope/*"` matches `"@scope/anything"` (prefix kept includes the trailing slash),
20
+ * but not the bare scope `"@scope"`.
21
+ */
22
+ static matches(name, pattern) {
23
+ if (pattern.endsWith("/*")) {
24
+ const prefix = pattern.slice(0, -1);
25
+ return name.startsWith(prefix);
26
+ }
27
+ return name === pattern;
28
+ }
29
+ };
30
+ const isSilk = (cfg) => "_isSilk" in cfg && cfg._isSilk === true;
31
+ /**
32
+ * Live {@link ChangesetConfig} reading via {@link ChangesetConfigReader}, cached per root.
33
+ *
34
+ * @remarks
35
+ * Requires `ChangesetConfigReader` (which requires `FileSystem`). Provide
36
+ * `ChangesetConfigReaderLive` + a platform layer (`NodeContext.layer`).
37
+ *
38
+ * @since 0.4.0
39
+ */
40
+ const ChangesetConfigLive = effect.Layer.effect(ChangesetConfig, effect.Effect.gen(function* () {
41
+ const reader = yield* require_ChangesetConfigReader.ChangesetConfigReader;
42
+ const cache = /* @__PURE__ */ new Map();
43
+ const read = (root) => effect.Effect.gen(function* () {
44
+ const hit = cache.get(root);
45
+ if (hit !== void 0) return hit;
46
+ const result = yield* reader.read(root).pipe(effect.Effect.option);
47
+ cache.set(root, result);
48
+ return result;
49
+ });
50
+ return {
51
+ mode: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
52
+ onNone: () => "none",
53
+ onSome: (cfg) => isSilk(cfg) ? "silk" : "vanilla"
54
+ }))),
55
+ versionPrivate: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
56
+ onNone: () => false,
57
+ onSome: (cfg) => {
58
+ const pp = cfg.privatePackages;
59
+ return pp !== void 0 && pp !== false && pp.version === true;
60
+ }
61
+ }))),
62
+ ignorePatterns: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
63
+ onNone: () => [],
64
+ onSome: (cfg) => cfg.ignore ?? []
65
+ }))),
66
+ isIgnored: (name, root) => read(root).pipe(effect.Effect.map(effect.Option.match({
67
+ onNone: () => false,
68
+ onSome: (cfg) => (cfg.ignore ?? []).some((p) => ChangesetConfig.matches(name, p))
69
+ }))),
70
+ fixed: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
71
+ onNone: () => [],
72
+ onSome: (cfg) => cfg.fixed ?? []
73
+ })))
74
+ };
75
+ }));
76
+
77
+ //#endregion
78
+ exports.ChangesetConfig = ChangesetConfig;
@@ -0,0 +1,78 @@
1
+ import { ChangesetConfigReader } from "./ChangesetConfigReader.js";
2
+ import { Context, Effect, Layer, Option } from "effect";
3
+
4
+ //#region ../silk-effects/dist/dev/pkg/services/ChangesetConfig.js
5
+ /**
6
+ * Accessor service over a workspace root's `.changeset/config.json`.
7
+ *
8
+ * @remarks
9
+ * Reads through {@link ChangesetConfigReader} (FileSystem-based) with a per-root cache.
10
+ * Every accessor is total (error channel `never`): a missing or unreadable config collapses
11
+ * to `mode: "none"` and empty/false defaults.
12
+ *
13
+ * @since 0.4.0
14
+ */
15
+ var ChangesetConfig = class extends Context.Tag("@savvy-web/silk-effects/ChangesetConfig")() {
16
+ /**
17
+ * The one ignore matcher: exact name match, or `@scope/*` wildcard.
18
+ *
19
+ * `"@scope/*"` matches `"@scope/anything"` (prefix kept includes the trailing slash),
20
+ * but not the bare scope `"@scope"`.
21
+ */
22
+ static matches(name, pattern) {
23
+ if (pattern.endsWith("/*")) {
24
+ const prefix = pattern.slice(0, -1);
25
+ return name.startsWith(prefix);
26
+ }
27
+ return name === pattern;
28
+ }
29
+ };
30
+ const isSilk = (cfg) => "_isSilk" in cfg && cfg._isSilk === true;
31
+ /**
32
+ * Live {@link ChangesetConfig} reading via {@link ChangesetConfigReader}, cached per root.
33
+ *
34
+ * @remarks
35
+ * Requires `ChangesetConfigReader` (which requires `FileSystem`). Provide
36
+ * `ChangesetConfigReaderLive` + a platform layer (`NodeContext.layer`).
37
+ *
38
+ * @since 0.4.0
39
+ */
40
+ const ChangesetConfigLive = Layer.effect(ChangesetConfig, Effect.gen(function* () {
41
+ const reader = yield* ChangesetConfigReader;
42
+ const cache = /* @__PURE__ */ new Map();
43
+ const read = (root) => Effect.gen(function* () {
44
+ const hit = cache.get(root);
45
+ if (hit !== void 0) return hit;
46
+ const result = yield* reader.read(root).pipe(Effect.option);
47
+ cache.set(root, result);
48
+ return result;
49
+ });
50
+ return {
51
+ mode: (root) => read(root).pipe(Effect.map(Option.match({
52
+ onNone: () => "none",
53
+ onSome: (cfg) => isSilk(cfg) ? "silk" : "vanilla"
54
+ }))),
55
+ versionPrivate: (root) => read(root).pipe(Effect.map(Option.match({
56
+ onNone: () => false,
57
+ onSome: (cfg) => {
58
+ const pp = cfg.privatePackages;
59
+ return pp !== void 0 && pp !== false && pp.version === true;
60
+ }
61
+ }))),
62
+ ignorePatterns: (root) => read(root).pipe(Effect.map(Option.match({
63
+ onNone: () => [],
64
+ onSome: (cfg) => cfg.ignore ?? []
65
+ }))),
66
+ isIgnored: (name, root) => read(root).pipe(Effect.map(Option.match({
67
+ onNone: () => false,
68
+ onSome: (cfg) => (cfg.ignore ?? []).some((p) => ChangesetConfig.matches(name, p))
69
+ }))),
70
+ fixed: (root) => read(root).pipe(Effect.map(Option.match({
71
+ onNone: () => [],
72
+ onSome: (cfg) => cfg.fixed ?? []
73
+ })))
74
+ };
75
+ }));
76
+
77
+ //#endregion
78
+ export { ChangesetConfig };
@@ -0,0 +1,194 @@
1
+ const require_ChangesetConfig = require('./ChangesetConfig.cjs');
2
+ const require_index = require('../../../../../../node_modules/.pnpm/workspaces-effect@1.2.0_@effect_platform@0.96.1_effect@3.21.3__effect@3.21.3/node_modules/workspaces-effect/index.cjs');
3
+ let effect = require("effect");
4
+ let node_path = require("node:path");
5
+ let _effect_platform = require("@effect/platform");
6
+
7
+ //#region ../silk-effects/dist/dev/pkg/services/SilkPublishability.js
8
+ const NPM_DEFAULT = "https://registry.npmjs.org/";
9
+ /**
10
+ * Default registry endpoints for the well-known target keys. Kept in sync with the
11
+ * bundler's `resolveTargets` so the pre-build fallback matches what the binding will carry
12
+ * (no trailing slash).
13
+ */
14
+ const DEFAULT_REGISTRIES = {
15
+ npm: "https://registry.npmjs.org",
16
+ github: "https://npm.pkg.github.com"
17
+ };
18
+ /**
19
+ * Silk publishability rules over `workspaces-effect`'s {@link PublishTarget}.
20
+ *
21
+ * @remarks
22
+ * In silk mode `private: true` is the norm on workspace `package.json`; publishability is
23
+ * derived from `publishConfig`, with the `private` flag consulted only as a last-resort
24
+ * default. All helpers are static so a consumer sees the full rule surface in one place.
25
+ *
26
+ * @since 0.4.0
27
+ */
28
+ var SilkPublishability = class {
29
+ /**
30
+ * Apply silk publishability rules to a raw `package.json` and the bundler's resolved
31
+ * target binding. Targets-first precedence:
32
+ *
33
+ * - A non-empty `publishConfig.targets` map (the bundler's Record-map form) makes the
34
+ * package publishable regardless of `private`. With a `binding` (post-prod-build), one
35
+ * {@link PublishTarget} is emitted per resolved registry target, its `directory` set to
36
+ * the bound group's `dist/prod/<group>/pkg` dir. Without a binding (pre-build), one
37
+ * placeholder target is emitted per declared key so publishability and target counts
38
+ * are correct; the directory is best-effort and unused until the build writes the
39
+ * binding.
40
+ * - Else `publishConfig.access` → one target at `publishConfig.directory`.
41
+ * - Else `private !== true` → one default public target.
42
+ * - Else `[]`.
43
+ *
44
+ * @param pkgName - The package's name (the base name for `true`/empty-object targets).
45
+ * @param raw - The raw `package.json` (silk reads the unschematized `publishConfig`).
46
+ * @param binding - The parsed `dist/prod/targets.json` binding, or `null` when the prod
47
+ * build has not run yet. See {@link readTargetsBinding}.
48
+ */
49
+ static detect(pkgName, raw, binding) {
50
+ const pc = raw.publishConfig;
51
+ const targets = pc?.targets;
52
+ if (targets && typeof targets === "object" && !Array.isArray(targets) && Object.keys(targets).length > 0) {
53
+ const access = pc?.access ?? "public";
54
+ if (binding) {
55
+ const dirByGroup = new Map(binding.groups.map((g) => [g.id, g.dir]));
56
+ return binding.targets.map((t) => new require_index.PublishTarget({
57
+ name: t.name,
58
+ registry: t.registry,
59
+ directory: dirByGroup.get(t.group) ?? `dist/prod/${t.group}/pkg`,
60
+ access
61
+ }));
62
+ }
63
+ return Object.keys(targets).map((id) => new require_index.PublishTarget({
64
+ name: pkgName,
65
+ registry: DEFAULT_REGISTRIES[id] ?? pc?.registry ?? NPM_DEFAULT,
66
+ directory: `dist/prod/${id}/pkg`,
67
+ access
68
+ }));
69
+ }
70
+ if (pc && (pc.access === "public" || pc.access === "restricted")) return [new require_index.PublishTarget({
71
+ name: pkgName,
72
+ registry: pc.registry ?? NPM_DEFAULT,
73
+ directory: pc.directory ?? ".",
74
+ access: pc.access
75
+ })];
76
+ if (raw.private !== true) return [new require_index.PublishTarget({
77
+ name: pkgName,
78
+ registry: pc?.registry ?? NPM_DEFAULT,
79
+ directory: pc?.directory ?? ".",
80
+ access: pc?.access ?? "public"
81
+ })];
82
+ return [];
83
+ }
84
+ /**
85
+ * Resolve a package's publish targets via {@link PublishabilityDetector}, then drop any
86
+ * whose built `directory` package.json is `private: true`. Returned targets keep the
87
+ * detector's original (possibly package-relative) `directory`.
88
+ */
89
+ static resolveTargets(pkg, root) {
90
+ return effect.Effect.gen(function* () {
91
+ const detector = yield* require_index.PublishabilityDetector;
92
+ const fs = yield* _effect_platform.FileSystem.FileSystem;
93
+ const targets = yield* detector.detect(pkg, root);
94
+ const kept = [];
95
+ for (const t of targets) if (!(yield* isTargetPrivate(fs, (0, node_path.isAbsolute)(t.directory) ? t.directory : (0, node_path.join)(pkg.path, t.directory)))) kept.push(t);
96
+ return kept;
97
+ });
98
+ }
99
+ /**
100
+ * The publishable, non-ignored packages, resolved through the single
101
+ * {@link PublishabilityDetector} (which already honors changeset ignore in adaptive mode).
102
+ */
103
+ static listPublishable(root) {
104
+ return effect.Effect.gen(function* () {
105
+ const discovery = yield* require_index.WorkspaceDiscovery;
106
+ const detector = yield* require_index.PublishabilityDetector;
107
+ const packages = yield* discovery.listPackages().pipe(effect.Effect.orDie);
108
+ const out = [];
109
+ for (const pkg of packages) {
110
+ const targets = yield* detector.detect(pkg, root);
111
+ if (targets.length > 0) out.push({
112
+ name: pkg.name,
113
+ version: pkg.version,
114
+ path: pkg.path,
115
+ targetCount: targets.length
116
+ });
117
+ }
118
+ return out;
119
+ });
120
+ }
121
+ };
122
+ /** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
123
+ const isTargetPrivate = (fs, targetDir) => fs.readFileString((0, node_path.join)(targetDir, "package.json")).pipe(effect.Effect.flatMap((content) => effect.Effect.try({
124
+ try: () => JSON.parse(content).private === true,
125
+ catch: () => /* @__PURE__ */ new Error("invalid package.json")
126
+ })), effect.Effect.orElseSucceed(() => false));
127
+ /** Read a raw `package.json` from disk via FileSystem; missing/unreadable/malformed → null. */
128
+ const readRaw = (fs, packageJsonPath) => fs.readFileString(packageJsonPath).pipe(effect.Effect.flatMap((content) => effect.Effect.try({
129
+ try: () => JSON.parse(content),
130
+ catch: () => /* @__PURE__ */ new Error("invalid package.json")
131
+ })), effect.Effect.orElseSucceed(() => null));
132
+ /**
133
+ * Read the bundler's `<pkgPath>/dist/prod/targets.json` binding via FileSystem.
134
+ *
135
+ * @remarks
136
+ * Returns `null` when the file is missing/unreadable/malformed — i.e. before the prod build
137
+ * has run. {@link SilkPublishability.detect} falls back to declared-key placeholders in that
138
+ * case. Used by the silk {@link PublishabilityDetector} layers and the workspace analyzer.
139
+ *
140
+ * @param fs - The FileSystem service.
141
+ * @param pkgPath - Absolute path to the package directory.
142
+ * @since 1.0.0
143
+ */
144
+ const readTargetsBinding = (fs, pkgPath) => fs.readFileString((0, node_path.join)(pkgPath, "dist", "prod", "targets.json")).pipe(effect.Effect.flatMap((content) => effect.Effect.try({
145
+ try: () => JSON.parse(content),
146
+ catch: () => /* @__PURE__ */ new Error("invalid targets.json")
147
+ })), effect.Effect.orElseSucceed(() => null));
148
+ /**
149
+ * Override of `workspaces-effect`'s {@link PublishabilityDetector} Tag with pure silk rules.
150
+ *
151
+ * @remarks Requires `FileSystem` (captured at layer build); `detect` reads the raw
152
+ * `package.json` from `pkg.packageJsonPath` and applies {@link SilkPublishability.detect}.
153
+ *
154
+ * @since 0.4.0
155
+ */
156
+ const SilkPublishabilityDetectorLive = effect.Layer.effect(require_index.PublishabilityDetector, effect.Effect.gen(function* () {
157
+ const fs = yield* _effect_platform.FileSystem.FileSystem;
158
+ return { detect: (pkg, _root) => effect.Effect.gen(function* () {
159
+ const raw = yield* readRaw(fs, pkg.packageJsonPath);
160
+ if (!raw) return [];
161
+ const binding = yield* readTargetsBinding(fs, pkg.path);
162
+ return SilkPublishability.detect(pkg.name, raw, binding);
163
+ }) };
164
+ }));
165
+ /**
166
+ * Ignore-aware override of {@link PublishabilityDetector}. `detect` short-circuits to `[]`
167
+ * for changeset-ignored packages, then dispatches on {@link ChangesetConfig.mode}:
168
+ * `none` → `[]`; `silk` → {@link SilkPublishability.detect}; `vanilla` → the library default.
169
+ *
170
+ * @remarks Requires `FileSystem` + {@link ChangesetConfig} at build.
171
+ *
172
+ * @since 0.4.0
173
+ */
174
+ const PublishabilityDetectorAdaptiveLive = effect.Layer.effect(require_index.PublishabilityDetector, effect.Effect.gen(function* () {
175
+ const fs = yield* _effect_platform.FileSystem.FileSystem;
176
+ const config = yield* require_ChangesetConfig.ChangesetConfig;
177
+ const vanilla = yield* effect.Effect.provide(require_index.PublishabilityDetector, require_index.PublishabilityDetectorLive);
178
+ return { detect: (pkg, root) => effect.Effect.gen(function* () {
179
+ if (yield* config.isIgnored(pkg.name, root)) return [];
180
+ const mode = yield* config.mode(root);
181
+ if (mode === "none") return [];
182
+ if (mode === "silk") {
183
+ const raw = yield* readRaw(fs, pkg.packageJsonPath);
184
+ if (!raw) return [];
185
+ const binding = yield* readTargetsBinding(fs, pkg.path);
186
+ return SilkPublishability.detect(pkg.name, raw, binding);
187
+ }
188
+ return yield* vanilla.detect(pkg, root);
189
+ }) };
190
+ }));
191
+
192
+ //#endregion
193
+ exports.SilkPublishability = SilkPublishability;
194
+ exports.readTargetsBinding = readTargetsBinding;
@@ -0,0 +1,193 @@
1
+ import { ChangesetConfig } from "./ChangesetConfig.js";
2
+ import { PublishTarget, PublishabilityDetector, PublishabilityDetectorLive, WorkspaceDiscovery } from "../../../../../../node_modules/.pnpm/workspaces-effect@1.2.0_@effect_platform@0.96.1_effect@3.21.3__effect@3.21.3/node_modules/workspaces-effect/index.js";
3
+ import { Effect, Layer } from "effect";
4
+ import { isAbsolute, join } from "node:path";
5
+ import { FileSystem } from "@effect/platform";
6
+
7
+ //#region ../silk-effects/dist/dev/pkg/services/SilkPublishability.js
8
+ const NPM_DEFAULT = "https://registry.npmjs.org/";
9
+ /**
10
+ * Default registry endpoints for the well-known target keys. Kept in sync with the
11
+ * bundler's `resolveTargets` so the pre-build fallback matches what the binding will carry
12
+ * (no trailing slash).
13
+ */
14
+ const DEFAULT_REGISTRIES = {
15
+ npm: "https://registry.npmjs.org",
16
+ github: "https://npm.pkg.github.com"
17
+ };
18
+ /**
19
+ * Silk publishability rules over `workspaces-effect`'s {@link PublishTarget}.
20
+ *
21
+ * @remarks
22
+ * In silk mode `private: true` is the norm on workspace `package.json`; publishability is
23
+ * derived from `publishConfig`, with the `private` flag consulted only as a last-resort
24
+ * default. All helpers are static so a consumer sees the full rule surface in one place.
25
+ *
26
+ * @since 0.4.0
27
+ */
28
+ var SilkPublishability = class {
29
+ /**
30
+ * Apply silk publishability rules to a raw `package.json` and the bundler's resolved
31
+ * target binding. Targets-first precedence:
32
+ *
33
+ * - A non-empty `publishConfig.targets` map (the bundler's Record-map form) makes the
34
+ * package publishable regardless of `private`. With a `binding` (post-prod-build), one
35
+ * {@link PublishTarget} is emitted per resolved registry target, its `directory` set to
36
+ * the bound group's `dist/prod/<group>/pkg` dir. Without a binding (pre-build), one
37
+ * placeholder target is emitted per declared key so publishability and target counts
38
+ * are correct; the directory is best-effort and unused until the build writes the
39
+ * binding.
40
+ * - Else `publishConfig.access` → one target at `publishConfig.directory`.
41
+ * - Else `private !== true` → one default public target.
42
+ * - Else `[]`.
43
+ *
44
+ * @param pkgName - The package's name (the base name for `true`/empty-object targets).
45
+ * @param raw - The raw `package.json` (silk reads the unschematized `publishConfig`).
46
+ * @param binding - The parsed `dist/prod/targets.json` binding, or `null` when the prod
47
+ * build has not run yet. See {@link readTargetsBinding}.
48
+ */
49
+ static detect(pkgName, raw, binding) {
50
+ const pc = raw.publishConfig;
51
+ const targets = pc?.targets;
52
+ if (targets && typeof targets === "object" && !Array.isArray(targets) && Object.keys(targets).length > 0) {
53
+ const access = pc?.access ?? "public";
54
+ if (binding) {
55
+ const dirByGroup = new Map(binding.groups.map((g) => [g.id, g.dir]));
56
+ return binding.targets.map((t) => new PublishTarget({
57
+ name: t.name,
58
+ registry: t.registry,
59
+ directory: dirByGroup.get(t.group) ?? `dist/prod/${t.group}/pkg`,
60
+ access
61
+ }));
62
+ }
63
+ return Object.keys(targets).map((id) => new PublishTarget({
64
+ name: pkgName,
65
+ registry: DEFAULT_REGISTRIES[id] ?? pc?.registry ?? NPM_DEFAULT,
66
+ directory: `dist/prod/${id}/pkg`,
67
+ access
68
+ }));
69
+ }
70
+ if (pc && (pc.access === "public" || pc.access === "restricted")) return [new PublishTarget({
71
+ name: pkgName,
72
+ registry: pc.registry ?? NPM_DEFAULT,
73
+ directory: pc.directory ?? ".",
74
+ access: pc.access
75
+ })];
76
+ if (raw.private !== true) return [new PublishTarget({
77
+ name: pkgName,
78
+ registry: pc?.registry ?? NPM_DEFAULT,
79
+ directory: pc?.directory ?? ".",
80
+ access: pc?.access ?? "public"
81
+ })];
82
+ return [];
83
+ }
84
+ /**
85
+ * Resolve a package's publish targets via {@link PublishabilityDetector}, then drop any
86
+ * whose built `directory` package.json is `private: true`. Returned targets keep the
87
+ * detector's original (possibly package-relative) `directory`.
88
+ */
89
+ static resolveTargets(pkg, root) {
90
+ return Effect.gen(function* () {
91
+ const detector = yield* PublishabilityDetector;
92
+ const fs = yield* FileSystem.FileSystem;
93
+ const targets = yield* detector.detect(pkg, root);
94
+ const kept = [];
95
+ for (const t of targets) if (!(yield* isTargetPrivate(fs, isAbsolute(t.directory) ? t.directory : join(pkg.path, t.directory)))) kept.push(t);
96
+ return kept;
97
+ });
98
+ }
99
+ /**
100
+ * The publishable, non-ignored packages, resolved through the single
101
+ * {@link PublishabilityDetector} (which already honors changeset ignore in adaptive mode).
102
+ */
103
+ static listPublishable(root) {
104
+ return Effect.gen(function* () {
105
+ const discovery = yield* WorkspaceDiscovery;
106
+ const detector = yield* PublishabilityDetector;
107
+ const packages = yield* discovery.listPackages().pipe(Effect.orDie);
108
+ const out = [];
109
+ for (const pkg of packages) {
110
+ const targets = yield* detector.detect(pkg, root);
111
+ if (targets.length > 0) out.push({
112
+ name: pkg.name,
113
+ version: pkg.version,
114
+ path: pkg.path,
115
+ targetCount: targets.length
116
+ });
117
+ }
118
+ return out;
119
+ });
120
+ }
121
+ };
122
+ /** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
123
+ const isTargetPrivate = (fs, targetDir) => fs.readFileString(join(targetDir, "package.json")).pipe(Effect.flatMap((content) => Effect.try({
124
+ try: () => JSON.parse(content).private === true,
125
+ catch: () => /* @__PURE__ */ new Error("invalid package.json")
126
+ })), Effect.orElseSucceed(() => false));
127
+ /** Read a raw `package.json` from disk via FileSystem; missing/unreadable/malformed → null. */
128
+ const readRaw = (fs, packageJsonPath) => fs.readFileString(packageJsonPath).pipe(Effect.flatMap((content) => Effect.try({
129
+ try: () => JSON.parse(content),
130
+ catch: () => /* @__PURE__ */ new Error("invalid package.json")
131
+ })), Effect.orElseSucceed(() => null));
132
+ /**
133
+ * Read the bundler's `<pkgPath>/dist/prod/targets.json` binding via FileSystem.
134
+ *
135
+ * @remarks
136
+ * Returns `null` when the file is missing/unreadable/malformed — i.e. before the prod build
137
+ * has run. {@link SilkPublishability.detect} falls back to declared-key placeholders in that
138
+ * case. Used by the silk {@link PublishabilityDetector} layers and the workspace analyzer.
139
+ *
140
+ * @param fs - The FileSystem service.
141
+ * @param pkgPath - Absolute path to the package directory.
142
+ * @since 1.0.0
143
+ */
144
+ const readTargetsBinding = (fs, pkgPath) => fs.readFileString(join(pkgPath, "dist", "prod", "targets.json")).pipe(Effect.flatMap((content) => Effect.try({
145
+ try: () => JSON.parse(content),
146
+ catch: () => /* @__PURE__ */ new Error("invalid targets.json")
147
+ })), Effect.orElseSucceed(() => null));
148
+ /**
149
+ * Override of `workspaces-effect`'s {@link PublishabilityDetector} Tag with pure silk rules.
150
+ *
151
+ * @remarks Requires `FileSystem` (captured at layer build); `detect` reads the raw
152
+ * `package.json` from `pkg.packageJsonPath` and applies {@link SilkPublishability.detect}.
153
+ *
154
+ * @since 0.4.0
155
+ */
156
+ const SilkPublishabilityDetectorLive = Layer.effect(PublishabilityDetector, Effect.gen(function* () {
157
+ const fs = yield* FileSystem.FileSystem;
158
+ return { detect: (pkg, _root) => Effect.gen(function* () {
159
+ const raw = yield* readRaw(fs, pkg.packageJsonPath);
160
+ if (!raw) return [];
161
+ const binding = yield* readTargetsBinding(fs, pkg.path);
162
+ return SilkPublishability.detect(pkg.name, raw, binding);
163
+ }) };
164
+ }));
165
+ /**
166
+ * Ignore-aware override of {@link PublishabilityDetector}. `detect` short-circuits to `[]`
167
+ * for changeset-ignored packages, then dispatches on {@link ChangesetConfig.mode}:
168
+ * `none` → `[]`; `silk` → {@link SilkPublishability.detect}; `vanilla` → the library default.
169
+ *
170
+ * @remarks Requires `FileSystem` + {@link ChangesetConfig} at build.
171
+ *
172
+ * @since 0.4.0
173
+ */
174
+ const PublishabilityDetectorAdaptiveLive = Layer.effect(PublishabilityDetector, Effect.gen(function* () {
175
+ const fs = yield* FileSystem.FileSystem;
176
+ const config = yield* ChangesetConfig;
177
+ const vanilla = yield* Effect.provide(PublishabilityDetector, PublishabilityDetectorLive);
178
+ return { detect: (pkg, root) => Effect.gen(function* () {
179
+ if (yield* config.isIgnored(pkg.name, root)) return [];
180
+ const mode = yield* config.mode(root);
181
+ if (mode === "none") return [];
182
+ if (mode === "silk") {
183
+ const raw = yield* readRaw(fs, pkg.packageJsonPath);
184
+ if (!raw) return [];
185
+ const binding = yield* readTargetsBinding(fs, pkg.path);
186
+ return SilkPublishability.detect(pkg.name, raw, binding);
187
+ }
188
+ return yield* vanilla.detect(pkg, root);
189
+ }) };
190
+ }));
191
+
192
+ //#endregion
193
+ export { SilkPublishability, readTargetsBinding };