@outbuild-company/schedule-core 1.20.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/schedule-core.global.js +1 -1
- package/dist/cdn/schedule-core.global.js.map +1 -1
- package/dist/index.cjs +325 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +325 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -345,6 +345,7 @@ var DISPATCH_ACTION_KIND = {
|
|
|
345
345
|
ACTIVITY_COPY: "activity-copy",
|
|
346
346
|
CALENDAR_RECONCILE: "calendar-reconcile",
|
|
347
347
|
ACTIVITY_PASTE_COPIED: "activity-paste-copied",
|
|
348
|
+
ACTIVITY_DUPLICATE: "activity-duplicate",
|
|
348
349
|
PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge",
|
|
349
350
|
BASELINE_APPLY: "baseline-apply",
|
|
350
351
|
PONDERATOR_CRITERION_SET: "ponderator-criterion-set",
|
|
@@ -593,6 +594,11 @@ function createActivitiesApi(engine) {
|
|
|
593
594
|
kind: DISPATCH_ACTION_KIND.ACTIVITY_PASTE_COPIED,
|
|
594
595
|
destination: { afterSiblingId }
|
|
595
596
|
}),
|
|
597
|
+
duplicate: (activityIds, context = {}) => engine.dispatch({
|
|
598
|
+
...context,
|
|
599
|
+
kind: DISPATCH_ACTION_KIND.ACTIVITY_DUPLICATE,
|
|
600
|
+
activityIds
|
|
601
|
+
}),
|
|
596
602
|
clearClipboard: (context = {}) => engine.dispatch({
|
|
597
603
|
...context,
|
|
598
604
|
kind: DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR
|
|
@@ -919,6 +925,7 @@ var REJECTION_REASON = {
|
|
|
919
925
|
NO_ACTIVITY_SELECTION: "no_activity_selection",
|
|
920
926
|
CLIPBOARD_EMPTY: "clipboard_empty",
|
|
921
927
|
ACTIVITY_PASTE_INVARIANT_FAILED: "activity_paste_invariant_failed",
|
|
928
|
+
SOLE_SCHEDULE_ROOT: "sole_schedule_root",
|
|
922
929
|
ANCHOR_SIBLING_CONFLICT: "anchor_sibling_conflict",
|
|
923
930
|
ANCHOR_SIBLING_NOT_FOUND: "anchor_sibling_not_found",
|
|
924
931
|
ANCHOR_SIBLING_WRONG_PARENT: "anchor_sibling_wrong_parent",
|
|
@@ -11935,12 +11942,34 @@ function draftRemainingDuration(activity) {
|
|
|
11935
11942
|
}
|
|
11936
11943
|
|
|
11937
11944
|
// src/p6/inactive-drafts.ts
|
|
11938
|
-
function
|
|
11945
|
+
function trackInactiveDataDateDrafts(state, input) {
|
|
11946
|
+
const p6Input = input ?? void 0;
|
|
11947
|
+
const beforeInitialPasses = snapshotInactiveDataDateDrafts(state, p6Input);
|
|
11948
|
+
return () => refreshInactiveDataDateDrafts(state, p6Input, beforeInitialPasses);
|
|
11949
|
+
}
|
|
11950
|
+
function snapshotInactiveDataDateDrafts(state, input) {
|
|
11951
|
+
if (input === void 0 || hasActiveDataDate(input.settings)) {
|
|
11952
|
+
return /* @__PURE__ */ new Map();
|
|
11953
|
+
}
|
|
11954
|
+
return new Map(
|
|
11955
|
+
state.getAllActivities().filter(
|
|
11956
|
+
(activity) => activity.p6 !== void 0 && Object.hasOwn(input.activities, activity.id)
|
|
11957
|
+
).map((activity) => [activity.id, structuredCloneActivity(activity)])
|
|
11958
|
+
);
|
|
11959
|
+
}
|
|
11960
|
+
function refreshInactiveDataDateDrafts(state, input, beforeInitialPasses) {
|
|
11939
11961
|
if (input === void 0 || hasActiveDataDate(input.settings)) return;
|
|
11940
11962
|
for (const activity of state.getAllActivities()) {
|
|
11941
|
-
if (Object.hasOwn(input.activities, activity.id))
|
|
11942
|
-
|
|
11943
|
-
|
|
11963
|
+
if (!Object.hasOwn(input.activities, activity.id)) {
|
|
11964
|
+
const p6 = createOutbuildP6ActivityDraft(activity);
|
|
11965
|
+
if (p6 !== void 0) state.setActivityField(activity.id, "p6", p6);
|
|
11966
|
+
continue;
|
|
11967
|
+
}
|
|
11968
|
+
const before = beforeInitialPasses.get(activity.id);
|
|
11969
|
+
if (before?.p6 === void 0 || activity.p6 === void 0) continue;
|
|
11970
|
+
const patch = ordinaryInputChanges(before, activity);
|
|
11971
|
+
if (Object.keys(patch).length === 0) continue;
|
|
11972
|
+
state.setActivityField(activity.id, "p6", { ...activity.p6, ...patch });
|
|
11944
11973
|
}
|
|
11945
11974
|
}
|
|
11946
11975
|
function synchronizeInactiveDataDateDrafts(state) {
|
|
@@ -18460,6 +18489,293 @@ async function dispatchActivityPasteCopied(action, options, deps) {
|
|
|
18460
18489
|
};
|
|
18461
18490
|
}
|
|
18462
18491
|
|
|
18492
|
+
// src/activity-copy-paste/duplicate.ts
|
|
18493
|
+
var CLONE_YIELD_EVERY_N = 200;
|
|
18494
|
+
var CREATE_OPTIONS = {
|
|
18495
|
+
skipCorrelativeRecompute: true,
|
|
18496
|
+
skipBeforeSnap: true,
|
|
18497
|
+
skipCacheInvalidation: true,
|
|
18498
|
+
skipRollupCascades: true
|
|
18499
|
+
};
|
|
18500
|
+
async function dispatchActivityDuplicate(action, options, deps) {
|
|
18501
|
+
const resolved = resolveDuplicateRuns(action.activityIds, deps.adapter);
|
|
18502
|
+
if (!resolved.ok) return resolved;
|
|
18503
|
+
const frozenParent = firstFrozenRunParent(resolved.runs, deps.adapter);
|
|
18504
|
+
if (frozenParent) {
|
|
18505
|
+
return { ok: false, reason: REJECTION_REASON.PARENT_FROZEN_BY_SIR };
|
|
18506
|
+
}
|
|
18507
|
+
deps.scheduler.invalidateAllCaches();
|
|
18508
|
+
const beforeSnapshot = await snapshotActivities(
|
|
18509
|
+
deps.adapter,
|
|
18510
|
+
/* @__PURE__ */ new Set()
|
|
18511
|
+
);
|
|
18512
|
+
const state = {
|
|
18513
|
+
originalToNew: /* @__PURE__ */ new Map(),
|
|
18514
|
+
createdIds: [],
|
|
18515
|
+
createdParentIds: []
|
|
18516
|
+
};
|
|
18517
|
+
for (const run of resolved.runs) {
|
|
18518
|
+
const lastOriginal = run.originals.at(-1);
|
|
18519
|
+
if (!lastOriginal) continue;
|
|
18520
|
+
let anchorId = lastOriginal.id;
|
|
18521
|
+
for (const original of run.originals) {
|
|
18522
|
+
anchorId = await cloneSubtree(
|
|
18523
|
+
original,
|
|
18524
|
+
run.parentId,
|
|
18525
|
+
anchorId,
|
|
18526
|
+
action,
|
|
18527
|
+
deps,
|
|
18528
|
+
state
|
|
18529
|
+
);
|
|
18530
|
+
}
|
|
18531
|
+
}
|
|
18532
|
+
recomputeCreatedParentRollups(state, deps);
|
|
18533
|
+
const correlativeShifts = recomputeCorrelativeIds(deps.adapter);
|
|
18534
|
+
const createdIdSet = new Set(state.createdIds.map(String));
|
|
18535
|
+
const touched = /* @__PURE__ */ new Set();
|
|
18536
|
+
seedJournalBeforeImages(
|
|
18537
|
+
deps.adapter,
|
|
18538
|
+
correlativeShifts,
|
|
18539
|
+
createdIdSet,
|
|
18540
|
+
beforeSnapshot
|
|
18541
|
+
);
|
|
18542
|
+
foldCorrelativeShifts(
|
|
18543
|
+
deps.adapter,
|
|
18544
|
+
correlativeShifts,
|
|
18545
|
+
createdIdSet,
|
|
18546
|
+
beforeSnapshot,
|
|
18547
|
+
touched
|
|
18548
|
+
);
|
|
18549
|
+
for (const createdId of state.createdIds) touched.add(String(createdId));
|
|
18550
|
+
const applied = replicateInternalLinks(state, deps);
|
|
18551
|
+
const { scheduledIds } = await runPostMutation(
|
|
18552
|
+
{
|
|
18553
|
+
adapter: deps.adapter,
|
|
18554
|
+
scheduler: deps.scheduler,
|
|
18555
|
+
sector: deps.sector,
|
|
18556
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
18557
|
+
},
|
|
18558
|
+
{
|
|
18559
|
+
action,
|
|
18560
|
+
autoscheduleFrom: state.createdIds.length > 0 ? AUTOSCHEDULE_SCOPE.ROOTS : null,
|
|
18561
|
+
recomputeParentsFrom: state.createdIds,
|
|
18562
|
+
preventSummaryTargetPullback: true,
|
|
18563
|
+
now: deps.now,
|
|
18564
|
+
options
|
|
18565
|
+
}
|
|
18566
|
+
);
|
|
18567
|
+
return {
|
|
18568
|
+
ok: true,
|
|
18569
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
18570
|
+
source: action,
|
|
18571
|
+
beforeSnap: beforeSnapshot,
|
|
18572
|
+
touchedIds: touched,
|
|
18573
|
+
scheduledIds,
|
|
18574
|
+
links: buildLinkChangesForBatch(
|
|
18575
|
+
applied,
|
|
18576
|
+
/* @__PURE__ */ new Map(),
|
|
18577
|
+
deps.adapter
|
|
18578
|
+
),
|
|
18579
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
18580
|
+
})
|
|
18581
|
+
};
|
|
18582
|
+
}
|
|
18583
|
+
function resolveDuplicateRuns(activityIds, adapter) {
|
|
18584
|
+
if (activityIds.length === 0) {
|
|
18585
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_IDS_EMPTY };
|
|
18586
|
+
}
|
|
18587
|
+
const requested = /* @__PURE__ */ new Set();
|
|
18588
|
+
const originals = [];
|
|
18589
|
+
for (const rawId of activityIds) {
|
|
18590
|
+
const activityId = String(rawId);
|
|
18591
|
+
if (requested.has(activityId)) continue;
|
|
18592
|
+
const activity = adapter.getActivity(activityId);
|
|
18593
|
+
if (!activity) {
|
|
18594
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
18595
|
+
}
|
|
18596
|
+
requested.add(activityId);
|
|
18597
|
+
originals.push(activity);
|
|
18598
|
+
}
|
|
18599
|
+
const roots = originals.filter(
|
|
18600
|
+
(activity) => !hasAncestorInSet(activity.id, requested, adapter)
|
|
18601
|
+
);
|
|
18602
|
+
const duplicatesSoleScheduleRoot = adapter.getRootIds().length === 1 && roots.some((activity) => activity.parentId == null);
|
|
18603
|
+
if (duplicatesSoleScheduleRoot) {
|
|
18604
|
+
return { ok: false, reason: REJECTION_REASON.SOLE_SCHEDULE_ROOT };
|
|
18605
|
+
}
|
|
18606
|
+
return { ok: true, runs: groupIntoContiguousRuns(roots, adapter) };
|
|
18607
|
+
}
|
|
18608
|
+
function firstFrozenRunParent(runs, adapter) {
|
|
18609
|
+
const checked = /* @__PURE__ */ new Set();
|
|
18610
|
+
for (const run of runs) {
|
|
18611
|
+
if (run.parentId === ROOT_PARENT_ID || checked.has(String(run.parentId))) {
|
|
18612
|
+
continue;
|
|
18613
|
+
}
|
|
18614
|
+
checked.add(String(run.parentId));
|
|
18615
|
+
const parent = adapter.getActivity(run.parentId);
|
|
18616
|
+
if (parent && isFrozen(parent)) return parent;
|
|
18617
|
+
}
|
|
18618
|
+
return null;
|
|
18619
|
+
}
|
|
18620
|
+
function groupIntoContiguousRuns(roots, adapter) {
|
|
18621
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
18622
|
+
for (const activity of roots) {
|
|
18623
|
+
const parentKey = normalizeParentKey(activity.parentId);
|
|
18624
|
+
const group = byParent.get(parentKey) ?? [];
|
|
18625
|
+
group.push(activity);
|
|
18626
|
+
byParent.set(parentKey, group);
|
|
18627
|
+
}
|
|
18628
|
+
const runs = [];
|
|
18629
|
+
for (const [parentKey, group] of byParent) {
|
|
18630
|
+
const parentId = parentKey === ROOT_PARENT_ID ? ROOT_PARENT_ID : parentKey;
|
|
18631
|
+
const positions = new Map(
|
|
18632
|
+
childrenInCorrelativeOrder(adapter, parentKey).map(
|
|
18633
|
+
(sibling, index) => [String(sibling.id), index]
|
|
18634
|
+
)
|
|
18635
|
+
);
|
|
18636
|
+
const positionOf = (activity) => positions.get(String(activity.id)) ?? -1;
|
|
18637
|
+
const ordered = [...group].sort(
|
|
18638
|
+
(left, right) => positionOf(left) - positionOf(right)
|
|
18639
|
+
);
|
|
18640
|
+
for (const originals of splitContiguous(ordered, positionOf)) {
|
|
18641
|
+
runs.push({ parentId, originals });
|
|
18642
|
+
}
|
|
18643
|
+
}
|
|
18644
|
+
return runs;
|
|
18645
|
+
}
|
|
18646
|
+
function splitContiguous(ordered, positionOf) {
|
|
18647
|
+
const runs = [];
|
|
18648
|
+
let previousPosition = null;
|
|
18649
|
+
for (const activity of ordered) {
|
|
18650
|
+
const position = positionOf(activity);
|
|
18651
|
+
const lastRun = runs.at(-1);
|
|
18652
|
+
if (lastRun && previousPosition !== null && position === previousPosition + 1) {
|
|
18653
|
+
lastRun.push(activity);
|
|
18654
|
+
} else {
|
|
18655
|
+
runs.push([activity]);
|
|
18656
|
+
}
|
|
18657
|
+
previousPosition = position;
|
|
18658
|
+
}
|
|
18659
|
+
return runs;
|
|
18660
|
+
}
|
|
18661
|
+
function seedJournalBeforeImages(adapter, shifts, excludeIds, beforeSnap) {
|
|
18662
|
+
const journal = adapter.peekWriteCapture();
|
|
18663
|
+
if (!journal) return;
|
|
18664
|
+
for (const shift of shifts) {
|
|
18665
|
+
if (excludeIds.has(shift.activityId) || beforeSnap.has(shift.activityId)) {
|
|
18666
|
+
continue;
|
|
18667
|
+
}
|
|
18668
|
+
const beforeImage = journal.before.get(shift.activityId);
|
|
18669
|
+
if (beforeImage) beforeSnap.set(shift.activityId, beforeImage);
|
|
18670
|
+
}
|
|
18671
|
+
}
|
|
18672
|
+
function childrenInCorrelativeOrder(adapter, parentKey) {
|
|
18673
|
+
return collectSiblings(adapter, parentKey).sort((left, right) => {
|
|
18674
|
+
const diff = readCorrelativeId(left) - readCorrelativeId(right);
|
|
18675
|
+
return diff || String(left.id).localeCompare(String(right.id));
|
|
18676
|
+
});
|
|
18677
|
+
}
|
|
18678
|
+
async function cloneSubtree(original, parentId, anchorId, action, deps, state) {
|
|
18679
|
+
const root = await createActivityCore(
|
|
18680
|
+
{
|
|
18681
|
+
kind: DISPATCH_ACTION_KIND.ACTIVITY_CREATE,
|
|
18682
|
+
parentId,
|
|
18683
|
+
afterSiblingId: anchorId,
|
|
18684
|
+
overrides: copyOverrides(original),
|
|
18685
|
+
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
18686
|
+
},
|
|
18687
|
+
deps,
|
|
18688
|
+
{ ...CREATE_OPTIONS, customIdReferenceId: original.id }
|
|
18689
|
+
);
|
|
18690
|
+
if (!root.ok) throw invariantViolation("create", root.reason);
|
|
18691
|
+
registerCreated(state, original, root.newId, parentId);
|
|
18692
|
+
await cloneDescendantsIteratively(original, root.newId, action, deps, state);
|
|
18693
|
+
return root.newId;
|
|
18694
|
+
}
|
|
18695
|
+
async function cloneDescendantsIteratively(rootOriginal, rootNewParentId, action, deps, state) {
|
|
18696
|
+
const pending = new Queue();
|
|
18697
|
+
pending.enqueue({ original: rootOriginal, newParentId: rootNewParentId });
|
|
18698
|
+
let processed = 0;
|
|
18699
|
+
while (!pending.isEmpty()) {
|
|
18700
|
+
const { original, newParentId } = pending.dequeue();
|
|
18701
|
+
for (const child of childrenInCorrelativeOrder(
|
|
18702
|
+
deps.adapter,
|
|
18703
|
+
String(original.id)
|
|
18704
|
+
)) {
|
|
18705
|
+
const created = await createActivityCore(
|
|
18706
|
+
{
|
|
18707
|
+
kind: DISPATCH_ACTION_KIND.ACTIVITY_CREATE,
|
|
18708
|
+
parentId: newParentId,
|
|
18709
|
+
overrides: copyOverrides(child),
|
|
18710
|
+
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
18711
|
+
},
|
|
18712
|
+
deps,
|
|
18713
|
+
CREATE_OPTIONS
|
|
18714
|
+
);
|
|
18715
|
+
if (!created.ok) throw invariantViolation("create", created.reason);
|
|
18716
|
+
registerCreated(state, child, created.newId, newParentId);
|
|
18717
|
+
pending.enqueue({ original: child, newParentId: created.newId });
|
|
18718
|
+
processed += 1;
|
|
18719
|
+
if (processed % CLONE_YIELD_EVERY_N === 0) await yieldToBrowser();
|
|
18720
|
+
}
|
|
18721
|
+
}
|
|
18722
|
+
}
|
|
18723
|
+
function invariantViolation(step, reason) {
|
|
18724
|
+
return new Error(
|
|
18725
|
+
`[ScheduleCore] activity-duplicate: unexpected ${step} rejection after preflight passed (${reason})`
|
|
18726
|
+
);
|
|
18727
|
+
}
|
|
18728
|
+
function registerCreated(state, original, newId, parentId) {
|
|
18729
|
+
state.originalToNew.set(String(original.id), newId);
|
|
18730
|
+
state.createdIds.push(newId);
|
|
18731
|
+
state.createdParentIds.push(parentId);
|
|
18732
|
+
}
|
|
18733
|
+
function recomputeCreatedParentRollups(state, deps) {
|
|
18734
|
+
const recomputed = /* @__PURE__ */ new Set();
|
|
18735
|
+
for (const parentId of [...state.createdParentIds].reverse()) {
|
|
18736
|
+
if (parentId === ROOT_PARENT_ID || recomputed.has(String(parentId))) {
|
|
18737
|
+
continue;
|
|
18738
|
+
}
|
|
18739
|
+
recomputed.add(String(parentId));
|
|
18740
|
+
recomputeRollupCascadesForParent(parentId, deps.adapter);
|
|
18741
|
+
}
|
|
18742
|
+
}
|
|
18743
|
+
function replicateInternalLinks(state, deps) {
|
|
18744
|
+
const applied = [];
|
|
18745
|
+
for (const originalId of state.originalToNew.keys()) {
|
|
18746
|
+
for (const linkId of deps.adapter.getOutgoingLinkIds(originalId)) {
|
|
18747
|
+
const link = deps.adapter.getLink(linkId);
|
|
18748
|
+
if (!link) continue;
|
|
18749
|
+
const source = state.originalToNew.get(String(link.source));
|
|
18750
|
+
const target = state.originalToNew.get(String(link.target));
|
|
18751
|
+
if (source === void 0 || target === void 0) continue;
|
|
18752
|
+
const operation = {
|
|
18753
|
+
kind: LINK_OPERATION_KIND.CREATE,
|
|
18754
|
+
source,
|
|
18755
|
+
target,
|
|
18756
|
+
type: link.type,
|
|
18757
|
+
lag: link.lag
|
|
18758
|
+
};
|
|
18759
|
+
const result = applyLinkOperation(operation, {
|
|
18760
|
+
port: deps.adapter,
|
|
18761
|
+
newLinkId: deps.linkIdGen.next
|
|
18762
|
+
});
|
|
18763
|
+
if (!result.applied) {
|
|
18764
|
+
throw invariantViolation(
|
|
18765
|
+
"link",
|
|
18766
|
+
result.rejected ?? REJECTION_REASON.INVALID
|
|
18767
|
+
);
|
|
18768
|
+
}
|
|
18769
|
+
applied.push({
|
|
18770
|
+
op: operation,
|
|
18771
|
+
finalLinkId: result.linkId,
|
|
18772
|
+
rejected: null
|
|
18773
|
+
});
|
|
18774
|
+
}
|
|
18775
|
+
}
|
|
18776
|
+
return applied;
|
|
18777
|
+
}
|
|
18778
|
+
|
|
18463
18779
|
// src/dispatch/dispatch.ts
|
|
18464
18780
|
async function performDispatch(action, options, deps) {
|
|
18465
18781
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_COPY) {
|
|
@@ -18477,6 +18793,9 @@ async function performDispatch(action, options, deps) {
|
|
|
18477
18793
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_PASTE_COPIED) {
|
|
18478
18794
|
return dispatchActivityPasteCopied(action, options, deps);
|
|
18479
18795
|
}
|
|
18796
|
+
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_DUPLICATE) {
|
|
18797
|
+
return dispatchActivityDuplicate(action, options, deps);
|
|
18798
|
+
}
|
|
18480
18799
|
if (action.kind === ACTIVITY_BATCH_ACTION_KIND) {
|
|
18481
18800
|
return dispatchActivityBatch(action, options, deps);
|
|
18482
18801
|
}
|
|
@@ -29781,13 +30100,14 @@ function initializeCore(input) {
|
|
|
29781
30100
|
loadDiagnostics.record
|
|
29782
30101
|
);
|
|
29783
30102
|
}
|
|
30103
|
+
const settleInactiveDrafts = trackInactiveDataDateDrafts(state, input.p6);
|
|
29784
30104
|
const initPromise = runInitialPasses({
|
|
29785
30105
|
state,
|
|
29786
30106
|
sector,
|
|
29787
30107
|
scheduler,
|
|
29788
30108
|
skipAutoSchedule: input.skipInitialAutoSchedule === true,
|
|
29789
30109
|
now: loadNow ?? void 0
|
|
29790
|
-
}).then(
|
|
30110
|
+
}).then(settleInactiveDrafts);
|
|
29791
30111
|
return {
|
|
29792
30112
|
dataDateCapability: dataDate?.capability ?? null,
|
|
29793
30113
|
sector,
|