@unchainedshop/core 5.0.0-alpha.4 → 5.0.0-alpha.7

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
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @unchainedshop/core
5
5
 
6
- Core orchestration package for the Unchained Engine. Integrates all core-* modules, provides business services, directors for plugins, and bulk import functionality.
6
+ Core orchestration for the Unchained Engine: domain modules, cross-module services, plugin directors, and bulk import/export.
7
7
 
8
8
  ## Installation
9
9
 
@@ -13,602 +13,148 @@ npm install @unchainedshop/core
13
13
 
14
14
  ## Usage
15
15
 
16
- ```typescript
17
- import { initCore, type UnchainedCore } from '@unchainedshop/core';
18
-
19
- const unchainedCore = await initCore({
20
- db,
21
- migrationRepository,
22
- options: {
23
- // Module-specific options
24
- },
25
- });
26
-
27
- // Access modules
28
- const products = await unchainedCore.modules.products.findProducts({});
29
-
30
- // Use services
31
- const pricing = await unchainedCore.services.orders.pricingSheet(order);
32
-
33
- // Bulk import
34
- await unchainedCore.bulkImporter.prepare('PRODUCT');
35
- ```
36
-
37
- ## API Overview
38
-
39
- ### Initialization
40
-
41
- | Export | Description |
42
- |--------|-------------|
43
- | `initCore` | Initialize the core with all modules and services |
44
- | `getAllAdapters` | Get all registered adapters across all directors |
45
-
46
- ### Modules
47
-
48
- The `modules` object provides access to all core-* module APIs:
49
-
50
- | Module | Description |
51
- |--------|-------------|
52
- | `products` | Product management |
53
- | `assortments` | Category/assortment management |
54
- | `filters` | Product filtering and search |
55
- | `orders` | Order management |
56
- | `users` | User management |
57
- | `payment` | Payment providers |
58
- | `delivery` | Delivery providers |
59
- | `warehousing` | Inventory and stock |
60
- | `enrollments` | Subscriptions |
61
- | `quotations` | Quote management |
62
- | `bookmarks` | User bookmarks |
63
- | `countries` | Country management |
64
- | `currencies` | Currency management |
65
- | `languages` | Language management |
66
- | `files` | File management |
67
- | `events` | Event history |
68
- | `worker` | Background jobs |
69
-
70
- ### Services
71
-
72
- Business logic services that orchestrate multiple modules:
73
-
74
- | Service | Description |
75
- |---------|-------------|
76
- | `orders` | Order pricing, checkout workflows |
77
- | `products` | Product pricing calculations |
78
- | `users` | User-related operations |
79
- | `files` | File operations with adapters |
80
-
81
- ### Directors
82
-
83
- Plugin directors provide extensibility through the Director/Adapter pattern. Each director manages a collection of adapters that implement specific behaviors.
84
-
85
- | Director | Description |
86
- |----------|-------------|
87
- | `PaymentDirector` | Payment processing adapters |
88
- | `DeliveryDirector` | Delivery/shipping adapters |
89
- | `WarehousingDirector` | Inventory and stock adapters |
90
- | `WorkerDirector` | Background job workers |
91
- | `FilterDirector` | Product search and filtering |
92
- | `EnrollmentDirector` | Subscription plan handling |
93
- | `QuotationDirector` | Quote/RFQ processing |
94
- | `ProductPricingDirector` | Product price calculations |
95
- | `OrderPricingDirector` | Order total calculations |
96
- | `DeliveryPricingDirector` | Delivery fee calculations |
97
- | `PaymentPricingDirector` | Payment fee calculations |
98
- | `ProductDiscountDirector` | Product-level discounts |
99
- | `OrderDiscountDirector` | Order-level discounts |
100
- | `MessagingDirector` | Email/notification templates |
101
-
102
- ## Director/Adapter Architecture
103
-
104
- The Unchained Engine uses a Director/Adapter pattern for extensibility. Directors manage collections of adapters, and adapters implement specific behaviors.
105
-
106
- ### Base Classes
16
+ Most applications initialize core through `startPlatform()` from `@unchainedshop/platform`. Infrastructure integrations can initialize it directly with an existing database and migration repository:
107
17
 
108
18
  ```typescript
109
- import { BaseAdapter, BaseDirector } from '@unchainedshop/utils';
110
- ```
111
-
112
- - **BaseDirector**: Factory function that creates a generic director with methods to register, unregister, and retrieve adapters
113
- - **BaseAdapter**: Base implementation providing logging and utility methods for all adapters
114
-
115
- ### Creating a Custom Adapter
116
-
117
- All adapters extend from `BaseAdapter` and must implement:
19
+ import { initCore } from '@unchainedshop/core';
118
20
 
119
- ```typescript
120
- const MyAdapter = {
121
- key: 'my-adapter', // Unique identifier
122
- label: 'My Custom Adapter', // Human-readable label
123
- version: '1.0.0', // Adapter version
124
-
125
- // Adapter-specific methods...
126
- };
127
-
128
- // Register with the appropriate director
129
- SomeDirector.registerAdapter(MyAdapter);
21
+ const core = await initCore({ db, migrationRepository });
22
+ const products = await core.modules.products.findProducts({});
130
23
  ```
131
24
 
132
- ### Payment Director
133
-
134
- Manages payment processing and orchestrates payment adapters.
135
-
136
- ```typescript
137
- import { PaymentDirector, type IPaymentAdapter } from '@unchainedshop/core';
138
-
139
- const MyPaymentAdapter: IPaymentAdapter = {
140
- key: 'my-payment',
141
- label: 'My Payment Gateway',
142
- version: '1.0.0',
25
+ `initCore()` accepts `options` for built-in modules, `modules` containing custom `{ configure }` factories, `services` for custom services, and `bulkImporter`/`bulkExporter` handler configuration. Built-in modules are included automatically. Platform startup additionally initializes plugin modules and lifecycle hooks, accounts, templates, HTTP API, and workers.
143
26
 
144
- typeSupported(type) {
145
- return type === 'CARD';
146
- },
27
+ ## Modules and services
147
28
 
148
- actions(config, context) {
149
- return {
150
- configurationError() { return null; },
151
- isActive() { return true; },
152
- isPayLaterAllowed() { return false; },
153
-
154
- async charge() {
155
- // Process payment charge
156
- return { transactionId: '...' };
157
- },
158
- async confirm() {
159
- // Confirm payment
160
- return { transactionId: '...' };
161
- },
162
- async cancel() {
163
- // Cancel payment
164
- return true;
165
- },
166
- async register() {
167
- // Register payment method
168
- return { token: '...' };
169
- },
170
- async sign() {
171
- // Sign payment request
172
- return '...';
173
- },
174
- async validate(token) {
175
- // Validate payment token
176
- return true;
177
- },
178
- };
179
- },
180
- };
29
+ `core.modules` exposes `products`, `assortments`, `filters`, `orders`, `users`, `payment`, `delivery`, `warehousing`, `enrollments`, `quotations`, `bookmarks`, `countries`, `currencies`, `languages`, `files`, `events`, and `worker`.
181
30
 
182
- PaymentDirector.registerAdapter(MyPaymentAdapter);
183
- ```
31
+ `core.services` coordinates operations across modules, including pricing, checkout, payment, delivery, filtering, files, and enrollment processing. See [module configuration](src/modules.ts) and [service composition](src/services/index.ts) for their current interfaces.
184
32
 
185
- ### Delivery Director
33
+ ## Plugin registration
186
34
 
187
- Manages delivery operations and coordinates shipping adapters.
35
+ Register built-in plugins explicitly before platform startup. Importing a plugin has no registration side effect:
188
36
 
189
37
  ```typescript
190
- import { DeliveryDirector, type IDeliveryAdapter } from '@unchainedshop/core';
191
-
192
- const MyDeliveryAdapter: IDeliveryAdapter = {
193
- key: 'my-delivery',
194
- label: 'My Shipping Provider',
195
- version: '1.0.0',
38
+ import { pluginRegistry } from '@unchainedshop/core';
39
+ import { StripePlugin } from '@unchainedshop/plugins/payment/stripe';
196
40
 
197
- typeSupported(type) {
198
- return type === 'SHIPPING';
199
- },
200
-
201
- actions(config, context) {
202
- return {
203
- configurationError() { return null; },
204
- isActive() { return true; },
205
- isAutoReleaseAllowed() { return false; },
206
-
207
- async send() {
208
- // Trigger delivery
209
- return { trackingNumber: '...' };
210
- },
211
- estimatedDeliveryThroughput(warehousingTime) {
212
- // Return estimated delivery time in ms
213
- return 3 * 24 * 60 * 60 * 1000; // 3 days
214
- },
215
- async pickUpLocations() {
216
- // Return available pickup locations
217
- return [];
218
- },
219
- async pickUpLocationById(locationId) {
220
- return null;
221
- },
222
- };
223
- },
224
- };
225
-
226
- DeliveryDirector.registerAdapter(MyDeliveryAdapter);
41
+ pluginRegistry.register(StripePlugin);
227
42
  ```
228
43
 
229
- ### Warehousing Director
230
-
231
- Manages inventory and stock operations, including NFT/token support.
232
-
233
- ```typescript
234
- import { WarehousingDirector, type IWarehousingAdapter } from '@unchainedshop/core';
235
-
236
- const MyWarehousingAdapter: IWarehousingAdapter = {
237
- key: 'my-warehouse',
238
- label: 'My Inventory System',
239
- version: '1.0.0',
240
-
241
- typeSupported(type) {
242
- return type === 'PHYSICAL';
243
- },
244
-
245
- actions(config, context) {
246
- return {
247
- configurationError() { return null; },
248
- isActive() { return true; },
249
-
250
- async stock(referenceDate) {
251
- // Return current stock quantity
252
- return 100;
253
- },
254
- async productionTime(quantity) {
255
- // Return production time in ms
256
- return 0;
257
- },
258
- async commissioningTime(quantity) {
259
- // Return commissioning time in ms
260
- return 24 * 60 * 60 * 1000; // 1 day
261
- },
262
- async estimatedStock() {
263
- return 100;
264
- },
265
- async estimatedDispatch() {
266
- return new Date();
267
- },
268
- // For tokenized products (NFTs):
269
- async tokenize() { return []; },
270
- async tokenMetadata(serial, date) { return {}; },
271
- async isInvalidateable(serial, date) { return false; },
272
- };
273
- },
274
- };
44
+ Alternatively, call `registerBasePlugins()`, `registerAllPlugins()`, or `registerCryptoPlugins()` from the corresponding `@unchainedshop/plugins/presets/base`, `/all`, or `/crypto` subpath. Package subpaths have no file extension.
275
45
 
276
- WarehousingDirector.registerAdapter(MyWarehousingAdapter);
277
- ```
46
+ Directors resolve adapters from `pluginRegistry` by the `adapterType` symbol provided by each domain's base adapter. `getAllAdapters()` lists adapters across the core directors.
278
47
 
279
- ### Worker Director
48
+ ### Custom adapters
280
49
 
281
- Manages background job processing and scheduled tasks.
50
+ The typed `registerX()` factories both create and register an `IPlugin`. Call them before platform startup:
282
51
 
283
52
  ```typescript
284
- import { WorkerDirector, type IWorkerAdapter } from '@unchainedshop/core';
285
-
286
- interface MyInput { email: string; subject: string; }
287
- interface MyOutput { messageId: string; }
288
-
289
- const MyWorkerAdapter: IWorkerAdapter<MyInput, MyOutput> = {
290
- key: 'my-worker',
291
- label: 'My Background Worker',
292
- version: '1.0.0',
293
- type: 'MY_WORK_TYPE', // Work type identifier
294
- external: false, // Runs in-process
295
- maxParallelAllocations: 10, // Max concurrent executions
296
-
297
- async doWork(input, unchainedAPI, workId) {
298
- // Process the work item
299
- const { email, subject } = input;
300
-
301
- // Return result
302
- return {
303
- success: true,
304
- result: { messageId: 'msg-123' },
305
- };
306
- },
307
- };
308
-
309
- WorkerDirector.registerAdapter(MyWorkerAdapter);
310
-
311
- // Schedule recurring work
312
- WorkerDirector.configureAutoscheduling({
313
- type: 'MY_WORK_TYPE',
314
- input: { email: 'test@example.com', subject: 'Test' },
315
- schedule: '0 * * * *', // Every hour (cron syntax)
53
+ import {
54
+ registerPaymentProvider,
55
+ registerDeliveryProvider,
56
+ registerPhysicalWarehousing,
57
+ registerProductPricing,
58
+ } from '@unchainedshop/core';
59
+
60
+ registerPaymentProvider({
61
+ adapterId: 'manual-payment',
62
+ isPayLaterAllowed: true,
63
+ charge: false,
316
64
  });
317
- ```
318
-
319
- ### Pricing Directors
320
65
 
321
- Pricing directors calculate prices using a chain of adapters. Each adapter can add, modify, or discount prices.
322
-
323
- ```typescript
324
- import { ProductPricingDirector, type IProductPricingAdapter } from '@unchainedshop/core';
325
-
326
- const MyPricingAdapter: IProductPricingAdapter = {
327
- key: 'my-pricing',
328
- label: 'My Pricing Logic',
329
- version: '1.0.0',
330
- orderIndex: 10, // Lower numbers run first
66
+ registerDeliveryProvider({
67
+ adapterId: 'manual-shipping',
68
+ autoReleaseAllowed: false,
69
+ send: true,
70
+ });
331
71
 
332
- isActivatedFor(context) {
333
- // Return true if this adapter should apply
334
- return true;
335
- },
72
+ registerPhysicalWarehousing({
73
+ adapterId: 'local-stock',
74
+ stock: 100,
75
+ commissioningTime: 24 * 60 * 60 * 1000,
76
+ });
336
77
 
337
- actions(params) {
338
- return {
339
- calculate() {
340
- // Add price calculations
341
- this.calculation.addItem({
342
- category: 'BASE',
343
- amount: 1000, // in smallest currency unit
344
- });
345
- },
346
- };
78
+ registerProductPricing({
79
+ adapterId: 'fixed-price',
80
+ orderIndex: 0,
81
+ calculate: async (sheet) => {
82
+ sheet.addItem({ amount: 1000, isTaxable: true, isNetPrice: false });
347
83
  },
348
- };
349
-
350
- ProductPricingDirector.registerAdapter(MyPricingAdapter);
84
+ });
351
85
  ```
352
86
 
353
- ### Discount Directors
87
+ The pricing example supplies a fixed base price in the smallest currency unit. Choose the pricing adapters appropriate to your shop so multiple adapters do not unintentionally add base prices.
354
88
 
355
- Discount directors manage coupon codes and automatic discounts.
89
+ | Concern | Factories |
90
+ |---------|-----------|
91
+ | Payment | `registerPaymentProvider`, `registerInvoicePayment` |
92
+ | Delivery | `registerDeliveryProvider`, `registerShippingDelivery`, `registerPickUpDelivery` |
93
+ | Warehousing | `registerPhysicalWarehousing`, `registerVirtualWarehousing` |
94
+ | Pricing | `registerProductPricing`, `registerOrderPricing`, `registerPaymentPricing`, `registerDeliveryPricing` |
95
+ | Discounts | `registerProductDiscount`, `registerOrderDiscount` |
96
+ | Filtering | `registerProductDiscoverabilityFilter`, `registerProductSearchFilter`, `registerAssortmentSearchFilter` |
97
+ | Files | `registerFileAdapter` |
98
+ | Quotations | `registerQuotation` |
99
+ | Enrollments | `registerEnrollment` |
100
+ | Workers | `registerWorker` |
356
101
 
357
- ```typescript
358
- import { OrderDiscountDirector, type IDiscountAdapter } from '@unchainedshop/core';
102
+ See the [factory implementations](src/factory/index.ts) for typed callback signatures. For capabilities beyond the factories, spread the relevant domain base adapter (for example `PaymentAdapter`) and its `actions()` defaults, then put the adapter in an `IPlugin` and call `pluginRegistry.register(plugin)`. This preserves the adapter's type symbol and required default methods.
359
103
 
360
- const MyDiscountAdapter: IDiscountAdapter = {
361
- key: 'my-discount',
362
- label: 'My Discount System',
363
- version: '1.0.0',
364
- orderIndex: 10,
104
+ ### Workers and recurring schedules
365
105
 
366
- isManualAdditionAllowed(code) {
367
- return code.startsWith('PROMO');
368
- },
369
- isManualRemovalAllowed() {
370
- return true;
371
- },
106
+ ```typescript
107
+ import { registerWorker, schedule, WorkerDirector } from '@unchainedshop/core';
372
108
 
373
- actions(context) {
374
- return {
375
- isValidForSystemTriggering() {
376
- // Auto-apply discount?
377
- return false;
378
- },
379
- isValidForCodeTriggering(code) {
380
- // Apply when code entered?
381
- return code === 'PROMO10';
382
- },
383
- discountForPricingAdapterKey(params) {
384
- // Return discount configuration for pricing adapter
385
- return {
386
- isNetPrice: false,
387
- rate: 0.1, // 10% off
388
- };
389
- },
390
- async reserve(code) {
391
- // Reserve discount (e.g., decrement coupon balance)
392
- },
393
- async release() {
394
- // Release reservation on order cancellation
395
- },
396
- };
109
+ registerWorker<{ message: string }, { logged: boolean }>({
110
+ type: 'LOG_MESSAGE',
111
+ maxParallelAllocations: 1,
112
+ process: async ({ message }) => {
113
+ console.log(message);
114
+ return { logged: true };
397
115
  },
398
- };
116
+ });
399
117
 
400
- OrderDiscountDirector.registerAdapter(MyDiscountAdapter);
118
+ WorkerDirector.configureAutoscheduling({
119
+ type: 'LOG_MESSAGE',
120
+ input: async () => ({ message: 'Hourly check' }),
121
+ schedule: schedule.parse.cron('0 * * * *'),
122
+ });
401
123
  ```
402
124
 
403
- ### Filter Director
404
-
405
- Manages product filtering and search functionality.
406
-
407
- ```typescript
408
- import { FilterDirector, type IFilterAdapter } from '@unchainedshop/core';
409
-
410
- const MyFilterAdapter: IFilterAdapter = {
411
- key: 'my-filter',
412
- label: 'My Search Filter',
413
- version: '1.0.0',
414
- orderIndex: 10,
415
-
416
- actions(context) {
417
- return {
418
- async aggregateProductIds(params) {
419
- // Return product IDs matching filter
420
- return ['product-1', 'product-2'];
421
- },
422
- async searchProducts(params, options) {
423
- // Search products
424
- return { productIds: [], totalCount: 0 };
425
- },
426
- async searchAssortments(params, options) {
427
- // Search assortments
428
- return { assortmentIds: [], totalCount: 0 };
429
- },
430
- transformProductSelector(selector, options) {
431
- // Modify MongoDB product selector
432
- return selector;
433
- },
434
- transformFilterSelector(selector, options) {
435
- // Modify MongoDB filter selector
436
- return selector;
437
- },
438
- transformSortStage(sort, options) {
439
- // Modify MongoDB sort stage
440
- return sort;
441
- },
442
- };
443
- },
444
- };
445
-
446
- FilterDirector.registerAdapter(MyFilterAdapter);
447
- ```
125
+ Autoscheduling accepts parsed `ScheduleData` and an asynchronous input factory. The platform's queue managers enqueue and execute the scheduled work.
448
126
 
449
- ### Messaging Director
127
+ ### Messaging templates
450
128
 
451
- The Messaging Director uses a template resolver pattern instead of traditional adapters.
129
+ Messaging uses template resolvers registered separately from adapters:
452
130
 
453
131
  ```typescript
454
132
  import { MessagingDirector } from '@unchainedshop/core';
455
133
 
456
- // Register a message template
457
- MessagingDirector.registerTemplate('ORDER_CONFIRMATION', async (context) => {
458
- const { order, user } = context;
459
-
460
- return [
461
- {
462
- type: 'EMAIL',
463
- input: {
464
- to: user.email,
465
- subject: `Order Confirmation #${order.orderNumber}`,
466
- html: '<h1>Thank you for your order!</h1>',
467
- },
134
+ MessagingDirector.registerTemplate('CUSTOM_NOTICE', async ({ email }) => [
135
+ {
136
+ type: 'EMAIL',
137
+ input: {
138
+ from: 'shop@example.com',
139
+ to: email,
140
+ subject: 'Shop notice',
141
+ text: 'Your requested notification.',
468
142
  },
469
- {
470
- type: 'SMS',
471
- input: {
472
- to: user.phone,
473
- text: `Order #${order.orderNumber} confirmed!`,
474
- },
475
- },
476
- ];
477
- });
478
- ```
479
-
480
- ### Quotation Director
481
-
482
- Handles quotation/RFQ (Request for Quote) operations.
483
-
484
- ```typescript
485
- import { QuotationDirector, type IQuotationAdapter } from '@unchainedshop/core';
486
-
487
- const MyQuotationAdapter: IQuotationAdapter = {
488
- key: 'my-quotation',
489
- label: 'My Quote System',
490
- version: '1.0.0',
491
-
492
- isActivatedFor(quotationContext, unchainedAPI) {
493
- return true;
494
143
  },
495
-
496
- actions(context) {
497
- return {
498
- configurationError() { return null; },
499
- isManualProposalRequired() { return true; },
500
- isManualRequestVerificationRequired() { return false; },
501
-
502
- async quote() {
503
- // Generate quote
504
- return { price: 1000, currency: 'CHF' };
505
- },
506
- async submitRequest(quotationContext) {
507
- // Submit RFQ
508
- },
509
- async verifyRequest(quotationContext) {
510
- // Verify RFQ
511
- },
512
- async rejectRequest(quotationContext) {
513
- // Reject RFQ
514
- },
515
- transformItemConfiguration(params) {
516
- return params.configuration;
517
- },
518
- };
519
- },
520
- };
521
-
522
- QuotationDirector.registerAdapter(MyQuotationAdapter);
523
- ```
524
-
525
- ### Enrollment Director
526
-
527
- Manages subscription/enrollment plans and recurring billing.
528
-
529
- ```typescript
530
- import { EnrollmentDirector, type IEnrollmentAdapter } from '@unchainedshop/core';
531
-
532
- const MyEnrollmentAdapter: IEnrollmentAdapter = {
533
- key: 'my-enrollment',
534
- label: 'My Subscription System',
535
- version: '1.0.0',
536
-
537
- isActivatedFor(productPlan) {
538
- return productPlan.type === 'PLAN_PRODUCT';
539
- },
540
-
541
- transformOrderItemToEnrollmentPlan(orderPosition, unchainedAPI) {
542
- return {
543
- configuration: orderPosition.configuration,
544
- };
545
- },
546
-
547
- actions(context) {
548
- return {
549
- async nextPeriod() {
550
- // Calculate next billing period
551
- return {
552
- start: new Date(),
553
- end: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
554
- };
555
- },
556
- isValidForActivation() {
557
- return true;
558
- },
559
- isOverdue() {
560
- return false;
561
- },
562
- async configurationForOrder(period) {
563
- // Return order configuration for period
564
- return {};
565
- },
566
- };
567
- },
568
- };
569
-
570
- EnrollmentDirector.registerAdapter(MyEnrollmentAdapter);
144
+ ]);
571
145
  ```
572
146
 
573
- ### Bulk Importer
147
+ Each returned item is work for its registered worker type, such as `EMAIL` or `TWILIO`. Template parameters depend on the caller; they are not automatically populated with order/user documents.
574
148
 
575
- | Method | Description |
576
- |--------|-------------|
577
- | `bulkImporter.prepare` | Prepare bulk import for entity type |
578
- | `bulkImporter.process` | Process prepared import data |
149
+ ## Bulk import and export
579
150
 
580
- ### Types
151
+ `core.bulkImporter.createBulkImporter(options)` creates an import session with `validate(event)`, `prepare(event, core)`, `execute()`, and `invalidateCaches(core)`. Events carry `entity`, `operation`, and `payload`; the built-in entities are `PRODUCT`, `ASSORTMENT`, and `FILTER`.
581
152
 
582
- | Export | Description |
583
- |--------|-------------|
584
- | `UnchainedCore` | Core instance type |
585
- | `UnchainedCoreOptions` | Initialization options |
586
- | `Modules` | All modules type |
587
- | `Services` | All services type |
588
- | `BulkImporter` | Bulk importer type |
153
+ For streams, use `validateEventStream(readStream)` and `pipeEventStream(readStream, importer, core)`, then execute the import and invalidate caches. See the [importer](src/bulk-importer/createBulkImporter.ts) and [exporter](src/bulk-exporter/index.ts) for the full interfaces.
589
154
 
590
- ## Configuration
155
+ ## Types
591
156
 
592
- ```typescript
593
- const core = await initCore({
594
- db,
595
- migrationRepository,
596
- modules: {
597
- // Custom modules
598
- },
599
- services: {
600
- // Custom services
601
- },
602
- bulkImporter: {
603
- handlers: {
604
- // Custom import handlers
605
- },
606
- },
607
- options: {
608
- // Module-specific options
609
- },
610
- });
611
- ```
157
+ The package exports `UnchainedCore`, `UnchainedCoreOptions`, `Services`, `BulkImporter`, `BulkExporter`, and `IPlugin`, along with domain adapter/context and pricing sheet types. Access the composed module type through `UnchainedCore['modules']`.
612
158
 
613
159
  ## License
614
160
 
@@ -15,7 +15,7 @@ export declare const ProductCreateSpecificationSchema: z.ZodMiniObject<{
15
15
  readonly ACTIVE: "ACTIVE";
16
16
  readonly DELETED: "DELETED";
17
17
  }>>>;
18
- published: z.ZodMiniOptional<z.ZodMiniNullable<z.iso.ZodMiniISODateTime>>;
18
+ published: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
19
19
  tags: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
20
20
  commerce: z.ZodMiniOptional<z.ZodMiniObject<{
21
21
  pricing: z.ZodMiniArray<z.ZodMiniObject<{
@@ -76,7 +76,7 @@ export declare const ProductCreatePayloadSchema: z.ZodMiniObject<{
76
76
  readonly ACTIVE: "ACTIVE";
77
77
  readonly DELETED: "DELETED";
78
78
  }>>>;
79
- published: z.ZodMiniOptional<z.ZodMiniNullable<z.iso.ZodMiniISODateTime>>;
79
+ published: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
80
80
  tags: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
81
81
  commerce: z.ZodMiniOptional<z.ZodMiniObject<{
82
82
  pricing: z.ZodMiniArray<z.ZodMiniObject<{
@@ -183,7 +183,7 @@ declare namespace createProduct {
183
183
  readonly ACTIVE: "ACTIVE";
184
184
  readonly DELETED: "DELETED";
185
185
  }>>>;
186
- published: z.ZodMiniOptional<z.ZodMiniNullable<z.iso.ZodMiniISODateTime>>;
186
+ published: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
187
187
  tags: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
188
188
  commerce: z.ZodMiniOptional<z.ZodMiniObject<{
189
189
  pricing: z.ZodMiniArray<z.ZodMiniObject<{
@@ -8,7 +8,7 @@ export const ProductCreateSpecificationSchema = z.object({
8
8
  type: z.enum(ProductType),
9
9
  sequence: z.optional(z.number()),
10
10
  status: z.nullish(z.enum(ProductStatus)),
11
- published: z.nullish(z.iso.datetime()),
11
+ published: z.nullish(z.string().check(z.refine((value) => !Number.isNaN(new Date(value).getTime()), 'Invalid date'))),
12
12
  tags: z.optional(z.array(z.string())),
13
13
  commerce: z.optional(z.object({
14
14
  pricing: z.array(z.object({
@@ -9,7 +9,7 @@ export declare const ProductUpdateSpecificationSchema: z.ZodMiniObject<{
9
9
  readonly ACTIVE: "ACTIVE";
10
10
  readonly DELETED: "DELETED";
11
11
  }>>>;
12
- published: z.ZodMiniOptional<z.ZodMiniNullable<z.iso.ZodMiniISODateTime>>;
12
+ published: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
13
13
  tags: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
14
14
  commerce: z.ZodMiniOptional<z.ZodMiniObject<{
15
15
  pricing: z.ZodMiniArray<z.ZodMiniObject<{
@@ -64,7 +64,7 @@ export declare const ProductUpdatePayloadSchema: z.ZodMiniObject<{
64
64
  readonly ACTIVE: "ACTIVE";
65
65
  readonly DELETED: "DELETED";
66
66
  }>>>;
67
- published: z.ZodMiniOptional<z.ZodMiniNullable<z.iso.ZodMiniISODateTime>>;
67
+ published: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
68
68
  tags: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
69
69
  commerce: z.ZodMiniOptional<z.ZodMiniObject<{
70
70
  pricing: z.ZodMiniArray<z.ZodMiniObject<{
@@ -165,7 +165,7 @@ declare namespace updateProduct {
165
165
  readonly ACTIVE: "ACTIVE";
166
166
  readonly DELETED: "DELETED";
167
167
  }>>>;
168
- published: z.ZodMiniOptional<z.ZodMiniNullable<z.iso.ZodMiniISODateTime>>;
168
+ published: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
169
169
  tags: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
170
170
  commerce: z.ZodMiniOptional<z.ZodMiniObject<{
171
171
  pricing: z.ZodMiniArray<z.ZodMiniObject<{
@@ -8,7 +8,7 @@ export const ProductUpdateSpecificationSchema = z.object({
8
8
  type: z.optional(z.string()),
9
9
  sequence: z.optional(z.number()),
10
10
  status: z.nullish(z.enum(ProductStatus)),
11
- published: z.nullish(z.iso.datetime()),
11
+ published: z.nullish(z.string().check(z.refine((value) => !Number.isNaN(new Date(value).getTime()), 'Invalid date'))),
12
12
  tags: z.optional(z.array(z.string())),
13
13
  commerce: z.optional(z.object({
14
14
  pricing: z.array(z.object({
@@ -11,9 +11,10 @@ export const BaseDiscountDirector = (directorName) => {
11
11
  resolveDiscountAdapterFromStaticCode: async (options) => {
12
12
  if (!context.order)
13
13
  return null;
14
- const discounts = await Promise.all(this.getAdapters()
15
- .filter((Adapter) => Adapter.isManualAdditionAllowed(options?.code))
16
- .map(async (Adapter) => {
14
+ const discounts = await Promise.all(this.getAdapters().map(async (Adapter) => {
15
+ if (!(await Adapter.isManualAdditionAllowed(options?.code))) {
16
+ return { Adapter, isValid: false };
17
+ }
17
18
  const adapter = await Adapter.actions({ context });
18
19
  return {
19
20
  Adapter,
@@ -48,9 +48,10 @@ export const BaseWorker = {
48
48
  retries: workConfig.retries || 0,
49
49
  };
50
50
  if (workConfig.input) {
51
- workData.input = (await workConfig.input(workData)) || {};
52
- if (!workData.input)
51
+ const input = await workConfig.input(workData);
52
+ if (!input)
53
53
  return null;
54
+ workData.input = input;
54
55
  }
55
56
  return unchainedAPI.modules.worker.ensureOneWork(workData);
56
57
  }));
@@ -33,5 +33,6 @@ export interface IOrderPricingSheet extends IPricingSheet<OrderPricingCalculatio
33
33
  taxAmount: number;
34
34
  meta?: any;
35
35
  }) => void;
36
+ discountSum: (discountId?: string) => number;
36
37
  }
37
38
  export declare const OrderPricingSheet: (params: PricingSheetParams<OrderPricingCalculation>) => IOrderPricingSheet;
@@ -15,6 +15,7 @@ export const OrderPricingSheet = (params) => {
15
15
  amount: taxAmount,
16
16
  baseCategory: category,
17
17
  discountId,
18
+ isNetPrice: false,
18
19
  meta,
19
20
  });
20
21
  }
@@ -25,6 +26,7 @@ export const OrderPricingSheet = (params) => {
25
26
  basePricingSheet.calculation.push({
26
27
  category: OrderPricingRowCategory.Items,
27
28
  amount,
29
+ isNetPrice: true,
28
30
  meta,
29
31
  });
30
32
  addTaxIfAvailabile(OrderPricingRowCategory.Items, taxAmount, meta);
@@ -34,6 +36,7 @@ export const OrderPricingSheet = (params) => {
34
36
  category: OrderPricingRowCategory.Discounts,
35
37
  amount,
36
38
  discountId,
39
+ isNetPrice: true,
37
40
  meta,
38
41
  });
39
42
  addTaxIfAvailabile(OrderPricingRowCategory.Discounts, taxAmount, meta, discountId);
@@ -42,6 +45,7 @@ export const OrderPricingSheet = (params) => {
42
45
  basePricingSheet.calculation.push({
43
46
  category: OrderPricingRowCategory.Delivery,
44
47
  amount,
48
+ isNetPrice: true,
45
49
  meta,
46
50
  });
47
51
  addTaxIfAvailabile(OrderPricingRowCategory.Delivery, taxAmount, meta);
@@ -50,6 +54,7 @@ export const OrderPricingSheet = (params) => {
50
54
  basePricingSheet.calculation.push({
51
55
  category: OrderPricingRowCategory.Payment,
52
56
  amount,
57
+ isNetPrice: true,
53
58
  meta,
54
59
  });
55
60
  addTaxIfAvailabile(OrderPricingRowCategory.Payment, taxAmount, meta);
@@ -60,20 +65,11 @@ export const OrderPricingSheet = (params) => {
60
65
  ...(filter || {}),
61
66
  });
62
67
  },
63
- gross() {
64
- return basePricingSheet.sum() - this.taxSum();
65
- },
66
- net() {
67
- return basePricingSheet.sum() - this.taxSum() - this.taxSum();
68
- },
69
- total({ category, useNetPrice, discountId } = { useNetPrice: false }) {
70
- const taxAmount = this.taxSum({ baseCategory: category, discountId });
71
- const amount = this.sum({ category, discountId }) - taxAmount;
72
- const netAmount = !category ? amount - taxAmount : amount;
73
- return {
74
- amount: Math.round(useNetPrice ? netAmount : netAmount + taxAmount),
75
- currencyCode: this.currencyCode,
76
- };
68
+ discountSum(discountId) {
69
+ return basePricingSheet.sum({
70
+ category: OrderPricingRowCategory.Discounts,
71
+ discountId,
72
+ });
77
73
  },
78
74
  discountPrices(explicitDiscountId) {
79
75
  const discountIds = pricingSheet
@@ -85,17 +81,21 @@ export const OrderPricingSheet = (params) => {
85
81
  .filter(Boolean);
86
82
  return [...new Set(discountIds)]
87
83
  .map((discountId) => {
88
- const amount = basePricingSheet.sum({
84
+ const amount = pricingSheet.sum({
89
85
  category: OrderPricingRowCategory.Discounts,
90
86
  discountId,
91
- });
87
+ }) +
88
+ pricingSheet.taxSum({
89
+ baseCategory: OrderPricingRowCategory.Discounts,
90
+ discountId,
91
+ });
92
92
  if (!amount) {
93
93
  return null;
94
94
  }
95
95
  return {
96
96
  discountId,
97
97
  amount: Math.round(amount),
98
- currencyCode: basePricingSheet.currencyCode,
98
+ currencyCode: pricingSheet.currencyCode,
99
99
  };
100
100
  })
101
101
  .filter(Boolean);
@@ -18,6 +18,12 @@ export type IWorkerDirector = IBaseDirector<IWorkerAdapter<any, any>> & {
18
18
  configureAutoscheduling: (workScheduleConfiguration: WorkScheduleConfiguration) => void;
19
19
  getAutoSchedules: () => [string, WorkScheduleConfiguration][];
20
20
  doWork: (work: Work, unchainedAPI: any) => Promise<WorkResult>;
21
+ allocateWork: (unchainedAPI: {
22
+ modules: Modules;
23
+ }, options?: {
24
+ types?: string[] | null;
25
+ worker?: string;
26
+ }) => Promise<Work | null>;
21
27
  processNextWork: (unchainedAPI: {
22
28
  modules: Modules;
23
29
  }, workerId?: string) => Promise<Work | null>;
@@ -57,22 +57,34 @@ export const WorkerDirector = {
57
57
  return errorOutput;
58
58
  }
59
59
  },
60
+ allocateWork: async (unchainedAPI, { types: requestedTypes, worker = unchainedAPI.modules.worker.workerId } = {}) => {
61
+ const lock = await unchainedAPI.modules.orders.acquireLock('allocation', 'worker');
62
+ try {
63
+ const adapters = WorkerDirector.getAdapters();
64
+ const allocationMap = await unchainedAPI.modules.worker.allocationMap();
65
+ const types = adapters
66
+ .filter((adapter) => {
67
+ if (requestedTypes && !requestedTypes.includes(adapter.type))
68
+ return false;
69
+ if (adapter.maxParallelAllocations &&
70
+ adapter.maxParallelAllocations <= allocationMap[adapter.type])
71
+ return false;
72
+ return true;
73
+ })
74
+ .map((adapter) => adapter.type);
75
+ return await unchainedAPI.modules.worker.allocateWork({
76
+ types,
77
+ worker,
78
+ });
79
+ }
80
+ finally {
81
+ await lock.release();
82
+ }
83
+ },
60
84
  processNextWork: async (unchainedAPI, workerId) => {
61
- const adapters = WorkerDirector.getAdapters();
62
- const allocationMap = await unchainedAPI.modules.worker.allocationMap();
63
- const types = adapters
64
- .filter((adapter) => {
65
- if (adapter.external)
66
- return false;
67
- if (adapter.maxParallelAllocations &&
68
- adapter.maxParallelAllocations <= allocationMap[adapter.type])
69
- return false;
70
- return true;
71
- })
72
- .map((adapter) => adapter.type);
73
85
  const worker = workerId ?? unchainedAPI.modules.worker.workerId;
74
- const work = await unchainedAPI.modules.worker.allocateWork({
75
- types,
86
+ const work = await WorkerDirector.allocateWork(unchainedAPI, {
87
+ types: WorkerDirector.getActivePluginTypes({ external: false }),
76
88
  worker,
77
89
  });
78
90
  if (work) {
@@ -0,0 +1,8 @@
1
+ import type { Modules } from '../modules.ts';
2
+ export declare function bulkAssignProductsToAssortmentService(this: Modules, { assortmentId, productIds }: {
3
+ assortmentId: string;
4
+ productIds: string[];
5
+ }): Promise<{
6
+ successIds: string[];
7
+ failedIds: string[];
8
+ }>;
@@ -0,0 +1,11 @@
1
+ import { executeBulkOperation } from '@unchainedshop/utils';
2
+ export async function bulkAssignProductsToAssortmentService({ assortmentId, productIds }) {
3
+ const result = await executeBulkOperation(productIds, async (productId) => {
4
+ if (!(await this.products.productExists({ productId })))
5
+ throw new Error('not-found');
6
+ const assignment = await this.assortments.products.create({ assortmentId, productId });
7
+ if (!assignment)
8
+ throw new Error('create-failed');
9
+ });
10
+ return result;
11
+ }
@@ -0,0 +1,7 @@
1
+ import type { Modules } from '../modules.ts';
2
+ export declare function bulkDeleteUsersService(this: Modules, { userIds }: {
3
+ userIds: string[];
4
+ }): Promise<{
5
+ successIds: string[];
6
+ failedIds: string[];
7
+ }>;
@@ -0,0 +1,11 @@
1
+ import { executeBulkOperation } from '@unchainedshop/utils';
2
+ import { deleteUserService } from "./deleteUser.js";
3
+ export async function bulkDeleteUsersService({ userIds }) {
4
+ return executeBulkOperation(userIds, async (userId) => {
5
+ if (!(await this.users.userExists({ userId })))
6
+ throw new Error('not-found');
7
+ const result = await deleteUserService.call(this, { userId });
8
+ if (!result)
9
+ throw new Error('delete-failed');
10
+ });
11
+ }
@@ -0,0 +1,7 @@
1
+ import type { Modules } from '../modules.ts';
2
+ export declare function bulkRemoveFiltersService(this: Modules, { filterIds }: {
3
+ filterIds: string[];
4
+ }): Promise<{
5
+ successIds: string[];
6
+ failedIds: string[];
7
+ }>;
@@ -0,0 +1,10 @@
1
+ import { executeBulkOperation } from '@unchainedshop/utils';
2
+ import { removeFilterService } from "./removeFilter.js";
3
+ export async function bulkRemoveFiltersService({ filterIds }) {
4
+ return executeBulkOperation(filterIds, async (filterId) => {
5
+ const filter = await this.filters.findFilter({ filterId });
6
+ if (!filter)
7
+ throw new Error('not-found');
8
+ await removeFilterService.call(this, { filter });
9
+ });
10
+ }
@@ -0,0 +1,7 @@
1
+ import type { Modules } from '../modules.ts';
2
+ export declare function bulkRemoveProductsService(this: Modules, { productIds }: {
3
+ productIds: string[];
4
+ }): Promise<{
5
+ successIds: string[];
6
+ failedIds: string[];
7
+ }>;
@@ -0,0 +1,23 @@
1
+ import { executeBulkOperation } from '@unchainedshop/utils';
2
+ import { removeProductService } from "./removeProduct.js";
3
+ export async function bulkRemoveProductsService({ productIds }) {
4
+ return executeBulkOperation(productIds, async (productId) => {
5
+ if (!(await this.products.productExists({ productId })))
6
+ throw new Error('not-found');
7
+ if (await this.products.firstActiveProductBundle(productId)) {
8
+ throw new Error('linked-to-active-bundle');
9
+ }
10
+ if (await this.products.firstActiveProductProxy(productId)) {
11
+ throw new Error('linked-to-active-variation');
12
+ }
13
+ if (await this.quotations.openQuotationWithProduct({ productId })) {
14
+ throw new Error('linked-to-quotation');
15
+ }
16
+ if (await this.enrollments.openEnrollmentWithProduct({ productId })) {
17
+ throw new Error('linked-to-enrollment');
18
+ }
19
+ const result = await removeProductService.call(this, { productId });
20
+ if (!result)
21
+ throw new Error('already-deleted');
22
+ });
23
+ }
@@ -0,0 +1,8 @@
1
+ import type { Modules } from '../modules.ts';
2
+ export declare function bulkSetFilterActiveService(this: Modules, { filterIds, isActive }: {
3
+ filterIds: string[];
4
+ isActive: boolean;
5
+ }): Promise<{
6
+ successIds: string[];
7
+ failedIds: string[];
8
+ }>;
@@ -0,0 +1,12 @@
1
+ import { executeBulkOperation } from '@unchainedshop/utils';
2
+ import { updateFilterService } from "./updateFilter.js";
3
+ export async function bulkSetFilterActiveService({ filterIds, isActive }) {
4
+ return executeBulkOperation(filterIds, async (filterId) => {
5
+ const filter = await this.filters.findFilter({ filterId });
6
+ if (!filter)
7
+ throw new Error('not-found');
8
+ if (!(await updateFilterService.call(this, filterId, { isActive }))) {
9
+ throw new Error('update-failed');
10
+ }
11
+ });
12
+ }
@@ -24,6 +24,7 @@ import { searchProductsService } from './searchProducts.ts';
24
24
  import { calculateDiscountTotalService } from './calculateDiscountTotal.ts';
25
25
  import { registerPaymentCredentialsService } from './registerPaymentCredentials.ts';
26
26
  import { processOrderService } from './processOrder.ts';
27
+ import { processEnrollmentService } from './processEnrollment.ts';
27
28
  import { checkoutOrderService } from './checkoutOrder.ts';
28
29
  import { confirmOrderService } from './confirmOrder.ts';
29
30
  import { rejectOrderService } from './rejectOrder.ts';
@@ -62,6 +63,11 @@ import { simulateConfigurablePriceRangeService } from './simulateConfigurablePri
62
63
  import { createFileDownloadURLService } from './createFileDownloadURL.ts';
63
64
  import { resolveTokenStatusService } from './resolveTokenStatus.ts';
64
65
  import { isTokenInvalidateableService } from './isTokenInvalidateable.ts';
66
+ import { bulkRemoveProductsService } from './bulkRemoveProducts.ts';
67
+ import { bulkRemoveFiltersService } from './bulkRemoveFilters.ts';
68
+ import { bulkDeleteUsersService } from './bulkDeleteUsers.ts';
69
+ import { bulkSetFilterActiveService } from './bulkSetFilterActive.ts';
70
+ import { bulkAssignProductsToAssortmentService } from './bulkAssignProductsToAssortment.ts';
65
71
  export type ServiceInterface = (this: Modules, ...args: any[]) => Promise<any> | any;
66
72
  export type Bound<T extends ServiceInterface> = OmitThisParameter<T>;
67
73
  export type CustomServices = Record<string, ServiceInterface | Record<string, ServiceInterface>>;
@@ -105,18 +111,23 @@ export default function initServices(modules: Modules, customServices?: CustomSe
105
111
  simulateProductDispatching: Bound<typeof simulateProductDispatchingService>;
106
112
  simulateProductInventory: Bound<typeof simulateProductInventoryService>;
107
113
  removeProduct: Bound<typeof removeProductService>;
114
+ bulkRemoveProducts: Bound<typeof bulkRemoveProductsService>;
108
115
  findProductSiblings: Bound<typeof findProductSiblingsService>;
109
116
  simulateConfigurablePriceRange: Bound<typeof simulateConfigurablePriceRangeService>;
110
117
  };
118
+ assortments: {
119
+ bulkAssignProductsToAssortment: Bound<typeof bulkAssignProductsToAssortmentService>;
120
+ };
111
121
  users: {
112
122
  migrateUserData: Bound<typeof migrateUserDataService>;
113
123
  updateUserAvatarAfterUpload: Bound<typeof updateUserAvatarAfterUploadService>;
114
124
  deleteUser: Bound<typeof deleteUserService>;
125
+ bulkDeleteUsers: Bound<typeof bulkDeleteUsersService>;
115
126
  };
116
127
  enrollments: {
117
128
  createEnrollmentFromCheckout: Bound<typeof createEnrollmentFromCheckoutService>;
118
129
  generateOrderFromEnrollment: Bound<typeof generateOrderFromEnrollmentService>;
119
- processEnrollment: Bound<typeof processOrderService>;
130
+ processEnrollment: Bound<typeof processEnrollmentService>;
120
131
  initializeEnrollment: Bound<typeof initializeEnrollmentService>;
121
132
  activateEnrollment: Bound<typeof activateEnrollmentService>;
122
133
  terminateEnrollment: Bound<typeof terminateEnrollmentService>;
@@ -139,6 +150,8 @@ export default function initServices(modules: Modules, customServices?: CustomSe
139
150
  updateFilter: Bound<typeof updateFilterService>;
140
151
  createFilterOption: Bound<typeof createFilterOptionService>;
141
152
  removeFilterOption: Bound<typeof removeFilterOptionService>;
153
+ bulkRemoveFilters: Bound<typeof bulkRemoveFiltersService>;
154
+ bulkSetFilterActive: Bound<typeof bulkSetFilterActiveService>;
142
155
  };
143
156
  warehousing: {
144
157
  ercMetadata: Bound<typeof ercMetadataService>;
@@ -23,6 +23,7 @@ import { searchProductsService } from "./searchProducts.js";
23
23
  import { calculateDiscountTotalService } from "./calculateDiscountTotal.js";
24
24
  import { registerPaymentCredentialsService } from "./registerPaymentCredentials.js";
25
25
  import { processOrderService } from "./processOrder.js";
26
+ import { processEnrollmentService } from "./processEnrollment.js";
26
27
  import { checkoutOrderService } from "./checkoutOrder.js";
27
28
  import { confirmOrderService } from "./confirmOrder.js";
28
29
  import { rejectOrderService } from "./rejectOrder.js";
@@ -61,6 +62,11 @@ import { simulateConfigurablePriceRangeService } from "./simulateConfigurablePri
61
62
  import { createFileDownloadURLService } from "./createFileDownloadURL.js";
62
63
  import { resolveTokenStatusService } from "./resolveTokenStatus.js";
63
64
  import { isTokenInvalidateableService } from "./isTokenInvalidateable.js";
65
+ import { bulkRemoveProductsService } from "./bulkRemoveProducts.js";
66
+ import { bulkRemoveFiltersService } from "./bulkRemoveFilters.js";
67
+ import { bulkDeleteUsersService } from "./bulkDeleteUsers.js";
68
+ import { bulkSetFilterActiveService } from "./bulkSetFilterActive.js";
69
+ import { bulkAssignProductsToAssortmentService } from "./bulkAssignProductsToAssortment.js";
64
70
  function bindMethodsToModules(modules) {
65
71
  return {
66
72
  get(target, prop, receiver) {
@@ -116,18 +122,23 @@ export default function initServices(modules, customServices = {}) {
116
122
  simulateProductDispatching: simulateProductDispatchingService,
117
123
  simulateProductInventory: simulateProductInventoryService,
118
124
  removeProduct: removeProductService,
125
+ bulkRemoveProducts: bulkRemoveProductsService,
119
126
  findProductSiblings: findProductSiblingsService,
120
127
  simulateConfigurablePriceRange: simulateConfigurablePriceRangeService,
121
128
  },
129
+ assortments: {
130
+ bulkAssignProductsToAssortment: bulkAssignProductsToAssortmentService,
131
+ },
122
132
  users: {
123
133
  migrateUserData: migrateUserDataService,
124
134
  updateUserAvatarAfterUpload: updateUserAvatarAfterUploadService,
125
135
  deleteUser: deleteUserService,
136
+ bulkDeleteUsers: bulkDeleteUsersService,
126
137
  },
127
138
  enrollments: {
128
139
  createEnrollmentFromCheckout: createEnrollmentFromCheckoutService,
129
140
  generateOrderFromEnrollment: generateOrderFromEnrollmentService,
130
- processEnrollment: processOrderService,
141
+ processEnrollment: processEnrollmentService,
131
142
  initializeEnrollment: initializeEnrollmentService,
132
143
  activateEnrollment: activateEnrollmentService,
133
144
  terminateEnrollment: terminateEnrollmentService,
@@ -150,6 +161,8 @@ export default function initServices(modules, customServices = {}) {
150
161
  updateFilter: updateFilterService,
151
162
  createFilterOption: createFilterOptionService,
152
163
  removeFilterOption: removeFilterOptionService,
164
+ bulkRemoveFilters: bulkRemoveFiltersService,
165
+ bulkSetFilterActive: bulkSetFilterActiveService,
153
166
  },
154
167
  warehousing: {
155
168
  ercMetadata: ercMetadataService,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unchainedshop/core",
3
3
  "description": "Core orchestration package for the Unchained Engine with business services and directors",
4
- "version": "5.0.0-alpha.4",
4
+ "version": "5.0.0-alpha.7",
5
5
  "main": "lib/core-index.js",
6
6
  "types": "lib/core-index.d.ts",
7
7
  "type": "module",
@@ -35,27 +35,27 @@
35
35
  },
36
36
  "homepage": "https://github.com/unchainedshop/unchained#readme",
37
37
  "dependencies": {
38
- "@unchainedshop/core-assortments": "^5.0.0-alpha.1",
39
- "@unchainedshop/core-bookmarks": "^5.0.0-alpha.1",
40
- "@unchainedshop/core-countries": "^5.0.0-alpha.1",
41
- "@unchainedshop/core-currencies": "^5.0.0-alpha.1",
42
- "@unchainedshop/core-delivery": "^5.0.0-alpha.1",
43
- "@unchainedshop/core-enrollments": "^5.0.0-alpha.1",
44
- "@unchainedshop/core-events": "^5.0.0-alpha.1",
45
- "@unchainedshop/core-files": "^5.0.0-alpha.1",
46
- "@unchainedshop/core-filters": "^5.0.0-alpha.1",
47
- "@unchainedshop/core-languages": "^5.0.0-alpha.1",
48
- "@unchainedshop/core-orders": "^5.0.0-alpha.1",
49
- "@unchainedshop/core-payment": "^5.0.0-alpha.1",
50
- "@unchainedshop/core-products": "^5.0.0-alpha.1",
51
- "@unchainedshop/core-quotations": "^5.0.0-alpha.1",
52
- "@unchainedshop/core-users": "^5.0.0-alpha.1",
53
- "@unchainedshop/core-warehousing": "^5.0.0-alpha.1",
54
- "@unchainedshop/core-worker": "^5.0.0-alpha.1",
55
- "@unchainedshop/events": "^5.0.0-alpha.1",
56
- "@unchainedshop/logger": "^5.0.0-alpha.1",
57
- "@unchainedshop/mongodb": "^5.0.0-alpha.1",
58
- "@unchainedshop/utils": "^5.0.0-alpha.1",
38
+ "@unchainedshop/core-assortments": "^5.0.0-alpha.7",
39
+ "@unchainedshop/core-bookmarks": "^5.0.0-alpha.7",
40
+ "@unchainedshop/core-countries": "^5.0.0-alpha.7",
41
+ "@unchainedshop/core-currencies": "^5.0.0-alpha.7",
42
+ "@unchainedshop/core-delivery": "^5.0.0-alpha.7",
43
+ "@unchainedshop/core-enrollments": "^5.0.0-alpha.7",
44
+ "@unchainedshop/core-events": "^5.0.0-alpha.7",
45
+ "@unchainedshop/core-files": "^5.0.0-alpha.7",
46
+ "@unchainedshop/core-filters": "^5.0.0-alpha.7",
47
+ "@unchainedshop/core-languages": "^5.0.0-alpha.7",
48
+ "@unchainedshop/core-orders": "^5.0.0-alpha.7",
49
+ "@unchainedshop/core-payment": "^5.0.0-alpha.7",
50
+ "@unchainedshop/core-products": "^5.0.0-alpha.7",
51
+ "@unchainedshop/core-quotations": "^5.0.0-alpha.7",
52
+ "@unchainedshop/core-users": "^5.0.0-alpha.7",
53
+ "@unchainedshop/core-warehousing": "^5.0.0-alpha.7",
54
+ "@unchainedshop/core-worker": "^5.0.0-alpha.7",
55
+ "@unchainedshop/events": "^5.0.0-alpha.7",
56
+ "@unchainedshop/logger": "^5.0.0-alpha.7",
57
+ "@unchainedshop/mongodb": "^5.0.0-alpha.7",
58
+ "@unchainedshop/utils": "^5.0.0-alpha.7",
59
59
  "minipass-json-stream": "^1.0.2",
60
60
  "zod": "^4.2.0"
61
61
  },