Files
trackgo-fe/.agents/skills/archify/scripts/update-contract.mjs
T
shancheas 166e0d40ac feat: introduce archify skill for generating architecture diagrams
- Added a new Archify skill, enabling users to create polished architecture, workflow, sequence, data-flow, and lifecycle diagrams.
- Implemented comprehensive functionality including rendering, validation, and delivery of diagrams in various formats.
- Integrated a user-friendly command-line interface for generating and previewing diagrams.
- Developed supporting files including package.json, LICENSE, and SKILL.md for documentation and licensing.
- Added unit tests to ensure reliability and functionality of the new skill.

These changes enhance the application by providing a structured approach to visualizing system architecture and workflows, improving user experience and data representation.
2026-08-31 11:31:29 +07:00

183 lines
6.6 KiB
JavaScript

export const SKILL_ID = 'archify';
export const EXPECTED_REPOSITORY = 'https://github.com/tt-a1i/archify';
export const DEFAULT_MANIFEST_URL = 'https://tt-a1i.github.io/archify/skill-updates/archify/stable.json';
const CONTROL_OR_BIDI = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
const HEX_40 = /^[a-f0-9]{40}$/;
const HEX_64 = /^[a-f0-9]{64}$/;
const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
const UTC_SECONDS = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
export class UpdateContractError extends Error {
constructor(message) {
super(message);
this.name = 'UpdateContractError';
}
}
function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function hasExactKeys(value, expected) {
return isPlainObject(value)
&& Object.keys(value).sort().join('\0') === [...expected].sort().join('\0');
}
export function parseSemver(value) {
if (typeof value !== 'string' || value.length > 128) {
throw new UpdateContractError(`invalid SemVer: ${JSON.stringify(value)}`);
}
const match = SEMVER.exec(value);
if (!match) throw new UpdateContractError(`invalid SemVer: ${JSON.stringify(value)}`);
const prerelease = match[4]?.split('.') ?? null;
if (prerelease?.some((identifier) => /^\d+$/.test(identifier)
&& identifier.length > 1 && identifier.startsWith('0'))) {
throw new UpdateContractError(`invalid SemVer: ${JSON.stringify(value)}`);
}
return {
core: match.slice(1, 4),
prerelease,
build: match[5]?.split('.') ?? null,
};
}
function compareNumericIdentifiers(left, right) {
if (left.length !== right.length) return left.length < right.length ? -1 : 1;
if (left === right) return 0;
return left < right ? -1 : 1;
}
function comparePrerelease(left, right) {
if (left === null && right === null) return 0;
if (left === null) return 1;
if (right === null) return -1;
const length = Math.max(left.length, right.length);
for (let index = 0; index < length; index += 1) {
if (left[index] === undefined) return -1;
if (right[index] === undefined) return 1;
if (left[index] === right[index]) continue;
const leftNumeric = /^\d+$/.test(left[index]);
const rightNumeric = /^\d+$/.test(right[index]);
if (leftNumeric && rightNumeric) return compareNumericIdentifiers(left[index], right[index]);
if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;
return left[index] < right[index] ? -1 : 1;
}
return 0;
}
export function compareSemver(leftValue, rightValue) {
const left = parseSemver(leftValue);
const right = parseSemver(rightValue);
for (let index = 0; index < left.core.length; index += 1) {
const comparison = compareNumericIdentifiers(left.core[index], right.core[index]);
if (comparison !== 0) return comparison;
}
return comparePrerelease(left.prerelease, right.prerelease);
}
export function releaseChannelForVersion(value) {
return parseSemver(value).prerelease ? 'development' : 'stable';
}
export function isStableCoreVersion(value) {
try {
const parsed = parseSemver(value);
return parsed.prerelease === null && parsed.build === null;
} catch {
return false;
}
}
export function validateCanonicalUtcTimestamp(value) {
if (typeof value !== 'string' || !UTC_SECONDS.test(value)) {
throw new UpdateContractError('publication time must use YYYY-MM-DDTHH:mm:ssZ');
}
const timestamp = Date.parse(value);
if (!Number.isFinite(timestamp)
|| new Date(timestamp).toISOString().replace('.000Z', 'Z') !== value) {
throw new UpdateContractError('publication time is not a real UTC calendar instant');
}
return value;
}
export function validateLocalRelease(value) {
if (!hasExactKeys(value, [
'schemaVersion', 'skillId', 'channel', 'version', 'source', 'updateManifestUrl',
])
|| value.schemaVersion !== 1
|| value.skillId !== SKILL_ID
|| !hasExactKeys(value.source, ['repository'])
|| value.source.repository !== EXPECTED_REPOSITORY
|| value.updateManifestUrl !== DEFAULT_MANIFEST_URL) {
throw new UpdateContractError('invalid local release identity');
}
const expectedChannel = releaseChannelForVersion(value.version);
if (value.channel !== expectedChannel) {
throw new UpdateContractError('local release channel does not match its version');
}
return {
schemaVersion: value.schemaVersion,
skillId: value.skillId,
channel: value.channel,
version: value.version,
source: { repository: value.source.repository },
updateManifestUrl: value.updateManifestUrl,
};
}
export function validateReleaseNotesUrl(value, version) {
if (!isStableCoreVersion(version)) {
throw new UpdateContractError('release notes require a stable core version');
}
const expected = `https://github.com/tt-a1i/archify/releases/tag/v${version}`;
if (value !== expected) {
throw new UpdateContractError('release notes URL is outside the exact trusted release path');
}
return value;
}
export function validateStableUpdateManifest(value) {
if (!hasExactKeys(value, [
'schemaVersion', 'skillId', 'channel', 'version', 'publishedAt', 'source',
'artifact', 'summary', 'releaseNotes', 'severity',
])
|| value.schemaVersion !== 1
|| value.skillId !== SKILL_ID
|| value.channel !== 'stable'
|| !isStableCoreVersion(value.version)
|| !hasExactKeys(value.source, ['repository', 'ref', 'treeSha'])
|| value.source.repository !== EXPECTED_REPOSITORY
|| value.source.ref !== `v${value.version}`
|| !HEX_40.test(value.source.treeSha)
|| !hasExactKeys(value.artifact, ['sha256'])
|| !HEX_64.test(value.artifact.sha256)) {
throw new UpdateContractError('invalid immutable stable release identity');
}
validateCanonicalUtcTimestamp(value.publishedAt);
if (typeof value.summary !== 'string' || value.summary.length < 1 || value.summary.length > 160
|| CONTROL_OR_BIDI.test(value.summary)) {
throw new UpdateContractError('invalid release summary');
}
validateReleaseNotesUrl(value.releaseNotes, value.version);
if (!['normal', 'security'].includes(value.severity)) {
throw new UpdateContractError('invalid update severity');
}
return {
schemaVersion: value.schemaVersion,
skillId: value.skillId,
channel: value.channel,
version: value.version,
publishedAt: value.publishedAt,
source: {
repository: value.source.repository,
ref: value.source.ref,
treeSha: value.source.treeSha,
},
artifact: { sha256: value.artifact.sha256 },
summary: value.summary,
releaseNotes: value.releaseNotes,
severity: value.severity,
};
}