@abide/abide 0.48.0 → 0.50.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/AGENTS.md +85 -59
- package/CHANGELOG.md +216 -0
- package/README.md +21 -10
- package/package.json +15 -4
- package/src/abideLsp.ts +5 -6
- package/src/abideResolverPlugin.ts +197 -169
- package/src/build.ts +79 -56
- package/src/buildCli.ts +3 -5
- package/src/buildDisconnected.ts +2 -5
- package/src/bundleApp.ts +2 -3
- package/src/checkAbide.ts +12 -2
- package/src/compile.ts +2 -4
- package/src/devEntry.ts +113 -28
- package/src/discoveryEntry.ts +3 -2
- package/src/lib/bundle/disconnected.abide +69 -73
- package/src/lib/bundle/infoPlist.ts +13 -7
- package/src/lib/bundle/installDownloads.ts +13 -3
- package/src/lib/bundle/installMacMenu.ts +13 -3
- package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
- package/src/lib/bundle/waitForServer.ts +6 -1
- package/src/lib/cli/parseArgvForRpc.ts +36 -9
- package/src/lib/cli/tokenizeArgvFlags.ts +4 -2
- package/src/lib/mcp/createMcpResourceServer.ts +13 -2
- package/src/lib/mcp/mcpTools.ts +15 -6
- package/src/lib/mcp/toolResultFromResponse.ts +40 -12
- package/src/lib/server/error.ts +6 -6
- package/src/lib/server/json.ts +17 -3
- package/src/lib/server/rpc/defineRpc.ts +58 -24
- package/src/lib/server/rpc/parseArgs.ts +113 -14
- package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
- package/src/lib/server/rpc/runWithRpcTimeout.ts +12 -1
- package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
- package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
- package/src/lib/server/rpc/validationError.ts +1 -1
- package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
- package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
- package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
- package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
- package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
- package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
- package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
- package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
- package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
- package/src/lib/server/runtime/createServer.ts +120 -324
- package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
- package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
- package/src/lib/server/runtime/finalizeResponse.ts +11 -1
- package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
- package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
- package/src/lib/server/runtime/pathStore.ts +15 -0
- package/src/lib/server/runtime/registryManifests.ts +1 -1
- package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
- package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +24 -16
- package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
- package/src/lib/server/runtime/streamFromIterator.ts +30 -2
- package/src/lib/server/runtime/textResponse.ts +23 -0
- package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/server/runtime/types/RequestStore.ts +27 -0
- package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
- package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
- package/src/lib/server/sockets/defineSocket.ts +5 -4
- package/src/lib/server/sse.ts +5 -1
- package/src/lib/shared/ASYNC_CELL.ts +5 -0
- package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
- package/src/lib/shared/HTTP_METHODS.ts +6 -0
- package/src/lib/shared/HttpError.ts +1 -5
- package/src/lib/shared/MCP_PATH.ts +6 -0
- package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
- package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
- package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
- package/src/lib/shared/activeCacheStore.ts +1 -0
- package/src/lib/shared/activePage.ts +1 -0
- package/src/lib/shared/buildArtifact.ts +20 -0
- package/src/lib/shared/buildRpcRequest.ts +6 -2
- package/src/lib/shared/buildSocketOverChannel.ts +4 -3
- package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
- package/src/lib/shared/cache.ts +156 -131
- package/src/lib/shared/canonicalJson.ts +24 -1
- package/src/lib/shared/changeAffectsClient.ts +12 -7
- package/src/lib/shared/createCacheStore.ts +31 -8
- package/src/lib/shared/createChannelLog.ts +24 -0
- package/src/lib/shared/createLifecycleChannel.ts +7 -1
- package/src/lib/shared/createReachable.ts +47 -78
- package/src/lib/shared/createRemoteFunction.ts +52 -30
- package/src/lib/shared/createRpcServerProgram.ts +820 -0
- package/src/lib/shared/decodeRefJson.ts +4 -4
- package/src/lib/shared/decodeResponse.ts +2 -2
- package/src/lib/shared/decodeWireBody.ts +35 -0
- package/src/lib/shared/detectRpcMethod.ts +8 -1
- package/src/lib/shared/done.ts +8 -2
- package/src/lib/shared/encodeRefJson.ts +4 -4
- package/src/lib/shared/encodeWireBody.ts +18 -0
- package/src/lib/shared/exitOnBuildFailure.ts +4 -3
- package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
- package/src/lib/shared/generateDeclarations.ts +72 -0
- package/src/lib/shared/hasSeedableRequest.ts +25 -0
- package/src/lib/shared/hydrationWindow.ts +53 -0
- package/src/lib/shared/isAsyncCell.ts +11 -0
- package/src/lib/shared/isAsyncIterable.ts +8 -0
- package/src/lib/shared/isSubscribable.ts +3 -3
- package/src/lib/shared/isThenable.ts +9 -0
- package/src/lib/shared/jsonSchemaForType.ts +258 -0
- package/src/lib/shared/lenientDecode.ts +15 -0
- package/src/lib/shared/loadProjectTsConfig.ts +28 -0
- package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
- package/src/lib/shared/matchRoute.ts +4 -14
- package/src/lib/shared/parseEnv.ts +17 -6
- package/src/lib/shared/peek.ts +14 -0
- package/src/lib/shared/pending.ts +9 -6
- package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
- package/src/lib/shared/prepareRpcModule.ts +91 -93
- package/src/lib/shared/prepareSocketModule.ts +3 -2
- package/src/lib/shared/probeRegistries.ts +5 -18
- package/src/lib/shared/reachable.ts +7 -10
- package/src/lib/shared/refresh.ts +12 -2
- package/src/lib/shared/refreshing.ts +8 -2
- package/src/lib/shared/resolvedCellsSlot.ts +13 -0
- package/src/lib/shared/reviveWireField.ts +49 -0
- package/src/lib/shared/reviveWireOutput.ts +36 -0
- package/src/lib/shared/rpcServerForRoot.ts +25 -0
- package/src/lib/shared/scanPages.ts +25 -0
- package/src/lib/shared/serializeEnv.ts +18 -6
- package/src/lib/shared/snapshotShippable.ts +8 -7
- package/src/lib/shared/snippet.ts +11 -6
- package/src/lib/shared/streamedCellsSlot.ts +14 -0
- package/src/lib/shared/subscribableFromResponse.ts +4 -4
- package/src/lib/shared/subscribableProbes.ts +1 -1
- package/src/lib/shared/tailProbeSlot.ts +1 -1
- package/src/lib/shared/types/AsyncComputed.ts +20 -0
- package/src/lib/shared/types/AsyncState.ts +13 -0
- package/src/lib/shared/types/CacheEntry.ts +7 -7
- package/src/lib/shared/types/CacheOptions.ts +23 -37
- package/src/lib/shared/types/CachePolicy.ts +25 -0
- package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
- package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
- package/src/lib/shared/types/HttpMethod.ts +3 -1
- package/src/lib/shared/types/InputCoercion.ts +17 -0
- package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
- package/src/lib/shared/types/OutputWirePlan.ts +17 -0
- package/src/lib/shared/types/PagesScan.ts +7 -0
- package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
- package/src/lib/shared/types/RemoteCallable.ts +8 -7
- package/src/lib/shared/types/RemoteFunction.ts +16 -15
- package/src/lib/shared/types/ResolvedCells.ts +11 -0
- package/src/lib/shared/types/ReturnBody.ts +15 -0
- package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
- package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
- package/src/lib/shared/types/Socket.ts +4 -4
- package/src/lib/shared/types/SsrPayload.ts +5 -1
- package/src/lib/shared/types/StreamPolicy.ts +11 -0
- package/src/lib/shared/types/StreamedCells.ts +19 -0
- package/src/lib/shared/types/StreamedResolution.ts +24 -6
- package/src/lib/shared/types/TailHooks.ts +1 -1
- package/src/lib/shared/types/WireKind.ts +11 -0
- package/src/lib/shared/url.ts +5 -0
- package/src/lib/shared/validationHttpError.ts +33 -0
- package/src/lib/shared/warmSeedKey.ts +16 -0
- package/src/lib/shared/wireJsonReplacer.ts +30 -0
- package/src/lib/shared/writeRpcDts.ts +11 -1
- package/src/lib/shared/writeTestSocketsDts.ts +1 -1
- package/src/lib/test/createTestApp.ts +19 -1
- package/src/lib/ui/README.md +1 -1
- package/src/lib/ui/activePendingCells.ts +14 -0
- package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
- package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -7
- package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
- package/src/lib/ui/compile/analyzeComponent.ts +89 -6
- package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
- package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
- package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
- package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
- package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
- package/src/lib/ui/compile/bindListenEvent.ts +5 -6
- package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
- package/src/lib/ui/compile/catchBinding.ts +7 -5
- package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
- package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
- package/src/lib/ui/compile/compileComponent.ts +19 -3
- package/src/lib/ui/compile/compileModule.ts +27 -58
- package/src/lib/ui/compile/compileSSR.ts +52 -13
- package/src/lib/ui/compile/compileShadow.ts +265 -42
- package/src/lib/ui/compile/composeProps.ts +15 -4
- package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
- package/src/lib/ui/compile/createShadowProgram.ts +35 -6
- package/src/lib/ui/compile/declaredNames.ts +36 -0
- package/src/lib/ui/compile/desugarSignals.ts +419 -37
- package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
- package/src/lib/ui/compile/generateBuild.ts +74 -17
- package/src/lib/ui/compile/generateSSR.ts +286 -75
- package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
- package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
- package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
- package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
- package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
- package/src/lib/ui/compile/isWhitespaceText.ts +10 -2
- package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
- package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
- package/src/lib/ui/compile/lowerContext.ts +10 -0
- package/src/lib/ui/compile/lowerDocAccess.ts +51 -10
- package/src/lib/ui/compile/lowerScript.ts +40 -4
- package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
- package/src/lib/ui/compile/parseTemplate.ts +18 -1090
- package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
- package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +26 -40
- package/src/lib/ui/compile/resolveReactiveExport.ts +4 -7
- package/src/lib/ui/compile/scopeCss.ts +27 -4
- package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
- package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
- package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
- package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
- package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
- package/src/lib/ui/compile/templateStartOffset.ts +15 -0
- package/src/lib/ui/compile/tryPlan.ts +4 -3
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
- package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
- package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
- package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
- package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
- package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
- package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
- package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
- package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
- package/src/lib/ui/computed.ts +28 -1
- package/src/lib/ui/createScope.ts +35 -68
- package/src/lib/ui/dom/anchoredBranch.ts +142 -0
- package/src/lib/ui/dom/appendText.ts +8 -3
- package/src/lib/ui/dom/awaitBlock.ts +62 -75
- package/src/lib/ui/dom/bindProp.ts +22 -0
- package/src/lib/ui/dom/bindableProp.ts +47 -0
- package/src/lib/ui/dom/cellPending.ts +24 -0
- package/src/lib/ui/dom/discardBoundary.ts +24 -6
- package/src/lib/ui/dom/disposeRange.ts +2 -1
- package/src/lib/ui/dom/each.ts +41 -12
- package/src/lib/ui/dom/eachAsync.ts +39 -3
- package/src/lib/ui/dom/fillBefore.ts +6 -7
- package/src/lib/ui/dom/fillBoundary.ts +2 -3
- package/src/lib/ui/dom/fillRange.ts +4 -4
- package/src/lib/ui/dom/hydrate.ts +6 -12
- package/src/lib/ui/dom/isComment.ts +1 -1
- package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
- package/src/lib/ui/dom/mergeProps.ts +1 -1
- package/src/lib/ui/dom/mount.ts +1 -2
- package/src/lib/ui/dom/mountChild.ts +9 -40
- package/src/lib/ui/dom/mountRange.ts +2 -3
- package/src/lib/ui/dom/mountSlot.ts +1 -1
- package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
- package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
- package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
- package/src/lib/ui/dom/on.ts +2 -2
- package/src/lib/ui/dom/readCell.ts +40 -0
- package/src/lib/ui/dom/restProps.ts +2 -2
- package/src/lib/ui/dom/spreadProps.ts +3 -4
- package/src/lib/ui/dom/switchBlock.ts +30 -7
- package/src/lib/ui/dom/tryBlock.ts +233 -70
- package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
- package/src/lib/ui/dom/when.ts +12 -2
- package/src/lib/ui/dom/withScope.ts +9 -2
- package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
- package/src/lib/ui/flight.ts +56 -0
- package/src/lib/ui/html.ts +12 -1
- package/src/lib/ui/isolateCellBarrier.ts +17 -0
- package/src/lib/ui/linked.ts +48 -2
- package/src/lib/ui/props.ts +17 -0
- package/src/lib/ui/remoteProxy.ts +84 -159
- package/src/lib/ui/renderChain.ts +53 -13
- package/src/lib/ui/renderToStream.ts +22 -18
- package/src/lib/ui/resumeSeedScript.ts +1 -1
- package/src/lib/ui/router.ts +86 -53
- package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
- package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
- package/src/lib/ui/runtime/CHILD_PRESENT.ts +2 -2
- package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
- package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
- package/src/lib/ui/runtime/RENDER.ts +10 -7
- package/src/lib/ui/runtime/RESUME.ts +4 -4
- package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
- package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
- package/src/lib/ui/runtime/applyPatchToTree.ts +17 -4
- package/src/lib/ui/runtime/blockId.ts +31 -0
- package/src/lib/ui/runtime/boundaryFor.ts +11 -0
- package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
- package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
- package/src/lib/ui/runtime/createDoc.ts +31 -54
- package/src/lib/ui/runtime/createEffectNode.ts +9 -0
- package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
- package/src/lib/ui/runtime/flushEffects.ts +33 -1
- package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
- package/src/lib/ui/runtime/nextBlockId.ts +8 -7
- package/src/lib/ui/runtime/renderPath.ts +16 -0
- package/src/lib/ui/runtime/scope.ts +11 -0
- package/src/lib/ui/runtime/toTeardown.ts +10 -3
- package/src/lib/ui/runtime/types/Boundary.ts +9 -0
- package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
- package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
- package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
- package/src/lib/ui/runtime/types/UiProps.ts +6 -5
- package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
- package/src/lib/ui/runtime/withPath.ts +16 -0
- package/src/lib/ui/runtime/withPathFrom.ts +20 -0
- package/src/lib/ui/runtime/withoutHydration.ts +22 -0
- package/src/lib/ui/seedStreamedResolution.ts +31 -6
- package/src/lib/ui/settleAsyncCells.ts +24 -0
- package/src/lib/ui/socketProxy.ts +1 -1
- package/src/lib/ui/startClient.ts +16 -31
- package/src/lib/ui/state.ts +9 -3
- package/src/lib/ui/trackedComputed.ts +68 -0
- package/src/lib/ui/types/Scope.ts +8 -24
- package/src/lib/ui/watch.ts +3 -3
- package/src/serverEntry.ts +14 -0
- package/template/src/server/rpc/getHello.ts +15 -13
- package/template/test/app.test.ts +1 -1
- package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
- package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
- package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
- package/src/lib/shared/hasReplayableRequest.ts +0 -17
- package/src/lib/shared/hydratingSlot.ts +0 -12
- package/src/lib/shared/outboxProbeSlot.ts +0 -20
- package/src/lib/shared/types/Outbox.ts +0 -9
- package/src/lib/shared/types/OutboxEntry.ts +0 -27
- package/src/lib/shared/types/SmartReadOptions.ts +0 -36
- package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
- package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
- package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
- package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
- package/src/lib/ui/compile/markupTokens.ts +0 -313
- package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
- package/src/lib/ui/dom/applyResolved.ts +0 -87
- package/src/lib/ui/dom/scopeLabel.ts +0 -21
- package/src/lib/ui/dom/text.ts +0 -20
- package/src/lib/ui/history.ts +0 -101
- package/src/lib/ui/installHotBridge.ts +0 -93
- package/src/lib/ui/installInspectorBridge.ts +0 -140
- package/src/lib/ui/outbox.ts +0 -35
- package/src/lib/ui/persist.ts +0 -115
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
- package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
- package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
- package/src/lib/ui/runtime/captureModelDoc.ts +0 -28
- package/src/lib/ui/runtime/hotInstances.ts +0 -10
- package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
- package/src/lib/ui/runtime/hotReplace.ts +0 -38
- package/src/lib/ui/runtime/liveScopes.ts +0 -15
- package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
- package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
- package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
- package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
- package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
- package/src/lib/ui/seedResolved.ts +0 -28
- package/src/lib/ui/sync.ts +0 -48
- package/src/lib/ui/types/History.ts +0 -14
- package/src/lib/ui/types/PersistHandle.ts +0 -11
- package/src/lib/ui/types/PersistenceStore.ts +0 -12
- package/src/lib/ui/types/ResolvedFrame.ts +0 -15
- package/src/lib/ui/types/SyncTransport.ts +0 -13
|
@@ -4,10 +4,14 @@ import { createRemoteFunction } from '../../shared/createRemoteFunction.ts'
|
|
|
4
4
|
import { forwardHeaders } from '../../shared/forwardHeaders.ts'
|
|
5
5
|
import { isReadOnlyMethod } from '../../shared/isReadOnlyMethod.ts'
|
|
6
6
|
import { resolveClientFlags } from '../../shared/resolveClientFlags.ts'
|
|
7
|
+
import type { CachePolicy } from '../../shared/types/CachePolicy.ts'
|
|
7
8
|
import type { ClientFlags } from '../../shared/types/ClientFlags.ts'
|
|
9
|
+
import type { ErrorJsonSchemas } from '../../shared/types/ErrorJsonSchemas.ts'
|
|
8
10
|
import type { HttpMethod } from '../../shared/types/HttpMethod.ts'
|
|
11
|
+
import type { InputCoercion } from '../../shared/types/InputCoercion.ts'
|
|
9
12
|
import type { RemoteFunction } from '../../shared/types/RemoteFunction.ts'
|
|
10
13
|
import type { StandardSchemaV1 } from '../../shared/types/StandardSchemaV1.ts'
|
|
14
|
+
import type { StreamPolicy } from '../../shared/types/StreamPolicy.ts'
|
|
11
15
|
import { requestContext } from '../runtime/requestContext.ts'
|
|
12
16
|
import { parseArgs } from './parseArgs.ts'
|
|
13
17
|
import { registerRpc } from './registerRpc.ts'
|
|
@@ -50,38 +54,57 @@ export function defineRpc<Args, Return>(
|
|
|
50
54
|
url: string,
|
|
51
55
|
handler: RemoteHandler<Args, Return>,
|
|
52
56
|
opts?: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
/* Namespaced schemas (ADR-0020): `input` types + validates args, `output` is the
|
|
58
|
+
success-body schema (OpenAPI/MCP), `files` validates multipart File parts. */
|
|
59
|
+
schemas?: {
|
|
60
|
+
input?: StandardSchemaV1
|
|
61
|
+
output?: StandardSchemaV1
|
|
62
|
+
files?: StandardSchemaV1
|
|
63
|
+
}
|
|
64
|
+
/* Surface-exposure flags (browser/mcp/cli) — which surfaces advertise this rpc. */
|
|
56
65
|
clients?: Partial<ClientFlags>
|
|
57
66
|
crossOrigin?: boolean
|
|
58
67
|
/* Per-rpc cap on actual received body bytes (413 past it); omitted = Bun's server-wide maxRequestBodySize. */
|
|
59
68
|
maxBodySize?: number
|
|
60
69
|
/* Per-rpc handler deadline (ms): a 504 once exceeded, on every surface (SSR/MCP/CLI/network). */
|
|
61
70
|
timeout?: number
|
|
62
|
-
/*
|
|
63
|
-
the client proxy parks the request for replay instead of just throwing. The
|
|
64
|
-
parked write drains on `rpc.outbox.retry()` (no auto-drain). Mutating methods
|
|
65
|
-
only. */
|
|
66
|
-
outbox?: boolean
|
|
67
|
-
/* Handler returns jsonl()/sse(): the bare call returns the Subscribable directly (for
|
|
71
|
+
/* Handler returns jsonl()/sse(): the bare call returns the NamedAsyncIterable directly (for
|
|
68
72
|
isomorphic `for await (… of fn(args))` in server code). Bundler-stamped; the router's
|
|
69
73
|
wire path (.fetch) is unaffected. */
|
|
70
74
|
streaming?: boolean
|
|
75
|
+
/* Numeric/boolean input fields → parseArgs coerces a string query/form value into the
|
|
76
|
+
typed value the input schema expects (ADR-0028). Bundler-stamped from the endpoint's
|
|
77
|
+
`Args` type; absent on the fail-open path (a GET field then stays a string as before). */
|
|
78
|
+
coerce?: InputCoercion
|
|
79
|
+
/* The handler's input args projected to JSON Schema (ADR-0030 input side). Bundler-stamped
|
|
80
|
+
from the endpoint's input-parameter type; feeds the OpenAPI parameters/body / MCP inputSchema
|
|
81
|
+
/ inspector input surface when no `schemas.input` validator is declared (which still
|
|
82
|
+
overrides it), and makes the endpoint advertisable to MCP/CLI. A SHAPE description only —
|
|
83
|
+
never runtime-validated, so it can't produce a 422. Absent on the fail-open path — the
|
|
84
|
+
surface then omits the input schema exactly as before. */
|
|
85
|
+
inputJsonSchema?: Record<string, unknown>
|
|
86
|
+
/* The handler's return type projected to JSON Schema (ADR-0030 D2). Bundler-stamped from the
|
|
87
|
+
endpoint's success-body type; feeds the OpenAPI 200 / MCP outputSchema / inspector surface
|
|
88
|
+
when no `schemas.output` validator is declared (which still overrides it). Absent on the
|
|
89
|
+
fail-open path — the surface then omits the response schema exactly as before. */
|
|
90
|
+
outputJsonSchema?: Record<string, unknown>
|
|
91
|
+
/* The handler's typed-error branches as a status-keyed JSON-Schema map (ADR-0030). Bundler-
|
|
92
|
+
stamped from the endpoint's `error.typed(...)` return branches; feeds the OpenAPI
|
|
93
|
+
`responses[status]` surface so each typed error's status + data payload is documented
|
|
94
|
+
alongside the 200. Absent on the fail-open path — the surface then omits the error
|
|
95
|
+
responses exactly as before. */
|
|
96
|
+
errorJsonSchemas?: ErrorJsonSchemas
|
|
97
|
+
/* Endpoint cache policy (ADR-0020) — read helpers only; readThrough's bottom layer. */
|
|
98
|
+
cache?: CachePolicy<Args>
|
|
99
|
+
/* Endpoint stream policy (ADR-0020) — streaming read helpers only; replay depth `n`. */
|
|
100
|
+
stream?: StreamPolicy
|
|
71
101
|
},
|
|
72
102
|
): RemoteFunction<Args, Return> {
|
|
73
|
-
/* `outbox: true` is a mutation contract — a read RPC has nothing to durably deliver,
|
|
74
|
-
so reject it at declaration rather than silently ignore. The server handler itself
|
|
75
|
-
is unaffected; durability is a client-proxy concern. */
|
|
76
|
-
if (opts?.outbox === true && isReadOnlyMethod(method)) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
`[abide] outbox: true is only valid on mutating RPCs (POST/PUT/PATCH/DELETE), not ${method}`,
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
103
|
const timeout = opts?.timeout
|
|
82
|
-
const inputSchema = opts?.
|
|
83
|
-
const
|
|
84
|
-
const
|
|
104
|
+
const inputSchema = opts?.schemas?.input
|
|
105
|
+
const inputJsonSchema = opts?.inputJsonSchema
|
|
106
|
+
const outputSchema = opts?.schemas?.output
|
|
107
|
+
const filesSchema = opts?.schemas?.files
|
|
85
108
|
/*
|
|
86
109
|
An input schema makes the handler safe to advertise to non-browser
|
|
87
110
|
surfaces. CLI flips on for any rpc with one (a human/script invokes it
|
|
@@ -89,8 +112,16 @@ export function defineRpc<Args, Return>(
|
|
|
89
112
|
model shouldn't be able to mutate/delete just because the handler
|
|
90
113
|
carries a schema, so mutating rpcs require an explicit clients.mcp.
|
|
91
114
|
Explicit `clients` always wins.
|
|
115
|
+
|
|
116
|
+
A build-projected `inputJsonSchema` (ADR-0030 input side — the handler's
|
|
117
|
+
input parameter type projected to JSON Schema, present only when the type
|
|
118
|
+
resolved cleanly) counts as a machine-advertisable contract too, so a
|
|
119
|
+
plainly-typed handler auto-exposes without a hand-written `schemas.input`.
|
|
120
|
+
This is SHAPE-only: the projection describes the input, it is NEVER run as
|
|
121
|
+
runtime validation (no 422) — declaring `schemas.input` is still what adds
|
|
122
|
+
the runtime narrowing.
|
|
92
123
|
*/
|
|
93
|
-
const hasSchema = inputSchema !== undefined
|
|
124
|
+
const hasSchema = inputSchema !== undefined || inputJsonSchema !== undefined
|
|
94
125
|
const clients = resolveClientFlags(opts?.clients, {
|
|
95
126
|
mcp: hasSchema && isReadOnlyMethod(method),
|
|
96
127
|
cli: hasSchema,
|
|
@@ -189,10 +220,12 @@ export function defineRpc<Args, Return>(
|
|
|
189
220
|
clients,
|
|
190
221
|
streaming: opts?.streaming ?? false,
|
|
191
222
|
crossOrigin: opts?.crossOrigin,
|
|
223
|
+
cache: opts?.cache,
|
|
224
|
+
stream: opts?.stream,
|
|
192
225
|
buildRequest,
|
|
193
226
|
invoke,
|
|
194
227
|
parseArgsForFetch: async (request) => {
|
|
195
|
-
const args = await parseArgs(method, request, opts?.maxBodySize)
|
|
228
|
+
const args = await parseArgs(method, request, opts?.maxBodySize, opts?.coerce)
|
|
196
229
|
/*
|
|
197
230
|
Compose this rpc's deadline into request().signal so a handler's
|
|
198
231
|
fetch(ext, { signal: request().signal }) is cancelled when the
|
|
@@ -225,12 +258,13 @@ export function defineRpc<Args, Return>(
|
|
|
225
258
|
registerRpc({
|
|
226
259
|
remote: remote as RemoteFunction<unknown, unknown>,
|
|
227
260
|
inputSchema,
|
|
261
|
+
inputJsonSchema,
|
|
228
262
|
outputSchema,
|
|
263
|
+
outputJsonSchema: opts?.outputJsonSchema,
|
|
264
|
+
errorJsonSchemas: opts?.errorJsonSchemas,
|
|
229
265
|
filesSchema,
|
|
230
|
-
clients,
|
|
231
266
|
timeout,
|
|
232
267
|
maxBodySize: opts?.maxBodySize,
|
|
233
|
-
crossOrigin: opts?.crossOrigin,
|
|
234
268
|
})
|
|
235
269
|
return remote
|
|
236
270
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { carriesBodyArgs } from '../../shared/carriesBodyArgs.ts'
|
|
2
2
|
import { contentTypeOf } from '../../shared/contentTypeOf.ts'
|
|
3
|
-
import {
|
|
3
|
+
import { decodeWireBody } from '../../shared/decodeWireBody.ts'
|
|
4
4
|
import { HttpError } from '../../shared/HttpError.ts'
|
|
5
5
|
import { REF_JSON_HEADER } from '../../shared/REF_JSON_HEADER.ts'
|
|
6
|
+
import { reviveWireField } from '../../shared/reviveWireField.ts'
|
|
6
7
|
import type { HttpMethod } from '../../shared/types/HttpMethod.ts'
|
|
8
|
+
import type { InputCoercion } from '../../shared/types/InputCoercion.ts'
|
|
9
|
+
import type { WireKind } from '../../shared/types/WireKind.ts'
|
|
7
10
|
import { error } from '../error.ts'
|
|
8
11
|
import { requestContext } from '../runtime/requestContext.ts'
|
|
9
12
|
import { readBodyWithinLimit } from './readBodyWithinLimit.ts'
|
|
@@ -62,6 +65,7 @@ export async function parseArgs(
|
|
|
62
65
|
method: HttpMethod,
|
|
63
66
|
request: Request,
|
|
64
67
|
maxBodySize?: number,
|
|
68
|
+
coerce?: InputCoercion,
|
|
65
69
|
): Promise<unknown> {
|
|
66
70
|
/*
|
|
67
71
|
Skip the URL parse entirely when the raw request URL has no query —
|
|
@@ -99,9 +103,7 @@ export async function parseArgs(
|
|
|
99
103
|
OpenAPI SDK) omits it and sends ordinary JSON — read with plain
|
|
100
104
|
JSON.parse, since the ref-json envelope is ambiguous with a 2-element
|
|
101
105
|
array body. */
|
|
102
|
-
body = bounded.headers.has(REF_JSON_HEADER)
|
|
103
|
-
? decodeRefJson(text)
|
|
104
|
-
: JSON.parse(text)
|
|
106
|
+
body = decodeWireBody(text, bounded.headers.has(REF_JSON_HEADER))
|
|
105
107
|
}
|
|
106
108
|
} else if (
|
|
107
109
|
contentType.includes('application/x-www-form-urlencoded') ||
|
|
@@ -122,24 +124,121 @@ export async function parseArgs(
|
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
if (!url) {
|
|
125
|
-
/* `body` is undefined or a plain object here
|
|
127
|
+
/* `body` is undefined or a plain object here. A form-encoded body arrives stringly, so
|
|
128
|
+
coerce its numeric/boolean fields (a JSON body's values are already typed — non-strings
|
|
129
|
+
the coercion leaves untouched). */
|
|
130
|
+
if (body !== undefined && coerce !== undefined) {
|
|
131
|
+
applyCoercion(body as Record<string, unknown>, coerce)
|
|
132
|
+
}
|
|
126
133
|
return body
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
/*
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
version strings like '1.0'), silently breaking GET validation. A correct fix
|
|
137
|
-
needs the schema threaded in here (or a coercing schema adapter at the rpc).
|
|
137
|
+
Query params (and form-encoded body fields) arrive as strings, so a numeric/boolean field
|
|
138
|
+
would reach schema validation as `'2'`/`'true'`. `coerce` (ADR-0028) is the build-time plan
|
|
139
|
+
of exactly which fields are numeric/boolean in the endpoint's `Args` type — resolved through
|
|
140
|
+
the warm server program, so a string-typed field that merely looks numeric (an id, a zip code,
|
|
141
|
+
`'1.0'`) is never listed and stays a string. Applied to the merged bag below; a JSON body's
|
|
142
|
+
already-typed values are non-strings the coercion skips.
|
|
138
143
|
*/
|
|
139
144
|
const bodyObject = (body ?? {}) as Record<string, unknown>
|
|
140
|
-
|
|
145
|
+
/* Collect the query into an object, arraying repeated keys (`?tag=a&tag=b` → `['a','b']`)
|
|
146
|
+
rather than letting `Object.fromEntries` silently keep only the last — mirrors
|
|
147
|
+
splitFormData, so a repeated field reaches the schema as an array, not a dropped value. */
|
|
148
|
+
const queryObject: Record<string, unknown> = {}
|
|
149
|
+
for (const [key, value] of url.searchParams) {
|
|
150
|
+
const existing = queryObject[key]
|
|
151
|
+
if (!(key in queryObject)) {
|
|
152
|
+
queryObject[key] = value
|
|
153
|
+
} else if (Array.isArray(existing)) {
|
|
154
|
+
existing.push(value)
|
|
155
|
+
} else {
|
|
156
|
+
queryObject[key] = [existing, value]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const merged = { ...queryObject, ...bodyObject }
|
|
141
160
|
if (Object.keys(merged).length === 0) {
|
|
142
161
|
return undefined
|
|
143
162
|
}
|
|
163
|
+
if (coerce !== undefined) {
|
|
164
|
+
applyCoercion(merged, coerce)
|
|
165
|
+
}
|
|
144
166
|
return merged
|
|
145
167
|
}
|
|
168
|
+
|
|
169
|
+
/*
|
|
170
|
+
Revives the plain-JSON wire values in `args` into the runtime types the plan declares, in place
|
|
171
|
+
(ADR-0028 scalars, ADR-0029 structured). The scalar kinds (number/boolean/date/bigint) touch
|
|
172
|
+
strings only — a value the merge layered in from a typed JSON body is already the right type and
|
|
173
|
+
left alone; a repeated key (`?tag=1&tag=2`) is an array whose string members revive per element.
|
|
174
|
+
The container kinds (set/map) build from a JSON array (or object) but pass an already-typed value
|
|
175
|
+
(from the abide client's ref-json body) through untouched. Every branch is fail-open: an
|
|
176
|
+
unrevivable value stays its JSON form so the schema surfaces an honest issue rather than a throw.
|
|
177
|
+
*/
|
|
178
|
+
function applyCoercion(args: Record<string, unknown>, coerce: InputCoercion): void {
|
|
179
|
+
for (const key in coerce) {
|
|
180
|
+
const kind = coerce[key]
|
|
181
|
+
if (kind === undefined || !(key in args)) {
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
args[key] = reviveValue(args[key], kind)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* One field's wire value → its runtime type per `kind`, fail-open. The container kinds wrap a JSON
|
|
189
|
+
array/object; the scalar kinds revive a string (or each string in a repeated-key array). An
|
|
190
|
+
already-typed value (Set/Map/Date/bigint from the abide ref-json body) passes through. */
|
|
191
|
+
function reviveValue(value: unknown, kind: WireKind): unknown {
|
|
192
|
+
/* Container kinds (set/map) decode through the shared wire codec; the input side additionally
|
|
193
|
+
accepts a plain object as Map entries — a query/form body can't send a JSON entries array
|
|
194
|
+
(ADR-0029 defers descending into array MEMBERS). */
|
|
195
|
+
if (kind === 'set' || kind === 'map') {
|
|
196
|
+
if (
|
|
197
|
+
kind === 'map' &&
|
|
198
|
+
!(value instanceof Map) &&
|
|
199
|
+
!Array.isArray(value) &&
|
|
200
|
+
value !== null &&
|
|
201
|
+
typeof value === 'object'
|
|
202
|
+
) {
|
|
203
|
+
return new Map(Object.entries(value))
|
|
204
|
+
}
|
|
205
|
+
return reviveWireField(value, kind)
|
|
206
|
+
}
|
|
207
|
+
/* Scalar kinds (number/boolean/date/bigint): query/form values arrive as strings — a lone
|
|
208
|
+
value, or each string in a repeated-key array. */
|
|
209
|
+
if (typeof value === 'string') {
|
|
210
|
+
return reviveScalar(value, kind)
|
|
211
|
+
}
|
|
212
|
+
if (Array.isArray(value)) {
|
|
213
|
+
return value.map((element) =>
|
|
214
|
+
typeof element === 'string' ? reviveScalar(element, kind) : element,
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
return value
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* One string → its number/boolean/date/bigint value, or the original string when it doesn't parse
|
|
221
|
+
cleanly (empty/whitespace, `NaN`, a non-`true`/`false` boolean, an unparseable date, a
|
|
222
|
+
non-integer bigint), so a bad value fails validation with the field intact instead of coercing
|
|
223
|
+
to `0`/`NaN`/`Invalid Date`. */
|
|
224
|
+
function reviveScalar(value: string, kind: WireKind): unknown {
|
|
225
|
+
if (kind === 'number') {
|
|
226
|
+
if (value.trim() === '') {
|
|
227
|
+
return value
|
|
228
|
+
}
|
|
229
|
+
const parsed = Number(value)
|
|
230
|
+
return Number.isNaN(parsed) ? value : parsed
|
|
231
|
+
}
|
|
232
|
+
if (kind === 'boolean') {
|
|
233
|
+
if (value === 'true') {
|
|
234
|
+
return true
|
|
235
|
+
}
|
|
236
|
+
if (value === 'false') {
|
|
237
|
+
return false
|
|
238
|
+
}
|
|
239
|
+
return value
|
|
240
|
+
}
|
|
241
|
+
/* date/bigint from a query/form string decode identically to the shared wire codec (ISO
|
|
242
|
+
string → Date, digit string → bigint), fail-open. */
|
|
243
|
+
return reviveWireField(value, kind)
|
|
244
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsonSchemaForSchema } from '../../shared/jsonSchemaForSchema.ts'
|
|
2
|
+
import type { RpcRegistryEntry } from './types/RpcRegistryEntry.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The ONE resolution every surface (MCP tools, OpenAPI, the inspector catalog, and the
|
|
6
|
+
standalone CLI manifest) uses for an rpc's advertised INPUT shape: a declared
|
|
7
|
+
`inputSchema` VALIDATOR overrides the build-projected `inputJsonSchema` (ADR-0030 input
|
|
8
|
+
side); with neither, `undefined`. Callers that need a non-empty schema apply the opaque
|
|
9
|
+
`?? jsonSchemaForSchema(undefined)` fallback themselves (MCP/OpenAPI/CLI want it; the
|
|
10
|
+
inspector leaves it undefined). One origin so the surfaces can't drift — the CLI manifest
|
|
11
|
+
emitter previously read only `inputSchema` and silently dropped every plainly-typed
|
|
12
|
+
handler's projected shape (no typed flags, no `--help`, array flags overwriting).
|
|
13
|
+
*/
|
|
14
|
+
export function resolveInputJsonSchema(
|
|
15
|
+
entry: RpcRegistryEntry,
|
|
16
|
+
): Record<string, unknown> | undefined {
|
|
17
|
+
return entry.inputSchema ? jsonSchemaForSchema(entry.inputSchema) : entry.inputJsonSchema
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
The output analog: an `outputSchema` VALIDATOR overrides the projected `outputJsonSchema`;
|
|
22
|
+
with neither, `undefined` (no output shape advertised — every surface leaves it off).
|
|
23
|
+
*/
|
|
24
|
+
export function resolveOutputJsonSchema(
|
|
25
|
+
entry: RpcRegistryEntry,
|
|
26
|
+
): Record<string, unknown> | undefined {
|
|
27
|
+
return entry.outputSchema ? jsonSchemaForSchema(entry.outputSchema) : entry.outputJsonSchema
|
|
28
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { abideLog } from '../../shared/abideLog.ts'
|
|
1
2
|
import { error } from '../error.ts'
|
|
2
3
|
|
|
3
4
|
/*
|
|
@@ -39,7 +40,17 @@ export function runWithRpcTimeout(
|
|
|
39
40
|
(late) => {
|
|
40
41
|
void late.body?.cancel()
|
|
41
42
|
},
|
|
42
|
-
() => {
|
|
43
|
+
(rejection) => {
|
|
44
|
+
/* The handler outlived its timeout and then rejected. A cooperative
|
|
45
|
+
abort (it honoured the composed signal) is expected and stays quiet;
|
|
46
|
+
any other error is a genuine handler bug and would otherwise leave
|
|
47
|
+
zero server-side trace behind the client's 504 — log it. */
|
|
48
|
+
const aborted =
|
|
49
|
+
(rejection as { name?: string } | undefined)?.name === 'AbortError'
|
|
50
|
+
if (!aborted) {
|
|
51
|
+
abideLog.error(rejection)
|
|
52
|
+
}
|
|
53
|
+
},
|
|
43
54
|
)
|
|
44
55
|
}
|
|
45
56
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { CachePolicy } from '../../../shared/types/CachePolicy.ts'
|
|
1
2
|
import type { ClientFlags } from '../../../shared/types/ClientFlags.ts'
|
|
2
3
|
import type { ErrorSpec } from '../../../shared/types/ErrorSpec.ts'
|
|
3
4
|
import type { RemoteFunction } from '../../../shared/types/RemoteFunction.ts'
|
|
4
5
|
import type { StandardSchemaV1 } from '../../../shared/types/StandardSchemaV1.ts'
|
|
6
|
+
import type { StreamPolicy } from '../../../shared/types/StreamPolicy.ts'
|
|
5
7
|
import type { TypedError } from './TypedError.ts'
|
|
6
8
|
import type { TypedResponse } from './TypedResponse.ts'
|
|
7
9
|
|
|
@@ -49,25 +51,39 @@ type RpcArgs<F extends RpcFn> = Parameters<F> extends [infer Args, ...unknown[]]
|
|
|
49
51
|
The RemoteFunction a bare overload produces from handler `F`. `SuccessBody`/`InferredErrors`
|
|
50
52
|
run over `Awaited<ReturnType<F>>` INLINE (not via an intermediate `extends RpcFn`-constrained
|
|
51
53
|
alias) — such an alias resolves `ReturnType` against the constraint bound and degrades the
|
|
52
|
-
body to `any`.
|
|
54
|
+
body to `any`.
|
|
53
55
|
*/
|
|
54
|
-
type RpcOf<F extends RpcFn
|
|
56
|
+
type RpcOf<F extends RpcFn> = RemoteFunction<
|
|
55
57
|
RpcArgs<F>,
|
|
56
58
|
SuccessBody<Awaited<ReturnType<F>>>,
|
|
57
|
-
InferredErrors<Awaited<ReturnType<F
|
|
58
|
-
Durable
|
|
59
|
+
InferredErrors<Awaited<ReturnType<F>>>
|
|
59
60
|
>
|
|
60
61
|
|
|
61
62
|
/*
|
|
62
|
-
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
`
|
|
66
|
-
|
|
67
|
-
(see MutatingRpcOpts) — a read RPC never accepts it.
|
|
63
|
+
The schema namespace (ADR-0020): `schemas: { input?, output?, files? }` replaces the
|
|
64
|
+
flat `inputSchema`/`outputSchema`/`filesSchema`. `input` drives the handler's arg type,
|
|
65
|
+
`output` is the success-body schema (OpenAPI 200 / MCP outputSchema — never drives arg
|
|
66
|
+
inference), `files` validates multipart File parts. The schema-bearing helper overloads
|
|
67
|
+
tighten specific members to required.
|
|
68
68
|
*/
|
|
69
|
-
type
|
|
70
|
-
|
|
69
|
+
type RpcSchemas = {
|
|
70
|
+
input?: StandardSchemaV1
|
|
71
|
+
output?: StandardSchemaV1
|
|
72
|
+
files?: StandardSchemaV1
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
Options every rpc overload accepts: the `schemas` namespace, the `clients` surface
|
|
77
|
+
flags (browser/mcp/cli), the same-origin
|
|
78
|
+
CSRF exemption (`crossOrigin`), the pre-parse body-byte ceiling
|
|
79
|
+
(`maxBodySize`), and the per-surface handler `timeout` (ms). Read helpers add the
|
|
80
|
+
endpoint `cache`/`stream` policy (ADR-0020); mutating helpers add neither — a write has
|
|
81
|
+
no replayable value to retain or stream. The kind-scoping is what makes `POST(fn, { cache })`
|
|
82
|
+
a compile error rather than a silent no-op. The single canonical source both `defineRpc`
|
|
83
|
+
and `RpcRegistryEntry` project from.
|
|
84
|
+
*/
|
|
85
|
+
type RpcSharedOpts = {
|
|
86
|
+
schemas?: RpcSchemas
|
|
71
87
|
clients?: Partial<ClientFlags>
|
|
72
88
|
crossOrigin?: boolean
|
|
73
89
|
maxBodySize?: number
|
|
@@ -75,63 +91,38 @@ type RpcBaseOpts = {
|
|
|
75
91
|
}
|
|
76
92
|
|
|
77
93
|
/*
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
so `
|
|
81
|
-
|
|
94
|
+
Read-helper (GET/HEAD) options: the shared base plus the endpoint `cache` policy
|
|
95
|
+
(`ttl`/`tags`/`throttle`/`debounce`/`shared`) and `stream` policy (`n`, replay depth).
|
|
96
|
+
Generic over `Args` so `cache.tags`'s `(args) => string[]` form is typed against the
|
|
97
|
+
rpc's own argument shape.
|
|
82
98
|
*/
|
|
83
|
-
type
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
unchanged — `rpc.outbox` exposes the queue. */
|
|
87
|
-
outbox?: boolean
|
|
99
|
+
type RpcReadOpts<Args> = RpcSharedOpts & {
|
|
100
|
+
cache?: CachePolicy<Args>
|
|
101
|
+
stream?: StreamPolicy
|
|
88
102
|
}
|
|
89
103
|
|
|
90
104
|
/*
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
becomes the
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
Four overloads by argument source:
|
|
105
|
+
The read helpers (GET/HEAD). The handler's return type is inferred whole
|
|
106
|
+
(`Awaited<ReturnType<F>>`), then split: `SuccessBody` becomes the caller's `Return`,
|
|
107
|
+
`InferredErrors` becomes the rpc's `Errors` (driving `isError`). Typed errors are raised
|
|
108
|
+
by returning an `error.typed(name, status, schema?)` constructor — there is no `errors:`
|
|
109
|
+
opt. Four overloads by argument source (most-specific first):
|
|
97
110
|
|
|
98
|
-
- `
|
|
99
|
-
|
|
100
|
-
`InferOutput<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
execution on every surface (SSR / MCP / CLI / network) — a 504 once
|
|
112
|
-
exceeded; on the network path it also aborts request().signal so a
|
|
113
|
-
handler's `fetch(ext, { signal: request().signal })` is cancelled, not
|
|
114
|
-
just abandoned.
|
|
115
|
-
- `Rpc(fn, { clients })` — schemaless but with explicit client
|
|
116
|
-
targeting (e.g. server-internal RPC with `clients: { browser: false }`).
|
|
117
|
-
- `Rpc(fn)` — bare handler, a single `F extends RpcFn` generic. Everything
|
|
118
|
-
is read off the handler: `Args` from its parameter (annotate it —
|
|
119
|
-
`POST((a: { id: string }) => …)` — or leave it nullary for `undefined`),
|
|
120
|
-
`Return` from the `TypedResponse<T>` brand on
|
|
121
|
-
`json`/`error`/`redirect`/`jsonl`/`sse`, `Errors` from any
|
|
122
|
-
`error.typed(...)` branches. You never pass `<Args, Return>` generics; a
|
|
123
|
-
stray one is a loud constraint error, not a silent `unknown` body.
|
|
111
|
+
- `GET(fn, { schemas: { input, files, output? }, cache?, … })` — multipart upload.
|
|
112
|
+
The handler receives the text fields (`InferOutput<input>`) intersected with the
|
|
113
|
+
validated File parts (`InferOutput<files>`); the call site sends a FormData. `files`
|
|
114
|
+
stays off the JSON-Schema projection (a File has no honest conversion) — only `input`
|
|
115
|
+
feeds MCP/CLI/OpenAPI. `Args` (the RemoteFunction's call type) is `InferInput<input>`.
|
|
116
|
+
- `GET(fn, { schemas: { input, output? }, cache?, … })` — `Args` = `InferInput<input>`,
|
|
117
|
+
the handler receives `InferOutput<input>`. `output` feeds the OpenAPI 200 / MCP tool
|
|
118
|
+
outputSchema and never drives arg inference.
|
|
119
|
+
- `GET(fn, opts)` — schemaless-with-opts (includes `schemas: { output }`-only): args
|
|
120
|
+
read off the handler `F`. `cache`/`clients`/`timeout`/… live here.
|
|
121
|
+
- `GET(fn)` — bare handler; everything reads off `F`: `Args` from its parameter, `Return`
|
|
122
|
+
from the `TypedResponse<T>` brand, `Errors` from any `error.typed(...)` branches. You
|
|
123
|
+
never pass `<Args, Return>` generics; a stray one is a loud constraint error.
|
|
124
124
|
*/
|
|
125
|
-
type
|
|
126
|
-
/*
|
|
127
|
-
`Rpc(fn, { inputSchema, filesSchema, … })` — multipart upload. The
|
|
128
|
-
handler receives the text fields (`InferOutput<InputSchema>`) intersected
|
|
129
|
-
with the validated File parts (`InferOutput<FilesSchema>`); both are merged
|
|
130
|
-
into one args bag. The call site sends a FormData (RemoteFunction's call
|
|
131
|
-
accepts it), since a File can't ride a JSON body. filesSchema stays off the
|
|
132
|
-
JSON-Schema projection — a File has no honest conversion (see
|
|
133
|
-
jsonSchemaForSchema) — so only inputSchema feeds MCP/CLI/OpenAPI.
|
|
134
|
-
*/
|
|
125
|
+
export type RpcHelper = {
|
|
135
126
|
<
|
|
136
127
|
R extends Response,
|
|
137
128
|
InputSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
@@ -141,30 +132,28 @@ type RpcHelperOf<Opts> = {
|
|
|
141
132
|
args: StandardSchemaV1.InferOutput<InputSchema> &
|
|
142
133
|
StandardSchemaV1.InferOutput<FilesSchema>,
|
|
143
134
|
) => R | Promise<R>,
|
|
144
|
-
opts:
|
|
145
|
-
|
|
146
|
-
filesSchema: FilesSchema
|
|
135
|
+
opts: RpcReadOpts<StandardSchemaV1.InferInput<InputSchema>> & {
|
|
136
|
+
schemas: { input: InputSchema; files: FilesSchema; output?: StandardSchemaV1 }
|
|
147
137
|
},
|
|
148
138
|
): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
|
|
149
139
|
<R extends Response, InputSchema extends StandardSchemaV1 = StandardSchemaV1>(
|
|
150
140
|
fn: (args: StandardSchemaV1.InferOutput<InputSchema>) => R | Promise<R>,
|
|
151
|
-
opts:
|
|
141
|
+
opts: RpcReadOpts<StandardSchemaV1.InferInput<InputSchema>> & {
|
|
142
|
+
schemas: { input: InputSchema; output?: StandardSchemaV1 }
|
|
143
|
+
},
|
|
152
144
|
): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
|
|
153
|
-
<F extends RpcFn>(fn: F, opts:
|
|
145
|
+
<F extends RpcFn>(fn: F, opts: RpcReadOpts<RpcArgs<F>>): RpcOf<F>
|
|
154
146
|
<F extends RpcFn>(fn: F): RpcOf<F>
|
|
155
147
|
}
|
|
156
148
|
|
|
157
|
-
/* The read helpers (GET/HEAD): no `outbox` — a read has nothing to durably deliver. */
|
|
158
|
-
export type RpcHelper = RpcHelperOf<RpcBaseOpts>
|
|
159
|
-
|
|
160
149
|
/*
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
150
|
+
The mutating helpers (POST/PUT/PATCH/DELETE). The base opts is the shared `RpcSharedOpts`
|
|
151
|
+
(no `cache`/`stream`) — coalesce-only is the method default and a write has no replayable
|
|
152
|
+
value to retain or stream — which is what makes `cache`/`stream` a compile error here while
|
|
153
|
+
they stay legal on the read helpers (`RpcReadOpts`) — the kind-scoping. Overloads mirror the read helpers by argument source
|
|
154
|
+
(multipart-upload, schema'd, schemaless-with-opts, bare).
|
|
166
155
|
*/
|
|
167
|
-
type
|
|
156
|
+
export type MutatingRpcHelper = {
|
|
168
157
|
<
|
|
169
158
|
R extends Response,
|
|
170
159
|
InputSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
@@ -174,35 +163,16 @@ type DurableMutatingRpcHelper = {
|
|
|
174
163
|
args: StandardSchemaV1.InferOutput<InputSchema> &
|
|
175
164
|
StandardSchemaV1.InferOutput<FilesSchema>,
|
|
176
165
|
) => R | Promise<R>,
|
|
177
|
-
opts:
|
|
178
|
-
|
|
179
|
-
filesSchema: FilesSchema
|
|
180
|
-
outbox: true
|
|
166
|
+
opts: RpcSharedOpts & {
|
|
167
|
+
schemas: { input: InputSchema; files: FilesSchema; output?: StandardSchemaV1 }
|
|
181
168
|
},
|
|
182
|
-
): RemoteFunction<
|
|
183
|
-
StandardSchemaV1.InferInput<InputSchema>,
|
|
184
|
-
SuccessBody<R>,
|
|
185
|
-
InferredErrors<R>,
|
|
186
|
-
true
|
|
187
|
-
>
|
|
169
|
+
): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
|
|
188
170
|
<R extends Response, InputSchema extends StandardSchemaV1 = StandardSchemaV1>(
|
|
189
171
|
fn: (args: StandardSchemaV1.InferOutput<InputSchema>) => R | Promise<R>,
|
|
190
|
-
opts:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
>
|
|
197
|
-
<F extends RpcFn>(fn: F, opts: MutatingRpcOpts & { outbox: true }): RpcOf<F, true>
|
|
172
|
+
opts: RpcSharedOpts & {
|
|
173
|
+
schemas: { input: InputSchema; output?: StandardSchemaV1 }
|
|
174
|
+
},
|
|
175
|
+
): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
|
|
176
|
+
<F extends RpcFn>(fn: F, opts: RpcSharedOpts): RpcOf<F>
|
|
177
|
+
<F extends RpcFn>(fn: F): RpcOf<F>
|
|
198
178
|
}
|
|
199
|
-
|
|
200
|
-
/*
|
|
201
|
-
The mutating helpers (POST/PUT/PATCH/DELETE). A durable (`outbox`) call is a normal
|
|
202
|
-
RemoteFunction — it throws exactly like a non-durable one and only parks the request as a
|
|
203
|
-
side-effect on an unreachable server — so there is no separate return shape; `outbox` rides
|
|
204
|
-
MutatingRpcOpts and `rpc.outbox` exposes the queue. The distinct opts base is what makes
|
|
205
|
-
`outbox` legal here and a compile error on the read helpers; the durable overloads then set
|
|
206
|
-
the return type's `Durable` bit so `rpc.outbox` is present without an optional chain.
|
|
207
|
-
*/
|
|
208
|
-
export type MutatingRpcHelper = DurableMutatingRpcHelper & RpcHelperOf<MutatingRpcOpts>
|