UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

93 lines • 4.12 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { FileChangeKind } from "../../types.js"; import { eventAt, field, textField } from "../../events/payload.js"; // The most commits GitHub lists in one push delivery; a push at the cap may // have lost some, so its paths are not the whole change. const COMMIT_CAP = 2048; // The lists each pushed commit carries, and the change each one is. const ACTIONS = ['added', 'removed', 'modified']; /** * Map one GitHub `push` webhook delivery onto mount paths. * * The consumer owns the receiver (a repository webhook for `push`) and passes * the event name from the `X-GitHub-Event` header with the JSON body. Nothing * here runs a loop. A mount reads one ref of one repository, so only a push to * that ref maps to anything. * * Each pushed commit lists the paths it added, removed and modified, so a * path's change is its first and last action across the push: one added and * then removed within the push never existed outside it and maps to nothing. * A forced push, a branch created or deleted, and a push at GitHub's commit cap * do not say what changed between the old head and the new one, so the whole * mount is UNKNOWN. Each event evicts the root listing above it, so the next * read fetches the new head's tree. * * Mirrors Python `GitHubEventHook` (`core/github/events.py`). */ export class GitHubEventHook { accessor; constructor(accessor) { this.accessor = accessor; } toEvents(root, eventName, payload) { return Promise.resolve(this.map(root, eventName, payload)); } map(root, eventName, payload) { if (eventName !== 'push') return []; const { owner, repo, ref } = this.accessor; const repository = field(payload, 'repository'); const name = textField(repository, 'full_name') ?? ''; if (name.toLowerCase() !== `${owner}/${repo}`.toLowerCase()) return []; const pushed = textField(payload, 'ref'); if (pushed !== `refs/heads/${ref}` && pushed !== `refs/tags/${ref}`) return []; const commits = field(payload, 'commits'); if (['forced', 'created', 'deleted'].some((flag) => field(payload, flag) === true) || !Array.isArray(commits) || commits.length >= COMMIT_CAP) { return [eventAt(root, '', FileChangeKind.UNKNOWN)]; } const first = new Map(); const last = new Map(); for (const commit of commits) { for (const action of ACTIONS) { const paths = field(commit, action); for (const path of Array.isArray(paths) ? paths : []) { if (typeof path !== 'string') continue; if (!first.has(path)) first.set(path, action); last.set(path, action); } } } const events = []; for (const [path, action] of first) { const final = last.get(path); if (action === 'added' && final === 'removed') continue; const kind = action === 'added' ? FileChangeKind.CREATE : final === 'removed' ? FileChangeKind.DELETE : FileChangeKind.UPDATE; events.push(eventAt(root, path, kind)); } return events; } } //# sourceMappingURL=events.js.map