| @@ -8,6 +8,7 @@ import type { · 7 unmodified lines |
| 8 | 8 | StoredSourceRecord, |
| 9 | 9 | StoredSourceCatalogRevisionRecord, |
| 10 | 10 | } from "#schema"; |
| 11 | +import * as Cache from "effect/Cache"; |
| 11 | 12 | import * as Context from "effect/Context"; |
| 12 | 13 | import * as Effect from "effect/Effect"; |
| 13 | 14 | import * as Layer from "effect/Layer"; |
| @@ -133,6 +134,22 @@ const catalogNamespaceFromPath = (path: string): string => { · 119 unmodified lines |
| 133 | 134 | |
| 134 | 135 | const descriptorPath = (descriptor: CatalogToolDescriptor): string => descriptor.path; |
| 135 | 136 | |
| 137 | +export const loadedSourceCatalogToolIndexEntryFromLoadedTool = ( |
| 138 | + tool: LoadedSourceCatalogTool, |
| 139 | +): LoadedSourceCatalogToolIndexEntry => ({ |
| 140 | + path: tool.path, |
| 141 | + searchNamespace: tool.searchNamespace, |
| 142 | + searchText: tool.searchText, |
| 143 | + source: tool.source, |
| 144 | + sourceRecord: tool.sourceRecord, |
| 145 | + capabilityId: tool.capabilityId, |
| 146 | + executableId: tool.executableId, |
| 147 | + capability: tool.capability, |
| 148 | + executable: tool.executable, |
| 149 | + descriptor: tool.descriptor, |
| 150 | + projectedCatalog: tool.projectedCatalog, |
| 151 | +}); |
| 152 | + |
| 136 | 153 | const optionalJsonString = (value: unknown): string | null => { |
| 137 | 154 | if (value === undefined || value === null) { |
| 138 | 155 | return null; |
| @@ -683,7 +700,6 @@ type RuntimeSourceCatalogStoreShape = { · 544 unmodified lines |
| 683 | 700 | loadWorkspaceSourceCatalogToolIndex: (input: { |
| 684 | 701 | scopeId: ScopeId; |
| 685 | 702 | actorScopeId?: ScopeId | null; |
| 686 | | − includeSchemas: boolean; |
| 687 | 703 | }) => Effect.Effect<readonly LoadedSourceCatalogToolIndexEntry[], Error, never>; |
| 688 | 704 | loadWorkspaceSourceCatalogToolByPath: (input: { |
| 689 | 705 | scopeId: ScopeId; |
| @@ -710,6 +726,23 @@ type SourceCatalogRuntimeServices = · 20 unmodified lines |
| 710 | 726 | | RuntimeSourceStoreService |
| 711 | 727 | | SourceArtifactStore; |
| 712 | 728 | |
| 729 | +type WorkspaceSourceCatalogCacheKey = { |
| 730 | + scopeId: ScopeId; |
| 731 | + actorScopeId: ScopeId | null; |
| 732 | + sourceFingerprint: string; |
| 733 | +}; |
| 734 | + |
| 735 | +const normalizeActorScopeId = ( |
| 736 | + actorScopeId?: ScopeId | null, |
| 737 | +): ScopeId | null => actorScopeId ?? null; |
| 738 | + |
| 739 | +const workspaceSourceFingerprint = (sources: readonly Source[]): string => |
| 740 | + JSON.stringify( |
| 741 | + [...sources].sort((left, right) => |
| 742 | + String(left.id).localeCompare(String(right.id)) |
| 743 | + ), |
| 744 | + ); |
| 745 | + |
| 713 | 746 | const ensureRuntimeCatalogWorkspace = ( |
| 714 | 747 | deps: RuntimeSourceCatalogStoreDeps, |
| 715 | 748 | scopeId: ScopeId, |
| @@ -731,19 +764,45 @@ const buildSnapshotFromArtifact = (input: { · 15 unmodified lines |
| 731 | 764 | return decodeCatalogSnapshotV1(input.artifact.snapshot); |
| 732 | 765 | }; |
| 733 | 766 | |
| 734 | | −const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: { |
| 735 | | − scopeId: ScopeId; |
| 736 | | − actorScopeId?: ScopeId | null; |
| 737 | | −}): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> => |
| 767 | +const loadWorkspaceSourcesWithDeps = ( |
| 768 | + deps: RuntimeSourceCatalogStoreDeps, |
| 769 | + input: { |
| 770 | + scopeId: ScopeId; |
| 771 | + actorScopeId?: ScopeId | null; |
| 772 | + }, |
| 773 | +): Effect.Effect<readonly Source[], Error, never> => |
| 738 | 774 | Effect.gen(function* () { |
| 739 | 775 | yield* ensureRuntimeCatalogWorkspace(deps, input.scopeId); |
| 740 | | − const sources = yield* deps.sourceStore.loadSourcesInScope( |
| 776 | + |
| 777 | + return yield* deps.sourceStore.loadSourcesInScope( |
| 741 | 778 | input.scopeId, |
| 742 | 779 | { |
| 743 | 780 | actorScopeId: input.actorScopeId, |
| 744 | 781 | }, |
| 745 | 782 | ); |
| 783 | + }); |
| 746 | 784 | |
| 785 | +const loadWorkspaceSourceCatalogCacheKeyWithDeps = ( |
| 786 | + deps: RuntimeSourceCatalogStoreDeps, |
| 787 | + input: { |
| 788 | + scopeId: ScopeId; |
| 789 | + actorScopeId?: ScopeId | null; |
| 790 | + }, |
| 791 | +): Effect.Effect<WorkspaceSourceCatalogCacheKey, Error, never> => |
| 792 | + Effect.map( |
| 793 | + loadWorkspaceSourcesWithDeps(deps, input), |
| 794 | + (sources) => ({ |
| 795 | + scopeId: input.scopeId, |
| 796 | + actorScopeId: normalizeActorScopeId(input.actorScopeId), |
| 797 | + sourceFingerprint: workspaceSourceFingerprint(sources), |
| 798 | + }), |
| 799 | + ); |
| 800 | + |
| 801 | +const loadLoadedSourceCatalogsFromSourcesWithDeps = ( |
| 802 | + deps: RuntimeSourceCatalogStoreDeps, |
| 803 | + sources: readonly Source[], |
| 804 | +): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> => |
| 805 | + Effect.gen(function* () { |
| 747 | 806 | const localCatalogs = yield* Effect.forEach(sources, (source) => |
| 748 | 807 | Effect.gen(function* () { |
| 749 | 808 | const artifact = yield* deps.sourceArtifactStore.read({ |
| @@ -781,6 +840,15 @@ const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps · 31 unmodified lines |
| 781 | 840 | return localCatalogs.filter((catalogEntry): catalogEntry is LoadedSourceCatalog => catalogEntry !== null); |
| 782 | 841 | }); |
| 783 | 842 | |
| 843 | +const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: { |
| 844 | + scopeId: ScopeId; |
| 845 | + actorScopeId?: ScopeId | null; |
| 846 | +}): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> => |
| 847 | + Effect.gen(function* () { |
| 848 | + const sources = yield* loadWorkspaceSourcesWithDeps(deps, input); |
| 849 | + return yield* loadLoadedSourceCatalogsFromSourcesWithDeps(deps, sources); |
| 850 | + }); |
| 851 | + |
| 784 | 852 | const loadSourceWithCatalogWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: { |
| 785 | 853 | scopeId: ScopeId; |
| 786 | 854 | sourceId: Source["id"]; |
| @@ -1107,7 +1175,6 @@ export const expandCatalogToolByPath = (input: { · 320 unmodified lines |
| 1107 | 1175 | export const loadWorkspaceSourceCatalogToolIndex = (input: { |
| 1108 | 1176 | scopeId: ScopeId; |
| 1109 | 1177 | actorScopeId?: ScopeId | null; |
| 1110 | | − includeSchemas: boolean; |
| 1111 | 1178 | }): Effect.Effect< |
| 1112 | 1179 | readonly LoadedSourceCatalogToolIndexEntry[], |
| 1113 | 1180 | Error, |
| @@ -1120,21 +1187,10 @@ export const loadWorkspaceSourceCatalogToolIndex = (input: { · 6 unmodified lines |
| 1120 | 1187 | }); |
| 1121 | 1188 | const tools = yield* expandCatalogTools({ |
| 1122 | 1189 | catalogs, |
| 1123 | | − includeSchemas: input.includeSchemas, |
| 1190 | + includeSchemas: false, |
| 1191 | + includeTypePreviews: false, |
| 1124 | 1192 | }); |
| 1125 | | − return tools.map((tool) => ({ |
| 1126 | | − path: tool.path, |
| 1127 | | − searchNamespace: tool.searchNamespace, |
| 1128 | | − searchText: tool.searchText, |
| 1129 | | − source: tool.source, |
| 1130 | | − sourceRecord: tool.sourceRecord, |
| 1131 | | − capabilityId: tool.capabilityId, |
| 1132 | | − executableId: tool.executableId, |
| 1133 | | − capability: tool.capability, |
| 1134 | | − executable: tool.executable, |
| 1135 | | − descriptor: tool.descriptor, |
| 1136 | | − projectedCatalog: tool.projectedCatalog, |
| 1137 | | − })); |
| 1193 | + return tools.map(loadedSourceCatalogToolIndexEntryFromLoadedTool); |
| 1138 | 1194 | }); |
| 1139 | 1195 | |
| 1140 | 1196 | export const loadWorkspaceSourceCatalogToolByPath = (input: { |
| @@ -1187,23 +1243,85 @@ export const RuntimeSourceCatalogStoreLive = Layer.effect( · 46 unmodified lines |
| 1187 | 1243 | sourceArtifactStore, |
| 1188 | 1244 | }; |
| 1189 | 1245 | |
| 1246 | + const workspaceCatalogCache = yield* Cache.make< |
| 1247 | + WorkspaceSourceCatalogCacheKey, |
| 1248 | + readonly LoadedSourceCatalog[], |
| 1249 | + Error |
| 1250 | + >({ |
| 1251 | + capacity: 32, |
| 1252 | + timeToLive: "10 minutes", |
| 1253 | + lookup: (key) => |
| 1254 | + loadWorkspaceSourceCatalogsWithDeps( |
| 1255 | + deps, |
| 1256 | + { |
| 1257 | + scopeId: key.scopeId, |
| 1258 | + actorScopeId: key.actorScopeId, |
| 1259 | + }, |
| 1260 | + ), |
| 1261 | + }); |
| 1262 | + |
| 1263 | + const leanWorkspaceToolIndexCache = yield* Cache.make< |
| 1264 | + WorkspaceSourceCatalogCacheKey, |
| 1265 | + readonly LoadedSourceCatalogToolIndexEntry[], |
| 1266 | + Error |
| 1267 | + >({ |
| 1268 | + capacity: 32, |
| 1269 | + timeToLive: "10 minutes", |
| 1270 | + lookup: (key) => |
| 1271 | + Effect.gen(function* () { |
| 1272 | + const catalogs = yield* workspaceCatalogCache.get(key); |
| 1273 | + const tools = yield* expandCatalogTools({ |
| 1274 | + catalogs, |
| 1275 | + includeSchemas: false, |
| 1276 | + includeTypePreviews: false, |
| 1277 | + }); |
| 1278 | + |
| 1279 | + return tools.map(loadedSourceCatalogToolIndexEntryFromLoadedTool); |
| 1280 | + }), |
| 1281 | + }); |
| 1282 | + |
| 1283 | + const loadCachedWorkspaceCatalogs = (input: { |
| 1284 | + scopeId: ScopeId; |
| 1285 | + actorScopeId?: ScopeId | null; |
| 1286 | + }) => |
| 1287 | + Effect.flatMap( |
| 1288 | + loadWorkspaceSourceCatalogCacheKeyWithDeps(deps, input), |
| 1289 | + (key) => workspaceCatalogCache.get(key), |
| 1290 | + ); |
| 1291 | + |
| 1292 | + const loadLeanWorkspaceToolIndex = (input: { |
| 1293 | + scopeId: ScopeId; |
| 1294 | + actorScopeId?: ScopeId | null; |
| 1295 | + }) => |
| 1296 | + Effect.flatMap( |
| 1297 | + loadWorkspaceSourceCatalogCacheKeyWithDeps(deps, input), |
| 1298 | + (key) => leanWorkspaceToolIndexCache.get(key), |
| 1299 | + ); |
| 1300 | + |
| 1190 | 1301 | return RuntimeSourceCatalogStoreService.of({ |
| 1191 | 1302 | loadWorkspaceSourceCatalogs: (input) => |
| 1192 | | − loadWorkspaceSourceCatalogsWithDeps(deps, input), |
| 1303 | + loadCachedWorkspaceCatalogs(input), |
| 1193 | 1304 | loadSourceWithCatalog: (input) => |
| 1194 | 1305 | loadSourceWithCatalogWithDeps(deps, input), |
| 1195 | 1306 | loadWorkspaceSourceCatalogToolIndex: (input) => |
| 1196 | | − loadWorkspaceSourceCatalogToolIndex(input).pipe( |
| 1197 | | − Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope), |
| 1198 | | − Effect.provideService(RuntimeSourceStoreService, sourceStore), |
| 1199 | | − Effect.provideService(SourceArtifactStore, sourceArtifactStore), |
| 1200 | | − ), |
| 1307 | + loadLeanWorkspaceToolIndex({ |
| 1308 | + scopeId: input.scopeId, |
| 1309 | + actorScopeId: input.actorScopeId, |
| 1310 | + }), |
| 1201 | 1311 | loadWorkspaceSourceCatalogToolByPath: (input) => |
| 1202 | | − loadWorkspaceSourceCatalogToolByPath(input).pipe( |
| 1203 | | − Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope), |
| 1204 | | − Effect.provideService(RuntimeSourceStoreService, sourceStore), |
| 1205 | | − Effect.provideService(SourceArtifactStore, sourceArtifactStore), |
| 1206 | | − ), |
| 1312 | + input.includeSchemas |
| 1313 | + ? loadWorkspaceSourceCatalogToolByPath(input).pipe( |
| 1314 | + Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope), |
| 1315 | + Effect.provideService(RuntimeSourceStoreService, sourceStore), |
| 1316 | + Effect.provideService(SourceArtifactStore, sourceArtifactStore), |
| 1317 | + ) |
| 1318 | + : Effect.map( |
| 1319 | + loadLeanWorkspaceToolIndex({ |
| 1320 | + scopeId: input.scopeId, |
| 1321 | + actorScopeId: input.actorScopeId, |
| 1322 | + }), |
| 1323 | + (tools) => tools.find((tool) => tool.path === input.path) ?? null, |
| 1324 | + ), |
| 1207 | 1325 | }); |
| 1208 | 1326 | }), |
| 1209 | 1327 | ); |