Skip to content

Commit

Permalink
Rework cell creation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
benjreinhart committed Jul 15, 2024
1 parent ca3b26f commit 5d79af6
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 160 deletions.
25 changes: 0 additions & 25 deletions packages/api/server/http.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import fs from 'node:fs/promises';
import { SRCBOOKS_DIR } from '../constants.mjs';
import express, { type Application } from 'express';
import cors from 'cors';
import type { MarkdownCellType, CodeCellType } from '@srcbook/shared';
import {
createSession,
findSession,
Expand All @@ -13,7 +12,6 @@ import {
updateSession,
sessionToResponse,
listSessions,
insertCellAt,
} from '../session.mjs';
import { generateSrcbook } from '../ai/srcbook-generator.mjs';
import { disk } from '../utils.mjs';
Expand Down Expand Up @@ -201,29 +199,6 @@ router.post('/sessions/:id/export', cors(), async (req, res) => {
}
});

router.options('/sessions/:id/cells', cors());
// Create a new cell. If no index is provided, append to the end, otherwise insert at the index
router.post('/sessions/:id/cells', cors(), async (req, res) => {
const { id } = req.params;
const { cell, index } = req.body as { cell: CodeCellType | MarkdownCellType; index: number };

if (cell.type !== 'code' && cell.type !== 'markdown') {
return res
.status(400)
.json({ error: true, message: 'Cell must be either a code cell or markdown cell' });
}

// First 2 cells are reserved (title and package.json)
if (typeof index !== 'number' || index < 2) {
return res.status(400).json({ error: true, message: 'Index is required' });
}

const session = await findSession(id);
const updatedCells = insertCellAt(session, cell, index);
updateSession(session, { cells: updatedCells });
return res.json({ error: false, result: cell });
});

router.options('/settings', cors());

router.get('/settings', cors(), async (_req, res) => {
Expand Down
32 changes: 32 additions & 0 deletions packages/api/server/ws.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
updateCell,
removeCell,
updateCodeCellFilename,
addCell,
} from '../session.mjs';
import { getSecrets } from '../config.mjs';
import type { SessionType } from '../types.mjs';
Expand All @@ -27,6 +28,7 @@ import type {
CellDeletePayloadType,
CellRenamePayloadType,
CellErrorType,
CellCreatePayloadType,
} from '@srcbook/shared';
import {
CellErrorPayloadSchema,
Expand All @@ -43,6 +45,7 @@ import {
TsServerStartPayloadSchema,
TsServerStopPayloadSchema,
TsServerCellDiagnosticsPayloadSchema,
CellCreatePayloadSchema,
} from '@srcbook/shared';
import tsservers from '../tsservers.mjs';
import { TsServer } from '../tsserver/tsserver.mjs';
Expand Down Expand Up @@ -263,6 +266,34 @@ async function cellStop(payload: CellStopPayloadType) {
}
}

async function cellCreate(payload: CellCreatePayloadType) {
const session = await findSession(payload.sessionId);

if (!session) {
throw new Error(`No session exists for session '${payload.sessionId}'`);
}

const { index, cell } = payload;

// TODO: handle potential errors
await addCell(session, cell, index);

if (
session.metadata.language === 'typescript' &&
cell.type === 'code' &&
tsservers.has(session.id)
) {
const tsserver = tsservers.get(session.id);

tsserver.open({
file: pathToCodeFile(session.dir, cell.filename),
fileContent: cell.source,
});

requestAllDiagnostics(tsserver, session);
}
}

function sendCellUpdateError(session: SessionType, cellId: string, errors: CellErrorType[]) {
wss.broadcast(`session:${session.id}`, 'cell:error', {
sessionId: session.id,
Expand Down Expand Up @@ -512,6 +543,7 @@ wss
.channel('session:*')
.incoming('cell:exec', CellExecPayloadSchema, cellExec)
.incoming('cell:stop', CellStopPayloadSchema, cellStop)
.incoming('cell:create', CellCreatePayloadSchema, cellCreate)
.incoming('cell:update', CellUpdatePayloadSchema, cellUpdate)
.incoming('cell:rename', CellRenamePayloadSchema, cellRename)
.incoming('cell:delete', CellDeletePayloadSchema, cellDelete)
Expand Down
17 changes: 17 additions & 0 deletions packages/api/session.mts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,23 @@ export async function listSessions(): Promise<Record<string, SessionType>> {
return sessions;
}

export async function addCell(
session: SessionType,
cell: MarkdownCellType | CodeCellType,
index: number,
) {
const cells = insertCellAt(session, cell, index);

session.cells = cells;

switch (cell.type) {
case 'markdown':
return writeReadmeToDisk(session.dir, session.metadata, session.cells);
case 'code':
return writeCellToDisk(session.dir, session.metadata, session.cells, cell);
}
}

export async function updateSession(
session: SessionType,
updates: Partial<SessionType>,
Expand Down
8 changes: 7 additions & 1 deletion packages/shared/src/schemas/websockets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import z from 'zod';
import { CellSchema, CellUpdateAttrsSchema } from './cells.js';
import { CellSchema, MarkdownCellSchema, CodeCellSchema, CellUpdateAttrsSchema } from './cells.js';
import { TsServerDiagnosticSchema } from './tsserver.js';

export const CellExecPayloadSchema = z.object({
Expand All @@ -12,6 +12,12 @@ export const CellStopPayloadSchema = z.object({
cellId: z.string(),
});

export const CellCreatePayloadSchema = z.object({
sessionId: z.string(),
index: z.number(),
cell: z.union([MarkdownCellSchema, CodeCellSchema]),
});

export const CellUpdatePayloadSchema = z.object({
sessionId: z.string(),
cellId: z.string(),
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/types/websockets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import z from 'zod';
import {
CellExecPayloadSchema,
CellStopPayloadSchema,
CellCreatePayloadSchema,
CellUpdatePayloadSchema,
CellUpdatedPayloadSchema,
CellRenamePayloadSchema,
Expand All @@ -19,6 +20,7 @@ import {

export type CellExecPayloadType = z.infer<typeof CellExecPayloadSchema>;
export type CellStopPayloadType = z.infer<typeof CellStopPayloadSchema>;
export type CellCreatePayloadType = z.infer<typeof CellCreatePayloadSchema>;
export type CellUpdatePayloadType = z.infer<typeof CellUpdatePayloadSchema>;
export type CellUpdatedPayloadType = z.infer<typeof CellUpdatedPayloadSchema>;
export type CellRenamePayloadType = z.infer<typeof CellRenamePayloadSchema>;
Expand Down
2 changes: 2 additions & 0 deletions packages/web/src/clients/websocket/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
CellOutputPayloadSchema,
CellCreatePayloadSchema,
CellUpdatedPayloadSchema,
DepsValidateResponsePayloadSchema,
CellExecPayloadSchema,
Expand Down Expand Up @@ -33,6 +34,7 @@ const IncomingSessionEvents = {
const OutgoingSessionEvents = {
'cell:exec': CellExecPayloadSchema,
'cell:stop': CellStopPayloadSchema,
'cell:create': CellCreatePayloadSchema,
'cell:update': CellUpdatePayloadSchema,
'cell:rename': CellRenamePayloadSchema,
'cell:delete': CellDeletePayloadSchema,
Expand Down
28 changes: 1 addition & 27 deletions packages/web/src/lib/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CellType, CodeCellType, MarkdownCellType, CodeLanguageType } from '@srcbook/shared';
import { CodeLanguageType } from '@srcbook/shared';
import { SessionType, FsObjectResultType, ExampleSrcbookType } from '@/types';

const API_BASE_URL = 'http://localhost:2150/api';
Expand Down Expand Up @@ -201,32 +201,6 @@ export async function exportSrcmdFile(sessionId: string, request: ExportSrcmdFil
return response.json();
}

interface CreateCellRequestType {
sessionId: string;
cell: CodeCellType | MarkdownCellType;
index?: number;
}

interface CreateCellResponseType {
error: boolean;
result: CellType;
}

export async function createCell(request: CreateCellRequestType): Promise<CreateCellResponseType> {
const response = await fetch(API_BASE_URL + '/sessions/' + request.sessionId + '/cells', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ cell: request.cell, index: request.index }),
});

if (!response.ok) {
console.error(response);
throw new Error('Request failed');
}

return response.json();
}

// Config settings
interface EditConfigRequestType {
baseDir?: string;
Expand Down
Loading

0 comments on commit 5d79af6

Please sign in to comment.