@ternent/concord 0.2.2
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 +46 -0
- package/SPEC.md +189 -0
- package/dist/concord/src/app.d.ts +3 -0
- package/dist/concord/src/app.d.ts.map +1 -0
- package/dist/concord/src/errors.d.ts +5 -0
- package/dist/concord/src/errors.d.ts.map +1 -0
- package/dist/concord/src/index.d.ts +4 -0
- package/dist/concord/src/index.d.ts.map +1 -0
- package/dist/concord/src/types.d.ts +93 -0
- package/dist/concord/src/types.d.ts.map +1 -0
- package/dist/index.js +430 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# `@ternent/concord`
|
|
2
|
+
|
|
3
|
+
Command-driven developer runtime for building non-custodial apps on top of `@ternent/ledger`.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createConcordApp } from "@ternent/concord";
|
|
7
|
+
|
|
8
|
+
const app = await createConcordApp({
|
|
9
|
+
identity,
|
|
10
|
+
storage,
|
|
11
|
+
plugins: [createTodoPlugin()]
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await app.load();
|
|
15
|
+
|
|
16
|
+
await app.command("todo.create-item", {
|
|
17
|
+
id: crypto.randomUUID(),
|
|
18
|
+
title: "Buy milk"
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await app.command("todo.rename-item", {
|
|
22
|
+
id: "todo_123",
|
|
23
|
+
title: "Buy oat milk"
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await app.commit({
|
|
27
|
+
metadata: {
|
|
28
|
+
message: "Create and refine first todo"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const todoState = app.getPluginState("todo");
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Concord keeps Ledger as the truth engine. Plugins own command ergonomics and projection logic, while commits remain explicit authored history boundaries.
|
|
36
|
+
|
|
37
|
+
Concord treats committed history as atomic truth. If any committed byte in reachable history is invalid, the document is globally invalid for normal runtime use, even though verification still reports the precise broken commits or entries.
|
|
38
|
+
|
|
39
|
+
The core lifecycle is:
|
|
40
|
+
|
|
41
|
+
1. `command(...)` stages one or more entries
|
|
42
|
+
2. local replay reflects committed truth plus staged truth
|
|
43
|
+
3. `commit(...)` groups staged entries into a signed commit with contextual metadata
|
|
44
|
+
4. replay rebuilds app state from that history
|
|
45
|
+
|
|
46
|
+
Entries carry domain meaning. Signed commits carry chain integrity.
|
package/SPEC.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# `@ternent/concord` Specification
|
|
2
|
+
|
|
3
|
+
## 1. Purpose
|
|
4
|
+
|
|
5
|
+
`@ternent/concord` is the developer-facing runtime for building non-custodial applications on top of `@ternent/ledger`.
|
|
6
|
+
|
|
7
|
+
Concord is command-first, but it is not auto-commit-first.
|
|
8
|
+
|
|
9
|
+
It exists to make this normal:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const app = await createConcordApp({
|
|
13
|
+
identity,
|
|
14
|
+
storage,
|
|
15
|
+
plugins: [createTodoPlugin()],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
await app.load();
|
|
19
|
+
|
|
20
|
+
await app.command("todo.create-item", {
|
|
21
|
+
id: crypto.randomUUID(),
|
|
22
|
+
title: "Buy milk",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await app.command("todo.rename-item", {
|
|
26
|
+
id: "todo_123",
|
|
27
|
+
title: "Buy oat milk",
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await app.commit({
|
|
31
|
+
metadata: {
|
|
32
|
+
message: "Create and refine first todo",
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Commands stage domain meaning. Commits author signed history boundaries.
|
|
38
|
+
|
|
39
|
+
## 2. Layering
|
|
40
|
+
|
|
41
|
+
- `@ternent/concord-protocol` provides deterministic protocol primitives
|
|
42
|
+
- `@ternent/ledger` owns append-only truth, signed commits, replay, and verification
|
|
43
|
+
- `@ternent/concord` owns command dispatch, plugin projection, and runtime composition
|
|
44
|
+
- framework adapters belong above Concord
|
|
45
|
+
|
|
46
|
+
Concord must never reimplement ledger truth mechanics.
|
|
47
|
+
|
|
48
|
+
## 3. Core model
|
|
49
|
+
|
|
50
|
+
### 3.1 Truth vs runtime state
|
|
51
|
+
|
|
52
|
+
- entries are units of meaning
|
|
53
|
+
- signed commits are the primary authored integrity boundary
|
|
54
|
+
- plugin state is derived from replay
|
|
55
|
+
- projection targets are derived from replay
|
|
56
|
+
- storage is persistence only
|
|
57
|
+
|
|
58
|
+
### 3.2 Command and commit lifecycle
|
|
59
|
+
|
|
60
|
+
The normal Concord write lifecycle is:
|
|
61
|
+
|
|
62
|
+
1. dispatch one or more commands
|
|
63
|
+
2. stage one or more ledger entries
|
|
64
|
+
3. replay committed truth plus staged truth into projected app state
|
|
65
|
+
4. explicitly commit staged entries into a signed commit
|
|
66
|
+
5. replay the updated committed history
|
|
67
|
+
|
|
68
|
+
Concord may support optional auto-commit hooks, but explicit commits are the default and primary model.
|
|
69
|
+
|
|
70
|
+
## 4. Public API
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
type ConcordApp = {
|
|
74
|
+
create(params?: ConcordCreateParams): Promise<void>;
|
|
75
|
+
load(): Promise<void>;
|
|
76
|
+
|
|
77
|
+
command<TInput = unknown>(
|
|
78
|
+
type: string,
|
|
79
|
+
input: TInput
|
|
80
|
+
): Promise<ConcordCommandResult>;
|
|
81
|
+
|
|
82
|
+
commit(input?: ConcordCommitInput): Promise<ConcordCommitResult>;
|
|
83
|
+
|
|
84
|
+
replay(options?: ConcordReplayOptions): Promise<void>;
|
|
85
|
+
recompute(): Promise<void>;
|
|
86
|
+
|
|
87
|
+
verify(): Promise<LedgerVerificationResult>;
|
|
88
|
+
|
|
89
|
+
exportLedger(): Promise<LedgerContainer>;
|
|
90
|
+
importLedger(container: LedgerContainer): Promise<void>;
|
|
91
|
+
|
|
92
|
+
getState(): Readonly<ConcordState>;
|
|
93
|
+
getPluginState<T = unknown>(pluginId: string): T;
|
|
94
|
+
|
|
95
|
+
subscribe(listener: (state: Readonly<ConcordState>) => void): () => void;
|
|
96
|
+
destroy(): Promise<void>;
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 5. State model
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
type ConcordState = {
|
|
104
|
+
ready: boolean;
|
|
105
|
+
integrityValid: boolean;
|
|
106
|
+
stagedCount: number;
|
|
107
|
+
plugins: Record<string, unknown>;
|
|
108
|
+
verification: LedgerVerificationResult | null;
|
|
109
|
+
};
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Rules:
|
|
113
|
+
|
|
114
|
+
- `plugins` is replay-derived app state
|
|
115
|
+
- `integrityValid` indicates whether the currently loaded committed history passed strict ledger verification
|
|
116
|
+
- `stagedCount` exposes local working-state depth without leaking raw ledger internals as the primary surface
|
|
117
|
+
- verification applies to the ledger artifact and current staged state, not to plugin state as authority
|
|
118
|
+
- `ready` means the app has a trustworthy projected runtime state available for normal use
|
|
119
|
+
|
|
120
|
+
## 6. Plugin model
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
type ConcordPlugin<PState = unknown> = {
|
|
124
|
+
id: string;
|
|
125
|
+
initialState(): PState;
|
|
126
|
+
commands?: Record<
|
|
127
|
+
string,
|
|
128
|
+
(
|
|
129
|
+
ctx: ConcordCommandContext,
|
|
130
|
+
input: unknown
|
|
131
|
+
) => Promise<LedgerAppendInput | LedgerAppendInput[]> | LedgerAppendInput | LedgerAppendInput[]
|
|
132
|
+
>;
|
|
133
|
+
project(
|
|
134
|
+
state: PState,
|
|
135
|
+
entry: LedgerReplayEntry,
|
|
136
|
+
ctx: ConcordProjectionContext
|
|
137
|
+
): PState;
|
|
138
|
+
selectors?: Record<string, (state: PState) => unknown>;
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Rules:
|
|
143
|
+
|
|
144
|
+
- commands return ledger append inputs
|
|
145
|
+
- commands stage entries; they do not implicitly define commit boundaries
|
|
146
|
+
- `project()` is a pure replay consumer over the plugin state slice and replay entry
|
|
147
|
+
- plugins define domain meaning, not truth mechanics
|
|
148
|
+
|
|
149
|
+
## 7. Projection targets
|
|
150
|
+
|
|
151
|
+
Projection targets consume replay output after plugin state is rebuilt for the current entry.
|
|
152
|
+
|
|
153
|
+
They:
|
|
154
|
+
|
|
155
|
+
- consume replayed truth
|
|
156
|
+
- may materialize query stores
|
|
157
|
+
- do not author truth
|
|
158
|
+
- do not replace plugin projection
|
|
159
|
+
|
|
160
|
+
Projection target failures fail the Concord operation. They are not swallowed.
|
|
161
|
+
|
|
162
|
+
## 8. Runtime rules
|
|
163
|
+
|
|
164
|
+
- Concord defaults to `autoCommit: false`
|
|
165
|
+
- `command()` stages entries and rebuilds local projected state from committed plus staged replay
|
|
166
|
+
- `commit()` creates a signed commit via Ledger and clears staged entries only after successful commit creation
|
|
167
|
+
- `verify()` validates the ledger artifact and current staged state; it does not itself replay
|
|
168
|
+
- `exportLedger()` exposes committed truth only
|
|
169
|
+
- staged truth remains visible through replayed app state until explicitly committed or cleared below Concord
|
|
170
|
+
|
|
171
|
+
Concord treats committed history as atomic truth.
|
|
172
|
+
If any committed byte in reachable history is invalid, Concord must not present projected state as trustworthy runtime state.
|
|
173
|
+
|
|
174
|
+
That means:
|
|
175
|
+
|
|
176
|
+
- `load()` and `importLedger()` may still succeed for diagnostic inspection
|
|
177
|
+
- invalid committed history forces `ready: false`
|
|
178
|
+
- invalid committed history forces `integrityValid: false`
|
|
179
|
+
- commands and commits are blocked until trustworthy runtime state exists again
|
|
180
|
+
|
|
181
|
+
## 9. Acceptance criteria
|
|
182
|
+
|
|
183
|
+
Concord is correct when:
|
|
184
|
+
|
|
185
|
+
- multiple commands can stage multiple entries before one explicit commit
|
|
186
|
+
- staged state is visible through replay before commit
|
|
187
|
+
- committed history is advanced only by explicit commit unless auto-commit is intentionally configured
|
|
188
|
+
- exported ledgers show signed commit records as the authored integrity boundary
|
|
189
|
+
- docs and examples teach `command -> command -> commit`, not `command = commit`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,UAAU,EAUV,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA0GpB,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,UAAU,CAAC,CA+arB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["errors.ts"],"names":[],"mappings":"AAAA,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createConcordApp } from "./app.js";
|
|
2
|
+
export { ConcordBoundaryError } from "./errors.js";
|
|
3
|
+
export type { ConcordApp, ConcordCommandContext, ConcordCommitInput, ConcordCommitResult, ConcordCommandResult, ConcordCreateParams, ConcordIdentityContext, ConcordPlugin, ConcordProjectionContext, ConcordProjectionReplayContext, ConcordProjectionTarget, ConcordReplayAppView, ConcordReplayOptions, ConcordRuntimePolicy, ConcordState, CreateConcordAppInput } from "./types.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,UAAU,EACV,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,wBAAwB,EACxB,8BAA8B,EAC9B,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACtB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { LedgerAppendInput, LedgerArmourContract, LedgerContainer, LedgerInstance, LedgerProtocolContract, LedgerReplayEntry, LedgerSealContract, LedgerStorageAdapter, LedgerVerificationResult } from '../../ledger-v2/src/index.ts';
|
|
2
|
+
export type ConcordCreateParams = {
|
|
3
|
+
metadata?: Record<string, unknown>;
|
|
4
|
+
};
|
|
5
|
+
export type ConcordReplayOptions = {
|
|
6
|
+
verify?: boolean;
|
|
7
|
+
decrypt?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type ConcordState = {
|
|
10
|
+
ready: boolean;
|
|
11
|
+
integrityValid: boolean;
|
|
12
|
+
stagedCount: number;
|
|
13
|
+
plugins: Record<string, unknown>;
|
|
14
|
+
verification: LedgerVerificationResult | null;
|
|
15
|
+
};
|
|
16
|
+
export type ConcordCommandResult = {
|
|
17
|
+
commitId?: string;
|
|
18
|
+
entryIds: string[];
|
|
19
|
+
stagedCount: number;
|
|
20
|
+
};
|
|
21
|
+
export type ConcordCommitInput = {
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
export type ConcordCommitResult = {
|
|
25
|
+
commitId: string;
|
|
26
|
+
entryIds: string[];
|
|
27
|
+
};
|
|
28
|
+
export type ConcordRuntimePolicy = {
|
|
29
|
+
autoCommit?: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type ConcordIdentityContext = {
|
|
32
|
+
author: string;
|
|
33
|
+
signer?: unknown;
|
|
34
|
+
decryptor?: unknown;
|
|
35
|
+
};
|
|
36
|
+
export type ConcordCommandContext = {
|
|
37
|
+
now(): string;
|
|
38
|
+
identity: ConcordIdentityContext;
|
|
39
|
+
getPluginState<T = unknown>(pluginId: string): T;
|
|
40
|
+
};
|
|
41
|
+
export type ConcordProjectionContext = {
|
|
42
|
+
decryptAvailable: boolean;
|
|
43
|
+
};
|
|
44
|
+
export type ConcordPlugin<PState = unknown> = {
|
|
45
|
+
id: string;
|
|
46
|
+
initialState(): PState;
|
|
47
|
+
commands?: Record<string, (ctx: ConcordCommandContext, input: unknown) => Promise<LedgerAppendInput | LedgerAppendInput[]> | LedgerAppendInput | LedgerAppendInput[]>;
|
|
48
|
+
project(state: PState, entry: LedgerReplayEntry, ctx: ConcordProjectionContext): PState;
|
|
49
|
+
selectors?: Record<string, (state: PState) => unknown>;
|
|
50
|
+
};
|
|
51
|
+
export type ConcordReplayAppView = {
|
|
52
|
+
getState(): Readonly<ConcordState>;
|
|
53
|
+
getPluginState<T = unknown>(pluginId: string): T;
|
|
54
|
+
};
|
|
55
|
+
export type ConcordProjectionReplayContext = {
|
|
56
|
+
app: ConcordReplayAppView;
|
|
57
|
+
};
|
|
58
|
+
export type ConcordProjectionTarget = {
|
|
59
|
+
name: string;
|
|
60
|
+
reset(): Promise<void> | void;
|
|
61
|
+
beginReplay?(ctx: ConcordProjectionReplayContext): Promise<void> | void;
|
|
62
|
+
applyEntry(entry: LedgerReplayEntry, ctx: ConcordProjectionReplayContext): Promise<void> | void;
|
|
63
|
+
endReplay?(ctx: ConcordProjectionReplayContext): Promise<void> | void;
|
|
64
|
+
destroy?(): Promise<void> | void;
|
|
65
|
+
};
|
|
66
|
+
export type CreateConcordAppInput = {
|
|
67
|
+
identity: ConcordIdentityContext;
|
|
68
|
+
storage: LedgerStorageAdapter;
|
|
69
|
+
plugins: ConcordPlugin[];
|
|
70
|
+
now?: () => string;
|
|
71
|
+
protocol?: LedgerProtocolContract;
|
|
72
|
+
seal?: LedgerSealContract;
|
|
73
|
+
armour?: LedgerArmourContract;
|
|
74
|
+
ledger?: LedgerInstance<unknown>;
|
|
75
|
+
projectionTargets?: ConcordProjectionTarget[];
|
|
76
|
+
policy?: ConcordRuntimePolicy;
|
|
77
|
+
};
|
|
78
|
+
export type ConcordApp = {
|
|
79
|
+
create(params?: ConcordCreateParams): Promise<void>;
|
|
80
|
+
load(): Promise<void>;
|
|
81
|
+
command<TInput = unknown>(type: string, input: TInput): Promise<ConcordCommandResult>;
|
|
82
|
+
commit(input?: ConcordCommitInput): Promise<ConcordCommitResult>;
|
|
83
|
+
replay(options?: ConcordReplayOptions): Promise<void>;
|
|
84
|
+
recompute(): Promise<void>;
|
|
85
|
+
verify(): Promise<LedgerVerificationResult>;
|
|
86
|
+
exportLedger(): Promise<LedgerContainer>;
|
|
87
|
+
importLedger(container: LedgerContainer): Promise<void>;
|
|
88
|
+
getState(): Readonly<ConcordState>;
|
|
89
|
+
getPluginState<T = unknown>(pluginId: string): T;
|
|
90
|
+
subscribe(listener: (state: Readonly<ConcordState>) => void): () => void;
|
|
91
|
+
destroy(): Promise<void>;
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => {
|
|
4
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
return value;
|
|
6
|
+
};
|
|
7
|
+
import { createLedger } from "@ternent/ledger";
|
|
8
|
+
class ConcordBoundaryError extends Error {
|
|
9
|
+
constructor(code, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
__publicField(this, "code");
|
|
12
|
+
this.name = "ConcordBoundaryError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function createDefaultNow() {
|
|
17
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
18
|
+
}
|
|
19
|
+
function isObject(value) {
|
|
20
|
+
return typeof value === "object" && value !== null;
|
|
21
|
+
}
|
|
22
|
+
function isLedgerReplayEntry(value) {
|
|
23
|
+
if (!isObject(value)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (typeof value.entryId !== "string" || typeof value.kind !== "string" || typeof value.author !== "string" || typeof value.authoredAt !== "string") {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (!(value.meta === null || isObject(value.meta))) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (!isObject(value.payload) || typeof value.payload.type !== "string") {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return value.payload.type === "plain" || value.payload.type === "encrypted" || value.payload.type === "decrypted";
|
|
36
|
+
}
|
|
37
|
+
function assertReplayEntries(value) {
|
|
38
|
+
if (Array.isArray(value) && value.every(isLedgerReplayEntry)) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
throw new ConcordBoundaryError(
|
|
42
|
+
"INVALID_LEDGER_PROJECTION",
|
|
43
|
+
"Concord requires ledger replay output to be LedgerReplayEntry[]."
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
function createInitialPluginState(plugins) {
|
|
47
|
+
return Object.fromEntries(plugins.map((plugin) => [plugin.id, plugin.initialState()]));
|
|
48
|
+
}
|
|
49
|
+
function assertKnownPlugin(pluginsById, pluginId) {
|
|
50
|
+
if (!pluginsById.has(pluginId)) {
|
|
51
|
+
throw new ConcordBoundaryError(
|
|
52
|
+
"UNKNOWN_PLUGIN",
|
|
53
|
+
`Unknown Concord plugin: ${pluginId}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function normalizeAppendInputs(value) {
|
|
58
|
+
const inputs = Array.isArray(value) ? value : [value];
|
|
59
|
+
if (inputs.length === 0) {
|
|
60
|
+
throw new ConcordBoundaryError(
|
|
61
|
+
"COMMAND_PRODUCED_NO_ENTRIES",
|
|
62
|
+
"Concord command handlers must return at least one LedgerAppendInput."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return inputs;
|
|
66
|
+
}
|
|
67
|
+
function assertInternalLedgerRequirements(input) {
|
|
68
|
+
if (typeof input.identity.author !== "string" || input.identity.author.length === 0) {
|
|
69
|
+
throw new ConcordBoundaryError(
|
|
70
|
+
"INVALID_IDENTITY",
|
|
71
|
+
"Concord requires identity.author when creating an internal ledger."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (!input.identity.signer) {
|
|
75
|
+
throw new ConcordBoundaryError(
|
|
76
|
+
"INVALID_IDENTITY",
|
|
77
|
+
"Concord requires identity.signer when creating an internal ledger."
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function createConcordApp(input) {
|
|
82
|
+
const plugins = [...input.plugins];
|
|
83
|
+
const projectionTargets = [...input.projectionTargets ?? []];
|
|
84
|
+
const now = input.now ?? createDefaultNow;
|
|
85
|
+
const policy = {
|
|
86
|
+
autoCommit: input.policy?.autoCommit ?? false
|
|
87
|
+
};
|
|
88
|
+
const pluginsById = /* @__PURE__ */ new Map();
|
|
89
|
+
const commands = /* @__PURE__ */ new Map();
|
|
90
|
+
const projectionTargetNames = /* @__PURE__ */ new Set();
|
|
91
|
+
for (const plugin of plugins) {
|
|
92
|
+
if (pluginsById.has(plugin.id)) {
|
|
93
|
+
throw new ConcordBoundaryError(
|
|
94
|
+
"DUPLICATE_PLUGIN_ID",
|
|
95
|
+
`Duplicate Concord plugin id: ${plugin.id}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
pluginsById.set(plugin.id, plugin);
|
|
99
|
+
for (const [commandType, handler] of Object.entries(plugin.commands ?? {})) {
|
|
100
|
+
if (commands.has(commandType)) {
|
|
101
|
+
throw new ConcordBoundaryError(
|
|
102
|
+
"DUPLICATE_COMMAND_TYPE",
|
|
103
|
+
`Duplicate Concord command type: ${commandType}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
commands.set(commandType, { plugin, handler });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const target of projectionTargets) {
|
|
110
|
+
if (projectionTargetNames.has(target.name)) {
|
|
111
|
+
throw new ConcordBoundaryError(
|
|
112
|
+
"DUPLICATE_PROJECTION_TARGET_NAME",
|
|
113
|
+
`Duplicate Concord projection target name: ${target.name}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
projectionTargetNames.add(target.name);
|
|
117
|
+
}
|
|
118
|
+
if (!input.ledger) {
|
|
119
|
+
assertInternalLedgerRequirements(input);
|
|
120
|
+
}
|
|
121
|
+
const ledger = input.ledger ?? await createLedger({
|
|
122
|
+
identity: {
|
|
123
|
+
signer: input.identity.signer,
|
|
124
|
+
authorResolver: () => input.identity.author,
|
|
125
|
+
decryptor: input.identity.decryptor
|
|
126
|
+
},
|
|
127
|
+
initialProjection: [],
|
|
128
|
+
projector: (entries, entry) => [
|
|
129
|
+
...entries,
|
|
130
|
+
entry
|
|
131
|
+
],
|
|
132
|
+
storage: input.storage,
|
|
133
|
+
now,
|
|
134
|
+
protocol: input.protocol,
|
|
135
|
+
seal: input.seal,
|
|
136
|
+
armour: input.armour,
|
|
137
|
+
autoCommit: false,
|
|
138
|
+
replayPolicy: {
|
|
139
|
+
verify: false,
|
|
140
|
+
decrypt: true
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
let state = {
|
|
144
|
+
ready: false,
|
|
145
|
+
integrityValid: false,
|
|
146
|
+
stagedCount: 0,
|
|
147
|
+
plugins: createInitialPluginState(plugins),
|
|
148
|
+
verification: null
|
|
149
|
+
};
|
|
150
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
151
|
+
function getPluginState(pluginId, source = state) {
|
|
152
|
+
assertKnownPlugin(pluginsById, pluginId);
|
|
153
|
+
return source.plugins[pluginId];
|
|
154
|
+
}
|
|
155
|
+
function publish(nextState) {
|
|
156
|
+
state = nextState;
|
|
157
|
+
for (const listener of listeners) {
|
|
158
|
+
listener(state);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function rebuildFromEntries(entriesValue, options) {
|
|
162
|
+
const entries = assertReplayEntries(entriesValue);
|
|
163
|
+
const nextState = {
|
|
164
|
+
ready: options.ready,
|
|
165
|
+
integrityValid: options.integrityValid,
|
|
166
|
+
stagedCount: ledger.getState().staged.length,
|
|
167
|
+
plugins: createInitialPluginState(plugins),
|
|
168
|
+
verification: options.verification
|
|
169
|
+
};
|
|
170
|
+
const replayView = {
|
|
171
|
+
getState() {
|
|
172
|
+
return nextState;
|
|
173
|
+
},
|
|
174
|
+
getPluginState(pluginId) {
|
|
175
|
+
return getPluginState(pluginId, nextState);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const replayContext = {
|
|
179
|
+
app: replayView
|
|
180
|
+
};
|
|
181
|
+
for (const target of projectionTargets) {
|
|
182
|
+
await target.reset();
|
|
183
|
+
}
|
|
184
|
+
for (const target of projectionTargets) {
|
|
185
|
+
await target.beginReplay?.(replayContext);
|
|
186
|
+
}
|
|
187
|
+
for (const entry of entries) {
|
|
188
|
+
for (const plugin of plugins) {
|
|
189
|
+
nextState.plugins[plugin.id] = plugin.project(
|
|
190
|
+
nextState.plugins[plugin.id],
|
|
191
|
+
entry,
|
|
192
|
+
{ decryptAvailable: Boolean(input.identity.decryptor) }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
for (const target of projectionTargets) {
|
|
196
|
+
await target.applyEntry(entry, replayContext);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
for (const target of projectionTargets) {
|
|
200
|
+
await target.endReplay?.(replayContext);
|
|
201
|
+
}
|
|
202
|
+
publish(nextState);
|
|
203
|
+
}
|
|
204
|
+
async function rebuildFromReplay(replayEntries, options) {
|
|
205
|
+
await rebuildFromEntries(replayEntries, options);
|
|
206
|
+
}
|
|
207
|
+
function requireReady() {
|
|
208
|
+
if (!state.ready) {
|
|
209
|
+
throw new ConcordBoundaryError(
|
|
210
|
+
"APP_NOT_READY",
|
|
211
|
+
"Concord app must be loaded or created before commands can run."
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function createCommandContext() {
|
|
216
|
+
return {
|
|
217
|
+
now,
|
|
218
|
+
identity: input.identity,
|
|
219
|
+
getPluginState(pluginId) {
|
|
220
|
+
return getPluginState(pluginId);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function publishIntegrityFailure(verification, resetPlugins) {
|
|
225
|
+
publish({
|
|
226
|
+
ready: false,
|
|
227
|
+
integrityValid: false,
|
|
228
|
+
stagedCount: ledger.getState().staged.length,
|
|
229
|
+
plugins: resetPlugins ? createInitialPluginState(plugins) : state.plugins,
|
|
230
|
+
verification
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async function ensureCommittedHistoryUsable(options) {
|
|
234
|
+
const verification = await ledger.verify();
|
|
235
|
+
if (verification.committedHistoryValid) {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
publishIntegrityFailure(
|
|
239
|
+
verification,
|
|
240
|
+
options?.resetPluginsOnFailure ?? true
|
|
241
|
+
);
|
|
242
|
+
if (options?.allowInspectionOnly) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
throw new ConcordBoundaryError(
|
|
246
|
+
"INVALID_COMMITTED_HISTORY",
|
|
247
|
+
"Concord cannot project runtime state from invalid committed history."
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
async function replay(options) {
|
|
251
|
+
if (!await ensureCommittedHistoryUsable()) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const replayEntries = await ledger.replay(options);
|
|
255
|
+
await rebuildFromReplay(replayEntries, {
|
|
256
|
+
ready: state.ready,
|
|
257
|
+
integrityValid: true,
|
|
258
|
+
verification: state.verification
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async function create(params) {
|
|
262
|
+
await ledger.create(params);
|
|
263
|
+
if (!await ensureCommittedHistoryUsable()) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
await rebuildFromReplay(await ledger.replay(), {
|
|
267
|
+
ready: true,
|
|
268
|
+
integrityValid: true,
|
|
269
|
+
verification: null
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
async function load() {
|
|
273
|
+
const loaded = await ledger.loadFromStorage();
|
|
274
|
+
if (!loaded) {
|
|
275
|
+
await ledger.create();
|
|
276
|
+
}
|
|
277
|
+
if (!await ensureCommittedHistoryUsable({
|
|
278
|
+
allowInspectionOnly: true,
|
|
279
|
+
resetPluginsOnFailure: true
|
|
280
|
+
})) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
await rebuildFromReplay(await ledger.replay(), {
|
|
284
|
+
ready: true,
|
|
285
|
+
integrityValid: true,
|
|
286
|
+
verification: null
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
async function command(type, inputValue) {
|
|
290
|
+
requireReady();
|
|
291
|
+
const registration = commands.get(type);
|
|
292
|
+
if (!registration) {
|
|
293
|
+
throw new ConcordBoundaryError(
|
|
294
|
+
"UNKNOWN_COMMAND",
|
|
295
|
+
`Unknown Concord command type: ${type}`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
const appendInputs = normalizeAppendInputs(
|
|
299
|
+
await registration.handler(createCommandContext(), inputValue)
|
|
300
|
+
);
|
|
301
|
+
const appendResults = await ledger.appendMany(appendInputs);
|
|
302
|
+
let commitResult;
|
|
303
|
+
if (policy.autoCommit) {
|
|
304
|
+
commitResult = await ledger.commit();
|
|
305
|
+
}
|
|
306
|
+
if (!await ensureCommittedHistoryUsable()) {
|
|
307
|
+
return {
|
|
308
|
+
commitId: commitResult?.commit.commitId,
|
|
309
|
+
entryIds: appendResults.map(
|
|
310
|
+
(result) => result.entry.entryId
|
|
311
|
+
),
|
|
312
|
+
stagedCount: ledger.getState().staged.length
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
await rebuildFromReplay(await ledger.replay(), {
|
|
316
|
+
ready: true,
|
|
317
|
+
integrityValid: true,
|
|
318
|
+
verification: null
|
|
319
|
+
});
|
|
320
|
+
return {
|
|
321
|
+
commitId: commitResult?.commit.commitId,
|
|
322
|
+
entryIds: appendResults.map(
|
|
323
|
+
(result) => result.entry.entryId
|
|
324
|
+
),
|
|
325
|
+
stagedCount: state.stagedCount
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
async function commit(input2) {
|
|
329
|
+
requireReady();
|
|
330
|
+
const result = await ledger.commit(input2);
|
|
331
|
+
if (!await ensureCommittedHistoryUsable()) {
|
|
332
|
+
return {
|
|
333
|
+
commitId: result.commit.commitId,
|
|
334
|
+
entryIds: result.committedEntryIds
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
await rebuildFromReplay(await ledger.replay(), {
|
|
338
|
+
ready: true,
|
|
339
|
+
integrityValid: true,
|
|
340
|
+
verification: null
|
|
341
|
+
});
|
|
342
|
+
return {
|
|
343
|
+
commitId: result.commit.commitId,
|
|
344
|
+
entryIds: result.committedEntryIds
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
async function recompute() {
|
|
348
|
+
if (!await ensureCommittedHistoryUsable()) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const replayEntries = await ledger.recompute();
|
|
352
|
+
await rebuildFromReplay(replayEntries, {
|
|
353
|
+
ready: state.ready,
|
|
354
|
+
integrityValid: true,
|
|
355
|
+
verification: state.verification
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
async function verify() {
|
|
359
|
+
const verification = await ledger.verify();
|
|
360
|
+
publish({
|
|
361
|
+
ready: state.ready && verification.committedHistoryValid,
|
|
362
|
+
integrityValid: verification.committedHistoryValid,
|
|
363
|
+
stagedCount: state.stagedCount,
|
|
364
|
+
plugins: state.plugins,
|
|
365
|
+
verification
|
|
366
|
+
});
|
|
367
|
+
return verification;
|
|
368
|
+
}
|
|
369
|
+
async function exportLedger() {
|
|
370
|
+
return ledger.export();
|
|
371
|
+
}
|
|
372
|
+
async function importLedger(container) {
|
|
373
|
+
await ledger.import(container);
|
|
374
|
+
if (!await ensureCommittedHistoryUsable({
|
|
375
|
+
allowInspectionOnly: true,
|
|
376
|
+
resetPluginsOnFailure: true
|
|
377
|
+
})) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
await rebuildFromReplay(await ledger.replay(), {
|
|
381
|
+
ready: true,
|
|
382
|
+
integrityValid: true,
|
|
383
|
+
verification: null
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function subscribe(listener) {
|
|
387
|
+
listeners.add(listener);
|
|
388
|
+
return () => {
|
|
389
|
+
listeners.delete(listener);
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
async function destroy() {
|
|
393
|
+
for (const target of projectionTargets) {
|
|
394
|
+
await target.destroy?.();
|
|
395
|
+
}
|
|
396
|
+
await ledger.destroy();
|
|
397
|
+
publish({
|
|
398
|
+
ready: false,
|
|
399
|
+
integrityValid: false,
|
|
400
|
+
stagedCount: 0,
|
|
401
|
+
plugins: createInitialPluginState(plugins),
|
|
402
|
+
verification: null
|
|
403
|
+
});
|
|
404
|
+
listeners.clear();
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
create,
|
|
408
|
+
load,
|
|
409
|
+
command,
|
|
410
|
+
commit,
|
|
411
|
+
replay,
|
|
412
|
+
recompute,
|
|
413
|
+
verify,
|
|
414
|
+
exportLedger,
|
|
415
|
+
importLedger,
|
|
416
|
+
getState() {
|
|
417
|
+
return state;
|
|
418
|
+
},
|
|
419
|
+
getPluginState(pluginId) {
|
|
420
|
+
return getPluginState(pluginId);
|
|
421
|
+
},
|
|
422
|
+
subscribe,
|
|
423
|
+
destroy
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
export {
|
|
427
|
+
ConcordBoundaryError,
|
|
428
|
+
createConcordApp
|
|
429
|
+
};
|
|
430
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/errors.ts","../src/app.ts"],"sourcesContent":["export class ConcordBoundaryError extends Error {\n code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = \"ConcordBoundaryError\";\n this.code = code;\n }\n}\n","import {\n createLedger,\n type LedgerAppendInput,\n type LedgerCommitResult,\n type LedgerContainer,\n type LedgerDecryptor,\n type LedgerIdentityContext,\n type LedgerInstance,\n type LedgerReplayEntry,\n type LedgerVerificationResult\n} from \"@ternent/ledger\";\nimport { ConcordBoundaryError } from \"./errors.js\";\nimport type {\n ConcordApp,\n ConcordCommandContext,\n ConcordCommandResult,\n ConcordCommitInput,\n ConcordCommitResult,\n ConcordCreateParams,\n ConcordPlugin,\n ConcordProjectionReplayContext,\n ConcordReplayOptions,\n ConcordState,\n CreateConcordAppInput\n} from \"./types.js\";\n\ntype CommandRegistration = {\n plugin: ConcordPlugin;\n handler: NonNullable<ConcordPlugin[\"commands\"]>[string];\n};\n\ntype RebuildStateOptions = {\n ready: boolean;\n integrityValid: boolean;\n verification: LedgerVerificationResult | null;\n};\n\nfunction createDefaultNow() {\n return new Date().toISOString();\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isLedgerReplayEntry(value: unknown): value is LedgerReplayEntry {\n if (!isObject(value)) {\n return false;\n }\n\n if (\n typeof value.entryId !== \"string\" ||\n typeof value.kind !== \"string\" ||\n typeof value.author !== \"string\" ||\n typeof value.authoredAt !== \"string\"\n ) {\n return false;\n }\n\n if (!(value.meta === null || isObject(value.meta))) {\n return false;\n }\n\n if (!isObject(value.payload) || typeof value.payload.type !== \"string\") {\n return false;\n }\n\n return (\n value.payload.type === \"plain\" ||\n value.payload.type === \"encrypted\" ||\n value.payload.type === \"decrypted\"\n );\n}\n\nfunction assertReplayEntries(value: unknown): LedgerReplayEntry[] {\n if (Array.isArray(value) && value.every(isLedgerReplayEntry)) {\n return value;\n }\n\n throw new ConcordBoundaryError(\n \"INVALID_LEDGER_PROJECTION\",\n \"Concord requires ledger replay output to be LedgerReplayEntry[].\"\n );\n}\n\nfunction createInitialPluginState(plugins: ConcordPlugin[]): Record<string, unknown> {\n return Object.fromEntries(plugins.map((plugin) => [plugin.id, plugin.initialState()]));\n}\n\nfunction assertKnownPlugin(\n pluginsById: Map<string, ConcordPlugin>,\n pluginId: string\n): void {\n if (!pluginsById.has(pluginId)) {\n throw new ConcordBoundaryError(\n \"UNKNOWN_PLUGIN\",\n `Unknown Concord plugin: ${pluginId}`\n );\n }\n}\n\nfunction normalizeAppendInputs(\n value: LedgerAppendInput | LedgerAppendInput[]\n): LedgerAppendInput[] {\n const inputs = Array.isArray(value) ? value : [value];\n if (inputs.length === 0) {\n throw new ConcordBoundaryError(\n \"COMMAND_PRODUCED_NO_ENTRIES\",\n \"Concord command handlers must return at least one LedgerAppendInput.\"\n );\n }\n return inputs;\n}\n\nfunction assertInternalLedgerRequirements(input: CreateConcordAppInput): void {\n if (typeof input.identity.author !== \"string\" || input.identity.author.length === 0) {\n throw new ConcordBoundaryError(\n \"INVALID_IDENTITY\",\n \"Concord requires identity.author when creating an internal ledger.\"\n );\n }\n\n if (!input.identity.signer) {\n throw new ConcordBoundaryError(\n \"INVALID_IDENTITY\",\n \"Concord requires identity.signer when creating an internal ledger.\"\n );\n }\n}\n\nexport async function createConcordApp(\n input: CreateConcordAppInput\n): Promise<ConcordApp> {\n const plugins = [...input.plugins];\n const projectionTargets = [...(input.projectionTargets ?? [])];\n const now = input.now ?? createDefaultNow;\n const policy = {\n autoCommit: input.policy?.autoCommit ?? false\n };\n\n const pluginsById = new Map<string, ConcordPlugin>();\n const commands = new Map<string, CommandRegistration>();\n const projectionTargetNames = new Set<string>();\n\n for (const plugin of plugins) {\n if (pluginsById.has(plugin.id)) {\n throw new ConcordBoundaryError(\n \"DUPLICATE_PLUGIN_ID\",\n `Duplicate Concord plugin id: ${plugin.id}`\n );\n }\n\n pluginsById.set(plugin.id, plugin);\n\n for (const [commandType, handler] of Object.entries(plugin.commands ?? {})) {\n if (commands.has(commandType)) {\n throw new ConcordBoundaryError(\n \"DUPLICATE_COMMAND_TYPE\",\n `Duplicate Concord command type: ${commandType}`\n );\n }\n\n commands.set(commandType, { plugin, handler });\n }\n }\n\n for (const target of projectionTargets) {\n if (projectionTargetNames.has(target.name)) {\n throw new ConcordBoundaryError(\n \"DUPLICATE_PROJECTION_TARGET_NAME\",\n `Duplicate Concord projection target name: ${target.name}`\n );\n }\n\n projectionTargetNames.add(target.name);\n }\n\n if (!input.ledger) {\n assertInternalLedgerRequirements(input);\n }\n\n const ledger =\n input.ledger ??\n (await createLedger<LedgerReplayEntry[]>({\n identity: {\n signer: input.identity.signer as LedgerIdentityContext[\"signer\"],\n authorResolver: () => input.identity.author,\n decryptor: input.identity.decryptor as LedgerDecryptor | undefined\n },\n initialProjection: [],\n projector: (entries: LedgerReplayEntry[], entry: LedgerReplayEntry) => [\n ...entries,\n entry\n ],\n storage: input.storage,\n now,\n protocol: input.protocol,\n seal: input.seal,\n armour: input.armour,\n autoCommit: false,\n replayPolicy: {\n verify: false,\n decrypt: true\n }\n }));\n\n let state: ConcordState = {\n ready: false,\n integrityValid: false,\n stagedCount: 0,\n plugins: createInitialPluginState(plugins),\n verification: null\n };\n\n const listeners = new Set<(value: Readonly<ConcordState>) => void>();\n\n function getPluginState<T = unknown>(pluginId: string, source = state): T {\n assertKnownPlugin(pluginsById, pluginId);\n return source.plugins[pluginId] as T;\n }\n\n function publish(nextState: ConcordState): void {\n state = nextState;\n for (const listener of listeners) {\n listener(state);\n }\n }\n\n async function rebuildFromEntries(\n entriesValue: unknown,\n options: RebuildStateOptions\n ): Promise<void> {\n const entries = assertReplayEntries(entriesValue);\n const nextState: ConcordState = {\n ready: options.ready,\n integrityValid: options.integrityValid,\n stagedCount: ledger.getState().staged.length,\n plugins: createInitialPluginState(plugins),\n verification: options.verification\n };\n\n const replayView = {\n getState(): Readonly<ConcordState> {\n return nextState;\n },\n getPluginState<T = unknown>(pluginId: string): T {\n return getPluginState<T>(pluginId, nextState);\n }\n };\n\n const replayContext: ConcordProjectionReplayContext = {\n app: replayView\n };\n\n for (const target of projectionTargets) {\n await target.reset();\n }\n\n for (const target of projectionTargets) {\n await target.beginReplay?.(replayContext);\n }\n\n for (const entry of entries) {\n for (const plugin of plugins) {\n nextState.plugins[plugin.id] = plugin.project(\n nextState.plugins[plugin.id],\n entry,\n { decryptAvailable: Boolean(input.identity.decryptor) }\n );\n }\n\n for (const target of projectionTargets) {\n await target.applyEntry(entry, replayContext);\n }\n }\n\n for (const target of projectionTargets) {\n await target.endReplay?.(replayContext);\n }\n\n publish(nextState);\n }\n\n async function rebuildFromReplay(\n replayEntries: unknown,\n options: RebuildStateOptions\n ): Promise<void> {\n await rebuildFromEntries(replayEntries, options);\n }\n\n function requireReady(): void {\n if (!state.ready) {\n throw new ConcordBoundaryError(\n \"APP_NOT_READY\",\n \"Concord app must be loaded or created before commands can run.\"\n );\n }\n }\n\n function createCommandContext(): ConcordCommandContext {\n return {\n now,\n identity: input.identity,\n getPluginState<T = unknown>(pluginId: string): T {\n return getPluginState<T>(pluginId);\n }\n };\n }\n\n function publishIntegrityFailure(\n verification: LedgerVerificationResult,\n resetPlugins: boolean\n ): void {\n publish({\n ready: false,\n integrityValid: false,\n stagedCount: ledger.getState().staged.length,\n plugins: resetPlugins ? createInitialPluginState(plugins) : state.plugins,\n verification\n });\n }\n\n async function ensureCommittedHistoryUsable(options?: {\n allowInspectionOnly?: boolean;\n resetPluginsOnFailure?: boolean;\n }): Promise<boolean> {\n const verification = await ledger.verify();\n if (verification.committedHistoryValid) {\n return true;\n }\n\n publishIntegrityFailure(\n verification,\n options?.resetPluginsOnFailure ?? true\n );\n\n if (options?.allowInspectionOnly) {\n return false;\n }\n\n throw new ConcordBoundaryError(\n \"INVALID_COMMITTED_HISTORY\",\n \"Concord cannot project runtime state from invalid committed history.\"\n );\n }\n\n async function replay(options?: ConcordReplayOptions): Promise<void> {\n if (!(await ensureCommittedHistoryUsable())) {\n return;\n }\n\n const replayEntries = await ledger.replay(options);\n await rebuildFromReplay(replayEntries, {\n ready: state.ready,\n integrityValid: true,\n verification: state.verification\n });\n }\n\n async function create(params?: ConcordCreateParams): Promise<void> {\n await ledger.create(params);\n\n if (!(await ensureCommittedHistoryUsable())) {\n return;\n }\n\n await rebuildFromReplay(await ledger.replay(), {\n ready: true,\n integrityValid: true,\n verification: null\n });\n }\n\n async function load(): Promise<void> {\n const loaded = await ledger.loadFromStorage();\n if (!loaded) {\n await ledger.create();\n }\n\n if (\n !(await ensureCommittedHistoryUsable({\n allowInspectionOnly: true,\n resetPluginsOnFailure: true\n }))\n ) {\n return;\n }\n\n await rebuildFromReplay(await ledger.replay(), {\n ready: true,\n integrityValid: true,\n verification: null\n });\n }\n\n async function command<TInput = unknown>(\n type: string,\n inputValue: TInput\n ): Promise<ConcordCommandResult> {\n requireReady();\n\n const registration = commands.get(type);\n if (!registration) {\n throw new ConcordBoundaryError(\n \"UNKNOWN_COMMAND\",\n `Unknown Concord command type: ${type}`\n );\n }\n\n const appendInputs = normalizeAppendInputs(\n await registration.handler(createCommandContext(), inputValue)\n );\n\n const appendResults = await ledger.appendMany(appendInputs);\n let commitResult: LedgerCommitResult | undefined;\n\n if (policy.autoCommit) {\n commitResult = await ledger.commit();\n }\n\n if (!(await ensureCommittedHistoryUsable())) {\n return {\n commitId: commitResult?.commit.commitId,\n entryIds: appendResults.map(\n (result: { entry: { entryId: string } }) => result.entry.entryId\n ),\n stagedCount: ledger.getState().staged.length\n };\n }\n\n await rebuildFromReplay(await ledger.replay(), {\n ready: true,\n integrityValid: true,\n verification: null\n });\n\n return {\n commitId: commitResult?.commit.commitId,\n entryIds: appendResults.map(\n (result: { entry: { entryId: string } }) => result.entry.entryId\n ),\n stagedCount: state.stagedCount\n };\n }\n\n async function commit(\n input?: ConcordCommitInput\n ): Promise<ConcordCommitResult> {\n requireReady();\n\n const result = await ledger.commit(input);\n\n if (!(await ensureCommittedHistoryUsable())) {\n return {\n commitId: result.commit.commitId,\n entryIds: result.committedEntryIds\n };\n }\n\n await rebuildFromReplay(await ledger.replay(), {\n ready: true,\n integrityValid: true,\n verification: null\n });\n\n return {\n commitId: result.commit.commitId,\n entryIds: result.committedEntryIds\n };\n }\n\n async function recompute(): Promise<void> {\n if (!(await ensureCommittedHistoryUsable())) {\n return;\n }\n\n const replayEntries = await ledger.recompute();\n await rebuildFromReplay(replayEntries, {\n ready: state.ready,\n integrityValid: true,\n verification: state.verification\n });\n }\n\n async function verify(): Promise<LedgerVerificationResult> {\n const verification = await ledger.verify();\n publish({\n ready: state.ready && verification.committedHistoryValid,\n integrityValid: verification.committedHistoryValid,\n stagedCount: state.stagedCount,\n plugins: state.plugins,\n verification\n });\n return verification;\n }\n\n async function exportLedger(): Promise<LedgerContainer> {\n return ledger.export();\n }\n\n async function importLedger(container: LedgerContainer): Promise<void> {\n await ledger.import(container);\n\n if (\n !(await ensureCommittedHistoryUsable({\n allowInspectionOnly: true,\n resetPluginsOnFailure: true\n }))\n ) {\n return;\n }\n\n await rebuildFromReplay(await ledger.replay(), {\n ready: true,\n integrityValid: true,\n verification: null\n });\n }\n\n function subscribe(\n listener: (value: Readonly<ConcordState>) => void\n ): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n }\n\n async function destroy(): Promise<void> {\n for (const target of projectionTargets) {\n await target.destroy?.();\n }\n\n await ledger.destroy();\n publish({\n ready: false,\n integrityValid: false,\n stagedCount: 0,\n plugins: createInitialPluginState(plugins),\n verification: null\n });\n listeners.clear();\n }\n\n return {\n create,\n load,\n command,\n commit,\n replay,\n recompute,\n verify,\n exportLedger,\n importLedger,\n getState() {\n return state;\n },\n getPluginState<T = unknown>(pluginId: string): T {\n return getPluginState<T>(pluginId);\n },\n subscribe,\n destroy\n };\n}\n"],"names":["input"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ternent/concord",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Command-driven developer runtime for building non-custodial apps on @ternent/ledger",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"SPEC.md"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"module": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@ternent/ledger": "0.1.3"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^24.3.0",
|
|
30
|
+
"typescript": "^5.6.3",
|
|
31
|
+
"vite": "^4.5.3",
|
|
32
|
+
"vite-plugin-dts": "^1.7.3",
|
|
33
|
+
"vitest": "^1.6.0",
|
|
34
|
+
"@ternent/armour": "0.1.3",
|
|
35
|
+
"@ternent/identity": "0.3.0",
|
|
36
|
+
"@ternent/rage": "0.1.2",
|
|
37
|
+
"@ternent/seal-cli": "0.3.5"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "pnpm --filter @ternent/rage build && vite build",
|
|
41
|
+
"test": "pnpm --filter @ternent/rage build && vitest run"
|
|
42
|
+
}
|
|
43
|
+
}
|