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.
Files changed (45) hide show
  1. package/README.md +19 -0
  2. package/changelog.md +154 -1
  3. package/dist/attribute-sets/attributeSetsApi.js +4 -4
  4. package/dist/attribute-sets/attributeSetsInterfaces.d.ts +1 -1
  5. package/dist/auth-provider/authProviderSchemas.d.ts +2 -0
  6. package/dist/auth-provider/authProviderSchemas.js +2 -0
  7. package/dist/auth-provider/authProvidersInterfaces.d.ts +4 -0
  8. package/dist/base/asyncModules.d.ts +18 -2
  9. package/dist/base/asyncModules.js +32 -8
  10. package/dist/base/syncModules.d.ts +41 -144
  11. package/dist/base/syncModules.js +67 -359
  12. package/dist/base/timeIntervals.d.ts +95 -0
  13. package/dist/base/timeIntervals.js +321 -0
  14. package/dist/base/utils.d.ts +65 -7
  15. package/dist/base/validation.js +0 -1
  16. package/dist/forms/formsApi.js +2 -2
  17. package/dist/forms/formsInterfaces.d.ts +3 -3
  18. package/dist/forms-data/formsDataApi.js +2 -2
  19. package/dist/forms-data/formsDataInterfaces.d.ts +4 -4
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.js +5 -0
  22. package/dist/integration-collections/integrationCollectionsApi.js +6 -6
  23. package/dist/integration-collections/integrationCollectionsInterfaces.d.ts +4 -0
  24. package/dist/integration-collections/integrationCollectionsSchemas.d.ts +4 -0
  25. package/dist/integration-collections/integrationCollectionsSchemas.js +2 -0
  26. package/dist/menus/menusApi.js +1 -1
  27. package/dist/orders/ordersInterfaces.d.ts +10 -0
  28. package/dist/orders/ordersSchemas.d.ts +10 -0
  29. package/dist/orders/ordersSchemas.js +12 -0
  30. package/dist/pages/pagesApi.d.ts +4 -4
  31. package/dist/pages/pagesApi.js +10 -9
  32. package/dist/pages/pagesInterfaces.d.ts +14 -4
  33. package/dist/pages/pagesSchemas.d.ts +15 -0
  34. package/dist/pages/pagesSchemas.js +13 -1
  35. package/dist/products/productsApi.d.ts +4 -4
  36. package/dist/products/productsApi.js +12 -10
  37. package/dist/products/productsInterfaces.d.ts +16 -4
  38. package/dist/products/productsSchemas.d.ts +17 -0
  39. package/dist/products/productsSchemas.js +14 -1
  40. package/dist/subscriptions/subscriptionsApi.d.ts +4 -4
  41. package/dist/subscriptions/subscriptionsApi.js +3 -3
  42. package/dist/subscriptions/subscriptionsInterfaces.d.ts +26 -5
  43. package/dist/subscriptions/subscriptionsSchemas.d.ts +27 -1
  44. package/dist/subscriptions/subscriptionsSchemas.js +20 -2
  45. package/package.json +1 -1
@@ -91,14 +91,17 @@ class SyncModules {
91
91
  /**
92
92
  * Sorts attributes by their positions.
93
93
  *
94
- * The API returns attributes as an object `{ marker: AttrObject }`.
95
- * Each attribute has a `position` field. The method rebuilds the object
96
- * with keys sorted by ascending `position` so that the display order
97
- * matches the order defined in the CMS.
98
- * @param {any} data - The data containing attributes.
99
- * @returns {any} Sorted attributes.
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) => Object.fromEntries(Object.entries(data).sort(([, a], [, b]) => a.position - b.position));
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
- * Clears arrays within the data structure.
244
- *
245
- * Traverses the data and fixes a specific edge case with image attributes:
246
- * when an `image` attribute has a single-element array in `value`,
247
- * the API returns an array but consumers expect a plain object.
248
- * In that case `value` is unwrapped: `[img]` → `img`.
249
- *
250
- * For all other keys the method recursively copies the structure unchanged.
251
- * @param {Record<string, any>} data - The data to clear.
252
- * @returns {any} Cleared data.
253
- */
254
- _clearArray(data) {
255
- if (Array.isArray(data)) {
256
- return data.map((item) => this._clearArray(item));
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
- _processScheduleDates(date, config, processDate) {
324
- // Handle weekly schedules
325
- if (config.inEveryWeek && !config.inEveryMonth) {
326
- let currentDate = new Date(date);
327
- // Calculate the last day of the current month
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
- // Handle monthly schedules
336
- if (config.inEveryMonth && !config.inEveryWeek) {
337
- const startDate = new Date(date);
338
- const targetDayOfMonth = startDate.getUTCDate();
339
- const numberOfMonths = 12;
340
- for (let i = 0; i < numberOfMonths; i++) {
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
- // Handle both weekly and monthly schedules
353
- if (config.inEveryMonth && config.inEveryWeek) {
354
- const startDate = new Date(date);
355
- const targetDayOfWeek = startDate.getUTCDay();
356
- const numberOfMonths = 12;
357
- for (let i = 0; i < numberOfMonths; i++) {
358
- const currentDate = new Date(startDate);
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
- * **1. `attributeValues`** attributes of pages, products and other entities.
557
- * Contains an object `{ marker: AttrObject }`. For each attribute:
558
- * - `_normalizeAdditionalFields` is called;
559
- * - numeric types (`integer`, `float`) are cast to a JS number (or `null`);
560
- * - `timeInterval` attributes are enriched with computed `timeIntervals`;
561
- * - the whole object is re-sorted by `position`.
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
- * **2. `attributes`** — form attributes (different API structure).
564
- * Same `additionalFields` and `timeInterval` processing,
565
- * but numbers are not normalized here (commented out — logic differs).
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
- * **3. `type`** a single attribute from an attribute set.
568
- * Same transformations as in case 1, but without sorting.
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
- // normalize numbers
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[];