180 lines · 6.8 KB · TypeScript
RawDownload
import { Effect, Option, Predicate, Schema } from "effect"; import {  type PluginStorageEntry,  type StorageDeps,  type StorageFailure,} from "@executor-js/sdk/core"; import { OperationBinding } from "./types"; // ---------------------------------------------------------------------------// OpenAPI plugin store (v2). The catalog row (integration.config) owns the// auth templates plus the spec's content hash; the resolved spec text itself// lives in the plugin blob store under `spec/<hash>` (it's multi-MB and only// a build input). This store keeps the per-operation invocation bindings// (method / path / params), keyed by integration slug, so `invokeTool` can map// a tool name back to its HTTP operation without re-parsing the spec on every// call. There are NO credential bindings, slots, or StoredSource credential// config here — those concepts are gone in v2.//// Operations are spec-derived (identical for every connection on an// integration), so they live under the org owner (the integration catalog is// tenant-level). The plugin storage facade partitions by owner; "org" keeps a// single shared copy per integration.// --------------------------------------------------------------------------- const OPERATION_COLLECTION = "operation";const STORE_OWNER = "org" as const;const OPERATION_KEY_VERSION = "op"; const encodeBinding = Schema.encodeSync(OperationBinding);const decodeBinding = Schema.decodeUnknownSync(OperationBinding);const decodeBindingJson = Schema.decodeUnknownSync(Schema.fromJsonString(OperationBinding)); const toJsonRecord = (value: unknown): Record<string, unknown> => value as Record<string, unknown>; const OperationStorage = Schema.Struct({  integration: Schema.String,  toolName: Schema.String,  binding: Schema.Unknown,});const decodeOperationStorage = Schema.decodeUnknownOption(OperationStorage); export interface StoredOperation {  /** The integration slug this operation belongs to. */  readonly integration: string;  /** The tool name (the `<tool>` address segment) this operation backs. */  readonly toolName: string;  readonly binding: OperationBinding;} const rowToOperation = (row: PluginStorageEntry): StoredOperation | null => {  const decoded = decodeOperationStorage(row.data);  if (Option.isNone(decoded)) return null;  const operation = decoded.value;  return {    integration: operation.integration,    toolName: operation.toolName,    binding: decodeBinding(      typeof operation.binding === "string"        ? decodeBindingJson(operation.binding)        : operation.binding,    ),  };}; const stableKeyHash = (value: string): string => {  let hash = 0xcbf29ce484222325n;  const prime = 0x100000001b3n;  const mask = 0xffffffffffffffffn;  for (let index = 0; index < value.length; index += 1) {    hash ^= BigInt(value.charCodeAt(index));    hash = (hash * prime) & mask;  }  return hash.toString(36).padStart(13, "0");}; const operationKey = (integration: string, toolName: string): string =>  `${OPERATION_KEY_VERSION}.${stableKeyHash(integration)}.${stableKeyHash(toolName)}`; const legacyOperationKey = (integration: string, toolName: string): string =>  `${integration}.${toolName}`; /** Blob key for a spec's content hash. Content-addressed so re-puts are *  idempotent and identical specs share one blob per partition. */export const specBlobKey = (specHash: string): string => `spec/${specHash}`; export interface OpenapiStore {  /** Replace all stored operations for an integration. */  readonly putOperations: (    integration: string,    operations: readonly StoredOperation[],  ) => Effect.Effect<void, StorageFailure>;  /** Look up one operation by integration + tool name. */  readonly getOperation: (    integration: string,    toolName: string,  ) => Effect.Effect<StoredOperation | null, StorageFailure>;  /** List every stored operation for an integration. */  readonly listOperations: (    integration: string,  ) => Effect.Effect<readonly StoredOperation[], StorageFailure>;  /** Drop all stored operations for an integration. */  readonly removeOperations: (integration: string) => Effect.Effect<void, StorageFailure>;  /** Persist resolved spec text under its content hash. Org-owned and   *  content-addressed; never removed on integration removal because another   *  integration in the tenant may share the hash. */  readonly putSpec: (specHash: string, specText: string) => Effect.Effect<void, StorageFailure>;  /** Load spec text by content hash; null when no blob exists. */  readonly getSpec: (specHash: string) => Effect.Effect<string | null, StorageFailure>;} export const makeDefaultOpenapiStore = ({ pluginStorage, blobs }: StorageDeps): OpenapiStore => {  const operationData = (operation: StoredOperation) => ({    integration: operation.integration,    toolName: operation.toolName,    binding: toJsonRecord(encodeBinding(operation.binding)),  });   const listRows = (integration: string) =>    pluginStorage      .list({ collection: OPERATION_COLLECTION })      .pipe(        Effect.map((rows: readonly PluginStorageEntry[]) =>          rows.filter((row) => rowToOperation(row)?.integration === integration),        ),      );   const removeOperations = (integration: string) =>    Effect.gen(function* () {      const rows = yield* listRows(integration);      yield* pluginStorage.removeMany({        owner: STORE_OWNER,        entries: rows.map((row) => ({ collection: OPERATION_COLLECTION, key: row.key })),      });    });   return {    putOperations: (integration, operations) =>      Effect.gen(function* () {        yield* removeOperations(integration);        yield* pluginStorage.putMany({          owner: STORE_OWNER,          entries: operations.map((operation) => ({            collection: OPERATION_COLLECTION,            key: operationKey(integration, operation.toolName),            data: operationData(operation),          })),        });      }),     getOperation: (integration, toolName) =>      Effect.gen(function* () {        const row = yield* pluginStorage.get({          collection: OPERATION_COLLECTION,          key: operationKey(integration, toolName),        });        if (row) return rowToOperation(row);        const legacyKey = legacyOperationKey(integration, toolName);        if (legacyKey.length > 255) return null;        const legacyRow = yield* pluginStorage.get({          collection: OPERATION_COLLECTION,          key: legacyKey,        });        return legacyRow ? rowToOperation(legacyRow) : null;      }),     listOperations: (integration) =>      listRows(integration).pipe(        Effect.map((rows) => rows.map(rowToOperation).filter(Predicate.isNotNull)),      ),     removeOperations,     putSpec: (specHash, specText) =>      blobs.put(specBlobKey(specHash), specText, { owner: STORE_OWNER }),     getSpec: (specHash) => blobs.get(specBlobKey(specHash)),  };};