diff --git a/src/wasm-lib/pkg/lang/std/engineConnection.ts b/src/wasm-lib/pkg/lang/std/engineConnection.ts new file mode 100644 index 000000000..169b198d4 --- /dev/null +++ b/src/wasm-lib/pkg/lang/std/engineConnection.ts @@ -0,0 +1,2144 @@ +import { Program, SourceRange } from 'lang/wasm' +import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env' +import { Models } from '@kittycad/lib' +import { exportSave } from 'lib/exportSave' +import { deferExecution, isOverlap, uuidv4 } from 'lib/utils' +import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme' +import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes' +import { + ArtifactGraph, + EngineCommand, + OrderedCommand, + ResponseMap, + createArtifactGraph, +} from 'lang/std/artifactGraph' +import { useModelingContext } from 'hooks/useModelingContext' +import { exportMake } from 'lib/exportMake' +import toast from 'react-hot-toast' +import { SettingsViaQueryString } from 'lib/settings/settingsTypes' +import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants' +import { KclManager } from 'lang/KclSingleton' + +// TODO(paultag): This ought to be tweakable. +const pingIntervalMs = 5_000 + +function isHighlightSetEntity_type( + data: any +): data is Models['HighlightSetEntity_type'] { + return data.entity_id && data.sequence +} + +type OkWebSocketResponseData = Models['OkWebSocketResponseData_type'] + +interface NewTrackArgs { + conn: EngineConnection + mediaStream: MediaStream +} + +export enum ExportIntent { + Save = 'save', + Make = 'make', +} + +type ClientMetrics = Models['ClientMetrics_type'] + +interface WebRTCClientMetrics extends ClientMetrics { + rtc_frame_height: number + rtc_frame_width: number + rtc_packets_lost: number + rtc_pli_count: number + rtc_pause_count: number + rtc_total_pauses_duration_sec: number +} + +type Value = U extends undefined + ? { type: T; value: U } + : U extends void + ? { type: T } + : { type: T; value: U } + +type State = Value + +export enum EngineConnectionStateType { + Fresh = 'fresh', + Connecting = 'connecting', + ConnectionEstablished = 'connection-established', + Disconnecting = 'disconnecting', + Disconnected = 'disconnected', +} + +export enum DisconnectingType { + Error = 'error', + Timeout = 'timeout', + Quit = 'quit', + Pause = 'pause', +} + +// Sorted by severity +export enum ConnectionError { + Unset = 0, + LongLoadingTime, + + ICENegotiate, + DataChannelError, + WebSocketError, + LocalDescriptionInvalid, + + // These are more severe than protocol errors because they don't even allow + // the program to do any protocol messages in the first place if they occur. + MissingAuthToken, + BadAuthToken, + TooManyConnections, + + // An unknown error is the most severe because it has not been classified + // or encountered before. + Unknown, +} + +export const CONNECTION_ERROR_TEXT: Record = { + [ConnectionError.Unset]: '', + [ConnectionError.LongLoadingTime]: + 'Loading is taking longer than expected...', + [ConnectionError.ICENegotiate]: 'ICE negotiation failed.', + [ConnectionError.DataChannelError]: 'The data channel signaled an error.', + [ConnectionError.WebSocketError]: 'The websocket signaled an error.', + [ConnectionError.LocalDescriptionInvalid]: + 'The local description is invalid.', + [ConnectionError.MissingAuthToken]: + 'Your authorization token is missing; please login again.', + [ConnectionError.BadAuthToken]: + 'Your authorization token is invalid; please login again.', + [ConnectionError.TooManyConnections]: 'There are too many connections.', + [ConnectionError.Unknown]: + 'An unexpected error occurred. Please report this to us.', +} + +export interface ErrorType { + // The error we've encountered. + error: ConnectionError + + // Additional context. + context?: any + + // We assign this in the state setter because we may have not failed at + // a Connecting state, which we check for there. + lastConnectingValue?: ConnectingValue +} + +export type DisconnectingValue = + | State + | State + | State + | State + +// These are ordered by the expected sequence. +export enum ConnectingType { + WebSocketConnecting = 'websocket-connecting', + WebSocketOpen = 'websocket-open', + PeerConnectionCreated = 'peer-connection-created', + ICEServersSet = 'ice-servers-set', + SetLocalDescription = 'set-local-description', + OfferedSdp = 'offered-sdp', + ReceivedSdp = 'received-sdp', + SetRemoteDescription = 'set-remote-description', + WebRTCConnecting = 'webrtc-connecting', + ICECandidateReceived = 'ice-candidate-received', + TrackReceived = 'track-received', + DataChannelRequested = 'data-channel-requested', + DataChannelConnecting = 'data-channel-connecting', + DataChannelEstablished = 'data-channel-established', +} + +export enum ConnectingTypeGroup { + WebSocket = 'WebSocket', + ICE = 'ICE', + WebRTC = 'WebRTC', +} + +export const initialConnectingTypeGroupState: Record< + ConnectingTypeGroup, + [ConnectingType, boolean | undefined][] +> = { + [ConnectingTypeGroup.WebSocket]: [ + [ConnectingType.WebSocketConnecting, undefined], + [ConnectingType.WebSocketOpen, undefined], + ], + [ConnectingTypeGroup.ICE]: [ + [ConnectingType.PeerConnectionCreated, undefined], + [ConnectingType.ICEServersSet, undefined], + [ConnectingType.SetLocalDescription, undefined], + [ConnectingType.OfferedSdp, undefined], + [ConnectingType.ReceivedSdp, undefined], + [ConnectingType.SetRemoteDescription, undefined], + [ConnectingType.WebRTCConnecting, undefined], + [ConnectingType.ICECandidateReceived, undefined], + ], + [ConnectingTypeGroup.WebRTC]: [ + [ConnectingType.TrackReceived, undefined], + [ConnectingType.DataChannelRequested, undefined], + [ConnectingType.DataChannelConnecting, undefined], + [ConnectingType.DataChannelEstablished, undefined], + ], +} + +export type ConnectingValue = + | State + | State + | State + | State + | State + | State + | State + | State + | State + | State + | State + | State + | State + | State + +export type EngineConnectionState = + | State + | State + | State + | State + | State + +export type PingPongState = 'OK' | 'TIMEOUT' + +export enum EngineConnectionEvents { + // Fires for each ping-pong success or failure. + PingPongChanged = 'ping-pong-changed', // (state: PingPongState) => void + + // For now, this is only used by the NetworkHealthIndicator. + // We can eventually use it for more, but one step at a time. + ConnectionStateChanged = 'connection-state-changed', // (state: EngineConnectionState) => void + + // These are used for the EngineCommandManager and were created + // before onConnectionStateChange existed. + ConnectionStarted = 'connection-started', // (engineConnection: EngineConnection) => void + Opened = 'opened', // (engineConnection: EngineConnection) => void + Closed = 'closed', // (engineConnection: EngineConnection) => void + NewTrack = 'new-track', // (track: NewTrackArgs) => void +} + +// EngineConnection encapsulates the connection(s) to the Engine +// for the EngineCommandManager; namely, the underlying WebSocket +// and WebRTC connections. +class EngineConnection extends EventTarget { + websocket?: WebSocket + pc?: RTCPeerConnection + unreliableDataChannel?: RTCDataChannel + mediaStream?: MediaStream + idleMode: boolean = false + promise?: Promise + + onIceCandidate = function ( + this: RTCPeerConnection, + event: RTCPeerConnectionIceEvent + ) {} + onIceCandidateError = function ( + this: RTCPeerConnection, + event: RTCPeerConnectionIceErrorEvent + ) {} + onConnectionStateChange = function (this: RTCPeerConnection, event: Event) {} + onDataChannelOpen = function (this: RTCDataChannel, event: Event) {} + onDataChannelClose = function (this: RTCDataChannel, event: Event) {} + onDataChannelError = function (this: RTCDataChannel, event: Event) {} + onDataChannelMessage = function (this: RTCDataChannel, event: MessageEvent) {} + onDataChannel = function ( + this: RTCPeerConnection, + event: RTCDataChannelEvent + ) {} + onTrack = function (this: RTCPeerConnection, event: RTCTrackEvent) {} + onWebSocketOpen = function (event: Event) {} + onWebSocketClose = function (event: Event) {} + onWebSocketError = function (event: Event) {} + onWebSocketMessage = function (event: MessageEvent) {} + onNetworkStatusReady = () => {} + + private _state: EngineConnectionState = { + type: EngineConnectionStateType.Fresh, + } + + get state(): EngineConnectionState { + return this._state + } + + set state(next: EngineConnectionState) { + console.log(`${JSON.stringify(this.state)} → ${JSON.stringify(next)}`) + + if (next.type === EngineConnectionStateType.Disconnecting) { + const sub = next.value + if (sub.type === DisconnectingType.Error) { + // Record the last step we failed at. + // (Check the current state that we're about to override that + // it was a Connecting state.) + if (this._state.type === EngineConnectionStateType.Connecting) { + if (!sub.value) sub.value = { error: ConnectionError.Unknown } + sub.value.lastConnectingValue = this._state.value + } + + console.error(sub.value) + } + } + this._state = next + + this.dispatchEvent( + new CustomEvent(EngineConnectionEvents.ConnectionStateChanged, { + detail: this._state, + }) + ) + } + + readonly url: string + private readonly token?: string + + // TODO: actual type is ClientMetrics + public webrtcStatsCollector?: () => Promise + private engineCommandManager: EngineCommandManager + + private pingPongSpan: { ping?: Date; pong?: Date } + private pingIntervalId: ReturnType = setInterval(() => {}, + 60_000) + isUsingConnectionLite: boolean = false + + constructor({ + engineCommandManager, + url, + token, + callbackOnEngineLiteConnect, + }: { + engineCommandManager: EngineCommandManager + url: string + token?: string + callbackOnEngineLiteConnect?: () => void + }) { + super() + + this.engineCommandManager = engineCommandManager + this.url = url + this.token = token + this.pingPongSpan = { ping: undefined, pong: undefined } + + if (callbackOnEngineLiteConnect) { + this.connectLite(callbackOnEngineLiteConnect) + this.isUsingConnectionLite = true + return + } + + // Without an interval ping, our connection will timeout. + // If this.idleMode is true we skip this logic so only reconnect + // happens on mouse move + this.pingIntervalId = setInterval(() => { + if (this.idleMode) return + + switch (this.state.type as EngineConnectionStateType) { + case EngineConnectionStateType.ConnectionEstablished: + // If there was no reply to the last ping, report a timeout and + // teardown the connection. + if (this.pingPongSpan.ping && !this.pingPongSpan.pong) { + this.dispatchEvent( + new CustomEvent(EngineConnectionEvents.PingPongChanged, { + detail: 'TIMEOUT', + }) + ) + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Timeout, + }, + } + this.disconnectAll() + + // Otherwise check the time between was >= pingIntervalMs, + // and if it was, then it's bad network health. + } else if (this.pingPongSpan.ping && this.pingPongSpan.pong) { + if ( + Math.abs( + this.pingPongSpan.pong.valueOf() - + this.pingPongSpan.ping.valueOf() + ) >= pingIntervalMs + ) { + this.dispatchEvent( + new CustomEvent(EngineConnectionEvents.PingPongChanged, { + detail: 'TIMEOUT', + }) + ) + } else { + this.dispatchEvent( + new CustomEvent(EngineConnectionEvents.PingPongChanged, { + detail: 'OK', + }) + ) + } + } + + this.send({ type: 'ping' }) + this.pingPongSpan.ping = new Date() + this.pingPongSpan.pong = undefined + break + case EngineConnectionStateType.Disconnecting: + case EngineConnectionStateType.Disconnected: + // We will do reconnection elsewhere, because we basically need + // to destroy this EngineConnection, and this setInterval loop + // lives inside it. (lee) I might change this in the future so it's + // outside this class. + break + default: + if (this.isConnecting()) break + // Means we never could do an initial connection. Reconnect everything. + if (!this.pingPongSpan.ping) this.connect() + break + } + }, pingIntervalMs) + + this.connect() + } + + // SHOULD ONLY BE USED FOR VITESTS + connectLite(callback: () => void) { + const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${256}&video_res_height=${256}` + + this.websocket = new WebSocket(url, []) + this.websocket.binaryType = 'arraybuffer' + + this.send = (a) => { + if (!this.websocket) return + this.websocket.send(JSON.stringify(a)) + } + this.onWebSocketOpen = (event) => { + this.send({ + type: 'headers', + headers: { + Authorization: `Bearer ${VITE_KC_DEV_TOKEN}`, + }, + }) + } + this.tearDown = () => {} + this.websocket.addEventListener('open', this.onWebSocketOpen) + + this.websocket?.addEventListener('message', ((event: MessageEvent) => { + const message: Models['WebSocketResponse_type'] = JSON.parse(event.data) + const pending = + this.engineCommandManager.pendingCommands[message.request_id || ''] + if (!('resp' in message)) return + + let resp = message.resp + + // If there's no body to the response, we can bail here. + if (!resp || !resp.type) { + return + } + + switch (resp.type) { + case 'pong': + break + + // Only fires on successful authentication. + case 'ice_server_info': + callback() + return + } + + if ( + !( + pending && + message.success && + (message.resp.type === 'modeling' || + message.resp.type === 'modeling_batch') + ) + ) + return + + if ( + message.resp.type === 'modeling' && + pending.command.type === 'modeling_cmd_req' && + message.request_id + ) { + this.engineCommandManager.responseMap[message.request_id] = message.resp + } else if ( + message.resp.type === 'modeling_batch' && + pending.command.type === 'modeling_cmd_batch_req' + ) { + let individualPendingResponses: { + [key: string]: Models['WebSocketRequest_type'] + } = {} + pending.command.requests.forEach(({ cmd, cmd_id }) => { + individualPendingResponses[cmd_id] = { + type: 'modeling_cmd_req', + cmd, + cmd_id, + } + }) + Object.entries(message.resp.data.responses).forEach( + ([commandId, response]) => { + if (!('response' in response)) return + const command = individualPendingResponses[commandId] + if (!command) return + if (command.type === 'modeling_cmd_req') + this.engineCommandManager.responseMap[commandId] = { + type: 'modeling', + data: { + modeling_response: response.response, + }, + } + } + ) + } + + pending.resolve([message]) + delete this.engineCommandManager.pendingCommands[message.request_id || ''] + }) as EventListener) + } + + isConnecting() { + return this.state.type === EngineConnectionStateType.Connecting + } + + isReady() { + return this.state.type === EngineConnectionStateType.ConnectionEstablished + } + + tearDown(opts?: { idleMode: boolean }) { + this.idleMode = opts?.idleMode ?? false + clearInterval(this.pingIntervalId) + + if (opts?.idleMode) { + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Pause, + }, + } + } + // Pass the state along + if (this.state.type === EngineConnectionStateType.Disconnecting) return + if (this.state.type === EngineConnectionStateType.Disconnected) return + + // Otherwise it's by default a "quit" + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Quit, + }, + } + + this.disconnectAll() + } + + /** + * Attempts to connect to the Engine over a WebSocket, and + * establish the WebRTC connections. + * + * This will attempt the full handshake, and retry if the connection + * did not establish. + */ + connect(reconnecting?: boolean): Promise { + return new Promise((resolve) => { + if (this.isConnecting() || this.isReady()) { + return + } + + const createPeerConnection = () => { + this.pc = new RTCPeerConnection({ + bundlePolicy: 'max-bundle', + }) + + // Other parts of the application expect pc to be initialized when firing. + this.dispatchEvent( + new CustomEvent(EngineConnectionEvents.ConnectionStarted, { + detail: this, + }) + ) + + // Data channels MUST BE specified before SDP offers because requesting + // them affects what our needs are! + const DATACHANNEL_NAME_UMC = 'unreliable_modeling_cmds' + this.pc?.createDataChannel?.(DATACHANNEL_NAME_UMC) + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.DataChannelRequested, + value: DATACHANNEL_NAME_UMC, + }, + } + + this.onIceCandidate = (event: RTCPeerConnectionIceEvent) => { + if (event.candidate === null) { + return + } + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.ICECandidateReceived, + }, + } + + // Request a candidate to use + this.send({ + type: 'trickle_ice', + candidate: { + candidate: event.candidate.candidate, + sdpMid: event.candidate.sdpMid || undefined, + sdpMLineIndex: event.candidate.sdpMLineIndex || undefined, + usernameFragment: event.candidate.usernameFragment || undefined, + }, + }) + } + this.pc?.addEventListener?.('icecandidate', this.onIceCandidate) + + this.onIceCandidateError = (_event: Event) => { + const event = _event as RTCPeerConnectionIceErrorEvent + console.warn( + `ICE candidate returned an error: ${event.errorCode}: ${event.errorText} for ${event.url}` + ) + } + this.pc?.addEventListener?.( + 'icecandidateerror', + this.onIceCandidateError + ) + + // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event + // Event type: generic Event type... + this.onConnectionStateChange = (event: any) => { + console.log('connectionstatechange: ' + event.target?.connectionState) + switch (event.target?.connectionState) { + // From what I understand, only after have we done the ICE song and + // dance is it safest to connect the video tracks / stream + case 'connected': + // Let the browser attach to the video stream now + this.dispatchEvent( + new CustomEvent(EngineConnectionEvents.NewTrack, { + detail: { conn: this, mediaStream: this.mediaStream! }, + }) + ) + break + case 'disconnected': + case 'failed': + this.pc?.removeEventListener('icecandidate', this.onIceCandidate) + this.pc?.removeEventListener( + 'icecandidateerror', + this.onIceCandidateError + ) + this.pc?.removeEventListener( + 'connectionstatechange', + this.onConnectionStateChange + ) + this.pc?.removeEventListener('track', this.onTrack) + + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Error, + value: { + error: ConnectionError.ICENegotiate, + context: event, + }, + }, + } + this.disconnectAll() + break + default: + break + } + } + this.pc?.addEventListener?.( + 'connectionstatechange', + this.onConnectionStateChange + ) + + this.onTrack = (event) => { + const mediaStream = event.streams[0] + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.TrackReceived, + }, + } + + this.webrtcStatsCollector = (): Promise => { + return new Promise((resolve, reject) => { + if (mediaStream.getVideoTracks().length !== 1) { + reject(new Error('too many video tracks to report')) + return + } + + let videoTrack = mediaStream.getVideoTracks()[0] + void this.pc?.getStats(videoTrack).then((videoTrackStats) => { + let client_metrics: WebRTCClientMetrics = { + rtc_frames_decoded: 0, + rtc_frames_dropped: 0, + rtc_frames_received: 0, + rtc_frames_per_second: 0, + rtc_freeze_count: 0, + rtc_jitter_sec: 0.0, + rtc_keyframes_decoded: 0, + rtc_total_freezes_duration_sec: 0.0, + rtc_frame_height: 0, + rtc_frame_width: 0, + rtc_packets_lost: 0, + rtc_pli_count: 0, + rtc_pause_count: 0, + rtc_total_pauses_duration_sec: 0.0, + } + + // TODO(paultag): Since we can technically have multiple WebRTC + // video tracks (even if the Server doesn't at the moment), we + // ought to send stats for every video track(?), and add the stream + // ID into it. This raises the cardinality of collected metrics + // when/if we do, but for now, just report the one stream. + + videoTrackStats.forEach((videoTrackReport) => { + if (videoTrackReport.type === 'inbound-rtp') { + client_metrics.rtc_frames_decoded = + videoTrackReport.framesDecoded || 0 + client_metrics.rtc_frames_dropped = + videoTrackReport.framesDropped || 0 + client_metrics.rtc_frames_received = + videoTrackReport.framesReceived || 0 + client_metrics.rtc_frames_per_second = + videoTrackReport.framesPerSecond || 0 + client_metrics.rtc_freeze_count = + videoTrackReport.freezeCount || 0 + client_metrics.rtc_jitter_sec = + videoTrackReport.jitter || 0.0 + client_metrics.rtc_keyframes_decoded = + videoTrackReport.keyFramesDecoded || 0 + client_metrics.rtc_total_freezes_duration_sec = + videoTrackReport.totalFreezesDuration || 0 + client_metrics.rtc_frame_height = + videoTrackReport.frameHeight || 0 + client_metrics.rtc_frame_width = + videoTrackReport.frameWidth || 0 + client_metrics.rtc_packets_lost = + videoTrackReport.packetsLost || 0 + client_metrics.rtc_pli_count = + videoTrackReport.pliCount || 0 + } else if (videoTrackReport.type === 'transport') { + // videoTrackReport.bytesReceived, + // videoTrackReport.bytesSent, + } + }) + resolve(client_metrics) + }) + }) + } + + // The app is eager to use the MediaStream; as soon as onNewTrack is + // called, the following sequence happens: + // EngineConnection.onNewTrack -> StoreState.setMediaStream -> + // Stream.tsx reacts to mediaStream change, setting a video element. + // We wait until connectionstatechange changes to "connected" + // to pass it to the rest of the application. + + this.mediaStream = mediaStream + } + this.pc?.addEventListener?.('track', this.onTrack) + + this.onDataChannel = (event) => { + this.unreliableDataChannel = event.channel + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.DataChannelConnecting, + value: event.channel.label, + }, + } + + this.onDataChannelOpen = (event) => { + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.DataChannelEstablished, + }, + } + + // Everything is now connected. + this.state = { + type: EngineConnectionStateType.ConnectionEstablished, + } + + this.engineCommandManager.inSequence = 1 + + this.dispatchEvent( + new CustomEvent(EngineConnectionEvents.Opened, { detail: this }) + ) + } + this.unreliableDataChannel?.addEventListener( + 'open', + this.onDataChannelOpen + ) + + this.onDataChannelClose = (event) => { + this.unreliableDataChannel?.removeEventListener( + 'open', + this.onDataChannelOpen + ) + this.unreliableDataChannel?.removeEventListener( + 'close', + this.onDataChannelClose + ) + this.unreliableDataChannel?.removeEventListener( + 'error', + this.onDataChannelError + ) + this.unreliableDataChannel?.removeEventListener( + 'message', + this.onDataChannelMessage + ) + this.pc?.removeEventListener('datachannel', this.onDataChannel) + this.disconnectAll() + } + + this.unreliableDataChannel?.addEventListener( + 'close', + this.onDataChannelClose + ) + + this.onDataChannelError = (event) => { + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Error, + value: { + error: ConnectionError.DataChannelError, + context: event, + }, + }, + } + this.disconnectAll() + } + this.unreliableDataChannel?.addEventListener( + 'error', + this.onDataChannelError + ) + + this.onDataChannelMessage = (event) => { + const result: UnreliableResponses = JSON.parse(event.data) + Object.values( + this.engineCommandManager.unreliableSubscriptions[result.type] || + {} + ).forEach( + // TODO: There is only one response that uses the unreliable channel atm, + // highlight_set_entity, if there are more it's likely they will all have the same + // sequence logic, but I'm not sure if we use a single global sequence or a sequence + // per unreliable subscription. + (callback) => { + if ( + result.type === 'highlight_set_entity' && + result?.data?.sequence && + result?.data.sequence > this.engineCommandManager.inSequence + ) { + this.engineCommandManager.inSequence = result.data.sequence + callback(result) + } else if (result.type !== 'highlight_set_entity') { + callback(result) + } + } + ) + } + this.unreliableDataChannel.addEventListener( + 'message', + this.onDataChannelMessage + ) + } + this.pc?.addEventListener?.('datachannel', this.onDataChannel) + } + + const createWebSocketConnection = () => { + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.WebSocketConnecting, + }, + } + + this.websocket = new WebSocket(this.url, []) + this.websocket.binaryType = 'arraybuffer' + + this.onWebSocketOpen = (event) => { + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.WebSocketOpen, + }, + } + + // This is required for when KCMA is running stand-alone / within desktop app. + // Otherwise when run in a browser, the token is sent implicitly via + // the Cookie header. + if (this.token) { + this.send({ + type: 'headers', + headers: { + Authorization: `Bearer ${this.token}`, + }, + }) + } + + // Send an initial ping + this.send({ type: 'ping' }) + this.pingPongSpan.ping = new Date() + } + this.websocket.addEventListener('open', this.onWebSocketOpen) + + this.onWebSocketClose = (event) => { + this.websocket?.removeEventListener('open', this.onWebSocketOpen) + this.websocket?.removeEventListener('close', this.onWebSocketClose) + this.websocket?.removeEventListener('error', this.onWebSocketError) + this.websocket?.removeEventListener( + 'message', + this.onWebSocketMessage + ) + + window.removeEventListener( + 'use-network-status-ready', + this.onNetworkStatusReady + ) + + this.disconnectAll() + } + this.websocket.addEventListener('close', this.onWebSocketClose) + + this.onWebSocketError = (event) => { + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Error, + value: { + error: ConnectionError.WebSocketError, + context: event, + }, + }, + } + + this.disconnectAll() + } + this.websocket.addEventListener('error', this.onWebSocketError) + + this.onWebSocketMessage = (event) => { + // In the EngineConnection, we're looking for messages to/from + // the server that relate to the ICE handshake, or WebRTC + // negotiation. There may be other messages (including ArrayBuffer + // messages) that are intended for the GUI itself, so be careful + // when assuming we're the only consumer or that all messages will + // be carefully formatted here. + + if (typeof event.data !== 'string') { + return + } + + const message: Models['WebSocketResponse_type'] = JSON.parse( + event.data + ) + + if (!message.success) { + const errorsString = message?.errors + ?.map((error) => { + return ` - ${error.error_code}: ${error.message}` + }) + .join('\n') + if (message.request_id) { + const artifactThatFailed = + this.engineCommandManager.artifactGraph.get(message.request_id) + console.error( + `Error in response to request ${message.request_id}:\n${errorsString} + failed cmd type was ${artifactThatFailed?.type}` + ) + // Check if this was a pending export command. + if ( + this.engineCommandManager.pendingExport?.commandId === + message.request_id + ) { + // Reject the promise with the error. + this.engineCommandManager.pendingExport.reject(errorsString) + toast.error(errorsString) + this.engineCommandManager.pendingExport = undefined + } + } else { + console.error(`Error from server:\n${errorsString}`) + } + + const firstError = message?.errors[0] + if (firstError.error_code === 'auth_token_invalid') { + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Error, + value: { + error: ConnectionError.BadAuthToken, + context: firstError.message, + }, + }, + } + this.disconnectAll() + } + return + } + + let resp = message.resp + + // If there's no body to the response, we can bail here. + if (!resp || !resp.type) { + return + } + + switch (resp.type) { + case 'pong': + this.pingPongSpan.pong = new Date() + break + + // Only fires on successful authentication. + case 'ice_server_info': + let ice_servers = resp.data?.ice_servers + + // Now that we have some ICE servers it makes sense + // to start initializing the RTCPeerConnection. RTCPeerConnection + // will begin the ICE process. + createPeerConnection() + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.PeerConnectionCreated, + }, + } + + // No ICE servers can be valid in a local dev. env. + if (ice_servers?.length === 0) { + console.warn('No ICE servers') + this.pc?.setConfiguration({ + bundlePolicy: 'max-bundle', + }) + } else { + // When we set the Configuration, we want to always force + // iceTransportPolicy to 'relay', since we know the topology + // of the ICE/STUN/TUN server and the engine. We don't wish to + // talk to the engine in any configuration /other/ than relay + // from a infra POV. + this.pc?.setConfiguration({ + bundlePolicy: 'max-bundle', + iceServers: ice_servers, + iceTransportPolicy: 'relay', + }) + } + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.ICEServersSet, + }, + } + + // We have an ICE Servers set now. We just setConfiguration, so let's + // start adding things we care about to the PeerConnection and let + // ICE negotiation happen in the background. Everything from here + // until the end of this function is setup of our end of the + // PeerConnection and waiting for events to fire our callbacks. + + // Add a transceiver to our SDP offer + this.pc?.addTransceiver('video', { + direction: 'recvonly', + }) + + // Create a session description offer based on our local environment + // that we will send to the remote end. The remote will send back + // what it supports via sdp_answer. + this.pc + ?.createOffer() + .then((offer: RTCSessionDescriptionInit) => { + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.SetLocalDescription, + }, + } + return this.pc?.setLocalDescription(offer).then(() => { + this.send({ + type: 'sdp_offer', + offer: offer as Models['RtcSessionDescription_type'], + }) + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.OfferedSdp, + }, + } + }) + }) + .catch((err: Error) => { + // The local description is invalid, so there's no point continuing. + this.state = { + type: EngineConnectionStateType.Disconnecting, + value: { + type: DisconnectingType.Error, + value: { + error: ConnectionError.LocalDescriptionInvalid, + context: err, + }, + }, + } + this.disconnectAll() + }) + break + + case 'sdp_answer': + let answer = resp.data?.answer + if (!answer || answer.type === 'unspecified') { + return + } + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.ReceivedSdp, + }, + } + + // As soon as this is set, RTCPeerConnection tries to + // establish a connection. + // @ts-ignore + // Have to ignore because dom.ts doesn't have the right type + void this.pc?.setRemoteDescription(answer) + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.SetRemoteDescription, + }, + } + + this.state = { + type: EngineConnectionStateType.Connecting, + value: { + type: ConnectingType.WebRTCConnecting, + }, + } + break + + case 'trickle_ice': + let candidate = resp.data?.candidate + void this.pc?.addIceCandidate(candidate as RTCIceCandidateInit) + break + + case 'metrics_request': + if (this.webrtcStatsCollector === undefined) { + // TODO: Error message here? + return + } + void this.webrtcStatsCollector().then((client_metrics) => { + this.send({ + type: 'metrics_response', + metrics: client_metrics, + }) + }) + break + } + } + this.websocket.addEventListener('message', this.onWebSocketMessage) + } + + if (reconnecting) { + createWebSocketConnection() + } else { + this.onNetworkStatusReady = () => { + createWebSocketConnection() + } + window.addEventListener( + 'use-network-status-ready', + this.onNetworkStatusReady + ) + } + }) + } + // Do not change this back to an object or any, we should only be sending the + // WebSocketRequest type! + unreliableSend(message: Models['WebSocketRequest_type']) { + // TODO(paultag): Add in logic to determine the connection state and + // take actions if needed? + this.unreliableDataChannel?.send( + typeof message === 'string' ? message : JSON.stringify(message) + ) + } + // Do not change this back to an object or any, we should only be sending the + // WebSocketRequest type! + send(message: Models['WebSocketRequest_type']) { + // Not connected, don't send anything + if (this.websocket?.readyState === 3) return + + // TODO(paultag): Add in logic to determine the connection state and + // take actions if needed? + this.websocket?.send( + typeof message === 'string' ? message : JSON.stringify(message) + ) + } + disconnectAll() { + if (this.websocket?.readyState === 1) { + this.websocket?.close() + } + if (this.unreliableDataChannel?.readyState === 'open') { + this.unreliableDataChannel?.close() + } + if (this.pc?.connectionState === 'connected') { + this.pc?.close() + } + + this.webrtcStatsCollector = undefined + + // Already triggered + if (this.state.type === EngineConnectionStateType.Disconnected) return + + const closedPc = !this.pc || this.pc?.connectionState === 'closed' + const closedUDC = + !this.unreliableDataChannel || + this.unreliableDataChannel?.readyState === 'closed' + + // Do not check when timing out because websockets take forever to + // report their disconnected state. + const closedWS = + (this.state.type === EngineConnectionStateType.Disconnecting && + this.state.value.type === DisconnectingType.Timeout) || + !this.websocket || + this.websocket?.readyState === 3 + + if (closedPc && closedUDC && closedWS) { + // Do not notify the rest of the program that we have cut off anything. + this.state = { type: EngineConnectionStateType.Disconnected } + } + } +} + +type ModelTypes = Models['OkModelingCmdResponse_type']['type'] + +type UnreliableResponses = Extract< + Models['OkModelingCmdResponse_type'], + { type: 'highlight_set_entity' | 'camera_drag_move' } +> +export interface UnreliableSubscription { + event: T + callback: (data: Extract) => void +} + +// TODO: Should eventually be replaced with native EventTarget event system, +// as it manages events in a more familiar way to other developers. +export interface Subscription { + event: T + callback: ( + data: Extract + ) => void +} + +export type CommandLog = + | { + type: 'send-modeling' + data: EngineCommand + } + | { + type: 'send-scene' + data: EngineCommand + } + | { + type: 'receive-reliable' + data: OkWebSocketResponseData + id: string + cmd_type?: string + } + | { + type: 'execution-done' + data: null + } + +export enum EngineCommandManagerEvents { + // engineConnection is available but scene setup may not have run + EngineAvailable = 'engine-available', + + // the whole scene is ready (settings loaded) + SceneReady = 'scene-ready', +} + +/** + * The EngineCommandManager is the main interface to the Engine for Modeling App. + * + * It is responsible for sending commands to the Engine, and managing the state + * of those commands. It also sets up and tears down the connection to the Engine + * through the {@link EngineConnection} class. + * + * As commands are send their state is tracked in {@link pendingCommands} and clear as soon as we receive a response. + * + * Also all commands that are sent are kept track of in {@link orderedCommands} and their responses are kept in {@link responseMap} + * Both of these data structures are used to process the {@link artifactGraph}. + */ + +interface PendingMessage { + command: EngineCommand + range: SourceRange + idToRangeMap: { [key: string]: SourceRange } + resolve: (data: [Models['WebSocketResponse_type']]) => void + reject: (reason: string) => void + promise: Promise<[Models['WebSocketResponse_type']]> + isSceneCommand: boolean +} +export class EngineCommandManager extends EventTarget { + /** + * The artifactGraph is a client-side representation of the commands that have been sent + * see: src/lang/std/artifactGraph-README.md for a full explanation. + */ + artifactGraph: ArtifactGraph = new Map() + /** + * The pendingCommands object is a map of the commands that have been sent to the engine that are still waiting on a reply + */ + pendingCommands: { + [commandId: string]: PendingMessage + } = {} + /** + * The orderedCommands array of all the the commands sent to the engine, un-folded from batches, and made into one long + * list of the individual commands, this is used to process all the commands into the artifactGraph + */ + orderedCommands: Array = [] + /** + * A map of the responses to the {@link orderedCommands}, when processing the commands into the artifactGraph, this response map allow + * us to look up the response by command id + */ + responseMap: ResponseMap = {} + /** + * A counter that is incremented with each command sent over the *unreliable* channel to the engine. + * This is compared to the latest received {@link inSequence} number to determine if we should ignore + * any out-of-order late responses in the unreliable channel. + */ + outSequence = 1 + /** + * The latest sequence number received from the engine over the *unreliable* channel. + * This is compared to the {@link outSequence} number to determine if we should ignore + * any out-of-order late responses in the unreliable channel. + */ + inSequence = 1 + engineConnection?: EngineConnection + defaultPlanes: DefaultPlanes | null = null + commandLogs: CommandLog[] = [] + pendingExport?: { + resolve: (a: null) => void + reject: (reason: string) => void + commandId: string + } + settings: SettingsViaQueryString + + /** + * Export intent traxcks the intent of the export. If it is null there is no + * export in progress. Otherwise it is an enum value of the intent. + * Another export cannot be started if one is already in progress. + */ + private _exportIntent: ExportIntent | null = null + _commandLogCallBack: (command: CommandLog[]) => void = () => {} + + subscriptions: { + [event: string]: { + [localUnsubscribeId: string]: (a: any) => void + } + } = {} as any + unreliableSubscriptions: { + [event: string]: { + [localUnsubscribeId: string]: (a: any) => void + } + } = {} as any + + constructor(settings?: SettingsViaQueryString) { + super() + + this.engineConnection = undefined + this.settings = settings + ? settings + : { + pool: null, + theme: Themes.Dark, + highlightEdges: true, + enableSSAO: true, + showScaleGrid: false, + } + } + + private _camControlsCameraChange = () => {} + set camControlsCameraChange(cb: () => void) { + this._camControlsCameraChange = cb + } + + private getAst: () => Program = () => + ({ start: 0, end: 0, body: [], nonCodeMeta: {} } as any) + set getAstCb(cb: () => Program) { + this.getAst = cb + } + private makeDefaultPlanes: () => Promise | null = () => null + private modifyGrid: (hidden: boolean) => Promise | null = () => null + + private onEngineConnectionOpened = () => {} + private onEngineConnectionClosed = () => {} + private onEngineConnectionStarted = ({ detail: engineConnection }: any) => {} + private onEngineConnectionNewTrack = ({ + detail, + }: CustomEvent) => {} + modelingSend: ReturnType['send'] = + (() => {}) as any + kclManager: null | KclManager = null + + set exportIntent(intent: ExportIntent | null) { + this._exportIntent = intent + } + + get exportIntent() { + return this._exportIntent + } + + start({ + setMediaStream, + setIsStreamReady, + width, + height, + token, + makeDefaultPlanes, + modifyGrid, + settings = { + pool: null, + theme: Themes.Dark, + highlightEdges: true, + enableSSAO: true, + showScaleGrid: false, + }, + // When passed, use a completely separate connecting code path that simply + // opens a websocket and this is a function that is called when connected. + callbackOnEngineLiteConnect, + }: { + callbackOnEngineLiteConnect?: () => void + setMediaStream: (stream: MediaStream) => void + setIsStreamReady: (isStreamReady: boolean) => void + width: number + height: number + token?: string + makeDefaultPlanes: () => Promise + modifyGrid: (hidden: boolean) => Promise + settings?: SettingsViaQueryString + }) { + if (settings) { + this.settings = settings + } + this.makeDefaultPlanes = makeDefaultPlanes + this.modifyGrid = modifyGrid + if (width === 0 || height === 0) { + return + } + + // If we already have an engine connection, just need to resize the stream. + if (this.engineConnection) { + this.handleResize({ + streamWidth: width, + streamHeight: height, + }) + return + } + + const additionalSettings = this.settings.enableSSAO + ? '&post_effect=ssao' + : '' + const pool = !this.settings.pool ? '' : `&pool=${this.settings.pool}` + const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}${additionalSettings}${pool}` + this.engineConnection = new EngineConnection({ + engineCommandManager: this, + url, + token, + callbackOnEngineLiteConnect, + }) + + // Nothing more to do when using a lite engine initialization + if (callbackOnEngineLiteConnect) return + + this.dispatchEvent( + new CustomEvent(EngineCommandManagerEvents.EngineAvailable, { + detail: this.engineConnection, + }) + ) + + this.onEngineConnectionOpened = async () => { + // Set the stream background color + // This takes RGBA values from 0-1 + // So we convert from the conventional 0-255 found in Figma + + void this.sendSceneCommand({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'set_background_color', + color: getThemeColorForEngine(this.settings.theme), + }, + }) + + // Sets the default line colors + const opposingTheme = getOppositeTheme(this.settings.theme) + this.sendSceneCommand({ + cmd_id: uuidv4(), + type: 'modeling_cmd_req', + cmd: { + type: 'set_default_system_properties', + color: getThemeColorForEngine(opposingTheme), + }, + }) + + // Set the edge lines visibility + this.sendSceneCommand({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'edge_lines_visible' as any, // TODO: update kittycad.ts to use the correct type + hidden: !this.settings.highlightEdges, + }, + }) + + this._camControlsCameraChange() + this.sendSceneCommand({ + // CameraControls subscribes to default_camera_get_settings response events + // firing this at connection ensure the camera's are synced initially + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_get_settings', + }, + }) + // We want modify the grid first because we don't want it to flash. + // Ideally these would already be default hidden in engine (TODO do + // that) https://github.com/KittyCAD/engine/issues/2282 + this.modifyGrid(!this.settings.showScaleGrid)?.then(async () => { + await this.initPlanes() + setIsStreamReady(true) + + // Other parts of the application should use this to react on scene ready. + this.dispatchEvent( + new CustomEvent(EngineCommandManagerEvents.SceneReady, { + detail: this.engineConnection, + }) + ) + }) + } + + this.engineConnection.addEventListener( + EngineConnectionEvents.Opened, + this.onEngineConnectionOpened + ) + + this.onEngineConnectionClosed = () => { + setIsStreamReady(false) + } + this.engineConnection.addEventListener( + EngineConnectionEvents.Closed, + this.onEngineConnectionClosed + ) + + this.onEngineConnectionStarted = ({ detail: engineConnection }: any) => { + engineConnection?.pc?.addEventListener( + 'datachannel', + (event: RTCDataChannelEvent) => { + let unreliableDataChannel = event.channel + + unreliableDataChannel.addEventListener( + 'message', + (event: MessageEvent) => { + const result: UnreliableResponses = JSON.parse(event.data) + Object.values( + this.unreliableSubscriptions[result.type] || {} + ).forEach( + // TODO: There is only one response that uses the unreliable channel atm, + // highlight_set_entity, if there are more it's likely they will all have the same + // sequence logic, but I'm not sure if we use a single global sequence or a sequence + // per unreliable subscription. + (callback) => { + let data = result?.data + if (isHighlightSetEntity_type(data)) { + if ( + data.sequence !== undefined && + data.sequence > this.inSequence + ) { + this.inSequence = data.sequence + callback(result) + } + } + } + ) + } + ) + } + ) + + // When the EngineConnection starts a connection, we want to register + // callbacks into the WebSocket/PeerConnection. + engineConnection.websocket?.addEventListener('message', (( + event: MessageEvent + ) => { + if (event.data instanceof ArrayBuffer) { + // If the data is an ArrayBuffer, it's the result of an export command, + // because in all other cases we send JSON strings. But in the case of + // export we send a binary blob. + // Pass this to our export function. + if (this.exportIntent === null) { + toast.error( + 'Export intent was not set, but export data was received' + ) + console.error( + 'Export intent was not set, but export data was received' + ) + return + } + + switch (this.exportIntent) { + case ExportIntent.Save: { + exportSave(event.data).then(() => { + this.pendingExport?.resolve(null) + }, this.pendingExport?.reject) + break + } + case ExportIntent.Make: { + exportMake(event.data).then((result) => { + if (result) { + this.pendingExport?.resolve(null) + } else { + this.pendingExport?.reject('Failed to make export') + } + }, this.pendingExport?.reject) + break + } + } + // Set the export intent back to null. + this.exportIntent = null + return + } + + const message: Models['WebSocketResponse_type'] = JSON.parse(event.data) + const pending = this.pendingCommands[message.request_id || ''] + + if (pending && !message.success) { + // handle bad case + pending.reject(`engine error: ${JSON.stringify(message.errors)}`) + delete this.pendingCommands[message.request_id || ''] + } + if ( + !( + pending && + message.success && + (message.resp.type === 'modeling' || + message.resp.type === 'modeling_batch') + ) + ) + return + + if ( + message.resp.type === 'modeling' && + pending.command.type === 'modeling_cmd_req' && + message.request_id + ) { + this.addCommandLog({ + type: 'receive-reliable', + data: message.resp, + id: message?.request_id || '', + cmd_type: pending?.command?.cmd?.type, + }) + + const modelingResponse = message.resp.data.modeling_response + + Object.values( + this.subscriptions[modelingResponse.type] || {} + ).forEach((callback) => callback(modelingResponse)) + + this.responseMap[message.request_id] = message.resp + } else if ( + message.resp.type === 'modeling_batch' && + pending.command.type === 'modeling_cmd_batch_req' + ) { + let individualPendingResponses: { + [key: string]: Models['WebSocketRequest_type'] + } = {} + pending.command.requests.forEach(({ cmd, cmd_id }) => { + individualPendingResponses[cmd_id] = { + type: 'modeling_cmd_req', + cmd, + cmd_id, + } + }) + Object.entries(message.resp.data.responses).forEach( + ([commandId, response]) => { + if (!('response' in response)) return + const command = individualPendingResponses[commandId] + if (!command) return + if (command.type === 'modeling_cmd_req') + this.addCommandLog({ + type: 'receive-reliable', + data: { + type: 'modeling', + data: { + modeling_response: response.response, + }, + }, + id: commandId, + cmd_type: command?.cmd?.type, + }) + + this.responseMap[commandId] = { + type: 'modeling', + data: { + modeling_response: response.response, + }, + } + } + ) + } + + pending.resolve([message]) + delete this.pendingCommands[message.request_id || ''] + }) as EventListener) + + this.onEngineConnectionNewTrack = ({ + detail: { mediaStream }, + }: CustomEvent) => { + mediaStream.getVideoTracks()[0].addEventListener('mute', () => { + console.error( + 'video track mute: check webrtc internals -> inbound rtp' + ) + }) + + setMediaStream(mediaStream) + } + this.engineConnection?.addEventListener( + EngineConnectionEvents.NewTrack, + this.onEngineConnectionNewTrack as EventListener + ) + + this.engineConnection?.connect() + } + this.engineConnection.addEventListener( + EngineConnectionEvents.ConnectionStarted, + this.onEngineConnectionStarted + ) + + return + } + + handleResize({ + streamWidth, + streamHeight, + }: { + streamWidth: number + streamHeight: number + }) { + if (!this.engineConnection?.isReady()) { + return + } + + const resizeCmd: EngineCommand = { + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'reconfigure_stream', + width: streamWidth, + height: streamHeight, + fps: 60, + }, + } + this.engineConnection?.send(resizeCmd) + } + + tearDown(opts?: { idleMode: boolean }) { + if (this.engineConnection) { + for (const pending of Object.values(this.pendingCommands)) { + pending.reject('no connection to send on') + } + + this.engineConnection?.removeEventListener?.( + EngineConnectionEvents.Opened, + this.onEngineConnectionOpened + ) + this.engineConnection.removeEventListener?.( + EngineConnectionEvents.Closed, + this.onEngineConnectionClosed + ) + this.engineConnection.removeEventListener?.( + EngineConnectionEvents.ConnectionStarted, + this.onEngineConnectionStarted + ) + this.engineConnection.removeEventListener?.( + EngineConnectionEvents.NewTrack, + this.onEngineConnectionNewTrack as EventListener + ) + + this.engineConnection?.tearDown(opts) + + // Our window.tearDown assignment causes this case to happen which is + // only really for tests. + // @ts-ignore + } else if (this.engineCommandManager?.engineConnection) { + // @ts-ignore + this.engineCommandManager?.engineConnection?.tearDown(opts) + } + } + async startNewSession() { + this.orderedCommands = [] + this.responseMap = {} + await this.initPlanes() + } + subscribeTo({ + event, + callback, + }: Subscription): () => void { + const localUnsubscribeId = uuidv4() + if (!this.subscriptions[event]) { + this.subscriptions[event] = {} + } + this.subscriptions[event][localUnsubscribeId] = callback + + return () => this.unSubscribeTo(event, localUnsubscribeId) + } + private unSubscribeTo(event: ModelTypes, id: string) { + delete this.subscriptions[event][id] + } + subscribeToUnreliable({ + event, + callback, + }: UnreliableSubscription): () => void { + const localUnsubscribeId = uuidv4() + if (!this.unreliableSubscriptions[event]) { + this.unreliableSubscriptions[event] = {} + } + this.unreliableSubscriptions[event][localUnsubscribeId] = callback + return () => this.unSubscribeToUnreliable(event, localUnsubscribeId) + } + private unSubscribeToUnreliable( + event: UnreliableResponses['type'], + id: string + ) { + delete this.unreliableSubscriptions[event][id] + } + // We make this a separate function so we can call it from wasm. + clearDefaultPlanes() { + this.defaultPlanes = null + } + async wasmGetDefaultPlanes(): Promise { + if (this.defaultPlanes === null) { + await this.initPlanes() + } + return JSON.stringify(this.defaultPlanes) + } + endSession() { + const deleteCmd: EngineCommand = { + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'scene_clear_all', + }, + } + this.clearDefaultPlanes() + this.engineConnection?.send(deleteCmd) + } + addCommandLog(message: CommandLog) { + if (this.commandLogs.length > 500) { + this.commandLogs.shift() + } + this.commandLogs.push(message) + + this._commandLogCallBack([...this.commandLogs]) + } + clearCommandLogs() { + this.commandLogs = [] + this._commandLogCallBack(this.commandLogs) + } + registerCommandLogCallback(callback: (command: CommandLog[]) => void) { + this._commandLogCallBack = callback + } + sendSceneCommand( + command: EngineCommand, + forceWebsocket = false + ): Promise { + if (this.engineConnection === undefined) { + return Promise.resolve(null) + } + + if (!this.engineConnection?.isReady()) { + return Promise.resolve(null) + } + + if ( + !( + command.type === 'modeling_cmd_req' && + (command.cmd.type === 'highlight_set_entity' || + command.cmd.type === 'mouse_move' || + command.cmd.type === 'camera_drag_move' || + command.cmd.type === ('default_camera_perspective_settings' as any)) + ) + ) { + // highlight_set_entity, mouse_move and camera_drag_move are sent over the unreliable channel and are too noisy + this.addCommandLog({ + type: 'send-scene', + data: command, + }) + } + + if (command.type === 'modeling_cmd_batch_req') { + this.engineConnection?.send(command) + // TODO - handlePendingCommands does not handle batch commands + // return this.handlePendingCommand(command.requests[0].cmd_id, command.cmd) + return Promise.resolve(null) + } + if (command.type !== 'modeling_cmd_req') return Promise.resolve(null) + const cmd = command.cmd + if ( + (cmd.type === 'camera_drag_move' || + cmd.type === 'handle_mouse_drag_move' || + cmd.type === 'default_camera_zoom' || + cmd.type === ('default_camera_perspective_settings' as any)) && + this.engineConnection?.unreliableDataChannel && + !forceWebsocket + ) { + ;(cmd as any).sequence = this.outSequence + this.outSequence++ + this.engineConnection?.unreliableSend(command) + return Promise.resolve(null) + } else if ( + cmd.type === 'highlight_set_entity' && + this.engineConnection?.unreliableDataChannel + ) { + cmd.sequence = this.outSequence + this.outSequence++ + this.engineConnection?.unreliableSend(command) + return Promise.resolve(null) + } else if ( + cmd.type === 'mouse_move' && + this.engineConnection.unreliableDataChannel + ) { + cmd.sequence = this.outSequence + this.outSequence++ + this.engineConnection?.unreliableSend(command) + return Promise.resolve(null) + } else if (cmd.type === 'export') { + const promise = new Promise((resolve, reject) => { + this.pendingExport = { + resolve, + reject: (reason: string) => { + this.exportIntent = null + reject(reason) + }, + commandId: command.cmd_id, + } + }) + this.engineConnection?.send(command) + return promise + } + if ( + command.cmd.type === 'default_camera_look_at' || + command.cmd.type === ('default_camera_perspective_settings' as any) + ) { + ;(cmd as any).sequence = this.outSequence++ + } + // since it's not mouse drag or highlighting send over TCP and keep track of the command + return this.sendCommand( + command.cmd_id, + { + command, + idToRangeMap: {}, + range: [0, 0], + }, + true // isSceneCommand + ) + .then(([a]) => a) + .catch((e) => { + // TODO: Previously was never caught, we are not rejecting these pendingCommands but this needs to be handled at some point. + /*noop*/ + return null + }) + } + /** + * A wrapper around the sendCommand where all inputs are JSON strings + */ + async sendModelingCommandFromWasm( + id: string, + rangeStr: string, + commandStr: string, + idToRangeStr: string + ): Promise { + if (this.engineConnection === undefined) return Promise.resolve() + if ( + !this.engineConnection?.isReady() && + !this.engineConnection.isUsingConnectionLite + ) + return Promise.resolve() + if (id === undefined) return Promise.reject(new Error('id is undefined')) + if (rangeStr === undefined) + return Promise.reject(new Error('rangeStr is undefined')) + if (commandStr === undefined) + return Promise.reject(new Error('commandStr is undefined')) + const range: SourceRange = JSON.parse(rangeStr) + const command: EngineCommand = JSON.parse(commandStr) + const idToRangeMap: { [key: string]: SourceRange } = + JSON.parse(idToRangeStr) + + // Current executeAst is stale, going to interrupt, a new executeAst will trigger + // Used in conjunction with rejectAllModelingCommands + if (this?.kclManager?.executeIsStale) { + return Promise.reject(EXECUTE_AST_INTERRUPT_ERROR_MESSAGE) + } + + const resp = await this.sendCommand(id, { + command, + range, + idToRangeMap, + }) + return JSON.stringify(resp[0]) + } + /** + * Common send command function used for both modeling and scene commands + * So that both have a common way to send pending commands with promises for the responses + */ + async sendCommand( + id: string, + message: { + command: PendingMessage['command'] + range: PendingMessage['range'] + idToRangeMap: PendingMessage['idToRangeMap'] + }, + isSceneCommand = false + ): Promise<[Models['WebSocketResponse_type']]> { + const { promise, resolve, reject } = promiseFactory() + this.pendingCommands[id] = { + resolve, + reject, + promise, + command: message.command, + range: message.range, + idToRangeMap: message.idToRangeMap, + isSceneCommand, + } + + if (message.command.type === 'modeling_cmd_req') { + this.orderedCommands.push({ + command: message.command, + range: message.range, + }) + } else if (message.command.type === 'modeling_cmd_batch_req') { + message.command.requests.forEach((req) => { + const cmd: EngineCommand = { + type: 'modeling_cmd_req', + cmd_id: req.cmd_id, + cmd: req.cmd, + } + this.orderedCommands.push({ + command: cmd, + range: message.idToRangeMap[req.cmd_id || ''], + }) + }) + } + this.engineConnection?.send(message.command) + return promise + } + + deferredArtifactPopulated = deferExecution((a?: null) => { + this.modelingSend({ type: 'Artifact graph populated' }) + }, 200) + deferredArtifactEmptied = deferExecution((a?: null) => { + this.modelingSend({ type: 'Artifact graph emptied' }) + }, 200) + + /** + * When an execution takes place we want to wait until we've got replies for all of the commands + * When this is done when we build the artifact map synchronously. + */ + async waitForAllCommands() { + await Promise.all(Object.values(this.pendingCommands).map((a) => a.promise)) + this.artifactGraph = createArtifactGraph({ + orderedCommands: this.orderedCommands, + responseMap: this.responseMap, + ast: this.getAst(), + }) + if (this.artifactGraph.size) { + this.deferredArtifactEmptied(null) + } else { + this.deferredArtifactPopulated(null) + } + } + + /** + * Reject all of the modeling pendingCommands created from sendModelingCommandFromWasm + * This interrupts the runtime of executeAst. Stops the AST processing and stops sending commands + * to the engine + */ + rejectAllModelingCommands(rejectionMessage: string) { + Object.values(this.pendingCommands).forEach( + ({ reject, isSceneCommand }) => + !isSceneCommand && reject(rejectionMessage) + ) + } + + async initPlanes() { + if (this.planesInitialized()) return + const planes = await this.makeDefaultPlanes() + this.defaultPlanes = planes + } + planesInitialized(): boolean { + return ( + !!this.defaultPlanes && + this.defaultPlanes.xy !== '' && + this.defaultPlanes.yz !== '' && + this.defaultPlanes.xz !== '' + ) + } + + async setPlaneHidden(id: string, hidden: boolean) { + if (this.engineConnection === undefined) return + + // Can't send commands if there's no connection + if ( + this.engineConnection.state.type === + EngineConnectionStateType.Disconnecting || + this.engineConnection.state.type === + EngineConnectionStateType.Disconnected + ) + return + + return await this.sendSceneCommand({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'object_visible', + object_id: id, + hidden: hidden, + }, + }) + } + + /** + * Set the visibility of the scale grid in the engine scene. + * @param visible - whether to show or hide the scale grid + */ + setScaleGridVisibility(visible: boolean) { + this.modifyGrid(!visible) + } + + // Some "objects" have the same source range, such as sketch_mode_start and start_path. + // So when passing a range, we need to also specify the command type + mapRangeToObjectId( + range: SourceRange, + commandTypeToTarget: string + ): string | undefined { + for (const [artifactId, artifact] of this.artifactGraph) { + if ('codeRef' in artifact && isOverlap(range, artifact.codeRef.range)) { + if (commandTypeToTarget === artifact.type) return artifactId + } + } + return undefined + } +} + +function promiseFactory() { + let resolve: (value: T | PromiseLike) => void = () => {} + let reject: (value: T | PromiseLike) => void = () => {} + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve + reject = _reject + }) + return { promise, resolve, reject } +} diff --git a/src/wasm-lib/pkg/lang/std/fileSystemManager.ts b/src/wasm-lib/pkg/lang/std/fileSystemManager.ts new file mode 100644 index 000000000..0bb71ed1a --- /dev/null +++ b/src/wasm-lib/pkg/lang/std/fileSystemManager.ts @@ -0,0 +1,81 @@ +import { isDesktop } from 'lib/isDesktop' + +/// FileSystemManager is a class that provides a way to read files from the local file system. +/// It assumes that you are in a project since it is solely used by the std lib +/// when executing code. +class FileSystemManager { + private _dir: string | null = null + + get dir() { + return this._dir ?? '' + } + + set dir(dir: string) { + this._dir = dir + } + + async join(dir: string, path: string): Promise { + return Promise.resolve(window.electron.path.join(dir, path)) + } + + async readFile(path: string): Promise { + // Using local file system only works from desktop. + if (!isDesktop()) { + return Promise.reject( + new Error( + 'This function can only be called from the desktop application' + ) + ) + } + + return this.join(this.dir, path).then((filePath) => { + return window.electron.readFile(filePath) + }) + } + + async exists(path: string): Promise { + // Using local file system only works from desktop. + if (!isDesktop()) { + return Promise.reject( + new Error( + 'This function can only be called from the desktop application' + ) + ) + } + + return this.join(this.dir, path).then(async (file) => { + try { + await window.electron.stat(file) + } catch (e) { + if (e === 'ENOENT') { + return false + } + } + return true + }) + } + + async getAllFiles(path: string): Promise { + // Using local file system only works from desktop. + if (!isDesktop()) { + return Promise.reject( + new Error( + 'This function can only be called from the desktop application' + ) + ) + } + + return this.join(this.dir, path).then((filepath) => { + return window.electron + .readdir(filepath) + .catch((error: Error) => { + return Promise.reject(new Error(`Error reading dir: ${error}`)) + }) + .then((files: string[]) => { + return files.map((filePath) => filePath) + }) + }) + } +} + +export const fileSystemManager = new FileSystemManager() diff --git a/src/wasm-lib/pkg/lib/coredump.ts b/src/wasm-lib/pkg/lib/coredump.ts new file mode 100644 index 000000000..1f76b69fc --- /dev/null +++ b/src/wasm-lib/pkg/lib/coredump.ts @@ -0,0 +1,425 @@ +import { CommandLog, EngineCommandManager } from 'lang/std/engineConnection' +import { WebrtcStats } from 'wasm-lib/kcl/bindings/WebrtcStats' +import { OsInfo } from 'wasm-lib/kcl/bindings/OsInfo' +import { isDesktop } from 'lib/isDesktop' +import { APP_VERSION } from 'routes/Settings' +import { UAParser } from 'ua-parser-js' +import screenshot from 'lib/screenshot' +import { VITE_KC_API_BASE_URL } from 'env' +import CodeManager from 'lang/codeManager' + +/* eslint-disable suggest-no-throw/suggest-no-throw -- + * All the throws in CoreDumpManager are intentional and should be caught and handled properly + * by the calling Promises with a catch block. The throws are essential to properly handling + * when the app isn't ready enough or otherwise unable to produce a core dump. By throwing + * instead of simply erroring, the code halts execution at the first point which it cannot + * complete the core dump request. + **/ + +/** + * CoreDumpManager module + * - for getting all the values from the JS world to pass to the Rust world for a core dump. + * @module lib/coredump + * @class + */ +// CoreDumpManager is instantiated in ModelingMachineProvider and passed to coreDump() in wasm.ts +// The async function coreDump() handles any errors thrown in its Promise catch method and rethrows +// them to so the toast handler in ModelingMachineProvider can show the user an error message toast +// TODO: Throw more +export class CoreDumpManager { + engineCommandManager: EngineCommandManager + codeManager: CodeManager + token: string | undefined + baseUrl: string = VITE_KC_API_BASE_URL + + constructor( + engineCommandManager: EngineCommandManager, + codeManager: CodeManager, + token: string | undefined + ) { + this.engineCommandManager = engineCommandManager + this.codeManager = codeManager + this.token = token + } + + // Get the token. + authToken(): string { + if (!this.token) { + throw new Error('Token not set') + } + return this.token + } + + // Get the base url. + baseApiUrl(): string { + return this.baseUrl + } + + // Get the version of the app from the package.json. + version(): string { + return APP_VERSION + } + + kclCode(): string { + return this.codeManager.code + } + + // Get the backend pool we've requested. + pool(): string { + return this.engineCommandManager.settings.pool || '' + } + + // Get the os information. + getOsInfo(): string { + if (this.isDesktop()) { + const osinfo: OsInfo = { + platform: window.electron.platform ?? null, + arch: window.electron.arch ?? null, + browser: 'desktop', + version: window.electron.version ?? null, + } + return JSON.stringify(osinfo) + } + + const userAgent = window.navigator.userAgent || 'unknown browser' + if (userAgent === 'unknown browser') { + const osinfo: OsInfo = { + platform: userAgent, + arch: userAgent, + version: userAgent, + browser: userAgent, + } + return JSON.stringify(osinfo) + } + + const parser = new UAParser(userAgent) + const parserResults = parser.getResult() + const osinfo: OsInfo = { + platform: parserResults.os.name || userAgent, + arch: parserResults.cpu.architecture || userAgent, + version: parserResults.os.version || userAgent, + browser: userAgent, + } + return JSON.stringify(osinfo) + } + + isDesktop(): boolean { + return isDesktop() + } + + getWebrtcStats(): Promise { + if (!this.engineCommandManager.engineConnection) { + // when the engine connection is not available, return an empty object. + return Promise.resolve(JSON.stringify({})) + } + + if (!this.engineCommandManager.engineConnection.webrtcStatsCollector) { + // when the engine connection is not available, return an empty object. + return Promise.resolve(JSON.stringify({})) + } + + return this.engineCommandManager.engineConnection + .webrtcStatsCollector() + .catch((error: any) => { + throw new Error(`Error getting webrtc stats: ${error}`) + }) + .then((stats: any) => { + const webrtcStats: WebrtcStats = { + packets_lost: stats.rtc_packets_lost, + frames_received: stats.rtc_frames_received, + frame_width: stats.rtc_frame_width, + frame_height: stats.rtc_frame_height, + frame_rate: stats.rtc_frames_per_second, + key_frames_decoded: stats.rtc_keyframes_decoded, + frames_dropped: stats.rtc_frames_dropped, + pause_count: stats.rtc_pause_count, + total_pauses_duration: stats.rtc_total_pauses_duration_sec, + freeze_count: stats.rtc_freeze_count, + total_freezes_duration: stats.rtc_total_freezes_duration_sec, + pli_count: stats.rtc_pli_count, + jitter: stats.rtc_jitter_sec, + } + return JSON.stringify(webrtcStats) + }) + } + + // Currently just a placeholder to begin loading singleton and xstate data into + getClientState(): Promise { + /** + * Check if a function is private method + */ + const isPrivateMethod = (key: string) => { + return key.length && key[0] === '_' + } + + // Turn off verbose logging by default + const verboseLogging = false + + /** + * Toggle verbose debug logging of step-by-step client state coredump data + */ + const debugLog = verboseLogging ? console.log : () => {} + + console.warn('CoreDump: Gathering client state') + + // Initialize the clientState object + let clientState = { + // singletons + engine_command_manager: { + artifact_map: {}, + command_logs: [] as CommandLog[], + engine_connection: { state: { type: '' } }, + default_planes: {}, + scene_command_artifacts: {}, + }, + kcl_manager: { + ast: {}, + kcl_errors: [], + }, + scene_infra: {}, + scene_entities_manager: {}, + editor_manager: {}, + // xstate + auth_machine: {}, + command_bar_machine: {}, + file_machine: {}, + home_machine: {}, + modeling_machine: {}, + settings_machine: {}, + } + debugLog('CoreDump: initialized clientState', clientState) + debugLog('CoreDump: globalThis.window', globalThis.window) + + try { + // Singletons + + // engine_command_manager + debugLog('CoreDump: engineCommandManager', this.engineCommandManager) + + // artifact map - this.engineCommandManager.artifactGraph + if (this.engineCommandManager?.artifactGraph) { + debugLog( + 'CoreDump: Engine Command Manager artifact map', + this.engineCommandManager.artifactGraph + ) + clientState.engine_command_manager.artifact_map = structuredClone( + this.engineCommandManager.artifactGraph + ) + } + + // command logs - this.engineCommandManager.commandLogs + if (this.engineCommandManager?.commandLogs) { + debugLog( + 'CoreDump: Engine Command Manager command logs', + this.engineCommandManager.commandLogs + ) + clientState.engine_command_manager.command_logs = structuredClone( + this.engineCommandManager.commandLogs + ) + } + + // default planes - this.engineCommandManager.defaultPlanes + if (this.engineCommandManager?.defaultPlanes) { + debugLog( + 'CoreDump: Engine Command Manager default planes', + this.engineCommandManager.defaultPlanes + ) + clientState.engine_command_manager.default_planes = structuredClone( + this.engineCommandManager.defaultPlanes + ) + } + + // engine connection state + if (this.engineCommandManager?.engineConnection?.state) { + debugLog( + 'CoreDump: Engine Command Manager engine connection state', + this.engineCommandManager.engineConnection.state + ) + clientState.engine_command_manager.engine_connection.state = + this.engineCommandManager.engineConnection.state + } + + // in sequence - this.engineCommandManager.inSequence + if (this.engineCommandManager?.inSequence) { + debugLog( + 'CoreDump: Engine Command Manager in sequence', + this.engineCommandManager.inSequence + ) + ;(clientState.engine_command_manager as any).in_sequence = + this.engineCommandManager.inSequence + } + + // out sequence - this.engineCommandManager.outSequence + if (this.engineCommandManager?.outSequence) { + debugLog( + 'CoreDump: Engine Command Manager out sequence', + this.engineCommandManager.outSequence + ) + ;(clientState.engine_command_manager as any).out_sequence = + this.engineCommandManager.outSequence + } + + // KCL Manager - globalThis?.window?.kclManager + const kclManager = (globalThis?.window as any)?.kclManager + debugLog('CoreDump: kclManager', kclManager) + + if (kclManager) { + // KCL Manager AST + debugLog('CoreDump: KCL Manager AST', kclManager?.ast) + if (kclManager?.ast) { + clientState.kcl_manager.ast = structuredClone(kclManager.ast) + } + + // KCL Errors + debugLog('CoreDump: KCL Errors', kclManager?.kclErrors) + if (kclManager?.kclErrors) { + clientState.kcl_manager.kcl_errors = structuredClone( + kclManager.kclErrors + ) + } + + // KCL isExecuting + debugLog('CoreDump: KCL isExecuting', kclManager?.isExecuting) + if (kclManager?.isExecuting) { + ;(clientState.kcl_manager as any).isExecuting = kclManager.isExecuting + } + + // KCL logs + debugLog('CoreDump: KCL logs', kclManager?.logs) + if (kclManager?.logs) { + ;(clientState.kcl_manager as any).logs = structuredClone( + kclManager.logs + ) + } + + // KCL programMemory + debugLog('CoreDump: KCL programMemory', kclManager?.programMemory) + if (kclManager?.programMemory) { + ;(clientState.kcl_manager as any).programMemory = structuredClone( + kclManager.programMemory + ) + } + + // KCL wasmInitFailed + debugLog('CoreDump: KCL wasmInitFailed', kclManager?.wasmInitFailed) + if (kclManager?.wasmInitFailed) { + ;(clientState.kcl_manager as any).wasmInitFailed = + kclManager.wasmInitFailed + } + } + + // Scene Infra - globalThis?.window?.sceneInfra + const sceneInfra = (globalThis?.window as any)?.sceneInfra + debugLog('CoreDump: Scene Infra', sceneInfra) + + if (sceneInfra) { + const sceneInfraSkipKeys = ['camControls'] + const sceneInfraKeys = Object.keys(sceneInfra) + .sort() + .filter((entry) => { + return ( + typeof sceneInfra[entry] !== 'function' && + !sceneInfraSkipKeys.includes(entry) + ) + }) + + debugLog('CoreDump: Scene Infra keys', sceneInfraKeys) + sceneInfraKeys.forEach((key: string) => { + debugLog('CoreDump: Scene Infra', key, sceneInfra[key]) + try { + ;(clientState.scene_infra as any)[key] = sceneInfra[key] + } catch (error) { + console.error( + 'CoreDump: unable to parse Scene Infra ' + key + ' data due to ', + error + ) + } + }) + } + + // Scene Entities Manager - globalThis?.window?.sceneEntitiesManager + const sceneEntitiesManager = (globalThis?.window as any) + ?.sceneEntitiesManager + debugLog('CoreDump: sceneEntitiesManager', sceneEntitiesManager) + + if (sceneEntitiesManager) { + // Scene Entities Manager active segments + debugLog( + 'CoreDump: Scene Entities Manager active segments', + sceneEntitiesManager?.activeSegments + ) + if (sceneEntitiesManager?.activeSegments) { + ;(clientState.scene_entities_manager as any).activeSegments = + structuredClone(sceneEntitiesManager.activeSegments) + } + } + + // Editor Manager - globalThis?.window?.editorManager + const editorManager = (globalThis?.window as any)?.editorManager + debugLog('CoreDump: editorManager', editorManager) + + if (editorManager) { + const editorManagerSkipKeys = ['camControls'] + const editorManagerKeys = Object.keys(editorManager) + .sort() + .filter((entry) => { + return ( + typeof editorManager[entry] !== 'function' && + !isPrivateMethod(entry) && + !editorManagerSkipKeys.includes(entry) + ) + }) + + debugLog('CoreDump: Editor Manager keys', editorManagerKeys) + editorManagerKeys.forEach((key: string) => { + debugLog('CoreDump: Editor Manager', key, editorManager[key]) + try { + ;(clientState.editor_manager as any)[key] = structuredClone( + editorManager[key] + ) + } catch (error) { + console.error( + 'CoreDump: unable to parse Editor Manager ' + + key + + ' data due to ', + error + ) + } + }) + } + + // enableMousePositionLogs - Not coredumped + // See https://github.com/KittyCAD/modeling-app/issues/2338#issuecomment-2136441998 + debugLog( + 'CoreDump: enableMousePositionLogs [not coredumped]', + (globalThis?.window as any)?.enableMousePositionLogs + ) + + // XState Machines + debugLog( + 'CoreDump: xstate services', + (globalThis?.window as any)?.__xstate__?.services + ) + + debugLog('CoreDump: final clientState', clientState) + + const clientStateJson = JSON.stringify(clientState) + debugLog('CoreDump: final clientState JSON', clientStateJson) + + return Promise.resolve(clientStateJson) + } catch (error) { + console.error('CoreDump: unable to return data due to ', error) + return Promise.reject(JSON.stringify(error)) + } + } + + // Return a data URL (png format) of the screenshot of the current page. + screenshot(): Promise { + return ( + screenshot() + .then((screenshotStr: string) => screenshotStr) + // maybe rust should handle an error, but an empty string at least doesn't cause the core dump to fail entirely + .catch((error: any) => ``) + ) + } +} diff --git a/src/wasm-lib/pkg/package.json b/src/wasm-lib/pkg/package.json new file mode 100644 index 000000000..95ea6b1d9 --- /dev/null +++ b/src/wasm-lib/pkg/package.json @@ -0,0 +1,19 @@ +{ + "name": "wasm-lib", + "type": "module", + "version": "0.1.0", + "repository": { + "type": "git", + "url": "https://github.com/KittyCAD/modeling-app" + }, + "files": [ + "wasm_lib_bg.wasm", + "wasm_lib.js", + "wasm_lib.d.ts" + ], + "main": "wasm_lib.js", + "types": "wasm_lib.d.ts", + "sideEffects": [ + "./snippets/*" + ] +} \ No newline at end of file diff --git a/src/wasm-lib/pkg/wasm_lib.d.ts b/src/wasm-lib/pkg/wasm_lib.d.ts new file mode 100644 index 000000000..79a9136df --- /dev/null +++ b/src/wasm-lib/pkg/wasm_lib.d.ts @@ -0,0 +1,339 @@ +/* tslint:disable */ +/* eslint-disable */ +/** +* @param {string} json +* @returns {string} +*/ +export function toml_stringify(json: string): string; +/** +* @param {string} program_str +* @param {string} memory_str +* @param {string} units +* @param {any} engine_manager +* @param {any} fs_manager +* @param {boolean} is_mock +* @returns {Promise} +*/ +export function execute_wasm(program_str: string, memory_str: string, units: string, engine_manager: any, fs_manager: any, is_mock: boolean): Promise; +/** +* @param {string} program_str +* @returns {Promise} +*/ +export function kcl_lint(program_str: string): Promise; +/** +* @param {any} engine_manager +* @returns {Promise} +*/ +export function make_default_planes(engine_manager: any): Promise; +/** +* @param {any} engine_manager +* @param {boolean} hidden +* @returns {Promise} +*/ +export function modify_grid(engine_manager: any, hidden: boolean): Promise; +/** +* @param {any} manager +* @param {string} program_str +* @param {string} sketch_name +* @param {string} plane_type +* @param {string} sketch_id +* @returns {Promise} +*/ +export function modify_ast_for_sketch_wasm(manager: any, program_str: string, sketch_name: string, plane_type: string, sketch_id: string): Promise; +/** +* @param {Uint8Array} data +* @returns {any} +*/ +export function deserialize_files(data: Uint8Array): any; +/** +* @param {string} js +* @returns {any} +*/ +export function lexer_wasm(js: string): any; +/** +* @param {string} js +* @returns {any} +*/ +export function parse_wasm(js: string): any; +/** +* @param {string} json_str +* @returns {any} +*/ +export function recast_wasm(json_str: string): any; +/** +* Run the `kcl` lsp server. +* @param {ServerConfig} config +* @param {any | undefined} engine_manager +* @param {string} units +* @param {string} token +* @param {string} baseurl +* @returns {Promise} +*/ +export function kcl_lsp_run(config: ServerConfig, engine_manager: any | undefined, units: string, token: string, baseurl: string): Promise; +/** +* Run the `copilot` lsp server. +* @param {ServerConfig} config +* @param {string} token +* @param {string} baseurl +* @returns {Promise} +*/ +export function copilot_lsp_run(config: ServerConfig, token: string, baseurl: string): Promise; +/** +* @param {Float64Array} points +* @returns {number} +*/ +export function is_points_ccw(points: Float64Array): number; +/** +* @param {number} arc_start_point_x +* @param {number} arc_start_point_y +* @param {number} arc_end_point_x +* @param {number} arc_end_point_y +* @param {number} tan_previous_point_x +* @param {number} tan_previous_point_y +* @param {boolean} obtuse +* @returns {TangentialArcInfoOutputWasm} +*/ +export function get_tangential_arc_to_info(arc_start_point_x: number, arc_start_point_y: number, arc_end_point_x: number, arc_end_point_y: number, tan_previous_point_x: number, tan_previous_point_y: number, obtuse: boolean): TangentialArcInfoOutputWasm; +/** +* Create the default program memory. +* @returns {any} +*/ +export function program_memory_init(): any; +/** +* Get a coredump. +* @param {any} core_dump_manager +* @returns {Promise} +*/ +export function coredump(core_dump_manager: any): Promise; +/** +* Get the default app settings. +* @returns {any} +*/ +export function default_app_settings(): any; +/** +* Parse the app settings. +* @param {string} toml_str +* @returns {any} +*/ +export function parse_app_settings(toml_str: string): any; +/** +* Get the default project settings. +* @returns {any} +*/ +export function default_project_settings(): any; +/** +* Parse (deserialize) the project settings. +* @param {string} toml_str +* @returns {any} +*/ +export function parse_project_settings(toml_str: string): any; +/** +* Serialize the project settings. +* @param {any} val +* @returns {any} +*/ +export function serialize_project_settings(val: any): any; +/** +* Base64 decode a string. +* @param {string} input +* @returns {Uint8Array} +*/ +export function base64_decode(input: string): Uint8Array; +/** +*/ +export class IntoUnderlyingByteSource { + free(): void; +/** +* @param {ReadableByteStreamController} controller +*/ + start(controller: ReadableByteStreamController): void; +/** +* @param {ReadableByteStreamController} controller +* @returns {Promise} +*/ + pull(controller: ReadableByteStreamController): Promise; +/** +*/ + cancel(): void; +/** +*/ + readonly autoAllocateChunkSize: number; +/** +*/ + readonly type: string; +} +/** +*/ +export class IntoUnderlyingSink { + free(): void; +/** +* @param {any} chunk +* @returns {Promise} +*/ + write(chunk: any): Promise; +/** +* @returns {Promise} +*/ + close(): Promise; +/** +* @param {any} reason +* @returns {Promise} +*/ + abort(reason: any): Promise; +} +/** +*/ +export class IntoUnderlyingSource { + free(): void; +/** +* @param {ReadableStreamDefaultController} controller +* @returns {Promise} +*/ + pull(controller: ReadableStreamDefaultController): Promise; +/** +*/ + cancel(): void; +} +/** +*/ +export class ServerConfig { + free(): void; +/** +* @param {AsyncIterator} into_server +* @param {WritableStream} from_server +* @param {any} fs +*/ + constructor(into_server: AsyncIterator, from_server: WritableStream, fs: any); +} +/** +*/ +export class TangentialArcInfoOutputWasm { + free(): void; +/** +* The length of the arc. +*/ + arc_length: number; +/** +* The midpoint of the arc x. +*/ + arc_mid_point_x: number; +/** +* The midpoint of the arc y. +*/ + arc_mid_point_y: number; +/** +* Flag to determine if the arc is counter clockwise. +*/ + ccw: number; +/** +* The geometric center of the arc x. +*/ + center_x: number; +/** +* The geometric center of the arc y. +*/ + center_y: number; +/** +* End angle of the arc in radians. +*/ + end_angle: number; +/** +* The radius of the arc. +*/ + radius: number; +/** +* Start angle of the arc in radians. +*/ + start_angle: number; +} + +export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; + +export interface InitOutput { + readonly memory: WebAssembly.Memory; + readonly toml_stringify: (a: number, b: number, c: number) => void; + readonly execute_wasm: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => number; + readonly kcl_lint: (a: number, b: number) => number; + readonly make_default_planes: (a: number) => number; + readonly modify_grid: (a: number, b: number) => number; + readonly modify_ast_for_sketch_wasm: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => number; + readonly deserialize_files: (a: number, b: number, c: number) => void; + readonly lexer_wasm: (a: number, b: number, c: number) => void; + readonly parse_wasm: (a: number, b: number, c: number) => void; + readonly recast_wasm: (a: number, b: number, c: number) => void; + readonly __wbg_serverconfig_free: (a: number) => void; + readonly serverconfig_new: (a: number, b: number, c: number) => number; + readonly kcl_lsp_run: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => number; + readonly copilot_lsp_run: (a: number, b: number, c: number, d: number, e: number) => number; + readonly is_points_ccw: (a: number, b: number) => number; + readonly __wbg_tangentialarcinfooutputwasm_free: (a: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_center_x: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_center_x: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_center_y: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_center_y: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_arc_mid_point_x: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_arc_mid_point_x: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_arc_mid_point_y: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_arc_mid_point_y: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_radius: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_radius: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_start_angle: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_start_angle: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_end_angle: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_end_angle: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_ccw: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_ccw: (a: number, b: number) => void; + readonly __wbg_get_tangentialarcinfooutputwasm_arc_length: (a: number) => number; + readonly __wbg_set_tangentialarcinfooutputwasm_arc_length: (a: number, b: number) => void; + readonly get_tangential_arc_to_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number; + readonly program_memory_init: (a: number) => void; + readonly coredump: (a: number) => number; + readonly default_app_settings: (a: number) => void; + readonly parse_app_settings: (a: number, b: number, c: number) => void; + readonly default_project_settings: (a: number) => void; + readonly parse_project_settings: (a: number, b: number, c: number) => void; + readonly serialize_project_settings: (a: number, b: number) => void; + readonly base64_decode: (a: number, b: number, c: number) => void; + readonly __wbg_intounderlyingsource_free: (a: number) => void; + readonly intounderlyingsource_pull: (a: number, b: number) => number; + readonly intounderlyingsource_cancel: (a: number) => void; + readonly __wbg_intounderlyingsink_free: (a: number) => void; + readonly intounderlyingsink_write: (a: number, b: number) => number; + readonly intounderlyingsink_close: (a: number) => number; + readonly intounderlyingsink_abort: (a: number, b: number) => number; + readonly __wbg_intounderlyingbytesource_free: (a: number) => void; + readonly intounderlyingbytesource_type: (a: number, b: number) => void; + readonly intounderlyingbytesource_autoAllocateChunkSize: (a: number) => number; + readonly intounderlyingbytesource_start: (a: number, b: number) => void; + readonly intounderlyingbytesource_pull: (a: number, b: number) => number; + readonly intounderlyingbytesource_cancel: (a: number) => void; + readonly __wbindgen_malloc: (a: number, b: number) => number; + readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; + readonly __wbindgen_export_2: WebAssembly.Table; + readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h441a9e3e174bc8f1: (a: number, b: number, c: number) => void; + readonly __wbindgen_add_to_stack_pointer: (a: number) => number; + readonly __wbindgen_free: (a: number, b: number, c: number) => void; + readonly __wbindgen_exn_store: (a: number) => void; + readonly wasm_bindgen__convert__closures__invoke2_mut__h38ba35493531eafd: (a: number, b: number, c: number, d: number) => void; +} + +export type SyncInitInput = BufferSource | WebAssembly.Module; +/** +* Instantiates the given `module`, which can either be bytes or +* a precompiled `WebAssembly.Module`. +* +* @param {SyncInitInput} module +* +* @returns {InitOutput} +*/ +export function initSync(module: SyncInitInput): InitOutput; + +/** +* If `module_or_path` is {RequestInfo} or {URL}, makes a request and +* for everything else, calls `WebAssembly.instantiate` directly. +* +* @param {InitInput | Promise} module_or_path +* +* @returns {Promise} +*/ +export default function __wbg_init (module_or_path?: InitInput | Promise): Promise; diff --git a/src/wasm-lib/pkg/wasm_lib.js b/src/wasm-lib/pkg/wasm_lib.js new file mode 100644 index 000000000..013300b37 --- /dev/null +++ b/src/wasm-lib/pkg/wasm_lib.js @@ -0,0 +1,1667 @@ +let wasm; + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { return heap[idx]; } + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +let WASM_VECTOR_LEN = 0; + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} + +const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(state => { + wasm.__wbindgen_export_2.get(state.dtor)(state.a, state.b) +}); + +function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + if (--state.cnt === 0) { + wasm.__wbindgen_export_2.get(state.dtor)(a, state.b); + CLOSURE_DTORS.unregister(state); + } else { + state.a = a; + } + } + }; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; +} +function __wbg_adapter_30(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h441a9e3e174bc8f1(arg0, arg1, addHeapObject(arg2)); +} + +/** +* @param {string} json +* @returns {string} +*/ +export function toml_stringify(json) { + let deferred3_0; + let deferred3_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.toml_stringify(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + var r3 = getInt32Memory0()[retptr / 4 + 3]; + var ptr2 = r0; + var len2 = r1; + if (r3) { + ptr2 = 0; len2 = 0; + throw takeObject(r2); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** +* @param {string} program_str +* @param {string} memory_str +* @param {string} units +* @param {any} engine_manager +* @param {any} fs_manager +* @param {boolean} is_mock +* @returns {Promise} +*/ +export function execute_wasm(program_str, memory_str, units, engine_manager, fs_manager, is_mock) { + const ptr0 = passStringToWasm0(program_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(memory_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(units, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.execute_wasm(ptr0, len0, ptr1, len1, ptr2, len2, addHeapObject(engine_manager), addHeapObject(fs_manager), is_mock); + return takeObject(ret); +} + +/** +* @param {string} program_str +* @returns {Promise} +*/ +export function kcl_lint(program_str) { + const ptr0 = passStringToWasm0(program_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kcl_lint(ptr0, len0); + return takeObject(ret); +} + +/** +* @param {any} engine_manager +* @returns {Promise} +*/ +export function make_default_planes(engine_manager) { + const ret = wasm.make_default_planes(addHeapObject(engine_manager)); + return takeObject(ret); +} + +/** +* @param {any} engine_manager +* @param {boolean} hidden +* @returns {Promise} +*/ +export function modify_grid(engine_manager, hidden) { + const ret = wasm.modify_grid(addHeapObject(engine_manager), hidden); + return takeObject(ret); +} + +/** +* @param {any} manager +* @param {string} program_str +* @param {string} sketch_name +* @param {string} plane_type +* @param {string} sketch_id +* @returns {Promise} +*/ +export function modify_ast_for_sketch_wasm(manager, program_str, sketch_name, plane_type, sketch_id) { + const ptr0 = passStringToWasm0(program_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(sketch_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(plane_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passStringToWasm0(sketch_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len3 = WASM_VECTOR_LEN; + const ret = wasm.modify_ast_for_sketch_wasm(addHeapObject(manager), ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3); + return takeObject(ret); +} + +function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1, 1) >>> 0; + getUint8Memory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; +} +/** +* @param {Uint8Array} data +* @returns {any} +*/ +export function deserialize_files(data) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.deserialize_files(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* @param {string} js +* @returns {any} +*/ +export function lexer_wasm(js) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(js, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.lexer_wasm(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* @param {string} js +* @returns {any} +*/ +export function parse_wasm(js) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(js, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.parse_wasm(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* @param {string} json_str +* @returns {any} +*/ +export function recast_wasm(json_str) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(json_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.recast_wasm(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +function _assertClass(instance, klass) { + if (!(instance instanceof klass)) { + throw new Error(`expected instance of ${klass.name}`); + } + return instance.ptr; +} +/** +* Run the `kcl` lsp server. +* @param {ServerConfig} config +* @param {any | undefined} engine_manager +* @param {string} units +* @param {string} token +* @param {string} baseurl +* @returns {Promise} +*/ +export function kcl_lsp_run(config, engine_manager, units, token, baseurl) { + _assertClass(config, ServerConfig); + var ptr0 = config.__destroy_into_raw(); + const ptr1 = passStringToWasm0(units, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passStringToWasm0(baseurl, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len3 = WASM_VECTOR_LEN; + const ret = wasm.kcl_lsp_run(ptr0, isLikeNone(engine_manager) ? 0 : addHeapObject(engine_manager), ptr1, len1, ptr2, len2, ptr3, len3); + return takeObject(ret); +} + +/** +* Run the `copilot` lsp server. +* @param {ServerConfig} config +* @param {string} token +* @param {string} baseurl +* @returns {Promise} +*/ +export function copilot_lsp_run(config, token, baseurl) { + _assertClass(config, ServerConfig); + var ptr0 = config.__destroy_into_raw(); + const ptr1 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(baseurl, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.copilot_lsp_run(ptr0, ptr1, len1, ptr2, len2); + return takeObject(ret); +} + +let cachedFloat64Memory0 = null; + +function getFloat64Memory0() { + if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { + cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); + } + return cachedFloat64Memory0; +} + +function passArrayF64ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 8, 8) >>> 0; + getFloat64Memory0().set(arg, ptr / 8); + WASM_VECTOR_LEN = arg.length; + return ptr; +} +/** +* @param {Float64Array} points +* @returns {number} +*/ +export function is_points_ccw(points) { + const ptr0 = passArrayF64ToWasm0(points, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.is_points_ccw(ptr0, len0); + return ret; +} + +/** +* @param {number} arc_start_point_x +* @param {number} arc_start_point_y +* @param {number} arc_end_point_x +* @param {number} arc_end_point_y +* @param {number} tan_previous_point_x +* @param {number} tan_previous_point_y +* @param {boolean} obtuse +* @returns {TangentialArcInfoOutputWasm} +*/ +export function get_tangential_arc_to_info(arc_start_point_x, arc_start_point_y, arc_end_point_x, arc_end_point_y, tan_previous_point_x, tan_previous_point_y, obtuse) { + const ret = wasm.get_tangential_arc_to_info(arc_start_point_x, arc_start_point_y, arc_end_point_x, arc_end_point_y, tan_previous_point_x, tan_previous_point_y, obtuse); + return TangentialArcInfoOutputWasm.__wrap(ret); +} + +/** +* Create the default program memory. +* @returns {any} +*/ +export function program_memory_init() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.program_memory_init(retptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* Get a coredump. +* @param {any} core_dump_manager +* @returns {Promise} +*/ +export function coredump(core_dump_manager) { + const ret = wasm.coredump(addHeapObject(core_dump_manager)); + return takeObject(ret); +} + +/** +* Get the default app settings. +* @returns {any} +*/ +export function default_app_settings() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.default_app_settings(retptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* Parse the app settings. +* @param {string} toml_str +* @returns {any} +*/ +export function parse_app_settings(toml_str) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(toml_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.parse_app_settings(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* Get the default project settings. +* @returns {any} +*/ +export function default_project_settings() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.default_project_settings(retptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* Parse (deserialize) the project settings. +* @param {string} toml_str +* @returns {any} +*/ +export function parse_project_settings(toml_str) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(toml_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.parse_project_settings(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* Serialize the project settings. +* @param {any} val +* @returns {any} +*/ +export function serialize_project_settings(val) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.serialize_project_settings(retptr, addHeapObject(val)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); +} +/** +* Base64 decode a string. +* @param {string} input +* @returns {Uint8Array} +*/ +export function base64_decode(input) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.base64_decode(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + var r3 = getInt32Memory0()[retptr / 4 + 3]; + if (r3) { + throw takeObject(r2); + } + var v2 = getArrayU8FromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 1, 1); + return v2; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} +function __wbg_adapter_234(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen__convert__closures__invoke2_mut__h38ba35493531eafd(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); +} + +const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingbytesource_free(ptr >>> 0)); +/** +*/ +export class IntoUnderlyingByteSource { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingByteSourceFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingbytesource_free(ptr); + } + /** + * @returns {string} + */ + get type() { + let deferred1_0; + let deferred1_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.intounderlyingbytesource_type(retptr, this.__wbg_ptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred1_0 = r0; + deferred1_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } + } + /** + * @returns {number} + */ + get autoAllocateChunkSize() { + const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {ReadableByteStreamController} controller + */ + start(controller) { + wasm.intounderlyingbytesource_start(this.__wbg_ptr, addHeapObject(controller)); + } + /** + * @param {ReadableByteStreamController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingbytesource_pull(this.__wbg_ptr, addHeapObject(controller)); + return takeObject(ret); + } + /** + */ + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingbytesource_cancel(ptr); + } +} + +const IntoUnderlyingSinkFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsink_free(ptr >>> 0)); +/** +*/ +export class IntoUnderlyingSink { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSinkFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsink_free(ptr); + } + /** + * @param {any} chunk + * @returns {Promise} + */ + write(chunk) { + const ret = wasm.intounderlyingsink_write(this.__wbg_ptr, addHeapObject(chunk)); + return takeObject(ret); + } + /** + * @returns {Promise} + */ + close() { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_close(ptr); + return takeObject(ret); + } + /** + * @param {any} reason + * @returns {Promise} + */ + abort(reason) { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_abort(ptr, addHeapObject(reason)); + return takeObject(ret); + } +} + +const IntoUnderlyingSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsource_free(ptr >>> 0)); +/** +*/ +export class IntoUnderlyingSource { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSourceFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsource_free(ptr); + } + /** + * @param {ReadableStreamDefaultController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingsource_pull(this.__wbg_ptr, addHeapObject(controller)); + return takeObject(ret); + } + /** + */ + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingsource_cancel(ptr); + } +} + +const ServerConfigFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_serverconfig_free(ptr >>> 0)); +/** +*/ +export class ServerConfig { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ServerConfigFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_serverconfig_free(ptr); + } + /** + * @param {AsyncIterator} into_server + * @param {WritableStream} from_server + * @param {any} fs + */ + constructor(into_server, from_server, fs) { + const ret = wasm.serverconfig_new(addHeapObject(into_server), addHeapObject(from_server), addHeapObject(fs)); + this.__wbg_ptr = ret >>> 0; + return this; + } +} + +const TangentialArcInfoOutputWasmFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_tangentialarcinfooutputwasm_free(ptr >>> 0)); +/** +*/ +export class TangentialArcInfoOutputWasm { + + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(TangentialArcInfoOutputWasm.prototype); + obj.__wbg_ptr = ptr; + TangentialArcInfoOutputWasmFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + TangentialArcInfoOutputWasmFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_tangentialarcinfooutputwasm_free(ptr); + } + /** + * The geometric center of the arc x. + * @returns {number} + */ + get center_x() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_center_x(this.__wbg_ptr); + return ret; + } + /** + * The geometric center of the arc x. + * @param {number} arg0 + */ + set center_x(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_center_x(this.__wbg_ptr, arg0); + } + /** + * The geometric center of the arc y. + * @returns {number} + */ + get center_y() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_center_y(this.__wbg_ptr); + return ret; + } + /** + * The geometric center of the arc y. + * @param {number} arg0 + */ + set center_y(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_center_y(this.__wbg_ptr, arg0); + } + /** + * The midpoint of the arc x. + * @returns {number} + */ + get arc_mid_point_x() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_arc_mid_point_x(this.__wbg_ptr); + return ret; + } + /** + * The midpoint of the arc x. + * @param {number} arg0 + */ + set arc_mid_point_x(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_arc_mid_point_x(this.__wbg_ptr, arg0); + } + /** + * The midpoint of the arc y. + * @returns {number} + */ + get arc_mid_point_y() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_arc_mid_point_y(this.__wbg_ptr); + return ret; + } + /** + * The midpoint of the arc y. + * @param {number} arg0 + */ + set arc_mid_point_y(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_arc_mid_point_y(this.__wbg_ptr, arg0); + } + /** + * The radius of the arc. + * @returns {number} + */ + get radius() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_radius(this.__wbg_ptr); + return ret; + } + /** + * The radius of the arc. + * @param {number} arg0 + */ + set radius(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_radius(this.__wbg_ptr, arg0); + } + /** + * Start angle of the arc in radians. + * @returns {number} + */ + get start_angle() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_start_angle(this.__wbg_ptr); + return ret; + } + /** + * Start angle of the arc in radians. + * @param {number} arg0 + */ + set start_angle(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_start_angle(this.__wbg_ptr, arg0); + } + /** + * End angle of the arc in radians. + * @returns {number} + */ + get end_angle() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_end_angle(this.__wbg_ptr); + return ret; + } + /** + * End angle of the arc in radians. + * @param {number} arg0 + */ + set end_angle(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_end_angle(this.__wbg_ptr, arg0); + } + /** + * Flag to determine if the arc is counter clockwise. + * @returns {number} + */ + get ccw() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_ccw(this.__wbg_ptr); + return ret; + } + /** + * Flag to determine if the arc is counter clockwise. + * @param {number} arg0 + */ + set ccw(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_ccw(this.__wbg_ptr, arg0); + } + /** + * The length of the arc. + * @returns {number} + */ + get arc_length() { + const ret = wasm.__wbg_get_tangentialarcinfooutputwasm_arc_length(this.__wbg_ptr); + return ret; + } + /** + * The length of the arc. + * @param {number} arg0 + */ + set arc_length(arg0) { + wasm.__wbg_set_tangentialarcinfooutputwasm_arc_length(this.__wbg_ptr, arg0); + } +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); + }; + imports.wbg.__wbindgen_string_get = function(arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof(obj) === 'string' ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_error_new = function(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_cb_drop = function(arg0) { + const obj = takeObject(arg0).original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; + } + const ret = false; + return ret; + }; + imports.wbg.__wbindgen_is_undefined = function(arg0) { + const ret = getObject(arg0) === undefined; + return ret; + }; + imports.wbg.__wbg_new_abda76e883ba8a5f = function() { + const ret = new Error(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { + const ret = getObject(arg1).stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }; + imports.wbg.__wbg_authToken_6662366493f84bbd = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg1).authToken(); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, arguments) }; + imports.wbg.__wbg_baseApiUrl_cd7531b650e04071 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg1).baseApiUrl(); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, arguments) }; + imports.wbg.__wbg_version_76dcc6f7584894a1 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg1).version(); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, arguments) }; + imports.wbg.__wbg_kclCode_3dd7c244069cea05 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg1).kclCode(); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, arguments) }; + imports.wbg.__wbg_pool_8f5e41c1e16363b6 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg1).pool(); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, arguments) }; + imports.wbg.__wbg_getOsInfo_40b40129acd0c675 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg1).getOsInfo(); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, arguments) }; + imports.wbg.__wbg_isDesktop_4539976cac1527ce = function() { return handleError(function (arg0) { + const ret = getObject(arg0).isDesktop(); + return ret; + }, arguments) }; + imports.wbg.__wbg_getWebrtcStats_bf5d4567a1577964 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).getWebrtcStats(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_getClientState_f537c13788cd29b1 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).getClientState(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_screenshot_8b354a177a3619cb = function() { return handleError(function (arg0) { + const ret = getObject(arg0).screenshot(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbindgen_object_clone_ref = function(arg0) { + const ret = getObject(arg0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_wasmGetDefaultPlanes_30ea3ce9f4160083 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).wasmGetDefaultPlanes(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_clearDefaultPlanes_83937e41bf1759ee = function() { return handleError(function (arg0) { + getObject(arg0).clearDefaultPlanes(); + }, arguments) }; + imports.wbg.__wbg_startNewSession_aebe8bfd4a65fd33 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).startNewSession(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_sendModelingCommandFromWasm_6c7e7b53d52a0309 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { + let deferred0_0; + let deferred0_1; + let deferred1_0; + let deferred1_1; + let deferred2_0; + let deferred2_1; + let deferred3_0; + let deferred3_1; + try { + deferred0_0 = arg1; + deferred0_1 = arg2; + deferred1_0 = arg3; + deferred1_1 = arg4; + deferred2_0 = arg5; + deferred2_1 = arg6; + deferred3_0 = arg7; + deferred3_1 = arg8; + const ret = getObject(arg0).sendModelingCommandFromWasm(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4), getStringFromWasm0(arg5, arg6), getStringFromWasm0(arg7, arg8)); + return addHeapObject(ret); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + }, arguments) }; + imports.wbg.__wbg_readFile_e49dda349149abc0 = function() { return handleError(function (arg0, arg1, arg2) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg1; + deferred0_1 = arg2; + const ret = getObject(arg0).readFile(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }, arguments) }; + imports.wbg.__wbg_exists_acffbefc6da89444 = function() { return handleError(function (arg0, arg1, arg2) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg1; + deferred0_1 = arg2; + const ret = getObject(arg0).exists(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }, arguments) }; + imports.wbg.__wbg_getAllFiles_e0a18dd1cdd831b1 = function() { return handleError(function (arg0, arg1, arg2) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg1; + deferred0_1 = arg2; + const ret = getObject(arg0).getAllFiles(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }, arguments) }; + imports.wbg.__wbindgen_boolean_get = function(arg0) { + const v = getObject(arg0); + const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2; + return ret; + }; + imports.wbg.__wbg_fetch_bc7c8e27076a5c84 = function(arg0) { + const ret = fetch(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_queueMicrotask_481971b0d87f3dd4 = function(arg0) { + queueMicrotask(getObject(arg0)); + }; + imports.wbg.__wbg_queueMicrotask_3cbae2ec6b6cd3d6 = function(arg0) { + const ret = getObject(arg0).queueMicrotask; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_function = function(arg0) { + const ret = typeof(getObject(arg0)) === 'function'; + return ret; + }; + imports.wbg.__wbg_crypto_d05b68a3572bb8ca = function(arg0) { + const ret = getObject(arg0).crypto; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_object = function(arg0) { + const val = getObject(arg0); + const ret = typeof(val) === 'object' && val !== null; + return ret; + }; + imports.wbg.__wbg_process_b02b3570280d0366 = function(arg0) { + const ret = getObject(arg0).process; + return addHeapObject(ret); + }; + imports.wbg.__wbg_versions_c1cb42213cedf0f5 = function(arg0) { + const ret = getObject(arg0).versions; + return addHeapObject(ret); + }; + imports.wbg.__wbg_node_43b1089f407e4ec2 = function(arg0) { + const ret = getObject(arg0).node; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_string = function(arg0) { + const ret = typeof(getObject(arg0)) === 'string'; + return ret; + }; + imports.wbg.__wbg_require_9a7e0f667ead4995 = function() { return handleError(function () { + const ret = module.require; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_msCrypto_10fc94afee92bd76 = function(arg0) { + const ret = getObject(arg0).msCrypto; + return addHeapObject(ret); + }; + imports.wbg.__wbg_randomFillSync_b70ccbdf4926a99d = function() { return handleError(function (arg0, arg1) { + getObject(arg0).randomFillSync(takeObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_getRandomValues_7e42b4fb8779dc6d = function() { return handleError(function (arg0, arg1) { + getObject(arg0).getRandomValues(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_fetch_921fad6ef9e883dd = function(arg0, arg1) { + const ret = getObject(arg0).fetch(getObject(arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_signal_a61f78a3478fd9bc = function(arg0) { + const ret = getObject(arg0).signal; + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_0d76b0581eca6298 = function() { return handleError(function () { + const ret = new AbortController(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_abort_2aa7521d5690750e = function(arg0) { + getObject(arg0).abort(); + }; + imports.wbg.__wbg_close_a994f9425dab445c = function() { return handleError(function (arg0) { + getObject(arg0).close(); + }, arguments) }; + imports.wbg.__wbg_enqueue_ea194723156c0cc2 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).enqueue(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_ready_2a66bc1809d7ed5c = function(arg0) { + const ret = getObject(arg0).ready; + return addHeapObject(ret); + }; + imports.wbg.__wbg_close_aa5e556ea5c0337f = function(arg0) { + const ret = getObject(arg0).close(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_releaseLock_6bbcfb877d412d49 = function(arg0) { + getObject(arg0).releaseLock(); + }; + imports.wbg.__wbg_write_0305cf168e5e805e = function(arg0, arg1) { + const ret = getObject(arg0).write(getObject(arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_d4ab7daa4cb33d5f = function() { return handleError(function () { + const ret = new FormData(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_append_056476f73715b602 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + getObject(arg0).append(getStringFromWasm0(arg1, arg2), getObject(arg3)); + }, arguments) }; + imports.wbg.__wbg_append_9fd018eae44ea54a = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) { + getObject(arg0).append(getStringFromWasm0(arg1, arg2), getObject(arg3), getStringFromWasm0(arg4, arg5)); + }, arguments) }; + imports.wbg.__wbg_append_9c9890ca2ce97dba = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); + }, arguments) }; + imports.wbg.__wbg_getWriter_01ddb812f0418756 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).getWriter(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_new_ab6fd82b10560829 = function() { return handleError(function () { + const ret = new Headers(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_append_7bfcb4937d1d5e29 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); + }, arguments) }; + imports.wbg.__wbg_view_7f0ce470793a340f = function(arg0) { + const ret = getObject(arg0).view; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_respond_b1a43b2e3a06d525 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).respond(arg1 >>> 0); + }, arguments) }; + imports.wbg.__wbg_newwithu8arraysequenceandoptions_366f462e1b363808 = function() { return handleError(function (arg0, arg1) { + const ret = new Blob(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_newwithstrandinit_3fd6fba4083ff2d0 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_byobRequest_72fca99f9c32c193 = function(arg0) { + const ret = getObject(arg0).byobRequest; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_close_184931724d961ccc = function() { return handleError(function (arg0) { + getObject(arg0).close(); + }, arguments) }; + imports.wbg.__wbg_instanceof_Response_849eb93e75734b6e = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof Response; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_url_5f6dc4009ac5f99d = function(arg0, arg1) { + const ret = getObject(arg1).url; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_status_61a01141acd3cf74 = function(arg0) { + const ret = getObject(arg0).status; + return ret; + }; + imports.wbg.__wbg_headers_9620bfada380764a = function(arg0) { + const ret = getObject(arg0).headers; + return addHeapObject(ret); + }; + imports.wbg.__wbg_text_450a059667fd91fd = function() { return handleError(function (arg0) { + const ret = getObject(arg0).text(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_new_16b304a2cfa7ff4a = function() { + const ret = new Array(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newnoargs_e258087cd0daa0ea = function(arg0, arg1) { + const ret = new Function(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_next_40fc327bfc8770e6 = function(arg0) { + const ret = getObject(arg0).next; + return addHeapObject(ret); + }; + imports.wbg.__wbg_next_196c84450b364254 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).next(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_done_298b57d23c0fc80c = function(arg0) { + const ret = getObject(arg0).done; + return ret; + }; + imports.wbg.__wbg_value_d93c65011f51a456 = function(arg0) { + const ret = getObject(arg0).value; + return addHeapObject(ret); + }; + imports.wbg.__wbg_iterator_2cee6dadfd956dfa = function() { + const ret = Symbol.iterator; + return addHeapObject(ret); + }; + imports.wbg.__wbg_get_e3c254076557e348 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_call_27c0f87801dedf93 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).call(getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_new_72fb9a18b5ae2624 = function() { + const ret = new Object(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_self_ce0dbfc45cf2f5be = function() { return handleError(function () { + const ret = self.self; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_window_c6fb939a7f436783 = function() { return handleError(function () { + const ret = window.window; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_globalThis_d1e6af4856ba331b = function() { return handleError(function () { + const ret = globalThis.globalThis; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_global_207b558942527489 = function() { return handleError(function () { + const ret = global.global; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_push_a5b05aedc7234f9f = function(arg0, arg1) { + const ret = getObject(arg0).push(getObject(arg1)); + return ret; + }; + imports.wbg.__wbg_new_28c511d9baebfa89 = function(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_toString_ffe4c9ea3b3532e9 = function(arg0) { + const ret = getObject(arg0).toString(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_call_b3ca7c6051f9bec1 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_next_a1c35169a4db2ac1 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).next(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_getTime_2bc4375165f02d15 = function(arg0) { + const ret = getObject(arg0).getTime(); + return ret; + }; + imports.wbg.__wbg_new0_7d84e5b2cd9fdc73 = function() { + const ret = new Date(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_toString_c816a20ab859d0c1 = function(arg0) { + const ret = getObject(arg0).toString(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_81740750da40724f = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return __wbg_adapter_234(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + const ret = new Promise(cb0); + return addHeapObject(ret); + } finally { + state0.a = state0.b = 0; + } + }; + imports.wbg.__wbg_resolve_b0083a7967828ec8 = function(arg0) { + const ret = Promise.resolve(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_then_0c86a60e8fcfe9f6 = function(arg0, arg1) { + const ret = getObject(arg0).then(getObject(arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_then_a73caa9a87991566 = function(arg0, arg1, arg2) { + const ret = getObject(arg0).then(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_buffer_12d079cc21e14bdb = function(arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_aa4a17c33a06e5cb = function(arg0, arg1, arg2) { + const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_63b92bc8671ed464 = function(arg0) { + const ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_set_a47bac70306a19a7 = function(arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); + }; + imports.wbg.__wbg_length_c20a40f15020d68a = function(arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_instanceof_Uint8Array_2b3bbecd033d19f6 = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof Uint8Array; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_newwithlength_e9b4878cebadb3d3 = function(arg0) { + const ret = new Uint8Array(arg0 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_buffer_dd7f74bc60f1faab = function(arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_subarray_a1f73cd4b5b42fe1 = function(arg0, arg1, arg2) { + const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_byteLength_58f7b4fab1919d44 = function(arg0) { + const ret = getObject(arg0).byteLength; + return ret; + }; + imports.wbg.__wbg_byteOffset_81d60f7392524f62 = function(arg0) { + const ret = getObject(arg0).byteOffset; + return ret; + }; + imports.wbg.__wbg_has_0af94d20077affa2 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.has(getObject(arg0), getObject(arg1)); + return ret; + }, arguments) }; + imports.wbg.__wbg_set_1f9b04f170055d33 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2)); + return ret; + }, arguments) }; + imports.wbg.__wbg_parse_66d1801634e099ac = function() { return handleError(function (arg0, arg1) { + const ret = JSON.parse(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_stringify_8887fe74e1c50d81 = function() { return handleError(function (arg0) { + const ret = JSON.stringify(getObject(arg0)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbindgen_memory = function() { + const ret = wasm.memory; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper14419 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 5618, __wbg_adapter_30); + return addHeapObject(ret); + }; + + return imports; +} + +function __wbg_init_memory(imports, maybe_memory) { + +} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedFloat64Memory0 = null; + cachedInt32Memory0 = null; + cachedUint8Memory0 = null; + + + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(input) { + if (wasm !== undefined) return wasm; + + if (typeof input === 'undefined') { + input = new URL('wasm_lib_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { + input = fetch(input); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await input, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync } +export default __wbg_init; diff --git a/src/wasm-lib/pkg/wasm_lib_bg.wasm b/src/wasm-lib/pkg/wasm_lib_bg.wasm new file mode 100644 index 000000000..e8a84296d Binary files /dev/null and b/src/wasm-lib/pkg/wasm_lib_bg.wasm differ diff --git a/src/wasm-lib/pkg/wasm_lib_bg.wasm.d.ts b/src/wasm-lib/pkg/wasm_lib_bg.wasm.d.ts new file mode 100644 index 000000000..1a027da35 --- /dev/null +++ b/src/wasm-lib/pkg/wasm_lib_bg.wasm.d.ts @@ -0,0 +1,67 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export function toml_stringify(a: number, b: number, c: number): void; +export function execute_wasm(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): number; +export function kcl_lint(a: number, b: number): number; +export function make_default_planes(a: number): number; +export function modify_grid(a: number, b: number): number; +export function modify_ast_for_sketch_wasm(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): number; +export function deserialize_files(a: number, b: number, c: number): void; +export function lexer_wasm(a: number, b: number, c: number): void; +export function parse_wasm(a: number, b: number, c: number): void; +export function recast_wasm(a: number, b: number, c: number): void; +export function __wbg_serverconfig_free(a: number): void; +export function serverconfig_new(a: number, b: number, c: number): number; +export function kcl_lsp_run(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number): number; +export function copilot_lsp_run(a: number, b: number, c: number, d: number, e: number): number; +export function is_points_ccw(a: number, b: number): number; +export function __wbg_tangentialarcinfooutputwasm_free(a: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_center_x(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_center_x(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_center_y(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_center_y(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_arc_mid_point_x(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_arc_mid_point_x(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_arc_mid_point_y(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_arc_mid_point_y(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_radius(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_radius(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_start_angle(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_start_angle(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_end_angle(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_end_angle(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_ccw(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_ccw(a: number, b: number): void; +export function __wbg_get_tangentialarcinfooutputwasm_arc_length(a: number): number; +export function __wbg_set_tangentialarcinfooutputwasm_arc_length(a: number, b: number): void; +export function get_tangential_arc_to_info(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number; +export function program_memory_init(a: number): void; +export function coredump(a: number): number; +export function default_app_settings(a: number): void; +export function parse_app_settings(a: number, b: number, c: number): void; +export function default_project_settings(a: number): void; +export function parse_project_settings(a: number, b: number, c: number): void; +export function serialize_project_settings(a: number, b: number): void; +export function base64_decode(a: number, b: number, c: number): void; +export function __wbg_intounderlyingsource_free(a: number): void; +export function intounderlyingsource_pull(a: number, b: number): number; +export function intounderlyingsource_cancel(a: number): void; +export function __wbg_intounderlyingsink_free(a: number): void; +export function intounderlyingsink_write(a: number, b: number): number; +export function intounderlyingsink_close(a: number): number; +export function intounderlyingsink_abort(a: number, b: number): number; +export function __wbg_intounderlyingbytesource_free(a: number): void; +export function intounderlyingbytesource_type(a: number, b: number): void; +export function intounderlyingbytesource_autoAllocateChunkSize(a: number): number; +export function intounderlyingbytesource_start(a: number, b: number): void; +export function intounderlyingbytesource_pull(a: number, b: number): number; +export function intounderlyingbytesource_cancel(a: number): void; +export function __wbindgen_malloc(a: number, b: number): number; +export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; +export const __wbindgen_export_2: WebAssembly.Table; +export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h441a9e3e174bc8f1(a: number, b: number, c: number): void; +export function __wbindgen_add_to_stack_pointer(a: number): number; +export function __wbindgen_free(a: number, b: number, c: number): void; +export function __wbindgen_exn_store(a: number): void; +export function wasm_bindgen__convert__closures__invoke2_mut__h38ba35493531eafd(a: number, b: number, c: number, d: number): void;