@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,28 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Loads the project's tsconfig (for lib/paths/baseUrl/moduleResolution — so the
|
|
5
|
+
server graph resolves imports the same way the app does) and forces type-check-only
|
|
6
|
+
overrides: `noEmit` (never write) and `skipLibCheck` (the warm server program only
|
|
7
|
+
queries handler return types, never reports diagnostics, so lib checking is dead
|
|
8
|
+
cost). Falls back to permissive defaults when no tsconfig is found. Distinct from
|
|
9
|
+
`loadShadowTsConfig` (the UI shadow's loader) so the server side carries no ui import.
|
|
10
|
+
*/
|
|
11
|
+
export function loadProjectTsConfig(cwd: string): ts.ParsedCommandLine {
|
|
12
|
+
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, 'tsconfig.json')
|
|
13
|
+
const base = configPath
|
|
14
|
+
? ts.parseJsonConfigFileContent(
|
|
15
|
+
ts.readConfigFile(configPath, ts.sys.readFile).config,
|
|
16
|
+
ts.sys,
|
|
17
|
+
cwd,
|
|
18
|
+
)
|
|
19
|
+
: { options: {}, fileNames: [], errors: [] }
|
|
20
|
+
return {
|
|
21
|
+
...base,
|
|
22
|
+
options: {
|
|
23
|
+
...base.options,
|
|
24
|
+
noEmit: true,
|
|
25
|
+
skipLibCheck: true,
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -36,7 +36,7 @@ the widest debugger support. Mutates and returns the same map.
|
|
|
36
36
|
*/
|
|
37
37
|
export function markFrameworkSourcesIgnored(map: SourceMap): SourceMap {
|
|
38
38
|
const ignored = (map.sources ?? []).reduce<number[]>((indices, source, index) => {
|
|
39
|
-
if (source
|
|
39
|
+
if (source?.includes(FRAMEWORK_LIB_PATH)) {
|
|
40
40
|
indices.push(index)
|
|
41
41
|
}
|
|
42
42
|
return indices
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { lenientDecode } from './lenientDecode.ts'
|
|
1
2
|
import { normalizePathname } from './normalizePathname.ts'
|
|
2
3
|
import type { RouteSegment } from './parseRouteSegments.ts'
|
|
3
4
|
import { parseRouteSegments } from './parseRouteSegments.ts'
|
|
@@ -83,17 +84,6 @@ export function matchRoute(
|
|
|
83
84
|
return best === undefined ? undefined : { route: best.route, params: best.params }
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
/* Percent-decodes a captured value. Lenient — a malformed sequence (`/%E0%A4%A`)
|
|
87
|
-
keeps the raw text rather than throwing, so a bad escape in a navigation or
|
|
88
|
-
request can't crash matching; the downstream lookup just misses naturally. */
|
|
89
|
-
function decodeParam(value: string): string {
|
|
90
|
-
try {
|
|
91
|
-
return decodeURIComponent(value)
|
|
92
|
-
} catch {
|
|
93
|
-
return value
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
87
|
/* Matches one parsed pattern against the path's segments, capturing decoded
|
|
98
88
|
params; undefined on mismatch. */
|
|
99
89
|
function matchSegments(
|
|
@@ -129,7 +119,7 @@ function matchFrom(
|
|
|
129
119
|
return false
|
|
130
120
|
}
|
|
131
121
|
if (segment.kind === 'param' && segment.catchAll) {
|
|
132
|
-
params[segment.name] = pathSegments.slice(pathIndex).map(
|
|
122
|
+
params[segment.name] = pathSegments.slice(pathIndex).map(lenientDecode).join('/')
|
|
133
123
|
return true
|
|
134
124
|
}
|
|
135
125
|
const value = pathSegments[pathIndex]
|
|
@@ -145,7 +135,7 @@ function matchFrom(
|
|
|
145
135
|
optional never captures an empty segment. */
|
|
146
136
|
if (value !== undefined && value !== '') {
|
|
147
137
|
const previous = params[segment.name]
|
|
148
|
-
params[segment.name] =
|
|
138
|
+
params[segment.name] = lenientDecode(value)
|
|
149
139
|
if (matchFrom(segments, pathSegments, segmentIndex + 1, pathIndex + 1, params)) {
|
|
150
140
|
return true
|
|
151
141
|
}
|
|
@@ -170,6 +160,6 @@ function matchFrom(
|
|
|
170
160
|
if (value === undefined || value === '') {
|
|
171
161
|
return false
|
|
172
162
|
}
|
|
173
|
-
params[segment.name] =
|
|
163
|
+
params[segment.name] = lenientDecode(value)
|
|
174
164
|
return matchFrom(segments, pathSegments, segmentIndex + 1, pathIndex + 1, params)
|
|
175
165
|
}
|
package/src/lib/shared/peek.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { cache } from './cache.ts'
|
|
2
|
+
import { isAsyncCell } from './isAsyncCell.ts'
|
|
3
|
+
import type { AsyncComputed } from './types/AsyncComputed.ts'
|
|
2
4
|
import type { RemoteFunction } from './types/RemoteFunction.ts'
|
|
3
5
|
import type { Socket } from './types/Socket.ts'
|
|
4
6
|
|
|
@@ -20,7 +22,19 @@ export function peek<Args, Return>(
|
|
|
20
22
|
args?: Args,
|
|
21
23
|
): Return | undefined
|
|
22
24
|
export function peek<T>(source: Socket<T>): T | undefined
|
|
25
|
+
export function peek<T>(cell: AsyncComputed<T>): T | undefined
|
|
23
26
|
export function peek(source: unknown, args?: unknown): unknown {
|
|
27
|
+
/* Null-tolerant: a promise/iterable subexpression peek-lifts to `undefined` while
|
|
28
|
+
pending (ADR-0032), so `peek(getFeed())` in a template hands us `undefined` on the
|
|
29
|
+
first pass — return `undefined` rather than routing a missing source into the cache. */
|
|
30
|
+
if (source == null) {
|
|
31
|
+
return undefined
|
|
32
|
+
}
|
|
33
|
+
/* An async cell carries its own probe surface — route to its method (`peek(cell)` ≡
|
|
34
|
+
`cell.peek()`), the same instance/standalone equivalence as an rpc or socket. */
|
|
35
|
+
if (isAsyncCell(source)) {
|
|
36
|
+
return source.peek()
|
|
37
|
+
}
|
|
24
38
|
/* A subscribable (socket/stream) carries its own latest-frame probe; an rpc does not
|
|
25
39
|
have Symbol.asyncIterator, so this cleanly splits the two even though both expose a
|
|
26
40
|
`.peek` method. */
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { isAsyncCell } from './isAsyncCell.ts'
|
|
1
2
|
import { probeRegistries } from './probeRegistries.ts'
|
|
3
|
+
import type { AsyncComputed } from './types/AsyncComputed.ts'
|
|
2
4
|
import type { CacheEntry } from './types/CacheEntry.ts'
|
|
3
5
|
import type { CacheSelector } from './types/CacheSelector.ts'
|
|
4
|
-
import type {
|
|
6
|
+
import type { NamedAsyncIterable } from './types/NamedAsyncIterable.ts'
|
|
5
7
|
|
|
6
8
|
/*
|
|
7
9
|
Reactive in-flight probe over both registries — the cache (calls) and the
|
|
@@ -15,18 +17,19 @@ tail registry (streams). Pending means "no value yet":
|
|
|
15
17
|
pending(subscribable) → that stream awaiting its first frame
|
|
16
18
|
(tail.status === 'pending'; true when nothing
|
|
17
19
|
is reading yet — there is no value either way)
|
|
18
|
-
pending(durableRpc) → that durable rpc's parked writes (a parked write
|
|
19
|
-
has no value yet); the bare form counts them too
|
|
20
20
|
Probes report, never act: reading one opens no fetch and no stream. SSR
|
|
21
21
|
loading state is driven by {#await}, not this. Scan semantics (tap order,
|
|
22
|
-
selector grammar, registry spans
|
|
23
|
-
probeRegistries.
|
|
22
|
+
selector grammar, registry spans) live in probeRegistries.
|
|
24
23
|
*/
|
|
25
24
|
// @documentation probes
|
|
26
25
|
export function pending<Args, Return>(
|
|
27
|
-
arg?: CacheSelector<Args, Return> |
|
|
26
|
+
arg?: CacheSelector<Args, Return> | NamedAsyncIterable<unknown> | AsyncComputed<unknown>,
|
|
28
27
|
args?: Args,
|
|
29
28
|
): boolean {
|
|
29
|
+
/* An async cell answers from its own facet (`pending(cell)` ≡ `cell.pending()`). */
|
|
30
|
+
if (isAsyncCell(arg)) {
|
|
31
|
+
return arg.pending()
|
|
32
|
+
}
|
|
30
33
|
return probeRegistries(arg, args, 'pending', unsettled, true)
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createResolverSlot } from './createResolverSlot.ts'
|
|
2
|
+
import type { PendingAsyncCells } from './types/PendingAsyncCells.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The active pending-async-cells slot — mirrors `cacheStoreSlot`. The server entry
|
|
6
|
+
installs an ALS-backed resolver (a fresh list per request, so concurrent SSR
|
|
7
|
+
renders never share the drain); with no resolver registered a single fallback
|
|
8
|
+
list is created lazily so isolated tests work without booting the runtime. Read
|
|
9
|
+
through `pendingAsyncCellsSlot.get()`; the SSR barrier (`settleAsyncCells`) drains it.
|
|
10
|
+
*/
|
|
11
|
+
export const pendingAsyncCellsSlot = createResolverSlot<PendingAsyncCells>(() => ({
|
|
12
|
+
promises: [],
|
|
13
|
+
}))
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HTTP_METHODS } from './HTTP_METHODS.ts'
|
|
2
2
|
import { prepareRemoteExport } from './prepareRemoteExport.ts'
|
|
3
|
+
import { DEFINE_RPC_GLOBAL, REMOTE_PROXY_GLOBAL } from './RPC_SHIM_GLOBALS.ts'
|
|
3
4
|
import { skipNonCode } from './skipNonCode.ts'
|
|
4
5
|
import type { HttpMethod } from './types/HttpMethod.ts'
|
|
6
|
+
import type { RpcBuildStamps } from './types/RpcBuildStamps.ts'
|
|
5
7
|
|
|
6
|
-
const RPC_NAMES =
|
|
8
|
+
const RPC_NAMES = HTTP_METHODS
|
|
7
9
|
const RPC_SET = new Set<string>(RPC_NAMES)
|
|
8
10
|
|
|
9
11
|
const SINGLE_EXPORT_ERROR =
|
|
@@ -11,21 +13,14 @@ const SINGLE_EXPORT_ERROR =
|
|
|
11
13
|
|
|
12
14
|
export type PreparedRpcModule = {
|
|
13
15
|
method: HttpMethod
|
|
14
|
-
/* `outbox: true` in the opts — the client proxy is emitted durable. */
|
|
15
|
-
durable: boolean
|
|
16
16
|
/* The handler calls jsonl()/sse() — the client proxy is emitted streaming (bare call
|
|
17
|
-
returns the
|
|
17
|
+
returns the NamedAsyncIterable). Congruent with the RemoteCallable conditional by construction. */
|
|
18
18
|
streaming: boolean
|
|
19
19
|
exportName: string
|
|
20
20
|
rewriteForServer: (url: string) => string
|
|
21
|
+
rewriteForClient: (url: string) => string
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
/* The `outbox` opts key plus its value's leading token (up to the next comma / whitespace /
|
|
24
|
-
closing brace). `outbox` is a BUILD-TIME flag: the client bundle is rewritten durable or
|
|
25
|
-
not from this scan, before any handler runs — so the value must be a literal the scan can
|
|
26
|
-
read, not a computed expression. */
|
|
27
|
-
const OUTBOX_OPT = /\boutbox\s*:\s*([^,\s}]+)/
|
|
28
|
-
|
|
29
24
|
/*
|
|
30
25
|
Scans an `$rpc/**` module once and returns its declared method + export
|
|
31
26
|
name plus a closure that, given the route URL, emits the server-side
|
|
@@ -37,19 +32,11 @@ character-by-character once.
|
|
|
37
32
|
A regex pass would be tidier but it can't tell a `GET` mention inside a
|
|
38
33
|
docstring or template literal from the real call, and it can't follow
|
|
39
34
|
nested generics like `GET<Map<K, V>>(`.
|
|
40
|
-
|
|
41
|
-
The two build-time `outbox` invariants live here, not at runtime, because the
|
|
42
|
-
client bundle's durability is decided here — making this the single source of
|
|
43
|
-
truth (the server-side defineRpc guard stays as a cheap backstop):
|
|
44
|
-
- `outbox` must be a literal `true`/`false`; a computed value can't be read at
|
|
45
|
-
bundle time, so it's rejected loudly instead of silently shipping a
|
|
46
|
-
non-durable client proxy.
|
|
47
|
-
- `outbox: true` is mutating-only — a read RPC (GET/HEAD) has nothing to
|
|
48
|
-
durably deliver.
|
|
49
35
|
*/
|
|
50
36
|
export function prepareRpcModule(
|
|
51
37
|
source: string,
|
|
52
38
|
importName: string,
|
|
39
|
+
stamps: RpcBuildStamps = {},
|
|
53
40
|
): PreparedRpcModule | undefined {
|
|
54
41
|
/*
|
|
55
42
|
The "no barrels" surface places each method at its own path
|
|
@@ -69,60 +56,99 @@ export function prepareRpcModule(
|
|
|
69
56
|
}
|
|
70
57
|
const { stripped, site } = prepared
|
|
71
58
|
const method = site.ident as HttpMethod
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
/* Streaming is decided by the warm server program's return-type query when it resolved a
|
|
60
|
+
verdict (ADR-0025 D2 — it sees the wrapper-indirection case the scan misses); undefined
|
|
61
|
+
(no warm program / unresolvable node) falls open to the char-scan, byte-identical to before. */
|
|
62
|
+
const streaming = stamps.streaming ?? detectStreaming(stripped, site.parenStart, site.parenEnd)
|
|
63
|
+
/* The call's top-level args (handler + optional opts), dropping a trailing-comma empty
|
|
64
|
+
part. The handler is elided on the client; `opts` (schemas/cache/stream) rides through as
|
|
65
|
+
a LIVE expression in the kept module (ADR-0022 D2) — evaluated in its real scope, so it can
|
|
66
|
+
reference imported constants, composed values, and separate modules. */
|
|
67
|
+
const argParts = splitTopLevelArgs(stripped, site.parenStart, site.parenEnd)
|
|
74
68
|
return {
|
|
75
69
|
method,
|
|
76
|
-
durable,
|
|
77
70
|
streaming,
|
|
78
71
|
exportName: site.exportName,
|
|
79
72
|
rewriteForServer(url: string): string {
|
|
80
|
-
const binding =
|
|
73
|
+
const binding = `${DEFINE_RPC_GLOBAL}(${JSON.stringify(method)}, ${JSON.stringify(url)}, `
|
|
81
74
|
const head = stripped.slice(0, site.callStart) + binding
|
|
82
|
-
|
|
75
|
+
/* Build-injected server opts: `streaming` (from the handler body / return type), the
|
|
76
|
+
`coerce` plan (numeric/boolean query fields → typed, ADR-0028), `inputJsonSchema` (the
|
|
77
|
+
handler's input args projected to JSON Schema, ADR-0030 input side), `outputJsonSchema`
|
|
78
|
+
(the handler return type projected to JSON Schema, ADR-0030 D2), and `errorJsonSchemas`
|
|
79
|
+
(the handler's typed-error branches as a status-keyed schema map, ADR-0030). All are
|
|
80
|
+
stamped into a fresh opts object that spreads the author's opts, so policy stays live.
|
|
81
|
+
With none present the original args pass through untouched. */
|
|
82
|
+
const injected: string[] = []
|
|
83
|
+
if (streaming) {
|
|
84
|
+
injected.push('streaming: true')
|
|
85
|
+
}
|
|
86
|
+
if (stamps.coercion !== undefined) {
|
|
87
|
+
injected.push(`coerce: ${JSON.stringify(stamps.coercion)}`)
|
|
88
|
+
}
|
|
89
|
+
if (stamps.inputSchema !== undefined) {
|
|
90
|
+
injected.push(`inputJsonSchema: ${JSON.stringify(stamps.inputSchema)}`)
|
|
91
|
+
}
|
|
92
|
+
if (stamps.outputSchema !== undefined) {
|
|
93
|
+
injected.push(`outputJsonSchema: ${JSON.stringify(stamps.outputSchema)}`)
|
|
94
|
+
}
|
|
95
|
+
if (stamps.errorSchemas !== undefined) {
|
|
96
|
+
injected.push(`errorJsonSchemas: ${JSON.stringify(stamps.errorSchemas)}`)
|
|
97
|
+
}
|
|
98
|
+
if (injected.length === 0) {
|
|
83
99
|
return head + stripped.slice(site.parenStart + 1)
|
|
84
100
|
}
|
|
85
|
-
/*
|
|
86
|
-
opts
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return `${head}${handler}, ${
|
|
101
|
+
/* Reuse the top-level arg split (handler + optional opts) so a trailing comma or absent
|
|
102
|
+
opts can't produce `...()`. `head` ends after the METHOD( paren; keep everything from
|
|
103
|
+
`)` onward. */
|
|
104
|
+
const [handler, opts] = argParts
|
|
105
|
+
const optsObject = opts
|
|
106
|
+
? `{ ${injected.join(', ')}, ...(${opts}) }`
|
|
107
|
+
: `{ ${injected.join(', ')} }`
|
|
108
|
+
return `${head}${handler}, ${optsObject}${stripped.slice(site.parenEnd)}`
|
|
109
|
+
},
|
|
110
|
+
/*
|
|
111
|
+
Client rewrite, symmetric with rewriteForServer: keep the real module, swap the
|
|
112
|
+
METHOD( call for a remoteProxy( call, and ELIDE the handler argument. The handler and
|
|
113
|
+
the imports only it used become dead code the bundler tree-shakes out of the client
|
|
114
|
+
bundle (proven safe by the D3 reachability guard). `method`/`url` are the build-time
|
|
115
|
+
scalars; `opts` is left VERBATIM as a live expression, so endpoint policy (cache/stream)
|
|
116
|
+
can reference imports; `streaming` is the only genuinely build-injected flag — it's
|
|
117
|
+
derived from the elided handler body (returns jsonl()/sse()), so it can't ride `opts`.
|
|
118
|
+
remoteProxy reads only streaming/cache/stream off the opts and ignores the rest.
|
|
119
|
+
*/
|
|
120
|
+
rewriteForClient(url: string): string {
|
|
121
|
+
const callHead = `${stripped.slice(0, site.callStart)}${REMOTE_PROXY_GLOBAL}(${JSON.stringify(method)}, ${JSON.stringify(url)}`
|
|
122
|
+
const opts = argParts[1]
|
|
123
|
+
/* Build-injected CLIENT opts: `streaming` (build-derived from the elided handler body)
|
|
124
|
+
and the `outputWirePlan` (the handler's structured success fields → the client revives
|
|
125
|
+
a `Set`/`Map`/`bigint`/`Date` off the decoded response, ADR-0029). Both are stamped
|
|
126
|
+
into a fresh opts object that spreads the author's live `opts`, so endpoint policy
|
|
127
|
+
(cache/stream/schemas) still rides through verbatim. */
|
|
128
|
+
const injected: string[] = []
|
|
129
|
+
if (streaming) {
|
|
130
|
+
injected.push('streaming: true')
|
|
131
|
+
}
|
|
132
|
+
if (stamps.outputWirePlan !== undefined) {
|
|
133
|
+
injected.push(`outputWirePlan: ${JSON.stringify(stamps.outputWirePlan)}`)
|
|
134
|
+
}
|
|
135
|
+
let argsText: string
|
|
136
|
+
if (injected.length === 0) {
|
|
137
|
+
argsText = opts === undefined ? '' : `, ${opts}`
|
|
138
|
+
} else {
|
|
139
|
+
const injectedText = injected.join(', ')
|
|
140
|
+
argsText =
|
|
141
|
+
opts === undefined
|
|
142
|
+
? `, { ${injectedText} }`
|
|
143
|
+
: `, { ${injectedText}, ...(${opts}) }`
|
|
144
|
+
}
|
|
145
|
+
/* Keep everything from the call's closing paren onward (same slicing discipline as
|
|
146
|
+
rewriteForServer) so any trailing content after the call survives. */
|
|
147
|
+
return `${callHead}${argsText}${stripped.slice(site.parenEnd)}`
|
|
93
148
|
},
|
|
94
149
|
}
|
|
95
150
|
}
|
|
96
151
|
|
|
97
|
-
/* Reads the `outbox` flag off the call's opts object (the trailing argument), enforcing the
|
|
98
|
-
two build-time invariants. Scoping to the opts object keeps the scan off the handler body,
|
|
99
|
-
so a handler that mentions `outbox:` doesn't misfire. */
|
|
100
|
-
function detectDurable(
|
|
101
|
-
source: string,
|
|
102
|
-
parenStart: number,
|
|
103
|
-
parenEnd: number,
|
|
104
|
-
method: HttpMethod,
|
|
105
|
-
): boolean {
|
|
106
|
-
const opts = lastArgText(source, parenStart, parenEnd)
|
|
107
|
-
const match = opts === undefined ? null : OUTBOX_OPT.exec(opts)
|
|
108
|
-
if (match === null) {
|
|
109
|
-
return false
|
|
110
|
-
}
|
|
111
|
-
const value = match[1]
|
|
112
|
-
if (value !== 'true' && value !== 'false') {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`[abide] \`outbox\` must be a literal \`true\` or \`false\` (got \`${value}\`) — it's a build-time flag the client bundle reads, so it can't be a computed expression`,
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
const durable = value === 'true'
|
|
118
|
-
if (durable && isReadOnlyMethod(method)) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
`[abide] outbox: true is only valid on mutating RPCs (POST/PUT/PATCH/DELETE), not ${method}`,
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
return durable
|
|
124
|
-
}
|
|
125
|
-
|
|
126
152
|
const STREAM_HELPERS = new Set(['jsonl', 'sse'])
|
|
127
153
|
const IDENT_START = /[A-Za-z_$]/
|
|
128
154
|
const IDENT_PART = /[A-Za-z0-9_$]/
|
|
@@ -131,8 +157,9 @@ const IDENT_PART = /[A-Za-z0-9_$]/
|
|
|
131
157
|
constructors of a `TypedResponse<AsyncIterable<Frame>>`. Congruent by construction with the
|
|
132
158
|
RemoteCallable conditional (Return is an AsyncIterable iff the handler calls jsonl/sse). Scans
|
|
133
159
|
the whole call-arg region depth-blind (the opts never mention jsonl/sse), skipping
|
|
134
|
-
strings/comments/regex via skipNonCode so a mention in a literal can't misfire.
|
|
135
|
-
literal-only limit
|
|
160
|
+
strings/comments/regex via skipNonCode so a mention in a literal can't misfire. A
|
|
161
|
+
literal-only limit: an indirection through a wrapper function isn't seen (the warm server
|
|
162
|
+
program's return-type query covers that case). */
|
|
136
163
|
function detectStreaming(source: string, parenStart: number, parenEnd: number): boolean {
|
|
137
164
|
let i = parenStart + 1
|
|
138
165
|
while (i < parenEnd) {
|
|
@@ -192,32 +219,3 @@ function splitTopLevelArgs(source: string, parenStart: number, parenEnd: number)
|
|
|
192
219
|
parts.push(source.slice(start, parenEnd).trim())
|
|
193
220
|
return parts.filter((part) => part.length > 0)
|
|
194
221
|
}
|
|
195
|
-
|
|
196
|
-
/*
|
|
197
|
-
The text of the call's final argument — the opts object for a `METHOD(handler, opts)` call.
|
|
198
|
-
Walks the arg list depth-aware, skipping strings / templates / comments / regex (skipNonCode)
|
|
199
|
-
so their commas and braces don't miscount, and returns the slice after the last top-level
|
|
200
|
-
comma. undefined when the call has a single argument (a bare handler, no opts).
|
|
201
|
-
*/
|
|
202
|
-
function lastArgText(source: string, parenStart: number, parenEnd: number): string | undefined {
|
|
203
|
-
let depth = 0
|
|
204
|
-
let lastComma = -1
|
|
205
|
-
let i = parenStart + 1
|
|
206
|
-
while (i < parenEnd) {
|
|
207
|
-
const skipped = skipNonCode(source, i)
|
|
208
|
-
if (skipped !== undefined) {
|
|
209
|
-
i = skipped
|
|
210
|
-
continue
|
|
211
|
-
}
|
|
212
|
-
const c = source[i]
|
|
213
|
-
if (c === '(' || c === '{' || c === '[') {
|
|
214
|
-
depth++
|
|
215
|
-
} else if (c === ')' || c === '}' || c === ']') {
|
|
216
|
-
depth--
|
|
217
|
-
} else if (c === ',' && depth === 0) {
|
|
218
|
-
lastComma = i
|
|
219
|
-
}
|
|
220
|
-
i++
|
|
221
|
-
}
|
|
222
|
-
return lastComma === -1 ? undefined : source.slice(lastComma + 1, parenEnd)
|
|
223
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { prepareRemoteExport } from './prepareRemoteExport.ts'
|
|
2
|
+
import { DEFINE_SOCKET_GLOBAL } from './RPC_SHIM_GLOBALS.ts'
|
|
2
3
|
|
|
3
4
|
const SINGLE_EXPORT_ERROR =
|
|
4
5
|
'[abide] $sockets module contains more than one `socket(...)` export — each file must declare exactly one socket'
|
|
@@ -37,8 +38,8 @@ export function prepareSocketModule(
|
|
|
37
38
|
const inner = stripped.slice(site.parenStart + 1, site.parenEnd).trim()
|
|
38
39
|
const binding =
|
|
39
40
|
inner.length === 0
|
|
40
|
-
?
|
|
41
|
-
:
|
|
41
|
+
? `${DEFINE_SOCKET_GLOBAL}(${JSON.stringify(name)})`
|
|
42
|
+
: `${DEFINE_SOCKET_GLOBAL}(${JSON.stringify(name)}, ${stripped.slice(site.parenStart + 1, site.parenEnd)})`
|
|
42
43
|
return stripped.slice(0, site.callStart) + binding + stripped.slice(site.parenEnd + 1)
|
|
43
44
|
},
|
|
44
45
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { cacheStores } from './cacheStores.ts'
|
|
2
2
|
import { isSubscribable } from './isSubscribable.ts'
|
|
3
|
-
import { outboxProbeSlot } from './outboxProbeSlot.ts'
|
|
4
3
|
import { selectorMatcher } from './selectorMatcher.ts'
|
|
5
4
|
import { selectorPrefix } from './selectorPrefix.ts'
|
|
6
5
|
import { tailProbeSlot } from './tailProbeSlot.ts'
|
|
7
6
|
import type { CacheEntry } from './types/CacheEntry.ts'
|
|
8
7
|
import type { CacheSelector } from './types/CacheSelector.ts'
|
|
9
8
|
import type { CacheStore } from './types/CacheStore.ts'
|
|
10
|
-
import type {
|
|
9
|
+
import type { NamedAsyncIterable } from './types/NamedAsyncIterable.ts'
|
|
11
10
|
|
|
12
11
|
/*
|
|
13
12
|
Shared scan behind the pending() / refreshing() probes: one selector
|
|
@@ -24,22 +23,11 @@ re-tracks narrowed. `matchesEntry` is the probe's cache-side question;
|
|
|
24
23
|
`field` selects the stream-side answer; `unprobed` is the answer for a
|
|
25
24
|
stream no prober can see (server, or tail never imported) — matching what
|
|
26
25
|
tail() itself reports there. The bare form (no selector) spans every
|
|
27
|
-
registry; fn/tag selectors are cache identities only
|
|
28
|
-
|
|
29
|
-
are no-ops and the current value returns.
|
|
30
|
-
|
|
31
|
-
A parked durable (`outbox`) write has no value yet, so it counts as pending —
|
|
32
|
-
but never refreshing (it's holding nothing to supersede), hence the term is
|
|
33
|
-
guarded to `field === 'pending'`. The outbox prober (registered into
|
|
34
|
-
outboxProbeSlot by browser/, empty on the server) reads its doc-backed queue,
|
|
35
|
-
which is the term's own reactive tap. It sits last under `||`: when a matching
|
|
36
|
-
cache call or stream already answers true the queue isn't read, but that's
|
|
37
|
-
monotonic-safe — the parked write can't flip a true result, and the moment the
|
|
38
|
-
cache/stream term falls back to false its lifecycle tap re-derives and the
|
|
39
|
-
queue is read then.
|
|
26
|
+
registry; fn/tag selectors are cache identities only. Outside a tracking scope
|
|
27
|
+
the taps are no-ops and the current value returns.
|
|
40
28
|
*/
|
|
41
29
|
export function probeRegistries<Args, Return>(
|
|
42
|
-
arg: CacheSelector<Args, Return> |
|
|
30
|
+
arg: CacheSelector<Args, Return> | NamedAsyncIterable<unknown> | undefined,
|
|
43
31
|
args: Args | undefined,
|
|
44
32
|
field: 'pending' | 'refreshing',
|
|
45
33
|
matchesEntry: (entry: CacheEntry) => boolean,
|
|
@@ -58,8 +46,7 @@ export function probeRegistries<Args, Return>(
|
|
|
58
46
|
const matches = selectorMatcher(arg, args, prefix)
|
|
59
47
|
return (
|
|
60
48
|
stores.some((store) => anyEntryMatches(store, matchesEntry, matches)) ||
|
|
61
|
-
streams?.[field] === true
|
|
62
|
-
(field === 'pending' && (outboxProbeSlot.probe?.(arg, args) ?? false))
|
|
49
|
+
streams?.[field] === true
|
|
63
50
|
)
|
|
64
51
|
}
|
|
65
52
|
|
|
@@ -4,10 +4,10 @@ import { parseBoundedEnvInt } from './parseBoundedEnvInt.ts'
|
|
|
4
4
|
/*
|
|
5
5
|
Isomorphic outbound reachability. `await reachable(host)` HEADs the host's
|
|
6
6
|
origin: the first call awaits a real probe (faithful — a down host costs the
|
|
7
|
-
full timeout, an up host one handshake) and
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
full timeout, an up host one handshake) and caches the verdict for one TTL, so
|
|
8
|
+
every later call within the TTL resolves instantly off the cached value. The
|
|
9
|
+
first call after the TTL expires re-probes; a host going down (or recovering) is
|
|
10
|
+
noticed on the next read past its cached verdict, not continuously.
|
|
11
11
|
|
|
12
12
|
if (!(await reachable('api.example.com'))) return error(503)
|
|
13
13
|
|
|
@@ -27,10 +27,10 @@ this is the honest way to fail a doomed outbound call fast — see online() for
|
|
|
27
27
|
the inbound/client-reported counterpart. The browser probes no-cors (a
|
|
28
28
|
completed opaque response proves connectivity without the host's CORS
|
|
29
29
|
blessing) and composes navigator.onLine in at read time, so a lost network
|
|
30
|
-
reports false instantly instead of waiting out the
|
|
30
|
+
reports false instantly instead of waiting out the cached value — except for
|
|
31
31
|
loopback hosts, which need no network.
|
|
32
32
|
|
|
33
|
-
ABIDE_REACHABLE_TTL (
|
|
33
|
+
ABIDE_REACHABLE_TTL (cache freshness, ms) and ABIDE_REACHABLE_TIMEOUT
|
|
34
34
|
(per-HEAD bound, ms) tune the server defaults; the browser has no env, so it
|
|
35
35
|
runs the defaults. The timeout is deliberately generous so a healthy-but-
|
|
36
36
|
distant host over a slow link is not mis-read as down.
|
|
@@ -38,8 +38,6 @@ distant host over a slow link is not mis-read as down.
|
|
|
38
38
|
const env = typeof process === 'undefined' ? undefined : process.env
|
|
39
39
|
const TTL_MS = parseBoundedEnvInt(env?.ABIDE_REACHABLE_TTL, 1_000, 600_000) ?? 30_000
|
|
40
40
|
const TIMEOUT_MS = parseBoundedEnvInt(env?.ABIDE_REACHABLE_TIMEOUT, 100, 60_000) ?? 3_000
|
|
41
|
-
/* Stop polling a host nobody has read in a few TTLs; the next read restarts it cold. */
|
|
42
|
-
const IDLE_MS = TTL_MS * 3
|
|
43
41
|
|
|
44
42
|
/* Status-agnostic HEAD: a completed response proves connectivity; reject/timeout does not.
|
|
45
43
|
The browser probes no-cors — an opaque response still completes, so a foreign origin
|
|
@@ -59,8 +57,7 @@ async function probeOrigin(origin: string): Promise<boolean> {
|
|
|
59
57
|
|
|
60
58
|
const registry = createReachable({
|
|
61
59
|
probe: probeOrigin,
|
|
62
|
-
|
|
63
|
-
idleMs: IDLE_MS,
|
|
60
|
+
ttlMs: TTL_MS,
|
|
64
61
|
})
|
|
65
62
|
|
|
66
63
|
/* Loopback = this machine: no network between, so no probe and no offline gate. */
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { cache } from './cache.ts'
|
|
2
|
+
import { isAsyncCell } from './isAsyncCell.ts'
|
|
3
|
+
import type { AsyncComputed } from './types/AsyncComputed.ts'
|
|
2
4
|
import type { CacheSelector } from './types/CacheSelector.ts'
|
|
3
5
|
|
|
4
6
|
/*
|
|
@@ -17,6 +19,14 @@ Instance sugar `getFoo.refresh(args?)` ≡ `refresh(getFoo, args?)`. Reports, ne
|
|
|
17
19
|
retains a spinner — pair with `refreshing()` to surface the in-flight reload.
|
|
18
20
|
*/
|
|
19
21
|
// @documentation cache
|
|
20
|
-
export function refresh<Args, Return>(
|
|
21
|
-
|
|
22
|
+
export function refresh<Args, Return>(
|
|
23
|
+
arg?: CacheSelector<Args, Return> | AsyncComputed<unknown>,
|
|
24
|
+
args?: Args,
|
|
25
|
+
): void {
|
|
26
|
+
/* An async cell re-invokes its own seed (`refresh(cell)` ≡ `cell.refresh()`). */
|
|
27
|
+
if (isAsyncCell(arg)) {
|
|
28
|
+
arg.refresh()
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
cache.refresh(arg as CacheSelector<Args, Return>, args)
|
|
22
32
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { isAsyncCell } from './isAsyncCell.ts'
|
|
1
2
|
import { probeRegistries } from './probeRegistries.ts'
|
|
3
|
+
import type { AsyncComputed } from './types/AsyncComputed.ts'
|
|
2
4
|
import type { CacheEntry } from './types/CacheEntry.ts'
|
|
3
5
|
import type { CacheSelector } from './types/CacheSelector.ts'
|
|
4
|
-
import type {
|
|
6
|
+
import type { NamedAsyncIterable } from './types/NamedAsyncIterable.ts'
|
|
5
7
|
|
|
6
8
|
/*
|
|
7
9
|
Reactive revalidation probe over both registries. Refreshing means "holding a
|
|
@@ -22,9 +24,13 @@ registry spans) live in probeRegistries.
|
|
|
22
24
|
*/
|
|
23
25
|
// @documentation probes
|
|
24
26
|
export function refreshing<Args, Return>(
|
|
25
|
-
arg?: CacheSelector<Args, Return> |
|
|
27
|
+
arg?: CacheSelector<Args, Return> | NamedAsyncIterable<unknown> | AsyncComputed<unknown>,
|
|
26
28
|
args?: Args,
|
|
27
29
|
): boolean {
|
|
30
|
+
/* An async cell answers from its own facet (`refreshing(cell)` ≡ `cell.refreshing()`). */
|
|
31
|
+
if (isAsyncCell(arg)) {
|
|
32
|
+
return arg.refreshing()
|
|
33
|
+
}
|
|
28
34
|
return probeRegistries(arg, args, 'refreshing', reloading, false)
|
|
29
35
|
}
|
|
30
36
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createResolverSlot } from './createResolverSlot.ts'
|
|
2
|
+
import type { ResolvedCells } from './types/ResolvedCells.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The active resolved-async-cells slot — mirrors `pendingAsyncCellsSlot`. The server entry installs
|
|
6
|
+
an ALS-backed resolver (a fresh list per request, so concurrent SSR renders never mix their warm
|
|
7
|
+
seeds); with no resolver registered a single fallback list is created lazily so isolated tests
|
|
8
|
+
work without booting the runtime. `createAsyncCell` pushes a `{key, value}` when a seed settles
|
|
9
|
+
server-side; the page renderer reads it at render-return to stamp `__SSR__.cells`.
|
|
10
|
+
*/
|
|
11
|
+
export const resolvedCellsSlot = createResolverSlot<ResolvedCells>(() => ({
|
|
12
|
+
entries: [],
|
|
13
|
+
}))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { WireKind } from './types/WireKind.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
One field's honest-JSON wire value → its declared runtime type, fail-open — the decode core
|
|
5
|
+
shared by the rpc RESPONSE revival (`reviveWireOutput`) and the INPUT revival (`parseArgs`), so
|
|
6
|
+
the two ends can't drift on how a wire form decodes (ADR-0029). A `Set`/`Map` already carried by
|
|
7
|
+
an abide ref-json body passes through; a wire array becomes the `Set`/`Map`; an ISO string becomes
|
|
8
|
+
a `Date`; a digit string becomes a `bigint`. An already-typed or non-matching value is returned
|
|
9
|
+
untouched so the codec never throws.
|
|
10
|
+
|
|
11
|
+
Callers layer their own-side extras on top: the input side additionally accepts a plain object as
|
|
12
|
+
`Map` entries and coerces query/form STRINGS to number/boolean (see `parseArgs`). Top-level fields
|
|
13
|
+
only — a structured value nested deeper is not descended into (deferred, ADR-0029).
|
|
14
|
+
*/
|
|
15
|
+
export function reviveWireField(value: unknown, kind: WireKind): unknown {
|
|
16
|
+
if (kind === 'set') {
|
|
17
|
+
if (value instanceof Set) {
|
|
18
|
+
return value
|
|
19
|
+
}
|
|
20
|
+
return Array.isArray(value) ? new Set(value) : value
|
|
21
|
+
}
|
|
22
|
+
if (kind === 'map') {
|
|
23
|
+
if (value instanceof Map) {
|
|
24
|
+
return value
|
|
25
|
+
}
|
|
26
|
+
return Array.isArray(value) ? new Map(value as [unknown, unknown][]) : value
|
|
27
|
+
}
|
|
28
|
+
if (kind === 'date') {
|
|
29
|
+
if (typeof value !== 'string') {
|
|
30
|
+
return value
|
|
31
|
+
}
|
|
32
|
+
const date = new Date(value)
|
|
33
|
+
return Number.isNaN(date.getTime()) ? value : date
|
|
34
|
+
}
|
|
35
|
+
if (kind === 'bigint') {
|
|
36
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
37
|
+
return value
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
return BigInt(value)
|
|
41
|
+
} catch {
|
|
42
|
+
/* A non-integer literal throws — keep the string rather than crash the decode. */
|
|
43
|
+
return value
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/* number/boolean ride as their JSON type on the wire; nothing to revive here (the input side
|
|
47
|
+
coerces them from query/form strings separately). */
|
|
48
|
+
return value
|
|
49
|
+
}
|