@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,9 +1,152 @@
|
|
|
1
1
|
import ts from 'typescript'
|
|
2
|
+
import { assignmentTargetNames } from './assignmentTargetNames.ts'
|
|
2
3
|
import { type ReactiveImportBindings, reactiveImportBindings } from './resolveReactiveExport.ts'
|
|
3
4
|
import { signalCallee } from './signalCallee.ts'
|
|
5
|
+
import type { InterpolationKind } from './types/InterpolationKind.ts'
|
|
6
|
+
import type { SeedTypeClassifier } from './types/SeedTypeClassifier.ts'
|
|
7
|
+
|
|
8
|
+
/* The routing decision for a no-marker `computed(seed)`: `true` routes to the eager
|
|
9
|
+
`trackedComputed` stream cell (read via `$$readCell`, the `cellReadNames` bucket), `false`
|
|
10
|
+
to the lazy `derive` doc slot (read as `name()`, the `computedNames` bucket). Shared by the
|
|
11
|
+
name-collection pass and the lowering so both land the binding in the identical bucket. */
|
|
12
|
+
type EagerStreamPredicate = (declaration: ts.VariableDeclaration) => boolean
|
|
4
13
|
|
|
5
14
|
const factory = ts.factory
|
|
6
15
|
|
|
16
|
+
/* True when `node` holds an `await` that runs at the seed's own top level — one NOT nested
|
|
17
|
+
inside a further function. The walk stops at every nested function boundary, so an
|
|
18
|
+
`await` in an inner callback (`items.map(async (x) => await f(x))`) does not count: only
|
|
19
|
+
a top-level `await` marks the seed itself as an async thunk the async-cell path unwraps. */
|
|
20
|
+
function hasTopLevelAwait(node: ts.Node): boolean {
|
|
21
|
+
let found = false
|
|
22
|
+
const visit = (child: ts.Node): void => {
|
|
23
|
+
if (found) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
/* A nested function is its own await scope — don't descend into it. */
|
|
27
|
+
if (
|
|
28
|
+
ts.isFunctionDeclaration(child) ||
|
|
29
|
+
ts.isFunctionExpression(child) ||
|
|
30
|
+
ts.isArrowFunction(child)
|
|
31
|
+
) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if (ts.isAwaitExpression(child)) {
|
|
35
|
+
found = true
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
ts.forEachChild(child, visit)
|
|
39
|
+
}
|
|
40
|
+
visit(node)
|
|
41
|
+
return found
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Normalises a `computed`/`linked` argument into a seed THUNK: a literal `() => …` /
|
|
45
|
+
`function` argument passes through unchanged (the author already wrote the thunk), any
|
|
46
|
+
other expression is wrapped as `() => arg`. The wrapper arrow is made ASYNC when the
|
|
47
|
+
expression contains a top-level `await` (`computed(await load())` → `async () => await
|
|
48
|
+
load()`), which is exactly the marker the runtime primitive uses to route to an async cell. */
|
|
49
|
+
function wrapSeed(argument: ts.Expression): ts.Expression {
|
|
50
|
+
if (ts.isArrowFunction(argument) || ts.isFunctionExpression(argument)) {
|
|
51
|
+
return argument
|
|
52
|
+
}
|
|
53
|
+
const modifiers = hasTopLevelAwait(argument)
|
|
54
|
+
? [factory.createModifier(ts.SyntaxKind.AsyncKeyword)]
|
|
55
|
+
: undefined
|
|
56
|
+
return factory.createArrowFunction(
|
|
57
|
+
modifiers,
|
|
58
|
+
undefined,
|
|
59
|
+
[],
|
|
60
|
+
undefined,
|
|
61
|
+
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
62
|
+
argument,
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* True for an async seed thunk — an arrow/function carrying the `async` modifier, whether
|
|
67
|
+
from `wrapSeed`'s `await` lowering or a passthrough `async () => …` literal the author
|
|
68
|
+
wrote. The routing signal: an async seed becomes an async cell (read via `$$readCell`),
|
|
69
|
+
a sync seed stays the lazy `derive` computed. */
|
|
70
|
+
function isAsyncSeed(node: ts.Expression): boolean {
|
|
71
|
+
if (!ts.isArrowFunction(node) && !ts.isFunctionExpression(node)) {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
const modifiers = ts.getModifiers(node)
|
|
75
|
+
return modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) ?? false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* The seed argument of a `computed`/`linked` declaration (undefined for the arg-less
|
|
79
|
+
`computed()` edge). Reused by both the name-collection pass and the lowering. */
|
|
80
|
+
function seedArgument(declaration: ts.VariableDeclaration): ts.Expression | undefined {
|
|
81
|
+
return (declaration.initializer as ts.CallExpression).arguments[0]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* True when a `computed(...)` declaration's wrapped seed is async — its await-lowered or
|
|
85
|
+
passthrough-async thunk routes to `scope().computed(...)` (an async cell) instead of the
|
|
86
|
+
lazy `scope().derive(...)`. The one predicate the collection pass and the lowering share. */
|
|
87
|
+
function isAsyncComputed(declaration: ts.VariableDeclaration): boolean {
|
|
88
|
+
const argument = seedArgument(declaration)
|
|
89
|
+
if (argument === undefined) {
|
|
90
|
+
return false
|
|
91
|
+
}
|
|
92
|
+
return isAsyncSeed(wrapSeed(argument))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* True for a SYNC `computed(...)` seed that is a bare CALL or IDENTIFIER — `computed(getStream())`
|
|
96
|
+
/ `computed(ref)`, the shape that may produce a stream/promise source. These route to the
|
|
97
|
+
eager `scope().trackedComputed(...)` (which probes its seed and auto-tracks a
|
|
98
|
+
`NamedAsyncIterable`), read via `$$readCell`; an arithmetic/member/literal seed stays on the
|
|
99
|
+
lazy `derive` path (read as `name()`). Excludes the async case (`isAsyncComputed` owns it) and
|
|
100
|
+
a literal `() => …` thunk (a direct thunk the author wrote — left lazy on `derive`). */
|
|
101
|
+
function isBareCallComputed(declaration: ts.VariableDeclaration): boolean {
|
|
102
|
+
const argument = seedArgument(declaration)
|
|
103
|
+
if (argument === undefined || isAsyncSeed(wrapSeed(argument))) {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
return ts.isCallExpression(argument) || ts.isIdentifier(argument)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Emits a compile WARNING (best-effort, never fatal) when a signal read appears AFTER the
|
|
110
|
+
first top-level `await` in an async seed — a value read there is no longer tracked, so the
|
|
111
|
+
cell won't reseed when it changes. Detection only: reads before the await (or with no
|
|
112
|
+
await) are fine. */
|
|
113
|
+
function warnPostAwaitReads(seed: ts.Expression, signalNames: ReadonlySet<string>): void {
|
|
114
|
+
if (signalNames.size === 0) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
/* Walk the seed's BODY — the arrow/function itself is the seed, not a nested scope, so
|
|
118
|
+
descend into it; the guard below then skips any FURTHER nested function. */
|
|
119
|
+
const body = ts.isArrowFunction(seed) || ts.isFunctionExpression(seed) ? seed.body : seed
|
|
120
|
+
let awaited = false
|
|
121
|
+
const flagged = new Set<string>()
|
|
122
|
+
const visit = (child: ts.Node): void => {
|
|
123
|
+
/* Nested functions have their own tracking; the await that matters is at the seed's
|
|
124
|
+
own top level, so don't descend into them (mirrors `hasTopLevelAwait`). */
|
|
125
|
+
if (
|
|
126
|
+
ts.isFunctionDeclaration(child) ||
|
|
127
|
+
ts.isFunctionExpression(child) ||
|
|
128
|
+
ts.isArrowFunction(child)
|
|
129
|
+
) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
if (ts.isAwaitExpression(child)) {
|
|
133
|
+
awaited = true
|
|
134
|
+
ts.forEachChild(child, visit)
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
if (awaited && ts.isIdentifier(child) && signalNames.has(child.text)) {
|
|
138
|
+
flagged.add(child.text)
|
|
139
|
+
}
|
|
140
|
+
ts.forEachChild(child, visit)
|
|
141
|
+
}
|
|
142
|
+
visit(body)
|
|
143
|
+
for (const name of flagged) {
|
|
144
|
+
console.warn(
|
|
145
|
+
`[abide] \`${name}\` is read after an \`await\` in an async computed/linked seed — reads after the first await are not tracked, so the cell will not reseed when \`${name}\` changes. Read it before the await (capture it into a local) to keep it tracked.`,
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
7
150
|
/* Throws on the removed `prop(...)` reader — props are now read by destructuring `props()`.
|
|
8
151
|
Walks all calls, so a stray one nested in a function is caught too. Bare reactive
|
|
9
152
|
primitives (`state(0)`) are the SURFACE now (recognised by import binding + lowered), so
|
|
@@ -44,13 +187,48 @@ between passes. Plain `state` becomes `model.x` access that `lowerDocAccess` low
|
|
|
44
187
|
patches/reads. No reactive declarations → an identity transformer (the explicit
|
|
45
188
|
`const model = doc(...)` form still works).
|
|
46
189
|
*/
|
|
47
|
-
export function desugarSignals(
|
|
190
|
+
export function desugarSignals(
|
|
191
|
+
source: ts.SourceFile,
|
|
192
|
+
/* Synthetic `const __cN = computed(...)` cells `analyzeComponent` injected for asyncIterable
|
|
193
|
+
interpolations (ADR-0019 Stage D). Their `computed` callee is UNIMPORTED, so import
|
|
194
|
+
resolution (`signalCallee`) can't recognize them — instead a declaration whose name is in
|
|
195
|
+
this set is treated as a bare-call computed slot: routed to an eager `trackedComputed`
|
|
196
|
+
stream cell and read via `$$readCell`, exactly as an explicit `state.computed(getStream())`
|
|
197
|
+
would be. Gated on the exact injected name (never a naming heuristic), so it can never
|
|
198
|
+
reclassify author code — an author's own `computed(...)` still needs its import. */
|
|
199
|
+
injectedCellNames: ReadonlySet<string> = new Set(),
|
|
200
|
+
/* The subset of `injectedCellNames` whose seed the author wrote `await` on (ADR-0032): a
|
|
201
|
+
BLOCKING peek-cell (`scope().trackedComputed(async …, false)` — an `AsyncComputed` that
|
|
202
|
+
joins the SSR barrier), vs a streaming one (`trackedComputed(async …, true)` — ships
|
|
203
|
+
pending, resolves on the client). Names not in this set are streaming. */
|
|
204
|
+
blockingCellNames: ReadonlySet<string> = new Set(),
|
|
205
|
+
/* Names the TEMPLATE writes (assigned in an event expression, or forwarded as a `bind:`
|
|
206
|
+
target) — see `writtenTemplateNames`. Unioned with the script's own write scan; a `props()`
|
|
207
|
+
binding whose name lands in the union is a WRITABLE prop (a `.value` cell via `bindableProp`)
|
|
208
|
+
rather than a read-only derive, so a two-way `bind:prop` on the parent can flow back. */
|
|
209
|
+
templateWrittenNames: ReadonlySet<string> = new Set(),
|
|
210
|
+
/* Type-directed seed classifier (ADR-0023): resolves a no-marker `computed(seed)`'s
|
|
211
|
+
async-ness from the seed's checker type via the warm shadow program. Absent (no warm
|
|
212
|
+
program, or any call site outside the shadow-warmed path) ⇒ every seed classifies
|
|
213
|
+
`undefined` ⇒ fail-open to the `isBareCallComputed` syntax heuristic — exactly today's
|
|
214
|
+
routing. The `await`-marker path (`isAsyncComputed`) is decided first and never consults it. */
|
|
215
|
+
seedClassify?: SeedTypeClassifier,
|
|
216
|
+
/* Absolute `.abide` source offset where this script's (trimmed) body begins — the base that
|
|
217
|
+
relocates a seed node's body-relative `getStart()` back to an ORIGINAL source location the
|
|
218
|
+
shadow's source→shadow `mappings` resolve. Unused when `seedClassify` is absent. */
|
|
219
|
+
scriptBase = 0,
|
|
220
|
+
): {
|
|
48
221
|
transformer: ts.TransformerFactory<ts.SourceFile>
|
|
49
222
|
stateNames: Set<string>
|
|
50
223
|
derivedNames: Set<string>
|
|
51
224
|
computedNames: Set<string>
|
|
225
|
+
cellReadNames: Set<string>
|
|
52
226
|
} {
|
|
53
227
|
assertNoRemovedReaders(source)
|
|
228
|
+
/* The full set of names written anywhere (this script + the template). A prop in this set
|
|
229
|
+
is upgraded to a writable cell; a prop only read stays a cheap read-only derive. */
|
|
230
|
+
const writtenNames = new Set<string>(templateWrittenNames)
|
|
231
|
+
assignmentTargetNames(source, writtenNames)
|
|
54
232
|
/* The file's reactive import bindings — each local (alias-safe) mapped to its
|
|
55
233
|
canonical primitive. The single recognition authority: every callee below resolves
|
|
56
234
|
against these import bindings and nothing else. */
|
|
@@ -58,9 +236,47 @@ export function desugarSignals(source: ts.SourceFile): {
|
|
|
58
236
|
const stateNames = new Set<string>()
|
|
59
237
|
const derivedNames = new Set<string>()
|
|
60
238
|
const computedNames = new Set<string>()
|
|
239
|
+
/* Names read through `$$readCell(name)`: every `linked` (a plain `State` or, when its seed
|
|
240
|
+
tracks an async source, an `AsyncState`) and every async `computed` (an `AsyncComputed`).
|
|
241
|
+
One read shape covers both — `$$readCell` peeks an async cell and reads `.value` off a
|
|
242
|
+
sync one — so `linked(getStream())` auto-tracks with no read-site branching. */
|
|
243
|
+
const cellReadNames = new Set<string>()
|
|
61
244
|
/* A `props()` destructure must be lowered even when it declares no reactive binding
|
|
62
245
|
(a rest-only `const { ...rest } = props()`), so track its presence on its own. */
|
|
63
246
|
let hasPropsDestructure = false
|
|
247
|
+
/* Resolves a computed seed's async-ness through the warm shadow classifier (ADR-0023):
|
|
248
|
+
maps the seed's absolute source location to its checker-type kind. `undefined` when no
|
|
249
|
+
classifier is threaded OR on any resolution failure (a throw / an unmapped seed), so the
|
|
250
|
+
caller degrades to `isBareCallComputed` — never mistakes a failure for a `sync` type. */
|
|
251
|
+
const seedKind = (seed: ts.Expression): InterpolationKind | undefined => {
|
|
252
|
+
if (seedClassify === undefined) {
|
|
253
|
+
return undefined
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const start = seed.getStart(source)
|
|
257
|
+
return seedClassify(scriptBase + start, source.text.slice(start, seed.getEnd()))
|
|
258
|
+
} catch {
|
|
259
|
+
return undefined
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/* The single routing authority for a no-marker `computed(seed)` — shared by the
|
|
263
|
+
name-collection pass below and `computedStatements` so both land the binding in the same
|
|
264
|
+
read-name bucket (a divergence would lower a reference to the wrong read form). Type-
|
|
265
|
+
directed when the seed classifier resolves the seed (`asyncIterable` → the eager stream
|
|
266
|
+
cell; `promise`/`sync` → the lazy `derive` slot), fail-open to today's `isBareCallComputed`
|
|
267
|
+
syntax heuristic otherwise. The `await`-marker seed (`isAsyncComputed`) is decided first by
|
|
268
|
+
each caller and excluded here (its wrapped thunk is async → returns false). */
|
|
269
|
+
const isEagerStreamComputed: EagerStreamPredicate = (declaration) => {
|
|
270
|
+
const argument = seedArgument(declaration)
|
|
271
|
+
if (argument === undefined || isAsyncSeed(wrapSeed(argument))) {
|
|
272
|
+
return false
|
|
273
|
+
}
|
|
274
|
+
const kind = seedKind(argument)
|
|
275
|
+
if (kind !== undefined) {
|
|
276
|
+
return kind === 'asyncIterable'
|
|
277
|
+
}
|
|
278
|
+
return isBareCallComputed(declaration)
|
|
279
|
+
}
|
|
64
280
|
for (const statement of source.statements) {
|
|
65
281
|
if (!ts.isVariableStatement(statement)) {
|
|
66
282
|
continue
|
|
@@ -78,25 +294,48 @@ export function desugarSignals(source: ts.SourceFile): {
|
|
|
78
294
|
}
|
|
79
295
|
hasPropsDestructure = true
|
|
80
296
|
for (const binding of propsDestructure(declaration).bindings) {
|
|
81
|
-
|
|
297
|
+
/* A prop the component writes/forwards becomes a `.value` cell (`bindableProp`,
|
|
298
|
+
read/written like `state(x, transform)` → `derivedNames`); one only read stays
|
|
299
|
+
a read-only derive read as `name()` (`computedNames`). */
|
|
300
|
+
if (writtenNames.has(binding.local)) {
|
|
301
|
+
derivedNames.add(binding.local)
|
|
302
|
+
} else {
|
|
303
|
+
computedNames.add(binding.local)
|
|
304
|
+
}
|
|
82
305
|
}
|
|
83
306
|
continue
|
|
84
307
|
}
|
|
85
308
|
if (!ts.isIdentifier(declaration.name)) {
|
|
86
309
|
continue
|
|
87
310
|
}
|
|
311
|
+
if (injectedCellNames.has(declaration.name.text)) {
|
|
312
|
+
/* Injected asyncIterable-interpolation cell (`const __cN = computed(expr)`): an
|
|
313
|
+
eager `trackedComputed` stream cell, read via `$$readCell` — recognized by its
|
|
314
|
+
injected name since `computed` is unimported here. */
|
|
315
|
+
cellReadNames.add(declaration.name.text)
|
|
316
|
+
continue
|
|
317
|
+
}
|
|
88
318
|
if (isPlainStateSlot(declaration, bindings)) {
|
|
89
319
|
/* Plain `state(initial)` → a serializable `model` doc slot. */
|
|
90
320
|
stateNames.add(declaration.name.text)
|
|
91
321
|
} else if (isComputedSlot(declaration, bindings)) {
|
|
92
|
-
/*
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
322
|
+
/* `computed(compute)` → either a lazy `scope().derive` doc slot referenced as
|
|
323
|
+
`name()` (a sync/promise seed), or an eager cell read via `$$readCell(name)`
|
|
324
|
+
when the wrapped seed is async (an `await`-lowered / passthrough-`async` thunk)
|
|
325
|
+
or its seed type resolves to a stream (`isEagerStreamComputed`, ADR-0023). */
|
|
326
|
+
if (isAsyncComputed(declaration) || isEagerStreamComputed(declaration)) {
|
|
327
|
+
cellReadNames.add(declaration.name.text)
|
|
328
|
+
} else {
|
|
329
|
+
computedNames.add(declaration.name.text)
|
|
330
|
+
}
|
|
331
|
+
} else if (callee === 'linked') {
|
|
332
|
+
/* `linked` → a cell read via `$$readCell(name)`: a plain `State` when the seed
|
|
333
|
+
is synchronous, an `AsyncState` when it tracks a promise/stream — one read
|
|
334
|
+
shape auto-tracks whichever source the runtime primitive resolved to. */
|
|
335
|
+
cellReadNames.add(declaration.name.text)
|
|
336
|
+
} else if (callee === 'state') {
|
|
337
|
+
/* `state(initial, transform)` → a `.value` cell (its write-coercion transform
|
|
338
|
+
forces a local store); referenced as `name.value`, unchanged. */
|
|
100
339
|
derivedNames.add(declaration.name.text)
|
|
101
340
|
}
|
|
102
341
|
}
|
|
@@ -106,6 +345,7 @@ export function desugarSignals(source: ts.SourceFile): {
|
|
|
106
345
|
stateNames.size > 0 ||
|
|
107
346
|
derivedNames.size > 0 ||
|
|
108
347
|
computedNames.size > 0 ||
|
|
348
|
+
cellReadNames.size > 0 ||
|
|
109
349
|
hasPropsDestructure
|
|
110
350
|
|
|
111
351
|
const transformer: ts.TransformerFactory<ts.SourceFile> = () => (root) => {
|
|
@@ -123,13 +363,31 @@ export function desugarSignals(source: ts.SourceFile): {
|
|
|
123
363
|
),
|
|
124
364
|
)
|
|
125
365
|
}
|
|
366
|
+
/* Every component signal name — the read set the post-`await` tracking lint checks
|
|
367
|
+
an async seed's body against. */
|
|
368
|
+
const signalNames = new Set<string>([
|
|
369
|
+
...stateNames,
|
|
370
|
+
...derivedNames,
|
|
371
|
+
...computedNames,
|
|
372
|
+
...cellReadNames,
|
|
373
|
+
])
|
|
126
374
|
for (const statement of root.statements) {
|
|
127
|
-
statements.push(
|
|
375
|
+
statements.push(
|
|
376
|
+
...loweredStatement(
|
|
377
|
+
statement,
|
|
378
|
+
bindings,
|
|
379
|
+
signalNames,
|
|
380
|
+
injectedCellNames,
|
|
381
|
+
blockingCellNames,
|
|
382
|
+
writtenNames,
|
|
383
|
+
isEagerStreamComputed,
|
|
384
|
+
),
|
|
385
|
+
)
|
|
128
386
|
}
|
|
129
387
|
return factory.updateSourceFile(root, statements)
|
|
130
388
|
}
|
|
131
389
|
|
|
132
|
-
return { transformer, stateNames, derivedNames, computedNames }
|
|
390
|
+
return { transformer, stateNames, derivedNames, computedNames, cellReadNames }
|
|
133
391
|
}
|
|
134
392
|
|
|
135
393
|
/* The lowered form of a top-level statement: state slots → `model.x = init`
|
|
@@ -139,16 +397,69 @@ export function desugarSignals(source: ts.SourceFile): {
|
|
|
139
397
|
function loweredStatement(
|
|
140
398
|
statement: ts.Statement,
|
|
141
399
|
bindings: ReactiveImportBindings,
|
|
400
|
+
signalNames: ReadonlySet<string>,
|
|
401
|
+
injectedCellNames: ReadonlySet<string>,
|
|
402
|
+
blockingCellNames: ReadonlySet<string>,
|
|
403
|
+
writtenNames: ReadonlySet<string>,
|
|
404
|
+
isEagerStreamComputed: EagerStreamPredicate,
|
|
142
405
|
): ts.Statement[] {
|
|
143
406
|
rejectMixedDeclaration(statement, bindings)
|
|
144
407
|
return (
|
|
408
|
+
injectedComputedStatements(statement, injectedCellNames, blockingCellNames) ??
|
|
145
409
|
stateAssignmentStatements(statement, bindings) ??
|
|
146
|
-
computedStatements(statement, bindings) ??
|
|
147
|
-
propsStatements(statement, bindings) ??
|
|
148
|
-
cellStatements(statement, bindings) ?? [statement]
|
|
410
|
+
computedStatements(statement, bindings, signalNames, isEagerStreamComputed) ??
|
|
411
|
+
propsStatements(statement, bindings, writtenNames) ??
|
|
412
|
+
cellStatements(statement, bindings, signalNames) ?? [statement]
|
|
149
413
|
)
|
|
150
414
|
}
|
|
151
415
|
|
|
416
|
+
/* Lowers a synthetic `const __vN = computed(<seed>)` (an async (sub)expression peek-cell
|
|
417
|
+
`analyzeComponent` injected, ADR-0032) to a `scope().trackedComputed(...)` cell read via
|
|
418
|
+
`$$readCell`. Two shapes, by the seed:
|
|
419
|
+
- a PROMISE seed (`computed(await X)` → an async `() => await X` thunk) → `trackedComputed(
|
|
420
|
+
thunk, <streaming>)`, where `streaming = !blockingCellNames.has(name)`: a blocking cell
|
|
421
|
+
(author `await`) joins the SSR barrier and resolves inline; a streaming one ships pending.
|
|
422
|
+
- an `AsyncIterable` seed (`computed(getStream())` → a bare `() => getStream()` thunk) →
|
|
423
|
+
`trackedComputed(thunk)`, byte-identical to an explicit `state.computed(getStream())`.
|
|
424
|
+
Recognized by the injected name (`computed` is unimported here); returns undefined for any
|
|
425
|
+
statement that is not one of these injected cells (each is its own single-declaration statement). */
|
|
426
|
+
function injectedComputedStatements(
|
|
427
|
+
statement: ts.Statement,
|
|
428
|
+
injectedCellNames: ReadonlySet<string>,
|
|
429
|
+
blockingCellNames: ReadonlySet<string>,
|
|
430
|
+
): ts.Statement[] | undefined {
|
|
431
|
+
if (!ts.isVariableStatement(statement)) {
|
|
432
|
+
return undefined
|
|
433
|
+
}
|
|
434
|
+
const statements: ts.Statement[] = []
|
|
435
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
436
|
+
if (!ts.isIdentifier(declaration.name) || !injectedCellNames.has(declaration.name.text)) {
|
|
437
|
+
return undefined
|
|
438
|
+
}
|
|
439
|
+
const name = declaration.name.text
|
|
440
|
+
const argument = seedArgument(declaration)
|
|
441
|
+
const wrapped =
|
|
442
|
+
argument === undefined
|
|
443
|
+
? factory.createArrowFunction(
|
|
444
|
+
undefined,
|
|
445
|
+
undefined,
|
|
446
|
+
[],
|
|
447
|
+
undefined,
|
|
448
|
+
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
449
|
+
factory.createIdentifier('undefined'),
|
|
450
|
+
)
|
|
451
|
+
: wrapSeed(argument)
|
|
452
|
+
/* A promise seed (async thunk) unwraps to the resolved value and carries the SSR tier flag;
|
|
453
|
+
a stream seed (bare thunk) probes to a frame cell and needs no flag (a stream never joins
|
|
454
|
+
the barrier), so it stays a single-arg call to byte-match the explicit form. */
|
|
455
|
+
const args = isAsyncSeed(wrapped)
|
|
456
|
+
? [wrapped, blockingCellNames.has(name) ? factory.createFalse() : factory.createTrue()]
|
|
457
|
+
: [wrapped]
|
|
458
|
+
statements.push(constDeclaration(name, scopeMethodCall('trackedComputed', args)))
|
|
459
|
+
}
|
|
460
|
+
return statements
|
|
461
|
+
}
|
|
462
|
+
|
|
152
463
|
/* Each lowering function above is all-or-nothing per VariableStatement: it returns
|
|
153
464
|
undefined the moment one declaration doesn't match its kind, so the whole statement
|
|
154
465
|
passes through verbatim. A statement mixing a reactive declaration with a differently-
|
|
@@ -243,6 +554,7 @@ function isComputedSlot(
|
|
|
243
554
|
function cellStatements(
|
|
244
555
|
statement: ts.Statement,
|
|
245
556
|
bindings: ReactiveImportBindings,
|
|
557
|
+
signalNames: ReadonlySet<string>,
|
|
246
558
|
): ts.Statement[] | undefined {
|
|
247
559
|
if (!ts.isVariableStatement(statement)) {
|
|
248
560
|
return undefined
|
|
@@ -258,7 +570,23 @@ function cellStatements(
|
|
|
258
570
|
return undefined
|
|
259
571
|
}
|
|
260
572
|
const args = (declaration.initializer as ts.CallExpression).arguments
|
|
261
|
-
|
|
573
|
+
/* `linked` wraps its seed argument (a bare value/`await`/stream expression becomes a
|
|
574
|
+
thunk, a literal `() => …` passes through) so the runtime primitive can route it to
|
|
575
|
+
an async cell; `state` is a value-taker — its args pass verbatim, never wrapped. */
|
|
576
|
+
if (callee === 'linked' && args[0] !== undefined) {
|
|
577
|
+
const wrapped = wrapSeed(args[0])
|
|
578
|
+
if (isAsyncSeed(wrapped)) {
|
|
579
|
+
warnPostAwaitReads(wrapped, signalNames)
|
|
580
|
+
}
|
|
581
|
+
statements.push(
|
|
582
|
+
constDeclaration(
|
|
583
|
+
declaration.name.text,
|
|
584
|
+
scopeMethodCall('linked', [wrapped, ...args.slice(1)]),
|
|
585
|
+
),
|
|
586
|
+
)
|
|
587
|
+
} else {
|
|
588
|
+
statements.push(constDeclaration(declaration.name.text, scopeMethodCall(callee, args)))
|
|
589
|
+
}
|
|
262
590
|
}
|
|
263
591
|
return statements
|
|
264
592
|
}
|
|
@@ -268,6 +596,8 @@ function cellStatements(
|
|
|
268
596
|
function computedStatements(
|
|
269
597
|
statement: ts.Statement,
|
|
270
598
|
bindings: ReactiveImportBindings,
|
|
599
|
+
signalNames: ReadonlySet<string>,
|
|
600
|
+
isEagerStreamComputed: EagerStreamPredicate,
|
|
271
601
|
): ts.Statement[] | undefined {
|
|
272
602
|
if (!ts.isVariableStatement(statement)) {
|
|
273
603
|
return undefined
|
|
@@ -282,22 +612,40 @@ function computedStatements(
|
|
|
282
612
|
return undefined
|
|
283
613
|
}
|
|
284
614
|
const name = declaration.name.text
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
615
|
+
const argument = seedArgument(declaration)
|
|
616
|
+
/* The seed thunk: a wrapped argument (bare expr / `await` / literal thunk), or an arg-
|
|
617
|
+
less `computed()` degenerating to `() => undefined`. */
|
|
618
|
+
const wrapped =
|
|
619
|
+
argument === undefined
|
|
620
|
+
? factory.createArrowFunction(
|
|
621
|
+
undefined,
|
|
622
|
+
undefined,
|
|
623
|
+
[],
|
|
624
|
+
undefined,
|
|
625
|
+
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
626
|
+
factory.createIdentifier('undefined'),
|
|
627
|
+
)
|
|
628
|
+
: wrapSeed(argument)
|
|
629
|
+
if (isAsyncSeed(wrapped)) {
|
|
630
|
+
/* Async seed → the eager `computed` primitive (an `AsyncComputed` cell, read via
|
|
631
|
+
`$$readCell`); the runtime unwraps the promise. */
|
|
632
|
+
warnPostAwaitReads(wrapped, signalNames)
|
|
633
|
+
statements.push(constDeclaration(name, scopeMethodCall('computed', [wrapped])))
|
|
634
|
+
} else if (isEagerStreamComputed(declaration)) {
|
|
635
|
+
/* Stream seed → the eager `trackedComputed`, which probes the seed and auto-tracks a
|
|
636
|
+
stream (`AsyncComputed`) or falls back to a lazy computed; read via `$$readCell`.
|
|
637
|
+
Reached when the seed type resolves to `asyncIterable` (ADR-0023) or, fail-open with
|
|
638
|
+
no warm program, when it is a bare call/identifier (`isBareCallComputed`). */
|
|
639
|
+
statements.push(constDeclaration(name, scopeMethodCall('trackedComputed', [wrapped])))
|
|
640
|
+
} else {
|
|
641
|
+
/* Sync arithmetic/member/literal seed → the lazy `derive` doc slot, read as `name()`. */
|
|
642
|
+
statements.push(
|
|
643
|
+
constDeclaration(
|
|
644
|
+
name,
|
|
645
|
+
scopeMethodCall('derive', [factory.createStringLiteral(name), wrapped]),
|
|
646
|
+
),
|
|
294
647
|
)
|
|
295
|
-
|
|
296
|
-
constDeclaration(
|
|
297
|
-
name,
|
|
298
|
-
scopeMethodCall('derive', [factory.createStringLiteral(name), compute]),
|
|
299
|
-
),
|
|
300
|
-
)
|
|
648
|
+
}
|
|
301
649
|
}
|
|
302
650
|
return statements
|
|
303
651
|
}
|
|
@@ -374,14 +722,18 @@ function propsBindingKey(element: ts.BindingElement): string {
|
|
|
374
722
|
throw new Error('abide: computed prop keys in `props()` destructuring are not supported')
|
|
375
723
|
}
|
|
376
724
|
|
|
377
|
-
/* If `statement` is a `const {…, ...rest} = props()` destructure, returns one
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
725
|
+
/* If `statement` is a `const {…, ...rest} = props()` destructure, returns one binding per
|
|
726
|
+
named prop — plus a `const rest = restProps($props, [consumed])` for a rest binding;
|
|
727
|
+
otherwise undefined. A prop the component only READS is a read-only reactive computed
|
|
728
|
+
(`scope().derive("name", () => $props["key"]?.() ?? default)`, read as `name()`). A prop
|
|
729
|
+
it WRITES or forwards (in `writtenNames`) is a writable `.value` cell instead
|
|
730
|
+
(`$$bindableProp($props, "key", () => default)`, read/written as `name.value`), so a
|
|
731
|
+
two-way `bind:prop` on the parent flows back. The `?? default` / `fallback` thunk applies
|
|
732
|
+
the binding's `= default` when the prop is absent. */
|
|
382
733
|
function propsStatements(
|
|
383
734
|
statement: ts.Statement,
|
|
384
735
|
bindings: ReactiveImportBindings,
|
|
736
|
+
writtenNames: ReadonlySet<string>,
|
|
385
737
|
): ts.Statement[] | undefined {
|
|
386
738
|
if (!ts.isVariableStatement(statement)) {
|
|
387
739
|
return undefined
|
|
@@ -396,6 +748,36 @@ function propsStatements(
|
|
|
396
748
|
}
|
|
397
749
|
const { bindings: propBindings, rest } = propsDestructure(declaration)
|
|
398
750
|
for (const { local, key, initializer } of propBindings) {
|
|
751
|
+
/* A written/forwarded prop → `$$bindableProp($props, "key", () => default)`. */
|
|
752
|
+
if (writtenNames.has(local)) {
|
|
753
|
+
const args: ts.Expression[] = [
|
|
754
|
+
factory.createIdentifier('$props'),
|
|
755
|
+
factory.createStringLiteral(key),
|
|
756
|
+
]
|
|
757
|
+
if (initializer !== undefined) {
|
|
758
|
+
args.push(
|
|
759
|
+
factory.createArrowFunction(
|
|
760
|
+
undefined,
|
|
761
|
+
undefined,
|
|
762
|
+
[],
|
|
763
|
+
undefined,
|
|
764
|
+
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
765
|
+
factory.createParenthesizedExpression(initializer),
|
|
766
|
+
),
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
statements.push(
|
|
770
|
+
constDeclaration(
|
|
771
|
+
local,
|
|
772
|
+
factory.createCallExpression(
|
|
773
|
+
factory.createIdentifier('$$bindableProp'),
|
|
774
|
+
undefined,
|
|
775
|
+
args,
|
|
776
|
+
),
|
|
777
|
+
),
|
|
778
|
+
)
|
|
779
|
+
continue
|
|
780
|
+
}
|
|
399
781
|
/* `$props["key"]?.()` — the parent thunk, optionally called. */
|
|
400
782
|
const read = factory.createCallChain(
|
|
401
783
|
factory.createElementAccessExpression(
|
|
@@ -430,7 +812,7 @@ function propsStatements(
|
|
|
430
812
|
),
|
|
431
813
|
)
|
|
432
814
|
}
|
|
433
|
-
/* The rest bag gathers every prop not named above (and not
|
|
815
|
+
/* The rest bag gathers every prop not named above (and not `children`). */
|
|
434
816
|
if (rest !== undefined) {
|
|
435
817
|
const consumed = propBindings.map((binding) => factory.createStringLiteral(binding.key))
|
|
436
818
|
statements.push(
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { referencedIdentifiers } from './referencedIdentifiers.ts'
|
|
2
|
+
|
|
3
|
+
/* True when EVERY referenced identifier of `expression` is prefix-evaluable: none is a
|
|
4
|
+
template-local binder and none is an async-cell name (still pending at prefix time). Over-collects
|
|
5
|
+
identifiers, so any doubt fails closed. Shared by the await-flight classifier (hoistableAwaits) and
|
|
6
|
+
the child-render-flight classifier (hoistableChildRenders) — a hoisted await's promise and a
|
|
7
|
+
hoisted child's props must clear the same bar to start in the render prefix. */
|
|
8
|
+
export function expressionIsPrefixEvaluable(
|
|
9
|
+
expression: string,
|
|
10
|
+
binders: ReadonlySet<string>,
|
|
11
|
+
cellReadNames: ReadonlySet<string>,
|
|
12
|
+
): boolean {
|
|
13
|
+
for (const name of referencedIdentifiers(expression)) {
|
|
14
|
+
if (binders.has(name) || cellReadNames.has(name)) {
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return true
|
|
19
|
+
}
|