swarm-deploy 0.1.0 → 0.2.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/CHANGELOG.md +44 -0
- package/README.md +680 -49
- package/RELEASING.md +2 -2
- package/SECURITY.md +2 -3
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +79 -22
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +19 -29
- package/dist/client.js +46 -55
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -1
- package/dist/files.d.ts +13 -28
- package/dist/files.js +13 -78
- package/dist/files.js.map +1 -1
- package/dist/hooks-module.d.ts +9 -0
- package/dist/hooks-module.js +118 -0
- package/dist/hooks-module.js.map +1 -0
- package/dist/hooks.d.ts +69 -0
- package/dist/hooks.js +88 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/release.d.ts +26 -0
- package/dist/release.js +419 -0
- package/dist/release.js.map +1 -0
- package/dist/server.d.ts +86 -0
- package/dist/server.js +405 -53
- package/dist/server.js.map +1 -1
- package/dist/storage/commit-journal.d.ts +39 -2
- package/dist/storage/commit-journal.js +140 -2
- package/dist/storage/commit-journal.js.map +1 -1
- package/dist/storage/commit-store.d.ts +69 -8
- package/dist/storage/commit-store.js +374 -22
- package/dist/storage/commit-store.js.map +1 -1
- package/dist/storage/layout.js +7 -1
- package/dist/storage/layout.js.map +1 -1
- package/dist/storage/link-store.d.ts +70 -0
- package/dist/storage/link-store.js +363 -0
- package/dist/storage/link-store.js.map +1 -0
- package/dist/storage/recovery.d.ts +2 -1
- package/dist/storage/recovery.js +8 -1
- package/dist/storage/recovery.js.map +1 -1
- package/dist/storage/retention.d.ts +41 -21
- package/dist/storage/retention.js +162 -8
- package/dist/storage/retention.js.map +1 -1
- package/dist/storage/tar-session-store.d.ts +36 -5
- package/dist/storage/tar-session-store.js +283 -55
- package/dist/storage/tar-session-store.js.map +1 -1
- package/dist/storage/tree-fs.d.ts +44 -0
- package/dist/storage/tree-fs.js +580 -0
- package/dist/storage/tree-fs.js.map +1 -0
- package/dist/storage/tree-staging.d.ts +9 -0
- package/dist/storage/tree-staging.js +64 -0
- package/dist/storage/tree-staging.js.map +1 -0
- package/dist/storage/types.d.ts +15 -0
- package/dist/symlinks.d.ts +36 -0
- package/dist/symlinks.js +128 -0
- package/dist/symlinks.js.map +1 -0
- package/dist/tar-protocol/controls.d.ts +25 -0
- package/dist/tar-protocol/controls.js +108 -2
- package/dist/tar-protocol/controls.js.map +1 -1
- package/dist/tar-protocol/direct-wire.d.ts +3 -3
- package/dist/tar-protocol/direct-wire.js +2 -2
- package/dist/tar-protocol/direct-wire.js.map +1 -1
- package/dist/tar-protocol/manifest.d.ts +13 -1
- package/dist/tar-protocol/manifest.js +26 -1
- package/dist/tar-protocol/manifest.js.map +1 -1
- package/dist/tar-protocol/tree-extract.d.ts +37 -0
- package/dist/tar-protocol/tree-extract.js +240 -0
- package/dist/tar-protocol/tree-extract.js.map +1 -0
- package/dist/tar-protocol/tree-manifest.d.ts +45 -0
- package/dist/tar-protocol/tree-manifest.js +277 -0
- package/dist/tar-protocol/tree-manifest.js.map +1 -0
- package/dist/tar-protocol/tree-path.d.ts +15 -0
- package/dist/tar-protocol/tree-path.js +68 -0
- package/dist/tar-protocol/tree-path.js.map +1 -0
- package/dist/tar-protocol/tree.d.ts +68 -0
- package/dist/tar-protocol/tree.js +269 -0
- package/dist/tar-protocol/tree.js.map +1 -0
- package/dist/tar-protocol/ustar.d.ts +14 -0
- package/dist/tar-protocol/ustar.js +81 -1
- package/dist/tar-protocol/ustar.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/package.json +3 -1
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type AbortSignalLike } from './abort.js';
|
|
2
|
+
import { SwarmDeployError } from './errors.js';
|
|
3
|
+
import type { ReleaseCoordinates } from './release.js';
|
|
4
|
+
import type { ArtifactKind } from './types.js';
|
|
5
|
+
export type HookFailurePhase = 'offer' | 'transfer' | 'verification' | 'beforeCommit' | 'commit' | 'afterCommit';
|
|
6
|
+
/** Immutable, secret-free description of the artifact a hook is observing. */
|
|
7
|
+
export interface HookArtifact {
|
|
8
|
+
name: string;
|
|
9
|
+
kind: ArtifactKind;
|
|
10
|
+
size: number;
|
|
11
|
+
/** The file digest, or the canonical tree digest. */
|
|
12
|
+
sha256: string;
|
|
13
|
+
transferId: string;
|
|
14
|
+
/** Present only for a directory artifact. */
|
|
15
|
+
entryCount?: number;
|
|
16
|
+
sourceParent?: string;
|
|
17
|
+
release?: ReleaseCoordinates;
|
|
18
|
+
}
|
|
19
|
+
export interface BeforeCommitContext {
|
|
20
|
+
artifact: HookArtifact;
|
|
21
|
+
/** Absolute path of the verified staging file. */
|
|
22
|
+
path: string;
|
|
23
|
+
resumed: boolean;
|
|
24
|
+
alreadyCommitted: false;
|
|
25
|
+
}
|
|
26
|
+
export interface AfterCommitContext {
|
|
27
|
+
artifact: HookArtifact;
|
|
28
|
+
/** Absolute path of the durably committed artifact. */
|
|
29
|
+
path: string;
|
|
30
|
+
resumed: boolean;
|
|
31
|
+
alreadyCommitted: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface HookFailureContext {
|
|
34
|
+
artifact: HookArtifact;
|
|
35
|
+
/** The best known absolute path, or `null` before one exists. */
|
|
36
|
+
path: string | null;
|
|
37
|
+
phase: HookFailurePhase;
|
|
38
|
+
resumed: boolean;
|
|
39
|
+
alreadyCommitted: boolean;
|
|
40
|
+
error: unknown;
|
|
41
|
+
}
|
|
42
|
+
export interface ServerHooks {
|
|
43
|
+
beforeCommit?(context: BeforeCommitContext): void | Promise<void>;
|
|
44
|
+
afterCommit?(context: AfterCommitContext): void | Promise<void>;
|
|
45
|
+
onFailure?(context: HookFailureContext): void | Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validates and freezes caller hooks. Each property is read exactly once and no
|
|
49
|
+
* receiver is bound, so later mutation of the caller's object cannot change the
|
|
50
|
+
* callbacks a running server invokes.
|
|
51
|
+
*/
|
|
52
|
+
export declare function snapshotHooks(hooks: ServerHooks | null | undefined): Readonly<ServerHooks>;
|
|
53
|
+
/**
|
|
54
|
+
* Wraps a failed callback. The message is fixed so callback text (which may
|
|
55
|
+
* carry secrets) never reaches the wire; the original error stays on `cause`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function hookError(phase: HookFailurePhase, cause: unknown): SwarmDeployError;
|
|
58
|
+
/**
|
|
59
|
+
* The error an `onFailure` hook should see: the raw exception for failures this
|
|
60
|
+
* module wrapped, otherwise the error unchanged. Only identity-tracked wrappers
|
|
61
|
+
* unwrap, so a forged `HOOK_FAILED` error cannot masquerade as one.
|
|
62
|
+
*/
|
|
63
|
+
export declare function callbackError(error: unknown): unknown;
|
|
64
|
+
/**
|
|
65
|
+
* Calls a callback with no receiver and settles when it does or when the
|
|
66
|
+
* server aborts, whichever is first. A callback that outlives an abort keeps
|
|
67
|
+
* running detached; its later rejection is swallowed.
|
|
68
|
+
*/
|
|
69
|
+
export declare function invokeHook<Context>(callback: (context: Context) => void | Promise<void>, context: Context, signal: AbortSignalLike): Promise<'completed' | 'aborted'>;
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.snapshotHooks = snapshotHooks;
|
|
4
|
+
exports.hookError = hookError;
|
|
5
|
+
exports.callbackError = callbackError;
|
|
6
|
+
exports.invokeHook = invokeHook;
|
|
7
|
+
const abort_js_1 = require("./abort.js");
|
|
8
|
+
const errors_js_1 = require("./errors.js");
|
|
9
|
+
const WRAPPED_CALLBACK_ERRORS = new WeakMap();
|
|
10
|
+
const HOOK_NAMES = ['beforeCommit', 'afterCommit', 'onFailure'];
|
|
11
|
+
/**
|
|
12
|
+
* Validates and freezes caller hooks. Each property is read exactly once and no
|
|
13
|
+
* receiver is bound, so later mutation of the caller's object cannot change the
|
|
14
|
+
* callbacks a running server invokes.
|
|
15
|
+
*/
|
|
16
|
+
function snapshotHooks(hooks) {
|
|
17
|
+
if (hooks === null || hooks === undefined)
|
|
18
|
+
return Object.freeze({});
|
|
19
|
+
if (typeof hooks !== 'object') {
|
|
20
|
+
throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, 'Invalid hooks');
|
|
21
|
+
}
|
|
22
|
+
for (const name of Reflect.ownKeys(hooks)) {
|
|
23
|
+
if (!HOOK_NAMES.includes(name)) {
|
|
24
|
+
throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, 'Unknown hook');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const snapshot = {};
|
|
28
|
+
for (const name of HOOK_NAMES) {
|
|
29
|
+
const callback = hooks[name];
|
|
30
|
+
if (callback === undefined)
|
|
31
|
+
continue;
|
|
32
|
+
if (typeof callback !== 'function') {
|
|
33
|
+
throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, `Invalid ${name} hook`);
|
|
34
|
+
}
|
|
35
|
+
Object.defineProperty(snapshot, name, {
|
|
36
|
+
value: callback,
|
|
37
|
+
enumerable: true,
|
|
38
|
+
writable: false,
|
|
39
|
+
configurable: false
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return Object.freeze(snapshot);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Wraps a failed callback. The message is fixed so callback text (which may
|
|
46
|
+
* carry secrets) never reaches the wire; the original error stays on `cause`.
|
|
47
|
+
*/
|
|
48
|
+
function hookError(phase, cause) {
|
|
49
|
+
const error = new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.HOOK_FAILED, `Deployment hook failed during ${phase}`, cause);
|
|
50
|
+
WRAPPED_CALLBACK_ERRORS.set(error, cause);
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The error an `onFailure` hook should see: the raw exception for failures this
|
|
55
|
+
* module wrapped, otherwise the error unchanged. Only identity-tracked wrappers
|
|
56
|
+
* unwrap, so a forged `HOOK_FAILED` error cannot masquerade as one.
|
|
57
|
+
*/
|
|
58
|
+
function callbackError(error) {
|
|
59
|
+
return error instanceof errors_js_1.SwarmDeployError && WRAPPED_CALLBACK_ERRORS.has(error)
|
|
60
|
+
? WRAPPED_CALLBACK_ERRORS.get(error)
|
|
61
|
+
: error;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Calls a callback with no receiver and settles when it does or when the
|
|
65
|
+
* server aborts, whichever is first. A callback that outlives an abort keeps
|
|
66
|
+
* running detached; its later rejection is swallowed.
|
|
67
|
+
*/
|
|
68
|
+
async function invokeHook(callback, context, signal) {
|
|
69
|
+
let pending;
|
|
70
|
+
try {
|
|
71
|
+
pending = Promise.resolve(callback(context));
|
|
72
|
+
}
|
|
73
|
+
catch (cause) {
|
|
74
|
+
pending = Promise.reject(cause);
|
|
75
|
+
}
|
|
76
|
+
pending.catch(() => { });
|
|
77
|
+
let release = () => { };
|
|
78
|
+
const aborted = new Promise((resolve) => {
|
|
79
|
+
release = (0, abort_js_1.onAbort)(signal, () => resolve('aborted'));
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
return await Promise.race([pending.then(() => 'completed'), aborted]);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
release();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":";;;;;;AAAA,yCAA0D;AAC1D,2CAAsD;AAqDtD,MAAM,uBAAuB,GAAG,IAAI,OAAO,EAA6B,CAAA;AACxE,MAAM,UAAU,GAAG,CAAC,cAAc,EAAE,aAAa,EAAE,WAAW,CAAU,CAAA;AAExE;;;;GAIG;AACH,uBAA8B,KAAqC;IACjE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACnE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,4BAAgB,CAAC,kBAAM,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAA;IACtE,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAE,UAA2C,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,4BAAgB,CAAC,kBAAM,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAA;QACrE,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAgB,EAAE,CAAA;IAChC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAY,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,IAAI,QAAQ,KAAK,SAAS;YAAE,SAAQ;QACpC,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,4BAAgB,CAAC,kBAAM,CAAC,gBAAgB,EAAE,WAAW,IAAI,OAAO,CAAC,CAAA;QAC7E,CAAC;QACD,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE;YACpC,KAAK,EAAE,QAAQ;YACf,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,KAAK;SACpB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;AAChC,CAAC;AAED;;;GAGG;AACH,mBAA0B,KAAuB,EAAE,KAAc;IAC/D,MAAM,KAAK,GAAG,IAAI,4BAAgB,CAChC,kBAAM,CAAC,WAAW,EAClB,iCAAiC,KAAK,EAAE,EACxC,KAAK,CACN,CAAA;IACD,uBAAuB,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IACzC,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,uBAA8B,KAAc;IAC1C,OAAO,KAAK,YAAY,4BAAgB,IAAI,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC;QAC5E,CAAC,CAAC,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC;QACpC,CAAC,CAAC,KAAK,CAAA;AACX,CAAC;AAED;;;;GAIG;AACI,KAAK,qBACV,QAAoD,EACpD,OAAgB,EAChB,MAAuB;IAEvB,IAAI,OAAsB,CAAA;IAC1B,IAAI,CAAC;QACH,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAA;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACjC,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IACvB,IAAI,OAAO,GAAG,GAAS,EAAE,GAAE,CAAC,CAAA;IAC5B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,EAAE;QACjD,OAAO,GAAG,IAAA,kBAAO,EAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IACF,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,WAAoB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAA;IAChF,CAAC;YAAS,CAAC;QACT,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export { ERRORS, SwarmDeployError, type ErrorCode } from './errors.js';
|
|
2
2
|
export type { Binary, BinaryInput, Digest, FingerprintEvent, KeyPair, Logger, PublicKey, PublicKeyInput, Seed, SeedInput, ServerScheduler } from './types.js';
|
|
3
3
|
export { parseSeed, parsePublicKey, generateSeed, keyPairFromSeed, publicKeyFromSeed } from './identity.js';
|
|
4
|
+
export type { AfterCommitContext, BeforeCommitContext, HookArtifact, HookFailureContext, HookFailurePhase, ServerHooks } from './hooks.js';
|
|
5
|
+
export { fixedSeriesKey, type ReleaseCoordinates, type VersionGranularity } from './release.js';
|
|
4
6
|
export { parseAllowlist } from './allowlist.js';
|
|
5
|
-
export type {
|
|
7
|
+
export type { ArtifactKind } from './types.js';
|
|
8
|
+
export type { UploadTarget } from './files.js';
|
|
9
|
+
export type { SymlinkRule } from './symlinks.js';
|
|
6
10
|
export { Server, type AllowlistKey, type RecoveryEvent, type RetentionEvent, type ServerCloseEvent, type ServerConnectionEvent, type ServerEvent, type ServerEventName, type ServerEventMap, type ServerListeningEvent, type ServerOfferEvent, type ServerOptions, type ServerProgressEvent, type ServerTransferLifecycleEvent } from './server.js';
|
|
7
|
-
export { Client, type
|
|
8
|
-
export type { StorageAdapter, StorageFileHandle, StorageMkdirOptions, StorageReadResult, StorageRmOptions, StorageStatFs, StorageStats, StorageWriteResult } from './storage/types.js';
|
|
11
|
+
export { Client, type ClientEvent, type ClientEventMap, type ClientEventName, type ClientOfferEvent, type ClientOptions, type ClientProgressEvent, type ClientResultEvent, type ClientUploadResult, type UploadResult, type UploadStatus } from './client.js';
|
|
12
|
+
export type { StorageAdapter, StorageFileHandle, StorageMkdirOptions, StorageReadResult, StorageRmOptions, StorageStatFs, StorageStats, StorageWriteResult, SymlinkCapableStorage } from './storage/types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Client = exports.Server = exports.parseAllowlist = exports.publicKeyFromSeed = exports.keyPairFromSeed = exports.generateSeed = exports.parsePublicKey = exports.parseSeed = exports.SwarmDeployError = exports.ERRORS = void 0;
|
|
3
|
+
exports.Client = exports.Server = exports.parseAllowlist = exports.fixedSeriesKey = exports.publicKeyFromSeed = exports.keyPairFromSeed = exports.generateSeed = exports.parsePublicKey = exports.parseSeed = exports.SwarmDeployError = exports.ERRORS = void 0;
|
|
4
4
|
var errors_js_1 = require("./errors.js");
|
|
5
5
|
Object.defineProperty(exports, "ERRORS", { enumerable: true, get: function () { return errors_js_1.ERRORS; } });
|
|
6
6
|
Object.defineProperty(exports, "SwarmDeployError", { enumerable: true, get: function () { return errors_js_1.SwarmDeployError; } });
|
|
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "parsePublicKey", { enumerable: true, get: functi
|
|
|
10
10
|
Object.defineProperty(exports, "generateSeed", { enumerable: true, get: function () { return identity_js_1.generateSeed; } });
|
|
11
11
|
Object.defineProperty(exports, "keyPairFromSeed", { enumerable: true, get: function () { return identity_js_1.keyPairFromSeed; } });
|
|
12
12
|
Object.defineProperty(exports, "publicKeyFromSeed", { enumerable: true, get: function () { return identity_js_1.publicKeyFromSeed; } });
|
|
13
|
+
var release_js_1 = require("./release.js");
|
|
14
|
+
Object.defineProperty(exports, "fixedSeriesKey", { enumerable: true, get: function () { return release_js_1.fixedSeriesKey; } });
|
|
13
15
|
var allowlist_js_1 = require("./allowlist.js");
|
|
14
16
|
Object.defineProperty(exports, "parseAllowlist", { enumerable: true, get: function () { return allowlist_js_1.parseAllowlist; } });
|
|
15
17
|
var server_js_1 = require("./server.js");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAsE;AAA7D,mGAAA,MAAM,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAcjC,6CAMsB;AALpB,wGAAA,SAAS,OAAA;AACT,6GAAA,cAAc,OAAA;AACd,2GAAA,YAAY,OAAA;AACZ,8GAAA,eAAe,OAAA;AACf,gHAAA,iBAAiB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAsE;AAA7D,mGAAA,MAAM,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAcjC,6CAMsB;AALpB,wGAAA,SAAS,OAAA;AACT,6GAAA,cAAc,OAAA;AACd,2GAAA,YAAY,OAAA;AACZ,8GAAA,eAAe,OAAA;AACf,gHAAA,iBAAiB,OAAA;AAUnB,2CAA+F;AAAtF,4GAAA,cAAc,OAAA;AACvB,+CAA+C;AAAtC,8GAAA,cAAc,OAAA;AAIvB,yCAeoB;AAdlB,mGAAA,MAAM,OAAA;AAeR,yCAYoB;AAXlB,mGAAA,MAAM,OAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type VersionGranularity = 'major' | 'minor';
|
|
2
|
+
export interface ReleaseCoordinates {
|
|
3
|
+
series: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
}
|
|
6
|
+
/** Protocol: artifact basenames are one safe path component, max 100 UTF-8 bytes (`+` disallowed). */
|
|
7
|
+
export declare const MAX_RELEASE_COMPONENT_BYTES = 100;
|
|
8
|
+
/**
|
|
9
|
+
* The persisted series of a template that captures no `{series}`.
|
|
10
|
+
*
|
|
11
|
+
* The template text itself cannot be the key: braces and slashes fail the
|
|
12
|
+
* commit-record basename rules, so every version-only pattern would reject at
|
|
13
|
+
* commit time. Hashing gives a safe 70-byte basename that is stable across
|
|
14
|
+
* restarts and identical on Node and Bare, and distinct templates that happen
|
|
15
|
+
* to describe the same literals stay distinct releases.
|
|
16
|
+
*/
|
|
17
|
+
export declare function fixedSeriesKey(template: string): string;
|
|
18
|
+
export declare class ReleaseMatcher {
|
|
19
|
+
#private;
|
|
20
|
+
constructor(patterns: Iterable<string>);
|
|
21
|
+
get size(): number;
|
|
22
|
+
get hasVersionPattern(): boolean;
|
|
23
|
+
match(name: string, sourceParent?: string): ReleaseCoordinates | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function compareReleaseVersions(left: string, right: string): number;
|
|
26
|
+
export declare function releaseVersionGroup(version: string, granularity: VersionGranularity): string;
|
package/dist/release.js
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ReleaseMatcher = exports.MAX_RELEASE_COMPONENT_BYTES = void 0;
|
|
7
|
+
exports.fixedSeriesKey = fixedSeriesKey;
|
|
8
|
+
exports.compareReleaseVersions = compareReleaseVersions;
|
|
9
|
+
exports.releaseVersionGroup = releaseVersionGroup;
|
|
10
|
+
const b4a_1 = __importDefault(require("b4a"));
|
|
11
|
+
const semver_1 = require("semver");
|
|
12
|
+
const files_js_1 = require("./files.js");
|
|
13
|
+
const hash_js_1 = require("./tar-protocol/hash.js");
|
|
14
|
+
const PLACEHOLDER_SERIES = '{series}';
|
|
15
|
+
const PLACEHOLDER_VERSION = '{version}';
|
|
16
|
+
/** Protocol: artifact basenames are one safe path component, max 100 UTF-8 bytes (`+` disallowed). */
|
|
17
|
+
exports.MAX_RELEASE_COMPONENT_BYTES = 100;
|
|
18
|
+
const SAFE_ARTIFACT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
19
|
+
/** Immediate source parent: same 100-byte single-component rule; `+` allowed for SemVer build metadata. */
|
|
20
|
+
const SAFE_SOURCE_PARENT = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
|
|
21
|
+
const FIXED_SERIES_PREFIX = 'fixed-';
|
|
22
|
+
/** The exact shape `fixedSeriesKey` produces; captures may not occupy it. */
|
|
23
|
+
const RESERVED_FIXED_SERIES = /^fixed-[0-9a-f]{64}$/;
|
|
24
|
+
/**
|
|
25
|
+
* The persisted series of a template that captures no `{series}`.
|
|
26
|
+
*
|
|
27
|
+
* The template text itself cannot be the key: braces and slashes fail the
|
|
28
|
+
* commit-record basename rules, so every version-only pattern would reject at
|
|
29
|
+
* commit time. Hashing gives a safe 70-byte basename that is stable across
|
|
30
|
+
* restarts and identical on Node and Bare, and distinct templates that happen
|
|
31
|
+
* to describe the same literals stay distinct releases.
|
|
32
|
+
*/
|
|
33
|
+
function fixedSeriesKey(template) {
|
|
34
|
+
return `${FIXED_SERIES_PREFIX}${b4a_1.default.toString((0, hash_js_1.sodiumSha256)(b4a_1.default.from(template)), 'hex')}`;
|
|
35
|
+
}
|
|
36
|
+
function placeholderCount(template, placeholder) {
|
|
37
|
+
let count = 0;
|
|
38
|
+
let index = 0;
|
|
39
|
+
while ((index = template.indexOf(placeholder, index)) !== -1) {
|
|
40
|
+
count++;
|
|
41
|
+
index += placeholder.length;
|
|
42
|
+
}
|
|
43
|
+
return count;
|
|
44
|
+
}
|
|
45
|
+
function assertPlaceholderLayout(template) {
|
|
46
|
+
if (template.includes(`${PLACEHOLDER_SERIES}${PLACEHOLDER_VERSION}`) ||
|
|
47
|
+
template.includes(`${PLACEHOLDER_VERSION}${PLACEHOLDER_SERIES}`)) {
|
|
48
|
+
throw new Error('Invalid release template: adjacent placeholder');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Literal text is matched verbatim, so a stray brace is always a typo, not a literal. */
|
|
52
|
+
function assertNoResidualBraces(template) {
|
|
53
|
+
const literals = template.split(PLACEHOLDER_SERIES).join('').split(PLACEHOLDER_VERSION).join('');
|
|
54
|
+
if (literals.includes('{') || literals.includes('}')) {
|
|
55
|
+
throw new Error('Invalid release template: unexpected brace');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function componentByteLength(value) {
|
|
59
|
+
return b4a_1.default.from(value).byteLength;
|
|
60
|
+
}
|
|
61
|
+
function isSafeArtifactName(value) {
|
|
62
|
+
return (typeof value === 'string' &&
|
|
63
|
+
value.length > 0 &&
|
|
64
|
+
SAFE_ARTIFACT.test(value) &&
|
|
65
|
+
componentByteLength(value) <= exports.MAX_RELEASE_COMPONENT_BYTES);
|
|
66
|
+
}
|
|
67
|
+
function isSafeSourceParent(value) {
|
|
68
|
+
return (typeof value === 'string' &&
|
|
69
|
+
value.length > 0 &&
|
|
70
|
+
SAFE_SOURCE_PARENT.test(value) &&
|
|
71
|
+
componentByteLength(value) <= exports.MAX_RELEASE_COMPONENT_BYTES &&
|
|
72
|
+
!value.includes('/') &&
|
|
73
|
+
!value.includes('\\'));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A captured `{series}` may not take the shape of a derived fixed-series key,
|
|
77
|
+
* so an upload cannot name itself into the rotation group of a version-only
|
|
78
|
+
* pattern. The namespace is reserved rather than escaped: a collision is a
|
|
79
|
+
* non-match, which fails closed as an unmatched offer.
|
|
80
|
+
*/
|
|
81
|
+
function isSafeSeriesCapture(value) {
|
|
82
|
+
return (SAFE_ARTIFACT.test(value) &&
|
|
83
|
+
componentByteLength(value) <= exports.MAX_RELEASE_COMPONENT_BYTES &&
|
|
84
|
+
!RESERVED_FIXED_SERIES.test(value));
|
|
85
|
+
}
|
|
86
|
+
function validateStrictReleaseVersion(raw) {
|
|
87
|
+
if (raw.length === 0 || componentByteLength(raw) > exports.MAX_RELEASE_COMPONENT_BYTES) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
if (raw.trim() !== raw)
|
|
91
|
+
return undefined;
|
|
92
|
+
if (/^[vV]/.test(raw))
|
|
93
|
+
return undefined;
|
|
94
|
+
if (/[/\\]/.test(raw))
|
|
95
|
+
return undefined;
|
|
96
|
+
try {
|
|
97
|
+
const parsed = new semver_1.SemVer(raw, { loose: false });
|
|
98
|
+
const core = raw.split('+', 1)[0];
|
|
99
|
+
if (core !== parsed.version)
|
|
100
|
+
return undefined;
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function normalizeCapturedVersion(raw) {
|
|
108
|
+
return validateStrictReleaseVersion(raw)?.version;
|
|
109
|
+
}
|
|
110
|
+
function parseReleaseVersion(raw) {
|
|
111
|
+
const parsed = validateStrictReleaseVersion(raw);
|
|
112
|
+
if (!parsed)
|
|
113
|
+
throw new Error('Invalid release version');
|
|
114
|
+
return parsed;
|
|
115
|
+
}
|
|
116
|
+
function parseSegmentParts(template) {
|
|
117
|
+
const parts = [];
|
|
118
|
+
let cursor = 0;
|
|
119
|
+
while (cursor < template.length) {
|
|
120
|
+
const seriesAt = template.indexOf(PLACEHOLDER_SERIES, cursor);
|
|
121
|
+
const versionAt = template.indexOf(PLACEHOLDER_VERSION, cursor);
|
|
122
|
+
let next = -1;
|
|
123
|
+
let kind = null;
|
|
124
|
+
if (seriesAt !== -1 && (versionAt === -1 || seriesAt < versionAt)) {
|
|
125
|
+
next = seriesAt;
|
|
126
|
+
kind = 'series';
|
|
127
|
+
}
|
|
128
|
+
else if (versionAt !== -1) {
|
|
129
|
+
next = versionAt;
|
|
130
|
+
kind = 'version';
|
|
131
|
+
}
|
|
132
|
+
if (next === -1) {
|
|
133
|
+
parts.push({ kind: 'literal', text: template.slice(cursor) });
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (next > cursor) {
|
|
137
|
+
parts.push({ kind: 'literal', text: template.slice(cursor, next) });
|
|
138
|
+
}
|
|
139
|
+
parts.push({ kind: kind });
|
|
140
|
+
cursor = next + (kind === 'series' ? PLACEHOLDER_SERIES.length : PLACEHOLDER_VERSION.length);
|
|
141
|
+
}
|
|
142
|
+
return parts;
|
|
143
|
+
}
|
|
144
|
+
function compileSegmentPlan(template) {
|
|
145
|
+
const seriesCount = placeholderCount(template, PLACEHOLDER_SERIES);
|
|
146
|
+
const versionCount = placeholderCount(template, PLACEHOLDER_VERSION);
|
|
147
|
+
if (seriesCount > 1 || versionCount > 1) {
|
|
148
|
+
throw new Error('Invalid release template: repeated placeholder');
|
|
149
|
+
}
|
|
150
|
+
const parts = parseSegmentParts(template);
|
|
151
|
+
return {
|
|
152
|
+
plan: { parts },
|
|
153
|
+
hasSeries: seriesCount > 0,
|
|
154
|
+
hasVersion: versionCount > 0
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function compileParentMatcher(parent) {
|
|
158
|
+
if (placeholderCount(parent, PLACEHOLDER_SERIES) === 0 &&
|
|
159
|
+
placeholderCount(parent, PLACEHOLDER_VERSION) === 0) {
|
|
160
|
+
return { literal: parent, plan: null, hasVersion: false };
|
|
161
|
+
}
|
|
162
|
+
const compiled = compileSegmentPlan(parent);
|
|
163
|
+
return { literal: null, plan: compiled.plan, hasVersion: compiled.hasVersion };
|
|
164
|
+
}
|
|
165
|
+
function compileTemplate(template) {
|
|
166
|
+
assertNoResidualBraces(template);
|
|
167
|
+
assertPlaceholderLayout(template);
|
|
168
|
+
const seriesCount = placeholderCount(template, PLACEHOLDER_SERIES);
|
|
169
|
+
const versionCount = placeholderCount(template, PLACEHOLDER_VERSION);
|
|
170
|
+
if (seriesCount === 0 && versionCount === 0) {
|
|
171
|
+
throw new Error('Invalid release template: missing placeholder');
|
|
172
|
+
}
|
|
173
|
+
if (seriesCount > 1 || versionCount > 1) {
|
|
174
|
+
throw new Error('Invalid release template: repeated placeholder');
|
|
175
|
+
}
|
|
176
|
+
const segments = template.split('/');
|
|
177
|
+
if (segments.some((segment) => segment.length === 0)) {
|
|
178
|
+
throw new Error('Invalid release template: empty segment');
|
|
179
|
+
}
|
|
180
|
+
if (segments.length > 2) {
|
|
181
|
+
throw new Error('Invalid release template: path separator outside parent boundary');
|
|
182
|
+
}
|
|
183
|
+
const fixedSeries = seriesCount === 0 ? fixedSeriesKey(template) : null;
|
|
184
|
+
if (fixedSeries !== null) {
|
|
185
|
+
try {
|
|
186
|
+
(0, files_js_1.validateBasename)(fixedSeries);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
throw new Error('Invalid release template: unusable fixed series key', { cause: error });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (segments.length === 1) {
|
|
193
|
+
const basename = compileSegmentPlan(template);
|
|
194
|
+
return {
|
|
195
|
+
fixedSeries,
|
|
196
|
+
versionInParent: false,
|
|
197
|
+
versionInBasename: basename.hasVersion,
|
|
198
|
+
parent: null,
|
|
199
|
+
basename: basename.plan
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const parentSegment = segments[0];
|
|
203
|
+
const basenameSegment = segments[1];
|
|
204
|
+
const parent = compileParentMatcher(parentSegment);
|
|
205
|
+
const basename = compileSegmentPlan(basenameSegment);
|
|
206
|
+
return {
|
|
207
|
+
fixedSeries,
|
|
208
|
+
versionInParent: parent.hasVersion,
|
|
209
|
+
versionInBasename: basename.hasVersion,
|
|
210
|
+
parent,
|
|
211
|
+
basename: basename.plan
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function assignPlaceholderPair(first, second, left, right) {
|
|
215
|
+
if (first === 'series' && second === 'version') {
|
|
216
|
+
if (!isSafeSeriesCapture(left))
|
|
217
|
+
return null;
|
|
218
|
+
const version = normalizeCapturedVersion(right);
|
|
219
|
+
return version ? { series: left, version } : null;
|
|
220
|
+
}
|
|
221
|
+
if (first === 'version' && second === 'series') {
|
|
222
|
+
const version = normalizeCapturedVersion(left);
|
|
223
|
+
if (!version)
|
|
224
|
+
return null;
|
|
225
|
+
if (!isSafeSeriesCapture(right))
|
|
226
|
+
return null;
|
|
227
|
+
return { series: right, version };
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
function splitSeriesThenVersion(stem) {
|
|
232
|
+
for (let index = stem.lastIndexOf('-'); index > 0; index = stem.lastIndexOf('-', index - 1)) {
|
|
233
|
+
const groups = assignPlaceholderPair('series', 'version', stem.slice(0, index), stem.slice(index + 1));
|
|
234
|
+
if (groups)
|
|
235
|
+
return groups;
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
function splitVersionThenSeries(stem) {
|
|
240
|
+
for (let index = stem.indexOf('-'); index > 0; index = stem.indexOf('-', index + 1)) {
|
|
241
|
+
const groups = assignPlaceholderPair('version', 'series', stem.slice(0, index), stem.slice(index + 1));
|
|
242
|
+
if (groups)
|
|
243
|
+
return groups;
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
function tryLiteralSeparator(stem, separator, first, second) {
|
|
248
|
+
if (separator.length === 0)
|
|
249
|
+
return null;
|
|
250
|
+
let index = stem.indexOf(separator);
|
|
251
|
+
while (index !== -1) {
|
|
252
|
+
const left = stem.slice(0, index);
|
|
253
|
+
const right = stem.slice(index + separator.length);
|
|
254
|
+
const groups = assignPlaceholderPair(first, second, left, right);
|
|
255
|
+
if (groups)
|
|
256
|
+
return groups;
|
|
257
|
+
index = stem.indexOf(separator, index + 1);
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
function matchStem(middle, stem) {
|
|
262
|
+
if (middle.length === 1) {
|
|
263
|
+
const part = middle[0];
|
|
264
|
+
if (part.kind === 'series') {
|
|
265
|
+
return isSafeSeriesCapture(stem) ? { series: stem } : null;
|
|
266
|
+
}
|
|
267
|
+
const version = normalizeCapturedVersion(stem);
|
|
268
|
+
return version ? { version } : null;
|
|
269
|
+
}
|
|
270
|
+
if (middle.length === 3 &&
|
|
271
|
+
middle[0].kind !== 'literal' &&
|
|
272
|
+
middle[1].kind === 'literal' &&
|
|
273
|
+
middle[2].kind !== 'literal') {
|
|
274
|
+
const first = middle[0].kind;
|
|
275
|
+
const separator = middle[1].text;
|
|
276
|
+
const second = middle[2].kind;
|
|
277
|
+
if (first === 'series' && second === 'version' && separator === '-') {
|
|
278
|
+
const split = splitSeriesThenVersion(stem);
|
|
279
|
+
if (split)
|
|
280
|
+
return split;
|
|
281
|
+
}
|
|
282
|
+
if (first === 'version' && second === 'series' && separator === '-') {
|
|
283
|
+
const split = splitVersionThenSeries(stem);
|
|
284
|
+
if (split)
|
|
285
|
+
return split;
|
|
286
|
+
}
|
|
287
|
+
return tryLiteralSeparator(stem, separator, first, second);
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
function matchSegmentPlan(plan, value) {
|
|
292
|
+
const { parts } = plan;
|
|
293
|
+
if (parts.length === 0)
|
|
294
|
+
return null;
|
|
295
|
+
let prefix = '';
|
|
296
|
+
let start = 0;
|
|
297
|
+
while (start < parts.length && parts[start].kind === 'literal') {
|
|
298
|
+
prefix += parts[start].text;
|
|
299
|
+
start++;
|
|
300
|
+
}
|
|
301
|
+
let suffix = '';
|
|
302
|
+
let end = parts.length - 1;
|
|
303
|
+
while (end >= start && parts[end].kind === 'literal') {
|
|
304
|
+
suffix = parts[end].text + suffix;
|
|
305
|
+
end--;
|
|
306
|
+
}
|
|
307
|
+
if (!value.startsWith(prefix) || !value.endsWith(suffix))
|
|
308
|
+
return null;
|
|
309
|
+
const stem = value.slice(prefix.length, value.length - suffix.length);
|
|
310
|
+
if (componentByteLength(stem) > exports.MAX_RELEASE_COMPONENT_BYTES)
|
|
311
|
+
return null;
|
|
312
|
+
const middle = parts.slice(start, end + 1);
|
|
313
|
+
if (middle.length === 0) {
|
|
314
|
+
return stem.length === 0 ? {} : null;
|
|
315
|
+
}
|
|
316
|
+
return matchStem(middle, stem);
|
|
317
|
+
}
|
|
318
|
+
function matchWithParent(pattern, sourceParent, name) {
|
|
319
|
+
if (!isSafeArtifactName(name))
|
|
320
|
+
return null;
|
|
321
|
+
let parentGroups = {};
|
|
322
|
+
const parent = pattern.parent;
|
|
323
|
+
if (parent.literal !== null) {
|
|
324
|
+
if (parent.literal !== sourceParent)
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
const matched = matchSegmentPlan(parent.plan, sourceParent);
|
|
329
|
+
if (matched === null)
|
|
330
|
+
return null;
|
|
331
|
+
parentGroups = matched;
|
|
332
|
+
}
|
|
333
|
+
const basenameGroups = matchSegmentPlan(pattern.basename, name);
|
|
334
|
+
if (basenameGroups === null)
|
|
335
|
+
return null;
|
|
336
|
+
return { ...parentGroups, ...basenameGroups };
|
|
337
|
+
}
|
|
338
|
+
function coordinatesFromGroups(pattern, groups, mode) {
|
|
339
|
+
const series = pattern.fixedSeries ??
|
|
340
|
+
(typeof groups.series === 'string' && groups.series.length > 0 ? groups.series : null);
|
|
341
|
+
if (series === null)
|
|
342
|
+
return null;
|
|
343
|
+
if (pattern.fixedSeries === null && !isSafeSeriesCapture(series))
|
|
344
|
+
return null;
|
|
345
|
+
const normalized = groups.version;
|
|
346
|
+
const requiresVersion = mode === 'basename'
|
|
347
|
+
? pattern.versionInBasename
|
|
348
|
+
: pattern.versionInBasename || pattern.versionInParent;
|
|
349
|
+
if (requiresVersion && normalized === undefined)
|
|
350
|
+
return null;
|
|
351
|
+
if (normalized !== undefined) {
|
|
352
|
+
return { series, version: normalized };
|
|
353
|
+
}
|
|
354
|
+
return { series };
|
|
355
|
+
}
|
|
356
|
+
class ReleaseMatcher {
|
|
357
|
+
#patterns;
|
|
358
|
+
constructor(patterns) {
|
|
359
|
+
const seen = new Set();
|
|
360
|
+
const compiled = [];
|
|
361
|
+
for (const template of patterns) {
|
|
362
|
+
if (typeof template !== 'string') {
|
|
363
|
+
throw new Error('Invalid release template');
|
|
364
|
+
}
|
|
365
|
+
if (seen.has(template)) {
|
|
366
|
+
throw new Error('Invalid release template: duplicate template');
|
|
367
|
+
}
|
|
368
|
+
seen.add(template);
|
|
369
|
+
compiled.push(compileTemplate(template));
|
|
370
|
+
}
|
|
371
|
+
this.#patterns = compiled;
|
|
372
|
+
}
|
|
373
|
+
get size() {
|
|
374
|
+
return this.#patterns.length;
|
|
375
|
+
}
|
|
376
|
+
get hasVersionPattern() {
|
|
377
|
+
return this.#patterns.some((pattern) => pattern.versionInBasename || pattern.versionInParent);
|
|
378
|
+
}
|
|
379
|
+
match(name, sourceParent) {
|
|
380
|
+
if (!isSafeArtifactName(name))
|
|
381
|
+
return null;
|
|
382
|
+
if (sourceParent !== undefined && !isSafeSourceParent(sourceParent))
|
|
383
|
+
return null;
|
|
384
|
+
for (const pattern of this.#patterns) {
|
|
385
|
+
if (pattern.parent !== null) {
|
|
386
|
+
if (sourceParent === undefined)
|
|
387
|
+
continue;
|
|
388
|
+
const groups = matchWithParent(pattern, sourceParent, name);
|
|
389
|
+
if (groups) {
|
|
390
|
+
const coords = coordinatesFromGroups(pattern, groups, 'full');
|
|
391
|
+
if (coords)
|
|
392
|
+
return coords;
|
|
393
|
+
}
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const basenameGroups = matchSegmentPlan(pattern.basename, name);
|
|
397
|
+
if (basenameGroups) {
|
|
398
|
+
const coords = coordinatesFromGroups(pattern, basenameGroups, 'basename');
|
|
399
|
+
if (coords)
|
|
400
|
+
return coords;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
exports.ReleaseMatcher = ReleaseMatcher;
|
|
407
|
+
function compareReleaseVersions(left, right) {
|
|
408
|
+
return (0, semver_1.compare)(parseReleaseVersion(left), parseReleaseVersion(right));
|
|
409
|
+
}
|
|
410
|
+
function releaseVersionGroup(version, granularity) {
|
|
411
|
+
if (granularity !== 'major' && granularity !== 'minor') {
|
|
412
|
+
throw new Error('Invalid version granularity');
|
|
413
|
+
}
|
|
414
|
+
const parsed = parseReleaseVersion(version);
|
|
415
|
+
if (granularity === 'major')
|
|
416
|
+
return String(parsed.major);
|
|
417
|
+
return `${parsed.major}.${parsed.minor}`;
|
|
418
|
+
}
|
|
419
|
+
//# sourceMappingURL=release.js.map
|