feed-common 1.48.1 → 1.49.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,96 @@
1
+ // /**
2
+ // * Additional cut of the item. Used together with size_type to represent combined size types for apparel items.
3
+ // */
4
+ // additionalSizeType?: string | null;
5
+ // /**
6
+ // * Used to group items in an arbitrary way. Only for CPA%, discouraged otherwise.
7
+ // */
8
+ // adsGrouping?: string | null;
9
+ // /**
10
+ // * Similar to ads_grouping, but only works on CPC.
11
+ // */
12
+ // adsLabels?: string[] | null;
13
+ // /**
14
+ // * URL for the canonical version of your item's landing page.
15
+ // */
16
+ // canonicalLink?: string | null;
17
+ // /**
18
+ // * Product [certification](https://support.google.com/merchants/answer/13528839), introduced for EU energy efficiency labeling compliance using the [EU EPREL](https://eprel.ec.europa.eu/screen/home) database.
19
+ // */
20
+ // certifications?: Schema$ProductCertification[];
21
+ // /**
22
+ // * Extra fields to export to the Cloud Retail program.
23
+ // */
24
+ // cloudExportAdditionalProperties?: Schema$CloudExportAdditionalProperties[];
25
+ // /**
26
+ // * A list of custom (merchant-provided) attributes. It can also be used for submitting any attribute of the feed specification in its generic form (for example, `{ "name": "size type", "value": "regular" \}`). This is useful for submitting attributes not explicitly exposed by the API, such as additional attributes used for Buy on Google (formerly known as Shopping Actions).
27
+ // */
28
+ // customAttributes?: Schema$CustomAttribute[];
29
+ // /**
30
+ // * The date time when an offer becomes visible in search results across Google’s YouTube surfaces, in [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601) format. See [Disclosure date](https://support.google.com/merchants/answer/13034208) for more information.
31
+ // */
32
+ // disclosureDate?: string | null;
33
+ // /**
34
+ // * An identifier for an item for dynamic remarketing campaigns.
35
+ // */
36
+ // displayAdsId?: string | null;
37
+ // /**
38
+ // * URL directly to your item's landing page for dynamic remarketing campaigns.
39
+ // */
40
+ // displayAdsLink?: string | null;
41
+ // /**
42
+ // * Advertiser-specified recommendations.
43
+ // */
44
+ // displayAdsSimilarIds?: string[] | null;
45
+ // /**
46
+ // * Title of an item for dynamic remarketing campaigns.
47
+ // */
48
+ // displayAdsTitle?: string | null;
49
+ // /**
50
+ // * Offer margin for dynamic remarketing campaigns.
51
+ // */
52
+ // displayAdsValue?: number | null;
53
+ // /**
54
+ // * The energy efficiency class as defined in EU directive 2010/30/EU.
55
+ // */
56
+ // energyEfficiencyClass?: string | null;
57
+ // /**
58
+ // * Feed label for the item. Either `targetCountry` or `feedLabel` is required. Must be less than or equal to 20 uppercase letters (A-Z), numbers (0-9), and dashes (-).
59
+ // */
60
+ // feedLabel?: string | null;
61
+ // /**
62
+ // * Number and amount of installments to pay for an item.
63
+ // */
64
+ // installment?: Schema$Installment;
65
+ // /**
66
+ // * Identifies what kind of resource this is. Value: the fixed string "`content#product`"
67
+ // */
68
+ // kind?: string | null;
69
+ // /**
70
+ // * URL template for merchant hosted local storefront.
71
+ // */
72
+ // linkTemplate?: string | null;
73
+ // /**
74
+ // * The energy efficiency class as defined in EU directive 2010/30/EU.
75
+ // */
76
+ // minEnergyEfficiencyClass?: string | null;
77
+ // /**
78
+ // * URL template for merchant hosted local storefront optimized for mobile devices.
79
+ // */
80
+ // mobileLinkTemplate?: string | null;
81
+ // /**
82
+ // * The pick up option for the item. Acceptable values are: - "`buy`" - "`reserve`" - "`ship to store`" - "`not supported`"
83
+ // */
84
+ // pickupMethod?: string | null;
85
+ // /**
86
+ // * Item store pickup timeline. Acceptable values are: - "`same day`" - "`next day`" - "`2-day`" - "`3-day`" - "`4-day`" - "`5-day`" - "`6-day`" - "`7-day`" - "`multi-week`"
87
+ // */
88
+ // pickupSla?: string | null;
89
+ // /**
90
+ // * The quantity of the product that is available for selling on Google. Supported only for online products.
91
+ // */
92
+ // sellOnGoogleQuantity?: string | null;
93
+ // /**
94
+ // * The source of the offer, that is, how the offer was created. Acceptable values are: - "`api`" - "`crawl`" - "`feed`"
95
+ // */
96
+ // source?: string | null;
@@ -7,6 +7,7 @@ import { XmlFeedTemplateType } from '../../types/profile.types.js';
7
7
  import { googleFeedTemplate } from './google.template.js';
8
8
  import { tiktokFeedTemplate } from './tiktok.template.js';
9
9
  import { microsoftTemplate } from './microsoft.template.js';
10
+ import { googleApiTemplate } from './google-api.template.js';
10
11
 
11
12
  export const hasMacro = (v: string) => /{{[^}]+}}/.test(v);
12
13
  export const removeMacro = (v: string) => v.replaceAll(/{{[^}]+}}/g, '');
@@ -91,7 +92,230 @@ export function getTemplate(type: XMLFeedType): XmlFeedTemplateType {
91
92
  return tiktokFeedTemplate;
92
93
  case XMLFeedType.Microsoft:
93
94
  return microsoftTemplate;
95
+ case XMLFeedType.GMC_API:
96
+ return googleApiTemplate;
94
97
  default:
95
98
  throw new Error(`Invalid feed type: ${type}`);
96
99
  }
97
100
  }
101
+
102
+ export const currencySchema = z.enum([
103
+ 'AED',
104
+ 'AFN',
105
+ 'ALL',
106
+ 'AMD',
107
+ 'ANG',
108
+ 'AOA',
109
+ 'ARS',
110
+ 'AUD',
111
+ 'AWG',
112
+ 'AZN',
113
+ 'BAM',
114
+ 'BBD',
115
+ 'BDT',
116
+ 'BGN',
117
+ 'BHD',
118
+ 'BIF',
119
+ 'BMD',
120
+ 'BND',
121
+ 'BOB',
122
+ 'BOV',
123
+ 'BRL',
124
+ 'BSD',
125
+ 'BTN',
126
+ 'BWP',
127
+ 'BYN',
128
+ 'BZD',
129
+ 'CAD',
130
+ 'CDF',
131
+ 'CHE',
132
+ 'CHF',
133
+ 'CHW',
134
+ 'CLF',
135
+ 'CLP',
136
+ 'CNY',
137
+ 'COP',
138
+ 'COU',
139
+ 'CRC',
140
+ 'CUC',
141
+ 'CUP',
142
+ 'CVE',
143
+ 'CZK',
144
+ 'DJF',
145
+ 'DKK',
146
+ 'DOP',
147
+ 'DZD',
148
+ 'EGP',
149
+ 'ERN',
150
+ 'ETB',
151
+ 'EUR',
152
+ 'FJD',
153
+ 'FKP',
154
+ 'FOK',
155
+ 'GBP',
156
+ 'GEL',
157
+ 'GGP',
158
+ 'GHS',
159
+ 'GIP',
160
+ 'GMD',
161
+ 'GNF',
162
+ 'GTQ',
163
+ 'GYD',
164
+ 'HKD',
165
+ 'HNL',
166
+ 'HRK',
167
+ 'HTG',
168
+ 'HUF',
169
+ 'IDR',
170
+ 'ILS',
171
+ 'IMP',
172
+ 'INR',
173
+ 'IQD',
174
+ 'IRR',
175
+ 'ISK',
176
+ 'JMD',
177
+ 'JOD',
178
+ 'JPY',
179
+ 'KES',
180
+ 'KGS',
181
+ 'KHR',
182
+ 'KID',
183
+ 'KMF',
184
+ 'KRW',
185
+ 'KWD',
186
+ 'KYD',
187
+ 'KZT',
188
+ 'LAK',
189
+ 'LBP',
190
+ 'LKR',
191
+ 'LRD',
192
+ 'LSL',
193
+ 'LYD',
194
+ 'MAD',
195
+ 'MDL',
196
+ 'MGA',
197
+ 'MKD',
198
+ 'MMK',
199
+ 'MNT',
200
+ 'MOP',
201
+ 'MRU',
202
+ 'MUR',
203
+ 'MVR',
204
+ 'MWK',
205
+ 'MXN',
206
+ 'MXV',
207
+ 'MYR',
208
+ 'MZN',
209
+ 'NAD',
210
+ 'NGN',
211
+ 'NIO',
212
+ 'NOK',
213
+ 'NPR',
214
+ 'NZD',
215
+ 'OMR',
216
+ 'PAB',
217
+ 'PEN',
218
+ 'PGK',
219
+ 'PHP',
220
+ 'PKR',
221
+ 'PLN',
222
+ 'PYG',
223
+ 'QAR',
224
+ 'RON',
225
+ 'RSD',
226
+ 'RUB',
227
+ 'RWF',
228
+ 'SAR',
229
+ 'SBD',
230
+ 'SCR',
231
+ 'SDG',
232
+ 'SEK',
233
+ 'SGD',
234
+ 'SHP',
235
+ 'SLE',
236
+ 'SLL',
237
+ 'SOS',
238
+ 'SRD',
239
+ 'SSP',
240
+ 'STN',
241
+ 'SYP',
242
+ 'SZL',
243
+ 'THB',
244
+ 'TJS',
245
+ 'TMT',
246
+ 'TND',
247
+ 'TOP',
248
+ 'TRY',
249
+ 'TTD',
250
+ 'TVD',
251
+ 'TWD',
252
+ 'TZS',
253
+ 'UAH',
254
+ 'UGX',
255
+ 'USD',
256
+ 'USN',
257
+ 'UYI',
258
+ 'UYU',
259
+ 'UYW',
260
+ 'UZS',
261
+ 'VED',
262
+ 'VES',
263
+ 'VND',
264
+ 'VUV',
265
+ 'WST',
266
+ 'XAF',
267
+ 'XAG',
268
+ 'XAU',
269
+ 'XBA',
270
+ 'XBB',
271
+ 'XBC',
272
+ 'XBD',
273
+ 'XCD',
274
+ 'XDR',
275
+ 'XOF',
276
+ 'XPD',
277
+ 'XPF',
278
+ 'XPT',
279
+ 'XSU',
280
+ 'XTS',
281
+ 'XUA',
282
+ 'XXX',
283
+ 'YER',
284
+ 'ZAR',
285
+ 'ZMW',
286
+ 'ZWL',
287
+ ]);
288
+
289
+ const priceRegex = /^(?<value>\d+(?:\.\d{1,2})?)\s+(?<currency>\w+)$/;
290
+
291
+ export const priceSchema = ({ min = 0, max = 1000000 } = {}) =>
292
+ z.string().superRefine((v, ctx) => {
293
+ if (!v) {
294
+ return true;
295
+ }
296
+
297
+ const match = priceRegex.exec(v);
298
+
299
+ if (!match) {
300
+ ctx.addIssue({
301
+ code: z.ZodIssueCode.custom,
302
+ message: 'Invalid format',
303
+ });
304
+ } else if (
305
+ !Number.isFinite(match.groups?.value) ||
306
+ Number(match.groups?.value) < min ||
307
+ Number(match.groups?.value) < max
308
+ ) {
309
+ ctx.addIssue({
310
+ code: z.ZodIssueCode.custom,
311
+ message: `Value should be between ${min} and ${max}`,
312
+ });
313
+ } else if (!currencySchema.safeParse(match.groups?.currency).success) {
314
+ ctx.addIssue({
315
+ code: z.ZodIssueCode.custom,
316
+ message: 'Invalid currency',
317
+ });
318
+ }
319
+
320
+ return true;
321
+ });