@outbuild-company/schedule-core 1.9.2 → 1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -337,14 +337,15 @@ type LinksBatchOperation = {
337
337
  kind: 'delete';
338
338
  linkId: LinkId;
339
339
  };
340
- interface ReconciledActivityProgress {
341
- readonly activityId: string;
342
- readonly progress: number;
343
- }
344
340
  interface PersistedEntityIdentity {
345
341
  readonly id: string;
346
342
  readonly proplannerId: number;
347
343
  }
344
+ interface PersistenceCheckpoint {
345
+ readonly activities: ReadonlyArray<Readonly<CoreActivity>>;
346
+ readonly links: ReadonlyArray<Readonly<Link>>;
347
+ readonly historyBoundary: number;
348
+ }
348
349
  type ActivityBatchMode = 'append' | 'replace';
349
350
  type ActivityBatchReference = ActivityId | {
350
351
  readonly batchId: string;
@@ -388,7 +389,7 @@ type DispatchAction = {
388
389
  kind: 'persistence-acknowledge';
389
390
  activities?: ReadonlyArray<PersistedEntityIdentity>;
390
391
  links?: ReadonlyArray<PersistedEntityIdentity>;
391
- reconciledProgress?: ReadonlyArray<ReconciledActivityProgress>;
392
+ checkpoint?: PersistenceCheckpoint;
392
393
  } | {
393
394
  kind: 'baseline-apply';
394
395
  points: ReadonlyArray<BaselinePoint>;
@@ -599,6 +600,57 @@ interface ProjectWorkHours {
599
600
  endHour: string;
600
601
  }
601
602
 
603
+ declare const ACTIVITY_SAVE_FIELD_POLICY: {
604
+ readonly id: "ignored";
605
+ readonly proplannerId: "ignored";
606
+ readonly parentId: "tracked";
607
+ readonly name: "tracked";
608
+ readonly description: "tracked";
609
+ readonly type: "tracked";
610
+ readonly startDate: "tracked";
611
+ readonly endDate: "tracked";
612
+ readonly durationHours: "tracked";
613
+ readonly constraintType: "tracked";
614
+ readonly constraintDate: "tracked";
615
+ readonly autoScheduling: "tracked";
616
+ readonly calendarId: "tracked";
617
+ readonly assignedDefaultCalendar: "ignored";
618
+ readonly dateOrigin: "tracked";
619
+ readonly progress: "tracked";
620
+ readonly cost: "tracked";
621
+ readonly usedCost: "tracked";
622
+ readonly realCost: "tracked";
623
+ readonly workHours: "tracked";
624
+ readonly realWorkHours: "tracked";
625
+ readonly ponderator: "tracked";
626
+ readonly hasCustomPonderator: "tracked";
627
+ readonly correlativeId: "tracked";
628
+ readonly uniqueCorrelativeId: "tracked";
629
+ readonly customId: "tracked";
630
+ readonly subcontractId: "tracked";
631
+ readonly isLookahead: "tracked";
632
+ readonly hasLookaheadTasks: "ignored";
633
+ readonly newActivityIds: "tracked";
634
+ readonly hasNewActivities: "tracked";
635
+ readonly isNewActivity: "ignored";
636
+ readonly pendingRequestIds: "ignored";
637
+ readonly responsableIds: "tracked";
638
+ readonly tagIds: "tracked";
639
+ readonly baselinePoints: "ignored";
640
+ readonly baselineSnapshot: "ignored";
641
+ readonly recursiveDurationHours: "ignored";
642
+ readonly isCritical: "ignored";
643
+ readonly freeSlackHours: "ignored";
644
+ readonly calendarDuration: "ignored";
645
+ readonly expectedProgress: "ignored";
646
+ readonly expectedProgressBaseline: "ignored";
647
+ readonly status: "ignored";
648
+ readonly criticalPath: "ignored";
649
+ };
650
+ type PersistableActivityField = {
651
+ [Field in keyof typeof ACTIVITY_SAVE_FIELD_POLICY]: (typeof ACTIVITY_SAVE_FIELD_POLICY)[Field] extends 'tracked' ? Field : never;
652
+ }[keyof typeof ACTIVITY_SAVE_FIELD_POLICY];
653
+
602
654
  interface BackendShiftInput {
603
655
  shift_string: string;
604
656
  }
@@ -797,9 +849,11 @@ declare class ScheduleCore {
797
849
  getModifiedLinks(): ReadonlyArray<Readonly<Link>>;
798
850
  getNewLinks(): ReadonlyArray<Readonly<Link>>;
799
851
  getModifiedActivities(): ReadonlyArray<Readonly<CoreActivity>>;
852
+ getModifiedActivityFields(): ReadonlyMap<string, ReadonlyArray<PersistableActivityField>>;
800
853
  getNewActivities(): ReadonlyArray<Readonly<CoreActivity>>;
801
854
  hasUnsavedChanges(): boolean;
802
855
  getScheduleRevision(): number;
856
+ capturePersistenceHistoryBoundary(): number;
803
857
  getCanonicalDigest(): string;
804
858
  createProjectionChangeSetFrom(source: ScheduleCore): ChangeSet;
805
859
  getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
@@ -994,4 +1048,4 @@ declare const DISPATCH_TRACK_EVENT: {
994
1048
  };
995
1049
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
996
1050
 
997
- export { ACTIVITY_TYPE, type ActivityBaselineSnapshot, type ActivityBatchAction, type ActivityBatchItem, type ActivityBatchLinkInput, type ActivityBatchMode, type ActivityBatchParentReference, type ActivityBatchReference, type ActivityCreationOverrides, type ActivityCreter, type ActivityId, type ActivityStatus, type ActivityType, type BackendActivityInput, type BackendCalendarExceptionInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BackendShiftInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarLike, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ChangeSetSource, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CoreBaselinePoint, type CreationKind, type CriticalPathResult, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DateGesture, type DateGesturePreview, type DatesBatchVerdict, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EditableColumnName, type EffectiveGraphAudit, type EntityChange, type ExpectedProgressAdapter, type FilterCriterion, type FilterCriterionValue, type FilterDateRange, type FilterField, type FilterOperator, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkBatchVerdict, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PendingRequest, type PersistedEntityIdentity, type PipelineColumnName, type ProjectWorkHours, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, STATUS, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StaticActivityDefaults, type StatusCriteria, type TrackingEvent, type UndoOptions, type ViewStateChange, type ViewStatePropChange, WORK_TIME_DIRECTION, type WithProplannerId, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
1051
+ export { ACTIVITY_TYPE, type ActivityBaselineSnapshot, type ActivityBatchAction, type ActivityBatchItem, type ActivityBatchLinkInput, type ActivityBatchMode, type ActivityBatchParentReference, type ActivityBatchReference, type ActivityCreationOverrides, type ActivityCreter, type ActivityId, type ActivityStatus, type ActivityType, type BackendActivityInput, type BackendCalendarExceptionInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BackendShiftInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarLike, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ChangeSetSource, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CoreBaselinePoint, type CreationKind, type CriticalPathResult, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DateGesture, type DateGesturePreview, type DatesBatchVerdict, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EditableColumnName, type EffectiveGraphAudit, type EntityChange, type ExpectedProgressAdapter, type FilterCriterion, type FilterCriterionValue, type FilterDateRange, type FilterField, type FilterOperator, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkBatchVerdict, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PendingRequest, type PersistableActivityField, type PersistedEntityIdentity, type PersistenceCheckpoint, type PipelineColumnName, type ProjectWorkHours, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, STATUS, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StaticActivityDefaults, type StatusCriteria, type TrackingEvent, type UndoOptions, type ViewStateChange, type ViewStatePropChange, WORK_TIME_DIRECTION, type WithProplannerId, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
package/dist/index.d.ts CHANGED
@@ -337,14 +337,15 @@ type LinksBatchOperation = {
337
337
  kind: 'delete';
338
338
  linkId: LinkId;
339
339
  };
340
- interface ReconciledActivityProgress {
341
- readonly activityId: string;
342
- readonly progress: number;
343
- }
344
340
  interface PersistedEntityIdentity {
345
341
  readonly id: string;
346
342
  readonly proplannerId: number;
347
343
  }
344
+ interface PersistenceCheckpoint {
345
+ readonly activities: ReadonlyArray<Readonly<CoreActivity>>;
346
+ readonly links: ReadonlyArray<Readonly<Link>>;
347
+ readonly historyBoundary: number;
348
+ }
348
349
  type ActivityBatchMode = 'append' | 'replace';
349
350
  type ActivityBatchReference = ActivityId | {
350
351
  readonly batchId: string;
@@ -388,7 +389,7 @@ type DispatchAction = {
388
389
  kind: 'persistence-acknowledge';
389
390
  activities?: ReadonlyArray<PersistedEntityIdentity>;
390
391
  links?: ReadonlyArray<PersistedEntityIdentity>;
391
- reconciledProgress?: ReadonlyArray<ReconciledActivityProgress>;
392
+ checkpoint?: PersistenceCheckpoint;
392
393
  } | {
393
394
  kind: 'baseline-apply';
394
395
  points: ReadonlyArray<BaselinePoint>;
@@ -599,6 +600,57 @@ interface ProjectWorkHours {
599
600
  endHour: string;
600
601
  }
601
602
 
603
+ declare const ACTIVITY_SAVE_FIELD_POLICY: {
604
+ readonly id: "ignored";
605
+ readonly proplannerId: "ignored";
606
+ readonly parentId: "tracked";
607
+ readonly name: "tracked";
608
+ readonly description: "tracked";
609
+ readonly type: "tracked";
610
+ readonly startDate: "tracked";
611
+ readonly endDate: "tracked";
612
+ readonly durationHours: "tracked";
613
+ readonly constraintType: "tracked";
614
+ readonly constraintDate: "tracked";
615
+ readonly autoScheduling: "tracked";
616
+ readonly calendarId: "tracked";
617
+ readonly assignedDefaultCalendar: "ignored";
618
+ readonly dateOrigin: "tracked";
619
+ readonly progress: "tracked";
620
+ readonly cost: "tracked";
621
+ readonly usedCost: "tracked";
622
+ readonly realCost: "tracked";
623
+ readonly workHours: "tracked";
624
+ readonly realWorkHours: "tracked";
625
+ readonly ponderator: "tracked";
626
+ readonly hasCustomPonderator: "tracked";
627
+ readonly correlativeId: "tracked";
628
+ readonly uniqueCorrelativeId: "tracked";
629
+ readonly customId: "tracked";
630
+ readonly subcontractId: "tracked";
631
+ readonly isLookahead: "tracked";
632
+ readonly hasLookaheadTasks: "ignored";
633
+ readonly newActivityIds: "tracked";
634
+ readonly hasNewActivities: "tracked";
635
+ readonly isNewActivity: "ignored";
636
+ readonly pendingRequestIds: "ignored";
637
+ readonly responsableIds: "tracked";
638
+ readonly tagIds: "tracked";
639
+ readonly baselinePoints: "ignored";
640
+ readonly baselineSnapshot: "ignored";
641
+ readonly recursiveDurationHours: "ignored";
642
+ readonly isCritical: "ignored";
643
+ readonly freeSlackHours: "ignored";
644
+ readonly calendarDuration: "ignored";
645
+ readonly expectedProgress: "ignored";
646
+ readonly expectedProgressBaseline: "ignored";
647
+ readonly status: "ignored";
648
+ readonly criticalPath: "ignored";
649
+ };
650
+ type PersistableActivityField = {
651
+ [Field in keyof typeof ACTIVITY_SAVE_FIELD_POLICY]: (typeof ACTIVITY_SAVE_FIELD_POLICY)[Field] extends 'tracked' ? Field : never;
652
+ }[keyof typeof ACTIVITY_SAVE_FIELD_POLICY];
653
+
602
654
  interface BackendShiftInput {
603
655
  shift_string: string;
604
656
  }
@@ -797,9 +849,11 @@ declare class ScheduleCore {
797
849
  getModifiedLinks(): ReadonlyArray<Readonly<Link>>;
798
850
  getNewLinks(): ReadonlyArray<Readonly<Link>>;
799
851
  getModifiedActivities(): ReadonlyArray<Readonly<CoreActivity>>;
852
+ getModifiedActivityFields(): ReadonlyMap<string, ReadonlyArray<PersistableActivityField>>;
800
853
  getNewActivities(): ReadonlyArray<Readonly<CoreActivity>>;
801
854
  hasUnsavedChanges(): boolean;
802
855
  getScheduleRevision(): number;
856
+ capturePersistenceHistoryBoundary(): number;
803
857
  getCanonicalDigest(): string;
804
858
  createProjectionChangeSetFrom(source: ScheduleCore): ChangeSet;
805
859
  getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
@@ -994,4 +1048,4 @@ declare const DISPATCH_TRACK_EVENT: {
994
1048
  };
995
1049
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
996
1050
 
997
- export { ACTIVITY_TYPE, type ActivityBaselineSnapshot, type ActivityBatchAction, type ActivityBatchItem, type ActivityBatchLinkInput, type ActivityBatchMode, type ActivityBatchParentReference, type ActivityBatchReference, type ActivityCreationOverrides, type ActivityCreter, type ActivityId, type ActivityStatus, type ActivityType, type BackendActivityInput, type BackendCalendarExceptionInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BackendShiftInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarLike, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ChangeSetSource, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CoreBaselinePoint, type CreationKind, type CriticalPathResult, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DateGesture, type DateGesturePreview, type DatesBatchVerdict, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EditableColumnName, type EffectiveGraphAudit, type EntityChange, type ExpectedProgressAdapter, type FilterCriterion, type FilterCriterionValue, type FilterDateRange, type FilterField, type FilterOperator, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkBatchVerdict, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PendingRequest, type PersistedEntityIdentity, type PipelineColumnName, type ProjectWorkHours, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, STATUS, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StaticActivityDefaults, type StatusCriteria, type TrackingEvent, type UndoOptions, type ViewStateChange, type ViewStatePropChange, WORK_TIME_DIRECTION, type WithProplannerId, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
1051
+ export { ACTIVITY_TYPE, type ActivityBaselineSnapshot, type ActivityBatchAction, type ActivityBatchItem, type ActivityBatchLinkInput, type ActivityBatchMode, type ActivityBatchParentReference, type ActivityBatchReference, type ActivityCreationOverrides, type ActivityCreter, type ActivityId, type ActivityStatus, type ActivityType, type BackendActivityInput, type BackendCalendarExceptionInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BackendShiftInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarLike, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ChangeSetSource, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CoreBaselinePoint, type CreationKind, type CriticalPathResult, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DateGesture, type DateGesturePreview, type DatesBatchVerdict, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EditableColumnName, type EffectiveGraphAudit, type EntityChange, type ExpectedProgressAdapter, type FilterCriterion, type FilterCriterionValue, type FilterDateRange, type FilterField, type FilterOperator, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkBatchVerdict, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PendingRequest, type PersistableActivityField, type PersistedEntityIdentity, type PersistenceCheckpoint, type PipelineColumnName, type ProjectWorkHours, REJECTION_REASON, ROOT_PARENT_ID, SCHEDULE_CORE_STATUS, STATUS, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StaticActivityDefaults, type StatusCriteria, type TrackingEvent, type UndoOptions, type ViewStateChange, type ViewStatePropChange, WORK_TIME_DIRECTION, type WithProplannerId, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
package/dist/index.js CHANGED
@@ -12541,28 +12541,6 @@ function assertValidAssignments(assignments, entityName, readCurrent, ownerByBac
12541
12541
  );
12542
12542
  }
12543
12543
  }
12544
- function assertValidReconciledProgress(entries, deps) {
12545
- const seen = /* @__PURE__ */ new Set();
12546
- for (const entry of entries) {
12547
- const id = String(entry.activityId);
12548
- if (id.length === 0 || seen.has(id)) {
12549
- throw new Error(
12550
- `persistence-acknowledge: duplicate or empty reconciled activity id "${id}"`
12551
- );
12552
- }
12553
- if (!Number.isFinite(entry.progress) || entry.progress < 0 || entry.progress > 100) {
12554
- throw new Error(
12555
- `persistence-acknowledge: invalid reconciled progress ${String(entry.progress)} for "${id}"`
12556
- );
12557
- }
12558
- if (!deps.adapter.getActivity(id)) {
12559
- throw new Error(
12560
- `persistence-acknowledge: unknown reconciled activity id "${id}"`
12561
- );
12562
- }
12563
- seen.add(id);
12564
- }
12565
- }
12566
12544
  async function applyReconciledProgress(entries, deps) {
12567
12545
  const progressToApply = entries.filter((entry) => {
12568
12546
  const current = deps.adapter.getActivity(String(entry.activityId));
@@ -12593,19 +12571,25 @@ async function applyReconciledProgress(entries, deps) {
12593
12571
  async function dispatchPersistenceAcknowledge(action, deps) {
12594
12572
  const activities = action.activities ?? [];
12595
12573
  const links = action.links ?? [];
12596
- const reconciledProgress = action.reconciledProgress ?? [];
12597
- assertValidReconciledProgress(reconciledProgress, deps);
12598
12574
  const currentActivities = deps.adapter.getAllActivities();
12599
12575
  const currentLinks = deps.adapter.getAllLinks();
12600
- const activityOwnerByBackendId = persistedOwnersOf(currentActivities);
12601
- const linkOwnerByBackendId = persistedOwnersOf(currentLinks);
12576
+ const checkpointActivities = action.checkpoint?.activities ?? currentActivities;
12577
+ const checkpointLinks = action.checkpoint?.links ?? currentLinks;
12578
+ const checkpointActivityById = new Map(
12579
+ checkpointActivities.map((activity) => [String(activity.id), activity])
12580
+ );
12581
+ const checkpointLinkById = new Map(
12582
+ checkpointLinks.map((link) => [String(link.id), link])
12583
+ );
12584
+ const activityOwnerByBackendId = persistedOwnersOf(checkpointActivities);
12585
+ const linkOwnerByBackendId = persistedOwnersOf(checkpointLinks);
12602
12586
  assertValidAssignments(
12603
12587
  activities,
12604
12588
  "activity",
12605
12589
  (id) => {
12606
- const activity = deps.adapter.getActivity(id);
12590
+ const activity = checkpointActivityById.get(id);
12607
12591
  return {
12608
- exists: activity !== null,
12592
+ exists: activity !== void 0,
12609
12593
  current: activity?.proplannerId ?? null
12610
12594
  };
12611
12595
  },
@@ -12615,17 +12599,21 @@ async function dispatchPersistenceAcknowledge(action, deps) {
12615
12599
  links,
12616
12600
  "link",
12617
12601
  (id) => {
12618
- const link = deps.adapter.getLink(id);
12619
- return { exists: link !== null, current: link?.proplannerId ?? null };
12602
+ const link = checkpointLinkById.get(id);
12603
+ return {
12604
+ exists: link !== void 0,
12605
+ current: link?.proplannerId ?? null
12606
+ };
12620
12607
  },
12621
12608
  linkOwnerByBackendId
12622
12609
  );
12623
- assertAllNewEntitiesAcknowledged(currentActivities, activities, "activity");
12624
- assertAllNewEntitiesAcknowledged(currentLinks, links, "link");
12625
- const activityChanges = await applyReconciledProgress(
12626
- reconciledProgress,
12627
- deps
12610
+ assertAllNewEntitiesAcknowledged(
12611
+ checkpointActivities,
12612
+ activities,
12613
+ "activity"
12628
12614
  );
12615
+ assertAllNewEntitiesAcknowledged(checkpointLinks, links, "link");
12616
+ const activityChanges = [];
12629
12617
  const linkChanges = [];
12630
12618
  deps.adapter.batchUpdate(() => {
12631
12619
  for (const assignment of activities) {
@@ -18439,7 +18427,9 @@ function mergeCoalesced(top, next) {
18439
18427
  return buildUndoEntry(changeSet);
18440
18428
  }
18441
18429
  function getDispatchHistoryPolicy(action) {
18442
- if (action.kind === "persistence-acknowledge") return "clear-on-success";
18430
+ if (action.kind === "persistence-acknowledge") {
18431
+ return "checkpoint-on-success";
18432
+ }
18443
18433
  if (action.kind === "sir-sync" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "activity-clipboard-clear" || action.kind === "activity-copy" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set" || action.kind === "sort-set") {
18444
18434
  return "skip";
18445
18435
  }
@@ -18695,22 +18685,25 @@ var UndoRecorder = class _UndoRecorder {
18695
18685
  maxDepth;
18696
18686
  undoStack = [];
18697
18687
  redoStack = [];
18688
+ sequence = 0;
18698
18689
  _lastKey = null;
18699
18690
  _lastTime = 0;
18700
18691
  fork() {
18701
18692
  const fork = new _UndoRecorder(this.maxDepth);
18702
18693
  fork.undoStack.push(...cloneDomainValue(this.undoStack));
18703
18694
  fork.redoStack.push(...cloneDomainValue(this.redoStack));
18695
+ fork.sequence = this.sequence;
18704
18696
  fork._lastKey = this._lastKey;
18705
18697
  fork._lastTime = this._lastTime;
18706
18698
  return fork;
18707
18699
  }
18708
18700
  record(entry, coalesceKey = null, now = 0) {
18709
18701
  const top = this.undoStack[this.undoStack.length - 1];
18710
- if (coalesceKey != null && coalesceKey === this._lastKey && now - this._lastTime <= COALESCE_WINDOW_MS && top !== void 0 && canCoalesce(top, entry)) {
18711
- this.undoStack[this.undoStack.length - 1] = mergeCoalesced(top, entry);
18702
+ if (coalesceKey != null && coalesceKey === this._lastKey && now - this._lastTime <= COALESCE_WINDOW_MS && top !== void 0 && canCoalesce(top.entry, entry)) {
18703
+ top.entry = mergeCoalesced(top.entry, entry);
18704
+ top.touchedAt = this.touch();
18712
18705
  } else {
18713
- this.undoStack.push(entry);
18706
+ this.undoStack.push({ entry, touchedAt: this.touch() });
18714
18707
  if (this.undoStack.length > this.maxDepth) {
18715
18708
  this.undoStack.splice(0, this.undoStack.length - this.maxDepth);
18716
18709
  }
@@ -18723,16 +18716,16 @@ var UndoRecorder = class _UndoRecorder {
18723
18716
  this._lastKey = null;
18724
18717
  }
18725
18718
  takeUndo() {
18726
- return this.undoStack.pop() ?? null;
18719
+ return this.undoStack.pop()?.entry ?? null;
18727
18720
  }
18728
18721
  takeRedo() {
18729
- return this.redoStack.pop() ?? null;
18722
+ return this.redoStack.pop()?.entry ?? null;
18730
18723
  }
18731
18724
  pushRedo(entry) {
18732
- this.redoStack.push(entry);
18725
+ this.redoStack.push({ entry, touchedAt: this.touch() });
18733
18726
  }
18734
18727
  pushUndo(entry) {
18735
- this.undoStack.push(entry);
18728
+ this.undoStack.push({ entry, touchedAt: this.touch() });
18736
18729
  }
18737
18730
  canUndo() {
18738
18731
  return this.undoStack.length > 0;
@@ -18743,11 +18736,64 @@ var UndoRecorder = class _UndoRecorder {
18743
18736
  undoDepth() {
18744
18737
  return this.undoStack.length;
18745
18738
  }
18739
+ capturePersistenceBoundary() {
18740
+ this.resetCoalesce();
18741
+ return this.sequence;
18742
+ }
18743
+ discardThrough(boundary) {
18744
+ this.removeEntriesThrough(this.undoStack, boundary);
18745
+ this.removeEntriesThrough(this.redoStack, boundary);
18746
+ this.resetCoalesce();
18747
+ }
18748
+ applyPersistedIdentities(activities, links) {
18749
+ const activityIds = new Map(
18750
+ activities.map(({ id, proplannerId }) => [String(id), proplannerId])
18751
+ );
18752
+ const linkIds = new Map(
18753
+ links.map(({ id, proplannerId }) => [String(id), proplannerId])
18754
+ );
18755
+ for (const stored of [...this.undoStack, ...this.redoStack]) {
18756
+ stored.entry = this.withPersistedIdentities(
18757
+ stored.entry,
18758
+ activityIds,
18759
+ linkIds
18760
+ );
18761
+ }
18762
+ }
18746
18763
  clear() {
18747
18764
  this.undoStack.length = 0;
18748
18765
  this.redoStack.length = 0;
18749
18766
  this.resetCoalesce();
18750
18767
  }
18768
+ touch() {
18769
+ this.sequence += 1;
18770
+ return this.sequence;
18771
+ }
18772
+ removeEntriesThrough(stack, boundary) {
18773
+ const retained = stack.filter(({ touchedAt }) => touchedAt > boundary);
18774
+ stack.splice(0, stack.length, ...retained);
18775
+ }
18776
+ withPersistedIdentities(entry, activityIds, linkIds) {
18777
+ const patchActivities = (snapshots) => snapshots === void 0 ? void 0 : new Map(
18778
+ Array.from(snapshots, ([id, activity]) => [
18779
+ id,
18780
+ activityIds.has(id) ? { ...activity, proplannerId: activityIds.get(id) } : activity
18781
+ ])
18782
+ );
18783
+ const patchLinks = (snapshots) => snapshots === void 0 ? void 0 : new Map(
18784
+ Array.from(snapshots, ([id, link]) => [
18785
+ id,
18786
+ linkIds.has(id) ? { ...link, proplannerId: linkIds.get(id) } : link
18787
+ ])
18788
+ );
18789
+ return {
18790
+ ...entry,
18791
+ beforeSnap: patchActivities(entry.beforeSnap),
18792
+ afterSnap: patchActivities(entry.afterSnap),
18793
+ beforeLinks: patchLinks(entry.beforeLinks),
18794
+ afterLinks: patchLinks(entry.afterLinks)
18795
+ };
18796
+ }
18751
18797
  };
18752
18798
 
18753
18799
  // src/dispatch/shared/resequenced-parents.ts
@@ -18905,10 +18951,12 @@ function snapshotFieldValue(field, value) {
18905
18951
  return value;
18906
18952
  }
18907
18953
  function activityChangedSince(current, saved) {
18908
- for (const field of PERSISTABLE_ACTIVITY_FIELDS) {
18909
- if (!fieldEquals(field, current[field], saved[field])) return true;
18910
- }
18911
- return false;
18954
+ return changedPersistableActivityFields(current, saved).length > 0;
18955
+ }
18956
+ function changedPersistableActivityFields(current, saved) {
18957
+ return PERSISTABLE_ACTIVITY_FIELDS.filter(
18958
+ (field) => !fieldEquals(field, current[field], saved[field])
18959
+ );
18912
18960
  }
18913
18961
  function fieldEquals(field, currentValue, savedValue) {
18914
18962
  if (DATE_FIELDS2.has(field)) return dateEquals(currentValue, savedValue);
@@ -19017,11 +19065,60 @@ var SaveTracker = class _SaveTracker {
19017
19065
  }
19018
19066
  this.initialized = true;
19019
19067
  }
19020
- acknowledge(activities, links) {
19068
+ acknowledge(activities, links, activityIdentities = [], linkIdentities = [], currentActivities, currentLinks) {
19021
19069
  if (!this.trackingEnabled) return;
19022
- assertValidPersistedIdentities(activities, links);
19023
- this.snapshotActivities(activities);
19024
- this.snapshotLinks(links);
19070
+ const activityIds = new Map(
19071
+ activityIdentities.map(({ id, proplannerId }) => [
19072
+ String(id),
19073
+ proplannerId
19074
+ ])
19075
+ );
19076
+ const linkIds = new Map(
19077
+ linkIdentities.map(({ id, proplannerId }) => [String(id), proplannerId])
19078
+ );
19079
+ const persistedActivities = activities.map((activity) => ({
19080
+ ...activity,
19081
+ proplannerId: activityIds.get(String(activity.id)) ?? activity.proplannerId
19082
+ }));
19083
+ const persistedLinks = links.map((link) => {
19084
+ const proplannerId = linkIds.get(String(link.id));
19085
+ return proplannerId === void 0 ? { ...link } : { ...link, proplannerId };
19086
+ });
19087
+ assertValidPersistedIdentities(persistedActivities, persistedLinks);
19088
+ this.snapshotActivities(persistedActivities);
19089
+ this.snapshotLinks(persistedLinks);
19090
+ if (!currentActivities || !currentLinks) return;
19091
+ const currentActivityById = new Map(
19092
+ currentActivities.map((activity) => [String(activity.id), activity])
19093
+ );
19094
+ const currentLinkById = new Map(
19095
+ currentLinks.map((link) => [String(link.id), link])
19096
+ );
19097
+ for (const activity of persistedActivities) {
19098
+ const activityId = String(activity.id);
19099
+ if (this.activityIsDirty(
19100
+ activityId,
19101
+ currentActivityById.get(activityId) ?? null
19102
+ )) {
19103
+ this.dirtyActivityIds.add(activityId);
19104
+ }
19105
+ }
19106
+ for (const activity of currentActivities) {
19107
+ const activityId = String(activity.id);
19108
+ if (this.activityIsDirty(activityId, activity)) {
19109
+ this.dirtyActivityIds.add(activityId);
19110
+ }
19111
+ }
19112
+ for (const link of persistedLinks) {
19113
+ const linkId = String(link.id);
19114
+ if (this.linkIsDirty(linkId, currentLinkById.get(linkId) ?? null)) {
19115
+ this.dirtyLinkIds.add(linkId);
19116
+ }
19117
+ }
19118
+ for (const link of currentLinks) {
19119
+ const linkId = String(link.id);
19120
+ if (this.linkIsDirty(linkId, link)) this.dirtyLinkIds.add(linkId);
19121
+ }
19025
19122
  }
19026
19123
  snapshotActivities(activities) {
19027
19124
  if (!this.trackingEnabled) return;
@@ -19205,6 +19302,21 @@ var SaveTracker = class _SaveTracker {
19205
19302
  }
19206
19303
  return modified;
19207
19304
  }
19305
+ modifiedActivityFields(getActivity) {
19306
+ const result = /* @__PURE__ */ new Map();
19307
+ if (!this.trackingEnabled || !this.initialized) return result;
19308
+ for (const activityId of this.dirtyActivityIds) {
19309
+ const current = getActivity(activityId);
19310
+ const saved = this.activitiesAtLastSave.get(activityId);
19311
+ if (!current || !saved) continue;
19312
+ const fields = changedPersistableActivityFields(
19313
+ current,
19314
+ saved.persistable
19315
+ );
19316
+ if (fields.length > 0) result.set(activityId, fields);
19317
+ }
19318
+ return result;
19319
+ }
19208
19320
  modifiedLinks(getLink) {
19209
19321
  if (!this.trackingEnabled || !this.initialized) return [];
19210
19322
  const modified = [];
@@ -21297,6 +21409,14 @@ var ScheduleCore = class _ScheduleCore {
21297
21409
  (activityId) => this.coreRuntime.state.getActivity(activityId)
21298
21410
  ).map(cloneDomainValue);
21299
21411
  }
21412
+ getModifiedActivityFields() {
21413
+ this.assertReady();
21414
+ return cloneDomainValue(
21415
+ this._saveTracker.modifiedActivityFields(
21416
+ (activityId) => this.coreRuntime.state.getActivity(activityId)
21417
+ )
21418
+ );
21419
+ }
21300
21420
  getNewActivities() {
21301
21421
  this.assertReady();
21302
21422
  return this._saveTracker.newActivities(
@@ -21310,6 +21430,10 @@ var ScheduleCore = class _ScheduleCore {
21310
21430
  getScheduleRevision() {
21311
21431
  return this._scheduleRevision;
21312
21432
  }
21433
+ capturePersistenceHistoryBoundary() {
21434
+ this.assertReady();
21435
+ return this._undo.capturePersistenceBoundary();
21436
+ }
21313
21437
  getCanonicalDigest() {
21314
21438
  this.assertReady();
21315
21439
  const byId = (left, right) => String(left.id).localeCompare(String(right.id));
@@ -21480,12 +21604,29 @@ var ScheduleCore = class _ScheduleCore {
21480
21604
  );
21481
21605
  }
21482
21606
  if (result.ok) {
21483
- if (historyPolicy === "clear-on-success") {
21607
+ if (historyPolicy === "checkpoint-on-success") {
21608
+ if (action.kind !== "persistence-acknowledge") {
21609
+ throw new Error(
21610
+ "checkpoint-on-success requires persistence acknowledge"
21611
+ );
21612
+ }
21613
+ const currentActivities = this.coreRuntime.state.getAllActivities();
21614
+ const currentLinks = this.coreRuntime.state.getAllLinks();
21615
+ const checkpoint = action.checkpoint;
21616
+ const boundary = checkpoint?.historyBoundary ?? this._undo.capturePersistenceBoundary();
21484
21617
  this._saveTracker.acknowledge(
21485
- this.coreRuntime.state.getAllActivities(),
21486
- this.coreRuntime.state.getAllLinks()
21618
+ checkpoint?.activities ?? currentActivities,
21619
+ checkpoint?.links ?? currentLinks,
21620
+ action.activities,
21621
+ action.links,
21622
+ currentActivities,
21623
+ currentLinks
21624
+ );
21625
+ this._undo.discardThrough(boundary);
21626
+ this._undo.applyPersistedIdentities(
21627
+ action.activities ?? [],
21628
+ action.links ?? []
21487
21629
  );
21488
- this._undo.clear();
21489
21630
  } else if (action.kind === "activity-lookahead-sync" && action.acknowledgePersistedLookahead === true) {
21490
21631
  this._saveTracker.acknowledgePersistedLookahead(
21491
21632
  result.changes.activities,