asynthetic 0.1.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Asynthetic — MCP server entry point with dual transport.
4
+ *
5
+ * Transport is chosen by environment:
6
+ * - PORT set (Railway/cloud): Express HTTP server exposing the modern
7
+ * Streamable HTTP endpoint (POST/GET/DELETE /mcp) plus the legacy
8
+ * HTTP+SSE endpoints (GET /sse + POST /messages) for older clients.
9
+ * - No PORT: stdio, so local use in Claude Code / Cursor / MCP Inspector
10
+ * is completely unaffected.
11
+ *
12
+ * In stdio mode stdout is the protocol channel — all logging goes to stderr.
13
+ */
14
+ import { randomUUID } from 'node:crypto';
15
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
16
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
18
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
19
+ import express from 'express';
20
+ import { buildServer, SERVER_NAME, SERVER_VERSION } from './server.js';
21
+ import { createStore } from './store/create-store.js';
22
+ // Load .env when present (dev convenience, no dotenv dependency).
23
+ try {
24
+ process.loadEnvFile();
25
+ }
26
+ catch {
27
+ // no .env file — fine, env vars may come from the MCP client config
28
+ }
29
+ const store = createStore();
30
+ async function startStdio() {
31
+ await buildServer(store).connect(new StdioServerTransport());
32
+ console.error('[asynthetic] Ready on stdio');
33
+ }
34
+ async function startHttp(port) {
35
+ const app = express();
36
+ app.use(express.json());
37
+ // --- Modern Streamable HTTP transport (one session per client) ---
38
+ const streamableTransports = {};
39
+ app.post('/mcp', async (req, res) => {
40
+ try {
41
+ const sessionId = req.headers['mcp-session-id'];
42
+ const existing = sessionId ? streamableTransports[sessionId] : undefined;
43
+ if (existing) {
44
+ await existing.handleRequest(req, res, req.body);
45
+ return;
46
+ }
47
+ if (sessionId || !isInitializeRequest(req.body)) {
48
+ res.status(400).json({
49
+ jsonrpc: '2.0',
50
+ error: { code: -32000, message: 'Bad Request: no valid session. Send an initialize request first.' },
51
+ id: null,
52
+ });
53
+ return;
54
+ }
55
+ const transport = new StreamableHTTPServerTransport({
56
+ sessionIdGenerator: () => randomUUID(),
57
+ onsessioninitialized: (sid) => {
58
+ streamableTransports[sid] = transport;
59
+ },
60
+ });
61
+ transport.onclose = () => {
62
+ const sid = transport.sessionId;
63
+ if (sid)
64
+ delete streamableTransports[sid];
65
+ };
66
+ await buildServer(store).connect(transport);
67
+ await transport.handleRequest(req, res, req.body);
68
+ }
69
+ catch (err) {
70
+ console.error('[asynthetic] /mcp error:', err);
71
+ if (!res.headersSent) {
72
+ res.status(500).json({
73
+ jsonrpc: '2.0',
74
+ error: { code: -32603, message: 'Internal server error' },
75
+ id: null,
76
+ });
77
+ }
78
+ }
79
+ });
80
+ // GET /mcp = server->client notification stream; DELETE /mcp = session end.
81
+ const handleSessionRequest = async (req, res) => {
82
+ const sessionId = req.headers['mcp-session-id'];
83
+ const transport = sessionId ? streamableTransports[sessionId] : undefined;
84
+ if (!transport) {
85
+ res.status(400).send('Invalid or missing Mcp-Session-Id');
86
+ return;
87
+ }
88
+ await transport.handleRequest(req, res);
89
+ };
90
+ app.get('/mcp', handleSessionRequest);
91
+ app.delete('/mcp', handleSessionRequest);
92
+ // --- Legacy HTTP+SSE transport (protocol 2024-11-05) for older clients ---
93
+ const sseTransports = {};
94
+ app.get('/sse', async (_req, res) => {
95
+ try {
96
+ const transport = new SSEServerTransport('/messages', res);
97
+ sseTransports[transport.sessionId] = transport;
98
+ res.on('close', () => {
99
+ delete sseTransports[transport.sessionId];
100
+ });
101
+ await buildServer(store).connect(transport);
102
+ }
103
+ catch (err) {
104
+ console.error('[asynthetic] /sse error:', err);
105
+ if (!res.headersSent)
106
+ res.status(500).send('Internal server error');
107
+ }
108
+ });
109
+ app.post('/messages', async (req, res) => {
110
+ const sessionId = req.query.sessionId;
111
+ const transport = sessionId ? sseTransports[sessionId] : undefined;
112
+ if (!transport) {
113
+ res.status(400).send('No transport found for sessionId');
114
+ return;
115
+ }
116
+ try {
117
+ await transport.handlePostMessage(req, res, req.body);
118
+ }
119
+ catch (err) {
120
+ console.error('[asynthetic] /messages error:', err);
121
+ if (!res.headersSent)
122
+ res.status(500).send('Internal server error');
123
+ }
124
+ });
125
+ // Health/info endpoint for platform checks and humans.
126
+ app.get('/', (_req, res) => {
127
+ res.json({
128
+ name: SERVER_NAME,
129
+ version: SERVER_VERSION,
130
+ store: store.describe(),
131
+ transports: { streamable_http: '/mcp', sse_legacy: '/sse' },
132
+ });
133
+ });
134
+ const httpServer = app.listen(port, () => {
135
+ console.error(`[asynthetic] Ready on HTTP :${port} (Streamable HTTP at /mcp, legacy SSE at /sse)`);
136
+ });
137
+ // Railway sends SIGTERM on redeploy/scale-down — close cleanly.
138
+ const shutdown = () => {
139
+ console.error('[asynthetic] Shutting down');
140
+ httpServer.close(() => process.exit(0));
141
+ setTimeout(() => process.exit(0), 5000).unref();
142
+ };
143
+ process.on('SIGTERM', shutdown);
144
+ process.on('SIGINT', shutdown);
145
+ }
146
+ async function main() {
147
+ console.error(`[asynthetic] Starting (store: ${store.describe()})`);
148
+ const port = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : Number.NaN;
149
+ if (Number.isFinite(port)) {
150
+ await startHttp(port);
151
+ }
152
+ else {
153
+ await startStdio();
154
+ }
155
+ }
156
+ main().catch((err) => {
157
+ console.error('[asynthetic] Fatal:', err);
158
+ process.exit(1);
159
+ });
160
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Asynthetic — MCP server factory.
3
+ *
4
+ * builds a fully-registered McpServer instance. A factory (rather than a
5
+ * module-level singleton) because HTTP mode serves many concurrent sessions
6
+ * and a Protocol instance binds to exactly one transport; stdio mode simply
7
+ * builds one.
8
+ */
9
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
+ import { type MigrationStore } from './store/store.js';
11
+ export declare const SERVER_NAME = "asynthetic";
12
+ export declare const SERVER_VERSION = "0.1.0-beta";
13
+ export declare function buildServer(store: MigrationStore): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Asynthetic — MCP server factory.
3
+ *
4
+ * builds a fully-registered McpServer instance. A factory (rather than a
5
+ * module-level singleton) because HTTP mode serves many concurrent sessions
6
+ * and a Protocol instance binds to exactly one transport; stdio mode simply
7
+ * builds one.
8
+ */
9
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
+ import { z } from 'zod';
11
+ import { interpretDescriptor, majorOf, normalizeVersion, resolveMigration, resolvedViaOf, toSummary, } from './store/store.js';
12
+ export const SERVER_NAME = 'asynthetic';
13
+ export const SERVER_VERSION = '0.1.0-beta';
14
+ const NOT_FOUND_GUIDANCE = 'No verified migration map exists for this request. Do NOT fabricate migration steps from model memory — ' +
15
+ 'consult the official changelog / release notes for this package instead.';
16
+ function jsonResult(payload, isError = false) {
17
+ return {
18
+ content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],
19
+ ...(isError ? { isError: true } : {}),
20
+ };
21
+ }
22
+ function storeError(err) {
23
+ console.error('[asynthetic] Store error:', err);
24
+ return jsonResult({ error: `Store lookup failed: ${err instanceof Error ? err.message : String(err)}` }, true);
25
+ }
26
+ const packageArg = z.string().min(1).describe('Package name as published, e.g. "@modelcontextprotocol/sdk"');
27
+ const ecosystemArg = z.string().default('npm').describe('Package ecosystem (default "npm")');
28
+ export function buildServer(store) {
29
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
30
+ server.registerTool('get_migration', {
31
+ title: 'Get migration map',
32
+ description: 'Returns the verified migration map for upgrading a package between two versions: ordered breaking ' +
33
+ 'changes with before/after code, deprecations, source-citation URLs, and a last_verified date. ' +
34
+ 'Accepts concrete versions or SemVer ranges (e.g. "^14.2.0", "~14.1.0", "15.x"); non-exact lookups ' +
35
+ 'resolve to the covering map and disclose how via match_type/resolved_via/match_note. ' +
36
+ 'If found=false, no verified data exists — do not guess.',
37
+ inputSchema: {
38
+ package: packageArg,
39
+ from_version: z
40
+ .string()
41
+ .min(1)
42
+ .describe('Version or SemVer range currently in use, e.g. "1.29.0", "^14.2.0", "1.x"'),
43
+ to_version: z
44
+ .string()
45
+ .min(1)
46
+ .describe('Version or SemVer range upgrading to, e.g. "2.0.0", "^15.0.0", "2.x"'),
47
+ ecosystem: ecosystemArg,
48
+ },
49
+ annotations: { readOnlyHint: true, openWorldHint: false },
50
+ }, async ({ package: pkg, from_version, to_version, ecosystem }) => {
51
+ const requested = { package: pkg, ecosystem, from_version, to_version };
52
+ try {
53
+ const maps = await store.getMapsForPackage(ecosystem, pkg);
54
+ const resolved = resolveMigration(maps, from_version, to_version);
55
+ if (!resolved) {
56
+ const available = maps.length > 0 ? maps.map(toSummary) : await store.listMaps();
57
+ return jsonResult({
58
+ found: false,
59
+ requested,
60
+ message: NOT_FOUND_GUIDANCE,
61
+ available_maps: available,
62
+ });
63
+ }
64
+ const { map, match_type } = resolved;
65
+ const match_note = match_type === 'exact'
66
+ ? undefined
67
+ : match_type === 'semver-range'
68
+ ? `Resolved via SemVer range processing: the requested window ${from_version} -> ${to_version} ` +
69
+ `falls inside the map verified for ${map.from_version} -> ${map.to_version}. ` +
70
+ 'Details specific to intermediate minor/patch versions may not be covered.'
71
+ : `No map exists for exactly ${from_version} -> ${to_version}; returning the map verified for ` +
72
+ `${map.from_version} -> ${map.to_version} (same major-version jump). ` +
73
+ 'Details specific to intermediate minor/patch versions may not be covered.';
74
+ return jsonResult({
75
+ found: true,
76
+ requested,
77
+ match_type,
78
+ resolved_via: resolvedViaOf(match_type),
79
+ ...(match_note ? { match_note } : {}),
80
+ migration: map,
81
+ });
82
+ }
83
+ catch (err) {
84
+ return storeError(err);
85
+ }
86
+ });
87
+ server.registerTool('get_breaking_changes', {
88
+ title: 'Get breaking changes in a version',
89
+ description: 'Returns the breaking changes introduced when upgrading TO the given version of a package (matched by ' +
90
+ 'major version against curated migration maps), with source citations and last_verified dates. ' +
91
+ 'Accepts concrete versions or SemVer ranges ("15", "^15.0.0", "15.x"); resolution is disclosed via ' +
92
+ 'match_type/resolved_via. If found=false, no verified data exists — do not guess.',
93
+ inputSchema: {
94
+ package: packageArg,
95
+ version: z
96
+ .string()
97
+ .min(1)
98
+ .describe('The version (or SemVer range) whose breaking changes you want, e.g. "2.0.0", "^15.0.0"'),
99
+ ecosystem: ecosystemArg,
100
+ },
101
+ annotations: { readOnlyHint: true, openWorldHint: false },
102
+ }, async ({ package: pkg, version, ecosystem }) => {
103
+ const requested = { package: pkg, ecosystem, version };
104
+ try {
105
+ const target = interpretDescriptor(version);
106
+ const maps = await store.getMapsForPackage(ecosystem, pkg);
107
+ const matching = target === null ? [] : maps.filter((m) => majorOf(m.to_version) === target.major);
108
+ if (matching.length === 0) {
109
+ const available = maps.length > 0 ? maps.map(toSummary) : await store.listMaps();
110
+ return jsonResult({
111
+ found: false,
112
+ requested,
113
+ message: NOT_FOUND_GUIDANCE,
114
+ available_maps: available,
115
+ });
116
+ }
117
+ return jsonResult({
118
+ found: true,
119
+ requested,
120
+ results: matching.map((map) => {
121
+ const match_type = normalizeVersion(map.to_version) === normalizeVersion(version)
122
+ ? 'exact'
123
+ : target.kind === 'range'
124
+ ? 'semver-range'
125
+ : 'major-version';
126
+ return {
127
+ from_version: map.from_version,
128
+ to_version: map.to_version,
129
+ match_type,
130
+ resolved_via: resolvedViaOf(match_type),
131
+ status: map.status,
132
+ last_verified: map.last_verified,
133
+ source_urls: map.source_urls,
134
+ summary: map.summary,
135
+ breaking_changes: map.breaking_changes,
136
+ deprecations: map.deprecations,
137
+ };
138
+ }),
139
+ });
140
+ }
141
+ catch (err) {
142
+ return storeError(err);
143
+ }
144
+ });
145
+ // Stub per brief §7/§11: declared so agents can discover it, but explicitly
146
+ // returns "not implemented" — it must never look like real compatibility data.
147
+ server.registerTool('check_compatibility', {
148
+ title: 'Check cross-package compatibility (not yet implemented)',
149
+ description: 'PLANNED: known compatibility issues between two package versions. Currently returns implemented=false ' +
150
+ 'and no data. Do not infer compatibility (or incompatibility) from this response.',
151
+ inputSchema: {
152
+ package_a: z.string().min(1),
153
+ version_a: z.string().min(1),
154
+ package_b: z.string().min(1),
155
+ version_b: z.string().min(1),
156
+ ecosystem: ecosystemArg,
157
+ },
158
+ annotations: { readOnlyHint: true, openWorldHint: false },
159
+ }, async () => jsonResult({
160
+ implemented: false,
161
+ message: 'Cross-package compatibility lookup is planned but not yet available. This response contains no ' +
162
+ 'compatibility data — do not treat it as evidence that the packages are (or are not) compatible.',
163
+ }));
164
+ return server;
165
+ }
166
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { MigrationStore } from './store.js';
2
+ export declare function createStore(): MigrationStore;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Store selection: Supabase when env vars are present, local JSON files
3
+ * otherwise. Never throws on missing configuration — the local fallback
4
+ * guarantees the server starts in development.
5
+ */
6
+ import { LocalFileStore } from './local-store.js';
7
+ import { SupabaseStore } from './supabase-store.js';
8
+ export function createStore() {
9
+ const url = process.env.SUPABASE_URL;
10
+ const key = process.env.SUPABASE_ANON_KEY ?? process.env.SUPABASE_KEY;
11
+ if (url && key)
12
+ return new SupabaseStore(url, key);
13
+ if (url || key) {
14
+ console.error('[asynthetic] Incomplete Supabase config (need both SUPABASE_URL and SUPABASE_ANON_KEY); falling back to local files.');
15
+ }
16
+ return new LocalFileStore();
17
+ }
18
+ //# sourceMappingURL=create-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-store.js","sourceRoot":"","sources":["../../src/store/create-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,UAAU,WAAW;IACzB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACtE,IAAI,GAAG,IAAI,GAAG;QAAE,OAAO,IAAI,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEnD,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,sHAAsH,CACvH,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,cAAc,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type MigrationMap } from '../types/migration-map.js';
2
+ import { type MapSummary, type MigrationStore } from './store.js';
3
+ export declare function loadLocalMaps(dataDir: string, opts?: {
4
+ includeStale?: boolean;
5
+ }): MigrationMap[];
6
+ export declare class LocalFileStore implements MigrationStore {
7
+ private readonly dataDir;
8
+ private cache;
9
+ constructor(dataDir?: string);
10
+ private load;
11
+ getMapsForPackage(ecosystem: string, pkg: string): Promise<MigrationMap[]>;
12
+ listMaps(): Promise<MapSummary[]>;
13
+ describe(): string;
14
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Local-file fallback store: reads migration maps from data/maps/**\/*.json.
3
+ *
4
+ * Used when Supabase env vars are absent so the server always starts in
5
+ * development. Every file is validated with MigrationMapSchema on load;
6
+ * invalid files are skipped with a stderr warning, never served.
7
+ */
8
+ import { readdirSync, readFileSync } from 'node:fs';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { MigrationMapSchema } from '../types/migration-map.js';
12
+ import { toSummary } from './store.js';
13
+ // Resolves to <project>/data/maps from both src/store (tsx) and dist/store (build).
14
+ const DEFAULT_DATA_DIR = fileURLToPath(new URL('../../data/maps', import.meta.url));
15
+ export function loadLocalMaps(dataDir, opts = {}) {
16
+ let entries;
17
+ try {
18
+ entries = readdirSync(dataDir, { recursive: true, withFileTypes: true });
19
+ }
20
+ catch (err) {
21
+ console.error(`[asynthetic] Cannot read data directory ${dataDir}:`, err);
22
+ return [];
23
+ }
24
+ const maps = [];
25
+ for (const entry of entries) {
26
+ if (!entry.isFile() || !entry.name.endsWith('.json'))
27
+ continue;
28
+ const filePath = path.join(entry.parentPath, entry.name);
29
+ try {
30
+ const parsed = MigrationMapSchema.safeParse(JSON.parse(readFileSync(filePath, 'utf8')));
31
+ if (!parsed.success) {
32
+ console.error(`[asynthetic] Skipping invalid map ${filePath}: ${parsed.error.issues
33
+ .slice(0, 3)
34
+ .map((i) => `${i.path.join('.')}: ${i.message}`)
35
+ .join('; ')}`);
36
+ continue;
37
+ }
38
+ if (!opts.includeStale && parsed.data.status === 'stale')
39
+ continue;
40
+ maps.push(parsed.data);
41
+ }
42
+ catch (err) {
43
+ console.error(`[asynthetic] Skipping unreadable map ${filePath}:`, err);
44
+ }
45
+ }
46
+ return maps;
47
+ }
48
+ export class LocalFileStore {
49
+ dataDir;
50
+ cache = null;
51
+ constructor(dataDir = process.env.MIGRATION_DATA_DIR ?? DEFAULT_DATA_DIR) {
52
+ this.dataDir = dataDir;
53
+ }
54
+ load() {
55
+ this.cache ??= loadLocalMaps(this.dataDir);
56
+ return this.cache;
57
+ }
58
+ async getMapsForPackage(ecosystem, pkg) {
59
+ const wanted = pkg.trim().toLowerCase();
60
+ return this.load().filter((m) => m.ecosystem === ecosystem && m.package.toLowerCase() === wanted);
61
+ }
62
+ async listMaps() {
63
+ return this.load().map(toSummary);
64
+ }
65
+ describe() {
66
+ return `local JSON files (${this.dataDir})`;
67
+ }
68
+ }
69
+ //# sourceMappingURL=local-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-store.js","sourceRoot":"","sources":["../../src/store/local-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { MigrationMap, MigrationStatus } from '../types/migration-map.js';
2
+ export interface MapSummary {
3
+ ecosystem: string;
4
+ package: string;
5
+ from_version: string;
6
+ to_version: string;
7
+ status: MigrationStatus;
8
+ last_verified: string;
9
+ }
10
+ export interface MigrationStore {
11
+ /** All non-stale maps for a package (case-insensitive package match). */
12
+ getMapsForPackage(ecosystem: string, pkg: string): Promise<MigrationMap[]>;
13
+ /** Summaries of every non-stale map in the store. */
14
+ listMaps(): Promise<MapSummary[]>;
15
+ /** Human-readable description of the backing store, for stderr logging. */
16
+ describe(): string;
17
+ }
18
+ export declare function toSummary(map: MigrationMap): MapSummary;
19
+ export declare function normalizeVersion(v: string): string;
20
+ export type MatchType = 'exact' | 'semver-range' | 'major-version';
21
+ /** Wire-format disclosure of how a lookup was resolved. */
22
+ export type ResolvedVia = 'exact_string' | 'semver_range' | 'major_version';
23
+ export declare function resolvedViaOf(match: MatchType): ResolvedVia;
24
+ export interface DescriptorInfo {
25
+ major: number;
26
+ kind: 'version' | 'range';
27
+ }
28
+ /**
29
+ * Interpret a user-supplied version descriptor.
30
+ *
31
+ * - Concrete/partial versions ("14.2.35", "2.0.0-beta.2", "14", "14.2") ->
32
+ * kind 'version'.
33
+ * - SemVer ranges ("^14.2.0", "~14.1.0", "14.x", ">=14.2 <15") -> kind
34
+ * 'range', anchored at the range's minimum satisfying version. A range
35
+ * spanning multiple majors resolves to its lowest major.
36
+ */
37
+ export declare function interpretDescriptor(desc: string): DescriptorInfo | null;
38
+ /** Major version of a descriptor ("2.0.0-beta.2" -> 2, "^14.2.0" -> 14). */
39
+ export declare function majorOf(v: string): number | null;
40
+ /**
41
+ * Exact from/to string match first; otherwise interpret both descriptors via
42
+ * SemVer and match maps whose from/to majors cover the requested window
43
+ * (e.g. "^14.2.0" -> "^15.0.0" finds the map verified for 14.2.35 -> 15.0.0).
44
+ * Callers must surface non-exact matches to the agent — the map's verified
45
+ * endpoints may differ from what was asked.
46
+ */
47
+ export declare function resolveMigration(maps: MigrationMap[], fromDesc: string, toDesc: string): {
48
+ map: MigrationMap;
49
+ match_type: MatchType;
50
+ } | null;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Store abstraction + version-matching logic shared by every backend.
3
+ *
4
+ * Stores are deliberately dumb: they return all maps for a package and let
5
+ * `resolveMigration` do the matching, so Supabase and the local-file fallback
6
+ * can never disagree on semantics.
7
+ *
8
+ * Matching is SemVer-aware: descriptors may be concrete versions ("14.2.35"),
9
+ * partial versions ("14", "14.2"), or ranges ("^14.2.0", "~14.1.0", "14.x").
10
+ */
11
+ import semver from 'semver';
12
+ export function toSummary(map) {
13
+ return {
14
+ ecosystem: map.ecosystem,
15
+ package: map.package,
16
+ from_version: map.from_version,
17
+ to_version: map.to_version,
18
+ status: map.status,
19
+ last_verified: map.last_verified,
20
+ };
21
+ }
22
+ export function normalizeVersion(v) {
23
+ return v.trim().replace(/^v/i, '');
24
+ }
25
+ export function resolvedViaOf(match) {
26
+ switch (match) {
27
+ case 'exact':
28
+ return 'exact_string';
29
+ case 'semver-range':
30
+ return 'semver_range';
31
+ case 'major-version':
32
+ return 'major_version';
33
+ }
34
+ }
35
+ /**
36
+ * Interpret a user-supplied version descriptor.
37
+ *
38
+ * - Concrete/partial versions ("14.2.35", "2.0.0-beta.2", "14", "14.2") ->
39
+ * kind 'version'.
40
+ * - SemVer ranges ("^14.2.0", "~14.1.0", "14.x", ">=14.2 <15") -> kind
41
+ * 'range', anchored at the range's minimum satisfying version. A range
42
+ * spanning multiple majors resolves to its lowest major.
43
+ */
44
+ export function interpretDescriptor(desc) {
45
+ const norm = normalizeVersion(desc);
46
+ const exact = semver.valid(norm, { loose: true });
47
+ if (exact)
48
+ return { major: semver.major(exact), kind: 'version' };
49
+ // Partial versions count as versions, not ranges, even though semver
50
+ // technically parses "14" as an X-range.
51
+ if (/^\d+(\.\d+)?$/.test(norm)) {
52
+ return { major: Number.parseInt(norm, 10), kind: 'version' };
53
+ }
54
+ if (semver.validRange(norm, { loose: true })) {
55
+ const min = semver.minVersion(norm, { loose: true });
56
+ if (min)
57
+ return { major: min.major, kind: 'range' };
58
+ }
59
+ // Last resort for non-semver strings like "1.x-legacy": leading digits.
60
+ const lead = norm.match(/^(\d+)/);
61
+ return lead ? { major: Number(lead[1]), kind: 'version' } : null;
62
+ }
63
+ /** Major version of a descriptor ("2.0.0-beta.2" -> 2, "^14.2.0" -> 14). */
64
+ export function majorOf(v) {
65
+ return interpretDescriptor(v)?.major ?? null;
66
+ }
67
+ /**
68
+ * Exact from/to string match first; otherwise interpret both descriptors via
69
+ * SemVer and match maps whose from/to majors cover the requested window
70
+ * (e.g. "^14.2.0" -> "^15.0.0" finds the map verified for 14.2.35 -> 15.0.0).
71
+ * Callers must surface non-exact matches to the agent — the map's verified
72
+ * endpoints may differ from what was asked.
73
+ */
74
+ export function resolveMigration(maps, fromDesc, toDesc) {
75
+ const from = normalizeVersion(fromDesc);
76
+ const to = normalizeVersion(toDesc);
77
+ const exact = maps.find((m) => normalizeVersion(m.from_version) === from && normalizeVersion(m.to_version) === to);
78
+ if (exact)
79
+ return { map: exact, match_type: 'exact' };
80
+ const fromInfo = interpretDescriptor(fromDesc);
81
+ const toInfo = interpretDescriptor(toDesc);
82
+ if (!fromInfo || !toInfo)
83
+ return null;
84
+ const candidate = maps.find((m) => majorOf(m.from_version) === fromInfo.major && majorOf(m.to_version) === toInfo.major);
85
+ if (!candidate)
86
+ return null;
87
+ const match_type = fromInfo.kind === 'range' || toInfo.kind === 'range' ? 'semver-range' : 'major-version';
88
+ return { map: candidate, match_type };
89
+ }
90
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/store/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { MigrationMap } from '../types/migration-map.js';
2
+ import { toSummary, type MapSummary, type MigrationStore } from './store.js';
3
+ export declare class SupabaseStore implements MigrationStore {
4
+ private readonly url;
5
+ private readonly client;
6
+ constructor(url: string, key: string);
7
+ getMapsForPackage(ecosystem: string, pkg: string): Promise<MigrationMap[]>;
8
+ listMaps(): Promise<MapSummary[]>;
9
+ describe(): string;
10
+ }
11
+ export { toSummary };