@rahul_vendure/vendure-plugin-wishlist 1.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/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # @rahul_vendure/vendure-plugin-wishlist
2
+
3
+ A Vendure plugin that adds wishlist functionality — customers can save product variants to their wishlist with single and bulk add/remove operations.
4
+
5
+ ## Features
6
+
7
+ - **Add/remove single items** to wishlist
8
+ - **Bulk add/remove** multiple items in one mutation
9
+ - **Idempotent** — adding an already-wishlisted item returns the existing entry
10
+ - **`isInWishlist` field** on `ProductVariant` — easy to check in storefront queries
11
+ - **Paginated list** — uses Vendure's built-in `ListQueryBuilder` for filtering/sorting/pagination
12
+ - **Unique constraint** — a customer can only wishlist a variant once
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @rahul_vendure/vendure-plugin-wishlist
18
+ ```
19
+
20
+ ## Setup
21
+
22
+ ```ts
23
+ import { WishlistPlugin } from '@rahul_vendure/vendure-plugin-wishlist';
24
+
25
+ const config: VendureConfig = {
26
+ plugins: [
27
+ WishlistPlugin.init(),
28
+ ],
29
+ };
30
+ ```
31
+
32
+ > **Important:** This plugin creates a `wishlist_item` table. If your project has
33
+ > `synchronize: false`, you need to generate and run a database migration after
34
+ > adding this plugin:
35
+ >
36
+ > ```bash
37
+ > npx vendure migrate
38
+ > ```
39
+
40
+ ## GraphQL API
41
+
42
+ All operations are on the **Shop API** and require authentication (`Permission.Owner`).
43
+
44
+ ### Queries
45
+
46
+ #### `wishlists`
47
+
48
+ Get the current customer's wishlist with pagination.
49
+
50
+ ```graphql
51
+ query {
52
+ wishlists(options: { take: 10, skip: 0 }) {
53
+ items {
54
+ id
55
+ createdAt
56
+ productVariant {
57
+ id
58
+ name
59
+ priceWithTax
60
+ featuredAsset {
61
+ preview
62
+ }
63
+ }
64
+ }
65
+ totalItems
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### Mutations
71
+
72
+ #### `addToWishlist`
73
+
74
+ Add a single product variant. Returns the existing item if already wishlisted.
75
+
76
+ ```graphql
77
+ mutation {
78
+ addToWishlist(productVariantId: "42") {
79
+ id
80
+ productVariant {
81
+ id
82
+ name
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ #### `removeFromWishlist`
89
+
90
+ Remove by wishlist item ID or product variant ID (at least one required).
91
+
92
+ ```graphql
93
+ mutation {
94
+ removeFromWishlist(productVariantId: "42") {
95
+ result
96
+ message
97
+ }
98
+ }
99
+ ```
100
+
101
+ #### `addMultipleToWishlist`
102
+
103
+ Bulk add multiple variants at once. Skips duplicates.
104
+
105
+ ```graphql
106
+ mutation {
107
+ addMultipleToWishlist(productVariantIds: ["42", "43", "44"]) {
108
+ id
109
+ productVariant {
110
+ id
111
+ name
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ #### `removeMultipleFromWishlist`
118
+
119
+ Bulk remove by product variant IDs.
120
+
121
+ ```graphql
122
+ mutation {
123
+ removeMultipleFromWishlist(productVariantIds: ["42", "43"]) {
124
+ result
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### Extended Fields
130
+
131
+ #### `ProductVariant.isInWishlist`
132
+
133
+ Returns `true` if the current customer has this variant in their wishlist.
134
+ Returns `false` for unauthenticated users.
135
+
136
+ ```graphql
137
+ query {
138
+ product(id: "1") {
139
+ variants {
140
+ id
141
+ name
142
+ isInWishlist
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ## Database
149
+
150
+ This plugin creates a `wishlist_item` table with:
151
+
152
+ | Column | Type | Description |
153
+ | --- | --- | --- |
154
+ | `id` | `ID` | Primary key |
155
+ | `customerId` | `ID` | Foreign key to `customer` |
156
+ | `productVariantId` | `ID` | Foreign key to `product_variant` |
157
+ | `createdAt` | `DateTime` | Auto-set on creation |
158
+ | `updatedAt` | `DateTime` | Auto-set on update |
159
+
160
+ A unique composite index on `(customerId, productVariantId)` prevents duplicates.
161
+
162
+ ## Compatibility
163
+
164
+ - Vendure `^3.0.0`
165
+
166
+ ## License
167
+
168
+ MIT
@@ -0,0 +1 @@
1
+ export declare const shopApiExtensions: import("graphql").DocumentNode;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.shopApiExtensions = void 0;
7
+ const graphql_tag_1 = __importDefault(require("graphql-tag"));
8
+ exports.shopApiExtensions = (0, graphql_tag_1.default) `
9
+ type WishlistItem implements Node {
10
+ id: ID!
11
+ createdAt: DateTime!
12
+ updatedAt: DateTime!
13
+ productVariant: ProductVariant!
14
+ }
15
+
16
+ type WishlistItemList implements PaginatedList {
17
+ items: [WishlistItem!]!
18
+ totalItems: Int!
19
+ }
20
+
21
+ input WishlistItemListOptions
22
+
23
+ extend type Query {
24
+ wishlists(options: WishlistItemListOptions): WishlistItemList!
25
+ }
26
+
27
+ extend type Mutation {
28
+ addToWishlist(productVariantId: ID!): WishlistItem!
29
+ """
30
+ Removes a wishlist item by its id or product variant id.
31
+ At least one must be specified.
32
+ """
33
+ removeFromWishlist(id: ID, productVariantId: ID): DeletionResponse!
34
+ """
35
+ Adds multiple product variants to wishlist at once.
36
+ """
37
+ addMultipleToWishlist(productVariantIds: [ID!]!): [WishlistItem!]!
38
+ """
39
+ Removes multiple wishlist items by their product variant ids.
40
+ """
41
+ removeMultipleFromWishlist(productVariantIds: [ID!]!): DeletionResponse!
42
+ }
43
+
44
+ extend type ProductVariant {
45
+ isInWishlist: Boolean!
46
+ }
47
+ `;
@@ -0,0 +1,8 @@
1
+ import { CustomerService, ProductVariant, RequestContext } from '@vendure/core';
2
+ import { WishlistService } from '../services/wishlist.service';
3
+ export declare class ProductVariantEntityResolver {
4
+ private customerService;
5
+ private wishlistService;
6
+ constructor(customerService: CustomerService, wishlistService: WishlistService);
7
+ isInWishlist(ctx: RequestContext, productVariant: ProductVariant): Promise<boolean>;
8
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ProductVariantEntityResolver = void 0;
16
+ const graphql_1 = require("@nestjs/graphql");
17
+ const core_1 = require("@vendure/core");
18
+ const wishlist_service_1 = require("../services/wishlist.service");
19
+ let ProductVariantEntityResolver = class ProductVariantEntityResolver {
20
+ constructor(customerService, wishlistService) {
21
+ this.customerService = customerService;
22
+ this.wishlistService = wishlistService;
23
+ }
24
+ async isInWishlist(ctx, productVariant) {
25
+ const userId = ctx.activeUserId;
26
+ if (!userId) {
27
+ return false;
28
+ }
29
+ const customer = await this.customerService.findOneByUserId(ctx, userId);
30
+ if (!customer) {
31
+ return false;
32
+ }
33
+ return this.wishlistService.isInWishlist(ctx, customer.id, productVariant.id);
34
+ }
35
+ };
36
+ exports.ProductVariantEntityResolver = ProductVariantEntityResolver;
37
+ __decorate([
38
+ (0, graphql_1.ResolveField)(),
39
+ __param(0, (0, core_1.Ctx)()),
40
+ __param(1, (0, graphql_1.Parent)()),
41
+ __metadata("design:type", Function),
42
+ __metadata("design:paramtypes", [core_1.RequestContext,
43
+ core_1.ProductVariant]),
44
+ __metadata("design:returntype", Promise)
45
+ ], ProductVariantEntityResolver.prototype, "isInWishlist", null);
46
+ exports.ProductVariantEntityResolver = ProductVariantEntityResolver = __decorate([
47
+ (0, graphql_1.Resolver)('ProductVariant'),
48
+ __metadata("design:paramtypes", [core_1.CustomerService,
49
+ wishlist_service_1.WishlistService])
50
+ ], ProductVariantEntityResolver);
@@ -0,0 +1,20 @@
1
+ import { DeletionResponse } from '@vendure/common/lib/generated-types';
2
+ import { CustomerService, ID, PaginatedList, RequestContext } from '@vendure/core';
3
+ import { WishlistItem } from '../entities/wishlist-item.entity';
4
+ import { WishlistService } from '../services/wishlist.service';
5
+ export declare class WishlistShopResolver {
6
+ private customerService;
7
+ private wishlistService;
8
+ constructor(customerService: CustomerService, wishlistService: WishlistService);
9
+ wishlists(ctx: RequestContext, args: {
10
+ options?: any;
11
+ }): Promise<PaginatedList<WishlistItem>>;
12
+ addToWishlist(ctx: RequestContext, productVariantId: ID): Promise<WishlistItem>;
13
+ removeFromWishlist(ctx: RequestContext, args: {
14
+ id?: ID;
15
+ productVariantId?: ID;
16
+ }): Promise<DeletionResponse>;
17
+ addMultipleToWishlist(ctx: RequestContext, productVariantIds: ID[]): Promise<WishlistItem[]>;
18
+ removeMultipleFromWishlist(ctx: RequestContext, productVariantIds: ID[]): Promise<DeletionResponse>;
19
+ private getCustomerForOwner;
20
+ }
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.WishlistShopResolver = void 0;
16
+ const graphql_1 = require("@nestjs/graphql");
17
+ const core_1 = require("@vendure/core");
18
+ const wishlist_service_1 = require("../services/wishlist.service");
19
+ let WishlistShopResolver = class WishlistShopResolver {
20
+ constructor(customerService, wishlistService) {
21
+ this.customerService = customerService;
22
+ this.wishlistService = wishlistService;
23
+ }
24
+ async wishlists(ctx, args) {
25
+ const customer = await this.getCustomerForOwner(ctx);
26
+ return this.wishlistService.findAllByCustomerId(ctx, customer.id, args.options);
27
+ }
28
+ async addToWishlist(ctx, productVariantId) {
29
+ const customer = await this.getCustomerForOwner(ctx);
30
+ return this.wishlistService.create(ctx, customer.id, productVariantId);
31
+ }
32
+ async removeFromWishlist(ctx, args) {
33
+ const customer = await this.getCustomerForOwner(ctx);
34
+ return this.wishlistService.delete(ctx, customer.id, args.id, args.productVariantId);
35
+ }
36
+ async addMultipleToWishlist(ctx, productVariantIds) {
37
+ const customer = await this.getCustomerForOwner(ctx);
38
+ return this.wishlistService.createMultiple(ctx, customer.id, productVariantIds);
39
+ }
40
+ async removeMultipleFromWishlist(ctx, productVariantIds) {
41
+ const customer = await this.getCustomerForOwner(ctx);
42
+ return this.wishlistService.deleteMultiple(ctx, customer.id, productVariantIds);
43
+ }
44
+ async getCustomerForOwner(ctx) {
45
+ const userId = ctx.activeUserId;
46
+ if (!userId) {
47
+ throw new core_1.ForbiddenError();
48
+ }
49
+ const customer = await this.customerService.findOneByUserId(ctx, userId);
50
+ if (!customer) {
51
+ throw new core_1.InternalServerError('error.no-customer-found-for-current-user');
52
+ }
53
+ return customer;
54
+ }
55
+ };
56
+ exports.WishlistShopResolver = WishlistShopResolver;
57
+ __decorate([
58
+ (0, graphql_1.Query)(),
59
+ (0, core_1.Allow)(core_1.Permission.Owner),
60
+ __param(0, (0, core_1.Ctx)()),
61
+ __param(1, (0, graphql_1.Args)()),
62
+ __metadata("design:type", Function),
63
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
64
+ __metadata("design:returntype", Promise)
65
+ ], WishlistShopResolver.prototype, "wishlists", null);
66
+ __decorate([
67
+ (0, graphql_1.Mutation)(),
68
+ (0, core_1.Allow)(core_1.Permission.Owner),
69
+ __param(0, (0, core_1.Ctx)()),
70
+ __param(1, (0, graphql_1.Args)('productVariantId')),
71
+ __metadata("design:type", Function),
72
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
73
+ __metadata("design:returntype", Promise)
74
+ ], WishlistShopResolver.prototype, "addToWishlist", null);
75
+ __decorate([
76
+ (0, graphql_1.Mutation)(),
77
+ (0, core_1.Allow)(core_1.Permission.Owner),
78
+ __param(0, (0, core_1.Ctx)()),
79
+ __param(1, (0, graphql_1.Args)()),
80
+ __metadata("design:type", Function),
81
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
82
+ __metadata("design:returntype", Promise)
83
+ ], WishlistShopResolver.prototype, "removeFromWishlist", null);
84
+ __decorate([
85
+ (0, graphql_1.Mutation)(),
86
+ (0, core_1.Allow)(core_1.Permission.Owner),
87
+ __param(0, (0, core_1.Ctx)()),
88
+ __param(1, (0, graphql_1.Args)('productVariantIds')),
89
+ __metadata("design:type", Function),
90
+ __metadata("design:paramtypes", [core_1.RequestContext, Array]),
91
+ __metadata("design:returntype", Promise)
92
+ ], WishlistShopResolver.prototype, "addMultipleToWishlist", null);
93
+ __decorate([
94
+ (0, graphql_1.Mutation)(),
95
+ (0, core_1.Allow)(core_1.Permission.Owner),
96
+ __param(0, (0, core_1.Ctx)()),
97
+ __param(1, (0, graphql_1.Args)('productVariantIds')),
98
+ __metadata("design:type", Function),
99
+ __metadata("design:paramtypes", [core_1.RequestContext, Array]),
100
+ __metadata("design:returntype", Promise)
101
+ ], WishlistShopResolver.prototype, "removeMultipleFromWishlist", null);
102
+ exports.WishlistShopResolver = WishlistShopResolver = __decorate([
103
+ (0, graphql_1.Resolver)(),
104
+ __metadata("design:paramtypes", [core_1.CustomerService,
105
+ wishlist_service_1.WishlistService])
106
+ ], WishlistShopResolver);
@@ -0,0 +1,2 @@
1
+ export declare const WISHLIST_PLUGIN_OPTIONS: unique symbol;
2
+ export declare const loggerCtx = "WishlistPlugin";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loggerCtx = exports.WISHLIST_PLUGIN_OPTIONS = void 0;
4
+ exports.WISHLIST_PLUGIN_OPTIONS = Symbol('WISHLIST_PLUGIN_OPTIONS');
5
+ exports.loggerCtx = 'WishlistPlugin';
@@ -0,0 +1,8 @@
1
+ import { Customer, DeepPartial, ID, ProductVariant, VendureEntity } from '@vendure/core';
2
+ export declare class WishlistItem extends VendureEntity {
3
+ constructor(input?: DeepPartial<WishlistItem>);
4
+ customer: Customer;
5
+ customerId: ID;
6
+ productVariant: ProductVariant;
7
+ productVariantId: ID;
8
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.WishlistItem = void 0;
13
+ const core_1 = require("@vendure/core");
14
+ const typeorm_1 = require("typeorm");
15
+ let WishlistItem = class WishlistItem extends core_1.VendureEntity {
16
+ constructor(input) {
17
+ super(input);
18
+ }
19
+ };
20
+ exports.WishlistItem = WishlistItem;
21
+ __decorate([
22
+ (0, typeorm_1.ManyToOne)(() => core_1.Customer, { eager: false }),
23
+ (0, typeorm_1.JoinColumn)({ name: 'customerId' }),
24
+ __metadata("design:type", core_1.Customer)
25
+ ], WishlistItem.prototype, "customer", void 0);
26
+ __decorate([
27
+ (0, typeorm_1.Column)(),
28
+ __metadata("design:type", Object)
29
+ ], WishlistItem.prototype, "customerId", void 0);
30
+ __decorate([
31
+ (0, typeorm_1.ManyToOne)(() => core_1.ProductVariant, { eager: true }),
32
+ (0, typeorm_1.JoinColumn)({ name: 'productVariantId' }),
33
+ __metadata("design:type", core_1.ProductVariant)
34
+ ], WishlistItem.prototype, "productVariant", void 0);
35
+ __decorate([
36
+ (0, typeorm_1.Column)(),
37
+ __metadata("design:type", Object)
38
+ ], WishlistItem.prototype, "productVariantId", void 0);
39
+ exports.WishlistItem = WishlistItem = __decorate([
40
+ (0, typeorm_1.Entity)(),
41
+ (0, typeorm_1.Index)(['customerId', 'productVariantId'], { unique: true }),
42
+ __metadata("design:paramtypes", [Object])
43
+ ], WishlistItem);
@@ -0,0 +1,4 @@
1
+ export { WishlistPlugin } from './wishlist.plugin';
2
+ export { WishlistPluginOptions } from './types';
3
+ export { WishlistItem } from './entities/wishlist-item.entity';
4
+ export { WishlistService } from './services/wishlist.service';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WishlistService = exports.WishlistItem = exports.WishlistPlugin = void 0;
4
+ var wishlist_plugin_1 = require("./wishlist.plugin");
5
+ Object.defineProperty(exports, "WishlistPlugin", { enumerable: true, get: function () { return wishlist_plugin_1.WishlistPlugin; } });
6
+ var wishlist_item_entity_1 = require("./entities/wishlist-item.entity");
7
+ Object.defineProperty(exports, "WishlistItem", { enumerable: true, get: function () { return wishlist_item_entity_1.WishlistItem; } });
8
+ var wishlist_service_1 = require("./services/wishlist.service");
9
+ Object.defineProperty(exports, "WishlistService", { enumerable: true, get: function () { return wishlist_service_1.WishlistService; } });
@@ -0,0 +1,16 @@
1
+ import { DeletionResponse } from '@vendure/common/lib/generated-types';
2
+ import { ID, ListQueryBuilder, ListQueryOptions, PaginatedList, ProductVariantService, RequestContext, TransactionalConnection } from '@vendure/core';
3
+ import { WishlistItem } from '../entities/wishlist-item.entity';
4
+ export declare class WishlistService {
5
+ private connection;
6
+ private listQueryBuilder;
7
+ private productVariantService;
8
+ constructor(connection: TransactionalConnection, listQueryBuilder: ListQueryBuilder, productVariantService: ProductVariantService);
9
+ findOne(ctx: RequestContext, id: ID): Promise<WishlistItem>;
10
+ findAllByCustomerId(ctx: RequestContext, customerId: ID, options?: ListQueryOptions<WishlistItem>): Promise<PaginatedList<WishlistItem>>;
11
+ create(ctx: RequestContext, customerId: ID, productVariantId: ID): Promise<WishlistItem>;
12
+ createMultiple(ctx: RequestContext, customerId: ID, productVariantIds: ID[]): Promise<WishlistItem[]>;
13
+ delete(ctx: RequestContext, customerId: ID, id?: ID | null, productVariantId?: ID | null): Promise<DeletionResponse>;
14
+ deleteMultiple(ctx: RequestContext, customerId: ID, productVariantIds: ID[]): Promise<DeletionResponse>;
15
+ isInWishlist(ctx: RequestContext, customerId: ID, productVariantId: ID): Promise<boolean>;
16
+ }
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.WishlistService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const generated_types_1 = require("@vendure/common/lib/generated-types");
15
+ const core_1 = require("@vendure/core");
16
+ const typeorm_1 = require("typeorm");
17
+ const wishlist_item_entity_1 = require("../entities/wishlist-item.entity");
18
+ let WishlistService = class WishlistService {
19
+ constructor(connection, listQueryBuilder, productVariantService) {
20
+ this.connection = connection;
21
+ this.listQueryBuilder = listQueryBuilder;
22
+ this.productVariantService = productVariantService;
23
+ }
24
+ async findOne(ctx, id) {
25
+ return this.connection.getRepository(ctx, wishlist_item_entity_1.WishlistItem).findOneOrFail({
26
+ where: { id },
27
+ relations: ['productVariant', 'productVariant.featuredAsset'],
28
+ });
29
+ }
30
+ async findAllByCustomerId(ctx, customerId, options) {
31
+ return this.listQueryBuilder
32
+ .build(wishlist_item_entity_1.WishlistItem, options, {
33
+ ctx,
34
+ where: { customerId },
35
+ relations: ['productVariant', 'productVariant.featuredAsset'],
36
+ })
37
+ .getManyAndCount()
38
+ .then(([items, totalItems]) => ({
39
+ items,
40
+ totalItems,
41
+ }));
42
+ }
43
+ async create(ctx, customerId, productVariantId) {
44
+ const productVariant = await this.productVariantService.findOne(ctx, productVariantId);
45
+ if (!productVariant) {
46
+ throw new core_1.UserInputError('Product variant not found');
47
+ }
48
+ const existing = await this.connection.getRepository(ctx, wishlist_item_entity_1.WishlistItem).findOne({
49
+ where: { customerId, productVariantId },
50
+ relations: ['productVariant', 'productVariant.featuredAsset'],
51
+ });
52
+ if (existing) {
53
+ return existing;
54
+ }
55
+ const wishlistItem = new wishlist_item_entity_1.WishlistItem({ customerId, productVariant });
56
+ const saved = await this.connection.getRepository(ctx, wishlist_item_entity_1.WishlistItem).save(wishlistItem);
57
+ return (0, core_1.assertFound)(this.findOne(ctx, saved.id));
58
+ }
59
+ async createMultiple(ctx, customerId, productVariantIds) {
60
+ if (!productVariantIds.length) {
61
+ return [];
62
+ }
63
+ const repo = this.connection.getRepository(ctx, wishlist_item_entity_1.WishlistItem);
64
+ // Fetch existing items and product variants in parallel
65
+ const [existingItems, productVariants] = await Promise.all([
66
+ repo.find({
67
+ where: { customerId, productVariantId: (0, typeorm_1.In)(productVariantIds) },
68
+ relations: ['productVariant', 'productVariant.featuredAsset'],
69
+ }),
70
+ Promise.all(productVariantIds.map((id) => this.productVariantService.findOne(ctx, id))),
71
+ ]);
72
+ // Validate all variants exist
73
+ const missingIdx = productVariants.findIndex((pv) => !pv);
74
+ if (missingIdx !== -1) {
75
+ throw new core_1.UserInputError(`Product variant not found: ${productVariantIds[missingIdx]}`);
76
+ }
77
+ // Filter out already-existing ones
78
+ const existingVariantIds = new Set(existingItems.map((item) => String(item.productVariantId)));
79
+ const newItems = productVariants
80
+ .filter((pv) => !existingVariantIds.has(String(pv.id)))
81
+ .map((pv) => new wishlist_item_entity_1.WishlistItem({ customerId, productVariant: pv }));
82
+ if (newItems.length === 0) {
83
+ return existingItems;
84
+ }
85
+ // Bulk save and re-fetch with relations
86
+ const savedItems = await repo.save(newItems);
87
+ const newlyCreated = await repo.find({
88
+ where: { id: (0, typeorm_1.In)(savedItems.map((i) => i.id)) },
89
+ relations: ['productVariant', 'productVariant.featuredAsset'],
90
+ });
91
+ return [...existingItems, ...newlyCreated];
92
+ }
93
+ async delete(ctx, customerId, id, productVariantId) {
94
+ if (!id && !productVariantId) {
95
+ throw new core_1.UserInputError('Either id or productVariantId must be provided.');
96
+ }
97
+ const where = { customerId };
98
+ if (id)
99
+ where.id = id;
100
+ if (productVariantId)
101
+ where.productVariantId = productVariantId;
102
+ const entity = await this.connection.getRepository(ctx, wishlist_item_entity_1.WishlistItem).findOne({ where });
103
+ if (!entity) {
104
+ return { result: generated_types_1.DeletionResult.NOT_DELETED, message: 'Wishlist item not found' };
105
+ }
106
+ try {
107
+ await this.connection.getRepository(ctx, wishlist_item_entity_1.WishlistItem).remove(entity);
108
+ return { result: generated_types_1.DeletionResult.DELETED };
109
+ }
110
+ catch (e) {
111
+ return { result: generated_types_1.DeletionResult.NOT_DELETED, message: e.toString() };
112
+ }
113
+ }
114
+ async deleteMultiple(ctx, customerId, productVariantIds) {
115
+ if (!productVariantIds.length) {
116
+ throw new core_1.UserInputError('productVariantIds must be provided.');
117
+ }
118
+ try {
119
+ const result = await this.connection
120
+ .getRepository(ctx, wishlist_item_entity_1.WishlistItem)
121
+ .delete({ customerId, productVariantId: (0, typeorm_1.In)(productVariantIds) });
122
+ return {
123
+ result: (result.affected || 0) > 0 ? generated_types_1.DeletionResult.DELETED : generated_types_1.DeletionResult.NOT_DELETED,
124
+ };
125
+ }
126
+ catch (e) {
127
+ return { result: generated_types_1.DeletionResult.NOT_DELETED, message: e.toString() };
128
+ }
129
+ }
130
+ async isInWishlist(ctx, customerId, productVariantId) {
131
+ const count = await this.connection.getRepository(ctx, wishlist_item_entity_1.WishlistItem).count({
132
+ where: { customerId, productVariantId },
133
+ });
134
+ return count > 0;
135
+ }
136
+ };
137
+ exports.WishlistService = WishlistService;
138
+ exports.WishlistService = WishlistService = __decorate([
139
+ (0, common_1.Injectable)(),
140
+ __metadata("design:paramtypes", [core_1.TransactionalConnection,
141
+ core_1.ListQueryBuilder,
142
+ core_1.ProductVariantService])
143
+ ], WishlistService);
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Configuration options for WishlistPlugin.
3
+ * Currently empty — reserved for future options.
4
+ */
5
+ export interface WishlistPluginOptions {
6
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ import { Type } from '@vendure/core';
2
+ import { WishlistPluginOptions } from './types';
3
+ export declare class WishlistPlugin {
4
+ static options: WishlistPluginOptions;
5
+ static init(options?: WishlistPluginOptions): Type<WishlistPlugin>;
6
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var WishlistPlugin_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.WishlistPlugin = void 0;
11
+ const core_1 = require("@vendure/core");
12
+ const api_extensions_1 = require("./api/api-extensions");
13
+ const product_variant_entity_resolver_1 = require("./api/product-variant-entity.resolver");
14
+ const wishlist_resolver_1 = require("./api/wishlist.resolver");
15
+ const constants_1 = require("./constants");
16
+ const wishlist_item_entity_1 = require("./entities/wishlist-item.entity");
17
+ const wishlist_service_1 = require("./services/wishlist.service");
18
+ let WishlistPlugin = WishlistPlugin_1 = class WishlistPlugin {
19
+ static init(options) {
20
+ this.options = options || {};
21
+ return WishlistPlugin_1;
22
+ }
23
+ };
24
+ exports.WishlistPlugin = WishlistPlugin;
25
+ exports.WishlistPlugin = WishlistPlugin = WishlistPlugin_1 = __decorate([
26
+ (0, core_1.VendurePlugin)({
27
+ imports: [core_1.PluginCommonModule],
28
+ entities: [wishlist_item_entity_1.WishlistItem],
29
+ providers: [
30
+ { provide: constants_1.WISHLIST_PLUGIN_OPTIONS, useFactory: () => WishlistPlugin.options },
31
+ wishlist_service_1.WishlistService,
32
+ ],
33
+ shopApiExtensions: {
34
+ schema: api_extensions_1.shopApiExtensions,
35
+ resolvers: [wishlist_resolver_1.WishlistShopResolver, product_variant_entity_resolver_1.ProductVariantEntityResolver],
36
+ },
37
+ compatibility: '^3.0.0',
38
+ })
39
+ ], WishlistPlugin);
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@rahul_vendure/vendure-plugin-wishlist",
3
+ "version": "1.0.0",
4
+ "description": "Vendure plugin for customer wishlists with bulk add/remove support",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/**/*",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "watch": "tsc -w",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "peerDependencies": {
17
+ "@vendure/core": "^3.0.0"
18
+ },
19
+ "dependencies": {
20
+ "graphql-tag": "^2.12.6"
21
+ },
22
+ "keywords": [
23
+ "vendure",
24
+ "vendure-plugin",
25
+ "wishlist",
26
+ "favorites"
27
+ ],
28
+ "license": "MIT"
29
+ }