oneentry 1.0.154 → 1.0.156

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.
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expandTimeIntervals = expandTimeIntervals;
4
+ exports.isTimeIntervalAttribute = isTimeIntervalAttribute;
5
+ exports.expandAttributeTimeIntervals = expandAttributeTimeIntervals;
6
+ const _MS_PER_MINUTE = 60000;
7
+ const _MS_PER_DAY = 86400000;
8
+ const _MS_PER_WEEK = 7 * _MS_PER_DAY;
9
+ /**
10
+ * Converts a date-ish value to the UTC midnight of its day, as epoch milliseconds.
11
+ *
12
+ * Everything downstream works on whole UTC days, so this is the single place
13
+ * where an incoming `Date` / ISO string / timestamp is reduced to a day key.
14
+ *
15
+ * `null` is rejected explicitly: `new Date(null)` is the epoch, not an invalid
16
+ * date, so a NaN check alone would silently accept it as 1970-01-01.
17
+ * @param {Date | string | number} value - The value to interpret as a date.
18
+ * @returns {number | null} Epoch ms of the UTC day start, or null when the value is not a valid date.
19
+ */
20
+ function _toUtcDayStart(value) {
21
+ if (value === null || value === undefined)
22
+ return null;
23
+ const date = value instanceof Date ? value : new Date(value);
24
+ if (Number.isNaN(date.getTime()))
25
+ return null;
26
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
27
+ }
28
+ /**
29
+ * Converts a time-of-day point to minutes since midnight.
30
+ *
31
+ * Guards the numeric contract: a missing component, or one arriving as a string
32
+ * (`{ hours: '9' }`), would otherwise poison the arithmetic downstream — either
33
+ * throwing `RangeError` on `toISOString` or, for strings, concatenating instead
34
+ * of adding.
35
+ * @param {ITimeIntervalPoint | undefined} point - The point to convert.
36
+ * @returns {number | null} Minutes since midnight, or null when the point is malformed.
37
+ */
38
+ function _toMinutes(point) {
39
+ if (!point)
40
+ return null;
41
+ if (!Number.isFinite(point.hours) || !Number.isFinite(point.minutes)) {
42
+ return null;
43
+ }
44
+ return point.hours * 60 + point.minutes;
45
+ }
46
+ /**
47
+ * Resolves the days a schedule is active on, clipped to the requested window.
48
+ *
49
+ * `anchor` is both the recurrence phase (which weekday / which day-of-month)
50
+ * and the first day the schedule is valid — nothing before it is ever emitted.
51
+ * The recurrence rule is open-ended, so `rangeEnd` (the window, intersected
52
+ * with the schedule's own validity end) is what stops the enumeration.
53
+ * @param {number} anchor - UTC day start the schedule is anchored to.
54
+ * @param {number} rangeStart - UTC day start of the first day to consider (inclusive).
55
+ * @param {number} rangeEnd - UTC day start of the last day to consider (inclusive).
56
+ * @param {boolean} inEveryWeek - Whether the schedule repeats weekly.
57
+ * @param {boolean} inEveryMonth - Whether the schedule repeats monthly.
58
+ * @returns {number[]} Ascending UTC day starts the schedule is active on.
59
+ */
60
+ function _resolveActiveDays(anchor, rangeStart, rangeEnd, inEveryWeek, inEveryMonth) {
61
+ const days = [];
62
+ // Weekly wins over monthly: repeating "every week" already visits every
63
+ // occurrence of the anchor's weekday in every month, which is exactly what
64
+ // the two flags combined have always meant.
65
+ if (inEveryWeek) {
66
+ const offset = Math.ceil((rangeStart - anchor) / _MS_PER_WEEK);
67
+ for (let day = anchor + Math.max(0, offset) * _MS_PER_WEEK; day <= rangeEnd; day += _MS_PER_WEEK) {
68
+ days.push(day);
69
+ }
70
+ return days;
71
+ }
72
+ // Monthly: the same day-of-month, every month. Months too short for that day
73
+ // (e.g. the 31st in February) are skipped rather than rolled over.
74
+ if (inEveryMonth) {
75
+ const dayOfMonth = new Date(anchor).getUTCDate();
76
+ const start = new Date(rangeStart);
77
+ let year = start.getUTCFullYear();
78
+ let month = start.getUTCMonth();
79
+ // Walk the calendar month by month. The cursor is the first of the month,
80
+ // not the emitted day: a month too short for `dayOfMonth` emits nothing and
81
+ // would otherwise leave the cursor standing still.
82
+ while (Date.UTC(year, month, 1) <= rangeEnd) {
83
+ const daysInMonth = new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
84
+ if (dayOfMonth <= daysInMonth) {
85
+ const day = Date.UTC(year, month, dayOfMonth);
86
+ if (day >= rangeStart && day <= rangeEnd)
87
+ days.push(day);
88
+ }
89
+ month += 1;
90
+ if (month > 11) {
91
+ month = 0;
92
+ year += 1;
93
+ }
94
+ }
95
+ return days;
96
+ }
97
+ // No recurrence: a plain date range — every day of it.
98
+ for (let day = rangeStart; day <= rangeEnd; day += _MS_PER_DAY) {
99
+ days.push(day);
100
+ }
101
+ return days;
102
+ }
103
+ /**
104
+ * Builds slots for one day from explicit `[start, end]` time pairs.
105
+ *
106
+ * Used by entity schedules, where each pair is emitted verbatim as one slot.
107
+ * @param {number} day - UTC day start, in epoch ms.
108
+ * @param {ITimeIntervalPoint[][]} times - Daily time ranges as `[start, end]` pairs.
109
+ * @param {TimeIntervalPair[]} out - Accumulator the produced slots are pushed to.
110
+ */
111
+ function _addSlotsFromTimes(day, times, out) {
112
+ times.forEach((range) => {
113
+ const [start, end] = range !== null && range !== void 0 ? range : [];
114
+ const startMinutes = _toMinutes(start);
115
+ const endMinutes = _toMinutes(end);
116
+ if (startMinutes === null || endMinutes === null)
117
+ return;
118
+ const from = day + startMinutes * _MS_PER_MINUTE;
119
+ const to = day + endMinutes * _MS_PER_MINUTE;
120
+ out.push([new Date(from).toISOString(), new Date(to).toISOString()]);
121
+ });
122
+ }
123
+ /**
124
+ * Builds slots for one day by slicing each range into fixed-length periods.
125
+ *
126
+ * Used by form schedules: `start=09:00, end=12:00, period=30` yields
127
+ * `[09:00–09:30], [09:30–10:00], …, [11:30–12:00]`. A trailing partial slot
128
+ * that would overrun `end` is not emitted.
129
+ * @param {number} day - UTC day start, in epoch ms.
130
+ * @param {ITimeIntervalRange[]} ranges - Daily ranges carrying a slot `period` in minutes.
131
+ * @param {TimeIntervalPair[]} out - Accumulator the produced slots are pushed to.
132
+ */
133
+ function _addSlotsFromRanges(day, ranges, out) {
134
+ ranges.forEach((range) => {
135
+ const startMinutes = _toMinutes(range === null || range === void 0 ? void 0 : range.start);
136
+ const endMinutes = _toMinutes(range === null || range === void 0 ? void 0 : range.end);
137
+ if (startMinutes === null || endMinutes === null)
138
+ return;
139
+ // A non-positive period would never advance the cursor; a non-numeric one
140
+ // would concatenate into it.
141
+ if (!Number.isFinite(range.period) || range.period <= 0)
142
+ return;
143
+ for (let minutes = startMinutes; minutes + range.period <= endMinutes; minutes += range.period) {
144
+ const from = day + minutes * _MS_PER_MINUTE;
145
+ const to = day + (minutes + range.period) * _MS_PER_MINUTE;
146
+ out.push([new Date(from).toISOString(), new Date(to).toISOString()]);
147
+ }
148
+ });
149
+ }
150
+ /**
151
+ * Deduplicates slots by value and sorts them by start, then end.
152
+ *
153
+ * A plain `Set` would not do: slots are arrays, so it would compare by
154
+ * reference and keep every duplicate.
155
+ * @param {TimeIntervalPair[]} slots - The slots to normalize.
156
+ * @returns {TimeIntervalPair[]} Sorted, deduplicated slots.
157
+ */
158
+ function _dedupeAndSort(slots) {
159
+ const unique = new Map();
160
+ slots.forEach((slot) => {
161
+ const key = `${slot[0]}|${slot[1]}`;
162
+ if (!unique.has(key))
163
+ unique.set(key, slot);
164
+ });
165
+ return [...unique.values()].sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1]));
166
+ }
167
+ /**
168
+ * Narrows a schedule to the entity shape (pages, products, blocks, attribute sets).
169
+ *
170
+ * Entity schedules carry `times`; form schedules never do.
171
+ * @param {ITimeIntervalEntitySchedule | ITimeIntervalSchedule} schedule - The schedule to test.
172
+ * @returns {boolean} True when the schedule is an entity schedule.
173
+ */
174
+ function _isEntitySchedule(schedule) {
175
+ return Array.isArray(schedule.times);
176
+ }
177
+ /**
178
+ * Expands a `timeInterval` schedule into concrete UTC slots for a given window.
179
+ *
180
+ * A schedule as returned by the API is a compact **recurrence rule** — an
181
+ * anchor date plus daily time ranges plus repeat flags — not a list of slots.
182
+ * Materializing it wholesale is what makes `timeInterval` attributes expensive
183
+ * (a year of half-hour slots runs to megabytes), so expansion is on demand and
184
+ * the window is required: only the caller knows how far it needs to resolve.
185
+ *
186
+ * Both schedule shapes the API returns are accepted:
187
+ * - **entity** — `attributeValues[marker].value[].values[]` on pages, products,
188
+ * blocks and attribute sets: a `dates` range with `times` pairs;
189
+ * - **form** — `attributes[marker].localizeInfos.intervals[]`: a `range` with
190
+ * `intervals` that carry a slot `period` in minutes.
191
+ *
192
+ * Semantics:
193
+ * - `dates[0]` / `range[0]` is both the recurrence phase and the first valid
194
+ * day — nothing earlier is emitted, however wide the window;
195
+ * - `dates[1]` / `range[1]` ends validity; when it does not extend past the
196
+ * start, the schedule is anchored to that day — with a recurrence flag set,
197
+ * recurrence is then open-ended and the window alone bounds the result;
198
+ * - `inEveryWeek` repeats every 7 days from the anchor; `inEveryMonth` repeats
199
+ * on the same day-of-month, skipping months that are too short; with both set
200
+ * the weekly rule applies, which is what it has always meant in practice;
201
+ * - with neither flag the schedule is a plain date range — every day of it;
202
+ * - the result is deduplicated and sorted by start, then end.
203
+ * @param {ITimeIntervalEntitySchedule | ITimeIntervalSchedule} schedule - A single schedule entry from a `timeInterval` attribute.
204
+ * @param {ITimeIntervalWindow} window - Inclusive `{ from, to }` range to resolve, compared at UTC day granularity.
205
+ * @returns {TimeIntervalPair[]} Sorted, deduplicated `[start, end]` ISO pairs; empty when the schedule is malformed or does not overlap the window.
206
+ * To expand a whole attribute at once, prefer {@link expandAttributeTimeIntervals}
207
+ * — it walks the groups and merges the results for you. Reach for this function
208
+ * directly when you already hold a single schedule, e.g. a form's
209
+ * `localizeInfos.intervals[]`.
210
+ * @example
211
+ * ```ts
212
+ * import { expandTimeIntervals } from 'oneentry';
213
+ *
214
+ * // Form attributes are an array keyed by `marker`, and carry their schedules
215
+ * // already typed on `localizeInfos.intervals`.
216
+ * const field = form.attributes.find((a) => a.marker === 'booking');
217
+ *
218
+ * const slots = (field?.localizeInfos.intervals ?? []).flatMap((schedule) =>
219
+ * expandTimeIntervals(schedule, { from: '2025-05-01', to: '2025-05-31' }),
220
+ * );
221
+ * // [['2025-05-07T09:00:00.000Z', '2025-05-07T10:00:00.000Z'], …]
222
+ * ```
223
+ */
224
+ function expandTimeIntervals(schedule, window) {
225
+ var _a, _b;
226
+ if (!schedule || !window)
227
+ return [];
228
+ const isEntity = _isEntitySchedule(schedule);
229
+ const bounds = isEntity ? schedule.dates : schedule.range;
230
+ if (!Array.isArray(bounds))
231
+ return [];
232
+ const anchor = _toUtcDayStart(bounds[0]);
233
+ if (anchor === null)
234
+ return [];
235
+ const windowFrom = _toUtcDayStart(window.from);
236
+ const windowTo = _toUtcDayStart(window.to);
237
+ if (windowFrom === null || windowTo === null || windowTo < windowFrom) {
238
+ return [];
239
+ }
240
+ const inEveryWeek = (_a = schedule.inEveryWeek) !== null && _a !== void 0 ? _a : false;
241
+ const inEveryMonth = (_b = schedule.inEveryMonth) !== null && _b !== void 0 ? _b : false;
242
+ // An anchor-only range (start === end) means the schedule is pinned to that
243
+ // day; with a recurrence rule it then runs open-ended and the window is the
244
+ // only horizon. Otherwise the second bound ends validity.
245
+ const declaredEnd = _toUtcDayStart(bounds[1]);
246
+ const isOpenEnded = (declaredEnd === null || declaredEnd <= anchor) &&
247
+ (inEveryWeek || inEveryMonth);
248
+ const validTo = isOpenEnded
249
+ ? Number.POSITIVE_INFINITY
250
+ : Math.max(anchor, declaredEnd !== null && declaredEnd !== void 0 ? declaredEnd : anchor);
251
+ const rangeStart = Math.max(anchor, windowFrom);
252
+ const rangeEnd = Math.min(validTo, windowTo);
253
+ if (rangeEnd < rangeStart)
254
+ return [];
255
+ const days = _resolveActiveDays(anchor, rangeStart, rangeEnd, inEveryWeek, inEveryMonth);
256
+ const slots = [];
257
+ days.forEach((day) => {
258
+ if (isEntity) {
259
+ _addSlotsFromTimes(day, schedule.times, slots);
260
+ }
261
+ else if (Array.isArray(schedule.intervals)) {
262
+ _addSlotsFromRanges(day, schedule.intervals, slots);
263
+ }
264
+ });
265
+ return _dedupeAndSort(slots);
266
+ }
267
+ /**
268
+ * Narrows an attribute value to a `timeInterval` attribute.
269
+ *
270
+ * `IAttributeValue.value` is `unknown` — its shape depends on `type` — so this
271
+ * guard is what lets you reach the schedules without a cast.
272
+ * @param {IAttributeValue | undefined} attr - The attribute value to test.
273
+ * @returns {boolean} True when the attribute is a `timeInterval` carrying an array of groups.
274
+ * @example
275
+ * ```ts
276
+ * const attr = page.attributeValues.interval;
277
+ * if (isTimeIntervalAttribute(attr)) {
278
+ * attr.value[0].values[0].dates; // fully typed, no cast
279
+ * }
280
+ * ```
281
+ */
282
+ function isTimeIntervalAttribute(attr) {
283
+ return !!attr && attr.type === 'timeInterval' && Array.isArray(attr.value);
284
+ }
285
+ /**
286
+ * Expands a whole `timeInterval` attribute into concrete UTC slots for a window.
287
+ *
288
+ * The one-call path for the common case: it walks the attribute's groups and
289
+ * their schedules, expands each with {@link expandTimeIntervals}, and merges the
290
+ * results. Merging matters — deduplication and ordering only hold within a
291
+ * single schedule, so combining groups by hand can yield duplicate or unsorted
292
+ * slots.
293
+ *
294
+ * Anything that is not a `timeInterval` attribute yields an empty array, so this
295
+ * is safe to call on an arbitrary attribute without checking `type` first.
296
+ *
297
+ * For **form** attributes the schedules are already typed at
298
+ * `localizeInfos.intervals`, so no equivalent helper is needed — map over them
299
+ * and call {@link expandTimeIntervals} directly.
300
+ * @param {IAttributeValue | undefined} attr - A `timeInterval` attribute value, e.g. `page.attributeValues.interval`.
301
+ * @param {ITimeIntervalWindow} window - Inclusive `{ from, to }` range to resolve, compared at UTC day granularity.
302
+ * @returns {TimeIntervalPair[]} Sorted, deduplicated `[start, end]` ISO pairs across every group; empty when the attribute is not a `timeInterval`.
303
+ * @example
304
+ * ```ts
305
+ * import { expandAttributeTimeIntervals } from 'oneentry';
306
+ *
307
+ * const slots = expandAttributeTimeIntervals(page.attributeValues.interval, {
308
+ * from: '2025-04-01',
309
+ * to: '2025-04-30',
310
+ * });
311
+ * // [['2025-04-14T09:00:00.000Z', '2025-04-14T10:00:00.000Z'], …]
312
+ * ```
313
+ */
314
+ function expandAttributeTimeIntervals(attr, window) {
315
+ if (!isTimeIntervalAttribute(attr))
316
+ return [];
317
+ const slots = attr.value.flatMap((group) => Array.isArray(group === null || group === void 0 ? void 0 : group.values)
318
+ ? group.values.flatMap((schedule) => expandTimeIntervals(schedule, window))
319
+ : []);
320
+ return _dedupeAndSort(slots);
321
+ }
@@ -2,6 +2,7 @@
2
2
  * @interface IConfig
3
3
  * @property {string} [token] - If your project is protected by a token, specify this token in this parameter.
4
4
  * @property {string} [guestId] - Guest identifier sent as the `x-guest-id` header on unauthenticated requests. Lets cart/wishlist/activity endpoints work for a guest. In the browser a stable id is auto-generated (localStorage) when omitted; on the server pass a per-visitor id. Can also be set later via `setGuestId`.
5
+ * @property {string} [deviceMetadata] - Device-metadata string sent as the `x-device-metadata` header on POST requests and token refresh, overriding the environment-derived fingerprint. The API binds refresh tokens to this header, so server-side flows that issue tokens on behalf of a browser (e.g. an OAuth code exchange) must pass the browser's string here — otherwise the token is bound to the server's fingerprint and cannot be refreshed from the browser. Obtain the string in the browser via `getDeviceMetadata()`. Can also be set later via `setDeviceMetadata`.
5
6
  * @property {string} [langCode] - specify the default language to avoid specifying it in every request.
6
7
  * @property {boolean} [traficLimit] - Some methods use multiple queries to make it easier to work with the API. Set this parameter to "false" to save traffic and decide for yourself what data you need.
7
8
  * @property {boolean} [rawData] - Set to true to receive raw API responses without any transformation.
@@ -44,6 +45,7 @@
44
45
  interface IConfig {
45
46
  token?: string;
46
47
  guestId?: string;
48
+ deviceMetadata?: string;
47
49
  langCode?: string;
48
50
  traficLimit?: boolean;
49
51
  rawData?: boolean;
@@ -146,7 +148,7 @@ interface ILocalizeInfo {
146
148
  }
147
149
  /**
148
150
  * @interface IAttributeLocalizeInfo
149
- * @property {ITimeIntervalSchedule[]} [intervals] - For attributes of type `timeInterval`, the precomputed schedule data attached to the localization payload.
151
+ * @property {ITimeIntervalSchedule[]} [intervals] - For attributes of type `timeInterval`, the schedule data attached to the localization payload. Each entry is a compact recurrence rule — pass it to `expandTimeIntervals` to resolve concrete slots.
150
152
  * @description Extension of {@link ILocalizeInfo} used by attribute entities — adds attribute-type-specific fields the API exposes via `localizeInfos`.
151
153
  */
152
154
  interface IAttributeLocalizeInfo extends ILocalizeInfo {
@@ -161,8 +163,9 @@ interface IAttributeLocalizeInfo extends ILocalizeInfo {
161
163
  * @property {number} selectedYear - Year the schedule applies to. Example: 2025.
162
164
  * @property {ITimeIntervalRange[]} intervals - Daily time ranges configured for the schedule.
163
165
  * @property {unknown[]} external - External overrides; empty array when none.
164
- * @property {unknown[]} range - Date ranges; empty array when none.
165
- * @description One entry of a `timeInterval` attribute's schedule.
166
+ * @property {string[]} range - Validity range as a pair of ISO dates `[from, to]`; empty array when none. When both entries are equal the schedule is anchored to that single day and its recurrence is open-ended. Example: ["2025-05-07T21:00:00.000Z", "2025-05-07T21:00:00.000Z"].
167
+ * @property {boolean} [inEveryWeek] - Whether the schedule repeats every week. Optional.
168
+ * @description One entry of a `timeInterval` **form** attribute's schedule, carried on `localizeInfos.intervals`. Holds a compact recurrence rule — pass it to `expandTimeIntervals` to resolve concrete slots for a given window.
166
169
  */
167
170
  interface ITimeIntervalSchedule {
168
171
  id: string;
@@ -171,8 +174,65 @@ interface ITimeIntervalSchedule {
171
174
  selectedYear: number;
172
175
  intervals: ITimeIntervalRange[];
173
176
  external: unknown[];
174
- range: unknown[];
177
+ range: string[];
178
+ inEveryWeek?: boolean;
175
179
  }
180
+ /**
181
+ * @interface ITimeIntervalEntitySchedule
182
+ * @property {string} id - Schedule entry identifier (UUID). Example: "bbc82c9f-1bc4-4c86-b83c-c062016eb7cb".
183
+ * @property {string} intervalId - Identifier of the parent interval group (UUID). Example: "c6466cd8-c55d-4583-97c5-42b684210f12".
184
+ * @property {string[]} dates - Validity range as a pair of ISO dates `[from, to]`. When both entries are equal the schedule is anchored to that single day and its recurrence is open-ended. Example: ["2025-04-14T00:00:00.000Z", "2025-04-14T00:00:00.000Z"].
185
+ * @property {ITimeIntervalPoint[][]} times - Daily time ranges, each a `[start, end]` pair. Example: [[{ "hours": 9, "minutes": 0 }, { "hours": 10, "minutes": 0 }]].
186
+ * @property {boolean} inEveryWeek - Whether the schedule repeats every week. Example: true.
187
+ * @property {boolean} inEveryMonth - Whether the schedule repeats every month. Example: true.
188
+ * @property {unknown[]} [exceptions] - Excluded dates; empty array when none. Optional.
189
+ * @property {ITimeIntervalRange[]} [intervals] - Slot-based ranges; empty array on entity schedules, which use `times` instead. Optional.
190
+ * @description One entry of a `timeInterval` attribute's schedule on pages, products, blocks and attribute sets — found at `attributeValues[marker].value[].values[]`. Holds a compact recurrence rule — pass it to `expandTimeIntervals` to resolve concrete slots for a given window.
191
+ */
192
+ interface ITimeIntervalEntitySchedule {
193
+ id: string;
194
+ intervalId: string;
195
+ dates: string[];
196
+ times: ITimeIntervalPoint[][];
197
+ inEveryWeek: boolean;
198
+ inEveryMonth: boolean;
199
+ exceptions?: unknown[];
200
+ intervals?: ITimeIntervalRange[];
201
+ }
202
+ /**
203
+ * @interface ITimeIntervalGroup
204
+ * @property {string} intervalId - Identifier of the interval group (UUID). Example: "c6466cd8-c55d-4583-97c5-42b684210f12".
205
+ * @property {ITimeIntervalEntitySchedule[]} values - The schedules configured under this group.
206
+ * @description One entry of a `timeInterval` attribute's `value` array — a group of schedules sharing an `intervalId`.
207
+ */
208
+ interface ITimeIntervalGroup {
209
+ intervalId: string;
210
+ values: ITimeIntervalEntitySchedule[];
211
+ }
212
+ /**
213
+ * @interface ITimeIntervalAttributeValue
214
+ * @property {'timeInterval'} type - Discriminant identifying the attribute as a `timeInterval`.
215
+ * @property {ITimeIntervalGroup[]} value - Groups of schedules attached to the attribute.
216
+ * @description {@link IAttributeValue} narrowed to `timeInterval` attributes, whose generic `value: unknown` resolves to an array of {@link ITimeIntervalGroup}. Narrow to it with `isTimeIntervalAttribute`, or skip straight to the slots with `expandAttributeTimeIntervals`.
217
+ */
218
+ interface ITimeIntervalAttributeValue extends IAttributeValue {
219
+ type: 'timeInterval';
220
+ value: ITimeIntervalGroup[];
221
+ }
222
+ /**
223
+ * @interface ITimeIntervalWindow
224
+ * @property {Date | string | number} from - Inclusive start of the window.
225
+ * @property {Date | string | number} to - Inclusive end of the window.
226
+ * @description The bounded range to expand a schedule into. Both bounds are inclusive and compared at UTC day granularity, so the time-of-day component of `from`/`to` is ignored. The window is required: a schedule is an open-ended recurrence rule, and only the caller knows how far it needs to be resolved.
227
+ */
228
+ interface ITimeIntervalWindow {
229
+ from: Date | string | number;
230
+ to: Date | string | number;
231
+ }
232
+ /**
233
+ * TimeIntervalPair — one resolved slot as a `[start, end]` pair of ISO 8601 UTC timestamps.
234
+ */
235
+ type TimeIntervalPair = [string, string];
176
236
  /**
177
237
  * @interface ITimeIntervalPoint
178
238
  * @property {number} hours - Hour component (0-23).
@@ -287,4 +347,4 @@ type LangType = string | Array<string>;
287
347
  * LocalizeType
288
348
  */
289
349
  type LocalizeType = ILocalizeInfo;
290
- export type { IAttributeLocalizeInfo, IAttributes, IAttributeValue, IAttributeValues, IConfig, IError, IHttpHeaders, IHttpOptions, ILocalizeInfo, IRating, ITimeIntervalPoint, ITimeIntervalRange, ITimeIntervalSchedule, LangType, LocalizeType, };
350
+ export type { IAttributeLocalizeInfo, IAttributes, IAttributeValue, IAttributeValues, IConfig, IError, IHttpHeaders, IHttpOptions, ILocalizeInfo, IRating, ITimeIntervalAttributeValue, ITimeIntervalEntitySchedule, ITimeIntervalGroup, ITimeIntervalPoint, ITimeIntervalRange, ITimeIntervalSchedule, ITimeIntervalWindow, LangType, LocalizeType, TimeIntervalPair, };
@@ -5,7 +5,6 @@ exports.createPaginatedSchema = createPaginatedSchema;
5
5
  exports.validateResponse = validateResponse;
6
6
  exports.validateResponseSafe = validateResponseSafe;
7
7
  /* eslint-disable jsdoc/reject-any-type */
8
- /* eslint-disable jsdoc/no-undefined-types */
9
8
  const zod_1 = require("zod");
10
9
  /**
11
10
  * Common validation schemas for API responses
@@ -43,7 +43,12 @@ class BlocksApi extends asyncModules_1.default {
43
43
  const validated = this._validateResponse(response, blocksSchemas_1.BlocksResponseSchema);
44
44
  if (!this.state.traficLimit) {
45
45
  const normalizeResponse = this._normalizeData(validated);
46
- await Promise.all(normalizeResponse.items.map((block) => this._enrichBlock(block, langCode, offset, limit, true)));
46
+ // On API error responses (e.g. 403 without list permission, 422) the
47
+ // normalized value is an IError without `items` — skip enrichment and
48
+ // return it as-is
49
+ if (Array.isArray(normalizeResponse.items)) {
50
+ await Promise.all(normalizeResponse.items.map((block) => this._enrichBlock(block, langCode, offset, limit, true)));
51
+ }
47
52
  return normalizeResponse;
48
53
  }
49
54
  return this._normalizeData(validated);
@@ -169,6 +174,11 @@ class BlocksApi extends asyncModules_1.default {
169
174
  signPrice,
170
175
  };
171
176
  const result = await this._fetchGet(`/${marker}/products?` + this._queryParamsToString(query));
177
+ // On API error responses there is no `items` array — return the (normalized)
178
+ // error as-is instead of producing `undefined` from `result.items`.
179
+ if (!Array.isArray(result === null || result === void 0 ? void 0 : result.items)) {
180
+ return this._normalizeData(result);
181
+ }
172
182
  return this._normalizeData(result.items);
173
183
  }
174
184
  /**
@@ -79,7 +79,7 @@ interface IFromPages {
79
79
  interface IFormsEntity {
80
80
  id: number;
81
81
  attributeSetId: number | null;
82
- type: 'order' | 'sing_in_up' | 'collection' | 'data' | 'rating' | null;
82
+ type: 'order' | 'sign_in_up' | 'collection' | 'data' | 'rating' | null;
83
83
  localizeInfos: IFormLocalizeInfo;
84
84
  version: number;
85
85
  position: number;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  import AdminsApi from './admins/adminsApi';
5
5
  import AttributesSetsApi from './attribute-sets/attributeSetsApi';
6
6
  import AuthProviderApi from './auth-provider/authProviderApi';
7
- import type { IConfig } from './base/utils';
7
+ import type { IAttributeValue, IConfig, ITimeIntervalAttributeValue, ITimeIntervalEntitySchedule, ITimeIntervalGroup, ITimeIntervalSchedule, ITimeIntervalWindow, TimeIntervalPair } from './base/utils';
8
8
  import BlocksApi from './blocks/blocksApi';
9
9
  import DiscountsApi from './discounts/discountsApi';
10
10
  import EventsApi from './events/eventsApi';
@@ -29,6 +29,8 @@ import TemplatePreviewsApi from './templates-preview/templatesPreviewApi';
29
29
  import UserActivityApi from './user-activity/userActivityApi';
30
30
  import UsersApi from './users/usersApi';
31
31
  import WsApi from './web-socket/wsApi';
32
+ export { expandAttributeTimeIntervals, expandTimeIntervals, isTimeIntervalAttribute, } from './base/timeIntervals';
33
+ export type { IAttributeValue, ITimeIntervalAttributeValue, ITimeIntervalEntitySchedule, ITimeIntervalGroup, ITimeIntervalSchedule, ITimeIntervalWindow, TimeIntervalPair, };
32
34
  /**
33
35
  * IDefineApi interface
34
36
  * @interface IDefineApi
@@ -123,6 +125,7 @@ interface IDefineApi {
123
125
  * @param {IConfig} config - Custom configuration settings
124
126
  * @param {string} [config.token] - Optional token parameter
125
127
  * @param {string} [config.guestId] - Optional guest identifier sent as the `x-guest-id` header for guest cart/wishlist/activity flows (only while unauthenticated). In the browser, if omitted, a stable per-device id is generated and persisted in localStorage. On the server you MUST pass a per-visitor `guestId` (or call `setGuestId`): the SDK never auto-generates a server id, to avoid sharing one guest across visitors.
128
+ * @param {string} [config.deviceMetadata] - Optional device-metadata string sent as the `x-device-metadata` header instead of the environment-derived fingerprint. Refresh tokens are bound to this header, so a server issuing tokens for a browser (OAuth code exchange) must pass the browser's string (from `getDeviceMetadata()`), or the token will not be refreshable from that browser.
126
129
  * @param {string} [config.langCode] - Optional langCode parameter
127
130
  * @param {boolean} [config.traficLimit] - Some methods use multiple queries to make it easier to work with the API. Set this parameter to "false" to save traffic and decide for yourself what data you need.
128
131
  * @param {string} [config.auth] - An object with authorization settings.
@@ -134,4 +137,3 @@ interface IDefineApi {
134
137
  * @description Define API.
135
138
  */
136
139
  export declare function defineOneEntry(url: string, config: IConfig): IDefineApi;
137
- export {};
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isTimeIntervalAttribute = exports.expandTimeIntervals = exports.expandAttributeTimeIntervals = void 0;
6
7
  exports.defineOneEntry = defineOneEntry;
7
8
  /**
8
9
  * OneEntry SDK
@@ -35,6 +36,10 @@ const templatesPreviewApi_1 = __importDefault(require("./templates-preview/templ
35
36
  const userActivityApi_1 = __importDefault(require("./user-activity/userActivityApi"));
36
37
  const usersApi_1 = __importDefault(require("./users/usersApi"));
37
38
  const wsApi_1 = __importDefault(require("./web-socket/wsApi"));
39
+ var timeIntervals_1 = require("./base/timeIntervals");
40
+ Object.defineProperty(exports, "expandAttributeTimeIntervals", { enumerable: true, get: function () { return timeIntervals_1.expandAttributeTimeIntervals; } });
41
+ Object.defineProperty(exports, "expandTimeIntervals", { enumerable: true, get: function () { return timeIntervals_1.expandTimeIntervals; } });
42
+ Object.defineProperty(exports, "isTimeIntervalAttribute", { enumerable: true, get: function () { return timeIntervals_1.isTimeIntervalAttribute; } });
38
43
  /**
39
44
  * Define API.
40
45
  * @function defineOneEntry
@@ -42,6 +47,7 @@ const wsApi_1 = __importDefault(require("./web-socket/wsApi"));
42
47
  * @param {IConfig} config - Custom configuration settings
43
48
  * @param {string} [config.token] - Optional token parameter
44
49
  * @param {string} [config.guestId] - Optional guest identifier sent as the `x-guest-id` header for guest cart/wishlist/activity flows (only while unauthenticated). In the browser, if omitted, a stable per-device id is generated and persisted in localStorage. On the server you MUST pass a per-visitor `guestId` (or call `setGuestId`): the SDK never auto-generates a server id, to avoid sharing one guest across visitors.
50
+ * @param {string} [config.deviceMetadata] - Optional device-metadata string sent as the `x-device-metadata` header instead of the environment-derived fingerprint. Refresh tokens are bound to this header, so a server issuing tokens for a browser (OAuth code exchange) must pass the browser's string (from `getDeviceMetadata()`), or the token will not be refreshable from that browser.
45
51
  * @param {string} [config.langCode] - Optional langCode parameter
46
52
  * @param {boolean} [config.traficLimit] - Some methods use multiple queries to make it easier to work with the API. Set this parameter to "false" to save traffic and decide for yourself what data you need.
47
53
  * @param {string} [config.auth] - An object with authorization settings.
@@ -277,6 +277,12 @@ class PagesApi extends asyncModules_1.default {
277
277
  * For example, if 100 pages use 3 different templates, this method makes 3 requests instead of 100.
278
278
  */
279
279
  async addTemplateToPages(data) {
280
+ // On API error responses (e.g. 403 without list permission, 422) the value
281
+ // is an IError, not an array — return it as-is instead of calling array
282
+ // methods (.filter/.map) on it and throwing a TypeError.
283
+ if (!Array.isArray(data)) {
284
+ return data;
285
+ }
280
286
  // Step 1: Collect unique templateIdentifiers from all pages
281
287
  const uniqueIdentifiers = [
282
288
  ...new Set(data
@@ -381,6 +387,11 @@ class StaffModule extends asyncModules_1.default {
381
387
  async getProductsByBlockMarker(marker, langCode = this.state.lang, offset = 0, limit = 30) {
382
388
  // Fetch products from the server
383
389
  const result = await this._fetchGet(`/${marker}/products?langCode=${langCode}&offset=${offset}&limit=${limit}`);
390
+ // On API error responses there is no `items` array — return the (normalized)
391
+ // error as-is instead of producing `undefined` from `result.items`.
392
+ if (!Array.isArray(result === null || result === void 0 ? void 0 : result.items)) {
393
+ return this._normalizeData(result);
394
+ }
384
395
  return this._normalizeData(result.items);
385
396
  }
386
397
  }
@@ -1,4 +1,4 @@
1
- import type { IAttributeValues, IError } from '../base/utils';
1
+ import type { IError } from '../base/utils';
2
2
  /**
3
3
  * @interface ITemplatesPreviewApi
4
4
  * @description This interface defines methods for retrieving template previews in the system, including fetching all previews, specific previews by marker.
@@ -69,7 +69,6 @@ interface ITemplatesPreviewApi {
69
69
  }
70
70
  * @property {string} identifier - The textual identifier for the record field. Example: "preview-templates"
71
71
  * @property {number} version - The version number of the object. Example: 1.
72
- * @property {IAttributeValues} attributeValues - Attribute values from index. Example: {}
73
72
  * @property {number} position - The position of the object. Example: 1.
74
73
  * @property {boolean} isUsed - Indicates whether the template preview is used. Example: true.
75
74
  * @property {string | null} [attributeSetIdentifier] - Text identifier used for a set of attributes. Example: "attribute_set_1".
@@ -81,7 +80,6 @@ interface ITemplatesPreviewEntity {
81
80
  proportions: ITemplatesPreviewProportions;
82
81
  identifier: string;
83
82
  version: number;
84
- attributeValues: IAttributeValues;
85
83
  position: number;
86
84
  isUsed: boolean;
87
85
  attributeSetIdentifier?: string | null;
@@ -45,7 +45,6 @@ export declare const TemplatePreviewEntitySchema: z.ZodObject<{
45
45
  }, z.core.$strip>;
46
46
  identifier: z.ZodString;
47
47
  version: z.ZodNumber;
48
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
49
48
  position: z.ZodNumber;
50
49
  isUsed: z.ZodBoolean;
51
50
  attributeSetIdentifier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -76,7 +75,6 @@ export declare const TemplatePreviewsResponseSchema: z.ZodArray<z.ZodObject<{
76
75
  }, z.core.$strip>;
77
76
  identifier: z.ZodString;
78
77
  version: z.ZodNumber;
79
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
80
78
  position: z.ZodNumber;
81
79
  isUsed: z.ZodBoolean;
82
80
  attributeSetIdentifier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -37,7 +37,6 @@ exports.TemplatePreviewEntitySchema = zod_1.z.object({
37
37
  }),
38
38
  identifier: zod_1.z.string(),
39
39
  version: zod_1.z.number(),
40
- attributeValues: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
41
40
  position: zod_1.z.number(),
42
41
  isUsed: zod_1.z.boolean(),
43
42
  attributeSetIdentifier: zod_1.z.string().nullable().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oneentry",
3
- "version": "1.0.154",
3
+ "version": "1.0.156",
4
4
  "description": "OneEntry NPM package",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,39 +0,0 @@
1
- /**
2
- * Result class for handling response data.
3
- * @description Result class for handling response data.
4
- */
5
- export default class Result {
6
- body: any;
7
- /**
8
- * Constructor that initializes the class with a given data.
9
- * @param {Response | string} data - Response or string data.
10
- * @description Constructor that initializes the class with a given data, which can be of type Response or string.
11
- */
12
- constructor(data: Response | string);
13
- /**
14
- * Asynchronously converts the body to a blob and returns the current instance.
15
- * @returns {Promise<Result>} Current instance.
16
- * @description Asynchronously converts the body to a blob and returns the current instance.
17
- */
18
- blob(): Promise<Result>;
19
- /**
20
- * Asynchronously parses the body as JSON and returns the current instance.
21
- * @returns {Promise<Result>} Current instance.
22
- * @description Asynchronously parses the body as JSON and returns the current instance.
23
- */
24
- json(): Promise<Result>;
25
- /**
26
- * Recursively removes language-specific data from the provided data or the body.
27
- * @param {string} langCode - Language code.
28
- * @param {any} [data] - Data to process.
29
- * @returns {any} Processed data.
30
- * @description Recursively removes language-specific data from the provided data or the body.
31
- */
32
- makeDataWithoutLang(langCode: string, data?: any): any;
33
- /**
34
- * Recursively simplifies arrays in the provided data or the body by removing single-element arrays.
35
- * @param {any} data - The data to simplify.
36
- * @returns {any} The simplified data.
37
- */
38
- makeDataWithoutArray(data?: any): any;
39
- }