qsharp-lang 0.1.0-dev.1

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 (55) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +74 -0
  3. package/dist/browser.d.ts +26 -0
  4. package/dist/browser.js +156 -0
  5. package/dist/cancellation.d.ts +10 -0
  6. package/dist/cancellation.js +31 -0
  7. package/dist/compiler/common.d.ts +31 -0
  8. package/dist/compiler/common.js +47 -0
  9. package/dist/compiler/compiler.d.ts +28 -0
  10. package/dist/compiler/compiler.js +62 -0
  11. package/dist/compiler/events.d.ts +54 -0
  12. package/dist/compiler/events.js +92 -0
  13. package/dist/compiler/worker-browser.d.ts +1 -0
  14. package/dist/compiler/worker-browser.js +43 -0
  15. package/dist/compiler/worker-node.d.ts +1 -0
  16. package/dist/compiler/worker-node.js +41 -0
  17. package/dist/compiler/worker-proxy.d.ts +7 -0
  18. package/dist/compiler/worker-proxy.js +16 -0
  19. package/dist/debug-service/debug-service.d.ts +35 -0
  20. package/dist/debug-service/debug-service.js +136 -0
  21. package/dist/debug-service/worker-browser.d.ts +1 -0
  22. package/dist/debug-service/worker-browser.js +32 -0
  23. package/dist/debug-service/worker-node.d.ts +1 -0
  24. package/dist/debug-service/worker-node.js +30 -0
  25. package/dist/debug-service/worker-proxy.d.ts +7 -0
  26. package/dist/debug-service/worker-proxy.js +22 -0
  27. package/dist/katas-content.generated.d.ts +61 -0
  28. package/dist/katas-content.generated.js +2499 -0
  29. package/dist/katas.d.ts +55 -0
  30. package/dist/katas.js +16 -0
  31. package/dist/language-service/language-service.d.ts +48 -0
  32. package/dist/language-service/language-service.js +85 -0
  33. package/dist/language-service/worker-browser.d.ts +1 -0
  34. package/dist/language-service/worker-browser.js +32 -0
  35. package/dist/language-service/worker-node.d.ts +1 -0
  36. package/dist/language-service/worker-node.js +30 -0
  37. package/dist/language-service/worker-proxy.d.ts +6 -0
  38. package/dist/language-service/worker-proxy.js +20 -0
  39. package/dist/log.d.ts +33 -0
  40. package/dist/log.js +92 -0
  41. package/dist/main.d.ts +11 -0
  42. package/dist/main.js +82 -0
  43. package/dist/samples.generated.d.ts +6 -0
  44. package/dist/samples.generated.js +62 -0
  45. package/dist/vsdiagnostic.d.ts +27 -0
  46. package/dist/vsdiagnostic.js +117 -0
  47. package/dist/worker-proxy.d.ts +95 -0
  48. package/dist/worker-proxy.js +226 -0
  49. package/lib/node/qsc_wasm.cjs +1010 -0
  50. package/lib/node/qsc_wasm.d.cts +266 -0
  51. package/lib/node/qsc_wasm_bg.wasm +0 -0
  52. package/lib/web/qsc_wasm.d.ts +328 -0
  53. package/lib/web/qsc_wasm.js +1026 -0
  54. package/lib/web/qsc_wasm_bg.wasm +0 -0
  55. package/package.json +35 -0
@@ -0,0 +1,226 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { log } from "./log.js";
4
+ /*
5
+ The WorkerProxy works by queuing up requests to send over to the Worker, only
6
+ ever having one in flight at a time. By queuing on the caller side, this allows
7
+ for cancellation (it checks if a request is cancelled before sending to the worker).
8
+
9
+ The queue contains an entry for each request with the data to send, the promise
10
+ to resolve, the event handler, and the cancellation token. When a request completes
11
+ the next one (if present) is fetched from the queue. If it is marked as cancelled,
12
+ it is resolved immediately, else it is marked as the current request and the command
13
+ sent to the worker. As events occurs on the current request the event handler is
14
+ invoked. When the response is received this is used to resolve the promise and
15
+ complete the request.
16
+ */
17
+ /**
18
+ * Function to create the proxy for a type. To be used from the main thread.
19
+ *
20
+ * @param postMessage A function to post messages to the worker
21
+ * @param terminator A function to call to tear down the worker thread
22
+ * @param methods A map of method names to be proxied and some metadata @see MethodMap
23
+ * @returns The proxy object. The caller should then set the onMsgFromWorker
24
+ * property to a callback that will receive messages from the worker.
25
+ */
26
+ export function createProxy(postMessage, terminator, methods) {
27
+ const queue = [];
28
+ const eventTarget = new EventTarget();
29
+ let curr;
30
+ let state = "idle";
31
+ function setState(newState) {
32
+ if (state === newState)
33
+ return;
34
+ state = newState;
35
+ if (proxy.onstatechange)
36
+ proxy.onstatechange(state);
37
+ }
38
+ function queueRequest(msg, requestEventTarget, cancellationToken) {
39
+ return new Promise((resolve, reject) => {
40
+ queue.push({
41
+ type: msg.type,
42
+ args: msg.args,
43
+ resolve,
44
+ reject,
45
+ requestEventTarget,
46
+ cancellationToken,
47
+ });
48
+ // If nothing was running when this got added, kick off processing
49
+ if (queue.length === 1)
50
+ doNextRequest();
51
+ });
52
+ }
53
+ function doNextRequest() {
54
+ if (curr)
55
+ return;
56
+ while ((curr = queue.shift())) {
57
+ // eslint-disable-line no-cond-assign
58
+ if (curr.cancellationToken?.isCancellationRequested) {
59
+ curr.reject("cancelled");
60
+ continue;
61
+ }
62
+ else {
63
+ break;
64
+ }
65
+ }
66
+ if (!curr) {
67
+ // Nothing else queued, signal that we're now idle and exit.
68
+ log.debug("Proxy: Worker queue is empty");
69
+ setState("idle");
70
+ return;
71
+ }
72
+ const msg = { type: curr.type, args: curr.args };
73
+ if (methods[curr.type] === "requestWithProgress") {
74
+ setState("busy");
75
+ }
76
+ log.debug("Proxy: Posting message to worker: %o", msg);
77
+ postMessage(msg);
78
+ }
79
+ function onMsgFromWorker(msg) {
80
+ if (log.getLogLevel() >= 4)
81
+ log.debug("Proxy: Received message from worker: %s", JSON.stringify(msg));
82
+ if (msg.messageType === "event") {
83
+ // For telemetry events, just log and exit. There is nothing else waiting to consume them.
84
+ if (msg.type === "telemetry-event") {
85
+ const detail = msg.detail;
86
+ log.logTelemetry(detail);
87
+ return;
88
+ }
89
+ const event = new Event(msg.type);
90
+ event.detail = msg.detail;
91
+ log.debug("Proxy: Posting event: %o", msg);
92
+ // Post to a currently attached event target if there's a "requestWithProgress"
93
+ // in progress
94
+ curr?.requestEventTarget?.dispatchEvent(event);
95
+ // Also post to the general event target
96
+ eventTarget.dispatchEvent(event);
97
+ }
98
+ else if (msg.messageType === "response") {
99
+ if (!curr) {
100
+ log.error("Proxy: No active request when message received: %o", msg);
101
+ return;
102
+ }
103
+ const result = {
104
+ success: msg.result.success,
105
+ data: msg.result.success ? msg.result.result : msg.result.error,
106
+ };
107
+ if (result.success) {
108
+ curr.resolve(result.data);
109
+ curr = undefined;
110
+ doNextRequest();
111
+ }
112
+ else {
113
+ curr.reject(result.data);
114
+ curr = undefined;
115
+ doNextRequest();
116
+ }
117
+ return;
118
+ }
119
+ }
120
+ // Create the proxy object to be returned
121
+ const proxy = {};
122
+ // Assign each method with the desired proxying behavior
123
+ for (const methodName of Object.keys(methods)) {
124
+ // @ts-expect-error - tricky to derive the type of the actual method here
125
+ proxy[methodName] = (...args) => {
126
+ let requestEventTarget = undefined;
127
+ switch (methods[methodName]) {
128
+ case "addEventListener":
129
+ {
130
+ // @ts-expect-error - can't get the typing of the rest parameters quite right
131
+ eventTarget.addEventListener(...args);
132
+ }
133
+ break;
134
+ case "removeEventListener":
135
+ {
136
+ // @ts-expect-error - can't get the typing of the rest parameters quite right
137
+ eventTarget.removeEventListener(...args);
138
+ }
139
+ break;
140
+ case "requestWithProgress": {
141
+ // For progress methods, the last argument is the event target
142
+ requestEventTarget = args[args.length - 1];
143
+ args = args.slice(0, args.length - 1);
144
+ }
145
+ // fallthrough
146
+ case "request": {
147
+ return queueRequest({ type: methodName, args }, requestEventTarget);
148
+ }
149
+ }
150
+ };
151
+ }
152
+ proxy.onstatechange = null;
153
+ proxy.terminate = () => {
154
+ // Kill the worker without a chance to shutdown. May be needed if it is not responding.
155
+ log.info("Proxy: Terminating the worker");
156
+ if (curr) {
157
+ log.debug("Proxy: Terminating running worker item of type: %s", curr.type);
158
+ curr.reject("terminated");
159
+ }
160
+ // Reject any outstanding items
161
+ while (queue.length) {
162
+ const item = queue.shift();
163
+ log.debug("Proxy: Terminating outstanding work item of type: %s", item?.type);
164
+ item?.reject("terminated");
165
+ }
166
+ terminator();
167
+ };
168
+ proxy.onMsgFromWorker = onMsgFromWorker;
169
+ return proxy;
170
+ }
171
+ /**
172
+ * Function to wrap a service in a dispatcher. To be used in the worker thread.
173
+ *
174
+ * @param service The service to be wrapped
175
+ * @param methods A map of method names. Should match the list passed into @see createProxy.
176
+ * @param eventNames The list of event names that the service can emit
177
+ * @param postMessage A function to post messages back to the main thread
178
+ * @returns A function that takes a message and invokes the corresponding
179
+ * method on the service. The caller should then set this method as a message handler.
180
+ */
181
+ export function createDispatcher(postMessage, service, methods, eventNames) {
182
+ log.debug("Worker: Constructing WorkerEventHandler");
183
+ function logAndPost(msg) {
184
+ log.debug("Worker: Sending %s message from worker: %o", msg.messageType, msg);
185
+ postMessage(msg);
186
+ }
187
+ const eventTarget = new EventTarget();
188
+ eventNames.forEach((eventName) => {
189
+ // Subscribe to all known events and forward them as messages to the main thread.
190
+ eventTarget.addEventListener(eventName, (ev) => {
191
+ logAndPost({
192
+ messageType: "event",
193
+ type: ev.type,
194
+ detail: ev.detail,
195
+ });
196
+ });
197
+ // If there's an addEventListener on the object itself, forward those events as well.
198
+ if (service.addEventListener) {
199
+ service.addEventListener(eventName, (ev) => {
200
+ logAndPost({
201
+ messageType: "event",
202
+ type: ev.type,
203
+ detail: ev.detail,
204
+ });
205
+ });
206
+ }
207
+ });
208
+ return function invokeMethod(req) {
209
+ // Pass the eventTarget to the methods marked as taking progress
210
+ return service[req.type]
211
+ .call(service, ...req.args, methods[req.type] === "requestWithProgress" ? eventTarget : undefined)
212
+ .then((result) => logAndPost({
213
+ messageType: "response",
214
+ type: req.type,
215
+ result: { success: true, result },
216
+ }))
217
+ .catch((err) => logAndPost({
218
+ // If this happens then the wasm code likely threw an exception/panicked rather than
219
+ // completing gracefully and fullfilling the promise. Communicate to the client
220
+ // that there was an error and it should reject the current request
221
+ messageType: "response",
222
+ type: req.type,
223
+ result: { success: false, error: err },
224
+ }));
225
+ };
226
+ }