UNPKG

@delta-base/server

Version:
1,390 lines (1,383 loc) 48.5 kB
import { VersionConflictError, ValidationError, InternalServerError, InvalidSubscriptionConfigError, SerializationError, StreamNotFoundError, StorageError, TimeoutError, EventStoreAlreadyExistsError, SubscriptionNotFoundError, EventStoreNotFoundError, RateLimitError, AuthorizationError, AuthenticationError } from '@delta-base/toolkit'; export { AuthenticationError, AuthorizationError, DeltaBaseError, EventStoreAlreadyExistsError, EventStoreNotFoundError, InternalServerError, InvalidSubscriptionConfigError, NotFoundError, RateLimitError, SerializationError, StorageError, StreamNotFoundError, StreamVersionConflictError, SubscriptionNotFoundError, TimeoutError, ValidationError, ValidationErrors, VersionConflictError, assertNotEmptyString, assertPositiveNumber, assertUnsignedBigInt, isAuthenticationError, isAuthorizationError, isDeltaBaseError, isEventStoreAlreadyExistsError, isEventStoreNotFoundError, isInternalServerError, isInvalidSubscriptionConfigError, isNotFoundError, isNumber, isRateLimitError, isSerializationError, isStorageError, isStreamNotFoundError, isStreamVersionConflictError, isString, isSubscriptionNotFoundError, isTimeoutError, isValidationError, isVersionConflictError } from '@delta-base/toolkit'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/clients/event-bus.ts var SubscriberType = /* @__PURE__ */ ((SubscriberType2) => { SubscriberType2["DurableObject"] = "durable_object"; SubscriberType2["Webhook"] = "webhook"; SubscriberType2["Queue"] = "queue"; SubscriberType2["CloudflareWorkflow"] = "cloudflare_workflow"; SubscriberType2["WebSocket"] = "websocket"; return SubscriberType2; })(SubscriberType || {}); var SubscriptionStatus = /* @__PURE__ */ ((SubscriptionStatus2) => { SubscriptionStatus2["Active"] = "ACTIVE"; SubscriptionStatus2["Suspended"] = "SUSPENDED"; SubscriptionStatus2["Error"] = "ERROR"; SubscriptionStatus2["Initializing"] = "INITIALIZING"; SubscriptionStatus2["Deleted"] = "DELETED"; return SubscriptionStatus2; })(SubscriptionStatus || {}); var _EventBus = class _EventBus { /** * Create a new EventBus client for a specific event store * * @param http - The HTTP client used for API requests * @param eventStoreName - The name of the event store to manage subscriptions for */ constructor(http, eventStoreName) { this.http = http; this.eventStoreName = eventStoreName; } /** * Subscribe to events from this event store * * @param options - Configuration for the subscription * @returns The created subscription information * @throws {InvalidSubscriptionConfigError} When subscription configuration is invalid * @throws {EventStoreNotFoundError} When the event store doesn't exist * @throws {ValidationError} When request validation fails * @throws {AuthenticationError} When authentication fails * * @example * ```typescript * import { * isInvalidSubscriptionConfigError, * isEventStoreNotFoundError * } from '@delta-base/server'; * * try { * const subscription = await eventBus.subscribe({ * eventFilter: 'user.*', * subscriber: { * type: SubscriberType.Webhook, * config: { * url: 'https://example.com/webhook', * headers: { 'X-API-Key': 'secret' }, * retryPolicy: { * maxAttempts: 3, * backoffMinutes: 5 * } * } * } * }); * * // Check if this is an existing subscription * if (subscription.isExistingSubscription) { * console.log('Found existing subscription:', subscription.message); * } else { * console.log('Created new subscription:', subscription.message); * } * } catch (error) { * if (isInvalidSubscriptionConfigError(error)) { * console.log(`Invalid configuration: ${error.configError}`); * // Handle invalid subscription config * } else if (isEventStoreNotFoundError(error)) { * console.log(`Event store '${error.eventStoreId}' not found`); * // Handle missing event store * } else { * throw error; // Re-throw unknown errors * } * } * ``` */ async subscribe(options) { const response = await this.http.post( `/api/event-stores/${this.eventStoreName}/subscribe`, options ); return this.mapSubscriptionResponse(response); } /** * Create a webhook subscription * * @param eventFilter - Pattern determining which events to receive * @param url - The URL that will receive HTTP POST requests with events * @param options - Additional configuration options * @returns The created subscription information * * @example * ```typescript * // Subscribe to all user events * const subscription = await eventBus.subscribeWebhook( * 'user.*', * 'https://example.com/webhook', * { * headers: { 'X-API-Key': 'secret' }, * retryPolicy: { * maxAttempts: 3, * backoffMinutes: 5 * } * } * ); * * // Check if this is an existing subscription with the same configuration * if (subscription.isExistingSubscription) { * console.log('Reusing existing subscription:', subscription.message); * } else { * console.log('Created new subscription:', subscription.message); * } * ``` */ async subscribeWebhook(eventFilter, url, options = {}) { const { subscriberId, initialPosition, ...webhookOptions } = options; return this.subscribe({ eventFilter, subscriberId, initialPosition, subscriber: { type: "webhook" /* Webhook */, config: { url, ...webhookOptions } } }); } // /** // * Create a Durable Object subscription // * // * @param eventFilter - Pattern determining which events to receive // * @param namespace - The Durable Object namespace binding name in your worker // * @param id - The ID of the Durable Object instance to deliver events to // * @param options - Additional configuration options // * @returns The created subscription information // * // * @example // * ```typescript // * // Subscribe to order events with a Durable Object // * const subscription = await eventBus.subscribeDurableObject( // * 'order.*', // * 'ORDER_PROCESSOR', // * 'order-processor-instance-1', // * { // * retryPolicy: { // * maxAttempts: 5, // * backoffMinutes: 10 // * } // * } // * ); // * ``` // */ // async subscribeDurableObject( // eventFilter: EventFilterPattern, // namespace: string, // id: string, // options: Omit<DurableObjectConfig, 'namespace' | 'id'> & { // subscriberId?: string; // initialPosition?: InitialPosition; // } = {} // ): Promise<Subscription> { // const { subscriberId, initialPosition, ...doOptions } = options; // return this.subscribe({ // eventFilter, // subscriberId, // initialPosition, // subscriber: { // type: SubscriberType.DurableObject, // config: { // namespace, // id, // ...doOptions, // }, // }, // }); // } // /** // * Create a queue subscription // * // * @param eventFilter - Pattern determining which events to receive // * @param queueName - The name of the queue to send events to // * @param options - Additional configuration options // * @returns The created subscription information // * // * @example // * ```typescript // * // Subscribe to notification events with a queue // * const subscription = await eventBus.subscribeQueue( // * 'notification.*', // * 'notification-queue', // * { // * region: 'us-east-1', // * batchSize: 10 // * } // * ); // * ``` // */ // async subscribeQueue( // eventFilter: EventFilterPattern, // queueName: string, // options: Omit<QueueConfig, 'queueName'> & { // subscriberId?: string; // initialPosition?: InitialPosition; // } = {} // ): Promise<Subscription> { // const { subscriberId, initialPosition, ...queueOptions } = options; // return this.subscribe({ // eventFilter, // subscriberId, // initialPosition, // subscriber: { // type: SubscriberType.Queue, // config: { // queueName, // ...queueOptions, // }, // }, // }); // } // /** // * Create a Cloudflare Workflow subscription // * // * @param eventFilter - Pattern determining which events to receive // * @param bindingName - The name of the workflow binding in your worker // * @param options - Additional configuration options // * @returns The created subscription information // * // * @example // * ```typescript // * // Subscribe to all events with a Cloudflare Workflow // * const subscription = await eventBus.subscribeWorkflow( // * '*', // * 'EVENT_WORKFLOW' // * ); // * ``` // */ // async subscribeWorkflow( // eventFilter: EventFilterPattern, // bindingName: string, // options: Omit<CloudflareWorkflowConfig, 'bindingName'> & { // subscriberId?: string; // initialPosition?: InitialPosition; // } = {} // ): Promise<Subscription> { // const { subscriberId, initialPosition, ...workflowOptions } = options; // return this.subscribe({ // eventFilter, // subscriberId, // initialPosition, // subscriber: { // type: SubscriberType.CloudflareWorkflow, // config: { // bindingName, // ...workflowOptions, // }, // }, // }); // } // /** // * Create a WebSocket subscription // * // * @param eventFilter - Pattern determining which events to receive // * @param managerId - The ID of the WebSocket manager to broadcast events to // * @param options - Additional configuration options // * @returns The created subscription information // * // * @example // * ```typescript // * // Subscribe to notification events with WebSockets // * const subscription = await eventBus.subscribeWebSocket( // * 'notification.*', // * 'notifications-websocket-manager' // * ); // * ``` // */ // async subscribeWebSocket( // eventFilter: EventFilterPattern, // managerId: string, // options: Omit<WebSocketConfig, 'managerId'> & { // subscriberId?: string; // initialPosition?: InitialPosition; // } = {} // ): Promise<Subscription> { // const { subscriberId, initialPosition, ...wsOptions } = options; // return this.subscribe({ // eventFilter, // subscriberId, // initialPosition, // subscriber: { // type: SubscriberType.WebSocket, // config: { // managerId, // ...wsOptions, // }, // }, // }); // } /** * Get details about a specific subscription * * @param subscriptionId - ID of the subscription to retrieve * @returns Subscription details * * @example * ```typescript * const subscription = await eventBus.getSubscription('sub_123456'); * console.log(subscription.status); // 'ACTIVE' * ``` */ async getSubscription(subscriptionId) { const response = await this.http.get( `/api/event-stores/${this.eventStoreName}/subscriptions/${subscriptionId}` ); return this.mapSubscriptionResponse(response); } /** * List all subscriptions for this event store * * @param options - Optional filtering and pagination parameters * @returns List of subscriptions and total count * * @example * ```typescript * // List all webhook subscriptions * const { subscriptions, totalCount } = await eventBus.listSubscriptions({ * subscriberType: SubscriberType.Webhook, * limit: 20, * offset: 0 * }); * ``` */ async listSubscriptions(options = {}) { const params = {}; if (options.subscriberType) { params.subscriberType = options.subscriberType; } if (options.eventFilter) { params.eventFilter = options.eventFilter; } if (options.limit) { params.limit = options.limit.toString(); } if (options.offset) { params.offset = options.offset.toString(); } const response = await this.http.get(`/api/event-stores/${this.eventStoreName}/subscriptions`, params); return { subscriptions: response.subscriptions.map( (sub) => this.mapSubscriptionResponse(sub) ), totalCount: response.totalCount }; } /** * Unsubscribe from events (delete a subscription) * * @param subscriptionId - ID of the subscription to delete * @returns Success message * * @example * ```typescript * const result = await eventBus.unsubscribe('sub_123456'); * console.log(result.success); // true * ``` */ async unsubscribe(subscriptionId) { return this.http.delete( `/api/event-stores/${this.eventStoreName}/subscriptions/${subscriptionId}` ); } /** * Map API subscription response to Subscription type * * @param response - The API response to map * @returns Mapped subscription * @private */ mapSubscriptionResponse(response) { return { subscriptionId: response.subscriptionId, status: response.status, statusDetails: response.statusDetails, eventStoreId: response.eventStoreId, eventFilter: response.eventFilter, subscriberId: response.subscriberId, subscriberType: response.subscriberType, createdAt: response.createdAt, updatedAt: response.updatedAt, lastProcessedEventGlobalPosition: response.lastProcessedEventGlobalPosition, isExistingSubscription: response.isExistingSubscription, message: response.message }; } }; __name(_EventBus, "EventBus"); var EventBus = _EventBus; // src/clients/event-store.ts var _EventStore = class _EventStore { /** * Creates a new EventStore client instance * * @param http - The HTTP client to use for API requests * @param eventStoreName - The name of the event store to interact with */ constructor(http, eventStoreName) { this.http = http; this.eventStoreName = eventStoreName; } /** * Aggregate events from a stream and compute a state * * @param streamId - The ID of the stream to aggregate events from * @param options - Configuration options for the aggregation process * @param options.initialState - Function that returns the initial state * @param options.evolve - Function that applies an event to the current state * @param options.read - Optional read options to control which events to include * @returns Promise resolving to the aggregation result with the computed state and stream metadata * * @example * ```typescript * // Define your state type and event types * type UserState = { email: string, isVerified: boolean }; * type UserEvent = * | { type: 'user.created', data: { email: string } } * | { type: 'user.verified', data: { verifiedAt: string } }; * * // Aggregate the stream into a state * const result = await eventStore.aggregateStream<UserState, UserEvent>( * 'user-123', * { * initialState: () => ({ email: '', isVerified: false }), * evolve: (state, event) => { * switch (event.type) { * case 'user.created': * return { ...state, email: event.data.email }; * case 'user.verified': * return { ...state, isVerified: true }; * default: * return state; * } * }, * read: { from: 0 } * } * ); * ``` */ async aggregateStream(streamId, options) { const readResult = await this.readStream(streamId, options.read); let state = options.initialState(); for (const event of readResult.events) { state = options.evolve(state, event); } return { currentStreamVersion: readResult.currentStreamVersion, state, streamExists: readResult.streamExists }; } /** * Read events from a stream * * @param streamId - The ID of the stream to read events from * @param options - The options for reading events * @param options.from - Optional starting position to read from * @param options.to - Optional ending position to read to * @param options.maxCount - Optional maximum number of events to read * @param options.expectedStreamVersion - Optional expected version for optimistic concurrency * @returns Promise resolving to the read result containing events and stream metadata * * @example * ```typescript * // Read all events from a stream * const result = await eventStore.readStream('user-123'); * * // Read events with a specific starting position * const result = await eventStore.readStream('user-123', { from: 5 }); * * // Read a specific range of events * const result = await eventStore.readStream('user-123', { from: 5, to: 10 }); * * // Read a limited number of events * const result = await eventStore.readStream('user-123', { maxCount: 100 }); * ``` */ async readStream(streamId, options) { const url = `/api/event-stores/${this.eventStoreName}/streams/${streamId}`; const apiOptions = {}; if (options) { if ("from" in options) { apiOptions.from = options.from.toString(); } if ("to" in options) { apiOptions.to = options.to.toString(); } if ("maxCount" in options && options.maxCount !== void 0) { apiOptions.maxCount = options.maxCount.toString(); } if ("expectedStreamVersion" in options && options.expectedStreamVersion !== void 0) { apiOptions.expectedStreamVersion = options.expectedStreamVersion.toString(); } } const response = await this.http.get( url, apiOptions ); const mappedEvents = (response.events || []).map((event) => ({ type: event.type, data: event.data, streamId: event.streamId, streamPosition: event.streamPosition, globalPosition: event.globalPosition, eventId: event.eventId || "unknown", schemaVersion: event.schemaVersion || "1.0.0", transactionId: event.transactionId || "0", createdAt: event.createdAt || (/* @__PURE__ */ new Date()).toISOString(), metadata: event.metadata || {} })); return { currentStreamVersion: response.currentStreamVersion, events: mappedEvents, streamExists: response.streamExists ?? true }; } /** * Append events to a stream * * @param streamId - The ID of the stream to append events to * @param events - Array of events to append to the stream * @param options - Optional parameters for the append operation * @param options.expectedStreamVersion - Optional expected version for optimistic concurrency * @returns Promise resolving to the append result with the next expected version * @throws {VersionConflictError} When expectedStreamVersion doesn't match current stream version * @throws {ValidationError} When request validation fails * @throws {EventStoreNotFoundError} When the event store doesn't exist * @throws {AuthenticationError} When authentication fails * * @example * ```typescript * import { * isVersionConflictError, * isValidationError * } from '@delta-base/server'; * * try { * // Append with optimistic concurrency control * await eventStore.appendToStream( * 'user-123', * [{ * type: 'user.updated', * data: { email: 'updated@example.com' } * }], * { expectedStreamVersion: 0n } * ); * } catch (error) { * if (isVersionConflictError(error)) { * console.log(`Version conflict: expected ${error.expectedVersion}, got ${error.currentVersion}`); * // Handle concurrency conflict * } else if (isValidationError(error)) { * console.log('Validation errors:', error.validationErrors); * // Handle validation failures * } else { * throw error; // Re-throw unknown errors * } * } * ``` */ async appendToStream(streamId, events, options) { const request = { events, options: { expectedStreamVersion: options?.expectedStreamVersion !== void 0 ? typeof options.expectedStreamVersion === "number" ? Number(options.expectedStreamVersion) : typeof options.expectedStreamVersion === "string" ? options.expectedStreamVersion === "no_stream" ? "no_stream" : options.expectedStreamVersion === "stream_exists" ? "stream_exists" : "any" : Number(options.expectedStreamVersion) : void 0 } }; const response = await this.http.post( `/api/event-stores/${this.eventStoreName}/streams/${streamId}`, request ); return { nextExpectedStreamVersion: response.nextExpectedStreamVersion, createdNewStream: !response.success }; } /** * Query events with flexible filtering options * * @param options - Query parameters for filtering events * @param options.streamId - Optional stream ID to filter by * @param options.type - Optional event type(s) to filter by * @param options.eventId - Optional specific event ID to retrieve * @param options.transactionId - Optional transaction ID to filter by * @param options.fromPosition - Optional starting global position * @param options.toPosition - Optional ending global position * @param options.fromDate - Optional starting date for time-based filtering * @param options.toDate - Optional ending date for time-based filtering * @param options.phase - Optional event phase to filter by * @param options.includeArchived - Whether to include archived events * @param options.limit - Maximum number of events to return * @param options.offset - Number of events to skip * @param options.sortBy - Field to sort results by * @param options.sortDirection - Direction to sort results * @param options.includeCount - Whether to include total count in results * @returns Promise resolving to the query result with events and pagination info * * @example * ```typescript * // Query all events of a specific type * const result = await eventStore.queryEvents({ * type: 'user.created' * }); * * // Query events with pagination * const result = await eventStore.queryEvents({ * limit: 20, * offset: 40, * includeCount: true * }); * * // Query events within a time range * const result = await eventStore.queryEvents({ * fromDate: '2023-01-01T00:00:00Z', * toDate: '2023-01-31T23:59:59Z' * }); * ``` */ async queryEvents(options = {}) { const url = `/api/event-stores/${this.eventStoreName}/events`; const queryParams = {}; for (const [key, value] of Object.entries(options)) { if (value !== void 0) { if (Array.isArray(value)) { queryParams[key] = value.join(","); } else { queryParams[key] = String(value); } } } const response = await this.http.get( url, queryParams ); return { events: (response.events || []).map( (event) => ({ type: event.type, data: event.data, streamId: event.streamId, streamPosition: Number(event.streamPosition || 0), globalPosition: Number(event.globalPosition || 0), eventId: event.eventId || "unknown", schemaVersion: event.schemaVersion || "1.0.0", transactionId: event.transactionId || "0", createdAt: event.createdAt || (/* @__PURE__ */ new Date()).toISOString(), metadata: event.metadata }) ), pagination: { limit: response.pagination.limit, offset: response.pagination.offset, total: response.pagination.total, hasMore: response.pagination.hasMore } }; } /** * Query events for a specific stream with filtering options * * @param streamId - The ID of the stream to query events from * @param options - Query parameters for filtering events * @param options.type - Optional event type(s) to filter by * @param options.eventId - Optional specific event ID to retrieve * @param options.transactionId - Optional transaction ID to filter by * @param options.fromPosition - Optional starting global position * @param options.toPosition - Optional ending global position * @param options.fromDate - Optional starting date for time-based filtering * @param options.toDate - Optional ending date for time-based filtering * @param options.phase - Optional event phase to filter by * @param options.includeArchived - Whether to include archived events * @param options.limit - Maximum number of events to return * @param options.offset - Number of events to skip * @param options.sortBy - Field to sort results by * @param options.sortDirection - Direction to sort results * @param options.includeCount - Whether to include total count in results * @returns Promise resolving to the query result with events and pagination info * * @example * ```typescript * // Query events for a specific stream * const result = await eventStore.queryStreamEvents('user-123', { * type: 'user.updated' * }); * ``` */ async queryStreamEvents(streamId, options = {}) { return this.queryEvents({ ...options, streamId }); } /** * Query streams with filtering options * * @param options - Query parameters for filtering streams * @param options.streamId - Optional specific stream ID to retrieve * @param options.streamType - Optional stream type(s) to filter by * @param options.streamIdPattern - Optional pattern to match stream IDs * @param options.minPosition - Optional minimum stream position * @param options.maxPosition - Optional maximum stream position * @param options.fromDate - Optional starting date for time-based filtering * @param options.toDate - Optional ending date for time-based filtering * @param options.includeArchived - Whether to include archived streams * @param options.includeMetadata - Whether to include stream metadata * @param options.limit - Maximum number of streams to return * @param options.offset - Number of streams to skip * @param options.sortBy - Field to sort results by * @param options.sortDirection - Direction to sort results * @param options.includeCount - Whether to include total count in results * @returns Promise resolving to the query result with streams and pagination info * * @example * ```typescript * // Query all streams * const result = await eventStore.queryStreams(); * * // Query streams by type * const result = await eventStore.queryStreams({ * streamType: 'user' * }); * * // Query streams with a pattern match * const result = await eventStore.queryStreams({ * streamIdPattern: 'user-%' * }); * ``` */ async queryStreams(options = {}) { const url = `/api/event-stores/${this.eventStoreName}/streams`; const queryParams = {}; for (const [key, value] of Object.entries(options)) { if (value !== void 0) { if (Array.isArray(value)) { queryParams[key] = value.join(","); } else { queryParams[key] = String(value); } } } const response = await this.http.get( url, queryParams ); return { streams: (response.streams || []).map((stream) => ({ streamId: stream.streamId, streamPosition: stream.streamPosition, streamType: stream.streamType, streamMetadata: stream.streamMetadata || {}, isArchived: stream.isArchived, lastArchivedPosition: stream.lastArchivedPosition, createdAt: stream.createdAt, updatedAt: stream.updatedAt })), pagination: { limit: response.pagination.limit, offset: response.pagination.offset, total: response.pagination.total, hasMore: response.pagination.hasMore } }; } /** * Query streams of a specific type with filtering options * * @param streamType - The stream type to filter by * @param options - Query parameters for filtering streams * @param options.streamId - Optional specific stream ID to retrieve * @param options.streamIdPattern - Optional pattern to match stream IDs * @param options.minPosition - Optional minimum stream position * @param options.maxPosition - Optional maximum stream position * @param options.fromDate - Optional starting date for time-based filtering * @param options.toDate - Optional ending date for time-based filtering * @param options.includeArchived - Whether to include archived streams * @param options.includeMetadata - Whether to include stream metadata * @param options.limit - Maximum number of streams to return * @param options.offset - Number of streams to skip * @param options.sortBy - Field to sort results by * @param options.sortDirection - Direction to sort results * @param options.includeCount - Whether to include total count in results * @returns Promise resolving to the query result with streams and pagination info * * @example * ```typescript * // Query all user streams * const result = await eventStore.queryStreamsByType('user'); * * // Query user streams with pagination * const result = await eventStore.queryStreamsByType('user', { * limit: 20, * offset: 0 * }); * ``` */ async queryStreamsByType(streamType, options = {}) { return this.queryStreams({ ...options, streamType }); } /** * Get a list of stream IDs in an event store * * @param options - Optional parameters for listing streams * @param options.limit - Maximum number of stream IDs to return * @param options.offset - Number of stream IDs to skip * @param options.pattern - Pattern to match stream IDs (e.g., 'user-*') * @returns Promise resolving to an object containing stream IDs and total count * * @example * ```typescript * // List all streams * const { streams, total } = await eventStore.listStreams(); * * // List streams with pagination * const { streams, total } = await eventStore.listStreams({ * limit: 50, * offset: 100 * }); * * // List streams matching a pattern * const { streams, total } = await eventStore.listStreams({ * pattern: 'user-*' * }); * ``` */ async listStreams(options) { const params = {}; if (options?.limit) { params.limit = options.limit.toString(); } if (options?.offset) { params.offset = options.offset.toString(); } if (options?.pattern) { params.pattern = options.pattern; } return this.http.get( `/api/event-stores/${this.eventStoreName}/streams`, params ); } }; __name(_EventStore, "EventStore"); var EventStore = _EventStore; // src/clients/management.ts var _ManagementClient = class _ManagementClient { /** * Creates a new ManagementClient instance. * * @param http - The HTTP client to use for API requests */ constructor(http) { this.http = http; } /** * Creates a new event store in the DeltaBase platform. * * @param options - Configuration for the new event store * @returns Promise resolving to the created event store information * @throws {EventStoreAlreadyExistsError} When an event store with the same name exists * @throws {ValidationError} When request validation fails * @throws {AuthenticationError} When authentication fails * @throws {InternalServerError} When the server encounters an error * * @example * ```typescript * import { * isEventStoreAlreadyExistsError, * isValidationError * } from '@delta-base/server'; * * try { * const eventStore = await managementClient.createEventStore({ * name: 'orders-production', * description: 'Production event store for order events', * settings: { * retentionPeriodDays: 90, * maxStreamSizeBytes: 1073741824 // 1GB * } * }); * } catch (error) { * if (isEventStoreAlreadyExistsError(error)) { * console.log(`Event store '${error.name}' already exists`); * // Handle existing event store * } else if (isValidationError(error)) { * console.log('Validation errors:', error.validationErrors); * // Handle validation failures * } else { * throw error; // Re-throw unknown errors * } * } * ``` */ async createEventStore(options) { const apiResponse = await this.http.post( "/api/event-stores", options ); return { id: apiResponse.eventStoreId, name: apiResponse.eventStoreName, description: apiResponse.description, region: void 0, // API doesn't return region currently createdAt: apiResponse.created, updatedAt: apiResponse.created, // API doesn't return updatedAt, use created as fallback status: "active" // API doesn't return status, assume active for new stores }; } /** * Retrieves a list of all event stores accessible to the current user. * * @returns Promise resolving to a list of event stores and total count * * @example * ```typescript * // Get all available event stores * const { eventStores, totalCount } = await managementClient.listEventStores(); * * // Display event store names * eventStores.forEach(store => { * console.log(`${store.name} (${store.id}): ${store.status}`); * }); * ``` */ async listEventStores() { const apiResponse = await this.http.get("/api/event-stores"); return { eventStores: apiResponse.eventStores.map((store) => ({ id: store.eventStoreId, name: store.eventStoreName, description: store.description, region: void 0, // API doesn't return region currently createdAt: store.created, updatedAt: store.created, // API doesn't return updatedAt, use created as fallback status: "active" // API doesn't return status, assume active })), totalCount: apiResponse.totalCount }; } /** * Retrieves detailed information about a specific event store. * * @param eventStoreName - Name of the event store to retrieve * @returns Promise resolving to detailed information about the event store * * @example * ```typescript * // Get details for a specific event store * const eventStore = await managementClient.getEventStore('es_12345'); * * // Access statistics * if (eventStore.statistics) { * console.log(`Total events: ${eventStore.statistics.totalEvents}`); * console.log(`Storage used: ${eventStore.statistics.databaseSizeBytes} bytes`); * } * ``` */ async getEventStore(eventStoreName) { const apiResponse = await this.http.get( `/api/event-stores/${eventStoreName}` ); return { id: apiResponse.eventStoreId, name: apiResponse.eventStoreName, description: apiResponse.description, region: void 0, // API doesn't return region currently createdAt: apiResponse.created, updatedAt: apiResponse.created, // API doesn't return updatedAt, use created as fallback status: "active", // API doesn't return status, assume active statistics: apiResponse.statistics, settings: apiResponse.settings && apiResponse.settings.retentionPeriodDays !== void 0 && apiResponse.settings.maxStreamSizeBytes !== void 0 ? { retentionPeriodDays: apiResponse.settings.retentionPeriodDays, maxStreamSizeBytes: apiResponse.settings.maxStreamSizeBytes } : void 0 }; } /** * Permanently deletes an event store and all its data. * * This operation cannot be undone. All event streams, events, and * subscriptions associated with this event store will be deleted. * * @param eventStoreName - Name of the event store to delete * @returns Promise that resolves when the deletion is complete * * @example * ```typescript * // Delete an event store * await managementClient.deleteEventStore('es_12345'); * ``` */ async deleteEventStore(eventStoreName) { await this.http.delete(`/api/event-stores/${eventStoreName}`); } /** * Updates settings for an existing event store. * * @param eventStoreName - ID of the event store to update * @param settings - New settings for the event store * @param settings.description - Optional new description for the event store * @param settings.retentionPeriodDays - Optional new retention period in days * @param settings.maxStreamSizeBytes - Optional new maximum stream size in bytes * @returns Promise resolving to the updated event store information * * @example * ```typescript * // Update event store description * const updated = await managementClient.updateEventStore('es_12345', { * description: 'Updated description for this event store' * }); * * // Update event store retention settings * const updated = await managementClient.updateEventStore('es_12345', { * retentionPeriodDays: 180, // Keep events for 6 months * maxStreamSizeBytes: 536870912 // 512MB per stream * }); * ``` */ async updateEventStore(eventStoreName, settings) { const apiResponse = await this.http.put( `/api/event-stores/${eventStoreName}`, settings ); return { id: apiResponse.eventStoreId, name: apiResponse.eventStoreName, description: apiResponse.description, region: void 0, // API doesn't return region currently createdAt: apiResponse.created, updatedAt: apiResponse.created, // API doesn't return updatedAt, use created as fallback status: "active" // API doesn't return status, assume active }; } }; __name(_ManagementClient, "ManagementClient"); var ManagementClient = _ManagementClient; // src/utils/http.ts var _HttpClient = class _HttpClient { constructor(config) { this.baseUrl = config.baseUrl.replace(/\/$/, ""); this.apiKey = config.apiKey; this.customHeaders = config.headers; } /** * Perform a GET request */ async get(path, params) { const url = new URL(`${this.baseUrl}${path}`); if (params) { for (const [key, value] of Object.entries(params)) { url.searchParams.append(key, value); } } const response = await fetch(url.toString(), { method: "GET", headers: this.getHeaders() }); return this.handleResponse(response); } /** * Perform a POST request */ async post(path, body) { const response = await fetch(`${this.baseUrl}${path}`, { method: "POST", headers: this.getHeaders(), body: body ? JSON.stringify(body) : void 0 }); return this.handleResponse(response); } /** * Perform a PUT request */ async put(path, body) { const response = await fetch(`${this.baseUrl}${path}`, { method: "PUT", headers: this.getHeaders(), body: body ? JSON.stringify(body) : void 0 }); return this.handleResponse(response); } /** * Perform a DELETE request */ async delete(path) { const response = await fetch(`${this.baseUrl}${path}`, { method: "DELETE", headers: this.getHeaders() }); return this.handleResponse(response); } /** * Get common headers for all requests */ getHeaders() { const headers = { "Content-Type": "application/json", Accept: "application/json" }; if (this.apiKey) { headers["X-API-Key"] = this.apiKey; } if (this.customHeaders) { Object.assign(headers, this.customHeaders); } return headers; } /** * Handle API response and convert to typed errors */ async handleResponse(response) { const contentType = response.headers.get("content-type"); const isJson = contentType?.includes("application/json"); const body = isJson ? await response.json() : await response.text(); if (!response.ok) { throw this.createTypedError(response.status, body); } return body; } /** * Create a typed error based on status code and response body */ createTypedError(status, body) { if (typeof body === "object" && body !== null) { const errorBody = body; if (status === 409 && this.isVersionConflictError(errorBody)) { return new VersionConflictError( errorBody.details, body, errorBody.currentVersion, errorBody.expectedVersion ); } if (status === 400 && this.isValidationError(errorBody)) { return new ValidationError( "Request validation failed", body, errorBody.details ); } if (this.isGeneralError(errorBody)) { switch (errorBody.error) { case "EVENT_STORE_NOT_FOUND": return new EventStoreNotFoundError( errorBody.message, body, errorBody.details?.eventStoreId || "unknown" ); case "SUBSCRIPTION_NOT_FOUND": return new SubscriptionNotFoundError( errorBody.message, body, errorBody.details?.subscriptionId || "unknown", errorBody.details?.eventStoreId || "unknown" ); case "EVENT_STORE_ALREADY_EXISTS": return new EventStoreAlreadyExistsError( errorBody.message, body, errorBody.details?.name || "unknown" ); case "TIMEOUT_ERROR": return new TimeoutError( errorBody.message, body, errorBody.details?.timeoutMs || 0, errorBody.details?.operation || "unknown" ); case "STORAGE_ERROR": return new StorageError( errorBody.message, body, errorBody.details?.storageType || "sqlite", errorBody.details?.operation || "read" ); case "STREAM_NOT_FOUND": return new StreamNotFoundError( errorBody.message, body, errorBody.details?.streamId || "unknown", errorBody.details?.eventStoreId || "unknown" ); case "SERIALIZATION_ERROR": return new SerializationError( errorBody.message, body, errorBody.details?.dataType || "event", errorBody.details?.originalData ); case "INVALID_CALLBACK_URL": case "INVALID_EVENT_FILTER": case "INVALID_SUBSCRIPTION_CONFIG": return new InvalidSubscriptionConfigError( errorBody.message, body, errorBody.error ); case "INTERNAL_SERVER_ERROR": return new InternalServerError(errorBody.message, body); } } } switch (status) { case 401: return new AuthenticationError("Authentication failed", body); case 403: return new AuthorizationError("Authorization failed", body); case 404: if (typeof body === "object" && body !== null) { const errorBody = body; if (errorBody.error === "STREAM_NOT_FOUND") { return new StreamNotFoundError( "Stream not found", body, "unknown", "unknown" ); } } return new EventStoreNotFoundError( "Resource not found", body, "unknown" ); case 408: return new TimeoutError("Request timeout", body, 0, "unknown"); case 409: return new VersionConflictError("Conflict detected", body, -1, -1); case 429: { const retryAfter = typeof body === "object" && body !== null ? body.retryAfter : void 0; return new RateLimitError("Rate limit exceeded", body, retryAfter); } default: return new InternalServerError(`HTTP Error: ${status}`, body); } } /** * Type guard for version conflict error responses */ isVersionConflictError(body) { return body.error === "Version conflict" && typeof body.details === "string" && typeof body.currentVersion === "number" && typeof body.expectedVersion === "number"; } /** * Type guard for validation error responses */ isValidationError(body) { return body.error === "Invalid request body" && Array.isArray(body.details) && body.details.every( (detail) => typeof detail === "object" && detail !== null && typeof detail.code === "string" && typeof detail.message === "string" && Array.isArray(detail.path) ); } /** * Type guard for general error responses */ isGeneralError(body) { return typeof body.error === "string" && typeof body.message === "string"; } }; __name(_HttpClient, "HttpClient"); var HttpClient = _HttpClient; // src/deltabase.ts var _DeltaBase = class _DeltaBase { /** * Creates a new DeltaBase client instance. * * @param config - Configuration options for connecting to the Delta-Base platform * @throws {Error} If neither apiKey nor headers are provided in the configuration * * @example * ```typescript * // Create with API key (standard authentication) * const client = new DeltaBase({ apiKey: "your-api-key" }); * * // Create with custom URL (production) * const prodClient = new DeltaBase({ * baseUrl: "https://api.delta-base.com", * apiKey: "your-api-key" * }); * * // Create with custom headers (internal service authentication) * const internalClient = new DeltaBase({ * baseUrl: "https://api.delta-base.com", * headers: { * 'X-Deltabase-Internal-Service': 'auth-service', * 'X-Deltabase-Internal-Token': 'internal-token' * } * }); * ``` */ constructor(config) { if (!config.apiKey && !config.headers) { throw new Error("Either apiKey or headers must be provided"); } const httpConfig = { baseUrl: config.baseUrl || "http://localhost:8787", apiKey: config.apiKey, headers: config.headers }; this.http = new HttpClient(httpConfig); } /** * Get a ManagementClient for managing event stores * * @returns A ManagementClient instance for creating and managing event stores * @example * ```typescript * const management = client.getManagement(); * const eventStore = await management.createEventStore({ * name: "my-event-store" * }); * ``` */ getManagement() { return new ManagementClient(this.http); } /** * Get an EventStore client for a specific event store * * @param eventStoreName The name of the event store to connect to * @returns An EventStore client instance configured for the specified event store * @example * ```typescript * const eventStore = client.getEventStore("my-event-store"); * await eventStore.appendToStream("user-123", [ * { type: "UserCreated", data: { userId: "123", name: "John" } } * ]); * ``` */ getEventStore(eventStoreName) { return new EventStore(this.http, eventStoreName); } /** * Get an EventBus client for a specific event store * * @param eventStoreName The name of the event store to manage subscriptions for * @returns An EventBus client instance configured for the specified event store * @example * ```typescript * const eventBus = client.getEventBus("my-event-store"); * const subscription = await eventBus.createSubscription({ * name: "user-events", * eventFilter: "user.*" * }); * ``` */ getEventBus(eventStoreName) { return new EventBus(this.http, eventStoreName); } }; __name(_DeltaBase, "DeltaBase"); var DeltaBase = _DeltaBase; export { DeltaBase, EventBus, EventStore, HttpClient, ManagementClient, SubscriberType, SubscriptionStatus }; //# sourceMappingURL=index.mjs.map //# sourceMappingURL=index.mjs.map