From 6c0ea915a896f2e9655b1f60d0ccf4acfa726557 Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 06:43:15 +0000 Subject: [PATCH 1/8] feat: expose preview automation APIs --- .changeset/fuzzy-browsers-connect.md | 5 ++ README.md | 69 ++++++++++++++++++++++++++++ package.json | 38 +++++++++------ src/auth/index.ts | 2 + src/auth/pairing.ts | 13 ++++-- src/auth/service.ts | 3 +- src/auth/transport.ts | 4 ++ src/auth/type.ts | 6 +++ src/cli/auth.ts | 8 +++- src/contracts/index.ts | 17 ++++++- src/orchestration/layer.ts | 36 +++++++++++++++ src/orchestration/service.ts | 9 +++- src/preview-viewport/index.ts | 34 ++++++++++++++ src/preview/index.ts | 11 +++++ src/preview/service.ts | 52 +++++++++++++++++++++ src/rpc/error.ts | 14 +++--- src/rpc/operation.ts | 12 +++-- src/rpc/ws-group.ts | 16 +++++-- src/runtime/index.ts | 1 + src/runtime/layer.ts | 5 ++ tsconfig.dts.json | 10 +++- vite.config.ts | 13 +++++- 22 files changed, 334 insertions(+), 44 deletions(-) create mode 100644 .changeset/fuzzy-browsers-connect.md create mode 100644 src/preview-viewport/index.ts create mode 100644 src/preview/index.ts create mode 100644 src/preview/service.ts diff --git a/.changeset/fuzzy-browsers-connect.md b/.changeset/fuzzy-browsers-connect.md new file mode 100644 index 0000000..9fa507f --- /dev/null +++ b/.changeset/fuzzy-browsers-connect.md @@ -0,0 +1,5 @@ +--- +"t3code-cli": minor +--- + +add public preview automation, pairing metadata, shell snapshot, and viewport APIs diff --git a/README.md b/README.md index 3f2adf1..5a2e208 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,75 @@ t3cli --environment ... # Use a spec - Set `T3CLI_ENV=` to select an environment when `--environment` is omitted - `T3CODE_URL` and `T3CODE_TOKEN` override the selected environment only when both are set +## Programmatic API + +Pairing accepts T3 client presentation metadata. Automation clients should identify themselves as +bots and provide a label that users can recognize in T3 Code: + +```ts +import * as Effect from "effect/Effect"; +import { T3AuthPairing } from "t3code-cli/auth"; +import { T3AuthPairingLayer } from "t3code-cli/runtime"; + +const pair = Effect.gen(function* () { + const auth = yield* T3AuthPairing; + return yield* auth.pair({ + pairingUrl, + clientMetadata: { + label: "aperture bridge", + deviceType: "bot", + os: "linux", + }, + }); +}).pipe(Effect.provide(T3AuthPairingLayer)); +``` + +`T3PreviewAutomation` registers a preview host and exposes T3's request stream, response RPC, and +focus RPC. Compose its live layer with the existing connection layers when supplying credentials +directly: + +```ts +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Stream from "effect/Stream"; +import { T3CodeConnectionProviderLive } from "t3code-cli/connection"; +import { T3CodeNodeRpcLayer } from "t3code-cli/node"; +import { T3PreviewAutomation, T3PreviewAutomationLive } from "t3code-cli/preview"; +import { T3RpcOperationsLive } from "t3code-cli/rpc"; + +const connectionLayer = T3CodeConnectionProviderLive({ + origin: { url }, + auth: { token }, +}); +const rpcLayer = T3CodeNodeRpcLayer.pipe(Layer.provide(connectionLayer)); +const previewLayer = T3PreviewAutomationLive.pipe( + Layer.provide(T3RpcOperationsLive.pipe(Layer.provide(rpcLayer))), +); + +const runHost = Effect.gen(function* () { + const preview = yield* T3PreviewAutomation; + yield* preview + .connect({ clientId, environmentId, supportedOperations }) + .pipe(Stream.runForEach(handlePreviewEvent)); +}).pipe(Effect.scoped, Effect.provide(previewLayer)); +``` + +`T3PreviewAutomationLayer` is the shorter CLI-config-backed layer for callers that use a selected +`t3cli` environment. + +`T3Orchestration.watchShellSnapshots()` emits the initial shell snapshot and a reduced snapshot for +each later project or thread event. A new full snapshot resets the reducer after reconnects. + +The viewport catalog and resolver are available without importing T3's private workspace packages: + +```ts +import { + PREVIEW_VIEWPORT_PRESETS, + PreviewViewportSetting, + resolvePreviewViewport, +} from "t3code-cli/preview-viewport"; +``` + ## Project Management ```sh diff --git a/package.json b/package.json index 299cc5d..c0e46da 100644 --- a/package.json +++ b/package.json @@ -26,72 +26,82 @@ "src" ], "type": "module", - "types": "./dist/src/index.d.ts", + "types": "./dist/index.d.ts", "exports": { ".": { - "types": "./dist/src/index.d.ts", + "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" }, "./application": { - "types": "./dist/src/application/index.d.ts", + "types": "./dist/application.d.ts", "import": "./dist/application.js", "default": "./dist/application.js" }, "./auth": { - "types": "./dist/src/auth/index.d.ts", + "types": "./dist/auth.d.ts", "import": "./dist/auth.js", "default": "./dist/auth.js" }, "./cli": { - "types": "./dist/src/cli/index.d.ts", + "types": "./dist/cli.d.ts", "import": "./dist/cli.js", "default": "./dist/cli.js" }, "./config": { - "types": "./dist/src/config/index.d.ts", + "types": "./dist/config.d.ts", "import": "./dist/config.js", "default": "./dist/config.js" }, "./connection": { - "types": "./dist/src/connection/index.d.ts", + "types": "./dist/connection.d.ts", "import": "./dist/connection.js", "default": "./dist/connection.js" }, "./contracts": { - "types": "./dist/src/contracts/index.d.ts", + "types": "./dist/contracts.d.ts", "import": "./dist/contracts.js", "default": "./dist/contracts.js" }, "./node": { - "types": "./dist/src/node/index.d.ts", + "types": "./dist/node.d.ts", "import": "./dist/node.js", "default": "./dist/node.js" }, "./orchestration": { - "types": "./dist/src/orchestration/index.d.ts", + "types": "./dist/orchestration.d.ts", "import": "./dist/orchestration.js", "default": "./dist/orchestration.js" }, + "./preview": { + "types": "./dist/preview.d.ts", + "import": "./dist/preview.js", + "default": "./dist/preview.js" + }, + "./preview-viewport": { + "types": "./dist/preview-viewport.d.ts", + "import": "./dist/preview-viewport.js", + "default": "./dist/preview-viewport.js" + }, "./rpc": { - "types": "./dist/src/rpc/index.d.ts", + "types": "./dist/rpc.d.ts", "import": "./dist/rpc.js", "default": "./dist/rpc.js" }, "./runtime": { - "types": "./dist/src/runtime/index.d.ts", + "types": "./dist/runtime.d.ts", "import": "./dist/runtime.js", "default": "./dist/runtime.js" }, "./t3tools": { - "types": "./dist/src/t3tools/index.d.ts", + "types": "./dist/t3tools.d.ts", "import": "./dist/t3tools.js", "default": "./dist/t3tools.js" }, "./package.json": "./package.json" }, "scripts": { - "build": "vp pack && tsc -p tsconfig.dts.json", + "build": "vp pack", "check": "vp check", "format": "vp fmt", "format:check": "vp fmt --check", diff --git a/src/auth/index.ts b/src/auth/index.ts index a97c150..021cc71 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1,4 +1,5 @@ export { T3Auth } from "./service.ts"; +export type { AuthClientPresentationMetadata } from "../contracts/index.ts"; export { T3AuthLive, makeT3Auth } from "./layer.ts"; export { T3LocalAuth, T3LocalAuthLive, makeT3LocalAuth } from "./local.ts"; export { T3LocalAuthOrigin, T3LocalAuthOriginLive, makeT3LocalAuthOrigin } from "./local-origin.ts"; @@ -29,6 +30,7 @@ export type { LocalAuthResult, LocalAuthTokenInput, LocalAuthTokenResult, + AuthPairInput, PairingUrl, PairResult, PersistEnvironmentInput, diff --git a/src/auth/pairing.ts b/src/auth/pairing.ts index 7bc613d..605d70b 100644 --- a/src/auth/pairing.ts +++ b/src/auth/pairing.ts @@ -5,13 +5,13 @@ import { Url } from "effect/unstable/http"; import { AuthPairingUrlError, AuthTransportError } from "./error.ts"; import { T3AuthTransport } from "./transport.ts"; -import type { PairingUrl, PairResult } from "./type.ts"; +import type { AuthPairInput, PairingUrl, PairResult } from "./type.ts"; export class T3AuthPairing extends Context.Service< T3AuthPairing, { readonly pair: ( - pairingUrl: string, + input: AuthPairInput, ) => Effect.Effect; } >()("t3cli/T3AuthPairing") {} @@ -19,9 +19,12 @@ export class T3AuthPairing extends Context.Service< export const makeT3AuthPairing = Effect.fn("makeT3AuthPairing")(function* () { const transport = yield* T3AuthTransport; - const pair = Effect.fn("T3AuthPairingLive.pair")(function* (pairingUrl: string) { - const parsed = yield* parsePairingUrl(pairingUrl); - const result = yield* transport.bootstrapBearer(parsed); + const pair = Effect.fn("T3AuthPairingLive.pair")(function* (input: AuthPairInput) { + const parsed = yield* parsePairingUrl(input.pairingUrl); + const result = yield* transport.bootstrapBearer({ + ...parsed, + ...(input.clientMetadata !== undefined ? { clientMetadata: input.clientMetadata } : {}), + }); return { url: parsed.baseUrl, token: result.sessionToken, diff --git a/src/auth/service.ts b/src/auth/service.ts index 04477c0..97a7159 100644 --- a/src/auth/service.ts +++ b/src/auth/service.ts @@ -11,6 +11,7 @@ import type { AuthUseResult, LocalAuthInput, LocalAuthResult, + AuthPairInput, PairResult, PersistEnvironmentInput, } from "./type.ts"; @@ -18,7 +19,7 @@ import type { export class T3Auth extends Context.Service< T3Auth, { - readonly pair: (value: string) => Effect.Effect; + readonly pair: (input: AuthPairInput) => Effect.Effect; readonly local: (input: LocalAuthInput) => Effect.Effect; readonly writeConfig: (input: AuthConfigInput) => Effect.Effect; readonly persistEnvironment: ( diff --git a/src/auth/transport.ts b/src/auth/transport.ts index 41eb775..695ddf9 100644 --- a/src/auth/transport.ts +++ b/src/auth/transport.ts @@ -9,6 +9,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import { HttpClient } from "effect/unstable/http"; +import type { AuthClientPresentationMetadata } from "../contracts/index.ts"; import { AuthTransportError } from "./error.ts"; import { type AuthBearerBootstrapResult, @@ -27,6 +28,7 @@ export class T3AuthTransport extends Context.Service< readonly bootstrapBearer: (input: { readonly baseUrl: string; readonly credential: string; + readonly clientMetadata?: AuthClientPresentationMetadata; }) => Effect.Effect; readonly getSession: ( connection: AuthTransportConnection, @@ -43,10 +45,12 @@ const makeT3AuthTransport = Effect.fn("makeT3AuthTransport")(function* () { const bootstrapBearer = Effect.fn("AuthTransport.bootstrapBearer")(function* (input: { readonly baseUrl: string; readonly credential: string; + readonly clientMetadata?: AuthClientPresentationMetadata; }) { const result = yield* bootstrapRemoteBearerSession({ httpBaseUrl: input.baseUrl, credential: input.credential, + ...(input.clientMetadata !== undefined ? { clientMetadata: input.clientMetadata } : {}), }).pipe( Effect.provideService(HttpClient.HttpClient, httpClient), Effect.mapError( diff --git a/src/auth/type.ts b/src/auth/type.ts index 63052f6..daa976a 100644 --- a/src/auth/type.ts +++ b/src/auth/type.ts @@ -1,3 +1,4 @@ +import type { AuthClientPresentationMetadata } from "../contracts/index.ts"; import type { AuthBearerBootstrapResult } from "./schema.ts"; import type { AuthSessionState } from "./schema.ts"; @@ -8,6 +9,11 @@ export type PairingUrl = { readonly credential: string; }; +export interface AuthPairInput { + readonly pairingUrl: string; + readonly clientMetadata?: AuthClientPresentationMetadata; +} + export type AuthConfigInput = { readonly name: string; readonly url: string; diff --git a/src/cli/auth.ts b/src/cli/auth.ts index d7b8901..e678bf9 100644 --- a/src/cli/auth.ts +++ b/src/cli/auth.ts @@ -75,7 +75,13 @@ const pairCommand = Command.make( const t3CliEnv = yield* loadT3CliEnv; const output = yield* T3Output; const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json"); - const result = yield* auth.pair(url); + const result = yield* auth.pair({ + pairingUrl: url, + clientMetadata: { + label: "t3cli", + deviceType: "bot", + }, + }); const fallbackName = yield* auth.defaultNameFromUrl(result.url); const environmentName = yield* persistAuthEnvironment({ explicitName: name, diff --git a/src/contracts/index.ts b/src/contracts/index.ts index 03992d6..469b2fa 100644 --- a/src/contracts/index.ts +++ b/src/contracts/index.ts @@ -1,8 +1,21 @@ +export type { AuthClientPresentationMetadata } from "../../upstream-t3code/packages/contracts/src/auth.ts"; export type { OrchestrationMessage, OrchestrationProjectShell, OrchestrationShellSnapshot, OrchestrationThread, OrchestrationThreadShell, - ServerProvider, -} from "@t3tools/contracts"; +} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; +export type { + PreviewAutomationHost, + PreviewAutomationHostFocus, + PreviewAutomationResizeInput, + PreviewAutomationResponse, + PreviewAutomationStreamEvent, +} from "../../upstream-t3code/packages/contracts/src/previewAutomation.ts"; +export type { PreviewViewportPresetId } from "../../upstream-t3code/packages/contracts/src/preview.ts"; +export { + PREVIEW_VIEWPORT_PRESET_IDS, + PreviewViewportSetting, +} from "../../upstream-t3code/packages/contracts/src/preview.ts"; +export type { ServerProvider } from "../../upstream-t3code/packages/contracts/src/server.ts"; diff --git a/src/orchestration/layer.ts b/src/orchestration/layer.ts index 9816249..371f768 100644 --- a/src/orchestration/layer.ts +++ b/src/orchestration/layer.ts @@ -8,17 +8,52 @@ import { ThreadId, WS_METHODS, type ClientOrchestrationCommand, + type OrchestrationShellSnapshot, type OrchestrationShellStreamItem, type OrchestrationThreadStreamItem, } from "@t3tools/contracts"; import { RpcError } from "../rpc/error.ts"; import { T3RpcOperations } from "../rpc/operation.ts"; +import { applyShellStreamEvent } from "../../upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"; import { T3Orchestration, type OpenThread, type Orchestration } from "./service.ts"; export const makeT3Orchestration = Effect.fn("makeT3Orchestration")(function* () { const rpc = yield* T3RpcOperations; + const watchShellSnapshots: Orchestration["watchShellSnapshots"] = () => + rpc + .subscribe(ORCHESTRATION_WS_METHODS.subscribeShell, (client) => + client[ORCHESTRATION_WS_METHODS.subscribeShell]({}), + ) + .pipe( + Stream.filter( + ( + item, + ): item is Exclude => + item.kind !== "synchronized", + ), + Stream.mapAccum( + () => Option.none(), + ( + current, + item, + ): readonly [ + Option.Option, + ReadonlyArray, + ] => { + if (item.kind === "snapshot") { + return [Option.some(item.snapshot), [item.snapshot]]; + } + if (Option.isNone(current)) { + return [current, []]; + } + const next = applyShellStreamEvent(current.value, item); + return [Option.some(next), [next]]; + }, + ), + ); + const watchShellSequence: Orchestration["watchShellSequence"] = () => rpc .subscribe(ORCHESTRATION_WS_METHODS.subscribeShell, (client) => @@ -126,6 +161,7 @@ export const makeT3Orchestration = Effect.fn("makeT3Orchestration")(function* () getShellSnapshot, getArchivedShellSnapshot, getThreadSnapshot, + watchShellSnapshots, watchShellSequence, watchThreadItems, openThread, diff --git a/src/orchestration/service.ts b/src/orchestration/service.ts index 0b415bf..6efaaab 100644 --- a/src/orchestration/service.ts +++ b/src/orchestration/service.ts @@ -9,8 +9,8 @@ import type { OrchestrationShellSnapshot, OrchestrationThread, OrchestrationThreadStreamItem, - ServerProviders, -} from "@t3tools/contracts"; +} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; +import type { ServerProviders } from "../../upstream-t3code/packages/contracts/src/server.ts"; import type { RpcError } from "../rpc/error.ts"; @@ -38,6 +38,11 @@ export type Orchestration = { readonly getThreadSnapshot: ( threadId: string, ) => Effect.Effect; + readonly watchShellSnapshots: () => Stream.Stream< + OrchestrationShellSnapshot, + OrchestrationError, + Scope.Scope + >; readonly watchShellSequence: () => Stream.Stream; readonly watchThreadItems: ( threadId: string, diff --git a/src/preview-viewport/index.ts b/src/preview-viewport/index.ts new file mode 100644 index 0000000..47e1f7d --- /dev/null +++ b/src/preview-viewport/index.ts @@ -0,0 +1,34 @@ +import { + PREVIEW_VIEWPORT_PRESETS as T3_PREVIEW_VIEWPORT_PRESETS, + resolvePreviewViewport as resolveT3PreviewViewport, +} from "../../upstream-t3code/packages/shared/src/previewViewport.ts"; + +import type { + PreviewAutomationResizeInput, + PreviewViewportPresetId, + PreviewViewportSetting as PreviewViewportSettingType, +} from "../contracts/index.ts"; + +export { + PREVIEW_VIEWPORT_PRESET_IDS, + PreviewViewportSetting, + type PreviewViewportPresetId, +} from "../contracts/index.ts"; + +export interface PreviewViewportPreset { + readonly id: PreviewViewportPresetId; + readonly label: string; + readonly category: "Desktop" | "Tablet" | "Phone"; + readonly detail: string; + readonly width: number; + readonly height: number; +} + +export const PREVIEW_VIEWPORT_PRESETS: ReadonlyArray = + T3_PREVIEW_VIEWPORT_PRESETS; + +export function resolvePreviewViewport( + input: PreviewAutomationResizeInput, +): PreviewViewportSettingType { + return resolveT3PreviewViewport(input); +} diff --git a/src/preview/index.ts b/src/preview/index.ts new file mode 100644 index 0000000..57c5acb --- /dev/null +++ b/src/preview/index.ts @@ -0,0 +1,11 @@ +export { + T3PreviewAutomation, + T3PreviewAutomationLive, + makeT3PreviewAutomation, +} from "./service.ts"; +export type { + PreviewAutomationHost, + PreviewAutomationHostFocus, + PreviewAutomationResponse, + PreviewAutomationStreamEvent, +} from "../contracts/index.ts"; diff --git a/src/preview/service.ts b/src/preview/service.ts new file mode 100644 index 0000000..1bc2801 --- /dev/null +++ b/src/preview/service.ts @@ -0,0 +1,52 @@ +import { WS_METHODS } from "@t3tools/contracts"; +import * as Context from "effect/Context"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import type * as Stream from "effect/Stream"; + +import type { + PreviewAutomationHost, + PreviewAutomationHostFocus, + PreviewAutomationResponse, + PreviewAutomationStreamEvent, +} from "../contracts/index.ts"; +import type { RpcError } from "../rpc/error.ts"; +import { T3RpcOperations } from "../rpc/operation.ts"; + +export class T3PreviewAutomation extends Context.Service< + T3PreviewAutomation, + { + readonly connect: ( + host: PreviewAutomationHost, + ) => Stream.Stream; + readonly respond: (response: PreviewAutomationResponse) => Effect.Effect; + readonly focusHost: (input: PreviewAutomationHostFocus) => Effect.Effect; + } +>()("t3cli/T3PreviewAutomation") {} + +export const makeT3PreviewAutomation = Effect.fn("makeT3PreviewAutomation")(function* () { + const rpc = yield* T3RpcOperations; + + const connect: T3PreviewAutomation["Service"]["connect"] = (host) => + rpc.subscribe(WS_METHODS.previewAutomationConnect, (client) => + client[WS_METHODS.previewAutomationConnect](host), + ); + const respond = Effect.fn("T3PreviewAutomation.respond")(function* ( + response: PreviewAutomationResponse, + ) { + return yield* rpc.run(WS_METHODS.previewAutomationRespond, (client) => + client[WS_METHODS.previewAutomationRespond](response), + ); + }); + const focusHost = Effect.fn("T3PreviewAutomation.focusHost")(function* ( + input: PreviewAutomationHostFocus, + ) { + return yield* rpc.run(WS_METHODS.previewAutomationFocusHost, (client) => + client[WS_METHODS.previewAutomationFocusHost](input), + ); + }); + + return T3PreviewAutomation.of({ connect, respond, focusHost }); +}); + +export const T3PreviewAutomationLive = Layer.effect(T3PreviewAutomation, makeT3PreviewAutomation()); diff --git a/src/rpc/error.ts b/src/rpc/error.ts index abdf037..a11f34e 100644 --- a/src/rpc/error.ts +++ b/src/rpc/error.ts @@ -1,15 +1,16 @@ import { ConnectionBlockedError, ConnectionTransientError, -} from "@t3tools/client-runtime/connection"; +} from "../../upstream-t3code/packages/client-runtime/src/connection/model.ts"; +import { EnvironmentAuthorizationError } from "../../upstream-t3code/packages/contracts/src/auth.ts"; +import { KeybindingsConfigError } from "../../upstream-t3code/packages/contracts/src/keybindings.ts"; import { - EnvironmentAuthorizationError, - KeybindingsConfigError, OrchestrationDispatchCommandError, OrchestrationGetSnapshotError, - ServerSettingsError, - TerminalError, -} from "@t3tools/contracts"; +} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; +import { PreviewAutomationError } from "../../upstream-t3code/packages/contracts/src/previewAutomation.ts"; +import { ServerSettingsError } from "../../upstream-t3code/packages/contracts/src/settings.ts"; +import { TerminalError } from "../../upstream-t3code/packages/contracts/src/terminal.ts"; import * as Schema from "effect/Schema"; import { RpcClientError } from "effect/unstable/rpc"; @@ -21,6 +22,7 @@ const RpcErrorCauseSchema = Schema.Union([ KeybindingsConfigError, OrchestrationDispatchCommandError, OrchestrationGetSnapshotError, + PreviewAutomationError, ServerSettingsError, TerminalError, ConnectionBlockedError, diff --git a/src/rpc/operation.ts b/src/rpc/operation.ts index eff7e70..69a2b26 100644 --- a/src/rpc/operation.ts +++ b/src/rpc/operation.ts @@ -1,11 +1,12 @@ +import type { EnvironmentAuthorizationError } from "../../upstream-t3code/packages/contracts/src/auth.ts"; +import type { KeybindingsConfigError } from "../../upstream-t3code/packages/contracts/src/keybindings.ts"; import type { - EnvironmentAuthorizationError, - KeybindingsConfigError, OrchestrationDispatchCommandError, OrchestrationGetSnapshotError, - ServerSettingsError, - TerminalError, -} from "@t3tools/contracts"; +} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; +import type { PreviewAutomationError } from "../../upstream-t3code/packages/contracts/src/previewAutomation.ts"; +import type { ServerSettingsError } from "../../upstream-t3code/packages/contracts/src/settings.ts"; +import type { TerminalError } from "../../upstream-t3code/packages/contracts/src/terminal.ts"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; @@ -22,6 +23,7 @@ export type CliRpcOperationError = | KeybindingsConfigError | OrchestrationDispatchCommandError | OrchestrationGetSnapshotError + | PreviewAutomationError | RpcClientError.RpcClientError | ServerSettingsError | TerminalError; diff --git a/src/rpc/ws-group.ts b/src/rpc/ws-group.ts index 39d512d..3265f0a 100644 --- a/src/rpc/ws-group.ts +++ b/src/rpc/ws-group.ts @@ -1,13 +1,14 @@ +import { EnvironmentAuthorizationError } from "../../upstream-t3code/packages/contracts/src/auth.ts"; +import { KeybindingsConfigError } from "../../upstream-t3code/packages/contracts/src/keybindings.ts"; import { - EnvironmentAuthorizationError, - KeybindingsConfigError, - ServerProviders, - ServerSettingsError, WS_METHODS, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc, + WsPreviewAutomationConnectRpc, + WsPreviewAutomationFocusHostRpc, + WsPreviewAutomationRespondRpc, WsServerProbeRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, @@ -16,7 +17,9 @@ import { WsTerminalOpenRpc, WsTerminalResizeRpc, WsTerminalWriteRpc, -} from "@t3tools/contracts"; +} from "../../upstream-t3code/packages/contracts/src/rpc.ts"; +import { ServerProviders } from "../../upstream-t3code/packages/contracts/src/server.ts"; +import { ServerSettingsError } from "../../upstream-t3code/packages/contracts/src/settings.ts"; import * as Schema from "effect/Schema"; import { Rpc, RpcGroup } from "effect/unstable/rpc"; @@ -48,6 +51,9 @@ export const CliWsRpcGroup = RpcGroup.make( WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc, + WsPreviewAutomationConnectRpc, + WsPreviewAutomationRespondRpc, + WsPreviewAutomationFocusHostRpc, WsServerProbeRpc, WsServerGetConfigRpc, ); diff --git a/src/runtime/index.ts b/src/runtime/index.ts index 50c5ad6..af12fb5 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -10,4 +10,5 @@ export { T3LocalAuthOriginLayer, T3LocalAuthTokenLayer, T3OrchestrationLayer, + T3PreviewAutomationLayer, } from "./layer.ts"; diff --git a/src/runtime/layer.ts b/src/runtime/layer.ts index a3f96d1..931134c 100644 --- a/src/runtime/layer.ts +++ b/src/runtime/layer.ts @@ -17,6 +17,7 @@ import { T3CodeConnectionError } from "../connection/error.ts"; import { T3PreparedConnectionProviderLive } from "../connection/prepared.ts"; import { T3CodeConnectionProvider, makeT3CodeConnectionProvider } from "../connection/service.ts"; import { T3OrchestrationLive } from "../orchestration/layer.ts"; +import { T3PreviewAutomationLive } from "../preview/service.ts"; import { T3RpcLive } from "../rpc/layer.ts"; import { T3RpcOperationsLive } from "../rpc/operation.ts"; import { T3RpcSessionFactoryLive } from "../rpc/session.ts"; @@ -74,6 +75,9 @@ const T3RpcLayer = T3RpcLive.pipe( ); const T3RpcOperationsLayer = T3RpcOperationsLive.pipe(Layer.provide(T3RpcLayer)); export const T3OrchestrationLayer = T3OrchestrationLive.pipe(Layer.provide(T3RpcOperationsLayer)); +export const T3PreviewAutomationLayer = T3PreviewAutomationLive.pipe( + Layer.provide(T3RpcOperationsLayer), +); const T3ApplicationLayer = T3ApplicationLive.pipe( Layer.provide(Layer.mergeAll(T3RpcOperationsLayer, T3OrchestrationLayer)), ); @@ -84,6 +88,7 @@ export const BaseAppLayer = Layer.mergeAll( T3RpcLayer, T3RpcOperationsLayer, T3OrchestrationLayer, + T3PreviewAutomationLayer, T3ApplicationLayer, NodeCliPathLayer, ); diff --git a/tsconfig.dts.json b/tsconfig.dts.json index 779d888..5ce6ab3 100644 --- a/tsconfig.dts.json +++ b/tsconfig.dts.json @@ -2,12 +2,18 @@ "extends": "./tsconfig.json", "compilerOptions": { "declaration": true, - "declarationDir": "dist", "emitDeclarationOnly": true, "noEmit": false, + "paths": { + "@t3tools/client-runtime/*": ["./upstream-t3code/packages/client-runtime/src/*/index.ts"], + "@t3tools/contracts": ["./upstream-t3code/packages/contracts/src/index.ts"], + "@t3tools/contracts/*": ["./upstream-t3code/packages/contracts/src/*.ts"], + "@t3tools/shared/*": ["./upstream-t3code/packages/shared/src/*.ts"] + }, "rootDir": "." }, "include": [ + "src/bin.ts", "src/index.ts", "src/application/index.ts", "src/auth/index.ts", @@ -17,6 +23,8 @@ "src/contracts/index.ts", "src/node/index.ts", "src/orchestration/index.ts", + "src/preview/index.ts", + "src/preview-viewport/index.ts", "src/rpc/index.ts", "src/runtime/index.ts", "src/t3tools/index.ts" diff --git a/vite.config.ts b/vite.config.ts index be85981..70f48ae 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -30,15 +30,24 @@ export default defineConfig({ contracts: "src/contracts/index.ts", node: "src/node/index.ts", orchestration: "src/orchestration/index.ts", + preview: "src/preview/index.ts", + "preview-viewport": "src/preview-viewport/index.ts", rpc: "src/rpc/index.ts", runtime: "src/runtime/index.ts", t3tools: "src/t3tools/index.ts", }, deps: { alwaysBundle: shouldBundlePackDependency, + dts: { + alwaysBundle: /^@t3tools\//, + neverBundle: true, + }, onlyBundle: false, }, - dts: false, + dts: { + eager: true, + tsconfig: "tsconfig.dts.json", + }, fixedExtension: false, format: "esm", hash: false, @@ -47,7 +56,7 @@ export default defineConfig({ codeSplitting: { groups: [ { - name: "shared", + name: (id) => (id.endsWith(".d.ts") ? "shared.d" : "shared"), minShareCount: 2, }, ], From 3aa393ba6c3936db64d7790a4a834ea5d5b5a466 Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 06:52:10 +0000 Subject: [PATCH 2/8] refactor: use bundled t3 imports --- package.json | 1 + pnpm-lock.yaml | 3 +++ src/contracts/index.ts | 15 +++++---------- src/orchestration/layer.ts | 2 +- src/orchestration/service.ts | 4 ++-- src/preview-viewport/index.ts | 2 +- src/rpc/error.ts | 14 +++++++------- src/rpc/operation.ts | 12 ++++++------ src/rpc/ws-group.ts | 10 +++++----- tsconfig.dts.json | 1 + tsconfig.json | 3 +++ 11 files changed, 35 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index c0e46da..f10c074 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,7 @@ "@effect/vitest": "catalog:", "@t3tools/client-runtime": "link:upstream-t3code/packages/client-runtime", "@t3tools/contracts": "workspace:*", + "@t3tools/shared": "link:upstream-t3code/packages/shared", "@total-typescript/shoehorn": "^0.1.2", "@types/node": "^26.1.2", "typescript": "^7.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae0ac8b..598169f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,6 +77,9 @@ importers: '@t3tools/contracts': specifier: workspace:* version: link:upstream-t3code/packages/contracts + '@t3tools/shared': + specifier: link:upstream-t3code/packages/shared + version: link:upstream-t3code/packages/shared '@total-typescript/shoehorn': specifier: ^0.1.2 version: 0.1.2 diff --git a/src/contracts/index.ts b/src/contracts/index.ts index 469b2fa..521d7d6 100644 --- a/src/contracts/index.ts +++ b/src/contracts/index.ts @@ -1,21 +1,16 @@ -export type { AuthClientPresentationMetadata } from "../../upstream-t3code/packages/contracts/src/auth.ts"; export type { + AuthClientPresentationMetadata, OrchestrationMessage, OrchestrationProjectShell, OrchestrationShellSnapshot, OrchestrationThread, OrchestrationThreadShell, -} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; -export type { PreviewAutomationHost, PreviewAutomationHostFocus, PreviewAutomationResizeInput, PreviewAutomationResponse, PreviewAutomationStreamEvent, -} from "../../upstream-t3code/packages/contracts/src/previewAutomation.ts"; -export type { PreviewViewportPresetId } from "../../upstream-t3code/packages/contracts/src/preview.ts"; -export { - PREVIEW_VIEWPORT_PRESET_IDS, - PreviewViewportSetting, -} from "../../upstream-t3code/packages/contracts/src/preview.ts"; -export type { ServerProvider } from "../../upstream-t3code/packages/contracts/src/server.ts"; + PreviewViewportPresetId, + ServerProvider, +} from "@t3tools/contracts"; +export { PREVIEW_VIEWPORT_PRESET_IDS, PreviewViewportSetting } from "@t3tools/contracts"; diff --git a/src/orchestration/layer.ts b/src/orchestration/layer.ts index 371f768..485f7bb 100644 --- a/src/orchestration/layer.ts +++ b/src/orchestration/layer.ts @@ -15,7 +15,7 @@ import { import { RpcError } from "../rpc/error.ts"; import { T3RpcOperations } from "../rpc/operation.ts"; -import { applyShellStreamEvent } from "../../upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"; +import { applyShellStreamEvent } from "#t3-shell-reducer"; import { T3Orchestration, type OpenThread, type Orchestration } from "./service.ts"; export const makeT3Orchestration = Effect.fn("makeT3Orchestration")(function* () { diff --git a/src/orchestration/service.ts b/src/orchestration/service.ts index 6efaaab..021376b 100644 --- a/src/orchestration/service.ts +++ b/src/orchestration/service.ts @@ -9,8 +9,8 @@ import type { OrchestrationShellSnapshot, OrchestrationThread, OrchestrationThreadStreamItem, -} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; -import type { ServerProviders } from "../../upstream-t3code/packages/contracts/src/server.ts"; + ServerProviders, +} from "@t3tools/contracts"; import type { RpcError } from "../rpc/error.ts"; diff --git a/src/preview-viewport/index.ts b/src/preview-viewport/index.ts index 47e1f7d..2c7a7f4 100644 --- a/src/preview-viewport/index.ts +++ b/src/preview-viewport/index.ts @@ -1,7 +1,7 @@ import { PREVIEW_VIEWPORT_PRESETS as T3_PREVIEW_VIEWPORT_PRESETS, resolvePreviewViewport as resolveT3PreviewViewport, -} from "../../upstream-t3code/packages/shared/src/previewViewport.ts"; +} from "@t3tools/shared/previewViewport"; import type { PreviewAutomationResizeInput, diff --git a/src/rpc/error.ts b/src/rpc/error.ts index a11f34e..a895b3a 100644 --- a/src/rpc/error.ts +++ b/src/rpc/error.ts @@ -1,16 +1,16 @@ import { ConnectionBlockedError, ConnectionTransientError, -} from "../../upstream-t3code/packages/client-runtime/src/connection/model.ts"; -import { EnvironmentAuthorizationError } from "../../upstream-t3code/packages/contracts/src/auth.ts"; -import { KeybindingsConfigError } from "../../upstream-t3code/packages/contracts/src/keybindings.ts"; +} from "@t3tools/client-runtime/connection"; import { + EnvironmentAuthorizationError, + KeybindingsConfigError, OrchestrationDispatchCommandError, OrchestrationGetSnapshotError, -} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; -import { PreviewAutomationError } from "../../upstream-t3code/packages/contracts/src/previewAutomation.ts"; -import { ServerSettingsError } from "../../upstream-t3code/packages/contracts/src/settings.ts"; -import { TerminalError } from "../../upstream-t3code/packages/contracts/src/terminal.ts"; + PreviewAutomationError, + ServerSettingsError, + TerminalError, +} from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import { RpcClientError } from "effect/unstable/rpc"; diff --git a/src/rpc/operation.ts b/src/rpc/operation.ts index 69a2b26..716e7de 100644 --- a/src/rpc/operation.ts +++ b/src/rpc/operation.ts @@ -1,12 +1,12 @@ -import type { EnvironmentAuthorizationError } from "../../upstream-t3code/packages/contracts/src/auth.ts"; -import type { KeybindingsConfigError } from "../../upstream-t3code/packages/contracts/src/keybindings.ts"; import type { + EnvironmentAuthorizationError, + KeybindingsConfigError, OrchestrationDispatchCommandError, OrchestrationGetSnapshotError, -} from "../../upstream-t3code/packages/contracts/src/orchestration.ts"; -import type { PreviewAutomationError } from "../../upstream-t3code/packages/contracts/src/previewAutomation.ts"; -import type { ServerSettingsError } from "../../upstream-t3code/packages/contracts/src/settings.ts"; -import type { TerminalError } from "../../upstream-t3code/packages/contracts/src/terminal.ts"; + PreviewAutomationError, + ServerSettingsError, + TerminalError, +} from "@t3tools/contracts"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; diff --git a/src/rpc/ws-group.ts b/src/rpc/ws-group.ts index 3265f0a..03591a5 100644 --- a/src/rpc/ws-group.ts +++ b/src/rpc/ws-group.ts @@ -1,6 +1,8 @@ -import { EnvironmentAuthorizationError } from "../../upstream-t3code/packages/contracts/src/auth.ts"; -import { KeybindingsConfigError } from "../../upstream-t3code/packages/contracts/src/keybindings.ts"; import { + EnvironmentAuthorizationError, + KeybindingsConfigError, + ServerProviders, + ServerSettingsError, WS_METHODS, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetArchivedShellSnapshotRpc, @@ -17,9 +19,7 @@ import { WsTerminalOpenRpc, WsTerminalResizeRpc, WsTerminalWriteRpc, -} from "../../upstream-t3code/packages/contracts/src/rpc.ts"; -import { ServerProviders } from "../../upstream-t3code/packages/contracts/src/server.ts"; -import { ServerSettingsError } from "../../upstream-t3code/packages/contracts/src/settings.ts"; +} from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import { Rpc, RpcGroup } from "effect/unstable/rpc"; diff --git a/tsconfig.dts.json b/tsconfig.dts.json index 5ce6ab3..fb966bc 100644 --- a/tsconfig.dts.json +++ b/tsconfig.dts.json @@ -5,6 +5,7 @@ "emitDeclarationOnly": true, "noEmit": false, "paths": { + "#t3-shell-reducer": ["./upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"], "@t3tools/client-runtime/*": ["./upstream-t3code/packages/client-runtime/src/*/index.ts"], "@t3tools/contracts": ["./upstream-t3code/packages/contracts/src/index.ts"], "@t3tools/contracts/*": ["./upstream-t3code/packages/contracts/src/*.ts"], diff --git a/tsconfig.json b/tsconfig.json index 20d7423..6150252 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,9 @@ "moduleResolution": "NodeNext", "noEmit": true, "noUncheckedIndexedAccess": true, + "paths": { + "#t3-shell-reducer": ["./upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"] + }, "skipLibCheck": true, "strict": true, "target": "ES2024", From c99e5734c114c35f2d48e7c5f005b715fc32721c Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 07:05:10 +0000 Subject: [PATCH 3/8] refactor: replace reducer path alias --- src/orchestration/layer.ts | 2 +- src/orchestration/shell-reducer/index.ts | 1 + tsconfig.dts.json | 1 - tsconfig.json | 3 --- 4 files changed, 2 insertions(+), 5 deletions(-) create mode 100644 src/orchestration/shell-reducer/index.ts diff --git a/src/orchestration/layer.ts b/src/orchestration/layer.ts index 485f7bb..dba21f1 100644 --- a/src/orchestration/layer.ts +++ b/src/orchestration/layer.ts @@ -15,7 +15,7 @@ import { import { RpcError } from "../rpc/error.ts"; import { T3RpcOperations } from "../rpc/operation.ts"; -import { applyShellStreamEvent } from "#t3-shell-reducer"; +import { applyShellStreamEvent } from "./shell-reducer/index.ts"; import { T3Orchestration, type OpenThread, type Orchestration } from "./service.ts"; export const makeT3Orchestration = Effect.fn("makeT3Orchestration")(function* () { diff --git a/src/orchestration/shell-reducer/index.ts b/src/orchestration/shell-reducer/index.ts new file mode 100644 index 0000000..ce53d80 --- /dev/null +++ b/src/orchestration/shell-reducer/index.ts @@ -0,0 +1 @@ +export { applyShellStreamEvent } from "../../../upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"; diff --git a/tsconfig.dts.json b/tsconfig.dts.json index fb966bc..5ce6ab3 100644 --- a/tsconfig.dts.json +++ b/tsconfig.dts.json @@ -5,7 +5,6 @@ "emitDeclarationOnly": true, "noEmit": false, "paths": { - "#t3-shell-reducer": ["./upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"], "@t3tools/client-runtime/*": ["./upstream-t3code/packages/client-runtime/src/*/index.ts"], "@t3tools/contracts": ["./upstream-t3code/packages/contracts/src/index.ts"], "@t3tools/contracts/*": ["./upstream-t3code/packages/contracts/src/*.ts"], diff --git a/tsconfig.json b/tsconfig.json index 6150252..20d7423 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,9 +7,6 @@ "moduleResolution": "NodeNext", "noEmit": true, "noUncheckedIndexedAccess": true, - "paths": { - "#t3-shell-reducer": ["./upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"] - }, "skipLibCheck": true, "strict": true, "target": "ES2024", From de2302bb4d8d4398f0703e4696a944159527f7d1 Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 07:21:42 +0000 Subject: [PATCH 4/8] feat: mirror upstream package exports --- .changeset/fuzzy-browsers-connect.md | 2 +- README.md | 8 +- package.json | 19 ++- patches/@effect__vitest@4.0.0-beta.78.patch | 54 ------- pnpm-lock.yaml | 164 ++++++++++---------- pnpm-workspace.yaml | 14 +- src/orchestration/layer.ts | 2 +- src/orchestration/shell-reducer/index.ts | 1 - src/preview-viewport/index.ts | 34 ---- src/rpc/layer.ts | 9 +- src/rpc/operation.ts | 15 +- src/sql/node-sqlite-client.ts | 3 + tsconfig.dts.json | 12 +- vite.config.ts | 15 +- 14 files changed, 148 insertions(+), 204 deletions(-) delete mode 100644 patches/@effect__vitest@4.0.0-beta.78.patch delete mode 100644 src/orchestration/shell-reducer/index.ts delete mode 100644 src/preview-viewport/index.ts diff --git a/.changeset/fuzzy-browsers-connect.md b/.changeset/fuzzy-browsers-connect.md index 9fa507f..dbe1768 100644 --- a/.changeset/fuzzy-browsers-connect.md +++ b/.changeset/fuzzy-browsers-connect.md @@ -2,4 +2,4 @@ "t3code-cli": minor --- -add public preview automation, pairing metadata, shell snapshot, and viewport APIs +add public preview automation, pairing metadata, shell snapshot, and mirrored T3 package APIs diff --git a/README.md b/README.md index 5a2e208..51c225d 100644 --- a/README.md +++ b/README.md @@ -122,14 +122,16 @@ const runHost = Effect.gen(function* () { `T3Orchestration.watchShellSnapshots()` emits the initial shell snapshot and a reduced snapshot for each later project or thread event. A new full snapshot resets the reducer after reconnects. -The viewport catalog and resolver are available without importing T3's private workspace packages: +T3 Code's shared and client-runtime export maps are mirrored under `t3code-cli/shared/*` and +`t3code-cli/client-runtime/*`. For example, the viewport catalog and resolver are available through +the matching shared subpath: ```ts import { PREVIEW_VIEWPORT_PRESETS, - PreviewViewportSetting, resolvePreviewViewport, -} from "t3code-cli/preview-viewport"; +} from "t3code-cli/shared/previewViewport"; +import { PreviewViewportSetting } from "t3code-cli/contracts"; ``` ## Project Management diff --git a/package.json b/package.json index f10c074..ef31cba 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,11 @@ "import": "./dist/cli.js", "default": "./dist/cli.js" }, + "./client-runtime/*": { + "types": "./dist/client-runtime/*.d.ts", + "import": "./dist/client-runtime/*.js", + "default": "./dist/client-runtime/*.js" + }, "./config": { "types": "./dist/config.d.ts", "import": "./dist/config.js", @@ -78,11 +83,6 @@ "import": "./dist/preview.js", "default": "./dist/preview.js" }, - "./preview-viewport": { - "types": "./dist/preview-viewport.d.ts", - "import": "./dist/preview-viewport.js", - "default": "./dist/preview-viewport.js" - }, "./rpc": { "types": "./dist/rpc.d.ts", "import": "./dist/rpc.js", @@ -93,6 +93,11 @@ "import": "./dist/runtime.js", "default": "./dist/runtime.js" }, + "./shared/*": { + "types": "./dist/shared/*.d.ts", + "import": "./dist/shared/*.js", + "default": "./dist/shared/*.js" + }, "./t3tools": { "types": "./dist/t3tools.d.ts", "import": "./dist/t3tools.js", @@ -116,9 +121,9 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@effect/platform-node": "4.0.0-beta.78", + "@effect/platform-node": "4.0.0-beta.102", "@napi-rs/keyring": "^1.3.0", - "effect": "4.0.0-beta.78", + "effect": "4.0.0-beta.102", "marked": "^15.0.12", "marked-terminal": "^7.3.0", "string-width": "^8.2.2", diff --git a/patches/@effect__vitest@4.0.0-beta.78.patch b/patches/@effect__vitest@4.0.0-beta.78.patch deleted file mode 100644 index 719d748..0000000 --- a/patches/@effect__vitest@4.0.0-beta.78.patch +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/dist/index.d.ts b/dist/index.d.ts ---- a/dist/index.d.ts -+++ b/dist/index.d.ts -@@ -7,8 +7,8 @@ - import type * as Schema from "effect/Schema"; - import type * as Scope from "effect/Scope"; - import type * as FC from "effect/testing/FastCheck"; --import * as V from "vitest"; -+import * as V from "vite-plus/test"; - /** - * @since 4.0.0 - */ --export * from "vitest"; -+export * from "vite-plus/test"; -diff --git a/dist/index.js b/dist/index.js ---- a/dist/index.js -+++ b/dist/index.js -@@ -1,6 +1,6 @@ --import * as V from "vitest"; -+import * as V from "vite-plus/test"; - import * as internal from "./internal/internal.js"; - /** - * @since 4.0.0 - */ --export * from "vitest"; -+export * from "vite-plus/test"; -diff --git a/dist/internal/internal.js b/dist/internal/internal.js ---- a/dist/internal/internal.js -+++ b/dist/internal/internal.js -@@ -1,5 +1,5 @@ - /** - * @since 4.0.0 - */ --import { getCurrentSuite } from "@vitest/runner"; -+import { getCurrentSuite } from "vite-plus/test/plugins/runner"; - import * as Cause from "effect/Cause"; -@@ -14,6 +14,6 @@ - import * as Scope from "effect/Scope"; - import * as fc from "effect/testing/FastCheck"; - import * as TestClock from "effect/testing/TestClock"; - import * as TestConsole from "effect/testing/TestConsole"; --import * as V from "vitest"; -+import * as V from "vite-plus/test"; - const runPromise = /*#__PURE__*/Effect.fnUntraced(function* (effect, _ctx) { -diff --git a/dist/utils.js b/dist/utils.js ---- a/dist/utils.js -+++ b/dist/utils.js -@@ -3,5 +3,5 @@ - import * as Option from "effect/Option"; - import * as Predicate from "effect/Predicate"; - import * as Result from "effect/Result"; - import * as assert from "node:assert"; --import { assert as vassert } from "vitest"; -+import { assert as vassert } from "vite-plus/test"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 598169f..53183d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,23 +7,23 @@ settings: catalogs: default: '@effect/platform-node': - specifier: 4.0.0-beta.78 - version: 4.0.0-beta.78 + specifier: 4.0.0-beta.102 + version: 4.0.0-beta.102 '@effect/vitest': - specifier: 4.0.0-beta.78 - version: 4.0.0-beta.78 + specifier: 4.0.0-beta.102 + version: 4.0.0-beta.102 '@noble/curves': - specifier: 2.2.0 - version: 2.2.0 + specifier: 1.9.1 + version: 1.9.1 '@noble/hashes': - specifier: 2.2.0 - version: 2.2.0 + specifier: 1.8.0 + version: 1.8.0 '@types/node': specifier: ^26.1.2 version: 26.1.2 effect: - specifier: 4.0.0-beta.78 - version: 4.0.0-beta.78 + specifier: 4.0.0-beta.102 + version: 4.0.0-beta.102 jose: specifier: 6.2.4 version: 6.2.4 @@ -35,23 +35,29 @@ catalogs: version: 2.9.0 patchedDependencies: - '@effect/vitest@4.0.0-beta.78': - hash: 74fd480109c3bd975255bf5573a64718451bcae02b523c432bc18e9aa27b52fd - path: patches/@effect__vitest@4.0.0-beta.78.patch + '@effect/platform-node@4.0.0-beta.102': + hash: cae7efcda6fd29f4db2efd357cb7dc6c41cb6adc957901d437e13ed1f3c017f0 + path: upstream-t3code/patches/@effect__platform-node@4.0.0-beta.102.patch + '@effect/vitest@4.0.0-beta.102': + hash: a607339aab944136a084a05f159aa0f5a69776934d4b835dab8f9992f1c13425 + path: upstream-t3code/patches/@effect__vitest@4.0.0-beta.102.patch + effect@4.0.0-beta.102: + hash: 71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488 + path: upstream-t3code/patches/effect@4.0.0-beta.102.patch importers: .: dependencies: '@effect/platform-node': - specifier: 4.0.0-beta.78 - version: 4.0.0-beta.78(effect@4.0.0-beta.78)(ioredis@5.10.1) + specifier: 4.0.0-beta.102 + version: 4.0.0-beta.102(patch_hash=cae7efcda6fd29f4db2efd357cb7dc6c41cb6adc957901d437e13ed1f3c017f0)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(ioredis@5.10.1) '@napi-rs/keyring': specifier: ^1.3.0 version: 1.3.0 effect: - specifier: 4.0.0-beta.78 - version: 4.0.0-beta.78 + specifier: 4.0.0-beta.102 + version: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) marked: specifier: ^15.0.12 version: 15.0.12 @@ -70,7 +76,7 @@ importers: version: 2.31.1(@types/node@26.1.2) '@effect/vitest': specifier: 'catalog:' - version: 4.0.0-beta.78(patch_hash=74fd480109c3bd975255bf5573a64718451bcae02b523c432bc18e9aa27b52fd)(effect@4.0.0-beta.78)(vitest@4.1.10) + version: 4.0.0-beta.102(patch_hash=a607339aab944136a084a05f159aa0f5a69776934d4b835dab8f9992f1c13425)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(vitest@4.1.10) '@t3tools/client-runtime': specifier: link:upstream-t3code/packages/client-runtime version: link:upstream-t3code/packages/client-runtime @@ -103,11 +109,11 @@ importers: version: link:../shared effect: specifier: 'catalog:' - version: 4.0.0-beta.78 + version: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) devDependencies: '@effect/vitest': specifier: 'catalog:' - version: 4.0.0-beta.78(patch_hash=74fd480109c3bd975255bf5573a64718451bcae02b523c432bc18e9aa27b52fd)(effect@4.0.0-beta.78)(vitest@4.1.10) + version: 4.0.0-beta.102(patch_hash=a607339aab944136a084a05f159aa0f5a69776934d4b835dab8f9992f1c13425)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(vitest@4.1.10) vite-plus: specifier: 'catalog:' version: 0.2.6(@types/node@26.1.2)(typescript@7.0.2)(vite@8.0.14(@types/node@26.1.2)(yaml@2.9.0))(yaml@2.9.0) @@ -116,11 +122,11 @@ importers: dependencies: effect: specifier: 'catalog:' - version: 4.0.0-beta.78 + version: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) devDependencies: '@effect/vitest': specifier: 'catalog:' - version: 4.0.0-beta.78(patch_hash=74fd480109c3bd975255bf5573a64718451bcae02b523c432bc18e9aa27b52fd)(effect@4.0.0-beta.78)(vitest@4.1.10) + version: 4.0.0-beta.102(patch_hash=a607339aab944136a084a05f159aa0f5a69776934d4b835dab8f9992f1c13425)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(vitest@4.1.10) vite-plus: specifier: 'catalog:' version: 0.2.6(@types/node@26.1.2)(typescript@7.0.2)(vite@8.0.14(@types/node@26.1.2)(yaml@2.9.0))(yaml@2.9.0) @@ -129,16 +135,16 @@ importers: dependencies: '@noble/curves': specifier: 'catalog:' - version: 2.2.0 + version: 1.9.1 '@noble/hashes': specifier: 'catalog:' - version: 2.2.0 + version: 1.8.0 '@t3tools/contracts': specifier: workspace:* version: link:../contracts effect: specifier: 'catalog:' - version: 4.0.0-beta.78 + version: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) jose: specifier: 'catalog:' version: 6.2.4 @@ -148,10 +154,10 @@ importers: devDependencies: '@effect/platform-node': specifier: 'catalog:' - version: 4.0.0-beta.78(effect@4.0.0-beta.78)(ioredis@5.10.1) + version: 4.0.0-beta.102(patch_hash=cae7efcda6fd29f4db2efd357cb7dc6c41cb6adc957901d437e13ed1f3c017f0)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(ioredis@5.10.1) '@effect/vitest': specifier: 'catalog:' - version: 4.0.0-beta.78(patch_hash=74fd480109c3bd975255bf5573a64718451bcae02b523c432bc18e9aa27b52fd)(effect@4.0.0-beta.78)(vitest@4.1.10) + version: 4.0.0-beta.102(patch_hash=a607339aab944136a084a05f159aa0f5a69776934d4b835dab8f9992f1c13425)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(vitest@4.1.10) '@types/node': specifier: 'catalog:' version: 26.1.2 @@ -235,23 +241,23 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@effect/platform-node-shared@4.0.0-beta.78': - resolution: {integrity: sha512-mo0ddTPATyCMyqzQasYDL7+NI29vozoMplom+qu9f/onDTd4xG5hvEEfGxfL0Ljygui6keG/YE/E9OZVf2z5WA==} + '@effect/platform-node-shared@4.0.0-beta.102': + resolution: {integrity: sha512-gVd793I72MrkX4dXo7eYtRKfNj0RW4eMRfVEKEJI16h2+mBDCzQ+gqMrog2hSTHQnaIbvbYShNQ4TVGuRCYZeQ==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-beta.78 + effect: ^4.0.0-beta.102 - '@effect/platform-node@4.0.0-beta.78': - resolution: {integrity: sha512-8ONrIS5/R9dq+0BJ6v3kUXNEkfjU6S3GzIYCH5gmHdiriRvIoBhXYNAITfRvZpfx1JPrKuP70cHyuQDjmJcDkQ==} + '@effect/platform-node@4.0.0-beta.102': + resolution: {integrity: sha512-wYVAU9jAePT+gouMr/EVz1CaW6yDLjPAgXYeEFLz7wugT5iZhFRag6mqajV/wwN3bzWP2fzZHokLuPmqD/rqeA==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-beta.78 + effect: ^4.0.0-beta.102 ioredis: ^5.7.0 - '@effect/vitest@4.0.0-beta.78': - resolution: {integrity: sha512-5KQsQYrQ/o7mfOVAxRtNnfD9M0W4OI6yQd0n/m2N7OOLxTdX4FwN4s/X4obykBC7ZEwH+bzMrFJiB4pq9lrQKQ==} + '@effect/vitest@4.0.0-beta.102': + resolution: {integrity: sha512-4dipFAYG6imOzrY3zy3BgzCJkbb9xESyzUef0WSx8bsK0/SpITqbJpdwKeOyDWLZ+rimjua8IbTFMAde865pIQ==} peerDependencies: - effect: ^4.0.0-beta.78 + effect: ^4.0.0-beta.102 vitest: ^3.0.0 || ^4.0.0 '@emnapi/core@1.10.0': @@ -401,13 +407,13 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@noble/curves@2.2.0': - resolution: {integrity: sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==} - engines: {node: '>= 20.19.0'} + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} - '@noble/hashes@2.2.0': - resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} - engines: {node: '>= 20.19.0'} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -1384,8 +1390,8 @@ packages: dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - effect@4.0.0-beta.78: - resolution: {integrity: sha512-j79Rl9QpHwMz/ZJWLNpZoiVj9N7zHqiLKN5EcYd/A8J1oqejILWQLfc4HPlvqHqKC8SK55LJ+X4gy4ONJ+JpfQ==} + effect@4.0.0-beta.102: + resolution: {integrity: sha512-z8Y+Q76Hh/kjLFZrXu8tGn6e+tDsg45R+UHhxd190pXxD53OGwf/G/zDxXTkse4HJ5mobNZfitLfUCp4fMvu6w==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1423,8 +1429,8 @@ packages: extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - fast-check@4.8.0: - resolution: {integrity: sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg==} + fast-check@4.9.0: + resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} engines: {node: '>=12.17.0'} fast-glob@3.3.3: @@ -1693,11 +1699,11 @@ packages: resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} hasBin: true - msgpackr@2.0.2: - resolution: {integrity: sha512-c5hYOXFbP79Slh6Dzd2wzk+jnV7mX1UxfMYtilnY1NmalXPqG8DGb5cYCMBrW4AsH3zekBBZd4QrKz9NhtvYLQ==} + msgpackr@2.0.5: + resolution: {integrity: sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==} - multipasta@0.2.7: - resolution: {integrity: sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==} + multipasta@0.2.8: + resolution: {integrity: sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q==} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -1994,8 +2000,8 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toml@4.1.1: - resolution: {integrity: sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw==} + toml@4.3.0: + resolution: {integrity: sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==} engines: {node: '>=20'} totalist@3.0.1: @@ -2013,8 +2019,8 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - undici@8.3.0: - resolution: {integrity: sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==} + undici@8.9.0: + resolution: {integrity: sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==} engines: {node: '>=22.19.0'} unicode-emoji-modifier-base@1.0.0: @@ -2025,8 +2031,8 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} - uuid@14.0.0: - resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true vite-plus@0.2.6: @@ -2339,29 +2345,29 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@effect/platform-node-shared@4.0.0-beta.78(effect@4.0.0-beta.78)': + '@effect/platform-node-shared@4.0.0-beta.102(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))': dependencies: '@types/ws': 8.18.1 - effect: 4.0.0-beta.78 + effect: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform-node@4.0.0-beta.78(effect@4.0.0-beta.78)(ioredis@5.10.1)': + '@effect/platform-node@4.0.0-beta.102(patch_hash=cae7efcda6fd29f4db2efd357cb7dc6c41cb6adc957901d437e13ed1f3c017f0)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(ioredis@5.10.1)': dependencies: - '@effect/platform-node-shared': 4.0.0-beta.78(effect@4.0.0-beta.78) - effect: 4.0.0-beta.78 + '@effect/platform-node-shared': 4.0.0-beta.102(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488)) + effect: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) ioredis: 5.10.1 mime: 4.1.0 - undici: 8.3.0 + undici: 8.9.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/vitest@4.0.0-beta.78(patch_hash=74fd480109c3bd975255bf5573a64718451bcae02b523c432bc18e9aa27b52fd)(effect@4.0.0-beta.78)(vitest@4.1.10)': + '@effect/vitest@4.0.0-beta.102(patch_hash=a607339aab944136a084a05f159aa0f5a69776934d4b835dab8f9992f1c13425)(effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488))(vitest@4.1.10)': dependencies: - effect: 4.0.0-beta.78 + effect: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) vitest: 4.1.10(@types/node@26.1.2)(@vitest/browser-preview@4.1.10)(vite@8.0.14(@types/node@26.1.2)(yaml@2.9.0)) '@emnapi/core@1.10.0': @@ -2483,11 +2489,11 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@noble/curves@2.2.0': + '@noble/curves@1.9.1': dependencies: - '@noble/hashes': 2.2.0 + '@noble/hashes': 1.8.0 - '@noble/hashes@2.2.0': {} + '@noble/hashes@1.8.0': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -3087,17 +3093,17 @@ snapshots: dom-accessibility-api@0.5.16: {} - effect@4.0.0-beta.78: + effect@4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488): dependencies: '@standard-schema/spec': 1.1.0 - fast-check: 4.8.0 + fast-check: 4.9.0 find-my-way-ts: 0.1.6 ini: 7.0.0 kubernetes-types: 1.30.0 - msgpackr: 2.0.2 - multipasta: 0.2.7 - toml: 4.1.1 - uuid: 14.0.0 + msgpackr: 2.0.5 + multipasta: 0.2.8 + toml: 4.3.0 + uuid: 14.0.1 yaml: 2.9.0 emoji-regex@8.0.0: {} @@ -3125,7 +3131,7 @@ snapshots: extendable-error@0.1.7: {} - fast-check@4.8.0: + fast-check@4.9.0: dependencies: pure-rand: 8.4.0 @@ -3360,11 +3366,11 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 optional: true - msgpackr@2.0.2: + msgpackr@2.0.5: optionalDependencies: msgpackr-extract: 3.0.4 - multipasta@0.2.7: {} + multipasta@0.2.8: {} mz@2.7.0: dependencies: @@ -3660,7 +3666,7 @@ snapshots: dependencies: is-number: 7.0.0 - toml@4.1.1: {} + toml@4.3.0: {} totalist@3.0.1: {} @@ -3692,13 +3698,13 @@ snapshots: undici-types@8.3.0: {} - undici@8.3.0: {} + undici@8.9.0: {} unicode-emoji-modifier-base@1.0.0: {} universalify@0.1.2: {} - uuid@14.0.0: {} + uuid@14.0.1: {} vite-plus@0.2.6(@types/node@26.1.2)(typescript@7.0.2)(vite@8.0.14(@types/node@26.1.2)(yaml@2.9.0))(yaml@2.9.0): dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9b2d32a..7514f99 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,12 +5,12 @@ packages: - upstream-t3code/packages/shared catalog: - "@effect/platform-node": 4.0.0-beta.78 - "@effect/vitest": 4.0.0-beta.78 - "@noble/curves": 2.2.0 - "@noble/hashes": 2.2.0 + "@effect/platform-node": 4.0.0-beta.102 + "@effect/vitest": 4.0.0-beta.102 + "@noble/curves": 1.9.1 + "@noble/hashes": 1.8.0 "@types/node": ^26.1.2 - effect: 4.0.0-beta.78 + effect: 4.0.0-beta.102 jose: 6.2.4 vite-plus: 0.2.6 vite: npm:@voidzero-dev/vite-plus-core@0.2.6 @@ -18,4 +18,6 @@ catalog: yaml: ^2.9.0 patchedDependencies: - "@effect/vitest@4.0.0-beta.78": patches/@effect__vitest@4.0.0-beta.78.patch + "@effect/platform-node@4.0.0-beta.102": upstream-t3code/patches/@effect__platform-node@4.0.0-beta.102.patch + "@effect/vitest@4.0.0-beta.102": upstream-t3code/patches/@effect__vitest@4.0.0-beta.102.patch + "effect@4.0.0-beta.102": upstream-t3code/patches/effect@4.0.0-beta.102.patch diff --git a/src/orchestration/layer.ts b/src/orchestration/layer.ts index dba21f1..e0f6fc3 100644 --- a/src/orchestration/layer.ts +++ b/src/orchestration/layer.ts @@ -12,10 +12,10 @@ import { type OrchestrationShellStreamItem, type OrchestrationThreadStreamItem, } from "@t3tools/contracts"; +import { applyShellStreamEvent } from "@t3tools/client-runtime/state/shell"; import { RpcError } from "../rpc/error.ts"; import { T3RpcOperations } from "../rpc/operation.ts"; -import { applyShellStreamEvent } from "./shell-reducer/index.ts"; import { T3Orchestration, type OpenThread, type Orchestration } from "./service.ts"; export const makeT3Orchestration = Effect.fn("makeT3Orchestration")(function* () { diff --git a/src/orchestration/shell-reducer/index.ts b/src/orchestration/shell-reducer/index.ts deleted file mode 100644 index ce53d80..0000000 --- a/src/orchestration/shell-reducer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { applyShellStreamEvent } from "../../../upstream-t3code/packages/client-runtime/src/state/shellReducer.ts"; diff --git a/src/preview-viewport/index.ts b/src/preview-viewport/index.ts deleted file mode 100644 index 2c7a7f4..0000000 --- a/src/preview-viewport/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - PREVIEW_VIEWPORT_PRESETS as T3_PREVIEW_VIEWPORT_PRESETS, - resolvePreviewViewport as resolveT3PreviewViewport, -} from "@t3tools/shared/previewViewport"; - -import type { - PreviewAutomationResizeInput, - PreviewViewportPresetId, - PreviewViewportSetting as PreviewViewportSettingType, -} from "../contracts/index.ts"; - -export { - PREVIEW_VIEWPORT_PRESET_IDS, - PreviewViewportSetting, - type PreviewViewportPresetId, -} from "../contracts/index.ts"; - -export interface PreviewViewportPreset { - readonly id: PreviewViewportPresetId; - readonly label: string; - readonly category: "Desktop" | "Tablet" | "Phone"; - readonly detail: string; - readonly width: number; - readonly height: number; -} - -export const PREVIEW_VIEWPORT_PRESETS: ReadonlyArray = - T3_PREVIEW_VIEWPORT_PRESETS; - -export function resolvePreviewViewport( - input: PreviewAutomationResizeInput, -): PreviewViewportSettingType { - return resolveT3PreviewViewport(input); -} diff --git a/src/rpc/layer.ts b/src/rpc/layer.ts index 1528422..6083870 100644 --- a/src/rpc/layer.ts +++ b/src/rpc/layer.ts @@ -1,3 +1,4 @@ +import type { ConnectionAttemptError } from "@t3tools/client-runtime/connection"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; @@ -8,16 +9,16 @@ import * as Schedule from "effect/Schedule"; import * as Scope from "effect/Scope"; import * as SynchronizedRef from "effect/SynchronizedRef"; +import { T3CodeConnectionError } from "../connection/error.ts"; import { T3PreparedConnectionProvider } from "../connection/prepared.ts"; import { RpcError } from "./error.ts"; import { T3RpcSessionFactory } from "./session.ts"; import { T3Rpc, type WsClient } from "./service.ts"; const connectionRetrySchedule = Schedule.exponential("100 millis").pipe( - Schedule.take(4), - Schedule.collectWhile((metadata: Schedule.Metadata) => - Predicate.isTagged(metadata.input, "ConnectionTransientError"), - ), + Schedule.setInputType(), + Schedule.upTo({ times: 4 }), + Schedule.while((metadata) => Predicate.isTagged(metadata.input, "ConnectionTransientError")), ); type Connection = { diff --git a/src/rpc/operation.ts b/src/rpc/operation.ts index 716e7de..2e7a18a 100644 --- a/src/rpc/operation.ts +++ b/src/rpc/operation.ts @@ -29,10 +29,9 @@ export type CliRpcOperationError = | TerminalError; export const rpcRetrySchedule = Schedule.exponential("100 millis").pipe( - Schedule.take(4), - Schedule.collectWhile((metadata: Schedule.Metadata) => - Predicate.isTagged(metadata.input, "RpcClientError"), - ), + Schedule.setInputType(), + Schedule.upTo({ times: 4 }), + Schedule.while((metadata) => Predicate.isTagged(metadata.input, "RpcClientError")), ); export type T3RpcOperationsService = { @@ -63,9 +62,7 @@ export const makeT3RpcOperations = Effect.fn("makeT3RpcOperations")(function* () Predicate.isTagged(error, "RpcClientError") ? rpc.disconnect : Effect.void, ), Effect.retry(rpcRetrySchedule), - Effect.mapError((error) => - Predicate.isTagged(error, "RpcError") ? error : toRpcError(error, method), - ), + Effect.mapError((error) => (error instanceof RpcError ? error : toRpcError(error, method))), ); const subscribe: T3RpcOperationsService["subscribe"] = ( @@ -77,9 +74,7 @@ export const makeT3RpcOperations = Effect.fn("makeT3RpcOperations")(function* () Predicate.isTagged(error, "RpcClientError") ? rpc.disconnect : Effect.void, ), Stream.retry(rpcRetrySchedule), - Stream.mapError((error) => - Predicate.isTagged(error, "RpcError") ? error : toRpcError(error, method), - ), + Stream.mapError((error) => (error instanceof RpcError ? error : toRpcError(error, method))), ); return { diff --git a/src/sql/node-sqlite-client.ts b/src/sql/node-sqlite-client.ts index 62fb57a..b84f647 100644 --- a/src/sql/node-sqlite-client.ts +++ b/src/sql/node-sqlite-client.ts @@ -91,6 +91,9 @@ export const makeNodeSqliteClient = Effect.fn("makeNodeSqliteClient")(function* executeValues(sql, params) { return runValues(sql, params); }, + executeValuesUnprepared(sql, params) { + return runValues(sql, params); + }, executeUnprepared(sql, params, rowTransform) { const effect = runRows(sql, params); return rowTransform === undefined ? effect : Effect.map(effect, rowTransform); diff --git a/tsconfig.dts.json b/tsconfig.dts.json index 5ce6ab3..f8dd121 100644 --- a/tsconfig.dts.json +++ b/tsconfig.dts.json @@ -5,7 +5,10 @@ "emitDeclarationOnly": true, "noEmit": false, "paths": { - "@t3tools/client-runtime/*": ["./upstream-t3code/packages/client-runtime/src/*/index.ts"], + "@t3tools/client-runtime/*": [ + "./upstream-t3code/packages/client-runtime/src/*/index.ts", + "./upstream-t3code/packages/client-runtime/src/*.ts" + ], "@t3tools/contracts": ["./upstream-t3code/packages/contracts/src/index.ts"], "@t3tools/contracts/*": ["./upstream-t3code/packages/contracts/src/*.ts"], "@t3tools/shared/*": ["./upstream-t3code/packages/shared/src/*.ts"] @@ -24,9 +27,12 @@ "src/node/index.ts", "src/orchestration/index.ts", "src/preview/index.ts", - "src/preview-viewport/index.ts", "src/rpc/index.ts", "src/runtime/index.ts", - "src/t3tools/index.ts" + "src/t3tools/index.ts", + "upstream-t3code/packages/client-runtime/src/*/index.ts", + "upstream-t3code/packages/client-runtime/src/operations/projects.ts", + "upstream-t3code/packages/client-runtime/src/state/*.ts", + "upstream-t3code/packages/shared/src/*.ts" ] } diff --git a/vite.config.ts b/vite.config.ts index 70f48ae..1bec48e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,8 @@ import "vite-plus/test/config"; import { defineConfig } from "vite-plus"; import packageJson from "./package.json" with { type: "json" }; +import clientRuntimePackageJson from "./upstream-t3code/packages/client-runtime/package.json" with { type: "json" }; +import sharedPackageJson from "./upstream-t3code/packages/shared/package.json" with { type: "json" }; function shouldBundlePackDependency(id: string): boolean { if (id === "@napi-rs/keyring" || id.startsWith("@napi-rs/keyring-")) { @@ -31,10 +33,21 @@ export default defineConfig({ node: "src/node/index.ts", orchestration: "src/orchestration/index.ts", preview: "src/preview/index.ts", - "preview-viewport": "src/preview-viewport/index.ts", rpc: "src/rpc/index.ts", runtime: "src/runtime/index.ts", t3tools: "src/t3tools/index.ts", + ...Object.fromEntries( + Object.entries(clientRuntimePackageJson.exports).map(([subpath, conditions]) => [ + `client-runtime/${subpath.slice(2)}`, + `upstream-t3code/packages/client-runtime/${conditions.default.slice(2)}`, + ]), + ), + ...Object.fromEntries( + Object.entries(sharedPackageJson.exports).map(([subpath, conditions]) => [ + `shared/${subpath.slice(2)}`, + `upstream-t3code/packages/shared/${conditions.import.slice(2)}`, + ]), + ), }, deps: { alwaysBundle: shouldBundlePackDependency, From db61a56f35eb4ea943adfb1f167428d9e9c07939 Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 07:42:23 +0000 Subject: [PATCH 5/8] add upstream synchronization script --- README.md | 15 ++ package.json | 4 +- pnpm-lock.yaml | 15 +- pnpm-workspace.yaml | 4 +- scripts/sync-upstream.ts | 330 +++++++++++++++++++++++++++++++++++++++ tsconfig.json | 2 +- 6 files changed, 360 insertions(+), 10 deletions(-) create mode 100644 scripts/sync-upstream.ts diff --git a/README.md b/README.md index 51c225d..c638b33 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,21 @@ import { import { PreviewViewportSetting } from "t3code-cli/contracts"; ``` +## Upstream Maintenance + +Synchronize dependency versions and patches with the current `upstream-t3code` revision: + +```sh +pnpm sync-upstream +``` + +Pass `--target` to update the submodule first. It accepts `stable`, `nightly`, `main`, a version such +as `0.0.31` or `v0.0.31`, or a Git ref or commit: + +```sh +pnpm sync-upstream --target stable +``` + ## Project Management ```sh diff --git a/package.json b/package.json index ef31cba..7c430ff 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "release:version": "changeset version && pnpm format", "release:check": "pnpm check && pnpm typecheck && pnpm pack --dry-run", "release:publish": "changeset publish", + "sync-upstream": "node scripts/sync-upstream.ts", "typecheck": "tsc --noEmit" }, "dependencies": { @@ -138,7 +139,8 @@ "@total-typescript/shoehorn": "^0.1.2", "@types/node": "^26.1.2", "typescript": "^7.0.2", - "vite-plus": "^0.2.6" + "vite-plus": "^0.2.6", + "yaml": "catalog:" }, "engines": { "node": ">=24.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53183d2..00fad57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,8 +25,8 @@ catalogs: specifier: 4.0.0-beta.102 version: 4.0.0-beta.102 jose: - specifier: 6.2.4 - version: 6.2.4 + specifier: 6.2.2 + version: 6.2.2 vite-plus: specifier: 0.2.6 version: 0.2.6 @@ -98,6 +98,9 @@ importers: vite-plus: specifier: ^0.2.6 version: 0.2.6(@types/node@26.1.2)(typescript@7.0.2)(vite@8.0.14(@types/node@26.1.2)(yaml@2.9.0))(yaml@2.9.0) + yaml: + specifier: 'catalog:' + version: 2.9.0 upstream-t3code/packages/client-runtime: dependencies: @@ -147,7 +150,7 @@ importers: version: 4.0.0-beta.102(patch_hash=71215759e1ac0a7f65d7b75d816986687ae6c3a6cba02d928d184ca71790d488) jose: specifier: 'catalog:' - version: 6.2.4 + version: 6.2.2 yaml: specifier: 'catalog:' version: 2.9.0 @@ -1546,8 +1549,8 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jose@6.2.4: - resolution: {integrity: sha512-N8acGzVsQy6M/fjFcxtysNc4Q379TcM5dM/qKkNtsHFji88yANnXTr7BLeP75iPnFwBfQzM/jg2BZ9+HZrHCZA==} + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3242,7 +3245,7 @@ snapshots: isexe@2.0.0: {} - jose@6.2.4: {} + jose@6.2.2: {} js-tokens@4.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7514f99..92f7530 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,7 +11,7 @@ catalog: "@noble/hashes": 1.8.0 "@types/node": ^26.1.2 effect: 4.0.0-beta.102 - jose: 6.2.4 + jose: 6.2.2 vite-plus: 0.2.6 vite: npm:@voidzero-dev/vite-plus-core@0.2.6 vitest: npm:@voidzero-dev/vite-plus-test@0.1.24 @@ -20,4 +20,4 @@ catalog: patchedDependencies: "@effect/platform-node@4.0.0-beta.102": upstream-t3code/patches/@effect__platform-node@4.0.0-beta.102.patch "@effect/vitest@4.0.0-beta.102": upstream-t3code/patches/@effect__vitest@4.0.0-beta.102.patch - "effect@4.0.0-beta.102": upstream-t3code/patches/effect@4.0.0-beta.102.patch + effect@4.0.0-beta.102: upstream-t3code/patches/effect@4.0.0-beta.102.patch diff --git a/scripts/sync-upstream.ts b/scripts/sync-upstream.ts new file mode 100644 index 0000000..4a93e7e --- /dev/null +++ b/scripts/sync-upstream.ts @@ -0,0 +1,330 @@ +#!/usr/bin/env node + +import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; +import * as NodeServices from "@effect/platform-node/NodeServices"; +import * as Console from "effect/Console"; +import * as Effect from "effect/Effect"; +import * as FileSystem from "effect/FileSystem"; +import * as Option from "effect/Option"; +import * as Path from "effect/Path"; +import * as Schema from "effect/Schema"; +import * as Stream from "effect/Stream"; +import { Command, Flag } from "effect/unstable/cli"; +import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; +import { parseDocument } from "yaml"; + +const synchronizedDependencies = [ + "@effect/platform-node", + "@effect/vitest", + "@noble/curves", + "@noble/hashes", + "effect", + "jose", + "yaml", +] as const; + +const WorkspaceConfig = Schema.Struct({ + catalog: Schema.Record(Schema.String, Schema.String), + patchedDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)), +}); + +const PackageJson = Schema.Record(Schema.String, Schema.Unknown); +const Dependencies = Schema.Record(Schema.String, Schema.String); +const decodePackageJson = Schema.decodeEffect(Schema.fromJsonString(PackageJson)); +const decodeDependencies = Schema.decodeUnknownEffect(Dependencies); +const decodeWorkspaceConfig = Schema.decodeUnknownEffect(WorkspaceConfig); + +export class SyncUpstreamError extends Schema.TaggedErrorClass()( + "SyncUpstreamError", + { + message: Schema.String, + cause: Schema.optional(Schema.Defect()), + }, +) {} + +const syncError = (message: string) => (cause: unknown) => + new SyncUpstreamError({ message, cause }); + +const repoRoot = Effect.service(Path.Path).pipe( + Effect.flatMap((path) => path.fromFileUrl(new URL("..", import.meta.url))), + Effect.mapError(syncError("failed to resolve the repository root")), +); + +const collectStream = (stream: Stream.Stream) => + stream.pipe( + Stream.decodeText(), + Stream.runFold( + () => "", + (output, chunk) => output + chunk, + ), + ); + +const runCommand = Effect.fn("runCommand")(function* ( + command: string, + args: ReadonlyArray, + cwd: string, + captureOutput: boolean = false, +) { + const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; + const commandText = [command, ...args].join(" "); + const child = yield* spawner + .spawn( + ChildProcess.make( + command, + args, + captureOutput + ? { cwd, stderr: "inherit" } + : { cwd, stdin: "inherit", stdout: "inherit", stderr: "inherit" }, + ), + ) + .pipe(Effect.mapError(syncError(`failed to start '${commandText}'`))); + const [output, exitCode] = yield* Effect.all( + [ + captureOutput ? collectStream(child.stdout) : Effect.succeed(""), + child.exitCode.pipe(Effect.map(Number)), + ], + { concurrency: "unbounded" }, + ).pipe(Effect.mapError(syncError(`failed while running '${commandText}'`))); + + if (exitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'${commandText}' exited with code ${exitCode}`, + }); + } + + return output.trim(); +}); + +const readWorkspaceConfig = Effect.fn("readWorkspaceConfig")(function* (filePath: string) { + const fs = yield* FileSystem.FileSystem; + const source = yield* fs + .readFileString(filePath) + .pipe(Effect.mapError(syncError(`failed to read '${filePath}'`))); + const document = yield* Effect.try({ + try: () => parseDocument(source), + catch: syncError(`failed to parse '${filePath}'`), + }); + if (document.errors.length > 0) { + return yield* new SyncUpstreamError({ message: `failed to parse '${filePath}'` }); + } + const value: unknown = document.toJS(); + const config = yield* decodeWorkspaceConfig(value).pipe( + Effect.mapError(syncError(`invalid workspace config in '${filePath}'`)), + ); + + return { config, document, source }; +}); + +const updateSubmodule = Effect.fn("updateSubmodule")(function* ( + root: string, + submodulePath: string, + target: string | undefined, +) { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const initialized = yield* fs + .exists(path.join(submodulePath, ".git")) + .pipe(Effect.mapError(syncError("failed to inspect upstream-t3code"))); + if (!initialized) { + yield* runCommand("git", ["submodule", "update", "--init", "--", "upstream-t3code"], root); + } + + const status = yield* runCommand("git", ["status", "--porcelain"], submodulePath, true).pipe( + Effect.scoped, + ); + if (status.length > 0) { + return yield* new SyncUpstreamError({ + message: "upstream-t3code has uncommitted changes; clean it before updating", + }); + } + + if (target === undefined) { + return yield* runCommand("git", ["rev-parse", "HEAD"], submodulePath, true).pipe(Effect.scoped); + } + + yield* Console.log(`Fetching upstream T3 Code for target '${target}'...`); + yield* runCommand("git", ["fetch", "origin", "--tags", "--force"], submodulePath); + + let ref: string; + if (target === "stable" || target === "nightly") { + const tags = yield* runCommand( + "git", + [ + "tag", + "--list", + target === "stable" ? "v[0-9]*" : "v*-nightly.*", + "--sort=-version:refname", + ], + submodulePath, + true, + ).pipe(Effect.scoped); + const tag = tags + .split("\n") + .find((candidate) => + target === "stable" + ? /^v\d+\.\d+\.\d+$/u.test(candidate) + : /^v\d+\.\d+\.\d+-nightly\..+$/u.test(candidate), + ); + if (tag === undefined) { + return yield* new SyncUpstreamError({ message: `no ${target} T3 Code tag was found` }); + } + ref = tag; + } else if (target === "main") { + ref = "origin/main"; + } else if (/^v?\d+\.\d+\.\d+(?:-.+)?$/u.test(target)) { + ref = target.startsWith("v") ? target : `v${target}`; + } else { + yield* runCommand("git", ["fetch", "origin", target], submodulePath); + ref = "FETCH_HEAD"; + } + + const commit = yield* runCommand( + "git", + ["rev-parse", "--verify", `${ref}^{commit}`], + submodulePath, + true, + ).pipe(Effect.scoped); + const currentCommit = yield* runCommand("git", ["rev-parse", "HEAD"], submodulePath, true).pipe( + Effect.scoped, + ); + + if (commit !== currentCommit) { + yield* runCommand("git", ["checkout", "--detach", commit], submodulePath); + } + + return commit; +}); + +const synchronizeConfig = Effect.fn("synchronizeConfig")(function* ( + root: string, + submodulePath: string, +) { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const packageJsonPath = path.join(root, "package.json"); + const workspacePath = path.join(root, "pnpm-workspace.yaml"); + const upstreamWorkspacePath = path.join(submodulePath, "pnpm-workspace.yaml"); + const packageJsonSource = yield* fs + .readFileString(packageJsonPath) + .pipe(Effect.mapError(syncError(`failed to read '${packageJsonPath}'`))); + const packageJson = yield* decodePackageJson(packageJsonSource).pipe( + Effect.mapError(syncError(`invalid package manifest in '${packageJsonPath}'`)), + ); + const dependencies = yield* decodeDependencies(packageJson.dependencies).pipe( + Effect.mapError(syncError(`invalid dependencies in '${packageJsonPath}'`)), + ); + const devDependencies = yield* decodeDependencies(packageJson.devDependencies).pipe( + Effect.mapError(syncError(`invalid devDependencies in '${packageJsonPath}'`)), + ); + const rootWorkspace = yield* readWorkspaceConfig(workspacePath); + const upstreamWorkspace = yield* readWorkspaceConfig(upstreamWorkspacePath); + const versions: Record = {}; + + for (const dependency of synchronizedDependencies) { + const version = upstreamWorkspace.config.catalog[dependency]; + if (version === undefined) { + return yield* new SyncUpstreamError({ + message: `upstream catalog does not define '${dependency}'`, + }); + } + versions[dependency] = version; + rootWorkspace.document.setIn(["catalog", dependency], version); + } + + const patchedDependencies = Object.fromEntries( + Object.entries(rootWorkspace.config.patchedDependencies ?? {}).filter( + ([dependency]) => !synchronizedDependencies.some((name) => dependency.startsWith(`${name}@`)), + ), + ); + for (const [dependency, patchFile] of Object.entries( + upstreamWorkspace.config.patchedDependencies ?? {}, + )) { + if (!synchronizedDependencies.some((name) => dependency.startsWith(`${name}@`))) { + continue; + } + const patchPath = path.join("upstream-t3code", patchFile); + const patchExists = yield* fs + .exists(path.join(root, patchPath)) + .pipe(Effect.mapError(syncError(`failed to inspect '${patchPath}'`))); + if (!patchExists) { + return yield* new SyncUpstreamError({ message: `upstream patch '${patchFile}' is missing` }); + } + patchedDependencies[dependency] = patchPath; + } + rootWorkspace.document.set("patchedDependencies", patchedDependencies); + + const nextDependencies = { ...dependencies }; + const nextDevDependencies = { ...devDependencies }; + for (const [dependency, version] of Object.entries(versions)) { + if (nextDependencies[dependency] !== undefined && nextDependencies[dependency] !== "catalog:") { + nextDependencies[dependency] = version; + } + if ( + nextDevDependencies[dependency] !== undefined && + nextDevDependencies[dependency] !== "catalog:" + ) { + nextDevDependencies[dependency] = version; + } + } + + const nextPackageJsonSource = `${JSON.stringify( + { + ...packageJson, + dependencies: nextDependencies, + devDependencies: nextDevDependencies, + }, + null, + 2, + )}\n`; + const nextWorkspaceSource = rootWorkspace.document.toString(); + + if (nextPackageJsonSource !== packageJsonSource) { + yield* fs + .writeFileString(packageJsonPath, nextPackageJsonSource) + .pipe(Effect.mapError(syncError(`failed to write '${packageJsonPath}'`))); + } + if (nextWorkspaceSource !== rootWorkspace.source) { + yield* fs + .writeFileString(workspacePath, nextWorkspaceSource) + .pipe(Effect.mapError(syncError(`failed to write '${workspacePath}'`))); + } + + return versions; +}); + +const syncUpstream = Effect.fn("syncUpstream")(function* (target: string | undefined) { + const path = yield* Path.Path; + const root = yield* repoRoot; + const submodulePath = path.join(root, "upstream-t3code"); + const commit = yield* updateSubmodule(root, submodulePath, target).pipe(Effect.scoped); + const versions = yield* synchronizeConfig(root, submodulePath); + + yield* Console.log("Installing synchronized dependencies..."); + yield* runCommand("pnpm", ["install"], root).pipe(Effect.scoped); + yield* Console.log(`T3 Code: ${commit}`); + for (const dependency of synchronizedDependencies) { + yield* Console.log(`${dependency}: ${versions[dependency]}`); + } +}); + +const syncUpstreamCommand = Command.make( + "sync-upstream", + { + target: Flag.string("target").pipe( + Flag.withDescription("T3 Code target: stable, nightly, main, a version, ref, or commit."), + Flag.optional, + ), + }, + ({ target }) => syncUpstream(Option.getOrUndefined(target)), +).pipe( + Command.withDescription( + "Update the upstream T3 Code submodule and synchronize required dependencies and patches.", + ), +); + +if (import.meta.main) { + Command.run(syncUpstreamCommand, { version: "0.0.0" }).pipe( + Effect.provide(NodeServices.layer), + NodeRuntime.runMain, + ); +} diff --git a/tsconfig.json b/tsconfig.json index 20d7423..0e63de2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,5 +12,5 @@ "target": "ES2024", "types": ["node"] }, - "include": ["src/**/*.ts", "vite.config.ts"] + "include": ["scripts/**/*.ts", "src/**/*.ts", "vite.config.ts"] } From 69eafb60e6c6d8b931c53df95d27fd48025c996a Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 07:55:14 +0000 Subject: [PATCH 6/8] inline upstream sync commands --- scripts/sync-upstream.ts | 218 +++++++++++++++++++++++++++------------ 1 file changed, 152 insertions(+), 66 deletions(-) diff --git a/scripts/sync-upstream.ts b/scripts/sync-upstream.ts index 4a93e7e..97a2106 100644 --- a/scripts/sync-upstream.ts +++ b/scripts/sync-upstream.ts @@ -28,7 +28,7 @@ const WorkspaceConfig = Schema.Struct({ patchedDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)), }); -const PackageJson = Schema.Record(Schema.String, Schema.Unknown); +const PackageJson = Schema.Record(Schema.String, Schema.MutableJson); const Dependencies = Schema.Record(Schema.String, Schema.String); const decodePackageJson = Schema.decodeEffect(Schema.fromJsonString(PackageJson)); const decodeDependencies = Schema.decodeUnknownEffect(Dependencies); @@ -59,42 +59,6 @@ const collectStream = (stream: Stream.Stream) => ), ); -const runCommand = Effect.fn("runCommand")(function* ( - command: string, - args: ReadonlyArray, - cwd: string, - captureOutput: boolean = false, -) { - const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; - const commandText = [command, ...args].join(" "); - const child = yield* spawner - .spawn( - ChildProcess.make( - command, - args, - captureOutput - ? { cwd, stderr: "inherit" } - : { cwd, stdin: "inherit", stdout: "inherit", stderr: "inherit" }, - ), - ) - .pipe(Effect.mapError(syncError(`failed to start '${commandText}'`))); - const [output, exitCode] = yield* Effect.all( - [ - captureOutput ? collectStream(child.stdout) : Effect.succeed(""), - child.exitCode.pipe(Effect.map(Number)), - ], - { concurrency: "unbounded" }, - ).pipe(Effect.mapError(syncError(`failed while running '${commandText}'`))); - - if (exitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'${commandText}' exited with code ${exitCode}`, - }); - } - - return output.trim(); -}); - const readWorkspaceConfig = Effect.fn("readWorkspaceConfig")(function* (filePath: string) { const fs = yield* FileSystem.FileSystem; const source = yield* fs @@ -122,43 +86,115 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( ) { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; + const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; const initialized = yield* fs .exists(path.join(submodulePath, ".git")) .pipe(Effect.mapError(syncError("failed to inspect upstream-t3code"))); if (!initialized) { - yield* runCommand("git", ["submodule", "update", "--init", "--", "upstream-t3code"], root); + const exitCode = Number( + yield* spawner.exitCode( + ChildProcess.make("git", ["submodule", "update", "--init", "--", "upstream-t3code"], { + cwd: root, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }), + ), + ); + if (exitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git submodule update --init -- upstream-t3code' exited with code ${exitCode}`, + }); + } } - const status = yield* runCommand("git", ["status", "--porcelain"], submodulePath, true).pipe( - Effect.scoped, + const statusProcess = yield* spawner.spawn( + ChildProcess.make("git", ["status", "--porcelain"], { + cwd: submodulePath, + stderr: "inherit", + }), + ); + const [status, statusExitCode] = yield* Effect.all( + [collectStream(statusProcess.stdout), statusProcess.exitCode.pipe(Effect.map(Number))], + { concurrency: "unbounded" }, ); - if (status.length > 0) { + if (statusExitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git status --porcelain' exited with code ${statusExitCode}`, + }); + } + if (status.trim().length > 0) { return yield* new SyncUpstreamError({ message: "upstream-t3code has uncommitted changes; clean it before updating", }); } + const currentCommitProcess = yield* spawner.spawn( + ChildProcess.make("git", ["rev-parse", "HEAD"], { + cwd: submodulePath, + stderr: "inherit", + }), + ); + const [currentCommitOutput, currentCommitExitCode] = yield* Effect.all( + [ + collectStream(currentCommitProcess.stdout), + currentCommitProcess.exitCode.pipe(Effect.map(Number)), + ], + { concurrency: "unbounded" }, + ); + if (currentCommitExitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git rev-parse HEAD' exited with code ${currentCommitExitCode}`, + }); + } + const currentCommit = currentCommitOutput.trim(); + if (target === undefined) { - return yield* runCommand("git", ["rev-parse", "HEAD"], submodulePath, true).pipe(Effect.scoped); + return currentCommit; } yield* Console.log(`Fetching upstream T3 Code for target '${target}'...`); - yield* runCommand("git", ["fetch", "origin", "--tags", "--force"], submodulePath); + const fetchExitCode = Number( + yield* spawner.exitCode( + ChildProcess.make("git", ["fetch", "origin", "--tags", "--force"], { + cwd: submodulePath, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }), + ), + ); + if (fetchExitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git fetch origin --tags --force' exited with code ${fetchExitCode}`, + }); + } let ref: string; if (target === "stable" || target === "nightly") { - const tags = yield* runCommand( - "git", - [ - "tag", - "--list", - target === "stable" ? "v[0-9]*" : "v*-nightly.*", - "--sort=-version:refname", - ], - submodulePath, - true, - ).pipe(Effect.scoped); - const tag = tags + const tagsProcess = yield* spawner.spawn( + ChildProcess.make( + "git", + [ + "tag", + "--list", + target === "stable" ? "v[0-9]*" : "v*-nightly.*", + "--sort=-version:refname", + ], + { cwd: submodulePath, stderr: "inherit" }, + ), + ); + const [tagsOutput, tagsExitCode] = yield* Effect.all( + [collectStream(tagsProcess.stdout), tagsProcess.exitCode.pipe(Effect.map(Number))], + { concurrency: "unbounded" }, + ); + if (tagsExitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git tag --list' exited with code ${tagsExitCode}`, + }); + } + const tag = tagsOutput + .trim() .split("\n") .find((candidate) => target === "stable" @@ -174,22 +210,57 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( } else if (/^v?\d+\.\d+\.\d+(?:-.+)?$/u.test(target)) { ref = target.startsWith("v") ? target : `v${target}`; } else { - yield* runCommand("git", ["fetch", "origin", target], submodulePath); + const targetFetchExitCode = Number( + yield* spawner.exitCode( + ChildProcess.make("git", ["fetch", "origin", target], { + cwd: submodulePath, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }), + ), + ); + if (targetFetchExitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git fetch origin ${target}' exited with code ${targetFetchExitCode}`, + }); + } ref = "FETCH_HEAD"; } - const commit = yield* runCommand( - "git", - ["rev-parse", "--verify", `${ref}^{commit}`], - submodulePath, - true, - ).pipe(Effect.scoped); - const currentCommit = yield* runCommand("git", ["rev-parse", "HEAD"], submodulePath, true).pipe( - Effect.scoped, + const commitProcess = yield* spawner.spawn( + ChildProcess.make("git", ["rev-parse", "--verify", `${ref}^{commit}`], { + cwd: submodulePath, + stderr: "inherit", + }), + ); + const [commitOutput, commitExitCode] = yield* Effect.all( + [collectStream(commitProcess.stdout), commitProcess.exitCode.pipe(Effect.map(Number))], + { concurrency: "unbounded" }, ); + if (commitExitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git rev-parse --verify ${ref}^{commit}' exited with code ${commitExitCode}`, + }); + } + const commit = commitOutput.trim(); if (commit !== currentCommit) { - yield* runCommand("git", ["checkout", "--detach", commit], submodulePath); + const checkoutExitCode = Number( + yield* spawner.exitCode( + ChildProcess.make("git", ["checkout", "--detach", commit], { + cwd: submodulePath, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }), + ), + ); + if (checkoutExitCode !== 0) { + return yield* new SyncUpstreamError({ + message: `'git checkout --detach ${commit}' exited with code ${checkoutExitCode}`, + }); + } } return commit; @@ -294,13 +365,28 @@ const synchronizeConfig = Effect.fn("synchronizeConfig")(function* ( const syncUpstream = Effect.fn("syncUpstream")(function* (target: string | undefined) { const path = yield* Path.Path; + const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; const root = yield* repoRoot; const submodulePath = path.join(root, "upstream-t3code"); const commit = yield* updateSubmodule(root, submodulePath, target).pipe(Effect.scoped); const versions = yield* synchronizeConfig(root, submodulePath); yield* Console.log("Installing synchronized dependencies..."); - yield* runCommand("pnpm", ["install"], root).pipe(Effect.scoped); + const installExitCode = Number( + yield* spawner.exitCode( + ChildProcess.make("pnpm", ["install"], { + cwd: root, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }), + ), + ); + if (installExitCode !== 0) { + yield* new SyncUpstreamError({ + message: `'pnpm install' exited with code ${installExitCode}`, + }); + } yield* Console.log(`T3 Code: ${commit}`); for (const dependency of synchronizedDependencies) { yield* Console.log(`${dependency}: ${versions[dependency]}`); From 3a1b8c8671b536058c2196932190eca93916ef27 Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 08:06:10 +0000 Subject: [PATCH 7/8] tighten upstream sync validation --- scripts/sync-upstream.ts | 218 +++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 98 deletions(-) diff --git a/scripts/sync-upstream.ts b/scripts/sync-upstream.ts index 97a2106..71767f5 100644 --- a/scripts/sync-upstream.ts +++ b/scripts/sync-upstream.ts @@ -13,25 +13,24 @@ import { Command, Flag } from "effect/unstable/cli"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import { parseDocument } from "yaml"; -const synchronizedDependencies = [ - "@effect/platform-node", - "@effect/vitest", - "@noble/curves", - "@noble/hashes", - "effect", - "jose", - "yaml", -] as const; +const synchronizedDependencyPatterns = ["@effect/*", "@noble/*", "effect", "jose", "yaml"]; const WorkspaceConfig = Schema.Struct({ catalog: Schema.Record(Schema.String, Schema.String), patchedDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)), }); -const PackageJson = Schema.Record(Schema.String, Schema.MutableJson); const Dependencies = Schema.Record(Schema.String, Schema.String); -const decodePackageJson = Schema.decodeEffect(Schema.fromJsonString(PackageJson)); -const decodeDependencies = Schema.decodeUnknownEffect(Dependencies); +const PackageJson = Schema.StructWithRest( + Schema.Struct({ + dependencies: Dependencies, + devDependencies: Dependencies, + }), + [Schema.Record(Schema.String, Schema.Json)], +); +const decodePackageJson = Schema.decodeEffect(Schema.fromJsonString(PackageJson), { + propertyOrder: "original", +}); const decodeWorkspaceConfig = Schema.decodeUnknownEffect(WorkspaceConfig); export class SyncUpstreamError extends Schema.TaggedErrorClass()( @@ -91,21 +90,24 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( .exists(path.join(submodulePath, ".git")) .pipe(Effect.mapError(syncError("failed to inspect upstream-t3code"))); if (!initialized) { - const exitCode = Number( - yield* spawner.exitCode( + yield* spawner + .exitCode( ChildProcess.make("git", ["submodule", "update", "--init", "--", "upstream-t3code"], { cwd: root, stdin: "inherit", stdout: "inherit", stderr: "inherit", }), - ), - ); - if (exitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git submodule update --init -- upstream-t3code' exited with code ${exitCode}`, - }); - } + ) + .pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git submodule update --init -- upstream-t3code' exited with code ${exitCode}`, + }), + ), + ); } const statusProcess = yield* spawner.spawn( @@ -114,15 +116,18 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( stderr: "inherit", }), ); - const [status, statusExitCode] = yield* Effect.all( - [collectStream(statusProcess.stdout), statusProcess.exitCode.pipe(Effect.map(Number))], + const [status] = yield* Effect.all( + [collectStream(statusProcess.stdout), statusProcess.exitCode], { concurrency: "unbounded" }, + ).pipe( + Effect.filterOrFail( + ([, exitCode]) => exitCode === 0, + ([, exitCode]) => + new SyncUpstreamError({ + message: `'git status --porcelain' exited with code ${exitCode}`, + }), + ), ); - if (statusExitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git status --porcelain' exited with code ${statusExitCode}`, - }); - } if (status.trim().length > 0) { return yield* new SyncUpstreamError({ message: "upstream-t3code has uncommitted changes; clean it before updating", @@ -135,18 +140,18 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( stderr: "inherit", }), ); - const [currentCommitOutput, currentCommitExitCode] = yield* Effect.all( - [ - collectStream(currentCommitProcess.stdout), - currentCommitProcess.exitCode.pipe(Effect.map(Number)), - ], + const [currentCommitOutput] = yield* Effect.all( + [collectStream(currentCommitProcess.stdout), currentCommitProcess.exitCode], { concurrency: "unbounded" }, + ).pipe( + Effect.filterOrFail( + ([, exitCode]) => exitCode === 0, + ([, exitCode]) => + new SyncUpstreamError({ + message: `'git rev-parse HEAD' exited with code ${exitCode}`, + }), + ), ); - if (currentCommitExitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git rev-parse HEAD' exited with code ${currentCommitExitCode}`, - }); - } const currentCommit = currentCommitOutput.trim(); if (target === undefined) { @@ -154,21 +159,24 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( } yield* Console.log(`Fetching upstream T3 Code for target '${target}'...`); - const fetchExitCode = Number( - yield* spawner.exitCode( + yield* spawner + .exitCode( ChildProcess.make("git", ["fetch", "origin", "--tags", "--force"], { cwd: submodulePath, stdin: "inherit", stdout: "inherit", stderr: "inherit", }), - ), - ); - if (fetchExitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git fetch origin --tags --force' exited with code ${fetchExitCode}`, - }); - } + ) + .pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git fetch origin --tags --force' exited with code ${exitCode}`, + }), + ), + ); let ref: string; if (target === "stable" || target === "nightly") { @@ -184,15 +192,18 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( { cwd: submodulePath, stderr: "inherit" }, ), ); - const [tagsOutput, tagsExitCode] = yield* Effect.all( - [collectStream(tagsProcess.stdout), tagsProcess.exitCode.pipe(Effect.map(Number))], + const [tagsOutput] = yield* Effect.all( + [collectStream(tagsProcess.stdout), tagsProcess.exitCode], { concurrency: "unbounded" }, + ).pipe( + Effect.filterOrFail( + ([, exitCode]) => exitCode === 0, + ([, exitCode]) => + new SyncUpstreamError({ + message: `'git tag --list' exited with code ${exitCode}`, + }), + ), ); - if (tagsExitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git tag --list' exited with code ${tagsExitCode}`, - }); - } const tag = tagsOutput .trim() .split("\n") @@ -210,21 +221,24 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( } else if (/^v?\d+\.\d+\.\d+(?:-.+)?$/u.test(target)) { ref = target.startsWith("v") ? target : `v${target}`; } else { - const targetFetchExitCode = Number( - yield* spawner.exitCode( + yield* spawner + .exitCode( ChildProcess.make("git", ["fetch", "origin", target], { cwd: submodulePath, stdin: "inherit", stdout: "inherit", stderr: "inherit", }), - ), - ); - if (targetFetchExitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git fetch origin ${target}' exited with code ${targetFetchExitCode}`, - }); - } + ) + .pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git fetch origin ${target}' exited with code ${exitCode}`, + }), + ), + ); ref = "FETCH_HEAD"; } @@ -234,33 +248,39 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( stderr: "inherit", }), ); - const [commitOutput, commitExitCode] = yield* Effect.all( - [collectStream(commitProcess.stdout), commitProcess.exitCode.pipe(Effect.map(Number))], + const [commitOutput] = yield* Effect.all( + [collectStream(commitProcess.stdout), commitProcess.exitCode], { concurrency: "unbounded" }, + ).pipe( + Effect.filterOrFail( + ([, exitCode]) => exitCode === 0, + ([, exitCode]) => + new SyncUpstreamError({ + message: `'git rev-parse --verify ${ref}^{commit}' exited with code ${exitCode}`, + }), + ), ); - if (commitExitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git rev-parse --verify ${ref}^{commit}' exited with code ${commitExitCode}`, - }); - } const commit = commitOutput.trim(); if (commit !== currentCommit) { - const checkoutExitCode = Number( - yield* spawner.exitCode( + yield* spawner + .exitCode( ChildProcess.make("git", ["checkout", "--detach", commit], { cwd: submodulePath, stdin: "inherit", stdout: "inherit", stderr: "inherit", }), - ), - ); - if (checkoutExitCode !== 0) { - return yield* new SyncUpstreamError({ - message: `'git checkout --detach ${commit}' exited with code ${checkoutExitCode}`, - }); - } + ) + .pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git checkout --detach ${commit}' exited with code ${exitCode}`, + }), + ), + ); } return commit; @@ -281,14 +301,13 @@ const synchronizeConfig = Effect.fn("synchronizeConfig")(function* ( const packageJson = yield* decodePackageJson(packageJsonSource).pipe( Effect.mapError(syncError(`invalid package manifest in '${packageJsonPath}'`)), ); - const dependencies = yield* decodeDependencies(packageJson.dependencies).pipe( - Effect.mapError(syncError(`invalid dependencies in '${packageJsonPath}'`)), - ); - const devDependencies = yield* decodeDependencies(packageJson.devDependencies).pipe( - Effect.mapError(syncError(`invalid devDependencies in '${packageJsonPath}'`)), - ); const rootWorkspace = yield* readWorkspaceConfig(workspacePath); const upstreamWorkspace = yield* readWorkspaceConfig(upstreamWorkspacePath); + const synchronizedDependencies = Object.keys(rootWorkspace.config.catalog).filter((dependency) => + synchronizedDependencyPatterns.some((pattern) => + pattern.endsWith("*") ? dependency.startsWith(pattern.slice(0, -1)) : dependency === pattern, + ), + ); const versions: Record = {}; for (const dependency of synchronizedDependencies) { @@ -324,8 +343,8 @@ const synchronizeConfig = Effect.fn("synchronizeConfig")(function* ( } rootWorkspace.document.set("patchedDependencies", patchedDependencies); - const nextDependencies = { ...dependencies }; - const nextDevDependencies = { ...devDependencies }; + const nextDependencies = { ...packageJson.dependencies }; + const nextDevDependencies = { ...packageJson.devDependencies }; for (const [dependency, version] of Object.entries(versions)) { if (nextDependencies[dependency] !== undefined && nextDependencies[dependency] !== "catalog:") { nextDependencies[dependency] = version; @@ -372,24 +391,27 @@ const syncUpstream = Effect.fn("syncUpstream")(function* (target: string | undef const versions = yield* synchronizeConfig(root, submodulePath); yield* Console.log("Installing synchronized dependencies..."); - const installExitCode = Number( - yield* spawner.exitCode( + yield* spawner + .exitCode( ChildProcess.make("pnpm", ["install"], { cwd: root, stdin: "inherit", stdout: "inherit", stderr: "inherit", }), - ), - ); - if (installExitCode !== 0) { - yield* new SyncUpstreamError({ - message: `'pnpm install' exited with code ${installExitCode}`, - }); - } + ) + .pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'pnpm install' exited with code ${exitCode}`, + }), + ), + ); yield* Console.log(`T3 Code: ${commit}`); - for (const dependency of synchronizedDependencies) { - yield* Console.log(`${dependency}: ${versions[dependency]}`); + for (const [dependency, version] of Object.entries(versions)) { + yield* Console.log(`${dependency}: ${version}`); } }); From 9d99159b4a504164b92ac1973869df42c11d403a Mon Sep 17 00:00:00 2001 From: tarik02 Date: Sat, 1 Aug 2026 08:12:38 +0000 Subject: [PATCH 8/8] refine process and rpc error handling --- scripts/sync-upstream.ts | 202 ++++++++++++++++++++++----------------- src/rpc/operation.ts | 8 +- 2 files changed, 118 insertions(+), 92 deletions(-) diff --git a/scripts/sync-upstream.ts b/scripts/sync-upstream.ts index 71767f5..6ee5bb4 100644 --- a/scripts/sync-upstream.ts +++ b/scripts/sync-upstream.ts @@ -78,6 +78,81 @@ const readWorkspaceConfig = Effect.fn("readWorkspaceConfig")(function* (filePath return { config, document, source }; }); +const resolveUpstreamRef = Effect.fn("resolveUpstreamRef")(function* ( + submodulePath: string, + target: string, +) { + const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; + + if (target === "stable" || target === "nightly") { + const tagsProcess = yield* spawner.spawn( + ChildProcess.make( + "git", + [ + "tag", + "--list", + target === "stable" ? "v[0-9]*" : "v*-nightly.*", + "--sort=-version:refname", + ], + { cwd: submodulePath, stderr: "inherit" }, + ), + ); + const [tagsOutput] = yield* Effect.all( + [ + collectStream(tagsProcess.stdout), + tagsProcess.exitCode.pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git tag --list' exited with code ${exitCode}`, + }), + ), + ), + ], + { concurrency: "unbounded" }, + ); + const tag = tagsOutput + .trim() + .split("\n") + .find((candidate) => + target === "stable" + ? /^v\d+\.\d+\.\d+$/u.test(candidate) + : /^v\d+\.\d+\.\d+-nightly\..+$/u.test(candidate), + ); + if (tag === undefined) { + return yield* new SyncUpstreamError({ message: `no ${target} T3 Code tag was found` }); + } + return tag; + } + if (target === "main") { + return "origin/main"; + } + if (/^v?\d+\.\d+\.\d+(?:-.+)?$/u.test(target)) { + return target.startsWith("v") ? target : `v${target}`; + } + + yield* spawner + .exitCode( + ChildProcess.make("git", ["fetch", "origin", target], { + cwd: submodulePath, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }), + ) + .pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git fetch origin ${target}' exited with code ${exitCode}`, + }), + ), + ); + return "FETCH_HEAD"; +}); + const updateSubmodule = Effect.fn("updateSubmodule")(function* ( root: string, submodulePath: string, @@ -117,16 +192,19 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( }), ); const [status] = yield* Effect.all( - [collectStream(statusProcess.stdout), statusProcess.exitCode], + [ + collectStream(statusProcess.stdout), + statusProcess.exitCode.pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git status --porcelain' exited with code ${exitCode}`, + }), + ), + ), + ], { concurrency: "unbounded" }, - ).pipe( - Effect.filterOrFail( - ([, exitCode]) => exitCode === 0, - ([, exitCode]) => - new SyncUpstreamError({ - message: `'git status --porcelain' exited with code ${exitCode}`, - }), - ), ); if (status.trim().length > 0) { return yield* new SyncUpstreamError({ @@ -141,16 +219,19 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( }), ); const [currentCommitOutput] = yield* Effect.all( - [collectStream(currentCommitProcess.stdout), currentCommitProcess.exitCode], + [ + collectStream(currentCommitProcess.stdout), + currentCommitProcess.exitCode.pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git rev-parse HEAD' exited with code ${exitCode}`, + }), + ), + ), + ], { concurrency: "unbounded" }, - ).pipe( - Effect.filterOrFail( - ([, exitCode]) => exitCode === 0, - ([, exitCode]) => - new SyncUpstreamError({ - message: `'git rev-parse HEAD' exited with code ${exitCode}`, - }), - ), ); const currentCommit = currentCommitOutput.trim(); @@ -178,69 +259,7 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( ), ); - let ref: string; - if (target === "stable" || target === "nightly") { - const tagsProcess = yield* spawner.spawn( - ChildProcess.make( - "git", - [ - "tag", - "--list", - target === "stable" ? "v[0-9]*" : "v*-nightly.*", - "--sort=-version:refname", - ], - { cwd: submodulePath, stderr: "inherit" }, - ), - ); - const [tagsOutput] = yield* Effect.all( - [collectStream(tagsProcess.stdout), tagsProcess.exitCode], - { concurrency: "unbounded" }, - ).pipe( - Effect.filterOrFail( - ([, exitCode]) => exitCode === 0, - ([, exitCode]) => - new SyncUpstreamError({ - message: `'git tag --list' exited with code ${exitCode}`, - }), - ), - ); - const tag = tagsOutput - .trim() - .split("\n") - .find((candidate) => - target === "stable" - ? /^v\d+\.\d+\.\d+$/u.test(candidate) - : /^v\d+\.\d+\.\d+-nightly\..+$/u.test(candidate), - ); - if (tag === undefined) { - return yield* new SyncUpstreamError({ message: `no ${target} T3 Code tag was found` }); - } - ref = tag; - } else if (target === "main") { - ref = "origin/main"; - } else if (/^v?\d+\.\d+\.\d+(?:-.+)?$/u.test(target)) { - ref = target.startsWith("v") ? target : `v${target}`; - } else { - yield* spawner - .exitCode( - ChildProcess.make("git", ["fetch", "origin", target], { - cwd: submodulePath, - stdin: "inherit", - stdout: "inherit", - stderr: "inherit", - }), - ) - .pipe( - Effect.filterOrFail( - (exitCode) => exitCode === 0, - (exitCode) => - new SyncUpstreamError({ - message: `'git fetch origin ${target}' exited with code ${exitCode}`, - }), - ), - ); - ref = "FETCH_HEAD"; - } + const ref = yield* resolveUpstreamRef(submodulePath, target); const commitProcess = yield* spawner.spawn( ChildProcess.make("git", ["rev-parse", "--verify", `${ref}^{commit}`], { @@ -249,16 +268,19 @@ const updateSubmodule = Effect.fn("updateSubmodule")(function* ( }), ); const [commitOutput] = yield* Effect.all( - [collectStream(commitProcess.stdout), commitProcess.exitCode], + [ + collectStream(commitProcess.stdout), + commitProcess.exitCode.pipe( + Effect.filterOrFail( + (exitCode) => exitCode === 0, + (exitCode) => + new SyncUpstreamError({ + message: `'git rev-parse --verify ${ref}^{commit}' exited with code ${exitCode}`, + }), + ), + ), + ], { concurrency: "unbounded" }, - ).pipe( - Effect.filterOrFail( - ([, exitCode]) => exitCode === 0, - ([, exitCode]) => - new SyncUpstreamError({ - message: `'git rev-parse --verify ${ref}^{commit}' exited with code ${exitCode}`, - }), - ), ); const commit = commitOutput.trim(); diff --git a/src/rpc/operation.ts b/src/rpc/operation.ts index 2e7a18a..438410a 100644 --- a/src/rpc/operation.ts +++ b/src/rpc/operation.ts @@ -62,7 +62,9 @@ export const makeT3RpcOperations = Effect.fn("makeT3RpcOperations")(function* () Predicate.isTagged(error, "RpcClientError") ? rpc.disconnect : Effect.void, ), Effect.retry(rpcRetrySchedule), - Effect.mapError((error) => (error instanceof RpcError ? error : toRpcError(error, method))), + Effect.mapError((error) => + Predicate.isTagged(error, "RpcError") ? error : toRpcError(error, method), + ), ); const subscribe: T3RpcOperationsService["subscribe"] = ( @@ -74,7 +76,9 @@ export const makeT3RpcOperations = Effect.fn("makeT3RpcOperations")(function* () Predicate.isTagged(error, "RpcClientError") ? rpc.disconnect : Effect.void, ), Stream.retry(rpcRetrySchedule), - Stream.mapError((error) => (error instanceof RpcError ? error : toRpcError(error, method))), + Stream.mapError((error) => + Predicate.isTagged(error, "RpcError") ? error : toRpcError(error, method), + ), ); return {