| @@ -8,6 +8,7 @@ import type { · 7 unmodified lines |
| 8 | StoredSourceRecord, | 8 | StoredSourceRecord, |
| 9 | StoredSourceCatalogRevisionRecord, | 9 | StoredSourceCatalogRevisionRecord, |
| 10 | } from "#schema"; | 10 | } from "#schema"; |
| | 11 | import * as Cache from "effect/Cache"; |
| 11 | import * as Context from "effect/Context"; | 12 | import * as Context from "effect/Context"; |
| 12 | import * as Effect from "effect/Effect"; | 13 | import * as Effect from "effect/Effect"; |
| 13 | import * as Layer from "effect/Layer"; | 14 | import * as Layer from "effect/Layer"; |
| @@ -133,6 +134,22 @@ const catalogNamespaceFromPath = (path: string): string => { · 119 unmodified lines |
| 133 | | 134 | |
| 134 | const descriptorPath = (descriptor: CatalogToolDescriptor): string => descriptor.path; | 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 | const optionalJsonString = (value: unknown): string | null => { | 153 | const optionalJsonString = (value: unknown): string | null => { |
| 137 | if (value === undefined || value === null) { | 154 | if (value === undefined || value === null) { |
| 138 | return null; | 155 | return null; |
| @@ -683,7 +700,6 @@ type RuntimeSourceCatalogStoreShape = { · 544 unmodified lines |
| 683 | loadWorkspaceSourceCatalogToolIndex: (input: { | 700 | loadWorkspaceSourceCatalogToolIndex: (input: { |
| 684 | scopeId: ScopeId; | 701 | scopeId: ScopeId; |
| 685 | actorScopeId?: ScopeId | null; | 702 | actorScopeId?: ScopeId | null; |
| 686 | includeSchemas: boolean; | | |
| 687 | }) => Effect.Effect<readonly LoadedSourceCatalogToolIndexEntry[], Error, never>; | 703 | }) => Effect.Effect<readonly LoadedSourceCatalogToolIndexEntry[], Error, never>; |
| 688 | loadWorkspaceSourceCatalogToolByPath: (input: { | 704 | loadWorkspaceSourceCatalogToolByPath: (input: { |
| 689 | scopeId: ScopeId; | 705 | scopeId: ScopeId; |
| @@ -710,6 +726,23 @@ type SourceCatalogRuntimeServices = · 20 unmodified lines |
| 710 | | RuntimeSourceStoreService | 726 | | RuntimeSourceStoreService |
| 711 | | SourceArtifactStore; | 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 | const ensureRuntimeCatalogWorkspace = ( | 746 | const ensureRuntimeCatalogWorkspace = ( |
| 714 | deps: RuntimeSourceCatalogStoreDeps, | 747 | deps: RuntimeSourceCatalogStoreDeps, |
| 715 | scopeId: ScopeId, | 748 | scopeId: ScopeId, |
| @@ -731,19 +764,45 @@ const buildSnapshotFromArtifact = (input: { · 15 unmodified lines |
| 731 | return decodeCatalogSnapshotV1(input.artifact.snapshot); | 764 | return decodeCatalogSnapshotV1(input.artifact.snapshot); |
| 732 | }; | 765 | }; |
| 733 | | 766 | |
| 734 | const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: { | 767 | const loadWorkspaceSourcesWithDeps = ( |
| 735 | scopeId: ScopeId; | 768 | deps: RuntimeSourceCatalogStoreDeps, |
| 736 | actorScopeId?: ScopeId | null; | 769 | input: { |
| 737 | }): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> => | 770 | scopeId: ScopeId; |
| | 771 | actorScopeId?: ScopeId | null; |
| | 772 | }, |
| | 773 | ): Effect.Effect<readonly Source[], Error, never> => |
| 738 | Effect.gen(function* () { | 774 | Effect.gen(function* () { |
| 739 | yield* ensureRuntimeCatalogWorkspace(deps, input.scopeId); | 775 | yield* ensureRuntimeCatalogWorkspace(deps, input.scopeId); |
| 740 | const sources = yield* deps.sourceStore.loadSourcesInScope( | 776 | |
| | 777 | return yield* deps.sourceStore.loadSourcesInScope( |
| 741 | input.scopeId, | 778 | input.scopeId, |
| 742 | { | 779 | { |
| 743 | actorScopeId: input.actorScopeId, | 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 | const localCatalogs = yield* Effect.forEach(sources, (source) => | 806 | const localCatalogs = yield* Effect.forEach(sources, (source) => |
| 748 | Effect.gen(function* () { | 807 | Effect.gen(function* () { |
| 749 | const artifact = yield* deps.sourceArtifactStore.read({ | 808 | const artifact = yield* deps.sourceArtifactStore.read({ |
| @@ -781,6 +840,15 @@ const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps · 31 unmodified lines |
| 781 | return localCatalogs.filter((catalogEntry): catalogEntry is LoadedSourceCatalog => catalogEntry !== null); | 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 | const loadSourceWithCatalogWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: { | 852 | const loadSourceWithCatalogWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: { |
| 785 | scopeId: ScopeId; | 853 | scopeId: ScopeId; |
| 786 | sourceId: Source["id"]; | 854 | sourceId: Source["id"]; |
| @@ -1107,7 +1175,6 @@ export const expandCatalogToolByPath = (input: { · 320 unmodified lines |
| 1107 | export const loadWorkspaceSourceCatalogToolIndex = (input: { | 1175 | export const loadWorkspaceSourceCatalogToolIndex = (input: { |
| 1108 | scopeId: ScopeId; | 1176 | scopeId: ScopeId; |
| 1109 | actorScopeId?: ScopeId | null; | 1177 | actorScopeId?: ScopeId | null; |
| 1110 | includeSchemas: boolean; | | |
| 1111 | }): Effect.Effect< | 1178 | }): Effect.Effect< |
| 1112 | readonly LoadedSourceCatalogToolIndexEntry[], | 1179 | readonly LoadedSourceCatalogToolIndexEntry[], |
| 1113 | Error, | 1180 | Error, |
| @@ -1120,21 +1187,10 @@ export const loadWorkspaceSourceCatalogToolIndex = (input: { · 6 unmodified lines |
| 1120 | }); | 1187 | }); |
| 1121 | const tools = yield* expandCatalogTools({ | 1188 | const tools = yield* expandCatalogTools({ |
| 1122 | catalogs, | 1189 | catalogs, |
| 1123 | includeSchemas: input.includeSchemas, | 1190 | includeSchemas: false, |
| | 1191 | includeTypePreviews: false, |
| 1124 | }); | 1192 | }); |
| 1125 | return tools.map((tool) => ({ | 1193 | return tools.map(loadedSourceCatalogToolIndexEntryFromLoadedTool); |
| 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 | })); | | |
| 1138 | }); | 1194 | }); |
| 1139 | | 1195 | |
| 1140 | export const loadWorkspaceSourceCatalogToolByPath = (input: { | 1196 | export const loadWorkspaceSourceCatalogToolByPath = (input: { |
| @@ -1187,23 +1243,85 @@ export const RuntimeSourceCatalogStoreLive = Layer.effect( · 46 unmodified lines |
| 1187 | sourceArtifactStore, | 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 | return RuntimeSourceCatalogStoreService.of({ | 1301 | return RuntimeSourceCatalogStoreService.of({ |
| 1191 | loadWorkspaceSourceCatalogs: (input) => | 1302 | loadWorkspaceSourceCatalogs: (input) => |
| 1192 | loadWorkspaceSourceCatalogsWithDeps(deps, input), | 1303 | loadCachedWorkspaceCatalogs(input), |
| 1193 | loadSourceWithCatalog: (input) => | 1304 | loadSourceWithCatalog: (input) => |
| 1194 | loadSourceWithCatalogWithDeps(deps, input), | 1305 | loadSourceWithCatalogWithDeps(deps, input), |
| 1195 | loadWorkspaceSourceCatalogToolIndex: (input) => | 1306 | loadWorkspaceSourceCatalogToolIndex: (input) => |
| 1196 | loadWorkspaceSourceCatalogToolIndex(input).pipe( | 1307 | loadLeanWorkspaceToolIndex({ |
| 1197 | Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope), | 1308 | scopeId: input.scopeId, |
| 1198 | Effect.provideService(RuntimeSourceStoreService, sourceStore), | 1309 | actorScopeId: input.actorScopeId, |
| 1199 | Effect.provideService(SourceArtifactStore, sourceArtifactStore), | 1310 | }), |
| 1200 | ), | | |
| 1201 | loadWorkspaceSourceCatalogToolByPath: (input) => | 1311 | loadWorkspaceSourceCatalogToolByPath: (input) => |
| 1202 | loadWorkspaceSourceCatalogToolByPath(input).pipe( | 1312 | input.includeSchemas |
| 1203 | Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope), | 1313 | ? loadWorkspaceSourceCatalogToolByPath(input).pipe( |
| 1204 | Effect.provideService(RuntimeSourceStoreService, sourceStore), | 1314 | Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope), |
| 1205 | Effect.provideService(SourceArtifactStore, sourceArtifactStore), | 1315 | Effect.provideService(RuntimeSourceStoreService, sourceStore), |
| 1206 | ), | 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 | ); |