@pisell/pisellos 2.3.72 → 2.3.73

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 (42) hide show
  1. package/dist/modules/OpenData/types.d.ts +1 -0
  2. package/dist/modules/Order/utils.js +6 -1
  3. package/dist/modules/ResourcePlanner/index.d.ts +0 -22
  4. package/dist/modules/ResourcePlanner/index.js +1 -180
  5. package/dist/modules/ResourcePlanner/localClock.d.ts +33 -0
  6. package/dist/modules/ResourcePlanner/localClock.js +183 -0
  7. package/dist/modules/ResourcePlanner/planner.d.ts +5 -14
  8. package/dist/modules/ResourcePlanner/planner.js +2019 -936
  9. package/dist/modules/ResourcePlanner/types.d.ts +346 -187
  10. package/dist/modules/ResourcePlanner/types.js +33 -1
  11. package/dist/solution/BookingByStep/index.d.ts +1 -1
  12. package/dist/solution/BookingTicket/index.js +4 -2
  13. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
  14. package/dist/solution/BookingTicket/utils/addProductDecision.js +3 -2
  15. package/dist/solution/UnifiedBookingSales/index.d.ts +110 -73
  16. package/dist/solution/UnifiedBookingSales/index.js +3349 -971
  17. package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
  18. package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.js +430 -0
  19. package/dist/solution/UnifiedBookingSales/types.d.ts +133 -76
  20. package/dist/solution/UnifiedBookingSales/types.js +4 -9
  21. package/lib/model/strategy/adapter/promotion/index.js +0 -51
  22. package/lib/modules/OpenData/types.d.ts +1 -0
  23. package/lib/modules/Order/utils.js +5 -1
  24. package/lib/modules/ResourcePlanner/index.d.ts +0 -22
  25. package/lib/modules/ResourcePlanner/index.js +0 -123
  26. package/lib/modules/ResourcePlanner/localClock.d.ts +33 -0
  27. package/lib/modules/ResourcePlanner/localClock.js +270 -0
  28. package/lib/modules/ResourcePlanner/planner.d.ts +5 -14
  29. package/lib/modules/ResourcePlanner/planner.js +1903 -798
  30. package/lib/modules/ResourcePlanner/types.d.ts +346 -187
  31. package/lib/modules/ResourcePlanner/types.js +19 -0
  32. package/lib/solution/BookingByStep/index.d.ts +1 -1
  33. package/lib/solution/BookingTicket/index.js +2 -1
  34. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
  35. package/lib/solution/BookingTicket/utils/addProductDecision.js +2 -0
  36. package/lib/solution/UnifiedBookingSales/index.d.ts +110 -73
  37. package/lib/solution/UnifiedBookingSales/index.js +2289 -374
  38. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
  39. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +376 -0
  40. package/lib/solution/UnifiedBookingSales/types.d.ts +133 -76
  41. package/lib/solution/UnifiedBookingSales/types.js +3 -2
  42. package/package.json +1 -1
@@ -0,0 +1,49 @@
1
+ import type { AvailabilityDateRange } from '../../modules/ResourcePlanner';
2
+ type AvailabilityId = number | string;
3
+ export declare class AvailabilityRemoteOccupancyCache {
4
+ private readonly maxCells;
5
+ private readonly cells;
6
+ private readonly resourceMeta;
7
+ private readonly resourceCellCounts;
8
+ constructor(maxCells?: number);
9
+ clear(): void;
10
+ read(params: {
11
+ scopeKey: string;
12
+ resourceIds: AvailabilityId[];
13
+ dateRange: AvailabilityDateRange;
14
+ }): Record<string, any>[] | null;
15
+ hasCompleteCoverage(params: {
16
+ scopeKey: string;
17
+ resourceIds: AvailabilityId[];
18
+ dateRange: AvailabilityDateRange;
19
+ }): boolean;
20
+ publish(params: {
21
+ scopeKey: string;
22
+ timezoneName: string;
23
+ resourceIds: AvailabilityId[];
24
+ dateRange: AvailabilityDateRange;
25
+ rawResources: Record<string, any>[];
26
+ version: number;
27
+ }): void;
28
+ private buildResourceKey;
29
+ private buildCellKey;
30
+ private touchCell;
31
+ /**
32
+ * A response is one coherent resource snapshot for its requested range. If any overlapping day
33
+ * already contains a newer response, publishing only the older response's remaining days would
34
+ * let a cross-midnight event leak back into that newer day when ranges are recomposed.
35
+ */
36
+ private canPublishResourceRange;
37
+ /**
38
+ * A day cell stores the original event range so it can be reconstructed without losing server
39
+ * identity. When a newer adjacent-day response replaces that event, evict older neighbour cells
40
+ * whose event overlaps the refreshed range. The next wider read becomes a cache miss instead of
41
+ * reintroducing stale occupancy from the neighbour.
42
+ */
43
+ private invalidateAdjacentOverlappingCells;
44
+ private deleteCell;
45
+ private deleteResourceCells;
46
+ private writeResourceMeta;
47
+ private writeCell;
48
+ }
49
+ export {};
@@ -0,0 +1,376 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/solution/UnifiedBookingSales/remoteOccupancyCache.ts
20
+ var remoteOccupancyCache_exports = {};
21
+ __export(remoteOccupancyCache_exports, {
22
+ AvailabilityRemoteOccupancyCache: () => AvailabilityRemoteOccupancyCache
23
+ });
24
+ module.exports = __toCommonJS(remoteOccupancyCache_exports);
25
+ var import_lodash_es = require("lodash-es");
26
+ var import_localClock = require("../../modules/ResourcePlanner/localClock");
27
+ function uniqueIds(values) {
28
+ const result = /* @__PURE__ */ new Map();
29
+ values.forEach((value) => {
30
+ if (value === void 0 || value === null || value === "")
31
+ return;
32
+ result.set(String(value), value);
33
+ });
34
+ return Array.from(result.values());
35
+ }
36
+ function buildDateList(dateRange) {
37
+ if (!(dateRange == null ? void 0 : dateRange.startDate) || !(dateRange == null ? void 0 : dateRange.endDate))
38
+ return [];
39
+ try {
40
+ const startOrdinal = (0, import_localClock.dateOrdinalFromLocalDate)(dateRange.startDate);
41
+ const endOrdinal = (0, import_localClock.dateOrdinalFromLocalDate)(dateRange.endDate);
42
+ if (endOrdinal < startOrdinal)
43
+ return [];
44
+ const dates = [];
45
+ let cursor = (0, import_localClock.localDateToScalarStart)(dateRange.startDate);
46
+ for (let ordinal = startOrdinal; ordinal <= endOrdinal; ordinal += 1) {
47
+ dates.push((0, import_localClock.formatLocalDate)(cursor));
48
+ cursor = (0, import_localClock.addLocalCalendarDays)(cursor, 1);
49
+ }
50
+ return dates;
51
+ } catch {
52
+ return [];
53
+ }
54
+ }
55
+ function resolveResourceId(raw) {
56
+ const id = raw.resourceId ?? raw.resource_id ?? raw.id;
57
+ if (id === void 0 || id === null || id === "")
58
+ return null;
59
+ return id;
60
+ }
61
+ function getRawEventStartAt(rawEvent) {
62
+ const startAt = rawEvent.start_at ?? rawEvent.startAt ?? rawEvent.start_time ?? rawEvent.startTime;
63
+ return startAt ? String(startAt) : null;
64
+ }
65
+ function getRawEventEndAt(rawEvent) {
66
+ const endAt = rawEvent.end_at ?? rawEvent.endAt ?? rawEvent.end_time ?? rawEvent.endTime;
67
+ return endAt ? String(endAt) : null;
68
+ }
69
+ function parseEventScalar(value) {
70
+ return (0, import_localClock.localDateTimeToScalar)((0, import_localClock.parseLocalDateTime)(value));
71
+ }
72
+ function buildEventIdentity(resourceId, rawEvent) {
73
+ var _a, _b;
74
+ const scheduleEventId = rawEvent.schedule_event_id ?? rawEvent.scheduleEventId;
75
+ if (scheduleEventId !== void 0 && scheduleEventId !== null && scheduleEventId !== "") {
76
+ return `scheduleEvent:${String(resourceId)}:${String(scheduleEventId)}`;
77
+ }
78
+ const bookingUid = rawEvent.booking_uid ?? rawEvent.bookingUid ?? ((_a = rawEvent.metadata) == null ? void 0 : _a.unique_identification_number);
79
+ if (bookingUid !== void 0 && bookingUid !== null && bookingUid !== "") {
80
+ return `bookingUid:${String(resourceId)}:${String(bookingUid)}`;
81
+ }
82
+ const productUid = rawEvent.product_uid ?? rawEvent.productUid ?? ((_b = rawEvent.metadata) == null ? void 0 : _b.product_uid);
83
+ if (productUid !== void 0 && productUid !== null && productUid !== "") {
84
+ return `productUid:${String(resourceId)}:${String(productUid)}`;
85
+ }
86
+ return [
87
+ "range",
88
+ String(resourceId),
89
+ getRawEventStartAt(rawEvent) || "",
90
+ getRawEventEndAt(rawEvent) || ""
91
+ ].join(":");
92
+ }
93
+ function bucketEventDates(params) {
94
+ const startAt = getRawEventStartAt(params.rawEvent);
95
+ const endAt = getRawEventEndAt(params.rawEvent);
96
+ if (!startAt || !endAt)
97
+ return [];
98
+ try {
99
+ const startScalar = parseEventScalar(startAt);
100
+ const endScalar = parseEventScalar(endAt);
101
+ if (endScalar <= startScalar)
102
+ return [];
103
+ const endInclusiveScalar = endScalar - 1e3;
104
+ const startDate = (0, import_localClock.formatLocalDate)(startScalar);
105
+ const endDate = (0, import_localClock.formatLocalDate)(endInclusiveScalar);
106
+ const startOrdinal = (0, import_localClock.dateOrdinalFromLocalDate)(startDate);
107
+ const endOrdinal = (0, import_localClock.dateOrdinalFromLocalDate)(endDate);
108
+ if (endOrdinal < startOrdinal)
109
+ return [];
110
+ const dates = [];
111
+ let cursorDate = startDate;
112
+ for (let ordinal = startOrdinal; ordinal <= endOrdinal; ordinal += 1) {
113
+ if (params.requestedDates.has(cursorDate)) {
114
+ dates.push(cursorDate);
115
+ }
116
+ cursorDate = (0, import_localClock.nextDate)(cursorDate);
117
+ }
118
+ return dates;
119
+ } catch {
120
+ return [];
121
+ }
122
+ }
123
+ function sortEventList(events) {
124
+ return [...events].sort((left, right) => {
125
+ const leftStart = getRawEventStartAt(left) || "";
126
+ const rightStart = getRawEventStartAt(right) || "";
127
+ if (leftStart !== rightStart)
128
+ return leftStart.localeCompare(rightStart);
129
+ const leftEnd = getRawEventEndAt(left) || "";
130
+ const rightEnd = getRawEventEndAt(right) || "";
131
+ if (leftEnd !== rightEnd)
132
+ return leftEnd.localeCompare(rightEnd);
133
+ return buildEventIdentity(left.resource_id ?? left.resourceId ?? "", left).localeCompare(buildEventIdentity(right.resource_id ?? right.resourceId ?? "", right));
134
+ });
135
+ }
136
+ var AvailabilityRemoteOccupancyCache = class {
137
+ constructor(maxCells = 4096) {
138
+ this.cells = /* @__PURE__ */ new Map();
139
+ this.resourceMeta = /* @__PURE__ */ new Map();
140
+ this.resourceCellCounts = /* @__PURE__ */ new Map();
141
+ this.maxCells = maxCells;
142
+ }
143
+ clear() {
144
+ this.cells.clear();
145
+ this.resourceMeta.clear();
146
+ this.resourceCellCounts.clear();
147
+ }
148
+ read(params) {
149
+ const requestedResourceIds = uniqueIds(params.resourceIds);
150
+ const requestedDates = buildDateList(params.dateRange);
151
+ if (requestedResourceIds.length === 0 || requestedDates.length === 0)
152
+ return [];
153
+ const resources = [];
154
+ for (const resourceId of requestedResourceIds) {
155
+ const resourceKey = this.buildResourceKey(params.scopeKey, resourceId);
156
+ const eventList = /* @__PURE__ */ new Map();
157
+ for (const date of requestedDates) {
158
+ const cellKey = this.buildCellKey(resourceKey, date);
159
+ const cell = this.touchCell(cellKey);
160
+ if (!cell)
161
+ return null;
162
+ cell.eventList.forEach((rawEvent) => {
163
+ eventList.set(buildEventIdentity(resourceId, rawEvent), (0, import_lodash_es.cloneDeep)(rawEvent));
164
+ });
165
+ }
166
+ const metadataEntry = this.resourceMeta.get(resourceKey);
167
+ if (!metadataEntry)
168
+ return null;
169
+ if (metadataEntry.status === "absent")
170
+ continue;
171
+ if (!metadataEntry.metadata)
172
+ return null;
173
+ resources.push({
174
+ ...(0, import_lodash_es.cloneDeep)(metadataEntry.metadata),
175
+ event_list: sortEventList(Array.from(eventList.values()))
176
+ });
177
+ }
178
+ return resources;
179
+ }
180
+ hasCompleteCoverage(params) {
181
+ const requestedResourceIds = uniqueIds(params.resourceIds);
182
+ const requestedDates = buildDateList(params.dateRange);
183
+ if (requestedResourceIds.length === 0 || requestedDates.length === 0)
184
+ return true;
185
+ for (const resourceId of requestedResourceIds) {
186
+ const resourceKey = this.buildResourceKey(params.scopeKey, resourceId);
187
+ for (const date of requestedDates) {
188
+ const cell = this.cells.get(this.buildCellKey(resourceKey, date));
189
+ if (!cell)
190
+ return false;
191
+ }
192
+ }
193
+ return true;
194
+ }
195
+ publish(params) {
196
+ const requestedResourceIds = uniqueIds(params.resourceIds);
197
+ const requestedDates = buildDateList(params.dateRange);
198
+ if (requestedResourceIds.length === 0 || requestedDates.length === 0)
199
+ return;
200
+ const requestedDateSet = new Set(requestedDates);
201
+ const publishedAt = Date.now();
202
+ const requestedResourceKeyById = new Map(
203
+ requestedResourceIds.map((resourceId) => [
204
+ String(resourceId),
205
+ this.buildResourceKey(params.scopeKey, resourceId)
206
+ ])
207
+ );
208
+ const rawResourceById = /* @__PURE__ */ new Map();
209
+ (params.rawResources || []).forEach((rawResource) => {
210
+ const resourceId = resolveResourceId(rawResource);
211
+ if (resourceId !== null && requestedResourceKeyById.has(String(resourceId))) {
212
+ rawResourceById.set(String(resourceId), rawResource);
213
+ }
214
+ });
215
+ requestedResourceIds.forEach((resourceId) => {
216
+ const resourceKey = requestedResourceKeyById.get(String(resourceId));
217
+ if (!resourceKey)
218
+ return;
219
+ if (!this.canPublishResourceRange(resourceKey, requestedDates, params.version))
220
+ return;
221
+ const rawResource = rawResourceById.get(String(resourceId));
222
+ const nextStatus = rawResource ? "present" : "absent";
223
+ const currentMetadata = this.resourceMeta.get(resourceKey);
224
+ if (currentMetadata && currentMetadata.version <= params.version && currentMetadata.status !== nextStatus) {
225
+ this.deleteResourceCells(resourceKey);
226
+ }
227
+ this.invalidateAdjacentOverlappingCells({
228
+ resourceKey,
229
+ requestedDates: requestedDateSet,
230
+ timezoneName: params.timezoneName
231
+ });
232
+ const metadata = rawResource ? (({ event_list: _eventList, ...rest }) => rest)(rawResource) : void 0;
233
+ this.writeResourceMeta(resourceKey, nextStatus, metadata, params.version);
234
+ const eventsByDate = /* @__PURE__ */ new Map();
235
+ requestedDates.forEach((date) => {
236
+ eventsByDate.set(date, /* @__PURE__ */ new Map());
237
+ });
238
+ const eventList = Array.isArray(rawResource == null ? void 0 : rawResource.event_list) ? rawResource.event_list : [];
239
+ eventList.forEach((event) => {
240
+ const rawEvent = (0, import_lodash_es.cloneDeep)(event);
241
+ const identity = buildEventIdentity(resourceId, rawEvent);
242
+ bucketEventDates({
243
+ rawEvent,
244
+ timezoneName: params.timezoneName,
245
+ requestedDates: requestedDateSet
246
+ }).forEach((date) => {
247
+ var _a;
248
+ (_a = eventsByDate.get(date)) == null ? void 0 : _a.set(identity, rawEvent);
249
+ });
250
+ });
251
+ requestedDates.forEach((date) => {
252
+ var _a;
253
+ this.writeCell({
254
+ key: this.buildCellKey(resourceKey, date),
255
+ resourceKey,
256
+ version: params.version,
257
+ eventList: sortEventList(Array.from(((_a = eventsByDate.get(date)) == null ? void 0 : _a.values()) || [])),
258
+ fetchedAt: publishedAt,
259
+ lastAccessedAt: publishedAt
260
+ });
261
+ });
262
+ });
263
+ }
264
+ buildResourceKey(scopeKey, resourceId) {
265
+ return `${scopeKey}:${String(resourceId)}`;
266
+ }
267
+ buildCellKey(resourceKey, date) {
268
+ return `${resourceKey}:${date}`;
269
+ }
270
+ touchCell(cellKey) {
271
+ const entry = this.cells.get(cellKey);
272
+ if (!entry)
273
+ return null;
274
+ entry.lastAccessedAt = Date.now();
275
+ this.cells.delete(cellKey);
276
+ this.cells.set(cellKey, entry);
277
+ return entry;
278
+ }
279
+ /**
280
+ * A response is one coherent resource snapshot for its requested range. If any overlapping day
281
+ * already contains a newer response, publishing only the older response's remaining days would
282
+ * let a cross-midnight event leak back into that newer day when ranges are recomposed.
283
+ */
284
+ canPublishResourceRange(resourceKey, requestedDates, version) {
285
+ const metadata = this.resourceMeta.get(resourceKey);
286
+ if (metadata && metadata.version > version)
287
+ return false;
288
+ return requestedDates.every((date) => {
289
+ const current = this.cells.get(this.buildCellKey(resourceKey, date));
290
+ return !current || current.version <= version;
291
+ });
292
+ }
293
+ /**
294
+ * A day cell stores the original event range so it can be reconstructed without losing server
295
+ * identity. When a newer adjacent-day response replaces that event, evict older neighbour cells
296
+ * whose event overlaps the refreshed range. The next wider read becomes a cache miss instead of
297
+ * reintroducing stale occupancy from the neighbour.
298
+ */
299
+ invalidateAdjacentOverlappingCells(params) {
300
+ const keysToDelete = [];
301
+ this.cells.forEach((cell, cellKey) => {
302
+ if (cell.resourceKey !== params.resourceKey)
303
+ return;
304
+ const cellDate = cellKey.slice(cell.resourceKey.length + 1);
305
+ if (params.requestedDates.has(cellDate))
306
+ return;
307
+ const overlapsRequestedDate = cell.eventList.some((rawEvent) => bucketEventDates({
308
+ rawEvent,
309
+ timezoneName: params.timezoneName,
310
+ requestedDates: params.requestedDates
311
+ }).length > 0);
312
+ if (overlapsRequestedDate)
313
+ keysToDelete.push(cellKey);
314
+ });
315
+ keysToDelete.forEach((cellKey) => this.deleteCell(cellKey));
316
+ }
317
+ deleteCell(cellKey) {
318
+ const entry = this.cells.get(cellKey);
319
+ if (!entry)
320
+ return;
321
+ this.cells.delete(cellKey);
322
+ const nextCount = (this.resourceCellCounts.get(entry.resourceKey) || 1) - 1;
323
+ if (nextCount <= 0) {
324
+ this.resourceCellCounts.delete(entry.resourceKey);
325
+ this.resourceMeta.delete(entry.resourceKey);
326
+ return;
327
+ }
328
+ this.resourceCellCounts.set(entry.resourceKey, nextCount);
329
+ }
330
+ deleteResourceCells(resourceKey) {
331
+ const keysToDelete = [];
332
+ this.cells.forEach((cell, cellKey) => {
333
+ if (cell.resourceKey === resourceKey)
334
+ keysToDelete.push(cellKey);
335
+ });
336
+ keysToDelete.forEach((cellKey) => this.deleteCell(cellKey));
337
+ }
338
+ writeResourceMeta(resourceKey, status, metadata, version) {
339
+ const current = this.resourceMeta.get(resourceKey);
340
+ if (current && current.version > version)
341
+ return;
342
+ this.resourceMeta.set(resourceKey, {
343
+ version,
344
+ status,
345
+ metadata: metadata ? (0, import_lodash_es.cloneDeep)(metadata) : void 0
346
+ });
347
+ }
348
+ writeCell(entry) {
349
+ const current = this.cells.get(entry.key);
350
+ if (current && current.version > entry.version)
351
+ return;
352
+ if (!current) {
353
+ this.resourceCellCounts.set(
354
+ entry.resourceKey,
355
+ (this.resourceCellCounts.get(entry.resourceKey) || 0) + 1
356
+ );
357
+ } else {
358
+ this.cells.delete(entry.key);
359
+ }
360
+ this.cells.set(entry.key, {
361
+ ...entry,
362
+ eventList: (0, import_lodash_es.cloneDeep)(entry.eventList)
363
+ });
364
+ while (this.cells.size > this.maxCells) {
365
+ const oldest = this.cells.entries().next().value;
366
+ if (!oldest)
367
+ break;
368
+ const [oldestKey] = oldest;
369
+ this.deleteCell(oldestKey);
370
+ }
371
+ }
372
+ };
373
+ // Annotate the CommonJS export names for ESM import in node:
374
+ 0 && (module.exports = {
375
+ AvailabilityRemoteOccupancyCache
376
+ });