@crowdedkingdoms/crowdyjs 8.22.0 → 9.0.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/MIGRATION.md +44 -0
- package/README.md +74 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/live-coding/assets/browser-authoring-index.json +1 -0
- package/dist/live-coding/assets/manifest.json +19 -0
- package/dist/live-coding/assets/tree-sitter-rust.wasm +0 -0
- package/dist/live-coding/assets/web-tree-sitter.wasm +0 -0
- package/dist/live-coding/browser-authoring-index.generated.d.ts +2 -0
- package/dist/live-coding/browser-authoring-index.generated.d.ts.map +1 -0
- package/dist/live-coding/browser-authoring-index.generated.js +5127 -0
- package/dist/live-coding/ide.d.ts +14 -7
- package/dist/live-coding/ide.d.ts.map +1 -1
- package/dist/live-coding/ide.js +296 -181
- package/dist/live-coding/index.d.ts +8 -0
- package/dist/live-coding/index.d.ts.map +1 -0
- package/dist/live-coding/index.js +7 -0
- package/dist/live-coding/lsp-protocol.d.ts +98 -0
- package/dist/live-coding/lsp-protocol.d.ts.map +1 -0
- package/dist/live-coding/lsp-protocol.js +70 -0
- package/dist/live-coding/monaco-services.d.ts +22 -0
- package/dist/live-coding/monaco-services.d.ts.map +1 -0
- package/dist/live-coding/monaco-services.js +69 -0
- package/dist/live-coding/platform-index.d.ts +29 -0
- package/dist/live-coding/platform-index.d.ts.map +1 -0
- package/dist/live-coding/platform-index.js +281 -0
- package/dist/live-coding/rust-analysis.d.ts +24 -0
- package/dist/live-coding/rust-analysis.d.ts.map +1 -0
- package/dist/live-coding/rust-analysis.js +312 -0
- package/dist/live-coding/rust-lsp-server.d.ts +43 -0
- package/dist/live-coding/rust-lsp-server.d.ts.map +1 -0
- package/dist/live-coding/rust-lsp-server.js +455 -0
- package/dist/live-coding/rust-lsp.worker.d.ts +2 -0
- package/dist/live-coding/rust-lsp.worker.d.ts.map +1 -0
- package/dist/live-coding/rust-lsp.worker.js +26 -0
- package/dist/live-coding/vfs.d.ts +41 -0
- package/dist/live-coding/vfs.d.ts.map +1 -0
- package/dist/live-coding/vfs.js +174 -0
- package/dist/live-coding/worker-transport.d.ts +60 -0
- package/dist/live-coding/worker-transport.d.ts.map +1 -0
- package/dist/live-coding/worker-transport.js +204 -0
- package/package.json +18 -6
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
export const DEFAULT_VFS_LIMITS = {
|
|
2
|
+
maxFiles: 32,
|
|
3
|
+
maxFileBytes: 256 * 1024,
|
|
4
|
+
maxWorkspaceBytes: 1024 * 1024,
|
|
5
|
+
};
|
|
6
|
+
export class VfsLimitError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = 'VFS_LIMIT';
|
|
10
|
+
this.name = 'VfsLimitError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class VirtualFileSystem {
|
|
14
|
+
constructor(workspaceUri = 'file:///player-mod', limits = {}) {
|
|
15
|
+
this.files = new Map();
|
|
16
|
+
this.encoder = new TextEncoder();
|
|
17
|
+
this.totalBytes = 0;
|
|
18
|
+
this.workspaceUri = workspaceUri.replace(/\/+$/, '');
|
|
19
|
+
const root = new URL(this.workspaceUri);
|
|
20
|
+
if (root.protocol !== 'file:' ||
|
|
21
|
+
root.search ||
|
|
22
|
+
root.hash ||
|
|
23
|
+
!root.pathname.startsWith('/') ||
|
|
24
|
+
root.pathname === '/') {
|
|
25
|
+
throw new Error('workspaceUri must be a non-root file URI');
|
|
26
|
+
}
|
|
27
|
+
this.limits = { ...DEFAULT_VFS_LIMITS, ...limits };
|
|
28
|
+
this.assertLimits();
|
|
29
|
+
}
|
|
30
|
+
get size() {
|
|
31
|
+
return this.files.size;
|
|
32
|
+
}
|
|
33
|
+
get bytes() {
|
|
34
|
+
return this.totalBytes;
|
|
35
|
+
}
|
|
36
|
+
open(item) {
|
|
37
|
+
const path = this.pathForUri(item.uri);
|
|
38
|
+
const previous = this.files.get(item.uri);
|
|
39
|
+
if (!previous && this.files.size >= this.limits.maxFiles) {
|
|
40
|
+
throw new VfsLimitError(`Workspace file limit is ${this.limits.maxFiles}`);
|
|
41
|
+
}
|
|
42
|
+
if (previous && item.version <= previous.version) {
|
|
43
|
+
return previous;
|
|
44
|
+
}
|
|
45
|
+
const next = this.makeDocument(item, path);
|
|
46
|
+
this.ensureFits(next.bytes, previous?.bytes ?? 0);
|
|
47
|
+
if (previous)
|
|
48
|
+
this.totalBytes -= previous.bytes;
|
|
49
|
+
this.files.set(item.uri, next);
|
|
50
|
+
this.totalBytes += next.bytes;
|
|
51
|
+
return next;
|
|
52
|
+
}
|
|
53
|
+
change(uri, version, changes) {
|
|
54
|
+
const previous = this.require(uri);
|
|
55
|
+
if (version <= previous.version) {
|
|
56
|
+
return { applied: false, document: previous };
|
|
57
|
+
}
|
|
58
|
+
let text = previous.text;
|
|
59
|
+
for (const change of changes) {
|
|
60
|
+
if (!change.range) {
|
|
61
|
+
text = change.text;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const start = offsetAt(text, change.range.start);
|
|
65
|
+
const end = offsetAt(text, change.range.end);
|
|
66
|
+
if (end < start)
|
|
67
|
+
throw new Error('Invalid content change range');
|
|
68
|
+
text = text.slice(0, start) + change.text + text.slice(end);
|
|
69
|
+
}
|
|
70
|
+
const next = this.makeDocument({
|
|
71
|
+
uri,
|
|
72
|
+
version,
|
|
73
|
+
languageId: previous.languageId,
|
|
74
|
+
text,
|
|
75
|
+
}, previous.path);
|
|
76
|
+
this.ensureFits(next.bytes, previous.bytes);
|
|
77
|
+
this.files.set(uri, next);
|
|
78
|
+
this.totalBytes += next.bytes - previous.bytes;
|
|
79
|
+
return { applied: true, document: next };
|
|
80
|
+
}
|
|
81
|
+
close(uri) {
|
|
82
|
+
const previous = this.files.get(uri);
|
|
83
|
+
if (!previous)
|
|
84
|
+
return false;
|
|
85
|
+
this.files.delete(uri);
|
|
86
|
+
this.totalBytes -= previous.bytes;
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
get(uri) {
|
|
90
|
+
return this.files.get(uri);
|
|
91
|
+
}
|
|
92
|
+
require(uri) {
|
|
93
|
+
const document = this.files.get(uri);
|
|
94
|
+
if (!document)
|
|
95
|
+
throw new Error(`Document is not open: ${uri}`);
|
|
96
|
+
return document;
|
|
97
|
+
}
|
|
98
|
+
documents() {
|
|
99
|
+
return [...this.files.values()];
|
|
100
|
+
}
|
|
101
|
+
clear() {
|
|
102
|
+
this.files.clear();
|
|
103
|
+
this.totalBytes = 0;
|
|
104
|
+
}
|
|
105
|
+
pathForUri(uri) {
|
|
106
|
+
let parsed;
|
|
107
|
+
try {
|
|
108
|
+
parsed = new URL(uri);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new Error(`Invalid document URI: ${uri}`);
|
|
112
|
+
}
|
|
113
|
+
if (parsed.protocol !== 'file:' ||
|
|
114
|
+
parsed.search ||
|
|
115
|
+
parsed.hash ||
|
|
116
|
+
!uri.startsWith(`${this.workspaceUri}/`)) {
|
|
117
|
+
throw new Error(`Document is outside ${this.workspaceUri}`);
|
|
118
|
+
}
|
|
119
|
+
const relative = decodeURIComponent(parsed.pathname.slice(new URL(this.workspaceUri).pathname.length + 1));
|
|
120
|
+
if (relative.length === 0 ||
|
|
121
|
+
relative.startsWith('/') ||
|
|
122
|
+
relative.split('/').some((part) => part === '' || part === '..' || part === '.')) {
|
|
123
|
+
throw new Error(`Unsafe document path: ${relative}`);
|
|
124
|
+
}
|
|
125
|
+
return relative;
|
|
126
|
+
}
|
|
127
|
+
makeDocument(item, path) {
|
|
128
|
+
if (!Number.isSafeInteger(item.version) || item.version < 0) {
|
|
129
|
+
throw new Error('Document version must be a non-negative safe integer');
|
|
130
|
+
}
|
|
131
|
+
const bytes = this.encoder.encode(item.text).byteLength;
|
|
132
|
+
if (bytes > this.limits.maxFileBytes) {
|
|
133
|
+
throw new VfsLimitError(`${path} is ${bytes} bytes; file limit is ${this.limits.maxFileBytes}`);
|
|
134
|
+
}
|
|
135
|
+
return Object.freeze({ ...item, path, bytes });
|
|
136
|
+
}
|
|
137
|
+
ensureFits(nextBytes, replacedBytes) {
|
|
138
|
+
const total = this.totalBytes - replacedBytes + nextBytes;
|
|
139
|
+
if (total > this.limits.maxWorkspaceBytes) {
|
|
140
|
+
throw new VfsLimitError(`Workspace is ${total} bytes; limit is ${this.limits.maxWorkspaceBytes}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
assertLimits() {
|
|
144
|
+
for (const [name, value] of Object.entries(this.limits)) {
|
|
145
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
146
|
+
throw new Error(`${name} must be a positive safe integer`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (this.limits.maxFileBytes > this.limits.maxWorkspaceBytes) {
|
|
150
|
+
throw new Error('maxFileBytes cannot exceed maxWorkspaceBytes');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export function offsetAt(text, position) {
|
|
155
|
+
if (!Number.isSafeInteger(position.line) ||
|
|
156
|
+
!Number.isSafeInteger(position.character) ||
|
|
157
|
+
position.line < 0 ||
|
|
158
|
+
position.character < 0) {
|
|
159
|
+
throw new Error('Invalid document position');
|
|
160
|
+
}
|
|
161
|
+
let offset = 0;
|
|
162
|
+
for (let line = 0; line < position.line; line++) {
|
|
163
|
+
const newline = text.indexOf('\n', offset);
|
|
164
|
+
if (newline < 0)
|
|
165
|
+
throw new Error('Document position is outside the text');
|
|
166
|
+
offset = newline + 1;
|
|
167
|
+
}
|
|
168
|
+
const newline = text.indexOf('\n', offset);
|
|
169
|
+
const lineEnd = newline < 0 ? text.length : newline;
|
|
170
|
+
const target = offset + position.character;
|
|
171
|
+
if (target > lineEnd)
|
|
172
|
+
throw new Error('Document position is outside the text');
|
|
173
|
+
return target;
|
|
174
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { AbstractMessageReader, AbstractMessageWriter, type DataCallback, type Disposable, type Message } from 'vscode-jsonrpc';
|
|
2
|
+
import { type JsonRpcMessage } from './lsp-protocol.js';
|
|
3
|
+
export type { Disposable };
|
|
4
|
+
export interface LanguageWorkerLike {
|
|
5
|
+
postMessage(message: unknown): void;
|
|
6
|
+
addEventListener(type: 'message' | 'error' | 'messageerror', listener: EventListener): void;
|
|
7
|
+
removeEventListener(type: 'message' | 'error' | 'messageerror', listener: EventListener): void;
|
|
8
|
+
terminate(): void;
|
|
9
|
+
}
|
|
10
|
+
/** Standard vscode-jsonrpc MessageReader over structured-clone worker messages. */
|
|
11
|
+
export declare class WorkerMessageReader extends AbstractMessageReader {
|
|
12
|
+
private readonly worker;
|
|
13
|
+
private callback;
|
|
14
|
+
private listening;
|
|
15
|
+
constructor(worker: LanguageWorkerLike);
|
|
16
|
+
listen(callback: DataCallback): Disposable;
|
|
17
|
+
dispose(): void;
|
|
18
|
+
private readonly onMessage;
|
|
19
|
+
private readonly onMessageError;
|
|
20
|
+
private readonly onWorkerError;
|
|
21
|
+
}
|
|
22
|
+
/** Standard vscode-jsonrpc MessageWriter over structured-clone worker messages. */
|
|
23
|
+
export declare class WorkerMessageWriter extends AbstractMessageWriter {
|
|
24
|
+
private readonly worker;
|
|
25
|
+
private disposed;
|
|
26
|
+
private errorCount;
|
|
27
|
+
constructor(worker: LanguageWorkerLike);
|
|
28
|
+
write(message: Message): Promise<void>;
|
|
29
|
+
end(): void;
|
|
30
|
+
dispose(): void;
|
|
31
|
+
}
|
|
32
|
+
export interface WorkerLanguageClientOptions {
|
|
33
|
+
requestTimeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* LSP 3.17 JSON-RPC client over a real vscode-jsonrpc MessageConnection.
|
|
37
|
+
* Monaco providers adapt editor calls to this connection; transport framing,
|
|
38
|
+
* request ids, cancellation, errors, and notifications are standard JSON-RPC.
|
|
39
|
+
*/
|
|
40
|
+
export declare class WorkerLanguageClient {
|
|
41
|
+
private readonly worker;
|
|
42
|
+
private readonly options;
|
|
43
|
+
private readonly reader;
|
|
44
|
+
private readonly writer;
|
|
45
|
+
private readonly connection;
|
|
46
|
+
private readonly transportErrorHandlers;
|
|
47
|
+
private readonly readerErrorDisposable;
|
|
48
|
+
private disposed;
|
|
49
|
+
private terminateTimer;
|
|
50
|
+
constructor(worker: LanguageWorkerLike, options?: WorkerLanguageClientOptions);
|
|
51
|
+
initialize(params: unknown): Promise<unknown>;
|
|
52
|
+
request(method: string, params?: unknown): Promise<unknown>;
|
|
53
|
+
notify(method: string, params?: unknown): Promise<void>;
|
|
54
|
+
onNotification(method: string, callback: (params: unknown) => void): Disposable;
|
|
55
|
+
shutdown(): void;
|
|
56
|
+
dispose(): void;
|
|
57
|
+
}
|
|
58
|
+
export declare function createDefaultRustLanguageWorker(): Worker;
|
|
59
|
+
export declare function isWorkerLspMessage(value: unknown): value is JsonRpcMessage;
|
|
60
|
+
//# sourceMappingURL=worker-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-transport.d.ts","sourceRoot":"","sources":["../../src/live-coding/worker-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EAErB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,OAAO,EACb,MAAM,gBAAgB,CAAC;AASxB,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,gBAAgB,CACd,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,cAAc,EAC1C,QAAQ,EAAE,aAAa,GACtB,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,cAAc,EAC1C,QAAQ,EAAE,aAAa,GACtB,IAAI,CAAC;IACR,SAAS,IAAI,IAAI,CAAC;CACnB;AAED,mFAAmF;AACnF,qBAAa,mBAAoB,SAAQ,qBAAqB;IAIhD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,SAAS,CAAS;gBAEG,MAAM,EAAE,kBAAkB;IAIvD,MAAM,CAAC,QAAQ,EAAE,YAAY,GAAG,UAAU;IAUjC,OAAO,IAAI,IAAI;IAWxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAOxB;IAEF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAE7B;IAEF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAM5B;CACH;AAED,mFAAmF;AACnF,qBAAa,mBAAoB,SAAQ,qBAAqB;IAIhD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAK;gBAEM,MAAM,EAAE,kBAAkB;IAIvD,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtC,GAAG,IAAI,IAAI;IAIF,OAAO,IAAI,IAAI;CAIzB;AAED,MAAM,WAAW,2BAA2B;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,qBAAa,oBAAoB;IAU7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAV1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqC;IAC5E,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAa;IACnD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAA8C;gBAGjD,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,2BAAgC;IAWtD,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAMnD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAsD3D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,cAAc,CACZ,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAClC,UAAU;IAIb,QAAQ,IAAI,IAAI;IAehB,OAAO,IAAI,IAAI;CAYhB;AAED,wBAAgB,+BAA+B,IAAI,MAAM,CAKxD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAE1E"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { AbstractMessageReader, AbstractMessageWriter, CancellationTokenSource, } from 'vscode-jsonrpc';
|
|
2
|
+
import { ExitNotification, InitializeRequest, InitializedNotification, ShutdownRequest, createProtocolConnection, } from 'vscode-languageserver-protocol';
|
|
3
|
+
import { decodeJsonRpcMessage, } from './lsp-protocol.js';
|
|
4
|
+
/** Standard vscode-jsonrpc MessageReader over structured-clone worker messages. */
|
|
5
|
+
export class WorkerMessageReader extends AbstractMessageReader {
|
|
6
|
+
constructor(worker) {
|
|
7
|
+
super();
|
|
8
|
+
this.worker = worker;
|
|
9
|
+
this.callback = null;
|
|
10
|
+
this.listening = false;
|
|
11
|
+
this.onMessage = (event) => {
|
|
12
|
+
const decoded = decodeJsonRpcMessage(event.data);
|
|
13
|
+
if (!decoded.ok) {
|
|
14
|
+
this.fireError(new Error(decoded.message));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this.callback?.(decoded.message);
|
|
18
|
+
};
|
|
19
|
+
this.onMessageError = () => {
|
|
20
|
+
this.fireError(new Error('Language worker sent an unreadable message'));
|
|
21
|
+
};
|
|
22
|
+
this.onWorkerError = (event) => {
|
|
23
|
+
const message = 'message' in event && typeof event.message === 'string'
|
|
24
|
+
? event.message
|
|
25
|
+
: 'Language worker failed';
|
|
26
|
+
this.fireError(new Error(message));
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
listen(callback) {
|
|
30
|
+
if (this.listening)
|
|
31
|
+
throw new Error('WorkerMessageReader is already listening');
|
|
32
|
+
this.listening = true;
|
|
33
|
+
this.callback = callback;
|
|
34
|
+
this.worker.addEventListener('message', this.onMessage);
|
|
35
|
+
this.worker.addEventListener('messageerror', this.onMessageError);
|
|
36
|
+
this.worker.addEventListener('error', this.onWorkerError);
|
|
37
|
+
return { dispose: () => this.dispose() };
|
|
38
|
+
}
|
|
39
|
+
dispose() {
|
|
40
|
+
if (this.listening) {
|
|
41
|
+
this.worker.removeEventListener('message', this.onMessage);
|
|
42
|
+
this.worker.removeEventListener('messageerror', this.onMessageError);
|
|
43
|
+
this.worker.removeEventListener('error', this.onWorkerError);
|
|
44
|
+
}
|
|
45
|
+
this.listening = false;
|
|
46
|
+
this.callback = null;
|
|
47
|
+
super.dispose();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Standard vscode-jsonrpc MessageWriter over structured-clone worker messages. */
|
|
51
|
+
export class WorkerMessageWriter extends AbstractMessageWriter {
|
|
52
|
+
constructor(worker) {
|
|
53
|
+
super();
|
|
54
|
+
this.worker = worker;
|
|
55
|
+
this.disposed = false;
|
|
56
|
+
this.errorCount = 0;
|
|
57
|
+
}
|
|
58
|
+
write(message) {
|
|
59
|
+
if (this.disposed)
|
|
60
|
+
return Promise.reject(new Error('Worker writer is disposed'));
|
|
61
|
+
try {
|
|
62
|
+
this.worker.postMessage(message);
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.errorCount++;
|
|
67
|
+
this.fireError(error, message, this.errorCount);
|
|
68
|
+
return Promise.reject(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
end() {
|
|
72
|
+
this.dispose();
|
|
73
|
+
}
|
|
74
|
+
dispose() {
|
|
75
|
+
this.disposed = true;
|
|
76
|
+
super.dispose();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* LSP 3.17 JSON-RPC client over a real vscode-jsonrpc MessageConnection.
|
|
81
|
+
* Monaco providers adapt editor calls to this connection; transport framing,
|
|
82
|
+
* request ids, cancellation, errors, and notifications are standard JSON-RPC.
|
|
83
|
+
*/
|
|
84
|
+
export class WorkerLanguageClient {
|
|
85
|
+
constructor(worker, options = {}) {
|
|
86
|
+
this.worker = worker;
|
|
87
|
+
this.options = options;
|
|
88
|
+
this.transportErrorHandlers = new Set();
|
|
89
|
+
this.disposed = false;
|
|
90
|
+
this.terminateTimer = null;
|
|
91
|
+
this.reader = new WorkerMessageReader(worker);
|
|
92
|
+
this.writer = new WorkerMessageWriter(worker);
|
|
93
|
+
this.readerErrorDisposable = this.reader.onError((error) => {
|
|
94
|
+
for (const handler of this.transportErrorHandlers)
|
|
95
|
+
handler(error);
|
|
96
|
+
});
|
|
97
|
+
this.connection = createProtocolConnection(this.reader, this.writer);
|
|
98
|
+
this.connection.listen();
|
|
99
|
+
}
|
|
100
|
+
async initialize(params) {
|
|
101
|
+
const result = await this.request(InitializeRequest.type.method, params);
|
|
102
|
+
await this.notify(InitializedNotification.type.method, {});
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
request(method, params) {
|
|
106
|
+
if (this.disposed)
|
|
107
|
+
return Promise.reject(new Error('Language client is disposed'));
|
|
108
|
+
const cancellation = new CancellationTokenSource();
|
|
109
|
+
const timeoutMs = this.options.requestTimeoutMs ?? 3000;
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
let settled = false;
|
|
112
|
+
const onTransportError = (error) => {
|
|
113
|
+
if (settled)
|
|
114
|
+
return;
|
|
115
|
+
settled = true;
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
cancellation.cancel();
|
|
118
|
+
cancellation.dispose();
|
|
119
|
+
this.transportErrorHandlers.delete(onTransportError);
|
|
120
|
+
reject(error);
|
|
121
|
+
};
|
|
122
|
+
const timer = setTimeout(() => {
|
|
123
|
+
if (settled)
|
|
124
|
+
return;
|
|
125
|
+
settled = true;
|
|
126
|
+
cancellation.cancel();
|
|
127
|
+
cancellation.dispose();
|
|
128
|
+
this.transportErrorHandlers.delete(onTransportError);
|
|
129
|
+
reject(new Error(`Language request timed out: ${method}`));
|
|
130
|
+
}, timeoutMs);
|
|
131
|
+
this.transportErrorHandlers.add(onTransportError);
|
|
132
|
+
const request = params === undefined
|
|
133
|
+
? this.connection.sendRequest(method, cancellation.token)
|
|
134
|
+
: this.connection.sendRequest(method, params, cancellation.token);
|
|
135
|
+
void request
|
|
136
|
+
.then((value) => {
|
|
137
|
+
if (settled)
|
|
138
|
+
return;
|
|
139
|
+
settled = true;
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
cancellation.dispose();
|
|
142
|
+
this.transportErrorHandlers.delete(onTransportError);
|
|
143
|
+
resolve(value);
|
|
144
|
+
}, (error) => {
|
|
145
|
+
if (settled)
|
|
146
|
+
return;
|
|
147
|
+
settled = true;
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
cancellation.dispose();
|
|
150
|
+
this.transportErrorHandlers.delete(onTransportError);
|
|
151
|
+
reject(error instanceof Error
|
|
152
|
+
? error
|
|
153
|
+
: new Error('Language request failed'));
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
notify(method, params) {
|
|
158
|
+
if (this.disposed)
|
|
159
|
+
return Promise.reject(new Error('Language client is disposed'));
|
|
160
|
+
return this.connection.sendNotification(method, params);
|
|
161
|
+
}
|
|
162
|
+
onNotification(method, callback) {
|
|
163
|
+
return this.connection.onNotification(method, callback);
|
|
164
|
+
}
|
|
165
|
+
shutdown() {
|
|
166
|
+
if (this.disposed || this.terminateTimer)
|
|
167
|
+
return;
|
|
168
|
+
const finish = () => {
|
|
169
|
+
if (this.disposed)
|
|
170
|
+
return;
|
|
171
|
+
void this.connection
|
|
172
|
+
.sendNotification(ExitNotification.type.method)
|
|
173
|
+
.catch(() => { })
|
|
174
|
+
.finally(() => this.dispose());
|
|
175
|
+
};
|
|
176
|
+
void this.request(ShutdownRequest.type.method)
|
|
177
|
+
.catch(() => { })
|
|
178
|
+
.finally(finish);
|
|
179
|
+
this.terminateTimer = setTimeout(finish, this.options.requestTimeoutMs ?? 3000);
|
|
180
|
+
}
|
|
181
|
+
dispose() {
|
|
182
|
+
if (this.disposed)
|
|
183
|
+
return;
|
|
184
|
+
this.disposed = true;
|
|
185
|
+
if (this.terminateTimer)
|
|
186
|
+
clearTimeout(this.terminateTimer);
|
|
187
|
+
this.terminateTimer = null;
|
|
188
|
+
this.transportErrorHandlers.clear();
|
|
189
|
+
this.readerErrorDisposable.dispose();
|
|
190
|
+
this.connection.dispose();
|
|
191
|
+
this.reader.dispose();
|
|
192
|
+
this.writer.dispose();
|
|
193
|
+
this.worker.terminate();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export function createDefaultRustLanguageWorker() {
|
|
197
|
+
return new Worker(new URL('./rust-lsp.worker.js', import.meta.url), {
|
|
198
|
+
type: 'module',
|
|
199
|
+
name: 'crowdy-rust-lsp',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
export function isWorkerLspMessage(value) {
|
|
203
|
+
return decodeJsonRpcMessage(value).ok;
|
|
204
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdedkingdoms/crowdyjs",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
|
+
"description": "Browser-first Crowded Kingdoms SDK with GraphQL, realtime, and local Rust authoring support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./stores": {
|
|
18
18
|
"import": "./dist/stores/index.js",
|
|
19
19
|
"types": "./dist/stores/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./live-coding": {
|
|
22
|
+
"import": "./dist/live-coding/index.js",
|
|
23
|
+
"types": "./dist/live-coding/index.d.ts"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"sideEffects": false,
|
|
@@ -33,7 +37,10 @@
|
|
|
33
37
|
"codegen": "graphql-codegen --config codegen.ts",
|
|
34
38
|
"check:schema": "npm run codegen && git diff --exit-code schema.gql src/generated/graphql.ts",
|
|
35
39
|
"codegen:watch": "graphql-codegen --config codegen.ts --watch",
|
|
36
|
-
"
|
|
40
|
+
"authoring-index:generate": "node scripts/generate-browser-authoring-index.mjs --write",
|
|
41
|
+
"authoring-index:drift": "node scripts/sync-browser-authoring-index.mjs",
|
|
42
|
+
"check:authoring-index": "node scripts/validate-browser-authoring-index.mjs && node scripts/generate-browser-authoring-index.mjs",
|
|
43
|
+
"build": "npm run check:authoring-index && tsc && node scripts/copy-live-coding-assets.mjs",
|
|
37
44
|
"test": "npm run build && node --test --test-concurrency=1 test/unit/*.test.mjs test/e2e/*.test.mjs",
|
|
38
45
|
"test:unit": "npm run build && node --test test/unit/*.test.mjs",
|
|
39
46
|
"test:e2e": "npm run build && node --test --test-concurrency=1 test/e2e/*.test.mjs",
|
|
@@ -50,7 +57,9 @@
|
|
|
50
57
|
"graphql",
|
|
51
58
|
"game-api",
|
|
52
59
|
"multiplayer",
|
|
53
|
-
"sdk"
|
|
60
|
+
"sdk",
|
|
61
|
+
"rust",
|
|
62
|
+
"webassembly"
|
|
54
63
|
],
|
|
55
64
|
"author": "",
|
|
56
65
|
"license": "MIT",
|
|
@@ -73,13 +82,16 @@
|
|
|
73
82
|
"ws": "^8.21.1"
|
|
74
83
|
},
|
|
75
84
|
"dependencies": {
|
|
85
|
+
"@codingame/monaco-vscode-api": "25.1.2",
|
|
76
86
|
"@codingame/monaco-vscode-editor-api": "25.1.2",
|
|
77
87
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
88
|
+
"@vscode/tree-sitter-wasm": "0.3.1",
|
|
78
89
|
"dompurify": "3.4.12",
|
|
79
90
|
"graphql": "^16.13.2",
|
|
80
91
|
"graphql-ws": "^6.0.8",
|
|
81
|
-
"
|
|
82
|
-
"vscode-
|
|
92
|
+
"vscode-jsonrpc": "8.2.0",
|
|
93
|
+
"vscode-languageserver-protocol": "3.17.5",
|
|
94
|
+
"web-tree-sitter": "0.26.11"
|
|
83
95
|
},
|
|
84
96
|
"overrides": {
|
|
85
97
|
"dompurify": "$dompurify"
|