export class CISimulator {
#baseUrl;
#pendingBuilds = new Map();
#webhooks = [];
#testDeliveryFn = null;
constructor(baseUrl) {
this.#baseUrl = baseUrl;
}
registerWebhook({ callbackUrl, buildId, expectedStatus }) {
this.#webhooks.push({ callbackUrl, buildId, expectedStatus });
console.log(`[CI] Registered webhook for build ${buildId}`);
}
startBuild(repo, commit) {
const buildId = `build-${Date.now()}`;
this.#pendingBuilds.set(buildId, { repo, commit, status: 'running' });
console.log(`[CI] Started build ${buildId} for ${repo}@${commit}`);
return buildId;
}
async completeBuild(buildId, status = 'success') {
const build = this.#pendingBuilds.get(buildId);
if (!build) {
throw new Error(`Build ${buildId} not found`);
}
build.status = status;
console.log(`[CI] Build ${buildId} completed with status: ${status}`);
const matchingWebhooks = this.#webhooks.filter((w) => w.buildId === buildId);
const deliveryPromises = matchingWebhooks.map((webhook) =>
this.#deliverWebhook(webhook, {
buildId,
repo: build.repo,
commit: build.commit,
status,
timestamp: new Date().toISOString(),
}, this.#testDeliveryFn)
);
await Promise.all(deliveryPromises);
}
async #deliverWebhook(webhook, payload, deliverFn) {
const { callbackUrl, expectedStatus } = webhook;
console.log(`[CI] Delivering webhook to ${callbackUrl}`);
try {
const url = new URL(callbackUrl);
const token = url.pathname.split('/').pop();
const responseBody = {
token,
json: payload,
metadata: {
source: 'ci-simulator',
buildId: payload.buildId,
},
};
if (deliverFn) {
await deliverFn(token, responseBody);
} else {
const response = await fetch(new URL(url.pathname, this.#baseUrl), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(responseBody),
});
if (!response.ok) {
console.error(`[CI] Webhook delivery failed: ${response.status}`);
} else {
console.log(`[CI] Webhook delivered successfully`);
}
}
} catch (error) {
console.error(`[CI] Webhook delivery error:`, error.message);
throw error;
}
}
getBuildStatus(buildId) {
return this.#pendingBuilds.get(buildId);
}
setTestDeliveryFn(fn) {
this.#testDeliveryFn = fn;
}
}