@springfield/ham-radio-utils 4.16.1 → 4.17.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.
- package/README.md +15 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/pack/driver-semver.d.ts +34 -0
- package/dist/pack/driver-semver.d.ts.map +1 -0
- package/dist/pack/driver-semver.js +85 -0
- package/dist/pack/driver-semver.js.map +1 -0
- package/dist/pack/pack-beta-cli.d.ts +3 -0
- package/dist/pack/pack-beta-cli.d.ts.map +1 -0
- package/dist/pack/pack-beta-cli.js +5 -0
- package/dist/pack/pack-beta-cli.js.map +1 -0
- package/dist/pack/pack-beta.d.ts +39 -0
- package/dist/pack/pack-beta.d.ts.map +1 -0
- package/dist/pack/pack-beta.js +242 -0
- package/dist/pack/pack-beta.js.map +1 -0
- package/dist/pack/ref-closure.d.ts +12 -0
- package/dist/pack/ref-closure.d.ts.map +1 -0
- package/dist/pack/ref-closure.js +93 -0
- package/dist/pack/ref-closure.js.map +1 -0
- package/package.json +5 -1
- package/src/index.ts +23 -0
- package/src/pack/driver-semver.ts +112 -0
- package/src/pack/pack-beta-cli.ts +5 -0
- package/src/pack/pack-beta.ts +340 -0
- package/src/pack/ref-closure.ts +120 -0
package/README.md
CHANGED
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Shared utilities for HamBench: memory-map codec, band plan, Callook license-class mapping, schema validation, and test helpers.
|
|
4
4
|
|
|
5
|
-
Docs: [HamBench developer docs](https://springfield-ham-radio.github.io/ham-radio-docs/developer/) · [License lookup](https://springfield-ham-radio.github.io/ham-radio-docs/developer/license-lookup.html)
|
|
5
|
+
Docs: [HamBench developer docs](https://springfield-ham-radio.github.io/ham-radio-docs/developer/) · [License lookup](https://springfield-ham-radio.github.io/ham-radio-docs/developer/license-lookup.html) · [Build a beta driver](https://springfield-ham-radio.github.io/ham-radio-docs/developer/beta-driver.html)
|
|
6
|
+
|
|
7
|
+
## Beta driver zips
|
|
8
|
+
|
|
9
|
+
`ham-radio-pack-beta` builds one local beta zip from a radio module working tree ([ADR 0010](https://github.com/springfield-ham-radio/ham-radio-docs/blob/main/docs/developer/adr/0010-local-beta-driver-zips.md)). It does not publish a release or write a catalog entry.
|
|
10
|
+
|
|
11
|
+
From a manufacturer module repo, after this package is installed:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
yarn pack:beta <modelId> [n]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`modelId` matches `configs/<modelId>.json`. `n` defaults to `1` (positive integer, no leading zero). The working-tree `version` and `minApiVersion` stay strict `major.minor.patch`. The zip is `dist-release/radio-module-<modelId>-<version>-beta.<n>.zip`, and the config inside the zip uses `<version>-beta.<n>`.
|
|
18
|
+
|
|
19
|
+
Stdout is the zip path, size in bytes, a `sha256:` digest, and `git rev-parse --short HEAD` when the module tree is a git checkout. A beta of the same `major.minor.patch` as the catalog is older than that catalog release (`compareToCatalogVersion`).
|
|
6
20
|
|
|
7
21
|
## License
|
|
8
22
|
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export * from './utils/schema-validator.js';
|
|
|
14
14
|
export * from './utils/to-hex-words.js';
|
|
15
15
|
export * from './utils/ui-logger.js';
|
|
16
16
|
export * from './utils/ui-logger-factory.js';
|
|
17
|
+
export { BETA_COUNTER_PATTERN, BETA_VERSION_PATTERN, MAX_BETA_ZIP_BYTES, STRICT_SEMVER_PATTERN, assertStrictSemver, closurePathsForConfig, compareDriverVersions, compareToCatalogVersion, expectedBetaZipName, formatBetaPackOutput, listConfigDescriptors, packBetaZip, packedBetaVersion, parseBetaCounter, parseConfigDescriptor, runPackBetaCli, shortGitSha, stageClosureWithBetaVersion, validateBetaDescriptor, } from './pack/pack-beta.js';
|
|
18
|
+
export type { PackBetaCliIo, PackBetaOptions, PackBetaResult, RadioModuleConfigDescriptor } from './pack/pack-beta.js';
|
|
17
19
|
export { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json';
|
|
18
20
|
export { default as radioMemoryMapSchema } from './schemas/radio-memory-map-schema.json';
|
|
19
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAE7C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,WAAW,EACX,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAGvH,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAuB;AAC5G,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wCAAwC,CAAuB"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export * from './utils/schema-validator.js';
|
|
|
14
14
|
export * from './utils/to-hex-words.js';
|
|
15
15
|
export * from './utils/ui-logger.js';
|
|
16
16
|
export * from './utils/ui-logger-factory.js';
|
|
17
|
+
export { BETA_COUNTER_PATTERN, BETA_VERSION_PATTERN, MAX_BETA_ZIP_BYTES, STRICT_SEMVER_PATTERN, assertStrictSemver, closurePathsForConfig, compareDriverVersions, compareToCatalogVersion, expectedBetaZipName, formatBetaPackOutput, listConfigDescriptors, packBetaZip, packedBetaVersion, parseBetaCounter, parseConfigDescriptor, runPackBetaCli, shortGitSha, stageClosureWithBetaVersion, validateBetaDescriptor, } from './pack/pack-beta.js';
|
|
17
18
|
// Export the radio protocol schema
|
|
18
19
|
export { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };
|
|
19
20
|
export { default as radioMemoryMapSchema } from './schemas/radio-memory-map-schema.json' with { type: 'json' };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAE7C,mCAAmC;AACnC,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC5G,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wCAAwC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC","sourcesContent":["export * from './memory/segmented-memory.js';\nexport * from './memory/memory-map-codec.js';\nexport * from './memory/memory-map-channels.js';\nexport * from './memory/memory-map-ui.js';\nexport * from './memory/memory-map-radio-codec.js';\n\nexport * from './test-utils/radio-channel-factory.js';\nexport * from './test-utils/radio-programmed-channel-factory.js';\n\nexport * from './utils/band-plan.js';\nexport * from './utils/frequency-display.js';\nexport * from './utils/memory-channel-utils.js';\nexport * from './utils/memory-data-utils.js';\nexport * from './utils/operator-class-mapper.js';\nexport * from './utils/schema-validator.js';\nexport * from './utils/to-hex-words.js';\nexport * from './utils/ui-logger.js';\nexport * from './utils/ui-logger-factory.js';\n\n// Export the radio protocol schema\nexport { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };\nexport { default as radioMemoryMapSchema } from './schemas/radio-memory-map-schema.json' with { type: 'json' };\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAE7C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,WAAW,EACX,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAG7B,mCAAmC;AACnC,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC5G,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wCAAwC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC","sourcesContent":["export * from './memory/segmented-memory.js';\nexport * from './memory/memory-map-codec.js';\nexport * from './memory/memory-map-channels.js';\nexport * from './memory/memory-map-ui.js';\nexport * from './memory/memory-map-radio-codec.js';\n\nexport * from './test-utils/radio-channel-factory.js';\nexport * from './test-utils/radio-programmed-channel-factory.js';\n\nexport * from './utils/band-plan.js';\nexport * from './utils/frequency-display.js';\nexport * from './utils/memory-channel-utils.js';\nexport * from './utils/memory-data-utils.js';\nexport * from './utils/operator-class-mapper.js';\nexport * from './utils/schema-validator.js';\nexport * from './utils/to-hex-words.js';\nexport * from './utils/ui-logger.js';\nexport * from './utils/ui-logger-factory.js';\n\nexport {\n BETA_COUNTER_PATTERN,\n BETA_VERSION_PATTERN,\n MAX_BETA_ZIP_BYTES,\n STRICT_SEMVER_PATTERN,\n assertStrictSemver,\n closurePathsForConfig,\n compareDriverVersions,\n compareToCatalogVersion,\n expectedBetaZipName,\n formatBetaPackOutput,\n listConfigDescriptors,\n packBetaZip,\n packedBetaVersion,\n parseBetaCounter,\n parseConfigDescriptor,\n runPackBetaCli,\n shortGitSha,\n stageClosureWithBetaVersion,\n validateBetaDescriptor,\n} from './pack/pack-beta.js';\nexport type { PackBetaCliIo, PackBetaOptions, PackBetaResult, RadioModuleConfigDescriptor } from './pack/pack-beta.js';\n\n// Export the radio protocol schema\nexport { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };\nexport { default as radioMemoryMapSchema } from './schemas/radio-memory-map-schema.json' with { type: 'json' };\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver version precedence for local beta zips against a strict catalog version.
|
|
3
|
+
* ADR 0010: registry `compareSemver` stays numeric-only. This helper is the
|
|
4
|
+
* prerelease rule HamBench applies to install directories.
|
|
5
|
+
*
|
|
6
|
+
* - `1.11.1-beta.1` is older than catalog `1.11.1`
|
|
7
|
+
* - `1.11.2-beta.1` is newer than catalog `1.11.1`
|
|
8
|
+
* - among betas, `n` is numeric (`2` is less than `10`)
|
|
9
|
+
*/
|
|
10
|
+
export declare const STRICT_SEMVER_PATTERN: RegExp;
|
|
11
|
+
export declare const BETA_COUNTER_PATTERN: RegExp;
|
|
12
|
+
export declare const BETA_VERSION_PATTERN: RegExp;
|
|
13
|
+
export interface ParsedDriverVersion {
|
|
14
|
+
major: number;
|
|
15
|
+
minor: number;
|
|
16
|
+
patch: number;
|
|
17
|
+
beta?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function assertStrictSemver(value: string, fieldLabel: string): void;
|
|
20
|
+
export declare function parseBetaCounter(raw: string | undefined): number;
|
|
21
|
+
export declare function packedBetaVersion(baseVersion: string, n: number): string;
|
|
22
|
+
export declare function parseDriverVersion(version: string): ParsedDriverVersion;
|
|
23
|
+
/**
|
|
24
|
+
* Positive when `left` is newer than `right`.
|
|
25
|
+
* A release is newer than a beta of the same major.minor.patch.
|
|
26
|
+
*/
|
|
27
|
+
export declare function compareDriverVersions(left: string, right: string): number;
|
|
28
|
+
/**
|
|
29
|
+
* Compare an installed driver version to a catalog `major.minor.patch`.
|
|
30
|
+
* Catalog strings are strict; prerelease catalog versions are rejected.
|
|
31
|
+
* Negative means the install is older than the catalog entry (HamBench offers Update).
|
|
32
|
+
*/
|
|
33
|
+
export declare function compareToCatalogVersion(installedVersion: string, catalogVersion: string): number;
|
|
34
|
+
//# sourceMappingURL=driver-semver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver-semver.d.ts","sourceRoot":"","sources":["../../src/pack/driver-semver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,qBAAqB,QAAoB,CAAC;AACvD,eAAO,MAAM,oBAAoB,QAAe,CAAC;AACjD,eAAO,MAAM,oBAAoB,QAA2C,CAAC;AAE7E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAI1E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAUhE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAoBvE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA6BzE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAMhG"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver version precedence for local beta zips against a strict catalog version.
|
|
3
|
+
* ADR 0010: registry `compareSemver` stays numeric-only. This helper is the
|
|
4
|
+
* prerelease rule HamBench applies to install directories.
|
|
5
|
+
*
|
|
6
|
+
* - `1.11.1-beta.1` is older than catalog `1.11.1`
|
|
7
|
+
* - `1.11.2-beta.1` is newer than catalog `1.11.1`
|
|
8
|
+
* - among betas, `n` is numeric (`2` is less than `10`)
|
|
9
|
+
*/
|
|
10
|
+
export const STRICT_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
11
|
+
export const BETA_COUNTER_PATTERN = /^[1-9]\d*$/;
|
|
12
|
+
export const BETA_VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)-beta\.([1-9]\d*)$/;
|
|
13
|
+
export function assertStrictSemver(value, fieldLabel) {
|
|
14
|
+
if (!STRICT_SEMVER_PATTERN.test(value)) {
|
|
15
|
+
throw new Error(`${fieldLabel} must be strict major.minor.patch (no prerelease or build metadata)`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function parseBetaCounter(raw) {
|
|
19
|
+
if (raw === undefined) {
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
if (!BETA_COUNTER_PATTERN.test(raw)) {
|
|
23
|
+
throw new Error('beta counter n must be a positive integer with no leading zero');
|
|
24
|
+
}
|
|
25
|
+
return Number(raw);
|
|
26
|
+
}
|
|
27
|
+
export function packedBetaVersion(baseVersion, n) {
|
|
28
|
+
return `${baseVersion}-beta.${n}`;
|
|
29
|
+
}
|
|
30
|
+
export function parseDriverVersion(version) {
|
|
31
|
+
const trimmed = version.trim();
|
|
32
|
+
const betaMatch = BETA_VERSION_PATTERN.exec(trimmed);
|
|
33
|
+
if (betaMatch) {
|
|
34
|
+
return {
|
|
35
|
+
major: Number(betaMatch[1]),
|
|
36
|
+
minor: Number(betaMatch[2]),
|
|
37
|
+
patch: Number(betaMatch[3]),
|
|
38
|
+
beta: Number(betaMatch[4]),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (!STRICT_SEMVER_PATTERN.test(trimmed)) {
|
|
42
|
+
throw new Error(`driver version must be strict major.minor.patch or major.minor.patch-beta.n: ${version}`);
|
|
43
|
+
}
|
|
44
|
+
const [major, minor, patch] = trimmed.split('.').map((part) => Number(part));
|
|
45
|
+
return { major: major ?? 0, minor: minor ?? 0, patch: patch ?? 0 };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Positive when `left` is newer than `right`.
|
|
49
|
+
* A release is newer than a beta of the same major.minor.patch.
|
|
50
|
+
*/
|
|
51
|
+
export function compareDriverVersions(left, right) {
|
|
52
|
+
const a = parseDriverVersion(left);
|
|
53
|
+
const b = parseDriverVersion(right);
|
|
54
|
+
if (a.major !== b.major) {
|
|
55
|
+
return a.major - b.major;
|
|
56
|
+
}
|
|
57
|
+
if (a.minor !== b.minor) {
|
|
58
|
+
return a.minor - b.minor;
|
|
59
|
+
}
|
|
60
|
+
if (a.patch !== b.patch) {
|
|
61
|
+
return a.patch - b.patch;
|
|
62
|
+
}
|
|
63
|
+
if (a.beta === undefined && b.beta === undefined) {
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
if (a.beta === undefined) {
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
if (b.beta === undefined) {
|
|
70
|
+
return -1;
|
|
71
|
+
}
|
|
72
|
+
return a.beta - b.beta;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Compare an installed driver version to a catalog `major.minor.patch`.
|
|
76
|
+
* Catalog strings are strict; prerelease catalog versions are rejected.
|
|
77
|
+
* Negative means the install is older than the catalog entry (HamBench offers Update).
|
|
78
|
+
*/
|
|
79
|
+
export function compareToCatalogVersion(installedVersion, catalogVersion) {
|
|
80
|
+
if (!STRICT_SEMVER_PATTERN.test(catalogVersion)) {
|
|
81
|
+
throw new Error(`catalog version must be strict major.minor.patch: ${catalogVersion}`);
|
|
82
|
+
}
|
|
83
|
+
return compareDriverVersions(installedVersion, catalogVersion);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=driver-semver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver-semver.js","sourceRoot":"","sources":["../../src/pack/driver-semver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Driver version precedence for local beta zips against a strict catalog version.\n * ADR 0010: registry `compareSemver` stays numeric-only. This helper is the\n * prerelease rule HamBench applies to install directories.\n *\n * - `1.11.1-beta.1` is older than catalog `1.11.1`\n * - `1.11.2-beta.1` is newer than catalog `1.11.1`\n * - among betas, `n` is numeric (`2` is less than `10`)\n */\n\nexport const STRICT_SEMVER_PATTERN = /^\\d+\\.\\d+\\.\\d+$/;\nexport const BETA_COUNTER_PATTERN = /^[1-9]\\d*$/;\nexport const BETA_VERSION_PATTERN = /^(\\d+)\\.(\\d+)\\.(\\d+)-beta\\.([1-9]\\d*)$/;\n\nexport interface ParsedDriverVersion {\n major: number;\n minor: number;\n patch: number;\n beta?: number;\n}\n\nexport function assertStrictSemver(value: string, fieldLabel: string): void {\n if (!STRICT_SEMVER_PATTERN.test(value)) {\n throw new Error(`${fieldLabel} must be strict major.minor.patch (no prerelease or build metadata)`);\n }\n}\n\nexport function parseBetaCounter(raw: string | undefined): number {\n if (raw === undefined) {\n return 1;\n }\n\n if (!BETA_COUNTER_PATTERN.test(raw)) {\n throw new Error('beta counter n must be a positive integer with no leading zero');\n }\n\n return Number(raw);\n}\n\nexport function packedBetaVersion(baseVersion: string, n: number): string {\n return `${baseVersion}-beta.${n}`;\n}\n\nexport function parseDriverVersion(version: string): ParsedDriverVersion {\n const trimmed = version.trim();\n const betaMatch = BETA_VERSION_PATTERN.exec(trimmed);\n\n if (betaMatch) {\n return {\n major: Number(betaMatch[1]),\n minor: Number(betaMatch[2]),\n patch: Number(betaMatch[3]),\n beta: Number(betaMatch[4]),\n };\n }\n\n if (!STRICT_SEMVER_PATTERN.test(trimmed)) {\n throw new Error(`driver version must be strict major.minor.patch or major.minor.patch-beta.n: ${version}`);\n }\n\n const [major, minor, patch] = trimmed.split('.').map((part) => Number(part));\n\n return { major: major ?? 0, minor: minor ?? 0, patch: patch ?? 0 };\n}\n\n/**\n * Positive when `left` is newer than `right`.\n * A release is newer than a beta of the same major.minor.patch.\n */\nexport function compareDriverVersions(left: string, right: string): number {\n const a = parseDriverVersion(left);\n const b = parseDriverVersion(right);\n\n if (a.major !== b.major) {\n return a.major - b.major;\n }\n\n if (a.minor !== b.minor) {\n return a.minor - b.minor;\n }\n\n if (a.patch !== b.patch) {\n return a.patch - b.patch;\n }\n\n if (a.beta === undefined && b.beta === undefined) {\n return 0;\n }\n\n if (a.beta === undefined) {\n return 1;\n }\n\n if (b.beta === undefined) {\n return -1;\n }\n\n return a.beta - b.beta;\n}\n\n/**\n * Compare an installed driver version to a catalog `major.minor.patch`.\n * Catalog strings are strict; prerelease catalog versions are rejected.\n * Negative means the install is older than the catalog entry (HamBench offers Update).\n */\nexport function compareToCatalogVersion(installedVersion: string, catalogVersion: string): number {\n if (!STRICT_SEMVER_PATTERN.test(catalogVersion)) {\n throw new Error(`catalog version must be strict major.minor.patch: ${catalogVersion}`);\n }\n\n return compareDriverVersions(installedVersion, catalogVersion);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-beta-cli.d.ts","sourceRoot":"","sources":["../../src/pack/pack-beta-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-beta-cli.js","sourceRoot":"","sources":["../../src/pack/pack-beta-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AACtE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { runPackBetaCli } from './pack-beta.js';\n\nconst exitCode = runPackBetaCli(process.argv.slice(2), process.cwd());\nprocess.exit(exitCode);\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export { assertStrictSemver, BETA_COUNTER_PATTERN, BETA_VERSION_PATTERN, compareDriverVersions, compareToCatalogVersion, packedBetaVersion, parseBetaCounter, STRICT_SEMVER_PATTERN, } from './driver-semver.js';
|
|
2
|
+
export declare const MAX_BETA_ZIP_BYTES: number;
|
|
3
|
+
export interface RadioModuleConfigDescriptor {
|
|
4
|
+
fileName: string;
|
|
5
|
+
relativeConfig: string;
|
|
6
|
+
modelId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
configVersion: string;
|
|
9
|
+
minApiVersion: string;
|
|
10
|
+
manufacturer: string;
|
|
11
|
+
parsed: unknown;
|
|
12
|
+
}
|
|
13
|
+
export interface PackBetaOptions {
|
|
14
|
+
outputDirectory?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface PackBetaResult {
|
|
17
|
+
zipPath: string;
|
|
18
|
+
zipName: string;
|
|
19
|
+
modelId: string;
|
|
20
|
+
baseVersion: string;
|
|
21
|
+
packedVersion: string;
|
|
22
|
+
size: number;
|
|
23
|
+
sha256: string;
|
|
24
|
+
gitSha?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function expectedBetaZipName(modelId: string, baseVersion: string, n: number): string;
|
|
27
|
+
export declare function packBetaZip(moduleRoot: string, modelId: string, n?: number, options?: PackBetaOptions): PackBetaResult;
|
|
28
|
+
export declare function validateBetaDescriptor(descriptor: Pick<RadioModuleConfigDescriptor, 'relativeConfig' | 'configVersion' | 'minApiVersion'>): void;
|
|
29
|
+
export declare function listConfigDescriptors(moduleRoot: string): RadioModuleConfigDescriptor[];
|
|
30
|
+
export declare function parseConfigDescriptor(moduleRoot: string, fileName: string, manufacturerDefault?: string): RadioModuleConfigDescriptor;
|
|
31
|
+
export declare function closurePathsForConfig(moduleRoot: string, descriptor: Pick<RadioModuleConfigDescriptor, 'relativeConfig'>): string[];
|
|
32
|
+
export declare function stageClosureWithBetaVersion(moduleRoot: string, stagingRoot: string, relativePaths: string[], configRelative: string, version: string): void;
|
|
33
|
+
export declare function shortGitSha(moduleRoot: string): string | undefined;
|
|
34
|
+
export declare function formatBetaPackOutput(result: Pick<PackBetaResult, 'zipPath' | 'size' | 'sha256' | 'gitSha'>): string;
|
|
35
|
+
export interface PackBetaCliIo {
|
|
36
|
+
write(chunk: string): void;
|
|
37
|
+
}
|
|
38
|
+
export declare function runPackBetaCli(args: readonly string[], moduleRoot: string, stdout?: PackBetaCliIo, stderr?: PackBetaCliIo): number;
|
|
39
|
+
//# sourceMappingURL=pack-beta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-beta.d.ts","sourceRoot":"","sources":["../../src/pack/pack-beta.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,kBAAkB,QAAc,CAAC;AAE9C,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,SAAI,EAAE,OAAO,GAAE,eAAoB,GAAG,cAAc,CA6CrH;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,2BAA2B,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,CAAC,GAAG,IAAI,CAsBhJ;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,2BAA2B,EAAE,CAavF;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,GAAG,2BAA2B,CAuDrI;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,2BAA2B,EAAE,gBAAgB,CAAC,GAAG,MAAM,EAAE,CAUnI;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAe3J;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYlE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,GAAG,MAAM,CAQnH;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,aAA8B,EAAE,MAAM,GAAE,aAA8B,GAAG,MAAM,CAiBlK"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build one local beta driver zip (ADR 0010).
|
|
3
|
+
*
|
|
4
|
+
* Usage from a radio module repo: `yarn pack:beta <modelId> [n]`
|
|
5
|
+
* n — positive beta counter (default 1, no leading zero).
|
|
6
|
+
*
|
|
7
|
+
* Writes `dist-release/radio-module-<modelId>-<version>-beta.<n>.zip` without
|
|
8
|
+
* modifying the working tree, catalog entries, or other zips in `dist-release/`.
|
|
9
|
+
* Does not call GitHub and does not skip a model that already has a release asset.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
import { spawnSync } from 'node:child_process';
|
|
13
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { tmpdir } from 'node:os';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
16
|
+
import { collectRefClosure } from './ref-closure.js';
|
|
17
|
+
import { assertStrictSemver, BETA_COUNTER_PATTERN, packedBetaVersion, parseBetaCounter } from './driver-semver.js';
|
|
18
|
+
export { assertStrictSemver, BETA_COUNTER_PATTERN, BETA_VERSION_PATTERN, compareDriverVersions, compareToCatalogVersion, packedBetaVersion, parseBetaCounter, STRICT_SEMVER_PATTERN, } from './driver-semver.js';
|
|
19
|
+
export const MAX_BETA_ZIP_BYTES = 1024 * 1024;
|
|
20
|
+
export function expectedBetaZipName(modelId, baseVersion, n) {
|
|
21
|
+
return `radio-module-${modelId}-${baseVersion}-beta.${n}.zip`;
|
|
22
|
+
}
|
|
23
|
+
export function packBetaZip(moduleRoot, modelId, n = 1, options = {}) {
|
|
24
|
+
if (!Number.isSafeInteger(n) || !BETA_COUNTER_PATTERN.test(String(n))) {
|
|
25
|
+
throw new Error('beta counter n must be a positive integer with no leading zero');
|
|
26
|
+
}
|
|
27
|
+
const descriptor = loadDescriptor(moduleRoot, modelId);
|
|
28
|
+
validateBetaDescriptor(descriptor);
|
|
29
|
+
const pathsToZip = closurePathsForConfig(moduleRoot, descriptor);
|
|
30
|
+
const version = packedBetaVersion(descriptor.configVersion, n);
|
|
31
|
+
const zipName = expectedBetaZipName(descriptor.modelId, descriptor.configVersion, n);
|
|
32
|
+
const outputDirectory = options.outputDirectory ?? join(moduleRoot, 'dist-release');
|
|
33
|
+
const zipPath = join(outputDirectory, zipName);
|
|
34
|
+
mkdirSync(outputDirectory, { recursive: true });
|
|
35
|
+
const stagingRoot = mkdtempSync(join(tmpdir(), 'pack-beta-'));
|
|
36
|
+
try {
|
|
37
|
+
stageClosureWithBetaVersion(moduleRoot, stagingRoot, pathsToZip, descriptor.relativeConfig, version);
|
|
38
|
+
createZipFromStaging(stagingRoot, zipPath, pathsToZip);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
rmSync(stagingRoot, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
const size = statSync(zipPath).size;
|
|
44
|
+
if (size > MAX_BETA_ZIP_BYTES) {
|
|
45
|
+
unlinkSync(zipPath);
|
|
46
|
+
throw new Error(`beta zip exceeds ${MAX_BETA_ZIP_BYTES} bytes (${size} bytes)`);
|
|
47
|
+
}
|
|
48
|
+
const sha256 = integrityFromFile(zipPath);
|
|
49
|
+
const gitSha = shortGitSha(moduleRoot);
|
|
50
|
+
return {
|
|
51
|
+
zipPath,
|
|
52
|
+
zipName,
|
|
53
|
+
modelId: descriptor.modelId,
|
|
54
|
+
baseVersion: descriptor.configVersion,
|
|
55
|
+
packedVersion: version,
|
|
56
|
+
size,
|
|
57
|
+
sha256,
|
|
58
|
+
...(gitSha ? { gitSha } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function validateBetaDescriptor(descriptor) {
|
|
62
|
+
const { relativeConfig, configVersion, minApiVersion } = descriptor;
|
|
63
|
+
if (typeof configVersion !== 'string' || configVersion.length === 0) {
|
|
64
|
+
throw new Error(`${relativeConfig} is missing version`);
|
|
65
|
+
}
|
|
66
|
+
if (configVersion.includes('-') || configVersion.includes('+')) {
|
|
67
|
+
throw new Error(`${relativeConfig} version must not include a prerelease or build metadata suffix`);
|
|
68
|
+
}
|
|
69
|
+
assertStrictSemver(configVersion, `${relativeConfig} version`);
|
|
70
|
+
if (typeof minApiVersion !== 'string' || minApiVersion.length === 0) {
|
|
71
|
+
throw new Error(`${relativeConfig} is missing minApiVersion`);
|
|
72
|
+
}
|
|
73
|
+
if (minApiVersion.includes('-') || minApiVersion.includes('+')) {
|
|
74
|
+
throw new Error(`${relativeConfig} minApiVersion must not include a prerelease or build metadata suffix`);
|
|
75
|
+
}
|
|
76
|
+
assertStrictSemver(minApiVersion, `${relativeConfig} minApiVersion`);
|
|
77
|
+
}
|
|
78
|
+
export function listConfigDescriptors(moduleRoot) {
|
|
79
|
+
const configDirectory = join(moduleRoot, 'configs');
|
|
80
|
+
if (!existsSync(configDirectory)) {
|
|
81
|
+
throw new Error('configs directory is missing');
|
|
82
|
+
}
|
|
83
|
+
const manufacturerDefault = readManufacturerDefault(moduleRoot);
|
|
84
|
+
return readdirSync(configDirectory)
|
|
85
|
+
.filter((fileName) => fileName.endsWith('.json'))
|
|
86
|
+
.sort()
|
|
87
|
+
.map((fileName) => parseConfigDescriptor(moduleRoot, fileName, manufacturerDefault));
|
|
88
|
+
}
|
|
89
|
+
export function parseConfigDescriptor(moduleRoot, fileName, manufacturerDefault) {
|
|
90
|
+
const relativeConfig = `configs/${fileName}`;
|
|
91
|
+
const absoluteConfig = join(moduleRoot, relativeConfig);
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = JSON.parse(readFileSync(absoluteConfig, 'utf8'));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw new Error(`${relativeConfig} is not valid JSON`);
|
|
98
|
+
}
|
|
99
|
+
const record = parsed && typeof parsed === 'object' ? parsed : {};
|
|
100
|
+
const id = record.id && typeof record.id === 'object' ? record.id : {};
|
|
101
|
+
const modelId = id.model;
|
|
102
|
+
const name = id.name;
|
|
103
|
+
const configVersion = record.version;
|
|
104
|
+
const minApiVersion = record.minApiVersion;
|
|
105
|
+
const manufacturer = typeof id.manufacturer === 'string' && id.manufacturer.length > 0 ? id.manufacturer : manufacturerDefault;
|
|
106
|
+
if (typeof modelId !== 'string' || modelId.length === 0) {
|
|
107
|
+
throw new Error(`${relativeConfig} is missing id.model`);
|
|
108
|
+
}
|
|
109
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
110
|
+
throw new Error(`${relativeConfig} is missing id.name`);
|
|
111
|
+
}
|
|
112
|
+
if (typeof configVersion !== 'string' || configVersion.length === 0) {
|
|
113
|
+
throw new Error(`${relativeConfig} is missing version`);
|
|
114
|
+
}
|
|
115
|
+
if (typeof minApiVersion !== 'string' || minApiVersion.length === 0) {
|
|
116
|
+
throw new Error(`${relativeConfig} is missing minApiVersion`);
|
|
117
|
+
}
|
|
118
|
+
if (typeof manufacturer !== 'string' || manufacturer.length === 0) {
|
|
119
|
+
throw new Error(`${relativeConfig} is missing id.manufacturer`);
|
|
120
|
+
}
|
|
121
|
+
const expectedFile = `configs/${modelId}.json`;
|
|
122
|
+
if (relativeConfig !== expectedFile) {
|
|
123
|
+
throw new Error(`${relativeConfig} must be named ${expectedFile} (id.model ${modelId})`);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
fileName,
|
|
127
|
+
relativeConfig,
|
|
128
|
+
modelId,
|
|
129
|
+
name,
|
|
130
|
+
configVersion,
|
|
131
|
+
minApiVersion,
|
|
132
|
+
manufacturer,
|
|
133
|
+
parsed,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export function closurePathsForConfig(moduleRoot, descriptor) {
|
|
137
|
+
const paths = collectRefClosure(moduleRoot, descriptor.relativeConfig);
|
|
138
|
+
for (const relativePath of paths) {
|
|
139
|
+
if (!existsSync(join(moduleRoot, relativePath))) {
|
|
140
|
+
throw new Error(`${descriptor.relativeConfig} $ref target is missing: ${relativePath}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return paths;
|
|
144
|
+
}
|
|
145
|
+
export function stageClosureWithBetaVersion(moduleRoot, stagingRoot, relativePaths, configRelative, version) {
|
|
146
|
+
for (const relativePath of relativePaths) {
|
|
147
|
+
const sourcePath = join(moduleRoot, relativePath);
|
|
148
|
+
const destPath = join(stagingRoot, relativePath);
|
|
149
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
150
|
+
if (relativePath === configRelative) {
|
|
151
|
+
const parsed = JSON.parse(readFileSync(sourcePath, 'utf8'));
|
|
152
|
+
parsed.version = version;
|
|
153
|
+
writeFileSync(destPath, `${JSON.stringify(parsed, null, 2)}\n`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
cpSync(sourcePath, destPath);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function shortGitSha(moduleRoot) {
|
|
161
|
+
const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {
|
|
162
|
+
cwd: moduleRoot,
|
|
163
|
+
encoding: 'utf8',
|
|
164
|
+
});
|
|
165
|
+
if (result.status !== 0) {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
const sha = result.stdout.trim();
|
|
169
|
+
return sha.length > 0 ? sha : undefined;
|
|
170
|
+
}
|
|
171
|
+
export function formatBetaPackOutput(result) {
|
|
172
|
+
const lines = [result.zipPath, String(result.size), result.sha256];
|
|
173
|
+
if (result.gitSha) {
|
|
174
|
+
lines.push(result.gitSha);
|
|
175
|
+
}
|
|
176
|
+
return `${lines.join('\n')}\n`;
|
|
177
|
+
}
|
|
178
|
+
export function runPackBetaCli(args, moduleRoot, stdout = process.stdout, stderr = process.stderr) {
|
|
179
|
+
const [modelId, nArg, ...rest] = args;
|
|
180
|
+
if (!modelId || rest.length > 0) {
|
|
181
|
+
stderr.write('usage: yarn pack:beta <modelId> [n]\n');
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const n = parseBetaCounter(nArg);
|
|
186
|
+
const result = packBetaZip(moduleRoot, modelId, n);
|
|
187
|
+
stdout.write(formatBetaPackOutput(result));
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
192
|
+
return 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function loadDescriptor(moduleRoot, modelId) {
|
|
196
|
+
const descriptors = listConfigDescriptors(moduleRoot);
|
|
197
|
+
const descriptor = descriptors.find((entry) => entry.modelId === modelId);
|
|
198
|
+
if (!descriptor) {
|
|
199
|
+
const known = descriptors.map((entry) => entry.modelId).join(', ');
|
|
200
|
+
throw new Error(`unknown modelId ${modelId}; expected one of: ${known}`);
|
|
201
|
+
}
|
|
202
|
+
return descriptor;
|
|
203
|
+
}
|
|
204
|
+
function readManufacturerDefault(moduleRoot) {
|
|
205
|
+
const packagePath = join(moduleRoot, 'package.json');
|
|
206
|
+
if (!existsSync(packagePath)) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
let parsed;
|
|
210
|
+
try {
|
|
211
|
+
parsed = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
throw new Error('package.json is not valid JSON');
|
|
215
|
+
}
|
|
216
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
const springfield = parsed.springfield;
|
|
220
|
+
if (!springfield || typeof springfield !== 'object') {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
const manufacturer = springfield.manufacturer;
|
|
224
|
+
return typeof manufacturer === 'string' && manufacturer.length > 0 ? manufacturer : undefined;
|
|
225
|
+
}
|
|
226
|
+
function createZipFromStaging(stagingRoot, zipPath, relativePaths) {
|
|
227
|
+
if (existsSync(zipPath)) {
|
|
228
|
+
unlinkSync(zipPath);
|
|
229
|
+
}
|
|
230
|
+
const zipResult = spawnSync('zip', ['-q', zipPath, ...relativePaths, '-x', '*.DS_Store'], {
|
|
231
|
+
cwd: stagingRoot,
|
|
232
|
+
encoding: 'utf8',
|
|
233
|
+
});
|
|
234
|
+
if (zipResult.status !== 0) {
|
|
235
|
+
throw new Error(zipResult.stderr || zipResult.stdout || 'zip failed');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function integrityFromFile(filePath) {
|
|
239
|
+
const hash = createHash('sha256').update(readFileSync(filePath)).digest('hex');
|
|
240
|
+
return `sha256:${hash}`;
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=pack-beta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-beta.js","sourceRoot":"","sources":["../../src/pack/pack-beta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build one local beta driver zip (ADR 0010).\n *\n * Usage from a radio module repo: `yarn pack:beta <modelId> [n]`\n * n — positive beta counter (default 1, no leading zero).\n *\n * Writes `dist-release/radio-module-<modelId>-<version>-beta.<n>.zip` without\n * modifying the working tree, catalog entries, or other zips in `dist-release/`.\n * Does not call GitHub and does not skip a model that already has a release asset.\n */\nimport { createHash } from 'node:crypto';\nimport { spawnSync } from 'node:child_process';\nimport { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { collectRefClosure } from './ref-closure.js';\nimport { assertStrictSemver, BETA_COUNTER_PATTERN, packedBetaVersion, parseBetaCounter } from './driver-semver.js';\n\nexport {\n assertStrictSemver,\n BETA_COUNTER_PATTERN,\n BETA_VERSION_PATTERN,\n compareDriverVersions,\n compareToCatalogVersion,\n packedBetaVersion,\n parseBetaCounter,\n STRICT_SEMVER_PATTERN,\n} from './driver-semver.js';\n\nexport const MAX_BETA_ZIP_BYTES = 1024 * 1024;\n\nexport interface RadioModuleConfigDescriptor {\n fileName: string;\n relativeConfig: string;\n modelId: string;\n name: string;\n configVersion: string;\n minApiVersion: string;\n manufacturer: string;\n parsed: unknown;\n}\n\nexport interface PackBetaOptions {\n outputDirectory?: string;\n}\n\nexport interface PackBetaResult {\n zipPath: string;\n zipName: string;\n modelId: string;\n baseVersion: string;\n packedVersion: string;\n size: number;\n sha256: string;\n gitSha?: string;\n}\n\nexport function expectedBetaZipName(modelId: string, baseVersion: string, n: number): string {\n return `radio-module-${modelId}-${baseVersion}-beta.${n}.zip`;\n}\n\nexport function packBetaZip(moduleRoot: string, modelId: string, n = 1, options: PackBetaOptions = {}): PackBetaResult {\n if (!Number.isSafeInteger(n) || !BETA_COUNTER_PATTERN.test(String(n))) {\n throw new Error('beta counter n must be a positive integer with no leading zero');\n }\n\n const descriptor = loadDescriptor(moduleRoot, modelId);\n validateBetaDescriptor(descriptor);\n\n const pathsToZip = closurePathsForConfig(moduleRoot, descriptor);\n const version = packedBetaVersion(descriptor.configVersion, n);\n const zipName = expectedBetaZipName(descriptor.modelId, descriptor.configVersion, n);\n const outputDirectory = options.outputDirectory ?? join(moduleRoot, 'dist-release');\n const zipPath = join(outputDirectory, zipName);\n\n mkdirSync(outputDirectory, { recursive: true });\n\n const stagingRoot = mkdtempSync(join(tmpdir(), 'pack-beta-'));\n\n try {\n stageClosureWithBetaVersion(moduleRoot, stagingRoot, pathsToZip, descriptor.relativeConfig, version);\n createZipFromStaging(stagingRoot, zipPath, pathsToZip);\n } finally {\n rmSync(stagingRoot, { recursive: true, force: true });\n }\n\n const size = statSync(zipPath).size;\n\n if (size > MAX_BETA_ZIP_BYTES) {\n unlinkSync(zipPath);\n throw new Error(`beta zip exceeds ${MAX_BETA_ZIP_BYTES} bytes (${size} bytes)`);\n }\n\n const sha256 = integrityFromFile(zipPath);\n const gitSha = shortGitSha(moduleRoot);\n\n return {\n zipPath,\n zipName,\n modelId: descriptor.modelId,\n baseVersion: descriptor.configVersion,\n packedVersion: version,\n size,\n sha256,\n ...(gitSha ? { gitSha } : {}),\n };\n}\n\nexport function validateBetaDescriptor(descriptor: Pick<RadioModuleConfigDescriptor, 'relativeConfig' | 'configVersion' | 'minApiVersion'>): void {\n const { relativeConfig, configVersion, minApiVersion } = descriptor;\n\n if (typeof configVersion !== 'string' || configVersion.length === 0) {\n throw new Error(`${relativeConfig} is missing version`);\n }\n\n if (configVersion.includes('-') || configVersion.includes('+')) {\n throw new Error(`${relativeConfig} version must not include a prerelease or build metadata suffix`);\n }\n\n assertStrictSemver(configVersion, `${relativeConfig} version`);\n\n if (typeof minApiVersion !== 'string' || minApiVersion.length === 0) {\n throw new Error(`${relativeConfig} is missing minApiVersion`);\n }\n\n if (minApiVersion.includes('-') || minApiVersion.includes('+')) {\n throw new Error(`${relativeConfig} minApiVersion must not include a prerelease or build metadata suffix`);\n }\n\n assertStrictSemver(minApiVersion, `${relativeConfig} minApiVersion`);\n}\n\nexport function listConfigDescriptors(moduleRoot: string): RadioModuleConfigDescriptor[] {\n const configDirectory = join(moduleRoot, 'configs');\n\n if (!existsSync(configDirectory)) {\n throw new Error('configs directory is missing');\n }\n\n const manufacturerDefault = readManufacturerDefault(moduleRoot);\n\n return readdirSync(configDirectory)\n .filter((fileName) => fileName.endsWith('.json'))\n .sort()\n .map((fileName) => parseConfigDescriptor(moduleRoot, fileName, manufacturerDefault));\n}\n\nexport function parseConfigDescriptor(moduleRoot: string, fileName: string, manufacturerDefault?: string): RadioModuleConfigDescriptor {\n const relativeConfig = `configs/${fileName}`;\n const absoluteConfig = join(moduleRoot, relativeConfig);\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(readFileSync(absoluteConfig, 'utf8')) as unknown;\n } catch {\n throw new Error(`${relativeConfig} is not valid JSON`);\n }\n\n const record = parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : {};\n const id = record.id && typeof record.id === 'object' ? (record.id as Record<string, unknown>) : {};\n const modelId = id.model;\n const name = id.name;\n const configVersion = record.version;\n const minApiVersion = record.minApiVersion;\n const manufacturer = typeof id.manufacturer === 'string' && id.manufacturer.length > 0 ? id.manufacturer : manufacturerDefault;\n\n if (typeof modelId !== 'string' || modelId.length === 0) {\n throw new Error(`${relativeConfig} is missing id.model`);\n }\n\n if (typeof name !== 'string' || name.length === 0) {\n throw new Error(`${relativeConfig} is missing id.name`);\n }\n\n if (typeof configVersion !== 'string' || configVersion.length === 0) {\n throw new Error(`${relativeConfig} is missing version`);\n }\n\n if (typeof minApiVersion !== 'string' || minApiVersion.length === 0) {\n throw new Error(`${relativeConfig} is missing minApiVersion`);\n }\n\n if (typeof manufacturer !== 'string' || manufacturer.length === 0) {\n throw new Error(`${relativeConfig} is missing id.manufacturer`);\n }\n\n const expectedFile = `configs/${modelId}.json`;\n\n if (relativeConfig !== expectedFile) {\n throw new Error(`${relativeConfig} must be named ${expectedFile} (id.model ${modelId})`);\n }\n\n return {\n fileName,\n relativeConfig,\n modelId,\n name,\n configVersion,\n minApiVersion,\n manufacturer,\n parsed,\n };\n}\n\nexport function closurePathsForConfig(moduleRoot: string, descriptor: Pick<RadioModuleConfigDescriptor, 'relativeConfig'>): string[] {\n const paths = collectRefClosure(moduleRoot, descriptor.relativeConfig);\n\n for (const relativePath of paths) {\n if (!existsSync(join(moduleRoot, relativePath))) {\n throw new Error(`${descriptor.relativeConfig} $ref target is missing: ${relativePath}`);\n }\n }\n\n return paths;\n}\n\nexport function stageClosureWithBetaVersion(moduleRoot: string, stagingRoot: string, relativePaths: string[], configRelative: string, version: string): void {\n for (const relativePath of relativePaths) {\n const sourcePath = join(moduleRoot, relativePath);\n const destPath = join(stagingRoot, relativePath);\n\n mkdirSync(dirname(destPath), { recursive: true });\n\n if (relativePath === configRelative) {\n const parsed = JSON.parse(readFileSync(sourcePath, 'utf8')) as Record<string, unknown>;\n parsed.version = version;\n writeFileSync(destPath, `${JSON.stringify(parsed, null, 2)}\\n`);\n } else {\n cpSync(sourcePath, destPath);\n }\n }\n}\n\nexport function shortGitSha(moduleRoot: string): string | undefined {\n const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {\n cwd: moduleRoot,\n encoding: 'utf8',\n });\n\n if (result.status !== 0) {\n return undefined;\n }\n\n const sha = result.stdout.trim();\n return sha.length > 0 ? sha : undefined;\n}\n\nexport function formatBetaPackOutput(result: Pick<PackBetaResult, 'zipPath' | 'size' | 'sha256' | 'gitSha'>): string {\n const lines = [result.zipPath, String(result.size), result.sha256];\n\n if (result.gitSha) {\n lines.push(result.gitSha);\n }\n\n return `${lines.join('\\n')}\\n`;\n}\n\nexport interface PackBetaCliIo {\n write(chunk: string): void;\n}\n\nexport function runPackBetaCli(args: readonly string[], moduleRoot: string, stdout: PackBetaCliIo = process.stdout, stderr: PackBetaCliIo = process.stderr): number {\n const [modelId, nArg, ...rest] = args;\n\n if (!modelId || rest.length > 0) {\n stderr.write('usage: yarn pack:beta <modelId> [n]\\n');\n return 1;\n }\n\n try {\n const n = parseBetaCounter(nArg);\n const result = packBetaZip(moduleRoot, modelId, n);\n stdout.write(formatBetaPackOutput(result));\n return 0;\n } catch (error) {\n stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n return 1;\n }\n}\n\nfunction loadDescriptor(moduleRoot: string, modelId: string): RadioModuleConfigDescriptor {\n const descriptors = listConfigDescriptors(moduleRoot);\n const descriptor = descriptors.find((entry) => entry.modelId === modelId);\n\n if (!descriptor) {\n const known = descriptors.map((entry) => entry.modelId).join(', ');\n throw new Error(`unknown modelId ${modelId}; expected one of: ${known}`);\n }\n\n return descriptor;\n}\n\nfunction readManufacturerDefault(moduleRoot: string): string | undefined {\n const packagePath = join(moduleRoot, 'package.json');\n\n if (!existsSync(packagePath)) {\n return undefined;\n }\n\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(readFileSync(packagePath, 'utf8')) as unknown;\n } catch {\n throw new Error('package.json is not valid JSON');\n }\n\n if (!parsed || typeof parsed !== 'object') {\n return undefined;\n }\n\n const springfield = (parsed as Record<string, unknown>).springfield;\n\n if (!springfield || typeof springfield !== 'object') {\n return undefined;\n }\n\n const manufacturer = (springfield as Record<string, unknown>).manufacturer;\n return typeof manufacturer === 'string' && manufacturer.length > 0 ? manufacturer : undefined;\n}\n\nfunction createZipFromStaging(stagingRoot: string, zipPath: string, relativePaths: string[]): void {\n if (existsSync(zipPath)) {\n unlinkSync(zipPath);\n }\n\n const zipResult = spawnSync('zip', ['-q', zipPath, ...relativePaths, '-x', '*.DS_Store'], {\n cwd: stagingRoot,\n encoding: 'utf8',\n });\n\n if (zipResult.status !== 0) {\n throw new Error(zipResult.stderr || zipResult.stdout || 'zip failed');\n }\n}\n\nfunction integrityFromFile(filePath: string): string {\n const hash = createHash('sha256').update(readFileSync(filePath)).digest('hex');\n return `sha256:${hash}`;\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @returns Sorted repo-relative paths, including `startRelative`
|
|
3
|
+
*/
|
|
4
|
+
export declare function collectRefClosure(moduleRoot: string, startRelative: string): string[];
|
|
5
|
+
export declare function readWorkingTreeText(moduleRoot: string, relativePath: string): string | undefined;
|
|
6
|
+
export declare function localRefTargets(value: unknown, into?: string[]): string[];
|
|
7
|
+
/**
|
|
8
|
+
* Path portion of a relative file `$ref`, without a JSON pointer.
|
|
9
|
+
* Fragment-only refs and absolute URIs are ignored.
|
|
10
|
+
*/
|
|
11
|
+
export declare function filePathFromRef(ref: string): string | undefined;
|
|
12
|
+
//# sourceMappingURL=ref-closure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ref-closure.d.ts","sourceRoot":"","sources":["../../src/pack/ref-closure.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CA2CrF;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQhG;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CA4B7E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAS/D"}
|