@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,75 @@
1
+ import { createSubscriber } from 'svelte/reactivity'
2
+ import type { CacheEntry } from './types/CacheEntry.ts'
3
+ import type { CacheInvalidation } from './types/CacheInvalidation.ts'
4
+ import type { CacheStore } from './types/CacheStore.ts'
5
+
6
+ /*
7
+ Returns a fresh cache store. On the server, every request gets its own
8
+ store via the AsyncLocalStorage RequestStore. On the client, a single
9
+ module-level store is created at startup and shared across the tab.
10
+
11
+ Each key gets a lazily-created Svelte subscriber. Reading a key from a
12
+ tracking scope ($derived / $effect) subscribes that scope; invalidating
13
+ the key dispatches an 'invalidate' event whose detail is a Set of affected
14
+ keys so each listener's lookup is O(1). The subscriber outlives entry
15
+ eviction — invalidating/refetching a key reuses the same subscriber, so
16
+ there's no listener churn or duplicate registration as cache values come
17
+ and go. It's evicted only when its last reactive reader tears down (the
18
+ client store is module-level/tab-scoped, so retaining a thunk per distinct
19
+ key would otherwise grow unbounded across a session), identity-guarded so
20
+ a concurrent re-subscribe isn't clobbered — mirroring subscribe.ts.
21
+ */
22
+ export function createCacheStore(): CacheStore {
23
+ const entries = new Map<string, CacheEntry>()
24
+ const events = new EventTarget()
25
+ const subscribers = new Map<string, () => void>()
26
+ const pendingRefresh = new Set<string>()
27
+
28
+ function subscribe(key: string): void {
29
+ const existing = subscribers.get(key)
30
+ if (existing) {
31
+ existing()
32
+ return
33
+ }
34
+ const registered = createSubscriber((update) => {
35
+ const onInvalidate = (event: Event) => {
36
+ if ((event as CustomEvent<CacheInvalidation>).detail.has(key)) {
37
+ update()
38
+ }
39
+ }
40
+ events.addEventListener('invalidate', onInvalidate)
41
+ return () => {
42
+ events.removeEventListener('invalidate', onInvalidate)
43
+ if (subscribers.get(key) === registered) {
44
+ subscribers.delete(key)
45
+ }
46
+ }
47
+ })
48
+ subscribers.set(key, registered)
49
+ registered()
50
+ }
51
+
52
+ /*
53
+ Store-wide tap for in-flight lifecycle. cache.pending selectors match many
54
+ entries (or all), so they re-derive by scanning `entries` and only need a
55
+ single "something changed" signal rather than per-key granularity. One
56
+ lazily-created subscriber for the whole store, evicted when its last reader
57
+ tears down — mirroring subscribe(key) and subscribe.ts.
58
+ */
59
+ let lifecycle: (() => void) | undefined
60
+ function trackLifecycle(): void {
61
+ if (!lifecycle) {
62
+ lifecycle = createSubscriber((update) => {
63
+ const onLifecycle = () => update()
64
+ events.addEventListener('lifecycle', onLifecycle)
65
+ return () => {
66
+ events.removeEventListener('lifecycle', onLifecycle)
67
+ lifecycle = undefined
68
+ }
69
+ })
70
+ }
71
+ lifecycle()
72
+ }
73
+
74
+ return { entries, events, subscribe, trackLifecycle, pendingRefresh }
75
+ }
@@ -0,0 +1,93 @@
1
+ /*
2
+ Single-slot-mailbox AsyncIterator factory shared by the in-process
3
+ socket fan-out (defineSocket) and the client-side ws proxy
4
+ (socketProxy). Callers push values, signal end, or signal an error;
5
+ the iterator drains a queue then awaits the next push. Cancellation
6
+ runs the optional `onClose` so subscribers can drop their backref.
7
+
8
+ The pending-value buffer is bounded: a subscriber whose `next()` falls
9
+ behind a chatty producer would otherwise grow it without limit, which on
10
+ the server is a remotely-triggerable memory-exhaustion vector. At the cap
11
+ the oldest pending value is dropped (live fan-out is latest-wins) so
12
+ memory stays bounded; terminal end/error slots are always appended and
13
+ never dropped.
14
+ */
15
+
16
+ type Slot<T> = { kind: 'value'; value: T } | { kind: 'end' } | { kind: 'error'; message: string }
17
+
18
+ export type PushIterator<T> = AsyncIterator<T, void, undefined> & {
19
+ push(value: T): void
20
+ end(): void
21
+ error(message: string): void
22
+ }
23
+
24
+ const DEFAULT_MAX_BUFFER = 1024
25
+
26
+ export function createPushIterator<T>(
27
+ onClose?: () => void,
28
+ maxBuffer = DEFAULT_MAX_BUFFER,
29
+ ): PushIterator<T> {
30
+ const buffer: Slot<T>[] = []
31
+ let waiter: ((slot: Slot<T>) => void) | undefined
32
+ let closed = false
33
+
34
+ function deliver(slot: Slot<T>): void {
35
+ if (closed) {
36
+ return
37
+ }
38
+ if (waiter) {
39
+ const wake = waiter
40
+ waiter = undefined
41
+ wake(slot)
42
+ return
43
+ }
44
+ // Drop the oldest pending value before exceeding the cap.
45
+ if (slot.kind === 'value' && buffer.length >= maxBuffer) {
46
+ buffer.shift()
47
+ }
48
+ buffer.push(slot)
49
+ }
50
+
51
+ function close(): void {
52
+ if (closed) {
53
+ return
54
+ }
55
+ closed = true
56
+ onClose?.()
57
+ }
58
+
59
+ return {
60
+ push(value) {
61
+ deliver({ kind: 'value', value })
62
+ },
63
+ end() {
64
+ deliver({ kind: 'end' })
65
+ },
66
+ error(message) {
67
+ deliver({ kind: 'error', message })
68
+ },
69
+ async next() {
70
+ if (closed) {
71
+ return { value: undefined, done: true }
72
+ }
73
+ const slot = buffer.shift() ?? (await new Promise<Slot<T>>((r) => (waiter = r)))
74
+ if (slot.kind === 'end') {
75
+ close()
76
+ return { value: undefined, done: true }
77
+ }
78
+ if (slot.kind === 'error') {
79
+ close()
80
+ throw new Error(slot.message)
81
+ }
82
+ return { value: slot.value, done: false }
83
+ },
84
+ async return() {
85
+ if (!closed) {
86
+ close()
87
+ waiter?.({ kind: 'end' })
88
+ waiter = undefined
89
+ }
90
+ return { value: undefined, done: true }
91
+ },
92
+ }
93
+ }
@@ -0,0 +1,99 @@
1
+ import { decodeResponse } from './decodeResponse.ts'
2
+ import { keyForRemoteCall } from './keyForRemoteCall.ts'
3
+ import { recordRemoteMeta } from './recordRemoteMeta.ts'
4
+ import { subscribableFromResponse } from './subscribableFromResponse.ts'
5
+ import type { ClientFlags } from './types/ClientFlags.ts'
6
+ import type { HttpVerb } from './types/HttpVerb.ts'
7
+ import type { RawRemoteFunction } from './types/RawRemoteFunction.ts'
8
+ import type { RemoteFunction } from './types/RemoteFunction.ts'
9
+ import type { Subscribable } from './types/Subscribable.ts'
10
+
11
+ /*
12
+ Assembles the public RemoteFunction shape used identically by the
13
+ server-side defineVerb (in-process handler invocation) and the
14
+ client-side remoteProxy (network fetch). Centralising the wiring here
15
+ keeps the call/raw/stream/fetch semantics — including WeakMap meta
16
+ recording, Content-Type decode, and Subscribable derivation — in one
17
+ place so the two halves can't drift.
18
+
19
+ - `buildRequest(args)` synthesizes the Request a meta reader (cache()) or
20
+ the client invoke needs. Server uses the inbound request's URL as the
21
+ base; client uses window.location. The result is memoised inside the
22
+ per-call `getRequest` thunk so the Request is built at most once per
23
+ call regardless of how many readers pull on it.
24
+ - `invoke(args, getRequest)` actually runs the call: server defineVerb
25
+ runs the handler and ignores `getRequest`; client remoteProxy calls
26
+ `fetch(getRequest())`. The thunk lets the server skip the Request
27
+ allocation entirely on the SSR hot path — the only consumer that ever
28
+ forces it is cache(), via the meta thunk recorded below.
29
+ - `parseArgsForFetch` is optional and only set by the server, so the
30
+ framework's router can call `.fetch(inboundRequest)` and have the
31
+ handler receive parsed args. Client `remoteProxy.fetch` just
32
+ forwards the request through invoke().
33
+ */
34
+ export function createRemoteFunction<Args, Return>(opts: {
35
+ method: HttpVerb
36
+ url: string
37
+ clients: ClientFlags
38
+ buildRequest: (args: Args | undefined) => Request
39
+ invoke: (args: Args | undefined, getRequest: () => Request) => Promise<Response>
40
+ parseArgsForFetch?: (request: Request) => Promise<Args | undefined>
41
+ }): RemoteFunction<Args, Return> {
42
+ const { method, url, clients, buildRequest, invoke, parseArgsForFetch } = opts
43
+
44
+ /*
45
+ Dispatch is the one-stop entry for both the plain call (no prebuilt
46
+ Request) and the fetch path (router hands us the inbound Request as
47
+ `prebuilt`). The `getRequest` thunk lazily synthesizes — or
48
+ short-circuits to the prebuilt one — and caches the result so the
49
+ client invoke + the cache meta reader share a single Request.
50
+ */
51
+ function dispatch(args: Args | undefined, prebuilt?: Request): Promise<Response> {
52
+ let cached = prebuilt
53
+ function getRequest(): Request {
54
+ if (cached === undefined) {
55
+ cached = buildRequest(args)
56
+ }
57
+ return cached
58
+ }
59
+ const promise = invoke(args, getRequest)
60
+ recordRemoteMeta(promise, getRequest)
61
+ return promise
62
+ }
63
+
64
+ /*
65
+ A body verb may receive a FormData in place of typed Args (the upload
66
+ escape hatch). It flows through dispatch only into buildRpcRequest /
67
+ keyForRemoteCall, both of which take it as-is, so the cast to Args is a
68
+ contained type lie — buildRpcRequest's `instanceof FormData` branch handles
69
+ it at runtime.
70
+ */
71
+ function rawCall(args: Args | FormData): Promise<Response> {
72
+ return dispatch(args as Args)
73
+ }
74
+ rawCall.method = method
75
+ rawCall.url = url
76
+ const raw = rawCall as RawRemoteFunction<Args>
77
+
78
+ function callable(args: Args | FormData): Promise<Return> {
79
+ return raw(args).then(decodeResponse) as Promise<Return>
80
+ }
81
+ callable.method = method
82
+ callable.url = url
83
+ callable.clients = clients
84
+ callable.raw = raw
85
+ callable.stream = (args?: Args | FormData): Subscribable<Return> => {
86
+ return subscribableFromResponse(keyForRemoteCall(method, url, args), () =>
87
+ raw(args as Args),
88
+ )
89
+ }
90
+ callable.fetch = parseArgsForFetch
91
+ ? async (request: Request): Promise<Response> => {
92
+ const args = await parseArgsForFetch(request)
93
+ return dispatch(args, request)
94
+ }
95
+ : (request: Request): Promise<Response> => {
96
+ return dispatch(undefined, request)
97
+ }
98
+ return callable as RemoteFunction<Args, Return>
99
+ }
@@ -0,0 +1,47 @@
1
+ import { HttpError } from './HttpError.ts'
2
+ import { STREAMING_CONTENT_TYPES } from './STREAMING_CONTENT_TYPES.ts'
3
+
4
+ /*
5
+ Decodes a Response into the natural body value based on Content-Type:
6
+ application/json (or `*\/+json`) → parsed JSON
7
+ text/* → string
8
+ 204 No Content / empty body → undefined
9
+ everything else → Blob
10
+
11
+ Non-2xx responses throw HttpError so the happy path never has to check
12
+ `.ok` — error handling moves into try/catch (or unhandled exception
13
+ propagation), and the success path types as Promise<Return> cleanly.
14
+
15
+ Streaming Content-Types (SSE / JSONL / NDJSON) throw a clear error
16
+ rather than silently doing the wrong thing: response.text() would hang
17
+ forever on a never-ending body and response.json() would fail mid-parse.
18
+ The error points callers at the right tools — `subscribe(fn.stream)(args)`
19
+ for a shared reactive view, or `fn.stream(args)` directly for a fresh
20
+ per-call AsyncIterable — both of which know how to consume the body
21
+ frame-by-frame.
22
+
23
+ Callers that need headers, streaming, or per-status branching should use
24
+ the `.raw(args)` escape hatch on the remote function instead — that
25
+ returns the underlying Response untouched.
26
+ */
27
+ export async function decodeResponse(response: Response): Promise<unknown> {
28
+ if (!response.ok) {
29
+ throw new HttpError(response)
30
+ }
31
+ if (response.status === 204) {
32
+ return undefined
33
+ }
34
+ const contentType = (response.headers.get('content-type') ?? '').toLowerCase()
35
+ if (STREAMING_CONTENT_TYPES.some((type) => contentType.startsWith(type))) {
36
+ throw new Error(
37
+ `[belte] response at ${response.url} is a stream (${contentType}) — use subscribe(fn.stream)(args) for a reactive view, or fn.stream(args) for per-call iteration, instead of awaiting the bare call or cache()`,
38
+ )
39
+ }
40
+ if (contentType.includes('json')) {
41
+ return response.json()
42
+ }
43
+ if (contentType.startsWith('text/')) {
44
+ return response.text()
45
+ }
46
+ return response.blob()
47
+ }
@@ -0,0 +1,27 @@
1
+ import type { CompileTarget } from './types/CompileTarget.ts'
2
+
3
+ /*
4
+ Picks the Bun compile target matching the current host. Throws if the
5
+ platform/arch pair isn't one of the supported Bun standalone targets — the
6
+ CLI's `--target` flag is the escape hatch for cross-compilation.
7
+ */
8
+ const HOST_TO_TARGET: Record<string, CompileTarget> = {
9
+ 'darwin-arm64': 'bun-darwin-arm64',
10
+ 'darwin-x64': 'bun-darwin-x64',
11
+ 'linux-arm64': 'bun-linux-arm64',
12
+ 'linux-x64': 'bun-linux-x64',
13
+ 'win32-x64': 'bun-windows-x64',
14
+ }
15
+
16
+ export function detectTarget(
17
+ platform: NodeJS.Platform = process.platform,
18
+ arch: NodeJS.Architecture = process.arch,
19
+ ): CompileTarget {
20
+ const target = HOST_TO_TARGET[`${platform}-${arch}`]
21
+ if (!target) {
22
+ throw new Error(
23
+ `[belte] unsupported host platform ${platform}/${arch}. Pass --target=<bun-...> explicitly.`,
24
+ )
25
+ }
26
+ return target
27
+ }
@@ -0,0 +1,9 @@
1
+ import type { CompileTarget } from './types/CompileTarget.ts'
2
+
3
+ /*
4
+ Executable filename suffix for a compile target — `.exe` on Windows targets,
5
+ empty elsewhere. Single source so every cross-compile output path agrees.
6
+ */
7
+ export function exeSuffix(target: CompileTarget): string {
8
+ return target.includes('windows') ? '.exe' : ''
9
+ }
@@ -0,0 +1,17 @@
1
+ import type { BuildOutput } from 'bun'
2
+ import { log } from './log.ts'
3
+
4
+ /*
5
+ On a failed Bun.build(), logs each diagnostic and exits non-zero. Every belte
6
+ build entrypoint (build / compile / buildCli / bundleApp) funnels its result
7
+ through here so failure reporting can't drift between them.
8
+ */
9
+ export function exitOnBuildFailure(result: BuildOutput): void {
10
+ if (result.success) {
11
+ return
12
+ }
13
+ result.logs.forEach((entry) => {
14
+ log.error(entry)
15
+ })
16
+ process.exit(1)
17
+ }
@@ -0,0 +1,16 @@
1
+ /*
2
+ App-configured inbound header names to forward onto in-process rpc Requests,
3
+ on top of the built-in FORWARDED_HEADERS. Set once at boot from
4
+ app.forwardHeaders so the SSR in-process path (defineVerb) and the MCP
5
+ dispatcher honour the same list without re-reading app config per call. A
6
+ module-level slot rather than threaded config because both call sites are deep
7
+ in the request path and the value is fixed for the process lifetime.
8
+ */
9
+ let names: readonly string[] = []
10
+
11
+ export const extraForwardHeaders = {
12
+ get: (): readonly string[] => names,
13
+ set: (configured: readonly string[]): void => {
14
+ names = configured
15
+ },
16
+ }
@@ -0,0 +1,9 @@
1
+ /*
2
+ The bare filename of a path, with directory and trailing extension stripped —
3
+ e.g. `users/list.ts` → `list`, `/_virtual/mcp-resources.ts` → `mcp-resources`.
4
+ Used to derive a virtual-module name from its path and to check an $rpc /
5
+ $sockets module's single export name against its file stem.
6
+ */
7
+ export function fileStem(path: string): string {
8
+ return (path.split('/').pop() ?? '').replace(/\.[^.]+$/, '')
9
+ }