@porulle/plugin-channel-connector 0.10.8 → 0.13.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/dist/catalog-field-mapping.d.ts +32 -0
- package/dist/catalog-field-mapping.js +178 -0
- package/dist/catalog-push-trigger.d.ts +26 -0
- package/dist/catalog-push-trigger.js +70 -0
- package/dist/hooks.js +18 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +159 -8
- package/dist/mock-connector.d.ts +31 -6
- package/dist/mock-connector.js +77 -2
- package/dist/schema.d.ts +876 -63
- package/dist/schema.js +76 -1
- package/dist/service.d.ts +167 -2
- package/dist/service.js +2402 -79
- package/package.json +4 -4
- package/src/catalog-field-mapping.ts +211 -0
- package/src/catalog-push-trigger.ts +99 -0
- package/src/hooks.ts +30 -6
- package/src/index.ts +222 -6
- package/src/mock-connector.ts +81 -2
- package/src/schema.ts +106 -0
- package/src/service.ts +3203 -184
package/dist/mock-connector.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CommerceValidationError } from "@porulle/core";
|
|
2
|
-
import type { ChannelCatalogItem, ChannelInventoryLevel, ChannelOrderSlice } from "@porulle/core";
|
|
2
|
+
import type { ChannelCatalogItem, ChannelConnectorError, ChannelInventoryLevel, ChannelOrderSlice, ChannelPushCatalogItem, ChannelPushCatalogPreviousField, ChannelStore } from "@porulle/core";
|
|
3
3
|
export interface MockChannelConnectorOptions {
|
|
4
4
|
catalog?: ChannelCatalogItem[];
|
|
5
5
|
inventory?: ChannelInventoryLevel[];
|
|
@@ -7,6 +7,9 @@ export interface MockChannelConnectorOptions {
|
|
|
7
7
|
throwOnInventory?: boolean;
|
|
8
8
|
inventoryDelayMs?: number;
|
|
9
9
|
onFetchInventory?: (ids: string[]) => void;
|
|
10
|
+
pushCatalogFailures?: Record<string, ChannelConnectorError>;
|
|
11
|
+
pushCatalogTransportError?: ChannelConnectorError;
|
|
12
|
+
onPushCatalog?: (items: ChannelPushCatalogItem[]) => void;
|
|
10
13
|
}
|
|
11
14
|
export declare function mockChannelConnector(options?: MockChannelConnectorOptions): {
|
|
12
15
|
providerId: string;
|
|
@@ -14,13 +17,14 @@ export declare function mockChannelConnector(options?: MockChannelConnectorOptio
|
|
|
14
17
|
importCatalog: true;
|
|
15
18
|
importInventory: true;
|
|
16
19
|
pushOrder: true;
|
|
20
|
+
pushCatalog: true;
|
|
17
21
|
receiveWebhooks: true;
|
|
18
22
|
};
|
|
19
|
-
importCatalog(): Promise<import("@porulle/core").Result<{
|
|
23
|
+
importCatalog(_store?: ChannelStore): Promise<import("@porulle/core").Result<{
|
|
20
24
|
items: ChannelCatalogItem[];
|
|
21
25
|
nextCursor: null;
|
|
22
26
|
}, never>>;
|
|
23
|
-
fetchInventory(_store:
|
|
27
|
+
fetchInventory(_store: ChannelStore, ids: string[] | undefined): Promise<{
|
|
24
28
|
ok: false;
|
|
25
29
|
error: CommerceValidationError;
|
|
26
30
|
} | {
|
|
@@ -28,11 +32,32 @@ export declare function mockChannelConnector(options?: MockChannelConnectorOptio
|
|
|
28
32
|
value: ChannelInventoryLevel[];
|
|
29
33
|
meta?: Record<string, unknown>;
|
|
30
34
|
}>;
|
|
31
|
-
pushOrder(_store:
|
|
35
|
+
pushOrder(_store: ChannelStore, slice: ChannelOrderSlice): Promise<import("@porulle/core").Result<{
|
|
32
36
|
remoteOrderId: string;
|
|
33
37
|
remoteUrl: string;
|
|
34
38
|
}, never>>;
|
|
35
|
-
|
|
39
|
+
pushCatalog(_store: ChannelStore, items: ChannelPushCatalogItem[], opts?: {
|
|
40
|
+
dryRun?: boolean;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
ok: false;
|
|
43
|
+
error: ChannelConnectorError;
|
|
44
|
+
} | {
|
|
45
|
+
ok: true;
|
|
46
|
+
value: {
|
|
47
|
+
outcomes: ({
|
|
48
|
+
externalId: string;
|
|
49
|
+
ok: boolean;
|
|
50
|
+
error: ChannelConnectorError;
|
|
51
|
+
} | {
|
|
52
|
+
previousFields?: ChannelPushCatalogPreviousField[];
|
|
53
|
+
externalId: string;
|
|
54
|
+
ok: boolean;
|
|
55
|
+
error?: never;
|
|
56
|
+
})[];
|
|
57
|
+
};
|
|
58
|
+
meta?: Record<string, unknown>;
|
|
59
|
+
}>;
|
|
60
|
+
fetchOrderStatus(_store: ChannelStore, remoteId: string): Promise<{
|
|
36
61
|
ok: false;
|
|
37
62
|
error: CommerceValidationError;
|
|
38
63
|
} | {
|
|
@@ -42,7 +67,7 @@ export declare function mockChannelConnector(options?: MockChannelConnectorOptio
|
|
|
42
67
|
};
|
|
43
68
|
meta?: Record<string, unknown>;
|
|
44
69
|
}>;
|
|
45
|
-
verifyWebhook(store:
|
|
70
|
+
verifyWebhook(store: ChannelStore, request: Request): Promise<{
|
|
46
71
|
ok: false;
|
|
47
72
|
error: CommerceValidationError;
|
|
48
73
|
} | {
|
package/dist/mock-connector.js
CHANGED
|
@@ -1,16 +1,67 @@
|
|
|
1
1
|
import { CommerceValidationError, Err, Ok, defineChannelConnector, } from "@porulle/core";
|
|
2
|
+
const defaultCatalog = [{
|
|
3
|
+
externalId: "mock-product-1",
|
|
4
|
+
slug: "mock-channel-product",
|
|
5
|
+
title: "Mock Channel Product",
|
|
6
|
+
description: "Imported through the mock connector.",
|
|
7
|
+
attributes: [{
|
|
8
|
+
locale: "en",
|
|
9
|
+
title: "Mock Channel Product",
|
|
10
|
+
subtitle: "A complete mock catalog item",
|
|
11
|
+
description: "Imported through the mock connector.",
|
|
12
|
+
richDescription: { blocks: [{ type: "paragraph", text: "Mock product details." }] },
|
|
13
|
+
seoTitle: "Mock Channel Product | Porulle",
|
|
14
|
+
seoDescription: "A mock product with the complete channel catalog shape.",
|
|
15
|
+
}],
|
|
16
|
+
images: [
|
|
17
|
+
{
|
|
18
|
+
externalId: "mock-image-primary",
|
|
19
|
+
url: "https://mock.channel.test/images/mock-product-1-primary.jpg",
|
|
20
|
+
alt: "Mock Channel Product",
|
|
21
|
+
role: "primary",
|
|
22
|
+
sortOrder: 0,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
externalId: "mock-image-variant",
|
|
26
|
+
url: "https://mock.channel.test/images/mock-variant-1.jpg",
|
|
27
|
+
alt: "Mock Channel Product blue variant",
|
|
28
|
+
role: "gallery",
|
|
29
|
+
sortOrder: 1,
|
|
30
|
+
variantExternalIds: ["mock-variant-1"],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
options: [{
|
|
34
|
+
name: "color",
|
|
35
|
+
displayName: "Color",
|
|
36
|
+
sortOrder: 0,
|
|
37
|
+
values: [{ value: "blue", displayValue: "Blue", sortOrder: 0 }],
|
|
38
|
+
}],
|
|
39
|
+
tags: ["mock", "featured"],
|
|
40
|
+
brand: "Porulle",
|
|
41
|
+
categories: ["mock-products"],
|
|
42
|
+
status: "active",
|
|
43
|
+
variants: [{
|
|
44
|
+
externalId: "mock-variant-1",
|
|
45
|
+
sku: "MOCK-SKU-1",
|
|
46
|
+
barcode: "0123456789012",
|
|
47
|
+
optionValues: { color: "blue" },
|
|
48
|
+
prices: [{ currency: "USD", amount: 2500 }],
|
|
49
|
+
}],
|
|
50
|
+
}];
|
|
2
51
|
export function mockChannelConnector(options = {}) {
|
|
3
52
|
const orders = new Map();
|
|
53
|
+
const catalog = new Map();
|
|
4
54
|
return defineChannelConnector({
|
|
5
55
|
providerId: "mock",
|
|
6
56
|
capabilities: {
|
|
7
57
|
importCatalog: true,
|
|
8
58
|
importInventory: true,
|
|
9
59
|
pushOrder: true,
|
|
60
|
+
pushCatalog: true,
|
|
10
61
|
receiveWebhooks: true,
|
|
11
62
|
},
|
|
12
|
-
async importCatalog() {
|
|
13
|
-
return Ok({ items: options.catalog ??
|
|
63
|
+
async importCatalog(_store) {
|
|
64
|
+
return Ok({ items: options.catalog ?? defaultCatalog, nextCursor: null });
|
|
14
65
|
},
|
|
15
66
|
async fetchInventory(_store, ids) {
|
|
16
67
|
const requestedIds = ids ?? [];
|
|
@@ -33,6 +84,30 @@ export function mockChannelConnector(options = {}) {
|
|
|
33
84
|
remoteUrl: `https://mock.channel.test/orders/${remoteOrderId}`,
|
|
34
85
|
});
|
|
35
86
|
},
|
|
87
|
+
async pushCatalog(_store, items, opts) {
|
|
88
|
+
if (options.pushCatalogTransportError)
|
|
89
|
+
return Err(options.pushCatalogTransportError);
|
|
90
|
+
const outcomes = items.map((item) => {
|
|
91
|
+
const error = options.pushCatalogFailures?.[item.externalId];
|
|
92
|
+
if (error)
|
|
93
|
+
return { externalId: item.externalId, ok: false, error };
|
|
94
|
+
const previous = catalog.get(item.externalId);
|
|
95
|
+
const previousFields = previous
|
|
96
|
+
? [...previous.fields, ...(previous.variants ?? []).flatMap((variant) => variant.fields)]
|
|
97
|
+
.map((field) => ({ fieldPath: field.fieldPath, value: structuredClone(field.value) }))
|
|
98
|
+
: [];
|
|
99
|
+
if (opts?.dryRun !== true)
|
|
100
|
+
catalog.set(item.externalId, structuredClone(item));
|
|
101
|
+
return {
|
|
102
|
+
externalId: item.externalId,
|
|
103
|
+
ok: true,
|
|
104
|
+
...(previousFields.length > 0 ? { previousFields } : {}),
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
if (opts?.dryRun !== true)
|
|
108
|
+
options.onPushCatalog?.(structuredClone(items));
|
|
109
|
+
return Ok({ outcomes });
|
|
110
|
+
},
|
|
36
111
|
async fetchOrderStatus(_store, remoteId) {
|
|
37
112
|
if (!orders.has(remoteId)) {
|
|
38
113
|
return Err(new CommerceValidationError(`Mock order "${remoteId}" was not found.`));
|