@abide/abide 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +70 -52
- package/CHANGELOG.md +177 -0
- package/README.md +12 -7
- 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,36 @@
|
|
|
1
|
+
import { reviveWireField } from './reviveWireField.ts'
|
|
2
|
+
import type { OutputWirePlan } from './types/OutputWirePlan.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The type-directed wire DECODE step for a rpc success response (ADR-0029 output path) — the
|
|
6
|
+
response-side sibling of parseArgs' input revival. The server encoded a handler's structured
|
|
7
|
+
return into honest JSON (`wireJsonReplacer`): a `Set` → array, a `Map` → `[K,V]` entries, a
|
|
8
|
+
`bigint` → digit string, a `Date` → ISO string. A wire array is ambiguous (a real `T[]` vs an
|
|
9
|
+
encoded `Set<T>`), so the abide client revives from the DECLARED kind the warm server program baked
|
|
10
|
+
onto the stub (`OutputWirePlan`), applied to a DECODED response body here. Only the fields named in
|
|
11
|
+
the plan are touched; a genuine array / scalar the plan omits is left as-is.
|
|
12
|
+
|
|
13
|
+
Mutates the decoded body in place — the value is call-private (a fresh JSON.parse result or a
|
|
14
|
+
freshly cloned warm value), never shared. Fail-open at every branch: no plan, a non-object body, or
|
|
15
|
+
an unrevivable field value keeps the honest-JSON form so a type/runtime mismatch degrades visibly
|
|
16
|
+
rather than throwing. Top-level fields only; a structured value nested deeper is not descended into
|
|
17
|
+
(deferred, ADR-0029).
|
|
18
|
+
*/
|
|
19
|
+
export function reviveWireOutput(value: unknown, plan: OutputWirePlan | undefined): unknown {
|
|
20
|
+
if (plan === undefined) {
|
|
21
|
+
return value
|
|
22
|
+
}
|
|
23
|
+
/* Only a plain object carries the named fields; a top-level array/scalar/null has none. */
|
|
24
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
25
|
+
return value
|
|
26
|
+
}
|
|
27
|
+
const record = value as Record<string, unknown>
|
|
28
|
+
for (const key in plan) {
|
|
29
|
+
const fieldPlan = plan[key]
|
|
30
|
+
if (fieldPlan === undefined || !(key in record)) {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
record[key] = reviveWireField(record[key], fieldPlan)
|
|
34
|
+
}
|
|
35
|
+
return value
|
|
36
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createRpcServerProgram, type RpcServerProgram } from './createRpcServerProgram.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Warm per-root accessor for the rpc server program (ADR-0025 D1), mirroring the UI shadow's
|
|
5
|
+
`interpolationClassifierForRoot`: the first rpc transform in a root builds the `ts.Program`,
|
|
6
|
+
every later transform (streaming/method query) reuses it. Fails open in two layers — a
|
|
7
|
+
program that can't build stores `undefined` so the root is not retried and every query falls
|
|
8
|
+
back to the char-scan (each query method also catches per-query throws). The cache lives
|
|
9
|
+
in the resolver plugin's `setup` closure, so it persists across a whole build and is dropped
|
|
10
|
+
between plugin instances.
|
|
11
|
+
*/
|
|
12
|
+
export function rpcServerForRoot(
|
|
13
|
+
cache: Map<string, RpcServerProgram | undefined>,
|
|
14
|
+
cwd: string,
|
|
15
|
+
rpcDir: string,
|
|
16
|
+
): RpcServerProgram | undefined {
|
|
17
|
+
if (!cache.has(cwd)) {
|
|
18
|
+
try {
|
|
19
|
+
cache.set(cwd, createRpcServerProgram(cwd, rpcDir))
|
|
20
|
+
} catch {
|
|
21
|
+
cache.set(cwd, undefined)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return cache.get(cwd)
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { Glob } from 'bun'
|
|
3
|
+
import { fileName } from './fileName.ts'
|
|
4
|
+
import type { PagesScan } from './types/PagesScan.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Walks src/ui/pages once and classifies each `.abide` leaf by filename: a
|
|
8
|
+
`page.abide` is a route (its URL is the folder path), a `layout.abide` is a
|
|
9
|
+
layout that wraps every page at or below its folder (keyed by the same folder
|
|
10
|
+
URL). Any other `.abide` file (a shared component) is ignored here — free to
|
|
11
|
+
live anywhere and be imported relatively. Shared by the resolver plugin's
|
|
12
|
+
pages/layouts manifests and generateDeclarations' routes.d.ts so the two never
|
|
13
|
+
diverge on what counts as a route.
|
|
14
|
+
*/
|
|
15
|
+
export async function scanPages(pagesDir: string): Promise<PagesScan> {
|
|
16
|
+
if (!existsSync(pagesDir)) {
|
|
17
|
+
return { pageFiles: [], layoutFiles: [] }
|
|
18
|
+
}
|
|
19
|
+
const allFiles = await Array.fromAsync(new Glob('**/*.abide').scan({ cwd: pagesDir }))
|
|
20
|
+
const leafIs = (name: string) => (file: string) => fileName(file) === name
|
|
21
|
+
return {
|
|
22
|
+
pageFiles: allFiles.filter(leafIs('page.abide')),
|
|
23
|
+
layoutFiles: allFiles.filter(leafIs('layout.abide')),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hasSeedableRequest } from './hasSeedableRequest.ts'
|
|
2
2
|
import type { CacheEntry } from './types/CacheEntry.ts'
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
The one store-filter predicate selecting which cache entries are candidates for a
|
|
6
|
-
warm snapshot: settled AND carrying a
|
|
7
|
-
|
|
8
|
-
(serializeCacheSnapshot, the renderer's render-return and
|
|
9
|
-
calls this instead of re-spelling the guard, so they
|
|
6
|
+
warm snapshot: settled AND carrying a seedable wire request (any method), so an
|
|
7
|
+
inline call hydrates warm on the client instead of re-firing after render. Every
|
|
8
|
+
selection site (serializeCacheSnapshot, the renderer's render-return and
|
|
9
|
+
streaming-drain filters) calls this instead of re-spelling the guard, so they
|
|
10
|
+
cannot drift.
|
|
10
11
|
|
|
11
|
-
Composed from `
|
|
12
|
+
Composed from `hasSeedableRequest` — the request half snapshotEntryFromCache
|
|
12
13
|
also gates on. snapshotEntryFromCache itself uses ONLY the request half (not
|
|
13
14
|
`settled`): the streaming-drain path hands it still-pending entries and awaits
|
|
14
15
|
them, so it can't require `settled` up front.
|
|
@@ -18,5 +19,5 @@ awaiting its body. The async response-level half (non-streaming + warmable kind)
|
|
|
18
19
|
lives in snapshotEntryFromCache, reading the resolved Response.
|
|
19
20
|
*/
|
|
20
21
|
export function snapshotShippable(entry: CacheEntry): boolean {
|
|
21
|
-
return entry.settled === true &&
|
|
22
|
+
return entry.settled === true && hasSeedableRequest(entry)
|
|
22
23
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createResolverSlot } from './createResolverSlot.ts'
|
|
2
|
+
import type { StreamedCells } from './types/StreamedCells.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The active streamed-cells slot — mirrors `resolvedCellsSlot`/`pendingAsyncCellsSlot`. The server
|
|
6
|
+
entry installs an ALS-backed resolver (a fresh list per request, so concurrent SSR renders never
|
|
7
|
+
mix their streamed values); with no resolver a single fallback list is created lazily so isolated
|
|
8
|
+
tests work without booting the runtime. `createAsyncCell` pushes a `{ key, promise }` when a
|
|
9
|
+
STREAMING cell constructs server-side; the page renderer drains it during the stream, awaiting each
|
|
10
|
+
promise and streaming an `__abideResolve({ cellKey, value })` chunk (ADR-0035).
|
|
11
|
+
*/
|
|
12
|
+
export const streamedCellsSlot = createResolverSlot<StreamedCells>(() => ({
|
|
13
|
+
entries: [],
|
|
14
|
+
}))
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { streamResponse } from './streamResponse.ts'
|
|
2
|
-
import type {
|
|
2
|
+
import type { NamedAsyncIterable } from './types/NamedAsyncIterable.ts'
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
|
-
Builds the
|
|
5
|
+
Builds the NamedAsyncIterable returned by `fn.stream(args)`. The carried
|
|
6
6
|
`name` is the cache-style key for (method, url, args) so tail()
|
|
7
7
|
dedupes multiple readers of identical args into one underlying
|
|
8
8
|
fetch. The fetch is deferred until the first iterator pull so
|
|
9
|
-
constructing the
|
|
9
|
+
constructing the NamedAsyncIterable (which happens on every $derived
|
|
10
10
|
re-evaluation) doesn't open a connection — tail()'s registry
|
|
11
11
|
short-circuits the second instance before it iterates.
|
|
12
12
|
*/
|
|
13
13
|
export function subscribableFromResponse<T>(
|
|
14
14
|
name: string,
|
|
15
15
|
fetchResponse: () => Promise<Response>,
|
|
16
|
-
):
|
|
16
|
+
): NamedAsyncIterable<T> {
|
|
17
17
|
return {
|
|
18
18
|
name,
|
|
19
19
|
[Symbol.asyncIterator]() {
|
|
@@ -29,7 +29,7 @@ re-derive by scanning.
|
|
|
29
29
|
const lifecycle = createLifecycleChannel()
|
|
30
30
|
|
|
31
31
|
/*
|
|
32
|
-
Installs the prober the shared probes call for a
|
|
32
|
+
Installs the prober the shared probes call for a NamedAsyncIterable (probeRegistries →
|
|
33
33
|
tailProbeSlot). Register-on-consume: an entry exists only while something is
|
|
34
34
|
consuming the stream, exactly as a cache entry exists only while a call is
|
|
35
35
|
retained. A name with no entry reads as "no value yet" (pending) without opening
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Internal slot the browser tail() registry registers its prober into, so the
|
|
3
|
-
shared probes (pending / refreshing) can answer for
|
|
3
|
+
shared probes (pending / refreshing) can answer for NamedAsyncIterables without
|
|
4
4
|
shared/ importing browser/. The prober taps the registry's lifecycle channel
|
|
5
5
|
(reactive inside $derived / $effect) and reports one stream by source name —
|
|
6
6
|
spanning its latest-wins and window entries — or, with no name, whether any
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ASYNC_CELL } from '../ASYNC_CELL.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
A read-only async cell — what `computed(await …)` / `computed(getStream())` produce.
|
|
5
|
+
It *tracks* an async source (a promise it unwrapped, or a NamedAsyncIterable whose
|
|
6
|
+
frames it follows) rather than holding a value. Unlike sync `Computed<T>` it has NO
|
|
7
|
+
`.value`; it is read through the probe family, always method-form — `peek()` (the
|
|
8
|
+
retained value or latest frame, or `undefined`), `pending()` (no value yet),
|
|
9
|
+
`refreshing()` (a value held while a fresher source is in flight), `error()` (the
|
|
10
|
+
last rejection), `refresh()` (re-invoke the source). The standalone probes route
|
|
11
|
+
here too: `peek(cell)` ≡ `cell.peek()`. Branded so those probes recognise it.
|
|
12
|
+
*/
|
|
13
|
+
export interface AsyncComputed<T> {
|
|
14
|
+
readonly [ASYNC_CELL]: true
|
|
15
|
+
peek(): T | undefined
|
|
16
|
+
pending(): boolean
|
|
17
|
+
refreshing(): boolean
|
|
18
|
+
error(): unknown
|
|
19
|
+
refresh(): void
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AsyncComputed } from './AsyncComputed.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
A writable async cell — what `linked(await …)` / `linked(getStream())` produce. Same
|
|
5
|
+
probe surface as `AsyncComputed<T>` plus `set(value)`, following the normal `linked`
|
|
6
|
+
write rule: a local write latches until the next *reseed* (a dependency change that
|
|
7
|
+
produces a new source). An arriving frame or a settling promise updates the value only
|
|
8
|
+
while unwritten, so a high-frequency stream never clobbers an in-progress edit; a
|
|
9
|
+
reseed clears the write and snaps the cell back to the live source.
|
|
10
|
+
*/
|
|
11
|
+
export interface AsyncState<T> extends AsyncComputed<T> {
|
|
12
|
+
set(value: T): void
|
|
13
|
+
}
|
|
@@ -37,20 +37,20 @@ into `__SSR__`; ones still pending were consumed via `{#await}` (render emitted
|
|
|
37
37
|
the pending branch without blocking) and stream a resolve chunk instead.
|
|
38
38
|
|
|
39
39
|
`refreshing` flips true while this entry is reloading data it already held —
|
|
40
|
-
either
|
|
40
|
+
either a keep-stale refetch (stale value still visible) or the default
|
|
41
41
|
drop-then-reload (the prior entry was invalidated and dropped, this is its
|
|
42
42
|
replacement read). It backs refreshing(), distinguishing a reload from a
|
|
43
43
|
first-ever load; cleared when the read settles.
|
|
44
44
|
|
|
45
|
-
`invalidation` holds the entry's
|
|
45
|
+
`invalidation` holds the entry's refetch policy: the refetch thunk (the call
|
|
46
46
|
captured with its args) plus its optional throttle/debounce window and runtime
|
|
47
47
|
timer state, so invalidate() keeps the stale value and revalidates this key —
|
|
48
48
|
rate-limited by the window — instead of dropping the entry. Set at registration
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
for a smart retained read (SWR unconditional for a replayable read — ADR-0020) or
|
|
50
|
+
a producer declaring a throttle/debounce window, or attached by a later read on an
|
|
51
|
+
entry that lacks it (hydrated snapshot entries always start without one) — first
|
|
52
|
+
wins. An armed `timer` is cleared if the entry is evicted, so a dead key never
|
|
53
|
+
refetches.
|
|
54
54
|
*/
|
|
55
55
|
export type CacheEntry = {
|
|
56
56
|
key: string
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Options for cache(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
Options for the explicit `cache(producer, args, options?)` call over a plain
|
|
3
|
+
producer, and the value-shape of an endpoint's `cache` policy (CachePolicy adds
|
|
4
|
+
only the arg-derived `tags` function form on top). A remote rpc has no options
|
|
5
|
+
argument any more — its policy lives on the definition (ADR-0020); this type is
|
|
6
|
+
the producer's only policy home. The key is always auto-derived
|
|
7
|
+
(producer-reference+args): hoist a producer to a stable reference to share its
|
|
8
|
+
entry across calls.
|
|
9
|
+
|
|
10
|
+
`ttl` is the milliseconds-past-resolve the entry stays live: 0 = dedupe only
|
|
11
|
+
(entry dropped once the promise settles — in-flight coalescing and pending()
|
|
12
|
+
visibility, nothing retained), any other number = TTL. Omitted = forever for a
|
|
13
|
+
producer.
|
|
14
|
+
|
|
12
15
|
`tags` is an array of free-form labels grouping unrelated calls so one
|
|
13
16
|
`cache.invalidate({ tags })` drops every entry sharing any of them — list
|
|
14
17
|
multiple when a call belongs to multiple invalidation groups. A unique tag (e.g.
|
|
@@ -17,39 +20,22 @@ a uuid) shared by a set of calls gives them their own private invalidation group
|
|
|
17
20
|
`shared` opts the entry into the process-level store instead of the default
|
|
18
21
|
request-scoped one (server) — a store that outlives every request. It selects
|
|
19
22
|
the store only; it does NOT retain (pair it with `ttl` to memoise across
|
|
20
|
-
requests). The shared store is keyed by
|
|
21
|
-
not put per-user data in it — it would be served to other users. Omit
|
|
22
|
-
for per-request data. Write only `shared: true`; there is no `false`
|
|
23
|
-
the client there is a single tab store, so the flag is a no-op there.
|
|
24
|
-
|
|
25
|
-
`swr` is stale-while-revalidate: it changes what a `cache.invalidate` hit does
|
|
26
|
-
to this key. Without it, an invalidate drops the entry and the next read shows
|
|
27
|
-
`pending()`. With it, the entry is kept and refetched in the background — the
|
|
28
|
-
existing (stale) value stays visible and `refreshing()` reports the in-flight
|
|
29
|
-
reload — so the reader never blanks. It governs only the refetch-after-invalidate;
|
|
30
|
-
the first fetch and arg-change fetches stay immediate regardless.
|
|
31
|
-
|
|
32
|
-
`swr: true` refetches immediately on every invalidate. An optional window
|
|
33
|
-
coalesces a burst (e.g. a socket spraying `cache.invalidate`) into far fewer
|
|
34
|
-
calls: `swr: { throttle: N }` refetches on the leading edge then at most once
|
|
35
|
-
per N ms while invalidations keep arriving; `swr: { debounce: N }` refetches
|
|
36
|
-
only after N ms of quiet. `swr` declares the call safe to re-run unprompted:
|
|
37
|
-
cache() throws at wrap time on throttle+debounce set at once, on ttl: 0 (nothing
|
|
38
|
-
retained, nothing to revalidate), and on a non-replayable remote method
|
|
39
|
-
(replaying a write is a state change disguised as a refresh). Producers are
|
|
40
|
-
uncheckable — set `swr` only on a producer that is a pure read.
|
|
23
|
+
requests). The shared store is keyed by producer-reference+args, never by user,
|
|
24
|
+
so do not put per-user data in it — it would be served to other users. Omit
|
|
25
|
+
`shared` for per-request data. Write only `shared: true`; there is no `false`
|
|
26
|
+
form. On the client there is a single tab store, so the flag is a no-op there.
|
|
41
27
|
|
|
42
|
-
`throttle`/`debounce`
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
`throttle`/`debounce` arm the keep-stale-and-revalidate refetch clock for a
|
|
29
|
+
producer: with one set, a `cache.invalidate` hit keeps the stale value visible
|
|
30
|
+
and coalesces a background refetch (leading-edge-then-coalesce, or
|
|
31
|
+
fire-after-quiet respectively) instead of dropping the entry. Set one, not both.
|
|
32
|
+
SWR is otherwise unconditional for replayable remote reads; there is no `swr`
|
|
33
|
+
toggle.
|
|
47
34
|
*/
|
|
48
35
|
export type CacheOptions = {
|
|
49
36
|
ttl?: number
|
|
50
37
|
tags?: string[]
|
|
51
38
|
shared?: boolean
|
|
52
|
-
swr?: boolean | { throttle?: number; debounce?: number }
|
|
53
39
|
throttle?: number
|
|
54
40
|
debounce?: number
|
|
55
41
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Endpoint cache policy, declared once on the rpc definition (ADR-0020). Same value
|
|
3
|
+
shape as the producer-side `CacheOptions` (`ttl` / `tags` / `throttle` / `debounce`
|
|
4
|
+
/ `shared`, no `swr` — SWR is unconditional for replayable reads), except `tags`
|
|
5
|
+
also accepts an arg-derived function so the group can vary by call. That makes the
|
|
6
|
+
type generic over the rpc's `Args`: `(args) => ['rates:' + args.base]` is typed
|
|
7
|
+
against the endpoint's own argument shape.
|
|
8
|
+
|
|
9
|
+
- `ttl` — retention/staleness in ms. Default Infinity: the entry is retained for its
|
|
10
|
+
store's lifetime — the request on the server (a non-shared read dies with the request,
|
|
11
|
+
regardless of ttl), the tab on the client (until invalidate/refresh). An explicit ttl
|
|
12
|
+
is a hard expiry on the server, a staleness deadline (SWR) on the client.
|
|
13
|
+
- `tags` — static invalidation-group labels, or a function of the call's args.
|
|
14
|
+
- `throttle` / `debounce` — the background refetch clock (set one, not both).
|
|
15
|
+
- `shared` — opt the entry into the process-level store (never per-user data — the store
|
|
16
|
+
is keyed by method+url+args, not by user). With the default Infinity ttl this memoises
|
|
17
|
+
across requests; an explicit ttl bounds it. A client no-op (one tab store).
|
|
18
|
+
*/
|
|
19
|
+
export type CachePolicy<Args> = {
|
|
20
|
+
ttl?: number
|
|
21
|
+
tags?: string[] | ((args: Args) => string[])
|
|
22
|
+
throttle?: number
|
|
23
|
+
debounce?: number
|
|
24
|
+
shared?: boolean
|
|
25
|
+
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HttpMethod } from './HttpMethod.ts'
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
Wire format for a single cached response shipped from SSR to client hydration.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
survive a JSON round-trip.
|
|
5
|
+
Any entry called inline during render (any method) with a textual Content-Type is
|
|
6
|
+
emitted, so the client hydrates warm instead of re-firing the call after render;
|
|
7
|
+
binary bodies don't survive a JSON round-trip. The seeded value is read warm once
|
|
8
|
+
— it is never auto-replayed (that stays GET-only, see REPLAYABLE_METHODS).
|
|
8
9
|
*/
|
|
9
10
|
export type CacheSnapshotEntry = {
|
|
10
11
|
key: string
|
|
11
12
|
url: string
|
|
12
|
-
method:
|
|
13
|
+
method: HttpMethod
|
|
13
14
|
status: number
|
|
14
15
|
statusText: string
|
|
15
16
|
headers: Array<[string, string]>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Status-keyed map of a handler's typed-error data schemas (ADR-0030), baked at build time from the
|
|
3
|
+
handler's `error.typed(name, status, schema?)` return branches. Each key is an HTTP status, each
|
|
4
|
+
value the error's `data` type projected to JSON Schema (multiple errors sharing a status combine
|
|
5
|
+
under `anyOf`; a nullary error with no data contributes a bare `{}`). Feeds the OpenAPI
|
|
6
|
+
`responses[status]` surface — the error-branch sibling of the success `outputJsonSchema`.
|
|
7
|
+
*/
|
|
8
|
+
export type ErrorJsonSchemas = Record<number, Record<string, unknown>>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WireKind } from './WireKind.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The build-time wire codec plan for a rpc's input args, keyed by field name (ADR-0028 for the
|
|
5
|
+
scalar `number`/`boolean` kinds, generalized by ADR-0029 to the structured `date`/`bigint`/`set`/
|
|
6
|
+
`map` kinds). A GET/form request delivers every field as a string, and a non-abide JSON client
|
|
7
|
+
sends a `Date` as an ISO string / a `Set` as an array, so parseArgs uses this plan to revive each
|
|
8
|
+
declared field into the typed value the input schema expects — before validation, on the exact
|
|
9
|
+
fields the author declared. String and other-typed fields are never listed, so a value that merely
|
|
10
|
+
looks numeric (an id, a zip code, `'1.0'`) stays a string. The plan is derived by the warm server
|
|
11
|
+
program from the real type graph and stamped into the server `defineRpc` call; a field is absent
|
|
12
|
+
when its type is anything but a recognized WireKind (or an array / optional thereof), so the codec
|
|
13
|
+
is precise and fail-open — no plan ⇒ today's string-through behavior. The abide client's own
|
|
14
|
+
POST/PUT/PATCH body already round-trips these kinds via ref-json; the plan covers the query-string
|
|
15
|
+
and non-abide-client paths where ref-json isn't in play.
|
|
16
|
+
*/
|
|
17
|
+
export type InputCoercion = Record<string, WireKind>
|
|
@@ -20,7 +20,7 @@ HTTP/SSE read face); one-shot rpc streams omit it. Implementers must signal
|
|
|
20
20
|
`hooks.replayed` in-band once the seed portion is delivered (even when
|
|
21
21
|
empty) so a seeded reader can commit its window atomically; see TailHooks.
|
|
22
22
|
*/
|
|
23
|
-
export interface
|
|
23
|
+
export interface NamedAsyncIterable<T> extends AsyncIterable<T> {
|
|
24
24
|
readonly name: string
|
|
25
25
|
tail?(count: number, hooks?: TailHooks): AsyncIterable<T>
|
|
26
26
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WireKind } from './WireKind.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The build-time wire codec plan for a rpc's success RESPONSE body, keyed by top-level field name
|
|
5
|
+
(ADR-0029 output path — the response-side sibling of `InputCoercion`). The server serializes a
|
|
6
|
+
handler's structured return through a value-directed replacer (`wireJsonReplacer`), so a `Set`
|
|
7
|
+
crosses as a JSON array, a `Map` as an `[K,V]` entries array, and a `bigint` as a digit string —
|
|
8
|
+
honest JSON a non-abide client still reads. But a wire array is ambiguous (a real `T[]` vs an
|
|
9
|
+
encoded `Set<T>`), so an abide client needs the DECLARED kind to revive: this plan carries only the
|
|
10
|
+
structured kinds (`date`/`bigint`/`set`/`map`) the client proxy revives from a decoded response body.
|
|
11
|
+
It is resolved by the warm server program from the handler's success-body type and baked onto the
|
|
12
|
+
CLIENT `remoteProxy` stub (next to the live `schemas`, ADR-0022 D2). A field absent from the plan (a
|
|
13
|
+
genuine array, a scalar, a `number`/`boolean` JSON already carries) is left untouched. Fail-open —
|
|
14
|
+
no plan / an unrevivable value keeps the honest-JSON form, never a throw. Top-level fields only in
|
|
15
|
+
this increment; a structured value nested inside another value is not descended into.
|
|
16
|
+
*/
|
|
17
|
+
export type OutputWirePlan = Record<string, WireKind>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The per-request list of in-flight async-cell promises registered during an SSR
|
|
3
|
+
render. `createAsyncCell` pushes each settling promise here (server-side only);
|
|
4
|
+
`settleAsyncCells` drains and awaits them between a component's cell declarations
|
|
5
|
+
and its template, so the resolved values bake into the first-pass HTML (ADR-0019
|
|
6
|
+
Tier-2) instead of the template peeking `undefined`.
|
|
7
|
+
*/
|
|
8
|
+
export type PendingAsyncCells = {
|
|
9
|
+
promises: Promise<unknown>[]
|
|
10
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { CachePolicy } from './CachePolicy.ts'
|
|
1
2
|
import type { HttpMethod } from './HttpMethod.ts'
|
|
2
3
|
import type { RemoteCallable } from './RemoteCallable.ts'
|
|
4
|
+
import type { StreamPolicy } from './StreamPolicy.ts'
|
|
3
5
|
|
|
4
6
|
/*
|
|
5
7
|
Bare-response remote function — same call shape as RemoteFunction but
|
|
@@ -11,4 +13,8 @@ implement custom error handling can opt out of the decode.
|
|
|
11
13
|
export type RawRemoteFunction<Args> = RemoteCallable<Args, Response> & {
|
|
12
14
|
readonly method: HttpMethod
|
|
13
15
|
readonly url: string
|
|
16
|
+
/* Endpoint cache/stream policy (ADR-0020), stamped on this variant too so readThrough can
|
|
17
|
+
read it whether the caller passed `fn` or `fn.raw`. Same value as the decoded sibling's. */
|
|
18
|
+
readonly cache?: CachePolicy<Args>
|
|
19
|
+
readonly stream?: StreamPolicy
|
|
14
20
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { NamedAsyncIterable } from './NamedAsyncIterable.ts'
|
|
1
2
|
import type { RpcOptions } from './RpcOptions.ts'
|
|
2
|
-
import type { Subscribable } from './Subscribable.ts'
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
Call signature shared by RemoteFunction and RawRemoteFunction. The base
|
|
@@ -10,24 +10,25 @@ optional-arg signature lets call sites write `fn()` instead of
|
|
|
10
10
|
stays callable while `Args` is still generic (cache() invokes producers
|
|
11
11
|
before `Args` resolves). FormData is the multipart upload escape hatch —
|
|
12
12
|
see RemoteFunction. The trailing `Opts` differs by variant: the smart bare
|
|
13
|
-
call (RemoteFunction)
|
|
14
|
-
|
|
13
|
+
call (RemoteFunction) takes no second argument at all (`Opts = never` — all
|
|
14
|
+
cache/stream policy lives on the rpc definition per ADR-0020), while `.raw`
|
|
15
|
+
(RawRemoteFunction) carries per-call transport options (RpcOptions:
|
|
15
16
|
signal/keepalive/priority/cache/headers). The server ignores either, so the
|
|
16
17
|
callable stays isomorphic.
|
|
17
18
|
|
|
18
19
|
A streaming `Resolved` (an `AsyncIterable<Frame>`, branded by jsonl()/sse()) makes
|
|
19
|
-
the bare call return a `
|
|
20
|
+
the bare call return a `NamedAsyncIterable<Frame>` synchronously — the iterable IS the
|
|
20
21
|
value, consumed by `for await` or `state(fn(args))`, no `.stream()`. Every other
|
|
21
22
|
`Resolved` stays `Promise<Resolved>`. `await`-ing a streaming call is a compile
|
|
22
|
-
error (a
|
|
23
|
+
error (a NamedAsyncIterable is not awaitable), which replaces the old runtime guard.
|
|
23
24
|
*/
|
|
24
25
|
/* `[Resolved] extends [never]` first: a handler that only ever returns error() resolves to
|
|
25
26
|
`never`, and `never extends AsyncIterable` is vacuously true — without this guard its call
|
|
26
|
-
would mistype as a
|
|
27
|
+
would mistype as a NamedAsyncIterable. A real streaming rpc's `Resolved` is `AsyncIterable<Frame>`. */
|
|
27
28
|
type CallResult<Resolved> = [Resolved] extends [never]
|
|
28
29
|
? Promise<Resolved>
|
|
29
30
|
: Resolved extends AsyncIterable<infer Frame>
|
|
30
|
-
?
|
|
31
|
+
? NamedAsyncIterable<Frame>
|
|
31
32
|
: Promise<Resolved>
|
|
32
33
|
|
|
33
34
|
export type RemoteCallable<Args, Resolved, Opts = RpcOptions> = ((
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import type { CachePolicy } from './CachePolicy.ts'
|
|
1
2
|
import type { ClientFlags } from './ClientFlags.ts'
|
|
2
3
|
import type { ErrorSpec } from './ErrorSpec.ts'
|
|
3
4
|
import type { HttpMethod } from './HttpMethod.ts'
|
|
4
|
-
import type { Outbox } from './Outbox.ts'
|
|
5
5
|
import type { RawRemoteFunction } from './RawRemoteFunction.ts'
|
|
6
6
|
import type { RemoteCallable } from './RemoteCallable.ts'
|
|
7
7
|
import type { RpcError } from './RpcError.ts'
|
|
8
8
|
import type { RpcErrorGuard } from './RpcErrorGuard.ts'
|
|
9
|
-
import type {
|
|
9
|
+
import type { StreamPolicy } from './StreamPolicy.ts'
|
|
10
10
|
|
|
11
11
|
/*
|
|
12
12
|
Remote function reference produced by GET/POST/... inside an `$rpc/**`
|
|
@@ -21,7 +21,7 @@ method, same url, same args, no decode. Pass `fn.raw` to cache() to
|
|
|
21
21
|
memoise raw Responses against the same cache key as `fn` (both share one
|
|
22
22
|
stored entry — the decode just happens on the way out for callers of
|
|
23
23
|
`fn`). A streaming handler (jsonl()/sse()) makes the bare call return a
|
|
24
|
-
`
|
|
24
|
+
`NamedAsyncIterable<Frame>` directly (the iterable IS the value) — consume it with
|
|
25
25
|
`for await (… of fn(args))` or `state(fn(args))`; there is no `.stream()`, and
|
|
26
26
|
`await`-ing a streaming call is a compile error. For sustained broadcast /
|
|
27
27
|
pub-sub use the `abide/server/socket` primitive — HTTP rpc isn't the place for
|
|
@@ -44,23 +44,29 @@ export type RemoteFunction<
|
|
|
44
44
|
Args,
|
|
45
45
|
Return,
|
|
46
46
|
Errors extends ErrorSpec = Record<never, never>,
|
|
47
|
-
|
|
48
|
-
> = RemoteCallable<Args, Return, SmartReadOptions> & {
|
|
47
|
+
> = RemoteCallable<Args, Return, never> & {
|
|
49
48
|
readonly method: HttpMethod
|
|
50
49
|
readonly url: string
|
|
51
50
|
readonly clients: ClientFlags
|
|
52
51
|
readonly crossOrigin?: boolean
|
|
52
|
+
/* Endpoint cache/stream policy declared on the rpc definition (ADR-0020), stamped
|
|
53
|
+
here so the smart read (readThrough) reads it as the bottom policy layer. Ships to
|
|
54
|
+
the client too — it governs client cache behaviour and is behaviour, not secrets
|
|
55
|
+
(`shared` is a client no-op). Absent on a plainly-defined rpc (method defaults only). */
|
|
56
|
+
readonly cache?: CachePolicy<Args>
|
|
57
|
+
readonly stream?: StreamPolicy
|
|
53
58
|
readonly raw: RawRemoteFunction<Args>
|
|
54
59
|
fetch(request: Request): Promise<Response>
|
|
55
60
|
/* Type-guard a caught error against this rpc's declared `errors` (plus the framework
|
|
56
|
-
`'validation'`
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
`'validation'`): narrows `.kind` and, for a known kind, `.data` — the per-rpc
|
|
62
|
+
replacement for a global guard, since the error name → data type mapping lives in
|
|
63
|
+
the rpc's own spec. */
|
|
59
64
|
readonly isError: RpcErrorGuard<Errors>
|
|
60
65
|
/* Pre-bound selector sugar: `fn.pending(args?)` ≡ `pending(fn, args?)`, and likewise for
|
|
61
66
|
refreshing / refresh / peek — the rpc is the leading selector, bound in. The argument is
|
|
62
67
|
this rpc's typed `Args` (the by-args refinement); tags / cross-cutting selection stay on
|
|
63
|
-
the globals. The bare call `fn(args
|
|
68
|
+
the globals. The bare call `fn(args)` IS the cached read (was `fn.cache`) — all cache
|
|
69
|
+
policy lives on the definition, so there is no second options argument, and
|
|
64
70
|
`refresh(args?)` refetches keeping the stale value visible (was `invalidate`, which
|
|
65
71
|
dropped). `peek(args?)` reads the retained value synchronously (a streaming rpc peeks its
|
|
66
72
|
latest frame). `patch` is fetch-only, so it is omitted for a streaming rpc (a stream isn't
|
|
@@ -93,9 +99,4 @@ export type RemoteFunction<
|
|
|
93
99
|
: {
|
|
94
100
|
patch(args: Args | undefined, updater: (current: Return) => Return): void
|
|
95
101
|
patch(updater: (current: Return) => Return): void
|
|
96
|
-
})
|
|
97
|
-
into the return type so a DURABLE rpc's `.outbox` is the required queue face (no optional
|
|
98
|
-
chain). A non-durable rpc keeps it optional — assignable to a durable one everywhere a
|
|
99
|
-
bare `RemoteFunction<Args, Return>` slot (cache selectors, registries) is expected, since
|
|
100
|
-
required→optional widens cleanly and no call site had to learn the `Durable` bit. */ &
|
|
101
|
-
(Durable extends true ? { readonly outbox: Outbox<Args> } : { readonly outbox?: Outbox<Args> })
|
|
102
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The per-request list of async-cell values that RESOLVED during an SSR render, each keyed by the
|
|
3
|
+
cell's serialization-stable render-path id (`${scope.id}:${index}`). `createAsyncCell` pushes an
|
|
4
|
+
entry when its seed settles server-side; the page renderer stamps them into `__SSR__.cells`
|
|
5
|
+
(ref-json-encoded) so the client hydrates the cell WARM — reading the value instead of re-running
|
|
6
|
+
the seed (no refetch, no pending flash). The sibling of `PendingAsyncCells` (the barrier's
|
|
7
|
+
in-flight list); this one holds settled VALUES, read at render-return rather than awaited.
|
|
8
|
+
*/
|
|
9
|
+
export type ResolvedCells = {
|
|
10
|
+
entries: { key: string; value: unknown }[]
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The build-time success-body descriptor for an rpc, derived from the handler's return type by the
|
|
3
|
+
warm server program (ADR-0030). Mirrors `RpcHelper`'s `SuccessBody<R>` projection: the handler's
|
|
4
|
+
`Awaited<ReturnType>` union has its `TypedError` branches dropped, and each success
|
|
5
|
+
`TypedResponse<Body>` contributes its `Body`. `type` is that body rendered as a TS type string
|
|
6
|
+
(the union of the surviving branches). For a streaming endpoint (`jsonl()`/`sse()` →
|
|
7
|
+
`TypedResponse<AsyncIterable<Frame>>`) `streaming` is true and `type` is the per-FRAME type — the
|
|
8
|
+
AsyncIterable element — so a consumer describes one streamed item rather than the iterable itself.
|
|
9
|
+
The query fails open to undefined (no handler/body resolvable), matching every other server-program
|
|
10
|
+
query.
|
|
11
|
+
*/
|
|
12
|
+
export type ReturnBody = {
|
|
13
|
+
type: string
|
|
14
|
+
streaming: boolean
|
|
15
|
+
}
|