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