sh3-core 0.16.0 → 0.16.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/dist/build.d.ts CHANGED
@@ -36,7 +36,34 @@ export interface Sh3ArtifactOptions {
36
36
  serverEntry?: string;
37
37
  /** Override manifest fields not extractable from code (description, author, requires). */
38
38
  manifest?: Partial<Pick<ArtifactManifest, 'description' | 'author' | 'requires'>>;
39
+ /**
40
+ * Append a semver build-metadata suffix to the artifact `version` written
41
+ * into `manifest.json`. Per ADR-013 the package.json version remains the
42
+ * release-version source; this option layers an artifact-level counter on
43
+ * top so test iterations and hotfix variants of the same release can ship
44
+ * as distinct artifacts without bumping the release number.
45
+ *
46
+ * - omit / undefined: artifact version equals package.json.version (default).
47
+ * - 'auto': resolved from `git describe --tags --abbrev=0` →
48
+ * `git rev-list --count <tag>..HEAD`. Suffix is omitted when the
49
+ * count is 0 (canonical release) or when git is unavailable.
50
+ * - string: literal suffix. Empty string is treated as no suffix.
51
+ *
52
+ * Example: package.json says "1.2.3", git is 4 commits past `v1.2.3` →
53
+ * manifest.json says `"version": "1.2.3+4"`.
54
+ */
55
+ buildSuffix?: 'auto' | string;
39
56
  }
57
+ /**
58
+ * Compose a release version (`pkgVersion`) with an optional semver
59
+ * build-metadata suffix. Pure function — no I/O — for testability.
60
+ *
61
+ * Returns `pkgVersion` unchanged when `suffix` is empty/undefined.
62
+ * Throws when `pkgVersion` already carries build metadata or when the
63
+ * suffix is not a valid semver build-metadata identifier
64
+ * (dot-separated `[0-9A-Za-z-]+`).
65
+ */
66
+ export declare function composeArtifactVersion(pkgVersion: string, suffix: string | undefined): string;
40
67
  /**
41
68
  * Vite plugin that produces a distributable artifact directory after build.
42
69
  *
package/dist/build.js CHANGED
@@ -17,6 +17,7 @@
17
17
  * });
18
18
  */
19
19
  import { readFileSync, writeFileSync, unlinkSync, readdirSync, copyFileSync, existsSync } from 'node:fs';
20
+ import { execSync } from 'node:child_process';
20
21
  import { join } from 'node:path';
21
22
  /**
22
23
  * Vite plugin that inlines extracted CSS into the JS bundle.
@@ -83,6 +84,52 @@ export function sh3CssInline() {
83
84
  },
84
85
  };
85
86
  }
87
+ /**
88
+ * Compose a release version (`pkgVersion`) with an optional semver
89
+ * build-metadata suffix. Pure function — no I/O — for testability.
90
+ *
91
+ * Returns `pkgVersion` unchanged when `suffix` is empty/undefined.
92
+ * Throws when `pkgVersion` already carries build metadata or when the
93
+ * suffix is not a valid semver build-metadata identifier
94
+ * (dot-separated `[0-9A-Za-z-]+`).
95
+ */
96
+ export function composeArtifactVersion(pkgVersion, suffix) {
97
+ if (!suffix)
98
+ return pkgVersion;
99
+ if (pkgVersion.includes('+')) {
100
+ throw new Error(`[sh3-artifact] package.json version "${pkgVersion}" already contains semver build metadata; cannot append additional suffix "${suffix}".`);
101
+ }
102
+ if (!/^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*$/.test(suffix)) {
103
+ throw new Error(`[sh3-artifact] buildSuffix "${suffix}" is not a valid semver build-metadata identifier (expected dot-separated [0-9A-Za-z-]+).`);
104
+ }
105
+ return `${pkgVersion}+${suffix}`;
106
+ }
107
+ /**
108
+ * Resolve the auto build suffix from git: count of commits between the
109
+ * latest reachable tag and HEAD. Returns '' if git is unavailable, no
110
+ * tags exist, or HEAD is exactly at the tag (canonical release).
111
+ */
112
+ function resolveAutoBuildSuffix() {
113
+ try {
114
+ const lastTag = execSync('git describe --tags --abbrev=0', {
115
+ encoding: 'utf-8',
116
+ stdio: ['ignore', 'pipe', 'ignore'],
117
+ }).trim();
118
+ if (!lastTag)
119
+ return '';
120
+ const count = execSync(`git rev-list --count ${lastTag}..HEAD`, {
121
+ encoding: 'utf-8',
122
+ stdio: ['ignore', 'pipe', 'ignore'],
123
+ }).trim();
124
+ const n = parseInt(count, 10);
125
+ if (!Number.isFinite(n) || n <= 0)
126
+ return '';
127
+ return String(n);
128
+ }
129
+ catch (_a) {
130
+ return '';
131
+ }
132
+ }
86
133
  /**
87
134
  * Vite plugin that produces a distributable artifact directory after build.
88
135
  *
@@ -224,6 +271,17 @@ export function sh3Artifact(options = {}) {
224
271
  throw new Error('[sh3-artifact] Missing "version" in package.json. Per ADR-013 the package '
225
272
  + 'version is read from package.json, not from the source manifest.');
226
273
  }
274
+ // --- Resolve build suffix and compose the artifact version ---
275
+ // Per ADR-013 amendment: package.json holds the release version;
276
+ // sh3Artifact may layer a build-metadata suffix on top.
277
+ let resolvedSuffix = '';
278
+ if (options.buildSuffix === 'auto') {
279
+ resolvedSuffix = resolveAutoBuildSuffix();
280
+ }
281
+ else if (typeof options.buildSuffix === 'string') {
282
+ resolvedSuffix = options.buildSuffix;
283
+ }
284
+ const artifactVersion = composeArtifactVersion(pkgVersion, resolvedSuffix);
227
285
  // --- Write manifest.json ---
228
286
  const overrides = (_c = options.manifest) !== null && _c !== void 0 ? _c : {};
229
287
  const finalDescription = (_d = overrides.description) !== null && _d !== void 0 ? _d : pkgDescription;
@@ -234,7 +292,7 @@ export function sh3Artifact(options = {}) {
234
292
  if (!finalAuthor) {
235
293
  throw new Error('[sh3-artifact] Missing "author". Add it to package.json or pass it via sh3Artifact({ manifest: { author } }).');
236
294
  }
237
- const manifest = Object.assign(Object.assign(Object.assign({ id: id || 'unknown', type, label: label || id || 'unknown', version: pkgVersion, contractVersion: 1, client: 'client.js' }, (hasServer ? { server: 'server.js' } : {})), { description: finalDescription, author: finalAuthor }), overrides);
295
+ const manifest = Object.assign(Object.assign(Object.assign({ id: id || 'unknown', type, label: label || id || 'unknown', version: artifactVersion, contractVersion: 1, client: 'client.js' }, (hasServer ? { server: 'server.js' } : {})), { description: finalDescription, author: finalAuthor }), overrides);
238
296
  writeFileSync(join(outDir, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
239
297
  // --- Log summary ---
240
298
  const files = ['manifest.json', 'client.js'];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { composeArtifactVersion } from './build';
3
+ describe('composeArtifactVersion', () => {
4
+ it('returns pkgVersion unchanged when suffix is undefined', () => {
5
+ expect(composeArtifactVersion('1.2.3', undefined)).toBe('1.2.3');
6
+ });
7
+ it('returns pkgVersion unchanged when suffix is empty string', () => {
8
+ expect(composeArtifactVersion('1.2.3', '')).toBe('1.2.3');
9
+ });
10
+ it('appends a numeric suffix as build metadata', () => {
11
+ expect(composeArtifactVersion('1.2.3', '42')).toBe('1.2.3+42');
12
+ });
13
+ it('appends dotted alphanumeric suffix as build metadata', () => {
14
+ expect(composeArtifactVersion('1.2.3', 'build.42')).toBe('1.2.3+build.42');
15
+ });
16
+ it('composes onto a prerelease version (semver allows -pre+meta)', () => {
17
+ expect(composeArtifactVersion('1.2.3-rc.1', '7')).toBe('1.2.3-rc.1+7');
18
+ });
19
+ it('throws when pkgVersion already has build metadata', () => {
20
+ expect(() => composeArtifactVersion('1.2.3+local', '42')).toThrow(/already contains semver build metadata/);
21
+ });
22
+ it('throws when suffix contains an invalid character', () => {
23
+ expect(() => composeArtifactVersion('1.2.3', 'build_42')).toThrow(/not a valid semver build-metadata identifier/);
24
+ });
25
+ it('throws when suffix has empty dot-separated identifier', () => {
26
+ expect(() => composeArtifactVersion('1.2.3', 'build..42')).toThrow(/not a valid semver build-metadata identifier/);
27
+ });
28
+ it('throws when suffix starts with a plus', () => {
29
+ expect(() => composeArtifactVersion('1.2.3', '+42')).toThrow(/not a valid semver build-metadata identifier/);
30
+ });
31
+ });
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export declare const VERSION = "0.16.0";
2
+ export declare const VERSION = "0.16.1";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export const VERSION = '0.16.0';
2
+ export const VERSION = '0.16.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"