@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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { NamedAsyncIterable } from './NamedAsyncIterable.ts'
|
|
1
2
|
import type { RpcOptions } from './RpcOptions.ts'
|
|
2
|
-
import type { Subscribable } from './Subscribable.ts'
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
Call signature shared by RemoteFunction and RawRemoteFunction. The base
|
|
@@ -10,24 +10,25 @@ optional-arg signature lets call sites write `fn()` instead of
|
|
|
10
10
|
stays callable while `Args` is still generic (cache() invokes producers
|
|
11
11
|
before `Args` resolves). FormData is the multipart upload escape hatch —
|
|
12
12
|
see RemoteFunction. The trailing `Opts` differs by variant: the smart bare
|
|
13
|
-
call (RemoteFunction)
|
|
14
|
-
|
|
13
|
+
call (RemoteFunction) takes no second argument at all (`Opts = never` — all
|
|
14
|
+
cache/stream policy lives on the rpc definition per ADR-0020), while `.raw`
|
|
15
|
+
(RawRemoteFunction) carries per-call transport options (RpcOptions:
|
|
15
16
|
signal/keepalive/priority/cache/headers). The server ignores either, so the
|
|
16
17
|
callable stays isomorphic.
|
|
17
18
|
|
|
18
19
|
A streaming `Resolved` (an `AsyncIterable<Frame>`, branded by jsonl()/sse()) makes
|
|
19
|
-
the bare call return a `
|
|
20
|
+
the bare call return a `NamedAsyncIterable<Frame>` synchronously — the iterable IS the
|
|
20
21
|
value, consumed by `for await` or `state(fn(args))`, no `.stream()`. Every other
|
|
21
22
|
`Resolved` stays `Promise<Resolved>`. `await`-ing a streaming call is a compile
|
|
22
|
-
error (a
|
|
23
|
+
error (a NamedAsyncIterable is not awaitable), which replaces the old runtime guard.
|
|
23
24
|
*/
|
|
24
25
|
/* `[Resolved] extends [never]` first: a handler that only ever returns error() resolves to
|
|
25
26
|
`never`, and `never extends AsyncIterable` is vacuously true — without this guard its call
|
|
26
|
-
would mistype as a
|
|
27
|
+
would mistype as a NamedAsyncIterable. A real streaming rpc's `Resolved` is `AsyncIterable<Frame>`. */
|
|
27
28
|
type CallResult<Resolved> = [Resolved] extends [never]
|
|
28
29
|
? Promise<Resolved>
|
|
29
30
|
: Resolved extends AsyncIterable<infer Frame>
|
|
30
|
-
?
|
|
31
|
+
? NamedAsyncIterable<Frame>
|
|
31
32
|
: Promise<Resolved>
|
|
32
33
|
|
|
33
34
|
export type RemoteCallable<Args, Resolved, Opts = RpcOptions> = ((
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import type { CachePolicy } from './CachePolicy.ts'
|
|
1
2
|
import type { ClientFlags } from './ClientFlags.ts'
|
|
2
3
|
import type { ErrorSpec } from './ErrorSpec.ts'
|
|
3
4
|
import type { HttpMethod } from './HttpMethod.ts'
|
|
4
|
-
import type { Outbox } from './Outbox.ts'
|
|
5
5
|
import type { RawRemoteFunction } from './RawRemoteFunction.ts'
|
|
6
6
|
import type { RemoteCallable } from './RemoteCallable.ts'
|
|
7
7
|
import type { RpcError } from './RpcError.ts'
|
|
8
8
|
import type { RpcErrorGuard } from './RpcErrorGuard.ts'
|
|
9
|
-
import type {
|
|
9
|
+
import type { StreamPolicy } from './StreamPolicy.ts'
|
|
10
10
|
|
|
11
11
|
/*
|
|
12
12
|
Remote function reference produced by GET/POST/... inside an `$rpc/**`
|
|
@@ -21,7 +21,7 @@ method, same url, same args, no decode. Pass `fn.raw` to cache() to
|
|
|
21
21
|
memoise raw Responses against the same cache key as `fn` (both share one
|
|
22
22
|
stored entry — the decode just happens on the way out for callers of
|
|
23
23
|
`fn`). A streaming handler (jsonl()/sse()) makes the bare call return a
|
|
24
|
-
`
|
|
24
|
+
`NamedAsyncIterable<Frame>` directly (the iterable IS the value) — consume it with
|
|
25
25
|
`for await (… of fn(args))` or `state(fn(args))`; there is no `.stream()`, and
|
|
26
26
|
`await`-ing a streaming call is a compile error. For sustained broadcast /
|
|
27
27
|
pub-sub use the `abide/server/socket` primitive — HTTP rpc isn't the place for
|
|
@@ -44,23 +44,29 @@ export type RemoteFunction<
|
|
|
44
44
|
Args,
|
|
45
45
|
Return,
|
|
46
46
|
Errors extends ErrorSpec = Record<never, never>,
|
|
47
|
-
|
|
48
|
-
> = RemoteCallable<Args, Return, SmartReadOptions> & {
|
|
47
|
+
> = RemoteCallable<Args, Return, never> & {
|
|
49
48
|
readonly method: HttpMethod
|
|
50
49
|
readonly url: string
|
|
51
50
|
readonly clients: ClientFlags
|
|
52
51
|
readonly crossOrigin?: boolean
|
|
52
|
+
/* Endpoint cache/stream policy declared on the rpc definition (ADR-0020), stamped
|
|
53
|
+
here so the smart read (readThrough) reads it as the bottom policy layer. Ships to
|
|
54
|
+
the client too — it governs client cache behaviour and is behaviour, not secrets
|
|
55
|
+
(`shared` is a client no-op). Absent on a plainly-defined rpc (method defaults only). */
|
|
56
|
+
readonly cache?: CachePolicy<Args>
|
|
57
|
+
readonly stream?: StreamPolicy
|
|
53
58
|
readonly raw: RawRemoteFunction<Args>
|
|
54
59
|
fetch(request: Request): Promise<Response>
|
|
55
60
|
/* Type-guard a caught error against this rpc's declared `errors` (plus the framework
|
|
56
|
-
`'validation'`
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
`'validation'`): narrows `.kind` and, for a known kind, `.data` — the per-rpc
|
|
62
|
+
replacement for a global guard, since the error name → data type mapping lives in
|
|
63
|
+
the rpc's own spec. */
|
|
59
64
|
readonly isError: RpcErrorGuard<Errors>
|
|
60
65
|
/* Pre-bound selector sugar: `fn.pending(args?)` ≡ `pending(fn, args?)`, and likewise for
|
|
61
66
|
refreshing / refresh / peek — the rpc is the leading selector, bound in. The argument is
|
|
62
67
|
this rpc's typed `Args` (the by-args refinement); tags / cross-cutting selection stay on
|
|
63
|
-
the globals. The bare call `fn(args
|
|
68
|
+
the globals. The bare call `fn(args)` IS the cached read (was `fn.cache`) — all cache
|
|
69
|
+
policy lives on the definition, so there is no second options argument, and
|
|
64
70
|
`refresh(args?)` refetches keeping the stale value visible (was `invalidate`, which
|
|
65
71
|
dropped). `peek(args?)` reads the retained value synchronously (a streaming rpc peeks its
|
|
66
72
|
latest frame). `patch` is fetch-only, so it is omitted for a streaming rpc (a stream isn't
|
|
@@ -93,9 +99,4 @@ export type RemoteFunction<
|
|
|
93
99
|
: {
|
|
94
100
|
patch(args: Args | undefined, updater: (current: Return) => Return): void
|
|
95
101
|
patch(updater: (current: Return) => Return): void
|
|
96
|
-
})
|
|
97
|
-
into the return type so a DURABLE rpc's `.outbox` is the required queue face (no optional
|
|
98
|
-
chain). A non-durable rpc keeps it optional — assignable to a durable one everywhere a
|
|
99
|
-
bare `RemoteFunction<Args, Return>` slot (cache selectors, registries) is expected, since
|
|
100
|
-
required→optional widens cleanly and no call site had to learn the `Durable` bit. */ &
|
|
101
|
-
(Durable extends true ? { readonly outbox: Outbox<Args> } : { readonly outbox?: Outbox<Args> })
|
|
102
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The per-request list of async-cell values that RESOLVED during an SSR render, each keyed by the
|
|
3
|
+
cell's serialization-stable render-path id (`${scope.id}:${index}`). `createAsyncCell` pushes an
|
|
4
|
+
entry when its seed settles server-side; the page renderer stamps them into `__SSR__.cells`
|
|
5
|
+
(ref-json-encoded) so the client hydrates the cell WARM — reading the value instead of re-running
|
|
6
|
+
the seed (no refetch, no pending flash). The sibling of `PendingAsyncCells` (the barrier's
|
|
7
|
+
in-flight list); this one holds settled VALUES, read at render-return rather than awaited.
|
|
8
|
+
*/
|
|
9
|
+
export type ResolvedCells = {
|
|
10
|
+
entries: { key: string; value: unknown }[]
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The build-time success-body descriptor for an rpc, derived from the handler's return type by the
|
|
3
|
+
warm server program (ADR-0030). Mirrors `RpcHelper`'s `SuccessBody<R>` projection: the handler's
|
|
4
|
+
`Awaited<ReturnType>` union has its `TypedError` branches dropped, and each success
|
|
5
|
+
`TypedResponse<Body>` contributes its `Body`. `type` is that body rendered as a TS type string
|
|
6
|
+
(the union of the surviving branches). For a streaming endpoint (`jsonl()`/`sse()` →
|
|
7
|
+
`TypedResponse<AsyncIterable<Frame>>`) `streaming` is true and `type` is the per-FRAME type — the
|
|
8
|
+
AsyncIterable element — so a consumer describes one streamed item rather than the iterable itself.
|
|
9
|
+
The query fails open to undefined (no handler/body resolvable), matching every other server-program
|
|
10
|
+
query.
|
|
11
|
+
*/
|
|
12
|
+
export type ReturnBody = {
|
|
13
|
+
type: string
|
|
14
|
+
streaming: boolean
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ErrorJsonSchemas } from './ErrorJsonSchemas.ts'
|
|
2
|
+
import type { InputCoercion } from './InputCoercion.ts'
|
|
3
|
+
import type { OutputWirePlan } from './OutputWirePlan.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The build-time facts the warm rpc server program resolves for one `$rpc/**` handler, gathered into
|
|
7
|
+
a single named payload that `prepareRpcModule` destructures (each rewrite direction picks the fields
|
|
8
|
+
it needs). Replaces a positional argument list where ordering was load-bearing — a new build stamp
|
|
9
|
+
is now one field on this shape rather than a new positional slot every caller must thread. Every
|
|
10
|
+
field is optional: an absent field means the warm program resolved no verdict (no program built, an
|
|
11
|
+
unresolvable node), and the rewrite falls open to its char-scan / today's behaviour exactly as
|
|
12
|
+
before. `streaming` overrides the char-scan streaming verdict; the rest are stamped as server opts
|
|
13
|
+
(`coercion`→`coerce`, `inputSchema`→`inputJsonSchema`, `outputSchema`→`outputJsonSchema`,
|
|
14
|
+
`errorSchemas`→`errorJsonSchemas`) or the client opt (`outputWirePlan`).
|
|
15
|
+
*/
|
|
16
|
+
export type RpcBuildStamps = {
|
|
17
|
+
streaming?: boolean
|
|
18
|
+
coercion?: InputCoercion
|
|
19
|
+
inputSchema?: Record<string, unknown>
|
|
20
|
+
outputSchema?: Record<string, unknown>
|
|
21
|
+
errorSchemas?: ErrorJsonSchemas
|
|
22
|
+
outputWirePlan?: OutputWirePlan
|
|
23
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { HttpError } from '../HttpError.ts'
|
|
2
2
|
import type { ErrorSpec } from './ErrorSpec.ts'
|
|
3
|
-
import type { OutboxEntry } from './OutboxEntry.ts'
|
|
4
3
|
import type { StandardSchemaV1 } from './StandardSchemaV1.ts'
|
|
5
4
|
import type { ValidationErrorData } from './ValidationErrorData.ts'
|
|
6
5
|
|
|
@@ -16,9 +15,9 @@ type DeclaredErrorData<
|
|
|
16
15
|
/*
|
|
17
16
|
A rpc's `isError` guard — branch a caught error on a kind THIS rpc knows, with the
|
|
18
17
|
data type narrowed from the rpc's own `errors` spec. `rpc.isError(err, 'outOfStock')`
|
|
19
|
-
narrows `.data` to that error's payload; the framework-reserved `'validation'`
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
narrows `.data` to that error's payload; the framework-reserved `'validation'` to its
|
|
19
|
+
fixed shape; any other string narrows `.kind` only (`.data` stays `unknown` — it
|
|
20
|
+
belongs to a different rpc, whose type isn't in scope here).
|
|
22
21
|
|
|
23
22
|
The declared-name overload comes first so an app error shadows the reserved ones on a
|
|
24
23
|
name collision, and so the kind argument autocompletes to the rpc's declared names.
|
|
@@ -32,9 +31,5 @@ export interface RpcErrorGuard<Errors extends ErrorSpec> {
|
|
|
32
31
|
error: unknown,
|
|
33
32
|
kind: 'validation',
|
|
34
33
|
): error is HttpError & { kind: 'validation'; data: ValidationErrorData }
|
|
35
|
-
(
|
|
36
|
-
error: unknown,
|
|
37
|
-
kind: 'queued',
|
|
38
|
-
): error is HttpError & { kind: 'queued'; data: OutboxEntry<unknown> }
|
|
39
34
|
<Kind extends string>(error: unknown, kind: Kind): error is HttpError & { kind: Kind }
|
|
40
35
|
}
|
|
@@ -9,8 +9,8 @@ fan-out and a client-side ws proxy by build target. Iterating the socket
|
|
|
9
9
|
is the live stream — no replay. `.tail(count)` opens a subscription seeded
|
|
10
10
|
with the last `count` frames of the retained tail (declared via
|
|
11
11
|
`{ tail: n }`; no-arg = the whole retained tail) before going live — the
|
|
12
|
-
optional
|
|
13
|
-
seeds from. `
|
|
12
|
+
optional NamedAsyncIterable retention capability a reactive `watch(socket, …)`
|
|
13
|
+
seeds from. `publish` is isomorphic: server code publishes in-process
|
|
14
14
|
and fans out to remote subscribers; client code sends a `pub` frame the
|
|
15
15
|
dispatcher validates against the topic's `clientPublish` flag. `clients`
|
|
16
16
|
exposes which adapter surfaces (browser / mcp / cli) advertise this
|
|
@@ -21,8 +21,8 @@ export interface Socket<T> extends AsyncIterable<T> {
|
|
|
21
21
|
readonly clients: ClientFlags
|
|
22
22
|
/* Send a frame to every subscriber (server always; client only when `clientPublish` is
|
|
23
23
|
set). Isomorphic: server code fans out in-process + to remote subscribers; client code
|
|
24
|
-
sends a validated `pub` frame. */
|
|
25
|
-
|
|
24
|
+
sends a validated `pub` frame. Mirrors Bun's `server.publish()`. */
|
|
25
|
+
publish(message: T): void
|
|
26
26
|
tail(count?: number, hooks?: TailHooks): AsyncIterable<T>
|
|
27
27
|
/* The latest retained frame, synchronously — `T | undefined` when none. The value
|
|
28
28
|
member of the probe family for a stream; `peek(socket)` routes here. */
|
|
@@ -10,7 +10,8 @@ write-set and read-set can't drift apart on a matching-string-key handshake.
|
|
|
10
10
|
|
|
11
11
|
Three partitions:
|
|
12
12
|
- `SsrBootState` fields seed ambient slots via the exhaustive `seedBootState` map;
|
|
13
|
-
- `cache`
|
|
13
|
+
- `cache` / `cells` are the warm-seed partitions (settled `cache()` values / resolved async-cell
|
|
14
|
+
values), drained into their client stores before mount;
|
|
14
15
|
- `route`/`params` are stamped for inspectability and are NOT read by the client
|
|
15
16
|
(the router re-resolves the route from the URL).
|
|
16
17
|
*/
|
|
@@ -18,4 +19,7 @@ export type SsrPayload = SsrBootState & {
|
|
|
18
19
|
route: string
|
|
19
20
|
params: Record<string, string>
|
|
20
21
|
cache?: CacheSnapshotEntry[]
|
|
22
|
+
/* Async-cell values that resolved during SSR, keyed by render-path id → ref-json string.
|
|
23
|
+
`startClient` seeds these into `CELL_SEED`; a hydrating cell reads its key to warm-hydrate. */
|
|
24
|
+
cells?: Record<string, string>
|
|
21
25
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Endpoint stream policy, declared once on a streaming rpc definition (ADR-0020).
|
|
3
|
+
`n` is the replay depth — how many retained frames a fresh reader gets before
|
|
4
|
+
going live, pairing with the server-side `tail` retention. Endpoint-fixed: a
|
|
5
|
+
late joiner can't request deeper replay than a fresh view. No runtime consumer
|
|
6
|
+
today (subscribableFromResponse ignores it); threaded onto the definition for
|
|
7
|
+
completeness and future use.
|
|
8
|
+
*/
|
|
9
|
+
export type StreamPolicy = {
|
|
10
|
+
n?: number
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The per-request list of STREAMING async-cell values that SETTLED during this SSR pass, each keyed by
|
|
3
|
+
the cell's serialization-stable render-path id (`${scope.id}:${index}`) (ADR-0035). A streaming cell
|
|
4
|
+
ships pending in the shell (excluded from the pre-mount `__SSR__.cells` warm-seed, which would
|
|
5
|
+
diverge from that pending markup), so instead `createAsyncCell.settleValue` records its resolved
|
|
6
|
+
VALUE here when it settles server-side. The page renderer reads this AFTER the shell — streaming an
|
|
7
|
+
`__abideResolve({ cellKey, value })` chunk per entry — so the client adopts the server-resolved
|
|
8
|
+
value post-hydration (no `loading…` flash) instead of only cold-re-running the seed.
|
|
9
|
+
|
|
10
|
+
Values (not promises) so the drain never AWAITS — a streaming cell may legitimately stay pending
|
|
11
|
+
through the whole request (the `{#if getFoo()}`-holds case), and awaiting it would hang the
|
|
12
|
+
response. Only cells that settled before the drain point are streamed; one still pending is simply
|
|
13
|
+
not (the client cold-runs it, unchanged). The streaming sibling of `ResolvedCells` (the BLOCKING
|
|
14
|
+
warm-seed, read at render-return and stamped into `__SSR__.cells`); this one is read later in the
|
|
15
|
+
stream and shipped out-of-order as chunks.
|
|
16
|
+
*/
|
|
17
|
+
export type StreamedCells = {
|
|
18
|
+
entries: { key: string; value: unknown }[]
|
|
19
|
+
}
|
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import type { CacheSnapshotEntry } from './CacheSnapshotEntry.ts'
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
Payload of one streamed `window.__abideResolve(...)` call.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
Payload of one streamed `window.__abideResolve(...)` call. Three arms:
|
|
5
|
+
|
|
6
|
+
- a full `CacheSnapshotEntry` (has `key` + body) settles a cache placeholder with warm data;
|
|
7
|
+
- a `{ key, miss }` marker means the server couldn't snapshot that cache body (binary, rejected,
|
|
8
|
+
evicted) so the client settles the placeholder with a live re-fetch instead;
|
|
9
|
+
- a `{ cellKey, value }` (ADR-0035) carries a STREAMING CELL's server-resolved value, keyed by its
|
|
10
|
+
render-path warm-key and `encodeRefJson`-encoded, applied post-hydration as a reactive update so
|
|
11
|
+
a non-cache peek stops flashing `loading…` on hydrate.
|
|
12
|
+
- a `{ cellSeed, value }` (ADR-0039) carries a STREAMED CHILD's blocking async-cell value that
|
|
13
|
+
resolved AFTER the head `__SSR__.cells` snapshot (its render was deferred to the drain), seeded
|
|
14
|
+
PRE-mount into `CELL_SEED` — the same warm partition as `__SSR__.cells` — so the child's cell
|
|
15
|
+
constructs resolved on hydrate rather than re-running.
|
|
16
|
+
|
|
17
|
+
Discriminate on `cellSeed` then `cellKey` (cell) then `miss` (cache miss) then default (cache entry).
|
|
9
18
|
*/
|
|
10
|
-
|
|
19
|
+
|
|
20
|
+
/* The cache arms — a warm snapshot entry or a `{ key, miss }` marker. `streamCacheResolutions`
|
|
21
|
+
yields exactly these (never the cell arm), so its element type is `CacheResolution`, keeping a
|
|
22
|
+
`.key` access on its results well-typed. */
|
|
23
|
+
export type CacheResolution = CacheSnapshotEntry | { key: string; miss: true }
|
|
24
|
+
|
|
25
|
+
export type StreamedResolution =
|
|
26
|
+
| CacheResolution
|
|
27
|
+
| { cellKey: string; value: string }
|
|
28
|
+
| { cellSeed: string; value: string }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Hooks a `tail(count, hooks)` caller passes to a
|
|
2
|
+
Hooks a `tail(count, hooks)` caller passes to a NamedAsyncIterable's retention
|
|
3
3
|
capability.
|
|
4
4
|
`replayed` must be signalled in-band exactly once per iteration — after the
|
|
5
5
|
last replayed frame, before any live frame, and even when nothing was
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The wire codec kind for a single top-level Args field (ADR-0029). Generalizes ADR-0028's scalar
|
|
3
|
+
`number`/`boolean` query coercion to the structured value kinds the warm server program can name
|
|
4
|
+
through the type graph — a `Date` / `Set` / `Map` by symbol identity, a `bigint` by type flag.
|
|
5
|
+
parseArgs revives a field's plain-JSON wire form into the runtime value the handler's declared
|
|
6
|
+
type expects: an ISO string → `Date`, a numeric string → `bigint`, a JSON array → `Set`, a JSON
|
|
7
|
+
entries array (or object) → `Map`. Top-level fields only in this increment — a structured value
|
|
8
|
+
NESTED inside another value is not descended into. Reviving is fail-open: an unrevivable value is
|
|
9
|
+
left as its JSON form, never thrown, so the plain-JSON / OpenAPI contract still works.
|
|
10
|
+
*/
|
|
11
|
+
export type WireKind = 'number' | 'boolean' | 'date' | 'bigint' | 'set' | 'map'
|
package/src/lib/shared/url.ts
CHANGED
|
@@ -116,6 +116,11 @@ function interpolate(segments: RouteSegment[], params: Query): string {
|
|
|
116
116
|
if (segment.optional && (value === undefined || value === '')) {
|
|
117
117
|
return undefined
|
|
118
118
|
}
|
|
119
|
+
/* A required `[name]`/`[...rest]` with no value would otherwise stringify to the
|
|
120
|
+
literal "undefined" and silently mis-route; fail loudly instead. */
|
|
121
|
+
if (value === undefined || value === null) {
|
|
122
|
+
throw new Error(`abide url(): missing required param "${segment.name}"`)
|
|
123
|
+
}
|
|
119
124
|
const text = String(value)
|
|
120
125
|
return segment.catchAll
|
|
121
126
|
? text.split('/').map(encodeURIComponent).join('/')
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fieldErrorsFromIssues } from './fieldErrorsFromIssues.ts'
|
|
2
|
+
import { HttpError } from './HttpError.ts'
|
|
3
|
+
import type { StandardSchemaV1 } from './types/StandardSchemaV1.ts'
|
|
4
|
+
import type { ValidationErrorData } from './types/ValidationErrorData.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Builds the HttpError a client-side pre-flight validation failure throws (ADR-0026 D2),
|
|
8
|
+
shaped IDENTICALLY to the one a caller gets when the server rejects the same input with a
|
|
9
|
+
422: `kind: 'validation'`, `status: 422`, `data: ValidationErrorData` (`{ issues, fields }`),
|
|
10
|
+
so an `error instanceof HttpError && error.kind === 'validation'` branch — and the
|
|
11
|
+
`error.data.fields` form-error map — read the same whether the rejection came from the
|
|
12
|
+
client pre-flight or the server.
|
|
13
|
+
|
|
14
|
+
This is a UX optimization ONLY, never a trust boundary: the server's unconditional
|
|
15
|
+
`inputSchema['~standard'].validate` → `validationError` 422 (defineRpc.ts) stays
|
|
16
|
+
authoritative. A client that skips or fakes this check is still fully validated on the
|
|
17
|
+
server.
|
|
18
|
+
|
|
19
|
+
The `data` mapping (`fieldErrorsFromIssues`) is the same isomorphic helper the server 422
|
|
20
|
+
uses; the response envelope (`{ $abideError, data }`, status 422, reason phrase) mirrors
|
|
21
|
+
`typedErrorResponse('validation', 422, …)` — reconstructed here with a plain web-standard
|
|
22
|
+
`Response` so the browser bundle never imports the server's Response builder. The status
|
|
23
|
+
text mirrors `STATUS_TEXT[422]`.
|
|
24
|
+
*/
|
|
25
|
+
export function validationHttpError(issues: readonly StandardSchemaV1.Issue[]): HttpError {
|
|
26
|
+
const data: ValidationErrorData = { issues, fields: fieldErrorsFromIssues(issues) }
|
|
27
|
+
const response = new Response(JSON.stringify({ $abideError: 'validation', data }), {
|
|
28
|
+
status: 422,
|
|
29
|
+
statusText: 'Unprocessable Content',
|
|
30
|
+
headers: { 'content-type': 'application/json' },
|
|
31
|
+
})
|
|
32
|
+
return new HttpError(response, 'validation', data)
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The single source of the async-cell warm-seed key format: the `${scopeId}:${cellIndex}` string that
|
|
3
|
+
pairs a cell's serialization-stable render-path scope id with its per-scope declaration ordinal.
|
|
4
|
+
|
|
5
|
+
Both sides of the SSR→client handoff mint the key through THIS callable — the isomorphic
|
|
6
|
+
`createAsyncCell` runs it server-side to key its `resolvedCells`/`streamedCells` entries (which the
|
|
7
|
+
page renderer stamps into `__SSR__.cells` / streams as `{ cellKey }` / `{ cellSeed }` chunks), and
|
|
8
|
+
client-side to look the value up in `CELL_SEED`. Because the two sides now invoke one function, the
|
|
9
|
+
byte-for-byte agreement the warm-seed depends on is a shared definition rather than a template
|
|
10
|
+
literal re-typed at each call site: changing the delimiter here changes it for both sides at once,
|
|
11
|
+
so the SSR-text-≡-first-client-render mismatch class (ADR-0033) cannot silently reopen from a
|
|
12
|
+
one-sided edit. Pure so it stays trivially testable and inlinable.
|
|
13
|
+
*/
|
|
14
|
+
export function warmSeedKey(scopeId: string, cellIndex: number): string {
|
|
15
|
+
return `${scopeId}:${cellIndex}`
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The value-directed wire ENCODE step for a rpc response body (ADR-0029 output path). A
|
|
3
|
+
`JSON.stringify` replacer that rewrites the structured runtime values plain JSON can't carry into
|
|
4
|
+
their honest-JSON wire form, so `json()` never drops a `Set`/`Map` to `{}` or throws on a `bigint`:
|
|
5
|
+
|
|
6
|
+
- `bigint` → its digit string (plain JSON.stringify throws on a bigint)
|
|
7
|
+
- `Set<T>` → a JSON array of its values (plain JSON.stringify emits `{}`)
|
|
8
|
+
- `Map<K,V>`→ a JSON array of `[K,V]` entries (uniform, carries non-string keys)
|
|
9
|
+
|
|
10
|
+
A `Date` needs no branch — its native `toJSON` already rewrote it to an ISO string before the
|
|
11
|
+
replacer runs. Value-directed (not type-directed), so it runs for EVERY client — a non-abide caller
|
|
12
|
+
(curl / an OpenAPI SDK) receives the same honest JSON the projected schema (`jsonSchemaForType`)
|
|
13
|
+
describes. It is naturally recursive: a returned array/object member surfaces through the same
|
|
14
|
+
replacer, so a `Set` nested inside an object still encodes. Never throws — an ordinary value falls
|
|
15
|
+
through untouched.
|
|
16
|
+
*/
|
|
17
|
+
export function wireJsonReplacer(_key: string, value: unknown): unknown {
|
|
18
|
+
if (typeof value === 'bigint') {
|
|
19
|
+
return value.toString()
|
|
20
|
+
}
|
|
21
|
+
if (value instanceof Set) {
|
|
22
|
+
return [...value]
|
|
23
|
+
}
|
|
24
|
+
if (value instanceof Map) {
|
|
25
|
+
/* A Map iterates as `[key, value]` pairs, so the spread yields the entries array the
|
|
26
|
+
client output plan (and the projected schema) expects. */
|
|
27
|
+
return [...value]
|
|
28
|
+
}
|
|
29
|
+
return value
|
|
30
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
1
2
|
import { augmentModule } from './augmentModule.ts'
|
|
2
3
|
import { carriesBodyArgs } from './carriesBodyArgs.ts'
|
|
3
4
|
import { detectRpcMethod } from './detectRpcMethod.ts'
|
|
4
5
|
import { fileStem } from './fileStem.ts'
|
|
5
6
|
import { RPC_ARGS_TYPE } from './RPC_ARGS_TYPE.ts'
|
|
6
7
|
import { rpcUrlForFile } from './rpcUrlForFile.ts'
|
|
8
|
+
import type { HttpMethod } from './types/HttpMethod.ts'
|
|
7
9
|
import { writeDts } from './writeDts.ts'
|
|
8
10
|
|
|
9
11
|
/*
|
|
@@ -22,15 +24,23 @@ export async function writeRpcDts({
|
|
|
22
24
|
rpcDir,
|
|
23
25
|
rpcFiles,
|
|
24
26
|
importName,
|
|
27
|
+
methodForModule,
|
|
25
28
|
}: {
|
|
26
29
|
cwd: string
|
|
27
30
|
rpcDir: string
|
|
28
31
|
rpcFiles: string[]
|
|
29
32
|
importName: string
|
|
33
|
+
/* Warm server-program method resolver (ADR-0025 D2): reads the method off the export's
|
|
34
|
+
helper symbol so an aliased/re-exported helper resolves where `detectRpcMethod`'s
|
|
35
|
+
`RPC_EXPORT` regex misses it. Absent (no warm program) or undefined-per-file falls open to
|
|
36
|
+
the regex, byte-identical to before. */
|
|
37
|
+
methodForModule?: (modulePath: string) => HttpMethod | undefined
|
|
30
38
|
}): Promise<void> {
|
|
31
39
|
const pairs = await Promise.all(
|
|
32
40
|
rpcFiles.map(async (file): Promise<[string, string] | undefined> => {
|
|
33
|
-
const method =
|
|
41
|
+
const method =
|
|
42
|
+
methodForModule?.(resolve(rpcDir, file)) ??
|
|
43
|
+
detectRpcMethod(await Bun.file(`${rpcDir}/${file}`).text())
|
|
34
44
|
// A body rpc's args can't ride a URL — leave it out of the url() rpc map.
|
|
35
45
|
if (!method || carriesBodyArgs(method)) {
|
|
36
46
|
return undefined
|
|
@@ -8,7 +8,7 @@ Emits a `.d.ts` that augments createTestApp's `SocketClient` interface with one
|
|
|
8
8
|
entry per $socket, keyed by socket name (the same key `app.sockets.<name>`
|
|
9
9
|
resolves at runtime). The `socket(...)` helper statically returns `Socket<T>`,
|
|
10
10
|
so each entry is the export's own type directly — `app.sockets.chat` types as
|
|
11
|
-
`Socket<ChatMessage>`, iterable for the live stream with `.tail`/`.
|
|
11
|
+
`Socket<ChatMessage>`, iterable for the live stream with `.tail`/`.publish`.
|
|
12
12
|
Written to `src/.abide/testSockets.d.ts` like its rpc sibling.
|
|
13
13
|
*/
|
|
14
14
|
export async function writeTestSocketsDts({
|
|
@@ -39,8 +39,11 @@ import { commandNameForUrl } from '../shared/commandNameForUrl.ts'
|
|
|
39
39
|
import { createCacheStore } from '../shared/createCacheStore.ts'
|
|
40
40
|
import { HEALTH_PATH } from '../shared/HEALTH_PATH.ts'
|
|
41
41
|
import { pageSlot } from '../shared/pageSlot.ts'
|
|
42
|
+
import { pendingAsyncCellsSlot } from '../shared/pendingAsyncCellsSlot.ts'
|
|
43
|
+
import { resolvedCellsSlot } from '../shared/resolvedCellsSlot.ts'
|
|
42
44
|
import { SOCKETS_PATH } from '../shared/SOCKETS_PATH.ts'
|
|
43
45
|
import { sharedCacheStoreSlot } from '../shared/sharedCacheStoreSlot.ts'
|
|
46
|
+
import { streamedCellsSlot } from '../shared/streamedCellsSlot.ts'
|
|
44
47
|
import type { RemoteFunction } from '../shared/types/RemoteFunction.ts'
|
|
45
48
|
import type { Socket } from '../shared/types/Socket.ts'
|
|
46
49
|
import { createTestSocketChannel } from './createTestSocketChannel.ts'
|
|
@@ -72,7 +75,7 @@ export type TestApp = {
|
|
|
72
75
|
/* Rpc calls over HTTP, keyed by command name: `app.rpc.getProduct({ id })`. */
|
|
73
76
|
rpc: RpcClient
|
|
74
77
|
/* Sockets keyed by name: `app.sockets.ticker` is the Socket — iterate it for
|
|
75
|
-
the live stream, `.tail(n)` to seed, `.
|
|
78
|
+
the live stream, `.tail(n)` to seed, `.publish(m)` to send. */
|
|
76
79
|
sockets: SocketClient
|
|
77
80
|
/* The /__abide/health payload, decoded. */
|
|
78
81
|
health: () => Promise<unknown>
|
|
@@ -101,12 +104,24 @@ export async function createTestApp(): Promise<TestApp> {
|
|
|
101
104
|
sharedResolver: sharedCacheStoreSlot.resolver,
|
|
102
105
|
pageResolver: pageSlot.resolver,
|
|
103
106
|
baseResolver: baseSlot.resolver,
|
|
107
|
+
pendingAsyncCellsResolver: pendingAsyncCellsSlot.resolver,
|
|
108
|
+
resolvedCellsResolver: resolvedCellsSlot.resolver,
|
|
109
|
+
streamedCellsResolver: streamedCellsSlot.resolver,
|
|
104
110
|
activeServer: serverSlot.active,
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
const sharedStore = createCacheStore()
|
|
108
114
|
sharedCacheStoreSlot.resolver = () => sharedStore
|
|
109
115
|
cacheStoreSlot.resolver = () => requestContext.getStore()?.cache ?? sharedStore
|
|
116
|
+
const sharedPendingAsyncCells = { promises: [] }
|
|
117
|
+
pendingAsyncCellsSlot.resolver = () =>
|
|
118
|
+
requestContext.getStore()?.pendingAsyncCells ?? sharedPendingAsyncCells
|
|
119
|
+
const sharedResolvedCells = { entries: [] }
|
|
120
|
+
resolvedCellsSlot.resolver = () =>
|
|
121
|
+
requestContext.getStore()?.resolvedCells ?? sharedResolvedCells
|
|
122
|
+
const sharedStreamedCells = { entries: [] }
|
|
123
|
+
streamedCellsSlot.resolver = () =>
|
|
124
|
+
requestContext.getStore()?.streamedCells ?? sharedStreamedCells
|
|
110
125
|
pageSlot.resolver = resolvePageSnapshot
|
|
111
126
|
|
|
112
127
|
/* Eager env validation, exactly as serverEntry: a top-level env(schema) in
|
|
@@ -188,6 +203,9 @@ export async function createTestApp(): Promise<TestApp> {
|
|
|
188
203
|
sharedCacheStoreSlot.resolver = previous.sharedResolver
|
|
189
204
|
pageSlot.resolver = previous.pageResolver
|
|
190
205
|
baseSlot.resolver = previous.baseResolver
|
|
206
|
+
pendingAsyncCellsSlot.resolver = previous.pendingAsyncCellsResolver
|
|
207
|
+
resolvedCellsSlot.resolver = previous.resolvedCellsResolver
|
|
208
|
+
streamedCellsSlot.resolver = previous.streamedCellsResolver
|
|
191
209
|
serverSlot.active = previous.activeServer
|
|
192
210
|
}
|
|
193
211
|
|
package/src/lib/ui/README.md
CHANGED
|
@@ -61,7 +61,7 @@ write-path microbench this runs ~20× faster than a deep-proxy signal baseline.
|
|
|
61
61
|
|
|
62
62
|
- `compileSSR` renders to an HTML string (byte-identical to the client DOM).
|
|
63
63
|
- `renderToStream` streams: pending shell first, then resolved `<template await>`
|
|
64
|
-
fragments out of order as their promises settle;
|
|
64
|
+
fragments out of order as their promises settle; the inline swap script swaps them in.
|
|
65
65
|
- `hydrate` adopts the server DOM in place (no re-render) for static-structure
|
|
66
66
|
components — claims existing nodes, splits merged text, wires effects/listeners.
|
|
67
67
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { pendingAsyncCellsSlot } from '../shared/pendingAsyncCellsSlot.ts'
|
|
2
|
+
import type { PendingAsyncCells } from '../shared/types/PendingAsyncCells.ts'
|
|
3
|
+
import { cellBarrierBacking } from './runtime/cellBarrierBacking.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The async-cell barrier list a registration (`createAsyncCell`) or a drain (`settleAsyncCells`) should
|
|
7
|
+
use RIGHT NOW: a per-render isolated list if one is active (a hoisted concurrent child render — see
|
|
8
|
+
`isolateCellBarrier`), else the per-request list from `pendingAsyncCellsSlot`. The single seam both
|
|
9
|
+
barrier consumers read, so an isolated render can't leak its in-flight cells into a sibling's or the
|
|
10
|
+
page's drain. Undefined only when neither a render override nor a request/fallback list is installed.
|
|
11
|
+
*/
|
|
12
|
+
export function activePendingCells(): PendingAsyncCells | undefined {
|
|
13
|
+
return cellBarrierBacking.active.current() ?? pendingAsyncCellsSlot.get()
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The control-block vocabulary — the ONE source of truth shared by the parser
|
|
3
|
+
(`parseTemplate`'s dispatch/error) and the LSP highlighter (`structuralHeadTokens`, driven
|
|
4
|
+
by the parse walk), so the keywords the highlighter colors can't drift from the blocks the
|
|
5
|
+
parser accepts (the `{#snippet}` head once went uncolored because a hand-copy omitted it).
|
|
6
|
+
|
|
7
|
+
`BLOCK_OPENERS` follow `{#`, `BLOCK_CONNECTORS` follow `{:`. `for await` is the async-each
|
|
8
|
+
opener form the highlighter colors as one phrase (the parser reads the `await` inside a
|
|
9
|
+
`for` head); it is not a distinct opener.
|
|
10
|
+
*/
|
|
11
|
+
export const BLOCK_OPENERS = ['if', 'for', 'await', 'switch', 'try', 'snippet'] as const
|
|
12
|
+
|
|
13
|
+
export const BLOCK_CONNECTORS = [
|
|
14
|
+
'else if',
|
|
15
|
+
'else',
|
|
16
|
+
'then',
|
|
17
|
+
'catch',
|
|
18
|
+
'finally',
|
|
19
|
+
'case',
|
|
20
|
+
'default',
|
|
21
|
+
] as const
|
|
@@ -17,7 +17,10 @@ across bundles. Emitted inline (not imported) so the generated render module is
|
|
|
17
17
|
self-contained.
|
|
18
18
|
*/
|
|
19
19
|
export const SSR_ESCAPE =
|
|
20
|
-
|
|
20
|
+
// Nullish escapes to empty text, never the literal `"undefined"` — a pending async
|
|
21
|
+
// read (ADR-0032 D3) renders nothing. `$attr` guards nullish before it calls `$esc`,
|
|
22
|
+
// so its whole-value undefined still drops the attribute.
|
|
23
|
+
'const $esc = (v) => (v == null ? "" : String(v)).replace(/[&<>"\']/g, (c) => ' +
|
|
21
24
|
"({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' })[c]);\n" +
|
|
22
25
|
'const $attr = (n, v) => (v === false || v === null || v === undefined) ? "" ' +
|
|
23
26
|
': v === true ? (" " + n) : (" " + n + \'="\' + $esc(v) + \'"\');\n' +
|