@abide/abide 0.49.0 → 0.50.1
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 +564 -437
- package/CHANGELOG.md +183 -0
- package/README.md +165 -202
- package/package.json +14 -5
- package/src/abideLsp.ts +5 -6
- package/src/abideResolverPlugin.ts +197 -169
- package/src/build.ts +79 -56
- package/src/buildDisconnected.ts +0 -2
- package/src/checkAbide.ts +12 -2
- 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/spawnEmbeddedServer.ts +13 -4
- package/src/lib/bundle/waitForServer.ts +6 -1
- package/src/lib/mcp/mcpTools.ts +15 -6
- 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 +98 -13
- package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
- 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/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 +14 -14
- 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 +4 -1
- 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/changeAffectsClient.ts +12 -7
- package/src/lib/shared/createCacheStore.ts +31 -8
- 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/{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/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/snapshotShippable.ts +8 -7
- 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/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 -8
- 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/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 +51 -12
- package/src/lib/ui/compile/compileShadow.ts +145 -18
- package/src/lib/ui/compile/composeProps.ts +12 -2
- 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 +418 -36
- package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
- package/src/lib/ui/compile/generateBuild.ts +52 -9
- package/src/lib/ui/compile/generateSSR.ts +242 -43
- 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/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 +26 -23
- 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 -1092
- 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 +21 -22
- 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 +43 -94
- 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/disposeRange.ts +2 -1
- package/src/lib/ui/dom/each.ts +26 -4
- package/src/lib/ui/dom/eachAsync.ts +39 -3
- 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/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/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/switchBlock.ts +30 -7
- package/src/lib/ui/dom/tryBlock.ts +230 -66
- 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 +2 -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/remoteProxy.ts +84 -159
- package/src/lib/ui/renderChain.ts +49 -13
- package/src/lib/ui/renderToStream.ts +22 -18
- package/src/lib/ui/resumeSeedScript.ts +1 -1
- package/src/lib/ui/router.ts +63 -36
- package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
- package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
- 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 +1 -1
- 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 +3 -42
- 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 +16 -6
- 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/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/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/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/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/mutateDocArray.ts +0 -38
- 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 -108
- package/src/lib/ui/installHotBridge.ts +0 -95
- 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 -43
- 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
|
@@ -2,7 +2,6 @@ import { applyPatchToTree } from './applyPatchToTree.ts'
|
|
|
2
2
|
import { batch } from './batch.ts'
|
|
3
3
|
import { createComputedNode } from './createComputedNode.ts'
|
|
4
4
|
import { createSignalNode } from './createSignalNode.ts'
|
|
5
|
-
import { PATCH_BUS } from './PATCH_BUS.ts'
|
|
6
5
|
import { pathSegments } from './pathSegments.ts'
|
|
7
6
|
import { readNode } from './readNode.ts'
|
|
8
7
|
import { trigger } from './trigger.ts'
|
|
@@ -59,12 +58,9 @@ export function createDoc(initial: unknown): Doc {
|
|
|
59
58
|
truth. Held apart from `nodes` so the structural wake/eviction never touches
|
|
60
59
|
them; their dirtiness is driven entirely by the deps they read (the signal
|
|
61
60
|
graph), not by tree mutations. They are not in `tree`, so `snapshot` omits
|
|
62
|
-
them, and they never pass through `apply
|
|
63
|
-
|
|
61
|
+
them, and they never pass through `apply` — a recompute is a downstream reaction
|
|
62
|
+
of the signal graph, not a tree change. */
|
|
64
63
|
const computed = new Map<string, ReactiveNode>()
|
|
65
|
-
/* Set to the returned document before any apply runs, so a PATCH_BUS event can
|
|
66
|
-
name the document it came from (reference identity, the undo/persistence key). */
|
|
67
|
-
let self: Doc
|
|
68
64
|
|
|
69
65
|
/* Links `path` into the trie under its parent and continues up the ancestor chain.
|
|
70
66
|
Stops at the first ancestor whose parent set already lists it — that link, and
|
|
@@ -227,10 +223,6 @@ export function createDoc(initial: unknown): Doc {
|
|
|
227
223
|
/* Segments index the tree, so they carry the REAL keys (unescaped); the path
|
|
228
224
|
strings (parentPath, node-map keys) stay escaped, re-walked through walkPath. */
|
|
229
225
|
const segments = patch.path === '' ? [] : pathSegments(patch.path)
|
|
230
|
-
/* Capture the pre-image only when a consumer is listening (the inverse's only
|
|
231
|
-
cost): a replace/remove inverts to the value it overwrote, an add to a
|
|
232
|
-
remove (computed post-apply, below, to resolve an array append's index). */
|
|
233
|
-
const before = PATCH_BUS.active ? walkPath(tree, patch.path) : undefined
|
|
234
226
|
tree = applyPatchToTree(tree, patch, segments)
|
|
235
227
|
const parentPath = parentPathOf(patch.path)
|
|
236
228
|
const parentValue = walkPath(tree, parentPath).value
|
|
@@ -270,40 +262,9 @@ export function createDoc(initial: unknown): Doc {
|
|
|
270
262
|
} else {
|
|
271
263
|
wakeSubtree(parentPath, true, true)
|
|
272
264
|
}
|
|
273
|
-
/* Announce the change before effects flush, so a patch an effect emits in
|
|
274
|
-
reaction lands AFTER this one on the bus — the journal stays chronological.
|
|
275
|
-
Emitting inside the batch keeps it ahead of the depth-0 flush on batch exit. */
|
|
276
|
-
if (PATCH_BUS.active) {
|
|
277
|
-
PATCH_BUS.emit({ doc: self, patch, inverse: inverseOf(patch, before) })
|
|
278
|
-
}
|
|
279
265
|
})
|
|
280
266
|
}
|
|
281
267
|
|
|
282
|
-
/* The patch that undoes `patch`, from the pre-image `before` (a value the change
|
|
283
|
-
overwrote/removed) and the now-mutated tree. An add inverts to removing the slot
|
|
284
|
-
it created — resolving an array append (`-`) to the concrete last index it took.
|
|
285
|
-
A replace/remove of a path that held nothing inverts to remove/nothing. */
|
|
286
|
-
function inverseOf(
|
|
287
|
-
patch: Patch,
|
|
288
|
-
before: ReturnType<typeof walkPath> | undefined,
|
|
289
|
-
): Patch | undefined {
|
|
290
|
-
if (patch.op === 'add') {
|
|
291
|
-
const parentPath = parentPathOf(patch.path)
|
|
292
|
-
const parent = walkPath(tree, parentPath).value
|
|
293
|
-
const resolved =
|
|
294
|
-
Array.isArray(parent) && patch.path.endsWith('/-')
|
|
295
|
-
? `${parentPath}/${parent.length - 1}`
|
|
296
|
-
: patch.path
|
|
297
|
-
return { op: 'remove', path: resolved }
|
|
298
|
-
}
|
|
299
|
-
if (patch.op === 'replace') {
|
|
300
|
-
return before?.exists
|
|
301
|
-
? { op: 'replace', path: patch.path, value: before.value }
|
|
302
|
-
: { op: 'remove', path: patch.path }
|
|
303
|
-
}
|
|
304
|
-
return before?.exists ? { op: 'add', path: patch.path, value: before.value } : undefined
|
|
305
|
-
}
|
|
306
|
-
|
|
307
268
|
/*
|
|
308
269
|
A stable accessor bound to one scalar leaf — what the compiler emits so a hot
|
|
309
270
|
loop has zero string work: the node, the parent container, and the leaf key
|
|
@@ -355,7 +316,7 @@ export function createDoc(initial: unknown): Doc {
|
|
|
355
316
|
}
|
|
356
317
|
}
|
|
357
318
|
|
|
358
|
-
self = {
|
|
319
|
+
const self: Doc = {
|
|
359
320
|
read,
|
|
360
321
|
cell,
|
|
361
322
|
derive,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { boundaryFor } from './boundaryFor.ts'
|
|
2
|
+
import { CURRENT_BOUNDARY } from './CURRENT_BOUNDARY.ts'
|
|
1
3
|
import { CURRENT_SCOPE } from './CURRENT_SCOPE.ts'
|
|
2
4
|
import { inScope } from './inScope.ts'
|
|
3
5
|
import { NODE_STATE } from './NODE_STATE.ts'
|
|
@@ -67,5 +69,12 @@ export function createEffectNode(fn: () => EffectResult): () => void {
|
|
|
67
69
|
if (OWNER.current !== undefined) {
|
|
68
70
|
OWNER.current.push(dispose)
|
|
69
71
|
}
|
|
72
|
+
/* Effects built inside a reactive `{#try}` associate with its boundary so a LATER re-run
|
|
73
|
+
throw (an async cell that rejects post-mount, read via a throwing peek) routes there
|
|
74
|
+
rather than the collect-and-rethrow fallback. Populated only under a boundary — the
|
|
75
|
+
node shape stays untouched, the lookup is cold (only on the throw path). */
|
|
76
|
+
if (CURRENT_BOUNDARY.current !== undefined) {
|
|
77
|
+
boundaryFor.set(node, CURRENT_BOUNDARY.current)
|
|
78
|
+
}
|
|
70
79
|
return dispose
|
|
71
80
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { RENDER } from './RENDER.ts'
|
|
2
2
|
|
|
3
|
-
/* Marks entry into a render/mount. The OUTERMOST one (depth 0)
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/* Marks entry into a render/mount. The OUTERMOST one (depth 0) clears the per-path
|
|
4
|
+
block-id counters so every render pass starts each path at 0 (a re-navigation to the
|
|
5
|
+
same route must not continue a prior pass's counter); a child component's render/mount
|
|
6
|
+
runs at depth > 0 and continues the same map. Pair with `exitRenderPass`. */
|
|
6
7
|
// @documentation plumbing
|
|
7
8
|
export function enterRenderPass(): void {
|
|
8
9
|
if (RENDER.depth === 0) {
|
|
9
|
-
RENDER.
|
|
10
|
+
RENDER.blockCounters.clear()
|
|
10
11
|
}
|
|
11
12
|
RENDER.depth += 1
|
|
12
13
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { boundaryFor } from './boundaryFor.ts'
|
|
1
2
|
import { NODE_STATE } from './NODE_STATE.ts'
|
|
2
3
|
import { REACTIVE_CONTEXT } from './REACTIVE_CONTEXT.ts'
|
|
3
4
|
import type { ReactiveNode } from './types/ReactiveNode.ts'
|
|
@@ -49,12 +50,21 @@ function drain(): void {
|
|
|
49
50
|
try {
|
|
50
51
|
updateIfNecessary(node)
|
|
51
52
|
} catch (error) {
|
|
52
|
-
/*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
it
|
|
57
|
-
|
|
53
|
+
/* A node built inside a reactive `{#try}` routes its throw to that boundary
|
|
54
|
+
(swap the guarded content to the catch branch) instead of the rethrow
|
|
55
|
+
fallback — this is the "later re-run throw" the sync boundary could not
|
|
56
|
+
catch. Reset it CLEAN either way so a later write to its deps can re-queue
|
|
57
|
+
it (otherwise `mark`'s CLEAN→dirty gate leaves it permanently inert). */
|
|
58
|
+
const boundary = boundaryFor.get(node)
|
|
59
|
+
if (boundary !== undefined) {
|
|
60
|
+
boundary.handle(error)
|
|
61
|
+
node.status = NODE_STATE.CLEAN
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
/* No boundary — one effect throwing must not strand the effects queued behind
|
|
65
|
+
it (they live in this same `batch`, unreachable once we swap
|
|
66
|
+
`pendingEffects`); keep draining and surface the error(s) once the graph has
|
|
67
|
+
settled rather than swallowing them. */
|
|
58
68
|
node.status = NODE_STATE.CLEAN
|
|
59
69
|
if (errors === undefined) {
|
|
60
70
|
errors = []
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
True for an `async () => …` / `async function () {}`. This is the async cell's
|
|
3
|
+
`await` marker at runtime: the `computed`/`linked` transform lowers `computed(await p)`
|
|
4
|
+
to `computed(async () => await p)`, so an async-function seed means "unwrap the
|
|
5
|
+
promise and track it" — as opposed to a plain thunk returning a promise, which is held
|
|
6
|
+
opaque (`Computed<Promise<T>>`). The tag is the reliable signal (`.constructor.name`
|
|
7
|
+
survives minification less reliably; `Symbol.toStringTag` is spec-stable).
|
|
8
|
+
*/
|
|
9
|
+
export function isAsyncFunction(value: unknown): boolean {
|
|
10
|
+
return (
|
|
11
|
+
typeof value === 'function' &&
|
|
12
|
+
(value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag] === 'AsyncFunction'
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { blockId } from './blockId.ts'
|
|
1
2
|
import { RENDER } from './RENDER.ts'
|
|
2
3
|
|
|
3
|
-
/* The next block id in the current render pass
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/* The next block id in the current render pass, namespaced by the ambient render-path
|
|
5
|
+
(ADR-0037). `await`/`try` blocks draw it in document order WITHIN their path; a page
|
|
6
|
+
id and a child component's id can never collide because they carry different paths, so
|
|
7
|
+
ids stay congruent SSR↔client even when the server renders sibling children concurrently.
|
|
8
|
+
Delegates to the shared `blockId` over the client's per-pass `RENDER.blockCounters` map. */
|
|
6
9
|
// @documentation plumbing
|
|
7
|
-
export function nextBlockId():
|
|
8
|
-
|
|
9
|
-
RENDER.blockId += 1
|
|
10
|
-
return id
|
|
10
|
+
export function nextBlockId(): string {
|
|
11
|
+
return blockId(RENDER.blockCounters)
|
|
11
12
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CURRENT_PATH } from './CURRENT_PATH.ts'
|
|
2
|
+
import { withPath } from './withPath.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The render-path a `<Child/>` mounts under — the boundary id of a STREAMED child component (ADR-0039).
|
|
6
|
+
Composes the child's ordinal segment onto the ambient path exactly as the client's `mountChild` /
|
|
7
|
+
`mountStreamedChild` does (`withPath(ordinal, …)`), so the server-written `abide:await:CHILDPATH`
|
|
8
|
+
boundary and the client adopter compute the SAME id with no counter to drift. It carries `/`
|
|
9
|
+
separators and never a `:`, so a child path (`products/0`) can't collide with an await-block id
|
|
10
|
+
(`products:0`). Server-emit-only (the streaming boundary is server codegen), so it tree-shakes out of
|
|
11
|
+
the client bundle like `$$flight`.
|
|
12
|
+
*/
|
|
13
|
+
// @documentation plumbing
|
|
14
|
+
export function renderPath(segment: string | number): string {
|
|
15
|
+
return withPath(segment, () => CURRENT_PATH.current)
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
A reactive error boundary — the runtime handle a `{#try}` installs so a LATER re-run
|
|
3
|
+
throw from a guarded effect (an async cell that rejects after mount, read via a throwing
|
|
4
|
+
peek) routes back to it. `handle(error)` swaps the guarded content to the catch branch;
|
|
5
|
+
the scheduler (`flushEffects.drain`) calls it when a node associated with this boundary
|
|
6
|
+
(`boundaryFor`) throws during a flush. Kept minimal so the boundary can install itself as
|
|
7
|
+
the ambient `CURRENT_BOUNDARY` around a guarded build without dragging DOM types here.
|
|
8
|
+
*/
|
|
9
|
+
export type Boundary = { handle(error: unknown): void }
|
|
@@ -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
|
}
|
|
@@ -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
|
+
}
|
|
@@ -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
|
}
|
|
@@ -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
|
+
}
|