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