@outbuild-company/schedule-core 1.14.2 → 1.15.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/cdn/schedule-core.global.js +1 -1
- package/dist/cdn/schedule-core.global.js.map +1 -1
- package/dist/index.cjs +911 -384
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -14
- package/dist/index.d.ts +63 -14
- package/dist/index.js +911 -384
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -341,6 +341,7 @@ var DISPATCH_ACTION_KIND = {
|
|
|
341
341
|
ACTIVITY_MOVE: "activity-move",
|
|
342
342
|
ACTIVITY_INDENT: "activity-indent",
|
|
343
343
|
ACTIVITY_OUTDENT: "activity-outdent",
|
|
344
|
+
ACTIVITY_TYPE_SET: "activity-type-set",
|
|
344
345
|
ACTIVITY_SET_PROGRESS: "activity-set-progress",
|
|
345
346
|
ACTIVITY_LOOKAHEAD_CONVERT: "activity-lookahead-convert",
|
|
346
347
|
SELECTION_TOGGLE: "selection-toggle",
|
|
@@ -349,7 +350,8 @@ var DISPATCH_ACTION_KIND = {
|
|
|
349
350
|
FILTER_SET: "filter-set",
|
|
350
351
|
SORT_SET: "sort-set",
|
|
351
352
|
SIR_SYNC: "sir-sync",
|
|
352
|
-
ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync"
|
|
353
|
+
ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync",
|
|
354
|
+
SCHEDULE_AUTO_SCHEDULE: "schedule-auto-schedule"
|
|
353
355
|
};
|
|
354
356
|
var ACTIVITY_BATCH_ACTION_KIND = "activity-batch";
|
|
355
357
|
var DISPATCH_LINK_OPERATION_KIND = LINK_OPERATION_KIND;
|
|
@@ -449,6 +451,8 @@ function batchLink(operation) {
|
|
|
449
451
|
return {
|
|
450
452
|
kind: DISPATCH_LINK_OPERATION_KIND.UPDATE,
|
|
451
453
|
linkId: operation.linkId,
|
|
454
|
+
...operation.sourceId === void 0 ? {} : { source: operation.sourceId },
|
|
455
|
+
...operation.targetId === void 0 ? {} : { target: operation.targetId },
|
|
452
456
|
...operation.relationship === void 0 ? {} : { type: linkType(operation.relationship) },
|
|
453
457
|
...operation.lagDays === void 0 ? {} : { lag: operation.lagDays }
|
|
454
458
|
};
|
|
@@ -505,6 +509,14 @@ function createActivitiesApi(engine) {
|
|
|
505
509
|
},
|
|
506
510
|
options
|
|
507
511
|
),
|
|
512
|
+
setType: (activityId, type, options) => engine.dispatch(
|
|
513
|
+
{
|
|
514
|
+
kind: DISPATCH_ACTION_KIND.ACTIVITY_TYPE_SET,
|
|
515
|
+
activityId,
|
|
516
|
+
type
|
|
517
|
+
},
|
|
518
|
+
options
|
|
519
|
+
),
|
|
508
520
|
applyDragBatch: (edits, options) => engine.dispatch(
|
|
509
521
|
{
|
|
510
522
|
kind: DISPATCH_ACTION_KIND.DATES_BATCH,
|
|
@@ -566,6 +578,13 @@ function createActivitiesApi(engine) {
|
|
|
566
578
|
...input,
|
|
567
579
|
kind: DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC
|
|
568
580
|
}),
|
|
581
|
+
autoSchedule: (activityId, options) => engine.dispatch(
|
|
582
|
+
{
|
|
583
|
+
kind: DISPATCH_ACTION_KIND.SCHEDULE_AUTO_SCHEDULE,
|
|
584
|
+
...activityId === void 0 ? {} : { activityId }
|
|
585
|
+
},
|
|
586
|
+
options
|
|
587
|
+
),
|
|
569
588
|
createDateGesturePreview: (gesture) => engine.createDateGesturePreview(gesture)
|
|
570
589
|
};
|
|
571
590
|
}
|
|
@@ -589,6 +608,8 @@ function createLinksApi(engine) {
|
|
|
589
608
|
{
|
|
590
609
|
kind: DISPATCH_ACTION_KIND.LINK_UPDATE,
|
|
591
610
|
linkId,
|
|
611
|
+
...patch.sourceId === void 0 ? {} : { source: patch.sourceId },
|
|
612
|
+
...patch.targetId === void 0 ? {} : { target: patch.targetId },
|
|
592
613
|
...patch.relationship === void 0 ? {} : { type: linkType(patch.relationship) },
|
|
593
614
|
...patch.lagDays === void 0 ? {} : { lag: patch.lagDays }
|
|
594
615
|
},
|
|
@@ -1902,6 +1923,87 @@ function assertNever(value, message) {
|
|
|
1902
1923
|
throw new Error(message);
|
|
1903
1924
|
}
|
|
1904
1925
|
|
|
1926
|
+
// src/activity-types/behavior.ts
|
|
1927
|
+
var PONDERATOR_WEIGHT = {
|
|
1928
|
+
OWN_BASELINE: "own-baseline",
|
|
1929
|
+
RECURSIVE_LEAF_SUM: "recursive-leaf-sum",
|
|
1930
|
+
ZERO: "zero"
|
|
1931
|
+
};
|
|
1932
|
+
var ACTIVITY_TYPE_BEHAVIOR = {
|
|
1933
|
+
[ACTIVITY_TYPE.PROJECT]: {
|
|
1934
|
+
alwaysSummary: true,
|
|
1935
|
+
holdsConstraint: false,
|
|
1936
|
+
carriesCustomId: false,
|
|
1937
|
+
forcedDuration: null,
|
|
1938
|
+
ponderatorWeight: PONDERATOR_WEIGHT.RECURSIVE_LEAF_SUM,
|
|
1939
|
+
promotesToProjectOnFirstChild: false
|
|
1940
|
+
},
|
|
1941
|
+
[ACTIVITY_TYPE.TASK]: {
|
|
1942
|
+
alwaysSummary: false,
|
|
1943
|
+
holdsConstraint: true,
|
|
1944
|
+
carriesCustomId: true,
|
|
1945
|
+
forcedDuration: null,
|
|
1946
|
+
ponderatorWeight: PONDERATOR_WEIGHT.OWN_BASELINE,
|
|
1947
|
+
promotesToProjectOnFirstChild: true
|
|
1948
|
+
},
|
|
1949
|
+
[ACTIVITY_TYPE.MILESTONE]: {
|
|
1950
|
+
alwaysSummary: false,
|
|
1951
|
+
holdsConstraint: true,
|
|
1952
|
+
carriesCustomId: true,
|
|
1953
|
+
forcedDuration: 0,
|
|
1954
|
+
ponderatorWeight: PONDERATOR_WEIGHT.ZERO,
|
|
1955
|
+
promotesToProjectOnFirstChild: true
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1958
|
+
|
|
1959
|
+
// src/activity-types/predicates.ts
|
|
1960
|
+
function isSummary(type, hasChildren) {
|
|
1961
|
+
return hasChildren || isActivityType(type) && ACTIVITY_TYPE_BEHAVIOR[type].alwaysSummary;
|
|
1962
|
+
}
|
|
1963
|
+
function isMilestoneType(type) {
|
|
1964
|
+
return type === ACTIVITY_TYPE.MILESTONE;
|
|
1965
|
+
}
|
|
1966
|
+
function isPromotableLeafType(type) {
|
|
1967
|
+
return isActivityType(type) && ACTIVITY_TYPE_BEHAVIOR[type].promotesToProjectOnFirstChild;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
// src/activity-types/transitions.ts
|
|
1971
|
+
var PROMOTION_TARGET_TYPE = ACTIVITY_TYPE.PROJECT;
|
|
1972
|
+
var DEMOTION_TARGET_TYPE = ACTIVITY_TYPE.TASK;
|
|
1973
|
+
function typeForNewDuration(currentDuration, newDuration) {
|
|
1974
|
+
if (newDuration === 0) return ACTIVITY_TYPE.MILESTONE;
|
|
1975
|
+
if (currentDuration === 0 && newDuration > 0) return ACTIVITY_TYPE.TASK;
|
|
1976
|
+
return null;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
// src/constants/activity-properties.ts
|
|
1980
|
+
var ACTIVITY_PROPERTY = {
|
|
1981
|
+
PARENT: "parentId",
|
|
1982
|
+
TYPE: "type",
|
|
1983
|
+
START_DATE: "startDate",
|
|
1984
|
+
END_DATE: "endDate",
|
|
1985
|
+
DURATION_HOURS: "durationHours",
|
|
1986
|
+
CONSTRAINT_TYPE: "constraintType",
|
|
1987
|
+
CONSTRAINT_DATE: "constraintDate",
|
|
1988
|
+
PROGRESS: "progress",
|
|
1989
|
+
COST: "cost",
|
|
1990
|
+
USED_COST: "usedCost",
|
|
1991
|
+
REAL_COST: "realCost",
|
|
1992
|
+
WORK_HOURS: "workHours",
|
|
1993
|
+
REAL_WORK_HOURS: "realWorkHours",
|
|
1994
|
+
CORRELATIVE_ID: "correlativeId",
|
|
1995
|
+
CUSTOM_ID: "customId",
|
|
1996
|
+
NEW_ACTIVITIES_ARRAY: "newActivityIds",
|
|
1997
|
+
HAS_NEW_ACTIVITIES: "hasNewActivities",
|
|
1998
|
+
CALENDAR_DURATION: "calendarDuration"};
|
|
1999
|
+
var LINK_PROPERTY = {
|
|
2000
|
+
SOURCE: "source",
|
|
2001
|
+
TARGET: "target",
|
|
2002
|
+
TYPE: "type",
|
|
2003
|
+
LAG: "lag",
|
|
2004
|
+
FREE_SLACK_HOURS: "freeSlackHours",
|
|
2005
|
+
IS_CRITICAL: "isCritical"};
|
|
2006
|
+
|
|
1905
2007
|
// src/autoscheduler/utils/cancellation.ts
|
|
1906
2008
|
function createIsCurrent(id, getCurrentId) {
|
|
1907
2009
|
return () => getCurrentId() === id;
|
|
@@ -2012,59 +2114,6 @@ function filterTopoOrder(order, scopedIds) {
|
|
|
2012
2114
|
return filtered;
|
|
2013
2115
|
}
|
|
2014
2116
|
|
|
2015
|
-
// src/activity-types/behavior.ts
|
|
2016
|
-
var PONDERATOR_WEIGHT = {
|
|
2017
|
-
OWN_BASELINE: "own-baseline",
|
|
2018
|
-
RECURSIVE_LEAF_SUM: "recursive-leaf-sum",
|
|
2019
|
-
ZERO: "zero"
|
|
2020
|
-
};
|
|
2021
|
-
var ACTIVITY_TYPE_BEHAVIOR = {
|
|
2022
|
-
[ACTIVITY_TYPE.PROJECT]: {
|
|
2023
|
-
alwaysSummary: true,
|
|
2024
|
-
holdsConstraint: false,
|
|
2025
|
-
carriesCustomId: false,
|
|
2026
|
-
forcedDuration: null,
|
|
2027
|
-
ponderatorWeight: PONDERATOR_WEIGHT.RECURSIVE_LEAF_SUM,
|
|
2028
|
-
promotesToProjectOnFirstChild: false
|
|
2029
|
-
},
|
|
2030
|
-
[ACTIVITY_TYPE.TASK]: {
|
|
2031
|
-
alwaysSummary: false,
|
|
2032
|
-
holdsConstraint: true,
|
|
2033
|
-
carriesCustomId: true,
|
|
2034
|
-
forcedDuration: null,
|
|
2035
|
-
ponderatorWeight: PONDERATOR_WEIGHT.OWN_BASELINE,
|
|
2036
|
-
promotesToProjectOnFirstChild: true
|
|
2037
|
-
},
|
|
2038
|
-
[ACTIVITY_TYPE.MILESTONE]: {
|
|
2039
|
-
alwaysSummary: false,
|
|
2040
|
-
holdsConstraint: true,
|
|
2041
|
-
carriesCustomId: true,
|
|
2042
|
-
forcedDuration: 0,
|
|
2043
|
-
ponderatorWeight: PONDERATOR_WEIGHT.ZERO,
|
|
2044
|
-
promotesToProjectOnFirstChild: true
|
|
2045
|
-
}
|
|
2046
|
-
};
|
|
2047
|
-
|
|
2048
|
-
// src/activity-types/predicates.ts
|
|
2049
|
-
function isSummary(type, hasChildren) {
|
|
2050
|
-
return hasChildren || isActivityType(type) && ACTIVITY_TYPE_BEHAVIOR[type].alwaysSummary;
|
|
2051
|
-
}
|
|
2052
|
-
function isMilestoneType(type) {
|
|
2053
|
-
return type === ACTIVITY_TYPE.MILESTONE;
|
|
2054
|
-
}
|
|
2055
|
-
function isPromotableLeafType(type) {
|
|
2056
|
-
return isActivityType(type) && ACTIVITY_TYPE_BEHAVIOR[type].promotesToProjectOnFirstChild;
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2059
|
-
// src/activity-types/transitions.ts
|
|
2060
|
-
var PROMOTION_TARGET_TYPE = ACTIVITY_TYPE.PROJECT;
|
|
2061
|
-
var DEMOTION_TARGET_TYPE = ACTIVITY_TYPE.TASK;
|
|
2062
|
-
function typeForNewDuration(currentDuration, newDuration) {
|
|
2063
|
-
if (newDuration === 0) return ACTIVITY_TYPE.MILESTONE;
|
|
2064
|
-
if (currentDuration === 0 && newDuration > 0) return ACTIVITY_TYPE.TASK;
|
|
2065
|
-
return null;
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
2117
|
// src/autoscheduler/utils/parent-link-expansion.ts
|
|
2069
2118
|
function isSummary2(id, adapter) {
|
|
2070
2119
|
const activity = adapter.getActivity(id);
|
|
@@ -3538,6 +3587,53 @@ function applyLinkUpdate(op, port) {
|
|
|
3538
3587
|
if (!link) {
|
|
3539
3588
|
return { applied: false, rejected: LINK_REJECTION_REASON.LINK_MISSING };
|
|
3540
3589
|
}
|
|
3590
|
+
const proposed = {
|
|
3591
|
+
...link,
|
|
3592
|
+
source: op.source ?? link.source,
|
|
3593
|
+
target: op.target ?? link.target,
|
|
3594
|
+
type: op.type ?? link.type,
|
|
3595
|
+
lag: op.lag ?? link.lag
|
|
3596
|
+
};
|
|
3597
|
+
if (String(proposed.source) === String(proposed.target)) {
|
|
3598
|
+
return { applied: false, rejected: LINK_REJECTION_REASON.SELF_LINK };
|
|
3599
|
+
}
|
|
3600
|
+
if (!port.getActivity(proposed.source) || !port.getActivity(proposed.target)) {
|
|
3601
|
+
return {
|
|
3602
|
+
applied: false,
|
|
3603
|
+
rejected: LINK_REJECTION_REASON.ACTIVITY_MISSING
|
|
3604
|
+
};
|
|
3605
|
+
}
|
|
3606
|
+
if (isSummaryHierarchyLink(proposed.source, proposed.target, port)) {
|
|
3607
|
+
return {
|
|
3608
|
+
applied: false,
|
|
3609
|
+
rejected: LINK_REJECTION_REASON.HIERARCHY_LINK
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
const currentLinks = port.getAllLinks();
|
|
3613
|
+
const duplicate = currentLinks.find(
|
|
3614
|
+
(candidate) => String(candidate.id) !== String(op.linkId) && linkKey(candidate) === linkKey(proposed)
|
|
3615
|
+
);
|
|
3616
|
+
if (duplicate) {
|
|
3617
|
+
return {
|
|
3618
|
+
applied: false,
|
|
3619
|
+
rejected: LINK_REJECTION_REASON.DUPLICATE,
|
|
3620
|
+
linkId: duplicate.id
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
const proposedLinks = currentLinks.map(
|
|
3624
|
+
(candidate) => String(candidate.id) === String(op.linkId) ? proposed : candidate
|
|
3625
|
+
);
|
|
3626
|
+
const effectiveVerdict = validateTopologyCommit({
|
|
3627
|
+
port,
|
|
3628
|
+
currentLinks,
|
|
3629
|
+
proposedLinks
|
|
3630
|
+
});
|
|
3631
|
+
if (!effectiveVerdict.ok) {
|
|
3632
|
+
return { applied: false, rejected: LINK_REJECTION_REASON.CYCLE };
|
|
3633
|
+
}
|
|
3634
|
+
if (String(link.source) !== String(proposed.source) || String(link.target) !== String(proposed.target)) {
|
|
3635
|
+
port.setLinkEndpoints(op.linkId, proposed.source, proposed.target);
|
|
3636
|
+
}
|
|
3541
3637
|
if (op.lag !== void 0) port.setLinkField(op.linkId, "lag", op.lag);
|
|
3542
3638
|
if (op.type !== void 0) port.setLinkField(op.linkId, "type", op.type);
|
|
3543
3639
|
return { applied: true, linkId: op.linkId };
|
|
@@ -4020,10 +4116,7 @@ function adjustLinkLagOnTaskMove(targetId, adapter, preEditTarget) {
|
|
|
4020
4116
|
calendarApi
|
|
4021
4117
|
);
|
|
4022
4118
|
if (newLag === link.lag) continue;
|
|
4023
|
-
|
|
4024
|
-
{ kind: LINK_OPERATION_KIND.UPDATE, linkId: link.id, lag: newLag },
|
|
4025
|
-
adapter
|
|
4026
|
-
);
|
|
4119
|
+
adapter.setLinkField(link.id, LINK_PROPERTY.LAG, newLag);
|
|
4027
4120
|
}
|
|
4028
4121
|
}
|
|
4029
4122
|
|
|
@@ -5587,6 +5680,7 @@ var CONSTRAINT_WARNING_KIND = {
|
|
|
5587
5680
|
VIOLATION: "constraint_violation"
|
|
5588
5681
|
};
|
|
5589
5682
|
var CHANGE_SET_SOURCE_KIND = {
|
|
5683
|
+
DISPATCH_MANY: "dispatch-many",
|
|
5590
5684
|
INIT: "init",
|
|
5591
5685
|
UNDO: "undo",
|
|
5592
5686
|
REDO: "redo"
|
|
@@ -5636,32 +5730,6 @@ var COLUMN_WRITABLE_FIELDS = new Set(
|
|
|
5636
5730
|
COLUMN_WRITABLE_FIELD_LIST
|
|
5637
5731
|
);
|
|
5638
5732
|
|
|
5639
|
-
// src/constants/activity-properties.ts
|
|
5640
|
-
var ACTIVITY_PROPERTY = {
|
|
5641
|
-
PARENT: "parentId",
|
|
5642
|
-
TYPE: "type",
|
|
5643
|
-
START_DATE: "startDate",
|
|
5644
|
-
END_DATE: "endDate",
|
|
5645
|
-
DURATION_HOURS: "durationHours",
|
|
5646
|
-
CONSTRAINT_TYPE: "constraintType",
|
|
5647
|
-
CONSTRAINT_DATE: "constraintDate",
|
|
5648
|
-
PROGRESS: "progress",
|
|
5649
|
-
COST: "cost",
|
|
5650
|
-
USED_COST: "usedCost",
|
|
5651
|
-
REAL_COST: "realCost",
|
|
5652
|
-
WORK_HOURS: "workHours",
|
|
5653
|
-
REAL_WORK_HOURS: "realWorkHours",
|
|
5654
|
-
CORRELATIVE_ID: "correlativeId",
|
|
5655
|
-
CUSTOM_ID: "customId",
|
|
5656
|
-
NEW_ACTIVITIES_ARRAY: "newActivityIds",
|
|
5657
|
-
HAS_NEW_ACTIVITIES: "hasNewActivities",
|
|
5658
|
-
CALENDAR_DURATION: "calendarDuration"};
|
|
5659
|
-
var LINK_PROPERTY = {
|
|
5660
|
-
TYPE: "type",
|
|
5661
|
-
LAG: "lag",
|
|
5662
|
-
FREE_SLACK_HOURS: "freeSlackHours",
|
|
5663
|
-
IS_CRITICAL: "isCritical"};
|
|
5664
|
-
|
|
5665
5733
|
// src/internal/state/dynamic-writes.ts
|
|
5666
5734
|
function setActivityFieldDynamic(state, activityId, field, value) {
|
|
5667
5735
|
state.setActivityFieldDynamic(activityId, field, value);
|
|
@@ -10916,48 +10984,267 @@ function cloneLink(link) {
|
|
|
10916
10984
|
return { ...link };
|
|
10917
10985
|
}
|
|
10918
10986
|
|
|
10919
|
-
// src/dispatch/
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
const
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
passes,
|
|
10937
|
-
progressInvalidation
|
|
10938
|
-
);
|
|
10939
|
-
await runDerivedRecomputes(deps, passes, args.now);
|
|
10940
|
-
return schedule;
|
|
10941
|
-
}
|
|
10942
|
-
function resolvePasses(args, progressInvalidation) {
|
|
10943
|
-
const requested = args.autoscheduleFrom !== null;
|
|
10944
|
-
const ran = !args.options.skipAutoSchedule && requested;
|
|
10945
|
-
const passes = new Set(
|
|
10946
|
-
resolveDerivedPasses(args.action, { ran }, args.options)
|
|
10947
|
-
);
|
|
10948
|
-
if (args.recomputeParentProgress || progressInvalidation.affectedParents.size > 0) {
|
|
10949
|
-
passes.add(DERIVED_PASS.PARENT_PROGRESS);
|
|
10987
|
+
// src/dispatch/sir-auto-reject.ts
|
|
10988
|
+
function detectSirAutoReject(beforeSnap, adapter, touchedIds) {
|
|
10989
|
+
const effects = [];
|
|
10990
|
+
for (const id of touchedIds) {
|
|
10991
|
+
const before = beforeSnap.get(id);
|
|
10992
|
+
if (!before) continue;
|
|
10993
|
+
const pendingRequestIds = before.pendingRequestIds ?? [];
|
|
10994
|
+
if (!isFrozen({ pendingRequestIds })) continue;
|
|
10995
|
+
if (adapter.getActivity(id) != null) continue;
|
|
10996
|
+
for (const pending of pendingRequestIds) {
|
|
10997
|
+
effects.push({
|
|
10998
|
+
kind: SCHEDULE_EFFECT_KIND.SIR_AUTO_REJECT,
|
|
10999
|
+
activityId: id,
|
|
11000
|
+
sirId: pending,
|
|
11001
|
+
reason: SIR_AUTO_REJECT_REASON.ACTIVITY_DELETED
|
|
11002
|
+
});
|
|
11003
|
+
}
|
|
10950
11004
|
}
|
|
10951
|
-
return
|
|
11005
|
+
return effects;
|
|
10952
11006
|
}
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
const
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
11007
|
+
|
|
11008
|
+
// src/dispatch/conversion.ts
|
|
11009
|
+
function snapshotToLink(l) {
|
|
11010
|
+
const view = {
|
|
11011
|
+
id: String(l.id),
|
|
11012
|
+
source: String(l.source),
|
|
11013
|
+
target: String(l.target),
|
|
11014
|
+
type: l.type,
|
|
11015
|
+
lag: Number(l.lag),
|
|
11016
|
+
freeSlackHours: typeof l.freeSlackHours === "number" && Number.isFinite(l.freeSlackHours) ? l.freeSlackHours : null,
|
|
11017
|
+
isCritical: l.isCritical === true
|
|
11018
|
+
};
|
|
11019
|
+
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
11020
|
+
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
11021
|
+
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
11022
|
+
return view;
|
|
11023
|
+
}
|
|
11024
|
+
|
|
11025
|
+
// src/dispatch/shared/change-set.ts
|
|
11026
|
+
var SIR_DETECTION_PHASE = {
|
|
11027
|
+
BEFORE_DIFF: "before-diff",
|
|
11028
|
+
AFTER_DIFF: "after-diff"
|
|
11029
|
+
};
|
|
11030
|
+
async function assembleChangeSet(adapter, args) {
|
|
11031
|
+
const allTouched = collectChangeSetIds(adapter, args);
|
|
11032
|
+
const before = new Map(args.beforeSnap);
|
|
11033
|
+
const captured = adapter.peekWriteCapture();
|
|
11034
|
+
if (captured) {
|
|
11035
|
+
const insertedIds = collectInsertedIds(captured);
|
|
11036
|
+
mergeDirtyBeforeImages(adapter, captured, insertedIds, before, allTouched);
|
|
11037
|
+
}
|
|
11038
|
+
const sirDetection = args.sirDetection;
|
|
11039
|
+
const beforeDiffEffects = sirDetection === SIR_DETECTION_PHASE.BEFORE_DIFF ? detectSirAutoReject(before, adapter, allTouched) : [];
|
|
11040
|
+
const correlativeBefore = buildCorrelativeBeforeMap(
|
|
11041
|
+
args.correlativeShifts,
|
|
11042
|
+
allTouched
|
|
11043
|
+
);
|
|
11044
|
+
const activityChanges = await buildActivityChanges(
|
|
11045
|
+
adapter,
|
|
11046
|
+
before,
|
|
11047
|
+
allTouched,
|
|
11048
|
+
correlativeBefore
|
|
11049
|
+
);
|
|
11050
|
+
const effects = sirDetection === SIR_DETECTION_PHASE.AFTER_DIFF ? detectSirAutoReject(before, adapter, allTouched) : beforeDiffEffects;
|
|
11051
|
+
const warnings = args.warnings ?? [];
|
|
11052
|
+
const linkChanges = mergeLinkChanges(
|
|
11053
|
+
args.links ?? [],
|
|
11054
|
+
captured ? buildCapturedCriticalPathLinkChanges(adapter, captured) : []
|
|
11055
|
+
);
|
|
11056
|
+
return {
|
|
11057
|
+
source: args.source,
|
|
11058
|
+
activities: activityChanges,
|
|
11059
|
+
links: linkChanges,
|
|
11060
|
+
calendars: [],
|
|
11061
|
+
trackingEvents: args.trackingEvents ?? [],
|
|
11062
|
+
...effects.length > 0 ? { effects } : {},
|
|
11063
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
11064
|
+
};
|
|
11065
|
+
}
|
|
11066
|
+
function buildCapturedCriticalPathLinkChanges(adapter, captured) {
|
|
11067
|
+
const changes = [];
|
|
11068
|
+
for (const operation of captured.ops) {
|
|
11069
|
+
if (operation.kind !== WRITE_OPERATION_KIND.LINK_FIELD) continue;
|
|
11070
|
+
const after = adapter.getLink(operation.linkId);
|
|
11071
|
+
if (!after) continue;
|
|
11072
|
+
const fields = {};
|
|
11073
|
+
for (const field of ["freeSlackHours", "isCritical"]) {
|
|
11074
|
+
if (!Object.is(operation.before[field], after[field])) {
|
|
11075
|
+
fields[field] = {
|
|
11076
|
+
before: operation.before[field],
|
|
11077
|
+
after: after[field]
|
|
11078
|
+
};
|
|
11079
|
+
}
|
|
11080
|
+
}
|
|
11081
|
+
if (Object.keys(fields).length === 0) continue;
|
|
11082
|
+
changes.push({
|
|
11083
|
+
id: operation.linkId,
|
|
11084
|
+
kind: ENTITY_CHANGE_KIND.UPDATED,
|
|
11085
|
+
fields,
|
|
11086
|
+
after: snapshotToLink(after)
|
|
11087
|
+
});
|
|
11088
|
+
}
|
|
11089
|
+
return changes;
|
|
11090
|
+
}
|
|
11091
|
+
function mergeLinkChanges(explicit, captured) {
|
|
11092
|
+
const merged = [];
|
|
11093
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
11094
|
+
for (const change of explicit) {
|
|
11095
|
+
const index = indexById.get(change.id);
|
|
11096
|
+
if (index === void 0) {
|
|
11097
|
+
indexById.set(change.id, merged.length);
|
|
11098
|
+
merged.push(change);
|
|
11099
|
+
continue;
|
|
11100
|
+
}
|
|
11101
|
+
const current = merged[index];
|
|
11102
|
+
if ((current.kind === ENTITY_CHANGE_KIND.UPDATED || current.kind === ENTITY_CHANGE_KIND.CREATED) && change.kind === ENTITY_CHANGE_KIND.UPDATED) {
|
|
11103
|
+
merged[index] = {
|
|
11104
|
+
...current,
|
|
11105
|
+
fields: { ...current.fields, ...change.fields },
|
|
11106
|
+
after: change.after
|
|
11107
|
+
};
|
|
11108
|
+
continue;
|
|
11109
|
+
}
|
|
11110
|
+
merged[index] = change;
|
|
11111
|
+
}
|
|
11112
|
+
for (const change of captured) {
|
|
11113
|
+
const index = indexById.get(change.id);
|
|
11114
|
+
if (index === void 0) {
|
|
11115
|
+
indexById.set(change.id, merged.length);
|
|
11116
|
+
merged.push(change);
|
|
11117
|
+
continue;
|
|
11118
|
+
}
|
|
11119
|
+
const current = merged[index];
|
|
11120
|
+
if (current.kind === ENTITY_CHANGE_KIND.DELETED) continue;
|
|
11121
|
+
const capturedFields = current.kind === ENTITY_CHANGE_KIND.CREATED ? Object.fromEntries(
|
|
11122
|
+
Object.entries(change.fields ?? {}).map(([field, value]) => [
|
|
11123
|
+
field,
|
|
11124
|
+
{ before: void 0, after: value.after }
|
|
11125
|
+
])
|
|
11126
|
+
) : change.fields;
|
|
11127
|
+
merged[index] = {
|
|
11128
|
+
...current,
|
|
11129
|
+
fields: { ...current.fields, ...capturedFields },
|
|
11130
|
+
after: change.after
|
|
11131
|
+
};
|
|
11132
|
+
}
|
|
11133
|
+
return merged;
|
|
11134
|
+
}
|
|
11135
|
+
function buildCorrelativeBeforeMap(shifts, touched) {
|
|
11136
|
+
if (!shifts || shifts.length === 0) return void 0;
|
|
11137
|
+
const correlativeBefore = /* @__PURE__ */ new Map();
|
|
11138
|
+
for (const shift of shifts) {
|
|
11139
|
+
correlativeBefore.set(shift.activityId, shift.before);
|
|
11140
|
+
touched.add(shift.activityId);
|
|
11141
|
+
}
|
|
11142
|
+
return correlativeBefore;
|
|
11143
|
+
}
|
|
11144
|
+
function collectChangeSetIds(adapter, args) {
|
|
11145
|
+
const ids = /* @__PURE__ */ new Set();
|
|
11146
|
+
for (const id of args.touchedIds) ids.add(id);
|
|
11147
|
+
for (const id of args.scheduledIds) ids.add(id);
|
|
11148
|
+
if (args.includeAllIds) {
|
|
11149
|
+
for (const id of adapter.getAllIds()) ids.add(id);
|
|
11150
|
+
}
|
|
11151
|
+
return ids;
|
|
11152
|
+
}
|
|
11153
|
+
function mergeDirtyBeforeImages(adapter, captured, insertedIds, before, touched) {
|
|
11154
|
+
for (const id of captured.dirty) {
|
|
11155
|
+
if (insertedIds.has(id) || before.has(id)) continue;
|
|
11156
|
+
const beforeImage = captured.before.get(id);
|
|
11157
|
+
const live = adapter.getActivity(id);
|
|
11158
|
+
if (!beforeImage || !live) continue;
|
|
11159
|
+
if (Object.keys(diffActivity(beforeImage, live)).length === 0) continue;
|
|
11160
|
+
before.set(id, beforeImage);
|
|
11161
|
+
touched.add(id);
|
|
11162
|
+
}
|
|
11163
|
+
}
|
|
11164
|
+
function collectInsertedIds(captured) {
|
|
11165
|
+
return new Set(
|
|
11166
|
+
captured.ops.filter((operation) => operation.kind === WRITE_OPERATION_KIND.INSERT).map((operation) => operation.activityId)
|
|
11167
|
+
);
|
|
11168
|
+
}
|
|
11169
|
+
|
|
11170
|
+
// src/dispatch/shared/post-mutation.ts
|
|
11171
|
+
var AUTOSCHEDULE_SCOPE = { ROOTS: "roots" };
|
|
11172
|
+
async function runPostMutation(deps, args) {
|
|
11173
|
+
const progressInvalidation = resolveProgressInvalidation(deps.adapter);
|
|
11174
|
+
const passes = resolvePasses(args, progressInvalidation);
|
|
11175
|
+
if (passes.has(DERIVED_PASS.AUTOSCHEDULE)) {
|
|
11176
|
+
await refreshMutatedParentBounds(deps.adapter, args);
|
|
11177
|
+
}
|
|
11178
|
+
const schedule = passes.has(DERIVED_PASS.AUTOSCHEDULE) ? await runAutoschedule(
|
|
11179
|
+
deps,
|
|
11180
|
+
args.autoscheduleFrom,
|
|
11181
|
+
args.preventSummaryTargetPullback
|
|
11182
|
+
) : emptySchedule();
|
|
11183
|
+
await rollUpParentBounds(
|
|
11184
|
+
deps.adapter,
|
|
11185
|
+
args,
|
|
11186
|
+
schedule.scheduledIds,
|
|
11187
|
+
passes,
|
|
11188
|
+
progressInvalidation
|
|
11189
|
+
);
|
|
11190
|
+
await runDerivedRecomputes(deps, passes, args.now);
|
|
11191
|
+
return schedule;
|
|
11192
|
+
}
|
|
11193
|
+
async function dispatchScheduleAutoSchedule(action, options, deps) {
|
|
11194
|
+
if (action.activityId !== void 0 && !deps.adapter.activityExists(action.activityId)) {
|
|
11195
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
11196
|
+
}
|
|
11197
|
+
const beforeSnap = await snapshotActivities(
|
|
11198
|
+
deps.adapter,
|
|
11199
|
+
new Set(deps.adapter.getAllIds())
|
|
11200
|
+
);
|
|
11201
|
+
deps.scheduler.invalidateAllCaches();
|
|
11202
|
+
const recomputeParentsFrom = action.activityId === void 0 ? deps.adapter.getRootIds() : [action.activityId];
|
|
11203
|
+
const { scheduledIds } = await runPostMutation(
|
|
11204
|
+
{
|
|
11205
|
+
adapter: deps.adapter,
|
|
11206
|
+
scheduler: deps.scheduler,
|
|
11207
|
+
sector: deps.sector,
|
|
11208
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
11209
|
+
},
|
|
11210
|
+
{
|
|
11211
|
+
action,
|
|
11212
|
+
autoscheduleFrom: action.activityId ?? AUTOSCHEDULE_SCOPE.ROOTS,
|
|
11213
|
+
recomputeParentsFrom,
|
|
11214
|
+
now: deps.now,
|
|
11215
|
+
options
|
|
11216
|
+
}
|
|
11217
|
+
);
|
|
11218
|
+
return {
|
|
11219
|
+
ok: true,
|
|
11220
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
11221
|
+
source: action,
|
|
11222
|
+
beforeSnap,
|
|
11223
|
+
touchedIds: recomputeParentsFrom,
|
|
11224
|
+
scheduledIds,
|
|
11225
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
11226
|
+
})
|
|
11227
|
+
};
|
|
11228
|
+
}
|
|
11229
|
+
function resolvePasses(args, progressInvalidation) {
|
|
11230
|
+
const requested = args.autoscheduleFrom !== null;
|
|
11231
|
+
const ran = !args.options.skipAutoSchedule && requested;
|
|
11232
|
+
const passes = new Set(
|
|
11233
|
+
resolveDerivedPasses(args.action, { ran }, args.options)
|
|
11234
|
+
);
|
|
11235
|
+
if (args.recomputeParentProgress || progressInvalidation.affectedParents.size > 0) {
|
|
11236
|
+
passes.add(DERIVED_PASS.PARENT_PROGRESS);
|
|
11237
|
+
}
|
|
11238
|
+
return passes;
|
|
11239
|
+
}
|
|
11240
|
+
function resolveProgressInvalidation(adapter) {
|
|
11241
|
+
const affectedParents = /* @__PURE__ */ new Set();
|
|
11242
|
+
const dirtySeeds = /* @__PURE__ */ new Set();
|
|
11243
|
+
const journal = adapter.peekWriteCapture();
|
|
11244
|
+
if (!journal) return { affectedParents, dirtySeeds };
|
|
11245
|
+
const affectParent = (parentId) => {
|
|
11246
|
+
if (parentId === null || isRootParent(parentId)) return;
|
|
11247
|
+
const normalized2 = String(parentId);
|
|
10961
11248
|
affectedParents.add(normalized2);
|
|
10962
11249
|
const childId = adapter.getChildren(normalized2)[0];
|
|
10963
11250
|
if (childId !== void 0) dirtySeeds.add(childId);
|
|
@@ -11006,8 +11293,27 @@ async function runAutoschedule(deps, autoscheduleFrom, preventSummaryTargetPullb
|
|
|
11006
11293
|
}
|
|
11007
11294
|
const result = await deps.scheduler.schedule(options);
|
|
11008
11295
|
const outcome = captureScheduledRows(deps.adapter, result.updatedIds);
|
|
11296
|
+
const warnings = collectConstraintWarnings(deps.adapter, result);
|
|
11009
11297
|
applyResults(deps.adapter, result);
|
|
11010
|
-
return outcome;
|
|
11298
|
+
return { ...outcome, warnings };
|
|
11299
|
+
}
|
|
11300
|
+
function collectConstraintWarnings(adapter, result) {
|
|
11301
|
+
const warnings = [];
|
|
11302
|
+
for (const plan of result.plans.values()) {
|
|
11303
|
+
const projectedDate = plan.earliestSchedulingStart;
|
|
11304
|
+
if (!projectedDate || plan.startDate >= projectedDate) continue;
|
|
11305
|
+
const activity = adapter.getActivity(plan.activityId);
|
|
11306
|
+
if (!activity?.constraintType || !activity.constraintDate) continue;
|
|
11307
|
+
warnings.push({
|
|
11308
|
+
kind: CONSTRAINT_WARNING_KIND.VIOLATION,
|
|
11309
|
+
activityId: activity.id,
|
|
11310
|
+
constraintType: activity.constraintType,
|
|
11311
|
+
constraintDate: activity.constraintDate,
|
|
11312
|
+
projectedDate,
|
|
11313
|
+
messageKey: CONSTRAINT_WARNING_KIND.VIOLATION
|
|
11314
|
+
});
|
|
11315
|
+
}
|
|
11316
|
+
return warnings;
|
|
11011
11317
|
}
|
|
11012
11318
|
function captureScheduledRows(adapter, updatedIds) {
|
|
11013
11319
|
const scheduledIds = [];
|
|
@@ -11019,10 +11325,10 @@ function captureScheduledRows(adapter, updatedIds) {
|
|
|
11019
11325
|
adapter.noteScheduledBefore(activityId, snap);
|
|
11020
11326
|
}
|
|
11021
11327
|
}
|
|
11022
|
-
return { scheduledIds };
|
|
11328
|
+
return { scheduledIds, warnings: [] };
|
|
11023
11329
|
}
|
|
11024
11330
|
function emptySchedule() {
|
|
11025
|
-
return { scheduledIds: [] };
|
|
11331
|
+
return { scheduledIds: [], warnings: [] };
|
|
11026
11332
|
}
|
|
11027
11333
|
async function refreshMutatedParentBounds(adapter, args) {
|
|
11028
11334
|
const mutated = args.collectDirty ? args.collectDirty([]) : collectDirtyForStructural(args.recomputeParentsFrom, []);
|
|
@@ -11132,241 +11438,58 @@ function asDate(value) {
|
|
|
11132
11438
|
if (value instanceof Date) return value;
|
|
11133
11439
|
if (typeof value === "string" || typeof value === "number") {
|
|
11134
11440
|
const parsed2 = new Date(value);
|
|
11135
|
-
return Number.isNaN(parsed2.getTime()) ? null : parsed2;
|
|
11136
|
-
}
|
|
11137
|
-
return null;
|
|
11138
|
-
}
|
|
11139
|
-
function sameDate(first, second) {
|
|
11140
|
-
if (first === null && second === null) return true;
|
|
11141
|
-
if (first === null || second === null) return false;
|
|
11142
|
-
return first.getTime() === second.getTime();
|
|
11143
|
-
}
|
|
11144
|
-
|
|
11145
|
-
// src/dispatch/shared/constraint-settle.ts
|
|
11146
|
-
async function settleConstraintEdits(deps, { postMutationArgs, revertTargets, revert }) {
|
|
11147
|
-
let { scheduledIds } = await runPostMutation(
|
|
11148
|
-
deps,
|
|
11149
|
-
withCriticalPathSuppressed(postMutationArgs)
|
|
11150
|
-
);
|
|
11151
|
-
let someEditReverted = false;
|
|
11152
|
-
for (const { activityId, priorConstraintDate } of revertTargets) {
|
|
11153
|
-
const reverted = revert(activityId, priorConstraintDate);
|
|
11154
|
-
someEditReverted = someEditReverted || reverted;
|
|
11155
|
-
}
|
|
11156
|
-
if (someEditReverted) {
|
|
11157
|
-
const resettled = await runPostMutation(
|
|
11158
|
-
deps,
|
|
11159
|
-
withCriticalPathSuppressed(postMutationArgs)
|
|
11160
|
-
);
|
|
11161
|
-
scheduledIds = mergeScheduledIds(scheduledIds, resettled.scheduledIds);
|
|
11162
|
-
}
|
|
11163
|
-
if (resolveRunCriticalPath(postMutationArgs.action)) {
|
|
11164
|
-
await runPostMutation(deps, criticalPathOnlyArgs(postMutationArgs));
|
|
11165
|
-
}
|
|
11166
|
-
return { scheduledIds };
|
|
11167
|
-
}
|
|
11168
|
-
function withCriticalPathSuppressed(args) {
|
|
11169
|
-
return {
|
|
11170
|
-
...args,
|
|
11171
|
-
options: { ...args.options, skipCriticalPath: true }
|
|
11172
|
-
};
|
|
11173
|
-
}
|
|
11174
|
-
function criticalPathOnlyArgs(args) {
|
|
11175
|
-
return {
|
|
11176
|
-
action: args.action,
|
|
11177
|
-
autoscheduleFrom: null,
|
|
11178
|
-
recomputeParentsFrom: [],
|
|
11179
|
-
now: null,
|
|
11180
|
-
options: args.options
|
|
11181
|
-
};
|
|
11182
|
-
}
|
|
11183
|
-
function mergeScheduledIds(firstSettleIds, secondSettleIds) {
|
|
11184
|
-
const merged = new Set(firstSettleIds);
|
|
11185
|
-
for (const scheduledId of secondSettleIds) merged.add(scheduledId);
|
|
11186
|
-
return [...merged];
|
|
11187
|
-
}
|
|
11188
|
-
|
|
11189
|
-
// src/dispatch/sir-auto-reject.ts
|
|
11190
|
-
function detectSirAutoReject(beforeSnap, adapter, touchedIds) {
|
|
11191
|
-
const effects = [];
|
|
11192
|
-
for (const id of touchedIds) {
|
|
11193
|
-
const before = beforeSnap.get(id);
|
|
11194
|
-
if (!before) continue;
|
|
11195
|
-
const pendingRequestIds = before.pendingRequestIds ?? [];
|
|
11196
|
-
if (!isFrozen({ pendingRequestIds })) continue;
|
|
11197
|
-
if (adapter.getActivity(id) != null) continue;
|
|
11198
|
-
for (const pending of pendingRequestIds) {
|
|
11199
|
-
effects.push({
|
|
11200
|
-
kind: SCHEDULE_EFFECT_KIND.SIR_AUTO_REJECT,
|
|
11201
|
-
activityId: id,
|
|
11202
|
-
sirId: pending,
|
|
11203
|
-
reason: SIR_AUTO_REJECT_REASON.ACTIVITY_DELETED
|
|
11204
|
-
});
|
|
11205
|
-
}
|
|
11206
|
-
}
|
|
11207
|
-
return effects;
|
|
11208
|
-
}
|
|
11209
|
-
|
|
11210
|
-
// src/dispatch/conversion.ts
|
|
11211
|
-
function snapshotToLink(l) {
|
|
11212
|
-
const view = {
|
|
11213
|
-
id: String(l.id),
|
|
11214
|
-
source: String(l.source),
|
|
11215
|
-
target: String(l.target),
|
|
11216
|
-
type: l.type,
|
|
11217
|
-
lag: Number(l.lag),
|
|
11218
|
-
freeSlackHours: typeof l.freeSlackHours === "number" && Number.isFinite(l.freeSlackHours) ? l.freeSlackHours : null,
|
|
11219
|
-
isCritical: l.isCritical === true
|
|
11220
|
-
};
|
|
11221
|
-
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
11222
|
-
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
11223
|
-
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
11224
|
-
return view;
|
|
11225
|
-
}
|
|
11226
|
-
|
|
11227
|
-
// src/dispatch/shared/change-set.ts
|
|
11228
|
-
var SIR_DETECTION_PHASE = {
|
|
11229
|
-
BEFORE_DIFF: "before-diff",
|
|
11230
|
-
AFTER_DIFF: "after-diff"
|
|
11231
|
-
};
|
|
11232
|
-
async function assembleChangeSet(adapter, args) {
|
|
11233
|
-
const allTouched = collectChangeSetIds(adapter, args);
|
|
11234
|
-
const before = new Map(args.beforeSnap);
|
|
11235
|
-
const captured = adapter.peekWriteCapture();
|
|
11236
|
-
if (captured) {
|
|
11237
|
-
const insertedIds = collectInsertedIds(captured);
|
|
11238
|
-
mergeDirtyBeforeImages(adapter, captured, insertedIds, before, allTouched);
|
|
11239
|
-
}
|
|
11240
|
-
const sirDetection = args.sirDetection;
|
|
11241
|
-
const beforeDiffEffects = sirDetection === SIR_DETECTION_PHASE.BEFORE_DIFF ? detectSirAutoReject(before, adapter, allTouched) : [];
|
|
11242
|
-
const correlativeBefore = buildCorrelativeBeforeMap(
|
|
11243
|
-
args.correlativeShifts,
|
|
11244
|
-
allTouched
|
|
11245
|
-
);
|
|
11246
|
-
const activityChanges = await buildActivityChanges(
|
|
11247
|
-
adapter,
|
|
11248
|
-
before,
|
|
11249
|
-
allTouched,
|
|
11250
|
-
correlativeBefore
|
|
11251
|
-
);
|
|
11252
|
-
const effects = sirDetection === SIR_DETECTION_PHASE.AFTER_DIFF ? detectSirAutoReject(before, adapter, allTouched) : beforeDiffEffects;
|
|
11253
|
-
const warnings = args.warnings ?? [];
|
|
11254
|
-
const linkChanges = mergeLinkChanges(
|
|
11255
|
-
args.links ?? [],
|
|
11256
|
-
captured ? buildCapturedCriticalPathLinkChanges(adapter, captured) : []
|
|
11257
|
-
);
|
|
11258
|
-
return {
|
|
11259
|
-
source: args.source,
|
|
11260
|
-
activities: activityChanges,
|
|
11261
|
-
links: linkChanges,
|
|
11262
|
-
calendars: [],
|
|
11263
|
-
trackingEvents: args.trackingEvents ?? [],
|
|
11264
|
-
...effects.length > 0 ? { effects } : {},
|
|
11265
|
-
...warnings.length > 0 ? { warnings } : {}
|
|
11266
|
-
};
|
|
11267
|
-
}
|
|
11268
|
-
function buildCapturedCriticalPathLinkChanges(adapter, captured) {
|
|
11269
|
-
const changes = [];
|
|
11270
|
-
for (const operation of captured.ops) {
|
|
11271
|
-
if (operation.kind !== WRITE_OPERATION_KIND.LINK_FIELD) continue;
|
|
11272
|
-
const after = adapter.getLink(operation.linkId);
|
|
11273
|
-
if (!after) continue;
|
|
11274
|
-
const fields = {};
|
|
11275
|
-
for (const field of ["freeSlackHours", "isCritical"]) {
|
|
11276
|
-
if (!Object.is(operation.before[field], after[field])) {
|
|
11277
|
-
fields[field] = {
|
|
11278
|
-
before: operation.before[field],
|
|
11279
|
-
after: after[field]
|
|
11280
|
-
};
|
|
11281
|
-
}
|
|
11282
|
-
}
|
|
11283
|
-
if (Object.keys(fields).length === 0) continue;
|
|
11284
|
-
changes.push({
|
|
11285
|
-
id: operation.linkId,
|
|
11286
|
-
kind: ENTITY_CHANGE_KIND.UPDATED,
|
|
11287
|
-
fields,
|
|
11288
|
-
after: snapshotToLink(after)
|
|
11289
|
-
});
|
|
11290
|
-
}
|
|
11291
|
-
return changes;
|
|
11292
|
-
}
|
|
11293
|
-
function mergeLinkChanges(explicit, captured) {
|
|
11294
|
-
const merged = [];
|
|
11295
|
-
const indexById = /* @__PURE__ */ new Map();
|
|
11296
|
-
for (const change of explicit) {
|
|
11297
|
-
const index = indexById.get(change.id);
|
|
11298
|
-
if (index === void 0) {
|
|
11299
|
-
indexById.set(change.id, merged.length);
|
|
11300
|
-
merged.push(change);
|
|
11301
|
-
continue;
|
|
11302
|
-
}
|
|
11303
|
-
const current = merged[index];
|
|
11304
|
-
if ((current.kind === ENTITY_CHANGE_KIND.UPDATED || current.kind === ENTITY_CHANGE_KIND.CREATED) && change.kind === ENTITY_CHANGE_KIND.UPDATED) {
|
|
11305
|
-
merged[index] = {
|
|
11306
|
-
...current,
|
|
11307
|
-
fields: { ...current.fields, ...change.fields },
|
|
11308
|
-
after: change.after
|
|
11309
|
-
};
|
|
11310
|
-
continue;
|
|
11311
|
-
}
|
|
11312
|
-
merged[index] = change;
|
|
11313
|
-
}
|
|
11314
|
-
for (const change of captured) {
|
|
11315
|
-
const index = indexById.get(change.id);
|
|
11316
|
-
if (index === void 0) {
|
|
11317
|
-
indexById.set(change.id, merged.length);
|
|
11318
|
-
merged.push(change);
|
|
11319
|
-
continue;
|
|
11320
|
-
}
|
|
11321
|
-
const current = merged[index];
|
|
11322
|
-
if (current.kind === ENTITY_CHANGE_KIND.DELETED) continue;
|
|
11323
|
-
const capturedFields = current.kind === ENTITY_CHANGE_KIND.CREATED ? Object.fromEntries(
|
|
11324
|
-
Object.entries(change.fields ?? {}).map(([field, value]) => [
|
|
11325
|
-
field,
|
|
11326
|
-
{ before: void 0, after: value.after }
|
|
11327
|
-
])
|
|
11328
|
-
) : change.fields;
|
|
11329
|
-
merged[index] = {
|
|
11330
|
-
...current,
|
|
11331
|
-
fields: { ...current.fields, ...capturedFields },
|
|
11332
|
-
after: change.after
|
|
11333
|
-
};
|
|
11441
|
+
return Number.isNaN(parsed2.getTime()) ? null : parsed2;
|
|
11334
11442
|
}
|
|
11335
|
-
return
|
|
11443
|
+
return null;
|
|
11336
11444
|
}
|
|
11337
|
-
function
|
|
11338
|
-
if (
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
correlativeBefore.set(shift.activityId, shift.before);
|
|
11342
|
-
touched.add(shift.activityId);
|
|
11343
|
-
}
|
|
11344
|
-
return correlativeBefore;
|
|
11445
|
+
function sameDate(first, second) {
|
|
11446
|
+
if (first === null && second === null) return true;
|
|
11447
|
+
if (first === null || second === null) return false;
|
|
11448
|
+
return first.getTime() === second.getTime();
|
|
11345
11449
|
}
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11450
|
+
|
|
11451
|
+
// src/dispatch/shared/constraint-settle.ts
|
|
11452
|
+
async function settleConstraintEdits(deps, { postMutationArgs, revertTargets, revert }) {
|
|
11453
|
+
let { scheduledIds } = await runPostMutation(
|
|
11454
|
+
deps,
|
|
11455
|
+
withCriticalPathSuppressed(postMutationArgs)
|
|
11456
|
+
);
|
|
11457
|
+
let someEditReverted = false;
|
|
11458
|
+
for (const { activityId, priorConstraintDate } of revertTargets) {
|
|
11459
|
+
const reverted = revert(activityId, priorConstraintDate);
|
|
11460
|
+
someEditReverted = someEditReverted || reverted;
|
|
11352
11461
|
}
|
|
11353
|
-
|
|
11354
|
-
|
|
11355
|
-
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
before.set(id, beforeImage);
|
|
11363
|
-
touched.add(id);
|
|
11462
|
+
if (someEditReverted) {
|
|
11463
|
+
const resettled = await runPostMutation(
|
|
11464
|
+
deps,
|
|
11465
|
+
withCriticalPathSuppressed(postMutationArgs)
|
|
11466
|
+
);
|
|
11467
|
+
scheduledIds = mergeScheduledIds(scheduledIds, resettled.scheduledIds);
|
|
11468
|
+
}
|
|
11469
|
+
if (resolveRunCriticalPath(postMutationArgs.action)) {
|
|
11470
|
+
await runPostMutation(deps, criticalPathOnlyArgs(postMutationArgs));
|
|
11364
11471
|
}
|
|
11472
|
+
return { scheduledIds };
|
|
11365
11473
|
}
|
|
11366
|
-
function
|
|
11367
|
-
return
|
|
11368
|
-
|
|
11369
|
-
|
|
11474
|
+
function withCriticalPathSuppressed(args) {
|
|
11475
|
+
return {
|
|
11476
|
+
...args,
|
|
11477
|
+
options: { ...args.options, skipCriticalPath: true }
|
|
11478
|
+
};
|
|
11479
|
+
}
|
|
11480
|
+
function criticalPathOnlyArgs(args) {
|
|
11481
|
+
return {
|
|
11482
|
+
action: args.action,
|
|
11483
|
+
autoscheduleFrom: null,
|
|
11484
|
+
recomputeParentsFrom: [],
|
|
11485
|
+
now: null,
|
|
11486
|
+
options: args.options
|
|
11487
|
+
};
|
|
11488
|
+
}
|
|
11489
|
+
function mergeScheduledIds(firstSettleIds, secondSettleIds) {
|
|
11490
|
+
const merged = new Set(firstSettleIds);
|
|
11491
|
+
for (const scheduledId of secondSettleIds) merged.add(scheduledId);
|
|
11492
|
+
return [...merged];
|
|
11370
11493
|
}
|
|
11371
11494
|
|
|
11372
11495
|
// src/dispatch/shared/helpers.ts
|
|
@@ -11445,6 +11568,18 @@ function buildLinkChangesForBatch(applied, beforeLinks, adapter) {
|
|
|
11445
11568
|
const afterLink = adapter.getLink(op.linkId);
|
|
11446
11569
|
if (!afterLink) continue;
|
|
11447
11570
|
const fields = {};
|
|
11571
|
+
if (beforeLink && String(beforeLink.source) !== String(afterLink.source)) {
|
|
11572
|
+
fields.source = {
|
|
11573
|
+
before: String(beforeLink.source),
|
|
11574
|
+
after: String(afterLink.source)
|
|
11575
|
+
};
|
|
11576
|
+
}
|
|
11577
|
+
if (beforeLink && String(beforeLink.target) !== String(afterLink.target)) {
|
|
11578
|
+
fields.target = {
|
|
11579
|
+
before: String(beforeLink.target),
|
|
11580
|
+
after: String(afterLink.target)
|
|
11581
|
+
};
|
|
11582
|
+
}
|
|
11448
11583
|
if (beforeLink && beforeLink.type !== afterLink.type) {
|
|
11449
11584
|
fields.type = { before: beforeLink.type, after: afterLink.type };
|
|
11450
11585
|
}
|
|
@@ -11713,6 +11848,12 @@ function collectBatchContext(operations, adapter) {
|
|
|
11713
11848
|
if (!link) continue;
|
|
11714
11849
|
activityIds.add(String(link.source));
|
|
11715
11850
|
activityIds.add(String(link.target));
|
|
11851
|
+
if (operation.kind === DISPATCH_LINK_OPERATION_KIND.UPDATE) {
|
|
11852
|
+
if (operation.source !== void 0)
|
|
11853
|
+
activityIds.add(String(operation.source));
|
|
11854
|
+
if (operation.target !== void 0)
|
|
11855
|
+
activityIds.add(String(operation.target));
|
|
11856
|
+
}
|
|
11716
11857
|
links.set(operation.linkId, { ...link });
|
|
11717
11858
|
}
|
|
11718
11859
|
return { activityIds, links };
|
|
@@ -11798,6 +11939,8 @@ function batchOpToLinkOperation(op, hoursPerDay) {
|
|
|
11798
11939
|
return {
|
|
11799
11940
|
kind: DISPATCH_LINK_OPERATION_KIND.UPDATE,
|
|
11800
11941
|
linkId: op.linkId,
|
|
11942
|
+
source: op.source,
|
|
11943
|
+
target: op.target,
|
|
11801
11944
|
type: op.type,
|
|
11802
11945
|
lag: op.lag === void 0 ? void 0 : lagDaysToHours(op.lag, hoursPerDay)
|
|
11803
11946
|
};
|
|
@@ -11821,8 +11964,11 @@ async function dispatchLinkBatch(operations, source, options, deps, deterministi
|
|
|
11821
11964
|
if (operations.length === 1 && applied[0] && applied[0].rejected !== null && source.kind !== DISPATCH_ACTION_KIND.INLINE_EDIT) {
|
|
11822
11965
|
return { ok: false, reason: applied[0].rejected };
|
|
11823
11966
|
}
|
|
11967
|
+
if (!options.skipAutoSchedule) {
|
|
11968
|
+
snapFreedMilestones(applied, beforeLinks, adapter);
|
|
11969
|
+
}
|
|
11824
11970
|
const triggerId = pickAutoscheduleTrigger(applied, beforeLinks, adapter);
|
|
11825
|
-
const { scheduledIds } = await runPostMutation(
|
|
11971
|
+
const { scheduledIds, warnings } = await runPostMutation(
|
|
11826
11972
|
{
|
|
11827
11973
|
adapter,
|
|
11828
11974
|
scheduler,
|
|
@@ -11860,10 +12006,32 @@ async function dispatchLinkBatch(operations, source, options, deps, deterministi
|
|
|
11860
12006
|
touchedIds: affectedActivityIds,
|
|
11861
12007
|
scheduledIds,
|
|
11862
12008
|
links: linkChanges,
|
|
12009
|
+
warnings,
|
|
11863
12010
|
hoursPerDay: sector.hoursPerDay
|
|
11864
12011
|
})
|
|
11865
12012
|
};
|
|
11866
12013
|
}
|
|
12014
|
+
function snapFreedMilestones(applied, beforeLinks, adapter) {
|
|
12015
|
+
for (const operation of applied) {
|
|
12016
|
+
if (operation.rejected !== null || operation.op.kind !== DISPATCH_LINK_OPERATION_KIND.DELETE) {
|
|
12017
|
+
continue;
|
|
12018
|
+
}
|
|
12019
|
+
const targetId = beforeLinks.get(operation.op.linkId)?.target;
|
|
12020
|
+
if (!targetId || adapter.getIncomingLinkIds(targetId).length > 0) continue;
|
|
12021
|
+
const activity = adapter.getActivity(targetId);
|
|
12022
|
+
if (!activity || activity.type !== ACTIVITY_TYPE.MILESTONE) continue;
|
|
12023
|
+
const startDate = adapter.getClosestWorkTime({
|
|
12024
|
+
date: activity.startDate,
|
|
12025
|
+
dir: WORK_TIME_DIRECTION.FUTURE,
|
|
12026
|
+
task: activity
|
|
12027
|
+
});
|
|
12028
|
+
if (startDate.getTime() === activity.startDate.getTime()) continue;
|
|
12029
|
+
adapter.setActivityFields(targetId, {
|
|
12030
|
+
startDate,
|
|
12031
|
+
endDate: new Date(startDate)
|
|
12032
|
+
});
|
|
12033
|
+
}
|
|
12034
|
+
}
|
|
11867
12035
|
function pickAutoscheduleTrigger(applied, beforeLinks, adapter) {
|
|
11868
12036
|
for (const operation of applied) {
|
|
11869
12037
|
if (operation.rejected !== null) continue;
|
|
@@ -11896,6 +12064,8 @@ function toLinkOperation(action, hoursPerDay) {
|
|
|
11896
12064
|
return {
|
|
11897
12065
|
kind: DISPATCH_LINK_OPERATION_KIND.UPDATE,
|
|
11898
12066
|
linkId: action.linkId,
|
|
12067
|
+
source: action.source,
|
|
12068
|
+
target: action.target,
|
|
11899
12069
|
type: action.type,
|
|
11900
12070
|
lag: action.lag === void 0 ? void 0 : lagDaysToHours(action.lag, hoursPerDay)
|
|
11901
12071
|
};
|
|
@@ -14827,6 +14997,158 @@ async function dispatchActivityOutdent(action, options, deps) {
|
|
|
14827
14997
|
};
|
|
14828
14998
|
}
|
|
14829
14999
|
|
|
15000
|
+
// src/dispatch/handlers/activity-type-set.ts
|
|
15001
|
+
async function dispatchActivityTypeSet(action, options, deps) {
|
|
15002
|
+
const { adapter, scheduler, sector } = deps;
|
|
15003
|
+
const activity = adapter.getActivity(action.activityId);
|
|
15004
|
+
if (!activity) {
|
|
15005
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
15006
|
+
}
|
|
15007
|
+
if (!isActivityType(action.type)) {
|
|
15008
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
15009
|
+
}
|
|
15010
|
+
const children = adapter.getChildren(action.activityId);
|
|
15011
|
+
const targetIsProject = action.type === ACTIVITY_TYPE.PROJECT;
|
|
15012
|
+
if (targetIsProject !== children.length > 0) {
|
|
15013
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
15014
|
+
}
|
|
15015
|
+
const allIds = new Set(adapter.getAllIds());
|
|
15016
|
+
const beforeSnap = await snapshotActivities(adapter, allIds);
|
|
15017
|
+
scheduler.invalidateAllCaches();
|
|
15018
|
+
if (targetIsProject && activity.type !== ACTIVITY_TYPE.PROJECT) {
|
|
15019
|
+
applyPromotion(action.activityId, children[0], deps);
|
|
15020
|
+
} else if (!targetIsProject && activity.type === ACTIVITY_TYPE.PROJECT) {
|
|
15021
|
+
applyDemotion(action.activityId, deps);
|
|
15022
|
+
}
|
|
15023
|
+
if (action.type !== ACTIVITY_TYPE.PROJECT) {
|
|
15024
|
+
applyLeafType(action.activityId, action.type, deps);
|
|
15025
|
+
ensureLeafCustomId(action.activityId, deps);
|
|
15026
|
+
} else {
|
|
15027
|
+
recomputeRollupCascadesForParent(action.activityId, adapter);
|
|
15028
|
+
}
|
|
15029
|
+
const beforeLinks = removeInvalidHierarchyLinks(action.activityId, deps);
|
|
15030
|
+
const { scheduledIds } = await runPostMutation(
|
|
15031
|
+
{
|
|
15032
|
+
adapter,
|
|
15033
|
+
scheduler,
|
|
15034
|
+
sector,
|
|
15035
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
15036
|
+
},
|
|
15037
|
+
{
|
|
15038
|
+
action,
|
|
15039
|
+
autoscheduleFrom: action.activityId,
|
|
15040
|
+
recomputeParentsFrom: [action.activityId],
|
|
15041
|
+
recomputeParentProgress: true,
|
|
15042
|
+
now: deps.now,
|
|
15043
|
+
options
|
|
15044
|
+
}
|
|
15045
|
+
);
|
|
15046
|
+
return {
|
|
15047
|
+
ok: true,
|
|
15048
|
+
changes: await assembleChangeSet(adapter, {
|
|
15049
|
+
source: action,
|
|
15050
|
+
beforeSnap,
|
|
15051
|
+
touchedIds: allIds,
|
|
15052
|
+
scheduledIds,
|
|
15053
|
+
sirDetection: SIR_DETECTION_PHASE.BEFORE_DIFF,
|
|
15054
|
+
links: buildLinkDeletions(beforeLinks),
|
|
15055
|
+
hoursPerDay: sector.hoursPerDay
|
|
15056
|
+
}),
|
|
15057
|
+
__beforeLinks: new Map(beforeLinks)
|
|
15058
|
+
};
|
|
15059
|
+
}
|
|
15060
|
+
function applyPromotion(activityId, childId, deps) {
|
|
15061
|
+
const activity = deps.adapter.getActivity(activityId);
|
|
15062
|
+
const oldCustomId = activity?.customId ?? null;
|
|
15063
|
+
const promotion = buildParentMutations({
|
|
15064
|
+
parentId: activityId,
|
|
15065
|
+
newChildId: childId,
|
|
15066
|
+
isCurrentlyLeaf: true,
|
|
15067
|
+
getParent: (id) => deps.adapter.getActivity(id) ?? null
|
|
15068
|
+
});
|
|
15069
|
+
if (!promotion) return;
|
|
15070
|
+
for (const [field, value] of Object.entries(promotion.fields)) {
|
|
15071
|
+
setActivityFieldDynamic(deps.adapter, activityId, field, value);
|
|
15072
|
+
}
|
|
15073
|
+
if (promotion.promotionSnapshot) {
|
|
15074
|
+
deps.adapter.setPromotionSnapshot(activityId, promotion.promotionSnapshot);
|
|
15075
|
+
}
|
|
15076
|
+
if (promotion.fields.customId === null) {
|
|
15077
|
+
deps.customIdTracker.releaseCustomId(oldCustomId);
|
|
15078
|
+
}
|
|
15079
|
+
}
|
|
15080
|
+
function applyDemotion(activityId, deps) {
|
|
15081
|
+
const activity = deps.adapter.getActivity(activityId);
|
|
15082
|
+
if (!activity) return;
|
|
15083
|
+
const demotion = buildParentDemotionMutations({
|
|
15084
|
+
parent: activity,
|
|
15085
|
+
remainingChildIds: [],
|
|
15086
|
+
defaultDurationHours: deps.sector.hoursPerDay,
|
|
15087
|
+
promotionSnapshot: deps.adapter.getPromotionSnapshot(activityId),
|
|
15088
|
+
computeEndDate: (startDate, durationHours) => deps.adapter.calculateEndDate({
|
|
15089
|
+
startDate,
|
|
15090
|
+
durationHours,
|
|
15091
|
+
task: activity
|
|
15092
|
+
})
|
|
15093
|
+
});
|
|
15094
|
+
if (!demotion) return;
|
|
15095
|
+
for (const [field, value] of Object.entries(demotion.fields)) {
|
|
15096
|
+
setActivityFieldDynamic(deps.adapter, activityId, field, value);
|
|
15097
|
+
}
|
|
15098
|
+
}
|
|
15099
|
+
function applyLeafType(activityId, type, deps) {
|
|
15100
|
+
const activity = deps.adapter.getActivity(activityId);
|
|
15101
|
+
if (!activity) return;
|
|
15102
|
+
deps.adapter.setActivityField(activityId, ACTIVITY_PROPERTY.TYPE, type);
|
|
15103
|
+
const durationHours = type === ACTIVITY_TYPE.MILESTONE ? 0 : activity.durationHours === 0 ? deps.sector.hoursPerDay : activity.durationHours;
|
|
15104
|
+
if (durationHours !== activity.durationHours) {
|
|
15105
|
+
deps.adapter.setActivityField(
|
|
15106
|
+
activityId,
|
|
15107
|
+
ACTIVITY_PROPERTY.DURATION_HOURS,
|
|
15108
|
+
durationHours
|
|
15109
|
+
);
|
|
15110
|
+
}
|
|
15111
|
+
const current = deps.adapter.getActivity(activityId);
|
|
15112
|
+
if (!current) return;
|
|
15113
|
+
const endDate = deps.adapter.calculateEndDate({
|
|
15114
|
+
startDate: current.startDate,
|
|
15115
|
+
durationHours,
|
|
15116
|
+
task: current
|
|
15117
|
+
});
|
|
15118
|
+
deps.adapter.setActivityField(
|
|
15119
|
+
activityId,
|
|
15120
|
+
ACTIVITY_PROPERTY.END_DATE,
|
|
15121
|
+
endDate
|
|
15122
|
+
);
|
|
15123
|
+
}
|
|
15124
|
+
function ensureLeafCustomId(activityId, deps) {
|
|
15125
|
+
const activity = deps.adapter.getActivity(activityId);
|
|
15126
|
+
if (activity?.customId) return;
|
|
15127
|
+
const previousSiblingId = findPrevSiblingHelper(activityId, deps.adapter);
|
|
15128
|
+
const previousCustomId = previousSiblingId ? deps.adapter.getActivity(previousSiblingId)?.customId : null;
|
|
15129
|
+
const generated = deps.customIdTracker.generateCustomIdForRestoredChild(
|
|
15130
|
+
previousCustomId || null
|
|
15131
|
+
);
|
|
15132
|
+
deps.adapter.setActivityField(
|
|
15133
|
+
activityId,
|
|
15134
|
+
ACTIVITY_PROPERTY.CUSTOM_ID,
|
|
15135
|
+
generated
|
|
15136
|
+
);
|
|
15137
|
+
}
|
|
15138
|
+
function removeInvalidHierarchyLinks(activityId, deps) {
|
|
15139
|
+
const beforeLinks = /* @__PURE__ */ new Map();
|
|
15140
|
+
for (const linkId of collectInvalidLinkIdsForCycle(
|
|
15141
|
+
[activityId],
|
|
15142
|
+
deps.adapter
|
|
15143
|
+
)) {
|
|
15144
|
+
const link = deps.adapter.getLink(linkId);
|
|
15145
|
+
if (!link) continue;
|
|
15146
|
+
beforeLinks.set(String(linkId), { ...link });
|
|
15147
|
+
deps.adapter.removeLink(linkId);
|
|
15148
|
+
}
|
|
15149
|
+
return beforeLinks;
|
|
15150
|
+
}
|
|
15151
|
+
|
|
14830
15152
|
// src/dispatch/handlers/set-progress.ts
|
|
14831
15153
|
async function dispatchActivitySetProgress(action, options, deps) {
|
|
14832
15154
|
const { adapter, ctx, scheduler, sector } = deps;
|
|
@@ -16145,6 +16467,9 @@ async function performDispatch(action, options, deps) {
|
|
|
16145
16467
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_OUTDENT) {
|
|
16146
16468
|
return dispatchActivityOutdent(action, options, deps);
|
|
16147
16469
|
}
|
|
16470
|
+
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_TYPE_SET) {
|
|
16471
|
+
return dispatchActivityTypeSet(action, options, deps);
|
|
16472
|
+
}
|
|
16148
16473
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_SET_PROGRESS) {
|
|
16149
16474
|
return dispatchActivitySetProgress(action, options, deps);
|
|
16150
16475
|
}
|
|
@@ -16181,6 +16506,9 @@ async function performDispatch(action, options, deps) {
|
|
|
16181
16506
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_CONVERT) {
|
|
16182
16507
|
return dispatchActivityLookaheadConvert(action, options, deps);
|
|
16183
16508
|
}
|
|
16509
|
+
if (action.kind === DISPATCH_ACTION_KIND.SCHEDULE_AUTO_SCHEDULE) {
|
|
16510
|
+
return dispatchScheduleAutoSchedule(action, options, deps);
|
|
16511
|
+
}
|
|
16184
16512
|
if (action.kind === DISPATCH_ACTION_KIND.INLINE_EDIT && (action.column === COLUMN.CUSTOM_PREDECESSORS || action.column === COLUMN.CUSTOM_SUCESSORS)) {
|
|
16185
16513
|
return dispatchInlineEditLinks(action, options, deps);
|
|
16186
16514
|
}
|
|
@@ -19594,16 +19922,59 @@ var ScheduleState = class _ScheduleState {
|
|
|
19594
19922
|
if (incoming)
|
|
19595
19923
|
this._incoming.set(String(link.target), detachLinkRef(incoming, link.id));
|
|
19596
19924
|
}
|
|
19925
|
+
_removeLinkRef(index, activityId, linkId) {
|
|
19926
|
+
const refs = index.get(activityId);
|
|
19927
|
+
if (refs) index.set(activityId, detachLinkRef(refs, linkId));
|
|
19928
|
+
}
|
|
19929
|
+
_insertLinkRefInOrder(index, activityId, linkId) {
|
|
19930
|
+
const precedingIds = /* @__PURE__ */ new Set();
|
|
19931
|
+
for (const existingId of this._links.keys()) {
|
|
19932
|
+
if (String(existingId) === String(linkId)) break;
|
|
19933
|
+
precedingIds.add(String(existingId));
|
|
19934
|
+
}
|
|
19935
|
+
const refs = [...index.get(activityId) ?? []];
|
|
19936
|
+
const insertionIndex = refs.findIndex(
|
|
19937
|
+
(existingId) => !precedingIds.has(String(existingId))
|
|
19938
|
+
);
|
|
19939
|
+
refs.splice(insertionIndex < 0 ? refs.length : insertionIndex, 0, linkId);
|
|
19940
|
+
index.set(activityId, refs);
|
|
19941
|
+
}
|
|
19597
19942
|
setLinkField(linkId, field, value) {
|
|
19598
19943
|
const link = this.getLink(linkId);
|
|
19599
19944
|
if (!link) return;
|
|
19600
19945
|
this._writeCapture.noteLinkField(String(linkId), link);
|
|
19601
19946
|
link[field] = value;
|
|
19602
19947
|
}
|
|
19948
|
+
setLinkEndpoints(linkId, source, target) {
|
|
19949
|
+
const link = this.getLink(linkId);
|
|
19950
|
+
if (!link) return;
|
|
19951
|
+
const sourceChanged = String(link.source) !== String(source);
|
|
19952
|
+
const targetChanged = String(link.target) !== String(target);
|
|
19953
|
+
if (!sourceChanged && !targetChanged) return;
|
|
19954
|
+
this._writeCapture.noteLinkField(String(linkId), link);
|
|
19955
|
+
if (sourceChanged) {
|
|
19956
|
+
this._removeLinkRef(this._outgoing, String(link.source), link.id);
|
|
19957
|
+
link.source = source;
|
|
19958
|
+
this._insertLinkRefInOrder(this._outgoing, String(source), link.id);
|
|
19959
|
+
}
|
|
19960
|
+
if (targetChanged) {
|
|
19961
|
+
this._removeLinkRef(this._incoming, String(link.target), link.id);
|
|
19962
|
+
link.target = target;
|
|
19963
|
+
this._insertLinkRefInOrder(this._incoming, String(target), link.id);
|
|
19964
|
+
}
|
|
19965
|
+
}
|
|
19603
19966
|
setLinkFieldDynamic(linkId, field, value) {
|
|
19604
|
-
if (!isMutableLinkField(field)) return;
|
|
19605
19967
|
const link = this.getLink(linkId);
|
|
19606
19968
|
if (!link) return;
|
|
19969
|
+
if (field === LINK_PROPERTY.SOURCE) {
|
|
19970
|
+
this.setLinkEndpoints(linkId, String(value), link.target);
|
|
19971
|
+
return;
|
|
19972
|
+
}
|
|
19973
|
+
if (field === LINK_PROPERTY.TARGET) {
|
|
19974
|
+
this.setLinkEndpoints(linkId, link.source, String(value));
|
|
19975
|
+
return;
|
|
19976
|
+
}
|
|
19977
|
+
if (!isMutableLinkField(field)) return;
|
|
19607
19978
|
this._writeCapture.noteLinkField(String(linkId), link);
|
|
19608
19979
|
Reflect.set(link, field, value);
|
|
19609
19980
|
}
|
|
@@ -19724,10 +20095,16 @@ var ScheduleState = class _ScheduleState {
|
|
|
19724
20095
|
}
|
|
19725
20096
|
}
|
|
19726
20097
|
_restoreLinkFields(ops) {
|
|
19727
|
-
for (const op of ops)
|
|
19728
|
-
if (op.kind
|
|
19729
|
-
|
|
20098
|
+
for (const op of ops) {
|
|
20099
|
+
if (op.kind !== WRITE_OPERATION_KIND.LINK_FIELD) continue;
|
|
20100
|
+
const live = this._links.get(op.linkId);
|
|
20101
|
+
if (!live) continue;
|
|
20102
|
+
this.setLinkEndpoints(op.linkId, op.before.source, op.before.target);
|
|
20103
|
+
for (const key of Object.keys(live)) {
|
|
20104
|
+
if (!Object.hasOwn(op.before, key)) Reflect.deleteProperty(live, key);
|
|
19730
20105
|
}
|
|
20106
|
+
Object.assign(live, op.before);
|
|
20107
|
+
}
|
|
19731
20108
|
}
|
|
19732
20109
|
_restoreActivityFields(before) {
|
|
19733
20110
|
for (const [id, snapshot] of before) {
|
|
@@ -20198,18 +20575,23 @@ function applyChangeSetSide(state, entry, side) {
|
|
|
20198
20575
|
for (const ch of cs.links) {
|
|
20199
20576
|
if (ch.kind === ENTITY_CHANGE_KIND.UPDATED) {
|
|
20200
20577
|
if (!ch.fields) continue;
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
|
|
20204
|
-
|
|
20205
|
-
|
|
20206
|
-
)
|
|
20207
|
-
|
|
20208
|
-
|
|
20209
|
-
|
|
20210
|
-
|
|
20578
|
+
const liveLink = state.getLink(String(ch.id));
|
|
20579
|
+
const sourceChange = ch.fields[LINK_PROPERTY.SOURCE];
|
|
20580
|
+
const targetChange = ch.fields[LINK_PROPERTY.TARGET];
|
|
20581
|
+
if (liveLink && (sourceChange || targetChange)) {
|
|
20582
|
+
state.setLinkEndpoints(
|
|
20583
|
+
String(ch.id),
|
|
20584
|
+
String(
|
|
20585
|
+
sourceChange ? side === CHANGE_SET_SIDE.BEFORE ? sourceChange.before : sourceChange.after : liveLink.source
|
|
20586
|
+
),
|
|
20587
|
+
String(
|
|
20588
|
+
targetChange ? side === CHANGE_SET_SIDE.BEFORE ? targetChange.before : targetChange.after : liveLink.target
|
|
20589
|
+
)
|
|
20590
|
+
);
|
|
20211
20591
|
}
|
|
20212
20592
|
for (const [field, fv] of Object.entries(ch.fields)) {
|
|
20593
|
+
if (field === LINK_PROPERTY.SOURCE || field === LINK_PROPERTY.TARGET)
|
|
20594
|
+
continue;
|
|
20213
20595
|
setLinkFieldDynamic(
|
|
20214
20596
|
state,
|
|
20215
20597
|
String(ch.id),
|
|
@@ -22772,6 +23154,15 @@ function evaluateEnd(formula, proposedEdgeTimeMs) {
|
|
|
22772
23154
|
|
|
22773
23155
|
// src/init/schedule-core.ts
|
|
22774
23156
|
var FORK_SOURCE = /* @__PURE__ */ Symbol("ScheduleCore.fork.source");
|
|
23157
|
+
function emptyChangeSet(source) {
|
|
23158
|
+
return {
|
|
23159
|
+
source,
|
|
23160
|
+
activities: [],
|
|
23161
|
+
links: [],
|
|
23162
|
+
calendars: [],
|
|
23163
|
+
trackingEvents: []
|
|
23164
|
+
};
|
|
23165
|
+
}
|
|
22775
23166
|
function acknowledgesPersistence(action) {
|
|
22776
23167
|
return action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC && action.acknowledgePersistedLookahead === true;
|
|
22777
23168
|
}
|
|
@@ -23297,15 +23688,20 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
23297
23688
|
createProjectionChangeSetFrom(source) {
|
|
23298
23689
|
this.assertReady();
|
|
23299
23690
|
source.assertReady();
|
|
23300
|
-
|
|
23301
|
-
|
|
23302
|
-
|
|
23303
|
-
|
|
23304
|
-
|
|
23305
|
-
|
|
23306
|
-
|
|
23307
|
-
|
|
23308
|
-
|
|
23691
|
+
return createProjectionChangeSet(
|
|
23692
|
+
this._snapshotProjection(),
|
|
23693
|
+
source._snapshotProjection()
|
|
23694
|
+
);
|
|
23695
|
+
}
|
|
23696
|
+
_snapshotProjection() {
|
|
23697
|
+
return {
|
|
23698
|
+
activities: this.getAllActivitiesView(),
|
|
23699
|
+
links: this.getAllLinksView(),
|
|
23700
|
+
calendars: this.getAllCalendars(),
|
|
23701
|
+
selectedIds: this.getSelectedActivityIds(),
|
|
23702
|
+
hiddenIds: this.getHiddenActivityIds(),
|
|
23703
|
+
order: collectBranchOrder(this.coreRuntime.state)
|
|
23704
|
+
};
|
|
23309
23705
|
}
|
|
23310
23706
|
/** @returns Deleted persisted activity identities since the last save. */
|
|
23311
23707
|
getDeletedActivitiesSinceLastSave() {
|
|
@@ -23367,6 +23763,17 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
23367
23763
|
const admitted = this._beginScheduleMutation(action);
|
|
23368
23764
|
return this._enqueue(() => this._dispatchInner(action, options, admitted));
|
|
23369
23765
|
}
|
|
23766
|
+
/**
|
|
23767
|
+
* Applies commands in order and runs deferred scheduling finalization once.
|
|
23768
|
+
*
|
|
23769
|
+
* Rejected commands do not roll back earlier accepted commands.
|
|
23770
|
+
*/
|
|
23771
|
+
async dispatchMany(actions, options = {}) {
|
|
23772
|
+
const admitted = actions.some(dispatchChangesSchedulingState) ? this._admitScheduleMutation() : false;
|
|
23773
|
+
return this._enqueue(
|
|
23774
|
+
() => this._dispatchManyInner(actions, options, admitted)
|
|
23775
|
+
);
|
|
23776
|
+
}
|
|
23370
23777
|
/**
|
|
23371
23778
|
* Serializes and applies an activity creation batch.
|
|
23372
23779
|
*
|
|
@@ -23404,7 +23811,125 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
23404
23811
|
this._settleScheduleMutation(admitted, changedState);
|
|
23405
23812
|
}
|
|
23406
23813
|
}
|
|
23407
|
-
async
|
|
23814
|
+
async _dispatchManyInner(actions, options, admitted) {
|
|
23815
|
+
const projectionBefore = this._snapshotProjection();
|
|
23816
|
+
const deferredOptions = {
|
|
23817
|
+
...options,
|
|
23818
|
+
skipAutoSchedule: true,
|
|
23819
|
+
skipCriticalPath: true
|
|
23820
|
+
};
|
|
23821
|
+
const results = [];
|
|
23822
|
+
const acceptedChangeSets = [];
|
|
23823
|
+
const acceptedActions = [];
|
|
23824
|
+
let changedSchedulingState = false;
|
|
23825
|
+
let finalizationRequested = false;
|
|
23826
|
+
let scopedActivityId;
|
|
23827
|
+
let requiresRootFinalization = false;
|
|
23828
|
+
let finalizationChangeSet = null;
|
|
23829
|
+
let autoScheduled = false;
|
|
23830
|
+
let completed = false;
|
|
23831
|
+
try {
|
|
23832
|
+
for (const action of actions) {
|
|
23833
|
+
if (action.kind === DISPATCH_ACTION_KIND.SCHEDULE_AUTO_SCHEDULE) {
|
|
23834
|
+
if (action.activityId !== void 0 && !this.coreRuntime.state.activityExists(action.activityId)) {
|
|
23835
|
+
results.push({
|
|
23836
|
+
ok: false,
|
|
23837
|
+
reason: REJECTION_REASON.ACTIVITY_NOT_FOUND
|
|
23838
|
+
});
|
|
23839
|
+
continue;
|
|
23840
|
+
}
|
|
23841
|
+
const changes = emptyChangeSet(action);
|
|
23842
|
+
results.push({ ok: true, changes });
|
|
23843
|
+
acceptedChangeSets.push(changes);
|
|
23844
|
+
acceptedActions.push(action);
|
|
23845
|
+
finalizationRequested = true;
|
|
23846
|
+
if (action.activityId === void 0 || scopedActivityId !== void 0 && scopedActivityId !== action.activityId) {
|
|
23847
|
+
requiresRootFinalization = true;
|
|
23848
|
+
} else {
|
|
23849
|
+
scopedActivityId = action.activityId;
|
|
23850
|
+
}
|
|
23851
|
+
continue;
|
|
23852
|
+
}
|
|
23853
|
+
const dispatched = await this._runDispatch(
|
|
23854
|
+
action,
|
|
23855
|
+
deferredOptions,
|
|
23856
|
+
true
|
|
23857
|
+
);
|
|
23858
|
+
results.push(dispatched.result);
|
|
23859
|
+
if (dispatched.result.ok) {
|
|
23860
|
+
acceptedChangeSets.push(dispatched.result.changes);
|
|
23861
|
+
acceptedActions.push(action);
|
|
23862
|
+
}
|
|
23863
|
+
if (dispatched.changedState) {
|
|
23864
|
+
changedSchedulingState = true;
|
|
23865
|
+
finalizationRequested = true;
|
|
23866
|
+
requiresRootFinalization = true;
|
|
23867
|
+
}
|
|
23868
|
+
}
|
|
23869
|
+
if (finalizationRequested) {
|
|
23870
|
+
const finalizationAction = requiresRootFinalization || scopedActivityId === void 0 ? { kind: DISPATCH_ACTION_KIND.SCHEDULE_AUTO_SCHEDULE } : {
|
|
23871
|
+
kind: DISPATCH_ACTION_KIND.SCHEDULE_AUTO_SCHEDULE,
|
|
23872
|
+
activityId: scopedActivityId
|
|
23873
|
+
};
|
|
23874
|
+
const finalized = await this._runDispatch(
|
|
23875
|
+
finalizationAction,
|
|
23876
|
+
options,
|
|
23877
|
+
true
|
|
23878
|
+
);
|
|
23879
|
+
if (finalized.result.ok) {
|
|
23880
|
+
finalizationChangeSet = finalized.result.changes;
|
|
23881
|
+
acceptedChangeSets.push(finalized.result.changes);
|
|
23882
|
+
autoScheduled = options.skipAutoSchedule !== true;
|
|
23883
|
+
}
|
|
23884
|
+
changedSchedulingState ||= finalized.changedState;
|
|
23885
|
+
}
|
|
23886
|
+
if (changedSchedulingState) {
|
|
23887
|
+
this._recordScheduleMutation(
|
|
23888
|
+
finalizationChangeSet !== null && options.skipAutoSchedule !== true && options.skipCriticalPath !== true
|
|
23889
|
+
);
|
|
23890
|
+
}
|
|
23891
|
+
completed = true;
|
|
23892
|
+
return {
|
|
23893
|
+
results,
|
|
23894
|
+
autoScheduled,
|
|
23895
|
+
changeSet: this._aggregateDispatchManyChangeSets(
|
|
23896
|
+
projectionBefore,
|
|
23897
|
+
acceptedChangeSets,
|
|
23898
|
+
acceptedActions
|
|
23899
|
+
)
|
|
23900
|
+
};
|
|
23901
|
+
} finally {
|
|
23902
|
+
if (!completed && changedSchedulingState) {
|
|
23903
|
+
this._recordScheduleMutation(false);
|
|
23904
|
+
}
|
|
23905
|
+
this._settleScheduleMutation(
|
|
23906
|
+
admitted,
|
|
23907
|
+
completed && changedSchedulingState
|
|
23908
|
+
);
|
|
23909
|
+
}
|
|
23910
|
+
}
|
|
23911
|
+
_aggregateDispatchManyChangeSets(before, changeSets, acceptedActions) {
|
|
23912
|
+
if (changeSets.length === 0) return null;
|
|
23913
|
+
const aggregate = createProjectionChangeSet(
|
|
23914
|
+
this._snapshotProjection(),
|
|
23915
|
+
before
|
|
23916
|
+
);
|
|
23917
|
+
const effects = changeSets.flatMap((changes) => changes.effects ?? []);
|
|
23918
|
+
const warnings = changeSets.flatMap((changes) => changes.warnings ?? []);
|
|
23919
|
+
return {
|
|
23920
|
+
...aggregate,
|
|
23921
|
+
source: {
|
|
23922
|
+
kind: CHANGE_SET_SOURCE_KIND.DISPATCH_MANY,
|
|
23923
|
+
actions: acceptedActions
|
|
23924
|
+
},
|
|
23925
|
+
trackingEvents: changeSets.flatMap(
|
|
23926
|
+
(changes) => changes.trackingEvents
|
|
23927
|
+
),
|
|
23928
|
+
...effects.length === 0 ? {} : { effects },
|
|
23929
|
+
...warnings.length === 0 ? {} : { warnings }
|
|
23930
|
+
};
|
|
23931
|
+
}
|
|
23932
|
+
async _runDispatch(action, options, deferScheduleRevision = false) {
|
|
23408
23933
|
this.assertReady();
|
|
23409
23934
|
if (this._isFork && acknowledgesPersistence(action)) {
|
|
23410
23935
|
return {
|
|
@@ -23533,9 +24058,11 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
23533
24058
|
const hasSubstantiveChanges = changeSetIsSubstantive(result.changes);
|
|
23534
24059
|
if (hasSubstantiveChanges) this._stateRevision++;
|
|
23535
24060
|
if (dispatchChangesSchedulingState(action) && hasSubstantiveChanges) {
|
|
23536
|
-
|
|
23537
|
-
|
|
23538
|
-
|
|
24061
|
+
if (!deferScheduleRevision) {
|
|
24062
|
+
this._recordScheduleMutation(
|
|
24063
|
+
options.skipAutoSchedule !== true && options.skipCriticalPath !== true && resolveRunCriticalPath(action)
|
|
24064
|
+
);
|
|
24065
|
+
}
|
|
23539
24066
|
return { result: toPublicDispatchResult(result), changedState: true };
|
|
23540
24067
|
}
|
|
23541
24068
|
return { result: toPublicDispatchResult(result), changedState: false };
|