@abide/abide 0.28.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 +607 -0
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/bin/abide.ts +212 -0
- package/package.json +155 -0
- package/src/abideLsp.ts +211 -0
- package/src/abideModules.d.ts +8 -0
- package/src/abideResolverPlugin.ts +923 -0
- package/src/appEntry.ts +151 -0
- package/src/assets/app.html +12 -0
- package/src/build.ts +143 -0
- package/src/buildCli.ts +127 -0
- package/src/buildDisconnected.ts +118 -0
- package/src/bundleApp.ts +147 -0
- package/src/bundleDisconnectedEntry.ts +14 -0
- package/src/checkAbide.ts +77 -0
- package/src/cliEntry.ts +25 -0
- package/src/clientBuildPlugins.ts +33 -0
- package/src/clientEntry.ts +17 -0
- package/src/compile.ts +63 -0
- package/src/controlServerWorker.ts +426 -0
- package/src/devEntry.ts +250 -0
- package/src/discoveryEntry.ts +81 -0
- package/src/lib/bundle/BundleMenu.ts +12 -0
- package/src/lib/bundle/BundleMenuItem.ts +25 -0
- package/src/lib/bundle/BundleWindow.ts +37 -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 +34 -0
- package/src/lib/bundle/buildWebviewLib.ts +111 -0
- package/src/lib/bundle/bundled.ts +35 -0
- package/src/lib/bundle/disconnected.abide +236 -0
- package/src/lib/bundle/disconnected.css +9 -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/abideMenu.mm +422 -0
- package/src/lib/bundle/native/webview.h +4557 -0
- package/src/lib/bundle/onMenu.ts +42 -0
- package/src/lib/bundle/openWebview.ts +104 -0
- package/src/lib/bundle/pngToIcns.ts +47 -0
- package/src/lib/bundle/probeAbideServer.ts +57 -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 +37 -0
- package/src/lib/bundle/spawnEmbeddedServer.ts +64 -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 +108 -0
- package/src/lib/cli/dispatchCommand.ts +71 -0
- package/src/lib/cli/loadEnvFromBinaryDir.ts +15 -0
- package/src/lib/cli/parseArgvForRpc.ts +100 -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 +176 -0
- package/src/lib/cli/runSession.ts +108 -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 +102 -0
- package/src/lib/mcp/createMcpServer.ts +48 -0
- package/src/lib/mcp/dispatchMcpRequest.ts +138 -0
- package/src/lib/mcp/mcpResourceServerSlot.ts +18 -0
- package/src/lib/mcp/mcpSurface.ts +295 -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 +47 -0
- package/src/lib/server/DELETE.ts +10 -0
- package/src/lib/server/GET.ts +10 -0
- package/src/lib/server/HEAD.ts +10 -0
- package/src/lib/server/PATCH.ts +10 -0
- package/src/lib/server/POST.ts +10 -0
- package/src/lib/server/PUT.ts +10 -0
- package/src/lib/server/agent.ts +86 -0
- package/src/lib/server/appDataDir.ts +16 -0
- package/src/lib/server/cli/buildEnvContent.ts +19 -0
- package/src/lib/server/cli/createTarGz.ts +77 -0
- package/src/lib/server/cli/handleCliDownload.ts +150 -0
- package/src/lib/server/cli/handleCliInstall.ts +37 -0
- package/src/lib/server/cli/installScript.ts +31 -0
- package/src/lib/server/cli/maxSourceMtime.ts +26 -0
- package/src/lib/server/cookies.ts +30 -0
- package/src/lib/server/env.ts +51 -0
- package/src/lib/server/error.ts +73 -0
- package/src/lib/server/json.ts +42 -0
- package/src/lib/server/jsonl.ts +47 -0
- package/src/lib/server/prompts/definePrompt.ts +21 -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 +17 -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/reachable.ts +45 -0
- package/src/lib/server/redirect.ts +43 -0
- package/src/lib/server/request.ts +19 -0
- package/src/lib/server/rpc/defineVerb.ts +210 -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 +127 -0
- package/src/lib/server/rpc/readBodyWithinLimit.ts +44 -0
- package/src/lib/server/rpc/registerVerb.ts +6 -0
- package/src/lib/server/rpc/runWithVerbTimeout.ts +49 -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 +87 -0
- package/src/lib/server/rpc/types/VerbRegistryEntry.ts +35 -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_READY_MESSAGE.ts +6 -0
- package/src/lib/server/runtime/DEV_REBUILD_MESSAGE.ts +4 -0
- package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +107 -0
- package/src/lib/server/runtime/SSR_SWAP_SCRIPT.ts +16 -0
- package/src/lib/server/runtime/acceptsGzip.ts +24 -0
- package/src/lib/server/runtime/buildCacheSnapshot.ts +61 -0
- package/src/lib/server/runtime/buildHealthPayload.ts +34 -0
- package/src/lib/server/runtime/buildInspectorSurface.ts +37 -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/createAppAssetServer.ts +76 -0
- package/src/lib/server/runtime/createAssetHeaderCache.ts +31 -0
- package/src/lib/server/runtime/createPublicAssetServer.ts +67 -0
- package/src/lib/server/runtime/createReachable.ts +109 -0
- package/src/lib/server/runtime/createRouteDispatcher.ts +127 -0
- package/src/lib/server/runtime/createServer.ts +674 -0
- package/src/lib/server/runtime/createUiPageRenderer.ts +181 -0
- package/src/lib/server/runtime/crossOriginForbidden.ts +17 -0
- package/src/lib/server/runtime/crossOriginGate.ts +29 -0
- package/src/lib/server/runtime/devClientFingerprint.ts +117 -0
- package/src/lib/server/runtime/devHotModuleResponse.ts +40 -0
- package/src/lib/server/runtime/devReloadResponse.ts +41 -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 +21 -0
- package/src/lib/server/runtime/getActiveServer.ts +6 -0
- package/src/lib/server/runtime/globToPathSet.ts +29 -0
- package/src/lib/server/runtime/gzipResponse.ts +46 -0
- package/src/lib/server/runtime/inProcessServer.ts +20 -0
- package/src/lib/server/runtime/internalErrorResponse.ts +25 -0
- package/src/lib/server/runtime/isCrossOriginRequest.ts +23 -0
- package/src/lib/server/runtime/listenOnOpenPort.ts +36 -0
- package/src/lib/server/runtime/logExposedSurfaces.ts +156 -0
- package/src/lib/server/runtime/maybeMountInspector.ts +97 -0
- package/src/lib/server/runtime/mimeForExtension.ts +14 -0
- package/src/lib/server/runtime/pageUrlFromStore.ts +15 -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/resolvePageSnapshot.ts +25 -0
- package/src/lib/server/runtime/respondWithEmbeddedAsset.ts +18 -0
- package/src/lib/server/runtime/runWithRequestScope.ts +150 -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 +83 -0
- package/src/lib/server/runtime/streamCacheResolutions.ts +37 -0
- package/src/lib/server/runtime/streamFromIterator.ts +86 -0
- package/src/lib/server/runtime/types/Assets.ts +6 -0
- package/src/lib/server/runtime/types/DevReloadStamp.ts +18 -0
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +24 -0
- package/src/lib/server/runtime/types/InspectorCacheSnapshot.ts +11 -0
- package/src/lib/server/runtime/types/InspectorContext.ts +30 -0
- package/src/lib/server/runtime/types/InspectorSocket.ts +17 -0
- package/src/lib/server/runtime/types/InspectorSurface.ts +13 -0
- package/src/lib/server/runtime/types/InspectorVerb.ts +27 -0
- package/src/lib/server/runtime/types/RequestStore.ts +55 -0
- package/src/lib/server/runtime/warnUnguardedMcp.ts +32 -0
- package/src/lib/server/runtime/withResponseDefaults.ts +24 -0
- package/src/lib/server/server.ts +33 -0
- package/src/lib/server/socket.ts +32 -0
- package/src/lib/server/sockets/createSocketDispatcher.ts +337 -0
- package/src/lib/server/sockets/defineSocket.ts +179 -0
- package/src/lib/server/sockets/lookupSocket.ts +6 -0
- package/src/lib/server/sockets/registerSocket.ts +6 -0
- package/src/lib/server/sockets/socketOperations.ts +36 -0
- package/src/lib/server/sockets/socketRegistry.ts +9 -0
- package/src/lib/server/sockets/types/Socket.ts +23 -0
- package/src/lib/server/sockets/types/SocketClientFrame.ts +19 -0
- package/src/lib/server/sockets/types/SocketOperation.ts +22 -0
- package/src/lib/server/sockets/types/SocketOptions.ts +26 -0
- package/src/lib/server/sockets/types/SocketRegistryEntry.ts +19 -0
- package/src/lib/server/sockets/types/SocketRoutes.ts +10 -0
- package/src/lib/server/sockets/types/SocketServerFrame.ts +24 -0
- package/src/lib/server/sse.ts +54 -0
- package/src/lib/shared/ABIDE_PACKAGE_NAME.ts +7 -0
- package/src/lib/shared/ABIDE_VERSION.ts +9 -0
- package/src/lib/shared/CACHE_CONTROL_VALUES.ts +16 -0
- package/src/lib/shared/CACHE_WRAPPED.ts +8 -0
- package/src/lib/shared/CLI_PATH.ts +7 -0
- package/src/lib/shared/DEV_HOT_PREFIX.ts +7 -0
- package/src/lib/shared/DEV_RELOAD_PATH.ts +6 -0
- package/src/lib/shared/HEALTH_PATH.ts +7 -0
- package/src/lib/shared/HttpError.ts +20 -0
- package/src/lib/shared/IDENTITY_PATH.ts +6 -0
- package/src/lib/shared/INSPECTOR_PATH.ts +7 -0
- package/src/lib/shared/NAV_HEADER.ts +8 -0
- package/src/lib/shared/OFFLINE_HEADER.ts +8 -0
- package/src/lib/shared/REMOTE_FUNCTION.ts +8 -0
- package/src/lib/shared/REPLAYABLE_METHODS.ts +12 -0
- package/src/lib/shared/SOCKETS_PATH.ts +7 -0
- package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +11 -0
- package/src/lib/shared/SocketDisconnectedError.ts +13 -0
- package/src/lib/shared/TEXT_PLAIN.ts +7 -0
- package/src/lib/shared/abideImportName.ts +44 -0
- package/src/lib/shared/abideLog.ts +38 -0
- package/src/lib/shared/activeCacheStore.ts +20 -0
- package/src/lib/shared/activePage.ts +25 -0
- package/src/lib/shared/appDataDir.ts +34 -0
- package/src/lib/shared/appNameSlot.ts +10 -0
- package/src/lib/shared/basePath.ts +10 -0
- package/src/lib/shared/basePathFromAppUrl.ts +20 -0
- package/src/lib/shared/baseSlot.ts +14 -0
- package/src/lib/shared/binaryDirEnvPath.ts +12 -0
- package/src/lib/shared/browserClientFlags.ts +10 -0
- package/src/lib/shared/buildRpcProxy.ts +39 -0
- package/src/lib/shared/buildRpcRequest.ts +70 -0
- package/src/lib/shared/buildSocketOverChannel.ts +58 -0
- package/src/lib/shared/bundleLayout.ts +36 -0
- package/src/lib/shared/cache.ts +951 -0
- package/src/lib/shared/cacheEntryFromSnapshot.ts +59 -0
- package/src/lib/shared/cacheStoreSlot.ts +16 -0
- package/src/lib/shared/cacheStores.ts +10 -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 +104 -0
- package/src/lib/shared/createChannelLog.ts +122 -0
- package/src/lib/shared/createLifecycleChannel.ts +56 -0
- package/src/lib/shared/createLivenessWatch.ts +118 -0
- package/src/lib/shared/createPushIterator.ts +127 -0
- package/src/lib/shared/createRemoteFunction.ts +122 -0
- package/src/lib/shared/createSubscriber.ts +55 -0
- package/src/lib/shared/createTraceContext.ts +21 -0
- package/src/lib/shared/dataDirEnvPath.ts +12 -0
- package/src/lib/shared/decodeResponse.ts +47 -0
- package/src/lib/shared/detectTarget.ts +27 -0
- package/src/lib/shared/detectVerbMethod.ts +17 -0
- package/src/lib/shared/emitLogRecord.ts +190 -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 +476 -0
- package/src/lib/shared/formatTraceparent.ts +6 -0
- package/src/lib/shared/forwardHeaders.ts +44 -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/health.ts +179 -0
- package/src/lib/shared/healthReadSlot.ts +11 -0
- package/src/lib/shared/healthSeedSlot.ts +12 -0
- package/src/lib/shared/html.ts +38 -0
- package/src/lib/shared/importNamesToStrip.ts +13 -0
- package/src/lib/shared/invalidateEvent.ts +11 -0
- package/src/lib/shared/invalidateTripwire.ts +40 -0
- package/src/lib/shared/isAbideHealthPayload.ts +11 -0
- package/src/lib/shared/isCompileTarget.ts +15 -0
- package/src/lib/shared/isDebugEnabled.ts +26 -0
- package/src/lib/shared/isDebugNegated.ts +19 -0
- package/src/lib/shared/isModuleNotFound.ts +16 -0
- package/src/lib/shared/isReadOnlyMethod.ts +14 -0
- package/src/lib/shared/isReplayableMethod.ts +7 -0
- package/src/lib/shared/isStreamingResponse.ts +11 -0
- package/src/lib/shared/isSubscribable.ts +15 -0
- package/src/lib/shared/jsonSchemaForPromptArguments.ts +29 -0
- package/src/lib/shared/jsonSchemaForSchema.ts +39 -0
- package/src/lib/shared/jsonlErrorFrame.ts +24 -0
- package/src/lib/shared/keyForRemoteCall.ts +29 -0
- package/src/lib/shared/keyMatchesPrefix.ts +9 -0
- package/src/lib/shared/lastConnectionPath.ts +7 -0
- package/src/lib/shared/layoutChainForRoute.ts +22 -0
- package/src/lib/shared/loadEnvFile.ts +17 -0
- package/src/lib/shared/loadEnvFromDataDir.ts +14 -0
- package/src/lib/shared/log.ts +24 -0
- package/src/lib/shared/logClosingRecord.ts +28 -0
- package/src/lib/shared/logTapSlot.ts +13 -0
- package/src/lib/shared/manifestModule.ts +39 -0
- package/src/lib/shared/matchesDebugPattern.ts +16 -0
- package/src/lib/shared/memoizeByKey.ts +32 -0
- package/src/lib/shared/normalizeTarget.ts +10 -0
- package/src/lib/shared/online.ts +51 -0
- package/src/lib/shared/page.ts +30 -0
- package/src/lib/shared/pageSlot.ts +17 -0
- package/src/lib/shared/pageUrlForFile.ts +14 -0
- package/src/lib/shared/parseBoundedEnvInt.ts +20 -0
- package/src/lib/shared/parseDebugPatterns.ts +21 -0
- package/src/lib/shared/parseEnv.ts +30 -0
- package/src/lib/shared/parsePromptMarkdown.ts +35 -0
- package/src/lib/shared/parseRouteSegments.ts +22 -0
- package/src/lib/shared/parseTraceparent.ts +26 -0
- package/src/lib/shared/pending.ts +30 -0
- package/src/lib/shared/prepareRpcModule.ts +59 -0
- package/src/lib/shared/prepareSocketModule.ts +49 -0
- package/src/lib/shared/probeRegistries.ts +68 -0
- package/src/lib/shared/producerKey.ts +32 -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/randomHexId.ts +14 -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/refreshing.ts +31 -0
- package/src/lib/shared/remoteMetaStore.ts +16 -0
- package/src/lib/shared/requestScopeSlot.ts +15 -0
- package/src/lib/shared/resolveClientFlags.ts +20 -0
- package/src/lib/shared/responseErrorText.ts +9 -0
- package/src/lib/shared/rpcTimeoutSlot.ts +9 -0
- package/src/lib/shared/rpcUrlForFile.ts +19 -0
- package/src/lib/shared/runningAsStandaloneBinary.ts +13 -0
- package/src/lib/shared/selectorMatcher.ts +68 -0
- package/src/lib/shared/selectorPrefix.ts +39 -0
- package/src/lib/shared/serializeEnv.ts +18 -0
- package/src/lib/shared/setAppName.ts +5 -0
- package/src/lib/shared/setBaseResolver.ts +6 -0
- package/src/lib/shared/setCacheStoreResolver.ts +6 -0
- package/src/lib/shared/setGlobalCacheStoreResolver.ts +6 -0
- package/src/lib/shared/setPageResolver.ts +7 -0
- package/src/lib/shared/setRequestScopeResolver.ts +6 -0
- package/src/lib/shared/snippet.ts +25 -0
- package/src/lib/shared/socketNameForFile.ts +11 -0
- package/src/lib/shared/socketTapSlot.ts +12 -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/tailProbeSlot.ts +16 -0
- package/src/lib/shared/toBunRoutePattern.ts +28 -0
- package/src/lib/shared/toScopeSet.ts +4 -0
- package/src/lib/shared/trace.ts +16 -0
- package/src/lib/shared/types/CacheEntry.ts +84 -0
- package/src/lib/shared/types/CacheInvalidation.ts +9 -0
- package/src/lib/shared/types/CacheOnContext.ts +25 -0
- package/src/lib/shared/types/CacheOptions.ts +39 -0
- package/src/lib/shared/types/CacheSelector.ts +17 -0
- package/src/lib/shared/types/CacheSnapshot.ts +16 -0
- package/src/lib/shared/types/CacheSnapshotEntry.ts +17 -0
- package/src/lib/shared/types/CacheStats.ts +13 -0
- package/src/lib/shared/types/CacheStore.ts +39 -0
- package/src/lib/shared/types/ChannelLog.ts +13 -0
- package/src/lib/shared/types/ClientFlags.ts +11 -0
- package/src/lib/shared/types/CompileTarget.ts +6 -0
- package/src/lib/shared/types/FrameworkLog.ts +13 -0
- package/src/lib/shared/types/HttpVerb.ts +1 -0
- package/src/lib/shared/types/LastConnection.ts +9 -0
- package/src/lib/shared/types/Log.ts +13 -0
- package/src/lib/shared/types/LogRecord.ts +42 -0
- package/src/lib/shared/types/LogVoice.ts +7 -0
- package/src/lib/shared/types/PageSnapshot.ts +14 -0
- package/src/lib/shared/types/PromptArgument.ts +12 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +14 -0
- package/src/lib/shared/types/RemoteCallable.ts +12 -0
- package/src/lib/shared/types/RemoteFunction.ts +47 -0
- package/src/lib/shared/types/ReplayableMethod.ts +7 -0
- package/src/lib/shared/types/RequestScopeInfo.ts +16 -0
- package/src/lib/shared/types/RpcInvoker.ts +6 -0
- package/src/lib/shared/types/SocketChannel.ts +17 -0
- package/src/lib/shared/types/SocketSubCallbacks.ts +13 -0
- package/src/lib/shared/types/StandardSchemaV1.ts +56 -0
- package/src/lib/shared/types/StreamedResolution.ts +10 -0
- package/src/lib/shared/types/Subscribable.ts +26 -0
- package/src/lib/shared/types/TailHooks.ts +12 -0
- package/src/lib/shared/types/TailOptions.ts +10 -0
- package/src/lib/shared/types/TraceContext.ts +17 -0
- package/src/lib/shared/url.ts +118 -0
- package/src/lib/shared/withBase.ts +11 -0
- package/src/lib/shared/withBaseUrl.ts +17 -0
- package/src/lib/shared/withJsonSchema.ts +21 -0
- package/src/lib/shared/writeDts.ts +12 -0
- package/src/lib/shared/writeHealthDts.ts +36 -0
- package/src/lib/shared/writeLastConnection.ts +13 -0
- package/src/lib/shared/writePublicAssetsDts.ts +31 -0
- package/src/lib/shared/writeRoutesDts.ts +73 -0
- package/src/lib/shared/writeRpcDts.ts +49 -0
- package/src/lib/shared/writeTestRpcDts.ts +45 -0
- package/src/lib/shared/writeTestSocketsDts.ts +34 -0
- package/src/lib/test/assertAgentFrameConformance.ts +73 -0
- package/src/lib/test/createScriptedSurface.ts +45 -0
- package/src/lib/test/createTestApp.ts +203 -0
- package/src/lib/test/createTestSocketChannel.ts +142 -0
- package/src/lib/ui/README.md +86 -0
- package/src/lib/ui/compile/SSR_ESCAPE.ts +25 -0
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +36 -0
- package/src/lib/ui/compile/VOID_TAGS.ts +21 -0
- package/src/lib/ui/compile/abideUiPlugin.ts +65 -0
- package/src/lib/ui/compile/analyzeComponent.ts +117 -0
- package/src/lib/ui/compile/assetModulesFile.ts +32 -0
- package/src/lib/ui/compile/branchElements.ts +50 -0
- package/src/lib/ui/compile/collectAbideDiagnostics.ts +59 -0
- package/src/lib/ui/compile/compileComponent.ts +20 -0
- package/src/lib/ui/compile/compileModule.ts +116 -0
- package/src/lib/ui/compile/compileSSR.ts +36 -0
- package/src/lib/ui/compile/compileShadow.ts +352 -0
- package/src/lib/ui/compile/createShadowLanguageService.ts +197 -0
- package/src/lib/ui/compile/createShadowProgram.ts +96 -0
- package/src/lib/ui/compile/decodeHtmlEntities.ts +49 -0
- package/src/lib/ui/compile/desugarSignals.ts +133 -0
- package/src/lib/ui/compile/escapeHtml.ts +15 -0
- package/src/lib/ui/compile/generateBuild.ts +638 -0
- package/src/lib/ui/compile/generateSSR.ts +380 -0
- package/src/lib/ui/compile/groupBindParts.ts +28 -0
- package/src/lib/ui/compile/hoistCells.ts +120 -0
- package/src/lib/ui/compile/loadShadowTsConfig.ts +31 -0
- package/src/lib/ui/compile/lowerDocAccess.ts +202 -0
- package/src/lib/ui/compile/nearestProjectRoot.ts +16 -0
- package/src/lib/ui/compile/parseTemplate.ts +396 -0
- package/src/lib/ui/compile/partitionSlots.ts +36 -0
- package/src/lib/ui/compile/prepareNestedScript.ts +42 -0
- package/src/lib/ui/compile/remapShadowDiagnostic.ts +30 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +85 -0
- package/src/lib/ui/compile/resolveAbideImports.ts +29 -0
- package/src/lib/ui/compile/scopeCss.ts +115 -0
- package/src/lib/ui/compile/shadowNaming.ts +11 -0
- package/src/lib/ui/compile/sourceToShadowOffset.ts +24 -0
- package/src/lib/ui/compile/staticAttrValue.ts +13 -0
- package/src/lib/ui/compile/stripEffects.ts +32 -0
- package/src/lib/ui/compile/types/AbideDiagnostic.ts +14 -0
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +25 -0
- package/src/lib/ui/compile/types/CompiledShadow.ts +15 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +16 -0
- package/src/lib/ui/compile/types/TemplateNode.ts +78 -0
- package/src/lib/ui/compile/types/TextPart.ts +8 -0
- package/src/lib/ui/derived.ts +28 -0
- package/src/lib/ui/doc.ts +15 -0
- package/src/lib/ui/dom/appendSnippet.ts +34 -0
- package/src/lib/ui/dom/appendStatic.ts +27 -0
- package/src/lib/ui/dom/appendText.ts +114 -0
- package/src/lib/ui/dom/applyResolved.ts +72 -0
- package/src/lib/ui/dom/attach.ts +20 -0
- package/src/lib/ui/dom/attr.ts +19 -0
- package/src/lib/ui/dom/awaitBlock.ts +224 -0
- package/src/lib/ui/dom/cloneStatic.ts +52 -0
- package/src/lib/ui/dom/each.ts +115 -0
- package/src/lib/ui/dom/eachAsync.ts +153 -0
- package/src/lib/ui/dom/hydrate.ts +35 -0
- package/src/lib/ui/dom/mount.ts +29 -0
- package/src/lib/ui/dom/mountChild.ts +33 -0
- package/src/lib/ui/dom/on.ts +15 -0
- package/src/lib/ui/dom/openChild.ts +22 -0
- package/src/lib/ui/dom/openRoot.ts +20 -0
- package/src/lib/ui/dom/switchBlock.ts +75 -0
- package/src/lib/ui/dom/text.ts +20 -0
- package/src/lib/ui/dom/tryBlock.ts +112 -0
- package/src/lib/ui/dom/types/EachRow.ts +3 -0
- package/src/lib/ui/dom/types/SwitchCase.ts +6 -0
- package/src/lib/ui/dom/when.ts +73 -0
- package/src/lib/ui/effect.ts +16 -0
- package/src/lib/ui/installHotBridge.ts +73 -0
- package/src/lib/ui/matchRoute.ts +89 -0
- package/src/lib/ui/navigate.ts +17 -0
- package/src/lib/ui/probeNavigation.ts +33 -0
- package/src/lib/ui/remoteProxy.ts +97 -0
- package/src/lib/ui/renderChain.ts +50 -0
- package/src/lib/ui/renderToStream.ts +104 -0
- package/src/lib/ui/router.ts +286 -0
- package/src/lib/ui/runtime/OUTLET_TAG.ts +8 -0
- package/src/lib/ui/runtime/OWNER.ts +8 -0
- package/src/lib/ui/runtime/REACTIVE_CONTEXT.ts +14 -0
- package/src/lib/ui/runtime/RENDER.ts +23 -0
- package/src/lib/ui/runtime/RESUME.ts +16 -0
- package/src/lib/ui/runtime/applyPatchToTree.ts +41 -0
- package/src/lib/ui/runtime/claimChild.ts +10 -0
- package/src/lib/ui/runtime/clientPage.ts +16 -0
- package/src/lib/ui/runtime/createComputedNode.ts +16 -0
- package/src/lib/ui/runtime/createDoc.ts +177 -0
- package/src/lib/ui/runtime/createEffectNode.ts +58 -0
- package/src/lib/ui/runtime/createSignalNode.ts +16 -0
- package/src/lib/ui/runtime/detachLink.ts +21 -0
- package/src/lib/ui/runtime/endTracking.ts +24 -0
- package/src/lib/ui/runtime/enterRenderPass.ts +12 -0
- package/src/lib/ui/runtime/exitRenderPass.ts +7 -0
- package/src/lib/ui/runtime/firstOutlet.ts +22 -0
- package/src/lib/ui/runtime/flushEffects.ts +17 -0
- package/src/lib/ui/runtime/hotInstances.ts +10 -0
- package/src/lib/ui/runtime/hotReloadEnabled.ts +8 -0
- package/src/lib/ui/runtime/hotReplace.ts +25 -0
- package/src/lib/ui/runtime/nextBlockId.ts +11 -0
- package/src/lib/ui/runtime/pathExists.ts +23 -0
- package/src/lib/ui/runtime/readNode.ts +17 -0
- package/src/lib/ui/runtime/registerHotInstance.ts +23 -0
- package/src/lib/ui/runtime/runNode.ts +28 -0
- package/src/lib/ui/runtime/runtimePath.ts +9 -0
- package/src/lib/ui/runtime/scope.ts +24 -0
- package/src/lib/ui/runtime/toTeardown.ts +26 -0
- package/src/lib/ui/runtime/track.ts +58 -0
- package/src/lib/ui/runtime/trigger.ts +44 -0
- package/src/lib/ui/runtime/types/Cell.ts +5 -0
- package/src/lib/ui/runtime/types/Derived.ts +3 -0
- package/src/lib/ui/runtime/types/Doc.ts +19 -0
- package/src/lib/ui/runtime/types/EffectResult.ts +10 -0
- package/src/lib/ui/runtime/types/HotInstance.ts +14 -0
- package/src/lib/ui/runtime/types/NavVerdict.ts +9 -0
- package/src/lib/ui/runtime/types/Patch.ts +11 -0
- package/src/lib/ui/runtime/types/ReactiveLink.ts +21 -0
- package/src/lib/ui/runtime/types/ReactiveNode.ts +25 -0
- package/src/lib/ui/runtime/types/Route.ts +8 -0
- package/src/lib/ui/runtime/types/RouteLoader.ts +7 -0
- package/src/lib/ui/runtime/types/SsrRender.ts +22 -0
- package/src/lib/ui/runtime/types/State.ts +3 -0
- package/src/lib/ui/runtime/types/Teardown.ts +5 -0
- package/src/lib/ui/runtime/types/UiComponent.ts +16 -0
- package/src/lib/ui/runtime/types/UiProps.ts +15 -0
- package/src/lib/ui/runtime/unlinkDeps.ts +20 -0
- package/src/lib/ui/runtime/untrack.ts +20 -0
- package/src/lib/ui/runtime/valueAtPath.ts +18 -0
- package/src/lib/ui/runtime/writeNode.ts +16 -0
- package/src/lib/ui/socketChannel.ts +227 -0
- package/src/lib/ui/socketProxy.ts +25 -0
- package/src/lib/ui/startClient.ts +58 -0
- package/src/lib/ui/state.ts +25 -0
- package/src/lib/ui/tail.ts +324 -0
- package/src/lib/ui/types/Layouts.ts +9 -0
- package/src/lib/ui/types/Pages.ts +8 -0
- package/src/preload.ts +19 -0
- package/src/scaffold.ts +153 -0
- package/src/serverBuildPlugins.ts +19 -0
- package/src/serverEntry.ts +95 -0
- package/template/bunfig.toml +4 -0
- package/template/package.json +18 -0
- package/template/src/app.ts +28 -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 +36 -0
- package/template/src/ui/Layout.abide +19 -0
- package/template/src/ui/app.css +21 -0
- package/template/src/ui/app.html +24 -0
- package/template/src/ui/pages/about/page.abide +9 -0
- package/template/src/ui/pages/page.abide +22 -0
- package/template/test/app.test.ts +30 -0
- package/template/tsconfig.json +18 -0
- package/tsconfig.app.json +17 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Server } from 'bun'
|
|
2
|
+
|
|
3
|
+
// Ports tried upward from `start` before giving up and letting the kernel assign one.
|
|
4
|
+
const SCAN_RANGE = 100
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Binds the real server, scanning upward from `start` for the first free port.
|
|
8
|
+
The listener that wins a port is the one that keeps it: unlike probing a
|
|
9
|
+
throwaway server and releasing it before the real bind, this leaves no window
|
|
10
|
+
for the chosen port to be stolen in between — the gap that crashed boot on
|
|
11
|
+
EADDRINUSE instead of stepping to the next port. `bindAt` does the actual
|
|
12
|
+
Bun.serve; only an in-use port is retried, any other failure propagates. After
|
|
13
|
+
SCAN_RANGE occupied ports it binds port 0 so the kernel assigns any free port.
|
|
14
|
+
*/
|
|
15
|
+
export function listenOnOpenPort(
|
|
16
|
+
bindAt: (port: number) => Server<unknown>,
|
|
17
|
+
start: number,
|
|
18
|
+
): Server<unknown> {
|
|
19
|
+
for (let port = start; port < start + SCAN_RANGE; port++) {
|
|
20
|
+
try {
|
|
21
|
+
return bindAt(port)
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (!isAddressInUse(error)) {
|
|
24
|
+
throw error
|
|
25
|
+
}
|
|
26
|
+
// port in use — try the next one up
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// every candidate was taken; bind to 0 so the kernel picks a free port
|
|
30
|
+
return bindAt(0)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Bun reports a taken port as an Error carrying code 'EADDRINUSE'.
|
|
34
|
+
function isAddressInUse(error: unknown): boolean {
|
|
35
|
+
return error instanceof Error && (error as { code?: string }).code === 'EADDRINUSE'
|
|
36
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { abideLog } from '../../shared/abideLog.ts'
|
|
2
|
+
import type { Pages } from '../../ui/types/Pages.ts'
|
|
3
|
+
import { verbRegistry } from '../rpc/verbRegistry.ts'
|
|
4
|
+
import { socketRegistry } from '../sockets/socketRegistry.ts'
|
|
5
|
+
import { ensureRegistriesLoaded } from './registryManifests.ts'
|
|
6
|
+
|
|
7
|
+
// Cell glyphs: PRESENT, ABSENT.
|
|
8
|
+
const PRESENT = '✓'
|
|
9
|
+
const ABSENT = '·'
|
|
10
|
+
const COLUMN_GAP = 2
|
|
11
|
+
|
|
12
|
+
const hasColor = typeof Bun !== 'undefined' && Bun.enableANSIColors
|
|
13
|
+
// Red foreground then `\x1b[39m` (default-foreground, not full reset) so the enclosing dim survives.
|
|
14
|
+
const redden = (text: string): string =>
|
|
15
|
+
hasColor ? `${Bun.color('red', 'ansi-256')}${text}\x1b[39m` : text
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
A declared inputSchema is what makes mcp/cli safe to advertise (see defineVerb /
|
|
19
|
+
defineSocket), so a missing schema gets a red `·` to flag the declaration whose
|
|
20
|
+
machine surfaces are gated behind it.
|
|
21
|
+
*/
|
|
22
|
+
const schemaCell = (hasSchema: boolean): string => (hasSchema ? PRESENT : redden(ABSENT))
|
|
23
|
+
const flag = (on: boolean): string => (on ? PRESENT : ABSENT)
|
|
24
|
+
|
|
25
|
+
// Display width ignoring ANSI color escapes, so colored glyphs don't inflate alignment.
|
|
26
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: the ESC (\x1b) is the intended match — stripping the color escape to measure visible width
|
|
27
|
+
const displayWidth = (cell: string): number => cell.replace(/\x1b\[[0-9;]*m/g, '').length
|
|
28
|
+
|
|
29
|
+
// A titled table: its header row plus data rows, all sharing the column layout below.
|
|
30
|
+
type SurfaceTable = { title: string; header: string[]; rows: string[][] }
|
|
31
|
+
|
|
32
|
+
// Per-column width: the widest cell's display width across every row given.
|
|
33
|
+
function columnWidths(rows: string[][]): number[] {
|
|
34
|
+
const columnCount = Math.max(...rows.map((row) => row.length))
|
|
35
|
+
return Array.from({ length: columnCount }, (_, column) =>
|
|
36
|
+
Math.max(...rows.map((row) => displayWidth(row[column] ?? ''))),
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
Left-aligns one row's cells into the given fixed column widths plus a gap.
|
|
42
|
+
Padding is explicit spaces measured by displayWidth so embedded color escapes
|
|
43
|
+
don't inflate the width and break alignment. Two-space indented.
|
|
44
|
+
*/
|
|
45
|
+
function renderRow(row: string[], widths: number[]): string {
|
|
46
|
+
return (
|
|
47
|
+
' ' +
|
|
48
|
+
row
|
|
49
|
+
.map((cell, column) => {
|
|
50
|
+
/* widths covers every column (computed from the longest row); the fallback
|
|
51
|
+
exists for noUncheckedIndexedAccess in consumer tsconfigs and pads only the gap. */
|
|
52
|
+
const width = widths[column] ?? displayWidth(cell)
|
|
53
|
+
return cell + ' '.repeat(width - displayWidth(cell) + COLUMN_GAP)
|
|
54
|
+
})
|
|
55
|
+
.join('')
|
|
56
|
+
.trimEnd()
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
Prints every non-empty table under one shared set of column widths, so the
|
|
62
|
+
identifier and glyph columns line up vertically across the page/socket/rpc
|
|
63
|
+
tables rather than each table aligning only within itself. Each table keeps its
|
|
64
|
+
own dim header row.
|
|
65
|
+
*/
|
|
66
|
+
function logTables(tables: SurfaceTable[]): void {
|
|
67
|
+
const present = tables.filter((table) => table.rows.length > 0)
|
|
68
|
+
if (present.length === 0) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
const widths = columnWidths(present.flatMap((table) => [table.header, ...table.rows]))
|
|
72
|
+
present.forEach((table) => {
|
|
73
|
+
abideLog.info(`${table.title}:`)
|
|
74
|
+
abideLog.detail(
|
|
75
|
+
[table.header, ...table.rows].map((row) => renderRow(row, widths)).join('\n'),
|
|
76
|
+
)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
Boot-time surface map: every page, socket, and rpc with the surfaces it is
|
|
82
|
+
exposed on, so abide's routing and multimodal-by-default exposure are auditable
|
|
83
|
+
rather than implicit. Three aligned tables — scan a column to spot a missing
|
|
84
|
+
surface, a row to see one declaration's reach:
|
|
85
|
+
|
|
86
|
+
- pages: each route with the nearest layout and error boundary wrapping it.
|
|
87
|
+
- sockets: name + client surfaces (schema/browser/mcp/cli) and whether clients
|
|
88
|
+
may publish.
|
|
89
|
+
- rpcs: method+path (headed `http` since http/openapi are unconditional) +
|
|
90
|
+
per-declaration client surfaces.
|
|
91
|
+
|
|
92
|
+
All three render under one shared column grid (see logTables): the identifier
|
|
93
|
+
leads flush-left in every table — page route, socket name, or rpc method+path —
|
|
94
|
+
then the surface columns, so the glyphs line up vertically across the tables.
|
|
95
|
+
rpc folds its method into a left-aligned prefix of the identifier cell, so paths
|
|
96
|
+
still start at a shared column. For sockets and rpcs the `schema`
|
|
97
|
+
column leads: it's what unlocks the non-browser surfaces, so a missing schema
|
|
98
|
+
reddens to flag the gated declaration. Loads the full registry, so it runs once
|
|
99
|
+
at boot and only under `abide` debug logging (DEBUG=abide) to avoid forcing
|
|
100
|
+
eager imports in production. Best-effort: enumeration failures are swallowed,
|
|
101
|
+
this is diagnostic only.
|
|
102
|
+
*/
|
|
103
|
+
export async function logExposedSurfaces(routing: { pages: Pages }): Promise<void> {
|
|
104
|
+
try {
|
|
105
|
+
await ensureRegistriesLoaded()
|
|
106
|
+
} catch {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const pageRows = Object.keys(routing.pages)
|
|
111
|
+
.map((route) => [route])
|
|
112
|
+
.sort()
|
|
113
|
+
|
|
114
|
+
const socketRows = Array.from(socketRegistry.values(), (entry) => [
|
|
115
|
+
entry.socket.name,
|
|
116
|
+
schemaCell(Boolean(entry.schema)),
|
|
117
|
+
flag(entry.clients.browser),
|
|
118
|
+
flag(entry.clients.mcp),
|
|
119
|
+
flag(entry.clients.cli),
|
|
120
|
+
flag(entry.allowClientPublish),
|
|
121
|
+
]).sort()
|
|
122
|
+
|
|
123
|
+
/*
|
|
124
|
+
rpc identifier = method left-aligned to a shared width then the path, so the
|
|
125
|
+
methods line up and every path starts at the same column — while the cell as
|
|
126
|
+
a whole still leads flush-left like the page route and socket name.
|
|
127
|
+
*/
|
|
128
|
+
const methodWidth = Math.max(
|
|
129
|
+
'http'.length,
|
|
130
|
+
...Array.from(verbRegistry.values(), (entry) => entry.remote.method.length),
|
|
131
|
+
)
|
|
132
|
+
const withMethod = (method: string, identifier: string): string =>
|
|
133
|
+
method.padEnd(methodWidth + COLUMN_GAP) + identifier
|
|
134
|
+
|
|
135
|
+
const rpcRows = Array.from(verbRegistry.values(), (entry) => [
|
|
136
|
+
withMethod(entry.remote.method, entry.remote.url),
|
|
137
|
+
schemaCell(Boolean(entry.inputSchema)),
|
|
138
|
+
flag(entry.clients.browser),
|
|
139
|
+
flag(entry.clients.mcp),
|
|
140
|
+
flag(entry.clients.cli),
|
|
141
|
+
]).sort()
|
|
142
|
+
|
|
143
|
+
logTables([
|
|
144
|
+
{ title: 'pages', header: ['page'], rows: pageRows },
|
|
145
|
+
{
|
|
146
|
+
title: 'sockets',
|
|
147
|
+
header: ['socket', 'schema', 'browser', 'mcp', 'cli', 'publish'],
|
|
148
|
+
rows: socketRows,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
title: 'rpcs',
|
|
152
|
+
header: [withMethod('http', ''), 'schema', 'browser', 'mcp', 'cli'],
|
|
153
|
+
rows: rpcRows,
|
|
154
|
+
},
|
|
155
|
+
])
|
|
156
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { abideLog } from '../../shared/abideLog.ts'
|
|
2
|
+
import { logTapSlot } from '../../shared/logTapSlot.ts'
|
|
3
|
+
import { requestScopeSlot } from '../../shared/requestScopeSlot.ts'
|
|
4
|
+
import { socketTapSlot } from '../../shared/socketTapSlot.ts'
|
|
5
|
+
import type { LogRecord } from '../../shared/types/LogRecord.ts'
|
|
6
|
+
import { buildCacheSnapshot } from './buildCacheSnapshot.ts'
|
|
7
|
+
import { buildInspectorSurface } from './buildInspectorSurface.ts'
|
|
8
|
+
import { ensureRegistriesLoaded } from './registryManifests.ts'
|
|
9
|
+
import type { InspectorContext } from './types/InspectorContext.ts'
|
|
10
|
+
|
|
11
|
+
/* The request handler `@abide/inspector` returns — serves its UI + data routes. */
|
|
12
|
+
type InspectorHandler = (request: Request, url: URL) => Promise<Response>
|
|
13
|
+
|
|
14
|
+
/* Shapes a published socket frame as a log record so the inspector's one feed
|
|
15
|
+
carries it like any other event — on the `socket` channel, tied to the active
|
|
16
|
+
trace when the publish happened inside a request (e.g. an RPC broadcasting). */
|
|
17
|
+
function socketFrameRecord(frame: { socket: string; message: unknown }): LogRecord {
|
|
18
|
+
const scope = requestScopeSlot.resolver?.()
|
|
19
|
+
return {
|
|
20
|
+
ts: Date.now(),
|
|
21
|
+
level: 'info',
|
|
22
|
+
channel: 'socket',
|
|
23
|
+
msg: frame.socket,
|
|
24
|
+
data: frame.message,
|
|
25
|
+
trace: scope?.trace.traceId,
|
|
26
|
+
elapsedMs: scope?.elapsedMs,
|
|
27
|
+
method: scope?.method,
|
|
28
|
+
path: scope?.path,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
Non-literal so Bun's bundler can't fold the optional package into a compiled
|
|
34
|
+
binary: the import stays external and resolves only at runtime, only when the
|
|
35
|
+
package is actually installed.
|
|
36
|
+
*/
|
|
37
|
+
const INSPECTOR_PACKAGE = '@abide/inspector'
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
Activates the opt-in inspector when ABIDE_ENABLE_INSPECTOR=true. The flag is the
|
|
41
|
+
activation switch; the package is installed explicitly (`bun add -d
|
|
42
|
+
@abide/inspector`) — core never installs anything. Returns the package's request
|
|
43
|
+
handler for createServer to route, or undefined when the flag is off or the
|
|
44
|
+
package isn't present (with a one-line install hint in that case).
|
|
45
|
+
|
|
46
|
+
The inspector exposes all traffic and the whole machine surface, so it's gated
|
|
47
|
+
behind the operator's explicit flag and announces itself loudly on mount —
|
|
48
|
+
enable it only in trusted/dev environments (mirrors warnUnguardedMcp).
|
|
49
|
+
*/
|
|
50
|
+
export async function maybeMountInspector(app: {
|
|
51
|
+
name: string
|
|
52
|
+
version: string
|
|
53
|
+
}): Promise<InspectorHandler | undefined> {
|
|
54
|
+
if (process.env.ABIDE_ENABLE_INSPECTOR !== 'true') {
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
const imported = (await import(INSPECTOR_PACKAGE).catch(() => undefined)) as
|
|
58
|
+
| { mountInspector: (context: InspectorContext) => InspectorHandler }
|
|
59
|
+
| undefined
|
|
60
|
+
if (!imported) {
|
|
61
|
+
abideLog.warn(
|
|
62
|
+
`ABIDE_ENABLE_INSPECTOR=true but ${INSPECTOR_PACKAGE} isn't installed — run \`bun add -d ${INSPECTOR_PACKAGE}\``,
|
|
63
|
+
)
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
const context: InspectorContext = {
|
|
67
|
+
app,
|
|
68
|
+
loadSurface: async () => {
|
|
69
|
+
await ensureRegistriesLoaded()
|
|
70
|
+
return buildInspectorSurface()
|
|
71
|
+
},
|
|
72
|
+
cacheSnapshot: buildCacheSnapshot,
|
|
73
|
+
/*
|
|
74
|
+
One process-wide tap each for the log and socket chokepoints; the
|
|
75
|
+
inspector owns both and fans out to its readers. Socket frames arrive
|
|
76
|
+
shaped as `socket`-channel records, so the inspector sees a single
|
|
77
|
+
uniform event stream.
|
|
78
|
+
*/
|
|
79
|
+
onRecord: (listener: (record: LogRecord) => void) => {
|
|
80
|
+
const onFrame = (frame: { socket: string; message: unknown }) =>
|
|
81
|
+
listener(socketFrameRecord(frame))
|
|
82
|
+
logTapSlot.tap = listener
|
|
83
|
+
socketTapSlot.tap = onFrame
|
|
84
|
+
return () => {
|
|
85
|
+
if (logTapSlot.tap === listener) {
|
|
86
|
+
logTapSlot.tap = undefined
|
|
87
|
+
}
|
|
88
|
+
if (socketTapSlot.tap === onFrame) {
|
|
89
|
+
socketTapSlot.tap = undefined
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
const handler = imported.mountInspector(context)
|
|
95
|
+
abideLog.warn('inspector enabled — exposes all traffic and the full surface; trusted use only')
|
|
96
|
+
return handler
|
|
97
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Derives the MIME type from a URL pathname using Bun.file().type, which
|
|
3
|
+
operates on the file extension synchronously without touching the disk. The
|
|
4
|
+
Bun.file ref here is never read from — it exists only to reuse Bun's
|
|
5
|
+
extension-to-MIME table. Cache by extension so repeat hits for the same
|
|
6
|
+
chunk type (.js / .css / .map / .svg / …) skip the BunFile allocation.
|
|
7
|
+
*/
|
|
8
|
+
const mimeByExtension = new Map<string, string>()
|
|
9
|
+
|
|
10
|
+
export function mimeForExtension(pathname: string): string {
|
|
11
|
+
const dot = pathname.lastIndexOf('.')
|
|
12
|
+
const extension = dot === -1 ? '' : pathname.slice(dot)
|
|
13
|
+
return mimeByExtension.getOrInsertComputed(extension, () => Bun.file(pathname).type)
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { withBaseUrl } from '../../shared/withBaseUrl.ts'
|
|
2
|
+
import type { RequestStore } from './types/RequestStore.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The browser-space URL `page.url` publishes for a request: store.url with the
|
|
6
|
+
mount base re-applied, matching what window.location shows under a proxy
|
|
7
|
+
mount, so an active-state compare against url() output hydrates identically.
|
|
8
|
+
store.url itself stays app-space (base-stripped by the proxy) for routing and
|
|
9
|
+
error-prefix matching. Memoized on the store so repeated page-proxy reads
|
|
10
|
+
share one URL.
|
|
11
|
+
*/
|
|
12
|
+
export function pageUrlFromStore(store: RequestStore): URL {
|
|
13
|
+
store.pageUrl ??= withBaseUrl(store.url)
|
|
14
|
+
return store.pageUrl
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { parseBoundedEnvInt } from '../../shared/parseBoundedEnvInt.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Parses ABIDE_IDLE_TIMEOUT into Bun's per-connection idle timeout in seconds.
|
|
5
|
+
Bun accepts 0–255 (0 disables the timeout); returns undefined for missing,
|
|
6
|
+
empty, or out-of-range/non-integer input so the caller keeps its default.
|
|
7
|
+
*/
|
|
8
|
+
export function parseIdleTimeout(value: string | undefined): number | undefined {
|
|
9
|
+
return parseBoundedEnvInt(value, 0, 255)
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { parseBoundedEnvInt } from '../../shared/parseBoundedEnvInt.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Parses a PORT env value into a usable TCP port (0–65535), returning undefined
|
|
5
|
+
for missing, empty, or out-of-range/non-integer input so the caller can fall
|
|
6
|
+
back to a default. A bare Number() would turn '' into 0 (a random
|
|
7
|
+
kernel-assigned port) and 'abc' into NaN, both silently wrong.
|
|
8
|
+
*/
|
|
9
|
+
export function parsePort(value: string | undefined): number | undefined {
|
|
10
|
+
return parseBoundedEnvInt(value, 0, 65535)
|
|
11
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { PromptRoutes } from '../prompts/types/PromptRoutes.ts'
|
|
2
|
+
import type { RemoteRoutes } from '../rpc/types/RemoteRoutes.ts'
|
|
3
|
+
import type { SocketRoutes } from '../sockets/types/SocketRoutes.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Process-wide slot for the rpc + sockets + prompts manifests. createServer
|
|
7
|
+
assigns once at boot (right after the route table is built); the MCP
|
|
8
|
+
server, the OpenAPI emitter, and prompt enumeration read it on first
|
|
9
|
+
request so they can lazy-import every module and walk the
|
|
10
|
+
verb/socket/prompt registries.
|
|
11
|
+
|
|
12
|
+
The slot pattern (mirrors getActiveServer) lets the framework-generated
|
|
13
|
+
McpServer bind to the manifests at module scope while the loaders stay
|
|
14
|
+
lazy until the first enumeration request.
|
|
15
|
+
*/
|
|
16
|
+
type RegistryManifests = {
|
|
17
|
+
rpc: RemoteRoutes
|
|
18
|
+
sockets: SocketRoutes
|
|
19
|
+
prompts: PromptRoutes
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let manifests: RegistryManifests | undefined
|
|
23
|
+
let loading: Promise<void> | undefined
|
|
24
|
+
|
|
25
|
+
export function setRegistryManifests(value: RegistryManifests): void {
|
|
26
|
+
manifests = value
|
|
27
|
+
loading = undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
On first call, eagerly imports every rpc + socket + prompt module so
|
|
32
|
+
defineVerb / defineSocket / definePrompt fire and populate the
|
|
33
|
+
registries. Idempotent — repeat calls reuse the same in-flight promise,
|
|
34
|
+
so concurrent first requests (e.g. /openapi.json + an MCP tools/list)
|
|
35
|
+
trigger exactly one load instead of racing to fire the full import set
|
|
36
|
+
each. Eager loading is acceptable here because enumeration (MCP
|
|
37
|
+
tool/resource/prompt lists, the OpenAPI document) fundamentally requires
|
|
38
|
+
the full surface; the alternative of per-call lazy loading produces flaky
|
|
39
|
+
first-call latency.
|
|
40
|
+
*/
|
|
41
|
+
export function ensureRegistriesLoaded(): Promise<void> {
|
|
42
|
+
if (!manifests) {
|
|
43
|
+
return Promise.resolve()
|
|
44
|
+
}
|
|
45
|
+
if (!loading) {
|
|
46
|
+
const { rpc, sockets, prompts } = manifests
|
|
47
|
+
loading = Promise.all([
|
|
48
|
+
...Object.values(rpc).map((loader) => loader()),
|
|
49
|
+
...Object.values(sockets).map((loader) => loader()),
|
|
50
|
+
...Object.values(prompts).map((loader) => loader()),
|
|
51
|
+
])
|
|
52
|
+
.then(() => undefined)
|
|
53
|
+
/*
|
|
54
|
+
Clear the memo on failure so a transient import error (a
|
|
55
|
+
module that throws at load, fixed by the next HMR pass)
|
|
56
|
+
doesn't poison every later enumeration request for the
|
|
57
|
+
process lifetime. The rejection still propagates to this
|
|
58
|
+
caller; the reset only affects subsequent calls.
|
|
59
|
+
*/
|
|
60
|
+
.catch((error) => {
|
|
61
|
+
loading = undefined
|
|
62
|
+
throw error
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
return loading
|
|
66
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PageSnapshot } from '../../shared/types/PageSnapshot.ts'
|
|
2
|
+
import { pageUrlFromStore } from './pageUrlFromStore.ts'
|
|
3
|
+
import { requestContext } from './requestContext.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The server's page resolver: the `page` proxy reads route/params/url off the
|
|
7
|
+
ALS request store, so layout-scoped components see the live match during SSR
|
|
8
|
+
without a module singleton leaking across concurrent or streaming renders.
|
|
9
|
+
route/params land just before render; url is set at the request boundary, so
|
|
10
|
+
404/error renders still get a correct page.url. Registered by serverEntry at
|
|
11
|
+
boot and mirrored by bootTestServer; undefined outside a request scope falls
|
|
12
|
+
through to activePage's empty fallback.
|
|
13
|
+
*/
|
|
14
|
+
export function resolvePageSnapshot(): PageSnapshot | undefined {
|
|
15
|
+
const store = requestContext.getStore()
|
|
16
|
+
if (!store) {
|
|
17
|
+
return undefined
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
route: store.route ?? '',
|
|
21
|
+
params: store.params ?? {},
|
|
22
|
+
url: pageUrlFromStore(store),
|
|
23
|
+
navigating: false,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Serves one compile-time-embedded asset: a gzip-capable client gets the stored
|
|
3
|
+
bytes as-is, anyone else gets them decompressed on the fly. Shared by the
|
|
4
|
+
`_app` and public/ asset servers so the decompress fallback lives once.
|
|
5
|
+
*/
|
|
6
|
+
export function respondWithEmbeddedAsset(
|
|
7
|
+
compressed: Uint8Array<ArrayBuffer>,
|
|
8
|
+
wantsGzip: boolean,
|
|
9
|
+
headers: { base: HeadersInit; gzip: HeadersInit },
|
|
10
|
+
): Response {
|
|
11
|
+
if (wantsGzip) {
|
|
12
|
+
return new Response(compressed, { headers: headers.gzip })
|
|
13
|
+
}
|
|
14
|
+
/* gunzipSync's Buffer is freshly allocated over a plain ArrayBuffer; @types/bun widens it to ArrayBufferLike, which BodyInit rejects. */
|
|
15
|
+
return new Response(Bun.gunzipSync(compressed) as Uint8Array<ArrayBuffer>, {
|
|
16
|
+
headers: headers.base,
|
|
17
|
+
})
|
|
18
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { abideLog } from '../../shared/abideLog.ts'
|
|
2
|
+
import { createCacheStore } from '../../shared/createCacheStore.ts'
|
|
3
|
+
import { createTraceContext } from '../../shared/createTraceContext.ts'
|
|
4
|
+
import { formatTraceparent } from '../../shared/formatTraceparent.ts'
|
|
5
|
+
import { isStreamingResponse } from '../../shared/isStreamingResponse.ts'
|
|
6
|
+
import { logClosingRecord } from '../../shared/logClosingRecord.ts'
|
|
7
|
+
import type { AppModule } from '../AppModule.ts'
|
|
8
|
+
import { internalErrorResponse } from './internalErrorResponse.ts'
|
|
9
|
+
import { requestContext } from './requestContext.ts'
|
|
10
|
+
import type { RequestStore } from './types/RequestStore.ts'
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
Establishes the per-request scope and runs `body` inside it: a fresh
|
|
14
|
+
CacheStore, the request's trace position and start time, and request metadata
|
|
15
|
+
published through the AsyncLocalStorage RequestStore (so cache(), request()/
|
|
16
|
+
server(), trace(), and log prefixes resolve without threading args), the
|
|
17
|
+
app's handleError — or the framework's 500 fallback — on a thrown body, and
|
|
18
|
+
optional request logging. The single seam every dynamic route crosses;
|
|
19
|
+
extracted from createServer so the scope, error, and logging behaviour is
|
|
20
|
+
exercisable through this interface without booting a Bun server.
|
|
21
|
+
*/
|
|
22
|
+
export function runWithRequestScope(
|
|
23
|
+
req: Request,
|
|
24
|
+
/* `url` skips the WHATWG re-parse when the caller already parsed it (the fetch fallback). */
|
|
25
|
+
options: { app?: AppModule; logRequests: boolean; url?: URL },
|
|
26
|
+
body: (store: RequestStore) => Promise<Response>,
|
|
27
|
+
): Promise<Response> {
|
|
28
|
+
const url = options.url ?? new URL(req.url)
|
|
29
|
+
const store: RequestStore = {
|
|
30
|
+
url,
|
|
31
|
+
req,
|
|
32
|
+
cache: createCacheStore(),
|
|
33
|
+
trace: createTraceContext(req.headers.get('traceparent')),
|
|
34
|
+
start: Bun.nanoseconds(),
|
|
35
|
+
}
|
|
36
|
+
return requestContext.run(store, async () => {
|
|
37
|
+
let response: Response
|
|
38
|
+
try {
|
|
39
|
+
response = await body(store)
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (options.app?.handleError) {
|
|
42
|
+
response = await options.app.handleError(error, req)
|
|
43
|
+
} else {
|
|
44
|
+
abideLog.error(error)
|
|
45
|
+
response = internalErrorResponse(error)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/*
|
|
49
|
+
Flush any cookies the handler set onto the outgoing response. Only when
|
|
50
|
+
a jar was materialized (cookies() was called) and only via append, so a
|
|
51
|
+
Set-Cookie the handler already placed on the response init survives.
|
|
52
|
+
*/
|
|
53
|
+
if (store.cookies) {
|
|
54
|
+
const setCookies = store.cookies.toSetCookieHeaders()
|
|
55
|
+
if (setCookies.length > 0) {
|
|
56
|
+
try {
|
|
57
|
+
setCookies.forEach((header) => {
|
|
58
|
+
response.headers.append('set-cookie', header)
|
|
59
|
+
})
|
|
60
|
+
} catch {
|
|
61
|
+
/* A passthrough Response (proxied fetch) carries immutable headers;
|
|
62
|
+
rebuild it with a mutable copy so the cookies still ship rather
|
|
63
|
+
than the append throwing and 500ing the successful response. */
|
|
64
|
+
response = new Response(response.body, response)
|
|
65
|
+
setCookies.forEach((header) => {
|
|
66
|
+
response.headers.append('set-cookie', header)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/*
|
|
72
|
+
Server-Timing is always-on (identical dev/build): `total` is what's
|
|
73
|
+
knowable at header time — for streamed responses the settled total
|
|
74
|
+
belongs to the closing log record — and the `traceparent` entry is the
|
|
75
|
+
one header browsers expose to page JS, so RUM tooling links the
|
|
76
|
+
frontend to this trace. Guarded: a passthrough Response (proxied
|
|
77
|
+
fetch) carries immutable headers.
|
|
78
|
+
*/
|
|
79
|
+
const headerMs = (Bun.nanoseconds() - store.start) / 1e6
|
|
80
|
+
const stats = store.cache.stats
|
|
81
|
+
const cacheEntry =
|
|
82
|
+
stats.hits + stats.misses + stats.coalesced > 0
|
|
83
|
+
? `, cache;desc="hits=${stats.hits} misses=${stats.misses} coalesced=${stats.coalesced}"`
|
|
84
|
+
: ''
|
|
85
|
+
try {
|
|
86
|
+
response.headers.set(
|
|
87
|
+
'server-timing',
|
|
88
|
+
`total;dur=${headerMs.toFixed(1)}, traceparent;desc="${formatTraceparent(store.trace)}"${cacheEntry}`,
|
|
89
|
+
)
|
|
90
|
+
} catch {
|
|
91
|
+
/* Immutable headers — skip; the closing log record still reports. */
|
|
92
|
+
}
|
|
93
|
+
if (!options.logRequests) {
|
|
94
|
+
return response
|
|
95
|
+
}
|
|
96
|
+
/*
|
|
97
|
+
The closing record logs at settle so its elapsed IS the total. Buffered
|
|
98
|
+
responses settle here; streaming bodies (SSE/JSONL) settle when the
|
|
99
|
+
stream ends — re-entered into the request scope because stream
|
|
100
|
+
callbacks don't inherit the ALS context.
|
|
101
|
+
*/
|
|
102
|
+
let settled = false
|
|
103
|
+
const emitClose = () => {
|
|
104
|
+
if (settled) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
settled = true
|
|
108
|
+
requestContext.run(store, () => {
|
|
109
|
+
const ms = (Bun.nanoseconds() - store.start) / 1e6
|
|
110
|
+
/* Copy freezes the tallies at settle — the live object keeps counting if late reads land. */
|
|
111
|
+
logClosingRecord(req.method, `${url.pathname}${url.search}`, response.status, ms, {
|
|
112
|
+
...store.cache.stats,
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
if (response.body && isStreamingResponse(response)) {
|
|
117
|
+
/*
|
|
118
|
+
A hand-pumped wrap rather than a TransformStream: streams typically
|
|
119
|
+
end by client disconnect (an SSE tab closing), which cancels the
|
|
120
|
+
readable without ever flushing a transform — Bun also skips the
|
|
121
|
+
transformer's cancel hook — so only this readable's own close/error/
|
|
122
|
+
cancel paths see every termination.
|
|
123
|
+
*/
|
|
124
|
+
const reader = response.body.getReader()
|
|
125
|
+
const monitored = new ReadableStream({
|
|
126
|
+
async pull(controller) {
|
|
127
|
+
try {
|
|
128
|
+
const { done, value } = await reader.read()
|
|
129
|
+
if (done) {
|
|
130
|
+
emitClose()
|
|
131
|
+
controller.close()
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
controller.enqueue(value)
|
|
135
|
+
} catch (error) {
|
|
136
|
+
emitClose()
|
|
137
|
+
controller.error(error)
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
cancel(reason) {
|
|
141
|
+
emitClose()
|
|
142
|
+
return reader.cancel(reason)
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
return new Response(monitored, response)
|
|
146
|
+
}
|
|
147
|
+
emitClose()
|
|
148
|
+
return response
|
|
149
|
+
})
|
|
150
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Escapes characters that could prematurely terminate the surrounding <script>
|
|
3
|
+
tag or be interpreted as HTML comment delimiters when a JSON literal is
|
|
4
|
+
inlined into an HTML document. U+2028 (LS) and U+2029 (PS) are valid in JSON
|
|
5
|
+
but break a `<script>` tag's inline content because the JavaScript lexer
|
|
6
|
+
treats them as line terminators; encode them as Unicode escapes.
|
|
7
|
+
*/
|
|
8
|
+
const LINE_SEPARATOR = String.fromCharCode(0x2028)
|
|
9
|
+
const PARAGRAPH_SEPARATOR = String.fromCharCode(0x2029)
|
|
10
|
+
|
|
11
|
+
export function safeJsonForScript(value: unknown): string {
|
|
12
|
+
return JSON.stringify(value)
|
|
13
|
+
.replace(/</g, '\\u003c')
|
|
14
|
+
.replace(/-->/g, '--\\u003e')
|
|
15
|
+
.replaceAll(LINE_SEPARATOR, '\\u2028')
|
|
16
|
+
.replaceAll(PARAGRAPH_SEPARATOR, '\\u2029')
|
|
17
|
+
}
|