@savvy-web/silk-effects 7.5.3 → 8.0.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.
- package/changesets/api/changelog.js +10 -5
- package/changesets/changelog/getReleaseLine.js +2 -5
- package/changesets/changelog/index.js +39 -19
- package/changesets/index.js +5 -2
- package/changesets/utils/logger.js +47 -20
- package/index.d.ts +171 -1336
- package/index.js +2 -10
- package/lint/cli/sections.js +1 -1
- package/package.json +6 -5
- package/services/BiomeSchemaSync.js +3 -3
- package/services/ChangesetConfigReader.js +3 -4
- package/services/ConfigDiscovery.js +2 -2
- package/services/SilkPublishability.js +2 -3
- package/services/SilkWorkspaceAnalyzer.js +2 -3
- package/errors/BiomeSyncError.js +0 -22
- package/errors/ChangesetConfigError.js +0 -21
- package/errors/ConfigNotFoundError.js +0 -22
- package/errors/PublishTargetBindingError.js +0 -30
- package/errors/WorkspaceAnalysisError.js +0 -22
- package/pr-body/body.js +0 -145
- package/pr-body/diagnostics.js +0 -104
- package/pr-body/index.js +0 -22
- package/pr-body/linked-issue.js +0 -44
- package/pr-body/markers.js +0 -103
- package/pr-body/references.js +0 -157
- package/pr-body/region.js +0 -105
- package/schemas/SavvyInstallSection.js +0 -232
- package/schemas/SavvySections.js +0 -194
- package/schemas/VersioningSchemas.js +0 -87
- package/schemas/WorkspaceAnalysisSchemas.js +0 -209
- package/utils/TrailingSlash.js +0 -18
package/index.js
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
import { SavvyBaseSection, SavvyHooksSection, SavvyToolchainSection, savvyBasePreamble, savvyHooksHygiene, savvyToolSection, savvyToolchainCheck } from "./schemas/SavvySections.js";
|
|
2
|
-
import { ChangesetConfigError } from "./errors/ChangesetConfigError.js";
|
|
3
1
|
import { ChangesetConfigReader } from "./services/ChangesetConfigReader.js";
|
|
4
|
-
import { PublishTargetBindingError } from "./errors/PublishTargetBindingError.js";
|
|
5
2
|
import { ChangesetConfig } from "./services/ChangesetConfig.js";
|
|
6
3
|
import { SilkPublishability, readTargetsBinding } from "./services/SilkPublishability.js";
|
|
7
4
|
import { changesets_exports } from "./changesets/index.js";
|
|
8
5
|
import { commitlint_exports } from "./commitlint/index.js";
|
|
9
|
-
import { BiomeSyncError } from "./errors/BiomeSyncError.js";
|
|
10
|
-
import { ConfigNotFoundError } from "./errors/ConfigNotFoundError.js";
|
|
11
|
-
import { WorkspaceAnalysisError } from "./errors/WorkspaceAnalysisError.js";
|
|
12
6
|
import { lint_exports } from "./lint/index.js";
|
|
13
|
-
import { pr_body_exports } from "./pr-body/index.js";
|
|
14
7
|
import { repos_exports } from "./repos/index.js";
|
|
15
|
-
import { LIFECYCLE_SCRIPTS_CONFIG_KEY, SavvyInstallSection, publishesBuiltLinkDirectory, savvyInstallBlock, savvyInstallDeps } from "./schemas/SavvyInstallSection.js";
|
|
16
|
-
import { AnalyzedWorkspace, SilkPublishConfig, WorkspaceAnalysis } from "./schemas/WorkspaceAnalysisSchemas.js";
|
|
17
8
|
import { BiomeSchemaSync, buildSchemaUrl, extractSemver } from "./services/BiomeSchemaSync.js";
|
|
18
9
|
import { ConfigDiscovery } from "./services/ConfigDiscovery.js";
|
|
19
10
|
import { SilkWorkspaceAnalyzer } from "./services/SilkWorkspaceAnalyzer.js";
|
|
20
11
|
import { turbo_exports } from "./turbo/index.js";
|
|
12
|
+
import { AnalyzedWorkspace, BiomeSyncError, ChangesetConfigError, ConfigNotFoundError, LIFECYCLE_SCRIPTS_CONFIG_KEY, PrBody, PublishTargetBindingError, SavvyBaseSection, SavvyHooksSection, SavvyInstallSection, SavvyToolchainSection, SilkPublishConfig, WorkspaceAnalysis, WorkspaceAnalysisError, publishesBuiltLinkDirectory, savvyBasePreamble, savvyHooksHygiene, savvyInstallBlock, savvyInstallDeps, savvyToolSection, savvyToolchainCheck } from "@savvy-web/silk-core";
|
|
21
13
|
|
|
22
|
-
export { AnalyzedWorkspace, BiomeSchemaSync, BiomeSyncError, ChangesetConfig, ChangesetConfigError, ChangesetConfigReader, changesets_exports as Changesets, commitlint_exports as Commitlint, ConfigDiscovery, ConfigNotFoundError, LIFECYCLE_SCRIPTS_CONFIG_KEY, lint_exports as Lint,
|
|
14
|
+
export { AnalyzedWorkspace, BiomeSchemaSync, BiomeSyncError, ChangesetConfig, ChangesetConfigError, ChangesetConfigReader, changesets_exports as Changesets, commitlint_exports as Commitlint, ConfigDiscovery, ConfigNotFoundError, LIFECYCLE_SCRIPTS_CONFIG_KEY, lint_exports as Lint, PrBody, PublishTargetBindingError, repos_exports as Repos, SavvyBaseSection, SavvyHooksSection, SavvyInstallSection, SavvyToolchainSection, SilkPublishConfig, SilkPublishability, SilkWorkspaceAnalyzer, turbo_exports as Turbo, WorkspaceAnalysis, WorkspaceAnalysisError, buildSchemaUrl, extractSemver, publishesBuiltLinkDirectory, readTargetsBinding, savvyBasePreamble, savvyHooksHygiene, savvyInstallBlock, savvyInstallDeps, savvyToolSection, savvyToolchainCheck };
|
package/lint/cli/sections.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/silk-effects",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.1",
|
|
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",
|
|
@@ -41,9 +41,10 @@
|
|
|
41
41
|
"@effected/package-json": "^0.15.0",
|
|
42
42
|
"@effected/templates": "^0.6.0",
|
|
43
43
|
"@effected/toml": "^0.7.0",
|
|
44
|
-
"@effected/walker": "^0.
|
|
45
|
-
"@effected/yaml": "^0.15.
|
|
44
|
+
"@effected/walker": "^0.9.0",
|
|
45
|
+
"@effected/yaml": "^0.15.1",
|
|
46
46
|
"@manypkg/get-packages": "^3.1.0",
|
|
47
|
+
"@savvy-web/silk-core": "0.1.1",
|
|
47
48
|
"mdast-util-heading-range": "^4.0.0",
|
|
48
49
|
"mdast-util-to-string": "^4.0.0",
|
|
49
50
|
"remark-gfm": "^4.0.1",
|
|
@@ -56,8 +57,8 @@
|
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
59
|
"@effected/commands": "^0.7.0",
|
|
59
|
-
"@effected/git": "^0.15.
|
|
60
|
-
"@effected/workspaces": "^0.21.
|
|
60
|
+
"@effected/git": "^0.15.1",
|
|
61
|
+
"@effected/workspaces": "^0.21.1",
|
|
61
62
|
"effect": "4.0.0-rc.115"
|
|
62
63
|
}
|
|
63
64
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BiomeSyncError } from "
|
|
1
|
+
import { BiomeSyncError } from "@savvy-web/silk-core";
|
|
2
2
|
import { Context, Effect, FileSystem, Layer } from "effect";
|
|
3
3
|
import { Jsonc, JsoncEdit, JsoncModifier } from "@effected/jsonc";
|
|
4
4
|
|
|
@@ -60,7 +60,7 @@ function findBiomeConfigs(cwd, fs) {
|
|
|
60
60
|
* const result = await Effect.runPromise(
|
|
61
61
|
* Effect.gen(function* () {
|
|
62
62
|
* const syncer = yield* BiomeSchemaSync;
|
|
63
|
-
* return yield* syncer.sync("^1.9.3");
|
|
63
|
+
* return yield* syncer.sync("^1.9.3", { cwd: "/path/to/repo" });
|
|
64
64
|
* }).pipe(
|
|
65
65
|
* Effect.provide(BiomeSchemaSync.layer),
|
|
66
66
|
* Effect.provide(NodeServices.layer),
|
|
@@ -85,7 +85,7 @@ var BiomeSchemaSync = class extends Context.Service()("@savvy-web/silk-effects/B
|
|
|
85
85
|
static layer = Layer.effect(this, Effect.gen(function* () {
|
|
86
86
|
const fs = yield* FileSystem.FileSystem;
|
|
87
87
|
const run = (version, options, write) => Effect.gen(function* () {
|
|
88
|
-
const cwd = options
|
|
88
|
+
const { cwd } = options;
|
|
89
89
|
const expectedUrl = buildSchemaUrl(extractSemver(version));
|
|
90
90
|
const configs = yield* findBiomeConfigs(cwd, fs);
|
|
91
91
|
const updated = [];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { ChangesetConfigError } from "
|
|
2
|
-
import { ChangesetConfigFile, SilkChangesetConfigFile } from "../schemas/VersioningSchemas.js";
|
|
1
|
+
import { ChangesetConfigError, ChangesetConfigFile, SilkChangesetConfigFile } from "@savvy-web/silk-core";
|
|
3
2
|
import { Context, Effect, FileSystem, Layer, Schema } from "effect";
|
|
4
3
|
|
|
5
4
|
//#region src/services/ChangesetConfigReader.ts
|
|
@@ -31,8 +30,8 @@ function isSilkChangelog(changelog) {
|
|
|
31
30
|
*
|
|
32
31
|
* @remarks
|
|
33
32
|
* Automatically detects whether the config uses the Silk changelog adapter
|
|
34
|
-
* (`@savvy-web/changesets`) and decodes as
|
|
35
|
-
* standard
|
|
33
|
+
* (`@savvy-web/changesets`) and decodes as `SilkChangesetConfigFile` or the
|
|
34
|
+
* standard `ChangesetConfigFile` accordingly.
|
|
36
35
|
*
|
|
37
36
|
* @example
|
|
38
37
|
* ```typescript
|
|
@@ -22,7 +22,7 @@ function safeExists(fs, path) {
|
|
|
22
22
|
* const result = await Effect.runPromise(
|
|
23
23
|
* Effect.gen(function* () {
|
|
24
24
|
* const discovery = yield* ConfigDiscovery;
|
|
25
|
-
* return yield* discovery.find("biome.json");
|
|
25
|
+
* return yield* discovery.find("biome.json", { cwd: "/path/to/repo" });
|
|
26
26
|
* }).pipe(
|
|
27
27
|
* Effect.provide(ConfigDiscovery.layer),
|
|
28
28
|
* Effect.provide(NodeServices.layer),
|
|
@@ -47,7 +47,7 @@ var ConfigDiscovery = class extends Context.Service()("@savvy-web/silk-effects/C
|
|
|
47
47
|
static layer = Layer.effect(this, Effect.gen(function* () {
|
|
48
48
|
const fs = yield* FileSystem.FileSystem;
|
|
49
49
|
const findAll = (name, options) => Effect.gen(function* () {
|
|
50
|
-
const cwd = options
|
|
50
|
+
const { cwd } = options;
|
|
51
51
|
const results = [];
|
|
52
52
|
const libPath = `${cwd}/lib/configs/${name}`;
|
|
53
53
|
if (yield* safeExists(fs, libPath)) results.push({
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { PublishTargetBindingError } from "../errors/PublishTargetBindingError.js";
|
|
2
|
-
import { trimTrailingSlashes } from "../utils/TrailingSlash.js";
|
|
3
1
|
import { ChangesetConfig } from "./ChangesetConfig.js";
|
|
2
|
+
import { PublishTargetBindingError, trimTrailingSlashes } from "@savvy-web/silk-core";
|
|
4
3
|
import { Effect, FileSystem, Layer } from "effect";
|
|
5
4
|
import { isAbsolute, join, relative } from "node:path";
|
|
6
5
|
import { PublishTarget, PublishabilityDetector, WorkspaceDiscovery } from "@effected/workspaces";
|
|
@@ -127,7 +126,7 @@ var SilkPublishability = class SilkPublishability {
|
|
|
127
126
|
* directories it names. A directory outside the binding means detection did
|
|
128
127
|
* not select the prod output — the `yaml-effect@0.7.1` shape, where a dev
|
|
129
128
|
* manifest carrying `catalog:` specifiers was packed and published. Rather
|
|
130
|
-
* than ship those bytes, fail with
|
|
129
|
+
* than ship those bytes, fail with `PublishTargetBindingError`.
|
|
131
130
|
*
|
|
132
131
|
* Before the prod build runs there is no binding, and the detector's
|
|
133
132
|
* placeholder directories are left alone.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ChangesetConfigReader } from "./ChangesetConfigReader.js";
|
|
2
2
|
import { ChangesetConfig } from "./ChangesetConfig.js";
|
|
3
3
|
import { SilkPublishability, readTargetsBinding } from "./SilkPublishability.js";
|
|
4
|
-
import { WorkspaceAnalysisError } from "
|
|
5
|
-
import { AnalyzedWorkspace, WorkspaceAnalysis } from "../schemas/WorkspaceAnalysisSchemas.js";
|
|
4
|
+
import { AnalyzedWorkspace, WorkspaceAnalysis, WorkspaceAnalysisError } from "@savvy-web/silk-core";
|
|
6
5
|
import { Context, Effect, FileSystem, Layer, Option } from "effect";
|
|
7
6
|
import { DependencyGraph, PackageManagerDetector, VersioningStrategy, WorkspaceDiscovery } from "@effected/workspaces";
|
|
8
7
|
|
|
@@ -16,7 +15,7 @@ import { DependencyGraph, PackageManagerDetector, VersioningStrategy, WorkspaceD
|
|
|
16
15
|
* Orchestrates `WorkspaceDiscovery`, `PackageManagerDetector` and
|
|
17
16
|
* {@link ChangesetConfigReader}, then classifies the result with
|
|
18
17
|
* `@effected/workspaces`' pure `VersioningStrategy` value class, to produce a
|
|
19
|
-
* complete
|
|
18
|
+
* complete `WorkspaceAnalysis` for a given workspace root.
|
|
20
19
|
*
|
|
21
20
|
* @example
|
|
22
21
|
* ```typescript
|
package/errors/BiomeSyncError.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Data } from "effect";
|
|
2
|
-
|
|
3
|
-
//#region src/errors/BiomeSyncError.ts
|
|
4
|
-
/**
|
|
5
|
-
* Raised when a Biome config file cannot be read or its `$schema` URL cannot be updated.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* Returned by `BiomeSchemaSync.sync` and `BiomeSchemaSync.check` when
|
|
9
|
-
* a `biome.json` or `biome.jsonc` file exists but cannot be read, contains invalid JSON,
|
|
10
|
-
* or cannot be written back to disk.
|
|
11
|
-
*
|
|
12
|
-
* @since 0.1.0
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
var BiomeSyncError = class extends Data.TaggedError("BiomeSyncError") {
|
|
16
|
-
get message() {
|
|
17
|
-
return `Failed to sync biome schema in ${this.path}: ${this.reason}`;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { BiomeSyncError };
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Data } from "effect";
|
|
2
|
-
|
|
3
|
-
//#region src/errors/ChangesetConfigError.ts
|
|
4
|
-
/**
|
|
5
|
-
* Raised when the `.changeset/config.json` file cannot be read or decoded.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* Returned by `ChangesetConfigReader.read` when the file is missing,
|
|
9
|
-
* contains invalid JSON, or fails Effect Schema validation.
|
|
10
|
-
*
|
|
11
|
-
* @since 0.1.0
|
|
12
|
-
* @public
|
|
13
|
-
*/
|
|
14
|
-
var ChangesetConfigError = class extends Data.TaggedError("ChangesetConfigError") {
|
|
15
|
-
get message() {
|
|
16
|
-
return `Failed to read changeset config at ${this.path}: ${this.reason}`;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
21
|
-
export { ChangesetConfigError };
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Data } from "effect";
|
|
2
|
-
|
|
3
|
-
//#region src/errors/ConfigNotFoundError.ts
|
|
4
|
-
/**
|
|
5
|
-
* Raised when a config file cannot be located in any of the expected locations.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* Returned by consumers that require a config file to exist. `ConfigDiscovery.find`
|
|
9
|
-
* itself returns `null` instead of failing — callers that need a hard failure should
|
|
10
|
-
* map `null` to this error.
|
|
11
|
-
*
|
|
12
|
-
* @since 0.1.0
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
var ConfigNotFoundError = class extends Data.TaggedError("ConfigNotFoundError") {
|
|
16
|
-
get message() {
|
|
17
|
-
return `Config '${this.name}' not found. Searched: ${this.searchedPaths.join(", ")}`;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { ConfigNotFoundError };
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Data } from "effect";
|
|
2
|
-
|
|
3
|
-
//#region src/errors/PublishTargetBindingError.ts
|
|
4
|
-
/**
|
|
5
|
-
* Raised when publishability detection selects a directory that the package's
|
|
6
|
-
* `dist/prod/targets.json` binding does not describe.
|
|
7
|
-
*
|
|
8
|
-
* @remarks
|
|
9
|
-
* The bundler's prod build writes `targets.json` naming every byte-group
|
|
10
|
-
* directory it produced. Once that binding exists it is authoritative: the only
|
|
11
|
-
* directories whose bytes may be published are the ones it lists. A detector
|
|
12
|
-
* that returns anything else — most often `publishConfig.directory` pointing at
|
|
13
|
-
* a **dev** build, because silk mode was misdetected — is about to pack an
|
|
14
|
-
* unresolved workspace manifest and ship it to a registry.
|
|
15
|
-
*
|
|
16
|
-
* That is the `yaml-effect@0.7.1` failure: detection picked `dist/dev/pkg`, the
|
|
17
|
-
* dev manifest still carried `catalog:` specifiers, and the published package
|
|
18
|
-
* was uninstallable (`EUNSUPPORTEDPROTOCOL`).
|
|
19
|
-
*
|
|
20
|
-
* @since 3.1.0
|
|
21
|
-
* @public
|
|
22
|
-
*/
|
|
23
|
-
var PublishTargetBindingError = class extends Data.TaggedError("PublishTargetBindingError") {
|
|
24
|
-
get message() {
|
|
25
|
-
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.`;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
//#endregion
|
|
30
|
-
export { PublishTargetBindingError };
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Data } from "effect";
|
|
2
|
-
|
|
3
|
-
//#region src/errors/WorkspaceAnalysisError.ts
|
|
4
|
-
/**
|
|
5
|
-
* Raised when workspace analysis fails for a given root directory.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* Returned by `SilkWorkspaceAnalyzer.analyze` when the analysis pipeline
|
|
9
|
-
* encounters an unrecoverable error — e.g. workspace discovery failure,
|
|
10
|
-
* package manager detection failure, or publishability detection errors.
|
|
11
|
-
*
|
|
12
|
-
* @since 0.2.0
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
var WorkspaceAnalysisError = class extends Data.TaggedError("WorkspaceAnalysisError") {
|
|
16
|
-
get message() {
|
|
17
|
-
return `Workspace analysis failed at ${this.root}: ${this.reason}`;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { WorkspaceAnalysisError };
|
package/pr-body/body.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { Region } from "./region.js";
|
|
2
|
-
import { Markers } from "./markers.js";
|
|
3
|
-
import { ClosingReferences, OwnedAttribute } from "./references.js";
|
|
4
|
-
|
|
5
|
-
//#region src/pr-body/body.ts
|
|
6
|
-
const FENCE = "```";
|
|
7
|
-
/**
|
|
8
|
-
* The marker-delimited reference region, merging this run's references with
|
|
9
|
-
* any an agent added.
|
|
10
|
-
*
|
|
11
|
-
* @remarks
|
|
12
|
-
* Module-private: the region is always emitted, empty or not, so an agent
|
|
13
|
-
* always has a target to write into — the same reservation the summary
|
|
14
|
-
* region makes.
|
|
15
|
-
*/
|
|
16
|
-
const buildReferencesRegion = (args) => {
|
|
17
|
-
const known = new Set(args.linkedIssues.map((issue) => issue.number));
|
|
18
|
-
const ownedNow = ClosingReferences.fromIssues(args.linkedIssues).dedupe();
|
|
19
|
-
const agentAdded = [...new Set(ClosingReferences.parseBare(args.carriedReferences))].filter((id) => !args.previouslyOwned.has(id) && !known.has(id)).sort((a, b) => a - b);
|
|
20
|
-
const lines = ClosingReferences.make({ ids: [...ownedNow.ids, ...agentAdded] }).renderBareLines();
|
|
21
|
-
return `${`${Markers.REFERENCES_START_PREFIX} ${OwnedAttribute.render(ownedNow.ids)} -->`}\n${lines === "" ? "" : `${lines}\n`}${Markers.REFERENCES_END}`;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* The proposed squash-commit message, fenced for an AI integration to
|
|
25
|
-
* rewrite.
|
|
26
|
-
*
|
|
27
|
-
* @remarks
|
|
28
|
-
* Module-private. Carries its own `Closes` references because the squash
|
|
29
|
-
* commit needs them too — a reference inside a fenced block is inert to
|
|
30
|
-
* GitHub's linker, which is precisely why the plain-text lines outside it
|
|
31
|
-
* are separate rather than shared.
|
|
32
|
-
*/
|
|
33
|
-
const buildSquashBlock = (args) => {
|
|
34
|
-
const closing = ClosingReferences.fromIssues(args.linkedIssues).renderTrailer();
|
|
35
|
-
const message = [
|
|
36
|
-
args.subject,
|
|
37
|
-
closing,
|
|
38
|
-
args.signoff
|
|
39
|
-
].filter((part) => part !== "").join("\n\n");
|
|
40
|
-
return `${FENCE}${Markers.SQUASH_FENCE_LANGUAGE}\n${message}\n${FENCE}`;
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* The shared managed-PR-body renderer and its carry-through readers — the
|
|
44
|
-
* single implementation of the contract `silk-release-action` dogfooded at
|
|
45
|
-
* `src/utils/pr-body.ts` (savvy-web/systems#419).
|
|
46
|
-
*
|
|
47
|
-
* @remarks
|
|
48
|
-
* Every operation is pure and total: markers absent, regions broken, or
|
|
49
|
-
* attributes malformed all degrade to the documented fail-safe result
|
|
50
|
-
* (preserve too much rather than delete someone's work) instead of failing —
|
|
51
|
-
* a regenerating action must still produce a body when the prior one is
|
|
52
|
-
* malformed. Use `PrBodyDiagnostic.scan` where a writer wants to be told
|
|
53
|
-
* about a broken pair instead of tolerating it.
|
|
54
|
-
*
|
|
55
|
-
* @public
|
|
56
|
-
*/
|
|
57
|
-
var ManagedPrBody = class ManagedPrBody {
|
|
58
|
-
constructor() {}
|
|
59
|
-
/**
|
|
60
|
-
* Build the region of the PR description the generating run owns.
|
|
61
|
-
*
|
|
62
|
-
* @remarks
|
|
63
|
-
* Delimited by `Markers.MANAGED_START` / `Markers.MANAGED_END` so
|
|
64
|
-
* {@link ManagedPrBody.upsert} can regenerate it without touching
|
|
65
|
-
* anything a human wrote around it. Layout, in order: the reserved
|
|
66
|
-
* summary region (nothing may sit above it — a reader meets the prose
|
|
67
|
-
* before the machinery), the proposed-squash-commit fence, and the
|
|
68
|
-
* bare-reference region. No preamble, no file listing, no linked-issues
|
|
69
|
-
* list, no run attribution — each said something already on the page.
|
|
70
|
-
*
|
|
71
|
-
* @public
|
|
72
|
-
*/
|
|
73
|
-
static build(options) {
|
|
74
|
-
const parts = [];
|
|
75
|
-
parts.push(`${Markers.SUMMARY_START}\n${options.summary === "" ? "" : `${options.summary}\n`}${Markers.SUMMARY_END}`);
|
|
76
|
-
parts.push(buildSquashBlock({
|
|
77
|
-
subject: options.subject,
|
|
78
|
-
linkedIssues: options.linkedIssues,
|
|
79
|
-
signoff: options.signoff
|
|
80
|
-
}));
|
|
81
|
-
const priorBody = options.priorBody ?? "";
|
|
82
|
-
parts.push(buildReferencesRegion({
|
|
83
|
-
linkedIssues: options.linkedIssues,
|
|
84
|
-
carriedReferences: ManagedPrBody.extractReferences(priorBody),
|
|
85
|
-
previouslyOwned: OwnedAttribute.parse(priorBody)
|
|
86
|
-
}));
|
|
87
|
-
return `${Markers.MANAGED_START}\n${parts.join("\n\n")}\n${Markers.MANAGED_END}`;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Put `managed` (a full {@link ManagedPrBody.build} result) into
|
|
91
|
-
* `existing`, replacing a previous managed region and leaving everything
|
|
92
|
-
* else alone.
|
|
93
|
-
*
|
|
94
|
-
* @remarks
|
|
95
|
-
* Human edits outside the markers survive; a body with no markers keeps
|
|
96
|
-
* its content and gains the region below it. See `Region.upsert` for the
|
|
97
|
-
* splice semantics.
|
|
98
|
-
*
|
|
99
|
-
* @public
|
|
100
|
-
*/
|
|
101
|
-
static upsert(existing, managed) {
|
|
102
|
-
return Region.upsert(existing, "silk-release", managed);
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* The summary region's current content, or `""` when it is empty or
|
|
106
|
-
* absent.
|
|
107
|
-
*
|
|
108
|
-
* @remarks
|
|
109
|
-
* Extraction exists because the managed region is REGENERATED on every
|
|
110
|
-
* run. Re-emitting the region empty would delete a summary the moment
|
|
111
|
-
* any commit landed — destructive and silent, with no signal back to the
|
|
112
|
-
* summariser whose work was discarded. Feed the result to
|
|
113
|
-
* {@link ManagedPrBody.build}'s `summary` option.
|
|
114
|
-
*
|
|
115
|
-
* @public
|
|
116
|
-
*/
|
|
117
|
-
static extractSummary(existing) {
|
|
118
|
-
const from = existing.indexOf(Markers.SUMMARY_START);
|
|
119
|
-
const to = existing.indexOf(Markers.SUMMARY_END);
|
|
120
|
-
if (from === -1 || to === -1 || to < from) return "";
|
|
121
|
-
return existing.slice(from + Markers.SUMMARY_START.length, to).trim();
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* The reference region's current content, or `""` when it is empty or
|
|
125
|
-
* absent.
|
|
126
|
-
*
|
|
127
|
-
* @remarks
|
|
128
|
-
* Symmetric to {@link ManagedPrBody.extractSummary}, and for the same
|
|
129
|
-
* reason. Located by `Markers.REFERENCES_START_PREFIX` — never the plain
|
|
130
|
-
* opening constant — because a generating run emits the attributed form.
|
|
131
|
-
*
|
|
132
|
-
* @public
|
|
133
|
-
*/
|
|
134
|
-
static extractReferences(existing) {
|
|
135
|
-
const from = existing.indexOf(Markers.REFERENCES_START_PREFIX);
|
|
136
|
-
const to = existing.indexOf(Markers.REFERENCES_END);
|
|
137
|
-
if (from === -1 || to === -1 || to < from) return "";
|
|
138
|
-
const openEnd = existing.indexOf("-->", from);
|
|
139
|
-
if (openEnd === -1 || openEnd > to) return "";
|
|
140
|
-
return existing.slice(openEnd + 3, to).trim();
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
//#endregion
|
|
145
|
-
export { ManagedPrBody };
|
package/pr-body/diagnostics.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { Region } from "./region.js";
|
|
2
|
-
import { Markers } from "./markers.js";
|
|
3
|
-
import { Schema } from "effect";
|
|
4
|
-
|
|
5
|
-
//#region src/pr-body/diagnostics.ts
|
|
6
|
-
/**
|
|
7
|
-
* The problems {@link PrBodyDiagnostic.scan} can report about a body's
|
|
8
|
-
* markers.
|
|
9
|
-
*
|
|
10
|
-
* @public
|
|
11
|
-
*/
|
|
12
|
-
const PrBodyDiagnosticCode = Schema.Literals(["unpairedMarker", "duplicateMarker"]);
|
|
13
|
-
/**
|
|
14
|
-
* One problem with a body's silk-release markers.
|
|
15
|
-
*
|
|
16
|
-
* @remarks
|
|
17
|
-
* Diagnostics are advisory VALUES, not a typed error channel: every parse and
|
|
18
|
-
* render operation in this namespace is deliberately total (a regenerating
|
|
19
|
-
* action must still produce a body when the prior one is malformed, and the
|
|
20
|
-
* fail-safe direction is to preserve too much rather than delete someone's
|
|
21
|
-
* work). `scan` exists for the writer that wants to be told about a broken
|
|
22
|
-
* pair before editing — the `pr-body` skill instructs an agent that finds a
|
|
23
|
-
* region missing its pair to stop and report rather than guess, and this is
|
|
24
|
-
* the check that instruction points at. A misplaced marker pair is worse than
|
|
25
|
-
* none: it makes the next regeneration rewrite content it does not own.
|
|
26
|
-
*
|
|
27
|
-
* @public
|
|
28
|
-
*/
|
|
29
|
-
var PrBodyDiagnostic = class PrBodyDiagnostic extends Schema.Class("PrBodyDiagnostic")({
|
|
30
|
-
code: PrBodyDiagnosticCode,
|
|
31
|
-
/** The region token the problem is about, e.g. `silk-release:summary`. */
|
|
32
|
-
token: Schema.String
|
|
33
|
-
}) {
|
|
34
|
-
/**
|
|
35
|
-
* A human-readable description, derived from the structured fields.
|
|
36
|
-
*
|
|
37
|
-
* @public
|
|
38
|
-
*/
|
|
39
|
-
get message() {
|
|
40
|
-
return this.code === "unpairedMarker" ? `region "${this.token}" has an unpaired or out-of-order marker — its :start and :end must both be present, in order` : `region "${this.token}" appears more than once — each silk-release region must be a single pair`;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Every marker problem in `body`, or an empty array when the markers are
|
|
44
|
-
* well-formed (including entirely absent — an unmanaged body is not a
|
|
45
|
-
* defect).
|
|
46
|
-
*
|
|
47
|
-
* @remarks
|
|
48
|
-
* The references region is located by its attributed opening prefix, so a
|
|
49
|
-
* marker carrying an `owned="…"` attribute counts as present.
|
|
50
|
-
*
|
|
51
|
-
* @public
|
|
52
|
-
*/
|
|
53
|
-
static scan(body) {
|
|
54
|
-
const diagnostics = [];
|
|
55
|
-
const tokens = [
|
|
56
|
-
["silk-release", Markers.MANAGED_START],
|
|
57
|
-
["silk-release:summary", Markers.SUMMARY_START],
|
|
58
|
-
["silk-release:references", Markers.REFERENCES_START_PREFIX]
|
|
59
|
-
];
|
|
60
|
-
for (const [token, openMarker] of tokens) {
|
|
61
|
-
const opens = countOccurrences(body, openMarker);
|
|
62
|
-
const closes = countOccurrences(body, Region.end(token));
|
|
63
|
-
if (opens === 0 && closes === 0) continue;
|
|
64
|
-
if (opens !== closes) {
|
|
65
|
-
diagnostics.push(PrBodyDiagnostic.make({
|
|
66
|
-
code: "unpairedMarker",
|
|
67
|
-
token
|
|
68
|
-
}));
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (opens > 1) {
|
|
72
|
-
diagnostics.push(PrBodyDiagnostic.make({
|
|
73
|
-
code: "duplicateMarker",
|
|
74
|
-
token
|
|
75
|
-
}));
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (body.indexOf(Region.end(token)) < body.indexOf(openMarker)) diagnostics.push(PrBodyDiagnostic.make({
|
|
79
|
-
code: "unpairedMarker",
|
|
80
|
-
token
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
return diagnostics;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* Non-overlapping occurrences of `needle` in `haystack`.
|
|
88
|
-
*
|
|
89
|
-
* @remarks
|
|
90
|
-
* Module-private on purpose — it exists only to keep `scan` readable.
|
|
91
|
-
*/
|
|
92
|
-
const countOccurrences = (haystack, needle) => {
|
|
93
|
-
let count = 0;
|
|
94
|
-
let from = 0;
|
|
95
|
-
for (;;) {
|
|
96
|
-
const at = haystack.indexOf(needle, from);
|
|
97
|
-
if (at === -1) return count;
|
|
98
|
-
count += 1;
|
|
99
|
-
from = at + needle.length;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
//#endregion
|
|
104
|
-
export { PrBodyDiagnostic, PrBodyDiagnosticCode };
|
package/pr-body/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { Region } from "./region.js";
|
|
3
|
-
import { Markers } from "./markers.js";
|
|
4
|
-
import { LinkedIssueRef } from "./linked-issue.js";
|
|
5
|
-
import { ClosingReferences, OwnedAttribute } from "./references.js";
|
|
6
|
-
import { ManagedPrBody } from "./body.js";
|
|
7
|
-
import { PrBodyDiagnostic, PrBodyDiagnosticCode } from "./diagnostics.js";
|
|
8
|
-
|
|
9
|
-
//#region src/pr-body/index.ts
|
|
10
|
-
var pr_body_exports = /* @__PURE__ */ __exportAll({
|
|
11
|
-
ClosingReferences: () => ClosingReferences,
|
|
12
|
-
LinkedIssueRef: () => LinkedIssueRef,
|
|
13
|
-
ManagedPrBody: () => ManagedPrBody,
|
|
14
|
-
Markers: () => Markers,
|
|
15
|
-
OwnedAttribute: () => OwnedAttribute,
|
|
16
|
-
PrBodyDiagnostic: () => PrBodyDiagnostic,
|
|
17
|
-
PrBodyDiagnosticCode: () => PrBodyDiagnosticCode,
|
|
18
|
-
Region: () => Region
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { ClosingReferences, LinkedIssueRef, ManagedPrBody, Markers, OwnedAttribute, PrBodyDiagnostic, PrBodyDiagnosticCode, Region, pr_body_exports };
|
package/pr-body/linked-issue.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
|
|
3
|
-
//#region src/pr-body/linked-issue.ts
|
|
4
|
-
/**
|
|
5
|
-
* The minimum an issue must carry to appear in a managed PR body.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* `state` is deliberately a tolerant `Schema.String` rather than a literal
|
|
9
|
-
* union: GitHub's REST API reports `"open"`/`"closed"` while GraphQL reports
|
|
10
|
-
* `"OPEN"`/`"CLOSED"`, and both actions pass their existing issue shapes
|
|
11
|
-
* through unchanged. **`LinkedIssueRef.isClosed` is the ONLY sanctioned way
|
|
12
|
-
* to test closedness** — it lowercases before comparing, so both spellings
|
|
13
|
-
* classify correctly. A hand-written `issue.state === "closed"` comparison
|
|
14
|
-
* silently misclassifies GraphQL's `"CLOSED"` as open, which re-links (and on
|
|
15
|
-
* merge auto-closes) an issue the release deliberately dropped.
|
|
16
|
-
*
|
|
17
|
-
* The class carries no instance members, so a plain
|
|
18
|
-
* `{ number, title, state }` literal satisfies the type structurally — both
|
|
19
|
-
* actions' existing `LinkedIssue` shapes are accepted without mapping.
|
|
20
|
-
*
|
|
21
|
-
* @public
|
|
22
|
-
*/
|
|
23
|
-
var LinkedIssueRef = class extends Schema.Class("LinkedIssueRef")({
|
|
24
|
-
number: Schema.Number.check(Schema.isInt()).check(Schema.isGreaterThan(0)),
|
|
25
|
-
title: Schema.String,
|
|
26
|
-
state: Schema.String
|
|
27
|
-
}) {
|
|
28
|
-
/**
|
|
29
|
-
* Whether an issue is closed, case-insensitively.
|
|
30
|
-
*
|
|
31
|
-
* @remarks
|
|
32
|
-
* The only sanctioned closedness test — see the class remarks for why a
|
|
33
|
-
* bare `state === "closed"` comparison is a silent bug against GraphQL
|
|
34
|
-
* payloads.
|
|
35
|
-
*
|
|
36
|
-
* @public
|
|
37
|
-
*/
|
|
38
|
-
static isClosed(issue) {
|
|
39
|
-
return issue.state.toLowerCase() === "closed";
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
//#endregion
|
|
44
|
-
export { LinkedIssueRef };
|