@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,106 @@
|
|
|
1
|
+
import { carriesBodyArgs } from '../../shared/carriesBodyArgs.ts'
|
|
2
|
+
import { commandNameForUrl } from '../../shared/commandNameForUrl.ts'
|
|
3
|
+
import { jsonSchemaForSchema } from '../../shared/jsonSchemaForSchema.ts'
|
|
4
|
+
import { verbRegistry } from '../rpc/verbRegistry.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Turns a verb's resolved JSON Schema into OpenAPI query parameters — one
|
|
8
|
+
per top-level property, marked required when the schema lists it. Used
|
|
9
|
+
for GET/DELETE/HEAD operations, which carry their args on the query
|
|
10
|
+
string (mirroring buildRpcRequest).
|
|
11
|
+
*/
|
|
12
|
+
function queryParameters(jsonSchema: Record<string, unknown>): Array<Record<string, unknown>> {
|
|
13
|
+
const properties = jsonSchema.properties as Record<string, unknown> | undefined
|
|
14
|
+
if (!properties) {
|
|
15
|
+
return []
|
|
16
|
+
}
|
|
17
|
+
const required = new Set((jsonSchema.required as string[] | undefined) ?? [])
|
|
18
|
+
return Object.entries(properties).map(([name, schema]) => ({
|
|
19
|
+
name,
|
|
20
|
+
in: 'query',
|
|
21
|
+
required: required.has(name),
|
|
22
|
+
schema,
|
|
23
|
+
}))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
Request body schema for a multipart upload verb: the text fields from
|
|
28
|
+
inputSchema, plus the binary parts. A File has no honest
|
|
29
|
+
Standard-Schema→JSON-Schema conversion, so the file parts are advertised
|
|
30
|
+
generically as additional binary properties rather than named per field.
|
|
31
|
+
*/
|
|
32
|
+
function multipartBodySchema(textSchema: Record<string, unknown>): Record<string, unknown> {
|
|
33
|
+
const textProperties = (textSchema.properties as Record<string, unknown> | undefined) ?? {}
|
|
34
|
+
const schema: Record<string, unknown> = {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: { ...textProperties },
|
|
37
|
+
additionalProperties: { type: 'string', format: 'binary' },
|
|
38
|
+
}
|
|
39
|
+
const required = (textSchema.required as string[] | undefined) ?? []
|
|
40
|
+
if (required.length > 0) {
|
|
41
|
+
schema.required = required
|
|
42
|
+
}
|
|
43
|
+
return schema
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
Builds an OpenAPI 3.1 document from the verb registry — the HTTP surface
|
|
48
|
+
every rpc exposes regardless of which non-browser clients it advertises.
|
|
49
|
+
GET/DELETE/HEAD args become query parameters; POST/PUT/PATCH args become
|
|
50
|
+
a JSON request body. operationId is the folder-prefixed command name so
|
|
51
|
+
it lines up with the MCP tool / CLI subcommand identifiers.
|
|
52
|
+
*/
|
|
53
|
+
export function buildOpenApiSpec(info: {
|
|
54
|
+
title: string
|
|
55
|
+
version: string
|
|
56
|
+
}): Record<string, unknown> {
|
|
57
|
+
const paths: Record<string, Record<string, unknown>> = {}
|
|
58
|
+
for (const entry of verbRegistry.values()) {
|
|
59
|
+
const url = entry.remote.url
|
|
60
|
+
const method = entry.remote.method
|
|
61
|
+
const jsonSchema = jsonSchemaForSchema(entry.inputSchema)
|
|
62
|
+
const description = jsonSchema.description as string | undefined
|
|
63
|
+
/*
|
|
64
|
+
When the verb declares an `outputSchema`, describe the 200 body
|
|
65
|
+
with it so external tooling sees the real return shape; otherwise
|
|
66
|
+
fall back to a bare OK.
|
|
67
|
+
*/
|
|
68
|
+
const okResponse: Record<string, unknown> = { description: 'OK' }
|
|
69
|
+
if (entry.outputSchema) {
|
|
70
|
+
okResponse.content = {
|
|
71
|
+
'application/json': {
|
|
72
|
+
schema: jsonSchemaForSchema(entry.outputSchema),
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const operation: Record<string, unknown> = {
|
|
77
|
+
operationId: commandNameForUrl(url),
|
|
78
|
+
...(description ? { description } : {}),
|
|
79
|
+
responses: { '200': okResponse },
|
|
80
|
+
}
|
|
81
|
+
if (carriesBodyArgs(method)) {
|
|
82
|
+
operation.requestBody = entry.filesSchema
|
|
83
|
+
? {
|
|
84
|
+
content: {
|
|
85
|
+
'multipart/form-data': {
|
|
86
|
+
schema: multipartBodySchema(jsonSchema),
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
: { content: { 'application/json': { schema: jsonSchema } } }
|
|
91
|
+
} else {
|
|
92
|
+
const parameters = queryParameters(jsonSchema)
|
|
93
|
+
if (parameters.length > 0) {
|
|
94
|
+
operation.parameters = parameters
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
paths[url] ??= {}
|
|
98
|
+
const path = paths[url]
|
|
99
|
+
path[method.toLowerCase()] = operation
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
openapi: '3.1.0',
|
|
103
|
+
info: { title: info.title, version: info.version },
|
|
104
|
+
paths,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IMMUTABLE_ASSET_CACHE_CONTROL,
|
|
3
|
+
REVALIDATE_ASSET_CACHE_CONTROL,
|
|
4
|
+
} from '../../shared/CACHE_CONTROL_VALUES.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Bun.build emits `[name]-[hash].[ext]` for chunks; hash is alnum and >=8 chars.
|
|
8
|
+
Source maps inherit the same name (e.g. foo-abc12345.js.map), so the suffix may be `.map`.
|
|
9
|
+
*/
|
|
10
|
+
const HASHED = /-[a-z0-9]{8,}\.[a-z0-9]+(\.map)?$/i
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
Returns the right `Cache-Control` for an asset path served under `/_app/`.
|
|
14
|
+
Hashed chunk filenames are content-addressed and immutable; everything else
|
|
15
|
+
(the entry bundle, the html shell, etc.) must revalidate every time.
|
|
16
|
+
*/
|
|
17
|
+
export function cacheControlForAsset(pathname: string): string {
|
|
18
|
+
if (HASHED.test(pathname)) {
|
|
19
|
+
return IMMUTABLE_ASSET_CACHE_CONTROL
|
|
20
|
+
}
|
|
21
|
+
return REVALIDATE_ASSET_CACHE_CONTROL
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Inspects the raw request URL (not the parsed pathname) for path-traversal
|
|
3
|
+
patterns. The WHATWG URL parser decodes `%2E%2E` to `..` and then collapses
|
|
4
|
+
dot-segments out of the pathname during normalization, so by the time
|
|
5
|
+
`url.pathname` is observable any encoded traversal has been masked. The
|
|
6
|
+
remaining literal `..` check guards against any future URL-parser quirk
|
|
7
|
+
that lets a normalised path through.
|
|
8
|
+
|
|
9
|
+
Hot path early-out: if none of the suspect substrings appear in the raw
|
|
10
|
+
URL we never lowercase the whole string nor walk segments.
|
|
11
|
+
*/
|
|
12
|
+
export function containsTraversal(rawUrl: string): boolean {
|
|
13
|
+
if (rawUrl.includes('\\')) {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
if (rawUrl.includes('..') && segmentContainsDotDot(rawUrl)) {
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
if (rawUrl.indexOf('%') === -1) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
const lower = rawUrl.toLowerCase()
|
|
23
|
+
return lower.includes('%2e%2e') || lower.includes('%2f') || lower.includes('%5c')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function segmentContainsDotDot(rawUrl: string): boolean {
|
|
27
|
+
const queryStart = rawUrl.indexOf('?')
|
|
28
|
+
const pathEnd = queryStart === -1 ? rawUrl.length : queryStart
|
|
29
|
+
const pathStart = rawUrl.indexOf('/', rawUrl.indexOf('://') + 3)
|
|
30
|
+
if (pathStart === -1 || pathStart >= pathEnd) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
return rawUrl
|
|
34
|
+
.slice(pathStart, pathEnd)
|
|
35
|
+
.split('/')
|
|
36
|
+
.some((segment) => segment === '..')
|
|
37
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mimeForExtension } from './mimeForExtension.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
A static-asset response's headers depend only on its pathname (extension →
|
|
5
|
+
Content-Type, path → Cache-Control), so each distinct pathname's header bundle
|
|
6
|
+
is built once and reused across every hit on that chunk — avoiding a per-request
|
|
7
|
+
allocation on a cold page load that pulls dozens of files. Each bundle carries
|
|
8
|
+
the plain `base` headers plus a `zstd` variant with `Content-Encoding: zstd`.
|
|
9
|
+
`cacheControlFor` lets callers vary the policy: hashed-aware for `/_app/`,
|
|
10
|
+
fixed for public/.
|
|
11
|
+
*/
|
|
12
|
+
type AssetHeaderBundle = {
|
|
13
|
+
base: HeadersInit
|
|
14
|
+
zstd: HeadersInit
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createAssetHeaderCache(
|
|
18
|
+
cacheControlFor: (pathname: string) => string,
|
|
19
|
+
): (pathname: string) => AssetHeaderBundle {
|
|
20
|
+
const cache = new Map<string, AssetHeaderBundle>()
|
|
21
|
+
return function headersFor(pathname) {
|
|
22
|
+
const cached = cache.get(pathname)
|
|
23
|
+
if (cached) {
|
|
24
|
+
return cached
|
|
25
|
+
}
|
|
26
|
+
const base: HeadersInit = {
|
|
27
|
+
'Content-Type': mimeForExtension(pathname),
|
|
28
|
+
Vary: 'Accept-Encoding',
|
|
29
|
+
'Cache-Control': cacheControlFor(pathname),
|
|
30
|
+
}
|
|
31
|
+
const bundle: AssetHeaderBundle = { base, zstd: { ...base, 'Content-Encoding': 'zstd' } }
|
|
32
|
+
cache.set(pathname, bundle)
|
|
33
|
+
return bundle
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { PUBLIC_ASSET_CACHE_CONTROL } from '../../shared/CACHE_CONTROL_VALUES.ts'
|
|
2
|
+
import { acceptsZstd } from './acceptsZstd.ts'
|
|
3
|
+
import { containsTraversal } from './containsTraversal.ts'
|
|
4
|
+
import { createAssetHeaderCache } from './createAssetHeaderCache.ts'
|
|
5
|
+
import { globToPathSet } from './globToPathSet.ts'
|
|
6
|
+
import type { Assets } from './types/Assets.ts'
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
Serves files from the project's `public/` folder at the site root. Two
|
|
10
|
+
sources, picked at construction:
|
|
11
|
+
|
|
12
|
+
- `publicAssets` (standalone compile): a map of root path → zstd bytes
|
|
13
|
+
embedded into the binary, mirroring the `_app` asset embed.
|
|
14
|
+
- `publicDir` on disk (dev + `belte start`): files read straight from
|
|
15
|
+
`${cwd}/src/browser/public`, with the set of paths snapshotted once at
|
|
16
|
+
boot (see below).
|
|
17
|
+
|
|
18
|
+
Returns a server fn that resolves to `undefined` when no public file
|
|
19
|
+
matches the request path, so the caller falls through to its own 404 /
|
|
20
|
+
middleware path. The path-traversal guard mirrors serveStaticAsset's
|
|
21
|
+
defence against encoded `..` segments in the raw URL.
|
|
22
|
+
|
|
23
|
+
Async because disk mode globs `publicDir` once at construction to build a
|
|
24
|
+
Set of the available paths: every page nav and RPC falls through here, so
|
|
25
|
+
a Set lookup beats a filesystem stat per miss. A file added to public/
|
|
26
|
+
after boot needs a server restart to be seen — the same restart a code
|
|
27
|
+
change already triggers under `bun --watch`.
|
|
28
|
+
*/
|
|
29
|
+
export async function createPublicAssetServer({
|
|
30
|
+
publicDir,
|
|
31
|
+
publicAssets,
|
|
32
|
+
}: {
|
|
33
|
+
publicDir: string
|
|
34
|
+
publicAssets?: Assets
|
|
35
|
+
}): Promise<(req: Request, url: URL) => Promise<Response | undefined>> {
|
|
36
|
+
const headersFor = createAssetHeaderCache(() => PUBLIC_ASSET_CACHE_CONTROL)
|
|
37
|
+
// `dot: true` keeps dotfiles (e.g. `.well-known/…`) servable, matching a raw disk stat.
|
|
38
|
+
const diskPaths = publicAssets
|
|
39
|
+
? new Set<string>()
|
|
40
|
+
: await globToPathSet(publicDir, '**/*', (file) => `/${file}`, { dot: true })
|
|
41
|
+
|
|
42
|
+
return async function servePublicAsset(req, url) {
|
|
43
|
+
if (containsTraversal(req.url)) {
|
|
44
|
+
return undefined
|
|
45
|
+
}
|
|
46
|
+
const wantsZstd = acceptsZstd(req)
|
|
47
|
+
const { base, zstd } = headersFor(url.pathname)
|
|
48
|
+
if (publicAssets) {
|
|
49
|
+
const compressed = publicAssets[url.pathname]
|
|
50
|
+
if (!compressed) {
|
|
51
|
+
return undefined
|
|
52
|
+
}
|
|
53
|
+
if (wantsZstd) {
|
|
54
|
+
return new Response(compressed, { headers: zstd })
|
|
55
|
+
}
|
|
56
|
+
return new Response(await Bun.zstdDecompress(compressed), { headers: base })
|
|
57
|
+
}
|
|
58
|
+
if (!diskPaths.has(url.pathname)) {
|
|
59
|
+
return undefined
|
|
60
|
+
}
|
|
61
|
+
return new Response(Bun.file(publicDir + url.pathname), { headers: base })
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Pages } from '../../browser/types/Pages.ts'
|
|
2
|
+
import { NO_STORE } from '../../shared/CACHE_CONTROL_VALUES.ts'
|
|
3
|
+
import { memoizeByKey } from '../../shared/memoizeByKey.ts'
|
|
4
|
+
import type { HttpVerb } from '../../shared/types/HttpVerb.ts'
|
|
5
|
+
import type { RemoteFunction } from '../../shared/types/RemoteFunction.ts'
|
|
6
|
+
import type { RemoteRoutes } from '../rpc/types/RemoteRoutes.ts'
|
|
7
|
+
import type { RequestStore } from './types/RequestStore.ts'
|
|
8
|
+
|
|
9
|
+
type AnyRemoteFunction = RemoteFunction<unknown, unknown>
|
|
10
|
+
|
|
11
|
+
/* Resolves a matched route to a Response, given the request and its scope. */
|
|
12
|
+
type RouteHandler = (
|
|
13
|
+
req: Request,
|
|
14
|
+
pathParams: Record<string, string>,
|
|
15
|
+
store: RequestStore,
|
|
16
|
+
) => Promise<Response>
|
|
17
|
+
|
|
18
|
+
/* Renders the page at `routeUrl` — injected so dispatch is testable without SSR. */
|
|
19
|
+
type RenderPage = (
|
|
20
|
+
routeUrl: string,
|
|
21
|
+
params: Record<string, string>,
|
|
22
|
+
store: RequestStore,
|
|
23
|
+
) => Promise<Response>
|
|
24
|
+
|
|
25
|
+
/* The framework's 405 — `Allow` names the permitted verb(s), body and NO_STORE shared so the rpc and page branches can't drift. */
|
|
26
|
+
function methodNotAllowed(allow: string): Response {
|
|
27
|
+
return new Response('Method Not Allowed', {
|
|
28
|
+
status: 405,
|
|
29
|
+
headers: { Allow: allow, 'Cache-Control': NO_STORE },
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
Owns route dispatch: deciding, per registered URL, whether a request hits an
|
|
35
|
+
rpc verb, a page render, or nothing — and the method-matching that picks the
|
|
36
|
+
status. Page URLs (under src/browser/pages/) serve GET/HEAD by rendering; rpc
|
|
37
|
+
URLs (under src/server/rpc/, `/rpc/...`) dispatch to the single declared verb,
|
|
38
|
+
405 on method mismatch; an unregistered URL is 404. Page and rpc URLs are
|
|
39
|
+
disjoint by construction, so each route lands in exactly one branch.
|
|
40
|
+
|
|
41
|
+
`renderPage` is injected rather than built here: dispatch decisions (the
|
|
42
|
+
405/404 branches and the rpc method match) are the behaviour worth testing,
|
|
43
|
+
and keeping the Svelte render behind the seam lets a test exercise them with a
|
|
44
|
+
stub instead of booting a server. The rpc-module loader is memoised internally
|
|
45
|
+
so each module loads once.
|
|
46
|
+
*/
|
|
47
|
+
export function createRouteDispatcher({
|
|
48
|
+
pages,
|
|
49
|
+
rpc,
|
|
50
|
+
renderPage,
|
|
51
|
+
}: {
|
|
52
|
+
pages: Pages
|
|
53
|
+
rpc: RemoteRoutes
|
|
54
|
+
renderPage: RenderPage
|
|
55
|
+
}): (routeUrl: string) => RouteHandler {
|
|
56
|
+
const loadRpc = memoizeByKey((url): Promise<AnyRemoteFunction | undefined> | undefined => {
|
|
57
|
+
const loader = rpc[url]
|
|
58
|
+
if (!loader) {
|
|
59
|
+
return undefined
|
|
60
|
+
}
|
|
61
|
+
/*
|
|
62
|
+
Each $rpc module has exactly one named export, validated at build
|
|
63
|
+
time. Pick the first export that looks like a RemoteFunction so the
|
|
64
|
+
framework stays tolerant of incidental re-exports.
|
|
65
|
+
*/
|
|
66
|
+
return loader().then((mod) => {
|
|
67
|
+
for (const value of Object.values(mod)) {
|
|
68
|
+
if (typeof value === 'function' && 'method' in value && 'url' in value) {
|
|
69
|
+
return value as AnyRemoteFunction
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
return function buildRouteHandler(routeUrl: string): RouteHandler {
|
|
77
|
+
const hasPage = pages[routeUrl] !== undefined
|
|
78
|
+
const hasRpc = rpc[routeUrl] !== undefined
|
|
79
|
+
return async function routeHandler(req, pathParams, store) {
|
|
80
|
+
const method = req.method as HttpVerb
|
|
81
|
+
if (hasRpc) {
|
|
82
|
+
const fn = await loadRpc(routeUrl)
|
|
83
|
+
if (fn && fn.method === method) {
|
|
84
|
+
return fn.fetch(req)
|
|
85
|
+
}
|
|
86
|
+
return methodNotAllowed(fn ? fn.method : '')
|
|
87
|
+
}
|
|
88
|
+
if (hasPage) {
|
|
89
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
90
|
+
return methodNotAllowed('GET, HEAD')
|
|
91
|
+
}
|
|
92
|
+
return renderPage(routeUrl, pathParams, store)
|
|
93
|
+
}
|
|
94
|
+
return new Response('Not Found', {
|
|
95
|
+
status: 404,
|
|
96
|
+
headers: { 'Cache-Control': NO_STORE },
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|