| 152 | } |
| 153 | |
| 154 | export const makeAppsStore = (input: { |
| 155 | readonly blobs: PluginBlobStore; |
| 156 | readonly pluginStorage: PluginStorageFacade; |
| 157 | }): AppsStore => { |
| 158 | const descriptors = input.pluginStorage.collection(descriptorCollection); |
| 159 | const tools = input.pluginStorage.collection(toolCollection); |
| 160 | const sources = input.pluginStorage.collection(sourceCollection); |
| 161 | const keyForTool = (app: string, name: string) => `${app}:${name}`; |
| 162 | return { |
| 163 | putBlob: (body, owner) => |
| 164 | sha256Hex(body).pipe( |
| 165 | Effect.flatMap((hash) => |
| 166 | input.blobs.put(`apps/${hash}`, body, { owner }).pipe(Effect.as(`apps/${hash}`)), |
| 167 | ), |
| 168 | ), |
| 169 | getBlob: (key) => input.blobs.get(key), |
| 170 | getDescriptorRecord: (app) => |
| 171 | descriptors.get({ key: app }).pipe( |
| 172 | Effect.map((entry) => |
| 173 | entry |
| 174 | ? { |
| 175 | sourceRef: entry.data.sourceRef, |
| 176 | descriptorKey: entry.data.descriptorKey, |
| 177 | } |
| 178 | : null, |
| 179 | ), |
| 180 | ), |
| 181 | putPublished: (descriptor, descriptorKey, owner, expectedSourceRef) => |
| 182 | Effect.gen(function* () { |
| 183 | const current = yield* descriptors.get({ key: descriptor.app }); |
| 184 | const actualSourceRef = current?.data.sourceRef ?? null; |
| 185 | if (actualSourceRef !== expectedSourceRef) { |
| 186 | return yield* new AppPublishConflictError({ |
| 187 | app: descriptor.app, |
| 188 | expectedSourceRef, |
| 189 | actualSourceRef, |
| 190 | }); |
| 191 | } |
| 192 | const now = descriptor.publishedAt; |
| 193 | const existing = yield* tools.query({ where: { app: descriptor.app } }); |
| 194 | const activeNames = new Set(descriptor.tools.map((tool) => tool.name)); |
| 195 | const entries: PutManyEntry[] = descriptor.tools.map((tool) => ({ |
| 196 | collection: toolCollection.name, |
| 197 | key: keyForTool(descriptor.app, tool.name), |
| 198 | data: { |
| 199 | app: descriptor.app, |
| 200 | name: tool.name, |
| 201 | sourceRef: descriptor.sourceRef, |
| 202 | descriptorKey, |
| 203 | bundleKey: tool.bundleKey, |
| 204 | description: tool.description, |
| 205 | inputSchema: tool.inputSchema, |
| 206 | outputSchema: tool.outputSchema, |
| 207 | integrations: tool.integrations, |
| 208 | annotations: tool.annotations, |
| 209 | tombstoned: false, |
| 210 | updatedAt: now, |
| 211 | }, |