oneentry 1.0.155 → 1.0.157
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 +19 -0
- package/changelog.md +154 -1
- package/dist/attribute-sets/attributeSetsApi.js +4 -4
- package/dist/attribute-sets/attributeSetsInterfaces.d.ts +1 -1
- package/dist/auth-provider/authProviderSchemas.d.ts +2 -0
- package/dist/auth-provider/authProviderSchemas.js +2 -0
- package/dist/auth-provider/authProvidersInterfaces.d.ts +4 -0
- package/dist/base/asyncModules.d.ts +18 -2
- package/dist/base/asyncModules.js +32 -8
- package/dist/base/syncModules.d.ts +41 -144
- package/dist/base/syncModules.js +67 -359
- package/dist/base/timeIntervals.d.ts +95 -0
- package/dist/base/timeIntervals.js +321 -0
- package/dist/base/utils.d.ts +65 -7
- package/dist/base/validation.js +0 -1
- package/dist/forms/formsApi.js +2 -2
- package/dist/forms/formsInterfaces.d.ts +3 -3
- package/dist/forms-data/formsDataApi.js +2 -2
- package/dist/forms-data/formsDataInterfaces.d.ts +4 -4
- package/dist/index.d.ts +3 -2
- package/dist/index.js +5 -0
- package/dist/integration-collections/integrationCollectionsApi.js +6 -6
- package/dist/integration-collections/integrationCollectionsInterfaces.d.ts +4 -0
- package/dist/integration-collections/integrationCollectionsSchemas.d.ts +4 -0
- package/dist/integration-collections/integrationCollectionsSchemas.js +2 -0
- package/dist/menus/menusApi.js +1 -1
- package/dist/orders/ordersInterfaces.d.ts +10 -0
- package/dist/orders/ordersSchemas.d.ts +10 -0
- package/dist/orders/ordersSchemas.js +12 -0
- package/dist/pages/pagesApi.d.ts +4 -4
- package/dist/pages/pagesApi.js +10 -9
- package/dist/pages/pagesInterfaces.d.ts +14 -4
- package/dist/pages/pagesSchemas.d.ts +15 -0
- package/dist/pages/pagesSchemas.js +13 -1
- package/dist/products/productsApi.d.ts +4 -4
- package/dist/products/productsApi.js +12 -10
- package/dist/products/productsInterfaces.d.ts +16 -4
- package/dist/products/productsSchemas.d.ts +17 -0
- package/dist/products/productsSchemas.js +14 -1
- package/dist/subscriptions/subscriptionsApi.d.ts +4 -4
- package/dist/subscriptions/subscriptionsApi.js +3 -3
- package/dist/subscriptions/subscriptionsInterfaces.d.ts +26 -5
- package/dist/subscriptions/subscriptionsSchemas.d.ts +27 -1
- package/dist/subscriptions/subscriptionsSchemas.js +20 -2
- package/package.json +1 -1
package/dist/base/syncModules.js
CHANGED
|
@@ -91,14 +91,17 @@ class SyncModules {
|
|
|
91
91
|
/**
|
|
92
92
|
* Sorts attributes by their positions.
|
|
93
93
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* @
|
|
94
|
+
* Each attribute has a `position` field, and the API returns them in no
|
|
95
|
+
* particular order. The method rebuilds the collection sorted by ascending
|
|
96
|
+
* `position`, so the display order matches the order defined in the CMS.
|
|
97
|
+
* Both container shapes the API uses are handled: an object keyed by marker
|
|
98
|
+
* (`attributeValues`) and an array (form `attributes`).
|
|
99
|
+
* @param {any} data - The attributes collection to sort.
|
|
100
|
+
* @returns {any} Sorted attributes, in the same container shape.
|
|
100
101
|
*/
|
|
101
|
-
this._sortAttributes = (data) =>
|
|
102
|
+
this._sortAttributes = (data) => Array.isArray(data)
|
|
103
|
+
? [...data].sort((a, b) => a.position - b.position)
|
|
104
|
+
: Object.fromEntries(Object.entries(data).sort(([, a], [, b]) => a.position - b.position));
|
|
102
105
|
this.state = state;
|
|
103
106
|
this._url = state.url;
|
|
104
107
|
this._nodeDeviceId = _generateId();
|
|
@@ -240,299 +243,44 @@ class SyncModules {
|
|
|
240
243
|
return body;
|
|
241
244
|
}
|
|
242
245
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
else if (typeof data === 'object' && data) {
|
|
259
|
-
const normalizeData = {};
|
|
260
|
-
Object.keys(data).forEach((key) => {
|
|
261
|
-
if (Array.isArray(data[key])) {
|
|
262
|
-
normalizeData[key] = this._clearArray(data[key]);
|
|
263
|
-
}
|
|
264
|
-
else if (!data[key] || typeof data[key] !== 'object') {
|
|
265
|
-
normalizeData[key] = data[key];
|
|
266
|
-
}
|
|
267
|
-
else if (key === 'attributeValues') {
|
|
268
|
-
const attrs = data[key];
|
|
269
|
-
Object.keys(attrs).forEach((attr) => {
|
|
270
|
-
// If an image attribute has a single-element value array,
|
|
271
|
-
// unwrap it to a plain object for consumer convenience.
|
|
272
|
-
if (attrs[attr].type === 'image' &&
|
|
273
|
-
attrs[attr].value.length === 1) {
|
|
274
|
-
attrs[attr].value = attrs[attr].value[0];
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
normalizeData[key] = data[key];
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
normalizeData[key] = this._clearArray(data[key]);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
return normalizeData;
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
return data;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Adds a specified number of days to a date.
|
|
291
|
-
* @param {Date} date - The initial date.
|
|
292
|
-
* @param {number} days - The number of days to add.
|
|
293
|
-
* @returns {any} The new date with added days.
|
|
294
|
-
*/
|
|
295
|
-
_addDays(date, days) {
|
|
296
|
-
const result = new Date(date);
|
|
297
|
-
result.setUTCDate(result.getUTCDate() + days);
|
|
298
|
-
return result;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Common logic for processing schedule dates (weekly, monthly, or both).
|
|
302
|
-
*
|
|
303
|
-
* Abstracts date iteration for three scheduling modes:
|
|
304
|
-
*
|
|
305
|
-
* - **`inEveryWeek` only**: starting from the start date, generates dates
|
|
306
|
-
* with a 7-day step until the end of the current month.
|
|
307
|
-
*
|
|
308
|
-
* - **`inEveryMonth` only**: pins the day-of-month from the start date
|
|
309
|
-
* and repeats it for each of the next 12 months. If the month does not
|
|
310
|
-
* have that day (e.g. Feb 31), the iteration is skipped.
|
|
311
|
-
*
|
|
312
|
-
* - **`inEveryWeek` + `inEveryMonth`**: for each of the next 12 months finds
|
|
313
|
-
* the first occurrence of the target weekday (from the start date), then
|
|
314
|
-
* iterates all occurrences of that weekday in the month with a 7-day step.
|
|
315
|
-
*
|
|
316
|
-
* `processDate(currentDate)` is called for every resolved date.
|
|
317
|
-
* @param {Date} date - The date for which to process intervals.
|
|
318
|
-
* @param {object} config - Configuration for schedule repetition.
|
|
319
|
-
* @param {boolean} config.inEveryWeek - Whether to repeat weekly.
|
|
320
|
-
* @param {boolean} config.inEveryMonth - Whether to repeat monthly.
|
|
321
|
-
* @param {(currentDate: Date) => void} processDate - Callback function to process each date.
|
|
246
|
+
* Normalizes the value of a single attribute in place.
|
|
247
|
+
*
|
|
248
|
+
* Applies the three type-driven fixes the API response needs, so that an
|
|
249
|
+
* attribute of a given type always reaches the consumer in the same shape,
|
|
250
|
+
* no matter which collection it arrived in:
|
|
251
|
+
*
|
|
252
|
+
* 1. **Single-file values** (`image`, `file`) — the API always sends an array,
|
|
253
|
+
* even for one file: `[img]` → `img`. Multi-file values and `groupOfImages`
|
|
254
|
+
* (a collection by definition) stay an array.
|
|
255
|
+
* 2. **Empty values** — an attribute with no value comes back as an empty
|
|
256
|
+
* localization map `{}`; it is replaced with `null`, the same marker the
|
|
257
|
+
* numeric branch already produced.
|
|
258
|
+
* 3. **Numbers** (`integer`, `float`, `real`) — cast to a JS number; anything
|
|
259
|
+
* that is not a number (including an empty value) becomes `null`.
|
|
260
|
+
* @param {any} attr - The attribute object to normalize in place.
|
|
322
261
|
*/
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const endOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
|
|
329
|
-
while (currentDate <= endOfMonth) {
|
|
330
|
-
processDate(currentDate);
|
|
331
|
-
// Move to the next week
|
|
332
|
-
currentDate = this._addDays(currentDate, 7);
|
|
333
|
-
}
|
|
262
|
+
_normalizeAttrValue(attr) {
|
|
263
|
+
if ((attr.type === 'image' || attr.type === 'file') &&
|
|
264
|
+
Array.isArray(attr.value) &&
|
|
265
|
+
attr.value.length === 1) {
|
|
266
|
+
attr.value = attr.value[0];
|
|
334
267
|
}
|
|
335
|
-
//
|
|
336
|
-
if (
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const currentDate = new Date(startDate);
|
|
342
|
-
currentDate.setUTCMonth(currentDate.getUTCMonth() + i);
|
|
343
|
-
// Try setting the current date to the target day of the month
|
|
344
|
-
currentDate.setUTCDate(targetDayOfMonth);
|
|
345
|
-
// Check if we have exceeded the month
|
|
346
|
-
if (currentDate.getUTCMonth() !== (startDate.getUTCMonth() + i) % 12) {
|
|
347
|
-
continue; // Skip this month if exceeded
|
|
348
|
-
}
|
|
349
|
-
processDate(currentDate);
|
|
350
|
-
}
|
|
268
|
+
// An attribute with no value arrives as an empty localization map.
|
|
269
|
+
if (attr.value &&
|
|
270
|
+
typeof attr.value === 'object' &&
|
|
271
|
+
!Array.isArray(attr.value) &&
|
|
272
|
+
Object.keys(attr.value).length === 0) {
|
|
273
|
+
attr.value = null;
|
|
351
274
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
currentDate.setUTCMonth(currentDate.getUTCMonth() + i);
|
|
360
|
-
// Set to the first day of the month
|
|
361
|
-
currentDate.setUTCDate(1);
|
|
362
|
-
// Find the first target day of the week in the current month
|
|
363
|
-
const daysUntilTargetDay = (targetDayOfWeek - currentDate.getUTCDay() + 7) % 7;
|
|
364
|
-
currentDate.setUTCDate(currentDate.getUTCDate() + daysUntilTargetDay);
|
|
365
|
-
// Iterate over all target days of the week in the current month
|
|
366
|
-
while (currentDate.getUTCMonth() ===
|
|
367
|
-
(startDate.getUTCMonth() + i) % 12) {
|
|
368
|
-
processDate(currentDate);
|
|
369
|
-
// Move to the next week (same day of the week)
|
|
370
|
-
currentDate.setUTCDate(currentDate.getUTCDate() + 7);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
275
|
+
if (attr.type === 'integer' ||
|
|
276
|
+
attr.type === 'float' ||
|
|
277
|
+
attr.type === 'real') {
|
|
278
|
+
// Number(null) is 0 and Number('') is 0 — an empty value must stay empty.
|
|
279
|
+
const isEmpty = attr.value === null || attr.value === undefined || attr.value === '';
|
|
280
|
+
const numValue = isEmpty ? NaN : Number(attr.value);
|
|
281
|
+
attr.value = isNaN(numValue) ? null : numValue;
|
|
373
282
|
}
|
|
374
283
|
}
|
|
375
|
-
/**
|
|
376
|
-
* Generates intervals for a specific date based on a schedule.
|
|
377
|
-
*
|
|
378
|
-
* For each date resolved by `_processScheduleDates`, iterates over
|
|
379
|
-
* the `schedule.times` array of time ranges. Each range is a pair
|
|
380
|
-
* `[startTime, endTime]` with `{ hours, minutes }` fields.
|
|
381
|
-
* Creates an ISO interval `[start.toISOString(), end.toISOString()]`
|
|
382
|
-
* and adds it to `utcIntervals` (Set deduplicates automatically).
|
|
383
|
-
* @param {Date} date - The date for which to generate intervals.
|
|
384
|
-
* @param {object} schedule - The schedule defining the intervals.
|
|
385
|
-
* @param {boolean} schedule.inEveryWeek - The number of weeks between intervals.
|
|
386
|
-
* @param {any[]} schedule.times - The times for each interval.
|
|
387
|
-
* @param {boolean} schedule.inEveryMonth - The month intervals for each interval.
|
|
388
|
-
* @param {Set<Array<string>>} utcIntervals - A set to store unique intervals.
|
|
389
|
-
*/
|
|
390
|
-
_generateIntervalsForDate(date, schedule, utcIntervals) {
|
|
391
|
-
this._processScheduleDates(date, schedule, (currentDate) => {
|
|
392
|
-
schedule.times.forEach((timeRange) => {
|
|
393
|
-
const [startTime, endTime] = timeRange;
|
|
394
|
-
const intervalStart = new Date(currentDate);
|
|
395
|
-
intervalStart.setUTCHours(startTime.hours, startTime.minutes, 0, 0);
|
|
396
|
-
const intervalEnd = new Date(currentDate);
|
|
397
|
-
intervalEnd.setUTCHours(endTime.hours, endTime.minutes, 0, 0);
|
|
398
|
-
utcIntervals.add([
|
|
399
|
-
intervalStart.toISOString(),
|
|
400
|
-
intervalEnd.toISOString(),
|
|
401
|
-
]);
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Adds time intervals to schedules.
|
|
407
|
-
*
|
|
408
|
-
* Accepts an array of schedule groups (structure of `timeInterval` attributes
|
|
409
|
-
* for pages/products). For each group iterates over `values` — the set of
|
|
410
|
-
* concrete schedules. Each schedule contains a date range `dates[0..1]`.
|
|
411
|
-
*
|
|
412
|
-
* If both boundaries are equal (`isSameDay`), intervals are generated only
|
|
413
|
-
* for that single date. Otherwise — for every day in the range inclusive.
|
|
414
|
-
*
|
|
415
|
-
* The result (`schedule.timeIntervals`) is a sorted array of ISO pairs,
|
|
416
|
-
* ready to pass to UI components.
|
|
417
|
-
* @param {any[]} schedules - The schedules to process.
|
|
418
|
-
* @returns {any} Schedules with added time intervals.
|
|
419
|
-
*/
|
|
420
|
-
_addTimeIntervalsToSchedules(schedules) {
|
|
421
|
-
schedules === null || schedules === void 0 ? void 0 : schedules.forEach((scheduleGroup) => {
|
|
422
|
-
// Skip if scheduleGroup.values is not an array
|
|
423
|
-
if (!scheduleGroup ||
|
|
424
|
-
!scheduleGroup.values ||
|
|
425
|
-
!Array.isArray(scheduleGroup.values)) {
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
scheduleGroup.values.forEach((schedule) => {
|
|
429
|
-
const utcIntervals = new Set();
|
|
430
|
-
const startDate = new Date(schedule.dates[0]);
|
|
431
|
-
const endDate = new Date(schedule.dates[1]);
|
|
432
|
-
const isSameDay = startDate.toISOString() === endDate.toISOString();
|
|
433
|
-
if (isSameDay) {
|
|
434
|
-
this._generateIntervalsForDate(startDate, schedule, utcIntervals);
|
|
435
|
-
}
|
|
436
|
-
else {
|
|
437
|
-
for (let currentDate = new Date(startDate); currentDate <= endDate; currentDate = this._addDays(currentDate, 1)) {
|
|
438
|
-
this._generateIntervalsForDate(currentDate, schedule, utcIntervals);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
schedule.timeIntervals = Array.from(utcIntervals).sort();
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
return schedules;
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Generates intervals for a specific date for form schedules.
|
|
448
|
-
*
|
|
449
|
-
* Unlike `_generateIntervalsForDate`, time ranges here have a different shape:
|
|
450
|
-
* each `timeInterval` contains `start`, `end` and `period`
|
|
451
|
-
* (slot length in minutes). The method slices the [start, end) window into
|
|
452
|
-
* fixed-length slots of `period` minutes:
|
|
453
|
-
*
|
|
454
|
-
* start=09:00, end=12:00, period=30 → [09:00–09:30], [09:30–10:00], …, [11:30–12:00]
|
|
455
|
-
*
|
|
456
|
-
* Generation stops if the next slot would exceed `end`.
|
|
457
|
-
* Each slot is added to `utcIntervals` (Set deduplicates automatically).
|
|
458
|
-
* @param {Date} date - The date for which to generate intervals.
|
|
459
|
-
* @param {object} interval - The interval configuration.
|
|
460
|
-
* @param {boolean} interval.inEveryWeek - Indicates whether the schedule is weekly.
|
|
461
|
-
* @param {boolean} interval.inEveryMonth - Indicates whether the schedule is monthly.
|
|
462
|
-
* @param {any[]} timeIntervals - The time intervals to process.
|
|
463
|
-
* @param {Set<Array<string>>} utcIntervals - A set to store unique intervals.
|
|
464
|
-
*/
|
|
465
|
-
_generateIntervalsForFormDate(date, interval, timeIntervals, utcIntervals) {
|
|
466
|
-
const generateTimeSlotsForDate = (currentDate) => {
|
|
467
|
-
timeIntervals.forEach((timeInterval) => {
|
|
468
|
-
let currentStart = timeInterval.start;
|
|
469
|
-
const endTime = timeInterval.end;
|
|
470
|
-
// Slice the window into slots of `period` minutes each.
|
|
471
|
-
while (currentStart.hours < endTime.hours ||
|
|
472
|
-
(currentStart.hours === endTime.hours &&
|
|
473
|
-
currentStart.minutes < endTime.minutes)) {
|
|
474
|
-
const intervalStart = new Date(currentDate);
|
|
475
|
-
intervalStart.setUTCHours(currentStart.hours, currentStart.minutes, 0, 0);
|
|
476
|
-
// Compute slot end: add period minutes with hour carry normalization.
|
|
477
|
-
const nextMinutes = currentStart.minutes + timeInterval.period;
|
|
478
|
-
const nextHours = currentStart.hours + Math.floor(nextMinutes / 60);
|
|
479
|
-
const minutes = nextMinutes % 60;
|
|
480
|
-
// If the slot end exceeds `end` — stop; partial slots are not emitted.
|
|
481
|
-
if (nextHours > endTime.hours ||
|
|
482
|
-
(nextHours === endTime.hours && minutes > endTime.minutes)) {
|
|
483
|
-
break;
|
|
484
|
-
}
|
|
485
|
-
const intervalEnd = new Date(currentDate);
|
|
486
|
-
intervalEnd.setUTCHours(nextHours, minutes, 0, 0);
|
|
487
|
-
utcIntervals.add([
|
|
488
|
-
intervalStart.toISOString(),
|
|
489
|
-
intervalEnd.toISOString(),
|
|
490
|
-
]);
|
|
491
|
-
currentStart = { hours: nextHours, minutes };
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
};
|
|
495
|
-
this._processScheduleDates(date, interval, generateTimeSlotsForDate);
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Adds time intervals to form schedules (different structure).
|
|
499
|
-
*
|
|
500
|
-
* Same as `_addTimeIntervalsToSchedules` but for `timeInterval` attributes
|
|
501
|
-
* in **forms** (different API data structure):
|
|
502
|
-
* - `interval.range[0..1]` instead of `schedule.dates[0..1]`
|
|
503
|
-
* - `interval.intervals` — array of time ranges with slots (`period`)
|
|
504
|
-
* instead of `[startTime, endTime]` pairs
|
|
505
|
-
*
|
|
506
|
-
* Result is written to `interval.timeIntervals`.
|
|
507
|
-
* @param {any[]} intervals - The intervals to process.
|
|
508
|
-
* @returns {any} Intervals with added time intervals.
|
|
509
|
-
*/
|
|
510
|
-
_addTimeIntervalsToFormSchedules(intervals) {
|
|
511
|
-
intervals.forEach((interval) => {
|
|
512
|
-
var _a, _b;
|
|
513
|
-
if (!interval.intervals || !Array.isArray(interval.intervals)) {
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
const utcIntervals = new Set();
|
|
517
|
-
const startDate = new Date(interval.range[0]);
|
|
518
|
-
const endDate = new Date(interval.range[1]);
|
|
519
|
-
const isSameDay = startDate.toISOString() === endDate.toISOString();
|
|
520
|
-
const intervalConfig = {
|
|
521
|
-
inEveryWeek: (_a = interval.inEveryWeek) !== null && _a !== void 0 ? _a : false,
|
|
522
|
-
inEveryMonth: (_b = interval.inEveryMonth) !== null && _b !== void 0 ? _b : false,
|
|
523
|
-
};
|
|
524
|
-
if (isSameDay) {
|
|
525
|
-
this._generateIntervalsForFormDate(startDate, intervalConfig, interval.intervals, utcIntervals);
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
for (let currentDate = new Date(startDate); currentDate <= endDate; currentDate = this._addDays(currentDate, 1)) {
|
|
529
|
-
this._generateIntervalsForFormDate(currentDate, intervalConfig, interval.intervals, utcIntervals);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
interval.timeIntervals = Array.from(utcIntervals).sort();
|
|
533
|
-
});
|
|
534
|
-
return intervals;
|
|
535
|
-
}
|
|
536
284
|
/**
|
|
537
285
|
* Transforms additionalFields from array to object keyed by marker.
|
|
538
286
|
*
|
|
@@ -553,19 +301,28 @@ class SyncModules {
|
|
|
553
301
|
*
|
|
554
302
|
* Handles three different attribute formats returned by the API:
|
|
555
303
|
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
* - `_normalizeAdditionalFields`
|
|
559
|
-
* -
|
|
560
|
-
*
|
|
561
|
-
* - the
|
|
304
|
+
* All three go through the same steps, so an attribute of a given type looks
|
|
305
|
+
* the same whichever collection it arrived in:
|
|
306
|
+
* - `_normalizeAdditionalFields` turns nested fields into a marker map;
|
|
307
|
+
* - `_normalizeAttrValue` unwraps single-file values, empties to `null` and
|
|
308
|
+
* casts numbers;
|
|
309
|
+
* - the collection is re-sorted by `position`.
|
|
310
|
+
*
|
|
311
|
+
* **1. `attributeValues`** — attributes of pages, products and other entities,
|
|
312
|
+
* an object `{ marker: AttrObject }`.
|
|
313
|
+
*
|
|
314
|
+
* **2. `attributes`** — form attributes, an array (or a marker map on some
|
|
315
|
+
* endpoints); form-only boolean flags (`isLogin`, `isSignUp`, notifications)
|
|
316
|
+
* are additionally coerced from `null` to `false`.
|
|
562
317
|
*
|
|
563
|
-
* **
|
|
564
|
-
*
|
|
565
|
-
*
|
|
318
|
+
* **3. `type`** — a standalone attribute: an attribute-set entry, a form-data
|
|
319
|
+
* field or a nested `additionalFields` entry. Same transformations, but there
|
|
320
|
+
* is no collection to sort.
|
|
566
321
|
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
322
|
+
* `timeInterval` attributes are left exactly as the API returned them — a
|
|
323
|
+
* compact recurrence rule. Resolving one into concrete slots is the caller's
|
|
324
|
+
* job, via `expandTimeIntervals`: the rule is open-ended, so only the caller
|
|
325
|
+
* knows how wide a window it needs.
|
|
569
326
|
*
|
|
570
327
|
* If none of the keys are found — data is returned unchanged.
|
|
571
328
|
* @param {any} data - The data to normalize.
|
|
@@ -577,20 +334,7 @@ class SyncModules {
|
|
|
577
334
|
Object.keys(data.attributeValues).forEach((attr) => {
|
|
578
335
|
const d = data.attributeValues[attr];
|
|
579
336
|
this._normalizeAdditionalFields(d);
|
|
580
|
-
|
|
581
|
-
if (d.type === 'integer' || d.type === 'float') {
|
|
582
|
-
const numValue = Number(d.value);
|
|
583
|
-
d.value = isNaN(numValue) ? null : numValue;
|
|
584
|
-
}
|
|
585
|
-
// add timeIntervals
|
|
586
|
-
if (data.attributeValues[attr].type === 'timeInterval') {
|
|
587
|
-
const schedules = data.attributeValues[attr].value;
|
|
588
|
-
// console.log('Schedules: ', JSON.stringify(schedules));
|
|
589
|
-
if (Array.isArray(schedules) && schedules.length > 0) {
|
|
590
|
-
const result = this._addTimeIntervalsToSchedules(schedules);
|
|
591
|
-
data.attributeValues[attr].value = result;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
337
|
+
this._normalizeAttrValue(d);
|
|
594
338
|
});
|
|
595
339
|
return {
|
|
596
340
|
...data,
|
|
@@ -608,64 +352,28 @@ class SyncModules {
|
|
|
608
352
|
if ('attributes' in data) {
|
|
609
353
|
const d = data.attributes;
|
|
610
354
|
Object.keys(d).forEach((attr) => {
|
|
611
|
-
var _a;
|
|
612
355
|
this._normalizeAdditionalFields(d[attr]);
|
|
356
|
+
this._normalizeAttrValue(d[attr]);
|
|
613
357
|
for (const field of booleanFields) {
|
|
614
358
|
if (field in d[attr] && d[attr][field] === null) {
|
|
615
359
|
d[attr][field] = false;
|
|
616
360
|
}
|
|
617
361
|
}
|
|
618
|
-
// Add time intervals
|
|
619
|
-
if (d[attr].type === 'timeInterval') {
|
|
620
|
-
const intervals = (_a = d[attr].localizeInfos) === null || _a === void 0 ? void 0 : _a.intervals;
|
|
621
|
-
// console.log('Schedules:: ', JSON.stringify(intervals));
|
|
622
|
-
if (intervals && Array.isArray(intervals) && intervals.length > 0) {
|
|
623
|
-
const result = this._addTimeIntervalsToFormSchedules(intervals);
|
|
624
|
-
d[attr].localizeInfos.intervals = result;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
362
|
});
|
|
628
|
-
return data;
|
|
363
|
+
return { ...data, attributes: this._sortAttributes(d) };
|
|
629
364
|
}
|
|
630
365
|
// For single attribute - for attribute sets
|
|
631
366
|
if ('type' in data) {
|
|
632
367
|
this._normalizeAdditionalFields(data);
|
|
368
|
+
this._normalizeAttrValue(data);
|
|
633
369
|
for (const field of booleanFields) {
|
|
634
370
|
if (field in data && data[field] === null) {
|
|
635
371
|
data[field] = false;
|
|
636
372
|
}
|
|
637
373
|
}
|
|
638
|
-
// Normalize numbers
|
|
639
|
-
if (data.type === 'integer' || data.type === 'float') {
|
|
640
|
-
const numValue = Number(data.value);
|
|
641
|
-
data.value = isNaN(numValue) ? null : numValue;
|
|
642
|
-
}
|
|
643
|
-
// Add time intervals
|
|
644
|
-
if (data.type === 'timeInterval') {
|
|
645
|
-
const schedules = data.value;
|
|
646
|
-
if (Array.isArray(schedules) && schedules.length > 0) {
|
|
647
|
-
const result = this._addTimeIntervalsToSchedules(schedules);
|
|
648
|
-
data.value = result;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
374
|
}
|
|
652
375
|
return data;
|
|
653
376
|
}
|
|
654
|
-
/**
|
|
655
|
-
* Processes data after fetching or receiving it.
|
|
656
|
-
*
|
|
657
|
-
* Final post-processing of the API response: first unwraps localized fields
|
|
658
|
-
* (`_normalizeData`), then fixes single-element image attributes
|
|
659
|
-
* (`_clearArray`). Called at the end of every fetch method.
|
|
660
|
-
* @param {any} data - The data to process.
|
|
661
|
-
* @param {any} [langCode] - The language code for processing.
|
|
662
|
-
* @returns {any} Processed data.
|
|
663
|
-
*/
|
|
664
|
-
_dataPostProcess(data, langCode = this.state.lang) {
|
|
665
|
-
const normalize = this._normalizeData(data, langCode);
|
|
666
|
-
const result = this._clearArray(normalize);
|
|
667
|
-
return result;
|
|
668
|
-
}
|
|
669
377
|
/**
|
|
670
378
|
* Sets the access token in the state.
|
|
671
379
|
* @param {string} accessToken - The access token to set.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { IAttributeValue, ITimeIntervalAttributeValue, ITimeIntervalEntitySchedule, ITimeIntervalSchedule, ITimeIntervalWindow, TimeIntervalPair } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* Expands a `timeInterval` schedule into concrete UTC slots for a given window.
|
|
4
|
+
*
|
|
5
|
+
* A schedule as returned by the API is a compact **recurrence rule** — an
|
|
6
|
+
* anchor date plus daily time ranges plus repeat flags — not a list of slots.
|
|
7
|
+
* Materializing it wholesale is what makes `timeInterval` attributes expensive
|
|
8
|
+
* (a year of half-hour slots runs to megabytes), so expansion is on demand and
|
|
9
|
+
* the window is required: only the caller knows how far it needs to resolve.
|
|
10
|
+
*
|
|
11
|
+
* Both schedule shapes the API returns are accepted:
|
|
12
|
+
* - **entity** — `attributeValues[marker].value[].values[]` on pages, products,
|
|
13
|
+
* blocks and attribute sets: a `dates` range with `times` pairs;
|
|
14
|
+
* - **form** — `attributes[marker].localizeInfos.intervals[]`: a `range` with
|
|
15
|
+
* `intervals` that carry a slot `period` in minutes.
|
|
16
|
+
*
|
|
17
|
+
* Semantics:
|
|
18
|
+
* - `dates[0]` / `range[0]` is both the recurrence phase and the first valid
|
|
19
|
+
* day — nothing earlier is emitted, however wide the window;
|
|
20
|
+
* - `dates[1]` / `range[1]` ends validity; when it does not extend past the
|
|
21
|
+
* start, the schedule is anchored to that day — with a recurrence flag set,
|
|
22
|
+
* recurrence is then open-ended and the window alone bounds the result;
|
|
23
|
+
* - `inEveryWeek` repeats every 7 days from the anchor; `inEveryMonth` repeats
|
|
24
|
+
* on the same day-of-month, skipping months that are too short; with both set
|
|
25
|
+
* the weekly rule applies, which is what it has always meant in practice;
|
|
26
|
+
* - with neither flag the schedule is a plain date range — every day of it;
|
|
27
|
+
* - the result is deduplicated and sorted by start, then end.
|
|
28
|
+
* @param {ITimeIntervalEntitySchedule | ITimeIntervalSchedule} schedule - A single schedule entry from a `timeInterval` attribute.
|
|
29
|
+
* @param {ITimeIntervalWindow} window - Inclusive `{ from, to }` range to resolve, compared at UTC day granularity.
|
|
30
|
+
* @returns {TimeIntervalPair[]} Sorted, deduplicated `[start, end]` ISO pairs; empty when the schedule is malformed or does not overlap the window.
|
|
31
|
+
* To expand a whole attribute at once, prefer {@link expandAttributeTimeIntervals}
|
|
32
|
+
* — it walks the groups and merges the results for you. Reach for this function
|
|
33
|
+
* directly when you already hold a single schedule, e.g. a form's
|
|
34
|
+
* `localizeInfos.intervals[]`.
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { expandTimeIntervals } from 'oneentry';
|
|
38
|
+
*
|
|
39
|
+
* // Form attributes are an array keyed by `marker`, and carry their schedules
|
|
40
|
+
* // already typed on `localizeInfos.intervals`.
|
|
41
|
+
* const field = form.attributes.find((a) => a.marker === 'booking');
|
|
42
|
+
*
|
|
43
|
+
* const slots = (field?.localizeInfos.intervals ?? []).flatMap((schedule) =>
|
|
44
|
+
* expandTimeIntervals(schedule, { from: '2025-05-01', to: '2025-05-31' }),
|
|
45
|
+
* );
|
|
46
|
+
* // [['2025-05-07T09:00:00.000Z', '2025-05-07T10:00:00.000Z'], …]
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function expandTimeIntervals(schedule: ITimeIntervalEntitySchedule | ITimeIntervalSchedule, window: ITimeIntervalWindow): TimeIntervalPair[];
|
|
50
|
+
/**
|
|
51
|
+
* Narrows an attribute value to a `timeInterval` attribute.
|
|
52
|
+
*
|
|
53
|
+
* `IAttributeValue.value` is `unknown` — its shape depends on `type` — so this
|
|
54
|
+
* guard is what lets you reach the schedules without a cast.
|
|
55
|
+
* @param {IAttributeValue | undefined} attr - The attribute value to test.
|
|
56
|
+
* @returns {boolean} True when the attribute is a `timeInterval` carrying an array of groups.
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* const attr = page.attributeValues.interval;
|
|
60
|
+
* if (isTimeIntervalAttribute(attr)) {
|
|
61
|
+
* attr.value[0].values[0].dates; // fully typed, no cast
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function isTimeIntervalAttribute(attr: IAttributeValue | undefined): attr is ITimeIntervalAttributeValue;
|
|
66
|
+
/**
|
|
67
|
+
* Expands a whole `timeInterval` attribute into concrete UTC slots for a window.
|
|
68
|
+
*
|
|
69
|
+
* The one-call path for the common case: it walks the attribute's groups and
|
|
70
|
+
* their schedules, expands each with {@link expandTimeIntervals}, and merges the
|
|
71
|
+
* results. Merging matters — deduplication and ordering only hold within a
|
|
72
|
+
* single schedule, so combining groups by hand can yield duplicate or unsorted
|
|
73
|
+
* slots.
|
|
74
|
+
*
|
|
75
|
+
* Anything that is not a `timeInterval` attribute yields an empty array, so this
|
|
76
|
+
* is safe to call on an arbitrary attribute without checking `type` first.
|
|
77
|
+
*
|
|
78
|
+
* For **form** attributes the schedules are already typed at
|
|
79
|
+
* `localizeInfos.intervals`, so no equivalent helper is needed — map over them
|
|
80
|
+
* and call {@link expandTimeIntervals} directly.
|
|
81
|
+
* @param {IAttributeValue | undefined} attr - A `timeInterval` attribute value, e.g. `page.attributeValues.interval`.
|
|
82
|
+
* @param {ITimeIntervalWindow} window - Inclusive `{ from, to }` range to resolve, compared at UTC day granularity.
|
|
83
|
+
* @returns {TimeIntervalPair[]} Sorted, deduplicated `[start, end]` ISO pairs across every group; empty when the attribute is not a `timeInterval`.
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { expandAttributeTimeIntervals } from 'oneentry';
|
|
87
|
+
*
|
|
88
|
+
* const slots = expandAttributeTimeIntervals(page.attributeValues.interval, {
|
|
89
|
+
* from: '2025-04-01',
|
|
90
|
+
* to: '2025-04-30',
|
|
91
|
+
* });
|
|
92
|
+
* // [['2025-04-14T09:00:00.000Z', '2025-04-14T10:00:00.000Z'], …]
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function expandAttributeTimeIntervals(attr: IAttributeValue | undefined, window: ITimeIntervalWindow): TimeIntervalPair[];
|