@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,8 +1,10 @@
|
|
|
1
1
|
/* The per-request SSR render context, threaded through a layout chain and every
|
|
2
|
-
child component a page inlines.
|
|
3
|
-
block draws
|
|
4
|
-
|
|
5
|
-
the
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
child component a page inlines. It is the block-id counter MAP (ADR-0037): each
|
|
3
|
+
`await`/`try` block draws a path-namespaced id via `blockId(ctx)` — keyed by the
|
|
4
|
+
ambient render-path, counting 0,1,2… in document order WITHIN each path. Both sides
|
|
5
|
+
compose the same ids (the client mounts each child under the same path), so the
|
|
6
|
+
streamed fragments and the `RESUME` manifest line up. Request-local (not a module
|
|
7
|
+
global) because SSR render is async and concurrent — but sibling child renders write
|
|
8
|
+
different path keys, so the shared map needs no locking and survives parallel renders
|
|
9
|
+
(which a single flat counter could not). */
|
|
10
|
+
export type RenderContext = Map<string, number>
|
|
@@ -9,10 +9,17 @@ import type { ResumeEntry } from '../RESUME.ts'
|
|
|
9
9
|
on the tag — never land here: they render inline during the async render pass and
|
|
10
10
|
seed `SsrRender.resume`.) */
|
|
11
11
|
export type SsrAwait = {
|
|
12
|
-
id:
|
|
12
|
+
id: string
|
|
13
13
|
promise: () => unknown
|
|
14
14
|
then: (value: unknown) => Promise<string>
|
|
15
15
|
catch?: (error: unknown) => Promise<string>
|
|
16
|
+
/* ADR-0039 (spike): a STANDALONE-UNIT boundary — a streamed child COMPONENT — carries no
|
|
17
|
+
resume value of its own (its `then` returns the child's rendered html and merges the child's
|
|
18
|
+
own awaits/resume for nested composition; the child re-mounts client-side rather than
|
|
19
|
+
adopting a RESUME[id] value). Marks `settle` to emit an html-only fragment with no resume seed
|
|
20
|
+
script, so the client never tries to decode a whole SsrRender as an await value. Absent for a
|
|
21
|
+
normal await block (which seeds its resolved value). */
|
|
22
|
+
htmlOnly?: boolean
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
/* The result of a component's server `render()`: the pending-shell HTML, the
|
|
@@ -23,5 +30,5 @@ export type SsrRender = {
|
|
|
23
30
|
html: string
|
|
24
31
|
state: unknown
|
|
25
32
|
awaits: SsrAwait[]
|
|
26
|
-
resume: Record<
|
|
33
|
+
resume: Record<string, ResumeEntry>
|
|
27
34
|
}
|
|
@@ -18,11 +18,7 @@ export type UiComponent = ((host: Element, props?: UiProps) => () => void) & {
|
|
|
18
18
|
hydrate?: (host: Element, props?: UiProps) => () => void
|
|
19
19
|
/* The bare client build (`(host, props) => void`) — appends the component's nodes
|
|
20
20
|
to `host`. A nested child mounts it into a marker range (`mountRange`/`mountChild`)
|
|
21
|
-
instead of the wrapped `mount
|
|
22
|
-
module's `build` on edit. */
|
|
21
|
+
instead of the wrapped `mount`. */
|
|
23
22
|
build: (host: Node, props?: UiProps) => void
|
|
24
23
|
hydratable?: boolean
|
|
25
|
-
/* Stable module id (project-relative source path) stamped by `compileModule`,
|
|
26
|
-
keying the component in the hot-reload registry — see `mountChild`. */
|
|
27
|
-
__abideId?: string
|
|
28
24
|
}
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
What a component is invoked with. A top-level page/layout is called by the router
|
|
3
3
|
(client) and `renderChain` (SSR) with its route params as reactive thunks, shaped
|
|
4
4
|
identically to the thunk map `mountChild` passes a nested child — so `props()` reads
|
|
5
|
-
(`$props[name]?.()`) work uniformly on both.
|
|
6
|
-
component
|
|
7
|
-
the router/SSR set
|
|
5
|
+
(`$props[name]?.()`) work uniformly on both. `children` is an ordinary prop thunk like
|
|
6
|
+
any other: on a component it returns the parent's `Snippet`; on a layout with a child
|
|
7
|
+
layer below it, the router/SSR set it to `() => CHILD_PRESENT` (a presence sentinel, not
|
|
8
|
+
a slot builder) so `{#if children}` reads truthy.
|
|
8
9
|
*/
|
|
9
|
-
export type UiProps = Record<string, (
|
|
10
|
-
|
|
10
|
+
export type UiProps = Record<string, () => unknown> & {
|
|
11
|
+
children?: () => unknown
|
|
11
12
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { withPath } from './withPath.ts'
|
|
2
|
+
|
|
3
|
+
/* Runs `build` under the render-path segment for a compiler `ordinal`, or plainly when `ordinal`
|
|
4
|
+
is undefined (a non-compiled caller that supplied no site id). Shared by the child-mount paths so
|
|
5
|
+
the "push a path segment iff an ordinal is present" convention lives in one place. */
|
|
6
|
+
export function withOptionalPath<T>(ordinal: number | undefined, build: () => T): T {
|
|
7
|
+
return ordinal === undefined ? build() : withPath(ordinal, build)
|
|
8
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CURRENT_PATH } from './CURRENT_PATH.ts'
|
|
2
|
+
import { withPathFrom } from './withPathFrom.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Pushes one render-path segment for the duration of `build`, RELATIVE to the live ambient path,
|
|
6
|
+
restoring after (synchronous build, so the save/restore is exact and nesting is a strict stack —
|
|
7
|
+
the same shape `withScope`/`enterRenderPass` use). The segment is `escapeKey`-escaped and joined
|
|
8
|
+
with `/`, so a segment carrying a `/` (a URL-shaped `{#each}` key) survives as one element. Used
|
|
9
|
+
at sites that run DURING the initial render walk (a layout layer, a `<Child/>` mount) where the
|
|
10
|
+
ambient path is the correct base. A control-flow block that rebuilds reactively after mount uses
|
|
11
|
+
`withPathFrom` with a captured base instead.
|
|
12
|
+
*/
|
|
13
|
+
// @documentation plumbing
|
|
14
|
+
export function withPath<T>(segment: string | number, build: () => T): T {
|
|
15
|
+
return withPathFrom(CURRENT_PATH.current, segment, build)
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ambientPathBacking } from './ambientPathBacking.ts'
|
|
2
|
+
import { escapeKey } from './escapeKey.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Runs `build` with the render path set to `base` + one escaped `segment` (ADR-0033 D1). Unlike
|
|
6
|
+
`withPath` (which pushes relative to the LIVE ambient), this composes from an EXPLICIT base — for
|
|
7
|
+
a control-flow block that rebuilds content reactively AFTER mount, when the ambient path is no
|
|
8
|
+
longer on the stack. The block captures `CURRENT_PATH.current` at construction (its render-time
|
|
9
|
+
ancestry) and re-establishes `base/segment` for every branch/row build — initial and every later
|
|
10
|
+
swap — so a component or cell built on a swap still gets its full, stable id, not a bare segment.
|
|
11
|
+
|
|
12
|
+
Pushes through the backing's `run` (not a set-then-restore slot): the server's AsyncLocalStorage
|
|
13
|
+
backing makes the composed path survive an `await` inside `build`, so a render body resuming after
|
|
14
|
+
a cell barrier or child render reads its own path; the client's synchronous module-var `run` is a
|
|
15
|
+
plain save/restore. `run` returns `build()`'s value (a value on the client, a promise on the server).
|
|
16
|
+
*/
|
|
17
|
+
export function withPathFrom<T>(base: string, segment: string | number, build: () => T): T {
|
|
18
|
+
const composedPath = base === '' ? escapeKey(segment) : `${base}/${escapeKey(segment)}`
|
|
19
|
+
return ambientPathBacking.active.run(composedPath, build)
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RENDER } from './RENDER.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Runs `build` with the hydration claim cursor cleared, restoring it after. The
|
|
5
|
+
control-flow blocks route FRESH builds through here — a pending/resolved await
|
|
6
|
+
branch, a try's catch branch, a fillBefore insert — so their build helpers
|
|
7
|
+
create nodes instead of trying to claim server DOM that isn't there. A rebuild
|
|
8
|
+
that fires while an outer hydrate pass is still active (e.g. a synchronous write
|
|
9
|
+
that flips a `when`/`switch` mid-hydrate) would otherwise make `cloneStatic` /
|
|
10
|
+
text claim discarded or nonexistent nodes and silently render nothing. The
|
|
11
|
+
`finally` restore lets a fresh build sit inside an ongoing hydrate pass without
|
|
12
|
+
ending it early.
|
|
13
|
+
*/
|
|
14
|
+
export function withoutHydration<T>(build: () => T): T {
|
|
15
|
+
const previous = RENDER.hydration
|
|
16
|
+
RENDER.hydration = undefined
|
|
17
|
+
try {
|
|
18
|
+
return build()
|
|
19
|
+
} finally {
|
|
20
|
+
RENDER.hydration = previous
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { activeCacheStore } from '../shared/activeCacheStore.ts'
|
|
2
2
|
import { cacheEntryFromSnapshot } from '../shared/cacheEntryFromSnapshot.ts'
|
|
3
|
+
import { decodeRefJson } from '../shared/decodeRefJson.ts'
|
|
3
4
|
import type { StreamedResolution } from '../shared/types/StreamedResolution.ts'
|
|
5
|
+
import { CELL_SEED } from './runtime/CELL_SEED.ts'
|
|
6
|
+
import { receiveStreamedCell } from './runtime/STREAMED_CELLS.ts'
|
|
4
7
|
|
|
5
8
|
/*
|
|
6
9
|
Seeds one streamed cache resolution into the active store — the single sink for the
|
|
@@ -9,12 +12,27 @@ so a `cache()` read resolves synchronously (no wire round-trip) and `<template a
|
|
|
9
12
|
adopts without a refetch; a `{ key, miss }` marker — a body the server couldn't snapshot
|
|
10
13
|
(binary / rejected / evicted) — is a no-op, so that read falls back to a live fetch.
|
|
11
14
|
|
|
12
|
-
Shared by startClient's boot drain
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
Shared by startClient's boot drain and the live `window.__abideResolve`, so no
|
|
16
|
+
resolved-frame consumer can swap DOM while silently dropping the cache channel — the
|
|
17
|
+
asymmetry that made streamed reads cold-miss to the network.
|
|
15
18
|
*/
|
|
16
19
|
// @documentation plumbing
|
|
17
20
|
export function seedStreamedResolution(resolution: StreamedResolution): void {
|
|
21
|
+
/* ADR-0039: a STREAMED CHILD's blocking async-cell value that resolved after the head snapshot.
|
|
22
|
+
Seed it into `CELL_SEED` (the same warm partition `__SSR__.cells` fills, storing the raw
|
|
23
|
+
ref-json string decoded at cell read) so the child's cell constructs RESOLVED when its
|
|
24
|
+
deferred mount runs — no flash, no re-run. Checked before `cellKey` (a distinct arm). */
|
|
25
|
+
if ('cellSeed' in resolution) {
|
|
26
|
+
CELL_SEED[resolution.cellSeed] = resolution.value
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
/* ADR-0035: a streaming CELL's server-resolved value, keyed by render-path id. Route it to the
|
|
30
|
+
mounted cell (or buffer until it registers) — decoded through the same ref-json codec the
|
|
31
|
+
server encoded it with, so a Set/Map/Date/bigint/cyclic value survives. */
|
|
32
|
+
if ('cellKey' in resolution) {
|
|
33
|
+
receiveStreamedCell(resolution.cellKey, decodeRefJson(resolution.value))
|
|
34
|
+
return
|
|
35
|
+
}
|
|
18
36
|
if ('miss' in resolution) {
|
|
19
37
|
return
|
|
20
38
|
}
|
|
@@ -22,10 +40,17 @@ export function seedStreamedResolution(resolution: StreamedResolution): void {
|
|
|
22
40
|
an unconsumed hydrated seed (`hydrated === true`, cleared by the first cache()
|
|
23
41
|
read). A live/settled non-hydrated entry is authoritative; clobbering it with a
|
|
24
42
|
stale snapshot would drop a fresher value (e.g. one a live fetch already wrote). */
|
|
25
|
-
const
|
|
26
|
-
const existing = entries.get(resolution.key)
|
|
43
|
+
const store = activeCacheStore()
|
|
44
|
+
const existing = store.entries.get(resolution.key)
|
|
27
45
|
if (existing !== undefined && existing.hydrated !== true) {
|
|
28
46
|
return
|
|
29
47
|
}
|
|
30
|
-
entries.set(resolution.key, cacheEntryFromSnapshot(resolution))
|
|
48
|
+
store.entries.set(resolution.key, cacheEntryFromSnapshot(resolution))
|
|
49
|
+
/* Wake any reader subscribed to this key's lifecycle channel. Seeding used to rely on
|
|
50
|
+
seed-before-mount ordering (valid for the await path, whose subscription reads the
|
|
51
|
+
resume manifest, not the cache); an auto-streamed BARE read (ADR-0024) has already
|
|
52
|
+
mounted and its throwing-peek subscribed the key, so without an explicit dispatch the
|
|
53
|
+
peek that read `undefined` never re-runs on the streamed value. markLifecycle re-derives
|
|
54
|
+
the peek scope — the same wake materializeRetained relies on for a late-landing value. */
|
|
55
|
+
store.markLifecycle(resolution.key)
|
|
31
56
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { activePendingCells } from './activePendingCells.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The SSR await-barrier the compiler lowers to `$$settleAsyncCells()` between a
|
|
5
|
+
component's cell declarations and its template (ADR-0019 Tier-2). It DRAINS the
|
|
6
|
+
request-scoped pending list (`splice(0)`) so each barrier awaits only the cells
|
|
7
|
+
declared since the last one — `renderChain` renders layout layers sequentially,
|
|
8
|
+
each with its own barrier, and a drain keeps a later layer from re-awaiting an
|
|
9
|
+
earlier settled layer's cells. `Promise.allSettled` never rejects the render: a
|
|
10
|
+
rejected cell settles into its `error()` state, surfaced at the read site (via
|
|
11
|
+
`$$readCell`), not here. On the client the list is always empty (cells register
|
|
12
|
+
server-side only), so the barrier is a no-op await.
|
|
13
|
+
*/
|
|
14
|
+
// @documentation plumbing
|
|
15
|
+
export async function settleAsyncCells(): Promise<void> {
|
|
16
|
+
const pending = activePendingCells()
|
|
17
|
+
if (pending === undefined) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
const promises = pending.promises.splice(0)
|
|
21
|
+
if (promises.length > 0) {
|
|
22
|
+
await Promise.allSettled(promises)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -11,7 +11,7 @@ over the multiplexed ws channel). Both paths produce identical Socket
|
|
|
11
11
|
shapes so user code reads the same on either side.
|
|
12
12
|
|
|
13
13
|
The Socket surface — bare iteration as the live stream, `.tail(n)` seeded
|
|
14
|
-
from the retained tail, `.
|
|
14
|
+
from the retained tail, `.publish` sending a server-validated `pub` frame —
|
|
15
15
|
is built by buildSocketOverChannel over the page's lazily-opened singleton
|
|
16
16
|
channel; this module only binds that builder to the browser channel so the
|
|
17
17
|
test harness can reuse the identical surface over its own channel.
|
|
@@ -5,18 +5,12 @@ import { sharedCacheStoreSlot } from '../shared/sharedCacheStoreSlot.ts'
|
|
|
5
5
|
import type { SsrPayload } from '../shared/types/SsrPayload.ts'
|
|
6
6
|
import type { StreamedResolution } from '../shared/types/StreamedResolution.ts'
|
|
7
7
|
import { probeNavigation } from './probeNavigation.ts'
|
|
8
|
-
import { seedBootState } from './seedBootState.ts'
|
|
9
|
-
|
|
10
|
-
/* Build-time flag the production client defines false (see build.ts `define`) so the dev-only
|
|
11
|
-
hot bridge — and the entire DOM runtime it statically pulls in for re-builds — is dead-code-
|
|
12
|
-
eliminated, not even emitted as a chunk. Dev defines it true; the test preload sets it on
|
|
13
|
-
globalThis so the bare reference resolves there. */
|
|
14
|
-
declare const __ABIDE_DEV__: boolean
|
|
15
|
-
|
|
16
8
|
import { router } from './router.ts'
|
|
9
|
+
import { CELL_SEED } from './runtime/CELL_SEED.ts'
|
|
17
10
|
import { clientPage } from './runtime/clientPage.ts'
|
|
18
11
|
import type { RouteLoader } from './runtime/types/RouteLoader.ts'
|
|
19
|
-
import {
|
|
12
|
+
import { seedBootState } from './seedBootState.ts'
|
|
13
|
+
import { seedStreamedResolution } from './seedStreamedResolution.ts'
|
|
20
14
|
|
|
21
15
|
/*
|
|
22
16
|
The official abide-ui client entry. Reads the server's `window.__SSR__` payload,
|
|
@@ -40,21 +34,6 @@ export function startClient(
|
|
|
40
34
|
if (target === null) {
|
|
41
35
|
throw new Error('[abide] startClient: missing #app target')
|
|
42
36
|
}
|
|
43
|
-
/* Dev only: the live-reload script sets `__abideDev` before this module runs, so the
|
|
44
|
-
runtime bridge is in place before any component mounts and records its instances
|
|
45
|
-
(mountChild) for hot replacement. Lazy-imported and `__ABIDE_DEV__`-gated so the
|
|
46
|
-
bridge (and the DOM runtime it drags in) is fully dead-code-eliminated in production
|
|
47
|
-
rather than shipped behind a runtime flag the minifier can't prove false. */
|
|
48
|
-
if (__ABIDE_DEV__ && (globalThis as { __abideDev?: boolean }).__abideDev) {
|
|
49
|
-
import('./installHotBridge.ts').then((module) => module.installHotBridge())
|
|
50
|
-
}
|
|
51
|
-
/* Inspector only: the server injects `__abideInspect` when ABIDE_ENABLE_INSPECTOR is on,
|
|
52
|
-
so the scope/router bridge arms before the router builds any scope. Inspector can be
|
|
53
|
-
enabled in production, so this stays a lazy chunk (not `__ABIDE_DEV__`-gated) — emitted
|
|
54
|
-
but fetched only when the flag is set, never weighing down the default client load. */
|
|
55
|
-
if ((globalThis as { __abideInspect?: boolean }).__abideInspect) {
|
|
56
|
-
import('./installInspectorBridge.ts').then((module) => module.installInspectorBridge())
|
|
57
|
-
}
|
|
58
37
|
const ssr = (globalThis as { __SSR__?: Partial<SsrPayload> }).__SSR__ ?? {}
|
|
59
38
|
/* Seed the per-page __SSR__ stamps into their shared slots before mount: the mount
|
|
60
39
|
base, app name (default log channel), health payload (so health()'s first probe is
|
|
@@ -78,16 +57,22 @@ export function startClient(
|
|
|
78
57
|
const streamed =
|
|
79
58
|
(globalThis as { __abideResumeCache?: StreamedResolution[] }).__abideResumeCache ?? []
|
|
80
59
|
for (const resolution of [...(ssr.cache ?? []), ...streamed]) {
|
|
81
|
-
|
|
60
|
+
seedStreamedResolution(resolution)
|
|
61
|
+
}
|
|
62
|
+
/* Seed the async-cell warm partition into `CELL_SEED` before mount: a hydrating
|
|
63
|
+
`createAsyncCell` reads its render-path key here to adopt the SSR-resolved value warm
|
|
64
|
+
instead of re-running its seed cold (Object.assign so an inline pre-bundle script that
|
|
65
|
+
already populated `__abideCells` isn't clobbered). */
|
|
66
|
+
if (ssr.cells !== undefined) {
|
|
67
|
+
Object.assign(CELL_SEED, ssr.cells)
|
|
82
68
|
}
|
|
83
69
|
/* Keep the cache channel live past boot: replace the head's buffering collector with
|
|
84
|
-
the store-connected sink so a post-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
through the one intake seam. */
|
|
70
|
+
the store-connected sink so a post-boot resolution — the inline doc-stream cache
|
|
71
|
+
script — seeds the store through `seedStreamedResolution` directly instead of pushing
|
|
72
|
+
to a buffer nothing drains again. The inline doc-stream script only ever hands this a
|
|
73
|
+
cache `StreamedResolution`. */
|
|
89
74
|
;(globalThis as { __abideResolve?: (resolution: StreamedResolution) => void }).__abideResolve =
|
|
90
|
-
|
|
75
|
+
seedStreamedResolution
|
|
91
76
|
|
|
92
77
|
return router(target, routes, layoutRoutes, probeNavigation)
|
|
93
78
|
}
|
package/src/lib/ui/state.ts
CHANGED
|
@@ -14,11 +14,17 @@ import { scope } from './scope.ts'
|
|
|
14
14
|
reached ambiently so a component can pass a named value down its subtree. */
|
|
15
15
|
type StateFn = {
|
|
16
16
|
/* No-arg form for an undefined initial with a declared type: `state<Foo>()` is
|
|
17
|
-
`State<Foo | undefined>`. Without it `state
|
|
18
|
-
|
|
19
|
-
narrows to `never`). */
|
|
17
|
+
`State<Foo | undefined>`. Without it `state(undefined)` infers `T = undefined`
|
|
18
|
+
(every `.value` access then narrows to `never`). */
|
|
20
19
|
<T>(): State<T | undefined>
|
|
21
20
|
<T>(initial: T, transform?: (next: T, previous: T) => T): State<T>
|
|
21
|
+
/* The no-arg form spelled out: `state<Foo>(undefined)` is `State<Foo | undefined>`
|
|
22
|
+
too. Kept a distinct overload (not `initial?: T`) BELOW the general form — a real
|
|
23
|
+
initial still binds `T` from the value; only an explicit `undefined` (which the
|
|
24
|
+
general form rejects as "not assignable to T") falls through to here. A bare
|
|
25
|
+
`state(undefined)` still resolves against the general form (`T = undefined`), so
|
|
26
|
+
this overload changes nothing for it. */
|
|
27
|
+
<T>(initial: undefined): State<T | undefined>
|
|
22
28
|
/* A writable cell reseeded from a reactive thunk (`state.linked(() => src())`). */
|
|
23
29
|
linked: typeof linked
|
|
24
30
|
/* A read-only cell computed from other cells (`state.computed(() => a() + b())`). */
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { isAsyncIterable } from '../shared/isAsyncIterable.ts'
|
|
2
|
+
import type { AsyncComputed } from '../shared/types/AsyncComputed.ts'
|
|
3
|
+
import type { NamedAsyncIterable } from '../shared/types/NamedAsyncIterable.ts'
|
|
4
|
+
import { createAsyncCell } from './runtime/createAsyncCell.ts'
|
|
5
|
+
import { createComputedNode } from './runtime/createComputedNode.ts'
|
|
6
|
+
import { isAsyncFunction } from './runtime/isAsyncFunction.ts'
|
|
7
|
+
import { OWNER } from './runtime/OWNER.ts'
|
|
8
|
+
import { readNode } from './runtime/readNode.ts'
|
|
9
|
+
import type { Computed } from './runtime/types/Computed.ts'
|
|
10
|
+
import { unlinkDeps } from './runtime/unlinkDeps.ts'
|
|
11
|
+
import { untrack } from './runtime/untrack.ts'
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
The eager, stream-classifying read-only computed the compiler routes a bare-call /
|
|
15
|
+
identifier seed to — `computed(getStream())` / `computed(ref)` (ADR-0019 D1). Unlike the
|
|
16
|
+
lazy `computed` primitive it PROBES its seed once to self-identify the source: a
|
|
17
|
+
`NamedAsyncIterable` (socket / streaming rpc) auto-tracks its frames as an `AsyncComputed`,
|
|
18
|
+
an `async () => await …` seed unwraps its promise, and anything else — a sync value, a bare
|
|
19
|
+
opaque promise — falls back to the lazy `Computed`, so laziness is preserved on the miss and
|
|
20
|
+
only a real stream/promise producer becomes a cell. It lives apart from the `computed`
|
|
21
|
+
primitive so a DIRECT `computed(() => …)` call stays lazy (the primitive never probes); the
|
|
22
|
+
eager probe is reached only through this compiler-emitted entry, matching how `linked`
|
|
23
|
+
already classifies its seed. The probe runs untracked — a throwaway read must not subscribe
|
|
24
|
+
the enclosing build.
|
|
25
|
+
*/
|
|
26
|
+
export function trackedComputed<T>(
|
|
27
|
+
compute: () => T | Promise<T> | NamedAsyncIterable<T>,
|
|
28
|
+
/* ADR-0032: when the compiler lifts a no-`await` async (sub)expression to a cell, it passes
|
|
29
|
+
`streaming: true` so the resolved promise does not join the SSR blocking barrier (the shell
|
|
30
|
+
ships pending, the client resolves). Default false = today's blocking registration; a stream
|
|
31
|
+
seed never registers regardless, so it only matters for a promise-unwrapping seed. */
|
|
32
|
+
streaming = false,
|
|
33
|
+
): Computed<T> | AsyncComputed<T> {
|
|
34
|
+
/* `await` marker: an async-function seed unwraps its promise into a read-only async cell. */
|
|
35
|
+
if (isAsyncFunction(compute)) {
|
|
36
|
+
return createAsyncCell(compute as () => unknown, {
|
|
37
|
+
writable: false,
|
|
38
|
+
streaming,
|
|
39
|
+
}) as AsyncComputed<T>
|
|
40
|
+
}
|
|
41
|
+
/* Probe the seed once (untracked) to detect a self-identifying stream. A discarded stream
|
|
42
|
+
iterable costs nothing — a streaming rpc defers its fetch to the first pull, which never
|
|
43
|
+
happens here; a throw means a plain reactive compute, so fall through to the lazy path
|
|
44
|
+
(it throws at read, unchanged). */
|
|
45
|
+
let probe: unknown
|
|
46
|
+
let threw = false
|
|
47
|
+
try {
|
|
48
|
+
probe = untrack(compute)
|
|
49
|
+
} catch {
|
|
50
|
+
threw = true
|
|
51
|
+
}
|
|
52
|
+
if (!threw && isAsyncIterable(probe)) {
|
|
53
|
+
return createAsyncCell(compute as () => unknown, {
|
|
54
|
+
writable: false,
|
|
55
|
+
streaming,
|
|
56
|
+
}) as AsyncComputed<T>
|
|
57
|
+
}
|
|
58
|
+
/* Sync value or a bare (opaque) promise: the lazy computed, identical to the primitive. */
|
|
59
|
+
const node = createComputedNode(compute as () => unknown)
|
|
60
|
+
if (OWNER.current !== undefined) {
|
|
61
|
+
OWNER.current.push(() => unlinkDeps(node))
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
get value(): T {
|
|
65
|
+
return readNode(node) as T
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { effect } from '../effect.ts'
|
|
|
3
3
|
import type { linked } from '../linked.ts'
|
|
4
4
|
import type { Patch } from '../runtime/types/Patch.ts'
|
|
5
5
|
import type { state } from '../state.ts'
|
|
6
|
-
import type {
|
|
6
|
+
import type { trackedComputed } from '../trackedComputed.ts'
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
9
|
A lexical scope: the unit that owns a region's reactive data, its lifetime, and
|
|
@@ -14,21 +14,14 @@ surface is the imported `state`/`state.linked`/`state.computed`/`effect` (see
|
|
|
14
14
|
scope (`$$scope().derive`/`.linked`/`.effect`, `state.share`/`.shared` → `share`/
|
|
15
15
|
`shared`). The data surface MIRRORS `Doc` (read/replace/add/remove/derive/apply/
|
|
16
16
|
snapshot) so the compiler can target a scope as a component's data binding directly;
|
|
17
|
-
it
|
|
17
|
+
it passes values down the tree as context (`share`/`shared`).
|
|
18
18
|
|
|
19
|
-
The reactive primitives
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Capabilities route where the scope's changes go: `record()` to an undo journal,
|
|
23
|
-
`persist()` to durable storage, `broadcast()` to peers — declared once, then `undo`/
|
|
24
|
-
`redo` act on a recorded scope. The `history`/`persist`/`sync` helpers it composes are
|
|
25
|
-
internal.
|
|
19
|
+
The reactive primitives remain on this internal shape because the lowered runtime calls
|
|
20
|
+
them (`$$scope().linked(...)`) — they are withdrawn from the AUTHOR-facing public surface
|
|
21
|
+
(docs/examples), not the runtime object.
|
|
26
22
|
*/
|
|
27
23
|
export type Scope = {
|
|
28
24
|
readonly id: string
|
|
29
|
-
/* Dev-only display name (the host component/element it mounted into) for the
|
|
30
|
-
inspector's Reactive tab; undefined for SSR/detached/child scopes. */
|
|
31
|
-
readonly label?: string
|
|
32
25
|
readonly parent: Scope | undefined
|
|
33
26
|
/* data — mirrors Doc */
|
|
34
27
|
read: <T>(path: string) => T
|
|
@@ -54,15 +47,15 @@ export type Scope = {
|
|
|
54
47
|
state: typeof state
|
|
55
48
|
linked: typeof linked
|
|
56
49
|
computed: typeof computed
|
|
50
|
+
/* The eager stream-classifying read-only computed a bare-call `computed(getStream())`
|
|
51
|
+
lowers to — auto-tracks a stream/promise producer, falls back to a lazy computed. */
|
|
52
|
+
trackedComputed: typeof trackedComputed
|
|
57
53
|
effect: typeof effect
|
|
58
54
|
/* Adopts a teardown into this scope's lifetime — the build's reactivity stopper
|
|
59
55
|
(effects/listeners), run first on `dispose` (before children, before capabilities).
|
|
60
56
|
Internal: the mount core registers the build's disposer here so a component has ONE
|
|
61
57
|
teardown (`dispose`) rather than a separate stop + dispose composed at every site. */
|
|
62
58
|
own: (dispose: () => void) => void
|
|
63
|
-
/* tree */
|
|
64
|
-
child: (initial?: unknown) => Scope
|
|
65
|
-
root: () => Scope
|
|
66
59
|
/* context — values shared DOWN the tree (not in the reactive doc, which doesn't
|
|
67
60
|
inherit): `share` puts a named value on this scope; `shared` reads the closest
|
|
68
61
|
ancestor (self included) that has the key, undefined if none. The value is held
|
|
@@ -70,15 +63,6 @@ export type Scope = {
|
|
|
70
63
|
plain object snapshot. */
|
|
71
64
|
share: (key: string, value: unknown) => void
|
|
72
65
|
shared: <T>(key: string) => T | undefined
|
|
73
|
-
/* capabilities — enable where the scope's changes go */
|
|
74
|
-
record: (options?: { limit?: number }) => void
|
|
75
|
-
persist: (key?: string) => void
|
|
76
|
-
broadcast: (transport: SyncTransport) => void
|
|
77
|
-
/* undo/redo — act on a recorded scope */
|
|
78
|
-
undo: () => void
|
|
79
|
-
redo: () => void
|
|
80
|
-
canUndo: () => boolean
|
|
81
|
-
canRedo: () => boolean
|
|
82
66
|
/* lifetime */
|
|
83
67
|
dispose: () => void
|
|
84
68
|
}
|
package/src/lib/ui/watch.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cache } from '../shared/cache.ts'
|
|
2
2
|
import { REMOTE_FUNCTION } from '../shared/REMOTE_FUNCTION.ts'
|
|
3
3
|
import type { CacheOnContext } from '../shared/types/CacheOnContext.ts'
|
|
4
|
+
import type { NamedAsyncIterable } from '../shared/types/NamedAsyncIterable.ts'
|
|
4
5
|
import type { RemoteFunction } from '../shared/types/RemoteFunction.ts'
|
|
5
|
-
import type { Subscribable } from '../shared/types/Subscribable.ts'
|
|
6
6
|
import { effect } from './effect.ts'
|
|
7
7
|
import { generationGuard } from './runtime/generationGuard.ts'
|
|
8
8
|
import type { EffectResult } from './runtime/types/EffectResult.ts'
|
|
@@ -40,7 +40,7 @@ export function watch(
|
|
|
40
40
|
handler: (values: unknown[]) => void,
|
|
41
41
|
): () => void
|
|
42
42
|
export function watch<Frame>(
|
|
43
|
-
source:
|
|
43
|
+
source: NamedAsyncIterable<Frame>,
|
|
44
44
|
handler: (frame: Frame, context: CacheOnContext) => void | Promise<void>,
|
|
45
45
|
): () => void
|
|
46
46
|
export function watch<Args, Return>(
|
|
@@ -74,7 +74,7 @@ export function watch(
|
|
|
74
74
|
existing cache.on loop is the single implementation this branch delegates to. */
|
|
75
75
|
if (isSubscribable(source)) {
|
|
76
76
|
return cache.on(
|
|
77
|
-
source as
|
|
77
|
+
source as NamedAsyncIterable<unknown>,
|
|
78
78
|
handler as (frame: unknown, context: CacheOnContext) => void | Promise<void>,
|
|
79
79
|
)
|
|
80
80
|
}
|
package/src/serverEntry.ts
CHANGED
|
@@ -33,8 +33,11 @@ import { cacheStoreSlot } from './lib/shared/cacheStoreSlot.ts'
|
|
|
33
33
|
import { createCacheStore } from './lib/shared/createCacheStore.ts'
|
|
34
34
|
import { loadEnvFromDataDir } from './lib/shared/loadEnvFromDataDir.ts'
|
|
35
35
|
import { pageSlot } from './lib/shared/pageSlot.ts'
|
|
36
|
+
import { pendingAsyncCellsSlot } from './lib/shared/pendingAsyncCellsSlot.ts'
|
|
37
|
+
import { resolvedCellsSlot } from './lib/shared/resolvedCellsSlot.ts'
|
|
36
38
|
import { runningAsStandaloneBinary } from './lib/shared/runningAsStandaloneBinary.ts'
|
|
37
39
|
import { sharedCacheStoreSlot } from './lib/shared/sharedCacheStoreSlot.ts'
|
|
40
|
+
import { streamedCellsSlot } from './lib/shared/streamedCellsSlot.ts'
|
|
38
41
|
|
|
39
42
|
/*
|
|
40
43
|
Resolve config into process.env before anything reads it (createServer reads
|
|
@@ -76,6 +79,17 @@ sharedCacheStoreSlot.resolver = () => sharedCacheStore
|
|
|
76
79
|
|
|
77
80
|
cacheStoreSlot.resolver = () => requestContext.getStore()?.cache ?? sharedCacheStore
|
|
78
81
|
|
|
82
|
+
/* One process-wide fallback list for reads with no request in flight (boot/cron/socket) —
|
|
83
|
+
real requests each carry their own `pendingAsyncCells`, so the SSR barrier drains a
|
|
84
|
+
per-request list and concurrent renders never cross-contaminate. */
|
|
85
|
+
const sharedPendingAsyncCells = { promises: [] }
|
|
86
|
+
pendingAsyncCellsSlot.resolver = () =>
|
|
87
|
+
requestContext.getStore()?.pendingAsyncCells ?? sharedPendingAsyncCells
|
|
88
|
+
const sharedResolvedCells = { entries: [] }
|
|
89
|
+
resolvedCellsSlot.resolver = () => requestContext.getStore()?.resolvedCells ?? sharedResolvedCells
|
|
90
|
+
const sharedStreamedCells = { entries: [] }
|
|
91
|
+
streamedCellsSlot.resolver = () => requestContext.getStore()?.streamedCells ?? sharedStreamedCells
|
|
92
|
+
|
|
79
93
|
pageSlot.resolver = resolvePageSnapshot
|
|
80
94
|
|
|
81
95
|
await createServer({
|
|
@@ -6,17 +6,18 @@ the HTTP method. The bundler swaps the runtime per build target: direct call on
|
|
|
6
6
|
the server, fetch over the network on the client.
|
|
7
7
|
|
|
8
8
|
Args (what the caller passes in) come from the handler's parameter type —
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
decoding) is inferred from the handler's return type via the
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
annotate the parameter and the body infers; a typed generic on the helper is a
|
|
10
|
+
compile error. Return (what the caller receives after Content-Type-driven
|
|
11
|
+
decoding) is inferred from the handler's return type via the `TypedResponse<T>`
|
|
12
|
+
brand on `json`/`error`/`redirect`/`jsonl`/`sse`, so plain
|
|
13
|
+
`GET(() => json({...}))` already types end-to-end.
|
|
14
14
|
|
|
15
|
-
For inbound validation pass a Standard Schema
|
|
16
|
-
second argument: `GET(fn, {
|
|
17
|
-
schema's output type and the server replies with 422 on validation
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
For inbound validation pass a Standard Schema under `schemas.input` in the
|
|
16
|
+
second argument: `GET(fn, { schemas: { input } })`. Args then infers from the
|
|
17
|
+
schema's output type and the server replies with 422 on validation failure. An
|
|
18
|
+
optional `schemas.output` describes the success body for the OpenAPI 200
|
|
19
|
+
response and the MCP tool output; without one the handler's return type is
|
|
20
|
+
projected to JSON Schema.
|
|
20
21
|
|
|
21
22
|
`json(...)` from `abide/server/json` is a thin wrapper over `Response.json`
|
|
22
23
|
that defaults `Cache-Control: no-store`, since intermediary caches shouldn't
|
|
@@ -24,10 +25,11 @@ memoise rpc replies (the framework's per-request cache handles in-process
|
|
|
24
25
|
dedupe). Other helpers are siblings, one per file: `abide/server/error`,
|
|
25
26
|
`abide/server/redirect`, `abide/server/sse`, `abide/server/jsonl`.
|
|
26
27
|
|
|
27
|
-
Every rpc value also exposes `.raw(args?)` (returns the underlying
|
|
28
|
+
Every rpc value also exposes `.raw(args?, init?)` (returns the underlying
|
|
28
29
|
`Response`) for callers that need headers or status. A streaming rpc — a
|
|
29
|
-
jsonl/sse handler — returns a `
|
|
30
|
-
call (`fn(args)`); iterate it with `for await` or react to it with
|
|
30
|
+
jsonl/sse handler — returns a `NamedAsyncIterable` over the frames from its bare
|
|
31
|
+
call (`fn(args)`); iterate it with `for await` or react to it with
|
|
32
|
+
`watch(fn(args), frame => …)`.
|
|
31
33
|
*/
|
|
32
34
|
|
|
33
35
|
import { GET } from '@abide/abide/server/GET'
|
|
@@ -5,7 +5,7 @@ same wiring as `abide start`, no fixtures) and hands back the whole surface:
|
|
|
5
5
|
app.fetch(path) — pages and raw HTTP, origin + mount base resolved
|
|
6
6
|
app.rpc.<name>(args) — RPCs over the real pipeline (CSRF, cookies, base),
|
|
7
7
|
typed and decoded; .raw(args) for the Response
|
|
8
|
-
app.sockets.<name> — a live Socket: iterate it (for await), .peek(), .
|
|
8
|
+
app.sockets.<name> — a live Socket: iterate it (for await), .peek(), .publish(m)
|
|
9
9
|
app.health() — the /__abide/health payload
|
|
10
10
|
|
|
11
11
|
`app.rpc` / `app.sockets` are typed from your own RPCs and sockets — generated
|