@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
package/src/build.ts
CHANGED
|
@@ -1,37 +1,44 @@
|
|
|
1
1
|
import { clientBuildPlugins } from './clientBuildPlugins.ts'
|
|
2
2
|
import { abideLog } from './lib/shared/abideLog.ts'
|
|
3
3
|
import { exitOnBuildFailure } from './lib/shared/exitOnBuildFailure.ts'
|
|
4
|
+
import { generateDeclarations } from './lib/shared/generateDeclarations.ts'
|
|
4
5
|
import { markFrameworkSourcesIgnored } from './lib/shared/markFrameworkSourcesIgnored.ts'
|
|
5
6
|
|
|
6
7
|
const CLIENT_ENTRY = new URL('./clientEntry.ts', import.meta.url).pathname
|
|
7
8
|
|
|
8
9
|
/*
|
|
9
|
-
Builds the client-side bundle into `${cwd}/dist
|
|
10
|
-
the abide-ui `.abide` loader, the
|
|
11
|
-
Tailwind. When `compress`, each emitted file is also
|
|
10
|
+
Builds the client-side bundle into `${cwd}/dist` (see `appDir` below for the
|
|
11
|
+
exact directory). Runs Bun.build with the abide-ui `.abide` loader, the
|
|
12
|
+
virtual-module resolver, and (optionally) Tailwind. When `compress`, each emitted file is also
|
|
12
13
|
written as a gzip-compressed `.gz` sibling (level 9 — paid once at build
|
|
13
14
|
time) so the server can stream the precompressed bytes directly to any client
|
|
14
15
|
that accepts gzip (effectively all), and decompress on the fly otherwise. Dev
|
|
15
16
|
skips compression (compressing on every rebuild dwarfs the bundle itself) — the
|
|
16
17
|
server falls back to serving the plain bytes when no `.gz` sibling exists.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
`_app` with two atomic renames. This keeps every build's writes
|
|
20
|
-
a unique path (so a stray concurrent build can never `rm` files Bun
|
|
21
|
-
mid-flushing — the "writing sourcemap: No such file or directory" race) and
|
|
22
|
-
means a
|
|
23
|
-
|
|
19
|
+
A production build (`clean`) emits into a per-build staging dir, then swaps it
|
|
20
|
+
into the stable `_app` with two atomic renames. This keeps every build's writes
|
|
21
|
+
isolated to a unique path (so a stray concurrent build can never `rm` files Bun
|
|
22
|
+
is mid-flushing — the "writing sourcemap: No such file or directory" race) and
|
|
23
|
+
means a reader never sees a half-built or emptied `_app`. `clean` also clears
|
|
24
|
+
the whole dist up front so downstream writers — the bundle's connect screen, the
|
|
25
|
+
CLI manifest — start fresh.
|
|
24
26
|
|
|
25
|
-
`clean`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
Dev (`clean: false`) instead emits each generation into its OWN
|
|
28
|
+
`_app.gen-<id>` directory and never mutates it afterward. A dev worker serves
|
|
29
|
+
from exactly the generation it was spawned on (via `ABIDE_APP_DIR`), so a rebuild
|
|
30
|
+
gives the new worker a new dir while the retiring worker keeps reading its own —
|
|
31
|
+
there is no shared mutable `_app`, hence no swap gap and no stale-hash 500s while
|
|
32
|
+
a replaced worker drains (both bind the port via reusePort). The orchestrator
|
|
33
|
+
prunes a generation dir once its worker has exited.
|
|
29
34
|
|
|
30
|
-
Returns
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
the
|
|
35
|
+
Returns `{ appDir }` — the directory the bundle now lives in (`_app` for a
|
|
36
|
+
production build, the generation dir for dev) — on success, or `false` on
|
|
37
|
+
failure. Never throws: a thrown Bun.build / fs error is logged and treated as a
|
|
38
|
+
failed build, so the dev loop (and its last-good server) survives instead of
|
|
39
|
+
crashing and orphaning the child. By default a failure exits the process
|
|
40
|
+
(one-shot `abide build` / `compile`); the dev orchestrator passes
|
|
41
|
+
`exitOnFailure: false`.
|
|
35
42
|
*/
|
|
36
43
|
// @documentation building
|
|
37
44
|
export async function build({
|
|
@@ -48,15 +55,19 @@ export async function build({
|
|
|
48
55
|
clean?: boolean
|
|
49
56
|
exitOnFailure?: boolean
|
|
50
57
|
dev?: boolean
|
|
51
|
-
} = {}): Promise<
|
|
58
|
+
} = {}): Promise<{ appDir: string } | false> {
|
|
52
59
|
const distDir = `${cwd}/dist`
|
|
53
|
-
|
|
54
|
-
// Per-build staging + holding dirs; the suffix isolates concurrent builds.
|
|
60
|
+
// The suffix isolates concurrent/successive builds.
|
|
55
61
|
const buildId = crypto.randomUUID().slice(0, 8)
|
|
56
|
-
|
|
62
|
+
/* Production owns the stable `_app`; dev owns a fresh per-generation dir it never
|
|
63
|
+
rewrites (see the header — this is what removes the shared-mutable-`_app` race). */
|
|
64
|
+
const appDir = clean ? `${distDir}/_app` : `${distDir}/_app.gen-${buildId}`
|
|
65
|
+
/* Production stages then atomically swaps into `_app`. Dev builds straight into its
|
|
66
|
+
generation dir — nothing reads it until a worker is spawned pointing at it. */
|
|
67
|
+
const stagingDir = clean ? `${distDir}/_app.staging-${buildId}` : appDir
|
|
57
68
|
const previousDir = `${distDir}/_app.old-${buildId}`
|
|
58
69
|
|
|
59
|
-
const fail = ():
|
|
70
|
+
const fail = (): false => {
|
|
60
71
|
if (exitOnFailure) {
|
|
61
72
|
process.exit(1)
|
|
62
73
|
}
|
|
@@ -74,27 +85,36 @@ export async function build({
|
|
|
74
85
|
tailwindWarning: 'bun-plugin-tailwind not installed; building without Tailwind',
|
|
75
86
|
})
|
|
76
87
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
if (!dev) {
|
|
89
|
+
abideLog.info('building client bundle…')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* generateDeclarations builds the rpc `ts.Program` ONCE per build (ADR-0025 D2's
|
|
93
|
+
alias-aware method detection) and writes every src/.abide/*.d.ts editor artifact. Run it
|
|
94
|
+
CONCURRENTLY with Bun.build so its program build hides under the bundle. The runtime
|
|
95
|
+
worker (dev worker / `abide start`) builds NO program at boot — only lazily on the first
|
|
96
|
+
rpc request for the wire codec — so all boot-time .d.ts generation is gone. It never
|
|
97
|
+
throws (editor types are non-critical), so it can't fail the build. */
|
|
98
|
+
const [result] = await Promise.all([
|
|
99
|
+
Bun.build({
|
|
100
|
+
entrypoints: [CLIENT_ENTRY],
|
|
101
|
+
outdir: stagingDir,
|
|
102
|
+
target: 'browser',
|
|
103
|
+
splitting: true,
|
|
104
|
+
minify,
|
|
105
|
+
sourcemap: 'linked',
|
|
106
|
+
/* The abideResolverPlugin's onEnd reachability guard (ADR-0022 D3) reads this to
|
|
107
|
+
classify surviving modules post-DCE and reject any server-only code that leaked. */
|
|
108
|
+
metafile: true,
|
|
109
|
+
naming: {
|
|
110
|
+
entry: 'client-[hash].[ext]',
|
|
111
|
+
chunk: '[name]-[hash].[ext]',
|
|
112
|
+
asset: '[name].[ext]',
|
|
113
|
+
},
|
|
114
|
+
plugins,
|
|
115
|
+
}),
|
|
116
|
+
generateDeclarations({ cwd }),
|
|
117
|
+
])
|
|
98
118
|
|
|
99
119
|
if (!result.success) {
|
|
100
120
|
await Bun.$`rm -rf ${stagingDir}`.quiet().nothrow()
|
|
@@ -135,29 +155,32 @@ export async function build({
|
|
|
135
155
|
: 0
|
|
136
156
|
|
|
137
157
|
/*
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
Production swap: move any existing `_app` aside, then rename staging into
|
|
159
|
+
place. The window where `_app` is absent is a single rename, so a reader
|
|
160
|
+
never observes a partial bundle; nothrow on the first move since no `_app`
|
|
161
|
+
exists on a fresh (just-cleaned) dist. Dev skips this entirely — its bundle
|
|
162
|
+
was built straight into the generation dir, which no worker reads until the
|
|
163
|
+
orchestrator spawns one on it.
|
|
143
164
|
*/
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
if (clean) {
|
|
166
|
+
await Bun.$`mv ${appDir} ${previousDir}`.quiet().nothrow()
|
|
167
|
+
await Bun.$`mv ${stagingDir} ${appDir}`.quiet()
|
|
168
|
+
await Bun.$`rm -rf ${previousDir}`.quiet().nothrow()
|
|
169
|
+
}
|
|
147
170
|
|
|
148
171
|
if (compress) {
|
|
149
172
|
abideLog.info(
|
|
150
|
-
`wrote ${result.outputs.length} files to ${
|
|
173
|
+
`wrote ${result.outputs.length} files to ${appDir} (+${result.outputs.length} .gz, ${(compressedBytes / 1024).toFixed(1)} KiB total)`,
|
|
151
174
|
)
|
|
152
175
|
// Per-file paths are noise at startup; surface them only under DEBUG=abide:build.
|
|
153
176
|
const buildLog = abideLog.channel('abide:build')
|
|
154
177
|
result.outputs.forEach((output) => {
|
|
155
|
-
buildLog(` - ${output.path.replace(stagingDir,
|
|
178
|
+
buildLog(` - ${output.path.replace(stagingDir, appDir)}`)
|
|
156
179
|
})
|
|
157
180
|
} else {
|
|
158
|
-
abideLog.info(`wrote ${result.outputs.length} files to ${
|
|
181
|
+
abideLog.info(`wrote ${result.outputs.length} files to ${appDir}`)
|
|
159
182
|
}
|
|
160
|
-
return
|
|
183
|
+
return { appDir }
|
|
161
184
|
} catch (error) {
|
|
162
185
|
abideLog.error(error)
|
|
163
186
|
await Bun.$`rm -rf ${stagingDir} ${previousDir}`.quiet().nothrow()
|
package/src/buildDisconnected.ts
CHANGED
|
@@ -51,8 +51,6 @@ export async function buildDisconnected({
|
|
|
51
51
|
entrypoints: [ENTRY, CSS_ENTRY],
|
|
52
52
|
target: 'browser',
|
|
53
53
|
minify: true,
|
|
54
|
-
// Connect screen ships in production bundles only — fold dev-only machinery out.
|
|
55
|
-
define: { __ABIDE_DEV__: 'false' },
|
|
56
54
|
plugins,
|
|
57
55
|
})
|
|
58
56
|
|
package/src/checkAbide.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import ts from 'typescript'
|
|
3
3
|
import { collectAbideDiagnostics } from './lib/ui/compile/collectAbideDiagnostics.ts'
|
|
4
|
-
import { createShadowProgram } from './lib/ui/compile/createShadowProgram.ts'
|
|
4
|
+
import { createShadowProgram, type ShadowProgram } from './lib/ui/compile/createShadowProgram.ts'
|
|
5
|
+
import { interpolationClassifierForRoot } from './lib/ui/compile/interpolationClassifierForRoot.ts'
|
|
5
6
|
import { nearestProjectRoot } from './lib/ui/compile/nearestProjectRoot.ts'
|
|
6
7
|
import type { AbideDiagnostic } from './lib/ui/compile/types/AbideDiagnostic.ts'
|
|
7
8
|
|
|
@@ -58,8 +59,17 @@ function collectByProject(cwd: string): { diagnostics: AbideDiagnostic[]; checke
|
|
|
58
59
|
const root = nearestProjectRoot(path, cwd)
|
|
59
60
|
byProject.set(root, [...(byProject.get(root) ?? []), path])
|
|
60
61
|
}
|
|
62
|
+
/* Per-root cache of the VERBATIM classifier program (ADR-0032). The type-check pass peek-wraps
|
|
63
|
+
async interpolations so they resolve to their settled value, but the classifier that decides
|
|
64
|
+
WHICH sub-expressions are async must read un-wrapped shadows — so it rides a separate verbatim
|
|
65
|
+
program, built (and reused) here per root. */
|
|
66
|
+
const classifierCache = new Map<string, ShadowProgram | undefined>()
|
|
61
67
|
const diagnostics = [...byProject].flatMap(([root, paths]) =>
|
|
62
|
-
collectAbideDiagnostics(
|
|
68
|
+
collectAbideDiagnostics(
|
|
69
|
+
createShadowProgram(root, paths, (abidePath) =>
|
|
70
|
+
interpolationClassifierForRoot(classifierCache, root, abidePath),
|
|
71
|
+
),
|
|
72
|
+
),
|
|
63
73
|
)
|
|
64
74
|
const checked = [...byProject.values()].reduce((total, paths) => total + paths.length, 0)
|
|
65
75
|
return { diagnostics, checked }
|
package/src/devEntry.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { watch } from 'node:fs'
|
|
1
|
+
import { readdirSync, watch } from 'node:fs'
|
|
2
2
|
import type { Subprocess } from 'bun'
|
|
3
3
|
import { build } from './build.ts'
|
|
4
4
|
import { DEFAULT_PORT } from './lib/server/runtime/DEFAULT_PORT.ts'
|
|
@@ -7,6 +7,7 @@ import { DEV_REBUILD_MESSAGE } from './lib/server/runtime/DEV_REBUILD_MESSAGE.ts
|
|
|
7
7
|
import { findOpenPort } from './lib/server/runtime/findOpenPort.ts'
|
|
8
8
|
import { abideLog } from './lib/shared/abideLog.ts'
|
|
9
9
|
import { changeAffectsClient } from './lib/shared/changeAffectsClient.ts'
|
|
10
|
+
import { DEV_REBUILD_PATH } from './lib/shared/DEV_REBUILD_PATH.ts'
|
|
10
11
|
|
|
11
12
|
/*
|
|
12
13
|
Dev orchestrator. Replaces `bun --watch` (which only watches the import graph,
|
|
@@ -27,6 +28,13 @@ loop we own end to end:
|
|
|
27
28
|
live-reload channel; it reconnects straight onto the already-listening
|
|
28
29
|
replacement and reloads itself.
|
|
29
30
|
|
|
31
|
+
Each client build lands in its OWN `dist/_app.gen-<id>` directory (build.ts,
|
|
32
|
+
`clean: false`) and the worker spawned for it is pinned to that dir via
|
|
33
|
+
`ABIDE_APP_DIR`. So the incumbent keeps serving its generation's chunks off disk
|
|
34
|
+
for the whole overlap while the replacement serves the new one — nothing is a
|
|
35
|
+
shared mutable `_app`, so a draining worker can never 500 on chunks the new build
|
|
36
|
+
would otherwise have deleted. A generation dir is pruned once its worker exits.
|
|
37
|
+
|
|
30
38
|
Restarts are serialized (a build mid-flight queues the next) and the port is
|
|
31
39
|
fixed so the browser tab stays valid across restarts. A failed build — or a
|
|
32
40
|
replacement that dies or hangs while booting — keeps the last-good server
|
|
@@ -56,8 +64,9 @@ function isGenerated(filename: string): boolean {
|
|
|
56
64
|
return filename.split(/[\\/]/).includes(GENERATED_DIR)
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
// clean:false leaves the live dist in place — each build
|
|
60
|
-
// so the running server never serves a half-built
|
|
67
|
+
// clean:false leaves the live dist in place — each build emits its own
|
|
68
|
+
// `_app.gen-<id>` dir, so the running server never serves a half-built bundle and
|
|
69
|
+
// a rebuild never mutates the dir an existing worker is reading.
|
|
61
70
|
const buildOptions = {
|
|
62
71
|
cwd,
|
|
63
72
|
minify: false,
|
|
@@ -69,16 +78,55 @@ const buildOptions = {
|
|
|
69
78
|
|
|
70
79
|
// The worker currently meant to be serving; undefined while crashed or replaced.
|
|
71
80
|
let server: Subprocess | undefined
|
|
81
|
+
// The `_app.gen-<id>` dir the active worker is pinned to — the bundle it serves and
|
|
82
|
+
// the dir to prune once it (and no successor sharing it) is gone.
|
|
83
|
+
let currentAppDir = `${cwd}/dist/_app`
|
|
72
84
|
let shuttingDown = false
|
|
73
85
|
// Worker exits since the last ready signal — bounds the crash-respawn loop.
|
|
74
86
|
let exitsWithoutReady = 0
|
|
75
87
|
|
|
88
|
+
/* Remove a spent generation dir. Guarded to a `_app.gen-*` path so a bug (or the
|
|
89
|
+
`${cwd}/dist/_app` fallback when the first build failed) can never `rm` the wrong
|
|
90
|
+
thing. Best-effort — a leftover is swept by pruneStaleBuildDirs next dev start. */
|
|
91
|
+
async function pruneGenDir(dir: string): Promise<void> {
|
|
92
|
+
if (!dir.includes('/_app.gen-')) {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
await Bun.$`rm -rf ${dir}`.quiet().nothrow()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Sweep generation/staging/old dirs left by a prior dev session that exited without
|
|
99
|
+
cleaning up (crash, kill -9). Runs once before the first build so dist doesn't
|
|
100
|
+
accumulate them across sessions. */
|
|
101
|
+
function pruneStaleBuildDirs(): void {
|
|
102
|
+
const distDir = `${cwd}/dist`
|
|
103
|
+
let entries: string[]
|
|
104
|
+
try {
|
|
105
|
+
entries = readdirSync(distDir)
|
|
106
|
+
} catch {
|
|
107
|
+
return // no dist yet — nothing to sweep
|
|
108
|
+
}
|
|
109
|
+
for (const name of entries) {
|
|
110
|
+
if (/^_app\.(gen|staging|old)-/.test(name)) {
|
|
111
|
+
void Bun.$`rm -rf ${distDir}/${name}`.quiet().nothrow()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
76
116
|
/*
|
|
77
|
-
Spawn a server worker against the fixed dev port
|
|
78
|
-
|
|
79
|
-
|
|
117
|
+
Spawn a server worker against the fixed dev port, pinned to `appDir` (its build
|
|
118
|
+
generation) via ABIDE_APP_DIR so its shell, preload manifest, and asset server all
|
|
119
|
+
read that one dir for its whole life. `ready` resolves when the worker reports its
|
|
120
|
+
listener is up and init() has run (DEV_READY_MESSAGE) — the cue that a replacement
|
|
121
|
+
may retire its predecessor.
|
|
80
122
|
*/
|
|
81
|
-
function spawnWorker(
|
|
123
|
+
function spawnWorker(
|
|
124
|
+
port: number,
|
|
125
|
+
appDir: string,
|
|
126
|
+
/* Only the first worker prints the diagnostic surface map (it eager-loads the
|
|
127
|
+
registry — off the reload hot path for every respawn, whose surface is identical). */
|
|
128
|
+
printSurface = false,
|
|
129
|
+
): { proc: Subprocess; ready: Promise<void> } {
|
|
82
130
|
const readiness = Promise.withResolvers<void>()
|
|
83
131
|
const proc = Bun.spawn({
|
|
84
132
|
cmd: ['bun', '--preload', PRELOAD, SERVER_ENTRY],
|
|
@@ -94,6 +142,8 @@ function spawnWorker(port: number): { proc: Subprocess; ready: Promise<void> } {
|
|
|
94
142
|
PORT: String(port),
|
|
95
143
|
ABIDE_DEV: '1',
|
|
96
144
|
ABIDE_PARENT_PID: String(process.pid),
|
|
145
|
+
ABIDE_APP_DIR: appDir,
|
|
146
|
+
ABIDE_DEV_SURFACE: printSurface ? '1' : '0',
|
|
97
147
|
},
|
|
98
148
|
stdio: ['inherit', 'inherit', 'inherit'],
|
|
99
149
|
// The child's POST /__abide/reload route signals a rebuild over IPC, so the
|
|
@@ -108,7 +158,7 @@ function spawnWorker(port: number): { proc: Subprocess; ready: Promise<void> } {
|
|
|
108
158
|
}
|
|
109
159
|
},
|
|
110
160
|
})
|
|
111
|
-
respawnOnUnexpectedExit(proc, port)
|
|
161
|
+
respawnOnUnexpectedExit(proc, port, appDir)
|
|
112
162
|
return { proc, ready: readiness.promise }
|
|
113
163
|
}
|
|
114
164
|
|
|
@@ -118,7 +168,7 @@ shutdown — used to leave the port dead until a manual restart. Respawn it,
|
|
|
118
168
|
giving up after MAX_EXITS_WITHOUT_READY exits in a row so a crash-on-boot
|
|
119
169
|
doesn't spin; the next save retries through rebuild.
|
|
120
170
|
*/
|
|
121
|
-
function respawnOnUnexpectedExit(proc: Subprocess, port: number): void {
|
|
171
|
+
function respawnOnUnexpectedExit(proc: Subprocess, port: number, appDir: string): void {
|
|
122
172
|
void proc.exited.then((exitCode) => {
|
|
123
173
|
if (shuttingDown || server !== proc) {
|
|
124
174
|
return
|
|
@@ -132,7 +182,8 @@ function respawnOnUnexpectedExit(proc: Subprocess, port: number): void {
|
|
|
132
182
|
return
|
|
133
183
|
}
|
|
134
184
|
abideLog.warn(`server exited unexpectedly (code ${exitCode}) — restarting`)
|
|
135
|
-
|
|
185
|
+
// Respawn on the SAME generation dir — the bundle is fine, the process crashed.
|
|
186
|
+
server = spawnWorker(port, appDir).proc
|
|
136
187
|
})
|
|
137
188
|
}
|
|
138
189
|
|
|
@@ -145,16 +196,21 @@ async function stopWorker(proc: Subprocess): Promise<void> {
|
|
|
145
196
|
}
|
|
146
197
|
|
|
147
198
|
/*
|
|
148
|
-
Zero-downtime swap
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
the new module graph's boot
|
|
152
|
-
|
|
153
|
-
|
|
199
|
+
Zero-downtime swap onto `nextAppDir` (the new build's generation dir). The
|
|
200
|
+
replacement overlaps the incumbent — both bind the dev port via reusePort (see
|
|
201
|
+
createServer) and the kernel keeps delivering connections to the incumbent until it
|
|
202
|
+
stops — so the port answers throughout the new module graph's boot, and each worker
|
|
203
|
+
serves its own generation dir so the overlap never crosses chunk sets. Only a
|
|
204
|
+
replacement that reports ready retires the incumbent; one that dies or hangs booting
|
|
205
|
+
is discarded and the last-good server keeps serving, mirroring how a failed build is
|
|
206
|
+
handled. A generation dir is pruned once no worker references it: the retired
|
|
207
|
+
incumbent's dir after it exits, or the failed replacement's own dir if it never
|
|
208
|
+
took over (unless a server-only restart reused the incumbent's dir — then it stays).
|
|
154
209
|
*/
|
|
155
|
-
async function replaceServer(port: number): Promise<void> {
|
|
210
|
+
async function replaceServer(port: number, nextAppDir: string): Promise<void> {
|
|
156
211
|
const previous = server
|
|
157
|
-
const
|
|
212
|
+
const previousAppDir = currentAppDir
|
|
213
|
+
const next = spawnWorker(port, nextAppDir)
|
|
158
214
|
const outcome = await Promise.race([
|
|
159
215
|
next.ready.then(() => 'ready' as const),
|
|
160
216
|
next.proc.exited.then(() => 'exited' as const),
|
|
@@ -165,11 +221,22 @@ async function replaceServer(port: number): Promise<void> {
|
|
|
165
221
|
await stopWorker(next.proc)
|
|
166
222
|
}
|
|
167
223
|
abideLog.warn('new server failed to boot — the previous one (if any) keeps serving')
|
|
224
|
+
// The replacement never served; its fresh generation dir is orphaned. Prune it,
|
|
225
|
+
// unless a server-only restart reused the still-serving incumbent's dir.
|
|
226
|
+
if (nextAppDir !== previousAppDir) {
|
|
227
|
+
await pruneGenDir(nextAppDir)
|
|
228
|
+
}
|
|
168
229
|
return
|
|
169
230
|
}
|
|
170
231
|
server = next.proc
|
|
232
|
+
currentAppDir = nextAppDir
|
|
171
233
|
if (previous) {
|
|
172
234
|
await stopWorker(previous)
|
|
235
|
+
// The incumbent is gone; retire its generation dir — unless the replacement is
|
|
236
|
+
// serving that same dir (a server-only restart with no client rebuild).
|
|
237
|
+
if (previousAppDir !== nextAppDir) {
|
|
238
|
+
await pruneGenDir(previousAppDir)
|
|
239
|
+
}
|
|
173
240
|
}
|
|
174
241
|
}
|
|
175
242
|
|
|
@@ -200,17 +267,20 @@ async function rebuild(port: number, skipClientBuild = false): Promise<void> {
|
|
|
200
267
|
}
|
|
201
268
|
building = true
|
|
202
269
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
270
|
+
/* A server/MCP-only change keeps the existing client bundle, so the replacement
|
|
271
|
+
worker reuses the incumbent's generation dir; a client rebuild produces a fresh
|
|
272
|
+
one. Either way the swap needs the dir the new worker should serve. */
|
|
273
|
+
const built = skipClientBuild ? { appDir: currentAppDir } : await build(buildOptions)
|
|
274
|
+
if (built) {
|
|
275
|
+
await replaceServer(port, built.appDir)
|
|
206
276
|
}
|
|
207
277
|
/* A server/MCP-only change reuses the existing client bundle. That's correct for
|
|
208
|
-
an rpc/socket handler BODY edit, but changing an rpc's method
|
|
209
|
-
|
|
210
|
-
|
|
278
|
+
an rpc/socket handler BODY edit, but changing an rpc's method or its export name
|
|
279
|
+
changes the client proxy stub too — the stale bundle then calls with the old shape
|
|
280
|
+
(e.g. a 405). Flag the skip so that case isn't silent. */
|
|
211
281
|
if (skipClientBuild) {
|
|
212
282
|
abideLog.info(
|
|
213
|
-
'server-only change — kept the existing client bundle; if you changed an rpc method
|
|
283
|
+
'server-only change — kept the existing client bundle; if you changed an rpc method or export name, save a client file (or restart dev) for a full rebuild',
|
|
214
284
|
)
|
|
215
285
|
}
|
|
216
286
|
} finally {
|
|
@@ -231,11 +301,23 @@ on the same predictable 3000+ address as `bun start`; reusing the number across
|
|
|
231
301
|
restarts (not re-scanning) is what keeps the tab valid.
|
|
232
302
|
*/
|
|
233
303
|
const port = findOpenPort(DEFAULT_PORT)
|
|
304
|
+
// Sweep generation dirs a prior session may have left behind before building afresh.
|
|
305
|
+
pruneStaleBuildDirs()
|
|
306
|
+
/* Announce the boot before the first build. build() stays silent under dev (its
|
|
307
|
+
"building…" log is gated so watch rebuilds don't spam), so without this line the
|
|
308
|
+
terminal shows nothing for the whole initial build — a cold boot reads as a hang.
|
|
309
|
+
One line up front: the server is alive, building, and where it will land. */
|
|
310
|
+
abideLog.info(`starting dev server at http://localhost:${port} — building client…`)
|
|
234
311
|
const firstBuild = await build(buildOptions)
|
|
235
|
-
if (
|
|
312
|
+
if (firstBuild) {
|
|
313
|
+
currentAppDir = firstBuild.appDir
|
|
314
|
+
} else {
|
|
315
|
+
// Leave currentAppDir at the `${cwd}/dist/_app` fallback; the worker will serve
|
|
316
|
+
// broken assets until a save produces a real generation, matching prior behaviour.
|
|
236
317
|
abideLog.warn('initial build failed — fix the error and save to retry')
|
|
237
318
|
}
|
|
238
|
-
|
|
319
|
+
// The initial worker prints the surface map once (printSurface); respawns don't.
|
|
320
|
+
server = spawnWorker(port, currentAppDir, true).proc
|
|
239
321
|
|
|
240
322
|
/*
|
|
241
323
|
ABIDE_DEV_NO_WATCH=1 skips the fs watcher: rebuild only on demand via POST
|
|
@@ -268,7 +350,7 @@ const watcher = manualRebuild
|
|
|
268
350
|
})
|
|
269
351
|
if (manualRebuild) {
|
|
270
352
|
abideLog.info(
|
|
271
|
-
`manual rebuild mode — POST http://localhost:${port}
|
|
353
|
+
`manual rebuild mode — POST http://localhost:${port}${DEV_REBUILD_PATH} to apply changes`,
|
|
272
354
|
)
|
|
273
355
|
}
|
|
274
356
|
|
|
@@ -279,6 +361,9 @@ const shutdown = async () => {
|
|
|
279
361
|
if (server) {
|
|
280
362
|
await stopWorker(server)
|
|
281
363
|
}
|
|
364
|
+
// Drop the active generation dir on the way out; any other leftover is swept on the
|
|
365
|
+
// next dev start by pruneStaleBuildDirs.
|
|
366
|
+
await pruneGenDir(currentAppDir)
|
|
282
367
|
process.exit(0)
|
|
283
368
|
}
|
|
284
369
|
process.on('SIGINT', shutdown)
|
package/src/discoveryEntry.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { rpc } from './_virtual/rpc.ts'
|
|
|
3
3
|
// @ts-expect-error virtual module resolved by abideResolverPlugin
|
|
4
4
|
import { sockets } from './_virtual/sockets.ts'
|
|
5
5
|
import type { CliManifestEntry } from './lib/cli/types/CliManifestEntry.ts'
|
|
6
|
+
import { resolveInputJsonSchema } from './lib/server/rpc/resolveRpcJsonSchema.ts'
|
|
6
7
|
import { rpcRegistry } from './lib/server/rpc/rpcRegistry.ts'
|
|
7
8
|
import { socketOperations } from './lib/server/sockets/socketOperations.ts'
|
|
8
9
|
import { socketRegistry } from './lib/server/sockets/socketRegistry.ts'
|
|
@@ -24,13 +25,13 @@ await Promise.all([
|
|
|
24
25
|
const manifest: Record<string, CliManifestEntry> = {}
|
|
25
26
|
|
|
26
27
|
for (const entry of rpcRegistry.values()) {
|
|
27
|
-
if (!entry.clients.cli) {
|
|
28
|
+
if (!entry.remote.clients.cli) {
|
|
28
29
|
continue
|
|
29
30
|
}
|
|
30
31
|
manifest[commandNameForUrl(entry.remote.url)] = {
|
|
31
32
|
method: entry.remote.method,
|
|
32
33
|
url: entry.remote.url,
|
|
33
|
-
jsonSchema:
|
|
34
|
+
jsonSchema: resolveInputJsonSchema(entry) ?? jsonSchemaForSchema(undefined),
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|