zephyr-agent
Version:
Zephyr plugin agent
353 lines (352 loc) • 15.5 kB
JavaScript
"use strict";
var __webpack_require__ = {};
(()=>{
__webpack_require__.d = (exports1, getters, values)=>{
var define = (defs, kind)=>{
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
enumerable: true,
[kind]: defs[key]
});
};
define(getters, "get");
define(values, "value");
};
})();
(()=>{
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
})();
(()=>{
__webpack_require__.r = (exports1)=>{
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
value: 'Module'
});
Object.defineProperty(exports1, '__esModule', {
value: true
});
};
})();
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
BuildParticipantFailedError: ()=>BuildParticipantFailedError,
BuildSession: ()=>BuildSession,
BuildSessionAbortedError: ()=>BuildSessionAbortedError,
BuildSessionAssetCollisionError: ()=>BuildSessionAssetCollisionError,
BuildSessionNotReadyError: ()=>BuildSessionNotReadyError,
BuildSessionRollbackError: ()=>BuildSessionRollbackError,
BuildSessionStateError: ()=>BuildSessionStateError
});
const ze_build_assets_js_namespaceObject = require("../lib/transformers/ze-build-assets.js");
const ze_snapshot_asset_path_js_namespaceObject = require("../lib/transformers/ze-snapshot-asset-path.js");
class BuildSessionStateError extends Error {
constructor(message){
super(message);
this.name = 'BuildSessionStateError';
}
}
class BuildSessionNotReadyError extends BuildSessionStateError {
readiness;
constructor(readiness){
const pending = [
...readiness.pendingParticipants.map((name)=>`participant:${name}`),
...readiness.pendingPostprocessors.map((name)=>`postprocessor:${name}`)
];
super(`Build session is not ready; waiting for ${pending.join(', ')}`);
this.name = 'BuildSessionNotReadyError';
this.readiness = readiness;
}
}
class BuildSessionAssetCollisionError extends Error {
path;
existingHash;
incomingHash;
constructor(path, existingHash, incomingHash){
super(`Conflicting assets were contributed for "${path}": ${existingHash} and ${incomingHash}`);
this.name = 'BuildSessionAssetCollisionError';
this.path = path;
this.existingHash = existingHash;
this.incomingHash = incomingHash;
}
}
class BuildSessionAbortedError extends Error {
reason;
constructor(reason){
const detail = reason instanceof Error ? reason.message : String(reason ?? 'aborted');
super(`Build session was aborted: ${detail}`);
this.name = 'BuildSessionAbortedError';
this.reason = reason;
}
}
class BuildParticipantFailedError extends Error {
participant;
cause;
constructor(participant, cause){
const detail = cause instanceof Error ? cause.message : String(cause);
super(`Build participant "${participant}" failed: ${detail}`);
this.name = 'BuildParticipantFailedError';
this.participant = participant;
this.cause = cause;
}
}
class BuildSessionRollbackError extends Error {
buildError;
rollbackError;
constructor(buildError, rollbackError){
super(`Build session failed and its rollback also failed: ${rollbackError.message}`);
this.name = 'BuildSessionRollbackError';
this.buildError = buildError;
this.rollbackError = rollbackError;
}
}
function createBarrier() {
let resolve;
let reject;
const promise = new Promise((res, rej)=>{
resolve = res;
reject = rej;
});
promise.catch(()=>void 0);
return {
promise,
resolve,
reject
};
}
function assertNonEmpty(value, description) {
if (!value.trim()) throw new BuildSessionStateError(`${description} must not be empty`);
}
function sameAsset(existing, incoming) {
return normalizeAssetPath(existing.path) === normalizeAssetPath(incoming.path) && existing.hash === incoming.hash && existing.size === incoming.size;
}
function normalizeAssetPath(assetPath) {
try {
return (0, ze_snapshot_asset_path_js_namespaceObject.normalizeSnapshotAssetPath)(assetPath);
} catch (error) {
throw new BuildSessionStateError(error instanceof Error ? error.message : String(error));
}
}
function cloneAssetsMap(assetsMap) {
return Object.fromEntries(Object.values(assetsMap).map((value)=>{
const normalizedPath = normalizeAssetPath(value.path);
const normalizedAsset = normalizedPath === value.path ? {
...value
} : (0, ze_build_assets_js_namespaceObject.zeBuildAssets)({
filepath: normalizedPath,
content: value.buffer
});
return [
normalizedAsset.hash,
Object.freeze(normalizedAsset)
];
}));
}
class BuildSession {
identity;
participants = new Map();
postprocessors = new Set();
completedParticipants = new Set();
completedPostprocessors = new Set();
contributions = new Map();
callbacks;
barrier = createBarrier();
state = 'collecting';
terminalError = null;
failureNotified = false;
sealedPublication = null;
publishPromise = null;
constructor(identity, participants, postprocessors, callbacks){
this.identity = Object.freeze({
...identity
});
this.callbacks = callbacks;
for (const participant of participants){
assertNonEmpty(participant.name, 'Participant name');
if (this.participants.has(participant.name)) throw new BuildSessionStateError(`Build participant "${participant.name}" was registered more than once`);
this.participants.set(participant.name, {
name: participant.name,
role: participant.role ?? participant.name,
required: participant.required ?? true
});
}
for (const postprocessor of postprocessors){
assertNonEmpty(postprocessor, 'Postprocessor name');
if (this.postprocessors.has(postprocessor)) throw new BuildSessionStateError(`Build postprocessor "${postprocessor}" was registered more than once`);
this.postprocessors.add(postprocessor);
}
this.resolveBarrierWhenReady();
}
get status() {
return this.state;
}
get failure() {
return this.terminalError;
}
get readiness() {
const pendingParticipants = [
...this.participants.values()
].filter((participant)=>participant.required && !this.completedParticipants.has(participant.name)).map((participant)=>participant.name);
const pendingPostprocessors = [
...this.postprocessors
].filter((postprocessor)=>!this.completedPostprocessors.has(postprocessor));
return Object.freeze({
ready: 0 === pendingParticipants.length && 0 === pendingPostprocessors.length,
pendingParticipants: Object.freeze(pendingParticipants),
pendingPostprocessors: Object.freeze(pendingPostprocessors)
});
}
get isTerminal() {
return 'published' === this.state || 'failed' === this.state || 'aborted' === this.state;
}
contribute(contribution) {
this.assertCollecting('contribute assets');
const participant = this.participants.get(contribution.participant);
if (!participant) throw new BuildSessionStateError(`Unknown build participant "${contribution.participant}"`);
if (this.completedParticipants.has(contribution.participant)) throw new BuildSessionStateError(`Build participant "${contribution.participant}" is already complete`);
assertNonEmpty(contribution.key, 'Contribution key');
let participantContributions = this.contributions.get(contribution.participant);
if (!participantContributions) {
participantContributions = new Map();
this.contributions.set(contribution.participant, participantContributions);
}
participantContributions.set(contribution.key, {
...contribution,
assetsMap: cloneAssetsMap(contribution.assetsMap)
});
}
completeParticipant(participant) {
this.assertCollecting('complete a participant');
if (!this.participants.has(participant)) throw new BuildSessionStateError(`Unknown build participant "${participant}"`);
this.completedParticipants.add(participant);
this.resolveBarrierWhenReady();
}
completePostprocess(postprocessor) {
this.assertCollecting('complete postprocessing');
if (!this.postprocessors.has(postprocessor)) throw new BuildSessionStateError(`Unknown build postprocessor "${postprocessor}"`);
this.completedPostprocessors.add(postprocessor);
this.resolveBarrierWhenReady();
}
fail(participant, cause) {
if (!this.participants.has(participant)) throw new BuildSessionStateError(`Unknown build participant "${participant}"`);
if (this.terminalError instanceof BuildSessionRollbackError) throw this.terminalError;
if (this.isTerminal) return;
if ('publishing' === this.state) throw new BuildSessionStateError('Cannot fail a build participant after its publisher has started');
this.rejectSession(new BuildParticipantFailedError(participant, cause), 'failed');
}
abort(reason) {
if (this.terminalError instanceof BuildSessionRollbackError) throw this.terminalError;
if (this.isTerminal) return;
if ('publishing' === this.state) throw new BuildSessionStateError('Cannot abort a build session after its publisher has started');
this.rejectSession(new BuildSessionAbortedError(reason), 'aborted');
}
seal() {
if (this.sealedPublication) return this.sealedPublication;
if (this.terminalError) throw this.terminalError;
if ('collecting' !== this.state) throw new BuildSessionStateError(`Cannot seal a build session while it is ${this.state}`);
const readiness = this.readiness;
if (!readiness.ready) throw new BuildSessionNotReadyError(readiness);
this.sealedPublication = this.createPublication();
this.state = 'sealed';
return this.sealedPublication;
}
publish() {
if (!this.publishPromise) this.publishPromise = this.runPublish();
return this.publishPromise;
}
async runPublish() {
try {
await this.barrier.promise;
if (this.terminalError) throw this.terminalError;
const publication = this.seal();
this.state = 'publishing';
await this.callbacks.prepare?.(this.identity);
const result = await this.callbacks.publish(publication);
await this.callbacks.finish?.(publication, result);
this.state = 'published';
return result;
} catch (error) {
if ('aborted' !== this.state) this.state = 'failed';
if (!this.terminalError) this.terminalError = error instanceof Error ? error : new Error(String(error));
this.notifyFailure();
throw this.terminalError;
}
}
createPublication() {
const assetsMap = {};
const assetsByPath = new Map();
const assetsByHash = new Map();
const publishedContributions = [];
for (const participant of this.participants.values()){
const participantContributions = this.contributions.get(participant.name);
if (participantContributions) for (const contribution of participantContributions.values()){
const publishedContribution = {
...contribution,
role: participant.role,
assetsMap: Object.freeze({
...contribution.assetsMap
})
};
publishedContributions.push(Object.freeze(publishedContribution));
for (const incoming of Object.values(contribution.assetsMap)){
const normalizedPath = normalizeAssetPath(incoming.path);
const samePath = assetsByPath.get(normalizedPath);
if (samePath && !sameAsset(samePath, incoming)) throw new BuildSessionAssetCollisionError(normalizedPath, samePath.hash, incoming.hash);
const sameHash = assetsByHash.get(incoming.hash);
if (sameHash && !sameAsset(sameHash, incoming)) throw new BuildSessionAssetCollisionError(incoming.path, sameHash.hash, incoming.hash);
if (!samePath && !sameHash) {
assetsByPath.set(normalizedPath, incoming);
assetsByHash.set(incoming.hash, incoming);
assetsMap[incoming.hash] = incoming;
}
}
}
}
return Object.freeze({
identity: this.identity,
assetsMap: Object.freeze(assetsMap),
contributions: Object.freeze(publishedContributions)
});
}
resolveBarrierWhenReady() {
if ('collecting' === this.state && this.readiness.ready) this.barrier.resolve();
}
rejectSession(error, status) {
this.terminalError = error;
this.state = status;
this.notifyFailure();
this.barrier.reject(this.terminalError);
if (this.terminalError instanceof BuildSessionRollbackError) throw this.terminalError;
}
notifyFailure() {
if (this.failureNotified || !this.terminalError) return;
this.failureNotified = true;
try {
this.callbacks.onFailure?.(this.identity, this.terminalError);
} catch (cleanupError) {
const normalizedCleanupError = cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError));
this.terminalError = new BuildSessionRollbackError(this.terminalError, normalizedCleanupError);
}
}
assertCollecting(action) {
if ('collecting' !== this.state) throw new BuildSessionStateError(`Cannot ${action} while build session is ${this.state}`);
}
}
exports.BuildParticipantFailedError = __webpack_exports__.BuildParticipantFailedError;
exports.BuildSession = __webpack_exports__.BuildSession;
exports.BuildSessionAbortedError = __webpack_exports__.BuildSessionAbortedError;
exports.BuildSessionAssetCollisionError = __webpack_exports__.BuildSessionAssetCollisionError;
exports.BuildSessionNotReadyError = __webpack_exports__.BuildSessionNotReadyError;
exports.BuildSessionRollbackError = __webpack_exports__.BuildSessionRollbackError;
exports.BuildSessionStateError = __webpack_exports__.BuildSessionStateError;
for(var __rspack_i in __webpack_exports__)if (-1 === [
"BuildParticipantFailedError",
"BuildSession",
"BuildSessionAbortedError",
"BuildSessionAssetCollisionError",
"BuildSessionNotReadyError",
"BuildSessionRollbackError",
"BuildSessionStateError"
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
Object.defineProperty(exports, '__esModule', {
value: true
});
//# sourceMappingURL=build-session.js.map