@aetherwing/fcp-core 0.1.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/event-log.d.ts +78 -0
- package/dist/event-log.d.ts.map +1 -0
- package/dist/event-log.js +184 -0
- package/dist/event-log.js.map +1 -0
- package/dist/formatter.d.ts +19 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +64 -0
- package/dist/formatter.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/parsed-op.d.ts +32 -0
- package/dist/parsed-op.d.ts.map +1 -0
- package/dist/parsed-op.js +41 -0
- package/dist/parsed-op.js.map +1 -0
- package/dist/server.d.ts +71 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +140 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +40 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +142 -0
- package/dist/session.js.map +1 -0
- package/dist/tokenizer.d.ts +26 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/tokenizer.js +114 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/verb-registry.d.ts +41 -0
- package/dist/verb-registry.d.ts.map +1 -0
- package/dist/verb-registry.js +65 -0
- package/dist/verb-registry.js.map +1 -0
- package/package.json +30 -0
- package/src/event-log.ts +209 -0
- package/src/formatter.ts +70 -0
- package/src/index.ts +40 -0
- package/src/parsed-op.ts +64 -0
- package/src/server.ts +241 -0
- package/src/session.ts +163 -0
- package/src/tokenizer.ts +108 -0
- package/src/verb-registry.ts +84 -0
- package/tests/event-log.test.ts +177 -0
- package/tests/formatter.test.ts +61 -0
- package/tests/parsed-op.test.ts +95 -0
- package/tests/server.test.ts +94 -0
- package/tests/session.test.ts +210 -0
- package/tests/tokenizer.test.ts +144 -0
- package/tests/verb-registry.test.ts +76 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic cursor-based event log with undo/redo and named checkpoints.
|
|
3
|
+
*
|
|
4
|
+
* Events are appended at the cursor position. The cursor always points
|
|
5
|
+
* one past the last applied event. Undo moves the cursor back; redo
|
|
6
|
+
* moves it forward. Appending a new event truncates the redo tail.
|
|
7
|
+
*
|
|
8
|
+
* Checkpoint sentinels are stored in the log but skipped during
|
|
9
|
+
* undo/redo traversal.
|
|
10
|
+
*/
|
|
11
|
+
export declare class EventLog<T> {
|
|
12
|
+
private events;
|
|
13
|
+
private _cursor;
|
|
14
|
+
private checkpoints;
|
|
15
|
+
/**
|
|
16
|
+
* Append an event, truncating any redo history beyond the cursor.
|
|
17
|
+
*/
|
|
18
|
+
append(event: T): void;
|
|
19
|
+
/**
|
|
20
|
+
* Create a named checkpoint at the current cursor position.
|
|
21
|
+
*/
|
|
22
|
+
checkpoint(name: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Undo up to `count` non-checkpoint events. Returns events in reverse
|
|
25
|
+
* order (most recent first) for the caller to reverse-apply.
|
|
26
|
+
*/
|
|
27
|
+
undo(count?: number): T[];
|
|
28
|
+
/**
|
|
29
|
+
* Undo to a named checkpoint. Returns events in reverse order.
|
|
30
|
+
* Returns null if the checkpoint doesn't exist or is at/beyond cursor.
|
|
31
|
+
*/
|
|
32
|
+
undoTo(name: string): T[] | null;
|
|
33
|
+
/**
|
|
34
|
+
* Redo up to `count` non-checkpoint events. Returns events in forward
|
|
35
|
+
* order for the caller to re-apply.
|
|
36
|
+
*/
|
|
37
|
+
redo(count?: number): T[];
|
|
38
|
+
/**
|
|
39
|
+
* Get the last N non-checkpoint events (up to cursor). Returned in
|
|
40
|
+
* chronological order (oldest first).
|
|
41
|
+
*/
|
|
42
|
+
recent(count?: number): T[];
|
|
43
|
+
/**
|
|
44
|
+
* Current cursor position (one past last applied event).
|
|
45
|
+
*/
|
|
46
|
+
get cursor(): number;
|
|
47
|
+
/**
|
|
48
|
+
* Total number of entries in the log (including checkpoints).
|
|
49
|
+
*/
|
|
50
|
+
get length(): number;
|
|
51
|
+
/**
|
|
52
|
+
* Whether there are events before the cursor that can be undone.
|
|
53
|
+
*/
|
|
54
|
+
canUndo(): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Whether there are events after the cursor that can be redone.
|
|
57
|
+
*/
|
|
58
|
+
canRedo(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Get the event index for a named checkpoint.
|
|
61
|
+
* Returns undefined if the checkpoint doesn't exist.
|
|
62
|
+
*/
|
|
63
|
+
getCheckpointIndex(name: string): number | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Number of named checkpoints.
|
|
66
|
+
*/
|
|
67
|
+
get checkpointCount(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Get all checkpoint names and their indices.
|
|
70
|
+
*/
|
|
71
|
+
getCheckpoints(): Map<string, number>;
|
|
72
|
+
/**
|
|
73
|
+
* Get non-checkpoint events from a given index to the cursor.
|
|
74
|
+
* Used for diff queries.
|
|
75
|
+
*/
|
|
76
|
+
eventsSince(fromIndex: number): T[];
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=event-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log.d.ts","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;GASG;AACH,qBAAa,QAAQ,CAAC,CAAC;IACrB,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,WAAW,CAA6B;IAEhD;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IActB;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO9B;;;OAGG;IACH,IAAI,CAAC,KAAK,GAAE,MAAU,GAAG,CAAC,EAAE;IAkB5B;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI;IAehC;;;OAGG;IACH,IAAI,CAAC,KAAK,GAAE,MAAU,GAAG,CAAC,EAAE;IAkB5B;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE;IAY3B;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;OAEG;IACH,OAAO,IAAI,OAAO;IAOlB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;;OAGG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD;;OAEG;IACH,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED;;OAEG;IACH,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIrC;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,EAAE;CAWpC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel object stored in the event log to mark a checkpoint.
|
|
3
|
+
*/
|
|
4
|
+
const CHECKPOINT_SENTINEL = Symbol("checkpoint");
|
|
5
|
+
function isCheckpoint(entry) {
|
|
6
|
+
return (typeof entry === "object" &&
|
|
7
|
+
entry !== null &&
|
|
8
|
+
"__type" in entry &&
|
|
9
|
+
entry.__type === CHECKPOINT_SENTINEL);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generic cursor-based event log with undo/redo and named checkpoints.
|
|
13
|
+
*
|
|
14
|
+
* Events are appended at the cursor position. The cursor always points
|
|
15
|
+
* one past the last applied event. Undo moves the cursor back; redo
|
|
16
|
+
* moves it forward. Appending a new event truncates the redo tail.
|
|
17
|
+
*
|
|
18
|
+
* Checkpoint sentinels are stored in the log but skipped during
|
|
19
|
+
* undo/redo traversal.
|
|
20
|
+
*/
|
|
21
|
+
export class EventLog {
|
|
22
|
+
events = [];
|
|
23
|
+
_cursor = 0;
|
|
24
|
+
checkpoints = new Map();
|
|
25
|
+
/**
|
|
26
|
+
* Append an event, truncating any redo history beyond the cursor.
|
|
27
|
+
*/
|
|
28
|
+
append(event) {
|
|
29
|
+
if (this._cursor < this.events.length) {
|
|
30
|
+
this.events.length = this._cursor;
|
|
31
|
+
// Remove checkpoints pointing beyond new length
|
|
32
|
+
for (const [name, idx] of this.checkpoints) {
|
|
33
|
+
if (idx > this._cursor) {
|
|
34
|
+
this.checkpoints.delete(name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
this.events.push(event);
|
|
39
|
+
this._cursor = this.events.length;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a named checkpoint at the current cursor position.
|
|
43
|
+
*/
|
|
44
|
+
checkpoint(name) {
|
|
45
|
+
this.checkpoints.set(name, this._cursor);
|
|
46
|
+
const sentinel = { __type: CHECKPOINT_SENTINEL, name };
|
|
47
|
+
this.events.push(sentinel);
|
|
48
|
+
this._cursor = this.events.length;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Undo up to `count` non-checkpoint events. Returns events in reverse
|
|
52
|
+
* order (most recent first) for the caller to reverse-apply.
|
|
53
|
+
*/
|
|
54
|
+
undo(count = 1) {
|
|
55
|
+
const result = [];
|
|
56
|
+
let pos = this._cursor - 1;
|
|
57
|
+
let undone = 0;
|
|
58
|
+
while (pos >= 0 && undone < count) {
|
|
59
|
+
const entry = this.events[pos];
|
|
60
|
+
if (!isCheckpoint(entry)) {
|
|
61
|
+
result.push(entry);
|
|
62
|
+
undone++;
|
|
63
|
+
}
|
|
64
|
+
pos--;
|
|
65
|
+
}
|
|
66
|
+
this._cursor = pos + 1;
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Undo to a named checkpoint. Returns events in reverse order.
|
|
71
|
+
* Returns null if the checkpoint doesn't exist or is at/beyond cursor.
|
|
72
|
+
*/
|
|
73
|
+
undoTo(name) {
|
|
74
|
+
const target = this.checkpoints.get(name);
|
|
75
|
+
if (target === undefined || target >= this._cursor)
|
|
76
|
+
return null;
|
|
77
|
+
const result = [];
|
|
78
|
+
for (let i = this._cursor - 1; i >= target; i--) {
|
|
79
|
+
const entry = this.events[i];
|
|
80
|
+
if (!isCheckpoint(entry)) {
|
|
81
|
+
result.push(entry);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
this._cursor = target;
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Redo up to `count` non-checkpoint events. Returns events in forward
|
|
89
|
+
* order for the caller to re-apply.
|
|
90
|
+
*/
|
|
91
|
+
redo(count = 1) {
|
|
92
|
+
const result = [];
|
|
93
|
+
let pos = this._cursor;
|
|
94
|
+
let redone = 0;
|
|
95
|
+
while (pos < this.events.length && redone < count) {
|
|
96
|
+
const entry = this.events[pos];
|
|
97
|
+
if (!isCheckpoint(entry)) {
|
|
98
|
+
result.push(entry);
|
|
99
|
+
redone++;
|
|
100
|
+
}
|
|
101
|
+
pos++;
|
|
102
|
+
}
|
|
103
|
+
this._cursor = pos;
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get the last N non-checkpoint events (up to cursor). Returned in
|
|
108
|
+
* chronological order (oldest first).
|
|
109
|
+
*/
|
|
110
|
+
recent(count) {
|
|
111
|
+
const limit = count ?? this._cursor;
|
|
112
|
+
const result = [];
|
|
113
|
+
for (let i = this._cursor - 1; i >= 0 && result.length < limit; i--) {
|
|
114
|
+
const entry = this.events[i];
|
|
115
|
+
if (!isCheckpoint(entry)) {
|
|
116
|
+
result.push(entry);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return result.reverse();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Current cursor position (one past last applied event).
|
|
123
|
+
*/
|
|
124
|
+
get cursor() {
|
|
125
|
+
return this._cursor;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Total number of entries in the log (including checkpoints).
|
|
129
|
+
*/
|
|
130
|
+
get length() {
|
|
131
|
+
return this.events.length;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Whether there are events before the cursor that can be undone.
|
|
135
|
+
*/
|
|
136
|
+
canUndo() {
|
|
137
|
+
for (let i = this._cursor - 1; i >= 0; i--) {
|
|
138
|
+
if (!isCheckpoint(this.events[i]))
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Whether there are events after the cursor that can be redone.
|
|
145
|
+
*/
|
|
146
|
+
canRedo() {
|
|
147
|
+
return this._cursor < this.events.length;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get the event index for a named checkpoint.
|
|
151
|
+
* Returns undefined if the checkpoint doesn't exist.
|
|
152
|
+
*/
|
|
153
|
+
getCheckpointIndex(name) {
|
|
154
|
+
return this.checkpoints.get(name);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Number of named checkpoints.
|
|
158
|
+
*/
|
|
159
|
+
get checkpointCount() {
|
|
160
|
+
return this.checkpoints.size;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get all checkpoint names and their indices.
|
|
164
|
+
*/
|
|
165
|
+
getCheckpoints() {
|
|
166
|
+
return new Map(this.checkpoints);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get non-checkpoint events from a given index to the cursor.
|
|
170
|
+
* Used for diff queries.
|
|
171
|
+
*/
|
|
172
|
+
eventsSince(fromIndex) {
|
|
173
|
+
const result = [];
|
|
174
|
+
const end = Math.min(this._cursor, this.events.length);
|
|
175
|
+
for (let i = fromIndex; i < end; i++) {
|
|
176
|
+
const entry = this.events[i];
|
|
177
|
+
if (!isCheckpoint(entry)) {
|
|
178
|
+
result.push(entry);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=event-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log.js","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a result message with a prefix character.
|
|
3
|
+
*
|
|
4
|
+
* Prefix conventions:
|
|
5
|
+
* + created
|
|
6
|
+
* ~ modified (edge/connection)
|
|
7
|
+
* * changed (property)
|
|
8
|
+
* - removed
|
|
9
|
+
* ! meta/group operation
|
|
10
|
+
* @ bulk/layout operation
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatResult(success: boolean, message: string, prefix?: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Suggest a correction for a misspelled input by finding the closest
|
|
15
|
+
* candidate using Levenshtein distance. Returns null if no candidate
|
|
16
|
+
* is close enough (distance > 3).
|
|
17
|
+
*/
|
|
18
|
+
export declare function suggest(input: string, candidates: string[]): string | null;
|
|
19
|
+
//# sourceMappingURL=formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAe1E"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a result message with a prefix character.
|
|
3
|
+
*
|
|
4
|
+
* Prefix conventions:
|
|
5
|
+
* + created
|
|
6
|
+
* ~ modified (edge/connection)
|
|
7
|
+
* * changed (property)
|
|
8
|
+
* - removed
|
|
9
|
+
* ! meta/group operation
|
|
10
|
+
* @ bulk/layout operation
|
|
11
|
+
*/
|
|
12
|
+
export function formatResult(success, message, prefix) {
|
|
13
|
+
if (!success)
|
|
14
|
+
return `ERROR: ${message}`;
|
|
15
|
+
if (prefix)
|
|
16
|
+
return `${prefix} ${message}`;
|
|
17
|
+
return message;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Suggest a correction for a misspelled input by finding the closest
|
|
21
|
+
* candidate using Levenshtein distance. Returns null if no candidate
|
|
22
|
+
* is close enough (distance > 3).
|
|
23
|
+
*/
|
|
24
|
+
export function suggest(input, candidates) {
|
|
25
|
+
if (candidates.length === 0)
|
|
26
|
+
return null;
|
|
27
|
+
let best = null;
|
|
28
|
+
let bestDist = Infinity;
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
const dist = levenshtein(input.toLowerCase(), candidate.toLowerCase());
|
|
31
|
+
if (dist < bestDist) {
|
|
32
|
+
bestDist = dist;
|
|
33
|
+
best = candidate;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return bestDist <= 3 ? best : null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Compute Levenshtein distance between two strings.
|
|
40
|
+
*/
|
|
41
|
+
function levenshtein(a, b) {
|
|
42
|
+
const m = a.length;
|
|
43
|
+
const n = b.length;
|
|
44
|
+
// Use a single-row DP array
|
|
45
|
+
const prev = new Array(n + 1);
|
|
46
|
+
for (let j = 0; j <= n; j++)
|
|
47
|
+
prev[j] = j;
|
|
48
|
+
for (let i = 1; i <= m; i++) {
|
|
49
|
+
let prevDiag = prev[0];
|
|
50
|
+
prev[0] = i;
|
|
51
|
+
for (let j = 1; j <= n; j++) {
|
|
52
|
+
const temp = prev[j];
|
|
53
|
+
if (a[i - 1] === b[j - 1]) {
|
|
54
|
+
prev[j] = prevDiag;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
prev[j] = 1 + Math.min(prevDiag, prev[j - 1], prev[j]);
|
|
58
|
+
}
|
|
59
|
+
prevDiag = temp;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return prev[n];
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAgB,EAChB,OAAe,EACf,MAAe;IAEf,IAAI,CAAC,OAAO;QAAE,OAAO,UAAU,OAAO,EAAE,CAAC;IACzC,IAAI,MAAM;QAAE,OAAO,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;IAC1C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,UAAoB;IACzD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,QAAQ,GAAG,QAAQ,CAAC;IAExB,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QACvE,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;YACpB,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,GAAG,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,CAAS,EAAE,CAAS;IACvC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAEnB,4BAA4B;IAC5B,MAAM,IAAI,GAAG,IAAI,KAAK,CAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACrB,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,CAAC;YACD,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { tokenize, isKeyValue, parseKeyValue, isArrow, isSelector, } from "./tokenizer.js";
|
|
2
|
+
export { parseOp, isParseError, type ParsedOp, type ParseError, } from "./parsed-op.js";
|
|
3
|
+
export { EventLog } from "./event-log.js";
|
|
4
|
+
export { VerbRegistry, type VerbSpec } from "./verb-registry.js";
|
|
5
|
+
export { SessionDispatcher, type SessionHooks, } from "./session.js";
|
|
6
|
+
export { formatResult, suggest } from "./formatter.js";
|
|
7
|
+
export { createFcpServer, type FcpServerConfig, type FcpDomainAdapter, type OpResult, type QueryResult, } from "./server.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,OAAO,EACP,UAAU,GACX,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,OAAO,EACP,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EACL,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGvD,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Tokenizer
|
|
2
|
+
export { tokenize, isKeyValue, parseKeyValue, isArrow, isSelector, } from "./tokenizer.js";
|
|
3
|
+
// Parsed operation
|
|
4
|
+
export { parseOp, isParseError, } from "./parsed-op.js";
|
|
5
|
+
// Event log
|
|
6
|
+
export { EventLog } from "./event-log.js";
|
|
7
|
+
// Verb registry
|
|
8
|
+
export { VerbRegistry } from "./verb-registry.js";
|
|
9
|
+
// Session
|
|
10
|
+
export { SessionDispatcher, } from "./session.js";
|
|
11
|
+
// Formatter
|
|
12
|
+
export { formatResult, suggest } from "./formatter.js";
|
|
13
|
+
// Server
|
|
14
|
+
export { createFcpServer, } from "./server.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,OAAO,EACP,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,mBAAmB;AACnB,OAAO,EACL,OAAO,EACP,YAAY,GAGb,MAAM,gBAAgB,CAAC;AAExB,YAAY;AACZ,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAiB,MAAM,oBAAoB,CAAC;AAEjE,UAAU;AACV,OAAO,EACL,iBAAiB,GAElB,MAAM,cAAc,CAAC;AAEtB,YAAY;AACZ,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEvD,SAAS;AACT,OAAO,EACL,eAAe,GAKhB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A successfully parsed operation.
|
|
3
|
+
*/
|
|
4
|
+
export interface ParsedOp {
|
|
5
|
+
verb: string;
|
|
6
|
+
positionals: string[];
|
|
7
|
+
params: Record<string, string>;
|
|
8
|
+
selectors: string[];
|
|
9
|
+
raw: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A parse failure.
|
|
13
|
+
*/
|
|
14
|
+
export interface ParseError {
|
|
15
|
+
success: false;
|
|
16
|
+
error: string;
|
|
17
|
+
raw: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse an operation string into a structured ParsedOp.
|
|
21
|
+
*
|
|
22
|
+
* First token becomes the verb. Remaining tokens are classified:
|
|
23
|
+
* @-prefixed -> selectors
|
|
24
|
+
* key:value -> params
|
|
25
|
+
* everything else -> positionals (in order)
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseOp(input: string): ParsedOp | ParseError;
|
|
28
|
+
/**
|
|
29
|
+
* Type guard: check if a parse result is an error.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isParseError(result: ParsedOp | ParseError): result is ParseError;
|
|
32
|
+
//# sourceMappingURL=parsed-op.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parsed-op.d.ts","sourceRoot":"","sources":["../src/parsed-op.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CA0B5D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,IAAI,UAAU,CAEhF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { tokenize, isKeyValue, parseKeyValue, isSelector } from "./tokenizer.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parse an operation string into a structured ParsedOp.
|
|
4
|
+
*
|
|
5
|
+
* First token becomes the verb. Remaining tokens are classified:
|
|
6
|
+
* @-prefixed -> selectors
|
|
7
|
+
* key:value -> params
|
|
8
|
+
* everything else -> positionals (in order)
|
|
9
|
+
*/
|
|
10
|
+
export function parseOp(input) {
|
|
11
|
+
const raw = input.trim();
|
|
12
|
+
const tokens = tokenize(raw);
|
|
13
|
+
if (tokens.length === 0) {
|
|
14
|
+
return { success: false, error: "empty operation", raw };
|
|
15
|
+
}
|
|
16
|
+
const verb = tokens[0].toLowerCase();
|
|
17
|
+
const positionals = [];
|
|
18
|
+
const params = {};
|
|
19
|
+
const selectors = [];
|
|
20
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
21
|
+
const token = tokens[i];
|
|
22
|
+
if (isSelector(token)) {
|
|
23
|
+
selectors.push(token);
|
|
24
|
+
}
|
|
25
|
+
else if (isKeyValue(token)) {
|
|
26
|
+
const { key, value } = parseKeyValue(token);
|
|
27
|
+
params[key] = value;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
positionals.push(token);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { verb, positionals, params, selectors, raw };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Type guard: check if a parse result is an error.
|
|
37
|
+
*/
|
|
38
|
+
export function isParseError(result) {
|
|
39
|
+
return "success" in result && result.success === false;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=parsed-op.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parsed-op.js","sourceRoot":"","sources":["../src/parsed-op.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAW,MAAM,gBAAgB,CAAC;AAsB1F;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE7B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAA6B;IACxD,OAAO,SAAS,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC;AACzD,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { EventLog } from "./event-log.js";
|
|
3
|
+
import type { ParsedOp } from "./parsed-op.js";
|
|
4
|
+
import type { VerbSpec } from "./verb-registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Result of executing a single operation.
|
|
7
|
+
*/
|
|
8
|
+
export interface OpResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
prefix?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Result of a query that may include an image.
|
|
15
|
+
*/
|
|
16
|
+
export interface QueryResult {
|
|
17
|
+
text: string;
|
|
18
|
+
image?: {
|
|
19
|
+
base64: string;
|
|
20
|
+
mimeType: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Domain adapter that an FCP domain must implement.
|
|
25
|
+
*/
|
|
26
|
+
export interface FcpDomainAdapter<Model, Event> {
|
|
27
|
+
/** Create a new empty model. */
|
|
28
|
+
createEmpty(title: string, params: Record<string, string>): Model;
|
|
29
|
+
/** Serialize model to saveable format. */
|
|
30
|
+
serialize(model: Model): Buffer | string;
|
|
31
|
+
/** Deserialize from file contents. */
|
|
32
|
+
deserialize(data: Buffer | string): Model;
|
|
33
|
+
/** Rebuild derived indices (e.g., after undo/redo). */
|
|
34
|
+
rebuildIndices(model: Model): void;
|
|
35
|
+
/** Return a compact digest for drift detection. */
|
|
36
|
+
getDigest(model: Model): string;
|
|
37
|
+
/** Execute a parsed operation against the model. */
|
|
38
|
+
dispatchOp(op: ParsedOp, model: Model, log: EventLog<Event>): OpResult | Promise<OpResult>;
|
|
39
|
+
/** Execute a query against the model. */
|
|
40
|
+
dispatchQuery(query: string, model: Model): string | QueryResult | Promise<string | QueryResult>;
|
|
41
|
+
/** Reverse a single event (for undo). */
|
|
42
|
+
reverseEvent(event: Event, model: Model): void;
|
|
43
|
+
/** Replay a single event (for redo). */
|
|
44
|
+
replayEvent(event: Event, model: Model): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Configuration for creating an FCP MCP server.
|
|
48
|
+
*/
|
|
49
|
+
export interface FcpServerConfig<Model, Event> {
|
|
50
|
+
/** Domain name (e.g., "midi", "studio"). Used as tool name prefix. */
|
|
51
|
+
domain: string;
|
|
52
|
+
/** Domain adapter implementing all domain-specific logic. */
|
|
53
|
+
adapter: FcpDomainAdapter<Model, Event>;
|
|
54
|
+
/** Verb specifications for this domain. */
|
|
55
|
+
verbs: VerbSpec[];
|
|
56
|
+
/** Optional reference card configuration. */
|
|
57
|
+
referenceCard?: {
|
|
58
|
+
sections?: Record<string, string>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create an MCP server wired up with FCP conventions.
|
|
63
|
+
*
|
|
64
|
+
* Registers 4 tools:
|
|
65
|
+
* {domain} — primary mutation tool (ops array)
|
|
66
|
+
* {domain}_query — read-only queries
|
|
67
|
+
* {domain}_session — lifecycle (new, open, save, checkpoint, undo, redo)
|
|
68
|
+
* {domain}_help — reference card
|
|
69
|
+
*/
|
|
70
|
+
export declare function createFcpServer<Model, Event>(config: FcpServerConfig<Model, Event>): McpServer;
|
|
71
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAInD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,EAAE,KAAK;IAC5C,gCAAgC;IAChC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IAClE,0CAA0C;IAC1C,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACzC,sCAAsC;IACtC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IAC1C,uDAAuD;IACvD,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACnC,mDAAmD;IACnD,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;IAChC,oDAAoD;IACpD,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3F,yCAAyC;IACzC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;IACjG,yCAAyC;IACzC,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC/C,wCAAwC;IACxC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,KAAK,EAAE,KAAK;IAC3C,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,EAAE,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACxC,2CAA2C;IAC3C,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,6CAA6C;IAC7C,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CACvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAC1C,MAAM,EAAE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GACpC,SAAS,CAoKX"}
|