mirror of
https://github.com/github/codeql-action.git
synced 2025-12-27 01:30:10 +08:00
693 lines
19 KiB
JavaScript
693 lines
19 KiB
JavaScript
// pkg/dist-src/createLogger.js
|
|
var createLogger = (logger) => ({
|
|
debug: () => {
|
|
},
|
|
info: () => {
|
|
},
|
|
warn: console.warn.bind(console),
|
|
error: console.error.bind(console),
|
|
...logger
|
|
});
|
|
|
|
// pkg/dist-src/generated/webhook-names.js
|
|
var emitterEventNames = [
|
|
"branch_protection_configuration",
|
|
"branch_protection_configuration.disabled",
|
|
"branch_protection_configuration.enabled",
|
|
"branch_protection_rule",
|
|
"branch_protection_rule.created",
|
|
"branch_protection_rule.deleted",
|
|
"branch_protection_rule.edited",
|
|
"check_run",
|
|
"check_run.completed",
|
|
"check_run.created",
|
|
"check_run.requested_action",
|
|
"check_run.rerequested",
|
|
"check_suite",
|
|
"check_suite.completed",
|
|
"check_suite.requested",
|
|
"check_suite.rerequested",
|
|
"code_scanning_alert",
|
|
"code_scanning_alert.appeared_in_branch",
|
|
"code_scanning_alert.closed_by_user",
|
|
"code_scanning_alert.created",
|
|
"code_scanning_alert.fixed",
|
|
"code_scanning_alert.reopened",
|
|
"code_scanning_alert.reopened_by_user",
|
|
"commit_comment",
|
|
"commit_comment.created",
|
|
"create",
|
|
"custom_property",
|
|
"custom_property.created",
|
|
"custom_property.deleted",
|
|
"custom_property.updated",
|
|
"custom_property_values",
|
|
"custom_property_values.updated",
|
|
"delete",
|
|
"dependabot_alert",
|
|
"dependabot_alert.auto_dismissed",
|
|
"dependabot_alert.auto_reopened",
|
|
"dependabot_alert.created",
|
|
"dependabot_alert.dismissed",
|
|
"dependabot_alert.fixed",
|
|
"dependabot_alert.reintroduced",
|
|
"dependabot_alert.reopened",
|
|
"deploy_key",
|
|
"deploy_key.created",
|
|
"deploy_key.deleted",
|
|
"deployment",
|
|
"deployment.created",
|
|
"deployment_protection_rule",
|
|
"deployment_protection_rule.requested",
|
|
"deployment_review",
|
|
"deployment_review.approved",
|
|
"deployment_review.rejected",
|
|
"deployment_review.requested",
|
|
"deployment_status",
|
|
"deployment_status.created",
|
|
"discussion",
|
|
"discussion.answered",
|
|
"discussion.category_changed",
|
|
"discussion.closed",
|
|
"discussion.created",
|
|
"discussion.deleted",
|
|
"discussion.edited",
|
|
"discussion.labeled",
|
|
"discussion.locked",
|
|
"discussion.pinned",
|
|
"discussion.reopened",
|
|
"discussion.transferred",
|
|
"discussion.unanswered",
|
|
"discussion.unlabeled",
|
|
"discussion.unlocked",
|
|
"discussion.unpinned",
|
|
"discussion_comment",
|
|
"discussion_comment.created",
|
|
"discussion_comment.deleted",
|
|
"discussion_comment.edited",
|
|
"fork",
|
|
"github_app_authorization",
|
|
"github_app_authorization.revoked",
|
|
"gollum",
|
|
"installation",
|
|
"installation.created",
|
|
"installation.deleted",
|
|
"installation.new_permissions_accepted",
|
|
"installation.suspend",
|
|
"installation.unsuspend",
|
|
"installation_repositories",
|
|
"installation_repositories.added",
|
|
"installation_repositories.removed",
|
|
"installation_target",
|
|
"installation_target.renamed",
|
|
"issue_comment",
|
|
"issue_comment.created",
|
|
"issue_comment.deleted",
|
|
"issue_comment.edited",
|
|
"issues",
|
|
"issues.assigned",
|
|
"issues.closed",
|
|
"issues.deleted",
|
|
"issues.demilestoned",
|
|
"issues.edited",
|
|
"issues.labeled",
|
|
"issues.locked",
|
|
"issues.milestoned",
|
|
"issues.opened",
|
|
"issues.pinned",
|
|
"issues.reopened",
|
|
"issues.transferred",
|
|
"issues.unassigned",
|
|
"issues.unlabeled",
|
|
"issues.unlocked",
|
|
"issues.unpinned",
|
|
"label",
|
|
"label.created",
|
|
"label.deleted",
|
|
"label.edited",
|
|
"marketplace_purchase",
|
|
"marketplace_purchase.cancelled",
|
|
"marketplace_purchase.changed",
|
|
"marketplace_purchase.pending_change",
|
|
"marketplace_purchase.pending_change_cancelled",
|
|
"marketplace_purchase.purchased",
|
|
"member",
|
|
"member.added",
|
|
"member.edited",
|
|
"member.removed",
|
|
"membership",
|
|
"membership.added",
|
|
"membership.removed",
|
|
"merge_group",
|
|
"merge_group.checks_requested",
|
|
"merge_group.destroyed",
|
|
"meta",
|
|
"meta.deleted",
|
|
"milestone",
|
|
"milestone.closed",
|
|
"milestone.created",
|
|
"milestone.deleted",
|
|
"milestone.edited",
|
|
"milestone.opened",
|
|
"org_block",
|
|
"org_block.blocked",
|
|
"org_block.unblocked",
|
|
"organization",
|
|
"organization.deleted",
|
|
"organization.member_added",
|
|
"organization.member_invited",
|
|
"organization.member_removed",
|
|
"organization.renamed",
|
|
"package",
|
|
"package.published",
|
|
"package.updated",
|
|
"page_build",
|
|
"personal_access_token_request",
|
|
"personal_access_token_request.approved",
|
|
"personal_access_token_request.cancelled",
|
|
"personal_access_token_request.created",
|
|
"personal_access_token_request.denied",
|
|
"ping",
|
|
"project",
|
|
"project.closed",
|
|
"project.created",
|
|
"project.deleted",
|
|
"project.edited",
|
|
"project.reopened",
|
|
"project_card",
|
|
"project_card.converted",
|
|
"project_card.created",
|
|
"project_card.deleted",
|
|
"project_card.edited",
|
|
"project_card.moved",
|
|
"project_column",
|
|
"project_column.created",
|
|
"project_column.deleted",
|
|
"project_column.edited",
|
|
"project_column.moved",
|
|
"projects_v2",
|
|
"projects_v2.closed",
|
|
"projects_v2.created",
|
|
"projects_v2.deleted",
|
|
"projects_v2.edited",
|
|
"projects_v2.reopened",
|
|
"projects_v2_item",
|
|
"projects_v2_item.archived",
|
|
"projects_v2_item.converted",
|
|
"projects_v2_item.created",
|
|
"projects_v2_item.deleted",
|
|
"projects_v2_item.edited",
|
|
"projects_v2_item.reordered",
|
|
"projects_v2_item.restored",
|
|
"projects_v2_status_update",
|
|
"projects_v2_status_update.created",
|
|
"projects_v2_status_update.deleted",
|
|
"projects_v2_status_update.edited",
|
|
"public",
|
|
"pull_request",
|
|
"pull_request.assigned",
|
|
"pull_request.auto_merge_disabled",
|
|
"pull_request.auto_merge_enabled",
|
|
"pull_request.closed",
|
|
"pull_request.converted_to_draft",
|
|
"pull_request.demilestoned",
|
|
"pull_request.dequeued",
|
|
"pull_request.edited",
|
|
"pull_request.enqueued",
|
|
"pull_request.labeled",
|
|
"pull_request.locked",
|
|
"pull_request.milestoned",
|
|
"pull_request.opened",
|
|
"pull_request.ready_for_review",
|
|
"pull_request.reopened",
|
|
"pull_request.review_request_removed",
|
|
"pull_request.review_requested",
|
|
"pull_request.synchronize",
|
|
"pull_request.unassigned",
|
|
"pull_request.unlabeled",
|
|
"pull_request.unlocked",
|
|
"pull_request_review",
|
|
"pull_request_review.dismissed",
|
|
"pull_request_review.edited",
|
|
"pull_request_review.submitted",
|
|
"pull_request_review_comment",
|
|
"pull_request_review_comment.created",
|
|
"pull_request_review_comment.deleted",
|
|
"pull_request_review_comment.edited",
|
|
"pull_request_review_thread",
|
|
"pull_request_review_thread.resolved",
|
|
"pull_request_review_thread.unresolved",
|
|
"push",
|
|
"registry_package",
|
|
"registry_package.published",
|
|
"registry_package.updated",
|
|
"release",
|
|
"release.created",
|
|
"release.deleted",
|
|
"release.edited",
|
|
"release.prereleased",
|
|
"release.published",
|
|
"release.released",
|
|
"release.unpublished",
|
|
"repository",
|
|
"repository.archived",
|
|
"repository.created",
|
|
"repository.deleted",
|
|
"repository.edited",
|
|
"repository.privatized",
|
|
"repository.publicized",
|
|
"repository.renamed",
|
|
"repository.transferred",
|
|
"repository.unarchived",
|
|
"repository_advisory",
|
|
"repository_advisory.published",
|
|
"repository_advisory.reported",
|
|
"repository_dispatch",
|
|
"repository_dispatch.sample.collected",
|
|
"repository_import",
|
|
"repository_ruleset",
|
|
"repository_ruleset.created",
|
|
"repository_ruleset.deleted",
|
|
"repository_ruleset.edited",
|
|
"repository_vulnerability_alert",
|
|
"repository_vulnerability_alert.create",
|
|
"repository_vulnerability_alert.dismiss",
|
|
"repository_vulnerability_alert.reopen",
|
|
"repository_vulnerability_alert.resolve",
|
|
"secret_scanning_alert",
|
|
"secret_scanning_alert.created",
|
|
"secret_scanning_alert.publicly_leaked",
|
|
"secret_scanning_alert.reopened",
|
|
"secret_scanning_alert.resolved",
|
|
"secret_scanning_alert.validated",
|
|
"secret_scanning_alert_location",
|
|
"secret_scanning_alert_location.created",
|
|
"secret_scanning_scan",
|
|
"secret_scanning_scan.completed",
|
|
"security_advisory",
|
|
"security_advisory.published",
|
|
"security_advisory.updated",
|
|
"security_advisory.withdrawn",
|
|
"security_and_analysis",
|
|
"sponsorship",
|
|
"sponsorship.cancelled",
|
|
"sponsorship.created",
|
|
"sponsorship.edited",
|
|
"sponsorship.pending_cancellation",
|
|
"sponsorship.pending_tier_change",
|
|
"sponsorship.tier_changed",
|
|
"star",
|
|
"star.created",
|
|
"star.deleted",
|
|
"status",
|
|
"sub_issues",
|
|
"sub_issues.parent_issue_added",
|
|
"sub_issues.parent_issue_removed",
|
|
"sub_issues.sub_issue_added",
|
|
"sub_issues.sub_issue_removed",
|
|
"team",
|
|
"team.added_to_repository",
|
|
"team.created",
|
|
"team.deleted",
|
|
"team.edited",
|
|
"team.removed_from_repository",
|
|
"team_add",
|
|
"watch",
|
|
"watch.started",
|
|
"workflow_dispatch",
|
|
"workflow_job",
|
|
"workflow_job.completed",
|
|
"workflow_job.in_progress",
|
|
"workflow_job.queued",
|
|
"workflow_job.waiting",
|
|
"workflow_run",
|
|
"workflow_run.completed",
|
|
"workflow_run.in_progress",
|
|
"workflow_run.requested"
|
|
];
|
|
|
|
// pkg/dist-src/event-handler/on.js
|
|
function handleEventHandlers(state, webhookName, handler) {
|
|
if (!state.hooks[webhookName]) {
|
|
state.hooks[webhookName] = [];
|
|
}
|
|
state.hooks[webhookName].push(handler);
|
|
}
|
|
function receiverOn(state, webhookNameOrNames, handler) {
|
|
if (Array.isArray(webhookNameOrNames)) {
|
|
webhookNameOrNames.forEach(
|
|
(webhookName) => receiverOn(state, webhookName, handler)
|
|
);
|
|
return;
|
|
}
|
|
if (["*", "error"].includes(webhookNameOrNames)) {
|
|
const webhookName = webhookNameOrNames === "*" ? "any" : webhookNameOrNames;
|
|
const message = `Using the "${webhookNameOrNames}" event with the regular Webhooks.on() function is not supported. Please use the Webhooks.on${webhookName.charAt(0).toUpperCase() + webhookName.slice(1)}() method instead`;
|
|
throw new Error(message);
|
|
}
|
|
if (!emitterEventNames.includes(webhookNameOrNames)) {
|
|
state.log.warn(
|
|
`"${webhookNameOrNames}" is not a known webhook name (https://developer.github.com/v3/activity/events/types/)`
|
|
);
|
|
}
|
|
handleEventHandlers(state, webhookNameOrNames, handler);
|
|
}
|
|
function receiverOnAny(state, handler) {
|
|
handleEventHandlers(state, "*", handler);
|
|
}
|
|
function receiverOnError(state, handler) {
|
|
handleEventHandlers(state, "error", handler);
|
|
}
|
|
|
|
// pkg/dist-src/event-handler/wrap-error-handler.js
|
|
function wrapErrorHandler(handler, error) {
|
|
let returnValue;
|
|
try {
|
|
returnValue = handler(error);
|
|
} catch (error2) {
|
|
console.log('FATAL: Error occurred in "error" event handler');
|
|
console.log(error2);
|
|
}
|
|
if (returnValue && returnValue.catch) {
|
|
returnValue.catch((error2) => {
|
|
console.log('FATAL: Error occurred in "error" event handler');
|
|
console.log(error2);
|
|
});
|
|
}
|
|
}
|
|
|
|
// pkg/dist-src/event-handler/receive.js
|
|
function getHooks(state, eventPayloadAction, eventName) {
|
|
const hooks = [state.hooks[eventName], state.hooks["*"]];
|
|
if (eventPayloadAction) {
|
|
hooks.unshift(state.hooks[`${eventName}.${eventPayloadAction}`]);
|
|
}
|
|
return [].concat(...hooks.filter(Boolean));
|
|
}
|
|
function receiverHandle(state, event) {
|
|
const errorHandlers = state.hooks.error || [];
|
|
if (event instanceof Error) {
|
|
const error = Object.assign(new AggregateError([event], event.message), {
|
|
event
|
|
});
|
|
errorHandlers.forEach((handler) => wrapErrorHandler(handler, error));
|
|
return Promise.reject(error);
|
|
}
|
|
if (!event || !event.name) {
|
|
const error = new Error("Event name not passed");
|
|
throw new AggregateError([error], error.message);
|
|
}
|
|
if (!event.payload) {
|
|
const error = new Error("Event name not passed");
|
|
throw new AggregateError([error], error.message);
|
|
}
|
|
const hooks = getHooks(
|
|
state,
|
|
"action" in event.payload ? event.payload.action : null,
|
|
event.name
|
|
);
|
|
if (hooks.length === 0) {
|
|
return Promise.resolve();
|
|
}
|
|
const errors = [];
|
|
const promises = hooks.map((handler) => {
|
|
let promise = Promise.resolve(event);
|
|
if (state.transform) {
|
|
promise = promise.then(state.transform);
|
|
}
|
|
return promise.then((event2) => {
|
|
return handler(event2);
|
|
}).catch((error) => errors.push(Object.assign(error, { event })));
|
|
});
|
|
return Promise.all(promises).then(() => {
|
|
if (errors.length === 0) {
|
|
return;
|
|
}
|
|
const error = new AggregateError(
|
|
errors,
|
|
errors.map((error2) => error2.message).join("\n")
|
|
);
|
|
Object.assign(error, {
|
|
event
|
|
});
|
|
errorHandlers.forEach((handler) => wrapErrorHandler(handler, error));
|
|
throw error;
|
|
});
|
|
}
|
|
|
|
// pkg/dist-src/event-handler/remove-listener.js
|
|
function removeListener(state, webhookNameOrNames, handler) {
|
|
if (Array.isArray(webhookNameOrNames)) {
|
|
webhookNameOrNames.forEach(
|
|
(webhookName) => removeListener(state, webhookName, handler)
|
|
);
|
|
return;
|
|
}
|
|
if (!state.hooks[webhookNameOrNames]) {
|
|
return;
|
|
}
|
|
for (let i = state.hooks[webhookNameOrNames].length - 1; i >= 0; i--) {
|
|
if (state.hooks[webhookNameOrNames][i] === handler) {
|
|
state.hooks[webhookNameOrNames].splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// pkg/dist-src/event-handler/index.js
|
|
function createEventHandler(options) {
|
|
const state = {
|
|
hooks: {},
|
|
log: createLogger(options && options.log)
|
|
};
|
|
if (options && options.transform) {
|
|
state.transform = options.transform;
|
|
}
|
|
return {
|
|
on: receiverOn.bind(null, state),
|
|
onAny: receiverOnAny.bind(null, state),
|
|
onError: receiverOnError.bind(null, state),
|
|
removeListener: removeListener.bind(null, state),
|
|
receive: receiverHandle.bind(null, state)
|
|
};
|
|
}
|
|
|
|
// pkg/dist-src/index.js
|
|
import { sign, verify } from "@octokit/webhooks-methods";
|
|
|
|
// pkg/dist-src/verify-and-receive.js
|
|
import { verifyWithFallback } from "@octokit/webhooks-methods";
|
|
async function verifyAndReceive(state, event) {
|
|
const matchesSignature = await verifyWithFallback(
|
|
state.secret,
|
|
event.payload,
|
|
event.signature,
|
|
state.additionalSecrets
|
|
).catch(() => false);
|
|
if (!matchesSignature) {
|
|
const error = new Error(
|
|
"[@octokit/webhooks] signature does not match event payload and secret"
|
|
);
|
|
return state.eventHandler.receive(
|
|
Object.assign(error, { event, status: 400 })
|
|
);
|
|
}
|
|
let payload;
|
|
try {
|
|
payload = JSON.parse(event.payload);
|
|
} catch (error) {
|
|
error.message = "Invalid JSON";
|
|
error.status = 400;
|
|
throw new AggregateError([error], error.message);
|
|
}
|
|
return state.eventHandler.receive({
|
|
id: event.id,
|
|
name: event.name,
|
|
payload
|
|
});
|
|
}
|
|
|
|
// pkg/dist-src/middleware/node/get-missing-headers.js
|
|
var WEBHOOK_HEADERS = [
|
|
"x-github-event",
|
|
"x-hub-signature-256",
|
|
"x-github-delivery"
|
|
];
|
|
function getMissingHeaders(request) {
|
|
return WEBHOOK_HEADERS.filter((header) => !(header in request.headers));
|
|
}
|
|
|
|
// pkg/dist-src/middleware/node/get-payload.js
|
|
function getPayload(request) {
|
|
if (typeof request.body === "object" && "rawBody" in request && request.rawBody instanceof Buffer) {
|
|
return Promise.resolve(request.rawBody.toString("utf8"));
|
|
} else if (typeof request.body === "string") {
|
|
return Promise.resolve(request.body);
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
let data = [];
|
|
request.on(
|
|
"error",
|
|
(error) => reject(new AggregateError([error], error.message))
|
|
);
|
|
request.on("data", (chunk) => data.push(chunk));
|
|
request.on(
|
|
"end",
|
|
() => (
|
|
// setImmediate improves the throughput by reducing the pressure from
|
|
// the event loop
|
|
setImmediate(
|
|
resolve,
|
|
data.length === 1 ? data[0].toString("utf8") : Buffer.concat(data).toString("utf8")
|
|
)
|
|
)
|
|
);
|
|
});
|
|
}
|
|
|
|
// pkg/dist-src/middleware/node/on-unhandled-request-default.js
|
|
function onUnhandledRequestDefault(request, response) {
|
|
response.writeHead(404, {
|
|
"content-type": "application/json"
|
|
});
|
|
response.end(
|
|
JSON.stringify({
|
|
error: `Unknown route: ${request.method} ${request.url}`
|
|
})
|
|
);
|
|
}
|
|
|
|
// pkg/dist-src/middleware/node/middleware.js
|
|
async function middleware(webhooks, options, request, response, next) {
|
|
let pathname;
|
|
try {
|
|
pathname = new URL(request.url, "http://localhost").pathname;
|
|
} catch (error) {
|
|
response.writeHead(422, {
|
|
"content-type": "application/json"
|
|
});
|
|
response.end(
|
|
JSON.stringify({
|
|
error: `Request URL could not be parsed: ${request.url}`
|
|
})
|
|
);
|
|
return true;
|
|
}
|
|
if (pathname !== options.path) {
|
|
next?.();
|
|
return false;
|
|
} else if (request.method !== "POST") {
|
|
onUnhandledRequestDefault(request, response);
|
|
return true;
|
|
}
|
|
if (!request.headers["content-type"] || !request.headers["content-type"].startsWith("application/json")) {
|
|
response.writeHead(415, {
|
|
"content-type": "application/json",
|
|
accept: "application/json"
|
|
});
|
|
response.end(
|
|
JSON.stringify({
|
|
error: `Unsupported "Content-Type" header value. Must be "application/json"`
|
|
})
|
|
);
|
|
return true;
|
|
}
|
|
const missingHeaders = getMissingHeaders(request).join(", ");
|
|
if (missingHeaders) {
|
|
response.writeHead(400, {
|
|
"content-type": "application/json"
|
|
});
|
|
response.end(
|
|
JSON.stringify({
|
|
error: `Required headers missing: ${missingHeaders}`
|
|
})
|
|
);
|
|
return true;
|
|
}
|
|
const eventName = request.headers["x-github-event"];
|
|
const signatureSHA256 = request.headers["x-hub-signature-256"];
|
|
const id = request.headers["x-github-delivery"];
|
|
options.log.debug(`${eventName} event received (id: ${id})`);
|
|
let didTimeout = false;
|
|
const timeout = setTimeout(() => {
|
|
didTimeout = true;
|
|
response.statusCode = 202;
|
|
response.end("still processing\n");
|
|
}, 9e3).unref();
|
|
try {
|
|
const payload = await getPayload(request);
|
|
await webhooks.verifyAndReceive({
|
|
id,
|
|
name: eventName,
|
|
payload,
|
|
signature: signatureSHA256
|
|
});
|
|
clearTimeout(timeout);
|
|
if (didTimeout) return true;
|
|
response.end("ok\n");
|
|
return true;
|
|
} catch (error) {
|
|
clearTimeout(timeout);
|
|
if (didTimeout) return true;
|
|
const err = Array.from(error.errors)[0];
|
|
const errorMessage = err.message ? `${err.name}: ${err.message}` : "Error: An Unspecified error occurred";
|
|
response.statusCode = typeof err.status !== "undefined" ? err.status : 500;
|
|
options.log.error(error);
|
|
response.end(
|
|
JSON.stringify({
|
|
error: errorMessage
|
|
})
|
|
);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// pkg/dist-src/middleware/node/index.js
|
|
function createNodeMiddleware(webhooks, {
|
|
path = "/api/github/webhooks",
|
|
log = createLogger()
|
|
} = {}) {
|
|
return middleware.bind(null, webhooks, {
|
|
path,
|
|
log
|
|
});
|
|
}
|
|
|
|
// pkg/dist-src/index.js
|
|
var Webhooks = class {
|
|
sign;
|
|
verify;
|
|
on;
|
|
onAny;
|
|
onError;
|
|
removeListener;
|
|
receive;
|
|
verifyAndReceive;
|
|
constructor(options) {
|
|
if (!options || !options.secret) {
|
|
throw new Error("[@octokit/webhooks] options.secret required");
|
|
}
|
|
const state = {
|
|
eventHandler: createEventHandler(options),
|
|
secret: options.secret,
|
|
additionalSecrets: options.additionalSecrets,
|
|
hooks: {},
|
|
log: createLogger(options.log)
|
|
};
|
|
this.sign = sign.bind(null, options.secret);
|
|
this.verify = verify.bind(null, options.secret);
|
|
this.on = state.eventHandler.on;
|
|
this.onAny = state.eventHandler.onAny;
|
|
this.onError = state.eventHandler.onError;
|
|
this.removeListener = state.eventHandler.removeListener;
|
|
this.receive = state.eventHandler.receive;
|
|
this.verifyAndReceive = verifyAndReceive.bind(null, state);
|
|
}
|
|
};
|
|
export {
|
|
Webhooks,
|
|
createEventHandler,
|
|
createNodeMiddleware,
|
|
emitterEventNames
|
|
};
|