[Canonical page](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# JsonProject

A project that lives in wasm memory between calls. It holds the accepted data, the baseline from the last solve and the cached result, so later calls send only edits and options. Create one with `new JsonProject(options_json)` or by calling [`clone()`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md#clone>) on an existing handle. For a one-shot run without a handle, use [`schedule`](<https://docs.dhtmlx.com/planning-engine/wasm/api/schedule.md>).

## Declaration

```ts
class JsonProject {
  constructor(options_json: string);
  solve(data_json: string | null | undefined, options_json: string): string;
  preview(changes_json: string, options_json: string): string;
  analyze(data_json: string | null | undefined, options_json: string): string;
  apply(changes_json: string): string;
  level(options_json: string): string;
  loadProfile(options_json: string): string;
  evaluate(candidates_json: string, options_json: string): string;
  slots(pool_json: string, requirements_json: string): string;
  suggestAssignments(slots_json: string, options_json: string): string;
  clone(): JsonProject;
  free(): void;
  [Symbol.dispose](): void;
}
```

The class has the same declaration in the browser build (`pkg/plen_wasm.js`) and the Node build (`pkg-node/plen_wasm.js`). In the browser build, [`init()`](<https://docs.dhtmlx.com/planning-engine/wasm/api/init.md>) must resolve before the first `new JsonProject`.

Release every handle, clones included, with [`free()`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md#free>) or a `using` declaration ([`[Symbol.dispose]`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md#symboldispose>)).

## Creating

```ts
new JsonProject(options_json: string)
```

Creates an empty project. It has no data and no baseline, so the first call is usually [`solve`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-solve.md>) with data.

### Request

| Argument | Payload | Required | Description |
|---|---|---|---|
| `options_json` | [`ProjectOptions`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md#projectoptions>) | yes | Project-wide options. `"{}"` for defaults |

### ProjectOptions

How the project treats imported data. The options are fixed for the handle's lifetime, and a [clone](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md#clone>) inherits them. Unknown fields are rejected.

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `validation` | `"strict"` \| `"repair"` | no | `"strict"` | `"strict"` rejects invalid data with a `validation` error. `"repair"` fixes duplicate ids and missing or inconsistent dates and reports each fix as a `repaired` note. `null` means the default |

What each mode accepts and fixes: [Accept messy input](<https://docs.dhtmlx.com/planning-engine/wasm/how-to/accept-messy-input.md>).

### Errors

The constructor is the only export that throws on bad input. It throws a **string**, the JSON text of an [error envelope](<https://docs.dhtmlx.com/planning-engine/wasm/wire-format.md#envelope>), not an `Error` object.

| `kind` | `code` | When |
|---|---|---|
| `parse` | `null` | `options_json` isn't valid JSON, isn't an object, or has an unknown field |
| `validation` | `null` | `validation` is a string other than `"strict"` or `"repair"` |

In the browser build, calling the constructor before `init()` resolves fails at host level; see [Host-level failures](<https://docs.dhtmlx.com/planning-engine/wasm/wire-format.md#host-level-failures>).

### Example

Create a handle in Node, with the thrown string parsed as an envelope:

```js
const { JsonProject } = require("./pkg-node/plen_wasm.js");

let project;
try {
  project = new JsonProject(JSON.stringify({ validation: "repair" }));
} catch (thrown) {
  const envelope = JSON.parse(thrown);
  throw new Error(`${envelope.error.kind}: ${envelope.error.message}`);
}
// ... solve, apply, preview
project.free();
```

`options_json` that fails:

```json
{ "validation": "lenient" }
```

What the constructor throws:

```json
{
  "ok": false,
  "error": { "kind": "validation", "code": null, "message": "unknown validation mode", "path": null }
}
```

## Operations

Each operation takes JSON text and returns an envelope as JSON text. None of them throws for bad input.

| Operation | Description |
|---|---|
| [`solve`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-solve.md>) | Load data or reschedule edits, and accept the dates as the baseline |
| [`preview`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-preview.md>) | Schedule hypothetical changes against the baseline without changing the project |
| [`analyze`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-analyze.md>) | Critical path and slack over supplied or current dates, without scheduling |
| [`apply`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-apply.md>) | Commit edits to the accepted state |
| [`level`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-level.md>) | Resolve resource overallocation with delays and splits, and accept the result |
| [`loadProfile`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-load-profile.md>) | Per-bucket load and capacity for resources on the baseline |
| [`evaluate`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-evaluate.md>) | Score candidate change sets against the baseline |
| [`slots`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-slots.md>) | Build assignment slots from workers' skills and task requirements |
| [`suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-suggest-assignments.md>) | Propose assignments that fill, repair or rebalance slots |

## Handle members

### clone

```ts
clone(): JsonProject
```

Returns a new handle with the same accepted state, baseline, cached result and options. The copy shares immutable state with the original until one of them is edited, and edits to one never affect the other. Works on a project in any state, solved or not.

**Returns** - a separate `JsonProject`. Free it on its own: freeing the original doesn't free the clone, and the other way round.

**Errors** - throws at host level when called on a freed handle.

```js
const proposal = project.clone();
try {
  const leveled = JSON.parse(proposal.level("{}"));
  // show leveled.result as a proposal; project is unchanged
} finally {
  proposal.free();
}
```

### free

```ts
free(): void
```

Releases the project's wasm memory right away. After `free()`, every method call on the handle throws an `Error` from the generated bindings, and so does a second `free()`.

A handle that becomes unreachable without `free()` is released eventually by a `FinalizationRegistry`, at a time the garbage collector chooses.

### [Symbol.dispose]

```ts
[Symbol.dispose](): void
```

The same function as `free()`. Lets hosts that support explicit resource management free the handle at the end of a scope:

```js
{
  using project = new JsonProject("{}");
  const envelope = JSON.parse(project.solve(JSON.stringify(data), "{}"));
  // project is freed when the block exits
}
```

## Example

Load a project, try an edit on a clone, and free both handles, in Node:

```js
const { JsonProject } = require("./pkg-node/plen_wasm.js");

const data = {
  config: { use_calendars: false },
  tasks: [
    { id: "a", start_date: "2026-08-10 08:00", duration: 2 },
    { id: "b", start_date: "2026-08-10 08:00", duration: 1 },
  ],
  links: [{ id: "ab", source: "a", target: "b" }],
};

const project = new JsonProject("{}");
try {
  JSON.parse(project.solve(JSON.stringify(data), "{}"));

  const copy = project.clone();
  try {
    copy.apply(JSON.stringify({ tasks: { update: [{ id: "a", duration: 4 }] } }));
    const tried = JSON.parse(copy.solve(undefined, "{}"));
    console.log(tried.result.updates); // a and b moved in the copy
  } finally {
    copy.free();
  }

  // The original still holds its own baseline.
  const current = JSON.parse(project.solve(undefined, "{}"));
  console.log(current.result.updates); // b: 2026-08-12 08:00 - 2026-08-13 08:00
} finally {
  project.free();
}
```

Response of `copy.solve(undefined, "{}")`:

```json
{
  "ok": true,
  "result": {
    "baselineId": "<opaque token>",
    "conflicts": [],
    "converged": true,
    "cycles": [],
    "notes": [],
    "updates": [
      { "id": "a", "kind": "task", "start_date": "2026-08-10 08:00", "end_date": "2026-08-14 08:00", "duration": 4 },
      { "id": "b", "kind": "task", "start_date": "2026-08-14 08:00", "end_date": "2026-08-15 08:00" }
    ]
  }
}
```

## Related

- [`init`](<https://docs.dhtmlx.com/planning-engine/wasm/api/init.md>) - loads the module before the first constructor call in the browser
- [`JsonProject.solve`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-solve.md>) - load data and accept the first baseline
- [Module and Handles](<https://docs.dhtmlx.com/planning-engine/wasm/module-and-handles.md>) - handle lifetime, cloning and freeing
- [Editing a Project](<https://docs.dhtmlx.com/planning-engine/wasm/live-projects/editing-a-project.md>) - change sets, applying and rescheduling
- [Error handling](<https://docs.dhtmlx.com/planning-engine/wasm/error-handling.md>) - the thrown constructor envelope and host-level failures
