@belte/belte 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +313 -0
- package/LICENSE +21 -0
- package/README.md +559 -0
- package/bin/belte.ts +183 -0
- package/package.json +110 -0
- package/src/App.svelte +31 -0
- package/src/appEntry.ts +151 -0
- package/src/assets/app.html +14 -0
- package/src/belteResolverPlugin.ts +858 -0
- package/src/build.ts +147 -0
- package/src/buildCli.ts +129 -0
- package/src/buildDisconnected.ts +122 -0
- package/src/bundleApp.ts +149 -0
- package/src/bundleDisconnectedEntry.ts +17 -0
- package/src/cliEntry.ts +25 -0
- package/src/clientBuildPlugins.ts +41 -0
- package/src/clientEntry.ts +7 -0
- package/src/compile.ts +64 -0
- package/src/controlServerWorker.ts +422 -0
- package/src/dedupeSveltePlugin.ts +66 -0
- package/src/devEntry.ts +169 -0
- package/src/discoveryEntry.ts +81 -0
- package/src/lib/browser/applyStreamedResolution.ts +33 -0
- package/src/lib/browser/cacheEntryFromSnapshot.ts +48 -0
- package/src/lib/browser/flushUnresolvedPlaceholders.ts +16 -0
- package/src/lib/browser/installStreamingPlaceholders.ts +32 -0
- package/src/lib/browser/openResolveStream.ts +42 -0
- package/src/lib/browser/page.svelte.ts +258 -0
- package/src/lib/browser/pageStreamController.ts +17 -0
- package/src/lib/browser/refetchPlaceholder.ts +12 -0
- package/src/lib/browser/remoteProxy.ts +37 -0
- package/src/lib/browser/socketChannel.ts +192 -0
- package/src/lib/browser/socketProxy.ts +57 -0
- package/src/lib/browser/startClient.ts +153 -0
- package/src/lib/browser/subscribe.ts +131 -0
- package/src/lib/browser/types/Errors.ts +9 -0
- package/src/lib/browser/types/Layouts.ts +7 -0
- package/src/lib/browser/types/Pages.ts +7 -0
- package/src/lib/browser/types/StreamingDeferred.ts +9 -0
- package/src/lib/bundle/BundleMenu.ts +11 -0
- package/src/lib/bundle/BundleMenuItem.ts +24 -0
- package/src/lib/bundle/BundleWindow.ts +36 -0
- package/src/lib/bundle/WEBVIEW_BUILD_REVISION.ts +9 -0
- package/src/lib/bundle/WEBVIEW_VERSION.ts +7 -0
- package/src/lib/bundle/bindConnectedFlag.ts +29 -0
- package/src/lib/bundle/bindRequestNavigate.ts +31 -0
- package/src/lib/bundle/buildWebviewLib.ts +111 -0
- package/src/lib/bundle/disconnected.css +9 -0
- package/src/lib/bundle/disconnected.svelte +386 -0
- package/src/lib/bundle/ensureWebviewLib.ts +20 -0
- package/src/lib/bundle/exitWithParent.ts +28 -0
- package/src/lib/bundle/infoPlist.ts +46 -0
- package/src/lib/bundle/installDownloads.ts +24 -0
- package/src/lib/bundle/installMacMenu.ts +39 -0
- package/src/lib/bundle/listenLocalControlServer.ts +19 -0
- package/src/lib/bundle/native/belteMenu.mm +422 -0
- package/src/lib/bundle/native/webview.h +4557 -0
- package/src/lib/bundle/onMenu.ts +41 -0
- package/src/lib/bundle/openWebview.ts +104 -0
- package/src/lib/bundle/pngToIcns.ts +47 -0
- package/src/lib/bundle/probeBelteServer.ts +34 -0
- package/src/lib/bundle/resolveServerBinary.ts +12 -0
- package/src/lib/bundle/resolveWebviewLib.ts +53 -0
- package/src/lib/bundle/serverBinaryFilename.ts +8 -0
- package/src/lib/bundle/signMacApp.ts +35 -0
- package/src/lib/bundle/spawnEmbeddedServer.ts +65 -0
- package/src/lib/bundle/stableLocalPort.ts +19 -0
- package/src/lib/bundle/waitForServer.ts +23 -0
- package/src/lib/bundle/webviewCachePath.ts +23 -0
- package/src/lib/bundle/webviewLibName.ts +11 -0
- package/src/lib/cli/connectToServer.ts +23 -0
- package/src/lib/cli/createClient.ts +170 -0
- package/src/lib/cli/dispatchCommand.ts +71 -0
- package/src/lib/cli/loadEnvFromBinaryDir.ts +16 -0
- package/src/lib/cli/parseArgvForRpc.ts +97 -0
- package/src/lib/cli/printHelp.ts +119 -0
- package/src/lib/cli/printSessionHelp.ts +27 -0
- package/src/lib/cli/printSessionStatus.ts +21 -0
- package/src/lib/cli/printTrimmed.ts +8 -0
- package/src/lib/cli/printValue.ts +10 -0
- package/src/lib/cli/resolveCliTarget.ts +48 -0
- package/src/lib/cli/runCli.ts +139 -0
- package/src/lib/cli/runSession.ts +105 -0
- package/src/lib/cli/startLocalInstance.ts +14 -0
- package/src/lib/cli/tokenizeLine.ts +51 -0
- package/src/lib/cli/types/CliManifest.ts +9 -0
- package/src/lib/cli/types/CliManifestEntry.ts +17 -0
- package/src/lib/cli/types/CliTarget.ts +13 -0
- package/src/lib/mcp/annotationsForMethod.ts +29 -0
- package/src/lib/mcp/createMcpResourceServer.ts +101 -0
- package/src/lib/mcp/createMcpServer.ts +42 -0
- package/src/lib/mcp/dispatchMcpRequest.ts +146 -0
- package/src/lib/mcp/mcpResourceServerSlot.ts +18 -0
- package/src/lib/mcp/mcpSurface.ts +265 -0
- package/src/lib/mcp/toolResultFromResponse.ts +66 -0
- package/src/lib/mcp/types/JsonRpcRequest.ts +12 -0
- package/src/lib/mcp/types/JsonRpcResponse.ts +20 -0
- package/src/lib/mcp/types/McpResourceContents.ts +10 -0
- package/src/lib/mcp/types/McpResourceDescriptor.ts +6 -0
- package/src/lib/mcp/types/McpResourceServer.ts +12 -0
- package/src/lib/mcp/types/McpServer.ts +9 -0
- package/src/lib/mcp/types/McpServerOptions.ts +16 -0
- package/src/lib/server/AppModule.ts +33 -0
- package/src/lib/server/DELETE.ts +9 -0
- package/src/lib/server/GET.ts +9 -0
- package/src/lib/server/HEAD.ts +9 -0
- package/src/lib/server/PATCH.ts +9 -0
- package/src/lib/server/POST.ts +9 -0
- package/src/lib/server/PUT.ts +9 -0
- package/src/lib/server/agent.ts +76 -0
- package/src/lib/server/appDataDir.ts +15 -0
- package/src/lib/server/cli/buildEnvContent.ts +19 -0
- package/src/lib/server/cli/createTarGz.ts +76 -0
- package/src/lib/server/cli/handleCliDownload.ts +153 -0
- package/src/lib/server/cli/handleCliInstall.ts +37 -0
- package/src/lib/server/cli/installScript.ts +29 -0
- package/src/lib/server/cli/maxSourceMtime.ts +26 -0
- package/src/lib/server/cookies.ts +29 -0
- package/src/lib/server/env.ts +50 -0
- package/src/lib/server/error.ts +70 -0
- package/src/lib/server/json.ts +28 -0
- package/src/lib/server/jsonl.ts +46 -0
- package/src/lib/server/prompts/definePrompt.ts +20 -0
- package/src/lib/server/prompts/promptRegistry.ts +9 -0
- package/src/lib/server/prompts/registerPrompt.ts +6 -0
- package/src/lib/server/prompts/renderPromptTemplate.ts +16 -0
- package/src/lib/server/prompts/types/Prompt.ts +13 -0
- package/src/lib/server/prompts/types/PromptOptions.ts +12 -0
- package/src/lib/server/prompts/types/PromptRegistryEntry.ts +13 -0
- package/src/lib/server/prompts/types/PromptRoutes.ts +10 -0
- package/src/lib/server/redirect.ts +42 -0
- package/src/lib/server/request.ts +18 -0
- package/src/lib/server/rpc/defineVerb.ts +133 -0
- package/src/lib/server/rpc/dispatchVerbInProcess.ts +46 -0
- package/src/lib/server/rpc/findVerbByCommandName.ts +18 -0
- package/src/lib/server/rpc/parseArgs.ts +95 -0
- package/src/lib/server/rpc/registerVerb.ts +6 -0
- package/src/lib/server/rpc/types/RemoteHandler.ts +27 -0
- package/src/lib/server/rpc/types/RemoteRoutes.ts +13 -0
- package/src/lib/server/rpc/types/TypedResponse.ts +18 -0
- package/src/lib/server/rpc/types/VerbHelper.ts +68 -0
- package/src/lib/server/rpc/types/VerbRegistryEntry.ts +29 -0
- package/src/lib/server/rpc/unprocessed.ts +14 -0
- package/src/lib/server/rpc/verbRegistry.ts +11 -0
- package/src/lib/server/runtime/DEFAULT_PORT.ts +6 -0
- package/src/lib/server/runtime/DEV_REBUILD_MESSAGE.ts +4 -0
- package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +29 -0
- package/src/lib/server/runtime/acceptsZstd.ts +8 -0
- package/src/lib/server/runtime/buildOpenApiSpec.ts +106 -0
- package/src/lib/server/runtime/cacheControlForAsset.ts +22 -0
- package/src/lib/server/runtime/containsTraversal.ts +37 -0
- package/src/lib/server/runtime/createAssetHeaderCache.ts +35 -0
- package/src/lib/server/runtime/createPublicAssetServer.ts +63 -0
- package/src/lib/server/runtime/createRouteDispatcher.ts +100 -0
- package/src/lib/server/runtime/createServer.ts +692 -0
- package/src/lib/server/runtime/devReloadResponse.ts +35 -0
- package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +27 -0
- package/src/lib/server/runtime/envSchemaStore.ts +15 -0
- package/src/lib/server/runtime/findOpenPort.ts +35 -0
- package/src/lib/server/runtime/getActiveServer.ts +6 -0
- package/src/lib/server/runtime/globToPathSet.ts +29 -0
- package/src/lib/server/runtime/inProcessServer.ts +20 -0
- package/src/lib/server/runtime/internalErrorResponse.ts +25 -0
- package/src/lib/server/runtime/isCrossOriginUpgrade.ts +19 -0
- package/src/lib/server/runtime/listenOnOpenPort.ts +36 -0
- package/src/lib/server/runtime/logExposedSurfaces.ts +162 -0
- package/src/lib/server/runtime/mimeForExtension.ts +20 -0
- package/src/lib/server/runtime/parseIdleTimeout.ts +10 -0
- package/src/lib/server/runtime/parsePort.ts +11 -0
- package/src/lib/server/runtime/registryManifests.ts +66 -0
- package/src/lib/server/runtime/requestContext.ts +5 -0
- package/src/lib/server/runtime/resolveStreamResponse.ts +29 -0
- package/src/lib/server/runtime/runWithRequestScope.ts +57 -0
- package/src/lib/server/runtime/safeJsonForScript.ts +17 -0
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +45 -0
- package/src/lib/server/runtime/serverSlot.ts +13 -0
- package/src/lib/server/runtime/setActiveServer.ts +6 -0
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +81 -0
- package/src/lib/server/runtime/streamCacheResolutions.ts +37 -0
- package/src/lib/server/runtime/streamFromIterator.ts +86 -0
- package/src/lib/server/runtime/streamStash.ts +64 -0
- package/src/lib/server/runtime/types/Assets.ts +1 -0
- package/src/lib/server/runtime/types/RequestStore.ts +27 -0
- package/src/lib/server/runtime/withResponseDefaults.ts +24 -0
- package/src/lib/server/server.ts +32 -0
- package/src/lib/server/socket.ts +31 -0
- package/src/lib/server/sockets/createSocketDispatcher.ts +311 -0
- package/src/lib/server/sockets/defineSocket.ts +167 -0
- package/src/lib/server/sockets/lookupSocket.ts +6 -0
- package/src/lib/server/sockets/recentHistory.ts +11 -0
- package/src/lib/server/sockets/registerSocket.ts +6 -0
- package/src/lib/server/sockets/socketOperations.ts +35 -0
- package/src/lib/server/sockets/socketRegistry.ts +9 -0
- package/src/lib/server/sockets/types/Socket.ts +21 -0
- package/src/lib/server/sockets/types/SocketClientFrame.ts +18 -0
- package/src/lib/server/sockets/types/SocketOperation.ts +22 -0
- package/src/lib/server/sockets/types/SocketOptions.ts +22 -0
- package/src/lib/server/sockets/types/SocketRegistryEntry.ts +17 -0
- package/src/lib/server/sockets/types/SocketRoutes.ts +10 -0
- package/src/lib/server/sockets/types/SocketServerFrame.ts +15 -0
- package/src/lib/server/sse.ts +53 -0
- package/src/lib/shared/BELTE_PACKAGE_NAME.ts +7 -0
- package/src/lib/shared/CACHE_CONTROL_VALUES.ts +16 -0
- package/src/lib/shared/HttpError.ts +19 -0
- package/src/lib/shared/RESOLVE_STREAM_PATH.ts +7 -0
- package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +11 -0
- package/src/lib/shared/activeCacheStore.ts +20 -0
- package/src/lib/shared/appDataDir.ts +34 -0
- package/src/lib/shared/belteImportName.ts +44 -0
- package/src/lib/shared/browserClientFlags.ts +10 -0
- package/src/lib/shared/buildRpcRequest.ts +70 -0
- package/src/lib/shared/bundleLayout.ts +36 -0
- package/src/lib/shared/bundled.ts +34 -0
- package/src/lib/shared/cache.ts +559 -0
- package/src/lib/shared/cacheStoreSlot.ts +16 -0
- package/src/lib/shared/canonicalJson.ts +63 -0
- package/src/lib/shared/carriesBodyArgs.ts +13 -0
- package/src/lib/shared/clearLastConnection.ts +7 -0
- package/src/lib/shared/commandNameForUrl.ts +17 -0
- package/src/lib/shared/createCacheStore.ts +75 -0
- package/src/lib/shared/createPushIterator.ts +93 -0
- package/src/lib/shared/createRemoteFunction.ts +99 -0
- package/src/lib/shared/decodeResponse.ts +47 -0
- package/src/lib/shared/detectTarget.ts +27 -0
- package/src/lib/shared/exeSuffix.ts +9 -0
- package/src/lib/shared/exitOnBuildFailure.ts +17 -0
- package/src/lib/shared/extraForwardHeaders.ts +16 -0
- package/src/lib/shared/fileStem.ts +9 -0
- package/src/lib/shared/findExportCallSite.ts +479 -0
- package/src/lib/shared/forwardHeaders.ts +41 -0
- package/src/lib/shared/getRemoteMeta.ts +5 -0
- package/src/lib/shared/globalCacheStore.ts +15 -0
- package/src/lib/shared/globalCacheStoreSlot.ts +14 -0
- package/src/lib/shared/importNamesToStrip.ts +13 -0
- package/src/lib/shared/invalidateEvent.ts +11 -0
- package/src/lib/shared/isCompileTarget.ts +15 -0
- package/src/lib/shared/isDebugEnabled.ts +23 -0
- package/src/lib/shared/isModuleNotFound.ts +16 -0
- package/src/lib/shared/isReadOnlyMethod.ts +14 -0
- package/src/lib/shared/isStreamingResponse.ts +11 -0
- package/src/lib/shared/jsonSchemaForPromptArguments.ts +29 -0
- package/src/lib/shared/jsonSchemaForSchema.ts +32 -0
- package/src/lib/shared/jsonlErrorFrame.ts +24 -0
- package/src/lib/shared/keyForRemoteCall.ts +29 -0
- package/src/lib/shared/lastConnectionPath.ts +7 -0
- package/src/lib/shared/loadEnvFile.ts +17 -0
- package/src/lib/shared/loadEnvFromDataDir.ts +15 -0
- package/src/lib/shared/loadSvelteConfig.ts +18 -0
- package/src/lib/shared/log.ts +104 -0
- package/src/lib/shared/manifestModule.ts +39 -0
- package/src/lib/shared/memoizeByKey.ts +24 -0
- package/src/lib/shared/nearestLayoutPrefix.ts +36 -0
- package/src/lib/shared/normalizeTarget.ts +10 -0
- package/src/lib/shared/pageUrlForFile.ts +14 -0
- package/src/lib/shared/parseBoundedEnvInt.ts +20 -0
- package/src/lib/shared/parseEnv.ts +30 -0
- package/src/lib/shared/parsePromptMarkdown.ts +34 -0
- package/src/lib/shared/parseRouteSegments.ts +22 -0
- package/src/lib/shared/prepareRpcModule.ts +59 -0
- package/src/lib/shared/prepareSocketModule.ts +49 -0
- package/src/lib/shared/programNameForPackage.ts +14 -0
- package/src/lib/shared/promptNameForFile.ts +10 -0
- package/src/lib/shared/queryStringFromArgs.ts +27 -0
- package/src/lib/shared/readEnvFile.ts +15 -0
- package/src/lib/shared/readLastConnection.ts +18 -0
- package/src/lib/shared/readPackageJson.ts +9 -0
- package/src/lib/shared/recordRemoteMeta.ts +5 -0
- package/src/lib/shared/remoteMetaStore.ts +16 -0
- package/src/lib/shared/resolveClientFlags.ts +20 -0
- package/src/lib/shared/responseErrorText.ts +9 -0
- package/src/lib/shared/rpcUrlForFile.ts +19 -0
- package/src/lib/shared/runningAsStandaloneBinary.ts +13 -0
- package/src/lib/shared/serializeEnv.ts +18 -0
- package/src/lib/shared/setCacheStoreResolver.ts +6 -0
- package/src/lib/shared/setGlobalCacheStoreResolver.ts +6 -0
- package/src/lib/shared/socketNameForFile.ts +11 -0
- package/src/lib/shared/sseErrorFrame.ts +29 -0
- package/src/lib/shared/streamResponse.ts +169 -0
- package/src/lib/shared/stripImport.ts +27 -0
- package/src/lib/shared/subscribableFromResponse.ts +51 -0
- package/src/lib/shared/toBunRoutePattern.ts +28 -0
- package/src/lib/shared/types/CacheEntry.ts +63 -0
- package/src/lib/shared/types/CacheInvalidation.ts +9 -0
- package/src/lib/shared/types/CacheOptions.ts +33 -0
- package/src/lib/shared/types/CacheSnapshot.ts +16 -0
- package/src/lib/shared/types/CacheSnapshotEntry.ts +15 -0
- package/src/lib/shared/types/CacheStore.ts +32 -0
- package/src/lib/shared/types/ClientFlags.ts +11 -0
- package/src/lib/shared/types/CompileTarget.ts +6 -0
- package/src/lib/shared/types/HttpVerb.ts +1 -0
- package/src/lib/shared/types/LastConnection.ts +9 -0
- package/src/lib/shared/types/PromptArgument.ts +12 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +13 -0
- package/src/lib/shared/types/RemoteFunction.ts +42 -0
- package/src/lib/shared/types/StandardSchemaV1.ts +57 -0
- package/src/lib/shared/types/StreamedResolution.ts +10 -0
- package/src/lib/shared/types/StreamingPlaceholder.ts +13 -0
- package/src/lib/shared/types/Subscribable.ts +15 -0
- package/src/lib/shared/types/SvelteConfig.ts +5 -0
- package/src/lib/shared/withJsonSchema.ts +20 -0
- package/src/lib/shared/writeLastConnection.ts +13 -0
- package/src/lib/shared/writeRoutesDts.ts +67 -0
- package/src/lib/test/clearVerbRegistry.ts +11 -0
- package/src/lib/test/createTestClient.ts +78 -0
- package/src/preload.ts +20 -0
- package/src/scaffold.ts +92 -0
- package/src/serverBuildPlugins.ts +25 -0
- package/src/serverEntry.ts +94 -0
- package/src/sveltePlugin.ts +58 -0
- package/src/tailwindStylePreprocessor.ts +62 -0
- package/template/bunfig.toml +4 -0
- package/template/package.json +19 -0
- package/template/src/app.ts +23 -0
- package/template/src/browser/app.css +21 -0
- package/template/src/browser/app.html +24 -0
- package/template/src/browser/pages/about/page.svelte +5 -0
- package/template/src/browser/pages/layout.svelte +26 -0
- package/template/src/browser/pages/page.svelte +20 -0
- package/template/src/bundle/icon.png +0 -0
- package/template/src/cli/banner.txt +3 -0
- package/template/src/cli/footer.txt +1 -0
- package/template/src/server/config.ts +17 -0
- package/template/src/server/rpc/getHello.ts +35 -0
- package/template/svelte.config.js +12 -0
- package/template/tsconfig.json +18 -0
- package/tsconfig.app.json +16 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PromptArgument } from './types/PromptArgument.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Turns a markdown prompt's frontmatter `arguments` list into the JSON
|
|
5
|
+
Schema the MCP dispatcher advertises in `prompts/list` (top-level string
|
|
6
|
+
properties + a `required` array). Prompt arguments are always strings —
|
|
7
|
+
MCP fills them from model output — so every property is `{ type: 'string' }`.
|
|
8
|
+
Returns undefined for an argument-less prompt so the generated module
|
|
9
|
+
omits the field entirely.
|
|
10
|
+
*/
|
|
11
|
+
export function jsonSchemaForPromptArguments(
|
|
12
|
+
args: PromptArgument[],
|
|
13
|
+
): Record<string, unknown> | undefined {
|
|
14
|
+
if (args.length === 0) {
|
|
15
|
+
return undefined
|
|
16
|
+
}
|
|
17
|
+
const properties = Object.fromEntries(
|
|
18
|
+
args.map((arg) => [
|
|
19
|
+
arg.name,
|
|
20
|
+
{ type: 'string', ...(arg.description ? { description: arg.description } : {}) },
|
|
21
|
+
]),
|
|
22
|
+
)
|
|
23
|
+
const required = args.filter((arg) => arg.required).map((arg) => arg.name)
|
|
24
|
+
return {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties,
|
|
27
|
+
...(required.length > 0 ? { required } : {}),
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from './types/StandardSchemaV1.ts'
|
|
2
|
+
|
|
3
|
+
const OPAQUE = { type: 'object', additionalProperties: true } as const
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Resolves a JSON Schema for an MCP tool's `inputSchema`, an OpenAPI body, a CLI
|
|
7
|
+
flag set, or the bundle setup form. Probes the schema's own projection:
|
|
8
|
+
|
|
9
|
+
1. `schema.toJsonSchema()` (Arktype 2+)
|
|
10
|
+
2. `schema.toJSONSchema()` (Zod 4, Effect Schema, or a withJsonSchema wrap)
|
|
11
|
+
3. Opaque object — the surface still works, the consumer just gets no shape hint
|
|
12
|
+
|
|
13
|
+
Schemas whose library exposes neither carry one via withJsonSchema. Returns a
|
|
14
|
+
fresh object each call; callers can mutate (e.g. add a description) without
|
|
15
|
+
aliasing the schema's own.
|
|
16
|
+
*/
|
|
17
|
+
export function jsonSchemaForSchema(schema: StandardSchemaV1 | undefined): Record<string, unknown> {
|
|
18
|
+
if (!schema) {
|
|
19
|
+
return { ...OPAQUE }
|
|
20
|
+
}
|
|
21
|
+
const candidate = schema as unknown as {
|
|
22
|
+
toJsonSchema?: () => Record<string, unknown>
|
|
23
|
+
toJSONSchema?: () => Record<string, unknown>
|
|
24
|
+
}
|
|
25
|
+
if (typeof candidate.toJsonSchema === 'function') {
|
|
26
|
+
return { ...candidate.toJsonSchema() }
|
|
27
|
+
}
|
|
28
|
+
if (typeof candidate.toJSONSchema === 'function') {
|
|
29
|
+
return { ...candidate.toJSONSchema() }
|
|
30
|
+
}
|
|
31
|
+
return { ...OPAQUE }
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The in-band error sentinel for a JSONL/NDJSON stream: when a handler's
|
|
3
|
+
generator throws, `jsonl()` emits a final `{"$error":"<message>"}` line,
|
|
4
|
+
and `streamResponse` re-throws it on the consumer side. Encoder and decoder
|
|
5
|
+
live together here so the sentinel field has one definition and can't drift
|
|
6
|
+
between the two ends of the wire.
|
|
7
|
+
*/
|
|
8
|
+
export const jsonlErrorFrame = {
|
|
9
|
+
// Error line for a thrown message, including the trailing newline.
|
|
10
|
+
encode(message: string): string {
|
|
11
|
+
return `${JSON.stringify({ $error: message })}\n`
|
|
12
|
+
},
|
|
13
|
+
// The message carried by a parsed line, or undefined when it isn't the error sentinel.
|
|
14
|
+
decode(parsed: unknown): string | undefined {
|
|
15
|
+
if (
|
|
16
|
+
parsed &&
|
|
17
|
+
typeof parsed === 'object' &&
|
|
18
|
+
typeof (parsed as { $error?: unknown }).$error === 'string'
|
|
19
|
+
) {
|
|
20
|
+
return (parsed as { $error: string }).$error
|
|
21
|
+
}
|
|
22
|
+
return undefined
|
|
23
|
+
},
|
|
24
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { canonicalJson } from './canonicalJson.ts'
|
|
2
|
+
import { carriesBodyArgs } from './carriesBodyArgs.ts'
|
|
3
|
+
import { queryStringFromArgs } from './queryStringFromArgs.ts'
|
|
4
|
+
import type { HttpVerb } from './types/HttpVerb.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Derives a cache key from a verb-defined remote function and its args. The
|
|
8
|
+
prefix is `${method} ${url}` where `url` is the route template. GET/DELETE/HEAD
|
|
9
|
+
serialise args onto the URL as `?key=value` (sorted, via queryStringFromArgs —
|
|
10
|
+
the same encoder buildRpcRequest builds its query with, so the key and the
|
|
11
|
+
synthesized Request can't disagree); POST/PUT/PATCH join args after a space as
|
|
12
|
+
canonical JSON. The verb split mirrors buildRpcRequest exactly.
|
|
13
|
+
*/
|
|
14
|
+
export function keyForRemoteCall(method: HttpVerb, url: string, args: unknown): string {
|
|
15
|
+
const prefix = `${method} ${url}`
|
|
16
|
+
if (!carriesBodyArgs(method)) {
|
|
17
|
+
if (args && typeof args === 'object' && !Array.isArray(args)) {
|
|
18
|
+
const search = queryStringFromArgs(args as Record<string, unknown>, true)
|
|
19
|
+
if (search.length > 0) {
|
|
20
|
+
return `${prefix}?${search}`
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return prefix
|
|
24
|
+
}
|
|
25
|
+
if (args === undefined) {
|
|
26
|
+
return prefix
|
|
27
|
+
}
|
|
28
|
+
return `${prefix} ${canonicalJson(args)}`
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { appDataDir } from './appDataDir.ts'
|
|
3
|
+
|
|
4
|
+
// Path to the per-program last-connection record, beside the data-dir `.env`.
|
|
5
|
+
export function lastConnectionPath(programName: string): string {
|
|
6
|
+
return join(appDataDir(programName), 'last-connection.json')
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readEnvFile } from './readEnvFile.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Reads a `.env` at `path` and merges each declared var into `process.env`
|
|
5
|
+
only when not already set. Fill-when-unset is the precedence rule the whole
|
|
6
|
+
env stack relies on: layers loaded earlier (shell/ambient, Bun's CWD `.env`)
|
|
7
|
+
win, and later callers back-fill only what's still missing. So a value's
|
|
8
|
+
source is invisible to the app — it reads one flat `process.env` (Bun.env is
|
|
9
|
+
the same object). Missing file is a no-op.
|
|
10
|
+
*/
|
|
11
|
+
export async function loadEnvFile(path: string): Promise<void> {
|
|
12
|
+
for (const [key, value] of Object.entries(await readEnvFile(path))) {
|
|
13
|
+
if (process.env[key] === undefined) {
|
|
14
|
+
process.env[key] = value
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { appDataDir } from './appDataDir.ts'
|
|
3
|
+
import { loadEnvFile } from './loadEnvFile.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Loads the user's `.env` from the program's per-user data dir into `process.env`.
|
|
7
|
+
This is the cwd-independent config layer — where the connect-screen form writes
|
|
8
|
+
the user's answers, and where a bundle launched via `open` (cwd `/`, so Bun's
|
|
9
|
+
CWD `.env` autoload finds nothing) actually picks up its config. Loaded before
|
|
10
|
+
the binary-dir `.env`, so a user's saved config overrides the shipped default;
|
|
11
|
+
still loses to a shell export or a CWD `.env` (fill-when-unset, see loadEnvFile).
|
|
12
|
+
*/
|
|
13
|
+
export async function loadEnvFromDataDir(programName: string): Promise<void> {
|
|
14
|
+
await loadEnvFile(join(appDataDir(programName), '.env'))
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SvelteConfig } from './types/SvelteConfig.ts'
|
|
2
|
+
|
|
3
|
+
const EXTENSIONS = ['js', 'mjs', 'ts'] as const
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Looks for `svelte.config.{js,mjs,ts}` in `cwd` and returns its default export.
|
|
7
|
+
Falls back to an empty config if no file is found.
|
|
8
|
+
*/
|
|
9
|
+
export async function loadSvelteConfig(cwd: string = process.cwd()): Promise<SvelteConfig> {
|
|
10
|
+
for (const extension of EXTENSIONS) {
|
|
11
|
+
const path = `${cwd}/svelte.config.${extension}`
|
|
12
|
+
if (await Bun.file(path).exists()) {
|
|
13
|
+
const module = await import(path)
|
|
14
|
+
return module.default as SvelteConfig
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return {}
|
|
18
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { isDebugEnabled } from './isDebugEnabled.ts'
|
|
2
|
+
|
|
3
|
+
const hasBun = typeof Bun !== 'undefined'
|
|
4
|
+
const useColor = hasBun && Bun.enableANSIColors
|
|
5
|
+
const RESET = '\x1b[0m'
|
|
6
|
+
const BOLD = '\x1b[1m'
|
|
7
|
+
const DIM = '\x1b[2m'
|
|
8
|
+
|
|
9
|
+
// Wraps `text` in a Bun-resolved ANSI color escape; no-op when colors are disabled or unavailable (browser).
|
|
10
|
+
function paint(color: string, text: string): string {
|
|
11
|
+
if (!useColor) {
|
|
12
|
+
return text
|
|
13
|
+
}
|
|
14
|
+
return `${Bun.color(color, 'ansi-256')}${text}${RESET}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Applies the ANSI dim attribute; no-op when colors are disabled.
|
|
18
|
+
function dim(text: string): string {
|
|
19
|
+
if (!useColor) {
|
|
20
|
+
return text
|
|
21
|
+
}
|
|
22
|
+
return `${DIM}${text}${RESET}`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Prefers a full stack trace when the value is an Error so logs include the call site.
|
|
26
|
+
function formatError(value: unknown): string {
|
|
27
|
+
if (value instanceof Error) {
|
|
28
|
+
return value.stack ?? value.message
|
|
29
|
+
}
|
|
30
|
+
return String(value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Maps an HTTP status code to a color that matches the usual server-log convention.
|
|
34
|
+
function colorStatus(status: number): string {
|
|
35
|
+
if (status >= 500) {
|
|
36
|
+
return paint('red', String(status))
|
|
37
|
+
}
|
|
38
|
+
if (status >= 400) {
|
|
39
|
+
return paint('yellow', String(status))
|
|
40
|
+
}
|
|
41
|
+
if (status >= 300) {
|
|
42
|
+
return paint('cyan', String(status))
|
|
43
|
+
}
|
|
44
|
+
return paint('green', String(status))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Maps an HTTP method to a color so the request log line is easy to scan.
|
|
48
|
+
function colorMethod(method: string): string {
|
|
49
|
+
const upper = method.toUpperCase()
|
|
50
|
+
if (upper === 'GET') {
|
|
51
|
+
return paint('green', upper)
|
|
52
|
+
}
|
|
53
|
+
if (upper === 'POST') {
|
|
54
|
+
return paint('blue', upper)
|
|
55
|
+
}
|
|
56
|
+
if (upper === 'PUT' || upper === 'PATCH') {
|
|
57
|
+
return paint('yellow', upper)
|
|
58
|
+
}
|
|
59
|
+
if (upper === 'DELETE') {
|
|
60
|
+
return paint('red', upper)
|
|
61
|
+
}
|
|
62
|
+
return paint('magenta', upper)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const BELTE = useColor ? `${BOLD}${Bun.color('magenta', 'ansi-256')}[belte]${RESET}` : '[belte]'
|
|
66
|
+
|
|
67
|
+
// Browser console already has its own DEBUG storage convention, but for the shared logger
|
|
68
|
+
// we honor the same DEBUG env. In the browser `process.env.DEBUG` may not exist.
|
|
69
|
+
const debugEnv = typeof process !== 'undefined' ? process.env.DEBUG : undefined
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
Shared logger used by both the build pipeline and the request handler.
|
|
73
|
+
Wraps console.* with ANSI coloring, a `[belte]` prefix, and a per-method/
|
|
74
|
+
per-status palette for `request()`. console.* is the side effect — logging
|
|
75
|
+
is intentionally impure.
|
|
76
|
+
*/
|
|
77
|
+
export const log = {
|
|
78
|
+
info(message: string): void {
|
|
79
|
+
console.log(`${BELTE} ${message}`)
|
|
80
|
+
},
|
|
81
|
+
warn(message: string): void {
|
|
82
|
+
console.warn(`${BELTE} ${paint('yellow', message)}`)
|
|
83
|
+
},
|
|
84
|
+
error(value: unknown): void {
|
|
85
|
+
console.error(`${BELTE} ${paint('red', formatError(value))}`)
|
|
86
|
+
},
|
|
87
|
+
success(message: string): void {
|
|
88
|
+
console.log(`${BELTE} ${paint('green', message)}`)
|
|
89
|
+
},
|
|
90
|
+
detail(message: string): void {
|
|
91
|
+
console.log(dim(message))
|
|
92
|
+
},
|
|
93
|
+
debug(scope: string, message: string): void {
|
|
94
|
+
if (!isDebugEnabled(scope, debugEnv)) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
console.log(`${dim(`[${scope}]`)} ${dim(message)}`)
|
|
98
|
+
},
|
|
99
|
+
request(method: string, path: string, status: number, durationMs: number): void {
|
|
100
|
+
console.log(
|
|
101
|
+
`${colorMethod(method)} ${path} ${colorStatus(status)} ${dim(`${durationMs.toFixed(2)}ms`)}`,
|
|
102
|
+
)
|
|
103
|
+
},
|
|
104
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { log } from './log.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Builds one of belte's virtual manifest modules — the `{ key: () => import(...) }`
|
|
5
|
+
map the bundler emits for rpc / sockets / prompts / pages / layouts. They differ
|
|
6
|
+
only in their files, the key derived per file, the import dir, the export name,
|
|
7
|
+
and the log label; this is the single shape they share.
|
|
8
|
+
*/
|
|
9
|
+
export function manifestModule(options: {
|
|
10
|
+
files: string[]
|
|
11
|
+
keyForFile: (file: string) => string
|
|
12
|
+
importDir: string
|
|
13
|
+
exportName: string
|
|
14
|
+
/*
|
|
15
|
+
A plain `resolved N <label>: keys` line at build time, when set. Omitted for
|
|
16
|
+
manifests whose contents are enumerated once at boot as an aligned table (see
|
|
17
|
+
logExposedSurfaces) — rpc/sockets/pages/layouts — which both avoids the
|
|
18
|
+
redundant list and the double-log of manifests (pages/layouts) loaded by both
|
|
19
|
+
the server and client bundles. prompts/errors have no table, so they pass a
|
|
20
|
+
label to keep their build-time line.
|
|
21
|
+
*/
|
|
22
|
+
label?: string
|
|
23
|
+
}): { contents: string; loader: 'js' } {
|
|
24
|
+
const entries = options.files
|
|
25
|
+
.toSorted()
|
|
26
|
+
.map((file) => ({ key: options.keyForFile(file), file }))
|
|
27
|
+
const lines = entries
|
|
28
|
+
.map(
|
|
29
|
+
({ key, file }) =>
|
|
30
|
+
` ${JSON.stringify(key)}: () => import(${JSON.stringify(`${options.importDir}/${file}`)}),`,
|
|
31
|
+
)
|
|
32
|
+
.join('\n')
|
|
33
|
+
if (options.label && entries.length > 0) {
|
|
34
|
+
log.info(
|
|
35
|
+
`resolved ${entries.length} ${options.label}: ${entries.map((entry) => entry.key).join(', ')}`,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
return { contents: `export const ${options.exportName} = {\n${lines}\n}\n`, loader: 'js' }
|
|
39
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Memoises an async loader keyed by string: the first call for a key starts the
|
|
3
|
+
load and caches its promise; later calls reuse it. `load` returns undefined when
|
|
4
|
+
the key has no loader, which is passed through (and not cached) so the caller can
|
|
5
|
+
treat "unknown key" distinctly from "loaded value". Used by the rpc-module and
|
|
6
|
+
socket-module load caches, which share this exact shape.
|
|
7
|
+
*/
|
|
8
|
+
export function memoizeByKey<T>(
|
|
9
|
+
load: (key: string) => Promise<T> | undefined,
|
|
10
|
+
): (key: string) => Promise<T> | undefined {
|
|
11
|
+
const cache = new Map<string, Promise<T>>()
|
|
12
|
+
return (key) => {
|
|
13
|
+
const existing = cache.get(key)
|
|
14
|
+
if (existing) {
|
|
15
|
+
return existing
|
|
16
|
+
}
|
|
17
|
+
const started = load(key)
|
|
18
|
+
if (!started) {
|
|
19
|
+
return undefined
|
|
20
|
+
}
|
|
21
|
+
cache.set(key, started)
|
|
22
|
+
return started
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Given a route URL and a list of directory prefixes that have a
|
|
3
|
+
layout.svelte, returns the deepest prefix that is an ancestor of the route.
|
|
4
|
+
Returns undefined when no layout applies. Implements the "nearest-only"
|
|
5
|
+
resolution from the plan — no stacking.
|
|
6
|
+
*/
|
|
7
|
+
export type NormalizedLayoutPrefix = {
|
|
8
|
+
prefix: string
|
|
9
|
+
dir: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function normalizeLayoutPrefixes(prefixes: Iterable<string>): NormalizedLayoutPrefix[] {
|
|
13
|
+
const out: NormalizedLayoutPrefix[] = []
|
|
14
|
+
for (const prefix of prefixes) {
|
|
15
|
+
out.push({ prefix, dir: prefix === '/' ? '' : prefix.replace(/^\//, '') })
|
|
16
|
+
}
|
|
17
|
+
return out
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function nearestLayoutPrefix(
|
|
21
|
+
routeUrl: string,
|
|
22
|
+
layoutPrefixes: NormalizedLayoutPrefix[],
|
|
23
|
+
): string | undefined {
|
|
24
|
+
const normalized = routeUrl === '/' ? '' : routeUrl.replace(/^\//, '')
|
|
25
|
+
let best: string | undefined
|
|
26
|
+
let bestLen = -1
|
|
27
|
+
for (const { prefix, dir } of layoutPrefixes) {
|
|
28
|
+
if (dir === '' || normalized === dir || normalized.startsWith(`${dir}/`)) {
|
|
29
|
+
if (dir.length > bestLen) {
|
|
30
|
+
best = prefix
|
|
31
|
+
bestLen = dir.length
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return best
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CompileTarget } from './types/CompileTarget.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Prepends the `bun-` prefix if missing so CLI users can pass either
|
|
5
|
+
`darwin-arm64` or the canonical `bun-darwin-arm64` form to `--target`.
|
|
6
|
+
*/
|
|
7
|
+
export function normalizeTarget(input: string): CompileTarget {
|
|
8
|
+
const normalized = input.startsWith('bun-') ? input : `bun-${input}`
|
|
9
|
+
return normalized as CompileTarget
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Maps a page-relative path (under `src/browser/pages/`) to its URL route. Pages are
|
|
3
|
+
folder-based: every leaf is `page.svelte` or `layout.svelte`, and the URL
|
|
4
|
+
is the directory path. Pages mount at the directory path; layouts mount at
|
|
5
|
+
the directory prefix. Dynamic segments keep their `[name]` / `[...rest]`
|
|
6
|
+
shape — translation to Bun's `:name` / `*` happens at server registration
|
|
7
|
+
via toBunRoutePattern; consumers see the readable form in `nav.route`.
|
|
8
|
+
*/
|
|
9
|
+
export function pageUrlForFile(relPath: string): string {
|
|
10
|
+
const segments = relPath.split('/')
|
|
11
|
+
segments.pop()
|
|
12
|
+
const path = segments.filter(Boolean).join('/')
|
|
13
|
+
return path === '' ? '/' : `/${path}`
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Parses an env string into an integer within [min, max], returning undefined for
|
|
3
|
+
missing, empty, or out-of-range/non-integer input so the caller can fall back to
|
|
4
|
+
a default. A bare Number() turns '' into 0 and 'abc' into NaN, both silently
|
|
5
|
+
wrong; this rejects them instead.
|
|
6
|
+
*/
|
|
7
|
+
export function parseBoundedEnvInt(
|
|
8
|
+
value: string | undefined,
|
|
9
|
+
min: number,
|
|
10
|
+
max: number,
|
|
11
|
+
): number | undefined {
|
|
12
|
+
if (value === undefined || value.trim() === '') {
|
|
13
|
+
return undefined
|
|
14
|
+
}
|
|
15
|
+
const parsed = Number(value)
|
|
16
|
+
if (!Number.isInteger(parsed) || parsed < min || parsed > max) {
|
|
17
|
+
return undefined
|
|
18
|
+
}
|
|
19
|
+
return parsed
|
|
20
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const ENV_LINE = /^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Parses `.env` text into a key→value record. Skips blanks, comments, and
|
|
5
|
+
malformed lines; strips a single layer of surrounding single or double quotes.
|
|
6
|
+
Intentionally minimal — no variable expansion, escapes, or multi-line. The pure
|
|
7
|
+
counterpart to loadEnvFile (which merges into process.env) and serializeEnv
|
|
8
|
+
(which writes records back), so all three round-trip the same shape.
|
|
9
|
+
*/
|
|
10
|
+
export function parseEnv(text: string): Record<string, string> {
|
|
11
|
+
const result: Record<string, string> = {}
|
|
12
|
+
for (const line of text.split('\n')) {
|
|
13
|
+
if (!line || line.startsWith('#')) {
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
16
|
+
const match = ENV_LINE.exec(line)
|
|
17
|
+
if (!match) {
|
|
18
|
+
continue
|
|
19
|
+
}
|
|
20
|
+
const [, key, rawValue] = match
|
|
21
|
+
const trimmed = rawValue?.trim() ?? ''
|
|
22
|
+
const unquoted =
|
|
23
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
24
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
25
|
+
? trimmed.slice(1, -1)
|
|
26
|
+
: trimmed
|
|
27
|
+
result[key as string] = unquoted
|
|
28
|
+
}
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { PromptArgument } from './types/PromptArgument.ts'
|
|
2
|
+
|
|
3
|
+
export type ParsedPromptMarkdown = {
|
|
4
|
+
description: string | undefined
|
|
5
|
+
arguments: PromptArgument[]
|
|
6
|
+
body: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Leading YAML frontmatter block fenced by `---` lines (CRLF tolerant).
|
|
10
|
+
const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
Splits a `src/mcp/prompts/**.md` file into its frontmatter metadata and
|
|
14
|
+
template body. The frontmatter (optional) carries `description` and an
|
|
15
|
+
`arguments` list; everything after the closing `---` is the prompt body,
|
|
16
|
+
interpolated at render time via `{{name}}` placeholders. A file with no
|
|
17
|
+
frontmatter is all body. Parsed with Bun.YAML — the resolver plugin runs
|
|
18
|
+
under Bun, so the native parser is always available at build time.
|
|
19
|
+
*/
|
|
20
|
+
export function parsePromptMarkdown(source: string): ParsedPromptMarkdown {
|
|
21
|
+
const match = FRONTMATTER.exec(source)
|
|
22
|
+
if (!match) {
|
|
23
|
+
return { description: undefined, arguments: [], body: source.trim() }
|
|
24
|
+
}
|
|
25
|
+
const frontmatter = (Bun.YAML.parse(match[1]) ?? {}) as {
|
|
26
|
+
description?: string
|
|
27
|
+
arguments?: PromptArgument[]
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
description: frontmatter.description,
|
|
31
|
+
arguments: Array.isArray(frontmatter.arguments) ? frontmatter.arguments : [],
|
|
32
|
+
body: source.slice(match[0].length).trim(),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type RouteSegment =
|
|
2
|
+
| { kind: 'literal'; value: string }
|
|
3
|
+
| { kind: 'param'; name: string; catchAll: boolean }
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Splits a belte route URL into typed segments. `[name]` becomes a param,
|
|
7
|
+
`[...rest]` becomes a catch-all param, anything else is a literal. Used
|
|
8
|
+
by toBunRoutePattern (server-side Bun pattern emission) and writeRoutesDts
|
|
9
|
+
(client-side `Routes` type augmentation) so the two consumers can't drift
|
|
10
|
+
on what counts as a param.
|
|
11
|
+
*/
|
|
12
|
+
export function parseRouteSegments(routeUrl: string): RouteSegment[] {
|
|
13
|
+
return routeUrl.split('/').map((segment) => {
|
|
14
|
+
if (segment.startsWith('[...') && segment.endsWith(']')) {
|
|
15
|
+
return { kind: 'param', name: segment.slice(4, -1), catchAll: true }
|
|
16
|
+
}
|
|
17
|
+
if (segment.startsWith('[') && segment.endsWith(']')) {
|
|
18
|
+
return { kind: 'param', name: segment.slice(1, -1), catchAll: false }
|
|
19
|
+
}
|
|
20
|
+
return { kind: 'literal', value: segment }
|
|
21
|
+
})
|
|
22
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { findExportCallSite } from './findExportCallSite.ts'
|
|
2
|
+
import { importNamesToStrip } from './importNamesToStrip.ts'
|
|
3
|
+
import { stripImport } from './stripImport.ts'
|
|
4
|
+
import type { HttpVerb } from './types/HttpVerb.ts'
|
|
5
|
+
|
|
6
|
+
const VERB_NAMES = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'] as const
|
|
7
|
+
const VERB_SET = new Set<string>(VERB_NAMES)
|
|
8
|
+
|
|
9
|
+
const SINGLE_EXPORT_ERROR =
|
|
10
|
+
'[belte] $rpc module contains more than one `<VERB>(...)` export — each file must declare exactly one remote function'
|
|
11
|
+
|
|
12
|
+
export type PreparedRpcModule = {
|
|
13
|
+
verb: HttpVerb
|
|
14
|
+
exportName: string
|
|
15
|
+
rewriteForServer: (url: string) => string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
Scans an `$rpc/**` module once and returns its declared verb + export
|
|
20
|
+
name plus a closure that, given the route URL, emits the server-side
|
|
21
|
+
rewrite (`__belteDefineVerb__("VERB", "<url>", … )` spliced into the
|
|
22
|
+
original source). The single scan replaces the prior separate
|
|
23
|
+
extract + rewrite passes, so the resolver plugin only walks each source
|
|
24
|
+
character-by-character once.
|
|
25
|
+
|
|
26
|
+
A regex pass would be tidier but it can't tell a `GET` mention inside a
|
|
27
|
+
docstring or template literal from the real call, and it can't follow
|
|
28
|
+
nested generics like `GET<Map<K, V>>(`.
|
|
29
|
+
*/
|
|
30
|
+
export function prepareRpcModule(
|
|
31
|
+
source: string,
|
|
32
|
+
importName: string,
|
|
33
|
+
): PreparedRpcModule | undefined {
|
|
34
|
+
/*
|
|
35
|
+
The "no barrels" surface places each verb at its own path
|
|
36
|
+
(`belte/server/GET`, `belte/server/POST`, …) — strip every one so
|
|
37
|
+
the user's verb import doesn't linger and side-effect-load the
|
|
38
|
+
stub module into the server bundle. The user may import under the
|
|
39
|
+
project's chosen name or the canonical package name, so strip both.
|
|
40
|
+
*/
|
|
41
|
+
const stripped = importNamesToStrip(importName).reduce(
|
|
42
|
+
(current, name) =>
|
|
43
|
+
VERB_NAMES.reduce((acc, verb) => stripImport(acc, `${name}/server/${verb}`), current),
|
|
44
|
+
source,
|
|
45
|
+
)
|
|
46
|
+
const site = findExportCallSite(stripped, (ident) => VERB_SET.has(ident), SINGLE_EXPORT_ERROR)
|
|
47
|
+
if (!site) {
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
50
|
+
const verb = site.ident as HttpVerb
|
|
51
|
+
return {
|
|
52
|
+
verb,
|
|
53
|
+
exportName: site.exportName,
|
|
54
|
+
rewriteForServer(url: string): string {
|
|
55
|
+
const binding = `__belteDefineVerb__(${JSON.stringify(verb)}, ${JSON.stringify(url)}, `
|
|
56
|
+
return stripped.slice(0, site.callStart) + binding + stripped.slice(site.parenStart + 1)
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { findExportCallSite } from './findExportCallSite.ts'
|
|
2
|
+
import { importNamesToStrip } from './importNamesToStrip.ts'
|
|
3
|
+
import { stripImport } from './stripImport.ts'
|
|
4
|
+
|
|
5
|
+
const SINGLE_EXPORT_ERROR =
|
|
6
|
+
'[belte] $sockets module contains more than one `socket(...)` export — each file must declare exactly one socket'
|
|
7
|
+
|
|
8
|
+
export type PreparedSocketModule = {
|
|
9
|
+
exportName: string
|
|
10
|
+
rewriteForServer: (name: string) => string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
Scans a `$sockets/**` module once and returns its declared export name
|
|
15
|
+
plus a closure that, given the socket name, emits the server-side
|
|
16
|
+
rewrite (`__belteDefineSocket__("<name>"[, opts])` spliced into the
|
|
17
|
+
original source). The single scan replaces the prior separate
|
|
18
|
+
extract + rewrite passes, so the resolver plugin only walks each source
|
|
19
|
+
character-by-character once.
|
|
20
|
+
*/
|
|
21
|
+
export function prepareSocketModule(
|
|
22
|
+
source: string,
|
|
23
|
+
importName: string,
|
|
24
|
+
): PreparedSocketModule | undefined {
|
|
25
|
+
/*
|
|
26
|
+
Strip the user's `socket` import under the project's chosen name and the
|
|
27
|
+
canonical package name so the dead import can't side-effect-load the
|
|
28
|
+
socket helper into the server bundle.
|
|
29
|
+
*/
|
|
30
|
+
const stripped = importNamesToStrip(importName).reduce(
|
|
31
|
+
(current, name) => stripImport(current, `${name}/server/socket`),
|
|
32
|
+
source,
|
|
33
|
+
)
|
|
34
|
+
const site = findExportCallSite(stripped, (ident) => ident === 'socket', SINGLE_EXPORT_ERROR)
|
|
35
|
+
if (!site) {
|
|
36
|
+
return undefined
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
exportName: site.exportName,
|
|
40
|
+
rewriteForServer(name: string): string {
|
|
41
|
+
const inner = stripped.slice(site.parenStart + 1, site.parenEnd).trim()
|
|
42
|
+
const binding =
|
|
43
|
+
inner.length === 0
|
|
44
|
+
? `__belteDefineSocket__(${JSON.stringify(name)})`
|
|
45
|
+
: `__belteDefineSocket__(${JSON.stringify(name)}, ${stripped.slice(site.parenStart + 1, site.parenEnd)})`
|
|
46
|
+
return stripped.slice(0, site.callStart) + binding + stripped.slice(site.parenEnd + 1)
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Derives the CLI program/binary name from a package.json `name` field.
|
|
3
|
+
Scoped names (`@scope/tool`) keep only the final segment so the value is
|
|
4
|
+
safe as a filesystem path, tar entry name, and CLI display name — a raw
|
|
5
|
+
`/` would otherwise nest the binary into an unexpected directory and break
|
|
6
|
+
the `/__belte/cli/<platform>` download route's path lookup. Falls back to
|
|
7
|
+
`app` when the name is absent or empty.
|
|
8
|
+
*/
|
|
9
|
+
export function programNameForPackage(name: string | undefined): string {
|
|
10
|
+
if (name === undefined || name === '') {
|
|
11
|
+
return 'app'
|
|
12
|
+
}
|
|
13
|
+
return name.split('/').pop() || 'app'
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Translates a prompt file path under `src/mcp/prompts/` into the prompt's
|
|
3
|
+
MCP name. Strips `.md` and joins nested folder segments with `-` (e.g.
|
|
4
|
+
`code/review.md` → `code-review`) so two prompts with the same stem in
|
|
5
|
+
different folders don't collide and the name stays a single valid MCP
|
|
6
|
+
prompt identifier.
|
|
7
|
+
*/
|
|
8
|
+
export function promptNameForFile(relativePath: string): string {
|
|
9
|
+
return relativePath.replace(/\.md$/, '').replaceAll('/', '-')
|
|
10
|
+
}
|