@things-factory/integration-sellercraft 8.0.0-beta.9 → 8.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. package/dist-server/tsconfig.tsbuildinfo +1 -1
  2. package/package.json +14 -14
  3. package/server/constants/index.ts +2 -0
  4. package/server/constants/order-status-mapping.ts +28 -0
  5. package/server/constants/platform.ts +6 -0
  6. package/server/controllers/index.ts +5 -0
  7. package/server/controllers/sellercraft/apis/add-inbound-order.ts +50 -0
  8. package/server/controllers/sellercraft/apis/echo.ts +14 -0
  9. package/server/controllers/sellercraft/apis/fetch-order-document.ts +18 -0
  10. package/server/controllers/sellercraft/apis/index.ts +8 -0
  11. package/server/controllers/sellercraft/apis/initiate-order-document.ts +17 -0
  12. package/server/controllers/sellercraft/apis/initiate-order-shipment.ts +29 -0
  13. package/server/controllers/sellercraft/apis/pack-marketplace-order.ts +35 -0
  14. package/server/controllers/sellercraft/apis/update-marketplace-order.ts +14 -0
  15. package/server/controllers/sellercraft/apis/update-product.ts +21 -0
  16. package/server/controllers/sellercraft/index.ts +7 -0
  17. package/server/controllers/sellercraft/platform-action.ts +39 -0
  18. package/server/controllers/sellercraft/sellercraft.ts +109 -0
  19. package/server/controllers/sellercraft-api/decorators.ts +52 -0
  20. package/server/controllers/sellercraft-api/index.ts +51 -0
  21. package/server/controllers/sellercraft-api/types.ts +0 -0
  22. package/server/controllers/sellercraft-channel-integration/apis/echo.ts +14 -0
  23. package/server/controllers/sellercraft-channel-integration/apis/index.ts +6 -0
  24. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-categories.ts +37 -0
  25. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-category-attributes.ts +65 -0
  26. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-order-package.ts +62 -0
  27. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-order.ts +92 -0
  28. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-product.ts +97 -0
  29. package/server/controllers/sellercraft-channel-integration/index.ts +7 -0
  30. package/server/controllers/sellercraft-channel-integration/platform-action.ts +39 -0
  31. package/server/controllers/sellercraft-channel-integration/sellercraft-channel-integration.ts +115 -0
  32. package/server/controllers/sellercraft-channel-integration-api/decorators.ts +45 -0
  33. package/server/controllers/sellercraft-channel-integration-api/index.ts +45 -0
  34. package/server/controllers/sellercraft-channel-integration-api/types.ts +0 -0
  35. package/server/index.ts +7 -0
  36. package/server/middlewares/index.ts +3 -0
  37. package/server/migrations/index.ts +9 -0
  38. package/server/routers/sellercraft-router.ts +326 -0
  39. package/server/routes.ts +32 -0
  40. package/server/service/index.ts +23 -0
  41. package/server/service/marketplace-channel/index.ts +6 -0
  42. package/server/service/marketplace-channel/marketplace-channel-order-mutation.ts +456 -0
  43. package/server/service/marketplace-channel/marketplace-channel-product-mutation.ts +282 -0
  44. package/server/service/marketplace-channel/marketplace-channel.ts +76 -0
  45. package/server/service/sellercraft/index.ts +6 -0
  46. package/server/service/sellercraft/sellercraft-mutation.ts +126 -0
  47. package/server/service/sellercraft/sellercraft-query.ts +43 -0
  48. package/server/service/sellercraft/sellercraft-type.ts +54 -0
  49. package/server/service/sellercraft/sellercraft.ts +96 -0
  50. package/server/utils/tokencraft-util.ts +60 -0
  51. package/tsconfig.json +9 -0
@@ -0,0 +1,65 @@
1
+ /* https://docs.sellercraft.co/docs/api-integrations/b3A6MjQzODUxMTE-ingest-channel-category-attributes */
2
+
3
+ export function ingestChannelCategoryAttributes() {
4
+ return {
5
+ method: 'post',
6
+ path: '/channel/ingest/category/attribute',
7
+ denormalize(req) {
8
+ let { channelAttributeKeys, scAttributeKeys } = req
9
+
10
+ let channel_attribute_keys = channelAttributeKeys.map(e => {
11
+ const {
12
+ channelCode: channel_code,
13
+ channelCountry: channel_country,
14
+ nativeCategoryId: native_category_id,
15
+ attributeKey: attribute_key,
16
+ valueType: value_type,
17
+ valueOptions: value_options,
18
+ displayName: display_name,
19
+ isCore: is_core,
20
+ isSearchProp: is_search_prop,
21
+ isOptional: is_optional
22
+ } = e
23
+
24
+ return {
25
+ channel_code,
26
+ channel_country,
27
+ native_category_id,
28
+ attribute_key,
29
+ value_type,
30
+ value_options,
31
+ display_name,
32
+ is_core,
33
+ is_search_prop,
34
+ is_optional
35
+ }
36
+ })
37
+
38
+ let sc_attribute_keys = scAttributeKeys.map(e => {
39
+ const {
40
+ channelCode: channel_code,
41
+ channelCountry: channel_country,
42
+ attributeKey: attribute_key,
43
+ displayName: display_name
44
+ } = e
45
+
46
+ return {
47
+ channel_code,
48
+ channel_country,
49
+ attribute_key,
50
+ display_name
51
+ }
52
+ })
53
+
54
+ return {
55
+ payload: {
56
+ channel_attribute_keys,
57
+ sc_attribute_keys
58
+ }
59
+ }
60
+ },
61
+ normalize(res) {
62
+ return res
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,62 @@
1
+ /* https://docs.sellercraft.co/docs/api-integrations/b3A6MjQzODUxMTQ-ingest-channel-order-package */
2
+
3
+ export function ingestChannelOrderPackage() {
4
+ return {
5
+ method: 'post',
6
+ path: '/channel/ingest/order/package',
7
+ denormalize(req) {
8
+ let {
9
+ channelShopId: channel_shop_id,
10
+ nativeOrderId: native_order_id,
11
+ nativePackageId: native_package_id,
12
+ shippingTrackingCode: shipping_tracking_code,
13
+ shippingTypeValue: shipping_type_value,
14
+ warehouseCode: warehouse_code,
15
+ shipper,
16
+ documents,
17
+ shipperLastMile: shipper_last_mile,
18
+ orderItemIds: order_item_ids
19
+ } = req
20
+
21
+ documents = documents.map(e => {
22
+ const { fileTypeValue: file_type_value, mimeType: mime_type, file } = e
23
+
24
+ return {
25
+ file_type_value,
26
+ mime_type,
27
+ file
28
+ }
29
+ })
30
+
31
+ shipper = {
32
+ is_cod_supported: shipper.isCodSupported,
33
+ name: shipper.name
34
+ }
35
+
36
+ shipper_last_mile = {
37
+ is_cod_supported: shipper_last_mile.isCodSupported,
38
+ name: shipper_last_mile.name
39
+ }
40
+
41
+ let newOrderPackage: any = {
42
+ channel_shop_id,
43
+ native_order_id,
44
+ native_package_id: native_package_id.toString(),
45
+ shipping_tracking_code,
46
+ shipping_type_value,
47
+ warehouse_code,
48
+ shipper,
49
+ documents,
50
+ shipper_last_mile,
51
+ order_item_ids
52
+ }
53
+
54
+ return {
55
+ payload: { ...newOrderPackage }
56
+ }
57
+ },
58
+ normalize(res) {
59
+ return res
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,92 @@
1
+ /* https://docs.sellercraft.co/docs/api-integrations/b3A6MjQzODQ4OTg-ingest-channel-order */
2
+
3
+ import { ORDER_STATUS } from '../../../constants'
4
+
5
+ export function ingestChannelOrder() {
6
+ return {
7
+ method: 'post',
8
+ path: '/channel/ingest/order',
9
+ denormalize(req) {
10
+ const { orders } = req
11
+
12
+ let newOrders = orders.map(order => {
13
+ return {
14
+ organisation_id: order.organisationId,
15
+ channel_shop_id: order.channelShopId,
16
+ native_order_id: order.id.toString(),
17
+ native_order_display_id: order?.sellercraftDisplayOrderNo,
18
+ ordered_at_date: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
19
+ ordered_at_time: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
20
+ updated_at_date: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
21
+ updated_at_time: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
22
+ customer_first_name: order.custFirstName,
23
+ customer_last_name: order.custLastName,
24
+ charges: order.charges.map(charge => {
25
+ return {
26
+ charge_type_value: charge.name,
27
+ amount_gross: parseFloat(charge.grossAmount) || 0,
28
+ amount_nett: parseFloat(charge.nettAmount) || 0
29
+ }
30
+ }),
31
+ address_shipping: order?.shipAddress1 ? {
32
+ first_name: order.shipFirstName,
33
+ last_name: order.shipLastName,
34
+ line_1: order.shipAddress1,
35
+ line_2: order.shipAddress2,
36
+ line_3: order.shipAddress3,
37
+ line_4: order.shipAddress4,
38
+ line_5: order.shipAddress5,
39
+ city: order.shipCity,
40
+ postal_code: order.shipPostalCode,
41
+ country: order.shipCountry,
42
+ phone_1: order.shipPhone1,
43
+ phone_2: order.shipPhone2
44
+ } : null,
45
+ address_billing: {
46
+ first_name: order.billFirstName,
47
+ last_name: order.billLastName,
48
+ line_1: order.billAddress1,
49
+ line_2: order.billAddress2,
50
+ line_3: order.billAddress3,
51
+ line_4: order.billAddress4,
52
+ line_5: order.billAddress5,
53
+ city: order.billCity,
54
+ postal_code: order.billPostalCode,
55
+ country: order.billCountry,
56
+ phone_1: order.billPhone1,
57
+ phone_2: order.billPhone2
58
+ },
59
+ order_items: order.mappedOrderItems.map(orderItem => {
60
+ return {
61
+ native_item_id: orderItem.id.toString(),
62
+ native_variant_id: orderItem.variationId.toString(),
63
+ currency_code: orderItem.currency,
64
+ ordered_at_date: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
65
+ ordered_at_time: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
66
+ updated_at_date: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
67
+ updated_at_time: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
68
+ sla_expires_at: orderItem?.slaExpiresAt ? new Date(orderItem.slaExpiresAt).toISOString() : null,
69
+ charges: orderItem.charges.map(charge => {
70
+ return {
71
+ charge_type_value: charge.name,
72
+ amount_gross: parseFloat(charge.grossAmount) || 0,
73
+ amount_nett: parseFloat(charge.nettAmount) || 0
74
+ }
75
+ }),
76
+ order_status_value: ORDER_STATUS[`${order.status}`]
77
+ }
78
+ }),
79
+ seller_logistics: order?.isSOF,
80
+ shipping_type: order?.shipAddress1 ? 'DROP_SHIPPING' : 'SELF_PICKUP'
81
+ }
82
+ })
83
+
84
+ return {
85
+ payload: [...newOrders]
86
+ }
87
+ },
88
+ normalize(res) {
89
+ return res
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,97 @@
1
+ /* https://docs.sellercraft.co/docs/api-integrations/b3A6MTY4NjQxODU-initiate-order-shipment */
2
+
3
+ export function ingestChannelProduct() {
4
+ return {
5
+ method: 'post',
6
+ path: '/channel/ingest/product',
7
+ denormalize(req) {
8
+ const { products } = req
9
+
10
+ let newProducts = products.map(product => {
11
+ let productVariations: any[] = product.variations.map(variant => {
12
+ return {
13
+ seller_sku: variant.variationSku,
14
+ native_variant_id: variant.variationId.toString(),
15
+ label: variant.name,
16
+ is_enabled: variant.isEnabled || true,
17
+ is_sellable: variant.isSellable || true,
18
+ is_deleted: false, // default
19
+ variant_attributes: variant?.attributes
20
+ ? variant?.attributes.map(attribute => {
21
+ return {
22
+ ...attribute,
23
+ native_attribute_id: attribute?.native_attribute_id?.toString()
24
+ }
25
+ })
26
+ : [],
27
+ stock_locked: variant?.stockLocked ? variant.stockLocked : 0,
28
+ native_stock_reported: variant?.stockReported ? variant.stockReported : 0,
29
+ price_full: variant.fullPrice || 0,
30
+ price_discounted: variant.priceDiscounted || variant.fullPrice || 0,
31
+ inventory_products: variant?.inventoryProducts
32
+ ? variant.inventoryProducts.map(inventoryProduct => {
33
+ return {
34
+ quantity: inventoryProduct.qty || 0,
35
+ name: inventoryProduct.name,
36
+ inventory_sku: inventoryProduct.sku,
37
+ product_versions: inventoryProduct?.productVersions
38
+ ? inventoryProduct.productVersions.map(productVersion => {
39
+ return {
40
+ label: productVersion.label,
41
+ package_content: productVersion.packageContent,
42
+ package_length_mm: productVersion?.packageLengthMM ? productVersion.packageLengthMM : 0,
43
+ package_width_mm: productVersion?.packageWidthMM ? productVersion.packageWidthMM : 0,
44
+ package_height_mm: productVersion?.packageHeightMM ? productVersion.packageHeightMM : 0,
45
+ package_weight_gram: productVersion?.packageWeightGram
46
+ ? productVersion.packageWeightGram
47
+ : 0,
48
+ stock_available: productVersion.qty || 0
49
+ }
50
+ })
51
+ : []
52
+ }
53
+ })
54
+ : [],
55
+ extra_metadata: variant?.extraMetadata
56
+ }
57
+ })
58
+
59
+ return {
60
+ organisation_id: product.organisationId,
61
+ channel_shop_id: product.channelShopId,
62
+ channel_code: product.channelCode,
63
+ channel_country: product.channelCountry,
64
+ native_category_id: product.categoryId.toString(),
65
+ native_product_id: product.productId.toString(),
66
+ label: product.name,
67
+ brand: product.brand || '',
68
+ is_verified: product.isVerified || true,
69
+ flexible_attributes:
70
+ product.channelCode == 'WCM' || product.channelCode == 'MGT' || product.channelCode == 'SPF' ? true : false, // add channels that do not support category_attributes ingestion
71
+ images:
72
+ product?.images?.map(image => {
73
+ return {
74
+ file_url: image.url
75
+ }
76
+ }) || [],
77
+ product_attributes:
78
+ product?.attributes?.map(attribute => {
79
+ return {
80
+ ...attribute,
81
+ native_attribute_id: attribute.native_attribute_id.toString()
82
+ }
83
+ }) || [],
84
+ variants: productVariations,
85
+ has_all_variants: product.channelCode == 'MGT' ? false : true
86
+ }
87
+ })
88
+
89
+ return {
90
+ payload: [...newProducts]
91
+ }
92
+ },
93
+ normalize(res) {
94
+ return res
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,7 @@
1
+ import { SellercraftChannelIntegrationAPI } from '../sellercraft-channel-integration-api'
2
+ import * as APIS from './apis'
3
+ import { action } from './platform-action'
4
+
5
+ export * from './sellercraft-channel-integration'
6
+
7
+ SellercraftChannelIntegrationAPI.registerPlatform('sellercraftChannelIntegration', action, APIS)
@@ -0,0 +1,39 @@
1
+ import { config } from '@things-factory/env'
2
+
3
+ import { SellercraftChannelIntegration } from './sellercraft-channel-integration'
4
+
5
+ const sellercraftConfig = config.get('sellercraftChannelIntegrationConfig', {})
6
+ const { apiKey } = sellercraftConfig
7
+
8
+ function substitute(path, obj) {
9
+ var props = []
10
+ var re = /{([^}]+)}/g
11
+ var text
12
+
13
+ while ((text = re.exec(path))) {
14
+ props.push(text[1])
15
+ }
16
+
17
+ var result = path
18
+ props.forEach(prop => {
19
+ let value = obj[prop.trim()]
20
+ result = result.replace(`{${prop}}`, value === undefined ? '' : value)
21
+ })
22
+
23
+ return result
24
+ }
25
+
26
+ export const action = async ({ method = 'get', path, request }) => {
27
+ const client = new SellercraftChannelIntegration({ apiKey })
28
+
29
+ const { resource = {}, payload = {} } = request
30
+
31
+ path = substitute(path, resource)
32
+
33
+ var response = await client[method](path, payload)
34
+ if (response.errors) {
35
+ throw response
36
+ }
37
+
38
+ return response
39
+ }
@@ -0,0 +1,115 @@
1
+ import fetch from 'node-fetch'
2
+ import { v4 as uuidv4 } from 'uuid'
3
+ import { createPayloadLog } from '@things-factory/integration-base'
4
+
5
+ const debug = require('debug')('things-factory:integration-sellercraft:sellercraft')
6
+
7
+ export type SellercraftConfig = {
8
+ apiKey: string
9
+ }
10
+
11
+ export class SellercraftChannelIntegration {
12
+ private config: SellercraftConfig
13
+
14
+ constructor(config: SellercraftConfig) {
15
+ this.config = {
16
+ ...config
17
+ }
18
+ }
19
+
20
+ getBaseUrl() {
21
+ return `https://open.sellercraft.co/v1`
22
+ }
23
+
24
+ generateRequestId() {
25
+ return uuidv4()
26
+ }
27
+
28
+ async post(path: string, data: any = {}) {
29
+ const { apiKey } = this.config
30
+
31
+ debug('data', data)
32
+
33
+ const jsondata = JSON.stringify(data)
34
+ const requestId: string = this.generateRequestId()
35
+ const fullPath: string = `${this.getBaseUrl()}${path}`
36
+
37
+ const response: any = await fetch(fullPath, {
38
+ method: 'post',
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ request_id: requestId,
42
+ 'X-Api-Key': apiKey
43
+ },
44
+ body: jsondata
45
+ })
46
+
47
+ const result = await response.json()
48
+ try {
49
+ createPayloadLog(JSON.parse(jsondata)[0]?.channel_shop_id || requestId, fullPath, jsondata, result, {
50
+ state: { domain: null }
51
+ })
52
+ } catch (e) {}
53
+ if (response.ok) {
54
+ return result
55
+ } else {
56
+ throw new Error(`(${response.status}) ${result.detail}`)
57
+ }
58
+ }
59
+
60
+ async put(path: string, data: any = {}) {
61
+ const { apiKey } = this.config
62
+
63
+ const jsondata = JSON.stringify(data)
64
+ const requestId: string = this.generateRequestId()
65
+ const fullPath: string = `${this.getBaseUrl()}${path}`
66
+ debug('data', data)
67
+
68
+ const response: any = await fetch(fullPath, {
69
+ method: 'put',
70
+ headers: {
71
+ 'Content-Type': 'application/json',
72
+ request_id: requestId,
73
+ 'x-api-key': apiKey
74
+ },
75
+ body: jsondata
76
+ })
77
+
78
+ if (response.ok) {
79
+ return await response.json()
80
+ } else {
81
+ const result = await response.json()
82
+ throw new Error(`(${response.status}) ${result.message}`)
83
+ }
84
+ }
85
+
86
+ async get(path: string, data: any = {}) {
87
+ const { apiKey } = this.config
88
+
89
+ const qs = Object.entries(data)
90
+ .map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`)
91
+ .join('&')
92
+
93
+ const fullPath: string = `${this.getBaseUrl()}${path}`
94
+ const endpoint = `${fullPath}${qs ? '?' + qs : ''}`
95
+ debug('endpoint', endpoint)
96
+
97
+ const requestId: string = this.generateRequestId()
98
+
99
+ const response: any = await fetch(endpoint, {
100
+ method: 'get',
101
+ headers: {
102
+ 'Content-Type': 'application/json',
103
+ request_id: requestId,
104
+ 'x-api-key': apiKey
105
+ }
106
+ })
107
+
108
+ if (response.ok) {
109
+ return await response.json()
110
+ } else {
111
+ const result = await response.json()
112
+ throw new Error(`(${response.status}) ${result.message}`)
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,45 @@
1
+ import Debug from 'debug'
2
+
3
+ import { Sellercraft } from '../../service'
4
+
5
+ const debug = Debug('things-factory:integration-marketplace:store-api-decorator')
6
+
7
+ const NOOP = v => v
8
+
9
+ export const api = (target: Object, property: string, descriptor: TypedPropertyDescriptor<any>): any => {
10
+ const method = descriptor.value
11
+
12
+ descriptor.value = async function (store: Sellercraft, request) {
13
+ const SellercraftChannelIntegrationAPI = this
14
+
15
+ var { platform } = store
16
+
17
+ var { action: platformAction, apis } = SellercraftChannelIntegrationAPI.getPlatform(platform)
18
+
19
+ var m = apis[method.name]
20
+ if (!m) {
21
+ throw Error(`Sellercraft doesn't have API ${method.name}`)
22
+ }
23
+
24
+ var {
25
+ path,
26
+ method: httpMethod = 'post',
27
+ denormalize = NOOP,
28
+ normalize = NOOP,
29
+ action = platformAction
30
+ } = m.apply(this, [request])
31
+
32
+ var denormalized = await denormalize(request || {}, { store })
33
+ debug('request', denormalized)
34
+
35
+ var response = await action.apply(this, [
36
+ { store, method: httpMethod, path, request: denormalized, platformAction }
37
+ ])
38
+
39
+ debug('response', response)
40
+
41
+ return await normalize(response, { store })
42
+ }
43
+
44
+ return descriptor
45
+ }
@@ -0,0 +1,45 @@
1
+ import { getRepository } from '@things-factory/shell'
2
+
3
+ import { Sellercraft } from '../../service'
4
+ import { api } from './decorators'
5
+
6
+ export class SellercraftChannelIntegrationAPI {
7
+ static platforms = {}
8
+
9
+ static registerPlatform(name, action, apis) {
10
+ SellercraftChannelIntegrationAPI.platforms[name] = {
11
+ action,
12
+ apis
13
+ }
14
+ }
15
+
16
+ static getPlatform(name) {
17
+ return SellercraftChannelIntegrationAPI.platforms[name]
18
+ }
19
+
20
+ static async getSellercraft(id) {
21
+ const repository = getRepository(Sellercraft)
22
+ return await repository.findOne({
23
+ where: { id },
24
+ relations: ['domain']
25
+ })
26
+ }
27
+
28
+ @api
29
+ static echo(sellercraft, req): any {}
30
+
31
+ @api
32
+ static ingestChannelCategories(sellercraft, req): any {}
33
+
34
+ @api
35
+ static ingestChannelCategoryAttributes(sellercraft, req): any {}
36
+
37
+ @api
38
+ static ingestChannelOrderPackage(sellercraft, req): any {}
39
+
40
+ @api
41
+ static ingestChannelOrder(sellercraft, req): any {}
42
+
43
+ @api
44
+ static ingestChannelProduct(sellercraft, req): any {}
45
+ }
@@ -0,0 +1,7 @@
1
+ import './routes'
2
+
3
+ export * from './migrations'
4
+ export * from './middlewares'
5
+ export * from './controllers'
6
+ export * from './service'
7
+ export * from './constants'
@@ -0,0 +1,3 @@
1
+ export function initMiddlewares(app) {
2
+ /* can add middlewares into app */
3
+ }
@@ -0,0 +1,9 @@
1
+ const glob = require('glob')
2
+ const path = require('path')
3
+
4
+ export var migrations = []
5
+
6
+ glob.sync(path.resolve(__dirname, '.', '**', '*.js')).forEach(function(file) {
7
+ if (file.indexOf('index.js') !== -1) return
8
+ migrations = migrations.concat(Object.values(require(path.resolve(file))) || [])
9
+ })