@elmeragroup/internal 0.2.0-canary.1 → 0.2.0-canary.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.
Files changed (2) hide show
  1. package/dist/release.mjs +203 -187
  2. package/package.json +6 -6
package/dist/release.mjs CHANGED
@@ -50,157 +50,6 @@ function readJson(path, schema) {
50
50
  return decodeJson(readFileSync(path, "utf8"), schema, path);
51
51
  }
52
52
  //#endregion
53
- //#region ../release/dist/version.js
54
- const numericIdentifier = /^(?:0|[1-9]\d*)$/;
55
- const canaryMarker = "-canary.";
56
- function isNumericIdentifier(value) {
57
- return numericIdentifier.test(value);
58
- }
59
- function stableComponents(version) {
60
- const [major, minor, patch, ...rest] = version.split(".");
61
- if (major === void 0 || minor === void 0 || patch === void 0 || rest.length !== 0) return;
62
- if (!isNumericIdentifier(major) || !isNumericIdentifier(minor) || !isNumericIdentifier(patch)) return;
63
- return [
64
- major,
65
- minor,
66
- patch
67
- ];
68
- }
69
- function canaryComponents(version) {
70
- const marker = version.indexOf(canaryMarker);
71
- if (marker === -1) return void 0;
72
- const base = version.slice(0, marker);
73
- const suffix = version.slice(marker + 8);
74
- if (stableComponents(base) === void 0 || !isNumericIdentifier(suffix)) return void 0;
75
- return {
76
- base,
77
- suffix
78
- };
79
- }
80
- function isStableReleaseVersion(version) {
81
- return stableComponents(version) !== void 0;
82
- }
83
- function isCanaryReleaseVersion(version) {
84
- return canaryComponents(version) !== void 0;
85
- }
86
- function isSupportedReleaseVersion(version) {
87
- return isStableReleaseVersion(version) || isCanaryReleaseVersion(version);
88
- }
89
- function assertReleaseVersion(version) {
90
- if (!isSupportedReleaseVersion(version)) throw new Error(`Unsupported release version ${version}; expected x.y.z or x.y.z-canary.N without leading zeros`);
91
- return version;
92
- }
93
- function assertCanaryReleaseVersion(version) {
94
- assertReleaseVersion(version);
95
- if (!isCanaryReleaseVersion(version)) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
96
- return version;
97
- }
98
- function assertStableReleaseVersion(version) {
99
- if (!isStableReleaseVersion(version)) throw new Error(`Expected a stable version; received ${version}`);
100
- return version;
101
- }
102
- function parseStableVersion(version) {
103
- const components = stableComponents(version);
104
- if (components === void 0) throw new Error(`Expected a stable version; received ${version}`);
105
- const [major, minor, patch] = components;
106
- return {
107
- major: BigInt(major),
108
- minor: BigInt(minor),
109
- patch: BigInt(patch)
110
- };
111
- }
112
- function parseCanaryVersion(version) {
113
- const components = canaryComponents(version);
114
- if (components === void 0) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
115
- return {
116
- base: components.base,
117
- n: BigInt(components.suffix)
118
- };
119
- }
120
- function formatStableVersion(version) {
121
- return `${version.major}.${version.minor}.${version.patch}`;
122
- }
123
- function formatCanaryVersion(version) {
124
- return `${version.base}-canary.${version.n}`;
125
- }
126
- function compareParsedStableVersions(left, right) {
127
- if (left.major !== right.major) return left.major > right.major ? 1 : -1;
128
- if (left.minor !== right.minor) return left.minor > right.minor ? 1 : -1;
129
- if (left.patch !== right.patch) return left.patch > right.patch ? 1 : -1;
130
- return 0;
131
- }
132
- function compareStableVersions(left, right) {
133
- return compareParsedStableVersions(parseStableVersion(left), parseStableVersion(right));
134
- }
135
- function nextPatchVersion(version) {
136
- const parsed = parseStableVersion(version);
137
- return formatStableVersion({
138
- ...parsed,
139
- patch: parsed.patch + 1n
140
- });
141
- }
142
- function compareCanaryVersions(left, right) {
143
- const parsedLeft = parseCanaryVersion(left);
144
- const parsedRight = parseCanaryVersion(right);
145
- const baseOrder = compareStableVersions(parsedLeft.base, parsedRight.base);
146
- if (baseOrder !== 0) return baseOrder;
147
- return parsedLeft.n === parsedRight.n ? 0 : parsedLeft.n > parsedRight.n ? 1 : -1;
148
- }
149
- //#endregion
150
- //#region ../release/dist/intent.js
151
- const releaseRecordOwner = "elmera-release";
152
- /** Asset name of the recorded archive on its GitHub draft release. */
153
- const releaseArchiveName = "release.tgz";
154
- const canaryRecordPrefix = "canary-";
155
- const IntentDocument = Schema.Struct({
156
- schema: Schema.Literal(1),
157
- owner: Schema.optionalKey(Schema.Literal(releaseRecordOwner)),
158
- channel: Schema.Literals(["stable", "canary"]),
159
- version: Schema.String,
160
- commit: Schema.String
161
- });
162
- function isCommit(value) {
163
- return /^[a-f0-9]{40}$/.test(value);
164
- }
165
- function assertCommit(commit) {
166
- if (!isCommit(commit)) throw new Error(`Expected a full commit SHA; received ${commit}`);
167
- return commit;
168
- }
169
- /** Record tag for a canary release of `commit`. */
170
- function canaryRecordTag(commit) {
171
- return `${canaryRecordPrefix}${commit}`;
172
- }
173
- function releaseTag(intent) {
174
- return intent.channel === "stable" ? `v${intent.version}` : canaryRecordTag(intent.commit);
175
- }
176
- function isReleaseTag(tag) {
177
- if (tag.startsWith("v")) return isStableReleaseVersion(tag.slice(1));
178
- return tag.startsWith(canaryRecordPrefix) && isCommit(tag.slice(7));
179
- }
180
- function assertReleaseTag(tag) {
181
- if (!isReleaseTag(tag)) throw new Error("Expected a stable or canary release record tag");
182
- return tag;
183
- }
184
- function parseIntent(text) {
185
- const value = decodeJson(text, IntentDocument, "release intent");
186
- if (value.channel === "stable") assertStableReleaseVersion(value.version);
187
- else assertCanaryReleaseVersion(value.version);
188
- return {
189
- channel: value.channel,
190
- version: value.version,
191
- commit: assertCommit(value.commit)
192
- };
193
- }
194
- function serializeIntent(intent) {
195
- return JSON.stringify({
196
- schema: 1,
197
- owner: releaseRecordOwner,
198
- channel: intent.channel,
199
- version: intent.version,
200
- commit: intent.commit
201
- });
202
- }
203
- //#endregion
204
53
  //#region ../release/dist/archive.js
205
54
  const PackedManifest = Schema.Struct({
206
55
  name: Schema.String,
@@ -210,6 +59,8 @@ const PackedManifest = Schema.Struct({
210
59
  channel: Schema.Literals(["canary", "stable"])
211
60
  })
212
61
  });
62
+ /** Scratch filename inside the scoped temp directory; unrelated to the record's asset name. */
63
+ const scratchArchiveName = "archive.tgz";
213
64
  /** Temporary directory removed when the surrounding Effect scope closes. Process kill is not covered. */
214
65
  function scratchDirectory() {
215
66
  return Effect.acquireRelease(lift(() => mkdtempSync(resolve(tmpdir(), "elmera-release-"))), (directory) => Effect.sync(() => rmSync(directory, {
@@ -225,7 +76,7 @@ function verifyReleaseArchive(intent, bytes, packageName) {
225
76
  return Effect.gen(function* () {
226
77
  const directory = yield* scratchDirectory();
227
78
  return yield* lift(() => {
228
- const archive = resolve(directory, releaseArchiveName);
79
+ const archive = resolve(directory, scratchArchiveName);
229
80
  writeFileSync(archive, bytes);
230
81
  const manifest = decodeJson(execFileSync("tar", [
231
82
  "-xOzf",
@@ -356,6 +207,103 @@ function createGitHubClient(environment) {
356
207
  };
357
208
  }
358
209
  //#endregion
210
+ //#region ../release/dist/version.js
211
+ const numericIdentifier = /^(?:0|[1-9]\d*)$/;
212
+ const canaryMarker = "-canary.";
213
+ function isNumericIdentifier(value) {
214
+ return numericIdentifier.test(value);
215
+ }
216
+ function stableComponents(version) {
217
+ const [major, minor, patch, ...rest] = version.split(".");
218
+ if (major === void 0 || minor === void 0 || patch === void 0 || rest.length !== 0) return;
219
+ if (!isNumericIdentifier(major) || !isNumericIdentifier(minor) || !isNumericIdentifier(patch)) return;
220
+ return [
221
+ major,
222
+ minor,
223
+ patch
224
+ ];
225
+ }
226
+ function canaryComponents(version) {
227
+ const marker = version.indexOf(canaryMarker);
228
+ if (marker === -1) return void 0;
229
+ const base = version.slice(0, marker);
230
+ const suffix = version.slice(marker + 8);
231
+ if (stableComponents(base) === void 0 || !isNumericIdentifier(suffix)) return void 0;
232
+ return {
233
+ base,
234
+ suffix
235
+ };
236
+ }
237
+ function isStableReleaseVersion(version) {
238
+ return stableComponents(version) !== void 0;
239
+ }
240
+ function isCanaryReleaseVersion(version) {
241
+ return canaryComponents(version) !== void 0;
242
+ }
243
+ function isSupportedReleaseVersion(version) {
244
+ return isStableReleaseVersion(version) || isCanaryReleaseVersion(version);
245
+ }
246
+ function assertReleaseVersion(version) {
247
+ if (!isSupportedReleaseVersion(version)) throw new Error(`Unsupported release version ${version}; expected x.y.z or x.y.z-canary.N without leading zeros`);
248
+ return version;
249
+ }
250
+ function assertCanaryReleaseVersion(version) {
251
+ assertReleaseVersion(version);
252
+ if (!isCanaryReleaseVersion(version)) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
253
+ return version;
254
+ }
255
+ function assertStableReleaseVersion(version) {
256
+ if (!isStableReleaseVersion(version)) throw new Error(`Expected a stable version; received ${version}`);
257
+ return version;
258
+ }
259
+ function parseStableVersion(version) {
260
+ const components = stableComponents(version);
261
+ if (components === void 0) throw new Error(`Expected a stable version; received ${version}`);
262
+ const [major, minor, patch] = components;
263
+ return {
264
+ major: BigInt(major),
265
+ minor: BigInt(minor),
266
+ patch: BigInt(patch)
267
+ };
268
+ }
269
+ function parseCanaryVersion(version) {
270
+ const components = canaryComponents(version);
271
+ if (components === void 0) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
272
+ return {
273
+ base: components.base,
274
+ n: BigInt(components.suffix)
275
+ };
276
+ }
277
+ function formatStableVersion(version) {
278
+ return `${version.major}.${version.minor}.${version.patch}`;
279
+ }
280
+ function formatCanaryVersion(version) {
281
+ return `${version.base}-canary.${version.n}`;
282
+ }
283
+ function compareParsedStableVersions(left, right) {
284
+ if (left.major !== right.major) return left.major > right.major ? 1 : -1;
285
+ if (left.minor !== right.minor) return left.minor > right.minor ? 1 : -1;
286
+ if (left.patch !== right.patch) return left.patch > right.patch ? 1 : -1;
287
+ return 0;
288
+ }
289
+ function compareStableVersions(left, right) {
290
+ return compareParsedStableVersions(parseStableVersion(left), parseStableVersion(right));
291
+ }
292
+ function nextPatchVersion(version) {
293
+ const parsed = parseStableVersion(version);
294
+ return formatStableVersion({
295
+ ...parsed,
296
+ patch: parsed.patch + 1n
297
+ });
298
+ }
299
+ function compareCanaryVersions(left, right) {
300
+ const parsedLeft = parseCanaryVersion(left);
301
+ const parsedRight = parseCanaryVersion(right);
302
+ const baseOrder = compareStableVersions(parsedLeft.base, parsedRight.base);
303
+ if (baseOrder !== 0) return baseOrder;
304
+ return parsedLeft.n === parsedRight.n ? 0 : parsedLeft.n > parsedRight.n ? 1 : -1;
305
+ }
306
+ //#endregion
359
307
  //#region ../release/dist/gate.js
360
308
  /** Reads the checked-out manifest's version and requires it to be a stable release version. */
361
309
  function readStableVersion(packageDirectory) {
@@ -393,6 +341,15 @@ function createStableReleaseGate(client, root, packageDirectory, trackedBranch)
393
341
  });
394
342
  }
395
343
  //#endregion
344
+ //#region ../release/dist/intent.js
345
+ function isCommit(value) {
346
+ return /^[a-f0-9]{40}$/.test(value);
347
+ }
348
+ function assertCommit(commit) {
349
+ if (!isCommit(commit)) throw new Error(`Expected a full commit SHA; received ${commit}`);
350
+ return commit;
351
+ }
352
+ //#endregion
396
353
  //#region ../release/dist/git.js
397
354
  function git(cwd, args) {
398
355
  return execFileSync("git", args, {
@@ -635,19 +592,25 @@ function canarySupersession(commit, base, registry, isAncestor, recordedVersion)
635
592
  if (highestStable !== void 0 && compareStableVersions(highestStable, base) >= 0) return "stable-superseded";
636
593
  return "owned";
637
594
  }
638
- /** Whether a fresh canary may be cut for this commit, or which published release already covers it. */
639
- function canaryEligibility(target, registry, isAncestor) {
640
- if (compareStableVersions(target.base, target.current) <= 0) throw new Error("The planned version must advance the stable version");
641
- return canarySupersession(target.commit, target.base, registry, isAncestor);
595
+ const skipReasons = {
596
+ "canary-superseded": "Skipping a commit superseded by a published canary",
597
+ "stable-superseded": "Skipping a commit superseded by a stable release",
598
+ "regressed-base": "Skipping a commit superseded by a canary on a newer base"
599
+ };
600
+ function skip(kind) {
601
+ return {
602
+ skip: kind,
603
+ reason: skipReasons[kind]
604
+ };
642
605
  }
643
606
  /**
644
- * Allocates the next canary number for `base`, or reports `undefined` when a published or reserved
645
- * canary already belongs to a newer base. A regressed base skips; it never blocks publication.
607
+ * Numbers the next canary for `base`, or reports `undefined` when a published or reserved canary
608
+ * already belongs to a newer base. A regressed base skips; it never blocks publication.
646
609
  */
647
- function allocateCanary(base, versions) {
610
+ function nextCanaryVersion(base, taken) {
648
611
  parseStableVersion(base);
649
612
  let highest = -1n;
650
- for (const version of versions) {
613
+ for (const version of taken) {
651
614
  if (!isCanaryReleaseVersion(version)) continue;
652
615
  const candidate = parseCanaryVersion(version);
653
616
  if (compareStableVersions(candidate.base, base) > 0) return void 0;
@@ -658,6 +621,19 @@ function allocateCanary(base, versions) {
658
621
  n: highest + 1n
659
622
  });
660
623
  }
624
+ /**
625
+ * Decides the canary for a checked main commit. Ancestry and the planned base settle whether the
626
+ * commit still owns the channel; published and reserved versions together settle the number, so a
627
+ * reservation is taken even when npm has not seen it yet.
628
+ */
629
+ function decideCanary(target, registry, reserved, isAncestor) {
630
+ if (compareStableVersions(target.base, target.current) <= 0) throw new Error("The planned version must advance the stable version");
631
+ const status = canarySupersession(target.commit, target.base, registry, isAncestor);
632
+ if (status !== "owned") return skip(status);
633
+ const version = nextCanaryVersion(target.base, [...registry.versions.keys(), ...reserved]);
634
+ if (version === void 0) return skip("regressed-base");
635
+ return { cut: version };
636
+ }
661
637
  /** Whether npm already carries this exact archive; a same-version mismatch is fatal. */
662
638
  function npmIdentity(release, registry) {
663
639
  const existing = registry.versions.get(release.version);
@@ -760,7 +736,52 @@ function publishVerifiedRelease(release, deps) {
760
736
  });
761
737
  }
762
738
  //#endregion
763
- //#region ../release/dist/ownership.js
739
+ //#region ../release/dist/record.js
740
+ const releaseRecordOwner = "elmera-release";
741
+ /** Asset name of the recorded archive on its GitHub draft release. */
742
+ const releaseArchiveName = "release.tgz";
743
+ const canaryRecordPrefix = "canary-";
744
+ const IntentDocument = Schema.Struct({
745
+ schema: Schema.Literal(1),
746
+ owner: Schema.optionalKey(Schema.Literal(releaseRecordOwner)),
747
+ channel: Schema.Literals(["stable", "canary"]),
748
+ version: Schema.String,
749
+ commit: Schema.String
750
+ });
751
+ /** Record tag for a canary release of `commit`. */
752
+ function canaryRecordTag(commit) {
753
+ return `${canaryRecordPrefix}${commit}`;
754
+ }
755
+ function releaseTag(intent) {
756
+ return intent.channel === "stable" ? `v${intent.version}` : canaryRecordTag(intent.commit);
757
+ }
758
+ function isReleaseTag(tag) {
759
+ if (tag.startsWith("v")) return isStableReleaseVersion(tag.slice(1));
760
+ return tag.startsWith(canaryRecordPrefix) && isCommit(tag.slice(7));
761
+ }
762
+ function assertReleaseTag(tag) {
763
+ if (!isReleaseTag(tag)) throw new Error("Expected a stable or canary release record tag");
764
+ return tag;
765
+ }
766
+ function parseIntent(text) {
767
+ const value = decodeJson(text, IntentDocument, "release intent");
768
+ if (value.channel === "stable") assertStableReleaseVersion(value.version);
769
+ else assertCanaryReleaseVersion(value.version);
770
+ return {
771
+ channel: value.channel,
772
+ version: value.version,
773
+ commit: assertCommit(value.commit)
774
+ };
775
+ }
776
+ function serializeIntent(intent) {
777
+ return JSON.stringify({
778
+ schema: 1,
779
+ owner: releaseRecordOwner,
780
+ channel: intent.channel,
781
+ version: intent.version,
782
+ commit: intent.commit
783
+ });
784
+ }
764
785
  const IntentProbe = Schema.Struct({
765
786
  schema: Schema.optionalKey(Schema.Json),
766
787
  owner: Schema.optionalKey(Schema.Json)
@@ -780,6 +801,10 @@ function intentMatchingTag(body, tag) {
780
801
  function clearlyForeignIdentity(parsed) {
781
802
  return parsed !== void 0 && parsed.owner !== void 0 && parsed.owner !== "elmera-release";
782
803
  }
804
+ /**
805
+ * Ownership is recognized before intent validation (ADR 0002). An owned or legacy record with an
806
+ * unreadable body throws so it is reported damaged rather than skipped as foreign.
807
+ */
783
808
  function classifyReleaseRecord(tag, body, assetNames) {
784
809
  if (!isReleaseTag(tag)) return { kind: "ignored" };
785
810
  const parsed = tryParseBody(body);
@@ -797,17 +822,6 @@ function classifyReleaseRecord(tag, body, assetNames) {
797
822
  };
798
823
  return { kind: "foreign" };
799
824
  }
800
- //#endregion
801
- //#region ../release/dist/store.js
802
- function assertSameIntent(saved, intended) {
803
- if (saved.commit !== intended.commit || saved.version !== intended.version || saved.channel !== intended.channel) throw new Error("Release intent differs from the saved release");
804
- }
805
- function releaseBodyText(value) {
806
- return value.body ?? "";
807
- }
808
- function assetNames(value) {
809
- return value.assets.map((asset) => asset.name);
810
- }
811
825
  /** `starterAllowed` records that an empty placeholder is only legal on a draft release. */
812
826
  function classifyReleaseAsset(starterAllowed, asset) {
813
827
  if (asset === void 0) return { state: "missing" };
@@ -825,6 +839,17 @@ function classifyReleaseAsset(starterAllowed, asset) {
825
839
  id: asset.id
826
840
  };
827
841
  }
842
+ //#endregion
843
+ //#region ../release/dist/store.js
844
+ function assertSameIntent(saved, intended) {
845
+ if (saved.commit !== intended.commit || saved.version !== intended.version || saved.channel !== intended.channel) throw new Error("Release intent differs from the saved release");
846
+ }
847
+ function releaseBodyText(value) {
848
+ return value.body ?? "";
849
+ }
850
+ function assetNames(value) {
851
+ return value.assets.map((asset) => asset.name);
852
+ }
828
853
  function savedReleaseFrom(value, intent) {
829
854
  const assets = value.assets.filter((asset) => asset.name === releaseArchiveName);
830
855
  if (assets.length > 1) throw new Error("Duplicate release archives");
@@ -993,10 +1018,6 @@ function createReleaseStore(client, packageName) {
993
1018
  }
994
1019
  //#endregion
995
1020
  //#region ../release/dist/engine.js
996
- function skipReason(status) {
997
- return status === "canary-superseded" ? "Skipping a commit superseded by a published canary" : "Skipping a commit superseded by a stable release";
998
- }
999
- const regressedBaseSkip = "Skipping a commit superseded by a canary on a newer base";
1000
1021
  /** Reserves the record, then packs, verifies, and uploads its archive. Resuming never repacks. */
1001
1022
  function recordRelease(intent, adapter, deps) {
1002
1023
  return Effect.gen(function* () {
@@ -1039,24 +1060,19 @@ function mainReleaseIntent(commit, deps) {
1039
1060
  if (recorded !== void 0) return recorded.intent;
1040
1061
  const base = yield* deps.plannedCanaryBase(line.current);
1041
1062
  const registry = yield* deps.readRegistry();
1042
- const status = yield* lift(() => canaryEligibility({
1063
+ const reserved = yield* deps.store.reservedCanaryVersions();
1064
+ const decision = yield* lift(() => decideCanary({
1043
1065
  commit,
1044
1066
  current: line.current,
1045
1067
  base
1046
- }, registry, deps.ancestry));
1047
- if (status !== "owned") {
1048
- deps.log(skipReason(status));
1049
- return;
1050
- }
1051
- const reserved = yield* deps.store.reservedCanaryVersions();
1052
- const version = yield* lift(() => allocateCanary(base, [...registry.versions.keys(), ...reserved]));
1053
- if (version === void 0) {
1054
- deps.log(regressedBaseSkip);
1068
+ }, registry, reserved, deps.ancestry));
1069
+ if ("skip" in decision) {
1070
+ deps.log(decision.reason);
1055
1071
  return;
1056
1072
  }
1057
1073
  return {
1058
1074
  channel: "canary",
1059
- version,
1075
+ version: decision.cut,
1060
1076
  commit
1061
1077
  };
1062
1078
  });
package/package.json CHANGED
@@ -12,13 +12,13 @@
12
12
  "vitest": "4.1.11",
13
13
  "tsdown": "0.23.0",
14
14
  "@elmeragroup/api-extractor": "0.0.0",
15
- "@elmeragroup/oxlint-plugin": "0.0.0",
16
- "@elmeragroup/api-artifacts": "0.0.0",
17
- "@elmeragroup/oxlint-plugin-anti-slop": "0.0.0",
18
15
  "@elmeragroup/typescript-config": "0.0.0",
19
- "@elmeragroup/release": "0.0.0"
16
+ "@elmeragroup/oxlint-plugin-anti-slop": "0.0.0",
17
+ "@elmeragroup/api-artifacts": "0.0.0",
18
+ "@elmeragroup/release": "0.0.0",
19
+ "@elmeragroup/oxlint-plugin": "0.0.0"
20
20
  },
21
- "version": "0.2.0-canary.1",
21
+ "version": "0.2.0-canary.2",
22
22
  "exports": {
23
23
  ".": {
24
24
  "types": "./dist/index.d.mts",
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "sideEffects": false,
71
71
  "elmeraRelease": {
72
- "commit": "53d7c332e91b6755b2b3f546ed3a39320acf914f",
72
+ "commit": "f71523541c8c6ca1a937804a6defa1c518b2d001",
73
73
  "channel": "canary"
74
74
  },
75
75
  "scripts": {