@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
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ErrorJsonSchemas } from './ErrorJsonSchemas.ts'
|
|
2
|
+
import type { InputCoercion } from './InputCoercion.ts'
|
|
3
|
+
import type { OutputWirePlan } from './OutputWirePlan.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The build-time facts the warm rpc server program resolves for one `$rpc/**` handler, gathered into
|
|
7
|
+
a single named payload that `prepareRpcModule` destructures (each rewrite direction picks the fields
|
|
8
|
+
it needs). Replaces a positional argument list where ordering was load-bearing — a new build stamp
|
|
9
|
+
is now one field on this shape rather than a new positional slot every caller must thread. Every
|
|
10
|
+
field is optional: an absent field means the warm program resolved no verdict (no program built, an
|
|
11
|
+
unresolvable node), and the rewrite falls open to its char-scan / today's behaviour exactly as
|
|
12
|
+
before. `streaming` overrides the char-scan streaming verdict; the rest are stamped as server opts
|
|
13
|
+
(`coercion`→`coerce`, `inputSchema`→`inputJsonSchema`, `outputSchema`→`outputJsonSchema`,
|
|
14
|
+
`errorSchemas`→`errorJsonSchemas`) or the client opt (`outputWirePlan`).
|
|
15
|
+
*/
|
|
16
|
+
export type RpcBuildStamps = {
|
|
17
|
+
streaming?: boolean
|
|
18
|
+
coercion?: InputCoercion
|
|
19
|
+
inputSchema?: Record<string, unknown>
|
|
20
|
+
outputSchema?: Record<string, unknown>
|
|
21
|
+
errorSchemas?: ErrorJsonSchemas
|
|
22
|
+
outputWirePlan?: OutputWirePlan
|
|
23
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { HttpError } from '../HttpError.ts'
|
|
2
2
|
import type { ErrorSpec } from './ErrorSpec.ts'
|
|
3
|
-
import type { OutboxEntry } from './OutboxEntry.ts'
|
|
4
3
|
import type { StandardSchemaV1 } from './StandardSchemaV1.ts'
|
|
5
4
|
import type { ValidationErrorData } from './ValidationErrorData.ts'
|
|
6
5
|
|
|
@@ -16,9 +15,9 @@ type DeclaredErrorData<
|
|
|
16
15
|
/*
|
|
17
16
|
A rpc's `isError` guard — branch a caught error on a kind THIS rpc knows, with the
|
|
18
17
|
data type narrowed from the rpc's own `errors` spec. `rpc.isError(err, 'outOfStock')`
|
|
19
|
-
narrows `.data` to that error's payload; the framework-reserved `'validation'`
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
narrows `.data` to that error's payload; the framework-reserved `'validation'` to its
|
|
19
|
+
fixed shape; any other string narrows `.kind` only (`.data` stays `unknown` — it
|
|
20
|
+
belongs to a different rpc, whose type isn't in scope here).
|
|
22
21
|
|
|
23
22
|
The declared-name overload comes first so an app error shadows the reserved ones on a
|
|
24
23
|
name collision, and so the kind argument autocompletes to the rpc's declared names.
|
|
@@ -32,9 +31,5 @@ export interface RpcErrorGuard<Errors extends ErrorSpec> {
|
|
|
32
31
|
error: unknown,
|
|
33
32
|
kind: 'validation',
|
|
34
33
|
): error is HttpError & { kind: 'validation'; data: ValidationErrorData }
|
|
35
|
-
(
|
|
36
|
-
error: unknown,
|
|
37
|
-
kind: 'queued',
|
|
38
|
-
): error is HttpError & { kind: 'queued'; data: OutboxEntry<unknown> }
|
|
39
34
|
<Kind extends string>(error: unknown, kind: Kind): error is HttpError & { kind: Kind }
|
|
40
35
|
}
|
|
@@ -9,8 +9,8 @@ fan-out and a client-side ws proxy by build target. Iterating the socket
|
|
|
9
9
|
is the live stream — no replay. `.tail(count)` opens a subscription seeded
|
|
10
10
|
with the last `count` frames of the retained tail (declared via
|
|
11
11
|
`{ tail: n }`; no-arg = the whole retained tail) before going live — the
|
|
12
|
-
optional
|
|
13
|
-
seeds from. `
|
|
12
|
+
optional NamedAsyncIterable retention capability a reactive `watch(socket, …)`
|
|
13
|
+
seeds from. `publish` is isomorphic: server code publishes in-process
|
|
14
14
|
and fans out to remote subscribers; client code sends a `pub` frame the
|
|
15
15
|
dispatcher validates against the topic's `clientPublish` flag. `clients`
|
|
16
16
|
exposes which adapter surfaces (browser / mcp / cli) advertise this
|
|
@@ -21,8 +21,8 @@ export interface Socket<T> extends AsyncIterable<T> {
|
|
|
21
21
|
readonly clients: ClientFlags
|
|
22
22
|
/* Send a frame to every subscriber (server always; client only when `clientPublish` is
|
|
23
23
|
set). Isomorphic: server code fans out in-process + to remote subscribers; client code
|
|
24
|
-
sends a validated `pub` frame. */
|
|
25
|
-
|
|
24
|
+
sends a validated `pub` frame. Mirrors Bun's `server.publish()`. */
|
|
25
|
+
publish(message: T): void
|
|
26
26
|
tail(count?: number, hooks?: TailHooks): AsyncIterable<T>
|
|
27
27
|
/* The latest retained frame, synchronously — `T | undefined` when none. The value
|
|
28
28
|
member of the probe family for a stream; `peek(socket)` routes here. */
|
|
@@ -10,7 +10,8 @@ write-set and read-set can't drift apart on a matching-string-key handshake.
|
|
|
10
10
|
|
|
11
11
|
Three partitions:
|
|
12
12
|
- `SsrBootState` fields seed ambient slots via the exhaustive `seedBootState` map;
|
|
13
|
-
- `cache`
|
|
13
|
+
- `cache` / `cells` are the warm-seed partitions (settled `cache()` values / resolved async-cell
|
|
14
|
+
values), drained into their client stores before mount;
|
|
14
15
|
- `route`/`params` are stamped for inspectability and are NOT read by the client
|
|
15
16
|
(the router re-resolves the route from the URL).
|
|
16
17
|
*/
|
|
@@ -18,4 +19,7 @@ export type SsrPayload = SsrBootState & {
|
|
|
18
19
|
route: string
|
|
19
20
|
params: Record<string, string>
|
|
20
21
|
cache?: CacheSnapshotEntry[]
|
|
22
|
+
/* Async-cell values that resolved during SSR, keyed by render-path id → ref-json string.
|
|
23
|
+
`startClient` seeds these into `CELL_SEED`; a hydrating cell reads its key to warm-hydrate. */
|
|
24
|
+
cells?: Record<string, string>
|
|
21
25
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Endpoint stream policy, declared once on a streaming rpc definition (ADR-0020).
|
|
3
|
+
`n` is the replay depth — how many retained frames a fresh reader gets before
|
|
4
|
+
going live, pairing with the server-side `tail` retention. Endpoint-fixed: a
|
|
5
|
+
late joiner can't request deeper replay than a fresh view. No runtime consumer
|
|
6
|
+
today (subscribableFromResponse ignores it); threaded onto the definition for
|
|
7
|
+
completeness and future use.
|
|
8
|
+
*/
|
|
9
|
+
export type StreamPolicy = {
|
|
10
|
+
n?: number
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The per-request list of STREAMING async-cell values that SETTLED during this SSR pass, each keyed by
|
|
3
|
+
the cell's serialization-stable render-path id (`${scope.id}:${index}`) (ADR-0035). A streaming cell
|
|
4
|
+
ships pending in the shell (excluded from the pre-mount `__SSR__.cells` warm-seed, which would
|
|
5
|
+
diverge from that pending markup), so instead `createAsyncCell.settleValue` records its resolved
|
|
6
|
+
VALUE here when it settles server-side. The page renderer reads this AFTER the shell — streaming an
|
|
7
|
+
`__abideResolve({ cellKey, value })` chunk per entry — so the client adopts the server-resolved
|
|
8
|
+
value post-hydration (no `loading…` flash) instead of only cold-re-running the seed.
|
|
9
|
+
|
|
10
|
+
Values (not promises) so the drain never AWAITS — a streaming cell may legitimately stay pending
|
|
11
|
+
through the whole request (the `{#if getFoo()}`-holds case), and awaiting it would hang the
|
|
12
|
+
response. Only cells that settled before the drain point are streamed; one still pending is simply
|
|
13
|
+
not (the client cold-runs it, unchanged). The streaming sibling of `ResolvedCells` (the BLOCKING
|
|
14
|
+
warm-seed, read at render-return and stamped into `__SSR__.cells`); this one is read later in the
|
|
15
|
+
stream and shipped out-of-order as chunks.
|
|
16
|
+
*/
|
|
17
|
+
export type StreamedCells = {
|
|
18
|
+
entries: { key: string; value: unknown }[]
|
|
19
|
+
}
|
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import type { CacheSnapshotEntry } from './CacheSnapshotEntry.ts'
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
Payload of one streamed `window.__abideResolve(...)` call.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
Payload of one streamed `window.__abideResolve(...)` call. Three arms:
|
|
5
|
+
|
|
6
|
+
- a full `CacheSnapshotEntry` (has `key` + body) settles a cache placeholder with warm data;
|
|
7
|
+
- a `{ key, miss }` marker means the server couldn't snapshot that cache body (binary, rejected,
|
|
8
|
+
evicted) so the client settles the placeholder with a live re-fetch instead;
|
|
9
|
+
- a `{ cellKey, value }` (ADR-0035) carries a STREAMING CELL's server-resolved value, keyed by its
|
|
10
|
+
render-path warm-key and `encodeRefJson`-encoded, applied post-hydration as a reactive update so
|
|
11
|
+
a non-cache peek stops flashing `loading…` on hydrate.
|
|
12
|
+
- a `{ cellSeed, value }` (ADR-0039) carries a STREAMED CHILD's blocking async-cell value that
|
|
13
|
+
resolved AFTER the head `__SSR__.cells` snapshot (its render was deferred to the drain), seeded
|
|
14
|
+
PRE-mount into `CELL_SEED` — the same warm partition as `__SSR__.cells` — so the child's cell
|
|
15
|
+
constructs resolved on hydrate rather than re-running.
|
|
16
|
+
|
|
17
|
+
Discriminate on `cellSeed` then `cellKey` (cell) then `miss` (cache miss) then default (cache entry).
|
|
9
18
|
*/
|
|
10
|
-
|
|
19
|
+
|
|
20
|
+
/* The cache arms — a warm snapshot entry or a `{ key, miss }` marker. `streamCacheResolutions`
|
|
21
|
+
yields exactly these (never the cell arm), so its element type is `CacheResolution`, keeping a
|
|
22
|
+
`.key` access on its results well-typed. */
|
|
23
|
+
export type CacheResolution = CacheSnapshotEntry | { key: string; miss: true }
|
|
24
|
+
|
|
25
|
+
export type StreamedResolution =
|
|
26
|
+
| CacheResolution
|
|
27
|
+
| { cellKey: string; value: string }
|
|
28
|
+
| { cellSeed: string; value: string }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Hooks a `tail(count, hooks)` caller passes to a
|
|
2
|
+
Hooks a `tail(count, hooks)` caller passes to a NamedAsyncIterable's retention
|
|
3
3
|
capability.
|
|
4
4
|
`replayed` must be signalled in-band exactly once per iteration — after the
|
|
5
5
|
last replayed frame, before any live frame, and even when nothing was
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The wire codec kind for a single top-level Args field (ADR-0029). Generalizes ADR-0028's scalar
|
|
3
|
+
`number`/`boolean` query coercion to the structured value kinds the warm server program can name
|
|
4
|
+
through the type graph — a `Date` / `Set` / `Map` by symbol identity, a `bigint` by type flag.
|
|
5
|
+
parseArgs revives a field's plain-JSON wire form into the runtime value the handler's declared
|
|
6
|
+
type expects: an ISO string → `Date`, a numeric string → `bigint`, a JSON array → `Set`, a JSON
|
|
7
|
+
entries array (or object) → `Map`. Top-level fields only in this increment — a structured value
|
|
8
|
+
NESTED inside another value is not descended into. Reviving is fail-open: an unrevivable value is
|
|
9
|
+
left as its JSON form, never thrown, so the plain-JSON / OpenAPI contract still works.
|
|
10
|
+
*/
|
|
11
|
+
export type WireKind = 'number' | 'boolean' | 'date' | 'bigint' | 'set' | 'map'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fieldErrorsFromIssues } from './fieldErrorsFromIssues.ts'
|
|
2
|
+
import { HttpError } from './HttpError.ts'
|
|
3
|
+
import type { StandardSchemaV1 } from './types/StandardSchemaV1.ts'
|
|
4
|
+
import type { ValidationErrorData } from './types/ValidationErrorData.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Builds the HttpError a client-side pre-flight validation failure throws (ADR-0026 D2),
|
|
8
|
+
shaped IDENTICALLY to the one a caller gets when the server rejects the same input with a
|
|
9
|
+
422: `kind: 'validation'`, `status: 422`, `data: ValidationErrorData` (`{ issues, fields }`),
|
|
10
|
+
so an `error instanceof HttpError && error.kind === 'validation'` branch — and the
|
|
11
|
+
`error.data.fields` form-error map — read the same whether the rejection came from the
|
|
12
|
+
client pre-flight or the server.
|
|
13
|
+
|
|
14
|
+
This is a UX optimization ONLY, never a trust boundary: the server's unconditional
|
|
15
|
+
`inputSchema['~standard'].validate` → `validationError` 422 (defineRpc.ts) stays
|
|
16
|
+
authoritative. A client that skips or fakes this check is still fully validated on the
|
|
17
|
+
server.
|
|
18
|
+
|
|
19
|
+
The `data` mapping (`fieldErrorsFromIssues`) is the same isomorphic helper the server 422
|
|
20
|
+
uses; the response envelope (`{ $abideError, data }`, status 422, reason phrase) mirrors
|
|
21
|
+
`typedErrorResponse('validation', 422, …)` — reconstructed here with a plain web-standard
|
|
22
|
+
`Response` so the browser bundle never imports the server's Response builder. The status
|
|
23
|
+
text mirrors `STATUS_TEXT[422]`.
|
|
24
|
+
*/
|
|
25
|
+
export function validationHttpError(issues: readonly StandardSchemaV1.Issue[]): HttpError {
|
|
26
|
+
const data: ValidationErrorData = { issues, fields: fieldErrorsFromIssues(issues) }
|
|
27
|
+
const response = new Response(JSON.stringify({ $abideError: 'validation', data }), {
|
|
28
|
+
status: 422,
|
|
29
|
+
statusText: 'Unprocessable Content',
|
|
30
|
+
headers: { 'content-type': 'application/json' },
|
|
31
|
+
})
|
|
32
|
+
return new HttpError(response, 'validation', data)
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The single source of the async-cell warm-seed key format: the `${scopeId}:${cellIndex}` string that
|
|
3
|
+
pairs a cell's serialization-stable render-path scope id with its per-scope declaration ordinal.
|
|
4
|
+
|
|
5
|
+
Both sides of the SSR→client handoff mint the key through THIS callable — the isomorphic
|
|
6
|
+
`createAsyncCell` runs it server-side to key its `resolvedCells`/`streamedCells` entries (which the
|
|
7
|
+
page renderer stamps into `__SSR__.cells` / streams as `{ cellKey }` / `{ cellSeed }` chunks), and
|
|
8
|
+
client-side to look the value up in `CELL_SEED`. Because the two sides now invoke one function, the
|
|
9
|
+
byte-for-byte agreement the warm-seed depends on is a shared definition rather than a template
|
|
10
|
+
literal re-typed at each call site: changing the delimiter here changes it for both sides at once,
|
|
11
|
+
so the SSR-text-≡-first-client-render mismatch class (ADR-0033) cannot silently reopen from a
|
|
12
|
+
one-sided edit. Pure so it stays trivially testable and inlinable.
|
|
13
|
+
*/
|
|
14
|
+
export function warmSeedKey(scopeId: string, cellIndex: number): string {
|
|
15
|
+
return `${scopeId}:${cellIndex}`
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The value-directed wire ENCODE step for a rpc response body (ADR-0029 output path). A
|
|
3
|
+
`JSON.stringify` replacer that rewrites the structured runtime values plain JSON can't carry into
|
|
4
|
+
their honest-JSON wire form, so `json()` never drops a `Set`/`Map` to `{}` or throws on a `bigint`:
|
|
5
|
+
|
|
6
|
+
- `bigint` → its digit string (plain JSON.stringify throws on a bigint)
|
|
7
|
+
- `Set<T>` → a JSON array of its values (plain JSON.stringify emits `{}`)
|
|
8
|
+
- `Map<K,V>`→ a JSON array of `[K,V]` entries (uniform, carries non-string keys)
|
|
9
|
+
|
|
10
|
+
A `Date` needs no branch — its native `toJSON` already rewrote it to an ISO string before the
|
|
11
|
+
replacer runs. Value-directed (not type-directed), so it runs for EVERY client — a non-abide caller
|
|
12
|
+
(curl / an OpenAPI SDK) receives the same honest JSON the projected schema (`jsonSchemaForType`)
|
|
13
|
+
describes. It is naturally recursive: a returned array/object member surfaces through the same
|
|
14
|
+
replacer, so a `Set` nested inside an object still encodes. Never throws — an ordinary value falls
|
|
15
|
+
through untouched.
|
|
16
|
+
*/
|
|
17
|
+
export function wireJsonReplacer(_key: string, value: unknown): unknown {
|
|
18
|
+
if (typeof value === 'bigint') {
|
|
19
|
+
return value.toString()
|
|
20
|
+
}
|
|
21
|
+
if (value instanceof Set) {
|
|
22
|
+
return [...value]
|
|
23
|
+
}
|
|
24
|
+
if (value instanceof Map) {
|
|
25
|
+
/* A Map iterates as `[key, value]` pairs, so the spread yields the entries array the
|
|
26
|
+
client output plan (and the projected schema) expects. */
|
|
27
|
+
return [...value]
|
|
28
|
+
}
|
|
29
|
+
return value
|
|
30
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
1
2
|
import { augmentModule } from './augmentModule.ts'
|
|
2
3
|
import { carriesBodyArgs } from './carriesBodyArgs.ts'
|
|
3
4
|
import { detectRpcMethod } from './detectRpcMethod.ts'
|
|
4
5
|
import { fileStem } from './fileStem.ts'
|
|
5
6
|
import { RPC_ARGS_TYPE } from './RPC_ARGS_TYPE.ts'
|
|
6
7
|
import { rpcUrlForFile } from './rpcUrlForFile.ts'
|
|
8
|
+
import type { HttpMethod } from './types/HttpMethod.ts'
|
|
7
9
|
import { writeDts } from './writeDts.ts'
|
|
8
10
|
|
|
9
11
|
/*
|
|
@@ -22,15 +24,23 @@ export async function writeRpcDts({
|
|
|
22
24
|
rpcDir,
|
|
23
25
|
rpcFiles,
|
|
24
26
|
importName,
|
|
27
|
+
methodForModule,
|
|
25
28
|
}: {
|
|
26
29
|
cwd: string
|
|
27
30
|
rpcDir: string
|
|
28
31
|
rpcFiles: string[]
|
|
29
32
|
importName: string
|
|
33
|
+
/* Warm server-program method resolver (ADR-0025 D2): reads the method off the export's
|
|
34
|
+
helper symbol so an aliased/re-exported helper resolves where `detectRpcMethod`'s
|
|
35
|
+
`RPC_EXPORT` regex misses it. Absent (no warm program) or undefined-per-file falls open to
|
|
36
|
+
the regex, byte-identical to before. */
|
|
37
|
+
methodForModule?: (modulePath: string) => HttpMethod | undefined
|
|
30
38
|
}): Promise<void> {
|
|
31
39
|
const pairs = await Promise.all(
|
|
32
40
|
rpcFiles.map(async (file): Promise<[string, string] | undefined> => {
|
|
33
|
-
const method =
|
|
41
|
+
const method =
|
|
42
|
+
methodForModule?.(resolve(rpcDir, file)) ??
|
|
43
|
+
detectRpcMethod(await Bun.file(`${rpcDir}/${file}`).text())
|
|
34
44
|
// A body rpc's args can't ride a URL — leave it out of the url() rpc map.
|
|
35
45
|
if (!method || carriesBodyArgs(method)) {
|
|
36
46
|
return undefined
|
|
@@ -8,7 +8,7 @@ Emits a `.d.ts` that augments createTestApp's `SocketClient` interface with one
|
|
|
8
8
|
entry per $socket, keyed by socket name (the same key `app.sockets.<name>`
|
|
9
9
|
resolves at runtime). The `socket(...)` helper statically returns `Socket<T>`,
|
|
10
10
|
so each entry is the export's own type directly — `app.sockets.chat` types as
|
|
11
|
-
`Socket<ChatMessage>`, iterable for the live stream with `.tail`/`.
|
|
11
|
+
`Socket<ChatMessage>`, iterable for the live stream with `.tail`/`.publish`.
|
|
12
12
|
Written to `src/.abide/testSockets.d.ts` like its rpc sibling.
|
|
13
13
|
*/
|
|
14
14
|
export async function writeTestSocketsDts({
|
|
@@ -39,8 +39,11 @@ import { commandNameForUrl } from '../shared/commandNameForUrl.ts'
|
|
|
39
39
|
import { createCacheStore } from '../shared/createCacheStore.ts'
|
|
40
40
|
import { HEALTH_PATH } from '../shared/HEALTH_PATH.ts'
|
|
41
41
|
import { pageSlot } from '../shared/pageSlot.ts'
|
|
42
|
+
import { pendingAsyncCellsSlot } from '../shared/pendingAsyncCellsSlot.ts'
|
|
43
|
+
import { resolvedCellsSlot } from '../shared/resolvedCellsSlot.ts'
|
|
42
44
|
import { SOCKETS_PATH } from '../shared/SOCKETS_PATH.ts'
|
|
43
45
|
import { sharedCacheStoreSlot } from '../shared/sharedCacheStoreSlot.ts'
|
|
46
|
+
import { streamedCellsSlot } from '../shared/streamedCellsSlot.ts'
|
|
44
47
|
import type { RemoteFunction } from '../shared/types/RemoteFunction.ts'
|
|
45
48
|
import type { Socket } from '../shared/types/Socket.ts'
|
|
46
49
|
import { createTestSocketChannel } from './createTestSocketChannel.ts'
|
|
@@ -72,7 +75,7 @@ export type TestApp = {
|
|
|
72
75
|
/* Rpc calls over HTTP, keyed by command name: `app.rpc.getProduct({ id })`. */
|
|
73
76
|
rpc: RpcClient
|
|
74
77
|
/* Sockets keyed by name: `app.sockets.ticker` is the Socket — iterate it for
|
|
75
|
-
the live stream, `.tail(n)` to seed, `.
|
|
78
|
+
the live stream, `.tail(n)` to seed, `.publish(m)` to send. */
|
|
76
79
|
sockets: SocketClient
|
|
77
80
|
/* The /__abide/health payload, decoded. */
|
|
78
81
|
health: () => Promise<unknown>
|
|
@@ -101,12 +104,24 @@ export async function createTestApp(): Promise<TestApp> {
|
|
|
101
104
|
sharedResolver: sharedCacheStoreSlot.resolver,
|
|
102
105
|
pageResolver: pageSlot.resolver,
|
|
103
106
|
baseResolver: baseSlot.resolver,
|
|
107
|
+
pendingAsyncCellsResolver: pendingAsyncCellsSlot.resolver,
|
|
108
|
+
resolvedCellsResolver: resolvedCellsSlot.resolver,
|
|
109
|
+
streamedCellsResolver: streamedCellsSlot.resolver,
|
|
104
110
|
activeServer: serverSlot.active,
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
const sharedStore = createCacheStore()
|
|
108
114
|
sharedCacheStoreSlot.resolver = () => sharedStore
|
|
109
115
|
cacheStoreSlot.resolver = () => requestContext.getStore()?.cache ?? sharedStore
|
|
116
|
+
const sharedPendingAsyncCells = { promises: [] }
|
|
117
|
+
pendingAsyncCellsSlot.resolver = () =>
|
|
118
|
+
requestContext.getStore()?.pendingAsyncCells ?? sharedPendingAsyncCells
|
|
119
|
+
const sharedResolvedCells = { entries: [] }
|
|
120
|
+
resolvedCellsSlot.resolver = () =>
|
|
121
|
+
requestContext.getStore()?.resolvedCells ?? sharedResolvedCells
|
|
122
|
+
const sharedStreamedCells = { entries: [] }
|
|
123
|
+
streamedCellsSlot.resolver = () =>
|
|
124
|
+
requestContext.getStore()?.streamedCells ?? sharedStreamedCells
|
|
110
125
|
pageSlot.resolver = resolvePageSnapshot
|
|
111
126
|
|
|
112
127
|
/* Eager env validation, exactly as serverEntry: a top-level env(schema) in
|
|
@@ -188,6 +203,9 @@ export async function createTestApp(): Promise<TestApp> {
|
|
|
188
203
|
sharedCacheStoreSlot.resolver = previous.sharedResolver
|
|
189
204
|
pageSlot.resolver = previous.pageResolver
|
|
190
205
|
baseSlot.resolver = previous.baseResolver
|
|
206
|
+
pendingAsyncCellsSlot.resolver = previous.pendingAsyncCellsResolver
|
|
207
|
+
resolvedCellsSlot.resolver = previous.resolvedCellsResolver
|
|
208
|
+
streamedCellsSlot.resolver = previous.streamedCellsResolver
|
|
191
209
|
serverSlot.active = previous.activeServer
|
|
192
210
|
}
|
|
193
211
|
|
package/src/lib/ui/README.md
CHANGED
|
@@ -61,7 +61,7 @@ write-path microbench this runs ~20× faster than a deep-proxy signal baseline.
|
|
|
61
61
|
|
|
62
62
|
- `compileSSR` renders to an HTML string (byte-identical to the client DOM).
|
|
63
63
|
- `renderToStream` streams: pending shell first, then resolved `<template await>`
|
|
64
|
-
fragments out of order as their promises settle;
|
|
64
|
+
fragments out of order as their promises settle; the inline swap script swaps them in.
|
|
65
65
|
- `hydrate` adopts the server DOM in place (no re-render) for static-structure
|
|
66
66
|
components — claims existing nodes, splits merged text, wires effects/listeners.
|
|
67
67
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { pendingAsyncCellsSlot } from '../shared/pendingAsyncCellsSlot.ts'
|
|
2
|
+
import type { PendingAsyncCells } from '../shared/types/PendingAsyncCells.ts'
|
|
3
|
+
import { cellBarrierBacking } from './runtime/cellBarrierBacking.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The async-cell barrier list a registration (`createAsyncCell`) or a drain (`settleAsyncCells`) should
|
|
7
|
+
use RIGHT NOW: a per-render isolated list if one is active (a hoisted concurrent child render — see
|
|
8
|
+
`isolateCellBarrier`), else the per-request list from `pendingAsyncCellsSlot`. The single seam both
|
|
9
|
+
barrier consumers read, so an isolated render can't leak its in-flight cells into a sibling's or the
|
|
10
|
+
page's drain. Undefined only when neither a render override nor a request/fallback list is installed.
|
|
11
|
+
*/
|
|
12
|
+
export function activePendingCells(): PendingAsyncCells | undefined {
|
|
13
|
+
return cellBarrierBacking.active.current() ?? pendingAsyncCellsSlot.get()
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The control-block vocabulary — the ONE source of truth shared by the parser
|
|
3
|
+
(`parseTemplate`'s dispatch/error) and the LSP highlighter (`structuralHeadTokens`, driven
|
|
4
|
+
by the parse walk), so the keywords the highlighter colors can't drift from the blocks the
|
|
5
|
+
parser accepts (the `{#snippet}` head once went uncolored because a hand-copy omitted it).
|
|
6
|
+
|
|
7
|
+
`BLOCK_OPENERS` follow `{#`, `BLOCK_CONNECTORS` follow `{:`. `for await` is the async-each
|
|
8
|
+
opener form the highlighter colors as one phrase (the parser reads the `await` inside a
|
|
9
|
+
`for` head); it is not a distinct opener.
|
|
10
|
+
*/
|
|
11
|
+
export const BLOCK_OPENERS = ['if', 'for', 'await', 'switch', 'try', 'snippet'] as const
|
|
12
|
+
|
|
13
|
+
export const BLOCK_CONNECTORS = [
|
|
14
|
+
'else if',
|
|
15
|
+
'else',
|
|
16
|
+
'then',
|
|
17
|
+
'catch',
|
|
18
|
+
'finally',
|
|
19
|
+
'case',
|
|
20
|
+
'default',
|
|
21
|
+
] as const
|
|
@@ -17,7 +17,10 @@ across bundles. Emitted inline (not imported) so the generated render module is
|
|
|
17
17
|
self-contained.
|
|
18
18
|
*/
|
|
19
19
|
export const SSR_ESCAPE =
|
|
20
|
-
|
|
20
|
+
// Nullish escapes to empty text, never the literal `"undefined"` — a pending async
|
|
21
|
+
// read (ADR-0032 D3) renders nothing. `$attr` guards nullish before it calls `$esc`,
|
|
22
|
+
// so its whole-value undefined still drops the attribute.
|
|
23
|
+
'const $esc = (v) => (v == null ? "" : String(v)).replace(/[&<>"\']/g, (c) => ' +
|
|
21
24
|
"({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' })[c]);\n" +
|
|
22
25
|
'const $attr = (n, v) => (v === false || v === null || v === undefined) ? "" ' +
|
|
23
26
|
': v === true ? (" " + n) : (" " + n + \'="\' + $esc(v) + \'"\');\n' +
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/*
|
|
2
2
|
The abide-ui runtime names every compiled component module depends on, each
|
|
3
|
-
paired with its package subpath (after the package name). One source of truth
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
paired with its package subpath (after the package name). One source of truth for
|
|
4
|
+
the import block `compileModule` emits at the head of every component module, so the
|
|
5
|
+
emitted imports can't drift from the real package exports (a test checks each
|
|
6
|
+
resolves). Order is the emit order.
|
|
7
7
|
|
|
8
8
|
`alias` (when set) is the LOCAL name codegen emits — a `$$`-prefixed form reserved
|
|
9
9
|
for the compiler so a user variable of the helper's bare name can never collide
|
|
10
|
-
(`each as $$each`). It defaults to `name
|
|
11
|
-
|
|
12
|
-
flip to its `$$` alias, set `alias` here in lockstep.
|
|
10
|
+
(`each as $$each`). It defaults to `name`. As each helper's emit sites flip to its
|
|
11
|
+
`$$` alias, set `alias` here in lockstep.
|
|
13
12
|
*/
|
|
14
13
|
export const UI_RUNTIME_IMPORTS: { name: string; specifier: string; alias?: string }[] = [
|
|
15
14
|
{ name: 'snippet', specifier: 'shared/snippet', alias: '$$snippet' },
|
|
@@ -44,15 +43,43 @@ export const UI_RUNTIME_IMPORTS: { name: string; specifier: string; alias?: stri
|
|
|
44
43
|
{ name: 'mountSlot', specifier: 'ui/dom/mountSlot', alias: '$$mountSlot' },
|
|
45
44
|
{ name: 'outlet', specifier: 'ui/dom/outlet', alias: '$$outlet' },
|
|
46
45
|
{ name: 'mountChild', specifier: 'ui/dom/mountChild', alias: '$$mountChild' },
|
|
46
|
+
{
|
|
47
|
+
name: 'mountStreamedChild',
|
|
48
|
+
specifier: 'ui/dom/mountStreamedChild',
|
|
49
|
+
alias: '$$mountStreamedChild',
|
|
50
|
+
},
|
|
47
51
|
{ name: 'mergeProps', specifier: 'ui/dom/mergeProps', alias: '$$mergeProps' },
|
|
48
52
|
{ name: 'spreadProps', specifier: 'ui/dom/spreadProps', alias: '$$spreadProps' },
|
|
49
53
|
{ name: 'restProps', specifier: 'ui/dom/restProps', alias: '$$restProps' },
|
|
54
|
+
{ name: 'bindProp', specifier: 'ui/dom/bindProp', alias: '$$bindProp' },
|
|
55
|
+
{ name: 'bindableProp', specifier: 'ui/dom/bindableProp', alias: '$$bindableProp' },
|
|
50
56
|
{ name: 'spreadAttrs', specifier: 'ui/dom/spreadAttrs', alias: '$$spreadAttrs' },
|
|
51
57
|
{ name: 'readCall', specifier: 'ui/dom/readCall', alias: '$$readCall' },
|
|
52
|
-
{ name: '
|
|
58
|
+
{ name: 'readCell', specifier: 'ui/dom/readCell', alias: '$$readCell' },
|
|
59
|
+
{ name: 'cellPending', specifier: 'ui/dom/cellPending', alias: '$$cellPending' },
|
|
60
|
+
{ name: 'withPath', specifier: 'ui/runtime/withPath', alias: '$$withPath' },
|
|
61
|
+
{ name: 'settleAsyncCells', specifier: 'ui/settleAsyncCells', alias: '$$settleAsyncCells' },
|
|
62
|
+
{ name: 'flight', specifier: 'ui/flight', alias: '$$flight' },
|
|
63
|
+
{
|
|
64
|
+
name: 'isolateCellBarrier',
|
|
65
|
+
specifier: 'ui/isolateCellBarrier',
|
|
66
|
+
alias: '$$isolateCellBarrier',
|
|
67
|
+
},
|
|
68
|
+
{ name: 'renderPath', specifier: 'ui/runtime/renderPath', alias: '$$renderPath' },
|
|
69
|
+
{
|
|
70
|
+
name: 'finalizeStreamedChildren',
|
|
71
|
+
specifier: 'ui/finalizeStreamedChildren',
|
|
72
|
+
alias: '$$finalizeStreamedChildren',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'mutateDocContainer',
|
|
76
|
+
specifier: 'ui/dom/mutateDocContainer',
|
|
77
|
+
alias: '$$mutateDocContainer',
|
|
78
|
+
},
|
|
53
79
|
{ name: 'hydrate', specifier: 'ui/dom/hydrate', alias: '$$hydrate' },
|
|
54
80
|
{ name: 'escapeKey', specifier: 'ui/runtime/escapeKey', alias: '$$escapeKey' },
|
|
55
81
|
{ name: 'nextBlockId', specifier: 'ui/runtime/nextBlockId', alias: '$$nextBlockId' },
|
|
82
|
+
{ name: 'blockId', specifier: 'ui/runtime/blockId', alias: '$$blockId' },
|
|
56
83
|
{
|
|
57
84
|
name: 'enterRenderPass',
|
|
58
85
|
specifier: 'ui/runtime/enterRenderPass',
|
|
@@ -4,8 +4,11 @@ import { isLayoutFile } from '../../shared/isLayoutFile.ts'
|
|
|
4
4
|
import { messageFromError } from '../../shared/messageFromError.ts'
|
|
5
5
|
import { AbideCompileError } from './AbideCompileError.ts'
|
|
6
6
|
import { compileModule } from './compileModule.ts'
|
|
7
|
+
import type { ShadowProgram } from './createShadowProgram.ts'
|
|
8
|
+
import { interpolationClassifierForRoot } from './interpolationClassifierForRoot.ts'
|
|
7
9
|
import { nearestProjectRoot } from './nearestProjectRoot.ts'
|
|
8
10
|
import { offsetToLineColumn } from './offsetToLineColumn.ts'
|
|
11
|
+
import { seedTypeClassifierForRoot } from './seedTypeClassifierForRoot.ts'
|
|
9
12
|
|
|
10
13
|
/*
|
|
11
14
|
Bun plugin that loads `.abide` single-file components: compiles each to the ES
|
|
@@ -38,11 +41,25 @@ export const abideUiPlugin: BunPlugin = {
|
|
|
38
41
|
/* Scoped CSS keyed by its virtual specifier, filled as each `.abide` loads and
|
|
39
42
|
read back when Bun resolves the matching `abide-style:` import (browser only). */
|
|
40
43
|
const cssByVirtual = new Map<string, string>()
|
|
44
|
+
/* One WARM shadow program per project root (ADR-0019, Stage B): the checker +
|
|
45
|
+
per-file mappings type-directed lowering needs, built lazily on first `.abide`
|
|
46
|
+
in a root and reused for every component there — not rebuilt per module. */
|
|
47
|
+
const shadowByRoot = new Map<string, ShadowProgram | undefined>()
|
|
41
48
|
|
|
42
49
|
build.onLoad({ filter: /\.abide$/ }, async (args) => {
|
|
43
50
|
const source = await Bun.file(args.path).text()
|
|
44
|
-
const
|
|
51
|
+
const root = nearestProjectRoot(args.path, process.cwd())
|
|
52
|
+
const moduleId = relative(root, args.path)
|
|
45
53
|
const isLayout = isLayoutFile(args.path)
|
|
54
|
+
/* The classifier for this file over its root's warm shadow program. Fail-open:
|
|
55
|
+
a program that can't build (or a file with no shadow) yields `undefined`, so
|
|
56
|
+
the module compiles exactly as before. */
|
|
57
|
+
const classify = interpolationClassifierForRoot(shadowByRoot, root, args.path)
|
|
58
|
+
/* The seed classifier for this file over the SAME warm shadow program (ADR-0023):
|
|
59
|
+
reuses the `shadowByRoot` cache `classify` just warmed, so no second program is
|
|
60
|
+
built. Fail-open identically — a no-marker `computed(seed)` routes by type when
|
|
61
|
+
it resolves, else by the `isBareCallComputed` syntax heuristic. */
|
|
62
|
+
const seedClassify = seedTypeClassifierForRoot(shadowByRoot, root, args.path)
|
|
46
63
|
/* Bun frames a plugin throw at `<file>:0` regardless of the real spot, so
|
|
47
64
|
carry the component path + resolved line:col in the message — otherwise a
|
|
48
65
|
control-flow / compile error reads as `:0` and (in deep imports) can look
|
|
@@ -66,7 +83,7 @@ export const abideUiPlugin: BunPlugin = {
|
|
|
66
83
|
the styles come from the analysis `compileModule` already ran, so the loader no
|
|
67
84
|
longer re-analyzes the source just to recover them. */
|
|
68
85
|
const { code, styles } = compileAbide(() =>
|
|
69
|
-
compileModule(source, { isLayout, moduleId }),
|
|
86
|
+
compileModule(source, { isLayout, moduleId, classify, seedClassify }),
|
|
70
87
|
)
|
|
71
88
|
/* Browser build with `<style>`(s): concatenate every scoped block's CSS and
|
|
72
89
|
pull it into the bundle via one virtual import, keyed by `moduleId` so the
|