@outbuild-company/schedule-core 1.9.4 → 1.10.0

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/dist/index.cjs CHANGED
@@ -338,6 +338,7 @@ var FIELD_REGISTRY = {
338
338
  },
339
339
  expectedProgressBaseline: {
340
340
  valueKind: "number",
341
+ equalityPrecision: 2,
341
342
  extract: (activity) => activity.expectedProgressBaseline
342
343
  },
343
344
  expectedProgress: {
@@ -426,6 +427,9 @@ function toTargetDate(value) {
426
427
  }
427
428
  return new Date(Number.NaN);
428
429
  }
430
+ function utcDayMs(date2) {
431
+ return Date.UTC(date2.getUTCFullYear(), date2.getUTCMonth(), date2.getUTCDate());
432
+ }
429
433
  function matchString(value, target, operator) {
430
434
  const isAbsent = value === null;
431
435
  if (operator === "includes") {
@@ -438,12 +442,13 @@ function matchString(value, target, operator) {
438
442
  if (operator === "isNot") return isAbsent || String(value) !== target;
439
443
  return false;
440
444
  }
441
- function matchNumber(value, target, operator) {
445
+ function matchNumber(value, target, operator, equalityPrecision) {
442
446
  if (value === null || typeof value !== "number") {
443
447
  return operator === "notEquals";
444
448
  }
445
- if (operator === "equals") return value === target;
446
- if (operator === "notEquals") return value !== target;
449
+ const equalityValue = equalityPrecision === void 0 ? value : Number(value.toFixed(equalityPrecision));
450
+ if (operator === "equals") return equalityValue === target;
451
+ if (operator === "notEquals") return equalityValue !== target;
447
452
  if (operator === "greaterThan") return value > target;
448
453
  if (operator === "lessThan") return value < target;
449
454
  if (operator === "greaterOrEqual") return value >= target;
@@ -455,14 +460,16 @@ function matchNumberBetween(value, minimum, maximum) {
455
460
  }
456
461
  function matchDate(value, target, operator) {
457
462
  if (!(value instanceof Date)) return false;
458
- if (operator === "after") return value.getTime() > target.getTime();
459
- if (operator === "before") return value.getTime() < target.getTime();
463
+ const valueDay = utcDayMs(value);
464
+ const targetDay = utcDayMs(target);
465
+ if (operator === "after") return valueDay > targetDay;
466
+ if (operator === "before") return valueDay < targetDay;
460
467
  return false;
461
468
  }
462
469
  function matchDateBetween(value, minimum, maximum) {
463
470
  if (!(value instanceof Date)) return false;
464
- const time = value.getTime();
465
- return time >= minimum.getTime() && time <= maximum.getTime();
471
+ const day = utcDayMs(value);
472
+ return day >= utcDayMs(minimum) && day <= utcDayMs(maximum);
466
473
  }
467
474
  function matchIdArray(value, allowed, operator) {
468
475
  const ids = Array.isArray(value) ? value : [];
@@ -507,7 +514,12 @@ function matchCriterion(activity, criterion, context) {
507
514
  return matchString(value, String(criterion.value), operator);
508
515
  }
509
516
  if (descriptor.valueKind === "number") {
510
- return matchNumber(value, Number(criterion.value), operator);
517
+ return matchNumber(
518
+ value,
519
+ Number(criterion.value),
520
+ operator,
521
+ descriptor.equalityPrecision
522
+ );
511
523
  }
512
524
  if (descriptor.valueKind === "date") {
513
525
  return matchDate(value, toTargetDate(criterion.value), operator);
@@ -529,10 +541,10 @@ function matchesCriteria(activity, filter, context) {
529
541
  );
530
542
  }
531
543
  function overlapsRange(activity, range) {
532
- const taskStart = activity.startDate.getTime();
533
- const taskEnd = activity.endDate.getTime();
534
- const windowStart = range.start.getTime();
535
- const windowEnd = range.end.getTime();
544
+ const taskStart = utcDayMs(activity.startDate);
545
+ const taskEnd = utcDayMs(activity.endDate);
546
+ const windowStart = utcDayMs(range.start);
547
+ const windowEnd = utcDayMs(range.end);
536
548
  const fullyInside = taskStart >= windowStart && taskEnd <= windowEnd;
537
549
  const startsInside = taskStart >= windowStart && taskStart <= windowEnd;
538
550
  const windowStartsInside = windowStart >= taskStart && windowStart <= taskEnd;
@@ -1681,13 +1693,19 @@ function resolveExpansionAnchors(originalLink, adapter) {
1681
1693
  userLag: originalLink.lag ?? 0,
1682
1694
  sourceEndpointIsStart,
1683
1695
  targetLag: targetEndpointIsFinish ? -endpointSpanHours(target, targetBounds, adapter) : 0,
1684
- summarySourceId: originalLink.type === LINK_TYPE.START_TO_START && sourceBounds ? originalLink.source : void 0
1696
+ summarySourceId: originalLink.type === LINK_TYPE.START_TO_START && sourceBounds ? originalLink.source : void 0,
1697
+ finishAnchoredSummaryTargetId: targetEndpointIsFinish && targetBounds ? originalLink.target : void 0
1685
1698
  };
1686
1699
  }
1687
1700
  function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, adapter, virtualIdCounter, linksByActivity) {
1688
1701
  const anchors = resolveExpansionAnchors(originalLink, adapter);
1689
1702
  const { source, sourceBounds, targetStart, userLag } = anchors;
1690
- const { sourceEndpointIsStart, targetLag, summarySourceId } = anchors;
1703
+ const {
1704
+ sourceEndpointIsStart,
1705
+ targetLag,
1706
+ summarySourceId,
1707
+ finishAnchoredSummaryTargetId
1708
+ } = anchors;
1691
1709
  let counter = virtualIdCounter;
1692
1710
  for (const srcId of sourceIds) {
1693
1711
  const sourceLag = sourceEndpointIsStart ? sourceLagForLeaf(srcId, source, sourceBounds, adapter) : 0;
@@ -1710,7 +1728,7 @@ function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, a
1710
1728
  targetLag,
1711
1729
  userLag + offTarget,
1712
1730
  summarySourceId,
1713
- offTarget !== 0 && targetLag === 0 ? originalLink.target : void 0
1731
+ finishAnchoredSummaryTargetId ?? (offTarget !== 0 && targetLag === 0 ? originalLink.target : void 0)
1714
1732
  )
1715
1733
  );
1716
1734
  counter += 1;
@@ -2158,7 +2176,7 @@ async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
2158
2176
  if (!maxStart || result.successorStart > maxStart) {
2159
2177
  maxStart = result.successorStart;
2160
2178
  drivingLinkId = String(link.id);
2161
- drivingOffsetIsStale = link._summaryTargetId !== void 0 && !predecessorMovedThisRun;
2179
+ drivingOffsetIsStale = link._summaryTargetId !== void 0 && !predecessorMovedThisRun && (link._targetLag === 0 || options.preventSummaryTargetPullback === true);
2162
2180
  }
2163
2181
  }
2164
2182
  const staleOffsetWouldPullBack = maxStart !== null && drivingOffsetIsStale && maxStart < activity.startDate;
@@ -10021,7 +10039,11 @@ async function runPostMutation(deps, args) {
10021
10039
  if (passes.has(DERIVED_PASS.AUTOSCHEDULE)) {
10022
10040
  await refreshMutatedParentBounds(deps.adapter, args);
10023
10041
  }
10024
- const schedule = passes.has(DERIVED_PASS.AUTOSCHEDULE) ? await runAutoschedule(deps, args.autoscheduleFrom) : emptySchedule();
10042
+ const schedule = passes.has(DERIVED_PASS.AUTOSCHEDULE) ? await runAutoschedule(
10043
+ deps,
10044
+ args.autoscheduleFrom,
10045
+ args.preventSummaryTargetPullback
10046
+ ) : emptySchedule();
10025
10047
  await rollUpParentBounds(
10026
10048
  deps.adapter,
10027
10049
  args,
@@ -10092,8 +10114,11 @@ function collectFieldProgressInvalidations(adapter, beforeById, dirtySeeds, affe
10092
10114
  }
10093
10115
  }
10094
10116
  }
10095
- async function runAutoschedule(deps, autoscheduleFrom) {
10117
+ async function runAutoschedule(deps, autoscheduleFrom, preventSummaryTargetPullback = false) {
10096
10118
  const options = autoscheduleFrom === "roots" ? {} : { triggerId: autoscheduleFrom };
10119
+ if (preventSummaryTargetPullback) {
10120
+ options.preventSummaryTargetPullback = true;
10121
+ }
10097
10122
  const result = await deps.scheduler.schedule(options);
10098
10123
  const outcome = captureScheduledRows(deps.adapter, result.updatedIds);
10099
10124
  applyResults(deps.adapter, result);
@@ -11562,14 +11587,21 @@ async function createActivityCore(action, deps, opts = {}) {
11562
11587
  recomputeUsedCostCascadeForActivity(newId, adapter);
11563
11588
  recomputeCostCascadeForActivity(newId, adapter);
11564
11589
  }
11565
- recomputeAndFoldCorrelatives(
11590
+ const correlativeShifts = recomputeCorrelatives(
11566
11591
  adapter,
11567
11592
  newId,
11568
- beforeSnap,
11569
11593
  initialTouched,
11570
11594
  opts
11571
11595
  );
11572
- return { ok: true, newId, parent, newActivity, beforeSnap, initialTouched };
11596
+ return {
11597
+ ok: true,
11598
+ newId,
11599
+ parent,
11600
+ newActivity,
11601
+ beforeSnap,
11602
+ initialTouched,
11603
+ correlativeShifts
11604
+ };
11573
11605
  }
11574
11606
  function validateCreateTarget(action, adapter) {
11575
11607
  const isRoot = isRootParent(action.parentId);
@@ -11664,20 +11696,16 @@ function applyParentMutations(action, newId, parent, adapter, parentWasLeaf) {
11664
11696
  );
11665
11697
  }
11666
11698
  }
11667
- function recomputeAndFoldCorrelatives(adapter, newId, beforeSnap, initialTouched, opts) {
11668
- if (opts.skipCorrelativeRecompute) return;
11669
- const correlativeShifts = recomputeCorrelativeIds(adapter);
11670
- if (opts.skipBeforeSnap) return;
11671
- foldCorrelativeShifts(
11672
- adapter,
11673
- correlativeShifts,
11674
- /* @__PURE__ */ new Set([String(newId)]),
11675
- beforeSnap,
11676
- initialTouched
11699
+ function recomputeCorrelatives(adapter, newId, initialTouched, opts) {
11700
+ if (opts.skipCorrelativeRecompute) return [];
11701
+ const shifts = recomputeCorrelativeIds(adapter).filter(
11702
+ (shift) => shift.activityId !== String(newId)
11677
11703
  );
11704
+ for (const shift of shifts) initialTouched.add(shift.activityId);
11705
+ return shifts;
11678
11706
  }
11679
11707
  async function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, hoursPerDay) {
11680
- const { newId, newActivity, beforeSnap, initialTouched } = coreResult;
11708
+ const { newId, newActivity, beforeSnap, initialTouched, correlativeShifts } = coreResult;
11681
11709
  const trackingEvent = {
11682
11710
  name: DISPATCH_TRACK_EVENT.ACTIVITY_CREATION,
11683
11711
  properties: {
@@ -11694,6 +11722,7 @@ async function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, h
11694
11722
  beforeSnap,
11695
11723
  touchedIds: initialTouched,
11696
11724
  scheduledIds,
11725
+ correlativeShifts,
11697
11726
  trackingEvents: [trackingEvent]});
11698
11727
  }
11699
11728
  async function dispatchActivityCreate(action, options, deps) {
@@ -11722,8 +11751,9 @@ async function dispatchActivityCreate(action, options, deps) {
11722
11751
  },
11723
11752
  {
11724
11753
  action,
11725
- autoscheduleFrom: "roots",
11754
+ autoscheduleFrom: newId,
11726
11755
  recomputeParentsFrom: [newId],
11756
+ preventSummaryTargetPullback: true,
11727
11757
  now: deps.now,
11728
11758
  options
11729
11759
  }
@@ -21410,6 +21440,11 @@ var ScheduleCore = class _ScheduleCore {
21410
21440
  this.assertReady();
21411
21441
  return this.coreRuntime.state.hiddenIds().map(String);
21412
21442
  }
21443
+ getVisibleActivityIds() {
21444
+ this.assertReady();
21445
+ const hiddenIds = new Set(this.coreRuntime.state.hiddenIds().map(String));
21446
+ return this.coreRuntime.state.getAllIds().map(String).filter((activityId) => !hiddenIds.has(activityId));
21447
+ }
21413
21448
  getActiveFilter() {
21414
21449
  this.assertReady();
21415
21450
  return cloneDomainValue(this.coreRuntime.state.getActiveFilter());