History

refactor: split catalog snapshots into fragment-based architecture

Source adapters now produce CatalogFragmentV1 instead of full CatalogSnapshotV1 objects. Fragments are assembled into snapshots at the sync boundary via createCatalogSnapshotV1FromFragments, keeping import metadata separate from catalog data. This prepares the IR pipeline for multi-fragment catalog composition.

Rhys Sullivan committed 8 months ago

Commit 8019c0b1 parent 7e913c013 files · +264 −115Browse files

Unified · Split

13 files
  1. packages/control-plane/src/ir/catalog.ts +9 −0
  2. packages/control-plane/src/runtime/local-source-artifacts.test.ts +16 −6
  3. packages/control-plane/src/runtime/local-source-artifacts.ts +13 −9
  4. packages/control-plane/src/runtime/source-adapters/google-discovery.test.ts +7 −4
  5. packages/control-plane/src/runtime/source-adapters/google-discovery.ts +10 −4
  6. packages/control-plane/src/runtime/source-adapters/graphql.ts +12 −6
  7. packages/control-plane/src/runtime/source-adapters/internal.ts +14 −21
  8. packages/control-plane/src/runtime/source-adapters/mcp.test.ts +11 −8
  9. packages/control-plane/src/runtime/source-adapters/mcp.ts +10 −4
  10. packages/control-plane/src/runtime/source-adapters/openapi.ts +10 −4
  11. packages/control-plane/src/runtime/source-catalog-snapshot.ts +127 −45
  12. packages/control-plane/src/runtime/source-catalog-support.ts +20 −2
  13. packages/control-plane/src/runtime/source-catalog-sync.ts +5 −2
packages/control-plane/src/ir/catalog.ts+9−0
@@ -1650,6 +1650,15 @@ export const createCatalogSnapshotV1 = (input: { · 1649 unmodified lines
1650 };1650 };
1651};1651};
1652 1652
1653export const createCatalogSnapshotV1FromFragments = (input: {
1654 import: ImportMetadata;
1655 fragments: readonly CatalogFragmentV1[];
1656}) =>
1657 createCatalogSnapshotV1({
1658 import: input.import,
1659 catalog: mergeCatalogFragments(input.fragments),
1660 });
1661
1653export const mergeCatalogFragments = (fragments: readonly CatalogFragmentV1[]): CatalogV1 => {1662export const mergeCatalogFragments = (fragments: readonly CatalogFragmentV1[]): CatalogV1 => {
1654 const catalog = emptyCatalog();1663 const catalog = emptyCatalog();
1655 const seenByCollection = new Map<string, string>();1664 const seenByCollection = new Map<string, string>();
packages/control-plane/src/runtime/local-source-artifacts.test.ts+16−6
@@ -20,8 +20,9 @@ import { · 19 unmodified lines
20} from "./local-source-artifacts";20} from "./local-source-artifacts";
21import type { ResolvedLocalWorkspaceContext } from "./local-config";21import type { ResolvedLocalWorkspaceContext } from "./local-config";
22import {22import {
23 createGraphqlCatalogSnapshot,23 createCatalogImportMetadata,
24 createOpenApiCatalogSnapshot,24 createGraphqlCatalogFragment,
25 createOpenApiCatalogFragment,
25} from "./source-catalog-snapshot";26} from "./source-catalog-snapshot";
26 27
27const makeContext = (): ResolvedLocalWorkspaceContext => {28const makeContext = (): ResolvedLocalWorkspaceContext => {
@@ -33,6 +34,7 @@ const makeContext = (): ResolvedLocalWorkspaceContext => { · 5 unmodified lines
33 configDirectory: join(workspaceRoot, ".executor"),34 configDirectory: join(workspaceRoot, ".executor"),
34 projectConfigPath: join(workspaceRoot, ".executor", "executor.jsonc"),35 projectConfigPath: join(workspaceRoot, ".executor", "executor.jsonc"),
35 homeConfigPath: join(workspaceRoot, ".executor-home.jsonc"),36 homeConfigPath: join(workspaceRoot, ".executor-home.jsonc"),
37 homeStateDirectory: join(workspaceRoot, ".executor-home-state"),
36 artifactsDirectory: join(workspaceRoot, ".executor", "artifacts"),38 artifactsDirectory: join(workspaceRoot, ".executor", "artifacts"),
37 stateDirectory: join(workspaceRoot, ".executor", "state"),39 stateDirectory: join(workspaceRoot, ".executor", "state"),
38 };40 };
@@ -63,7 +65,7 @@ const makeSource = (): Source => ({ · 24 unmodified lines
63 65
64const makeArtifact = () => {66const makeArtifact = () => {
65 const source = makeSource();67 const source = makeSource();
66 const snapshot = createOpenApiCatalogSnapshot({68 const fragment = createOpenApiCatalogFragment({
67 source,69 source,
68 documents: [{70 documents: [{
69 documentKind: "openapi",71 documentKind: "openapi",
@@ -77,7 +79,11 @@ const makeArtifact = () => { · 7 unmodified lines
77 return buildLocalSourceArtifact({79 return buildLocalSourceArtifact({
78 source,80 source,
79 syncResult: {81 syncResult: {
80 snapshot,82 fragment,
83 importMetadata: createCatalogImportMetadata({
84 source,
85 adapterKey: "openapi",
86 }),
81 sourceHash: source.sourceHash,87 sourceHash: source.sourceHash,
82 },88 },
83 });89 });
@@ -92,7 +98,7 @@ const makeGraphqlArtifact = () => { · 8 unmodified lines
92 defaultHeaders: null,98 defaultHeaders: null,
93 },99 },
94 };100 };
95 const snapshot = createGraphqlCatalogSnapshot({101 const fragment = createGraphqlCatalogFragment({
96 source,102 source,
97 documents: [{103 documents: [{
98 documentKind: "graphql_introspection",104 documentKind: "graphql_introspection",
@@ -128,7 +134,11 @@ const makeGraphqlArtifact = () => { · 29 unmodified lines
128 return buildLocalSourceArtifact({134 return buildLocalSourceArtifact({
129 source,135 source,
130 syncResult: {136 syncResult: {
131 snapshot,137 fragment,
138 importMetadata: createCatalogImportMetadata({
139 source,
140 adapterKey: "graphql",
141 }),
132 sourceHash: source.sourceHash,142 sourceHash: source.sourceHash,
133 },143 },
134 });144 });
packages/control-plane/src/runtime/local-source-artifacts.ts+13−9
@@ -16,10 +16,15 @@ import * as Schema from "effect/Schema"; · 15 unmodified lines
16import { decodeCatalogSnapshotV1 } from "../ir/catalog";16import { decodeCatalogSnapshotV1 } from "../ir/catalog";
17import {17import {
18 CatalogSnapshotV1Schema,18 CatalogSnapshotV1Schema,
19 type CatalogSnapshotV1,
19 type NativeBlob,20 type NativeBlob,
20 type SourceDocument,21 type SourceDocument,
21} from "../ir/model";22} from "../ir/model";
22import type { SourceCatalogSyncResult } from "./source-catalog-support";23import {
24 contentHash,
25 snapshotFromSourceCatalogSyncResult,
26 type SourceCatalogSyncResult,
27} from "./source-catalog-support";
23import type { ResolvedLocalWorkspaceContext } from "./local-config";28import type { ResolvedLocalWorkspaceContext } from "./local-config";
24import {29import {
25 LocalFileSystemError,30 LocalFileSystemError,
@@ -30,8 +35,6 @@ import { · 4 unmodified lines
30 createSourceCatalogRevisionRecord,35 createSourceCatalogRevisionRecord,
31 stableSourceCatalogId,36 stableSourceCatalogId,
32} from "./source-definitions";37} from "./source-definitions";
33import { contentHash } from "./source-catalog-support";
34
35const LOCAL_SOURCE_ARTIFACT_VERSION = 3 as const;38const LOCAL_SOURCE_ARTIFACT_VERSION = 3 as const;
36 39
37export const LocalSourceArtifactSchema = Schema.Struct({40export const LocalSourceArtifactSchema = Schema.Struct({
@@ -155,10 +158,10 @@ const hydrateArtifactSourceDocuments = (input: { · 117 unmodified lines
155 return nextArtifact;158 return nextArtifact;
156};159};
157 160
158const snapshotHash = (snapshot: SourceCatalogSyncResult["snapshot"]): string =>161const snapshotHash = (snapshot: CatalogSnapshotV1): string =>
159 contentHash(JSON.stringify(snapshot));162 contentHash(JSON.stringify(snapshot));
160 163
161const importMetadataHash = (snapshot: SourceCatalogSyncResult["snapshot"]): string =>164const importMetadataHash = (snapshot: { import: SourceCatalogSyncResult["importMetadata"] }): string =>
162 contentHash(JSON.stringify(snapshot.import));165 contentHash(JSON.stringify(snapshot.import));
163 166
164export const buildLocalSourceArtifact = (input: {167export const buildLocalSourceArtifact = (input: {
@@ -167,13 +170,14 @@ export const buildLocalSourceArtifact = (input: { · 2 unmodified lines
167}): LocalSourceArtifact => {170}): LocalSourceArtifact => {
168 const catalogId: SourceCatalogId = stableSourceCatalogId(input.source);171 const catalogId: SourceCatalogId = stableSourceCatalogId(input.source);
169 const generatedAt = Date.now();172 const generatedAt = Date.now();
170 const importHash = importMetadataHash(input.syncResult.snapshot);173 const snapshot = snapshotFromSourceCatalogSyncResult(input.syncResult);
171 const hash = snapshotHash(input.syncResult.snapshot);174 const importHash = importMetadataHash(snapshot);
175 const hash = snapshotHash(snapshot);
172 const revision = createSourceCatalogRevisionRecord({176 const revision = createSourceCatalogRevisionRecord({
173 source: input.source,177 source: input.source,
174 catalogId,178 catalogId,
175 revisionNumber: 1,179 revisionNumber: 1,
176 importMetadataJson: JSON.stringify(input.syncResult.snapshot.import),180 importMetadataJson: JSON.stringify(snapshot.import),
177 importMetadataHash: importHash,181 importMetadataHash: importHash,
178 snapshotHash: hash,182 snapshotHash: hash,
179 });183 });
@@ -184,7 +188,7 @@ export const buildLocalSourceArtifact = (input: { · 4 unmodified lines
184 catalogId,188 catalogId,
185 generatedAt,189 generatedAt,
186 revision,190 revision,
187 snapshot: input.syncResult.snapshot,191 snapshot,
188 };192 };
189};193};
190 194
packages/control-plane/src/runtime/source-adapters/google-discovery.test.ts+7−4
@@ -6,6 +6,7 @@ import { SourceIdSchema } from "#schema"; · 5 unmodified lines
6import * as Effect from "effect/Effect";6import * as Effect from "effect/Effect";
7 7
8import { googleDiscoverySourceAdapter } from "./google-discovery";8import { googleDiscoverySourceAdapter } from "./google-discovery";
9import { snapshotFromSourceCatalogSyncResult } from "../source-catalog-support";
9import { createSourceFromPayload } from "../source-definitions";10import { createSourceFromPayload } from "../source-definitions";
10 11
11const fetchLiveDiscoveryDocument = async (): Promise<string> => {12const fetchLiveDiscoveryDocument = async (): Promise<string> => {
@@ -102,12 +103,14 @@ describe("google discovery source adapter", () => { · 90 unmodified lines
102 }),103 }),
103 }),104 }),
104 );105 );
106 const snapshot = snapshotFromSourceCatalogSyncResult(syncResult);
105 107
106 expect(Object.values(syncResult.snapshot.catalog.documents)[0]?.kind).toBe("google-discovery");108 expect(syncResult.fragment.version).toBe("ir.v1.fragment");
107 expect(Object.keys(syncResult.snapshot.catalog.resources)).not.toHaveLength(0);109 expect(Object.values(snapshot.catalog.documents)[0]?.kind).toBe("google-discovery");
108 expect(Object.keys(syncResult.snapshot.catalog.capabilities).length).toBeGreaterThan(50);110 expect(Object.keys(snapshot.catalog.resources)).not.toHaveLength(0);
111 expect(Object.keys(snapshot.catalog.capabilities).length).toBeGreaterThan(50);
109 expect(112 expect(
110 Object.values(syncResult.snapshot.catalog.capabilities).some((capability) =>113 Object.values(snapshot.catalog.capabilities).some((capability) =>
111 capability.surface.toolPath.join(".") === "google.sheets.spreadsheets.values.get"114 capability.surface.toolPath.join(".") === "google.sheets.spreadsheets.values.get"
112 ),115 ),
113 ).toBe(true);116 ).toBe(true);
packages/control-plane/src/runtime/source-adapters/google-discovery.ts+10−4
@@ -15,9 +15,11 @@ import * as Effect from "effect/Effect"; · 14 unmodified lines
15import * as Schema from "effect/Schema";15import * as Schema from "effect/Schema";
16 16
17import {17import {
18 createGoogleDiscoveryCatalogSnapshot,18 createCatalogImportMetadata,
19 createGoogleDiscoveryCatalogFragment,
19 type GoogleDiscoveryCatalogOperationInput,20 type GoogleDiscoveryCatalogOperationInput,
20} from "../source-catalog-snapshot";21} from "../source-catalog-snapshot";
22import { createSourceCatalogSyncResult } from "../source-catalog-support";
21import type { SourceAdapter } from "./types";23import type { SourceAdapter } from "./types";
22import {24import {
23 ConnectHttpAuthSchema,25 ConnectHttpAuthSchema,
@@ -336,8 +338,8 @@ export const googleDiscoverySourceAdapter: SourceAdapter = { · 312 unmodified lines
336 const definitions = compileGoogleDiscoveryToolDefinitions(manifest);338 const definitions = compileGoogleDiscoveryToolDefinitions(manifest);
337 const now = Date.now();339 const now = Date.now();
338 340
339 return {341 return createSourceCatalogSyncResult({
340 snapshot: createGoogleDiscoveryCatalogSnapshot({342 fragment: createGoogleDiscoveryCatalogFragment({
341 source,343 source,
342 documents: [{344 documents: [{
343 documentKind: "google_discovery",345 documentKind: "google_discovery",
@@ -352,8 +354,12 @@ export const googleDiscoverySourceAdapter: SourceAdapter = { · 8 unmodified lines
352 })354 })
353 ),355 ),
354 }),356 }),
357 importMetadata: createCatalogImportMetadata({
358 source,
359 adapterKey: "google_discovery",
360 }),
355 sourceHash: manifest.sourceHash,361 sourceHash: manifest.sourceHash,
356 };362 });
357 }),363 }),
358 getOauth2SetupConfig: ({ source }) => googleDiscoveryOauth2SetupConfig(source),364 getOauth2SetupConfig: ({ source }) => googleDiscoveryOauth2SetupConfig(source),
359 normalizeOauthClientInput: (input) =>365 normalizeOauthClientInput: (input) =>
packages/control-plane/src/runtime/source-adapters/graphql.ts+12−6
@@ -16,9 +16,11 @@ import * as Effect from "effect/Effect"; · 15 unmodified lines
16import * as Schema from "effect/Schema";16import * as Schema from "effect/Schema";
17 17
18import {18import {
19 createGraphqlCatalogSnapshot,19 createCatalogImportMetadata,
20 createGraphqlCatalogFragment,
20 type GraphqlCatalogOperationInput,21 type GraphqlCatalogOperationInput,
21} from "../source-catalog-snapshot";22} from "../source-catalog-snapshot";
23import { createSourceCatalogSyncResult } from "../source-catalog-support";
22import type { SourceAdapter } from "./types";24import type { SourceAdapter } from "./types";
23import {25import {
24 ConnectHttpAuthSchema,26 ConnectHttpAuthSchema,
@@ -327,8 +329,8 @@ export const graphqlSourceAdapter: SourceAdapter = { · 302 unmodified lines
327 }),329 }),
328 );330 );
329 const now = Date.now();331 const now = Date.now();
330 const snapshot = yield* Effect.sync(() =>332 const fragment = yield* Effect.sync(() =>
331 createGraphqlCatalogSnapshot({333 createGraphqlCatalogFragment({
332 source,334 source,
333 documents: [{335 documents: [{
334 documentKind: "graphql_introspection",336 documentKind: "graphql_introspection",
@@ -347,10 +349,14 @@ export const graphqlSourceAdapter: SourceAdapter = { · 12 unmodified lines
347 }),349 }),
348 );350 );
349 351
350 return {352 return createSourceCatalogSyncResult({
351 snapshot,353 fragment,
354 importMetadata: createCatalogImportMetadata({
355 source,
356 adapterKey: "graphql",
357 }),
352 sourceHash: manifest.sourceHash,358 sourceHash: manifest.sourceHash,
353 };359 });
354 }).pipe(360 }).pipe(
355 Effect.withSpan("graphql.syncCatalog", {361 Effect.withSpan("graphql.syncCatalog", {
356 attributes: {362 attributes: {
packages/control-plane/src/runtime/source-adapters/internal.ts+14−21
@@ -2,6 +2,8 @@ import * as Effect from "effect/Effect"; · 1 unmodified line
2import * as Schema from "effect/Schema";2import * as Schema from "effect/Schema";
3import type { Source } from "#schema";3import type { Source } from "#schema";
4 4
5import { createCatalogImportMetadata } from "../source-catalog-snapshot";
6import { createSourceCatalogSyncResult } from "../source-catalog-support";
5import type { SourceAdapter } from "./types";7import type { SourceAdapter } from "./types";
6import {8import {
7 decodeBindingConfig,9 decodeBindingConfig,
@@ -99,28 +101,19 @@ export const internalSourceAdapter: SourceAdapter = { · 91 unmodified lines
99 };101 };
100 }),102 }),
101 shouldAutoProbe: () => false,103 shouldAutoProbe: () => false,
102 syncCatalog: () => Effect.succeed({104 syncCatalog: ({ source }) =>
103 snapshot: {105 Effect.succeed(createSourceCatalogSyncResult({
104 version: "ir.v1.snapshot",106 fragment: {
105 import: {107 version: "ir.v1.fragment",
106 sourceKind: "custom",108 },
107 adapterKey: "internal",109 importMetadata: {
110 ...createCatalogImportMetadata({
111 source,
112 adapterKey: "internal",
113 }),
108 importerVersion: "ir.v1.internal",114 importerVersion: "ir.v1.internal",
109 importedAt: new Date().toISOString(),
110 sourceConfigHash: "internal",115 sourceConfigHash: "internal",
111 },116 },
112 catalog: {117 sourceHash: null,
113 version: "ir.v1",118 })),
114 documents: {},
115 resources: {},
116 scopes: {},
117 symbols: {},
118 capabilities: {},
119 executables: {},
120 responseSets: {},
121 diagnostics: {},
122 },
123 },
124 sourceHash: null,
125 }),
126};119};
packages/control-plane/src/runtime/source-adapters/mcp.test.ts+11−8
@@ -26,6 +26,7 @@ import { · 25 unmodified lines
26 expandCatalogToolByPath,26 expandCatalogToolByPath,
27 type LoadedSourceCatalog,27 type LoadedSourceCatalog,
28} from "../source-catalog-runtime";28} from "../source-catalog-runtime";
29import { snapshotFromSourceCatalogSyncResult } from "../source-catalog-support";
29import { createSourceFromPayload } from "../source-definitions";30import { createSourceFromPayload } from "../source-definitions";
30import { mcpSourceAdapter } from "./mcp";31import { mcpSourceAdapter } from "./mcp";
31 32
@@ -252,13 +253,14 @@ describe("mcp source adapter", () => { · 220 unmodified lines
252 cookies: {},253 cookies: {},
253 bodyValues: {},254 bodyValues: {},
254 expiresAt: null,255 expiresAt: null,
255 refreshAfter: null,256 refreshAfter: null,
256 }),257 }),
257 });258 });
259 const snapshot = snapshotFromSourceCatalogSyncResult(syncResult);
258 260
259 const capability = Object.values(syncResult.snapshot.catalog.capabilities)[0]!;261 const capability = Object.values(snapshot.catalog.capabilities)[0]!;
260 const executable = Object.values(syncResult.snapshot.catalog.executables)[0]!;262 const executable = Object.values(snapshot.catalog.executables)[0]!;
261 const document = Object.values(syncResult.snapshot.catalog.documents)[0]!;263 const document = Object.values(snapshot.catalog.documents)[0]!;
262 const rawManifest = document.native?.[0]?.value;264 const rawManifest = document.native?.[0]?.value;
263 265
264 expect(capability.surface.title).toBe("Read File");266 expect(capability.surface.title).toBe("Read File");
@@ -339,14 +341,15 @@ describe("mcp source adapter", () => { · 74 unmodified lines
339 cookies: {},341 cookies: {},
340 bodyValues: {},342 bodyValues: {},
341 expiresAt: null,343 expiresAt: null,
342 refreshAfter: null,344 refreshAfter: null,
343 }),345 }),
344 });346 });
347 const snapshot = snapshotFromSourceCatalogSyncResult(syncResult);
345 348
346 const tool = yield* expandCatalogToolByPath({349 const tool = yield* expandCatalogToolByPath({
347 catalogs: [makeLoadedCatalog({350 catalogs: [makeLoadedCatalog({
348 source,351 source,
349 snapshot: syncResult.snapshot,352 snapshot,
350 })],353 })],
351 path: "mcp.demo.read_file",354 path: "mcp.demo.read_file",
352 });355 });
packages/control-plane/src/runtime/source-adapters/mcp.ts+10−4
@@ -19,11 +19,13 @@ import * as Effect from "effect/Effect"; · 18 unmodified lines
19import * as Schema from "effect/Schema";19import * as Schema from "effect/Schema";
20 20
21import {21import {
22 createMcpCatalogSnapshot,22 createCatalogImportMetadata,
23 createMcpCatalogFragment,
23 type McpCatalogOperationInput,24 type McpCatalogOperationInput,
24} from "../source-catalog-snapshot";25} from "../source-catalog-snapshot";
25import {26import {
26 contentHash,27 contentHash,
28 createSourceCatalogSyncResult,
27 type SourceCatalogSyncResult,29 type SourceCatalogSyncResult,
28} from "../source-catalog-support";30} from "../source-catalog-support";
29import { namespaceFromSourceName } from "../source-names";31import { namespaceFromSourceName } from "../source-names";
@@ -160,8 +162,8 @@ export const catalogSyncResultFromMcpManifest = (input: { · 130 unmodified lines
160 const manifestJson = JSON.stringify(input.manifest);162 const manifestJson = JSON.stringify(input.manifest);
161 const manifestHash = contentHash(manifestJson);163 const manifestHash = contentHash(manifestJson);
162 164
163 return {165 return createSourceCatalogSyncResult({
164 snapshot: createMcpCatalogSnapshot({166 fragment: createMcpCatalogFragment({
165 source: input.source,167 source: input.source,
166 documents: [{168 documents: [{
167 documentKind: "mcp_manifest",169 documentKind: "mcp_manifest",
@@ -176,8 +178,12 @@ export const catalogSyncResultFromMcpManifest = (input: { · 8 unmodified lines
176 })178 })
177 ),179 ),
178 }),180 }),
181 importMetadata: createCatalogImportMetadata({
182 source: input.source,
183 adapterKey: "mcp",
184 }),
179 sourceHash: manifestHash,185 sourceHash: manifestHash,
180 };186 });
181};187};
182 188
183export const mcpSourceAdapter: SourceAdapter = {189export const mcpSourceAdapter: SourceAdapter = {
packages/control-plane/src/runtime/source-adapters/openapi.ts+10−4
@@ -20,9 +20,11 @@ import * as Effect from "effect/Effect"; · 19 unmodified lines
20import * as Schema from "effect/Schema";20import * as Schema from "effect/Schema";
21 21
22import {22import {
23 createOpenApiCatalogSnapshot,23 createCatalogImportMetadata,
24 createOpenApiCatalogFragment,
24 type OpenApiCatalogOperationInput,25 type OpenApiCatalogOperationInput,
25} from "../source-catalog-snapshot";26} from "../source-catalog-snapshot";
27import { createSourceCatalogSyncResult } from "../source-catalog-support";
26import type { SourceAdapter } from "./types";28import type { SourceAdapter } from "./types";
27import {29import {
28 ConnectHttpAuthSchema,30 ConnectHttpAuthSchema,
@@ -299,8 +301,8 @@ export const openApiSourceAdapter: SourceAdapter = { · 270 unmodified lines
299 const definitions = compileOpenApiToolDefinitions(manifest);301 const definitions = compileOpenApiToolDefinitions(manifest);
300 const now = Date.now();302 const now = Date.now();
301 303
302 return {304 return createSourceCatalogSyncResult({
303 snapshot: createOpenApiCatalogSnapshot({305 fragment: createOpenApiCatalogFragment({
304 source,306 source,
305 documents: [{307 documents: [{
306 documentKind: "openapi",308 documentKind: "openapi",
@@ -314,7 +316,11 @@ export const openApiSourceAdapter: SourceAdapter = { · 7 unmodified lines
314 refHintTable: manifest.refHintTable,316 refHintTable: manifest.refHintTable,
315 })),317 })),
316 }),318 }),
319 importMetadata: createCatalogImportMetadata({
320 source,
321 adapterKey: "openapi",
322 }),
317 sourceHash: manifest.sourceHash,323 sourceHash: manifest.sourceHash,
318 };324 });
319 }),325 }),
320};326};
packages/control-plane/src/runtime/source-catalog-snapshot.ts+127−45
@@ -14,8 +14,7 @@ import type { · 13 unmodified lines
14import type { Source } from "#schema";14import type { Source } from "#schema";
15 15
16import {16import {
17 createCatalogSnapshotV1,17 createCatalogSnapshotV1FromFragments,
18 createEmptyCatalogV1,
19} from "../ir/catalog";18} from "../ir/catalog";
20import {19import {
21 CapabilityIdSchema,20 CapabilityIdSchema,
@@ -36,8 +35,8 @@ import { · 14 unmodified lines
36import type {35import type {
37 AuthRequirement,36 AuthRequirement,
38 Capability,37 Capability,
38 CatalogFragmentV1,
39 CatalogSnapshotV1,39 CatalogSnapshotV1,
40 CatalogV1,
41 ContentSpec,40 ContentSpec,
42 DocumentationBlock,41 DocumentationBlock,
43 EffectKind,42 EffectKind,
@@ -63,6 +62,18 @@ import { namespaceFromSourceName } from "./source-names"; · 19 unmodified lines
63 62
64type JsonSchema = boolean | Record<string, unknown>;63type JsonSchema = boolean | Record<string, unknown>;
65 64
65type CatalogFragmentBuilder = {
66 version: "ir.v1.fragment";
67 documents: NonNullable<CatalogFragmentV1["documents"]>;
68 resources: NonNullable<CatalogFragmentV1["resources"]>;
69 scopes: NonNullable<CatalogFragmentV1["scopes"]>;
70 symbols: NonNullable<CatalogFragmentV1["symbols"]>;
71 capabilities: NonNullable<CatalogFragmentV1["capabilities"]>;
72 executables: NonNullable<CatalogFragmentV1["executables"]>;
73 responseSets: NonNullable<CatalogFragmentV1["responseSets"]>;
74 diagnostics: NonNullable<CatalogFragmentV1["diagnostics"]>;
75};
76
66export type CatalogSourceDocumentInput = {77export type CatalogSourceDocumentInput = {
67 documentKind: string;78 documentKind: string;
68 documentKey: string;79 documentKey: string;
@@ -262,8 +273,37 @@ const importMetadataFor = (input: { · 193 unmodified lines
262 }),273 }),
263});274});
264 275
276export const createCatalogImportMetadata = (input: {
277 source: Source;
278 adapterKey: string;
279}) => importMetadataFor(input);
280
281const createEmptyCatalogFragment = (): CatalogFragmentBuilder => ({
282 version: "ir.v1.fragment",
283 documents: {},
284 resources: {},
285 scopes: {},
286 symbols: {},
287 capabilities: {},
288 executables: {},
289 responseSets: {},
290 diagnostics: {},
291});
292
293const finalizeCatalogFragment = (fragment: CatalogFragmentBuilder): CatalogFragmentV1 => ({
294 version: "ir.v1.fragment",
295 ...(Object.keys(fragment.documents).length > 0 ? { documents: fragment.documents } : {}),
296 ...(Object.keys(fragment.resources).length > 0 ? { resources: fragment.resources } : {}),
297 ...(Object.keys(fragment.scopes).length > 0 ? { scopes: fragment.scopes } : {}),
298 ...(Object.keys(fragment.symbols).length > 0 ? { symbols: fragment.symbols } : {}),
299 ...(Object.keys(fragment.capabilities).length > 0 ? { capabilities: fragment.capabilities } : {}),
300 ...(Object.keys(fragment.executables).length > 0 ? { executables: fragment.executables } : {}),
301 ...(Object.keys(fragment.responseSets).length > 0 ? { responseSets: fragment.responseSets } : {}),
302 ...(Object.keys(fragment.diagnostics).length > 0 ? { diagnostics: fragment.diagnostics } : {}),
303});
304
265const addDiagnostic = (305const addDiagnostic = (
266 catalog: CatalogV1,306 catalog: CatalogFragmentBuilder,
267 input: Omit<ImportDiagnostic, "id">,307 input: Omit<ImportDiagnostic, "id">,
268) => {308) => {
269 const id = DiagnosticIdSchema.make(`diag_${stableHash(input)}`);309 const id = DiagnosticIdSchema.make(`diag_${stableHash(input)}`);
@@ -310,7 +350,7 @@ const docsFrom = (input: { · 40 unmodified lines
310};350};
311 351
312const createJsonSchemaImporter = (input: {352const createJsonSchemaImporter = (input: {
313 catalog: CatalogV1;353 catalog: CatalogFragmentBuilder;
314 source: Source;354 source: Source;
315 resourceId: ReturnType<typeof ResourceIdSchema.make>;355 resourceId: ReturnType<typeof ResourceIdSchema.make>;
316 documentId: ReturnType<typeof DocumentIdSchema.make>;356 documentId: ReturnType<typeof DocumentIdSchema.make>;
@@ -800,7 +840,7 @@ const createJsonSchemaImporter = (input: { · 483 unmodified lines
800};840};
801 841
802const exampleSymbolFromValue = (input: {842const exampleSymbolFromValue = (input: {
803 catalog: CatalogV1;843 catalog: CatalogFragmentBuilder;
804 source: Source;844 source: Source;
805 documentId: ReturnType<typeof DocumentIdSchema.make>;845 documentId: ReturnType<typeof DocumentIdSchema.make>;
806 pointer: string;846 pointer: string;
@@ -886,7 +926,7 @@ const preferredResponseContentTypes = ( · 79 unmodified lines
886};926};
887 927
888const createServiceScope = (input: {928const createServiceScope = (input: {
889 catalog: CatalogV1;929 catalog: CatalogFragmentBuilder;
890 source: Source;930 source: Source;
891 documentId: ReturnType<typeof DocumentIdSchema.make>;931 documentId: ReturnType<typeof DocumentIdSchema.make>;
892 defaults?: Scope["defaults"];932 defaults?: Scope["defaults"];
@@ -936,7 +976,7 @@ const googleDiscoveryServerSpecs = ( · 43 unmodified lines
936};976};
937 977
938const createOperationScope = (input: {978const createOperationScope = (input: {
939 catalog: CatalogV1;979 catalog: CatalogFragmentBuilder;
940 source: Source;980 source: Source;
941 documentId: ReturnType<typeof DocumentIdSchema.make>;981 documentId: ReturnType<typeof DocumentIdSchema.make>;
942 parentScopeId: ReturnType<typeof ScopeIdSchema.make>;982 parentScopeId: ReturnType<typeof ScopeIdSchema.make>;
@@ -967,7 +1007,7 @@ const createOperationScope = (input: { · 24 unmodified lines
967};1007};
968 1008
969const responseSetFromSingleResponse = (input: {1009const responseSetFromSingleResponse = (input: {
970 catalog: CatalogV1;1010 catalog: CatalogFragmentBuilder;
971 responseId: ReturnType<typeof ResponseSymbolIdSchema.make>;1011 responseId: ReturnType<typeof ResponseSymbolIdSchema.make>;
972 provenance: ProvenanceRef[];1012 provenance: ProvenanceRef[];
973 traits?: ResponseSet["variants"][number]["traits"];1013 traits?: ResponseSet["variants"][number]["traits"];
@@ -993,7 +1033,7 @@ const responseSetFromSingleResponse = (input: { · 19 unmodified lines
993};1033};
994 1034
995const responseSetFromVariants = (input: {1035const responseSetFromVariants = (input: {
996 catalog: CatalogV1;1036 catalog: CatalogFragmentBuilder;
997 variants: ResponseSet["variants"];1037 variants: ResponseSet["variants"];
998 provenance: ProvenanceRef[];1038 provenance: ProvenanceRef[];
999}) => {1039}) => {
@@ -1038,7 +1078,7 @@ const statusMatchFromHttpStatusCode = ( · 38 unmodified lines
1038};1078};
1039 1079
1040const ensureOpenApiSecuritySchemeSymbol = (input: {1080const ensureOpenApiSecuritySchemeSymbol = (input: {
1041 catalog: CatalogV1;1081 catalog: CatalogFragmentBuilder;
1042 source: Source;1082 source: Source;
1043 documentId: ReturnType<typeof DocumentIdSchema.make>;1083 documentId: ReturnType<typeof DocumentIdSchema.make>;
1044 schemeName: string;1084 schemeName: string;
@@ -1147,7 +1187,7 @@ const ensureOpenApiSecuritySchemeSymbol = (input: { · 102 unmodified lines
1147};1187};
1148 1188
1149const openApiAuthRequirementToIr = (input: {1189const openApiAuthRequirementToIr = (input: {
1150 catalog: CatalogV1;1190 catalog: CatalogFragmentBuilder;
1151 source: Source;1191 source: Source;
1152 documentId: ReturnType<typeof DocumentIdSchema.make>;1192 documentId: ReturnType<typeof DocumentIdSchema.make>;
1153 authRequirement: OpenApiToolProviderData["authRequirement"] | undefined;1193 authRequirement: OpenApiToolProviderData["authRequirement"] | undefined;
@@ -1199,7 +1239,7 @@ const openApiAuthRequirementToIr = (input: { · 45 unmodified lines
1199};1239};
1200 1240
1201const contentSpecsFromOpenApiContents = (input: {1241const contentSpecsFromOpenApiContents = (input: {
1202 catalog: CatalogV1;1242 catalog: CatalogFragmentBuilder;
1203 source: Source;1243 source: Source;
1204 documentId: ReturnType<typeof DocumentIdSchema.make>;1244 documentId: ReturnType<typeof DocumentIdSchema.make>;
1205 importer: JsonSchemaImporter;1245 importer: JsonSchemaImporter;
@@ -1239,7 +1279,7 @@ const contentSpecsFromOpenApiContents = (input: { · 33 unmodified lines
1239 });1279 });
1240 1280
1241const createOpenApiHeaderSymbol = (input: {1281const createOpenApiHeaderSymbol = (input: {
1242 catalog: CatalogV1;1282 catalog: CatalogFragmentBuilder;
1243 source: Source;1283 source: Source;
1244 documentId: ReturnType<typeof DocumentIdSchema.make>;1284 documentId: ReturnType<typeof DocumentIdSchema.make>;
1245 importer: JsonSchemaImporter;1285 importer: JsonSchemaImporter;
@@ -1307,7 +1347,7 @@ const createOpenApiHeaderSymbol = (input: { · 61 unmodified lines
1307};1347};
1308 1348
1309const createHttpCapabilityFromOpenApi = (input: {1349const createHttpCapabilityFromOpenApi = (input: {
1310 catalog: CatalogV1;1350 catalog: CatalogFragmentBuilder;
1311 source: Source;1351 source: Source;
1312 documentId: ReturnType<typeof DocumentIdSchema.make>;1352 documentId: ReturnType<typeof DocumentIdSchema.make>;
1313 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;1353 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;
@@ -1714,7 +1754,7 @@ const createHttpCapabilityFromOpenApi = (input: { · 400 unmodified lines
1714};1754};
1715 1755
1716const createHttpCapabilityFromGoogleDiscovery = (input: {1756const createHttpCapabilityFromGoogleDiscovery = (input: {
1717 catalog: CatalogV1;1757 catalog: CatalogFragmentBuilder;
1718 source: Source;1758 source: Source;
1719 documentId: ReturnType<typeof DocumentIdSchema.make>;1759 documentId: ReturnType<typeof DocumentIdSchema.make>;
1720 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;1760 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;
@@ -1927,7 +1967,7 @@ const createHttpCapabilityFromGoogleDiscovery = (input: { · 206 unmodified lines
1927};1967};
1928 1968
1929const createGraphqlCapability = (input: {1969const createGraphqlCapability = (input: {
1930 catalog: CatalogV1;1970 catalog: CatalogFragmentBuilder;
1931 source: Source;1971 source: Source;
1932 documentId: ReturnType<typeof DocumentIdSchema.make>;1972 documentId: ReturnType<typeof DocumentIdSchema.make>;
1933 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;1973 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;
@@ -2047,7 +2087,7 @@ const createGraphqlCapability = (input: { · 113 unmodified lines
2047};2087};
2048 2088
2049const createMcpCapability = (input: {2089const createMcpCapability = (input: {
2050 catalog: CatalogV1;2090 catalog: CatalogFragmentBuilder;
2051 source: Source;2091 source: Source;
2052 documentId: ReturnType<typeof DocumentIdSchema.make>;2092 documentId: ReturnType<typeof DocumentIdSchema.make>;
2053 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;2093 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;
@@ -2147,19 +2187,18 @@ const createMcpCapability = (input: { · 93 unmodified lines
2147 } satisfies Capability;2187 } satisfies Capability;
2148};2188};
2149 2189
2150const buildCatalogSnapshot = (input: {2190const buildCatalogFragment = (input: {
2151 source: Source;2191 source: Source;
2152 adapterKey: string;
2153 documents: readonly CatalogSourceDocumentInput[];2192 documents: readonly CatalogSourceDocumentInput[];
2154 serviceScopeDefaults?: Scope["defaults"];2193 serviceScopeDefaults?: Scope["defaults"];
2155 registerOperations: (context: {2194 registerOperations: (context: {
2156 catalog: CatalogV1;2195 catalog: CatalogFragmentBuilder;
2157 documentId: ReturnType<typeof DocumentIdSchema.make>;2196 documentId: ReturnType<typeof DocumentIdSchema.make>;
2158 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;2197 serviceScopeId: ReturnType<typeof ScopeIdSchema.make>;
2159 importer: JsonSchemaImporter;2198 importer: JsonSchemaImporter;
2160 }) => void;2199 }) => void;
2161}): CatalogSnapshotV1 => {2200}): CatalogFragmentV1 => {
2162 const catalog = createEmptyCatalogV1();2201 const catalog = createEmptyCatalogFragment();
2163 const documents = input.documents.length > 02202 const documents = input.documents.length > 0
2164 ? input.documents2203 ? input.documents
2165 : [{2204 : [{
@@ -2237,19 +2276,14 @@ const buildCatalogSnapshot = (input: { · 71 unmodified lines
2237 });2276 });
2238 importer.finalize();2277 importer.finalize();
2239 2278
2240 const snapshot = createCatalogSnapshotV1({2279 return finalizeCatalogFragment(catalog);
2241 import: importMetadataFor(input),
2242 catalog,
2243 });
2244
2245 return snapshot;
2246};2280};
2247 2281
2248export const createOpenApiCatalogSnapshot = (input: {2282export const createOpenApiCatalogFragment = (input: {
2249 source: Source;2283 source: Source;
2250 documents: readonly CatalogSourceDocumentInput[];2284 documents: readonly CatalogSourceDocumentInput[];
2251 operations: readonly OpenApiCatalogOperationInput[];2285 operations: readonly OpenApiCatalogOperationInput[];
2252}): CatalogSnapshotV1 => {2286}): CatalogFragmentV1 => {
2253 const rootSchema = (() => {2287 const rootSchema = (() => {
2254 const primaryDocumentText = input.documents[0]?.contentText;2288 const primaryDocumentText = input.documents[0]?.contentText;
2255 if (!primaryDocumentText) {2289 if (!primaryDocumentText) {
@@ -2263,9 +2297,8 @@ export const createOpenApiCatalogSnapshot = (input: { · 7 unmodified lines
2263 }2297 }
2264 })();2298 })();
2265 2299
2266 return buildCatalogSnapshot({2300 return buildCatalogFragment({
2267 source: input.source,2301 source: input.source,
2268 adapterKey: "openapi",
2269 documents: input.documents,2302 documents: input.documents,
2270 serviceScopeDefaults: (() => {2303 serviceScopeDefaults: (() => {
2271 const documentServers = openApiServerSpecs(2304 const documentServers = openApiServerSpecs(
@@ -2292,14 +2325,26 @@ export const createOpenApiCatalogSnapshot = (input: { · 20 unmodified lines
2292 });2325 });
2293};2326};
2294 2327
2295export const createGoogleDiscoveryCatalogSnapshot = (input: {2328export const createOpenApiCatalogSnapshot = (input: {
2296 source: Source;2329 source: Source;
2297 documents: readonly CatalogSourceDocumentInput[];2330 documents: readonly CatalogSourceDocumentInput[];
2298 operations: readonly GoogleDiscoveryCatalogOperationInput[];2331 operations: readonly OpenApiCatalogOperationInput[];
2299}): CatalogSnapshotV1 =>2332}): CatalogSnapshotV1 =>
2300 buildCatalogSnapshot({2333 createCatalogSnapshotV1FromFragments({
2334 import: createCatalogImportMetadata({
2335 source: input.source,
2336 adapterKey: "openapi",
2337 }),
2338 fragments: [createOpenApiCatalogFragment(input)],
2339 });
2340
2341export const createGoogleDiscoveryCatalogFragment = (input: {
2342 source: Source;
2343 documents: readonly CatalogSourceDocumentInput[];
2344 operations: readonly GoogleDiscoveryCatalogOperationInput[];
2345}): CatalogFragmentV1 =>
2346 buildCatalogFragment({
2301 source: input.source,2347 source: input.source,
2302 adapterKey: "google_discovery",
2303 documents: input.documents,2348 documents: input.documents,
2304 serviceScopeDefaults: (() => {2349 serviceScopeDefaults: (() => {
2305 const servers = googleDiscoveryServerSpecs(input.operations[0]);2350 const servers = googleDiscoveryServerSpecs(input.operations[0]);
@@ -2319,14 +2364,26 @@ export const createGoogleDiscoveryCatalogSnapshot = (input: { · 13 unmodified lines
2319 },2364 },
2320 });2365 });
2321 2366
2322export const createGraphqlCatalogSnapshot = (input: {2367export const createGoogleDiscoveryCatalogSnapshot = (input: {
2323 source: Source;2368 source: Source;
2324 documents: readonly CatalogSourceDocumentInput[];2369 documents: readonly CatalogSourceDocumentInput[];
2325 operations: readonly GraphqlCatalogOperationInput[];2370 operations: readonly GoogleDiscoveryCatalogOperationInput[];
2326}): CatalogSnapshotV1 =>2371}): CatalogSnapshotV1 =>
2327 buildCatalogSnapshot({2372 createCatalogSnapshotV1FromFragments({
2373 import: createCatalogImportMetadata({
2374 source: input.source,
2375 adapterKey: "google_discovery",
2376 }),
2377 fragments: [createGoogleDiscoveryCatalogFragment(input)],
2378 });
2379
2380export const createGraphqlCatalogFragment = (input: {
2381 source: Source;
2382 documents: readonly CatalogSourceDocumentInput[];
2383 operations: readonly GraphqlCatalogOperationInput[];
2384}): CatalogFragmentV1 =>
2385 buildCatalogFragment({
2328 source: input.source,2386 source: input.source,
2329 adapterKey: "graphql",
2330 documents: input.documents,2387 documents: input.documents,
2331 registerOperations: ({ catalog, documentId, serviceScopeId, importer }) => {2388 registerOperations: ({ catalog, documentId, serviceScopeId, importer }) => {
2332 for (const operation of input.operations) {2389 for (const operation of input.operations) {
@@ -2342,14 +2399,26 @@ export const createGraphqlCatalogSnapshot = (input: { · 9 unmodified lines
2342 },2399 },
2343 });2400 });
2344 2401
2345export const createMcpCatalogSnapshot = (input: {2402export const createGraphqlCatalogSnapshot = (input: {
2346 source: Source;2403 source: Source;
2347 documents: readonly CatalogSourceDocumentInput[];2404 documents: readonly CatalogSourceDocumentInput[];
2348 operations: readonly McpCatalogOperationInput[];2405 operations: readonly GraphqlCatalogOperationInput[];
2349}): CatalogSnapshotV1 =>2406}): CatalogSnapshotV1 =>
2350 buildCatalogSnapshot({2407 createCatalogSnapshotV1FromFragments({
2408 import: createCatalogImportMetadata({
2409 source: input.source,
2410 adapterKey: "graphql",
2411 }),
2412 fragments: [createGraphqlCatalogFragment(input)],
2413 });
2414
2415export const createMcpCatalogFragment = (input: {
2416 source: Source;
2417 documents: readonly CatalogSourceDocumentInput[];
2418 operations: readonly McpCatalogOperationInput[];
2419}): CatalogFragmentV1 =>
2420 buildCatalogFragment({
2351 source: input.source,2421 source: input.source,
2352 adapterKey: "mcp",
2353 documents: input.documents,2422 documents: input.documents,
2354 registerOperations: ({ catalog, documentId, serviceScopeId, importer }) => {2423 registerOperations: ({ catalog, documentId, serviceScopeId, importer }) => {
2355 for (const operation of input.operations) {2424 for (const operation of input.operations) {
@@ -2364,3 +2433,16 @@ export const createMcpCatalogSnapshot = (input: { · 8 unmodified lines
2364 }2433 }
2365 },2434 },
2366 });2435 });
2436
2437export const createMcpCatalogSnapshot = (input: {
2438 source: Source;
2439 documents: readonly CatalogSourceDocumentInput[];
2440 operations: readonly McpCatalogOperationInput[];
2441}): CatalogSnapshotV1 =>
2442 createCatalogSnapshotV1FromFragments({
2443 import: createCatalogImportMetadata({
2444 source: input.source,
2445 adapterKey: "mcp",
2446 }),
2447 fragments: [createMcpCatalogFragment(input)],
2448 });
packages/control-plane/src/runtime/source-catalog-support.ts+20−2
@@ -1,5 +1,10 @@
1import { sha256Hex } from "@executor/codemode-core";1import { sha256Hex } from "@executor/codemode-core";
2import type { CatalogSnapshotV1 } from "../ir/model";2import { createCatalogSnapshotV1FromFragments } from "../ir/catalog";
3import type {
4 CatalogFragmentV1,
5 CatalogSnapshotV1,
6 ImportMetadata,
7} from "../ir/model";
3 8
4export const normalizeSearchText = (9export const normalizeSearchText = (
5 ...parts: ReadonlyArray<string | null | undefined>10 ...parts: ReadonlyArray<string | null | undefined>
@@ -13,6 +18,19 @@ export const normalizeSearchText = ( · 7 unmodified lines
13export const contentHash = (value: string): string => sha256Hex(value);18export const contentHash = (value: string): string => sha256Hex(value);
14 19
15export type SourceCatalogSyncResult = {20export type SourceCatalogSyncResult = {
16 snapshot: CatalogSnapshotV1;21 fragment: CatalogFragmentV1;
22 importMetadata: ImportMetadata;
17 sourceHash: string | null;23 sourceHash: string | null;
18};24};
25
26export const createSourceCatalogSyncResult = (
27 input: SourceCatalogSyncResult,
28): SourceCatalogSyncResult => input;
29
30export const snapshotFromSourceCatalogSyncResult = (
31 syncResult: SourceCatalogSyncResult,
32): CatalogSnapshotV1 =>
33 createCatalogSnapshotV1FromFragments({
34 import: syncResult.importMetadata,
35 fragments: [syncResult.fragment],
36 });
packages/control-plane/src/runtime/source-catalog-sync.ts+5−2
@@ -31,6 +31,7 @@ import { · 30 unmodified lines
31 catalogSyncResultFromMcpManifest,31 catalogSyncResultFromMcpManifest,
32} from "./source-adapters/mcp";32} from "./source-adapters/mcp";
33import { SecretMaterialResolverService } from "./secret-material-providers";33import { SecretMaterialResolverService } from "./secret-material-providers";
34import { snapshotFromSourceCatalogSyncResult } from "./source-catalog-support";
34import {35import {
35 refreshSourceTypeDeclarationInBackground,36 refreshSourceTypeDeclarationInBackground,
36} from "./source-type-declarations";37} from "./source-type-declarations";
@@ -139,6 +140,7 @@ const syncSourceCatalogWithDeps = ( · 102 unmodified lines
139 actorAccountId: input.actorAccountId,140 actorAccountId: input.actorAccountId,
140 }),141 }),
141 });142 });
143 const snapshot = snapshotFromSourceCatalogSyncResult(syncResult);
142 yield* deps.sourceArtifactStore.write({144 yield* deps.sourceArtifactStore.write({
143 context: workspaceContext,145 context: workspaceContext,
144 sourceId: input.source.id,146 sourceId: input.source.id,
@@ -172,7 +174,7 @@ const syncSourceCatalogWithDeps = ( · 27 unmodified lines
172 refreshSourceTypeDeclarationInBackground({174 refreshSourceTypeDeclarationInBackground({
173 context: workspaceContext,175 context: workspaceContext,
174 source: input.source,176 source: input.source,
175 snapshot: syncResult.snapshot,177 snapshot,
176 });178 });
177 });179 });
178 }).pipe(180 }).pipe(
@@ -203,6 +205,7 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 24 unmodified lines
203 endpoint: input.source.endpoint,205 endpoint: input.source.endpoint,
204 manifest: input.manifest,206 manifest: input.manifest,
205 });207 });
208 const snapshot = snapshotFromSourceCatalogSyncResult(syncResult);
206 209
207 yield* deps.sourceArtifactStore.write({210 yield* deps.sourceArtifactStore.write({
208 context: workspaceContext,211 context: workspaceContext,
@@ -217,7 +220,7 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 8 unmodified lines
217 refreshSourceTypeDeclarationInBackground({220 refreshSourceTypeDeclarationInBackground({
218 context: workspaceContext,221 context: workspaceContext,
219 source: input.source,222 source: input.source,
220 snapshot: syncResult.snapshot,223 snapshot,
221 });224 });
222 });225 });
223 });226 });