@outbuild-company/schedule-core 1.10.4 → 1.12.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/README.md CHANGED
@@ -1,61 +1,32 @@
1
- <div align="center">
2
-
3
- # Schedule Core
4
-
5
- **A UI-agnostic domain engine for project schedules.**
6
-
7
- Activities, dependencies, working calendars, constraints, autoscheduling, and critical path — behind one typed boundary.
1
+ <div class="ob-hero">
2
+ <img src="docs/api/assets/outbuild-mark.svg" alt="Outbuild" class="ob-hero-mark" width="96" />
3
+ <div>
4
+ <p class="ob-eyebrow">OUTBUILD DEVELOPER PLATFORM</p>
5
+ <h1>Schedule Core</h1>
6
+ <p class="ob-hero-copy">The shared scheduling engine used by Outbuild across frontend, backend, tests and background jobs.</p>
7
+ </div>
8
+ </div>
8
9
 
9
10
  [![CI](https://github.com/OutBuild-Construction-Software/schedule-core/actions/workflows/ci.yml/badge.svg)](https://github.com/OutBuild-Construction-Software/schedule-core/actions/workflows/ci.yml)
10
11
  [![npm](https://img.shields.io/npm/v/%40outbuild-company%2Fschedule-core)](https://www.npmjs.com/package/@outbuild-company/schedule-core)
12
+ [![Documentation](https://img.shields.io/badge/docs-Schedule_Core-7DFF8A?labelColor=121212)](https://outbuild-construction-software.github.io/schedule-core/)
11
13
 
12
- </div>
13
-
14
- ## Why Schedule Core?
14
+ Schedule Core centralizes Outbuild's scheduling calculations and business rules
15
+ so every consumer produces the same result. It owns canonical schedule state;
16
+ rendering, HTTP requests and database writes remain outside the library.
15
17
 
16
- Scheduling behavior needs one authority. If dates, dependency cascades, calendar math, and summary rollups are split between a Gantt widget and application code, the same edit can acquire different meanings in different consumers.
18
+ ## What it handles
17
19
 
18
- Schedule Core owns normalized schedule state and its mutations. A consumer supplies backend data, sends typed commands, and projects the resulting changes. Rendering and storage stay at the boundary.
19
-
20
- ## Mental model
21
-
22
- ```mermaid
23
- flowchart LR
24
- Payload["Backend payload"] --> Normalize["Parse + normalize"]
25
- Normalize --> Core
26
- Command["DispatchAction"] --> Core
27
-
28
- subgraph Engine["Schedule Core"]
29
- Core["Canonical state"] --> Pipeline["Validate · mutate · derive"]
30
- Pipeline --> Core
31
- end
32
-
33
- Pipeline --> Changes["ChangeSet"]
34
- Core --> Reads["Read-only snapshots"]
35
- Changes --> Consumers["UI · persistence · analytics"]
36
- Reads --> Consumers
37
- ```
38
-
39
- | Part | Contract |
20
+ | Capability | What the engine owns |
40
21
  | --- | --- |
41
- | **Input** | Backend-shaped sector, activities, links, and calendars. Construction parses and normalizes them. |
42
- | **State** | `ScheduleCore` owns the live activity, link, calendar, hierarchy, view-state, and history model. |
43
- | **Command** | A `DispatchAction` expresses an edit, structural operation, dependency change, or synchronization event. |
44
- | **Result** | Accepted commands return a `ChangeSet`; expected validation failures return `{ ok: false, reason }`. |
45
- | **Consumer** | Applies projections, presents warnings, sends analytics, and performs I/O. |
46
-
47
- ## Core concepts
48
-
49
- | Concept | Role in the engine |
50
- | --- | --- |
51
- | **Activities** | Tasks, milestones, and summary projects in a hierarchical schedule. |
52
- | **Links** | Typed dependencies with working-time lag and effective-graph cycle guards. |
53
- | **Calendars** | Working days, shifts, exceptions, and date/duration calculations. |
54
- | **Constraints** | ASAP, ALAP, start-based, finish-based, and fixed-date scheduling rules. |
55
- | **Autoscheduler** | Applies dependency, constraint, calendar, and parent-bound rules after relevant mutations. |
56
- | **Critical path** | Computes early/late dates, slack, and critical flags through an asynchronous job. |
57
- | **ChangeSet** | Entity diffs plus view state, ordering, tracking events, effects, and constraint warnings. |
58
- | **History** | Undo/redo state and persistence checkpoints owned by the core instance. |
22
+ | Automatic scheduling | Dependency, constraint and hierarchy propagation. |
23
+ | Critical Path | Early and late dates, slack and critical activities. |
24
+ | Working calendars | Calendar-aware dates, durations and working-time lag. |
25
+ | Activities and dependencies | Validated creation, edits, hierarchy and links. |
26
+ | Baselines and filtering | Shared derived state for every consumer. |
27
+ | Undo and redo | Reversible commands with recalculated dependent state. |
28
+ | Fork and projection | Isolated scenarios without changing official state. |
29
+ | UTC-safe calculations | Scheduling behavior independent from local time zones. |
59
30
 
60
31
  ## Use it in 30 seconds
61
32
 
@@ -65,125 +36,94 @@ npm install @outbuild-company/schedule-core
65
36
 
66
37
  ```ts
67
38
  import {
68
- COLUMN,
69
- ScheduleCore,
70
- type ScheduleCoreInput
39
+ ACTIVITY_TYPE,
40
+ ROOT_PARENT_ID,
41
+ createSchedule
71
42
  } from '@outbuild-company/schedule-core';
72
43
 
73
- const input: ScheduleCoreInput = getScheduleInput();
74
- const core = new ScheduleCore(input);
75
- await core.ready;
76
-
77
- const activity = core.getAllActivitiesView()[0];
78
- if (activity) {
79
- const result = await core.dispatch({
80
- kind: 'inline-edit',
81
- activityId: activity.id,
82
- column: COLUMN.TEXT,
83
- newValue: 'Updated activity'
44
+ const backend = await fetch('/api/schedules/42').then((response) =>
45
+ response.json()
46
+ );
47
+
48
+ const schedule = await createSchedule({
49
+ sector: backend.sector,
50
+ activities: backend.activities ?? [],
51
+ links: backend.links ?? [],
52
+ calendars: backend.calendars ?? [],
53
+ baseCalendars: backend.baseCalendars ?? []
54
+ });
55
+
56
+ try {
57
+ const result = await schedule.activities.create({
58
+ activityId: 'A1',
59
+ parentId: ROOT_PARENT_ID,
60
+ overrides: {
61
+ name: 'Excavation',
62
+ type: ACTIVITY_TYPE.TASK,
63
+ durationDays: 3
64
+ }
84
65
  });
85
66
 
86
- if (!result.ok) throw new Error(result.reason);
87
- applyChangeSet(result.changes);
67
+ console.log(result.ok);
68
+ console.log(schedule.activities.get('A1')?.name);
69
+ } finally {
70
+ schedule.destroy();
88
71
  }
89
-
90
- core.destroy();
91
72
  ```
92
73
 
93
- `getScheduleInput()` and `applyChangeSet()` belong to the consumer. The package exports one supported root entry point; deep imports are internal.
74
+ Output:
94
75
 
95
- See the [API reference](docs/API.md) for initialization, commands, read APIs, persistence, undo/redo, forks, critical path, constants, and exported types.
96
-
97
- ## What a change contains
98
-
99
- ```ts
100
- interface ChangeSet {
101
- source: ChangeSetSource;
102
- activities: ReadonlyArray<EntityChange<CoreActivity>>;
103
- links: ReadonlyArray<EntityChange<Link>>;
104
- calendars: ReadonlyArray<EntityChange<Calendar>>;
105
- trackingEvents: ReadonlyArray<TrackingEvent>;
106
- viewState?: ReadonlyArray<ViewStateChange>;
107
- order?: ReadonlyArray<BranchOrderChange>;
108
- effects?: ReadonlyArray<ScheduleEffect>;
109
- warnings?: ReadonlyArray<ConstraintWarning>;
110
- }
76
+ ```text
77
+ true
78
+ Excavation
111
79
  ```
112
80
 
113
- Changes are data, not side effects. A successful dispatch updates core state; consumers decide how to render, persist, report, or transmit the returned records.
81
+ The library accepts backend-shaped schedule data, normalizes it internally and
82
+ returns a ready instance. A successful mutation updates core state and returns
83
+ the complete `ChangeSet`; a rejected mutation leaves state untouched.
114
84
 
115
- ## Core invariants
85
+ ## One engine, two public entry points
116
86
 
117
- - **Serialized writes.** State-changing operations on one `ScheduleCore` instance run through a single operation queue.
118
- - **Isolated reads.** Public read methods return cloned snapshots or read-only collections; mutating a returned value cannot mutate core state.
119
- - **Guarded commits.** A rejected dispatch restores captured mutations. A failed rollback poisons the instance instead of continuing with uncertain state.
120
- - **Cycle-safe graph edits.** New dependency and hierarchy mutations are checked against the effective dependency graph. Pre-existing cycles can be inspected with `auditEffectiveGraph()`.
121
- - **Explicit time units.** Duration commands accept working days; domain snapshots expose `durationHours`, and link lag is stored and exposed in working hours.
122
- - **Persistence is acknowledged.** Storage remains external; a successful `persistence-acknowledge` establishes the saved checkpoint and history boundary.
123
-
124
- The complete, code-linked contract lives in [INVARIANTS.md](INVARIANTS.md).
125
-
126
- ## Architecture principles
127
-
128
- ### One mutation boundary
129
-
130
- Domain writes enter through `dispatch()` or `applyActivityBatch()`. Handlers validate commands, update the canonical state, run the required derived passes, and assemble one result for the consumer.
131
-
132
- ### Explicit ownership
133
-
134
- The core owns schedule entities, temporal calculations, dependency semantics, derived fields, and change tracking. Consumers own transport, persistence execution, UI projection, and presentation of rejected commands.
135
-
136
- ### Narrow internal roles
137
-
138
- `ScheduleState` is the store, but subsystems depend on smaller capabilities such as activity readers/writers, calendar operations, the write journal, and the autoscheduler port. Architecture tests ratchet layer boundaries and public-entry reachability.
139
-
140
- ### Legacy engines behind adapters
141
-
142
- The package has no runtime dependency on React or the DHTMLX package. Its calendar and critical-path implementations do retain adapted legacy engine code and Gantt-shaped internal contracts. The application-side bridge projects `ChangeSet` data into DHTMLX; that bridge remains outside this package.
143
-
144
- ## What Schedule Core is not
145
-
146
- Schedule Core does not:
147
-
148
- - render a Gantt chart or React component;
149
- - own a browser application's component state;
150
- - fetch schedule data or make HTTP requests;
151
- - write to a database or storage service;
152
- - decide how warnings, analytics, or rejected commands are presented.
87
+ | Consumer | Entry point |
88
+ | --- | --- |
89
+ | Backend, worker, script or test | `await createSchedule(input)` |
90
+ | Outbuild frontend | The active `scheduleActions` bridge |
91
+ | Low-level integration | `new ScheduleCore(input)` and `await schedule.ready` |
153
92
 
154
- It does track dirty entities and persistence checkpoints, but the consumer performs the actual save.
93
+ The bridge and programmatic API use the same engine, validations and scheduling
94
+ rules. The frontend bridge additionally owns the live DHTMLX projection;
95
+ DHTMLX is a UI layer, not the scheduling authority.
155
96
 
156
- ## Repository map
97
+ ## API model
157
98
 
158
99
  ```text
159
- src/
160
- ├── index.ts public package contract
161
- ├── init/ facade, initialization, read API
162
- ├── dispatch/ commands, handlers, ChangeSets, undo/redo
163
- ├── internal/ state, hierarchy, ordering, dependency graph
164
- ├── autoscheduler/ ASAP/ALAP scheduling and link propagation
165
- ├── calendar/ working-time API and engine adapter
166
- ├── columns/ typed field mutation pipelines
167
- ├── propagations/ parent and derived-field propagation
168
- ├── critical-path/ critical-path facade and legacy adapter
169
- ├── constraints/ constraint vocabulary and date semantics
170
- ├── boundary/ backend normalization and save tracking
171
- └── testing/ fixtures, scenarios, recordings, and parity harnesses
100
+ backend data → ready schedule → validated action → result + updated state
172
101
  ```
173
102
 
174
- ## Testing
103
+ | Namespace | Responsibility |
104
+ | --- | --- |
105
+ | `schedule.activities` | Read and mutate activities and hierarchy. |
106
+ | `schedule.links` | Read and mutate dependencies. |
107
+ | `schedule.calendars` | Read calendars and reconcile dates. |
108
+ | `schedule.selection` | Own selected activity IDs. |
109
+ | `schedule.view` | Filter, order and visibility. |
110
+ | `schedule.baselines` | Apply and read baseline state. |
111
+ | `schedule.persistence` | Read dirty state and acknowledge saved checkpoints. |
112
+ | `schedule.history` | Undo and redo. |
113
+ | `schedule.projection` | Compare official and forked schedules. |
114
+ | `schedule.criticalPath` | Recompute and await Critical Path work. |
115
+ | `schedule.diagnostics` | Audit graphs, revisions and canonical state. |
175
116
 
176
- [Vitest](https://vitest.dev/) runs two explicit projects:
117
+ ## Documentation
177
118
 
178
- - **Unit** sibling tests for domain modules, mutation pipelines, calendars, scheduling, graph rules, history, and architecture gates.
179
- - **Integration** parity suites and recorded sessions over captured project data.
119
+ - [Start with the visual API guide](https://outbuild-construction-software.github.io/schedule-core/documents/Schedule_Core.html)
120
+ - [Read the guide in this repository](docs/api/index.md)
121
+ - [Find every namespace and method](docs/api/reference.md)
180
122
 
181
- The repository also contains Gherkin behavior scenarios, production-derived recordings, undo/redo corpus checks, large-project regression cases, and compile-time boundary tests. These are verification strategies, not a published coverage or benchmark claim.
123
+ Every guide uses the same format:
182
124
 
183
- ```bash
184
- npm test # all Vitest projects
185
- npm run test:unit # unit project
186
- npm run test:integration # parity and session replays
125
+ ```text
126
+ state before API call → returned result → state after
187
127
  ```
188
128
 
189
129
  ## Development
@@ -197,19 +137,8 @@ npm run typecheck:boundary
197
137
  npm run lint
198
138
  npm test
199
139
  npm run build
140
+ npm run docs:check
200
141
  ```
201
142
 
202
- The build emits ESM, CommonJS, type declarations, source maps, and a browser IIFE under `dist/`. The package declares no runtime dependencies.
203
-
204
- ## Documentation
205
-
206
- - [Public API](docs/API.md) — supported entry point, lifecycle, commands, reads, persistence, and types.
207
- - [Model invariants](INVARIANTS.md) — verified units, dates, queueing, write capture, persistence, and graph rules.
208
- - [Domain and architecture index](docs/domain/README.md) — canonical map of the detailed domain documentation.
209
- - [State roles](docs/domain/STATE_ROLES.md) — internal capability boundaries around `ScheduleState`.
210
- - [Activity ownership](docs/domain/model/SCHEDULE_CORE_OWNERSHIP.md) — current core/render authority by field category.
211
- - [Temporal model](docs/domain/scheduling/temporal-model.md) — temporal vocabulary, policies, and known open decisions.
212
- - [Calendars](docs/domain/dates-calendars/CALENDARS.md) — working-time model, normalization, and engine boundary.
213
- - [Constraints](docs/domain/constraints/01-constraint-types-catalog.md) — supported constraint vocabulary and semantics.
214
- - [Architecture audit](docs/audit/README.md) — measured implementation map, findings, and unresolved questions.
215
- - [Changelog](CHANGELOG.md) — released behavior and package history.
143
+ The package emits ESM, CommonJS, type declarations, source maps and a browser
144
+ IIFE. It has no runtime dependencies.