@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.
Files changed (326) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/LICENSE +21 -0
  3. package/README.md +559 -0
  4. package/bin/belte.ts +183 -0
  5. package/package.json +110 -0
  6. package/src/App.svelte +31 -0
  7. package/src/appEntry.ts +151 -0
  8. package/src/assets/app.html +14 -0
  9. package/src/belteResolverPlugin.ts +858 -0
  10. package/src/build.ts +147 -0
  11. package/src/buildCli.ts +129 -0
  12. package/src/buildDisconnected.ts +122 -0
  13. package/src/bundleApp.ts +149 -0
  14. package/src/bundleDisconnectedEntry.ts +17 -0
  15. package/src/cliEntry.ts +25 -0
  16. package/src/clientBuildPlugins.ts +41 -0
  17. package/src/clientEntry.ts +7 -0
  18. package/src/compile.ts +64 -0
  19. package/src/controlServerWorker.ts +422 -0
  20. package/src/dedupeSveltePlugin.ts +66 -0
  21. package/src/devEntry.ts +169 -0
  22. package/src/discoveryEntry.ts +81 -0
  23. package/src/lib/browser/applyStreamedResolution.ts +33 -0
  24. package/src/lib/browser/cacheEntryFromSnapshot.ts +48 -0
  25. package/src/lib/browser/flushUnresolvedPlaceholders.ts +16 -0
  26. package/src/lib/browser/installStreamingPlaceholders.ts +32 -0
  27. package/src/lib/browser/openResolveStream.ts +42 -0
  28. package/src/lib/browser/page.svelte.ts +258 -0
  29. package/src/lib/browser/pageStreamController.ts +17 -0
  30. package/src/lib/browser/refetchPlaceholder.ts +12 -0
  31. package/src/lib/browser/remoteProxy.ts +37 -0
  32. package/src/lib/browser/socketChannel.ts +192 -0
  33. package/src/lib/browser/socketProxy.ts +57 -0
  34. package/src/lib/browser/startClient.ts +153 -0
  35. package/src/lib/browser/subscribe.ts +131 -0
  36. package/src/lib/browser/types/Errors.ts +9 -0
  37. package/src/lib/browser/types/Layouts.ts +7 -0
  38. package/src/lib/browser/types/Pages.ts +7 -0
  39. package/src/lib/browser/types/StreamingDeferred.ts +9 -0
  40. package/src/lib/bundle/BundleMenu.ts +11 -0
  41. package/src/lib/bundle/BundleMenuItem.ts +24 -0
  42. package/src/lib/bundle/BundleWindow.ts +36 -0
  43. package/src/lib/bundle/WEBVIEW_BUILD_REVISION.ts +9 -0
  44. package/src/lib/bundle/WEBVIEW_VERSION.ts +7 -0
  45. package/src/lib/bundle/bindConnectedFlag.ts +29 -0
  46. package/src/lib/bundle/bindRequestNavigate.ts +31 -0
  47. package/src/lib/bundle/buildWebviewLib.ts +111 -0
  48. package/src/lib/bundle/disconnected.css +9 -0
  49. package/src/lib/bundle/disconnected.svelte +386 -0
  50. package/src/lib/bundle/ensureWebviewLib.ts +20 -0
  51. package/src/lib/bundle/exitWithParent.ts +28 -0
  52. package/src/lib/bundle/infoPlist.ts +46 -0
  53. package/src/lib/bundle/installDownloads.ts +24 -0
  54. package/src/lib/bundle/installMacMenu.ts +39 -0
  55. package/src/lib/bundle/listenLocalControlServer.ts +19 -0
  56. package/src/lib/bundle/native/belteMenu.mm +422 -0
  57. package/src/lib/bundle/native/webview.h +4557 -0
  58. package/src/lib/bundle/onMenu.ts +41 -0
  59. package/src/lib/bundle/openWebview.ts +104 -0
  60. package/src/lib/bundle/pngToIcns.ts +47 -0
  61. package/src/lib/bundle/probeBelteServer.ts +34 -0
  62. package/src/lib/bundle/resolveServerBinary.ts +12 -0
  63. package/src/lib/bundle/resolveWebviewLib.ts +53 -0
  64. package/src/lib/bundle/serverBinaryFilename.ts +8 -0
  65. package/src/lib/bundle/signMacApp.ts +35 -0
  66. package/src/lib/bundle/spawnEmbeddedServer.ts +65 -0
  67. package/src/lib/bundle/stableLocalPort.ts +19 -0
  68. package/src/lib/bundle/waitForServer.ts +23 -0
  69. package/src/lib/bundle/webviewCachePath.ts +23 -0
  70. package/src/lib/bundle/webviewLibName.ts +11 -0
  71. package/src/lib/cli/connectToServer.ts +23 -0
  72. package/src/lib/cli/createClient.ts +170 -0
  73. package/src/lib/cli/dispatchCommand.ts +71 -0
  74. package/src/lib/cli/loadEnvFromBinaryDir.ts +16 -0
  75. package/src/lib/cli/parseArgvForRpc.ts +97 -0
  76. package/src/lib/cli/printHelp.ts +119 -0
  77. package/src/lib/cli/printSessionHelp.ts +27 -0
  78. package/src/lib/cli/printSessionStatus.ts +21 -0
  79. package/src/lib/cli/printTrimmed.ts +8 -0
  80. package/src/lib/cli/printValue.ts +10 -0
  81. package/src/lib/cli/resolveCliTarget.ts +48 -0
  82. package/src/lib/cli/runCli.ts +139 -0
  83. package/src/lib/cli/runSession.ts +105 -0
  84. package/src/lib/cli/startLocalInstance.ts +14 -0
  85. package/src/lib/cli/tokenizeLine.ts +51 -0
  86. package/src/lib/cli/types/CliManifest.ts +9 -0
  87. package/src/lib/cli/types/CliManifestEntry.ts +17 -0
  88. package/src/lib/cli/types/CliTarget.ts +13 -0
  89. package/src/lib/mcp/annotationsForMethod.ts +29 -0
  90. package/src/lib/mcp/createMcpResourceServer.ts +101 -0
  91. package/src/lib/mcp/createMcpServer.ts +42 -0
  92. package/src/lib/mcp/dispatchMcpRequest.ts +146 -0
  93. package/src/lib/mcp/mcpResourceServerSlot.ts +18 -0
  94. package/src/lib/mcp/mcpSurface.ts +265 -0
  95. package/src/lib/mcp/toolResultFromResponse.ts +66 -0
  96. package/src/lib/mcp/types/JsonRpcRequest.ts +12 -0
  97. package/src/lib/mcp/types/JsonRpcResponse.ts +20 -0
  98. package/src/lib/mcp/types/McpResourceContents.ts +10 -0
  99. package/src/lib/mcp/types/McpResourceDescriptor.ts +6 -0
  100. package/src/lib/mcp/types/McpResourceServer.ts +12 -0
  101. package/src/lib/mcp/types/McpServer.ts +9 -0
  102. package/src/lib/mcp/types/McpServerOptions.ts +16 -0
  103. package/src/lib/server/AppModule.ts +33 -0
  104. package/src/lib/server/DELETE.ts +9 -0
  105. package/src/lib/server/GET.ts +9 -0
  106. package/src/lib/server/HEAD.ts +9 -0
  107. package/src/lib/server/PATCH.ts +9 -0
  108. package/src/lib/server/POST.ts +9 -0
  109. package/src/lib/server/PUT.ts +9 -0
  110. package/src/lib/server/agent.ts +76 -0
  111. package/src/lib/server/appDataDir.ts +15 -0
  112. package/src/lib/server/cli/buildEnvContent.ts +19 -0
  113. package/src/lib/server/cli/createTarGz.ts +76 -0
  114. package/src/lib/server/cli/handleCliDownload.ts +153 -0
  115. package/src/lib/server/cli/handleCliInstall.ts +37 -0
  116. package/src/lib/server/cli/installScript.ts +29 -0
  117. package/src/lib/server/cli/maxSourceMtime.ts +26 -0
  118. package/src/lib/server/cookies.ts +29 -0
  119. package/src/lib/server/env.ts +50 -0
  120. package/src/lib/server/error.ts +70 -0
  121. package/src/lib/server/json.ts +28 -0
  122. package/src/lib/server/jsonl.ts +46 -0
  123. package/src/lib/server/prompts/definePrompt.ts +20 -0
  124. package/src/lib/server/prompts/promptRegistry.ts +9 -0
  125. package/src/lib/server/prompts/registerPrompt.ts +6 -0
  126. package/src/lib/server/prompts/renderPromptTemplate.ts +16 -0
  127. package/src/lib/server/prompts/types/Prompt.ts +13 -0
  128. package/src/lib/server/prompts/types/PromptOptions.ts +12 -0
  129. package/src/lib/server/prompts/types/PromptRegistryEntry.ts +13 -0
  130. package/src/lib/server/prompts/types/PromptRoutes.ts +10 -0
  131. package/src/lib/server/redirect.ts +42 -0
  132. package/src/lib/server/request.ts +18 -0
  133. package/src/lib/server/rpc/defineVerb.ts +133 -0
  134. package/src/lib/server/rpc/dispatchVerbInProcess.ts +46 -0
  135. package/src/lib/server/rpc/findVerbByCommandName.ts +18 -0
  136. package/src/lib/server/rpc/parseArgs.ts +95 -0
  137. package/src/lib/server/rpc/registerVerb.ts +6 -0
  138. package/src/lib/server/rpc/types/RemoteHandler.ts +27 -0
  139. package/src/lib/server/rpc/types/RemoteRoutes.ts +13 -0
  140. package/src/lib/server/rpc/types/TypedResponse.ts +18 -0
  141. package/src/lib/server/rpc/types/VerbHelper.ts +68 -0
  142. package/src/lib/server/rpc/types/VerbRegistryEntry.ts +29 -0
  143. package/src/lib/server/rpc/unprocessed.ts +14 -0
  144. package/src/lib/server/rpc/verbRegistry.ts +11 -0
  145. package/src/lib/server/runtime/DEFAULT_PORT.ts +6 -0
  146. package/src/lib/server/runtime/DEV_REBUILD_MESSAGE.ts +4 -0
  147. package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +29 -0
  148. package/src/lib/server/runtime/acceptsZstd.ts +8 -0
  149. package/src/lib/server/runtime/buildOpenApiSpec.ts +106 -0
  150. package/src/lib/server/runtime/cacheControlForAsset.ts +22 -0
  151. package/src/lib/server/runtime/containsTraversal.ts +37 -0
  152. package/src/lib/server/runtime/createAssetHeaderCache.ts +35 -0
  153. package/src/lib/server/runtime/createPublicAssetServer.ts +63 -0
  154. package/src/lib/server/runtime/createRouteDispatcher.ts +100 -0
  155. package/src/lib/server/runtime/createServer.ts +692 -0
  156. package/src/lib/server/runtime/devReloadResponse.ts +35 -0
  157. package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +27 -0
  158. package/src/lib/server/runtime/envSchemaStore.ts +15 -0
  159. package/src/lib/server/runtime/findOpenPort.ts +35 -0
  160. package/src/lib/server/runtime/getActiveServer.ts +6 -0
  161. package/src/lib/server/runtime/globToPathSet.ts +29 -0
  162. package/src/lib/server/runtime/inProcessServer.ts +20 -0
  163. package/src/lib/server/runtime/internalErrorResponse.ts +25 -0
  164. package/src/lib/server/runtime/isCrossOriginUpgrade.ts +19 -0
  165. package/src/lib/server/runtime/listenOnOpenPort.ts +36 -0
  166. package/src/lib/server/runtime/logExposedSurfaces.ts +162 -0
  167. package/src/lib/server/runtime/mimeForExtension.ts +20 -0
  168. package/src/lib/server/runtime/parseIdleTimeout.ts +10 -0
  169. package/src/lib/server/runtime/parsePort.ts +11 -0
  170. package/src/lib/server/runtime/registryManifests.ts +66 -0
  171. package/src/lib/server/runtime/requestContext.ts +5 -0
  172. package/src/lib/server/runtime/resolveStreamResponse.ts +29 -0
  173. package/src/lib/server/runtime/runWithRequestScope.ts +57 -0
  174. package/src/lib/server/runtime/safeJsonForScript.ts +17 -0
  175. package/src/lib/server/runtime/serializeCacheSnapshot.ts +45 -0
  176. package/src/lib/server/runtime/serverSlot.ts +13 -0
  177. package/src/lib/server/runtime/setActiveServer.ts +6 -0
  178. package/src/lib/server/runtime/snapshotEntryFromCache.ts +81 -0
  179. package/src/lib/server/runtime/streamCacheResolutions.ts +37 -0
  180. package/src/lib/server/runtime/streamFromIterator.ts +86 -0
  181. package/src/lib/server/runtime/streamStash.ts +64 -0
  182. package/src/lib/server/runtime/types/Assets.ts +1 -0
  183. package/src/lib/server/runtime/types/RequestStore.ts +27 -0
  184. package/src/lib/server/runtime/withResponseDefaults.ts +24 -0
  185. package/src/lib/server/server.ts +32 -0
  186. package/src/lib/server/socket.ts +31 -0
  187. package/src/lib/server/sockets/createSocketDispatcher.ts +311 -0
  188. package/src/lib/server/sockets/defineSocket.ts +167 -0
  189. package/src/lib/server/sockets/lookupSocket.ts +6 -0
  190. package/src/lib/server/sockets/recentHistory.ts +11 -0
  191. package/src/lib/server/sockets/registerSocket.ts +6 -0
  192. package/src/lib/server/sockets/socketOperations.ts +35 -0
  193. package/src/lib/server/sockets/socketRegistry.ts +9 -0
  194. package/src/lib/server/sockets/types/Socket.ts +21 -0
  195. package/src/lib/server/sockets/types/SocketClientFrame.ts +18 -0
  196. package/src/lib/server/sockets/types/SocketOperation.ts +22 -0
  197. package/src/lib/server/sockets/types/SocketOptions.ts +22 -0
  198. package/src/lib/server/sockets/types/SocketRegistryEntry.ts +17 -0
  199. package/src/lib/server/sockets/types/SocketRoutes.ts +10 -0
  200. package/src/lib/server/sockets/types/SocketServerFrame.ts +15 -0
  201. package/src/lib/server/sse.ts +53 -0
  202. package/src/lib/shared/BELTE_PACKAGE_NAME.ts +7 -0
  203. package/src/lib/shared/CACHE_CONTROL_VALUES.ts +16 -0
  204. package/src/lib/shared/HttpError.ts +19 -0
  205. package/src/lib/shared/RESOLVE_STREAM_PATH.ts +7 -0
  206. package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +11 -0
  207. package/src/lib/shared/activeCacheStore.ts +20 -0
  208. package/src/lib/shared/appDataDir.ts +34 -0
  209. package/src/lib/shared/belteImportName.ts +44 -0
  210. package/src/lib/shared/browserClientFlags.ts +10 -0
  211. package/src/lib/shared/buildRpcRequest.ts +70 -0
  212. package/src/lib/shared/bundleLayout.ts +36 -0
  213. package/src/lib/shared/bundled.ts +34 -0
  214. package/src/lib/shared/cache.ts +559 -0
  215. package/src/lib/shared/cacheStoreSlot.ts +16 -0
  216. package/src/lib/shared/canonicalJson.ts +63 -0
  217. package/src/lib/shared/carriesBodyArgs.ts +13 -0
  218. package/src/lib/shared/clearLastConnection.ts +7 -0
  219. package/src/lib/shared/commandNameForUrl.ts +17 -0
  220. package/src/lib/shared/createCacheStore.ts +75 -0
  221. package/src/lib/shared/createPushIterator.ts +93 -0
  222. package/src/lib/shared/createRemoteFunction.ts +99 -0
  223. package/src/lib/shared/decodeResponse.ts +47 -0
  224. package/src/lib/shared/detectTarget.ts +27 -0
  225. package/src/lib/shared/exeSuffix.ts +9 -0
  226. package/src/lib/shared/exitOnBuildFailure.ts +17 -0
  227. package/src/lib/shared/extraForwardHeaders.ts +16 -0
  228. package/src/lib/shared/fileStem.ts +9 -0
  229. package/src/lib/shared/findExportCallSite.ts +479 -0
  230. package/src/lib/shared/forwardHeaders.ts +41 -0
  231. package/src/lib/shared/getRemoteMeta.ts +5 -0
  232. package/src/lib/shared/globalCacheStore.ts +15 -0
  233. package/src/lib/shared/globalCacheStoreSlot.ts +14 -0
  234. package/src/lib/shared/importNamesToStrip.ts +13 -0
  235. package/src/lib/shared/invalidateEvent.ts +11 -0
  236. package/src/lib/shared/isCompileTarget.ts +15 -0
  237. package/src/lib/shared/isDebugEnabled.ts +23 -0
  238. package/src/lib/shared/isModuleNotFound.ts +16 -0
  239. package/src/lib/shared/isReadOnlyMethod.ts +14 -0
  240. package/src/lib/shared/isStreamingResponse.ts +11 -0
  241. package/src/lib/shared/jsonSchemaForPromptArguments.ts +29 -0
  242. package/src/lib/shared/jsonSchemaForSchema.ts +32 -0
  243. package/src/lib/shared/jsonlErrorFrame.ts +24 -0
  244. package/src/lib/shared/keyForRemoteCall.ts +29 -0
  245. package/src/lib/shared/lastConnectionPath.ts +7 -0
  246. package/src/lib/shared/loadEnvFile.ts +17 -0
  247. package/src/lib/shared/loadEnvFromDataDir.ts +15 -0
  248. package/src/lib/shared/loadSvelteConfig.ts +18 -0
  249. package/src/lib/shared/log.ts +104 -0
  250. package/src/lib/shared/manifestModule.ts +39 -0
  251. package/src/lib/shared/memoizeByKey.ts +24 -0
  252. package/src/lib/shared/nearestLayoutPrefix.ts +36 -0
  253. package/src/lib/shared/normalizeTarget.ts +10 -0
  254. package/src/lib/shared/pageUrlForFile.ts +14 -0
  255. package/src/lib/shared/parseBoundedEnvInt.ts +20 -0
  256. package/src/lib/shared/parseEnv.ts +30 -0
  257. package/src/lib/shared/parsePromptMarkdown.ts +34 -0
  258. package/src/lib/shared/parseRouteSegments.ts +22 -0
  259. package/src/lib/shared/prepareRpcModule.ts +59 -0
  260. package/src/lib/shared/prepareSocketModule.ts +49 -0
  261. package/src/lib/shared/programNameForPackage.ts +14 -0
  262. package/src/lib/shared/promptNameForFile.ts +10 -0
  263. package/src/lib/shared/queryStringFromArgs.ts +27 -0
  264. package/src/lib/shared/readEnvFile.ts +15 -0
  265. package/src/lib/shared/readLastConnection.ts +18 -0
  266. package/src/lib/shared/readPackageJson.ts +9 -0
  267. package/src/lib/shared/recordRemoteMeta.ts +5 -0
  268. package/src/lib/shared/remoteMetaStore.ts +16 -0
  269. package/src/lib/shared/resolveClientFlags.ts +20 -0
  270. package/src/lib/shared/responseErrorText.ts +9 -0
  271. package/src/lib/shared/rpcUrlForFile.ts +19 -0
  272. package/src/lib/shared/runningAsStandaloneBinary.ts +13 -0
  273. package/src/lib/shared/serializeEnv.ts +18 -0
  274. package/src/lib/shared/setCacheStoreResolver.ts +6 -0
  275. package/src/lib/shared/setGlobalCacheStoreResolver.ts +6 -0
  276. package/src/lib/shared/socketNameForFile.ts +11 -0
  277. package/src/lib/shared/sseErrorFrame.ts +29 -0
  278. package/src/lib/shared/streamResponse.ts +169 -0
  279. package/src/lib/shared/stripImport.ts +27 -0
  280. package/src/lib/shared/subscribableFromResponse.ts +51 -0
  281. package/src/lib/shared/toBunRoutePattern.ts +28 -0
  282. package/src/lib/shared/types/CacheEntry.ts +63 -0
  283. package/src/lib/shared/types/CacheInvalidation.ts +9 -0
  284. package/src/lib/shared/types/CacheOptions.ts +33 -0
  285. package/src/lib/shared/types/CacheSnapshot.ts +16 -0
  286. package/src/lib/shared/types/CacheSnapshotEntry.ts +15 -0
  287. package/src/lib/shared/types/CacheStore.ts +32 -0
  288. package/src/lib/shared/types/ClientFlags.ts +11 -0
  289. package/src/lib/shared/types/CompileTarget.ts +6 -0
  290. package/src/lib/shared/types/HttpVerb.ts +1 -0
  291. package/src/lib/shared/types/LastConnection.ts +9 -0
  292. package/src/lib/shared/types/PromptArgument.ts +12 -0
  293. package/src/lib/shared/types/RawRemoteFunction.ts +13 -0
  294. package/src/lib/shared/types/RemoteFunction.ts +42 -0
  295. package/src/lib/shared/types/StandardSchemaV1.ts +57 -0
  296. package/src/lib/shared/types/StreamedResolution.ts +10 -0
  297. package/src/lib/shared/types/StreamingPlaceholder.ts +13 -0
  298. package/src/lib/shared/types/Subscribable.ts +15 -0
  299. package/src/lib/shared/types/SvelteConfig.ts +5 -0
  300. package/src/lib/shared/withJsonSchema.ts +20 -0
  301. package/src/lib/shared/writeLastConnection.ts +13 -0
  302. package/src/lib/shared/writeRoutesDts.ts +67 -0
  303. package/src/lib/test/clearVerbRegistry.ts +11 -0
  304. package/src/lib/test/createTestClient.ts +78 -0
  305. package/src/preload.ts +20 -0
  306. package/src/scaffold.ts +92 -0
  307. package/src/serverBuildPlugins.ts +25 -0
  308. package/src/serverEntry.ts +94 -0
  309. package/src/sveltePlugin.ts +58 -0
  310. package/src/tailwindStylePreprocessor.ts +62 -0
  311. package/template/bunfig.toml +4 -0
  312. package/template/package.json +19 -0
  313. package/template/src/app.ts +23 -0
  314. package/template/src/browser/app.css +21 -0
  315. package/template/src/browser/app.html +24 -0
  316. package/template/src/browser/pages/about/page.svelte +5 -0
  317. package/template/src/browser/pages/layout.svelte +26 -0
  318. package/template/src/browser/pages/page.svelte +20 -0
  319. package/template/src/bundle/icon.png +0 -0
  320. package/template/src/cli/banner.txt +3 -0
  321. package/template/src/cli/footer.txt +1 -0
  322. package/template/src/server/config.ts +17 -0
  323. package/template/src/server/rpc/getHello.ts +35 -0
  324. package/template/svelte.config.js +12 -0
  325. package/template/tsconfig.json +18 -0
  326. 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,6 @@
1
+ import type { Server } from 'bun'
2
+ import { serverSlot } from './serverSlot.ts'
3
+
4
+ export function setActiveServer(server: Server<unknown>): void {
5
+ serverSlot.active = server
6
+ }
@@ -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
+ }