@outbuild-company/schedule-core 1.6.1 → 1.6.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
@@ -204,6 +204,26 @@ interface EntityChange<T> {
204
204
  after: Readonly<T> | null;
205
205
  }
206
206
 
207
+ declare const EDITABLE: {
208
+ readonly TEXT: "text";
209
+ readonly DESCRIPTION: "description";
210
+ readonly DURATION: "duration";
211
+ readonly PROGRESS: "progress";
212
+ readonly COST: "cost";
213
+ readonly USED_COST: "used_cost";
214
+ readonly HH_WORK_TIME: "hhWorkTime";
215
+ readonly START_DATE: "start_date";
216
+ readonly END_DATE: "end_date";
217
+ readonly CONSTRAINT_TYPE: "constraint_type";
218
+ readonly CONSTRAINT_DATE: "constraint_date";
219
+ readonly CALENDAR_ID: "calendar_id";
220
+ readonly CUSTOM_ID: "custom_id";
221
+ readonly SUBCONTRACT_ID: "subcontractId";
222
+ readonly RESPONSABLES: "responsables";
223
+ readonly TAGS: "tags";
224
+ readonly CUSTOM_PREDECESSORS: "custom_predecessors";
225
+ readonly CUSTOM_SUCESSORS: "custom_sucessors";
226
+ };
207
227
  declare const COLUMN: {
208
228
  readonly REAL_WORK: "real_work";
209
229
  readonly REAL_COST: "real_cost";
@@ -245,8 +265,11 @@ declare const COLUMN: {
245
265
  readonly CUSTOM_PREDECESSORS: "custom_predecessors";
246
266
  readonly CUSTOM_SUCESSORS: "custom_sucessors";
247
267
  };
268
+ type EditableColumnName = (typeof EDITABLE)[keyof typeof EDITABLE];
248
269
  type ColumnName = (typeof COLUMN)[keyof typeof COLUMN];
249
270
 
271
+ type PipelineColumnName = Exclude<(typeof EDITABLE)[keyof typeof EDITABLE], typeof COLUMN.CUSTOM_PREDECESSORS | typeof COLUMN.CUSTOM_SUCESSORS>;
272
+
250
273
  declare const CREATION_OVERRIDE_FIELD_LIST: readonly ["name", "description", "type", "startDate", "durationHours", "constraintType", "constraintDate", "calendarId", "assignedDefaultCalendar", "autoScheduling", "progress", "cost", "usedCost", "realCost", "workHours", "realWorkHours", "ponderator", "hasCustomPonderator", "customId", "subcontractId", "responsableIds", "tagIds", "isLookahead"];
251
274
  type CreationOverrideField = (typeof CREATION_OVERRIDE_FIELD_LIST)[number];
252
275
  type CreationOverrides = Partial<Pick<CoreActivity, CreationOverrideField>>;
@@ -322,6 +345,32 @@ interface PersistedEntityIdentity {
322
345
  readonly id: string;
323
346
  readonly proplannerId: number;
324
347
  }
348
+ type ActivityBatchMode = 'append' | 'replace';
349
+ type ActivityBatchReference = ActivityId | {
350
+ readonly batchId: string;
351
+ };
352
+ type ActivityBatchParentReference = '0' | ActivityBatchReference;
353
+ interface ActivityBatchItem {
354
+ readonly batchId: string;
355
+ readonly parent: ActivityBatchParentReference;
356
+ readonly index?: number;
357
+ readonly activityId?: ActivityId;
358
+ readonly overrides?: CreationOverrides;
359
+ }
360
+ interface ActivityBatchLinkInput {
361
+ readonly source: ActivityBatchReference;
362
+ readonly target: ActivityBatchReference;
363
+ readonly type: LinkType;
364
+ readonly lag: number;
365
+ readonly linkId?: LinkId;
366
+ }
367
+ interface ActivityBatchAction {
368
+ readonly kind: 'activity-batch';
369
+ readonly mode: ActivityBatchMode;
370
+ readonly items: ReadonlyArray<ActivityBatchItem>;
371
+ readonly links: ReadonlyArray<ActivityBatchLinkInput>;
372
+ readonly eventSource?: string;
373
+ }
325
374
 
326
375
  type DispatchAction = {
327
376
  kind: 'persistence-acknowledge';
@@ -339,7 +388,7 @@ type DispatchAction = {
339
388
  } | {
340
389
  kind: 'inline-edit';
341
390
  activityId: ActivityId;
342
- column: string;
391
+ column: EditableColumnName;
343
392
  newValue: unknown;
344
393
  } | {
345
394
  kind: 'dates-batch';
@@ -352,7 +401,7 @@ type DispatchAction = {
352
401
  kind: 'bulk-edit';
353
402
  edits: ReadonlyArray<{
354
403
  activityId: ActivityId;
355
- column: string;
404
+ column: PipelineColumnName;
356
405
  newValue: unknown;
357
406
  }>;
358
407
  eventSource?: string;
@@ -441,6 +490,7 @@ type DispatchAction = {
441
490
  activityIds: ReadonlyArray<ActivityId>;
442
491
  isLookahead: boolean;
443
492
  hasLookaheadTasks: boolean;
493
+ acknowledgePersistedLookahead?: boolean;
444
494
  eventSource?: string;
445
495
  } | {
446
496
  kind: 'activity-paste';
@@ -455,6 +505,7 @@ type DispatchAction = {
455
505
  links: ReadonlyArray<PastedLinkInput>;
456
506
  eventSource?: string;
457
507
  };
508
+ type InternalDispatchAction = DispatchAction | ActivityBatchAction;
458
509
  interface PastedActivityInput {
459
510
  readonly originalId: ActivityId;
460
511
  readonly originalParentId: ActivityId | '0';
@@ -533,14 +584,15 @@ interface ViewStateChange {
533
584
  readonly checked?: ViewStatePropChange;
534
585
  readonly visible?: ViewStatePropChange;
535
586
  }
587
+ type ChangeSetSource = InternalDispatchAction | {
588
+ kind: 'init';
589
+ } | {
590
+ kind: 'undo';
591
+ } | {
592
+ kind: 'redo';
593
+ };
536
594
  interface ChangeSet {
537
- source: DispatchAction | {
538
- kind: 'init';
539
- } | {
540
- kind: 'undo';
541
- } | {
542
- kind: 'redo';
543
- };
595
+ source: ChangeSetSource;
544
596
  activities: ReadonlyArray<EntityChange<CoreActivity>>;
545
597
  links: ReadonlyArray<EntityChange<Link>>;
546
598
  calendars: ReadonlyArray<EntityChange<Calendar>>;
@@ -619,7 +671,6 @@ interface BackendActivityInput {
619
671
  has_childs: boolean;
620
672
  isOnLookahead: boolean;
621
673
  sumOfDurationRecursively: number | null;
622
- freeSlack: number | null;
623
674
  is_critical: boolean;
624
675
  newActivitiesArray: string | (string | number)[] | null;
625
676
  hasNewActivities: boolean | null;
@@ -684,6 +735,11 @@ interface ScheduleCoreReporter {
684
735
  }
685
736
 
686
737
  type ScheduleCoreStatus = 'ready' | 'destroyed' | 'poisoned';
738
+ declare const SCHEDULE_CORE_STATUS: {
739
+ readonly READY: "ready";
740
+ readonly DESTROYED: "destroyed";
741
+ readonly POISONED: "poisoned";
742
+ };
687
743
  interface ScheduleCoreInput extends BackendScheduleInput {
688
744
  skipInitialAutoSchedule?: boolean;
689
745
  baselinePoints?: readonly BaselinePoint[];
@@ -693,12 +749,56 @@ interface ScheduleCoreInput extends BackendScheduleInput {
693
749
  reporter?: ScheduleCoreReporter;
694
750
  }
695
751
 
752
+ type DateGesture = {
753
+ readonly kind: 'move';
754
+ readonly activityIds: readonly ActivityId[];
755
+ readonly anchorActivityId: ActivityId;
756
+ } | {
757
+ readonly kind: 'resize-start' | 'resize-end';
758
+ readonly activityId: ActivityId;
759
+ };
760
+ interface DateGesturePreview {
761
+ getDates(activityId: ActivityId, proposedEdgeTimeMs: number): {
762
+ startTimeMs: number;
763
+ endTimeMs: number;
764
+ } | null;
765
+ }
766
+
767
+ interface UndoOptions {
768
+ readonly skipCriticalPath?: boolean;
769
+ readonly expectedUndoDepth?: number;
770
+ }
771
+
696
772
  declare class ScheduleCore {
697
773
  private _status;
698
774
  private readonly coreRuntime;
699
775
  private readonly _undo;
700
776
  private _opQueue;
701
777
  private _scheduleRevision;
778
+ /**
779
+ * Revision counting mutations that have been ADMITTED, whether or not they
780
+ * have changed anything yet. `_scheduleRevision` counts the ones that
781
+ * actually did.
782
+ *
783
+ * INVARIANT: `_pendingScheduleRevision >= _scheduleRevision`, and equality
784
+ * means no mutation is in flight.
785
+ *
786
+ * The two exist separately because the useful instant and the knowable
787
+ * instant are not the same one. A critical-path calculation becomes garbage
788
+ * the moment the next mutation is admitted, but whether that mutation is
789
+ * substantive is only knowable once its ChangeSet exists, which is hundreds
790
+ * of milliseconds later at project scale. Measured live on a 12268-activity
791
+ * project: the cancellation flag flipped 929 ms into a 930 ms calculation,
792
+ * 812 into 812 and 785 into 785, because the job's apply block queues behind
793
+ * the very dispatch whose completion cancels it. Cancellation and job end
794
+ * were the same event, so every discarded job ran the whole calculation.
795
+ *
796
+ * Splitting the counter buys the early signal without moving the meaning of
797
+ * `_scheduleRevision`, whose three readers (`isCriticalPathSettled`,
798
+ * `createDateGesturePreview` and the job's own `isCurrent`) are all written
799
+ * against "the state actually changed".
800
+ */
801
+ private _pendingScheduleRevision;
702
802
  private _criticalPathRevision;
703
803
  private _activeCriticalPath;
704
804
  constructor(input: ScheduleCoreInput);
@@ -709,6 +809,7 @@ declare class ScheduleCore {
709
809
  get status(): ScheduleCoreStatus;
710
810
  getSector(): Readonly<SectorMetadata>;
711
811
  getActivityView(id: ActivityId): Readonly<CoreActivity> | null;
812
+ createDateGesturePreview(gesture: DateGesture): DateGesturePreview;
712
813
  getAllActivitiesView(): ReadonlyArray<Readonly<CoreActivity>>;
713
814
  getChildrenView(parentId: ActivityId | '0'): ReadonlyArray<Readonly<CoreActivity>>;
714
815
  getLinkView(id: LinkId): Readonly<Link> | null;
@@ -734,8 +835,51 @@ declare class ScheduleCore {
734
835
  getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
735
836
  getDeletedLinksSinceLastSave(): ReadonlyArray<Readonly<DeletedLinkSnapshot>>;
736
837
  private _enqueue;
838
+ /**
839
+ * Marks a mutation as admitted and invalidates the calculation in flight.
840
+ *
841
+ * Called BEFORE `_enqueue`, which is the point of the whole thing: the wait
842
+ * in the operation queue is part of the window a running calculation wastes,
843
+ * and it is the longest part of it when several mutations are already
844
+ * queued.
845
+ *
846
+ * `dispatchChangesSchedulingState` is a pure function of the action, so this
847
+ * decision needs no state and cannot be wrong about the action's nature. What
848
+ * it cannot know yet is whether the mutation will produce anything, which is
849
+ * what `_settleScheduleMutation` reconciles afterwards.
850
+ */
851
+ private _beginScheduleMutation;
852
+ /** Undo and redo have no action to classify: reaching them IS the mutation. */
853
+ private _admitScheduleMutation;
854
+ /**
855
+ * Closes a mutation admitted by `_beginScheduleMutation`, on EVERY exit path.
856
+ *
857
+ * `changedState` false means the mutation was admitted and produced nothing
858
+ * (rejected, non-substantive, or rolled back). The pending revision walks
859
+ * back so the invariant holds and `isCriticalPathSettled` keeps telling the
860
+ * truth, and the calculation this mutation killed for nothing is re-armed.
861
+ *
862
+ * The re-arm goes through the queue and that is not incidental. Started
863
+ * outside it, the calculation would run immediately while the next queued
864
+ * mutation is still waiting, and that mutation would kill it again on
865
+ * admission: a cancel-and-restart treadmill burning a fresh project-wide deep
866
+ * copy per lap. Inside the queue the restart cannot happen until the queue
867
+ * drains, so at most one calculation is armed per settled mutation. The burst
868
+ * scenario will NOT catch a regression here, because there every dispatch
869
+ * succeeds and this path is never taken.
870
+ *
871
+ * It re-arms through `recomputeCriticalPath` rather than enqueueing the job
872
+ * directly, because the queue slot must NOT await the job: the job's own
873
+ * apply block needs a later slot on this same queue, so awaiting it from
874
+ * inside a slot deadlocks the core. `recomputeCriticalPath` already has the
875
+ * shape that returns the job's promise out of the slot instead of awaiting
876
+ * it, and it keeps `_criticalPathReady` pointing at the live calculation.
877
+ */
878
+ private _settleScheduleMutation;
737
879
  dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
880
+ applyActivityBatch(action: ActivityBatchAction, options?: DispatchOptions): Promise<DispatchResult>;
738
881
  private _dispatchInner;
882
+ private _runDispatch;
739
883
  recomputeCriticalPath(): Promise<{
740
884
  changes: ChangeSet;
741
885
  } | null>;
@@ -791,13 +935,12 @@ declare class ScheduleCore {
791
935
  private _recordScheduleMutation;
792
936
  private _startCriticalPathForCurrentRevision;
793
937
  private _runCriticalPathAndCapture;
794
- private _recomputeAfterHistoryRestore;
795
- undo(options?: {
796
- skipCriticalPath?: boolean;
797
- }): Promise<ChangeSet | null>;
938
+ undo(options?: UndoOptions): Promise<ChangeSet | null>;
939
+ private _runUndo;
798
940
  redo(options?: {
799
941
  skipCriticalPath?: boolean;
800
942
  }): Promise<ChangeSet | null>;
943
+ private _runRedo;
801
944
  canUndo(): boolean;
802
945
  canRedo(): boolean;
803
946
  clearHistory(): void;
@@ -812,16 +955,15 @@ declare class ScheduleCore {
812
955
  private _rollback;
813
956
  }
814
957
 
815
- interface ViewStateSeed {
816
- readonly hiddenIds: readonly ActivityId[];
817
- }
818
958
  interface ParsedInput {
819
959
  sector: SectorMetadata;
820
960
  activities: CoreActivity[];
821
961
  links: Link[];
822
962
  calendars: Calendar[];
823
963
  baseCalendars: Calendar[];
824
- viewStateSeed: ViewStateSeed;
964
+ viewStateSeed: {
965
+ readonly hiddenIds: readonly ActivityId[];
966
+ };
825
967
  }
826
968
  declare function parseFromBackend(input: BackendScheduleInput, reporter?: ScheduleCoreReporter): ParsedInput;
827
969
 
@@ -955,4 +1097,4 @@ declare const DISPATCH_TRACK_EVENT: {
955
1097
  };
956
1098
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
957
1099
 
958
- export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
1100
+ export { ACTIVITY_TYPE, type ActivityBaselineSnapshot, type ActivityBatchAction, type ActivityBatchItem, type ActivityBatchLinkInput, type ActivityBatchMode, type ActivityBatchParentReference, type ActivityBatchReference, 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 CreationOverrides, 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 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 PastedActivityInput, type PastedLinkInput, 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 };
package/dist/index.d.ts CHANGED
@@ -204,6 +204,26 @@ interface EntityChange<T> {
204
204
  after: Readonly<T> | null;
205
205
  }
206
206
 
207
+ declare const EDITABLE: {
208
+ readonly TEXT: "text";
209
+ readonly DESCRIPTION: "description";
210
+ readonly DURATION: "duration";
211
+ readonly PROGRESS: "progress";
212
+ readonly COST: "cost";
213
+ readonly USED_COST: "used_cost";
214
+ readonly HH_WORK_TIME: "hhWorkTime";
215
+ readonly START_DATE: "start_date";
216
+ readonly END_DATE: "end_date";
217
+ readonly CONSTRAINT_TYPE: "constraint_type";
218
+ readonly CONSTRAINT_DATE: "constraint_date";
219
+ readonly CALENDAR_ID: "calendar_id";
220
+ readonly CUSTOM_ID: "custom_id";
221
+ readonly SUBCONTRACT_ID: "subcontractId";
222
+ readonly RESPONSABLES: "responsables";
223
+ readonly TAGS: "tags";
224
+ readonly CUSTOM_PREDECESSORS: "custom_predecessors";
225
+ readonly CUSTOM_SUCESSORS: "custom_sucessors";
226
+ };
207
227
  declare const COLUMN: {
208
228
  readonly REAL_WORK: "real_work";
209
229
  readonly REAL_COST: "real_cost";
@@ -245,8 +265,11 @@ declare const COLUMN: {
245
265
  readonly CUSTOM_PREDECESSORS: "custom_predecessors";
246
266
  readonly CUSTOM_SUCESSORS: "custom_sucessors";
247
267
  };
268
+ type EditableColumnName = (typeof EDITABLE)[keyof typeof EDITABLE];
248
269
  type ColumnName = (typeof COLUMN)[keyof typeof COLUMN];
249
270
 
271
+ type PipelineColumnName = Exclude<(typeof EDITABLE)[keyof typeof EDITABLE], typeof COLUMN.CUSTOM_PREDECESSORS | typeof COLUMN.CUSTOM_SUCESSORS>;
272
+
250
273
  declare const CREATION_OVERRIDE_FIELD_LIST: readonly ["name", "description", "type", "startDate", "durationHours", "constraintType", "constraintDate", "calendarId", "assignedDefaultCalendar", "autoScheduling", "progress", "cost", "usedCost", "realCost", "workHours", "realWorkHours", "ponderator", "hasCustomPonderator", "customId", "subcontractId", "responsableIds", "tagIds", "isLookahead"];
251
274
  type CreationOverrideField = (typeof CREATION_OVERRIDE_FIELD_LIST)[number];
252
275
  type CreationOverrides = Partial<Pick<CoreActivity, CreationOverrideField>>;
@@ -322,6 +345,32 @@ interface PersistedEntityIdentity {
322
345
  readonly id: string;
323
346
  readonly proplannerId: number;
324
347
  }
348
+ type ActivityBatchMode = 'append' | 'replace';
349
+ type ActivityBatchReference = ActivityId | {
350
+ readonly batchId: string;
351
+ };
352
+ type ActivityBatchParentReference = '0' | ActivityBatchReference;
353
+ interface ActivityBatchItem {
354
+ readonly batchId: string;
355
+ readonly parent: ActivityBatchParentReference;
356
+ readonly index?: number;
357
+ readonly activityId?: ActivityId;
358
+ readonly overrides?: CreationOverrides;
359
+ }
360
+ interface ActivityBatchLinkInput {
361
+ readonly source: ActivityBatchReference;
362
+ readonly target: ActivityBatchReference;
363
+ readonly type: LinkType;
364
+ readonly lag: number;
365
+ readonly linkId?: LinkId;
366
+ }
367
+ interface ActivityBatchAction {
368
+ readonly kind: 'activity-batch';
369
+ readonly mode: ActivityBatchMode;
370
+ readonly items: ReadonlyArray<ActivityBatchItem>;
371
+ readonly links: ReadonlyArray<ActivityBatchLinkInput>;
372
+ readonly eventSource?: string;
373
+ }
325
374
 
326
375
  type DispatchAction = {
327
376
  kind: 'persistence-acknowledge';
@@ -339,7 +388,7 @@ type DispatchAction = {
339
388
  } | {
340
389
  kind: 'inline-edit';
341
390
  activityId: ActivityId;
342
- column: string;
391
+ column: EditableColumnName;
343
392
  newValue: unknown;
344
393
  } | {
345
394
  kind: 'dates-batch';
@@ -352,7 +401,7 @@ type DispatchAction = {
352
401
  kind: 'bulk-edit';
353
402
  edits: ReadonlyArray<{
354
403
  activityId: ActivityId;
355
- column: string;
404
+ column: PipelineColumnName;
356
405
  newValue: unknown;
357
406
  }>;
358
407
  eventSource?: string;
@@ -441,6 +490,7 @@ type DispatchAction = {
441
490
  activityIds: ReadonlyArray<ActivityId>;
442
491
  isLookahead: boolean;
443
492
  hasLookaheadTasks: boolean;
493
+ acknowledgePersistedLookahead?: boolean;
444
494
  eventSource?: string;
445
495
  } | {
446
496
  kind: 'activity-paste';
@@ -455,6 +505,7 @@ type DispatchAction = {
455
505
  links: ReadonlyArray<PastedLinkInput>;
456
506
  eventSource?: string;
457
507
  };
508
+ type InternalDispatchAction = DispatchAction | ActivityBatchAction;
458
509
  interface PastedActivityInput {
459
510
  readonly originalId: ActivityId;
460
511
  readonly originalParentId: ActivityId | '0';
@@ -533,14 +584,15 @@ interface ViewStateChange {
533
584
  readonly checked?: ViewStatePropChange;
534
585
  readonly visible?: ViewStatePropChange;
535
586
  }
587
+ type ChangeSetSource = InternalDispatchAction | {
588
+ kind: 'init';
589
+ } | {
590
+ kind: 'undo';
591
+ } | {
592
+ kind: 'redo';
593
+ };
536
594
  interface ChangeSet {
537
- source: DispatchAction | {
538
- kind: 'init';
539
- } | {
540
- kind: 'undo';
541
- } | {
542
- kind: 'redo';
543
- };
595
+ source: ChangeSetSource;
544
596
  activities: ReadonlyArray<EntityChange<CoreActivity>>;
545
597
  links: ReadonlyArray<EntityChange<Link>>;
546
598
  calendars: ReadonlyArray<EntityChange<Calendar>>;
@@ -619,7 +671,6 @@ interface BackendActivityInput {
619
671
  has_childs: boolean;
620
672
  isOnLookahead: boolean;
621
673
  sumOfDurationRecursively: number | null;
622
- freeSlack: number | null;
623
674
  is_critical: boolean;
624
675
  newActivitiesArray: string | (string | number)[] | null;
625
676
  hasNewActivities: boolean | null;
@@ -684,6 +735,11 @@ interface ScheduleCoreReporter {
684
735
  }
685
736
 
686
737
  type ScheduleCoreStatus = 'ready' | 'destroyed' | 'poisoned';
738
+ declare const SCHEDULE_CORE_STATUS: {
739
+ readonly READY: "ready";
740
+ readonly DESTROYED: "destroyed";
741
+ readonly POISONED: "poisoned";
742
+ };
687
743
  interface ScheduleCoreInput extends BackendScheduleInput {
688
744
  skipInitialAutoSchedule?: boolean;
689
745
  baselinePoints?: readonly BaselinePoint[];
@@ -693,12 +749,56 @@ interface ScheduleCoreInput extends BackendScheduleInput {
693
749
  reporter?: ScheduleCoreReporter;
694
750
  }
695
751
 
752
+ type DateGesture = {
753
+ readonly kind: 'move';
754
+ readonly activityIds: readonly ActivityId[];
755
+ readonly anchorActivityId: ActivityId;
756
+ } | {
757
+ readonly kind: 'resize-start' | 'resize-end';
758
+ readonly activityId: ActivityId;
759
+ };
760
+ interface DateGesturePreview {
761
+ getDates(activityId: ActivityId, proposedEdgeTimeMs: number): {
762
+ startTimeMs: number;
763
+ endTimeMs: number;
764
+ } | null;
765
+ }
766
+
767
+ interface UndoOptions {
768
+ readonly skipCriticalPath?: boolean;
769
+ readonly expectedUndoDepth?: number;
770
+ }
771
+
696
772
  declare class ScheduleCore {
697
773
  private _status;
698
774
  private readonly coreRuntime;
699
775
  private readonly _undo;
700
776
  private _opQueue;
701
777
  private _scheduleRevision;
778
+ /**
779
+ * Revision counting mutations that have been ADMITTED, whether or not they
780
+ * have changed anything yet. `_scheduleRevision` counts the ones that
781
+ * actually did.
782
+ *
783
+ * INVARIANT: `_pendingScheduleRevision >= _scheduleRevision`, and equality
784
+ * means no mutation is in flight.
785
+ *
786
+ * The two exist separately because the useful instant and the knowable
787
+ * instant are not the same one. A critical-path calculation becomes garbage
788
+ * the moment the next mutation is admitted, but whether that mutation is
789
+ * substantive is only knowable once its ChangeSet exists, which is hundreds
790
+ * of milliseconds later at project scale. Measured live on a 12268-activity
791
+ * project: the cancellation flag flipped 929 ms into a 930 ms calculation,
792
+ * 812 into 812 and 785 into 785, because the job's apply block queues behind
793
+ * the very dispatch whose completion cancels it. Cancellation and job end
794
+ * were the same event, so every discarded job ran the whole calculation.
795
+ *
796
+ * Splitting the counter buys the early signal without moving the meaning of
797
+ * `_scheduleRevision`, whose three readers (`isCriticalPathSettled`,
798
+ * `createDateGesturePreview` and the job's own `isCurrent`) are all written
799
+ * against "the state actually changed".
800
+ */
801
+ private _pendingScheduleRevision;
702
802
  private _criticalPathRevision;
703
803
  private _activeCriticalPath;
704
804
  constructor(input: ScheduleCoreInput);
@@ -709,6 +809,7 @@ declare class ScheduleCore {
709
809
  get status(): ScheduleCoreStatus;
710
810
  getSector(): Readonly<SectorMetadata>;
711
811
  getActivityView(id: ActivityId): Readonly<CoreActivity> | null;
812
+ createDateGesturePreview(gesture: DateGesture): DateGesturePreview;
712
813
  getAllActivitiesView(): ReadonlyArray<Readonly<CoreActivity>>;
713
814
  getChildrenView(parentId: ActivityId | '0'): ReadonlyArray<Readonly<CoreActivity>>;
714
815
  getLinkView(id: LinkId): Readonly<Link> | null;
@@ -734,8 +835,51 @@ declare class ScheduleCore {
734
835
  getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
735
836
  getDeletedLinksSinceLastSave(): ReadonlyArray<Readonly<DeletedLinkSnapshot>>;
736
837
  private _enqueue;
838
+ /**
839
+ * Marks a mutation as admitted and invalidates the calculation in flight.
840
+ *
841
+ * Called BEFORE `_enqueue`, which is the point of the whole thing: the wait
842
+ * in the operation queue is part of the window a running calculation wastes,
843
+ * and it is the longest part of it when several mutations are already
844
+ * queued.
845
+ *
846
+ * `dispatchChangesSchedulingState` is a pure function of the action, so this
847
+ * decision needs no state and cannot be wrong about the action's nature. What
848
+ * it cannot know yet is whether the mutation will produce anything, which is
849
+ * what `_settleScheduleMutation` reconciles afterwards.
850
+ */
851
+ private _beginScheduleMutation;
852
+ /** Undo and redo have no action to classify: reaching them IS the mutation. */
853
+ private _admitScheduleMutation;
854
+ /**
855
+ * Closes a mutation admitted by `_beginScheduleMutation`, on EVERY exit path.
856
+ *
857
+ * `changedState` false means the mutation was admitted and produced nothing
858
+ * (rejected, non-substantive, or rolled back). The pending revision walks
859
+ * back so the invariant holds and `isCriticalPathSettled` keeps telling the
860
+ * truth, and the calculation this mutation killed for nothing is re-armed.
861
+ *
862
+ * The re-arm goes through the queue and that is not incidental. Started
863
+ * outside it, the calculation would run immediately while the next queued
864
+ * mutation is still waiting, and that mutation would kill it again on
865
+ * admission: a cancel-and-restart treadmill burning a fresh project-wide deep
866
+ * copy per lap. Inside the queue the restart cannot happen until the queue
867
+ * drains, so at most one calculation is armed per settled mutation. The burst
868
+ * scenario will NOT catch a regression here, because there every dispatch
869
+ * succeeds and this path is never taken.
870
+ *
871
+ * It re-arms through `recomputeCriticalPath` rather than enqueueing the job
872
+ * directly, because the queue slot must NOT await the job: the job's own
873
+ * apply block needs a later slot on this same queue, so awaiting it from
874
+ * inside a slot deadlocks the core. `recomputeCriticalPath` already has the
875
+ * shape that returns the job's promise out of the slot instead of awaiting
876
+ * it, and it keeps `_criticalPathReady` pointing at the live calculation.
877
+ */
878
+ private _settleScheduleMutation;
737
879
  dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
880
+ applyActivityBatch(action: ActivityBatchAction, options?: DispatchOptions): Promise<DispatchResult>;
738
881
  private _dispatchInner;
882
+ private _runDispatch;
739
883
  recomputeCriticalPath(): Promise<{
740
884
  changes: ChangeSet;
741
885
  } | null>;
@@ -791,13 +935,12 @@ declare class ScheduleCore {
791
935
  private _recordScheduleMutation;
792
936
  private _startCriticalPathForCurrentRevision;
793
937
  private _runCriticalPathAndCapture;
794
- private _recomputeAfterHistoryRestore;
795
- undo(options?: {
796
- skipCriticalPath?: boolean;
797
- }): Promise<ChangeSet | null>;
938
+ undo(options?: UndoOptions): Promise<ChangeSet | null>;
939
+ private _runUndo;
798
940
  redo(options?: {
799
941
  skipCriticalPath?: boolean;
800
942
  }): Promise<ChangeSet | null>;
943
+ private _runRedo;
801
944
  canUndo(): boolean;
802
945
  canRedo(): boolean;
803
946
  clearHistory(): void;
@@ -812,16 +955,15 @@ declare class ScheduleCore {
812
955
  private _rollback;
813
956
  }
814
957
 
815
- interface ViewStateSeed {
816
- readonly hiddenIds: readonly ActivityId[];
817
- }
818
958
  interface ParsedInput {
819
959
  sector: SectorMetadata;
820
960
  activities: CoreActivity[];
821
961
  links: Link[];
822
962
  calendars: Calendar[];
823
963
  baseCalendars: Calendar[];
824
- viewStateSeed: ViewStateSeed;
964
+ viewStateSeed: {
965
+ readonly hiddenIds: readonly ActivityId[];
966
+ };
825
967
  }
826
968
  declare function parseFromBackend(input: BackendScheduleInput, reporter?: ScheduleCoreReporter): ParsedInput;
827
969
 
@@ -955,4 +1097,4 @@ declare const DISPATCH_TRACK_EVENT: {
955
1097
  };
956
1098
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
957
1099
 
958
- export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
1100
+ export { ACTIVITY_TYPE, type ActivityBaselineSnapshot, type ActivityBatchAction, type ActivityBatchItem, type ActivityBatchLinkInput, type ActivityBatchMode, type ActivityBatchParentReference, type ActivityBatchReference, 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 CreationOverrides, 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 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 PastedActivityInput, type PastedLinkInput, 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 };