@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/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/baselines/apply-baseline-points.ts
18896
- function applyBaselinePoints(adapter, points, reporter, recordDiagnostic = noopLoadDiagnosticRecorder) {
18897
- const idByProplanner = /* @__PURE__ */ new Map();
18898
- adapter.forEachActivity((a, id) => {
18899
- const pid = a.proplannerId;
18900
- if (typeof pid === "number") idByProplanner.set(pid, id);
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
- const grouped = /* @__PURE__ */ new Map();
18903
- for (const p of points) {
18904
- const activityId = p.activityId;
18905
- if (typeof activityId !== "number") {
18906
- recordDiagnostic(
18907
- LOAD_DIAGNOSTIC_CODE.INVALID_BASELINE_POINT,
18908
- String(activityId ?? "unknown")
18909
- );
18910
- continue;
18911
- }
18912
- const coreId = idByProplanner.get(activityId);
18913
- if (coreId === void 0) {
18914
- recordDiagnostic(
18915
- LOAD_DIAGNOSTIC_CODE.INVALID_BASELINE_POINT,
18916
- String(activityId)
18917
- );
18918
- continue;
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
- changed2.push(coreId);
18982
+ return DEFAULT_CONSTRAINT_TYPE;
18943
18983
  }
18944
- adapter.forEachActivity((a, id) => {
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
- // src/baselines/baseline-snapshot.ts
18956
- function buildBaselineSnapshot(activity) {
18957
- const active = getActiveBaseline(activity);
18958
- if (!active) return null;
18959
- return {
18960
- startDate: active.startDate,
18961
- endDate: active.endDate,
18962
- durationDays: active.durationDays,
18963
- cost: active.cost,
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 emitBaselineSnapshotColumns(state) {
18968
- const changed2 = [];
18969
- state.forEachActivity((activity, id) => {
18970
- const snapshot = buildBaselineSnapshot(activity);
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
- // src/baselines/apply-baseline-overlay.ts
18985
- function applyBaselineOverlay(state, points, now, reporter, defaultBaseCalendarId = null, recordDiagnostic = noopLoadDiagnosticRecorder) {
18986
- const changed2 = /* @__PURE__ */ new Set();
18987
- for (const id of applyBaselinePoints(
18988
- state,
18989
- points,
18990
- reporter,
18991
- recordDiagnostic
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
- if (now) {
18996
- for (const id of runExpectedProgressBase(state, now, defaultBaseCalendarId))
18997
- changed2.add(id);
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
- for (const id of emitBaselineSnapshotColumns(state)) changed2.add(id);
19000
- for (const id of emitRealCost(state)) changed2.add(id);
19001
- return [...changed2];
19025
+ return ids;
19002
19026
  }
19003
-
19004
- // src/propagations/downward/recompute-all-ponderators.ts
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
- // src/dispatch/shared/baseline-weighted-fields.ts
19018
- function recomputeBaselineWeightedFields(deps, criterion) {
19019
- recomputeAllPonderators(criterion, deps.adapter);
19020
- recomputeAllProgressRollup(deps.adapter, criterion);
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
- emitRealCost(deps.adapter);
19030
- if (deps.now) applyStatusPass(deps.adapter, deps.sector.statusCriteria);
19036
+ return rawId;
19031
19037
  }
19032
-
19033
- // src/dispatch/handlers/baseline-apply.ts
19034
- function activeBaselineSignature(deps) {
19035
- return JSON.stringify(
19036
- deps.adapter.getAllIds().map((id) => {
19037
- const point = deps.adapter.getActivity(id)?.baselinePoints.find((candidate) => candidate.isActiveVersion);
19038
- return point ? [
19039
- String(id),
19040
- point.versionId,
19041
- point.startDate?.getTime() ?? null,
19042
- point.endDate?.getTime() ?? null,
19043
- point.durationDays,
19044
- point.cost,
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
- async function dispatchBaselineApply(action, deps) {
19054
- const beforeSnap = await snapshotActivities(
19055
- deps.adapter,
19056
- new Set(deps.adapter.getAllIds())
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
- const activeBaselineBefore = activeBaselineSignature(deps);
19059
- const changedIds = applyBaselineOverlay(
19060
- deps.adapter,
19061
- action.points,
19062
- deps.now,
19063
- deps.reporter,
19064
- deps.defaultBaseCalendarId ?? null,
19065
- void 0
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
- applyBaselineVariancePass(deps.adapter, deps.sector.hoursPerDay);
19068
- applyRoleBaselineVariancePass(deps.adapter, deps.sector.hoursPerDay);
19069
- if (activeBaselineSignature(deps) !== activeBaselineBefore) {
19070
- recomputeBaselineWeightedFields(
19071
- deps,
19072
- deps.sector.activityCreter ?? ACTIVITY_PONDERATOR_CRITERION.DURATION
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
- ok: true,
19077
- changes: await assembleChangeSet(deps.adapter, {
19078
- source: action,
19079
- beforeSnap,
19080
- touchedIds: changedIds,
19081
- scheduledIds: [],
19082
- hoursPerDay: deps.sector.hoursPerDay
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
- // src/dispatch/handlers/ponderator-criterion-set.ts
19088
- function isValidCriterion(value) {
19089
- return Object.values(ACTIVITY_PONDERATOR_CRITERION).includes(
19090
- value
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
- async function dispatchPonderatorCriterionSet(action, deps) {
19094
- if (!isValidCriterion(action.criterion)) {
19095
- return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
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/dispatch/handlers/status-criteria-set.ts
19114
- async function dispatchStatusCriteriaSet(action, deps) {
19115
- const resolved = resolveStatusCriteria(action.criteria);
19116
- const allIds = new Set(deps.adapter.getAllIds());
19117
- const beforeSnap = await snapshotActivities(deps.adapter, allIds);
19118
- const changedIds = applyStatusPass(deps.adapter, resolved);
19119
- deps.sector.statusCriteria = resolved;
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
- // src/dispatch/handlers/calendar-reconcile.ts
19133
- async function dispatchCalendarReconcile(action, options, deps) {
19134
- const beforeSnap = /* @__PURE__ */ new Map();
19135
- const touchedIds = /* @__PURE__ */ new Set();
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
- const { scheduledIds } = await runPostMutation(
19162
- {
19163
- adapter: deps.adapter,
19164
- scheduler: deps.scheduler,
19165
- sector: deps.sector,
19166
- defaultBaseCalendarId: deps.defaultBaseCalendarId,
19167
- runAutomaticP6Schedule: deps.runAutomaticP6Schedule
19168
- },
19169
- {
19170
- action,
19171
- autoscheduleFrom: AUTOSCHEDULE_SCOPE.ROOTS,
19172
- recomputeParentsFrom: touchedIds,
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 DATE_FIELDS = /* @__PURE__ */ new Set([
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 (DATE_FIELDS.has(field) && typeof value === "string") {
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 = resolveCalendarId(task);
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 resolveCalendarId(task) {
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 resolveCalendarId(task) ?? DEFAULT_CALENDAR_KEY;
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: pickDefaultCalendarId(calendars)
26236
+ defaultCalendarId: pickDefaultCalendarId2(calendars)
24300
26237
  };
24301
26238
  }
24302
- function pickDefaultCalendarId(calendars) {
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 toTs = parseExceptionDate(to);
24368
- if (fromTs === null || toTs === null) return [];
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: () => pickDefaultCalendarId(options.calendars.values()),
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(parentKeyOf2(deletedFields.parentId?.before));
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(parentKeyOf2(fields.parentId?.before));
27054
+ touched.add(parentKeyOf4(fields.parentId?.before));
25104
27055
  }
25105
27056
  }
25106
27057
  return touched;
25107
27058
  }
25108
- function parentKeyOf2(parentId) {
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 isMidnight = constraintDate.getUTCHours() === 0 && constraintDate.getUTCMinutes() === 0 && constraintDate.getUTCSeconds() === 0 && constraintDate.getUTCMilliseconds() === 0;
29247
- return isMidnight && (constraintType === "fnlt" || constraintType === "mfo");
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
- if (action.kind !== DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE) {
33763
- throw new Error(
33764
- "checkpoint-on-success requires persistence acknowledge"
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 = action.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
- action.activities,
33775
- action.links,
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
- this._undo.discardThrough(boundary);
33783
- this._undo.applyPersistedIdentities(
33784
- action.activities ?? [],
33785
- action.links ?? []
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