@outbuild-company/schedule-core 1.32.0 → 1.33.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/build-fingerprint.json +2 -2
- package/dist/cdn/schedule-core.global.js +1 -1
- package/dist/cdn/schedule-core.global.js.map +1 -1
- package/dist/index.cjs +2333 -795
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +308 -193
- package/dist/index.d.ts +308 -193
- package/dist/index.js +2332 -796
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -342,6 +342,7 @@ var DISPATCH_ACTION_KIND = {
|
|
|
342
342
|
CELL_CLIPBOARD_CLEAR: "cell-clipboard-clear",
|
|
343
343
|
CELL_PASTE_COPIED: "cell-paste-copied",
|
|
344
344
|
PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge",
|
|
345
|
+
PERSISTENCE_REBASE: "persistence-rebase",
|
|
345
346
|
BASELINE_APPLY: "baseline-apply",
|
|
346
347
|
PONDERATOR_CRITERION_SET: "ponderator-criterion-set",
|
|
347
348
|
STATUS_CRITERIA_SET: "status-criteria-set",
|
|
@@ -787,6 +788,10 @@ function createScheduleNamespaces(engine) {
|
|
|
787
788
|
acknowledge: (checkpoint) => engine.dispatch({
|
|
788
789
|
kind: DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE,
|
|
789
790
|
checkpoint
|
|
791
|
+
}),
|
|
792
|
+
rebase: (remote) => engine.dispatch({
|
|
793
|
+
kind: DISPATCH_ACTION_KIND.PERSISTENCE_REBASE,
|
|
794
|
+
remote
|
|
790
795
|
})
|
|
791
796
|
},
|
|
792
797
|
history: {
|
|
@@ -2443,6 +2448,7 @@ var ACTIVITY_PROPERTY = {
|
|
|
2443
2448
|
DURATION_HOURS: "durationHours",
|
|
2444
2449
|
CONSTRAINT_TYPE: "constraintType",
|
|
2445
2450
|
CONSTRAINT_DATE: "constraintDate",
|
|
2451
|
+
AUTO_SCHEDULING: "autoScheduling",
|
|
2446
2452
|
CALENDAR_ID: "calendarId",
|
|
2447
2453
|
PROGRESS: "progress",
|
|
2448
2454
|
COST: "cost",
|
|
@@ -2450,7 +2456,9 @@ var ACTIVITY_PROPERTY = {
|
|
|
2450
2456
|
REAL_COST: "realCost",
|
|
2451
2457
|
WORK_HOURS: "workHours",
|
|
2452
2458
|
REAL_WORK_HOURS: "realWorkHours",
|
|
2459
|
+
PONDERATOR: "ponderator",
|
|
2453
2460
|
CORRELATIVE_ID: "correlativeId",
|
|
2461
|
+
UNIQUE_CORRELATIVE_ID: "uniqueCorrelativeId",
|
|
2454
2462
|
CUSTOM_ID: "customId",
|
|
2455
2463
|
NEW_ACTIVITIES_ARRAY: "newActivityIds",
|
|
2456
2464
|
HAS_NEW_ACTIVITIES: "hasNewActivities",
|
|
@@ -6847,6 +6855,21 @@ function findPipelineForColumn(columnName) {
|
|
|
6847
6855
|
}
|
|
6848
6856
|
|
|
6849
6857
|
// src/dispatch/types.ts
|
|
6858
|
+
var PERSISTENCE_REBASE_CONFLICT_KIND = {
|
|
6859
|
+
FIELD: "field",
|
|
6860
|
+
DELETED_REMOTELY: "deleted-remotely",
|
|
6861
|
+
DELETED_LOCALLY: "deleted-locally",
|
|
6862
|
+
PARENT_REMOVED: "parent-removed",
|
|
6863
|
+
BECAME_PARENT_REMOTELY: "became-parent-remotely",
|
|
6864
|
+
BECAME_PARENT_LOCALLY: "became-parent-locally",
|
|
6865
|
+
DUPLICATE_LINK: "duplicate-link",
|
|
6866
|
+
ORDER: "order",
|
|
6867
|
+
LINK_CYCLE: "link-cycle"
|
|
6868
|
+
};
|
|
6869
|
+
var PERSISTENCE_REBASE_ENTITY = {
|
|
6870
|
+
ACTIVITY: "activity",
|
|
6871
|
+
LINK: "link"
|
|
6872
|
+
};
|
|
6850
6873
|
var ACTIVITY_BATCH_MODE = {
|
|
6851
6874
|
APPEND: "append",
|
|
6852
6875
|
REPLACE: "replace"
|
|
@@ -18892,284 +18915,2184 @@ function normalizeBaselinePoint(point, reporter, diagnostic2, isMilestone = fals
|
|
|
18892
18915
|
};
|
|
18893
18916
|
}
|
|
18894
18917
|
|
|
18895
|
-
// src/
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
|
|
18899
|
-
|
|
18900
|
-
|
|
18918
|
+
// src/boundary/backend/constants.ts
|
|
18919
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
18920
|
+
var EXCEPTION_DATE_FORMAT_REGEX = /^(\d{4})[/-](\d{1,2})[/-](\d{1,2})/;
|
|
18921
|
+
var EXCEPTION_TIME_FORMAT_REGEX = /^\d{4}[/-]\d{1,2}[/-]\d{1,2}[ T](\d{1,2}):(\d{2})/;
|
|
18922
|
+
var WEEKDAYS_PER_WEEK = 7;
|
|
18923
|
+
var EXCEPTION_EVERY_TYPE = {
|
|
18924
|
+
DAILY: "DAILY",
|
|
18925
|
+
WEEKLY: "WEEKLY",
|
|
18926
|
+
MONTHLY: "MONTHLY",
|
|
18927
|
+
YEARLY: "YEARLY"
|
|
18928
|
+
};
|
|
18929
|
+
var MONTHS_PER_YEAR = 12;
|
|
18930
|
+
var CALENDAR_FALLBACK_WORKING_DAYS = [false, true, true, true, true, true, false];
|
|
18931
|
+
var WORKING_DAY_TRUE_VALUES = /* @__PURE__ */ new Set([
|
|
18932
|
+
"1",
|
|
18933
|
+
"true"
|
|
18934
|
+
]);
|
|
18935
|
+
var DEFAULT_ACTIVITY_CRETER = ACTIVITY_PONDERATOR_CRITERION.DURATION;
|
|
18936
|
+
var DEFAULT_CONSTRAINT_TYPE = CONSTRAINT_TYPE.ASAP;
|
|
18937
|
+
var CONSTRAINT_LABEL_MAP = {
|
|
18938
|
+
ASAP: CONSTRAINT_TYPE.ASAP,
|
|
18939
|
+
ALAP: CONSTRAINT_TYPE.ALAP,
|
|
18940
|
+
SNET: CONSTRAINT_TYPE.SNET,
|
|
18941
|
+
SNLT: CONSTRAINT_TYPE.SNLT,
|
|
18942
|
+
FNET: CONSTRAINT_TYPE.FNET,
|
|
18943
|
+
FNLT: CONSTRAINT_TYPE.FNLT,
|
|
18944
|
+
MSO: CONSTRAINT_TYPE.MSO,
|
|
18945
|
+
MFO: CONSTRAINT_TYPE.MFO,
|
|
18946
|
+
"AS SOON AS POSSIBLE": CONSTRAINT_TYPE.ASAP,
|
|
18947
|
+
"AS LATE AS POSSIBLE": CONSTRAINT_TYPE.ALAP,
|
|
18948
|
+
"START NO EARLIER THAN": CONSTRAINT_TYPE.SNET,
|
|
18949
|
+
"START NO LATER THAN": CONSTRAINT_TYPE.SNLT,
|
|
18950
|
+
"FINISH NO EARLIER THAN": CONSTRAINT_TYPE.FNET,
|
|
18951
|
+
"FINISH NO LATER THAN": CONSTRAINT_TYPE.FNLT,
|
|
18952
|
+
"MUST START ON": CONSTRAINT_TYPE.MSO,
|
|
18953
|
+
"MUST FINISH ON": CONSTRAINT_TYPE.MFO
|
|
18954
|
+
};
|
|
18955
|
+
|
|
18956
|
+
// src/boundary/backend/normalize-activity.ts
|
|
18957
|
+
function normalizeActivities(rawActivities, context) {
|
|
18958
|
+
const hiddenIds = [];
|
|
18959
|
+
const activities = rawActivities.map((raw) => {
|
|
18960
|
+
const activity = parseActivity(raw, context);
|
|
18961
|
+
if (raw.should_be_showed === false) hiddenIds.push(activity.id);
|
|
18962
|
+
return activity;
|
|
18901
18963
|
});
|
|
18902
|
-
|
|
18903
|
-
|
|
18904
|
-
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
18913
|
-
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
|
|
18918
|
-
|
|
18919
|
-
}
|
|
18920
|
-
const arr = grouped.get(coreId);
|
|
18921
|
-
if (arr) arr.push(p);
|
|
18922
|
-
else grouped.set(coreId, [p]);
|
|
18923
|
-
}
|
|
18924
|
-
const changed2 = [];
|
|
18925
|
-
for (const [coreId, group] of grouped) {
|
|
18926
|
-
const isMilestone = adapter.getActivity(coreId)?.type === ACTIVITY_TYPE.MILESTONE;
|
|
18927
|
-
adapter.setActivityField(
|
|
18928
|
-
coreId,
|
|
18929
|
-
"baselinePoints",
|
|
18930
|
-
group.map(
|
|
18931
|
-
(point) => normalizeBaselinePoint(
|
|
18932
|
-
point,
|
|
18933
|
-
reporter,
|
|
18934
|
-
{
|
|
18935
|
-
record: recordDiagnostic,
|
|
18936
|
-
sampleId: coreId
|
|
18937
|
-
},
|
|
18938
|
-
isMilestone
|
|
18939
|
-
)
|
|
18940
|
-
).filter((point) => point !== null)
|
|
18964
|
+
forceRootProjectToRoot(activities);
|
|
18965
|
+
const activityIds = new Set(activities.map((activity) => activity.id));
|
|
18966
|
+
reparentOrphansToRoot(
|
|
18967
|
+
activities,
|
|
18968
|
+
activityIds,
|
|
18969
|
+
context.reporter,
|
|
18970
|
+
context.recordDiagnostic
|
|
18971
|
+
);
|
|
18972
|
+
return { activities, activityIds, hiddenIds };
|
|
18973
|
+
}
|
|
18974
|
+
function parseConstraintLabel(label, reporter, recordDiagnostic, activityId) {
|
|
18975
|
+
if (!label) return null;
|
|
18976
|
+
const normalized2 = CONSTRAINT_LABEL_MAP[label.toUpperCase()];
|
|
18977
|
+
if (normalized2 === void 0) {
|
|
18978
|
+
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.UNKNOWN_CONSTRAINT, activityId);
|
|
18979
|
+
reporter.warn(
|
|
18980
|
+
`[parseFromBackend] unknown constraint label "${label}" \u2014 defaulting to "asap"`
|
|
18941
18981
|
);
|
|
18942
|
-
|
|
18982
|
+
return DEFAULT_CONSTRAINT_TYPE;
|
|
18943
18983
|
}
|
|
18944
|
-
|
|
18945
|
-
if (grouped.has(id)) return;
|
|
18946
|
-
const existing = a.baselinePoints;
|
|
18947
|
-
if (Array.isArray(existing) && existing.length > 0) {
|
|
18948
|
-
adapter.setActivityField(id, "baselinePoints", []);
|
|
18949
|
-
changed2.push(id);
|
|
18950
|
-
}
|
|
18951
|
-
});
|
|
18952
|
-
return changed2;
|
|
18984
|
+
return normalized2;
|
|
18953
18985
|
}
|
|
18954
|
-
|
|
18955
|
-
|
|
18956
|
-
|
|
18957
|
-
|
|
18958
|
-
|
|
18959
|
-
|
|
18960
|
-
|
|
18961
|
-
|
|
18962
|
-
|
|
18963
|
-
|
|
18964
|
-
workHours: active.workHours
|
|
18965
|
-
};
|
|
18986
|
+
function resolveConstraint(raw, reporter, recordDiagnostic) {
|
|
18987
|
+
const constraintType = parseConstraintLabel(
|
|
18988
|
+
raw.constraint,
|
|
18989
|
+
reporter,
|
|
18990
|
+
recordDiagnostic,
|
|
18991
|
+
String(raw.unique_id)
|
|
18992
|
+
);
|
|
18993
|
+
const constraintTakesDate = constraintType !== CONSTRAINT_TYPE.ASAP && constraintType !== CONSTRAINT_TYPE.ALAP;
|
|
18994
|
+
const constraintDate = constraintTakesDate && raw.constraint_date ? parseBackendDate(raw.constraint_date) : null;
|
|
18995
|
+
return { constraintType, constraintDate };
|
|
18966
18996
|
}
|
|
18967
|
-
function
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
|
|
18971
|
-
if (!snapshot) {
|
|
18972
|
-
if (activity.baselineSnapshot !== null) {
|
|
18973
|
-
state.setActivityField(id, "baselineSnapshot", null);
|
|
18974
|
-
changed2.push(id);
|
|
18975
|
-
}
|
|
18976
|
-
return;
|
|
18977
|
-
}
|
|
18978
|
-
state.setActivityField(id, "baselineSnapshot", snapshot);
|
|
18979
|
-
changed2.push(id);
|
|
18980
|
-
});
|
|
18981
|
-
return changed2;
|
|
18997
|
+
function resolveCalendarId(rawCalendarId, knownCalendarIds, defaultCalendarId) {
|
|
18998
|
+
if (rawCalendarId == null) return defaultCalendarId;
|
|
18999
|
+
const coerced = String(rawCalendarId);
|
|
19000
|
+
return knownCalendarIds.has(coerced) ? coerced : defaultCalendarId;
|
|
18982
19001
|
}
|
|
18983
|
-
|
|
18984
|
-
|
|
18985
|
-
|
|
18986
|
-
|
|
18987
|
-
|
|
18988
|
-
|
|
18989
|
-
|
|
18990
|
-
|
|
18991
|
-
|
|
18992
|
-
)) {
|
|
18993
|
-
changed2.add(id);
|
|
19002
|
+
function parseNewActivityIds(raw) {
|
|
19003
|
+
if (raw == null || raw === "") return [];
|
|
19004
|
+
if (Array.isArray(raw)) return raw.map(String);
|
|
19005
|
+
if (typeof raw !== "string") return [];
|
|
19006
|
+
try {
|
|
19007
|
+
const parsed2 = JSON.parse(raw);
|
|
19008
|
+
return Array.isArray(parsed2) ? parsed2.map(String) : [];
|
|
19009
|
+
} catch {
|
|
19010
|
+
return [];
|
|
18994
19011
|
}
|
|
18995
|
-
|
|
18996
|
-
|
|
18997
|
-
|
|
19012
|
+
}
|
|
19013
|
+
function hasUsableId(entry) {
|
|
19014
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
19015
|
+
if (!("id" in entry)) return false;
|
|
19016
|
+
const value = entry.id;
|
|
19017
|
+
return typeof value === "string" || typeof value === "number";
|
|
19018
|
+
}
|
|
19019
|
+
function parseIdList(raw) {
|
|
19020
|
+
if (!Array.isArray(raw)) return [];
|
|
19021
|
+
const ids = [];
|
|
19022
|
+
for (const entry of raw) {
|
|
19023
|
+
if (hasUsableId(entry)) ids.push(String(entry.id));
|
|
18998
19024
|
}
|
|
18999
|
-
|
|
19000
|
-
for (const id of emitRealCost(state)) changed2.add(id);
|
|
19001
|
-
return [...changed2];
|
|
19025
|
+
return ids;
|
|
19002
19026
|
}
|
|
19003
|
-
|
|
19004
|
-
|
|
19005
|
-
function recomputeAllPonderators(criterion, adapter) {
|
|
19006
|
-
const basisById = /* @__PURE__ */ new Map();
|
|
19007
|
-
adapter.forEachActivity((_a, id) => {
|
|
19008
|
-
adapter.setActivityField(id, COLUMN.PONDERATOR, 0);
|
|
19009
|
-
});
|
|
19010
|
-
adapter.forEachActivity((_a, id) => {
|
|
19011
|
-
if (adapter.getChildren(id).length > 0) {
|
|
19012
|
-
recomputePonderatorsForParent(String(id), criterion, adapter, basisById);
|
|
19013
|
-
}
|
|
19014
|
-
});
|
|
19027
|
+
function parseParentId(rawParentId) {
|
|
19028
|
+
return rawParentId === ROOT_PARENT_ID ? null : String(rawParentId);
|
|
19015
19029
|
}
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
if (deps.now) {
|
|
19022
|
-
runExpectedProgressBase(
|
|
19023
|
-
deps.adapter,
|
|
19024
|
-
deps.now,
|
|
19025
|
-
deps.defaultBaseCalendarId ?? null
|
|
19026
|
-
);
|
|
19027
|
-
applyExpectedProgressLive(deps.adapter, deps.now);
|
|
19030
|
+
function normalizePersistedActivityId(rawId) {
|
|
19031
|
+
if (rawId == null) return null;
|
|
19032
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19033
|
+
if (!isValidIdentity) {
|
|
19034
|
+
throw new Error(`invalid activity proplannerId ${String(rawId)}`);
|
|
19028
19035
|
}
|
|
19029
|
-
|
|
19030
|
-
if (deps.now) applyStatusPass(deps.adapter, deps.sector.statusCriteria);
|
|
19036
|
+
return rawId;
|
|
19031
19037
|
}
|
|
19032
|
-
|
|
19033
|
-
|
|
19034
|
-
|
|
19035
|
-
|
|
19036
|
-
|
|
19037
|
-
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
|
|
19045
|
-
point.workHours,
|
|
19046
|
-
point.baseCalendarId,
|
|
19047
|
-
point.hoursPerDay,
|
|
19048
|
-
point.hoursPerWeek
|
|
19049
|
-
] : [String(id), null];
|
|
19050
|
-
})
|
|
19038
|
+
function parseProgress(raw, reporter, recordDiagnostic, activityId) {
|
|
19039
|
+
const progress = Number(raw);
|
|
19040
|
+
if (!Number.isFinite(progress)) {
|
|
19041
|
+
throw new TypeError(`invalid activity progress ${String(raw)}`);
|
|
19042
|
+
}
|
|
19043
|
+
const isBelowNotStarted = progress < NOT_STARTED_PROGRESS;
|
|
19044
|
+
const isAboveCompleted = progress > MAX_TOLERATED_PROGRESS;
|
|
19045
|
+
const isWithinRange = !isBelowNotStarted && !isAboveCompleted;
|
|
19046
|
+
if (isWithinRange) return progress;
|
|
19047
|
+
const clamped = isBelowNotStarted ? NOT_STARTED_PROGRESS : COMPLETED_PROGRESS;
|
|
19048
|
+
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.PROGRESS_OUT_OF_RANGE, activityId);
|
|
19049
|
+
reporter.warn(
|
|
19050
|
+
`[parseFromBackend] activity "${activityId}" progress ${String(raw)} is out of range \u2014 clamping to ${clamped}`
|
|
19051
19051
|
);
|
|
19052
|
+
return clamped;
|
|
19052
19053
|
}
|
|
19053
|
-
|
|
19054
|
-
|
|
19055
|
-
|
|
19056
|
-
|
|
19054
|
+
function parseCriticality(raw, reporter) {
|
|
19055
|
+
if (typeof raw.is_critical === "boolean") return raw.is_critical;
|
|
19056
|
+
reporter.error(
|
|
19057
|
+
"[parseFromBackend] invalid activity is_critical \u2014 defaulting to false",
|
|
19058
|
+
new TypeError(
|
|
19059
|
+
`activity "${raw.unique_id}" received ${typeof raw.is_critical}`
|
|
19060
|
+
)
|
|
19057
19061
|
);
|
|
19058
|
-
|
|
19059
|
-
|
|
19060
|
-
|
|
19061
|
-
|
|
19062
|
-
|
|
19063
|
-
|
|
19064
|
-
|
|
19065
|
-
|
|
19062
|
+
return false;
|
|
19063
|
+
}
|
|
19064
|
+
function parseBaselinePoints(raw, reporter, recordDiagnostic, activityId, isMilestone) {
|
|
19065
|
+
if (!Array.isArray(raw)) return [];
|
|
19066
|
+
return raw.map(
|
|
19067
|
+
(point) => normalizeBaselinePoint(
|
|
19068
|
+
point,
|
|
19069
|
+
reporter,
|
|
19070
|
+
{
|
|
19071
|
+
record: recordDiagnostic,
|
|
19072
|
+
sampleId: activityId
|
|
19073
|
+
},
|
|
19074
|
+
isMilestone
|
|
19075
|
+
)
|
|
19076
|
+
).filter((point) => point !== null);
|
|
19077
|
+
}
|
|
19078
|
+
function parseActivity(raw, context) {
|
|
19079
|
+
const calendarId = resolveCalendarId(
|
|
19080
|
+
raw.calendarId,
|
|
19081
|
+
context.knownCalendarIds,
|
|
19082
|
+
context.defaultCalendarId
|
|
19066
19083
|
);
|
|
19067
|
-
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
19071
|
-
|
|
19072
|
-
|
|
19073
|
-
|
|
19074
|
-
}
|
|
19084
|
+
const inheritedDefaultCalendar = raw.calendarId == null || !context.knownCalendarIds.has(String(raw.calendarId));
|
|
19085
|
+
const progress = parseProgress(
|
|
19086
|
+
raw.progress,
|
|
19087
|
+
context.reporter,
|
|
19088
|
+
context.recordDiagnostic,
|
|
19089
|
+
String(raw.unique_id)
|
|
19090
|
+
);
|
|
19091
|
+
const { constraintType, constraintDate } = resolveConstraint(
|
|
19092
|
+
raw,
|
|
19093
|
+
context.reporter,
|
|
19094
|
+
context.recordDiagnostic
|
|
19095
|
+
);
|
|
19096
|
+
const isMilestone = raw.type === ACTIVITY_TYPE.MILESTONE;
|
|
19097
|
+
const startDate = parseBackendDate(raw.start_date);
|
|
19098
|
+
const endDate = isMilestone ? startDate : parseBackendDate(raw.end_date);
|
|
19099
|
+
const durationDays = isMilestone ? 0 : Number(raw.duration);
|
|
19100
|
+
const isCritical = parseCriticality(raw, context.reporter);
|
|
19075
19101
|
return {
|
|
19076
|
-
|
|
19077
|
-
|
|
19078
|
-
|
|
19079
|
-
|
|
19080
|
-
|
|
19081
|
-
|
|
19082
|
-
|
|
19083
|
-
|
|
19102
|
+
id: String(raw.unique_id),
|
|
19103
|
+
proplannerId: normalizePersistedActivityId(raw.id),
|
|
19104
|
+
parentId: parseParentId(raw.parent_id),
|
|
19105
|
+
name: raw.name,
|
|
19106
|
+
description: raw.description ?? "",
|
|
19107
|
+
type: raw.type,
|
|
19108
|
+
startDate,
|
|
19109
|
+
endDate,
|
|
19110
|
+
durationHours: durationToHours(durationDays, context.hoursPerDay),
|
|
19111
|
+
constraintType,
|
|
19112
|
+
constraintDate,
|
|
19113
|
+
autoScheduling: isAutoSchedulingEnabledForProgress(progress),
|
|
19114
|
+
calendarId,
|
|
19115
|
+
assignedDefaultCalendar: calendarId != null && inheritedDefaultCalendar,
|
|
19116
|
+
dateOrigin: raw.date_origin == null ? null : parseBackendDate(raw.date_origin),
|
|
19117
|
+
progress,
|
|
19118
|
+
cost: Number(raw.cost),
|
|
19119
|
+
usedCost: Number(raw.used_cost ?? 0),
|
|
19120
|
+
realCost: Number(raw.real_cost ?? 0),
|
|
19121
|
+
workHours: Number(raw.hhWorkTime),
|
|
19122
|
+
realWorkHours: Number(raw.real_work ?? 0),
|
|
19123
|
+
ponderator: Number(raw.ponderator ?? 0),
|
|
19124
|
+
hasCustomPonderator: raw.hasCustomPonderator,
|
|
19125
|
+
correlativeId: raw.correlative_id,
|
|
19126
|
+
uniqueCorrelativeId: raw.unique_correlative_id,
|
|
19127
|
+
customId: normalizeCustomId(raw),
|
|
19128
|
+
subcontractId: raw.subcontractId,
|
|
19129
|
+
isLookahead: raw.isOnLookahead,
|
|
19130
|
+
hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
|
|
19131
|
+
newActivityIds: parseNewActivityIds(raw.newActivitiesArray),
|
|
19132
|
+
hasNewActivities: raw.hasNewActivities ?? false,
|
|
19133
|
+
...raw.isNewActivity === true ? { isNewActivity: true } : {},
|
|
19134
|
+
pendingRequestIds: parseIdList(raw.pendingRequests),
|
|
19135
|
+
responsableIds: parseIdList(raw.responsables),
|
|
19136
|
+
tagIds: parseIdList(raw.tags),
|
|
19137
|
+
baselinePoints: parseBaselinePoints(
|
|
19138
|
+
raw.baseline_points,
|
|
19139
|
+
context.reporter,
|
|
19140
|
+
context.recordDiagnostic,
|
|
19141
|
+
String(raw.unique_id),
|
|
19142
|
+
isMilestone
|
|
19143
|
+
),
|
|
19144
|
+
baselineSnapshot: null,
|
|
19145
|
+
baselineVarianceDays: null,
|
|
19146
|
+
projectBaselineVarianceDays: null,
|
|
19147
|
+
primaryBaselineVarianceDays: null,
|
|
19148
|
+
recursiveDurationHours: durationToHours(
|
|
19149
|
+
Number(raw.sumOfDurationRecursively ?? 0),
|
|
19150
|
+
context.hoursPerDay
|
|
19151
|
+
),
|
|
19152
|
+
calendarDuration: computeCalendarDuration({
|
|
19153
|
+
type: raw.type,
|
|
19154
|
+
startDate,
|
|
19155
|
+
endDate
|
|
19156
|
+
}),
|
|
19157
|
+
isCritical,
|
|
19158
|
+
freeSlackHours: null,
|
|
19159
|
+
expectedProgress: null,
|
|
19160
|
+
expectedProgressBaseline: null,
|
|
19161
|
+
status: null,
|
|
19162
|
+
criticalPath: null
|
|
19084
19163
|
};
|
|
19085
19164
|
}
|
|
19086
|
-
|
|
19087
|
-
|
|
19088
|
-
|
|
19089
|
-
|
|
19090
|
-
|
|
19165
|
+
function normalizeCustomId(raw) {
|
|
19166
|
+
return raw.has_childs ? null : raw.custom_id;
|
|
19167
|
+
}
|
|
19168
|
+
function forceRootProjectToRoot(activities) {
|
|
19169
|
+
const rootProject = activities.find(
|
|
19170
|
+
(activity) => activity.correlativeId === 0 && activity.type === ACTIVITY_TYPE.PROJECT
|
|
19091
19171
|
);
|
|
19172
|
+
if (rootProject) rootProject.parentId = null;
|
|
19092
19173
|
}
|
|
19093
|
-
|
|
19094
|
-
|
|
19095
|
-
|
|
19174
|
+
function reparentOrphansToRoot(activities, ids, reporter, recordDiagnostic) {
|
|
19175
|
+
for (const activity of activities) {
|
|
19176
|
+
if (activity.parentId === null) continue;
|
|
19177
|
+
if (!ids.has(activity.parentId)) {
|
|
19178
|
+
reporter.warn(
|
|
19179
|
+
`[parseFromBackend] activity "${activity.id}" references unknown parent "${activity.parentId}" \u2014 re-parenting to root (null)`
|
|
19180
|
+
);
|
|
19181
|
+
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.ORPHAN_ACTIVITY, activity.id);
|
|
19182
|
+
activity.parentId = null;
|
|
19183
|
+
}
|
|
19096
19184
|
}
|
|
19097
|
-
const allIds = new Set(deps.adapter.getAllIds());
|
|
19098
|
-
const beforeSnap = await snapshotActivities(deps.adapter, allIds);
|
|
19099
|
-
recomputeBaselineWeightedFields(deps, action.criterion);
|
|
19100
|
-
deps.sector.activityCreter = action.criterion;
|
|
19101
|
-
return {
|
|
19102
|
-
ok: true,
|
|
19103
|
-
changes: await assembleChangeSet(deps.adapter, {
|
|
19104
|
-
source: action,
|
|
19105
|
-
beforeSnap,
|
|
19106
|
-
touchedIds: allIds,
|
|
19107
|
-
scheduledIds: [],
|
|
19108
|
-
hoursPerDay: deps.sector.hoursPerDay
|
|
19109
|
-
})
|
|
19110
|
-
};
|
|
19111
19185
|
}
|
|
19112
19186
|
|
|
19113
|
-
// src/
|
|
19114
|
-
|
|
19115
|
-
|
|
19116
|
-
const
|
|
19117
|
-
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
return {
|
|
19121
|
-
ok: true,
|
|
19122
|
-
changes: await assembleChangeSet(deps.adapter, {
|
|
19123
|
-
source: action,
|
|
19124
|
-
beforeSnap,
|
|
19125
|
-
touchedIds: changedIds,
|
|
19126
|
-
scheduledIds: [],
|
|
19127
|
-
hoursPerDay: deps.sector.hoursPerDay
|
|
19128
|
-
})
|
|
19129
|
-
};
|
|
19187
|
+
// src/boundary/backend/normalize-link.ts
|
|
19188
|
+
function resolveLinkType(rawType) {
|
|
19189
|
+
if (typeof rawType !== "string") return null;
|
|
19190
|
+
const fromLabel = parseLinkTypeCode(rawType);
|
|
19191
|
+
if (fromLabel) return fromLabel;
|
|
19192
|
+
if (isLinkType(rawType)) return rawType;
|
|
19193
|
+
return null;
|
|
19130
19194
|
}
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19135
|
-
|
|
19136
|
-
for (const activity of deps.adapter.getAllActivities()) {
|
|
19137
|
-
if (!shouldReconcile(activity, deps)) continue;
|
|
19138
|
-
const calendar = deps.ctx.calendars.getCalendar(activity.calendarId);
|
|
19139
|
-
if (!calendar) continue;
|
|
19140
|
-
const startReference = calendar.isWorkTime(
|
|
19141
|
-
activity.startDate,
|
|
19142
|
-
CALENDAR_UNIT.HOUR
|
|
19143
|
-
) ? startOfUtcDay4(activity.startDate) : activity.startDate;
|
|
19144
|
-
const startDate = calendar.getClosestWorkTime({
|
|
19145
|
-
date: startReference,
|
|
19146
|
-
direction: WORK_TIME_DIRECTION.FUTURE,
|
|
19147
|
-
unit: CALENDAR_UNIT.HOUR
|
|
19148
|
-
});
|
|
19149
|
-
const endDate = calendar.calculateEndDate(
|
|
19150
|
-
startDate,
|
|
19151
|
-
activity.durationHours
|
|
19152
|
-
);
|
|
19153
|
-
if (sameInstant(startDate, activity.startDate) && sameInstant(endDate, activity.endDate)) {
|
|
19154
|
-
continue;
|
|
19155
|
-
}
|
|
19156
|
-
const activityId = String(activity.id);
|
|
19157
|
-
beforeSnap.set(activityId, structuredCloneActivity(activity));
|
|
19158
|
-
touchedIds.add(activityId);
|
|
19159
|
-
deps.adapter.setActivityFields(activityId, { startDate, endDate });
|
|
19195
|
+
function normalizePersistedLinkId(rawId) {
|
|
19196
|
+
if (rawId == null) return void 0;
|
|
19197
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19198
|
+
if (!isValidIdentity) {
|
|
19199
|
+
throw new Error(`invalid link proplannerId ${String(rawId)}`);
|
|
19160
19200
|
}
|
|
19161
|
-
|
|
19162
|
-
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
|
|
19169
|
-
|
|
19170
|
-
|
|
19171
|
-
|
|
19172
|
-
|
|
19201
|
+
return rawId;
|
|
19202
|
+
}
|
|
19203
|
+
function normalizeLinks(rawLinks, context) {
|
|
19204
|
+
const parsedLinks = [];
|
|
19205
|
+
for (const rawLink of rawLinks) {
|
|
19206
|
+
try {
|
|
19207
|
+
parsedLinks.push(
|
|
19208
|
+
parseLink(rawLink, context.knownActivityIds, context.hoursPerDay)
|
|
19209
|
+
);
|
|
19210
|
+
} catch (parseError2) {
|
|
19211
|
+
context.recordDiagnostic(
|
|
19212
|
+
LOAD_DIAGNOSTIC_CODE.INVALID_LINK,
|
|
19213
|
+
String(rawLink.unique_id ?? rawLink.id)
|
|
19214
|
+
);
|
|
19215
|
+
context.reporter.warn(
|
|
19216
|
+
"[parseFromBackend] dropping invalid link",
|
|
19217
|
+
parseError2
|
|
19218
|
+
);
|
|
19219
|
+
}
|
|
19220
|
+
}
|
|
19221
|
+
return dedupeLinksBySignature(parsedLinks, context.recordDiagnostic);
|
|
19222
|
+
}
|
|
19223
|
+
function parseLink(link, activityIds, hoursPerDay) {
|
|
19224
|
+
const id = String(link.unique_id ?? link.id);
|
|
19225
|
+
const source = String(link.source_id ?? link.source ?? "");
|
|
19226
|
+
const target = String(link.target_id ?? link.target ?? "");
|
|
19227
|
+
if (!activityIds.has(source)) {
|
|
19228
|
+
throw new Error(
|
|
19229
|
+
`[parseFromBackend] link "${id}" source "${source}" does not exist`
|
|
19230
|
+
);
|
|
19231
|
+
}
|
|
19232
|
+
if (!activityIds.has(target)) {
|
|
19233
|
+
throw new Error(
|
|
19234
|
+
`[parseFromBackend] link "${id}" target "${target}" does not exist`
|
|
19235
|
+
);
|
|
19236
|
+
}
|
|
19237
|
+
const type = resolveLinkType(link.type);
|
|
19238
|
+
if (type === null) {
|
|
19239
|
+
throw new Error(
|
|
19240
|
+
`[parseFromBackend] link "${id}" has invalid type "${String(link.type)}"`
|
|
19241
|
+
);
|
|
19242
|
+
}
|
|
19243
|
+
const ganttIdRaw = link.ganttId;
|
|
19244
|
+
const sectorIdRaw = link.sectorId;
|
|
19245
|
+
const proplannerId = normalizePersistedLinkId(link.id);
|
|
19246
|
+
return {
|
|
19247
|
+
id,
|
|
19248
|
+
source,
|
|
19249
|
+
target,
|
|
19250
|
+
type,
|
|
19251
|
+
lag: lagDaysToHours(Number(link.lag ?? 0), hoursPerDay),
|
|
19252
|
+
freeSlackHours: null,
|
|
19253
|
+
isCritical: false,
|
|
19254
|
+
...typeof ganttIdRaw === "number" ? { ganttId: ganttIdRaw } : {},
|
|
19255
|
+
...typeof sectorIdRaw === "number" ? { sectorId: sectorIdRaw } : {},
|
|
19256
|
+
...proplannerId === void 0 ? {} : { proplannerId }
|
|
19257
|
+
};
|
|
19258
|
+
}
|
|
19259
|
+
function dedupeLinksBySignature(links, recordDiagnostic) {
|
|
19260
|
+
const bySignature = /* @__PURE__ */ new Map();
|
|
19261
|
+
for (const link of links) {
|
|
19262
|
+
const signature = `${link.source}-${link.target}-${link.type}`;
|
|
19263
|
+
const existing = bySignature.get(signature);
|
|
19264
|
+
if (!existing) {
|
|
19265
|
+
bySignature.set(signature, link);
|
|
19266
|
+
continue;
|
|
19267
|
+
}
|
|
19268
|
+
const incomingWins = linkIdAsNumber(link) > linkIdAsNumber(existing);
|
|
19269
|
+
const discarded = incomingWins ? existing : link;
|
|
19270
|
+
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.DUPLICATE_LINK, discarded.id);
|
|
19271
|
+
if (incomingWins) {
|
|
19272
|
+
bySignature.set(signature, link);
|
|
19273
|
+
}
|
|
19274
|
+
}
|
|
19275
|
+
return [...bySignature.values()];
|
|
19276
|
+
}
|
|
19277
|
+
function linkIdAsNumber(link) {
|
|
19278
|
+
const numeric = Number(link.id);
|
|
19279
|
+
return Number.isFinite(numeric) ? numeric : -Infinity;
|
|
19280
|
+
}
|
|
19281
|
+
|
|
19282
|
+
// src/boundary/save/field-policy.ts
|
|
19283
|
+
var SAVE_FIELD_POLICY = {
|
|
19284
|
+
TRACKED: "tracked",
|
|
19285
|
+
IGNORED: "ignored"
|
|
19286
|
+
};
|
|
19287
|
+
|
|
19288
|
+
// src/boundary/save/activity-changes.ts
|
|
19289
|
+
var ACTIVITY_SAVE_FIELD_POLICY = {
|
|
19290
|
+
id: SAVE_FIELD_POLICY.IGNORED,
|
|
19291
|
+
proplannerId: SAVE_FIELD_POLICY.IGNORED,
|
|
19292
|
+
parentId: SAVE_FIELD_POLICY.TRACKED,
|
|
19293
|
+
name: SAVE_FIELD_POLICY.TRACKED,
|
|
19294
|
+
description: SAVE_FIELD_POLICY.TRACKED,
|
|
19295
|
+
type: SAVE_FIELD_POLICY.TRACKED,
|
|
19296
|
+
startDate: SAVE_FIELD_POLICY.TRACKED,
|
|
19297
|
+
endDate: SAVE_FIELD_POLICY.TRACKED,
|
|
19298
|
+
durationHours: SAVE_FIELD_POLICY.TRACKED,
|
|
19299
|
+
constraintType: SAVE_FIELD_POLICY.TRACKED,
|
|
19300
|
+
constraintDate: SAVE_FIELD_POLICY.TRACKED,
|
|
19301
|
+
autoScheduling: SAVE_FIELD_POLICY.TRACKED,
|
|
19302
|
+
calendarId: SAVE_FIELD_POLICY.TRACKED,
|
|
19303
|
+
assignedDefaultCalendar: SAVE_FIELD_POLICY.IGNORED,
|
|
19304
|
+
dateOrigin: SAVE_FIELD_POLICY.TRACKED,
|
|
19305
|
+
progress: SAVE_FIELD_POLICY.TRACKED,
|
|
19306
|
+
cost: SAVE_FIELD_POLICY.TRACKED,
|
|
19307
|
+
usedCost: SAVE_FIELD_POLICY.TRACKED,
|
|
19308
|
+
realCost: SAVE_FIELD_POLICY.TRACKED,
|
|
19309
|
+
workHours: SAVE_FIELD_POLICY.TRACKED,
|
|
19310
|
+
realWorkHours: SAVE_FIELD_POLICY.TRACKED,
|
|
19311
|
+
ponderator: SAVE_FIELD_POLICY.TRACKED,
|
|
19312
|
+
hasCustomPonderator: SAVE_FIELD_POLICY.TRACKED,
|
|
19313
|
+
correlativeId: SAVE_FIELD_POLICY.TRACKED,
|
|
19314
|
+
uniqueCorrelativeId: SAVE_FIELD_POLICY.TRACKED,
|
|
19315
|
+
customId: SAVE_FIELD_POLICY.TRACKED,
|
|
19316
|
+
subcontractId: SAVE_FIELD_POLICY.TRACKED,
|
|
19317
|
+
isLookahead: SAVE_FIELD_POLICY.TRACKED,
|
|
19318
|
+
hasLookaheadTasks: SAVE_FIELD_POLICY.IGNORED,
|
|
19319
|
+
newActivityIds: SAVE_FIELD_POLICY.TRACKED,
|
|
19320
|
+
hasNewActivities: SAVE_FIELD_POLICY.TRACKED,
|
|
19321
|
+
isNewActivity: SAVE_FIELD_POLICY.IGNORED,
|
|
19322
|
+
pendingRequestIds: SAVE_FIELD_POLICY.IGNORED,
|
|
19323
|
+
responsableIds: SAVE_FIELD_POLICY.TRACKED,
|
|
19324
|
+
tagIds: SAVE_FIELD_POLICY.TRACKED,
|
|
19325
|
+
baselinePoints: SAVE_FIELD_POLICY.IGNORED,
|
|
19326
|
+
baselineSnapshot: SAVE_FIELD_POLICY.IGNORED,
|
|
19327
|
+
recursiveDurationHours: SAVE_FIELD_POLICY.IGNORED,
|
|
19328
|
+
isCritical: SAVE_FIELD_POLICY.IGNORED,
|
|
19329
|
+
freeSlackHours: SAVE_FIELD_POLICY.IGNORED,
|
|
19330
|
+
calendarDuration: SAVE_FIELD_POLICY.IGNORED,
|
|
19331
|
+
baselineVarianceDays: SAVE_FIELD_POLICY.IGNORED,
|
|
19332
|
+
projectBaselineVarianceDays: SAVE_FIELD_POLICY.IGNORED,
|
|
19333
|
+
primaryBaselineVarianceDays: SAVE_FIELD_POLICY.IGNORED,
|
|
19334
|
+
expectedProgress: SAVE_FIELD_POLICY.IGNORED,
|
|
19335
|
+
expectedProgressBaseline: SAVE_FIELD_POLICY.IGNORED,
|
|
19336
|
+
status: SAVE_FIELD_POLICY.IGNORED,
|
|
19337
|
+
criticalPath: SAVE_FIELD_POLICY.IGNORED,
|
|
19338
|
+
p6: SAVE_FIELD_POLICY.IGNORED
|
|
19339
|
+
};
|
|
19340
|
+
var PERSISTABLE_ACTIVITY_FIELDS = Object.keys(ACTIVITY_SAVE_FIELD_POLICY).filter((field) => {
|
|
19341
|
+
return ACTIVITY_SAVE_FIELD_POLICY[field] === SAVE_FIELD_POLICY.TRACKED;
|
|
19342
|
+
});
|
|
19343
|
+
var DATE_FIELDS = /* @__PURE__ */ new Set([
|
|
19344
|
+
"startDate",
|
|
19345
|
+
"endDate",
|
|
19346
|
+
"constraintDate",
|
|
19347
|
+
"dateOrigin"
|
|
19348
|
+
]);
|
|
19349
|
+
var ID_ARRAY_FIELDS = /* @__PURE__ */ new Set([
|
|
19350
|
+
"newActivityIds",
|
|
19351
|
+
"responsableIds",
|
|
19352
|
+
"tagIds"
|
|
19353
|
+
]);
|
|
19354
|
+
function projectPersistableActivity(activity) {
|
|
19355
|
+
const entries = PERSISTABLE_ACTIVITY_FIELDS.map(
|
|
19356
|
+
(field) => [field, snapshotFieldValue(field, activity[field])]
|
|
19357
|
+
);
|
|
19358
|
+
return Object.fromEntries(entries);
|
|
19359
|
+
}
|
|
19360
|
+
function snapshotFieldValue(field, value) {
|
|
19361
|
+
if (DATE_FIELDS.has(field)) {
|
|
19362
|
+
return value instanceof Date ? new Date(value) : null;
|
|
19363
|
+
}
|
|
19364
|
+
if (ID_ARRAY_FIELDS.has(field)) {
|
|
19365
|
+
return Array.isArray(value) ? [...value] : [];
|
|
19366
|
+
}
|
|
19367
|
+
return value;
|
|
19368
|
+
}
|
|
19369
|
+
function activityChangedSince(current, saved) {
|
|
19370
|
+
return changedPersistableActivityFields(current, saved).length > 0;
|
|
19371
|
+
}
|
|
19372
|
+
function changedPersistableActivityFields(current, saved) {
|
|
19373
|
+
return PERSISTABLE_ACTIVITY_FIELDS.filter(
|
|
19374
|
+
(field) => !fieldEquals(field, current[field], saved[field])
|
|
19375
|
+
);
|
|
19376
|
+
}
|
|
19377
|
+
function fieldEquals(field, currentValue, savedValue) {
|
|
19378
|
+
if (DATE_FIELDS.has(field)) return dateEquals(currentValue, savedValue);
|
|
19379
|
+
if (ID_ARRAY_FIELDS.has(field))
|
|
19380
|
+
return idArrayEquals(currentValue, savedValue);
|
|
19381
|
+
return (currentValue ?? null) === (savedValue ?? null);
|
|
19382
|
+
}
|
|
19383
|
+
function dateEquals(current, saved) {
|
|
19384
|
+
const currentTime = current instanceof Date ? current.getTime() : null;
|
|
19385
|
+
const savedTime = saved instanceof Date ? saved.getTime() : null;
|
|
19386
|
+
return currentTime === savedTime;
|
|
19387
|
+
}
|
|
19388
|
+
function idArrayEquals(current, saved) {
|
|
19389
|
+
const currentIds = new Set(Array.isArray(current) ? current.map(String) : []);
|
|
19390
|
+
const savedIds = new Set(Array.isArray(saved) ? saved.map(String) : []);
|
|
19391
|
+
if (currentIds.size !== savedIds.size) return false;
|
|
19392
|
+
for (const currentId of currentIds) {
|
|
19393
|
+
if (!savedIds.has(currentId)) return false;
|
|
19394
|
+
}
|
|
19395
|
+
return true;
|
|
19396
|
+
}
|
|
19397
|
+
|
|
19398
|
+
// src/boundary/save/link-changes.ts
|
|
19399
|
+
var LINK_SAVE_FIELD_POLICY = {
|
|
19400
|
+
id: SAVE_FIELD_POLICY.IGNORED,
|
|
19401
|
+
source: SAVE_FIELD_POLICY.TRACKED,
|
|
19402
|
+
target: SAVE_FIELD_POLICY.TRACKED,
|
|
19403
|
+
type: SAVE_FIELD_POLICY.TRACKED,
|
|
19404
|
+
lag: SAVE_FIELD_POLICY.TRACKED,
|
|
19405
|
+
freeSlackHours: SAVE_FIELD_POLICY.IGNORED,
|
|
19406
|
+
isCritical: SAVE_FIELD_POLICY.IGNORED,
|
|
19407
|
+
ganttId: SAVE_FIELD_POLICY.IGNORED,
|
|
19408
|
+
sectorId: SAVE_FIELD_POLICY.IGNORED,
|
|
19409
|
+
proplannerId: SAVE_FIELD_POLICY.IGNORED
|
|
19410
|
+
};
|
|
19411
|
+
var TRACKED_LINK_FIELDS = Object.keys(LINK_SAVE_FIELD_POLICY).filter((field) => {
|
|
19412
|
+
return LINK_SAVE_FIELD_POLICY[field] === SAVE_FIELD_POLICY.TRACKED;
|
|
19413
|
+
});
|
|
19414
|
+
function linkChangedSince(baseline, current) {
|
|
19415
|
+
return TRACKED_LINK_FIELDS.some(
|
|
19416
|
+
(field) => baseline[field] !== current[field]
|
|
19417
|
+
);
|
|
19418
|
+
}
|
|
19419
|
+
function checkNoUpdatedLinks(baseline, current) {
|
|
19420
|
+
const baselineById = new Map(
|
|
19421
|
+
baseline.map((link) => [String(link.id), link])
|
|
19422
|
+
);
|
|
19423
|
+
return current.filter((link) => {
|
|
19424
|
+
const base = baselineById.get(String(link.id));
|
|
19425
|
+
if (!base) return false;
|
|
19426
|
+
return linkChangedSince(base, link);
|
|
19427
|
+
});
|
|
19428
|
+
}
|
|
19429
|
+
|
|
19430
|
+
// src/dispatch/handlers/persistence-rebase-topology.ts
|
|
19431
|
+
var parentKeyOf2 = (parentId) => isRootParent(parentId) ? null : String(parentId);
|
|
19432
|
+
function mergedParents(input) {
|
|
19433
|
+
const parents = /* @__PURE__ */ new Map();
|
|
19434
|
+
for (const local of input.localActivities) {
|
|
19435
|
+
parents.set(String(local.id), parentKeyOf2(local.parentId));
|
|
19436
|
+
}
|
|
19437
|
+
for (const created of input.createActivities) {
|
|
19438
|
+
parents.set(String(created.id), parentKeyOf2(created.parentId));
|
|
19439
|
+
}
|
|
19440
|
+
for (const update of input.updateActivities) {
|
|
19441
|
+
if (update.fields.parentId === void 0) continue;
|
|
19442
|
+
parents.set(update.activityId, parentKeyOf2(update.fields.parentId));
|
|
19443
|
+
}
|
|
19444
|
+
for (const removedId of input.removeActivityIds) parents.delete(removedId);
|
|
19445
|
+
return parents;
|
|
19446
|
+
}
|
|
19447
|
+
function findParentCycle(parents) {
|
|
19448
|
+
const settled = /* @__PURE__ */ new Set();
|
|
19449
|
+
for (const start of parents.keys()) {
|
|
19450
|
+
const path = [];
|
|
19451
|
+
const onPath = /* @__PURE__ */ new Set();
|
|
19452
|
+
let current = start;
|
|
19453
|
+
while (current !== null && current !== void 0 && !settled.has(current)) {
|
|
19454
|
+
if (onPath.has(current)) return path.slice(path.indexOf(current));
|
|
19455
|
+
onPath.add(current);
|
|
19456
|
+
path.push(current);
|
|
19457
|
+
current = parents.get(current);
|
|
19458
|
+
}
|
|
19459
|
+
for (const visited of path) settled.add(visited);
|
|
19460
|
+
}
|
|
19461
|
+
return null;
|
|
19462
|
+
}
|
|
19463
|
+
function restoreRemoteParent(activityId, remote, input) {
|
|
19464
|
+
const existing = input.updateActivities.find(
|
|
19465
|
+
(update) => update.activityId === activityId
|
|
19466
|
+
);
|
|
19467
|
+
if (existing) {
|
|
19468
|
+
const index = input.updateActivities.indexOf(existing);
|
|
19469
|
+
input.updateActivities[index] = {
|
|
19470
|
+
activityId,
|
|
19471
|
+
fields: { ...existing.fields, parentId: remote.parentId }
|
|
19472
|
+
};
|
|
19473
|
+
return;
|
|
19474
|
+
}
|
|
19475
|
+
input.updateActivities.push({
|
|
19476
|
+
activityId,
|
|
19477
|
+
fields: { parentId: remote.parentId }
|
|
19478
|
+
});
|
|
19479
|
+
}
|
|
19480
|
+
function repairParentCycles(input) {
|
|
19481
|
+
const remoteById = new Map(
|
|
19482
|
+
input.remoteActivities.map((remote) => [String(remote.id), remote])
|
|
19483
|
+
);
|
|
19484
|
+
const conflicts = [];
|
|
19485
|
+
let parents = mergedParents(input);
|
|
19486
|
+
let cycle = findParentCycle(parents);
|
|
19487
|
+
while (cycle !== null) {
|
|
19488
|
+
const localChoice = cycle.find((activityId) => {
|
|
19489
|
+
const remote2 = remoteById.get(activityId);
|
|
19490
|
+
return remote2 !== void 0 && parentKeyOf2(remote2.parentId) !== parents.get(activityId);
|
|
19491
|
+
});
|
|
19492
|
+
const remote = localChoice === void 0 ? void 0 : remoteById.get(localChoice);
|
|
19493
|
+
if (localChoice === void 0 || remote === void 0) break;
|
|
19494
|
+
conflicts.push({
|
|
19495
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
19496
|
+
id: localChoice,
|
|
19497
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.FIELD,
|
|
19498
|
+
field: ACTIVITY_PROPERTY.PARENT,
|
|
19499
|
+
mine: parents.get(localChoice) ?? null,
|
|
19500
|
+
theirs: parentKeyOf2(remote.parentId)
|
|
19501
|
+
});
|
|
19502
|
+
restoreRemoteParent(localChoice, remote, input);
|
|
19503
|
+
parents = mergedParents(input);
|
|
19504
|
+
cycle = findParentCycle(parents);
|
|
19505
|
+
}
|
|
19506
|
+
return conflicts;
|
|
19507
|
+
}
|
|
19508
|
+
function cyclicActivityCount(adapter) {
|
|
19509
|
+
const edges = projectEffectiveEdges(adapter, adapter.getAllLinks());
|
|
19510
|
+
return circularComponents(edges).reduce(
|
|
19511
|
+
(total, component) => total + component.length,
|
|
19512
|
+
0
|
|
19513
|
+
);
|
|
19514
|
+
}
|
|
19515
|
+
function breakDependencyCycles(adapter, localLinkIdsInCreationOrder, changes) {
|
|
19516
|
+
const removed = /* @__PURE__ */ new Map();
|
|
19517
|
+
const conflicts = [];
|
|
19518
|
+
if (cyclicActivityCount(adapter) === 0) return { conflicts, removed };
|
|
19519
|
+
const withdrawn = [];
|
|
19520
|
+
for (const linkId of localLinkIdsInCreationOrder) {
|
|
19521
|
+
const link = adapter.getLink(linkId);
|
|
19522
|
+
if (!link) continue;
|
|
19523
|
+
withdrawn.push({ ...link });
|
|
19524
|
+
adapter.removeLink(linkId);
|
|
19525
|
+
}
|
|
19526
|
+
let cyclic = cyclicActivityCount(adapter);
|
|
19527
|
+
for (const link of withdrawn) {
|
|
19528
|
+
adapter.addLink(link);
|
|
19529
|
+
const withLink = cyclicActivityCount(adapter);
|
|
19530
|
+
if (withLink <= cyclic) {
|
|
19531
|
+
cyclic = withLink;
|
|
19532
|
+
continue;
|
|
19533
|
+
}
|
|
19534
|
+
adapter.removeLink(String(link.id));
|
|
19535
|
+
removed.set(String(link.id), link);
|
|
19536
|
+
conflicts.push({
|
|
19537
|
+
entity: PERSISTENCE_REBASE_ENTITY.LINK,
|
|
19538
|
+
id: String(link.id),
|
|
19539
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.LINK_CYCLE,
|
|
19540
|
+
field: null,
|
|
19541
|
+
mine: snapshotToLink(link),
|
|
19542
|
+
theirs: null
|
|
19543
|
+
});
|
|
19544
|
+
}
|
|
19545
|
+
changes.push(...buildLinkDeletions(new Map(removed)));
|
|
19546
|
+
return { conflicts, removed };
|
|
19547
|
+
}
|
|
19548
|
+
|
|
19549
|
+
// src/dispatch/handlers/persistence-rebase-plan.ts
|
|
19550
|
+
var ORDER_FIELDS = /* @__PURE__ */ new Set([
|
|
19551
|
+
ACTIVITY_PROPERTY.CORRELATIVE_ID,
|
|
19552
|
+
ACTIVITY_PROPERTY.UNIQUE_CORRELATIVE_ID
|
|
19553
|
+
]);
|
|
19554
|
+
var RECOMPUTED_FIELDS = /* @__PURE__ */ new Set([
|
|
19555
|
+
ACTIVITY_PROPERTY.END_DATE
|
|
19556
|
+
]);
|
|
19557
|
+
var RECOMPUTED_PARENT_FIELDS = /* @__PURE__ */ new Set(
|
|
19558
|
+
[
|
|
19559
|
+
ACTIVITY_PROPERTY.START_DATE,
|
|
19560
|
+
ACTIVITY_PROPERTY.DURATION_HOURS,
|
|
19561
|
+
ACTIVITY_PROPERTY.COST,
|
|
19562
|
+
ACTIVITY_PROPERTY.USED_COST,
|
|
19563
|
+
ACTIVITY_PROPERTY.WORK_HOURS
|
|
19564
|
+
]
|
|
19565
|
+
);
|
|
19566
|
+
var FOLLOWS_REMOTE_FIELDS = /* @__PURE__ */ new Set([
|
|
19567
|
+
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
19568
|
+
ACTIVITY_PROPERTY.HAS_NEW_ACTIVITIES
|
|
19569
|
+
]);
|
|
19570
|
+
var PARENT_DERIVED_FIELDS = /* @__PURE__ */ new Set([
|
|
19571
|
+
...RECOMPUTED_PARENT_FIELDS,
|
|
19572
|
+
ACTIVITY_PROPERTY.PROGRESS,
|
|
19573
|
+
ACTIVITY_PROPERTY.TYPE,
|
|
19574
|
+
ACTIVITY_PROPERTY.REAL_COST,
|
|
19575
|
+
ACTIVITY_PROPERTY.PONDERATOR,
|
|
19576
|
+
ACTIVITY_PROPERTY.AUTO_SCHEDULING
|
|
19577
|
+
]);
|
|
19578
|
+
var UNION_MERGED_FIELDS = /* @__PURE__ */ new Set([
|
|
19579
|
+
ACTIVITY_PROPERTY.NEW_ACTIVITIES_ARRAY
|
|
19580
|
+
]);
|
|
19581
|
+
function planPersistenceRebase(input) {
|
|
19582
|
+
const plan = {
|
|
19583
|
+
createActivities: [],
|
|
19584
|
+
updateActivities: [],
|
|
19585
|
+
removeActivityIds: [],
|
|
19586
|
+
createLinks: [],
|
|
19587
|
+
updateLinks: [],
|
|
19588
|
+
removeLinkIds: [],
|
|
19589
|
+
remoteChanges: [],
|
|
19590
|
+
conflicts: []
|
|
19591
|
+
};
|
|
19592
|
+
planActivities(input, plan);
|
|
19593
|
+
plan.conflicts.push(
|
|
19594
|
+
...repairParentCycles({
|
|
19595
|
+
localActivities: input.localActivities,
|
|
19596
|
+
remoteActivities: input.remoteActivities,
|
|
19597
|
+
createActivities: plan.createActivities,
|
|
19598
|
+
removeActivityIds: plan.removeActivityIds,
|
|
19599
|
+
updateActivities: plan.updateActivities
|
|
19600
|
+
})
|
|
19601
|
+
);
|
|
19602
|
+
planLinks(input, plan);
|
|
19603
|
+
return plan;
|
|
19604
|
+
}
|
|
19605
|
+
function collectParentIds2(activities) {
|
|
19606
|
+
const parentIds = /* @__PURE__ */ new Set();
|
|
19607
|
+
for (const activity of activities) {
|
|
19608
|
+
if (isRootParent(activity.parentId)) continue;
|
|
19609
|
+
parentIds.add(String(activity.parentId));
|
|
19610
|
+
}
|
|
19611
|
+
return parentIds;
|
|
19612
|
+
}
|
|
19613
|
+
function collectLinkTargetIds(links) {
|
|
19614
|
+
return new Set(links.map((link) => String(link.target)));
|
|
19615
|
+
}
|
|
19616
|
+
function indexStructure(input) {
|
|
19617
|
+
return {
|
|
19618
|
+
localParentIds: collectParentIds2(input.localActivities),
|
|
19619
|
+
remoteParentIds: collectParentIds2(input.remoteActivities),
|
|
19620
|
+
localTargetIds: collectLinkTargetIds(input.localLinks),
|
|
19621
|
+
remoteTargetIds: collectLinkTargetIds(input.remoteLinks)
|
|
19622
|
+
};
|
|
19623
|
+
}
|
|
19624
|
+
function mergeContextFor(activityId, structure) {
|
|
19625
|
+
return {
|
|
19626
|
+
local: {
|
|
19627
|
+
isParent: structure.localParentIds.has(activityId),
|
|
19628
|
+
hasPredecessors: structure.localTargetIds.has(activityId)
|
|
19629
|
+
},
|
|
19630
|
+
remote: {
|
|
19631
|
+
isParent: structure.remoteParentIds.has(activityId),
|
|
19632
|
+
hasPredecessors: structure.remoteTargetIds.has(activityId)
|
|
19633
|
+
}
|
|
19634
|
+
};
|
|
19635
|
+
}
|
|
19636
|
+
function isRecomputedOnSide(field, side, activity) {
|
|
19637
|
+
if (RECOMPUTED_FIELDS.has(field)) return true;
|
|
19638
|
+
if (side.isParent && RECOMPUTED_PARENT_FIELDS.has(field)) return true;
|
|
19639
|
+
const isScheduledStart = field === ACTIVITY_PROPERTY.START_DATE && side.hasPredecessors && activity.autoScheduling;
|
|
19640
|
+
if (isScheduledStart) return true;
|
|
19641
|
+
return field === ACTIVITY_PROPERTY.PONDERATOR && !activity.hasCustomPonderator;
|
|
19642
|
+
}
|
|
19643
|
+
function isDerivedOnSide(field, side, activity) {
|
|
19644
|
+
if (isRecomputedOnSide(field, side, activity)) return true;
|
|
19645
|
+
if (FOLLOWS_REMOTE_FIELDS.has(field)) return true;
|
|
19646
|
+
return side.isParent && PARENT_DERIVED_FIELDS.has(field);
|
|
19647
|
+
}
|
|
19648
|
+
function sourceEditsOf(activity, base, side) {
|
|
19649
|
+
if (base === null) return /* @__PURE__ */ new Set();
|
|
19650
|
+
return new Set(
|
|
19651
|
+
changedPersistableActivityFields(activity, base).filter(
|
|
19652
|
+
(field) => !isDerivedOnSide(field, side, activity)
|
|
19653
|
+
)
|
|
19654
|
+
);
|
|
19655
|
+
}
|
|
19656
|
+
function planActivities(input, plan) {
|
|
19657
|
+
const structure = indexStructure(input);
|
|
19658
|
+
const localById = new Map(
|
|
19659
|
+
input.localActivities.map((activity) => [String(activity.id), activity])
|
|
19660
|
+
);
|
|
19661
|
+
const remoteIds = /* @__PURE__ */ new Set();
|
|
19662
|
+
for (const remote of input.remoteActivities) {
|
|
19663
|
+
const activityId = String(remote.id);
|
|
19664
|
+
remoteIds.add(activityId);
|
|
19665
|
+
const base = input.checkpoint.checkpointActivity(activityId);
|
|
19666
|
+
const local = localById.get(activityId) ?? null;
|
|
19667
|
+
const context = mergeContextFor(activityId, structure);
|
|
19668
|
+
planRemoteActivity(activityId, remote, base, local, context, plan);
|
|
19669
|
+
}
|
|
19670
|
+
for (const activityId of input.checkpoint.checkpointActivityIds()) {
|
|
19671
|
+
if (remoteIds.has(activityId)) continue;
|
|
19672
|
+
const local = localById.get(activityId);
|
|
19673
|
+
if (!local) continue;
|
|
19674
|
+
const base = input.checkpoint.checkpointActivity(activityId);
|
|
19675
|
+
planRemotelyDeletedActivity(activityId, base, local, plan);
|
|
19676
|
+
}
|
|
19677
|
+
}
|
|
19678
|
+
function planRemoteActivity(activityId, remote, base, local, context, plan) {
|
|
19679
|
+
if (base === null) {
|
|
19680
|
+
if (local === null) {
|
|
19681
|
+
plan.createActivities.push(remote);
|
|
19682
|
+
return;
|
|
19683
|
+
}
|
|
19684
|
+
planActivityFields(activityId, remote, null, local, context, plan);
|
|
19685
|
+
return;
|
|
19686
|
+
}
|
|
19687
|
+
const remoteFields = changedPersistableActivityFields(remote, base);
|
|
19688
|
+
if (local === null) {
|
|
19689
|
+
if (remoteFields.length === 0) return;
|
|
19690
|
+
plan.createActivities.push(remote);
|
|
19691
|
+
plan.conflicts.push({
|
|
19692
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
19693
|
+
id: activityId,
|
|
19694
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.DELETED_LOCALLY,
|
|
19695
|
+
field: null,
|
|
19696
|
+
mine: null,
|
|
19697
|
+
theirs: projectPersistableActivity(remote)
|
|
19698
|
+
});
|
|
19699
|
+
return;
|
|
19700
|
+
}
|
|
19701
|
+
planActivityFields(activityId, remote, base, local, context, plan);
|
|
19702
|
+
}
|
|
19703
|
+
var MERGE_SIDE = { LOCAL: "local", REMOTE: "remote" };
|
|
19704
|
+
function typeSideOf(context) {
|
|
19705
|
+
if (context.local.isParent) return MERGE_SIDE.LOCAL;
|
|
19706
|
+
if (context.remote.isParent) return MERGE_SIDE.REMOTE;
|
|
19707
|
+
return null;
|
|
19708
|
+
}
|
|
19709
|
+
function winningSide(merge, field) {
|
|
19710
|
+
const remoteChanged = merge.remoteFields.includes(field);
|
|
19711
|
+
const localChanged = merge.localChangedFields.has(field);
|
|
19712
|
+
if (field === ACTIVITY_PROPERTY.TYPE) {
|
|
19713
|
+
if (!localChanged) return MERGE_SIDE.REMOTE;
|
|
19714
|
+
if (!remoteChanged) return MERGE_SIDE.LOCAL;
|
|
19715
|
+
return typeSideOf(merge.context) ?? MERGE_SIDE.REMOTE;
|
|
19716
|
+
}
|
|
19717
|
+
if (isRecomputedOnSide(field, merge.context.local, merge.local)) {
|
|
19718
|
+
return MERGE_SIDE.REMOTE;
|
|
19719
|
+
}
|
|
19720
|
+
if (!localChanged) return MERGE_SIDE.REMOTE;
|
|
19721
|
+
if (merge.remoteSourceFields.has(field)) return MERGE_SIDE.REMOTE;
|
|
19722
|
+
const isParentOnBothSides = merge.context.local.isParent && merge.context.remote.isParent;
|
|
19723
|
+
const followsRemote = FOLLOWS_REMOTE_FIELDS.has(field) || isParentOnBothSides && PARENT_DERIVED_FIELDS.has(field);
|
|
19724
|
+
if (remoteChanged && followsRemote) return MERGE_SIDE.REMOTE;
|
|
19725
|
+
return MERGE_SIDE.LOCAL;
|
|
19726
|
+
}
|
|
19727
|
+
function planActivityFields(activityId, remote, base, local, context, plan) {
|
|
19728
|
+
const localPersistable = projectPersistableActivity(local);
|
|
19729
|
+
const remotePersistable = projectPersistableActivity(remote);
|
|
19730
|
+
const differsFromLocal = changedPersistableActivityFields(
|
|
19731
|
+
remote,
|
|
19732
|
+
localPersistable
|
|
19733
|
+
);
|
|
19734
|
+
if (differsFromLocal.length === 0) return;
|
|
19735
|
+
const remoteFields = base === null ? differsFromLocal : changedPersistableActivityFields(remote, base);
|
|
19736
|
+
const merge = {
|
|
19737
|
+
activityId,
|
|
19738
|
+
remote,
|
|
19739
|
+
local,
|
|
19740
|
+
remotePersistable,
|
|
19741
|
+
localPersistable,
|
|
19742
|
+
remoteFields,
|
|
19743
|
+
localFields: sourceEditsOf(local, base, context.local),
|
|
19744
|
+
localChangedFields: new Set(
|
|
19745
|
+
base === null ? [] : changedPersistableActivityFields(local, base)
|
|
19746
|
+
),
|
|
19747
|
+
remoteSourceFields: base === null ? new Set(differsFromLocal) : sourceEditsOf(remote, base, context.remote),
|
|
19748
|
+
context
|
|
19749
|
+
};
|
|
19750
|
+
planBecameParentConflicts(merge, plan);
|
|
19751
|
+
const fields = mergeFields(merge, differsFromLocal, plan);
|
|
19752
|
+
if (Object.keys(fields).length === 0) return;
|
|
19753
|
+
plan.updateActivities.push({ activityId, fields });
|
|
19754
|
+
}
|
|
19755
|
+
function mergeFields(merge, differsFromLocal, plan) {
|
|
19756
|
+
const { remotePersistable } = merge;
|
|
19757
|
+
const fields = {};
|
|
19758
|
+
for (const field of differsFromLocal) {
|
|
19759
|
+
if (ORDER_FIELDS.has(field) || field === ACTIVITY_PROPERTY.AUTO_SCHEDULING) {
|
|
19760
|
+
continue;
|
|
19761
|
+
}
|
|
19762
|
+
if (UNION_MERGED_FIELDS.has(field)) {
|
|
19763
|
+
mergeUnionField(merge, field, fields);
|
|
19764
|
+
continue;
|
|
19765
|
+
}
|
|
19766
|
+
if (field === ACTIVITY_PROPERTY.HAS_NEW_ACTIVITIES && fields.hasNewActivities !== void 0) {
|
|
19767
|
+
continue;
|
|
19768
|
+
}
|
|
19769
|
+
if (winningSide(merge, field) === MERGE_SIDE.LOCAL) continue;
|
|
19770
|
+
Reflect.set(fields, field, remotePersistable[field]);
|
|
19771
|
+
if (merge.remoteSourceFields.has(field)) {
|
|
19772
|
+
reportSourceField(merge, field, plan);
|
|
19773
|
+
}
|
|
19774
|
+
}
|
|
19775
|
+
if (fields.progress !== void 0 && !merge.context.local.isParent) {
|
|
19776
|
+
fields.autoScheduling = isAutoSchedulingEnabledForProgress(fields.progress);
|
|
19777
|
+
}
|
|
19778
|
+
keepMilestoneDurationConsistent(merge, fields, plan);
|
|
19779
|
+
return fields;
|
|
19780
|
+
}
|
|
19781
|
+
function mergeUnionField(merge, field, fields) {
|
|
19782
|
+
const { localPersistable, remotePersistable } = merge;
|
|
19783
|
+
const merged = unionOfIds(localPersistable[field], remotePersistable[field]);
|
|
19784
|
+
Reflect.set(fields, field, merged);
|
|
19785
|
+
Reflect.set(
|
|
19786
|
+
fields,
|
|
19787
|
+
ACTIVITY_PROPERTY.HAS_NEW_ACTIVITIES,
|
|
19788
|
+
merged.length > 0 || localPersistable.hasNewActivities || remotePersistable.hasNewActivities
|
|
19789
|
+
);
|
|
19790
|
+
}
|
|
19791
|
+
function keepMilestoneDurationConsistent(merge, fields, plan) {
|
|
19792
|
+
const finalType = fields.type ?? merge.local.type;
|
|
19793
|
+
const finalDuration = fields.durationHours ?? merge.local.durationHours;
|
|
19794
|
+
if (finalType !== ACTIVITY_TYPE.MILESTONE || finalDuration === 0) return;
|
|
19795
|
+
const remoteSetDuration = fields.durationHours !== void 0;
|
|
19796
|
+
const field = remoteSetDuration ? ACTIVITY_PROPERTY.TYPE : ACTIVITY_PROPERTY.DURATION_HOURS;
|
|
19797
|
+
if (remoteSetDuration) {
|
|
19798
|
+
fields.type = merge.remote.type;
|
|
19799
|
+
} else {
|
|
19800
|
+
fields.durationHours = 0;
|
|
19801
|
+
}
|
|
19802
|
+
plan.conflicts.push({
|
|
19803
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
19804
|
+
id: merge.activityId,
|
|
19805
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.FIELD,
|
|
19806
|
+
field,
|
|
19807
|
+
mine: merge.localPersistable[field],
|
|
19808
|
+
theirs: remoteSetDuration ? merge.remote.type : 0
|
|
19809
|
+
});
|
|
19810
|
+
}
|
|
19811
|
+
function reportSourceField(merge, field, plan) {
|
|
19812
|
+
const mine = merge.localPersistable[field];
|
|
19813
|
+
const theirs = merge.remotePersistable[field];
|
|
19814
|
+
if (merge.localFields.has(field)) {
|
|
19815
|
+
plan.conflicts.push({
|
|
19816
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
19817
|
+
id: merge.activityId,
|
|
19818
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.FIELD,
|
|
19819
|
+
field,
|
|
19820
|
+
mine,
|
|
19821
|
+
theirs
|
|
19822
|
+
});
|
|
19823
|
+
return;
|
|
19824
|
+
}
|
|
19825
|
+
plan.remoteChanges.push({
|
|
19826
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
19827
|
+
id: merge.activityId,
|
|
19828
|
+
field,
|
|
19829
|
+
before: mine,
|
|
19830
|
+
after: theirs
|
|
19831
|
+
});
|
|
19832
|
+
}
|
|
19833
|
+
function unionOfIds(mine, theirs) {
|
|
19834
|
+
const merged = /* @__PURE__ */ new Set();
|
|
19835
|
+
for (const value of Array.isArray(mine) ? mine : [])
|
|
19836
|
+
merged.add(String(value));
|
|
19837
|
+
for (const value of Array.isArray(theirs) ? theirs : [])
|
|
19838
|
+
merged.add(String(value));
|
|
19839
|
+
return [...merged];
|
|
19840
|
+
}
|
|
19841
|
+
function leafValuesOf(fields, activity) {
|
|
19842
|
+
const picked = {};
|
|
19843
|
+
for (const field of fields) {
|
|
19844
|
+
if (!PARENT_DERIVED_FIELDS.has(field)) continue;
|
|
19845
|
+
if (field === ACTIVITY_PROPERTY.TYPE) continue;
|
|
19846
|
+
Reflect.set(picked, field, activity[field]);
|
|
19847
|
+
}
|
|
19848
|
+
return picked;
|
|
19849
|
+
}
|
|
19850
|
+
function planBecameParentConflicts(merge, plan) {
|
|
19851
|
+
const { context, localPersistable, remotePersistable } = merge;
|
|
19852
|
+
if (context.local.isParent === context.remote.isParent) return;
|
|
19853
|
+
const droppedLeafValues = context.remote.isParent ? leafValuesOf(merge.localFields, localPersistable) : leafValuesOf(merge.remoteFields, remotePersistable);
|
|
19854
|
+
if (Object.keys(droppedLeafValues).length === 0) return;
|
|
19855
|
+
const conflict = context.remote.isParent ? {
|
|
19856
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.BECAME_PARENT_REMOTELY,
|
|
19857
|
+
mine: droppedLeafValues,
|
|
19858
|
+
theirs: remotePersistable
|
|
19859
|
+
} : {
|
|
19860
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.BECAME_PARENT_LOCALLY,
|
|
19861
|
+
mine: localPersistable,
|
|
19862
|
+
theirs: droppedLeafValues
|
|
19863
|
+
};
|
|
19864
|
+
plan.conflicts.push({
|
|
19865
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
19866
|
+
id: merge.activityId,
|
|
19867
|
+
field: null,
|
|
19868
|
+
...conflict
|
|
19869
|
+
});
|
|
19870
|
+
}
|
|
19871
|
+
function planRemotelyDeletedActivity(activityId, base, local, plan) {
|
|
19872
|
+
plan.removeActivityIds.push(activityId);
|
|
19873
|
+
const localEdited = base !== null && changedPersistableActivityFields(local, base).length > 0;
|
|
19874
|
+
if (!localEdited) return;
|
|
19875
|
+
plan.conflicts.push({
|
|
19876
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
19877
|
+
id: activityId,
|
|
19878
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.DELETED_REMOTELY,
|
|
19879
|
+
field: null,
|
|
19880
|
+
mine: projectPersistableActivity(local),
|
|
19881
|
+
theirs: null
|
|
19882
|
+
});
|
|
19883
|
+
}
|
|
19884
|
+
function planLinks(input, plan) {
|
|
19885
|
+
const localById = new Map(
|
|
19886
|
+
input.localLinks.map((link) => [String(link.id), link])
|
|
19887
|
+
);
|
|
19888
|
+
const remoteIds = /* @__PURE__ */ new Set();
|
|
19889
|
+
const createdRemotely = [];
|
|
19890
|
+
for (const remote of input.remoteLinks) {
|
|
19891
|
+
const linkId = String(remote.id);
|
|
19892
|
+
remoteIds.add(linkId);
|
|
19893
|
+
const base = input.checkpoint.checkpointLink(linkId);
|
|
19894
|
+
const local = localById.get(linkId) ?? null;
|
|
19895
|
+
if (base === null && local === null) createdRemotely.push(remote);
|
|
19896
|
+
planRemoteLink(linkId, remote, base, local, plan);
|
|
19897
|
+
}
|
|
19898
|
+
for (const linkId of input.checkpoint.checkpointLinkIds()) {
|
|
19899
|
+
if (remoteIds.has(linkId)) continue;
|
|
19900
|
+
const local = localById.get(linkId);
|
|
19901
|
+
if (!local) continue;
|
|
19902
|
+
const base = input.checkpoint.checkpointLink(linkId);
|
|
19903
|
+
planRemotelyDeletedLink(linkId, base, local, plan);
|
|
19904
|
+
}
|
|
19905
|
+
planDuplicateLinks(input, createdRemotely, remoteIds, plan);
|
|
19906
|
+
}
|
|
19907
|
+
function endpointPairKey(link) {
|
|
19908
|
+
return [String(link.source), String(link.target)].sort().join("|");
|
|
19909
|
+
}
|
|
19910
|
+
function planDuplicateLinks(input, createdRemotely, remoteIds, plan) {
|
|
19911
|
+
if (createdRemotely.length === 0) return;
|
|
19912
|
+
const remoteByPair = new Map(
|
|
19913
|
+
createdRemotely.map((remote) => [endpointPairKey(remote), remote])
|
|
19914
|
+
);
|
|
19915
|
+
for (const local of input.localLinks) {
|
|
19916
|
+
const linkId = String(local.id);
|
|
19917
|
+
const isUnsavedLocally = !remoteIds.has(linkId) && input.checkpoint.checkpointLink(linkId) === null;
|
|
19918
|
+
if (!isUnsavedLocally) continue;
|
|
19919
|
+
const remote = remoteByPair.get(endpointPairKey(local));
|
|
19920
|
+
if (!remote) continue;
|
|
19921
|
+
plan.removeLinkIds.push(linkId);
|
|
19922
|
+
const mine = persistableLinkOf(local);
|
|
19923
|
+
const theirs = persistableLinkOf(remote);
|
|
19924
|
+
if (changedLinkFields(mine, theirs).length === 0) continue;
|
|
19925
|
+
plan.conflicts.push({
|
|
19926
|
+
entity: PERSISTENCE_REBASE_ENTITY.LINK,
|
|
19927
|
+
id: String(remote.id),
|
|
19928
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.DUPLICATE_LINK,
|
|
19929
|
+
field: null,
|
|
19930
|
+
mine,
|
|
19931
|
+
theirs
|
|
19932
|
+
});
|
|
19933
|
+
}
|
|
19934
|
+
}
|
|
19935
|
+
function changedLinkFields(current, saved) {
|
|
19936
|
+
return TRACKED_LINK_FIELDS.filter((field) => current[field] !== saved[field]);
|
|
19937
|
+
}
|
|
19938
|
+
function planRemoteLink(linkId, remote, base, local, plan) {
|
|
19939
|
+
if (base === null) {
|
|
19940
|
+
if (local === null) {
|
|
19941
|
+
plan.createLinks.push(remote);
|
|
19942
|
+
return;
|
|
19943
|
+
}
|
|
19944
|
+
planLinkFields(linkId, remote, null, local, plan);
|
|
19945
|
+
return;
|
|
19946
|
+
}
|
|
19947
|
+
if (local === null) {
|
|
19948
|
+
if (changedLinkFields(remote, base).length === 0) return;
|
|
19949
|
+
plan.createLinks.push(remote);
|
|
19950
|
+
plan.conflicts.push({
|
|
19951
|
+
entity: PERSISTENCE_REBASE_ENTITY.LINK,
|
|
19952
|
+
id: linkId,
|
|
19953
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.DELETED_LOCALLY,
|
|
19954
|
+
field: null,
|
|
19955
|
+
mine: null,
|
|
19956
|
+
theirs: persistableLinkOf(remote)
|
|
19957
|
+
});
|
|
19958
|
+
return;
|
|
19959
|
+
}
|
|
19960
|
+
planLinkFields(linkId, remote, base, local, plan);
|
|
19961
|
+
}
|
|
19962
|
+
function persistableLinkOf(link) {
|
|
19963
|
+
return {
|
|
19964
|
+
source: String(link.source),
|
|
19965
|
+
target: String(link.target),
|
|
19966
|
+
type: link.type,
|
|
19967
|
+
lag: Number(link.lag)
|
|
19968
|
+
};
|
|
19969
|
+
}
|
|
19970
|
+
function planLinkFields(linkId, remote, base, local, plan) {
|
|
19971
|
+
const remoteLink = persistableLinkOf(remote);
|
|
19972
|
+
const localLink = persistableLinkOf(local);
|
|
19973
|
+
const remoteFields = base === null ? changedLinkFields(remoteLink, localLink) : changedLinkFields(remoteLink, base);
|
|
19974
|
+
if (remoteFields.length === 0) return;
|
|
19975
|
+
const localFields = new Set(
|
|
19976
|
+
base === null ? [] : changedLinkFields(localLink, base)
|
|
19977
|
+
);
|
|
19978
|
+
const differsFromLocal = new Set(
|
|
19979
|
+
changedLinkFields(remoteLink, localLink)
|
|
19980
|
+
);
|
|
19981
|
+
const fields = {};
|
|
19982
|
+
for (const field of remoteFields) {
|
|
19983
|
+
if (!differsFromLocal.has(field)) continue;
|
|
19984
|
+
Reflect.set(fields, field, remoteLink[field]);
|
|
19985
|
+
if (localFields.has(field)) {
|
|
19986
|
+
plan.conflicts.push({
|
|
19987
|
+
entity: PERSISTENCE_REBASE_ENTITY.LINK,
|
|
19988
|
+
id: linkId,
|
|
19989
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.FIELD,
|
|
19990
|
+
field,
|
|
19991
|
+
mine: localLink[field],
|
|
19992
|
+
theirs: remoteLink[field]
|
|
19993
|
+
});
|
|
19994
|
+
continue;
|
|
19995
|
+
}
|
|
19996
|
+
plan.remoteChanges.push({
|
|
19997
|
+
entity: PERSISTENCE_REBASE_ENTITY.LINK,
|
|
19998
|
+
id: linkId,
|
|
19999
|
+
field,
|
|
20000
|
+
before: localLink[field],
|
|
20001
|
+
after: remoteLink[field]
|
|
20002
|
+
});
|
|
20003
|
+
}
|
|
20004
|
+
if (Object.keys(fields).length === 0) return;
|
|
20005
|
+
plan.updateLinks.push({ linkId, fields });
|
|
20006
|
+
}
|
|
20007
|
+
function planRemotelyDeletedLink(linkId, base, local, plan) {
|
|
20008
|
+
plan.removeLinkIds.push(linkId);
|
|
20009
|
+
const localEdited = base !== null && changedLinkFields(persistableLinkOf(local), base).length > 0;
|
|
20010
|
+
if (!localEdited) return;
|
|
20011
|
+
plan.conflicts.push({
|
|
20012
|
+
entity: PERSISTENCE_REBASE_ENTITY.LINK,
|
|
20013
|
+
id: linkId,
|
|
20014
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.DELETED_REMOTELY,
|
|
20015
|
+
field: null,
|
|
20016
|
+
mine: persistableLinkOf(local),
|
|
20017
|
+
theirs: null
|
|
20018
|
+
});
|
|
20019
|
+
}
|
|
20020
|
+
|
|
20021
|
+
// src/dispatch/handlers/persistence-rebase-order.ts
|
|
20022
|
+
var parentKeyOf3 = (parentId) => isRootParent(parentId) ? ROOT_PARENT_ID : String(parentId);
|
|
20023
|
+
var sortValueOf = (correlativeId) => {
|
|
20024
|
+
const value = Number(correlativeId);
|
|
20025
|
+
return Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER;
|
|
20026
|
+
};
|
|
20027
|
+
function siblingOrdersOf(entries) {
|
|
20028
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
20029
|
+
for (const entry of entries) {
|
|
20030
|
+
const key = parentKeyOf3(entry.parentId);
|
|
20031
|
+
const siblings = grouped.get(key) ?? [];
|
|
20032
|
+
siblings.push(entry);
|
|
20033
|
+
grouped.set(key, siblings);
|
|
20034
|
+
}
|
|
20035
|
+
const orders = /* @__PURE__ */ new Map();
|
|
20036
|
+
for (const [key, siblings] of grouped) {
|
|
20037
|
+
siblings.sort(
|
|
20038
|
+
(left, right) => sortValueOf(left.correlativeId) - sortValueOf(right.correlativeId) || left.id.localeCompare(right.id)
|
|
20039
|
+
);
|
|
20040
|
+
orders.set(
|
|
20041
|
+
key,
|
|
20042
|
+
siblings.map((entry) => entry.id)
|
|
20043
|
+
);
|
|
20044
|
+
}
|
|
20045
|
+
return orders;
|
|
20046
|
+
}
|
|
20047
|
+
var entryOf = (activity) => ({
|
|
20048
|
+
id: String(activity.id),
|
|
20049
|
+
parentId: activity.parentId,
|
|
20050
|
+
correlativeId: activity.correlativeId
|
|
20051
|
+
});
|
|
20052
|
+
function captureSiblingOrders(localActivities, remoteActivities, checkpoint) {
|
|
20053
|
+
const baseEntries = [];
|
|
20054
|
+
for (const activityId of checkpoint.checkpointActivityIds()) {
|
|
20055
|
+
const saved = checkpoint.checkpointActivity(activityId);
|
|
20056
|
+
if (saved === null) continue;
|
|
20057
|
+
baseEntries.push({
|
|
20058
|
+
id: activityId,
|
|
20059
|
+
parentId: saved.parentId,
|
|
20060
|
+
correlativeId: saved.correlativeId
|
|
20061
|
+
});
|
|
20062
|
+
}
|
|
20063
|
+
return {
|
|
20064
|
+
local: siblingOrdersOf(localActivities.map(entryOf)),
|
|
20065
|
+
remote: siblingOrdersOf(remoteActivities.map(entryOf)),
|
|
20066
|
+
base: siblingOrdersOf(baseEntries)
|
|
20067
|
+
};
|
|
20068
|
+
}
|
|
20069
|
+
function isReordered(side, base) {
|
|
20070
|
+
const inBase = new Set(base);
|
|
20071
|
+
const inSide = new Set(side);
|
|
20072
|
+
const sideCommon = side.filter((activityId) => inBase.has(activityId));
|
|
20073
|
+
const baseCommon = base.filter((activityId) => inSide.has(activityId));
|
|
20074
|
+
return sideCommon.some(
|
|
20075
|
+
(activityId, index) => baseCommon[index] !== activityId
|
|
20076
|
+
);
|
|
20077
|
+
}
|
|
20078
|
+
function weave(primary, secondary, members) {
|
|
20079
|
+
const merged = primary.filter((activityId) => members.has(activityId));
|
|
20080
|
+
let anchorIndex = -1;
|
|
20081
|
+
for (const activityId of secondary) {
|
|
20082
|
+
const placedAt = merged.indexOf(activityId);
|
|
20083
|
+
if (placedAt !== -1) {
|
|
20084
|
+
anchorIndex = placedAt;
|
|
20085
|
+
continue;
|
|
20086
|
+
}
|
|
20087
|
+
if (!members.has(activityId)) continue;
|
|
20088
|
+
merged.splice(anchorIndex + 1, 0, activityId);
|
|
20089
|
+
anchorIndex += 1;
|
|
20090
|
+
}
|
|
20091
|
+
for (const activityId of members) {
|
|
20092
|
+
if (!merged.includes(activityId)) merged.push(activityId);
|
|
20093
|
+
}
|
|
20094
|
+
return merged;
|
|
20095
|
+
}
|
|
20096
|
+
function planParentOrder(parentKey, members, sources) {
|
|
20097
|
+
const local = sources.local.get(parentKey) ?? [];
|
|
20098
|
+
const remote = sources.remote.get(parentKey) ?? [];
|
|
20099
|
+
const base = sources.base.get(parentKey) ?? [];
|
|
20100
|
+
const localReordered = isReordered(local, base);
|
|
20101
|
+
const remoteReordered = isReordered(remote, base);
|
|
20102
|
+
const keepsLocalOrder = localReordered && !remoteReordered;
|
|
20103
|
+
const order = keepsLocalOrder ? weave(local, remote, members) : weave(remote, local, members);
|
|
20104
|
+
const overridesLocalOrder = localReordered && remoteReordered && isReordered(local, order);
|
|
20105
|
+
return {
|
|
20106
|
+
order,
|
|
20107
|
+
conflict: overridesLocalOrder ? {
|
|
20108
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
20109
|
+
id: parentKey,
|
|
20110
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.ORDER,
|
|
20111
|
+
field: ACTIVITY_PROPERTY.CORRELATIVE_ID,
|
|
20112
|
+
mine: local.filter((activityId) => members.has(activityId)),
|
|
20113
|
+
theirs: order
|
|
20114
|
+
} : null
|
|
20115
|
+
};
|
|
20116
|
+
}
|
|
20117
|
+
function currentSiblings(adapter) {
|
|
20118
|
+
return siblingOrdersOf(adapter.getAllActivities().map(entryOf));
|
|
20119
|
+
}
|
|
20120
|
+
function applySiblingOrder(adapter, sources) {
|
|
20121
|
+
const { orders, conflicts } = planSiblingOrders(adapter, sources);
|
|
20122
|
+
return { shifts: renumberCorrelatives(adapter, orders), conflicts };
|
|
20123
|
+
}
|
|
20124
|
+
function planSiblingOrders(adapter, sources) {
|
|
20125
|
+
const orders = /* @__PURE__ */ new Map();
|
|
20126
|
+
const conflicts = [];
|
|
20127
|
+
for (const [parentKey, siblings] of currentSiblings(adapter)) {
|
|
20128
|
+
const planned = planParentOrder(parentKey, new Set(siblings), sources);
|
|
20129
|
+
orders.set(parentKey, planned.order);
|
|
20130
|
+
if (planned.conflict !== null) conflicts.push(planned.conflict);
|
|
20131
|
+
}
|
|
20132
|
+
return { orders, conflicts };
|
|
20133
|
+
}
|
|
20134
|
+
function renumberCorrelatives(adapter, orders) {
|
|
20135
|
+
const shifts = [];
|
|
20136
|
+
let counter = 0;
|
|
20137
|
+
const pending = [...orders.get(ROOT_PARENT_ID) ?? []].reverse();
|
|
20138
|
+
while (pending.length > 0) {
|
|
20139
|
+
const activityId = pending.pop();
|
|
20140
|
+
if (activityId === void 0) break;
|
|
20141
|
+
const shift = writeCorrelative(adapter, activityId, counter);
|
|
20142
|
+
if (shift !== null) shifts.push(shift);
|
|
20143
|
+
counter += 1;
|
|
20144
|
+
pushChildrenInReverse(pending, orders.get(activityId) ?? []);
|
|
20145
|
+
}
|
|
20146
|
+
return shifts;
|
|
20147
|
+
}
|
|
20148
|
+
function writeCorrelative(adapter, activityId, correlativeId) {
|
|
20149
|
+
const activity = adapter.getActivity(activityId);
|
|
20150
|
+
if (!activity || activity.correlativeId === correlativeId) return null;
|
|
20151
|
+
const before = Number(activity.correlativeId);
|
|
20152
|
+
adapter.setActivityField(
|
|
20153
|
+
activityId,
|
|
20154
|
+
ACTIVITY_PROPERTY.CORRELATIVE_ID,
|
|
20155
|
+
correlativeId
|
|
20156
|
+
);
|
|
20157
|
+
return { activityId, before: Number.isFinite(before) ? before : void 0 };
|
|
20158
|
+
}
|
|
20159
|
+
function pushChildrenInReverse(pending, children) {
|
|
20160
|
+
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
20161
|
+
const childId = children[index];
|
|
20162
|
+
if (childId !== void 0) pending.push(childId);
|
|
20163
|
+
}
|
|
20164
|
+
}
|
|
20165
|
+
|
|
20166
|
+
// src/dispatch/handlers/persistence-rebase-rekey.ts
|
|
20167
|
+
var hasNoPersistedIdentity = (proplannerId) => proplannerId === null || proplannerId === void 0 || proplannerId === 0;
|
|
20168
|
+
var isUnpersisted = (activity) => hasNoPersistedIdentity(activity.proplannerId);
|
|
20169
|
+
function nextFreeLinkId(deps, taken) {
|
|
20170
|
+
let candidate = deps.linkIdGen.next();
|
|
20171
|
+
const isTaken = (linkId) => deps.adapter.getLink(linkId) !== null || deps.isLinkIdReserved(String(linkId)) || taken.has(String(linkId));
|
|
20172
|
+
while (isTaken(candidate)) candidate = deps.linkIdGen.next();
|
|
20173
|
+
return candidate;
|
|
20174
|
+
}
|
|
20175
|
+
function planLinkRekeys(remoteLinks, deps) {
|
|
20176
|
+
const remoteLinkIds = /* @__PURE__ */ new Set();
|
|
20177
|
+
for (const remote of remoteLinks) {
|
|
20178
|
+
remoteLinkIds.add(String(remote.id));
|
|
20179
|
+
deps.linkIdGen.bump(remote.id);
|
|
20180
|
+
}
|
|
20181
|
+
const taken = new Set(remoteLinkIds);
|
|
20182
|
+
const rekeys = [];
|
|
20183
|
+
for (const local of deps.adapter.getAllLinks()) {
|
|
20184
|
+
const linkId = String(local.id);
|
|
20185
|
+
if (!remoteLinkIds.has(linkId)) continue;
|
|
20186
|
+
if (!hasNoPersistedIdentity(local.proplannerId)) continue;
|
|
20187
|
+
const nextId = String(nextFreeLinkId(deps, taken));
|
|
20188
|
+
taken.add(nextId);
|
|
20189
|
+
rekeys.push({ from: linkId, to: nextId });
|
|
20190
|
+
}
|
|
20191
|
+
return rekeys;
|
|
20192
|
+
}
|
|
20193
|
+
function remoteCustomIdOwners(remoteActivities) {
|
|
20194
|
+
const owners = /* @__PURE__ */ new Map();
|
|
20195
|
+
for (const remote of remoteActivities) {
|
|
20196
|
+
if (remote.customId) owners.set(remote.customId, String(remote.id));
|
|
20197
|
+
}
|
|
20198
|
+
return owners;
|
|
20199
|
+
}
|
|
20200
|
+
function isLocalCustomIdEdit(local, deps) {
|
|
20201
|
+
const saved = deps.persistenceCheckpoint.checkpointActivity(String(local.id));
|
|
20202
|
+
return saved === null || saved.customId !== local.customId;
|
|
20203
|
+
}
|
|
20204
|
+
function planCustomIdRekeys(remoteActivities, deps) {
|
|
20205
|
+
const owners = remoteCustomIdOwners(remoteActivities);
|
|
20206
|
+
deps.customIdTracker.populateFromActivities(remoteActivities);
|
|
20207
|
+
const rekeys = [];
|
|
20208
|
+
for (const local of deps.adapter.getVisualOrderIds()) {
|
|
20209
|
+
const activity = deps.adapter.getActivity(local);
|
|
20210
|
+
if (!activity?.customId) continue;
|
|
20211
|
+
const owner = owners.get(activity.customId);
|
|
20212
|
+
if (owner === void 0) continue;
|
|
20213
|
+
const isSameEntity = owner === String(activity.id) && !isUnpersisted(activity);
|
|
20214
|
+
if (isSameEntity) continue;
|
|
20215
|
+
if (!isLocalCustomIdEdit(activity, deps)) continue;
|
|
20216
|
+
rekeys.push({
|
|
20217
|
+
activityId: activity.id,
|
|
20218
|
+
nextCustomId: deps.customIdTracker.generateCustomIdForRestoredChild(
|
|
20219
|
+
activity.customId
|
|
20220
|
+
)
|
|
20221
|
+
});
|
|
20222
|
+
}
|
|
20223
|
+
return rekeys;
|
|
20224
|
+
}
|
|
20225
|
+
function nextFreeActivityId(deps, claimed) {
|
|
20226
|
+
let candidate = deps.activityIdGen.next();
|
|
20227
|
+
const isTaken = (activityId) => deps.adapter.activityExists(activityId) || deps.isActivityIdReserved(String(activityId)) || claimed.has(String(activityId));
|
|
20228
|
+
while (isTaken(candidate)) candidate = deps.activityIdGen.next();
|
|
20229
|
+
return candidate;
|
|
20230
|
+
}
|
|
20231
|
+
function planLocalRekeys(remote, deps) {
|
|
20232
|
+
const remoteActivities = remote.activities;
|
|
20233
|
+
const remoteIds = /* @__PURE__ */ new Set();
|
|
20234
|
+
const remoteUniqueCorrelativeIds = /* @__PURE__ */ new Set();
|
|
20235
|
+
for (const remote2 of remoteActivities) {
|
|
20236
|
+
remoteIds.add(String(remote2.id));
|
|
20237
|
+
remoteUniqueCorrelativeIds.add(String(remote2.uniqueCorrelativeId));
|
|
20238
|
+
deps.activityIdGen.bump(remote2.id);
|
|
20239
|
+
deps.uidGen.bump(remote2.uniqueCorrelativeId);
|
|
20240
|
+
}
|
|
20241
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
20242
|
+
const activities = [];
|
|
20243
|
+
const uniqueCorrelativeIds = [];
|
|
20244
|
+
for (const local of deps.adapter.getAllActivities()) {
|
|
20245
|
+
if (!isUnpersisted(local)) continue;
|
|
20246
|
+
const localId = String(local.id);
|
|
20247
|
+
if (remoteIds.has(localId)) {
|
|
20248
|
+
const nextId = nextFreeActivityId(deps, claimed);
|
|
20249
|
+
claimed.add(String(nextId));
|
|
20250
|
+
activities.push({
|
|
20251
|
+
from: localId,
|
|
20252
|
+
to: String(nextId),
|
|
20253
|
+
parentId: deps.adapter.getParent(local.id)?.id ?? null,
|
|
20254
|
+
childIds: [...deps.adapter.getChildren(local.id)],
|
|
20255
|
+
outgoingLinkIds: [...deps.adapter.getOutgoingLinkIds(local.id)],
|
|
20256
|
+
incomingLinkIds: [...deps.adapter.getIncomingLinkIds(local.id)]
|
|
20257
|
+
});
|
|
20258
|
+
}
|
|
20259
|
+
if (remoteUniqueCorrelativeIds.has(String(local.uniqueCorrelativeId))) {
|
|
20260
|
+
uniqueCorrelativeIds.push({
|
|
20261
|
+
activityId: local.id,
|
|
20262
|
+
nextUniqueCorrelativeId: deps.uidGen.next()
|
|
20263
|
+
});
|
|
20264
|
+
}
|
|
20265
|
+
}
|
|
20266
|
+
return {
|
|
20267
|
+
activities,
|
|
20268
|
+
links: planLinkRekeys(remote.links, deps),
|
|
20269
|
+
uniqueCorrelativeIds,
|
|
20270
|
+
customIds: planCustomIdRekeys(remoteActivities, deps)
|
|
20271
|
+
};
|
|
20272
|
+
}
|
|
20273
|
+
function collectRekeyTouchedIds(plan) {
|
|
20274
|
+
const touched = /* @__PURE__ */ new Set();
|
|
20275
|
+
for (const rekey of plan.activities) {
|
|
20276
|
+
touched.add(rekey.from);
|
|
20277
|
+
touched.add(rekey.to);
|
|
20278
|
+
if (rekey.parentId !== null) touched.add(String(rekey.parentId));
|
|
20279
|
+
for (const childId of rekey.childIds) touched.add(String(childId));
|
|
20280
|
+
}
|
|
20281
|
+
for (const rekey of plan.uniqueCorrelativeIds) {
|
|
20282
|
+
touched.add(String(rekey.activityId));
|
|
20283
|
+
}
|
|
20284
|
+
for (const rekey of plan.customIds) touched.add(String(rekey.activityId));
|
|
20285
|
+
return touched;
|
|
20286
|
+
}
|
|
20287
|
+
function replaceId(ids, from, to) {
|
|
20288
|
+
return ids.map((candidate) => String(candidate) === from ? to : candidate);
|
|
20289
|
+
}
|
|
20290
|
+
function moveActivityIdentity(rekey, adapter) {
|
|
20291
|
+
const current = adapter.getActivity(rekey.from);
|
|
20292
|
+
if (!current) return;
|
|
20293
|
+
const relocated = structuredCloneActivity(current);
|
|
20294
|
+
relocated.id = rekey.to;
|
|
20295
|
+
const wasChecked = adapter.isChecked(rekey.from);
|
|
20296
|
+
const wasVisible = adapter.isVisible(rekey.from);
|
|
20297
|
+
const lastStartDate = adapter.getLastStartDate(rekey.from);
|
|
20298
|
+
const promotionSnapshot = adapter.getPromotionSnapshot(rekey.from);
|
|
20299
|
+
const progressFallback = adapter.getProgressFallback(rekey.from);
|
|
20300
|
+
adapter.removeActivity(rekey.from);
|
|
20301
|
+
adapter.addActivity(relocated);
|
|
20302
|
+
if (wasChecked) adapter.setChecked(rekey.to, true);
|
|
20303
|
+
if (!wasVisible) adapter.setVisible(rekey.to, false);
|
|
20304
|
+
if (lastStartDate) adapter.setLastStartDate(rekey.to, lastStartDate);
|
|
20305
|
+
if (promotionSnapshot) {
|
|
20306
|
+
adapter.setPromotionSnapshot(rekey.to, promotionSnapshot);
|
|
20307
|
+
}
|
|
20308
|
+
if (progressFallback !== null) {
|
|
20309
|
+
adapter.setProgressFallback(rekey.to, progressFallback);
|
|
20310
|
+
}
|
|
20311
|
+
for (const childId of rekey.childIds) {
|
|
20312
|
+
adapter.setActivityFields(childId, { parentId: rekey.to });
|
|
20313
|
+
}
|
|
20314
|
+
for (const linkId of rekey.outgoingLinkIds) {
|
|
20315
|
+
const link = adapter.getLink(linkId);
|
|
20316
|
+
if (link) adapter.setLinkEndpoints(linkId, rekey.to, link.target);
|
|
20317
|
+
}
|
|
20318
|
+
for (const linkId of rekey.incomingLinkIds) {
|
|
20319
|
+
const link = adapter.getLink(linkId);
|
|
20320
|
+
if (link) adapter.setLinkEndpoints(linkId, link.source, rekey.to);
|
|
20321
|
+
}
|
|
20322
|
+
const parent = adapter.getParent(rekey.to);
|
|
20323
|
+
if (!parent) return;
|
|
20324
|
+
const parentTracksNewActivity = parent.newActivityIds.some(
|
|
20325
|
+
(candidate) => String(candidate) === rekey.from
|
|
20326
|
+
);
|
|
20327
|
+
if (!parentTracksNewActivity) return;
|
|
20328
|
+
adapter.setActivityFields(parent.id, {
|
|
20329
|
+
newActivityIds: replaceId(parent.newActivityIds, rekey.from, rekey.to)
|
|
20330
|
+
});
|
|
20331
|
+
}
|
|
20332
|
+
function moveLinkIdentities(rekeys, adapter) {
|
|
20333
|
+
const before = /* @__PURE__ */ new Map();
|
|
20334
|
+
const after = [];
|
|
20335
|
+
for (const rekey of rekeys) {
|
|
20336
|
+
const current = adapter.getLink(rekey.from);
|
|
20337
|
+
if (!current) continue;
|
|
20338
|
+
before.set(rekey.from, { ...current });
|
|
20339
|
+
adapter.removeLink(rekey.from);
|
|
20340
|
+
const relocated = { ...current, id: rekey.to };
|
|
20341
|
+
adapter.addLink(relocated);
|
|
20342
|
+
after.push(relocated);
|
|
20343
|
+
}
|
|
20344
|
+
return { before, after };
|
|
20345
|
+
}
|
|
20346
|
+
function applyLocalRekeys(plan, adapter) {
|
|
20347
|
+
for (const rekey of plan.uniqueCorrelativeIds) {
|
|
20348
|
+
adapter.setActivityField(
|
|
20349
|
+
rekey.activityId,
|
|
20350
|
+
ACTIVITY_PROPERTY.UNIQUE_CORRELATIVE_ID,
|
|
20351
|
+
rekey.nextUniqueCorrelativeId
|
|
20352
|
+
);
|
|
20353
|
+
}
|
|
20354
|
+
for (const rekey of plan.customIds) {
|
|
20355
|
+
adapter.setActivityField(
|
|
20356
|
+
rekey.activityId,
|
|
20357
|
+
ACTIVITY_PROPERTY.CUSTOM_ID,
|
|
20358
|
+
rekey.nextCustomId
|
|
20359
|
+
);
|
|
20360
|
+
}
|
|
20361
|
+
for (const rekey of plan.activities) moveActivityIdentity(rekey, adapter);
|
|
20362
|
+
return moveLinkIdentities(plan.links, adapter);
|
|
20363
|
+
}
|
|
20364
|
+
|
|
20365
|
+
// src/dispatch/handlers/persistence-rebase.ts
|
|
20366
|
+
function pickDefaultCalendarId(calendars) {
|
|
20367
|
+
for (const calendar of calendars.values()) {
|
|
20368
|
+
if (calendar.is_default) return String(calendar.id);
|
|
20369
|
+
}
|
|
20370
|
+
return null;
|
|
20371
|
+
}
|
|
20372
|
+
function namesUnknownCalendar(action, calendars) {
|
|
20373
|
+
return action.remote.activities.some(
|
|
20374
|
+
(row2) => row2.calendarId != null && !calendars.has(String(row2.calendarId))
|
|
20375
|
+
);
|
|
20376
|
+
}
|
|
20377
|
+
function normalizeRemote(action, deps) {
|
|
20378
|
+
const { activities, activityIds } = normalizeActivities(
|
|
20379
|
+
[...action.remote.activities],
|
|
20380
|
+
{
|
|
20381
|
+
knownCalendarIds: new Set(deps.calendars.keys()),
|
|
20382
|
+
defaultCalendarId: pickDefaultCalendarId(deps.calendars),
|
|
20383
|
+
hoursPerDay: deps.sector.hoursPerDay,
|
|
20384
|
+
reporter: deps.reporter,
|
|
20385
|
+
recordDiagnostic: noopLoadDiagnosticRecorder
|
|
20386
|
+
}
|
|
20387
|
+
);
|
|
20388
|
+
const links = normalizeLinks([...action.remote.links], {
|
|
20389
|
+
knownActivityIds: activityIds,
|
|
20390
|
+
hoursPerDay: deps.sector.hoursPerDay,
|
|
20391
|
+
reporter: deps.reporter,
|
|
20392
|
+
recordDiagnostic: noopLoadDiagnosticRecorder
|
|
20393
|
+
});
|
|
20394
|
+
return { activities, links };
|
|
20395
|
+
}
|
|
20396
|
+
function hasPersistedRows(remote) {
|
|
20397
|
+
return remote.activities.every(
|
|
20398
|
+
(activity) => typeof activity.proplannerId === "number" && activity.proplannerId > 0
|
|
20399
|
+
);
|
|
20400
|
+
}
|
|
20401
|
+
async function dispatchPersistenceRebase(action, options, deps) {
|
|
20402
|
+
const { adapter, scheduler, sector } = deps;
|
|
20403
|
+
if (namesUnknownCalendar(action, deps.calendars)) {
|
|
20404
|
+
return { ok: false, reason: REJECTION_REASON.INVALID };
|
|
20405
|
+
}
|
|
20406
|
+
const remote = normalizeRemote(action, deps);
|
|
20407
|
+
if (!hasPersistedRows(remote)) {
|
|
20408
|
+
return { ok: false, reason: REJECTION_REASON.INVALID };
|
|
20409
|
+
}
|
|
20410
|
+
scheduler.invalidateAllCaches();
|
|
20411
|
+
const localLinkOrder = unsavedLinkIdsInCreationOrder(adapter);
|
|
20412
|
+
const rekeys = await rekeyCollidingLocalRows(remote, deps);
|
|
20413
|
+
const plan = planPersistenceRebase({
|
|
20414
|
+
remoteActivities: remote.activities,
|
|
20415
|
+
remoteLinks: remote.links,
|
|
20416
|
+
localActivities: adapter.getAllActivities(),
|
|
20417
|
+
localLinks: adapter.getAllLinks(),
|
|
20418
|
+
checkpoint: deps.persistenceCheckpoint
|
|
20419
|
+
});
|
|
20420
|
+
const siblingSources = captureSiblingOrders(
|
|
20421
|
+
adapter.getAllActivities(),
|
|
20422
|
+
remote.activities,
|
|
20423
|
+
deps.persistenceCheckpoint
|
|
20424
|
+
);
|
|
20425
|
+
const removedActivityIds = new Set(plan.removeActivityIds);
|
|
20426
|
+
const touchedIds = collectTouchedActivityIds(plan, adapter);
|
|
20427
|
+
const beforeSnap = await snapshotBeforeRekeys(adapter, touchedIds, rekeys);
|
|
20428
|
+
for (const activityId of rekeys.touchedIds) touchedIds.add(activityId);
|
|
20429
|
+
const beforeLinks = captureLinksBefore(plan, removedActivityIds, adapter);
|
|
20430
|
+
const viewStateChanges2 = captureDeletedViewState(adapter, removedActivityIds);
|
|
20431
|
+
for (const linkId of beforeLinks.keys()) adapter.removeLink(linkId);
|
|
20432
|
+
for (const activityId of removedActivityIds)
|
|
20433
|
+
adapter.removeActivity(activityId);
|
|
20434
|
+
for (const activity of plan.createActivities) {
|
|
20435
|
+
adapter.addActivity(structuredCloneActivity(activity));
|
|
20436
|
+
deps.uidGen.bump(activity.uniqueCorrelativeId);
|
|
20437
|
+
deps.customIdTracker.populateFromActivities([activity]);
|
|
20438
|
+
}
|
|
20439
|
+
for (const merge of plan.updateActivities) {
|
|
20440
|
+
adapter.setActivityFields(merge.activityId, merge.fields);
|
|
20441
|
+
}
|
|
20442
|
+
deriveLeafEndDates(plan, adapter);
|
|
20443
|
+
const orphanConflicts = reparentOrphans(
|
|
20444
|
+
adapter,
|
|
20445
|
+
deps.persistenceCheckpoint,
|
|
20446
|
+
touchedIds
|
|
20447
|
+
);
|
|
20448
|
+
demoteEmptiedParents(deps, touchedIds);
|
|
20449
|
+
normalizeMergedParents(adapter, touchedIds);
|
|
20450
|
+
recomputeTouchedRollups(adapter, touchedIds);
|
|
20451
|
+
const linkChanges = [
|
|
20452
|
+
...rekeyedLinkChanges(rekeys.links),
|
|
20453
|
+
...applyLinkPlan(plan, beforeLinks, adapter)
|
|
20454
|
+
];
|
|
20455
|
+
const droppedLinkConflicts = dropLinksWithoutEndpoints(adapter, linkChanges);
|
|
20456
|
+
const cycleRepair = breakDependencyCycles(
|
|
20457
|
+
adapter,
|
|
20458
|
+
followRekeys(localLinkOrder, rekeys.plan.links),
|
|
20459
|
+
linkChanges
|
|
20460
|
+
);
|
|
20461
|
+
const createdIds = new Set(
|
|
20462
|
+
plan.createActivities.map((activity) => String(activity.id))
|
|
20463
|
+
);
|
|
20464
|
+
const insertedIds = /* @__PURE__ */ new Set([
|
|
20465
|
+
...createdIds,
|
|
20466
|
+
...rekeys.plan.activities.map((rekey) => rekey.to)
|
|
20467
|
+
]);
|
|
20468
|
+
const siblingOrder = applySiblingOrder(adapter, siblingSources);
|
|
20469
|
+
const correlativeShifts = [
|
|
20470
|
+
...siblingOrder.shifts,
|
|
20471
|
+
...recomputeCorrelativeIds(adapter)
|
|
20472
|
+
].filter((shift) => !insertedIds.has(shift.activityId));
|
|
20473
|
+
const { scheduledIds } = await runPostMutation(
|
|
20474
|
+
{
|
|
20475
|
+
adapter,
|
|
20476
|
+
scheduler,
|
|
20477
|
+
sector,
|
|
20478
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId,
|
|
20479
|
+
runAutomaticP6Schedule: deps.runAutomaticP6Schedule
|
|
20480
|
+
},
|
|
20481
|
+
{
|
|
20482
|
+
action,
|
|
20483
|
+
autoscheduleFrom: AUTOSCHEDULE_SCOPE.ROOTS,
|
|
20484
|
+
recomputeParentsFrom: withChildrenOfParents(touchedIds, adapter),
|
|
20485
|
+
recomputeParentProgress: true,
|
|
20486
|
+
now: deps.now,
|
|
20487
|
+
options
|
|
20488
|
+
}
|
|
20489
|
+
);
|
|
20490
|
+
alignParentAutoScheduling(adapter, touchedIds);
|
|
20491
|
+
const changes = await assembleChangeSet(adapter, {
|
|
20492
|
+
source: action,
|
|
20493
|
+
beforeSnap,
|
|
20494
|
+
touchedIds,
|
|
20495
|
+
scheduledIds,
|
|
20496
|
+
links: linkChanges,
|
|
20497
|
+
hoursPerDay: sector.hoursPerDay,
|
|
20498
|
+
correlativeShifts
|
|
20499
|
+
});
|
|
20500
|
+
const report = {
|
|
20501
|
+
remoteChanges: plan.remoteChanges,
|
|
20502
|
+
conflicts: [
|
|
20503
|
+
...plan.conflicts,
|
|
20504
|
+
...orphanConflicts,
|
|
20505
|
+
...siblingOrder.conflicts,
|
|
20506
|
+
...droppedLinkConflicts,
|
|
20507
|
+
...cycleRepair.conflicts
|
|
20508
|
+
],
|
|
20509
|
+
createdActivityIds: [...createdIds],
|
|
20510
|
+
removedActivityIds: [...removedActivityIds],
|
|
20511
|
+
rekeyedActivities: rekeys.plan.activities.map(({ from, to }) => ({
|
|
20512
|
+
from,
|
|
20513
|
+
to
|
|
20514
|
+
})),
|
|
20515
|
+
rekeyedLinks: rekeys.plan.links,
|
|
20516
|
+
createdLinkIds: plan.createLinks.map((link) => String(link.id)),
|
|
20517
|
+
removedLinkIds: linkIdsGoneFromState(linkChanges, adapter)
|
|
20518
|
+
};
|
|
20519
|
+
return {
|
|
20520
|
+
ok: true,
|
|
20521
|
+
changes: viewStateChanges2.length > 0 ? { ...changes, viewState: viewStateChanges2 } : changes,
|
|
20522
|
+
rebase: report,
|
|
20523
|
+
__beforeSnap: beforeSnap,
|
|
20524
|
+
__beforeLinks: new Map([
|
|
20525
|
+
...rekeys.links.before,
|
|
20526
|
+
...beforeLinks,
|
|
20527
|
+
...cycleRepair.removed
|
|
20528
|
+
]),
|
|
20529
|
+
__rebaseCheckpoint: { activities: remote.activities, links: remote.links }
|
|
20530
|
+
};
|
|
20531
|
+
}
|
|
20532
|
+
function linkIdsGoneFromState(linkChanges, adapter) {
|
|
20533
|
+
return linkChanges.filter((change) => change.kind === ENTITY_CHANGE_KIND.DELETED).map((change) => String(change.id)).filter((linkId) => !adapter.getLink(linkId));
|
|
20534
|
+
}
|
|
20535
|
+
async function rekeyCollidingLocalRows(remote, deps) {
|
|
20536
|
+
const plan = planLocalRekeys(remote, deps);
|
|
20537
|
+
const touchedIds = collectRekeyTouchedIds(plan);
|
|
20538
|
+
const beforeSnap = await snapshotActivities(deps.adapter, touchedIds);
|
|
20539
|
+
const links = applyLocalRekeys(plan, deps.adapter);
|
|
20540
|
+
return { plan, touchedIds, beforeSnap, links };
|
|
20541
|
+
}
|
|
20542
|
+
function rekeyedLinkChanges(links) {
|
|
20543
|
+
const changes = [
|
|
20544
|
+
...buildLinkDeletions(new Map(links.before))
|
|
20545
|
+
];
|
|
20546
|
+
for (const after of links.after) {
|
|
20547
|
+
changes.push({
|
|
20548
|
+
id: String(after.id),
|
|
20549
|
+
kind: ENTITY_CHANGE_KIND.CREATED,
|
|
20550
|
+
fields: {
|
|
20551
|
+
source: { before: void 0, after: String(after.source) },
|
|
20552
|
+
target: { before: void 0, after: String(after.target) },
|
|
20553
|
+
type: { before: void 0, after: after.type },
|
|
20554
|
+
lag: { before: void 0, after: Number(after.lag) }
|
|
20555
|
+
},
|
|
20556
|
+
after: snapshotToLink(after)
|
|
20557
|
+
});
|
|
20558
|
+
}
|
|
20559
|
+
return changes;
|
|
20560
|
+
}
|
|
20561
|
+
async function snapshotBeforeRekeys(adapter, touchedIds, rekeys) {
|
|
20562
|
+
const touchedOutsideRekey = new Set(
|
|
20563
|
+
[...touchedIds].filter((activityId) => !rekeys.touchedIds.has(activityId))
|
|
20564
|
+
);
|
|
20565
|
+
return new Map([
|
|
20566
|
+
...await snapshotActivities(adapter, touchedOutsideRekey),
|
|
20567
|
+
...rekeys.beforeSnap
|
|
20568
|
+
]);
|
|
20569
|
+
}
|
|
20570
|
+
function deriveLeafEndDates(plan, adapter) {
|
|
20571
|
+
for (const merge of plan.updateActivities) {
|
|
20572
|
+
const activity = adapter.getActivity(merge.activityId);
|
|
20573
|
+
if (!activity || adapter.hasChildren(activity.id)) continue;
|
|
20574
|
+
adapter.setActivityField(
|
|
20575
|
+
activity.id,
|
|
20576
|
+
ACTIVITY_PROPERTY.END_DATE,
|
|
20577
|
+
adapter.calculateEndDate({
|
|
20578
|
+
startDate: activity.startDate,
|
|
20579
|
+
durationHours: activity.durationHours,
|
|
20580
|
+
task: activity
|
|
20581
|
+
})
|
|
20582
|
+
);
|
|
20583
|
+
}
|
|
20584
|
+
}
|
|
20585
|
+
function unsavedLinkIdsInCreationOrder(adapter) {
|
|
20586
|
+
return adapter.getAllLinks().filter((link) => {
|
|
20587
|
+
const proplannerId = link.proplannerId;
|
|
20588
|
+
return proplannerId === null || proplannerId === void 0 || proplannerId === 0;
|
|
20589
|
+
}).map((link) => String(link.id)).sort((left, right) => Number(left) - Number(right));
|
|
20590
|
+
}
|
|
20591
|
+
function followRekeys(linkIds, rekeys) {
|
|
20592
|
+
const renamed = new Map(rekeys.map(({ from, to }) => [from, to]));
|
|
20593
|
+
return linkIds.map((linkId) => renamed.get(linkId) ?? linkId);
|
|
20594
|
+
}
|
|
20595
|
+
function alignParentAutoScheduling(adapter, touchedIds) {
|
|
20596
|
+
for (const activityId of touchedIds) {
|
|
20597
|
+
if (!adapter.hasChildren(activityId)) continue;
|
|
20598
|
+
const activity = adapter.getActivity(activityId);
|
|
20599
|
+
if (!activity) continue;
|
|
20600
|
+
const autoScheduling = isAutoSchedulingEnabledForProgress(
|
|
20601
|
+
activity.progress
|
|
20602
|
+
);
|
|
20603
|
+
if (activity.autoScheduling === autoScheduling) continue;
|
|
20604
|
+
adapter.setActivityField(
|
|
20605
|
+
activityId,
|
|
20606
|
+
ACTIVITY_PROPERTY.AUTO_SCHEDULING,
|
|
20607
|
+
autoScheduling
|
|
20608
|
+
);
|
|
20609
|
+
}
|
|
20610
|
+
}
|
|
20611
|
+
function normalizeMergedParents(adapter, touchedIds) {
|
|
20612
|
+
for (const activityId of touchedIds) {
|
|
20613
|
+
if (!adapter.hasChildren(activityId)) continue;
|
|
20614
|
+
const activity = adapter.getActivity(activityId);
|
|
20615
|
+
if (!activity) continue;
|
|
20616
|
+
if (activity.type !== ACTIVITY_TYPE.PROJECT) {
|
|
20617
|
+
adapter.setActivityField(
|
|
20618
|
+
activityId,
|
|
20619
|
+
ACTIVITY_PROPERTY.TYPE,
|
|
20620
|
+
ACTIVITY_TYPE.PROJECT
|
|
20621
|
+
);
|
|
20622
|
+
}
|
|
20623
|
+
if (activity.customId) {
|
|
20624
|
+
adapter.setActivityField(activityId, ACTIVITY_PROPERTY.CUSTOM_ID, null);
|
|
20625
|
+
}
|
|
20626
|
+
}
|
|
20627
|
+
}
|
|
20628
|
+
function demoteEmptiedParents(deps, touchedIds) {
|
|
20629
|
+
const { adapter } = deps;
|
|
20630
|
+
for (const activityId of touchedIds) {
|
|
20631
|
+
const activity = adapter.getActivity(activityId);
|
|
20632
|
+
if (!activity || activity.type !== ACTIVITY_TYPE.PROJECT) continue;
|
|
20633
|
+
if (isRootParent(activity.parentId) || adapter.hasChildren(activityId)) {
|
|
20634
|
+
continue;
|
|
20635
|
+
}
|
|
20636
|
+
const demotion = buildParentDemotionMutations({
|
|
20637
|
+
parent: activity,
|
|
20638
|
+
remainingChildIds: [],
|
|
20639
|
+
defaultDurationHours: deps.sector.hoursPerDay,
|
|
20640
|
+
promotionSnapshot: adapter.getPromotionSnapshot(activityId),
|
|
20641
|
+
computeEndDate: (startDate, durationHours) => adapter.calculateEndDate({ startDate, durationHours, task: activity }),
|
|
20642
|
+
dataDateCapability: deps.dataDateCapability,
|
|
20643
|
+
hasCompleteSchedulingState: deps.hasCompleteSchedulingState
|
|
20644
|
+
});
|
|
20645
|
+
if (!demotion) continue;
|
|
20646
|
+
adapter.setActivityFields(activityId, demotion.fields);
|
|
20647
|
+
const previousSiblingId = findPrevSiblingHelper(activityId, adapter);
|
|
20648
|
+
const previousCustomId = previousSiblingId ? adapter.getActivity(previousSiblingId)?.customId : null;
|
|
20649
|
+
adapter.setActivityField(
|
|
20650
|
+
activityId,
|
|
20651
|
+
ACTIVITY_PROPERTY.CUSTOM_ID,
|
|
20652
|
+
deps.customIdTracker.generateCustomIdForRestoredChild(
|
|
20653
|
+
previousCustomId || null
|
|
20654
|
+
)
|
|
20655
|
+
);
|
|
20656
|
+
}
|
|
20657
|
+
}
|
|
20658
|
+
function recomputeTouchedRollups(adapter, touchedIds) {
|
|
20659
|
+
const parentIds = /* @__PURE__ */ new Set();
|
|
20660
|
+
for (const activityId of touchedIds) {
|
|
20661
|
+
const activity = adapter.getActivity(activityId);
|
|
20662
|
+
if (!activity) continue;
|
|
20663
|
+
if (adapter.hasChildren(activityId)) parentIds.add(activityId);
|
|
20664
|
+
if (!isRootParent(activity.parentId)) {
|
|
20665
|
+
parentIds.add(String(activity.parentId));
|
|
20666
|
+
}
|
|
20667
|
+
}
|
|
20668
|
+
for (const parentId of parentIds) {
|
|
20669
|
+
recomputeRollupCascadesForParent(parentId, adapter);
|
|
20670
|
+
}
|
|
20671
|
+
}
|
|
20672
|
+
function withChildrenOfParents(activityIds, adapter) {
|
|
20673
|
+
const seeds = new Set(activityIds);
|
|
20674
|
+
for (const activityId of activityIds) {
|
|
20675
|
+
for (const childId of adapter.getChildren(activityId)) {
|
|
20676
|
+
seeds.add(String(childId));
|
|
20677
|
+
}
|
|
20678
|
+
}
|
|
20679
|
+
return seeds;
|
|
20680
|
+
}
|
|
20681
|
+
function collectTouchedActivityIds(plan, adapter) {
|
|
20682
|
+
const touched = /* @__PURE__ */ new Set();
|
|
20683
|
+
const touchParent = (parentId) => {
|
|
20684
|
+
if (parentId === null || parentId === void 0) return;
|
|
20685
|
+
if (isRootParent(parentId)) return;
|
|
20686
|
+
touched.add(String(parentId));
|
|
20687
|
+
};
|
|
20688
|
+
for (const activityId of plan.removeActivityIds) {
|
|
20689
|
+
touched.add(activityId);
|
|
20690
|
+
touchParent(adapter.getActivity(activityId)?.parentId);
|
|
20691
|
+
}
|
|
20692
|
+
for (const activity of plan.createActivities) {
|
|
20693
|
+
touched.add(String(activity.id));
|
|
20694
|
+
touchParent(activity.parentId);
|
|
20695
|
+
}
|
|
20696
|
+
for (const merge of plan.updateActivities) {
|
|
20697
|
+
touched.add(merge.activityId);
|
|
20698
|
+
touchParent(adapter.getActivity(merge.activityId)?.parentId);
|
|
20699
|
+
if (merge.fields.parentId !== void 0) touchParent(merge.fields.parentId);
|
|
20700
|
+
}
|
|
20701
|
+
return touched;
|
|
20702
|
+
}
|
|
20703
|
+
function captureLinksBefore(plan, removedActivityIds, adapter) {
|
|
20704
|
+
const before = /* @__PURE__ */ new Map();
|
|
20705
|
+
const incident = collectIncidentLinkIds(removedActivityIds, adapter);
|
|
20706
|
+
for (const linkId of [...plan.removeLinkIds, ...incident]) {
|
|
20707
|
+
const link = adapter.getLink(linkId);
|
|
20708
|
+
if (link) before.set(String(linkId), { ...link });
|
|
20709
|
+
}
|
|
20710
|
+
return before;
|
|
20711
|
+
}
|
|
20712
|
+
function nearestSurvivingAncestor(missingParentId, adapter, checkpoint) {
|
|
20713
|
+
const visited = /* @__PURE__ */ new Set();
|
|
20714
|
+
let current = missingParentId;
|
|
20715
|
+
while (current !== null && !visited.has(current)) {
|
|
20716
|
+
if (adapter.activityExists(current)) return current;
|
|
20717
|
+
visited.add(current);
|
|
20718
|
+
const saved = checkpoint.checkpointActivity(current);
|
|
20719
|
+
if (!saved || isRootParent(saved.parentId)) return null;
|
|
20720
|
+
current = String(saved.parentId);
|
|
20721
|
+
}
|
|
20722
|
+
return null;
|
|
20723
|
+
}
|
|
20724
|
+
function reparentOrphans(adapter, checkpoint, touchedIds) {
|
|
20725
|
+
const conflicts = [];
|
|
20726
|
+
for (const activity of adapter.getAllActivities()) {
|
|
20727
|
+
if (isRootParent(activity.parentId)) continue;
|
|
20728
|
+
if (adapter.activityExists(String(activity.parentId))) continue;
|
|
20729
|
+
const previousParentId = String(activity.parentId);
|
|
20730
|
+
const adoptiveParentId = nearestSurvivingAncestor(
|
|
20731
|
+
previousParentId,
|
|
20732
|
+
adapter,
|
|
20733
|
+
checkpoint
|
|
20734
|
+
);
|
|
20735
|
+
adapter.setActivityField(
|
|
20736
|
+
activity.id,
|
|
20737
|
+
ACTIVITY_PROPERTY.PARENT,
|
|
20738
|
+
adoptiveParentId
|
|
20739
|
+
);
|
|
20740
|
+
touchedIds.add(String(activity.id));
|
|
20741
|
+
if (adoptiveParentId !== null) touchedIds.add(adoptiveParentId);
|
|
20742
|
+
conflicts.push({
|
|
20743
|
+
entity: PERSISTENCE_REBASE_ENTITY.ACTIVITY,
|
|
20744
|
+
id: String(activity.id),
|
|
20745
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.PARENT_REMOVED,
|
|
20746
|
+
field: ACTIVITY_PROPERTY.PARENT,
|
|
20747
|
+
mine: previousParentId,
|
|
20748
|
+
theirs: adoptiveParentId ?? ROOT_PARENT_ID
|
|
20749
|
+
});
|
|
20750
|
+
}
|
|
20751
|
+
return conflicts;
|
|
20752
|
+
}
|
|
20753
|
+
function applyLinkPlan(plan, beforeLinks, adapter) {
|
|
20754
|
+
const changes = [
|
|
20755
|
+
...buildLinkDeletions(new Map(beforeLinks))
|
|
20756
|
+
];
|
|
20757
|
+
for (const link of plan.createLinks) {
|
|
20758
|
+
if (adapter.getLink(link.id)) continue;
|
|
20759
|
+
adapter.addLink({ ...link, freeSlackHours: null, isCritical: false });
|
|
20760
|
+
const after = adapter.getLink(link.id);
|
|
20761
|
+
if (!after) continue;
|
|
20762
|
+
changes.push({
|
|
20763
|
+
id: String(link.id),
|
|
20764
|
+
kind: ENTITY_CHANGE_KIND.CREATED,
|
|
20765
|
+
fields: {
|
|
20766
|
+
source: { before: void 0, after: String(after.source) },
|
|
20767
|
+
target: { before: void 0, after: String(after.target) },
|
|
20768
|
+
type: { before: void 0, after: after.type },
|
|
20769
|
+
lag: { before: void 0, after: Number(after.lag) }
|
|
20770
|
+
},
|
|
20771
|
+
after: snapshotToLink(after)
|
|
20772
|
+
});
|
|
20773
|
+
}
|
|
20774
|
+
for (const merge of plan.updateLinks) {
|
|
20775
|
+
const before = adapter.getLink(merge.linkId);
|
|
20776
|
+
if (!before) continue;
|
|
20777
|
+
const beforeView = snapshotToLink(before);
|
|
20778
|
+
const fields = {};
|
|
20779
|
+
for (const [field, value] of Object.entries(merge.fields)) {
|
|
20780
|
+
adapter.setLinkFieldDynamic(merge.linkId, field, value);
|
|
20781
|
+
fields[field] = { before: Reflect.get(beforeView, field), after: value };
|
|
20782
|
+
}
|
|
20783
|
+
const after = adapter.getLink(merge.linkId);
|
|
20784
|
+
if (!after) continue;
|
|
20785
|
+
changes.push({
|
|
20786
|
+
id: String(merge.linkId),
|
|
20787
|
+
kind: ENTITY_CHANGE_KIND.UPDATED,
|
|
20788
|
+
fields,
|
|
20789
|
+
after: snapshotToLink(after)
|
|
20790
|
+
});
|
|
20791
|
+
}
|
|
20792
|
+
return changes;
|
|
20793
|
+
}
|
|
20794
|
+
function dropLinksWithoutEndpoints(adapter, changes) {
|
|
20795
|
+
const conflicts = [];
|
|
20796
|
+
for (const link of adapter.getAllLinks()) {
|
|
20797
|
+
const missingEndpoint = [link.source, link.target].find(
|
|
20798
|
+
(activityId) => !adapter.activityExists(String(activityId))
|
|
20799
|
+
);
|
|
20800
|
+
if (missingEndpoint === void 0) continue;
|
|
20801
|
+
const linkId = String(link.id);
|
|
20802
|
+
const before = { ...link };
|
|
20803
|
+
const sourceIsMissing = String(link.source) === String(missingEndpoint);
|
|
20804
|
+
adapter.removeLink(linkId);
|
|
20805
|
+
changes.push(...buildLinkDeletions(/* @__PURE__ */ new Map([[linkId, before]])));
|
|
20806
|
+
conflicts.push({
|
|
20807
|
+
entity: PERSISTENCE_REBASE_ENTITY.LINK,
|
|
20808
|
+
id: linkId,
|
|
20809
|
+
kind: PERSISTENCE_REBASE_CONFLICT_KIND.DELETED_REMOTELY,
|
|
20810
|
+
field: sourceIsMissing ? LINK_PROPERTY.SOURCE : LINK_PROPERTY.TARGET,
|
|
20811
|
+
mine: snapshotToLink(before),
|
|
20812
|
+
theirs: null
|
|
20813
|
+
});
|
|
20814
|
+
}
|
|
20815
|
+
return conflicts;
|
|
20816
|
+
}
|
|
20817
|
+
|
|
20818
|
+
// src/baselines/apply-baseline-points.ts
|
|
20819
|
+
function applyBaselinePoints(adapter, points, reporter, recordDiagnostic = noopLoadDiagnosticRecorder) {
|
|
20820
|
+
const idByProplanner = /* @__PURE__ */ new Map();
|
|
20821
|
+
adapter.forEachActivity((a, id) => {
|
|
20822
|
+
const pid = a.proplannerId;
|
|
20823
|
+
if (typeof pid === "number") idByProplanner.set(pid, id);
|
|
20824
|
+
});
|
|
20825
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
20826
|
+
for (const p of points) {
|
|
20827
|
+
const activityId = p.activityId;
|
|
20828
|
+
if (typeof activityId !== "number") {
|
|
20829
|
+
recordDiagnostic(
|
|
20830
|
+
LOAD_DIAGNOSTIC_CODE.INVALID_BASELINE_POINT,
|
|
20831
|
+
String(activityId ?? "unknown")
|
|
20832
|
+
);
|
|
20833
|
+
continue;
|
|
20834
|
+
}
|
|
20835
|
+
const coreId = idByProplanner.get(activityId);
|
|
20836
|
+
if (coreId === void 0) {
|
|
20837
|
+
recordDiagnostic(
|
|
20838
|
+
LOAD_DIAGNOSTIC_CODE.INVALID_BASELINE_POINT,
|
|
20839
|
+
String(activityId)
|
|
20840
|
+
);
|
|
20841
|
+
continue;
|
|
20842
|
+
}
|
|
20843
|
+
const arr = grouped.get(coreId);
|
|
20844
|
+
if (arr) arr.push(p);
|
|
20845
|
+
else grouped.set(coreId, [p]);
|
|
20846
|
+
}
|
|
20847
|
+
const changed2 = [];
|
|
20848
|
+
for (const [coreId, group] of grouped) {
|
|
20849
|
+
const isMilestone = adapter.getActivity(coreId)?.type === ACTIVITY_TYPE.MILESTONE;
|
|
20850
|
+
adapter.setActivityField(
|
|
20851
|
+
coreId,
|
|
20852
|
+
"baselinePoints",
|
|
20853
|
+
group.map(
|
|
20854
|
+
(point) => normalizeBaselinePoint(
|
|
20855
|
+
point,
|
|
20856
|
+
reporter,
|
|
20857
|
+
{
|
|
20858
|
+
record: recordDiagnostic,
|
|
20859
|
+
sampleId: coreId
|
|
20860
|
+
},
|
|
20861
|
+
isMilestone
|
|
20862
|
+
)
|
|
20863
|
+
).filter((point) => point !== null)
|
|
20864
|
+
);
|
|
20865
|
+
changed2.push(coreId);
|
|
20866
|
+
}
|
|
20867
|
+
adapter.forEachActivity((a, id) => {
|
|
20868
|
+
if (grouped.has(id)) return;
|
|
20869
|
+
const existing = a.baselinePoints;
|
|
20870
|
+
if (Array.isArray(existing) && existing.length > 0) {
|
|
20871
|
+
adapter.setActivityField(id, "baselinePoints", []);
|
|
20872
|
+
changed2.push(id);
|
|
20873
|
+
}
|
|
20874
|
+
});
|
|
20875
|
+
return changed2;
|
|
20876
|
+
}
|
|
20877
|
+
|
|
20878
|
+
// src/baselines/baseline-snapshot.ts
|
|
20879
|
+
function buildBaselineSnapshot(activity) {
|
|
20880
|
+
const active = getActiveBaseline(activity);
|
|
20881
|
+
if (!active) return null;
|
|
20882
|
+
return {
|
|
20883
|
+
startDate: active.startDate,
|
|
20884
|
+
endDate: active.endDate,
|
|
20885
|
+
durationDays: active.durationDays,
|
|
20886
|
+
cost: active.cost,
|
|
20887
|
+
workHours: active.workHours
|
|
20888
|
+
};
|
|
20889
|
+
}
|
|
20890
|
+
function emitBaselineSnapshotColumns(state) {
|
|
20891
|
+
const changed2 = [];
|
|
20892
|
+
state.forEachActivity((activity, id) => {
|
|
20893
|
+
const snapshot = buildBaselineSnapshot(activity);
|
|
20894
|
+
if (!snapshot) {
|
|
20895
|
+
if (activity.baselineSnapshot !== null) {
|
|
20896
|
+
state.setActivityField(id, "baselineSnapshot", null);
|
|
20897
|
+
changed2.push(id);
|
|
20898
|
+
}
|
|
20899
|
+
return;
|
|
20900
|
+
}
|
|
20901
|
+
state.setActivityField(id, "baselineSnapshot", snapshot);
|
|
20902
|
+
changed2.push(id);
|
|
20903
|
+
});
|
|
20904
|
+
return changed2;
|
|
20905
|
+
}
|
|
20906
|
+
|
|
20907
|
+
// src/baselines/apply-baseline-overlay.ts
|
|
20908
|
+
function applyBaselineOverlay(state, points, now, reporter, defaultBaseCalendarId = null, recordDiagnostic = noopLoadDiagnosticRecorder) {
|
|
20909
|
+
const changed2 = /* @__PURE__ */ new Set();
|
|
20910
|
+
for (const id of applyBaselinePoints(
|
|
20911
|
+
state,
|
|
20912
|
+
points,
|
|
20913
|
+
reporter,
|
|
20914
|
+
recordDiagnostic
|
|
20915
|
+
)) {
|
|
20916
|
+
changed2.add(id);
|
|
20917
|
+
}
|
|
20918
|
+
if (now) {
|
|
20919
|
+
for (const id of runExpectedProgressBase(state, now, defaultBaseCalendarId))
|
|
20920
|
+
changed2.add(id);
|
|
20921
|
+
}
|
|
20922
|
+
for (const id of emitBaselineSnapshotColumns(state)) changed2.add(id);
|
|
20923
|
+
for (const id of emitRealCost(state)) changed2.add(id);
|
|
20924
|
+
return [...changed2];
|
|
20925
|
+
}
|
|
20926
|
+
|
|
20927
|
+
// src/propagations/downward/recompute-all-ponderators.ts
|
|
20928
|
+
function recomputeAllPonderators(criterion, adapter) {
|
|
20929
|
+
const basisById = /* @__PURE__ */ new Map();
|
|
20930
|
+
adapter.forEachActivity((_a, id) => {
|
|
20931
|
+
adapter.setActivityField(id, COLUMN.PONDERATOR, 0);
|
|
20932
|
+
});
|
|
20933
|
+
adapter.forEachActivity((_a, id) => {
|
|
20934
|
+
if (adapter.getChildren(id).length > 0) {
|
|
20935
|
+
recomputePonderatorsForParent(String(id), criterion, adapter, basisById);
|
|
20936
|
+
}
|
|
20937
|
+
});
|
|
20938
|
+
}
|
|
20939
|
+
|
|
20940
|
+
// src/dispatch/shared/baseline-weighted-fields.ts
|
|
20941
|
+
function recomputeBaselineWeightedFields(deps, criterion) {
|
|
20942
|
+
recomputeAllPonderators(criterion, deps.adapter);
|
|
20943
|
+
recomputeAllProgressRollup(deps.adapter, criterion);
|
|
20944
|
+
if (deps.now) {
|
|
20945
|
+
runExpectedProgressBase(
|
|
20946
|
+
deps.adapter,
|
|
20947
|
+
deps.now,
|
|
20948
|
+
deps.defaultBaseCalendarId ?? null
|
|
20949
|
+
);
|
|
20950
|
+
applyExpectedProgressLive(deps.adapter, deps.now);
|
|
20951
|
+
}
|
|
20952
|
+
emitRealCost(deps.adapter);
|
|
20953
|
+
if (deps.now) applyStatusPass(deps.adapter, deps.sector.statusCriteria);
|
|
20954
|
+
}
|
|
20955
|
+
|
|
20956
|
+
// src/dispatch/handlers/baseline-apply.ts
|
|
20957
|
+
function activeBaselineSignature(deps) {
|
|
20958
|
+
return JSON.stringify(
|
|
20959
|
+
deps.adapter.getAllIds().map((id) => {
|
|
20960
|
+
const point = deps.adapter.getActivity(id)?.baselinePoints.find((candidate) => candidate.isActiveVersion);
|
|
20961
|
+
return point ? [
|
|
20962
|
+
String(id),
|
|
20963
|
+
point.versionId,
|
|
20964
|
+
point.startDate?.getTime() ?? null,
|
|
20965
|
+
point.endDate?.getTime() ?? null,
|
|
20966
|
+
point.durationDays,
|
|
20967
|
+
point.cost,
|
|
20968
|
+
point.workHours,
|
|
20969
|
+
point.baseCalendarId,
|
|
20970
|
+
point.hoursPerDay,
|
|
20971
|
+
point.hoursPerWeek
|
|
20972
|
+
] : [String(id), null];
|
|
20973
|
+
})
|
|
20974
|
+
);
|
|
20975
|
+
}
|
|
20976
|
+
async function dispatchBaselineApply(action, deps) {
|
|
20977
|
+
const beforeSnap = await snapshotActivities(
|
|
20978
|
+
deps.adapter,
|
|
20979
|
+
new Set(deps.adapter.getAllIds())
|
|
20980
|
+
);
|
|
20981
|
+
const activeBaselineBefore = activeBaselineSignature(deps);
|
|
20982
|
+
const changedIds = applyBaselineOverlay(
|
|
20983
|
+
deps.adapter,
|
|
20984
|
+
action.points,
|
|
20985
|
+
deps.now,
|
|
20986
|
+
deps.reporter,
|
|
20987
|
+
deps.defaultBaseCalendarId ?? null,
|
|
20988
|
+
void 0
|
|
20989
|
+
);
|
|
20990
|
+
applyBaselineVariancePass(deps.adapter, deps.sector.hoursPerDay);
|
|
20991
|
+
applyRoleBaselineVariancePass(deps.adapter, deps.sector.hoursPerDay);
|
|
20992
|
+
if (activeBaselineSignature(deps) !== activeBaselineBefore) {
|
|
20993
|
+
recomputeBaselineWeightedFields(
|
|
20994
|
+
deps,
|
|
20995
|
+
deps.sector.activityCreter ?? ACTIVITY_PONDERATOR_CRITERION.DURATION
|
|
20996
|
+
);
|
|
20997
|
+
}
|
|
20998
|
+
return {
|
|
20999
|
+
ok: true,
|
|
21000
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
21001
|
+
source: action,
|
|
21002
|
+
beforeSnap,
|
|
21003
|
+
touchedIds: changedIds,
|
|
21004
|
+
scheduledIds: [],
|
|
21005
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
21006
|
+
})
|
|
21007
|
+
};
|
|
21008
|
+
}
|
|
21009
|
+
|
|
21010
|
+
// src/dispatch/handlers/ponderator-criterion-set.ts
|
|
21011
|
+
function isValidCriterion(value) {
|
|
21012
|
+
return Object.values(ACTIVITY_PONDERATOR_CRITERION).includes(
|
|
21013
|
+
value
|
|
21014
|
+
);
|
|
21015
|
+
}
|
|
21016
|
+
async function dispatchPonderatorCriterionSet(action, deps) {
|
|
21017
|
+
if (!isValidCriterion(action.criterion)) {
|
|
21018
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
21019
|
+
}
|
|
21020
|
+
const allIds = new Set(deps.adapter.getAllIds());
|
|
21021
|
+
const beforeSnap = await snapshotActivities(deps.adapter, allIds);
|
|
21022
|
+
recomputeBaselineWeightedFields(deps, action.criterion);
|
|
21023
|
+
deps.sector.activityCreter = action.criterion;
|
|
21024
|
+
return {
|
|
21025
|
+
ok: true,
|
|
21026
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
21027
|
+
source: action,
|
|
21028
|
+
beforeSnap,
|
|
21029
|
+
touchedIds: allIds,
|
|
21030
|
+
scheduledIds: [],
|
|
21031
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
21032
|
+
})
|
|
21033
|
+
};
|
|
21034
|
+
}
|
|
21035
|
+
|
|
21036
|
+
// src/dispatch/handlers/status-criteria-set.ts
|
|
21037
|
+
async function dispatchStatusCriteriaSet(action, deps) {
|
|
21038
|
+
const resolved = resolveStatusCriteria(action.criteria);
|
|
21039
|
+
const allIds = new Set(deps.adapter.getAllIds());
|
|
21040
|
+
const beforeSnap = await snapshotActivities(deps.adapter, allIds);
|
|
21041
|
+
const changedIds = applyStatusPass(deps.adapter, resolved);
|
|
21042
|
+
deps.sector.statusCriteria = resolved;
|
|
21043
|
+
return {
|
|
21044
|
+
ok: true,
|
|
21045
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
21046
|
+
source: action,
|
|
21047
|
+
beforeSnap,
|
|
21048
|
+
touchedIds: changedIds,
|
|
21049
|
+
scheduledIds: [],
|
|
21050
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
21051
|
+
})
|
|
21052
|
+
};
|
|
21053
|
+
}
|
|
21054
|
+
|
|
21055
|
+
// src/dispatch/handlers/calendar-reconcile.ts
|
|
21056
|
+
async function dispatchCalendarReconcile(action, options, deps) {
|
|
21057
|
+
const beforeSnap = /* @__PURE__ */ new Map();
|
|
21058
|
+
const touchedIds = /* @__PURE__ */ new Set();
|
|
21059
|
+
for (const activity of deps.adapter.getAllActivities()) {
|
|
21060
|
+
if (!shouldReconcile(activity, deps)) continue;
|
|
21061
|
+
const calendar = deps.ctx.calendars.getCalendar(activity.calendarId);
|
|
21062
|
+
if (!calendar) continue;
|
|
21063
|
+
const startReference = calendar.isWorkTime(
|
|
21064
|
+
activity.startDate,
|
|
21065
|
+
CALENDAR_UNIT.HOUR
|
|
21066
|
+
) ? startOfUtcDay4(activity.startDate) : activity.startDate;
|
|
21067
|
+
const startDate = calendar.getClosestWorkTime({
|
|
21068
|
+
date: startReference,
|
|
21069
|
+
direction: WORK_TIME_DIRECTION.FUTURE,
|
|
21070
|
+
unit: CALENDAR_UNIT.HOUR
|
|
21071
|
+
});
|
|
21072
|
+
const endDate = calendar.calculateEndDate(
|
|
21073
|
+
startDate,
|
|
21074
|
+
activity.durationHours
|
|
21075
|
+
);
|
|
21076
|
+
if (sameInstant(startDate, activity.startDate) && sameInstant(endDate, activity.endDate)) {
|
|
21077
|
+
continue;
|
|
21078
|
+
}
|
|
21079
|
+
const activityId = String(activity.id);
|
|
21080
|
+
beforeSnap.set(activityId, structuredCloneActivity(activity));
|
|
21081
|
+
touchedIds.add(activityId);
|
|
21082
|
+
deps.adapter.setActivityFields(activityId, { startDate, endDate });
|
|
21083
|
+
}
|
|
21084
|
+
const { scheduledIds } = await runPostMutation(
|
|
21085
|
+
{
|
|
21086
|
+
adapter: deps.adapter,
|
|
21087
|
+
scheduler: deps.scheduler,
|
|
21088
|
+
sector: deps.sector,
|
|
21089
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId,
|
|
21090
|
+
runAutomaticP6Schedule: deps.runAutomaticP6Schedule
|
|
21091
|
+
},
|
|
21092
|
+
{
|
|
21093
|
+
action,
|
|
21094
|
+
autoscheduleFrom: AUTOSCHEDULE_SCOPE.ROOTS,
|
|
21095
|
+
recomputeParentsFrom: touchedIds,
|
|
19173
21096
|
now: deps.now,
|
|
19174
21097
|
options
|
|
19175
21098
|
}
|
|
@@ -19867,6 +21790,9 @@ async function performDispatch(action, options, deps) {
|
|
|
19867
21790
|
if (action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE) {
|
|
19868
21791
|
return dispatchPersistenceAcknowledge(action, deps);
|
|
19869
21792
|
}
|
|
21793
|
+
if (action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_REBASE) {
|
|
21794
|
+
return dispatchPersistenceRebase(action, options, deps);
|
|
21795
|
+
}
|
|
19870
21796
|
if (action.kind === DISPATCH_ACTION_KIND.BASELINE_APPLY) {
|
|
19871
21797
|
return dispatchBaselineApply(action, deps);
|
|
19872
21798
|
}
|
|
@@ -19973,18 +21899,63 @@ function selectionDeps(deps) {
|
|
|
19973
21899
|
}
|
|
19974
21900
|
|
|
19975
21901
|
// src/columns/shared/field-types.ts
|
|
19976
|
-
var
|
|
21902
|
+
var DATE_FIELDS2 = /* @__PURE__ */ new Set([
|
|
19977
21903
|
"startDate",
|
|
19978
21904
|
"endDate",
|
|
19979
21905
|
"constraintDate"
|
|
19980
21906
|
]);
|
|
19981
21907
|
function coerceActivityFieldValue(field, value) {
|
|
19982
|
-
if (
|
|
21908
|
+
if (DATE_FIELDS2.has(field) && typeof value === "string") {
|
|
19983
21909
|
return new Date(value);
|
|
19984
21910
|
}
|
|
19985
21911
|
return value;
|
|
19986
21912
|
}
|
|
19987
21913
|
|
|
21914
|
+
// src/calendar/closest-past-work-time.ts
|
|
21915
|
+
var MS_PER_MINUTE = 60 * 1e3;
|
|
21916
|
+
var MS_PER_DAY = 24 * 60 * MS_PER_MINUTE;
|
|
21917
|
+
var MINUTES_PER_HOUR = 60;
|
|
21918
|
+
var MAX_LOOKBACK_DAYS = 3660;
|
|
21919
|
+
var LAST_MINUTE_OF_DAY_MS = MS_PER_DAY - MS_PER_MINUTE;
|
|
21920
|
+
var WINDOW_SEPARATOR = "-";
|
|
21921
|
+
var TIME_SEPARATOR = ":";
|
|
21922
|
+
function closestPastWorkTime(calendar, date2, unit) {
|
|
21923
|
+
if (calendar.isWorkTime({ date: date2, unit })) return date2;
|
|
21924
|
+
const dayStart = utcDayStart(date2);
|
|
21925
|
+
if (unit !== CALENDAR_UNIT.DAY) {
|
|
21926
|
+
const elapsed = date2.getTime() - dayStart;
|
|
21927
|
+
const sameDayEnd = latestWindowEnd(calendar, dayStart, elapsed);
|
|
21928
|
+
if (sameDayEnd !== null) return new Date(dayStart + sameDayEnd);
|
|
21929
|
+
}
|
|
21930
|
+
const searchesFromWorkingDay = calendar.getWorkHours(date2).length > 0;
|
|
21931
|
+
for (let back = 1; back <= MAX_LOOKBACK_DAYS; back++) {
|
|
21932
|
+
const candidate = dayStart - back * MS_PER_DAY;
|
|
21933
|
+
const lastEnd = latestWindowEnd(calendar, candidate, MS_PER_DAY);
|
|
21934
|
+
if (lastEnd === null) continue;
|
|
21935
|
+
if (unit !== CALENDAR_UNIT.DAY) {
|
|
21936
|
+
const end = searchesFromWorkingDay ? Math.min(lastEnd, LAST_MINUTE_OF_DAY_MS) : lastEnd;
|
|
21937
|
+
return new Date(candidate + end);
|
|
21938
|
+
}
|
|
21939
|
+
return new Date(candidate + MS_PER_DAY);
|
|
21940
|
+
}
|
|
21941
|
+
throw new Error(
|
|
21942
|
+
`No working day found within ${MAX_LOOKBACK_DAYS} days before ${date2.toISOString()}`
|
|
21943
|
+
);
|
|
21944
|
+
}
|
|
21945
|
+
function utcDayStart(date2) {
|
|
21946
|
+
return Date.UTC(date2.getUTCFullYear(), date2.getUTCMonth(), date2.getUTCDate());
|
|
21947
|
+
}
|
|
21948
|
+
function latestWindowEnd(calendar, dayStart, atOrBefore) {
|
|
21949
|
+
const ends = calendar.getWorkHours(new Date(dayStart)).map(windowEndMs).filter((end) => end <= atOrBefore);
|
|
21950
|
+
return ends.length ? Math.max(...ends) : null;
|
|
21951
|
+
}
|
|
21952
|
+
function windowEndMs(window2) {
|
|
21953
|
+
const end = window2.split(WINDOW_SEPARATOR)[1] ?? "";
|
|
21954
|
+
const [hours = "0", minutes = "0"] = end.split(TIME_SEPARATOR);
|
|
21955
|
+
const endMs = (Number(hours) * MINUTES_PER_HOUR + Number(minutes)) * MS_PER_MINUTE;
|
|
21956
|
+
return endMs;
|
|
21957
|
+
}
|
|
21958
|
+
|
|
19988
21959
|
// src/calendar/adapter.ts
|
|
19989
21960
|
var WORK_CALENDAR_UNIT = { MINUTE: "minute" };
|
|
19990
21961
|
function createWorkCalendarAdapter(instance, options = {}) {
|
|
@@ -20000,6 +21971,9 @@ function createWorkCalendarAdapter(instance, options = {}) {
|
|
|
20000
21971
|
function wrapWorkCalendar(calendar, defaultUnit) {
|
|
20001
21972
|
return {
|
|
20002
21973
|
getClosestWorkTime(query) {
|
|
21974
|
+
if (query.direction === WORK_TIME_DIRECTION.PAST) {
|
|
21975
|
+
return closestPastWorkTime(calendar, query.date, query.unit);
|
|
21976
|
+
}
|
|
20003
21977
|
return calendar.getClosestWorkTime({
|
|
20004
21978
|
date: query.date,
|
|
20005
21979
|
dir: query.direction,
|
|
@@ -22896,7 +24870,7 @@ var CalendarCalculator = class {
|
|
|
22896
24870
|
this.reader = setup.reader;
|
|
22897
24871
|
}
|
|
22898
24872
|
resolveCalendar(task) {
|
|
22899
|
-
const calendarId =
|
|
24873
|
+
const calendarId = resolveCalendarId2(task);
|
|
22900
24874
|
return this.reader.getCalendar(calendarId ?? DEFAULT_CALENDAR_ID);
|
|
22901
24875
|
}
|
|
22902
24876
|
getClosestWorkTime(params) {
|
|
@@ -22953,14 +24927,14 @@ function memoize(cache, key, compute) {
|
|
|
22953
24927
|
return result;
|
|
22954
24928
|
}
|
|
22955
24929
|
var DEFAULT_CALENDAR_KEY = "__default__";
|
|
22956
|
-
function
|
|
24930
|
+
function resolveCalendarId2(task) {
|
|
22957
24931
|
if (!task) return null;
|
|
22958
24932
|
const calendarId = task.calendarId;
|
|
22959
24933
|
if (calendarId == null) return null;
|
|
22960
24934
|
return String(calendarId);
|
|
22961
24935
|
}
|
|
22962
24936
|
function calendarKey(task) {
|
|
22963
|
-
return
|
|
24937
|
+
return resolveCalendarId2(task) ?? DEFAULT_CALENDAR_KEY;
|
|
22964
24938
|
}
|
|
22965
24939
|
|
|
22966
24940
|
// src/internal/state/hierarchy-index.ts
|
|
@@ -24245,43 +26219,6 @@ function computeProjectWorkHours(calendars) {
|
|
|
24245
26219
|
return { startHour: earliestTime(starts), endHour: latestTime(ends) };
|
|
24246
26220
|
}
|
|
24247
26221
|
|
|
24248
|
-
// src/boundary/backend/constants.ts
|
|
24249
|
-
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
24250
|
-
var EXCEPTION_DATE_FORMAT_REGEX = /^(\d{4})[/-](\d{1,2})[/-](\d{1,2})/;
|
|
24251
|
-
var WEEKDAYS_PER_WEEK = 7;
|
|
24252
|
-
var EXCEPTION_EVERY_TYPE = {
|
|
24253
|
-
DAILY: "DAILY",
|
|
24254
|
-
WEEKLY: "WEEKLY",
|
|
24255
|
-
MONTHLY: "MONTHLY",
|
|
24256
|
-
YEARLY: "YEARLY"
|
|
24257
|
-
};
|
|
24258
|
-
var MONTHS_PER_YEAR = 12;
|
|
24259
|
-
var CALENDAR_FALLBACK_WORKING_DAYS = [false, true, true, true, true, true, false];
|
|
24260
|
-
var WORKING_DAY_TRUE_VALUES = /* @__PURE__ */ new Set([
|
|
24261
|
-
"1",
|
|
24262
|
-
"true"
|
|
24263
|
-
]);
|
|
24264
|
-
var DEFAULT_ACTIVITY_CRETER = ACTIVITY_PONDERATOR_CRITERION.DURATION;
|
|
24265
|
-
var DEFAULT_CONSTRAINT_TYPE = CONSTRAINT_TYPE.ASAP;
|
|
24266
|
-
var CONSTRAINT_LABEL_MAP = {
|
|
24267
|
-
ASAP: CONSTRAINT_TYPE.ASAP,
|
|
24268
|
-
ALAP: CONSTRAINT_TYPE.ALAP,
|
|
24269
|
-
SNET: CONSTRAINT_TYPE.SNET,
|
|
24270
|
-
SNLT: CONSTRAINT_TYPE.SNLT,
|
|
24271
|
-
FNET: CONSTRAINT_TYPE.FNET,
|
|
24272
|
-
FNLT: CONSTRAINT_TYPE.FNLT,
|
|
24273
|
-
MSO: CONSTRAINT_TYPE.MSO,
|
|
24274
|
-
MFO: CONSTRAINT_TYPE.MFO,
|
|
24275
|
-
"AS SOON AS POSSIBLE": CONSTRAINT_TYPE.ASAP,
|
|
24276
|
-
"AS LATE AS POSSIBLE": CONSTRAINT_TYPE.ALAP,
|
|
24277
|
-
"START NO EARLIER THAN": CONSTRAINT_TYPE.SNET,
|
|
24278
|
-
"START NO LATER THAN": CONSTRAINT_TYPE.SNLT,
|
|
24279
|
-
"FINISH NO EARLIER THAN": CONSTRAINT_TYPE.FNET,
|
|
24280
|
-
"FINISH NO LATER THAN": CONSTRAINT_TYPE.FNLT,
|
|
24281
|
-
"MUST START ON": CONSTRAINT_TYPE.MSO,
|
|
24282
|
-
"MUST FINISH ON": CONSTRAINT_TYPE.MFO
|
|
24283
|
-
};
|
|
24284
|
-
|
|
24285
26222
|
// src/boundary/backend/normalize-calendar.ts
|
|
24286
26223
|
function normalizeCalendars(schedulingCalendars, sectorCalendars, rawBaseCalendars, exceptionRange = null) {
|
|
24287
26224
|
const hasSchedulingCalendars = schedulingCalendars !== void 0 && schedulingCalendars.length > 0;
|
|
@@ -24296,10 +26233,10 @@ function normalizeCalendars(schedulingCalendars, sectorCalendars, rawBaseCalenda
|
|
|
24296
26233
|
calendars,
|
|
24297
26234
|
baseCalendars,
|
|
24298
26235
|
knownCalendarIds: new Set(calendars.map((calendar) => calendar.id)),
|
|
24299
|
-
defaultCalendarId:
|
|
26236
|
+
defaultCalendarId: pickDefaultCalendarId2(calendars)
|
|
24300
26237
|
};
|
|
24301
26238
|
}
|
|
24302
|
-
function
|
|
26239
|
+
function pickDefaultCalendarId2(calendars) {
|
|
24303
26240
|
let firstCalendarId = null;
|
|
24304
26241
|
for (const calendar of calendars) {
|
|
24305
26242
|
firstCalendarId ??= calendar.id;
|
|
@@ -24364,14 +26301,24 @@ function parseExceptionDays(exceptions, range) {
|
|
|
24364
26301
|
}
|
|
24365
26302
|
function expandRangeToUtcKeys(from, to, range, everyType) {
|
|
24366
26303
|
const fromTs = parseExceptionDate(from);
|
|
24367
|
-
const
|
|
24368
|
-
if (fromTs === null ||
|
|
26304
|
+
const toDay = parseExceptionDate(to);
|
|
26305
|
+
if (fromTs === null || toDay === null) return [];
|
|
26306
|
+
const toTs = endsAtMidnightAfterStart(from, to, fromTs, toDay) ? toDay - DAY_MS : toDay;
|
|
24369
26307
|
if (range && (toTs < range.start || fromTs > range.end)) return [];
|
|
24370
26308
|
const first = range ? Math.max(fromTs, range.start) : fromTs;
|
|
24371
26309
|
const last = range ? Math.min(toTs, range.end) : toTs;
|
|
24372
26310
|
const timestamps = steppedTimestamps(fromTs, first, last, everyType);
|
|
24373
26311
|
return timestamps.map(String);
|
|
24374
26312
|
}
|
|
26313
|
+
function endsAtMidnightAfterStart(from, to, fromDay, toDay) {
|
|
26314
|
+
if (toDay <= fromDay) return false;
|
|
26315
|
+
return isMidnight(to) && !isMidnight(from);
|
|
26316
|
+
}
|
|
26317
|
+
function isMidnight(raw) {
|
|
26318
|
+
if (typeof raw !== "string") return false;
|
|
26319
|
+
const match = EXCEPTION_TIME_FORMAT_REGEX.exec(raw.trim());
|
|
26320
|
+
return match !== null && Number(match[1]) === 0 && Number(match[2]) === 0;
|
|
26321
|
+
}
|
|
24375
26322
|
function steppedTimestamps(anchor, first, last, everyType) {
|
|
24376
26323
|
switch (everyType) {
|
|
24377
26324
|
case EXCEPTION_EVERY_TYPE.DAILY:
|
|
@@ -24546,7 +26493,7 @@ function buildPipelineContext(adapter, options) {
|
|
|
24546
26493
|
return {
|
|
24547
26494
|
calendars: adapter.calendarReader,
|
|
24548
26495
|
hasCalendar: (calendarId) => options.calendars.has(calendarId),
|
|
24549
|
-
getDefaultCalendarId: () =>
|
|
26496
|
+
getDefaultCalendarId: () => pickDefaultCalendarId2(options.calendars.values()),
|
|
24550
26497
|
hierarchy: createStateBackedHierarchyReader(adapter),
|
|
24551
26498
|
activityReader: createStateBackedActivityReader(adapter),
|
|
24552
26499
|
dateFormat: options.dateFormat ?? "YYYY-MM-DD",
|
|
@@ -24663,7 +26610,8 @@ function mergeCoalesced(top, next) {
|
|
|
24663
26610
|
var DISPATCH_HISTORY_POLICY = {
|
|
24664
26611
|
RECORD: "record",
|
|
24665
26612
|
SKIP: "skip",
|
|
24666
|
-
CHECKPOINT_ON_SUCCESS: "checkpoint-on-success"
|
|
26613
|
+
CHECKPOINT_ON_SUCCESS: "checkpoint-on-success",
|
|
26614
|
+
REBASE_ON_SUCCESS: "rebase-on-success"
|
|
24667
26615
|
};
|
|
24668
26616
|
var CHANGE_SET_SIDE = {
|
|
24669
26617
|
BEFORE: "before",
|
|
@@ -24673,6 +26621,9 @@ function getDispatchHistoryPolicy(action) {
|
|
|
24673
26621
|
if (action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE) {
|
|
24674
26622
|
return DISPATCH_HISTORY_POLICY.CHECKPOINT_ON_SUCCESS;
|
|
24675
26623
|
}
|
|
26624
|
+
if (action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_REBASE) {
|
|
26625
|
+
return DISPATCH_HISTORY_POLICY.REBASE_ON_SUCCESS;
|
|
26626
|
+
}
|
|
24676
26627
|
if (action.kind === DISPATCH_ACTION_KIND.SIR_SYNC || action.kind === DISPATCH_ACTION_KIND.CALENDAR_RECONCILE || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC || action.kind === DISPATCH_ACTION_KIND.BASELINE_APPLY || action.kind === DISPATCH_ACTION_KIND.PONDERATOR_CRITERION_SET || action.kind === DISPATCH_ACTION_KIND.STATUS_CRITERIA_SET || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_IMPORT || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_COPY || action.kind === DISPATCH_ACTION_KIND.SELECTION_TOGGLE || action.kind === DISPATCH_ACTION_KIND.SELECTION_REPLACE || action.kind === DISPATCH_ACTION_KIND.VISIBILITY_SET || action.kind === DISPATCH_ACTION_KIND.FILTER_SET || action.kind === DISPATCH_ACTION_KIND.SORT_SET || action.kind === DISPATCH_ACTION_KIND.VIEW_ATTRIBUTES_SET || action.kind === DISPATCH_ACTION_KIND.GROUP_SET || action.kind === DISPATCH_ACTION_KIND.WBS_DEPTH_SET) {
|
|
24677
26628
|
return DISPATCH_HISTORY_POLICY.SKIP;
|
|
24678
26629
|
}
|
|
@@ -25088,7 +27039,7 @@ function collectResequencedParents(changes, parentOf) {
|
|
|
25088
27039
|
if (change.kind === ENTITY_CHANGE_KIND.DELETED) {
|
|
25089
27040
|
const deletedFields = change.fields;
|
|
25090
27041
|
if (deletedFields && "parentId" in deletedFields) {
|
|
25091
|
-
touched.add(
|
|
27042
|
+
touched.add(parentKeyOf4(deletedFields.parentId?.before));
|
|
25092
27043
|
}
|
|
25093
27044
|
continue;
|
|
25094
27045
|
}
|
|
@@ -25100,12 +27051,12 @@ function collectResequencedParents(changes, parentOf) {
|
|
|
25100
27051
|
if (!movedPositionally) continue;
|
|
25101
27052
|
addParentOf(activityId);
|
|
25102
27053
|
if ("parentId" in fields) {
|
|
25103
|
-
touched.add(
|
|
27054
|
+
touched.add(parentKeyOf4(fields.parentId?.before));
|
|
25104
27055
|
}
|
|
25105
27056
|
}
|
|
25106
27057
|
return touched;
|
|
25107
27058
|
}
|
|
25108
|
-
function
|
|
27059
|
+
function parentKeyOf4(parentId) {
|
|
25109
27060
|
if (parentId === null || parentId === void 0) return ROOT_PARENT_ID;
|
|
25110
27061
|
return String(parentId);
|
|
25111
27062
|
}
|
|
@@ -25149,154 +27100,6 @@ function buildStateSnapshot(activities, links, calendars, baseCalendars = []) {
|
|
|
25149
27100
|
};
|
|
25150
27101
|
}
|
|
25151
27102
|
|
|
25152
|
-
// src/boundary/save/field-policy.ts
|
|
25153
|
-
var SAVE_FIELD_POLICY = {
|
|
25154
|
-
TRACKED: "tracked",
|
|
25155
|
-
IGNORED: "ignored"
|
|
25156
|
-
};
|
|
25157
|
-
|
|
25158
|
-
// src/boundary/save/activity-changes.ts
|
|
25159
|
-
var ACTIVITY_SAVE_FIELD_POLICY = {
|
|
25160
|
-
id: SAVE_FIELD_POLICY.IGNORED,
|
|
25161
|
-
proplannerId: SAVE_FIELD_POLICY.IGNORED,
|
|
25162
|
-
parentId: SAVE_FIELD_POLICY.TRACKED,
|
|
25163
|
-
name: SAVE_FIELD_POLICY.TRACKED,
|
|
25164
|
-
description: SAVE_FIELD_POLICY.TRACKED,
|
|
25165
|
-
type: SAVE_FIELD_POLICY.TRACKED,
|
|
25166
|
-
startDate: SAVE_FIELD_POLICY.TRACKED,
|
|
25167
|
-
endDate: SAVE_FIELD_POLICY.TRACKED,
|
|
25168
|
-
durationHours: SAVE_FIELD_POLICY.TRACKED,
|
|
25169
|
-
constraintType: SAVE_FIELD_POLICY.TRACKED,
|
|
25170
|
-
constraintDate: SAVE_FIELD_POLICY.TRACKED,
|
|
25171
|
-
autoScheduling: SAVE_FIELD_POLICY.TRACKED,
|
|
25172
|
-
calendarId: SAVE_FIELD_POLICY.TRACKED,
|
|
25173
|
-
assignedDefaultCalendar: SAVE_FIELD_POLICY.IGNORED,
|
|
25174
|
-
dateOrigin: SAVE_FIELD_POLICY.TRACKED,
|
|
25175
|
-
progress: SAVE_FIELD_POLICY.TRACKED,
|
|
25176
|
-
cost: SAVE_FIELD_POLICY.TRACKED,
|
|
25177
|
-
usedCost: SAVE_FIELD_POLICY.TRACKED,
|
|
25178
|
-
realCost: SAVE_FIELD_POLICY.TRACKED,
|
|
25179
|
-
workHours: SAVE_FIELD_POLICY.TRACKED,
|
|
25180
|
-
realWorkHours: SAVE_FIELD_POLICY.TRACKED,
|
|
25181
|
-
ponderator: SAVE_FIELD_POLICY.TRACKED,
|
|
25182
|
-
hasCustomPonderator: SAVE_FIELD_POLICY.TRACKED,
|
|
25183
|
-
correlativeId: SAVE_FIELD_POLICY.TRACKED,
|
|
25184
|
-
uniqueCorrelativeId: SAVE_FIELD_POLICY.TRACKED,
|
|
25185
|
-
customId: SAVE_FIELD_POLICY.TRACKED,
|
|
25186
|
-
subcontractId: SAVE_FIELD_POLICY.TRACKED,
|
|
25187
|
-
isLookahead: SAVE_FIELD_POLICY.TRACKED,
|
|
25188
|
-
hasLookaheadTasks: SAVE_FIELD_POLICY.IGNORED,
|
|
25189
|
-
newActivityIds: SAVE_FIELD_POLICY.TRACKED,
|
|
25190
|
-
hasNewActivities: SAVE_FIELD_POLICY.TRACKED,
|
|
25191
|
-
isNewActivity: SAVE_FIELD_POLICY.IGNORED,
|
|
25192
|
-
pendingRequestIds: SAVE_FIELD_POLICY.IGNORED,
|
|
25193
|
-
responsableIds: SAVE_FIELD_POLICY.TRACKED,
|
|
25194
|
-
tagIds: SAVE_FIELD_POLICY.TRACKED,
|
|
25195
|
-
baselinePoints: SAVE_FIELD_POLICY.IGNORED,
|
|
25196
|
-
baselineSnapshot: SAVE_FIELD_POLICY.IGNORED,
|
|
25197
|
-
recursiveDurationHours: SAVE_FIELD_POLICY.IGNORED,
|
|
25198
|
-
isCritical: SAVE_FIELD_POLICY.IGNORED,
|
|
25199
|
-
freeSlackHours: SAVE_FIELD_POLICY.IGNORED,
|
|
25200
|
-
calendarDuration: SAVE_FIELD_POLICY.IGNORED,
|
|
25201
|
-
baselineVarianceDays: SAVE_FIELD_POLICY.IGNORED,
|
|
25202
|
-
projectBaselineVarianceDays: SAVE_FIELD_POLICY.IGNORED,
|
|
25203
|
-
primaryBaselineVarianceDays: SAVE_FIELD_POLICY.IGNORED,
|
|
25204
|
-
expectedProgress: SAVE_FIELD_POLICY.IGNORED,
|
|
25205
|
-
expectedProgressBaseline: SAVE_FIELD_POLICY.IGNORED,
|
|
25206
|
-
status: SAVE_FIELD_POLICY.IGNORED,
|
|
25207
|
-
criticalPath: SAVE_FIELD_POLICY.IGNORED,
|
|
25208
|
-
p6: SAVE_FIELD_POLICY.IGNORED
|
|
25209
|
-
};
|
|
25210
|
-
var PERSISTABLE_ACTIVITY_FIELDS = Object.keys(ACTIVITY_SAVE_FIELD_POLICY).filter((field) => {
|
|
25211
|
-
return ACTIVITY_SAVE_FIELD_POLICY[field] === SAVE_FIELD_POLICY.TRACKED;
|
|
25212
|
-
});
|
|
25213
|
-
var DATE_FIELDS2 = /* @__PURE__ */ new Set([
|
|
25214
|
-
"startDate",
|
|
25215
|
-
"endDate",
|
|
25216
|
-
"constraintDate",
|
|
25217
|
-
"dateOrigin"
|
|
25218
|
-
]);
|
|
25219
|
-
var ID_ARRAY_FIELDS = /* @__PURE__ */ new Set([
|
|
25220
|
-
"newActivityIds",
|
|
25221
|
-
"responsableIds",
|
|
25222
|
-
"tagIds"
|
|
25223
|
-
]);
|
|
25224
|
-
function projectPersistableActivity(activity) {
|
|
25225
|
-
const entries = PERSISTABLE_ACTIVITY_FIELDS.map(
|
|
25226
|
-
(field) => [field, snapshotFieldValue(field, activity[field])]
|
|
25227
|
-
);
|
|
25228
|
-
return Object.fromEntries(entries);
|
|
25229
|
-
}
|
|
25230
|
-
function snapshotFieldValue(field, value) {
|
|
25231
|
-
if (DATE_FIELDS2.has(field)) {
|
|
25232
|
-
return value instanceof Date ? new Date(value) : null;
|
|
25233
|
-
}
|
|
25234
|
-
if (ID_ARRAY_FIELDS.has(field)) {
|
|
25235
|
-
return Array.isArray(value) ? [...value] : [];
|
|
25236
|
-
}
|
|
25237
|
-
return value;
|
|
25238
|
-
}
|
|
25239
|
-
function activityChangedSince(current, saved) {
|
|
25240
|
-
return changedPersistableActivityFields(current, saved).length > 0;
|
|
25241
|
-
}
|
|
25242
|
-
function changedPersistableActivityFields(current, saved) {
|
|
25243
|
-
return PERSISTABLE_ACTIVITY_FIELDS.filter(
|
|
25244
|
-
(field) => !fieldEquals(field, current[field], saved[field])
|
|
25245
|
-
);
|
|
25246
|
-
}
|
|
25247
|
-
function fieldEquals(field, currentValue, savedValue) {
|
|
25248
|
-
if (DATE_FIELDS2.has(field)) return dateEquals(currentValue, savedValue);
|
|
25249
|
-
if (ID_ARRAY_FIELDS.has(field))
|
|
25250
|
-
return idArrayEquals(currentValue, savedValue);
|
|
25251
|
-
return (currentValue ?? null) === (savedValue ?? null);
|
|
25252
|
-
}
|
|
25253
|
-
function dateEquals(current, saved) {
|
|
25254
|
-
const currentTime = current instanceof Date ? current.getTime() : null;
|
|
25255
|
-
const savedTime = saved instanceof Date ? saved.getTime() : null;
|
|
25256
|
-
return currentTime === savedTime;
|
|
25257
|
-
}
|
|
25258
|
-
function idArrayEquals(current, saved) {
|
|
25259
|
-
const currentIds = new Set(Array.isArray(current) ? current.map(String) : []);
|
|
25260
|
-
const savedIds = new Set(Array.isArray(saved) ? saved.map(String) : []);
|
|
25261
|
-
if (currentIds.size !== savedIds.size) return false;
|
|
25262
|
-
for (const currentId of currentIds) {
|
|
25263
|
-
if (!savedIds.has(currentId)) return false;
|
|
25264
|
-
}
|
|
25265
|
-
return true;
|
|
25266
|
-
}
|
|
25267
|
-
|
|
25268
|
-
// src/boundary/save/link-changes.ts
|
|
25269
|
-
var LINK_SAVE_FIELD_POLICY = {
|
|
25270
|
-
id: SAVE_FIELD_POLICY.IGNORED,
|
|
25271
|
-
source: SAVE_FIELD_POLICY.TRACKED,
|
|
25272
|
-
target: SAVE_FIELD_POLICY.TRACKED,
|
|
25273
|
-
type: SAVE_FIELD_POLICY.TRACKED,
|
|
25274
|
-
lag: SAVE_FIELD_POLICY.TRACKED,
|
|
25275
|
-
freeSlackHours: SAVE_FIELD_POLICY.IGNORED,
|
|
25276
|
-
isCritical: SAVE_FIELD_POLICY.IGNORED,
|
|
25277
|
-
ganttId: SAVE_FIELD_POLICY.IGNORED,
|
|
25278
|
-
sectorId: SAVE_FIELD_POLICY.IGNORED,
|
|
25279
|
-
proplannerId: SAVE_FIELD_POLICY.IGNORED
|
|
25280
|
-
};
|
|
25281
|
-
var TRACKED_LINK_FIELDS = Object.keys(LINK_SAVE_FIELD_POLICY).filter((field) => {
|
|
25282
|
-
return LINK_SAVE_FIELD_POLICY[field] === SAVE_FIELD_POLICY.TRACKED;
|
|
25283
|
-
});
|
|
25284
|
-
function linkChangedSince(baseline, current) {
|
|
25285
|
-
return TRACKED_LINK_FIELDS.some(
|
|
25286
|
-
(field) => baseline[field] !== current[field]
|
|
25287
|
-
);
|
|
25288
|
-
}
|
|
25289
|
-
function checkNoUpdatedLinks(baseline, current) {
|
|
25290
|
-
const baselineById = new Map(
|
|
25291
|
-
baseline.map((link) => [String(link.id), link])
|
|
25292
|
-
);
|
|
25293
|
-
return current.filter((link) => {
|
|
25294
|
-
const base = baselineById.get(String(link.id));
|
|
25295
|
-
if (!base) return false;
|
|
25296
|
-
return linkChangedSince(base, link);
|
|
25297
|
-
});
|
|
25298
|
-
}
|
|
25299
|
-
|
|
25300
27103
|
// src/previsualization/canonical-digest.ts
|
|
25301
27104
|
function normalized(value) {
|
|
25302
27105
|
if (value === void 0) return ["undefined"];
|
|
@@ -25747,6 +27550,30 @@ var SaveTracker = class _SaveTracker {
|
|
|
25747
27550
|
hasCheckpointLinkId(linkId) {
|
|
25748
27551
|
return this.linksAtLastSave.has(linkId);
|
|
25749
27552
|
}
|
|
27553
|
+
rebaseBaseline(activities, links) {
|
|
27554
|
+
return {
|
|
27555
|
+
activities: activities.map((activity) => {
|
|
27556
|
+
const saved = this.p6ActivitiesAtLastSave.get(String(activity.id));
|
|
27557
|
+
return saved === void 0 ? activity : { ...activity, p6: cloneDomainValue(saved) };
|
|
27558
|
+
}),
|
|
27559
|
+
links,
|
|
27560
|
+
p6Settings: cloneDomainValue(this.p6SettingsAtLastSave),
|
|
27561
|
+
p6ProjectState: cloneDomainValue(this.p6ProjectStateAtLastSave),
|
|
27562
|
+
p6LatestRunLog: cloneDomainValue(this.p6LatestRunLogAtLastSave)
|
|
27563
|
+
};
|
|
27564
|
+
}
|
|
27565
|
+
checkpointActivity(activityId) {
|
|
27566
|
+
return this.activitiesAtLastSave.get(activityId)?.persistable ?? null;
|
|
27567
|
+
}
|
|
27568
|
+
checkpointLink(linkId) {
|
|
27569
|
+
return this.linksAtLastSave.get(linkId) ?? null;
|
|
27570
|
+
}
|
|
27571
|
+
checkpointActivityIds() {
|
|
27572
|
+
return [...this.activitiesAtLastSave.keys()];
|
|
27573
|
+
}
|
|
27574
|
+
checkpointLinkIds() {
|
|
27575
|
+
return [...this.linksAtLastSave.keys()];
|
|
27576
|
+
}
|
|
25750
27577
|
hasUnsavedChanges() {
|
|
25751
27578
|
return this.trackingEnabled && this.initialized && (this.dirtyActivityIds.size > 0 || this.dirtyLinkIds.size > 0 || this.p6TrackingEnabled && (this.dirtyP6ActivityIds.size > 0 || this.p6SettingsIsDirty() || this.p6ProjectStateDirty || this.p6LatestRunLogDirty));
|
|
25752
27579
|
}
|
|
@@ -25866,332 +27693,6 @@ var SaveTracker = class _SaveTracker {
|
|
|
25866
27693
|
}
|
|
25867
27694
|
};
|
|
25868
27695
|
|
|
25869
|
-
// src/boundary/backend/normalize-activity.ts
|
|
25870
|
-
function normalizeActivities(rawActivities, context) {
|
|
25871
|
-
const hiddenIds = [];
|
|
25872
|
-
const activities = rawActivities.map((raw) => {
|
|
25873
|
-
const activity = parseActivity(raw, context);
|
|
25874
|
-
if (raw.should_be_showed === false) hiddenIds.push(activity.id);
|
|
25875
|
-
return activity;
|
|
25876
|
-
});
|
|
25877
|
-
forceRootProjectToRoot(activities);
|
|
25878
|
-
const activityIds = new Set(activities.map((activity) => activity.id));
|
|
25879
|
-
reparentOrphansToRoot(
|
|
25880
|
-
activities,
|
|
25881
|
-
activityIds,
|
|
25882
|
-
context.reporter,
|
|
25883
|
-
context.recordDiagnostic
|
|
25884
|
-
);
|
|
25885
|
-
return { activities, activityIds, hiddenIds };
|
|
25886
|
-
}
|
|
25887
|
-
function parseConstraintLabel(label, reporter, recordDiagnostic, activityId) {
|
|
25888
|
-
if (!label) return null;
|
|
25889
|
-
const normalized2 = CONSTRAINT_LABEL_MAP[label.toUpperCase()];
|
|
25890
|
-
if (normalized2 === void 0) {
|
|
25891
|
-
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.UNKNOWN_CONSTRAINT, activityId);
|
|
25892
|
-
reporter.warn(
|
|
25893
|
-
`[parseFromBackend] unknown constraint label "${label}" \u2014 defaulting to "asap"`
|
|
25894
|
-
);
|
|
25895
|
-
return DEFAULT_CONSTRAINT_TYPE;
|
|
25896
|
-
}
|
|
25897
|
-
return normalized2;
|
|
25898
|
-
}
|
|
25899
|
-
function resolveConstraint(raw, reporter, recordDiagnostic) {
|
|
25900
|
-
const constraintType = parseConstraintLabel(
|
|
25901
|
-
raw.constraint,
|
|
25902
|
-
reporter,
|
|
25903
|
-
recordDiagnostic,
|
|
25904
|
-
String(raw.unique_id)
|
|
25905
|
-
);
|
|
25906
|
-
const constraintTakesDate = constraintType !== CONSTRAINT_TYPE.ASAP && constraintType !== CONSTRAINT_TYPE.ALAP;
|
|
25907
|
-
const constraintDate = constraintTakesDate && raw.constraint_date ? parseBackendDate(raw.constraint_date) : null;
|
|
25908
|
-
return { constraintType, constraintDate };
|
|
25909
|
-
}
|
|
25910
|
-
function resolveCalendarId2(rawCalendarId, knownCalendarIds, defaultCalendarId) {
|
|
25911
|
-
if (rawCalendarId == null) return defaultCalendarId;
|
|
25912
|
-
const coerced = String(rawCalendarId);
|
|
25913
|
-
return knownCalendarIds.has(coerced) ? coerced : defaultCalendarId;
|
|
25914
|
-
}
|
|
25915
|
-
function parseNewActivityIds(raw) {
|
|
25916
|
-
if (raw == null || raw === "") return [];
|
|
25917
|
-
if (Array.isArray(raw)) return raw.map(String);
|
|
25918
|
-
if (typeof raw !== "string") return [];
|
|
25919
|
-
try {
|
|
25920
|
-
const parsed2 = JSON.parse(raw);
|
|
25921
|
-
return Array.isArray(parsed2) ? parsed2.map(String) : [];
|
|
25922
|
-
} catch {
|
|
25923
|
-
return [];
|
|
25924
|
-
}
|
|
25925
|
-
}
|
|
25926
|
-
function hasUsableId(entry) {
|
|
25927
|
-
if (typeof entry !== "object" || entry === null) return false;
|
|
25928
|
-
if (!("id" in entry)) return false;
|
|
25929
|
-
const value = entry.id;
|
|
25930
|
-
return typeof value === "string" || typeof value === "number";
|
|
25931
|
-
}
|
|
25932
|
-
function parseIdList(raw) {
|
|
25933
|
-
if (!Array.isArray(raw)) return [];
|
|
25934
|
-
const ids = [];
|
|
25935
|
-
for (const entry of raw) {
|
|
25936
|
-
if (hasUsableId(entry)) ids.push(String(entry.id));
|
|
25937
|
-
}
|
|
25938
|
-
return ids;
|
|
25939
|
-
}
|
|
25940
|
-
function parseParentId(rawParentId) {
|
|
25941
|
-
return rawParentId === ROOT_PARENT_ID ? null : String(rawParentId);
|
|
25942
|
-
}
|
|
25943
|
-
function normalizePersistedActivityId(rawId) {
|
|
25944
|
-
if (rawId == null) return null;
|
|
25945
|
-
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
25946
|
-
if (!isValidIdentity) {
|
|
25947
|
-
throw new Error(`invalid activity proplannerId ${String(rawId)}`);
|
|
25948
|
-
}
|
|
25949
|
-
return rawId;
|
|
25950
|
-
}
|
|
25951
|
-
function parseProgress(raw, reporter, recordDiagnostic, activityId) {
|
|
25952
|
-
const progress = Number(raw);
|
|
25953
|
-
if (!Number.isFinite(progress)) {
|
|
25954
|
-
throw new TypeError(`invalid activity progress ${String(raw)}`);
|
|
25955
|
-
}
|
|
25956
|
-
const isBelowNotStarted = progress < NOT_STARTED_PROGRESS;
|
|
25957
|
-
const isAboveCompleted = progress > MAX_TOLERATED_PROGRESS;
|
|
25958
|
-
const isWithinRange = !isBelowNotStarted && !isAboveCompleted;
|
|
25959
|
-
if (isWithinRange) return progress;
|
|
25960
|
-
const clamped = isBelowNotStarted ? NOT_STARTED_PROGRESS : COMPLETED_PROGRESS;
|
|
25961
|
-
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.PROGRESS_OUT_OF_RANGE, activityId);
|
|
25962
|
-
reporter.warn(
|
|
25963
|
-
`[parseFromBackend] activity "${activityId}" progress ${String(raw)} is out of range \u2014 clamping to ${clamped}`
|
|
25964
|
-
);
|
|
25965
|
-
return clamped;
|
|
25966
|
-
}
|
|
25967
|
-
function parseCriticality(raw, reporter) {
|
|
25968
|
-
if (typeof raw.is_critical === "boolean") return raw.is_critical;
|
|
25969
|
-
reporter.error(
|
|
25970
|
-
"[parseFromBackend] invalid activity is_critical \u2014 defaulting to false",
|
|
25971
|
-
new TypeError(
|
|
25972
|
-
`activity "${raw.unique_id}" received ${typeof raw.is_critical}`
|
|
25973
|
-
)
|
|
25974
|
-
);
|
|
25975
|
-
return false;
|
|
25976
|
-
}
|
|
25977
|
-
function parseBaselinePoints(raw, reporter, recordDiagnostic, activityId, isMilestone) {
|
|
25978
|
-
if (!Array.isArray(raw)) return [];
|
|
25979
|
-
return raw.map(
|
|
25980
|
-
(point) => normalizeBaselinePoint(
|
|
25981
|
-
point,
|
|
25982
|
-
reporter,
|
|
25983
|
-
{
|
|
25984
|
-
record: recordDiagnostic,
|
|
25985
|
-
sampleId: activityId
|
|
25986
|
-
},
|
|
25987
|
-
isMilestone
|
|
25988
|
-
)
|
|
25989
|
-
).filter((point) => point !== null);
|
|
25990
|
-
}
|
|
25991
|
-
function parseActivity(raw, context) {
|
|
25992
|
-
const calendarId = resolveCalendarId2(
|
|
25993
|
-
raw.calendarId,
|
|
25994
|
-
context.knownCalendarIds,
|
|
25995
|
-
context.defaultCalendarId
|
|
25996
|
-
);
|
|
25997
|
-
const inheritedDefaultCalendar = raw.calendarId == null || !context.knownCalendarIds.has(String(raw.calendarId));
|
|
25998
|
-
const progress = parseProgress(
|
|
25999
|
-
raw.progress,
|
|
26000
|
-
context.reporter,
|
|
26001
|
-
context.recordDiagnostic,
|
|
26002
|
-
String(raw.unique_id)
|
|
26003
|
-
);
|
|
26004
|
-
const { constraintType, constraintDate } = resolveConstraint(
|
|
26005
|
-
raw,
|
|
26006
|
-
context.reporter,
|
|
26007
|
-
context.recordDiagnostic
|
|
26008
|
-
);
|
|
26009
|
-
const isMilestone = raw.type === ACTIVITY_TYPE.MILESTONE;
|
|
26010
|
-
const startDate = parseBackendDate(raw.start_date);
|
|
26011
|
-
const endDate = isMilestone ? startDate : parseBackendDate(raw.end_date);
|
|
26012
|
-
const durationDays = isMilestone ? 0 : Number(raw.duration);
|
|
26013
|
-
const isCritical = parseCriticality(raw, context.reporter);
|
|
26014
|
-
return {
|
|
26015
|
-
id: String(raw.unique_id),
|
|
26016
|
-
proplannerId: normalizePersistedActivityId(raw.id),
|
|
26017
|
-
parentId: parseParentId(raw.parent_id),
|
|
26018
|
-
name: raw.name,
|
|
26019
|
-
description: raw.description ?? "",
|
|
26020
|
-
type: raw.type,
|
|
26021
|
-
startDate,
|
|
26022
|
-
endDate,
|
|
26023
|
-
durationHours: durationToHours(durationDays, context.hoursPerDay),
|
|
26024
|
-
constraintType,
|
|
26025
|
-
constraintDate,
|
|
26026
|
-
autoScheduling: isAutoSchedulingEnabledForProgress(progress),
|
|
26027
|
-
calendarId,
|
|
26028
|
-
assignedDefaultCalendar: calendarId != null && inheritedDefaultCalendar,
|
|
26029
|
-
dateOrigin: raw.date_origin == null ? null : parseBackendDate(raw.date_origin),
|
|
26030
|
-
progress,
|
|
26031
|
-
cost: Number(raw.cost),
|
|
26032
|
-
usedCost: Number(raw.used_cost ?? 0),
|
|
26033
|
-
realCost: Number(raw.real_cost ?? 0),
|
|
26034
|
-
workHours: Number(raw.hhWorkTime),
|
|
26035
|
-
realWorkHours: Number(raw.real_work ?? 0),
|
|
26036
|
-
ponderator: Number(raw.ponderator ?? 0),
|
|
26037
|
-
hasCustomPonderator: raw.hasCustomPonderator,
|
|
26038
|
-
correlativeId: raw.correlative_id,
|
|
26039
|
-
uniqueCorrelativeId: raw.unique_correlative_id,
|
|
26040
|
-
customId: normalizeCustomId(raw),
|
|
26041
|
-
subcontractId: raw.subcontractId,
|
|
26042
|
-
isLookahead: raw.isOnLookahead,
|
|
26043
|
-
hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
|
|
26044
|
-
newActivityIds: parseNewActivityIds(raw.newActivitiesArray),
|
|
26045
|
-
hasNewActivities: raw.hasNewActivities ?? false,
|
|
26046
|
-
...raw.isNewActivity === true ? { isNewActivity: true } : {},
|
|
26047
|
-
pendingRequestIds: parseIdList(raw.pendingRequests),
|
|
26048
|
-
responsableIds: parseIdList(raw.responsables),
|
|
26049
|
-
tagIds: parseIdList(raw.tags),
|
|
26050
|
-
baselinePoints: parseBaselinePoints(
|
|
26051
|
-
raw.baseline_points,
|
|
26052
|
-
context.reporter,
|
|
26053
|
-
context.recordDiagnostic,
|
|
26054
|
-
String(raw.unique_id),
|
|
26055
|
-
isMilestone
|
|
26056
|
-
),
|
|
26057
|
-
baselineSnapshot: null,
|
|
26058
|
-
baselineVarianceDays: null,
|
|
26059
|
-
projectBaselineVarianceDays: null,
|
|
26060
|
-
primaryBaselineVarianceDays: null,
|
|
26061
|
-
recursiveDurationHours: durationToHours(
|
|
26062
|
-
Number(raw.sumOfDurationRecursively ?? 0),
|
|
26063
|
-
context.hoursPerDay
|
|
26064
|
-
),
|
|
26065
|
-
calendarDuration: computeCalendarDuration({
|
|
26066
|
-
type: raw.type,
|
|
26067
|
-
startDate,
|
|
26068
|
-
endDate
|
|
26069
|
-
}),
|
|
26070
|
-
isCritical,
|
|
26071
|
-
freeSlackHours: null,
|
|
26072
|
-
expectedProgress: null,
|
|
26073
|
-
expectedProgressBaseline: null,
|
|
26074
|
-
status: null,
|
|
26075
|
-
criticalPath: null
|
|
26076
|
-
};
|
|
26077
|
-
}
|
|
26078
|
-
function normalizeCustomId(raw) {
|
|
26079
|
-
return raw.has_childs ? null : raw.custom_id;
|
|
26080
|
-
}
|
|
26081
|
-
function forceRootProjectToRoot(activities) {
|
|
26082
|
-
const rootProject = activities.find(
|
|
26083
|
-
(activity) => activity.correlativeId === 0 && activity.type === ACTIVITY_TYPE.PROJECT
|
|
26084
|
-
);
|
|
26085
|
-
if (rootProject) rootProject.parentId = null;
|
|
26086
|
-
}
|
|
26087
|
-
function reparentOrphansToRoot(activities, ids, reporter, recordDiagnostic) {
|
|
26088
|
-
for (const activity of activities) {
|
|
26089
|
-
if (activity.parentId === null) continue;
|
|
26090
|
-
if (!ids.has(activity.parentId)) {
|
|
26091
|
-
reporter.warn(
|
|
26092
|
-
`[parseFromBackend] activity "${activity.id}" references unknown parent "${activity.parentId}" \u2014 re-parenting to root (null)`
|
|
26093
|
-
);
|
|
26094
|
-
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.ORPHAN_ACTIVITY, activity.id);
|
|
26095
|
-
activity.parentId = null;
|
|
26096
|
-
}
|
|
26097
|
-
}
|
|
26098
|
-
}
|
|
26099
|
-
|
|
26100
|
-
// src/boundary/backend/normalize-link.ts
|
|
26101
|
-
function resolveLinkType(rawType) {
|
|
26102
|
-
if (typeof rawType !== "string") return null;
|
|
26103
|
-
const fromLabel = parseLinkTypeCode(rawType);
|
|
26104
|
-
if (fromLabel) return fromLabel;
|
|
26105
|
-
if (isLinkType(rawType)) return rawType;
|
|
26106
|
-
return null;
|
|
26107
|
-
}
|
|
26108
|
-
function normalizePersistedLinkId(rawId) {
|
|
26109
|
-
if (rawId == null) return void 0;
|
|
26110
|
-
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
26111
|
-
if (!isValidIdentity) {
|
|
26112
|
-
throw new Error(`invalid link proplannerId ${String(rawId)}`);
|
|
26113
|
-
}
|
|
26114
|
-
return rawId;
|
|
26115
|
-
}
|
|
26116
|
-
function normalizeLinks(rawLinks, context) {
|
|
26117
|
-
const parsedLinks = [];
|
|
26118
|
-
for (const rawLink of rawLinks) {
|
|
26119
|
-
try {
|
|
26120
|
-
parsedLinks.push(
|
|
26121
|
-
parseLink(rawLink, context.knownActivityIds, context.hoursPerDay)
|
|
26122
|
-
);
|
|
26123
|
-
} catch (parseError2) {
|
|
26124
|
-
context.recordDiagnostic(
|
|
26125
|
-
LOAD_DIAGNOSTIC_CODE.INVALID_LINK,
|
|
26126
|
-
String(rawLink.unique_id ?? rawLink.id)
|
|
26127
|
-
);
|
|
26128
|
-
context.reporter.warn(
|
|
26129
|
-
"[parseFromBackend] dropping invalid link",
|
|
26130
|
-
parseError2
|
|
26131
|
-
);
|
|
26132
|
-
}
|
|
26133
|
-
}
|
|
26134
|
-
return dedupeLinksBySignature(parsedLinks, context.recordDiagnostic);
|
|
26135
|
-
}
|
|
26136
|
-
function parseLink(link, activityIds, hoursPerDay) {
|
|
26137
|
-
const id = String(link.unique_id ?? link.id);
|
|
26138
|
-
const source = String(link.source_id ?? link.source ?? "");
|
|
26139
|
-
const target = String(link.target_id ?? link.target ?? "");
|
|
26140
|
-
if (!activityIds.has(source)) {
|
|
26141
|
-
throw new Error(
|
|
26142
|
-
`[parseFromBackend] link "${id}" source "${source}" does not exist`
|
|
26143
|
-
);
|
|
26144
|
-
}
|
|
26145
|
-
if (!activityIds.has(target)) {
|
|
26146
|
-
throw new Error(
|
|
26147
|
-
`[parseFromBackend] link "${id}" target "${target}" does not exist`
|
|
26148
|
-
);
|
|
26149
|
-
}
|
|
26150
|
-
const type = resolveLinkType(link.type);
|
|
26151
|
-
if (type === null) {
|
|
26152
|
-
throw new Error(
|
|
26153
|
-
`[parseFromBackend] link "${id}" has invalid type "${String(link.type)}"`
|
|
26154
|
-
);
|
|
26155
|
-
}
|
|
26156
|
-
const ganttIdRaw = link.ganttId;
|
|
26157
|
-
const sectorIdRaw = link.sectorId;
|
|
26158
|
-
const proplannerId = normalizePersistedLinkId(link.id);
|
|
26159
|
-
return {
|
|
26160
|
-
id,
|
|
26161
|
-
source,
|
|
26162
|
-
target,
|
|
26163
|
-
type,
|
|
26164
|
-
lag: lagDaysToHours(Number(link.lag ?? 0), hoursPerDay),
|
|
26165
|
-
freeSlackHours: null,
|
|
26166
|
-
isCritical: false,
|
|
26167
|
-
...typeof ganttIdRaw === "number" ? { ganttId: ganttIdRaw } : {},
|
|
26168
|
-
...typeof sectorIdRaw === "number" ? { sectorId: sectorIdRaw } : {},
|
|
26169
|
-
...proplannerId === void 0 ? {} : { proplannerId }
|
|
26170
|
-
};
|
|
26171
|
-
}
|
|
26172
|
-
function dedupeLinksBySignature(links, recordDiagnostic) {
|
|
26173
|
-
const bySignature = /* @__PURE__ */ new Map();
|
|
26174
|
-
for (const link of links) {
|
|
26175
|
-
const signature = `${link.source}-${link.target}-${link.type}`;
|
|
26176
|
-
const existing = bySignature.get(signature);
|
|
26177
|
-
if (!existing) {
|
|
26178
|
-
bySignature.set(signature, link);
|
|
26179
|
-
continue;
|
|
26180
|
-
}
|
|
26181
|
-
const incomingWins = linkIdAsNumber(link) > linkIdAsNumber(existing);
|
|
26182
|
-
const discarded = incomingWins ? existing : link;
|
|
26183
|
-
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.DUPLICATE_LINK, discarded.id);
|
|
26184
|
-
if (incomingWins) {
|
|
26185
|
-
bySignature.set(signature, link);
|
|
26186
|
-
}
|
|
26187
|
-
}
|
|
26188
|
-
return [...bySignature.values()];
|
|
26189
|
-
}
|
|
26190
|
-
function linkIdAsNumber(link) {
|
|
26191
|
-
const numeric = Number(link.id);
|
|
26192
|
-
return Number.isFinite(numeric) ? numeric : -Infinity;
|
|
26193
|
-
}
|
|
26194
|
-
|
|
26195
27696
|
// src/boundary/backend/normalize-sector.ts
|
|
26196
27697
|
function parseActivityCreter(raw) {
|
|
26197
27698
|
if (typeof raw !== "string") return DEFAULT_ACTIVITY_CRETER;
|
|
@@ -29243,8 +30744,8 @@ function normalizeLatestConstraintDate(input) {
|
|
|
29243
30744
|
});
|
|
29244
30745
|
}
|
|
29245
30746
|
function isMidnightFinishConstraint(constraintType, constraintDate) {
|
|
29246
|
-
const
|
|
29247
|
-
return
|
|
30747
|
+
const isMidnight2 = constraintDate.getUTCHours() === 0 && constraintDate.getUTCMinutes() === 0 && constraintDate.getUTCSeconds() === 0 && constraintDate.getUTCMilliseconds() === 0;
|
|
30748
|
+
return isMidnight2 && (constraintType === "fnlt" || constraintType === "mfo");
|
|
29248
30749
|
}
|
|
29249
30750
|
function constrainLatestFinish(activity, latestFinish, adapter) {
|
|
29250
30751
|
const bounds = computeConstraintBounds(activity, adapter);
|
|
@@ -32023,8 +33524,36 @@ function emptyChangeSet2(source) {
|
|
|
32023
33524
|
trackingEvents: []
|
|
32024
33525
|
};
|
|
32025
33526
|
}
|
|
33527
|
+
function resolvePersistenceAcknowledgement(action, result, saveTracker) {
|
|
33528
|
+
if (action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE) {
|
|
33529
|
+
return {
|
|
33530
|
+
checkpoint: action.checkpoint,
|
|
33531
|
+
historyBoundary: action.checkpoint?.historyBoundary,
|
|
33532
|
+
persistedActivities: action.activities,
|
|
33533
|
+
persistedLinks: action.links,
|
|
33534
|
+
clearsHistory: false
|
|
33535
|
+
};
|
|
33536
|
+
}
|
|
33537
|
+
if (action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_REBASE) {
|
|
33538
|
+
const rebaseCheckpoint = result.__rebaseCheckpoint;
|
|
33539
|
+
if (rebaseCheckpoint === void 0) {
|
|
33540
|
+
throw new Error("rebase-on-success requires a rebase checkpoint");
|
|
33541
|
+
}
|
|
33542
|
+
return {
|
|
33543
|
+
checkpoint: saveTracker.rebaseBaseline(
|
|
33544
|
+
rebaseCheckpoint.activities,
|
|
33545
|
+
rebaseCheckpoint.links
|
|
33546
|
+
),
|
|
33547
|
+
historyBoundary: void 0,
|
|
33548
|
+
persistedActivities: void 0,
|
|
33549
|
+
persistedLinks: void 0,
|
|
33550
|
+
clearsHistory: true
|
|
33551
|
+
};
|
|
33552
|
+
}
|
|
33553
|
+
throw new Error("checkpoint-on-success requires persistence acknowledge");
|
|
33554
|
+
}
|
|
32026
33555
|
function acknowledgesPersistence(action) {
|
|
32027
|
-
return action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC && action.acknowledgePersistedLookahead === true;
|
|
33556
|
+
return action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE || action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_REBASE || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC && action.acknowledgePersistedLookahead === true;
|
|
32028
33557
|
}
|
|
32029
33558
|
function changesRequireFilterProjectionRebuild(changes) {
|
|
32030
33559
|
for (const change of changes.activities) {
|
|
@@ -33669,6 +35198,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
33669
35198
|
linkIdGen: this.coreRuntime.linkIdGenerator,
|
|
33670
35199
|
isActivityIdReserved: (activityId) => this._saveTracker.hasCheckpointActivityId(activityId),
|
|
33671
35200
|
isLinkIdReserved: (linkId) => this._saveTracker.hasCheckpointLinkId(linkId),
|
|
35201
|
+
persistenceCheckpoint: this._saveTracker,
|
|
33672
35202
|
uidGen: this.coreRuntime.uniqueCorrelativeIdGenerator,
|
|
33673
35203
|
customIdTracker: this.coreRuntime.customIdTracker,
|
|
33674
35204
|
reporter: this.coreRuntime.reporter,
|
|
@@ -33758,32 +35288,37 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
33758
35288
|
);
|
|
33759
35289
|
}
|
|
33760
35290
|
if (result.ok) {
|
|
33761
|
-
if (historyPolicy === DISPATCH_HISTORY_POLICY.CHECKPOINT_ON_SUCCESS) {
|
|
33762
|
-
|
|
33763
|
-
|
|
33764
|
-
|
|
33765
|
-
|
|
33766
|
-
|
|
35291
|
+
if (historyPolicy === DISPATCH_HISTORY_POLICY.CHECKPOINT_ON_SUCCESS || historyPolicy === DISPATCH_HISTORY_POLICY.REBASE_ON_SUCCESS) {
|
|
35292
|
+
const acknowledgement = resolvePersistenceAcknowledgement(
|
|
35293
|
+
action,
|
|
35294
|
+
result,
|
|
35295
|
+
this._saveTracker
|
|
35296
|
+
);
|
|
33767
35297
|
const currentActivities = this.coreRuntime.state.getAllActivities();
|
|
33768
35298
|
const currentLinks = this.coreRuntime.state.getAllLinks();
|
|
33769
|
-
const checkpoint =
|
|
33770
|
-
const boundary = checkpoint?.historyBoundary ?? this._undo.capturePersistenceBoundary();
|
|
35299
|
+
const checkpoint = acknowledgement.checkpoint;
|
|
33771
35300
|
this._saveTracker.acknowledge(
|
|
33772
35301
|
checkpoint?.activities ?? currentActivities,
|
|
33773
35302
|
checkpoint?.links ?? currentLinks,
|
|
33774
|
-
|
|
33775
|
-
|
|
35303
|
+
acknowledgement.persistedActivities,
|
|
35304
|
+
acknowledgement.persistedLinks,
|
|
33776
35305
|
currentActivities,
|
|
33777
35306
|
currentLinks,
|
|
33778
35307
|
checkpoint?.p6Settings ?? this.coreRuntime.state.dataDateSettings(),
|
|
33779
35308
|
checkpoint?.p6ProjectState === void 0 ? this.coreRuntime.state.dataDateProjectState() : checkpoint.p6ProjectState,
|
|
33780
35309
|
checkpoint?.p6LatestRunLog === void 0 ? this.coreRuntime.state.dataDateLatestRunLog() : checkpoint.p6LatestRunLog
|
|
33781
35310
|
);
|
|
33782
|
-
|
|
33783
|
-
|
|
33784
|
-
|
|
33785
|
-
|
|
33786
|
-
|
|
35311
|
+
if (acknowledgement.clearsHistory) {
|
|
35312
|
+
this._undo.clear();
|
|
35313
|
+
} else {
|
|
35314
|
+
this._undo.discardThrough(
|
|
35315
|
+
acknowledgement.historyBoundary ?? this._undo.capturePersistenceBoundary()
|
|
35316
|
+
);
|
|
35317
|
+
this._undo.applyPersistedIdentities(
|
|
35318
|
+
acknowledgement.persistedActivities ?? [],
|
|
35319
|
+
acknowledgement.persistedLinks ?? []
|
|
35320
|
+
);
|
|
35321
|
+
}
|
|
33787
35322
|
} else if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC && action.acknowledgePersistedLookahead === true) {
|
|
33788
35323
|
this._saveTracker.acknowledgePersistedLookahead(
|
|
33789
35324
|
result.changes.activities,
|
|
@@ -34347,6 +35882,7 @@ function toPublicDispatchResult(result) {
|
|
|
34347
35882
|
const {
|
|
34348
35883
|
__beforeSnap: droppedSnapshots,
|
|
34349
35884
|
__beforeLinks: droppedLinks,
|
|
35885
|
+
__rebaseCheckpoint: droppedRebaseCheckpoint,
|
|
34350
35886
|
...publicResult
|
|
34351
35887
|
} = result;
|
|
34352
35888
|
return publicResult;
|
|
@@ -35176,6 +36712,6 @@ var CREATION_KIND = {
|
|
|
35176
36712
|
ROOT: "root"
|
|
35177
36713
|
};
|
|
35178
36714
|
|
|
35179
|
-
export { ACTIVITY_BATCH_ACTION_KIND, ACTIVITY_BATCH_MODE, ACTIVITY_DATE_EDGE, ACTIVITY_FIELD, ACTIVITY_PONDERATOR_CRITERION, ACTIVITY_PROGRESS_ORIGIN, ACTIVITY_TYPE, BASELINE_ROLE, CALENDAR_UNIT, CHANGE_SET_SOURCE_KIND, COLUMN, COMPLETED_PROGRESS, CONFIRMATION_KIND, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CONSTRAINT_WARNING_KIND, CREATION_KIND, P6_PROPERTY_CONTRACT as DATA_DATE_PROPERTY_CONTRACT, DATE_GESTURE_KIND, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_LINK_OPERATION_KIND, DISPATCH_TRACK_EVENT, P6CapabilityError as DataDateCapabilityError, EDIT_ALERT_KEY, EDIT_DENIAL_REASON, ENTITY_CHANGE_KIND, FILTER_FIELD, FILTER_LOGIC, FILTER_OPERATOR, FILTER_REJECTION_REASON, FILTER_VALUE_KIND, FLAT_WBS_DEPTH, GROUP_ID_PREFIX, GROUP_ID_SEPARATOR, LINK_BATCH_OPERATION_KIND, LINK_CODE, LINK_CODE_TO_TYPE, LINK_REJECTION_REASON, LINK_RELATIONSHIP, LINK_TYPE, LINK_TYPE_CODE, LOOKAHEAD_CONFIRMATION_KIND, LookaheadActualTransitionError, MINIMUM_GROUP_DEPTH, MINIMUM_GROUP_LEVEL, NEW_ACTIVITY_DEFAULTS, NOT_STARTED_PROGRESS, ORDER_DIRECTION, ORDER_MODE, PASTE_PLACEMENT, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, SCHEDULE_EFFECT_KIND, SIR_AUTO_REJECT_REASON, STATUS, STATUS_CRITERIA, ScheduleCore, UNASSIGNED_GROUP_LABEL, UNASSIGNED_VALUE_ID, VIEW_ATTRIBUTE_KIND, VISUAL_ROW_KIND, WORK_TIME_DIRECTION, allowedSecondaryConstraintTypes, appendGroupRowId, buildGroupRowId, checkNoUpdatedLinks, computeExpectedProgress, createCalendarReader, createLookaheadDerivedFields, createOutbuildP6ScheduleSettings as createOutbuildDataDateScheduleSettings, createSchedule, expectedProgressFromBaseline, formatUtcAsP6WallDate, getActiveBaseline, getUnsavedActivities, isRootParent, isViewAttributeCriterion, isViewAttributeOrderRule, mapP6ScheduleOptions as mapDataDateScheduleOptions, normalizeParentKey, parseFromBackend, parseP6DayAsCalendarStart, parseP6WallDateAsUtc, planLookaheadProgressActuals, prepareLookaheadProgressActuals, resolveExpectedFinishDateSelection, resolveLookaheadActualDateSelection, willRunCriticalPath, yieldToBrowser };
|
|
36715
|
+
export { ACTIVITY_BATCH_ACTION_KIND, ACTIVITY_BATCH_MODE, ACTIVITY_DATE_EDGE, ACTIVITY_FIELD, ACTIVITY_PONDERATOR_CRITERION, ACTIVITY_PROGRESS_ORIGIN, ACTIVITY_TYPE, BASELINE_ROLE, CALENDAR_UNIT, CHANGE_SET_SOURCE_KIND, COLUMN, COMPLETED_PROGRESS, CONFIRMATION_KIND, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CONSTRAINT_WARNING_KIND, CREATION_KIND, P6_PROPERTY_CONTRACT as DATA_DATE_PROPERTY_CONTRACT, DATE_GESTURE_KIND, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_LINK_OPERATION_KIND, DISPATCH_TRACK_EVENT, P6CapabilityError as DataDateCapabilityError, EDIT_ALERT_KEY, EDIT_DENIAL_REASON, ENTITY_CHANGE_KIND, FILTER_FIELD, FILTER_LOGIC, FILTER_OPERATOR, FILTER_REJECTION_REASON, FILTER_VALUE_KIND, FLAT_WBS_DEPTH, GROUP_ID_PREFIX, GROUP_ID_SEPARATOR, LINK_BATCH_OPERATION_KIND, LINK_CODE, LINK_CODE_TO_TYPE, LINK_REJECTION_REASON, LINK_RELATIONSHIP, LINK_TYPE, LINK_TYPE_CODE, LOOKAHEAD_CONFIRMATION_KIND, LookaheadActualTransitionError, MINIMUM_GROUP_DEPTH, MINIMUM_GROUP_LEVEL, NEW_ACTIVITY_DEFAULTS, NOT_STARTED_PROGRESS, ORDER_DIRECTION, ORDER_MODE, PASTE_PLACEMENT, PERSISTENCE_REBASE_CONFLICT_KIND, PERSISTENCE_REBASE_ENTITY, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, SCHEDULE_EFFECT_KIND, SIR_AUTO_REJECT_REASON, STATUS, STATUS_CRITERIA, ScheduleCore, UNASSIGNED_GROUP_LABEL, UNASSIGNED_VALUE_ID, VIEW_ATTRIBUTE_KIND, VISUAL_ROW_KIND, WORK_TIME_DIRECTION, allowedSecondaryConstraintTypes, appendGroupRowId, buildGroupRowId, checkNoUpdatedLinks, computeExpectedProgress, createCalendarReader, createLookaheadDerivedFields, createOutbuildP6ScheduleSettings as createOutbuildDataDateScheduleSettings, createSchedule, expectedProgressFromBaseline, formatUtcAsP6WallDate, getActiveBaseline, getUnsavedActivities, isRootParent, isViewAttributeCriterion, isViewAttributeOrderRule, mapP6ScheduleOptions as mapDataDateScheduleOptions, normalizeParentKey, parseFromBackend, parseP6DayAsCalendarStart, parseP6WallDateAsUtc, planLookaheadProgressActuals, prepareLookaheadProgressActuals, resolveExpectedFinishDateSelection, resolveLookaheadActualDateSelection, willRunCriticalPath, yieldToBrowser };
|
|
35180
36716
|
//# sourceMappingURL=index.js.map
|
|
35181
36717
|
//# sourceMappingURL=index.js.map
|