rebackend-drizzle 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -19,3 +19,4 @@ pnpm add rebackend rebackend-drizzle drizzle-orm farrow-schema
19
19
  ## Related Package
20
20
 
21
21
  - `rebackend`: core runtime, DDD/CQRS primitives, outbox, dispatcher, UoW
22
+ - `rebackend-farrow-http`: official Farrow HTTP adapter for request handling and context bridge
@@ -0,0 +1,4 @@
1
+ export * from './transaction';
2
+ export * from './repository';
3
+ export * from './outbox';
4
+ export * from './presets';
@@ -0,0 +1,90 @@
1
+ import type { InferInsertModel, InferSelectModel, Table } from 'drizzle-orm';
2
+ import type { EventOutbox, OutboxEntryInput, OutboxRecord, OutboxRecordStatus, OutboxSourceType } from 'rebackend';
3
+ export type DrizzleOutboxSelectRow<TTable extends Table> = InferSelectModel<TTable>;
4
+ export type DrizzleOutboxInsertRowShape<TTable extends Table> = InferInsertModel<TTable>;
5
+ export type DrizzleOutboxSelectKey<TTable extends Table> = Extract<keyof DrizzleOutboxSelectRow<TTable>, string>;
6
+ export type DrizzleOutboxColumnMap<TTable extends Table, TIdKey extends DrizzleOutboxSelectKey<TTable>, TEventNameKey extends DrizzleOutboxSelectKey<TTable>, TPayloadKey extends DrizzleOutboxSelectKey<TTable>, TSourceTypeKey extends DrizzleOutboxSelectKey<TTable>, TSourceNameKey extends DrizzleOutboxSelectKey<TTable>, TStatusKey extends DrizzleOutboxSelectKey<TTable>, TAttemptsKey extends DrizzleOutboxSelectKey<TTable>, TCreatedAtKey extends DrizzleOutboxSelectKey<TTable>, TDispatchedAtKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined, TLastErrorKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined> = {
7
+ id: TIdKey;
8
+ eventName: TEventNameKey;
9
+ payload: TPayloadKey;
10
+ sourceType: TSourceTypeKey;
11
+ sourceName: TSourceNameKey;
12
+ status: TStatusKey;
13
+ attempts: TAttemptsKey;
14
+ createdAt: TCreatedAtKey;
15
+ dispatchedAt?: TDispatchedAtKey;
16
+ lastError?: TLastErrorKey;
17
+ };
18
+ export type DrizzleOutboxInsertRow = {
19
+ eventName: string;
20
+ payload: string;
21
+ sourceType: OutboxSourceType;
22
+ sourceName: string;
23
+ status: OutboxRecordStatus;
24
+ attempts: number;
25
+ createdAt: Date;
26
+ dispatchedAt: Date | null;
27
+ lastError: string | null;
28
+ };
29
+ export type DrizzleOutboxStoredRow = DrizzleOutboxInsertRow & {
30
+ id: number;
31
+ };
32
+ export type DrizzleOutboxCodec<TRow, TInsertRow> = {
33
+ toInsertRow(entry: OutboxEntryInput, now: Date): TInsertRow;
34
+ fromStoredRow(row: TRow): OutboxRecord;
35
+ };
36
+ export declare const createJsonDrizzleOutboxCodec: () => DrizzleOutboxCodec<DrizzleOutboxStoredRow, DrizzleOutboxInsertRow>;
37
+ export type DrizzleOutboxOptions<TDb, TTransaction, TStoredRow, TInsertRow> = {
38
+ db: TDb;
39
+ codec: DrizzleOutboxCodec<TStoredRow, TInsertRow>;
40
+ insertRows: (executor: TDb | TTransaction, rows: readonly TInsertRow[]) => Promise<void>;
41
+ listRows: (executor: TDb | TTransaction) => Promise<readonly TStoredRow[]>;
42
+ listPendingRows: (executor: TDb | TTransaction, limit?: number) => Promise<readonly TStoredRow[]>;
43
+ markRowsDispatched: (executor: TDb | TTransaction, ids: readonly number[], dispatchedAt: Date) => Promise<void>;
44
+ markRowsFailed: (executor: TDb | TTransaction, ids: readonly number[], error: string) => Promise<void>;
45
+ };
46
+ export declare const createDrizzleOutbox: <TDb, TTransaction, TStoredRow, TInsertRow>(options: DrizzleOutboxOptions<TDb, TTransaction, TStoredRow, TInsertRow>) => EventOutbox;
47
+ export type DrizzleTableOutboxOptions<TDb, TTransaction, TTable extends Table, TIdKey extends DrizzleOutboxSelectKey<TTable>, TEventNameKey extends DrizzleOutboxSelectKey<TTable>, TPayloadKey extends DrizzleOutboxSelectKey<TTable>, TSourceTypeKey extends DrizzleOutboxSelectKey<TTable>, TSourceNameKey extends DrizzleOutboxSelectKey<TTable>, TStatusKey extends DrizzleOutboxSelectKey<TTable>, TAttemptsKey extends DrizzleOutboxSelectKey<TTable>, TCreatedAtKey extends DrizzleOutboxSelectKey<TTable>, TDispatchedAtKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined, TLastErrorKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined> = {
48
+ db: TDb;
49
+ table: TTable;
50
+ columns: DrizzleOutboxColumnMap<TTable, TIdKey, TEventNameKey, TPayloadKey, TSourceTypeKey, TSourceNameKey, TStatusKey, TAttemptsKey, TCreatedAtKey, TDispatchedAtKey, TLastErrorKey>;
51
+ operations: {
52
+ insertRows(input: {
53
+ executor: TDb | TTransaction;
54
+ table: TTable;
55
+ rows: readonly DrizzleOutboxInsertRowShape<TTable>[];
56
+ }): Promise<void>;
57
+ listRows(input: {
58
+ executor: TDb | TTransaction;
59
+ table: TTable;
60
+ }): Promise<readonly DrizzleOutboxSelectRow<TTable>[]>;
61
+ listPendingRows(input: {
62
+ executor: TDb | TTransaction;
63
+ table: TTable;
64
+ statusKey: TStatusKey;
65
+ pendingStatus: OutboxRecordStatus;
66
+ limit?: number;
67
+ }): Promise<readonly DrizzleOutboxSelectRow<TTable>[]>;
68
+ markRowsDispatched(input: {
69
+ executor: TDb | TTransaction;
70
+ table: TTable;
71
+ ids: readonly number[];
72
+ idKey: TIdKey;
73
+ statusKey: TStatusKey;
74
+ attemptsKey: TAttemptsKey;
75
+ dispatchedAtKey?: TDispatchedAtKey;
76
+ dispatchedAt: Date;
77
+ }): Promise<void>;
78
+ markRowsFailed(input: {
79
+ executor: TDb | TTransaction;
80
+ table: TTable;
81
+ ids: readonly number[];
82
+ idKey: TIdKey;
83
+ statusKey: TStatusKey;
84
+ attemptsKey: TAttemptsKey;
85
+ lastErrorKey?: TLastErrorKey;
86
+ error: string;
87
+ }): Promise<void>;
88
+ };
89
+ };
90
+ export declare const createDrizzleTableOutbox: <TDb, TTransaction, TTable extends Table, TIdKey extends DrizzleOutboxSelectKey<TTable>, TEventNameKey extends DrizzleOutboxSelectKey<TTable>, TPayloadKey extends DrizzleOutboxSelectKey<TTable>, TSourceTypeKey extends DrizzleOutboxSelectKey<TTable>, TSourceNameKey extends DrizzleOutboxSelectKey<TTable>, TStatusKey extends DrizzleOutboxSelectKey<TTable>, TAttemptsKey extends DrizzleOutboxSelectKey<TTable>, TCreatedAtKey extends DrizzleOutboxSelectKey<TTable>, TDispatchedAtKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined, TLastErrorKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined>(options: DrizzleTableOutboxOptions<TDb, TTransaction, TTable, TIdKey, TEventNameKey, TPayloadKey, TSourceTypeKey, TSourceNameKey, TStatusKey, TAttemptsKey, TCreatedAtKey, TDispatchedAtKey, TLastErrorKey>) => EventOutbox;
@@ -0,0 +1,36 @@
1
+ import { type InferSelectModel, type Table } from 'drizzle-orm';
2
+ import type { AggregateState, AnyAggregateDefinition } from 'rebackend';
3
+ import { type DrizzleSelectKey, type DrizzleTableAggregateRepositoryOptions } from './repository';
4
+ import { type DrizzleOutboxColumnMap } from './outbox';
5
+ type QueryRows<TRow> = PromiseLike<readonly TRow[]>;
6
+ type SelectWhereQuery<TRow> = QueryRows<TRow> & {
7
+ limit(limit: number): QueryRows<TRow>;
8
+ };
9
+ type SelectFromQuery<TTable extends Table> = QueryRows<InferSelectModel<TTable>> & {
10
+ where(condition: unknown): SelectWhereQuery<InferSelectModel<TTable>>;
11
+ };
12
+ export type BasicDrizzleTableExecutor<TTable extends Table> = {
13
+ select(): {
14
+ from(table: TTable): SelectFromQuery<TTable>;
15
+ };
16
+ insert(table: TTable): {
17
+ values(values: unknown): unknown;
18
+ };
19
+ update(table: TTable): {
20
+ set(values: Record<string, unknown>): {
21
+ where(condition: unknown): unknown;
22
+ };
23
+ };
24
+ };
25
+ export type DrizzleMutationSuccessJudge = (result: unknown) => boolean;
26
+ export type BasicDrizzleTableAggregateRepositoryOptions<TAggregate extends AnyAggregateDefinition, TDb extends BasicDrizzleTableExecutor<TTable>, TTransaction extends BasicDrizzleTableExecutor<TTable>, TTable extends Table, TIdentityRowKey extends DrizzleSelectKey<TTable>, TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>, TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined, TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined = undefined> = Omit<DrizzleTableAggregateRepositoryOptions<TAggregate, TDb, TTransaction, TTable, TIdentityRowKey, TIdentityStateKey, TVersionRowKey, TVersionStateKey>, 'operations'> & {
27
+ didUpdateSucceed: DrizzleMutationSuccessJudge;
28
+ };
29
+ export declare const createBasicDrizzleTableAggregateRepository: <TAggregate extends AnyAggregateDefinition, TDb extends BasicDrizzleTableExecutor<TTable>, TTransaction extends BasicDrizzleTableExecutor<TTable>, TTable extends Table, TIdentityRowKey extends DrizzleSelectKey<TTable>, TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>, TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined, TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined = undefined>(options: BasicDrizzleTableAggregateRepositoryOptions<TAggregate, TDb, TTransaction, TTable, TIdentityRowKey, TIdentityStateKey, TVersionRowKey, TVersionStateKey>) => import("rebackend").AggregateRepository<TAggregate>;
30
+ export type BasicDrizzleTableOutboxOptions<TDb extends BasicDrizzleTableExecutor<TTable>, _TTransaction extends BasicDrizzleTableExecutor<TTable>, TTable extends Table, TIdKey extends Extract<keyof InferSelectModel<TTable>, string>, TEventNameKey extends Extract<keyof InferSelectModel<TTable>, string>, TPayloadKey extends Extract<keyof InferSelectModel<TTable>, string>, TSourceTypeKey extends Extract<keyof InferSelectModel<TTable>, string>, TSourceNameKey extends Extract<keyof InferSelectModel<TTable>, string>, TStatusKey extends Extract<keyof InferSelectModel<TTable>, string>, TAttemptsKey extends Extract<keyof InferSelectModel<TTable>, string>, TCreatedAtKey extends Extract<keyof InferSelectModel<TTable>, string>, TDispatchedAtKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined, TLastErrorKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined> = {
31
+ db: TDb;
32
+ table: TTable;
33
+ columns: DrizzleOutboxColumnMap<TTable, TIdKey, TEventNameKey, TPayloadKey, TSourceTypeKey, TSourceNameKey, TStatusKey, TAttemptsKey, TCreatedAtKey, TDispatchedAtKey, TLastErrorKey>;
34
+ };
35
+ export declare const createBasicDrizzleTableOutbox: <TDb extends BasicDrizzleTableExecutor<TTable>, _TTransaction extends BasicDrizzleTableExecutor<TTable>, TTable extends Table, TIdKey extends Extract<keyof InferSelectModel<TTable>, string>, TEventNameKey extends Extract<keyof InferSelectModel<TTable>, string>, TPayloadKey extends Extract<keyof InferSelectModel<TTable>, string>, TSourceTypeKey extends Extract<keyof InferSelectModel<TTable>, string>, TSourceNameKey extends Extract<keyof InferSelectModel<TTable>, string>, TStatusKey extends Extract<keyof InferSelectModel<TTable>, string>, TAttemptsKey extends Extract<keyof InferSelectModel<TTable>, string>, TCreatedAtKey extends Extract<keyof InferSelectModel<TTable>, string>, TDispatchedAtKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined, TLastErrorKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined>(options: BasicDrizzleTableOutboxOptions<TDb, _TTransaction, TTable, TIdKey, TEventNameKey, TPayloadKey, TSourceTypeKey, TSourceNameKey, TStatusKey, TAttemptsKey, TCreatedAtKey, TDispatchedAtKey, TLastErrorKey>) => import("rebackend").EventOutbox;
36
+ export {};
@@ -0,0 +1,69 @@
1
+ import type { InferInsertModel, InferSelectModel, Table } from 'drizzle-orm';
2
+ import type { AggregateIdentity, AggregateRepository, AggregateState, AnyAggregateDefinition } from 'rebackend';
3
+ export type DrizzleSelectRow<TTable extends Table> = InferSelectModel<TTable>;
4
+ export type DrizzleInsertRow<TTable extends Table> = InferInsertModel<TTable>;
5
+ type KeysMatching<TRow, TValue> = Extract<{
6
+ [K in keyof TRow]-?: Exclude<TRow[K], null | undefined> extends TValue ? K : never;
7
+ }[keyof TRow], string>;
8
+ export type DrizzleSelectKeyMatching<TTable extends Table, TValue> = KeysMatching<DrizzleSelectRow<TTable>, TValue>;
9
+ export type DrizzleInsertKeyMatching<TTable extends Table, TValue> = KeysMatching<DrizzleInsertRow<TTable>, TValue>;
10
+ export type DrizzleSelectKey<TTable extends Table> = Extract<keyof DrizzleSelectRow<TTable>, string>;
11
+ export type DrizzleAggregateRepositoryOptions<TAggregate extends AnyAggregateDefinition, TDb, TTransaction, TRow> = {
12
+ db: TDb;
13
+ load: (executor: TDb | TTransaction, id: AggregateIdentity<TAggregate>) => Promise<TRow | null | undefined>;
14
+ insert: (executor: TDb | TTransaction, next: AggregateState<TAggregate>) => Promise<void>;
15
+ update: (input: {
16
+ executor: TDb | TTransaction;
17
+ next: AggregateState<TAggregate>;
18
+ current: AggregateState<TAggregate>;
19
+ optimisticLock: boolean;
20
+ }) => Promise<boolean>;
21
+ deserialize: (row: TRow) => AggregateState<TAggregate>;
22
+ };
23
+ export declare const createDrizzleAggregateRepository: <TAggregate extends AnyAggregateDefinition, TDb, TTransaction, TRow>(options: DrizzleAggregateRepositoryOptions<TAggregate, TDb, TTransaction, TRow>) => AggregateRepository<TAggregate>;
24
+ export type DrizzleTableAggregateRepositoryOptions<TAggregate extends AnyAggregateDefinition, TDb, TTransaction, TTable extends Table, TIdentityRowKey extends DrizzleSelectKey<TTable>, TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>, TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined, TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined = undefined> = {
25
+ db: TDb;
26
+ table: TTable;
27
+ identity: {
28
+ stateKey: TIdentityStateKey;
29
+ rowKey: TIdentityRowKey;
30
+ };
31
+ version?: {
32
+ stateKey: TVersionStateKey;
33
+ rowKey: TVersionRowKey;
34
+ };
35
+ serialize: {
36
+ insert(state: AggregateState<TAggregate>): DrizzleInsertRow<TTable>;
37
+ update(state: AggregateState<TAggregate>): Partial<DrizzleInsertRow<TTable>>;
38
+ };
39
+ deserialize(row: DrizzleSelectRow<TTable>): AggregateState<TAggregate>;
40
+ operations: {
41
+ findById(input: {
42
+ executor: TDb | TTransaction;
43
+ table: TTable;
44
+ rowKey: TIdentityRowKey;
45
+ id: AggregateIdentity<TAggregate>;
46
+ }): Promise<DrizzleSelectRow<TTable> | null | undefined>;
47
+ insertOne(input: {
48
+ executor: TDb | TTransaction;
49
+ table: TTable;
50
+ row: DrizzleInsertRow<TTable>;
51
+ }): Promise<void>;
52
+ updateById(input: {
53
+ executor: TDb | TTransaction;
54
+ table: TTable;
55
+ rowKey: TIdentityRowKey;
56
+ id: AggregateIdentity<TAggregate>;
57
+ row: Partial<DrizzleInsertRow<TTable>>;
58
+ optimisticLock: boolean;
59
+ expectedVersion?: number;
60
+ versionRowKey?: TVersionRowKey;
61
+ }): Promise<boolean>;
62
+ };
63
+ };
64
+ export declare const createDrizzleTableAggregateRepository: <TAggregate extends AnyAggregateDefinition, TDb, TTransaction, TTable extends Table, TIdentityRowKey extends DrizzleSelectKey<TTable>, TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>, TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined, TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined = undefined>(options: DrizzleTableAggregateRepositoryOptions<TAggregate, TDb, TTransaction, TTable, TIdentityRowKey, TIdentityStateKey, TVersionRowKey, TVersionStateKey>) => AggregateRepository<TAggregate>;
65
+ export type DrizzleVersionedAggregateRow = {
66
+ version?: number;
67
+ };
68
+ export declare const hasSameVersion: <TRow extends DrizzleVersionedAggregateRow, TState extends DrizzleVersionedAggregateRow>(row: TRow | null | undefined, state: TState) => boolean;
69
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { PersistenceContext, TransactionRunner, MaybePromise } from 'rebackend';
2
+ export type DrizzleTransactionCapable<TTransaction> = {
3
+ transaction<T>(transaction: (tx: TTransaction) => MaybePromise<T>): MaybePromise<T>;
4
+ };
5
+ export declare const createDrizzleTransactionRunner: <TDb extends DrizzleTransactionCapable<TTransaction>, TTransaction>(db: TDb) => TransactionRunner<TTransaction>;
6
+ export declare const resolveDrizzleExecutor: <TDb, TTransaction>(db: TDb, context?: PersistenceContext) => TDb | TTransaction;
@@ -0,0 +1 @@
1
+ export * from './drizzle';
package/package.json CHANGED
@@ -1,29 +1,27 @@
1
1
  {
2
2
  "name": "rebackend-drizzle",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Drizzle adapters for Rebackend.",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
7
- "types": "src/index.ts",
7
+ "types": "dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./src/index.ts",
10
+ "types": "./dist/index.d.ts",
11
11
  "require": "./dist/index.js",
12
12
  "default": "./dist/index.js"
13
13
  }
14
14
  },
15
15
  "files": [
16
- "dist",
17
- "src/index.ts",
18
- "src/drizzle"
16
+ "dist"
19
17
  ],
20
18
  "keywords": [
21
19
  "rebackend",
22
20
  "drizzle",
23
21
  "adapter"
24
22
  ],
25
- "author": "",
26
- "license": "ISC",
23
+ "author": "AisonSu <aisonsu@outlook.com>",
24
+ "license": "UNLICENSED",
27
25
  "publishConfig": {
28
26
  "access": "public"
29
27
  },
@@ -39,19 +37,22 @@
39
37
  "dependencies": {
40
38
  "drizzle-orm": "^0.45.2",
41
39
  "farrow-schema": "^2.3.3",
42
- "rebackend": "0.1.0"
40
+ "rebackend": "^0.1.1"
43
41
  },
44
42
  "devDependencies": {
45
43
  "@types/node": "^25.5.0",
46
44
  "@types/sql.js": "^1.4.11",
45
+ "rimraf": "^6.0.1",
47
46
  "sql.js": "^1.14.1",
48
47
  "tsx": "^4.21.0",
49
48
  "typescript": "^6.0.2"
50
49
  },
51
50
  "scripts": {
52
- "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
51
+ "clean": "rimraf dist",
52
+ "prebuild": "pnpm --filter rebackend build",
53
53
  "build": "pnpm run clean && tsc -p tsconfig.build.json",
54
54
  "pretest": "pnpm --filter rebackend build",
55
+ "pretypecheck": "pnpm --filter rebackend build",
55
56
  "predemo:drizzle-adapter": "pnpm --filter rebackend build",
56
57
  "predemo:drizzle-table-adapter": "pnpm --filter rebackend build",
57
58
  "predemo:drizzle-sqljs": "pnpm --filter rebackend build",
@@ -1,4 +0,0 @@
1
- export * from './transaction'
2
- export * from './repository'
3
- export * from './outbox'
4
- export * from './presets'
@@ -1,387 +0,0 @@
1
- import type { InferInsertModel, InferSelectModel, Table } from 'drizzle-orm'
2
-
3
- import type {
4
- AnyDomainEventAction,
5
- EventOutbox,
6
- OutboxEntryInput,
7
- OutboxRecord,
8
- OutboxRecordStatus,
9
- OutboxSourceType,
10
- } from 'rebackend'
11
-
12
- import { resolveDrizzleExecutor } from './transaction'
13
-
14
- export type DrizzleOutboxSelectRow<TTable extends Table> = InferSelectModel<TTable>
15
- export type DrizzleOutboxInsertRowShape<TTable extends Table> = InferInsertModel<TTable>
16
- export type DrizzleOutboxSelectKey<TTable extends Table> = Extract<
17
- keyof DrizzleOutboxSelectRow<TTable>,
18
- string
19
- >
20
-
21
- export type DrizzleOutboxColumnMap<
22
- TTable extends Table,
23
- TIdKey extends DrizzleOutboxSelectKey<TTable>,
24
- TEventNameKey extends DrizzleOutboxSelectKey<TTable>,
25
- TPayloadKey extends DrizzleOutboxSelectKey<TTable>,
26
- TSourceTypeKey extends DrizzleOutboxSelectKey<TTable>,
27
- TSourceNameKey extends DrizzleOutboxSelectKey<TTable>,
28
- TStatusKey extends DrizzleOutboxSelectKey<TTable>,
29
- TAttemptsKey extends DrizzleOutboxSelectKey<TTable>,
30
- TCreatedAtKey extends DrizzleOutboxSelectKey<TTable>,
31
- TDispatchedAtKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined,
32
- TLastErrorKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined,
33
- > = {
34
- id: TIdKey
35
- eventName: TEventNameKey
36
- payload: TPayloadKey
37
- sourceType: TSourceTypeKey
38
- sourceName: TSourceNameKey
39
- status: TStatusKey
40
- attempts: TAttemptsKey
41
- createdAt: TCreatedAtKey
42
- dispatchedAt?: TDispatchedAtKey
43
- lastError?: TLastErrorKey
44
- }
45
-
46
- export type DrizzleOutboxInsertRow = {
47
- eventName: string
48
- payload: string
49
- sourceType: OutboxSourceType
50
- sourceName: string
51
- status: OutboxRecordStatus
52
- attempts: number
53
- createdAt: Date
54
- dispatchedAt: Date | null
55
- lastError: string | null
56
- }
57
-
58
- export type DrizzleOutboxStoredRow = DrizzleOutboxInsertRow & {
59
- id: number
60
- }
61
-
62
- export type DrizzleOutboxCodec<TRow, TInsertRow> = {
63
- toInsertRow(entry: OutboxEntryInput, now: Date): TInsertRow
64
- fromStoredRow(row: TRow): OutboxRecord
65
- }
66
-
67
- export const createJsonDrizzleOutboxCodec = (): DrizzleOutboxCodec<
68
- DrizzleOutboxStoredRow,
69
- DrizzleOutboxInsertRow
70
- > => {
71
- return {
72
- toInsertRow(entry, now) {
73
- return {
74
- eventName: entry.event.name,
75
- payload: JSON.stringify({
76
- key: entry.event.key,
77
- version: entry.event.version,
78
- payload: entry.event.payload,
79
- }),
80
- sourceType: entry.sourceType,
81
- sourceName: entry.sourceName,
82
- status: 'pending',
83
- attempts: 0,
84
- createdAt: now,
85
- dispatchedAt: null,
86
- lastError: null,
87
- }
88
- },
89
- fromStoredRow(row) {
90
- const envelope = JSON.parse(row.payload) as {
91
- key: string
92
- version: number
93
- payload: AnyDomainEventAction['payload']
94
- }
95
-
96
- return {
97
- id: row.id,
98
- event: {
99
- kind: 'DomainEventAction',
100
- key: envelope.key,
101
- name: row.eventName,
102
- version: envelope.version,
103
- payload: envelope.payload,
104
- },
105
- sourceType: row.sourceType,
106
- sourceName: row.sourceName,
107
- status: row.status,
108
- attempts: row.attempts,
109
- createdAt: row.createdAt,
110
- dispatchedAt: row.dispatchedAt ?? undefined,
111
- lastError: row.lastError ?? undefined,
112
- }
113
- },
114
- }
115
- }
116
-
117
- export type DrizzleOutboxOptions<TDb, TTransaction, TStoredRow, TInsertRow> = {
118
- db: TDb
119
- codec: DrizzleOutboxCodec<TStoredRow, TInsertRow>
120
- insertRows: (executor: TDb | TTransaction, rows: readonly TInsertRow[]) => Promise<void>
121
- listRows: (executor: TDb | TTransaction) => Promise<readonly TStoredRow[]>
122
- listPendingRows: (executor: TDb | TTransaction, limit?: number) => Promise<readonly TStoredRow[]>
123
- markRowsDispatched: (
124
- executor: TDb | TTransaction,
125
- ids: readonly number[],
126
- dispatchedAt: Date,
127
- ) => Promise<void>
128
- markRowsFailed: (
129
- executor: TDb | TTransaction,
130
- ids: readonly number[],
131
- error: string,
132
- ) => Promise<void>
133
- }
134
-
135
- export const createDrizzleOutbox = <TDb, TTransaction, TStoredRow, TInsertRow>(
136
- options: DrizzleOutboxOptions<TDb, TTransaction, TStoredRow, TInsertRow>,
137
- ): EventOutbox => {
138
- return {
139
- async append(entries, context) {
140
- if (entries.length === 0) {
141
- return
142
- }
143
-
144
- const executor = resolveDrizzleExecutor<TDb, TTransaction>(options.db, context)
145
- const now = new Date()
146
- const rows = entries.map((entry) => options.codec.toInsertRow(entry, now))
147
-
148
- await options.insertRows(executor, rows)
149
- },
150
- async list() {
151
- const executor = resolveDrizzleExecutor<TDb, TTransaction>(options.db)
152
- const rows = await options.listRows(executor)
153
-
154
- return rows.map((row) => options.codec.fromStoredRow(row))
155
- },
156
- async listPending(limit) {
157
- const executor = resolveDrizzleExecutor<TDb, TTransaction>(options.db)
158
- const rows = await options.listPendingRows(executor, limit)
159
-
160
- return rows.map((row) => options.codec.fromStoredRow(row))
161
- },
162
- async markDispatched(ids) {
163
- if (ids.length === 0) {
164
- return
165
- }
166
-
167
- const executor = resolveDrizzleExecutor<TDb, TTransaction>(options.db)
168
- await options.markRowsDispatched(executor, ids, new Date())
169
- },
170
- async markFailed(ids, error) {
171
- if (ids.length === 0) {
172
- return
173
- }
174
-
175
- const executor = resolveDrizzleExecutor<TDb, TTransaction>(options.db)
176
- await options.markRowsFailed(executor, ids, error)
177
- },
178
- }
179
- }
180
-
181
- export type DrizzleTableOutboxOptions<
182
- TDb,
183
- TTransaction,
184
- TTable extends Table,
185
- TIdKey extends DrizzleOutboxSelectKey<TTable>,
186
- TEventNameKey extends DrizzleOutboxSelectKey<TTable>,
187
- TPayloadKey extends DrizzleOutboxSelectKey<TTable>,
188
- TSourceTypeKey extends DrizzleOutboxSelectKey<TTable>,
189
- TSourceNameKey extends DrizzleOutboxSelectKey<TTable>,
190
- TStatusKey extends DrizzleOutboxSelectKey<TTable>,
191
- TAttemptsKey extends DrizzleOutboxSelectKey<TTable>,
192
- TCreatedAtKey extends DrizzleOutboxSelectKey<TTable>,
193
- TDispatchedAtKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined,
194
- TLastErrorKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined,
195
- > = {
196
- db: TDb
197
- table: TTable
198
- columns: DrizzleOutboxColumnMap<
199
- TTable,
200
- TIdKey,
201
- TEventNameKey,
202
- TPayloadKey,
203
- TSourceTypeKey,
204
- TSourceNameKey,
205
- TStatusKey,
206
- TAttemptsKey,
207
- TCreatedAtKey,
208
- TDispatchedAtKey,
209
- TLastErrorKey
210
- >
211
- operations: {
212
- insertRows(input: {
213
- executor: TDb | TTransaction
214
- table: TTable
215
- rows: readonly DrizzleOutboxInsertRowShape<TTable>[]
216
- }): Promise<void>
217
- listRows(input: {
218
- executor: TDb | TTransaction
219
- table: TTable
220
- }): Promise<readonly DrizzleOutboxSelectRow<TTable>[]>
221
- listPendingRows(input: {
222
- executor: TDb | TTransaction
223
- table: TTable
224
- statusKey: TStatusKey
225
- pendingStatus: OutboxRecordStatus
226
- limit?: number
227
- }): Promise<readonly DrizzleOutboxSelectRow<TTable>[]>
228
- markRowsDispatched(input: {
229
- executor: TDb | TTransaction
230
- table: TTable
231
- ids: readonly number[]
232
- idKey: TIdKey
233
- statusKey: TStatusKey
234
- attemptsKey: TAttemptsKey
235
- dispatchedAtKey?: TDispatchedAtKey
236
- dispatchedAt: Date
237
- }): Promise<void>
238
- markRowsFailed(input: {
239
- executor: TDb | TTransaction
240
- table: TTable
241
- ids: readonly number[]
242
- idKey: TIdKey
243
- statusKey: TStatusKey
244
- attemptsKey: TAttemptsKey
245
- lastErrorKey?: TLastErrorKey
246
- error: string
247
- }): Promise<void>
248
- }
249
- }
250
-
251
- export const createDrizzleTableOutbox = <
252
- TDb,
253
- TTransaction,
254
- TTable extends Table,
255
- TIdKey extends DrizzleOutboxSelectKey<TTable>,
256
- TEventNameKey extends DrizzleOutboxSelectKey<TTable>,
257
- TPayloadKey extends DrizzleOutboxSelectKey<TTable>,
258
- TSourceTypeKey extends DrizzleOutboxSelectKey<TTable>,
259
- TSourceNameKey extends DrizzleOutboxSelectKey<TTable>,
260
- TStatusKey extends DrizzleOutboxSelectKey<TTable>,
261
- TAttemptsKey extends DrizzleOutboxSelectKey<TTable>,
262
- TCreatedAtKey extends DrizzleOutboxSelectKey<TTable>,
263
- TDispatchedAtKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined,
264
- TLastErrorKey extends DrizzleOutboxSelectKey<TTable> | undefined = undefined,
265
- >(
266
- options: DrizzleTableOutboxOptions<
267
- TDb,
268
- TTransaction,
269
- TTable,
270
- TIdKey,
271
- TEventNameKey,
272
- TPayloadKey,
273
- TSourceTypeKey,
274
- TSourceNameKey,
275
- TStatusKey,
276
- TAttemptsKey,
277
- TCreatedAtKey,
278
- TDispatchedAtKey,
279
- TLastErrorKey
280
- >,
281
- ): EventOutbox => {
282
- const codec: DrizzleOutboxCodec<
283
- DrizzleOutboxSelectRow<TTable>,
284
- DrizzleOutboxInsertRowShape<TTable>
285
- > = {
286
- toInsertRow(entry, now) {
287
- const row = {
288
- [options.columns.eventName]: entry.event.name,
289
- [options.columns.payload]: JSON.stringify({
290
- key: entry.event.key,
291
- version: entry.event.version,
292
- payload: entry.event.payload,
293
- }),
294
- [options.columns.sourceType]: entry.sourceType,
295
- [options.columns.sourceName]: entry.sourceName,
296
- [options.columns.status]: 'pending',
297
- [options.columns.attempts]: 0,
298
- [options.columns.createdAt]: now,
299
- }
300
-
301
- return row as DrizzleOutboxInsertRowShape<TTable>
302
- },
303
- fromStoredRow(row) {
304
- const dispatchedAtKey = options.columns.dispatchedAt
305
- const lastErrorKey = options.columns.lastError
306
- const rowRecord = row as Record<string, unknown>
307
- const envelope = JSON.parse(row[options.columns.payload] as string) as {
308
- key: string
309
- version: number
310
- payload: AnyDomainEventAction['payload']
311
- }
312
-
313
- return {
314
- id: row[options.columns.id] as number,
315
- event: {
316
- kind: 'DomainEventAction',
317
- key: envelope.key,
318
- name: row[options.columns.eventName] as string,
319
- version: envelope.version,
320
- payload: envelope.payload,
321
- },
322
- sourceType: row[options.columns.sourceType] as OutboxSourceType,
323
- sourceName: row[options.columns.sourceName] as string,
324
- status: row[options.columns.status] as OutboxRecordStatus,
325
- attempts: row[options.columns.attempts] as number,
326
- createdAt: row[options.columns.createdAt] as OutboxRecord['createdAt'],
327
- dispatchedAt: dispatchedAtKey
328
- ? (rowRecord[dispatchedAtKey] as OutboxRecord['dispatchedAt'])
329
- : undefined,
330
- lastError: lastErrorKey
331
- ? (rowRecord[lastErrorKey] as OutboxRecord['lastError'])
332
- : undefined,
333
- }
334
- },
335
- }
336
-
337
- return createDrizzleOutbox<
338
- TDb,
339
- TTransaction,
340
- DrizzleOutboxSelectRow<TTable>,
341
- DrizzleOutboxInsertRowShape<TTable>
342
- >({
343
- db: options.db,
344
- codec,
345
- insertRows: (executor, rows) =>
346
- options.operations.insertRows({
347
- executor,
348
- table: options.table,
349
- rows,
350
- }),
351
- listRows: (executor) =>
352
- options.operations.listRows({
353
- executor,
354
- table: options.table,
355
- }),
356
- listPendingRows: (executor, limit) =>
357
- options.operations.listPendingRows({
358
- executor,
359
- table: options.table,
360
- statusKey: options.columns.status,
361
- pendingStatus: 'pending',
362
- limit,
363
- }),
364
- markRowsDispatched: (executor, ids, dispatchedAt) =>
365
- options.operations.markRowsDispatched({
366
- executor,
367
- table: options.table,
368
- ids,
369
- idKey: options.columns.id,
370
- statusKey: options.columns.status,
371
- attemptsKey: options.columns.attempts,
372
- dispatchedAtKey: options.columns.dispatchedAt,
373
- dispatchedAt,
374
- }),
375
- markRowsFailed: (executor, ids, error) =>
376
- options.operations.markRowsFailed({
377
- executor,
378
- table: options.table,
379
- ids,
380
- idKey: options.columns.id,
381
- statusKey: options.columns.status,
382
- attemptsKey: options.columns.attempts,
383
- lastErrorKey: options.columns.lastError,
384
- error,
385
- }),
386
- })
387
- }
@@ -1,274 +0,0 @@
1
- import {
2
- and,
3
- eq,
4
- getTableColumns,
5
- inArray,
6
- sql,
7
- type InferSelectModel,
8
- type Table,
9
- } from 'drizzle-orm'
10
-
11
- import type { AggregateState, AnyAggregateDefinition } from 'rebackend'
12
-
13
- import {
14
- createDrizzleTableAggregateRepository,
15
- type DrizzleSelectKey,
16
- type DrizzleTableAggregateRepositoryOptions,
17
- } from './repository'
18
- import {
19
- createDrizzleTableOutbox,
20
- type DrizzleOutboxColumnMap,
21
- type DrizzleTableOutboxOptions,
22
- } from './outbox'
23
-
24
- type QueryRows<TRow> = PromiseLike<readonly TRow[]>
25
-
26
- type SelectWhereQuery<TRow> = QueryRows<TRow> & {
27
- limit(limit: number): QueryRows<TRow>
28
- }
29
-
30
- type SelectFromQuery<TTable extends Table> = QueryRows<InferSelectModel<TTable>> & {
31
- where(condition: unknown): SelectWhereQuery<InferSelectModel<TTable>>
32
- }
33
-
34
- export type BasicDrizzleTableExecutor<TTable extends Table> = {
35
- select(): {
36
- from(table: TTable): SelectFromQuery<TTable>
37
- }
38
- insert(table: TTable): {
39
- values(values: unknown): unknown
40
- }
41
- update(table: TTable): {
42
- set(values: Record<string, unknown>): {
43
- where(condition: unknown): unknown
44
- }
45
- }
46
- }
47
-
48
- export type DrizzleMutationSuccessJudge = (result: unknown) => boolean
49
-
50
- export type BasicDrizzleTableAggregateRepositoryOptions<
51
- TAggregate extends AnyAggregateDefinition,
52
- TDb extends BasicDrizzleTableExecutor<TTable>,
53
- TTransaction extends BasicDrizzleTableExecutor<TTable>,
54
- TTable extends Table,
55
- TIdentityRowKey extends DrizzleSelectKey<TTable>,
56
- TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>,
57
- TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined,
58
- TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined =
59
- undefined,
60
- > = Omit<
61
- DrizzleTableAggregateRepositoryOptions<
62
- TAggregate,
63
- TDb,
64
- TTransaction,
65
- TTable,
66
- TIdentityRowKey,
67
- TIdentityStateKey,
68
- TVersionRowKey,
69
- TVersionStateKey
70
- >,
71
- 'operations'
72
- > & {
73
- didUpdateSucceed: DrizzleMutationSuccessJudge
74
- }
75
-
76
- export const createBasicDrizzleTableAggregateRepository = <
77
- TAggregate extends AnyAggregateDefinition,
78
- TDb extends BasicDrizzleTableExecutor<TTable>,
79
- TTransaction extends BasicDrizzleTableExecutor<TTable>,
80
- TTable extends Table,
81
- TIdentityRowKey extends DrizzleSelectKey<TTable>,
82
- TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>,
83
- TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined,
84
- TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined =
85
- undefined,
86
- >(
87
- options: BasicDrizzleTableAggregateRepositoryOptions<
88
- TAggregate,
89
- TDb,
90
- TTransaction,
91
- TTable,
92
- TIdentityRowKey,
93
- TIdentityStateKey,
94
- TVersionRowKey,
95
- TVersionStateKey
96
- >,
97
- ) => {
98
- const columns = getTableColumns(options.table)
99
- const identityColumn = columns[options.identity.rowKey]
100
- const versionRowKey = options.version?.rowKey
101
- const versionColumn = versionRowKey ? columns[versionRowKey] : undefined
102
-
103
- const repositoryOptions: DrizzleTableAggregateRepositoryOptions<
104
- TAggregate,
105
- TDb,
106
- TTransaction,
107
- TTable,
108
- TIdentityRowKey,
109
- TIdentityStateKey,
110
- TVersionRowKey,
111
- TVersionStateKey
112
- > = {
113
- ...options,
114
- operations: {
115
- async findById({ executor, table, id }) {
116
- const rows = await executor.select().from(table).where(eq(identityColumn, id)).limit(1)
117
- return rows[0] ?? null
118
- },
119
- async insertOne({ executor, table, row }) {
120
- await executor.insert(table).values(row)
121
- },
122
- async updateById({ executor, table, id, row, optimisticLock, expectedVersion }) {
123
- const whereCondition =
124
- optimisticLock && versionColumn && expectedVersion !== undefined
125
- ? and(eq(identityColumn, id), eq(versionColumn, expectedVersion))
126
- : eq(identityColumn, id)
127
-
128
- const result = await executor.update(table).set(row).where(whereCondition)
129
- return options.didUpdateSucceed(result)
130
- },
131
- },
132
- }
133
-
134
- return createDrizzleTableAggregateRepository(repositoryOptions)
135
- }
136
-
137
- export type BasicDrizzleTableOutboxOptions<
138
- TDb extends BasicDrizzleTableExecutor<TTable>,
139
- _TTransaction extends BasicDrizzleTableExecutor<TTable>,
140
- TTable extends Table,
141
- TIdKey extends Extract<keyof InferSelectModel<TTable>, string>,
142
- TEventNameKey extends Extract<keyof InferSelectModel<TTable>, string>,
143
- TPayloadKey extends Extract<keyof InferSelectModel<TTable>, string>,
144
- TSourceTypeKey extends Extract<keyof InferSelectModel<TTable>, string>,
145
- TSourceNameKey extends Extract<keyof InferSelectModel<TTable>, string>,
146
- TStatusKey extends Extract<keyof InferSelectModel<TTable>, string>,
147
- TAttemptsKey extends Extract<keyof InferSelectModel<TTable>, string>,
148
- TCreatedAtKey extends Extract<keyof InferSelectModel<TTable>, string>,
149
- TDispatchedAtKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined,
150
- TLastErrorKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined,
151
- > = {
152
- db: TDb
153
- table: TTable
154
- columns: DrizzleOutboxColumnMap<
155
- TTable,
156
- TIdKey,
157
- TEventNameKey,
158
- TPayloadKey,
159
- TSourceTypeKey,
160
- TSourceNameKey,
161
- TStatusKey,
162
- TAttemptsKey,
163
- TCreatedAtKey,
164
- TDispatchedAtKey,
165
- TLastErrorKey
166
- >
167
- }
168
-
169
- export const createBasicDrizzleTableOutbox = <
170
- TDb extends BasicDrizzleTableExecutor<TTable>,
171
- _TTransaction extends BasicDrizzleTableExecutor<TTable>,
172
- TTable extends Table,
173
- TIdKey extends Extract<keyof InferSelectModel<TTable>, string>,
174
- TEventNameKey extends Extract<keyof InferSelectModel<TTable>, string>,
175
- TPayloadKey extends Extract<keyof InferSelectModel<TTable>, string>,
176
- TSourceTypeKey extends Extract<keyof InferSelectModel<TTable>, string>,
177
- TSourceNameKey extends Extract<keyof InferSelectModel<TTable>, string>,
178
- TStatusKey extends Extract<keyof InferSelectModel<TTable>, string>,
179
- TAttemptsKey extends Extract<keyof InferSelectModel<TTable>, string>,
180
- TCreatedAtKey extends Extract<keyof InferSelectModel<TTable>, string>,
181
- TDispatchedAtKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined,
182
- TLastErrorKey extends Extract<keyof InferSelectModel<TTable>, string> | undefined = undefined,
183
- >(
184
- options: BasicDrizzleTableOutboxOptions<
185
- TDb,
186
- _TTransaction,
187
- TTable,
188
- TIdKey,
189
- TEventNameKey,
190
- TPayloadKey,
191
- TSourceTypeKey,
192
- TSourceNameKey,
193
- TStatusKey,
194
- TAttemptsKey,
195
- TCreatedAtKey,
196
- TDispatchedAtKey,
197
- TLastErrorKey
198
- >,
199
- ) => {
200
- const columns = getTableColumns(options.table)
201
- const idColumn = columns[options.columns.id]
202
- const attemptsColumn = columns[options.columns.attempts]
203
- const statusColumn = columns[options.columns.status]
204
-
205
- const outboxOptions: DrizzleTableOutboxOptions<
206
- TDb,
207
- _TTransaction,
208
- TTable,
209
- TIdKey,
210
- TEventNameKey,
211
- TPayloadKey,
212
- TSourceTypeKey,
213
- TSourceNameKey,
214
- TStatusKey,
215
- TAttemptsKey,
216
- TCreatedAtKey,
217
- TDispatchedAtKey,
218
- TLastErrorKey
219
- > = {
220
- ...options,
221
- operations: {
222
- async insertRows({ executor, table, rows }) {
223
- await executor.insert(table).values(rows)
224
- },
225
- async listRows({ executor, table }) {
226
- return executor.select().from(table)
227
- },
228
- async listPendingRows({ executor, table, pendingStatus, limit }) {
229
- const query = executor.select().from(table).where(eq(statusColumn, pendingStatus))
230
- return limit === undefined ? query : query.limit(limit)
231
- },
232
- async markRowsDispatched({
233
- executor,
234
- table,
235
- ids,
236
- statusKey,
237
- attemptsKey,
238
- dispatchedAtKey,
239
- dispatchedAt,
240
- }) {
241
- const updateRow: Record<string, unknown> = {
242
- [statusKey]: 'dispatched',
243
- [attemptsKey]: sql`${attemptsColumn} + 1`,
244
- }
245
-
246
- if (dispatchedAtKey) {
247
- updateRow[dispatchedAtKey] = dispatchedAt
248
- }
249
-
250
- await executor
251
- .update(table)
252
- .set(updateRow)
253
- .where(inArray(idColumn, [...ids]))
254
- },
255
- async markRowsFailed({ executor, table, ids, statusKey, attemptsKey, lastErrorKey, error }) {
256
- const updateRow: Record<string, unknown> = {
257
- [statusKey]: 'failed',
258
- [attemptsKey]: sql`${attemptsColumn} + 1`,
259
- }
260
-
261
- if (lastErrorKey) {
262
- updateRow[lastErrorKey] = error
263
- }
264
-
265
- await executor
266
- .update(table)
267
- .set(updateRow)
268
- .where(inArray(idColumn, [...ids]))
269
- },
270
- },
271
- }
272
-
273
- return createDrizzleTableOutbox(outboxOptions)
274
- }
@@ -1,221 +0,0 @@
1
- import type { InferInsertModel, InferSelectModel, Table } from 'drizzle-orm'
2
-
3
- import type {
4
- AggregateIdentity,
5
- AggregateRepository,
6
- AggregateState,
7
- AnyAggregateDefinition,
8
- } from 'rebackend'
9
-
10
- import { resolveDrizzleExecutor } from './transaction'
11
-
12
- export type DrizzleSelectRow<TTable extends Table> = InferSelectModel<TTable>
13
- export type DrizzleInsertRow<TTable extends Table> = InferInsertModel<TTable>
14
-
15
- type KeysMatching<TRow, TValue> = Extract<
16
- {
17
- [K in keyof TRow]-?: Exclude<TRow[K], null | undefined> extends TValue ? K : never
18
- }[keyof TRow],
19
- string
20
- >
21
-
22
- export type DrizzleSelectKeyMatching<TTable extends Table, TValue> = KeysMatching<
23
- DrizzleSelectRow<TTable>,
24
- TValue
25
- >
26
- export type DrizzleInsertKeyMatching<TTable extends Table, TValue> = KeysMatching<
27
- DrizzleInsertRow<TTable>,
28
- TValue
29
- >
30
- export type DrizzleSelectKey<TTable extends Table> = Extract<keyof DrizzleSelectRow<TTable>, string>
31
-
32
- export type DrizzleAggregateRepositoryOptions<
33
- TAggregate extends AnyAggregateDefinition,
34
- TDb,
35
- TTransaction,
36
- TRow,
37
- > = {
38
- db: TDb
39
- load: (
40
- executor: TDb | TTransaction,
41
- id: AggregateIdentity<TAggregate>,
42
- ) => Promise<TRow | null | undefined>
43
- insert: (executor: TDb | TTransaction, next: AggregateState<TAggregate>) => Promise<void>
44
- update: (input: {
45
- executor: TDb | TTransaction
46
- next: AggregateState<TAggregate>
47
- current: AggregateState<TAggregate>
48
- optimisticLock: boolean
49
- }) => Promise<boolean>
50
- deserialize: (row: TRow) => AggregateState<TAggregate>
51
- }
52
-
53
- export const createDrizzleAggregateRepository = <
54
- TAggregate extends AnyAggregateDefinition,
55
- TDb,
56
- TTransaction,
57
- TRow,
58
- >(
59
- options: DrizzleAggregateRepositoryOptions<TAggregate, TDb, TTransaction, TRow>,
60
- ): AggregateRepository<TAggregate> => {
61
- return {
62
- async findById(id, context) {
63
- const executor = resolveDrizzleExecutor<TDb, TTransaction>(options.db, context)
64
- const row = await options.load(executor, id)
65
-
66
- if (row === null || row === undefined) {
67
- return null
68
- }
69
-
70
- return options.deserialize(row)
71
- },
72
- async save(input, context) {
73
- const executor = resolveDrizzleExecutor<TDb, TTransaction>(options.db, context)
74
-
75
- if (input.mode === 'create') {
76
- await options.insert(executor, input.next)
77
- return
78
- }
79
-
80
- if (input.current === null) {
81
- throw new Error('Drizzle aggregate update requires current aggregate state.')
82
- }
83
-
84
- return options.update({
85
- executor,
86
- next: input.next,
87
- current: input.current,
88
- optimisticLock: input.optimisticLock,
89
- })
90
- },
91
- }
92
- }
93
-
94
- export type DrizzleTableAggregateRepositoryOptions<
95
- TAggregate extends AnyAggregateDefinition,
96
- TDb,
97
- TTransaction,
98
- TTable extends Table,
99
- TIdentityRowKey extends DrizzleSelectKey<TTable>,
100
- TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>,
101
- TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined,
102
- TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined =
103
- undefined,
104
- > = {
105
- db: TDb
106
- table: TTable
107
- identity: {
108
- stateKey: TIdentityStateKey
109
- rowKey: TIdentityRowKey
110
- }
111
- version?: {
112
- stateKey: TVersionStateKey
113
- rowKey: TVersionRowKey
114
- }
115
- serialize: {
116
- insert(state: AggregateState<TAggregate>): DrizzleInsertRow<TTable>
117
- update(state: AggregateState<TAggregate>): Partial<DrizzleInsertRow<TTable>>
118
- }
119
- deserialize(row: DrizzleSelectRow<TTable>): AggregateState<TAggregate>
120
- operations: {
121
- findById(input: {
122
- executor: TDb | TTransaction
123
- table: TTable
124
- rowKey: TIdentityRowKey
125
- id: AggregateIdentity<TAggregate>
126
- }): Promise<DrizzleSelectRow<TTable> | null | undefined>
127
- insertOne(input: {
128
- executor: TDb | TTransaction
129
- table: TTable
130
- row: DrizzleInsertRow<TTable>
131
- }): Promise<void>
132
- updateById(input: {
133
- executor: TDb | TTransaction
134
- table: TTable
135
- rowKey: TIdentityRowKey
136
- id: AggregateIdentity<TAggregate>
137
- row: Partial<DrizzleInsertRow<TTable>>
138
- optimisticLock: boolean
139
- expectedVersion?: number
140
- versionRowKey?: TVersionRowKey
141
- }): Promise<boolean>
142
- }
143
- }
144
-
145
- export const createDrizzleTableAggregateRepository = <
146
- TAggregate extends AnyAggregateDefinition,
147
- TDb,
148
- TTransaction,
149
- TTable extends Table,
150
- TIdentityRowKey extends DrizzleSelectKey<TTable>,
151
- TIdentityStateKey extends Extract<keyof AggregateState<TAggregate>, string>,
152
- TVersionRowKey extends DrizzleSelectKey<TTable> | undefined = undefined,
153
- TVersionStateKey extends Extract<keyof AggregateState<TAggregate>, string> | undefined =
154
- undefined,
155
- >(
156
- options: DrizzleTableAggregateRepositoryOptions<
157
- TAggregate,
158
- TDb,
159
- TTransaction,
160
- TTable,
161
- TIdentityRowKey,
162
- TIdentityStateKey,
163
- TVersionRowKey,
164
- TVersionStateKey
165
- >,
166
- ): AggregateRepository<TAggregate> => {
167
- return createDrizzleAggregateRepository<TAggregate, TDb, TTransaction, DrizzleSelectRow<TTable>>({
168
- db: options.db,
169
- load: async (executor, id) => {
170
- return options.operations.findById({
171
- executor,
172
- table: options.table,
173
- rowKey: options.identity.rowKey,
174
- id,
175
- })
176
- },
177
- insert: async (executor, next) => {
178
- await options.operations.insertOne({
179
- executor,
180
- table: options.table,
181
- row: options.serialize.insert(next),
182
- })
183
- },
184
- update: async ({ executor, next, current, optimisticLock }) => {
185
- const versionRowKey = options.version?.rowKey
186
- const currentVersion = options.version
187
- ? (current[options.version.stateKey as keyof AggregateState<TAggregate>] as
188
- | number
189
- | undefined)
190
- : undefined
191
-
192
- return options.operations.updateById({
193
- executor,
194
- table: options.table,
195
- rowKey: options.identity.rowKey,
196
- id: next[
197
- options.identity.stateKey as keyof AggregateState<TAggregate>
198
- ] as AggregateIdentity<TAggregate>,
199
- row: options.serialize.update(next),
200
- optimisticLock,
201
- expectedVersion: currentVersion,
202
- versionRowKey,
203
- })
204
- },
205
- deserialize: options.deserialize,
206
- })
207
- }
208
-
209
- export type DrizzleVersionedAggregateRow = {
210
- version?: number
211
- }
212
-
213
- export const hasSameVersion = <
214
- TRow extends DrizzleVersionedAggregateRow,
215
- TState extends DrizzleVersionedAggregateRow,
216
- >(
217
- row: TRow | null | undefined,
218
- state: TState,
219
- ): boolean => {
220
- return row?.version === state.version
221
- }
@@ -1,31 +0,0 @@
1
- import type { PersistenceContext, TransactionRunner, MaybePromise } from 'rebackend'
2
-
3
- export type DrizzleTransactionCapable<TTransaction> = {
4
- transaction<T>(transaction: (tx: TTransaction) => MaybePromise<T>): MaybePromise<T>
5
- }
6
-
7
- export const createDrizzleTransactionRunner = <
8
- TDb extends DrizzleTransactionCapable<TTransaction>,
9
- TTransaction,
10
- >(
11
- db: TDb,
12
- ): TransactionRunner<TTransaction> => {
13
- return {
14
- async run<T>(work: (transaction: TTransaction) => MaybePromise<T>): Promise<T> {
15
- return Promise.resolve(db.transaction((transaction) => work(transaction)))
16
- },
17
- }
18
- }
19
-
20
- export const resolveDrizzleExecutor = <TDb, TTransaction>(
21
- db: TDb,
22
- context?: PersistenceContext,
23
- ): TDb | TTransaction => {
24
- const transaction = context?.transaction
25
-
26
- if (transaction === null || transaction === undefined) {
27
- return db
28
- }
29
-
30
- return transaction as TTransaction
31
- }
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './drizzle'