@eventualize/relational-storage-adapter 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@eventualize/relational-storage-adapter",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/outsidenote/eventualize-js",
8
+ "directory": "packages/relational-storage-adapter"
9
+ },
5
10
  "main": "dist/index.js",
6
11
  "types": "dist/index.d.ts",
7
12
  "exports": {
@@ -9,7 +14,13 @@
9
14
  "import": "./dist/index.js",
10
15
  "types": "./dist/index.d.ts"
11
16
  },
12
- "./*": "./dist/*.js"
17
+ "./*": {
18
+ "import": "./dist/*.js",
19
+ "types": "./dist/*.d.ts"
20
+ }
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
13
24
  },
14
25
  "scripts": {
15
26
  "build": "tsc --build",
@@ -25,5 +36,8 @@
25
36
  "devDependencies": {
26
37
  "dotenv": "^17.2.3",
27
38
  "prisma": "^7.1.0"
28
- }
39
+ },
40
+ "files": [
41
+ "dist"
42
+ ]
29
43
  }
@@ -1,3 +0,0 @@
1
- import "dotenv/config";
2
- declare const _default: import("@prisma/config").PrismaConfigInternal;
3
- export default _default;
package/prisma.config.js DELETED
@@ -1,15 +0,0 @@
1
- // This file was generated by Prisma and assumes you have installed the following:
2
- // npm install --save-dev prisma dotenv
3
- import "dotenv/config";
4
- import { defineConfig, env } from "prisma/config";
5
- console.log('database url:', env("DATABASE_URL"));
6
- export default defineConfig({
7
- schema: "prisma/schema.prisma",
8
- migrations: {
9
- path: "prisma/migrations",
10
- },
11
- datasource: {
12
- url: env("DATABASE_URL"),
13
- },
14
- });
15
- //# sourceMappingURL=prisma.config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prisma.config.js","sourceRoot":"","sources":["prisma.config.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,uCAAuC;AACvC,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAA;AAEjD,eAAe,YAAY,CAAC;IAC1B,MAAM,EAAE,sBAAsB;IAC9B,UAAU,EAAE;QACV,IAAI,EAAE,mBAAmB;KAC1B;IACD,UAAU,EAAE;QACV,GAAG,EAAE,GAAG,CAAC,cAAc,CAAC;KACzB;CACF,CAAC,CAAC"}
package/prisma.config.ts DELETED
@@ -1,16 +0,0 @@
1
- // This file was generated by Prisma and assumes you have installed the following:
2
- // npm install --save-dev prisma dotenv
3
- import "dotenv/config";
4
- import { defineConfig, env } from "prisma/config";
5
-
6
- console.log('database url:', env("DATABASE_URL"))
7
-
8
- export default defineConfig({
9
- schema: "prisma/schema.prisma",
10
- migrations: {
11
- path: "prisma/migrations",
12
- },
13
- datasource: {
14
- url: env("DATABASE_URL"),
15
- },
16
- });
@@ -1,305 +0,0 @@
1
- import { IEvDbPayloadData } from '@eventualize/types/IEvDbEventPayload';
2
- import IEvDbEventMetadata from '@eventualize/types/IEvDbEventMetadata';
3
- import EvDbStreamCursor from '@eventualize/types/EvDbStreamCursor';
4
- import EvDbMessage from '@eventualize/types/EvDbMessage';
5
- import IEvDbStorageSnapshotAdapter from '@eventualize/types/IEvDbStorageSnapshotAdapter';
6
- import IEvDbStorageStreamAdapter from '@eventualize/types/IEvDbStorageStreamAdapter';
7
- import EvDbStreamAddress from '@eventualize/types/EvDbStreamAddress';
8
- import EvDbViewAddress from '@eventualize/types/EvDbViewAddress';
9
- import { EvDbStoredSnapshotResultRaw } from '@eventualize/types/EvDbStoredSnapshotResult';
10
- import { EvDbStoredSnapshotData } from '@eventualize/types/EvDbStoredSnapshotData';
11
- import EvDbEvent from '@eventualize/types/EvDbEvent';
12
- import StreamStoreAffected from '@eventualize/types/StreamStoreAffected';
13
- import EvDbContinuousFetchOptions from '@eventualize/types/EvDbContinuousFetchOptions';
14
- import EvDbMessageFilter from '@eventualize/types/EvDbMessageFilter';
15
- import { EvDbShardName } from '@eventualize/types/primitiveTypes';
16
-
17
- import { Prisma } from './generated/prisma/client.js';
18
- import { PrismaQueryProvider } from './EvDbRelationalStorageAdapterQueries.js';
19
- // import { eventsCreateManyInput } from './generated/prisma/models';
20
-
21
- // Type definitions for records
22
- export interface EvDbEventRecord extends IEvDbEventMetadata {
23
- id: string;
24
- payload: IEvDbPayloadData;
25
- }
26
-
27
- export interface EvDbSnapshotRecord {
28
- id: string;
29
- streamType: string;
30
- streamId: string;
31
- viewName: string;
32
- offset: bigint;
33
- state: IEvDbPayloadData;
34
- }
35
-
36
- export interface IEvDbOutboxTransformer {
37
- transform(message: EvDbMessage): EvDbMessage;
38
- }
39
-
40
- export interface EvDbStorageContext {
41
- schema?: string;
42
- shortId: string;
43
- id: string;
44
- }
45
-
46
- const serializePayload = (payload: IEvDbPayloadData) => Buffer.from(JSON.stringify(payload), 'utf-8');
47
- const deserializePayload = (payload: any): IEvDbPayloadData => {
48
- if (!!payload && typeof payload == 'object') {
49
- return payload;
50
- }
51
- return {};
52
- }
53
-
54
- /**
55
- * Prisma-based storage adapter for EvDb
56
- * Replaces SQL Server-specific adapter with database-agnostic Prisma implementation
57
- */
58
- export class EvDbPrismaStorageAdapter implements IEvDbStorageSnapshotAdapter, IEvDbStorageStreamAdapter {
59
- private readonly queryProvider: PrismaQueryProvider;
60
- protected readonly databaseType: string = 'prisma';
61
-
62
- constructor(
63
- private readonly prisma: any,
64
- ) {
65
- this.queryProvider = new PrismaQueryProvider(prisma);
66
- }
67
- getFromOutbox(filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null): Promise<AsyncIterable<EvDbMessage>> {
68
- throw new Error('Method not implemented.');
69
- }
70
- getFromOutboxAsync(shard: EvDbShardName, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null, cancellation?: AbortSignal): AsyncIterable<EvDbMessage> {
71
- throw new Error('Method not implemented.');
72
- }
73
- getRecordsFromOutboxAsync(filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null, cancellation?: AbortSignal): AsyncIterable<EvDbMessage>;
74
- getRecordsFromOutboxAsync(shard: EvDbShardName, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null, cancellation?: AbortSignal): AsyncIterable<EvDbMessage>;
75
- getRecordsFromOutboxAsync(shard: unknown, filter?: unknown, options?: unknown, cancellation?: unknown): AsyncIterable<EvDbMessage> {
76
- throw new Error('Method not implemented.');
77
- }
78
- subscribeToMessageAsync(handler: (message: EvDbMessage) => Promise<void>, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null): Promise<void>;
79
- subscribeToMessageAsync(handler: (message: EvDbMessage) => Promise<void>, shard: EvDbShardName, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null): Promise<void>;
80
- subscribeToMessageAsync(handler: unknown, shard: unknown, filter?: unknown, options?: unknown): Promise<void> {
81
- throw new Error('Method not implemented.');
82
- }
83
-
84
- /**
85
- * Store stream events in a transaction
86
- */
87
- async storeStreamAsync(
88
- events: ReadonlyArray<EvDbEvent>,
89
- messages: ReadonlyArray<EvDbMessage>,
90
- ): Promise<StreamStoreAffected> {
91
- try {
92
- const eventsToInsert: Prisma.eventsCreateInput[] = events.map((event) => {
93
- const streamCursor = event.streamCursor as EvDbStreamCursor;
94
- return {
95
- id: crypto.randomUUID(),
96
- stream_type: streamCursor.streamType,
97
- stream_id: streamCursor.streamId,
98
- offset: streamCursor.offset,
99
- event_type: event.eventType,
100
- captured_by: event.capturedBy,
101
- captured_at: event.capturedAt,
102
- payload: event.payload,
103
- }
104
- });
105
-
106
- const messagesToInsert: Prisma.outboxCreateInput[] = messages.map(message => {
107
- const streamCursor = message.streamCursor as EvDbStreamCursor;
108
- return {
109
- id: message.id,
110
- stream_type: streamCursor.streamType,
111
- stream_id: streamCursor.streamId,
112
- offset: streamCursor.offset,
113
- event_type: message.eventType,
114
- channel: message.channel,
115
- message_type: message.messageType,
116
- captured_by: message.capturedBy,
117
- captured_at: message.capturedAt,
118
- stored_at: message.storedAt,
119
- payload: message.payload,
120
- serialize_type: 'json',
121
- }
122
- })
123
-
124
- const storeEventsQuery = this.queryProvider.saveEvents(eventsToInsert);
125
- const storeMessagesQuery = this.queryProvider.saveMessages(messagesToInsert);
126
-
127
- const queryResult = await this.prisma.$transaction([
128
- storeEventsQuery,
129
- storeMessagesQuery
130
- ]);
131
-
132
- const numEvents = queryResult[0].count;
133
- const numMessages = queryResult[1].count;
134
- return new StreamStoreAffected(numEvents, numMessages);
135
- } catch (error) {
136
- if (this.isOccException(error)) {
137
- throw new Error('OPTIMISTIC_CONCURRENCY_VIOLATION');
138
- }
139
- throw error;
140
- }
141
- }
142
-
143
- /**
144
- * Store outbox messages in a transaction
145
- */
146
- async storeOutboxMessagesAsync(
147
- shardName: EvDbShardName,
148
- records: EvDbMessage[],
149
- ): Promise<number> {
150
- throw new Error('Method not implemented.');
151
- }
152
-
153
- /**
154
- * Get the last offset for a stream
155
- */
156
- async getLastOffsetAsync(
157
- streamAddress: EvDbStreamAddress
158
- ): Promise<number> {
159
- try {
160
- const { streamType, streamId } = streamAddress;
161
- const result = await this.queryProvider.getLastOffset(streamType, streamId);
162
- return Number(result?.offset ?? -1);
163
- } catch (error) {
164
- throw error;
165
- }
166
- }
167
-
168
- /**
169
- * Get events for a stream since a specific offset
170
- */
171
- async *getEventsAsync(
172
- streamCursor: EvDbStreamCursor,
173
- pageSize: number = 100
174
- ): AsyncGenerator<EvDbEvent, void, undefined> {
175
- const { streamType, streamId } = streamCursor;
176
- let currentOffset = streamCursor.offset;
177
- while (true) {
178
- try {
179
- const events = await this.queryProvider.getEvents(
180
- streamType,
181
- streamId,
182
- currentOffset
183
- );
184
-
185
- if (events.length === 0) {
186
- break;
187
- }
188
-
189
- for (const event of events) {
190
- yield new EvDbEvent(
191
- event.event_type,
192
- new EvDbStreamCursor(event.stream_type, event.stream_id, Number(event.offset)),
193
- { payloadType: event.event_type, payload: deserializePayload(event.payload) },
194
- event.captured_at,
195
- event.captured_by, event.stored_at
196
- );
197
- currentOffset = Math.max(currentOffset, Number(event.offset))
198
- }
199
-
200
- if (events.length < pageSize) {
201
- break; // Reached the end of the stream in the last page
202
- }
203
- } catch (error) {
204
- throw error;
205
- }
206
- }
207
- }
208
-
209
- /**
210
- * Get messages from outbox with optional filtering
211
- */
212
- async getMessagesAsync(
213
- shardName: EvDbShardName,
214
- sinceDate: Date,
215
- channels?: string[],
216
- messageTypes?: string[],
217
- cancellationToken?: AbortSignal
218
- ): Promise<EvDbMessage[]> {
219
- try {
220
- const tableName = this.getTableNameForShard(shardName);
221
- const messages = await this.queryProvider.getMessages(
222
- tableName,
223
- sinceDate,
224
- channels,
225
- messageTypes
226
- );
227
-
228
- return messages as EvDbMessage[];
229
- } catch (error) {
230
- throw error;
231
- }
232
- }
233
-
234
- /**
235
- * Get snapshot for a stream view
236
- */
237
- async getSnapshotAsync(
238
- viewAddress: EvDbViewAddress
239
- ): Promise<EvDbStoredSnapshotResultRaw> {
240
- const { streamType, streamId, viewName } = viewAddress;
241
- try {
242
- const snapshot = await this.queryProvider.getSnapshot(
243
- streamType,
244
- streamId,
245
- viewName
246
- );
247
-
248
- if (!snapshot) return EvDbStoredSnapshotResultRaw.Empty;
249
-
250
- return new EvDbStoredSnapshotResultRaw(
251
- Number(snapshot.offset),
252
- snapshot.stored_at,
253
- deserializePayload(snapshot.state),
254
- );
255
- } catch (error) {
256
- throw error;
257
- }
258
- }
259
-
260
- /**
261
- * Save a snapshot
262
- */
263
- async storeSnapshotAsync(record: EvDbStoredSnapshotData): Promise<void> {
264
- try {
265
- await this.queryProvider.saveSnapshot({
266
- id: record.id,
267
- stream_type: record.streamType,
268
- stream_id: record.streamId,
269
- view_name: record.viewName,
270
- offset: record.offset,
271
- state: record.state,
272
- stored_at: new Date(),
273
- });
274
-
275
- } catch (error) {
276
- throw error;
277
- }
278
- }
279
-
280
- /**
281
- * Check if an exception is an optimistic concurrency conflict
282
- */
283
- private isOccException(error: unknown): boolean {
284
- // P2002 = Unique constraint violation
285
- // P2034 = Transaction conflict
286
- const anyError = error as any;
287
- return !!error && anyError?.code === 'P2002' || anyError.code === 'P2034';
288
- }
289
-
290
- /**
291
- * Get table name for shard
292
- */
293
- private getTableNameForShard(shardName: EvDbShardName): string {
294
- throw new Error('Method not implemented.');
295
- }
296
-
297
- /**
298
- * Close the database connection
299
- */
300
- async close(): Promise<void> {
301
- await this.prisma.$disconnect();
302
- }
303
- }
304
-
305
- export default EvDbPrismaStorageAdapter;
@@ -1,27 +0,0 @@
1
- import IEvDbStorageAdmin from "@eventualize/types/IEvDbStorageAdmin";
2
-
3
-
4
- export default class EvDbPrismaStorageAdmin implements IEvDbStorageAdmin {
5
- constructor(private prisma: any) { }
6
- async clearEnvironmentAsync(): Promise<void> {
7
- await Promise.all([
8
- this.prisma.events.deleteMany({}),
9
- this.prisma.snapshot.deleteMany({}),
10
- this.prisma.outbox.deleteMany({}),
11
- ]);
12
- }
13
-
14
- createEnvironmentAsync(): Promise<void> {
15
- throw new Error("Method not implemented.");
16
- }
17
- destroyEnvironmentAsync(): Promise<void> {
18
- throw new Error('Method not implemented.');
19
- }
20
- async close(): Promise<void> {
21
- await this.prisma.$disconnect();
22
- }
23
- disposeAsync(): Promise<void> {
24
- throw new Error("Method not implemented.");
25
- }
26
-
27
- }
@@ -1,118 +0,0 @@
1
- import { PrismaClient, Prisma } from './generated/prisma/client.js';
2
-
3
- // ============================================================================
4
- // Prisma-based Query Methods (Type-safe alternatives to raw SQL)
5
- // ============================================================================
6
-
7
- export class PrismaQueryProvider {
8
- constructor(private prisma: PrismaClient) { }
9
-
10
- /**
11
- * Get the last offset for a stream using Prisma
12
- */
13
- async getLastOffset(stream_type: string, stream_id: string) {
14
- return this.prisma.events.findFirst({
15
- where: { stream_type, stream_id },
16
- select: { offset: true },
17
- orderBy: { offset: 'desc' },
18
- });
19
- }
20
-
21
- /**
22
- * Get events for a stream since an offset using Prisma
23
- */
24
- async getEvents(stream_type: string, stream_id: string, since_offset: number, pageSize: number = 100) {
25
- return this.prisma.events.findMany({
26
- where: {
27
- stream_type,
28
- stream_id,
29
- offset: { gte: since_offset },
30
- },
31
- select: {
32
- id: true,
33
- stream_type: true,
34
- stream_id: true,
35
- offset: true,
36
- event_type: true,
37
- captured_at: true,
38
- captured_by: true,
39
- payload: true,
40
- stored_at: true
41
- },
42
- take: pageSize
43
- });
44
- }
45
-
46
- /**
47
- * Get messages with filtering using Prisma
48
- */
49
- async getMessages(table_name: string, since_date: Date, channels?: string[], message_types?: string[]) {
50
- const now = new Date();
51
- const oneSecondAgo = new Date(now.getTime() - 1000);
52
-
53
- // Note: This assumes a dynamic table name approach
54
- // In Prisma, you'd typically use $queryRawUnsafe for dynamic table names
55
- const whereClause: any = {
56
- stored_at: { gte: since_date, lt: oneSecondAgo },
57
- };
58
-
59
- if (channels && channels.length > 0) {
60
- whereClause.channel = { in: channels };
61
- }
62
-
63
- if (message_types && message_types.length > 0) {
64
- whereClause.messageType = { in: message_types };
65
- }
66
-
67
- // This would need to be adapted based on your actual Prisma schema
68
- return this.prisma.$queryRawUnsafe(
69
- `SELECT * FROM ${table_name} WHERE stored_at >= $1 AND stored_at < $2`,
70
- since_date,
71
- oneSecondAgo
72
- );
73
- }
74
-
75
- /**
76
- * Save events in batch using Prisma
77
- */
78
- saveEvents(events: Array<Prisma.eventsCreateInput>) {
79
- return this.prisma.events.createMany({ data: events });
80
- }
81
-
82
- /**
83
- * Save messages in batch using Prisma
84
- */
85
- saveMessages(messages: Array<Prisma.outboxCreateInput>) {
86
- return this.prisma.outbox.createMany({ data: messages });
87
- }
88
-
89
- /**
90
- * Get snapshot using Prisma
91
- */
92
- async getSnapshot(streamType: string, streamId: string, viewName: string) {
93
- return this.prisma.snapshot.findFirst({
94
- where: {
95
- stream_type: streamType,
96
- stream_id: streamId,
97
- view_name: viewName,
98
- },
99
- select: {
100
- state: true,
101
- stored_at: true,
102
- offset: true,
103
- },
104
- orderBy: {
105
- offset: 'desc',
106
- },
107
- });
108
- }
109
-
110
- /**
111
- * Save snapshot using Prisma
112
- */
113
- async saveSnapshot(data: Prisma.snapshotCreateInput) {
114
- return this.prisma.snapshot.create({
115
- data,
116
- });
117
- }
118
- }
@@ -1,34 +0,0 @@
1
-
2
- /* !!! This is code generated by Prisma. Do not edit directly. !!! */
3
- /* eslint-disable */
4
- // biome-ignore-all lint: generated file
5
- // @ts-nocheck
6
- /*
7
- * This file should be your main import to use Prisma-related types and utilities in a browser.
8
- * Use it to get access to models, enums, and input types.
9
- *
10
- * This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
11
- * See `client.ts` for the standard, server-side entry point.
12
- *
13
- * 🟢 You can import this file directly.
14
- */
15
-
16
- import * as Prisma from './internal/prismaNamespaceBrowser.js'
17
- export { Prisma }
18
- export * as $Enums from './enums.js'
19
- export * from './enums.js';
20
- /**
21
- * Model events
22
- * This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
23
- */
24
- export type events = Prisma.eventsModel
25
- /**
26
- * Model outbox
27
- * This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
28
- */
29
- export type outbox = Prisma.outboxModel
30
- /**
31
- * Model snapshot
32
- * This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
33
- */
34
- export type snapshot = Prisma.snapshotModel
@@ -1,56 +0,0 @@
1
-
2
- /* !!! This is code generated by Prisma. Do not edit directly. !!! */
3
- /* eslint-disable */
4
- // biome-ignore-all lint: generated file
5
- // @ts-nocheck
6
- /*
7
- * This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
8
- * If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
9
- *
10
- * 🟢 You can import this file directly.
11
- */
12
-
13
- import * as process from 'node:process'
14
- import * as path from 'node:path'
15
- import { fileURLToPath } from 'node:url'
16
- globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
17
-
18
- import * as runtime from "@prisma/client/runtime/client"
19
- import * as $Enums from "./enums.js"
20
- import * as $Class from "./internal/class.js"
21
- import * as Prisma from "./internal/prismaNamespace.js"
22
-
23
- export * as $Enums from './enums.js'
24
- export * from "./enums.js"
25
- /**
26
- * ## Prisma Client
27
- *
28
- * Type-safe database client for TypeScript
29
- * @example
30
- * ```
31
- * const prisma = new PrismaClient()
32
- * // Fetch zero or more Events
33
- * const events = await prisma.events.findMany()
34
- * ```
35
- *
36
- * Read more in our [docs](https://pris.ly/d/client).
37
- */
38
- export const PrismaClient = $Class.getPrismaClientClass()
39
- export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
40
- export { Prisma }
41
-
42
- /**
43
- * Model events
44
- * This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
45
- */
46
- export type events = Prisma.eventsModel
47
- /**
48
- * Model outbox
49
- * This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
50
- */
51
- export type outbox = Prisma.outboxModel
52
- /**
53
- * Model snapshot
54
- * This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
55
- */
56
- export type snapshot = Prisma.snapshotModel