@unchainedshop/core 4.6.0 → 4.6.2

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.
Files changed (110) hide show
  1. package/lib/bulk-exporter/createBulkExporter.d.ts +33 -0
  2. package/lib/bulk-exporter/createBulkExporter.js +46 -0
  3. package/lib/bulk-exporter/handlers/exportAssortmentsHandler.d.ts +38 -0
  4. package/lib/bulk-exporter/handlers/exportAssortmentsHandler.js +137 -0
  5. package/lib/bulk-exporter/handlers/exportFiltersHandler.d.ts +26 -0
  6. package/lib/bulk-exporter/handlers/exportFiltersHandler.js +88 -0
  7. package/lib/bulk-exporter/handlers/exportProductsHandler.d.ts +40 -0
  8. package/lib/bulk-exporter/handlers/exportProductsHandler.js +238 -0
  9. package/lib/bulk-exporter/handlers/exportUsersHandler.d.ts +40 -0
  10. package/lib/bulk-exporter/handlers/exportUsersHandler.js +340 -0
  11. package/lib/bulk-exporter/handlers/generateCSVFileAndUrl.d.ts +13 -0
  12. package/lib/bulk-exporter/handlers/generateCSVFileAndUrl.js +21 -0
  13. package/lib/bulk-exporter/handlers/toCSV.d.ts +2 -0
  14. package/lib/bulk-exporter/handlers/toCSV.js +5 -0
  15. package/lib/bulk-exporter/index.d.ts +3 -0
  16. package/lib/bulk-exporter/index.js +3 -0
  17. package/lib/core-index.d.ts +7 -1
  18. package/lib/core-index.js +5 -1
  19. package/lib/directors/FileDirector.d.ts +10 -0
  20. package/lib/directors/FileDirector.js +25 -0
  21. package/lib/directors/WorkerAdapter.d.ts +2 -0
  22. package/lib/errors/common.d.ts +568 -0
  23. package/lib/errors/common.js +789 -0
  24. package/lib/errors/index.d.ts +1 -0
  25. package/lib/errors/index.js +1 -0
  26. package/lib/plugins/PluginRegistry.d.ts +42 -0
  27. package/lib/plugins/PluginRegistry.js +113 -0
  28. package/lib/services/addCartDiscount.d.ts +10 -0
  29. package/lib/services/addCartDiscount.js +27 -0
  30. package/lib/services/addCartItem.d.ts +15 -0
  31. package/lib/services/addCartItem.js +41 -0
  32. package/lib/services/addCartQuotation.d.ts +16 -0
  33. package/lib/services/addCartQuotation.js +52 -0
  34. package/lib/services/addEmail.d.ts +7 -0
  35. package/lib/services/addEmail.js +17 -0
  36. package/lib/services/addProductAssignment.d.ts +3 -0
  37. package/lib/services/addProductAssignment.js +63 -0
  38. package/lib/services/authenticateWithPassword.d.ts +15 -0
  39. package/lib/services/authenticateWithPassword.js +34 -0
  40. package/lib/services/changePassword.d.ts +10 -0
  41. package/lib/services/changePassword.js +19 -0
  42. package/lib/services/countOrdersWithFilters.d.ts +3 -0
  43. package/lib/services/countOrdersWithFilters.js +25 -0
  44. package/lib/services/createAssortment.d.ts +7 -0
  45. package/lib/services/createAssortment.js +10 -0
  46. package/lib/services/createBookmark.d.ts +8 -0
  47. package/lib/services/createBookmark.js +21 -0
  48. package/lib/services/createDeliveryProvider.d.ts +6 -0
  49. package/lib/services/createDeliveryProvider.js +19 -0
  50. package/lib/services/createEnrollment.d.ts +17 -0
  51. package/lib/services/createEnrollment.js +33 -0
  52. package/lib/services/createProduct.d.ts +7 -0
  53. package/lib/services/createProduct.js +8 -0
  54. package/lib/services/createProductBundleItem.d.ts +5 -0
  55. package/lib/services/createProductBundleItem.js +25 -0
  56. package/lib/services/createProductReview.d.ts +8 -0
  57. package/lib/services/createProductReview.js +12 -0
  58. package/lib/services/deliverOrder.d.ts +5 -0
  59. package/lib/services/deliverOrder.js +26 -0
  60. package/lib/services/findActiveWorkTypes.d.ts +2 -0
  61. package/lib/services/findActiveWorkTypes.js +6 -0
  62. package/lib/services/findDeliveryInterfaces.d.ts +7 -0
  63. package/lib/services/findDeliveryInterfaces.js +13 -0
  64. package/lib/services/findOrdersWithFilters.d.ts +9 -0
  65. package/lib/services/findOrdersWithFilters.js +25 -0
  66. package/lib/services/findPaymentInterfaces.d.ts +7 -0
  67. package/lib/services/findPaymentInterfaces.js +13 -0
  68. package/lib/services/findWarehousingInterfaces.d.ts +7 -0
  69. package/lib/services/findWarehousingInterfaces.js +13 -0
  70. package/lib/services/getDeliveryDiscounts.d.ts +8 -0
  71. package/lib/services/getDeliveryDiscounts.js +14 -0
  72. package/lib/services/getPaymentDiscounts.d.ts +8 -0
  73. package/lib/services/getPaymentDiscounts.js +14 -0
  74. package/lib/services/getShopInfo.d.ts +25 -0
  75. package/lib/services/getShopInfo.js +47 -0
  76. package/lib/services/impersonateUser.d.ts +8 -0
  77. package/lib/services/impersonateUser.js +20 -0
  78. package/lib/services/loginAsGuest.d.ts +10 -0
  79. package/lib/services/loginAsGuest.js +19 -0
  80. package/lib/services/obfuscateWorkerData.d.ts +4 -0
  81. package/lib/services/obfuscateWorkerData.js +4 -0
  82. package/lib/services/orderPricingAccessors.d.ts +18 -0
  83. package/lib/services/orderPricingAccessors.js +29 -0
  84. package/lib/services/payOrder.d.ts +5 -0
  85. package/lib/services/payOrder.js +25 -0
  86. package/lib/services/registerUser.d.ts +14 -0
  87. package/lib/services/registerUser.js +44 -0
  88. package/lib/services/removeBookmark.d.ts +6 -0
  89. package/lib/services/removeBookmark.js +10 -0
  90. package/lib/services/removeCartItem.d.ts +6 -0
  91. package/lib/services/removeCartItem.js +19 -0
  92. package/lib/services/resetPasswordWithMigration.d.ts +14 -0
  93. package/lib/services/resetPasswordWithMigration.js +31 -0
  94. package/lib/services/resolveWorkType.d.ts +1 -0
  95. package/lib/services/resolveWorkType.js +7 -0
  96. package/lib/services/searchProductsWithAssortment.d.ts +19 -0
  97. package/lib/services/searchProductsWithAssortment.js +17 -0
  98. package/lib/services/toggleBookmark.d.ts +8 -0
  99. package/lib/services/toggleBookmark.js +33 -0
  100. package/lib/services/updateCart.d.ts +15 -0
  101. package/lib/services/updateCart.js +27 -0
  102. package/lib/services/updateCartItem.d.ts +11 -0
  103. package/lib/services/updateCartItem.js +37 -0
  104. package/lib/services/updateEnrollment.d.ts +13 -0
  105. package/lib/services/updateEnrollment.js +36 -0
  106. package/lib/services/verifyEmailWithMigration.d.ts +15 -0
  107. package/lib/services/verifyEmailWithMigration.js +23 -0
  108. package/lib/utils/getFileAdapter.d.ts +1 -0
  109. package/lib/utils/getFileAdapter.js +7 -0
  110. package/package.json +1 -1
@@ -0,0 +1,33 @@
1
+ import exportProductsHandler from './handlers/exportProductsHandler.ts';
2
+ import exportAssortmentsHandler from './handlers/exportAssortmentsHandler.ts';
3
+ import exportFiltersHandler from './handlers/exportFiltersHandler.ts';
4
+ import { z } from 'zod';
5
+ import type { CSVFileResult } from './handlers/generateCSVFileAndUrl.ts';
6
+ export declare const EXPORTS_DIRECTORY = "exports";
7
+ export type ExportFiles = Record<string, CSVFileResult | null>;
8
+ export interface BulkExportOperationResult {
9
+ entity: string;
10
+ success: boolean;
11
+ files: ExportFiles;
12
+ }
13
+ export interface BulkExportHandler<T = unknown> {
14
+ payloadSchema?: z.ZodObject<z.ZodRawShape>;
15
+ (params: Record<string, unknown>, locales: string[], unchainedAPI: T): Promise<ExportFiles>;
16
+ }
17
+ export interface BulkExporterOptions {
18
+ handlers?: Record<string, BulkExportHandler>;
19
+ }
20
+ export default function createBulkExporterFactory(bulkExporterOptions?: BulkExporterOptions): {
21
+ createBulkExporter: ({ entity }: {
22
+ entity: string;
23
+ }) => {
24
+ validate: (payload: Record<string, unknown>) => Promise<void>;
25
+ execute: <T>(payload: Record<string, unknown>, locales: string[], unchainedApi: T) => Promise<({
26
+ entity: string;
27
+ success: boolean;
28
+ files: ExportFiles;
29
+ } | null)[]>;
30
+ };
31
+ };
32
+ export type BulkExporter = ReturnType<typeof createBulkExporterFactory>;
33
+ export { exportAssortmentsHandler, exportProductsHandler, exportFiltersHandler };
@@ -0,0 +1,46 @@
1
+ import exportProductsHandler from "./handlers/exportProductsHandler.js";
2
+ import exportAssortmentsHandler from "./handlers/exportAssortmentsHandler.js";
3
+ import exportFiltersHandler from "./handlers/exportFiltersHandler.js";
4
+ import { createLogger } from '@unchainedshop/logger';
5
+ import { z } from 'zod';
6
+ import exportUsersHandler from "./handlers/exportUsersHandler.js";
7
+ const logger = createLogger('unchained:bulk-export');
8
+ export const EXPORTS_DIRECTORY = 'exports';
9
+ let bulkOperationHandlers = {};
10
+ export default function createBulkExporterFactory(bulkExporterOptions) {
11
+ bulkOperationHandlers = {
12
+ ASSORTMENTS: exportAssortmentsHandler,
13
+ PRODUCTS: exportProductsHandler,
14
+ FILTERS: exportFiltersHandler,
15
+ USER: exportUsersHandler,
16
+ ...(bulkExporterOptions?.handlers || {}),
17
+ };
18
+ const createBulkExporter = ({ entity }) => {
19
+ const type = entity.toUpperCase();
20
+ const exportHandler = bulkOperationHandlers[type];
21
+ if (!exportHandler) {
22
+ throw new Error(`Export entity (${entity}) is not supported`);
23
+ }
24
+ return {
25
+ validate: async (payload) => {
26
+ logger.debug(`Validating ${type} export payload`);
27
+ try {
28
+ if (exportHandler.payloadSchema) {
29
+ exportHandler.payloadSchema.parse(payload);
30
+ }
31
+ }
32
+ catch (e) {
33
+ throw new Error(`${type}: ${e.message}`);
34
+ }
35
+ },
36
+ execute: async (payload, locales, unchainedApi) => {
37
+ const files = await exportHandler(payload, locales, unchainedApi);
38
+ return [{ entity: type, success: true, files }, null];
39
+ },
40
+ };
41
+ };
42
+ return {
43
+ createBulkExporter,
44
+ };
45
+ }
46
+ export { exportAssortmentsHandler, exportProductsHandler, exportFiltersHandler };
@@ -0,0 +1,38 @@
1
+ import type { UnchainedCore } from '../../core-index.ts';
2
+ import { z } from 'zod';
3
+ export declare const AssortmentExportPayloadSchema: z.ZodObject<{
4
+ exportAssortments: z.ZodOptional<z.ZodBoolean>;
5
+ exportLinks: z.ZodOptional<z.ZodBoolean>;
6
+ exportProducts: z.ZodOptional<z.ZodBoolean>;
7
+ exportFilters: z.ZodOptional<z.ZodBoolean>;
8
+ queryString: z.ZodOptional<z.ZodString>;
9
+ includeInactive: z.ZodOptional<z.ZodBoolean>;
10
+ includeLeaves: z.ZodOptional<z.ZodBoolean>;
11
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
12
+ }, z.core.$strip>;
13
+ export interface AssortmentExportParams {
14
+ exportAssortments?: boolean;
15
+ exportLinks?: boolean;
16
+ exportProducts?: boolean;
17
+ exportFilters?: boolean;
18
+ [key: string]: any;
19
+ }
20
+ declare const exportAssortmentsHandler: {
21
+ ({ exportAssortments, exportFilters, exportLinks, exportProducts, ...params }: AssortmentExportParams, locales: string[], unchainedAPI: UnchainedCore): Promise<{
22
+ assortments: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
23
+ filters: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
24
+ products: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
25
+ children: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
26
+ }>;
27
+ payloadSchema: z.ZodObject<{
28
+ exportAssortments: z.ZodOptional<z.ZodBoolean>;
29
+ exportLinks: z.ZodOptional<z.ZodBoolean>;
30
+ exportProducts: z.ZodOptional<z.ZodBoolean>;
31
+ exportFilters: z.ZodOptional<z.ZodBoolean>;
32
+ queryString: z.ZodOptional<z.ZodString>;
33
+ includeInactive: z.ZodOptional<z.ZodBoolean>;
34
+ includeLeaves: z.ZodOptional<z.ZodBoolean>;
35
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
36
+ }, z.core.$strip>;
37
+ };
38
+ export default exportAssortmentsHandler;
@@ -0,0 +1,137 @@
1
+ import generateCSVFileAndURL from "./generateCSVFileAndUrl.js";
2
+ import { z } from 'zod';
3
+ import { EXPORTS_DIRECTORY } from "../createBulkExporter.js";
4
+ export const AssortmentExportPayloadSchema = z.object({
5
+ exportAssortments: z.boolean().optional(),
6
+ exportLinks: z.boolean().optional(),
7
+ exportProducts: z.boolean().optional(),
8
+ exportFilters: z.boolean().optional(),
9
+ queryString: z.string().optional(),
10
+ includeInactive: z.boolean().optional(),
11
+ includeLeaves: z.boolean().optional(),
12
+ tags: z.array(z.string()).optional(),
13
+ });
14
+ const ASSORTMENT_CSV_SCHEMA = {
15
+ base: ['_id', 'isActive', 'isBase', 'isRoot', 'sequence', 'tags'],
16
+ textFields: ['title', 'subtitle', 'description', 'slug'],
17
+ filterFields: ['_id', 'assortmentId', 'filterId', 'tags', 'sortKey'],
18
+ productFields: ['_id', 'assortmentId', 'productId', 'tags', 'sortKey'],
19
+ childrenFields: ['_id', 'assortmentId', 'childAssortmentId', 'tags', 'sortKey'],
20
+ };
21
+ const buildAssortmentHeaders = (locales) => [
22
+ ...ASSORTMENT_CSV_SCHEMA.base,
23
+ ...locales.flatMap((locale) => ASSORTMENT_CSV_SCHEMA.textFields.map((field) => `texts.${locale}.${field}`)),
24
+ 'meta',
25
+ ];
26
+ const buildFilterHeaders = () => ASSORTMENT_CSV_SCHEMA.filterFields;
27
+ const buildProductHeaders = () => ASSORTMENT_CSV_SCHEMA.productFields;
28
+ const buildChildrenHeaders = () => ASSORTMENT_CSV_SCHEMA.childrenFields;
29
+ const fetchAssortmentTexts = async (modules, assortmentId) => {
30
+ const texts = await modules.assortments.texts.findTexts({ assortmentId });
31
+ return texts.reduce((acc, t) => ({ ...acc, [t.locale]: t }), {});
32
+ };
33
+ const exportAssortmentsHandler = async ({ exportAssortments, exportFilters, exportLinks, exportProducts, ...params }, locales, unchainedAPI) => {
34
+ const { modules } = unchainedAPI;
35
+ const assortments = await modules.assortments.findAssortments({ ...params });
36
+ const assortmentRows = [];
37
+ const filterRows = [];
38
+ const productRows = [];
39
+ const childrenRows = [];
40
+ for await (const assortment of assortments) {
41
+ if (exportAssortments) {
42
+ const texts = await fetchAssortmentTexts(modules, assortment._id);
43
+ const row = { ...assortment };
44
+ locales.forEach((locale) => {
45
+ const t = texts[locale] || {};
46
+ ASSORTMENT_CSV_SCHEMA.textFields.forEach((field) => {
47
+ let value = t[field];
48
+ if (Array.isArray(value))
49
+ value = value.join(';');
50
+ row[`texts.${locale}.${field}`] = value ?? '';
51
+ });
52
+ });
53
+ row['meta'] = typeof assortment?.meta === 'object' ? JSON.stringify(assortment.meta) : '';
54
+ assortmentRows.push(row);
55
+ }
56
+ if (exportFilters) {
57
+ const assortmentFilters = await modules.assortments.filters.findFilters({
58
+ assortmentId: assortment._id,
59
+ });
60
+ filterRows.push(...assortmentFilters.map(({ _id, filterId, tags, sortKey }) => ({
61
+ _id,
62
+ assortmentId: assortment._id,
63
+ filterId,
64
+ tags: tags || '',
65
+ sortKey,
66
+ })));
67
+ }
68
+ if (exportProducts) {
69
+ const assortmentProducts = await modules.assortments.products.findAssortmentProducts({
70
+ assortmentId: assortment._id,
71
+ });
72
+ productRows.push(...assortmentProducts.map(({ _id, productId, tags, sortKey }) => ({
73
+ _id,
74
+ assortmentId: assortment._id,
75
+ productId,
76
+ tags: tags || '',
77
+ sortKey,
78
+ })));
79
+ }
80
+ if (exportLinks) {
81
+ const links = await modules.assortments.links.findLinks({ assortmentId: assortment._id });
82
+ childrenRows.push(...links
83
+ .filter((l) => l.childAssortmentId !== assortment._id)
84
+ .map(({ _id, childAssortmentId, tags, sortKey }) => ({
85
+ _id,
86
+ assortmentId: assortment._id,
87
+ childAssortmentId,
88
+ tags: tags || '',
89
+ sortKey,
90
+ })));
91
+ }
92
+ }
93
+ const assortmentsCSV = exportAssortments
94
+ ? await generateCSVFileAndURL({
95
+ headers: buildAssortmentHeaders(locales),
96
+ rows: assortmentRows,
97
+ directoryName: EXPORTS_DIRECTORY,
98
+ fileName: 'assortments_export.csv',
99
+ unchainedAPI,
100
+ })
101
+ : null;
102
+ const filtersCSV = exportFilters
103
+ ? await generateCSVFileAndURL({
104
+ headers: buildFilterHeaders(),
105
+ rows: filterRows,
106
+ directoryName: EXPORTS_DIRECTORY,
107
+ fileName: 'assortment_filters_export.csv',
108
+ unchainedAPI,
109
+ })
110
+ : null;
111
+ const productsCSV = exportProducts
112
+ ? await generateCSVFileAndURL({
113
+ headers: buildProductHeaders(),
114
+ rows: productRows,
115
+ directoryName: EXPORTS_DIRECTORY,
116
+ fileName: 'assortment_products_export.csv',
117
+ unchainedAPI,
118
+ })
119
+ : null;
120
+ const childrenCSV = exportLinks
121
+ ? await generateCSVFileAndURL({
122
+ headers: buildChildrenHeaders(),
123
+ rows: childrenRows,
124
+ directoryName: EXPORTS_DIRECTORY,
125
+ fileName: 'assortment_links_export.csv',
126
+ unchainedAPI,
127
+ })
128
+ : null;
129
+ return {
130
+ assortments: assortmentsCSV,
131
+ filters: filtersCSV,
132
+ products: productsCSV,
133
+ children: childrenCSV,
134
+ };
135
+ };
136
+ export default exportAssortmentsHandler;
137
+ exportAssortmentsHandler.payloadSchema = AssortmentExportPayloadSchema;
@@ -0,0 +1,26 @@
1
+ import type { UnchainedCore } from '../../core-index.ts';
2
+ import { z } from 'zod';
3
+ export declare const FilterExportPayloadSchema: z.ZodObject<{
4
+ exportFilters: z.ZodOptional<z.ZodBoolean>;
5
+ exportFilterOptions: z.ZodOptional<z.ZodBoolean>;
6
+ queryString: z.ZodOptional<z.ZodString>;
7
+ includeInactive: z.ZodOptional<z.ZodBoolean>;
8
+ }, z.core.$strip>;
9
+ export interface FilterExportParams {
10
+ exportFilters?: boolean;
11
+ exportFilterOptions?: boolean;
12
+ [key: string]: any;
13
+ }
14
+ declare const exportFiltersHandler: {
15
+ ({ exportFilterOptions, exportFilters, ...params }: FilterExportParams, locales: string[], unchainedAPI: UnchainedCore): Promise<{
16
+ filters: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
17
+ filterOptions: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
18
+ }>;
19
+ payloadSchema: z.ZodObject<{
20
+ exportFilters: z.ZodOptional<z.ZodBoolean>;
21
+ exportFilterOptions: z.ZodOptional<z.ZodBoolean>;
22
+ queryString: z.ZodOptional<z.ZodString>;
23
+ includeInactive: z.ZodOptional<z.ZodBoolean>;
24
+ }, z.core.$strip>;
25
+ };
26
+ export default exportFiltersHandler;
@@ -0,0 +1,88 @@
1
+ import generateCSVFileAndURL from "./generateCSVFileAndUrl.js";
2
+ import { z } from 'zod';
3
+ import { EXPORTS_DIRECTORY } from "../createBulkExporter.js";
4
+ export const FilterExportPayloadSchema = z.object({
5
+ exportFilters: z.boolean().optional(),
6
+ exportFilterOptions: z.boolean().optional(),
7
+ queryString: z.string().optional(),
8
+ includeInactive: z.boolean().optional(),
9
+ });
10
+ const FILTER_CSV_SCHEMA = {
11
+ filterFields: ['_id', 'key', 'type', 'isActive'],
12
+ optionFields: ['optionId', 'filterId', 'value'],
13
+ textFields: ['title', 'subtitle'],
14
+ };
15
+ const buildFilterHeaders = (locales) => [
16
+ ...FILTER_CSV_SCHEMA.filterFields,
17
+ ...locales.flatMap((locale) => FILTER_CSV_SCHEMA.textFields.map((field) => `texts.${locale}.${field}`)),
18
+ 'meta',
19
+ ];
20
+ const buildOptionHeaders = (locales) => [
21
+ ...FILTER_CSV_SCHEMA.optionFields,
22
+ ...locales.flatMap((locale) => FILTER_CSV_SCHEMA.textFields.map((field) => `texts.${locale}.${field}`)),
23
+ ];
24
+ const mapTextsToRow = (row, texts, locales) => {
25
+ locales.forEach((locale) => {
26
+ const t = texts[locale] || {};
27
+ FILTER_CSV_SCHEMA.textFields.forEach((field) => {
28
+ row[`texts.${locale}.${field}`] = t[field] ?? '';
29
+ });
30
+ });
31
+ };
32
+ const fetchTexts = async (modules, filterId, filterOptionValue) => {
33
+ const texts = await modules.filters.texts.findTexts({ filterId, filterOptionValue });
34
+ return texts.reduce((acc, t) => ({ ...acc, [t.locale]: t }), {});
35
+ };
36
+ const exportFiltersHandler = async ({ exportFilterOptions, exportFilters, ...params }, locales, unchainedAPI) => {
37
+ const { modules } = unchainedAPI;
38
+ const filters = await modules.filters.findFilters({ ...params });
39
+ const filterRows = [];
40
+ const optionRows = [];
41
+ for await (const filter of filters) {
42
+ if (exportFilters) {
43
+ const filterTexts = await fetchTexts(modules, filter._id);
44
+ const row = {
45
+ _id: filter._id,
46
+ key: filter.key,
47
+ type: filter.type,
48
+ isActive: filter.isActive,
49
+ meta: typeof filter?.meta === 'object' ? JSON.stringify(filter.meta) : '',
50
+ };
51
+ mapTextsToRow(row, filterTexts, locales);
52
+ filterRows.push(row);
53
+ }
54
+ if (exportFilterOptions) {
55
+ for await (const optionValue of filter.options) {
56
+ const optionTexts = await fetchTexts(modules, filter._id, optionValue);
57
+ const optionRow = {
58
+ optionId: `${filter._id}:${optionValue}`,
59
+ filterId: filter._id,
60
+ value: optionValue,
61
+ };
62
+ mapTextsToRow(optionRow, optionTexts, locales);
63
+ optionRows.push(optionRow);
64
+ }
65
+ }
66
+ }
67
+ const filtersCSV = exportFilters
68
+ ? await generateCSVFileAndURL({
69
+ headers: buildFilterHeaders(locales),
70
+ rows: filterRows,
71
+ directoryName: EXPORTS_DIRECTORY,
72
+ fileName: 'filters_export.csv',
73
+ unchainedAPI,
74
+ })
75
+ : null;
76
+ const optionsCSV = exportFilterOptions
77
+ ? await generateCSVFileAndURL({
78
+ headers: buildOptionHeaders(locales),
79
+ rows: optionRows,
80
+ directoryName: EXPORTS_DIRECTORY,
81
+ fileName: 'filter_options_export.csv',
82
+ unchainedAPI,
83
+ })
84
+ : null;
85
+ return { filters: filtersCSV, filterOptions: optionsCSV };
86
+ };
87
+ export default exportFiltersHandler;
88
+ exportFiltersHandler.payloadSchema = FilterExportPayloadSchema;
@@ -0,0 +1,40 @@
1
+ import type { UnchainedCore } from '../../core-index.ts';
2
+ import { z } from 'zod';
3
+ export declare const ProductExportPayloadSchema: z.ZodObject<{
4
+ exportProducts: z.ZodOptional<z.ZodBoolean>;
5
+ exportBundleItems: z.ZodOptional<z.ZodBoolean>;
6
+ exportPrices: z.ZodOptional<z.ZodBoolean>;
7
+ exportVariations: z.ZodOptional<z.ZodBoolean>;
8
+ exportVariationOptions: z.ZodOptional<z.ZodBoolean>;
9
+ queryString: z.ZodOptional<z.ZodString>;
10
+ includeDrafts: z.ZodOptional<z.ZodBoolean>;
11
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
12
+ }, z.core.$strip>;
13
+ export interface ProductExportParams {
14
+ exportProducts?: boolean;
15
+ exportBundleItems?: boolean;
16
+ exportPrices?: boolean;
17
+ exportVariations?: boolean;
18
+ exportVariationOptions?: boolean;
19
+ [key: string]: any;
20
+ }
21
+ declare const exportProductsHandler: {
22
+ ({ exportBundleItems, exportPrices, exportProducts, exportVariationOptions, exportVariations, ...params }: ProductExportParams, locales: string[], unchainedAPI: UnchainedCore): Promise<{
23
+ products: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
24
+ prices: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
25
+ bundles: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
26
+ variations: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
27
+ variationOptions: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
28
+ }>;
29
+ payloadSchema: z.ZodObject<{
30
+ exportProducts: z.ZodOptional<z.ZodBoolean>;
31
+ exportBundleItems: z.ZodOptional<z.ZodBoolean>;
32
+ exportPrices: z.ZodOptional<z.ZodBoolean>;
33
+ exportVariations: z.ZodOptional<z.ZodBoolean>;
34
+ exportVariationOptions: z.ZodOptional<z.ZodBoolean>;
35
+ queryString: z.ZodOptional<z.ZodString>;
36
+ includeDrafts: z.ZodOptional<z.ZodBoolean>;
37
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
38
+ }, z.core.$strip>;
39
+ };
40
+ export default exportProductsHandler;
@@ -0,0 +1,238 @@
1
+ import generateCSVFileAndURL from "./generateCSVFileAndUrl.js";
2
+ import { z } from 'zod';
3
+ import { EXPORTS_DIRECTORY } from "../createBulkExporter.js";
4
+ export const ProductExportPayloadSchema = z.object({
5
+ exportProducts: z.boolean().optional(),
6
+ exportBundleItems: z.boolean().optional(),
7
+ exportPrices: z.boolean().optional(),
8
+ exportVariations: z.boolean().optional(),
9
+ exportVariationOptions: z.boolean().optional(),
10
+ queryString: z.string().optional(),
11
+ includeDrafts: z.boolean().optional(),
12
+ tags: z.array(z.string()).optional(),
13
+ });
14
+ const PRODUCT_CSV_SCHEMA = {
15
+ base: ['_id', 'sku', 'baseUnit', 'sequence', 'status', 'tags', 'updated', 'published', 'type'],
16
+ textFields: ['title', 'subtitle', 'description', 'vendor', 'brand', 'labels', 'slug'],
17
+ priceFields: [
18
+ 'productId',
19
+ 'amount',
20
+ 'currencyCode',
21
+ 'countryCode',
22
+ 'isTaxable',
23
+ 'isNetPrice',
24
+ 'maxQuantity',
25
+ ],
26
+ bundleItemHeaders: ['productId', 'bundleItemProductId', 'quantity', 'configuration'],
27
+ variationItemHeaders: ['productId', 'variationId', 'key', 'type'],
28
+ variationTextFields: ['title', 'subtitle'],
29
+ variationOptionItemHeaders: ['variationId', 'value'],
30
+ variationOptionTextFields: ['title', 'subtitle'],
31
+ };
32
+ const buildProductHeaders = (locales) => [
33
+ ...PRODUCT_CSV_SCHEMA.base,
34
+ ...locales.flatMap((l) => PRODUCT_CSV_SCHEMA.textFields.map((f) => `texts.${l}.${f}`)),
35
+ 'supply.weightInGram',
36
+ 'supply.heightInMillimeters',
37
+ 'supply.lengthInMillimeters',
38
+ 'supply.widthInMillimeters',
39
+ 'meta',
40
+ ];
41
+ const buildPriceHeaders = () => PRODUCT_CSV_SCHEMA.priceFields;
42
+ const buildBundleHeaders = () => PRODUCT_CSV_SCHEMA.bundleItemHeaders;
43
+ const buildVariationHeaders = (locales) => [
44
+ ...PRODUCT_CSV_SCHEMA.variationItemHeaders,
45
+ ...locales.flatMap((l) => PRODUCT_CSV_SCHEMA.variationTextFields.map((f) => `texts.${l}.${f}`)),
46
+ ];
47
+ const buildVariationOptionsHeaders = (locales) => [
48
+ ...PRODUCT_CSV_SCHEMA.variationOptionItemHeaders,
49
+ ...locales.flatMap((l) => PRODUCT_CSV_SCHEMA.variationOptionTextFields.map((f) => `texts.${l}.${f}`)),
50
+ ];
51
+ const mapTextsToRow = (row, texts, locales, fields) => {
52
+ locales.forEach((locale) => {
53
+ const t = texts.find((x) => x.locale === locale) || {};
54
+ fields.forEach((f) => {
55
+ row[`texts.${locale}.${f}`] = Array.isArray(t[f]) ? t[f].join(';') : (t[f] ?? '');
56
+ });
57
+ });
58
+ };
59
+ const buildProductRow = (product, locales) => {
60
+ const row = {};
61
+ PRODUCT_CSV_SCHEMA.base.forEach((k) => (row[k] = product[k] ?? ''));
62
+ row['supply.weightInGram'] = product?.dimensions?.weight ?? '';
63
+ row['supply.heightInMillimeters'] = product?.dimensions?.height ?? '';
64
+ row['supply.lengthInMillimeters'] = product?.dimensions?.length ?? '';
65
+ row['supply.widthInMillimeters'] = product?.dimensions?.width ?? '';
66
+ row['meta'] =
67
+ typeof product?.meta === 'object'
68
+ ? JSON.stringify(product?.meta, Object.keys(product?.meta).sort())
69
+ : product?.meta;
70
+ mapTextsToRow(row, product.texts ?? [], locales, PRODUCT_CSV_SCHEMA.textFields);
71
+ return row;
72
+ };
73
+ const buildPriceRows = (productId, prices = []) => prices.map((p) => ({
74
+ productId,
75
+ amount: p.amount ?? '',
76
+ isNetPrice: p.isNetPrice ?? '',
77
+ isTaxable: p.isTaxable ?? '',
78
+ currencyCode: p.currency?.isoCode ?? '',
79
+ countryCode: p.country?.isoCode ?? '',
80
+ maxQuantity: p.maxQuantity ?? '',
81
+ }));
82
+ const buildBundleRows = (productId, bundles = []) => bundles.map((b) => ({
83
+ productId,
84
+ bundleItemProductId: b.productId,
85
+ quantity: b.quantity ?? 1,
86
+ configuration: (b.configuration || []).map((c) => Object.values(c).join(':')).join(';'),
87
+ }));
88
+ const buildVariationRows = (productId, variations = [], locales) => {
89
+ const variationRows = [];
90
+ const optionRows = [];
91
+ variations.forEach((v) => {
92
+ const row = {
93
+ productId,
94
+ variationId: v._id,
95
+ key: v.key,
96
+ type: v.type,
97
+ };
98
+ mapTextsToRow(row, Object.values(v.texts || {}), locales, PRODUCT_CSV_SCHEMA.variationTextFields);
99
+ variationRows.push(row);
100
+ const options = v.options?.[v._id] ?? [];
101
+ options.forEach((o) => {
102
+ const optRow = {
103
+ variationId: v._id,
104
+ value: o.productVariationOption,
105
+ };
106
+ mapTextsToRow(optRow, Object.values(o.texts || {}), locales, PRODUCT_CSV_SCHEMA.variationOptionTextFields);
107
+ optionRows.push(optRow);
108
+ });
109
+ });
110
+ return { variationRows, optionRows };
111
+ };
112
+ const exportProductsHandler = async ({ exportBundleItems, exportPrices, exportProducts, exportVariationOptions, exportVariations, ...params }, locales, unchainedAPI) => {
113
+ const { queryString, includeDrafts, tags } = params;
114
+ const products = await unchainedAPI.modules.products.findProducts({
115
+ includeDrafts,
116
+ queryString,
117
+ tags,
118
+ });
119
+ const normalized = { products: {}, prices: {}, bundles: {}, variations: {} };
120
+ for await (const p of products) {
121
+ const productId = p._id;
122
+ const productTexts = exportProducts
123
+ ? await unchainedAPI.modules.products.texts.findTexts({ productId })
124
+ : null;
125
+ const variations = exportVariations
126
+ ? await unchainedAPI.modules.products.variations.findProductVariations({ productId })
127
+ : [];
128
+ const normalizedVariations = [];
129
+ if (exportVariations) {
130
+ for await (const v of variations) {
131
+ const variationTexts = exportVariations
132
+ ? await unchainedAPI.modules.products.variations.texts.findVariationTexts({
133
+ productVariationId: v._id,
134
+ productVariationOptionValue: null,
135
+ })
136
+ : [];
137
+ const options = [];
138
+ if (exportVariationOptions) {
139
+ for await (const o of v.options || []) {
140
+ const optionTexts = await unchainedAPI.modules.products.variations.texts.findVariationTexts({
141
+ productVariationId: v._id,
142
+ productVariationOptionValue: o,
143
+ });
144
+ options.push({
145
+ _id: `${v._id}:${o}`,
146
+ productVariationOption: o,
147
+ texts: Object.fromEntries(optionTexts.map((t) => [t.locale, t])),
148
+ });
149
+ }
150
+ }
151
+ normalizedVariations.push({
152
+ ...v,
153
+ texts: exportVariations
154
+ ? Object.fromEntries(variationTexts.map((t) => [t.locale, t]))
155
+ : {},
156
+ options: exportVariationOptions ? { [v._id]: options } : {},
157
+ });
158
+ }
159
+ }
160
+ normalized.products[productId] = exportProducts ? { ...p, texts: productTexts } : p;
161
+ normalized.prices[productId] = exportPrices ? (p.commerce?.pricing ?? []) : [];
162
+ normalized.bundles[productId] = exportBundleItems ? (p.bundleItems ?? []) : [];
163
+ normalized.variations[productId] = normalizedVariations;
164
+ }
165
+ const productRows = [];
166
+ const priceRows = [];
167
+ const bundleRows = [];
168
+ const variationRows = [];
169
+ const variationOptionRows = [];
170
+ for (const pid in normalized.products) {
171
+ if (exportProducts) {
172
+ productRows.push(buildProductRow(normalized.products[pid], locales));
173
+ }
174
+ if (exportPrices) {
175
+ priceRows.push(...buildPriceRows(pid, normalized.prices[pid]));
176
+ }
177
+ if (exportBundleItems) {
178
+ bundleRows.push(...buildBundleRows(pid, normalized.bundles[pid]));
179
+ }
180
+ const { variationRows: vr, optionRows: or } = buildVariationRows(pid, normalized.variations[pid], locales);
181
+ variationRows.push(...vr);
182
+ variationOptionRows.push(...or);
183
+ }
184
+ const productsCSV = exportProducts
185
+ ? await generateCSVFileAndURL({
186
+ headers: buildProductHeaders(locales),
187
+ rows: productRows,
188
+ directoryName: EXPORTS_DIRECTORY,
189
+ fileName: 'products_export.csv',
190
+ unchainedAPI,
191
+ })
192
+ : null;
193
+ const pricesCSV = exportPrices
194
+ ? await generateCSVFileAndURL({
195
+ headers: buildPriceHeaders(),
196
+ rows: priceRows,
197
+ directoryName: EXPORTS_DIRECTORY,
198
+ fileName: 'products_prices_export.csv',
199
+ unchainedAPI,
200
+ })
201
+ : null;
202
+ const bundlesCSV = exportBundleItems
203
+ ? await generateCSVFileAndURL({
204
+ headers: buildBundleHeaders(),
205
+ rows: bundleRows,
206
+ directoryName: EXPORTS_DIRECTORY,
207
+ fileName: 'products_bundle_items_export.csv',
208
+ unchainedAPI,
209
+ })
210
+ : null;
211
+ const variationsCSV = exportVariations
212
+ ? await generateCSVFileAndURL({
213
+ headers: buildVariationHeaders(locales),
214
+ rows: variationRows,
215
+ directoryName: EXPORTS_DIRECTORY,
216
+ fileName: 'products_variations_export.csv',
217
+ unchainedAPI,
218
+ })
219
+ : null;
220
+ const variationOptionsCSV = exportVariationOptions
221
+ ? await generateCSVFileAndURL({
222
+ headers: buildVariationOptionsHeaders(locales),
223
+ rows: variationOptionRows,
224
+ directoryName: EXPORTS_DIRECTORY,
225
+ fileName: 'products_variation_options_export.csv',
226
+ unchainedAPI,
227
+ })
228
+ : null;
229
+ return {
230
+ products: productsCSV,
231
+ prices: pricesCSV,
232
+ bundles: bundlesCSV,
233
+ variations: variationsCSV,
234
+ variationOptions: variationOptionsCSV,
235
+ };
236
+ };
237
+ export default exportProductsHandler;
238
+ exportProductsHandler.payloadSchema = ProductExportPayloadSchema;