@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.
Files changed (43) hide show
  1. package/MIGRATION.md +44 -0
  2. package/README.md +74 -3
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/live-coding/assets/browser-authoring-index.json +1 -0
  7. package/dist/live-coding/assets/manifest.json +19 -0
  8. package/dist/live-coding/assets/tree-sitter-rust.wasm +0 -0
  9. package/dist/live-coding/assets/web-tree-sitter.wasm +0 -0
  10. package/dist/live-coding/browser-authoring-index.generated.d.ts +2 -0
  11. package/dist/live-coding/browser-authoring-index.generated.d.ts.map +1 -0
  12. package/dist/live-coding/browser-authoring-index.generated.js +5127 -0
  13. package/dist/live-coding/ide.d.ts +14 -7
  14. package/dist/live-coding/ide.d.ts.map +1 -1
  15. package/dist/live-coding/ide.js +296 -181
  16. package/dist/live-coding/index.d.ts +8 -0
  17. package/dist/live-coding/index.d.ts.map +1 -0
  18. package/dist/live-coding/index.js +7 -0
  19. package/dist/live-coding/lsp-protocol.d.ts +98 -0
  20. package/dist/live-coding/lsp-protocol.d.ts.map +1 -0
  21. package/dist/live-coding/lsp-protocol.js +70 -0
  22. package/dist/live-coding/monaco-services.d.ts +22 -0
  23. package/dist/live-coding/monaco-services.d.ts.map +1 -0
  24. package/dist/live-coding/monaco-services.js +69 -0
  25. package/dist/live-coding/platform-index.d.ts +29 -0
  26. package/dist/live-coding/platform-index.d.ts.map +1 -0
  27. package/dist/live-coding/platform-index.js +281 -0
  28. package/dist/live-coding/rust-analysis.d.ts +24 -0
  29. package/dist/live-coding/rust-analysis.d.ts.map +1 -0
  30. package/dist/live-coding/rust-analysis.js +312 -0
  31. package/dist/live-coding/rust-lsp-server.d.ts +43 -0
  32. package/dist/live-coding/rust-lsp-server.d.ts.map +1 -0
  33. package/dist/live-coding/rust-lsp-server.js +455 -0
  34. package/dist/live-coding/rust-lsp.worker.d.ts +2 -0
  35. package/dist/live-coding/rust-lsp.worker.d.ts.map +1 -0
  36. package/dist/live-coding/rust-lsp.worker.js +26 -0
  37. package/dist/live-coding/vfs.d.ts +41 -0
  38. package/dist/live-coding/vfs.d.ts.map +1 -0
  39. package/dist/live-coding/vfs.js +174 -0
  40. package/dist/live-coding/worker-transport.d.ts +60 -0
  41. package/dist/live-coding/worker-transport.d.ts.map +1 -0
  42. package/dist/live-coding/worker-transport.js +204 -0
  43. package/package.json +18 -6
@@ -1,16 +1,23 @@
1
1
  import { type LiveCodingHandle, type MountLiveCodingOptions } from './mount.js';
2
+ import { type PlatformIndex } from './platform-index.js';
2
3
  export interface MountLiveCodingIDEOptions extends MountLiveCodingOptions {
3
- /** Authenticated wss endpoint ending in /authoring-lsp. */
4
- languageServiceUrl?: string;
5
- /** Current app token or a getter. Tokens are sent only after WSS connects. */
6
- appToken?: string | (() => string | null);
7
4
  /** Consumer-provided Monaco editor worker (bundler-specific). */
8
5
  editorWorkerFactory?: () => Worker;
6
+ /** Optional module-worker factory; one worker is created for this editor. */
7
+ languageWorkerFactory?: () => Worker;
8
+ /**
9
+ * Generated platform symbol index. The worker strictly validates its schema;
10
+ * omit it to use the byte-identical embedded game export.
11
+ */
12
+ platformIndex?: PlatformIndex | unknown;
13
+ /** JSON-RPC request timeout; defaults to 3 seconds. */
14
+ languageRequestTimeoutMs?: number;
9
15
  }
10
16
  /**
11
- * Lazy Monaco live-coding IDE. Monaco and the language-client stack stay out
12
- * of the importing game's hot bundle until this async mount is called.
13
- * Missing LSP configuration degrades to the dependency-free textarea panel.
17
+ * Lazy Monaco live-coding IDE backed only by a browser module worker and local
18
+ * WASM parser assets. If Monaco, Worker, or WASM startup fails, the dependency-
19
+ * free textarea is mounted; there is deliberately no server fallback.
14
20
  */
15
21
  export declare function mountLiveCodingIDE(el: HTMLElement, options: MountLiveCodingIDEOptions): Promise<LiveCodingHandle>;
22
+ export declare function isCurrentDiagnosticVersion(version: unknown, currentVersion: number): boolean;
16
23
  //# sourceMappingURL=ide.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ide.d.ts","sourceRoot":"","sources":["../../src/live-coding/ide.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC5B,MAAM,YAAY,CAAC;AAKpB,MAAM,WAAW,yBAA0B,SAAQ,sBAAsB;IACvE,2DAA2D;IAC3D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,MAAM,MAAM,CAAC;CACpC;AAYD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,gBAAgB,CAAC,CAwN3B"}
1
+ {"version":3,"file":"ide.d.ts","sourceRoot":"","sources":["../../src/live-coding/ide.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC5B,MAAM,YAAY,CAAC;AAUpB,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAY7B,MAAM,WAAW,yBAA0B,SAAQ,sBAAsB;IACvE,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,MAAM,MAAM,CAAC;IACnC,6EAA6E;IAC7E,qBAAqB,CAAC,EAAE,MAAM,MAAM,CAAC;IACrC;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IACxC,uDAAuD;IACvD,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAgBD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,gBAAgB,CAAC,CAwX3B;AA8GD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAKT"}
@@ -1,46 +1,42 @@
1
1
  import { LiveCodingController, } from './live-coding-controller.js';
2
2
  import { mountLiveCoding, } from './mount.js';
3
- import { PLAYER_CODE_TEMPLATES, } from './templates.js';
4
- let vscodeServicesPromise = null;
3
+ import { isRecord, } from './lsp-protocol.js';
4
+ import { loadPlatformIndex, } from './platform-index.js';
5
+ import { PLAYER_CODE_TEMPLATES } from './templates.js';
6
+ import { acquireMonacoWorkspace, ensureMonacoServicesInitialized, } from './monaco-services.js';
7
+ let monacoLanguagesRegistered = false;
5
8
  /**
6
- * Lazy Monaco live-coding IDE. Monaco and the language-client stack stay out
7
- * of the importing game's hot bundle until this async mount is called.
8
- * Missing LSP configuration degrades to the dependency-free textarea panel.
9
+ * Lazy Monaco live-coding IDE backed only by a browser module worker and local
10
+ * WASM parser assets. If Monaco, Worker, or WASM startup fails, the dependency-
11
+ * free textarea is mounted; there is deliberately no server fallback.
9
12
  */
10
13
  export async function mountLiveCodingIDE(el, options) {
11
- const token = resolveToken(options.appToken);
12
- if (!options.languageServiceUrl || !token) {
14
+ if (!options.languageWorkerFactory && typeof Worker === 'undefined') {
13
15
  return mountLiveCoding(el, options);
14
16
  }
15
17
  if (options.editorWorkerFactory) {
16
18
  globalThis.MonacoEnvironment = { getWorker: options.editorWorkerFactory };
17
19
  }
20
+ let languageWorker = null;
21
+ let languageClient = null;
22
+ let releaseWorkspace = null;
23
+ let teardownMountedIde = null;
18
24
  try {
19
- const [monaco, languageClientModule, wsJsonRpc, vscodeWrapperModule, workerFactoryModule,] = await Promise.all([
25
+ const platformIndex = options.platformIndex === undefined
26
+ ? undefined
27
+ : loadPlatformIndex(options.platformIndex);
28
+ const workerTransportPromise = import('./worker-transport.js');
29
+ await ensureMonacoServicesInitialized();
30
+ const [monaco, workerTransport] = await Promise.all([
20
31
  import('@codingame/monaco-vscode-editor-api'),
21
- import('monaco-languageclient'),
22
- import('vscode-ws-jsonrpc'),
23
- import('monaco-languageclient/vscodeApiWrapper'),
24
- import('monaco-languageclient/workerFactory'),
32
+ workerTransportPromise,
25
33
  ]);
26
- if (!vscodeServicesPromise) {
27
- const wrapper = new vscodeWrapperModule.MonacoVscodeApiWrapper({
28
- $type: 'classic',
29
- viewsConfig: { $type: 'EditorService' },
30
- userConfiguration: {
31
- json: JSON.stringify({
32
- 'workbench.colorTheme': 'Default Dark Modern',
33
- 'editor.wordBasedSuggestions': 'off',
34
- }),
35
- },
36
- monacoWorkerFactory: workerFactoryModule.configureDefaultWorkerFactory,
37
- });
38
- vscodeServicesPromise = wrapper.start();
34
+ if (!monacoLanguagesRegistered) {
35
+ monaco.languages.register({ id: 'rust', extensions: ['.rs'] });
36
+ installRustSyntaxHighlighting(monaco);
37
+ monaco.languages.register({ id: 'toml', extensions: ['.toml'] });
38
+ monacoLanguagesRegistered = true;
39
39
  }
40
- await vscodeServicesPromise;
41
- monaco.languages.register({ id: 'rust', extensions: ['.rs'] });
42
- installRustSyntaxHighlighting(monaco);
43
- monaco.languages.register({ id: 'toml', extensions: ['.toml'] });
44
40
  const templates = options.templates ?? PLAYER_CODE_TEMPLATES;
45
41
  const root = document.createElement('div');
46
42
  root.className = 'ck-live-coding ck-live-coding-ide';
@@ -73,6 +69,10 @@ export async function mountLiveCodingIDE(el, options) {
73
69
  ...options,
74
70
  onStatus: (value) => renderStatus(status, meter, value),
75
71
  });
72
+ const workspaceLease = acquireMonacoWorkspace();
73
+ releaseWorkspace = workspaceLease.release;
74
+ const modelRootUri = workspaceLease.uri;
75
+ const ownsModel = (uri) => uri.toString().startsWith(`${modelRootUri}/`);
76
76
  const editor = monaco.editor.create(editorHost, {
77
77
  automaticLayout: true,
78
78
  minimap: { enabled: false },
@@ -81,9 +81,7 @@ export async function mountLiveCodingIDE(el, options) {
81
81
  tabSize: 2,
82
82
  });
83
83
  let models = [];
84
- let languageClient = null;
85
- let socket = null;
86
- let modelRootUri = 'file:///player-mod';
84
+ const languageDisposables = [];
87
85
  const sourceJson = () => JSON.stringify(Object.fromEntries(models.map(({ path, model }) => [path, model.getValue()])));
88
86
  const showModel = (path) => {
89
87
  const found = models.find((entry) => entry.path === path);
@@ -94,16 +92,61 @@ export async function mountLiveCodingIDE(el, options) {
94
92
  child.dataset.path === path ? 'true' : 'false';
95
93
  }
96
94
  };
97
- const setFiles = (sourceFilesJson) => {
98
- for (const entry of models)
95
+ const closeModels = () => {
96
+ for (const entry of models) {
97
+ if (entry.path.endsWith('.rs')) {
98
+ void languageClient
99
+ ?.notify('textDocument/didClose', {
100
+ textDocument: { uri: entry.model.uri.toString() },
101
+ })
102
+ .catch(() => { });
103
+ }
104
+ entry.changeSubscription.dispose();
99
105
  entry.model.dispose();
106
+ }
100
107
  models = [];
108
+ };
109
+ const openModel = (entry) => {
110
+ if (!entry.path.endsWith('.rs'))
111
+ return;
112
+ void languageClient
113
+ ?.notify('textDocument/didOpen', {
114
+ textDocument: {
115
+ uri: entry.model.uri.toString(),
116
+ languageId: 'rust',
117
+ version: entry.model.getVersionId(),
118
+ text: entry.model.getValue(),
119
+ },
120
+ })
121
+ .catch(() => { });
122
+ };
123
+ const setFiles = (sourceFilesJson) => {
124
+ closeModels();
101
125
  tabs.replaceChildren();
102
126
  const files = parseFiles(sourceFilesJson);
103
127
  for (const [path, content] of Object.entries(files)) {
104
128
  const uri = monaco.Uri.parse(`${modelRootUri}/${path}`);
105
129
  const model = monaco.editor.createModel(content, path.endsWith('.rs') ? 'rust' : path.endsWith('.toml') ? 'toml' : 'plaintext', uri);
106
- models.push({ path, model });
130
+ const entry = {
131
+ path,
132
+ model,
133
+ changeSubscription: { dispose: () => { } },
134
+ };
135
+ entry.changeSubscription = model.onDidChangeContent(() => {
136
+ if (!path.endsWith('.rs'))
137
+ return;
138
+ void languageClient
139
+ ?.notify('textDocument/didChange', {
140
+ textDocument: {
141
+ uri: model.uri.toString(),
142
+ version: model.getVersionId(),
143
+ },
144
+ contentChanges: [{ text: model.getValue() }],
145
+ })
146
+ .catch(() => { });
147
+ });
148
+ models.push(entry);
149
+ openModel(entry);
107
150
  const tab = button(path);
108
151
  tab.dataset.path = path;
109
152
  tab.addEventListener('click', () => showModel(path));
@@ -116,9 +159,8 @@ export async function mountLiveCodingIDE(el, options) {
116
159
  const refreshTemplates = () => {
117
160
  template.replaceChildren();
118
161
  const matching = templates.filter((item) => item.target === target.value);
119
- for (const item of matching) {
162
+ for (const item of matching)
120
163
  template.append(new Option(item.title, item.id));
121
- }
122
164
  if (matching[0])
123
165
  setFiles(matching[0].sourceFilesJson);
124
166
  };
@@ -144,117 +186,178 @@ export async function mountLiveCodingIDE(el, options) {
144
186
  draft.addEventListener('click', () => doDeploy(true));
145
187
  stop.addEventListener('click', () => controller.stop());
146
188
  const markerSubscription = monaco.editor.onDidChangeMarkers(() => {
147
- const markers = monaco.editor.getModelMarkers({});
189
+ const markers = monaco.editor.getModelMarkers({ owner: 'crowdy-rust' });
148
190
  const errors = markers.filter((marker) => marker.severity === monaco.MarkerSeverity.Error).length;
149
191
  const warnings = markers.filter((marker) => marker.severity === monaco.MarkerSeverity.Warning).length;
150
192
  problems.textContent = `${errors} error(s) · ${warnings} warning(s)`;
151
193
  });
194
+ let tornDown = false;
195
+ teardownMountedIde = (graceful) => {
196
+ if (tornDown)
197
+ return;
198
+ tornDown = true;
199
+ markerSubscription.dispose();
200
+ for (const disposable of languageDisposables)
201
+ disposable.dispose();
202
+ controller.stop();
203
+ closeModels();
204
+ if (graceful)
205
+ languageClient?.shutdown();
206
+ else
207
+ languageClient?.dispose();
208
+ editor.dispose();
209
+ root.remove();
210
+ releaseWorkspace?.();
211
+ releaseWorkspace = null;
212
+ };
152
213
  refreshTemplates();
153
- const authenticated = await authenticatedSocket(options.languageServiceUrl, token, String(options.appId), sourceJson());
154
- socket = authenticated.socket;
155
- const initialSource = sourceJson();
156
- modelRootUri = authenticated.workspaceUri.replace(/\/$/, '');
157
- setFiles(initialSource);
158
- const adapter = websocketAdapter(socket);
159
- const reader = new wsJsonRpc.WebSocketMessageReader(adapter);
160
- const writer = new wsJsonRpc.WebSocketMessageWriter(adapter);
161
- languageClient = new languageClientModule.MonacoLanguageClient({
162
- name: 'Crowdy Rust',
163
- clientOptions: {
164
- documentSelector: [{ language: 'rust' }, { language: 'toml' }],
165
- workspaceFolder: {
166
- uri: monaco.Uri.parse(modelRootUri),
167
- name: 'player-mod',
168
- index: 0,
214
+ languageWorker =
215
+ options.languageWorkerFactory?.() ??
216
+ workerTransport.createDefaultRustLanguageWorker();
217
+ languageClient = new workerTransport.WorkerLanguageClient(languageWorker, {
218
+ requestTimeoutMs: options.languageRequestTimeoutMs,
219
+ });
220
+ languageDisposables.push(languageClient.onNotification('textDocument/publishDiagnostics', (params) => {
221
+ if (!isRecord(params) || typeof params.uri !== 'string')
222
+ return;
223
+ const model = monaco.editor.getModel(monaco.Uri.parse(params.uri));
224
+ if (!model)
225
+ return;
226
+ if (!isCurrentDiagnosticVersion(params.version, model.getVersionId())) {
227
+ return;
228
+ }
229
+ const diagnostics = Array.isArray(params.diagnostics)
230
+ ? params.diagnostics
231
+ : [];
232
+ monaco.editor.setModelMarkers(model, 'crowdy-rust', diagnostics.map((diagnostic) => ({
233
+ ...toMonacoRange(diagnostic.range),
234
+ severity: diagnosticSeverity(monaco, diagnostic.severity),
235
+ message: diagnostic.message,
236
+ code: diagnostic.code,
237
+ source: diagnostic.source,
238
+ })));
239
+ }));
240
+ await languageClient.initialize({
241
+ processId: null,
242
+ clientInfo: { name: 'CrowdyJS Monaco', version: '1' },
243
+ rootUri: modelRootUri,
244
+ capabilities: {
245
+ general: { positionEncodings: ['utf-16'] },
246
+ textDocument: {
247
+ completion: {},
248
+ hover: { contentFormat: ['markdown', 'plaintext'] },
249
+ documentSymbol: {},
250
+ definition: {},
251
+ publishDiagnostics: { versionSupport: true },
169
252
  },
170
253
  },
171
- messageTransports: { reader, writer },
254
+ initializationOptions: {
255
+ ...(platformIndex === undefined
256
+ ? {}
257
+ : { platformIndex }),
258
+ },
172
259
  });
173
- await languageClient.start();
260
+ for (const entry of models)
261
+ openModel(entry);
262
+ languageDisposables.push(monaco.languages.registerCompletionItemProvider('rust', {
263
+ triggerCharacters: [':', '.'],
264
+ provideCompletionItems: async (model, position) => {
265
+ if (!ownsModel(model.uri))
266
+ return { suggestions: [] };
267
+ const word = model.getWordUntilPosition(position);
268
+ const range = {
269
+ startLineNumber: position.lineNumber,
270
+ startColumn: word.startColumn,
271
+ endLineNumber: position.lineNumber,
272
+ endColumn: position.column,
273
+ };
274
+ const response = (await languageClient?.request('textDocument/completion', lspDocumentPosition(model.uri.toString(), position)));
275
+ return {
276
+ suggestions: (response ?? []).map((item) => ({
277
+ label: item.label,
278
+ kind: completionKind(monaco, item.kind),
279
+ detail: item.detail,
280
+ documentation: item.documentation,
281
+ insertText: item.insertText ?? item.label,
282
+ range,
283
+ })),
284
+ };
285
+ },
286
+ }), monaco.languages.registerHoverProvider('rust', {
287
+ provideHover: async (model, position) => {
288
+ if (!ownsModel(model.uri))
289
+ return null;
290
+ const response = (await languageClient?.request('textDocument/hover', lspDocumentPosition(model.uri.toString(), position)));
291
+ if (!response)
292
+ return null;
293
+ return {
294
+ contents: [{ value: response.contents.value }],
295
+ range: response.range
296
+ ? new monaco.Range(response.range.start.line + 1, response.range.start.character + 1, response.range.end.line + 1, response.range.end.character + 1)
297
+ : undefined,
298
+ };
299
+ },
300
+ }), monaco.languages.registerDocumentSymbolProvider('rust', {
301
+ provideDocumentSymbols: async (model) => {
302
+ if (!ownsModel(model.uri))
303
+ return [];
304
+ const response = (await languageClient?.request('textDocument/documentSymbol', { textDocument: { uri: model.uri.toString() } }));
305
+ return (response ?? []).map((symbol) => ({
306
+ name: symbol.name,
307
+ detail: symbol.detail ?? '',
308
+ kind: symbolKind(monaco, symbol.kind),
309
+ tags: [],
310
+ range: toMonacoRange(symbol.range),
311
+ selectionRange: toMonacoRange(symbol.selectionRange),
312
+ }));
313
+ },
314
+ }), monaco.languages.registerDefinitionProvider('rust', {
315
+ provideDefinition: async (model, position) => {
316
+ if (!ownsModel(model.uri))
317
+ return null;
318
+ const response = (await languageClient?.request('textDocument/definition', lspDocumentPosition(model.uri.toString(), position)));
319
+ if (!response)
320
+ return null;
321
+ return {
322
+ uri: monaco.Uri.parse(response.uri),
323
+ range: toMonacoRange(response.range),
324
+ };
325
+ },
326
+ }));
174
327
  void controller.refreshUsage().catch(() => { });
175
328
  return {
176
329
  controller,
177
- destroy: () => {
178
- markerSubscription.dispose();
179
- controller.stop();
180
- void languageClient?.stop().catch(() => { });
181
- socket?.close();
182
- editor.dispose();
183
- for (const entry of models)
184
- entry.model.dispose();
185
- root.remove();
186
- },
330
+ destroy: () => teardownMountedIde?.(true),
187
331
  };
188
332
  }
189
333
  catch (error) {
190
- console.warn('Monaco live-coding IDE unavailable; using textarea fallback', error);
334
+ if (teardownMountedIde) {
335
+ teardownMountedIde(false);
336
+ }
337
+ else {
338
+ languageClient?.dispose();
339
+ if (!languageClient)
340
+ languageWorker?.terminate();
341
+ releaseWorkspace?.();
342
+ releaseWorkspace = null;
343
+ }
344
+ console.warn('Browser Rust IDE unavailable; using textarea fallback', error);
191
345
  el.replaceChildren();
192
346
  return mountLiveCoding(el, options);
193
347
  }
194
348
  }
195
349
  function installRustSyntaxHighlighting(monaco) {
196
350
  const keywords = new Set([
197
- 'as',
198
- 'async',
199
- 'await',
200
- 'break',
201
- 'const',
202
- 'continue',
203
- 'crate',
204
- 'dyn',
205
- 'else',
206
- 'enum',
207
- 'extern',
208
- 'false',
209
- 'fn',
210
- 'for',
211
- 'if',
212
- 'impl',
213
- 'in',
214
- 'let',
215
- 'loop',
216
- 'match',
217
- 'mod',
218
- 'move',
219
- 'mut',
220
- 'pub',
221
- 'ref',
222
- 'return',
223
- 'self',
224
- 'static',
225
- 'struct',
226
- 'super',
227
- 'trait',
228
- 'true',
229
- 'type',
230
- 'unsafe',
231
- 'use',
232
- 'where',
233
- 'while',
351
+ 'as', 'async', 'await', 'break', 'const', 'continue', 'crate', 'dyn',
352
+ 'else', 'enum', 'extern', 'false', 'fn', 'for', 'if', 'impl', 'in',
353
+ 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return',
354
+ 'self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe',
355
+ 'use', 'where', 'while',
234
356
  ]);
235
357
  const types = new Set([
236
- 'Self',
237
- 'String',
238
- 'Vec',
239
- 'Option',
240
- 'Result',
241
- 'bool',
242
- 'char',
243
- 'str',
244
- 'usize',
245
- 'isize',
246
- 'u8',
247
- 'u16',
248
- 'u32',
249
- 'u64',
250
- 'u128',
251
- 'i8',
252
- 'i16',
253
- 'i32',
254
- 'i64',
255
- 'i128',
256
- 'f32',
257
- 'f64',
358
+ 'Self', 'String', 'Vec', 'Option', 'Result', 'bool', 'char', 'str',
359
+ 'usize', 'isize', 'u8', 'u16', 'u32', 'u64', 'u128', 'i8', 'i16',
360
+ 'i32', 'i64', 'i128', 'f32', 'f64',
258
361
  ]);
259
362
  const state = {
260
363
  clone() {
@@ -272,25 +375,25 @@ function installRustSyntaxHighlighting(monaco) {
272
375
  while (index < line.length) {
273
376
  const rest = line.slice(index);
274
377
  let match;
275
- if ((match = rest.match(/^\s+/))) {
378
+ if ((match = rest.match(/^\s+/u))) {
276
379
  tokens.push({ startIndex: index, scopes: '' });
277
380
  }
278
- else if ((match = rest.match(/^\/\/.*$/))) {
381
+ else if ((match = rest.match(/^\/\/.*$/u))) {
279
382
  tokens.push({ startIndex: index, scopes: 'comment.rust' });
280
383
  }
281
- else if ((match = rest.match(/^\/\*.*?(?:\*\/|$)/))) {
384
+ else if ((match = rest.match(/^\/\*.*?(?:\*\/|$)/u))) {
282
385
  tokens.push({ startIndex: index, scopes: 'comment.rust' });
283
386
  }
284
- else if ((match = rest.match(/^"(?:\\.|[^"\\])*"?/))) {
387
+ else if ((match = rest.match(/^"(?:\\.|[^"\\])*"?/u))) {
285
388
  tokens.push({ startIndex: index, scopes: 'string.rust' });
286
389
  }
287
- else if ((match = rest.match(/^(?:0x[\da-fA-F_]+|\d[\d_]*)/))) {
390
+ else if ((match = rest.match(/^(?:0x[\da-fA-F_]+|\d[\d_]*)/u))) {
288
391
  tokens.push({ startIndex: index, scopes: 'number.rust' });
289
392
  }
290
- else if ((match = rest.match(/^[A-Za-z_]\w*!/))) {
393
+ else if ((match = rest.match(/^[A-Za-z_]\w*!/u))) {
291
394
  tokens.push({ startIndex: index, scopes: 'macro.rust' });
292
395
  }
293
- else if ((match = rest.match(/^[A-Za-z_]\w*/))) {
396
+ else if ((match = rest.match(/^[A-Za-z_]\w*/u))) {
294
397
  const word = match[0];
295
398
  tokens.push({
296
399
  startIndex: index,
@@ -302,7 +405,7 @@ function installRustSyntaxHighlighting(monaco) {
302
405
  });
303
406
  }
304
407
  else {
305
- match = rest.match(/^./);
408
+ match = rest.match(/^./u);
306
409
  tokens.push({ startIndex: index, scopes: 'delimiter.rust' });
307
410
  }
308
411
  index += match?.[0].length ?? 1;
@@ -311,65 +414,77 @@ function installRustSyntaxHighlighting(monaco) {
311
414
  },
312
415
  });
313
416
  }
314
- function resolveToken(token) {
315
- const value = typeof token === 'function' ? token() : token;
316
- return value && value.length > 0 ? value : null;
317
- }
318
417
  function parseFiles(sourceFilesJson) {
319
418
  const parsed = JSON.parse(sourceFilesJson);
320
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
419
+ if (!isRecord(parsed))
321
420
  throw new Error('sourceFilesJson must be an object');
322
- }
323
421
  return Object.fromEntries(Object.entries(parsed).map(([path, value]) => {
324
- if (typeof value !== 'string')
422
+ if (typeof value !== 'string' ||
423
+ path.startsWith('/') ||
424
+ path.split('/').some((part) => part === '' || part === '.' || part === '..')) {
325
425
  throw new Error(`Invalid source file ${path}`);
426
+ }
326
427
  return [path, value];
327
428
  }));
328
429
  }
329
- async function authenticatedSocket(url, token, appId, sourceFilesJson) {
330
- const socket = new WebSocket(url);
331
- let workspaceUri = 'file:///player-mod';
332
- await new Promise((resolve, reject) => {
333
- const timeout = setTimeout(() => reject(new Error('language service timeout')), 10000);
334
- socket.addEventListener('open', () => {
335
- socket.send(JSON.stringify({
336
- type: 'authenticate',
337
- token,
338
- appId,
339
- sourceFilesJson,
340
- }));
341
- }, { once: true });
342
- socket.addEventListener('message', (event) => {
343
- try {
344
- const message = JSON.parse(String(event.data));
345
- if (message.type !== 'ready' || !message.workspaceUri) {
346
- throw new Error('language service refused session');
347
- }
348
- workspaceUri = message.workspaceUri;
349
- clearTimeout(timeout);
350
- resolve();
351
- }
352
- catch (error) {
353
- clearTimeout(timeout);
354
- reject(error);
355
- }
356
- }, { once: true });
357
- socket.addEventListener('close', (event) => {
358
- clearTimeout(timeout);
359
- reject(new Error(event.reason || 'language service closed'));
360
- }, { once: true });
361
- });
362
- return { socket, workspaceUri };
430
+ function lspDocumentPosition(uri, position) {
431
+ return {
432
+ textDocument: { uri },
433
+ position: {
434
+ line: position.lineNumber - 1,
435
+ character: position.column - 1,
436
+ },
437
+ };
363
438
  }
364
- function websocketAdapter(socket) {
439
+ function toMonacoRange(range) {
365
440
  return {
366
- send: (content) => socket.send(content),
367
- onMessage: (callback) => socket.addEventListener('message', (event) => callback(event.data)),
368
- onError: (callback) => socket.addEventListener('error', (event) => callback(event)),
369
- onClose: (callback) => socket.addEventListener('close', (event) => callback(event.code, event.reason)),
370
- dispose: () => socket.close(),
441
+ startLineNumber: range.start.line + 1,
442
+ startColumn: range.start.character + 1,
443
+ endLineNumber: range.end.line + 1,
444
+ endColumn: range.end.character + 1,
371
445
  };
372
446
  }
447
+ export function isCurrentDiagnosticVersion(version, currentVersion) {
448
+ return (version === undefined ||
449
+ (Number.isSafeInteger(version) && version === currentVersion));
450
+ }
451
+ function diagnosticSeverity(monaco, severity) {
452
+ return severity === 1
453
+ ? monaco.MarkerSeverity.Error
454
+ : severity === 2
455
+ ? monaco.MarkerSeverity.Warning
456
+ : severity === 3
457
+ ? monaco.MarkerSeverity.Info
458
+ : monaco.MarkerSeverity.Hint;
459
+ }
460
+ function completionKind(monaco, kind) {
461
+ const kinds = monaco.languages.CompletionItemKind;
462
+ return ({
463
+ 3: kinds.Function,
464
+ 5: kinds.Field,
465
+ 6: kinds.Variable,
466
+ 7: kinds.Class,
467
+ 9: kinds.Module,
468
+ 13: kinds.Enum,
469
+ 14: kinds.Keyword,
470
+ 15: kinds.Snippet,
471
+ 21: kinds.Constant,
472
+ }[kind] ?? kinds.Text);
473
+ }
474
+ function symbolKind(monaco, kind) {
475
+ const kinds = monaco.languages.SymbolKind;
476
+ return ({
477
+ 2: kinds.Module,
478
+ 5: kinds.Class,
479
+ 6: kinds.Method,
480
+ 10: kinds.Enum,
481
+ 11: kinds.Interface,
482
+ 12: kinds.Function,
483
+ 13: kinds.Variable,
484
+ 14: kinds.Constant,
485
+ 23: kinds.Struct,
486
+ }[kind] ?? kinds.Object);
487
+ }
373
488
  function select(values) {
374
489
  const element = document.createElement('select');
375
490
  for (const value of values)
@@ -0,0 +1,8 @@
1
+ export { LiveCodingController, type LiveCodingControllerOptions, type LiveCodingStatus, type LiveCodingTarget, } from './live-coding-controller.js';
2
+ export { mountLiveCoding, type LiveCodingHandle, type MountLiveCodingOptions, } from './mount.js';
3
+ export { mountLiveCodingIDE, type MountLiveCodingIDEOptions, } from './ide.js';
4
+ export { PLAYER_CODE_TEMPLATES, templateById, type PlayerCodeTemplate, } from './templates.js';
5
+ export { EMBEDDED_PLATFORM_INDEX, MAX_PLATFORM_CRATES, MAX_PLATFORM_CRATE_FIELD_LENGTH, MAX_PLATFORM_INDEX_BYTES, computePlatformIndexContentHash, loadPlatformIndex, type PlatformCrate, type PlatformIndex, type PlatformSymbol, type PlatformSymbolKind, } from './platform-index.js';
6
+ export { DEFAULT_VFS_LIMITS, VfsLimitError, VirtualFileSystem, offsetAt, type VirtualDocument, type VirtualFileSystemLimits, } from './vfs.js';
7
+ export { WorkerLanguageClient, WorkerMessageReader, WorkerMessageWriter, createDefaultRustLanguageWorker, type LanguageWorkerLike, type WorkerLanguageClientOptions, } from './worker-transport.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/live-coding/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,eAAe,EACf,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,GAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,+BAA+B,EAC/B,wBAAwB,EACxB,+BAA+B,EAC/B,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,uBAAuB,GAC7B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,+BAA+B,EAC/B,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,GACjC,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { LiveCodingController, } from './live-coding-controller.js';
2
+ export { mountLiveCoding, } from './mount.js';
3
+ export { mountLiveCodingIDE, } from './ide.js';
4
+ export { PLAYER_CODE_TEMPLATES, templateById, } from './templates.js';
5
+ export { EMBEDDED_PLATFORM_INDEX, MAX_PLATFORM_CRATES, MAX_PLATFORM_CRATE_FIELD_LENGTH, MAX_PLATFORM_INDEX_BYTES, computePlatformIndexContentHash, loadPlatformIndex, } from './platform-index.js';
6
+ export { DEFAULT_VFS_LIMITS, VfsLimitError, VirtualFileSystem, offsetAt, } from './vfs.js';
7
+ export { WorkerLanguageClient, WorkerMessageReader, WorkerMessageWriter, createDefaultRustLanguageWorker, } from './worker-transport.js';