@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,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
|
+
}
|