Skip to content

Commit 3f0bb0e

Browse files
committed
Feat: Convert event subscribers to typescript
1 parent a049644 commit 3f0bb0e

13 files changed

Lines changed: 282 additions & 78 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ cypress
2828
/packages/evershop/config
2929
.prettierignore
3030
.prettierrc
31-
mysqlToPostgres.js
31+
mysqlToPostgres.js
32+
/docs

packages/evershop/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
"import": "./dist/lib/event/emitter.js",
3232
"types": "./dist/lib/event/emitter.d.ts"
3333
},
34+
"./lib/event/subscriber": {
35+
"import": "./dist/lib/event/subscriber.js",
36+
"types": "./dist/lib/event/subscriber.d.ts"
37+
},
3438
"./lib/postgres": {
3539
"import": "./dist/lib/postgres/connection.js",
3640
"types": "./dist/lib/postgres/connection.d.ts"

packages/evershop/src/lib/event/emitter.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
import { insert } from '@evershop/postgres-query-builder';
22
import { pool } from '../postgres/connection.js';
3+
import { EventDataRegistry, EventName } from '../../types/event.js';
34

45
/**
5-
* Emit an event, this is used to trigger actions when an event occurs
6+
* Emit a typed event. The event data type is inferred from the event name.
7+
* @param name - The name of the event (must be registered in EventDataRegistry)
8+
* @param data - The data to emit (type is inferred from event name)
9+
*/
10+
export async function emit<T extends EventName>(
11+
name: T,
12+
data: EventDataRegistry[T]
13+
): Promise<void>;
14+
15+
/**
16+
* Emit an untyped event. Use this for dynamic events that aren't registered.
617
* @param name - The name of the event
718
* @param data - The data to emit
819
*/
20+
export async function emit(
21+
name: string,
22+
data: Record<string, any>
23+
): Promise<void>;
24+
25+
// Implementation
926
export async function emit(name: string, data: Record<string, any>) {
1027
await insert('event')
1128
.given({
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { EventDataRegistry, EventName } from '../../types/event.js';
2+
3+
/**
4+
* Type-safe event subscriber function.
5+
* Use this type to ensure your subscriber receives the correct event data type.
6+
*
7+
* @example
8+
* ```typescript
9+
* import { EventSubscriber } from '@evershop/evershop/lib/event/subscriber';
10+
*
11+
* const handler: EventSubscriber<'order.placed'> = async (data) => {
12+
* // data is typed as EventDataRegistry['order.placed']
13+
* console.log(data.orderId); // TypeScript knows this exists
14+
* };
15+
*
16+
* export default handler;
17+
* ```
18+
*/
19+
export type EventSubscriber<T extends EventName> = (
20+
data: EventDataRegistry[T]
21+
) => Promise<void> | void;
22+
23+
/**
24+
* Helper function to create a type-safe event subscriber.
25+
* Provides better IDE autocomplete and type checking.
26+
*
27+
* @example
28+
* ```typescript
29+
* import { createSubscriber } from '@evershop/evershop/lib/event/subscriber';
30+
*
31+
* export default createSubscriber('order.placed', async (data) => {
32+
* // data is automatically typed
33+
* await sendEmail(data.orderId);
34+
* });
35+
* ```
36+
*/
37+
export function createSubscriber<T extends EventName>(
38+
eventName: T,
39+
handler: EventSubscriber<T>
40+
): (data: EventDataRegistry[T]) => Promise<void> {
41+
return async (data: EventDataRegistry[T]) => {
42+
await handler(data);
43+
};
44+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* Catalog module event types
3+
* These events are emitted by the catalog module with complete table row data
4+
*/
5+
declare module '@evershop/evershop/types/event' {
6+
interface EventDataRegistry {
7+
/**
8+
* Fired when a new product is created
9+
* Data: Complete product table row
10+
*/
11+
product_created: {
12+
product_id: number;
13+
uuid: string;
14+
type: string;
15+
variant_group_id: number | null;
16+
visibility: boolean;
17+
group_id: number;
18+
image: string | null;
19+
sku: string;
20+
price: number;
21+
qty: number;
22+
weight: number | null;
23+
manage_stock: boolean;
24+
stock_availability: boolean;
25+
tax_class: number | null;
26+
status: boolean;
27+
created_at: string;
28+
updated_at: string;
29+
category_id: number | null;
30+
};
31+
32+
/**
33+
* Fired when a product is updated
34+
* Data: Complete product table row
35+
*/
36+
product_updated: {
37+
product_id: number;
38+
uuid: string;
39+
type: string;
40+
variant_group_id: number | null;
41+
visibility: boolean;
42+
group_id: number;
43+
image: string | null;
44+
sku: string;
45+
price: number;
46+
qty: number;
47+
weight: number | null;
48+
manage_stock: boolean;
49+
stock_availability: boolean;
50+
tax_class: number | null;
51+
status: boolean;
52+
created_at: string;
53+
updated_at: string;
54+
category_id: number | null;
55+
};
56+
57+
/**
58+
* Fired when a product is deleted
59+
* Data: Complete product table row
60+
*/
61+
product_deleted: {
62+
product_id: number;
63+
uuid: string;
64+
type: string;
65+
variant_group_id: number | null;
66+
visibility: boolean;
67+
group_id: number;
68+
image: string | null;
69+
sku: string;
70+
price: number;
71+
qty: number;
72+
weight: number | null;
73+
manage_stock: boolean;
74+
stock_availability: boolean;
75+
tax_class: number | null;
76+
status: boolean;
77+
created_at: string;
78+
updated_at: string;
79+
category_id: number | null;
80+
};
81+
82+
/**
83+
* Fired when a product image is added
84+
* Data: Complete product_image table row
85+
*/
86+
product_image_added: {
87+
product_image_id: number;
88+
product_image_product_id: number;
89+
origin_image: string;
90+
thumb_image: string | null;
91+
listing_image: string | null;
92+
single_image: string | null;
93+
is_main: boolean;
94+
};
95+
96+
/**
97+
* Fired when a new category is created
98+
* Data: Complete category table row
99+
*/
100+
category_created: {
101+
category_id: number;
102+
uuid: string;
103+
status: boolean;
104+
parent_id: number | null;
105+
include_in_nav: boolean;
106+
position: number | null;
107+
show_products: boolean;
108+
created_at: string;
109+
updated_at: string;
110+
};
111+
112+
/**
113+
* Fired when a category is updated
114+
* Data: Complete category table row
115+
*/
116+
category_updated: {
117+
category_id: number;
118+
uuid: string;
119+
status: boolean;
120+
parent_id: number | null;
121+
include_in_nav: boolean;
122+
position: number | null;
123+
show_products: boolean;
124+
created_at: string;
125+
updated_at: string;
126+
};
127+
128+
/**
129+
* Fired when a category is deleted
130+
* Data: Complete category table row
131+
*/
132+
category_deleted: {
133+
category_id: number;
134+
uuid: string;
135+
status: boolean;
136+
parent_id: number | null;
137+
include_in_nav: boolean;
138+
position: number | null;
139+
show_products: boolean;
140+
created_at: string;
141+
updated_at: string;
142+
};
143+
}
144+
}
145+
146+
export {};

packages/evershop/src/modules/catalog/subscribers/category_created/buildUrlRewrite.js renamed to packages/evershop/src/modules/catalog/subscribers/category_created/buildUrlRewrite.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import {
55
} from '@evershop/postgres-query-builder';
66
import { error } from '../../../../lib/log/logger.js';
77
import { pool } from '../../../../lib/postgres/connection.js';
8+
import { EventSubscriber } from '../../../../lib/event/subscriber.js';
89

9-
export default async function buildUrlReWrite(data) {
10+
const buildUrlReWrite: EventSubscriber<'category_created'> = async (data) => {
1011
const categoryId = data.category_id;
1112
const categoryUuid = data.uuid;
1213

@@ -55,4 +56,6 @@ export default async function buildUrlReWrite(data) {
5556
} catch (err) {
5657
error(err);
5758
}
58-
}
59+
};
60+
61+
export default buildUrlReWrite;

packages/evershop/src/modules/catalog/subscribers/category_deleted/deleteUrlRewrite.js renamed to packages/evershop/src/modules/catalog/subscribers/category_deleted/deleteUrlRewrite.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { execute, select } from '@evershop/postgres-query-builder';
22
import { error } from '../../../../lib/log/logger.js';
33
import { pool } from '../../../../lib/postgres/connection.js';
4+
import { EventSubscriber } from '../../../../lib/event/subscriber.js';
45

5-
export default async function deleteUrlReWrite(data) {
6+
const deleteUrlReWrite: EventSubscriber<'category_deleted'> = async (data) => {
67
try {
78
const categoryUuid = data.uuid;
89
// Get the current url rewrite for this category
@@ -29,4 +30,6 @@ export default async function deleteUrlReWrite(data) {
2930
} catch (err) {
3031
error(err);
3132
}
32-
}
33+
};
34+
35+
export default deleteUrlReWrite;

packages/evershop/src/modules/catalog/subscribers/category_updated/builUrlRewrite.js renamed to packages/evershop/src/modules/catalog/subscribers/category_updated/builUrlRewrite.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import {
55
} from '@evershop/postgres-query-builder';
66
import { error } from '../../../../lib/log/logger.js';
77
import { pool } from '../../../../lib/postgres/connection.js';
8+
import { EventSubscriber } from '../../../../lib/event/subscriber.js';
89

9-
export default async function buildUrlReWrite(data) {
10+
const buildUrlReWrite: EventSubscriber<'category_updated'> = async (data) => {
1011
try {
1112
const categoryUUid = data.uuid;
1213
const categoryId = data.category_id;
@@ -68,4 +69,6 @@ export default async function buildUrlReWrite(data) {
6869
} catch (err) {
6970
error(err);
7071
}
71-
}
72+
};
73+
74+
export default buildUrlReWrite;

packages/evershop/src/modules/catalog/subscribers/product_created/builUrlRewrite.js renamed to packages/evershop/src/modules/catalog/subscribers/product_created/buildUrlRewrite.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { insertOnUpdate, select } from '@evershop/postgres-query-builder';
22
import { error } from '../../../../lib/log/logger.js';
33
import { pool } from '../../../../lib/postgres/connection.js';
4+
import { EventSubscriber } from '../../../../lib/event/subscriber.js';
45

5-
export default async function buildUrlReWrite(data) {
6+
const buildUrlRewrite: EventSubscriber<'product_created'> = async (data) => {
67
try {
78
const productId = data.product_id;
89
const productUuid = data.uuid;
@@ -58,4 +59,6 @@ export default async function buildUrlReWrite(data) {
5859
} catch (e) {
5960
error(e);
6061
}
61-
}
62+
};
63+
64+
export default buildUrlRewrite;

packages/evershop/src/modules/catalog/subscribers/product_deleted/deleteUrlRewrite.js renamed to packages/evershop/src/modules/catalog/subscribers/product_deleted/deleteUrlRewrite.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { execute } from '@evershop/postgres-query-builder';
22
import { pool } from '../../../../lib/postgres/connection.js';
3+
import { EventSubscriber } from '../../../../lib/event/subscriber.js';
34

4-
export default async function buildUrlReWrite(data) {
5+
const buildUrlReWrite: EventSubscriber<'product_deleted'> = async (data) => {
56
const productUuid = data.uuid;
67

78
// Delete the url rewrite for the product
89
await execute(
910
pool,
1011
`DELETE FROM url_rewrite WHERE entity_uuid = '${productUuid}' AND entity_type = 'product'`
1112
);
12-
}
13+
};
14+
15+
export default buildUrlReWrite;

0 commit comments

Comments
 (0)