diff --git a/package-lock.json b/package-lock.json index a06d4fa05..432c3db15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2366,23 +2366,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@buf/bufbuild_protovalidate.bufbuild_es": { - "version": "1.10.0-20240212200630-3014d81c3a48.1", - "resolved": "https://buf.build/gen/npm/v1/@buf/bufbuild_protovalidate.bufbuild_es/-/bufbuild_protovalidate.bufbuild_es-1.10.0-20240212200630-3014d81c3a48.1.tgz", - "peerDependencies": { - "@bufbuild/protobuf": "^1.10.0" - } - }, - "node_modules/@buf/bufbuild_protovalidate.connectrpc_es": { - "version": "1.5.0-20240212200630-3014d81c3a48.1", - "resolved": "https://buf.build/gen/npm/v1/@buf/bufbuild_protovalidate.connectrpc_es/-/bufbuild_protovalidate.connectrpc_es-1.5.0-20240212200630-3014d81c3a48.1.tgz", - "dependencies": { - "@buf/bufbuild_protovalidate.bufbuild_es": "1.10.0-20240212200630-3014d81c3a48.1" - }, - "peerDependencies": { - "@connectrpc/connect": "^1.5.0" - } - }, "node_modules/@buf/googleapis_googleapis.community_timostamm-protobuf-ts": { "version": "2.9.4-20240827201746-e7f8d366f526.4", "resolved": "https://buf.build/gen/npm/v1/@buf/googleapis_googleapis.community_timostamm-protobuf-ts/-/googleapis_googleapis.community_timostamm-protobuf-ts-2.9.4-20240827201746-e7f8d366f526.4.tgz", @@ -2403,34 +2386,29 @@ } }, "node_modules/@buf/jlewi_foyle.bufbuild_es": { - "version": "1.10.0-20241018224331-ac296bc95724.1", - "resolved": "https://buf.build/gen/npm/v1/@buf/jlewi_foyle.bufbuild_es/-/jlewi_foyle.bufbuild_es-1.10.0-20241018224331-ac296bc95724.1.tgz", + "version": "1.10.0-20241219050144-856e893555b3.1", + "resolved": "https://buf.build/gen/npm/v1/@buf/jlewi_foyle.bufbuild_es/-/jlewi_foyle.bufbuild_es-1.10.0-20241219050144-856e893555b3.1.tgz", "dependencies": { - "@buf/bufbuild_protovalidate.bufbuild_es": "1.10.0-20240212200630-3014d81c3a48.1", - "@buf/stateful_runme.bufbuild_es": "1.10.0-20240913234806-45813f39881a.1" + "@buf/stateful_runme.bufbuild_es": "1.10.0-20241021161352-11c142f88aee.1" }, "peerDependencies": { "@bufbuild/protobuf": "^1.10.0" } }, "node_modules/@buf/jlewi_foyle.connectrpc_es": { - "version": "1.5.0-20241018224331-ac296bc95724.1", - "resolved": "https://buf.build/gen/npm/v1/@buf/jlewi_foyle.connectrpc_es/-/jlewi_foyle.connectrpc_es-1.5.0-20241018224331-ac296bc95724.1.tgz", + "version": "1.5.0-20241219050144-856e893555b3.1", + "resolved": "https://buf.build/gen/npm/v1/@buf/jlewi_foyle.connectrpc_es/-/jlewi_foyle.connectrpc_es-1.5.0-20241219050144-856e893555b3.1.tgz", "dependencies": { - "@buf/bufbuild_protovalidate.connectrpc_es": "1.5.0-20240212200630-3014d81c3a48.1", - "@buf/jlewi_foyle.bufbuild_es": "1.10.0-20241018224331-ac296bc95724.1", - "@buf/stateful_runme.connectrpc_es": "1.5.0-20240913234806-45813f39881a.1" + "@buf/jlewi_foyle.bufbuild_es": "1.10.0-20241219050144-856e893555b3.1", + "@buf/stateful_runme.connectrpc_es": "1.5.0-20241021161352-11c142f88aee.1" }, "peerDependencies": { "@connectrpc/connect": "^1.5.0" } }, "node_modules/@buf/stateful_runme.bufbuild_es": { - "version": "1.10.0-20240913234806-45813f39881a.1", - "resolved": "https://buf.build/gen/npm/v1/@buf/stateful_runme.bufbuild_es/-/stateful_runme.bufbuild_es-1.10.0-20240913234806-45813f39881a.1.tgz", - "dependencies": { - "@buf/bufbuild_protovalidate.bufbuild_es": "1.10.0-20240212200630-3014d81c3a48.1" - }, + "version": "1.10.0-20241021161352-11c142f88aee.1", + "resolved": "https://buf.build/gen/npm/v1/@buf/stateful_runme.bufbuild_es/-/stateful_runme.bufbuild_es-1.10.0-20241021161352-11c142f88aee.1.tgz", "peerDependencies": { "@bufbuild/protobuf": "^1.10.0" } @@ -2444,11 +2422,10 @@ } }, "node_modules/@buf/stateful_runme.connectrpc_es": { - "version": "1.5.0-20240913234806-45813f39881a.1", - "resolved": "https://buf.build/gen/npm/v1/@buf/stateful_runme.connectrpc_es/-/stateful_runme.connectrpc_es-1.5.0-20240913234806-45813f39881a.1.tgz", + "version": "1.5.0-20241021161352-11c142f88aee.1", + "resolved": "https://buf.build/gen/npm/v1/@buf/stateful_runme.connectrpc_es/-/stateful_runme.connectrpc_es-1.5.0-20241021161352-11c142f88aee.1.tgz", "dependencies": { - "@buf/bufbuild_protovalidate.connectrpc_es": "1.5.0-20240212200630-3014d81c3a48.1", - "@buf/stateful_runme.bufbuild_es": "1.10.0-20240913234806-45813f39881a.1" + "@buf/stateful_runme.bufbuild_es": "1.10.0-20241021161352-11c142f88aee.1" }, "peerDependencies": { "@connectrpc/connect": "^1.5.0" diff --git a/src/extension/ai/converters.ts b/src/extension/ai/converters.ts index 349e2e568..3b3079891 100644 --- a/src/extension/ai/converters.ts +++ b/src/extension/ai/converters.ts @@ -11,8 +11,6 @@ import * as serializerTypes from '../grpc/serializerTypes' import * as serializer from '../serializer' import { Kernel } from '../kernel' -import * as protos from './protos' - // Converter provides converstion routines from vscode data types to protocol buffer types. // It is a class because in order to handle the conversion we need to keep track of the kernel // because we need to add execution information to the cells before serializing. @@ -30,8 +28,7 @@ export class Converter { let notebookDataWithExec = new vscode.NotebookData(cellDataWithExec) // marshalNotebook returns a protocol buffer using the ts client library from buf we need to // convert it to es - let notebookProto = serializer.GrpcSerializer.marshalNotebook(notebookDataWithExec) - return protos.notebookTSToES(notebookProto) + return serializer.GrpcSerializer.marshalNotebook(notebookDataWithExec) } } diff --git a/src/extension/kernel.ts b/src/extension/kernel.ts index a9db4a819..37864e2f2 100644 --- a/src/extension/kernel.ts +++ b/src/extension/kernel.ts @@ -103,7 +103,7 @@ import { handleCellOutputMessage } from './messages/cellOutput' import handleGitHubMessage, { handleGistMessage } from './messages/github' import { getNotebookCategories } from './utils' import PanelManager from './panels/panelManager' -import { GrpcSerializer, SerializerBase } from './serializer' +import { GrpcSerializer } from './serializer' import { askAlternativeOutputsAction, openSplitViewAsMarkdownText } from './commands' import { handlePlatformApiMessage } from './messages/platformRequest' import { handleGCPMessage } from './messages/gcp' @@ -151,7 +151,7 @@ export class Kernel implements Disposable { protected activeTerminals: ActiveTerminal[] = [] protected category?: string protected panelManager: PanelManager - protected serializer?: SerializerBase + protected serializer?: GrpcSerializer protected reporter?: GrpcReporter protected featuresState$?: FeatureObserver diff --git a/src/extension/messages/platformRequest/saveCellExecution.ts b/src/extension/messages/platformRequest/saveCellExecution.ts index df09cd8d2..b97af232f 100644 --- a/src/extension/messages/platformRequest/saveCellExecution.ts +++ b/src/extension/messages/platformRequest/saveCellExecution.ts @@ -4,7 +4,7 @@ import { Uri, env, workspace, commands } from 'vscode' import { TelemetryReporter } from 'vscode-telemetry' import getMAC from 'getmac' import YAML from 'yaml' -import { FetchResult } from '@apollo/client' +import { FetchResult, MutationOptions } from '@apollo/client' import { ClientMessages, NOTEBOOK_AUTOSAVE_ON, RUNME_FRONTMATTER_PARSED } from '../../../constants' import { ClientMessage, FeatureName, IApiMessage } from '../../../types' @@ -188,7 +188,7 @@ export default async function saveCellExecution( }, }, } - const result = await graphClient.mutate(mutation) + const result = await graphClient.mutate(mutation as MutationOptions) data = result } // TODO: Remove the legacy createCellExecution mutation once the reporter is fully tested. diff --git a/src/extension/serializer.ts b/src/extension/serializer.ts index 6c514bccc..371d1cb4f 100644 --- a/src/extension/serializer.ts +++ b/src/extension/serializer.ts @@ -19,10 +19,25 @@ import { NotebookCellExecutionSummary, commands, } from 'vscode' -import { GrpcTransport } from '@protobuf-ts/grpc-transport' import { ulid } from 'ulidx' import { maskString } from 'data-guardian' import YAML from 'yaml' +import { ParserService } from '@buf/stateful_runme.connectrpc_es/runme/parser/v1/parser_connect' +import { createGrpcTransport, GrpcTransportOptions } from '@connectrpc/connect-node' +import { createClient as createConnectClient, Client as ConnectClient } from '@connectrpc/connect' +// ts bindings generated by protoc-gen-es +import { + RunmeIdentity, + RunmeSession, + Notebook, + Frontmatter, + CellOutput, + CellKind, + CellExecutionSummary, + DeserializeRequest, + SerializeRequest, + SerializeRequestOptions, +} from '@buf/stateful_runme.bufbuild_es/runme/parser/v1/parser_pb' import { Serializer } from '../types' import { @@ -34,24 +49,13 @@ import { RUNME_FRONTMATTER_PARSED, VSCODE_LANGUAGEID_MAP, } from '../constants' -import { ServerLifecycleIdentity, getSessionOutputs } from '../utils/configuration' +import { ServerLifecycleIdentity, getSessionOutputs, getTLSEnabled } from '../utils/configuration' -import { - DeserializeRequest, - SerializeRequest, - Notebook, - RunmeIdentity, - CellKind, - CellOutput, - SerializeRequestOptions, - RunmeSession, - Frontmatter, -} from './grpc/serializerTypes' -import { initParserClient, ParserServiceClient, type ReadyPromise } from './grpc/client' +import { type ReadyPromise } from './grpc/client' import Languages from './languages' import { PLATFORM_OS } from './constants' import { initWasm } from './utils' -import { IServer } from './server/kernelServer' +import KernelServer from './server/kernelServer' import { Kernel } from './kernel' import { getCellById } from './cell' import { IProcessInfoState } from './terminal/terminalState' @@ -474,36 +478,35 @@ export class WasmSerializer extends SerializerBase { } export class GrpcSerializer extends SerializerBase { - private client?: ParserServiceClient + private client!: ConnectClient protected ready: ReadyPromise + protected serverUrl!: string + // todo(sebastian): naive cache for now, consider use lifecycle events for gc protected readonly plainCache = new Map>() protected readonly maskedCache = new Map>() protected readonly notebookDataCache = new Map() protected readonly cacheDocUriMapping: Map = new Map() - private serverReadyListener: Disposable | undefined + private serverReadyListener?: Disposable constructor( protected context: ExtensionContext, - protected server: IServer, + protected server: KernelServer, kernel: Kernel, ) { super(context, kernel) this.togglePreviewButton(GrpcSerializer.sessionOutputsEnabled()) + // cleanup listener when it's outlived its purpose this.ready = new Promise((resolve) => { const disposable = server.onTransportReady(() => { disposable.dispose() resolve() }) }) - - this.serverReadyListener = server.onTransportReady(({ transport }) => - this.initParserClient(transport), - ) - + this.serverReadyListener = server.onTransportReady(async () => await this.initClient()) this.disposables.push( // todo(sebastian): delete entries on session reset not notebook editor lifecycle // workspace.onDidCloseNotebookDocument(this.handleCloseNotebook.bind(this)), @@ -512,8 +515,45 @@ export class GrpcSerializer extends SerializerBase { ) } - private async initParserClient(transport?: GrpcTransport) { - this.client = initParserClient(transport ?? (await this.server.transport())) + private async initClient(): Promise { + // Server options: + // (1) pre-existing, started internally by this extension (assuming local execution so it has permissions to start) + // (2) pre-existing, started externally, presumably at the this.serverUrl address + // In both cases we need a key & cert (assuming tls) which may reside in: + // (a) this project: '/path/to/projectRoot/tls' + // (b) the runme binary server default: '~/.config/runme/tls' + // (c) other, manually configured + + // assuming 2a, which is the default when starting the server as a result of loading this extension + const addTlsConfigIfEnabled = async () => { + try { + if (!getTLSEnabled()) { + return {} + } + const pems = await KernelServer.getTLS(this.server.getTLSDir()) + + return { + nodeOptions: { + key: pems.privKeyPEM, + cert: pems.certPEM, + rejectUnauthorized: false, + }, + } + } catch (e: any) { + throw new Error(`Failed to read TLS files: ${e instanceof Error ? e.message : String(e)}`) + } + } + + const s = getTLSEnabled() ? 's' : '' + this.serverUrl = `http${s}://${this.server.address()}` + const tlsConfig = await addTlsConfigIfEnabled() + let grpcOptions = { + baseUrl: this.serverUrl, + httpVersion: '2', + ...tlsConfig, + } + + this.client = createConnectClient(ParserService, createGrpcTransport(grpcOptions)) } public togglePreviewButton(state: boolean) { @@ -702,14 +742,13 @@ export class GrpcSerializer extends SerializerBase { await notebook.save() const source = await workspace.fs.readFile(notebook.uri) - const des = await this.client!.deserialize( - DeserializeRequest.create({ - source, - options: { identity }, - }), - ) + const dreq = new DeserializeRequest({ + source, + options: { identity }, + }) + + const deserialized = (await this.client.deserialize(dreq)).notebook - const deserialized = des.response.notebook if (!deserialized) { return false } @@ -750,10 +789,10 @@ export class GrpcSerializer extends SerializerBase { const cacheId = GrpcSerializer.getDocumentCacheId(data.metadata) this.notebookDataCache.set(cacheId as string, data) - const serialRequest = { notebook } + const serialRequest = new SerializeRequest({ notebook }) const cacheOutputs = this.cacheNotebookOutputs(notebook, cacheId) - const request = this.client!.serialize(serialRequest) + const request = this.client.serialize(serialRequest) // run in parallel const [serialResult] = await Promise.all([request, cacheOutputs]) @@ -762,12 +801,11 @@ export class GrpcSerializer extends SerializerBase { await this.saveNotebookOutputsByCacheId(cacheId) } - const { result } = serialResult.response - if (result === undefined) { + if (serialResult.result === undefined) { throw new Error('serialization of notebook failed') } - return result + return serialResult.result } static sessionOutputsEnabled() { @@ -777,6 +815,7 @@ export class GrpcSerializer extends SerializerBase { return isSessionOutputs && isAutoSaveOn } + // unable to abstract due to RunmeSession struct potential differences & notebook ts type validation issues private async cacheNotebookOutputs( notebook: Notebook, cacheId: string | undefined, @@ -786,19 +825,19 @@ export class GrpcSerializer extends SerializerBase { const sid = this.kernel.getRunnerEnvironment()?.getSessionId() if (sid && docUri) { const relativePath = path.basename(docUri.fsPath) - session = { + session = new RunmeSession({ id: sid, document: { relativePath }, - } + }) } const outputs = { enabled: true, summary: true } - const options = SerializeRequestOptions.clone({ + const options = new SerializeRequestOptions({ outputs, session, }) - const maskedNotebook = Notebook.clone(notebook) + const maskedNotebook = notebook.clone() maskedNotebook.cells.forEach((cell) => { cell.value = maskString(cell.value) cell.outputs.forEach((out) => { @@ -811,16 +850,16 @@ export class GrpcSerializer extends SerializerBase { }) }) - const plainReq = { notebook, options } - const plainRes = this.client!.serialize(plainReq) + const plainReq = new SerializeRequest({ notebook, options }) + const plainRes = this.client.serialize(plainReq) - const maskedReq = { notebook: maskedNotebook, options } - const masked = this.client!.serialize(maskedReq).then((maskedRes) => { - if (maskedRes.response.result === undefined) { + const maskedReq = new SerializeRequest({ notebook: maskedNotebook, options }) + const masked = this.client.serialize(maskedReq).then((maskedRes) => { + if (maskedRes.result === undefined) { console.error('serialization of masked notebook failed') return Promise.resolve(new Uint8Array()) } - return maskedRes.response.result + return maskedRes.result }) if (!cacheId) { @@ -830,11 +869,11 @@ export class GrpcSerializer extends SerializerBase { } const plain = await plainRes - if (plain.response.result === undefined) { + if (plain.result === undefined) { throw new Error('serialization of notebook outputs failed') } - const bytes = plain.response.result + const bytes = plain.result if (!cacheId) { console.error('skip plain caching since no lifecycleId was found') } else { @@ -844,7 +883,7 @@ export class GrpcSerializer extends SerializerBase { await Promise.all([plain, masked]) } - // marshalNotebook converts VSCode's NotebookData to the Notebook proto. + // vscode/NotebookData to buf-es/Notebook public static marshalNotebook( data: NotebookData, config?: { @@ -853,7 +892,7 @@ export class GrpcSerializer extends SerializerBase { }, ): Notebook { // the bulk copies cleanly except for what's below - const notebook = Notebook.clone(data as any) + const notebook = new Notebook(data as any) // cannot gurantee it wasn't changed if (notebook.metadata[RUNME_FRONTMATTER_PARSED]) { @@ -886,7 +925,7 @@ export class GrpcSerializer extends SerializerBase { typeof metadata['runme.dev/frontmatter'] !== 'string' ) { log.warn('no frontmatter found in metadata') - return { + return new Frontmatter({ category: '', tag: '', cwd: '', @@ -897,7 +936,7 @@ export class GrpcSerializer extends SerializerBase { shell: '', skipPrompts: false, terminalRows: '', - } + }) } const rawFrontmatter = metadata['runme.dev/frontmatter'] @@ -917,7 +956,7 @@ export class GrpcSerializer extends SerializerBase { } } - return { + return new Frontmatter({ runme: { id: data.runme?.id || '', version: data.runme?.version || '', @@ -929,7 +968,7 @@ export class GrpcSerializer extends SerializerBase { shell: '', skipPrompts: false, terminalRows: '', - } + }) } private static marshalCellOutputs( @@ -945,13 +984,13 @@ export class GrpcSerializer extends SerializerBase { // todo(sebastian): consider sending error state too if (dataOut.processInfo?.exitReason?.type === 'exit') { if (dataOut.processInfo.exitReason.code) { - out.processInfo!.exitReason!.code!.value = dataOut.processInfo.exitReason.code + out.processInfo!.exitReason!.code = dataOut.processInfo.exitReason.code } else { out.processInfo!.exitReason!.code = undefined } if (dataOut.processInfo?.pid !== undefined) { - out.processInfo!.pid = { value: dataOut.processInfo.pid.toString() } + out.processInfo!.pid = BigInt(dataOut.processInfo.pid) } else { out.processInfo!.pid = undefined } @@ -966,7 +1005,7 @@ export class GrpcSerializer extends SerializerBase { private static marshalCellExecutionSummary( executionSummary: NotebookCellExecutionSummary | undefined, - ) { + ): CellExecutionSummary | undefined { if (!executionSummary) { return undefined } @@ -976,13 +1015,13 @@ export class GrpcSerializer extends SerializerBase { return undefined } - return { - success: { value: success }, + return new CellExecutionSummary({ + success: success, timing: { - endTime: { value: timing!.endTime.toString() }, - startTime: { value: timing!.startTime.toString() }, + endTime: BigInt(timing!.endTime), + startTime: BigInt(timing!.startTime), }, - } + }) } protected async reviveNotebook( @@ -991,18 +1030,33 @@ export class GrpcSerializer extends SerializerBase { token: CancellationToken, ): Promise { const identity = this.lifecycleIdentity - const deserialRequest = DeserializeRequest.create({ source: content, options: { identity } }) - const request = await this.client!.deserialize(deserialRequest) + const deserializeRequest = new DeserializeRequest({ + source: content, + options: { identity }, + }) + let res + try { + res = await this.client.deserialize(deserializeRequest) + } catch (e: any) { + if (e.name === 'ConnectError') { + e.message = `Unable to connect to the serializer service at ${this.serverUrl}` + } + log.error('Error in reviveNotebook ', e as any) + throw e + } + const notebook = res.notebook - const { notebook } = request.response if (notebook === undefined) { throw new Error('deserialization failed to revive notebook') } - const _notebook = this.applyIdentity(notebook) + this.applyIdentity(notebook) + if (!notebook) { + return this.printCell('⚠️ __Error__: no cells found!') + } // we can remove ugly casting once we switch to GRPC - return _notebook as unknown as Serializer.Notebook + return notebook as unknown as Serializer.Notebook } public dispose(): void { diff --git a/src/extension/server/kernelServer.ts b/src/extension/server/kernelServer.ts index 8c3bf76ea..3576d3f49 100644 --- a/src/extension/server/kernelServer.ts +++ b/src/extension/server/kernelServer.ts @@ -167,7 +167,7 @@ class KernelServer implements IServer { return !!(getCustomServerAddress() || this.#forceExternalServer) } - private static async getTLS(tlsDir: string) { + public static async getTLS(tlsDir: string) { try { const certPEM = await fs.readFile(path.join(tlsDir, 'cert.pem')) const privKeyPEM = await fs.readFile(path.join(tlsDir, 'key.pem')) @@ -178,7 +178,7 @@ class KernelServer implements IServer { } } - protected getTLSDir(): string { + public getTLSDir(): string { return getTLSDir(this.extBasePath) } diff --git a/tests/extension/serializer.test.ts b/tests/extension/serializer.test.ts index 61de13e1a..7ad268783 100644 --- a/tests/extension/serializer.test.ts +++ b/tests/extension/serializer.test.ts @@ -8,9 +8,9 @@ import { } from 'vscode' import { expect, vi, it, describe, beforeEach } from 'vitest' import { isValid } from 'ulidx' +import { RunmeIdentity, Notebook } from '@buf/stateful_runme.bufbuild_es/runme/parser/v1/parser_pb' import { GrpcSerializer, SerializerBase, WasmSerializer } from '../../src/extension/serializer' -import { RunmeIdentity } from '../../src/extension/grpc/serializerTypes' import type { Kernel } from '../../src/extension/kernel' import { EventEmitter, Uri } from '../../__mocks__/vscode' import { Serializer } from '../../src/types' @@ -67,6 +67,7 @@ vi.mock('../../src/extension/languages', () => ({ vi.mock('../../src/extension/utils', () => ({ initWasm: vi.fn(), + isWindows: vi.fn().mockReturnValue(false), })) vi.mock('../../src/extension/features') @@ -367,7 +368,7 @@ describe('GrpcSerializer', () => { const serializer: any = new GrpcSerializer(context, new Server(), new Kernel()) serializer.client = { deserialize: vi.fn().mockResolvedValue({ - response: { notebook: { cells: descells, metadata } }, + notebook: { cells: descells, metadata }, }), } vi.mocked(workspace.applyEdit).mockResolvedValue(true) @@ -462,11 +463,11 @@ describe('GrpcSerializer', () => { const summary = notebookData.cells[1].executionSummary expect(summary?.success).toBeDefined() - expect(summary?.success?.value).toStrictEqual(false) + expect(summary?.success).toStrictEqual(false) expect(summary?.timing).toBeDefined() - expect(summary?.timing?.startTime?.value).toStrictEqual('1701444499517') - expect(summary?.timing?.endTime?.value).toStrictEqual('1701444501696') + expect(summary?.timing?.startTime).toStrictEqual(1701444499517n) + expect(summary?.timing?.endTime).toStrictEqual(1701444501696n) }) }) @@ -481,15 +482,15 @@ describe('GrpcSerializer', () => { const items = cells.outputs[0].items expect(items.length).toBe(2) items.forEach((item) => { - expect((item.data as any).type).toBe('Buffer') + expect(item.type).toBe('Buffer') expect(item.mime).toBeDefined() }) const { processInfo } = cells.outputs[0] expect(processInfo?.exitReason).toBeDefined() expect(processInfo?.exitReason?.type).toStrictEqual('exit') - expect(processInfo?.exitReason?.code?.value).toStrictEqual(16) + expect(processInfo?.exitReason?.code).toStrictEqual(16) expect(processInfo?.pid).toBeDefined() - expect(processInfo?.pid?.value).toStrictEqual('98354') + expect(processInfo?.pid).toStrictEqual(98354n) }) }) @@ -626,7 +627,7 @@ describe('GrpcSerializer', () => { const fixture = deepCopyFixture() const writeableSer: any = new GrpcSerializer(context, new Server(), new Kernel()) writeableSer.client = { - serialize: vi.fn().mockResolvedValue({ response: { result: fakeCachedBytes } }), + serialize: vi.fn().mockResolvedValue({ result: fakeCachedBytes }), } writeableSer.cacheDocUriMapping.set(fixture.metadata['runme.dev/cacheId'], fakeSrcDocUri) ContextState.getKey = vi.fn().mockImplementation(() => true) @@ -675,20 +676,18 @@ describe('GrpcSerializer', () => { const context: any = { extensionUri: { fsPath: '/foo/bar' }, } - const fixture = { + const fixture = new Notebook({ cells: [], metadata: { 'runme.dev/finalLineBreaks': '1', 'runme.dev/frontmatter': '---\nrunme:\n id: 01HF7B0KJPF469EG9ZWDNKKACQ\n version: v2.0\n---', }, - } + }) const serialize = vi.fn().mockImplementation(() => Promise.resolve({ - response: { - result: new Uint8Array([4, 3, 2, 1]), - }, + result: new Uint8Array([4, 3, 2, 1]), }), ) const ser = new GrpcSerializer(context, new Server(), new Kernel())