@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,45 @@
|
|
|
1
|
+
import type { CacheEntry } from '../../shared/types/CacheEntry.ts'
|
|
2
|
+
import type { CacheSnapshot } from '../../shared/types/CacheSnapshot.ts'
|
|
3
|
+
import type { CacheSnapshotEntry } from '../../shared/types/CacheSnapshotEntry.ts'
|
|
4
|
+
import type { CacheStore } from '../../shared/types/CacheStore.ts'
|
|
5
|
+
import { snapshotEntryFromCache } from './snapshotEntryFromCache.ts'
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
Partitions the request-scoped cache for SSR. Entries settled by the time
|
|
9
|
+
`render()` returns were consumed via `await` (render blocked on them) and ship
|
|
10
|
+
inline in the document's `__SSR__` blob. Entries still pending were consumed
|
|
11
|
+
via `{#await}` — render emitted their pending branch without blocking — so they
|
|
12
|
+
go to `pending` for the response streamer to drain and resolve over the wire.
|
|
13
|
+
|
|
14
|
+
Unlike the old buffer-everything path, this never awaits the pending promises:
|
|
15
|
+
that's the whole point of streaming. Settled entries are read concurrently (the
|
|
16
|
+
awaits are immediate since they're already resolved, but their body reads run in
|
|
17
|
+
parallel); pending entries are handed back as-is for the streamer to await one
|
|
18
|
+
chunk at a time.
|
|
19
|
+
*/
|
|
20
|
+
export async function serializeCacheSnapshot(store: CacheStore): Promise<CacheSnapshot> {
|
|
21
|
+
const settled: CacheEntry[] = []
|
|
22
|
+
const pending: CacheEntry[] = []
|
|
23
|
+
for (const entry of store.entries.values()) {
|
|
24
|
+
/* Producer entries carry no wire request — nothing to rehydrate against, skip. */
|
|
25
|
+
if (!entry.request) {
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
const method = entry.request.method.toUpperCase()
|
|
29
|
+
if (method !== 'GET' && method !== 'DELETE') {
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
if (entry.settled) {
|
|
33
|
+
settled.push(entry)
|
|
34
|
+
} else {
|
|
35
|
+
pending.push(entry)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const snapshots = await Promise.all(
|
|
39
|
+
settled.map((entry) => snapshotEntryFromCache(store, entry)),
|
|
40
|
+
)
|
|
41
|
+
const inline = snapshots.filter(
|
|
42
|
+
(snapshot): snapshot is CacheSnapshotEntry => snapshot !== undefined,
|
|
43
|
+
)
|
|
44
|
+
return { inline, pending }
|
|
45
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Server } from 'bun'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Internal holder for the active Bun.serve instance. setActiveServer is
|
|
5
|
+
called once from createServer after Bun.serve resolves; the public
|
|
6
|
+
`server()` function and any internal callers read through this slot
|
|
7
|
+
and throw when accessed before init completes. `Server<unknown>` matches
|
|
8
|
+
Bun's generic — ws.data is opaque to user code since the only ws path
|
|
9
|
+
is the framework-managed sockets dispatcher.
|
|
10
|
+
*/
|
|
11
|
+
export const serverSlot: { active: Server<unknown> | undefined } = {
|
|
12
|
+
active: undefined,
|
|
13
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { CacheEntry } from '../../shared/types/CacheEntry.ts'
|
|
2
|
+
import type { CacheSnapshotEntry } from '../../shared/types/CacheSnapshotEntry.ts'
|
|
3
|
+
import type { CacheStore } from '../../shared/types/CacheStore.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Awaits one cache entry and turns it into a wire-safe snapshot, or undefined
|
|
7
|
+
when it can't ship. Shared by the inline snapshot path (settled entries,
|
|
8
|
+
resolves immediately) and the streaming drain (pending {#await} entries,
|
|
9
|
+
resolves whenever the underlying fetch lands). Only GET/DELETE with a textual
|
|
10
|
+
Content-Type survive — other methods can't be replayed without the original
|
|
11
|
+
request body, and binary bodies don't round-trip through JSON.
|
|
12
|
+
|
|
13
|
+
Reads the body once and replaces the entry's promise with a string-bodied
|
|
14
|
+
Response so later `shareable()` clones operate on a buffered body instead of
|
|
15
|
+
teeing the original stream. Returns undefined on a rejected fetch (the client
|
|
16
|
+
falls back to a live re-fetch on cache miss) or when the entry was evicted /
|
|
17
|
+
replaced between resolution and read (a concurrent invalidate) so the snapshot
|
|
18
|
+
never ships a key that no longer matches the live store.
|
|
19
|
+
*/
|
|
20
|
+
export async function snapshotEntryFromCache(
|
|
21
|
+
store: CacheStore,
|
|
22
|
+
entry: CacheEntry,
|
|
23
|
+
): Promise<CacheSnapshotEntry | undefined> {
|
|
24
|
+
/* Producer entries have no wire request to replay — they never snapshot. */
|
|
25
|
+
if (!entry.request) {
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
const method = entry.request.method.toUpperCase()
|
|
29
|
+
if (method !== 'GET' && method !== 'DELETE') {
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
const response = await readSettled(entry.promise as Promise<Response>)
|
|
33
|
+
if (!response) {
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
if (store.entries.get(entry.key) !== entry) {
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
const contentType = (response.headers.get('content-type') ?? '').toLowerCase()
|
|
40
|
+
if (!isTextual(contentType)) {
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
const body = await response.text()
|
|
44
|
+
entry.promise = Promise.resolve(
|
|
45
|
+
new Response(body, {
|
|
46
|
+
status: response.status,
|
|
47
|
+
statusText: response.statusText,
|
|
48
|
+
headers: response.headers,
|
|
49
|
+
}),
|
|
50
|
+
)
|
|
51
|
+
return {
|
|
52
|
+
key: entry.key,
|
|
53
|
+
url: entry.request.url,
|
|
54
|
+
method,
|
|
55
|
+
status: response.status,
|
|
56
|
+
statusText: response.statusText,
|
|
57
|
+
headers: Array.from(response.headers.entries()),
|
|
58
|
+
body,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function readSettled(promise: Promise<Response>): Promise<Response | undefined> {
|
|
63
|
+
try {
|
|
64
|
+
return await promise
|
|
65
|
+
} catch {
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isTextual(contentType: string): boolean {
|
|
71
|
+
if (contentType.startsWith('text/')) {
|
|
72
|
+
return true
|
|
73
|
+
}
|
|
74
|
+
if (contentType.includes('json')) {
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
if (contentType.includes('xml')) {
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CacheEntry } from '../../shared/types/CacheEntry.ts'
|
|
2
|
+
import type { CacheSnapshotEntry } from '../../shared/types/CacheSnapshotEntry.ts'
|
|
3
|
+
import type { CacheStore } from '../../shared/types/CacheStore.ts'
|
|
4
|
+
import type { StreamedResolution } from '../../shared/types/StreamedResolution.ts'
|
|
5
|
+
import { snapshotEntryFromCache } from './snapshotEntryFromCache.ts'
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
Drains the pending ({#await}) cache entries in resolution order — whichever
|
|
9
|
+
fetch lands next is yielded next, so a slow entry never blocks a fast one
|
|
10
|
+
behind it. Yields exactly one StreamedResolution per entry: the snapshot when
|
|
11
|
+
serialization succeeds, or a `{ key, miss }` marker when the body can't ship so
|
|
12
|
+
the client placeholder re-fetches instead of hanging on a deferred that never
|
|
13
|
+
settles.
|
|
14
|
+
*/
|
|
15
|
+
export async function* streamCacheResolutions(
|
|
16
|
+
store: CacheStore,
|
|
17
|
+
pending: CacheEntry[],
|
|
18
|
+
): AsyncIterable<StreamedResolution> {
|
|
19
|
+
/*
|
|
20
|
+
Tag each pending serialization with its key so the loop can drop exactly
|
|
21
|
+
the one that just settled. Deleting inside the `.then` instead would race
|
|
22
|
+
the loop — already-resolved promises empty the map before the first
|
|
23
|
+
Promise.race runs — so removal happens here, after the await.
|
|
24
|
+
*/
|
|
25
|
+
const inflight = new Map<string, Promise<{ key: string; snapshot?: CacheSnapshotEntry }>>()
|
|
26
|
+
for (const entry of pending) {
|
|
27
|
+
inflight.set(
|
|
28
|
+
entry.key,
|
|
29
|
+
snapshotEntryFromCache(store, entry).then((snapshot) => ({ key: entry.key, snapshot })),
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
while (inflight.size > 0) {
|
|
33
|
+
const { key, snapshot } = await Promise.race(inflight.values())
|
|
34
|
+
inflight.delete(key)
|
|
35
|
+
yield snapshot ?? { key, miss: true }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Shared body builder for the streaming respond helpers (`jsonl`, `sse`).
|
|
3
|
+
Both flow the same shape — pull from an AsyncIterator, encode each frame
|
|
4
|
+
to bytes, emit a sentinel `error` frame on a generator throw, and route
|
|
5
|
+
ReadableStream's `cancel` into `iter.return()` so the handler's
|
|
6
|
+
`for await` exits via its normal control path. Only the per-frame
|
|
7
|
+
encoding and the optional keepalive payload differ between the two.
|
|
8
|
+
|
|
9
|
+
Keepalive is opt-in: SSE uses `: keepalive\n\n` every 15s so proxies
|
|
10
|
+
don't drop an idle connection; jsonl has no spec-defined comment, so it
|
|
11
|
+
omits keepalive entirely.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type StreamEncoder<T> = {
|
|
15
|
+
encodeFrame: (value: T) => string
|
|
16
|
+
encodeError: (message: string) => string
|
|
17
|
+
keepaliveMs?: number
|
|
18
|
+
keepalivePayload?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function streamFromIterator<T>(
|
|
22
|
+
iterable: AsyncIterable<T>,
|
|
23
|
+
encoder: StreamEncoder<T>,
|
|
24
|
+
): ReadableStream<Uint8Array> {
|
|
25
|
+
const textEncoder = new TextEncoder()
|
|
26
|
+
const iterator = iterable[Symbol.asyncIterator]()
|
|
27
|
+
let keepalive: ReturnType<typeof setInterval> | undefined
|
|
28
|
+
|
|
29
|
+
function stopKeepalive(): void {
|
|
30
|
+
if (keepalive !== undefined) {
|
|
31
|
+
clearInterval(keepalive)
|
|
32
|
+
keepalive = undefined
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new ReadableStream<Uint8Array>({
|
|
37
|
+
start(controller) {
|
|
38
|
+
if (encoder.keepaliveMs !== undefined && encoder.keepalivePayload !== undefined) {
|
|
39
|
+
const payload = textEncoder.encode(encoder.keepalivePayload)
|
|
40
|
+
keepalive = setInterval(() => {
|
|
41
|
+
/*
|
|
42
|
+
Every close/cancel path clears this interval synchronously,
|
|
43
|
+
so a tick can't normally hit a closed controller — but
|
|
44
|
+
enqueue throws on a closed/errored stream, and an uncaught
|
|
45
|
+
throw in a timer crashes the process. Guard + self-stop.
|
|
46
|
+
*/
|
|
47
|
+
try {
|
|
48
|
+
controller.enqueue(payload)
|
|
49
|
+
} catch {
|
|
50
|
+
stopKeepalive()
|
|
51
|
+
}
|
|
52
|
+
}, encoder.keepaliveMs)
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async pull(controller) {
|
|
56
|
+
try {
|
|
57
|
+
const next = await iterator.next()
|
|
58
|
+
if (next.done) {
|
|
59
|
+
stopKeepalive()
|
|
60
|
+
controller.close()
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
controller.enqueue(textEncoder.encode(encoder.encodeFrame(next.value)))
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
66
|
+
controller.enqueue(textEncoder.encode(encoder.encodeError(message)))
|
|
67
|
+
stopKeepalive()
|
|
68
|
+
controller.close()
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
cancel(reason) {
|
|
72
|
+
stopKeepalive()
|
|
73
|
+
/*
|
|
74
|
+
Route cancel into the generator's normal exit, but swallow a
|
|
75
|
+
rejection from its cleanup: a `finally` that throws on `.return()`
|
|
76
|
+
would otherwise surface as an unhandled rejection (process-fatal
|
|
77
|
+
under Bun's default) on a client disconnect — a path every
|
|
78
|
+
sse/jsonl/resolve stream hits routinely.
|
|
79
|
+
*/
|
|
80
|
+
return iterator.return?.(reason)?.then(
|
|
81
|
+
() => undefined,
|
|
82
|
+
() => undefined,
|
|
83
|
+
)
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { CacheEntry } from '../../shared/types/CacheEntry.ts'
|
|
2
|
+
import type { CacheStore } from '../../shared/types/CacheStore.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Cross-request holding area bridging an SSR render to its out-of-band resolution
|
|
6
|
+
stream. When a page flushes with pending {#await} reads, renderPage stashes the
|
|
7
|
+
request-scoped cache store plus its in-flight entries here under a random token
|
|
8
|
+
and ships the token in `__SSR__`. The browser then opens the resolve endpoint,
|
|
9
|
+
which TAKES the stash and awaits those SAME promises — so the handlers run once,
|
|
10
|
+
not again, even though the resolve stream is a separate request.
|
|
11
|
+
|
|
12
|
+
The SSR request scope exits as soon as the buffered document is sent, but the
|
|
13
|
+
stash holds a reference so the store (and its in-flight promises) survive until
|
|
14
|
+
the resolve stream drains them. A TTL evicts stashes whose client never connects
|
|
15
|
+
(JS disabled, navigated away before hydration) so they can't leak.
|
|
16
|
+
*/
|
|
17
|
+
type StashedStream = {
|
|
18
|
+
store: CacheStore
|
|
19
|
+
pending: CacheEntry[]
|
|
20
|
+
timer: ReturnType<typeof setTimeout>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const streams = new Map<string, StashedStream>()
|
|
24
|
+
|
|
25
|
+
const STASH_TTL_MS = 30_000
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
The TTL bounds how long a stash lives, but not how many can pile up: a burst
|
|
29
|
+
of streamed renders whose clients never connect would retain that many request
|
|
30
|
+
stores until each times out. Cap the count and evict the oldest (insertion
|
|
31
|
+
order on the Map) so peak memory stays bounded regardless of arrival rate.
|
|
32
|
+
*/
|
|
33
|
+
const MAX_STASHES = 1024
|
|
34
|
+
|
|
35
|
+
export function stashPendingStream(store: CacheStore, pending: CacheEntry[]): string {
|
|
36
|
+
if (streams.size >= MAX_STASHES) {
|
|
37
|
+
const oldest = streams.keys().next().value
|
|
38
|
+
if (oldest !== undefined) {
|
|
39
|
+
const evicted = streams.get(oldest)
|
|
40
|
+
if (evicted) {
|
|
41
|
+
clearTimeout(evicted.timer)
|
|
42
|
+
}
|
|
43
|
+
streams.delete(oldest)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const token = crypto.randomUUID()
|
|
47
|
+
const timer = setTimeout(() => streams.delete(token), STASH_TTL_MS)
|
|
48
|
+
timer.unref?.()
|
|
49
|
+
streams.set(token, { store, pending, timer })
|
|
50
|
+
return token
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Single-use: removes the stash so a token can't be drained twice. */
|
|
54
|
+
export function takePendingStream(
|
|
55
|
+
token: string,
|
|
56
|
+
): { store: CacheStore; pending: CacheEntry[] } | undefined {
|
|
57
|
+
const stashed = streams.get(token)
|
|
58
|
+
if (!stashed) {
|
|
59
|
+
return undefined
|
|
60
|
+
}
|
|
61
|
+
clearTimeout(stashed.timer)
|
|
62
|
+
streams.delete(token)
|
|
63
|
+
return { store: stashed.store, pending: stashed.pending }
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Assets = Record<string, Uint8Array>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CacheStore } from '../../../shared/types/CacheStore.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Per-request state propagated through AsyncLocalStorage. Every field is
|
|
5
|
+
populated once at the server's fetch boundary; helpers and verb-defined
|
|
6
|
+
remote functions read from it without threading arguments through user code.
|
|
7
|
+
The inbound request's AbortSignal is reached via `req.signal` rather than a
|
|
8
|
+
separate field.
|
|
9
|
+
*/
|
|
10
|
+
export type RequestStore = {
|
|
11
|
+
url: URL
|
|
12
|
+
req: Request
|
|
13
|
+
cache: CacheStore
|
|
14
|
+
/*
|
|
15
|
+
The request's cookie jar, materialized lazily by the first cookies() call
|
|
16
|
+
and flushed to Set-Cookie headers when the scope returns. Undefined while a
|
|
17
|
+
request never touches cookies, so the common path parses and emits nothing.
|
|
18
|
+
*/
|
|
19
|
+
cookies?: Bun.CookieMap
|
|
20
|
+
/*
|
|
21
|
+
File parts split off a multipart/form-data body by parseArgs, grouped by
|
|
22
|
+
field name, for files() to read. Files never enter the handler's args so the
|
|
23
|
+
input schema keeps validating a plain object; undefined when the request
|
|
24
|
+
carried no file parts.
|
|
25
|
+
*/
|
|
26
|
+
files?: Record<string, File[]>
|
|
27
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Merges a caller's `ResponseInit` over a respond helper's default headers.
|
|
3
|
+
The helper's defaults seed the header set; the caller's headers overlay them
|
|
4
|
+
per-key (so an explicit `cache-control` wins over the helper's `no-store`),
|
|
5
|
+
and the rest of `init` (status, statusText) passes straight through. Shared
|
|
6
|
+
by `json` / `jsonl` / `sse` / `error` / `redirect` so every helper accepts a
|
|
7
|
+
final `ResponseInit` with identical override semantics.
|
|
8
|
+
|
|
9
|
+
A helper that owns the status itself (`error`, `redirect`) passes it as the
|
|
10
|
+
final `status` argument; it is applied last so it always wins over any
|
|
11
|
+
`init.status`, keeping that precedence inside the helper rather than relying
|
|
12
|
+
on each call site spreading in the right order.
|
|
13
|
+
*/
|
|
14
|
+
export function withResponseDefaults(
|
|
15
|
+
init: ResponseInit | undefined,
|
|
16
|
+
defaultHeaders: Record<string, string>,
|
|
17
|
+
status?: number,
|
|
18
|
+
): ResponseInit {
|
|
19
|
+
const headers = new Headers(defaultHeaders)
|
|
20
|
+
new Headers(init?.headers).forEach((value, key) => {
|
|
21
|
+
headers.set(key, value)
|
|
22
|
+
})
|
|
23
|
+
return { ...init, headers, ...(status !== undefined && { status }) }
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Server } from 'bun'
|
|
2
|
+
import { getActiveServer } from './runtime/getActiveServer.ts'
|
|
3
|
+
import { inProcessServer } from './runtime/inProcessServer.ts'
|
|
4
|
+
import { requestContext } from './runtime/requestContext.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Returns the active Bun.serve instance. Mirrors `request()`'s function-call
|
|
8
|
+
shape so call sites appear in stack traces (a Proxy trap intermediates and
|
|
9
|
+
obscures them).
|
|
10
|
+
|
|
11
|
+
When no Bun.serve has booted, resolution forks on the request scope:
|
|
12
|
+
- Inside a scope it is in-process dispatch (CLI / MCP / test client), which
|
|
13
|
+
never boots a server — return the no-op inProcessServer so handler idioms
|
|
14
|
+
(server().timeout/publish/requestIP …) run unchanged instead of throwing.
|
|
15
|
+
createServer sets the slot at boot before any request, so an empty slot
|
|
16
|
+
while a scope is live can only be in-process.
|
|
17
|
+
- Outside any scope it is a genuine before-init misuse (module top-level /
|
|
18
|
+
app.ts init); keep throwing — silent undefined would mask it and strand
|
|
19
|
+
later property reads with cryptic errors.
|
|
20
|
+
*/
|
|
21
|
+
export function server(): Server<unknown> {
|
|
22
|
+
const active = getActiveServer()
|
|
23
|
+
if (active) {
|
|
24
|
+
return active
|
|
25
|
+
}
|
|
26
|
+
if (requestContext.getStore()) {
|
|
27
|
+
return inProcessServer
|
|
28
|
+
}
|
|
29
|
+
throw new Error(
|
|
30
|
+
'[belte] server() called before init — make sure your call happens inside or after app.ts init() resolves',
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '../shared/types/StandardSchemaV1.ts'
|
|
2
|
+
import type { Socket } from './sockets/types/Socket.ts'
|
|
3
|
+
import type { SocketOptions } from './sockets/types/SocketOptions.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Declares a Socket inside a file under `src/server/sockets/`. Each file contains
|
|
7
|
+
exactly one export, named after the file (e.g. `chat.ts` →
|
|
8
|
+
`export const chat = socket<ChatMessage>(...)`). The bundler reads the
|
|
9
|
+
export name from the filename and the socket name from the file path
|
|
10
|
+
under `src/server/sockets/`, then rewrites this call to bind the name into the
|
|
11
|
+
runtime implementation (defineSocket on the server, socketProxy on the
|
|
12
|
+
client). Opts (history, clientPublish, schema, clients) live on the
|
|
13
|
+
server side only; the client target discards them.
|
|
14
|
+
|
|
15
|
+
When `schema` is set, `T` infers from `InferOutput<Schema>` and publish
|
|
16
|
+
payloads validate against it on the server. `clients` controls which
|
|
17
|
+
adapter surfaces (browser / mcp / cli) advertise the socket — defaults
|
|
18
|
+
to browser-only when schemaless, all surfaces when a schema is present.
|
|
19
|
+
|
|
20
|
+
This function exists only for the type signature; calling it directly
|
|
21
|
+
means the bundler plugin didn't process the file, which throws.
|
|
22
|
+
*/
|
|
23
|
+
export function socket<Schema extends StandardSchemaV1>(
|
|
24
|
+
opts: SocketOptions<Schema> & { schema: Schema },
|
|
25
|
+
): Socket<StandardSchemaV1.InferOutput<Schema>>
|
|
26
|
+
export function socket<T = unknown>(opts?: SocketOptions): Socket<T>
|
|
27
|
+
export function socket<T = unknown>(_opts?: SocketOptions): Socket<T> {
|
|
28
|
+
throw new Error(
|
|
29
|
+
'[belte] `socket(...)` was called outside an $sockets module — the socket helper is only valid as the value of `export const <filename> = ...` inside a file under src/server/sockets/',
|
|
30
|
+
)
|
|
31
|
+
}
|