-
Notifications
You must be signed in to change notification settings - Fork 0
Add Springboard docs CLI and jamapp support infrastructure #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mickmister
wants to merge
23
commits into
vk/6c2d-vk-wrapper-app
Choose a base branch
from
vk/e1e4-copy-jamtools-fe
base: vk/6c2d-vk-wrapper-app
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
900da7a
Move springboard dev to single-port ModuleRunner
24883d9
Add midi_files_module to @jamtools/core package exports
4e02efa
Refactor io_module to use separate .browser.ts and .node.ts files
e0366d8
Use package.json export conditions for platform-specific io_dependencies
5786193
Fix ES module import reassignment in io_module for Vite dev mode
48723f5
Fix @tonejs/midi import to use default import with type import
51fe57e
Fix dev plugin stream and module-graph typings
c6b22d8
Add e2e dev-route HMR test for registerServerModule
ac6c546
docs: clarify registerServerModule contract
76d916a
apps/vite-test: add start script and configurable port
b2a694f
Committed the two requested files as `apps/vite-test: add start scrip…
1543cb5
apps/vite-test: add debug log for module load
2335c8e
fix: use single-port dev server in vite plugin
b3b9628
fix: install vite dev middleware before spa fallback
6a8c1c4
refactor: remove unused vite plugin modules
c44e8be
Committed the deletions as `refactor: remove unused vite plugin modul…
98cb178
refactor: store generated vite files in node_modules
8fb45c6
edit scripts
1616cba
fix: serve dev web entry through vite transform
b7b6ea0
fix: stop forcing localhost npm registry in ci
94e21c4
Merge branch 'preview-pr-70-vk-8100-fix-vite-dev-nod' into vk/e1e4-co…
6db3543
Improve Springboard agent docs
32c43a1
Fix springboard reset preserving registered modules
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import process from 'node:process'; | ||
|
|
||
| import crosswsNode from 'crossws/adapters/node'; | ||
|
|
||
| import { initApp } from 'springboard/server/hono_app'; | ||
| import { makeWebsocketServerCoreDependenciesWithSqlite } from 'springboard/platforms/node/services/ws_server_core_dependencies'; | ||
| import { LocalJsonNodeKVStoreService } from 'springboard/platforms/node/services/node_kvstore_service'; | ||
| import { CoreDependencies, Springboard } from 'springboard/core'; | ||
| import { | ||
| springboard, | ||
| clearRegisteredModules, | ||
| clearRegisteredClassModules, | ||
| clearRegisteredSplashScreen, | ||
| } from 'springboard/core/engine/register'; | ||
| import { resetServerRegistry } from 'springboard/server/register'; | ||
|
|
||
| export type DevServerHandle = { | ||
| fetch: (request: Request) => Promise<Response>; | ||
| ws: ReturnType<typeof crosswsNode>; | ||
| dispose: () => Promise<void>; | ||
| }; | ||
|
|
||
| springboard.reset(); | ||
| clearRegisteredModules(); | ||
| clearRegisteredClassModules(); | ||
| clearRegisteredSplashScreen(); | ||
| resetServerRegistry(); | ||
|
|
||
| await import('../src/server-entry.ts'); | ||
|
|
||
| export async function createDevServer(): Promise<DevServerHandle> { | ||
| const nodeKvDeps = await makeWebsocketServerCoreDependenciesWithSqlite(); | ||
| const useWebSocketsForRpc = import.meta.env.VITE_USE_WEBSOCKETS_FOR_RPC === 'true'; | ||
|
|
||
| let wsNode: ReturnType<typeof crosswsNode>; | ||
|
|
||
| const { app, serverAppDependencies, injectResources, createWebSocketHooks } = initApp({ | ||
| broadcastMessage: (message) => { | ||
| return wsNode.publish('event', message); | ||
| }, | ||
| remoteKV: nodeKvDeps.kvStoreFromKysely, | ||
| userAgentKV: new LocalJsonNodeKVStoreService('userAgent'), | ||
| enableStaticRoutes: false, | ||
| }); | ||
|
|
||
| app.notFound((c) => { | ||
| c.header('x-springboard-fallback', '1'); | ||
| return c.text('', 404); | ||
| }); | ||
|
|
||
| wsNode = crosswsNode({ | ||
| hooks: createWebSocketHooks(useWebSocketsForRpc), | ||
| }); | ||
|
|
||
| const coreDeps: CoreDependencies = { | ||
| log: console.log, | ||
| showError: console.error, | ||
| storage: serverAppDependencies.storage, | ||
| isMaestro: () => true, | ||
| rpc: serverAppDependencies.rpc, | ||
| }; | ||
|
|
||
| Object.assign(coreDeps, serverAppDependencies); | ||
|
|
||
| const engine = new Springboard(coreDeps, {}); | ||
|
|
||
| injectResources({ | ||
| engine, | ||
| serveStaticFile: async (c, _fileName, headers) => { | ||
| Object.entries(headers).forEach(([key, value]) => { | ||
| c.header(key, value); | ||
| }); | ||
| c.status(404); | ||
| return c.text('Not found'); | ||
| }, | ||
| getEnvValue: (name) => process.env[name], | ||
| }); | ||
|
|
||
| await engine.initialize(); | ||
|
|
||
| return { | ||
| fetch: app.fetch, | ||
| ws: wsNode, | ||
| dispose: async () => { | ||
| wsNode.closeAll(); | ||
| }, | ||
| }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| import process from 'node:process'; | ||
| import path from 'node:path'; | ||
|
|
||
| import { serve } from '@hono/node-server'; | ||
| import crosswsNode from 'crossws/adapters/node'; | ||
| import type { Server } from 'node:http'; | ||
|
|
||
| import { initApp } from 'springboard/server/hono_app'; | ||
| import { makeWebsocketServerCoreDependenciesWithSqlite } from 'springboard/platforms/node/services/ws_server_core_dependencies'; | ||
| import { LocalJsonNodeKVStoreService } from 'springboard/platforms/node/services/node_kvstore_service'; | ||
| import { CoreDependencies, Springboard } from 'springboard/core'; | ||
| import '../src/server-entry.ts'; | ||
|
|
||
| /** | ||
| * Node.js server entrypoint with HMR support | ||
| * | ||
| * This file is generated by the Springboard Vite plugin and serves as the | ||
| * entry point for the Node.js dev server. It: | ||
| * | ||
| * 1. Imports the user's application entry (which registers modules) | ||
| * 2. Exports start/stop functions for lifecycle management | ||
| * 3. Supports HMR via import.meta.hot.dispose() | ||
| */ | ||
|
|
||
| let server: Server | null = null; | ||
| let engine: Springboard | null = null; | ||
|
|
||
| /** | ||
| * Start the node server | ||
| */ | ||
| export async function start() { | ||
| // If server is already running, stop it first | ||
| if (server) { | ||
| await stop(); | ||
| } | ||
|
|
||
| try { | ||
| const webappFolder = process.env.WEBAPP_FOLDER || './dist'; | ||
| const webappDistFolder = webappFolder; | ||
|
|
||
| const nodeKvDeps = await makeWebsocketServerCoreDependenciesWithSqlite(); | ||
| const useWebSocketsForRpc = import.meta.env.VITE_USE_WEBSOCKETS_FOR_RPC === 'true'; | ||
|
|
||
| let wsNode: ReturnType<typeof crosswsNode>; | ||
|
|
||
| const { app, serverAppDependencies, injectResources, createWebSocketHooks } = initApp({ | ||
| broadcastMessage: (message) => { | ||
| return wsNode.publish('event', message); | ||
| }, | ||
| remoteKV: nodeKvDeps.kvStoreFromKysely, | ||
| userAgentKV: new LocalJsonNodeKVStoreService('userAgent'), | ||
| }); | ||
|
|
||
| wsNode = crosswsNode({ | ||
| hooks: createWebSocketHooks(useWebSocketsForRpc), | ||
| }); | ||
|
|
||
| // Use configured port (ignores process.env.PORT to avoid conflicts) | ||
| const port = 1337; | ||
|
|
||
| // Start the HTTP server | ||
| server = serve({ | ||
| fetch: app.fetch, | ||
| port, | ||
| }, (info) => { | ||
| console.log(`Server listening on http://localhost:${info.port}`); | ||
| }); | ||
|
|
||
| server.on('upgrade', (request, socket, head) => { | ||
| const url = new URL(request.url || '', `http://${request.headers.host}`); | ||
| if (url.pathname === '/ws') { | ||
| wsNode.handleUpgrade(request, socket, head); | ||
| } else { | ||
| socket.end('HTTP/1.1 404 Not Found\r\n\r\n'); | ||
| } | ||
| }); | ||
|
|
||
| const coreDeps: CoreDependencies = { | ||
| log: console.log, | ||
| showError: console.error, | ||
| storage: serverAppDependencies.storage, | ||
| isMaestro: () => true, | ||
| rpc: serverAppDependencies.rpc, | ||
| }; | ||
|
|
||
| Object.assign(coreDeps, serverAppDependencies); | ||
|
|
||
| const extraDeps = {}; // TODO: remove this extraDeps thing from the framework | ||
|
|
||
| engine = new Springboard(coreDeps, extraDeps); | ||
|
|
||
| injectResources({ | ||
| engine, | ||
| serveStaticFile: async (c, fileName, headers) => { | ||
| try { | ||
| const fullPath = `${webappDistFolder}/${fileName}`; | ||
| const fs = await import('node:fs'); | ||
| const data = await fs.promises.readFile(fullPath, 'utf-8'); | ||
| c.status(200); | ||
|
|
||
| if (headers) { | ||
| Object.entries(headers).forEach(([key, value]) => { | ||
| c.header(key, value); | ||
| }); | ||
| } | ||
|
|
||
| return c.body(data); | ||
| } catch (error) { | ||
| console.error('Error serving file:', error); | ||
| c.status(404); | ||
| return c.text('404 Not found'); | ||
| } | ||
| }, | ||
| getEnvValue: name => process.env[name], | ||
| }); | ||
|
|
||
| await engine.initialize(); | ||
| console.log('Node application started successfully'); | ||
| } catch (error) { | ||
| console.error('Failed to start node server:', error); | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Stop the node server | ||
| */ | ||
| export async function stop() { | ||
| if (!server) { | ||
| return; | ||
| } | ||
|
|
||
| return new Promise<void>((resolve, reject) => { | ||
| const timeout = setTimeout(() => { | ||
| reject(new Error('Server close timeout')); | ||
| }, 5000); | ||
|
|
||
| server!.close((err) => { | ||
| clearTimeout(timeout); | ||
| if (err) { | ||
| reject(err); | ||
| } else { | ||
| console.log('Server stopped successfully'); | ||
| server = null; | ||
| engine = null; // TODO: add explicit shutdown once the engine exposes it | ||
| resolve(); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| // HMR support: clean up before module reload | ||
| if (import.meta.hot) { | ||
| import.meta.hot.dispose(async () => { | ||
| console.log('[HMR] Stopping server before reload...'); | ||
| await stop(); | ||
| }); | ||
| } | ||
|
|
||
| // Auto-start in production builds (not in dev mode) | ||
| if (!import.meta.env.DEV) { | ||
| start().catch(console.error); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
packages/jamtools/core/src/modules/io/io_dependencies.browser.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import {BrowserQwertyService} from '@jamtools/core/services/browser/browser_qwerty_service'; | ||
| import {BrowserMidiService} from '@jamtools/core/services/browser/browser_midi_service'; | ||
| import type {IoDeps} from './io_dependencies_types'; | ||
|
|
||
| export const createIoDependencies = async (): Promise<IoDeps> => { | ||
| const qwerty = new BrowserQwertyService(document); | ||
| const midi = new BrowserMidiService(); | ||
| return { | ||
| qwerty, | ||
| midi, | ||
| }; | ||
| }; |
21 changes: 21 additions & 0 deletions
21
packages/jamtools/core/src/modules/io/io_dependencies.node.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import {NodeQwertyService} from '@jamtools/core/services/node/node_qwerty_service'; | ||
| import {NodeMidiService} from '@jamtools/core/services/node/node_midi_service'; | ||
| import {MockMidiService} from '@jamtools/core/test/services/mock_midi_service'; | ||
| import {MockQwertyService} from '@jamtools/core/test/services/mock_qwerty_service'; | ||
| import type {IoDeps} from './io_dependencies_types'; | ||
|
|
||
| export const createIoDependencies = async (): Promise<IoDeps> => { | ||
| if (process.env.DISABLE_IO === 'true') { | ||
| return { | ||
| qwerty: new MockQwertyService(), | ||
| midi: new MockMidiService(), | ||
| }; | ||
| } | ||
|
|
||
| const qwerty = new NodeQwertyService(); | ||
| const midi = new NodeMidiService(); | ||
| return { | ||
| qwerty, | ||
| midi, | ||
| }; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import {MockMidiService} from '@jamtools/core/test/services/mock_midi_service'; | ||
| import {MockQwertyService} from '@jamtools/core/test/services/mock_qwerty_service'; | ||
| import type {IoDeps} from './io_dependencies_types'; | ||
|
|
||
| // Default implementation for testing | ||
| export const createIoDependencies = async (): Promise<IoDeps> => { | ||
| return { | ||
| qwerty: new MockQwertyService(), | ||
| midi: new MockMidiService(), | ||
| }; | ||
| }; |
8 changes: 8 additions & 0 deletions
8
packages/jamtools/core/src/modules/io/io_dependencies_types.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import {MidiService, QwertyService} from '@jamtools/core/types/io_types'; | ||
|
|
||
| export type IoDeps = { | ||
| midi: MidiService; | ||
| qwerty: QwertyService; | ||
| } | ||
|
|
||
| export type CreateIoDependencies = () => Promise<IoDeps>; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
./modules/io/io_dependenciesexport mapstypestoio_dependencies_types.d.ts, which defines only type aliases and does not declare thecreateIoDependenciesvalue export that the runtime files provide. TypeScript consumers importingcreateIoDependenciesfrom this subpath will get missing-export type errors even though the JS export exists.Useful? React with 👍 / 👎.